62 Commits

Author SHA1 Message Date
7eee3a13a6 Add Russian localization who (kto) for our russian friends 2025-05-25 11:09:43 +02:00
263cf8e2e4 Add chatSniffer localization and configuration UI 2025-05-25 10:58:08 +02:00
ccbf0f8dc2 Add chatSniffer configuration and integrate into Heimdall initialization 2025-05-25 10:54:01 +02:00
63027c2dcf Update module initialization messages to use consistent format 2025-05-25 10:51:41 +02:00
d46c874604 Add timestamp logging to ChatSniffer events 2025-05-25 10:48:11 +02:00
fd4f707b6c Add ChatSniffer module and update saved variables 2025-05-25 10:47:58 +02:00
1168876dcc Fix FriendsFrame_OnEvent to pass additional parameters so it actually works naturally 2025-05-25 10:45:17 +02:00
bdf5afe436 Comment out debug print statements in Messenger module and fix queue loading 2025-05-25 10:41:02 +02:00
85ff907f05 Reset whoQueryIdx to 1 when no WHO query is found 2025-05-25 10:39:45 +02:00
7ae9db030b Remove redundant event registration for ADDON_LOADED in Heimdall.lua 2025-05-25 10:34:48 +02:00
edf8a12865 Refactor message queuing to use NetworkMessenger and Messenger based on configuration 2025-05-21 01:42:41 +02:00
d081eedd47 Fix debug print statement to include message count 2025-05-20 20:45:42 +02:00
8532db5a25 Rename dumpTable to dump and make it work with any values 2025-05-20 20:40:25 +02:00
26e783ee2e Update subproject commit reference in Meta 2025-05-20 20:20:59 +02:00
4bd237abef Hook friends list show to NOT show while we're waiting for who results 2025-05-20 20:19:41 +02:00
0ab14de0e2 CCP 2025-05-20 20:04:41 +02:00
a25b6a20d5 Release 3.12.0 2025-05-20 20:02:47 +02:00
e85c14ea45 Update StinkyTracker to use ReactiveValue for accessing ignored and stinkies lists 2025-05-18 16:05:12 +02:00
a564178ca2 Fix dumpTable function to ensure keys and values are converted to strings before printing 2025-05-18 16:05:03 +02:00
b4a4011b18 Refactor NetworkMessenger initialization to use ReactiveValue for queue management 2025-05-18 16:00:39 +02:00
3f3d252104 Update subproject commit reference in Meta 2025-05-18 15:54:48 +02:00
287be2a31c Move data definitions into their separate modules 2025-05-18 15:54:38 +02:00
3ef0e4c935 Refactor Heimdall messenger module to improve structure and utilize ReactiveValue for queue management 2025-05-18 15:54:11 +02:00
ce92e8e12c Refactor Commander and StinkyTracker modules for improved clarity and consistency in type annotations 2025-05-18 12:48:26 +02:00
03597d1b5e Update subproject commit reference in Meta 2025-05-18 12:45:11 +02:00
36ad9783e5 Move all config definitions to their respective modules 2025-05-18 12:43:55 +02:00
565db30125 Refactor multiple Heimdall modules to use class-based structure for improved organization and clarity 2025-05-18 12:31:26 +02:00
017cbf01f8 Refactor Heimdall modules to use class-based structure for improved organization and clarity 2025-05-18 12:28:25 +02:00
b16cf762ac Refactor Heimdall modules to improve structure and clarity, including AchievementSniffer, BonkDetector, Bully, Commander, and Config. 2025-05-18 12:27:00 +02:00
0057ac3a5c Refactor CombatAlerter, Commander, Inviter, Macroer, Sniffer modules for improved structure and clarity 2025-05-18 12:12:57 +02:00
0edf0561d8 Refactor DeathReporter, CombatAlerter, Commander, Configurator, and AgentTracker modules 2025-05-18 11:48:50 +02:00
1129d787b5 Refactor BonkDetector and Bully modules for improved structure and clarity 2025-05-18 11:43:40 +02:00
8a24496801 Add scratch.lua to .gitignore 2025-05-18 11:37:05 +02:00
6cb918c13c Refactor Heimdall module fields for improved organization and clarity 2025-05-18 11:36:40 +02:00
e3eefadb75 Refactor AgentTracker and related modules to improve agent management and logging 2025-05-18 11:16:13 +02:00
eab562b36d Enhance dumpTable function to include optional message parameter for improved logging 2025-05-18 11:15:52 +02:00
20a7c0eead Refactor StinkyTracker to improve tracking and ignore functionality 2025-05-18 10:51:33 +02:00
f70c5adfcf Add debug logging for stinky changes and simplify stinky handling 2025-05-18 10:31:36 +02:00
52246e2e16 Update subproject commit reference in Meta 2025-05-18 10:18:45 +02:00
4897a5b1a9 Update ticker field type in HeimdallMessengerData and HeimdallNetworkMessengerData 2025-05-18 10:18:11 +02:00
2381f68912 Updater meta config 2025-05-18 10:10:45 +02:00
ad969e8604 Update subproject commit reference in Meta 2025-05-18 10:09:02 +02:00
31678f9a82 Fix config frame visibility in shared.Config.Init() 2025-05-18 10:08:23 +02:00
1785af4d9d Remove the premature exit 2025-05-05 01:16:57 +02:00
69d7efe6f0 Release 3.11.0 2025-05-05 01:16:31 +02:00
202c5d0f46 Update release script to automatically tag releases
Maybe it's a bit better who knows...........
2025-05-05 01:16:21 +02:00
476e907205 Release 3.10.4 2025-05-05 01:15:55 +02:00
724194abe2 Fix up the release to be a little more manual 2025-05-05 01:02:59 +02:00
d57b573683 Update
Some checks failed
Release Workflow / release (push) Failing after 18s
2025-05-05 00:58:16 +02:00
5d8afe8db7 Add the FUCKING REMOVED _
Some checks failed
Release Workflow / release (push) Failing after 18s
2025-05-05 00:30:58 +02:00
b14f1ff2f9 Update
Some checks failed
Release Workflow / release (push) Failing after 18s
2025-05-05 00:02:47 +02:00
a1301abdb2 Update 2025-05-05 00:02:38 +02:00
f897183920 Update 2025-05-04 23:56:58 +02:00
c1885ce76a Fix up config to comply with meta
Update
2025-05-04 23:39:35 +02:00
e676d53e97 Update meta 2025-05-04 23:23:48 +02:00
f05156b257 Update 2025-05-04 21:38:03 +02:00
c8f9d81b3e Remove meaningless vscode settings 2025-05-04 21:37:57 +02:00
3a1639ab27 Clean up the modules a little 2025-05-04 20:47:00 +02:00
1da1e7bf9f Code format
Some checks failed
Release Workflow / release (push) Failing after 23s
2025-05-04 15:09:47 +02:00
304fbcbaae Update meta
Some checks failed
Release Workflow / release (push) Failing after 20s
Update meta
2025-05-04 15:07:42 +02:00
80f8500f6e Add luacheckrcAdd debug logs
Some checks failed
Release Workflow / release (push) Failing after 18s
2025-04-30 20:18:15 +02:00
78cbcbde9d Add luacheckrc 2025-04-30 20:13:10 +02:00
40 changed files with 8435 additions and 7529 deletions

View File

@@ -1,86 +0,0 @@
name: Release Workflow
on: [push]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install zip
run: sudo apt-get install zip
- name: Configure Git
run: |
git config --global user.name "Git Admin"
git config --global user.email "gitadmin@quack-lab.dev"
- name: Run deploy script
run: bash deploy.sh
- name: Check for Existing Release Commit
id: check_release_commit
run: |
# Check if the latest commit is a release commit
if git log -1 --pretty=%B | grep -q "Release"; then
echo "Release commit found. Skipping version bump."
echo "::set-output name=release_commit::true"
else
echo "No release commit found. Proceeding with version bump."
echo "::set-output name=release_commit::false"
fi
- name: Run Release Script
if: steps.check_release_commit.outputs.release_commit == 'false'
run: bash release.sh
- name: Push Changes
if: steps.check_release_commit.outputs.release_commit == 'false'
run: |
git push origin HEAD:refs/heads/master --tags
- name: Determine Tag
id: determine_tag
run: |
# Check if the last commit has a tag
TAG=$(git describe --tags --exact-match 2>/dev/null || echo "")
if [ -z "$TAG" ]; then
# Get the latest tag
LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
# Increment the minor version
IFS='.' read -r -a VERSION_PARTS <<< "$LATEST_TAG"
VERSION_PARTS[2]=$((VERSION_PARTS[2]+1))
TAG="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${VERSION_PARTS[2]}"
# Create a new tag
git tag $TAG
git push origin $TAG
fi
echo "::set-output name=tag::$TAG"
- name: Create Release
id: create_release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.JEBENI_TOKEN }}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"tag_name": "${{ steps.determine_tag.outputs.tag }}",
"name": "${{ steps.determine_tag.outputs.tag }}",
"draft": false,
"prerelease": false
}' \
http://srv-captain--git:3000/api/v1/repos/dave/wow-Heimdall/releases
- name: Upload Release Asset
run: |
RELEASE_ID=$(curl -s \
-H "Authorization: token ${{ secrets.JEBENI_TOKEN }}" \
http://srv-captain--git:3000/api/v1/repos/dave/wow-Heimdall/releases/tags/${{ steps.determine_tag.outputs.tag }} | jq -r '.id')
curl -X POST \
-H "Authorization: token ${{ secrets.JEBENI_TOKEN }}" \
-F "attachment=@Heimdall.zip" \
"http://srv-captain--git:3000/api/v1/repos/dave/wow-Heimdall/releases/${RELEASE_ID}/assets?name=Heimdall.zip"

1
.gitignore vendored
View File

@@ -0,0 +1 @@
scratch.lua

5
.luacheckrc Symbolic link
View File

@@ -0,0 +1,5 @@
globals = { "CykaPersistentData", "CreateFrame", "GetItemInfo", "aura_env" }
unused_args = false
max_line_length = 500
exclude_files = { "Meta/" }
global = false

14
.luarc.json Symbolic link
View File

@@ -0,0 +1,14 @@
{
"workspace": {
"library": [
"./Meta"
]
},
"diagnostics.disable": [
"unused-local",
"unused-vararg"
],
"diagnostics.globals": [
"aura_env"
]
}

View File

@@ -1,6 +0,0 @@
{
"Lua.diagnostics.globals": [
"UIParent",
"BattlefieldMinimap"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
## Interface: 70300 ## Interface: 70300
## Title: Heimdall ## Title: Heimdall
## Version: 3.10.1 ## Version: 3.12.0
## Notes: Watches over areas and alerts when hostiles spotted ## Notes: Watches over areas and alerts when hostiles spotted
## Author: Cyka ## Author: Cyka
## SavedVariables: Heimdall_Data, Heimdall_Achievements ## SavedVariables: Heimdall_Data, Heimdall_Achievements, Heimdall_Chat
_L.lua _L.lua
Modules/CLEUParser.lua Modules/CLEUParser.lua
@@ -33,4 +33,5 @@ Modules/NetworkMessenger.lua
Modules/StinkyCache.lua Modules/StinkyCache.lua
Modules/Configurator.lua Modules/Configurator.lua
Modules/AchievementSniffer.lua Modules/AchievementSniffer.lua
Modules/ChatSniffer.lua
Heimdall.lua Heimdall.lua

2
Meta

Submodule Meta updated: c0337ef97f...eee043a846

View File

@@ -1,10 +1,19 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "AchievementSniffer" local ModuleName = "AchievementSniffer"
local HeimdallRoot = "Interface\\AddOns\\Heimdall\\" ---@class HeimdallAchievementSnifferConfig
local TextureRoot = HeimdallRoot .. "Texture\\" ---@field enabled boolean
---@field debug boolean
-----@field texture string
-----@field offsetX number
-----@field offsetY number
---@field rescan boolean
---@field scanInterval number
-----@field iconScale number
-- local HeimdallRoot = "Interface\\AddOns\\Heimdall\\"
-- local TextureRoot = HeimdallRoot .. "Texture\\"
local Achievements = { local Achievements = {
15, 15,
@@ -75,223 +84,221 @@ local Achievements = {
12448, 12448,
} }
---@diagnostic disable-next-line: missing-fields ---@class AchievementSniffer
shared.AchievementSniffer = {} shared.AchievementSniffer = {
function shared.AchievementSniffer.Init() Init = function()
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Module initialized", ModuleName))
end
local guidMap = {}
---@class AchievementData
---@field id number
---@field date string
---@field completed boolean
---@class Heimdall_Achievements
---@field players table<string, table<number, AchievementData>>
---@field alreadySeen table<string, boolean>
---@field rescan boolean
if not Heimdall_Achievements then Heimdall_Achievements = {} end
if not Heimdall_Achievements.players then Heimdall_Achievements.players = {} end
if not Heimdall_Achievements.alreadySeen then Heimdall_Achievements.alreadySeen = {} end
--local framePool = {}
--for i = 1, 40 do
-- local frame = CreateFrame("Frame", "HeimdallAchievementSnifferNameplate" .. i, UIParent)
-- local texture = frame:CreateTexture(nil, "ARTWORK")
-- texture:SetAllPoints(frame)
-- texture:SetTexture(TextureRoot .. Heimdall_Data.config.achievementSniffer.texture)
-- frame.texture = texture
-- frame:Hide()
-- table.insert(framePool, frame)
--end
---@param name string
---@return boolean
local function ShouldInspect(name)
local should = false
if not Heimdall_Achievements.players[name] then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Player %s does not have prior achievement data", ModuleName, name))
end
should = true
end
if Heimdall_Achievements.alreadySeen[name] then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Player %s has already been seen", ModuleName, name))
end
-- Save some memory
Heimdall_Achievements.players[name] = nil
should = false
end
if Heimdall_Data.config.achievementSniffer.rescan then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Rescan is enabled", ModuleName))
end
should = true
end
return should
end
-- It's not working well AT ALL
-- I don't know how to do it better
-- It simply just does not work...
--local function UpdateFrames()
-- for i, frame in ipairs(framePool) do
-- local unit = "nameplate" .. i
-- if not UnitExists(unit) then
-- --if Heimdall_Data.config.achievementSniffer.debug then
-- -- print(string.format("[%s] Unit %s does not exist, hiding frame", ModuleName, unit))
-- --end
-- frame:Hide()
-- else
-- --local unitFrame = _G[string.format("ElvUI_NamePlate%dHealthBar", i)]
-- local unitFrame = _G[string.format("NamePlate%d", i)]
-- if unitFrame == nil then
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit frame for %s not found", ModuleName, unit))
-- end
-- frame:Hide()
-- else
-- local unitName = UnitName(unit)
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit frame found for %s (%s)", ModuleName, unit, unitName))
-- end
-- frame:Show()
-- frame:SetSize(32, 32)
-- frame:SetFrameStrata("HIGH")
-- frame:SetFrameLevel(100)
-- frame:SetScale(Heimdall_Data.config.achievementSniffer.iconScale)
-- frame.texture:SetTexture(TextureRoot .. Heimdall_Data.config.achievementSniffer.texture)
-- frame:SetPoint("CENTER", unitFrame, "CENTER",
-- Heimdall_Data.config.achievementSniffer.offsetX,
-- Heimdall_Data.config.achievementSniffer.offsetY)
-- frame:SetParent(unitFrame)
-- frame:SetAlpha(1)
-- local exists = ShouldInspect(unitName)
-- if exists then
-- frame.texture:SetVertexColor(1, 0, 0, 1)
-- else
-- frame.texture:SetVertexColor(0, 1, 0, 1)
-- end
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Frame updated for %s", ModuleName, unitName))
-- end
-- end
-- end
-- end
--end
---@param unit string
local function TryInspect(unit)
local targetPlayer = UnitIsPlayer(unit)
if not targetPlayer then return end
local targetName = UnitName(unit)
local targetGuid = UnitGUID(unit)
guidMap[targetGuid] = targetName
if not ShouldInspect(targetName) then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Not inspecting player: %s", ModuleName, targetName))
end
return
end
local canInspect = CheckInteractDistance(unit, 1)
if canInspect then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Inspecting player: %s", ModuleName, targetName))
end
SetAchievementComparisonUnit(unit)
else
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Cannot inspect player (too far?): %s", ModuleName, targetName))
end
end
end
---@param name string
local function Scan(name)
if Heimdall_Data.config.achievementSniffer.debug then if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Scanning achievements for %s", ModuleName, name)) print(string.format("[%s] Module initialized", ModuleName))
end end
Heimdall_Achievements.players[name] = {} local guidMap = {}
for i, aid in ipairs(Achievements) do
local completed, month, day, year = GetAchievementComparisonInfo(aid)
if completed then
---@type string
local yearstr = "" .. year
if year < 100 then yearstr = "20" .. year end
local date = string.format("%04d-%02d-%02d", yearstr, month, day) ---@class AchievementData
---@field id number
---@field date string
---@field completed boolean
local data = { ---@class Heimdall_Achievements
id = aid, ---@field players table<string, table<number, AchievementData>>
date = date, ---@field alreadySeen table<string, boolean>
completed = completed, ---@field rescan boolean
} if not Heimdall_Achievements then Heimdall_Achievements = {} end
if not Heimdall_Achievements.players then Heimdall_Achievements.players = {} end
if not Heimdall_Achievements.alreadySeen then Heimdall_Achievements.alreadySeen = {} end
--local framePool = {}
--for i = 1, 40 do
-- local frame = CreateFrame("Frame", "HeimdallAchievementSnifferNameplate" .. i, UIParent)
-- local texture = frame:CreateTexture(nil, "ARTWORK")
-- texture:SetAllPoints(frame)
-- texture:SetTexture(TextureRoot .. Heimdall_Data.config.achievementSniffer.texture)
-- frame.texture = texture
-- frame:Hide()
-- table.insert(framePool, frame)
--end
---@param name string
---@return boolean
local function ShouldInspect(name)
local should = false
if not Heimdall_Achievements.players[name] then
if Heimdall_Data.config.achievementSniffer.debug then if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Achievement %d completed on %s", ModuleName, aid, date)) print(string.format("[%s] Player %s does not have prior achievement data", ModuleName, name))
end
should = true
end
if Heimdall_Achievements.alreadySeen[name] then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Player %s has already been seen", ModuleName, name))
end
-- Save some memory
Heimdall_Achievements.players[name] = nil
should = false
end
if Heimdall_Data.config.achievementSniffer.rescan then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Rescan is enabled", ModuleName))
end
should = true
end
return should
end
-- It's not working well AT ALL
-- I don't know how to do it better
-- It simply just does not work...
--local function UpdateFrames()
-- for i, frame in ipairs(framePool) do
-- local unit = "nameplate" .. i
-- if not UnitExists(unit) then
-- --if Heimdall_Data.config.achievementSniffer.debug then
-- -- print(string.format("[%s] Unit %s does not exist, hiding frame", ModuleName, unit))
-- --end
-- frame:Hide()
-- else
-- --local unitFrame = _G[string.format("ElvUI_NamePlate%dHealthBar", i)]
-- local unitFrame = _G[string.format("NamePlate%d", i)]
-- if unitFrame == nil then
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit frame for %s not found", ModuleName, unit))
-- end
-- frame:Hide()
-- else
-- local unitName = UnitName(unit)
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit frame found for %s (%s)", ModuleName, unit, unitName))
-- end
-- frame:Show()
-- frame:SetSize(32, 32)
-- frame:SetFrameStrata("HIGH")
-- frame:SetFrameLevel(100)
-- frame:SetScale(Heimdall_Data.config.achievementSniffer.iconScale)
-- frame.texture:SetTexture(TextureRoot .. Heimdall_Data.config.achievementSniffer.texture)
-- frame:SetPoint("CENTER", unitFrame, "CENTER",
-- Heimdall_Data.config.achievementSniffer.offsetX,
-- Heimdall_Data.config.achievementSniffer.offsetY)
-- frame:SetParent(unitFrame)
-- frame:SetAlpha(1)
-- local exists = ShouldInspect(unitName)
-- if exists then
-- frame.texture:SetVertexColor(1, 0, 0, 1)
-- else
-- frame.texture:SetVertexColor(0, 1, 0, 1)
-- end
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Frame updated for %s", ModuleName, unitName))
-- end
-- end
-- end
-- end
--end
---@param unit string
local function TryInspect(unit)
local targetPlayer = UnitIsPlayer(unit)
if not targetPlayer then return end
local targetName = UnitName(unit)
local targetGuid = UnitGUID(unit)
guidMap[targetGuid] = targetName
if not ShouldInspect(targetName) then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Not inspecting player: %s", ModuleName, targetName))
end
return
end
local canInspect = CheckInteractDistance(unit, 1)
if canInspect then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Inspecting player: %s", ModuleName, targetName))
end
SetAchievementComparisonUnit(unit)
else
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Cannot inspect player (too far?): %s", ModuleName, targetName))
end end
Heimdall_Achievements.players[name][aid] = data
end end
end end
--UpdateFrames()
end
local nameplateFrame = CreateFrame("Frame") ---@param name string
nameplateFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED") local function Scan(name)
nameplateFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
nameplateFrame:SetScript("OnEvent", function(self, event, unit)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Event triggered: %s for unit: %s", ModuleName, event, unit))
end
if event == "NAME_PLATE_UNIT_ADDED" then
TryInspect(unit)
end
--UpdateFrames()
end)
local inspectFrame = CreateFrame("Frame")
inspectFrame:RegisterEvent("INSPECT_ACHIEVEMENT_READY")
inspectFrame:SetScript("OnEvent", function(self, event, guid)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
local name = guidMap[guid]
if not name then
if Heimdall_Data.config.achievementSniffer.debug then if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] No name found for guid: %s", ModuleName, guid)) print(string.format("[%s] Scanning achievements for %s", ModuleName, name))
end end
return Heimdall_Achievements.players[name] = {}
end for i, aid in ipairs(Achievements) do
if Heimdall_Data.config.achievementSniffer.debug then local completed, month, day, year = GetAchievementComparisonInfo(aid)
print(string.format("[%s] Event triggered: %s for player: %s", ModuleName, event, name)) if completed then
end ---@type string
Scan(name) local yearstr = "" .. year
end) if year < 100 then yearstr = "20" .. year end
local date = string.format("%04d-%02d-%02d", yearstr, month, day)
local function Tick() local data = {
C_Timer.NewTimer(Heimdall_Data.config.achievementSniffer.scanInterval, Tick) id = aid,
if not Heimdall_Data.config.achievementSniffer.enabled then return end date = date,
completed = completed,
if Heimdall_Data.config.achievementSniffer.debug then }
print(string.format("[%s] Scanning achievements for everyone on screen", ModuleName)) if Heimdall_Data.config.achievementSniffer.debug then
end print(string.format("[%s] Achievement %d completed on %s", ModuleName, aid, date))
for i = 1, 40 do end
local unit = "nameplate" .. i Heimdall_Achievements.players[name][aid] = data
if UnitExists(unit) then end
TryInspect(unit)
--else
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit %s does not exist, nothing to inspect", ModuleName, unit))
-- end
end end
--UpdateFrames()
end end
--UpdateFrames()
end
Tick()
print("[Heimdall] AchievementSniffer loaded") local nameplateFrame = CreateFrame("Frame")
end nameplateFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
nameplateFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
nameplateFrame:SetScript("OnEvent", function(self, event, unit)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Event triggered: %s for unit: %s", ModuleName, event, unit))
end
if event == "NAME_PLATE_UNIT_ADDED" then TryInspect(unit) end
--UpdateFrames()
end)
local inspectFrame = CreateFrame("Frame")
inspectFrame:RegisterEvent("INSPECT_ACHIEVEMENT_READY")
inspectFrame:SetScript("OnEvent", function(self, event, guid)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
local name = guidMap[guid]
if not name then
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] No name found for guid: %s", ModuleName, guid))
end
return
end
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Event triggered: %s for player: %s", ModuleName, event, name))
end
Scan(name)
end)
local function Tick()
C_Timer.NewTimer(Heimdall_Data.config.achievementSniffer.scanInterval, Tick)
if not Heimdall_Data.config.achievementSniffer.enabled then return end
if Heimdall_Data.config.achievementSniffer.debug then
print(string.format("[%s] Scanning achievements for everyone on screen", ModuleName))
end
for i = 1, 40 do
local unit = "nameplate" .. i
if UnitExists(unit) then
TryInspect(unit)
--else
-- if Heimdall_Data.config.achievementSniffer.debug then
-- print(string.format("[%s] Unit %s does not exist, nothing to inspect", ModuleName, unit))
-- end
end
end
--UpdateFrames()
end
Tick()
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -1,109 +1,144 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "AgentTracker" local ModuleName = "AgentTracker"
---@diagnostic disable-next-line: missing-fields ---@class AgentTrackerData
shared.AgentTracker = {} ---@field agents ReactiveValue<table<string, string>>
function shared.AgentTracker.Init()
--/run Heimdall_Data.config.agents["Cyheuraeth"]=date("%Y-%m-%dT%H:%M:%S")
---@type table<string, boolean>
local channelRosterFrame = CreateFrame("Frame")
channelRosterFrame:RegisterEvent("CHANNEL_ROSTER_UPDATE")
channelRosterFrame:SetScript("OnEvent", function(self, event, index)
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Channel roster update received", ModuleName))
end
if not Heimdall_Data.config.agentTracker.enabled then
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Module disabled, ignoring roster update", ModuleName))
end
return
end
local name = GetChannelDisplayInfo(index)
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Processing channel update: %s (index: %d)", ModuleName, name or "nil", index))
end
if name ~= Heimdall_Data.config.agentTracker.masterChannel then
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Ignoring non-master channel: %s", ModuleName, name or "nil"))
end
return
end
local count = select(5, GetChannelDisplayInfo(index))
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Processing %d members in channel", ModuleName, count))
end
local newAgents = 0 ---@class HeimdallAgentTrackerConfig
for i = 1, count do ---@field enabled boolean
local name = GetChannelRosterInfo(index, i) ---@field debug boolean
if name then ---@field channels string[]
local isNewAgent = not Heimdall_Data.config.agents[name]
Heimdall_Data.config.agents[name] = date("%Y-%m-%dT%H:%M:%S") ---@class AgentTracker
if isNewAgent then shared.AgentTracker = {
newAgents = newAgents + 1 ---@param name string
end ---@return boolean
Track = function(name)
if not name then return false end
local exists = shared.AgentTracker.IsAgent(name)
if exists then return false end
shared.agentTracker.agents[name] = date("%Y-%m-%dT%H:%M:%S")
-- Heimdall_Data.config.agents[name] = date("%Y-%m-%dT%H:%M:%S")
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Tracking new agent: %s", ModuleName, name))
shared.dump(shared.agentTracker.agents)
end
return true
end,
---@param name string
---@return boolean
IsAgent = function(name)
if not name then return false end
return shared.agentTracker.agents[name] ~= nil
end,
---@param callback fun(agent: string)
OnChange = function(callback) shared.agentTracker.agents:onChange(callback) end,
---@param callback fun(agent: string)
ForEach = function(callback)
---@type table<string, string>
local agents = shared.agentTracker.agents:get()
for name, _ in pairs(agents) do
callback(name)
end
end,
---@return nil
Init = function()
shared.agentTracker = {
agents = ReactiveValue.new(Heimdall_Data.config.agents),
}
--/run Heimdall_Data.config.agents["Cyheuraeth"]=date("%Y-%m-%dT%H:%M:%S")
---@type table<string, boolean>
local channelRosterFrame = CreateFrame("Frame")
channelRosterFrame:RegisterEvent("CHANNEL_ROSTER_UPDATE")
channelRosterFrame:SetScript("OnEvent", function(self, event, index)
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Channel roster update received", ModuleName))
end
if not Heimdall_Data.config.agentTracker.enabled then
if Heimdall_Data.config.agentTracker.debug then if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] %s agent: %s", ModuleName, isNewAgent and "Added new" or "Updated existing", print(string.format("[%s] Module disabled, ignoring roster update", ModuleName))
name)) end
return
end
local name = GetChannelDisplayInfo(index)
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Processing channel update: %s (index: %d)", ModuleName, name or "nil", index))
end
if name ~= Heimdall_Data.config.agentTracker.masterChannel then
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Ignoring non-master channel: %s", ModuleName, name or "nil"))
end
return
end
local count = select(5, GetChannelDisplayInfo(index))
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Processing %d members in channel", ModuleName, count))
end
local newAgents = 0
for i = 1, count do
name = GetChannelRosterInfo(index, i)
shared.AgentTracker.Track(name)
end
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Roster update complete - Added %d new agents", ModuleName, newAgents))
end
end)
local agentTrackerChannelSniffer = CreateFrame("Frame")
agentTrackerChannelSniffer:RegisterEvent("CHAT_MSG_CHANNEL")
agentTrackerChannelSniffer:SetScript("OnEvent", function(self, event, msg, sender, ...)
-- if Heimdall_Data.config.agentTracker.debug then
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
-- end
if not Heimdall_Data.config.agentTracker.enabled then
-- if Heimdall_Data.config.agentTracker.debug then
-- print(string.format("[%s] Module disabled, ignoring channel message", ModuleName))
-- end
return
end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.agentTracker.channels) do
if channel == channelname then
ok = true
break
end end
end end
end if not ok then
if Heimdall_Data.config.agentTracker.debug then
if Heimdall_Data.config.agentTracker.debug then print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
print(string.format("[%s] Roster update complete - Added %d new agents", ModuleName, newAgents)) end
end return
end)
local agentTrackerChannelSniffer = CreateFrame("Frame")
agentTrackerChannelSniffer:RegisterEvent("CHAT_MSG_CHANNEL")
agentTrackerChannelSniffer:SetScript("OnEvent", function(self, event, msg, sender, ...)
-- if Heimdall_Data.config.agentTracker.debug then
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
-- end
if not Heimdall_Data.config.agentTracker.enabled then
-- if Heimdall_Data.config.agentTracker.debug then
-- print(string.format("[%s] Module disabled, ignoring channel message", ModuleName))
-- end
return
end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.agentTracker.channels) do
if channel == channelname then
ok = true
break
end end
end
if not ok then
if Heimdall_Data.config.agentTracker.debug then if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName)) print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
end end
return
end
if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.agentTracker)
end
sender = string.match(sender, "^[^-]+")
local new = shared.AgentTracker.Track(sender)
sender = string.match(sender, "^[^-]+") if Heimdall_Data.config.agentTracker.debug then
local isNewAgent = not Heimdall_Data.config.agents[sender] print(
Heimdall_Data.config.agents[sender] = date("%Y-%m-%dT%H:%M:%S") string.format(
"[%s] %s agent from message: %s",
ModuleName,
new and "Added new" or "Updated existing",
sender
)
)
end
end)
if Heimdall_Data.config.agentTracker.debug then if Heimdall_Data.config.agentTracker.debug then
print(string.format("[%s] %s agent from message: %s", ModuleName, print(string.format("[%s] Module initialized", ModuleName))
isNewAgent and "Added new" or "Updated existing", sender)) shared.dump(shared.agentTracker.agents:get(), "Agents")
end end
end) print(string.format("[%s] Module initialized", ModuleName))
end,
if Heimdall_Data.config.agentTracker.debug then }
local count = 0
for _ in pairs(Heimdall_Data.config.agents) do count = count + 1 end
print(string.format("[%s] Module initialized - Tracking %d agents", ModuleName, count))
end
print("[Heimdall] AgentTracker loaded")
end

