Compare commits

...

1321 commits

Author SHA1 Message Date
Kelsi
be4cbad0b0 fix: unify lava UV scroll timer across render passes to prevent flicker
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
Lava M2 models used independent static-local start times in pass 1
and pass 2 for UV scroll animation. Since static locals initialize
on first call, the two timers started at slightly different times
(microseconds to frames apart), causing a permanent UV offset mismatch
between passes — visible as texture flicker/jumping on lava surfaces.

Replace both function-scoped statics with a single file-scoped
kLavaAnimStart constant, ensuring both passes compute identical UV
offsets from the same epoch.
2026-03-22 16:25:32 -07:00
Kelsi
b6047cdce8 feat: add world map navigation API for WorldMapFrame compatibility
Implement the core map functions needed by WorldMapFrame.lua:
- SetMapToCurrentZone — sets map view from player's current mapId/zone
- GetCurrentMapContinent — returns continent (1=Kalimdor, 2=EK, etc.)
- GetCurrentMapZone — returns current zone ID
- SetMapZoom(continent, zone) — navigate map view
- GetMapContinents — returns continent name list
- GetMapZones(continent) — returns zone names per continent
- GetNumMapLandmarks — stub (returns 0)

Maps game mapId (0=EK, 1=Kalimdor, 530=Outland, 571=Northrend) to
WoW's continent numbering. Internal state tracks which continent/zone
the map UI is currently viewing.
2026-03-22 16:18:52 -07:00
Kelsi
f9856c1046 feat: implement GameTooltip methods with real item/spell/aura data
Replace empty stub GameTooltip methods with working implementations:

- SetInventoryItem(unit, slot): populates tooltip with equipped item
  name and quality-colored text via GetInventoryItemLink + GetItemInfo
- SetBagItem(bag, slot): populates from GetContainerItemInfo + GetItemInfo
- SetSpellByID(spellId): populates with spell name/rank from GetSpellInfo
- SetAction(slot): delegates to SetSpellByID or item lookup via GetActionInfo
- SetUnitBuff/SetUnitDebuff: populates from UnitBuff/UnitDebuff data
- SetHyperlink: parses item: and spell: links to populate name
- GetItem/GetSpell: now return real item/spell data when tooltip is populated

Also fix GetCVar/SetCVar conflict — remove Lua-side overrides that
were shadowing the C-side implementations (which return real screen
dimensions and sensible defaults for common CVars).
2026-03-22 16:13:39 -07:00
Kelsi
31ab76427f fix: remove dead duplicate ufNpcFlags check and add missing UNIT_MODEL_CHANGED events
In the CREATE update block, the ufNpcFlags check at the end of the
else-if chain was unreachable dead code — it was already handled
earlier in the same chain. Remove the duplicate.

In the VALUES update block, mount display changes via field updates
fired mountCallback_ but not the UNIT_MODEL_CHANGED addon event,
unlike the CREATE path which fired both. Add the missing event so
Lua addons are notified when the player mounts/dismounts via VALUES
updates (the common case for aura-based mounting).

Also fire UNIT_MODEL_CHANGED for target/focus/pet display ID changes
in the VALUES displayIdChanged path, matching the CREATE path behavior.
2026-03-22 16:09:57 -07:00
Kelsi
cbdf03c07e feat: add quest objective leaderboard API for WatchFrame quest tracking
Implement GetNumQuestLeaderBoards and GetQuestLogLeaderBoard — the core
functions WatchFrame.lua and QuestLogFrame.lua use to display objective
progress like "Kobold Vermin slain: 3/8" or "Linen Cloth: 2/6".

GetNumQuestLeaderBoards counts kill + item objectives from the parsed
SMSG_QUEST_QUERY_RESPONSE data. GetQuestLogLeaderBoard returns the
formatted progress text, type ("monster"/"item"/"object"), and
completion status for each objective.

Also adds ExpandQuestHeader/CollapseQuestHeader (no-ops for flat quest
list) and GetQuestLogSpecialItemInfo stub.
2026-03-22 16:04:33 -07:00
Kelsi
296121f5e7 feat: add GetPlayerFacing, GetCVar/SetCVar for minimap and addon settings
GetPlayerFacing() returns player orientation in radians, needed by
minimap addons for arrow rotation and facing-dependent mechanics.

GetCVar(name) returns sensible defaults for commonly queried CVars
(uiScale, screen dimensions, nameplate visibility, sound toggles,
autoLoot). SetCVar is a no-op stub for addon compatibility.
2026-03-22 15:58:45 -07:00
Kelsi
73ce601bb5 feat: fire PLAYER_ENTERING_WORLD and critical login events for addons
PLAYER_ENTERING_WORLD is the single most important WoW addon event —
virtually every addon registers for it to initialize UI, state, and
data structures. It was never fired, causing widespread addon init
failures on login and after teleports.

Now fired from:
- handleLoginVerifyWorld (initial login + same-map teleports)
- handleNewWorld (cross-map teleports, instance transitions)

Also fires:
- PLAYER_LOGIN on initial world entry only
- ZONE_CHANGED_NEW_AREA on all world entries
- UPDATE_WORLD_STATES on initial entry
- SPELLS_CHANGED + LEARNED_SPELL_IN_TAB after SMSG_INITIAL_SPELLS
  (so spell book addons can initialize on login)
2026-03-22 15:50:05 -07:00
Kelsi
5086520354 feat: add spell book tab API for SpellBookFrame addon compatibility
Implement GetNumSpellTabs, GetSpellTabInfo, GetSpellBookItemInfo, and
GetSpellBookItemName — the core functions SpellBookFrame.lua needs to
organize known spells into class skill line tabs.

Tabs are built lazily from knownSpells grouped by SkillLineAbility.dbc
mappings (category 7 = class). A "General" tab collects spells not in
any class skill line. Tabs auto-rebuild when the spell count changes.

Also adds SpellBookTab struct and getSpellBookTabs() to GameHandler.
2026-03-22 15:40:40 -07:00
Kelsi
f29ebbdd71 feat: add quest watch/tracking and selection Lua API for WatchFrame
Implement the quest tracking functions needed by WatchFrame.lua:
- SelectQuestLogEntry/GetQuestLogSelection — quest log selection state
- GetNumQuestWatches — count of tracked quests
- GetQuestIndexForWatch(watchIdx) — map Nth watched quest to log index
- AddQuestWatch/RemoveQuestWatch — toggle quest tracking by log index
- IsQuestWatched — check if a quest log entry is tracked
- GetQuestLink — generate colored quest link string

Backed by existing trackedQuestIds_ set and questLog_ vector.
Adds selectedQuestLogIndex_ state to GameHandler for quest selection.
2026-03-22 15:36:25 -07:00
Kelsi
6d72228f66 feat: add GetInventorySlotInfo for PaperDollFrame and BankFrame
Maps WoW equipment slot names (e.g. "HeadSlot", "MainHandSlot") to
inventory slot IDs, empty-slot textures, and relic check flags.
Supports case-insensitive matching with optional "Slot" suffix stripping.

Unblocks PaperDollFrame.lua and BankFrame.lua which call this function
to resolve slot button IDs during UI initialization.
2026-03-22 15:30:53 -07:00
Kelsi
ab8ff6b7e5 feat: add UnitStat and combat chance Lua API for character sheet addons
Expose server-authoritative player stats to Lua addons:

- UnitStat(unit, statIndex) — returns STR/AGI/STA/INT/SPI (base,
  effective, posBuff, negBuff) matching the WoW API 4-return signature
- GetDodgeChance, GetParryChance, GetBlockChance — defensive stats
- GetCritChance, GetRangedCritChance — physical crit percentages
- GetSpellCritChance(school) — per-school spell crit
- GetCombatRating(index) — WotLK combat rating system
- GetSpellBonusDamage, GetSpellBonusHealing — caster stat display
- GetAttackPowerForStat, GetRangedAttackPower — melee/ranged AP

All data is already tracked from SMSG_UPDATE_OBJECT field updates;
these functions simply expose existing GameHandler getters to Lua.
Enables PaperDollFrame, DejaCharacterStats, and similar addons.
2026-03-22 15:25:20 -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
Kelsi
329a1f4b12 feat: add IsActionInRange, GetActionInfo, and GetActionCount Lua API
IsActionInRange(slot) checks if the spell on an action bar slot is within
range of the current target, using DBC spell range data and entity positions.
Returns 1/0/nil matching the WoW API contract.

GetActionInfo(slot) returns action type ("spell"/"item"/"macro"), id, and
subType for action bar interrogation by bar addons.

GetActionCount(slot) returns item stack count across backpack and bags for
consumable tracking on action bars.
2026-03-22 15:11:29 -07:00
Kelsi
ce4f93dfcb feat: add UnitCastingInfo/UnitChannelInfo Lua API and fix SMSG_CAST_FAILED events
Expose cast/channel state to Lua addons via UnitCastingInfo(unit) and
UnitChannelInfo(unit), matching the WoW API signature (name, text, texture,
startTime, endTime, isTradeSkill, castID, notInterruptible). Works for
player, target, focus, and pet units using existing UnitCastState tracking.

Also fix handleCastFailed (SMSG_CAST_FAILED, Classic/TBC path) to fire
UNIT_SPELLCAST_FAILED and UNIT_SPELLCAST_STOP events — previously only
the WotLK SMSG_CAST_RESULT path fired these, leaving Classic/TBC addons
unaware of cast failures.

Adds isChannel field to UnitCastState and getCastTimeTotal() accessor.
2026-03-22 15:05:29 -07:00
Kelsi Rae Davis
1482694495
Merge pull request #19 from ldmonster/fix/wotlk-render
fix/wotlk-render: WotLK rendering stability, Intel GPU compatibility & terrain OOM fixes
2026-03-22 13:40:10 -07:00
Paul
027640189a make start on ubuntu intel video cards 2026-03-22 21:47:12 +03:00
Paul
7565019dc9 log falling 2026-03-22 21:40:16 +03:00
Paul
bd725f0bbe build fix 2026-03-22 21:39:40 +03:00
Paul
572bb4ef36 fix preview white textutes 2026-03-22 21:38:56 +03:00
Kelsi
3103662528 fix: query corpse position on ghost login for accurate minimap marker
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
When logging in while already dead (reconnect/crash recovery), send
MSG_CORPSE_QUERY to get the server-authoritative corpse location.
Without this, the minimap corpse marker would be missing or point to
the wrong position after reconnecting as a ghost.
2026-03-21 14:13:03 -07:00
Kelsi
42222e4095 feat: handle MSG_CORPSE_QUERY for server-authoritative corpse position
Parse MSG_CORPSE_QUERY server response to get the exact corpse location
and map ID. Also send the query after releasing spirit so the minimap
corpse marker points to the correct position even when the player died
in an instance and releases to an outdoor graveyard.

Previously the corpse position was only set from the entity death
location, which could be wrong for cross-map ghost runs.
2026-03-21 14:08:47 -07:00
Kelsi
a4c8fd621d fix: use geoset 503 for bare shins to reduce knee width discontinuity
Change the bare shin (no boots) default from geoset 502 to 503 across
all four code paths (character creation, character preview, equipment
update, NPC rendering).

Geoset 503 has Y width ~0.44 which better matches the thigh mesh
width (~0.42) than 502's width (~0.39), reducing the visible gap at
the knee joint where lower and upper leg meshes meet.
2026-03-21 13:53:02 -07:00
Kelsi
afc5266acf feat: add UnitDistanceSquared for proximity and range check addons
Returns squared distance between the player and a unit, plus a boolean
indicating whether the calculation was possible. Squared distance
avoids sqrt for efficient range comparisons.

Used by DBM, BigWigs, and proximity warning addons for raid encounter
range checks (e.g., "spread 10 yards" mechanics).
2026-03-21 12:52:56 -07:00
Kelsi
c836b421fc feat: add CheckInteractDistance for proximity-based addon checks
Returns true if the player is within interaction distance of a unit:
- Index 1: Inspect range (28 yards)
- Index 2: Trade range (11 yards)
- Index 3: Duel range (10 yards)
- Index 4: Follow range (28 yards)

Used by trade addons, inspect addons, and proximity detection addons.
2026-03-21 12:33:39 -07:00
Kelsi
42b776dbf8 feat: add IsSpellInRange for healing and range-check addons
Returns 1 if the spell can reach the target, 0 if out of range, nil
if range can't be determined. Compares player-to-target distance against
the spell's maxRange from Spell.dbc via SpellDataResolver.

Used by healing addons (Healbot, VuhDo, Clique) to check if heals can
reach party members, and by action bar addons for range coloring.
2026-03-21 12:23:08 -07:00
Kelsi
d575c06bc1 feat: add UnitIsVisible for entity visibility checks
Returns true when the unit's entity exists in the entity manager
(within UPDATE_OBJECT range). Unlike UnitExists which falls back to
party member data, UnitIsVisible only returns true for entities that
can actually be rendered on screen.

Used by nameplate addons and proximity addons to check if a unit is
within visual range.

Session 14 commit #100: 95 new API functions, 113 new events.
2026-03-21 11:49:23 -07:00
Kelsi
be841fb3e1 feat: fire AUTOFOLLOW_BEGIN and AUTOFOLLOW_END events
Fire AUTOFOLLOW_BEGIN when the player starts following another unit
via /follow. Fire AUTOFOLLOW_END when following is cancelled. Used by
movement addons and AFK detection addons.
2026-03-21 11:45:52 -07:00
Kelsi
1f6865afce feat: fire PLAYER_LOGOUT event when logout begins
Fire PLAYER_LOGOUT from SMSG_LOGOUT_RESPONSE when the server confirms
logout. Addons use this to save state and perform cleanup before the
player leaves the world.
2026-03-21 11:22:57 -07:00
Kelsi
1fd220de29 feat: fire QUEST_TURNED_IN event when quest rewards are received
Fire QUEST_TURNED_IN with questId from SMSG_QUESTGIVER_QUEST_COMPLETE
when a quest is successfully completed and removed from the quest log.
Used by quest tracking addons (Questie, QuestHelper) and achievement
tracking addons.
2026-03-21 11:13:36 -07:00
Kelsi
1988e778c7 feat: fire CHAT_MSG_COMBAT_XP_GAIN on area exploration XP
Fire CHAT_MSG_COMBAT_XP_GAIN from SMSG_EXPLORATION_EXPERIENCE when the
player discovers a new area and gains exploration XP. Used by XP
tracking addons to count all XP sources including discovery.
2026-03-21 11:02:46 -07:00
Kelsi
24e2069225 feat: add UnitGroupRolesAssigned for LFG role display in raid frames
Returns "TANK", "HEALER", "DAMAGER", or "NONE" based on the WotLK LFG
roles bitmask from SMSG_GROUP_LIST. Used by raid frame addons (Grid,
VuhDo, Healbot) to display role icons next to player names.
2026-03-21 10:47:47 -07:00
Kelsi
d8c0820c76 feat: fire CHAT_MSG_COMBAT_FACTION_CHANGE on reputation changes
Fire CHAT_MSG_COMBAT_FACTION_CHANGE with the reputation change message
alongside UPDATE_FACTION when faction standings change. Used by
reputation tracking addons (FactionFriend, RepHelper) that parse
reputation gain messages.
2026-03-21 10:33:21 -07:00
Kelsi
964437cdf4 feat: fire TRAINER_UPDATE and SPELLS_CHANGED after trainer purchase
Fire TRAINER_UPDATE from SMSG_TRAINER_BUY_SUCCEEDED so trainer UI
addons refresh the spell list (marking learned spells as unavailable).
Also fire SPELLS_CHANGED so spellbook and action bar addons detect
the newly learned spell.
2026-03-21 10:27:43 -07:00
Kelsi
5d6376f3f1 feat: add UnitCanAttack and UnitCanCooperate for targeting addons
UnitCanAttack(unit, otherUnit): returns true if otherUnit is hostile
(attackable). UnitCanCooperate(unit, otherUnit): returns true if
otherUnit is friendly (can receive beneficial spells).

Used by nameplate addons for coloring and by targeting addons for
filtering hostile/friendly units.
2026-03-21 10:19:29 -07:00
Kelsi
a4ff315c81 feat: add UnitCreatureFamily for hunter pet and beast lore addons
Returns the creature family name (Wolf, Cat, Bear, etc.) for NPC units.
Data from CreatureInfo cache (creature_template family field). Used by
hunter pet management addons and tooltips that show pet family info.
2026-03-21 10:13:04 -07:00
Kelsi
3ad917bd95 feat: add colorStr and GenerateHexColor methods to RAID_CLASS_COLORS
Enhance RAID_CLASS_COLORS entries with colorStr hex string field and
GenerateHexColor()/GenerateHexColorMarkup() methods. Many addons
(Prat, Details, oUF) use colorStr to build colored chat text and
GenerateHexColor for inline color markup.
2026-03-21 10:02:34 -07:00
Kelsi
9b2f100387 feat: fire UNIT_MODEL_CHANGED on mount display changes
Fire UNIT_MODEL_CHANGED for the player when mount display ID changes
(mounting or dismounting). Mount addons and portrait addons now get
notified when the player's visual model switches between ground and
mounted form.
2026-03-21 09:53:32 -07:00
Kelsi
c97898712b feat: add GetSpellPowerCost for spell cost display addons
Returns a table of power cost entries: {{ type=powerType, cost=amount,
name=powerName }}. Data from SpellDataResolver (Spell.dbc ManaCost and
PowerType fields). Used by spell tooltip addons and action bar addons
that display mana/rage/energy costs.
2026-03-21 09:38:41 -07:00
Kelsi
8dca33e5cc feat: add UnitOnTaxi function for flight path detection
Returns true when the player is on a taxi/flight path. Used by action
bar addons to disable abilities during flight and by map addons to
track taxi state.
2026-03-21 09:32:40 -07:00
Kelsi
b1171327cb fix: UnitIsDead falls back to party member stats for out-of-range units
Previously UnitIsDead returned false for out-of-range party members
(entity not in entity manager). Now checks curHealth==0 from
SMSG_PARTY_MEMBER_STATS data, so raid frame addons correctly show
dead members in other zones as dead.
2026-03-21 09:23:20 -07:00
Kelsi
4364fa7bbe fix: UnitPowerType falls back to party member stats for out-of-range units
Previously UnitPowerType returned 0 (MANA) for party members who are
out of entity range. Now falls back to SMSG_PARTY_MEMBER_STATS power
type data, so raid frame addons correctly color rage/energy/runic
power bars for distant party members.
2026-03-21 09:18:25 -07:00
Kelsi
9267aec0b0 feat: fire UPDATE_WORLD_STATES event on world state changes
Fire UPDATE_WORLD_STATES from SMSG_UPDATE_WORLD_STATE when BG scores,
zone capture progress, or other world state variables change. Used by
BG score addons and world PvP objective tracking addons.
2026-03-21 09:12:59 -07:00
Kelsi
ac9214c03f feat: fire UNIT_THREAT_LIST_UPDATE event on threat changes
Fire UNIT_THREAT_LIST_UPDATE from SMSG_THREAT_UPDATE,
SMSG_HIGHEST_THREAT_UPDATE, and SMSG_THREAT_CLEAR. Threat data is
already parsed and stored in threatLists_ — this event notifies
addon systems when the data changes.

Used by Omen, ThreatPlates, and other threat meter addons to refresh
their displays when threat values update.
2026-03-21 09:08:02 -07:00
Kelsi
f580fd7e6b feat: add UnitDetailedThreatSituation for detailed threat queries
Implement UnitDetailedThreatSituation(unit, mobUnit) returning:
- isTanking (boolean)
- status (0-3, same as UnitThreatSituation)
- threatPct (100 if tanking, 0 otherwise)
- rawThreatPct (same)
- threatValue (0 — no server threat data available)

Used by Omen and other threat meter addons that query detailed threat
info per mob-target pair.
2026-03-21 09:02:47 -07:00
Kelsi
dcd78f4f28 feat: add UnitThreatSituation for threat meter and tank addons
Implement UnitThreatSituation(unit, mobUnit) returning 0-3 threat level:
- 0: not on threat table
- 1: in combat but not tanking (mob targeting someone else)
- 3: securely tanking (mob is targeting this unit)

Approximated from mob's UNIT_FIELD_TARGET to determine who the mob is
attacking. Used by threat meter addons (Omen, ThreatPlates) and tank
UI addons to display threat state.
2026-03-21 08:57:38 -07:00
Kelsi
4af9838ab4 feat: add UnitIsTapped, UnitIsTappedByPlayer, UnitIsTappedByAllThreatList
Add three tapped-state query functions for addons:
- UnitIsTapped(unit): true if any player has tagged the mob
- UnitIsTappedByPlayer(unit): true if local player can loot (tapped+lootable)
- UnitIsTappedByAllThreatList(unit): true if shared-tag mob

Used by nameplate addons (Plater, TidyPlates) and unit frame addons
to determine and display tap ownership state.
2026-03-21 08:48:58 -07:00
Kelsi
aebc905261 feat: show grey focus frame name for tapped mobs
Extend tapped-by-other detection to the focus frame, matching the
target frame and nameplate treatment. All three UI elements (nameplate,
target frame, focus frame) now consistently show grey for tapped mobs.
2026-03-21 08:42:56 -07:00
Kelsi
57ccee2c28 feat: show grey target frame name for tapped mobs
Extend the tapped-by-other-player check to the target frame. Mobs
tagged by another player now show a grey name color on the target
frame, matching the grey nameplate treatment and WoW's behavior.

Players can now see at a glance on both nameplates AND target frame
whether a mob is tagged.
2026-03-21 08:37:39 -07:00
Kelsi
586e9e74ff feat: show grey nameplates for mobs tapped by other players
Check UNIT_DYNFLAG_TAPPED_BY_PLAYER (0x0004) on hostile NPC nameplates.
Mobs tagged by another player now show grey health bars instead of red,
matching WoW's visual indication that the mob won't yield loot/XP.

Mobs with TAPPED_BY_ALL_THREAT_LIST (0x0008) still show red since
those are shared-tag mobs that give loot to everyone.
2026-03-21 08:33:54 -07:00
Kelsi
82990f5891 feat: fire UNIT_FLAGS event when unit flags change
Fire UNIT_FLAGS for player/target/focus when UNIT_FIELD_FLAGS updates.
Covers PvP flag, combat state, silenced, disarmed, and other flag
changes. Used by nameplate addons for PvP indicators and by unit frame
addons tracking CC/silence state.
2026-03-21 08:22:52 -07:00
Kelsi
e7be60c624 feat: fire UNIT_FACTION event when unit faction template changes
Fire UNIT_FACTION for player/target/focus when UNIT_FIELD_FACTIONTEMPLATE
updates. Covers PvP flag toggling, mind control faction swaps, and any
server-side faction changes. Used by nameplate addons to update hostility
coloring and by PvP addons tracking faction state.
2026-03-21 08:17:38 -07:00
Kelsi
2c6a345e32 feat: fire UNIT_MODEL_CHANGED event when unit display model changes
Fire UNIT_MODEL_CHANGED for player/target/focus/pet when their
UNIT_FIELD_DISPLAYID update field changes. This covers polymorph,
mount display changes, shapeshifting, and model swaps.

Used by unit frame addons that display 3D portraits and by nameplate
addons that track model state changes.
2026-03-21 08:13:47 -07:00
Kelsi
a4d54e83bc feat: fire MIRROR_TIMER_PAUSE event when breath/fatigue timer pauses
Fire MIRROR_TIMER_PAUSE from SMSG_PAUSE_MIRROR_TIMER with paused state
(1=paused, 0=resumed). Completes the mirror timer event trio alongside
MIRROR_TIMER_START and MIRROR_TIMER_STOP.
2026-03-21 08:07:39 -07:00
Kelsi
a73c680190 feat: add common WoW global constants for addon compatibility
Add frequently referenced WoW global constants that many addons check:
- MAX_TALENT_TABS, MAX_NUM_TALENTS
- BOOKTYPE_SPELL, BOOKTYPE_PET
- MAX_PARTY_MEMBERS, MAX_RAID_MEMBERS, MAX_ARENA_TEAMS
- INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED
- NUM_BAG_SLOTS, NUM_BANKBAGSLOTS, CONTAINER_BAG_OFFSET
- MAX_SKILLLINE_TABS, TRADE_ENCHANT_SLOT

Prevents nil-reference errors when addons use these standard constants.
2026-03-21 07:58:38 -07:00
Kelsi
2da0883544 feat: fire BARBER_SHOP_OPEN and BARBER_SHOP_CLOSE events
Fire BARBER_SHOP_OPEN when the barber shop UI is enabled
(SMSG_ENABLE_BARBER_SHOP). Fire BARBER_SHOP_CLOSE when the barber
shop completes or is dismissed. Used by UI customization addons.
2026-03-21 07:54:30 -07:00
Kelsi
5ee2b55f4b feat: fire MIRROR_TIMER_START and MIRROR_TIMER_STOP events
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
Fire MIRROR_TIMER_START with type, value, maxValue, scale, and paused
args when breath/fatigue/fire timers begin. Fire MIRROR_TIMER_STOP with
type when they end. Timer types: 0=fatigue, 1=breath, 2=fire.

Used by timer bar addons to display breath/fatigue countdown overlays.
2026-03-21 07:48:06 -07:00
Kelsi
774f9bf214 feat: fire BAG_UPDATE and UNIT_INVENTORY_CHANGED on item received
Fire BAG_UPDATE and UNIT_INVENTORY_CHANGED from SMSG_ITEM_PUSH_RESULT
when any item is received (loot, quest reward, trade, mail). Bag addons
(Bagnon, AdiBags) immediately show new items, and equipment tracking
addons detect inventory changes.
2026-03-21 07:33:22 -07:00
Kelsi
c6a6849c86 feat: fire PET_BAR_UPDATE event when pet action bar changes
Fire PET_BAR_UPDATE when:
- Pet is summoned (SMSG_PET_SPELLS with new spell list)
- Pet learns a new spell (SMSG_PET_LEARNED_SPELL)

Used by pet action bar addons to refresh their display when the pet's
available abilities change.
2026-03-21 07:30:01 -07:00
Kelsi
6ab1a189c7 feat: fire GUILD_INVITE_REQUEST event with inviter and guild names
Fire GUILD_INVITE_REQUEST when another player invites the local player
to a guild. Includes inviterName and guildName as arguments. Used by
auto-accept guild addons and invitation notification addons.
2026-03-21 07:23:15 -07:00
Kelsi
d24d12fb8f feat: fire PARTY_INVITE_REQUEST event with inviter name
Fire PARTY_INVITE_REQUEST when another player invites the local player
to a group. Used by auto-accept group addons and invite notification
addons. Includes the inviter's name as the first argument.
2026-03-21 07:17:20 -07:00
Kelsi
70a50e45f5 feat: fire CONFIRM_TALENT_WIPE event with respec cost
Fire CONFIRM_TALENT_WIPE with the gold cost when the trainer offers
to reset talents (MSG_TALENT_WIPE_CONFIRM). Used by talent management
addons to show the respec cost dialog.
2026-03-21 07:12:38 -07:00
Kelsi
df79e08788 fix: fire GROUP_ROSTER_UPDATE when group is destroyed
SMSG_GROUP_DESTROYED clears all party state but wasn't firing addon
events. Raid frame addons (Grid, VuhDo, Healbot) now properly hide
when the group disbands. Also fires PARTY_MEMBERS_CHANGED for compat.
2026-03-21 07:07:32 -07:00
Kelsi
12fb8f73f7 feat: fire BAG_UPDATE and PLAYER_MONEY events after selling items
SMSG_SELL_ITEM success now fires BAG_UPDATE so bag addons refresh
their display, and PLAYER_MONEY so gold tracking addons see the
sell price income immediately.
2026-03-21 06:52:41 -07:00
Kelsi
b407d5d632 feat: fire RESURRECT_REQUEST event when another player offers resurrection
Fire RESURRECT_REQUEST with the caster's name from SMSG_RESURRECT_REQUEST
when a player/NPC offers to resurrect the dead player. Used by auto-accept
resurrection addons and death tracking addons.
2026-03-21 06:47:32 -07:00
Kelsi
5d93108a88 feat: fire CONFIRM_SUMMON event on warlock/meeting stone summons
Fire CONFIRM_SUMMON from SMSG_SUMMON_REQUEST when another player
summons via warlock portal or meeting stone. Used by auto-accept
summon addons and summon notification addons.
2026-03-21 06:42:59 -07:00
Kelsi
de903e363c feat: fire PLAYER_STARTED_MOVING and PLAYER_STOPPED_MOVING events
Track horizontal movement flag transitions (forward/backward/strafe)
and fire events when the player starts or stops moving. Used by
Healbot, cast-while-moving addons, and nameplate addons that track
player movement state for positioning optimization.
2026-03-21 06:38:48 -07:00
Kelsi
d36172fc90 fix: fire PLAYER_MONEY event from SMSG_LOOT_MONEY_NOTIFY
The loot money handler directly updates playerMoneyCopper_ but wasn't
firing PLAYER_MONEY. The update object path fires it when the coinage
field changes, but there can be a delay. Now gold-tracking addons
(Accountant, GoldTracker) immediately see looted money.
2026-03-21 06:32:41 -07:00
Kelsi
b3ad64099b feat: fire UNIT_PET event when pet is summoned, dismissed, or dies
Fire UNIT_PET with "player" as arg from SMSG_PET_SPELLS when:
- Pet is cleared (dismissed/dies) — both size-based and guid=0 paths
- Pet is summoned (new pet GUID received with spell list)

Used by pet frame addons and unit frame addons to show/hide pet
frames and update pet action bars when pet state changes.
2026-03-21 06:27:55 -07:00
Kelsi
5ab6286f7e fix: include pet unit ID in UNIT_HEALTH/POWER events from dedicated packets
SMSG_HEALTH_UPDATE and SMSG_POWER_UPDATE were not checking for the pet
GUID when dispatching addon events. Pet health/power bar addons now
properly receive UNIT_HEALTH and UNIT_POWER with unitId="pet".

The UPDATE_OBJECT and UNIT_AURA paths already had the pet check.
2026-03-21 06:23:03 -07:00
Kelsi
b04e36aaa4 fix: include pet unit ID in all spellcast addon events
Add pet GUID check to all spellcast event dispatchers:
- UNIT_SPELLCAST_START
- UNIT_SPELLCAST_SUCCEEDED
- UNIT_SPELLCAST_CHANNEL_START
- UNIT_SPELLCAST_CHANNEL_STOP
- UNIT_SPELLCAST_INTERRUPTED + STOP

Previously these events only fired for player/target/focus, meaning
pet cast bars and pet spell tracking addons wouldn't work. Now pet
spellcasts properly fire with unitId="pet".
2026-03-21 06:19:14 -07:00
Kelsi
6687c617d9 fix: display Lua errors from OnUpdate, executeFile, executeString as UI errors
Extend the Lua error UI display to cover all error paths:
- OnUpdate frame callbacks (previously only logged)
- executeFile loading errors (now also shown as UI error)
- executeString /run errors (now also shown as UI error)

This ensures addon developers see ALL Lua errors in-game, not just
event handler errors from the previous commit.
2026-03-21 06:08:17 -07:00
Kelsi
900626f5fe feat: fire UPDATE_BATTLEFIELD_STATUS event on BG queue/join/leave
Fire UPDATE_BATTLEFIELD_STATUS with the status code when battlefield
status changes (queued, ready to join, in progress, waiting to leave).
Used by BG queue addons and PvP addons to track battleground state.
2026-03-21 06:02:28 -07:00
Kelsi
19b8d31da2 feat: display Lua addon errors as in-game UI errors
Previously Lua addon errors only logged to the log file. Now they
display as red UI error text to the player (same as spell errors and
game warnings), helping addon developers debug issues in real-time.

Add LuaErrorCallback to LuaEngine, fire it from event handler and
frame OnEvent pcall error paths. Wire the callback to GameHandler's
addUIError in application.cpp.
2026-03-21 06:00:06 -07:00
Kelsi
64c0c75bbf feat: fire CHAT_MSG_COMBAT_HONOR_GAIN event on PvP honor kills
Fire CHAT_MSG_COMBAT_HONOR_GAIN from SMSG_PVP_CREDIT with the honor
message text. Used by PvP addons (HonorSpy, HonorTracker) to track
honor gains and kill counts.
2026-03-21 05:47:22 -07:00
Kelsi
d20357415b feat: fire PLAYER_CONTROL_LOST/GAINED on movement control changes
Fire PLAYER_CONTROL_LOST when SMSG_CLIENT_CONTROL_UPDATE revokes player
movement (stun, fear, mind control, etc.) and PLAYER_CONTROL_GAINED when
movement is restored.

Used by loss-of-control addons and action bar addons to show stun/CC
indicators and disable ability buttons during crowd control.
2026-03-21 05:42:57 -07:00
Kelsi
2e6400f22e feat: fire MAIL_INBOX_UPDATE and UPDATE_PENDING_MAIL events
Fire MAIL_INBOX_UPDATE when the mail list is received/refreshed
(SMSG_MAIL_LIST_RESULT), so mail addons can update their display.

Fire UPDATE_PENDING_MAIL when new mail arrives (SMSG_RECEIVED_MAIL),
enabling minimap mail icon addons and notification addons to react.
2026-03-21 05:38:35 -07:00
Kelsi
2560bd1307 feat: fire QUEST_WATCH_UPDATE on kill and item objective progress
Fire QUEST_WATCH_UPDATE (with quest ID for kills) and QUEST_LOG_UPDATE
when quest objectives progress:
- Kill objectives: when SMSG_QUESTUPDATE_ADD_KILL updates a kill count
- Item objectives: when SMSG_QUESTUPDATE_ADD_ITEM updates an item count

Used by quest tracker addons (Questie, QuestHelper) and the built-in
quest tracker to refresh objective display when progress changes.
2026-03-21 05:33:29 -07:00
Kelsi
b5f7659db5 feat: fire MERCHANT_UPDATE and BAG_UPDATE events after purchase
Fire MERCHANT_UPDATE after a successful SMSG_BUY_ITEM so vendor addons
refresh their stock display. Also fire BAG_UPDATE so bag addons show
the newly purchased item immediately.
2026-03-21 05:27:34 -07:00
Kelsi
a02e021730 fix: fire UNIT_SPELLCAST_FAILED/STOP for other units on SPELL_FAILED_OTHER
SMSG_SPELL_FAILED_OTHER was clearing the unit cast state but not firing
addon events. Cast bar addons (Quartz, ClassicCastbars) showing target/
focus cast bars need UNIT_SPELLCAST_FAILED and UNIT_SPELLCAST_STOP to
clear the bar when another unit's cast fails.

Now fires both events for target and focus units, matching the behavior
already implemented for the player's own cast failures.
2026-03-21 05:22:17 -07:00
Kelsi
7f0d9fe432 feat: fire PLAYER_GUILD_UPDATE event on guild join/disband
Fire PLAYER_GUILD_UPDATE when the player's guild membership changes:
- When guild name is first resolved (player joins guild/logs in)
- When guild is disbanded

Used by guild frame addons and guild info display to update when
guild status changes.
2026-03-21 05:17:40 -07:00
Kelsi
82d3abe5da feat: add GetAddOnMetadata for reading TOC directives from Lua
Implement GetAddOnMetadata(addonNameOrIndex, key) which reads arbitrary
TOC file directives. All directives are now stored in the addon info
registry table under a "metadata" sub-table.

This enables addons to read their own version, author, X-* custom
fields, and other TOC metadata at runtime. Used by addon managers,
version checkers, and self-updating addons.
2026-03-21 05:13:28 -07:00
Kelsi
d4c1eda22b feat: fire PARTY_LEADER_CHANGED event on leader changes
Fire PARTY_LEADER_CHANGED (with GROUP_ROSTER_UPDATE) from both:
- SMSG_GROUP_SET_LEADER: when a new leader is named by string
- SMSG_REAL_GROUP_UPDATE: when leader GUID changes via group update

Used by raid frame addons to update leader crown icons and by
group management addons to track leadership changes.
2026-03-21 05:03:03 -07:00
Kelsi
494175e2a7 feat: add remaining CHAT_MSG_* event mappings
Map 5 previously unmapped chat types to addon events:
- CHAT_MSG_MONSTER_PARTY (NPC party chat in dungeons/scripted events)
- CHAT_MSG_AFK (player AFK auto-reply messages)
- CHAT_MSG_DND (player DND auto-reply messages)
- CHAT_MSG_LOOT (loot roll/distribution messages)
- CHAT_MSG_SKILL (skill-up messages)

All WoW chat types in the ChatType enum are now mapped to addon events.
2026-03-21 04:57:19 -07:00
Kelsi
f99f4a732a feat: fire INSPECT_READY event from both WotLK and Classic inspect paths
Fire INSPECT_READY with the inspected player's GUID when inspection
results are received. Fires from both:
- WotLK SMSG_TALENTS_INFO type=1 (talent + gear inspect)
- Classic SMSG_INSPECT (gear-only inspect)

Used by GearScore, TacoTip, and other inspection addons that need
to know when inspect data is available for a specific player.
2026-03-21 04:53:08 -07:00
Kelsi
74d7e969ab feat: add action bar constants and functions for Bartender/Dominos compat
Add essential WoW action bar globals and functions that action bar
addons (Bartender4, Dominos, CT_BarMod) require on initialization:

Constants: NUM_ACTIONBAR_BUTTONS, NUM_ACTIONBAR_PAGES, NUM_PET_ACTION_SLOTS
Functions: GetActionBarPage, ChangeActionBarPage, GetBonusBarOffset,
  GetActionText, GetActionCount
Binding: GetBindingKey, GetBindingAction, SetBinding, SaveBindings
Macro: GetNumMacros, GetMacroInfo, GetMacroBody, GetMacroIndexByName
Stance: GetNumShapeshiftForms, GetShapeshiftFormInfo
Pet: GetPetActionInfo, GetPetActionsUsable

These prevent nil-reference errors during addon initialization and
enable basic action bar addon functionality.
2026-03-21 04:48:06 -07:00
Kelsi
1f3e362512 feat: add RAID_TARGET_UPDATE event and GetRaidTargetIndex/SetRaidTarget
Fire RAID_TARGET_UPDATE event when raid markers (skull, cross, etc.)
are set or cleared on targets. Add two Lua API functions:
- GetRaidTargetIndex(unit) returns marker index 1-8 (or nil)
- SetRaidTarget(unit, index) sets marker 1-8 (or 0 to clear)

Enables raid marking addons and nameplate addons that display raid
icons to react to marker changes in real-time.
2026-03-21 04:43:42 -07:00
Kelsi
8e51754615 feat: fire READY_CHECK, READY_CHECK_CONFIRM, READY_CHECK_FINISHED events
Fire addon events for the raid ready check system:
- READY_CHECK fires when a ready check is initiated, with initiator name
- READY_CHECK_CONFIRM fires for each player's response, with GUID and
  ready state (1=ready, 0=not ready)
- READY_CHECK_FINISHED fires when the ready check period ends

These events are used by raid frame addons (Grid, VuhDo, Healbot) to
show ready check status on unit frames, and by raid management addons
to track responsiveness.
2026-03-21 04:38:35 -07:00
Kelsi
70a5d3240c feat: add ACHIEVEMENT_EARNED event and 15 missing CHAT_MSG_* events
Fire ACHIEVEMENT_EARNED event when a player earns an achievement,
enabling achievement tracking addons.

Add 15 previously unmapped chat type → addon event mappings:
- CHAT_MSG_ACHIEVEMENT, CHAT_MSG_GUILD_ACHIEVEMENT
- CHAT_MSG_WHISPER_INFORM (echo of sent whispers)
- CHAT_MSG_RAID_LEADER, CHAT_MSG_BATTLEGROUND_LEADER
- CHAT_MSG_MONSTER_SAY/YELL/EMOTE/WHISPER
- CHAT_MSG_RAID_BOSS_EMOTE/WHISPER
- CHAT_MSG_BG_SYSTEM_NEUTRAL/ALLIANCE/HORDE

These events are needed by boss mod addons (DBM, BigWigs) to detect
boss emotes, by achievement trackers, and by chat filter addons that
process all message types.
2026-03-21 04:28:15 -07:00
Kelsi
6a0e86efe8 fix: IsUsableAction now checks spell power cost from DBC
IsUsableAction previously always returned notEnoughMana=false. Now it
checks the spell's mana cost from SpellDataResolver against the player's
current power, matching the same fix applied to IsUsableSpell.

This fixes action bar addons (Bartender, Dominos) incorrectly showing
abilities as usable when the player lacks mana/rage/energy.
2026-03-21 04:23:07 -07:00
Kelsi
91794f421e feat: add spell power cost to SpellDataResolver; fix IsUsableSpell mana check
Extend SpellDataInfo with manaCost and powerType fields, extracted from
Spell.dbc ManaCost and PowerType columns. This enables IsUsableSpell()
to properly check if the player has enough mana/rage/energy to cast.

Previously IsUsableSpell always returned notEnoughMana=false since cost
data wasn't available. Now it compares the spell's DBC mana cost against
the player's current power, returning accurate usability and mana state.

This fixes action bar addons showing abilities as usable when the player
lacks sufficient power, and enables OmniCC-style cooldown text to
properly dim insufficient-power abilities.
2026-03-21 04:20:58 -07:00
Kelsi
c7e16646fc feat: resolve spell cast time and range from DBC for GetSpellInfo
Add SpellDataResolver that lazily loads Spell.dbc, SpellCastTimes.dbc,
and SpellRange.dbc to provide cast time and range data. GetSpellInfo()
now returns real castTime (ms), minRange, and maxRange instead of
hardcoded 0 values.

This enables spell tooltip addons, cast bar addons (Quartz), and range
check addons to display accurate spell information. The DBC chain is:
  Spell.dbc[CastingTimeIndex] → SpellCastTimes.dbc[Base ms]
  Spell.dbc[RangeIndex] → SpellRange.dbc[MinRange, MaxRange]

Follows the same lazy-loading pattern as SpellIconPathResolver and
ItemIconPathResolver.
2026-03-21 04:16:12 -07:00
Kelsi
cfb9e09e1d feat: cache player class/race from name queries for UnitClass/UnitRace
Add playerClassRaceCache_ that stores classId and raceId from
SMSG_NAME_QUERY_RESPONSE. This enables UnitClass and UnitRace to return
correct data for players who were previously seen but are now out of
UPDATE_OBJECT range.

Fallback chain for UnitClass/UnitRace is now:
1. Entity update fields (UNIT_FIELD_BYTES_0) — for nearby entities
2. Name query cache — for previously queried players
3. getPlayerClass/Race() — for the local player

This improves class-colored names in chat, unit frames, and nameplates
for players who move out of view range.
2026-03-21 04:11:48 -07:00
Kelsi
d6a25ca8f2 fix: unit API functions return data for out-of-range party members
Previously UnitHealth, UnitHealthMax, UnitPower, UnitPowerMax, UnitLevel,
UnitName, and UnitExists returned 0/"Unknown"/false for party members in
other zones because the entity doesn't exist in the entity manager.

Now these functions fall back to SMSG_PARTY_MEMBER_STATS data stored in
GroupMember structs, which provides health, power, level, and name for
all party members regardless of distance. UnitName also falls back to
the player name cache.

This fixes raid frame addons (Grid, Healbot, VuhDo) showing blank/zero
data for party members who are out of UPDATE_OBJECT range.
2026-03-21 04:04:39 -07:00
Kelsi
61b54cfa74 feat: add unit state query functions and fix UnitAffectingCombat
Add 6 commonly needed unit state functions:
- UnitIsGhost(unit) checks ghost flag from UNIT_FIELD_FLAGS
- UnitIsDeadOrGhost(unit) combines dead + ghost checks
- UnitIsAFK(unit) / UnitIsDND(unit) check player flags
- UnitPlayerControlled(unit) true for players and player pets
- UnitSex(unit) reads gender from UNIT_FIELD_BYTES_0 byte 2

Fix UnitAffectingCombat to check UNIT_FLAG_IN_COMBAT (0x00080000)
from entity update fields for any unit, not just "player". Previously
returned false for all non-player units.

These functions are needed by unit frame addons (SUF, Pitbull, oUF)
to properly display ghost state, AFK/DND status, and combat state.
2026-03-21 03:59:04 -07:00
Kelsi
ec082e029c fix: UnitClass and UnitRace now work for target, focus, party, and all units
Previously UnitClass() only returned the correct class for "player" and
returned "Unknown" for all other units (target, focus, party1-4, etc.).
UnitRace() had the same bug.

Now both functions read UNIT_FIELD_BYTES_0 from the entity's update
fields to resolve class (byte 1) and race (byte 0) for any unit. This
fixes unit frame addons, class-colored names, and race-based logic for
all unit IDs.

Also fix UnitRace to return 3 values (localized, English, raceId) to
match WoW's API signature — previously it only returned 1.
2026-03-21 03:55:23 -07:00
Kelsi
8229a963d1 feat: add player name tab-completion in chat input
When typing commands like /w, /whisper, /invite, /trade, /duel, /follow,
/inspect, etc., pressing Tab now cycles through matching player names.

Name sources (in priority order):
1. Last whisper sender (most likely target for /r follow-ups)
2. Party/raid members
3. Friends list
4. Nearby visible players

Tab cycles through all matches; single match auto-appends a space.
Complements the existing slash-command tab-completion.
2026-03-21 03:49:02 -07:00
Kelsi
0d49cc8b94 fix: handle NPC facing-only rotation in SMSG_MONSTER_MOVE
Fix bug where NPCs receiving moveType=4 (FacingAngle) or moveType=3
(FacingTarget) monster move packets with zero waypoints would not
rotate in place. The handler only processed orientation when hasDest
was true, but facing-only updates have no destination waypoints.

Now NPCs properly rotate when:
- moveType=4: server specifies an exact facing angle (e.g., NPC turns
  to face the player during dialogue or scripted events)
- moveType=3: NPC should face a specific target entity

This fixes NPCs appearing frozen/unresponsive during scripted events,
quest interactions, and patrol waypoint facing changes.
2026-03-21 03:38:17 -07:00
Kelsi
a63f980e02 feat: add guild roster Lua API for guild management addons
Implement 5 guild-related WoW Lua API functions:
- IsInGuild() returns whether the player is in a guild
- GetGuildInfo("player") returns guildName, rankName, rankIndex
- GetNumGuildMembers() returns totalMembers, onlineMembers
- GetGuildRosterInfo(index) returns full 11-value tuple: name, rank,
  rankIndex, level, class, zone, note, officerNote, online, status, classId
- GetGuildRosterMOTD() returns the guild message of the day

Data sourced from SMSG_GUILD_ROSTER and SMSG_GUILD_QUERY_RESPONSE.
Enables guild management addons (GreenWall, officer tools, roster UIs).
2026-03-21 03:34:31 -07:00
Kelsi
7807058f9c feat: add SendAddonMessage and RegisterAddonMessagePrefix for addon comms
Implement the addon messaging API used by virtually every multiplayer
addon (DBM, BigWigs, EPGP, RC Loot Council, WeakAuras, etc.):

- SendAddonMessage(prefix, text, chatType, target) sends an addon
  message encoded as "prefix\ttext" via the appropriate chat channel
- RegisterAddonMessagePrefix(prefix) registers a prefix for filtering
  incoming addon messages
- IsAddonMessagePrefixRegistered(prefix) checks registration status
- C_ChatInfo table with aliases for the above functions (newer API compat)

Without these functions, all inter-addon communication between players
fails, breaking boss mods, loot distribution, and group coordination.
2026-03-21 03:31:54 -07:00
Kelsi
b2826ce589 feat: fire PLAYER_UPDATE_RESTING event on rest state changes
Fire PLAYER_UPDATE_RESTING when the player enters or leaves a resting
area (inn/capital city). Fires from both the SET_REST_START packet and
the QUEST_FORCE_REMOVE rest-state update path. Used by XP bar addons
and rest state indicator addons.
2026-03-21 03:27:09 -07:00
Kelsi
e64f9f4585 fix: add mail, auction, quest, and trade windows to Escape key chain
The Escape key now properly closes these windows before showing the
escape menu:
- Mail window (closeMailbox)
- Auction house (closeAuctionHouse)
- Quest details dialog (declineQuest)
- Quest offer reward dialog (closeQuestOfferReward)
- Quest request items dialog (closeQuestRequestItems)
- Trade window (cancelTrade)

Previously these windows required clicking their close button since
Escape would skip directly to the escape menu.
2026-03-21 03:24:23 -07:00
Kelsi
a39acd71ba feat: apply M2 color alpha and transparency tracks to batch opacity
Apply at-rest values from M2 color alpha and transparency animation
tracks to batch rendering opacity. This fixes models that should render
as semi-transparent (ghosts, ethereal effects, fading doodads) but were
previously rendering at full opacity.

The fix multiplies colorAlphas[batch.colorIndex] and
textureWeights[batch.transparencyIndex] into batchOpacity during model
setup. Zero values are skipped to avoid the edge case where animated
tracks start at 0 (invisible) and animate up — baking that first
keyframe would make the entire batch permanently invisible.
2026-03-21 03:14:57 -07:00
Kelsi
4f4c169825 feat: add GetNumFriends/GetFriendInfo/GetNumIgnores/GetIgnoreName API
Implement friend and ignore list query functions for social addons:
- GetNumFriends() returns friend count from contacts list
- GetFriendInfo(index) returns 7-value tuple: name, level, class, area,
  connected, status (AFK/DND), note
- GetNumIgnores() returns ignore count
- GetIgnoreName(index) returns ignored player's name

Data sourced from the contacts list populated by SMSG_FRIEND_LIST and
SMSG_CONTACT_LIST. Area names resolved from AreaTable.dbc.
2026-03-21 03:08:37 -07:00
Kelsi
b7e5034f27 feat: fire GUILD_ROSTER_UPDATE and GUILD_MOTD events for guild addons
Fire GUILD_ROSTER_UPDATE from SMSG_GUILD_ROSTER and from guild events
(member join/leave/kick, promotions, leader changes, online/offline,
disbanded). Fire GUILD_MOTD with the MOTD text when received.

These events are needed by guild management addons (GuildGreet,
GuildRoster replacements, officer tools) to refresh their UI.
2026-03-21 03:04:59 -07:00
Kelsi
b8d92b5ff2 feat: fire FRIENDLIST_UPDATE and IGNORELIST_UPDATE events
Fire FRIENDLIST_UPDATE from all three friend list packet handlers:
- SMSG_FRIEND_LIST (Classic format)
- SMSG_CONTACT_LIST (WotLK format)
- SMSG_FRIEND_STATUS (add/remove/online/offline updates)

Fire IGNORELIST_UPDATE when SMSG_CONTACT_LIST includes ignore entries.

These events are used by social addons to refresh their UI when the
friend/ignore list changes.
2026-03-21 03:01:55 -07:00
Kelsi
8f2a2dfbb4 feat: fire UNIT_NAME_UPDATE event when player names are resolved
Fire UNIT_NAME_UPDATE for target/focus/player when SMSG_NAME_QUERY_RESPONSE
resolves a player's name. Nameplate and unit frame addons use this event
to update displayed names when they become available asynchronously.
2026-03-21 02:58:55 -07:00
Kelsi
3b8165cbef feat: fire events for loot rolls, trade windows, and duels
Add missing addon events for three gameplay systems:

Loot rolls:
- START_LOOT_ROLL fires on SMSG_LOOT_START_ROLL with slot and countdown
- LOOT_SLOT_CLEARED fires when a loot item is removed (SMSG_LOOT_REMOVED)

Trade:
- TRADE_REQUEST when another player initiates a trade
- TRADE_SHOW when the trade window opens
- TRADE_CLOSED when trade is cancelled, declined, or completed
- TRADE_ACCEPT_UPDATE when the trade partner accepts

Duels:
- DUEL_REQUESTED with challenger name on incoming duel challenge
- DUEL_FINISHED when a duel completes or is cancelled
2026-03-21 02:57:00 -07:00
Kelsi
7105672f06 feat: resolve item icon paths from ItemDisplayInfo.dbc for Lua API
Add ItemIconPathResolver that lazily loads ItemDisplayInfo.dbc to map
displayInfoId → icon texture path. This fixes three Lua API functions
that previously returned nil for item icons:

- GetItemInfo() field 10 (texture) now returns the icon path
- GetActionTexture() for item-type action bar slots now returns icons
- GetLootSlotInfo() field 1 (texture) now returns proper item icons
  instead of incorrectly using the spell icon resolver

Follows the same lazy-loading pattern as SpellIconPathResolver. The DBC
is loaded once on first query and cached for all subsequent lookups.
2026-03-21 02:53:07 -07:00
Kelsi
e21f808714 feat: support SavedVariablesPerCharacter for per-character addon data
Implement the SavedVariablesPerCharacter TOC directive that many addons
use to store different settings per character (Bartender, Dominos,
MoveAnything, WeakAuras, etc.). Without this, all characters share the
same addon data file.

Per-character files are stored as <AddonName>.<CharacterName>.lua.saved
alongside the existing account-wide <AddonName>.lua.saved files. The
character name is resolved from the player GUID at world entry time.

Changes:
- TocFile::getSavedVariablesPerCharacter() parses the TOC directive
- AddonManager loads/saves per-character vars alongside account-wide vars
- Character name set from game handler before addon loading
2026-03-21 02:46:21 -07:00
Kelsi
0d2fd02dca feat: add 40+ frame metatable methods to prevent addon nil-reference errors
Add commonly called frame methods as no-ops or with basic state tracking
on the frame metatable, so any CreateFrame result supports them:

Layout: SetFrameLevel/Get, SetFrameStrata/Get, SetScale/Get/GetEffective,
  ClearAllPoints, SetID/GetID, GetLeft/Right/Top/Bottom, GetNumPoints,
  GetPoint, SetHitRectInsets
Behavior: EnableMouse, EnableMouseWheel, SetMovable, SetResizable,
  RegisterForDrag, SetClampedToScreen, SetToplevel, Raise, Lower,
  StartMoving, StopMovingOrSizing, RegisterForClicks, IsMouseOver
Visual: SetBackdrop, SetBackdropColor, SetBackdropBorderColor
Scripting: HookScript (chains with existing SetScript handlers),
  SetAttribute/GetAttribute, GetObjectType
Sizing: SetMinResize, SetMaxResize

These prevent the most common addon errors when addons call standard
WoW frame methods on CreateFrame results.
2026-03-21 02:39:44 -07:00
Kelsi
b99bf7021b feat: add WoW table/string/math/bit utility functions for addon compat
Add commonly used WoW global utility functions that many addons depend on:

Table: tContains, tInvert, CopyTable, tDeleteItem
String: strupper, strlower, strfind, strsub, strlen, strrep, strbyte,
  strchar, strrev, gsub, gmatch, strjoin
Math: Clamp, Round
Bit ops: bit.band, bit.bor, bit.bxor, bit.bnot, bit.lshift, bit.rshift
  (pure Lua implementation for Lua 5.1 which lacks native bit ops)

These prevent nil-reference errors and missing-function crashes in
addons that use standard WoW utility globals.
2026-03-21 02:37:56 -07:00
Kelsi
154140f185 feat: add UIDropDownMenu framework, font objects, and UI global stubs
Add the UIDropDownMenu compatibility framework used by virtually every
addon with settings or selection menus: UIDropDownMenu_Initialize,
CreateInfo, AddButton, SetWidth, SetText, GetText, SetSelectedID, etc.

Add global font object stubs (GameFontNormal, GameFontHighlight, etc.)
referenced by CreateFontString template arguments.

Add UISpecialFrames table, InterfaceOptionsFrame for addon panels,
InterfaceOptions_AddCategory, and common font color constants
(GRAY_FONT_COLOR, NORMAL_FONT_COLOR, etc.).

These globals prevent nil-reference errors in most popular addons.
2026-03-21 02:36:06 -07:00
Kelsi
760c6a2790 feat: fire PLAYER_ENTER_COMBAT and PLAYER_LEAVE_COMBAT events
Fire PLAYER_ENTER_COMBAT when the player's auto-attack starts
(SMSG_ATTACKSTART) and PLAYER_LEAVE_COMBAT when auto-attack stops.
These events are distinct from PLAYER_REGEN_DISABLED/ENABLED — they
specifically track physical melee combat state and are used by
combat-aware addons for weapon swing timers and attack state tracking.
2026-03-21 02:31:59 -07:00
Kelsi
60904e2e15 fix: fire talent/spell events correctly when learning talents
Fix bug where learning a talent caused an early return before firing
LEARNED_SPELL_IN_TAB and SPELLS_CHANGED events, leaving talent addons
unaware of changes. Now talent learning fires CHARACTER_POINTS_CHANGED,
PLAYER_TALENT_UPDATE, LEARNED_SPELL_IN_TAB, and SPELLS_CHANGED.

Also fire CHARACTER_POINTS_CHANGED, ACTIVE_TALENT_GROUP_CHANGED, and
PLAYER_TALENT_UPDATE from handleTalentsInfo (SMSG_TALENTS_INFO), so
talent addons update when the full talent state is received from the
server (login, spec switch, respec).

Also fire UNIT_HEALTH/UNIT_POWER events from SMSG_HEALTH_UPDATE and
SMSG_POWER_UPDATE packets for real-time unit frame updates.
2026-03-21 02:29:48 -07:00
Kelsi
d75f2c62e5 feat: fire UNIT_HEALTH/UNIT_POWER events from dedicated update packets
SMSG_HEALTH_UPDATE and SMSG_POWER_UPDATE are high-frequency WotLK
packets that update entity health/power values but weren't firing
addon events. Unit frame addons (Pitbull, oUF, SUF) depend on these
events to update health/mana bars in real-time.

Now fire UNIT_HEALTH for player/target/focus on SMSG_HEALTH_UPDATE
and UNIT_POWER on SMSG_POWER_UPDATE, matching the events already
fired from the UPDATE_OBJECT path.
2026-03-21 02:26:44 -07:00
Kelsi
55ef607093 feat: add talent tree Lua API for talent inspection addons
Implement 5 talent-related WoW Lua API functions:
- GetNumTalentTabs() returns class-specific talent tree count (usually 3)
- GetTalentTabInfo(tab) returns name, icon, pointsSpent, background
- GetNumTalents(tab) returns talent count in a specific tree
- GetTalentInfo(tab, index) returns full 8-value tuple with name, tier,
  column, current rank, max rank, and availability
- GetActiveTalentGroup() returns active spec (1 or 2)

Data sourced from Talent.dbc, TalentTab.dbc, and the server-sent talent
info packet. Enables talent addons and spec display addons.
2026-03-21 02:22:35 -07:00
Kelsi
0a6fdfb8b1 feat: add GetNumSkillLines and GetSkillLineInfo for profession addons
Implement skill line API functions that profession and tradeskill addons
need to display player skills:
- GetNumSkillLines() returns count of player skills
- GetSkillLineInfo(index) returns full 12-value tuple: name, isHeader,
  isExpanded, rank, tempPoints, modifier, maxRank, isAbandonable, etc.

Data comes from SMSG_SKILLS_INFO update fields and SkillLine.dbc names.
2026-03-21 02:18:25 -07:00
Kelsi
855f00c5b5 feat: add LibStub and CallbackHandler-1.0 for Ace3 addon compatibility
Implement LibStub — the universal library version management system that
virtually every WoW addon framework depends on (Ace3, LibDataBroker,
LibSharedMedia, etc.). Without LibStub, most popular addons fail to load.

Also implement CallbackHandler-1.0 — the standard event callback library
used by Ace3-based addons for inter-module communication. Supports
RegisterCallback, UnregisterCallback, UnregisterAllCallbacks, and Fire.

These two libraries unlock the entire Ace3 addon ecosystem.
2026-03-21 02:15:50 -07:00
Kelsi
c20db42479 feat: fire UNIT_SPELLCAST_SENT and UNIT_SPELLCAST_STOP events
Fire UNIT_SPELLCAST_SENT when the player initiates a spell cast (before
server confirms), enabling cast bar addons like Quartz to show latency.
Includes target name and spell ID as arguments.

Fire UNIT_SPELLCAST_STOP whenever a cast bar should disappear:
- On successful cast completion (SMSG_SPELL_GO)
- On cast failure (SMSG_CAST_RESULT with error)
- On spell interrupt (SMSG_SPELL_FAILURE/SMSG_SPELL_FAILED_OTHER)
- On manual cast cancel

These events are essential for cast bar replacement addons to properly
track when casts begin and end.
2026-03-21 02:10:09 -07:00
Kelsi
6e863a323a feat: add UseAction, CancelUnitBuff, and CastSpellByID Lua functions
Implement 3 critical gameplay Lua API functions:
- UseAction(slot) activates an action bar slot (spell/item), enabling
  action bar addons like Bartender/Dominos to fire abilities
- CancelUnitBuff("player", index) cancels a buff by index, enabling
  auto-cancel and buff management addons
- CastSpellByID(id) casts a spell by numeric ID, enabling macro addons
  and spell queuing systems
2026-03-21 02:03:51 -07:00
Kelsi
45850c5aa9 feat: add targeting Lua API functions for addon and macro support
Implement 8 targeting functions commonly used by unit frame addons,
targeting macros, and click-casting addons:
- TargetUnit(unitId) / ClearTarget()
- FocusUnit(unitId) / ClearFocus()
- AssistUnit(unitId) — target the given unit's target
- TargetLastTarget() — return to previous target
- TargetNearestEnemy() / TargetNearestFriend() — tab-targeting
2026-03-21 01:58:03 -07:00
Kelsi
3ae18f03a1 feat: fire UNIT_HEALTH/POWER/AURA events for party members; fix closeLoot event
Fire UNIT_HEALTH, UNIT_POWER, and UNIT_AURA events from
SMSG_PARTY_MEMBER_STATS with proper unit IDs (party1..4, raid1..40).
Previously, health/power changes for party members via the stats packet
were silent — raid frame addons never got notified.

Also fix closeLoot() not firing LOOT_CLOSED event when the loot window
is closed by the player (only handleLootReleaseResponse fired it).
2026-03-21 01:55:30 -07:00
Kelsi
00a97aae3f fix: remove Lua stubs overriding C implementations; add GameTooltip and frame factories
Fix GetScreenWidth/GetScreenHeight/GetNumLootItems/GetFramerate being
overridden by hardcoded Lua stubs that ran after the C functions were
registered. Now the real C implementations correctly take effect.

Add GameTooltip global frame with 20+ methods (SetOwner, ClearLines,
AddLine, AddDoubleLine, SetText, NumLines, GetText, SetHyperlink, etc.)
and ShoppingTooltip1/2 — critical for virtually all WoW addons.

Add frame:CreateTexture() and frame:CreateFontString() methods returning
stub objects with common API methods, enabling UI creation addons.

Add real GetFramerate() returning actual FPS from ImGui.
2026-03-21 01:52:59 -07:00
Kelsi
ce26284b90 feat: add GetCursorPosition, screen size queries, and frame positioning methods
Add global Lua API functions:
- GetCursorPosition() returns mouse x,y screen coordinates
- GetScreenWidth()/GetScreenHeight() return window dimensions

Add frame methods for UI layout:
- SetPoint, SetSize, SetWidth, SetHeight, GetWidth, GetHeight, GetCenter
- SetAlpha, GetAlpha, SetParent, GetParent

These enable UI customization addons to query cursor position, screen
dimensions, and manage frame layout — fundamental for unit frames,
action bars, and tooltip addons.
2026-03-21 01:44:59 -07:00
Kelsi
8555c80aa2 feat: add loot window Lua API for addon compatibility
Implement 6 loot-related WoW Lua API functions:
- GetNumLootItems() returns count of items in loot window
- GetLootSlotInfo(slot) returns texture, name, quantity, quality, locked
- GetLootSlotLink(slot) returns item link string
- LootSlot(slot) takes an item from loot
- CloseLoot() closes the loot window
- GetLootMethod() returns current group loot method

These pair with the LOOT_OPENED/LOOT_CLOSED events to enable loot
addons (AutoLoot, loot filters, master loot helpers).
2026-03-21 01:42:03 -07:00
Kelsi
7459f27771 feat: add targettarget, focustarget, pettarget, mouseovertarget unit IDs
Support compound unit IDs that resolve an entity's current target via
UNIT_FIELD_TARGET_LO/HI update fields. This enables addons to query
target-of-target info (e.g., UnitName("targettarget"), UnitHealth("focustarget"))
which is essential for threat meters and unit frame addons.
2026-03-21 01:37:44 -07:00
Kelsi
74125b7340 feat: fire LOOT/GOSSIP/QUEST/TRAINER addon events on window open/close
Fire the following events for addon compatibility:
- LOOT_OPENED, LOOT_CLOSED on loot window open/close
- GOSSIP_SHOW, GOSSIP_CLOSED on gossip/quest-list window open/close
- QUEST_DETAIL when quest details are shown to the player
- QUEST_COMPLETE when quest offer reward dialog opens
- TRAINER_SHOW, TRAINER_CLOSED on trainer window open/close
2026-03-21 01:35:18 -07:00
Kelsi
fe8950bd4b feat: add action bar, combo points, reaction, and connection Lua API functions
Implement 10 new WoW Lua API functions for addon compatibility:
- GetComboPoints, UnitReaction, UnitIsConnected for unit frames/raid addons
- HasAction, GetActionTexture, IsCurrentAction, IsUsableAction, GetActionCooldown
  for action bar addons (Bartender, Dominos, etc.)
- UnitMana/UnitManaMax as Classic-era aliases for UnitPower/UnitPowerMax
2026-03-21 01:31:34 -07:00
Kelsi
32a51aa93d feat: add mouseover unit ID support and fire UPDATE_MOUSEOVER_UNIT/PLAYER_FOCUS_CHANGED events
Add "mouseover" as a valid unit ID in resolveUnitGuid so Lua API functions
like UnitName("mouseover"), UnitHealth("mouseover") etc. work for addons.
Fire UPDATE_MOUSEOVER_UNIT event when the mouseover target changes, and
PLAYER_FOCUS_CHANGED event when focus is set or cleared.
2026-03-21 01:26:37 -07:00
Kelsi
22798d1c76 feat: fire MAIL_SHOW/CLOSED and AUCTION_HOUSE_SHOW/CLOSED events
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
Fire MAIL_SHOW when mailbox opens (SMSG_SHOW_MAILBOX) and MAIL_CLOSED
when it closes. Fire AUCTION_HOUSE_SHOW when AH opens and AUCTION_HOUSE_CLOSED
when it closes. Used by mail addons (Postal) and AH addons (Auctionator).
2026-03-20 22:43:29 -07:00
Kelsi
395d6cdcba feat: fire BANKFRAME_OPENED and BANKFRAME_CLOSED events for bank addons
Fire BANKFRAME_OPENED when bank window opens and BANKFRAME_CLOSED when
it closes. Used by bank management addons (Bagnon, BankItems) to detect
when the player is interacting with their bank.
2026-03-20 22:32:21 -07:00
Kelsi
8cc90a69e8 feat: fire MERCHANT_SHOW and MERCHANT_CLOSED events for vendor addons
Fire MERCHANT_SHOW when vendor window opens (SMSG_LIST_INVENTORY) and
MERCHANT_CLOSED when vendor is closed. Used by vendor price addons and
auto-sell addons that need to detect vendor interaction state.
2026-03-20 22:22:36 -07:00
Kelsi
37a5b4c9d9 feat: fire ACTIONBAR_SLOT_CHANGED event on action bar updates
Fire when SMSG_ACTION_BUTTONS populates the action bar on login and when
SMSG_SUPERCEDED_SPELL upgrades spell ranks on the bar. Used by action bar
addons (Bartender, Dominos) to refresh their displays.
2026-03-20 22:13:57 -07:00
Kelsi
fc182f8653 feat: fire SKILL_LINES_CHANGED event when player skills update
Detect changes in player skill values after extractSkillFields() and fire
SKILL_LINES_CHANGED when any skill value changes. Used by profession
tracking addons and skill bar displays.
2026-03-20 21:57:27 -07:00
Kelsi
d68ef2ceb6 feat: fire CHAT_MSG_MONEY and CHAT_MSG_COMBAT_XP_GAIN events
Fire CHAT_MSG_MONEY when gold is looted (used by gold tracking addons
like MoneyFu, Titan Panel). Fire CHAT_MSG_COMBAT_XP_GAIN when XP is
earned (used by XP tracking addons and leveling speed calculators).
2026-03-20 21:47:39 -07:00
Kelsi
44d2b80998 feat: fire CHAT_MSG_LOOT event when items are looted
Fire CHAT_MSG_LOOT addon event from SMSG_ITEM_PUSH_RESULT with the loot
message text, item ID, and count. Used by loot tracking addons (AutoLootPlus,
Loot Appraiser) and damage meters that track loot distribution.
2026-03-20 21:27:04 -07:00
Kelsi
0885f885e8 feat: fire PLAYER_FLAGS_CHANGED event when player flags update
Fires when AFK/DND status, PvP flag, ghost state, or other player flags
change via PLAYER_FLAGS update field. Enables addons that track player
status changes (FlagRSP, TRP3, etc.).
2026-03-20 21:18:25 -07:00
Kelsi
3dcd489e81 feat: show random suffix names in auction owner sold/expired notifications
Parse randomPropertyId from SMSG_AUCTION_OWNER_NOTIFICATION to display
full item names in sold/bid/expired messages like "Your auction of
Gloves of the Monkey has sold!" Completes suffix resolution across
all 9 item display contexts.
2026-03-20 21:02:12 -07:00
Kelsi
9f49f543f6 feat: show random suffix names in auction outbid and expired notifications
Apply getRandomPropertyName() to SMSG_AUCTION_BIDDER_NOTIFICATION and
SMSG_AUCTION_REMOVED_NOTIFICATION so outbid/expired messages show full
item names like "You have been outbid on Leggings of the Eagle" instead
of just "Leggings". Completes suffix name resolution across all AH contexts.
2026-03-20 20:57:23 -07:00
Kelsi
df55242c50 feat: add GetCoinTextureString/GetCoinText Lua money formatting utility
Formats copper amounts into "Xg Ys Zc" strings for addon display.
GetCoinText is aliased to GetCoinTextureString. Used by money display
addons (Titan Panel, MoneyFu) and auction/vendor price formatting.
2026-03-20 20:44:59 -07:00
Kelsi
b659ab9caf feat: add GetPlayerInfoByGUID Lua API for damage meter player identification
Returns (class, englishClass, race, englishRace, sex, name, realm) for a
GUID string. Resolves player name from entity cache. Returns class/race
info for the local player. Used by Details!, Recount, and Skada to
identify players in COMBAT_LOG_EVENT_UNFILTERED data.
2026-03-20 20:22:15 -07:00
Kelsi
8be8d31b85 feat: add GetItemLink Lua API for clickable item links from item IDs
Returns WoW-format quality-colored item link for any item ID from the
item info cache. Used by loot addons, tooltip addons, and chat formatting
to create clickable item references.
2026-03-20 20:07:45 -07:00
Kelsi
6d2a94a844 feat: add GetSpellLink Lua API for clickable spell links in chat
Returns WoW-format spell link string "|cff71d5ff|Hspell:ID|h[Name]|h|r"
for a spell ID or name. Used by damage meters, chat addons, and WeakAuras
to create clickable spell references in chat messages.
2026-03-20 19:57:13 -07:00
Kelsi
bc4ff501e2 feat: show random suffix names in AH bids and seller auction tabs
Extend random property name resolution to the Bids tab and Your Auctions
(seller) tab. All three auction house tabs now display items with their
full suffix names (e.g., "Gloves of the Monkey" instead of "Gloves").
2026-03-20 19:37:17 -07:00
Kelsi
a13dfff9a1 feat: show random suffix names in auction house item listings
Append suffix name from getRandomPropertyName() to auction browse results
so items display as "Leggings of the Eagle" instead of just "Leggings"
in the auction house search table. Uses the randomPropertyId field from
the SMSG_AUCTION_LIST_RESULT packet data.
2026-03-20 19:33:01 -07:00
Kelsi
99f4ded3b5 feat: show random suffix names in loot roll popup and roll-won messages
Apply getRandomPropertyName() to SMSG_LOOT_START_ROLL and SMSG_LOOT_ROLL_WON
handlers so items with random suffixes display correctly in group loot
contexts (e.g., "Leggings of the Eagle" in the Need/Greed popup and
"Player wins Leggings of the Eagle (Need 85)" in chat).
2026-03-20 19:22:59 -07:00
Kelsi
4b3e377add feat: resolve random property/suffix names for item display
Load ItemRandomProperties.dbc and ItemRandomSuffix.dbc lazily to resolve
suffix names like "of the Eagle", "of the Monkey" etc. Add
getRandomPropertyName(id) callback on GameHandler wired through Application.
Append suffix to item names in SMSG_ITEM_PUSH_RESULT loot notifications
so items display as "Leggings of the Eagle" instead of just "Leggings".
2026-03-20 19:18:30 -07:00
Kelsi
23a7d3718c fix: return WoW-standard (start, duration, enabled) from GetSpellCooldown
Previously returned (0, remaining) which broke addons computing remaining
time as start + duration - GetTime(). Now returns (GetTime(), remaining, 1)
when on cooldown and (0, 0, 1) when off cooldown, plus the third 'enabled'
value that WoW always returns. Fixes cooldown display in OmniCC and similar.
2026-03-20 19:03:34 -07:00
Kelsi
3a4d59d584 feat: add GetXPExhaustion and GetRestState Lua APIs for rested XP tracking
GetXPExhaustion() returns rested XP pool remaining (nil if none).
GetRestState() returns 1 (normal) or 2 (rested) based on inn/city state.
Used by XP bar addons like Titan Panel and XP tracking WeakAuras.
2026-03-20 18:53:56 -07:00
Kelsi
2b99011cd8 fix: cap gossipPois_ vector growth and add soft frame rate limiter
Cap gossipPois_ at 200 entries (both gossip POI and quest POI paths) to
prevent unbounded memory growth from rapid gossip/quest queries. Add soft
240 FPS frame rate limiter when vsync is off to prevent 100% CPU usage —
sleeps for remaining frame budget when frame completes in under 4ms.
2026-03-20 18:51:05 -07:00
Kelsi
4bd237b654 feat: add IsUsableSpell Lua API for spell usability checks
Returns (usable, noMana) tuple. Checks if the spell is known and not on
cooldown. Accepts spell ID or name. Used by action bar addons and
WeakAuras for conditional spell display (greyed out when unusable).
2026-03-20 18:42:33 -07:00
Kelsi
ce128990d2 feat: add IsInInstance, GetInstanceInfo, and GetInstanceDifficulty Lua APIs
IsInInstance() returns whether player is in an instance and the type.
GetInstanceInfo() returns map name, instance type, difficulty index/name,
and max players. GetInstanceDifficulty() returns 1-based difficulty index.
Critical for raid/dungeon addons like DBM for instance detection.
2026-03-20 18:33:44 -07:00
Kelsi
2a9a7fe04e feat: add UnitClassification Lua API for nameplate and boss mod addons
Returns WoW-standard classification strings: "normal", "elite", "rareelite",
"worldboss", or "rare" based on creature rank from CreatureCache. Used by
nameplate addons (Plater, TidyPlates) and boss mods (DBM) to detect elite/
boss/rare mobs for special handling.
2026-03-20 18:25:39 -07:00
Kelsi
180990b9f1 feat: play minimap ping sound when party members ping the map
Add playMinimapPing() to UiSoundManager with MapPing.wav (falls back to
target select sound). Play the ping sound in MSG_MINIMAP_PING handler
when the sender is not the local player. Provides audio feedback for
party member map pings, matching WoW behavior.
2026-03-20 18:21:34 -07:00
Kelsi
f03ed8551b feat: add GetGameTime, GetServerTime, UnitXP, and UnitXPMax Lua APIs
GetGameTime() returns server game hours and minutes from the day/night
cycle. GetServerTime() returns Unix timestamp. UnitXP("player") and
UnitXPMax("player") return current and next-level XP values. Used by
XP tracking addons and time-based conditionals.
2026-03-20 18:16:12 -07:00
Kelsi
71837ade19 feat: show zone name on loading screen during world transitions
Add setZoneName() to LoadingScreen and display the map name from Map.dbc
as large gold text with drop shadow above the progress bar. Shown in both
render() and renderOverlay() paths. Zone name is resolved from gameHandler's
getMapName(mapId) during world load. Improves feedback during zone transitions.
2026-03-20 18:12:23 -07:00
Kelsi
ff1840415e fix: invoke despawn callbacks on disconnect to prevent renderer leaks
Mirror the zone-transition cleanup in disconnect(): fire despawn callbacks
for all entities before clearing the entity manager. Prevents M2 instances
and character models from leaking when the player disconnects and reconnects
quickly (e.g., server kick, network recovery).
2026-03-20 18:07:00 -07:00
Kelsi
922177abe0 fix: invoke despawn callbacks during zone transitions to release renderer resources
handleNewWorld() previously called entityManager.clear() directly without
notifying the renderer, leaving stale M2 instances and character models
allocated. Now iterates all entities and fires creatureDespawnCallback,
playerDespawnCallback, and gameObjectDespawnCallback before clearing.
Also clears player caches (visible items, cast states, aura cache,
combat text) to prevent state leaking between zones.
2026-03-20 18:05:09 -07:00
Kelsi
1d7eaaf2a0 fix: compute aura expirationTime for addon countdown timers
The expirationTime field (7th return value of UnitBuff/UnitDebuff/UnitAura)
was hardcoded to 0. Now returns GetTime() + remaining seconds, matching
WoW's convention where addons compute remaining = expirationTime - GetTime().
Enables buff/debuff timer addons like OmniCC and WeakAuras.
2026-03-20 18:00:57 -07:00
Kelsi
5adb9370d2 fix: return caster unit ID from UnitBuff/UnitDebuff/UnitAura
The caster field (8th return value) was always nil. Now returns the
caster's unit ID ("player", "target", "focus", "pet") or hex GUID
string for other units. Enables addons to identify who applied a
buff/debuff for filtering and tracking purposes.
2026-03-20 17:58:53 -07:00
Kelsi
ffe16f5cf2 feat: add equipment slot Lua API for gear inspection addons
Add GetInventoryItemLink(unit, slotId), GetInventoryItemID(unit, slotId),
and GetInventoryItemTexture(unit, slotId) for WoW inventory slots 1-19
(Head through Tabard). Returns quality-colored item links with WoW format.
Enables gear inspection and item level calculation addons.
2026-03-20 17:56:20 -07:00
Kelsi
3f0b152fe9 fix: return debuff type string from UnitBuff/UnitDebuff/UnitAura
The debuffType field (5th return value) was always nil. Now resolves
dispel type from Spell.dbc via getSpellDispelType(): returns "Magic",
"Curse", "Disease", or "Poison" for debuffs. Enables dispel-focused
addons like Decursive and Grid to detect debuff categories.
2026-03-20 17:53:01 -07:00
Kelsi
7c5bec50ef fix: increase world packet size limit from 16KB to 32KB
The 0x4000 (16384) limit was too conservative and could disconnect the
client when the server sends large packets such as SMSG_GUILD_ROSTER
with 500+ members (~30KB) or SMSG_AUCTION_LIST with many results.
Increase to 0x8000 (32768) which covers all normal gameplay while still
protecting against framing desync from encryption errors.
2026-03-20 17:49:49 -07:00
Kelsi
f712d3de94 feat: add quest log Lua API for quest tracking addons
Add GetNumQuestLogEntries(), GetQuestLogTitle(index), GetQuestLogQuestText(index),
and IsQuestComplete(questID). GetQuestLogTitle returns WoW-compatible 8 values
including title, isComplete flag, and questID. Enables quest tracking addons
like Questie and QuestHelper to access the player's quest log.
2026-03-20 17:37:35 -07:00
Kelsi
ee59c37b83 feat: add loot method change notifications and CRITERIA_UPDATE event
Show "Loot method changed to Master Looter/Round Robin/etc." in chat when
group loot method changes via SMSG_GROUP_LIST. Fire CRITERIA_UPDATE addon
event with criteria ID and progress when achievement criteria progress
changes, enabling achievement tracking addons.
2026-03-20 17:33:34 -07:00
Kelsi
c44e1bde0a feat: fire UPDATE_FACTION, QUEST_ACCEPTED, and QUEST_LOG_UPDATE events
Fire UPDATE_FACTION when reputation standings change (SMSG_SET_FACTION_STANDING).
Fire QUEST_ACCEPTED with quest ID when a new quest is added to the log.
Fire QUEST_LOG_UPDATE on both quest acceptance and quest completion.
Enables reputation tracking and quest log addons.
2026-03-20 17:28:28 -07:00
Kelsi
14007c81df feat: add /cancelqueuedspell command to clear spell queue
Add cancelQueuedSpell() method that clears queuedSpellId_ and
queuedSpellTarget_. Wire /cancelqueuedspell and /stopspellqueue
slash commands. Useful for combat macros that need to prevent
queued spells from firing after a current cast.
2026-03-20 17:24:16 -07:00
Kelsi
8761ad9301 fix: clean up combat text, cast bars, and aura cache on entity destroy
When SMSG_DESTROY_OBJECT removes an entity, now also purge combat text
entries targeting that GUID (prevents floating damage numbers on despawned
mobs), erase unit cast state (prevents stale cast bars), and clear cached
auras (prevents stale buff/debuff data for destroyed units).
2026-03-20 17:19:18 -07:00
Kelsi
0f480f5ada feat: add container/bag Lua API for bag addon support
Add GetContainerNumSlots(bag), GetContainerItemInfo(bag, slot),
GetContainerItemLink(bag, slot), and GetContainerNumFreeSlots(bag).
Container 0 = backpack (16 slots), containers 1-4 = equipped bags.
Returns item count, quality, and WoW-format item links with quality
colors. Enables bag management addons (Bagnon, OneBag, AdiBags).
2026-03-20 17:14:07 -07:00
Kelsi
e6fbdfcc02 feat: add /dump command for Lua expression evaluation and debugging
/dump <expression> evaluates a Lua expression and prints the result to
chat. For tables, iterates key-value pairs and displays them. Aliases:
/print. Useful for addon development and debugging game state queries
like "/dump GetSpellInfo(133)" or "/dump UnitHealth('player')".
2026-03-20 17:05:48 -07:00
Kelsi
b3f406c6d3 fix: sync cloud density with weather intensity and DBC cloud coverage
Cloud renderer's density was hardcoded at 0.35 and never updated from the
DBC-driven cloudDensity parameter. Now setDensity() is called each frame
with the lighting manager's cloud coverage value. Active weather (rain/
snow/storm) additionally boosts cloud density by up to 0.4 so clouds
visibly thicken during storms.
2026-03-20 16:50:32 -07:00
Kelsi
d7d6819855 feat: add generic UnitAura(unit, index, filter) Lua API function
Add UnitAura() that accepts WoW-compatible filter strings: "HELPFUL" for
buffs, "HARMFUL" for debuffs. Delegates to existing UnitBuff/UnitDebuff
logic. Many addons (WeakAuras, Grid, etc.) use UnitAura with filter
strings rather than separate UnitBuff/UnitDebuff calls.
2026-03-20 16:42:06 -07:00
Kelsi
4cdccb7430 feat: fire BAG_UPDATE and PLAYER_EQUIPMENT_CHANGED events for addons
Fire BAG_UPDATE and UNIT_INVENTORY_CHANGED when item stack/durability
fields change in UPDATE_OBJECT VALUES path. Fire PLAYER_EQUIPMENT_CHANGED
when equipment slot fields change. Enables bag addons (Bagnon, OneBag) and
gear tracking addons to react to inventory changes.
2026-03-20 16:38:57 -07:00
Kelsi
ae18d25996 feat: add sun height attenuation and warm sunset tint to lens flare
Reduce flare intensity when sun is near the horizon via smoothstep on
sunDir.z (0→0.25 range). Apply amber/orange color shift to flare elements
at sunrise/sunset for a warm golden glow. Prevents overly bright flares
at low sun angles while enhancing atmospheric mood.
2026-03-20 16:34:11 -07:00
Kelsi
bf62061a31 feat: expand slash command autocomplete with 30+ missing commands
Add /reload, /reloadui, /rl, /ready, /notready, /readycheck, /cancellogout,
/clearmainassist, /clearmaintank, /mainassist, /maintank, /cloak, /gdemote,
/gkick, /gleader, /gmotd, /gpromote, /gquit, /groster, /leaveparty,
/removefriend, /score, /script, /targetenemy, /targetfriend, /targetlast,
/ticket, and more to the tab-completion list. Alphabetically sorted.
2026-03-20 16:29:32 -07:00
Kelsi
00201c1232 feat: show enchant name and XP source creature in chat messages
SMSG_ENCHANTMENTLOG now resolves spell name and shows "You enchant with
[name]" or "[Caster] enchants your item with [name]" instead of silent
debug log. SMSG_LOG_XPGAIN now shows creature name: "Wolf dies, you gain
45 experience" instead of generic "You gain 45 experience" for kill XP.
2026-03-20 16:21:52 -07:00
Kelsi
21ead2aa4b feat: add /reload command to re-initialize addon system
Add AddonManager::reload() which saves all SavedVariables, shuts down the
Lua VM, re-initializes it, rescans .toc files, and reloads all addons.
Wire /reload, /reloadui, /rl slash commands that call reload() and fire
VARIABLES_LOADED + PLAYER_LOGIN + PLAYER_ENTERING_WORLD lifecycle events.
Essential for addon development and troubleshooting.
2026-03-20 16:17:04 -07:00
Kelsi
23ebfc7e85 feat: add LFG role check confirmation popup with CMSG_LFG_SET_ROLES
When the dungeon finder initiates a role check (SMSG_LFG_ROLE_CHECK_UPDATE
state=2), show a centered popup with Tank/Healer/DPS checkboxes and
Accept/Leave Queue buttons. Accept sends CMSG_LFG_SET_ROLES with the
selected role mask. Previously only showed passive "Role check in progress"
text with no way to respond.
2026-03-20 16:10:29 -07:00
Kelsi
df7feed648 feat: add distinct STORM weather type with wind-driven particles
Add Weather::Type::STORM enum value and wire it from SMSG_WEATHER type 3.
Storm particles are faster (70 units/s vs rain's 50), wind-angled at 15+
units lateral velocity with gusty turbulence, darker blue-grey tint, and
shorter lifetime. Previously storms rendered identically to rain.
2026-03-20 15:56:58 -07:00
Kelsi
d1bcd2f844 fix: resolve compiler warnings in lua_engine and game_screen
Remove unused getPlayerUnit() helper in lua_engine.cpp (-Wunused-function).
Increase countStr buffer from 8 to 16 bytes in action bar item count
display to eliminate -Wformat-truncation warning for %d with int32_t.
Build is now warning-free.
2026-03-20 15:53:43 -07:00
Kelsi
4b6ed04926 feat: add GetZoneText, GetSubZoneText, and GetMinimapZoneText Lua APIs
Add zone name query functions using worldStateZoneId + getAreaName lookup.
GetRealZoneText is aliased to GetZoneText. These are heavily used by boss
mod addons (DBM) for zone detection and by quest tracking addons.
2026-03-20 15:44:25 -07:00
Kelsi
0dd1b08504 feat: fire spellcast channel and interrupt events for Lua addons
Add UNIT_SPELLCAST_CHANNEL_START (MSG_CHANNEL_START), UNIT_SPELLCAST_CHANNEL_STOP
(MSG_CHANNEL_UPDATE with 0ms remaining), UNIT_SPELLCAST_FAILED (SMSG_CAST_RESULT
with error), and UNIT_SPELLCAST_INTERRUPTED (SMSG_SPELL_FAILURE) events. These
enable addons to track channeled spells and cast interruptions for all units.
2026-03-20 15:37:33 -07:00
Kelsi
e033efc998 feat: add bid status indicators to auction house UI
Show [Winning] (green) or [Outbid] (red) labels on the Bids tab based on
bidderGuid vs player GUID comparison. Show [Bid] (gold) indicator on the
seller's Auctions tab when someone has placed a bid on their listing.
Improves auction house usability by making bid status visible at a glance.
2026-03-20 15:31:41 -07:00
Kelsi
fb7b2b5390 feat: add 9 more WoW Lua API functions for group and unit queries
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
Add UnitAffectingCombat, GetNumRaidMembers, GetNumPartyMembers, UnitInParty,
UnitInRaid, UnitIsUnit, UnitIsFriend, UnitIsEnemy, and UnitCreatureType.
These are commonly used by raid/group addons for party composition checks,
combat state queries, and mob type identification. Total API count now 55.
2026-03-20 15:21:38 -07:00
Kelsi
50ca4f71f9 fix: correct NPC equipment geoset group assignments for gloves and boots
NPC character models used wrong geoset groups: gloves were group 3 (300s)
instead of group 4 (400s), boots were group 4 (400s) instead of group 5
(500s), matching the character preview code. Also remove spurious "torso"
geoset from group 5 (conflicted with boots) — chest armor controls only
group 8 (sleeves), not a separate torso visibility group. Fixes NPC
equipment rendering with incorrect body part meshes.
2026-03-20 15:15:15 -07:00
Kelsi
66f779c186 feat: add zone change and login sequence events for Lua addons
Fire ZONE_CHANGED_NEW_AREA and ZONE_CHANGED when worldStateZoneId changes
in SMSG_INIT_WORLD_STATES. Add VARIABLES_LOADED and PLAYER_LOGIN events in
the addon loading sequence (before PLAYER_ENTERING_WORLD), and fire
PLAYER_ENTERING_WORLD on subsequent world entries (teleport, instance).
Enables zone-aware addons like DBM and quest trackers.
2026-03-20 15:05:29 -07:00
Kelsi
5eaf738b66 feat: show quest POI markers on the world map overlay
Add QuestPoi struct and setQuestPois() to WorldMap, render quest objective
markers as cyan circles with golden outlines and quest title labels. Wire
gossipPois_ (from SMSG_QUEST_POI_QUERY_RESPONSE) through GameScreen to the
world map so quest objectives are visible alongside party dots and taxi nodes.
2026-03-20 15:00:29 -07:00
Kelsi
3790adfa06 feat: replace hardcoded state stubs with real game state in Lua API
IsMounted, IsFlying, IsSwimming, IsResting, IsFalling, and IsStealthed
now query actual GameHandler state (mount display ID, movement flags,
resting flag, aura list) instead of returning false. Add GetUnitSpeed
for player run speed. Fixes addon conditionals that depend on player
movement/mount/combat state.
2026-03-20 14:57:13 -07:00
Kelsi
90ccfbfc4e fix: fire GROUP_ROSTER_UPDATE on group uninvite and leave
handleGroupUninvite and leaveGroup cleared partyData but did not fire
GROUP_ROSTER_UPDATE/PARTY_MEMBERS_CHANGED events, so addon group tracking
would not update when kicked or leaving. Now both paths fire both events.
2026-03-20 14:44:48 -07:00
Kelsi
dbac4eb4f0 feat: add WoW compatibility stubs for broader addon support
Add error handling (geterrorhandler, seterrorhandler, debugstack, securecall,
issecurevariable), CVar system (GetCVar, GetCVarBool, SetCVar), screen/state
queries (GetScreenWidth/Height, GetFramerate, GetNetStats, IsLoggedIn,
IsMounted, IsFlying, etc.), UI stubs (StaticPopup_Show/Hide, StopSound),
and RAID_CLASS_COLORS table. Prevents common addon load errors.
2026-03-20 14:38:50 -07:00
Kelsi
ae30137705 feat: add COMBAT_LOG_EVENT_UNFILTERED and cooldown start events
Fire COMBAT_LOG_EVENT_UNFILTERED from addCombatText with WoW-compatible
subevent names (SWING_DAMAGE, SPELL_DAMAGE, SPELL_HEAL, etc.), source/dest
GUIDs, names, spell info, and amount. Also fire SPELL_UPDATE_COOLDOWN and
ACTIONBAR_UPDATE_COOLDOWN when cooldowns start (handleSpellCooldown), not
just when they end. Enables damage meter and boss mod addons.
2026-03-20 14:35:00 -07:00
Kelsi
ae627193f8 feat: fire combat, spell, and cooldown events for Lua addon system
Add PLAYER_REGEN_DISABLED/ENABLED (combat enter/leave) via per-frame
edge detection, LEARNED_SPELL_IN_TAB/SPELLS_CHANGED on spell learn/remove,
SPELL_UPDATE_COOLDOWN/ACTIONBAR_UPDATE_COOLDOWN on cooldown finish, and
PLAYER_XP_UPDATE on XP field changes. Total addon events now at 34.
2026-03-20 14:27:46 -07:00
Kelsi
4c10974553 feat: add party/raid unit IDs and game events for Lua addon system
Extend resolveUnit() to support party1-4, raid1-40, and use resolveUnitGuid
for UnitGUID/UnitIsPlayer/UnitBuff/UnitDebuff (including unitAurasCache for
party member auras). Fire UNIT_HEALTH, UNIT_POWER, UNIT_AURA, UNIT_SPELLCAST_START,
UNIT_SPELLCAST_SUCCEEDED, GROUP_ROSTER_UPDATE, and PARTY_MEMBERS_CHANGED events
to Lua addons from the corresponding packet handlers.
2026-03-20 14:15:00 -07:00
Kelsi
22b0cc8a3c feat: add GetSpellInfo, GetSpellTexture, GetItemInfo and more Lua API functions
Add spell icon path resolution via SpellIcon.dbc + Spell.dbc lazy loading,
wired through GameHandler callback. Fix UnitBuff/UnitDebuff to return icon
texture paths instead of nil. Add GetLocale, GetBuildInfo, GetCurrentMapAreaID.
2026-03-20 13:58:54 -07:00
Kelsi
3ff43a530f feat: add hooksecurefunc, UIParent, and noop stubs for addon compat
- hooksecurefunc(tblOrName, name, hook) — hook any function to run
  additional code after it executes without replacing the original.
  Supports both global and table method forms.

- UIParent, WorldFrame — standard parent frames that many addons
  reference as parents for their own frames.

- Noop stubs: SetDesaturation, SetPortraitTexture, PlaySound,
  PlaySoundFile — prevent errors from addons that call these
  visual/audio functions which don't have implementations yet.
2026-03-20 13:27:27 -07:00
Kelsi
0a62529b55 feat: add DEFAULT_CHAT_FRAME with AddMessage for addon output
Many WoW addons use DEFAULT_CHAT_FRAME:AddMessage(text, r, g, b) to
output colored text to chat. Implemented as a Lua table with AddMessage
that converts RGB floats to WoW color codes and calls print().
Also aliased as ChatFrame1 for compatibility.

Example: DEFAULT_CHAT_FRAME:AddMessage("Hello!", 1, 0.5, 0)
2026-03-20 13:18:16 -07:00
Kelsi
ee3f60a1bb feat: add GetNumAddOns and GetAddOnInfo for addon introspection
- GetNumAddOns() — returns count of loaded addons
- GetAddOnInfo(indexOrName) — returns name, title, notes, loadable

Addon info is stored in the Lua registry from the .toc directives
and populated before addon files execute. Useful for addon managers
and compatibility checks between addons.

Total WoW API: 33 functions.
2026-03-20 13:07:45 -07:00
Kelsi
66431ab762 feat: fire ADDON_LOADED event after each addon finishes loading
Fire ADDON_LOADED(addonName) after all of an addon's files have been
executed. This is the standard WoW pattern for addon initialization —
addons register for this event to set up defaults after SavedVariables
are loaded:

  local f = CreateFrame("Frame")
  f:RegisterEvent("ADDON_LOADED")
  f:SetScript("OnEvent", function(self, event, name)
      if name == "MyAddon" then
          MyAddonDB = MyAddonDB or {defaults}
      end
  end)

Total addon events: 20.
2026-03-20 12:52:25 -07:00
Kelsi
05a37036c7 feat: add UnitBuff and UnitDebuff API for Lua addons
Implement the most commonly used buff/debuff query functions:

- UnitBuff(unitId, index) — query the Nth buff on a unit
- UnitDebuff(unitId, index) — query the Nth debuff on a unit

Returns WoW-compatible 11-value tuple: name, rank, icon, count,
debuffType, duration, expirationTime, caster, isStealable,
shouldConsolidate, spellId.

Supports "player" and "target" unit IDs. Essential for buff tracking
addons (WeakAuras-style), healer addons, and combat analysis tools.

Total WoW API: 31 functions.
2026-03-20 12:45:43 -07:00
Kelsi
7d178d00fa fix: exclude vendored Lua 5.1.5 from Semgrep security scan
The Semgrep security scan was failing because vendored Lua 5.1.5 source
uses strcpy/strncpy which are flagged as insecure C functions. These are
false positives in frozen third-party code that we don't modify.

Added .semgrepignore to exclude all vendored extern/ directories
(lua-5.1.5, imgui, stb, vk-bootstrap, FidelityFX SDKs).
2026-03-20 12:27:59 -07: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
5ea5588c14 feat: add 6 more WoW API functions for Lua addons
- UnitRace(unitId) — returns race name ("Human", "Orc", etc.)
- UnitPowerType(unitId) — returns power type ID and name ("MANA", "RAGE", etc.)
- GetNumGroupMembers() — party/raid member count
- UnitGUID(unitId) — returns hex GUID string (0x format)
- UnitIsPlayer(unitId) — true if target is a player (not NPC)
- InCombatLockdown() — true if player is in combat

Total WoW API surface: 29 functions.
2026-03-20 12:15:36 -07:00
Kelsi
b235345b2c feat: add C_Timer.After and C_Timer.NewTicker for Lua addons
Implement WoW's C_Timer API used by most modern addons:

- C_Timer.After(seconds, callback) — fire callback after delay
- C_Timer.NewTicker(seconds, callback, iterations) — repeating timer
  with optional iteration limit and :Cancel() method

Implemented in pure Lua using a hidden OnUpdate frame that
auto-hides when no timers are pending (zero overhead when idle).

Example:
  C_Timer.After(3, function() print("3 sec later!") end)
  local ticker = C_Timer.NewTicker(1, function() print("tick") end, 5)
2026-03-20 12:11:24 -07:00
Kelsi
1f8660f329 feat: add OnUpdate frame script for per-frame addon callbacks
Frames can now set an OnUpdate script that fires every frame with
the elapsed time as an argument. This enables addon timers, polling,
and animations.

  local f = CreateFrame("Frame")
  f:SetScript("OnUpdate", function(self, elapsed)
      -- called every frame with deltaTime
  end)

OnUpdate only fires for visible frames (frame:Hide() pauses it).
Tracked in __WoweeOnUpdateFrames table, dispatched via
LuaEngine::dispatchOnUpdate() called from the Application main loop.
2026-03-20 12:07:22 -07:00
Kelsi
c7e25beaf1 feat: fire PLAYER_MONEY, PLAYER_DEAD, PLAYER_ALIVE addon events
Fire more gameplay events to Lua addons:
- PLAYER_MONEY — when gold/silver/copper changes (both CREATE and VALUES paths)
- PLAYER_DEAD — on forced death (SMSG_FORCED_DEATH_UPDATE)
- PLAYER_ALIVE — when ghost flag clears (player resurrected)

Total addon events: 19 (2 world + 12 chat + 5 gameplay).
2026-03-20 11:56:59 -07:00
Kelsi
269d9e2d40 feat: fire PLAYER_TARGET_CHANGED and PLAYER_LEVEL_UP addon events
Add a generic AddonEventCallback to GameHandler for firing named events
with string arguments directly from game logic. Wire it to the addon
system in Application.

New events fired:
- PLAYER_TARGET_CHANGED — when target is set or cleared
- PLAYER_LEVEL_UP(newLevel) — on level up

The generic callback pattern makes it easy to add more events from
game_handler.cpp without touching Application/AddonManager code.
Total addon events: 16 (2 world + 12 chat + 2 gameplay).
2026-03-20 11:51:46 -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
c1820fd07d feat: add WoW utility functions and SlashCmdList for addon slash commands
Utility functions:
- strsplit(delim, str), strtrim(str), wipe(table)
- date(format), time() — safe replacements for removed os.date/os.time
- format (alias for string.format), tinsert/tremove (table aliases)

SlashCmdList system:
- Addons can register custom slash commands via the standard WoW pattern:
  SLASH_MYADDON1 = "/myaddon"
  SlashCmdList["MYADDON"] = function(args) ... end
- Chat input checks SlashCmdList before built-in commands
- dispatchSlashCommand() iterates SLASH_<NAME>1..9 globals to match

Total WoW API surface: 23 functions + SlashCmdList + 14 events.
2026-03-20 11:40:58 -07:00
Kelsi
52a97e7730 feat: add action WoW API functions for Lua addons
Add 5 more essential WoW API functions for addon development:

- SendChatMessage(msg, type, lang, target) — send chat messages
  (SAY, YELL, WHISPER, PARTY, GUILD, OFFICER, RAID, BG)
- CastSpellByName(name) — cast highest rank of named spell
- IsSpellKnown(spellId) — check if player knows a spell
- GetSpellCooldown(nameOrId) — get remaining cooldown
- HasTarget() — check if player has a target

Total WoW API surface: 18 functions across Unit, Game, and Action
categories. Addons can now query state, react to events, send
messages, and cast spells.
2026-03-20 11:34:04 -07:00
Kelsi
0a0ddbfd9f feat: fire CHAT_MSG_* events to Lua addons for all chat types
Wire chat messages to the addon event system via AddonChatCallback.
Every chat message now fires the corresponding WoW event:

- CHAT_MSG_SAY, CHAT_MSG_YELL, CHAT_MSG_WHISPER
- CHAT_MSG_PARTY, CHAT_MSG_GUILD, CHAT_MSG_OFFICER
- CHAT_MSG_RAID, CHAT_MSG_RAID_WARNING, CHAT_MSG_BATTLEGROUND
- CHAT_MSG_SYSTEM, CHAT_MSG_CHANNEL, CHAT_MSG_EMOTE

Event handlers receive (eventName, message, senderName) arguments.
Addons can now filter, react to, or log chat messages in real-time.
2026-03-20 11:29:53 -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
52064eb438 feat: show item tooltip for /use macros when spell tooltip unavailable
When hovering a /use macro whose item's on-use spell isn't in the DBC
(no rich spell tooltip available), the tooltip fell back to showing raw
macro text. Now searches the item info cache and shows the full item
tooltip (stats, quality, binding, description) as a more useful
fallback for /use macros.
2026-03-20 10:12:42 -07:00
Kelsi
71a3abe5d7 feat: show item icon for /use macros on action bar
Macros with /use ItemName tried to find the item as a spell name for
icon resolution, which fails for items without a matching spell (e.g.
engineering trinkets, quest items). Now falls back to searching the
item info cache by name and showing the item's display icon when no
spell name matches.
2026-03-20 10:06:14 -07:00
Kelsi
503115292b fix: save character config when quest tracking changes
setQuestTracked() modified trackedQuestIds_ but didn't call
saveCharacterConfig(), so tracked quests were only persisted if
another action (like editing a macro or rearranging the action bar)
happened to trigger a save before logout. Now saves immediately
when quests are tracked or untracked.
2026-03-20 09:48:27 -07:00
Kelsi
d9ab1c8297 feat: persist tracked quest IDs across sessions
Quest tracking choices (right-click → Track on the quest objective
tracker) were lost on logout because trackedQuestIds_ was not saved
in the character config. Now saves tracked quest IDs as a comma-
separated list and restores them on login, so the quest tracker
shows the same quests the player chose to track in their previous
session.
2026-03-20 09:44:41 -07:00
Kelsi
6b61d24438 feat: document mouseover and @ target syntax in /macrohelp
Add [target=mouseover] and the @ shorthand syntax (@focus, @pet,
@mouseover, @player, @target) to the /macrohelp output. These are
commonly used for mouseover healing macros and were already supported
but not documented in the in-game help.
2026-03-20 09:32:14 -07:00
Kelsi
52d8da0ef0 feat: update /help output with missing commands
The /help text was missing several commonly-used commands: /castsequence,
/use, /threat, /combatlog, /mark, /raidinfo, /assist, /inspect,
/chathelp. Reorganized categories for clarity and added all missing
entries to match the expanded auto-complete list.
2026-03-20 09:27:22 -07:00
Kelsi
0e14174764 feat: expand chat auto-complete with 30+ missing slash commands
Many working slash commands were missing from the chat auto-complete
suggestions: /equipset, /focus, /clearfocus, /cleartarget, /inspect,
/played, /screenshot, /pvp, /duel, /threat, /unstuck, /logout, /loc,
/friend, /ignore, /unignore, /ginvite, /mark, /raidinfo, /helm,
/forfeit, /kneel, /assist, /castsequence, /stopmacro, /help, and more.
Reorganized alphabetically for maintainability.
2026-03-20 09:23:21 -07:00
Kelsi
b89aa36483 fix: clear spell visual negative cache on world entry
The spell visual failed-model cache was never cleared across world
changes, so models that failed to load during initial asset loading
(before MPQ/CASC data was fully indexed) would never retry. Now clears
spellVisualFailedModels_ in resetCombatVisualState() alongside the
active spell visual cleanup, giving failed models a fresh attempt on
each world entry.
2026-03-20 09:14:53 -07:00
Kelsi
6bd950e817 feat: support /use macros for action bar icon and indicators
Macros with /use ItemName (e.g. /use Healthstone, /use Engineering
trinket) had no icon, cooldown, or tooltip on the action bar because
only /cast and /castsequence were recognized. Now the spell resolution
also handles /use by looking up the item name in the item info cache
and finding its on-use spell ID. Added getItemInfoCache() accessor.
2026-03-20 09:08:49 -07:00
Kelsi
b960a1cdd5 fix: invalidate macro spell cache when spells are learned/removed
The macro primary spell cache stored 0 (no spell found) when a macro
referenced a spell the player hadn't learned yet. After learning the
spell from a trainer or leveling up, the cache was never refreshed,
so the macro button stayed broken. Now tracks the known spell count
and clears the cache when it changes, ensuring newly learned spells
are resolved on the next frame.
2026-03-20 08:52:57 -07:00
Kelsi
87e1ac7cdd feat: support /castsequence macros for action bar icon and indicators
Macros with /castsequence were treated as having no primary spell, so
they showed no icon, cooldown, range, power, or tooltip on the action
bar. Now both resolveMacroPrimarySpellId() and the icon derivation
code recognize /castsequence commands, strip the reset= spec, and
use comma-separation to find the first spell in the sequence. This
gives /castsequence macros the same visual indicators as /cast macros.
2026-03-20 08:43:19 -07:00
Kelsi
a5609659c7 feat: show cast-failed red flash on macro action bar buttons
The error-flash overlay (red fade on spell cast failure) only applied to
SPELL-type slots. Macro buttons never flashed red when their primary
spell failed to cast. Now resolves the macro's primary spell and checks
the actionFlashEndTimes_ map for a matching flash, completing macro
action bar parity with spell buttons across all 6 visual indicators.
2026-03-20 08:38:24 -07:00
Kelsi
a2df2ff596 feat: show insufficient-power tint on macro action bar buttons
The insufficient-power indicator only applied to SPELL-type slots.
Macro buttons like /cast Fireball never showed the power tint when the
player was out of mana. Now resolves the macro's primary spell and
checks its power cost against the player's current power, giving the
same visual feedback as regular spell buttons.
2026-03-20 08:32:07 -07:00
Kelsi
1d53c35ed7 feat: show out-of-range red tint on macro action bar buttons
The out-of-range indicator (red tint) only applied to SPELL-type action
bar slots. Macro buttons like /cast Frostbolt never turned red even when
the target was out of range. Now resolves the macro's primary spell via
the cached lookup and checks its max range against the target distance,
giving the same visual feedback as regular spell buttons.
2026-03-20 08:27:10 -07:00
Kelsi
3b20485c79 feat: show spell tooltip on macro action bar hover
Hovering a macro button on the action bar previously showed "Macro #N"
with raw macro text. Now resolves the macro's primary spell via the
cached lookup and shows its full rich tooltip (name, school, cost, cast
time, range, description) — same as hovering a regular spell button.
Falls back to the raw text display if no primary spell is found.
Also shows the cooldown remaining in red when the spell is on cooldown.
2026-03-20 08:18:28 -07:00
Kelsi
a103fb5168 fix: key macro cooldown cache by macro ID instead of slot index
The macro primary spell cache was keyed by action bar slot index, so
switching characters or rearranging macros could return stale spell IDs
from the previous character's macro in that slot. Now keyed by macro ID,
which is stable per-macro regardless of which slot it occupies.
2026-03-20 08:14:08 -07:00
Kelsi
bfbf590ee2 refactor: cache macro primary spell ID to avoid per-frame name search
The macro cooldown display from the previous commit iterated all known
spells (400+) every frame for each macro on the action bar, doing
lowercase string comparisons. Moved the spell name resolution into a
cached lookup (macroPrimarySpellCache_) that only runs once per macro
and is invalidated when macro text is edited. The per-frame path now
just does a single hash map lookup + spellCooldowns check.
2026-03-20 08:11:13 -07:00
Kelsi
670055b873 feat: show spell cooldown on macro action bar buttons
Macro buttons on the action bar never showed cooldowns — a /cast
Fireball macro would display no cooldown sweep or timer even when
Fireball was on cooldown. Now resolves the macro's primary spell (from
the first /cast command, stripping conditionals and alternatives) and
checks its cooldown via spellCooldowns. The cooldown sweep overlay and
countdown text display using the resolved spell's remaining time.
2026-03-20 08:07:20 -07:00
Kelsi
2e879fe354 fix: sync item cooldowns to action bar slots on login
The cooldown sync after SMSG_ACTION_BUTTONS and SMSG_INITIAL_SPELLS
only handled SPELL-type action bar slots. ITEM-type slots (potions,
trinkets, engineering items) were skipped, so items on the action bar
showed no cooldown overlay after login even if their on-use spell was
on cooldown. Now looks up each item's on-use spell IDs from the item
info cache and syncs any matching spellCooldowns entries.
2026-03-20 08:01:54 -07:00
Kelsi
625754f0f7 fix: let FSR3 settings persist across restarts without env var
FSR2/FSR3 upscaling mode was forcibly reverted to FSR1 on every startup
unless the WOWEE_ALLOW_STARTUP_FSR2 environment variable was set. This
meant users had to re-select FSR 3.x and re-enable frame generation on
every launch. Removed the env var requirement since the deferred
activation (wait until IN_WORLD state) already provides sufficient
startup safety by preventing FSR init during login/character screens.
2026-03-20 07:53:07 -07:00
Kelsi
1ed6380152 fix: raise initial cooldown count cap from 256 to 1024
Some server implementations include cooldown entries for all spells
(even with zero remaining time) to communicate category cooldown data.
The previous 256 cap could truncate these entries, causing missing
cooldown tracking for spells near the end of the list. Raised to match
the spell count cap for consistency.
2026-03-20 07:32:15 -07:00
Kelsi
eeb116ff7e fix: raise initial spell count cap from 256 to 1024
WotLK characters with all ability ranks, mounts, companion pets,
professions, and racial skills can know 400-600 spells. The previous
256 cap truncated the spell list, causing missing spells in the
spellbook, broken /cast commands for truncated spells, and missing
cooldown tracking for spells beyond the cap.
2026-03-20 07:28:24 -07:00
Kelsi
f101ed7c83 fix: clear spell visual instances on map change/world entry
Active spell visual M2 instances were never cleaned up when the player
teleported to a different map or re-entered the world. Orphaned effects
could linger visually from the previous combat session. Now properly
removes all active spell visual instances in resetCombatVisualState(),
which is called on every world entry.
2026-03-20 07:23:38 -07:00
Kelsi
e24c39f4be fix: add UNIT_FIELD_AURAFLAGS to update field name table
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
UNIT_FIELD_AURAFLAGS was defined in the UF enum and used in Classic and
Turtle JSON files (index 98) but missing from the kUFNames lookup table.
The JSON loader silently skipped it, so Classic/Turtle aura flag data
from UPDATE_OBJECT was never mapped. This could cause aura display
issues on Classic 1.12 and Turtle WoW servers.
2026-03-20 07:16:34 -07:00
Kelsi
ebc7d66dfe fix: add honor/arena currency to update field name table
PLAYER_FIELD_HONOR_CURRENCY and PLAYER_FIELD_ARENA_CURRENCY were added
to the UF enum and JSON files in cycle 1, but the kUFNames lookup table
in update_field_table.cpp was not updated. This meant the JSON loader
could not map these field names to their enum values, so honor and
arena point values from UPDATE_OBJECT were silently ignored.
2026-03-20 07:12:40 -07:00
Kelsi
5172c07e15 fix: include category cooldowns in initial spell cooldown tracking
SMSG_INITIAL_SPELLS cooldown entries have both cooldownMs (individual)
and categoryCooldownMs (shared, e.g. potions). The handler only checked
cooldownMs, so spells with category-only cooldowns (cooldownMs=0,
categoryCooldownMs=120000) were not tracked. Now uses the maximum of
both values, ensuring potion and similar shared cooldowns show on the
action bar after login.
2026-03-20 07:02:57 -07:00
Kelsi
533831e18d fix: sync pending spell cooldowns to action bar after login
SMSG_SPELL_COOLDOWN arrives before SMSG_ACTION_BUTTONS during login,
so cooldown times were stored in spellCooldowns but never applied to
the newly populated action bar slots. Players would see all abilities
as ready immediately after login even if spells were on cooldown.
Now applies pending cooldowns from the spellCooldowns map to each
matching slot when the action bar is first populated.
2026-03-20 06:59:23 -07:00
Kelsi
72993121ab feat: add pulsing yellow flash to chat tabs with unread messages
Chat tabs with unread messages now pulse yellow to attract attention.
The existing unread count "(N)" suffix was text-only and easy to miss,
especially for whisper and guild tabs. The pulsing color clears when
the tab is clicked, matching standard WoW chat tab behavior.
2026-03-20 06:47:39 -07:00
Kelsi
22742fedb8 feat: add [raid], [noraid], and [spec:N] macro conditionals
Add commonly-used WoW macro conditionals:
- [raid]/[noraid] — checks if the player is in a raid group (groupType
  == 1) vs a regular party. Used for conditional healing/targeting in
  raid content.
- [spec:1]/[spec:2] — checks the active talent spec (1-based index).
  Used for dual-spec macros that swap gear sets or use different
  rotations per spec.

Updated /macrohelp to list the new conditionals.
2026-03-20 06:42:43 -07:00
Kelsi
a6fe5662c8 fix: implement [target=pet] and [@pet] macro target specifiers
The /macrohelp listed [target=pet] as supported but the conditional
evaluator didn't handle the "pet" specifier for target= or @ syntax.
Now resolves to the player's active pet GUID (or skips the alternative
if no pet is active). Essential for hunter/warlock macros like:
  /cast [target=pet] Mend Pet
  /cast [@pet,dead] Revive Pet
2026-03-20 06:38:13 -07:00
Kelsi
fa82d32a9f feat: add [indoors]/[outdoors] macro conditionals via WMO detection
Add indoor/outdoor state macro conditionals using the renderer's WMO
interior detection. Essential for mount macros that need to select
ground mounts indoors vs flying mounts outdoors. The Renderer now
caches the insideWmo state in playerIndoors_ and exposes it via
isPlayerIndoors(). Updated /macrohelp to list the new conditionals.
2026-03-20 06:29:33 -07:00
Kelsi
114478271e feat: add [pet], [nopet], [group], [nogroup] macro conditionals
Add frequently-used macro conditionals for pet and group state:
- [pet]/[nopet] — checks if the player has an active pet (hunters,
  warlocks, DKs). Essential for pet management macros.
- [group]/[nogroup]/[party] — checks if the player is in a party or
  raid. Used for conditional targeting and ability usage.

Updated /macrohelp output to list the new conditionals.
2026-03-20 06:25:02 -07:00
Kelsi
a9e0a99f2b feat: add /macrohelp command to list available macro conditionals
Players can now type /macrohelp to see all supported macro conditionals
grouped by category (state, target, form, keys, aura). Also added to
the /help output and chat auto-complete list. This helps users discover
the macro system without external documentation.
2026-03-20 06:17:23 -07:00
Kelsi
d7059c66dc feat: add mounted/swimming/flying/stealthed/channeling macro conditionals
Add commonly-used WoW macro conditionals that were missing:
- [mounted]/[nomounted] — checks isMounted() state
- [swimming]/[noswimming] — checks SWIMMING movement flag
- [flying]/[noflying] — checks CAN_FLY + FLYING movement flags
- [stealthed]/[nostealthed] — checks UNIT_FLAG_SNEAKING (0x02000000)
- [channeling]/[nochanneling] — checks if currently channeling a spell

These are essential for common macros like mount/dismount toggles,
rogue opener macros, and conditional cast sequences.
2026-03-20 06:13:27 -07:00
Kelsi
6b7975107e fix: add proficiency warning to vendor/loot item tooltips
The proficiency check added in the previous commit only applied to the
ItemDef tooltip variant (inventory items). Vendor, loot, and AH
tooltips use the ItemQueryResponseData variant which was missing the
check. Now both tooltip paths show "You can't use this type of item."
in red when the player lacks weapon or armor proficiency.
2026-03-20 06:07:38 -07:00
Kelsi
120c2967eb feat: show proficiency warning in item tooltips
Item tooltips now display a red "You can't use this type of item."
warning when the player lacks proficiency for the weapon or armor
subclass (e.g. a mage hovering over a plate item or a two-handed
sword). Uses the existing canUseWeaponSubclass/canUseArmorSubclass
checks against SMSG_SET_PROFICIENCY bitmasks.
2026-03-20 06:04:29 -07:00
Kelsi
bc2085b0fc fix: increase compressed UPDATE_OBJECT decompressed size limit to 5MB
Capital cities and large raids can produce UPDATE_OBJECT packets that
decompress to more than 1MB. The real WoW client handles up to ~10MB.
Bump the limit from 1MB to 5MB to avoid silently dropping entity
updates in densely populated areas like Dalaran or 40-man raids.
2026-03-20 05:59:11 -07:00
Kelsi
bda5bb0a2b fix: add negative cache for failed spell visual model loads
Spell visual M2 models that fail to load (missing file, empty model,
or GPU upload failure) were re-attempted on every subsequent spell cast,
causing repeated file I/O during combat. Now caches failed model IDs in
spellVisualFailedModels_ so they are skipped on subsequent attempts.
2026-03-20 05:56:33 -07:00
Kelsi
90edb3bc07 feat: use M2 animation duration for spell visual lifetime
Spell visual effects previously used a fixed 3.5s duration for all
effects, causing some to linger too long and overlap during combat.
Now queries the M2 model's default animation duration via the new
getInstanceAnimDuration() method and clamps it to 0.5-5s. Effects
without animations fall back to a 2s default. This makes spell impacts
feel more responsive and reduces visual clutter.
2026-03-20 05:52:47 -07:00
Kelsi
29c938dec2 feat: add Isle of Conquest to battleground score frame
Add IoC (map 628) to the BG score display with Alliance/Horde
reinforcement counters (world state keys 4221/4222, max 300).
2026-03-20 05:40:53 -07:00
Kelsi
9d1fb39363 feat: add "Usable" filter to auction house and query token item names
Add a "Usable" checkbox to the AH search UI that filters results to
items the player can actually equip/use (server-side filtering via the
usableOnly parameter in CMSG_AUCTION_LIST_ITEMS). Also ensure token
item names for extended costs are queried from the server via
ensureItemInfo() so they display properly instead of "Item#12345".
2026-03-20 05:34:17 -07:00
Kelsi
5230815353 feat: display detailed honor/arena/token costs for vendor items
Load ItemExtendedCost.dbc and show specific costs (e.g. "2000 Honor",
"200 Arena", "30x Badge of Justice") instead of generic "[Tokens]" for
vendor items with extended costs. Items with both gold and token costs
now show both. Token item names are resolved from item info cache.
2026-03-20 05:28:45 -07:00
Kelsi
595ea466c2 fix: update local equipment set GUID on save confirmation and auto-request played time on login
SMSG_EQUIPMENT_SET_SAVED now updates the local set's GUID from the
server response, preventing duplicate set creation when clicking
"Update" on a newly-saved set. New sets are also added to the local
list immediately so the UI reflects them without a relog.

Additionally, CMSG_PLAYED_TIME is now auto-sent on initial world entry
(with sendToChat=false) so the character Stats tab shows total and
level time immediately without requiring /played.
2026-03-20 05:17:27 -07:00
Kelsi
e68a1fa2ec fix: guard equipment set packets against unsupported expansions
Classic and TBC lack equipment set opcodes, so sending save/use/delete
packets would transmit wire opcode 0xFFFF and potentially disconnect the
client. Now all three methods check wireOpcode != 0xFFFF before sending,
and the Outfits tab is only shown when the expansion supports equipment
sets (via supportsEquipmentSets() check).
2026-03-20 05:12:24 -07:00
Kelsi
9600dd40e3 fix: correct CMSG_EQUIPMENT_SET_USE packet format
The packet previously sent only a uint32 setId, which does not match
the WotLK protocol. AzerothCore/TrinityCore expect 19 iterations of
(PackedGuid itemGuid + uint8 srcBag + uint8 srcSlot). Now looks up the
equipment set's target item GUIDs and searches equipment, backpack, and
extra bags to provide correct source locations for each item.
2026-03-20 05:01:21 -07:00
Kelsi
1ae4cfaf3f fix: auto-acknowledge cinematic and movie triggers to prevent server hangs
Send CMSG_NEXT_CINEMATIC_CAMERA in response to SMSG_TRIGGER_CINEMATIC
and CMSG_COMPLETE_MOVIE in response to SMSG_TRIGGER_MOVIE. Some WotLK
servers block further packets or disconnect clients that don't respond
to these triggers, especially during the intro cinematic on first login.
2026-03-20 04:53:54 -07:00
Kelsi
f4d705738b fix: send CMSG_SET_WATCHED_FACTION when tracking a reputation
setWatchedFactionId() previously only stored the faction locally.
Now it also sends CMSG_SET_WATCHED_FACTION with the correct repListId
to the server, so the tracked faction persists across sessions.
2026-03-20 04:50:49 -07:00
Kelsi
ae56f2eb80 feat: implement equipment set save, update, and delete
Add saveEquipmentSet() and deleteEquipmentSet() methods that send
CMSG_EQUIPMENT_SET_SAVE and CMSG_DELETEEQUIPMENT_SET packets. The save
packet captures all 19 equipment slot GUIDs via packed GUID encoding.
The Outfits tab now always shows (not just when sets exist), with an
input field to create new sets and Update/Delete buttons per set.
2026-03-20 04:43:46 -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
0830215b31 fix: force Node.js 24 for GitHub Actions to resolve CI failure
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
2026-03-19 20:08:14 -07:00
Kelsi
0b8e1834f6 feat: group dungeon finder list by expansion with separator headers
Some checks failed
Build / Build (arm64) (push) Has been cancelled
Build / Build (x86-64) (push) Has been cancelled
Build / Build (macOS arm64) (push) Has been cancelled
Build / Build (windows-arm64) (push) Has been cancelled
Build / Build (windows-x86-64) (push) Has been cancelled
Security / CodeQL (C/C++) (push) Has been cancelled
Security / Semgrep (push) Has been cancelled
Security / Sanitizer Build (ASan/UBSan) (push) Has been cancelled
Categorize dungeons into Random/Classic/TBC/WotLK sections with visual
separators in the dropdown for easier navigation.
2026-03-18 12:43:04 -07:00
Kelsi
86cc6e16a4 fix: correct PET_CAST_FAILED expansion format and parse LFG role choices
SMSG_PET_CAST_FAILED: Classic/TBC omit the castCount byte (matching
SMSG_CAST_FAILED pattern). Without this fix, TBC parsing reads garbage.
SMSG_LFG_ROLE_CHOSEN: surface role selection messages in chat during
dungeon finder role checks.
2026-03-18 12:40:20 -07:00
Kelsi
d149255c58 feat: implement petition signing flow for guild charter creation
Parse SMSG_PETITION_QUERY_RESPONSE, SMSG_PETITION_SHOW_SIGNATURES,
and SMSG_PETITION_SIGN_RESULTS. Add UI to view signatures, sign
petitions, and turn in completed charters. Send CMSG_PETITION_SIGN
and CMSG_TURN_IN_PETITION packets.
2026-03-18 12:31:48 -07:00
Kelsi
41e15349c5 feat: improve arena team UI with names, types, and roster requests
Store team name and type (2v2/3v3/5v5) from SMSG_ARENA_TEAM_QUERY_RESPONSE.
Display proper team labels instead of raw IDs. Add Load/Refresh roster
buttons and CMSG_ARENA_TEAM_ROSTER request support.
2026-03-18 12:26:23 -07:00
Kelsi
aed8c94544 feat: add instance difficulty indicator on minimap
Show Normal/Heroic/25-Man difficulty badge below zone name when inside
a dungeon or raid instance. Orange-highlighted for heroic modes.
2026-03-18 12:21:41 -07:00
Kelsi
801f29f043 fix: sync player appearance after barber shop or polymorph
PLAYER_BYTES and PLAYER_BYTES_2 changes in SMSG_UPDATE_OBJECT now
update the Character struct's appearanceBytes and facialFeatures,
and fire an appearance-changed callback that resets the inventory
screen preview so it reloads with the new hair/face values.
2026-03-18 12:17:00 -07:00
Kelsi
2e134b686d fix: correct BattlemasterList.dbc IDs for arenas and Isle of Conquest
Arena and BG type IDs now match actual 3.3.5a BattlemasterList.dbc:
Nagrand Arena=4, Blade's Edge=5, Ruins of Lordaeron=8, Dalaran
Sewers=10, Ring of Valor=11, Isle of Conquest=30, Random BG=32.
2026-03-18 12:04:38 -07:00
Kelsi
5d5083683f fix: correct Eye of the Storm bgTypeId and simplify BG invite popup
Eye of the Storm uses bgTypeId 7 (from BattlemasterList.dbc), not 6.
BG invite popup now uses the stored bgName from the queue slot instead
of re-deriving the name with a duplicate switch statement.
2026-03-18 12:03:36 -07:00
Kelsi
64fd7eddf8 feat: implement barber shop UI with hair/facial customization
Adds a functional barber shop window triggered by SMSG_ENABLE_BARBER_SHOP.
Players can adjust hair style, hair color, and facial features using
sliders bounded by race/gender max values. Sends CMSG_ALTER_APPEARANCE
on confirm; server result closes the window on success. Escape key
also closes the barber shop.
2026-03-18 11:58:01 -07:00
Kelsi
8dfd916fe4 feat: add right-click context menu to target and focus frames
Right-clicking the target or focus frame name now opens a context
menu with Set Focus/Target, Clear Focus, Whisper, Invite, Trade,
Duel, Inspect, Add Friend, and Ignore options (player-specific
options only shown for player targets).
2026-03-18 11:48:22 -07:00
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
d6c752fba5 feat: Escape key closes topmost open window before showing menu
Escape now closes UI windows in priority order (vendor, bank, trainer,
who, combat log, social, talents, spellbook, quest log, character,
inventory, world map) before falling through to the escape menu, matching
standard WoW behavior.
2026-03-18 11:35:05 -07:00
Kelsi
f283f9eb86 fix: show equipment durability summary in repair button tooltip
The Repair All button tooltip now shows how many items are damaged or
broken instead of a generic message, helping players gauge repair need.
2026-03-18 11:30:34 -07:00
Kelsi
4a30fdf9f6 feat: add spell icon to nameplate cast bars
Nameplate cast bars now display the spell icon to the left of the bar,
matching the visual treatment of target frame and party cast bars.
2026-03-18 11:29:08 -07:00
Kelsi
0caf945a44 feat: add NumLock auto-run toggle and HUD indicator
NumLock now toggles auto-run alongside the existing tilde key. A cyan
[Auto-Run] indicator appears in the player info area when active.
2026-03-18 11:25:35 -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
ef4cf461a5 feat: add duration countdown and stack count to nameplate debuff dots
Nameplate debuff indicators now show: clock-sweep overlay for elapsed
duration, countdown text below each dot (color-coded red < 5s, yellow
< 15s), stack count badge, and duration in hover tooltip.
2026-03-18 11:21:14 -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
17d652947c feat: extend cursor hover to NPCs and players
Hand cursor now shows when hovering over any interactive entity in the
3D world (NPCs, players, game objects), not just game objects. Helps
identify clickable targets at a glance.
2026-03-18 10:56:44 -07:00
Kelsi
1cff1a03a5 feat: add clock display on minimap
Show local time (12-hour AM/PM) at the bottom-right corner of the
minimap with a semi-transparent background.
2026-03-18 10:54:03 -07:00
Kelsi
7f2ee8aa7e fix: add error sound on cast failure and AFK/DND whisper auto-reply
Play UI error sound on SMSG_CAST_FAILED for consistent audio feedback,
matching other error handlers (vendor, inventory, trainer).
Auto-reply to incoming whispers with AFK/DND message when player has
set /afk or /dnd status.
2026-03-18 10:50:42 -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
a417a00d3a feat: add FPS counter to latency meter
Display color-coded FPS alongside latency at top of screen.
Green >=60, yellow >=30, red <30. Shows FPS even without latency data.
2026-03-18 10:27:25 -07:00
Kelsi
6a0b0a99d1 fix: add /loc to /help command listing 2026-03-18 10:23:42 -07:00
Kelsi
09860e5fc6 feat: add /loc command to show player coordinates
Type /loc, /coords, or /whereami in chat to display current position
(X, Y, Z) and zone name as a system message. Useful for sharing
locations or debugging position issues.
2026-03-18 10:22:39 -07:00
Kelsi
dfddc71ebb docs: update status with nameplate and combat text features 2026-03-18 10:19:25 -07:00
Kelsi
355b75c3c7 feat: add creature type and guild name to focus frame
Show creature type (Beast, Humanoid, etc.) on the focus frame next to
the rank badge, matching the target frame. Also display player guild
names on focus frame for player targets.
2026-03-18 10:14:09 -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
1ea9334eca feat: enable login screen background music
Re-enable the login screen music system that was previously disabled.
Randomly selects from available tracks in assets/Original Music/ and
plays them at 80% volume during authentication.
2026-03-18 10:08:44 -07:00
Kelsi
402bbc2f14 feat: elite/boss/rare border decorations on nameplates
Add rank-specific outer borders on NPC nameplates: gold for Elite and
Rare Elite, red for Boss, silver for Rare. Provides immediate visual
identification of dangerous mobs without needing to target them.
2026-03-18 10:07:40 -07:00
Kelsi
fd7886f4ce feat: show NPC subtitle on nameplates
Display creature subtitles (e.g. <Reagent Vendor>, <Innkeeper>) below
NPC names on nameplates, mirroring the guild tag display for players.
The subtitle is fetched from the creature info cache populated by
SMSG_CREATURE_QUERY_RESPONSE.
2026-03-18 10:05:49 -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
02a1b5cbf3 fix: show reflected spell name in combat text
SMSG_SPELL_MISS_LOG REFLECT entries include a reflectSpellId field that
was parsed but discarded. Now store it in SpellMissLogEntry and pass it
to addCombatText, so floating combat text shows the actual reflected
spell name instead of the original cast spell.
2026-03-18 09:59:54 -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
6aea48aea9 feat: show guild name on target frame for players
Display <GuildName> below the player name in the target frame,
using the same guild name cache as nameplates.
2026-03-18 09:48:03 -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
003ad8b20c fix: read WotLK periodic damage isCrit byte in SMSG_PERIODICAURALOG
The WotLK periodic damage format includes an isCrit byte after resisted
(21 bytes total, not 20). Missing this byte caused parse misalignment
for multi-effect periodicauralog packets. Also use the already-read
isCrit on periodic heals to display critical HoT ticks distinctly.
2026-03-18 09:17:00 -07:00
Kelsi
8b7786f2b3 feat: display combo points on target frame
Add 5-dot combo point indicator between target power bar and cast bar.
Lit dots are yellow (1-4 CP) or red (5 CP) with glow effect; unlit
dots show as dark outlines. Only visible when the player's combo target
matches the current target.
2026-03-18 09:08:46 -07:00
Kelsi
6d9adc547a fix: extend world-load animation callbacks to handle online players
loadOnlineWorldTerrain re-registers the death/respawn/swing callbacks,
overriding the ones from setupUICallbacks. The world-load versions only
checked creatureInstances_, so the player lookup fix from the previous
commit was silently reverted whenever the world loaded. Now both
registration sites check playerInstances_ as a fallback.
2026-03-18 08:52:00 -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
100d66d18b fix: play death/attack animations for online players, not just NPCs
Death, respawn, and melee swing callbacks only checked
creatureInstances_, so online players never played death animation when
killed, never returned to idle on resurrect, and never showed attack
swings. Extended all three callbacks to also check playerInstances_.

Also extended the game_handler death/respawn callback triggers to fire
for PLAYER entities (not just UNIT), and added spawn-time death
detection for players that are already dead when first seen.
2026-03-18 08:43:19 -07:00
Kelsi
e54ed1d46f fix: pass correct offset to setPlayerOnTransport on transport boarding
Both CREATE_OBJECT and MOVEMENT update paths called
setPlayerOnTransport(guid, vec3(0)) then immediately overwrote
playerTransportOffset_ on the next line. This left a one-frame window
where the composed world position used (0,0,0) as the local offset,
causing the player to visually snap to the transport origin. Compute the
canonical offset first and pass it directly.
2026-03-18 08:39:35 -07:00
Kelsi
a619f44dfb fix: add per-frame animation sync for online players
Online players had no animation state machine — once Run started from a
movement packet, it never transitioned back to Stand/Idle. This mirrors
the creature sync loop: position, orientation, and locomotion animation
(Run/Walk/Swim/Fly ↔ Stand/SwimIdle/FlyIdle) are now driven per-frame
based on Entity::isActivelyMoving() state transitions.

Also cleans up creatureRenderPosCache_ on player despawn.
2026-03-18 08:33:45 -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
0b33bcbe53 fix: reject oversized MonsterMove spline and fix loot format comment
Change WotLK MonsterMove pointCount > 1000 from cap-to-1000 to return
false. Capping caused the parser to read only 1000 of N points, leaving
the remaining point data unread and misaligning subsequent reads.

Also correct misleading loot response comment: Classic/TBC DO include
randomSuffix and randomPropertyId (22 bytes/item, same as WotLK). The
only WotLK difference is the quest item list appended after regular
items.
2026-03-18 08:18:21 -07:00
Kelsi
64b03ffdf5 fix: add bounds checks to update block and field parsers
Check remaining packet data before reading update type, GUIDs, object
type, and block count in parseUpdateBlock and parseUpdateFields. Prevents
silent garbage reads when the parser reaches the end of a truncated or
misaligned packet.
2026-03-18 08:08:08 -07:00
Kelsi
d1c99b1c0e fix: add bounds checks to WotLK movement block parser
Complete the parser hardening across all expansions. Check remaining
bytes before every conditional read in the WotLK base
UpdateObjectParser::parseMovementBlock: LIVING entry (66-byte minimum),
transport, pitch, fall time, jumping, spline elevation, speeds,
POSITION, STATIONARY, and all tail flags (HAS_TARGET, TRANSPORT,
VEHICLE, ROTATION, LOWGUID, HIGHGUID). Prevents silent garbage reads
when Packet::readUInt8/readFloat return 0 past EOF.
2026-03-18 08:04:00 -07:00
Kelsi
e802decc84 fix: add bounds checks to TBC movement block parser
Same hardening as the Classic and Turtle parsers: check remaining bytes
before every conditional read in TbcPacketParsers::parseMovementBlock.
Change spline pointCount > 256 to return false instead of capping to
zero (which silently consumed wrong bytes for the endPoint).
2026-03-18 08:01:39 -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
14cd6c82b2 fix: add bounds checks to Classic movement block parser
Mirror the Turtle parser hardening: check remaining bytes before every
conditional read in ClassicPacketParsers::parseMovementBlock. Prevents
silent garbage reads (readUInt8 returns 0 past EOF) that corrupt
subsequent update fields and lose NPC data in multi-block packets.
2026-03-18 07:47:46 -07:00
Kelsi
0a04a00234 fix: harden Turtle movement block parser with bounds checks
The Turtle parseMovementBlock had no bounds checking on any reads.
Since Packet::readUInt8() returns 0 past the end without failing, the
parser could "succeed" with all-zero garbage data, then subsequent
parseUpdateFields would read from wrong positions, producing
"truncated field value" and "truncated update mask" errors.

Added bounds checks before every conditional read section (transport,
swimming pitch, fall time, jumping, spline elevation, speeds, spline
data, tail flags). Also removed the WotLK movement block fallback from
the Turtle parser chain — WotLK format is fundamentally incompatible
(uint16 flags, 9 speeds) and false-positive parses corrupt NPC data.
Also changed spline pointCount > 256 from cap-to-zero to return false
so the parser correctly fails instead of silently dropping waypoints.
2026-03-18 07:39:40 -07:00
Kelsi
ce3caf0438 fix: auto-detect Classic vs WotLK spline format in UPDATE_OBJECT
The spline parser assumed WotLK format (durationMod, durationModNext,
conditional PARABOLIC fields) for all expansions. Classic/Turtle has a
simpler layout: timePassed+duration+splineId+pointCount directly.
Reading WotLK-specific fields from Classic data consumed wrong bytes,
causing pointCount to read garbage and the entire update block to fail
— losing dozens of NPC spawns in multi-block packets.

Now tries Classic format first (pointCount at offset 12), then WotLK
(offset 20+), then compact fallback. Also fixes WotLK SMSG_SPELL_GO
hit/miss targets to use full uint64 GUIDs instead of PackedGuid, which
was the root cause of garbage missCount values (46, 64, 241).
2026-03-18 07:23:51 -07:00
Kelsi
6484dfc32d fix: gate spline verticalAccel/effectStartTime on PARABOLIC flag
The legacy UPDATE_OBJECT spline path was reading verticalAccel (float)
and effectStartTime (uint32) unconditionally, but these 8 bytes are
only present when SPLINEFLAG_PARABOLIC (0x00000800) is set. Without
the flag, the extra reads shifted the stream by 8 bytes, causing
pointCount to read garbage (e.g. 3323328650) and failing the entire
update block parse.
2026-03-18 07:05:17 -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
3c60ef8464 fix: add hex dump diagnostics to spell-go missCount parsing
When SMSG_SPELL_GO reads a suspiciously high missCount (>20), log
the surrounding packet bytes, castFlags, and position for debugging
the persistent offset error causing garbage miss counts (46, 48, 241).
2026-03-18 06:57:15 -07:00
Kelsi
c8922e4826 fix: stop player movement before game object interaction
Servers may reject CMSG_GAMEOBJ_USE or cancel the resulting pickup
spell cast if movement flags are still active. Now sends MSG_MOVE_STOP
to clear directional movement before the interaction packet. Also adds
diagnostic logging for GO interactions to help trace collection issues.
2026-03-18 06:49:43 -07:00
Kelsi
f8f514d28c fix: add $C (class) and $R (race) quest text placeholders
Quest dialogs were showing literal "$C" instead of the player's class
name. Added support for $c/$C (class) and $r/$R (race) placeholders
in both game_screen and quest_log_screen substitution functions.
2026-03-18 06:49:37 -07:00
Kelsi
e0346c85df fix: salvage spell-go hit data when miss targets are truncated
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
SMSG_SPELL_GO packets with unreasonably high miss counts (48, 118, 241)
were causing the entire packet to be discarded, losing all combat hit
data. Now salvage the successfully-parsed hit targets (needed for combat
text, health bars, animations) instead of discarding everything. Also
add spellId/hitCount to truncation warnings for easier diagnosis.
2026-03-18 06:23:03 -07:00
Kelsi
379ca116d1 fix: eliminate full spatial index rebuild on M2 instance removal
M2Renderer::removeInstance() was calling rebuildSpatialIndex() for every
single removal, causing 25-90ms frame hitches during entity despawns.
Now uses O(1) lookup via instanceIndexById, incremental spatial grid
cell removal, and swap-remove from the instance vector. The auxiliary
index vectors are rebuilt cheaply since they're small.
2026-03-18 06:20:24 -07:00
Kelsi
702155ff4f fix: correct SMSG_SPELL_GO REFLECT miss payload size (WotLK/TBC)
WotLK and TBC parsers were reading uint32+uint8 (5 bytes) for
SPELL_MISS_REFLECT entries, but the server only sends uint8
reflectResult (1 byte). This caused a 4-byte misalignment after every
reflected spell, corrupting subsequent miss entries and SpellCastTargets
parsing. Classic parser was already correct.
2026-03-18 06:20:18 -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
fabcde42a5 fix: clarify death dialog — auto-release label and resurrection hint
'Release in X:XX' implied a client-enforced forced release; renamed to
'Auto-release in X:XX' (server-driven) and added 'Or wait for a player
to resurrect you.' hint so players know they can stay dead without
clicking Release Spirit.
2026-03-18 05:39:42 -07:00
Kelsi
90843ea989 fix: don't set releasedSpirit_ optimistically in releaseSpirit()
Setting releasedSpirit_=true immediately on CMSG_REPOP_REQUEST raced
with PLAYER_FLAGS field updates that arrive from the server before it
processes the repop: the PLAYER_FLAGS handler saw wasGhost=true /
nowGhost=false and fired the 'ghost cleared' path, wiping corpseMapId_
and corpseGuid_ — so the minimap skull marker and the Resurrect from
Corpse dialog never appeared.

Ghost state is now driven entirely by the server-confirmed PLAYER_FLAGS
GHOST bit (and the login-as-ghost path), eliminating the race.
2026-03-18 05:35:23 -07:00
Kelsi
d0f544395e feat: add mounted/group/channeling/casting/vehicle macro conditionals
Extends evaluateMacroConditionals() with [mounted], [nomounted],
[group], [nogroup], [raid], [channeling], [nochanneling],
[channeling:SpellName], [casting], [nocasting], [vehicle], [novehicle].
2026-03-18 05:23:32 -07:00
Kelsi
4e13a344e8 feat: add buff:/nobuff:/debuff:/nodebuff: macro conditionals
Macro conditions now support checking aura presence:
  [buff:Power Word: Fortitude]  — player has the named buff
  [nobuff:Frost Armor]          — player does NOT have the named buff
  [debuff:Faerie Fire]          — target has the named debuff
  [nodebuff:Hunter's Mark]      — target does NOT have the named debuff

Name matching is case-insensitive. When a target override (@target etc.)
is active the check uses that unit's aura list instead of the player's.
2026-03-18 05:20:15 -07:00
Kelsi
a802e05091 feat: add /mark slash command for setting raid target icons
Adds /mark [icon], /marktarget, and /raidtarget slash commands that
set a raid mark on the current target. Accepts icon names (star,
circle, diamond, triangle, moon, square, cross, skull), numbers 1-8,
or "clear"/"none" to remove the mark. Defaults to skull when no
argument is given.
2026-03-18 05:16:14 -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
586408516b fix: correct character geoset group ranges for other-player equipment rendering
setOnlinePlayerEquipment used wrong geoset ID ranges for boots (402+ instead
of 501+), gloves (301+ instead of 401+), and chest/sleeves (501+ instead of
801+), and was missing bare-shin (502), bare-wrist (801), and bare-leg (1301)
defaults. This caused other players to render with missing shin/wrist geometry
and wrong geosets when wearing equipment (the "shin mesh" gap in status.md).

Now mirrors the CharacterPreview::applyEquipment logic exactly:
- Group 4 (4xx) forearms/gloves: default 401, equipment 401+gg
- Group 5 (5xx) shins/boots:    default 502, equipment 501+gg
- Group 8 (8xx) wrists/sleeves: default 801, equipment 801+gg
- Group 13 (13xx) legs/pants:   default 1301, equipment 1301+gg
2026-03-18 04:42:21 -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
167e710f92 feat: add /equipset macro command for saved equipment set switching
/equipset <name> equips a saved set by case-insensitive prefix match;
/equipset with no argument lists available sets in chat.
2026-03-18 03:53:59 -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
4025e6576c feat: implement /castsequence macro command
Supports: /castsequence [conds] [reset=N/target/combat] Spell1, Spell2, ...
Cycles through the spell list on successive button presses. State is keyed
by spell list so the same sequence shared across macros stays in sync.
2026-03-18 03:36:05 -07:00
Kelsi
df7150503b feat: /assist now accepts name and macro conditional arguments
/assist TankName targets whoever TankName is targeting; /assist [target=focus]
assists your focus target. Mirrors /target and /focus conditional support.
2026-03-18 03:31:40 -07:00
Kelsi
5d4b0b0f04 feat: show target-of-focus with health bar in focus frame
Healers and tanks can now see who their focus target is targeting,
with a compact percentage health bar — mirrors the ToT in the target frame.
2026-03-18 03:29:48 -07:00
Kelsi
a151531a2a feat: show health bar on target-of-target in target frame
The ToT health bar gives healers immediate % health readout of whoever
the target is attacking, without needing to click-through to that unit.
2026-03-18 03:28:06 -07:00
Kelsi
11c07f19cb feat: add macro conditional support to /cleartarget and /startattack
/cleartarget [dead] now clears target only when it meets conditions;
/startattack [harm,nodead] respects conditionals including target=mouseover.
2026-03-18 03:25:34 -07:00
Kelsi
6cd3c613ef feat: add macro conditional support to /target and /focus commands
/target [target=mouseover], /target [mod:shift] BossName; DefaultMob,
/focus [target=mouseover], and /focus PlayerName all now evaluate WoW
macro conditionals and resolve named/mouseover targets correctly.
2026-03-18 03:21:27 -07:00
Kelsi
e2a484256c feat: show spell icon on macro buttons via #showtooltip directive
- getMacroShowtooltipArg() parses the #showtooltip [SpellName] directive
- Action bar macro buttons now display the named spell's icon when
  #showtooltip SpellName is present at the top of the macro body
- For bare #showtooltip (no argument), derives the icon from the first
  /cast line in the macro (stripping conditionals and rank suffixes)
- Falls back to "Macro" text label only when no spell can be resolved
2026-03-18 03:16:05 -07:00
Kelsi
28d7d3ec00 feat: track mouseover on party frames; fix /cast !spell; update macro editor hint 2026-03-18 03:11:34 -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
d2b2a25393 feat: extend macro conditionals to /use command 2026-03-18 03:06:23 -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
c676d99fc2 feat: add /petattack, /petfollow, /petstay, /petpassive, /petaggressive macro commands
Adds the standard WoW pet control slash commands used in macros:
- /petattack     — attack current target
- /petfollow     — follow player
- /petstay / /pethalt — stop and hold position
- /petpassive    — set passive react mode
- /petdefensive  — set defensive react mode
- /petaggressive — set aggressive react mode
- /petdismiss    — dismiss the pet

All commands also appear in Tab-autocomplete.
2026-03-18 02:32:49 -07:00
Kelsi
ae3e57ac3b feat: add /cancelform, /cancelshapeshift, /cancelaura slash commands
These are standard WoW macro commands:

- /cancelform / /cancelshapeshift: exits current shapeshift form by
  cancelling the first permanent aura (flag 0x20) on the player
- /cancelaura <name|#id>: cancels a specific player buff by spell name
  or numeric ID (e.g. /cancelaura Stealth, /cancelaura #1784)

Also expand the Tab-autocomplete command list to include /cancelaura,
/cancelform, /cancelshapeshift, /dismount, /sit, /stand, /startattack,
/stopcasting, /target, and other commands that were previously missing.
2026-03-18 02:30:35 -07:00
Kelsi
c3be43de58 fix: skip #showtooltip and other # directives when executing macros
Macros often start with a #showtooltip or #show directive line; these
should not be executed as chat commands.  The firstMacroCommand() helper
now scans forward through the macro text, skipping blank lines and any
line starting with '#', and executes the first actual command line.

Applies to all three execution paths: left-click, keyboard shortcut,
and right-click Execute menu item.
2026-03-18 02:27:34 -07:00
Kelsi
db0f868549 feat: extend /use command to support bag/slot notation and equip slot numbers
Adds WoW macro-standard /use argument forms alongside the existing
item-name search:

- /use 0 <slot>   — backpack slot N (1-based, bag 0)
- /use 1-4 <slot> — equipped bag slot N (1-based bag index)
- /use <N>        — equip slot N (1-based, e.g. /use 16 = main hand)

These are the standard forms used in macros like:
  #showtooltip
  /use 13          (trinket 1)
  /cast Arcane Blast
2026-03-18 02:23:47 -07:00
Kelsi
b236a85454 docs: update status.md — water refraction fix, date 2026-03-18 2026-03-18 02:20:59 -07:00
Kelsi
fa3a5ec67e fix: correct water refraction barrier srcAccessMask to prevent VK_ERROR_DEVICE_LOST
The captureSceneHistory barrier was using srcAccessMask=0 with
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT when transitioning the swapchain
image from PRESENT_SRC_KHR to TRANSFER_SRC_OPTIMAL.  This does not
flush the GPU's color attachment write caches, causing VK_ERROR_DEVICE_LOST
on strict drivers (AMD, Mali) that require explicit cache invalidation
before transfer reads.

Fix: use VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT + COLOR_ATTACHMENT_OUTPUT
as the source mask so color writes are properly made visible to the
transfer unit before the image copy begins.

Also remove the now-unnecessary "requires FSR" restriction in the
settings UI — water refraction can be enabled independently of FSR.
2026-03-18 02:20:35 -07:00
Kelsi
8abb65a813 feat: execute macros via keyboard shortcuts; support numeric /cast spell IDs
Two companion improvements for the macro system:

- Keyboard shortcut handler now executes MACRO slots (1-0 keys) by running
  the first line of their text as a command, same as left-click
- /cast now accepts a numeric spell ID or #ID prefix (e.g. /cast 133,
  /cast #133) in addition to spell names — enables standard WoW macro
  syntax and direct spell ID testing
2026-03-18 02:14:10 -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
1588c1029a fix: add user feedback for ATTACKSWING_NOTSTANDING and CANT_ATTACK
Both handlers silently cleared state with no visible message, leaving the
player unsure why their attack failed.  Split the shared case block:

- NOTSTANDING: show "You need to stand up to fight." (rate-limited to 1.25s
  via the existing autoAttackRangeWarnCooldown_ guard), keep auto-attack
  active so it fires once the player stands.

- CANT_ATTACK: call stopAutoAttack() to end the attack loop (target is a
  critter, civilian, or already dead — no point retrying), then show "You
  can't attack that." with the same rate limiter.
2026-03-18 01:46:19 -07:00
Kelsi
36158ae3e3 fix: show macro ID in action bar tooltip and context menu header
Macro slots stored from SMSG_ACTION_BUTTONS had no tooltip and no context
menu header — hovering or right-clicking gave a blank result.  Add an
"else if MACRO" branch to both the tooltip and the popup-context-item so
that "Macro #N" is displayed in both places.  Clearing via right-click
still works via the existing "Clear Slot" item which was already outside
the type branches.
2026-03-18 01:42:07 -07:00
Kelsi
7a0c7241ba fix: parse macro action bar slots from SMSG_ACTION_BUTTONS
Macro slots (type 0x40 / 64) were silently dropped by the default branch
of the SMSG_ACTION_BUTTONS type switch, leaving the bar empty for any slot
a player had set to a macro.  ActionBarSlot::MACRO already existed and the
UI already rendered it; only the parser was missing the case.  Add
case 0x40 to map to ActionBarSlot::MACRO for Classic (type=64), TBC, and
WotLK formats, which all share the same 0x40 encoding for macros.
2026-03-18 01:35:39 -07:00
Kelsi
5801af41bc fix: correct Turtle WoW SMSG_INIT_WORLD_STATES format and remove dead minRepeatMs branch
Turtle WoW is Classic 1.12-based and uses the Classic packet format for
SMSG_INIT_WORLD_STATES (no areaId uint32 field before count), not WotLK
format.  Including it in the WotLK branch caused the parser to consume 4
bytes of the count+first-key as a phantom areaId, misaligning all world
state key/value pairs (BG scores, zone events, flag states).

Also remove the dead `turtleMode ? 150 : 150` branch in
performGameObjectInteractionNow — both arms were identical so the ternary
had no effect; replace with a constexpr constant.
2026-03-18 01:30:20 -07:00
Kelsi
57b44d2347 fix: clear craft queue on spell failure and all cast reset paths
craftQueueSpellId_ and craftQueueRemaining_ were already cleared in
cancelCast(), stopCasting(), and SMSG_CAST_RESULT failure, but were
missing from five other cast-abort paths:

- SMSG_SPELL_FAILURE (mid-cast interrupt): queue persisted after
  combat interruption, risking a ghost re-cast on the next SMSG_SPELL_GO
- handleCastFailed() (SMSG_CAST_FAILED): queue persisted if the server
  rejected a craft before it started
- Player login state reset: leftover queue from prior session survived
  into the new world session
- Same-map resurrection (SMSG_NEW_WORLD): queue persisted through
  spirit-healer resurrection teleport
- Regular world transfer (SMSG_NEW_WORLD): queue persisted across zone
  changes and dungeon portals
2026-03-18 01:15:04 -07:00
Kelsi
6be695078b fix: clear spell queue in stopCasting; fix SMSG_SPELL_DELAYED castTimeTotal; clear cast on same-map res
- stopCasting() (invoked by /stopcasting) now clears queuedSpellId_/
  queuedSpellTarget_ and craftQueueSpellId_/craftQueueRemaining_ so a
  queued spell cannot fire silently after the player explicitly cancels.
- SMSG_SPELL_DELAYED now extends castTimeTotal alongside castTimeRemaining
  for the local player, matching the existing other-unit handling and
  keeping the cast bar progress percentage accurate after server-imposed
  cast delays.
- Same-map resurrection path (SMSG_NEW_WORLD same-map) now resets casting,
  castIsChannel, currentCastSpellId, castTimeRemaining, and the spell queue
  as a defensive measure (player is dead and cannot be casting, but this
  ensures state is clean on respawn).
2026-03-18 00:59:15 -07:00
Kelsi
76ba428b87 fix: /target command selects nearest matching entity
Previously used arbitrary map-iteration order (last match), meaning
'/target Kobold' might target a far-away enemy instead of the closest.

Now computes squared distance for every prefix-matching entity and
keeps the nearest one, matching WoW's own /target behaviour.
2026-03-18 00:39:32 -07:00
Kelsi
60d5edf97f fix: cancel timed cast immediately on movement start
When the player starts moving (forward/backward/strafe/jump) while a
timed non-channeled cast is in progress, call cancelCast() before
sending the movement packet.  Previously the cast bar kept counting
down until the server sent SMSG_SPELL_FAILED, causing a visible lag.

Channeled spells are excluded (server ends those via MSG_CHANNEL_UPDATE).
Turning opcodes are excluded (turning while casting is allowed in WoW).
2026-03-18 00:25:04 -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
0f8852d290 fix: clear selfResAvailable_ when player releases spirit 2026-03-18 00:09:22 -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
395a8f77c4 fix: clear corpse reclaim delay on world reset and resurrection
Reset corpseReclaimAvailableMs_ to 0 in both world-teardown/re-login
and ghost-flag-cleared paths so the PvP delay countdown never bleeds
into subsequent deaths or sessions.
2026-03-17 23:57:47 -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
2acab47eee fix: correct corpse reclaim — SMSG_DEATH_RELEASE_LOC is graveyard, not corpse
Two bugs prevented "Resurrect from Corpse" from working:

1. SMSG_DEATH_RELEASE_LOC was overwriting corpseX_/Y_/Z_/MapId_ with the
   graveyard spawn point (where the ghost appears after releasing spirit),
   not the actual corpse location.  canReclaimCorpse() was therefore comparing
   the ghost's distance to the graveyard instead of the real corpse, so the
   button never appeared when the ghost returned to the death position.
   Fix: read and log the packet but leave corpseX_/Y_/Z_ untouched.

2. reclaimCorpse() fell back to playerGuid when corpseGuid_ == 0.
   CMSG_RECLAIM_CORPSE requires the corpse object's own GUID; the server
   looks it up by GUID and silently rejects an unknown one.
   Fix: gate reclaimCorpse() on corpseGuid_ being known (set when the
   corpse object arrives in SMSG_UPDATE_OBJECT), and add canReclaimCorpse()
   guard for the same.

Corpse position is now sourced only from:
  - Health-drop detection (primary, fires immediately on death)
  - SMSG_UPDATE_OBJECT CORPSE type (updates when object enters view range)
2026-03-17 23:44:55 -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
3e3bbf915e fix: parse SMSG_TRADE_STATUS_EXTENDED correctly for Classic/TBC
WotLK inserts a uint32 tradeId between isSelf and slotCount, and
appends uint32 createPlayedTime at the end of each slot (52-byte
trail vs 48 for Classic/TBC). Without the expansion check, Classic
and TBC parsers consumed tradeId as part of slotCount, resulting in
a bogus slot count and corrupted trade window item display.

Now gates the tradeId read and adjusts SLOT_TRAIL size based on
isActiveExpansion("wotlk").
2026-03-17 22:42:20 -07:00
Kelsi
87cb293297 fix: consume SpellCastTargets bytes after miss list in Classic/TBC SpellGo
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
Added skipClassicSpellCastTargets() and skipTbcSpellCastTargets() calls
in parseSpellGo() for both expansions, matching the same fix applied to
WotLK SpellGoParser and both SpellStartParsers. Prevents packet stream
misalignment for ground-targeted and AoE spells (Blizzard, Rain of
Fire, Flamestrike, etc.) where the server appends DEST_LOCATION or
other target fields after the hit/miss lists.
2026-03-17 22:29:02 -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
dd64724dbb fix: consume all SpellCastTargets bytes in WotLK SpellStartParser
Replaced partial UNIT/OBJECT-only flag handling with full WotLK
SpellCastTargets layout: UNIT/UNIT_MINIPET/CORPSE/GAMEOBJECT share
one PackedGuid, ITEM/TRADE_ITEM share one PackedGuid, SOURCE_LOCATION
and DEST_LOCATION are each PackedGuid+3floats (transport-relative),
STRING is null-terminated. Prevents byte-stream corruption on
ground-targeted AoE and similar multi-field target packets.
2026-03-17 22:20:03 -07:00
Kelsi
a4415eb207 fix: clamp pointCount in handleMonsterMoveTransport to prevent DoS
handleMonsterMoveTransport() read a server-supplied pointCount without
any bounds check before iterating. A malformed packet with
pointCount=0xFFFFFFFF would loop billions of times. All other parsers
(MonsterMoveParser::parse, TBC parseMonsterMove) cap at 1000 or 16384.

Added kMaxTransportSplinePoints=1000 cap with a LOG_WARNING, matching
the limit used by MonsterMoveParser::parse() in world_packets.cpp.
2026-03-17 22:08:25 -07:00
Kelsi
b00025918c feat: draw player facing arrow at minimap center
The minimap had a comment "skip self (already drawn as arrow)" but no
code that actually drew the arrow. Players had no visual indication of
which direction they were facing on the minimap.

Draws a chevron-shaped white/gold arrow at the minimap center:
- On fixed-north minimap: arrow rotates to match camera compass bearing
  (computed from camera forward vector: atan2(-fwd.x, fwd.y))
- On rotating minimap: arrow points straight up because the minimap
  already rotates to put camera-forward at the top
- Style: two filled triangles (tip+left half, tip+right half) with dark
  outline for readability against all map backgrounds
- Rendered last so it sits on top of all other minimap markers
2026-03-17 22:05:24 -07:00
Kelsi
c870460dea fix: wire Warden module tick, generateRC4Keys, and unload callbacks
The funcList_ dispatchers were populated by initializeModule() but the
public tick(), generateRC4Keys(), and unload() methods had their actual
call sites commented out as TODOs.

- tick(): now calls funcList_.tick(deltaMs) so the emulated module can
  run its internal periodic scheduler.
- generateRC4Keys(): now calls funcList_.generateRC4Keys(packet) so
  the Warden crypto stream is re-keyed as the module expects.
- unload(): now calls funcList_.unload(nullptr) before freeing module
  memory, allowing the module to clean up its own state.

All three paths already guard on !loaded_ || !funcList_.<fn> so they
are no-ops when the module is not loaded or Unicorn is unavailable.
2026-03-17 22:00:06 -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
b29d76bbc8 feat: highlight quest-starting items in loot window with gold indicator
Items with startQuestId != 0 now show:
- Gold outer glow border (2px) around the item icon
- Gold "!" badge in the top-right corner of the icon
- "Begins a Quest" label in gold on the second text line

Matches WoW's visual convention for quest-pickup items in loot rolls.
2026-03-17 21:17:22 -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
e62ae8b03e feat: add local time clock display below minimap coordinates
Shows current local time in HH:MM format in a small dimmed label just
below the coordinate display near the minimap. Uses localtime_r (POSIX)
with a _WIN32 fallback. The clock complements the existing coordinate
and zone name overlays, matching the WoW default UI minimap area.
2026-03-17 20:06:05 -07:00
Kelsi
63f4d10ab1 fix: apply interruptibility coloring to target-of-target cast bar
The ToT (target-of-target) cast bar was still using a fixed orange-yellow
color regardless of spell interruptibility. Now uses the same green/red
scheme as the target frame and nameplate cast bars: green = interruptible
(can Kick/Counterspell), red = not interruptible, both pulse at >80%.
2026-03-17 20:02:02 -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
614fcf6b98 feat: show orange nameplate border when hostile NPC is targeting player
When a hostile unit has UNIT_FIELD_TARGET pointing to the local player,
highlight its nameplate with an orange border so players can immediately
see which enemies are attacking them vs. attacking group members.

Priority: gold=selected, orange=targeting you, dark=default.
2026-03-17 19:47:45 -07:00
Kelsi
1f20f55c62 fix: set interruptible flag on channel start for non-player casters
MSG_CHANNEL_START for NPCs/bosses was leaving UnitCastState::interruptible
at its default (true) instead of checking Spell.dbc AttributesEx.
2026-03-17 19:45:45 -07:00
Kelsi
7c932559e0 fix: apply interruptibility coloring to boss frame cast bars
Boss encounter frames were still using the old fixed orange/red cast bar
color. Update them to match the target frame: green = interruptible,
red = SPELL_ATTR_EX_NOT_INTERRUPTIBLE, both pulse at >80% completion.
2026-03-17 19:44:48 -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
b8712f380d fix: show sub-zone name in minimap label using server-reported zone ID
The zone label above the minimap now preferentially uses the zone/area
name from getWorldStateZoneId() (populated via SMSG_INIT_WORLD_STATES)
rather than the renderer's map-level zone name. This means the label
correctly shows "Ironforge", "Wailing Caverns", etc. instead of always
showing the parent continent zone name.
2026-03-17 19:16:02 -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
4a439fb0d1 feat: add clock-sweep arc to buff bar and target aura icons
Aura icons on the player buff bar and the target frame now display a
WoW-style dark fan overlay that sweeps clockwise as the buff/debuff
elapses, providing instant visual feedback on remaining duration.
The sweep uses AuraSlot::maxDurationMs / getRemainingMs() — the same
data that already drives the numeric countdown — so no new state is
required. Only temporary auras (maxDurationMs > 0) show a sweep;
permanent buffs remain unaffected.
2026-03-17 19:04:40 -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
d558e3a927 fix: separate SMSG_PLAY_SPELL_IMPACT from SMSG_PLAY_OBJECT_SOUND and spawn impact visual
SMSG_PLAY_SPELL_IMPACT has a different wire format from SMSG_PLAY_OBJECT_SOUND:
it carries uint64 targetGuid + uint32 visualId (same as SMSG_PLAY_SPELL_VISUAL),
not uint32 soundId + uint64 sourceGuid.

Previously both were handled together, causing the target GUID low-bytes to be
misread as a sound ID and the visualId to be missed entirely.

Now each handler parses its own format correctly.  SMSG_PLAY_SPELL_IMPACT resolves
the target entity position and calls playSpellVisual() to spawn the M2 impact effect
at that location.
2026-03-17 18:26:55 -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
06ad676be1 fix: surface barber shop, NPC, and LFG autojoin errors in UIError overlay
Add addUIError() for remaining error-only chat-message cases:
- SMSG_BARBER_SHOP_RESULT non-zero result (not enough money, wrong
  location, must stand up)
- SMSG_NPC_WONT_TALK ("That creature can't talk to you right now")
- SMSG_LFG_AUTOJOIN_FAILED and SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER

Completes the UIError improvement pass: all server-reported failure
events now surface as the red on-screen overlay, not chat-only.
2026-03-17 18:08:27 -07:00
Kelsi
2d00f00261 fix: surface LFG/auction/chat/pet errors in UIError overlay
Add addUIError() alongside addSystemChatMessage() for:
- SMSG_CHAT_WRONG_FACTION / SMSG_CHAT_NOT_IN_PARTY / SMSG_CHAT_RESTRICTED
- SMSG_LFG_JOIN_RESULT failure, LFG proposal failure (state=0), LFG
  role check missing-role failure
- SMSG_AUCTION_COMMAND_RESULT error cases (bid/post/cancel/buyout)
- SMSG_PLAYERBINDERROR (hearthstone not bound / bind failed)
- SMSG_READ_ITEM_FAILED
- SMSG_PET_NAME_INVALID

Consistent with the rest of the error-overlay pass: players now see
these failures as the red on-screen overlay text, not just in chat.
2026-03-17 17:56:53 -07:00
Kelsi
cd39cd821f fix: show zone transfer failures and rename errors in UIError overlay
- SMSG_TRANSFER_ABORTED: all zone/instance portal rejection reasons shown as UIError
  (expansion required, instance full, too many instances, zone in combat, etc.)
2026-03-17 17:49:06 -07:00
Kelsi
8411c39eaf fix: surface rename/stable/durability loss errors in UIError overlay
- SMSG_CHAR_RENAME error: "Rename failed: [reason]" shown as UIError
- SMSG_STABLE_RESULT failure (0x09): stable error shown as UIError
- SMSG_DURABILITY_DAMAGE_DEATH: durability loss % shown as UIError overlay
2026-03-17 17:48:04 -07:00
Kelsi
5ad849666d fix: surface pet/raid/talent/instakill errors in UIError overlay
- SMSG_PET_TAME_FAILURE: "Failed to tame: [reason]" shown as UIError
- SMSG_RAID_GROUP_ONLY: "Must be in raid group" shown as UIError
- SMSG_RAID_READY_CHECK_ERROR: all ready check failures shown as UIError
- SMSG_RESET_FAILED_NOTIFY: instance reset failure shown as UIError
- SMSG_TALENTS_INVOLUNTARILY_RESET: talents reset notification shown as UIError
- SMSG_EQUIPMENT_SET_USE_RESULT failure: shown as UIError
- SMSG_SPELLINSTAKILLLOG (player victim): instakill notification shown as UIError
2026-03-17 17:45:45 -07:00
Kelsi
0f2f9ff78d fix: show group kick/party command failures in UIError overlay
- handleGroupUninvite: "You have been removed from the group." now shown as UIError
- handlePartyCommandResult: all party errors (group full, not leader, wrong faction,
  ignoring you, etc.) now also shown as UIError overlay
2026-03-17 17:43:10 -07:00
Kelsi
b22183b000 fix: surface fishing/BG/party/instance/zone notifications in UIError overlay
- Fishing bobber splash: "A fish is on your line!" shown as UIError (time-critical)
- SMSG_BATTLEFIELD_PORT_DENIED: shown as UIError
- SMSG_INSTANCE_RESET_FAILED: failure reason shown as UIError
- SMSG_GROUP_DESTROYED: "Party disbanded" shown as UIError
- SMSG_CORPSE_NOT_IN_INSTANCE: shown as UIError
- SMSG_ZONE_UNDER_ATTACK: "[Zone] is under attack!" shown as UIError
- SMSG_AREA_TRIGGER_MESSAGE: zone/area entry messages shown as UIError
2026-03-17 17:39:02 -07:00
Kelsi
220f1b177c fix: surface trainer/resurrect/innkeeper/difficulty errors in UIError overlay
- SMSG_TRAINER_BUY_FAILED: "Cannot learn [spell]" now appears as red overlay
- SMSG_RESURRECT_FAILED: all resurrection failure reasons shown as UIError
- SMSG_BINDZONEREPLY error: "Too far from innkeeper" shown as UIError
- SMSG_CHANGEPLAYER_DIFFICULTY_RESULT error: reason shown as UIError
- SMSG_INVENTORY_CHANGE_FAILURE case 1: level-gated equip error now calls
  addUIError before the early break, matching all other inventory error paths
2026-03-17 17:36:25 -07:00
Kelsi
495dfb7aae fix: show buy/sell vendor failures in UIError overlay
SMSG_BUY_FAILED ("Not enough money", "Sold out", etc.) and
SMSG_SELL_ITEM non-zero results now call addUIError() so the error
appears on screen alongside the chat message.
2026-03-17 17:25:27 -07:00
Kelsi
fba6aba80d fix: show inventory, mount, and socket errors in UIError overlay
Several server-reported action failures were posting to chat only
without firing the red on-screen UIError overlay:
- SMSG_INVENTORY_CHANGE_FAILURE: bag full, wrong slot, can't equip, etc.
- SMSG_MOUNTRESULT / SMSG_DISMOUNTRESULT: mount denied errors
- SMSG_QUESTLOG_FULL: quest log at capacity
- SMSG_SOCKET_GEMS_RESULT: gem socketing failure

All now call addUIError() in addition to addSystemChatMessage() so
players see the error immediately on screen without looking at chat.
2026-03-17 17:24:23 -07:00
Kelsi
dcf9aeed92 fix: show SMSG_CAST_FAILED reason in UIError overlay
handleCastFailed was only posting to chat; now also calls addUIError
so mid-cast server rejections (e.g. "Interrupted") show the same red
on-screen overlay as SMSG_CAST_RESULT failures already did.
2026-03-17 17:20:24 -07:00
Kelsi
caad20285b feat: add hover tooltips to character sheet stats panel
Hovering over Armor, primary stats (Strength/Agility/Stamina/
Intellect/Spirit), and secondary rating stats now shows a brief
description of the stat's in-game effect — matching WoW's native
character screen behavior.

Uses ImGui::BeginGroup/EndGroup to make multi-widget rows (stat +
green bonus) respond to a single IsItemHovered check.
2026-03-17 17:16:56 -07:00
Kelsi
d1a392cd0e feat: add colors for SKILL, LOOT, BG system, and monster chat types
Added distinct colors for chat types that previously fell through to
the gray default: SKILL (cyan), LOOT (light purple), GUILD_ACHIEVEMENT
(gold), MONSTER_WHISPER/RAID_BOSS_WHISPER (pink), RAID_BOSS_EMOTE
(orange), MONSTER_PARTY (blue), BG_SYSTEM_NEUTRAL/ALLIANCE/HORDE
(gold/blue/red), and AFK/DND (light gray).
2026-03-17 17:00:46 -07:00
Kelsi
1e80e294f0 feat: add Heroic and Unique-Equipped indicators to chat link tooltips
Chat item link tooltips now show "Heroic" (green) for items with
ITEM_FLAG_HEROIC_TOOLTIP (0x8) and "Unique-Equipped" for items with
ITEM_FLAG_UNIQUE_EQUIPPABLE (0x1000000), matching InventoryScreen.
"Unique" text is now gold-colored to match as well.
2026-03-17 16:56:37 -07:00
Kelsi
cb99dbaea4 feat: add elemental resistances and full spell descriptions to chat link tooltips
Chat item link tooltips now show per-school elemental resistances
(Holy/Fire/Nature/Frost/Shadow/Arcane) when non-zero, matching the
inventory tooltip. Spell effect text (Use/Equip/Chance on Hit) now
shows the full spell description instead of just the spell name,
consistent with InventoryScreen::renderItemTooltip.
2026-03-17 16:54:40 -07:00
Kelsi
7e6de75e8a feat: show skill, reputation, class and race requirements in chat link tooltips
Chat item link tooltips now match InventoryScreen for required-skill
(SkillLine.dbc), required-reputation (Faction.dbc), class restriction,
and race restriction. Red text when the player does not meet the
requirement, grey otherwise.
2026-03-17 16:47:33 -07:00
Kelsi
dab03f2729 feat: show item set name and bonuses in chat item link tooltips
Chat link tooltips (hover over item links in chat) were missing item set
information already shown in the inventory tooltip.  Now shows:
- Set name with equipped/total piece count (e.g. "Tier 9 (2/5)")
- Each set bonus with its piece-threshold, colored green when active
  and grey when inactive
- Falls back to "Set (id N)" when ItemSet.dbc is unavailable

Lazy-loads ItemSet.dbc on first hover; consistent with
InventoryScreen::renderItemTooltip formatting.
2026-03-17 16:43:57 -07:00
Kelsi
dee33db0aa feat: show gem socket slots and socket bonus in chat item link tooltips
Item tooltips shown when hovering chat links already displayed all stats,
spells, and flavor text, but gem sockets were missing.  Add the same
socket rendering used in the inventory tooltip:

- Iterate socketColor[0..2]; for each non-zero slot show a colored label
  (Meta / Red / Yellow / Blue Socket) in the socket's faction color
- Lazy-load SpellItemEnchantment.dbc to resolve the socketBonus enchant
  name; fall back to "(id N)" when the record is not found
- Consistent with InventoryScreen::renderItemTooltip formatting
2026-03-17 16:42:19 -07:00
Kelsi
973db16658 feat: add screen-space weather particle overlay (rain/snow/storm)
Weather type and intensity are already tracked from SMSG_WEATHER, but
only an icon was shown next to the zone name.  This adds a fullscreen
ImDrawList overlay that renders:
- Rain (type 1): diagonal rain streaks proportional to intensity
- Snow (type 2): gently swaying snowflakes with two-tone highlight
- Storm (type 3): heavy rain + dark fog-vignette on screen edges

Particles wrap at screen boundaries and are re-seeded on type or
resolution change.  Delta time is capped at 50 ms to prevent teleporting
after focus loss.  No heap allocations at runtime (static local arrays).
2026-03-17 16:34:39 -07:00
Kelsi
1f1925797f feat: show cooldown overlay on pet action bar spell buttons
Pet spell buttons now dim and display remaining cooldown time when a
spell is on cooldown, matching the feedback available on the player
action bar. Clicking a pet spell while it is on cooldown is also
suppressed to prevent spam-sending CMSG_PET_ACTION to the server.
Cooldown time appears as a text overlay (seconds or "Nm" for minutes)
and is also shown in the hover tooltip.
2026-03-17 15:59:27 -07:00
Kelsi
98dc2a0dc7 feat: show Max Level bar at max level instead of hiding XP bar
When nextLevelXp==0 and playerLevel>0, render a gold fully-filled bar
with centered "Max Level" text instead of hiding the XP bar entirely.
Fixes missing closing brace that caused all subsequent methods to fail
compilation.
2026-03-17 15:28:33 -07:00
Kelsi
c15ef915bf feat: add Ctrl+1..3 keyboard shortcuts for stance/form/presence switching
Ctrl+1, Ctrl+2, Ctrl+3 (up to Ctrl+8 for Druids with many forms) now
cast the Nth available stance spell for classes that use a stance bar.
Ordering matches the stance bar UI so visual and keyboard positions align.
Normal action bar keys 1–= are skipped when Ctrl is held to prevent
accidental spell casts instead of stance switches.
2026-03-17 15:18:04 -07:00
Kelsi
6d83027226 feat: add stance/form/presence bar for Warriors, Druids, Death Knights, Rogues, Priests
Renders a stance bar to the left of the main action bar showing the
player's known stance spells filtered to only those they have learned:
- Warrior: Battle Stance, Defensive Stance, Berserker Stance
- Death Knight: Blood Presence, Frost Presence, Unholy Presence
- Druid: Bear/Dire Bear, Cat, Travel, Aquatic, Moonkin, Tree, Flight forms
- Rogue: Stealth
- Priest: Shadowform

Active form detected from permanent player auras (maxDurationMs == -1).
Clicking an inactive stance casts the corresponding spell. Active stance
shown with green border/tint; inactive stances are slightly dimmed.
Spell name tooltips shown on hover using existing SpellbookScreen lookup.
2026-03-17 15:12:58 -07:00
Kelsi
4edc4017ed feat: show extra stats in equipped item comparison panel (shift-hover)
When shift-hovering an item link in chat to compare with equipped gear,
also display extra stats (hit/crit/haste/AP/SP/expertise) for the
currently-equipped item, matching what is shown for the hovered item.
2026-03-17 14:50:28 -07:00
Kelsi
3b79f44b54 feat: show item flavor/lore text in item tooltip
Display item description (flavor text) from SMSG_ITEM_QUERY_SINGLE_RESPONSE
at the bottom of item tooltips in gold color, matching WoW's standard
tooltip layout where lore text appears below stats and effects.
2026-03-17 14:44:15 -07:00
Kelsi
020ba134cd feat: show item spell effects (Use/Equip/Teaches) in item tooltip
Display Use, Equip, Chance on Hit, and Teaches spell effects from
SMSG_ITEM_QUERY_SINGLE_RESPONSE in item tooltips. Looks up spell
name from Spell.dbc via SpellbookScreen for readable descriptions.
2026-03-17 14:43:22 -07:00
Kelsi
03397ec23c feat: show extra item stats in tooltip (hit/crit/haste/sp/ap/expertise)
Display all ExtraStat entries from SMSG_ITEM_QUERY_SINGLE_RESPONSE in
the item tooltip (hit rating, crit rating, haste, spell power, attack
power, expertise, resilience, etc.). These were previously silently
discarded, making WotLK/TBC gear tooltips incomplete.
2026-03-17 14:42:00 -07:00
Kelsi
f04875514e feat: improve item tooltip with bind type, item level, weapon damage range, and required level
Add standard WoW tooltip fields that were previously missing:
- Bind type (Binds when picked up/equipped/used, Quest Item)
- Unique indicator
- Item Level XX
- Weapon damage range (e.g. '22 - 41 Damage  Speed 2.20') replacing bare DPS
- Damage per second sub-line in dimmed text
- Requires Level XX
2026-03-17 14:41:00 -07:00
Kelsi
8b57e6fa45 feat: add HONOR_GAIN floating combat text for PvP honor gains
Show '+X Honor' floating text in gold when SMSG_PVP_CREDIT is received,
matching WoW's native behavior. Also add HONOR_GAIN to the combat log
panel for a complete record. Previously only a chat message was added.
2026-03-17 14:38:57 -07:00
Kelsi
b6ea78dfab fix: show spell name in REFLECT floating combat text
REFLECT entries already stored the reflected spell ID but the floating
text display showed only "Reflected"/"You Reflect" without the name.
Now shows "Reflected: Fireball" or "Reflect: Frost Nova", matching the
pattern already used by INTERRUPT, DISPEL, and STEAL entries.
2026-03-17 14:26:10 -07:00
Kelsi
5513c4aad5 fix: apply skull-red color and "Lv ??" to level-0 mobs in focus frame
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
Consistent with the target frame fix: focus targets with level 0
(unknown/?? mobs) now show skull-red instead of grey, and display
"Lv ??" instead of "Lv 0".
2026-03-17 14:18:49 -07:00
Kelsi
39f4162ec1 fix: show skull-red color and "Lv ??" for unknown-level mobs in target frame
Level 0 in the update fields means the server hasn't sent or
the mob is undetectable (e.g. high-level raid bosses). Previously
these were colored grey (no-XP path) and displayed "Lv 0". Now
they correctly show skull-red and display "Lv ??" to match WoW.
2026-03-17 14:16:14 -07:00
Kelsi
8b9d626aec feat: show directional arrow on world map player marker
Replace the static filled circle with a red triangle arrow that
rotates to match the character's current facing direction.
Uses the same render-space yaw convention as the 3D scene so
the arrow matches in-world orientation.
2026-03-17 14:10:56 -07:00
Kelsi
b23dbc9ab7 feat: apply out-of-range red tint to ranged items on action bar
Extend the existing out-of-range check to cover ITEM slots whose
inventory type is Ranged (bow/gun/crossbow, 40 yd), RangedRight
(wand, 40 yd), or Thrown (30 yd). The check runs after barItemDef
is resolved so the inventory type is available.
2026-03-17 13:59:42 -07:00
Kelsi
5031351736 fix: add free-list to WardenEmulator heap allocator to prevent exhaustion
The bump-pointer allocator never reused freed blocks, causing the 16 MB
emulated heap to exhaust in long sessions even when blocks were freed.

- First-fit reuse from a free-list before advancing the bump pointer
- Coalesce adjacent free blocks to limit fragmentation
- Roll back the bump pointer when the top free block reaches it
- Reset allocator state on initialize() so re-runs start clean
2026-03-17 13:55:37 -07:00
Kelsi
ae40d393c3 feat: show tactical role badges in party frames; fix talent reset
- Add MT/MA/Asst badges to party frames (matching raid frame treatment)
- Clear learnedTalents_ on SMSG_TALENTS_INVOLUNTARILY_RESET so the
  talent screen stays accurate after a server-side talent wipe
2026-03-17 13:50:49 -07:00
Kelsi
f70df191a9 feat: show tactical role badges (MT/MA/Asst) in raid frames
Render "MT" (orange), "MA" (blue), and "A" (light blue) in the
bottom-left of each raid cell using member flags from SMSG_GROUP_LIST
and SMSG_REAL_GROUP_UPDATE (bits 0x02/0x04/0x01). Complements the
existing LFG role badges at bottom-right.
2026-03-17 13:47:53 -07:00
Kelsi
1daead3767 feat: implement SMSG_REAL_GROUP_UPDATE handler
Parse group type, member flags, and leader GUID instead of silently
discarding the packet. Updates partyData so group frames reflect
role changes and leadership transitions in real time.
2026-03-17 13:44:14 -07:00
Kelsi
a43a43ed8e fix: evict oldest minimap tile textures when cache exceeds 128 entries
Prevents unbounded GPU memory growth in long play sessions where the player
visits many zones. Tiles are inserted into a FIFO deque; when the count of
successfully-loaded tiles exceeds MAX_TILE_CACHE (128), the oldest entry is
destroyed and removed from both the cache map and the deque.

At 256×256×4 bytes per tile this caps minimap GPU usage at ~32 MB.
2026-03-17 13:38:18 -07:00
Kelsi
217edc81d9 feat: add item quality link colours to loot roll, loot notify, and loot all-passed messages 2026-03-17 13:33:07 -07:00
Kelsi
6260ac281e feat: extend item link quality colours to vendor purchase, pet feed, and LFG reward messages 2026-03-17 13:27:27 -07:00
Kelsi
29b5b6f959 feat: show item quality colours in loot, quest-item, and auction chat messages
Add buildItemLink() helper that formats |cff...|Hitem:...|h[Name]|h|r links so
the chat renderer draws item names in their quality colour (grey/white/green/
blue/purple/orange) with a small icon and tooltip on hover.

Applied to: loot received (SMSG_ITEM_PUSH_RESULT), looted from corpse
(handleLootRemoved), quest item count updates, and all three auction
house notifications (sold, outbid, expired).
2026-03-17 13:25:33 -07:00
Kelsi
4049f73ca6 refactor: replace raw console output with LOG_* macros in warden_emulator, transport_manager, keybinding_manager 2026-03-17 13:09:18 -07:00
Kelsi
bf5219c822 refactor: replace std::cout/cerr with LOG_* macros in warden_module.cpp
Convert 60+ raw console output calls to structured LOG_INFO/WARNING/ERROR
macros for consistent logging, proper timestamps, and filtering support.
Remove unused <iostream> include.
2026-03-17 13:04:25 -07:00
Kelsi
8169f5d5c0 feat: add audio feedback for level-up, achievements, duels, group invites, and inventory errors
Wire up remaining UISoundManager calls for milestone and notification events:
- playLevelUp() on SMSG_LEVELUP_INFO
- playAchievementAlert() on SMSG_ACHIEVEMENT_EARNED (self only)
- playTargetSelect() on duel request and group invite
- playError() on inventory change failure
2026-03-17 12:37:19 -07:00
Kelsi
119002626e feat: show chat message when a spell is removed from spellbook
handleRemovedSpell now displays "You have unlearned: [SpellName]."
matching the existing handleLearnedSpell feedback pattern.
2026-03-17 12:35:05 -07:00
Kelsi
6fbf5b5797 feat: add audio feedback for item loot, vendor buy/sell, and spell learning
Wire up remaining UISoundManager calls for core gameplay actions:
- playLootItem() on SMSG_ITEM_PUSH_RESULT and handleLootRemoved
- playPickupBag() on successful vendor purchase (SMSG_BUY_ITEM)
- playDropOnGround() on successful item sell (SMSG_SELL_ITEM)
- playQuestActivate() on trainer spell purchase success
2026-03-17 12:31:38 -07:00
Kelsi
a0b978f95b feat: add audio feedback for quest accept/complete and transaction errors
Wire up UISoundManager calls that were loaded but never invoked:
- playQuestActivate() on quest accept
- playQuestComplete() on server-confirmed quest completion
- playError() on trainer buy failure, vendor buy failure, and sell failure
2026-03-17 12:28:15 -07:00
Kelsi
8c3060f261 feat: show XP percentage in experience bar tooltip
The XP bar tooltip now displays current progress as a percentage
(e.g., "Current: 45000 / 100000 XP (45.0%)"), making it easier to
gauge leveling progress at a glance.
2026-03-17 12:17:23 -07:00
Kelsi
b80d88bded feat: add 'Hold Shift to compare' hint to ItemDef tooltip
The ItemQueryResponseData tooltip overload had this hint but the
primary ItemDef overload did not. Players hovering gear in their
inventory now see the comparison prompt when an equipped equivalent
exists.
2026-03-17 12:12:11 -07:00
Kelsi
1c3f2f4ae3 feat: show exploration XP as floating combat text
Area discovery XP was only shown in the system chat log. Now it also
appears as a floating "+XP" number like kill XP, giving immediate
visual feedback when discovering new zones.
2026-03-17 12:02:17 -07:00
Kelsi
67e6c9a984 fix: TBC parseMailList returns true on empty mailbox for consistency
WotLK and Classic parsers return true on success regardless of mail
count, but TBC returned !inbox.empty() which falsely signals parse
failure on an empty mailbox, potentially causing callers to skip
valid empty-mailbox state.
2026-03-17 11:58:20 -07:00
Kelsi
9750110436 fix: complete item tooltip stat comparison for all secondary stats
The shift-hover gear comparison was missing secondary stat types
(Defense, Dodge, Parry, Block Rating, Hit/Crit/Haste variants,
Healing, Spell Damage, Spell Pen) — only 10 of 22 stat types had
labels. Also adds full extra stats and DPS comparison to the
ItemQueryResponseData tooltip overload (loot window) which had none.
2026-03-17 11:54:01 -07:00
Kelsi
c017c61d2c fix: remove unused syncCounts variable in Warden handler
Eliminates the -Wunused-variable warning for the syncCounts array
that was declared but never populated in the synchronous Warden
check response path.
2026-03-17 11:37:49 -07:00
Kelsi
ef5532cf15 fix: add TBC chat message parser to prevent 12-byte misalignment
TBC 2.4.3 SMSG_MESSAGECHAT has no senderGuid(u64) or unknown(u32)
prefix before type-specific data. The WotLK base parser reads these
12 bytes unconditionally, causing complete misalignment of all chat
message fields — every chat message on a TBC server would parse
garbage for sender, channel, and message content.
2026-03-17 11:23:37 -07:00
Kelsi
e1be8667ed fix: add TBC game object query parser for correct string count
TBC 2.4.3 SMSG_GAMEOBJECT_QUERY_RESPONSE has 2 extra strings after
name[4] (iconName + castBarCaption). WotLK has 3 (adds unk1). Without
this override, the WotLK parser's third readString() consumed bytes
from the data[24] fields, corrupting game object type-specific data
and breaking interactions with doors, chests, mailboxes, and
transports on TBC servers.
2026-03-17 11:20:50 -07:00
Kelsi
1b86f76d31 fix: add TBC overrides for quest giver status and channel packets
TBC 2.4.3 sends quest giver status as uint32 (like Classic), not uint8
(WotLK). Without this override, reading uint8 consumed only 1 of 4
bytes, misaligning all subsequent packet data and breaking quest
markers on NPCs.

TBC channel join/leave packets use Classic format (name+password only).
The WotLK base prepends channelId/hasVoice/joinedByZone, causing
servers to reject the malformed packets and breaking channel features.
2026-03-17 11:16:02 -07:00
Kelsi
dc8619464a fix: add TBC guild roster parser to avoid gender byte misalignment
TBC 2.4.3 SMSG_GUILD_ROSTER has the same rank structure as WotLK
(variable rankCount + goldLimit + bank tab permissions), but does NOT
include a gender byte per member (WotLK added it). Without this
override, TBC fell through to the WotLK parser which read a spurious
gender byte, causing every subsequent field in each member entry to
misalign.
2026-03-17 11:10:54 -07:00
Kelsi
a7f7c4aa93 feat: show power type names in combat log energize/drain entries
Combat log now shows specific power type names (Mana, Rage, Energy,
Focus, Happiness, Runic Power) instead of generic "power" for ENERGIZE
and POWER_DRAIN events. Uses the powerType field added to CombatLogEntry
in the previous commit.
2026-03-17 11:03:20 -07:00
Kelsi
01685cc0bb feat: add ghost mode visual overlay when player is dead
Apply a cold blue-grey fullscreen overlay when the player is in ghost
form, creating a desaturated, muted appearance that clearly signals the
death state. Uses the existing overlay pipeline infrastructure. Applied
in both parallel and non-parallel rendering paths, after underwater tint
but before brightness adjustment so UI elements remain unaffected.
2026-03-17 10:58:07 -07:00
Kelsi
2d53ff0c07 feat: show environmental damage type in combat text and log
Fall, lava, drowning, fatigue, slime, and fire damage now display their
specific type instead of generic "Environmental damage" in both floating
combat text and the combat log window. The envType byte from
SMSG_ENVIRONMENTAL_DAMAGE_LOG is propagated via the powerType field to
the display layer. Added powerType to CombatLogEntry for consistent
access in the persistent combat log.
2026-03-17 10:54:07 -07:00
Kelsi
1152a70201 fix: handle transport data in other player movement packets
Other players on transports (boats, zeppelins, trams) were not properly
tracked because handleOtherPlayerMovement() did not read transport data
from MSG_MOVE_* packets. This caused entities to slide off transports
between movement updates since no transport attachment was established.

Now reads the transport GUID and local offset from the packet using
expansion-aware wire flags (0x200 for WotLK/TBC, 0x02000000 for
Classic/Turtle), registers a transport attachment so the entity follows
the transport smoothly via updateAttachedTransportChildren(), and clears
the attachment when the player disembarks.
2026-03-17 10:40:35 -07:00
Kelsi
f5297f9945 feat: show craft queue count on cast bar during batch crafting 2026-03-17 10:30:18 -07:00
Kelsi
9aed192503 fix: load skill DBCs on login and handle loot slot changes
- Load SkillLine.dbc and SkillLineAbility.dbc during SMSG_INITIAL_SPELLS
  so isProfessionSpell() works immediately without visiting a trainer
- Implement SMSG_LOOT_SLOT_CHANGED handler to remove items taken by
  other players in group loot, keeping the loot window in sync
2026-03-17 10:20:29 -07:00
Kelsi
7b03d5363b feat: profession crafting improvements and combat sound fixes
- Suppress spell sounds for profession/tradeskill spells (crafting is silent)
- Add craft quantity UI to profession trainer: recipe selector, quantity
  input, Create button, and Stop button for active queue
- Known recipes show Create button to cast directly from trainer window
- Craft queue auto-recasts on CREATE_ITEM completion, cancels on failure
- Fix missing combat sounds: player spell impacts on enemies, enemy spell
  cast sounds targeting player, instant melee ability weapon sounds
2026-03-17 10:12:49 -07:00
Kelsi
502d506a44 feat: make bag windows draggable 2026-03-17 10:12:35 -07:00
Kelsi
192c6175b8 feat: add brightness slider to Video settings
Black overlay dims below 50%, white overlay brightens above 50%.
Persisted in settings.cfg, with restore-defaults support.
2026-03-17 09:04:53 -07:00
Kelsi
cf3fe70f1f fix: hide window on shutdown to prevent OS force-close dialog
SDL_HideWindow immediately on shutdown so the OS doesn't show a
"not responding" dialog during the slow cleanup process.
2026-03-17 09:04:47 -07:00
Kelsi
3667ff4998 fix: use uniform 22-byte loot item size for Classic/TBC/Turtle
SMSG_LOOT_RESPONSE items include randomSuffix and randomPropertyId
fields across all expansions, not just WotLK. Using 14-byte size for
Classic/TBC caused item data to be read at wrong offsets.
2026-03-17 09:04:40 -07:00
Kelsi
203514abc7 fix: correct minimap orientation and arrow direction, compact key ring UI
Remove stray X-flip in minimap display shader that mirrored the map
horizontally (West on right instead of East). Fix arrow rotation
fallback path (missing negation) and add character-facing-relative
arrow in rotateWithCamera mode.

Compact key ring: 24px slots in 8-column grid, only show rows with
items, hide when empty. Add Show Key Ring toggle in Settings with
persistence.
2026-03-17 08:18:46 -07:00
Kelsi
e38324619e fix: resolve missing Classic spell icons on action bar and talents
When Classic is active, loadDBC("Spell.dbc") finds the WotLK base DBC
(234 fields) since no binary Classic DBC exists. The Classic layout says
IconID is at field 117, but in the WotLK DBC that field contains
unrelated data (mostly zeros). This caused all spell icon lookups to
fail silently.

Now detects the DBC/layout field count mismatch and falls back to the
WotLK field index 133, which is correct for the base DBC. Classic spell
IDs are a subset of WotLK, so the icon mapping works correctly.
2026-03-17 07:42:01 -07:00
Kelsi
8378eb9232 fix: correct sync Warden MODULE check returning 0x01 instead of 0x00
The sync path's MODULE handler was returning 0x01 (module found) for
unwanted cheat DLLs (WPESPY, TAMIA, PRXDRVPE, etc.) instead of 0x00
(not found). Since VMaNGOS compares the result as a boolean, returning
any non-zero value for a cheat module tells the server "this cheat DLL
is loaded," triggering Warden penalties that accumulate into a kick
after ~3-5 minutes.

Also adds ±4KB hint window search to searchCodePattern for faster
PAGE_A resolution without full brute-force, and restores the turtle
PAGE_A fallback (confirmed patterns are runtime-patched offsets not
present in the on-disk PE).
2026-03-17 07:19:37 -07:00
Kelsi
ad511dad5e fix: correct KUSER_SHARED_DATA field offsets for Warden anticheat
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
Multiple fields were at wrong offsets causing MEM_CHECK comparison
failures against expected Windows 7 SP1 values. Key fixes:
- LargePageMinimum: 0x248→0x244
- NtProductType at 0x264 was 0, now 1 (VER_NT_WORKSTATION)
- ProductTypeIsValid at 0x268 was missing
- ProcessorFeatures at 0x274 was clobbered by misplaced NtProductType
- NumberOfPhysicalPages: 0x300→0x2E8
- ActiveConsoleId at 0x2D8 was 4, now 1
- Added SuiteMask, NXSupportPolicy, and other missing fields
2026-03-16 20:55:30 -07:00
Kelsi
e3c2269b16 fix: increase descriptor pool sizes to prevent Vulkan crash
Terrain pool 16384→65536, WMO pool 8192→32768. The previous sizes
were too small for the load/unload radii, causing pool exhaustion
and a hard crash when streaming terrain on large maps.
2026-03-16 17:46:32 -07:00
Kelsi
6fd32ecdc6 fix: skip Warden HASH_RESULT on strict servers when no CR match
Sending a wrong hash to AzerothCore/WotLK servers triggers an
account ban. When no pre-computed challenge-response entry matches
the server seed, skip the response entirely so the server times out
with a kick (recoverable) instead of verifying a bad hash and
banning (unrecoverable). Turtle/Classic servers remain unchanged
as they only log Warden failures.

Also adds RX silence detection and fixes Turtle isTurtle flag
propagation in MEM_CHECK path.
2026-03-16 17:38:25 -07:00
Kelsi
a3279ea1ad fix: async Warden PAGE_A/PAGE_B checks to prevent main-loop stalls
Move 5-second brute-force HMAC-SHA1 code pattern searches to a
background thread via std::async. The main loop now detects PAGE_A/B
checks, launches the response builder async, and drains the result
in update() — encrypting and sending on the main thread to keep
wardenCrypto_ RC4 state thread-safe.

Also adds Turtle WoW PE binary support (isTurtle flag, dedicated exe
search, runtime patches), searchCodePattern with result caching,
writeLE32 public API, and Warden scan entry verification.
2026-03-16 16:46:29 -07:00
Kelsi
f0a515ff9c fix: stabilize classic/turtle world session handling
Some checks failed
Build / Build (arm64) (push) Has been cancelled
Build / Build (x86-64) (push) Has been cancelled
Build / Build (macOS arm64) (push) Has been cancelled
Build / Build (windows-arm64) (push) Has been cancelled
Build / Build (windows-x86-64) (push) Has been cancelled
Security / CodeQL (C/C++) (push) Has been cancelled
Security / Semgrep (push) Has been cancelled
Security / Sanitizer Build (ASan/UBSan) (push) Has been cancelled
2026-03-15 06:13:36 -07:00
Kelsi
43ebae217c fix: align turtle world packet parsing
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
2026-03-15 03:40:58 -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
4dba20b757 fix: avoid unsigned subtraction checks in packet bounds 2026-03-14 22:27:42 -07:00
Kelsi
eea3784976 fix: harden turtle movement parsing and warden fallback 2026-03-14 22:18:28 -07:00
Kelsi
f44ef7b9ea fix: optimize turtle monster move wrapped parsing 2026-03-14 22:01:26 -07:00
Kelsi
bce1f4d211 fix: reject malformed monster move payloads 2026-03-14 21:52:03 -07:00
Kelsi
f57893a459 fix(combatlog): reject truncated spell damage log tails 2026-03-14 21:52:03 -07:00
Kelsi
f07b730473 fix(combatlog): reject truncated resist logs 2026-03-14 21:52:03 -07:00
Kelsi
5c8a2afa35 fix(combatlog): accept extended TBC spell damage payloads 2026-03-14 21:52:03 -07:00
Kelsi
385ac1e66c fix(combatlog): reject truncated instakill logs without spell id 2026-03-14 21:52:03 -07:00
Kelsi
83a368aa85 fix(combatlog): reject spell start packets missing target flags 2026-03-14 21:52:03 -07:00
Kelsi
f4ecef2ec5 fix(combatlog): reject truncated classic attacker-state packets 2026-03-14 21:52:03 -07:00
Kelsi
4d4e5ed3b9 fix(combatlog): enforce TBC attacker-state packet bounds 2026-03-14 21:52:03 -07:00
Kelsi
71e34e41b7 fix(combatlog): clamp attacker-state subdamage count to payload 2026-03-14 21:52:03 -07:00
Kelsi
90bc9118f9 fix(combatlog): validate packed GUID bounds in spell energize log 2026-03-14 21:52:03 -07:00
Kelsi
80d59a80aa fix(combatlog): relax packed GUID minimum-size gates 2026-03-14 21:52:03 -07:00
Kelsi
c9467778dc fix(combatlog): enforce TBC spell damage/heal packet bounds 2026-03-14 21:52:03 -07:00
Kelsi
6ccfdc9d11 fix(combatlog): validate packed GUID bounds in spell damage/heal logs 2026-03-14 21:52:03 -07:00
Kelsi
24a63beb3c fix(combatlog): reject truncated spell start target GUIDs 2026-03-14 21:52:03 -07:00
Kelsi
bcfdcce062 fix(combatlog): reject truncated spell go packets missing counts 2026-03-14 21:52:03 -07:00
Kelsi
b24da8463c fix(combatlog): avoid partial spell miss log entries on truncation 2026-03-14 21:52:03 -07:00
Kelsi
f0ba85fa80 fix(combatlog): reset spell go parser output before decode 2026-03-14 21:52:03 -07:00
Kelsi
6b290009aa fix(combatlog): fail classic and tbc spell go parse on truncation 2026-03-14 21:52:03 -07:00
Kelsi
e0ac81450d fix(combatlog): enforce full spell start fixed-field bounds 2026-03-14 21:52:03 -07:00
Kelsi
918762501f fix(combatlog): fail spell go parse on truncated target lists 2026-03-14 21:52:03 -07:00
Kelsi
ffa6dda4d9 fix(combatlog): validate packed GUID bounds in attacker state parsers 2026-03-14 21:52:03 -07:00
Kelsi
5a9be91fac fix(combatlog): validate packed guid bounds in spell go parser 2026-03-14 21:52:03 -07:00
Kelsi
4561eb8696 fix(combatlog): validate packed GUID bounds in spell start parser 2026-03-14 21:52:03 -07:00
Kelsi
c9858655f6 fix(combatlog): validate packed guid bounds in classic spell cast parsers 2026-03-14 21:52:03 -07:00
Kelsi
69ff91e9a2 fix(combatlog): validate packed GUID bounds in spell cast parsers 2026-03-14 21:52:03 -07:00
Kelsi
5ecc46623a fix(combatlog): consume full spell go target lists when capped 2026-03-14 21:52:03 -07:00
Kelsi
c90c8fb8cf fix(combatlog): parse full spell miss target lists 2026-03-14 21:52:03 -07:00
Kelsi
a962422b12 fix(combatlog): map alternate immune2 spell miss value 2026-03-14 21:52:03 -07:00
Kelsi
753f4ef1be fix(combatlog): map immune2 spell miss results correctly 2026-03-14 21:52:03 -07:00
Kelsi
5911b8eb01 fix(combatlog): show resisted amount from resist log packets 2026-03-14 21:52:03 -07:00
Kelsi
5575fc6f28 fix(combatlog): preserve unknown source for environmental entries 2026-03-14 21:52:03 -07:00
Kelsi Rae Davis
ecb56e9a35
Merge pull request #17 from Kelsidavis/maint
Merge branch 'master' into maint
2026-03-14 21:47:07 -07:00
Kelsi
d34a3967f6 Merge branch 'master' into maint 2026-03-14 09:24:19 -07:00
Kelsi
075b4c1772 fix(gameplay): tighten TB elevator bounds and reset stale combat visuals
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
2026-03-14 09:19:16 -07:00
Kelsi
448560a0d2 fix(ui): correct minimap center compass arrow orientation 2026-03-14 09:06:55 -07:00
Kelsi
38210ec186 fix(gameplay): keep timeout animation stable on repeated presses and harden M2 elevator sync 2026-03-14 09:02:20 -07:00
Kelsi
f7a996ab26 fix(ui): avoid double-processing bag toggle hotkey 2026-03-14 08:44:45 -07:00
Kelsi
2c32b72f95 feat(ui): show keyring in inventory 2026-03-14 08:42:25 -07:00
Kelsi
800862c50a fix(ui): cache ghost opacity updates to state changes 2026-03-14 08:31:08 -07:00
Kelsi
cebca9a882 fix(gameplay): stabilize run animation and clean ghost/death visuals 2026-03-14 08:27:32 -07:00
Kelsi
1a4b21955c fix(transport): add Thunder Bluff lift-specific M2 attach bounds 2026-03-14 08:09:23 -07:00
Kelsi
422ff99b2a fix(ui): close trainer window immediately on close request 2026-03-14 07:43:52 -07:00
Kelsi
f235b8641f fix(animation): avoid forced stand reset after spline move 2026-03-14 07:41:50 -07:00
Kelsi
9cd52c4dd7 fix(loot): gate auto-loot sends per loot session 2026-03-14 07:31:15 -07:00
Kelsi
c1baffadf0 fix(input): release mouse on stalls and clean quest keybind duplication 2026-03-14 07:29:39 -07:00
Kelsi
013f6be162 fix(mail): correct WotLK mail list attachment parsing 2026-03-14 07:14:15 -07:00
Kelsi
5fa5020af5 fix(mail): use attachment item guid for WotLK take item 2026-03-14 07:11:18 -07:00
Kelsi
5a10ae9df0 fix(trade): allow accept after peer accepts first 2026-03-14 07:02:52 -07:00
Kelsi
aa9dc128d8 fix(chat): make /r resolve last whisper sender reliably 2026-03-14 06:56:16 -07:00
Kelsi
76f54bbd2c fix(minimap): keep a single player direction indicator 2026-03-14 06:48:14 -07:00
Kelsi
5b195781ad fix(death): restore corpse reclaim and enforce ghost grayscale 2026-03-14 06:43:49 -07:00
Kelsi
7b5ead8bd9 fix(terrain): drop chunks when material descriptor allocation fails 2026-03-14 06:34:09 -07:00
Kelsi
e7c1000fc2 fix(minimap): remove duplicate shader direction arrow 2026-03-14 06:22:32 -07:00
Kelsi
0b1ea464bb fix(render): premultiply glow sprite color by alpha 2026-03-14 06:10:20 -07:00
Kelsi
93cc092ee1 fix(render): narrow glow-card mesh suppression 2026-03-14 06:07:05 -07:00
Kelsi
e0d7cba330 fix(render): replace torch and lantern glow cards with sprites 2026-03-14 06:02:15 -07:00
Kelsi
d7a0a9f675 fix(wmo): disable portal traversal for outdoor camera groups 2026-03-14 05:57:27 -07:00
Kelsi
a2f9ccc9b9 fix(vendor): detect repair NPC flag correctly 2026-03-14 05:39:00 -07:00
Kelsi
7e7ad325dc fix(vendor): request list inventory for repair gossip 2026-03-14 05:31:17 -07:00
Kelsi
e0828cc35c fix(input): don't block hotkeys on WantCaptureKeyboard 2026-03-14 05:20:23 -07:00
Kelsi
251ed7733b fix(loot): send GAMEOBJ_REPORT_USE when supported
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
2026-03-14 04:47:34 -07:00
Kelsi
5a63d3799c fix(loot): retry delayed loot for gameobject use 2026-03-14 04:41:46 -07:00
Kelsi
5be80a9cc6 fix(chat): suppress hotkeys while UI captures keyboard 2026-03-14 04:36:25 -07:00
Kelsi
94e5855d53 fix(ui): prefer quest objective gameobjects on right-click 2026-03-14 04:33:46 -07:00
Kelsi
f0c01bf352 Merge branch 'maint' 2026-03-14 04:31:13 -07:00
Kelsi
a5b877de67 fix(chat): suppress alnum hotkeys while typing 2026-03-14 03:54:26 -07:00
Kelsi
14e58eaa01 fix(chat): disable shortcuts while typing 2026-03-14 03:49:42 -07:00
Kelsi
aed1f2ad21 fix(input): reserve movement keys for movement 2026-03-14 03:47:14 -07:00
Kelsi
a09a24e58e Revert "fix(input): block WASD and Q/E movement"
This reverts commit 8391f93ca6.
2026-03-14 03:46:10 -07:00
Kelsi
8391f93ca6 fix(input): block WASD and Q/E movement 2026-03-14 03:39:49 -07:00
Kelsi
565c78d141 fix(input): reserve Q for strafe-left 2026-03-14 03:37:24 -07:00
Kelsi
6cfb439fd6 fix(vulkan): defer resource frees until frame fence 2026-03-14 03:32:31 -07:00
Kelsi
c1b66f73c5 fix(vulkan): defer resource frees until frame fence 2026-03-14 03:25:52 -07:00
Kelsi Rae Davis
edd934a3e3
Merge pull request #16 from Kelsidavis/maint
Maint
2026-03-14 02:31:54 -07:00
Kelsi
6a7071fd64 fix(combatlog): validate classic spell damage and heal GUIDs 2026-03-14 02:10:14 -07:00
Kelsi
011a148105 fix(combatlog): validate packed damage shield GUIDs 2026-03-14 02:01:07 -07:00
Kelsi
f6d8c01779 fix(combatlog): validate packed spell miss GUIDs 2026-03-14 01:54:01 -07:00
Kelsi
b059bbcf89 fix(combatlog): parse classic spell damage shield GUIDs as packed 2026-03-14 01:47:06 -07:00
Kelsi
468880e2c8 fix(combatlog): validate packed resist log GUIDs 2026-03-14 01:39:53 -07:00
Kelsi
0968a11234 fix(combatlog): validate packed instakill GUIDs 2026-03-14 01:32:45 -07:00
Kelsi
a48f6d1044 fix(combatlog): parse classic immune log GUIDs as packed 2026-03-14 01:25:47 -07:00
Kelsi
0fc887a3d2 fix(combatlog): validate packed proc log GUIDs 2026-03-14 01:18:28 -07:00
Kelsi
dbdc45a8a9 fix(combatlog): validate packed dispel-family GUIDs 2026-03-14 01:10:43 -07:00
Kelsi
bd8c46fa49 fix(combatlog): parse classic dispel failed GUIDs as packed 2026-03-14 01:00:56 -07:00
Kelsi
1fa2cbc64e fix(combatlog): parse classic dispel and spellsteal GUIDs as packed 2026-03-14 00:53:42 -07:00
Kelsi
fd8ea4e69e fix(combatlog): parse classic proc log GUIDs as packed 2026-03-14 00:45:50 -07:00
Kelsi
8ba5ca5337 fix(combatlog): parse classic instakill log GUIDs as packed 2026-03-14 00:38:22 -07:00
Kelsi
9c3b5d17cf fix(combatlog): parse classic resist log GUIDs as packed 2026-03-14 00:31:35 -07:00
Kelsi
ed5134d601 fix(combatlog): parse classic spelllogexecute GUIDs as packed 2026-03-14 00:24:21 -07:00
Kelsi
a147347393 fix(combattext): honor health leech multipliers 2026-03-14 00:16:28 -07:00
Kelsi Rae Davis
f5386060b6
Merge pull request #15 from Kelsidavis/maint
Maint
2026-03-14 00:13:47 -07:00
Kelsi
209f8db382 fix(combattext): honor power drain multipliers 2026-03-14 00:06:05 -07:00
Kelsi
64483a31d5 fix(combattext): show power drain separately from damage 2026-03-13 23:56:44 -07:00
Kelsi
e9d2c43191 fix(combatlog): validate classic spelllogexecute packed GUIDs 2026-03-13 23:47:57 -07:00
Kelsi
842771cb10 fix(combatlog): validate tbc spelllogexecute effect GUIDs 2026-03-13 23:40:39 -07:00
Kelsi
57265bfa4f fix(combattext): render evade results explicitly 2026-03-13 23:32:57 -07:00
Kelsi
6095170167 fix(combattext): correct reflect miss floating text 2026-03-13 23:24:09 -07:00
Kelsi
3f1083e9b5 fix(combatlog): consume reflect payload in spell-go miss entries 2026-03-13 23:15:56 -07:00
Kelsi
77d53baa09 fix(combattext): render deflect and reflect miss events 2026-03-13 23:08:49 -07:00
Kelsi
dceaf8f1ac fix(combattext): show aura names for dispel and spellsteal 2026-03-13 23:00:49 -07:00
Kelsi
5392243575 fix(combatlog): stop treating spell break logs as damage shield hits 2026-03-13 22:53:04 -07:00
Kelsi
3e4708fe15 fix(combatlog): parse classic spell miss GUIDs as packed 2026-03-13 22:45:59 -07:00
Kelsi
46b297aacc fix(combatlog): consume reflect payload in spell miss logs 2026-03-13 22:38:35 -07:00
Kelsi
21762485ea fix(combatlog): guard truncated spell energize packets 2026-03-13 22:30:25 -07:00
Kelsi
3ef5b546fb fix(combatlog): render instakill events explicitly 2026-03-13 22:22:00 -07:00
Kelsi
5be55b1b14 fix(combatlog): validate full TBC spell-go header 2026-03-13 22:14:04 -07:00
Kelsi
cf68c156f1 fix(combatlog): accept short packed spell-go packets 2026-03-13 21:16:24 -07:00
Kelsi
16c8a2fd33 fix(combatlog): parse packed spell-go hit target GUIDs 2026-03-13 21:08:00 -07:00
Kelsi
d4d876a563 fix(combatlog): list all dispelled and stolen auras in system messages 2026-03-13 21:00:34 -07:00
Kelsi
c5e7dde931 fix(combatlog): parse proc log GUIDs in classic and tbc 2026-03-13 20:52:34 -07:00
Kelsi
f09913d6d2 fix(combatlog): render interrupt floating combat text 2026-03-13 20:45:26 -07:00
Kelsi
d61bb036a7 fix(combatlog): parse classic dispel and steal aura entries correctly 2026-03-13 20:38:59 -07:00
Kelsi
1214369755 fix(combatlog): log outgoing proc resist events 2026-03-13 20:30:39 -07:00
Kelsi
91dc45d19e fix(combatlog): dedupe duplicate spellsteal aura logs 2026-03-13 20:23:24 -07:00
Kelsi
3edf280e06 fix(combatlog): log all dispelled and stolen auras 2026-03-13 20:14:02 -07:00
Kelsi
98c195fb8e fix(combatlog): preserve spellsteal in dispel log handler 2026-03-13 20:06:39 -07:00
Kelsi
c45951b368 fix(combatlog): distinguish spellsteal from dispel 2026-03-13 19:58:37 -07:00
Kelsi
a48eab43b8 fix(combatlog): show resist entries for resist log packets 2026-03-13 19:51:21 -07:00
Kelsi
3fa495d9ea fix(combatlog): preserve spell ids in spell miss events 2026-03-13 19:43:50 -07:00
Kelsi
23023dc140 fix(combatlog): keep spell-go miss metadata 2026-03-13 19:36:42 -07:00
Kelsi
db681ec4c6 fix(combatlog): target drain and leech self-gain events correctly 2026-03-13 19:28:22 -07:00
Kelsi
6af9f90f45 ignore 2026-03-13 19:22:55 -07:00
Kelsi
e51b215f85 feat: add DISPEL and INTERRUPT combat log entries for dispel/spellsteal/interrupt events
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
2026-03-13 12:03:07 -07:00
Kelsi
ffef3dda7e fix: pass actual GUIDs in SMSG_SPELL_CHANCE_PROC_LOG combat log entries 2026-03-13 11:57:45 -07:00
Kelsi
38111fe8c0 fix: pass actual GUIDs in SMSG_SPELLLOGEXECUTE power drain and health leech combat log entries 2026-03-13 11:55:23 -07:00
Kelsi
d48ead939b fix: pass actual GUIDs in SMSG_SPELLINSTAKILLLOG combat log entries 2026-03-13 11:54:14 -07:00
Kelsi
0982f557d2 fix: pass actual GUIDs for environmental damage and energize combat log entries
SMSG_ENVIRONMENTALDAMAGELOG and SMSG_ENVIRONMENTAL_DAMAGE_LOG now pass
dstGuid=victimGuid with srcGuid=0 (no caster for env damage), ensuring
the combat log shows an empty source name rather than the player's
current target.

SMSG_SPELLENERGIZERLOG now passes casterGuid/victimGuid so the log
correctly attributes mana/energy restoration to the actual caster
rather than the player's current target.
2026-03-13 11:52:31 -07:00
Kelsi
3bdd3f1d3f fix: pass actual GUIDs and spellId to SPELLDAMAGESHIELD and SPELLORDAMAGE_IMMUNE combat log
These handlers already had casterGuid/victimGuid available but were
discarding the packet spellId and not passing GUIDs to addCombatText.
Now the combat log entries show the correct attacker/victim names and
the spell that caused the reflect/immune event.
2026-03-13 11:51:07 -07:00
Kelsi
8213de1d0f fix: pass actual GUIDs to combat log in SPELLLOGMISS and PROCRESIST handlers
SMSG_SPELLLOGMISS and SMSG_PROCRESIST already parsed casterGuid /
victimGuid from the packet but discarded them when calling addCombatText.
Now pass those GUIDs so combat log entries record the actual
attacker/victim names rather than falling back to current target.
2026-03-13 11:50:00 -07:00
Kelsi
d40e8f1618 fix: combat log uses actual attacker/victim GUIDs instead of current target
addCombatText now accepts optional srcGuid/dstGuid parameters. When
provided, the persistent combat log resolves names from the actual
packet GUIDs rather than always falling back to playerGuid/targetGuid.

Updated handleAttackerStateUpdate, handleSpellDamageLog,
handleSpellHealLog, and SMSG_PERIODICAURALOG to pass data.attackerGuid
/ data.targetGuid (or casterGuid/victimGuid), so the combat log
correctly records the attacker name when being hit by enemies the
player has not selected as their current target.

All 48 existing call sites use the 0/0 default and are unaffected.
2026-03-13 11:48:42 -07:00
Kelsi
a05abc8881 feat: include spell names in MISS/DODGE/PARRY/BLOCK/IMMUNE/ABSORB/RESIST combat log entries 2026-03-13 11:39:22 -07:00
Kelsi
04768f41de fix: targetEnemy uses faction isHostile() instead of targeting all non-player units 2026-03-13 11:37:13 -07:00
Kelsi
84d5a1125f feat: show gem sockets and item set bonuses in ItemDef tooltip path 2026-03-13 11:34:48 -07:00
Kelsi
ef7494700e feat: parse and display Heroic/Unique/Unique-Equipped item flags in tooltips 2026-03-13 11:32:32 -07:00
Kelsi
03f8642fad feat: parse and display elemental resistances and race restrictions in item tooltips
- Store holyRes/fireRes/natureRes/frostRes/shadowRes/arcaneRes in ItemQueryResponseData
- Parse resistance fields in WotLK, TBC, and Classic parsers (previously discarded)
- Display non-zero resistances (e.g. "+40 Fire Resistance") in both tooltip paths
- Add getPlayerRace() accessor to GameHandler
- Show race restriction line (e.g. "Races: Blood Elf, Draenei") in both tooltip paths,
  highlighted red when player's race is not allowed
- Useful for fire/nature/frost resist gear (Onyxia, AQ40, Naxx encounters)
2026-03-13 11:23:55 -07:00
Kelsi
0741b4d9e3 feat: show class restriction in item tooltip (e.g. "Classes: Paladin")
Display the allowableClass bitmask parsed from SMSG_ITEM_QUERY as a
human-readable "Classes: X, Y" line. Text is highlighted red when the
player's own class is not in the allowed set. Hidden when all classes
can use the item (no restriction).
2026-03-13 11:15:01 -07:00
Kelsi
6e8704c520 feat: show skill/reputation requirements in ItemDef tooltip path
Extend the inventory item (ItemDef) tooltip to also display
skill and reputation requirements by consulting the item query
cache (ItemQueryResponseData) when available, matching the
behavior already added to the ItemQueryResponseData tooltip path.
2026-03-13 11:13:10 -07:00
Kelsi
b0b47c354a feat: parse and display item skill/reputation requirements in tooltips
- Store requiredSkill, requiredSkillRank, allowableClass, allowableRace,
  requiredReputationFaction, and requiredReputationRank from
  SMSG_ITEM_QUERY_SINGLE_RESPONSE in ItemQueryResponseData (was discarded)
- Show "Requires <Skill> (<rank>)" in item tooltip, highlighted red when
  the player doesn't have sufficient skill level
- Show "Requires <Rank> with <Faction>" for reputation-gated items
- Skill names resolved from SkillLine.dbc; faction names from Faction.dbc
- Also fix loot window tooltip suppressing items with names starting with 'I'
2026-03-13 11:11:33 -07:00
Kelsi
a6c4f6d2e9 feat: show effective skill value with bonus indicator in skills panel
Read the third update field (bonusTemp/bonusPerm) for each skill slot so the
skills tab displays the actual buffed value rather than just the base value.
Skills buffed by food/potions/items now show "value / max (+N)" with a cyan
name, and maxed-out skills show a gold bar and name for quick identification.
2026-03-13 10:58:05 -07:00
Kelsi
c48065473f fix: show specific zone name in hearthstone inventory tooltip
The inventory screen item tooltip showed only the continent name
(Eastern Kingdoms, Kalimdor, etc.) for the hearthstone home location.
Apply the same zone-name lookup already used by the action bar tooltip:
prefer the zone name from homeBindZoneId_ via getWhoAreaName(), falling
back to the continent name if the zone is unavailable.
2026-03-13 10:47:10 -07:00
Kelsi
6bca3dd6c5 fix: correct item spell trigger type labels in tooltips
- Add trigger 4 (soulstone), 5 (no-delay use), 6 (learn/recipe) as "Use:"
  — all show as "Use:" in WoW, matching client behavior
- Fix trigger 6 which was incorrectly labeled "Soulstone" (trigger 4 is
  soulstone; trigger 6 is LEARN_SPELL_ID used by recipe/pattern items)
- Both ItemDef tooltip and ItemSlot inline tooltip are now consistent
2026-03-13 10:36:28 -07:00
Kelsi
fa9017c6dc fix: update homeBindZoneId on SMSG_PLAYERBOUND so hearthstone tooltip stays accurate
SMSG_PLAYERBOUND fires when the player sets a new hearthstone location.
Previously homeBindMapId_ and homeBindZoneId_ were only set by
SMSG_BINDPOINTUPDATE (login), so the tooltip would show the old zone
until next login. Now both are updated on SMSG_PLAYERBOUND as well.
2026-03-13 10:33:44 -07:00
Kelsi
605d046838 fix: extend quality color bounds for Artifact/Heirloom in chat links and loot roll
Handle quality indices 6 (Artifact) and 7 (Heirloom) in all remaining
quality-color lookup tables: chat link hex colors and loot roll tooltip.
2026-03-13 10:30:54 -07:00
Kelsi
8da5e5c029 feat: extend quality colors for Artifact/Heirloom and add guild bank repair
- Add light gold (e6cc80) color for quality 6 (Artifact) and 7 (Heirloom)
  in the loot roll window and loot toast notification displays
- Add "Repair (Guild)" button next to "Repair All" in vendor window when
  player is in a guild, using guild bank funds for the repair cost
2026-03-13 10:29:56 -07:00
Kelsi
bbbc4efced feat: add Heirloom and Artifact item quality tiers with light gold color
Extends ItemQuality enum with ARTIFACT (6) and HEIRLOOM (7) to match
WotLK 3.3.5a quality values, with light gold color (e6cc80) and
display name support in inventory UI and tooltips.
2026-03-13 10:22:34 -07:00
Kelsi
cc24597983 feat: show hearthstone bind zone name in tooltip instead of continent 2026-03-13 10:18:31 -07:00
Kelsi
b03c326bcd feat: show logout countdown overlay with cancel button 2026-03-13 10:13:54 -07:00
Kelsi
792d8e1cf5 feat: show estimated BG wait time in queue indicator 2026-03-13 10:10:04 -07:00
Kelsi
c31ab8c8b6 feat: play error sound when UI error messages are triggered 2026-03-13 10:05:10 -07:00
Kelsi
44ff2dd4ee feat: show rain particles and audio during thunderstorm weather
Storm weather (wType==3 from SMSG_WEATHER) previously rendered no
visual particles and no audio. Map it to RAIN in the weather system so
thunderstorms produce rain particles at the server-sent intensity level,
and the ambient sound manager picks up rain_heavy/medium/light audio
from the same intensity logic already used for plain rain.

This pairs with the lightning commit — storms now have both rain
particles and lightning flashes for a complete thunderstorm experience.
2026-03-13 09:59:58 -07:00
Kelsi
727dfa5c6c feat: integrate lightning system for storm weather and heavy rain
The lightning system (lightning.hpp/cpp) was fully implemented but never
wired into the renderer. Connect it now:
- Enable lightning during server storm weather (wType==3, intensity>0.1)
  and heavy rain (wType==1, intensity>0.7) as a bonus visual
- Scale lightning intensity proportionally to weather intensity
- Render in both parallel (SEC_POST) and fallback rendering paths
- Update and shutdown alongside the weather system
- Show active lightning info in the performance HUD weather section
2026-03-13 09:52:23 -07:00
Kelsi
d58c2f4269 feat: show taxi flight destination indicator below minimap
Stores the destination node name when activateTaxi() is called and
displays a "✈ → <Destination>" indicator in the minimap indicator
stack while isOnTaxiFlight() is true. Falls back to "✈ In Flight"
when the destination name is unavailable.
2026-03-13 09:44:27 -07:00
Kelsi
f5a834b543 feat: add /clear chat command and movement speed display in stats
- /clear slash command empties the chat history (was listed in
  autocomplete but never handled)
- Stats panel shows run/flight/swim speed as percentage of base only
  when non-default (e.g. mounted or speed-buffed), under a new
  Movement section
2026-03-13 09:38:39 -07:00
Kelsi
01ec830555 feat: show calendar pending invites indicator below minimap (WotLK)
Add a pulsing purple "Calendar: N Invite(s)" notification below the
minimap indicator stack when the server reports unacknowledged calendar
invites (SMSG_CALENDAR_SEND_NUM_PENDING / EVENT_INVITE_ALERT).

Only rendered when the WotLK expansion is active since the calendar
system is WotLK-exclusive. Consistent with the existing New Mail, talent
point, BG queue, and LFG queue indicator stack.
2026-03-13 09:25:23 -07:00
Kelsi
fdd6ca30c3 feat: display active player title in gold below the player frame name 2026-03-13 09:10:03 -07:00
Kelsi
736d266c7e feat: add target-of-target display in target frame with click-to-target 2026-03-13 09:06:48 -07:00
Kelsi
dd412af093 feat: show spell/ranged hit and haste ratings separately when they differ from melee 2026-03-13 09:03:13 -07:00
Kelsi
850e4e798d feat: add right-click context menu to reputation rows to set tracked faction 2026-03-13 09:01:30 -07:00
Kelsi
84f9d2e493 feat: color class names by WoW class color in character selection screen 2026-03-13 08:57:44 -07:00
Kelsi
a9f21b2820 feat: show percentage conversions for WotLK combat ratings
Convert raw combat rating values to meaningful percentages using
level-scaled divisors based on known WotLK level-80 constants
(from gtCombatRatings.dbc):

  Hit Rating   : 26.23 per 1% at level 80
  Expertise    : 8.19 per expertise pt (0.25% dodge/parry each)
  Haste        : 32.79 per 1% at level 80
  Armor Pen    : 13.99 per 1% at level 80
  Resilience   : 94.27 per 1% at level 80

Each stat now displays as "Hit Rating: 120 (4.58%)" instead of
just "Hit Rating: 120". The divisor scales by pow(level/80, 0.93)
for characters below level 80.
2026-03-13 08:54:15 -07:00
Kelsi
3e85c78790 feat: show zone and map names in character selection screen
Replace raw zone/map IDs with human-readable names via the existing
getWhoAreaName() and getMapName() DBC caches. The Zone column is also
widened from fixed 55px to a stretch column so names fit properly.
Falls back to numeric IDs gracefully when DBC data is unavailable.
2026-03-13 08:43:11 -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
ea7b276125 refactor: use gameHandler.getMapName in instance lockout window
Replaces the static local Map.dbc cache in renderInstanceLockoutsWindow()
with the existing GameHandler::getMapName() accessor, eliminating duplicate
DBC loading. Moves getMapName declaration to public interface.
2026-03-13 08:23:43 -07:00
Kelsi
75139aca77 feat: display LFG dungeon name in DungeonFinder UI status banner
Extend the LFGDungeons.dbc name lookup to the Dungeon Finder window UI:
- Queued state: "In queue for Culling of Stratholme (1:23)"
- Proposal state: "Group found for Halls of Lightning!"
- InDungeon state: "In dungeon (Utgarde Pinnacle)"
- FinishedDungeon state: "Culling of Stratholme complete"
- Proposal accept banner: "A group has been found for <dungeon>!"
All states fall back gracefully when DBC name is unavailable.
2026-03-13 08:16:59 -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
59e29e2988 feat: show taxi destination name in flight messages
Replace generic "Taxi: requesting flight..." and "Flight started." with
"Requesting flight to [node name]..." and "Flight to [node name] started."
using the already-loaded taxiNodes_ map. Falls back to generic text when
the node name is unavailable.
2026-03-13 08:05:16 -07:00
Kelsi
9fe2ef381c feat: use zone name in battlefield entry invite message
Replace raw bfZoneId integer with getAreaName() lookup in
SMSG_BATTLEFIELD_MGR_ENTRY_INVITE so players see "Wintergrasp"
instead of "zone 4197" in the invitation prompt.
2026-03-13 08:00:46 -07:00
Kelsi
a9835f6873 feat: show force-kill notification and resolve Meeting Stone zone name 2026-03-13 07:54:02 -07:00
Kelsi
8cac557f86 feat: notify player when dungeon difficulty changes 2026-03-13 07:42:40 -07:00
Kelsi
ee3c12b2c0 feat: announce weather changes in chat (rain/snow/storm/clear) 2026-03-13 07:39:41 -07:00
Kelsi
67e4497945 feat: improve arena team event messages and add vote kick feedback 2026-03-13 07:37:40 -07:00
Kelsi
90c88d7ecd feat: show equipment set name in save confirmation
SMSG_EQUIPMENT_SET_SAVED: parse the set index and GUID, look up the
matching set name from equipmentSets_, and show
"Equipment set \"<name>\" saved." instead of the generic message.
Falls back to "Equipment set saved." when the set is not yet in the
local cache (e.g. first save before SMSG_EQUIPMENT_SET_LIST arrives).
2026-03-13 07:31:44 -07:00
Kelsi
d2f2d6db72 fix: distinguish auction owner notification action types
SMSG_AUCTION_OWNER_NOTIFICATION action field was ignored — all events
showed "has sold!" regardless. Now:
  - action 0 (won/sold): "Your auction of <item> has sold!"
  - action 1 (expired):  "Your auction of <item> has expired."
  - action 2 (bid placed): "A bid has been placed on your auction of <item>."
2026-03-13 07:27:01 -07:00
Kelsi
0a41ef7285 feat: improve player feedback for purchases, friend status, and instance entry
- SMSG_BUY_ITEM: show "Purchased: <item name> x<count>" confirmation
  using pendingBuyItemId_ set at buy time (fallback to "item #N")
- handleFriendStatus: look up name from contacts_ (populated by
  SMSG_FRIEND_LIST) before playerNameCache, reducing "Unknown" fallbacks
  for online/offline/removed notifications
- Channel member list: also check playerNameCache when entity manager
  has no name, reducing "(unknown)" placeholders
- setFocus: use Unit::getName() (covers NPCs too) + playerNameCache
  fallback instead of Player-only cast
- SMSG_INSTANCE_LOCK_WARNING_QUERY: show dungeon name + difficulty +
  remaining time when auto-accepting a saved instance re-entry
2026-03-13 07:20:58 -07:00
Kelsi
2be793cfba feat: improve calendar lockout and char rename messages
- SMSG_CALENDAR_RAID_LOCKOUT_ADDED: show dungeon name (from Map.dbc)
  and difficulty label (Normal/Heroic/25-Man/25-Man Heroic)
- SMSG_CALENDAR_RAID_LOCKOUT_REMOVED: show dungeon name instead of raw
  map ID; now emits a chat message (was silent/LOG_DEBUG only)
- SMSG_CHAR_RENAME: map result codes 1-7 to human-readable strings
  ("Name already in use.", "Name too short.", etc.) instead of
  "Character rename failed (error N)."
2026-03-13 07:14:40 -07:00
Kelsi
2c72d8462d feat: add map name lookups and improve instance/RAF/misc feedback
- Add getMapName() helper backed by Map.dbc (parallel to getAreaName)
- SMSG_RAID_INSTANCE_MESSAGE: show dungeon name instead of raw map ID
- SMSG_INSTANCE_RESET: show dungeon name instead of raw map ID
- SMSG_INSTANCE_RESET_FAILED: show dungeon name instead of raw map ID
- SMSG_EQUIPMENT_SET_SAVED: show "Equipment set saved." confirmation
- SMSG_PROPOSE_LEVEL_GRANT: show mentor name offering a level grant (RAF)
- SMSG_REFER_A_FRIEND_EXPIRED: show link-expired message
- SMSG_REFER_A_FRIEND_FAILURE: show reason-mapped error message
- SMSG_REPORT_PVP_AFK_RESULT: show success/failure feedback
- SMSG_QUEST_CONFIRM_ACCEPT: add playerNameCache fallback for sharer name
2026-03-13 07:10:10 -07:00
Kelsi
28ce441214 feat: add missing player feedback for level-up, pet spells, and pet name errors
- SMSG_LEVELUP_INFO: show \"You have reached level N!\" chat message on level-up
  (was only calling the UI ding callback without any chat notification)
- SMSG_PET_LEARNED_SPELL: show \"Your pet has learned X.\" with spell name lookup
  (was LOG_DEBUG only)
- SMSG_PET_NAME_INVALID: show \"That pet name is invalid.\"
  (was silently consumed)
2026-03-13 07:02:20 -07:00
Kelsi
8e67a41983 fix: show battleground names instead of IDs in SMSG_BATTLEFIELD_STATUS messages
Replace raw \"Battleground #2\" with proper names (Warsong Gulch, Arathi Basin,
Eye of the Storm, Strand of the Ancients, Isle of Conquest, arena names, etc.)
for all three expansions' BG type ID space.
2026-03-13 06:59:02 -07:00
Kelsi
20b59c9d63 fix: correct LFG vote kick result logic and show item names in dungeon rewards
- handleLfgBootProposalUpdate: was using myAnswer (player's own vote) to determine
  if the boot passed — should use bootVotes >= votesNeeded instead. Player who voted
  yes would see "passed" even if the vote failed, and vice versa.
- handleLfgPlayerReward: look up item name from item cache instead of showing
  raw "item #12345" for dungeon reward items
2026-03-13 06:56:37 -07:00
Kelsi
cf88a960f4 feat: add missing SMSG_CHANNEL_NOTIFY feedback for 12 unhandled notification types
Previously, PLAYER_NOT_FOUND, ANNOUNCEMENTS_ON/OFF, MODERATION_ON/OFF,
PLAYER_BANNED, PLAYER_UNBANNED, PLAYER_NOT_BANNED, INVITE, WRONG_FACTION,
INVITE_WRONG_FACTION, NOT_MODERATED, PLAYER_INVITED, and PLAYER_INVITE_BANNED
all fell silently to the default log-only path. Now each shows an appropriate
system message in chat.
2026-03-13 06:48:12 -07:00
Kelsi
9216a6da28 fix: show zone names in hearthstone bind messages, add playerNameCache to duel challenger
- SMSG_BINDPOINTUPDATE: show zone name in \"Your home has been set to X.\" (was just \"Your home has been set.\")
- SMSG_PLAYERBOUND: replace \"map N, zone N\" raw IDs with zone name lookup
- SMSG_BINDER_CONFIRM: suppress redundant \"This innkeeper is now your home location.\" since SMSG_PLAYERBOUND fires immediately after with zone context
- SMSG_DUEL_REQUESTED: add playerNameCache fallback before hex GUID for challenger name
2026-03-13 06:46:56 -07:00
Kelsi
ecc02595de fix: improve guild command result messages and suppress repeated guild name announcements
- Map GuildCommandError codes to human-readable strings instead of showing raw
  error numbers (e.g. \"error 4\" → \"No player named X is online.\")
- Handle errorCode==0 for QUIT command: show \"You have left the guild.\" and
  clear guild state (name, ranks, roster) — previously silent
- Handle errorCode==0 for CREATE and INVITE commands with appropriate messages
- Substitute %s-style error messages with the player name from data.name
- Suppress repeated \"Guild: <Name>\" chat message on every SMSG_GUILD_QUERY_RESPONSE;
  only announce once when the guild name is first learned at login
2026-03-13 06:43:11 -07:00
Kelsi
e882110e7f fix: suppress repeated group-join chat spam on every SMSG_GROUP_LIST update
Previously "You are now in a group with N members." was shown on every
GROUP_LIST packet, which fires for each party stat update. Now only show
a message on actual state transitions: joining, leaving the group.
2026-03-13 06:38:50 -07:00
Kelsi
3a143b9b5b fix: use playerNameCache fallback and show zone name in summon/trade requests
- SMSG_SUMMON_REQUEST: fall back to playerNameCache when entity not in
  range; include zone name from getAreaName() in the summon message
  (e.g. "Bob is summoning you to Stormwind.")
- SMSG_TRADE_STATUS BEGIN_TRADE: fall back to playerNameCache when the
  trade initiator's entity is not visible
2026-03-13 06:37:26 -07:00
Kelsi
8a81ffa29c fix: show quest name in QUESTGIVER_QUEST_FAILED, use playerNameCache for achievements
- SMSG_QUESTGIVER_QUEST_FAILED: look up quest title from questLog_ and
  include it in the failure message (same pattern as QUESTUPDATE_FAILED
  fix from previous session)
- SMSG_ACHIEVEMENT_EARNED: fall back to playerNameCache for non-visible
  players before showing a raw hex GUID in the achievement message
2026-03-13 06:36:04 -07:00
Kelsi
b1015abffe fix: use actual pct in durability death message, fix pet cast failure feedback
- SMSG_DURABILITY_DAMAGE_DEATH: use the actual pct field from the packet
  instead of hardcoding "10%"
- SMSG_PET_CAST_FAILED: read reason as uint8 (not uint32), look up spell
  name and show human-readable failure reason to player
- Trade status 9 (REJECTED): show "Trade declined." instead of "Trade
  cancelled." to distinguish explicit decline from cancellation
2026-03-13 06:30:30 -07:00
Kelsi
2a52aedbf7 fix: show quest name instead of ID in failed/timed-out quest messages
SMSG_QUESTUPDATE_FAILED and SMSG_QUESTUPDATE_FAILEDTIMER were emitting
generic "Quest 12345 failed!" messages. Now looks up the title from
questLog_ and shows e.g. "\"Report to Gryan Stoutmantle\" failed!" for
a much more readable notification. Falls back to the generic form if
the title is not cached.
2026-03-13 06:24:16 -07:00
Kelsi
156ddfad9a fix: pass power type from POWER_DRAIN energize to color-code combat text
SMSG_SPELLLOGEXECUTE POWER_DRAIN reads drainPower but was not passing it
to addCombatText, so drained-resource returns showed as blue (mana) even
for rage or energy. Now correctly colored following the energize palette
added in the earlier commit.
2026-03-13 06:22:51 -07:00
Kelsi
43b007cdcd fix: only show SMSG_DISPEL_FAILED message when player is the caster
The dispel-failed handler was showing the failure notification for every
dispel attempt in the party/raid, regardless of who cast it. Now checks
casterGuid == playerGuid before showing "X failed to dispel." so only
the player's own failed dispels surface in chat.
2026-03-13 06:21:33 -07:00
Kelsi
d79c79e1bc feat: show chat messages for channel notification events
SMSG_CHANNEL_NOTIFY carries many event types that were silently dropped
in the default case: wrong password, muted, banned, throttled, kicked,
not owner, not moderator, password changed, owner changed, invalid name,
not in area, not in LFG. These are now surfaced as system chat messages
matching WoW-standard phrasing.
2026-03-13 06:18:23 -07:00
Kelsi
38ab1e0aea fix: show correct duel fled message when loser left the duel area
SMSG_DUEL_WINNER type=1 means the loser fled the duel zone rather than
being defeated; was previously treated the same as a normal win. Now
shows "X has fled from the duel. Y wins!" for the flee case vs the
standard "X has defeated Y in a duel!" for a normal outcome.
2026-03-13 06:16:19 -07:00
Kelsi
3b499d6871 fix: prefix SMSG_SPELL_FAILURE error message with spell name
Previously a spell failure like "Not in range" gave no context about
which spell failed. Now the message reads e.g. "Fireball: Not in range"
using the spell name from the DBC cache. Falls back to the bare reason
string if the spell name is not yet cached.
2026-03-13 06:14:28 -07:00
Kelsi
c58fc3073f fix: clear action bar slots when spells are removed or unlearned
SMSG_REMOVED_SPELL and SMSG_SEND_UNLEARN_SPELLS both erased spells from
knownSpells but left stale references on the action bar. After a respec
or forced spell removal, action bar buttons would show removed talents
and spells as still present. Now both handlers clear matching slots and
persist the updated bar layout.
2026-03-13 06:11:10 -07:00
Kelsi
d9b9d1d2f2 fix: show dodge/parry/block/immune combat text when enemy spell misses player
SMSG_SPELLLOGMISS contains miss events for both directions: spells the
player cast that missed, and enemy spells that missed the player. The
victim side (dodge/parry/block/immune/absorb/resist) was silently
discarded. Now both caster==player and victim==player generate the
appropriate combat text floater.
2026-03-13 06:09:42 -07:00
Kelsi
4507a223cc feat: color-code ENERGIZE combat text by power type
Mana (0)=blue, Rage (1)=red, Focus (2)=orange, Energy (3)=yellow,
Runic Power (6)=teal. Previously all energize events showed as blue
regardless of resource type, making it impossible to distinguish
e.g. a Warrior's Rage generation from a Mage's Mana return.

Power type is now captured from SMSG_SPELLENERGIZELOG (uint8) and
SMSG_PERIODICAURALOG OBS_MOD_POWER/PERIODIC_ENERGIZE (uint32 cast
to uint8) and stored in CombatTextEntry::powerType.
2026-03-13 06:08:21 -07:00
Kelsi
b9c16e9be5 fix: suppress duplicate "Upgraded to X" message on trainer rank-up
When buying a higher spell rank from a trainer, SMSG_TRAINER_BUY_SUCCEEDED
already announces "You have learned X", and SMSG_SUPERCEDED_SPELLS would
then also print "Upgraded to X" — two messages for one action.

Fix: check if the new spell ID was already in knownSpells before inserting
it in handleSupercededSpell. If so, the trainer handler already announced
it and we skip the redundant "Upgraded to" message. Non-trainer supersedes
(quest rewards, etc.) where the spell wasn't pre-inserted still show it.
2026-03-13 06:00:39 -07:00
Kelsi
dfe091473c fix: show action bar cooldown timers for spells on cooldown at login
SMSG_INITIAL_SPELLS delivers active cooldowns which were stored in
spellCooldowns but never propagated to the action bar slot
cooldownRemaining/cooldownTotal fields. This meant that spells with
remaining cooldowns at login time showed no countdown overlay on the
action bar. Sync the action bar slots from spellCooldowns after
loadCharacterConfig() to restore the correct timers.
2026-03-13 05:58:57 -07:00
Kelsi
2b131548aa fix: show descriptive party command error messages
Replace the generic "Party command failed (error N)" message with
WoW-standard error strings for each PartyResult code, matching what
the original client displays (e.g. "Your party is full.", "%s is
already in a group.", "%s is ignoring you.", etc.).
2026-03-13 05:54:01 -07:00
Kelsi
b34df01331 fix: suppress duplicate chat message when learning trainer spell
SMSG_TRAINER_BUY_SUCCEEDED pre-inserts the spell into knownSpells and
shows "You have learned X." The subsequent SMSG_LEARNED_SPELL packet
would then show a second "You have learned a new spell: X." message.

Fix: check if the spell was already in knownSpells before inserting in
handleLearnedSpell. If it was pre-inserted by the trainer handler, skip
the chat notification to avoid the duplicate.
2026-03-13 05:51:15 -07:00
Kelsi
7dc12bb35e fix: persist action bar config after superceded spell slot upgrade
After automatically upgrading action bar slots to the new spell rank
in handleSupercededSpell, save the character config so the upgraded
slot IDs persist across sessions.
2026-03-13 05:42:24 -07:00
Kelsi
2d587d0d4b feat: upgrade action bar slots to new spell rank on supercede
When a spell is superceded (e.g. Fireball Rank 1 -> Rank 2 after
training), update any action bar slots referencing the old spell ID
to point to the new rank. This matches WoW client behaviour where
training a new rank automatically upgrades your action bars so you
don't have to manually re-place the spell.
2026-03-13 05:37:15 -07:00
Kelsi
3c704088af fix: clear lastInteractedGoGuid_ on world transfer in handleNewWorld
Same-map teleports (dungeon teleporters, etc.) clear casting state but
were not clearing lastInteractedGoGuid_.  If a gather cast was in progress
when the teleport happened, the stale GO guid could theoretically trigger
a spurious CMSG_LOOT on the destination map.

Also clears lastInteractedGoGuid_ in handleNewWorld alongside the rest of
the casting-state teardown for consistency with other reset paths.
2026-03-13 05:26:27 -07:00
Kelsi
103bb5a513 fix: query item info in SMSG_LOOT_START_ROLL and use live name in roll popup
SMSG_LOOT_START_ROLL was not calling queryItemInfo(), so the roll popup
would display item IDs instead of names when the item had not been
previously cached (e.g. first time seeing that item in the session).

Also update renderLootRollPopup to prefer the live ItemQueryResponseData
name/quality over the snapshot captured at parse time, so the popup
shows the correct name once SMSG_ITEM_QUERY_SINGLE_RESPONSE arrives.
2026-03-13 05:23:31 -07:00
Kelsi
48bcee32b4 fix: match spell ID in handleSpellGo to prevent proc spells triggering gather loot
wasInTimedCast checked casting == true but not whether the completing spell
was actually the gather cast.  A triggered/proc spell (SMSG_SPELL_GO with
a different spellId) could arrive while a gather cast is active (casting==true),
satisfying the old guard and firing lootTarget prematurely.

Require data.spellId == currentCastSpellId so only the spell that started
the cast bar triggers the post-gather CMSG_LOOT dispatch.
2026-03-13 05:12:22 -07:00
Kelsi
f44defec38 feat: show fish-hooked notification when fishing bobber splashes
When the server sends SMSG_GAMEOBJECT_CUSTOM_ANIM with animId=0 for a GO
of type 17 (FISHINGNODE), a fish has been hooked and the player needs to
click the bobber quickly.  Add a system chat message and a UI sound to
alert the player — previously there was no visual/audio feedback beyond
the bobber animation itself.
2026-03-13 05:07:51 -07:00
Kelsi
01f4ef5e79 fix: clear lastInteractedGoGuid_ for non-lootable GO interactions
Mailboxes, doors, buttons, and other non-lootable GOs set shouldSendLoot=false
so no CMSG_LOOT is dispatched — but lastInteractedGoGuid_ was still set.
Without SMSG_LOOT_RESPONSE to clear it, a subsequent timed cast completion
(e.g. player buffs at the mailbox) would fire a spurious CMSG_LOOT for the
mailbox GUID.
2026-03-13 05:06:00 -07:00
Kelsi
6878f120e9 fix: clear lastInteractedGoGuid_ in handleCastFailed path
SMSG_CAST_FAILED is a direct rejection (e.g. insufficient range, no mana)
before the cast starts.  Missing this path meant a stale gather-node guid
could survive into the next timed cast if SMSG_CAST_FAILED fired instead
of SMSG_SPELL_FAILURE.
2026-03-13 05:03:50 -07:00
Kelsi
7a4347dbac fix: clear lastInteractedGoGuid_ on cast failure, cancel, and world reset
If a gather cast was interrupted by SMSG_SPELL_FAILURE (e.g. player took
damage during mining), lastInteractedGoGuid_ was left set.  A subsequent
timed cast completion would then fire CMSG_LOOT for the stale node even
though the gather never completed.

Clear lastInteractedGoGuid_ in all cast-termination paths:
- SMSG_SPELL_FAILURE (cast interrupted by server)
- SMSG_CAST_RESULT non-zero (cast rejected before it started)
- cancelCast() (player or system cancelled the cast)
- World reset / logout block (state-clear boundary)
2026-03-13 05:02:58 -07:00
Kelsi
cc2b413e22 fix: guard gather-node CMSG_LOOT dispatch against instant casts and proc spells
handleSpellGo fired lootTarget(lastInteractedGoGuid_) on ANY player spell
completion, including instant casts and proc/triggered spells that arrive
while the gather cast is still in flight.  Save the casting flag before
clearing it and only dispatch CMSG_LOOT when wasInTimedCast is true — this
ensures only the gather cast completion triggers the post-gather loot send,
not unrelated instant spells that also produce SMSG_SPELL_GO.
2026-03-13 04:59:05 -07:00
Kelsi
2c6902d27d fix: mining nodes no longer report invalid target and now open loot after gather
Two bugs fixed:
1. Retry logic (for Classic) re-sent CMSG_GAMEOBJ_USE at 0.15s while the
   gather cast was in-flight, causing SPELL_FAILED_BAD_TARGETS. Now clears
   pendingGameObjectLootRetries_ as soon as SMSG_SPELL_START shows the player
   started a cast (gather accepted).

2. CMSG_LOOT was sent immediately before the gather cast completed, then
   never sent again — so the loot window never opened. Now tracks the last
   interacted GO and sends CMSG_LOOT in handleSpellGo once the gather spell
   completes, matching how the real client behaves.
2026-03-13 04:37:36 -07:00
Kelsi
4272491d56 feat: send CMSG_SET_ACTION_BUTTON to server when action bar slot changes
Action bar changes (dragging spells/items) were only saved locally.
Now notifies the server via CMSG_SET_ACTION_BUTTON so the layout
persists across relogs. Supports Classic (5-byte) and TBC/WotLK
(packed uint32) wire formats.
2026-03-13 04:25:05 -07:00
Kelsi
8f08d75748 fix: cache player death position so corpse reclaim works in Classic
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
Classic 1.12 does not send SMSG_DEATH_RELEASE_LOC, leaving corpseMapId_=0
and preventing the 'Resurrect from Corpse' button from appearing.

- When health reaches 0 via VALUES update, immediately cache movementInfo
  as corpse position (canonical->server axis swap applied correctly)
- Do the same on UNIT_DYNFLAG_DEAD set path
- Clear corpseMapId_ when ghost flag is removed (corpse reclaimed)
- Clear corpseMapId_ in same-map spirit-healer resurrection path

The CORPSE object detection (UPDATE_OBJECT) and SMSG_DEATH_RELEASE_LOC
(TBC/WotLK) will still override with exact server coordinates when received.
2026-03-13 04:04:38 -07:00
Kelsi
499638142e feat: make quest tracker movable, resizable, and right-edge-anchored
- Remove NoDecoration flag to allow ImGui drag/resize
- Store questTrackerRightOffset_ instead of absolute X so tracker
  stays pinned to the right edge when the window is resized
- Persist position (right offset + Y) and size in settings.cfg
- Clamp to screen bounds after drag
2026-03-13 04:04:29 -07:00
Kelsi
85767187b1 fix: clear gameObjectDisplayIdWmoCache_ on world transition, add stale-entry guard
gameObjectDisplayIdWmoCache_ was not cleared on world unload/transition,
causing stale WMO model IDs (e.g. 40006, 40003) to be looked up after
the renderer cleared its model list, resulting in "Cannot create instance
of unloaded WMO model" errors on zone re-entry.

Changes:
- Clear gameObjectDisplayIdWmoCache_ alongside other GO caches on world reset
- Add WMORenderer::isModelLoaded() for cache-hit validation
- Inline GO WMO path now verifies cached model is still renderer-resident
  before using it; evicts stale entries and falls back to reload
2026-03-13 03:43:55 -07:00
Kelsi
0487d2eda6 fix: check loadModel return before createInstance for WMO doodads
When the M2 model cache is full (>6000 entries), loadModel() returns
false and the model is never added to the GPU cache. The WMO instance
doodad path was calling createInstanceWithMatrix() unconditionally,
generating hundreds of "Cannot create instance: model X not loaded"
warnings on zone entry. Add the same guard already present in the
terrain doodad path.
2026-03-13 03:41:42 -07:00
Kelsi
863faf9b54 fix: correct talent rank indexing — store 1-indexed, fix prereq and learn checks
SMSG_TALENTS_INFO wire format sends 0-indexed ranks (0=has rank 1). Both
handlers were storing raw 0-indexed values, but handleSpellLearnedServer
correctly stored rank+1 (1-indexed). This caused:
 - getTalentRank() returning 0 for both "not learned" and "has rank 1",
   making pointsInTree always wrong and blocking tier access
 - Prereq check `prereqRank < DBC_prereqRank` always met when not learned
   (0 < 0 = false), incorrectly unlocking talents
 - Click handler sending wrong desiredRank to server

Fixes:
 - Both SMSG_TALENTS_INFO handlers: store rank+1u (1-indexed)
 - talent_screen.cpp prereq check: change < to <= (DBC is 0-indexed,
   storage is 1-indexed; must use > for "met", <= for "not met")
 - talent_screen.cpp click handler: send currentRank directly (1-indexed
   value equals what CMSG_LEARN_TALENT requestedRank expects)
 - Tooltip: display prereqRank+1 so "Requires 1 point" shows correctly
2026-03-13 03:32:45 -07:00
Kelsi
952f36b732 fix: correct minimap player arrow orientation (was 90° off, E/W appeared flipped) 2026-03-13 03:19:05 -07:00
Kelsi
ebd9cf5542 fix: handle MSG_MOVE_SET_*_SPEED opcodes to suppress unhandled opcode warnings 2026-03-13 03:13:29 -07:00
Kelsi
64439673ce fix: show repair button when vendor has NPC_FLAG_REPAIR (0x40) set
Vendors that open directly (without gossip menu) never triggered the
armorer gossip path, so canRepair was always false and the Repair button
was hidden. Now also check the NPC's unit flags for NPC_FLAG_REPAIR when
the vendor list arrives, fixing armorers accessed directly.
2026-03-13 03:06:45 -07:00
Kelsi
8f3f1b21af fix: check vertices before skin load so WotLK (v264) character M2s parse correctly
TBC races like Draenei use version-264 M2 files with no embedded skin;
indices come from a separate .skin file loaded after M2::load().
The premature isValid() check (which requires non-empty indices) always
failed for WotLK-format character models, making Draenei (and Blood Elf)
players invisible.

Fix: only check vertices.empty() right after load(), then validate fully
with isValid() after the skin file is loaded.
2026-03-13 02:58:42 -07:00
Kelsi
27213c1d40 fix: robust SMSG_ATTACKERSTATEUPDATE parsing for WotLK format
Two issues in the WotLK SMSG_ATTACKERSTATEUPDATE parser:

1. subDamageCount could read a school-mask byte when a packed GUID is
   off by one byte, producing values like 32/40/44/48 (shadow/frost/etc
   school masks) as the count. The parser then tried to read 32-48
   sub-damages before hitting EOF. Fix: silently clamp subDamageCount to
   floor(remaining/20) so we only attempt entries that actually fit.

2. After sub-damages, AzerothCore sends victimState(4)+unk1(4)+unk2(4)+
   overkill(4) (16 bytes), not the 8-byte victimState+overkill the
   parser was reading. Fix: consume unk1 and unk2 before reading overkill.
   Also handle the hitInfo-conditional HITINFO_BLOCK/RAGE_GAIN/FAKE_DAMAGE
   fields at the end of the packet.
2026-03-13 02:47:40 -07:00
Kelsi
1cd8e53b2f fix: handle SPLINEFLAG_ANIMATION in UPDATE_OBJECT legacy spline layout
When SPLINEFLAG_ANIMATION (0x00400000) is set, AzerothCore inserts 5 bytes
(uint8 animationType + int32 animTime) between durationModNext and
verticalAccel in the SMSG_UPDATE_OBJECT MoveSpline block. The parser was
not accounting for these bytes, causing verticalAccel, effectStartTime,
and pointCount to be read from the wrong offset.

This produced garbage pointCount values (e.g. 3322451254) triggering the
"Spline pointCount invalid (legacy+compact)" fallback path and breaking
UPDATE_OBJECT parsing for animated-spline entities, causing all subsequent
update blocks in the same packet to be dropped.
2026-03-13 02:38:53 -07:00
Kelsi
61adb4a803 fix: free terrain descriptor sets when unloading mid-finalization tiles
When unloadTile() was called for a tile still in finalizingTiles_
(mid-incremental-finalization), terrain chunks already uploaded to the
GPU (terrainMeshDone=true) were not being cleaned up. The early-return
path correctly removed water and M2/WMO instances but missed calling
terrainRenderer->removeTile(), causing descriptor sets to leak.

After ~20 minutes of play the VkDescriptorPool (MAX_MATERIAL_SETS=16384)
filled up, causing all subsequent terrain material allocations to fail
and the log to flood with "failed to allocate material descriptor set".

Fix: check fit->terrainMeshDone before the early return and call
terrainRenderer->removeTile() to free those descriptor sets.
2026-03-13 02:33:02 -07:00
Kelsi
862d743f87 fix: WMO culling dead-end group fallback and minimap arrow direction
wmo_renderer: when portal BFS starts from a group with no portal refs
(utility/transition group), the rest of the WMO becomes invisible because
BFS only adds the starting group. Fix: if cameraGroup has portalCount==0,
fall back to marking all groups visible (same as camera-outside behavior).

renderer: minimap player-orientation arrow was pointing the wrong direction.
The shader convention is arrowRotation=0→North, positive→clockwise (West),
negative→East. The correct mapping from canonical yaw is arrowRotation =
-canonical_yaw. Fixed both render paths (cameraController and gameHandler)
in both the FXAA and non-FXAA minimap render calls.

World-map W-key: already corrected in prior commit (13c096f); users with
stale ~/.wowee/settings.cfg should rebind via Settings > Controls.
2026-03-13 02:25:06 -07:00
Kelsi
d4bf8c871e fix: clear gameObjectDisplayIdFailedCache_ on world reset and zone change
The failed-model cache introduced in f855327 would persist across map
changes, permanently suppressing models that failed on one map but might
be valid assets on another (or after a client update). Clear it in the
world reset path alongside the existing gameObjectDisplayIdModelCache_
clear, so model loads get a fresh attempt on each zone change.
2026-03-13 01:53:59 -07:00
Kelsi
d58c55ce8d fix: allow ribbon-only M2 models to load and silence transport doodad load errors
Two follow-up fixes for the ribbon emitter implementation and the
transport-doodad stall fix:

1. loadModel() rejected any M2 with no vertices AND no particles, but
   ribbon-only spell-effect models (e.g. weapon trail or aura ribbons)
   have neither. These models were silently invisible even though the
   ribbon rendering pipeline added in 1108aa9 is fully capable of
   rendering them. Extended the guard to also accept models that have
   ribbon emitters, matching the particle-emitter precedent.

2. processPendingTransportDoodads() ignored the bool return of
   loadModel(), calling createInstance() even when the model was
   rejected, generating spurious "Cannot create instance: model X not
   loaded" warnings for every failed doodad path. Check the return
   value and continue to the next doodad on failure.
2026-03-13 01:49:22 -07:00
Kelsi
f855327054 fix: eliminate 490ms transport-doodad stall and GPU device-loss crash
Three root causes identified from wowee.log crash at frame 134368:

1. processPendingTransportDoodads() was doing N separate synchronous
   GPU uploads (vkQueueSubmit + vkWaitForFences per texture per doodad).
   With 30+ doodads × multiple textures, this caused the 489ms stall in
   the 'gameobject/transport queues' update stage. Fixed by wrapping the
   entire batch in beginUploadBatch()/endUploadBatch() so all texture
   layout transitions are submitted in a single async command buffer.

2. Game objects whose M2 model has no geometry/particles (empty or
   unsupported format) were retried every frame because loadModel()
   returns false without adding to gameObjectDisplayIdModelCache_.
   Added gameObjectDisplayIdFailedCache_ to permanently skip these
   display IDs after the first failure, stopping the per-frame spam.

3. renderM2Ribbons() only checked ribbonPipeline_ != null, not
   ribbonAdditivePipeline_. If additive pipeline creation failed, any
   ribbon with additive blending would call vkCmdBindPipeline with
   VK_NULL_HANDLE, causing VK_ERROR_DEVICE_LOST on the GPU side.
   Extended the early-return guard to cover both ribbon pipelines.
2026-03-13 01:45:31 -07:00
Kelsi
367b48af6b fix: handle short loot-failure response in LootResponseParser
Servers send a 9-byte packet (guid+lootType) with lootType=LOOT_NONE when
loot is unavailable (locked chest, another player looting, needs a key).
The previous parser required ≥14 bytes (guid+lootType+gold+itemCount) and
logged a spurious WARNING for every such failure response.

Now:
- Accept the 9-byte form; return false so the caller skips opening the
  loot window (correct behaviour for a failure/empty response).
- Log at DEBUG level instead of WARNING for the short form.
- Keep the original WARNING for genuinely malformed packets < 9 bytes.
2026-03-13 01:29:21 -07:00
Kelsi
13c096f3e9 fix: resolve keybinding conflicts for Q, M, and grave keys
- TOGGLE_QUEST_LOG: change default from Q to None — Q conflicts with
  strafe-left in camera_controller; quest log already accessible via
  TOGGLE_QUESTS (L, the standard WoW binding)
- Equipment Set Manager: remove hardcoded SDL_SCANCODE_GRAVE shortcut
  (~` should not be used for this)
- World map M key: remove duplicate SDL_SCANCODE_M self-handler from
  world_map.cpp::render() that was desync-ing with game_screen's
  TOGGLE_WORLD_MAP binding; game_screen now owns open/close, render()
  handles initial zone load and ESC-close signalling via isOpen()
2026-03-13 01:27:30 -07:00
Kelsi
1108aa9ae6 feat: implement M2 ribbon emitter rendering for spell trail effects
Parse M2RibbonEmitter data (WotLK format) from M2 files — bone index,
position, color/alpha/height tracks, edgesPerSecond, edgeLifetime,
gravity. Add CPU-side trail simulation per instance (edge birth at bone
world position, lifetime expiry, gravity droop). New m2_ribbon.vert/frag
shaders render a triangle-strip quad per emitter using the existing
particleTexLayout_ descriptor set. Supports both alpha-blend and additive
pipeline variants based on material blend mode. Fixes invisible spell
trail effects (~5-10%% of spell visuals) that were silently skipped.
2026-03-13 01:17:30 -07:00
Kelsi
022d387d95 fix: correct corpse retrieval coordinate mismatch and detect corpse objects
- canReclaimCorpse() and getCorpseDistance() compared canonical movementInfo
  (x=north=server_y, y=west=server_x) against raw server corpseX_/Y_ causing
  the proximity check to always report wrong distance even when standing on corpse
- Fix: use corpseY_ for canonical north and corpseX_ for canonical west
- Also detect OBJECT_TYPE_CORPSE update blocks owned by the player to set
  corpse coordinates at login-as-ghost (before SMSG_DEATH_RELEASE_LOC arrives)
2026-03-13 00:59:43 -07:00
Kelsi
acf99354b3 feat: add ghost mode grayscale screen effect
- FXAA path: repurpose _pad field as 'desaturate' push constant; when
  ghostMode_ is true, convert final pixel to grayscale with slight cool
  blue tint using luma(0.299,0.587,0.114) mix
- Non-FXAA path: apply a high-opacity gray overlay (rgba 0.5,0.5,0.55,0.82)
  over the scene for a washed-out look
- Both parallel (SEC_POST) and single-threaded render paths covered
- ghostMode_ flag set each frame from gameHandler->isPlayerGhost()
2026-03-13 00:59:36 -07:00
Kelsi
d3159791de fix: rewrite handleTalentsInfo with correct WotLK SMSG_TALENTS_INFO packet format
TalentsInfoParser used a completely wrong byte layout (expected big-endian
counts, wrong field order), causing unspentTalentPoints to always be misread.
This made canLearn always false so clicking talents did nothing.

New format matches the actual WoW 3.3.5a wire format:
  uint8 talentType, uint32 unspentTalents, uint8 groupCount, uint8 activeGroup,
  per-group: uint8 talentCount, [uint32 id + uint8 rank]×N, uint8 glyphCount, [uint16]×M

Matches the proven parsing logic in handleInspectResults.
2026-03-13 00:47:04 -07:00
Kelsi
e4fd4b4e6d feat: parse SMSG_SET_FLAT/PCT_SPELL_MODIFIER and apply talent modifiers to spell tooltips
Implements SMSG_SET_FLAT_SPELL_MODIFIER and SMSG_SET_PCT_SPELL_MODIFIER
(previously consumed silently). Parses per-group (uint8 groupIndex, uint8
SpellModOp, int32 value) tuples sent by the server after login and talent
changes, and stores them in spellFlatMods_/spellPctMods_ maps keyed by
(SpellModOp, groupIndex).

Exposes getSpellFlatMod(op)/getSpellPctMod(op) accessors and a static
applySpellMod() helper. Clears both maps on character login alongside
spellCooldowns. Surfaces talent-modified mana cost and cast time in the
spellbook tooltip via SpellModOp::Cost and SpellModOp::CastingTime lookups.
2026-03-12 23:59:38 -07:00
Kelsi
74d5984ee2 feat: parse arena header in MSG_PVP_LOG_DATA and show arena scoreboard
Previously the arena path in handlePvpLogData consumed the packet and
returned early with no data. Now the two-team header is parsed (rating
change, new rating, team name), followed by the same player list and
winner fields as battlegrounds.

The BgScoreboardData struct gains ArenaTeamScore fields (teamName,
ratingChange, newRating) populated when isArena=true.

The BG scoreboard UI is updated to:
- Use "Arena Score" window title for arenas
- Show each team's name and rating delta at the top
- Identify the winner by team name instead of faction label
2026-03-12 23:46:38 -07:00
Kelsi
de5c122307 feat: parse SMSG_SET_FACTION_ATWAR/VISIBLE and show at-war status in reputation panel
- Parse SMSG_SET_FACTION_ATWAR (uint32 repListId + uint8 set) to track
  per-faction at-war flags in initialFactions_ flags byte
- Parse SMSG_SET_FACTION_VISIBLE (uint32 repListId + uint8 visible) to
  track faction visibility changes from the server
- Add FACTION_FLAG_* constants (VISIBLE, AT_WAR, HIDDEN, etc.) to GameHandler
- Build repListId <-> factionId bidirectional maps when loading Faction.dbc
  (ReputationListID field 1); used to correlate flag packets with standings
- Fix Faction.dbc field layout comment: field 1=ReputationListID, field 23=Name
  (was incorrectly documented as field 22 with no ReputationListID field)
- Add isFactionAtWar(), isFactionVisible(), getFactionIdByRepListId(),
  getRepListIdByFactionId() accessors on GameHandler
- Reputation panel now shows watched faction at top, highlights at-war
  factions in red with "(At War)" label, and marks tracked faction in gold
2026-03-12 23:30:44 -07:00
Kelsi
1d9dc6dcae feat: parse SMSG_RESPOND_INSPECT_ACHIEVEMENTS and request on inspect
When the player inspects another player on WotLK 3.3.5a, also send
CMSG_QUERY_INSPECT_ACHIEVEMENTS so the server responds with
SMSG_RESPOND_INSPECT_ACHIEVEMENTS.  The new handler parses the
achievement-id/date sentinel-terminated block (same layout as
SMSG_ALL_ACHIEVEMENT_DATA but prefixed with a packed guid) and stores
the earned achievement IDs keyed by GUID in
inspectedPlayerAchievements_.  The new public getter
getInspectedPlayerAchievements(guid) exposes this data for the inspect
UI.  The cache is cleared on world entry to prevent stale data.
QueryInspectAchievementsPacket::build() handles the CMSG wire format
(uint64 guid + uint8 unk=0).
2026-03-12 23:23:02 -07:00
Kelsi
0089b3a160 feat: extend SMSG_SPELLLOGEXECUTE to parse power drain, health leech, interrupt cast, and feed pet effects
- Effect 10 (POWER_DRAIN): show PERIODIC_DAMAGE text on victim, ENERGIZE on caster;
  handles Drain Mana, Viper Sting, Fel Drain, etc.
- Effect 11 (HEALTH_LEECH): show SPELL_DAMAGE on victim, HEAL on caster;
  handles Drain Life, Death Coil, etc.
- Effect 24/114 (CREATE_ITEM/CREATE_ITEM2): existing profession crafting feedback
  extended to also cover CREATE_ITEM2 (engineering/enchanting recipes using alt effect)
- Effect 26 (INTERRUPT_CAST): clear the interrupted unit's cast bar from unitCastStates_
  so the cast bar dismisses immediately rather than waiting for the next update packet
- Effect 49 (FEED_PET): show "You feed your pet <item>." message for hunter pet feeding

All effects are expansion-aware: TBC/Classic use full uint64 GUIDs, WotLK uses packed GUIDs.
2026-03-12 23:09:04 -07:00
Kelsi
e029e8649f feat: parse SMSG_SPELLLOGEXECUTE CREATE_ITEM effects for profession crafting feedback
- Implement SMSG_SPELLLOGEXECUTE handler with expansion-aware caster GUID reading
  (packed_guid for WotLK/Classic, full uint64 for TBC)
- Parse effect type 24 (SPELL_EFFECT_CREATE_ITEM): show "You create <item> using
  <spell>." in chat when the player uses a profession or any create-item spell
- Look up item name via ensureItemInfo/getItemInfo and spell name via spellNameCache_
- Fall back to "You create: <item>." when the spell name is not cached
- Safely consume unknown effect types by stopping parse at first unrecognized effect
  to avoid packet misalignment on variable-length sub-records
- Adds visible crafting feedback complementary to SMSG_ITEM_PUSH_RESULT (which shows
  "Received:" for looted/obtained items) with a profession-specific "create" message
2026-03-12 22:53:33 -07:00
Kelsi
d52c49c9fa fix: FXAA sharpening and MSAA exclusion
- Post-FXAA unsharp mask: when FSR2 is active alongside FXAA, forward
  the FSR2 sharpness value (0–2) to the FXAA fragment shader via a new
  vec4 push constant. A contrast-adaptive sharpening step (unsharp mask
  scaled to 0–0.3) is applied after FXAA blending, recovering the
  crispness that FXAA's sub-pixel blend removes.  At sharpness=2.0 the
  output matches RCAS quality; at sharpness=0 the step is a no-op.

- MSAA guard: setFXAAEnabled() refuses to activate FXAA when hardware
  MSAA is in use. FXAA's role is to supplement FSR temporal AA, not to
  stack on top of MSAA which already resolves jaggies during the scene
  render pass.
2026-03-12 22:38:37 -07:00
Kelsi
b832940509 fix: separate SMSG_QUEST_POI_QUERY_RESPONSE from consume-only stubs; add SMSG_SERVERTIME, SMSG_KICK_REASON, SMSG_GROUPACTION_THROTTLED, SMSG_GMRESPONSE_RECEIVED handlers
- Fix bug where SMSG_REDIRECT_CLIENT, SMSG_PVP_QUEUE_STATS, SMSG_PLAYER_SKINNED, etc.
  were incorrectly falling through to handleQuestPoiQueryResponse instead of being
  silently consumed; add separate setReadPos break for those opcodes
- Implement SMSG_SERVERTIME: sync gameTime_ from server's unix timestamp
- Implement SMSG_KICK_REASON: show player a chat message with reason for group removal
- Implement SMSG_GROUPACTION_THROTTLED: notify player of rate-limit with wait time
- Implement SMSG_GMRESPONSE_RECEIVED: display GM ticket response in chat and UI error
- Implement SMSG_GMRESPONSE_STATUS_UPDATE: show ticket status changes in chat
- Silence voice chat, dance, commentator, and debug/cheat opcodes with explicit consume
  cases rather than falling to the unhandled-opcode warning log
2026-03-12 22:35:37 -07:00
Kelsi
c5a6979d69 feat: handle SMSG_BATTLEFIELD_MGR_* and SMSG_CALENDAR_* opcodes
Implements WotLK Outdoor Battlefield Manager (Wintergrasp/Tol Barad):
- Parse SMSG_BATTLEFIELD_MGR_ENTRY_INVITE, ENTERED, QUEUE_INVITE,
  QUEUE_REQUEST_RESPONSE, EJECT_PENDING, EJECTED, STATE_CHANGE
- Store bfMgrInvitePending_/bfMgrActive_/bfMgrZoneId_ state
- Send CMSG_BATTLEFIELD_MGR_ENTRY_INVITE_RESPONSE via acceptBfMgrInvite() /
  declineBfMgrInvite() accessors
- Add renderBfMgrInvitePopup() UI dialog with Enter/Decline buttons;
  recognises Wintergrasp (zone 4197) and Tol Barad (zone 5095) by name

Implements WotLK Calendar notifications:
- SMSG_CALENDAR_SEND_NUM_PENDING: track pending invite count
- SMSG_CALENDAR_COMMAND_RESULT: map 15 error codes to friendly messages
- SMSG_CALENDAR_EVENT_INVITE_ALERT: notify player of new event invite with title
- SMSG_CALENDAR_EVENT_STATUS: show per-event RSVP status changes (9 statuses)
- SMSG_CALENDAR_RAID_LOCKOUT_ADDED/REMOVED: log raid lockout calendar entries
- Remaining SMSG_CALENDAR_* packets safely consumed
- requestCalendar() sends CMSG_CALENDAR_GET_CALENDAR + GET_NUM_PENDING
2026-03-12 22:25:46 -07:00
Kelsi
dd38026b23 feat: parse SMSG_GMTICKET_GETTICKET/SYSTEMSTATUS and SMSG_SPELLINSTAKILLLOG
Previously SMSG_GMTICKET_GETTICKET and SMSG_GMTICKET_SYSTEMSTATUS were
silently consumed. Now both are fully parsed:
- SMSG_GMTICKET_GETTICKET decodes all four status codes (no ticket,
  open ticket, closed, suspended), extracts ticket text, age and
  server-estimated wait time, and stores them on GameHandler.
- SMSG_GMTICKET_SYSTEMSTATUS shows a chat message when GM support
  goes offline/online.
- Added requestGmTicket() (sends CMSG_GMTICKET_GETTICKET) called
  automatically when the GM Ticket UI window is opened, so the player
  sees their existing open ticket text and wait time on first open.
- GM Ticket UI window now shows current-ticket status bar, estimated
  wait time, and hides the Delete button when no ticket is active.

Also implements SMSG_SPELLINSTAKILLLOG (previously silently consumed):
parses caster/victim/spellId for all expansions and emits combat text
when the local player is involved in an instant-kill spell event (e.g.
Execute, Obliterate).
2026-03-12 22:14:46 -07:00
Kelsi
9b60108fa6 feat: handle SMSG_MEETINGSTONE, LFG timeout, SMSG_WHOIS, and SMSG_MIRRORIMAGE_DATA
Add handlers for 14 previously-unhandled server opcodes:

LFG error/timeout states (WotLK Dungeon Finder):
- SMSG_LFG_TIMEDOUT: invite timed out, shows message and re-opens LFG UI
- SMSG_LFG_OTHER_TIMEDOUT: another player's response timed out
- SMSG_LFG_AUTOJOIN_FAILED: auto-join failed with reason code
- SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER: no players available for auto-join
- SMSG_LFG_LEADER_IS_LFM: party leader is in LFM mode

Meeting Stone (Classic/TBC era group-finding feature):
- SMSG_MEETINGSTONE_SETQUEUE: shows zone and level range in chat
- SMSG_MEETINGSTONE_COMPLETE: group ready notification
- SMSG_MEETINGSTONE_IN_PROGRESS: search ongoing notification
- SMSG_MEETINGSTONE_MEMBER_ADDED: player name resolved and shown in chat
- SMSG_MEETINGSTONE_JOINFAILED: localized error message (4 reason codes)
- SMSG_MEETINGSTONE_LEAVE: queue departure notification

Other:
- SMSG_WHOIS: displays GM /whois result line-by-line in system chat
- SMSG_MIRRORIMAGE_DATA: parses WotLK mirror image unit display ID and
  applies it to the entity so mirror images render with correct appearance
2026-03-12 22:07:03 -07:00
Kelsi
ebaf95cc42 fix: remove Y-flip counter-hacks in FSR shaders; invert mouse by default; FSR1 disables MSAA
FSR EASU and FSR2 sharpen fragment shaders had a manual Y-flip to undo
the now-removed postprocess.vert flip.  Strip those since the vertex
shader no longer flips, making all postprocess paths consistent.

Also flip the default mouse Y-axis to match user expectation (mouse
down = look up / flight-sim style) and make FSR1 disable MSAA on
enable, matching FSR2 behaviour (FSR provides its own spatial AA).
2026-03-12 21:59:41 -07:00
Kelsi
f8f57411f2 feat: implement SMSG_BATTLEFIELD_LIST handler
Parse the battleground availability list sent by the server when the
player opens the BG finder.  Handles all three expansion wire formats:
- Classic: bgTypeId + isRegistered + count + instanceIds
- TBC:     adds isHoliday byte
- WotLK:   adds minLevel/maxLevel for bracket display

Stores results in availableBgs_ (public via getAvailableBgs()) so the
UI can show available battlegrounds and running instance counts without
an additional server round-trip.
2026-03-12 21:54:48 -07:00
Kelsi
793c2b5611 fix: remove incorrect Y-flip in postprocess vertex shader
postprocess.vert.glsl had `TexCoord.y = 1.0 - TexCoord.y` which
inverted the vertical sampling of the scene texture.  Vulkan textures
use v=0 at the top, matching framebuffer row 0, so no flip is needed.
The camera already flips the projection matrix (mat[1][1] *= -1) so
the scene is rendered correctly oriented; the extra inversion in the
postprocess pass flipped FXAA output upside down.

Fixes: FXAA shows camera upside down
Also fixes: FSR1 upscale and FSR3 sharpen passes (same vertex shader)
2026-03-12 21:52:00 -07:00
Kelsi
4c1bc842bc fix: normalize TBC SMSG_INIT_EXTRA_AURA_INFO_OBSOLETE harmful bit to WotLK debuff convention
TBC aura packets (SMSG_INIT_EXTRA_AURA_INFO_OBSOLETE / SMSG_SET_EXTRA_AURA_INFO_OBSOLETE)
use flag bit 0x02 for harmful (debuff) auras, same as Classic 1.12. The UI checks bit 0x80
for debuff display, following the WotLK SMSG_AURA_UPDATE convention. Without normalization,
all TBC debuffs were displayed in the buff bar instead of the debuff bar.

Normalize using (flags & 0x02) ? 0x80 : 0, matching the fix applied to Classic in 9b09278.
2026-03-12 21:39:22 -07:00
Kelsi
9b092782c9 fix: normalize Classic UNIT_FIELD_AURAFLAGS harmful bit to WotLK debuff convention
The buff/debuff bar uses 0x80 (WotLK convention) to identify debuffs.
Classic UNIT_FIELD_AURAFLAGS uses 0x02 for harmful auras instead.
Map Classic 0x02 → 0x80 during aura rebuild so the UI correctly
separates buffs from debuffs for Classic players.
2026-03-12 21:34:16 -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
fb8c251a82 feat: implement SMSG_ACHIEVEMENT_DELETED, SMSG_CRITERIA_DELETED, SMSG_FORCED_DEATH_UPDATE
- SMSG_ACHIEVEMENT_DELETED: removes achievement from earnedAchievements_ and
  achievementDates_ so the achievements UI stays accurate after revocation
- SMSG_CRITERIA_DELETED: removes criteria from criteriaProgress_ tracking
- SMSG_FORCED_DEATH_UPDATE: sets playerDead_ when server force-kills the
  player (GM command, scripted events) instead of silently consuming
2026-03-12 21:28:24 -07:00
Kelsi
758ca76bd3 feat: parse MSG_INSPECT_ARENA_TEAMS and display in inspect window
Implements MSG_INSPECT_ARENA_TEAMS (WotLK): reads the inspected player's
arena team data (2v2/3v3/5v5 bracket, team name, personal rating,
week/season W-L) and stores it in InspectResult.arenaTeams.

The inspect window now shows an "Arena Teams" section below the gear list
when arena team data is available, displaying bracket, team name, rating,
and win/loss record.

Also implement SMSG_COMPLAIN_RESULT with user-visible feedback for
report-player results.
2026-03-12 21:27:02 -07:00
Kelsi
a1edddd1f0 feat: open dungeon finder UI when server sends SMSG_OPEN_LFG_DUNGEON_FINDER
Previously SMSG_OPEN_LFG_DUNGEON_FINDER was consumed silently with no UI
response. Now it fires an OpenLfgCallback wired to openDungeonFinder() on
the GameScreen, so the dungeon finder window opens as the server requests.
2026-03-12 21:24:42 -07:00
Kelsi
e68ffbc711 feat: populate Classic playerAuras from UNIT_FIELD_AURAS update fields
Classic WoW (1.12) does not use SMSG_AURA_UPDATE like WotLK or TBC.
Instead, active aura spell IDs are sent via 48 consecutive UNIT_FIELD_AURAS
slots in SMSG_UPDATE_OBJECT CREATE_OBJECT and VALUES blocks.

Previously these fields were only used for mount spell ID detection.
Now on CREATE_OBJECT and VALUES updates for the player entity (Classic
only), any changed UNIT_FIELD_AURAS slot triggers a full rebuild of
playerAuras from the entity's accumulated field state, enabling the
buff/debuff bar to display active auras for Classic players.
2026-03-12 21:19:17 -07:00
Kelsi
470421879a feat: implement SMSG_GROUP_SET_LEADER and BG player join/leave notifications
- SMSG_GROUP_SET_LEADER: parse leader name, update partyData.leaderGuid
  by name lookup, display system message announcing the new leader
- SMSG_BATTLEGROUND_PLAYER_JOINED: parse guid, show named entry message
  when player is in nameCache
- SMSG_BATTLEGROUND_PLAYER_LEFT: parse guid, show named exit message
  when player is in nameCache

Replaces three LOG_INFO/ignore stubs with functional packet handlers.
2026-03-12 21:08:40 -07:00
Kelsi
7b3578420a fix: correct camera mouse-Y inversion (mouse-down should look down by default)
SDL yrel > 0 means the mouse moved downward. In WoW, moving the mouse
down should decrease pitch (look down), but the previous code did
+= yrel which increased pitch (look up). This made the camera appear
inverted — moving the mouse down tilted the view upward. The invertMouse
option accidentally produced the correct WoW-default behaviour.

Fix: negate the default invert factor so mouse-down = look down without
InvertMouse, and mouse-down = look up when InvertMouse is enabled.
2026-03-12 21:06:07 -07:00
Kelsi
91535fa9ae feat: parse SMSG_ARENA_TEAM_ROSTER and display member list in Arena UI
Add ArenaTeamMember / ArenaTeamRoster structs, parse the WotLK 3.3.5a
roster packet (guid, online flag, name, per-player week/season W/L,
personal rating), store per-teamId, and render a 4-column table
(Name / Rating / Week / Season) inside the existing Arena social tab.
Online members are highlighted green; offline members are greyed out.
2026-03-12 21:01:51 -07:00
Kelsi
a728952058 feat: add defensive and penetration stats to character sheet
Display Defense Rating, Dodge Rating, Parry Rating, Block Rating,
Block Value, Armor Penetration, and Spell Penetration from equipped
items in the Stats tab. Previously these stat types (12-15, 44, 47,
48) were parsed from item data but silently dropped.
2026-03-12 20:55:39 -07:00
Kelsi
5684b16721 fix: talent screen hang (uint8_t overflow) and camera pitch limit
- Change maxRow/maxCol from uint8_t to int in renderTalentTree to prevent
  infinite loop: uint8_t col <= 255 never exits since col wraps 255→0.
  Add sanity cap of 15 rows/cols to guard against corrupt DBC data.
- Fix dangling reference warning in getFormattedTitle (lambda reference)
- Raise MAX_PITCH from 35° to 88° to match WoW standard upward look range
2026-03-12 20:52:58 -07:00
Kelsi
f5d23a3a12 feat: add equipment set manager window (backtick key)
Lists all saved equipment sets from SMSG_EQUIPMENT_SET_LIST with
icon placeholder and an Equip button per set. Clicking either the
icon or the Equip button sends CMSG_EQUIPMENT_SET_USE to swap the
player's gear to that set. Window toggled with the ` (backtick) key.
2026-03-12 20:28:03 -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
a87d62abf8 feat: add melee swing timer bar to player frame during auto-attack
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
Shows a thin progress bar below the player health/power bars whenever
the player is auto-attacking. The bar fills from the last swing timestamp
to the next expected swing based on the main-hand weapon's delay (from
ItemQueryResponseData::delayMs). Falls back to 2.0s for unarmed. Turns
gold and shows "Swing!" when the timer is complete to signal readiness.
Hides when not auto-attacking.
2026-03-12 20:05:36 -07:00
Kelsi
a7261a0d15 feat: trigger camera rumble shake on storm weather transition
When SMSG_WEATHER sets storm (type 3) with intensity > 0.3,
fire a low-frequency (6Hz) camera shake to simulate thunder.
Magnitude scales with intensity: 0.03–0.07 world units.
2026-03-12 19:46:01 -07:00
Kelsi
bba2f20588 feat: implement CMSG_PET_RENAME with rename dialog in pet frame
- Add PetRenamePacket::build(petGuid, name, isDeclined) builder
- Add GameHandler::renamePet(newName) — sends packet via petGuid_
- Add 'Rename Pet' to pet frame context menu (right-click pet name)
- Modal input dialog with 12-char limit matches server validation
2026-03-12 19:42:31 -07:00
Kelsi
9aa4b223dc feat: implement SMSG_CAMERA_SHAKE with sinusoidal camera shake effect
- Add triggerShake(magnitude, frequency, duration) to CameraController
- Apply envelope-decaying sinusoidal XYZ offset to camera in update()
- Handle SMSG_CAMERA_SHAKE opcode in GameHandler dispatch
- Translate shakeId to magnitude (minor <50: 0.04, larger: 0.08 world units)
- Wire CameraShakeCallback from GameHandler through to CameraController
- Shake uses 18Hz oscillation with 30% fade-out envelope at end of duration
2026-03-12 19:37:53 -07:00
Kelsi
214c1a9ff8 feat: enable FXAA alongside FSR3 in settings and add FXAA to Ultra preset
- Remove fsr2Active guard that prevented FXAA when FSR3 was active
- FXAA checkbox now always enabled; tooltip adapts to explain FSR3+FXAA combo
  when FSR3 is active ('recommended ultra-quality combination')
- Performance HUD shows 'FXAA: ON (FSR3+FXAA combined)' when both active
- Ultra graphics preset now enables FXAA (8x MSAA + FXAA for max smoothness)
- Preset detection updated to require FXAA for Ultra match
2026-03-12 19:27:00 -07:00
Kelsi
284b98d93a feat: implement pet stable system (MSG_LIST_STABLED_PETS, CMSG_STABLE_PET, CMSG_UNSTABLE_PET)
- Parse MSG_LIST_STABLED_PETS (SMSG): populate StabledPet list with
  petNumber, entry, level, name, displayId, and active status
- Detect stable master via gossip option text/keyword matching and
  auto-send MSG_LIST_STABLED_PETS request to open the stable UI
- Refresh list automatically after SMSG_STABLE_RESULT to reflect state
- New packet builders: ListStabledPetsPacket, StablePetPacket, UnstablePetPacket
- New public API: requestStabledPetList(), stablePet(slot), unstablePet(petNumber)
- Stable window UI: shows active/stabled pets with store/retrieve buttons,
  slot count, refresh, and close; opens when server sends pet list
- Clear stable state on world logout/disconnect
2026-03-12 19:15:52 -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
cd01d07a91 fix: wait for GPU idle before freeing M2/WMO model buffers to prevent device lost
cleanupUnusedModels() runs every 5 seconds and freed vertex/index buffers
without waiting for the GPU to finish the previous frame's command buffer.
This caused VK_ERROR_DEVICE_LOST (-4) after extended gameplay when tiles
stream out and their models are freed mid-render.

Add vkDeviceWaitIdle() before the buffer destroy loop in both M2Renderer
and WMORenderer cleanupUnusedModels(). The wait only happens when there are
models to remove, so quiet sessions have no overhead.
2026-03-12 19:01:15 -07:00
Kelsi
eafd09aca0 feat: allow FXAA alongside FSR1 and FSR3 simultaneously
- Remove !fsr_.enabled / !fsr2_.enabled guards that blocked FXAA init
- FXAA can now coexist with FSR1 and FSR3 simultaneously
- Priority: FSR3 > FXAA > FSR1
  - FSR3 + FXAA: scene renders at FSR3 internal res, temporal AA runs,
    then FXAA reads FSR3 history and applies spatial AA to swapchain
    (replaces RCAS sharpening for ultra-quality native mode)
  - FXAA + FSR1: scene renders at native res, FXAA post-processes;
    FSR1 resources exist but are idle (FXAA wins for better quality)
  - FSR3 only / FSR1 only: unchanged paths
- Fix missing fxaa.frag.spv: shader was present but uncompiled; the
  CMake compile_shaders() function will now pick it up on next build
2026-03-12 18:58:30 -07:00
Kelsi
a97941f062 feat: implement SMSG_PET_ACTION_FEEDBACK with human-readable messages
Parse pet action feedback opcodes and display messages in system chat:
dead, nothing_to_attack, cant_attack_target, target_too_far,
no_path, cant_attack_immune. Replaces consume stub.
2026-03-12 18:25:02 -07:00
Kelsi
5883654e1e feat: replace page-text chat-dump with proper book/scroll window
handlePageTextQueryResponse() now collects pages into bookPages_ vector
instead of dumping lines to system chat. Multi-page items (nextPageId != 0)
are automatically chained by requesting subsequent pages. The book window
opens automatically when pages arrive, shows formatted text in a parchment-
styled ImGui window with Prev/Next page navigation and a Close button.

SMSG_READ_ITEM_OK clears bookPages_ so each item read starts fresh;
handleGameObjectPageText() does the same before querying the first page.
Closes the long-standing issue where reading scrolls and tattered notes
spammed many separate chat messages instead of showing a readable UI.
2026-03-12 18:21:50 -07:00
Kelsi
218d68e275 feat: parse Classic SMSG_INSPECT gear + implement temp weapon enchant timers
Classic 1.12 SMSG_INSPECT (wire 0x115): parse PackedGUID + 19×uint32
itemEntries to populate InspectResult and inspectedPlayerItemEntries_ cache,
enabling gear inspection of other players on Classic servers. Triggers item
queries for all filled slots so the inspect window shows names/ilevels.

SMSG_ITEM_ENCHANT_TIME_UPDATE: parse itemGuid/slot/durationSec/playerGuid and
store per-slot expire timestamps in tempEnchantTimers_. Fires 5min/1min
chat warnings before expiry. getTempEnchantRemainingMs() helper queries live
remaining time. Buff bar renders timed slot buttons (gold/teal/purple per
slot) that pulse red below 60s — useful for Shaman imbues, Rogue poisons,
whetstones and oils across all three expansions.
2026-03-12 18:15:51 -07:00
Kelsi
2f479c6230 feat: implement master loot UI for SMSG_LOOT_MASTER_LIST
Parse master loot candidate GUIDs from SMSG_LOOT_MASTER_LIST and display
a "Give to..." popup menu on item click when master loot is active.
Sends CMSG_LOOT_MASTER_GIVE with loot GUID, slot, and target GUID.
Clears candidates when loot window is closed.
2026-03-12 17:58:24 -07:00
Kelsi
6957ba97ea feat: show stat gains in level-up toast from SMSG_LEVELUP_INFO
Parse hp/mana/str/agi/sta/int/spi deltas from SMSG_LEVELUP_INFO payload
and display them in green below the "You have reached level X!" banner.
Extends DING_DURATION to 4s to give players time to read the gains.
2026-03-12 17:54:49 -07:00
Kelsi
6df8c72cf7 feat: add WotLK equipment set UI to character screen
- Expose equipment sets via public EquipmentSetInfo getter
- Populate equipmentSetInfo_ from handleEquipmentSetList()
- Implement useEquipmentSet() sending CMSG_EQUIPMENT_SET_USE
- Add "Outfits" tab in character screen listing saved sets with Equip button
2026-03-12 17:48:08 -07:00
Kelsi
882cb1bae3 feat: implement WotLK glyph display in talent screen
Store glyph IDs from SMSG_TALENTS_INFO (previously discarded) in
learnedGlyphs_[2][6] per talent spec. Load GlyphProperties.dbc to
map glyphId to spellId and major/minor type. Add a Glyphs tab to
the talent screen showing all 6 slots with spell icons and names.
Also clear vehicleId_ on SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA.
2026-03-12 17:39:35 -07:00
Kelsi
b7c1aa39a9 feat: add WotLK vehicle exit UI with Leave Vehicle button
Parse SMSG_PLAYER_VEHICLE_DATA (PackedGuid + uint32 vehicleId) and
track in-vehicle state. Add sendRequestVehicleExit() which sends
CMSG_REQUEST_VEHICLE_EXIT. Render a floating red "Leave Vehicle"
button above the action bar whenever vehicleId_ is non-zero.
State cleared on world leave and zone transfer.
2026-03-12 17:25:00 -07:00
Kelsi
fbcec9e7bf feat: show FXAA status in performance HUD 2026-03-12 17:01:20 -07:00
Kelsi
806744c483 refactor: consolidate duplicate quest trackers; make primary tracker draggable
Remove the redundant renderObjectiveTracker (simpler, fixed-position) and
apply draggable position tracking to renderQuestObjectiveTracker (the primary
tracker with context menus, item icons, and click-to-open-quest-log).

- questTrackerPos_ / questTrackerPosInit_ now drive the primary tracker
- Default position is top-right (below minimap at y=320)
- Drag saves to settings.cfg immediately (quest_tracker_x/y keys)
- Both trackers were rendering simultaneously — this eliminates the duplicate
2026-03-12 16:52:12 -07:00
Kelsi
925d15713c feat: make quest tracker draggable with persistent position 2026-03-12 16:47:42 -07:00
Kelsi
6e95709b68 feat: add FXAA post-process anti-aliasing, combinable with MSAA 2026-03-12 16:43:48 -07:00
Kelsi
819a690c33 feat: show resurrection flash banner when player transitions ghost→alive 2026-03-12 16:33:08 -07:00
Kelsi
42d66bc876 feat: show quality-coloured loot toast when items are received
SMSG_ITEM_PUSH_RESULT now fires a new ItemLootCallback that
game_screen.cpp uses to push a compact slide-in toast at the
bottom-left of the screen.  Each toast:

- Shows a quality-tinted left accent bar (grey/white/green/blue/
  purple/orange matching WoW quality colours)
- Displays "Loot: <item name>" with the name in quality colour
- Appends " x<N>" for stacked pickups
- Coalesces repeated pickups of the same item (adds count, resets timer)
- Stacks up to 5 entries, 3 s lifetime with 0.15 s slide-in and 0.7 s
  fade-out
2026-03-12 16:24:11 -07:00
Kelsi
129fa84fe3 feat: add PvP honor credit toast on honorable kill
SMSG_PVP_CREDIT previously only wrote a system chat message.  Now it
also fires a new PvpHonorCallback, which game_screen.cpp uses to push
a compact dark-red toast at the top-right of the screen showing
"⚔ +N Honor" with a 3.5 s lifetime and smooth fade in/out.
2026-03-12 16:19:25 -07:00
Kelsi
59fc7cebaf feat: show toast when a nearby player levels up
When the server sends a level-up update for a non-self player entity,
fire the existing OtherPlayerLevelUpCallback which was previously
unregistered in the UI layer.  GameScreen now:

- Registers the callback once and stores {guid, level} entries
- Lazily resolves the player name from the name cache at render time
- Renders gold-bordered toasts bottom-centre with a ★ icon and fade/slide
  animation ("Thrall is now level 60!"), coalescing duplicates
- Prunes entries after 4 s with a 1 s fade-out
2026-03-12 16:12:21 -07:00
Kelsi
b52e9c29c6 feat: highlight quest GO objectives as cyan triangles on the minimap
Quest game-object objectives (negative npcOrGoId entries, e.g. gather 5
crystals) now render as larger bright-cyan triangles distinct from the
standard amber GO markers. Tooltip appends "(quest)" to the name.
Also refactors the minimap quest-entry build to track both NPC and GO
kill-objective entries from the tracked quest log.
2026-03-12 16:05:34 -07:00
Kelsi
3f340ca235 feat: highlight quest kill objective mobs as gold dots on the minimap
Quest kill objective NPCs are now rendered as larger gold dots (3.5px)
with a dark outline on the minimap, distinct from standard hostile (red)
and friendly (white) dots. Only shows mobs for incomplete objectives in
tracked quests (or all active quests if none are tracked). Hovering the
dot shows a tooltip with the unit name and "(quest)" annotation.
2026-03-12 15:59:30 -07:00
Kelsi
c3afe543c6 feat: show quest objective progress toasts on kill and item collection
Adds a visual progress overlay at bottom-right when quest kill counts
or item collection updates arrive. Each toast shows the quest title,
objective name, a fill-progress bar, and an X/Y count. Toasts coalesce
when the same objective updates multiple times, and auto-dismiss after 4s.
Wires a new QuestProgressCallback through GameHandler to trigger the UI.
2026-03-12 15:57:09 -07:00
Kelsi
5216582f15 feat: show whisper toast notification when a player whispers you
Adds a slide-in toast overlay at the bottom-left of the screen whenever
an incoming whisper arrives. Toasts display "Whisper from:", the sender
name in gold, and a truncated message preview. Up to 3 toasts stack with
a 5s lifetime; each fades in over 0.25s and fades out in the final 1s.
2026-03-12 15:53:45 -07:00
Kelsi
77879769d3 feat: show area discovery toast with XP gain when exploring new zones 2026-03-12 15:42:55 -07:00
Kelsi
98ad71df0d feat: show class-colored health bars on player nameplates 2026-03-12 15:36:25 -07:00
Kelsi
9e4c3d67d9 feat: show interactable game object dots on minimap when NPC dots enabled 2026-03-12 15:28:31 -07:00
Kelsi
78ad20f95d feat: add cooldown tracker panel showing all active spell cooldowns
A new opt-in panel (Settings > Interface > Show Cooldown Tracker) lists
all spells currently on cooldown, sorted longest-to-shortest, with
spell icons and color-coded remaining time (red>30s, orange>10s,
yellow>5s, green<5s). Adds getSpellCooldowns() accessor to GameHandler.
Setting persists to ~/.wowee/settings.cfg.
2026-03-12 15:25:07 -07:00
Kelsi
c503bc9432 feat: show nearby other-player dots on minimap when NPC dots are enabled 2026-03-12 15:20:31 -07:00
Kelsi
39fc6a645e feat: show party member dots on minimap with class colors and name tooltip
Small colored squares appear on the minimap for each online party member
at their server-reported position. Dots use WoW class colors when the
entity is loaded, gold for the party leader, and light blue otherwise;
dead members show as gray. Hovering a dot shows the member's name.
2026-03-12 15:19:08 -07:00
Kelsi
bff690ea53 feat: show zone name tooltip on party member name hover in party frame 2026-03-12 15:11:09 -07:00
Kelsi
e2f36f6ac5 feat: show party member dots on world map with name labels and class colors 2026-03-12 15:05:52 -07:00
Kelsi
6dc630c1d8 feat: add Arena tab to Social frame showing per-team rating and weekly/season record 2026-03-12 14:58:48 -07:00
Kelsi
6645845d05 feat: show aura stack/charge count on boss frame aura icons for parity 2026-03-12 14:53:14 -07:00
Kelsi
c0c750a76e feat: show aura stack/charge count on focus frame aura icons for parity with target frame 2026-03-12 14:50:59 -07:00
Kelsi
6044739661 feat: show group leader crown on world nameplate for party/raid leader players 2026-03-12 14:48:53 -07:00
Kelsi
61412ae06d feat: show group leader crown on player frame when you are party/raid leader 2026-03-12 14:47:51 -07:00
Kelsi
d682ec4ca7 feat: show group leader crown on focus frame for parity with target frame 2026-03-12 14:43:58 -07:00
Kelsi
2f234af43b feat: show group leader crown on target frame when targeting party leader 2026-03-12 14:39:29 -07:00
Kelsi
6d21a8cb8d feat: add distance indicator to focus frame for range awareness 2026-03-12 14:34:21 -07:00
Kelsi
b44ff09b63 feat: add pulsing golden glow to Attack action bar slot when auto-attacking 2026-03-12 14:32:15 -07:00
Kelsi
950a4e2991 feat: show raid mark icon on focus frame to match target frame parity 2026-03-12 14:30:15 -07:00
Kelsi
65f19b2d53 feat: show durability warning overlay when gear is damaged or broken
Displays a bottom-center HUD banner when any equipped item drops below
20% durability (yellow) or reaches 0 (red "broken" alert), matching
WoW's own repair reminder UX.
2026-03-12 14:25:37 -07:00
Kelsi
d34f505eea feat: add quest indicator, classification badge, and subtitle to focus frame
Mirrors the target frame improvements: NPC focus targets now show
quest giver ! / ? indicators, Elite/Rare/Boss/Rare Elite badges,
and the creature subtitle (e.g. '<Grand Marshal of the Alliance>').
Keeps the focus and target frames in consistent feature parity.
2026-03-12 14:21:02 -07:00
Kelsi
9494b1e607 feat: show quest giver ! and ? indicators on nameplates
For non-hostile NPCs with quest status data, displays a colored symbol
to the right of the nameplate name:
  !  (gold)  — quest available
  !  (gray)  — low-level quest
  ?  (gold)  — quest ready to turn in
  ?  (gray)  — quest incomplete

Displayed adjacent to the existing quest-kill sword icon, maintaining
the existing icon offset logic so both can coexist.
2026-03-12 14:18:22 -07:00
Kelsi
d2db0b46ff feat: show quest giver ! and ? indicators in target frame
Reads QuestGiverStatus from the existing npcQuestStatus_ cache and
displays a colored badge next to the target's name:
  !  (gold)  — quest available
  !  (gray)  — low-level quest available
  ?  (gold)  — quest ready to turn in
  ?  (gray)  — quest incomplete / in progress
Matches the standard WoW quest indicator convention.
2026-03-12 14:15:45 -07:00
Kelsi
1165aa6e74 feat: display creature subtitle in target frame
Shows the NPC subtitle (e.g. '<Warchief of the Horde>') below the
creature name in the target frame, using the subName field already
parsed from SMSG_CREATURE_QUERY_RESPONSE. Adds getCachedCreatureSubName()
accessor to GameHandler. Matches the official client's presentation.
2026-03-12 14:14:25 -07:00
Kelsi
8cb0f1d0ef feat: show Elite/Rare/Boss classification badge in target frame
Reads creature rank (0=Normal, 1=Elite, 2=RareElite, 3=Boss, 4=Rare)
from the existing creatureInfoCache populated by creature query responses.
Shows a colored badge next to the level: gold for Elite, purple for
Rare Elite, red for Boss, cyan for Rare — each with a tooltip. Adds
getCreatureRank() accessor to GameHandler for UI use.
2026-03-12 14:13:09 -07:00
Kelsi
a03ee33f8c feat: add power bar to boss frames for energy/mana tracking
Energy bosses (e.g. Anub'arak, various WotLK encounters) use energy as
their ability cooldown mechanic — tracking it in the boss frame lets
raiders anticipate major ability casts. Mana, rage, focus, and energy
all shown with type-appropriate colors as a slim 6px bar below HP.
2026-03-12 14:09:01 -07:00
Kelsi
fb843026ad feat: add LFG queue time indicator below minimap with role-check pulsing 2026-03-12 14:00:14 -07:00
Kelsi
e2b2425230 feat: add cast bar to target-of-target frame with pulsing interrupt warning 2026-03-12 13:58:30 -07:00
Kelsi
f39ba56390 feat: show raid mark symbols on raid frame cells beside leader crown 2026-03-12 13:50:46 -07:00
Kelsi
e6f48dd822 feat: show raid mark symbols on minimap party member dots with name tooltip 2026-03-12 13:48:01 -07:00
Kelsi
1e76df7c98 feat: show raid mark symbols on party frame member names 2026-03-12 13:47:02 -07:00
Kelsi
3d1b187986 feat: add aura icons to boss frame with DoT tracking and duration overlays 2026-03-12 13:43:12 -07:00
Kelsi
3665723622 feat: add aura icons to target-of-target frame with debuff coloring and tooltips 2026-03-12 13:39:36 -07:00
Kelsi
abfb6ecdb5 feat: add spell name tooltips to nameplate debuff dots on hover
When hovering over a player-applied DoT/debuff indicator square on an
enemy nameplate, the spell name is now shown as a tooltip. Uses direct
mouse-position hit test since nameplates render into the background
draw list rather than an ImGui window.
2026-03-12 13:36:06 -07:00
Kelsi
8d7391d73e feat: upgrade pet action bar to rich spell tooltips with autocast status
Pet ability buttons now show full spell info (name, description, range,
cost, cooldown) instead of just the spell name. Built-in commands (Follow,
Stay, Attack, etc.) keep their existing simple labels. Autocast-enabled
spells show "Autocast: On" at the bottom of the tooltip.
2026-03-12 13:33:48 -07:00
Kelsi
c76ac579cb feat: add aura icons to focus frame with rich tooltips and duration overlays
The focus frame now shows buff/debuff icons matching the target frame:
debuffs first with dispel-type border colors, buffs after with green
borders, duration countdowns on each icon, and rich spell info tooltips
on hover. Uses getUnitAuras() falling back to getTargetAuras() when
focus happens to also be the current target.
2026-03-12 13:32:10 -07:00
Kelsi
9336b2943c feat: add debuff dots to raid frame and NPC name tooltips on minimap quest markers
- Raid frame now shows dispellable debuff dots (magic/curse/disease/poison)
  in the bottom of each cell, matching the existing party frame behavior;
  hovering a dot shows the debuff type and spell names for that dispel type
- Minimap quest giver dots (! and ?) now show a tooltip with the NPC name
  and whether the NPC has a new quest or a quest ready to turn in
2026-03-12 13:28:49 -07:00
Kelsi
d46feee4fc feat: show debuff type and spell names on party frame debuff dot hover 2026-03-12 13:23:21 -07:00
Kelsi
2268f7ac34 feat: add item tooltips to quest tracker overlay item objectives 2026-03-12 13:22:20 -07:00
Kelsi
6ffc0cec3d feat: show spell tooltip on hover in combat log 2026-03-12 13:21:00 -07:00
Kelsi
fe4fc714c3 feat: add item tooltips to quest objective item tracking 2026-03-12 13:19:10 -07:00
Kelsi
0ffcf001a5 feat: show full item tooltip on action bar item hover 2026-03-12 13:14:24 -07:00
Kelsi
d7c4bdcd57 feat: add item tooltips to quest reward items in quest log 2026-03-12 13:12:24 -07:00
Kelsi
3ea1b96681 feat: show spell description in trainer window tooltip
Trainer spell tooltips now show the spell's effect description from
Spell.dbc (e.g. "Sends a shadowy bolt at the enemy...") above the
status/requirement lines, matching the WoW trainer UI style.
Also styles the spell name yellow (like WoW) and moves status to
TextDisabled for better visual hierarchy.
2026-03-12 13:09:40 -07:00
Kelsi
a10139284d feat: show spell tooltip text instead of name in item spell effects
Item "Equip:" and "Use:" spell effects now display the spell's
description text from Spell.dbc (e.g. "Increases your Spell Power by 30.")
rather than the internal spell name (e.g. "Mana Spring Totem").
Falls back to the name when description is unavailable (e.g. older DBCs).
Adds getSpellDescription() to GameHandler, backed by the existing
loadSpellNameCache() pass which now reads the Tooltip field.
2026-03-12 13:08:41 -07:00
Kelsi
ed2b50af26 fix: look up socket bonus name from SpellItemEnchantment.dbc
socketBonus is a SpellItemEnchantment entry ID, not a spell ID.
Previously getSpellName() was called on it, which produced wrong or
empty results. Now a lazy SpellItemEnchantment.dbc cache in the item
tooltip correctly resolves names like "+6 All Stats".
2026-03-12 12:57:15 -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
Kelsi
bc0d98adae feat: show item set piece count and active bonuses in item tooltip 2026-03-12 12:43:53 -07:00
Kelsi
a56b50df2b feat: show average item level in character stats panel 2026-03-12 12:41:05 -07:00
Kelsi
48f12d9ca8 feat: parse item set ID and display set name in item tooltip via ItemSet.dbc 2026-03-12 12:35:56 -07:00
Kelsi
d48e4fb7c3 feat: resolve enchant names from SpellItemEnchantment.dbc in inspect window 2026-03-12 12:32:19 -07:00
Kelsi
60794c6e0f feat: track and display elemental resistances in character stats panel 2026-03-12 12:24:15 -07:00
Kelsi
0a2cd213dc feat: display gem socket slots and socket bonus in item tooltips 2026-03-12 12:15:08 -07:00
Kelsi
a90f2acd26 feat: populate unitAurasCache_ from SMSG_PARTY_MEMBER_STATS aura block
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
SMSG_PARTY_MEMBER_STATS includes a 64-bit aura presence mask + per-slot
spellId/flags that was previously read and discarded. Now populate
unitAurasCache_[memberGuid] from this data so party frame debuff dots
show even when no dedicated SMSG_AURA_UPDATE has been received for that
unit. For Classic/TBC (no flags byte), infer debuff status from dispel
type — any spell with a non-zero dispel type is treated as a debuff.
2026-03-12 12:07:46 -07:00
Kelsi
562fc13a6a fix: wire TOGGLE_CHARACTER_SCREEN (C) and TOGGLE_BAGS (B) keybindings
Both actions were defined in KeybindingManager but never wired to the
input handling block — C had no effect and B did nothing. Connect them:
- C toggles inventoryScreen's character panel (equipment slots view)
- B opens all separate bags, or falls back to toggling the unified view
2026-03-12 12:05:05 -07:00
Kelsi
79c0887db2 feat: add BG scoreboard (MSG_PVP_LOG_DATA) and fix TBC aura cache for party frames
- Parse MSG_PVP_LOG_DATA to populate BgScoreboardData (players, KB, deaths,
  HKs, honor, BG-specific stats, winner)
- Add /score command to request the scorecard while in a battleground
- Render sortable per-player table with team color-coding and self-highlight
- Refresh button re-requests live data from server
- Fix TBC SMSG_INIT/SET_EXTRA_AURA_INFO_OBSOLETE to populate unitAurasCache_
  for all GUIDs (not just player/target), mirroring WotLK aura update behavior
  so party frame debuff dots work on TBC servers
2026-03-12 12:02:59 -07:00
Kelsi
a4c23b7fa2 feat: add per-unit aura cache and dispellable debuff indicators on party frames
Extend the aura tracking system to cache auras for any unit (not just
player and current target), so healers can see dispellable debuffs on
party members. Colored 8px dots appear below the power bar:
  Magic=blue, Curse=purple, Disease=brown, Poison=green
One dot per dispel type; non-dispellable auras are suppressed.
Cache is populated via existing SMSG_AURA_UPDATE/SMSG_AURA_UPDATE_ALL
handling and cleared on world exit.
2026-03-12 11:44:30 -07:00
Kelsi
9c276d1072 feat: add encounter-level DPS/HPS tracking to DPS meter
Track cumulative player damage/healing for the full combat encounter
using the persistent CombatLog, shown alongside the existing 2.5s
rolling window. The encounter row appears after 3s of combat and
persists post-combat until the next engagement, giving a stable
full-fight average rather than the spiky per-window reading.
2026-03-12 11:40:31 -07:00
Kelsi
a336bebbe5 feat: add debuff dot indicators on hostile target nameplate
Show small colored squares below the health bar of the current
hostile target indicating player-applied auras. Colors map to
dispel types from Spell.dbc: blue=Magic, purple=Curse,
yellow=Disease, green=Poison, grey=other/physical.

Dots are positioned below the cast bar if one is active,
otherwise directly below the health bar. They are clipped
to the nameplate width and only rendered for the targeted
hostile unit to keep the display readable.
2026-03-12 11:31:45 -07:00
Kelsi
f3e399e0ff feat: show unread message count on chat tabs
Each non-General chat tab now shows an unread count in parentheses
(e.g. "Whispers (3)") when messages arrive while that tab is inactive.
The counter clears when the tab is selected. The General tab is excluded
since it shows all messages anyway.
2026-03-12 11:23:01 -07:00
Kelsi
db1f111054 feat: add Guild chat tab and fix Trade/LFG tab index after insertion
Inserts a dedicated "Guild" tab between Whispers and Trade/LFG that
shows guild, officer, and guild achievement messages. Updates the
Trade/LFG channel-name filter from hardcoded index 3 to 4 to match
the new tab order.
2026-03-12 11:21:12 -07:00
Kelsi
afcd6f2db3 feat: add CHANNEL option to chat type dropdown with channel picker
Adds an 11th chat type "CHANNEL" to the dropdown, displaying a secondary
combo box populated from the player's joined channels. Typing /1, /2 etc.
in the input now also auto-switches the dropdown to CHANNEL mode and
selects the corresponding channel. Input text is colored cyan for channel
messages to visually distinguish them from other chat types.
2026-03-12 11:16:42 -07:00
Kelsi
20fef40b7b feat: show area trigger messages as screen banners
SMSG_AREA_TRIGGER_MESSAGE events (dungeon enter messages, objective
triggers, etc.) were previously only appended to chat. Now they also
appear as animated slide-up toasts in the lower-center of the screen:
blue-bordered dark panel with light-blue text, 4.5s lifetime with
35ms slide-in/out animation. Up to 4 simultaneous toasts stack
vertically. Messages still go to chat as before.
2026-03-12 11:06:40 -07:00
Kelsi
9fe7bbf826 feat: show lootable corpse diamonds on minimap
Dead units with UNIT_DYNFLAG_LOOTABLE (0x0001) set are rendered as small
yellow-green diamonds on the minimap, distinct from live NPC dots. A hover
tooltip shows the unit name. Uses the dynamic flags already tracked by the
update-object parser, so no new server data is needed.
2026-03-12 11:04:10 -07:00
Kelsi
661f7e3e8d feat: add persistent combat log window (/combatlog or /cl)
Stores up to 500 combat events in a rolling deque alongside the existing
floating combat text. Events are populated via the existing addCombatText()
call site, resolving attacker/target names from the entity manager and
player name cache at event time.

- CombatLogEntry struct in spell_defines.hpp (type, amount, spellId,
  isPlayerSource, timestamp, sourceName, targetName)
- getCombatLog() / clearCombatLog() accessors on GameHandler
- renderCombatLog() in GameScreen: scrollable two-column table (Time +
  Event), color-coded by event category, with Damage/Healing/Misc filter
  checkboxes, auto-scroll toggle, and Clear button
- /combatlog (/cl) chat command toggles the window
2026-03-12 11:00:10 -07:00
Kelsi
36d40905e1 feat: add in-window search bar to who results window
Add a search field with "Search" button directly in the who results
window so players can query without using the chat box. Pressing Enter
in the search field also triggers a new /who query.
2026-03-12 10:45:31 -07:00
Kelsi
367390a852 feat: add /who results window with sortable player table
Store structured WhoEntry data from SMSG_WHO responses and show them
in a dedicated popup window with Name/Guild/Level/Class/Zone columns.
Right-click on any row to Whisper, Invite, Add Friend, or Ignore.
Window auto-opens when /who or /whois is typed; shows online count
in the title bar. Results persist until the next /who query.
2026-03-12 10:41:18 -07:00
Kelsi
2f0fe302bc feat: add Trade, Duel, and Inspect to nameplate player context menu 2026-03-12 10:27:51 -07:00
Kelsi
1f7f1076ca feat: add Shaman totem frame with element-colored duration bars
- Renders below pet frame, visible only for Shaman (class 7)
- Shows each active totem (Earth/Fire/Water/Air) with its spell name
  and a colored countdown progress bar
- Colored element dot (brown/red/blue/light-blue) identifies element
- Only rendered when at least one totem is active
2026-03-12 10:14:44 -07:00
Kelsi
ef08366efc feat: add /use and /equip slash commands for inventory items by name
- /use <item name>  — searches backpack then bags (case-insensitive),
  calls useItemBySlot() / useItemInBag() for the first match
- /equip <item name> — same search, calls autoEquipItemBySlot() /
  autoEquipItemInBag() for the first match
- Both commands print an error if the item is not found
- Added both to tab-autocomplete list and /help output
2026-03-12 10:06:11 -07:00
Kelsi
611946bd3e feat: add Trade and Set Note to social frame friends context menu
- "Trade" option initiates a trade via existing initiateTrade(guid) API
  (only shown for online friends with a known GUID)
- "Set Note" option opens an inline popup with InputText pre-filled with
  the current note; Enter or OK saves via setFriendNote(), Esc/Cancel discards
2026-03-12 10:01:35 -07:00
Kelsi
71b0a18238 feat: implement quest sharing with party via CMSG_PUSHQUESTTOPARTY 2026-03-12 09:56:38 -07:00
Kelsi
e781ede5b2 feat: implement /chathelp command with channel-specific help text 2026-03-12 09:45:03 -07:00
Kelsi
88c3cfe7ab feat: show pet happiness bar in pet frame for hunter pets 2026-03-12 09:43:23 -07:00
Kelsi
84b31d3bbd feat: show spell name in proc trigger combat text when spellId is known 2026-03-12 09:41:45 -07:00
Kelsi
aaae07e477 feat: implement /wts and /wtb trade channel shortcuts 2026-03-12 09:38:29 -07:00
Kelsi
ec93981a9d feat: add /stopfollow and /zone slash commands, update /help 2026-03-12 09:36:14 -07:00
Kelsi
aaa3649975 feat: implement /cast slash command with rank support 2026-03-12 09:30:59 -07:00
Kelsi
c1a090a17c feat: show kick target name and reason in LFG vote-kick UI
Parse the optional reason and target name strings from
SMSG_LFG_BOOT_PROPOSAL_UPDATE and display them in the Dungeon
Finder vote-kick section. Strings are cleared when the vote ends.
2026-03-12 09:09:41 -07:00
Kelsi
d8d59dcdc8 feat: show live per-player responses in ready check popup
Track each player's ready/not-ready response as MSG_RAID_READY_CHECK_CONFIRM
packets arrive. Display a color-coded table (green=Ready, red=Not Ready) in
the ready check popup so the raid leader can see who has responded in real
time. Results clear when a new check starts or finishes.
2026-03-12 09:07:37 -07:00
Kelsi
09d4a6ab41 feat: add pet stance indicator and Passive/Defensive/Aggressive buttons to pet frame
Show Psv/Def/Agg stance buttons (color-coded blue/green/red) above the
pet action bar. Active stance is highlighted; clicking sends CMSG_PET_ACTION
with the server-provided slot value for correct packet format, falling back
to the wire-protocol action ID if the slot is not in the action bar.
Also label stance slots 1/4/6 in the action bar as Psv/Def/Agg with
proper full-name tooltips.
2026-03-12 09:05:28 -07:00
Kelsi
fb8377f3ca fix: dismiss loot roll popup when any player wins, not only when we win
SMSG_LOOT_ROLL_WON signals the roll contest is over for this slot;
clear pendingLootRollActive_ unconditionally so the popup does not
linger if a different group member wins while we have not yet voted.
2026-03-12 09:00:31 -07:00
Kelsi
7acaa4d301 feat: show live roll results from group members in loot roll popup
Track each player's roll (need/greed/disenchant/pass + value) as
SMSG_LOOT_ROLL packets arrive while our roll window is open. Display
a color-coded table in the popup: green=need, blue=greed,
purple=disenchant, gray=pass. Roll value hidden for pass.
2026-03-12 08:59:38 -07:00
Kelsi
8a24638ced fix: use uint64_t for chat tab typeMask to avoid UB for ChatType values >= 32, add missing boss/party chat types to Combat tab 2026-03-12 08:54:29 -07:00
Kelsi
eaf60b4f79 feat: apply class color to target-of-target frame player names 2026-03-12 08:50:14 -07:00
Kelsi
ee7de739fc fix: include class name in /who response chat messages 2026-03-12 08:49:18 -07:00
Kelsi
641f943268 feat: inspect window shows class-colored name, class label, and average item level 2026-03-12 08:47:59 -07:00
Kelsi
0b14141e97 refactor: consolidate all class name/color lookups to shared helpers, remove 4 duplicate class tables 2026-03-12 08:46:26 -07:00
Kelsi
30b821f7ba refactor: replace duplicate class color switch in social frame with classColorVec4 helper, color friend names by class 2026-03-12 08:44:08 -07:00
Kelsi
339ee4dbba refactor: use classColorVec4 helper in guild roster, color member names by class 2026-03-12 08:42:55 -07:00
Kelsi
b5131b19a3 feat: color minimap party dots by class instead of uniform blue 2026-03-12 08:40:54 -07:00
Kelsi
f4a31fef2a feat: apply class colors to focus frame player name
Player entities shown in the focus frame now display their canonical
WoW class color (from UNIT_FIELD_BYTES_0), consistent with how player
names are now colored in the target frame, party frame, raid frame,
and nameplates.
2026-03-12 08:37:14 -07:00
Kelsi
c73da1629b refactor: use classColorVec4 helper in player frame
Replace the duplicated 10-case switch in renderPlayerFrame with a call
to the shared classColorVec4() helper, keeping the single source of truth
for Blizzard class colors.
2026-03-12 08:35:47 -07:00
Kelsi
41d121df1d refactor: consolidate class color lookups into shared helpers
Add classColorVec4(), classColorU32(), and entityClassId() to the
anonymous namespace so the canonical Blizzard class colors are defined
in exactly one place. Refactor the three existing class color blocks
(party frame, raid frame, nameplates) to use these helpers. Also apply
class colors to player names in the target frame.
2026-03-12 08:33:34 -07:00
Kelsi
8f68d1efb9 feat: show WoW class colors on player nameplates
Player nameplates previously used a flat cyan for all players. Now they
display the canonical Blizzard class color (Warrior=#C79C6E,
Paladin=#F58CBA, Hunter=#ABD473, etc.) read from UNIT_FIELD_BYTES_0.
This makes it easy to identify player classes at a glance in the world,
especially useful in PvP and group content. NPC nameplates keep the
existing red (hostile) / yellow (friendly) coloring.
2026-03-12 08:30:27 -07:00
Kelsi
d6d70f62c7 feat: apply WoW class colors to raid frame member names
Same class color logic as the party frame: read UNIT_FIELD_BYTES_0 byte 1
from the entity manager to determine each member's class, then draw their
name in the canonical Blizzard class color. Dead/offline members keep the
gray color since their status is more important than their class identity.
2026-03-12 08:29:17 -07:00
Kelsi
25e90acf27 feat: color party frame member names by WoW class
Uses UNIT_FIELD_BYTES_0 (byte 1) from the entity's update fields to
determine each party member's class when they are loaded in the world,
and applies canonical WoW class colors to their name in the 5-man
party frame. Falls back to gold (leader) or light gray (others) when
the entity is not currently loaded. All 10 classes (Warrior, Paladin,
Hunter, Rogue, Priest, Death Knight, Shaman, Mage, Warlock, Druid)
use the standard Blizzard-matching hex values.
2026-03-12 08:28:08 -07:00
Kelsi
2fbf3f28e6 feat: use colored coin display for quest reward money in quest log 2026-03-12 08:21:52 -07:00
Kelsi
92ce7459bb feat: add dispel-type border colors to target frame debuffs (magic/curse/disease/poison) 2026-03-12 08:20:14 -07:00
Kelsi
dedafdf443 feat: use colored coin display for sell price in item tooltips 2026-03-12 08:19:07 -07:00
Kelsi
06c8c26b8a feat: extend colored coin display to item tooltip, quests, AH, guild bank, buyback, taxi 2026-03-12 08:15:46 -07:00
Kelsi
6649f1583a feat: use colored coin display in loot window gold amount 2026-03-12 08:13:03 -07:00
Kelsi
b5567b362f feat: add colored gold/silver/copper text in vendor, trainer, and mail windows 2026-03-12 08:10:17 -07:00
Kelsi
8a9248be79 feat: add spell icons to target and focus frame cast bars 2026-03-12 08:03:43 -07:00
Kelsi
844a0002dc feat: improve combat text with drop shadows and larger crit size
Switch combat float text from ImGui::TextColored to draw list rendering
for drop shadows on all entries (readability over complex backgrounds).
Critical hit/heal events render at 1.35× normal font size for visual
impact, matching the WoW combat feedback convention.
2026-03-12 08:00:27 -07:00
Kelsi
0674dc9ec2 feat: add spell icons to boss and party member cast bars
Consistent with the player cast bar, show the spell icon (12×12 for
boss, 10×10 for party) to the left of each cast bar progress widget.
Falls back gracefully to the icon-less layout when no icon is found.
2026-03-12 07:58:36 -07:00
Kelsi
e41788c63f feat: display current zone name inside minimap top edge
Shows the player's current zone name (from server zone ID via
ZoneManager) as a golden label at the top of the minimap circle.
Gracefully absent when zone ID is 0 (loading screens, undetected zones).
2026-03-12 07:56:59 -07:00
Kelsi
6cd8dc0d9d feat: show spell icon in cast bar alongside progress bar
Display the casting spell's icon (20×20) to the left of the progress
bar using the existing getSpellIcon DBC lookup. Falls back gracefully
to the icon-less layout when no icon is available (e.g. before DBC
load or for unknown spells).
2026-03-12 07:52:47 -07:00
Kelsi
d9ce056e13 feat: add zone name labels and hover coordinates to world map
- Draw zone name text centered in each zone rect on the continent view;
  only rendered when the rect is large enough to fit the label without
  crowding (explored zones get gold text, unexplored get dim grey)
- Show WoW coordinates under the cursor when hovering the map image in
  continent or zone view, bottom-right corner of the map panel
2026-03-12 07:45:51 -07:00
Kelsi
dd8e09c2d9 feat: show hovered world coordinates in minimap tooltip 2026-03-12 07:41:22 -07:00
Kelsi
ddb8f06c3a feat: add class-color and zone tooltip to friends tab in guild roster 2026-03-12 07:39:11 -07:00
Kelsi
d26bac0221 feat: store and display enchant indicators in inspect window gear list 2026-03-12 07:37:29 -07:00
Kelsi
7cb4887011 feat: add threat status bar and pulling-aggro alert to threat window 2026-03-12 07:32:28 -07:00
Kelsi
bab66cfa35 feat: display mail expiry date and urgency warnings in mailbox 2026-03-12 07:28:18 -07:00
Kelsi
344556c639 feat: show class name with class color and zone tooltip in social frame friends list 2026-03-12 07:25:56 -07:00
Kelsi
c25f7b0e52 feat: store and display achievement earn dates in achievement window tooltip 2026-03-12 07:22:36 -07:00
Kelsi
381fc54c89 feat: add hover tooltips to quest kill objective minimap markers 2026-03-12 07:18:11 -07:00
Kelsi
9a08edae09 feat: add Low Health Vignette toggle in Settings > Interface
The persistent red-edge vignette (below 20% HP) now has an on/off
checkbox under Settings > Interface > Screen Effects, alongside the
existing Damage Flash toggle. The preference is persisted to settings.cfg.
2026-03-12 07:15:08 -07:00
Kelsi
8cba8033ba feat: add tooltip to XP bar showing XP to level and rested details
Hovering the XP bar now shows a breakdown: current XP, XP remaining
to the next level, rested bonus amount in XP and as a percentage of
a full level, and whether the player is currently resting.
2026-03-12 07:12:02 -07:00
Kelsi
8858edde05 fix: correct chat bubble Y-coordinate projection
The camera bakes the Vulkan Y-flip into the projection matrix, so no
extra Y-inversion is needed when converting NDC to screen pixels. This
matches the convention used by the nameplate and minimap marker code.
The old formula double-flipped Y, causing chat bubbles to appear at
mirrored positions (e.g. below characters instead of above their heads).
2026-03-12 07:10:45 -07:00
Kelsi
17022b9b40 feat: add persistent low-health vignette when HP below 20%
Screen edges pulse red (at ~1.5 Hz) whenever the player is alive but
below 20% HP, with intensity scaling inversely with remaining health.
Complements the existing on-hit damage flash by providing continuous
danger awareness during sustained low-HP situations.
2026-03-12 07:07:46 -07:00
Kelsi
b8e7fee9e7 feat: add quest kill objective markers on minimap
Live NPCs that match active tracked quest kill objectives are now shown
on the minimap as gold circles with an 'x' mark, making it easier to
spot remaining quest targets at a glance without needing to open the map.
Only shows targets for incomplete objectives in tracked quests.
2026-03-12 07:04:45 -07:00
Kelsi
92361c37df feat: out-of-range indicator on party and raid frames
Party members beyond 40 yards show a gray desaturated health bar
with 'OOR' text instead of HP values. Raid frame cells get a dark
overlay and gray health bar when a member is out of range. Range is
computed from the server-reported posX/posY in SMSG_PARTY_MEMBER_STATS
vs the local player entity position.
2026-03-12 06:58:42 -07:00
Kelsi
d817e4144c feat: debuff dispel-type border coloring in buff bar
Read DispelType from Spell.dbc (new field in all expansion DBC layouts)
and use it to color debuff icon borders: magic=blue, curse=purple,
disease=brown, poison=green, other=red. Buffs remain green-bordered.
Adds getSpellDispelType() to GameHandler for lazy cache lookup.
2026-03-12 06:55:16 -07:00
Kelsi
9a21e19486 feat: highlight chat messages that mention the local player
When a chat message contains the player's character name, the message
is rendered with a golden highlight background and bright yellow text.
A whisper notification sound plays (at most once per new-message scan)
to alert the player. Outgoing whispers and system messages are
excluded from mention detection.
2026-03-12 06:45:27 -07:00
Kelsi
c14b338a92 feat: add Tab autocomplete for slash commands in chat input
Pressing Tab while typing a slash command cycles through all matching
commands (e.g. /em<Tab> → /emote, /emote<Tab> → /emote again).
Unambiguous matches append a trailing space. Repeated Tab presses
cycle forward through all matches. History navigation (Up/Down)
resets the autocomplete session.
2026-03-12 06:38:10 -07:00
Kelsi
68251b647d feat: add spell/quest/achievement hyperlink rendering in chat
Extend chat renderTextWithLinks to handle |Hspell:, |Hquest:, and
|Hachievement: link types in addition to |Hitem:. Spell links show
a small icon and tooltip via renderSpellInfoTooltip; quest links
open the quest log on click; achievement links show a tooltip.
Also wire assetMgr into renderChatWindow for icon lookup.
2026-03-12 06:30:30 -07:00
Kelsi
e8fe53650b feat: add respawn countdown timer to the death dialog
The "You are dead." dialog now shows a "Release in M:SS" countdown
tracking time elapsed since death. The countdown runs from 6 minutes
(WoW's forced-release window) and disappears once it reaches zero.
Timer resets automatically when the player is no longer dead.
2026-03-12 06:14:18 -07:00
Kelsi
39bf8fb01e feat: play audio notification when a whisper is received
Adds UiSoundManager::playWhisperReceived() which uses the dedicated
Whisper_TellMale/Female.wav files (or falls back to iSelectTarget.wav
if absent). The sound is triggered once per new incoming CHAT_MSG_WHISPER
message by scanning new chat history entries in the raid warning overlay
update loop.
2026-03-12 06:12:37 -07:00
Kelsi
bcd984c1c5 feat: sort buff/debuff icons by remaining duration
Both the player buff bar and target frame aura display now sort auras
so that shorter-duration (more urgent) buffs/debuffs appear first.
Permanent auras (no duration) sort to the end. In the target frame,
debuffs are sorted before buffs. In the player buff bar, the existing
buffs-first / debuffs-second pass ordering is preserved, with
ascending duration sort within each group.
2026-03-12 06:08:26 -07:00
Kelsi
3e8f03c7b7 feat: add PROC_TRIGGER floating combat text for spell procs
Handles SMSG_SPELL_CHANCE_PROC_LOG (previously silently ignored) to
display gold "PROC!" floating text when the player triggers a spell
proc. Reads caster/target packed GUIDs and spell ID from the packet
header; skips variable-length effect payload.

Adds CombatTextEntry::PROC_TRIGGER type with gold color rendering,
visible alongside existing damage/heal/energize floating numbers.
2026-03-12 06:06:41 -07:00
Kelsi
10ad246e29 feat: grey out action bar item slots when item is not in inventory
Item slots on the action bar now display a dark grey tint when the
item is no longer in the player's backpack, bags, or equipment slots.
This mirrors WoW's visual feedback for consumed or missing items,
matching the priority chain: cooldown > GCD > out-of-range >
insufficient-power > item-missing.
2026-03-12 06:03:04 -07:00
Kelsi
39634f442b feat: add insufficient-power tint to action bar spell slots
Spell icons now render with a purple desaturated tint when the player
lacks enough mana/rage/energy/runic power to cast them. Power cost and
type are read from Spell.dbc via the spellbook's DBC cache. The spell
tooltip also shows "Not enough power" in purple when applicable.

Priority: cooldown > GCD > out-of-range > insufficient-power so states
don't conflict. Adds SpellbookScreen::getSpellPowerInfo() as a public
DBC accessor.
2026-03-12 06:01:42 -07:00
Kelsi
8081a43d85 feat: add out-of-range tint to action bar spell slots
Ranged spell icons dim to a red tint when the current target is farther
than the spell's max range (read from SpellRange.dbc via spellbook data).
Melee/self spells (max range ≤ 5 yd or unknown) are excluded. The
spell tooltip also shows "Out of range" in red when applicable.

Adds SpellbookScreen::getSpellMaxRange() as a public accessor so
game_screen can query DBC range data without duplicating DBC loading.
2026-03-12 05:57:45 -07:00
Kelsi
bc5a7867a9 feat: zone entry toast and unspent talent points indicator
- Zone entry toast: centered slide-down banner when entering a new
  zone (tracks renderer's zone name, fires on change)
- Talent indicator: pulsing green '! N Talent Points Available' below
  minimap alongside existing New Mail / BG queue indicators
2026-03-12 05:44:25 -07:00
Kelsi
b6a43d6ce7 feat: track and visualize global cooldown (GCD) on action bar
- GameHandler tracks GCD in gcdTotal_/gcdStartedAt_ (time-based)
- SMSG_SPELL_COOLDOWN: spellId=0 entries (<=2s) are treated as GCD
- castSpell(): optimistically starts 1.5s GCD client-side on cast
- Action bar: non-cooldown slots show subtle dark sweep + dim tint
  during the GCD window, matching WoW standard behavior
2026-03-12 05:38:13 -07:00
Kelsi
61c0b91e39 feat: show weather icon next to zone name above minimap
Appends a color-coded Unicode weather symbol to the zone name:
- Rain (type 1): blue ⛆ when intensity > 5%
- Snow (type 2): ice-blue ❄ when intensity > 5%
- Storm/Fog (type 3): gray ☁ when intensity > 5%
Symbol is hidden when weather is clear or absent.
2026-03-12 05:34:56 -07:00
Kelsi
8fd9b6afc9 feat: add pulsing combat status indicators to player and target frames
- Player frame shows pulsing red [Combat] badge next to level when in combat
- Target frame shows pulsing [Attacking] badge when engaged with target
- Both pulse at 4Hz and include hover tooltips for clarity
2026-03-12 05:28:47 -07:00
Kelsi
162fd790ef feat: add right-click context menu to minimap
Adds a WoW-style popup on right-click within the minimap circle with:
- Zoom In / Zoom Out controls
- Rotate with Camera toggle (with checkmark state)
- Square Shape toggle (with checkmark state)
- Show NPC Dots toggle (with checkmark state)
2026-03-12 05:25:46 -07:00
Kelsi
f5d67c3c7f feat: add Shift+hover item comparison in vendor window
Extend renderItemTooltip(ItemQueryResponseData) to accept an optional
Inventory* parameter. When Shift is held and an equipped item in the
same slot exists, show: equipped item name, item level diff (▲/▼/=),
and stat diffs for Armor/Str/Agi/Sta/Int/Spi. Pass the player's
inventory from the vendor window hover handler to enable this.
2026-03-12 05:20:44 -07:00
Kelsi
5827a8fcdd feat: add Shaman totem bar in player frame
Store active totem state (slot, spellId, duration, placedAt) from
SMSG_TOTEM_CREATED. Render 4 element slots (Earth/Fire/Water/Air) as
color-coded duration bars in the player frame for Shamans (class 7).
Shows countdown seconds, element letter when inactive, and tooltip
with spell name + remaining time on hover.
2026-03-12 05:16:43 -07:00
Kelsi
8efdaed7e4 feat: add gold glow when action bar spell comes off cooldown
When a spell's cooldown expires, its action bar slot briefly animates
with a pulsing gold border (4 pulses over 1.5 seconds, fading out) to
draw attention that the ability is ready again. Uses per-slot state
tracking with static maps inside the render lambda.
2026-03-12 05:12:58 -07:00
Kelsi
c35bf8d953 feat: add duel countdown overlay (3-2-1-Fight!)
Parse SMSG_DUEL_COUNTDOWN to get the countdown duration, track the
start time, and render a large centered countdown overlay. Numbers
display in pulsing gold; transitions to pulsing red 'Fight!' for the
last 0.5 seconds. Countdown clears on SMSG_DUEL_COMPLETE.
2026-03-12 05:06:14 -07:00
Kelsi
29a989e1f4 feat: add reputation bar above XP bar
Show a color-coded reputation progress bar for the most recently gained
faction above the XP bar. The bar is auto-shown when any faction rep
changes (watchedFactionId_ tracks the last changed faction). Colors
follow WoW conventions: red=Hated/Hostile, orange=Unfriendly,
yellow=Neutral, green=Friendly, blue=Honored, purple=Revered,
gold=Exalted. Tooltip shows exact standing values on hover.
2026-03-12 05:03:03 -07:00
Kelsi
0a03bf9028 feat: add cast bar to pet frame
Show an orange cast bar in the pet frame when the pet is casting a
spell, matching the party frame cast bar pattern. Displays spell name
and time remaining; falls back to 'Casting...' when spell name is
unavailable from Spell.dbc.
2026-03-12 04:59:24 -07:00
Kelsi
b682e8c686 feat: add countdown timer to loot roll popup
Show a color-coded progress bar (green→yellow→pulsing red) in the loot
roll window indicating time remaining to make a roll decision. The
countdown duration is read from SMSG_LOOT_START_ROLL (or defaults to
60s for the SMSG_LOOT_ROLL path). Remaining seconds are displayed on
the bar itself.
2026-03-12 04:57:36 -07:00
Kelsi
b34bf39746 feat: add quest completion toast notification
When a quest is turned in (SMSG_QUESTGIVER_QUEST_COMPLETE), a gold-bordered
toast slides in from the right showing "Quest Complete" header with the quest
title, consistent with the rep change and achievement toast systems.
2026-03-12 04:53:03 -07:00
Kelsi
71df1ccf6f feat: apply WoW class colors to guild roster class column
Online guild members now show their class name in the standard WoW
class color in the guild roster table, matching the familiar in-game
appearance. Offline members retain the dimmed gray style.
2026-03-12 04:46:25 -07:00
Kelsi
102b34db2f feat: apply buff expiry color warning to target aura timers
Target frame debuff/buff timers now use the same urgency colors as
the player buff bar: white >30s, orange <30s, pulsing red <10s.
2026-03-12 04:44:48 -07:00
Kelsi
fb6e7c7b57 feat: color-code quest tracker objectives green when complete
Completed kill/item objectives now display in green instead of gray,
giving an immediate visual cue about which objectives are done vs.
still in progress on the on-screen quest tracker.
2026-03-12 04:42:48 -07:00
Kelsi
271518ee08 feat: use WoW standard class colors for player name in player frame
Player name in the unit frame now shows in the official WoW class
color (warrior=tan, paladin=pink, hunter=green, rogue=yellow,
priest=white, DK=red, shaman=blue, mage=cyan, warlock=purple,
druid=orange) matching the familiar in-game appearance.
2026-03-12 04:39:38 -07:00
Kelsi
f04a5c8f3e feat: add buff expiry color warning on timer overlay
Buff/debuff countdown timers now change color as expiry approaches:
white (>30s) → orange (<30s) → pulsing red (<10s). This gives players
a clear visual cue to reapply important buffs before they fall off.
2026-03-12 04:34:00 -07:00
Kelsi
1b3dc52563 feat: improve party frame dead/offline member display
Dead party members now show a gray "Dead" progress bar instead of
"0/2000" health values, and offline members show a dimmed "Offline"
bar. The power bar is suppressed for both states to reduce clutter.
2026-03-12 04:31:01 -07:00
Kelsi
7475a4fff3 feat: add persistent coordinate display below minimap
Always-visible player coordinates (X, Y in canonical WoW space) rendered
as warm-yellow text on a semi-transparent pill just below the minimap
circle, eliminating the need to hover for position info.
2026-03-12 04:27:26 -07:00
Kelsi
2e504232ec feat: add item icons and full tooltips to inspect window gear list 2026-03-12 04:24:37 -07:00
Kelsi
10e9e94a73 feat: add interrupt pulse to nameplate cast bars for hostile casters 2026-03-12 04:21:33 -07:00
Kelsi
b8141262d2 feat: add low mana pulse and interrupt alert on cast bars
- Mana bar pulses dim blue when below 20% (matches health bar low-hp pulse)
- Target, focus, and boss cast bars pulse orange when cast is > 80% complete,
  signalling the closing interrupt window across all frame types
2026-03-12 04:18:39 -07:00
Kelsi
3014c79c1f feat: show item stack count on action bar slots
Consumable items (potions, food, etc.) on the action bar now show their
remaining stack count in the bottom-right corner of the icon. Shows red
when count is 1 (last one), white otherwise. Counts across all bag slots.
2026-03-12 04:12:07 -07:00
Kelsi
5adb5e0e9f feat: add health bar color transitions for player and pet frames
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 health bar now transitions green→orange→pulsing red as HP drops
(>50%=green, 20-50%=orange, <20%=pulsing red). Pet frame gets the same
3-tier color scheme. Target and party frames already had color coding.
2026-03-12 04:06:46 -07:00
Kelsi
d14982d125 feat: add DPS/HPS meter showing real-time damage and healing output
Floating window right of the cast bar showing player's DPS and healing
per second, derived from combat text entries. Uses actual combat duration
as denominator for accurate readings at fight start. Toggle in Settings
> Network. Saves to settings.cfg.
2026-03-12 04:04:27 -07:00
Kelsi
797bb5d964 feat: add center-screen raid warning and boss emote overlay
RAID_WARNING messages show as flashing red/yellow large text.
RAID_BOSS_EMOTE and MONSTER_EMOTE show as amber text.
Each message fades in quickly, holds for 5 seconds, then fades out.
Up to 3 messages stack vertically below the target frame area.
Dark semi-transparent background box improves readability.
Messages are detected from new chat history entries each frame.
2026-03-12 03:52:54 -07:00
Kelsi
66ec35b106 feat: show decimal precision for short action bar cooldowns
Display "1.5" instead of "1s" for cooldowns under 5 seconds,
matching WoW's default cooldown text behaviour for GCDs and
short ability cooldowns where sub-second timing matters.
2026-03-12 03:48:12 -07:00
Kelsi
6068d0d68d feat: add HP% text and cast bars to nameplates
- Show health percentage centered on each nameplate health bar
- Show purple cast bar below health bar when a unit is actively casting
- Display spell name (from Spell.dbc cache) above cast bar
- Show time remaining (e.g. "1.4s") centered on cast bar fill
- All elements respect the existing nameplate alpha fade-out at distance
2026-03-12 03:44:32 -07:00
Kelsi
fa947eb9c7 Add quest objective tracker overlay on right side of screen
Shows tracked quests (or first 5 active quests if none tracked) below the
minimap with live kill/item objective counts and completion status.
2026-03-12 03:39:10 -07:00
Kelsi
63c8e82913 Move latency meter to top-center of screen
Relocate the ms indicator from below the minimap to a small centered
overlay at the top of the screen, with a semi-transparent background
for better readability during gameplay.
2026-03-12 03:31:09 -07:00
Kelsi
d70db7fa0b Reduce damage flash vignette opacity for subtler combat feedback
Peak alpha reduced from 180 to 100 (71% → 39%) so the red edge flash is
noticeable but less intrusive during combat.
2026-03-12 03:24:25 -07:00
Kelsi
6cf511aa7f Add damage flash toggle setting and fix map explored zone reveal
Persist damage_flash to settings.cfg; checkbox in Interface > Screen Effects.
Fix world map fog: trust server exploration mask unconditionally when present,
always reveal the current zone immediately regardless of server mask state.
2026-03-12 03:21:49 -07:00
Kelsi
40a98f2436 Fix talent tab crash and missing talent points at level 10
Unique child window and button IDs per tab prevent ImGui state corruption
when switching talent tabs. Parse SMSG_INSPECT_TALENT type=0 properly to
populate unspentTalentPoints_ and learnedTalents_ from the server response.
2026-03-12 03:15:56 -07:00
Kelsi
6ab9ba65f9 Store and display played time on character Stats tab
Save totalTimePlayed/levelTimePlayed from SMSG_PLAYED_TIME. Request a
fresh update whenever the character screen is opened. Show total and
level-played time in a two-column layout below the stats panel.
2026-03-12 03:09:52 -07:00
Kelsi
46eb66b77f Store and display achievement criteria progress from SMSG_CRITERIA_UPDATE
Track criteria progress (criteriaId → counter) from SMSG_CRITERIA_UPDATE
and SMSG_ALL_ACHIEVEMENT_DATA. Add a Criteria tab to the achievement window
showing live progress values alongside the existing Earned achievements tab.
2026-03-12 03:03:02 -07:00
Kelsi
920950dfbd Add threat list window showing live aggro data for current target
Store SMSG_THREAT_UPDATE/SMSG_HIGHEST_THREAT_UPDATE in a per-unit map
(sorted descending by threat) and clear on SMSG_THREAT_REMOVE/CLEAR.
Show a threat window (/threat or via target frame button) with a progress
bar per player and gold highlight for the tank, red if local player has aggro.
2026-03-12 02:59:09 -07:00
Kelsi
43de2be1f2 Add inspect window showing talent summary and gear for inspected players
Store inspect results (talent points, dual-spec state, gear entries) in a
new InspectResult struct instead of discarding them as chat messages.
Open the inspect window automatically from all Inspect menu items and /inspect.
2026-03-12 02:52:40 -07:00
Kelsi
92db25038c Parse SMSG_ARENA_TEAM_STATS and display in character screen PvP tab 2026-03-12 02:35:29 -07:00
Kelsi
2bdd024f19 Add GM Ticket window (/ticket, /gm commands and Esc menu button) 2026-03-12 02:31:12 -07:00
Kelsi
3964a33c55 Add /help slash command listing all available commands 2026-03-12 02:23:24 -07:00
Kelsi
adf8e6414e Show boot vote progress in LFG UI; fix unused screenH warning 2026-03-12 02:17:49 -07:00
Kelsi
f4754797bc Add Achievements list window (Y key toggle) with search filter 2026-03-12 02:09:35 -07:00
Kelsi
7a1f330655 Add minimap coordinate tooltip and play time warning display
Hovering over the minimap now shows a tooltip with the player's
WoW canonical coordinates (X=North, Y=West) and a hint about
Ctrl+click pinging.

SMSG_PLAY_TIME_WARNING is now parsed (type + minutes) and shown
as both a chat message and a UIError overlay rather than silently
dropped.
2026-03-12 01:57:03 -07:00
Kelsi
1bc3e6b677 Add Channels tab to social frame and reputation change toast
Social frame now has three tabs: Friends, Ignore, and Channels. The
Channels tab lists joined channels with right-click Leave and an input
to join new channels.

Also adds a slide-in reputation change toast in the lower-right corner:
shows faction name, delta (+/-), and current standing tier (Honored,
Revered, etc.) whenever SMSG_SET_FACTION_STANDING fires a rep change.
2026-03-12 01:51:18 -07:00
Kelsi
fb6630a7ae Add Ignore tab to social frame with view/unignore/add support
Social frame now has Friends and Ignore tabs. Friends tab shows online
players first, then offline with a separator, and supports right-click
Whisper/Invite/Remove. Ignore tab lists all ignored players from
ignoreCache with right-click Unignore and an inline add-ignore field.
2026-03-12 01:31:44 -07:00
Kelsi
06456faa63 Extend UIErrorsFrame to spell failures, interrupts, server shutdown warnings 2026-03-12 01:22:42 -07:00
Kelsi
25e2c60603 Add UIErrorsFrame: center-bottom spell error overlay with fade-out 2026-03-12 01:15:11 -07:00
Kelsi
955b22841e Wire SMSG_FORCE_ANIM animId to emoteAnimCallback 2026-03-12 01:04:16 -07:00
Kelsi
eb9ca8e227 Fix item cooldowns not showing on action bar item-type slots
SMSG_ITEM_COOLDOWN now resolves itemId via onlineItems_ and applies cooldown
to both SPELL-type and ITEM-type action bar slots. Classic SMSG_SPELL_COOLDOWN
also uses the embedded itemId field to update ITEM-type slots.
2026-03-12 00:59:25 -07:00
Kelsi
9e5f7c481e Wire achievement toast and ding effect callbacks
Level-up now calls triggerDing() (sound + emote + fade text) in addition to
the screen flash. Achievement earned now calls triggerAchievementToast() via
setAchievementEarnedCallback(), making the existing toast animation visible.
2026-03-12 00:56:31 -07:00
Kelsi
d8f2fedae1 Implement renderSocialFrame: compact friends panel with minimap toggle button
Shows online/AFK/DND/offline status dots, whisper/invite/remove context menus,
and inline add-friend field. Minimap gets a smiley-face button (top-left) with
a green dot badge when friends are online, toggling the panel.
2026-03-12 00:53:57 -07:00
Kelsi
c89dc50b6c Distinguish channeled spells in cast bar with blue color and draining animation
Adds castIsChannel flag set on MSG_CHANNEL_START, cleared on all cast resets.
Cast bar now drains right-to-left in blue for channels vs gold fill for casts.
2026-03-12 00:43:29 -07:00
Kelsi
c13e18cb55 Add Set Raid Mark submenu to target, party, and raid frame context menus
Implements setRaidMark() using the existing RaidTargetUpdatePacket and exposes
it via right-click on target frame, party member frames, and raid cell frames.
2026-03-12 00:39:56 -07:00
Kelsi
c0f19f5883 Add missing context menu items and nameplate right-click menus
- Focus frame: add Add Friend / Ignore items for player targets
- Guild roster: add Add Friend / Ignore items to member context menu
- Nameplates: right-click shows Target/Set Focus/Whisper/Invite/Friend/Ignore popup
2026-03-12 00:26:47 -07:00
Kelsi
d0f2916885 Add right-click context menus to bag bar slots and backpack 2026-03-12 00:21:25 -07:00
Kelsi
778363bfaf Add Add Friend/Ignore to party member context menu 2026-03-12 00:19:10 -07:00
Kelsi
e13993de9b Add 'Add to Action Bar' option to spellbook right-click context menu 2026-03-12 00:16:35 -07:00
Kelsi
928f00de41 Add Show Helm/Cloak checkboxes to Equipment tab; expose isHelmVisible/isCloakVisible 2026-03-12 00:13:48 -07:00
Kelsi
d072c852f3 Add AFK/DND toggles to player frame menu and right-click context to pet frame 2026-03-12 00:10:54 -07:00
Kelsi
5fdcb5df81 Wire up TOGGLE_QUEST_LOG keybinding (Q key) to open quest log screen 2026-03-12 00:05:55 -07:00
Kelsi
1cab2e1156 Add Abandon Quest option to quest tracker right-click menu 2026-03-12 00:04:11 -07:00
Kelsi
347e958703 Improve player frame context menu: name header, Leave Group when in group 2026-03-12 00:03:23 -07:00
Kelsi
c170216e1c Add Invite to Group to friends list right-click menu for online friends 2026-03-12 00:02:20 -07:00
Kelsi
00a66b7114 Add right-click context menu to action bar slots with Cast/Use and Clear Slot 2026-03-11 23:59:51 -07:00
Kelsi
9705904052 Add Follow option to target frame and party member context menus 2026-03-11 23:58:37 -07:00
Kelsi
7943edf252 Add Duel and Inspect to target, focus, and party member context menus 2026-03-11 23:56:57 -07:00
Kelsi
716c0c0e4c Add right-click context menu to quest log list entries 2026-03-11 23:54:19 -07:00
Kelsi
109b0a984a Add right-click context menu to focus frame
Shows Target, Clear Focus, and player-only actions (Whisper, Invite, Trade) when right-clicking the focus name.
2026-03-11 23:51:27 -07:00
Kelsi
d3221ff253 Add right-click context menu to target-of-target frame
Right-clicking the ToT name shows Target and Set Focus options; clicking the name still targets the unit.
2026-03-11 23:50:41 -07:00
Kelsi
2cd4672912 Add Invite to Group to chat message right-click menu
Allows inviting players directly from chat messages, consistent with the target frame and party frame context menus.
2026-03-11 23:49:37 -07:00
Kelsi
72e07fbe3f Add Whisper and Invite to Group to guild member context menu
Social actions appear at the top of the right-click menu when the member is online, matching WoW's guild roster behavior.
2026-03-11 23:48:07 -07:00
Kelsi
8eb451aab5 Add right-click context menu to spellbook spell rows
Allows casting or copying a spell link from the context menu, mirroring WoW's standard spellbook right-click behavior.
2026-03-11 23:46:47 -07:00
Kelsi
9b8bc2e977 Add right-click context menu to quest objective tracker
Right-clicking a quest title in the HUD tracker shows options to open
it in the Quest Log or toggle tracking (track/stop tracking).
2026-03-11 23:42:28 -07:00
Kelsi
54750d4656 Add right-click context menu to target frame name
Right-clicking the target's name now shows: Set Focus, Clear Target,
and for player targets: Whisper, Invite to Group, Trade, Add Friend, Ignore.
2026-03-11 23:41:05 -07:00
Kelsi
08bdd9eb36 Add low durability warning indicator below minimap
Shows 'Low durability' in orange when any equipped item falls below 20%
durability, and a pulsing red 'Item breaking!' warning below 5%. Uses
the same stacked indicator slot system as the latency and mail notices.
2026-03-11 23:35:51 -07:00
Kelsi
971ada6397 Add Shift+right-click destroy for inventory items with confirmation popup
Holding Shift while right-clicking any non-quest inventory item opens a
destroy confirmation popup instead of performing the normal equip/use
action. Item tooltips now show a 'Shift+RClick to destroy' hint at the
bottom (highlighted in red when Shift is held).
2026-03-11 23:32:43 -07:00
Kelsi
88436fa400 Add jump-to-bottom indicator in chat when scrolled up
Shows a "New messages" button below the chat history area when the
user has scrolled away from the latest messages. Clicking it jumps
back to the most recent messages.
2026-03-11 23:24:27 -07:00
Kelsi
c433188edb Show AFK/DND status badge on player frame
Adds a yellow <AFK> or orange <DND> label next to the player name
when those modes are active, with a tooltip explaining how to cancel.
2026-03-11 23:21:27 -07:00
Kelsi
fe61d6acce Add corpse direction indicator on minimap for ghost players
When the player is a ghost, shows a small X marker at the corpse
position on the minimap. If the corpse is off-screen, draws an edge
arrow on the minimap border pointing toward it.
2026-03-11 23:19:48 -07:00
Kelsi
b3d3814ce9 Add search bar and Active/Ready filter to quest log
Adds a name search input and All/Active/Ready radio buttons above the
quest list. Clears the filter automatically when openAndSelectQuest() is
called so the target quest is always visible.
2026-03-11 23:17:38 -07:00
Kelsi
3446fffe86 Add local time clock below minimap indicators 2026-03-11 23:13:31 -07:00
Kelsi
7e271df032 Add level-up golden burst overlay effect
When the player gains a level, a golden vignette flashes on screen edges
and a large "Level X!" text briefly appears in the center, both fading
over ~1 second. Uses the existing LevelUpCallback from GameHandler.
2026-03-11 23:10:21 -07:00
Kelsi
745768511b Show all buyback items in vendor window (not just the most recent) 2026-03-11 23:08:35 -07:00
Kelsi
682cb8d44b Add chat input history navigation with Up/Down arrows
Up arrow in the chat input field recalls previously sent messages.
Down arrow moves forward through history; going past the end clears input.
History stores up to 50 unique entries and resets position after each send.
2026-03-11 23:06:24 -07:00
Kelsi
e002266607 Add scroll wheel zoom to minimap 2026-03-11 23:01:37 -07:00
Kelsi
ae8f900410 Add Ctrl+click minimap ping sending
Ctrl+clicking on the minimap converts screen position to world coordinates
and sends MSG_MINIMAP_PING to the server. A local ping is also added
immediately so the sender sees their own ping.
2026-03-11 23:00:03 -07:00
Kelsi
97662800d5 Add /target command and screen damage flash vignette
/target <name> searches visible entities by case-insensitive prefix match.
Red vignette flashes on screen edges when player HP drops, fading over 0.5s.
2026-03-11 22:57:04 -07:00
Kelsi
6e7a32ec7f Add nameplate scale setting to Interface settings tab
Adds a 0.5x-2.0x scale slider under Nameplates in the Interface settings tab. The scale multiplies the base 80×8px nameplate health bar dimensions. Setting is persisted to settings.cfg as 'nameplate_scale'.
2026-03-11 22:49:54 -07:00
Kelsi
d31c483944 Add player position arrow to minimap
Draws a yellow directional arrow at the minimap center showing the player's facing direction, with a white dot at the player's position. The arrow rotates with the camera in rotate-with-camera mode and always points in the player's current facing direction.
2026-03-11 22:47:17 -07:00
Kelsi
823e2bcec6 Add Sell All Junk button to vendor window
Scans the backpack and all bags for grey (POOR quality) items with a sell price and shows a 'Sell All Junk (N items)' button at the top of the vendor window. Clicking it sells all matching items in one action. Button only appears when there are sellable junk items.
2026-03-11 22:44:23 -07:00
Kelsi
b2d1edc9db Show corpse distance on reclaim corpse button
When the player is a ghost, the 'Resurrect from Corpse' popup now shows how many yards away the corpse is, updating in real-time as the ghost moves. Distance is only shown when the corpse is on the same map.
2026-03-11 22:41:26 -07:00
Kelsi
355001c236 Add action bar scale setting to Interface settings tab
Adds a 0.5x-1.5x scale slider under Action Bars in the Interface settings tab. The scale multiplies the base 48px slot size for both the main bar and XP bar layout calculations. The setting is persisted to settings.cfg as 'action_bar_scale'.
2026-03-11 22:39:59 -07:00
Kelsi
69fd0b03a2 Add right-click context menu to player unit frame
Right-clicking the player name in the unit frame opens a popup with 'Open Character' (opens the character/equipment screen) and 'Toggle PvP' options, consistent with the existing right-click menus on party and raid frames.
2026-03-11 22:36:58 -07:00
Kelsi
18e42c22d4 Add HP percentage text to raid frame health bars
Each raid member cell now shows the health percentage centered on the health bar, with a drop shadow for readability. The text is omitted when no health data is available.
2026-03-11 22:31:55 -07:00
Kelsi
c9c20ce433 Display quest rewards (money and items) in quest log details pane
Parses reward money, guaranteed items, and choice items from SMSG_QUEST_QUERY_RESPONSE fixed header for both Classic/TBC (40-field) and WotLK (55-field) layouts. Rewards are shown in the quest details pane below objective progress with icons, names, and counts.
2026-03-11 22:30:16 -07:00
Kelsi
1693abffd3 Improve cooldown text format and show HP values on party health bars
Cooldowns now display as "Xs" (seconds), "XmYs" (minutes+seconds), or "Xh" (hours) instead of the previous bare number or "Xm"-only format. Party member health bars now show "current/max" HP text (abbreviated to "Xk/Yk" for large values) directly on the progress bar.
2026-03-11 22:25:15 -07:00
Kelsi
b44857dad9 Add Train All Available button to trainer window
Shows a footer button listing the count and total cost of all currently trainable spells. Clicking it sends a train request for each spell that meets level, prerequisite, and gold requirements. Button is disabled when nothing is trainable or the player cannot afford the full batch.
2026-03-11 22:23:26 -07:00
Kelsi
9a199e20b6 Add Loot All button to loot window 2026-03-11 22:16:19 -07:00
Kelsi
2f1c9eb01b Add FOV slider to settings, expand combat chat tab with skills/achievements/NPC speech 2026-03-11 22:13:22 -07:00
Kelsi
861bb3404f Improve vendor window: quantity selector, stock status colors, disable out-of-stock items 2026-03-11 22:10:43 -07:00
Kelsi
c9cfa864bf Add Achievements tab to character screen with search filter 2026-03-11 22:07:44 -07:00
Kelsi
c9ea61aba7 Fix Exalted reputation tier not displaying correctly (off-by-one in getTier loop) 2026-03-11 22:06:16 -07:00
Kelsi
a207ceef6c Add secondary stats (AP, SP, hit, crit, haste, etc.) to character stats panel 2026-03-11 22:03:33 -07:00
Kelsi
386de826af Add right-click context menu on chat messages for whisper/friend/ignore 2026-03-11 22:00:30 -07:00
Kelsi
f5de4d2031 Add shift-click spell linking to chat from spellbook 2026-03-11 21:57:13 -07:00
Kelsi
25c5d257ae Enhance Friends tab with add/remove/note/whisper and add Ignore List tab 2026-03-11 21:53:15 -07:00
Kelsi
c09ebae5af Add shift-click item linking to bank and guild bank windows 2026-03-11 21:50:07 -07:00
Kelsi
fc7cc44ef7 Add right-click context menu to raid frame cells with leader kick support 2026-03-11 21:47:10 -07:00
Kelsi
300e3ba71f Show quest status icons in gossip window based on QuestGiverStatus
Quest list in NPC gossip now shows colored status icons:
! (yellow) = available, ? (yellow) = ready to turn in,
! or ? (gray) = available low-level or in-progress incomplete.
2026-03-11 21:39:32 -07:00
Kelsi
54eae9bffc Add shift-click links and Take All to mail attachments
Mail attachment icons and names now support shift-click to insert item
links. Item names also show rich tooltips on hover. Adds a "Take All"
button when a mail has multiple attachments.
2026-03-11 21:37:15 -07:00
Kelsi
23cfb9b640 Add shift-click chat links to AH bids and owner auctions tabs 2026-03-11 21:34:28 -07:00
Kelsi
99d1f5778c Fix trade window peer tooltips; add shift-click links in trade and loot roll
Trade window now shows rich item tooltips for both sides (peer items were
missing tooltips). Both trade sides and the loot roll popup now support
shift-click to insert item links into the chat input.
2026-03-11 21:32:54 -07:00
Kelsi
7cfeed1e28 Shift-click items in quest/AH windows to insert chat links
Adds shift-click-to-link support in auction house browse results, quest
details reward items, quest offer/reward window choice and fixed items,
and quest request-items required item list.
2026-03-11 21:31:09 -07:00
Kelsi
3d40e4dee5 Shift-click items in loot and vendor windows to insert chat links 2026-03-11 21:27:16 -07:00
Kelsi
0075fdd5e1 Show item icons in quest objective tracker 2026-03-11 21:24:03 -07:00
Kelsi
43c0e9b2e8 Add spell search filter to trainer window 2026-03-11 21:21:14 -07:00
Kelsi
bbf4806fe8 Add item search filter to vendor window 2026-03-11 21:19:47 -07:00
Kelsi
7ab0b036c7 Add rich item tooltip to buyback item row in vendor window 2026-03-11 21:17:29 -07:00
Kelsi
95ac97a41c Use rich item tooltips in bank window slots 2026-03-11 21:15:41 -07:00
Kelsi
e34357a0a4 Use rich item tooltips in mail attachments and guild bank slots 2026-03-11 21:14:27 -07:00
Kelsi
4394f93a17 Use rich item tooltips in quest details window; fix shift-click chat link ordering 2026-03-11 21:11:58 -07:00
Kelsi
43c239ee2f Shift-click bag items to insert item links into chat input 2026-03-11 21:09:42 -07:00
Kelsi
e415451f89 Show item icons inline in chat item links 2026-03-11 21:03:51 -07:00
Kelsi
5bafacc372 Use full item tooltips in all auction house tabs 2026-03-11 21:02:02 -07:00
Kelsi
458c9ebe8c Show item icons for item objectives in quest log 2026-03-11 20:57:39 -07:00
Kelsi
3c0e58bff4 Show item icon and quality color in buyback table 2026-03-11 20:52:42 -07:00
Kelsi
647967cccb Show item icons in vendor window item list 2026-03-11 20:49:25 -07:00
Kelsi
764cf86e38 Show spell icons in trainer window with dimming for unavailable spells 2026-03-11 20:48:03 -07:00
Kelsi
d9a58115f9 Show item icons and rich tooltips in trade window slots 2026-03-11 20:42:26 -07:00
Kelsi
4ceb313fb2 Show item icons in quest turn-in required items list 2026-03-11 20:41:02 -07:00
Kelsi
1e8c85d850 Show item icons in mail read-view attachment list 2026-03-11 20:39:15 -07:00
Kelsi
26fab2d5d0 Show item icons in guild bank window
Replace text-only buttons with icon+draw-list rendering that matches
the style of the regular bank, loot, and vendor windows. Item icons are
looked up via inventoryScreen.getItemIcon(info->displayInfoId); falls
back to a coloured bordered square with two-letter abbreviation when
the texture is not yet cached. Stack count is overlaid in the
bottom-right corner. Withdraw still fires on left-click.
2026-03-11 20:33:46 -07:00
Kelsi
2e92ec903c Fix SMSG_ITEM_COOLDOWN missing cooldownTotal for sweep animation
SMSG_ITEM_COOLDOWN (on-use trinket/item cooldowns) was only setting
cooldownRemaining, leaving cooldownTotal=0. The action bar clock-sweep
overlay requires both fields; without cooldownTotal the fan shrinks
instantly rather than showing the correct elapsed arc.
2026-03-11 20:21:37 -07:00
Kelsi
8c2f69ca0e Rate-limit icon GPU uploads in spellbook, action bar, and inventory screens
Opening the spellbook on a new tab, logging in with many auras/action slots, or
opening a full bag all triggered synchronous BLP-decode + GPU uploads for every
uncached icon in one frame, causing a visible stall. Apply the same 4-per-frame
upload cap that was added to talent_screen, so icons load progressively.
2026-03-11 20:17:41 -07:00
Kelsi
6dd7213083 Fix zombie renderer instances on same-map SMSG_NEW_WORLD teleports
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
When SMSG_NEW_WORLD fires with the same map ID (dungeon wing teleporters,
GM teleports, etc.), entityManager.clear() was called but renderer
instances in creatureInstances_/playerInstances_/gameObjectInstances_
were never despawned.  Fresh CREATE_OBJECTs from the server hit the
early-return guard (guid already in creatureInstances_) and were skipped,
leaving entities in the entity manager without matching renderer state.

Fix: pass isSameMap as isInitialEntry to the world-entry callback.  This
routes same-map SMSG_NEW_WORLD through the reconnect path which properly
despawns all renderer instances before the server resends CREATE_OBJECTs.
2026-03-11 19:59:42 -07:00
Kelsi
7dbf950323 Fix talent tab hang when switching trees by rate-limiting icon uploads
Switching from Arms to Fury (or any previously-unseen tab) caused a
multi-frame stall because getSpellIcon() loaded and uploaded all ~20
BLP textures synchronously in a single frame.  Limit new icon GPU
uploads to 4 per frame; uncached icons return null and are loaded on
subsequent frames, spreading the cost over ~5 frames with no visible
hang.
2026-03-11 19:48:00 -07:00
Kelsi
711cb966ef Fix chest interaction and measure server RTT for latency meter
Chests (and lockboxes, coffers, etc.) failed to open because CMSG_LOOT
was only sent on Classic/Turtle expansions, and only when GO type was
already cached as type 3.  Fix: always send CMSG_LOOT after
CMSG_GAMEOBJ_USE (server silently ignores it for non-lootable objects).
Also broaden CMSG_GAMEOBJ_REPORT_USE to all non-mailbox WotLK GOs.

Latency meter: record pingTimestamp_ in sendPing() and compute RTT in
handlePong(); add toggleable "Show Latency Meter" checkbox in Interface
settings (saved to settings.cfg).
2026-03-11 19:45:03 -07:00
Kelsi
14f672ab6a Pre-query vendor item information to avoid placeholder display
Proactively call ensureItemInfo() for both vendor items and buyback items
during rendering. This ensures item names and stats are available before
display, eliminating "Item <id>" placeholders and providing instant tooltip
info, matching the pattern used for quest rewards and action bar items.
2026-03-11 19:29:10 -07:00
Kelsi
b5291d1883 Revert quest reward window delay that caused dialog to hang
The delayed-opening logic conflicted with quest details' use of the same
questDetailsOpenTime variable, causing the reward dialog to never appear.
Reverted to immediately opening the window. Item info queries are still
triggered, but will populate asynchronously with placeholders shown initially.
2026-03-11 19:11:02 -07:00
Kelsi
510370dc7b Delay quest reward window opening to load item icons/names
Add 100ms delay before opening the quest offer reward dialog, giving item
info queries time to complete. Prevents "Item X" placeholders where players
can't see item names or icons needed to choose rewards. Reuses the existing
questDetailsOpenTime mechanism with delayed flag check in isQuestOfferRewardOpen().
2026-03-11 18:55:13 -07:00
Kelsi
9b9d56543c Re-query player names during rendering to resolve pending queries
When a player nameplate is about to render with an empty name (showing
"Player (level)" placeholder), actively re-request the name query. Since
queryPlayerName() is idempotent (won't duplicate pending queries), this
ensures that slow network responses don't cause players to permanently
display as "Player (67)" even after the response arrives. Rendering code
now triggers name queries to completion before falling back to placeholders.
2026-03-11 18:53:23 -07:00
Kelsi
182b6686ac Pre-query action bar item information to avoid placeholder display
When items are placed on the action bar, pre-fetch their ItemDef information
so the action bar displays the item name instead of a generic "Item" placeholder.
This ensures item names are available when the action bar is rendered, consistent
with the fix applied to quest reward items display.

Calls queryItemInfo() when an item is assigned to an action bar slot.
2026-03-11 18:36:32 -07:00
Kelsi
68a379610e Fix animation timing precision loss by replacing fmod with iterative subtraction
Floating-point fmod() loses precision with large accumulated time values, causing
subtle jumps/hitches in animation loops. Replace with iterative duration subtraction
to keep animationTime bounded and maintain precision, consistent with the fix
applied to character_renderer.cpp.

Applies to:
- M2 creature/object animation loops (main update)
- M2 particle-only instance wrapping (3333ms limit)
- M2 global sequence timing resolution
- M2 animated particle tile indexing
- Mount bobbing motion (sinusoidal rider motion)
- Character footstep trigger timing
- Mount footstep trigger timing

All timing computations now use the same precision-preserving approach.
2026-03-11 18:14:25 -07:00
Kelsi
f6f072a957 Increase lava/magma particle emission rate from 32 to 48 per second
Addresses sparseness in lava/magma effects noted in status documentation.
Higher emission rate (48 vs 32 per second) makes lava/slime areas visually
denser and more immersive while staying within GPU budget constraints.
2026-03-11 17:51:55 -07:00
Kelsi
eef269ffb8 Fix quest reward items showing as 'Item {number}' on first frame
Quest reward items (both in details and offer-reward windows) were showing as "Item {itemId}"
placeholders because the window opened immediately after receiving SMSG_QUESTGIVER_QUEST_DETAILS,
before the item query responses from pre-fetched queries had time to arrive.

Solution: Delay opening the quest details window by 100ms to allow item queries to complete
and be cached before the window first renders. Uses std::chrono::steady_clock for timing.

- Add questDetailsOpenTime field to track delayed opening timestamp
- Modify isQuestDetailsOpen() to check timer and open window when time expires
- Reset timer whenever quest details window closes
- Updated comment to clarify pre-fetch benefits both details and offer-reward windows
2026-03-11 17:27:23 -07:00
Kelsi
b5a48729b8 Add diagnostic logging for player appearance extraction failures
Log a warning when player model spawn fails due to appearance data extraction failure.
This helps diagnose why players appear invisible (missing field indices or malformed
update packets). Logs at both CREATE_OBJECT and VALUES update points.
2026-03-11 17:12:05 -07:00
Kelsi
b7479cbb50 Fix running animation hitching by using duration subtraction instead of fmod
Replace floating-point fmod() with iterative duration subtraction to preserve precision.
When animation time accumulates over many loops, fmod() loses precision with large values,
causing subtle jumps/hitches in looping animations. Subtracting the duration instead keeps
animationTime bounded in [0, duration) and avoids precision loss.
2026-03-11 17:02:15 -07:00
Kelsi
eb3cdbcc5f Fix stacked item count display in bags after looting
Move onlineItems_ lookup outside field iteration to ensure consistent item
reference when updating stack count/durability. This fixes an issue where
stacked item counts in open bags wouldn't update immediately when looting
additional items until the item was moved to another slot.
2026-03-11 16:58:36 -07:00
Kelsi
f7c752a316 Hide nameplates/health bars for corpses except when selected
Corpses no longer display nameplates or health bars unless they are the current
target (selected for loot or skinning). When selected, corpses show a minimal
grey nameplate with no health fill.
2026-03-11 16:54:30 -07:00
Kelsi
4d0eef1f6f Skip tab-targeting empty looted corpses
Dead creatures with no remaining loot items are now excluded from tab-targeting
cycle. Prevents cycling through empty corpses when looking for targetable enemies.
Corpses with available loot remain targetable.
2026-03-11 16:52:53 -07:00
Kelsi
bfeb978eff Fix character screen weapon slot positioning regression
Weapon slots were positioned too far right (at rightColX) causing overlap with right
column equipment. Repositioned to center column area (contentStartX + slotSize + 8px),
after left column. 3D preview renders on top, no visual conflict.
2026-03-11 16:49:27 -07:00
Kelsi
0c8fb94f0c Increase lava/magma smoke particle emission rate from 16 to 32 per second
Double the smoke particle emission rate to create visually richer lava and magma
effects. Current implementation emitted only 16 particles/sec per emitter (~88 in
steady state), which appeared sparse especially in multi-emitter lava areas.
Increasing to 32/sec provides denser steam/smoke effects (~176 in steady state)
while remaining well under the 1000 particle cap. This tuning opportunity was
documented in status.md as a known gap in visual completeness.
2026-03-11 16:47:07 -07:00
Kelsi
3f0e19970e Fix character screen weapon slots layout positioning
Position weapon slots (main hand, off hand, ranged) to align with the right
column instead of appearing in the left column where they crowd the main
equipment slots (tabbard, bracers, etc.). Weapons now positioned consistently
with the 3-column layout at rightColX instead of appearing at the default left
cursor position.
2026-03-11 16:32:30 -07:00
Kelsi
047b9157ad Validate transport registration before player attachment
Add upfront validation to setPlayerOnTransport to ensure the transport
GUID is registered in transportGuids_ before attaching the player. This
prevents transport desyncs when movement packets reference transports
that haven't been spawned/registered yet.
2026-03-11 16:06:36 -07:00
Kelsi
e2e049b718 docs: add Getting Started guide for new users 2026-03-11 15:36:44 -07:00
Kelsi
17bf963f3e docs: add comprehensive troubleshooting guide for users 2026-03-11 15:36:02 -07:00
Kelsi
2b8bb76d7a docs: add comprehensive multi-expansion architecture guide 2026-03-11 15:35:23 -07:00
Kelsi
1598766b1e docs: add Graphics & Performance guide with quality presets documentation 2026-03-11 15:34:16 -07:00
Kelsi
c77bd15538 docs: remove outdated 3D positional audio note - feature is implemented 2026-03-11 15:31:42 -07:00
Kelsi
90e7d61b6d docs: update for graphics presets and accurate shadow status 2026-03-11 15:30:45 -07:00
Kelsi
6f7c57d975 feat: add graphics quality presets system
Implement quick-access quality presets (Low, Medium, High, Ultra) that adjust multiple graphics settings at once for better user experience. Each preset configures:
- Shadow rendering and distance
- Anti-aliasing (MSAA) level
- Normal mapping and parallax mapping
- Ground clutter density

The system automatically detects when settings deviate from a preset and marks them as "Custom". Presets are persisted to settings.cfg for consistency across sessions. Users can quickly switch between performance and quality modes or tweak individual settings as needed.
2026-03-11 15:21:48 -07:00
Kelsi
6a8939d420 Harden final 8 parsers against truncated packets (100% coverage)
Remaining parsers now have upfront bounds checking:
- CharCreateResponseParser: validate 1 byte minimum
- QueryTimeResponseParser: validate 8 bytes (serverTime + offset)
- PlayedTimeParser: validate 9 bytes (totalTime + levelTime + flag)
- FriendStatusParser: validate 9 bytes + conditional string/flag
- LogoutResponseParser: validate 5 bytes (result + instant)
- RandomRollParser: validate 28 bytes (two GUIDs + three UInt32s)
- XpGainParser: validate 13 bytes base + conditional kill XP fields
- GroupInviteResponseParser: validate 1 byte + string (safe)

Packet parser hardening now at 100% coverage (all 106 parsers)
2026-03-11 15:08:48 -07:00
Kelsi
80c4e77c12 Harden GuildQueryResponseParser against truncated packets
Add bounds validation before reading guild name and 10 rank names.
Gracefully handle missing emblem data with safe defaults.
2026-03-11 14:46:44 -07:00
Kelsi
1979aa926b Harden TrainerListParser against truncated packets
Add upfront validation for header fields and per-spell bounds checking
before reading trainer spell data. Gracefully handle truncated greeting.
2026-03-11 14:44:52 -07:00
Kelsi
26f1a2d606 Harden GuildBankListParser against truncated packets
Cap tabCount to 8, add bounds validation before each tab and item read.
Gracefully handle truncated tab names, icons, and enchant data.
2026-03-11 14:43:03 -07:00
Kelsi
3849ad75ce Harden GuildRosterParser against unbounded memory allocation
Cap numMembers to 1000 and rankCount to 20 to prevent OOM attacks.
Add per-field bounds checking for member data with graceful truncation.
2026-03-11 14:42:09 -07:00
Kelsi
2c67331bc3 Harden MotdParser and UpdateObjectParser against truncated packets
- MotdParser: cap lineCount to 64 to prevent unbounded memory allocation,
  add bounds check before each string read
- UpdateObjectParser: add bounds validation before each update mask block
  and field value read to prevent reading past packet boundary
2026-03-11 14:41:25 -07:00
Kelsi
6fa1e49cb2 Harden CharEnumParser against truncated packets
Add upfront validation and per-field bounds checking to prevent
undefined behavior when parsing truncated SMSG_CHAR_ENUM packets.
Gracefully handle missing character data with safe defaults.
2026-03-11 14:40:07 -07:00
Kelsi
9892d82c52 Add upfront validation to group-related parsers
SMSG_PARTY_COMMAND_RESULT improvements:
- Validate 8-byte minimum for command + result + name string
- Graceful handling of truncated result field

SMSG_GROUP_DECLINE improvements:
- Validate 1-byte minimum for playerName CString
- Prevent reading from empty packets

Ensures consistent error handling for group system packets.
2026-03-11 14:38:11 -07:00
Kelsi
b699557597 Cap auction count in AuctionListResultParser
SMSG_AUCTION_LIST_RESULT (Classic/TBC/WotLK) improvements:
- Cap auction count to 256 (prevents unbounded memory allocation)
- Each entry is 80-104 bytes depending on expansion
- Prevents DoS from servers sending huge auction lists
- Log warning when cap is reached

Prevents memory exhaustion from malformed auction house packets.
2026-03-11 14:37:27 -07:00
Kelsi
6e94a3345f Add upfront validation to CastFailedParser
SMSG_CAST_FAILED (3.3.5a) improvements:
- Validate 6-byte minimum for castCount + spellId + result
- Prevent reading from truncated packets

Ensures consistent error handling for spell failure feedback.
2026-03-11 14:35:29 -07:00
Kelsi
4f3e817913 Harden GossipMessageParser against malformed packets
SMSG_GOSSIP_MESSAGE (3.3.5a) improvements:
- Validate 20-byte minimum for npcGuid + menuId + titleTextId + optionCount
- Cap optionCount to 64 (prevents unbounded memory allocation)
- Validate 12-byte minimum before each option read (fixed fields + 2 strings)
- Cap questCount to 64 (prevents unbounded memory allocation)
- Validate 18-byte minimum before each quest read (fixed fields + title string)
- Graceful truncation with partial list support

Prevents DoS from servers sending malformed gossip menus with huge option/quest lists.
2026-03-11 14:34:20 -07:00
Kelsi
efc394ce9e Cap spell cooldown entries in SpellCooldownParser
SMSG_SPELL_COOLDOWN (3.3.5a) improvements:
- Validate 9-byte minimum for guid + flags
- Cap cooldown entries to 512 (each entry is 8 bytes: spellId + ms)
- Prevent unbounded memory allocation from malformed packets
- Log warning when cap is reached with remaining data ignored

Prevents DoS from servers sending malformed cooldown lists.
2026-03-11 14:33:02 -07:00
Kelsi
1d4f69add3 Harden combat log parsers against malformed packets
SMSG_ATTACKERSTATEUPDATE (3.3.5a) improvements:
- Validate 13-byte minimum for hitInfo + GUIDs + totalDamage + count
- Cap subDamageCount to 64 (each entry is 20 bytes)
- Validate 20-byte minimum before each sub-damage entry read
- Validate 8-byte minimum before victimState/overkill read
- Validate 4-byte minimum before blocked amount read (optional field)

SMSG_SPELLDAMAGELOG (3.3.5a) improvements:
- Validate 30-byte minimum for all required fields
- Validate core fields before reading (21-byte check)
- Validate trailing fields (10-byte check) before reading flags/crit

SMSG_SPELLHEALLOG (3.3.5a) improvements:
- Validate 21-byte minimum for all required fields
- Validate remaining fields (17-byte check) before reading heal data
- Graceful truncation with field initialization

Prevents DoS and undefined behavior from high-frequency combat log packets.
2026-03-11 14:32:03 -07:00
Kelsi
68b3cef0fe Harden AuraUpdateParser against malformed packets
WotLK SMSG_AURA_UPDATE (3.3.5a) improvements:
- Cap entry count to 512 (isAll) or 1 (single) to prevent unbounded loop DoS
- Validate 5-byte minimum before each slot+spellId read
- Validate 3-byte minimum before flags/level/charges read
- Validate space before casterGuid packed GUID read
- Validate 8-byte minimum before duration field reads
- Validate 4-byte minimum before each effect amount read
- Graceful truncation with field initialization and partial read support
- Log all truncation events with entry index information

Prevents DoS and undefined behavior from high-frequency aura update packets.
2026-03-11 14:30:57 -07:00
Kelsi
7034bc5f63 Cap hit/miss counts in Classic and TBC spell parsers
Add DoS protection to Classic and TBC parseSpellGo implementations:
- Cap hitCount and missCount to 128 each (prevents OOM from huge arrays)
- Track actual reads vs expected counts
- Log truncation warnings with index information
- Graceful truncation with count updates

Ensures consistent hardening across all expansion variants (Vanilla/TBC/WotLK).
2026-03-11 14:29:37 -07:00
Kelsi
164124783b Harden SpellStart and SpellGo parsers against malformed packets
WotLK SMSG_SPELL_START (3.3.5a) improvements:
- Validate 22-byte minimum for packed GUIDs + fixed fields
- Validate targetFlags read (4 bytes)
- Validate targetGuid packed read with size check

WotLK SMSG_SPELL_GO (3.3.5a) improvements:
- Validate 24-byte minimum for core fields
- Cap hitCount to 128 to prevent OOM from huge target lists
- Cap missCount to 128 with same protection
- In-loop validation: check 8 bytes before each hit GUID read
- In-loop validation: check 2 bytes minimum before each miss entry (packed GUID + type)
- Graceful truncation with partial read support and count updates

Prevents DoS and undefined behavior from servers sending malformed combat packets.
2026-03-11 14:28:41 -07:00
Kelsi
98739c1610 Harden NameQueryResponseParser against malformed packets
Add upfront and in-loop validation for the WotLK variant of name query responses:
- Validate packed GUID and found flag reads (minimum 2 bytes)
- Validate strings can be read before attempting parse
- Validate 3 final uint8 fields (race/gender/class) exist before reading
- Graceful truncation handling with field initialization

Prevents undefined behavior from servers sending truncated/malformed packets.
2026-03-11 14:27:39 -07:00
Kelsi
2f1b142e14 Add packet size validation to SMSG_CREATURE_QUERY_RESPONSE parsing
Improve robustness of creature query response parsing by adding defensive
size checks to both WotLK/TBC and Classic variants:

- WotLK/TBC (world_packets.cpp): Add upfront validation for entry field,
  validate minimum size (16 bytes) before reading fixed fields
  (typeFlags, creatureType, family, rank), graceful truncation handling
- Classic (packet_parsers_classic.cpp): Add upfront entry validation,
  enhance existing truncation check with default field initialization,
  improve logging consistency
- Both variants now initialize fields to 0 on truncation and log warnings
  with entry context

Part of ongoing Tier 2 work to improve multi-expansion packet parsing robustness
against malformed or truncated server packets.
2026-03-11 14:19:58 -07:00
Kelsi
e464300346 Add pointCount cap to SMSG_MONSTER_MOVE spline parsing
Improve robustness of monster move spline parsing by capping the pointCount
field to prevent excessive iteration from malformed or malicious packets.

- WotLK: Cap pointCount to 1000 waypoints (realistic maximum for movement)
- Vanilla (Turtle): Reduce existing cap from 16384 to 1000 and add warning
  logging when cap is applied
- Both variants now log warnings when cap is exceeded, including guid context

A malicious or corrupted server sending an unrealistic pointCount value (e.g.
uint32_max) could previously cause the client to allocate excessive memory or
iterate excessively. The 1000-waypoint cap aligns with realistic movement
paths while protecting against DoS vectors.

Part of ongoing Tier 2 work to improve multi-expansion packet parsing robustness.
2026-03-11 14:13:09 -07:00
Kelsi
73abbc2a08 Add packet size validation to SMSG_GAMEOBJECT_QUERY_RESPONSE parsing
Improve robustness of game object query response parsing by adding defensive
size checks to both WotLK/TBC and Classic variants:

- WotLK/TBC (world_packets.cpp): Add upfront validation for entry, type,
  displayId fields, and improved in-loop handling for variable-length data
  array with partial data graceful degradation
- Classic (packet_parsers_classic.cpp): Add upfront validation for entry,
  type, displayId fields, and enhanced in-loop data array read with
  truncation detection
- Both variants now log warnings when data fields are truncated

Part of ongoing Tier 2 work to improve multi-expansion packet parsing robustness
against malformed or truncated server packets.
2026-03-11 14:11:45 -07:00
Kelsi
d1414b6a46 Add packet size validation to SMSG_INITIAL_SPELLS parsing
Improve robustness of initial spells parsing by adding defensive size checks:

- Validate minimum packet size for header (talentSpec + spellCount)
- Cap spellCount to max 256 spells to prevent excessive iteration
- Add in-loop size checks for each spell entry before reading (4 bytes
  vanilla, 6 bytes TBC/WotLK)
- Validate minimum size for cooldownCount field (optional, gracefully
  handles truncation before it)
- Cap cooldownCount to max 256 cooldowns to prevent excessive iteration
- Add in-loop size checks for each cooldown entry before reading (14 bytes
  vanilla, 16 bytes TBC/WotLK)
- Log warnings on packet truncation with clear context

Applies to both vanilla format (Classic) and TBC/WotLK format variants.
Part of ongoing Tier 2 work to improve multi-expansion packet parsing
robustness against malformed or truncated server packets.
2026-03-11 14:10:20 -07:00
Kelsi
f472ee3be8 Add packet size validation to SMSG_ITEM_QUERY_SINGLE_RESPONSE parsing
Improve robustness of item query response parsing across all three expansions
by adding defensive size checks and bounds validation:

- WotLK (world_packets.cpp): Add upfront validation for fixed-size fields,
  bounds cap on statsCount (max 10), in-loop size checks for stat pairs,
  and improved logging for truncation detection
- Classic (packet_parsers_classic.cpp): Add upfront validation for fixed fields,
  in-loop checks for 10 fixed stat pairs and 5 damage entries, and graceful
  truncation handling
- TBC (packet_parsers_tbc.cpp): Add upfront validation, statsCount bounds cap,
  and in-loop size checks for variable-length stats and fixed damage entries

All changes are backward compatible and log warnings on packet truncation.
This is part of ongoing Tier 2 work to improve multi-expansion packet parsing
robustness against malformed or truncated server packets.
2026-03-11 14:08:59 -07:00
Kelsi
d7e1a3773c Add validation caps and in-loop size checks to gossip message parsing
Improve gossip message parser robustness by:
- Adding count caps (max 256 options/quests) to prevent excessive memory allocation
- Adding in-loop size validation to detect truncated packets
- Gracefully breaking loops instead of reading garbage when packet runs out
- Logging warnings when packet truncation is detected

Applies to both Classic and TBC parseGossipMessage implementations.
Part of Tier 1/2 work to improve parser robustness across multi-expansion support.
2026-03-11 13:56:16 -07:00
Kelsi
d14f82cb7c Add packet size validation to character enum and movement parsing
Improve parser robustness by adding defensive size checks to prevent reading
beyond packet boundaries. Specifically:

- parseCharEnum (Classic/TBC): Add packet size validation and character count cap
  (max 32 chars) to prevent truncated packets from silently parsing garbage data
- parseMovementBlock (Classic/TBC): Add early validation for minimum packet size
  before reading updateFlags to catch empty packets early
- All changes are backward compatible and log warnings on truncation

This is part of Tier 1/2 work to improve multi-expansion packet parsing robustness
and prevent undefined behavior from malformed or truncated server packets.
2026-03-11 13:55:20 -07:00
Kelsi
fe2987dae1 feat: add frustum culling to quest marker rendering for consistency
Add view-frustum intersection testing to QuestMarkerRenderer::render() using
Frustum::intersectsSphere(), bringing quest marker culling in line with the
character instance and WMO group frustum culling improvements. Reduces marker
visibility testing overhead in scenes with many off-screen quest givers.
2026-03-11 13:30:07 -07:00
Kelsi
2c25e08a25 feat: upgrade character instance culling from distance/backface-check to frustum-sphere testing
Replace ad-hoc cone-based backface culling with proper view-frustum intersection
testing using Frustum::intersectsSphere(). Characters are now culled based on
visibility within the view frustum, improving accuracy in complex scenes and
reducing overdraw. Maintains distance-based culling for broad radius filtering.
2026-03-11 13:10:44 -07:00
Kelsi
a10e3e86fb feat: upgrade WMO group frustum culling from basic forward-check to proper frustum-AABB testing
Replace the basic forward-vector culling (which only culls when all AABB
corners are behind the camera) with proper frustum-AABB intersection testing
for more accurate and aggressive visibility culling. This reduces overdraw
and improves rendering performance in WMO-heavy scenes (dungeons, buildings).
2026-03-11 12:43:22 -07:00
Kelsi
508b7e839b feat: enable shadow rendering in character preview for visual depth
Enable shadows in character preview with 0.5 strength for a subtle
lighting effect that improves visual accuracy. Removes clearShadowMap()
call and enables shadowParams in preview UBO. Enhances character
appearance fidelity when viewing equipment and customization options.
2026-03-11 12:21:07 -07:00
Kelsi
6426bde7ea feat: enhance NPC tabard rendering with ItemDisplayInfo.dbc variant lookup
Look up tabard display ID from CreatureDisplayInfoExtra and map to
geoset variant via ItemDisplayInfo.dbc to select correct tabard
meshes. Falls back to hardcoded 1201 if DBC lookup unavailable.
Improves NPC appearance variety with proper scope handling.
2026-03-11 12:01:45 -07:00
Kelsi
4be7910fdf refactor: consolidate QueryTimer struct to shared header
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
Move QueryTimer from m2_renderer.cpp and wmo_renderer.cpp to
vk_frame_data.hpp for reuse. Removes 13 lines of duplicate code.
2026-03-11 11:42:01 -07:00
Kelsi
b5a2175269 refactor: consolidate duplicate ShadowParamsUBO structure definition
Move ShadowParamsUBO from 5 separate shadow rendering functions (2 in
m2_renderer, 1 in terrain_renderer, 1 in wmo_renderer) into shared
vk_frame_data.hpp header. Eliminates 5 identical local struct definitions
and improves consistency across all shadow pass implementations. Structure
layout matches shader std140 uniform buffer requirements.
2026-03-11 11:37:58 -07:00
Kelsi
b3d8651db9 refactor: consolidate duplicate environment variable utility functions
Move envSizeMBOrDefault and envSizeOrDefault from 4 separate rendering
modules (character_renderer, m2_renderer, terrain_renderer, wmo_renderer)
into shared vk_utils.hpp header as inline functions. Use the most robust
version which includes overflow checking for MB-to-bytes conversion. This
eliminates 7 identical local function definitions and improves consistency
across all rendering modules.
2026-03-11 11:36:06 -07:00
Kelsi
cda703b0f4 refactor: consolidate duplicate ShadowPush structure definition
Move ShadowPush from 4 separate rendering modules (character_renderer,
m2_renderer, terrain_renderer, wmo_renderer) into shared vk_frame_data.hpp
header. This eliminates 4 identical local struct definitions and ensures
consistency across all shadow rendering passes. Add vk_frame_data.hpp include
to character_renderer.cpp.
2026-03-11 11:32:08 -07:00
Kelsi
3202c1392d refactor: extract shared attachment lookup logic into helper function
Consolidated duplicate attachment point resolution code used by both
attachWeapon() and getAttachmentTransform(). New findAttachmentBone()
helper encapsulates the complete lookup chain: attachment by ID, fallback
scan, key-bone fallback, and validation. Eliminates ~55 lines of duplicate
code while improving maintainability and consistency.
2026-03-11 11:15:06 -07:00
Kelsi
bc6cd6e5f2 refactor: remove duplicate weapon key-bone fallback in attachWeapon()
Consolidated identical key-bone lookup logic that appeared at lines 3076
and 3099. Both performed the same search for weapon attachment points
(ID 1/2 for right/left hand). Removed duplication while preserving
behavior and improving code clarity with better comments.
2026-03-11 10:53:52 -07:00
Kelsi
9578e123cc fix: revert tabard DBC enhancement due to scope issue
The itExtra variable is not in scope at the tabard rendering site.
Reverted to original hardcoded 1201 fallback which is working reliably.
DBC variant approach requires refactoring variable scope.
2026-03-11 10:39:35 -07:00
Kelsi
71597c9a03 feat: enhance NPC tabard rendering to use ItemDisplayInfo.dbc variants
Reads equipped tabard display ID from CreatureDisplayInfoExtra (slot 9)
and looks up the corresponding geoset group in ItemDisplayInfo.dbc to
select the correct tabard variant. Falls back to hardcoded 1201 if DBC
unavailable. Improves NPC appearance variety without risky features.
2026-03-11 10:37:41 -07:00
Kelsi
589ec3c263 refactor: consolidate duplicate NPC helmet attachment code paths
Remove redundant helmet attachment code path (lines 6490-6566) that was
disabled and inferior to the main path. The main path (enabled in Loop 25)
provides better fallback logic by trying attachment points 0 and 11,
includes proper logging, and has undergone validation.

This consolidation reduces code duplication by 78 lines, improves
maintainability, and eliminates potentially wasteful spawn-time overhead
from the disabled path.
2026-03-11 10:14:49 -07:00
Kelsi
0d002c9070 feat: enable NPC helmet attachments with fallback logic for missing attachment points
Add fallback logic to use bone 0 for head attachment point (ID 11) when models
don't have it explicitly defined. This improves helmet rendering compatibility
on humanoid NPC models that lack explicit attachment 11 definitions. Re-enable
helmet attachments now that the fallback logic is in place.
2026-03-11 09:56:04 -07:00
Kelsi
176b8bdc3d feat: increase smoke particle emission rate from 8 to 16 per second for denser effects 2026-03-11 09:30:57 -07:00
Kelsi
1808d98978 feat: implement TOGGLE_MINIMAP and TOGGLE_RAID_FRAMES keybindings
- Add showMinimap_ and showRaidFrames_ visibility flags to GameScreen
- Wire up TOGGLE_MINIMAP (M key) to toggle minimap visibility
- Wire up TOGGLE_RAID_FRAMES (F key) to toggle party/raid frame visibility
- Conditional rendering of minimap markers and party frames
- Completes keybinding manager integration for all 15 customizable actions
2026-03-11 09:24:37 -07:00
Kelsi
1aa404d670 refactor: use keybinding manager for Escape (settings) and Enter (chat) keys
- Replace hardcoded SDL_SCANCODE_ESCAPE with TOGGLE_SETTINGS keybinding
- Replace hardcoded SDL_SCANCODE_RETURN with TOGGLE_CHAT keybinding
- Allows customization of these keys through Settings UI
2026-03-11 09:08:15 -07:00
Kelsi
f3415c2aff feat: implement TOGGLE_INVENTORY keybinding for I key in game_screen
- Add inventory window toggle on I key press
- Integrates with keybinding manager system for customizable inventory key
2026-03-11 09:05:17 -07:00
Kelsi
332c2f6d3f feat: add TOGGLE_BAGS action and integrate inventory screen with keybinding manager
- Add TOGGLE_BAGS action to keybinding manager (B key default)
- Update inventory_screen.cpp to use keybinding manager for bag and character toggles
- Maintain consistent keybinding system across all UI windows
2026-03-11 09:02:15 -07:00
Kelsi
7220737d48 refactor: use keybinding manager for spellbook and talents toggles instead of hardcoded keys 2026-03-11 08:58:20 -07:00
Kelsi
46365f4738 fix: correct keybinding defaults to match WoW standard keys
The keybinding manager had incorrect default key assignments:
- TOGGLE_SPELLBOOK was S (should be P - WoW standard)
- TOGGLE_TALENTS was K (should be N - WoW standard)

These mismatched the actual hardcoded keys in spellbook_screen.cpp (P) and
talent_screen.cpp (N), as well as user expectations from standard WoW.

Update keybinding defaults to align with WoW conventions and the actual UI
implementations that are using these keys.
2026-03-11 08:42:58 -07:00
Kelsi
82d00c94c0 refactor: use keybinding manager for quest log toggle instead of hardcoded L key
The quest log screen was using a hardcoded SDL_SCANCODE_L key check instead of
the keybinding manager system, preventing users from customizing the keybinding.

Update to use KeybindingManager::Action::TOGGLE_QUESTS (bound to L by default),
allowing users to customize the quest log toggle key through the Settings UI
while maintaining the default WoW key binding.

This enables consistency with other customizable window toggles that already use
the keybinding system (Character Screen, Inventory, Spellbook, World Map, etc.).
2026-03-11 08:28:34 -07:00
Kelsi
9809106a84 fix: resolve keybinding conflict - reassign TOGGLE_RAID_FRAMES from R to F
The R key was previously assigned to TOGGLE_RAID_FRAMES in the keybinding
manager but was never actually implemented (raid frames had no visibility
toggle). Loop 10 implemented R for camera reset, creating a conflict.

Reassign TOGGLE_RAID_FRAMES to F (an unused key) to prevent the conflict.
This aligns with the intention that R is now the standard camera reset key.
2026-03-11 08:09:55 -07:00
Kelsi
a8fd977a53 feat: re-enable R key for camera reset with chat input safeguard
Allow R key to reset camera position/rotation when chat input is not active.
Previously disabled due to conflict with chat reply command. Now uses the same
safety check as movement keys (ImGui::GetIO().WantTextInput).

Implements edge-triggered reset on R key press, matching X key (sit) pattern.
2026-03-11 07:53:36 -07:00
Kelsi
a3e0d36a72 feat: add World Map visibility toggle with keybinding support
Implement showWorldMap_ state variable and TOGGLE_WORLD_MAP keybinding
integration to allow players to customize the W key binding for opening/
closing the World Map, consistent with other window toggles like Nameplates
(V key) and Guild Roster (O key).
2026-03-11 07:38:08 -07:00
Kelsi
3092d406fa fix: enable NPC tabard geosets for proper equipment rendering
Enable tabard mesh rendering for NPCs by reading geoset variant from
ItemDisplayInfo.dbc (slot 9). Tabards now render like other equipment
instead of being disabled due to the previous flickering issue.
2026-03-11 07:24:01 -07:00
Kelsi
0d9404c704 feat: expand keybinding system with 4 new customizable actions
- Add World Map (W), Nameplates (V), Raid Frames (R), Quest Log (Q) to
  KeybindingManager enum with customizable default bindings
- Replace hard-coded V key check for nameplate toggle with
  KeybindingManager::isActionPressed() to support customization
- Update config file persistence to handle new bindings
- Infrastructure in place for implementing visibility toggles on other
  windows (World Map, Raid Frames, Quest Log) with future UI refactoring
2026-03-11 07:19:54 -07:00
Kelsi
f7a79b436e feat: integrate keybinding customization UI into Settings window
- Extended KeybindingManager enum with TOGGLE_GUILD_ROSTER (O) and
  TOGGLE_DUNGEON_FINDER (J) to replace hard-coded key checks
- Added Controls tab in Settings UI for rebinding all 10 customizable actions
- Implemented real-time key capture and binding with visual feedback
- Integrated keybinding persistence with main settings.cfg file
- Replaced hard-coded O key (Guild Roster) and I key (Dungeon Finder) checks
  with KeybindingManager::isActionPressed() calls
- Added Reset to Defaults button for restoring original keybindings
2026-03-11 06:51:48 -07:00
Kelsi
e6741f815a feat: add keybinding manager for customizable action shortcuts
Implement KeybindingManager singleton class to support:
- Storing and loading keybinding configuration from ini files
- Querying whether an action's keybinding was pressed
- Runtime rebinding of actions to different keys
- Default keybinding set: C=Character, I=Inventory, S=Spellbook, K=Talents,
  L=Quests, M=Minimap, Esc=Settings, Enter=Chat

This is the foundation for user-customizable keybindings. Integration with
UI controls and replacement of hard-coded ImGui::IsKeyPressed calls will
follow in subsequent improvements.
2026-03-11 06:26:57 -07:00
Kelsi
79c8d93c45 fix: use expansion-aware field indices for spell icon loading
The spell icon loader was incorrectly assuming WotLK field 133 (IconID)
for any DBC with >= 200 fields. This breaks Classic/TBC where IconID
is at different fields:
- Classic: field 117
- TBC: field 124
- WotLK: field 133

Now always uses expansion-aware layout (spellL) when available, falling
back to hardcoded field 133 only if the layout is missing.

Fixes missing spell icons on Classic and TBC expansions.
2026-03-11 05:26:38 -07:00
Kelsi
593f06bdf7 fix: correct Classic/TBC loot packet format parsing (missing randomSuffix/randomPropId)
SMSG_LOOT_START_ROLL, SMSG_LOOT_ALL_PASSED, and loot roll handlers unconditionally
read randomSuffix and randomPropertyId fields. These fields only exist in WotLK 3.3.5a
and NOT in Classic 1.12 / TBC 2.4.3, causing packet stream corruption on Classic/TBC servers.

Packet format differences:
- WotLK: includes randomSuffix (4) + randomPropId (4) fields
- Classic/TBC: no random property fields

Fix gates the field reads based on active expansion:
- SMSG_LOOT_START_ROLL: WotLK 33 bytes vs Classic/TBC 25 bytes
- SMSG_LOOT_ALL_PASSED: WotLK 24 bytes vs Classic/TBC 16 bytes
- SMSG_LOOT_ROLL: WotLK 34 bytes vs Classic/TBC 26 bytes
- SMSG_LOOT_ROLL_WON: WotLK 34 bytes vs Classic/TBC 26 bytes

This prevents packet stream desynchronization when loot rolls occur on Classic/TBC servers.
2026-03-11 05:09:43 -07:00
Kelsi
dd67c88175 fix: conditionally include trailing byte in CMSG_BUY_ITEM for Classic/TBC
CMSG_BUY_ITEM format differs by expansion:
- WotLK 3.3.5a / AzerothCore: includes trailing uint8(0) after count field (17 bytes)
- Classic 1.12 / TBC 2.4.3: no trailing byte (16 bytes)

The static BuyItemPacket::build() helper always adds the byte (AzerothCore compat).
GameHandler::buyItem() now gates the byte based on active expansion, allowing
Classic/TBC servers to receive correctly-sized packets.
2026-03-11 04:49:18 -07:00
Kelsi
ed48a3c425 fix: replace fragile heuristic in SMSG_INITIAL_SPELLS with explicit Classic format flag
Classic 1.12 uses uint16 spellId + uint16 slot (4 bytes/spell); TBC and WotLK
use uint32 spellId + uint16 unknown (6 bytes/spell). The old size-based heuristic
could misdetect TBC packets that happened to fit both layouts. Add a vanillaFormat
parameter to InitialSpellsParser::parse and override parseInitialSpells in
ClassicPacketParsers to always pass true, eliminating the ambiguity.
2026-03-11 04:38:30 -07:00
Kelsi
9d0da6242d fix: correct Classic/TBC MSG_MOVE_TELEPORT_ACK movement info parsing
Classic 1.12 and TBC 2.4.3 movement packets omit the moveFlags2 (uint16)
field present in WotLK 3.3.5a. The prior handler unconditionally read 2 bytes
for moveFlags2, shifting the timestamp and position reads by 2 bytes and
producing garbage coordinates after a teleport. Now gated by expansion.
2026-03-11 04:32:00 -07:00
Kelsi
d3241dce9e fix: handle Classic 1.12 SMSG_WEATHER missing isAbrupt byte
Classic 1.12 sends weatherType(4)+intensity(4) with no trailing isAbrupt byte;
TBC/WotLK append uint8 isAbrupt. The prior check required >= 9 bytes, so weather
never updated on Classic servers. Now accept >= 8 bytes and read isAbrupt only if
the byte is present.
2026-03-11 04:25:00 -07:00
Kelsi
fed03f970c fix: correct SMSG_BATTLEFIELD_STATUS Classic 1.12 packet layout
Classic uses queueSlot(4)+bgTypeId(4)+unk(2)+instanceId(4)+isReg(1)+statusId(4);
TBC/WotLK prefixes arenaType(1)+unk(1) before bgTypeId. Reading TBC format on
Classic caused bgTypeId to be read from wrong offset, corrupting BG queue state.
2026-03-11 04:22:18 -07:00
Kelsi
8493729a10 fix: use uint16 spellId in Classic 1.12 SMSG_LEARNED/REMOVED/SUPERCEDED_SPELL
Classic 1.12 (vmangos/cmangos) sends uint16 spellIds in SMSG_LEARNED_SPELL,
SMSG_REMOVED_SPELL, and SMSG_SUPERCEDED_SPELL. TBC 2.4.3 and WotLK 3.3.5a
use uint32. The handlers were unconditionally reading uint32, causing the
first byte of the next field to be consumed as part of the spellId on
Classic, producing garbage spell IDs and breaking known-spell tracking.

Apply the same Classic/TBC+WotLK gate used by the SMSG_INITIAL_SPELLS
heuristic: read uint16 for Classic, uint32 for all others.
2026-03-11 04:08:16 -07:00
Kelsi
750b270502 fix: use expansion-aware item size in LootResponseParser for Classic/TBC
The previous per-iteration heuristic (remaining >= 22 → 22 bytes, >= 14 → 14 bytes)
incorrectly parsed Classic/TBC multi-item loots: 2+ items × 14 bytes would
trigger the 22-byte WotLK path for the first item, corrupting subsequent items.

Classic 1.12 and TBC 2.4.3 use 14 bytes/item (slot+itemId+count+displayInfo+slotType).
WotLK 3.3.5a uses 22 bytes/item (adds randomSuffix+randomPropertyId).

Add isWotlkFormat bool parameter to LootResponseParser::parse and pass
isActiveExpansion('wotlk') from handleLootResponse.
2026-03-11 04:01:07 -07:00
Kelsi
dd7d74cb93 fix: correct SMSG_SPELL_FAILURE Classic format and result enum shift
Classic 1.12 SMSG_SPELL_FAILURE omits the castCount byte that TBC/WotLK
include (format: uint64 GUID + uint32 spellId + uint8 failReason).
The previous code read a castCount for all expansions, misaligning
spellId and failReason for Classic by one byte.

Also apply the same +1 enum shift used in parseCastFailed/parseCastResult:
Classic result 0=AFFECTING_COMBAT maps to WotLK 1=AFFECTING_COMBAT,
so Classic failReason=0 now correctly shows an error instead of being
silently swallowed.
2026-03-11 03:54:33 -07:00
Kelsi
d6e398d814 fix: add Classic parseCastResult override with result enum +1 shift
Classic 1.12 SMSG_CAST_RESULT uses an enum starting at 0=AFFECTING_COMBAT
(no SUCCESS entry), while WotLK starts at 0=SUCCESS, 1=AFFECTING_COMBAT.
Without this override, Classic result codes were handled by TBC's
parseCastResult which passed them unshifted, causing result 0
(AFFECTING_COMBAT) to be silently treated as success with no error shown.

This applies the same +1 shift used in parseCastFailed so all Classic
spell failure codes map correctly to getSpellCastResultString.
2026-03-11 03:53:18 -07:00
Kelsi
2f0809b570 fix: correct TBC aura entry minimum-size guard from 13 to 15 bytes
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
Each SMSG_INIT/SET_EXTRA_AURA_INFO entry is 15 bytes:
  uint8 slot(1) + uint32 spellId(4) + uint8 effectIndex(1)
  + uint8 flags(1) + uint32 durationMs(4) + uint32 maxDurMs(4) = 15

The previous guard of 13 would allow the loop to start reading a
partial entry, silently returning zeroes for durationMs/maxDurMs
when 13-14 bytes remained in the packet.
2026-03-11 03:49:54 -07:00
Kelsi
144c87a72f feat: show spell failure reason in chat from SMSG_SPELL_FAILURE
SMSG_SPELL_FAILURE carries a failReason byte (same enum as SMSG_CAST_RESULT)
that was previously ignored. Now parse castCount+spellId+failReason and
display the localized reason string for the player's interrupted casts
(e.g. 'Interrupted', 'Stunned', 'Can\'t do that while moving').
2026-03-11 03:42:41 -07:00
Kelsi
1446d4fddd fix: pass player power type to getSpellCastResultString for result 85
Result 85 is 'not enough power' — the message should say 'Not enough
rage', 'Not enough energy', 'Not enough runic power', etc. based on
the player's actual power type rather than always showing 'Not enough
mana'.
2026-03-11 03:41:49 -07:00
Kelsi
84a6ee4801 fix: surface absorb/resist in SMSG_ENVIRONMENTAL_DAMAGE_LOG (Classic/TBC)
The Classic/TBC variant handler was discarding the resisted field entirely
(only reading absorbed but discarding it). Now reads and shows both as
ABSORB/RESIST combat text, matching the WotLK SMSG_ENVIRONMENTALDAMAGELOG
fix from the previous commit.
2026-03-11 03:40:41 -07:00
Kelsi
00db93b7f2 fix: show RESIST (not MISS) for SMSG_PROCRESIST combat text
SMSG_PROCRESIST is sent when a proc effect is resisted. Show 'Resisted'
rather than 'Miss' to correctly communicate what happened to the player.
2026-03-11 03:38:39 -07:00
Kelsi
fb01361837 feat: show blocked amount and reduced damage on VICTIMSTATE_BLOCKS
When an attack is partially blocked, the server sends the remaining
damage in totalDamage and the blocked amount in data.blocked. Show
both: the damage taken and a 'Block N' entry. When block amount is
zero (full block with no damage), just show 'Block'.
2026-03-11 03:36:45 -07:00
Kelsi
d1c5e09127 fix: correct SMSG_PERIODICAURALOG packet format for WotLK 3.3.5a
WotLK adds an overkill(4) field between damage and school for aura type
3/89 (periodic damage), and adds absorbed(4)+isCrit(1) after overHeal
for aura type 8/124/45 (periodic heal). Without these fields the absorb
and resist values were reading out-of-alignment, producing garbage data.

Also surfaces the heal-absorbed amount as ABSORB combat text (e.g. when
a HoT tick is partially absorbed by Vampiric Embrace counter-healing).
2026-03-11 03:34:27 -07:00
Kelsi
f50cb04887 feat: surface absorb/resist from SMSG_ENVIRONMENTALDAMAGELOG
Environmental damage (drowning, lava, fire) also carries absorb/resist
fields. Show these as ABSORB/RESIST combat text so players see the full
picture of incoming environmental hits, consistent with spell/melee.
2026-03-11 03:31:33 -07:00
Kelsi
031448ec6d feat: show absorb/resist on periodic damage (DoT) ticks
SMSG_PERIODICAURALOG already parsed abs/res fields for type 3/89 but
discarded them. Surface these as ABSORB/RESIST combat text so players
see when DoT ticks are being partially absorbed (e.g. vs. PW:Shield).
2026-03-11 03:30:24 -07:00
Kelsi
dfc78572f5 feat: show melee absorb/resist in combat text from SMSG_ATTACKERSTATEUPDATE
Sub-damage entries carry absorbed/resisted per school. Accumulate these
and emit ABSORB/RESIST combat text alongside the hit damage when nonzero,
matching the behavior just added for SMSG_SPELLNONMELEEDAMAGELOG.
2026-03-11 03:29:37 -07:00
Kelsi
d2ae4d8215 feat: show partial absorb/resist amounts in spell combat text
handleSpellDamageLog now emits ABSORB/RESIST entries when data.absorbed
or data.resisted are nonzero, so players see 'Absorbed 123' alongside
damage numbers (e.g. vs. Power Word: Shield or Ice Barrier).
handleSpellHealLog does the same for heal absorbs (e.g. Vampiric Embrace
counter-absorbs). renderCombatText now formats amount when nonzero.
2026-03-11 03:28:19 -07:00
Kelsi
e902375763 feat: add ABSORB and RESIST combat text types for spell misses
Adds dedicated CombatTextEntry::Type entries for ABSORB (miss type 7)
and RESIST (miss type 8), replacing the generic MISS display. Updates
missTypes arrays in SMSG_SPELLLOGMISS and SMSG_SPELL_GO, and adds
light-blue "Absorb" and grey "Resist" rendering in the combat text overlay.
2026-03-11 03:23:01 -07:00
Kelsi
d5196abaec fix: show IMMUNE text for miss type 5 in SMSG_SPELL_GO and SMSG_SPELLLOGMISS
IMMUNE misses (spell miss type 5) were shown as generic MISS text in both
spell cast feedback handlers. Now consistently shows IMMUNE combat text
to match the fix already applied to SMSG_ATTACKERSTATEUPDATE.
2026-03-11 03:13:14 -07:00
Kelsi
35683920ff fix: handle EVADE/IMMUNE/DEFLECT victimStates in melee combat text
SMSG_ATTACKERSTATEUPDATE victimState values 5 (EVADE), 6 (IMMUNE), and
7 (DEFLECT) were previously falling through to the damage display path,
showing incorrect damage numbers instead of the proper miss/immune feedback.
Now correctly shows MISS for evade/deflect and IMMUNE for immune hits.
2026-03-11 03:09:39 -07:00
Kelsi
1646bef1c2 fix: add size guards to spell learn/remove handlers and implement SMSG_SPELLSTEALLOG
handleLearnedSpell, handleRemovedSpell, handleSupercededSpell, and
handleUnlearnSpells all lacked size checks before reading packet fields.
Also implements SMSG_SPELLSTEALLOG (previously silently consumed) with
proper player feedback showing the stolen spell name when the local player
is the caster, matching the same expansion-conditional packed-guid format
as SPELLDISPELLOG.
2026-03-11 03:03:44 -07:00
Kelsi
ae6c2aa056 fix: correct SMSG_SPELLDISPELLOG entry size from 8 to 5 bytes
Each dispelled spell entry is uint32(spellId) + uint8(isPositive) = 5 bytes,
not uint32 + uint32 = 8 bytes as the loop previously assumed.

The incorrect stride caused the second and subsequent entries to be read at
wrong offsets, potentially showing the wrong spell name for multi-dispels.
2026-03-11 02:57:05 -07:00
Kelsi
603e52e5b0 fix: add size check and skip WotLK guid suffix in handleCooldownEvent
SMSG_COOLDOWN_EVENT in WotLK appends an 8-byte unit guid after the spellId.
The handler was reading without a size check and not consuming the trailing
guid, which could misalign subsequent reads.
2026-03-11 02:51:58 -07:00
Kelsi
0a17683545 fix: correct WotLK packed guid format in SMSG_PROCRESIST and SMSG_TOTEM_CREATED
Both opcodes use packed GUIDs in WotLK 3.3.5a but were reading full uint64,
causing incorrect GUID parsing and potentially matching wrong player entities.

SMSG_PROCRESIST: caster + victim guids (packed in WotLK, uint64 in TBC/Classic)
SMSG_TOTEM_CREATED: totem guid (packed in WotLK, uint64 in TBC/Classic)
2026-03-11 02:50:53 -07:00
Kelsi
9cd7e7978d fix: correct SMSG_DISPEL_FAILED packet format and improve message
WotLK sends spellId(4) + packed_guid caster + packed_guid victim, while
TBC/Classic sends full uint64 caster + uint64 victim + spellId(4).
The previous handler assumed TBC format unconditionally, causing incorrect
reads in WotLK mode.

Also use the spell name cache to display "Purge failed to dispel." rather
than a raw "Dispel failed! (spell N)" message.
2026-03-11 02:49:37 -07:00
Kelsi
1f4880985b fix: correct SMSG_SPELLLOGMISS packet format for all expansions
All expansions send spellId(4) before the caster guid — the previous
handler was missing this field entirely, causing the caster guid read
to consume spellId bytes and corrupt all subsequent parsing.

Additionally, in WotLK mode, victim guids inside the per-miss loop are
packed guids (not full uint64), matching the caster guid format.

Also handle the REFLECT (missInfo=11) extra payload in WotLK: the server
appends reflectSpellId(4) + reflectResult(1) for reflected spells, which
previously caused the following loop entries to be mis-parsed.
2026-03-11 02:47:15 -07:00
Kelsi
d696da9227 fix: also use school mask for pre-cast melee range/facing check in castSpell()
Same DBC-driven physical school detection replaces the brittle hardcoded
warrior spell list in the pre-cast range check, so rogue, DK, paladin,
feral druid, and hunter melee abilities get correct range/facing enforcement.
2026-03-11 02:40:27 -07:00
Kelsi
21c55ad6b4 fix: detect melee abilities via spell school mask instead of hardcoded spell ID list
Replace the brittle warrior-only hardcoded spell ID list for melee ability
detection with a DBC-driven check: physical school mask (1) from spellNameCache_
covers warrior, rogue, DK, paladin, feral druid, and all other physical-school
instant abilities generically. Instant detection: spellId != currentCastSpellId.
2026-03-11 02:39:25 -07:00
Kelsi
643d48ee89 fix: show reason-specific messages for SMSG_TRANSFER_ABORTED
Replace generic 'Transfer aborted' message with WotLK TRANSFER_ABORT_*
reason codes: difficulty, expansion required, instance full, too many
instances, zone in combat, etc.
2026-03-11 02:36:55 -07:00
Kelsi
3082df2ac0 fix: use packed guids in SMSG_SPELLDAMAGESHIELD for WotLK and read absorbed field
WotLK 3.3.5a format uses packed guids (not full uint64) for victim and caster,
and adds an absorbed(4) field before schoolMask. Classic/TBC use full uint64 guids.
Previously the handler always read full uint64 guids, causing misparse on WotLK
(e.g. Thorns and Shield Spike damage shield combat text was garbled/wrong).
2026-03-11 02:27:57 -07:00
Kelsi
c4b2089d31 fix: handle OBS_MOD_POWER and PERIODIC_ENERGIZE aura types in SMSG_PERIODICAURALOG
Add PERIODIC_ENERGIZE (91) and OBS_MOD_POWER (46) handling so mana/energy/rage
restore ticks from common WotLK auras (Replenishment, Mana Spring Totem, Divine
Plea, etc.) appear as ENERGIZE in floating combat text. Also handle PERIODIC_MANA_LEECH
(98) to properly consume its 12 bytes instead of halting mid-event parse.
2026-03-11 02:25:42 -07:00
Kelsi
a67feb6d93 Fix handleInstanceDifficulty to handle variable-length packet formats
MSG_SET_DUNGEON_DIFFICULTY sends 4 or 12 bytes (difficulty + optional
isInGroup + savedBool) while SMSG_INSTANCE_DIFFICULTY sends 8 bytes
(difficulty + heroic).  The previous guard of < 8 caused the handler
to silently return for 4-byte variants, leaving instanceDifficulty_
unchanged.  Now reads as much as available and infers heroic flag from
the field count.
2026-03-11 02:08:38 -07:00
Kelsi
7c77c4a81e Fix per-frame particle descriptor set leak in M2 renderer
Pre-allocate one stable VkDescriptorSet per particle emitter at model
upload time (particleTexSets[]) instead of allocating a new set from
materialDescPool_ every frame for each particle group.  The per-frame
path exhausted the 8192-set pool in ~14 s at 60 fps with 10 active
particle emitters, causing GPU device-lost crashes.  The old path is
kept as an explicit fallback but should never be reached in practice.
2026-03-11 02:01:23 -07:00
Kelsi
570465f51a fix: handle MSG_SET_DUNGEON_DIFFICULTY and suppress SMSG_LEARNED_DANCE_MOVES warnings 2026-03-11 01:48:18 -07:00
Kelsi
f043077746 fix: free WMO and M2 material descriptor sets on group/model destroy to prevent pool exhaustion 2026-03-11 01:44:12 -07:00
Kelsi
4393798409 fix: parse WotLK areaId field in SMSG_INIT_WORLD_STATES to fix truncation warning 2026-03-11 01:40:33 -07:00
Kelsi
ce36171000 fix: prefer variationIndex=0 run animation and silence spurious compositeWithRegions warns
- character_renderer: playAnimation now prefers the primary variation
  (variationIndex==0) when multiple sequences share the same animation ID;
  this fixes hitching on human female run where a variation sequence was
  selected first before the base cycle
- character_renderer: move the compositeWithRegions size-mismatch warning
  inside the else branch so it only fires when sizes genuinely don't match,
  not for every successful 1:1 or scaled blit
2026-03-11 01:35:37 -07:00
Kelsi
f462db6bfa fix: terrain descriptor pool leak, minimap quest markers, and item comparison UI
- terrain_renderer: add FREE_DESCRIPTOR_SET_BIT flag and vkFreeDescriptorSets
  in destroyChunkGPU so material descriptor sets are returned to the pool;
  prevents GPU device lost from pool exhaustion near populated areas
- game_screen: fix projectToMinimap to use the exact inverse of the minimap
  shader transform so quest objective markers appear at the correct position
  and orientation regardless of camera bearing
- inventory_screen: fix item comparison tooltip to not compare equipped items
  against themselves (character screen); add item level diff line; show (=)
  indicator when stats are equal rather than bare value which looked identical
  to the item's own tooltip
2026-03-11 01:29:56 -07:00
Kelsi
568c566e1a fix: correct quest offer reward parser and trade slot trail size
- QuestOfferRewardParser: replace 4-variant heuristic with 0..16 byte
  prefix scan × fixed/variable arrays (34 candidates total).  AzerothCore
  WotLK 3.3.5a sends uint32 autoFinish + uint32 suggestedPlayers = 8 bytes
  before emoteCount; old uint8 read caused 3-byte misalignment, producing
  wrong item IDs and missing icons on quest reward windows.  Scoring now
  strongly favours the 8-byte prefix and exact byte consumption.
- Quest reward tooltip: delegate to InventoryScreen::renderItemTooltip()
  for full stats (armor, DPS, stats, bind type, etc.); show "Loading…"
  while item data is still fetching instead of showing nothing.
- SMSG_TRADE_STATUS_EXTENDED: fix SLOT_TRAIL 49→52 bytes.  AC 3.3.5a
  sends giftCreatorGuid(8) + 6 enchant slots(24) + randPropId(4) +
  suffixFactor(4) + durability(4) + maxDurability(4) + createPlayedTime(4)
  = 52 bytes after isWrapped; wrong skip misaligned all subsequent slots.
2026-03-11 01:00:08 -07:00
Kelsi
170ff1597c fix: prefetch item info for trade slot items in SMSG_TRADE_STATUS_EXTENDED
After parsing the peer's trade window state, query item info for all
occupied slots so item names display immediately rather than showing
'Item 12345' until the cache is populated on the next frame.
2026-03-11 00:46:11 -07:00
Kelsi
06facc0060 feat: implement trade window UI with item slots and gold offering
Previously trade only showed an accept/decline popup with no way to
actually offer items or gold. This commit adds the complete trade flow:

Packets:
- CMSG_SET_TRADE_ITEM (tradeSlot, bag, bagSlot) — add item to slot
- CMSG_CLEAR_TRADE_ITEM (tradeSlot) — remove item from slot
- CMSG_SET_TRADE_GOLD (uint64 copper) — set gold offered
- CMSG_UNACCEPT_TRADE — unaccept without cancelling
- SMSG_TRADE_STATUS_EXTENDED parser — updates trade slot/gold state

State:
- TradeSlot struct: itemId, displayId, stackCount, bag, bagSlot
- myTradeSlots_/peerTradeSlots_ arrays (6 slots each)
- myTradeGold_/peerTradeGold_ (copper)
- resetTradeState() helper clears all state on cancel/complete/close

UI (renderTradeWindow):
- Two-column layout: my offer | peer offer
- Each column shows 6 item slots with item names
- Double-click own slot to remove; right-click empty slot to open
  backpack picker popup
- Gold input field (copper, Enter to set)
- Accept Trade / Cancel buttons
- Window close button triggers cancel trade
2026-03-11 00:44:07 -07:00
Kelsi
7c5d688c00 fix: show area name in SMSG_ZONE_UNDER_ATTACK system message
Replace the raw area ID in the zone-under-attack message with the
resolved area name from the zone manager, matching retail WoW behavior
('Hillsbrad Foothills is under attack!' instead of 'area 267').
2026-03-11 00:36:40 -07:00
Kelsi
eaf827668a fix: parse SMSG_QUESTUPDATE_ADD_PVP_KILL and update quest log kill counts
Previously only displayed a chat message without updating the quest
tracker. Now parses the full packet (guid+questId+count+reqCount),
stores progress under entry-key 0 in killCounts, and shows a progress
message matching the format used for creature kills.

Handles both WotLK (4-field) and Classic (3-field, no reqCount) variants
with fallback to the existing killCounts or killObjectives for reqCount.
2026-03-11 00:34:23 -07:00
Kelsi
77ce54833a feat: add quest kill objective indicator (⚔) to unit nameplates
Yellow crossed-swords icon appears to the right of the unit name when
the creature's entry is an incomplete kill objective in a tracked quest.
Updated icon is suppressed once the kill count is satisfied.

Uses unit->getEntry() (Unit subclass method) rather than the base
Entity pointer, matching how questKillEntries keys are stored.
2026-03-11 00:29:35 -07:00
Kelsi
72a16a2427 fix: clear gossip/quest POI markers on map change (SMSG_NEW_WORLD)
Quest POI markers are map-specific. Clearing gossipPois_ on world entry
prevents stale markers from previous maps being displayed on the new map.
Quest POIs will be re-fetched as the quest log re-queries on the new map.
2026-03-11 00:24:35 -07:00
Kelsi
2ee0934653 fix: remove quest POI minimap markers when quest is abandoned
When abandonQuest() removes a quest from the log, also remove any
gossipPoi markers tagged with that questId (data field) so stale
objective markers don't linger on the minimap.
2026-03-11 00:21:33 -07:00
Kelsi
ef0e171da5 fix: deduplicate quest POI markers on repeated queries and fix LOG_DEBUG ordering
Remove existing POI markers for a quest before adding new ones (using the
data field as questId tag) so repeated CMSG_QUEST_POI_QUERY calls don't
accumulate duplicate markers. Also fix LOG_DEBUG to appear before the move.
2026-03-11 00:20:31 -07:00
Kelsi
6f5bdb2e91 feat: implement WotLK quest POI query to show objective locations on minimap
Send CMSG_QUEST_POI_QUERY alongside each CMSG_QUEST_QUERY (WotLK only,
gated by questLogStride == 5 and opcode availability). Parse the response
to extract POI region centroids and add them as GossipPoi markers so the
existing minimap rendering shows quest objective locations as cyan diamonds.

Each quest POI region is reduced to its centroid point; markers for the
current map only are shown. This gives players visual guidance for where
to go for active quests directly on the minimap.
2026-03-11 00:18:23 -07:00
Kelsi
12aa5e01b6 fix: correct game-object quest objective handling and item count fallback
- SMSG_QUESTUPDATE_ADD_KILL: use absolute value of npcOrGoId when looking
  up required count from killObjectives (negative values = game objects)
- applyPackedKillCountsFromFields: same fix — use abs(npcOrGoId) as map key
  so GO objective counts are stored with the correct entry key
- SMSG_QUESTUPDATE_ADD_ITEM: also match quests via itemObjectives when
  requiredItemCounts is not yet populated (race at quest accept time)
- Quest log and minimap sidebar: fall back to GO name cache for entries
  that return empty from getCachedCreatureName (interact/loot objectives)
2026-03-11 00:13:09 -07:00
Kelsi
e64b566d72 fix: correct TBC quest objective parsing and show creature names in quest log
SMSG_QUEST_QUERY_RESPONSE uses 40 fixed uint32 fields + 4 strings for both
Classic/Turtle and TBC, but the isClassicLayout flag was only set for stride-3
expansions (Classic/Turtle). TBC (stride 4) was incorrectly using the WotLK
55-field path, causing objective parsing to fail.

- Extend isClassicLayout to cover stride <= 4 (includes TBC)
- Refactor extractQuestQueryObjectives to try both layouts with fallback,
  matching the robustness of pickBestQuestQueryTexts
- Pre-fetch creature/GO/item name queries when quest objectives are parsed
  so names are ready before the player opens the quest log
- Quest log detail view: show creature names instead of raw entry IDs for
  kill objectives, and show required count (x/y) for item objectives
2026-03-11 00:05:05 -07:00
Kelsi
73439a4457 feat: restore quest kill counts from update fields using parsed objectives
Parse kill/item objectives from SMSG_QUEST_QUERY_RESPONSE binary data:
- extractQuestQueryObjectives() scans past the fixed integer header and
  variable-length strings to reach the 4 entity + 6 item objective entries
  (using known offsets: 40 fields for Classic/TBC, 55 for WotLK)
- Objectives stored in QuestLogEntry.killObjectives / itemObjectives arrays
- After storing, applyPackedKillCountsFromFields() reads 6-bit packed counts
  from update-field slots (stride+2 / stride+3) and populates killCounts
  using the parsed creature/GO entry IDs as keys

This means on login, quests that were in progress show correct kill count
progress (e.g. "2/5 Defias Bandits killed") without waiting for the first
server SMSG_QUESTUPDATE_ADD_KILL notification.
2026-03-10 23:52:18 -07:00
Kelsi
7e55d21cdd feat: read quest completion state from update fields on login and mid-session
resyncQuestLogFromServerSlots now reads the state field (slot*stride+1)
alongside the quest ID field, and marks quest.complete=true when the
server reports QuestStatus=1 (complete/ready-to-turn-in). Previously,
quests that were already complete before login would remain incorrectly
marked as incomplete until SMSG_QUESTUPDATE_COMPLETE fired, which only
happens when objectives are NEWLY completed during the session.

applyQuestStateFromFields() is a lightweight companion called from both
the CREATE and VALUES update handlers that applies the same state-field
check to already-tracked quests mid-session, catching the case where
the last objective completes via an update-field delta rather than the
dedicated quest-complete packet.

Works across all expansion strides (Classic stride=3, TBC stride=4,
WotLK stride=5); guarded against stride<2 (no state field available).
2026-03-10 23:33:38 -07:00
Kelsi
3a7ff71262 fix: use expansion-aware explored zone count to prevent fog-of-war corruption
Classic 1.12 and Turtle WoW have only 64 PLAYER_EXPLORED_ZONES uint32
fields (zone IDs pack into 2048 bits). TBC and WotLK use 128 (needed for
Outland/Northrend zone IDs up to bit 4095).

The hardcoded PLAYER_EXPLORED_ZONES_COUNT=128 caused extractExploredZoneFields
to read 64 extra fields beyond the actual zone block in Classic/Turtle —
consuming PLAYER_REST_STATE_EXPERIENCE, PLAYER_FIELD_COINAGE, and character-
points fields as zone flags. On the world map, this could mark zones as
explored based on random bit patterns in those unrelated fields.

Add `exploredZonesCount()` virtual method to PacketParsers (default=128,
Classic/Turtle override=64) and use it in extractExploredZoneFields to
limit reads to the correct block and zero-fill remaining slots.
2026-03-10 23:18:16 -07:00
Kelsi
1b55ebb387 fix: correct PLAYER_REST_STATE_EXPERIENCE wire indices for all expansions
The REST_STATE_EXPERIENCE field was erroneously set to the same index as
PLAYER_SKILL_INFO_START in all four expansion JSON files, causing the
rested XP tracker to read the first skill slot ID as the rested XP value.

Correct indices derived from layout: EXPLORED_ZONES_START + 128 zone
fields (or 64 for Classic) immediately precede PLAYER_FIELD_COINAGE, with
REST_STATE_EXPERIENCE in the one slot between them.

- WotLK: 636 → 1169  (1041 + 128 = 1169, before COINAGE=1170)
- Classic: 718 → 1175 (1111 + 64 = 1175, before COINAGE=1176)
- TBC: 928 → 1440    (1312 + 128 = 1440, before COINAGE=1441)
- Turtle: 718 → 1175 (same as Classic layout)
2026-03-10 23:14:18 -07:00
Kelsi
99de1fa3e5 feat: track UNIT_FIELD_STAT0-4 from server update fields for accurate character stats
Add UNIT_FIELD_STAT0-4 (STR/AGI/STA/INT/SPI) to the UF enum and wire up
per-expansion indices in all four expansion JSON files (WotLK: 84-88,
Classic/Turtle: 138-142, TBC: 159-163). Read the values in both CREATE
and VALUES player update handlers and store in playerStats_[5].

renderStatsPanel now uses the server-authoritative totals when available,
falling back to the previous 20+level estimate only if the server hasn't
sent UNIT_FIELD_STAT* yet. Item-query bonuses are still shown as (+N)
alongside the server total for both paths.
2026-03-10 23:08:15 -07:00
Kelsi
d95abfb607 feat: propagate OBJECT_FIELD_SCALE_X through creature and GO spawn pipeline
Reads OBJECT_FIELD_SCALE_X (field 4, cross-expansion) from CREATE_OBJECT
update fields and passes it through the full creature and game object spawn
chain: game_handler callbacks → pending spawn structs → async load results
→ createInstance() calls. This gives boss giants, gnomes, children, and
other non-unit-scale NPCs correct visual size, and ensures scaled GOs
(e.g. large treasure chests, oversized plants) render at the server-specified
scale rather than always at 1.0.

- Added OBJECT_FIELD_SCALE_X to UF enum and all expansion update_fields.json
- Added float scale to CreatureSpawnCallback and GameObjectSpawnCallback
- Propagated scale through PendingCreatureSpawn, PreparedCreatureModel,
  PendingGameObjectSpawn, PreparedGameObjectWMO
- Used scale in charRenderer/m2Renderer/wmoRenderer createInstance() calls
- Sanity-clamped raw float to [0.01, 100.0] range before use
2026-03-10 22:45:47 -07:00
Kelsi
b658743e94 feat: highlight required level in item tooltips when player is under-level
Display 'Requires Level N' in red when the player does not meet the
item's level requirement, and in normal colour when they do. Applies
to both equipped-item and bag-item tooltip paths.
2026-03-10 22:27:04 -07:00
Kelsi
00a939a733 fix: world map exploration fallback when server mask is unavailable
When the server has not sent SMSG_INIT_WORLD_STATES or the mask is
empty, fall back to locally-accumulated explored zones tracked by
player position. The local set is cleared when a real server mask
arrives so it doesn't persist stale data.
2026-03-10 22:27:00 -07:00
Kelsi
6928b8ddf6 feat: desaturate quest markers for trivial (gray) quests
Trivial/low-level quests now show gray '!' / '?' markers instead of
yellow, matching the in-game distinction between available and trivial
quests. Add grayscale parameter to QuestMarkerRenderer::setMarker and
the push-constant block; application sets grayscale=1.0 for trivial
markers and 0.0 for all others.
2026-03-10 22:26:56 -07:00
Kelsi
19eb7a1fb7 fix: animation stutter, resolution crash, memory cap, spell tooltip hints, GO collision
- Animation stutter: skip playAnimation(Run) for the local player in the
  server movement callback — the player renderer state machine already manages
  it; resetting animTime on every movement packet caused visible stutter
- Resolution crash: reorder swapchain recreation so old swapchain is only
  destroyed after confirming the new build succeeded; add null-swapchain
  guard in beginFrame to survive the retry window
- Memory cap: reduce cache budget from 80% uncapped to 50% hard-capped at
  16 GB to prevent excessive RAM use on high-memory systems
- Spell tooltip: suppress "Drag to action bar / Double-click to cast" hints
  when the tooltip is shown from the action bar (showUsageHints=false)
- M2 collision: add watermelon/melon/squash/gourd to foliage (no-collision);
  exclude chair/bench/stool/seat/throne from smallSolidProp so invisible chair
  bounding boxes no longer trap the player
2026-03-10 22:26:50 -07:00
Kelsi
8f2974b17c feat: add standalone LFG group-found popup (renderLfgProposalPopup)
Shows a centered modal when LfgState::Proposal is active regardless of
whether the Dungeon Finder window is open, matching WoW behaviour where
the accept/decline prompt always appears over the game world.
Mirrors the BG invite popup pattern; buttons call lfgAcceptProposal().
2026-03-10 21:40:21 -07:00
Kelsi
7c8bda0907 fix: suppress wchar_t '>= 0' tautological comparison warning on arm64
On Windows arm64, wchar_t is unsigned so 'wc >= 0' is always true
and GCC/Clang emit -Wtype-limits. Drop the redundant lower bound
check — only the upper bound 'wc <= 0x7f' is needed.
2026-03-10 21:34:54 -07:00
Kelsi
b4469b1577 fix: correct buff bar and quest tracker vertical positions
- Buff bar was at Y=140 which overlaps the minimap (Y=10 to Y=210);
  moved to Y=215 (just below minimap bottom edge) with 8 icons per row
- Quest tracker moved from Y=200 (inside minimap area) to Y=320 to
  leave space for up to 3 rows of buffs between minimap and tracker
- Both are right-anchored and no longer conflict with the minimap or
  each other in typical usage (up to ~20 active auras)
2026-03-10 21:32:58 -07:00
Kelsi
a7474b96cf fix: move buff bar to top-right, split buffs/debuffs, raise aura cap to 40
- Relocates buff bar from top-left Y=145 (overlapping party frames) to
  top-right (screenW - barW - 10, 140) where it doesn't conflict with
  party/raid frames anchored on the left side
- Increases max shown auras from 16 to 40 (WotLK supports 48 slots)
- Two-pass rendering: buffs shown first, debuffs below with a spacing gap
  between them; both still use green/red borders for visual distinction
- Widens row to 12 icons for better horizontal use of screen space
2026-03-10 21:29:47 -07:00
Kelsi
5fbeb7938c feat: right-click context menu for party member frames
Right-clicking a party member name in the 5-man party frame opens
a context menu with: Target, Set Focus, Whisper, Trade, Inspect.
- Whisper switches chat type to WHISPER and pre-fills the target name
- Trade calls GameHandler::initiateTrade(guid)
- Inspect sets target then calls GameHandler::inspectTarget()
- Uses BeginPopupContextItem tied to the Selectable widget
2026-03-10 21:27:26 -07:00
Kelsi
bc18fb7c3e feat: leader crown and LFG role indicators in party/raid frames
- Party frames: gold star prefix and gold name color for group leader;
  LFG role badges [T]/[H]/[D] shown inline after member name
- Raid frames: leader name rendered in gold with a corner star marker;
  role letter (T/H/D) drawn in bottom-right corner of each compact cell;
  uses partyData.leaderGuid already present in the function scope
- Minimap party dots already use gold for leader (unchanged)
2026-03-10 21:24:40 -07:00
Kelsi
4a445081d8 feat: latency indicator, BG queue status, and ToT improvements
- Add latency indicator below minimap (color-coded: green/yellow/orange/red)
  using the lastLatency value measured via CMSG_PING/SMSG_PONG
- Add BG queue status indicator below minimap when in WAIT_QUEUE
  (abbreviated name: AV/WSG/AB/EotS etc.)
- Target-of-Target frame: add level display and click-to-target support
- Expose getLatencyMs() accessor on GameHandler
2026-03-10 21:19:42 -07:00
Kelsi
8ab83987f1 feat: focus target frame with health/power bars and cast bar
Add a compact focus target frame on the right side of the screen
when the player has a focus target set via /focus.

- Shows [Focus] label, name (colored by hostility/level diff), level
- HP bar with green→yellow→red coloring; power bar with type colors
- Cast bar showing spell name and remaining time when focus is casting
- Clicking the frame targets the focus entity
- Clears automatically when focus is lost (/clearfocus)
2026-03-10 21:15:24 -07:00
Kelsi
a7a559cdcc feat: battleground invitation popup with countdown timer
Replace the text-only "/join to enter" message with an interactive
popup that shows the BG name, a live countdown progress bar, and
Enter/Leave Queue buttons.

- Parse STATUS_WAIT_JOIN timeout from SMSG_BATTLEFIELD_STATUS
- Store inviteReceivedTime (steady_clock) on the queue slot
- BgQueueSlot moved to public section so UI can read invite details
- Add declineBattlefield() that sends CMSG_BATTLEFIELD_PORT(action=0)
- acceptBattlefield() optimistically sets statusId=3 to dismiss popup
- renderBgInvitePopup: colored countdown bar (green→yellow→red),
  named BG (Alterac Valley, Warsong Gulch, etc.), auto-dismisses on expiry
2026-03-10 21:12:28 -07:00
Kelsi
4986308581 feat: rich item tooltips in vendor and loot-roll windows
- Vendor window: replace manual stat-only tooltip with full renderItemTooltip
  (now shows bind type, slot, weapon stats, armor, extra stats, spell effects,
  flavor text, and sell price — consistent with inventory)
- Loot-roll popup: add item icon and hover tooltip via renderItemTooltip
- Loot-roll: pre-fetch item info via queryItemInfo when roll prompt appears
2026-03-10 20:59:02 -07:00
Kelsi
6275a45ec0 feat: achievement name in toast, parse earned achievements, loot item tooltips
- Parse SMSG_ALL_ACHIEVEMENT_DATA on login to populate earnedAchievements_ set
- Pass achievement name through callback so toast shows name instead of ID
- Add renderItemTooltip(ItemQueryResponseData) overload for loot/non-inventory contexts
- Loot window now shows full item tooltip on hover (stats, sell price, bind type, etc.)
2026-03-10 20:53:21 -07:00
Kelsi
984decd664 fix: pre-fetch quest reward item info when quest details packet arrives
When SMSG_QUESTGIVER_QUEST_DETAILS is received (quest accept dialog),
immediately query item info for all rewardChoiceItems and rewardItems.
This ensures item names and icons are cached before the offer-reward
dialog opens on turn-in, eliminating the "Item {id}" placeholder that
appeared when the dialog opened before item queries completed.
2026-03-10 20:39:49 -07:00
Kelsi
b87b6cee0f fix: add ParentAreaNum/MapID to AreaTable DBC layout for world map exploration
AreaTable["ParentAreaNum"] was missing from all expansion DBC layouts,
causing getUInt32(i, 0xFFFFFFFF) to return 0 for every area's parent.
This made childBitsByParent keyed by 0 instead of the actual parent area
IDs, so sub-zone explore bits were never associated with their parent zones
on the world map.

Result: newly explored sub-zones (e.g. Stormwind Keep) would not reveal
their parent continent zones (Stormwind City) because the zone's exploreBits
only included the direct zone bit, not sub-zone bits.

Fix: add "MapID": 1, "ParentAreaNum": 2 to all expansion AreaTable layouts.
2026-03-10 20:35:42 -07:00
Kelsi
2b9f216dae fix: rest state detection and minimap north-up orientation
- WotLK opcode 0x21E is aliased to both SMSG_SET_REST_START and
  SMSG_QUEST_FORCE_REMOVE. In WotLK, treat as SET_REST_START (non-zero
  = entering rest area, zero = leaving); Classic/TBC treat as quest removal.
- PLAYER_BYTES_2 rest state byte: change from `& 0x01` to `!= 0` to also
  detect REST_TYPE_IN_CITY (value 2), not just REST_TYPE_IN_TAVERN (1).
- Minimap arrow: server orientation (π/2=North) needed conversion to
  minimap arrow space (0=North). Subtract π/2 in both render paths so
  arrow points North when player faces North.
2026-03-10 20:29:55 -07:00
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
Kelsi
701cb94ba6 physics: apply server walk and swim speed overrides to CameraController
serverWalkSpeed_ and serverSwimSpeed_ were stored in GameHandler but
never exposed or synced to the camera controller. The controller used
hardcoded WOW_WALK_SPEED and speed*SWIM_SPEED_FACTOR regardless of
server-sent speed changes.

Add getServerWalkSpeed()/getServerSwimSpeed() accessors, walkSpeedOverride_
and swimSpeedOverride_ fields in CameraController, and sync all three
server speeds each frame. Both swim speed sites (main and camera-collision
path) now use the override when set. This makes Slow debuffs (walk speed),
Swim Form, and Engineering fins actually affect movement speed.
2026-03-10 13:11:50 -07:00
Kelsi
f2337aeaa7 physics: disable gravity when server sends SMSG_MOVE_GRAVITY_DISABLE
SMSG_MOVE_GRAVITY_DISABLE/ENABLE now correctly set/clear the LEVITATING
movement flag instead of passing flag=0. GameHandler::isGravityDisabled()
reads the LEVITATING bit and is synced to CameraController each frame.

When gravity is disabled the physics loop bleeds off downward velocity
and skips gravity accumulation, so Levitate and similar effects actually
float the player rather than letting them fall through the world.
2026-03-10 13:07:34 -07:00
Kelsi
dd6f6d1174 physics: also block strafe input when movement is rooted 2026-03-10 13:02:35 -07:00
Kelsi
21604461fc physics: block client-side movement when server roots the player
When SMSG_FORCE_MOVE_ROOT sets ROOT in movementInfo.flags, the
camera controller was not aware and continued to accept directional
input. This caused position desync (client moves, server sees player
as rooted).

- Add movementRooted_ flag to CameraController with setter/getter.
- Block nowForward/nowBackward/nowStrafe when movementRooted_ is set.
- Sync isPlayerRooted() from GameHandler to CameraController each
  frame alongside the existing run-speed sync in application.cpp.
- Add GameHandler::isPlayerRooted() convenience accessor.
2026-03-10 13:01:44 -07:00
Kelsi
ea291179dd gameplay: fix talent reset and ignore list population on login
- SMSG_IGNORE_LIST was silently consumed; now parses guid+name pairs to
  populate ignoreCache so /unignore works correctly for pre-existing
  ignores loaded at login.

- MSG_TALENT_WIPE_CONFIRM was discarded without responding; now parses
  the NPC GUID and cost, shows a confirm dialog, and sends the required
  response packet when the player confirms. Without this, talent reset
  via Talent Master NPC was completely broken.
2026-03-10 12:53:05 -07:00
Kelsi
9291637977 movement: fix jumpXYSpeed to be 0 when jumping in place
jumpXYSpeed should reflect actual horizontal movement at jump time:
- non-zero (run/walk speed) only when movement flags indicate forward/
  backward/strafe movement
- zero when jumping straight up without horizontal movement

This prevents the server from thinking the player launched with full run
speed when they jumped in place, which could affect position prediction.
2026-03-10 12:37:53 -07:00
Kelsi
4cf73a6def movement: track fallTime and jump fields in movement packets
Previously movementInfo.fallTime was always 0 and jumpVelocity/jumpSinAngle/
jumpCosAngle/jumpXYSpeed were never populated.  The server reads fallTime
unconditionally from every movement packet and uses it to compute fall damage
and anti-cheat heuristics; the jump fields are required when FALLING is set.

Changes:
- Add isFalling_ / fallStartMs_ to track fall state across packets
- MSG_MOVE_JUMP: set isFalling_=true, record fallStartMs_, populate jump fields
  (jumpVelocity=7.96, direction from facing angle, jumpXYSpeed from server
  run speed or walk speed when WALKING flag is set)
- MSG_MOVE_FALL_LAND: clear all fall/jump fields
- sendMovement: update movementInfo.fallTime = (time - fallStartMs_) each call
  so every heartbeat and position packet carries the correct elapsed fall time
- World entry: reset all fall/jump fields alongside the flag reset
2026-03-10 12:36:56 -07:00
Kelsi
70abb12398 physics: send MSG_MOVE_JUMP on knockback to set FALLING flag correctly
applyKnockBack() sets grounded=false and applies vertical velocity, but
the normal jump detection path (nowJump && !wasJumping && grounded) never
fires during a server-driven knockback because no jump key is pressed.

Without MSG_MOVE_JUMP the game_handler never sets MovementFlags::FALLING
in movementInfo.flags, so all subsequent heartbeat packets carry incorrect
flags — the server sees the player as grounded while airborne.

Fix: fire movementCallback(MSG_MOVE_JUMP) directly from applyKnockBack()
so the FALLING flag is set immediately. MSG_MOVE_FALL_LAND is already sent
when grounded becomes true again (the existing wasFalling && grounded path).
2026-03-10 12:30:13 -07:00
Kelsi
c622fde7be physics: implement knockback simulation from SMSG_MOVE_KNOCK_BACK
Previously the handler ACKed with current position and ignored the
velocity fields entirely (vcos/vsin/hspeed/vspeed were [[maybe_unused]]).
The server expects the client to fly through the air on knockback — without
simulation the player stays in place while the server models them as airborne,
causing position desync and rubberbanding.

Changes:
- CameraController: add applyKnockBack(vcos, vsin, hspeed, vspeed)
  that sets knockbackHorizVel_ and launches verticalVelocity = -vspeed
  (server sends vspeed as negative for upward launches, matching TrinityCore)
- Physics loop: each tick adds knockbackHorizVel_ to targetPos then applies
  exponential drag (KNOCKBACK_HORIZ_DRAG=4.5/s) until velocity < 0.05 u/s
- GameHandler: parse all four fields, add KnockBackCallback, call it for
  the local player so the camera controller receives the impulse
- Application: register the callback — routes server knockback to physics

The existing ACK path is unchanged; the server gets position confirmation
as before while the client now actually simulates the trajectory.
2026-03-10 12:28:11 -07:00
Kelsi
dd3f9e5b9e wmo: enable portal culling by default after AABB transform fix
The AABB transform bug (direct min/max transform was wrong for rotated
WMOs) was fixed in a prior commit. Portal culling now uses the correct
world-space AABB computed from all 8 corners, so frustum intersection
is valid.

The AABB-based test is conservative (no portal plane-side check): a
visible portal can only be incorrectly INCLUDED, never EXCLUDED. This
means no geometry can disappear, and any overdraw is handled by the
z-buffer. Enable by default to get the performance benefit inside WMOs
and dungeons.
2026-03-10 12:11:13 -07:00
Kelsi
8856af6b2d lfg: implement CMSG_LFG_SET_BOOT_VOTE and vote-to-kick UI
CMSG_LFG_SET_BOOT_VOTE was defined in the opcode table but never sent.
- Add GameHandler::lfgSetBootVote(bool) which sends the packet
- Fix handleLfgBootProposalUpdate() to set lfgState_=Boot while the
  vote is in progress and return to InDungeon when it ends
- Add Yes/No vote buttons to the Dungeon Finder window when in Boot state
2026-03-10 12:08:58 -07:00
Kelsi
acbfe99401 anim: also trigger animation update on walk/run transitions for creatures
Extend the locomotion state-change detection to include the WALKING
movement flag. Previously a creature that switched from walking to
running (or vice versa) while staying in the moving state would keep
playing the wrong animation because only the moving/idle transition
was tracked.

Add creatureWasWalking_ alongside creatureWasSwimming_ and
creatureWasFlying_; guard the walking check with isMovingNow to avoid
spurious triggers when the flag flips while the creature is idle.
Clear and erase the new map at world reset and creature/player despawn.
2026-03-10 12:04:59 -07:00
Kelsi
2717018631 anim: fix creature animation not updating on swim/fly state transitions
Previously, the animation update for other entities (creatures, players)
was only triggered when the moving/idle state changed. This meant a
creature landing while still moving would stay in FlyForward instead of
switching to Run, and a flying-idle creature touching down would keep
the FlyIdle animation instead of returning to Stand.

Fix: track creatureWasSwimming_ and creatureWasFlying_ alongside
creatureWasMoving_, and fire the animation update whenever any of the
three locomotion flags change. Clean up the new maps on world reset and
on per-creature despawn.
2026-03-10 12:03:33 -07:00
Kelsi
8a20ccb69d anim: fix fly animation IDs to 158/159 (FlyIdle/FlyForward) 2026-03-10 11:58:19 -07:00
Kelsi
30a65320fb anim: add flying state tracking and Fly/FlyIdle animation selection for entities
Previously the move-flags callback only tracked SWIMMING and WALKING,
so flying players/mounts always played Run(5) or Stand(0) animations
instead of Fly(61)/FlyIdle(60).

Changes:
- Add creatureFlyingState_ (mirroring creatureSwimmingState_) set by
  the FLYING flag (0x01000000) in unitMoveFlagsCallback_.
- Update animation selection: moving+flying → 61 (Fly/FlyForward),
  idle+flying → 60 (FlyIdle/hover). Flying takes priority over swim
  in the priority chain: fly > swim > walk > run.
- Clear creatureFlyingState_ on world reset.
2026-03-10 11:56:50 -07:00
Kelsi
a33119c070 net: dispatch MSG_MOVE_ROOT and MSG_MOVE_UNROOT for other entities 2026-03-10 11:54:15 -07:00
Kelsi
1180f0227c rendering: fix WMO portal AABB transform for rotated WMOs
isPortalVisible() was computing the world-space AABB by directly
transforming pMin/pMax with the model matrix. This is incorrect for
rotated WMOs — when the model matrix includes rotations, components can
be swapped or negated, yielding an inverted AABB (worldMin.x >
worldMax.x) that causes frustum.intersectsAABB() to fail.

Fix: transform all 8 corners of the portal bounding box and take the
component-wise min/max, which gives the correct world-space AABB for any
rotation/scale. This was the root cause of portals being incorrectly
culled in rotated WMO instances (e.g. many dungeon and city WMOs).

Also squash the earlier spline-speed no-op fix (parse guid + float
instead of consuming the full packet for SMSG_SPLINE_SET_FLIGHT_SPEED
and friends) into this commit.
2026-03-10 11:51:43 -07:00
Kelsi
8152314ba8 net: dispatch MSG_MOVE_SET_PITCH, GRAVITY_CHNG, UPDATE_CAN_FLY, UPDATE_CAN_TRANSITION_SWIM_FLY
These four movement-broadcast opcodes (server relaying another player's
movement packet) were not dispatched at all, causing nearby entity
positions to be silently dropped for pitch changes and gravity/fly state
broadcasts. Also add them to the kMoveOpcodes batch-parse table used by
SMSG_COMPRESSED_MOVES, and parse SMSG_SPLINE_SET_FLIGHT/WALK/etc. speeds
properly instead of consuming the whole packet.
2026-03-10 11:44:57 -07:00
Kelsi
cfc6dc37c8 net: fix SMSG_SPLINE_MOVE_UNSET_FLYING and parse UNROOT/UNSET_HOVER/WATER_WALK
Previously these four spline-move opcodes were silently consumed with
packet.setReadPos(getSize()), skipping even the packed-GUID read.

- SMSG_SPLINE_MOVE_UNSET_FLYING: now reads packed guid and fires
  unitMoveFlagsCallback_(guid, 0) to clear the flying animation state on
  nearby entities (counterpart to SMSG_SPLINE_MOVE_SET_FLYING).
- SMSG_SPLINE_MOVE_UNROOT, SMSG_SPLINE_MOVE_UNSET_HOVER,
  SMSG_SPLINE_MOVE_WATER_WALK: now properly parse the packed guid instead
  of consuming the full packet; no animation-state callback needed.
2026-03-10 11:42:54 -07:00
Kelsi
84558fda69 net: ack SMSG_MOVE_SET/UNSET_CAN_TRANSITION_SWIM_FLY and SMSG_MOVE_SET_COLLISION_HGT
These three server-push opcodes were silently consumed without sending
the required client acks, causing the server to stall waiting for
confirmation before granting the capability.

- SMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY →
  CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK (via handleForceMoveFlagChange)
- SMSG_MOVE_UNSET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY →
  same ack opcode (no separate unset ack exists in WotLK 3.3.5a)
- SMSG_MOVE_SET_COLLISION_HGT → CMSG_MOVE_SET_COLLISION_HGT_ACK via new
  handleMoveSetCollisionHeight() which appends the float height after the
  standard movement block (required by server-side ack validation)
2026-03-10 11:40:46 -07:00
Kelsi
c72186fd11 net: ack SMSG_MOVE_GRAVITY_DISABLE/ENABLE and fix fall-through bug
These opcodes were inadvertently falling through to the LAND_WALK
handler (same case label), causing incorrect CMSG_MOVE_WATER_WALK_ACK
acks to be sent for gravity changes. Split into dedicated cases that
send CMSG_MOVE_GRAVITY_DISABLE_ACK and CMSG_MOVE_GRAVITY_ENABLE_ACK
respectively, as required by the server protocol.
2026-03-10 11:36:06 -07:00
Kelsi
b3441ee9ce net: ack SMSG_MOVE_LAND_WALK and SMSG_MOVE_NORMAL_FALL
These are the removal counterparts to SMSG_MOVE_WATER_WALK and
SMSG_MOVE_FEATHER_FALL. The server expects the matching ack with the
flag cleared; previously these packets were consumed silently which
could leave the server's state machine waiting for an acknowledgement.
2026-03-10 11:34:56 -07:00
Kelsi
ca141bb131 net: send CMSG_MOVE_FLIGHT_ACK in response to SMSG_MOVE_SET/UNSET_FLIGHT
SMSG_MOVE_SET_FLIGHT and SMSG_MOVE_UNSET_FLIGHT were previously consumed
silently without sending the required ack. Most server implementations
expect CMSG_MOVE_FLIGHT_ACK before toggling the FLYING movement flag on
the player; without it the server may not grant or revoke flight state.
Also updates movementInfo.flags so subsequent movement packets reflect
the FLYING flag correctly.
2026-03-10 11:33:47 -07:00
Kelsi
71cabddbd6 net: add MSG_MOVE_START_DESCEND to other-player movement dispatch
The complement to MSG_MOVE_START_ASCEND was missing from both the
main dispatch switch and the compressed-moves opcode table, causing
downward vertical movement of flying players to be dropped.
2026-03-10 11:30:55 -07:00
Kelsi
785df23f1b net: dispatch flying movement opcodes (pitch up/down, ascend/descend) for other players
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
MSG_MOVE_START_PITCH_UP, MSG_MOVE_START_PITCH_DOWN, MSG_MOVE_STOP_PITCH,
MSG_MOVE_START_ASCEND, MSG_MOVE_STOP_ASCEND were defined in the opcode
table but never routed. The server relays these when another player
pitches or ascends/descends while flying. Without dispatch, position
updates embedded in these packets were silently dropped, causing flying
players to appear to not move vertically. Also adds these to the
compressed-moves opcode recognition array.
2026-03-10 11:29:13 -07:00
Kelsi
274419584e net: add MSG_MOVE_SET_WALK/RUN_MODE to compressed-moves batch dispatch
handleCompressedMoves uses a hardcoded opcode array to recognise which
sub-packets should be routed to handleOtherPlayerMovement. The two newly
dispatched opcodes were not in this list, so walk/run mode transitions
embedded in SMSG_COMPRESSED_MOVES / SMSG_MULTIPLE_MOVES batches were
silently dropped.
2026-03-10 11:25:58 -07:00
Kelsi
d96a87aafc net: dispatch MSG_MOVE_SET_WALK_MODE and MSG_MOVE_SET_RUN_MODE through handleOtherPlayerMovement
These two opcodes were defined in the opcode table but never routed to
handleOtherPlayerMovement. The server sends them when another player
explicitly toggles walk/run mode. Without dispatch, the WALKING flag
from these packets was never processed, so other players appeared to
always run even after switching to walk mode (until the next heartbeat).
2026-03-10 11:24:15 -07:00
Kelsi
ec665bae25 net: store moveFlags in UpdateBlock for Classic and TBC parsers
Extends the cold-join fix (block.moveFlags) to the Classic and TBC
parseMovementBlock implementations so that SMSG_UPDATE_OBJECT CREATE
packets on Classic/TBC servers also initialise entity swim/walk state
from the spawn-time movement flags via unitMoveFlagsCallback_.
2026-03-10 11:21:13 -07:00
Kelsi
48d21f97bd net: correct pitch condition to use FLYING=0x01000000 not SPLINE_ELEVATION=0x02000000
The previous fix added SWIMMING (0x00200000) correctly but kept 0x02000000
which is SPLINE_ELEVATION (smooth vertical spline offset), not the FLYING
flag. WotLK 3.3.5a FLYING = 0x01000000; pitch should be read when SWIMMING
or FLYING are active. This corrects the condition and updates the comment.
2026-03-10 11:16:40 -07:00
Kelsi
863ea742f6 net/game: initialise entity swim/walk state from spawn packet and SPLINE_MOVE opcodes
UpdateBlock now stores moveFlags from the LIVING movement block so the
cold-join problem is fixed: entities already swimming or walking when the
client joins get their animation state correctly initialised from the
SMSG_UPDATE_OBJECT CREATE_OBJECT packet rather than waiting for the next
MSG_MOVE_* heartbeat.

Additionally, SMSG_SPLINE_MOVE_START_SWIM, SMSG_SPLINE_MOVE_STOP_SWIM,
SMSG_SPLINE_MOVE_SET_WALK_MODE, SMSG_SPLINE_MOVE_SET_RUN_MODE, and
SMSG_SPLINE_MOVE_SET_FLYING now fire unitMoveFlagsCallback_ with
synthesised flags so explicit server-driven mode transitions update
animation state immediately without waiting for a heartbeat.
2026-03-10 11:14:58 -07:00
Kelsi
3439df0333 net: fix UPDATEFLAG_LIVING pitch misalignment for swimming entities
parseMovementBlock was checking moveFlags & 0x02000000 for the pitch
field, but SWIMMING is 0x00200000 in WotLK 3.3.5a. Swimming NPCs and
players in SMSG_UPDATE_OBJECT packets never triggered the pitch read,
so the fallTime/jumpData/splineElevation fields were read from the wrong
offsets, producing incorrect positions and orientations.

Fix: check both SWIMMING (0x00200000) and FLYING (0x02000000), matching
the WotLK format — same condition used in the write path.
2026-03-10 11:03:33 -07:00
Kelsi
d7ebc5c8c7 game/rendering: drive Walk(4) and swim state from movement flags
Add UnitMoveFlagsCallback fired on every MSG_MOVE_* with the raw
movement flags field. Application.cpp uses it to update swimming
and walking state from any packet, not just explicit START_SWIM/
STOP_SWIM opcodes — fixing cold-join cases where a player is already
swimming when we enter the world.

Per-frame animation sync now selects Walk(4) when the WALKING flag is
set, Run(5) otherwise, and Swim(42)/SwimIdle(41) when swimming.
UnitAnimHintCallback is simplified to jump (38=JumpMid) only.
2026-03-10 10:55:23 -07:00
Kelsi
333ada8eb6 rendering/game: track per-entity swim state for correct water animations
- Add creatureSwimmingState_ map to track which units are swimming
- unitAnimHintCallback with animId=42 (Swim): marks entity as swimming
- unitAnimHintCallback with animId=0 (MSG_MOVE_STOP_SWIM): clears swim state
- Per-frame sync: uses Swim(42)/SwimIdle(41) when swimming, Run(5)/Stand(0) otherwise
  — creatures/players now show SwimIdle when standing still in water
- Clear creatureSwimmingState_ on creature/player despawn and world reset
2026-03-10 10:36:45 -07:00
Kelsi
14c2bc97b1 rendering/game: fix other-player movement animations and add jump/swim hints
- MSG_MOVE_STOP/STOP_STRAFE/STOP_TURN/STOP_SWIM/FALL_LAND: snap entity to
  stop position (duration=0) and pass durationMs=0 to renderer so the
  Run-animation flash is suppressed; per-frame sync plays Stand on next frame
- MSG_MOVE_JUMP: fire new UnitAnimHintCallback with anim 38 (JumpMid) so
  other players and NPCs visually leave the ground during jumps
- MSG_MOVE_START_SWIM: fire UnitAnimHintCallback with anim 42 (Swim)
- Wire up UnitAnimHintCallback in application.cpp; skips Death (anim 1)
2026-03-10 10:30:50 -07:00
Kelsi
137b25f318 rendering: fix NPC movement animation IDs and remove redundant player anim
The renderer's CharAnimState machine already drives player character
animations (Run=5, Walk=4, Jump, Swim, etc.) — remove the conflicting
camera controller code added in the previous commit.

Fix creature movement animations to use the correct WoW M2 IDs:
4=Walk, 5=Run. Both the per-frame sync loop and the SMSG_MONSTER_MOVE
spline callback now use Run (5) for NPC movement.
2026-03-10 10:19:13 -07:00
Kelsi
279c30367a rendering: use Walk (anim 5) vs Run (anim 4) based on movement pace
CameraController now plays Walk (5) for backpedal/slow pace and Run (4)
for forward running (runPace), matching WoW's animation convention.
Also handles transitions between Walk and Run while already moving.
2026-03-10 10:10:46 -07:00
Kelsi
baaa063342 rendering: play Run animation during SMSG_MONSTER_MOVE spline paths
creatureMoveCallback now plays anim 4 (Run) when a spline move begins
(durationMs > 0), mirroring the per-frame sync logic so NPC/player
characters animate correctly during server-driven path moves as well
as position-sync moves.
2026-03-10 10:09:02 -07:00
Kelsi
4e137c4061 rendering: drive Run/Stand animations from actual movement state
CameraController now transitions the player character to Run (anim 4)
on movement start and back to Stand (anim 0) on stop, guarded by a
prevPlayerMoving_ flag so animation time is not reset every frame.
Death animation (anim 1) is never overridden.

Application creature sync similarly switches creature models to Run (4)
when they move between server positions and Stand (0) when they stop,
with per-guid creatureWasMoving_ tracking to avoid per-frame resets.
2026-03-10 10:06:56 -07:00
Kelsi
c8d9d6b792 rendering/game: make player model semi-transparent in ghost form
Add GhostStateCallback to GameHandler, fired when PLAYER_FLAGS_GHOST
transitions on or off in UPDATE_OBJECT / login detection. Add
setInstanceOpacity() to CharacterRenderer to directly set opacity
without disturbing fade-in state. Application wires the callback to
set opacity 0.5 on ghost entry and 1.0 on resurrect.
2026-03-10 09:57:24 -07:00
Kelsi
366321042f rendering/game: sync camera sit state from server-confirmed stand state
Add CameraController::setSitting() and call it from the StandStateCallback
so the camera blocks movement when the server confirms the player is
sitting or kneeling (stand states 1-6, 8). This prevents the player
from sliding across the ground after sitting.

Death (state 7) deliberately leaves sitting=false so the player can
still respawn/move after death without input being blocked.
2026-03-10 09:51:15 -07:00
Kelsi
9f3c236c48 game/rendering: drive player stand-state animation from SMSG_STANDSTATE_UPDATE
Add StandStateCallback to GameHandler, fired when the server confirms
a stand state change (SMSG_STANDSTATE_UPDATE). Connect in Application
to map the WoW stand state (0-8) to M2 animation IDs on the player
character model:
  - 0 = Stand → anim 0 (Stand)
  - 1-6 = Sit variants → anim 27 (SitGround)
  - 7 = Dead → anim 1 (Death)
  - 8 = Kneel → anim 72 (Kneel)

Sit and Kneel animations are looped so the held-pose frame stays
visible; Death stays on the final frame.
2026-03-10 09:46:46 -07:00
Kelsi
59c50e3beb game/rendering: play SpellCast animation during SMSG_SPELL_START
Add SpellCastAnimCallback to GameHandler, triggered on SMSG_SPELL_START
(start=true) and cleared on SMSG_SPELL_GO / SMSG_SPELL_FAILURE
(start=false) for both the player and other units.

Connect the callback in Application to play animation 3 (SpellCast) on
the player character, NPCs, and other players when they begin a cast.
The cast animation is one-shot (loop=false) so it auto-returns to Stand
when complete via the existing return-to-idle logic.

Also fire stop-cast on spell failure to cancel any stuck cast pose.
2026-03-10 09:42:17 -07:00
Kelsi
c20d7c2638 rendering: return to Stand after one-shot emote animations complete
When a non-looping animation (e.g. wave, cheer, laugh emote) reaches
its end, transition back to Stand (animation 0) rather than freezing
on the last frame. Death (animation 1) is excluded — it stays on the
final frame as expected.

Fixes NPCs and players getting stuck in emote poses after SMSG_EMOTE.
2026-03-10 09:36:58 -07:00
Kelsi
60b93cdfd9 rendering/game: remove leftover debug dump I/O from hot paths
Remove active file-I/O debug block in character_renderer.cpp that
wrote composite textures as raw binary files to /tmp on every texture
composite generation. Remove the now-unused <fstream> include.

Remove the 10-shot hex dump of decompressed SMSG_MONSTER_MOVE payloads
in game_handler.cpp (dumpCount static); format has been confirmed.
2026-03-10 09:30:59 -07:00
Kelsi
55895340e9 game: connect emote animation callback to creature/player renderers
SMSG_EMOTE packets for NPCs and other players were received but the
emoteAnimCallback_ was never wired to the rendering layer.  Register
the callback in application.cpp so that when the server sends an emote
animation ID, the corresponding CharacterRenderer instance plays it as
a one-shot animation (loop=false), falling back to idle on completion.

Lookups check creatureInstances_ first, then playerInstances_ so both
NPCs and other online players respond to server emote packets.
2026-03-10 09:25:58 -07:00
Kelsi
b2dccca58c rendering: re-enable WMO camera collision with asymmetric smoothing
Previously disabled because the per-frame raycast caused erratic zoom
snapping at doorway transitions.  Re-enable using an asymmetrically-
smoothed collision limit: pull-in reacts quickly (τ≈60 ms) to prevent
the camera from ever visibly clipping through walls, while recovery is
slow (τ≈400 ms) so walking through a doorway zooms back out gradually
instead of snapping.

Uses wmoRenderer->raycastBoundingBoxes() which already has strict wall
filters (|normal.z|<0.20, surface-alignment check, ±0.9 height band)
to ignore floors, ramps, and arch geometry.
2026-03-10 09:13:31 -07:00
Kelsi
c622e547c9 game: clear in-flight NPC/GO query sets on disconnect
pendingCreatureQueries and pendingGameObjectQueries_ were never cleared on
disconnect. If a query was sent but the response lost (e.g. server
disconnect mid-flight), the entry would remain in the pending set after
reconnect, causing queryCreatureInfo/queryGameObjectInfo to silently skip
re-issuing the query — leaving NPC and GO names unpopulated for those
entries.

Clear both sets on disconnect so reconnect sees them as unqueried and
re-sends the queries as needed. creatureInfoCache/gameObjectInfoCache_ are
intentionally preserved across sessions to avoid re-querying entries whose
responses did arrive.
2026-03-10 09:01:34 -07:00
Kelsi
6763cfcda0 game: fix log level for GameObject CREATE messages (WARNING → DEBUG)
Every GameObject CREATE block was logged at WARNING level, spamming the
warning log with doors, chests, and other world objects. Demote to DEBUG
since this is routine spawn traffic; keep transport detection at INFO since
those are noteworthy.
2026-03-10 09:00:17 -07:00
Kelsi
4f51103cdb game: clear permanent-failure and dead-creature caches on same-map reconnect
After reconnect, `creaturePermanentFailureGuids_` and `deadCreatureGuids_`
could retain stale entries for GUIDs not tracked in `creatureInstances_`
(creatures that failed to load or died before being spawned).  These stale
entries would silently block re-spawning or cause wrong death state on the
fresh CREATE_OBJECTs the server sends after reconnect.

Clear both caches in the reconnect-to-same-map path so server state is
authoritative after every reconnect.
2026-03-10 08:55:23 -07:00
Kelsi
a06ac018ea game: use targeted entity cleanup on reconnect to same map, preserving terrain
The previous reconnect fix caused loadOnlineWorldTerrain to run, which
cleared and reloaded all terrain tiles — unnecessarily heavy for a
reconnect where the map hasn't changed.

New path: when isInitialEntry=true and mapId==loadedMapId_, despawn all
tracked creature/player/GO instances from the renderer (proper cleanup),
clear all pending spawn queues, update player position, and return — the
terrain stays loaded and the server's fresh CREATE_OBJECTs repopulate
entities normally.
2026-03-10 08:50:25 -07:00
Kelsi
d22f4b30ac game: process partial UPDATE_OBJECT packets when a block parse fails
Previously, if any single block in an SMSG_UPDATE_OBJECT packet failed
to parse (e.g. unusual spline flags), the entire packet was dropped and
all entities in it were lost. On busy zones with many CREATE_OBJECTs in
one packet, one bad NPC movement block would silently suppress all NPCs
that followed it in the same packet.

- parseUpdateObject: break instead of return false on block failure,
  so already-parsed blocks are returned to the caller
- handleUpdateObject: fall through to process partial data when parsing
  returns false but some blocks were successfully parsed
2026-03-10 08:38:39 -07:00
Kelsi
54246345bb game: fix NPCs not spawning on reconnect to same map
On disconnect/reconnect to the same map, entityManager was not cleared
and creatureInstances_ still held old entries from the previous session.
When the server re-sent CREATE_OBJECT for the same GUIDs, the spawn
callback's early-return guard (creatureInstances_.count(guid)) silently
dropped every NPC re-spawn, leaving the world empty.

Fixes:
- disconnect() now calls entityManager.clear() to purge stale entities
- WorldEntryCallback gains a bool isInitialEntry parameter (true on first
  login or reconnect, false on in-world teleport/flight landing)
- Same-map optimization path skipped when isInitialEntry=true, so
  loadOnlineWorldTerrain runs its full cleanup and properly despawns old
  creature/player instances before the server refreshes them
2026-03-10 08:35:36 -07:00
Kelsi
baab997da8 ui: fix XP bar overlapping second action bar by positioning above both bars 2026-03-10 08:28:48 -07:00
Kelsi
0a157d3255 game: add area name cache from WorldMapArea.dbc for /who zone display and exploration messages
- Load WorldMapArea.dbc lazily on first use to build areaId→name lookup
- /who results now show [Zone Name] alongside level: 'Name - Level 70 [Stormwind City]'
- SMSG_EXPLORATION_EXPERIENCE now shows 'Discovered Elwynn Forest! Gained X experience.'
  instead of generic 'Discovered new area!' message when the zone name is available
- Cache is populated once per session and shared across both callsites
2026-03-10 08:06:21 -07:00
Kelsi
846ba58d2e ui,game: show creature names in quest kill count tracker and progress messages
Quest kill count tracker in the HUD now resolves creature names from the
cached creature query results and displays them as "Name: x/y" instead
of bare "x/y". The system chat progress message on kill also includes
the creature name when available, matching retail client behavior.
2026-03-10 07:45:53 -07:00
Kelsi
03c4d59592 game: fix talent state not resetting across login sessions
Replace static-local firstSpecReceived with talentsInitialized_ member
variable, reset in handleLoginVerifyWorld alongside other per-session
state. Also clear learnedTalents_, unspentTalentPoints_, and
activeTalentSpec_ at world entry so reconnects and character switches
start from a clean talent state instead of carrying over stale data.
2026-03-10 07:41:27 -07:00
Kelsi
2a9d26e1ea game,ui: add rest state tracking and rested XP bar overlay
- Track PLAYER_REST_STATE_EXPERIENCE update field for all expansions
  (WotLK=636, Classic=718, TBC=928, Turtle=718)
- Set isResting_ flag from SMSG_SET_REST_START packet
- XP bar shows rested bonus as a lighter purple overlay extending
  beyond the current fill to (currentXp + restedXp) position
- Tooltip text changes to "%u / %u XP  (+%u rested)" when bonus exists
- "zzz" indicator shown at bar right edge while resting
2026-03-10 07:35:30 -07:00
234 changed files with 78253 additions and 5837 deletions

View file

@ -7,6 +7,7 @@ on:
branches: [master]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
WOWEE_AMD_FSR2_REPO: https://github.com/GPUOpen-Effects/FidelityFX-FSR2.git
WOWEE_AMD_FSR2_REF: master
WOWEE_FFX_SDK_REPO: https://github.com/Kelsidavis/FidelityFX-SDK.git

View file

@ -4,6 +4,9 @@ on:
push:
tags: ['v*']
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: write

View file

@ -7,6 +7,9 @@ on:
branches: [master]
workflow_dispatch:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read

8
.gitignore vendored
View file

@ -48,6 +48,7 @@ extern/*
!extern/imgui
!extern/vk-bootstrap
!extern/vk_mem_alloc.h
!extern/lua-5.1.5
# ImGui state
imgui.ini
@ -100,3 +101,10 @@ node_modules/
# Python cache artifacts
tools/__pycache__/
*.pyc
# artifacts
.codex-loop/
# Local agent instructions
AGENTS.md
codex-loop.sh

8
.semgrepignore Normal file
View file

@ -0,0 +1,8 @@
# Vendored third-party code (frozen releases, not ours to modify)
extern/lua-5.1.5/
extern/imgui/
extern/stb_image.h
extern/stb_image_write.h
extern/vk-bootstrap/
extern/FidelityFX-FSR2/
extern/FidelityFX-SDK/

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.15)
project(wowee VERSION 1.0.0 LANGUAGES CXX)
project(wowee VERSION 1.0.0 LANGUAGES C CXX)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 20)
@ -550,6 +550,12 @@ set(WOWEE_SOURCES
src/ui/quest_log_screen.cpp
src/ui/spellbook_screen.cpp
src/ui/talent_screen.cpp
src/ui/keybinding_manager.cpp
# Addons
src/addons/addon_manager.cpp
src/addons/lua_engine.cpp
src/addons/toc_parser.cpp
# Main
src/main.cpp
@ -653,6 +659,7 @@ set(WOWEE_HEADERS
include/ui/inventory_screen.hpp
include/ui/spellbook_screen.hpp
include/ui/talent_screen.hpp
include/ui/keybinding_manager.hpp
)
set(WOWEE_PLATFORM_SOURCES)
@ -666,6 +673,27 @@ if(WIN32)
list(APPEND WOWEE_PLATFORM_SOURCES resources/wowee.rc)
endif()
# ---- Lua 5.1.5 (vendored, static library) ----
set(LUA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/lua-5.1.5/src)
set(LUA_SOURCES
${LUA_DIR}/lapi.c ${LUA_DIR}/lcode.c ${LUA_DIR}/ldebug.c
${LUA_DIR}/ldo.c ${LUA_DIR}/ldump.c ${LUA_DIR}/lfunc.c
${LUA_DIR}/lgc.c ${LUA_DIR}/llex.c ${LUA_DIR}/lmem.c
${LUA_DIR}/lobject.c ${LUA_DIR}/lopcodes.c ${LUA_DIR}/lparser.c
${LUA_DIR}/lstate.c ${LUA_DIR}/lstring.c ${LUA_DIR}/ltable.c
${LUA_DIR}/ltm.c ${LUA_DIR}/lundump.c ${LUA_DIR}/lvm.c
${LUA_DIR}/lzio.c ${LUA_DIR}/lauxlib.c ${LUA_DIR}/lbaselib.c
${LUA_DIR}/ldblib.c ${LUA_DIR}/liolib.c ${LUA_DIR}/lmathlib.c
${LUA_DIR}/loslib.c ${LUA_DIR}/ltablib.c ${LUA_DIR}/lstrlib.c
${LUA_DIR}/linit.c
)
add_library(lua51 STATIC ${LUA_SOURCES})
set_target_properties(lua51 PROPERTIES LINKER_LANGUAGE C C_STANDARD 99 POSITION_INDEPENDENT_CODE ON)
target_include_directories(lua51 PUBLIC ${LUA_DIR})
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(lua51 PRIVATE -w)
endif()
# Create executable
add_executable(wowee ${WOWEE_SOURCES} ${WOWEE_HEADERS} ${WOWEE_PLATFORM_SOURCES})
if(TARGET opcodes-generate)
@ -707,6 +735,7 @@ target_link_libraries(wowee PRIVATE
OpenSSL::Crypto
Threads::Threads
ZLIB::ZLIB
lua51
${CMAKE_DL_LIBS}
)

View file

@ -1,96 +1,256 @@
{
"Spell": {
"ID": 0, "Attributes": 5, "IconID": 117,
"Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1
"ID": 0,
"Attributes": 5,
"AttributesEx": 6,
"IconID": 117,
"Name": 120,
"Tooltip": 147,
"Rank": 129,
"SchoolEnum": 1,
"CastingTimeIndex": 15,
"PowerType": 28,
"ManaCost": 29,
"RangeIndex": 33,
"DispelType": 4
},
"SpellRange": {
"MaxRange": 2
},
"ItemDisplayInfo": {
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
"ID": 0,
"LeftModel": 1,
"LeftModelTexture": 3,
"InventoryIcon": 5,
"GeosetGroup1": 7,
"GeosetGroup3": 9,
"TextureArmUpper": 14,
"TextureArmLower": 15,
"TextureHand": 16,
"TextureTorsoUpper": 17,
"TextureTorsoLower": 18,
"TextureLegUpper": 19,
"TextureLegLower": 20,
"TextureFoot": 21
},
"CharSections": {
"RaceID": 1, "SexID": 2, "BaseSection": 3,
"VariationIndex": 4, "ColorIndex": 5,
"Texture1": 6, "Texture2": 7, "Texture3": 8,
"RaceID": 1,
"SexID": 2,
"BaseSection": 3,
"VariationIndex": 4,
"ColorIndex": 5,
"Texture1": 6,
"Texture2": 7,
"Texture3": 8,
"Flags": 9
},
"SpellIcon": { "ID": 0, "Path": 1 },
"SpellIcon": {
"ID": 0,
"Path": 1
},
"FactionTemplate": {
"ID": 0, "Faction": 1, "FactionGroup": 3,
"FriendGroup": 4, "EnemyGroup": 5,
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
"ID": 0,
"Faction": 1,
"FactionGroup": 3,
"FriendGroup": 4,
"EnemyGroup": 5,
"Enemy0": 6,
"Enemy1": 7,
"Enemy2": 8,
"Enemy3": 9
},
"Faction": {
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
"ReputationBase0": 10, "ReputationBase1": 11,
"ReputationBase2": 12, "ReputationBase3": 13
"ID": 0,
"ReputationRaceMask0": 2,
"ReputationRaceMask1": 3,
"ReputationRaceMask2": 4,
"ReputationRaceMask3": 5,
"ReputationBase0": 10,
"ReputationBase1": 11,
"ReputationBase2": 12,
"ReputationBase3": 13
},
"AreaTable": {
"ID": 0,
"MapID": 1,
"ParentAreaNum": 2,
"ExploreFlag": 3
},
"AreaTable": { "ID": 0, "ExploreFlag": 3 },
"CreatureDisplayInfoExtra": {
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
"EquipDisplay9": 17, "EquipDisplay10": 18, "BakeName": 20
"ID": 0,
"RaceID": 1,
"SexID": 2,
"SkinID": 3,
"FaceID": 4,
"HairStyleID": 5,
"HairColorID": 6,
"FacialHairID": 7,
"EquipDisplay0": 8,
"EquipDisplay1": 9,
"EquipDisplay2": 10,
"EquipDisplay3": 11,
"EquipDisplay4": 12,
"EquipDisplay5": 13,
"EquipDisplay6": 14,
"EquipDisplay7": 15,
"EquipDisplay8": 16,
"EquipDisplay9": 17,
"EquipDisplay10": 18,
"BakeName": 20
},
"CreatureDisplayInfo": {
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
"Skin1": 6, "Skin2": 7, "Skin3": 8
"ID": 0,
"ModelID": 1,
"ExtraDisplayId": 3,
"Skin1": 6,
"Skin2": 7,
"Skin3": 8
},
"TaxiNodes": {
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5
"ID": 0,
"MapID": 1,
"X": 2,
"Y": 3,
"Z": 4,
"Name": 5
},
"TaxiPath": {
"ID": 0,
"FromNode": 1,
"ToNode": 2,
"Cost": 3
},
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
"TaxiPathNode": {
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
"X": 4, "Y": 5, "Z": 6
"ID": 0,
"PathID": 1,
"NodeIndex": 2,
"MapID": 3,
"X": 4,
"Y": 5,
"Z": 6
},
"TalentTab": {
"ID": 0, "Name": 1, "ClassMask": 12,
"OrderIndex": 14, "BackgroundFile": 15
"ID": 0,
"Name": 1,
"ClassMask": 12,
"OrderIndex": 14,
"BackgroundFile": 15
},
"Talent": {
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
"ID": 0,
"TabID": 1,
"Row": 2,
"Column": 3,
"RankSpell0": 4,
"PrereqTalent0": 9,
"PrereqRank0": 12
},
"SkillLineAbility": {
"SkillLineID": 1,
"SpellID": 2
},
"SkillLine": {
"ID": 0,
"Category": 1,
"Name": 3
},
"Map": {
"ID": 0,
"InternalName": 1
},
"CreatureModelData": {
"ID": 0,
"ModelPath": 2
},
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
"Map": { "ID": 0, "InternalName": 1 },
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
"CharHairGeosets": {
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
"RaceID": 1,
"SexID": 2,
"Variation": 3,
"GeosetID": 4
},
"CharacterFacialHairStyles": {
"RaceID": 0, "SexID": 1, "Variation": 2,
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
"RaceID": 0,
"SexID": 1,
"Variation": 2,
"Geoset100": 3,
"Geoset300": 4,
"Geoset200": 5
},
"GameObjectDisplayInfo": {
"ID": 0,
"ModelName": 1
},
"Emotes": {
"ID": 0,
"AnimID": 2
},
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
"Emotes": { "ID": 0, "AnimID": 2 },
"EmotesText": {
"ID": 0, "Command": 1, "EmoteRef": 2,
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
"ID": 0,
"Command": 1,
"EmoteRef": 2,
"OthersTargetTextID": 3,
"SenderTargetTextID": 5,
"OthersNoTargetTextID": 7,
"SenderNoTargetTextID": 9
},
"EmotesTextData": {
"ID": 0,
"Text": 1
},
"EmotesTextData": { "ID": 0, "Text": 1 },
"Light": {
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
"ID": 0,
"MapID": 1,
"X": 2,
"Z": 3,
"Y": 4,
"InnerRadius": 5,
"OuterRadius": 6,
"LightParamsID": 7,
"LightParamsIDRain": 8,
"LightParamsIDUnderwater": 9
},
"LightParams": {
"LightParamsID": 0
},
"LightParams": { "LightParamsID": 0 },
"LightIntBand": {
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
"BlockIndex": 1,
"NumKeyframes": 2,
"TimeKey0": 3,
"Value0": 19
},
"LightFloatBand": {
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
"BlockIndex": 1,
"NumKeyframes": 2,
"TimeKey0": 3,
"Value0": 19
},
"WorldMapArea": {
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
"DisplayMapID": 8, "ParentWorldMapID": 10
"ID": 0,
"MapID": 1,
"AreaID": 2,
"AreaName": 3,
"LocLeft": 4,
"LocRight": 5,
"LocTop": 6,
"LocBottom": 7,
"DisplayMapID": 8,
"ParentWorldMapID": 10
},
"SpellVisual": {
"ID": 0,
"CastKit": 2,
"ImpactKit": 3,
"MissileModel": 8
},
"SpellVisualKit": {
"ID": 0,
"BaseEffect": 5,
"SpecialEffect0": 11,
"SpecialEffect1": 12,
"SpecialEffect2": 13
},
"SpellVisualEffectName": {
"ID": 0,
"FilePath": 2
}
}

View file

@ -273,7 +273,7 @@
"SMSG_INVENTORY_CHANGE_FAILURE": "0x112",
"SMSG_OPEN_CONTAINER": "0x113",
"CMSG_INSPECT": "0x114",
"SMSG_INSPECT": "0x115",
"SMSG_INSPECT_RESULTS_UPDATE": "0x115",
"CMSG_INITIATE_TRADE": "0x116",
"CMSG_BEGIN_TRADE": "0x117",
"CMSG_BUSY_TRADE": "0x118",
@ -300,7 +300,7 @@
"CMSG_NEW_SPELL_SLOT": "0x12D",
"CMSG_CAST_SPELL": "0x12E",
"CMSG_CANCEL_CAST": "0x12F",
"SMSG_CAST_RESULT": "0x130",
"SMSG_CAST_FAILED": "0x130",
"SMSG_SPELL_START": "0x131",
"SMSG_SPELL_GO": "0x132",
"SMSG_SPELL_FAILURE": "0x133",
@ -504,8 +504,7 @@
"CMSG_GM_SET_SECURITY_GROUP": "0x1F9",
"CMSG_GM_NUKE": "0x1FA",
"MSG_RANDOM_ROLL": "0x1FB",
"SMSG_ENVIRONMENTALDAMAGELOG": "0x1FC",
"CMSG_RWHOIS_OBSOLETE": "0x1FD",
"SMSG_ENVIRONMENTAL_DAMAGE_LOG": "0x1FC",
"SMSG_RWHOIS": "0x1FE",
"MSG_LOOKING_FOR_GROUP": "0x1FF",
"CMSG_SET_LOOKING_FOR_GROUP": "0x200",
@ -528,7 +527,6 @@
"CMSG_GMTICKET_GETTICKET": "0x211",
"SMSG_GMTICKET_GETTICKET": "0x212",
"CMSG_UNLEARN_TALENTS": "0x213",
"SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE": "0x214",
"SMSG_GAMEOBJECT_DESPAWN_ANIM": "0x215",
"MSG_CORPSE_QUERY": "0x216",
"CMSG_GMTICKET_DELETETICKET": "0x217",
@ -538,7 +536,7 @@
"SMSG_GMTICKET_SYSTEMSTATUS": "0x21B",
"CMSG_SPIRIT_HEALER_ACTIVATE": "0x21C",
"CMSG_SET_STAT_CHEAT": "0x21D",
"SMSG_SET_REST_START": "0x21E",
"SMSG_QUEST_FORCE_REMOVE": "0x21E",
"CMSG_SKILL_BUY_STEP": "0x21F",
"CMSG_SKILL_BUY_RANK": "0x220",
"CMSG_XP_CHEAT": "0x221",
@ -571,8 +569,6 @@
"CMSG_BATTLEFIELD_LIST": "0x23C",
"SMSG_BATTLEFIELD_LIST": "0x23D",
"CMSG_BATTLEFIELD_JOIN": "0x23E",
"SMSG_BATTLEFIELD_WIN_OBSOLETE": "0x23F",
"SMSG_BATTLEFIELD_LOSE_OBSOLETE": "0x240",
"CMSG_TAXICLEARNODE": "0x241",
"CMSG_TAXIENABLENODE": "0x242",
"CMSG_ITEM_TEXT_QUERY": "0x243",
@ -605,7 +601,6 @@
"SMSG_AUCTION_BIDDER_NOTIFICATION": "0x25E",
"SMSG_AUCTION_OWNER_NOTIFICATION": "0x25F",
"SMSG_PROCRESIST": "0x260",
"SMSG_STANDSTATE_CHANGE_FAILURE_OBSOLETE": "0x261",
"SMSG_DISPEL_FAILED": "0x262",
"SMSG_SPELLORDAMAGE_IMMUNE": "0x263",
"CMSG_AUCTION_LIST_BIDDER_ITEMS": "0x264",
@ -693,8 +688,8 @@
"SMSG_SCRIPT_MESSAGE": "0x2B6",
"SMSG_DUEL_COUNTDOWN": "0x2B7",
"SMSG_AREA_TRIGGER_MESSAGE": "0x2B8",
"CMSG_TOGGLE_HELM": "0x2B9",
"CMSG_TOGGLE_CLOAK": "0x2BA",
"CMSG_SHOWING_HELM": "0x2B9",
"CMSG_SHOWING_CLOAK": "0x2BA",
"SMSG_MEETINGSTONE_JOINFAILED": "0x2BB",
"SMSG_PLAYER_SKINNED": "0x2BC",
"SMSG_DURABILITY_DAMAGE_DEATH": "0x2BD",
@ -821,6 +816,5 @@
"SMSG_LOTTERY_RESULT_OBSOLETE": "0x337",
"SMSG_CHARACTER_PROFILE": "0x338",
"SMSG_CHARACTER_PROFILE_REALM_CONNECTED": "0x339",
"SMSG_UNK": "0x33A",
"SMSG_DEFENSE_MESSAGE": "0x33B"
}

View file

@ -1,5 +1,6 @@
{
"OBJECT_FIELD_ENTRY": 3,
"OBJECT_FIELD_SCALE_X": 4,
"UNIT_FIELD_TARGET_LO": 16,
"UNIT_FIELD_TARGET_HI": 17,
"UNIT_FIELD_BYTES_0": 36,
@ -13,15 +14,22 @@
"UNIT_FIELD_DISPLAYID": 131,
"UNIT_FIELD_MOUNTDISPLAYID": 133,
"UNIT_FIELD_AURAS": 50,
"UNIT_FIELD_AURAFLAGS": 98,
"UNIT_NPC_FLAGS": 147,
"UNIT_DYNAMIC_FLAGS": 143,
"UNIT_FIELD_RESISTANCES": 154,
"UNIT_FIELD_STAT0": 138,
"UNIT_FIELD_STAT1": 139,
"UNIT_FIELD_STAT2": 140,
"UNIT_FIELD_STAT3": 141,
"UNIT_FIELD_STAT4": 142,
"UNIT_END": 188,
"PLAYER_FLAGS": 190,
"PLAYER_BYTES": 191,
"PLAYER_BYTES_2": 192,
"PLAYER_XP": 716,
"PLAYER_NEXT_LEVEL_XP": 717,
"PLAYER_REST_STATE_EXPERIENCE": 1175,
"PLAYER_FIELD_COINAGE": 1176,
"PLAYER_QUEST_LOG_START": 198,
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
@ -33,6 +41,8 @@
"PLAYER_END": 1282,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_STACK_COUNT": 14,
"ITEM_FIELD_DURABILITY": 48,
"ITEM_FIELD_MAXDURABILITY": 49,
"CONTAINER_FIELD_NUM_SLOTS": 48,
"CONTAINER_FIELD_SLOT_1": 50
}

View file

@ -1,98 +1,303 @@
{
"Spell": {
"ID": 0, "Attributes": 5, "IconID": 124,
"Name": 127, "Tooltip": 154, "Rank": 136, "SchoolMask": 215
"ID": 0,
"Attributes": 5,
"AttributesEx": 6,
"IconID": 124,
"Name": 127,
"Tooltip": 154,
"Rank": 136,
"SchoolMask": 215,
"CastingTimeIndex": 22,
"PowerType": 35,
"ManaCost": 36,
"RangeIndex": 40,
"DispelType": 3
},
"SpellRange": {
"MaxRange": 4
},
"ItemDisplayInfo": {
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
"ID": 0,
"LeftModel": 1,
"LeftModelTexture": 3,
"InventoryIcon": 5,
"GeosetGroup1": 7,
"GeosetGroup3": 9,
"TextureArmUpper": 14,
"TextureArmLower": 15,
"TextureHand": 16,
"TextureTorsoUpper": 17,
"TextureTorsoLower": 18,
"TextureLegUpper": 19,
"TextureLegLower": 20,
"TextureFoot": 21
},
"CharSections": {
"RaceID": 1, "SexID": 2, "BaseSection": 3,
"VariationIndex": 4, "ColorIndex": 5,
"Texture1": 6, "Texture2": 7, "Texture3": 8,
"RaceID": 1,
"SexID": 2,
"BaseSection": 3,
"VariationIndex": 4,
"ColorIndex": 5,
"Texture1": 6,
"Texture2": 7,
"Texture3": 8,
"Flags": 9
},
"SpellIcon": { "ID": 0, "Path": 1 },
"SpellIcon": {
"ID": 0,
"Path": 1
},
"FactionTemplate": {
"ID": 0, "Faction": 1, "FactionGroup": 3,
"FriendGroup": 4, "EnemyGroup": 5,
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
"ID": 0,
"Faction": 1,
"FactionGroup": 3,
"FriendGroup": 4,
"EnemyGroup": 5,
"Enemy0": 6,
"Enemy1": 7,
"Enemy2": 8,
"Enemy3": 9
},
"Faction": {
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
"ReputationBase0": 10, "ReputationBase1": 11,
"ReputationBase2": 12, "ReputationBase3": 13
"ID": 0,
"ReputationRaceMask0": 2,
"ReputationRaceMask1": 3,
"ReputationRaceMask2": 4,
"ReputationRaceMask3": 5,
"ReputationBase0": 10,
"ReputationBase1": 11,
"ReputationBase2": 12,
"ReputationBase3": 13
},
"CharTitles": {
"ID": 0,
"Title": 2,
"TitleBit": 20
},
"AreaTable": {
"ID": 0,
"MapID": 1,
"ParentAreaNum": 2,
"ExploreFlag": 3
},
"AreaTable": { "ID": 0, "ExploreFlag": 3 },
"CreatureDisplayInfoExtra": {
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
"EquipDisplay9": 17, "EquipDisplay10": 18, "BakeName": 20
"ID": 0,
"RaceID": 1,
"SexID": 2,
"SkinID": 3,
"FaceID": 4,
"HairStyleID": 5,
"HairColorID": 6,
"FacialHairID": 7,
"EquipDisplay0": 8,
"EquipDisplay1": 9,
"EquipDisplay2": 10,
"EquipDisplay3": 11,
"EquipDisplay4": 12,
"EquipDisplay5": 13,
"EquipDisplay6": 14,
"EquipDisplay7": 15,
"EquipDisplay8": 16,
"EquipDisplay9": 17,
"EquipDisplay10": 18,
"BakeName": 20
},
"CreatureDisplayInfo": {
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
"Skin1": 6, "Skin2": 7, "Skin3": 8
"ID": 0,
"ModelID": 1,
"ExtraDisplayId": 3,
"Skin1": 6,
"Skin2": 7,
"Skin3": 8
},
"TaxiNodes": {
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5,
"MountDisplayIdAllianceFallback": 12, "MountDisplayIdHordeFallback": 13,
"MountDisplayIdAlliance": 14, "MountDisplayIdHorde": 15
"ID": 0,
"MapID": 1,
"X": 2,
"Y": 3,
"Z": 4,
"Name": 5,
"MountDisplayIdAllianceFallback": 12,
"MountDisplayIdHordeFallback": 13,
"MountDisplayIdAlliance": 14,
"MountDisplayIdHorde": 15
},
"TaxiPath": {
"ID": 0,
"FromNode": 1,
"ToNode": 2,
"Cost": 3
},
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
"TaxiPathNode": {
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
"X": 4, "Y": 5, "Z": 6
"ID": 0,
"PathID": 1,
"NodeIndex": 2,
"MapID": 3,
"X": 4,
"Y": 5,
"Z": 6
},
"TalentTab": {
"ID": 0, "Name": 1, "ClassMask": 12,
"OrderIndex": 14, "BackgroundFile": 15
"ID": 0,
"Name": 1,
"ClassMask": 12,
"OrderIndex": 14,
"BackgroundFile": 15
},
"Talent": {
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
"ID": 0,
"TabID": 1,
"Row": 2,
"Column": 3,
"RankSpell0": 4,
"PrereqTalent0": 9,
"PrereqRank0": 12
},
"SkillLineAbility": {
"SkillLineID": 1,
"SpellID": 2
},
"SkillLine": {
"ID": 0,
"Category": 1,
"Name": 3
},
"Map": {
"ID": 0,
"InternalName": 1
},
"CreatureModelData": {
"ID": 0,
"ModelPath": 2
},
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
"Map": { "ID": 0, "InternalName": 1 },
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
"CharHairGeosets": {
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
"RaceID": 1,
"SexID": 2,
"Variation": 3,
"GeosetID": 4
},
"CharacterFacialHairStyles": {
"RaceID": 0, "SexID": 1, "Variation": 2,
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
"RaceID": 0,
"SexID": 1,
"Variation": 2,
"Geoset100": 3,
"Geoset300": 4,
"Geoset200": 5
},
"GameObjectDisplayInfo": {
"ID": 0,
"ModelName": 1
},
"Emotes": {
"ID": 0,
"AnimID": 2
},
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
"Emotes": { "ID": 0, "AnimID": 2 },
"EmotesText": {
"ID": 0, "Command": 1, "EmoteRef": 2,
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
"ID": 0,
"Command": 1,
"EmoteRef": 2,
"OthersTargetTextID": 3,
"SenderTargetTextID": 5,
"OthersNoTargetTextID": 7,
"SenderNoTargetTextID": 9
},
"EmotesTextData": {
"ID": 0,
"Text": 1
},
"EmotesTextData": { "ID": 0, "Text": 1 },
"Light": {
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
"ID": 0,
"MapID": 1,
"X": 2,
"Z": 3,
"Y": 4,
"InnerRadius": 5,
"OuterRadius": 6,
"LightParamsID": 7,
"LightParamsIDRain": 8,
"LightParamsIDUnderwater": 9
},
"LightParams": {
"LightParamsID": 0
},
"LightParams": { "LightParamsID": 0 },
"LightIntBand": {
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
"BlockIndex": 1,
"NumKeyframes": 2,
"TimeKey0": 3,
"Value0": 19
},
"LightFloatBand": {
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
"BlockIndex": 1,
"NumKeyframes": 2,
"TimeKey0": 3,
"Value0": 19
},
"WorldMapArea": {
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
"DisplayMapID": 8, "ParentWorldMapID": 10
"ID": 0,
"MapID": 1,
"AreaID": 2,
"AreaName": 3,
"LocLeft": 4,
"LocRight": 5,
"LocTop": 6,
"LocBottom": 7,
"DisplayMapID": 8,
"ParentWorldMapID": 10
},
"SpellItemEnchantment": {
"ID": 0,
"Name": 8
},
"ItemSet": {
"ID": 0,
"Name": 1,
"Item0": 18,
"Item1": 19,
"Item2": 20,
"Item3": 21,
"Item4": 22,
"Item5": 23,
"Item6": 24,
"Item7": 25,
"Item8": 26,
"Item9": 27,
"Spell0": 28,
"Spell1": 29,
"Spell2": 30,
"Spell3": 31,
"Spell4": 32,
"Spell5": 33,
"Spell6": 34,
"Spell7": 35,
"Spell8": 36,
"Spell9": 37,
"Threshold0": 38,
"Threshold1": 39,
"Threshold2": 40,
"Threshold3": 41,
"Threshold4": 42,
"Threshold5": 43,
"Threshold6": 44,
"Threshold7": 45,
"Threshold8": 46,
"Threshold9": 47
},
"SpellVisual": {
"ID": 0,
"CastKit": 2,
"ImpactKit": 3,
"MissileModel": 8
},
"SpellVisualKit": {
"ID": 0,
"BaseEffect": 5,
"SpecialEffect0": 11,
"SpecialEffect1": 12,
"SpecialEffect2": 13
},
"SpellVisualEffectName": {
"ID": 0,
"FilePath": 2
}
}

View file

@ -1,5 +1,6 @@
{
"OBJECT_FIELD_ENTRY": 3,
"OBJECT_FIELD_SCALE_X": 4,
"UNIT_FIELD_TARGET_LO": 16,
"UNIT_FIELD_TARGET_HI": 17,
"UNIT_FIELD_BYTES_0": 36,
@ -16,12 +17,18 @@
"UNIT_NPC_FLAGS": 168,
"UNIT_DYNAMIC_FLAGS": 164,
"UNIT_FIELD_RESISTANCES": 185,
"UNIT_FIELD_STAT0": 159,
"UNIT_FIELD_STAT1": 160,
"UNIT_FIELD_STAT2": 161,
"UNIT_FIELD_STAT3": 162,
"UNIT_FIELD_STAT4": 163,
"UNIT_END": 234,
"PLAYER_FLAGS": 236,
"PLAYER_BYTES": 237,
"PLAYER_BYTES_2": 238,
"PLAYER_XP": 926,
"PLAYER_NEXT_LEVEL_XP": 927,
"PLAYER_REST_STATE_EXPERIENCE": 1440,
"PLAYER_FIELD_COINAGE": 1441,
"PLAYER_QUEST_LOG_START": 244,
"PLAYER_FIELD_INV_SLOT_HEAD": 650,
@ -30,8 +37,12 @@
"PLAYER_FIELD_BANKBAG_SLOT_1": 784,
"PLAYER_SKILL_INFO_START": 928,
"PLAYER_EXPLORED_ZONES_START": 1312,
"PLAYER_FIELD_HONOR_CURRENCY": 1505,
"PLAYER_FIELD_ARENA_CURRENCY": 1506,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_STACK_COUNT": 14,
"ITEM_FIELD_DURABILITY": 60,
"ITEM_FIELD_MAXDURABILITY": 61,
"CONTAINER_FIELD_NUM_SLOTS": 64,
"CONTAINER_FIELD_SLOT_1": 66
}

View file

@ -1,96 +1,293 @@
{
"Spell": {
"ID": 0, "Attributes": 5, "IconID": 117,
"Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1
"ID": 0,
"Attributes": 5,
"AttributesEx": 6,
"IconID": 117,
"Name": 120,
"Tooltip": 147,
"Rank": 129,
"SchoolEnum": 1,
"CastingTimeIndex": 15,
"PowerType": 28,
"ManaCost": 29,
"RangeIndex": 33,
"DispelType": 4
},
"SpellRange": {
"MaxRange": 2
},
"ItemDisplayInfo": {
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
"ID": 0,
"LeftModel": 1,
"LeftModelTexture": 3,
"InventoryIcon": 5,
"GeosetGroup1": 7,
"GeosetGroup3": 9,
"TextureArmUpper": 14,
"TextureArmLower": 15,
"TextureHand": 16,
"TextureTorsoUpper": 17,
"TextureTorsoLower": 18,
"TextureLegUpper": 19,
"TextureLegLower": 20,
"TextureFoot": 21
},
"CharSections": {
"RaceID": 1, "SexID": 2, "BaseSection": 3,
"VariationIndex": 4, "ColorIndex": 5,
"Texture1": 6, "Texture2": 7, "Texture3": 8,
"RaceID": 1,
"SexID": 2,
"BaseSection": 3,
"VariationIndex": 4,
"ColorIndex": 5,
"Texture1": 6,
"Texture2": 7,
"Texture3": 8,
"Flags": 9
},
"SpellIcon": { "ID": 0, "Path": 1 },
"SpellIcon": {
"ID": 0,
"Path": 1
},
"FactionTemplate": {
"ID": 0, "Faction": 1, "FactionGroup": 3,
"FriendGroup": 4, "EnemyGroup": 5,
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
"ID": 0,
"Faction": 1,
"FactionGroup": 3,
"FriendGroup": 4,
"EnemyGroup": 5,
"Enemy0": 6,
"Enemy1": 7,
"Enemy2": 8,
"Enemy3": 9
},
"Faction": {
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
"ReputationBase0": 10, "ReputationBase1": 11,
"ReputationBase2": 12, "ReputationBase3": 13
"ID": 0,
"ReputationRaceMask0": 2,
"ReputationRaceMask1": 3,
"ReputationRaceMask2": 4,
"ReputationRaceMask3": 5,
"ReputationBase0": 10,
"ReputationBase1": 11,
"ReputationBase2": 12,
"ReputationBase3": 13
},
"AreaTable": {
"ID": 0,
"MapID": 1,
"ParentAreaNum": 2,
"ExploreFlag": 3
},
"AreaTable": { "ID": 0, "ExploreFlag": 3 },
"CreatureDisplayInfoExtra": {
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
"EquipDisplay9": 17, "BakeName": 18
"ID": 0,
"RaceID": 1,
"SexID": 2,
"SkinID": 3,
"FaceID": 4,
"HairStyleID": 5,
"HairColorID": 6,
"FacialHairID": 7,
"EquipDisplay0": 8,
"EquipDisplay1": 9,
"EquipDisplay2": 10,
"EquipDisplay3": 11,
"EquipDisplay4": 12,
"EquipDisplay5": 13,
"EquipDisplay6": 14,
"EquipDisplay7": 15,
"EquipDisplay8": 16,
"EquipDisplay9": 17,
"BakeName": 18
},
"CreatureDisplayInfo": {
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
"Skin1": 6, "Skin2": 7, "Skin3": 8
"ID": 0,
"ModelID": 1,
"ExtraDisplayId": 3,
"Skin1": 6,
"Skin2": 7,
"Skin3": 8
},
"TaxiNodes": {
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5
"ID": 0,
"MapID": 1,
"X": 2,
"Y": 3,
"Z": 4,
"Name": 5
},
"TaxiPath": {
"ID": 0,
"FromNode": 1,
"ToNode": 2,
"Cost": 3
},
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
"TaxiPathNode": {
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
"X": 4, "Y": 5, "Z": 6
"ID": 0,
"PathID": 1,
"NodeIndex": 2,
"MapID": 3,
"X": 4,
"Y": 5,
"Z": 6
},
"TalentTab": {
"ID": 0, "Name": 1, "ClassMask": 12,
"OrderIndex": 14, "BackgroundFile": 15
"ID": 0,
"Name": 1,
"ClassMask": 12,
"OrderIndex": 14,
"BackgroundFile": 15
},
"Talent": {
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
"ID": 0,
"TabID": 1,
"Row": 2,
"Column": 3,
"RankSpell0": 4,
"PrereqTalent0": 9,
"PrereqRank0": 12
},
"SkillLineAbility": {
"SkillLineID": 1,
"SpellID": 2
},
"SkillLine": {
"ID": 0,
"Category": 1,
"Name": 3
},
"Map": {
"ID": 0,
"InternalName": 1
},
"CreatureModelData": {
"ID": 0,
"ModelPath": 2
},
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
"Map": { "ID": 0, "InternalName": 1 },
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
"CharHairGeosets": {
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
"RaceID": 1,
"SexID": 2,
"Variation": 3,
"GeosetID": 4
},
"CharacterFacialHairStyles": {
"RaceID": 0, "SexID": 1, "Variation": 2,
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
"RaceID": 0,
"SexID": 1,
"Variation": 2,
"Geoset100": 3,
"Geoset300": 4,
"Geoset200": 5
},
"GameObjectDisplayInfo": {
"ID": 0,
"ModelName": 1
},
"Emotes": {
"ID": 0,
"AnimID": 2
},
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
"Emotes": { "ID": 0, "AnimID": 2 },
"EmotesText": {
"ID": 0, "Command": 1, "EmoteRef": 2,
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
"ID": 0,
"Command": 1,
"EmoteRef": 2,
"OthersTargetTextID": 3,
"SenderTargetTextID": 5,
"OthersNoTargetTextID": 7,
"SenderNoTargetTextID": 9
},
"EmotesTextData": {
"ID": 0,
"Text": 1
},
"EmotesTextData": { "ID": 0, "Text": 1 },
"Light": {
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
"ID": 0,
"MapID": 1,
"X": 2,
"Z": 3,
"Y": 4,
"InnerRadius": 5,
"OuterRadius": 6,
"LightParamsID": 7,
"LightParamsIDRain": 8,
"LightParamsIDUnderwater": 9
},
"LightParams": {
"LightParamsID": 0
},
"LightParams": { "LightParamsID": 0 },
"LightIntBand": {
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
"BlockIndex": 1,
"NumKeyframes": 2,
"TimeKey0": 3,
"Value0": 19
},
"LightFloatBand": {
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
"BlockIndex": 1,
"NumKeyframes": 2,
"TimeKey0": 3,
"Value0": 19
},
"WorldMapArea": {
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
"DisplayMapID": 8, "ParentWorldMapID": 10
"ID": 0,
"MapID": 1,
"AreaID": 2,
"AreaName": 3,
"LocLeft": 4,
"LocRight": 5,
"LocTop": 6,
"LocBottom": 7,
"DisplayMapID": 8,
"ParentWorldMapID": 10
},
"SpellItemEnchantment": {
"ID": 0,
"Name": 8
},
"ItemSet": {
"ID": 0,
"Name": 1,
"Item0": 10,
"Item1": 11,
"Item2": 12,
"Item3": 13,
"Item4": 14,
"Item5": 15,
"Item6": 16,
"Item7": 17,
"Item8": 18,
"Item9": 19,
"Spell0": 20,
"Spell1": 21,
"Spell2": 22,
"Spell3": 23,
"Spell4": 24,
"Spell5": 25,
"Spell6": 26,
"Spell7": 27,
"Spell8": 28,
"Spell9": 29,
"Threshold0": 30,
"Threshold1": 31,
"Threshold2": 32,
"Threshold3": 33,
"Threshold4": 34,
"Threshold5": 35,
"Threshold6": 36,
"Threshold7": 37,
"Threshold8": 38,
"Threshold9": 39
},
"SpellVisual": {
"ID": 0,
"CastKit": 2,
"ImpactKit": 3,
"MissileModel": 8
},
"SpellVisualKit": {
"ID": 0,
"BaseEffect": 5,
"SpecialEffect0": 11,
"SpecialEffect1": 12,
"SpecialEffect2": 13
},
"SpellVisualEffectName": {
"ID": 0,
"FilePath": 2
}
}

View file

@ -1,300 +1,6 @@
{
"CMSG_PING": "0x1DC",
"CMSG_AUTH_SESSION": "0x1ED",
"CMSG_CHAR_CREATE": "0x036",
"CMSG_CHAR_ENUM": "0x037",
"CMSG_CHAR_DELETE": "0x038",
"CMSG_PLAYER_LOGIN": "0x03D",
"MSG_MOVE_START_FORWARD": "0x0B5",
"MSG_MOVE_START_BACKWARD": "0x0B6",
"MSG_MOVE_STOP": "0x0B7",
"MSG_MOVE_START_STRAFE_LEFT": "0x0B8",
"MSG_MOVE_START_STRAFE_RIGHT": "0x0B9",
"MSG_MOVE_STOP_STRAFE": "0x0BA",
"MSG_MOVE_JUMP": "0x0BB",
"MSG_MOVE_START_TURN_LEFT": "0x0BC",
"MSG_MOVE_START_TURN_RIGHT": "0x0BD",
"MSG_MOVE_STOP_TURN": "0x0BE",
"MSG_MOVE_SET_FACING": "0x0DA",
"MSG_MOVE_FALL_LAND": "0x0C9",
"MSG_MOVE_START_SWIM": "0x0CA",
"MSG_MOVE_STOP_SWIM": "0x0CB",
"MSG_MOVE_HEARTBEAT": "0x0EE",
"SMSG_AUTH_CHALLENGE": "0x1EC",
"SMSG_AUTH_RESPONSE": "0x1EE",
"SMSG_CHAR_CREATE": "0x03A",
"SMSG_CHAR_ENUM": "0x03B",
"SMSG_CHAR_DELETE": "0x03C",
"SMSG_CHARACTER_LOGIN_FAILED": "0x041",
"SMSG_PONG": "0x1DD",
"SMSG_LOGIN_VERIFY_WORLD": "0x236",
"SMSG_INIT_WORLD_STATES": "0x2C2",
"SMSG_LOGIN_SETTIMESPEED": "0x042",
"SMSG_TUTORIAL_FLAGS": "0x0FD",
"SMSG_INITIALIZE_FACTIONS": "0x122",
"SMSG_WARDEN_DATA": "0x2E6",
"CMSG_WARDEN_DATA": "0x2E7",
"SMSG_NOTIFICATION": "0x1CB",
"SMSG_ACCOUNT_DATA_TIMES": "0x209",
"SMSG_UPDATE_OBJECT": "0x0A9",
"SMSG_COMPRESSED_UPDATE_OBJECT": "0x1F6",
"SMSG_PARTYKILLLOG": "0x1F5",
"SMSG_MONSTER_MOVE_TRANSPORT": "0x2AE",
"SMSG_SPLINE_MOVE_SET_WALK_MODE": "0x30E",
"SMSG_SPLINE_MOVE_SET_RUN_MODE": "0x30D",
"SMSG_SPLINE_SET_RUN_SPEED": "0x2FE",
"SMSG_SPLINE_SET_RUN_BACK_SPEED": "0x2FF",
"SMSG_SPLINE_SET_SWIM_SPEED": "0x300",
"SMSG_DESTROY_OBJECT": "0x0AA",
"CMSG_MESSAGECHAT": "0x095",
"SMSG_MESSAGECHAT": "0x096",
"CMSG_WHO": "0x062",
"SMSG_WHO": "0x063",
"CMSG_PLAYED_TIME": "0x1CC",
"SMSG_PLAYED_TIME": "0x1CD",
"CMSG_QUERY_TIME": "0x1CE",
"SMSG_QUERY_TIME_RESPONSE": "0x1CF",
"SMSG_FRIEND_STATUS": "0x068",
"SMSG_CONTACT_LIST": "0x067",
"CMSG_ADD_FRIEND": "0x069",
"CMSG_DEL_FRIEND": "0x06A",
"CMSG_ADD_IGNORE": "0x06C",
"CMSG_DEL_IGNORE": "0x06D",
"CMSG_PLAYER_LOGOUT": "0x04A",
"CMSG_LOGOUT_REQUEST": "0x04B",
"CMSG_LOGOUT_CANCEL": "0x04E",
"SMSG_LOGOUT_RESPONSE": "0x04C",
"SMSG_LOGOUT_COMPLETE": "0x04D",
"CMSG_STANDSTATECHANGE": "0x101",
"CMSG_SHOWING_HELM": "0x2B9",
"CMSG_SHOWING_CLOAK": "0x2BA",
"CMSG_TOGGLE_PVP": "0x253",
"CMSG_GUILD_INVITE": "0x082",
"CMSG_GUILD_ACCEPT": "0x084",
"CMSG_GUILD_DECLINE": "0x085",
"CMSG_GUILD_INFO": "0x087",
"CMSG_GUILD_ROSTER": "0x089",
"CMSG_GUILD_PROMOTE": "0x08B",
"CMSG_GUILD_DEMOTE": "0x08C",
"CMSG_GUILD_LEAVE": "0x08D",
"CMSG_GUILD_MOTD": "0x091",
"SMSG_GUILD_INFO": "0x088",
"SMSG_GUILD_ROSTER": "0x08A",
"CMSG_GUILD_QUERY": "0x054",
"SMSG_GUILD_QUERY_RESPONSE": "0x055",
"SMSG_GUILD_INVITE": "0x083",
"CMSG_GUILD_REMOVE": "0x08E",
"SMSG_GUILD_EVENT": "0x092",
"SMSG_GUILD_COMMAND_RESULT": "0x093",
"MSG_RAID_READY_CHECK": "0x322",
"SMSG_ITEM_PUSH_RESULT": "0x166",
"CMSG_DUEL_ACCEPTED": "0x16C",
"CMSG_DUEL_CANCELLED": "0x16D",
"SMSG_DUEL_REQUESTED": "0x167",
"CMSG_INITIATE_TRADE": "0x116",
"MSG_RANDOM_ROLL": "0x1FB",
"CMSG_SET_SELECTION": "0x13D",
"CMSG_NAME_QUERY": "0x050",
"SMSG_NAME_QUERY_RESPONSE": "0x051",
"CMSG_CREATURE_QUERY": "0x060",
"SMSG_CREATURE_QUERY_RESPONSE": "0x061",
"CMSG_GAMEOBJECT_QUERY": "0x05E",
"SMSG_GAMEOBJECT_QUERY_RESPONSE": "0x05F",
"CMSG_SET_ACTIVE_MOVER": "0x26A",
"CMSG_BINDER_ACTIVATE": "0x1B5",
"SMSG_LOG_XPGAIN": "0x1D0",
"_NOTE_MONSTER_MOVE": "These look swapped vs vanilla (0x0DD/0x2FB) but may be intentional Turtle WoW changes. Check if NPC movement breaks.",
"SMSG_MONSTER_MOVE": "0x2FB",
"SMSG_COMPRESSED_MOVES": "0x06B",
"CMSG_ATTACKSWING": "0x141",
"CMSG_ATTACKSTOP": "0x142",
"SMSG_ATTACKSTART": "0x143",
"SMSG_ATTACKSTOP": "0x144",
"SMSG_ATTACKERSTATEUPDATE": "0x14A",
"SMSG_AI_REACTION": "0x13C",
"SMSG_SPELLNONMELEEDAMAGELOG": "0x250",
"SMSG_PLAY_SPELL_VISUAL": "0x1F3",
"SMSG_SPELLHEALLOG": "0x150",
"SMSG_SPELLENERGIZELOG": "0x151",
"SMSG_PERIODICAURALOG": "0x24E",
"SMSG_ENVIRONMENTAL_DAMAGE_LOG": "0x1FC",
"CMSG_CAST_SPELL": "0x12E",
"CMSG_CANCEL_CAST": "0x12F",
"CMSG_CANCEL_AURA": "0x136",
"SMSG_CAST_FAILED": "0x130",
"SMSG_SPELL_START": "0x131",
"SMSG_SPELL_GO": "0x132",
"SMSG_SPELL_FAILURE": "0x133",
"SMSG_SPELL_COOLDOWN": "0x134",
"SMSG_COOLDOWN_EVENT": "0x135",
"SMSG_EQUIPMENT_SET_SAVED": "0x137",
"SMSG_INITIAL_SPELLS": "0x12A",
"SMSG_LEARNED_SPELL": "0x12B",
"SMSG_SUPERCEDED_SPELL": "0x12C",
"SMSG_REMOVED_SPELL": "0x203",
"SMSG_SPELL_DELAYED": "0x1E2",
"SMSG_SET_FLAT_SPELL_MODIFIER": "0x266",
"SMSG_SET_PCT_SPELL_MODIFIER": "0x267",
"CMSG_LEARN_TALENT": "0x251",
"MSG_TALENT_WIPE_CONFIRM": "0x2AA",
"CMSG_GROUP_INVITE": "0x06E",
"SMSG_GROUP_INVITE": "0x06F",
"CMSG_GROUP_ACCEPT": "0x072",
"CMSG_GROUP_DECLINE": "0x073",
"SMSG_GROUP_DECLINE": "0x074",
"CMSG_GROUP_UNINVITE_GUID": "0x076",
"SMSG_GROUP_UNINVITE": "0x077",
"CMSG_GROUP_SET_LEADER": "0x078",
"SMSG_GROUP_SET_LEADER": "0x079",
"CMSG_GROUP_DISBAND": "0x07B",
"SMSG_GROUP_LIST": "0x07D",
"SMSG_PARTY_COMMAND_RESULT": "0x07F",
"MSG_RAID_TARGET_UPDATE": "0x321",
"CMSG_REQUEST_RAID_INFO": "0x2CD",
"SMSG_RAID_INSTANCE_INFO": "0x2CC",
"CMSG_AUTOSTORE_LOOT_ITEM": "0x108",
"CMSG_LOOT": "0x15D",
"CMSG_LOOT_MONEY": "0x15E",
"CMSG_LOOT_RELEASE": "0x15F",
"SMSG_LOOT_RESPONSE": "0x160",
"SMSG_LOOT_RELEASE_RESPONSE": "0x161",
"SMSG_LOOT_REMOVED": "0x162",
"SMSG_LOOT_MONEY_NOTIFY": "0x163",
"SMSG_LOOT_CLEAR_MONEY": "0x165",
"CMSG_ACTIVATETAXI": "0x1AD",
"CMSG_GOSSIP_HELLO": "0x17B",
"CMSG_GOSSIP_SELECT_OPTION": "0x17C",
"SMSG_GOSSIP_MESSAGE": "0x17D",
"SMSG_GOSSIP_COMPLETE": "0x17E",
"SMSG_NPC_TEXT_UPDATE": "0x180",
"CMSG_GAMEOBJ_USE": "0x0B1",
"CMSG_QUESTGIVER_STATUS_QUERY": "0x182",
"SMSG_QUESTGIVER_STATUS": "0x183",
"CMSG_QUESTGIVER_HELLO": "0x184",
"SMSG_QUESTGIVER_QUEST_LIST": "0x185",
"CMSG_QUESTGIVER_QUERY_QUEST": "0x186",
"SMSG_QUESTGIVER_QUEST_DETAILS": "0x188",
"CMSG_QUESTGIVER_ACCEPT_QUEST": "0x189",
"CMSG_QUESTGIVER_COMPLETE_QUEST": "0x18A",
"SMSG_QUESTGIVER_REQUEST_ITEMS": "0x18B",
"CMSG_QUESTGIVER_REQUEST_REWARD": "0x18C",
"SMSG_QUESTGIVER_OFFER_REWARD": "0x18D",
"CMSG_QUESTGIVER_CHOOSE_REWARD": "0x18E",
"SMSG_QUESTGIVER_QUEST_INVALID": "0x18F",
"SMSG_QUESTGIVER_QUEST_COMPLETE": "0x191",
"CMSG_QUESTLOG_REMOVE_QUEST": "0x194",
"SMSG_QUESTUPDATE_ADD_KILL": "0x199",
"SMSG_QUESTUPDATE_COMPLETE": "0x198",
"SMSG_QUEST_FORCE_REMOVE": "0x21E",
"CMSG_QUEST_QUERY": "0x05C",
"SMSG_QUEST_QUERY_RESPONSE": "0x05D",
"SMSG_QUESTLOG_FULL": "0x195",
"CMSG_LIST_INVENTORY": "0x19E",
"SMSG_LIST_INVENTORY": "0x19F",
"CMSG_SELL_ITEM": "0x1A0",
"SMSG_SELL_ITEM": "0x1A1",
"CMSG_BUY_ITEM": "0x1A2",
"CMSG_BUYBACK_ITEM": "0x1A6",
"SMSG_BUY_FAILED": "0x1A5",
"CMSG_TRAINER_LIST": "0x1B0",
"SMSG_TRAINER_LIST": "0x1B1",
"CMSG_TRAINER_BUY_SPELL": "0x1B2",
"SMSG_TRAINER_BUY_FAILED": "0x1B4",
"CMSG_ITEM_QUERY_SINGLE": "0x056",
"SMSG_ITEM_QUERY_SINGLE_RESPONSE": "0x058",
"CMSG_USE_ITEM": "0x0AB",
"CMSG_AUTOEQUIP_ITEM": "0x10A",
"CMSG_SWAP_ITEM": "0x10C",
"CMSG_SWAP_INV_ITEM": "0x10D",
"SMSG_INVENTORY_CHANGE_FAILURE": "0x112",
"CMSG_INSPECT": "0x114",
"SMSG_INSPECT_RESULTS_UPDATE": "0x115",
"CMSG_REPOP_REQUEST": "0x15A",
"SMSG_RESURRECT_REQUEST": "0x15B",
"CMSG_RESURRECT_RESPONSE": "0x15C",
"CMSG_SPIRIT_HEALER_ACTIVATE": "0x21C",
"SMSG_SPIRIT_HEALER_CONFIRM": "0x222",
"MSG_MOVE_TELEPORT_ACK": "0x0C7",
"SMSG_TRANSFER_PENDING": "0x03F",
"SMSG_NEW_WORLD": "0x03E",
"MSG_MOVE_WORLDPORT_ACK": "0x0DC",
"SMSG_TRANSFER_ABORTED": "0x040",
"SMSG_FORCE_RUN_SPEED_CHANGE": "0x0E2",
"SMSG_CLIENT_CONTROL_UPDATE": "0x159",
"CMSG_FORCE_RUN_SPEED_CHANGE_ACK": "0x0E3",
"SMSG_SHOWTAXINODES": "0x1A9",
"SMSG_ACTIVATETAXIREPLY": "0x1AE",
"SMSG_NEW_TAXI_PATH": "0x1AF",
"CMSG_ACTIVATETAXIEXPRESS": "0x312",
"CMSG_TAXINODE_STATUS_QUERY": "0x1AA",
"SMSG_TAXINODE_STATUS": "0x1AB",
"SMSG_TRAINER_BUY_SUCCEEDED": "0x1B3",
"SMSG_BINDPOINTUPDATE": "0x155",
"SMSG_SET_PROFICIENCY": "0x127",
"SMSG_ACTION_BUTTONS": "0x129",
"SMSG_LEVELUP_INFO": "0x1D4",
"SMSG_PLAY_SOUND": "0x2D2",
"CMSG_UPDATE_ACCOUNT_DATA": "0x20B",
"CMSG_BATTLEFIELD_LIST": "0x23C",
"SMSG_BATTLEFIELD_LIST": "0x23D",
"CMSG_BATTLEFIELD_JOIN": "0x23E",
"CMSG_BATTLEFIELD_STATUS": "0x2D3",
"SMSG_BATTLEFIELD_STATUS": "0x2D4",
"CMSG_BATTLEFIELD_PORT": "0x2D5",
"CMSG_BATTLEMASTER_HELLO": "0x2D7",
"MSG_PVP_LOG_DATA": "0x2E0",
"CMSG_LEAVE_BATTLEFIELD": "0x2E1",
"SMSG_GROUP_JOINED_BATTLEGROUND": "0x2E8",
"MSG_BATTLEGROUND_PLAYER_POSITIONS": "0x2E9",
"SMSG_BATTLEGROUND_PLAYER_JOINED": "0x2EC",
"SMSG_BATTLEGROUND_PLAYER_LEFT": "0x2ED",
"CMSG_BATTLEMASTER_JOIN": "0x2EE",
"CMSG_EMOTE": "0x102",
"SMSG_EMOTE": "0x103",
"CMSG_TEXT_EMOTE": "0x104",
"SMSG_TEXT_EMOTE": "0x105",
"CMSG_JOIN_CHANNEL": "0x097",
"CMSG_LEAVE_CHANNEL": "0x098",
"SMSG_CHANNEL_NOTIFY": "0x099",
"CMSG_CHANNEL_LIST": "0x09A",
"SMSG_CHANNEL_LIST": "0x09B",
"SMSG_INSPECT_TALENT": "0x3F4",
"SMSG_SHOW_MAILBOX": "0x297",
"CMSG_GET_MAIL_LIST": "0x23A",
"SMSG_MAIL_LIST_RESULT": "0x23B",
"CMSG_SEND_MAIL": "0x238",
"SMSG_SEND_MAIL_RESULT": "0x239",
"CMSG_MAIL_TAKE_MONEY": "0x245",
"CMSG_MAIL_TAKE_ITEM": "0x246",
"CMSG_MAIL_DELETE": "0x249",
"CMSG_MAIL_MARK_AS_READ": "0x247",
"SMSG_RECEIVED_MAIL": "0x285",
"MSG_QUERY_NEXT_MAIL_TIME": "0x284",
"CMSG_BANKER_ACTIVATE": "0x1B7",
"SMSG_SHOW_BANK": "0x1B8",
"CMSG_BUY_BANK_SLOT": "0x1B9",
"SMSG_BUY_BANK_SLOT_RESULT": "0x1BA",
"CMSG_AUTOSTORE_BANK_ITEM": "0x282",
"CMSG_AUTOBANK_ITEM": "0x283",
"MSG_AUCTION_HELLO": "0x255",
"CMSG_AUCTION_SELL_ITEM": "0x256",
"CMSG_AUCTION_REMOVE_ITEM": "0x257",
"CMSG_AUCTION_LIST_ITEMS": "0x258",
"CMSG_AUCTION_LIST_OWNER_ITEMS": "0x259",
"CMSG_AUCTION_PLACE_BID": "0x25A",
"SMSG_AUCTION_COMMAND_RESULT": "0x25B",
"SMSG_AUCTION_LIST_RESULT": "0x25C",
"SMSG_AUCTION_OWNER_LIST_RESULT": "0x25D",
"SMSG_AUCTION_OWNER_NOTIFICATION": "0x25E",
"SMSG_AUCTION_BIDDER_NOTIFICATION": "0x260",
"CMSG_AUCTION_LIST_BIDDER_ITEMS": "0x264",
"SMSG_AUCTION_BIDDER_LIST_RESULT": "0x265",
"MSG_MOVE_TIME_SKIPPED": "0x319",
"SMSG_CANCEL_AUTO_REPEAT": "0x29C",
"SMSG_WEATHER": "0x2F4",
"SMSG_QUESTUPDATE_ADD_ITEM": "0x19A",
"CMSG_GUILD_DISBAND": "0x08F",
"CMSG_GUILD_LEADER": "0x090",
"CMSG_GUILD_SET_PUBLIC_NOTE": "0x234",
"CMSG_GUILD_SET_OFFICER_NOTE": "0x235"
"_extends": "../classic/opcodes.json",
"_remove": [
"MSG_SET_DUNGEON_DIFFICULTY"
]
}

View file

@ -1,5 +1,6 @@
{
"OBJECT_FIELD_ENTRY": 3,
"OBJECT_FIELD_SCALE_X": 4,
"UNIT_FIELD_TARGET_LO": 16,
"UNIT_FIELD_TARGET_HI": 17,
"UNIT_FIELD_BYTES_0": 36,
@ -13,15 +14,22 @@
"UNIT_FIELD_DISPLAYID": 131,
"UNIT_FIELD_MOUNTDISPLAYID": 133,
"UNIT_FIELD_AURAS": 50,
"UNIT_FIELD_AURAFLAGS": 98,
"UNIT_NPC_FLAGS": 147,
"UNIT_DYNAMIC_FLAGS": 143,
"UNIT_FIELD_RESISTANCES": 154,
"UNIT_FIELD_STAT0": 138,
"UNIT_FIELD_STAT1": 139,
"UNIT_FIELD_STAT2": 140,
"UNIT_FIELD_STAT3": 141,
"UNIT_FIELD_STAT4": 142,
"UNIT_END": 188,
"PLAYER_FLAGS": 190,
"PLAYER_BYTES": 191,
"PLAYER_BYTES_2": 192,
"PLAYER_XP": 716,
"PLAYER_NEXT_LEVEL_XP": 717,
"PLAYER_REST_STATE_EXPERIENCE": 1175,
"PLAYER_FIELD_COINAGE": 1176,
"PLAYER_QUEST_LOG_START": 198,
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
@ -33,6 +41,8 @@
"PLAYER_END": 1282,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_STACK_COUNT": 14,
"ITEM_FIELD_DURABILITY": 48,
"ITEM_FIELD_MAXDURABILITY": 49,
"CONTAINER_FIELD_NUM_SLOTS": 48,
"CONTAINER_FIELD_SLOT_1": 50
}
}

View file

@ -1,99 +1,319 @@
{
"Spell": {
"ID": 0, "Attributes": 4, "IconID": 133,
"Name": 136, "Tooltip": 139, "Rank": 153, "SchoolMask": 225
"ID": 0,
"Attributes": 4,
"AttributesEx": 5,
"IconID": 133,
"Name": 136,
"Tooltip": 139,
"Rank": 153,
"SchoolMask": 225,
"PowerType": 14,
"ManaCost": 39,
"CastingTimeIndex": 47,
"RangeIndex": 49,
"DispelType": 2
},
"SpellRange": {
"MaxRange": 4
},
"ItemDisplayInfo": {
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
"ID": 0,
"LeftModel": 1,
"LeftModelTexture": 3,
"InventoryIcon": 5,
"GeosetGroup1": 7,
"GeosetGroup3": 9,
"TextureArmUpper": 14,
"TextureArmLower": 15,
"TextureHand": 16,
"TextureTorsoUpper": 17,
"TextureTorsoLower": 18,
"TextureLegUpper": 19,
"TextureLegLower": 20,
"TextureFoot": 21
},
"CharSections": {
"RaceID": 1, "SexID": 2, "BaseSection": 3,
"VariationIndex": 4, "ColorIndex": 5,
"Texture1": 6, "Texture2": 7, "Texture3": 8,
"RaceID": 1,
"SexID": 2,
"BaseSection": 3,
"VariationIndex": 4,
"ColorIndex": 5,
"Texture1": 6,
"Texture2": 7,
"Texture3": 8,
"Flags": 9
},
"SpellIcon": { "ID": 0, "Path": 1 },
"SpellIcon": {
"ID": 0,
"Path": 1
},
"FactionTemplate": {
"ID": 0, "Faction": 1, "FactionGroup": 3,
"FriendGroup": 4, "EnemyGroup": 5,
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
"ID": 0,
"Faction": 1,
"FactionGroup": 3,
"FriendGroup": 4,
"EnemyGroup": 5,
"Enemy0": 6,
"Enemy1": 7,
"Enemy2": 8,
"Enemy3": 9
},
"Faction": {
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
"ReputationBase0": 10, "ReputationBase1": 11,
"ReputationBase2": 12, "ReputationBase3": 13
"ID": 0,
"ReputationRaceMask0": 2,
"ReputationRaceMask1": 3,
"ReputationRaceMask2": 4,
"ReputationRaceMask3": 5,
"ReputationBase0": 10,
"ReputationBase1": 11,
"ReputationBase2": 12,
"ReputationBase3": 13
},
"CharTitles": {
"ID": 0,
"Title": 2,
"TitleBit": 36
},
"Achievement": {
"ID": 0,
"Title": 4,
"Description": 21,
"Points": 39
},
"AchievementCriteria": {
"ID": 0,
"AchievementID": 1,
"Quantity": 4,
"Description": 9
},
"AreaTable": {
"ID": 0,
"MapID": 1,
"ParentAreaNum": 2,
"ExploreFlag": 3
},
"Achievement": { "ID": 0, "Title": 4, "Description": 21 },
"AreaTable": { "ID": 0, "ExploreFlag": 3 },
"CreatureDisplayInfoExtra": {
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
"EquipDisplay9": 17, "EquipDisplay10": 18, "BakeName": 20
"ID": 0,
"RaceID": 1,
"SexID": 2,
"SkinID": 3,
"FaceID": 4,
"HairStyleID": 5,
"HairColorID": 6,
"FacialHairID": 7,
"EquipDisplay0": 8,
"EquipDisplay1": 9,
"EquipDisplay2": 10,
"EquipDisplay3": 11,
"EquipDisplay4": 12,
"EquipDisplay5": 13,
"EquipDisplay6": 14,
"EquipDisplay7": 15,
"EquipDisplay8": 16,
"EquipDisplay9": 17,
"EquipDisplay10": 18,
"BakeName": 20
},
"CreatureDisplayInfo": {
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
"Skin1": 6, "Skin2": 7, "Skin3": 8
"ID": 0,
"ModelID": 1,
"ExtraDisplayId": 3,
"Skin1": 6,
"Skin2": 7,
"Skin3": 8
},
"TaxiNodes": {
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5,
"MountDisplayIdAllianceFallback": 20, "MountDisplayIdHordeFallback": 21,
"MountDisplayIdAlliance": 22, "MountDisplayIdHorde": 23
"ID": 0,
"MapID": 1,
"X": 2,
"Y": 3,
"Z": 4,
"Name": 5,
"MountDisplayIdAllianceFallback": 20,
"MountDisplayIdHordeFallback": 21,
"MountDisplayIdAlliance": 22,
"MountDisplayIdHorde": 23
},
"TaxiPath": {
"ID": 0,
"FromNode": 1,
"ToNode": 2,
"Cost": 3
},
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
"TaxiPathNode": {
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
"X": 4, "Y": 5, "Z": 6
"ID": 0,
"PathID": 1,
"NodeIndex": 2,
"MapID": 3,
"X": 4,
"Y": 5,
"Z": 6
},
"TalentTab": {
"ID": 0, "Name": 1, "ClassMask": 20,
"OrderIndex": 22, "BackgroundFile": 23
"ID": 0,
"Name": 1,
"ClassMask": 20,
"OrderIndex": 22,
"BackgroundFile": 23
},
"Talent": {
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
"ID": 0,
"TabID": 1,
"Row": 2,
"Column": 3,
"RankSpell0": 4,
"PrereqTalent0": 9,
"PrereqRank0": 12
},
"SkillLineAbility": {
"SkillLineID": 1,
"SpellID": 2
},
"SkillLine": {
"ID": 0,
"Category": 1,
"Name": 3
},
"Map": {
"ID": 0,
"InternalName": 1
},
"CreatureModelData": {
"ID": 0,
"ModelPath": 2
},
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
"Map": { "ID": 0, "InternalName": 1 },
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
"CharHairGeosets": {
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
"RaceID": 1,
"SexID": 2,
"Variation": 3,
"GeosetID": 4
},
"CharacterFacialHairStyles": {
"RaceID": 0, "SexID": 1, "Variation": 2,
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
"RaceID": 0,
"SexID": 1,
"Variation": 2,
"Geoset100": 3,
"Geoset300": 4,
"Geoset200": 5
},
"GameObjectDisplayInfo": {
"ID": 0,
"ModelName": 1
},
"Emotes": {
"ID": 0,
"AnimID": 2
},
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
"Emotes": { "ID": 0, "AnimID": 2 },
"EmotesText": {
"ID": 0, "Command": 1, "EmoteRef": 2,
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
"ID": 0,
"Command": 1,
"EmoteRef": 2,
"OthersTargetTextID": 3,
"SenderTargetTextID": 5,
"OthersNoTargetTextID": 7,
"SenderNoTargetTextID": 9
},
"EmotesTextData": {
"ID": 0,
"Text": 1
},
"EmotesTextData": { "ID": 0, "Text": 1 },
"Light": {
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
"ID": 0,
"MapID": 1,
"X": 2,
"Z": 3,
"Y": 4,
"InnerRadius": 5,
"OuterRadius": 6,
"LightParamsID": 7,
"LightParamsIDRain": 8,
"LightParamsIDUnderwater": 9
},
"LightParams": {
"LightParamsID": 0
},
"LightParams": { "LightParamsID": 0 },
"LightIntBand": {
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
"BlockIndex": 1,
"NumKeyframes": 2,
"TimeKey0": 3,
"Value0": 19
},
"LightFloatBand": {
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
"BlockIndex": 1,
"NumKeyframes": 2,
"TimeKey0": 3,
"Value0": 19
},
"WorldMapArea": {
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
"DisplayMapID": 8, "ParentWorldMapID": 10
"ID": 0,
"MapID": 1,
"AreaID": 2,
"AreaName": 3,
"LocLeft": 4,
"LocRight": 5,
"LocTop": 6,
"LocBottom": 7,
"DisplayMapID": 8,
"ParentWorldMapID": 10
},
"SpellItemEnchantment": {
"ID": 0,
"Name": 8
},
"ItemSet": {
"ID": 0,
"Name": 1,
"Item0": 18,
"Item1": 19,
"Item2": 20,
"Item3": 21,
"Item4": 22,
"Item5": 23,
"Item6": 24,
"Item7": 25,
"Item8": 26,
"Item9": 27,
"Spell0": 28,
"Spell1": 29,
"Spell2": 30,
"Spell3": 31,
"Spell4": 32,
"Spell5": 33,
"Spell6": 34,
"Spell7": 35,
"Spell8": 36,
"Spell9": 37,
"Threshold0": 38,
"Threshold1": 39,
"Threshold2": 40,
"Threshold3": 41,
"Threshold4": 42,
"Threshold5": 43,
"Threshold6": 44,
"Threshold7": 45,
"Threshold8": 46,
"Threshold9": 47
},
"LFGDungeons": {
"ID": 0,
"Name": 1
},
"SpellVisual": {
"ID": 0,
"CastKit": 2,
"ImpactKit": 3,
"MissileModel": 8
},
"SpellVisualKit": {
"ID": 0,
"BaseEffect": 5,
"SpecialEffect0": 11,
"SpecialEffect1": 12,
"SpecialEffect2": 13
},
"SpellVisualEffectName": {
"ID": 0,
"FilePath": 2
}
}

View file

@ -1,5 +1,6 @@
{
"OBJECT_FIELD_ENTRY": 3,
"OBJECT_FIELD_SCALE_X": 4,
"UNIT_FIELD_TARGET_LO": 6,
"UNIT_FIELD_TARGET_HI": 7,
"UNIT_FIELD_BYTES_0": 23,
@ -16,12 +17,20 @@
"UNIT_NPC_FLAGS": 82,
"UNIT_DYNAMIC_FLAGS": 147,
"UNIT_FIELD_RESISTANCES": 99,
"UNIT_FIELD_STAT0": 84,
"UNIT_FIELD_STAT1": 85,
"UNIT_FIELD_STAT2": 86,
"UNIT_FIELD_STAT3": 87,
"UNIT_FIELD_STAT4": 88,
"UNIT_END": 148,
"UNIT_FIELD_ATTACK_POWER": 123,
"UNIT_FIELD_RANGED_ATTACK_POWER": 126,
"PLAYER_FLAGS": 150,
"PLAYER_BYTES": 153,
"PLAYER_BYTES_2": 154,
"PLAYER_XP": 634,
"PLAYER_NEXT_LEVEL_XP": 635,
"PLAYER_REST_STATE_EXPERIENCE": 1169,
"PLAYER_FIELD_COINAGE": 1170,
"PLAYER_QUEST_LOG_START": 158,
"PLAYER_FIELD_INV_SLOT_HEAD": 324,
@ -30,8 +39,22 @@
"PLAYER_FIELD_BANKBAG_SLOT_1": 458,
"PLAYER_SKILL_INFO_START": 636,
"PLAYER_EXPLORED_ZONES_START": 1041,
"PLAYER_CHOSEN_TITLE": 1349,
"PLAYER_FIELD_MOD_DAMAGE_DONE_POS": 1171,
"PLAYER_FIELD_MOD_HEALING_DONE_POS": 1192,
"PLAYER_BLOCK_PERCENTAGE": 1024,
"PLAYER_DODGE_PERCENTAGE": 1025,
"PLAYER_PARRY_PERCENTAGE": 1026,
"PLAYER_CRIT_PERCENTAGE": 1029,
"PLAYER_RANGED_CRIT_PERCENTAGE": 1030,
"PLAYER_SPELL_CRIT_PERCENTAGE1": 1032,
"PLAYER_FIELD_COMBAT_RATING_1": 1231,
"PLAYER_FIELD_HONOR_CURRENCY": 1422,
"PLAYER_FIELD_ARENA_CURRENCY": 1423,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_STACK_COUNT": 14,
"ITEM_FIELD_DURABILITY": 60,
"ITEM_FIELD_MAXDURABILITY": 61,
"CONTAINER_FIELD_NUM_SLOTS": 64,
"CONTAINER_FIELD_SLOT_1": 66
}

View file

@ -0,0 +1,36 @@
-- HelloWorld addon — demonstrates the WoWee addon system
-- Initialize saved variables (persisted across sessions)
if not HelloWorldDB then
HelloWorldDB = { loginCount = 0 }
end
HelloWorldDB.loginCount = (HelloWorldDB.loginCount or 0) + 1
-- Create a frame and register for events (standard WoW addon pattern)
local f = CreateFrame("Frame", "HelloWorldFrame")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:RegisterEvent("CHAT_MSG_SAY")
f:SetScript("OnEvent", function(self, event, ...)
if event == "PLAYER_ENTERING_WORLD" then
local name = UnitName("player")
local level = UnitLevel("player")
print("|cff00ff00[HelloWorld]|r Welcome, " .. name .. "! (Level " .. level .. ")")
print("|cff00ff00[HelloWorld]|r Login count: " .. HelloWorldDB.loginCount)
elseif event == "CHAT_MSG_SAY" then
local msg, sender = ...
if msg and sender then
print("|cff00ff00[HelloWorld]|r " .. sender .. " said: " .. msg)
end
end
end)
-- Register a custom slash command
SLASH_HELLOWORLD1 = "/hello"
SLASH_HELLOWORLD2 = "/hw"
SlashCmdList["HELLOWORLD"] = function(args)
print("|cff00ff00[HelloWorld]|r Hello! " .. (args ~= "" and args or "Type /hello <message>"))
print("|cff00ff00[HelloWorld]|r Sessions: " .. HelloWorldDB.loginCount)
end
print("|cff00ff00[HelloWorld]|r Addon loaded. Type /hello to test.")

View file

@ -0,0 +1,5 @@
## Interface: 30300
## Title: Hello World
## Notes: Test addon for the WoWee addon system
## SavedVariables: HelloWorldDB
HelloWorld.lua

View file

@ -41,7 +41,6 @@
"SMSG_SPLINE_MOVE_SET_RUN_BACK_SPEED": "SMSG_SPLINE_SET_RUN_BACK_SPEED",
"SMSG_SPLINE_MOVE_SET_RUN_SPEED": "SMSG_SPLINE_SET_RUN_SPEED",
"SMSG_SPLINE_MOVE_SET_SWIM_SPEED": "SMSG_SPLINE_SET_SWIM_SPEED",
"SMSG_UPDATE_AURA_DURATION": "SMSG_EQUIPMENT_SET_SAVED",
"SMSG_VICTIMSTATEUPDATE_OBSOLETE": "SMSG_BATTLEFIELD_PORT_DENIED"
}
}

126
EXPANSION_GUIDE.md Normal file
View file

@ -0,0 +1,126 @@
# Multi-Expansion Architecture Guide
WoWee supports three World of Warcraft expansions in a unified codebase using an expansion profile system. This guide explains how the multi-expansion support works.
## Supported Expansions
- **Vanilla (Classic) 1.12** - Original World of Warcraft
- **The Burning Crusade (TBC) 2.4.3** - First expansion
- **Wrath of the Lich King (WotLK) 3.3.5a** - Second expansion
## Architecture Overview
The multi-expansion support is built on the **Expansion Profile** system:
1. **ExpansionProfile** (`include/game/expansion_profile.hpp`) - Metadata about each expansion
- Defines protocol version, data paths, asset locations
- Specifies which packet parsers to use
2. **Packet Parsers** - Expansion-specific message handling
- `packet_parsers_classic.cpp` - Vanilla 1.12 message parsing
- `packet_parsers_tbc.cpp` - TBC 2.4.3 message parsing
- `packet_parsers_wotlk.cpp` (default) - WotLK 3.3.5a message parsing
3. **Update Fields** - Expansion-specific entity data layout
- Loaded from `update_fields.json` in expansion data directory
- Defines UNIT_END, OBJECT_END, field indices for stats/health/mana
## How to Use Different Expansions
### At Startup
WoWee auto-detects the expansion based on:
1. Realm list response (protocol version)
2. Server build number
3. Update field count
### Manual Selection
Set environment variable:
```bash
WOWEE_EXPANSION=tbc ./wowee # Force TBC
WOWEE_EXPANSION=classic ./wowee # Force Classic
```
## Key Differences Between Expansions
### Packet Format Differences
#### SMSG_SPELL_COOLDOWN
- **Classic**: 12 bytes per entry (spellId + itemId + cooldown, no flags)
- **TBC/WotLK**: 8 bytes per entry (spellId + cooldown) + flags byte
#### SMSG_ACTION_BUTTONS
- **Classic**: 120 slots, no mode byte
- **TBC**: 132 slots, no mode byte
- **WotLK**: 144 slots + uint8 mode byte
#### SMSG_PARTY_MEMBER_STATS
- **Classic/TBC**: Full uint64 for guid, uint16 health
- **WotLK**: PackedGuid format, uint32 health
### Data Differences
- **Talent trees**: Different spell IDs and tree structure per expansion
- **Items**: Different ItemDisplayInfo entries
- **Spells**: Different base stats, cooldowns
- **Character textures**: Expansion-specific variants for races
## Adding Support for Another Expansion
1. Create new expansion profile entry in `expansion_profile.cpp`
2. Add packet parser file (`packet_parsers_*.cpp`) for message variants
3. Create update_fields.json with correct field layout
4. Test realm connection and character loading
## Code Patterns
### Checking Current Expansion
```cpp
#include "game/expansion_profile.hpp"
// Global helper
bool isClassicLikeExpansion() {
auto profile = ExpansionProfile::getActive();
return profile && (profile->name == "Classic" || profile->name == "Vanilla");
}
// Specific check
if (GameHandler::getInstance().isActiveExpansion("tbc")) {
// TBC-specific code
}
```
### Expansion-Specific Packet Parsing
```cpp
// In packet_parsers_*.cpp, implement expansion-specific logic
bool parseXxxPacket(BitStream& data, ...) {
// Custom logic for this expansion's packet format
}
```
## Common Issues
### "Update fields mismatch" Error
- Ensure `update_fields.json` matches server's field layout
- Check OBJECT_END and UNIT_END values
- Verify field indices for your target expansion
### "Unknown packet" Warnings
- Expansion-specific opcodes may not be registered
- Check packet parser registration in `game_handler.cpp`
- Verify expansion profile is active
### Packet Parsing Failures
- Each expansion has different struct layouts
- Always read data size first, then upfront validate
- Use size capping (e.g., max 100 items in list)
## References
- `include/game/expansion_profile.hpp` - Expansion metadata
- `docs/status.md` - Current feature support by expansion
- `src/game/packet_parsers_*.cpp` - Format-specific parsing logic
- `docs/` directory - Additional protocol documentation

218
GETTING_STARTED.md Normal file
View file

@ -0,0 +1,218 @@
# Getting Started with WoWee
WoWee is a native C++ World of Warcraft client that connects to private servers. This guide walks you through setting up and playing WoWee.
## Prerequisites
- **World of Warcraft Game Data** (Vanilla 1.12, TBC 2.4.3, or WotLK 3.3.5a)
- **A Private Server** (AzerothCore, TrinityCore, Mangos, or Turtle WoW compatible)
- **System Requirements**: Linux, macOS, or Windows with a Vulkan-capable GPU
## Installation
### Step 1: Build WoWee
See [Building](README.md#building) section in README for detailed build instructions.
**Quick start (Linux/macOS)**:
```bash
./build.sh
cd build/bin
./wowee
```
**Quick start (Windows)**:
```powershell
.\build.ps1
cd build\bin
.\wowee.exe
```
### Step 2: Extract Game Data
WoWee needs game assets from your WoW installation:
**Using provided script (Linux/macOS)**:
```bash
./extract_assets.sh /path/to/wow/directory
```
**Using provided script (Windows)**:
```powershell
.\extract_assets.ps1 -WowDirectory "C:\Program Files\World of Warcraft"
```
**Manual extraction**:
1. Install [StormLib](https://github.com/ladislav-zezula/StormLib)
2. Extract to `./Data/`:
```
Data/
├── dbc/ # DBC files
├── map/ # World map data
├── adt/ # Terrain chunks
├── wmo/ # Building models
├── m2/ # Character/creature models
└── blp/ # Textures
```
### Step 3: Connect to a Server
1. **Start WoWee**
```bash
cd build/bin && ./wowee
```
2. **Enter Realm Information**
- Server Address: e.g., `localhost:3724` or `play.example.com:3724`
- WoWee fetches the realm list automatically
- Select your realm and click **Connect**
3. **Choose Character**
- Select existing character or create new one
- Customize appearance and settings
- Click **Enter World**
## First Steps in Game
### Default Controls
| Action | Key |
|--------|-----|
| Move Forward | W |
| Move Backward | S |
| Strafe Left | A |
| Strafe Right | D |
| Jump | Space |
| Toggle Chat | Enter |
| Interact (talk to NPC, loot) | F |
| Open Inventory | B |
| Open Spellbook | P |
| Open Talent Tree | T |
| Open Quest Log | Q |
| Open World Map | W (when not typing) |
| Toggle Minimap | M |
| Toggle Nameplates | V |
| Toggle Party Frames | F |
| Toggle Settings | Escape |
| Target Next Enemy | Tab |
| Target Previous Enemy | Shift+Tab |
### Customizing Controls
Press **Escape****Keybindings** to customize hotkeys.
## Recommended First Steps
### 1. Adjust Graphics Settings
- Press Escape → **Video Settings**
- Select appropriate **Graphics Preset** for your GPU:
- **LOW**: Low-end GPUs or when performance is priority
- **MEDIUM**: Balanced quality and performance
- **HIGH**: Good GPU with modern drivers
- **ULTRA**: High-end GPU for maximum quality
### 2. Adjust Audio
- Press Escape → **Audio Settings**
- Set **Master Volume** to preferred level
- Adjust individual audio tracks (Music, Ambient, UI, etc.)
- Toggle **Original Soundtrack** if available
### 3. Configure UI
- Press Escape → **Game Settings**
- Minimap preferences (rotation, square mode, zoom)
- Bag settings (separate windows, compact mode)
- Action bar visibility
### 4. Complete First Quest
- Talk to nearby NPCs (they have quest markers ! or ?)
- Accept quest, complete objectives, return for reward
- Level up and gain experience
## Important Notes
### Data Directory
Game data is loaded from `Data/` subdirectory:
- If running from build folder: `../../Data` (symlinked automatically)
- If running from binary folder: `./Data` (must exist)
- If running in-place: Ensure `Data/` is in correct location
### Settings
- Settings are saved to `~/.wowee/settings.cfg` (Linux/macOS)
- Or `%APPDATA%\wowee\settings.cfg` (Windows)
- Keybindings, graphics settings, and UI state persist
### Multi-Expansion Support
WoWee auto-detects expansion from server:
- **Vanilla 1.12** - Original game
- **TBC 2.4.3** - Burning Crusade
- **WotLK 3.3.5a** - Wrath of the Lich King
You can override with environment variable:
```bash
WOWEE_EXPANSION=tbc ./wowee # Force TBC
```
## Troubleshooting
### "No realm list" or "Connection Failed"
- Check server address is correct
- Verify server is running
- See [Troubleshooting Guide](TROUBLESHOOTING.md#connection-issues)
### Graphics Errors
- See [Graphics Troubleshooting](TROUBLESHOOTING.md#graphics-issues)
- Start with LOW graphics preset
- Update GPU driver
### Audio Not Working
- Check system audio is enabled
- Verify audio files are extracted
- See [Audio Troubleshooting](TROUBLESHOOTING.md#audio-issues)
### General Issues
- Comprehensive troubleshooting: See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
- Check logs in `~/.wowee/logs/` for errors
- Verify expansion matches server requirements
## Server Configuration
### Tested Servers
- **AzerothCore** - Full support, recommended for learning
- **TrinityCore** - Full support, extensive customization
- **Mangos** - Full support, solid foundation
- **Turtle WoW** - Full support, 1.17 custom content
### Server Requirements
- Must support Vanilla, TBC, or WotLK protocol
- Warden anti-cheat supported (module execution via emulation)
- Network must allow connections to realm list and world server ports
See [Multi-Expansion Guide](EXPANSION_GUIDE.md) for protocol details.
## Next Steps
1. **Explore the World** - Travel to different zones and enjoy the landscape
2. **Join a Guild** - Find other players to group with
3. **Run Dungeons** - Experience instanced content
4. **PvP** - Engage in player-versus-player combat
5. **Twink Alt** - Create additional characters
6. **Customize Settings** - Fine-tune graphics, audio, and UI
## Getting Help
- **Game Issues**: See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
- **Graphics Help**: See [Graphics & Performance](README.md#graphics--performance) section
- **Multi-Expansion**: See [EXPANSION_GUIDE.md](EXPANSION_GUIDE.md)
- **Building Issues**: See [README.md](README.md#building)
## Tips for Better Performance
- Start with reasonable graphics preset for your GPU
- Close other applications when testing
- Keep GPU drivers updated
- Use FSR2 (if supported) for smooth 60+ FPS on weaker hardware
- Monitor frame rate with debug overlay (if available)
## Enjoy!
WoWee is a project to experience classic World of Warcraft on a modern engine. Have fun exploring Azeroth!

View file

@ -66,10 +66,33 @@ Protocol Compatible with **Vanilla (Classic) 1.12 + TBC 2.4.3 + WotLK 3.3.5a**.
- **Gossip** -- NPC interaction, dialogue options
- **Chat** -- Tabs/channels, emotes, chat bubbles, clickable URLs, clickable item links with tooltips
- **Party** -- Group invites, party list, out-of-range member health via SMSG_PARTY_MEMBER_STATS
- **Pets** -- Pet tracking via SMSG_PET_SPELLS, dismiss pet button
- **Pets** -- Pet tracking via SMSG_PET_SPELLS, action bar (10 slots with icon/autocast tinting/tooltips), dismiss button
- **Map Exploration** -- Subzone-level fog-of-war reveal matching retail behavior
- **Warden** -- Warden anti-cheat module execution via Unicorn Engine x86 emulation (cross-platform, no Wine)
- **UI** -- Loading screens with progress bar, settings window (shadow distance slider), minimap with zoom/rotation/square mode, top-right minimap mute speaker, separate bag windows with compact-empty mode (aggregate view)
- **UI** -- Loading screens with progress bar, settings window with graphics quality presets (LOW/MEDIUM/HIGH/ULTRA), shadow distance slider, minimap with zoom/rotation/square mode, top-right minimap mute speaker, separate bag windows with compact-empty mode (aggregate view)
## Graphics & Performance
### Quality Presets
WoWee includes four built-in graphics quality presets to help you quickly balance visual quality and performance:
| Preset | Shadows | MSAA | Normal Mapping | Clutter Density |
|--------|---------|------|----------------|-----------------|
| **LOW** | Off | Off | Disabled | 25% |
| **MEDIUM** | 200m distance | 2x | Basic | 60% |
| **HIGH** | 350m distance | 4x | Full (0.8x) | 100% |
| **ULTRA** | 500m distance | 8x | Enhanced (1.2x) | 150% |
Press Escape to open **Video Settings** and select a preset, or adjust individual settings for a custom configuration.
### Performance Tips
- Start with **LOW** or **MEDIUM** if you experience frame drops
- Shadows and MSAA have the largest impact on performance
- Reduce **shadow distance** if shadows cause issues
- Disable **water refraction** if you encounter GPU errors (requires FSR to be active)
- Use **FSR2** (built-in upscaling) for better frame rates on modern GPUs
## Building

186
TROUBLESHOOTING.md Normal file
View file

@ -0,0 +1,186 @@
# Troubleshooting Guide
This guide covers common issues and solutions for WoWee.
## Connection Issues
### "Authentication Failed"
- **Cause**: Incorrect server address, expired realm list, or version mismatch
- **Solution**:
1. Verify server address in realm list is correct
2. Ensure your WoW data directory is for the correct expansion (Vanilla/TBC/WotLK)
3. Check that the emulator server is running and reachable
### "Realm List Connection Failed"
- **Cause**: Server is down, firewall blocking connection, or DNS issue
- **Solution**:
1. Verify server IP/hostname is correct
2. Test connectivity: `ping realm-server-address`
3. Check firewall rules for port 3724 (auth) and 8085 (realm list)
4. Try using IP address instead of hostname (DNS issues)
### "Connection Lost During Login"
- **Cause**: Network timeout, server overload, or incompatible protocol version
- **Solution**:
1. Check your network connection
2. Reduce number of assets loading (lower graphics preset)
3. Verify server supports this expansion version
## Graphics Issues
### "VK_ERROR_DEVICE_LOST" or Client Crashes
- **Cause**: GPU driver issue, insufficient VRAM, or graphics feature incompatibility
- **Solution**:
1. **Immediate**: Disable advanced graphics features:
- Press Escape → Video Settings
- Set graphics preset to **LOW**
- Disable Water Refraction (requires FSR)
- Disable MSAA (set to Off)
2. **Medium term**: Update GPU driver to latest version
3. **Verify**: Use a graphics test tool to ensure GPU stability
4. **If persists**: Try FSR2 disabled mode - check renderer logs
### Black Screen or Rendering Issues
- **Cause**: Missing shaders, GPU memory allocation failure, or incorrect graphics settings
- **Solution**:
1. Check logs: Look in `~/.wowee/logs/` for error messages
2. Verify shaders compiled: Check for `.spv` files in `assets/shaders/compiled/`
3. Reduce shadow distance: Press Escape → Video Settings → Lower shadow distance from 300m to 100m
4. Disable shadows entirely if issues persist
### Low FPS or Frame Stuttering
- **Cause**: Too high graphics settings for your GPU, memory fragmentation, or asset loading
- **Solution**:
1. Apply lower graphics preset: Escape → LOW or MEDIUM
2. Disable MSAA: Set to "Off"
3. Reduce draw distance: Move further away from complex areas
4. Close other applications consuming GPU memory
5. Check CPU usage - if high, reduce number of visible entities
### Water/Terrain Flickering
- **Cause**: Shadow mapping artifacts, terrain LOD issues, or GPU memory pressure
- **Solution**:
1. Increase shadow distance slightly (150m to 200m)
2. Disable shadows entirely as last resort
3. Check GPU memory usage
## Audio Issues
### No Sound
- **Cause**: Audio initialization failed, missing audio data, or incorrect mixer setup
- **Solution**:
1. Check system audio is working: Test with another application
2. Verify audio files extracted: Check for `.wav` files in `Data/Audio/`
3. Unmute audio: Look for speaker icon in minimap (top-right) - click to unmute
4. Check settings: Escape → Audio Settings → Master Volume > 0
### Sound Cutting Out
- **Cause**: Audio buffer underrun, too many simultaneous sounds, or driver issue
- **Solution**:
1. Lower audio volume: Escape → Audio Settings → Reduce Master Volume
2. Disable distant ambient sounds: Reduce Ambient Volume
3. Reduce number of particle effects
4. Update audio driver
## Gameplay Issues
### Character Stuck or Not Moving
- **Cause**: Network synchronization issue, collision bug, or server desync
- **Solution**:
1. Try pressing Escape to deselect any target, then move
2. Jump (Spacebar) to test physics
3. Reload the character: Press Escape → Disconnect → Reconnect
4. Check for transport/vehicle state: Press 'R' to dismount if applicable
### Spells Not Casting or Showing "Error"
- **Cause**: Cooldown, mana insufficient, target out of range, or server desync
- **Solution**:
1. Verify spell is off cooldown (action bar shows availability)
2. Check mana/energy: Look at player frame (top-left)
3. Verify target range: Hover action bar button for range info
4. Check server logs for error messages (combat log will show reason)
### Quests Not Updating
- **Cause**: Objective already completed in different session, quest giver not found, or network desync
- **Solution**:
1. Check quest objective: Open quest log (Q key) → Verify objective requirements
2. Re-interact with NPC to trigger update packet
3. Reload character if issue persists
### Items Not Appearing in Inventory
- **Cause**: Inventory full, item filter active, or network desync
- **Solution**:
1. Check inventory space: Open inventory (B key) → Count free slots
2. Verify item isn't already there: Search inventory for item name
3. Check if bags are full: Open bag windows, consolidate items
4. Reload character if item is still missing
## Performance Optimization
### For Low-End GPUs
```
Graphics Preset: LOW
- Shadows: OFF
- MSAA: OFF
- Normal Mapping: Disabled
- Clutter Density: 25%
- Draw Distance: Minimum
- Particles: Reduced
```
### For Mid-Range GPUs
```
Graphics Preset: MEDIUM
- Shadows: 200m
- MSAA: 2x
- Normal Mapping: Basic
- Clutter Density: 60%
- FSR2: Enabled (if desired)
```
### For High-End GPUs
```
Graphics Preset: HIGH or ULTRA
- Shadows: 350-500m
- MSAA: 4-8x
- Normal Mapping: Full (1.2x strength)
- Clutter Density: 100-150%
- FSR2: Optional (for 4K smoothness)
```
## Getting Help
### Check Logs
Detailed logs are saved to:
- **Linux/macOS**: `~/.wowee/logs/`
- **Windows**: `%APPDATA%\wowee\logs\`
Include relevant log entries when reporting issues.
### Check Server Compatibility
- **AzerothCore**: Full support
- **TrinityCore**: Full support
- **Mangos**: Full support
- **Turtle WoW**: Full support (1.17)
### Report Issues
If you encounter a bug:
1. Enable logging: Watch console for error messages
2. Reproduce the issue consistently
3. Gather system info: GPU, driver version, OS
4. Check if issue is expansion-specific (Classic/TBC/WotLK)
5. Report with detailed steps to reproduce
### Clear Cache
If experiencing persistent issues, clear WoWee's cache:
```bash
# Linux/macOS
rm -rf ~/.wowee/warden_cache/
rm -rf ~/.wowee/asset_cache/
# Windows
rmdir %APPDATA%\wowee\warden_cache\ /s
rmdir %APPDATA%\wowee\asset_cache\ /s
```
Then restart WoWee to rebuild cache.

View file

@ -10,9 +10,7 @@ layout(push_constant) uniform PushConstants {
} pc;
void main() {
// Undo the vertex shader Y flip (postprocess.vert flips for Vulkan overlay,
// but we need standard UV coords for texture sampling)
vec2 tc = vec2(TexCoord.x, 1.0 - TexCoord.y);
vec2 tc = TexCoord;
vec2 texelSize = pc.params.xy;
float sharpness = pc.params.z;

Binary file not shown.

View file

@ -21,9 +21,7 @@ vec3 fsrFetch(vec2 p, vec2 off) {
}
void main() {
// Undo the vertex shader Y flip (postprocess.vert flips for Vulkan overlay,
// but we need standard UV coords for texture sampling)
vec2 tc = vec2(TexCoord.x, 1.0 - TexCoord.y);
vec2 tc = TexCoord;
// Map output pixel to input space
vec2 pp = tc * fsr.con2.xy; // output pixel position

Binary file not shown.

View file

@ -0,0 +1,155 @@
#version 450
// FXAA 3.11 — Fast Approximate Anti-Aliasing post-process pass.
// Reads the resolved scene color and outputs a smoothed result.
// Push constant: rcpFrame = vec2(1/width, 1/height), sharpness (0=off, 2=max), desaturate (1=ghost grayscale).
layout(set = 0, binding = 0) uniform sampler2D uScene;
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 outColor;
layout(push_constant) uniform PC {
vec2 rcpFrame;
float sharpness; // 0 = no sharpen, 2 = max (matches FSR2 RCAS range)
float desaturate; // 1 = full grayscale (ghost mode), 0 = normal color
} pc;
// Quality tuning
#define FXAA_EDGE_THRESHOLD (1.0/8.0) // minimum edge contrast to process
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) // ignore very dark regions
#define FXAA_SEARCH_STEPS 12
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
#define FXAA_SUBPIX 0.75
#define FXAA_SUBPIX_TRIM (1.0/4.0)
#define FXAA_SUBPIX_TRIM_SCALE (1.0/(1.0 - FXAA_SUBPIX_TRIM))
#define FXAA_SUBPIX_CAP (3.0/4.0)
float luma(vec3 c) {
return dot(c, vec3(0.299, 0.587, 0.114));
}
void main() {
vec2 uv = TexCoord;
vec2 rcp = pc.rcpFrame;
// --- Centre and cardinal neighbours ---
vec3 rgbM = texture(uScene, uv).rgb;
vec3 rgbN = texture(uScene, uv + vec2( 0.0, -1.0) * rcp).rgb;
vec3 rgbS = texture(uScene, uv + vec2( 0.0, 1.0) * rcp).rgb;
vec3 rgbE = texture(uScene, uv + vec2( 1.0, 0.0) * rcp).rgb;
vec3 rgbW = texture(uScene, uv + vec2(-1.0, 0.0) * rcp).rgb;
float lumaN = luma(rgbN);
float lumaS = luma(rgbS);
float lumaE = luma(rgbE);
float lumaW = luma(rgbW);
float lumaM = luma(rgbM);
float lumaMin = min(lumaM, min(min(lumaN, lumaS), min(lumaE, lumaW)));
float lumaMax = max(lumaM, max(max(lumaN, lumaS), max(lumaE, lumaW)));
float range = lumaMax - lumaMin;
// Early exit on smooth regions
if (range < max(FXAA_EDGE_THRESHOLD_MIN, lumaMax * FXAA_EDGE_THRESHOLD)) {
outColor = vec4(rgbM, 1.0);
return;
}
// --- Diagonal neighbours ---
vec3 rgbNW = texture(uScene, uv + vec2(-1.0, -1.0) * rcp).rgb;
vec3 rgbNE = texture(uScene, uv + vec2( 1.0, -1.0) * rcp).rgb;
vec3 rgbSW = texture(uScene, uv + vec2(-1.0, 1.0) * rcp).rgb;
vec3 rgbSE = texture(uScene, uv + vec2( 1.0, 1.0) * rcp).rgb;
float lumaNW = luma(rgbNW);
float lumaNE = luma(rgbNE);
float lumaSW = luma(rgbSW);
float lumaSE = luma(rgbSE);
// --- Sub-pixel blend factor ---
float lumaL = (lumaN + lumaS + lumaE + lumaW) * 0.25;
float rangeL = abs(lumaL - lumaM);
float blendL = max(0.0, (rangeL / range) - FXAA_SUBPIX_TRIM) * FXAA_SUBPIX_TRIM_SCALE;
blendL = min(FXAA_SUBPIX_CAP, blendL) * FXAA_SUBPIX;
// --- Edge orientation (horizontal vs. vertical) ---
float edgeHorz =
abs(-2.0*lumaW + lumaNW + lumaSW)
+ 2.0*abs(-2.0*lumaM + lumaN + lumaS)
+ abs(-2.0*lumaE + lumaNE + lumaSE);
float edgeVert =
abs(-2.0*lumaS + lumaSW + lumaSE)
+ 2.0*abs(-2.0*lumaM + lumaW + lumaE)
+ abs(-2.0*lumaN + lumaNW + lumaNE);
bool horzSpan = (edgeHorz >= edgeVert);
float lengthSign = horzSpan ? rcp.y : rcp.x;
float luma1 = horzSpan ? lumaN : lumaW;
float luma2 = horzSpan ? lumaS : lumaE;
float grad1 = abs(luma1 - lumaM);
float grad2 = abs(luma2 - lumaM);
lengthSign = (grad1 >= grad2) ? -lengthSign : lengthSign;
// --- Edge search ---
vec2 posB = uv;
vec2 offNP = horzSpan ? vec2(rcp.x, 0.0) : vec2(0.0, rcp.y);
if (!horzSpan) posB.x += lengthSign * 0.5;
if ( horzSpan) posB.y += lengthSign * 0.5;
float lumaMLSS = lumaM - (luma1 + luma2) * 0.5;
float gradientScaled = max(grad1, grad2) * 0.25;
vec2 posN = posB - offNP;
vec2 posP = posB + offNP;
bool done1 = false, done2 = false;
float lumaEnd1 = 0.0, lumaEnd2 = 0.0;
for (int i = 0; i < FXAA_SEARCH_STEPS; ++i) {
if (!done1) lumaEnd1 = luma(texture(uScene, posN).rgb) - lumaMLSS;
if (!done2) lumaEnd2 = luma(texture(uScene, posP).rgb) - lumaMLSS;
done1 = done1 || (abs(lumaEnd1) >= gradientScaled * FXAA_SEARCH_THRESHOLD);
done2 = done2 || (abs(lumaEnd2) >= gradientScaled * FXAA_SEARCH_THRESHOLD);
if (done1 && done2) break;
if (!done1) posN -= offNP;
if (!done2) posP += offNP;
}
float dstN = horzSpan ? (uv.x - posN.x) : (uv.y - posN.y);
float dstP = horzSpan ? (posP.x - uv.x) : (posP.y - uv.y);
bool dirN = (dstN < dstP);
float lumaEndFinal = dirN ? lumaEnd1 : lumaEnd2;
float spanLength = dstN + dstP;
float pixelOffset = (dirN ? dstN : dstP) / spanLength;
bool goodSpan = ((lumaEndFinal < 0.0) != (lumaMLSS < 0.0));
float pixelOffsetFinal = max(goodSpan ? pixelOffset : 0.0, blendL);
vec2 finalUV = uv;
if ( horzSpan) finalUV.y += pixelOffsetFinal * lengthSign;
if (!horzSpan) finalUV.x += pixelOffsetFinal * lengthSign;
vec3 fxaaResult = texture(uScene, finalUV).rgb;
// Post-FXAA contrast-adaptive sharpening (unsharp mask).
// Counteracts FXAA's sub-pixel blur when sharpness > 0.
if (pc.sharpness > 0.0) {
vec2 r = pc.rcpFrame;
vec3 blur = (texture(uScene, uv + vec2(-r.x, 0)).rgb
+ texture(uScene, uv + vec2( r.x, 0)).rgb
+ texture(uScene, uv + vec2(0, -r.y)).rgb
+ texture(uScene, uv + vec2(0, r.y)).rgb) * 0.25;
// scale sharpness from [0,2] to a modest [0, 0.3] boost factor
float s = pc.sharpness * 0.15;
fxaaResult = clamp(fxaaResult + s * (fxaaResult - blur), 0.0, 1.0);
}
// Ghost mode: desaturate to grayscale (with a slight cool blue tint).
if (pc.desaturate > 0.5) {
float gray = dot(fxaaResult, vec3(0.299, 0.587, 0.114));
fxaaResult = mix(fxaaResult, vec3(gray, gray, gray * 1.05), pc.desaturate);
}
outColor = vec4(fxaaResult, 1.0);
}

Binary file not shown.

View file

@ -25,6 +25,9 @@ void main() {
if (lum < 0.05) discard;
}
float edge = smoothstep(0.5, 0.4, length(p - 0.5));
outColor = texColor * vColor * vec4(vec3(1.0), edge);
// Soft circular falloff for point-sprite edges.
float edge = 1.0 - smoothstep(0.4, 0.5, length(p - 0.5));
float alpha = texColor.a * vColor.a * edge;
vec3 rgb = texColor.rgb * vColor.rgb * alpha;
outColor = vec4(rgb, alpha);
}

View file

@ -0,0 +1,25 @@
#version 450
// M2 ribbon emitter fragment shader.
// Samples the ribbon texture, multiplied by vertex color and alpha.
// Uses additive blending (pipeline-level) for magic/spell trails.
layout(set = 1, binding = 0) uniform sampler2D uTexture;
layout(location = 0) in vec3 vColor;
layout(location = 1) in float vAlpha;
layout(location = 2) in vec2 vUV;
layout(location = 3) in float vFogFactor;
layout(location = 0) out vec4 outColor;
void main() {
vec4 tex = texture(uTexture, vUV);
// For additive ribbons alpha comes from texture luminance; multiply by vertex alpha.
float a = tex.a * vAlpha;
if (a < 0.01) discard;
vec3 rgb = tex.rgb * vColor;
// Ribbons fade slightly with fog (additive blend attenuated toward black = invisible in fog).
rgb *= vFogFactor;
outColor = vec4(rgb, a);
}

Binary file not shown.

View file

@ -0,0 +1,43 @@
#version 450
// M2 ribbon emitter vertex shader.
// Ribbon geometry is generated CPU-side as a triangle strip.
// Vertex format: pos(3) + color(3) + alpha(1) + uv(2) = 9 floats.
layout(set = 0, binding = 0) uniform PerFrame {
mat4 view;
mat4 projection;
mat4 lightSpaceMatrix;
vec4 lightDir;
vec4 lightColor;
vec4 ambientColor;
vec4 viewPos;
vec4 fogColor;
vec4 fogParams;
vec4 shadowParams;
};
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aColor;
layout(location = 2) in float aAlpha;
layout(location = 3) in vec2 aUV;
layout(location = 0) out vec3 vColor;
layout(location = 1) out float vAlpha;
layout(location = 2) out vec2 vUV;
layout(location = 3) out float vFogFactor;
void main() {
vec4 worldPos = vec4(aPos, 1.0);
vec4 viewPos4 = view * worldPos;
gl_Position = projection * viewPos4;
float dist = length(viewPos4.xyz);
float fogStart = fogParams.x;
float fogEnd = fogParams.y;
vFogFactor = clamp((fogEnd - dist) / max(fogEnd - fogStart, 0.001), 0.0, 1.0);
vColor = aColor;
vAlpha = aAlpha;
vUV = aUV;
}

Binary file not shown.

View file

@ -40,23 +40,27 @@ void main() {
float cs = cos(push.rotation);
float sn = sin(push.rotation);
vec2 rotated = vec2(center.x * cs - center.y * sn, center.x * sn + center.y * cs);
vec2 mapUV = push.playerUV + vec2(-rotated.x, rotated.y) * push.zoomRadius * 2.0;
vec2 mapUV = push.playerUV + vec2(rotated.x, rotated.y) * push.zoomRadius * 2.0;
vec4 mapColor = texture(uComposite, mapUV);
// Player arrow
float acs = cos(push.arrowRotation);
float asn = sin(push.arrowRotation);
vec2 ac = center;
vec2 arrowPos = vec2(-(ac.x * acs - ac.y * asn), ac.x * asn + ac.y * acs);
vec2 tip = vec2(0.0, -0.04);
vec2 left = vec2(-0.02, 0.02);
vec2 right = vec2(0.02, 0.02);
if (pointInTriangle(arrowPos, tip, left, right)) {
mapColor = vec4(1.0, 0.8, 0.0, 1.0);
// Single player direction indicator (center arrow) rendered in-shader.
vec2 local = center; // [-0.5, 0.5] around minimap center
float ac = cos(push.arrowRotation);
float as = sin(push.arrowRotation);
// TexCoord Y grows downward on screen; use negative Y so 0-angle points North (up).
vec2 tip = vec2(0.0, -0.09);
vec2 left = vec2(-0.045, 0.02);
vec2 right = vec2( 0.045, 0.02);
mat2 rot = mat2(ac, -as, as, ac);
tip = rot * tip;
left = rot * left;
right = rot * right;
if (pointInTriangle(local, tip, left, right)) {
mapColor.rgb = vec3(1.0, 0.86, 0.05);
}
float centerDot = smoothstep(0.016, 0.0, length(local));
mapColor.rgb = mix(mapColor.rgb, vec3(1.0), centerDot * 0.95);
// Dark border ring
float border = smoothstep(0.48, 0.5, dist);

View file

@ -6,5 +6,7 @@ void main() {
// Fullscreen triangle trick: 3 vertices, no vertex buffer
TexCoord = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(TexCoord * 2.0 - 1.0, 0.0, 1.0);
TexCoord.y = 1.0 - TexCoord.y; // flip Y for Vulkan
// No Y-flip: scene textures use Vulkan convention (v=0 at top),
// and NDC y=-1 already maps to framebuffer top, so the triangle
// naturally samples the correct row without any inversion.
}

Binary file not shown.

View file

@ -5,6 +5,7 @@ layout(set = 1, binding = 0) uniform sampler2D markerTexture;
layout(push_constant) uniform Push {
mat4 model;
float alpha;
float grayscale; // 0 = full colour, 1 = fully desaturated (trivial quests)
} push;
layout(location = 0) in vec2 TexCoord;
@ -14,5 +15,7 @@ layout(location = 0) out vec4 outColor;
void main() {
vec4 texColor = texture(markerTexture, TexCoord);
if (texColor.a < 0.1) discard;
outColor = vec4(texColor.rgb, texColor.a * push.alpha);
float lum = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));
vec3 rgb = mix(texColor.rgb, vec3(lum), push.grayscale);
outColor = vec4(rgb, texColor.a * push.alpha);
}

Binary file not shown.

View file

@ -1,6 +1,6 @@
# Project Status
**Last updated**: 2026-03-07
**Last updated**: 2026-03-18
## What This Repo Is
@ -25,6 +25,9 @@ Implemented (working in normal use):
- Talent tree UI with proper visuals and functionality
- Pet tracking (SMSG_PET_SPELLS), dismiss pet button
- Party: group invites, party list, out-of-range member health (SMSG_PARTY_MEMBER_STATS)
- Nameplates: NPC subtitles, guild names, elite/boss/rare borders, quest/raid indicators, cast bars, debuff dots
- Floating combat text: world-space damage/heal numbers above entities with 3D projection
- Target/focus frames: guild name, creature type, rank badges, combo points, cast bars
- Map exploration: subzone-level fog-of-war reveal
- Warden anti-cheat: full module execution via Unicorn Engine x86 emulation; module caching
- Audio: ambient, movement, combat, spell, and UI sound systems
@ -35,10 +38,9 @@ Implemented (working in normal use):
In progress / known gaps:
- Transports: M2 transports (trams) working with position-delta riding; WMO transports (ships, zeppelins) working with path following; some edge cases remain
- 3D positional audio: not implemented (mono/stereo only)
- Visual edge cases: some M2/WMO rendering gaps (character shin mesh, some particle effects)
- Interior rendering: WMO interior shadows disabled (too dark); lava steam particles sparse
- Water refraction: implemented but disabled by default (can cause VK_ERROR_DEVICE_LOST on some GPUs)
- Visual edge cases: some M2/WMO rendering gaps (some particle effects)
- Lava steam particles: sparse in some areas (tuning opportunity)
- Water refraction: enabled by default; srcAccessMask barrier fix (2026-03-18) resolved prior VK_ERROR_DEVICE_LOST on AMD/Mali GPUs
## Where To Look

34
extern/lua-5.1.5/COPYRIGHT vendored Normal file
View file

@ -0,0 +1,34 @@
Lua License
-----------
Lua is licensed under the terms of the MIT license reproduced below.
This means that Lua is free software and can be used for both academic
and commercial purposes at absolutely no cost.
For details and rationale, see http://www.lua.org/license.html .
===============================================================================
Copyright (C) 1994-2012 Lua.org, PUC-Rio.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================================================
(end of COPYRIGHT)

183
extern/lua-5.1.5/HISTORY vendored Normal file
View file

@ -0,0 +1,183 @@
HISTORY for Lua 5.1
* Changes from version 5.0 to 5.1
-------------------------------
Language:
+ new module system.
+ new semantics for control variables of fors.
+ new semantics for setn/getn.
+ new syntax/semantics for varargs.
+ new long strings and comments.
+ new `mod' operator (`%')
+ new length operator #t
+ metatables for all types
API:
+ new functions: lua_createtable, lua_get(set)field, lua_push(to)integer.
+ user supplies memory allocator (lua_open becomes lua_newstate).
+ luaopen_* functions must be called through Lua.
Implementation:
+ new configuration scheme via luaconf.h.
+ incremental garbage collection.
+ better handling of end-of-line in the lexer.
+ fully reentrant parser (new Lua function `load')
+ better support for 64-bit machines.
+ native loadlib support for Mac OS X.
+ standard distribution in only one library (lualib.a merged into lua.a)
* Changes from version 4.0 to 5.0
-------------------------------
Language:
+ lexical scoping.
+ Lua coroutines.
+ standard libraries now packaged in tables.
+ tags replaced by metatables and tag methods replaced by metamethods,
stored in metatables.
+ proper tail calls.
+ each function can have its own global table, which can be shared.
+ new __newindex metamethod, called when we insert a new key into a table.
+ new block comments: --[[ ... ]].
+ new generic for.
+ new weak tables.
+ new boolean type.
+ new syntax "local function".
+ (f()) returns the first value returned by f.
+ {f()} fills a table with all values returned by f.
+ \n ignored in [[\n .
+ fixed and-or priorities.
+ more general syntax for function definition (e.g. function a.x.y:f()...end).
+ more general syntax for function calls (e.g. (print or write)(9)).
+ new functions (time/date, tmpfile, unpack, require, load*, etc.).
API:
+ chunks are loaded by using lua_load; new luaL_loadfile and luaL_loadbuffer.
+ introduced lightweight userdata, a simple "void*" without a metatable.
+ new error handling protocol: the core no longer prints error messages;
all errors are reported to the caller on the stack.
+ new lua_atpanic for host cleanup.
+ new, signal-safe, hook scheme.
Implementation:
+ new license: MIT.
+ new, faster, register-based virtual machine.
+ support for external multithreading and coroutines.
+ new and consistent error message format.
+ the core no longer needs "stdio.h" for anything (except for a single
use of sprintf to convert numbers to strings).
+ lua.c now runs the environment variable LUA_INIT, if present. It can
be "@filename", to run a file, or the chunk itself.
+ support for user extensions in lua.c.
sample implementation given for command line editing.
+ new dynamic loading library, active by default on several platforms.
+ safe garbage-collector metamethods.
+ precompiled bytecodes checked for integrity (secure binary dostring).
+ strings are fully aligned.
+ position capture in string.find.
+ read('*l') can read lines with embedded zeros.
* Changes from version 3.2 to 4.0
-------------------------------
Language:
+ new "break" and "for" statements (both numerical and for tables).
+ uniform treatment of globals: globals are now stored in a Lua table.
+ improved error messages.
+ no more '$debug': full speed *and* full debug information.
+ new read form: read(N) for next N bytes.
+ general read patterns now deprecated.
(still available with -DCOMPAT_READPATTERNS.)
+ all return values are passed as arguments for the last function
(old semantics still available with -DLUA_COMPAT_ARGRET)
+ garbage collection tag methods for tables now deprecated.
+ there is now only one tag method for order.
API:
+ New API: fully re-entrant, simpler, and more efficient.
+ New debug API.
Implementation:
+ faster than ever: cleaner virtual machine and new hashing algorithm.
+ non-recursive garbage-collector algorithm.
+ reduced memory usage for programs with many strings.
+ improved treatment for memory allocation errors.
+ improved support for 16-bit machines (we hope).
+ code now compiles unmodified as both ANSI C and C++.
+ numbers in bases other than 10 are converted using strtoul.
+ new -f option in Lua to support #! scripts.
+ luac can now combine text and binaries.
* Changes from version 3.1 to 3.2
-------------------------------
+ redirected all output in Lua's core to _ERRORMESSAGE and _ALERT.
+ increased limit on the number of constants and globals per function
(from 2^16 to 2^24).
+ debugging info (lua_debug and hooks) moved into lua_state and new API
functions provided to get and set this info.
+ new debug lib gives full debugging access within Lua.
+ new table functions "foreachi", "sort", "tinsert", "tremove", "getn".
+ new io functions "flush", "seek".
* Changes from version 3.0 to 3.1
-------------------------------
+ NEW FEATURE: anonymous functions with closures (via "upvalues").
+ new syntax:
- local variables in chunks.
- better scope control with DO block END.
- constructors can now be also written: { record-part; list-part }.
- more general syntax for function calls and lvalues, e.g.:
f(x).y=1
o:f(x,y):g(z)
f"string" is sugar for f("string")
+ strings may now contain arbitrary binary data (e.g., embedded zeros).
+ major code re-organization and clean-up; reduced module interdependecies.
+ no arbitrary limits on the total number of constants and globals.
+ support for multiple global contexts.
+ better syntax error messages.
+ new traversal functions "foreach" and "foreachvar".
+ the default for numbers is now double.
changing it to use floats or longs is easy.
+ complete debug information stored in pre-compiled chunks.
+ sample interpreter now prompts user when run interactively, and also
handles control-C interruptions gracefully.
* Changes from version 2.5 to 3.0
-------------------------------
+ NEW CONCEPT: "tag methods".
Tag methods replace fallbacks as the meta-mechanism for extending the
semantics of Lua. Whereas fallbacks had a global nature, tag methods
work on objects having the same tag (e.g., groups of tables).
Existing code that uses fallbacks should work without change.
+ new, general syntax for constructors {[exp] = exp, ... }.
+ support for handling variable number of arguments in functions (varargs).
+ support for conditional compilation ($if ... $else ... $end).
+ cleaner semantics in API simplifies host code.
+ better support for writing libraries (auxlib.h).
+ better type checking and error messages in the standard library.
+ luac can now also undump.
* Changes from version 2.4 to 2.5
-------------------------------
+ io and string libraries are now based on pattern matching;
the old libraries are still available for compatibility
+ dofile and dostring can now return values (via return statement)
+ better support for 16- and 64-bit machines
+ expanded documentation, with more examples
* Changes from version 2.2 to 2.4
-------------------------------
+ external compiler creates portable binary files that can be loaded faster
+ interface for debugging and profiling
+ new "getglobal" fallback
+ new functions for handling references to Lua objects
+ new functions in standard lib
+ only one copy of each string is stored
+ expanded documentation, with more examples
* Changes from version 2.1 to 2.2
-------------------------------
+ functions now may be declared with any "lvalue" as a name
+ garbage collection of functions
+ support for pipes
* Changes from version 1.1 to 2.1
-------------------------------
+ object-oriented support
+ fallbacks
+ simplified syntax for tables
+ many internal improvements
(end of HISTORY)

99
extern/lua-5.1.5/INSTALL vendored Normal file
View file

@ -0,0 +1,99 @@
INSTALL for Lua 5.1
* Building Lua
------------
Lua is built in the src directory, but the build process can be
controlled from the top-level Makefile.
Building Lua on Unix systems should be very easy. First do "make" and
see if your platform is listed. If so, just do "make xxx", where xxx
is your platform name. The platforms currently supported are:
aix ansi bsd freebsd generic linux macosx mingw posix solaris
If your platform is not listed, try the closest one or posix, generic,
ansi, in this order.
See below for customization instructions and for instructions on how
to build with other Windows compilers.
If you want to check that Lua has been built correctly, do "make test"
after building Lua. Also, have a look at the example programs in test.
* Installing Lua
--------------
Once you have built Lua, you may want to install it in an official
place in your system. In this case, do "make install". The official
place and the way to install files are defined in Makefile. You must
have the right permissions to install files.
If you want to build and install Lua in one step, do "make xxx install",
where xxx is your platform name.
If you want to install Lua locally, then do "make local". This will
create directories bin, include, lib, man, and install Lua there as
follows:
bin: lua luac
include: lua.h luaconf.h lualib.h lauxlib.h lua.hpp
lib: liblua.a
man/man1: lua.1 luac.1
These are the only directories you need for development.
There are man pages for lua and luac, in both nroff and html, and a
reference manual in html in doc, some sample code in test, and some
useful stuff in etc. You don't need these directories for development.
If you want to install Lua locally, but in some other directory, do
"make install INSTALL_TOP=xxx", where xxx is your chosen directory.
See below for instructions for Windows and other systems.
* Customization
-------------
Three things can be customized by editing a file:
- Where and how to install Lua -- edit Makefile.
- How to build Lua -- edit src/Makefile.
- Lua features -- edit src/luaconf.h.
You don't actually need to edit the Makefiles because you may set the
relevant variables when invoking make.
On the other hand, if you need to select some Lua features, you'll need
to edit src/luaconf.h. The edited file will be the one installed, and
it will be used by any Lua clients that you build, to ensure consistency.
We strongly recommend that you enable dynamic loading. This is done
automatically for all platforms listed above that have this feature
(and also Windows). See src/luaconf.h and also src/Makefile.
* Building Lua on Windows and other systems
-----------------------------------------
If you're not using the usual Unix tools, then the instructions for
building Lua depend on the compiler you use. You'll need to create
projects (or whatever your compiler uses) for building the library,
the interpreter, and the compiler, as follows:
library: lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c
lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c
ltable.c ltm.c lundump.c lvm.c lzio.c
lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c loslib.c
ltablib.c lstrlib.c loadlib.c linit.c
interpreter: library, lua.c
compiler: library, luac.c print.c
If you use Visual Studio .NET, you can use etc/luavs.bat in its
"Command Prompt".
If all you want is to build the Lua interpreter, you may put all .c files
in a single project, except for luac.c and print.c. Or just use etc/all.c.
To use Lua as a library in your own programs, you'll need to know how to
create and use libraries with your compiler.
As mentioned above, you may edit luaconf.h to select some features before
building Lua.
(end of INSTALL)

128
extern/lua-5.1.5/Makefile vendored Normal file
View file

@ -0,0 +1,128 @@
# makefile for installing Lua
# see INSTALL for installation instructions
# see src/Makefile and src/luaconf.h for further customization
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
# Your platform. See PLATS for possible values.
PLAT= none
# Where to install. The installation starts in the src and doc directories,
# so take care if INSTALL_TOP is not an absolute path.
INSTALL_TOP= /usr/local
INSTALL_BIN= $(INSTALL_TOP)/bin
INSTALL_INC= $(INSTALL_TOP)/include
INSTALL_LIB= $(INSTALL_TOP)/lib
INSTALL_MAN= $(INSTALL_TOP)/man/man1
#
# You probably want to make INSTALL_LMOD and INSTALL_CMOD consistent with
# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h (and also with etc/lua.pc).
INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V
INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V
# How to install. If your install program does not support "-p", then you
# may have to run ranlib on the installed liblua.a (do "make ranlib").
INSTALL= install -p
INSTALL_EXEC= $(INSTALL) -m 0755
INSTALL_DATA= $(INSTALL) -m 0644
#
# If you don't have install you can use cp instead.
# INSTALL= cp -p
# INSTALL_EXEC= $(INSTALL)
# INSTALL_DATA= $(INSTALL)
# Utilities.
MKDIR= mkdir -p
RANLIB= ranlib
# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========
# Convenience platforms targets.
PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
# What to install.
TO_BIN= lua luac
TO_INC= lua.h luaconf.h lualib.h lauxlib.h ../etc/lua.hpp
TO_LIB= liblua.a
TO_MAN= lua.1 luac.1
# Lua version and release.
V= 5.1
R= 5.1.5
all: $(PLAT)
$(PLATS) clean:
cd src && $(MAKE) $@
test: dummy
src/lua test/hello.lua
install: dummy
cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)
ranlib:
cd src && cd $(INSTALL_LIB) && $(RANLIB) $(TO_LIB)
local:
$(MAKE) install INSTALL_TOP=..
none:
@echo "Please do"
@echo " make PLATFORM"
@echo "where PLATFORM is one of these:"
@echo " $(PLATS)"
@echo "See INSTALL for complete instructions."
# make may get confused with test/ and INSTALL in a case-insensitive OS
dummy:
# echo config parameters
echo:
@echo ""
@echo "These are the parameters currently set in src/Makefile to build Lua $R:"
@echo ""
@cd src && $(MAKE) -s echo
@echo ""
@echo "These are the parameters currently set in Makefile to install Lua $R:"
@echo ""
@echo "PLAT = $(PLAT)"
@echo "INSTALL_TOP = $(INSTALL_TOP)"
@echo "INSTALL_BIN = $(INSTALL_BIN)"
@echo "INSTALL_INC = $(INSTALL_INC)"
@echo "INSTALL_LIB = $(INSTALL_LIB)"
@echo "INSTALL_MAN = $(INSTALL_MAN)"
@echo "INSTALL_LMOD = $(INSTALL_LMOD)"
@echo "INSTALL_CMOD = $(INSTALL_CMOD)"
@echo "INSTALL_EXEC = $(INSTALL_EXEC)"
@echo "INSTALL_DATA = $(INSTALL_DATA)"
@echo ""
@echo "See also src/luaconf.h ."
@echo ""
# echo private config parameters
pecho:
@echo "V = $(V)"
@echo "R = $(R)"
@echo "TO_BIN = $(TO_BIN)"
@echo "TO_INC = $(TO_INC)"
@echo "TO_LIB = $(TO_LIB)"
@echo "TO_MAN = $(TO_MAN)"
# echo config parameters as Lua code
# uncomment the last sed expression if you want nil instead of empty strings
lecho:
@echo "-- installation parameters for Lua $R"
@echo "VERSION = '$V'"
@echo "RELEASE = '$R'"
@$(MAKE) echo | grep = | sed -e 's/= /= "/' -e 's/$$/"/' #-e 's/""/nil/'
@echo "-- EOF"
# list targets that do not create files (but not all makes understand .PHONY)
.PHONY: all $(PLATS) clean test install local none dummy echo pecho lecho
# (end of Makefile)

37
extern/lua-5.1.5/README vendored Normal file
View file

@ -0,0 +1,37 @@
README for Lua 5.1
See INSTALL for installation instructions.
See HISTORY for a summary of changes since the last released version.
* What is Lua?
------------
Lua is a powerful, light-weight programming language designed for extending
applications. Lua is also frequently used as a general-purpose, stand-alone
language. Lua is free software.
For complete information, visit Lua's web site at http://www.lua.org/ .
For an executive summary, see http://www.lua.org/about.html .
Lua has been used in many different projects around the world.
For a short list, see http://www.lua.org/uses.html .
* Availability
------------
Lua is freely available for both academic and commercial purposes.
See COPYRIGHT and http://www.lua.org/license.html for details.
Lua can be downloaded at http://www.lua.org/download.html .
* Installation
------------
Lua is implemented in pure ANSI C, and compiles unmodified in all known
platforms that have an ANSI C compiler. In most Unix-like platforms, simply
do "make" with a suitable target. See INSTALL for detailed instructions.
* Origin
------
Lua is developed at Lua.org, a laboratory of the Department of Computer
Science of PUC-Rio (the Pontifical Catholic University of Rio de Janeiro
in Brazil).
For more information about the authors, see http://www.lua.org/authors.html .
(end of README)

497
extern/lua-5.1.5/doc/contents.html vendored Normal file
View file

@ -0,0 +1,497 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Lua 5.1 Reference Manual - contents</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=utf-8">
<STYLE TYPE="text/css">
ul {
list-style-type: none ;
}
</STYLE>
</HEAD>
<BODY>
<HR>
<H1>
<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="" BORDER=0></A>
Lua 5.1 Reference Manual
</H1>
<P>
The reference manual is the official definition of the Lua language.
For a complete introduction to Lua programming, see the book
<A HREF="http://www.lua.org/docs.html#pil">Programming in Lua</A>.
<P>
This manual is also available as a book:
<BLOCKQUOTE>
<A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">
<IMG SRC="cover.png" ALT="" TITLE="buy from Amazon" BORDER=1 ALIGN="left" HSPACE=12>
</A>
<B>Lua 5.1 Reference Manual</B>
<BR>by R. Ierusalimschy, L. H. de Figueiredo, W. Celes
<BR>Lua.org, August 2006
<BR>ISBN 85-903798-3-3
<BR CLEAR="all">
</BLOCKQUOTE>
<P>
<A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">Buy a copy</A>
of this book and
<A HREF="http://www.lua.org/donations.html">help to support</A>
the Lua project.
<P>
<A HREF="manual.html">start</A>
&middot;
<A HREF="#contents">contents</A>
&middot;
<A HREF="#index">index</A>
&middot;
<A HREF="http://www.lua.org/manual/">other versions</A>
<HR>
<SMALL>
Copyright &copy; 2006&ndash;2012 Lua.org, PUC-Rio.
Freely available under the terms of the
<A HREF="http://www.lua.org/license.html">Lua license</A>.
</SMALL>
<H2><A NAME="contents">Contents</A></H2>
<UL style="padding: 0">
<LI><A HREF="manual.html">1 &ndash; Introduction</A>
<P>
<LI><A HREF="manual.html#2">2 &ndash; The Language</A>
<UL>
<LI><A HREF="manual.html#2.1">2.1 &ndash; Lexical Conventions</A>
<LI><A HREF="manual.html#2.2">2.2 &ndash; Values and Types</A>
<UL>
<LI><A HREF="manual.html#2.2.1">2.2.1 &ndash; Coercion</A>
</UL>
<LI><A HREF="manual.html#2.3">2.3 &ndash; Variables</A>
<LI><A HREF="manual.html#2.4">2.4 &ndash; Statements</A>
<UL>
<LI><A HREF="manual.html#2.4.1">2.4.1 &ndash; Chunks</A>
<LI><A HREF="manual.html#2.4.2">2.4.2 &ndash; Blocks</A>
<LI><A HREF="manual.html#2.4.3">2.4.3 &ndash; Assignment</A>
<LI><A HREF="manual.html#2.4.4">2.4.4 &ndash; Control Structures</A>
<LI><A HREF="manual.html#2.4.5">2.4.5 &ndash; For Statement</A>
<LI><A HREF="manual.html#2.4.6">2.4.6 &ndash; Function Calls as Statements</A>
<LI><A HREF="manual.html#2.4.7">2.4.7 &ndash; Local Declarations</A>
</UL>
<LI><A HREF="manual.html#2.5">2.5 &ndash; Expressions</A>
<UL>
<LI><A HREF="manual.html#2.5.1">2.5.1 &ndash; Arithmetic Operators</A>
<LI><A HREF="manual.html#2.5.2">2.5.2 &ndash; Relational Operators</A>
<LI><A HREF="manual.html#2.5.3">2.5.3 &ndash; Logical Operators</A>
<LI><A HREF="manual.html#2.5.4">2.5.4 &ndash; Concatenation</A>
<LI><A HREF="manual.html#2.5.5">2.5.5 &ndash; The Length Operator</A>
<LI><A HREF="manual.html#2.5.6">2.5.6 &ndash; Precedence</A>
<LI><A HREF="manual.html#2.5.7">2.5.7 &ndash; Table Constructors</A>
<LI><A HREF="manual.html#2.5.8">2.5.8 &ndash; Function Calls</A>
<LI><A HREF="manual.html#2.5.9">2.5.9 &ndash; Function Definitions</A>
</UL>
<LI><A HREF="manual.html#2.6">2.6 &ndash; Visibility Rules</A>
<LI><A HREF="manual.html#2.7">2.7 &ndash; Error Handling</A>
<LI><A HREF="manual.html#2.8">2.8 &ndash; Metatables</A>
<LI><A HREF="manual.html#2.9">2.9 &ndash; Environments</A>
<LI><A HREF="manual.html#2.10">2.10 &ndash; Garbage Collection</A>
<UL>
<LI><A HREF="manual.html#2.10.1">2.10.1 &ndash; Garbage-Collection Metamethods</A>
<LI><A HREF="manual.html#2.10.2">2.10.2 &ndash; Weak Tables</A>
</UL>
<LI><A HREF="manual.html#2.11">2.11 &ndash; Coroutines</A>
</UL>
<P>
<LI><A HREF="manual.html#3">3 &ndash; The Application Program Interface</A>
<UL>
<LI><A HREF="manual.html#3.1">3.1 &ndash; The Stack</A>
<LI><A HREF="manual.html#3.2">3.2 &ndash; Stack Size</A>
<LI><A HREF="manual.html#3.3">3.3 &ndash; Pseudo-Indices</A>
<LI><A HREF="manual.html#3.4">3.4 &ndash; C Closures</A>
<LI><A HREF="manual.html#3.5">3.5 &ndash; Registry</A>
<LI><A HREF="manual.html#3.6">3.6 &ndash; Error Handling in C</A>
<LI><A HREF="manual.html#3.7">3.7 &ndash; Functions and Types</A>
<LI><A HREF="manual.html#3.8">3.8 &ndash; The Debug Interface</A>
</UL>
<P>
<LI><A HREF="manual.html#4">4 &ndash; The Auxiliary Library</A>
<UL>
<LI><A HREF="manual.html#4.1">4.1 &ndash; Functions and Types</A>
</UL>
<P>
<LI><A HREF="manual.html#5">5 &ndash; Standard Libraries</A>
<UL>
<LI><A HREF="manual.html#5.1">5.1 &ndash; Basic Functions</A>
<LI><A HREF="manual.html#5.2">5.2 &ndash; Coroutine Manipulation</A>
<LI><A HREF="manual.html#5.3">5.3 &ndash; Modules</A>
<LI><A HREF="manual.html#5.4">5.4 &ndash; String Manipulation</A>
<UL>
<LI><A HREF="manual.html#5.4.1">5.4.1 &ndash; Patterns</A>
</UL>
<LI><A HREF="manual.html#5.5">5.5 &ndash; Table Manipulation</A>
<LI><A HREF="manual.html#5.6">5.6 &ndash; Mathematical Functions</A>
<LI><A HREF="manual.html#5.7">5.7 &ndash; Input and Output Facilities</A>
<LI><A HREF="manual.html#5.8">5.8 &ndash; Operating System Facilities</A>
<LI><A HREF="manual.html#5.9">5.9 &ndash; The Debug Library</A>
</UL>
<P>
<LI><A HREF="manual.html#6">6 &ndash; Lua Stand-alone</A>
<P>
<LI><A HREF="manual.html#7">7 &ndash; Incompatibilities with the Previous Version</A>
<UL>
<LI><A HREF="manual.html#7.1">7.1 &ndash; Changes in the Language</A>
<LI><A HREF="manual.html#7.2">7.2 &ndash; Changes in the Libraries</A>
<LI><A HREF="manual.html#7.3">7.3 &ndash; Changes in the API</A>
</UL>
<P>
<LI><A HREF="manual.html#8">8 &ndash; The Complete Syntax of Lua</A>
</UL>
<H2><A NAME="index">Index</A></H2>
<TABLE WIDTH="100%">
<TR VALIGN="top">
<TD>
<H3><A NAME="functions">Lua functions</A></H3>
<A HREF="manual.html#pdf-_G">_G</A><BR>
<A HREF="manual.html#pdf-_VERSION">_VERSION</A><BR>
<P>
<A HREF="manual.html#pdf-assert">assert</A><BR>
<A HREF="manual.html#pdf-collectgarbage">collectgarbage</A><BR>
<A HREF="manual.html#pdf-dofile">dofile</A><BR>
<A HREF="manual.html#pdf-error">error</A><BR>
<A HREF="manual.html#pdf-getfenv">getfenv</A><BR>
<A HREF="manual.html#pdf-getmetatable">getmetatable</A><BR>
<A HREF="manual.html#pdf-ipairs">ipairs</A><BR>
<A HREF="manual.html#pdf-load">load</A><BR>
<A HREF="manual.html#pdf-loadfile">loadfile</A><BR>
<A HREF="manual.html#pdf-loadstring">loadstring</A><BR>
<A HREF="manual.html#pdf-module">module</A><BR>
<A HREF="manual.html#pdf-next">next</A><BR>
<A HREF="manual.html#pdf-pairs">pairs</A><BR>
<A HREF="manual.html#pdf-pcall">pcall</A><BR>
<A HREF="manual.html#pdf-print">print</A><BR>
<A HREF="manual.html#pdf-rawequal">rawequal</A><BR>
<A HREF="manual.html#pdf-rawget">rawget</A><BR>
<A HREF="manual.html#pdf-rawset">rawset</A><BR>
<A HREF="manual.html#pdf-require">require</A><BR>
<A HREF="manual.html#pdf-select">select</A><BR>
<A HREF="manual.html#pdf-setfenv">setfenv</A><BR>
<A HREF="manual.html#pdf-setmetatable">setmetatable</A><BR>
<A HREF="manual.html#pdf-tonumber">tonumber</A><BR>
<A HREF="manual.html#pdf-tostring">tostring</A><BR>
<A HREF="manual.html#pdf-type">type</A><BR>
<A HREF="manual.html#pdf-unpack">unpack</A><BR>
<A HREF="manual.html#pdf-xpcall">xpcall</A><BR>
<P>
<A HREF="manual.html#pdf-coroutine.create">coroutine.create</A><BR>
<A HREF="manual.html#pdf-coroutine.resume">coroutine.resume</A><BR>
<A HREF="manual.html#pdf-coroutine.running">coroutine.running</A><BR>
<A HREF="manual.html#pdf-coroutine.status">coroutine.status</A><BR>
<A HREF="manual.html#pdf-coroutine.wrap">coroutine.wrap</A><BR>
<A HREF="manual.html#pdf-coroutine.yield">coroutine.yield</A><BR>
<P>
<A HREF="manual.html#pdf-debug.debug">debug.debug</A><BR>
<A HREF="manual.html#pdf-debug.getfenv">debug.getfenv</A><BR>
<A HREF="manual.html#pdf-debug.gethook">debug.gethook</A><BR>
<A HREF="manual.html#pdf-debug.getinfo">debug.getinfo</A><BR>
<A HREF="manual.html#pdf-debug.getlocal">debug.getlocal</A><BR>
<A HREF="manual.html#pdf-debug.getmetatable">debug.getmetatable</A><BR>
<A HREF="manual.html#pdf-debug.getregistry">debug.getregistry</A><BR>
<A HREF="manual.html#pdf-debug.getupvalue">debug.getupvalue</A><BR>
<A HREF="manual.html#pdf-debug.setfenv">debug.setfenv</A><BR>
<A HREF="manual.html#pdf-debug.sethook">debug.sethook</A><BR>
<A HREF="manual.html#pdf-debug.setlocal">debug.setlocal</A><BR>
<A HREF="manual.html#pdf-debug.setmetatable">debug.setmetatable</A><BR>
<A HREF="manual.html#pdf-debug.setupvalue">debug.setupvalue</A><BR>
<A HREF="manual.html#pdf-debug.traceback">debug.traceback</A><BR>
</TD>
<TD>
<H3>&nbsp;</H3>
<A HREF="manual.html#pdf-file:close">file:close</A><BR>
<A HREF="manual.html#pdf-file:flush">file:flush</A><BR>
<A HREF="manual.html#pdf-file:lines">file:lines</A><BR>
<A HREF="manual.html#pdf-file:read">file:read</A><BR>
<A HREF="manual.html#pdf-file:seek">file:seek</A><BR>
<A HREF="manual.html#pdf-file:setvbuf">file:setvbuf</A><BR>
<A HREF="manual.html#pdf-file:write">file:write</A><BR>
<P>
<A HREF="manual.html#pdf-io.close">io.close</A><BR>
<A HREF="manual.html#pdf-io.flush">io.flush</A><BR>
<A HREF="manual.html#pdf-io.input">io.input</A><BR>
<A HREF="manual.html#pdf-io.lines">io.lines</A><BR>
<A HREF="manual.html#pdf-io.open">io.open</A><BR>
<A HREF="manual.html#pdf-io.output">io.output</A><BR>
<A HREF="manual.html#pdf-io.popen">io.popen</A><BR>
<A HREF="manual.html#pdf-io.read">io.read</A><BR>
<A HREF="manual.html#pdf-io.stderr">io.stderr</A><BR>
<A HREF="manual.html#pdf-io.stdin">io.stdin</A><BR>
<A HREF="manual.html#pdf-io.stdout">io.stdout</A><BR>
<A HREF="manual.html#pdf-io.tmpfile">io.tmpfile</A><BR>
<A HREF="manual.html#pdf-io.type">io.type</A><BR>
<A HREF="manual.html#pdf-io.write">io.write</A><BR>
<P>
<A HREF="manual.html#pdf-math.abs">math.abs</A><BR>
<A HREF="manual.html#pdf-math.acos">math.acos</A><BR>
<A HREF="manual.html#pdf-math.asin">math.asin</A><BR>
<A HREF="manual.html#pdf-math.atan">math.atan</A><BR>
<A HREF="manual.html#pdf-math.atan2">math.atan2</A><BR>
<A HREF="manual.html#pdf-math.ceil">math.ceil</A><BR>
<A HREF="manual.html#pdf-math.cos">math.cos</A><BR>
<A HREF="manual.html#pdf-math.cosh">math.cosh</A><BR>
<A HREF="manual.html#pdf-math.deg">math.deg</A><BR>
<A HREF="manual.html#pdf-math.exp">math.exp</A><BR>
<A HREF="manual.html#pdf-math.floor">math.floor</A><BR>
<A HREF="manual.html#pdf-math.fmod">math.fmod</A><BR>
<A HREF="manual.html#pdf-math.frexp">math.frexp</A><BR>
<A HREF="manual.html#pdf-math.huge">math.huge</A><BR>
<A HREF="manual.html#pdf-math.ldexp">math.ldexp</A><BR>
<A HREF="manual.html#pdf-math.log">math.log</A><BR>
<A HREF="manual.html#pdf-math.log10">math.log10</A><BR>
<A HREF="manual.html#pdf-math.max">math.max</A><BR>
<A HREF="manual.html#pdf-math.min">math.min</A><BR>
<A HREF="manual.html#pdf-math.modf">math.modf</A><BR>
<A HREF="manual.html#pdf-math.pi">math.pi</A><BR>
<A HREF="manual.html#pdf-math.pow">math.pow</A><BR>
<A HREF="manual.html#pdf-math.rad">math.rad</A><BR>
<A HREF="manual.html#pdf-math.random">math.random</A><BR>
<A HREF="manual.html#pdf-math.randomseed">math.randomseed</A><BR>
<A HREF="manual.html#pdf-math.sin">math.sin</A><BR>
<A HREF="manual.html#pdf-math.sinh">math.sinh</A><BR>
<A HREF="manual.html#pdf-math.sqrt">math.sqrt</A><BR>
<A HREF="manual.html#pdf-math.tan">math.tan</A><BR>
<A HREF="manual.html#pdf-math.tanh">math.tanh</A><BR>
<P>
<A HREF="manual.html#pdf-os.clock">os.clock</A><BR>
<A HREF="manual.html#pdf-os.date">os.date</A><BR>
<A HREF="manual.html#pdf-os.difftime">os.difftime</A><BR>
<A HREF="manual.html#pdf-os.execute">os.execute</A><BR>
<A HREF="manual.html#pdf-os.exit">os.exit</A><BR>
<A HREF="manual.html#pdf-os.getenv">os.getenv</A><BR>
<A HREF="manual.html#pdf-os.remove">os.remove</A><BR>
<A HREF="manual.html#pdf-os.rename">os.rename</A><BR>
<A HREF="manual.html#pdf-os.setlocale">os.setlocale</A><BR>
<A HREF="manual.html#pdf-os.time">os.time</A><BR>
<A HREF="manual.html#pdf-os.tmpname">os.tmpname</A><BR>
<P>
<A HREF="manual.html#pdf-package.cpath">package.cpath</A><BR>
<A HREF="manual.html#pdf-package.loaded">package.loaded</A><BR>
<A HREF="manual.html#pdf-package.loaders">package.loaders</A><BR>
<A HREF="manual.html#pdf-package.loadlib">package.loadlib</A><BR>
<A HREF="manual.html#pdf-package.path">package.path</A><BR>
<A HREF="manual.html#pdf-package.preload">package.preload</A><BR>
<A HREF="manual.html#pdf-package.seeall">package.seeall</A><BR>
<P>
<A HREF="manual.html#pdf-string.byte">string.byte</A><BR>
<A HREF="manual.html#pdf-string.char">string.char</A><BR>
<A HREF="manual.html#pdf-string.dump">string.dump</A><BR>
<A HREF="manual.html#pdf-string.find">string.find</A><BR>
<A HREF="manual.html#pdf-string.format">string.format</A><BR>
<A HREF="manual.html#pdf-string.gmatch">string.gmatch</A><BR>
<A HREF="manual.html#pdf-string.gsub">string.gsub</A><BR>
<A HREF="manual.html#pdf-string.len">string.len</A><BR>
<A HREF="manual.html#pdf-string.lower">string.lower</A><BR>
<A HREF="manual.html#pdf-string.match">string.match</A><BR>
<A HREF="manual.html#pdf-string.rep">string.rep</A><BR>
<A HREF="manual.html#pdf-string.reverse">string.reverse</A><BR>
<A HREF="manual.html#pdf-string.sub">string.sub</A><BR>
<A HREF="manual.html#pdf-string.upper">string.upper</A><BR>
<P>
<A HREF="manual.html#pdf-table.concat">table.concat</A><BR>
<A HREF="manual.html#pdf-table.insert">table.insert</A><BR>
<A HREF="manual.html#pdf-table.maxn">table.maxn</A><BR>
<A HREF="manual.html#pdf-table.remove">table.remove</A><BR>
<A HREF="manual.html#pdf-table.sort">table.sort</A><BR>
</TD>
<TD>
<H3>C API</H3>
<A HREF="manual.html#lua_Alloc">lua_Alloc</A><BR>
<A HREF="manual.html#lua_CFunction">lua_CFunction</A><BR>
<A HREF="manual.html#lua_Debug">lua_Debug</A><BR>
<A HREF="manual.html#lua_Hook">lua_Hook</A><BR>
<A HREF="manual.html#lua_Integer">lua_Integer</A><BR>
<A HREF="manual.html#lua_Number">lua_Number</A><BR>
<A HREF="manual.html#lua_Reader">lua_Reader</A><BR>
<A HREF="manual.html#lua_State">lua_State</A><BR>
<A HREF="manual.html#lua_Writer">lua_Writer</A><BR>
<P>
<A HREF="manual.html#lua_atpanic">lua_atpanic</A><BR>
<A HREF="manual.html#lua_call">lua_call</A><BR>
<A HREF="manual.html#lua_checkstack">lua_checkstack</A><BR>
<A HREF="manual.html#lua_close">lua_close</A><BR>
<A HREF="manual.html#lua_concat">lua_concat</A><BR>
<A HREF="manual.html#lua_cpcall">lua_cpcall</A><BR>
<A HREF="manual.html#lua_createtable">lua_createtable</A><BR>
<A HREF="manual.html#lua_dump">lua_dump</A><BR>
<A HREF="manual.html#lua_equal">lua_equal</A><BR>
<A HREF="manual.html#lua_error">lua_error</A><BR>
<A HREF="manual.html#lua_gc">lua_gc</A><BR>
<A HREF="manual.html#lua_getallocf">lua_getallocf</A><BR>
<A HREF="manual.html#lua_getfenv">lua_getfenv</A><BR>
<A HREF="manual.html#lua_getfield">lua_getfield</A><BR>
<A HREF="manual.html#lua_getglobal">lua_getglobal</A><BR>
<A HREF="manual.html#lua_gethook">lua_gethook</A><BR>
<A HREF="manual.html#lua_gethookcount">lua_gethookcount</A><BR>
<A HREF="manual.html#lua_gethookmask">lua_gethookmask</A><BR>
<A HREF="manual.html#lua_getinfo">lua_getinfo</A><BR>
<A HREF="manual.html#lua_getlocal">lua_getlocal</A><BR>
<A HREF="manual.html#lua_getmetatable">lua_getmetatable</A><BR>
<A HREF="manual.html#lua_getstack">lua_getstack</A><BR>
<A HREF="manual.html#lua_gettable">lua_gettable</A><BR>
<A HREF="manual.html#lua_gettop">lua_gettop</A><BR>
<A HREF="manual.html#lua_getupvalue">lua_getupvalue</A><BR>
<A HREF="manual.html#lua_insert">lua_insert</A><BR>
<A HREF="manual.html#lua_isboolean">lua_isboolean</A><BR>
<A HREF="manual.html#lua_iscfunction">lua_iscfunction</A><BR>
<A HREF="manual.html#lua_isfunction">lua_isfunction</A><BR>
<A HREF="manual.html#lua_islightuserdata">lua_islightuserdata</A><BR>
<A HREF="manual.html#lua_isnil">lua_isnil</A><BR>
<A HREF="manual.html#lua_isnone">lua_isnone</A><BR>
<A HREF="manual.html#lua_isnoneornil">lua_isnoneornil</A><BR>
<A HREF="manual.html#lua_isnumber">lua_isnumber</A><BR>
<A HREF="manual.html#lua_isstring">lua_isstring</A><BR>
<A HREF="manual.html#lua_istable">lua_istable</A><BR>
<A HREF="manual.html#lua_isthread">lua_isthread</A><BR>
<A HREF="manual.html#lua_isuserdata">lua_isuserdata</A><BR>
<A HREF="manual.html#lua_lessthan">lua_lessthan</A><BR>
<A HREF="manual.html#lua_load">lua_load</A><BR>
<A HREF="manual.html#lua_newstate">lua_newstate</A><BR>
<A HREF="manual.html#lua_newtable">lua_newtable</A><BR>
<A HREF="manual.html#lua_newthread">lua_newthread</A><BR>
<A HREF="manual.html#lua_newuserdata">lua_newuserdata</A><BR>
<A HREF="manual.html#lua_next">lua_next</A><BR>
<A HREF="manual.html#lua_objlen">lua_objlen</A><BR>
<A HREF="manual.html#lua_pcall">lua_pcall</A><BR>
<A HREF="manual.html#lua_pop">lua_pop</A><BR>
<A HREF="manual.html#lua_pushboolean">lua_pushboolean</A><BR>
<A HREF="manual.html#lua_pushcclosure">lua_pushcclosure</A><BR>
<A HREF="manual.html#lua_pushcfunction">lua_pushcfunction</A><BR>
<A HREF="manual.html#lua_pushfstring">lua_pushfstring</A><BR>
<A HREF="manual.html#lua_pushinteger">lua_pushinteger</A><BR>
<A HREF="manual.html#lua_pushlightuserdata">lua_pushlightuserdata</A><BR>
<A HREF="manual.html#lua_pushliteral">lua_pushliteral</A><BR>
<A HREF="manual.html#lua_pushlstring">lua_pushlstring</A><BR>
<A HREF="manual.html#lua_pushnil">lua_pushnil</A><BR>
<A HREF="manual.html#lua_pushnumber">lua_pushnumber</A><BR>
<A HREF="manual.html#lua_pushstring">lua_pushstring</A><BR>
<A HREF="manual.html#lua_pushthread">lua_pushthread</A><BR>
<A HREF="manual.html#lua_pushvalue">lua_pushvalue</A><BR>
<A HREF="manual.html#lua_pushvfstring">lua_pushvfstring</A><BR>
<A HREF="manual.html#lua_rawequal">lua_rawequal</A><BR>
<A HREF="manual.html#lua_rawget">lua_rawget</A><BR>
<A HREF="manual.html#lua_rawgeti">lua_rawgeti</A><BR>
<A HREF="manual.html#lua_rawset">lua_rawset</A><BR>
<A HREF="manual.html#lua_rawseti">lua_rawseti</A><BR>
<A HREF="manual.html#lua_register">lua_register</A><BR>
<A HREF="manual.html#lua_remove">lua_remove</A><BR>
<A HREF="manual.html#lua_replace">lua_replace</A><BR>
<A HREF="manual.html#lua_resume">lua_resume</A><BR>
<A HREF="manual.html#lua_setallocf">lua_setallocf</A><BR>
<A HREF="manual.html#lua_setfenv">lua_setfenv</A><BR>
<A HREF="manual.html#lua_setfield">lua_setfield</A><BR>
<A HREF="manual.html#lua_setglobal">lua_setglobal</A><BR>
<A HREF="manual.html#lua_sethook">lua_sethook</A><BR>
<A HREF="manual.html#lua_setlocal">lua_setlocal</A><BR>
<A HREF="manual.html#lua_setmetatable">lua_setmetatable</A><BR>
<A HREF="manual.html#lua_settable">lua_settable</A><BR>
<A HREF="manual.html#lua_settop">lua_settop</A><BR>
<A HREF="manual.html#lua_setupvalue">lua_setupvalue</A><BR>
<A HREF="manual.html#lua_status">lua_status</A><BR>
<A HREF="manual.html#lua_toboolean">lua_toboolean</A><BR>
<A HREF="manual.html#lua_tocfunction">lua_tocfunction</A><BR>
<A HREF="manual.html#lua_tointeger">lua_tointeger</A><BR>
<A HREF="manual.html#lua_tolstring">lua_tolstring</A><BR>
<A HREF="manual.html#lua_tonumber">lua_tonumber</A><BR>
<A HREF="manual.html#lua_topointer">lua_topointer</A><BR>
<A HREF="manual.html#lua_tostring">lua_tostring</A><BR>
<A HREF="manual.html#lua_tothread">lua_tothread</A><BR>
<A HREF="manual.html#lua_touserdata">lua_touserdata</A><BR>
<A HREF="manual.html#lua_type">lua_type</A><BR>
<A HREF="manual.html#lua_typename">lua_typename</A><BR>
<A HREF="manual.html#lua_upvalueindex">lua_upvalueindex</A><BR>
<A HREF="manual.html#lua_xmove">lua_xmove</A><BR>
<A HREF="manual.html#lua_yield">lua_yield</A><BR>
</TD>
<TD>
<H3>auxiliary library</H3>
<A HREF="manual.html#luaL_Buffer">luaL_Buffer</A><BR>
<A HREF="manual.html#luaL_Reg">luaL_Reg</A><BR>
<P>
<A HREF="manual.html#luaL_addchar">luaL_addchar</A><BR>
<A HREF="manual.html#luaL_addlstring">luaL_addlstring</A><BR>
<A HREF="manual.html#luaL_addsize">luaL_addsize</A><BR>
<A HREF="manual.html#luaL_addstring">luaL_addstring</A><BR>
<A HREF="manual.html#luaL_addvalue">luaL_addvalue</A><BR>
<A HREF="manual.html#luaL_argcheck">luaL_argcheck</A><BR>
<A HREF="manual.html#luaL_argerror">luaL_argerror</A><BR>
<A HREF="manual.html#luaL_buffinit">luaL_buffinit</A><BR>
<A HREF="manual.html#luaL_callmeta">luaL_callmeta</A><BR>
<A HREF="manual.html#luaL_checkany">luaL_checkany</A><BR>
<A HREF="manual.html#luaL_checkint">luaL_checkint</A><BR>
<A HREF="manual.html#luaL_checkinteger">luaL_checkinteger</A><BR>
<A HREF="manual.html#luaL_checklong">luaL_checklong</A><BR>
<A HREF="manual.html#luaL_checklstring">luaL_checklstring</A><BR>
<A HREF="manual.html#luaL_checknumber">luaL_checknumber</A><BR>
<A HREF="manual.html#luaL_checkoption">luaL_checkoption</A><BR>
<A HREF="manual.html#luaL_checkstack">luaL_checkstack</A><BR>
<A HREF="manual.html#luaL_checkstring">luaL_checkstring</A><BR>
<A HREF="manual.html#luaL_checktype">luaL_checktype</A><BR>
<A HREF="manual.html#luaL_checkudata">luaL_checkudata</A><BR>
<A HREF="manual.html#luaL_dofile">luaL_dofile</A><BR>
<A HREF="manual.html#luaL_dostring">luaL_dostring</A><BR>
<A HREF="manual.html#luaL_error">luaL_error</A><BR>
<A HREF="manual.html#luaL_getmetafield">luaL_getmetafield</A><BR>
<A HREF="manual.html#luaL_getmetatable">luaL_getmetatable</A><BR>
<A HREF="manual.html#luaL_gsub">luaL_gsub</A><BR>
<A HREF="manual.html#luaL_loadbuffer">luaL_loadbuffer</A><BR>
<A HREF="manual.html#luaL_loadfile">luaL_loadfile</A><BR>
<A HREF="manual.html#luaL_loadstring">luaL_loadstring</A><BR>
<A HREF="manual.html#luaL_newmetatable">luaL_newmetatable</A><BR>
<A HREF="manual.html#luaL_newstate">luaL_newstate</A><BR>
<A HREF="manual.html#luaL_openlibs">luaL_openlibs</A><BR>
<A HREF="manual.html#luaL_optint">luaL_optint</A><BR>
<A HREF="manual.html#luaL_optinteger">luaL_optinteger</A><BR>
<A HREF="manual.html#luaL_optlong">luaL_optlong</A><BR>
<A HREF="manual.html#luaL_optlstring">luaL_optlstring</A><BR>
<A HREF="manual.html#luaL_optnumber">luaL_optnumber</A><BR>
<A HREF="manual.html#luaL_optstring">luaL_optstring</A><BR>
<A HREF="manual.html#luaL_prepbuffer">luaL_prepbuffer</A><BR>
<A HREF="manual.html#luaL_pushresult">luaL_pushresult</A><BR>
<A HREF="manual.html#luaL_ref">luaL_ref</A><BR>
<A HREF="manual.html#luaL_register">luaL_register</A><BR>
<A HREF="manual.html#luaL_typename">luaL_typename</A><BR>
<A HREF="manual.html#luaL_typerror">luaL_typerror</A><BR>
<A HREF="manual.html#luaL_unref">luaL_unref</A><BR>
<A HREF="manual.html#luaL_where">luaL_where</A><BR>
</TD>
</TR>
</TABLE>
<P>
<HR>
<SMALL CLASS="footer">
Last update:
Mon Feb 13 18:53:32 BRST 2012
</SMALL>
<!--
Last change: revised for Lua 5.1.5
-->
</BODY>
</HTML>

BIN
extern/lua-5.1.5/doc/cover.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
extern/lua-5.1.5/doc/logo.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

163
extern/lua-5.1.5/doc/lua.1 vendored Normal file
View file

@ -0,0 +1,163 @@
.\" $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $
.TH LUA 1 "$Date: 2006/01/06 16:03:34 $"
.SH NAME
lua \- Lua interpreter
.SH SYNOPSIS
.B lua
[
.I options
]
[
.I script
[
.I args
]
]
.SH DESCRIPTION
.B lua
is the stand-alone Lua interpreter.
It loads and executes Lua programs,
either in textual source form or
in precompiled binary form.
(Precompiled binaries are output by
.BR luac ,
the Lua compiler.)
.B lua
can be used as a batch interpreter and also interactively.
.LP
The given
.I options
(see below)
are executed and then
the Lua program in file
.I script
is loaded and executed.
The given
.I args
are available to
.I script
as strings in a global table named
.BR arg .
If these arguments contain spaces or other characters special to the shell,
then they should be quoted
(but note that the quotes will be removed by the shell).
The arguments in
.B arg
start at 0,
which contains the string
.RI ' script '.
The index of the last argument is stored in
.BR arg.n .
The arguments given in the command line before
.IR script ,
including the name of the interpreter,
are available in negative indices in
.BR arg .
.LP
At the very start,
before even handling the command line,
.B lua
executes the contents of the environment variable
.BR LUA_INIT ,
if it is defined.
If the value of
.B LUA_INIT
is of the form
.RI '@ filename ',
then
.I filename
is executed.
Otherwise, the string is assumed to be a Lua statement and is executed.
.LP
Options start with
.B '\-'
and are described below.
You can use
.B "'\--'"
to signal the end of options.
.LP
If no arguments are given,
then
.B "\-v \-i"
is assumed when the standard input is a terminal;
otherwise,
.B "\-"
is assumed.
.LP
In interactive mode,
.B lua
prompts the user,
reads lines from the standard input,
and executes them as they are read.
If a line does not contain a complete statement,
then a secondary prompt is displayed and
lines are read until a complete statement is formed or
a syntax error is found.
So, one way to interrupt the reading of an incomplete statement is
to force a syntax error:
adding a
.B ';'
in the middle of a statement is a sure way of forcing a syntax error
(except inside multiline strings and comments; these must be closed explicitly).
If a line starts with
.BR '=' ,
then
.B lua
displays the values of all the expressions in the remainder of the
line. The expressions must be separated by commas.
The primary prompt is the value of the global variable
.BR _PROMPT ,
if this value is a string;
otherwise, the default prompt is used.
Similarly, the secondary prompt is the value of the global variable
.BR _PROMPT2 .
So,
to change the prompts,
set the corresponding variable to a string of your choice.
You can do that after calling the interpreter
or on the command line
(but in this case you have to be careful with quotes
if the prompt string contains a space; otherwise you may confuse the shell.)
The default prompts are "> " and ">> ".
.SH OPTIONS
.TP
.B \-
load and execute the standard input as a file,
that is,
not interactively,
even when the standard input is a terminal.
.TP
.BI \-e " stat"
execute statement
.IR stat .
You need to quote
.I stat
if it contains spaces, quotes,
or other characters special to the shell.
.TP
.B \-i
enter interactive mode after
.I script
is executed.
.TP
.BI \-l " name"
call
.BI require(' name ')
before executing
.IR script .
Typically used to load libraries.
.TP
.B \-v
show version information.
.SH "SEE ALSO"
.BR luac (1)
.br
http://www.lua.org/
.SH DIAGNOSTICS
Error messages should be self explanatory.
.SH AUTHORS
R. Ierusalimschy,
L. H. de Figueiredo,
and
W. Celes
.\" EOF

83
extern/lua-5.1.5/doc/lua.css vendored Normal file
View file

@ -0,0 +1,83 @@
body {
color: #000000 ;
background-color: #FFFFFF ;
font-family: Helvetica, Arial, sans-serif ;
text-align: justify ;
margin-right: 30px ;
margin-left: 30px ;
}
h1, h2, h3, h4 {
font-family: Verdana, Geneva, sans-serif ;
font-weight: normal ;
font-style: italic ;
}
h2 {
padding-top: 0.4em ;
padding-bottom: 0.4em ;
padding-left: 30px ;
padding-right: 30px ;
margin-left: -30px ;
background-color: #E0E0FF ;
}
h3 {
padding-left: 0.5em ;
border-left: solid #E0E0FF 1em ;
}
table h3 {
padding-left: 0px ;
border-left: none ;
}
a:link {
color: #000080 ;
background-color: inherit ;
text-decoration: none ;
}
a:visited {
background-color: inherit ;
text-decoration: none ;
}
a:link:hover, a:visited:hover {
color: #000080 ;
background-color: #E0E0FF ;
}
a:link:active, a:visited:active {
color: #FF0000 ;
}
hr {
border: 0 ;
height: 1px ;
color: #a0a0a0 ;
background-color: #a0a0a0 ;
}
:target {
background-color: #F8F8F8 ;
padding: 8px ;
border: solid #a0a0a0 2px ;
}
.footer {
color: gray ;
font-size: small ;
}
input[type=text] {
border: solid #a0a0a0 2px ;
border-radius: 2em ;
-moz-border-radius: 2em ;
background-image: url('images/search.png') ;
background-repeat: no-repeat;
background-position: 4px center ;
padding-left: 20px ;
height: 2em ;
}

172
extern/lua-5.1.5/doc/lua.html vendored Normal file
View file

@ -0,0 +1,172 @@
<!-- $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $ -->
<HTML>
<HEAD>
<TITLE>LUA man page</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H2>NAME</H2>
lua - Lua interpreter
<H2>SYNOPSIS</H2>
<B>lua</B>
[
<I>options</I>
]
[
<I>script</I>
[
<I>args</I>
]
]
<H2>DESCRIPTION</H2>
<B>lua</B>
is the stand-alone Lua interpreter.
It loads and executes Lua programs,
either in textual source form or
in precompiled binary form.
(Precompiled binaries are output by
<B>luac</B>,
the Lua compiler.)
<B>lua</B>
can be used as a batch interpreter and also interactively.
<P>
The given
<I>options</I>
(see below)
are executed and then
the Lua program in file
<I>script</I>
is loaded and executed.
The given
<I>args</I>
are available to
<I>script</I>
as strings in a global table named
<B>arg</B>.
If these arguments contain spaces or other characters special to the shell,
then they should be quoted
(but note that the quotes will be removed by the shell).
The arguments in
<B>arg</B>
start at 0,
which contains the string
'<I>script</I>'.
The index of the last argument is stored in
<B>arg.n</B>.
The arguments given in the command line before
<I>script</I>,
including the name of the interpreter,
are available in negative indices in
<B>arg</B>.
<P>
At the very start,
before even handling the command line,
<B>lua</B>
executes the contents of the environment variable
<B>LUA_INIT</B>,
if it is defined.
If the value of
<B>LUA_INIT</B>
is of the form
'@<I>filename</I>',
then
<I>filename</I>
is executed.
Otherwise, the string is assumed to be a Lua statement and is executed.
<P>
Options start with
<B>'-'</B>
and are described below.
You can use
<B>'--'</B>
to signal the end of options.
<P>
If no arguments are given,
then
<B>"-v -i"</B>
is assumed when the standard input is a terminal;
otherwise,
<B>"-"</B>
is assumed.
<P>
In interactive mode,
<B>lua</B>
prompts the user,
reads lines from the standard input,
and executes them as they are read.
If a line does not contain a complete statement,
then a secondary prompt is displayed and
lines are read until a complete statement is formed or
a syntax error is found.
So, one way to interrupt the reading of an incomplete statement is
to force a syntax error:
adding a
<B>';'</B>
in the middle of a statement is a sure way of forcing a syntax error
(except inside multiline strings and comments; these must be closed explicitly).
If a line starts with
<B>'='</B>,
then
<B>lua</B>
displays the values of all the expressions in the remainder of the
line. The expressions must be separated by commas.
The primary prompt is the value of the global variable
<B>_PROMPT</B>,
if this value is a string;
otherwise, the default prompt is used.
Similarly, the secondary prompt is the value of the global variable
<B>_PROMPT2</B>.
So,
to change the prompts,
set the corresponding variable to a string of your choice.
You can do that after calling the interpreter
or on the command line
(but in this case you have to be careful with quotes
if the prompt string contains a space; otherwise you may confuse the shell.)
The default prompts are "&gt; " and "&gt;&gt; ".
<H2>OPTIONS</H2>
<P>
<B>-</B>
load and execute the standard input as a file,
that is,
not interactively,
even when the standard input is a terminal.
<P>
<B>-e </B><I>stat</I>
execute statement
<I>stat</I>.
You need to quote
<I>stat </I>
if it contains spaces, quotes,
or other characters special to the shell.
<P>
<B>-i</B>
enter interactive mode after
<I>script</I>
is executed.
<P>
<B>-l </B><I>name</I>
call
<B>require</B>('<I>name</I>')
before executing
<I>script</I>.
Typically used to load libraries.
<P>
<B>-v</B>
show version information.
<H2>SEE ALSO</H2>
<B>luac</B>(1)
<BR>
<A HREF="http://www.lua.org/">http://www.lua.org/</A>
<H2>DIAGNOSTICS</H2>
Error messages should be self explanatory.
<H2>AUTHORS</H2>
R. Ierusalimschy,
L. H. de Figueiredo,
and
W. Celes
<!-- EOF -->
</BODY>
</HTML>

136
extern/lua-5.1.5/doc/luac.1 vendored Normal file
View file

@ -0,0 +1,136 @@
.\" $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $
.TH LUAC 1 "$Date: 2006/01/06 16:03:34 $"
.SH NAME
luac \- Lua compiler
.SH SYNOPSIS
.B luac
[
.I options
] [
.I filenames
]
.SH DESCRIPTION
.B luac
is the Lua compiler.
It translates programs written in the Lua programming language
into binary files that can be later loaded and executed.
.LP
The main advantages of precompiling chunks are:
faster loading,
protecting source code from accidental user changes,
and
off-line syntax checking.
.LP
Pre-compiling does not imply faster execution
because in Lua chunks are always compiled into bytecodes before being executed.
.B luac
simply allows those bytecodes to be saved in a file for later execution.
.LP
Pre-compiled chunks are not necessarily smaller than the corresponding source.
The main goal in pre-compiling is faster loading.
.LP
The binary files created by
.B luac
are portable only among architectures with the same word size and byte order.
.LP
.B luac
produces a single output file containing the bytecodes
for all source files given.
By default,
the output file is named
.BR luac.out ,
but you can change this with the
.B \-o
option.
.LP
In the command line,
you can mix
text files containing Lua source and
binary files containing precompiled chunks.
This is useful to combine several precompiled chunks,
even from different (but compatible) platforms,
into a single precompiled chunk.
.LP
You can use
.B "'\-'"
to indicate the standard input as a source file
and
.B "'\--'"
to signal the end of options
(that is,
all remaining arguments will be treated as files even if they start with
.BR "'\-'" ).
.LP
The internal format of the binary files produced by
.B luac
is likely to change when a new version of Lua is released.
So,
save the source files of all Lua programs that you precompile.
.LP
.SH OPTIONS
Options must be separate.
.TP
.B \-l
produce a listing of the compiled bytecode for Lua's virtual machine.
Listing bytecodes is useful to learn about Lua's virtual machine.
If no files are given, then
.B luac
loads
.B luac.out
and lists its contents.
.TP
.BI \-o " file"
output to
.IR file ,
instead of the default
.BR luac.out .
(You can use
.B "'\-'"
for standard output,
but not on platforms that open standard output in text mode.)
The output file may be a source file because
all files are loaded before the output file is written.
Be careful not to overwrite precious files.
.TP
.B \-p
load files but do not generate any output file.
Used mainly for syntax checking and for testing precompiled chunks:
corrupted files will probably generate errors when loaded.
Lua always performs a thorough integrity test on precompiled chunks.
Bytecode that passes this test is completely safe,
in the sense that it will not break the interpreter.
However,
there is no guarantee that such code does anything sensible.
(None can be given, because the halting problem is unsolvable.)
If no files are given, then
.B luac
loads
.B luac.out
and tests its contents.
No messages are displayed if the file passes the integrity test.
.TP
.B \-s
strip debug information before writing the output file.
This saves some space in very large chunks,
but if errors occur when running a stripped chunk,
then the error messages may not contain the full information they usually do.
For instance,
line numbers and names of local variables are lost.
.TP
.B \-v
show version information.
.SH FILES
.TP 15
.B luac.out
default output file
.SH "SEE ALSO"
.BR lua (1)
.br
http://www.lua.org/
.SH DIAGNOSTICS
Error messages should be self explanatory.
.SH AUTHORS
L. H. de Figueiredo,
R. Ierusalimschy and
W. Celes
.\" EOF

145
extern/lua-5.1.5/doc/luac.html vendored Normal file
View file

@ -0,0 +1,145 @@
<!-- $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $ -->
<HTML>
<HEAD>
<TITLE>LUAC man page</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H2>NAME</H2>
luac - Lua compiler
<H2>SYNOPSIS</H2>
<B>luac</B>
[
<I>options</I>
] [
<I>filenames</I>
]
<H2>DESCRIPTION</H2>
<B>luac</B>
is the Lua compiler.
It translates programs written in the Lua programming language
into binary files that can be later loaded and executed.
<P>
The main advantages of precompiling chunks are:
faster loading,
protecting source code from accidental user changes,
and
off-line syntax checking.
<P>
Precompiling does not imply faster execution
because in Lua chunks are always compiled into bytecodes before being executed.
<B>luac</B>
simply allows those bytecodes to be saved in a file for later execution.
<P>
Precompiled chunks are not necessarily smaller than the corresponding source.
The main goal in precompiling is faster loading.
<P>
The binary files created by
<B>luac</B>
are portable only among architectures with the same word size and byte order.
<P>
<B>luac</B>
produces a single output file containing the bytecodes
for all source files given.
By default,
the output file is named
<B>luac.out</B>,
but you can change this with the
<B>-o</B>
option.
<P>
In the command line,
you can mix
text files containing Lua source and
binary files containing precompiled chunks.
This is useful because several precompiled chunks,
even from different (but compatible) platforms,
can be combined into a single precompiled chunk.
<P>
You can use
<B>'-'</B>
to indicate the standard input as a source file
and
<B>'--'</B>
to signal the end of options
(that is,
all remaining arguments will be treated as files even if they start with
<B>'-'</B>).
<P>
The internal format of the binary files produced by
<B>luac</B>
is likely to change when a new version of Lua is released.
So,
save the source files of all Lua programs that you precompile.
<P>
<H2>OPTIONS</H2>
Options must be separate.
<P>
<B>-l</B>
produce a listing of the compiled bytecode for Lua's virtual machine.
Listing bytecodes is useful to learn about Lua's virtual machine.
If no files are given, then
<B>luac</B>
loads
<B>luac.out</B>
and lists its contents.
<P>
<B>-o </B><I>file</I>
output to
<I>file</I>,
instead of the default
<B>luac.out</B>.
(You can use
<B>'-'</B>
for standard output,
but not on platforms that open standard output in text mode.)
The output file may be a source file because
all files are loaded before the output file is written.
Be careful not to overwrite precious files.
<P>
<B>-p</B>
load files but do not generate any output file.
Used mainly for syntax checking and for testing precompiled chunks:
corrupted files will probably generate errors when loaded.
Lua always performs a thorough integrity test on precompiled chunks.
Bytecode that passes this test is completely safe,
in the sense that it will not break the interpreter.
However,
there is no guarantee that such code does anything sensible.
(None can be given, because the halting problem is unsolvable.)
If no files are given, then
<B>luac</B>
loads
<B>luac.out</B>
and tests its contents.
No messages are displayed if the file passes the integrity test.
<P>
<B>-s</B>
strip debug information before writing the output file.
This saves some space in very large chunks,
but if errors occur when running a stripped chunk,
then the error messages may not contain the full information they usually do.
For instance,
line numbers and names of local variables are lost.
<P>
<B>-v</B>
show version information.
<H2>FILES</H2>
<P>
<B>luac.out</B>
default output file
<H2>SEE ALSO</H2>
<B>lua</B>(1)
<BR>
<A HREF="http://www.lua.org/">http://www.lua.org/</A>
<H2>DIAGNOSTICS</H2>
Error messages should be self explanatory.
<H2>AUTHORS</H2>
L. H. de Figueiredo,
R. Ierusalimschy and
W. Celes
<!-- EOF -->
</BODY>
</HTML>

24
extern/lua-5.1.5/doc/manual.css vendored Normal file
View file

@ -0,0 +1,24 @@
h3 code {
font-family: inherit ;
font-size: inherit ;
}
pre, code {
font-size: 12pt ;
}
span.apii {
float: right ;
font-family: inherit ;
font-style: normal ;
font-size: small ;
color: gray ;
}
p+h1, ul+h1 {
padding-top: 0.4em ;
padding-bottom: 0.4em ;
padding-left: 30px ;
margin-left: -30px ;
background-color: #E0E0FF ;
}

8804
extern/lua-5.1.5/doc/manual.html vendored Normal file

File diff suppressed because it is too large Load diff

40
extern/lua-5.1.5/doc/readme.html vendored Normal file
View file

@ -0,0 +1,40 @@
<HTML>
<HEAD>
<TITLE>Lua documentation</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
</HEAD>
<BODY>
<HR>
<H1>
<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua" BORDER=0></A>
Documentation
</H1>
This is the documentation included in the source distribution of Lua 5.1.5.
<UL>
<LI><A HREF="contents.html">Reference manual</A>
<LI><A HREF="lua.html">lua man page</A>
<LI><A HREF="luac.html">luac man page</A>
<LI><A HREF="../README">lua/README</A>
<LI><A HREF="../etc/README">lua/etc/README</A>
<LI><A HREF="../test/README">lua/test/README</A>
</UL>
Lua's
<A HREF="http://www.lua.org/">official web site</A>
contains updated documentation,
especially the
<A HREF="http://www.lua.org/manual/5.1/">reference manual</A>.
<P>
<HR>
<SMALL>
Last update:
Fri Feb 3 09:44:42 BRST 2012
</SMALL>
</BODY>
</HTML>

44
extern/lua-5.1.5/etc/Makefile vendored Normal file
View file

@ -0,0 +1,44 @@
# makefile for Lua etc
TOP= ..
LIB= $(TOP)/src
INC= $(TOP)/src
BIN= $(TOP)/src
SRC= $(TOP)/src
TST= $(TOP)/test
CC= gcc
CFLAGS= -O2 -Wall -I$(INC) $(MYCFLAGS)
MYCFLAGS=
MYLDFLAGS= -Wl,-E
MYLIBS= -lm
#MYLIBS= -lm -Wl,-E -ldl -lreadline -lhistory -lncurses
RM= rm -f
default:
@echo 'Please choose a target: min noparser one strict clean'
min: min.c
$(CC) $(CFLAGS) $@.c -L$(LIB) -llua $(MYLIBS)
echo 'print"Hello there!"' | ./a.out
noparser: noparser.o
$(CC) noparser.o $(SRC)/lua.o -L$(LIB) -llua $(MYLIBS)
$(BIN)/luac $(TST)/hello.lua
-./a.out luac.out
-./a.out -e'a=1'
one:
$(CC) $(CFLAGS) all.c $(MYLIBS)
./a.out $(TST)/hello.lua
strict:
-$(BIN)/lua -e 'print(a);b=2'
-$(BIN)/lua -lstrict -e 'print(a)'
-$(BIN)/lua -e 'function f() b=2 end f()'
-$(BIN)/lua -lstrict -e 'function f() b=2 end f()'
clean:
$(RM) a.out core core.* *.o luac.out
.PHONY: default min noparser one strict clean

37
extern/lua-5.1.5/etc/README vendored Normal file
View file

@ -0,0 +1,37 @@
This directory contains some useful files and code.
Unlike the code in ../src, everything here is in the public domain.
If any of the makes fail, you're probably not using the same libraries
used to build Lua. Set MYLIBS in Makefile accordingly.
all.c
Full Lua interpreter in a single file.
Do "make one" for a demo.
lua.hpp
Lua header files for C++ using 'extern "C"'.
lua.ico
A Lua icon for Windows (and web sites: save as favicon.ico).
Drawn by hand by Markus Gritsch <gritsch@iue.tuwien.ac.at>.
lua.pc
pkg-config data for Lua
luavs.bat
Script to build Lua under "Visual Studio .NET Command Prompt".
Run it from the toplevel as etc\luavs.bat.
min.c
A minimal Lua interpreter.
Good for learning and for starting your own.
Do "make min" for a demo.
noparser.c
Linking with noparser.o avoids loading the parsing modules in lualib.a.
Do "make noparser" for a demo.
strict.lua
Traps uses of undeclared global variables.
Do "make strict" for a demo.

38
extern/lua-5.1.5/etc/all.c vendored Normal file
View file

@ -0,0 +1,38 @@
/*
* all.c -- Lua core, libraries and interpreter in a single file
*/
#define luaall_c
#include "lapi.c"
#include "lcode.c"
#include "ldebug.c"
#include "ldo.c"
#include "ldump.c"
#include "lfunc.c"
#include "lgc.c"
#include "llex.c"
#include "lmem.c"
#include "lobject.c"
#include "lopcodes.c"
#include "lparser.c"
#include "lstate.c"
#include "lstring.c"
#include "ltable.c"
#include "ltm.c"
#include "lundump.c"
#include "lvm.c"
#include "lzio.c"
#include "lauxlib.c"
#include "lbaselib.c"
#include "ldblib.c"
#include "liolib.c"
#include "linit.c"
#include "lmathlib.c"
#include "loadlib.c"
#include "loslib.c"
#include "lstrlib.c"
#include "ltablib.c"
#include "lua.c"

9
extern/lua-5.1.5/etc/lua.hpp vendored Normal file
View file

@ -0,0 +1,9 @@
// lua.hpp
// Lua header files for C++
// <<extern "C">> not supplied automatically because Lua also compiles as C++
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

BIN
extern/lua-5.1.5/etc/lua.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

31
extern/lua-5.1.5/etc/lua.pc vendored Normal file
View file

@ -0,0 +1,31 @@
# lua.pc -- pkg-config data for Lua
# vars from install Makefile
# grep '^V=' ../Makefile
V= 5.1
# grep '^R=' ../Makefile
R= 5.1.5
# grep '^INSTALL_.*=' ../Makefile | sed 's/INSTALL_TOP/prefix/'
prefix= /usr/local
INSTALL_BIN= ${prefix}/bin
INSTALL_INC= ${prefix}/include
INSTALL_LIB= ${prefix}/lib
INSTALL_MAN= ${prefix}/man/man1
INSTALL_LMOD= ${prefix}/share/lua/${V}
INSTALL_CMOD= ${prefix}/lib/lua/${V}
# canonical vars
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: Lua
Description: An Extensible Extension Language
Version: ${R}
Requires:
Libs: -L${libdir} -llua -lm
Cflags: -I${includedir}
# (end of lua.pc)

28
extern/lua-5.1.5/etc/luavs.bat vendored Normal file
View file

@ -0,0 +1,28 @@
@rem Script to build Lua under "Visual Studio .NET Command Prompt".
@rem Do not run from this directory; run it from the toplevel: etc\luavs.bat .
@rem It creates lua51.dll, lua51.lib, lua.exe, and luac.exe in src.
@rem (contributed by David Manura and Mike Pall)
@setlocal
@set MYCOMPILE=cl /nologo /MD /O2 /W3 /c /D_CRT_SECURE_NO_DEPRECATE
@set MYLINK=link /nologo
@set MYMT=mt /nologo
cd src
%MYCOMPILE% /DLUA_BUILD_AS_DLL l*.c
del lua.obj luac.obj
%MYLINK% /DLL /out:lua51.dll l*.obj
if exist lua51.dll.manifest^
%MYMT% -manifest lua51.dll.manifest -outputresource:lua51.dll;2
%MYCOMPILE% /DLUA_BUILD_AS_DLL lua.c
%MYLINK% /out:lua.exe lua.obj lua51.lib
if exist lua.exe.manifest^
%MYMT% -manifest lua.exe.manifest -outputresource:lua.exe
%MYCOMPILE% l*.c print.c
del lua.obj linit.obj lbaselib.obj ldblib.obj liolib.obj lmathlib.obj^
loslib.obj ltablib.obj lstrlib.obj loadlib.obj
%MYLINK% /out:luac.exe *.obj
if exist luac.exe.manifest^
%MYMT% -manifest luac.exe.manifest -outputresource:luac.exe
del *.obj *.manifest
cd ..

39
extern/lua-5.1.5/etc/min.c vendored Normal file
View file

@ -0,0 +1,39 @@
/*
* min.c -- a minimal Lua interpreter
* loads stdin only with minimal error handling.
* no interaction, and no standard library, only a "print" function.
*/
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"
static int print(lua_State *L)
{
int n=lua_gettop(L);
int i;
for (i=1; i<=n; i++)
{
if (i>1) printf("\t");
if (lua_isstring(L,i))
printf("%s",lua_tostring(L,i));
else if (lua_isnil(L,i))
printf("%s","nil");
else if (lua_isboolean(L,i))
printf("%s",lua_toboolean(L,i) ? "true" : "false");
else
printf("%s:%p",luaL_typename(L,i),lua_topointer(L,i));
}
printf("\n");
return 0;
}
int main(void)
{
lua_State *L=lua_open();
lua_register(L,"print",print);
if (luaL_dofile(L,NULL)!=0) fprintf(stderr,"%s\n",lua_tostring(L,-1));
lua_close(L);
return 0;
}

50
extern/lua-5.1.5/etc/noparser.c vendored Normal file
View file

@ -0,0 +1,50 @@
/*
* The code below can be used to make a Lua core that does not contain the
* parsing modules (lcode, llex, lparser), which represent 35% of the total core.
* You'll only be able to load binary files and strings, precompiled with luac.
* (Of course, you'll have to build luac with the original parsing modules!)
*
* To use this module, simply compile it ("make noparser" does that) and list
* its object file before the Lua libraries. The linker should then not load
* the parsing modules. To try it, do "make luab".
*
* If you also want to avoid the dump module (ldump.o), define NODUMP.
* #define NODUMP
*/
#define LUA_CORE
#include "llex.h"
#include "lparser.h"
#include "lzio.h"
LUAI_FUNC void luaX_init (lua_State *L) {
UNUSED(L);
}
LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
UNUSED(z);
UNUSED(buff);
UNUSED(name);
lua_pushliteral(L,"parser not loaded");
lua_error(L);
return NULL;
}
#ifdef NODUMP
#include "lundump.h"
LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) {
UNUSED(f);
UNUSED(w);
UNUSED(data);
UNUSED(strip);
#if 1
UNUSED(L);
return 0;
#else
lua_pushliteral(L,"dumper not loaded");
lua_error(L);
#endif
}
#endif

41
extern/lua-5.1.5/etc/strict.lua vendored Normal file
View file

@ -0,0 +1,41 @@
--
-- strict.lua
-- checks uses of undeclared global variables
-- All global variables must be 'declared' through a regular assignment
-- (even assigning nil will do) in a main chunk before being used
-- anywhere or assigned to inside a function.
--
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
mt.__declared = {}
local function what ()
local d = getinfo(3, "S")
return d and d.what or "C"
end
mt.__newindex = function (t, n, v)
if not mt.__declared[n] then
local w = what()
if w ~= "main" and w ~= "C" then
error("assign to undeclared variable '"..n.."'", 2)
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
mt.__index = function (t, n)
if not mt.__declared[n] and what() ~= "C" then
error("variable '"..n.."' is not declared", 2)
end
return rawget(t, n)
end

182
extern/lua-5.1.5/src/Makefile vendored Normal file
View file

@ -0,0 +1,182 @@
# makefile for building Lua
# see ../INSTALL for installation instructions
# see ../Makefile and luaconf.h for further customization
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
# Your platform. See PLATS for possible values.
PLAT= none
CC= gcc
CFLAGS= -O2 -Wall $(MYCFLAGS)
AR= ar rcu
RANLIB= ranlib
RM= rm -f
LIBS= -lm $(MYLIBS)
MYCFLAGS=
MYLDFLAGS=
MYLIBS=
# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========
PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
LUA_A= liblua.a
CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \
lundump.o lvm.o lzio.o
LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \
lstrlib.o loadlib.o linit.o
LUA_T= lua
LUA_O= lua.o
LUAC_T= luac
LUAC_O= luac.o print.o
ALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O)
ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
ALL_A= $(LUA_A)
default: $(PLAT)
all: $(ALL_T)
o: $(ALL_O)
a: $(ALL_A)
$(LUA_A): $(CORE_O) $(LIB_O)
$(AR) $@ $(CORE_O) $(LIB_O) # DLL needs all object files
$(RANLIB) $@
$(LUA_T): $(LUA_O) $(LUA_A)
$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)
$(LUAC_T): $(LUAC_O) $(LUA_A)
$(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
clean:
$(RM) $(ALL_T) $(ALL_O)
depend:
@$(CC) $(CFLAGS) -MM l*.c print.c
echo:
@echo "PLAT = $(PLAT)"
@echo "CC = $(CC)"
@echo "CFLAGS = $(CFLAGS)"
@echo "AR = $(AR)"
@echo "RANLIB = $(RANLIB)"
@echo "RM = $(RM)"
@echo "MYCFLAGS = $(MYCFLAGS)"
@echo "MYLDFLAGS = $(MYLDFLAGS)"
@echo "MYLIBS = $(MYLIBS)"
# convenience targets for popular platforms
none:
@echo "Please choose a platform:"
@echo " $(PLATS)"
aix:
$(MAKE) all CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" MYLDFLAGS="-brtl -bexpall"
ansi:
$(MAKE) all MYCFLAGS=-DLUA_ANSI
bsd:
$(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E"
freebsd:
$(MAKE) all MYCFLAGS="-DLUA_USE_LINUX" MYLIBS="-Wl,-E -lreadline"
generic:
$(MAKE) all MYCFLAGS=
linux:
$(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses"
macosx:
$(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-lreadline"
# use this on Mac OS X 10.3-
# $(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX
mingw:
$(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \
"AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
"MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe
$(MAKE) "LUAC_T=luac.exe" luac.exe
posix:
$(MAKE) all MYCFLAGS=-DLUA_USE_POSIX
solaris:
$(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl"
# list targets that do not create files (but not all makes understand .PHONY)
.PHONY: all $(PLATS) default o a clean depend echo none
# DO NOT DELETE
lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \
lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \
lundump.h lvm.h
lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h
lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h
lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \
ltable.h
ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h
ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \
llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \
lfunc.h lstring.h lgc.h ltable.h lvm.h
ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \
ltable.h lundump.h lvm.h
ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \
lzio.h lmem.h lundump.h
lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \
lstate.h ltm.h lzio.h
lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h
liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h
llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \
lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h
lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h
lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
ltm.h lzio.h lmem.h ldo.h
loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h
lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \
ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h
lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h
loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h
lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \
lfunc.h lstring.h lgc.h ltable.h
lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h
lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \
ltm.h lzio.h lstring.h lgc.h
lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h
ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h
ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h
ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \
lmem.h lstring.h lgc.h ltable.h
lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h
luac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \
lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \
lundump.h
lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h
lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h
lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \
lzio.h
print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \
ltm.h lzio.h lmem.h lopcodes.h lundump.h
# (end of Makefile)

1087
extern/lua-5.1.5/src/lapi.c vendored Normal file

File diff suppressed because it is too large Load diff

16
extern/lua-5.1.5/src/lapi.h vendored Normal file
View file

@ -0,0 +1,16 @@
/*
** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions from Lua API
** See Copyright Notice in lua.h
*/
#ifndef lapi_h
#define lapi_h
#include "lobject.h"
LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o);
#endif

652
extern/lua-5.1.5/src/lauxlib.c vendored Normal file
View file

@ -0,0 +1,652 @@
/*
** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* This file uses only the official API of Lua.
** Any function declared here could be written as an application function.
*/
#define lauxlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#define FREELIST_REF 0 /* free list of references */
/* convert a stack index to positive */
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \
lua_gettop(L) + (i) + 1)
/*
** {======================================================
** Error-report functions
** =======================================================
*/
LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) {
lua_Debug ar;
if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
return luaL_error(L, "bad argument #%d (%s)", narg, extramsg);
lua_getinfo(L, "n", &ar);
if (strcmp(ar.namewhat, "method") == 0) {
narg--; /* do not count `self' */
if (narg == 0) /* error is in the self argument itself? */
return luaL_error(L, "calling " LUA_QS " on bad self (%s)",
ar.name, extramsg);
}
if (ar.name == NULL)
ar.name = "?";
return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)",
narg, ar.name, extramsg);
}
LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) {
const char *msg = lua_pushfstring(L, "%s expected, got %s",
tname, luaL_typename(L, narg));
return luaL_argerror(L, narg, msg);
}
static void tag_error (lua_State *L, int narg, int tag) {
luaL_typerror(L, narg, lua_typename(L, tag));
}
LUALIB_API void luaL_where (lua_State *L, int level) {
lua_Debug ar;
if (lua_getstack(L, level, &ar)) { /* check function at level */
lua_getinfo(L, "Sl", &ar); /* get info about it */
if (ar.currentline > 0) { /* is there info? */
lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
return;
}
}
lua_pushliteral(L, ""); /* else, no information available... */
}
LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {
va_list argp;
va_start(argp, fmt);
luaL_where(L, 1);
lua_pushvfstring(L, fmt, argp);
va_end(argp);
lua_concat(L, 2);
return lua_error(L);
}
/* }====================================================== */
LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def,
const char *const lst[]) {
const char *name = (def) ? luaL_optstring(L, narg, def) :
luaL_checkstring(L, narg);
int i;
for (i=0; lst[i]; i++)
if (strcmp(lst[i], name) == 0)
return i;
return luaL_argerror(L, narg,
lua_pushfstring(L, "invalid option " LUA_QS, name));
}
LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */
if (!lua_isnil(L, -1)) /* name already in use? */
return 0; /* leave previous value on top, but return 0 */
lua_pop(L, 1);
lua_newtable(L); /* create metatable */
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */
return 1;
}
LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
void *p = lua_touserdata(L, ud);
if (p != NULL) { /* value is a userdata? */
if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
lua_pop(L, 2); /* remove both metatables */
return p;
}
}
}
luaL_typerror(L, ud, tname); /* else error */
return NULL; /* to avoid warnings */
}
LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) {
if (!lua_checkstack(L, space))
luaL_error(L, "stack overflow (%s)", mes);
}
LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) {
if (lua_type(L, narg) != t)
tag_error(L, narg, t);
}
LUALIB_API void luaL_checkany (lua_State *L, int narg) {
if (lua_type(L, narg) == LUA_TNONE)
luaL_argerror(L, narg, "value expected");
}
LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) {
const char *s = lua_tolstring(L, narg, len);
if (!s) tag_error(L, narg, LUA_TSTRING);
return s;
}
LUALIB_API const char *luaL_optlstring (lua_State *L, int narg,
const char *def, size_t *len) {
if (lua_isnoneornil(L, narg)) {
if (len)
*len = (def ? strlen(def) : 0);
return def;
}
else return luaL_checklstring(L, narg, len);
}
LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {
lua_Number d = lua_tonumber(L, narg);
if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
tag_error(L, narg, LUA_TNUMBER);
return d;
}
LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) {
return luaL_opt(L, luaL_checknumber, narg, def);
}
LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) {
lua_Integer d = lua_tointeger(L, narg);
if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
tag_error(L, narg, LUA_TNUMBER);
return d;
}
LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg,
lua_Integer def) {
return luaL_opt(L, luaL_checkinteger, narg, def);
}
LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) {
if (!lua_getmetatable(L, obj)) /* no metatable? */
return 0;
lua_pushstring(L, event);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) {
lua_pop(L, 2); /* remove metatable and metafield */
return 0;
}
else {
lua_remove(L, -2); /* remove only metatable */
return 1;
}
}
LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) {
obj = abs_index(L, obj);
if (!luaL_getmetafield(L, obj, event)) /* no metafield? */
return 0;
lua_pushvalue(L, obj);
lua_call(L, 1, 1);
return 1;
}
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
const luaL_Reg *l) {
luaI_openlib(L, libname, l, 0);
}
static int libsize (const luaL_Reg *l) {
int size = 0;
for (; l->name; l++) size++;
return size;
}
LUALIB_API void luaI_openlib (lua_State *L, const char *libname,
const luaL_Reg *l, int nup) {
if (libname) {
int size = libsize(l);
/* check whether lib already exists */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
lua_getfield(L, -1, libname); /* get _LOADED[libname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)
luaL_error(L, "name conflict for module " LUA_QS, libname);
lua_pushvalue(L, -1);
lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */
}
lua_remove(L, -2); /* remove _LOADED table */
lua_insert(L, -(nup+1)); /* move library table to below upvalues */
}
for (; l->name; l++) {
int i;
for (i=0; i<nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
lua_pushcclosure(L, l->func, nup);
lua_setfield(L, -(nup+2), l->name);
}
lua_pop(L, nup); /* remove upvalues */
}
/*
** {======================================================
** getn-setn: size for arrays
** =======================================================
*/
#if defined(LUA_COMPAT_GETN)
static int checkint (lua_State *L, int topop) {
int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1;
lua_pop(L, topop);
return n;
}
static void getsizes (lua_State *L) {
lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES");
if (lua_isnil(L, -1)) { /* no `size' table? */
lua_pop(L, 1); /* remove nil */
lua_newtable(L); /* create it */
lua_pushvalue(L, -1); /* `size' will be its own metatable */
lua_setmetatable(L, -2);
lua_pushliteral(L, "kv");
lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */
}
}
LUALIB_API void luaL_setn (lua_State *L, int t, int n) {
t = abs_index(L, t);
lua_pushliteral(L, "n");
lua_rawget(L, t);
if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */
lua_pushliteral(L, "n"); /* use it */
lua_pushinteger(L, n);
lua_rawset(L, t);
}
else { /* use `sizes' */
getsizes(L);
lua_pushvalue(L, t);
lua_pushinteger(L, n);
lua_rawset(L, -3); /* sizes[t] = n */
lua_pop(L, 1); /* remove `sizes' */
}
}
LUALIB_API int luaL_getn (lua_State *L, int t) {
int n;
t = abs_index(L, t);
lua_pushliteral(L, "n"); /* try t.n */
lua_rawget(L, t);
if ((n = checkint(L, 1)) >= 0) return n;
getsizes(L); /* else try sizes[t] */
lua_pushvalue(L, t);
lua_rawget(L, -2);
if ((n = checkint(L, 2)) >= 0) return n;
return (int)lua_objlen(L, t);
}
#endif
/* }====================================================== */
LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p,
const char *r) {
const char *wild;
size_t l = strlen(p);
luaL_Buffer b;
luaL_buffinit(L, &b);
while ((wild = strstr(s, p)) != NULL) {
luaL_addlstring(&b, s, wild - s); /* push prefix */
luaL_addstring(&b, r); /* push replacement in place of pattern */
s = wild + l; /* continue after `p' */
}
luaL_addstring(&b, s); /* push last suffix */
luaL_pushresult(&b);
return lua_tostring(L, -1);
}
LUALIB_API const char *luaL_findtable (lua_State *L, int idx,
const char *fname, int szhint) {
const char *e;
lua_pushvalue(L, idx);
do {
e = strchr(fname, '.');
if (e == NULL) e = fname + strlen(fname);
lua_pushlstring(L, fname, e - fname);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) { /* no such field? */
lua_pop(L, 1); /* remove this nil */
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
lua_pushlstring(L, fname, e - fname);
lua_pushvalue(L, -2);
lua_settable(L, -4); /* set new table into field */
}
else if (!lua_istable(L, -1)) { /* field has a non-table value? */
lua_pop(L, 2); /* remove table and value */
return fname; /* return problematic part of the name */
}
lua_remove(L, -2); /* remove previous table */
fname = e + 1;
} while (*e == '.');
return NULL;
}
/*
** {======================================================
** Generic Buffer manipulation
** =======================================================
*/
#define bufflen(B) ((B)->p - (B)->buffer)
#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B)))
#define LIMIT (LUA_MINSTACK/2)
static int emptybuffer (luaL_Buffer *B) {
size_t l = bufflen(B);
if (l == 0) return 0; /* put nothing on stack */
else {
lua_pushlstring(B->L, B->buffer, l);
B->p = B->buffer;
B->lvl++;
return 1;
}
}
static void adjuststack (luaL_Buffer *B) {
if (B->lvl > 1) {
lua_State *L = B->L;
int toget = 1; /* number of levels to concat */
size_t toplen = lua_strlen(L, -1);
do {
size_t l = lua_strlen(L, -(toget+1));
if (B->lvl - toget + 1 >= LIMIT || toplen > l) {
toplen += l;
toget++;
}
else break;
} while (toget < B->lvl);
lua_concat(L, toget);
B->lvl = B->lvl - toget + 1;
}
}
LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) {
if (emptybuffer(B))
adjuststack(B);
return B->buffer;
}
LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
while (l--)
luaL_addchar(B, *s++);
}
LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
luaL_addlstring(B, s, strlen(s));
}
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
emptybuffer(B);
lua_concat(B->L, B->lvl);
B->lvl = 1;
}
LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
lua_State *L = B->L;
size_t vl;
const char *s = lua_tolstring(L, -1, &vl);
if (vl <= bufffree(B)) { /* fit into buffer? */
memcpy(B->p, s, vl); /* put it there */
B->p += vl;
lua_pop(L, 1); /* remove from stack */
}
else {
if (emptybuffer(B))
lua_insert(L, -2); /* put buffer before new value */
B->lvl++; /* add new value into B stack */
adjuststack(B);
}
}
LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
B->L = L;
B->p = B->buffer;
B->lvl = 0;
}
/* }====================================================== */
LUALIB_API int luaL_ref (lua_State *L, int t) {
int ref;
t = abs_index(L, t);
if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* remove from stack */
return LUA_REFNIL; /* `nil' has a unique fixed reference */
}
lua_rawgeti(L, t, FREELIST_REF); /* get first free element */
ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */
lua_pop(L, 1); /* remove it from stack */
if (ref != 0) { /* any free element? */
lua_rawgeti(L, t, ref); /* remove it from list */
lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */
}
else { /* no free elements */
ref = (int)lua_objlen(L, t);
ref++; /* create new reference */
}
lua_rawseti(L, t, ref);
return ref;
}
LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
if (ref >= 0) {
t = abs_index(L, t);
lua_rawgeti(L, t, FREELIST_REF);
lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */
lua_pushinteger(L, ref);
lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */
}
}
/*
** {======================================================
** Load functions
** =======================================================
*/
typedef struct LoadF {
int extraline;
FILE *f;
char buff[LUAL_BUFFERSIZE];
} LoadF;
static const char *getF (lua_State *L, void *ud, size_t *size) {
LoadF *lf = (LoadF *)ud;
(void)L;
if (lf->extraline) {
lf->extraline = 0;
*size = 1;
return "\n";
}
if (feof(lf->f)) return NULL;
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
return (*size > 0) ? lf->buff : NULL;
}
static int errfile (lua_State *L, const char *what, int fnameindex) {
const char *serr = strerror(errno);
const char *filename = lua_tostring(L, fnameindex) + 1;
lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
lua_remove(L, fnameindex);
return LUA_ERRFILE;
}
LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
LoadF lf;
int status, readstatus;
int c;
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
lf.extraline = 0;
if (filename == NULL) {
lua_pushliteral(L, "=stdin");
lf.f = stdin;
}
else {
lua_pushfstring(L, "@%s", filename);
lf.f = fopen(filename, "r");
if (lf.f == NULL) return errfile(L, "open", fnameindex);
}
c = getc(lf.f);
if (c == '#') { /* Unix exec. file? */
lf.extraline = 1;
while ((c = getc(lf.f)) != EOF && c != '\n') ; /* skip first line */
if (c == '\n') c = getc(lf.f);
}
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
/* skip eventual `#!...' */
while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
lf.extraline = 0;
}
ungetc(c, lf.f);
status = lua_load(L, getF, &lf, lua_tostring(L, -1));
readstatus = ferror(lf.f);
if (filename) fclose(lf.f); /* close file (even in case of errors) */
if (readstatus) {
lua_settop(L, fnameindex); /* ignore results from `lua_load' */
return errfile(L, "read", fnameindex);
}
lua_remove(L, fnameindex);
return status;
}
typedef struct LoadS {
const char *s;
size_t size;
} LoadS;
static const char *getS (lua_State *L, void *ud, size_t *size) {
LoadS *ls = (LoadS *)ud;
(void)L;
if (ls->size == 0) return NULL;
*size = ls->size;
ls->size = 0;
return ls->s;
}
LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size,
const char *name) {
LoadS ls;
ls.s = buff;
ls.size = size;
return lua_load(L, getS, &ls, name);
}
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) {
return luaL_loadbuffer(L, s, strlen(s), s);
}
/* }====================================================== */
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud;
(void)osize;
if (nsize == 0) {
free(ptr);
return NULL;
}
else
return realloc(ptr, nsize);
}
static int panic (lua_State *L) {
(void)L; /* to avoid warnings */
fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
lua_tostring(L, -1));
return 0;
}
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
if (L) lua_atpanic(L, &panic);
return L;
}

174
extern/lua-5.1.5/src/lauxlib.h vendored Normal file
View file

@ -0,0 +1,174 @@
/*
** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
#ifndef lauxlib_h
#define lauxlib_h
#include <stddef.h>
#include <stdio.h>
#include "lua.h"
#if defined(LUA_COMPAT_GETN)
LUALIB_API int (luaL_getn) (lua_State *L, int t);
LUALIB_API void (luaL_setn) (lua_State *L, int t, int n);
#else
#define luaL_getn(L,i) ((int)lua_objlen(L, i))
#define luaL_setn(L,i,j) ((void)0) /* no op! */
#endif
#if defined(LUA_COMPAT_OPENLIB)
#define luaI_openlib luaL_openlib
#endif
/* extra error code for `luaL_load' */
#define LUA_ERRFILE (LUA_ERRERR+1)
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname,
const luaL_Reg *l, int nup);
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
const luaL_Reg *l);
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
size_t *l);
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
const char *def, size_t *l);
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
lua_Integer def);
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
const char *const lst[]);
LUALIB_API int (luaL_ref) (lua_State *L, int t);
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
const char *name);
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
const char *r);
LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
const char *fname, int szhint);
/*
** ===============================================================
** some useful macros
** ===============================================================
*/
#define luaL_argcheck(L, cond,numarg,extramsg) \
((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_dostring(L, s) \
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
/*
** {======================================================
** Generic Buffer manipulation
** =======================================================
*/
typedef struct luaL_Buffer {
char *p; /* current position in buffer */
int lvl; /* number of strings in the stack (level) */
lua_State *L;
char buffer[LUAL_BUFFERSIZE];
} luaL_Buffer;
#define luaL_addchar(B,c) \
((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
(*(B)->p++ = (char)(c)))
/* compatibility only */
#define luaL_putchar(B,c) luaL_addchar(B,c)
#define luaL_addsize(B,n) ((B)->p += (n))
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
/* }====================================================== */
/* compatibility with ref system */
/* pre-defined references */
#define LUA_NOREF (-2)
#define LUA_REFNIL (-1)
#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
(lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
#define luaL_reg luaL_Reg
#endif

653
extern/lua-5.1.5/src/lbaselib.c vendored Normal file
View file

@ -0,0 +1,653 @@
/*
** $Id: lbaselib.c,v 1.191.1.6 2008/02/14 16:46:22 roberto Exp $
** Basic library
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define lbaselib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/*
** If your system does not support `stdout', you can just remove this function.
** If you need, you can define your own `print' function, following this
** model but changing `fputs' to put the strings at a proper place
** (a console window or a log file, for instance).
*/
static int luaB_print (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
lua_getglobal(L, "tostring");
for (i=1; i<=n; i++) {
const char *s;
lua_pushvalue(L, -1); /* function to be called */
lua_pushvalue(L, i); /* value to print */
lua_call(L, 1, 1);
s = lua_tostring(L, -1); /* get result */
if (s == NULL)
return luaL_error(L, LUA_QL("tostring") " must return a string to "
LUA_QL("print"));
if (i>1) fputs("\t", stdout);
fputs(s, stdout);
lua_pop(L, 1); /* pop result */
}
fputs("\n", stdout);
return 0;
}
static int luaB_tonumber (lua_State *L) {
int base = luaL_optint(L, 2, 10);
if (base == 10) { /* standard conversion */
luaL_checkany(L, 1);
if (lua_isnumber(L, 1)) {
lua_pushnumber(L, lua_tonumber(L, 1));
return 1;
}
}
else {
const char *s1 = luaL_checkstring(L, 1);
char *s2;
unsigned long n;
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
n = strtoul(s1, &s2, base);
if (s1 != s2) { /* at least one valid digit? */
while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */
if (*s2 == '\0') { /* no invalid trailing characters? */
lua_pushnumber(L, (lua_Number)n);
return 1;
}
}
}
lua_pushnil(L); /* else not a number */
return 1;
}
static int luaB_error (lua_State *L) {
int level = luaL_optint(L, 2, 1);
lua_settop(L, 1);
if (lua_isstring(L, 1) && level > 0) { /* add extra information? */
luaL_where(L, level);
lua_pushvalue(L, 1);
lua_concat(L, 2);
}
return lua_error(L);
}
static int luaB_getmetatable (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_getmetatable(L, 1)) {
lua_pushnil(L);
return 1; /* no metatable */
}
luaL_getmetafield(L, 1, "__metatable");
return 1; /* returns either __metatable field (if present) or metatable */
}
static int luaB_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_checktype(L, 1, LUA_TTABLE);
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
"nil or table expected");
if (luaL_getmetafield(L, 1, "__metatable"))
luaL_error(L, "cannot change a protected metatable");
lua_settop(L, 2);
lua_setmetatable(L, 1);
return 1;
}
static void getfunc (lua_State *L, int opt) {
if (lua_isfunction(L, 1)) lua_pushvalue(L, 1);
else {
lua_Debug ar;
int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1);
luaL_argcheck(L, level >= 0, 1, "level must be non-negative");
if (lua_getstack(L, level, &ar) == 0)
luaL_argerror(L, 1, "invalid level");
lua_getinfo(L, "f", &ar);
if (lua_isnil(L, -1))
luaL_error(L, "no function environment for tail call at level %d",
level);
}
}
static int luaB_getfenv (lua_State *L) {
getfunc(L, 1);
if (lua_iscfunction(L, -1)) /* is a C function? */
lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */
else
lua_getfenv(L, -1);
return 1;
}
static int luaB_setfenv (lua_State *L) {
luaL_checktype(L, 2, LUA_TTABLE);
getfunc(L, 0);
lua_pushvalue(L, 2);
if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) {
/* change environment of current thread */
lua_pushthread(L);
lua_insert(L, -2);
lua_setfenv(L, -2);
return 0;
}
else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0)
luaL_error(L,
LUA_QL("setfenv") " cannot change environment of given object");
return 1;
}
static int luaB_rawequal (lua_State *L) {
luaL_checkany(L, 1);
luaL_checkany(L, 2);
lua_pushboolean(L, lua_rawequal(L, 1, 2));
return 1;
}
static int luaB_rawget (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checkany(L, 2);
lua_settop(L, 2);
lua_rawget(L, 1);
return 1;
}
static int luaB_rawset (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checkany(L, 2);
luaL_checkany(L, 3);
lua_settop(L, 3);
lua_rawset(L, 1);
return 1;
}
static int luaB_gcinfo (lua_State *L) {
lua_pushinteger(L, lua_getgccount(L));
return 1;
}
static int luaB_collectgarbage (lua_State *L) {
static const char *const opts[] = {"stop", "restart", "collect",
"count", "step", "setpause", "setstepmul", NULL};
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL};
int o = luaL_checkoption(L, 1, "collect", opts);
int ex = luaL_optint(L, 2, 0);
int res = lua_gc(L, optsnum[o], ex);
switch (optsnum[o]) {
case LUA_GCCOUNT: {
int b = lua_gc(L, LUA_GCCOUNTB, 0);
lua_pushnumber(L, res + ((lua_Number)b/1024));
return 1;
}
case LUA_GCSTEP: {
lua_pushboolean(L, res);
return 1;
}
default: {
lua_pushnumber(L, res);
return 1;
}
}
}
static int luaB_type (lua_State *L) {
luaL_checkany(L, 1);
lua_pushstring(L, luaL_typename(L, 1));
return 1;
}
static int luaB_next (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, 1))
return 2;
else {
lua_pushnil(L);
return 1;
}
}
static int luaB_pairs (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
lua_pushvalue(L, 1); /* state, */
lua_pushnil(L); /* and initial value */
return 3;
}
static int ipairsaux (lua_State *L) {
int i = luaL_checkint(L, 2);
luaL_checktype(L, 1, LUA_TTABLE);
i++; /* next value */
lua_pushinteger(L, i);
lua_rawgeti(L, 1, i);
return (lua_isnil(L, -1)) ? 0 : 2;
}
static int luaB_ipairs (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
lua_pushvalue(L, 1); /* state, */
lua_pushinteger(L, 0); /* and initial value */
return 3;
}
static int load_aux (lua_State *L, int status) {
if (status == 0) /* OK? */
return 1;
else {
lua_pushnil(L);
lua_insert(L, -2); /* put before error message */
return 2; /* return nil plus error message */
}
}
static int luaB_loadstring (lua_State *L) {
size_t l;
const char *s = luaL_checklstring(L, 1, &l);
const char *chunkname = luaL_optstring(L, 2, s);
return load_aux(L, luaL_loadbuffer(L, s, l, chunkname));
}
static int luaB_loadfile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL);
return load_aux(L, luaL_loadfile(L, fname));
}
/*
** Reader for generic `load' function: `lua_load' uses the
** stack for internal stuff, so the reader cannot change the
** stack top. Instead, it keeps its resulting string in a
** reserved slot inside the stack.
*/
static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
(void)ud; /* to avoid warnings */
luaL_checkstack(L, 2, "too many nested functions");
lua_pushvalue(L, 1); /* get function */
lua_call(L, 0, 1); /* call it */
if (lua_isnil(L, -1)) {
*size = 0;
return NULL;
}
else if (lua_isstring(L, -1)) {
lua_replace(L, 3); /* save string in a reserved stack slot */
return lua_tolstring(L, 3, size);
}
else luaL_error(L, "reader function must return a string");
return NULL; /* to avoid warnings */
}
static int luaB_load (lua_State *L) {
int status;
const char *cname = luaL_optstring(L, 2, "=(load)");
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, 3); /* function, eventual name, plus one reserved slot */
status = lua_load(L, generic_reader, NULL, cname);
return load_aux(L, status);
}
static int luaB_dofile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL);
int n = lua_gettop(L);
if (luaL_loadfile(L, fname) != 0) lua_error(L);
lua_call(L, 0, LUA_MULTRET);
return lua_gettop(L) - n;
}
static int luaB_assert (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_toboolean(L, 1))
return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!"));
return lua_gettop(L);
}
static int luaB_unpack (lua_State *L) {
int i, e, n;
luaL_checktype(L, 1, LUA_TTABLE);
i = luaL_optint(L, 2, 1);
e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
if (i > e) return 0; /* empty range */
n = e - i + 1; /* number of elements */
if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */
return luaL_error(L, "too many results to unpack");
lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */
while (i++ < e) /* push arg[i + 1...e] */
lua_rawgeti(L, 1, i);
return n;
}
static int luaB_select (lua_State *L) {
int n = lua_gettop(L);
if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
lua_pushinteger(L, n-1);
return 1;
}
else {
int i = luaL_checkint(L, 1);
if (i < 0) i = n + i;
else if (i > n) i = n;
luaL_argcheck(L, 1 <= i, 1, "index out of range");
return n - i;
}
}
static int luaB_pcall (lua_State *L) {
int status;
luaL_checkany(L, 1);
status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0);
lua_pushboolean(L, (status == 0));
lua_insert(L, 1);
return lua_gettop(L); /* return status + all results */
}
static int luaB_xpcall (lua_State *L) {
int status;
luaL_checkany(L, 2);
lua_settop(L, 2);
lua_insert(L, 1); /* put error function under function to be called */
status = lua_pcall(L, 0, LUA_MULTRET, 1);
lua_pushboolean(L, (status == 0));
lua_replace(L, 1);
return lua_gettop(L); /* return status + all results */
}
static int luaB_tostring (lua_State *L) {
luaL_checkany(L, 1);
if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */
return 1; /* use its value */
switch (lua_type(L, 1)) {
case LUA_TNUMBER:
lua_pushstring(L, lua_tostring(L, 1));
break;
case LUA_TSTRING:
lua_pushvalue(L, 1);
break;
case LUA_TBOOLEAN:
lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false"));
break;
case LUA_TNIL:
lua_pushliteral(L, "nil");
break;
default:
lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1));
break;
}
return 1;
}
static int luaB_newproxy (lua_State *L) {
lua_settop(L, 1);
lua_newuserdata(L, 0); /* create proxy */
if (lua_toboolean(L, 1) == 0)
return 1; /* no metatable */
else if (lua_isboolean(L, 1)) {
lua_newtable(L); /* create a new metatable `m' ... */
lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */
lua_pushboolean(L, 1);
lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */
}
else {
int validproxy = 0; /* to check if weaktable[metatable(u)] == true */
if (lua_getmetatable(L, 1)) {
lua_rawget(L, lua_upvalueindex(1));
validproxy = lua_toboolean(L, -1);
lua_pop(L, 1); /* remove value */
}
luaL_argcheck(L, validproxy, 1, "boolean or proxy expected");
lua_getmetatable(L, 1); /* metatable is valid; get it */
}
lua_setmetatable(L, 2);
return 1;
}
static const luaL_Reg base_funcs[] = {
{"assert", luaB_assert},
{"collectgarbage", luaB_collectgarbage},
{"dofile", luaB_dofile},
{"error", luaB_error},
{"gcinfo", luaB_gcinfo},
{"getfenv", luaB_getfenv},
{"getmetatable", luaB_getmetatable},
{"loadfile", luaB_loadfile},
{"load", luaB_load},
{"loadstring", luaB_loadstring},
{"next", luaB_next},
{"pcall", luaB_pcall},
{"print", luaB_print},
{"rawequal", luaB_rawequal},
{"rawget", luaB_rawget},
{"rawset", luaB_rawset},
{"select", luaB_select},
{"setfenv", luaB_setfenv},
{"setmetatable", luaB_setmetatable},
{"tonumber", luaB_tonumber},
{"tostring", luaB_tostring},
{"type", luaB_type},
{"unpack", luaB_unpack},
{"xpcall", luaB_xpcall},
{NULL, NULL}
};
/*
** {======================================================
** Coroutine library
** =======================================================
*/
#define CO_RUN 0 /* running */
#define CO_SUS 1 /* suspended */
#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */
#define CO_DEAD 3
static const char *const statnames[] =
{"running", "suspended", "normal", "dead"};
static int costatus (lua_State *L, lua_State *co) {
if (L == co) return CO_RUN;
switch (lua_status(co)) {
case LUA_YIELD:
return CO_SUS;
case 0: {
lua_Debug ar;
if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */
return CO_NOR; /* it is running */
else if (lua_gettop(co) == 0)
return CO_DEAD;
else
return CO_SUS; /* initial state */
}
default: /* some error occured */
return CO_DEAD;
}
}
static int luaB_costatus (lua_State *L) {
lua_State *co = lua_tothread(L, 1);
luaL_argcheck(L, co, 1, "coroutine expected");
lua_pushstring(L, statnames[costatus(L, co)]);
return 1;
}
static int auxresume (lua_State *L, lua_State *co, int narg) {
int status = costatus(L, co);
if (!lua_checkstack(co, narg))
luaL_error(L, "too many arguments to resume");
if (status != CO_SUS) {
lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);
return -1; /* error flag */
}
lua_xmove(L, co, narg);
lua_setlevel(L, co);
status = lua_resume(co, narg);
if (status == 0 || status == LUA_YIELD) {
int nres = lua_gettop(co);
if (!lua_checkstack(L, nres + 1))
luaL_error(L, "too many results to resume");
lua_xmove(co, L, nres); /* move yielded values */
return nres;
}
else {
lua_xmove(co, L, 1); /* move error message */
return -1; /* error flag */
}
}
static int luaB_coresume (lua_State *L) {
lua_State *co = lua_tothread(L, 1);
int r;
luaL_argcheck(L, co, 1, "coroutine expected");
r = auxresume(L, co, lua_gettop(L) - 1);
if (r < 0) {
lua_pushboolean(L, 0);
lua_insert(L, -2);
return 2; /* return false + error message */
}
else {
lua_pushboolean(L, 1);
lua_insert(L, -(r + 1));
return r + 1; /* return true + `resume' returns */
}
}
static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L));
if (r < 0) {
if (lua_isstring(L, -1)) { /* error object is a string? */
luaL_where(L, 1); /* add extra info */
lua_insert(L, -2);
lua_concat(L, 2);
}
lua_error(L); /* propagate error */
}
return r;
}
static int luaB_cocreate (lua_State *L) {
lua_State *NL = lua_newthread(L);
luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
"Lua function expected");
lua_pushvalue(L, 1); /* move function to top */
lua_xmove(L, NL, 1); /* move function from L to NL */
return 1;
}
static int luaB_cowrap (lua_State *L) {
luaB_cocreate(L);
lua_pushcclosure(L, luaB_auxwrap, 1);
return 1;
}
static int luaB_yield (lua_State *L) {
return lua_yield(L, lua_gettop(L));
}
static int luaB_corunning (lua_State *L) {
if (lua_pushthread(L))
lua_pushnil(L); /* main thread is not a coroutine */
return 1;
}
static const luaL_Reg co_funcs[] = {
{"create", luaB_cocreate},
{"resume", luaB_coresume},
{"running", luaB_corunning},
{"status", luaB_costatus},
{"wrap", luaB_cowrap},
{"yield", luaB_yield},
{NULL, NULL}
};
/* }====================================================== */
static void auxopen (lua_State *L, const char *name,
lua_CFunction f, lua_CFunction u) {
lua_pushcfunction(L, u);
lua_pushcclosure(L, f, 1);
lua_setfield(L, -2, name);
}
static void base_open (lua_State *L) {
/* set global _G */
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setglobal(L, "_G");
/* open lib into global table */
luaL_register(L, "_G", base_funcs);
lua_pushliteral(L, LUA_VERSION);
lua_setglobal(L, "_VERSION"); /* set global _VERSION */
/* `ipairs' and `pairs' need auxiliary functions as upvalues */
auxopen(L, "ipairs", luaB_ipairs, ipairsaux);
auxopen(L, "pairs", luaB_pairs, luaB_next);
/* `newproxy' needs a weaktable as upvalue */
lua_createtable(L, 0, 1); /* new table `w' */
lua_pushvalue(L, -1); /* `w' will be its own metatable */
lua_setmetatable(L, -2);
lua_pushliteral(L, "kv");
lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */
lua_pushcclosure(L, luaB_newproxy, 1);
lua_setglobal(L, "newproxy"); /* set global `newproxy' */
}
LUALIB_API int luaopen_base (lua_State *L) {
base_open(L);
luaL_register(L, LUA_COLIBNAME, co_funcs);
return 2;
}

831
extern/lua-5.1.5/src/lcode.c vendored Normal file
View file

@ -0,0 +1,831 @@
/*
** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
#include <stdlib.h>
#define lcode_c
#define LUA_CORE
#include "lua.h"
#include "lcode.h"
#include "ldebug.h"
#include "ldo.h"
#include "lgc.h"
#include "llex.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "ltable.h"
#define hasjumps(e) ((e)->t != (e)->f)
static int isnumeral(expdesc *e) {
return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);
}
void luaK_nil (FuncState *fs, int from, int n) {
Instruction *previous;
if (fs->pc > fs->lasttarget) { /* no jumps to current position? */
if (fs->pc == 0) { /* function start? */
if (from >= fs->nactvar)
return; /* positions are already clean */
}
else {
previous = &fs->f->code[fs->pc-1];
if (GET_OPCODE(*previous) == OP_LOADNIL) {
int pfrom = GETARG_A(*previous);
int pto = GETARG_B(*previous);
if (pfrom <= from && from <= pto+1) { /* can connect both? */
if (from+n-1 > pto)
SETARG_B(*previous, from+n-1);
return;
}
}
}
}
luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */
}
int luaK_jump (FuncState *fs) {
int jpc = fs->jpc; /* save list of jumps to here */
int j;
fs->jpc = NO_JUMP;
j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP);
luaK_concat(fs, &j, jpc); /* keep them on hold */
return j;
}
void luaK_ret (FuncState *fs, int first, int nret) {
luaK_codeABC(fs, OP_RETURN, first, nret+1, 0);
}
static int condjump (FuncState *fs, OpCode op, int A, int B, int C) {
luaK_codeABC(fs, op, A, B, C);
return luaK_jump(fs);
}
static void fixjump (FuncState *fs, int pc, int dest) {
Instruction *jmp = &fs->f->code[pc];
int offset = dest-(pc+1);
lua_assert(dest != NO_JUMP);
if (abs(offset) > MAXARG_sBx)
luaX_syntaxerror(fs->ls, "control structure too long");
SETARG_sBx(*jmp, offset);
}
/*
** returns current `pc' and marks it as a jump target (to avoid wrong
** optimizations with consecutive instructions not in the same basic block).
*/
int luaK_getlabel (FuncState *fs) {
fs->lasttarget = fs->pc;
return fs->pc;
}
static int getjump (FuncState *fs, int pc) {
int offset = GETARG_sBx(fs->f->code[pc]);
if (offset == NO_JUMP) /* point to itself represents end of list */
return NO_JUMP; /* end of list */
else
return (pc+1)+offset; /* turn offset into absolute position */
}
static Instruction *getjumpcontrol (FuncState *fs, int pc) {
Instruction *pi = &fs->f->code[pc];
if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
return pi-1;
else
return pi;
}
/*
** check whether list has any jump that do not produce a value
** (or produce an inverted value)
*/
static int need_value (FuncState *fs, int list) {
for (; list != NO_JUMP; list = getjump(fs, list)) {
Instruction i = *getjumpcontrol(fs, list);
if (GET_OPCODE(i) != OP_TESTSET) return 1;
}
return 0; /* not found */
}
static int patchtestreg (FuncState *fs, int node, int reg) {
Instruction *i = getjumpcontrol(fs, node);
if (GET_OPCODE(*i) != OP_TESTSET)
return 0; /* cannot patch other instructions */
if (reg != NO_REG && reg != GETARG_B(*i))
SETARG_A(*i, reg);
else /* no register to put value or register already has the value */
*i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));
return 1;
}
static void removevalues (FuncState *fs, int list) {
for (; list != NO_JUMP; list = getjump(fs, list))
patchtestreg(fs, list, NO_REG);
}
static void patchlistaux (FuncState *fs, int list, int vtarget, int reg,
int dtarget) {
while (list != NO_JUMP) {
int next = getjump(fs, list);
if (patchtestreg(fs, list, reg))
fixjump(fs, list, vtarget);
else
fixjump(fs, list, dtarget); /* jump to default target */
list = next;
}
}
static void dischargejpc (FuncState *fs) {
patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);
fs->jpc = NO_JUMP;
}
void luaK_patchlist (FuncState *fs, int list, int target) {
if (target == fs->pc)
luaK_patchtohere(fs, list);
else {
lua_assert(target < fs->pc);
patchlistaux(fs, list, target, NO_REG, target);
}
}
void luaK_patchtohere (FuncState *fs, int list) {
luaK_getlabel(fs);
luaK_concat(fs, &fs->jpc, list);
}
void luaK_concat (FuncState *fs, int *l1, int l2) {
if (l2 == NO_JUMP) return;
else if (*l1 == NO_JUMP)
*l1 = l2;
else {
int list = *l1;
int next;
while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
list = next;
fixjump(fs, list, l2);
}
}
void luaK_checkstack (FuncState *fs, int n) {
int newstack = fs->freereg + n;
if (newstack > fs->f->maxstacksize) {
if (newstack >= MAXSTACK)
luaX_syntaxerror(fs->ls, "function or expression too complex");
fs->f->maxstacksize = cast_byte(newstack);
}
}
void luaK_reserveregs (FuncState *fs, int n) {
luaK_checkstack(fs, n);
fs->freereg += n;
}
static void freereg (FuncState *fs, int reg) {
if (!ISK(reg) && reg >= fs->nactvar) {
fs->freereg--;
lua_assert(reg == fs->freereg);
}
}
static void freeexp (FuncState *fs, expdesc *e) {
if (e->k == VNONRELOC)
freereg(fs, e->u.s.info);
}
static int addk (FuncState *fs, TValue *k, TValue *v) {
lua_State *L = fs->L;
TValue *idx = luaH_set(L, fs->h, k);
Proto *f = fs->f;
int oldsize = f->sizek;
if (ttisnumber(idx)) {
lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));
return cast_int(nvalue(idx));
}
else { /* constant not found; create a new entry */
setnvalue(idx, cast_num(fs->nk));
luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,
MAXARG_Bx, "constant table overflow");
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
setobj(L, &f->k[fs->nk], v);
luaC_barrier(L, f, v);
return fs->nk++;
}
}
int luaK_stringK (FuncState *fs, TString *s) {
TValue o;
setsvalue(fs->L, &o, s);
return addk(fs, &o, &o);
}
int luaK_numberK (FuncState *fs, lua_Number r) {
TValue o;
setnvalue(&o, r);
return addk(fs, &o, &o);
}
static int boolK (FuncState *fs, int b) {
TValue o;
setbvalue(&o, b);
return addk(fs, &o, &o);
}
static int nilK (FuncState *fs) {
TValue k, v;
setnilvalue(&v);
/* cannot use nil as key; instead use table itself to represent nil */
sethvalue(fs->L, &k, fs->h);
return addk(fs, &k, &v);
}
void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
if (e->k == VCALL) { /* expression is an open function call? */
SETARG_C(getcode(fs, e), nresults+1);
}
else if (e->k == VVARARG) {
SETARG_B(getcode(fs, e), nresults+1);
SETARG_A(getcode(fs, e), fs->freereg);
luaK_reserveregs(fs, 1);
}
}
void luaK_setoneret (FuncState *fs, expdesc *e) {
if (e->k == VCALL) { /* expression is an open function call? */
e->k = VNONRELOC;
e->u.s.info = GETARG_A(getcode(fs, e));
}
else if (e->k == VVARARG) {
SETARG_B(getcode(fs, e), 2);
e->k = VRELOCABLE; /* can relocate its simple result */
}
}
void luaK_dischargevars (FuncState *fs, expdesc *e) {
switch (e->k) {
case VLOCAL: {
e->k = VNONRELOC;
break;
}
case VUPVAL: {
e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0);
e->k = VRELOCABLE;
break;
}
case VGLOBAL: {
e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info);
e->k = VRELOCABLE;
break;
}
case VINDEXED: {
freereg(fs, e->u.s.aux);
freereg(fs, e->u.s.info);
e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux);
e->k = VRELOCABLE;
break;
}
case VVARARG:
case VCALL: {
luaK_setoneret(fs, e);
break;
}
default: break; /* there is one value available (somewhere) */
}
}
static int code_label (FuncState *fs, int A, int b, int jump) {
luaK_getlabel(fs); /* those instructions may be jump targets */
return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);
}
static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: {
luaK_nil(fs, reg, 1);
break;
}
case VFALSE: case VTRUE: {
luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
break;
}
case VK: {
luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);
break;
}
case VKNUM: {
luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval));
break;
}
case VRELOCABLE: {
Instruction *pc = &getcode(fs, e);
SETARG_A(*pc, reg);
break;
}
case VNONRELOC: {
if (reg != e->u.s.info)
luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0);
break;
}
default: {
lua_assert(e->k == VVOID || e->k == VJMP);
return; /* nothing to do... */
}
}
e->u.s.info = reg;
e->k = VNONRELOC;
}
static void discharge2anyreg (FuncState *fs, expdesc *e) {
if (e->k != VNONRELOC) {
luaK_reserveregs(fs, 1);
discharge2reg(fs, e, fs->freereg-1);
}
}
static void exp2reg (FuncState *fs, expdesc *e, int reg) {
discharge2reg(fs, e, reg);
if (e->k == VJMP)
luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */
if (hasjumps(e)) {
int final; /* position after whole expression */
int p_f = NO_JUMP; /* position of an eventual LOAD false */
int p_t = NO_JUMP; /* position of an eventual LOAD true */
if (need_value(fs, e->t) || need_value(fs, e->f)) {
int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
p_f = code_label(fs, reg, 0, 1);
p_t = code_label(fs, reg, 1, 0);
luaK_patchtohere(fs, fj);
}
final = luaK_getlabel(fs);
patchlistaux(fs, e->f, final, reg, p_f);
patchlistaux(fs, e->t, final, reg, p_t);
}
e->f = e->t = NO_JUMP;
e->u.s.info = reg;
e->k = VNONRELOC;
}
void luaK_exp2nextreg (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
freeexp(fs, e);
luaK_reserveregs(fs, 1);
exp2reg(fs, e, fs->freereg - 1);
}
int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
if (e->k == VNONRELOC) {
if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */
if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */
exp2reg(fs, e, e->u.s.info); /* put value on it */
return e->u.s.info;
}
}
luaK_exp2nextreg(fs, e); /* default */
return e->u.s.info;
}
void luaK_exp2val (FuncState *fs, expdesc *e) {
if (hasjumps(e))
luaK_exp2anyreg(fs, e);
else
luaK_dischargevars(fs, e);
}
int luaK_exp2RK (FuncState *fs, expdesc *e) {
luaK_exp2val(fs, e);
switch (e->k) {
case VKNUM:
case VTRUE:
case VFALSE:
case VNIL: {
if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */
e->u.s.info = (e->k == VNIL) ? nilK(fs) :
(e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) :
boolK(fs, (e->k == VTRUE));
e->k = VK;
return RKASK(e->u.s.info);
}
else break;
}
case VK: {
if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */
return RKASK(e->u.s.info);
else break;
}
default: break;
}
/* not a constant in the right range: put it in a register */
return luaK_exp2anyreg(fs, e);
}
void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
switch (var->k) {
case VLOCAL: {
freeexp(fs, ex);
exp2reg(fs, ex, var->u.s.info);
return;
}
case VUPVAL: {
int e = luaK_exp2anyreg(fs, ex);
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);
break;
}
case VGLOBAL: {
int e = luaK_exp2anyreg(fs, ex);
luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);
break;
}
case VINDEXED: {
int e = luaK_exp2RK(fs, ex);
luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);
break;
}
default: {
lua_assert(0); /* invalid var kind to store */
break;
}
}
freeexp(fs, ex);
}
void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
int func;
luaK_exp2anyreg(fs, e);
freeexp(fs, e);
func = fs->freereg;
luaK_reserveregs(fs, 2);
luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key));
freeexp(fs, key);
e->u.s.info = func;
e->k = VNONRELOC;
}
static void invertjump (FuncState *fs, expdesc *e) {
Instruction *pc = getjumpcontrol(fs, e->u.s.info);
lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
GET_OPCODE(*pc) != OP_TEST);
SETARG_A(*pc, !(GETARG_A(*pc)));
}
static int jumponcond (FuncState *fs, expdesc *e, int cond) {
if (e->k == VRELOCABLE) {
Instruction ie = getcode(fs, e);
if (GET_OPCODE(ie) == OP_NOT) {
fs->pc--; /* remove previous OP_NOT */
return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
}
/* else go through */
}
discharge2anyreg(fs, e);
freeexp(fs, e);
return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond);
}
void luaK_goiftrue (FuncState *fs, expdesc *e) {
int pc; /* pc of last jump */
luaK_dischargevars(fs, e);
switch (e->k) {
case VK: case VKNUM: case VTRUE: {
pc = NO_JUMP; /* always true; do nothing */
break;
}
case VJMP: {
invertjump(fs, e);
pc = e->u.s.info;
break;
}
default: {
pc = jumponcond(fs, e, 0);
break;
}
}
luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */
luaK_patchtohere(fs, e->t);
e->t = NO_JUMP;
}
static void luaK_goiffalse (FuncState *fs, expdesc *e) {
int pc; /* pc of last jump */
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: case VFALSE: {
pc = NO_JUMP; /* always false; do nothing */
break;
}
case VJMP: {
pc = e->u.s.info;
break;
}
default: {
pc = jumponcond(fs, e, 1);
break;
}
}
luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */
luaK_patchtohere(fs, e->f);
e->f = NO_JUMP;
}
static void codenot (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: case VFALSE: {
e->k = VTRUE;
break;
}
case VK: case VKNUM: case VTRUE: {
e->k = VFALSE;
break;
}
case VJMP: {
invertjump(fs, e);
break;
}
case VRELOCABLE:
case VNONRELOC: {
discharge2anyreg(fs, e);
freeexp(fs, e);
e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0);
e->k = VRELOCABLE;
break;
}
default: {
lua_assert(0); /* cannot happen */
break;
}
}
/* interchange true and false lists */
{ int temp = e->f; e->f = e->t; e->t = temp; }
removevalues(fs, e->f);
removevalues(fs, e->t);
}
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
t->u.s.aux = luaK_exp2RK(fs, k);
t->k = VINDEXED;
}
static int constfolding (OpCode op, expdesc *e1, expdesc *e2) {
lua_Number v1, v2, r;
if (!isnumeral(e1) || !isnumeral(e2)) return 0;
v1 = e1->u.nval;
v2 = e2->u.nval;
switch (op) {
case OP_ADD: r = luai_numadd(v1, v2); break;
case OP_SUB: r = luai_numsub(v1, v2); break;
case OP_MUL: r = luai_nummul(v1, v2); break;
case OP_DIV:
if (v2 == 0) return 0; /* do not attempt to divide by 0 */
r = luai_numdiv(v1, v2); break;
case OP_MOD:
if (v2 == 0) return 0; /* do not attempt to divide by 0 */
r = luai_nummod(v1, v2); break;
case OP_POW: r = luai_numpow(v1, v2); break;
case OP_UNM: r = luai_numunm(v1); break;
case OP_LEN: return 0; /* no constant folding for 'len' */
default: lua_assert(0); r = 0; break;
}
if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */
e1->u.nval = r;
return 1;
}
static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
if (constfolding(op, e1, e2))
return;
else {
int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;
int o1 = luaK_exp2RK(fs, e1);
if (o1 > o2) {
freeexp(fs, e1);
freeexp(fs, e2);
}
else {
freeexp(fs, e2);
freeexp(fs, e1);
}
e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);
e1->k = VRELOCABLE;
}
}
static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1,
expdesc *e2) {
int o1 = luaK_exp2RK(fs, e1);
int o2 = luaK_exp2RK(fs, e2);
freeexp(fs, e2);
freeexp(fs, e1);
if (cond == 0 && op != OP_EQ) {
int temp; /* exchange args to replace by `<' or `<=' */
temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */
cond = 1;
}
e1->u.s.info = condjump(fs, op, cond, o1, o2);
e1->k = VJMP;
}
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) {
expdesc e2;
e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;
switch (op) {
case OPR_MINUS: {
if (!isnumeral(e))
luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */
codearith(fs, OP_UNM, e, &e2);
break;
}
case OPR_NOT: codenot(fs, e); break;
case OPR_LEN: {
luaK_exp2anyreg(fs, e); /* cannot operate on constants */
codearith(fs, OP_LEN, e, &e2);
break;
}
default: lua_assert(0);
}
}
void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
switch (op) {
case OPR_AND: {
luaK_goiftrue(fs, v);
break;
}
case OPR_OR: {
luaK_goiffalse(fs, v);
break;
}
case OPR_CONCAT: {
luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */
break;
}
case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
case OPR_MOD: case OPR_POW: {
if (!isnumeral(v)) luaK_exp2RK(fs, v);
break;
}
default: {
luaK_exp2RK(fs, v);
break;
}
}
}
void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) {
switch (op) {
case OPR_AND: {
lua_assert(e1->t == NO_JUMP); /* list must be closed */
luaK_dischargevars(fs, e2);
luaK_concat(fs, &e2->f, e1->f);
*e1 = *e2;
break;
}
case OPR_OR: {
lua_assert(e1->f == NO_JUMP); /* list must be closed */
luaK_dischargevars(fs, e2);
luaK_concat(fs, &e2->t, e1->t);
*e1 = *e2;
break;
}
case OPR_CONCAT: {
luaK_exp2val(fs, e2);
if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {
lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1);
freeexp(fs, e1);
SETARG_B(getcode(fs, e2), e1->u.s.info);
e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info;
}
else {
luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */
codearith(fs, OP_CONCAT, e1, e2);
}
break;
}
case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break;
case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break;
case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break;
case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break;
case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break;
case OPR_POW: codearith(fs, OP_POW, e1, e2); break;
case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break;
case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break;
case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break;
case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break;
case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break;
case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break;
default: lua_assert(0);
}
}
void luaK_fixline (FuncState *fs, int line) {
fs->f->lineinfo[fs->pc - 1] = line;
}
static int luaK_code (FuncState *fs, Instruction i, int line) {
Proto *f = fs->f;
dischargejpc(fs); /* `pc' will change */
/* put new instruction in code array */
luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction,
MAX_INT, "code size overflow");
f->code[fs->pc] = i;
/* save corresponding line information */
luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int,
MAX_INT, "code size overflow");
f->lineinfo[fs->pc] = line;
return fs->pc++;
}
int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) {
lua_assert(getOpMode(o) == iABC);
lua_assert(getBMode(o) != OpArgN || b == 0);
lua_assert(getCMode(o) != OpArgN || c == 0);
return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline);
}
int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
lua_assert(getCMode(o) == OpArgN);
return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);
}
void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
int b = (tostore == LUA_MULTRET) ? 0 : tostore;
lua_assert(tostore != 0);
if (c <= MAXARG_C)
luaK_codeABC(fs, OP_SETLIST, base, b, c);
else {
luaK_codeABC(fs, OP_SETLIST, base, b, 0);
luaK_code(fs, cast(Instruction, c), fs->ls->lastline);
}
fs->freereg = base + 1; /* free registers with list values */
}

76
extern/lua-5.1.5/src/lcode.h vendored Normal file
View file

@ -0,0 +1,76 @@
/*
** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
#ifndef lcode_h
#define lcode_h
#include "llex.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
/*
** Marks the end of a patch list. It is an invalid value both as an absolute
** address, and as a list link (would link an element to itself).
*/
#define NO_JUMP (-1)
/*
** grep "ORDER OPR" if you change these enums
*/
typedef enum BinOpr {
OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW,
OPR_CONCAT,
OPR_NE, OPR_EQ,
OPR_LT, OPR_LE, OPR_GT, OPR_GE,
OPR_AND, OPR_OR,
OPR_NOBINOPR
} BinOpr;
typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
#define getcode(fs,e) ((fs)->f->code[(e)->u.s.info])
#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx)
#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C);
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);
LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r);
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_jump (FuncState *fs);
LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
LUAI_FUNC int luaK_getlabel (FuncState *fs);
LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v);
LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2);
LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
#endif

398
extern/lua-5.1.5/src/ldblib.c vendored Normal file
View file

@ -0,0 +1,398 @@
/*
** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ldblib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static int db_getregistry (lua_State *L) {
lua_pushvalue(L, LUA_REGISTRYINDEX);
return 1;
}
static int db_getmetatable (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_getmetatable(L, 1)) {
lua_pushnil(L); /* no metatable */
}
return 1;
}
static int db_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
"nil or table expected");
lua_settop(L, 2);
lua_pushboolean(L, lua_setmetatable(L, 1));
return 1;
}
static int db_getfenv (lua_State *L) {
luaL_checkany(L, 1);
lua_getfenv(L, 1);
return 1;
}
static int db_setfenv (lua_State *L) {
luaL_checktype(L, 2, LUA_TTABLE);
lua_settop(L, 2);
if (lua_setfenv(L, 1) == 0)
luaL_error(L, LUA_QL("setfenv")
" cannot change environment of given object");
return 1;
}
static void settabss (lua_State *L, const char *i, const char *v) {
lua_pushstring(L, v);
lua_setfield(L, -2, i);
}
static void settabsi (lua_State *L, const char *i, int v) {
lua_pushinteger(L, v);
lua_setfield(L, -2, i);
}
static lua_State *getthread (lua_State *L, int *arg) {
if (lua_isthread(L, 1)) {
*arg = 1;
return lua_tothread(L, 1);
}
else {
*arg = 0;
return L;
}
}
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
if (L == L1) {
lua_pushvalue(L, -2);
lua_remove(L, -3);
}
else
lua_xmove(L1, L, 1);
lua_setfield(L, -2, fname);
}
static int db_getinfo (lua_State *L) {
lua_Debug ar;
int arg;
lua_State *L1 = getthread(L, &arg);
const char *options = luaL_optstring(L, arg+2, "flnSu");
if (lua_isnumber(L, arg+1)) {
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
lua_pushnil(L); /* level out of range */
return 1;
}
}
else if (lua_isfunction(L, arg+1)) {
lua_pushfstring(L, ">%s", options);
options = lua_tostring(L, -1);
lua_pushvalue(L, arg+1);
lua_xmove(L, L1, 1);
}
else
return luaL_argerror(L, arg+1, "function or level expected");
if (!lua_getinfo(L1, options, &ar))
return luaL_argerror(L, arg+2, "invalid option");
lua_createtable(L, 0, 2);
if (strchr(options, 'S')) {
settabss(L, "source", ar.source);
settabss(L, "short_src", ar.short_src);
settabsi(L, "linedefined", ar.linedefined);
settabsi(L, "lastlinedefined", ar.lastlinedefined);
settabss(L, "what", ar.what);
}
if (strchr(options, 'l'))
settabsi(L, "currentline", ar.currentline);
if (strchr(options, 'u'))
settabsi(L, "nups", ar.nups);
if (strchr(options, 'n')) {
settabss(L, "name", ar.name);
settabss(L, "namewhat", ar.namewhat);
}
if (strchr(options, 'L'))
treatstackoption(L, L1, "activelines");
if (strchr(options, 'f'))
treatstackoption(L, L1, "func");
return 1; /* return table */
}
static int db_getlocal (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
const char *name;
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2));
if (name) {
lua_xmove(L1, L, 1);
lua_pushstring(L, name);
lua_pushvalue(L, -2);
return 2;
}
else {
lua_pushnil(L);
return 1;
}
}
static int db_setlocal (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
luaL_checkany(L, arg+3);
lua_settop(L, arg+3);
lua_xmove(L, L1, 1);
lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
return 1;
}
static int auxupvalue (lua_State *L, int get) {
const char *name;
int n = luaL_checkint(L, 2);
luaL_checktype(L, 1, LUA_TFUNCTION);
if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
if (name == NULL) return 0;
lua_pushstring(L, name);
lua_insert(L, -(get+1));
return get + 1;
}
static int db_getupvalue (lua_State *L) {
return auxupvalue(L, 1);
}
static int db_setupvalue (lua_State *L) {
luaL_checkany(L, 3);
return auxupvalue(L, 0);
}
static const char KEY_HOOK = 'h';
static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail return"};
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushlightuserdata(L, L);
lua_rawget(L, -2);
if (lua_isfunction(L, -1)) {
lua_pushstring(L, hooknames[(int)ar->event]);
if (ar->currentline >= 0)
lua_pushinteger(L, ar->currentline);
else lua_pushnil(L);
lua_assert(lua_getinfo(L, "lS", ar));
lua_call(L, 2, 0);
}
}
static int makemask (const char *smask, int count) {
int mask = 0;
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
if (count > 0) mask |= LUA_MASKCOUNT;
return mask;
}
static char *unmakemask (int mask, char *smask) {
int i = 0;
if (mask & LUA_MASKCALL) smask[i++] = 'c';
if (mask & LUA_MASKRET) smask[i++] = 'r';
if (mask & LUA_MASKLINE) smask[i++] = 'l';
smask[i] = '\0';
return smask;
}
static void gethooktable (lua_State *L) {
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX);
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
lua_createtable(L, 0, 1);
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
}
}
static int db_sethook (lua_State *L) {
int arg, mask, count;
lua_Hook func;
lua_State *L1 = getthread(L, &arg);
if (lua_isnoneornil(L, arg+1)) {
lua_settop(L, arg+1);
func = NULL; mask = 0; count = 0; /* turn off hooks */
}
else {
const char *smask = luaL_checkstring(L, arg+2);
luaL_checktype(L, arg+1, LUA_TFUNCTION);
count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count);
}
gethooktable(L);
lua_pushlightuserdata(L, L1);
lua_pushvalue(L, arg+1);
lua_rawset(L, -3); /* set new hook */
lua_pop(L, 1); /* remove hook table */
lua_sethook(L1, func, mask, count); /* set hooks */
return 0;
}
static int db_gethook (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
char buff[5];
int mask = lua_gethookmask(L1);
lua_Hook hook = lua_gethook(L1);
if (hook != NULL && hook != hookf) /* external hook? */
lua_pushliteral(L, "external hook");
else {
gethooktable(L);
lua_pushlightuserdata(L, L1);
lua_rawget(L, -2); /* get hook */
lua_remove(L, -2); /* remove hook table */
}
lua_pushstring(L, unmakemask(mask, buff));
lua_pushinteger(L, lua_gethookcount(L1));
return 3;
}
static int db_debug (lua_State *L) {
for (;;) {
char buffer[250];
fputs("lua_debug> ", stderr);
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
lua_pcall(L, 0, 0, 0)) {
fputs(lua_tostring(L, -1), stderr);
fputs("\n", stderr);
}
lua_settop(L, 0); /* remove eventual returns */
}
}
#define LEVELS1 12 /* size of the first part of the stack */
#define LEVELS2 10 /* size of the second part of the stack */
static int db_errorfb (lua_State *L) {
int level;
int firstpart = 1; /* still before eventual `...' */
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
if (lua_isnumber(L, arg+2)) {
level = (int)lua_tointeger(L, arg+2);
lua_pop(L, 1);
}
else
level = (L == L1) ? 1 : 0; /* level 0 may be this own function */
if (lua_gettop(L) == arg)
lua_pushliteral(L, "");
else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */
else lua_pushliteral(L, "\n");
lua_pushliteral(L, "stack traceback:");
while (lua_getstack(L1, level++, &ar)) {
if (level > LEVELS1 && firstpart) {
/* no more than `LEVELS2' more levels? */
if (!lua_getstack(L1, level+LEVELS2, &ar))
level--; /* keep going */
else {
lua_pushliteral(L, "\n\t..."); /* too many levels */
while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */
level++;
}
firstpart = 0;
continue;
}
lua_pushliteral(L, "\n\t");
lua_getinfo(L1, "Snl", &ar);
lua_pushfstring(L, "%s:", ar.short_src);
if (ar.currentline > 0)
lua_pushfstring(L, "%d:", ar.currentline);
if (*ar.namewhat != '\0') /* is there a name? */
lua_pushfstring(L, " in function " LUA_QS, ar.name);
else {
if (*ar.what == 'm') /* main? */
lua_pushfstring(L, " in main chunk");
else if (*ar.what == 'C' || *ar.what == 't')
lua_pushliteral(L, " ?"); /* C function or tail call */
else
lua_pushfstring(L, " in function <%s:%d>",
ar.short_src, ar.linedefined);
}
lua_concat(L, lua_gettop(L) - arg);
}
lua_concat(L, lua_gettop(L) - arg);
return 1;
}
static const luaL_Reg dblib[] = {
{"debug", db_debug},
{"getfenv", db_getfenv},
{"gethook", db_gethook},
{"getinfo", db_getinfo},
{"getlocal", db_getlocal},
{"getregistry", db_getregistry},
{"getmetatable", db_getmetatable},
{"getupvalue", db_getupvalue},
{"setfenv", db_setfenv},
{"sethook", db_sethook},
{"setlocal", db_setlocal},
{"setmetatable", db_setmetatable},
{"setupvalue", db_setupvalue},
{"traceback", db_errorfb},
{NULL, NULL}
};
LUALIB_API int luaopen_debug (lua_State *L) {
luaL_register(L, LUA_DBLIBNAME, dblib);
return 1;
}

638
extern/lua-5.1.5/src/ldebug.c vendored Normal file
View file

@ -0,0 +1,638 @@
/*
** $Id: ldebug.c,v 2.29.1.6 2008/05/08 16:56:26 roberto Exp $
** Debug Interface
** See Copyright Notice in lua.h
*/
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#define ldebug_c
#define LUA_CORE
#include "lua.h"
#include "lapi.h"
#include "lcode.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lvm.h"
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name);
static int currentpc (lua_State *L, CallInfo *ci) {
if (!isLua(ci)) return -1; /* function is not a Lua function? */
if (ci == L->ci)
ci->savedpc = L->savedpc;
return pcRel(ci->savedpc, ci_func(ci)->l.p);
}
static int currentline (lua_State *L, CallInfo *ci) {
int pc = currentpc(L, ci);
if (pc < 0)
return -1; /* only active lua functions have current-line information */
else
return getline(ci_func(ci)->l.p, pc);
}
/*
** this function can be called asynchronous (e.g. during a signal)
*/
LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
if (func == NULL || mask == 0) { /* turn off hooks? */
mask = 0;
func = NULL;
}
L->hook = func;
L->basehookcount = count;
resethookcount(L);
L->hookmask = cast_byte(mask);
return 1;
}
LUA_API lua_Hook lua_gethook (lua_State *L) {
return L->hook;
}
LUA_API int lua_gethookmask (lua_State *L) {
return L->hookmask;
}
LUA_API int lua_gethookcount (lua_State *L) {
return L->basehookcount;
}
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
int status;
CallInfo *ci;
lua_lock(L);
for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) {
level--;
if (f_isLua(ci)) /* Lua function? */
level -= ci->tailcalls; /* skip lost tail calls */
}
if (level == 0 && ci > L->base_ci) { /* level found? */
status = 1;
ar->i_ci = cast_int(ci - L->base_ci);
}
else if (level < 0) { /* level is of a lost tail call? */
status = 1;
ar->i_ci = 0;
}
else status = 0; /* no such level */
lua_unlock(L);
return status;
}
static Proto *getluaproto (CallInfo *ci) {
return (isLua(ci) ? ci_func(ci)->l.p : NULL);
}
static const char *findlocal (lua_State *L, CallInfo *ci, int n) {
const char *name;
Proto *fp = getluaproto(ci);
if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL)
return name; /* is a local variable in a Lua function */
else {
StkId limit = (ci == L->ci) ? L->top : (ci+1)->func;
if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */
return "(*temporary)";
else
return NULL;
}
}
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
CallInfo *ci = L->base_ci + ar->i_ci;
const char *name = findlocal(L, ci, n);
lua_lock(L);
if (name)
luaA_pushobject(L, ci->base + (n - 1));
lua_unlock(L);
return name;
}
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
CallInfo *ci = L->base_ci + ar->i_ci;
const char *name = findlocal(L, ci, n);
lua_lock(L);
if (name)
setobjs2s(L, ci->base + (n - 1), L->top - 1);
L->top--; /* pop value */
lua_unlock(L);
return name;
}
static void funcinfo (lua_Debug *ar, Closure *cl) {
if (cl->c.isC) {
ar->source = "=[C]";
ar->linedefined = -1;
ar->lastlinedefined = -1;
ar->what = "C";
}
else {
ar->source = getstr(cl->l.p->source);
ar->linedefined = cl->l.p->linedefined;
ar->lastlinedefined = cl->l.p->lastlinedefined;
ar->what = (ar->linedefined == 0) ? "main" : "Lua";
}
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
}
static void info_tailcall (lua_Debug *ar) {
ar->name = ar->namewhat = "";
ar->what = "tail";
ar->lastlinedefined = ar->linedefined = ar->currentline = -1;
ar->source = "=(tail call)";
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
ar->nups = 0;
}
static void collectvalidlines (lua_State *L, Closure *f) {
if (f == NULL || f->c.isC) {
setnilvalue(L->top);
}
else {
Table *t = luaH_new(L, 0, 0);
int *lineinfo = f->l.p->lineinfo;
int i;
for (i=0; i<f->l.p->sizelineinfo; i++)
setbvalue(luaH_setnum(L, t, lineinfo[i]), 1);
sethvalue(L, L->top, t);
}
incr_top(L);
}
static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
Closure *f, CallInfo *ci) {
int status = 1;
if (f == NULL) {
info_tailcall(ar);
return status;
}
for (; *what; what++) {
switch (*what) {
case 'S': {
funcinfo(ar, f);
break;
}
case 'l': {
ar->currentline = (ci) ? currentline(L, ci) : -1;
break;
}
case 'u': {
ar->nups = f->c.nupvalues;
break;
}
case 'n': {
ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL;
if (ar->namewhat == NULL) {
ar->namewhat = ""; /* not found */
ar->name = NULL;
}
break;
}
case 'L':
case 'f': /* handled by lua_getinfo */
break;
default: status = 0; /* invalid option */
}
}
return status;
}
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
int status;
Closure *f = NULL;
CallInfo *ci = NULL;
lua_lock(L);
if (*what == '>') {
StkId func = L->top - 1;
luai_apicheck(L, ttisfunction(func));
what++; /* skip the '>' */
f = clvalue(func);
L->top--; /* pop function */
}
else if (ar->i_ci != 0) { /* no tail call? */
ci = L->base_ci + ar->i_ci;
lua_assert(ttisfunction(ci->func));
f = clvalue(ci->func);
}
status = auxgetinfo(L, what, ar, f, ci);
if (strchr(what, 'f')) {
if (f == NULL) setnilvalue(L->top);
else setclvalue(L, L->top, f);
incr_top(L);
}
if (strchr(what, 'L'))
collectvalidlines(L, f);
lua_unlock(L);
return status;
}
/*
** {======================================================
** Symbolic Execution and code checker
** =======================================================
*/
#define check(x) if (!(x)) return 0;
#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode)
#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize)
static int precheck (const Proto *pt) {
check(pt->maxstacksize <= MAXSTACK);
check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
check(!(pt->is_vararg & VARARG_NEEDSARG) ||
(pt->is_vararg & VARARG_HASARG));
check(pt->sizeupvalues <= pt->nups);
check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0);
check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
return 1;
}
#define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1])
int luaG_checkopenop (Instruction i) {
switch (GET_OPCODE(i)) {
case OP_CALL:
case OP_TAILCALL:
case OP_RETURN:
case OP_SETLIST: {
check(GETARG_B(i) == 0);
return 1;
}
default: return 0; /* invalid instruction after an open call */
}
}
static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) {
switch (mode) {
case OpArgN: check(r == 0); break;
case OpArgU: break;
case OpArgR: checkreg(pt, r); break;
case OpArgK:
check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize);
break;
}
return 1;
}
static Instruction symbexec (const Proto *pt, int lastpc, int reg) {
int pc;
int last; /* stores position of last instruction that changed `reg' */
last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */
check(precheck(pt));
for (pc = 0; pc < lastpc; pc++) {
Instruction i = pt->code[pc];
OpCode op = GET_OPCODE(i);
int a = GETARG_A(i);
int b = 0;
int c = 0;
check(op < NUM_OPCODES);
checkreg(pt, a);
switch (getOpMode(op)) {
case iABC: {
b = GETARG_B(i);
c = GETARG_C(i);
check(checkArgMode(pt, b, getBMode(op)));
check(checkArgMode(pt, c, getCMode(op)));
break;
}
case iABx: {
b = GETARG_Bx(i);
if (getBMode(op) == OpArgK) check(b < pt->sizek);
break;
}
case iAsBx: {
b = GETARG_sBx(i);
if (getBMode(op) == OpArgR) {
int dest = pc+1+b;
check(0 <= dest && dest < pt->sizecode);
if (dest > 0) {
int j;
/* check that it does not jump to a setlist count; this
is tricky, because the count from a previous setlist may
have the same value of an invalid setlist; so, we must
go all the way back to the first of them (if any) */
for (j = 0; j < dest; j++) {
Instruction d = pt->code[dest-1-j];
if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break;
}
/* if 'j' is even, previous value is not a setlist (even if
it looks like one) */
check((j&1) == 0);
}
}
break;
}
}
if (testAMode(op)) {
if (a == reg) last = pc; /* change register `a' */
}
if (testTMode(op)) {
check(pc+2 < pt->sizecode); /* check skip */
check(GET_OPCODE(pt->code[pc+1]) == OP_JMP);
}
switch (op) {
case OP_LOADBOOL: {
if (c == 1) { /* does it jump? */
check(pc+2 < pt->sizecode); /* check its jump */
check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST ||
GETARG_C(pt->code[pc+1]) != 0);
}
break;
}
case OP_LOADNIL: {
if (a <= reg && reg <= b)
last = pc; /* set registers from `a' to `b' */
break;
}
case OP_GETUPVAL:
case OP_SETUPVAL: {
check(b < pt->nups);
break;
}
case OP_GETGLOBAL:
case OP_SETGLOBAL: {
check(ttisstring(&pt->k[b]));
break;
}
case OP_SELF: {
checkreg(pt, a+1);
if (reg == a+1) last = pc;
break;
}
case OP_CONCAT: {
check(b < c); /* at least two operands */
break;
}
case OP_TFORLOOP: {
check(c >= 1); /* at least one result (control variable) */
checkreg(pt, a+2+c); /* space for results */
if (reg >= a+2) last = pc; /* affect all regs above its base */
break;
}
case OP_FORLOOP:
case OP_FORPREP:
checkreg(pt, a+3);
/* go through */
case OP_JMP: {
int dest = pc+1+b;
/* not full check and jump is forward and do not skip `lastpc'? */
if (reg != NO_REG && pc < dest && dest <= lastpc)
pc += b; /* do the jump */
break;
}
case OP_CALL:
case OP_TAILCALL: {
if (b != 0) {
checkreg(pt, a+b-1);
}
c--; /* c = num. returns */
if (c == LUA_MULTRET) {
check(checkopenop(pt, pc));
}
else if (c != 0)
checkreg(pt, a+c-1);
if (reg >= a) last = pc; /* affect all registers above base */
break;
}
case OP_RETURN: {
b--; /* b = num. returns */
if (b > 0) checkreg(pt, a+b-1);
break;
}
case OP_SETLIST: {
if (b > 0) checkreg(pt, a + b);
if (c == 0) {
pc++;
check(pc < pt->sizecode - 1);
}
break;
}
case OP_CLOSURE: {
int nup, j;
check(b < pt->sizep);
nup = pt->p[b]->nups;
check(pc + nup < pt->sizecode);
for (j = 1; j <= nup; j++) {
OpCode op1 = GET_OPCODE(pt->code[pc + j]);
check(op1 == OP_GETUPVAL || op1 == OP_MOVE);
}
if (reg != NO_REG) /* tracing? */
pc += nup; /* do not 'execute' these pseudo-instructions */
break;
}
case OP_VARARG: {
check((pt->is_vararg & VARARG_ISVARARG) &&
!(pt->is_vararg & VARARG_NEEDSARG));
b--;
if (b == LUA_MULTRET) check(checkopenop(pt, pc));
checkreg(pt, a+b-1);
break;
}
default: break;
}
}
return pt->code[last];
}
#undef check
#undef checkjump
#undef checkreg
/* }====================================================== */
int luaG_checkcode (const Proto *pt) {
return (symbexec(pt, pt->sizecode, NO_REG) != 0);
}
static const char *kname (Proto *p, int c) {
if (ISK(c) && ttisstring(&p->k[INDEXK(c)]))
return svalue(&p->k[INDEXK(c)]);
else
return "?";
}
static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos,
const char **name) {
if (isLua(ci)) { /* a Lua function? */
Proto *p = ci_func(ci)->l.p;
int pc = currentpc(L, ci);
Instruction i;
*name = luaF_getlocalname(p, stackpos+1, pc);
if (*name) /* is a local? */
return "local";
i = symbexec(p, pc, stackpos); /* try symbolic execution */
lua_assert(pc != -1);
switch (GET_OPCODE(i)) {
case OP_GETGLOBAL: {
int g = GETARG_Bx(i); /* global index */
lua_assert(ttisstring(&p->k[g]));
*name = svalue(&p->k[g]);
return "global";
}
case OP_MOVE: {
int a = GETARG_A(i);
int b = GETARG_B(i); /* move from `b' to `a' */
if (b < a)
return getobjname(L, ci, b, name); /* get name for `b' */
break;
}
case OP_GETTABLE: {
int k = GETARG_C(i); /* key index */
*name = kname(p, k);
return "field";
}
case OP_GETUPVAL: {
int u = GETARG_B(i); /* upvalue index */
*name = p->upvalues ? getstr(p->upvalues[u]) : "?";
return "upvalue";
}
case OP_SELF: {
int k = GETARG_C(i); /* key index */
*name = kname(p, k);
return "method";
}
default: break;
}
}
return NULL; /* no useful name found */
}
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
Instruction i;
if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1))
return NULL; /* calling function is not Lua (or is unknown) */
ci--; /* calling function */
i = ci_func(ci)->l.p->code[currentpc(L, ci)];
if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL ||
GET_OPCODE(i) == OP_TFORLOOP)
return getobjname(L, ci, GETARG_A(i), name);
else
return NULL; /* no useful name can be found */
}
/* only ANSI way to check whether a pointer points to an array */
static int isinstack (CallInfo *ci, const TValue *o) {
StkId p;
for (p = ci->base; p < ci->top; p++)
if (o == p) return 1;
return 0;
}
void luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
const char *name = NULL;
const char *t = luaT_typenames[ttype(o)];
const char *kind = (isinstack(L->ci, o)) ?
getobjname(L, L->ci, cast_int(o - L->base), &name) :
NULL;
if (kind)
luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)",
op, kind, name, t);
else
luaG_runerror(L, "attempt to %s a %s value", op, t);
}
void luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
lua_assert(!ttisstring(p1) && !ttisnumber(p1));
luaG_typeerror(L, p1, "concatenate");
}
void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) {
TValue temp;
if (luaV_tonumber(p1, &temp) == NULL)
p2 = p1; /* first operand is wrong */
luaG_typeerror(L, p2, "perform arithmetic on");
}
int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
const char *t1 = luaT_typenames[ttype(p1)];
const char *t2 = luaT_typenames[ttype(p2)];
if (t1[2] == t2[2])
luaG_runerror(L, "attempt to compare two %s values", t1);
else
luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
return 0;
}
static void addinfo (lua_State *L, const char *msg) {
CallInfo *ci = L->ci;
if (isLua(ci)) { /* is Lua code? */
char buff[LUA_IDSIZE]; /* add file:line information */
int line = currentline(L, ci);
luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE);
luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
}
}
void luaG_errormsg (lua_State *L) {
if (L->errfunc != 0) { /* is there an error handling function? */
StkId errfunc = restorestack(L, L->errfunc);
if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR);
setobjs2s(L, L->top, L->top - 1); /* move argument */
setobjs2s(L, L->top - 1, errfunc); /* push function */
incr_top(L);
luaD_call(L, L->top - 2, 1); /* call it */
}
luaD_throw(L, LUA_ERRRUN);
}
void luaG_runerror (lua_State *L, const char *fmt, ...) {
va_list argp;
va_start(argp, fmt);
addinfo(L, luaO_pushvfstring(L, fmt, argp));
va_end(argp);
luaG_errormsg(L);
}

33
extern/lua-5.1.5/src/ldebug.h vendored Normal file
View file

@ -0,0 +1,33 @@
/*
** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions from Debug Interface module
** See Copyright Notice in lua.h
*/
#ifndef ldebug_h
#define ldebug_h
#include "lstate.h"
#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1)
#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0)
#define resethookcount(L) (L->hookcount = L->basehookcount)
LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o,
const char *opname);
LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2);
LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...);
LUAI_FUNC void luaG_errormsg (lua_State *L);
LUAI_FUNC int luaG_checkcode (const Proto *pt);
LUAI_FUNC int luaG_checkopenop (Instruction i);
#endif

519
extern/lua-5.1.5/src/ldo.c vendored Normal file
View file

@ -0,0 +1,519 @@
/*
** $Id: ldo.c,v 2.38.1.4 2012/01/18 02:27:10 roberto Exp $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
#define ldo_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lundump.h"
#include "lvm.h"
#include "lzio.h"
/*
** {======================================================
** Error-recovery functions
** =======================================================
*/
/* chain list of long jump buffers */
struct lua_longjmp {
struct lua_longjmp *previous;
luai_jmpbuf b;
volatile int status; /* error code */
};
void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
switch (errcode) {
case LUA_ERRMEM: {
setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG));
break;
}
case LUA_ERRERR: {
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
break;
}
case LUA_ERRSYNTAX:
case LUA_ERRRUN: {
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
break;
}
}
L->top = oldtop + 1;
}
static void restore_stack_limit (lua_State *L) {
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */
int inuse = cast_int(L->ci - L->base_ci);
if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */
luaD_reallocCI(L, LUAI_MAXCALLS);
}
}
static void resetstack (lua_State *L, int status) {
L->ci = L->base_ci;
L->base = L->ci->base;
luaF_close(L, L->base); /* close eventual pending closures */
luaD_seterrorobj(L, status, L->base);
L->nCcalls = L->baseCcalls;
L->allowhook = 1;
restore_stack_limit(L);
L->errfunc = 0;
L->errorJmp = NULL;
}
void luaD_throw (lua_State *L, int errcode) {
if (L->errorJmp) {
L->errorJmp->status = errcode;
LUAI_THROW(L, L->errorJmp);
}
else {
L->status = cast_byte(errcode);
if (G(L)->panic) {
resetstack(L, errcode);
lua_unlock(L);
G(L)->panic(L);
}
exit(EXIT_FAILURE);
}
}
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
struct lua_longjmp lj;
lj.status = 0;
lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj;
LUAI_TRY(L, &lj,
(*f)(L, ud);
);
L->errorJmp = lj.previous; /* restore old error handler */
return lj.status;
}
/* }====================================================== */
static void correctstack (lua_State *L, TValue *oldstack) {
CallInfo *ci;
GCObject *up;
L->top = (L->top - oldstack) + L->stack;
for (up = L->openupval; up != NULL; up = up->gch.next)
gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
for (ci = L->base_ci; ci <= L->ci; ci++) {
ci->top = (ci->top - oldstack) + L->stack;
ci->base = (ci->base - oldstack) + L->stack;
ci->func = (ci->func - oldstack) + L->stack;
}
L->base = (L->base - oldstack) + L->stack;
}
void luaD_reallocstack (lua_State *L, int newsize) {
TValue *oldstack = L->stack;
int realsize = newsize + 1 + EXTRA_STACK;
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue);
L->stacksize = realsize;
L->stack_last = L->stack+newsize;
correctstack(L, oldstack);
}
void luaD_reallocCI (lua_State *L, int newsize) {
CallInfo *oldci = L->base_ci;
luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo);
L->size_ci = newsize;
L->ci = (L->ci - oldci) + L->base_ci;
L->end_ci = L->base_ci + L->size_ci - 1;
}
void luaD_growstack (lua_State *L, int n) {
if (n <= L->stacksize) /* double size is enough? */
luaD_reallocstack(L, 2*L->stacksize);
else
luaD_reallocstack(L, L->stacksize + n);
}
static CallInfo *growCI (lua_State *L) {
if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */
luaD_throw(L, LUA_ERRERR);
else {
luaD_reallocCI(L, 2*L->size_ci);
if (L->size_ci > LUAI_MAXCALLS)
luaG_runerror(L, "stack overflow");
}
return ++L->ci;
}
void luaD_callhook (lua_State *L, int event, int line) {
lua_Hook hook = L->hook;
if (hook && L->allowhook) {
ptrdiff_t top = savestack(L, L->top);
ptrdiff_t ci_top = savestack(L, L->ci->top);
lua_Debug ar;
ar.event = event;
ar.currentline = line;
if (event == LUA_HOOKTAILRET)
ar.i_ci = 0; /* tail call; no debug information about it */
else
ar.i_ci = cast_int(L->ci - L->base_ci);
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
L->ci->top = L->top + LUA_MINSTACK;
lua_assert(L->ci->top <= L->stack_last);
L->allowhook = 0; /* cannot call hooks inside a hook */
lua_unlock(L);
(*hook)(L, &ar);
lua_lock(L);
lua_assert(!L->allowhook);
L->allowhook = 1;
L->ci->top = restorestack(L, ci_top);
L->top = restorestack(L, top);
}
}
static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
int i;
int nfixargs = p->numparams;
Table *htab = NULL;
StkId base, fixed;
for (; actual < nfixargs; ++actual)
setnilvalue(L->top++);
#if defined(LUA_COMPAT_VARARG)
if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */
int nvar = actual - nfixargs; /* number of extra arguments */
lua_assert(p->is_vararg & VARARG_HASARG);
luaC_checkGC(L);
luaD_checkstack(L, p->maxstacksize);
htab = luaH_new(L, nvar, 1); /* create `arg' table */
for (i=0; i<nvar; i++) /* put extra arguments into `arg' table */
setobj2n(L, luaH_setnum(L, htab, i+1), L->top - nvar + i);
/* store counter in field `n' */
setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar));
}
#endif
/* move fixed parameters to final position */
fixed = L->top - actual; /* first fixed argument */
base = L->top; /* final position of first argument */
for (i=0; i<nfixargs; i++) {
setobjs2s(L, L->top++, fixed+i);
setnilvalue(fixed+i);
}
/* add `arg' parameter */
if (htab) {
sethvalue(L, L->top++, htab);
lua_assert(iswhite(obj2gco(htab)));
}
return base;
}
static StkId tryfuncTM (lua_State *L, StkId func) {
const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);
StkId p;
ptrdiff_t funcr = savestack(L, func);
if (!ttisfunction(tm))
luaG_typeerror(L, func, "call");
/* Open a hole inside the stack at `func' */
for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);
incr_top(L);
func = restorestack(L, funcr); /* previous call may change stack */
setobj2s(L, func, tm); /* tag method is the new function to be called */
return func;
}
#define inc_ci(L) \
((L->ci == L->end_ci) ? growCI(L) : \
(condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci))
int luaD_precall (lua_State *L, StkId func, int nresults) {
LClosure *cl;
ptrdiff_t funcr;
if (!ttisfunction(func)) /* `func' is not a function? */
func = tryfuncTM(L, func); /* check the `function' tag method */
funcr = savestack(L, func);
cl = &clvalue(func)->l;
L->ci->savedpc = L->savedpc;
if (!cl->isC) { /* Lua function? prepare its call */
CallInfo *ci;
StkId st, base;
Proto *p = cl->p;
luaD_checkstack(L, p->maxstacksize);
func = restorestack(L, funcr);
if (!p->is_vararg) { /* no varargs? */
base = func + 1;
if (L->top > base + p->numparams)
L->top = base + p->numparams;
}
else { /* vararg function */
int nargs = cast_int(L->top - func) - 1;
base = adjust_varargs(L, p, nargs);
func = restorestack(L, funcr); /* previous call may change the stack */
}
ci = inc_ci(L); /* now `enter' new function */
ci->func = func;
L->base = ci->base = base;
ci->top = L->base + p->maxstacksize;
lua_assert(ci->top <= L->stack_last);
L->savedpc = p->code; /* starting point */
ci->tailcalls = 0;
ci->nresults = nresults;
for (st = L->top; st < ci->top; st++)
setnilvalue(st);
L->top = ci->top;
if (L->hookmask & LUA_MASKCALL) {
L->savedpc++; /* hooks assume 'pc' is already incremented */
luaD_callhook(L, LUA_HOOKCALL, -1);
L->savedpc--; /* correct 'pc' */
}
return PCRLUA;
}
else { /* if is a C function, call it */
CallInfo *ci;
int n;
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
ci = inc_ci(L); /* now `enter' new function */
ci->func = restorestack(L, funcr);
L->base = ci->base = ci->func + 1;
ci->top = L->top + LUA_MINSTACK;
lua_assert(ci->top <= L->stack_last);
ci->nresults = nresults;
if (L->hookmask & LUA_MASKCALL)
luaD_callhook(L, LUA_HOOKCALL, -1);
lua_unlock(L);
n = (*curr_func(L)->c.f)(L); /* do the actual call */
lua_lock(L);
if (n < 0) /* yielding? */
return PCRYIELD;
else {
luaD_poscall(L, L->top - n);
return PCRC;
}
}
}
static StkId callrethooks (lua_State *L, StkId firstResult) {
ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */
luaD_callhook(L, LUA_HOOKRET, -1);
if (f_isLua(L->ci)) { /* Lua function? */
while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */
luaD_callhook(L, LUA_HOOKTAILRET, -1);
}
return restorestack(L, fr);
}
int luaD_poscall (lua_State *L, StkId firstResult) {
StkId res;
int wanted, i;
CallInfo *ci;
if (L->hookmask & LUA_MASKRET)
firstResult = callrethooks(L, firstResult);
ci = L->ci--;
res = ci->func; /* res == final position of 1st result */
wanted = ci->nresults;
L->base = (ci - 1)->base; /* restore base */
L->savedpc = (ci - 1)->savedpc; /* restore savedpc */
/* move results to correct place */
for (i = wanted; i != 0 && firstResult < L->top; i--)
setobjs2s(L, res++, firstResult++);
while (i-- > 0)
setnilvalue(res++);
L->top = res;
return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */
}
/*
** Call a function (C or Lua). The function to be called is at *func.
** The arguments are on the stack, right after the function.
** When returns, all the results are on the stack, starting at the original
** function position.
*/
void luaD_call (lua_State *L, StkId func, int nResults) {
if (++L->nCcalls >= LUAI_MAXCCALLS) {
if (L->nCcalls == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow");
else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
}
if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */
luaV_execute(L, 1); /* call it */
L->nCcalls--;
luaC_checkGC(L);
}
static void resume (lua_State *L, void *ud) {
StkId firstArg = cast(StkId, ud);
CallInfo *ci = L->ci;
if (L->status == 0) { /* start coroutine? */
lua_assert(ci == L->base_ci && firstArg > L->base);
if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA)
return;
}
else { /* resuming from previous yield */
lua_assert(L->status == LUA_YIELD);
L->status = 0;
if (!f_isLua(ci)) { /* `common' yield? */
/* finish interrupted execution of `OP_CALL' */
lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||
GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL);
if (luaD_poscall(L, firstArg)) /* complete it... */
L->top = L->ci->top; /* and correct top if not multiple results */
}
else /* yielded inside a hook: just continue its execution */
L->base = L->ci->base;
}
luaV_execute(L, cast_int(L->ci - L->base_ci));
}
static int resume_error (lua_State *L, const char *msg) {
L->top = L->ci->base;
setsvalue2s(L, L->top, luaS_new(L, msg));
incr_top(L);
lua_unlock(L);
return LUA_ERRRUN;
}
LUA_API int lua_resume (lua_State *L, int nargs) {
int status;
lua_lock(L);
if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci))
return resume_error(L, "cannot resume non-suspended coroutine");
if (L->nCcalls >= LUAI_MAXCCALLS)
return resume_error(L, "C stack overflow");
luai_userstateresume(L, nargs);
lua_assert(L->errfunc == 0);
L->baseCcalls = ++L->nCcalls;
status = luaD_rawrunprotected(L, resume, L->top - nargs);
if (status != 0) { /* error? */
L->status = cast_byte(status); /* mark thread as `dead' */
luaD_seterrorobj(L, status, L->top);
L->ci->top = L->top;
}
else {
lua_assert(L->nCcalls == L->baseCcalls);
status = L->status;
}
--L->nCcalls;
lua_unlock(L);
return status;
}
LUA_API int lua_yield (lua_State *L, int nresults) {
luai_userstateyield(L, nresults);
lua_lock(L);
if (L->nCcalls > L->baseCcalls)
luaG_runerror(L, "attempt to yield across metamethod/C-call boundary");
L->base = L->top - nresults; /* protect stack slots below */
L->status = LUA_YIELD;
lua_unlock(L);
return -1;
}
int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t old_top, ptrdiff_t ef) {
int status;
unsigned short oldnCcalls = L->nCcalls;
ptrdiff_t old_ci = saveci(L, L->ci);
lu_byte old_allowhooks = L->allowhook;
ptrdiff_t old_errfunc = L->errfunc;
L->errfunc = ef;
status = luaD_rawrunprotected(L, func, u);
if (status != 0) { /* an error occurred? */
StkId oldtop = restorestack(L, old_top);
luaF_close(L, oldtop); /* close eventual pending closures */
luaD_seterrorobj(L, status, oldtop);
L->nCcalls = oldnCcalls;
L->ci = restoreci(L, old_ci);
L->base = L->ci->base;
L->savedpc = L->ci->savedpc;
L->allowhook = old_allowhooks;
restore_stack_limit(L);
}
L->errfunc = old_errfunc;
return status;
}
/*
** Execute a protected parser.
*/
struct SParser { /* data to `f_parser' */
ZIO *z;
Mbuffer buff; /* buffer to be used by the scanner */
const char *name;
};
static void f_parser (lua_State *L, void *ud) {
int i;
Proto *tf;
Closure *cl;
struct SParser *p = cast(struct SParser *, ud);
int c = luaZ_lookahead(p->z);
luaC_checkGC(L);
tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
&p->buff, p->name);
cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
cl->l.p = tf;
for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */
cl->l.upvals[i] = luaF_newupval(L);
setclvalue(L, L->top, cl);
incr_top(L);
}
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) {
struct SParser p;
int status;
p.z = z; p.name = name;
luaZ_initbuffer(L, &p.buff);
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
luaZ_freebuffer(L, &p.buff);
return status;
}

57
extern/lua-5.1.5/src/ldo.h vendored Normal file
View file

@ -0,0 +1,57 @@
/*
** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
#ifndef ldo_h
#define ldo_h
#include "lobject.h"
#include "lstate.h"
#include "lzio.h"
#define luaD_checkstack(L,n) \
if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \
luaD_growstack(L, n); \
else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));
#define incr_top(L) {luaD_checkstack(L,1); L->top++;}
#define savestack(L,p) ((char *)(p) - (char *)L->stack)
#define restorestack(L,n) ((TValue *)((char *)L->stack + (n)))
#define saveci(L,p) ((char *)(p) - (char *)L->base_ci)
#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n)))
/* results from luaD_precall */
#define PCRLUA 0 /* initiated a call to a Lua function */
#define PCRC 1 /* did a call to a C function */
#define PCRYIELD 2 /* C funtion yielded */
/* type of protected functions, to be ran by `runprotected' */
typedef void (*Pfunc) (lua_State *L, void *ud);
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);
LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line);
LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t oldtop, ptrdiff_t ef);
LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult);
LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);
LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);
LUAI_FUNC void luaD_growstack (lua_State *L, int n);
LUAI_FUNC void luaD_throw (lua_State *L, int errcode);
LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
#endif

164
extern/lua-5.1.5/src/ldump.c vendored Normal file
View file

@ -0,0 +1,164 @@
/*
** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
** save precompiled Lua chunks
** See Copyright Notice in lua.h
*/
#include <stddef.h>
#define ldump_c
#define LUA_CORE
#include "lua.h"
#include "lobject.h"
#include "lstate.h"
#include "lundump.h"
typedef struct {
lua_State* L;
lua_Writer writer;
void* data;
int strip;
int status;
} DumpState;
#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D)
#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D)
static void DumpBlock(const void* b, size_t size, DumpState* D)
{
if (D->status==0)
{
lua_unlock(D->L);
D->status=(*D->writer)(D->L,b,size,D->data);
lua_lock(D->L);
}
}
static void DumpChar(int y, DumpState* D)
{
char x=(char)y;
DumpVar(x,D);
}
static void DumpInt(int x, DumpState* D)
{
DumpVar(x,D);
}
static void DumpNumber(lua_Number x, DumpState* D)
{
DumpVar(x,D);
}
static void DumpVector(const void* b, int n, size_t size, DumpState* D)
{
DumpInt(n,D);
DumpMem(b,n,size,D);
}
static void DumpString(const TString* s, DumpState* D)
{
if (s==NULL || getstr(s)==NULL)
{
size_t size=0;
DumpVar(size,D);
}
else
{
size_t size=s->tsv.len+1; /* include trailing '\0' */
DumpVar(size,D);
DumpBlock(getstr(s),size,D);
}
}
#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D)
static void DumpFunction(const Proto* f, const TString* p, DumpState* D);
static void DumpConstants(const Proto* f, DumpState* D)
{
int i,n=f->sizek;
DumpInt(n,D);
for (i=0; i<n; i++)
{
const TValue* o=&f->k[i];
DumpChar(ttype(o),D);
switch (ttype(o))
{
case LUA_TNIL:
break;
case LUA_TBOOLEAN:
DumpChar(bvalue(o),D);
break;
case LUA_TNUMBER:
DumpNumber(nvalue(o),D);
break;
case LUA_TSTRING:
DumpString(rawtsvalue(o),D);
break;
default:
lua_assert(0); /* cannot happen */
break;
}
}
n=f->sizep;
DumpInt(n,D);
for (i=0; i<n; i++) DumpFunction(f->p[i],f->source,D);
}
static void DumpDebug(const Proto* f, DumpState* D)
{
int i,n;
n= (D->strip) ? 0 : f->sizelineinfo;
DumpVector(f->lineinfo,n,sizeof(int),D);
n= (D->strip) ? 0 : f->sizelocvars;
DumpInt(n,D);
for (i=0; i<n; i++)
{
DumpString(f->locvars[i].varname,D);
DumpInt(f->locvars[i].startpc,D);
DumpInt(f->locvars[i].endpc,D);
}
n= (D->strip) ? 0 : f->sizeupvalues;
DumpInt(n,D);
for (i=0; i<n; i++) DumpString(f->upvalues[i],D);
}
static void DumpFunction(const Proto* f, const TString* p, DumpState* D)
{
DumpString((f->source==p || D->strip) ? NULL : f->source,D);
DumpInt(f->linedefined,D);
DumpInt(f->lastlinedefined,D);
DumpChar(f->nups,D);
DumpChar(f->numparams,D);
DumpChar(f->is_vararg,D);
DumpChar(f->maxstacksize,D);
DumpCode(f,D);
DumpConstants(f,D);
DumpDebug(f,D);
}
static void DumpHeader(DumpState* D)
{
char h[LUAC_HEADERSIZE];
luaU_header(h);
DumpBlock(h,LUAC_HEADERSIZE,D);
}
/*
** dump Lua function as precompiled chunk
*/
int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)
{
DumpState D;
D.L=L;
D.writer=w;
D.data=data;
D.strip=strip;
D.status=0;
DumpHeader(&D);
DumpFunction(f,NULL,&D);
return D.status;
}

174
extern/lua-5.1.5/src/lfunc.c vendored Normal file
View file

@ -0,0 +1,174 @@
/*
** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
#include <stddef.h>
#define lfunc_c
#define LUA_CORE
#include "lua.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) {
Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems)));
luaC_link(L, obj2gco(c), LUA_TFUNCTION);
c->c.isC = 1;
c->c.env = e;
c->c.nupvalues = cast_byte(nelems);
return c;
}
Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) {
Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems)));
luaC_link(L, obj2gco(c), LUA_TFUNCTION);
c->l.isC = 0;
c->l.env = e;
c->l.nupvalues = cast_byte(nelems);
while (nelems--) c->l.upvals[nelems] = NULL;
return c;
}
UpVal *luaF_newupval (lua_State *L) {
UpVal *uv = luaM_new(L, UpVal);
luaC_link(L, obj2gco(uv), LUA_TUPVAL);
uv->v = &uv->u.value;
setnilvalue(uv->v);
return uv;
}
UpVal *luaF_findupval (lua_State *L, StkId level) {
global_State *g = G(L);
GCObject **pp = &L->openupval;
UpVal *p;
UpVal *uv;
while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) {
lua_assert(p->v != &p->u.value);
if (p->v == level) { /* found a corresponding upvalue? */
if (isdead(g, obj2gco(p))) /* is it dead? */
changewhite(obj2gco(p)); /* ressurect it */
return p;
}
pp = &p->next;
}
uv = luaM_new(L, UpVal); /* not found: create a new one */
uv->tt = LUA_TUPVAL;
uv->marked = luaC_white(g);
uv->v = level; /* current value lives in the stack */
uv->next = *pp; /* chain it in the proper position */
*pp = obj2gco(uv);
uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */
uv->u.l.next = g->uvhead.u.l.next;
uv->u.l.next->u.l.prev = uv;
g->uvhead.u.l.next = uv;
lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
return uv;
}
static void unlinkupval (UpVal *uv) {
lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */
uv->u.l.prev->u.l.next = uv->u.l.next;
}
void luaF_freeupval (lua_State *L, UpVal *uv) {
if (uv->v != &uv->u.value) /* is it open? */
unlinkupval(uv); /* remove from open list */
luaM_free(L, uv); /* free upvalue */
}
void luaF_close (lua_State *L, StkId level) {
UpVal *uv;
global_State *g = G(L);
while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) {
GCObject *o = obj2gco(uv);
lua_assert(!isblack(o) && uv->v != &uv->u.value);
L->openupval = uv->next; /* remove from `open' list */
if (isdead(g, o))
luaF_freeupval(L, uv); /* free upvalue */
else {
unlinkupval(uv);
setobj(L, &uv->u.value, uv->v);
uv->v = &uv->u.value; /* now current value lives here */
luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */
}
}
}
Proto *luaF_newproto (lua_State *L) {
Proto *f = luaM_new(L, Proto);
luaC_link(L, obj2gco(f), LUA_TPROTO);
f->k = NULL;
f->sizek = 0;
f->p = NULL;
f->sizep = 0;
f->code = NULL;
f->sizecode = 0;
f->sizelineinfo = 0;
f->sizeupvalues = 0;
f->nups = 0;
f->upvalues = NULL;
f->numparams = 0;
f->is_vararg = 0;
f->maxstacksize = 0;
f->lineinfo = NULL;
f->sizelocvars = 0;
f->locvars = NULL;
f->linedefined = 0;
f->lastlinedefined = 0;
f->source = NULL;
return f;
}
void luaF_freeproto (lua_State *L, Proto *f) {
luaM_freearray(L, f->code, f->sizecode, Instruction);
luaM_freearray(L, f->p, f->sizep, Proto *);
luaM_freearray(L, f->k, f->sizek, TValue);
luaM_freearray(L, f->lineinfo, f->sizelineinfo, int);
luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar);
luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *);
luaM_free(L, f);
}
void luaF_freeclosure (lua_State *L, Closure *c) {
int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) :
sizeLclosure(c->l.nupvalues);
luaM_freemem(L, c, size);
}
/*
** Look for n-th local variable at line `line' in function `func'.
** Returns NULL if not found.
*/
const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
int i;
for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) {
if (pc < f->locvars[i].endpc) { /* is variable active? */
local_number--;
if (local_number == 0)
return getstr(f->locvars[i].varname);
}
}
return NULL; /* not found */
}

34
extern/lua-5.1.5/src/lfunc.h vendored Normal file
View file

@ -0,0 +1,34 @@
/*
** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
#ifndef lfunc_h
#define lfunc_h
#include "lobject.h"
#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \
cast(int, sizeof(TValue)*((n)-1)))
#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \
cast(int, sizeof(TValue *)*((n)-1)))
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e);
LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e);
LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_close (lua_State *L, StkId level);
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);
LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
int pc);
#endif

710
extern/lua-5.1.5/src/lgc.c vendored Normal file
View file

@ -0,0 +1,710 @@
/*
** $Id: lgc.c,v 2.38.1.2 2011/03/18 18:05:38 roberto Exp $
** Garbage Collector
** See Copyright Notice in lua.h
*/
#include <string.h>
#define lgc_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#define GCSTEPSIZE 1024u
#define GCSWEEPMAX 40
#define GCSWEEPCOST 10
#define GCFINALIZECOST 100
#define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS))
#define makewhite(g,x) \
((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g)))
#define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
#define black2gray(x) resetbit((x)->gch.marked, BLACKBIT)
#define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT)
#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT)
#define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT)
#define KEYWEAK bitmask(KEYWEAKBIT)
#define VALUEWEAK bitmask(VALUEWEAKBIT)
#define markvalue(g,o) { checkconsistency(o); \
if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); }
#define markobject(g,t) { if (iswhite(obj2gco(t))) \
reallymarkobject(g, obj2gco(t)); }
#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause)
static void removeentry (Node *n) {
lua_assert(ttisnil(gval(n)));
if (iscollectable(gkey(n)))
setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */
}
static void reallymarkobject (global_State *g, GCObject *o) {
lua_assert(iswhite(o) && !isdead(g, o));
white2gray(o);
switch (o->gch.tt) {
case LUA_TSTRING: {
return;
}
case LUA_TUSERDATA: {
Table *mt = gco2u(o)->metatable;
gray2black(o); /* udata are never gray */
if (mt) markobject(g, mt);
markobject(g, gco2u(o)->env);
return;
}
case LUA_TUPVAL: {
UpVal *uv = gco2uv(o);
markvalue(g, uv->v);
if (uv->v == &uv->u.value) /* closed? */
gray2black(o); /* open upvalues are never black */
return;
}
case LUA_TFUNCTION: {
gco2cl(o)->c.gclist = g->gray;
g->gray = o;
break;
}
case LUA_TTABLE: {
gco2h(o)->gclist = g->gray;
g->gray = o;
break;
}
case LUA_TTHREAD: {
gco2th(o)->gclist = g->gray;
g->gray = o;
break;
}
case LUA_TPROTO: {
gco2p(o)->gclist = g->gray;
g->gray = o;
break;
}
default: lua_assert(0);
}
}
static void marktmu (global_State *g) {
GCObject *u = g->tmudata;
if (u) {
do {
u = u->gch.next;
makewhite(g, u); /* may be marked, if left from previous GC */
reallymarkobject(g, u);
} while (u != g->tmudata);
}
}
/* move `dead' udata that need finalization to list `tmudata' */
size_t luaC_separateudata (lua_State *L, int all) {
global_State *g = G(L);
size_t deadmem = 0;
GCObject **p = &g->mainthread->next;
GCObject *curr;
while ((curr = *p) != NULL) {
if (!(iswhite(curr) || all) || isfinalized(gco2u(curr)))
p = &curr->gch.next; /* don't bother with them */
else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) {
markfinalized(gco2u(curr)); /* don't need finalization */
p = &curr->gch.next;
}
else { /* must call its gc method */
deadmem += sizeudata(gco2u(curr));
markfinalized(gco2u(curr));
*p = curr->gch.next;
/* link `curr' at the end of `tmudata' list */
if (g->tmudata == NULL) /* list is empty? */
g->tmudata = curr->gch.next = curr; /* creates a circular list */
else {
curr->gch.next = g->tmudata->gch.next;
g->tmudata->gch.next = curr;
g->tmudata = curr;
}
}
}
return deadmem;
}
static int traversetable (global_State *g, Table *h) {
int i;
int weakkey = 0;
int weakvalue = 0;
const TValue *mode;
if (h->metatable)
markobject(g, h->metatable);
mode = gfasttm(g, h->metatable, TM_MODE);
if (mode && ttisstring(mode)) { /* is there a weak mode? */
weakkey = (strchr(svalue(mode), 'k') != NULL);
weakvalue = (strchr(svalue(mode), 'v') != NULL);
if (weakkey || weakvalue) { /* is really weak? */
h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */
h->marked |= cast_byte((weakkey << KEYWEAKBIT) |
(weakvalue << VALUEWEAKBIT));
h->gclist = g->weak; /* must be cleared after GC, ... */
g->weak = obj2gco(h); /* ... so put in the appropriate list */
}
}
if (weakkey && weakvalue) return 1;
if (!weakvalue) {
i = h->sizearray;
while (i--)
markvalue(g, &h->array[i]);
}
i = sizenode(h);
while (i--) {
Node *n = gnode(h, i);
lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
if (ttisnil(gval(n)))
removeentry(n); /* remove empty entries */
else {
lua_assert(!ttisnil(gkey(n)));
if (!weakkey) markvalue(g, gkey(n));
if (!weakvalue) markvalue(g, gval(n));
}
}
return weakkey || weakvalue;
}
/*
** All marks are conditional because a GC may happen while the
** prototype is still being created
*/
static void traverseproto (global_State *g, Proto *f) {
int i;
if (f->source) stringmark(f->source);
for (i=0; i<f->sizek; i++) /* mark literals */
markvalue(g, &f->k[i]);
for (i=0; i<f->sizeupvalues; i++) { /* mark upvalue names */
if (f->upvalues[i])
stringmark(f->upvalues[i]);
}
for (i=0; i<f->sizep; i++) { /* mark nested protos */
if (f->p[i])
markobject(g, f->p[i]);
}
for (i=0; i<f->sizelocvars; i++) { /* mark local-variable names */
if (f->locvars[i].varname)
stringmark(f->locvars[i].varname);
}
}
static void traverseclosure (global_State *g, Closure *cl) {
markobject(g, cl->c.env);
if (cl->c.isC) {
int i;
for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */
markvalue(g, &cl->c.upvalue[i]);
}
else {
int i;
lua_assert(cl->l.nupvalues == cl->l.p->nups);
markobject(g, cl->l.p);
for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */
markobject(g, cl->l.upvals[i]);
}
}
static void checkstacksizes (lua_State *L, StkId max) {
int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */
int s_used = cast_int(max - L->stack); /* part of stack in use */
if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */
return; /* do not touch the stacks */
if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)
luaD_reallocCI(L, L->size_ci/2); /* still big enough... */
condhardstacktests(luaD_reallocCI(L, ci_used + 1));
if (4*s_used < L->stacksize &&
2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)
luaD_reallocstack(L, L->stacksize/2); /* still big enough... */
condhardstacktests(luaD_reallocstack(L, s_used));
}
static void traversestack (global_State *g, lua_State *l) {
StkId o, lim;
CallInfo *ci;
markvalue(g, gt(l));
lim = l->top;
for (ci = l->base_ci; ci <= l->ci; ci++) {
lua_assert(ci->top <= l->stack_last);
if (lim < ci->top) lim = ci->top;
}
for (o = l->stack; o < l->top; o++)
markvalue(g, o);
for (; o <= lim; o++)
setnilvalue(o);
checkstacksizes(l, lim);
}
/*
** traverse one gray object, turning it to black.
** Returns `quantity' traversed.
*/
static l_mem propagatemark (global_State *g) {
GCObject *o = g->gray;
lua_assert(isgray(o));
gray2black(o);
switch (o->gch.tt) {
case LUA_TTABLE: {
Table *h = gco2h(o);
g->gray = h->gclist;
if (traversetable(g, h)) /* table is weak? */
black2gray(o); /* keep it gray */
return sizeof(Table) + sizeof(TValue) * h->sizearray +
sizeof(Node) * sizenode(h);
}
case LUA_TFUNCTION: {
Closure *cl = gco2cl(o);
g->gray = cl->c.gclist;
traverseclosure(g, cl);
return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :
sizeLclosure(cl->l.nupvalues);
}
case LUA_TTHREAD: {
lua_State *th = gco2th(o);
g->gray = th->gclist;
th->gclist = g->grayagain;
g->grayagain = o;
black2gray(o);
traversestack(g, th);
return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
sizeof(CallInfo) * th->size_ci;
}
case LUA_TPROTO: {
Proto *p = gco2p(o);
g->gray = p->gclist;
traverseproto(g, p);
return sizeof(Proto) + sizeof(Instruction) * p->sizecode +
sizeof(Proto *) * p->sizep +
sizeof(TValue) * p->sizek +
sizeof(int) * p->sizelineinfo +
sizeof(LocVar) * p->sizelocvars +
sizeof(TString *) * p->sizeupvalues;
}
default: lua_assert(0); return 0;
}
}
static size_t propagateall (global_State *g) {
size_t m = 0;
while (g->gray) m += propagatemark(g);
return m;
}
/*
** The next function tells whether a key or value can be cleared from
** a weak table. Non-collectable objects are never removed from weak
** tables. Strings behave as `values', so are never removed too. for
** other objects: if really collected, cannot keep them; for userdata
** being finalized, keep them in keys, but not in values
*/
static int iscleared (const TValue *o, int iskey) {
if (!iscollectable(o)) return 0;
if (ttisstring(o)) {
stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */
return 0;
}
return iswhite(gcvalue(o)) ||
(ttisuserdata(o) && (!iskey && isfinalized(uvalue(o))));
}
/*
** clear collected entries from weaktables
*/
static void cleartable (GCObject *l) {
while (l) {
Table *h = gco2h(l);
int i = h->sizearray;
lua_assert(testbit(h->marked, VALUEWEAKBIT) ||
testbit(h->marked, KEYWEAKBIT));
if (testbit(h->marked, VALUEWEAKBIT)) {
while (i--) {
TValue *o = &h->array[i];
if (iscleared(o, 0)) /* value was collected? */
setnilvalue(o); /* remove value */
}
}
i = sizenode(h);
while (i--) {
Node *n = gnode(h, i);
if (!ttisnil(gval(n)) && /* non-empty entry? */
(iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) {
setnilvalue(gval(n)); /* remove value ... */
removeentry(n); /* remove entry from table */
}
}
l = h->gclist;
}
}
static void freeobj (lua_State *L, GCObject *o) {
switch (o->gch.tt) {
case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break;
case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;
case LUA_TTABLE: luaH_free(L, gco2h(o)); break;
case LUA_TTHREAD: {
lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread);
luaE_freethread(L, gco2th(o));
break;
}
case LUA_TSTRING: {
G(L)->strt.nuse--;
luaM_freemem(L, o, sizestring(gco2ts(o)));
break;
}
case LUA_TUSERDATA: {
luaM_freemem(L, o, sizeudata(gco2u(o)));
break;
}
default: lua_assert(0);
}
}
#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM)
static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
GCObject *curr;
global_State *g = G(L);
int deadmask = otherwhite(g);
while ((curr = *p) != NULL && count-- > 0) {
if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */
sweepwholelist(L, &gco2th(curr)->openupval);
if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */
lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT));
makewhite(g, curr); /* make it white (for next cycle) */
p = &curr->gch.next;
}
else { /* must erase `curr' */
lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT));
*p = curr->gch.next;
if (curr == g->rootgc) /* is the first element of the list? */
g->rootgc = curr->gch.next; /* adjust first */
freeobj(L, curr);
}
}
return p;
}
static void checkSizes (lua_State *L) {
global_State *g = G(L);
/* check size of string hash */
if (g->strt.nuse < cast(lu_int32, g->strt.size/4) &&
g->strt.size > MINSTRTABSIZE*2)
luaS_resize(L, g->strt.size/2); /* table is too big */
/* check size of buffer */
if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */
size_t newsize = luaZ_sizebuffer(&g->buff) / 2;
luaZ_resizebuffer(L, &g->buff, newsize);
}
}
static void GCTM (lua_State *L) {
global_State *g = G(L);
GCObject *o = g->tmudata->gch.next; /* get first element */
Udata *udata = rawgco2u(o);
const TValue *tm;
/* remove udata from `tmudata' */
if (o == g->tmudata) /* last element? */
g->tmudata = NULL;
else
g->tmudata->gch.next = udata->uv.next;
udata->uv.next = g->mainthread->next; /* return it to `root' list */
g->mainthread->next = o;
makewhite(g, o);
tm = fasttm(L, udata->uv.metatable, TM_GC);
if (tm != NULL) {
lu_byte oldah = L->allowhook;
lu_mem oldt = g->GCthreshold;
L->allowhook = 0; /* stop debug hooks during GC tag method */
g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */
setobj2s(L, L->top, tm);
setuvalue(L, L->top+1, udata);
L->top += 2;
luaD_call(L, L->top - 2, 0);
L->allowhook = oldah; /* restore hooks */
g->GCthreshold = oldt; /* restore threshold */
}
}
/*
** Call all GC tag methods
*/
void luaC_callGCTM (lua_State *L) {
while (G(L)->tmudata)
GCTM(L);
}
void luaC_freeall (lua_State *L) {
global_State *g = G(L);
int i;
g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */
sweepwholelist(L, &g->rootgc);
for (i = 0; i < g->strt.size; i++) /* free all string lists */
sweepwholelist(L, &g->strt.hash[i]);
}
static void markmt (global_State *g) {
int i;
for (i=0; i<NUM_TAGS; i++)
if (g->mt[i]) markobject(g, g->mt[i]);
}
/* mark root set */
static void markroot (lua_State *L) {
global_State *g = G(L);
g->gray = NULL;
g->grayagain = NULL;
g->weak = NULL;
markobject(g, g->mainthread);
/* make global table be traversed before main stack */
markvalue(g, gt(g->mainthread));
markvalue(g, registry(L));
markmt(g);
g->gcstate = GCSpropagate;
}
static void remarkupvals (global_State *g) {
UpVal *uv;
for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {
lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
if (isgray(obj2gco(uv)))
markvalue(g, uv->v);
}
}
static void atomic (lua_State *L) {
global_State *g = G(L);
size_t udsize; /* total size of userdata to be finalized */
/* remark occasional upvalues of (maybe) dead threads */
remarkupvals(g);
/* traverse objects cautch by write barrier and by 'remarkupvals' */
propagateall(g);
/* remark weak tables */
g->gray = g->weak;
g->weak = NULL;
lua_assert(!iswhite(obj2gco(g->mainthread)));
markobject(g, L); /* mark running thread */
markmt(g); /* mark basic metatables (again) */
propagateall(g);
/* remark gray again */
g->gray = g->grayagain;
g->grayagain = NULL;
propagateall(g);
udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */
marktmu(g); /* mark `preserved' userdata */
udsize += propagateall(g); /* remark, to propagate `preserveness' */
cleartable(g->weak); /* remove collected objects from weak tables */
/* flip current white */
g->currentwhite = cast_byte(otherwhite(g));
g->sweepstrgc = 0;
g->sweepgc = &g->rootgc;
g->gcstate = GCSsweepstring;
g->estimate = g->totalbytes - udsize; /* first estimate */
}
static l_mem singlestep (lua_State *L) {
global_State *g = G(L);
/*lua_checkmemory(L);*/
switch (g->gcstate) {
case GCSpause: {
markroot(L); /* start a new collection */
return 0;
}
case GCSpropagate: {
if (g->gray)
return propagatemark(g);
else { /* no more `gray' objects */
atomic(L); /* finish mark phase */
return 0;
}
}
case GCSsweepstring: {
lu_mem old = g->totalbytes;
sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);
if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */
g->gcstate = GCSsweep; /* end sweep-string phase */
lua_assert(old >= g->totalbytes);
g->estimate -= old - g->totalbytes;
return GCSWEEPCOST;
}
case GCSsweep: {
lu_mem old = g->totalbytes;
g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
if (*g->sweepgc == NULL) { /* nothing more to sweep? */
checkSizes(L);
g->gcstate = GCSfinalize; /* end sweep phase */
}
lua_assert(old >= g->totalbytes);
g->estimate -= old - g->totalbytes;
return GCSWEEPMAX*GCSWEEPCOST;
}
case GCSfinalize: {
if (g->tmudata) {
GCTM(L);
if (g->estimate > GCFINALIZECOST)
g->estimate -= GCFINALIZECOST;
return GCFINALIZECOST;
}
else {
g->gcstate = GCSpause; /* end collection */
g->gcdept = 0;
return 0;
}
}
default: lua_assert(0); return 0;
}
}
void luaC_step (lua_State *L) {
global_State *g = G(L);
l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul;
if (lim == 0)
lim = (MAX_LUMEM-1)/2; /* no limit */
g->gcdept += g->totalbytes - g->GCthreshold;
do {
lim -= singlestep(L);
if (g->gcstate == GCSpause)
break;
} while (lim > 0);
if (g->gcstate != GCSpause) {
if (g->gcdept < GCSTEPSIZE)
g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/
else {
g->gcdept -= GCSTEPSIZE;
g->GCthreshold = g->totalbytes;
}
}
else {
setthreshold(g);
}
}
void luaC_fullgc (lua_State *L) {
global_State *g = G(L);
if (g->gcstate <= GCSpropagate) {
/* reset sweep marks to sweep all elements (returning them to white) */
g->sweepstrgc = 0;
g->sweepgc = &g->rootgc;
/* reset other collector lists */
g->gray = NULL;
g->grayagain = NULL;
g->weak = NULL;
g->gcstate = GCSsweepstring;
}
lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate);
/* finish any pending sweep phase */
while (g->gcstate != GCSfinalize) {
lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
singlestep(L);
}
markroot(L);
while (g->gcstate != GCSpause) {
singlestep(L);
}
setthreshold(g);
}
void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {
global_State *g = G(L);
lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
lua_assert(ttype(&o->gch) != LUA_TTABLE);
/* must keep invariant? */
if (g->gcstate == GCSpropagate)
reallymarkobject(g, v); /* restore invariant */
else /* don't mind */
makewhite(g, o); /* mark as white just to avoid other barriers */
}
void luaC_barrierback (lua_State *L, Table *t) {
global_State *g = G(L);
GCObject *o = obj2gco(t);
lua_assert(isblack(o) && !isdead(g, o));
lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
black2gray(o); /* make table gray (again) */
t->gclist = g->grayagain;
g->grayagain = o;
}
void luaC_link (lua_State *L, GCObject *o, lu_byte tt) {
global_State *g = G(L);
o->gch.next = g->rootgc;
g->rootgc = o;
o->gch.marked = luaC_white(g);
o->gch.tt = tt;
}
void luaC_linkupval (lua_State *L, UpVal *uv) {
global_State *g = G(L);
GCObject *o = obj2gco(uv);
o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */
g->rootgc = o;
if (isgray(o)) {
if (g->gcstate == GCSpropagate) {
gray2black(o); /* closed upvalues need barrier */
luaC_barrier(L, uv, uv->v);
}
else { /* sweep phase: sweep it (turning it into white) */
makewhite(g, o);
lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
}
}
}

110
extern/lua-5.1.5/src/lgc.h vendored Normal file
View file

@ -0,0 +1,110 @@
/*
** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $
** Garbage Collector
** See Copyright Notice in lua.h
*/
#ifndef lgc_h
#define lgc_h
#include "lobject.h"
/*
** Possible states of the Garbage Collector
*/
#define GCSpause 0
#define GCSpropagate 1
#define GCSsweepstring 2
#define GCSsweep 3
#define GCSfinalize 4
/*
** some userful bit tricks
*/
#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m)))
#define setbits(x,m) ((x) |= (m))
#define testbits(x,m) ((x) & (m))
#define bitmask(b) (1<<(b))
#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
#define l_setbit(x,b) setbits(x, bitmask(b))
#define resetbit(x,b) resetbits(x, bitmask(b))
#define testbit(x,b) testbits(x, bitmask(b))
#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2)))
#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2)))
#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2)))
/*
** Layout for bit use in `marked' field:
** bit 0 - object is white (type 0)
** bit 1 - object is white (type 1)
** bit 2 - object is black
** bit 3 - for userdata: has been finalized
** bit 3 - for tables: has weak keys
** bit 4 - for tables: has weak values
** bit 5 - object is fixed (should not be collected)
** bit 6 - object is "super" fixed (only the main thread)
*/
#define WHITE0BIT 0
#define WHITE1BIT 1
#define BLACKBIT 2
#define FINALIZEDBIT 3
#define KEYWEAKBIT 3
#define VALUEWEAKBIT 4
#define FIXEDBIT 5
#define SFIXEDBIT 6
#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
#define isblack(x) testbit((x)->gch.marked, BLACKBIT)
#define isgray(x) (!isblack(x) && !iswhite(x))
#define otherwhite(g) (g->currentwhite ^ WHITEBITS)
#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS)
#define changewhite(x) ((x)->gch.marked ^= WHITEBITS)
#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT)
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)
#define luaC_checkGC(L) { \
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \
if (G(L)->totalbytes >= G(L)->GCthreshold) \
luaC_step(L); }
#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \
luaC_barrierf(L,obj2gco(p),gcvalue(v)); }
#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \
luaC_barrierback(L,t); }
#define luaC_objbarrier(L,p,o) \
{ if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \
luaC_barrierf(L,obj2gco(p),obj2gco(o)); }
#define luaC_objbarriert(L,t,o) \
{ if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); }
LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all);
LUAI_FUNC void luaC_callGCTM (lua_State *L);
LUAI_FUNC void luaC_freeall (lua_State *L);
LUAI_FUNC void luaC_step (lua_State *L);
LUAI_FUNC void luaC_fullgc (lua_State *L);
LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);
LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);
LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);
LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t);
#endif

38
extern/lua-5.1.5/src/linit.c vendored Normal file
View file

@ -0,0 +1,38 @@
/*
** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $
** Initialization of libraries for lua.c
** See Copyright Notice in lua.h
*/
#define linit_c
#define LUA_LIB
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static const luaL_Reg lualibs[] = {
{"", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_DBLIBNAME, luaopen_debug},
{NULL, NULL}
};
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib = lualibs;
for (; lib->func; lib++) {
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
}

556
extern/lua-5.1.5/src/liolib.c vendored Normal file
View file

@ -0,0 +1,556 @@
/*
** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define liolib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#define IO_INPUT 1
#define IO_OUTPUT 2
static const char *const fnames[] = {"input", "output"};
static int pushresult (lua_State *L, int i, const char *filename) {
int en = errno; /* calls to Lua API may change this value */
if (i) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushnil(L);
if (filename)
lua_pushfstring(L, "%s: %s", filename, strerror(en));
else
lua_pushfstring(L, "%s", strerror(en));
lua_pushinteger(L, en);
return 3;
}
}
static void fileerror (lua_State *L, int arg, const char *filename) {
lua_pushfstring(L, "%s: %s", filename, strerror(errno));
luaL_argerror(L, arg, lua_tostring(L, -1));
}
#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
static int io_type (lua_State *L) {
void *ud;
luaL_checkany(L, 1);
ud = lua_touserdata(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
lua_pushnil(L); /* not a file */
else if (*((FILE **)ud) == NULL)
lua_pushliteral(L, "closed file");
else
lua_pushliteral(L, "file");
return 1;
}
static FILE *tofile (lua_State *L) {
FILE **f = tofilep(L);
if (*f == NULL)
luaL_error(L, "attempt to use a closed file");
return *f;
}
/*
** When creating file handles, always creates a `closed' file handle
** before opening the actual file; so, if there is a memory error, the
** file is not left opened.
*/
static FILE **newfile (lua_State *L) {
FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
*pf = NULL; /* file handle is currently `closed' */
luaL_getmetatable(L, LUA_FILEHANDLE);
lua_setmetatable(L, -2);
return pf;
}
/*
** function to (not) close the standard files stdin, stdout, and stderr
*/
static int io_noclose (lua_State *L) {
lua_pushnil(L);
lua_pushliteral(L, "cannot close standard file");
return 2;
}
/*
** function to close 'popen' files
*/
static int io_pclose (lua_State *L) {
FILE **p = tofilep(L);
int ok = lua_pclose(L, *p);
*p = NULL;
return pushresult(L, ok, NULL);
}
/*
** function to close regular files
*/
static int io_fclose (lua_State *L) {
FILE **p = tofilep(L);
int ok = (fclose(*p) == 0);
*p = NULL;
return pushresult(L, ok, NULL);
}
static int aux_close (lua_State *L) {
lua_getfenv(L, 1);
lua_getfield(L, -1, "__close");
return (lua_tocfunction(L, -1))(L);
}
static int io_close (lua_State *L) {
if (lua_isnone(L, 1))
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
tofile(L); /* make sure argument is a file */
return aux_close(L);
}
static int io_gc (lua_State *L) {
FILE *f = *tofilep(L);
/* ignore closed files */
if (f != NULL)
aux_close(L);
return 0;
}
static int io_tostring (lua_State *L) {
FILE *f = *tofilep(L);
if (f == NULL)
lua_pushliteral(L, "file (closed)");
else
lua_pushfstring(L, "file (%p)", f);
return 1;
}
static int io_open (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = newfile(L);
*pf = fopen(filename, mode);
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}
/*
** this function has a separated environment, which defines the
** correct __close for 'popen' files
*/
static int io_popen (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = newfile(L);
*pf = lua_popen(L, filename, mode);
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}
static int io_tmpfile (lua_State *L) {
FILE **pf = newfile(L);
*pf = tmpfile();
return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
}
static FILE *getiofile (lua_State *L, int findex) {
FILE *f;
lua_rawgeti(L, LUA_ENVIRONINDEX, findex);
f = *(FILE **)lua_touserdata(L, -1);
if (f == NULL)
luaL_error(L, "standard %s file is closed", fnames[findex - 1]);
return f;
}
static int g_iofile (lua_State *L, int f, const char *mode) {
if (!lua_isnoneornil(L, 1)) {
const char *filename = lua_tostring(L, 1);
if (filename) {
FILE **pf = newfile(L);
*pf = fopen(filename, mode);
if (*pf == NULL)
fileerror(L, 1, filename);
}
else {
tofile(L); /* check that it's a valid file handle */
lua_pushvalue(L, 1);
}
lua_rawseti(L, LUA_ENVIRONINDEX, f);
}
/* return current value */
lua_rawgeti(L, LUA_ENVIRONINDEX, f);
return 1;
}
static int io_input (lua_State *L) {
return g_iofile(L, IO_INPUT, "r");
}
static int io_output (lua_State *L) {
return g_iofile(L, IO_OUTPUT, "w");
}
static int io_readline (lua_State *L);
static void aux_lines (lua_State *L, int idx, int toclose) {
lua_pushvalue(L, idx);
lua_pushboolean(L, toclose); /* close/not close file when finished */
lua_pushcclosure(L, io_readline, 2);
}
static int f_lines (lua_State *L) {
tofile(L); /* check that it's a valid file handle */
aux_lines(L, 1, 0);
return 1;
}
static int io_lines (lua_State *L) {
if (lua_isnoneornil(L, 1)) { /* no arguments? */
/* will iterate over default input */
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);
return f_lines(L);
}
else {
const char *filename = luaL_checkstring(L, 1);
FILE **pf = newfile(L);
*pf = fopen(filename, "r");
if (*pf == NULL)
fileerror(L, 1, filename);
aux_lines(L, lua_gettop(L), 1);
return 1;
}
}
/*
** {======================================================
** READ
** =======================================================
*/
static int read_number (lua_State *L, FILE *f) {
lua_Number d;
if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
lua_pushnumber(L, d);
return 1;
}
else {
lua_pushnil(L); /* "result" to be removed */
return 0; /* read fails */
}
}
static int test_eof (lua_State *L, FILE *f) {
int c = getc(f);
ungetc(c, f);
lua_pushlstring(L, NULL, 0);
return (c != EOF);
}
static int read_line (lua_State *L, FILE *f) {
luaL_Buffer b;
luaL_buffinit(L, &b);
for (;;) {
size_t l;
char *p = luaL_prepbuffer(&b);
if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */
luaL_pushresult(&b); /* close buffer */
return (lua_objlen(L, -1) > 0); /* check whether read something */
}
l = strlen(p);
if (l == 0 || p[l-1] != '\n')
luaL_addsize(&b, l);
else {
luaL_addsize(&b, l - 1); /* do not include `eol' */
luaL_pushresult(&b); /* close buffer */
return 1; /* read at least an `eol' */
}
}
}
static int read_chars (lua_State *L, FILE *f, size_t n) {
size_t rlen; /* how much to read */
size_t nr; /* number of chars actually read */
luaL_Buffer b;
luaL_buffinit(L, &b);
rlen = LUAL_BUFFERSIZE; /* try to read that much each time */
do {
char *p = luaL_prepbuffer(&b);
if (rlen > n) rlen = n; /* cannot read more than asked */
nr = fread(p, sizeof(char), rlen, f);
luaL_addsize(&b, nr);
n -= nr; /* still have to read `n' chars */
} while (n > 0 && nr == rlen); /* until end of count or eof */
luaL_pushresult(&b); /* close buffer */
return (n == 0 || lua_objlen(L, -1) > 0);
}
static int g_read (lua_State *L, FILE *f, int first) {
int nargs = lua_gettop(L) - 1;
int success;
int n;
clearerr(f);
if (nargs == 0) { /* no arguments? */
success = read_line(L, f);
n = first+1; /* to return 1 result */
}
else { /* ensure stack space for all results and for auxlib's buffer */
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
success = 1;
for (n = first; nargs-- && success; n++) {
if (lua_type(L, n) == LUA_TNUMBER) {
size_t l = (size_t)lua_tointeger(L, n);
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
}
else {
const char *p = lua_tostring(L, n);
luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
switch (p[1]) {
case 'n': /* number */
success = read_number(L, f);
break;
case 'l': /* line */
success = read_line(L, f);
break;
case 'a': /* file */
read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */
success = 1; /* always success */
break;
default:
return luaL_argerror(L, n, "invalid format");
}
}
}
}
if (ferror(f))
return pushresult(L, 0, NULL);
if (!success) {
lua_pop(L, 1); /* remove last result */
lua_pushnil(L); /* push nil instead */
}
return n - first;
}
static int io_read (lua_State *L) {
return g_read(L, getiofile(L, IO_INPUT), 1);
}
static int f_read (lua_State *L) {
return g_read(L, tofile(L), 2);
}
static int io_readline (lua_State *L) {
FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
int sucess;
if (f == NULL) /* file is already closed? */
luaL_error(L, "file is already closed");
sucess = read_line(L, f);
if (ferror(f))
return luaL_error(L, "%s", strerror(errno));
if (sucess) return 1;
else { /* EOF */
if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */
lua_settop(L, 0);
lua_pushvalue(L, lua_upvalueindex(1));
aux_close(L); /* close it */
}
return 0;
}
}
/* }====================================================== */
static int g_write (lua_State *L, FILE *f, int arg) {
int nargs = lua_gettop(L) - 1;
int status = 1;
for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */
status = status &&
fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
}
else {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
status = status && (fwrite(s, sizeof(char), l, f) == l);
}
}
return pushresult(L, status, NULL);
}
static int io_write (lua_State *L) {
return g_write(L, getiofile(L, IO_OUTPUT), 1);
}
static int f_write (lua_State *L) {
return g_write(L, tofile(L), 2);
}
static int f_seek (lua_State *L) {
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, "cur", modenames);
long offset = luaL_optlong(L, 3, 0);
op = fseek(f, offset, mode[op]);
if (op)
return pushresult(L, 0, NULL); /* error */
else {
lua_pushinteger(L, ftell(f));
return 1;
}
}
static int f_setvbuf (lua_State *L) {
static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
static const char *const modenames[] = {"no", "full", "line", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, NULL, modenames);
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
int res = setvbuf(f, NULL, mode[op], sz);
return pushresult(L, res == 0, NULL);
}
static int io_flush (lua_State *L) {
return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
}
static int f_flush (lua_State *L) {
return pushresult(L, fflush(tofile(L)) == 0, NULL);
}
static const luaL_Reg iolib[] = {
{"close", io_close},
{"flush", io_flush},
{"input", io_input},
{"lines", io_lines},
{"open", io_open},
{"output", io_output},
{"popen", io_popen},
{"read", io_read},
{"tmpfile", io_tmpfile},
{"type", io_type},
{"write", io_write},
{NULL, NULL}
};
static const luaL_Reg flib[] = {
{"close", io_close},
{"flush", f_flush},
{"lines", f_lines},
{"read", f_read},
{"seek", f_seek},
{"setvbuf", f_setvbuf},
{"write", f_write},
{"__gc", io_gc},
{"__tostring", io_tostring},
{NULL, NULL}
};
static void createmeta (lua_State *L) {
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_register(L, NULL, flib); /* file methods */
}
static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
*newfile(L) = f;
if (k > 0) {
lua_pushvalue(L, -1);
lua_rawseti(L, LUA_ENVIRONINDEX, k);
}
lua_pushvalue(L, -2); /* copy environment */
lua_setfenv(L, -2); /* set it */
lua_setfield(L, -3, fname);
}
static void newfenv (lua_State *L, lua_CFunction cls) {
lua_createtable(L, 0, 1);
lua_pushcfunction(L, cls);
lua_setfield(L, -2, "__close");
}
LUALIB_API int luaopen_io (lua_State *L) {
createmeta(L);
/* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
newfenv(L, io_fclose);
lua_replace(L, LUA_ENVIRONINDEX);
/* open library */
luaL_register(L, LUA_IOLIBNAME, iolib);
/* create (and set) default files */
newfenv(L, io_noclose); /* close function for default files */
createstdfile(L, stdin, IO_INPUT, "stdin");
createstdfile(L, stdout, IO_OUTPUT, "stdout");
createstdfile(L, stderr, 0, "stderr");
lua_pop(L, 1); /* pop environment for default files */
lua_getfield(L, -1, "popen");
newfenv(L, io_pclose); /* create environment for 'popen' */
lua_setfenv(L, -2); /* set fenv for 'popen' */
lua_pop(L, 1); /* pop 'popen' */
return 1;
}

463
extern/lua-5.1.5/src/llex.c vendored Normal file
View file

@ -0,0 +1,463 @@
/*
** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <locale.h>
#include <string.h>
#define llex_c
#define LUA_CORE
#include "lua.h"
#include "ldo.h"
#include "llex.h"
#include "lobject.h"
#include "lparser.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "lzio.h"
#define next(ls) (ls->current = zgetc(ls->z))
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
/* ORDER RESERVED */
const char *const luaX_tokens [] = {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"..", "...", "==", ">=", "<=", "~=",
"<number>", "<name>", "<string>", "<eof>",
NULL
};
#define save_and_next(ls) (save(ls, ls->current), next(ls))
static void save (LexState *ls, int c) {
Mbuffer *b = ls->buff;
if (b->n + 1 > b->buffsize) {
size_t newsize;
if (b->buffsize >= MAX_SIZET/2)
luaX_lexerror(ls, "lexical element too long", 0);
newsize = b->buffsize * 2;
luaZ_resizebuffer(ls->L, b, newsize);
}
b->buffer[b->n++] = cast(char, c);
}
void luaX_init (lua_State *L) {
int i;
for (i=0; i<NUM_RESERVED; i++) {
TString *ts = luaS_new(L, luaX_tokens[i]);
luaS_fix(ts); /* reserved words are never collected */
lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);
ts->tsv.reserved = cast_byte(i+1); /* reserved word */
}
}
#define MAXSRC 80
const char *luaX_token2str (LexState *ls, int token) {
if (token < FIRST_RESERVED) {
lua_assert(token == cast(unsigned char, token));
return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) :
luaO_pushfstring(ls->L, "%c", token);
}
else
return luaX_tokens[token-FIRST_RESERVED];
}
static const char *txtToken (LexState *ls, int token) {
switch (token) {
case TK_NAME:
case TK_STRING:
case TK_NUMBER:
save(ls, '\0');
return luaZ_buffer(ls->buff);
default:
return luaX_token2str(ls, token);
}
}
void luaX_lexerror (LexState *ls, const char *msg, int token) {
char buff[MAXSRC];
luaO_chunkid(buff, getstr(ls->source), MAXSRC);
msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg);
if (token)
luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token));
luaD_throw(ls->L, LUA_ERRSYNTAX);
}
void luaX_syntaxerror (LexState *ls, const char *msg) {
luaX_lexerror(ls, msg, ls->t.token);
}
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
lua_State *L = ls->L;
TString *ts = luaS_newlstr(L, str, l);
TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */
if (ttisnil(o)) {
setbvalue(o, 1); /* make sure `str' will not be collected */
luaC_checkGC(L);
}
return ts;
}
static void inclinenumber (LexState *ls) {
int old = ls->current;
lua_assert(currIsNewline(ls));
next(ls); /* skip `\n' or `\r' */
if (currIsNewline(ls) && ls->current != old)
next(ls); /* skip `\n\r' or `\r\n' */
if (++ls->linenumber >= MAX_INT)
luaX_syntaxerror(ls, "chunk has too many lines");
}
void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {
ls->decpoint = '.';
ls->L = L;
ls->lookahead.token = TK_EOS; /* no look-ahead token */
ls->z = z;
ls->fs = NULL;
ls->linenumber = 1;
ls->lastline = 1;
ls->source = source;
luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
next(ls); /* read first char */
}
/*
** =======================================================
** LEXICAL ANALYZER
** =======================================================
*/
static int check_next (LexState *ls, const char *set) {
if (!strchr(set, ls->current))
return 0;
save_and_next(ls);
return 1;
}
static void buffreplace (LexState *ls, char from, char to) {
size_t n = luaZ_bufflen(ls->buff);
char *p = luaZ_buffer(ls->buff);
while (n--)
if (p[n] == from) p[n] = to;
}
static void trydecpoint (LexState *ls, SemInfo *seminfo) {
/* format error: try to update decimal point separator */
struct lconv *cv = localeconv();
char old = ls->decpoint;
ls->decpoint = (cv ? cv->decimal_point[0] : '.');
buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {
/* format error with correct decimal point: no more options */
buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */
luaX_lexerror(ls, "malformed number", TK_NUMBER);
}
}
/* LUA_NUMBER */
static void read_numeral (LexState *ls, SemInfo *seminfo) {
lua_assert(isdigit(ls->current));
do {
save_and_next(ls);
} while (isdigit(ls->current) || ls->current == '.');
if (check_next(ls, "Ee")) /* `E'? */
check_next(ls, "+-"); /* optional exponent sign */
while (isalnum(ls->current) || ls->current == '_')
save_and_next(ls);
save(ls, '\0');
buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */
trydecpoint(ls, seminfo); /* try to update decimal point separator */
}
static int skip_sep (LexState *ls) {
int count = 0;
int s = ls->current;
lua_assert(s == '[' || s == ']');
save_and_next(ls);
while (ls->current == '=') {
save_and_next(ls);
count++;
}
return (ls->current == s) ? count : (-count) - 1;
}
static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
int cont = 0;
(void)(cont); /* avoid warnings when `cont' is not used */
save_and_next(ls); /* skip 2nd `[' */
if (currIsNewline(ls)) /* string starts with a newline? */
inclinenumber(ls); /* skip it */
for (;;) {
switch (ls->current) {
case EOZ:
luaX_lexerror(ls, (seminfo) ? "unfinished long string" :
"unfinished long comment", TK_EOS);
break; /* to avoid warnings */
#if defined(LUA_COMPAT_LSTR)
case '[': {
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd `[' */
cont++;
#if LUA_COMPAT_LSTR == 1
if (sep == 0)
luaX_lexerror(ls, "nesting of [[...]] is deprecated", '[');
#endif
}
break;
}
#endif
case ']': {
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd `]' */
#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2
cont--;
if (sep == 0 && cont >= 0) break;
#endif
goto endloop;
}
break;
}
case '\n':
case '\r': {
save(ls, '\n');
inclinenumber(ls);
if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
break;
}
default: {
if (seminfo) save_and_next(ls);
else next(ls);
}
}
} endloop:
if (seminfo)
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),
luaZ_bufflen(ls->buff) - 2*(2 + sep));
}
static void read_string (LexState *ls, int del, SemInfo *seminfo) {
save_and_next(ls);
while (ls->current != del) {
switch (ls->current) {
case EOZ:
luaX_lexerror(ls, "unfinished string", TK_EOS);
continue; /* to avoid warnings */
case '\n':
case '\r':
luaX_lexerror(ls, "unfinished string", TK_STRING);
continue; /* to avoid warnings */
case '\\': {
int c;
next(ls); /* do not save the `\' */
switch (ls->current) {
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
case '\n': /* go through */
case '\r': save(ls, '\n'); inclinenumber(ls); continue;
case EOZ: continue; /* will raise an error next loop */
default: {
if (!isdigit(ls->current))
save_and_next(ls); /* handles \\, \", \', and \? */
else { /* \xxx */
int i = 0;
c = 0;
do {
c = 10*c + (ls->current-'0');
next(ls);
} while (++i<3 && isdigit(ls->current));
if (c > UCHAR_MAX)
luaX_lexerror(ls, "escape sequence too large", TK_STRING);
save(ls, c);
}
continue;
}
}
save(ls, c);
next(ls);
continue;
}
default:
save_and_next(ls);
}
}
save_and_next(ls); /* skip delimiter */
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,
luaZ_bufflen(ls->buff) - 2);
}
static int llex (LexState *ls, SemInfo *seminfo) {
luaZ_resetbuffer(ls->buff);
for (;;) {
switch (ls->current) {
case '\n':
case '\r': {
inclinenumber(ls);
continue;
}
case '-': {
next(ls);
if (ls->current != '-') return '-';
/* else is a comment */
next(ls);
if (ls->current == '[') {
int sep = skip_sep(ls);
luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */
if (sep >= 0) {
read_long_string(ls, NULL, sep); /* long comment */
luaZ_resetbuffer(ls->buff);
continue;
}
}
/* else short comment */
while (!currIsNewline(ls) && ls->current != EOZ)
next(ls);
continue;
}
case '[': {
int sep = skip_sep(ls);
if (sep >= 0) {
read_long_string(ls, seminfo, sep);
return TK_STRING;
}
else if (sep == -1) return '[';
else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING);
}
case '=': {
next(ls);
if (ls->current != '=') return '=';
else { next(ls); return TK_EQ; }
}
case '<': {
next(ls);
if (ls->current != '=') return '<';
else { next(ls); return TK_LE; }
}
case '>': {
next(ls);
if (ls->current != '=') return '>';
else { next(ls); return TK_GE; }
}
case '~': {
next(ls);
if (ls->current != '=') return '~';
else { next(ls); return TK_NE; }
}
case '"':
case '\'': {
read_string(ls, ls->current, seminfo);
return TK_STRING;
}
case '.': {
save_and_next(ls);
if (check_next(ls, ".")) {
if (check_next(ls, "."))
return TK_DOTS; /* ... */
else return TK_CONCAT; /* .. */
}
else if (!isdigit(ls->current)) return '.';
else {
read_numeral(ls, seminfo);
return TK_NUMBER;
}
}
case EOZ: {
return TK_EOS;
}
default: {
if (isspace(ls->current)) {
lua_assert(!currIsNewline(ls));
next(ls);
continue;
}
else if (isdigit(ls->current)) {
read_numeral(ls, seminfo);
return TK_NUMBER;
}
else if (isalpha(ls->current) || ls->current == '_') {
/* identifier or reserved word */
TString *ts;
do {
save_and_next(ls);
} while (isalnum(ls->current) || ls->current == '_');
ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff));
if (ts->tsv.reserved > 0) /* reserved word? */
return ts->tsv.reserved - 1 + FIRST_RESERVED;
else {
seminfo->ts = ts;
return TK_NAME;
}
}
else {
int c = ls->current;
next(ls);
return c; /* single-char tokens (+ - / ...) */
}
}
}
}
}
void luaX_next (LexState *ls) {
ls->lastline = ls->linenumber;
if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
ls->t = ls->lookahead; /* use this one */
ls->lookahead.token = TK_EOS; /* and discharge it */
}
else
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
}
void luaX_lookahead (LexState *ls) {
lua_assert(ls->lookahead.token == TK_EOS);
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
}

81
extern/lua-5.1.5/src/llex.h vendored Normal file
View file

@ -0,0 +1,81 @@
/*
** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
#ifndef llex_h
#define llex_h
#include "lobject.h"
#include "lzio.h"
#define FIRST_RESERVED 257
/* maximum length of a reserved word */
#define TOKEN_LEN (sizeof("function")/sizeof(char))
/*
* WARNING: if you change the order of this enumeration,
* grep "ORDER RESERVED"
*/
enum RESERVED {
/* terminal symbols denoted by reserved words */
TK_AND = FIRST_RESERVED, TK_BREAK,
TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
/* other terminal symbols */
TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
TK_NAME, TK_STRING, TK_EOS
};
/* number of reserved words */
#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1))
/* array with token `names' */
LUAI_DATA const char *const luaX_tokens [];
typedef union {
lua_Number r;
TString *ts;
} SemInfo; /* semantics information */
typedef struct Token {
int token;
SemInfo seminfo;
} Token;
typedef struct LexState {
int current; /* current character (charint) */
int linenumber; /* input line counter */
int lastline; /* line of last token `consumed' */
Token t; /* current token */
Token lookahead; /* look ahead token */
struct FuncState *fs; /* `FuncState' is private to the parser */
struct lua_State *L;
ZIO *z; /* input stream */
Mbuffer *buff; /* buffer for tokens */
TString *source; /* current source name */
char decpoint; /* locale decimal point */
} LexState;
LUAI_FUNC void luaX_init (lua_State *L);
LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,
TString *source);
LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);
LUAI_FUNC void luaX_next (LexState *ls);
LUAI_FUNC void luaX_lookahead (LexState *ls);
LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token);
LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s);
LUAI_FUNC const char *luaX_token2str (LexState *ls, int token);
#endif

128
extern/lua-5.1.5/src/llimits.h vendored Normal file
View file

@ -0,0 +1,128 @@
/*
** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $
** Limits, basic types, and some other `installation-dependent' definitions
** See Copyright Notice in lua.h
*/
#ifndef llimits_h
#define llimits_h
#include <limits.h>
#include <stddef.h>
#include "lua.h"
typedef LUAI_UINT32 lu_int32;
typedef LUAI_UMEM lu_mem;
typedef LUAI_MEM l_mem;
/* chars used as small naturals (so that `char' is reserved for characters) */
typedef unsigned char lu_byte;
#define MAX_SIZET ((size_t)(~(size_t)0)-2)
#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2)
#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
/*
** conversion of pointer to integer
** this is for hashing only; there is no problem if the integer
** cannot hold the whole pointer value
*/
#define IntPoint(p) ((unsigned int)(lu_mem)(p))
/* type to ensure maximum alignment */
typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
/* result of a `usual argument conversion' over lua_Number */
typedef LUAI_UACNUMBER l_uacNumber;
/* internal assertions for in-house debugging */
#ifdef lua_assert
#define check_exp(c,e) (lua_assert(c), (e))
#define api_check(l,e) lua_assert(e)
#else
#define lua_assert(c) ((void)0)
#define check_exp(c,e) (e)
#define api_check luai_apicheck
#endif
#ifndef UNUSED
#define UNUSED(x) ((void)(x)) /* to avoid warnings */
#endif
#ifndef cast
#define cast(t, exp) ((t)(exp))
#endif
#define cast_byte(i) cast(lu_byte, (i))
#define cast_num(i) cast(lua_Number, (i))
#define cast_int(i) cast(int, (i))
/*
** type for virtual-machine instructions
** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
*/
typedef lu_int32 Instruction;
/* maximum stack for a Lua function */
#define MAXSTACK 250
/* minimum size for the string table (must be power of 2) */
#ifndef MINSTRTABSIZE
#define MINSTRTABSIZE 32
#endif
/* minimum size for string buffer */
#ifndef LUA_MINBUFFER
#define LUA_MINBUFFER 32
#endif
#ifndef lua_lock
#define lua_lock(L) ((void) 0)
#define lua_unlock(L) ((void) 0)
#endif
#ifndef luai_threadyield
#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
#endif
/*
** macro to control inclusion of some hard tests on stack reallocation
*/
#ifndef HARDSTACKTESTS
#define condhardstacktests(x) ((void)0)
#else
#define condhardstacktests(x) x
#endif
#endif

263
extern/lua-5.1.5/src/lmathlib.c vendored Normal file
View file

@ -0,0 +1,263 @@
/*
** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $
** Standard mathematical library
** See Copyright Notice in lua.h
*/
#include <stdlib.h>
#include <math.h>
#define lmathlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#undef PI
#define PI (3.14159265358979323846)
#define RADIANS_PER_DEGREE (PI/180.0)
static int math_abs (lua_State *L) {
lua_pushnumber(L, fabs(luaL_checknumber(L, 1)));
return 1;
}
static int math_sin (lua_State *L) {
lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
return 1;
}
static int math_sinh (lua_State *L) {
lua_pushnumber(L, sinh(luaL_checknumber(L, 1)));
return 1;
}
static int math_cos (lua_State *L) {
lua_pushnumber(L, cos(luaL_checknumber(L, 1)));
return 1;
}
static int math_cosh (lua_State *L) {
lua_pushnumber(L, cosh(luaL_checknumber(L, 1)));
return 1;
}
static int math_tan (lua_State *L) {
lua_pushnumber(L, tan(luaL_checknumber(L, 1)));
return 1;
}
static int math_tanh (lua_State *L) {
lua_pushnumber(L, tanh(luaL_checknumber(L, 1)));
return 1;
}
static int math_asin (lua_State *L) {
lua_pushnumber(L, asin(luaL_checknumber(L, 1)));
return 1;
}
static int math_acos (lua_State *L) {
lua_pushnumber(L, acos(luaL_checknumber(L, 1)));
return 1;
}
static int math_atan (lua_State *L) {
lua_pushnumber(L, atan(luaL_checknumber(L, 1)));
return 1;
}
static int math_atan2 (lua_State *L) {
lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int math_ceil (lua_State *L) {
lua_pushnumber(L, ceil(luaL_checknumber(L, 1)));
return 1;
}
static int math_floor (lua_State *L) {
lua_pushnumber(L, floor(luaL_checknumber(L, 1)));
return 1;
}
static int math_fmod (lua_State *L) {
lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int math_modf (lua_State *L) {
double ip;
double fp = modf(luaL_checknumber(L, 1), &ip);
lua_pushnumber(L, ip);
lua_pushnumber(L, fp);
return 2;
}
static int math_sqrt (lua_State *L) {
lua_pushnumber(L, sqrt(luaL_checknumber(L, 1)));
return 1;
}
static int math_pow (lua_State *L) {
lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int math_log (lua_State *L) {
lua_pushnumber(L, log(luaL_checknumber(L, 1)));
return 1;
}
static int math_log10 (lua_State *L) {
lua_pushnumber(L, log10(luaL_checknumber(L, 1)));
return 1;
}
static int math_exp (lua_State *L) {
lua_pushnumber(L, exp(luaL_checknumber(L, 1)));
return 1;
}
static int math_deg (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE);
return 1;
}
static int math_rad (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE);
return 1;
}
static int math_frexp (lua_State *L) {
int e;
lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e));
lua_pushinteger(L, e);
return 2;
}
static int math_ldexp (lua_State *L) {
lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)));
return 1;
}
static int math_min (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
lua_Number dmin = luaL_checknumber(L, 1);
int i;
for (i=2; i<=n; i++) {
lua_Number d = luaL_checknumber(L, i);
if (d < dmin)
dmin = d;
}
lua_pushnumber(L, dmin);
return 1;
}
static int math_max (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
lua_Number dmax = luaL_checknumber(L, 1);
int i;
for (i=2; i<=n; i++) {
lua_Number d = luaL_checknumber(L, i);
if (d > dmax)
dmax = d;
}
lua_pushnumber(L, dmax);
return 1;
}
static int math_random (lua_State *L) {
/* the `%' avoids the (rare) case of r==1, and is needed also because on
some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
switch (lua_gettop(L)) { /* check number of arguments */
case 0: { /* no arguments */
lua_pushnumber(L, r); /* Number between 0 and 1 */
break;
}
case 1: { /* only upper limit */
int u = luaL_checkint(L, 1);
luaL_argcheck(L, 1<=u, 1, "interval is empty");
lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */
break;
}
case 2: { /* lower and upper limits */
int l = luaL_checkint(L, 1);
int u = luaL_checkint(L, 2);
luaL_argcheck(L, l<=u, 2, "interval is empty");
lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */
break;
}
default: return luaL_error(L, "wrong number of arguments");
}
return 1;
}
static int math_randomseed (lua_State *L) {
srand(luaL_checkint(L, 1));
return 0;
}
static const luaL_Reg mathlib[] = {
{"abs", math_abs},
{"acos", math_acos},
{"asin", math_asin},
{"atan2", math_atan2},
{"atan", math_atan},
{"ceil", math_ceil},
{"cosh", math_cosh},
{"cos", math_cos},
{"deg", math_deg},
{"exp", math_exp},
{"floor", math_floor},
{"fmod", math_fmod},
{"frexp", math_frexp},
{"ldexp", math_ldexp},
{"log10", math_log10},
{"log", math_log},
{"max", math_max},
{"min", math_min},
{"modf", math_modf},
{"pow", math_pow},
{"rad", math_rad},
{"random", math_random},
{"randomseed", math_randomseed},
{"sinh", math_sinh},
{"sin", math_sin},
{"sqrt", math_sqrt},
{"tanh", math_tanh},
{"tan", math_tan},
{NULL, NULL}
};
/*
** Open math library
*/
LUALIB_API int luaopen_math (lua_State *L) {
luaL_register(L, LUA_MATHLIBNAME, mathlib);
lua_pushnumber(L, PI);
lua_setfield(L, -2, "pi");
lua_pushnumber(L, HUGE_VAL);
lua_setfield(L, -2, "huge");
#if defined(LUA_COMPAT_MOD)
lua_getfield(L, -1, "fmod");
lua_setfield(L, -2, "mod");
#endif
return 1;
}

86
extern/lua-5.1.5/src/lmem.c vendored Normal file
View file

@ -0,0 +1,86 @@
/*
** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/
#include <stddef.h>
#define lmem_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
/*
** About the realloc function:
** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
** (`osize' is the old size, `nsize' is the new size)
**
** Lua ensures that (ptr == NULL) iff (osize == 0).
**
** * frealloc(ud, NULL, 0, x) creates a new block of size `x'
**
** * frealloc(ud, p, x, 0) frees the block `p'
** (in this specific case, frealloc must return NULL).
** particularly, frealloc(ud, NULL, 0, 0) does nothing
** (which is equivalent to free(NULL) in ANSI C)
**
** frealloc returns NULL if it cannot create or reallocate the area
** (any reallocation to an equal or smaller size cannot fail!)
*/
#define MINSIZEARRAY 4
void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,
int limit, const char *errormsg) {
void *newblock;
int newsize;
if (*size >= limit/2) { /* cannot double it? */
if (*size >= limit) /* cannot grow even a little? */
luaG_runerror(L, errormsg);
newsize = limit; /* still have at least one free place */
}
else {
newsize = (*size)*2;
if (newsize < MINSIZEARRAY)
newsize = MINSIZEARRAY; /* minimum size */
}
newblock = luaM_reallocv(L, block, *size, newsize, size_elems);
*size = newsize; /* update only when everything else is OK */
return newblock;
}
void *luaM_toobig (lua_State *L) {
luaG_runerror(L, "memory allocation error: block too big");
return NULL; /* to avoid warnings */
}
/*
** generic allocation routine.
*/
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
global_State *g = G(L);
lua_assert((osize == 0) == (block == NULL));
block = (*g->frealloc)(g->ud, block, osize, nsize);
if (block == NULL && nsize > 0)
luaD_throw(L, LUA_ERRMEM);
lua_assert((nsize == 0) == (block == NULL));
g->totalbytes = (g->totalbytes - osize) + nsize;
return block;
}

49
extern/lua-5.1.5/src/lmem.h vendored Normal file
View file

@ -0,0 +1,49 @@
/*
** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/
#ifndef lmem_h
#define lmem_h
#include <stddef.h>
#include "llimits.h"
#include "lua.h"
#define MEMERRMSG "not enough memory"
#define luaM_reallocv(L,b,on,n,e) \
((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \
luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \
luaM_toobig(L))
#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)
#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0)
#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t))
#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t))
#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t)))
#define luaM_newvector(L,n,t) \
cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t)))
#define luaM_growvector(L,v,nelems,size,t,limit,e) \
if ((nelems)+1 > (size)) \
((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
#define luaM_reallocvector(L, v,oldn,n,t) \
((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))
LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,
size_t size);
LUAI_FUNC void *luaM_toobig (lua_State *L);
LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size,
size_t size_elem, int limit,
const char *errormsg);
#endif

666
extern/lua-5.1.5/src/loadlib.c vendored Normal file
View file

@ -0,0 +1,666 @@
/*
** $Id: loadlib.c,v 1.52.1.4 2009/09/09 13:17:16 roberto Exp $
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
** This module contains an implementation of loadlib for Unix systems
** that have dlfcn, an implementation for Darwin (Mac OS X), an
** implementation for Windows, and a stub for other systems.
*/
#include <stdlib.h>
#include <string.h>
#define loadlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/* prefix for open functions in C libraries */
#define LUA_POF "luaopen_"
/* separator for open functions in C libraries */
#define LUA_OFSEP "_"
#define LIBPREFIX "LOADLIB: "
#define POF LUA_POF
#define LIB_FAIL "open"
/* error codes for ll_loadfunc */
#define ERRLIB 1
#define ERRFUNC 2
#define setprogdir(L) ((void)0)
static void ll_unloadlib (void *lib);
static void *ll_load (lua_State *L, const char *path);
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym);
#if defined(LUA_DL_DLOPEN)
/*
** {========================================================================
** This is an implementation of loadlib based on the dlfcn interface.
** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
** as an emulation layer on top of native functions.
** =========================================================================
*/
#include <dlfcn.h>
static void ll_unloadlib (void *lib) {
dlclose(lib);
}
static void *ll_load (lua_State *L, const char *path) {
void *lib = dlopen(path, RTLD_NOW);
if (lib == NULL) lua_pushstring(L, dlerror());
return lib;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = (lua_CFunction)dlsym(lib, sym);
if (f == NULL) lua_pushstring(L, dlerror());
return f;
}
/* }====================================================== */
#elif defined(LUA_DL_DLL)
/*
** {======================================================================
** This is an implementation of loadlib for Windows using native functions.
** =======================================================================
*/
#include <windows.h>
#undef setprogdir
static void setprogdir (lua_State *L) {
char buff[MAX_PATH + 1];
char *lb;
DWORD nsize = sizeof(buff)/sizeof(char);
DWORD n = GetModuleFileNameA(NULL, buff, nsize);
if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL)
luaL_error(L, "unable to get ModuleFileName");
else {
*lb = '\0';
luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff);
lua_remove(L, -2); /* remove original string */
}
}
static void pusherror (lua_State *L) {
int error = GetLastError();
char buffer[128];
if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, 0, buffer, sizeof(buffer), NULL))
lua_pushstring(L, buffer);
else
lua_pushfstring(L, "system error %d\n", error);
}
static void ll_unloadlib (void *lib) {
FreeLibrary((HINSTANCE)lib);
}
static void *ll_load (lua_State *L, const char *path) {
HINSTANCE lib = LoadLibraryA(path);
if (lib == NULL) pusherror(L);
return lib;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);
if (f == NULL) pusherror(L);
return f;
}
/* }====================================================== */
#elif defined(LUA_DL_DYLD)
/*
** {======================================================================
** Native Mac OS X / Darwin Implementation
** =======================================================================
*/
#include <mach-o/dyld.h>
/* Mac appends a `_' before C function names */
#undef POF
#define POF "_" LUA_POF
static void pusherror (lua_State *L) {
const char *err_str;
const char *err_file;
NSLinkEditErrors err;
int err_num;
NSLinkEditError(&err, &err_num, &err_file, &err_str);
lua_pushstring(L, err_str);
}
static const char *errorfromcode (NSObjectFileImageReturnCode ret) {
switch (ret) {
case NSObjectFileImageInappropriateFile:
return "file is not a bundle";
case NSObjectFileImageArch:
return "library is for wrong CPU type";
case NSObjectFileImageFormat:
return "bad format";
case NSObjectFileImageAccess:
return "cannot access file";
case NSObjectFileImageFailure:
default:
return "unable to load library";
}
}
static void ll_unloadlib (void *lib) {
NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES);
}
static void *ll_load (lua_State *L, const char *path) {
NSObjectFileImage img;
NSObjectFileImageReturnCode ret;
/* this would be a rare case, but prevents crashing if it happens */
if(!_dyld_present()) {
lua_pushliteral(L, "dyld not present");
return NULL;
}
ret = NSCreateObjectFileImageFromFile(path, &img);
if (ret == NSObjectFileImageSuccess) {
NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE |
NSLINKMODULE_OPTION_RETURN_ON_ERROR);
NSDestroyObjectFileImage(img);
if (mod == NULL) pusherror(L);
return mod;
}
lua_pushstring(L, errorfromcode(ret));
return NULL;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym);
if (nss == NULL) {
lua_pushfstring(L, "symbol " LUA_QS " not found", sym);
return NULL;
}
return (lua_CFunction)NSAddressOfSymbol(nss);
}
/* }====================================================== */
#else
/*
** {======================================================
** Fallback for other systems
** =======================================================
*/
#undef LIB_FAIL
#define LIB_FAIL "absent"
#define DLMSG "dynamic libraries not enabled; check your Lua installation"
static void ll_unloadlib (void *lib) {
(void)lib; /* to avoid warnings */
}
static void *ll_load (lua_State *L, const char *path) {
(void)path; /* to avoid warnings */
lua_pushliteral(L, DLMSG);
return NULL;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
(void)lib; (void)sym; /* to avoid warnings */
lua_pushliteral(L, DLMSG);
return NULL;
}
/* }====================================================== */
#endif
static void **ll_register (lua_State *L, const char *path) {
void **plib;
lua_pushfstring(L, "%s%s", LIBPREFIX, path);
lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
if (!lua_isnil(L, -1)) /* is there an entry? */
plib = (void **)lua_touserdata(L, -1);
else { /* no entry yet; create one */
lua_pop(L, 1);
plib = (void **)lua_newuserdata(L, sizeof(const void *));
*plib = NULL;
luaL_getmetatable(L, "_LOADLIB");
lua_setmetatable(L, -2);
lua_pushfstring(L, "%s%s", LIBPREFIX, path);
lua_pushvalue(L, -2);
lua_settable(L, LUA_REGISTRYINDEX);
}
return plib;
}
/*
** __gc tag method: calls library's `ll_unloadlib' function with the lib
** handle
*/
static int gctm (lua_State *L) {
void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
if (*lib) ll_unloadlib(*lib);
*lib = NULL; /* mark library as closed */
return 0;
}
static int ll_loadfunc (lua_State *L, const char *path, const char *sym) {
void **reg = ll_register(L, path);
if (*reg == NULL) *reg = ll_load(L, path);
if (*reg == NULL)
return ERRLIB; /* unable to load library */
else {
lua_CFunction f = ll_sym(L, *reg, sym);
if (f == NULL)
return ERRFUNC; /* unable to find function */
lua_pushcfunction(L, f);
return 0; /* return function */
}
}
static int ll_loadlib (lua_State *L) {
const char *path = luaL_checkstring(L, 1);
const char *init = luaL_checkstring(L, 2);
int stat = ll_loadfunc(L, path, init);
if (stat == 0) /* no errors? */
return 1; /* return the loaded function */
else { /* error; error message is on stack top */
lua_pushnil(L);
lua_insert(L, -2);
lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
return 3; /* return nil, error message, and where */
}
}
/*
** {======================================================
** 'require' function
** =======================================================
*/
static int readable (const char *filename) {
FILE *f = fopen(filename, "r"); /* try to open file */
if (f == NULL) return 0; /* open failed */
fclose(f);
return 1;
}
static const char *pushnexttemplate (lua_State *L, const char *path) {
const char *l;
while (*path == *LUA_PATHSEP) path++; /* skip separators */
if (*path == '\0') return NULL; /* no more templates */
l = strchr(path, *LUA_PATHSEP); /* find next separator */
if (l == NULL) l = path + strlen(path);
lua_pushlstring(L, path, l - path); /* template */
return l;
}
static const char *findfile (lua_State *L, const char *name,
const char *pname) {
const char *path;
name = luaL_gsub(L, name, ".", LUA_DIRSEP);
lua_getfield(L, LUA_ENVIRONINDEX, pname);
path = lua_tostring(L, -1);
if (path == NULL)
luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
lua_pushliteral(L, ""); /* error accumulator */
while ((path = pushnexttemplate(L, path)) != NULL) {
const char *filename;
filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);
lua_remove(L, -2); /* remove path template */
if (readable(filename)) /* does file exist and is readable? */
return filename; /* return that file name */
lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
lua_remove(L, -2); /* remove file name */
lua_concat(L, 2); /* add entry to possible error message */
}
return NULL; /* not found */
}
static void loaderror (lua_State *L, const char *filename) {
luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
lua_tostring(L, 1), filename, lua_tostring(L, -1));
}
static int loader_Lua (lua_State *L) {
const char *filename;
const char *name = luaL_checkstring(L, 1);
filename = findfile(L, name, "path");
if (filename == NULL) return 1; /* library not found in this path */
if (luaL_loadfile(L, filename) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static const char *mkfuncname (lua_State *L, const char *modname) {
const char *funcname;
const char *mark = strchr(modname, *LUA_IGMARK);
if (mark) modname = mark + 1;
funcname = luaL_gsub(L, modname, ".", LUA_OFSEP);
funcname = lua_pushfstring(L, POF"%s", funcname);
lua_remove(L, -2); /* remove 'gsub' result */
return funcname;
}
static int loader_C (lua_State *L) {
const char *funcname;
const char *name = luaL_checkstring(L, 1);
const char *filename = findfile(L, name, "cpath");
if (filename == NULL) return 1; /* library not found in this path */
funcname = mkfuncname(L, name);
if (ll_loadfunc(L, filename, funcname) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static int loader_Croot (lua_State *L) {
const char *funcname;
const char *filename;
const char *name = luaL_checkstring(L, 1);
const char *p = strchr(name, '.');
int stat;
if (p == NULL) return 0; /* is root */
lua_pushlstring(L, name, p - name);
filename = findfile(L, lua_tostring(L, -1), "cpath");
if (filename == NULL) return 1; /* root not found */
funcname = mkfuncname(L, name);
if ((stat = ll_loadfunc(L, filename, funcname)) != 0) {
if (stat != ERRFUNC) loaderror(L, filename); /* real error */
lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
name, filename);
return 1; /* function not found */
}
return 1;
}
static int loader_preload (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
lua_getfield(L, LUA_ENVIRONINDEX, "preload");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.preload") " must be a table");
lua_getfield(L, -1, name);
if (lua_isnil(L, -1)) /* not found? */
lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
return 1;
}
static const int sentinel_ = 0;
#define sentinel ((void *)&sentinel_)
static int ll_require (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
int i;
lua_settop(L, 1); /* _LOADED table will be at index 2 */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, 2, name);
if (lua_toboolean(L, -1)) { /* is it there? */
if (lua_touserdata(L, -1) == sentinel) /* check loops */
luaL_error(L, "loop or previous error loading module " LUA_QS, name);
return 1; /* package is already loaded */
}
/* else must load it; iterate over available loaders */
lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.loaders") " must be a table");
lua_pushliteral(L, ""); /* error message accumulator */
for (i=1; ; i++) {
lua_rawgeti(L, -2, i); /* get a loader */
if (lua_isnil(L, -1))
luaL_error(L, "module " LUA_QS " not found:%s",
name, lua_tostring(L, -2));
lua_pushstring(L, name);
lua_call(L, 1, 1); /* call it */
if (lua_isfunction(L, -1)) /* did it find module? */
break; /* module loaded successfully */
else if (lua_isstring(L, -1)) /* loader returned error message? */
lua_concat(L, 2); /* accumulate it */
else
lua_pop(L, 1);
}
lua_pushlightuserdata(L, sentinel);
lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
lua_pushstring(L, name); /* pass name as argument to module */
lua_call(L, 1, 1); /* run loaded module */
if (!lua_isnil(L, -1)) /* non-nil return? */
lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
lua_getfield(L, 2, name);
if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
lua_pushboolean(L, 1); /* use true as result */
lua_pushvalue(L, -1); /* extra copy to be returned */
lua_setfield(L, 2, name); /* _LOADED[name] = true */
}
return 1;
}
/* }====================================================== */
/*
** {======================================================
** 'module' function
** =======================================================
*/
static void setfenv (lua_State *L) {
lua_Debug ar;
if (lua_getstack(L, 1, &ar) == 0 ||
lua_getinfo(L, "f", &ar) == 0 || /* get calling function */
lua_iscfunction(L, -1))
luaL_error(L, LUA_QL("module") " not called from a Lua function");
lua_pushvalue(L, -2);
lua_setfenv(L, -2);
lua_pop(L, 1);
}
static void dooptions (lua_State *L, int n) {
int i;
for (i = 2; i <= n; i++) {
lua_pushvalue(L, i); /* get option (a function) */
lua_pushvalue(L, -2); /* module */
lua_call(L, 1, 0);
}
}
static void modinit (lua_State *L, const char *modname) {
const char *dot;
lua_pushvalue(L, -1);
lua_setfield(L, -2, "_M"); /* module._M = module */
lua_pushstring(L, modname);
lua_setfield(L, -2, "_NAME");
dot = strrchr(modname, '.'); /* look for last dot in module name */
if (dot == NULL) dot = modname;
else dot++;
/* set _PACKAGE as package name (full module name minus last part) */
lua_pushlstring(L, modname, dot - modname);
lua_setfield(L, -2, "_PACKAGE");
}
static int ll_module (lua_State *L) {
const char *modname = luaL_checkstring(L, 1);
int loaded = lua_gettop(L) + 1; /* index of _LOADED table */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, loaded, modname); /* get _LOADED[modname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)
return luaL_error(L, "name conflict for module " LUA_QS, modname);
lua_pushvalue(L, -1);
lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */
}
/* check whether table already has a _NAME field */
lua_getfield(L, -1, "_NAME");
if (!lua_isnil(L, -1)) /* is table an initialized module? */
lua_pop(L, 1);
else { /* no; initialize it */
lua_pop(L, 1);
modinit(L, modname);
}
lua_pushvalue(L, -1);
setfenv(L);
dooptions(L, loaded - 1);
return 0;
}
static int ll_seeall (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
if (!lua_getmetatable(L, 1)) {
lua_createtable(L, 0, 1); /* create new metatable */
lua_pushvalue(L, -1);
lua_setmetatable(L, 1);
}
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setfield(L, -2, "__index"); /* mt.__index = _G */
return 0;
}
/* }====================================================== */
/* auxiliary mark (for internal use) */
#define AUXMARK "\1"
static void setpath (lua_State *L, const char *fieldname, const char *envname,
const char *def) {
const char *path = getenv(envname);
if (path == NULL) /* no environment variable? */
lua_pushstring(L, def); /* use default */
else {
/* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
LUA_PATHSEP AUXMARK LUA_PATHSEP);
luaL_gsub(L, path, AUXMARK, def);
lua_remove(L, -2);
}
setprogdir(L);
lua_setfield(L, -2, fieldname);
}
static const luaL_Reg pk_funcs[] = {
{"loadlib", ll_loadlib},
{"seeall", ll_seeall},
{NULL, NULL}
};
static const luaL_Reg ll_funcs[] = {
{"module", ll_module},
{"require", ll_require},
{NULL, NULL}
};
static const lua_CFunction loaders[] =
{loader_preload, loader_Lua, loader_C, loader_Croot, NULL};
LUALIB_API int luaopen_package (lua_State *L) {
int i;
/* create new type _LOADLIB */
luaL_newmetatable(L, "_LOADLIB");
lua_pushcfunction(L, gctm);
lua_setfield(L, -2, "__gc");
/* create `package' table */
luaL_register(L, LUA_LOADLIBNAME, pk_funcs);
#if defined(LUA_COMPAT_LOADLIB)
lua_getfield(L, -1, "loadlib");
lua_setfield(L, LUA_GLOBALSINDEX, "loadlib");
#endif
lua_pushvalue(L, -1);
lua_replace(L, LUA_ENVIRONINDEX);
/* create `loaders' table */
lua_createtable(L, sizeof(loaders)/sizeof(loaders[0]) - 1, 0);
/* fill it with pre-defined loaders */
for (i=0; loaders[i] != NULL; i++) {
lua_pushcfunction(L, loaders[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */
setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */
setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */
/* store config information */
lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n"
LUA_EXECDIR "\n" LUA_IGMARK);
lua_setfield(L, -2, "config");
/* set field `loaded' */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
lua_setfield(L, -2, "loaded");
/* set field `preload' */
lua_newtable(L);
lua_setfield(L, -2, "preload");
lua_pushvalue(L, LUA_GLOBALSINDEX);
luaL_register(L, NULL, ll_funcs); /* open lib into global table */
lua_pop(L, 1);
return 1; /* return 'package' table */
}

214
extern/lua-5.1.5/src/lobject.c vendored Normal file
View file

@ -0,0 +1,214 @@
/*
** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $
** Some generic functions over Lua objects
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define lobject_c
#define LUA_CORE
#include "lua.h"
#include "ldo.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#include "lvm.h"
const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL};
/*
** converts an integer to a "floating point byte", represented as
** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
** eeeee != 0 and (xxx) otherwise.
*/
int luaO_int2fb (unsigned int x) {
int e = 0; /* expoent */
while (x >= 16) {
x = (x+1) >> 1;
e++;
}
if (x < 8) return x;
else return ((e+1) << 3) | (cast_int(x) - 8);
}
/* converts back */
int luaO_fb2int (int x) {
int e = (x >> 3) & 31;
if (e == 0) return x;
else return ((x & 7)+8) << (e - 1);
}
int luaO_log2 (unsigned int x) {
static const lu_byte log_2[256] = {
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
};
int l = -1;
while (x >= 256) { l += 8; x >>= 8; }
return l + log_2[x];
}
int luaO_rawequalObj (const TValue *t1, const TValue *t2) {
if (ttype(t1) != ttype(t2)) return 0;
else switch (ttype(t1)) {
case LUA_TNIL:
return 1;
case LUA_TNUMBER:
return luai_numeq(nvalue(t1), nvalue(t2));
case LUA_TBOOLEAN:
return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */
case LUA_TLIGHTUSERDATA:
return pvalue(t1) == pvalue(t2);
default:
lua_assert(iscollectable(t1));
return gcvalue(t1) == gcvalue(t2);
}
}
int luaO_str2d (const char *s, lua_Number *result) {
char *endptr;
*result = lua_str2number(s, &endptr);
if (endptr == s) return 0; /* conversion failed */
if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */
*result = cast_num(strtoul(s, &endptr, 16));
if (*endptr == '\0') return 1; /* most common case */
while (isspace(cast(unsigned char, *endptr))) endptr++;
if (*endptr != '\0') return 0; /* invalid trailing characters? */
return 1;
}
static void pushstr (lua_State *L, const char *str) {
setsvalue2s(L, L->top, luaS_new(L, str));
incr_top(L);
}
/* this function handles only `%d', `%c', %f, %p, and `%s' formats */
const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
int n = 1;
pushstr(L, "");
for (;;) {
const char *e = strchr(fmt, '%');
if (e == NULL) break;
setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt));
incr_top(L);
switch (*(e+1)) {
case 's': {
const char *s = va_arg(argp, char *);
if (s == NULL) s = "(null)";
pushstr(L, s);
break;
}
case 'c': {
char buff[2];
buff[0] = cast(char, va_arg(argp, int));
buff[1] = '\0';
pushstr(L, buff);
break;
}
case 'd': {
setnvalue(L->top, cast_num(va_arg(argp, int)));
incr_top(L);
break;
}
case 'f': {
setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber)));
incr_top(L);
break;
}
case 'p': {
char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */
sprintf(buff, "%p", va_arg(argp, void *));
pushstr(L, buff);
break;
}
case '%': {
pushstr(L, "%");
break;
}
default: {
char buff[3];
buff[0] = '%';
buff[1] = *(e+1);
buff[2] = '\0';
pushstr(L, buff);
break;
}
}
n += 2;
fmt = e+2;
}
pushstr(L, fmt);
luaV_concat(L, n+1, cast_int(L->top - L->base) - 1);
L->top -= n;
return svalue(L->top - 1);
}
const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
const char *msg;
va_list argp;
va_start(argp, fmt);
msg = luaO_pushvfstring(L, fmt, argp);
va_end(argp);
return msg;
}
void luaO_chunkid (char *out, const char *source, size_t bufflen) {
if (*source == '=') {
strncpy(out, source+1, bufflen); /* remove first char */
out[bufflen-1] = '\0'; /* ensures null termination */
}
else { /* out = "source", or "...source" */
if (*source == '@') {
size_t l;
source++; /* skip the `@' */
bufflen -= sizeof(" '...' ");
l = strlen(source);
strcpy(out, "");
if (l > bufflen) {
source += (l-bufflen); /* get last part of file name */
strcat(out, "...");
}
strcat(out, source);
}
else { /* out = [string "string"] */
size_t len = strcspn(source, "\n\r"); /* stop at first newline */
bufflen -= sizeof(" [string \"...\"] ");
if (len > bufflen) len = bufflen;
strcpy(out, "[string \"");
if (source[len] != '\0') { /* must truncate? */
strncat(out, source, len);
strcat(out, "...");
}
else
strcat(out, source);
strcat(out, "\"]");
}
}
}

381
extern/lua-5.1.5/src/lobject.h vendored Normal file
View file

@ -0,0 +1,381 @@
/*
** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $
** Type definitions for Lua objects
** See Copyright Notice in lua.h
*/
#ifndef lobject_h
#define lobject_h
#include <stdarg.h>
#include "llimits.h"
#include "lua.h"
/* tags for values visible from Lua */
#define LAST_TAG LUA_TTHREAD
#define NUM_TAGS (LAST_TAG+1)
/*
** Extra tags for non-values
*/
#define LUA_TPROTO (LAST_TAG+1)
#define LUA_TUPVAL (LAST_TAG+2)
#define LUA_TDEADKEY (LAST_TAG+3)
/*
** Union of all collectable objects
*/
typedef union GCObject GCObject;
/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
/*
** Common header in struct form
*/
typedef struct GCheader {
CommonHeader;
} GCheader;
/*
** Union of all Lua values
*/
typedef union {
GCObject *gc;
void *p;
lua_Number n;
int b;
} Value;
/*
** Tagged Values
*/
#define TValuefields Value value; int tt
typedef struct lua_TValue {
TValuefields;
} TValue;
/* Macros to test type */
#define ttisnil(o) (ttype(o) == LUA_TNIL)
#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
#define ttisstring(o) (ttype(o) == LUA_TSTRING)
#define ttistable(o) (ttype(o) == LUA_TTABLE)
#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
/* Macros to access values */
#define ttype(o) ((o)->tt)
#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
#define tsvalue(o) (&rawtsvalue(o)->tsv)
#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
#define uvalue(o) (&rawuvalue(o)->uv)
#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
/*
** for internal debug only
*/
#define checkconsistency(obj) \
lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))
#define checkliveness(g,obj) \
lua_assert(!iscollectable(obj) || \
((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
/* Macros to set values */
#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
#define setnvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
#define setpvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }
#define setbvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
#define setsvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \
checkliveness(G(L),i_o); }
#define setuvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \
checkliveness(G(L),i_o); }
#define setthvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \
checkliveness(G(L),i_o); }
#define setclvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \
checkliveness(G(L),i_o); }
#define sethvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
checkliveness(G(L),i_o); }
#define setptvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \
checkliveness(G(L),i_o); }
#define setobj(L,obj1,obj2) \
{ const TValue *o2=(obj2); TValue *o1=(obj1); \
o1->value = o2->value; o1->tt=o2->tt; \
checkliveness(G(L),o1); }
/*
** different types of sets, according to destination
*/
/* from stack to (same) stack */
#define setobjs2s setobj
/* to stack (not from same stack) */
#define setobj2s setobj
#define setsvalue2s setsvalue
#define sethvalue2s sethvalue
#define setptvalue2s setptvalue
/* from table to same table */
#define setobjt2t setobj
/* to table */
#define setobj2t setobj
/* to new object */
#define setobj2n setobj
#define setsvalue2n setsvalue
#define setttype(obj, tt) (ttype(obj) = (tt))
#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
typedef TValue *StkId; /* index to stack elements */
/*
** String headers for string table
*/
typedef union TString {
L_Umaxalign dummy; /* ensures maximum alignment for strings */
struct {
CommonHeader;
lu_byte reserved;
unsigned int hash;
size_t len;
} tsv;
} TString;
#define getstr(ts) cast(const char *, (ts) + 1)
#define svalue(o) getstr(rawtsvalue(o))
typedef union Udata {
L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
struct {
CommonHeader;
struct Table *metatable;
struct Table *env;
size_t len;
} uv;
} Udata;
/*
** Function Prototypes
*/
typedef struct Proto {
CommonHeader;
TValue *k; /* constants used by the function */
Instruction *code;
struct Proto **p; /* functions defined inside the function */
int *lineinfo; /* map from opcodes to source lines */
struct LocVar *locvars; /* information about local variables */
TString **upvalues; /* upvalue names */
TString *source;
int sizeupvalues;
int sizek; /* size of `k' */
int sizecode;
int sizelineinfo;
int sizep; /* size of `p' */
int sizelocvars;
int linedefined;
int lastlinedefined;
GCObject *gclist;
lu_byte nups; /* number of upvalues */
lu_byte numparams;
lu_byte is_vararg;
lu_byte maxstacksize;
} Proto;
/* masks for new-style vararg */
#define VARARG_HASARG 1
#define VARARG_ISVARARG 2
#define VARARG_NEEDSARG 4
typedef struct LocVar {
TString *varname;
int startpc; /* first point where variable is active */
int endpc; /* first point where variable is dead */
} LocVar;
/*
** Upvalues
*/
typedef struct UpVal {
CommonHeader;
TValue *v; /* points to stack or to its own value */
union {
TValue value; /* the value (when closed) */
struct { /* double linked list (when open) */
struct UpVal *prev;
struct UpVal *next;
} l;
} u;
} UpVal;
/*
** Closures
*/
#define ClosureHeader \
CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
struct Table *env
typedef struct CClosure {
ClosureHeader;
lua_CFunction f;
TValue upvalue[1];
} CClosure;
typedef struct LClosure {
ClosureHeader;
struct Proto *p;
UpVal *upvals[1];
} LClosure;
typedef union Closure {
CClosure c;
LClosure l;
} Closure;
#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)
#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)
/*
** Tables
*/
typedef union TKey {
struct {
TValuefields;
struct Node *next; /* for chaining */
} nk;
TValue tvk;
} TKey;
typedef struct Node {
TValue i_val;
TKey i_key;
} Node;
typedef struct Table {
CommonHeader;
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
lu_byte lsizenode; /* log2 of size of `node' array */
struct Table *metatable;
TValue *array; /* array part */
Node *node;
Node *lastfree; /* any free position is before this position */
GCObject *gclist;
int sizearray; /* size of `array' array */
} Table;
/*
** `module' operation for hashing (size is always a power of 2)
*/
#define lmod(s,size) \
(check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))
#define twoto(x) (1<<(x))
#define sizenode(t) (twoto((t)->lsizenode))
#define luaO_nilobject (&luaO_nilobject_)
LUAI_DATA const TValue luaO_nilobject_;
#define ceillog2(x) (luaO_log2((x)-1) + 1)
LUAI_FUNC int luaO_log2 (unsigned int x);
LUAI_FUNC int luaO_int2fb (unsigned int x);
LUAI_FUNC int luaO_fb2int (int x);
LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);
LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);
LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
va_list argp);
LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);
#endif

102
extern/lua-5.1.5/src/lopcodes.c vendored Normal file
View file

@ -0,0 +1,102 @@
/*
** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
** See Copyright Notice in lua.h
*/
#define lopcodes_c
#define LUA_CORE
#include "lopcodes.h"
/* ORDER OP */
const char *const luaP_opnames[NUM_OPCODES+1] = {
"MOVE",
"LOADK",
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"GETGLOBAL",
"GETTABLE",
"SETGLOBAL",
"SETUPVAL",
"SETTABLE",
"NEWTABLE",
"SELF",
"ADD",
"SUB",
"MUL",
"DIV",
"MOD",
"POW",
"UNM",
"NOT",
"LEN",
"CONCAT",
"JMP",
"EQ",
"LT",
"LE",
"TEST",
"TESTSET",
"CALL",
"TAILCALL",
"RETURN",
"FORLOOP",
"FORPREP",
"TFORLOOP",
"SETLIST",
"CLOSE",
"CLOSURE",
"VARARG",
NULL
};
#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
const lu_byte luaP_opmodes[NUM_OPCODES] = {
/* T A B C mode opcode */
opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */
,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */
,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */
,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */
,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */
,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */
,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */
,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */
,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */
,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */
,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */
,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */
,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */
};

268
extern/lua-5.1.5/src/lopcodes.h vendored Normal file
View file

@ -0,0 +1,268 @@
/*
** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
#ifndef lopcodes_h
#define lopcodes_h
#include "llimits.h"
/*===========================================================================
We assume that instructions are unsigned numbers.
All instructions have an opcode in the first 6 bits.
Instructions can have the following fields:
`A' : 8 bits
`B' : 9 bits
`C' : 9 bits
`Bx' : 18 bits (`B' and `C' together)
`sBx' : signed Bx
A signed argument is represented in excess K; that is, the number
value is the unsigned value minus K. K is exactly the maximum value
for that argument (so that -max is represented by 0, and +max is
represented by 2*max), which is half the maximum for the corresponding
unsigned argument.
===========================================================================*/
enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
/*
** size and position of opcode arguments.
*/
#define SIZE_C 9
#define SIZE_B 9
#define SIZE_Bx (SIZE_C + SIZE_B)
#define SIZE_A 8
#define SIZE_OP 6
#define POS_OP 0
#define POS_A (POS_OP + SIZE_OP)
#define POS_C (POS_A + SIZE_A)
#define POS_B (POS_C + SIZE_C)
#define POS_Bx POS_C
/*
** limits for opcode arguments.
** we use (signed) int to manipulate most arguments,
** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
*/
#if SIZE_Bx < LUAI_BITSINT-1
#define MAXARG_Bx ((1<<SIZE_Bx)-1)
#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
#else
#define MAXARG_Bx MAX_INT
#define MAXARG_sBx MAX_INT
#endif
#define MAXARG_A ((1<<SIZE_A)-1)
#define MAXARG_B ((1<<SIZE_B)-1)
#define MAXARG_C ((1<<SIZE_C)-1)
/* creates a mask with `n' 1 bits at position `p' */
#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p)
/* creates a mask with `n' 0 bits at position `p' */
#define MASK0(n,p) (~MASK1(n,p))
/*
** the following macros help to manipulate instructions
*/
#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
#define GETARG_A(i) (cast(int, ((i)>>POS_A) & MASK1(SIZE_A,0)))
#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
((cast(Instruction, u)<<POS_A)&MASK1(SIZE_A,POS_A))))
#define GETARG_B(i) (cast(int, ((i)>>POS_B) & MASK1(SIZE_B,0)))
#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
((cast(Instruction, b)<<POS_B)&MASK1(SIZE_B,POS_B))))
#define GETARG_C(i) (cast(int, ((i)>>POS_C) & MASK1(SIZE_C,0)))
#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \
((cast(Instruction, b)<<POS_C)&MASK1(SIZE_C,POS_C))))
#define GETARG_Bx(i) (cast(int, ((i)>>POS_Bx) & MASK1(SIZE_Bx,0)))
#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \
((cast(Instruction, b)<<POS_Bx)&MASK1(SIZE_Bx,POS_Bx))))
#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
#define SETARG_sBx(i,b) SETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))
#define CREATE_ABC(o,a,b,c) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_A) \
| (cast(Instruction, b)<<POS_B) \
| (cast(Instruction, c)<<POS_C))
#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_A) \
| (cast(Instruction, bc)<<POS_Bx))
/*
** Macros to operate RK indices
*/
/* this bit 1 means constant (0 means register) */
#define BITRK (1 << (SIZE_B - 1))
/* test whether value is a constant */
#define ISK(x) ((x) & BITRK)
/* gets the index of the constant */
#define INDEXK(r) ((int)(r) & ~BITRK)
#define MAXINDEXRK (BITRK - 1)
/* code a constant index as a RK value */
#define RKASK(x) ((x) | BITRK)
/*
** invalid register that fits in 8 bits
*/
#define NO_REG MAXARG_A
/*
** R(x) - register
** Kst(x) - constant (in constant table)
** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
*/
/*
** grep "ORDER OP" if you change these enums
*/
typedef enum {
/*----------------------------------------------------------------------
name args description
------------------------------------------------------------------------*/
OP_MOVE,/* A B R(A) := R(B) */
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */
OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */
OP_SETUPVAL,/* A B UpValue[B] := R(A) */
OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
OP_UNM,/* A B R(A) := -R(B) */
OP_NOT,/* A B R(A) := not R(B) */
OP_LEN,/* A B R(A) := length of R(B) */
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
OP_JMP,/* sBx pc+=sBx */
OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
OP_FORLOOP,/* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
} OpCode;
#define NUM_OPCODES (cast(int, OP_VARARG) + 1)
/*===========================================================================
Notes:
(*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
and can be 0: OP_CALL then sets `top' to last_result+1, so
next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
(*) In OP_VARARG, if (B == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0).
(*) In OP_RETURN, if (B == 0) then return up to `top'
(*) In OP_SETLIST, if (B == 0) then B = `top';
if (C == 0) then next `instruction' is real C
(*) For comparisons, A specifies what condition the test should accept
(true or false).
(*) All `skips' (pc++) assume that next instruction is a jump
===========================================================================*/
/*
** masks for instruction properties. The format is:
** bits 0-1: op mode
** bits 2-3: C arg mode
** bits 4-5: B arg mode
** bit 6: instruction set register A
** bit 7: operator is a test
*/
enum OpArgMask {
OpArgN, /* argument is not used */
OpArgU, /* argument is used */
OpArgR, /* argument is a register or a jump offset */
OpArgK /* argument is a constant or register/constant */
};
LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];
#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */
/* number of list items to accumulate before a SETLIST instruction */
#define LFIELDS_PER_FLUSH 50
#endif

243
extern/lua-5.1.5/src/loslib.c vendored Normal file
View file

@ -0,0 +1,243 @@
/*
** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $
** Standard Operating System library
** See Copyright Notice in lua.h
*/
#include <errno.h>
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define loslib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static int os_pushresult (lua_State *L, int i, const char *filename) {
int en = errno; /* calls to Lua API may change this value */
if (i) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushnil(L);
lua_pushfstring(L, "%s: %s", filename, strerror(en));
lua_pushinteger(L, en);
return 3;
}
}
static int os_execute (lua_State *L) {
lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));
return 1;
}
static int os_remove (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
return os_pushresult(L, remove(filename) == 0, filename);
}
static int os_rename (lua_State *L) {
const char *fromname = luaL_checkstring(L, 1);
const char *toname = luaL_checkstring(L, 2);
return os_pushresult(L, rename(fromname, toname) == 0, fromname);
}
static int os_tmpname (lua_State *L) {
char buff[LUA_TMPNAMBUFSIZE];
int err;
lua_tmpnam(buff, err);
if (err)
return luaL_error(L, "unable to generate a unique filename");
lua_pushstring(L, buff);
return 1;
}
static int os_getenv (lua_State *L) {
lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
return 1;
}
static int os_clock (lua_State *L) {
lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
return 1;
}
/*
** {======================================================
** Time/Date operations
** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
** wday=%w+1, yday=%j, isdst=? }
** =======================================================
*/
static void setfield (lua_State *L, const char *key, int value) {
lua_pushinteger(L, value);
lua_setfield(L, -2, key);
}
static void setboolfield (lua_State *L, const char *key, int value) {
if (value < 0) /* undefined? */
return; /* does not set field */
lua_pushboolean(L, value);
lua_setfield(L, -2, key);
}
static int getboolfield (lua_State *L, const char *key) {
int res;
lua_getfield(L, -1, key);
res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
lua_pop(L, 1);
return res;
}
static int getfield (lua_State *L, const char *key, int d) {
int res;
lua_getfield(L, -1, key);
if (lua_isnumber(L, -1))
res = (int)lua_tointeger(L, -1);
else {
if (d < 0)
return luaL_error(L, "field " LUA_QS " missing in date table", key);
res = d;
}
lua_pop(L, 1);
return res;
}
static int os_date (lua_State *L) {
const char *s = luaL_optstring(L, 1, "%c");
time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
struct tm *stm;
if (*s == '!') { /* UTC? */
stm = gmtime(&t);
s++; /* skip `!' */
}
else
stm = localtime(&t);
if (stm == NULL) /* invalid date? */
lua_pushnil(L);
else if (strcmp(s, "*t") == 0) {
lua_createtable(L, 0, 9); /* 9 = number of fields */
setfield(L, "sec", stm->tm_sec);
setfield(L, "min", stm->tm_min);
setfield(L, "hour", stm->tm_hour);
setfield(L, "day", stm->tm_mday);
setfield(L, "month", stm->tm_mon+1);
setfield(L, "year", stm->tm_year+1900);
setfield(L, "wday", stm->tm_wday+1);
setfield(L, "yday", stm->tm_yday+1);
setboolfield(L, "isdst", stm->tm_isdst);
}
else {
char cc[3];
luaL_Buffer b;
cc[0] = '%'; cc[2] = '\0';
luaL_buffinit(L, &b);
for (; *s; s++) {
if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */
luaL_addchar(&b, *s);
else {
size_t reslen;
char buff[200]; /* should be big enough for any conversion result */
cc[1] = *(++s);
reslen = strftime(buff, sizeof(buff), cc, stm);
luaL_addlstring(&b, buff, reslen);
}
}
luaL_pushresult(&b);
}
return 1;
}
static int os_time (lua_State *L) {
time_t t;
if (lua_isnoneornil(L, 1)) /* called without args? */
t = time(NULL); /* get current time */
else {
struct tm ts;
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1); /* make sure table is at the top */
ts.tm_sec = getfield(L, "sec", 0);
ts.tm_min = getfield(L, "min", 0);
ts.tm_hour = getfield(L, "hour", 12);
ts.tm_mday = getfield(L, "day", -1);
ts.tm_mon = getfield(L, "month", -1) - 1;
ts.tm_year = getfield(L, "year", -1) - 1900;
ts.tm_isdst = getboolfield(L, "isdst");
t = mktime(&ts);
}
if (t == (time_t)(-1))
lua_pushnil(L);
else
lua_pushnumber(L, (lua_Number)t);
return 1;
}
static int os_difftime (lua_State *L) {
lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
(time_t)(luaL_optnumber(L, 2, 0))));
return 1;
}
/* }====================================================== */
static int os_setlocale (lua_State *L) {
static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
LC_NUMERIC, LC_TIME};
static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
"numeric", "time", NULL};
const char *l = luaL_optstring(L, 1, NULL);
int op = luaL_checkoption(L, 2, "all", catnames);
lua_pushstring(L, setlocale(cat[op], l));
return 1;
}
static int os_exit (lua_State *L) {
exit(luaL_optint(L, 1, EXIT_SUCCESS));
}
static const luaL_Reg syslib[] = {
{"clock", os_clock},
{"date", os_date},
{"difftime", os_difftime},
{"execute", os_execute},
{"exit", os_exit},
{"getenv", os_getenv},
{"remove", os_remove},
{"rename", os_rename},
{"setlocale", os_setlocale},
{"time", os_time},
{"tmpname", os_tmpname},
{NULL, NULL}
};
/* }====================================================== */
LUALIB_API int luaopen_os (lua_State *L) {
luaL_register(L, LUA_OSLIBNAME, syslib);
return 1;
}

1339
extern/lua-5.1.5/src/lparser.c vendored Normal file

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more