From 40ae5055aac160d374180b145e4a100c8a2593c1 Mon Sep 17 00:00:00 2001 From: Thijs Daniels Date: Mon, 21 Sep 2026 03:14:18 +0200 Subject: [PATCH 1/2] fix(addon): support Classic Era and WoW Forever clients without GetSpecialization --- WowAddon/DecktationContext/CHANGELOG.md | 13 +++++ .../DecktationContext/DecktationContext.lua | 50 +++++++++++-------- .../DecktationContext/DecktationContext.toc | 4 +- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/WowAddon/DecktationContext/CHANGELOG.md b/WowAddon/DecktationContext/CHANGELOG.md index b674fb0..2bed99f 100644 --- a/WowAddon/DecktationContext/CHANGELOG.md +++ b/WowAddon/DecktationContext/CHANGELOG.md @@ -1,5 +1,18 @@ # Decktation Context Addon - Changelog +## Version 1.1.1 (2026-09-21) + +### Classic & WoW Forever Compatibility Update + +**Fixed:** +- Fixed Lua error calling nil `GetSpecialization()` on Classic Era and WoW Forever clients +- Added talent tree fallback to determine active spec based on spent talent points in Classic +- Registered `PLAYER_TALENT_UPDATE` and `CHARACTER_POINTS_CHANGED` events +- Fixed duplicate `EventFrame:SetScript("OnEvent")` call overwriting main event handlers + +**Added:** +- Added interface numbers for Classic Era and WoW Forever to TOC + ## Version 1.1.0 (2026-02-07) ### Midnight Compatibility Update diff --git a/WowAddon/DecktationContext/DecktationContext.lua b/WowAddon/DecktationContext/DecktationContext.lua index 587d418..7aff4ed 100644 --- a/WowAddon/DecktationContext/DecktationContext.lua +++ b/WowAddon/DecktationContext/DecktationContext.lua @@ -73,6 +73,29 @@ local function GetZoneInfo() return zone, subzone end +-- Get specialization (Retail) or talent tree with most points (Classic) +local function GetPlayerSpec() + if GetSpecialization then + local specIndex = GetSpecialization() + if specIndex then + local _, specName = GetSpecializationInfo(specIndex) + return specName or "" + end + elseif GetNumTalentTabs and GetTalentTabInfo then + local maxPoints = 0 + local activeSpec = "" + for tabIndex = 1, GetNumTalentTabs() do + local name, _, pointsSpent = GetTalentTabInfo(tabIndex) + if pointsSpent and pointsSpent > maxPoints then + maxPoints = pointsSpent + activeSpec = name or "" + end + end + return activeSpec + end + return "" +end + -- Update current context function UpdateContext() -- Wrap in pcall to prevent crashes @@ -145,13 +168,7 @@ function UpdateContext() Context.class = classToken or "" -- Get specialization - local specIndex = GetSpecialization() - if specIndex then - local _, specName = GetSpecializationInfo(specIndex) - Context.spec = specName or "" - else - Context.spec = "" - end + Context.spec = GetPlayerSpec() Context.lastUpdate = time() end) @@ -300,6 +317,9 @@ EventFrame:RegisterEvent("GROUP_ROSTER_UPDATE") EventFrame:RegisterEvent("ENCOUNTER_START") EventFrame:RegisterEvent("ENCOUNTER_END") EventFrame:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED") +EventFrame:RegisterEvent("PLAYER_TALENT_UPDATE") +EventFrame:RegisterEvent("CHARACTER_POINTS_CHANGED") +EventFrame:RegisterEvent("PLAYER_LOGOUT") EventFrame:SetScript("OnEvent", function(self, event, ...) if event == "PLAYER_LOGIN" then @@ -323,20 +343,10 @@ EventFrame:SetScript("OnEvent", function(self, event, ...) elseif event == "ENCOUNTER_END" then Context.boss = "" SaveContext() - elseif event == "PLAYER_SPECIALIZATION_CHANGED" then - local specIndex = GetSpecialization() - if specIndex then - local _, specName = GetSpecializationInfo(specIndex) - Context.spec = specName or "" - end + elseif event == "PLAYER_SPECIALIZATION_CHANGED" or event == "PLAYER_TALENT_UPDATE" or event == "CHARACTER_POINTS_CHANGED" then + Context.spec = GetPlayerSpec() SaveContext() - end -end) - --- Force save on logout -EventFrame:RegisterEvent("PLAYER_LOGOUT") -EventFrame:SetScript("OnEvent", function(self, event) - if event == "PLAYER_LOGOUT" then + elseif event == "PLAYER_LOGOUT" then SaveContext() end end) diff --git a/WowAddon/DecktationContext/DecktationContext.toc b/WowAddon/DecktationContext/DecktationContext.toc index 7c7e918..0280c09 100644 --- a/WowAddon/DecktationContext/DecktationContext.toc +++ b/WowAddon/DecktationContext/DecktationContext.toc @@ -1,8 +1,8 @@ -## Interface: 120000 +## Interface: 120000, 11506, 16001, 160000 ## Title: Decktation Context ## Notes: Exports WoW context for Decktation voice-to-text plugin ## Author: Decktation -## Version: 1.1.0 +## Version: 1.1.1 ## IconTexture: Interface\Icons\Ability_Warrior_CommandingShout ## SavedVariables: DecktationContextDB, DecktationErrorLog ## X-Category: Communication From 35aa327573e7742cd8ad5f984db993f052615e13 Mon Sep 17 00:00:00 2001 From: Thijs Daniels Date: Sat, 26 Sep 2026 18:30:28 +0200 Subject: [PATCH 2/2] fix(addon): add Classic zone API fallbacks for WoW Forever beta maps --- WowAddon/DecktationContext/CHANGELOG.md | 1 + .../DecktationContext/DecktationContext.lua | 39 ++++++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/WowAddon/DecktationContext/CHANGELOG.md b/WowAddon/DecktationContext/CHANGELOG.md index 2bed99f..0082f18 100644 --- a/WowAddon/DecktationContext/CHANGELOG.md +++ b/WowAddon/DecktationContext/CHANGELOG.md @@ -9,6 +9,7 @@ - Added talent tree fallback to determine active spec based on spent talent points in Classic - Registered `PLAYER_TALENT_UPDATE` and `CHARACTER_POINTS_CHANGED` events - Fixed duplicate `EventFrame:SetScript("OnEvent")` call overwriting main event handlers +- Added fallback to Classic zone APIs (`GetRealZoneText`, `GetZoneText`, `GetSubZoneText`) when `C_Map` is unavailable or returns empty (e.g. on WoW Forever beta maps) **Added:** - Added interface numbers for Classic Era and WoW Forever to TOC diff --git a/WowAddon/DecktationContext/DecktationContext.lua b/WowAddon/DecktationContext/DecktationContext.lua index 7aff4ed..f6c428a 100644 --- a/WowAddon/DecktationContext/DecktationContext.lua +++ b/WowAddon/DecktationContext/DecktationContext.lua @@ -57,20 +57,41 @@ local function Initialize() SaveContext() end --- Get zone information using modern C_Map API (Midnight-compatible) +-- Get zone information using modern C_Map API with fallback to Classic APIs local function GetZoneInfo() - local mapID = C_Map.GetBestMapForUnit("player") - if not mapID then - return "", "" + local zone = "" + local subzone = "" + + -- Try modern C_Map API first (Retail / Midnight / TWW) + if C_Map and C_Map.GetBestMapForUnit and C_Map.GetMapInfo then + local mapID = C_Map.GetBestMapForUnit("player") + if mapID then + local mapInfo = C_Map.GetMapInfo(mapID) + if mapInfo and mapInfo.name and mapInfo.name ~= "" then + zone = mapInfo.name + end + end end - local mapInfo = C_Map.GetMapInfo(mapID) - local zone = mapInfo and mapInfo.name or "" + -- Fallback to Classic / legacy zone APIs if C_Map returned nil or empty + if not zone or zone == "" then + if GetRealZoneText then + zone = GetRealZoneText() or "" + end + if (not zone or zone == "") and GetZoneText then + zone = GetZoneText() or "" + end + end - -- Get subzone from minimap text (still works in Midnight) - local subzone = GetMinimapZoneText() or "" + -- Get subzone from minimap or subzone text + if GetMinimapZoneText then + subzone = GetMinimapZoneText() or "" + end + if (not subzone or subzone == "") and GetSubZoneText then + subzone = GetSubZoneText() or "" + end - return zone, subzone + return zone or "", subzone or "" end -- Get specialization (Retail) or talent tree with most points (Classic)