View File

@@ -1,107 +1,142 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "BonkDetector" local ModuleName = "BonkDetector"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallBonkDetectorConfig
shared.BonkDetector = {} ---@field enabled boolean
function shared.BonkDetector.Init() ---@field debug boolean
---@type table<string, number> ---@field channels string[]
local lastReportTime = {} ---@field throttle number
local frame = CreateFrame("Frame") ---@class BonkDetector
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") shared.BonkDetector = {
frame:SetScript("OnEvent", function(self, event, ...) ---@return nil
-- if Heimdall_Data.config.bonkDetector.debug then Init = function()
-- print(string.format("[%s] Combat log event received", ModuleName)) ---@type table<string, number>
-- end local lastReportTime = {}
if not Heimdall_Data.config.bonkDetector.enabled then
-- if Heimdall_Data.config.bonkDetector.debug then
-- print(string.format("[%s] Module disabled, ignoring combat event", ModuleName))
-- end
return
end
local subevent = select(2, ...) local frame = CreateFrame("Frame")
if not subevent:find("_DAMAGE") then frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
if Heimdall_Data.config.bonkDetector.debug then frame:SetScript("OnEvent", function(self, event, ...)
print(string.format("[%s] Not a damage event, ignoring: %s", ModuleName, subevent)) -- if Heimdall_Data.config.bonkDetector.debug then
-- print(string.format("[%s] Combat log event received", ModuleName))
-- end
if not Heimdall_Data.config.bonkDetector.enabled then
-- if Heimdall_Data.config.bonkDetector.debug then
-- print(string.format("[%s] Module disabled, ignoring combat event", ModuleName))
-- end
return
end end
return
end
local source, err = CLEUParser.GetSourceName(...) local subevent = select(2, ...)
if err then if not subevent:find("_DAMAGE") then
if Heimdall_Data.config.bonkDetector.debug then if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Error getting source name: %s", ModuleName, err)) print(string.format("[%s] Not a damage event, ignoring: %s", ModuleName, subevent))
end
return
end end
return
end ---@type string|nil, string, string, string, string
local sourceGUID = CLEUParser.GetSourceGUID(...) local err, source, sourceGUID, destination, destinationGUID
if not string.find(sourceGUID, "Player") then
if Heimdall_Data.config.bonkDetector.debug then source, err = CLEUParser.GetSourceName(...)
print(string.format("[%s] Source %s is not a player, nothing to do", ModuleName, source)) if err then
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Error getting source name: %s", ModuleName, err))
end
return
end end
return sourceGUID, err = CLEUParser.GetSourceGUID(...)
end if err then
if Heimdall_Data.config.bonkDetector.debug then
local destination, err = CLEUParser.GetDestName(...) print(string.format("[%s] Error getting source GUID: %s", ModuleName, err))
if err then end
if Heimdall_Data.config.bonkDetector.debug then return
print(string.format("[%s] Error getting destination name: %s", ModuleName, err))
end end
return if not string.find(sourceGUID, "Player") then
end if Heimdall_Data.config.bonkDetector.debug then
local destinationGUID = CLEUParser.GetDestGUID(...) print(string.format("[%s] Source %s is not a player, nothing to do", ModuleName, source))
if not string.find(destinationGUID, "Player") then end
if Heimdall_Data.config.bonkDetector.debug then return
print(string.format("[%s] Destination %s is not a player, nothing to do", ModuleName, destination))
end end
return
end
if source == destination then destination, err = CLEUParser.GetDestName(...)
if Heimdall_Data.config.bonkDetector.debug then if err then
print(string.format("[%s] Source and destination are the same, ignoring event", ModuleName)) if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Error getting destination name: %s", ModuleName, err))
end
return
end end
return destinationGUID, err = CLEUParser.GetDestGUID(...)
end if err then
if Heimdall_Data.config.bonkDetector.debug then
local currentTime = GetTime() print(string.format("[%s] Error getting destination GUID: %s", ModuleName, err))
local throttle = Heimdall_Data.config.bonkDetector.throttle end
return
if lastReportTime[source] and (currentTime - lastReportTime[source]) < throttle then
if Heimdall_Data.config.bonkDetector.debug then
local timeLeft = throttle - (currentTime - lastReportTime[source])
print(string.format("[%s] Damage report throttled for %s (%.1f seconds remaining)",
ModuleName, source, timeLeft))
end end
return if not string.find(destinationGUID, "Player") then
end if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Destination %s is not a player, nothing to do", ModuleName, destination))
lastReportTime[source] = currentTime end
return
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Processing damage event - Source: %s, Target: %s, Type: %s",
ModuleName, source, destination, subevent))
end
for _, channel in pairs(Heimdall_Data.config.bonkDetector.channels) do
local locale = shared.GetLocaleForChannel(channel)
local msg = string.format(shared._L("bonkDetected", locale), source, destination, subevent)
---@type Message
local message = {
channel = "C",
data = channel,
message = msg,
}
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Queuing bonk detector message", ModuleName))
shared.dumpTable(message)
end end
table.insert(shared.messenger.queue, message)
end
end)
print("[Heimdall] BonkDetector loaded") if source == destination then
end if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Source and destination are the same, ignoring event", ModuleName))
end
return
end
local currentTime = GetTime()
local throttle = Heimdall_Data.config.bonkDetector.throttle
if lastReportTime[source] and (currentTime - lastReportTime[source]) < throttle then
if Heimdall_Data.config.bonkDetector.debug then
local timeLeft = throttle - (currentTime - lastReportTime[source])
print(
string.format(
"[%s] Damage report throttled for %s (%.1f seconds remaining)",
ModuleName,
source,
timeLeft
)
)
end
return
end
lastReportTime[source] = currentTime
if Heimdall_Data.config.bonkDetector.debug then
print(
string.format(
"[%s] Processing damage event - Source: %s, Target: %s, Type: %s",
ModuleName,
source,
destination,
subevent
)
)
end
for _, channel in pairs(Heimdall_Data.config.bonkDetector.channels) do
local locale = shared.GetLocaleForChannel(channel)
local msg = string.format(shared._L("bonkDetected", locale), source, destination, subevent)
---@type Message
local message = {
channel = "C",
data = channel,
message = msg,
}
if Heimdall_Data.config.bonkDetector.debug then
print(string.format("[%s] Queuing bonk detector message", ModuleName))
shared.dump(message)
end
table.insert(shared.messenger.queue, message)
end
end)
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -1,13 +1,16 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Bully" local ModuleName = "Bully"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallBullyConfig
shared.Bully = {} ---@field enabled boolean
function shared.Bully.Init() ---@field debug boolean
if Heimdall_Data.config.bully.debug then
---@class Bully
shared.Bully = {
---@return nil
Init = function()
if Heimdall_Data.config.bully.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName)) print(string.format("[%s] Module initialized", ModuleName))
end end,
print("[Heimdall] Bully loaded") }
end

View File

@@ -12,12 +12,12 @@ local function Init()
["destGUID"] = 8, ["destGUID"] = 8,
["destName"] = 9, ["destName"] = 9,
["destFlags"] = 10, ["destFlags"] = 10,
["destRaidFlags"] = 11 ["destRaidFlags"] = 11,
}, },
["GENERIC_SPELL"] = { ["GENERIC_SPELL"] = {
["spellId"] = 12, ["spellId"] = 12,
["spellName"] = 13, ["spellName"] = 13,
["spellSchool"] = 14 ["spellSchool"] = 14,
}, },
["GENERIC_DAMAGE"] = { ["GENERIC_DAMAGE"] = {
["amount"] = 15, ["amount"] = 15,
@@ -29,19 +29,19 @@ local function Init()
["critical"] = 21, ["critical"] = 21,
["glancing"] = 22, ["glancing"] = 22,
["crushing"] = 23, ["crushing"] = 23,
["isOffHand"] = 24 ["isOffHand"] = 24,
}, },
["GENERIC_MISSED"] = { ["GENERIC_MISSED"] = {
["missType"] = 15, ["missType"] = 15,
["isOffHand"] = 16, ["isOffHand"] = 16,
["amountMissed"] = 17, ["amountMissed"] = 17,
["critical"] = 18 ["critical"] = 18,
}, },
["GENERIC_HEAL"] = { ["GENERIC_HEAL"] = {
["amount"] = 15, ["amount"] = 15,
["overhealing"] = 16, ["overhealing"] = 16,
["absorbed"] = 17, ["absorbed"] = 17,
["critical"] = 18 ["critical"] = 18,
}, },
["GENERIC_HEAL_ABSORBED"] = { ["GENERIC_HEAL_ABSORBED"] = {
["extraGUID"] = 15, ["extraGUID"] = 15,
@@ -52,44 +52,44 @@ local function Init()
["extraSpellName"] = 20, ["extraSpellName"] = 20,
["extraSchool"] = 21, ["extraSchool"] = 21,
["absorbedAmount"] = 22, ["absorbedAmount"] = 22,
["totalAmount"] = 23 ["totalAmount"] = 23,
}, },
["GENERIC_ENERGIZE"] = { ["GENERIC_ENERGIZE"] = {
["amount"] = 15, ["amount"] = 15,
["overEnergize"] = 16, ["overEnergize"] = 16,
["powerType"] = 17 ["powerType"] = 17,
}, },
["GENERIC_DRAIN"] = { ["GENERIC_DRAIN"] = {
["amount"] = 15, ["amount"] = 15,
["powerType"] = 16, ["powerType"] = 16,
["extraAmount"] = 17 ["extraAmount"] = 17,
}, },
["GENERIC_LEECH"] = { ["GENERIC_LEECH"] = {
["amount"] = 15, ["amount"] = 15,
["powerType"] = 16, ["powerType"] = 16,
["extraAmount"] = 17 ["extraAmount"] = 17,
}, },
["GENERIC_INTERRUPT"] = { ["GENERIC_INTERRUPT"] = {
["extraSpellId"] = 15, ["extraSpellId"] = 15,
["extraSpellName"] = 16, ["extraSpellName"] = 16,
["extraSchool"] = 17 ["extraSchool"] = 17,
}, },
["GENERIC_DISPEL"] = { ["GENERIC_DISPEL"] = {
["extraSpellId"] = 15, ["extraSpellId"] = 15,
["extraSpellName"] = 16, ["extraSpellName"] = 16,
["extraSchool"] = 17, ["extraSchool"] = 17,
["auraType"] = 18 ["auraType"] = 18,
}, },
["GENERIC_DISPEL_FAILED"] = { ["GENERIC_DISPEL_FAILED"] = {
["extraSpellId"] = 15, ["extraSpellId"] = 15,
["extraSpellName"] = 16, ["extraSpellName"] = 16,
["extraSchool"] = 17 ["extraSchool"] = 17,
}, },
["GENERIC_STOLEN"] = { ["GENERIC_STOLEN"] = {
["extraSpellId"] = 15, ["extraSpellId"] = 15,
["extraSpellName"] = 16, ["extraSpellName"] = 16,
["extraSchool"] = 17, ["extraSchool"] = 17,
["auraType"] = 18 ["auraType"] = 18,
}, },
["GENERIC_EXTRA_ATTACKS"] = { ["amount"] = 15 }, ["GENERIC_EXTRA_ATTACKS"] = { ["amount"] = 15 },
["GENERIC_AURA_APPLIED"] = { ["auraType"] = 15, ["amount"] = 16 }, ["GENERIC_AURA_APPLIED"] = { ["auraType"] = 15, ["amount"] = 16 },
@@ -102,38 +102,32 @@ local function Init()
["extraSpellId"] = 15, ["extraSpellId"] = 15,
["extraSpellName"] = 16, ["extraSpellName"] = 16,
["extraSchool"] = 17, ["extraSchool"] = 17,
["auraType"] = 18 ["auraType"] = 18,
}, },
["GENERIC_CAST_START"] = {}, ["GENERIC_CAST_START"] = {},
["GENERIC_CAST_SUCCESS"] = {}, ["GENERIC_CAST_SUCCESS"] = {},
["GENERIC_CAST_FAILED"] = {} ["GENERIC_CAST_FAILED"] = {},
} }
CLEUEventInfo["SWING_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] CLEUEventInfo["SWING_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"]
CLEUEventInfo["SWING_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] CLEUEventInfo["SWING_MISSED"] = CLEUEventInfo["GENERIC_MISSED"]
CLEUEventInfo["SWING_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] CLEUEventInfo["SWING_HEAL"] = CLEUEventInfo["GENERIC_HEAL"]
CLEUEventInfo["SWING_HEAL_ABSORBED"] = CLEUEventInfo["SWING_HEAL_ABSORBED"] = CLEUEventInfo["GENERIC_HEAL_ABSORBED"]
CLEUEventInfo["GENERIC_HEAL_ABSORBED"]
CLEUEventInfo["SWING_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] CLEUEventInfo["SWING_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"]
CLEUEventInfo["SWING_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] CLEUEventInfo["SWING_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"]
CLEUEventInfo["SWING_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] CLEUEventInfo["SWING_LEECH"] = CLEUEventInfo["GENERIC_LEECH"]
CLEUEventInfo["SWING_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"] CLEUEventInfo["SWING_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"]
CLEUEventInfo["SWING_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] CLEUEventInfo["SWING_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"]
CLEUEventInfo["SWING_DISPEL_FAILED"] = CLEUEventInfo["SWING_DISPEL_FAILED"] = CLEUEventInfo["GENERIC_DISPEL_FAILED"]
CLEUEventInfo["GENERIC_DISPEL_FAILED"]
CLEUEventInfo["SWING_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] CLEUEventInfo["SWING_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"]
CLEUEventInfo["SWING_EXTRA_ATTACKS"] = CLEUEventInfo["SWING_EXTRA_ATTACKS"] = CLEUEventInfo["GENERIC_EXTRA_ATTACKS"]
CLEUEventInfo["GENERIC_EXTRA_ATTACKS"]
CLEUEventInfo["SWING_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"] CLEUEventInfo["SWING_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"]
CLEUEventInfo["SWING_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"] CLEUEventInfo["SWING_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"]
CLEUEventInfo["SWING_AURA_APPLIED_DOSE"] = CLEUEventInfo["SWING_AURA_APPLIED_DOSE"] = CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"]
CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] CLEUEventInfo["SWING_AURA_REMOVED_DOSE"] = CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"]
CLEUEventInfo["SWING_AURA_REMOVED_DOSE"] =
CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"]
CLEUEventInfo["SWING_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"] CLEUEventInfo["SWING_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"]
CLEUEventInfo["SWING_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"] CLEUEventInfo["SWING_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"]
CLEUEventInfo["SWING_AURA_BROKEN_SPELL"] = CLEUEventInfo["SWING_AURA_BROKEN_SPELL"] = CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"]
CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"]
CLEUEventInfo["SWING_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"] CLEUEventInfo["SWING_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"]
CLEUEventInfo["SWING_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"] CLEUEventInfo["SWING_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"]
CLEUEventInfo["SWING_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"] CLEUEventInfo["SWING_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"]
@@ -141,28 +135,22 @@ local function Init()
CLEUEventInfo["RANGE_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] CLEUEventInfo["RANGE_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"]
CLEUEventInfo["RANGE_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] CLEUEventInfo["RANGE_MISSED"] = CLEUEventInfo["GENERIC_MISSED"]
CLEUEventInfo["RANGE_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] CLEUEventInfo["RANGE_HEAL"] = CLEUEventInfo["GENERIC_HEAL"]
CLEUEventInfo["RANGE_HEAL_ABSORBED"] = CLEUEventInfo["RANGE_HEAL_ABSORBED"] = CLEUEventInfo["GENERIC_HEAL_ABSORBED"]
CLEUEventInfo["GENERIC_HEAL_ABSORBED"]
CLEUEventInfo["RANGE_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] CLEUEventInfo["RANGE_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"]
CLEUEventInfo["RANGE_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] CLEUEventInfo["RANGE_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"]
CLEUEventInfo["RANGE_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] CLEUEventInfo["RANGE_LEECH"] = CLEUEventInfo["GENERIC_LEECH"]
CLEUEventInfo["RANGE_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"] CLEUEventInfo["RANGE_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"]
CLEUEventInfo["RANGE_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] CLEUEventInfo["RANGE_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"]
CLEUEventInfo["RANGE_DISPEL_FAILED"] = CLEUEventInfo["RANGE_DISPEL_FAILED"] = CLEUEventInfo["GENERIC_DISPEL_FAILED"]
CLEUEventInfo["GENERIC_DISPEL_FAILED"]
CLEUEventInfo["RANGE_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] CLEUEventInfo["RANGE_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"]
CLEUEventInfo["RANGE_EXTRA_ATTACKS"] = CLEUEventInfo["RANGE_EXTRA_ATTACKS"] = CLEUEventInfo["GENERIC_EXTRA_ATTACKS"]
CLEUEventInfo["GENERIC_EXTRA_ATTACKS"]
CLEUEventInfo["RANGE_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"] CLEUEventInfo["RANGE_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"]
CLEUEventInfo["RANGE_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"] CLEUEventInfo["RANGE_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"]
CLEUEventInfo["RANGE_AURA_APPLIED_DOSE"] = CLEUEventInfo["RANGE_AURA_APPLIED_DOSE"] = CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"]
CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] CLEUEventInfo["RANGE_AURA_REMOVED_DOSE"] = CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"]
CLEUEventInfo["RANGE_AURA_REMOVED_DOSE"] =
CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"]
CLEUEventInfo["RANGE_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"] CLEUEventInfo["RANGE_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"]
CLEUEventInfo["RANGE_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"] CLEUEventInfo["RANGE_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"]
CLEUEventInfo["RANGE_AURA_BROKEN_SPELL"] = CLEUEventInfo["RANGE_AURA_BROKEN_SPELL"] = CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"]
CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"]
CLEUEventInfo["RANGE_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"] CLEUEventInfo["RANGE_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"]
CLEUEventInfo["RANGE_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"] CLEUEventInfo["RANGE_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"]
CLEUEventInfo["RANGE_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"] CLEUEventInfo["RANGE_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"]
@@ -170,28 +158,22 @@ local function Init()
CLEUEventInfo["SPELL_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] CLEUEventInfo["SPELL_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"]
CLEUEventInfo["SPELL_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] CLEUEventInfo["SPELL_MISSED"] = CLEUEventInfo["GENERIC_MISSED"]
CLEUEventInfo["SPELL_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] CLEUEventInfo["SPELL_HEAL"] = CLEUEventInfo["GENERIC_HEAL"]
CLEUEventInfo["SPELL_HEAL_ABSORBED"] = CLEUEventInfo["SPELL_HEAL_ABSORBED"] = CLEUEventInfo["GENERIC_HEAL_ABSORBED"]
CLEUEventInfo["GENERIC_HEAL_ABSORBED"]
CLEUEventInfo["SPELL_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] CLEUEventInfo["SPELL_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"]
CLEUEventInfo["SPELL_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] CLEUEventInfo["SPELL_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"]
CLEUEventInfo["SPELL_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] CLEUEventInfo["SPELL_LEECH"] = CLEUEventInfo["GENERIC_LEECH"]
CLEUEventInfo["SPELL_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"] CLEUEventInfo["SPELL_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"]
CLEUEventInfo["SPELL_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] CLEUEventInfo["SPELL_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"]
CLEUEventInfo["SPELL_DISPEL_FAILED"] = CLEUEventInfo["SPELL_DISPEL_FAILED"] = CLEUEventInfo["GENERIC_DISPEL_FAILED"]
CLEUEventInfo["GENERIC_DISPEL_FAILED"]
CLEUEventInfo["SPELL_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] CLEUEventInfo["SPELL_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"]
CLEUEventInfo["SPELL_EXTRA_ATTACKS"] = CLEUEventInfo["SPELL_EXTRA_ATTACKS"] = CLEUEventInfo["GENERIC_EXTRA_ATTACKS"]
CLEUEventInfo["GENERIC_EXTRA_ATTACKS"]
CLEUEventInfo["SPELL_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"] CLEUEventInfo["SPELL_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"]
CLEUEventInfo["SPELL_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"] CLEUEventInfo["SPELL_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"]
CLEUEventInfo["SPELL_AURA_APPLIED_DOSE"] = CLEUEventInfo["SPELL_AURA_APPLIED_DOSE"] = CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"]
CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] CLEUEventInfo["SPELL_AURA_REMOVED_DOSE"] = CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"]
CLEUEventInfo["SPELL_AURA_REMOVED_DOSE"] =
CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"]
CLEUEventInfo["SPELL_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"] CLEUEventInfo["SPELL_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"]
CLEUEventInfo["SPELL_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"] CLEUEventInfo["SPELL_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"]
CLEUEventInfo["SPELL_AURA_BROKEN_SPELL"] = CLEUEventInfo["SPELL_AURA_BROKEN_SPELL"] = CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"]
CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"]
CLEUEventInfo["SPELL_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"] CLEUEventInfo["SPELL_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"]
CLEUEventInfo["SPELL_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"] CLEUEventInfo["SPELL_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"]
CLEUEventInfo["SPELL_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"] CLEUEventInfo["SPELL_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"]
@@ -199,39 +181,25 @@ local function Init()
CLEUEventInfo["SPELL_PERIODIC_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"] CLEUEventInfo["SPELL_PERIODIC_DAMAGE"] = CLEUEventInfo["GENERIC_DAMAGE"]
CLEUEventInfo["SPELL_PERIODIC_MISSED"] = CLEUEventInfo["GENERIC_MISSED"] CLEUEventInfo["SPELL_PERIODIC_MISSED"] = CLEUEventInfo["GENERIC_MISSED"]
CLEUEventInfo["SPELL_PERIODIC_HEAL"] = CLEUEventInfo["GENERIC_HEAL"] CLEUEventInfo["SPELL_PERIODIC_HEAL"] = CLEUEventInfo["GENERIC_HEAL"]
CLEUEventInfo["SPELL_PERIODIC_HEAL_ABSORBED"] = CLEUEventInfo["SPELL_PERIODIC_HEAL_ABSORBED"] = CLEUEventInfo["GENERIC_HEAL_ABSORBED"]
CLEUEventInfo["GENERIC_HEAL_ABSORBED"]
CLEUEventInfo["SPELL_PERIODIC_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"] CLEUEventInfo["SPELL_PERIODIC_ENERGIZE"] = CLEUEventInfo["GENERIC_ENERGIZE"]
CLEUEventInfo["SPELL_PERIODIC_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"] CLEUEventInfo["SPELL_PERIODIC_DRAIN"] = CLEUEventInfo["GENERIC_DRAIN"]
CLEUEventInfo["SPELL_PERIODIC_LEECH"] = CLEUEventInfo["GENERIC_LEECH"] CLEUEventInfo["SPELL_PERIODIC_LEECH"] = CLEUEventInfo["GENERIC_LEECH"]
CLEUEventInfo["SPELL_PERIODIC_INTERRUPT"] = CLEUEventInfo["SPELL_PERIODIC_INTERRUPT"] = CLEUEventInfo["GENERIC_INTERRUPT"]
CLEUEventInfo["GENERIC_INTERRUPT"]
CLEUEventInfo["SPELL_PERIODIC_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"] CLEUEventInfo["SPELL_PERIODIC_DISPEL"] = CLEUEventInfo["GENERIC_DISPEL"]
CLEUEventInfo["SPELL_PERIODIC_DISPEL_FAILED"] = CLEUEventInfo["SPELL_PERIODIC_DISPEL_FAILED"] = CLEUEventInfo["GENERIC_DISPEL_FAILED"]
CLEUEventInfo["GENERIC_DISPEL_FAILED"]
CLEUEventInfo["SPELL_PERIODIC_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"] CLEUEventInfo["SPELL_PERIODIC_STOLEN"] = CLEUEventInfo["GENERIC_STOLEN"]
CLEUEventInfo["SPELL_PERIODIC_EXTRA_ATTACKS"] = CLEUEventInfo["SPELL_PERIODIC_EXTRA_ATTACKS"] = CLEUEventInfo["GENERIC_EXTRA_ATTACKS"]
CLEUEventInfo["GENERIC_EXTRA_ATTACKS"] CLEUEventInfo["SPELL_PERIODIC_AURA_APPLIED"] = CLEUEventInfo["GENERIC_AURA_APPLIED"]
CLEUEventInfo["SPELL_PERIODIC_AURA_APPLIED"] = CLEUEventInfo["SPELL_PERIODIC_AURA_REMOVED"] = CLEUEventInfo["GENERIC_AURA_REMOVED"]
CLEUEventInfo["GENERIC_AURA_APPLIED"] CLEUEventInfo["SPELL_PERIODIC_AURA_APPLIED_DOSE"] = CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"]
CLEUEventInfo["SPELL_PERIODIC_AURA_REMOVED"] = CLEUEventInfo["SPELL_PERIODIC_AURA_REMOVED_DOSE"] = CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"]
CLEUEventInfo["GENERIC_AURA_REMOVED"] CLEUEventInfo["SPELL_PERIODIC_AURA_REFRESH"] = CLEUEventInfo["GENERIC_AURA_REFRESH"]
CLEUEventInfo["SPELL_PERIODIC_AURA_APPLIED_DOSE"] = CLEUEventInfo["SPELL_PERIODIC_AURA_BROKEN"] = CLEUEventInfo["GENERIC_AURA_BROKEN"]
CLEUEventInfo["GENERIC_AURA_APPLIED_DOSE"] CLEUEventInfo["SPELL_PERIODIC_AURA_BROKEN_SPELL"] = CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"]
CLEUEventInfo["SPELL_PERIODIC_AURA_REMOVED_DOSE"] = CLEUEventInfo["SPELL_PERIODIC_CAST_START"] = CLEUEventInfo["GENERIC_CAST_START"]
CLEUEventInfo["GENERIC_AURA_REMOVED_DOSE"] CLEUEventInfo["SPELL_PERIODIC_CAST_SUCCESS"] = CLEUEventInfo["GENERIC_CAST_SUCCESS"]
CLEUEventInfo["SPELL_PERIODIC_AURA_REFRESH"] = CLEUEventInfo["SPELL_PERIODIC_CAST_FAILED"] = CLEUEventInfo["GENERIC_CAST_FAILED"]
CLEUEventInfo["GENERIC_AURA_REFRESH"]
CLEUEventInfo["SPELL_PERIODIC_AURA_BROKEN"] =
CLEUEventInfo["GENERIC_AURA_BROKEN"]
CLEUEventInfo["SPELL_PERIODIC_AURA_BROKEN_SPELL"] =
CLEUEventInfo["GENERIC_AURA_BROKEN_SPELL"]
CLEUEventInfo["SPELL_PERIODIC_CAST_START"] =
CLEUEventInfo["GENERIC_CAST_START"]
CLEUEventInfo["SPELL_PERIODIC_CAST_SUCCESS"] =
CLEUEventInfo["GENERIC_CAST_SUCCESS"]
CLEUEventInfo["SPELL_PERIODIC_CAST_FAILED"] =
CLEUEventInfo["GENERIC_CAST_FAILED"]
---@class CLEUParser ---@class CLEUParser
CLEUParser = { CLEUParser = {
@@ -239,132 +207,88 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetTimestamp = function(...) GetTimestamp = function(...)
local val = select(CLEUEventInfo["GENERIC"]["timestamp"], ...) local val = select(CLEUEventInfo["GENERIC"]["timestamp"], ...)
if val == nil then if val == nil then return 0, "Timestamp is nil or missing" end
return 0, "Timestamp is nil or missing" if type(val) ~= "number" then return 0, "Timestamp is not a number" end
end
if type(val) ~= "number" then
return 0, "Timestamp is not a number"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return string, nil|string ---@return string, nil|string
GetSubevent = function(...) GetSubevent = function(...)
local val = select(CLEUEventInfo["GENERIC"]["subevent"], ...) local val = select(CLEUEventInfo["GENERIC"]["subevent"], ...)
if val == nil then if val == nil then return "", "Subevent is nil or missing" end
return "", "Subevent is nil or missing" if type(val) ~= "string" then return "", "Subevent is not a string" end
end
if type(val) ~= "string" then
return "", "Subevent is not a string"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return boolean, nil|string ---@return boolean, nil|string
GetHideCaster = function(...) GetHideCaster = function(...)
local val = select(CLEUEventInfo["GENERIC"]["hideCaster"], ...) local val = select(CLEUEventInfo["GENERIC"]["hideCaster"], ...)
if val == nil then if val == nil then return false, "HideCaster is nil or missing" end
return false, "HideCaster is nil or missing" if type(val) ~= "boolean" then return false, "HideCaster is not a boolean" end
end
if type(val) ~= "boolean" then
return false, "HideCaster is not a boolean"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return string, nil|string ---@return string, nil|string
GetSourceGUID = function(...) GetSourceGUID = function(...)
local val = select(CLEUEventInfo["GENERIC"]["sourceGUID"], ...) local val = select(CLEUEventInfo["GENERIC"]["sourceGUID"], ...)
if val == nil then if val == nil then return "", "SourceGUID is nil or missing" end
return "", "SourceGUID is nil or missing" if type(val) ~= "string" then return "", "SourceGUID is not a string" end
end
if type(val) ~= "string" then
return "", "SourceGUID is not a string"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return string, nil|string ---@return string, nil|string
GetSourceName = function(...) GetSourceName = function(...)
local val = select(CLEUEventInfo["GENERIC"]["sourceName"], ...) local val = select(CLEUEventInfo["GENERIC"]["sourceName"], ...)
if val == nil then if val == nil then return "", "SourceName is nil or missing" end
return "", "SourceName is nil or missing" if type(val) ~= "string" then return "", "SourceName is not a string" end
end
if type(val) ~= "string" then
return "", "SourceName is not a string"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return number, nil|string ---@return number, nil|string
GetSourceFlags = function(...) GetSourceFlags = function(...)
local val = select(CLEUEventInfo["GENERIC"]["sourceFlags"], ...) local val = select(CLEUEventInfo["GENERIC"]["sourceFlags"], ...)
if val == nil then if val == nil then return 0, "SourceFlags is nil or missing" end
return 0, "SourceFlags is nil or missing" if type(val) ~= "number" then return 0, "SourceFlags is not a number" end
end
if type(val) ~= "number" then
return 0, "SourceFlags is not a number"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return number, nil|string ---@return number, nil|string
GetSourceRaidFlags = function(...) GetSourceRaidFlags = function(...)
local val = select(CLEUEventInfo["GENERIC"]["sourceRaidFlags"], ...) local val = select(CLEUEventInfo["GENERIC"]["sourceRaidFlags"], ...)
if val == nil then if val == nil then return 0, "SourceRaidFlags is nil or missing" end
return 0, "SourceRaidFlags is nil or missing" if type(val) ~= "number" then return 0, "SourceRaidFlags is not a number" end
end
if type(val) ~= "number" then
return 0, "SourceRaidFlags is not a number"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return string, nil|string ---@return string, nil|string
GetDestGUID = function(...) GetDestGUID = function(...)
local val = select(CLEUEventInfo["GENERIC"]["destGUID"], ...) local val = select(CLEUEventInfo["GENERIC"]["destGUID"], ...)
if val == nil then if val == nil then return "", "DestGUID is nil or missing" end
return "", "DestGUID is nil or missing" if type(val) ~= "string" then return "", "DestGUID is not a string" end
end
if type(val) ~= "string" then
return "", "DestGUID is not a string"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return string, nil|string ---@return string, nil|string
GetDestName = function(...) GetDestName = function(...)
local val = select(CLEUEventInfo["GENERIC"]["destName"], ...) local val = select(CLEUEventInfo["GENERIC"]["destName"], ...)
if val == nil then if val == nil then return "", "DestName is nil or missing" end
return "", "DestName is nil or missing" if type(val) ~= "string" then return "", "DestName is not a string" end
end
if type(val) ~= "string" then
return "", "DestName is not a string"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return number, nil|string ---@return number, nil|string
GetDestFlags = function(...) GetDestFlags = function(...)
local val = select(CLEUEventInfo["GENERIC"]["destFlags"], ...) local val = select(CLEUEventInfo["GENERIC"]["destFlags"], ...)
if val == nil then if val == nil then return 0, "DestFlags is nil or missing" end
return 0, "DestFlags is nil or missing" if type(val) ~= "number" then return 0, "DestFlags is not a number" end
end
if type(val) ~= "number" then
return 0, "DestFlags is not a number"
end
return val, nil return val, nil
end, end,
---@param ... any ---@param ... any
---@return number, nil|string ---@return number, nil|string
GetDestRaidFlags = function(...) GetDestRaidFlags = function(...)
local val = select(CLEUEventInfo["GENERIC"]["destRaidFlags"], ...) local val = select(CLEUEventInfo["GENERIC"]["destRaidFlags"], ...)
if val == nil then if val == nil then return 0, "DestRaidFlags is nil or missing" end
return 0, "DestRaidFlags is nil or missing" if type(val) ~= "number" then return 0, "DestRaidFlags is not a number" end
end
if type(val) ~= "number" then
return 0, "DestRaidFlags is not a number"
end
return val, nil return val, nil
end, end,
@@ -380,9 +304,7 @@ local function Init()
GetSpellId = function(...) GetSpellId = function(...)
local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellId"], ...) local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellId"], ...)
if val == nil then return 0, "SpellId is nil or missing" end if val == nil then return 0, "SpellId is nil or missing" end
if type(val) ~= "number" then if type(val) ~= "number" then return 0, "SpellId is not a number" end
return 0, "SpellId is not a number"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -396,12 +318,8 @@ local function Init()
---@return string, nil|string ---@return string, nil|string
GetSpellName = function(...) GetSpellName = function(...)
local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellName"], ...) local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellName"], ...)
if val == nil then if val == nil then return "", "SpellName is nil or missing" end
return "", "SpellName is nil or missing" if type(val) ~= "string" then return "", "SpellName is not a string" end
end
if type(val) ~= "string" then
return "", "SpellName is not a string"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -414,14 +332,9 @@ local function Init()
---@param ... any ---@param ... any
---@return number, nil|string ---@return number, nil|string
GetSpellSchool = function(...) GetSpellSchool = function(...)
local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellSchool"], local val = select(CLEUEventInfo["GENERIC_SPELL"]["spellSchool"], ...)
...) if val == nil then return 0, "SpellSchool is nil or missing" end
if val == nil then if type(val) ~= "number" then return 0, "SpellSchool is not a number" end
return 0, "SpellSchool is nil or missing"
end
if type(val) ~= "number" then
return 0, "SpellSchool is not a number"
end
return val, nil return val, nil
end, end,
@@ -451,15 +364,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetAmount = function(...) GetAmount = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["amount"], ...) local val = select(CLEUEventInfo[subevent]["amount"], ...)
if val == nil then return 0, "Amount is nil or missing" end if val == nil then return 0, "Amount is nil or missing" end
if type(val) ~= "number" then if type(val) ~= "number" then return 0, "Amount is not a number" end
return 0, "Amount is not a number"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -479,21 +387,12 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetOverkill = function(...) GetOverkill = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0, if not CLEUEventInfo[subevent] then return 0, "Subevent is not a valid event" end
string.format("Failed getting subevent due to: %s", err) if not CLEUEventInfo[subevent]["overkill"] then return 0, "Overkill is nil or missing" end
end
if not CLEUEventInfo[subevent] then
return 0, "Subevent is not a valid event"
end
if not CLEUEventInfo[subevent]["overkill"] then
return 0, "Overkill is nil or missing"
end
local val = select(CLEUEventInfo[subevent]["overkill"], ...) local val = select(CLEUEventInfo[subevent]["overkill"], ...)
if val == nil then return 0, "Overkill is nil or missing" end if val == nil then return 0, "Overkill is nil or missing" end
if type(val) ~= "number" then if type(val) ~= "number" then return 0, "Overkill is not a number" end
return 0, "Overkill is not a number"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -513,15 +412,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetSchool = function(...) GetSchool = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["school"], ...) local val = select(CLEUEventInfo[subevent]["school"], ...)
if val == nil then return 0, "School is nil or missing" end if val == nil then return 0, "School is nil or missing" end
if type(val) ~= "number" then if type(val) ~= "number" then return 0, "School is not a number" end
return 0, "School is not a number"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -543,17 +437,10 @@ local function Init()
---@return boolean, nil|string ---@return boolean, nil|string
GetResisted = function(...) GetResisted = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return false, string.format("Failed getting subevent due to: %s", err) end
return false,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["resisted"], ...) local val = select(CLEUEventInfo[subevent]["resisted"], ...)
if val == nil then if val == nil then return false, "Resisted is nil or missing" end
return false, "Resisted is nil or missing" if type(val) ~= "boolean" then return false, "Resisted is not a boolean" end
end
if type(val) ~= "boolean" then
return false, "Resisted is not a boolean"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -575,17 +462,10 @@ local function Init()
---@return boolean, nil|string ---@return boolean, nil|string
GetBlocked = function(...) GetBlocked = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return false, string.format("Failed getting subevent due to: %s", err) end
return false,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["blocked"], ...) local val = select(CLEUEventInfo[subevent]["blocked"], ...)
if val == nil then if val == nil then return false, "Blocked is nil or missing" end
return false, "Blocked is nil or missing" if type(val) ~= "boolean" then return false, "Blocked is not a boolean" end
end
if type(val) ~= "boolean" then
return false, "Blocked is not a boolean"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -608,17 +488,10 @@ local function Init()
---@return boolean, nil|string ---@return boolean, nil|string
GetAbsorbed = function(...) GetAbsorbed = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return false, string.format("Failed getting subevent due to: %s", err) end
return false,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["absorbed"], ...) local val = select(CLEUEventInfo[subevent]["absorbed"], ...)
if val == nil then if val == nil then return false, "Absorbed is nil or missing" end
return false, "Absorbed is nil or missing" if type(val) ~= "boolean" then return false, "Absorbed is not a boolean" end
end
if type(val) ~= "boolean" then
return false, "Absorbed is not a boolean"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -640,17 +513,10 @@ local function Init()
---@return boolean, nil|string ---@return boolean, nil|string
GetCritical = function(...) GetCritical = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return false, string.format("Failed getting subevent due to: %s", err) end
return false,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["critical"], ...) local val = select(CLEUEventInfo[subevent]["critical"], ...)
if val == nil then if val == nil then return false, "Critical is nil or missing" end
return false, "Critical is nil or missing" if type(val) ~= "boolean" then return false, "Critical is not a boolean" end
end
if type(val) ~= "boolean" then
return false, "Critical is not a boolean"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -670,17 +536,10 @@ local function Init()
---@return boolean, nil|string ---@return boolean, nil|string
GetGlancing = function(...) GetGlancing = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return false, string.format("Failed getting subevent due to: %s", err) end
return false,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["glancing"], ...) local val = select(CLEUEventInfo[subevent]["glancing"], ...)
if val == nil then if val == nil then return false, "Glancing is nil or missing" end
return false, "Glancing is nil or missing" if type(val) ~= "boolean" then return false, "Glancing is not a boolean" end
end
if type(val) ~= "boolean" then
return false, "Glancing is not a boolean"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -700,17 +559,10 @@ local function Init()
---@return boolean, nil|string ---@return boolean, nil|string
GetCrushing = function(...) GetCrushing = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return false, string.format("Failed getting subevent due to: %s", err) end
return false,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["crushing"], ...) local val = select(CLEUEventInfo[subevent]["crushing"], ...)
if val == nil then if val == nil then return false, "Crushing is nil or missing" end
return false, "Crushing is nil or missing" if type(val) ~= "boolean" then return false, "Crushing is not a boolean" end
end
if type(val) ~= "boolean" then
return false, "Crushing is not a boolean"
end
return val, nil return val, nil
end, end,
--- Specific to subevents prefixed by: --- Specific to subevents prefixed by:
@@ -731,17 +583,10 @@ local function Init()
---@return boolean, nil|string ---@return boolean, nil|string
GetIsOffHand = function(...) GetIsOffHand = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return false, string.format("Failed getting subevent due to: %s", err) end
return false,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["isOffHand"], ...) local val = select(CLEUEventInfo[subevent]["isOffHand"], ...)
if val == nil then if val == nil then return false, "IsOffHand is nil or missing" end
return false, "IsOffHand is nil or missing" if type(val) ~= "boolean" then return false, "IsOffHand is not a boolean" end
end
if type(val) ~= "boolean" then
return false, "IsOffHand is not a boolean"
end
return val, nil return val, nil
end, end,
@@ -764,17 +609,10 @@ local function Init()
---@return string, nil|string ---@return string, nil|string
GetMissType = function(...) GetMissType = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return "", string.format("Failed getting subevent due to: %s", err) end
return "",
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["missType"], ...) local val = select(CLEUEventInfo[subevent]["missType"], ...)
if val == nil then if val == nil then return "", "MissType is nil or missing" end
return "", "MissType is nil or missing" if type(val) ~= "string" then return "", "MissType is not a string" end
end
if type(val) ~= "string" then
return "", "MissType is not a string"
end
return val, nil return val, nil
end, end,
@@ -797,17 +635,10 @@ local function Init()
--- return type is unconfirmed! --- return type is unconfirmed!
GetAmountMissed = function(...) GetAmountMissed = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["amountMissed"], ...) local val = select(CLEUEventInfo[subevent]["amountMissed"], ...)
if val == nil then if val == nil then return 0, "AmountMissed is nil or missing" end
return 0, "AmountMissed is nil or missing" if type(val) ~= "number" then return 0, "AmountMissed is not a number" end
end
if type(val) ~= "number" then
return 0, "AmountMissed is not a number"
end
return val, nil return val, nil
end, end,
@@ -830,17 +661,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetOverhealing = function(...) GetOverhealing = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["overhealing"], ...) local val = select(CLEUEventInfo[subevent]["overhealing"], ...)
if val == nil then if val == nil then return 0, "Overhealing is nil or missing" end
return 0, "Overhealing is nil or missing" if type(val) ~= "number" then return 0, "Overhealing is not a number" end
end
if type(val) ~= "number" then
return 0, "Overhealing is not a number"
end
return val, nil return val, nil
end, end,
@@ -861,17 +685,10 @@ local function Init()
---@return string, nil|string ---@return string, nil|string
GetExtraGUID = function(...) GetExtraGUID = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return "", string.format("Failed getting subevent due to: %s", err) end
return "",
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["extraGUID"], ...) local val = select(CLEUEventInfo[subevent]["extraGUID"], ...)
if val == nil then if val == nil then return "", "ExtraGUID is nil or missing" end
return "", "ExtraGUID is nil or missing" if type(val) ~= "string" then return "", "ExtraGUID is not a string" end
end
if type(val) ~= "string" then
return "", "ExtraGUID is not a string"
end
return val, nil return val, nil
end, end,
@@ -892,17 +709,10 @@ local function Init()
---@return string, nil|string ---@return string, nil|string
GetExtraName = function(...) GetExtraName = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return "", string.format("Failed getting subevent due to: %s", err) end
return "",
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["extraName"], ...) local val = select(CLEUEventInfo[subevent]["extraName"], ...)
if val == nil then if val == nil then return "", "ExtraName is nil or missing" end
return "", "ExtraName is nil or missing" if type(val) ~= "string" then return "", "ExtraName is not a string" end
end
if type(val) ~= "string" then
return "", "ExtraName is not a string"
end
return val, nil return val, nil
end, end,
@@ -923,17 +733,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetExtraFlags = function(...) GetExtraFlags = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["extraFlags"], ...) local val = select(CLEUEventInfo[subevent]["extraFlags"], ...)
if val == nil then if val == nil then return 0, "ExtraFlags is nil or missing" end
return 0, "ExtraFlags is nil or missing" if type(val) ~= "number" then return 0, "ExtraFlags is not a number" end
end
if type(val) ~= "number" then
return 0, "ExtraFlags is not a number"
end
return val, nil return val, nil
end, end,
@@ -954,17 +757,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetExtraRaidFlags = function(...) GetExtraRaidFlags = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["extraRaidFlags"], ...) local val = select(CLEUEventInfo[subevent]["extraRaidFlags"], ...)
if val == nil then if val == nil then return 0, "ExtraRaidFlags is nil or missing" end
return 0, "ExtraRaidFlags is nil or missing" if type(val) ~= "number" then return 0, "ExtraRaidFlags is not a number" end
end
if type(val) ~= "number" then
return 0, "ExtraRaidFlags is not a number"
end
return val, nil return val, nil
end, end,
@@ -989,17 +785,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetExtraSpellID = function(...) GetExtraSpellID = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["extraSpellID"], ...) local val = select(CLEUEventInfo[subevent]["extraSpellID"], ...)
if val == nil then if val == nil then return 0, "ExtraSpellID is nil or missing" end
return 0, "ExtraSpellID is nil or missing" if type(val) ~= "number" then return 0, "ExtraSpellID is not a number" end
end
if type(val) ~= "number" then
return 0, "ExtraSpellID is not a number"
end
return val, nil return val, nil
end, end,
@@ -1025,17 +814,10 @@ local function Init()
---@return string, nil|string ---@return string, nil|string
GetExtraSpellName = function(...) GetExtraSpellName = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return "", string.format("Failed getting subevent due to: %s", err) end
return "",
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["extraSpellName"], ...) local val = select(CLEUEventInfo[subevent]["extraSpellName"], ...)
if val == nil then if val == nil then return "", "extraSpellName is nil or missing" end
return "", "extraSpellName is nil or missing" if type(val) ~= "string" then return "", "extraSpellName is not a string" end
end
if type(val) ~= "string" then
return "", "extraSpellName is not a string"
end
return val, nil return val, nil
end, end,
@@ -1061,17 +843,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetExtraSchool = function(...) GetExtraSchool = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["extraSchool"], ...) local val = select(CLEUEventInfo[subevent]["extraSchool"], ...)
if val == nil then if val == nil then return 0, "ExtraSchool is nil or missing" end
return 0, "ExtraSchool is nil or missing" if type(val) ~= "number" then return 0, "ExtraSchool is not a number" end
end
if type(val) ~= "number" then
return 0, "ExtraSchool is not a number"
end
return val, nil return val, nil
end, end,
@@ -1092,17 +867,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetAbsorbedAmount = function(...) GetAbsorbedAmount = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["absorbedAmount"], ...) local val = select(CLEUEventInfo[subevent]["absorbedAmount"], ...)
if val == nil then if val == nil then return 0, "AbsorbedAmount is nil or missing" end
return 0, "AbsorbedAmount is nil or missing" if type(val) ~= "number" then return 0, "AbsorbedAmount is not a number" end
end
if type(val) ~= "number" then
return 0, "AbsorbedAmount is not a number"
end
return val, nil return val, nil
end, end,
@@ -1123,17 +891,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetOverEnergize = function(...) GetOverEnergize = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["overEnergize"], ...) local val = select(CLEUEventInfo[subevent]["overEnergize"], ...)
if val == nil then if val == nil then return 0, "OverEnergize is nil or missing" end
return 0, "OverEnergize is nil or missing" if type(val) ~= "number" then return 0, "OverEnergize is not a number" end
end
if type(val) ~= "number" then
return 0, "OverEnergize is not a number"
end
return val, nil return val, nil
end, end,
@@ -1158,17 +919,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetPowerType = function(...) GetPowerType = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["powerType"], ...) local val = select(CLEUEventInfo[subevent]["powerType"], ...)
if val == nil then if val == nil then return 0, "PowerType is nil or missing" end
return 0, "PowerType is nil or missing" if type(val) ~= "number" then return 0, "PowerType is not a number" end
end
if type(val) ~= "number" then
return 0, "PowerType is not a number"
end
return val, nil return val, nil
end, end,
@@ -1190,17 +944,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetExtraAmount = function(...) GetExtraAmount = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["extraAmount"], ...) local val = select(CLEUEventInfo[subevent]["extraAmount"], ...)
if val == nil then if val == nil then return 0, "ExtraAmount is nil or missing" end
return 0, "ExtraAmount is nil or missing" if type(val) ~= "number" then return 0, "ExtraAmount is not a number" end
end
if type(val) ~= "number" then
return 0, "ExtraAmount is not a number"
end
return val, nil return val, nil
end, end,
@@ -1229,17 +976,10 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetExtraSpellId = function(...) GetExtraSpellId = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["extraSpellId"], ...) local val = select(CLEUEventInfo[subevent]["extraSpellId"], ...)
if val == nil then if val == nil then return 0, "ExtraSpellId is nil or missing" end
return 0, "ExtraSpellId is nil or missing" if type(val) ~= "number" then return 0, "ExtraSpellId is not a number" end
end
if type(val) ~= "number" then
return 0, "ExtraSpellId is not a number"
end
return val, nil return val, nil
end, end,
@@ -1268,17 +1008,12 @@ local function Init()
---@return number, nil|string ---@return number, nil|string
GetExtraAuraType = function(...) GetExtraAuraType = function(...)
local subevent, err = CLEUParser.GetSubevent(...) local subevent, err = CLEUParser.GetSubevent(...)
if err then if err then return 0, string.format("Failed getting subevent due to: %s", err) end
return 0,
string.format("Failed getting subevent due to: %s", err)
end
local val = select(CLEUEventInfo[subevent]["auraType"], ...) local val = select(CLEUEventInfo[subevent]["auraType"], ...)
if val == nil then return 0, "AuraType is nil or missing" end if val == nil then return 0, "AuraType is nil or missing" end
if type(val) ~= "number" then if type(val) ~= "number" then return 0, "AuraType is not a number" end
return 0, "AuraType is not a number"
end
return val, nil return val, nil
end end,
} }
end end
@@ -1286,7 +1021,5 @@ local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_LOGIN") frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_ENTERING_WORLD") frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("GUILD_ROSTER_UPDATE") frame:RegisterEvent("GUILD_ROSTER_UPDATE")
frame:SetScript("OnEvent", function(self, event, ...) frame:SetScript("OnEvent", function(self, event, ...) Init() end)
Init()
end)
Init() Init()

49
Modules/ChatSniffer.lua Normal file
View File

@@ -0,0 +1,49 @@
local _, shared = ...
---@cast shared HeimdallShared
local ModuleName = "ChatSniffer"
---@class HeimdallChatSnifferConfig
---@field enabled boolean
---@field debug boolean
---@class ChatSniffer
shared.ChatSniffer = {
Init = function()
Heimdall_Chat = Heimdall_Chat or {}
local frame = CreateFrame("Frame")
frame:RegisterEvent("CHAT_MSG_SAY")
frame:RegisterEvent("CHAT_MSG_YELL")
frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:RegisterEvent("CHAT_MSG_WHISPER")
frame:RegisterEvent("CHAT_MSG_CHANNEL_JOIN")
frame:RegisterEvent("CHAT_MSG_CHANNEL_LEAVE")
frame:RegisterEvent("CHAT_MSG_EMOTE")
frame:RegisterEvent("CHAT_MSG_PARTY")
frame:RegisterEvent("CHAT_MSG_PARTY_LEADER")
frame:RegisterEvent("CHAT_MSG_RAID")
frame:RegisterEvent("CHAT_MSG_RAID_LEADER")
frame:RegisterEvent("CHAT_MSG_RAID_WARNING")
frame:RegisterEvent("CHAT_MSG_SYSTEM")
frame:RegisterEvent("CHAT_MSG_TEXT_EMOTE")
frame:RegisterEvent("CHAT_MSG_YELL")
frame:SetScript("OnEvent", function(self, event, msg, sender, language, channel)
if not Heimdall_Data.config.chatSniffer.enabled then return end
if not Heimdall_Data.config.chatSniffer.debug then
shared.dump(string.format("[%s] got message", { event, msg, sender, language, channel }))
end
local timestamp = date("%Y-%m-%d %H:%M:%S")
local log = string.format(
"%s|%s|%s|%s|%s|%s",
tostring(timestamp),
tostring(event),
tostring(sender),
tostring(msg),
tostring(language),
tostring(channel)
)
Heimdall_Chat[#Heimdall_Chat + 1] = log
end)
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -1,118 +1,141 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "CombatAlerter" local ModuleName = "CombatAlerter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallCombatAlerterConfig
shared.CombatAlerter = {} ---@field enabled boolean
function shared.CombatAlerter.Init() ---@field debug boolean
local alerted = {} ---@field channels string[]
local combatAlerterFrame = CreateFrame("Frame")
combatAlerterFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") ---@class CombatAlerter
combatAlerterFrame:SetScript("OnEvent", function(self, event, ...) shared.CombatAlerter = {
if Heimdall_Data.config.combatAlerter.debug then Init = function()
print(string.format("[%s] Combat log event received", ModuleName)) local alerted = {}
end local combatAlerterFrame = CreateFrame("Frame")
if not Heimdall_Data.config.combatAlerter.enabled then combatAlerterFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
combatAlerterFrame:SetScript("OnEvent", function(self, event, ...)
if Heimdall_Data.config.combatAlerter.debug then if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Module disabled, ignoring combat event", ModuleName)) print(string.format("[%s] Combat log event received", ModuleName))
end end
return if not Heimdall_Data.config.combatAlerter.enabled then
end
local destination, err = CLEUParser.GetDestName(...)
if err then
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Error getting destination: %s", ModuleName, err))
end
return
end
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Combat event destination: %s", ModuleName, destination))
end
if destination ~= UnitName("player") then
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Ignoring event - not targeted at player", ModuleName))
end
return
end
local source, err = CLEUParser.GetSourceName(...)
if err then
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Error getting source, using 'unknown': %s", ModuleName, err))
end
source = "unknown"
end
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Combat event source: %s", ModuleName, source))
end
if shared.stinkyTracker.stinkies and shared.stinkyTracker.stinkies[source] then
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Source is tracked stinky: %s (Already alerted: %s)", ModuleName, source,
tostring(alerted[source] or false)))
end
if alerted[source] then return end
alerted[source] = true
local x, y = GetPlayerMapPosition("player")
local zone, subZone = GetZoneText(), GetSubZoneText()
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Player location: %s/%s at %.2f,%.2f", ModuleName, zone, subZone, x * 100,
y * 100))
end
SetMapToCurrentZone()
SetMapByID(GetCurrentMapAreaID())
local areaId = GetCurrentMapAreaID()
for _, channel in pairs(Heimdall_Data.config.combatAlerter.channels) do
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(shared._L("combatAlerterInCombat", locale),
source,
shared._L("zone", locale), shared._L("subZone", locale),
tostring(areaId),
x * 100, y * 100
)
---@type Message
local msg = {
channel = "C",
data = channel,
message = text,
}
if Heimdall_Data.config.combatAlerter.debug then if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Queuing alert message", ModuleName)) print(string.format("[%s] Module disabled, ignoring combat event", ModuleName))
shared.dumpTable(msg)
end end
table.insert(shared.messenger.queue, msg) return
end end
elseif Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Source not in stinky list, ignoring: %s", ModuleName, source))
end
end)
local combatTriggerFrame = CreateFrame("Frame") ---@type string|nil, string, string
combatTriggerFrame:RegisterEvent("PLAYER_REGEN_DISABLED") local err, source, destination
combatTriggerFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
combatTriggerFrame:SetScript("OnEvent", function(self, event, ...) destination, err = CLEUParser.GetDestName(...)
if Heimdall_Data.config.combatAlerter.debug then if err then
print(string.format("[%s] Combat state changed: %s", ModuleName, event)) if Heimdall_Data.config.combatAlerter.debug then
if event == "PLAYER_REGEN_DISABLED" then print(string.format("[%s] Error getting destination: %s", ModuleName, err))
print(string.format("[%s] Entered combat - Resetting alerts", ModuleName)) end
else return
print(string.format("[%s] Left combat - Resetting alerts", ModuleName))
end end
end
alerted = {}
end)
if Heimdall_Data.config.combatAlerter.debug then if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Combat event destination: %s", ModuleName, destination))
end
if destination ~= UnitName("player") then
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Ignoring event - not targeted at player", ModuleName))
end
return
end
source, err = CLEUParser.GetSourceName(...)
if err then
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Error getting source, using 'unknown': %s", ModuleName, err))
end
source = "unknown"
end
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Combat event source: %s", ModuleName, source))
end
if shared.StinkyTracker.IsStinky(source) then
if Heimdall_Data.config.combatAlerter.debug then
print(
string.format(
"[%s] Source is tracked stinky: %s (Already alerted: %s)",
ModuleName,
source,
tostring(alerted[source] or false)
)
)
end
if alerted[source] then return end
alerted[source] = true
local x, y = GetPlayerMapPosition("player")
local zone, subZone = GetZoneText(), GetSubZoneText()
if Heimdall_Data.config.combatAlerter.debug then
print(
string.format(
"[%s] Player location: %s/%s at %.2f,%.2f",
ModuleName,
zone,
subZone,
x * 100,
y * 100
)
)
end
SetMapToCurrentZone()
SetMapByID(GetCurrentMapAreaID())
local areaId = GetCurrentMapAreaID()
for _, channel in pairs(Heimdall_Data.config.combatAlerter.channels) do
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(
shared._L("combatAlerterInCombat", locale),
source,
shared._L("zone", locale),
shared._L("subZone", locale),
tostring(areaId),
x * 100,
y * 100
)
---@type Message
local msg = {
channel = "C",
data = channel,
message = text,
}
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Queuing alert message", ModuleName))
shared.dump(msg)
end
table.insert(shared.messenger.queue, msg)
end
elseif Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Source not in stinky list, ignoring: %s", ModuleName, source))
end
end)
local combatTriggerFrame = CreateFrame("Frame")
combatTriggerFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
combatTriggerFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
combatTriggerFrame:SetScript("OnEvent", function(self, event, ...)
if Heimdall_Data.config.combatAlerter.debug then
print(string.format("[%s] Combat state changed: %s", ModuleName, event))
if event == "PLAYER_REGEN_DISABLED" then
print(string.format("[%s] Entered combat - Resetting alerts", ModuleName))
else
print(string.format("[%s] Left combat - Resetting alerts", ModuleName))
end
end
alerted = {}
end)
if Heimdall_Data.config.combatAlerter.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName)) print(string.format("[%s] Module initialized", ModuleName))
end end,
print("[Heimdall] CombatAlerter loaded") }
end

View File

@@ -1,8 +1,14 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Commander" local ModuleName = "Commander"
---@class HeimdallCommanderConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field commander string
---@field commands table<string, boolean>
local helpMessages = { local helpMessages = {
ru = { ru = {
"1) who - пишет вам никнеймы текущих врагов и локу.", "1) who - пишет вам никнеймы текущих врагов и локу.",
@@ -29,311 +35,404 @@ local helpMessages = {
"9) note <name> i..j - lists notes from i to j for the character.", "9) note <name> i..j - lists notes from i to j for the character.",
"10) note <name> delete i - deletes the i-th note for the character.", "10) note <name> delete i - deletes the i-th note for the character.",
"11) note <name> delete i..j - deletes notes from i to j for the character.", "11) note <name> delete i..j - deletes notes from i to j for the character.",
} },
} }
---@diagnostic disable-next-line: missing-fields ---@class Commander
shared.Commander = {} shared.Commander = {
function shared.Commander.Init() Init = function()
---@param text string ---@param text string
---@param size number ---@param size number
---@return string[] ---@return string[]
local function Partition(text, size) local function Partition(text, size)
local words = {} local words = {}
for word in text:gmatch("[^,]+") do for word in text:gmatch("[^,]+") do
words[#words + 1] = word words[#words + 1] = word
end end
local ret = {} local ret = {}
local currentChunk = "" local currentChunk = ""
for _, word in ipairs(words) do for _, word in ipairs(words) do
if #currentChunk + #word + 1 <= size then if #currentChunk + #word + 1 <= size then
currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word) currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word)
else else
if #currentChunk > 0 then if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
ret[#ret + 1] = currentChunk currentChunk = word
end end
currentChunk = word
end end
end
if #currentChunk > 0 then if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
ret[#ret + 1] = currentChunk
end
return ret return ret
end end
---@param arr table<string, Player> ---@param arr table<string, Player>
---@return string[] ---@return string[]
local function Count(arr) local function Count(arr)
local ret = {} local ret = {}
for _, player in pairs(arr) do for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(player.zone) then if shared.Whoer.ShouldNotifyForZone(player.zone) then ret[player.zone] = (ret[player.zone] or 0) + 1 end
ret[player.zone] = (ret[player.zone] or 0) + 1
end end
end local text = {}
local text = {} for zone, count in pairs(ret) do
for zone, count in pairs(ret) do text[#text + 1] = string.format("%s: %d", zone, count)
text[#text + 1] = string.format("%s: %d", zone, count)
end
return text
end
---@param arr table<string, Player>
---@return string[]
local function CountPartitioned(arr)
local count = Count(arr)
local text = {}
for _, line in pairs(Partition(strjoin(", ", unpack(count)), 200)) do
text[#text + 1] = line
end
return text
end
---@param arr table<string, Player>
---@return string[]
local function Who(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(player.zone) then
ret[#ret + 1] = string.format("%s/%s (%s) %s", player.name, player.class, player.zone,
player.stinky and "(!!!!)" or "")
end end
return text
end end
if Heimdall_Data.config.commander.debug then ---@param arr table<string, Player>
print(string.format("[%s] Command result: %s", ModuleName, strjoin(", ", unpack(ret)))) ---@return string[]
end local function CountPartitioned(arr)
return ret local count = Count(arr)
end local text = {}
---@param arr table<string, Player> ---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
---@return string[] for _, line in pairs(Partition(strjoin(", ", unpack(count)), 200)) do
local function WhoPartitioned(arr) text[#text + 1] = line
local who = Who(arr)
local text = {}
for _, line in pairs(Partition(strjoin(", ", unpack(who)), 200)) do
text[#text + 1] = "who: " .. line
end
return text
end
---@param arr table<string, Player>
---@return string[]
local function CountClass(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(player.zone) then
ret[player.class] = (ret[player.class] or 0) + 1
end end
return text
end end
local text = {} ---@param arr table<string, Player>
for class, count in pairs(ret) do ---@return string[]
text[#text + 1] = string.format("%s: %d", class, count) local function Who(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(player.zone) then
ret[#ret + 1] = string.format(
"%s/%s (%s) %s",
player.name,
player.class,
player.zone,
player.stinky and "(!!!!)" or ""
)
end
end
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Command result: %s", ModuleName, strjoin(", ", unpack(ret))))
end
return ret
end end
if Heimdall_Data.config.commander.debug then -- This is really ugly, duplicating methods like this
print(string.format("[%s] Message text: %s", ModuleName, strjoin(", ", unpack(text)))) -- But I have no better idea
-- We would have to drag reference channel all the way here
-- And then in here do some kind of deciding based on the fucking channel locale
-- That's also a nasty solution... I guess adding "kto" is better
---@param arr table<string, Player>
---@return string[]
local function WhoRu(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(player.zone) then
shared.dump(player)
ret[#ret + 1] = string.format(
"%s/%s (%s) %s",
player.name,
shared._L(player.class, "ru"),
shared._L(player.zone, "ru"),
player.stinky and "(!!!!)" or ""
)
end
end
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Command result: %s", ModuleName, strjoin(", ", unpack(ret))))
end
return ret
end end
return text ---@param arr table<string, Player>
end ---@return string[]
---@param arr table<string, Player> local function WhoPartitioned(arr)
---@return string[] local who = Who(arr)
local function CountClassPartitioned(arr) local text = {}
local countClass = CountClass(arr) ---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
local text = {} for _, line in pairs(Partition(strjoin(", ", unpack(who)), 200)) do
for _, line in pairs(Partition(strjoin(", ", unpack(countClass)), 200)) do text[#text + 1] = "who: " .. line
text[#text + 1] = line end
return text
end end
return text ---@param arr table<string, Player>
end ---@return string[]
local function CountClassPartitionedStinkies() local function WhoPartitionedRu(arr)
if Heimdall_Data.config.commander.debug then local who = WhoRu(arr)
print(string.format("[%s] Executing: CountClassPartitionedStinkies", local text = {}
ModuleName)) ---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
for _, line in pairs(Partition(strjoin(", ", unpack(who)), 200)) do
text[#text + 1] = "кто: " .. line
end
return text
end end
local res = CountClassPartitioned(HeimdallStinkies) ---@param arr table<string, Player>
if #res == 0 then ---@return string[]
return { "No stinkies found" } local function CountClass(arr)
local ret = {}
for _, player in pairs(arr) do
if shared.Whoer.ShouldNotifyForZone(player.zone) then
ret[player.class] = (ret[player.class] or 0) + 1
end
end
local text = {}
for class, count in pairs(ret) do
text[#text + 1] = string.format("%s: %d", class, count)
end
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Message text: %s", ModuleName, strjoin(", ", unpack(text))))
end
return text
end end
return res ---@param arr table<string, Player>
end ---@return string[]
local function WhoPartitionedStinkies() local function CountClassPartitioned(arr)
if Heimdall_Data.config.commander.debug then local countClass = CountClass(arr)
print(string.format("[%s] Executing: WhoPartitionedStinkies", local text = {}
ModuleName)) ---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
shared.dumpTable(HeimdallStinkies) for _, line in pairs(Partition(strjoin(", ", unpack(countClass)), 200)) do
text[#text + 1] = line
end
return text
end end
local res = WhoPartitioned(HeimdallStinkies) local function CountClassPartitionedStinkies()
if #res == 0 then if Heimdall_Data.config.commander.debug then
return { "No stinkies found" } print(string.format("[%s] Executing: CountClassPartitionedStinkies", ModuleName))
end
local res = CountClassPartitioned(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end end
return res local function WhoPartitionedStinkies()
end if Heimdall_Data.config.commander.debug then
local function CountPartitionedStinkies() print(string.format("[%s] Executing: WhoPartitionedStinkies", ModuleName))
if Heimdall_Data.config.commander.debug then shared.dump(HeimdallStinkies)
print(string.format("[%s] Executing: CountPartitionedStinkies", end
ModuleName)) local res = WhoPartitioned(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end end
local res = CountPartitioned(HeimdallStinkies) local function WhoPartitionedStinkiesRu()
if #res == 0 then if Heimdall_Data.config.commander.debug then
return { "No stinkies found" } print(string.format("[%s] Executing: WhoPartitionedStinkies", ModuleName))
shared.dump(HeimdallStinkies)
end
local res = WhoPartitionedRu(HeimdallStinkies)
if #res == 0 then return { "No stinkies found" } end
return res
end end
return res local function CountPartitionedStinkies()
end if Heimdall_Data.config.commander.debug then
local function HelpRu() print(string.format("[%s] Executing: CountPartitionedStinkies", ModuleName))
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpRu", ModuleName)) end end
return helpMessages.ru local res = CountPartitioned(HeimdallStinkies)
end if #res == 0 then return { "No stinkies found" } end
local function HelpEn() return res
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpEn", ModuleName)) end end
return helpMessages.en local function HelpRu()
end if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpRu", ModuleName)) end
local groupInviteFrame = CreateFrame("Frame") return helpMessages.ru
groupInviteFrame:SetScript("OnEvent", function(self, event, ...) end
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Event received", ModuleName)) end local function HelpEn()
AcceptGroup() if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpEn", ModuleName)) end
groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST") return helpMessages.en
C_Timer.NewTimer(0.1, function() end
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Click event triggered", ModuleName)) end local groupInviteFrame = CreateFrame("Frame")
_G["StaticPopup1Button1"]:Click() groupInviteFrame:SetScript("OnEvent", function(self, event, ...)
end, 1) if Heimdall_Data.config.commander.debug then print(string.format("[%s] Event received", ModuleName)) end
end) AcceptGroup()
local function JoinGroup()
if Heimdall_Data.config.commander.debug then print(string.format("[%s] JoinGroup command received", ModuleName)) end
groupInviteFrame:RegisterEvent("PARTY_INVITE_REQUEST")
C_Timer.NewTimer(10, function()
groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST") groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST")
end, 1) C_Timer.NewTimer(0.1, function()
return { "+" }
end
local function LeaveGroup()
if Heimdall_Data.config.commander.debug then print(string.format("[%s] LeaveGroup command received", ModuleName)) end
LeaveParty()
return {}
end
---@param target string
local function FollowTarget(target)
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Following target: %s", ModuleName, target)) end
if not target then return end
FollowUnit(target)
return {}
end
---@param args string[]
local function MacroTarget(args)
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Macroing: %s", ModuleName,
strjoin(" ", unpack(args))))
end
if #args < 2 or #args % 2 ~= 0 then
if #args < 2 or #args % 2 ~= 0 then
if Heimdall_Data.config.commander.debug then if Heimdall_Data.config.commander.debug then
print(string.format( print(string.format("[%s] Click event triggered", ModuleName))
"[%s] Invalid number of arguments for MacroTarget", ModuleName)) end
_G["StaticPopup1Button1"]:Click()
end, 1)
end)
local function JoinGroup()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] JoinGroup command received", ModuleName))
end
groupInviteFrame:RegisterEvent("PARTY_INVITE_REQUEST")
C_Timer.NewTimer(10, function() groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST") end, 1)
return { "+" }
end
local function LeaveGroup()
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] LeaveGroup command received", ModuleName))
end
LeaveParty()
return {}
end
---@param target string
local function FollowTarget(target)
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Following target: %s", ModuleName, target))
end
if not target then return end
FollowUnit(target)
return {}
end
---@param args string[]
local function MacroTarget(args)
if Heimdall_Data.config.commander.debug then
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
print(string.format("[%s] Macroing: %s", ModuleName, strjoin(" ", unpack(args))))
end
if #args < 2 or #args % 2 ~= 0 then
if #args < 2 or #args % 2 ~= 0 then
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Invalid number of arguments for MacroTarget", ModuleName))
end
return {}
end
end
table.remove(args, 1)
for i = 1, #args do
local stinky = strtrim(args[i])
local name = stinky:match("([^/]+)")
local class = stinky:match("/([^ $]+)")
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Adding stinky: %s/%s", ModuleName, name, tostring(class)))
end
shared.StinkyTracker.Track({
name = name,
class = class or "unknown",
seenAt = GetTime(),
hostile = true,
})
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Added stinky: %s/%s", ModuleName, name, tostring(class)))
end
end
return {}
end
---@param args string[]
local function IgnoreMacroTarget(args)
if Heimdall_Data.config.commander.debug then
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
print(string.format("[%s] Macroing: %s", ModuleName, strjoin(" ", unpack(args))))
end
if #args < 1 then
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Invalid number of arguments for IgnoreMacroTarget", ModuleName))
end end
return {} return {}
end end
end table.remove(args, 1)
table.remove(args, 1)
for i = 1, #args do for i = 1, #args do
local stinky = strtrim(args[i]) local stinky = strtrim(args[i])
local name = stinky:match("([^/]+)") local name = stinky:match("([^/]+)")
local class = stinky:match("/([^ $]+)") if Heimdall_Data.config.commander.debug then
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Ignoring stinky: %s", ModuleName, name))
print(string.format("[%s] Adding stinky: %s/%s", ModuleName, name, tostring(class))) end
shared.StinkyTracker.Ignore(name)
end end
shared.stinkyTracker.stinkies[name] = { return {}
name = name,
class = class or "unknown",
seenAt = GetTime(),
hostile = true
}
end end
return {}
end
---@class Command ---@class Command
---@field keywordRe string ---@field keywordRe string
---@field commanderOnly boolean ---@field commanderOnly boolean
---@field callback fun(...: any): string[] ---@field callback fun(...: any): string[]
local commands = { local commands = {
{ keywordRe = "^who$", commanderOnly = false, callback = WhoPartitionedStinkies }, { keywordRe = "^who$", commanderOnly = false, callback = WhoPartitionedStinkies },
{ keywordRe = "^howmany$", commanderOnly = false, callback = CountPartitionedStinkies }, { keywordRe = "^кто$", commanderOnly = false, callback = WhoPartitionedStinkiesRu },
{ keywordRe = "^classes$", commanderOnly = false, callback = CountClassPartitionedStinkies }, { keywordRe = "^howmany$", commanderOnly = false, callback = CountPartitionedStinkies },
{ keywordRe = "^help$", commanderOnly = false, callback = HelpRu }, { keywordRe = "^classes$", commanderOnly = false, callback = CountClassPartitionedStinkies },
{ keywordRe = "^helpen$", commanderOnly = false, callback = HelpEn }, { keywordRe = "^help$", commanderOnly = false, callback = HelpRu },
{ keywordRe = "^joingroup$", commanderOnly = false, callback = JoinGroup }, { keywordRe = "^helpen$", commanderOnly = false, callback = HelpEn },
{ keywordRe = "^leavegroup$", commanderOnly = false, callback = LeaveGroup }, { keywordRe = "^joingroup$", commanderOnly = false, callback = JoinGroup },
{ keywordRe = "^follow$", commanderOnly = false, callback = FollowTarget }, { keywordRe = "^leavegroup$", commanderOnly = false, callback = LeaveGroup },
{ keywordRe = "^macro", commanderOnly = false, callback = MacroTarget }, { keywordRe = "^follow$", commanderOnly = false, callback = FollowTarget },
} { keywordRe = "^macro", commanderOnly = false, callback = MacroTarget },
{ keywordRe = "^ignore", commanderOnly = false, callback = IgnoreMacroTarget },
}
local commanderChannelFrame = CreateFrame("Frame") local commanderChannelFrame = CreateFrame("Frame")
commanderChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL") commanderChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
commanderChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) commanderChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.commander.debug then
-- print(string.format("[%s] Event received", ModuleName))
-- shared.dumpTable(Heimdall_Data.config.commander)
--end
if not Heimdall_Data.config.commander.enabled then
--if Heimdall_Data.config.commander.debug then --if Heimdall_Data.config.commander.debug then
-- print(string.format("[%s] Module disabled, ignoring event", ModuleName)) -- print(string.format("[%s] Event received", ModuleName))
-- shared.dump(Heimdall_Data.config.commander)
--end --end
return if not Heimdall_Data.config.commander.enabled then
end --if Heimdall_Data.config.commander.debug then
local channelId = select(6, ...) -- print(string.format("[%s] Module disabled, ignoring event", ModuleName))
local _, channelname = GetChannelName(channelId) --end
local ok = false return
for _, channel in pairs(Heimdall_Data.config.commander.channels) do
if channel == channelname then
ok = true
break
end end
end local channelId = select(6, ...)
if not ok then local _, channelname = GetChannelName(channelId)
if Heimdall_Data.config.commander.debug then local ok = false
print(string.format("[%s] Channel name '%s' does not match any of the channels '%s'", ModuleName, for _, channel in pairs(Heimdall_Data.config.commander.channels) do
channelname, table.concat(Heimdall_Data.config.commander.channels, ", "))) if channel == channelname then
ok = true
break
end
end
if not ok then
if Heimdall_Data.config.commander.debug then
print(
string.format(
"[%s] Channel name '%s' does not match any of the channels '%s'",
ModuleName,
channelname,
table.concat(Heimdall_Data.config.commander.channels, ", ")
)
)
end
return
end end
return
end
sender = string.match(sender, "^[^-]+") sender = string.match(sender, "^[^-]+")
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Message from: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.commander)
end
for _, command in ipairs(commands) do
local enabled = Heimdall_Data.config.commander.commands[command.keywordRe] == true or false
if Heimdall_Data.config.commander.debug then if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Command match: %s = %s", ModuleName, command.keywordRe, tostring(enabled))) print(string.format("[%s] Message from: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.commander)
end end
if enabled and
(not command.commanderOnly for _, command in ipairs(commands) do
or (command.commanderOnly local enabled = Heimdall_Data.config.commander.commands[command.keywordRe] == true or false
and sender == Heimdall_Data.config.commander.commander)) then if Heimdall_Data.config.commander.debug then
if msg:match(command.keywordRe) then print(
local messages = command.callback({ strsplit(",", msg) }) string.format("[%s] Command match: %s = %s", ModuleName, command.keywordRe, tostring(enabled))
if Heimdall_Data.config.commander.debug then )
print(string.format("[%s] Messages to send: %s", ModuleName, strjoin(", ", unpack(messages)))) end
end if
for _, message in ipairs(messages) do enabled
---@type Message and (
local msg = { not command.commanderOnly
channel = "C", -- if Heimdall_Data.config.commander.debug then print(string.format("[%s] Ignoring command, sender %s not commander %s", ModuleName, sender, Heimdall_Data.config.commander.commander)) end
data = channelname,
message = message or (command.commanderOnly and sender == Heimdall_Data.config.commander.commander)
} )
then
if msg:match(command.keywordRe) then
---@diagnostic disable-next-line: redundant-parameter Currently luals does not support variadic functions as a @field
local messages = command.callback({ strsplit(",", msg) })
if Heimdall_Data.config.commander.debug then if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Queuing message", ModuleName)) print(string.format("[%s] Messages to send: %s", ModuleName, #messages))
shared.dumpTable(msg) shared.dump(messages)
end
for _, message in ipairs(messages) do
---@type Message
local returnmsg = {
channel = "C",
data = channelname,
message = message,
}
if Heimdall_Data.config.commander.debug then
print(string.format("[%s] Queuing message", ModuleName))
shared.dump(msg)
end
if Heimdall_Data.config.networkMessenger.enabled then
shared.NetworkMessenger.Enqueue(returnmsg)
elseif Heimdall_Data.config.messenger.enabled then
shared.Messenger.Enqueue(returnmsg)
end
end end
--table.insert(shared.messenger.queue, msg)
table.insert(shared.networkMessenger.queue, msg)
end end
end end
end end
end end)
end)
print("[Heimdall] Commander module loaded") print(string.format("[%s] Module initialized", ModuleName))
end end,
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,12 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Configurator" local ModuleName = "Configurator"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallConfiguratorConfig
shared.Configurator = {} ---@field enabled boolean
function shared.Configurator.Init() ---@field debug boolean
print("[Heimdall] Configurator module loaded")
end ---@class Configurator
shared.Configurator = {
Init = function() print(string.format("[%s] Module initialized", ModuleName)) end,
}

View File

@@ -1,185 +1,268 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "DeathReporter" local ModuleName = "DeathReporter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallDeathReporterConfig
shared.DeathReporter = {} ---@field enabled boolean
function shared.DeathReporter.Init() ---@field debug boolean
---@type table<string, number> ---@field throttle number
local recentDeaths = {} ---@field doWhisper boolean
---@type table<string, number> ---@field channels string[]
local recentDuels = {} ---@field zoneOverride string?
---@field duelThrottle number
---@param source string ---@class DeathReporter
---@param destination string shared.DeathReporter = {
---@param spellName string Init = function()
local function RegisterDeath(source, destination, spellName) ---@type table<string, number>
if Heimdall_Data.config.deathReporter.debug then local recentDeaths = {}
print(string.format("[%s] Processing death event - Source: %s, Target: %s, Spell: %s", ---@type table<string, number>
ModuleName, source, destination, spellName)) local recentDuels = {}
end
if not Heimdall_Data.config.deathReporter.enabled then ---@param source string
---@param destination string
---@param spellName string
local function RegisterDeath(source, destination, spellName)
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Module disabled, ignoring death event", ModuleName)) print(
string.format(
"[%s] Processing death event - Source: %s, Target: %s, Spell: %s",
ModuleName,
source,
destination,
spellName
)
)
end end
return
end
if recentDeaths[destination] and GetTime() - recentDeaths[destination] < Heimdall_Data.config.deathReporter.throttle then if not Heimdall_Data.config.deathReporter.enabled then
if Heimdall_Data.config.deathReporter.debug then
local timeLeft = Heimdall_Data.config.deathReporter.throttle - (GetTime() - recentDeaths[destination])
print(string.format("[%s] Death report throttled for %s (%.1f seconds remaining)",
ModuleName, destination, timeLeft))
end
return
end
if recentDuels[destination] and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle then
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Ignoring death report - Recent duel detected for target: %s", ModuleName,
destination))
end
return
end
if recentDuels[source] and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle then
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Ignoring death report - Recent duel detected for source: %s", ModuleName,
source))
end
return
end
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Recording death for %s", ModuleName, destination))
end
recentDeaths[destination] = GetTime()
C_Timer.NewTimer(3, function()
if recentDuels[destination] and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle then
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Cancelling delayed death report - Recent duel detected for: %s", ModuleName, print(string.format("[%s] Module disabled, ignoring death event", ModuleName))
destination))
end end
return return
end end
if recentDuels[source] and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle then if
recentDeaths[destination]
and GetTime() - recentDeaths[destination] < Heimdall_Data.config.deathReporter.throttle
then
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Cancelling delayed death report - Recent duel detected for: %s", ModuleName, local timeLeft = Heimdall_Data.config.deathReporter.throttle
source)) - (GetTime() - recentDeaths[destination])
print(
string.format(
"[%s] Death report throttled for %s (%.1f seconds remaining)",
ModuleName,
destination,
timeLeft
)
)
end
return
end
if
recentDuels[destination]
and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle
then
if Heimdall_Data.config.deathReporter.debug then
print(
string.format(
"[%s] Ignoring death report - Recent duel detected for target: %s",
ModuleName,
destination
)
)
end
return
end
if
recentDuels[source]
and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle
then
if Heimdall_Data.config.deathReporter.debug then
print(
string.format(
"[%s] Ignoring death report - Recent duel detected for source: %s",
ModuleName,
source
)
)
end end
return return
end end
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Sending death report - %s killed %s with %s", print(string.format("[%s] Recording death for %s", ModuleName, destination))
ModuleName, source, destination, spellName))
end end
recentDeaths[destination] = GetTime()
local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown" C_Timer.NewTimer(3, function()
if Heimdall_Data.config.spotter.zoneOverride then if
zone = Heimdall_Data.config.spotter.zoneOverride or "" recentDuels[destination]
subzone = "" and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle
end then
if Heimdall_Data.config.deathReporter.debug then
local x, y = GetPlayerMapPosition("player") print(
if Heimdall_Data.config.deathReporter.debug then string.format(
print(string.format("[%s] Player coordinates: %.2f, %.2f", ModuleName, x * 100, y * 100)) "[%s] Cancelling delayed death report - Recent duel detected for: %s",
end ModuleName,
SetMapToCurrentZone() destination
SetMapByID(GetCurrentMapAreaID()) )
local zoneId = GetCurrentMapAreaID() )
end
for _, channel in pairs(Heimdall_Data.config.deathReporter.channels) do return
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(shared._L("killed", locale),
source,
destination,
shared._L(spellName, locale),
shared._L(zone, locale), shared._L(subzone, locale),
zoneId,
x * 100, y * 100)
---@type Message
local msg = {
channel = "C",
data = channel,
message = text
}
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Queuing death report message", ModuleName))
shared.dumpTable(msg)
end end
table.insert(shared.messenger.queue, msg)
if
recentDuels[source]
and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle
then
if Heimdall_Data.config.deathReporter.debug then
print(
string.format(
"[%s] Cancelling delayed death report - Recent duel detected for: %s",
ModuleName,
source
)
)
end
return
end
if Heimdall_Data.config.deathReporter.debug then
print(
string.format(
"[%s] Sending death report - %s killed %s with %s",
ModuleName,
source,
destination,
spellName
)
)
end
local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown"
if Heimdall_Data.config.spotter.zoneOverride then
zone = Heimdall_Data.config.spotter.zoneOverride or ""
subzone = ""
end
local x, y = GetPlayerMapPosition("player")
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Player coordinates: %.2f, %.2f", ModuleName, x * 100, y * 100))
end
SetMapToCurrentZone()
SetMapByID(GetCurrentMapAreaID())
local zoneId = GetCurrentMapAreaID()
for _, channel in pairs(Heimdall_Data.config.deathReporter.channels) do
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(
shared._L("killed", locale),
source,
destination,
shared._L(spellName, locale),
shared._L(zone, locale),
shared._L(subzone, locale),
zoneId,
x * 100,
y * 100
)
---@type Message
local msg = {
channel = "C",
data = channel,
message = text,
}
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Queuing death report message", ModuleName))
shared.dump(msg)
end
table.insert(shared.messenger.queue, msg)
end
end)
end
local cleuFrame = CreateFrame("Frame")
cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
cleuFrame:SetScript("OnEvent", function(self, event, ...)
-- if Heimdall_Data.config.deathReporter.debug then
-- print(string.format("[%s] Received combat log event", ModuleName))
-- end
if not Heimdall_Data.config.deathReporter.enabled then return end
local overkill, source, destination, spellName, sourceGUID, destinationGUID, err
overkill, err = CLEUParser.GetOverkill(...)
if not err and overkill > 0 then
source, err = CLEUParser.GetSourceName(...)
if err then
source = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting source name", ModuleName))
end
end
destination, err = CLEUParser.GetDestName(...)
if err then
destination = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting destination name", ModuleName))
end
end
spellName, err = CLEUParser.GetSpellName(...)
if err then
spellName = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting spell name", ModuleName))
end
end
sourceGUID, err = CLEUParser.GetSourceGUID(...)
if err or not string.match(sourceGUID, "Player") then return end
destinationGUID, err = CLEUParser.GetDestGUID(...)
if err or not string.match(destinationGUID, "Player") then return end
RegisterDeath(source, destination, spellName)
end end
end) end)
end
local cleuFrame = CreateFrame("Frame") local systemMessageFrame = CreateFrame("Frame")
cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") systemMessageFrame:RegisterEvent("CHAT_MSG_SYSTEM")
cleuFrame:SetScript("OnEvent", function(self, event, ...) systemMessageFrame:SetScript("OnEvent", function(self, event, msg)
-- if Heimdall_Data.config.deathReporter.debug then if not Heimdall_Data.config.deathReporter.enabled then return end
-- print(string.format("[%s] Received combat log event", ModuleName)) local source, destination = string.match(msg, "([^ ]+) has defeated ([^ ]+) in a duel")
-- end
if not Heimdall_Data.config.deathReporter.enabled then return end
local overkill, err = CLEUParser.GetOverkill(...)
if not err and overkill > 0 then
local source, err = CLEUParser.GetSourceName(...)
if err then
source = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting source name", ModuleName))
end
end
local destination, err = CLEUParser.GetDestName(...)
if err then
destination = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting destination name", ModuleName))
end
end
local spellName, err = CLEUParser.GetSpellName(...)
if err then
spellName = "unknown"
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Error getting spell name", ModuleName))
end
end
local sourceGUID, err = CLEUParser.GetSourceGUID(...)
if err or not string.match(sourceGUID, "Player") then return end
local destinationGUID, err = CLEUParser.GetDestGUID(...)
if err or not string.match(destinationGUID, "Player") then return end
RegisterDeath(source, destination, spellName)
end
end)
local systemMessageFrame = CreateFrame("Frame")
systemMessageFrame:RegisterEvent("CHAT_MSG_SYSTEM")
systemMessageFrame:SetScript("OnEvent", function(self, event, msg)
if not Heimdall_Data.config.deathReporter.enabled then return end
local source, destination = string.match(msg, "([^ ]+) has defeated ([^ ]+) in a duel")
if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Received system message: %s", ModuleName, msg))
print(string.format("[%s] Source: %s, Destination: %s", ModuleName, tostring(source), tostring(destination)))
end
if not source or not destination then return end
source = string.match(source, "([^-]+)")
destination = string.match(destination, "([^-]+)")
if source and destination then
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Detected duel between %s and %s", ModuleName, source, destination)) print(string.format("[%s] Received system message: %s", ModuleName, msg))
print(
string.format(
"[%s] Source: %s, Destination: %s",
ModuleName,
tostring(source),
tostring(destination)
)
)
end end
local now = GetTime() if not source or not destination then return end
recentDuels[source] = now source = string.match(source, "([^-]+)")
recentDuels[destination] = now destination = string.match(destination, "([^-]+)")
end if source and destination then
end) if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Detected duel between %s and %s", ModuleName, source, destination))
end
local now = GetTime()
recentDuels[source] = now
recentDuels[destination] = now
end
end)
if Heimdall_Data.config.deathReporter.debug then if Heimdall_Data.config.deathReporter.debug then
print(string.format("[%s] Module initialized with throttle: %.1fs, duel throttle: %.1fs", print(
ModuleName, Heimdall_Data.config.deathReporter.throttle, Heimdall_Data.config.deathReporter.duelThrottle)) string.format(
end "[%s] Module initialized with throttle: %.1fs, duel throttle: %.1fs",
print("[Heimdall] DeathReporter loaded") ModuleName,
end Heimdall_Data.config.deathReporter.throttle,
Heimdall_Data.config.deathReporter.duelThrottle
)
)
end
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -1,51 +1,63 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Dueler" local ModuleName = "Dueler"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallDuelerConfig
shared.Dueler = {} ---@field enabled boolean
function shared.Dueler.Init() ---@field debug boolean
local frame = CreateFrame("Frame") ---@field declineOther boolean
frame:RegisterEvent("DUEL_REQUESTED")
frame:SetScript("OnEvent", function(self, event, sender)
if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Duel request received from: %s", ModuleName, sender))
end
if not Heimdall_Data.config.dueler.enabled then
if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Module disabled, ignoring duel request", ModuleName))
end
return
end
if Heimdall_Data.config.dueler.debug then ---@class Dueler
print(string.format("[%s] Checking if sender '%s' is in agents list", ModuleName, sender)) shared.Dueler = {
end Init = function()
local frame = CreateFrame("Frame")
local allow = Heimdall_Data.config.agents[sender] frame:RegisterEvent("DUEL_REQUESTED")
if allow then frame:SetScript("OnEvent", function(self, event, sender)
if Heimdall_Data.config.dueler.debug then if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Accepting duel from trusted agent: %s", ModuleName, sender)) print(string.format("[%s] Duel request received from: %s", ModuleName, sender))
end end
AcceptDuel() if not Heimdall_Data.config.dueler.enabled then
else
if Heimdall_Data.config.dueler.declineOther then
if Heimdall_Data.config.dueler.debug then if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Auto-declining duel from untrusted sender: %s", ModuleName, sender)) print(string.format("[%s] Module disabled, ignoring duel request", ModuleName))
end end
CancelDuel() return
end
if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Checking if sender '%s' is in agents list", ModuleName, sender))
end
local allow = shared.AgentTracker.IsAgent(sender)
if allow then
if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Accepting duel from trusted agent: %s", ModuleName, sender))
end
AcceptDuel()
else else
if Heimdall_Data.config.dueler.debug then if Heimdall_Data.config.dueler.declineOther then
print(string.format("[%s] Leaving duel request from %s for manual response", ModuleName, sender)) if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Auto-declining duel from untrusted sender: %s", ModuleName, sender))
end
CancelDuel()
else
if Heimdall_Data.config.dueler.debug then
print(
string.format("[%s] Leaving duel request from %s for manual response", ModuleName, sender)
)
end
end end
end end
end end)
end)
if Heimdall_Data.config.dueler.debug then if Heimdall_Data.config.dueler.debug then
print(string.format("[%s] Module initialized with auto-decline: %s", ModuleName, print(
tostring(Heimdall_Data.config.dueler.declineOther))) string.format(
end "[%s] Module initialized with auto-decline: %s",
print("[Heimdall] Dueler loaded") ModuleName,
end tostring(Heimdall_Data.config.dueler.declineOther)
)
)
end
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -1,28 +1,31 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
if not shared.dumpTable then if not shared.dump then
---@param table table ---@param value any
---@param msg string?
---@param depth number? ---@param depth number?
shared.dumpTable = function(table, depth) shared.dump = function(value, msg, depth)
if not table then if not value then
print(tostring(table)) print(tostring(value))
return return
end end
if depth == nil then if type(value) ~= "table" then
depth = 0 print(tostring(value))
end
if (depth > 200) then
print("Error: Depth > 200 in dumpTable()")
return return
end end
for k, v in pairs(table) do if msg then print(msg) end
if (type(v) == "table") then if depth == nil then depth = 0 end
print(string.rep(" ", depth) .. k .. ":") if depth > 200 then
shared.dumpTable(v, depth + 1) print("Error: Depth > 200 in dump()")
return
end
for k, v in pairs(value) do
if type(v) == "table" then
print(string.rep(" ", depth) .. tostring(k) .. ":")
shared.dump(v, msg, depth + 1)
else else
print(string.rep(" ", depth) .. k .. ": ", v) print(string.rep(" ", depth) .. tostring(k) .. ": " .. tostring(v))
end end
end end
end end

View File

@@ -1,61 +1,65 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Echoer" local ModuleName = "Echoer"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallEchoerConfig
shared.Echoer = {} ---@field enabled boolean
function shared.Echoer.Init() ---@field debug boolean
local frame = CreateFrame("Frame") ---@field channels string[]
frame:RegisterEvent("CHAT_MSG_CHANNEL") ---@field prefix string
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.echoer.debug then
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
--end
if not Heimdall_Data.config.echoer.enabled then ---@class Echoer
shared.Echoer = {
Init = function()
local frame = CreateFrame("Frame")
frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.echoer.debug then --if Heimdall_Data.config.echoer.debug then
-- print(string.format("[%s] Module disabled, ignoring message", ModuleName)) -- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
--end --end
return
end
local channelId = select(6, ...) if not Heimdall_Data.config.echoer.enabled then
local _, channelname = GetChannelName(channelId) --if Heimdall_Data.config.echoer.debug then
local ok = false -- print(string.format("[%s] Module disabled, ignoring message", ModuleName))
for _, channel in pairs(Heimdall_Data.config.echoer.channels) do --end
if channel == channelname then return
ok = true
break
end end
end
if not ok then
if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.echoer)
end
if string.find(msg, "^" .. Heimdall_Data.config.echoer.prefix) then local channelId = select(6, ...)
if Heimdall_Data.config.echoer.debug then local _, channelname = GetChannelName(channelId)
print(string.format("[%s] Found echo command in message: %s", ModuleName, msg)) local ok = false
for _, channel in pairs(Heimdall_Data.config.echoer.channels) do
if channel == channelname then
ok = true
break
end
end end
local msg = string.sub(msg, string.len(Heimdall_Data.config.echoer.prefix) + 1) if not ok then
if Heimdall_Data.config.echoer.debug then if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Echoing message: %s", ModuleName, msg)) print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.echoer)
end end
SendChatMessage(msg, "SAY")
elseif Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Message does not start with echo prefix", ModuleName))
end
end)
if Heimdall_Data.config.echoer.debug then if string.find(msg, "^" .. Heimdall_Data.config.echoer.prefix) then
if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Found echo command in message: %s", ModuleName, msg))
end
local echomsg = string.sub(msg, string.len(Heimdall_Data.config.echoer.prefix) + 1)
if Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Echoing message: %s", ModuleName, echomsg))
end
SendChatMessage(echomsg, "SAY")
elseif Heimdall_Data.config.echoer.debug then
print(string.format("[%s] Message does not start with echo prefix", ModuleName))
end
end)
if Heimdall_Data.config.echoer.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName)) print(string.format("[%s] Module initialized", ModuleName))
end end,
print("[Heimdall] Echoer loaded") }
end

View File

@@ -1,62 +1,66 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Emoter" local ModuleName = "Emoter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallEmoterConfig
shared.Emoter = {} ---@field enabled boolean
function shared.Emoter.Init() ---@field debug boolean
local frame = CreateFrame("Frame") ---@field channels string[]
frame:RegisterEvent("CHAT_MSG_CHANNEL") ---@field prefix string
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.emoter.debug then
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
--end
if not Heimdall_Data.config.emoter.enabled then ---@class Emoter
shared.Emoter = {
Init = function()
local frame = CreateFrame("Frame")
frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.emoter.debug then --if Heimdall_Data.config.emoter.debug then
-- print(string.format("[%s] Module disabled, ignoring message", ModuleName)) -- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
--end --end
return
end
local channelId = select(6, ...) if not Heimdall_Data.config.emoter.enabled then
local _, channelname = GetChannelName(channelId) --if Heimdall_Data.config.emoter.debug then
local ok = false -- print(string.format("[%s] Module disabled, ignoring message", ModuleName))
for _, channel in pairs(Heimdall_Data.config.emoter.channels) do --end
if channel == channelname then return
ok = true
break
end end
end
if not ok then local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.emoter.channels) do
if channel == channelname then
ok = true
break
end
end
if not ok then
if Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if Heimdall_Data.config.emoter.debug then if Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName)) print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.emoter)
end end
return
end
if Heimdall_Data.config.emoter.debug then if string.find(msg, "^" .. Heimdall_Data.config.emoter.prefix) then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender)) if Heimdall_Data.config.emoter.debug then
shared.dumpTable(Heimdall_Data.config.emoter) print(string.format("[%s] Found emote command in message: %s", ModuleName, msg))
end end
local emote = string.sub(msg, string.len(Heimdall_Data.config.emoter.prefix) + 1)
if string.find(msg, "^" .. Heimdall_Data.config.emoter.prefix) then if Heimdall_Data.config.emoter.debug then
if Heimdall_Data.config.emoter.debug then print(string.format("[%s] Performing emote: %s", ModuleName, emote))
print(string.format("[%s] Found emote command in message: %s", ModuleName, msg)) end
DoEmote(emote)
elseif Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Message does not start with emote prefix", ModuleName))
end end
local emote = string.sub(msg, string.len(Heimdall_Data.config.emoter.prefix) + 1) end)
if Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Performing emote: %s", ModuleName, emote))
end
DoEmote(emote)
elseif Heimdall_Data.config.emoter.debug then
print(string.format("[%s] Message does not start with emote prefix", ModuleName))
end
end)
if Heimdall_Data.config.emoter.debug then if Heimdall_Data.config.emoter.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName)) print(string.format("[%s] Module initialized", ModuleName))
end end,
print("[Heimdall] Emoter loaded") }
end

View File

@@ -1,249 +1,275 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Inviter" local ModuleName = "Inviter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallInviterConfig
shared.Inviter = {} ---@field enabled boolean
function shared.Inviter.Init() ---@field debug boolean
if type(Heimdall_Data.config.inviter.listeningChannel) == "string" then ---@field channels string[]
Heimdall_Data.config.inviter.listeningChannel = { ---@field keyword string
[Heimdall_Data.config.inviter.listeningChannel] = true ---@field allAssist boolean
} ---@field agentsAssist boolean
end ---@field throttle number
---@type Timer ---@field kickOffline boolean
local updateTimer = nil ---@field cleanupInterval number
---@field afkThreshold number
---@field listeningChannel table<string, boolean>
local function FixGroup() ---@class Inviter
if Heimdall_Data.config.inviter.debug then shared.Inviter = {
print(string.format("[%s] Checking and fixing group configuration", ModuleName)) Init = function()
-- Fallback for old config
if type(Heimdall_Data.config.inviter.listeningChannel) == "string" then
Heimdall_Data.config.inviter.listeningChannel = {
[Heimdall_Data.config.inviter.listeningChannel] = true,
}
end end
---@type Timer
local updateTimer = nil
if not IsInRaid() then local function FixGroup()
if Heimdall_Data.config.inviter.debug then if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Converting party to raid", ModuleName)) print(string.format("[%s] Checking and fixing group configuration", ModuleName))
end
ConvertToRaid()
end
if Heimdall_Data.config.inviter.allAssist then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Setting all members to assistant", ModuleName))
end
SetEveryoneIsAssistant()
end
if Heimdall_Data.config.inviter.agentsAssist then
if Heimdall_Data.config.inviter.debug then
local agentCount = 0
for _ in pairs(Heimdall_Data.config.agents) do agentCount = agentCount + 1 end
print(string.format("[%s] Processing %d agents for assistant promotion", ModuleName, agentCount))
end end
for name, _ in pairs(Heimdall_Data.config.agents) do if not IsInRaid() then
if UnitInParty(name) and not UnitIsGroupLeader(name) and not UnitIsRaidOfficer(name) then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Promoting agent to assistant: %s", ModuleName, name))
end
PromoteToAssistant(name, true)
elseif Heimdall_Data.config.inviter.debug then
if not UnitInParty(name) then
print(string.format("[%s] Agent not in party: %s", ModuleName, name))
elseif UnitIsGroupLeader(name) then
print(string.format("[%s] Agent is already leader: %s", ModuleName, name))
elseif UnitIsRaidOfficer(name) then
print(string.format("[%s] Agent is already assistant: %s", ModuleName, name))
end
end
end
end
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Group configuration update complete", ModuleName))
end
end
---@param name string
---@return Frame?
local function FindPlayerRaidFrame(name)
for group = 1, 8 do
for player = 1, 5 do
local button = _G[string.format("ElvUF_RaidGroup%dUnitButton%d", group, player)]
if Heimdall_Data.config.inviter.debug then if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] button = %s", ModuleName, tostring(button))) print(string.format("[%s] Converting party to raid", ModuleName))
end
local unitName = button and button.unit and UnitName(button.unit)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] unitName = %s", ModuleName, tostring(unitName)))
end
if unitName == name then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] unitName == name", ModuleName))
end
return button
end end
ConvertToRaid()
end end
end
return nil
end
local framePool = {} if Heimdall_Data.config.inviter.allAssist then
local playerButtons = {} if Heimdall_Data.config.inviter.debug then
setmetatable(playerButtons, { __mode = "kv" }) print(string.format("[%s] Setting all members to assistant", ModuleName))
---@param players string[] end
local function OverlayKickButtons(players) SetEveryoneIsAssistant()
for _, frame in pairs(framePool) do end
frame:Hide()
end
for _, name in pairs(players) do
local frame = FindPlayerRaidFrame(name)
if frame then
playerButtons[name] = frame
local button = framePool[frame.unit] or CreateFrame("Button",
string.format("HeimdallKickButton%s", frame.unit, frame, "SecureActionButtonTemplate"))
framePool[frame.unit] = button
button:SetSize(frame.UNIT_WIDTH / 2, frame.UNIT_HEIGHT / 2) if Heimdall_Data.config.inviter.agentsAssist then
button:SetPoint("CENTER", frame, "CENTER", 0, 0) if Heimdall_Data.config.inviter.debug then
button:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon") print(string.format("[%s] Processing agents for assistant promotion", ModuleName))
button:SetHighlightTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon") end
button:SetPushedTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetDisabledTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon") shared.AgentTracker.ForEach(function(agent)
button:SetAlpha(0.5) if UnitInParty(agent) and not UnitIsGroupLeader(agent) and not UnitIsRaidOfficer(agent) then
button:Show() if Heimdall_Data.config.inviter.debug then
button:SetScript("OnClick", function() print(string.format("[%s] Promoting agent to assistant: %s", ModuleName, agent))
UninviteUnit(name) end
button:Hide() PromoteToAssistant(agent, true)
elseif Heimdall_Data.config.inviter.debug then
if not UnitInParty(agent) then
print(string.format("[%s] Agent not in party: %s", ModuleName, agent))
elseif UnitIsGroupLeader(agent) then
print(string.format("[%s] Agent is already leader: %s", ModuleName, agent))
elseif UnitIsRaidOfficer(agent) then
print(string.format("[%s] Agent is already assistant: %s", ModuleName, agent))
end
end
end) end)
else end
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Frame for player %s not found", ModuleName, name)) if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Group configuration update complete", ModuleName))
end
end
---@param name string
---@return Frame?
local function FindPlayerRaidFrame(name)
for group = 1, 8 do
for player = 1, 5 do
local button = _G[string.format("ElvUF_RaidGroup%dUnitButton%d", group, player)]
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] button = %s", ModuleName, tostring(button)))
end
local unitName = button and button.unit and UnitName(button.unit)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] unitName = %s", ModuleName, tostring(unitName)))
end
if unitName == name then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] unitName == name", ModuleName))
end
return button
end
end
end
return nil
end
local framePool = {}
local playerButtons = {}
setmetatable(playerButtons, { __mode = "kv" })
---@param players string[]
local function OverlayKickButtons(players)
for _, frame in pairs(framePool) do
frame:Hide()
end
for _, name in pairs(players) do
local frame = FindPlayerRaidFrame(name)
if frame then
playerButtons[name] = frame
-- All of these are ELVUI specific so they won't be in our meta...
---@diagnostic disable-next-line: undefined-field
local button = framePool[frame.unit]
or CreateFrame(
"Button",
---@diagnostic disable-next-line: undefined-field
string.format("HeimdallKickButton%s", frame.unit, frame, "SecureActionButtonTemplate")
)
---@diagnostic disable-next-line: undefined-field
framePool[frame.unit] = button
---@diagnostic disable-next-line: undefined-field
button:SetSize(frame.UNIT_WIDTH / 2, frame.UNIT_HEIGHT / 2)
button:SetPoint("CENTER", frame, "CENTER", 0, 0)
button:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetHighlightTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetPushedTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetDisabledTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
button:SetAlpha(0.5)
button:Show()
button:SetScript("OnClick", function()
UninviteUnit(name)
button:Hide()
end)
else
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Frame for player %s not found", ModuleName, name))
end
end end
end end
end end
end
---@type table<string, number> ---@type table<string, number>
local groupMembers = {} local groupMembers = {}
local function CleanGroups() local function CleanGroups()
if not Heimdall_Data.config.inviter.kickOffline then return end if not Heimdall_Data.config.inviter.kickOffline then return end
print("Cleaning groups") print("Cleaning groups")
local now = GetTime() local now = GetTime()
for i = 1, 40 do for i = 1, 40 do
local unit = "raid" .. i local unit = "raid" .. i
if UnitExists(unit) then if UnitExists(unit) then
local name = UnitName(unit) local name = UnitName(unit)
if name then if name then
-- When we load (into game) we want to consider everyone "online" -- When we load (into game) we want to consider everyone "online"
-- In other words everyone we haven't seen before is "online" the first time we see them -- In other words everyone we haven't seen before is "online" the first time we see them
-- This is to avoid kicking people who might not be offline which we don't know because we just logged in -- This is to avoid kicking people who might not be offline which we don't know because we just logged in
if not groupMembers[name] then if not groupMembers[name] then
groupMembers[name] = now
else
local online = UnitIsConnected(unit)
if online then
groupMembers[name] = now groupMembers[name] = now
else
local online = UnitIsConnected(unit)
if online then groupMembers[name] = now end
end end
end end
end end
end end
end local afkPlayers = {}
local afkPlayers = {} for name, time in pairs(groupMembers) do
for name, time in pairs(groupMembers) do if not UnitInParty(name) then
if not UnitInParty(name) then print(string.format("%s no longer in party", name))
print(string.format("%s no longer in party", name)) groupMembers[name] = nil
groupMembers[name] = nil else
else if time < now - Heimdall_Data.config.inviter.afkThreshold then
if time < now - Heimdall_Data.config.inviter.afkThreshold then print(string.format("Kicking %s for being offline", name))
print(string.format("Kicking %s for being offline", name)) afkPlayers[#afkPlayers + 1] = name
afkPlayers[#afkPlayers + 1] = name -- Blyat this is protected...
-- Blyat this is protected... -- UninviteUnit(name)
-- UninviteUnit(name) end
end end
end end
OverlayKickButtons(afkPlayers)
end end
OverlayKickButtons(afkPlayers) local function Tick()
end CleanGroups()
local function Tick() C_Timer.NewTimer(Heimdall_Data.config.inviter.cleanupInterval, Tick, 1)
CleanGroups()
C_Timer.NewTimer(Heimdall_Data.config.inviter.cleanupInterval, Tick, 1)
end
Tick()
local groupRosterUpdateFrame = CreateFrame("Frame")
groupRosterUpdateFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
groupRosterUpdateFrame:SetScript("OnEvent", function(self, event, ...)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Event received: %s", ModuleName, event))
end end
Tick()
if not Heimdall_Data.config.inviter.enabled then local groupRosterUpdateFrame = CreateFrame("Frame")
groupRosterUpdateFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
groupRosterUpdateFrame:SetScript("OnEvent", function(self, event, ...)
if Heimdall_Data.config.inviter.debug then if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Module disabled, ignoring event", ModuleName)) print(string.format("[%s] Event received: %s", ModuleName, event))
end end
return
if not Heimdall_Data.config.inviter.enabled then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Module disabled, ignoring event", ModuleName))
end
return
end
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Group roster changed - Checking configuration", ModuleName))
end
if updateTimer then updateTimer:Cancel() end
updateTimer = C_Timer.NewTimer(Heimdall_Data.config.inviter.throttle, FixGroup)
end)
local inviterChannelFrame = CreateFrame("Frame")
inviterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
inviterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.inviter.debug then
-- print(string.format("[%s] Chat message received: %s", ModuleName, msg))
-- shared.dump(Heimdall_Data.config.inviter)
--end
if not Heimdall_Data.config.inviter.enabled then return end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Channel name: %s", ModuleName, channelname))
end
local ok = false
for _, channel in pairs(Heimdall_Data.config.inviter.channels) do
if channel == channelname then
ok = true
break
end
end
if not ok then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if msg == Heimdall_Data.config.inviter.keyword then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Inviting %s", ModuleName, sender))
end
InviteUnit(sender)
else
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Message does not match keyword", ModuleName))
end
end
end)
if Heimdall_Data.config.inviter.debug then
print(
string.format(
"[%s] Module initialized - All assist: %s, Agents assist: %s",
ModuleName,
tostring(Heimdall_Data.config.inviter.allAssist),
tostring(Heimdall_Data.config.inviter.agentsAssist)
)
)
end end
if Heimdall_Data.config.inviter.debug then if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Group roster changed - Checking configuration", ModuleName)) print(
string.format(
"[%s] Module initialized - All assist: %s, Agents assist: %s",
ModuleName,
tostring(Heimdall_Data.config.inviter.allAssist),
tostring(Heimdall_Data.config.inviter.agentsAssist)
)
)
end end
if updateTimer then updateTimer:Cancel() end print(string.format("[%s] Module initialized", ModuleName))
updateTimer = C_Timer.NewTimer(Heimdall_Data.config.inviter.throttle, FixGroup) end,
end) }
local inviterChannelFrame = CreateFrame("Frame")
inviterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
inviterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.inviter.debug then
-- print(string.format("[%s] Chat message received: %s", ModuleName, msg))
-- shared.dumpTable(Heimdall_Data.config.inviter)
--end
if not Heimdall_Data.config.inviter.enabled then return end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Channel name: %s", ModuleName, channelname))
end
local ok = false
for _, channel in pairs(Heimdall_Data.config.inviter.channels) do
if channel == channelname then
ok = true
break
end
end
if not ok then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
end
return
end
if msg == Heimdall_Data.config.inviter.keyword then
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Inviting %s", ModuleName, sender))
end
InviteUnit(sender)
else
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Message does not match keyword", ModuleName))
end
end
end)
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Module initialized - All assist: %s, Agents assist: %s",
ModuleName,
tostring(Heimdall_Data.config.inviter.allAssist),
tostring(Heimdall_Data.config.inviter.agentsAssist)))
end
if Heimdall_Data.config.inviter.debug then
print(string.format("[%s] Module initialized - All assist: %s, Agents assist: %s",
ModuleName,
tostring(Heimdall_Data.config.inviter.allAssist),
tostring(Heimdall_Data.config.inviter.agentsAssist)))
end
print("[Heimdall] Inviter loaded")
end

View File

@@ -1,107 +1,101 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Macroer" local ModuleName = "Macroer"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallMacroerConfig
shared.Macroer = {} ---@field enabled boolean
function shared.Macroer.Init() ---@field debug boolean
---@class stinky ---@field priority string[]
---@field name string
---@field class string
---@field seenAt number
---@field hostile boolean
local function FindOrCreateMacro(macroName) ---@class Macroer
if Heimdall_Data.config.macroer.debug then shared.Macroer = {
print(string.format("[%s] Finding or creating macro: %s", ModuleName, macroName)) Init = function()
end local function FindOrCreateMacro(macroName)
local idx = GetMacroIndexByName(macroName) if Heimdall_Data.config.macroer.debug then
if idx == 0 then print(string.format("[%s] Finding or creating macro: %s", ModuleName, macroName))
if Heimdall_Data.config.macroer.debug then end
print(string.format("[%s] Creating new macro: %s", ModuleName, macroName)) local idx = GetMacroIndexByName(macroName)
end if idx == 0 then
CreateMacro(macroName, "INV_Misc_QuestionMark", "") if Heimdall_Data.config.macroer.debug then
end print(string.format("[%s] Creating new macro: %s", ModuleName, macroName))
idx = GetMacroIndexByName(macroName) end
if Heimdall_Data.config.macroer.debug then CreateMacro(macroName, "INV_Misc_QuestionMark", "")
print(string.format("[%s] Macro index: %d", ModuleName, idx)) end
end idx = GetMacroIndexByName(macroName)
return idx if Heimdall_Data.config.macroer.debug then print(string.format("[%s] Macro index: %d", ModuleName, idx)) end
end return idx
end
---@param stinkies table<string, stinky> ---@param stinkies table<string, Stinky>
local function FixMacro(stinkies) local function FixMacro(stinkies)
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Fixing macro with %d stinkies", ModuleName, #stinkies)) print(string.format("[%s] Fixing macro with %d stinkies", ModuleName, #stinkies))
end end
if not Heimdall_Data.config.macroer.enabled then if not Heimdall_Data.config.macroer.enabled then
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Module disabled, skipping macro update", ModuleName)) print(string.format("[%s] Module disabled, skipping macro update", ModuleName))
end end
return return
end end
if InCombatLockdown() then if InCombatLockdown() then
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] In combat, skipping macro update", ModuleName)) print(string.format("[%s] In combat, skipping macro update", ModuleName))
end end
return return
end end
local priorityMap = {} local priorityMap = {}
for priority, className in ipairs(Heimdall_Data.config.macroer.priority) do for priority, className in ipairs(Heimdall_Data.config.macroer.priority) do
priorityMap[className] = priority priorityMap[className] = priority
end end
local minPriority = #Heimdall_Data.config.macroer.priority + 1 local minPriority = #Heimdall_Data.config.macroer.priority + 1
local sortedStinkies = {} local sortedStinkies = {}
for _, stinky in pairs(stinkies) do for _, stinky in pairs(stinkies) do
if not Heimdall_Data.config.agents[stinky.name] then if not shared.AgentTracker.IsAgent(stinky.name) then sortedStinkies[#sortedStinkies + 1] = stinky end
sortedStinkies[#sortedStinkies + 1] = stinky end
end
end
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Processing %d non-agent stinkies", ModuleName, #sortedStinkies)) print(string.format("[%s] Processing %d non-agent stinkies", ModuleName, #sortedStinkies))
end end
table.sort(sortedStinkies, function(a, b) table.sort(sortedStinkies, function(a, b)
local aPriority = priorityMap[a.class] or minPriority local aPriority = priorityMap[a.class] or minPriority
local bPriority = priorityMap[b.class] or minPriority local bPriority = priorityMap[b.class] or minPriority
return aPriority > bPriority return aPriority > bPriority
end) end)
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Sorted stinkies: %d", ModuleName, #sortedStinkies)) print(string.format("[%s] Sorted stinkies: %d", ModuleName, #sortedStinkies))
shared.dumpTable(sortedStinkies) shared.dump(sortedStinkies)
end end
local lines = { "/targetenemy" } local lines = { "/targetenemy" }
for _, stinky in pairs(sortedStinkies) do for _, stinky in pairs(sortedStinkies) do
if stinky.seenAt > GetTime() - 600 then if stinky.seenAt > GetTime() - 600 then
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Adding target macro for: %s", ModuleName, stinky.name)) print(string.format("[%s] Adding target macro for: %s", ModuleName, stinky.name))
end end
lines[#lines + 1] = string.format("/tar %s", stinky.name) lines[#lines + 1] = string.format("/tar %s", stinky.name)
end end
end end
local idx = FindOrCreateMacro("HeimdallTarget") local idx = FindOrCreateMacro("HeimdallTarget")
local body = strjoin("\n", unpack(lines)) ---@diagnostic disable-next-line: param-type-mismatch
if Heimdall_Data.config.macroer.debug then local body = strjoin("\n", unpack(lines))
print(string.format("[%s] Updating macro with %d lines", ModuleName, #lines)) if Heimdall_Data.config.macroer.debug then
end print(string.format("[%s] Updating macro with %d lines", ModuleName, #lines))
EditMacro(idx, "HeimdallTarget", "INV_Misc_QuestionMark", body) end
end EditMacro(idx, "HeimdallTarget", "INV_Misc_QuestionMark", body)
end
shared.stinkyTracker.stinkies:onChange(function(value) shared.StinkyTracker.OnChange(function(stinkies)
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then
print(string.format("[%s] Stinkies changed, updating macro", ModuleName)) print(string.format("[%s] Stinkies changed, updating macro", ModuleName))
end shared.dump(stinkies)
FixMacro(value) end
end) FixMacro(stinkies)
end)
if Heimdall_Data.config.macroer.debug then if Heimdall_Data.config.macroer.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName)) print(string.format("[%s] Module initialized", ModuleName))
end end,
print("[Heimdall] Macroer loaded") }
end

View File

@@ -1,137 +1,176 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Messenger" local ModuleName = "Messenger"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallMessengerConfig
shared.Messenger = {} ---@field enabled boolean
function shared.Messenger.Init() ---@field debug boolean
---@class Message ---@field interval number
---@field message string
---@field channel string
---@field data string
local function FindOrJoinChannel(channelName, password) ---@class HeimdallMessengerData
local channelId = GetChannelName(channelName) ---@field queue ReactiveValue<table<string, Message>>
if channelId == 0 then ---@field ticker Timer?
---@class Message
---@field message string
---@field channel string
---@field data string
---@class Messenger
shared.Messenger = {
---@param message Message
Enqueue = function(message) table.insert(shared.messenger.queue, message) end,
Init = function()
shared.messenger = {
queue = ReactiveValue.new({}),
}
local function FindOrJoinChannel(channelName, password)
local channelId = GetChannelName(channelName)
if channelId == 0 then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel not found, joining: %s", ModuleName, channelName))
end
if password then
JoinPermanentChannel(channelName, password)
else
JoinPermanentChannel(channelName)
end
end
channelId = GetChannelName(channelName)
if Heimdall_Data.config.messenger.debug then if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel not found, joining: %s", ModuleName, channelName)) print(string.format("[%s] Channel found with ID: %s (%s)", ModuleName, channelId, channelName))
end
if password then
JoinPermanentChannel(channelName, password)
else
JoinPermanentChannel(channelName)
end end
return channelId
end end
channelId = GetChannelName(channelName)
if not shared.messenger.ticker then
local function DoMessage()
-- if Heimdall_Data.config.messenger.debug then
-- print(
-- string.format(
-- "[%s] Processing message queue - Size: %d",
-- ModuleName,
-- #shared.messenger.queue:get()
-- )
-- )
-- end
if not Heimdall_Data.config.messenger.enabled then
-- if Heimdall_Data.config.messenger.debug then
-- print(string.format("[%s] Module disabled, skipping message processing", ModuleName))
-- end
return
end
---@type Message
local message = shared.messenger.queue[1]
if not message then
-- if Heimdall_Data.config.messenger.debug then
-- print(string.format("[%s] Message queue empty", ModuleName))
-- end
return
end
if Heimdall_Data.config.messenger.debug then shared.dump(message, "[%s] Processing message:") end
if not message.message or message.message == "" then
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Invalid message: empty content", ModuleName))
end
return
end
if not message.channel or message.channel == "" then
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Invalid message: no channel specified", ModuleName))
end
return
end
if string.find(message.channel, "^C") then
if Heimdall_Data.config.messenger.debug then
shared.dump(
message,
string.format("[%s] Converting channel type from C to CHANNEL", ModuleName)
)
end
message.channel = "CHANNEL"
elseif string.find(message.channel, "^W") then
if Heimdall_Data.config.messenger.debug then
shared.dump(
message,
string.format("[%s] Converting channel type from W to WHISPER", ModuleName)
)
end
message.channel = "WHISPER"
end
if message.channel == "CHANNEL" and message.data and string.match(message.data, "%D") then
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Processing channel message:", ModuleName))
end
local channelId = GetChannelName(message.data)
if channelId == 0 then
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Channel not found, joining:", ModuleName))
end
channelId = FindOrJoinChannel(message.data)
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel join result - ID: %s", ModuleName, channelId))
end
end
message.data = tostring(channelId)
end
table.remove(shared.messenger.queue, 1)
if not message.message or message.message == "" then
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Skipping empty message", ModuleName))
end
return
end
if not message.channel or message.channel == "" then
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Skipping message with no channel", ModuleName))
end
return
end
if not message.data or message.data == "" then
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Skipping message with no data", ModuleName))
end
return
end
if Heimdall_Data.config.messenger.debug then
shared.dump(message, string.format("[%s] Sending message:", ModuleName))
end
if string.len(message.message) > 255 then
shared.dump(message, string.format("[%s] Message too long!!!!: %s", ModuleName, message.message))
return
end
SendChatMessage(message.message, message.channel, nil, message.data)
end
local function Tick()
-- if Heimdall_Data.config.messenger.debug then
-- print(string.format("[%s] Tick - Queue size: %d", ModuleName, #shared.messenger.queue:get()))
-- end
DoMessage()
shared.messenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.messenger.interval, Tick, 1)
end
Tick()
end
if Heimdall_Data.config.messenger.debug then if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel found with ID: %s (%s)", ModuleName, channelId, channelName)) print(
string.format(
"[%s] Module initialized with interval: %s",
ModuleName,
Heimdall_Data.config.messenger.interval
)
)
end end
return channelId print(string.format("[%s] Module initialized", ModuleName))
end end,
}
---@diagnostic disable-next-line: missing-fields
if not shared.messenger then shared.messenger = {} end
if not shared.messenger.queue then shared.messenger.queue = {} end
if not shared.messenger.ticker then
local function DoMessage()
--if Heimdall_Data.config.messenger.debug then
-- print(string.format("[%s] Processing message queue", ModuleName))
--end
if not Heimdall_Data.config.messenger.enabled then
--if Heimdall_Data.config.messenger.debug then
-- print(string.format("[%s] Module disabled, skipping message processing", ModuleName))
--end
return
end
---@type Message
local message = shared.messenger.queue[1]
if not message then
--if Heimdall_Data.config.messenger.debug then
-- print(string.format("[%s] Message queue empty", ModuleName))
--end
return
end
if not message.message or message.message == "" then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Invalid message: empty content", ModuleName))
end
return
end
if not message.channel or message.channel == "" then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Invalid message: no channel specified", ModuleName))
end
return
end
if string.find(message.channel, "^C") then
message.channel = "CHANNEL"
elseif string.find(message.channel, "^W") then
message.channel = "WHISPER"
end
if message.channel == "CHANNEL" and message.data and string.match(message.data, "%D") then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Processing channel message: '%s' to '%s'", ModuleName, message.message,
message.data))
end
local channelId = GetChannelName(message.data)
if channelId == 0 then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel not found, attempting to join: %s", ModuleName, message.data))
end
channelId = FindOrJoinChannel(message.data)
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Channel join result - ID: %s", ModuleName, channelId))
end
end
message.data = tostring(channelId)
end
table.remove(shared.messenger.queue, 1)
if not message.message or message.message == "" then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Skipping empty message", ModuleName))
end
return
end
if not message.channel or message.channel == "" then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Skipping message with no channel", ModuleName))
end
return
end
if not message.data or message.data == "" then
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Skipping message with no data", ModuleName))
end
return
end
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Sending message: '%s' to %s:%s", ModuleName, message.message, message.channel,
message.data))
end
if string.len(message.message) > 255 then
print(string.format("[%s] Message too long!!!!: %s", ModuleName, message.message))
return
end
SendChatMessage(message.message, message.channel, nil, message.data)
end
local function Tick()
--if Heimdall_Data.config.messenger.debug then
-- local queueSize = #shared.messenger.queue
-- print(string.format("[%s] Queue check - Messages pending: %d", ModuleName, queueSize))
--end
DoMessage()
shared.messenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.messenger.interval, Tick, 1)
end
Tick()
end
if Heimdall_Data.config.messenger.debug then
print(string.format("[%s] Module initialized with interval: %s", ModuleName,
Heimdall_Data.config.messenger.interval))
end
print("[Heimdall] Messenger loaded")
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,76 +1,85 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Network" local ModuleName = "Network"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallNetworkConfig
shared.Network = {} ---@field enabled boolean
function shared.Network.Init() ---@field debug boolean
if not shared.network then shared.network = {} end ---@field members string[]
local updatePending = false ---@field updateInterval number
local function FriendListUpdate() ---@class HeimdallNetworkData
updatePending = false ---@field ticker Timer?
if not Heimdall_Data.config.network.enabled then return end
---@type table<string, boolean> ---@class Network
local friends = {} shared.Network = {
for i = 1, GetNumFriends() do Init = function()
local name, level, class, area, connected, status, note, RAF = GetFriendInfo(i) if not shared.network then shared.network = {} end
if name then local updatePending = false
friends[name] = connected
if Heimdall_Data.config.network.debug then local function FriendListUpdate()
print(string.format("[%s] Friend %s is %s", ModuleName, name, connected and "online" or "offline")) updatePending = false
if not Heimdall_Data.config.network.enabled then return end
---@type table<string, boolean>
local friends = {}
for i = 1, GetNumFriends() do
local name, _, _, _, connected, _, _, _ = GetFriendInfo(i)
if name then
friends[name] = connected
if Heimdall_Data.config.network.debug then
print(
string.format("[%s] Friend %s is %s", ModuleName, name, connected and "online" or "offline")
)
end
else
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Friend %s is nil", ModuleName, i))
end
end end
else end
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Friend %s is nil", ModuleName, i)) for _, member in ipairs(Heimdall_Data.config.network.members) do
if friends[member] == nil and member ~= UnitName("player") then
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Adding friend %s", ModuleName, member))
end
AddFriend(member)
end end
end end
friends[UnitName("player")] = true
shared.networkNodes = {}
-- Why are we skipping this again...?
-- if false then shared.networkNodes[#shared.networkNodes + 1] = UnitName("player") end
for _, player in ipairs(Heimdall_Data.config.network.members) do
if friends[player] then
shared.networkNodes[#shared.networkNodes + 1] = player
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Adding network node %s", ModuleName, player))
end
end
end
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Network nodes:", ModuleName))
shared.dump(shared.networkNodes)
end
end end
for _, member in ipairs(Heimdall_Data.config.network.members) do local friendsFrame = CreateFrame("Frame")
if friends[member] == nil and member ~= UnitName("player") then friendsFrame:RegisterEvent("FRIENDLIST_UPDATE")
if Heimdall_Data.config.network.debug then friendsFrame:SetScript("OnEvent", function(self, event, ...) end)
print(string.format("[%s] Adding friend %s", ModuleName, member))
end
AddFriend(member)
end
end
friends[UnitName("player")] = true
shared.networkNodes = {} local function NetworkTick()
if false then shared.networkNodes[#shared.networkNodes + 1] = UnitName("player") end if Heimdall_Data.config.network.debug then print("Network module is updating.") end
for _, player in ipairs(Heimdall_Data.config.network.members) do ShowFriends()
if friends[player] then updatePending = true
shared.networkNodes[#shared.networkNodes + 1] = player C_Timer.After(1, function()
if Heimdall_Data.config.network.debug then if updatePending then FriendListUpdate() end
print(string.format("[%s] Adding network node %s", ModuleName, player)) end)
end shared.network.ticker = C_Timer.NewTimer(Heimdall_Data.config.network.updateInterval, NetworkTick, 1)
end
end end
if Heimdall_Data.config.network.debug then
print(string.format("[%s] Network nodes:", ModuleName))
shared.dumpTable(shared.networkNodes)
end
end
local friendsFrame = CreateFrame("Frame") NetworkTick()
friendsFrame:RegisterEvent("FRIENDLIST_UPDATE") print(string.format("[%s] Module initialized", ModuleName))
friendsFrame:SetScript("OnEvent", function(self, event, ...) end,
end) }
local function NetworkTick()
if Heimdall_Data.config.network.debug then
print("Network module is updating.")
end
ShowFriends()
updatePending = true
C_Timer.After(1, function()
if updatePending then FriendListUpdate() end
end)
shared.network.ticker = C_Timer.NewTimer(Heimdall_Data.config.network.updateInterval, NetworkTick, 1)
end
NetworkTick()
print("[Heimdall] Network module loaded")
end

View File

@@ -1,178 +1,204 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "NetworkMessenger" local ModuleName = "NetworkMessenger"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallNetworkMessengerConfig
shared.NetworkMessenger = {} ---@field enabled boolean
function shared.NetworkMessenger.Init() ---@field debug boolean
RegisterAddonMessagePrefix(Heimdall_Data.config.addonPrefix) ---@field interval number
if not shared.networkMessenger then shared.networkMessenger = {} end ---@class HeimdallNetworkMessengerData
if not shared.networkMessenger.queue then shared.networkMessenger.queue = {} end ---@field queue table<string, Message>
if not shared.networkMessenger.ticker then ---@field ticker Timer?
local function DoMessage()
--if Heimdall_Data.config.networkMessenger.debug then
-- print(string.format("[%s] Processing network message queue", ModuleName))
--end
if not Heimdall_Data.config.networkMessenger.enabled then
--if Heimdall_Data.config.networkMessenger.debug then
-- print(string.format("[%s] Module disabled, skipping network message processing", ModuleName))
--end
return
end
---@type Message
local message = shared.networkMessenger.queue[1]
if not message then
--if Heimdall_Data.config.networkMessenger.debug then
-- print(string.format("[%s] Network message queue empty", ModuleName))
--end
return
end
if not message.message or message.message == "" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Invalid network message: empty content", ModuleName))
end
return
end
if not message.channel or message.channel == "" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Invalid network message: no channel specified", ModuleName))
end
return
end
table.remove(shared.networkMessenger.queue, 1) ---@class NetworkMessenger
if not message.message or message.message == "" then shared.NetworkMessenger = {
if Heimdall_Data.config.networkMessenger.debug then ---@param message Message
print(string.format("[%s] Skipping empty network message", ModuleName)) Enqueue = function(message) table.insert(shared.networkMessenger.queue, message) end,
Init = function()
RegisterAddonMessagePrefix(Heimdall_Data.config.addonPrefix)
shared.networkMessenger = {
queue = ReactiveValue.new({}),
}
if not shared.networkMessenger.ticker then
local function DoMessage()
--if Heimdall_Data.config.networkMessenger.debug then
-- print(string.format("[%s] Processing network message queue", ModuleName))
--end
if not Heimdall_Data.config.networkMessenger.enabled then
--if Heimdall_Data.config.networkMessenger.debug then
-- print(string.format("[%s] Module disabled, skipping network message processing", ModuleName))
--end
return
end end
return ---@type Message
end local message = shared.networkMessenger.queue[1]
if not message.channel or message.channel == "" then if not message then
if Heimdall_Data.config.networkMessenger.debug then --if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Skipping network message with no channel", ModuleName)) -- print(string.format("[%s] Network message queue empty", ModuleName))
--end
return
end end
return if not message.message or message.message == "" then
end if Heimdall_Data.config.networkMessenger.debug then
if not message.data or message.data == "" then print(string.format("[%s] Invalid network message: empty content", ModuleName))
if Heimdall_Data.config.networkMessenger.debug then end
print(string.format("[%s] Skipping network message with no data", ModuleName)) return
end end
return if not message.channel or message.channel == "" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Invalid network message: no channel specified", ModuleName))
end
return
end
table.remove(shared.networkMessenger.queue, 1)
if not message.message or message.message == "" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Skipping empty network message", ModuleName))
end
return
end
if not message.channel or message.channel == "" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Skipping network message with no channel", ModuleName))
end
return
end
if not message.data or message.data == "" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Skipping network message with no data", ModuleName))
end
return
end
if Heimdall_Data.config.networkMessenger.debug then
print(
string.format(
"[%s] Sending network message: '%s' to %s:%s",
ModuleName,
message.message,
message.channel,
message.data
)
)
end
local payload = string.format("dmessage|%s|%s|%s", message.message, message.channel, message.data)
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Payload: %s", ModuleName, payload))
end
if not shared.networkNodes or #shared.networkNodes == 0 then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] No network nodes found, wtf????", ModuleName))
end
return
end
local target = shared.networkNodes[1]
SendAddonMessage(Heimdall_Data.config.addonPrefix, payload, "WHISPER", target)
end end
local function Tick()
--if Heimdall_Data.config.networkMessenger.debug then
-- local queueSize = #shared.networkMessenger.queue
-- print(string.format("[%s] Queue check - Network messages pending: %d", ModuleName, queueSize))
--end
DoMessage()
shared.networkMessenger.ticker =
C_Timer.NewTimer(Heimdall_Data.config.networkMessenger.interval, Tick, 1)
end
Tick()
end
-- If we are the leader then we delegate messages (dmessage)
-- If we get a "message" command from leader then we send the message
local nextIdx = 1
local addonMsgFrame = CreateFrame("Frame")
addonMsgFrame:RegisterEvent("CHAT_MSG_ADDON")
addonMsgFrame:SetScript("OnEvent", function(self, event, prefix, message, channel, source)
if not Heimdall_Data.config.networkMessenger.enabled then return end
if prefix ~= Heimdall_Data.config.addonPrefix then return end
source = string.match(source, "[^%-]+")
if Heimdall_Data.config.networkMessenger.debug then if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Sending network message: '%s' to %s:%s", ModuleName, message.message, print(string.format("[%s] Received message from %s: %s", ModuleName, source, message))
message.channel,
message.data))
end end
local payload = string.format("dmessage|%s|%s|%s", message.message, message.channel, message.data) if #shared.networkNodes == 0 then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Payload: %s", ModuleName, payload))
end
if not shared.networkNodes or #shared.networkNodes == 0 then
if Heimdall_Data.config.networkMessenger.debug then if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] No network nodes found, wtf????", ModuleName)) print(string.format("[%s] No network nodes found, wtf????", ModuleName))
end end
return return
end end
local target = shared.networkNodes[1]
SendAddonMessage(Heimdall_Data.config.addonPrefix, payload, "WHISPER", target) -- There should always be at least one network node ergo should always exist a leader
end -- Because the us, the player, is also a node
local function Tick() --local networkLeader = shared.networkNodes[1]
--if Heimdall_Data.config.networkMessenger.debug then --if source ~= networkLeader then
-- local queueSize = #shared.networkMessenger.queue -- if Heimdall_Data.config.networkMessenger.debug then
-- print(string.format("[%s] Queue check - Network messages pending: %d", ModuleName, queueSize)) -- print(string.format("[%s] Message from %s is not from the network leader (%s)", ModuleName, source,
-- networkLeader))
-- end
-- return
--end --end
DoMessage()
shared.networkMessenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.networkMessenger.interval, Tick, 1)
end
Tick()
end
-- If we are the leader then we delegate messages (dmessage) local parts = shared.Split(message, "|")
-- If we get a "message" command from leader then we send the message
local nextIdx = 1
local addonMsgFrame = CreateFrame("Frame")
addonMsgFrame:RegisterEvent("CHAT_MSG_ADDON")
addonMsgFrame:SetScript("OnEvent", function(self, event, prefix, message, channel, source)
if not Heimdall_Data.config.networkMessenger.enabled then return end
if prefix ~= Heimdall_Data.config.addonPrefix then return end
source = string.match(source, "[^%-]+")
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Received message from %s: %s", ModuleName, source, message))
end
if #shared.networkNodes == 0 then
if Heimdall_Data.config.networkMessenger.debug then if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] No network nodes found, wtf????", ModuleName)) print(string.format("[%s] Received message parts:", ModuleName))
shared.dump(parts)
end end
return local command = strtrim(parts[1])
end if command == "message" then
local content = strtrim(tostring(parts[2]))
local targetchannel = strtrim(tostring(parts[3]))
local target = strtrim(tostring(parts[4]))
if Heimdall_Data.config.networkMessenger.debug then
print(
string.format(
"[%s] Received message command: %s %s %s",
ModuleName,
content,
targetchannel,
target
)
)
end
---@type Message
local msg = {
channel = targetchannel,
message = content,
data = target,
}
table.insert(shared.messenger.queue, msg)
elseif command == "dmessage" then
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Received dmessage command", ModuleName))
end
parts[1] = "message"
local content = table.concat(parts, "|")
-- There should always be at least one network node ergo should always exist a leader if nextIdx > #shared.networkNodes then nextIdx = 1 end
-- Because the us, the player, is also a node local recipient = shared.networkNodes[nextIdx]
--local networkLeader = shared.networkNodes[1] nextIdx = nextIdx + 1
--if source ~= networkLeader then if Heimdall_Data.config.networkMessenger.debug then
-- if Heimdall_Data.config.networkMessenger.debug then print(string.format("[%s] Sending message %s to %s", ModuleName, content, recipient))
-- print(string.format("[%s] Message from %s is not from the network leader (%s)", ModuleName, source, end
-- networkLeader)) SendAddonMessage(Heimdall_Data.config.addonPrefix, content, "WHISPER", recipient)
-- end
-- return
--end
local parts = shared.Split(message, "|")
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Received message parts:", ModuleName))
shared.dumpTable(parts)
end
local command = strtrim(parts[1])
if command == "message" then
local message = strtrim(tostring(parts[2]))
local channel = strtrim(tostring(parts[3]))
local target = strtrim(tostring(parts[4]))
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Received message command: %s %s %s", ModuleName, message, channel, target))
end end
---@type Message end)
local msg = {
channel = channel, --/run Heimdall_Data.Test()
message = message, Heimdall_Data.Test = function()
data = target local testmsg = {
channel = "W",
message = "Hi, mom!",
data = "Secundus",
} }
table.insert(shared.messenger.queue, msg) for i = 1, 36 do
elseif command == "dmessage" then table.insert(shared.networkMessenger.queue, testmsg)
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Received dmessage command", ModuleName))
end end
parts[1] = "message"
local message = table.concat(parts, "|")
if nextIdx > #shared.networkNodes then
nextIdx = 1
end
local recipient = shared.networkNodes[nextIdx]
nextIdx = nextIdx + 1
if Heimdall_Data.config.networkMessenger.debug then
print(string.format("[%s] Sending message %s to %s", ModuleName, message, recipient))
end
SendAddonMessage(Heimdall_Data.config.addonPrefix, message, "WHISPER", recipient)
end end
end)
--/run Heimdall_Data.Test() print(string.format("[%s] Module initialized", ModuleName))
Heimdall_Data.Test = function() end,
local testmsg = { }
channel = "W",
message = "Hi, mom!",
data = "Secundus"
}
for i = 1, 36 do
table.insert(shared.networkMessenger.queue, testmsg)
end
end
print("[Heimdall] NetworkMessenger module loaded")
end

View File

@@ -1,301 +1,307 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Noter" local ModuleName = "Noter"
---@class HeimdallNoterConfig
---@field enabled boolean
---@field debug boolean
---@field channels string[]
---@field lastNotes number
---@class Note ---@class Note
---@field source string ---@field source string
---@field for string ---@field for string
---@field date string ---@field date string
---@field note string ---@field note string
---@diagnostic disable-next-line: missing-fields ---@class Noter
shared.Noter = {} shared.Noter = {
function shared.Noter.Init() Init = function()
---Hopefully this will not be necessary -- ---Hopefully this will not be necessary
---@param text string -- ---@param text string
---@param size number -- ---@param size number
---@return string[] -- ---@return string[]
local function Partition(text, size) -- local function Partition(text, size)
local words = {} -- local words = {}
for word in text:gmatch("[^,]+") do -- for word in text:gmatch("[^,]+") do
words[#words + 1] = word -- words[#words + 1] = word
end -- end
local ret = {} -- local ret = {}
local currentChunk = "" -- local currentChunk = ""
for _, word in ipairs(words) do -- for _, word in ipairs(words) do
if #currentChunk + #word + 1 <= size then -- if #currentChunk + #word + 1 <= size then
currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word) -- currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word)
else -- else
if #currentChunk > 0 then -- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
ret[#ret + 1] = currentChunk -- currentChunk = word
end -- end
currentChunk = word -- end
-- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
-- return ret
-- end
---@param array any[]
---@return any[]
local function Compact(array)
local compacted = {}
for _, v in pairs(array) do
compacted[#compacted + 1] = v
end end
return compacted
end end
if #currentChunk > 0 then ---@param name string
ret[#ret + 1] = currentChunk ---@param args string[]
end local function DeleteNotes(name, args)
return ret
end
---@param array any[]
---@return any[]
local function Compact(array)
local compacted = {}
for _, v in pairs(array) do
compacted[#compacted + 1] = v
end
return compacted
end
---@param name string
---@param args string[]
local function DeleteNotes(name, args)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Delete note command received for: %s", ModuleName, name))
end
local range = args[4]
if range then
if Heimdall_Data.config.noter.debug then if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Range received for delete note: %s", ModuleName, range)) print(string.format("[%s] Delete note command received for: %s", ModuleName, name))
end end
local indices = shared.Split(range, "..") local range = args[4]
if Heimdall_Data.config.noter.debug then if range then
print(string.format("[%s] Indices for range deletion: %s", ModuleName,
table.concat(indices, ", ")))
shared.dumpTable(indices)
end
local start = tonumber(indices[1])
local finish = tonumber(indices[2])
if not start then
if Heimdall_Data.config.noter.debug then if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Invalid start range for delete note: %s", ModuleName, print(string.format("[%s] Range received for delete note: %s", ModuleName, range))
tostring(start))) end
local indices = shared.Split(range, "..")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Indices for range deletion: %s", ModuleName, table.concat(indices, ", ")))
shared.dump(indices)
end
local start = tonumber(indices[1])
local finish = tonumber(indices[2])
if not start then
if Heimdall_Data.config.noter.debug then
print(
string.format("[%s] Invalid start range for delete note: %s", ModuleName, tostring(start))
)
end
return
end
if not finish then finish = start end
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Deleting note range %s to %s for: %s", ModuleName, start, finish, name))
end
-- Here, because we are deleting random notes, we lose the "iterative" index property
-- Ie it's not longer 1..100, it might be 1..47, 50, 68..100
-- Which means that we cannot use ipairs, bad!
for i = start, finish do
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if not Heimdall_Data.config.notes[name][i] then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note at index %s does not exist", ModuleName, i))
end
else
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Deleting note %s at index %s", ModuleName, name, i))
shared.dump(Heimdall_Data.config.notes[name][i])
end
Heimdall_Data.config.notes[name][i] = nil
end
end
Heimdall_Data.config.notes[name] = Compact(Heimdall_Data.config.notes[name])
end
end
---@param channel string
---@param index number
---@param note Note
local function PrintNote(channel, index, note)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing note at index %d for: %s", ModuleName, index, note.source))
print(string.format("[%s] [%s][%d] %s: %s", ModuleName, note.source, index, note.date, note.note))
end
---@type Message
local msg = {
channel = "C",
data = channel,
message = string.format("[%s][%d] %s: %s", note.source, index, note.date, note.note),
}
if Heimdall_Data.config.networkMessenger.enabled then
shared.NetworkMessenger.Enqueue(msg)
elseif Heimdall_Data.config.messenger.enabled then
shared.Messenger.Enqueue(msg)
end
end
---@param name string
---@param args string[]
local function PrintNotes(channel, name, args)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Print note command received for: %s", ModuleName, name))
end
local range = args[3]
if not range then
if Heimdall_Data.config.noter.debug then
print(
string.format(
"[%s] No range specified for print note, defaulting to last %d notes",
ModuleName,
Heimdall_Data.config.noter.lastNotes
)
)
end
local notes = Heimdall_Data.config.notes[name] or {}
local start = math.max(1, #notes - Heimdall_Data.config.noter.lastNotes + 1)
local finish = #notes
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing notes from %d to %d for: %s", ModuleName, start, finish, name))
end
for i = start, finish do
PrintNote(channel, i, notes[i])
end
return
end
if range then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Range received for print note: %s", ModuleName, range))
end
local indices = shared.Split(range, "..")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Indices for range printing: %s", ModuleName, table.concat(indices, ", ")))
shared.dump(indices)
end
local start = tonumber(indices[1])
local finish = tonumber(indices[2])
if not start then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Invalid start range for print note: %s", ModuleName, tostring(start)))
end
return
end
if not finish then finish = start end
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing note range %s to %s for: %s", ModuleName, start, finish, name))
end
for i = start, finish do
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if not Heimdall_Data.config.notes[name][i] then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note at index %s does not exist", ModuleName, i))
end
else
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing note %s at index %s", ModuleName, name, i))
shared.dump(Heimdall_Data.config.notes[name][i])
end
PrintNote(channel, i, Heimdall_Data.config.notes[name][i])
end
end
end
end
---@param name string
---@param sender string
---@param args string[]
local function AddNote(name, sender, args)
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender))
shared.dump(args)
end
local msgparts = {}
for i = 3, #args do
msgparts[#msgparts + 1] = args[i]
end
local msg = table.concat(msgparts, " ")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender))
print(string.format("[%s] Note: %s", ModuleName, msg))
end
local note = {
source = sender,
date = date("%Y-%m-%dT%H:%M:%S"),
note = msg,
}
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note", ModuleName))
shared.dump(note)
end
table.insert(Heimdall_Data.config.notes[name], note)
end
-- Here's the plan:
-- Implement a "note" command, that will do everything
-- Saying "note <name> <note>" will add a note to the list for the character
-- Saying "note <name>" will list last N notes
-- Saying "note <name> i" will list the i-th note
-- Saying "note <name> i..j" will list notes from i to j
-- Saying "note <name> delete i" will delete the i-th note
-- Saying "note <name> delete i..j" will delete notes from i to j
local noterChannelFrame = CreateFrame("Frame")
noterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
noterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Event received", ModuleName))
-- shared.dump(Heimdall_Data.config.noter)
--end
if not Heimdall_Data.config.noter.enabled then
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Module disabled, ignoring event", ModuleName))
--end
return
end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.noter.channels) do
if channelname == channel then
ok = true
break
end
end
if not ok then
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Channel %s does not match the master channel %s", ModuleName, channelname, Heimdall_Data.config.noter.masterChannel))
--end
return
end
sender = string.match(sender, "^[^-]+")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Message from: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.noter)
end
if not msg or msg == "" then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Empty message, ignoring", ModuleName))
end end
return return
end end
if not finish then finish = start end local args = { strsplit(" ", msg) }
if Heimdall_Data.config.noter.debug then if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Deleting note range %s to %s for: %s", ModuleName, start, finish, name)) print(string.format("[%s] Arguments received: %s", ModuleName, table.concat(args, ", ")))
shared.dump(args)
end end
local command = args[1]
-- Here, because we are deleting random notes, we lose the "iterative" index property if command == "note" then
-- Ie it's not longer 1..100, it might be 1..47, 50, 68..100 local name = strtrim(string.lower(args[2] or ""))
-- Which means that we cannot use ipairs, bad!
for i = start, finish do
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if not Heimdall_Data.config.notes[name][i] then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note at index %s does not exist", ModuleName, i))
end
else
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Deleting note %s at index %s", ModuleName, name, i))
shared.dumpTable(Heimdall_Data.config.notes[name][i])
end
Heimdall_Data.config.notes[name][i] = nil
end
end
Heimdall_Data.config.notes[name] = Compact(Heimdall_Data.config.notes[name])
end
end
---@param channel string
---@param index number
---@param note Note
local function PrintNote(channel, index, note)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing note at index %d for: %s", ModuleName, index, note.source))
print(string.format("[%s] [%s][%d] %s: %s", ModuleName, note.source, index, note.date, note.note))
end
---@type Message
local msg = {
channel = "C",
data = channel,
message = string.format("[%s][%d] %s: %s", note.source, index, note.date, note.note)
}
--table.insert(shared.messenger.queue, msg)
table.insert(shared.networkMessenger.queue, msg)
end
---@param name string
---@param args string[]
local function PrintNotes(channel, name, args)
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Print note command received for: %s", ModuleName, name))
end
local range = args[3]
if not range then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] No range specified for print note, defaulting to last %d notes", ModuleName,
Heimdall_Data.config.noter.lastNotes))
end
local notes = Heimdall_Data.config.notes[name] or {}
local start = math.max(1, #notes - Heimdall_Data.config.noter.lastNotes + 1)
local finish = #notes
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Printing notes from %d to %d for: %s", ModuleName, start, finish, name))
end
for i = start, finish do
PrintNote(channel, i, notes[i])
end
return
end
if range then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Range received for print note: %s", ModuleName, range))
end
local indices = shared.Split(range, "..")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Indices for range printing: %s", ModuleName,
table.concat(indices, ", ")))
shared.dumpTable(indices)
end
local start = tonumber(indices[1])
local finish = tonumber(indices[2])
if not start then
if Heimdall_Data.config.noter.debug then if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Invalid start range for print note: %s", ModuleName, print(string.format("[%s] Note command received for: %s", ModuleName, name))
tostring(start)))
end end
return local note = strtrim(args[3] or "")
end if Heimdall_Data.config.noter.debug then print(string.format("[%s] Note: %s", ModuleName, note)) end
if note == "delete" then
if not finish then finish = start end DeleteNotes(name, args)
if Heimdall_Data.config.noter.debug then elseif string.find(note, "^[%d%.]*$") then
print(string.format("[%s] Printing note range %s to %s for: %s", ModuleName, start, finish, name)) PrintNotes(channelname, name, args)
end
for i = start, finish do
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if not Heimdall_Data.config.notes[name][i] then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note at index %s does not exist", ModuleName, i))
end
else else
if Heimdall_Data.config.noter.debug then AddNote(name, sender, args)
print(string.format("[%s] Printing note %s at index %s", ModuleName, name, i))
shared.dumpTable(Heimdall_Data.config.notes[name][i])
end
PrintNote(channel, i, Heimdall_Data.config.notes[name][i])
end end
end end
end end)
end
---@param name string print(string.format("[%s] Module initialized", ModuleName))
---@param sender string end,
---@param args string[] }
local function AddNote(name, sender, args)
if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender))
shared.dumpTable(args)
end
local msgparts = {}
for i = 3, #args do
msgparts[#msgparts + 1] = args[i]
end
local msg = table.concat(msgparts, " ")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender))
print(string.format("[%s] Note: %s", ModuleName, msg))
end
local note = {
source = sender,
date = date("%Y-%m-%dT%H:%M:%S"),
note = msg
}
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Adding note", ModuleName))
shared.dumpTable(note)
end
table.insert(Heimdall_Data.config.notes[name], note)
end
-- Here's the plan:
-- Implement a "note" command, that will do everything
-- Saying "note <name> <note>" will add a note to the list for the character
-- Saying "note <name>" will list last N notes
-- Saying "note <name> i" will list the i-th note
-- Saying "note <name> i..j" will list notes from i to j
-- Saying "note <name> delete i" will delete the i-th note
-- Saying "note <name> delete i..j" will delete notes from i to j
local noterChannelFrame = CreateFrame("Frame")
noterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
noterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Event received", ModuleName))
-- shared.dumpTable(Heimdall_Data.config.noter)
--end
if not Heimdall_Data.config.noter.enabled then
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Module disabled, ignoring event", ModuleName))
--end
return
end
local channelId = select(6, ...)
local _, channelname = GetChannelName(channelId)
local ok = false
for _, channel in pairs(Heimdall_Data.config.noter.channels) do
if channelname == channel then
ok = true
break
end
end
if not ok then
--if Heimdall_Data.config.noter.debug then
-- print(string.format("[%s] Channel %s does not match the master channel %s", ModuleName, channelname, Heimdall_Data.config.noter.masterChannel))
--end
return
end
sender = string.match(sender, "^[^-]+")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Message from: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.noter)
end
if not msg or msg == "" then
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Empty message, ignoring", ModuleName))
end
return
end
local args = { strsplit(" ", msg) }
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Arguments received: %s", ModuleName, table.concat(args, ", ")))
shared.dumpTable(args)
end
local command = args[1]
if command == "note" then
local name = strtrim(string.lower(args[2] or ""))
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note command received for: %s", ModuleName, name))
end
local note = strtrim(args[3] or "")
if Heimdall_Data.config.noter.debug then
print(string.format("[%s] Note: %s", ModuleName, note))
end
if note == "delete" then
DeleteNotes(name, args)
elseif string.find(note, "^[%d%.]*$") then
PrintNotes(channelname, name, args)
else
AddNote(name, sender, args)
end
end
end)
print("[Heimdall] Commander module loaded")
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,90 +1,92 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Sniffer" local ModuleName = "Sniffer"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallSnifferConfig
shared.Sniffer = {} ---@field enabled boolean
function shared.Sniffer.Init() ---@field debug boolean
if Heimdall_Data.config.sniffer.debug then ---@field channels string[]
print(string.format("[%s] Module initializing", ModuleName)) ---@field throttle number -- throttleTime in the original code, matching config name now
end ---@field zoneOverride string?
local smellThrottle = {} ---@field stinky boolean
local SmellStinky = function(stinky)
if Heimdall_Data.config.sniffer.debug then
print(string.format("%s: SmellStinky", ModuleName))
shared.dumpTable(Heimdall_Data.config.sniffer)
end
if not Heimdall_Data.config.sniffer.enabled then return end
if Heimdall_Data.config.sniffer.stinky and
not shared.IsStinky(stinky) then
if Heimdall_Data.config.sniffer.debug then
print(string.format("%s: Stinky not found in config", ModuleName))
end
return
end
if smellThrottle[stinky] and GetTime() - smellThrottle[stinky] < Heimdall_Data.config.sniffer.throttleTime then
if Heimdall_Data.config.sniffer.debug then
print(string.format("%s: Throttled", ModuleName))
end
return
end
smellThrottle[stinky] = GetTime()
for _, channel in pairs(Heimdall_Data.config.sniffer.channels) do ---@class Sniffer
local locale = shared.GetLocaleForChannel(channel) shared.Sniffer = {
local text = string.format(shared._L("snifferStinky", locale), stinky) Init = function()
---@type Message if Heimdall_Data.config.sniffer.debug then print(string.format("[%s] Module initializing", ModuleName)) end
local msg = { local smellThrottle = {}
channel = "C", local SmellStinky = function(stinky)
data = channel,
message = text,
}
if Heimdall_Data.config.sniffer.debug then if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Queuing sniffer message", ModuleName)) print(string.format("%s: SmellStinky", ModuleName))
shared.dumpTable(msg) shared.dump(Heimdall_Data.config.sniffer)
end end
table.insert(shared.messenger.queue, msg) if not Heimdall_Data.config.sniffer.enabled then return end
end if Heimdall_Data.config.sniffer.stinky and not shared.IsStinky(stinky) then
end if Heimdall_Data.config.sniffer.debug then
print(string.format("%s: Stinky not found in config", ModuleName))
end
return
end
if smellThrottle[stinky] and GetTime() - smellThrottle[stinky] < Heimdall_Data.config.sniffer.throttle then
if Heimdall_Data.config.sniffer.debug then print(string.format("%s: Throttled", ModuleName)) end
return
end
smellThrottle[stinky] = GetTime()
local cleuFrame = CreateFrame("Frame") for _, channel in pairs(Heimdall_Data.config.sniffer.channels) do
cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") local locale = shared.GetLocaleForChannel(channel)
cleuFrame:SetScript("OnEvent", function(self, event, ...) local text = string.format(shared._L("snifferStinky", locale), stinky)
if Heimdall_Data.config.sniffer.debug then ---@type Message
print(string.format("[%s] Received event: %s", ModuleName, event)) local msg = {
end channel = "C",
if not Heimdall_Data.config.sniffer.enabled then data = channel,
if Heimdall_Data.config.sniffer.debug then message = text,
print(string.format("[%s] Module disabled, ignoring event", ModuleName)) }
if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Queuing sniffer message", ModuleName))
shared.dump(msg)
end
table.insert(shared.messenger.queue, msg)
end end
return
end end
local source, err = CLEUParser.GetSourceName(...)
if Heimdall_Data.config.sniffer.debug then local cleuFrame = CreateFrame("Frame")
print(string.format("[%s] Processing source: %s", ModuleName, source)) cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
end cleuFrame:SetScript("OnEvent", function(self, event, ...)
if err then
if Heimdall_Data.config.sniffer.debug then if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Error parsing source: %s", ModuleName, err)) print(string.format("[%s] Received event: %s", ModuleName, event))
end end
return if not Heimdall_Data.config.sniffer.enabled then
end if Heimdall_Data.config.sniffer.debug then
SmellStinky(source) print(string.format("[%s] Module disabled, ignoring event", ModuleName))
local destination, err = CLEUParser.GetDestName(...) end
if Heimdall_Data.config.sniffer.debug then return
print(string.format("[%s] Processing destination: %s", ModuleName, destination)) end
end local source, destination, err
if err then source, err = CLEUParser.GetSourceName(...)
if Heimdall_Data.config.sniffer.debug then if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Error parsing destination: %s", ModuleName, err)) print(string.format("[%s] Processing source: %s", ModuleName, source))
end end
return if err then
end if Heimdall_Data.config.sniffer.debug then
SmellStinky(destination) print(string.format("[%s] Error parsing source: %s", ModuleName, err))
end) end
if Heimdall_Data.config.sniffer.debug then return
end
SmellStinky(source)
destination, err = CLEUParser.GetDestName(...)
if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Processing destination: %s", ModuleName, destination))
end
if err then
if Heimdall_Data.config.sniffer.debug then
print(string.format("[%s] Error parsing destination: %s", ModuleName, err))
end
return
end
SmellStinky(destination)
end)
if Heimdall_Data.config.sniffer.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName)) print(string.format("[%s] Module initialized", ModuleName))
end end,
print("[Heimdall] Sniffer loaded") }
end

View File

@@ -1,220 +1,238 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "Spotter" local ModuleName = "Spotter"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallSpotterConfig
shared.Spotter = {} ---@field enabled boolean
function shared.Spotter.Init() ---@field debug boolean
local function FormatHP(hp) ---@field everyone boolean
if hp > 1e9 then ---@field hostile boolean
return string.format("%.1fB", hp / 1e9) ---@field alliance boolean
elseif hp > 1e6 then ---@field stinky boolean
return string.format("%.1fM", hp / 1e6) ---@field channels string[]
elseif hp > 1e3 then ---@field zoneOverride string?
return string.format("%.1fK", hp / 1e3) ---@field throttleTime number
else
return hp
end
end
---@type table<string, number> ---@class Spotter
local throttleTable = {} shared.Spotter = {
Init = function()
---@param unit string local function FormatHP(hp)
---@param name string if hp > 1e9 then
---@param faction string return string.format("%.1fB", hp / 1e9)
---@param hostile boolean elseif hp > 1e6 then
---@return boolean return string.format("%.1fM", hp / 1e6)
---@return string? error elseif hp > 1e3 then
local function ShouldNotify(unit, name, faction, hostile) return string.format("%.1fK", hp / 1e3)
if Heimdall_Data.config.spotter.debug then else
print(string.format("[%s] Checking notification criteria for %s (%s)", ModuleName, name, faction)) return hp
end
if Heimdall_Data.config.agents[name] then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Skipping agent: %s", ModuleName, name))
end end
return false
end end
if Heimdall_Data.config.spotter.stinky then ---@type table<string, number>
if shared.IsStinky(name) then local throttleTable = {}
---@param unit string
---@param name string
---@param faction string
---@param hostile boolean
---@return boolean
---@return string? error
local function ShouldNotify(unit, name, faction, hostile)
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Checking notification criteria for %s (%s)", ModuleName, name, faction))
end
if shared.AgentTracker.IsAgent(name) then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Notifying - Found stinky: %s", ModuleName, name)) print(string.format("[%s] Skipping agent: %s", ModuleName, name))
end end
return true return false
end end
if Heimdall_Data.config.spotter.stinky then
if shared.IsStinky(name) then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Notifying - Found stinky: %s", ModuleName, name))
end
return true
end
end
if Heimdall_Data.config.spotter.alliance then
if faction == "Alliance" then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Notifying - Found Alliance player: %s", ModuleName, name))
end
return true
end
end
if Heimdall_Data.config.spotter.hostile then
if hostile then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Notifying - Found hostile player: %s", ModuleName, name))
end
return true
end
end
if Heimdall_Data.config.spotter.debug then
print(
string.format(
"[%s] Using everyone setting: %s",
ModuleName,
tostring(Heimdall_Data.config.spotter.everyone)
)
)
end
return Heimdall_Data.config.spotter.everyone
end end
if Heimdall_Data.config.spotter.alliance then ---@param unit string
if faction == "Alliance" then ---@return string?
local function NotifySpotted(unit)
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Processing spotted unit: %s", ModuleName, unit))
end
if not unit then return string.format("Could not find unit %s", tostring(unit)) end
if not UnitIsPlayer(unit) then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Notifying - Found Alliance player: %s", ModuleName, name)) print(string.format("[%s] Ignoring non-player unit: %s", ModuleName, unit))
end end
return true return nil
end end
end
if Heimdall_Data.config.spotter.hostile then local name = UnitName(unit)
if hostile then if not name then return string.format("Could not find name for unit %s", tostring(unit)) end
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Processing player: %s", ModuleName, name))
end
local time = GetTime()
if throttleTable[name] and time - throttleTable[name] < Heimdall_Data.config.spotter.throttleTime then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Notifying - Found hostile player: %s", ModuleName, name)) local remainingTime = Heimdall_Data.config.spotter.throttleTime - (time - throttleTable[name])
print(
string.format("[%s] Player %s throttled for %.1f more seconds", ModuleName, name, remainingTime)
)
end end
return true return string.format("Throttled %s", tostring(name))
end end
end throttleTable[name] = time
if Heimdall_Data.config.spotter.debug then local race = UnitRace(unit)
print(string.format("[%s] Using everyone setting: %s", ModuleName, if not race then return string.format("Could not find race for unit %s", tostring(unit)) end
tostring(Heimdall_Data.config.spotter.everyone))) local faction = shared.raceMap[race]
end if not faction then return string.format("Could not find faction for race %s", tostring(race)) end
return Heimdall_Data.config.spotter.everyone
end
---@param unit string
---@return string?
local function NotifySpotted(unit)
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Processing spotted unit: %s", ModuleName, unit))
end
if not unit then return string.format("Could not find unit %s", tostring(unit)) end
if not UnitIsPlayer(unit) then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Ignoring non-player unit: %s", ModuleName, unit)) print(string.format("[%s] Player %s is %s (%s)", ModuleName, name, race, faction))
end end
return nil
end
local name = UnitName(unit) local hostile = UnitCanAttack("player", unit)
if not name then return string.format("Could not find name for unit %s", tostring(unit)) end
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Processing player: %s", ModuleName, name))
end
local time = GetTime()
if throttleTable[name] and time - throttleTable[name] < Heimdall_Data.config.spotter.throttleTime then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
local remainingTime = Heimdall_Data.config.spotter.throttleTime - (time - throttleTable[name]) print(string.format("[%s] Player %s is %s", ModuleName, name, hostile and "hostile" or "friendly"))
print(string.format("[%s] Player %s throttled for %.1f more seconds", ModuleName, name, remainingTime))
end end
return string.format("Throttled %s", tostring(name))
end
throttleTable[name] = time
local race = UnitRace(unit) local doNotify = ShouldNotify(unit, name, faction, hostile)
if not race then return string.format("Could not find race for unit %s", tostring(unit)) end if not doNotify then
local faction = shared.raceMap[race] if Heimdall_Data.config.spotter.debug then
if not faction then return string.format("Could not find faction for race %s", tostring(race)) end print(string.format("[%s] Skipping notification for %s", ModuleName, name))
if Heimdall_Data.config.spotter.debug then end
print(string.format("[%s] Player %s is %s (%s)", ModuleName, name, race, faction)) return string.format("Not notifying for %s", tostring(name))
end end
local hostile = UnitCanAttack("player", unit) local hp = UnitHealth(unit)
if Heimdall_Data.config.spotter.debug then if not hp then return string.format("Could not find hp for unit %s", tostring(unit)) end
print(string.format("[%s] Player %s is %s", ModuleName, name, hostile and "hostile" or "friendly")) local maxHp = UnitHealthMax(unit)
end if not maxHp then return string.format("Could not find maxHp for unit %s", tostring(unit)) end
local doNotify = ShouldNotify(unit, name, faction, hostile)
if not doNotify then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Skipping notification for %s", ModuleName, name)) print(string.format("[%s] Player %s health: %s/%s", ModuleName, name, FormatHP(hp), FormatHP(maxHp)))
end end
return string.format("Not notifying for %s", tostring(name))
end
local hp = UnitHealth(unit) local class = UnitClass(unit)
if not hp then return string.format("Could not find hp for unit %s", tostring(unit)) end if not class then return string.format("Could not find class for unit %s", tostring(unit)) end
local maxHp = UnitHealthMax(unit)
if not maxHp then return string.format("Could not find maxHp for unit %s", tostring(unit)) end
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Player %s health: %s/%s", ModuleName, name, FormatHP(hp), FormatHP(maxHp)))
end
local class = UnitClass(unit) local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown"
if not class then return string.format("Could not find class for unit %s", tostring(unit)) end if Heimdall_Data.config.spotter.zoneOverride then
zone = Heimdall_Data.config.spotter.zoneOverride or ""
subzone = ""
end
local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown" local x, y = GetPlayerMapPosition("player")
if Heimdall_Data.config.spotter.zoneOverride then
zone = Heimdall_Data.config.spotter.zoneOverride or ""
subzone = ""
end
local x, y = GetPlayerMapPosition("player")
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Player %s coordinates: %.2f, %.2f", ModuleName, name, x * 100, y * 100))
end
local pvpOn = UnitIsPVP(unit)
local stinky = shared.IsStinky(name) or false
SetMapToCurrentZone()
SetMapByID(GetCurrentMapAreaID())
local areaId = tostring(GetCurrentMapAreaID())
for _, channel in pairs(Heimdall_Data.config.spotter.channels) do
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Processing channel: %s", ModuleName, channel)) print(string.format("[%s] Player %s coordinates: %.2f, %.2f", ModuleName, name, x * 100, y * 100))
end end
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(shared._L("spotterSpotted", locale),
hostile and shared._L("hostile", locale) or shared._L("friendly", locale),
name,
shared._L(class, locale),
stinky and string.format("(%s)", "!!!!") or "",
shared._L(race, locale),
shared._L(faction, locale),
pvpOn and shared._L("pvpOn", locale) or shared._L("pvpOff", locale),
string.gsub(FormatHP(hp), "M", "kk"),
string.gsub(FormatHP(maxHp), "M", "kk"),
shared._L(zone, locale), shared._L(subzone, locale),
areaId,
x * 100, y * 100)
---@type Message local pvpOn = UnitIsPVP(unit)
local msg = { local stinky = shared.IsStinky(name) or false
channel = "C", SetMapToCurrentZone()
data = channel, SetMapByID(GetCurrentMapAreaID())
message = text local areaId = tostring(GetCurrentMapAreaID())
}
for _, channel in pairs(Heimdall_Data.config.spotter.channels) do
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Processing channel: %s", ModuleName, channel))
end
local locale = shared.GetLocaleForChannel(channel)
local text = string.format(
shared._L("spotterSpotted", locale),
hostile and shared._L("hostile", locale) or shared._L("friendly", locale),
name,
shared._L(class, locale),
stinky and string.format("(%s)", "!!!!") or "",
shared._L(race, locale),
shared._L(faction, locale),
pvpOn and shared._L("pvpOn", locale) or shared._L("pvpOff", locale),
string.gsub(FormatHP(hp), "M", "kk"),
string.gsub(FormatHP(maxHp), "M", "kk"),
shared._L(zone, locale),
shared._L(subzone, locale),
areaId,
x * 100,
y * 100
)
---@type Message
local msg = {
channel = "C",
data = channel,
message = text,
}
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Queuing spotter message", ModuleName))
shared.dump(msg)
end
table.insert(shared.messenger.queue, msg)
end
end
local frame = CreateFrame("Frame")
frame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
frame:RegisterEvent("UNIT_TARGET")
frame:SetScript("OnEvent", function(self, event, unit)
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Queuing spotter message", ModuleName)) print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target"))
shared.dumpTable(msg)
end end
table.insert(shared.messenger.queue, msg)
end
end
local frame = CreateFrame("Frame") if not Heimdall_Data.config.spotter.enabled then
frame:RegisterEvent("NAME_PLATE_UNIT_ADDED") if Heimdall_Data.config.spotter.debug then
frame:RegisterEvent("UNIT_TARGET") print(string.format("[%s] Module disabled, ignoring event", ModuleName))
frame:SetScript("OnEvent", function(self, event, unit) end
if Heimdall_Data.config.spotter.debug then return
print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target"))
end
if not Heimdall_Data.config.spotter.enabled then
if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Module disabled, ignoring event", ModuleName))
end end
return
end
if event == "UNIT_TARGET" then if event == "UNIT_TARGET" then unit = "target" end
unit = "target"
end
local err = NotifySpotted(unit) local err = NotifySpotted(unit)
if err then if err then
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then
print(string.format("[%s] Error processing unit %s: %s", ModuleName, unit, err)) print(string.format("[%s] Error processing unit %s: %s", ModuleName, unit, err))
end
end end
end end)
end)
if Heimdall_Data.config.spotter.debug then if Heimdall_Data.config.spotter.debug then print(string.format("[%s] Module initialized", ModuleName)) end
print(string.format("[%s] Module initialized", ModuleName)) print(string.format("[%s] Module initialized", ModuleName))
end end,
print("[Heimdall] Spotter loaded") }
end

View File

@@ -1,60 +1,83 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "StinkyCache" local ModuleName = "StinkyCache"
---@diagnostic disable-next-line: missing-fields ---@class HeimdallStinkyCacheConfig
shared.StinkyCache = {} ---@field enabled boolean
function shared.StinkyCache.Init() ---@field debug boolean
shared.stinkyCache = { ---@field commander string
stinkies = {}, ---@field ttl number
}
---@param name string ---@class HeimdallStinkyCacheData
local function AskCommander(name) ---@field stinkies table<string, {value: number, timestamp: number}>
if Heimdall_Data.config.stinkyCache.debug then
print(string.format("[%s] Asking commander %s about %s", ModuleName,
Heimdall_Data.config.stinkyCache.commander, name))
end
local messageParts = { "isstinky", name }
local message = table.concat(messageParts, "|")
SendAddonMessage(Heimdall_Data.config.addonPrefix,
message, "WHISPER",
Heimdall_Data.config.stinkyCache.commander)
return
end
local addonMessageFrame = CreateFrame("Frame") ---@class StinkyCache
addonMessageFrame:RegisterEvent("CHAT_MSG_ADDON") shared.StinkyCache = {
addonMessageFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) Init = function()
if sender == Heimdall_Data.config.stinkyCache.commander then shared.stinkyCache = {
stinkies = {},
}
---@param name string
local function AskCommander(name)
if Heimdall_Data.config.stinkyCache.debug then if Heimdall_Data.config.stinkyCache.debug then
print(string.format("[%s] Received stinky from commander %s: %s", ModuleName, print(
Heimdall_Data.config.stinkyCache.commander, msg)) string.format(
end "[%s] Asking commander %s about %s",
local name, value = {strsplit("|", msg)} ModuleName,
shared.stinkyCache.stinkies[name] = { value = value, timestamp = time() } Heimdall_Data.config.stinkyCache.commander,
else name
if Heimdall_Data.config.stinkyCache.debug then )
print(string.format("[%s] Received stinky from non-commander %s: %s", ModuleName, sender, msg)) )
end
local parts = {strsplit("|", msg)}
local command, name = parts[1], parts[2]
if parts[1] == "isstinky" then
local res = Heimdall_Data.config.stinkies[parts[2]]
end end
local messageParts = { "isstinky", name }
local message = table.concat(messageParts, "|")
SendAddonMessage(
Heimdall_Data.config.addonPrefix,
message,
"WHISPER",
Heimdall_Data.config.stinkyCache.commander
)
return
end end
end)
setmetatable(shared.stinkyCache.stinkies, { local addonMessageFrame = CreateFrame("Frame")
__index = function(self, key) addonMessageFrame:RegisterEvent("CHAT_MSG_ADDON")
local value = rawget(self, key) addonMessageFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
local now = GetTime() if sender == Heimdall_Data.config.stinkyCache.commander then
if value == nil or now - value.timestamp > Heimdall_Data.config.stinkyCache.ttl then if Heimdall_Data.config.stinkyCache.debug then
AskCommander(key) print(
string.format(
"[%s] Received stinky from commander %s: %s",
ModuleName,
Heimdall_Data.config.stinkyCache.commander,
msg
)
)
end
local parts = { strsplit("|", msg) }
local name, value = parts[1], parts[2]
shared.stinkyCache.stinkies[name] = { value = value, timestamp = time() }
else
if Heimdall_Data.config.stinkyCache.debug then
print(string.format("[%s] Received stinky from non-commander %s: %s", ModuleName, sender, msg))
end
local parts = { strsplit("|", msg) }
local command, name = parts[1], parts[2]
if parts[1] == "isstinky" then local res = Heimdall_Data.config.stinkies[parts[2]] end
end end
return rawget(self, key) end)
end
}) setmetatable(shared.stinkyCache.stinkies, {
print("[Heimdall] StinkyCache module loaded") __index = function(self, key)
end local value = rawget(self, key)
local now = GetTime()
if value == nil or now - value.timestamp > Heimdall_Data.config.stinkyCache.ttl then
AskCommander(key)
end
return rawget(self, key)
end,
})
print(string.format("[%s] Module initialized", ModuleName))
end,
}

View File

@@ -1,261 +1,364 @@
local addonname, shared = ... local _, shared = ...
---@cast shared HeimdallShared ---@cast shared HeimdallShared
---@cast addonname string
local ModuleName = "StinkyTracker" local ModuleName = "StinkyTracker"
---@diagnostic disable-next-line: missing-fields ---@class Stinky
shared.StinkyTracker = {} ---@field name string
function shared.StinkyTracker.Init() ---@field class string
shared.stinkyTracker = { ---@field seenAt number
stinkies = ReactiveValue.new({}) ---@field hostile boolean
}
local whoRegex = "([^ -/]+)-?%w*/(%w+)" ---@class HeimdallStinkyTrackerConfig
---@param msg string ---@field enabled boolean
---@return table<string, stinky> ---@field debug boolean
local function ParseWho(msg) ---@field ignoredTimeout number
---@field channels string[]
---@class StinkyTrackerData
---@field stinkies ReactiveValue<table<string, Stinky>>
---@field ignored ReactiveValue<table<string, number>>
---@class StinkyTracker
shared.StinkyTracker = {
---@param stinky Stinky
---@return boolean
Track = function(stinky)
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Parsing WHO message: '%s'", ModuleName, msg)) print(string.format("[%s] Request to track stinky: %s (%s)", ModuleName, stinky.name, stinky.class))
end end
local stinkies = {} local ignored = shared.stinkyTracker.ignored[stinky.name]
for name, class in string.gmatch(msg, whoRegex) do -- TODO: Add a config option for the ignored timeout
stinkies[name] = { if ignored and ignored > GetTime() - 60 then
if Heimdall_Data.config.stinkyTracker.debug then
print(
string.format(
"[%s] Stinky is ignored, not tracking: %s (%s)",
ModuleName,
stinky.name,
stinky.class
)
)
shared.dump(shared.stinkyTracker.ignored:get())
shared.dump(shared.stinkyTracker.stinkies:get())
end
return false
else
-- Timed out or was never ignored
shared.stinkyTracker.stinkies[stinky.name] = nil
end
shared.stinkyTracker.stinkies[stinky.name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Stinky is now tracked: %s (%s)", ModuleName, stinky.name, stinky.class))
shared.dump(shared.stinkyTracker.stinkies:get())
shared.dump(shared.stinkyTracker.ignored:get())
end
return true
end,
---@param name string
---@return nil
Ignore = function(name)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Request to ignore stinky: %s", ModuleName, name))
end
shared.stinkyTracker.ignored[name] = GetTime()
shared.stinkyTracker.stinkies[name] = nil
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Stinky is now ignored: %s", ModuleName, name))
shared.dump(shared.stinkyTracker.ignored:get())
shared.dump(shared.stinkyTracker.stinkies:get())
end
end,
---@param name string
---@return boolean
IsStinky = function(name)
if not shared.stinkyTracker.stinkies then return false end
if not shared.stinkyTracker.stinkies[name] then return false end
if shared.stinkyTracker.ignored[name] then return false end
return true
end,
---@param callback fun(stinkies: table<string, Stinky>)
---@return nil
OnChange = function(callback) shared.stinkyTracker.stinkies:onChange(callback) end,
---@param callback fun(name: string, stinky: Stinky)
---@return nil
ForEach = function(callback)
---@type table<string, Stinky>
local stinkies = shared.stinkyTracker.stinkies:get()
for name, stinky in pairs(stinkies) do
callback(name, stinky)
end
end,
Init = function()
shared.stinkyTracker = {
stinkies = ReactiveValue.new({}),
ignored = ReactiveValue.new({}),
}
local whoRegex = "([^ -/]+)-?%w*/(%w+)"
---@param msg string
---@return table<string, Stinky>
local function ParseWho(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Parsing WHO message: '%s'", ModuleName, msg))
end
local stinkies = {}
for name, class in string.gmatch(msg, whoRegex) do
stinkies[name] = {
name = name,
class = class,
seenAt = GetTime(),
hostile = true,
}
if Heimdall_Data.config.stinkyTracker.debug then
print(
string.format(
"[%s] Found hostile player: %s (%s) at %s",
ModuleName,
name,
class,
date("%H:%M:%S", time())
)
)
shared.dump(stinkies)
end
end
return stinkies
end
local seeRegex = "I see %((%w+)%) ([^ -/]+)-?%w*/(%w+)"
---@param msg string
---@return table<string, Stinky>
local function ParseSee(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Parsing SEE message: '%s'", ModuleName, msg))
end
local stinkies = {}
local aggression, name, class = string.match(msg, seeRegex)
if not name or not class then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Error: Invalid SEE message format", ModuleName))
end
return stinkies
end
local stinky = {
name = name, name = name,
class = class, class = class,
seenAt = GetTime(), seenAt = GetTime(),
hostile = true hostile = aggression == "hostile",
} }
stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found hostile player: %s (%s) at %s", ModuleName, name, class, print(
date("%H:%M:%S", time()))) string.format(
shared.dumpTable(stinkies) "[%s] Found stinky in SEE: %s (%s) - %s at %s",
end ModuleName,
end name,
return stinkies class,
end aggression,
date("%H:%M:%S", time())
local seeRegex = "I see %((%w+)%) ([^ -/]+)-?%w*/(%w+)" )
---@param msg string )
---@return table<string, stinky> shared.dump(stinkies)
local function ParseSee(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Parsing SEE message: '%s'", ModuleName, msg))
end
local stinkies = {}
local aggression, name, class = string.match(msg, seeRegex)
if not name or not class then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Error: Invalid SEE message format", ModuleName))
end end
return stinkies return stinkies
end end
local stinky = {
name = name,
class = class,
seenAt = GetTime(),
hostile = aggression == "hostile"
}
stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinky in SEE: %s (%s) - %s at %s", ModuleName, name, class, aggression,
date("%H:%M:%S", time())))
shared.dumpTable(stinkies)
end
return stinkies
end
local arrivedRegex = "([^ -/]+)-?%w*; c:([^;]+)" local arrivedRegex = "([^ -/]+)-?%w*; c:([^;]+)"
local arrivedRegexAlt = "([^ -/]+)-?%w*%(!!!!%); c:([^;]+)" local arrivedRegexAlt = "([^ -/]+)-?%w*%(!!!!%); c:([^;]+)"
---@param msg string ---@param msg string
---@return table<string, stinky> ---@return table<string, Stinky>
local function ParseArrived(msg) local function ParseArrived(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: Parsing arrived message: %s", ModuleName, msg))
end
local stinkies = {}
local name, class = string.match(msg, arrivedRegex)
if not name or not class then
name, class = string.match(msg, arrivedRegexAlt)
end
if not name or not class then
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: No valid stinky found in arrived message", ModuleName)) print(string.format("%s: Parsing arrived message: %s", ModuleName, msg))
end
local stinkies = {}
local name, class = string.match(msg, arrivedRegex)
if not name or not class then
name, class = string.match(msg, arrivedRegexAlt)
end
if not name or not class then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: No valid stinky found in arrived message", ModuleName))
end
return stinkies
end
local stinky = {
name = name,
class = class,
seenAt = GetTime(),
hostile = true,
}
stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: Found stinky in arrived: %s/%s", ModuleName, name, class))
shared.dump(stinkies)
end end
return stinkies return stinkies
end end
local stinky = {
name = name,
class = class,
seenAt = GetTime(),
hostile = true
}
stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("%s: Found stinky in arrived: %s/%s", ModuleName, name, class))
shared.dumpTable(stinkies)
end
return stinkies
end
local frame = CreateFrame("Frame") local frame = CreateFrame("Frame")
frame:RegisterEvent("CHAT_MSG_CHANNEL") frame:RegisterEvent("CHAT_MSG_CHANNEL")
frame:SetScript("OnEvent", function(self, event, msg, sender, ...) frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
--if Heimdall_Data.config.stinkyTracker.debug then
-- print(string.format("[%s] Event received: %s from %s", ModuleName, event, sender))
--end
if not Heimdall_Data.config.stinkyTracker.enabled then
--if Heimdall_Data.config.stinkyTracker.debug then --if Heimdall_Data.config.stinkyTracker.debug then
-- print(string.format("[%s] Module disabled, ignoring event", ModuleName)) -- print(string.format("[%s] Event received: %s from %s", ModuleName, event, sender))
--end --end
return if not Heimdall_Data.config.stinkyTracker.enabled then
end --if Heimdall_Data.config.stinkyTracker.debug then
local channelId = select(6, ...) -- print(string.format("[%s] Module disabled, ignoring event", ModuleName))
local _, channelname = GetChannelName(channelId) --end
local ok = false return
for _, channel in pairs(Heimdall_Data.config.stinkyTracker.channels) do end
if channel == channelname then local channelId = select(6, ...)
ok = true local _, channelname = GetChannelName(channelId)
break local ok = false
for _, channel in pairs(Heimdall_Data.config.stinkyTracker.channels) do
if channel == channelname then
ok = true
break
end
end
if not ok then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Ignoring message from non-master channel: %s", ModuleName, channelname))
end
return
end end
end
if not ok then
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Ignoring message from non-master channel: %s", ModuleName, channelname)) print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dump(Heimdall_Data.config.stinkyTracker)
end end
return
end
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
shared.dumpTable(Heimdall_Data.config.stinkyTracker)
end
if string.find(msg, "^who:") then local stinkies = {}
if Heimdall_Data.config.stinkyTracker.debug then if string.find(msg, "^who:") then
print(string.format("[%s] Processing WHO message from %s", ModuleName, sender)) if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing WHO message from %s", ModuleName, sender))
end
local whoStinkies = ParseWho(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in WHO message", ModuleName))
shared.dump(whoStinkies)
end
for name, stinky in pairs(whoStinkies) do
stinkies[name] = stinky
end
end end
local whoStinkies = ParseWho(msg) if string.find(msg, "^I see") then
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in WHO message", ModuleName)) print(string.format("[%s] Processing SEE message from %s", ModuleName, sender))
end
local seeStinkies = ParseSee(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in SEE message", ModuleName))
shared.dump(seeStinkies)
end
for name, stinky in pairs(seeStinkies) do
stinkies[name] = stinky
end
end end
for name, stinky in pairs(whoStinkies) do if string.find(msg, "arrived to") or string.find(msg, "moved to") then
if stinky.hostile then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing ARRIVED message from %s", ModuleName, sender))
end
local arrivedStinkies = ParseArrived(msg)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in ARRIVED message", ModuleName))
shared.dump(arrivedStinkies)
end
for name, stinky in pairs(arrivedStinkies) do
stinkies[name] = stinky
end
end
for name, stinky in pairs(stinkies) do
if shared.stinkyTracker.ignored[name] then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Ignoring stinky: %s (%s)", ModuleName, name, stinky.class))
end
shared.stinkyTracker.ignored[name] = nil
else
shared.stinkyTracker.stinkies[name] = stinky shared.stinkyTracker.stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Added hostile stinky from WHO: %s (%s)", ModuleName, name, stinky print(string.format("[%s] Added stinky: %s (%s)", ModuleName, name, stinky.class))
.class))
end end
end end
end end
end
if string.find(msg, "^I see") then -- Log total stinky count after processing
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing SEE message from %s", ModuleName, sender)) local count = 0
end for _ in pairs(shared.stinkyTracker.stinkies:get()) do
local seeStinkies = ParseSee(msg) count = count + 1
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in SEE message", ModuleName))
end
for name, stinky in pairs(seeStinkies) do
if stinky.hostile then
shared.stinkyTracker.stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Added hostile stinky from SEE: %s (%s)", ModuleName, name, stinky
.class))
end
end end
if not stinky.hostile then print(string.format("[%s] Current total stinkies tracked: %d", ModuleName, count))
end
shared.StinkyTracker.ForEach(function(name, stinky)
if shared.AgentTracker.IsAgent(name) then
shared.stinkyTracker.stinkies[name] = nil shared.stinkyTracker.stinkies[name] = nil
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Removed non-hostile stinky from SEE: %s", ModuleName, name)) print(string.format("[%s] Removed agent from stinkies: %s", ModuleName, name))
end end
end end
end end)
end end)
if string.find(msg, "arrived to") or string.find(msg, "moved to") then
local targetFrame = CreateFrame("Frame")
targetFrame:RegisterEvent("UNIT_TARGET")
targetFrame:SetScript("OnEvent", function(self, event, unit)
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Processing ARRIVED message from %s", ModuleName, sender)) print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target"))
end end
local arrivedStinkies = ParseArrived(msg) unit = "target"
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Found stinkies in ARRIVED message", ModuleName)) if not Heimdall_Data.config.stinkyTracker.enabled then
end
for name, stinky in pairs(arrivedStinkies) do
shared.stinkyTracker.stinkies[name] = stinky
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Added stinky from ARRIVED: %s (%s)", ModuleName, name, stinky.class)) print(string.format("[%s] Module disabled, ignoring event", ModuleName))
end end
return
end end
end
-- Log total stinky count after processing local name = UnitName(unit)
if Heimdall_Data.config.stinkyTracker.debug then if not UnitIsPlayer(unit) then
local count = 0
for _ in pairs(shared.stinkyTracker.stinkies:get()) do count = count + 1 end
print(string.format("[%s] Current total stinkies tracked: %d", ModuleName, count))
end
for name, stinky in pairs(shared.stinkyTracker.stinkies) do
if Heimdall_Data.config.agents[name] then
shared.stinkyTracker.stinkies[name] = nil
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Removed agent from stinkies: %s", ModuleName, name)) print(string.format("[%s] Target %s is not a player, nothing to do", ModuleName, name))
end end
return
end end
end
end)
local targetFrame = CreateFrame("Frame") local enemy = UnitCanAttack("player", unit)
targetFrame:RegisterEvent("UNIT_TARGET") if enemy then
targetFrame:SetScript("OnEvent", function(self, event, unit) if Heimdall_Data.config.stinkyTracker.debug then
if Heimdall_Data.config.stinkyTracker.debug then print(string.format("[%s] Target %s is enemy - tracking as stinky", ModuleName, name))
print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target")) end
end shared.stinkyTracker.stinkies[name] = {
local unit = "target" name = name,
class = UnitClass(unit),
seenAt = GetTime(),
hostile = true,
}
return
end
if not shared.stinkyTracker.stinkies[name] then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Target %s is friendly and not stinky, nothing to do", ModuleName, name))
end
return
end
if not Heimdall_Data.config.stinkyTracker.enabled then
if Heimdall_Data.config.stinkyTracker.debug then if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Module disabled, ignoring event", ModuleName)) print(string.format("[%s] Target %s is friendly and stinky - removing from stinkies", ModuleName, name))
end end
return shared.stinkyTracker.stinkies[name] = nil
end end)
local name = UnitName(unit) if Heimdall_Data.config.stinkyTracker.debug then print(string.format("[%s] Module initialized", ModuleName)) end
if not UnitIsPlayer(unit) then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Target %s is not a player, nothing to do", ModuleName, name))
end
return
end
local enemy = UnitCanAttack("player", unit)
if enemy then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Target %s is enemy - tracking as stinky", ModuleName, name))
end
shared.stinkyTracker.stinkies[name] = {
name = name,
class = UnitClass(unit),
seenAt = GetTime(),
hostile = true
}
return
end
if not shared.stinkyTracker.stinkies[name] then
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Target %s is friendly and not stinky, nothing to do", ModuleName, name))
end
return
end
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Target %s is friendly and stinky - removing from stinkies", ModuleName, name))
end
shared.stinkyTracker.stinkies[name] = nil
end)
if Heimdall_Data.config.stinkyTracker.debug then
print(string.format("[%s] Module initialized", ModuleName)) print(string.format("[%s] Module initialized", ModuleName))
end end,
print("[Heimdall] StinkyTracker loaded") }
end

File diff suppressed because it is too large Load Diff

View File

@@ -6,9 +6,7 @@ local function StringToMap(str, deliminer)
local parts = { strsplit(deliminer, str) } local parts = { strsplit(deliminer, str) }
for _, line in ipairs(parts) do for _, line in ipairs(parts) do
line = strtrim(line) line = strtrim(line)
if line ~= "" then if line ~= "" then map[line] = true end
map[line] = true
end
end end
return map return map
end end
@@ -21,9 +19,7 @@ local function StringToArray(str, deliminer)
local array = { strsplit(deliminer, str) } local array = { strsplit(deliminer, str) }
for i, line in ipairs(array) do for i, line in ipairs(array) do
line = strtrim(line) line = strtrim(line)
if line ~= "" then if line ~= "" then ret[i] = line end
ret[i] = line
end
end end
return ret return ret
end end
@@ -39,7 +35,7 @@ local config = {
stinky = aura_env.config.spotter.stinky, stinky = aura_env.config.spotter.stinky,
notifyChannel = aura_env.config.spotter.notifyChannel, notifyChannel = aura_env.config.spotter.notifyChannel,
zoneOverride = aura_env.config.spotter.zoneOverride, zoneOverride = aura_env.config.spotter.zoneOverride,
throttleTime = aura_env.config.spotter.throttleTime throttleTime = aura_env.config.spotter.throttleTime,
}, },
who = { who = {
enabled = aura_env.config.who.enabled, enabled = aura_env.config.who.enabled,

2
_L.lua
View File

@@ -90,6 +90,7 @@ shared._Locale = {
updateInterval = "Update Interval", updateInterval = "Update Interval",
networkMessenger = "Network Messenger", networkMessenger = "Network Messenger",
queries = "Who queries", queries = "Who queries",
chatSniffer = "Chat Sniffer",
}, },
ru = { ru = {
bonkDetected = "%s ударил %s (%s)", bonkDetected = "%s ударил %s (%s)",
@@ -175,6 +176,7 @@ shared._Locale = {
updateInterval = "Интервал Обновления", updateInterval = "Интервал Обновления",
networkMessenger = "Сетевой Мессенджер", networkMessenger = "Сетевой Мессенджер",
queries = "Запросы Who", queries = "Запросы Who",
chatSniffer = "Сниффер Чата",
["Orgrimmar"] = "Оргриммар", ["Orgrimmar"] = "Оргриммар",
["Valley of Strength"] = "Долина Силы", ["Valley of Strength"] = "Долина Силы",
["Valley of Trials"] = "Долина Испытаний", ["Valley of Trials"] = "Долина Испытаний",

View File

@@ -1,43 +1,61 @@
#!/bin/bash #!/bin/bash
# Default version increment echo "Figuring out the tag..."
DEFAULT_INCREMENT="0.0.1" TAG=$(git describe --tags --exact-match 2>/dev/null || echo "")
if [ -z "$TAG" ]; then
# Get the latest tag
LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
# Increment the patch version
IFS='.' read -r -a VERSION_PARTS <<< "$LATEST_TAG"
VERSION_PARTS[2]=$((VERSION_PARTS[2]+1))
TAG="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${VERSION_PARTS[2]}"
# Create a new tag
git tag $TAG
fi
echo "Tag: $TAG"
# Get the increment parameter or use the default echo "Building the thing..."
INCREMENT=${1:-$DEFAULT_INCREMENT} sed -i "s/## Version: .*/## Version: $TAG/" Heimdall.toc
sed -i "s/local VERSION = .*/local VERSION = \"$TAG\"/" Heimdall.lua
git add Heimdall.toc Heimdall.lua
git commit -m "Release $TAG"
git tag -f $TAG
git push origin $TAG
# Function to increment the version rm Heimdall-${TAG}.zip
increment_version() { mkdir Heimdall
local version=$1 cp *.lua *.toc Heimdall
local increment=$2 cp -r Modules Heimdall
cp -r Sounds Heimdall
cp -r Texture Heimdall
7z a Heimdall-${TAG}.zip Heimdall
rm -rf Heimdall
IFS='.' read -r -a version_parts <<< "$version" echo "Creating a release..."
IFS='.' read -r -a increment_parts <<< "$increment" TOKEN="$GITEA_API_KEY"
GITEA="https://git.site.quack-lab.dev"
REPO="dave/wow-heimdall"
# Create a release
RELEASE_RESPONSE=$(curl -s -X POST \
-H "Authorization: token $TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"tag_name": "'"$TAG"'",
"name": "'"$TAG"'",
"draft": false,
"prerelease": false
}' \
$GITEA/api/v1/repos/$REPO/releases)
# Increment major, minor, and patch # Extract the release ID
version_parts[0]=$((version_parts[0] + increment_parts[0])) echo $RELEASE_RESPONSE
version_parts[1]=$((version_parts[1] + increment_parts[1])) RELEASE_ID=$(echo $RELEASE_RESPONSE | awk -F'"id":' '{print $2+0; exit}')
version_parts[2]=$((version_parts[2] + increment_parts[2])) echo "Release ID: $RELEASE_ID"
# Reset minor and patch if major is incremented echo "Uploading the things..."
if [ "${increment_parts[0]}" -gt 0 ]; then curl -X POST \
version_parts[1]=0 -H "Authorization: token $TOKEN" \
version_parts[2]=0 -F "attachment=@Heimdall-${TAG}.zip" \
elif [ "${increment_parts[1]}" -gt 0 ]; then "$GITEA/api/v1/repos/$REPO/releases/${RELEASE_ID}/assets?name=Heimdall-${TAG}.zip"
version_parts[2]=0 rm Heimdall-${TAG}.zip
fi
echo "${version_parts[0]}.${version_parts[1]}.${version_parts[2]}"
}
CURRENT_VERSION=$(grep -oP '## Version: \K[0-9]+\.[0-9]+\.[0-9]+' Heimdall.toc)
NEW_VERSION=$(increment_version "$CURRENT_VERSION" "$INCREMENT")
sed -i "s/## Version: $CURRENT_VERSION/## Version: $NEW_VERSION/" Heimdall.toc
sed -i "s/local VERSION = \"$CURRENT_VERSION\"/local VERSION = \"$NEW_VERSION\"/" Heimdall.lua
# Git operations
git add Heimdall.lua Heimdall.toc
git commit -m "Release $NEW_VERSION"
git tag "$NEW_VERSION"
echo "Deployment complete. New version: $NEW_VERSION"

12
stylua.toml Symbolic link
View File

@@ -0,0 +1,12 @@
syntax = "All" # Specify a disambiguation for the style of Lua syntax being formatted. Possible options: All (default), Lua51, Lua52, Lua53, Lua54, LuaJIT, Luau, CfxLua
column_width = 120 # Approximate line length for printing. Used as a guide for line wrapping - this is not a hard requirement: lines may fall under or over the limit.
line_endings = "Windows" # Line endings type. Possible options: Unix (LF) or Windows (CRLF)
indent_type = "Tabs" # Indent type. Possible options: Tabs or Spaces
indent_width = 4 # Character size of single indentation. If indent_type is set to Tabs, this option is used as a heuristic to determine column width only.
quote_style = "AutoPreferDouble" # Quote style for string literals. Possible options: AutoPreferDouble, AutoPreferSingle, ForceDouble, ForceSingle. AutoPrefer styles will prefer the specified quote style, but fall back to the alternative if it has fewer string escapes. Force styles always use the specified style regardless of escapes.
call_parentheses = "Always" # Whether parentheses should be applied on function calls with a single string/table argument. Possible options: Always, NoSingleString, NoSingleTable, None, Input. Always applies parentheses in all cases. NoSingleString omits parentheses on calls with a single string argument. Similarly, NoSingleTable omits parentheses on calls with a single table argument. None omits parentheses in both cases. Note: parentheses are still kept in situations where removal can lead to obscurity (e.g. foo "bar".setup -> foo("bar").setup, since the index is on the call result, not the string). Input removes all automation and preserves parentheses only if they were present in input code: consistency is not enforced.
space_after_function_names = "Never" # Specify whether to add a space between the function name and parentheses. Possible options: Never, Definitions, Calls, or Always
collapse_simple_statement = "Always" # Specify whether to collapse simple statements. Possible options: Never, FunctionOnly, ConditionalOnly, or Always
[sort_requires]
enabled = false