Compare commits
300 Commits
Author | SHA1 | Date | |
---|---|---|---|
7eee3a13a6 | |||
263cf8e2e4 | |||
ccbf0f8dc2 | |||
63027c2dcf | |||
d46c874604 | |||
fd4f707b6c | |||
1168876dcc | |||
bdf5afe436 | |||
85ff907f05 | |||
7ae9db030b | |||
edf8a12865 | |||
d081eedd47 | |||
8532db5a25 | |||
26e783ee2e | |||
4bd237abef | |||
0ab14de0e2 | |||
a25b6a20d5 | |||
e85c14ea45 | |||
a564178ca2 | |||
b4a4011b18 | |||
3f3d252104 | |||
287be2a31c | |||
3ef0e4c935 | |||
ce92e8e12c | |||
03597d1b5e | |||
36ad9783e5 | |||
565db30125 | |||
017cbf01f8 | |||
b16cf762ac | |||
0057ac3a5c | |||
0edf0561d8 | |||
1129d787b5 | |||
8a24496801 | |||
6cb918c13c | |||
e3eefadb75 | |||
eab562b36d | |||
20a7c0eead | |||
f70c5adfcf | |||
52246e2e16 | |||
4897a5b1a9 | |||
2381f68912 | |||
ad969e8604 | |||
31678f9a82 | |||
1785af4d9d | |||
69d7efe6f0 | |||
202c5d0f46 | |||
476e907205 | |||
724194abe2 | |||
d57b573683 | |||
5d8afe8db7 | |||
b14f1ff2f9 | |||
a1301abdb2 | |||
f897183920 | |||
c1885ce76a | |||
e676d53e97 | |||
f05156b257 | |||
c8f9d81b3e | |||
3a1639ab27 | |||
1da1e7bf9f | |||
304fbcbaae | |||
80f8500f6e | |||
78cbcbde9d | |||
![]() |
8db1cb179c | ||
a065e47545 | |||
5271029b84 | |||
7c4fb53baa | |||
1c2fb471a6 | |||
85f72b14e0 | |||
99261beb08 | |||
d34be4f18d | |||
3d26832e54 | |||
cd80c9fc0e | |||
672ca07782 | |||
b06313c4eb | |||
31be9b0ce8 | |||
d69a53b4c8 | |||
7aa5d50a6c | |||
d047c632ed | |||
9eb5c04a33 | |||
498432d968 | |||
e04eb57202 | |||
25fe8350a7 | |||
9a10386e65 | |||
75ab8646e8 | |||
37ef42e53d | |||
b2aa1b75f3 | |||
31bcd3481c | |||
966078c339 | |||
fdcd816235 | |||
054d8ab7ef | |||
0327359300 | |||
c61a4b0c04 | |||
950f95cef1 | |||
d40670fef6 | |||
d6e5e7941f | |||
a06e9027c6 | |||
8fb0595ef8 | |||
d3808a887e | |||
1ed379c23d | |||
0e66db1d8a | |||
bac16d22a9 | |||
8b4de82142 | |||
35930c52a4 | |||
293e71e619 | |||
7150189a0d | |||
0e951d7089 | |||
ef89c3001b | |||
b538c7b5de | |||
6c234e7fa4 | |||
407d8f2da2 | |||
9b755cd0be | |||
19f9d4bda9 | |||
de744337ad | |||
63ba6d2da1 | |||
79b77ee6c3 | |||
0e2935f844 | |||
0ad6a23daa | |||
35398ebf38 | |||
62b028cf56 | |||
ad676915bb | |||
61ebb22a85 | |||
8348b93b30 | |||
550e11b488 | |||
ed10ea496d | |||
6e5c7510d0 | |||
68d0393915 | |||
f1e07f0a3b | |||
90100f6f5b | |||
995966e952 | |||
a90eb8248f | |||
ffca28c67d | |||
20a2a95ce6 | |||
145fd02ba8 | |||
196a5a8cfa | |||
439e9b29d1 | |||
a3f1a0e96d | |||
c81e349e90 | |||
30068a5b11 | |||
036b6b23a8 | |||
128eb44003 | |||
0f72b2048e | |||
1e4045ab7a | |||
b871549087 | |||
58760831dc | |||
0a8ab00637 | |||
8396801d80 | |||
19af9894e5 | |||
7293f5a8fa | |||
9d12609147 | |||
2c7089504f | |||
8fbff23bee | |||
0c5078e3f3 | |||
d143a18838 | |||
ba889e442c | |||
4511ecbf0a | |||
fe37bebd2c | |||
d987436892 | |||
9f4e19104f | |||
c0568075e1 | |||
13415fb065 | |||
ca13d1e364 | |||
bf916dd8d5 | |||
4aa168ebcc | |||
2cea01f367 | |||
2ec0aea19c | |||
846584d6fe | |||
1b5912a1bf | |||
954dbfa425 | |||
42ec90a5df | |||
0b4350c8ae | |||
da28805882 | |||
6551e24069 | |||
28ef8cb33a | |||
d4b0dee037 | |||
799d3e1ddd | |||
e6f3bac946 | |||
05c7e71794 | |||
41b980d118 | |||
3efd99cdc8 | |||
119eb7965b | |||
f4421f0334 | |||
688f2f4b30 | |||
87300bf48a | |||
8cbad47acc | |||
241615238c | |||
319e6cdd77 | |||
d54e93ad85 | |||
efe0002e02 | |||
e58d92c399 | |||
fa18138c3b | |||
2a5d6e5157 | |||
c32549fa87 | |||
2689e39d70 | |||
25f2310c25 | |||
0bed5ecf41 | |||
ec2f146095 | |||
75a84baa42 | |||
1bc7ebc92a | |||
d620f577c1 | |||
7af1b40222 | |||
82f1539815 | |||
e38ba012a8 | |||
a0322718c1 | |||
01ca12f80e | |||
3376b4fa7c | |||
308b65e2f6 | |||
fa5b73b5fe | |||
0fd088320d | |||
a109c631cd | |||
cf61a74fa8 | |||
8fa4effb6b | |||
5ca69bbe24 | |||
8816468ba0 | |||
e9f17c585c | |||
373ca377a2 | |||
770420a5b2 | |||
22b1b6bc73 | |||
18fd4bb9d2 | |||
ca333a93e3 | |||
4c404225d2 | |||
9f86a4e0f9 | |||
6273263c4e | |||
0b6b8df1a9 | |||
fe730a19df | |||
636ef87cb1 | |||
d333901576 | |||
d104bcc1fa | |||
dbfbc2c347 | |||
dd620c14d3 | |||
744098abc7 | |||
d41554271d | |||
23bfbf9f4a | |||
f52ed8c791 | |||
604371a2e1 | |||
44cec2d2fb | |||
cbe9ef7303 | |||
002970484d | |||
f063ceb4e5 | |||
1eaefffe04 | |||
1291d21216 | |||
1c198f0133 | |||
23bf656f82 | |||
30fae67f6c | |||
2726955034 | |||
e433bc3319 | |||
71df812170 | |||
abb8540c12 | |||
12e0c23ea9 | |||
b98ecdd0a4 | |||
7314c67357 | |||
6b74e01f0a | |||
6becc08e18 | |||
f66a990103 | |||
c3b9772512 | |||
6bb1cc683c | |||
b5473e05e7 | |||
939ca47e3c | |||
c16e5f1e47 | |||
059f917acc | |||
43b08f22dd | |||
aa7e4a3d3e | |||
18f2b44941 | |||
b2925285a2 | |||
c0a6a3c082 | |||
7c7edcf959 | |||
dee5053345 | |||
58e071e77b | |||
a7e85acd67 | |||
176d184d91 | |||
32543d04a0 | |||
4b43ee86c0 | |||
25959be98f | |||
cb6680304f | |||
40646f16bc | |||
55e7ee2428 | |||
a2930577d3 | |||
e572f50de7 | |||
47b7f5d85a | |||
8ac29e4378 | |||
be81a31302 | |||
2e44a1ef31 | |||
d182cc1418 | |||
d3004019c6 | |||
fca49c6302 | |||
8b085009a9 | |||
2ba6d190f0 | |||
8c5a94a12a | |||
bf9e1f0319 | |||
903baf7f38 | |||
5b585ebba7 | |||
34bae5dc7b | |||
4f97859533 | |||
4c55e65863 | |||
016f0be480 | |||
fbf35d6d77 | |||
30ee1c717e | |||
e3286571b1 | |||
ece39790d2 | |||
eedadb0a3f | |||
00f7cba8fc |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1 +1,3 @@
|
|||||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tga filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1 @@
|
|||||||
Meta
|
scratch.lua
|
||||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "Meta"]
|
||||||
|
path = Meta
|
||||||
|
url = https://git.site.quack-lab.dev/dave/wow_Meta
|
5
.luacheckrc
Symbolic link
5
.luacheckrc
Symbolic link
@@ -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
14
.luarc.json
Symbolic link
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"workspace": {
|
||||||
|
"library": [
|
||||||
|
"./Meta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"diagnostics.disable": [
|
||||||
|
"unused-local",
|
||||||
|
"unused-vararg"
|
||||||
|
],
|
||||||
|
"diagnostics.globals": [
|
||||||
|
"aura_env"
|
||||||
|
]
|
||||||
|
}
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"Lua.diagnostics.globals": [
|
|
||||||
"UIParent"
|
|
||||||
]
|
|
||||||
}
|
|
945
Heimdall.lua
945
Heimdall.lua
@@ -2,382 +2,615 @@ local addonname, shared = ...
|
|||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
---@cast addonname string
|
||||||
|
|
||||||
-- TODO: Implement counting kills and display on whosniffer
|
local VERSION = "3.12.0"
|
||||||
-- Take last N seconds of combatlog into account ie. count who does damage to who
|
shared.VERSION = VERSION
|
||||||
-- Maybe even make an alert when someone does too much damage to someone else...
|
|
||||||
-- But that would not be trivial as of now, I can't think of a way to do it sensibly
|
|
||||||
|
|
||||||
local function init()
|
local function init()
|
||||||
---@class Heimdall_Data
|
---@class Heimdall_Data
|
||||||
---@field config HeimdallConfig
|
---@field config HeimdallConfig
|
||||||
---@field stinkies table<string, boolean>
|
if not Heimdall_Data then Heimdall_Data = {} end
|
||||||
if not Heimdall_Data then Heimdall_Data = {} end
|
|
||||||
|
|
||||||
---@class InitTable
|
---@class InitTable
|
||||||
---@field Init fun(): nil
|
---@field Init fun(): nil
|
||||||
|
|
||||||
---@class HeimdallShared
|
---@class HeimdallShared
|
||||||
---@field raceMap table<string, string>
|
---@field raceMap table<string, string>
|
||||||
---@field classColors table<string, string>
|
---@field classColors table<string, string>
|
||||||
---@field messenger HeimdallMessengerData
|
---@field messenger HeimdallMessengerData
|
||||||
---@field who HeimdallWhoData
|
---@field who HeimdallWhoData
|
||||||
---@field stinkyTracker HeimdallStinkyTrackerData
|
---@field stinkyTracker StinkyTrackerData
|
||||||
---@field dumpTable fun(table: any, depth?: number): nil
|
---@field agentTracker AgentTrackerData
|
||||||
---@field utf8len fun(input: string): number
|
---@field networkNodes string[]
|
||||||
---@field padString fun(input: string, targetLength: number, left?: boolean): string
|
---@field network HeimdallNetworkData
|
||||||
---@field GetOrDefault fun(table: table<any, any>, keys: string[], default: any): any
|
---@field networkMessenger HeimdallNetworkMessengerData
|
||||||
---@field Whoer InitTable
|
---@field stinkyCache HeimdallStinkyCacheData
|
||||||
---@field Messenger InitTable
|
---@field _L fun(key: string, locale: string): string
|
||||||
---@field Spotter InitTable
|
---@field _Locale Localization
|
||||||
---@field DeathReporter InitTable
|
---@field VERSION string
|
||||||
---@field Inviter InitTable
|
---@field dump fun(table: any, msg?: string, depth?: number): nil
|
||||||
---@field Dueler InitTable
|
---@field utf8len fun(input: string): number
|
||||||
---@field Bully InitTable
|
---@field padString fun(input: string, targetLength: number, left?: boolean): string
|
||||||
---@field AgentTracker InitTable
|
---@field GetOrDefault fun(table: table<any, any>, keys: string[], default: any): any
|
||||||
---@field Emoter InitTable
|
---@field Split fun(input: string, deliminer: string): string[]
|
||||||
---@field Echoer InitTable
|
---@field IsStinky fun(name: string): boolean
|
||||||
---@field Macroer InitTable
|
---@field Memoize fun(f: function): function
|
||||||
---@field Commander InitTable
|
---@field GetLocaleForChannel fun(channel: string): string
|
||||||
---@field StinkyTracker InitTable
|
---@field WhoQueryService WhoQueryService
|
||||||
---@field CombatAlerter InitTable
|
---@field AchievementSniffer AchievementSniffer
|
||||||
---@field Config InitTable
|
---@field AgentTracker AgentTracker
|
||||||
|
---@field BonkDetector BonkDetector
|
||||||
|
---@field Bully Bully
|
||||||
|
---@field CombatAlerter CombatAlerter
|
||||||
|
---@field Commander Commander
|
||||||
|
---@field Config Config
|
||||||
|
---@field Configurator Configurator
|
||||||
|
---@field DeathReporter DeathReporter
|
||||||
|
---@field Dueler Dueler
|
||||||
|
---@field Echoer Echoer
|
||||||
|
---@field Emoter Emoter
|
||||||
|
---@field Inviter Inviter
|
||||||
|
---@field Macroer Macroer
|
||||||
|
---@field Messenger Messenger
|
||||||
|
---@field MinimapTagger MinimapTagger
|
||||||
|
---@field Network Network
|
||||||
|
---@field NetworkMessenger NetworkMessenger
|
||||||
|
---@field Noter Noter
|
||||||
|
---@field Sniffer Sniffer
|
||||||
|
---@field Spotter Spotter
|
||||||
|
---@field StinkyCache StinkyCache
|
||||||
|
---@field StinkyTracker StinkyTracker
|
||||||
|
---@field Whoer Whoer
|
||||||
|
---@field ChatSniffer ChatSniffer
|
||||||
|
|
||||||
--- Config ---
|
--- Config ---
|
||||||
---@class HeimdallConfig
|
---@class HeimdallConfig
|
||||||
---@field spotter HeimdallSpotterConfig
|
---@field spotter HeimdallSpotterConfig
|
||||||
---@field who HeimdallWhoConfig
|
---@field who HeimdallWhoConfig
|
||||||
---@field messenger HeimdallMessengerConfig
|
---@field messenger HeimdallMessengerConfig
|
||||||
---@field deathReporter HeimdallDeathReporterConfig
|
---@field deathReporter HeimdallDeathReporterConfig
|
||||||
---@field inviter HeimdallInviterConfig
|
---@field inviter HeimdallInviterConfig
|
||||||
---@field dueler HeimdallDuelerConfig
|
---@field dueler HeimdallDuelerConfig
|
||||||
---@field bully HeimdallBullyConfig
|
---@field agentTracker HeimdallAgentTrackerConfig
|
||||||
---@field agentTracker HeimdallAgentTrackerConfig
|
---@field emoter HeimdallEmoterConfig
|
||||||
---@field emoter HeimdallEmoterConfig
|
---@field echoer HeimdallEchoerConfig
|
||||||
---@field echoer HeimdallEchoerConfig
|
---@field macroer HeimdallMacroerConfig
|
||||||
---@field macroer HeimdallMacroerConfig
|
---@field commander HeimdallCommanderConfig
|
||||||
---@field commander HeimdallCommanderConfig
|
---@field stinkyTracker HeimdallStinkyTrackerConfig
|
||||||
---@field stinkyTracker HeimdallStinkyTrackerConfig
|
---@field combatAlerter HeimdallCombatAlerterConfig
|
||||||
---@field combatAlerter HeimdallCombatAlerterConfig
|
---@field sniffer HeimdallSnifferConfig
|
||||||
---@field whisperNotify table<string, string>
|
---@field noter HeimdallNoterConfig
|
||||||
---@field stinkies table<string, boolean>
|
---@field network HeimdallNetworkConfig
|
||||||
---@field agents table<string, string>
|
---@field networkMessenger HeimdallNetworkMessengerConfig
|
||||||
|
---@field configurator HeimdallConfiguratorConfig
|
||||||
|
---@field stinkyCache HeimdallStinkyCacheConfig
|
||||||
|
---@field achievementSniffer HeimdallAchievementSnifferConfig
|
||||||
|
---@field chatSniffer HeimdallChatSnifferConfig
|
||||||
|
---@field whisperNotify table<string, string>
|
||||||
|
---@field addonPrefix string
|
||||||
|
---@field stinkies table<string, boolean>
|
||||||
|
---@field agents table<string, string>
|
||||||
|
---@field scale number
|
||||||
|
---@field notes table<string, Note[]>
|
||||||
|
---@field channelLocale table<string, string>
|
||||||
|
---@field locale string
|
||||||
|
---@field debug boolean
|
||||||
|
|
||||||
---@class HeimdallSpotterConfig
|
shared.GetOrDefault = function(table, keys, default)
|
||||||
---@field enabled boolean
|
local value = default
|
||||||
---@field everyone boolean
|
if not table then return value end
|
||||||
---@field hostile boolean
|
if not keys then return value end
|
||||||
---@field alliance boolean
|
|
||||||
---@field stinky boolean
|
|
||||||
---@field notifyChannel string
|
|
||||||
---@field zoneOverride string?
|
|
||||||
---@field throttleTime number
|
|
||||||
|
|
||||||
---@class HeimdallWhoConfig
|
local traverse = table
|
||||||
---@field enabled boolean
|
for i = 1, #keys do
|
||||||
---@field ignored table<string, boolean>
|
local key = keys[i]
|
||||||
---@field notifyChannel string
|
if traverse[key] ~= nil then
|
||||||
---@field ttl number
|
traverse = traverse[key]
|
||||||
---@field doWhisper boolean
|
else
|
||||||
---@field zoneNotifyFor table<string, boolean>
|
break
|
||||||
|
end
|
||||||
|
|
||||||
---@class HeimdallMessengerConfig
|
if i == #keys then value = traverse end
|
||||||
---@field enabled boolean
|
end
|
||||||
---@field interval number
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
---@class HeimdallDeathReporterConfig
|
--/run Heimdall_Data.config.who.queries="g-\"БеспредеЛ\"|ally"
|
||||||
---@field enabled boolean
|
Heimdall_Data.config = {
|
||||||
---@field throttle number
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "debug" }, false),
|
||||||
---@field doWhisper boolean
|
chatSniffer = {
|
||||||
---@field notifyChannel string
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "chatSniffer", "enabled" }, false),
|
||||||
---@field zoneOverride string?
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "chatSniffer", "debug" }, false),
|
||||||
---@field duelThrottle number
|
},
|
||||||
|
spotter = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "enabled" }, true),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "debug" }, false),
|
||||||
|
everyone = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "everyone" }, false),
|
||||||
|
hostile = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "hostile" }, true),
|
||||||
|
alliance = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "alliance" }, true),
|
||||||
|
stinky = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "stinky" }, true),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "channels" }, { "Agent" }),
|
||||||
|
zoneOverride = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "zoneOverride" }, nil),
|
||||||
|
throttleTime = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "throttleTime" }, 10),
|
||||||
|
},
|
||||||
|
who = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "who", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "who", "debug" }, false),
|
||||||
|
ignored = shared.GetOrDefault(Heimdall_Data, { "config", "who", "ignored" }, {}),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "who", "channels" }, { "Agent" }),
|
||||||
|
ttl = shared.GetOrDefault(Heimdall_Data, { "config", "who", "ttl" }, 20),
|
||||||
|
doWhisper = shared.GetOrDefault(Heimdall_Data, { "config", "who", "doWhisper" }, true),
|
||||||
|
zoneNotifyFor = shared.GetOrDefault(Heimdall_Data, { "config", "who", "zoneNotifyFor" }, {
|
||||||
|
["Orgrimmar"] = true,
|
||||||
|
["Thunder Bluff"] = true,
|
||||||
|
["Undercity"] = true,
|
||||||
|
["Durotar"] = true,
|
||||||
|
["Echo Isles"] = true,
|
||||||
|
["Valley of Trials"] = true,
|
||||||
|
}),
|
||||||
|
queries = shared.GetOrDefault(Heimdall_Data, { "config", "who", "queries" }, ""),
|
||||||
|
},
|
||||||
|
messenger = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "messenger", "enabled" }, true),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "messenger", "debug" }, false),
|
||||||
|
interval = shared.GetOrDefault(Heimdall_Data, { "config", "messenger", "interval" }, 0.2),
|
||||||
|
},
|
||||||
|
deathReporter = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "debug" }, false),
|
||||||
|
throttle = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "throttle" }, 10),
|
||||||
|
doWhisper = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "doWhisper" }, true),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "channels" }, { "Agent" }),
|
||||||
|
zoneOverride = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "zoneOverride" }, nil),
|
||||||
|
duelThrottle = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "duelThrottle" }, 5),
|
||||||
|
},
|
||||||
|
inviter = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "channels" }, { "Agent" }),
|
||||||
|
keyword = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "keyword" }, "+"),
|
||||||
|
allAssist = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "allAssist" }, false),
|
||||||
|
agentsAssist = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "agentsAssist" }, false),
|
||||||
|
throttle = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "throttle" }, 1),
|
||||||
|
kickOffline = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "kickOffline" }, false),
|
||||||
|
cleanupInterval = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "cleanupInterval" }, 10),
|
||||||
|
afkThreshold = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "afkThreshold" }, 300),
|
||||||
|
listeningChannel = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "listeningChannel" }, {}),
|
||||||
|
},
|
||||||
|
dueler = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "dueler", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "dueler", "debug" }, false),
|
||||||
|
declineOther = shared.GetOrDefault(Heimdall_Data, { "config", "dueler", "declineOther" }, false),
|
||||||
|
},
|
||||||
|
bully = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "bully", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "bully", "debug" }, false),
|
||||||
|
},
|
||||||
|
agentTracker = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "agentTracker", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "agentTracker", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "agentTracker", "channels" }, { "Agent" }),
|
||||||
|
},
|
||||||
|
emoter = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "emoter", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "emoter", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "emoter", "channels" }, { "Agent" }),
|
||||||
|
prefix = shared.GetOrDefault(Heimdall_Data, { "config", "emoter", "prefix" }, ""),
|
||||||
|
},
|
||||||
|
echoer = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "echoer", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "echoer", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "echoer", "channels" }, { "Agent" }),
|
||||||
|
prefix = shared.GetOrDefault(Heimdall_Data, { "config", "echoer", "prefix" }, ""),
|
||||||
|
},
|
||||||
|
macroer = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "macroer", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "macroer", "debug" }, false),
|
||||||
|
priority = shared.GetOrDefault(Heimdall_Data, { "config", "macroer", "priority" }, {}),
|
||||||
|
},
|
||||||
|
agents = shared.GetOrDefault(Heimdall_Data, { "config", "agents" }, {}),
|
||||||
|
commander = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "commander", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "commander", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "commander", "channels" }, { "Agent" }),
|
||||||
|
commander = shared.GetOrDefault(Heimdall_Data, { "config", "commander", "commander" }, "Heimdállr"),
|
||||||
|
commands = shared.GetOrDefault(Heimdall_Data, { "config", "commander", "commands" }, {}),
|
||||||
|
},
|
||||||
|
stinkyTracker = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "channels" }, { "Agent" }),
|
||||||
|
ignoredTimeout = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "ignoredTimeout" }, 600),
|
||||||
|
},
|
||||||
|
combatAlerter = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "combatAlerter", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "combatAlerter", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "combatAlerter", "channels" }, { "Agent" }),
|
||||||
|
},
|
||||||
|
messageDelegator = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "messageDelegator", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "messageDelegator", "debug" }, false),
|
||||||
|
delegates = shared.GetOrDefault(Heimdall_Data, { "config", "messageDelegator", "delegates" }, {}),
|
||||||
|
masterChannel = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "messageDelegator", "masterChannel" },
|
||||||
|
"Agent"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
sniffer = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "sniffer", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "sniffer", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "sniffer", "channels" }, { "Agent" }),
|
||||||
|
throttle = shared.GetOrDefault(Heimdall_Data, { "config", "sniffer", "throttle" }, 10),
|
||||||
|
zoneOverride = shared.GetOrDefault(Heimdall_Data, { "config", "sniffer", "zoneOverride" }, nil),
|
||||||
|
stinky = shared.GetOrDefault(Heimdall_Data, { "config", "sniffer", "stinky" }, true),
|
||||||
|
},
|
||||||
|
minimapTagger = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "channels" }, { "Agent" }),
|
||||||
|
throttle = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "throttle" }, 10),
|
||||||
|
scale = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "scale" }, 3),
|
||||||
|
tagTTL = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "tagTTL" }, 1),
|
||||||
|
tagSound = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "tagSound" }, false),
|
||||||
|
tagSoundFile = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "tagSoundFile" },
|
||||||
|
"MGSSpot.ogg"
|
||||||
|
),
|
||||||
|
tagSoundThrottle = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "tagSoundThrottle" }, 0),
|
||||||
|
tagTextureFile = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "tagTextureFile" },
|
||||||
|
"Aura4.tga"
|
||||||
|
),
|
||||||
|
---
|
||||||
|
alertTTL = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "alertTTL" }, 1),
|
||||||
|
alertSound = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "alertSound" }, false),
|
||||||
|
alertSoundFile = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "alertSoundFile" },
|
||||||
|
"OOF.ogg"
|
||||||
|
),
|
||||||
|
alertSoundThrottle = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "alertSoundThrottle" },
|
||||||
|
0
|
||||||
|
),
|
||||||
|
alertTextureFile = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "alertTextureFile" },
|
||||||
|
"Aura27.tga"
|
||||||
|
),
|
||||||
|
---
|
||||||
|
combatTTL = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "combatTTL" }, 1),
|
||||||
|
combatSound = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "combatSound" }, false),
|
||||||
|
combatSoundFile = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "combatSoundFile" },
|
||||||
|
"StarScream.ogg"
|
||||||
|
),
|
||||||
|
combatSoundThrottle = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "combatSoundThrottle" },
|
||||||
|
2
|
||||||
|
),
|
||||||
|
combatTextureFile = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "combatTextureFile" },
|
||||||
|
"Aura58.tga"
|
||||||
|
),
|
||||||
|
---
|
||||||
|
helpTTL = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "helpTTL" }, 1),
|
||||||
|
helpSound = shared.GetOrDefault(Heimdall_Data, { "config", "minimapTagger", "helpSound" }, false),
|
||||||
|
helpSoundFile = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "helpSoundFile" },
|
||||||
|
"MedicGangsterParadise.ogg"
|
||||||
|
),
|
||||||
|
helpSoundThrottle = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "helpSoundThrottle" },
|
||||||
|
2
|
||||||
|
),
|
||||||
|
helpTextureFile = shared.GetOrDefault(
|
||||||
|
Heimdall_Data,
|
||||||
|
{ "config", "minimapTagger", "helpTextureFile" },
|
||||||
|
"Aura68.tga"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
whisperNotify = shared.GetOrDefault(Heimdall_Data, { "config", "whisperNotify" }, {}),
|
||||||
|
stinkies = shared.GetOrDefault(Heimdall_Data, { "config", "stinkies" }, {}),
|
||||||
|
notes = shared.GetOrDefault(Heimdall_Data, { "config", "notes" }, {}),
|
||||||
|
scale = shared.GetOrDefault(Heimdall_Data, { "config", "scale" }, 1),
|
||||||
|
locale = shared.GetOrDefault(Heimdall_Data, { "config", "locale" }, "en"),
|
||||||
|
bonkDetector = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "bonkDetector", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "bonkDetector", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "bonkDetector", "channels" }, { "Agent" }),
|
||||||
|
throttle = shared.GetOrDefault(Heimdall_Data, { "config", "bonkDetector", "throttle" }, 5),
|
||||||
|
},
|
||||||
|
noter = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "noter", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "noter", "debug" }, false),
|
||||||
|
channels = shared.GetOrDefault(Heimdall_Data, { "config", "noter", "channels" }, { "Agent" }),
|
||||||
|
lastNotes = shared.GetOrDefault(Heimdall_Data, { "config", "noter", "lastNotes" }, 5),
|
||||||
|
},
|
||||||
|
network = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "network", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "network", "debug" }, false),
|
||||||
|
members = shared.GetOrDefault(Heimdall_Data, { "config", "network", "members" }, {}),
|
||||||
|
updateInterval = shared.GetOrDefault(Heimdall_Data, { "config", "network", "updateInterval" }, 10),
|
||||||
|
},
|
||||||
|
networkMessenger = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "networkMessenger", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "networkMessenger", "debug" }, false),
|
||||||
|
interval = shared.GetOrDefault(Heimdall_Data, { "config", "networkMessenger", "interval" }, 0.01),
|
||||||
|
},
|
||||||
|
configurator = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "configurator", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "configurator", "debug" }, false),
|
||||||
|
},
|
||||||
|
stinkyCache = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyCache", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyCache", "debug" }, false),
|
||||||
|
commander = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyCache", "commander" }, "Heimdállr"),
|
||||||
|
ttl = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyCache", "ttl" }, 10),
|
||||||
|
},
|
||||||
|
achievementSniffer = {
|
||||||
|
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "achievementSniffer", "enabled" }, false),
|
||||||
|
debug = shared.GetOrDefault(Heimdall_Data, { "config", "achievementSniffer", "debug" }, false),
|
||||||
|
--texture = shared.GetOrDefault(Heimdall_Data, { "config", "achievementSniffer", "texture" }, "Aura53.tga"),
|
||||||
|
--offsetX = shared.GetOrDefault(Heimdall_Data, { "config", "achievementSniffer", "offsetX" }, 0),
|
||||||
|
--offsetY = shared.GetOrDefault(Heimdall_Data, { "config", "achievementSniffer", "offsetY" }, 0),
|
||||||
|
rescan = shared.GetOrDefault(Heimdall_Data, { "config", "achievementSniffer", "rescan" }, false),
|
||||||
|
scanInterval = shared.GetOrDefault(Heimdall_Data, { "config", "achievementSniffer", "scanInterval" }, 1),
|
||||||
|
--iconScale = shared.GetOrDefault(Heimdall_Data, { "config", "achievementSniffer", "iconScale" }, 1),
|
||||||
|
},
|
||||||
|
addonPrefix = shared.GetOrDefault(Heimdall_Data, { "config", "addonPrefix" }, "HEIMDALL"),
|
||||||
|
channelLocale = shared.GetOrDefault(Heimdall_Data, { "config", "channelLocale" }, {}),
|
||||||
|
}
|
||||||
|
|
||||||
---@class HeimdallInviterConfig
|
shared.raceMap = {
|
||||||
---@field enabled boolean
|
["Orc"] = "Horde",
|
||||||
---@field listeningChannel string
|
["Undead"] = "Horde",
|
||||||
---@field keyword string
|
["Tauren"] = "Horde",
|
||||||
---@field allAssist boolean
|
["Troll"] = "Horde",
|
||||||
---@field agentsAssist boolean
|
["Blood Elf"] = "Horde",
|
||||||
---@field throttle number
|
["Goblin"] = "Horde",
|
||||||
---@field kickOffline boolean
|
["Human"] = "Alliance",
|
||||||
---@field cleanupInterval number
|
["Dwarf"] = "Alliance",
|
||||||
---@field afkThreshold number
|
["Night Elf"] = "Alliance",
|
||||||
|
["Gnome"] = "Alliance",
|
||||||
|
["Draenei"] = "Alliance",
|
||||||
|
["Worgen"] = "Alliance",
|
||||||
|
["Vulpera"] = "Horde",
|
||||||
|
["Nightborne"] = "Horde",
|
||||||
|
["Zandalari Troll"] = "Horde",
|
||||||
|
["Kul Tiran"] = "Alliance",
|
||||||
|
["Dark Iron Dwarf"] = "Alliance",
|
||||||
|
["Void Elf"] = "Alliance",
|
||||||
|
["Lightforged Draenei"] = "Alliance",
|
||||||
|
["Mechagnome"] = "Alliance",
|
||||||
|
["Mag'har Orc"] = "Horde",
|
||||||
|
}
|
||||||
|
|
||||||
---@class HeimdallDuelerConfig
|
shared.classColors = {
|
||||||
---@field enabled boolean
|
["Warrior"] = "C69B6D",
|
||||||
---@field declineOther boolean
|
["Paladin"] = "F48CBA",
|
||||||
|
["Hunter"] = "AAD372",
|
||||||
|
["Rogue"] = "FFF468",
|
||||||
|
["Priest"] = "FFFFFF",
|
||||||
|
["Death Knight"] = "C41E3A",
|
||||||
|
["Shaman"] = "0070DD",
|
||||||
|
["Mage"] = "3FC7EB",
|
||||||
|
["Warlock"] = "8788EE",
|
||||||
|
["Monk"] = "00FF98",
|
||||||
|
["Druid"] = "FF7C0A",
|
||||||
|
["Demon Hunter"] = "A330C9",
|
||||||
|
}
|
||||||
|
|
||||||
---@class HeimdallBullyConfig
|
---@param input string
|
||||||
---@field enabled boolean
|
---@return number
|
||||||
|
shared.utf8len = function(input)
|
||||||
|
if not input then return 0 end
|
||||||
|
local len = 0
|
||||||
|
local i = 1
|
||||||
|
local n = #input
|
||||||
|
while i <= n do
|
||||||
|
local c = input:byte(i)
|
||||||
|
if c >= 0 and c <= 127 then
|
||||||
|
i = i + 1
|
||||||
|
elseif c >= 194 and c <= 223 then
|
||||||
|
i = i + 2
|
||||||
|
elseif c >= 224 and c <= 239 then
|
||||||
|
i = i + 3
|
||||||
|
elseif c >= 240 and c <= 244 then
|
||||||
|
i = i + 4
|
||||||
|
else
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
len = len + 1
|
||||||
|
end
|
||||||
|
return len
|
||||||
|
end
|
||||||
|
---@param input string
|
||||||
|
---@param targetLength number
|
||||||
|
---@param left boolean
|
||||||
|
---@return string
|
||||||
|
shared.padString = function(input, targetLength, left)
|
||||||
|
left = left or false
|
||||||
|
local len = shared.utf8len(input)
|
||||||
|
if len < targetLength then
|
||||||
|
if left then
|
||||||
|
input = input .. string.rep(" ", targetLength - len)
|
||||||
|
else
|
||||||
|
input = string.rep(" ", targetLength - len) .. input
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return input
|
||||||
|
end
|
||||||
|
|
||||||
---@class HeimdallAgentTrackerConfig
|
---@param input string
|
||||||
---@field enabled boolean
|
---@param deliminer string
|
||||||
---@field masterChannel string
|
---@return table<number, string>
|
||||||
|
shared.Split = function(input, deliminer)
|
||||||
|
if deliminer == nil then deliminer = "%s" end
|
||||||
|
local t = {}
|
||||||
|
for str in string.gmatch(input, "([^" .. deliminer .. "]+)") do
|
||||||
|
table.insert(t, str)
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
---@param name string
|
||||||
|
---@return boolean
|
||||||
|
shared.IsStinky = function(name)
|
||||||
|
return Heimdall_Data.config.stinkies[name] ~= nil or shared.StinkyCache[name] ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
---@class HeimdallEmoterConfig
|
---@param f function
|
||||||
---@field enabled boolean
|
---@return function
|
||||||
---@field masterChannel string
|
shared.Memoize = function(f)
|
||||||
---@field prefix string
|
local mem = {} -- memoizing table
|
||||||
|
setmetatable(mem, { __mode = "kv" }) -- make it weak
|
||||||
|
return function(x) -- new version of 'f', with memoizing
|
||||||
|
if Heimdall_Data.config.debug then print(string.format("[Heimdall] Memoize %s", tostring(x))) end
|
||||||
|
local r = mem[x]
|
||||||
|
if r == nil then -- no previous result?
|
||||||
|
if Heimdall_Data.config.debug then
|
||||||
|
print(string.format("[Heimdall] Memoize %s is nil, calling original function", tostring(x)))
|
||||||
|
end
|
||||||
|
r = f(x) -- calls original function
|
||||||
|
if Heimdall_Data.config.debug then
|
||||||
|
print(string.format("[Heimdall] Memoized result for %s: %s", tostring(x), tostring(r)))
|
||||||
|
end
|
||||||
|
mem[x] = r -- store result for reuse
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.debug then
|
||||||
|
print(string.format("[Heimdall] Memoize %s is %s", tostring(x), tostring(r)))
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
---@class HeimdallEchoerConfig
|
---@param channel string
|
||||||
---@field enabled boolean
|
---@return string
|
||||||
---@field masterChannel string
|
shared.GetLocaleForChannel = function(channel) return Heimdall_Data.config.channelLocale[channel] or "en" end
|
||||||
---@field prefix string
|
|
||||||
|
|
||||||
---@class HeimdallMacroerConfig
|
---@param key string
|
||||||
---@field enabled boolean
|
---@param locale string
|
||||||
---@field priority string[]
|
---@return string
|
||||||
|
shared._L = function(key, locale)
|
||||||
|
local localeTable = shared._Locale[locale]
|
||||||
|
if not localeTable then
|
||||||
|
if Heimdall_Data.config.debug then
|
||||||
|
print(string.format("[Heimdall] Locale %s not found", tostring(locale)))
|
||||||
|
end
|
||||||
|
return key
|
||||||
|
end
|
||||||
|
local value = localeTable[key]
|
||||||
|
if not value then
|
||||||
|
if Heimdall_Data.config.debug then
|
||||||
|
print(string.format("[Heimdall] Key %s not found in locale %s", tostring(key), tostring(locale)))
|
||||||
|
end
|
||||||
|
return key
|
||||||
|
end
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
---@class HeimdallCommanderConfig
|
shared.Messenger.Init()
|
||||||
---@field enabled boolean
|
shared.StinkyTracker.Init()
|
||||||
---@field masterChannel string
|
shared.AgentTracker.Init()
|
||||||
---@field commander string
|
shared.Whoer.Init()
|
||||||
---@field commands table<string, boolean>
|
shared.Spotter.Init()
|
||||||
|
shared.DeathReporter.Init()
|
||||||
---@class HeimdallStinkyTrackerConfig
|
shared.Inviter.Init()
|
||||||
---@field enabled boolean
|
shared.Dueler.Init()
|
||||||
---@field masterChannel string
|
shared.Bully.Init()
|
||||||
|
shared.Macroer.Init()
|
||||||
---@class HeimdallCombatAlerterConfig
|
shared.Commander.Init()
|
||||||
---@field enabled boolean
|
shared.CombatAlerter.Init()
|
||||||
---@field masterChannel string
|
shared.Config.Init()
|
||||||
|
shared.MinimapTagger.Init()
|
||||||
--- Data ---
|
shared.BonkDetector.Init()
|
||||||
---@class HeimdallMessengerData
|
shared.Sniffer.Init()
|
||||||
---@field queue table<string, Message>
|
shared.Noter.Init()
|
||||||
---@field ticker number?
|
shared.Network.Init()
|
||||||
|
shared.NetworkMessenger.Init()
|
||||||
---@class HeimdallWhoData
|
shared.Configurator.Init()
|
||||||
---@field updateTicker number?
|
shared.StinkyCache.Init()
|
||||||
---@field whoTicker number?
|
shared.AchievementSniffer.Init()
|
||||||
---@field ignored table<string, boolean>
|
shared.ChatSniffer.Init()
|
||||||
|
print("Heimdall loaded!")
|
||||||
---@class HeimdallStinkyTrackerData
|
|
||||||
---@field stinkies ReactiveValue
|
|
||||||
|
|
||||||
shared.GetOrDefault = function(table, keys, default)
|
|
||||||
local value = default
|
|
||||||
if not table then return value end
|
|
||||||
if not keys then return value end
|
|
||||||
|
|
||||||
local traverse = table
|
|
||||||
for i = 1, #keys do
|
|
||||||
local key = keys[i]
|
|
||||||
if traverse[key] ~= nil then
|
|
||||||
traverse = traverse[key]
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
if i == #keys then
|
|
||||||
value = traverse
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
|
|
||||||
shared.messenger = {
|
|
||||||
queue = {}
|
|
||||||
}
|
|
||||||
shared.who = {
|
|
||||||
ignored = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
Heimdall_Data.config = {
|
|
||||||
spotter = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "enabled" }, true),
|
|
||||||
everyone = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "everyone" }, false),
|
|
||||||
hostile = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "hostile" }, true),
|
|
||||||
alliance = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "alliance" }, true),
|
|
||||||
stinky = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "stinky" }, true),
|
|
||||||
notifyChannel = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "notifyChannel" }, "Agent"),
|
|
||||||
zoneOverride = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "zoneOverride" }, nil),
|
|
||||||
throttleTime = shared.GetOrDefault(Heimdall_Data, { "config", "spotter", "throttleTime" }, 10)
|
|
||||||
},
|
|
||||||
who = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "who", "enabled" }, false),
|
|
||||||
ignored = shared.GetOrDefault(Heimdall_Data, { "config", "who", "ignored" }, {}),
|
|
||||||
notifyChannel = shared.GetOrDefault(Heimdall_Data, { "config", "who", "notifyChannel" }, "Agent"),
|
|
||||||
ttl = shared.GetOrDefault(Heimdall_Data, { "config", "who", "ttl" }, 20),
|
|
||||||
doWhisper = shared.GetOrDefault(Heimdall_Data, { "config", "who", "doWhisper" }, true),
|
|
||||||
zoneNotifyFor = shared.GetOrDefault(Heimdall_Data, { "config", "who", "zoneNotifyFor" }, {
|
|
||||||
["Orgrimmar"] = true,
|
|
||||||
["Thunder Bluff"] = true,
|
|
||||||
["Undercity"] = true,
|
|
||||||
["Durotar"] = true,
|
|
||||||
["Echo Isles"] = true,
|
|
||||||
["Valley of Trials"] = true,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
messenger = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "messenger", "enabled" }, true),
|
|
||||||
interval = shared.GetOrDefault(Heimdall_Data, { "config", "messenger", "interval" }, 0.2),
|
|
||||||
},
|
|
||||||
deathReporter = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "enabled" }, false),
|
|
||||||
throttle = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "throttle" }, 10),
|
|
||||||
doWhisper = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "doWhisper" }, true),
|
|
||||||
notifyChannel = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "notifyChannel" }, "Agent"),
|
|
||||||
zoneOverride = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "zoneOverride" }, nil),
|
|
||||||
duelThrottle = shared.GetOrDefault(Heimdall_Data, { "config", "deathReporter", "duelThrottle" }, 5),
|
|
||||||
},
|
|
||||||
whisperNotify = shared.GetOrDefault(Heimdall_Data, { "config", "whisperNotify" }, {}),
|
|
||||||
stinkies = shared.GetOrDefault(Heimdall_Data, { "config", "stinkies" }, {}),
|
|
||||||
inviter = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "enabled" }, false),
|
|
||||||
listeningChannel = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "listeningChannel" }, "Agent"),
|
|
||||||
keyword = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "keyword" }, "+"),
|
|
||||||
allAssist = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "allAssist" }, false),
|
|
||||||
agentsAssist = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "agentsAssist" }, false),
|
|
||||||
throttle = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "throttle" }, 1),
|
|
||||||
kickOffline = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "kickOffline" }, false),
|
|
||||||
cleanupInterval = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "cleanupInterval" }, 10),
|
|
||||||
afkThreshold = shared.GetOrDefault(Heimdall_Data, { "config", "inviter", "afkThreshold" }, 300),
|
|
||||||
},
|
|
||||||
dueler = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "dueler", "enabled" }, false),
|
|
||||||
declineOther = shared.GetOrDefault(Heimdall_Data, { "config", "dueler", "declineOther" }, false),
|
|
||||||
},
|
|
||||||
bully = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "bully", "enabled" }, false),
|
|
||||||
},
|
|
||||||
agentTracker = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "agentTracker", "enabled" }, false),
|
|
||||||
masterChannel = shared.GetOrDefault(Heimdall_Data, { "config", "agentTracker", "masterChannel" }, "Agent"),
|
|
||||||
},
|
|
||||||
emoter = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "emoter", "enabled" }, false),
|
|
||||||
masterChannel = shared.GetOrDefault(Heimdall_Data, { "config", "emoter", "masterChannel" }, "Agent"),
|
|
||||||
prefix = shared.GetOrDefault(Heimdall_Data, { "config", "emoter", "prefix" }, ""),
|
|
||||||
},
|
|
||||||
echoer = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "echoer", "enabled" }, false),
|
|
||||||
masterChannel = shared.GetOrDefault(Heimdall_Data, { "config", "echoer", "masterChannel" }, "Agent"),
|
|
||||||
prefix = shared.GetOrDefault(Heimdall_Data, { "config", "echoer", "prefix" }, ""),
|
|
||||||
},
|
|
||||||
macroer = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "macroer", "enabled" }, false),
|
|
||||||
priority = shared.GetOrDefault(Heimdall_Data, { "config", "macroer", "priority" }, {}),
|
|
||||||
},
|
|
||||||
agents = shared.GetOrDefault(Heimdall_Data, { "config", "agents" }, {}),
|
|
||||||
commander = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "commander", "enabled" }, false),
|
|
||||||
masterChannel = shared.GetOrDefault(Heimdall_Data, { "config", "commander", "masterChannel" }, "Agent"),
|
|
||||||
commander = shared.GetOrDefault(Heimdall_Data, { "config", "commander", "commander" }, "Heimdállr"),
|
|
||||||
commands = shared.GetOrDefault(Heimdall_Data, { "config", "commander", "commands" }, {}),
|
|
||||||
},
|
|
||||||
stinkyTracker = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "enabled" }, false),
|
|
||||||
masterChannel = shared.GetOrDefault(Heimdall_Data, { "config", "stinkyTracker", "masterChannel" }, "Agent"),
|
|
||||||
},
|
|
||||||
combatAlerter = {
|
|
||||||
enabled = shared.GetOrDefault(Heimdall_Data, { "config", "combatAlerter", "enabled" }, false),
|
|
||||||
masterChannel = shared.GetOrDefault(Heimdall_Data, { "config", "combatAlerter", "masterChannel" }, "Agent"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
shared.raceMap = {
|
|
||||||
["Orc"] = "Horde",
|
|
||||||
["Undead"] = "Horde",
|
|
||||||
["Tauren"] = "Horde",
|
|
||||||
["Troll"] = "Horde",
|
|
||||||
["Blood Elf"] = "Horde",
|
|
||||||
["Goblin"] = "Horde",
|
|
||||||
["Human"] = "Alliance",
|
|
||||||
["Dwarf"] = "Alliance",
|
|
||||||
["Night Elf"] = "Alliance",
|
|
||||||
["Gnome"] = "Alliance",
|
|
||||||
["Draenei"] = "Alliance",
|
|
||||||
["Worgen"] = "Alliance",
|
|
||||||
["Vulpera"] = "Horde",
|
|
||||||
["Nightborne"] = "Horde",
|
|
||||||
["Zandalari Troll"] = "Horde",
|
|
||||||
["Kul Tiran"] = "Alliance",
|
|
||||||
["Dark Iron Dwarf"] = "Alliance",
|
|
||||||
["Void Elf"] = "Alliance",
|
|
||||||
["Lightforged Draenei"] = "Alliance",
|
|
||||||
["Mechagnome"] = "Alliance",
|
|
||||||
["Mag'har Orc"] = "Horde"
|
|
||||||
}
|
|
||||||
|
|
||||||
shared.classColors = {
|
|
||||||
["Warrior"] = "C69B6D",
|
|
||||||
["Paladin"] = "F48CBA",
|
|
||||||
["Hunter"] = "AAD372",
|
|
||||||
["Rogue"] = "FFF468",
|
|
||||||
["Priest"] = "FFFFFF",
|
|
||||||
["Death Knight"] = "C41E3A",
|
|
||||||
["Shaman"] = "0070DD",
|
|
||||||
["Mage"] = "3FC7EB",
|
|
||||||
["Warlock"] = "8788EE",
|
|
||||||
["Monk"] = "00FF98",
|
|
||||||
["Druid"] = "FF7C0A",
|
|
||||||
["Demon Hunter"] = "A330C9"
|
|
||||||
}
|
|
||||||
|
|
||||||
---@param input string
|
|
||||||
---@return number
|
|
||||||
shared.utf8len = function(input)
|
|
||||||
if not input then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
local len = 0
|
|
||||||
local i = 1
|
|
||||||
local n = #input
|
|
||||||
while i <= n do
|
|
||||||
local c = input:byte(i)
|
|
||||||
if c >= 0 and c <= 127 then
|
|
||||||
i = i + 1
|
|
||||||
elseif c >= 194 and c <= 223 then
|
|
||||||
i = i + 2
|
|
||||||
elseif c >= 224 and c <= 239 then
|
|
||||||
i = i + 3
|
|
||||||
elseif c >= 240 and c <= 244 then
|
|
||||||
i = i + 4
|
|
||||||
else
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
len = len + 1
|
|
||||||
end
|
|
||||||
return len
|
|
||||||
end
|
|
||||||
---@param input string
|
|
||||||
---@param targetLength number
|
|
||||||
---@param left boolean
|
|
||||||
---@return string
|
|
||||||
shared.padString = function(input, targetLength, left)
|
|
||||||
left = left or false
|
|
||||||
local len = shared.utf8len(input)
|
|
||||||
if len < targetLength then
|
|
||||||
if left then
|
|
||||||
input = input .. string.rep(" ", targetLength - len)
|
|
||||||
else
|
|
||||||
input = string.rep(" ", targetLength - len) .. input
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return input
|
|
||||||
end
|
|
||||||
|
|
||||||
shared.Messenger.Init()
|
|
||||||
shared.StinkyTracker.Init()
|
|
||||||
shared.AgentTracker.Init()
|
|
||||||
shared.Whoer.Init()
|
|
||||||
shared.Spotter.Init()
|
|
||||||
shared.DeathReporter.Init()
|
|
||||||
shared.Inviter.Init()
|
|
||||||
shared.Dueler.Init()
|
|
||||||
shared.Bully.Init()
|
|
||||||
shared.Macroer.Init()
|
|
||||||
shared.Commander.Init()
|
|
||||||
shared.CombatAlerter.Init()
|
|
||||||
shared.Config.Init()
|
|
||||||
print("Heimdall loaded!")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local loadedFrame = CreateFrame("Frame")
|
local loadedFrame = CreateFrame("Frame")
|
||||||
loadedFrame:RegisterEvent("ADDON_LOADED")
|
loadedFrame:RegisterEvent("ADDON_LOADED")
|
||||||
loadedFrame:SetScript("OnEvent", function(self, event, addonName)
|
loadedFrame:SetScript("OnEvent", function(self, event, addonName)
|
||||||
if addonName == addonname then
|
if addonName == addonname then init() end
|
||||||
init()
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- Create the import/export frame
|
||||||
|
local ccpFrame = CreateFrame("Frame", "CCPFrame", UIParent)
|
||||||
|
ccpFrame:SetSize(512 * 1.5, 512 * 1.5)
|
||||||
|
ccpFrame:SetPoint("CENTER")
|
||||||
|
ccpFrame:SetFrameStrata("HIGH")
|
||||||
|
ccpFrame:EnableMouse(true)
|
||||||
|
ccpFrame:SetMovable(true)
|
||||||
|
ccpFrame:SetResizable(false)
|
||||||
|
ccpFrame:SetBackdrop({
|
||||||
|
bgFile = "Interface/Tooltips/UI-Tooltip-Background",
|
||||||
|
edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
|
||||||
|
tile = true,
|
||||||
|
tileSize = 4,
|
||||||
|
edgeSize = 4,
|
||||||
|
insets = {
|
||||||
|
left = 4,
|
||||||
|
right = 4,
|
||||||
|
top = 4,
|
||||||
|
bottom = 4,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ccpFrame:SetBackdropColor(0, 0, 0, 0.8)
|
||||||
|
ccpFrame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
|
||||||
|
|
||||||
|
ccpFrame:SetMovable(true)
|
||||||
|
ccpFrame:EnableMouse(true)
|
||||||
|
ccpFrame:RegisterForDrag("LeftButton")
|
||||||
|
ccpFrame:SetScript("OnDragStart", function(self) self:StartMoving() end)
|
||||||
|
ccpFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
|
||||||
|
ccpFrame:SetScript("OnShow", function(self) self:SetScale(1) end)
|
||||||
|
ccpFrame:Hide()
|
||||||
|
|
||||||
|
-- Create scroll frame
|
||||||
|
local scrollFrame = CreateFrame("ScrollFrame", "CCPFrameScrollFrame", ccpFrame, "UIPanelScrollFrameTemplate")
|
||||||
|
scrollFrame:SetPoint("TOPLEFT", ccpFrame, "TOPLEFT", 10, -10)
|
||||||
|
scrollFrame:SetPoint("BOTTOMRIGHT", ccpFrame, "BOTTOMRIGHT", -30, 10)
|
||||||
|
|
||||||
|
-- Create the text box
|
||||||
|
local ccpFrameTextBox = CreateFrame("EditBox", "CCPFrameTextBox", scrollFrame)
|
||||||
|
ccpFrameTextBox:SetSize(512 * 1.5 - 40, 512 * 1.5 - 20)
|
||||||
|
ccpFrameTextBox:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0)
|
||||||
|
ccpFrameTextBox:SetFont("Fonts\\FRIZQT__.ttf", 12)
|
||||||
|
ccpFrameTextBox:SetTextColor(1, 1, 1, 1)
|
||||||
|
ccpFrameTextBox:SetTextInsets(10, 10, 10, 10)
|
||||||
|
ccpFrameTextBox:SetMultiLine(true)
|
||||||
|
ccpFrameTextBox:SetAutoFocus(true)
|
||||||
|
ccpFrameTextBox:SetMaxLetters(1000000)
|
||||||
|
ccpFrameTextBox:SetScript("OnEscapePressed", function(self) ccpFrame:Hide() end)
|
||||||
|
|
||||||
|
-- Set the scroll frame's scroll child
|
||||||
|
scrollFrame:SetScrollChild(ccpFrameTextBox)
|
||||||
|
|
||||||
|
CCP = function(window)
|
||||||
|
window = window or 1
|
||||||
|
local charFrame = _G["ChatFrame" .. window]
|
||||||
|
local maxLines = charFrame:GetNumMessages() or 0
|
||||||
|
local chat = {}
|
||||||
|
for i = 1, maxLines do
|
||||||
|
local currentMsg = charFrame:GetMessageInfo(i)
|
||||||
|
chat[#chat + 1] = currentMsg
|
||||||
|
end
|
||||||
|
ccpFrameTextBox:SetText(table.concat(chat, "\n"))
|
||||||
|
ccpFrame:Show()
|
||||||
|
ccpFrameTextBox:SetFocus()
|
||||||
|
end
|
||||||
|
16
Heimdall.toc
16
Heimdall.toc
@@ -1,17 +1,18 @@
|
|||||||
## Interface: 70300
|
## Interface: 70300
|
||||||
## Title: Heimdall
|
## Title: Heimdall
|
||||||
## Version: 3.0.0
|
## 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
|
## SavedVariables: Heimdall_Data, Heimdall_Achievements, Heimdall_Chat
|
||||||
|
|
||||||
#core
|
_L.lua
|
||||||
Modules/CLEUParser.lua
|
Modules/CLEUParser.lua
|
||||||
Modules/ReactiveValue.lua
|
Modules/ReactiveValue.lua
|
||||||
Modules/DumpTable.lua
|
Modules/DumpTable.lua
|
||||||
Modules/Spotter.lua
|
Modules/Spotter.lua
|
||||||
Modules/Whoer.lua
|
Modules/Whoer.lua
|
||||||
Modules/Messenger.lua
|
Modules/Messenger.lua
|
||||||
|
Modules/Network.lua
|
||||||
Modules/DeathReporter.lua
|
Modules/DeathReporter.lua
|
||||||
Modules/Inviter.lua
|
Modules/Inviter.lua
|
||||||
Modules/Dueler.lua
|
Modules/Dueler.lua
|
||||||
@@ -23,5 +24,14 @@ Modules/Macroer.lua
|
|||||||
Modules/Commander.lua
|
Modules/Commander.lua
|
||||||
Modules/StinkyTracker.lua
|
Modules/StinkyTracker.lua
|
||||||
Modules/CombatAlerter.lua
|
Modules/CombatAlerter.lua
|
||||||
|
Modules/MinimapTagger.lua
|
||||||
Modules/Config.lua
|
Modules/Config.lua
|
||||||
|
Modules/BonkDetector.lua
|
||||||
|
Modules/Sniffer.lua
|
||||||
|
Modules/Noter.lua
|
||||||
|
Modules/NetworkMessenger.lua
|
||||||
|
Modules/StinkyCache.lua
|
||||||
|
Modules/Configurator.lua
|
||||||
|
Modules/AchievementSniffer.lua
|
||||||
|
Modules/ChatSniffer.lua
|
||||||
Heimdall.lua
|
Heimdall.lua
|
BIN
Heimdall.zip
(Stored with Git LFS)
BIN
Heimdall.zip
(Stored with Git LFS)
Binary file not shown.
1
Meta
Submodule
1
Meta
Submodule
Submodule Meta added at eee043a846
304
Modules/AchievementSniffer.lua
Normal file
304
Modules/AchievementSniffer.lua
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
local _, shared = ...
|
||||||
|
---@cast shared HeimdallShared
|
||||||
|
local ModuleName = "AchievementSniffer"
|
||||||
|
|
||||||
|
---@class HeimdallAchievementSnifferConfig
|
||||||
|
---@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 = {
|
||||||
|
15,
|
||||||
|
958,
|
||||||
|
1266,
|
||||||
|
2078,
|
||||||
|
2141,
|
||||||
|
2200,
|
||||||
|
4958,
|
||||||
|
5456,
|
||||||
|
5749,
|
||||||
|
6460,
|
||||||
|
6753,
|
||||||
|
7382,
|
||||||
|
7383,
|
||||||
|
7384,
|
||||||
|
8929,
|
||||||
|
8982,
|
||||||
|
9017,
|
||||||
|
9038,
|
||||||
|
9493,
|
||||||
|
10059,
|
||||||
|
10079,
|
||||||
|
10278,
|
||||||
|
10657,
|
||||||
|
10672,
|
||||||
|
10684,
|
||||||
|
10688,
|
||||||
|
10689,
|
||||||
|
10692,
|
||||||
|
10693,
|
||||||
|
10698,
|
||||||
|
10790,
|
||||||
|
10875,
|
||||||
|
11124,
|
||||||
|
11126,
|
||||||
|
11127,
|
||||||
|
11128,
|
||||||
|
11153,
|
||||||
|
11157,
|
||||||
|
11164,
|
||||||
|
11188,
|
||||||
|
11189,
|
||||||
|
11190,
|
||||||
|
11446,
|
||||||
|
11473,
|
||||||
|
11610,
|
||||||
|
11657,
|
||||||
|
11658,
|
||||||
|
11659,
|
||||||
|
11660,
|
||||||
|
11674,
|
||||||
|
11992,
|
||||||
|
11993,
|
||||||
|
11994,
|
||||||
|
11995,
|
||||||
|
11996,
|
||||||
|
11997,
|
||||||
|
11998,
|
||||||
|
11999,
|
||||||
|
12000,
|
||||||
|
12001,
|
||||||
|
12020,
|
||||||
|
12026,
|
||||||
|
12074,
|
||||||
|
12445,
|
||||||
|
12447,
|
||||||
|
12448,
|
||||||
|
}
|
||||||
|
|
||||||
|
---@class AchievementSniffer
|
||||||
|
shared.AchievementSniffer = {
|
||||||
|
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
|
||||||
|
print(string.format("[%s] Scanning achievements for %s", ModuleName, name))
|
||||||
|
end
|
||||||
|
Heimdall_Achievements.players[name] = {}
|
||||||
|
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)
|
||||||
|
|
||||||
|
local data = {
|
||||||
|
id = aid,
|
||||||
|
date = date,
|
||||||
|
completed = completed,
|
||||||
|
}
|
||||||
|
if Heimdall_Data.config.achievementSniffer.debug then
|
||||||
|
print(string.format("[%s] Achievement %d completed on %s", ModuleName, aid, date))
|
||||||
|
end
|
||||||
|
Heimdall_Achievements.players[name][aid] = data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--UpdateFrames()
|
||||||
|
end
|
||||||
|
|
||||||
|
local nameplateFrame = CreateFrame("Frame")
|
||||||
|
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,
|
||||||
|
}
|
@@ -1,39 +1,144 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
local ModuleName = "AgentTracker"
|
||||||
|
|
||||||
---@diagnostic disable-next-line: missing-fields
|
---@class AgentTrackerData
|
||||||
shared.AgentTracker = {}
|
---@field agents ReactiveValue<table<string, string>>
|
||||||
function shared.AgentTracker.Init()
|
|
||||||
---@type table<string, boolean>
|
---@class HeimdallAgentTrackerConfig
|
||||||
local channelRosterFrame = CreateFrame("Frame")
|
---@field enabled boolean
|
||||||
channelRosterFrame:RegisterEvent("CHANNEL_ROSTER_UPDATE")
|
---@field debug boolean
|
||||||
channelRosterFrame:SetScript("OnEvent", function(self, event, index)
|
---@field channels string[]
|
||||||
if not Heimdall_Data.config.agentTracker.enabled then return end
|
|
||||||
local name = GetChannelDisplayInfo(index)
|
---@class AgentTracker
|
||||||
if name ~= Heimdall_Data.config.agentTracker.masterChannel then return end
|
shared.AgentTracker = {
|
||||||
local count = select(5, GetChannelDisplayInfo(index))
|
---@param name string
|
||||||
for i = 1, count do
|
---@return boolean
|
||||||
local name = GetChannelRosterInfo(index, i)
|
Track = function(name)
|
||||||
if name then
|
if not name then return false end
|
||||||
Heimdall_Data.config.agents[name] = date("%Y-%m-%dT%H:%M:%S")
|
local exists = shared.AgentTracker.IsAgent(name)
|
||||||
end
|
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
|
end
|
||||||
--shared.dumpTable(Heimdall_Data.config.agents)
|
return true
|
||||||
end)
|
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),
|
||||||
|
}
|
||||||
|
|
||||||
local agentTrackerChannelSniffer = CreateFrame("Frame")
|
--/run Heimdall_Data.config.agents["Cyheuraeth"]=date("%Y-%m-%dT%H:%M:%S")
|
||||||
agentTrackerChannelSniffer:RegisterEvent("CHAT_MSG_CHANNEL")
|
---@type table<string, boolean>
|
||||||
agentTrackerChannelSniffer:SetScript("OnEvent", function(self, event, msg, sender, ...)
|
local channelRosterFrame = CreateFrame("Frame")
|
||||||
if not Heimdall_Data.config.agentTracker.enabled then return end
|
channelRosterFrame:RegisterEvent("CHANNEL_ROSTER_UPDATE")
|
||||||
local channelId = select(6, ...)
|
channelRosterFrame:SetScript("OnEvent", function(self, event, index)
|
||||||
local channelname = GetChannelName(channelId)
|
if Heimdall_Data.config.agentTracker.debug then
|
||||||
if not channelname then return end
|
print(string.format("[%s] Channel roster update received", ModuleName))
|
||||||
if channelname ~= Heimdall_Data.config.who.notifyChannel then return end
|
end
|
||||||
local agentName = sender
|
if not Heimdall_Data.config.agentTracker.enabled then
|
||||||
if not agentName then return end
|
if Heimdall_Data.config.agentTracker.debug then
|
||||||
Heimdall_Data.config.agents[agentName] = date("%Y-%m-%dT%H:%M:%S")
|
print(string.format("[%s] Module disabled, ignoring roster update", ModuleName))
|
||||||
end)
|
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
|
||||||
|
|
||||||
print("Heimdall - AgentTracker loaded")
|
local newAgents = 0
|
||||||
end
|
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
|
||||||
|
if not ok then
|
||||||
|
if Heimdall_Data.config.agentTracker.debug then
|
||||||
|
print(string.format("[%s] Channel name does not match any of the channels", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.agentTracker.debug then
|
||||||
|
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
|
||||||
|
end
|
||||||
|
|
||||||
|
sender = string.match(sender, "^[^-]+")
|
||||||
|
local new = shared.AgentTracker.Track(sender)
|
||||||
|
|
||||||
|
if Heimdall_Data.config.agentTracker.debug then
|
||||||
|
print(
|
||||||
|
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
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
shared.dump(shared.agentTracker.agents:get(), "Agents")
|
||||||
|
end
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
142
Modules/BonkDetector.lua
Normal file
142
Modules/BonkDetector.lua
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
local _, shared = ...
|
||||||
|
---@cast shared HeimdallShared
|
||||||
|
local ModuleName = "BonkDetector"
|
||||||
|
|
||||||
|
---@class HeimdallBonkDetectorConfig
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field debug boolean
|
||||||
|
---@field channels string[]
|
||||||
|
---@field throttle number
|
||||||
|
|
||||||
|
---@class BonkDetector
|
||||||
|
shared.BonkDetector = {
|
||||||
|
---@return nil
|
||||||
|
Init = function()
|
||||||
|
---@type table<string, number>
|
||||||
|
local lastReportTime = {}
|
||||||
|
|
||||||
|
local frame = CreateFrame("Frame")
|
||||||
|
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
|
||||||
|
frame:SetScript("OnEvent", function(self, event, ...)
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
local subevent = select(2, ...)
|
||||||
|
if not subevent:find("_DAMAGE") then
|
||||||
|
if Heimdall_Data.config.bonkDetector.debug then
|
||||||
|
print(string.format("[%s] Not a damage event, ignoring: %s", ModuleName, subevent))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type string|nil, string, string, string, string
|
||||||
|
local err, source, sourceGUID, destination, destinationGUID
|
||||||
|
|
||||||
|
source, err = CLEUParser.GetSourceName(...)
|
||||||
|
if err then
|
||||||
|
if Heimdall_Data.config.bonkDetector.debug then
|
||||||
|
print(string.format("[%s] Error getting source name: %s", ModuleName, err))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
sourceGUID, err = CLEUParser.GetSourceGUID(...)
|
||||||
|
if err then
|
||||||
|
if Heimdall_Data.config.bonkDetector.debug then
|
||||||
|
print(string.format("[%s] Error getting source GUID: %s", ModuleName, err))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not string.find(sourceGUID, "Player") then
|
||||||
|
if Heimdall_Data.config.bonkDetector.debug then
|
||||||
|
print(string.format("[%s] Source %s is not a player, nothing to do", ModuleName, source))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
destination, err = CLEUParser.GetDestName(...)
|
||||||
|
if err then
|
||||||
|
if Heimdall_Data.config.bonkDetector.debug then
|
||||||
|
print(string.format("[%s] Error getting destination name: %s", ModuleName, err))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
destinationGUID, err = CLEUParser.GetDestGUID(...)
|
||||||
|
if err then
|
||||||
|
if Heimdall_Data.config.bonkDetector.debug then
|
||||||
|
print(string.format("[%s] Error getting destination GUID: %s", ModuleName, err))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not string.find(destinationGUID, "Player") then
|
||||||
|
if Heimdall_Data.config.bonkDetector.debug then
|
||||||
|
print(string.format("[%s] Destination %s is not a player, nothing to do", ModuleName, destination))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if source == destination then
|
||||||
|
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,
|
||||||
|
}
|
@@ -1,9 +1,16 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
local ModuleName = "Bully"
|
||||||
|
|
||||||
---@diagnostic disable-next-line: missing-fields
|
---@class HeimdallBullyConfig
|
||||||
shared.Bully = {}
|
---@field enabled boolean
|
||||||
function shared.Bully.Init()
|
---@field debug boolean
|
||||||
print("Heimdall - Bully loaded")
|
|
||||||
end
|
---@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))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
@@ -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
49
Modules/ChatSniffer.lua
Normal 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,
|
||||||
|
}
|
@@ -1,49 +1,141 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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")
|
|
||||||
combatAlerterFrame:SetScript("OnEvent", function(self, event, ...)
|
|
||||||
if not Heimdall_Data.config.combatAlerter.enabled then return end
|
|
||||||
local destination, err = CLEUParser.GetDestName(...)
|
|
||||||
if err then return end
|
|
||||||
if destination ~= UnitName("player") then return end
|
|
||||||
local source, err = CLEUParser.GetSourceName(...)
|
|
||||||
if err then source = "unknown" end
|
|
||||||
|
|
||||||
if shared.stinkyTracker.stinkies and shared.stinkyTracker.stinkies[source] then
|
---@class CombatAlerter
|
||||||
if alerted[source] then return end
|
shared.CombatAlerter = {
|
||||||
alerted[source] = true
|
Init = function()
|
||||||
local x, y = GetPlayerMapPosition("player")
|
local alerted = {}
|
||||||
---@type Message
|
local combatAlerterFrame = CreateFrame("Frame")
|
||||||
local msg = {
|
combatAlerterFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
|
||||||
channel = "CHANNEL",
|
combatAlerterFrame:SetScript("OnEvent", function(self, event, ...)
|
||||||
data = Heimdall_Data.config.combatAlerter.masterChannel,
|
if Heimdall_Data.config.combatAlerter.debug then
|
||||||
message = string.format("%s is attacking me in %s(%s) at %2.2f,%2.2f ",
|
print(string.format("[%s] Combat log event received", ModuleName))
|
||||||
source,
|
end
|
||||||
GetZoneText(), GetSubZoneText(),
|
if not Heimdall_Data.config.combatAlerter.enabled then
|
||||||
x * 100, y * 100
|
if Heimdall_Data.config.combatAlerter.debug then
|
||||||
),
|
print(string.format("[%s] Module disabled, ignoring combat event", ModuleName))
|
||||||
}
|
end
|
||||||
table.insert(shared.messenger.queue, msg)
|
return
|
||||||
end
|
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")
|
|
||||||
-- We want to only alert once per target per combat encounter
|
|
||||||
-- Even a small throttle would probably spam too much here
|
|
||||||
-- ....but maybe we can call it a 120 second throttle or something?
|
|
||||||
-- We will see
|
|
||||||
combatTriggerFrame:SetScript("OnEvent", function(self, event, ...)
|
|
||||||
alerted = {}
|
|
||||||
end)
|
|
||||||
|
|
||||||
print("Heimdall - CombatAlerter loaded")
|
destination, err = CLEUParser.GetDestName(...)
|
||||||
end
|
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
|
||||||
|
|
||||||
|
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))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
@@ -1,14 +1,27 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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 - пишет вам никнеймы текущих врагов и локу.",
|
||||||
"2) classes = пишет какие классы. (например 1 паладин 1 прист 1 рога.",
|
"2) classes - покажет классы врагов и число.",
|
||||||
"3) howmany - общее число врагов (например дуротар 4 . оргримар 2 ) ",
|
"3) howmany - общее число врагов (дурик 4 . огри 2 ) ",
|
||||||
"4) + - автоматическое приглашение в группу с дуельным рогой для сброса кд и общего сбора. ",
|
"4) + - атоинвайт в сбор пати и сброса кд.",
|
||||||
"5) ++ - автоматическое приглашение в группу альянса. (если нужен рефрак)",
|
"5) ++ -автоинвайт в пати аликов (если нужен рефрак)",
|
||||||
|
"6 ) note Никнейм текст - добавление заметки.",
|
||||||
|
"7) note Никнейм - посмотреть последние заметки.",
|
||||||
|
"8) note Никнейм 5 - посмотреть конкретную заметку.",
|
||||||
|
"9) note Никнейм 1..5 - посмотреть заметки от 1 до 5",
|
||||||
|
"10) note Никнейм delete 1 - удалить заметку номер 1",
|
||||||
|
"11) note Никнейм delete 1..5 - удалить заметки 1 до 5",
|
||||||
},
|
},
|
||||||
en = {
|
en = {
|
||||||
"1) who - reports currently tracked stinkies in orgrimmar and durotar",
|
"1) who - reports currently tracked stinkies in orgrimmar and durotar",
|
||||||
@@ -16,208 +29,410 @@ local helpMessages = {
|
|||||||
"3) howmany - reports stinkies grouped by zone",
|
"3) howmany - reports stinkies grouped by zone",
|
||||||
"4) + - automatically invites to group with duel rogue for cd reset",
|
"4) + - automatically invites to group with duel rogue for cd reset",
|
||||||
"5) ++ - automatically invites to alliance group",
|
"5) ++ - automatically invites to alliance group",
|
||||||
}
|
"6) note <name> <note> - adds a note for the specified character.",
|
||||||
|
"7) note <name> - lists the last N notes for the character.",
|
||||||
|
"8) note <name> i - lists the i-th note 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.",
|
||||||
|
"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 Heimdall_Data.config.who.zoneNotifyFor[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 Heimdall_Data.config.who.zoneNotifyFor[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
|
||||||
return ret
|
---@param arr table<string, Player>
|
||||||
end
|
---@return string[]
|
||||||
---@param arr table<string, Player>
|
local function CountPartitioned(arr)
|
||||||
---@return string[]
|
local count = Count(arr)
|
||||||
local function WhoPartitioned(arr)
|
local text = {}
|
||||||
local who = Who(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(count)), 200)) do
|
||||||
for _, line in pairs(Partition(strjoin(", ", unpack(who)), 200)) do
|
text[#text + 1] = line
|
||||||
text[#text + 1] = 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 Heimdall_Data.config.who.zoneNotifyFor[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
|
||||||
return text
|
-- This is really ugly, duplicating methods like this
|
||||||
end
|
-- But I have no better idea
|
||||||
---@param arr table<string, Player>
|
-- We would have to drag reference channel all the way here
|
||||||
---@return string[]
|
-- And then in here do some kind of deciding based on the fucking channel locale
|
||||||
local function CountClassPartitioned(arr)
|
-- That's also a nasty solution... I guess adding "kto" is better
|
||||||
local countClass = CountClass(arr)
|
---@param arr table<string, Player>
|
||||||
local text = {}
|
---@return string[]
|
||||||
for _, line in pairs(Partition(strjoin(", ", unpack(countClass)), 200)) do
|
local function WhoRu(arr)
|
||||||
text[#text + 1] = line
|
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[]
|
||||||
local function CountClassPartitionedStinkies()
|
local function WhoPartitioned(arr)
|
||||||
local res = CountClassPartitioned(HeimdallStinkies)
|
local who = Who(arr)
|
||||||
if #res == 0 then
|
local text = {}
|
||||||
return { "No stinkies found" }
|
---@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] = "who: " .. line
|
||||||
|
end
|
||||||
|
return text
|
||||||
end
|
end
|
||||||
return res
|
---@param arr table<string, Player>
|
||||||
end
|
---@return string[]
|
||||||
local function WhoPartitionedStinkies()
|
local function WhoPartitionedRu(arr)
|
||||||
local res = WhoPartitioned(HeimdallStinkies)
|
local who = WhoRu(arr)
|
||||||
if #res == 0 then
|
local text = {}
|
||||||
return { "No stinkies found" }
|
---@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
|
||||||
return res
|
---@param arr table<string, Player>
|
||||||
end
|
---@return string[]
|
||||||
local function CountPartitionedStinkies()
|
local function CountClass(arr)
|
||||||
local res = CountPartitioned(HeimdallStinkies)
|
local ret = {}
|
||||||
if #res == 0 then
|
for _, player in pairs(arr) do
|
||||||
return { "No stinkies found" }
|
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 HelpRu() return helpMessages.ru end
|
local function CountClassPartitioned(arr)
|
||||||
local function HelpEn() return helpMessages.en end
|
local countClass = CountClass(arr)
|
||||||
local groupInviteFrame = CreateFrame("Frame")
|
local text = {}
|
||||||
groupInviteFrame:SetScript("OnEvent", function(self, event, ...)
|
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
|
||||||
AcceptGroup()
|
for _, line in pairs(Partition(strjoin(", ", unpack(countClass)), 200)) do
|
||||||
groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST")
|
text[#text + 1] = line
|
||||||
C_Timer.NewTimer(0.1, function()
|
end
|
||||||
_G["StaticPopup1Button1"]:Click()
|
return text
|
||||||
end, 1)
|
end
|
||||||
end)
|
local function CountClassPartitionedStinkies()
|
||||||
local function JoinGroup()
|
if Heimdall_Data.config.commander.debug then
|
||||||
groupInviteFrame:RegisterEvent("PARTY_INVITE_REQUEST")
|
print(string.format("[%s] Executing: CountClassPartitionedStinkies", ModuleName))
|
||||||
C_Timer.NewTimer(10, function()
|
end
|
||||||
|
local res = CountClassPartitioned(HeimdallStinkies)
|
||||||
|
if #res == 0 then return { "No stinkies found" } end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
local function WhoPartitionedStinkies()
|
||||||
|
if Heimdall_Data.config.commander.debug then
|
||||||
|
print(string.format("[%s] Executing: WhoPartitionedStinkies", ModuleName))
|
||||||
|
shared.dump(HeimdallStinkies)
|
||||||
|
end
|
||||||
|
local res = WhoPartitioned(HeimdallStinkies)
|
||||||
|
if #res == 0 then return { "No stinkies found" } end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
local function WhoPartitionedStinkiesRu()
|
||||||
|
if Heimdall_Data.config.commander.debug then
|
||||||
|
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
|
||||||
|
local function CountPartitionedStinkies()
|
||||||
|
if Heimdall_Data.config.commander.debug then
|
||||||
|
print(string.format("[%s] Executing: CountPartitionedStinkies", ModuleName))
|
||||||
|
end
|
||||||
|
local res = CountPartitioned(HeimdallStinkies)
|
||||||
|
if #res == 0 then return { "No stinkies found" } end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
local function HelpRu()
|
||||||
|
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpRu", ModuleName)) end
|
||||||
|
return helpMessages.ru
|
||||||
|
end
|
||||||
|
local function HelpEn()
|
||||||
|
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Executing: HelpEn", ModuleName)) end
|
||||||
|
return helpMessages.en
|
||||||
|
end
|
||||||
|
local groupInviteFrame = CreateFrame("Frame")
|
||||||
|
groupInviteFrame:SetScript("OnEvent", function(self, event, ...)
|
||||||
|
if Heimdall_Data.config.commander.debug then print(string.format("[%s] Event received", ModuleName)) end
|
||||||
|
AcceptGroup()
|
||||||
groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST")
|
groupInviteFrame:UnregisterEvent("PARTY_INVITE_REQUEST")
|
||||||
end, 1)
|
C_Timer.NewTimer(0.1, function()
|
||||||
return { "+" }
|
if Heimdall_Data.config.commander.debug then
|
||||||
end
|
print(string.format("[%s] Click event triggered", ModuleName))
|
||||||
local function LeaveGroup()
|
end
|
||||||
LeaveParty()
|
_G["StaticPopup1Button1"]:Click()
|
||||||
return {}
|
end, 1)
|
||||||
end
|
end)
|
||||||
---@param target string
|
local function JoinGroup()
|
||||||
local function FollowTarget(target)
|
if Heimdall_Data.config.commander.debug then
|
||||||
if not target then return end
|
print(string.format("[%s] JoinGroup command received", ModuleName))
|
||||||
FollowUnit(target)
|
end
|
||||||
return {}
|
groupInviteFrame:RegisterEvent("PARTY_INVITE_REQUEST")
|
||||||
end
|
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
|
||||||
|
|
||||||
---@class Command
|
---@param args string[]
|
||||||
---@field keywordRe string
|
local function MacroTarget(args)
|
||||||
---@field commanderOnly boolean
|
if Heimdall_Data.config.commander.debug then
|
||||||
---@field callback fun(...: any): string[]
|
---@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)
|
||||||
|
|
||||||
local commands = {
|
for i = 1, #args do
|
||||||
{ keywordRe = "^who", commanderOnly = false, callback = WhoPartitionedStinkies },
|
local stinky = strtrim(args[i])
|
||||||
{ keywordRe = "^howmany", commanderOnly = false, callback = CountPartitionedStinkies },
|
local name = stinky:match("([^/]+)")
|
||||||
{ keywordRe = "^classes", commanderOnly = false, callback = CountClassPartitionedStinkies },
|
local class = stinky:match("/([^ $]+)")
|
||||||
{ keywordRe = "^help", commanderOnly = false, callback = HelpRu },
|
if Heimdall_Data.config.commander.debug then
|
||||||
{ keywordRe = "^helpen", commanderOnly = false, callback = HelpEn },
|
print(string.format("[%s] Adding stinky: %s/%s", ModuleName, name, tostring(class)))
|
||||||
{ keywordRe = "^joingroup", commanderOnly = false, callback = JoinGroup },
|
end
|
||||||
{ keywordRe = "^leavegroup", commanderOnly = false, callback = LeaveGroup },
|
shared.StinkyTracker.Track({
|
||||||
{ keywordRe = "^follow", commanderOnly = false, callback = FollowTarget },
|
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
|
||||||
|
|
||||||
local commanderChannelFrame = CreateFrame("Frame")
|
---@param args string[]
|
||||||
commanderChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
|
local function IgnoreMacroTarget(args)
|
||||||
commanderChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
|
if Heimdall_Data.config.commander.debug then
|
||||||
if not Heimdall_Data.config.commander.enabled then return end
|
---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack
|
||||||
local channelId = select(6, ...)
|
print(string.format("[%s] Macroing: %s", ModuleName, strjoin(" ", unpack(args))))
|
||||||
local _, channelname = GetChannelName(channelId)
|
end
|
||||||
if channelname ~= Heimdall_Data.config.commander.masterChannel then return end
|
if #args < 1 then
|
||||||
sender = string.match(sender, "^[^-]+")
|
if Heimdall_Data.config.commander.debug then
|
||||||
|
print(string.format("[%s] Invalid number of arguments for IgnoreMacroTarget", ModuleName))
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
table.remove(args, 1)
|
||||||
|
|
||||||
for _, command in ipairs(commands) do
|
for i = 1, #args do
|
||||||
local enabled = Heimdall_Data.config.commander.commands[command.keywordRe] == true or false
|
local stinky = strtrim(args[i])
|
||||||
if enabled and
|
local name = stinky:match("([^/]+)")
|
||||||
(not command.commanderOnly
|
if Heimdall_Data.config.commander.debug then
|
||||||
or (command.commanderOnly
|
print(string.format("[%s] Ignoring stinky: %s", ModuleName, name))
|
||||||
and sender == Heimdall_Data.config.commander.commander)) then
|
end
|
||||||
if msg:match(command.keywordRe) then
|
shared.StinkyTracker.Ignore(name)
|
||||||
local messages = command.callback({ strsplit(" ", msg) })
|
end
|
||||||
for _, message in ipairs(messages) do
|
return {}
|
||||||
---@type Message
|
end
|
||||||
local msg = {
|
|
||||||
channel = "CHANNEL",
|
---@class Command
|
||||||
data = channelname,
|
---@field keywordRe string
|
||||||
message = message
|
---@field commanderOnly boolean
|
||||||
}
|
---@field callback fun(...: any): string[]
|
||||||
table.insert(shared.messenger.queue, msg)
|
|
||||||
|
local commands = {
|
||||||
|
{ keywordRe = "^who$", commanderOnly = false, callback = WhoPartitionedStinkies },
|
||||||
|
{ keywordRe = "^кто$", commanderOnly = false, callback = WhoPartitionedStinkiesRu },
|
||||||
|
{ keywordRe = "^howmany$", commanderOnly = false, callback = CountPartitionedStinkies },
|
||||||
|
{ keywordRe = "^classes$", commanderOnly = false, callback = CountClassPartitionedStinkies },
|
||||||
|
{ keywordRe = "^help$", commanderOnly = false, callback = HelpRu },
|
||||||
|
{ keywordRe = "^helpen$", commanderOnly = false, callback = HelpEn },
|
||||||
|
{ keywordRe = "^joingroup$", commanderOnly = false, callback = JoinGroup },
|
||||||
|
{ keywordRe = "^leavegroup$", commanderOnly = false, callback = LeaveGroup },
|
||||||
|
{ keywordRe = "^follow$", commanderOnly = false, callback = FollowTarget },
|
||||||
|
{ keywordRe = "^macro", commanderOnly = false, callback = MacroTarget },
|
||||||
|
{ keywordRe = "^ignore", commanderOnly = false, callback = IgnoreMacroTarget },
|
||||||
|
}
|
||||||
|
|
||||||
|
local commanderChannelFrame = CreateFrame("Frame")
|
||||||
|
commanderChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL")
|
||||||
|
commanderChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
|
||||||
|
--if Heimdall_Data.config.commander.debug then
|
||||||
|
-- print(string.format("[%s] Event received", ModuleName))
|
||||||
|
-- shared.dump(Heimdall_Data.config.commander)
|
||||||
|
--end
|
||||||
|
if not Heimdall_Data.config.commander.enabled then
|
||||||
|
--if Heimdall_Data.config.commander.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.commander.channels) do
|
||||||
|
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
|
||||||
|
|
||||||
|
sender = string.match(sender, "^[^-]+")
|
||||||
|
if Heimdall_Data.config.commander.debug then
|
||||||
|
print(string.format("[%s] Message from: %s", ModuleName, sender))
|
||||||
|
shared.dump(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
|
||||||
|
print(
|
||||||
|
string.format("[%s] Command match: %s = %s", ModuleName, command.keywordRe, tostring(enabled))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if
|
||||||
|
enabled
|
||||||
|
and (
|
||||||
|
not command.commanderOnly
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
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
|
||||||
|
print(string.format("[%s] Messages to send: %s", ModuleName, #messages))
|
||||||
|
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
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end)
|
||||||
end)
|
|
||||||
|
|
||||||
print("Heimdall - Commander loaded")
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
end
|
end,
|
||||||
|
}
|
||||||
|
3887
Modules/Config.lua
3887
Modules/Config.lua
File diff suppressed because it is too large
Load Diff
12
Modules/Configurator.lua
Normal file
12
Modules/Configurator.lua
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
local _, shared = ...
|
||||||
|
---@cast shared HeimdallShared
|
||||||
|
local ModuleName = "Configurator"
|
||||||
|
|
||||||
|
---@class HeimdallConfiguratorConfig
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field debug boolean
|
||||||
|
|
||||||
|
---@class Configurator
|
||||||
|
shared.Configurator = {
|
||||||
|
Init = function() print(string.format("[%s] Module initialized", ModuleName)) end,
|
||||||
|
}
|
@@ -1,116 +1,268 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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 not Heimdall_Data.config.deathReporter.enabled then return end
|
local recentDeaths = {}
|
||||||
if recentDeaths[destination]
|
---@type table<string, number>
|
||||||
and GetTime() - recentDeaths[destination] < Heimdall_Data.config.deathReporter.throttle then
|
local recentDuels = {}
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if recentDuels[destination]
|
---@param source string
|
||||||
and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle then
|
---@param destination string
|
||||||
print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination))
|
---@param spellName string
|
||||||
return
|
local function RegisterDeath(source, destination, spellName)
|
||||||
end
|
if Heimdall_Data.config.deathReporter.debug then
|
||||||
if recentDuels[source]
|
print(
|
||||||
and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle then
|
string.format(
|
||||||
print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination))
|
"[%s] Processing death event - Source: %s, Target: %s, Spell: %s",
|
||||||
return
|
ModuleName,
|
||||||
end
|
source,
|
||||||
|
destination,
|
||||||
recentDeaths[destination] = GetTime()
|
spellName
|
||||||
C_Timer.NewTimer(3, function()
|
)
|
||||||
if recentDuels[destination]
|
)
|
||||||
and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle then
|
|
||||||
print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination))
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
if recentDuels[source]
|
|
||||||
and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle then
|
if not Heimdall_Data.config.deathReporter.enabled then
|
||||||
print(string.format("Cancelling death reports for %s and %s because of recent duel", source, destination))
|
if Heimdall_Data.config.deathReporter.debug then
|
||||||
|
print(string.format("[%s] Module disabled, ignoring death event", ModuleName))
|
||||||
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local zone = Heimdall_Data.config.deathReporter.zoneOverride
|
if
|
||||||
if zone == nil or zone == "" then
|
recentDeaths[destination]
|
||||||
zone = string.format("%s (%s)", GetZoneText(), GetSubZoneText())
|
and GetTime() - recentDeaths[destination] < Heimdall_Data.config.deathReporter.throttle
|
||||||
|
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
|
end
|
||||||
|
|
||||||
local text = string.format("%s killed %s with %s in %s",
|
if
|
||||||
tostring(source),
|
recentDuels[destination]
|
||||||
tostring(destination),
|
and GetTime() - recentDuels[destination] < Heimdall_Data.config.deathReporter.duelThrottle
|
||||||
tostring(spellName),
|
then
|
||||||
tostring(zone))
|
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
|
||||||
|
|
||||||
---@type Message
|
if
|
||||||
local msg = {
|
recentDuels[source]
|
||||||
channel = "CHANNEL",
|
and GetTime() - recentDuels[source] < Heimdall_Data.config.deathReporter.duelThrottle
|
||||||
data = Heimdall_Data.config.deathReporter.notifyChannel,
|
then
|
||||||
message = text,
|
if Heimdall_Data.config.deathReporter.debug then
|
||||||
}
|
print(
|
||||||
table.insert(shared.messenger.queue, msg)
|
string.format(
|
||||||
|
"[%s] Ignoring death report - Recent duel detected for source: %s",
|
||||||
|
ModuleName,
|
||||||
|
source
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if Heimdall_Data.config.deathReporter.doWhisper then
|
if Heimdall_Data.config.deathReporter.debug then
|
||||||
for _, name in pairs(Heimdall_Data.config.whisperNotify) do
|
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
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Cancelling delayed death report - Recent duel detected for: %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] 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 = {
|
local msg = {
|
||||||
channel = "WHISPER",
|
channel = "C",
|
||||||
data = name,
|
data = channel,
|
||||||
message = text,
|
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)
|
table.insert(shared.messenger.queue, msg)
|
||||||
end
|
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 not Heimdall_Data.config.deathReporter.enabled then return end
|
if not Heimdall_Data.config.deathReporter.enabled then return end
|
||||||
local overkill, err = CLEUParser.GetOverkill(...)
|
local source, destination = string.match(msg, "([^ ]+) has defeated ([^ ]+) in a duel")
|
||||||
if not err and overkill > 0 then
|
if Heimdall_Data.config.deathReporter.debug then
|
||||||
local source, err = CLEUParser.GetSourceName(...)
|
print(string.format("[%s] Received system message: %s", ModuleName, msg))
|
||||||
if err then source = "unknown" end
|
print(
|
||||||
local destination, err = CLEUParser.GetDestName(...)
|
string.format(
|
||||||
if err then destination = "unknown" end
|
"[%s] Source: %s, Destination: %s",
|
||||||
local spellName, err = CLEUParser.GetSpellName(...)
|
ModuleName,
|
||||||
if err then spellName = "unknown" end
|
tostring(source),
|
||||||
local sourceGUID, err = CLEUParser.GetSourceGUID(...)
|
tostring(destination)
|
||||||
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
|
end
|
||||||
RegisterDeath(source, destination, spellName)
|
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
|
||||||
|
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
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Module initialized with throttle: %.1fs, duel throttle: %.1fs",
|
||||||
|
ModuleName,
|
||||||
|
Heimdall_Data.config.deathReporter.throttle,
|
||||||
|
Heimdall_Data.config.deathReporter.duelThrottle
|
||||||
|
)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end)
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
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 not source or not destination then return end
|
|
||||||
source = string.match(source, "([^-]+)")
|
|
||||||
destination = string.match(destination, "([^-]+)")
|
|
||||||
if source and destination then
|
|
||||||
print(string.format("Detected duel between %s and %s", source, destination))
|
|
||||||
local now = GetTime()
|
|
||||||
recentDuels[source] = now
|
|
||||||
recentDuels[destination] = now
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
print("Heimdall - DeathReporter loaded")
|
|
||||||
end
|
|
||||||
|
@@ -1,25 +1,63 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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)
|
---@class Dueler
|
||||||
if not Heimdall_Data.config.dueler.enabled then return end
|
shared.Dueler = {
|
||||||
local allow = Heimdall_Data.config.agents[sender]
|
Init = function()
|
||||||
if allow then
|
local frame = CreateFrame("Frame")
|
||||||
print("Heimdall - Dueler - Accepting duel from " .. sender)
|
frame:RegisterEvent("DUEL_REQUESTED")
|
||||||
AcceptDuel()
|
frame:SetScript("OnEvent", function(self, event, sender)
|
||||||
else
|
if Heimdall_Data.config.dueler.debug then
|
||||||
if Heimdall_Data.config.dueler.autoDecline then
|
print(string.format("[%s] Duel request received from: %s", ModuleName, sender))
|
||||||
print("Heimdall - Dueler - Auto declining duel from " .. sender)
|
end
|
||||||
CancelDuel()
|
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
|
end
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
print("Heimdall - Dueler loaded")
|
if Heimdall_Data.config.dueler.debug then
|
||||||
end
|
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
|
||||||
|
if Heimdall_Data.config.dueler.declineOther then
|
||||||
|
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)
|
||||||
|
|
||||||
|
if Heimdall_Data.config.dueler.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Module initialized with auto-decline: %s",
|
||||||
|
ModuleName,
|
||||||
|
tostring(Heimdall_Data.config.dueler.declineOther)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -1,36 +1,65 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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 not Heimdall_Data.config.echoer.enabled then return end
|
|
||||||
|
|
||||||
local channelId = select(6, ...)
|
---@class Echoer
|
||||||
local channelname = ""
|
shared.Echoer = {
|
||||||
---@type any[]
|
Init = function()
|
||||||
local channels = { GetChannelList() }
|
local frame = CreateFrame("Frame")
|
||||||
for i = 1, #channels, 2 do
|
frame:RegisterEvent("CHAT_MSG_CHANNEL")
|
||||||
---@type number
|
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
|
||||||
local id = channels[i]
|
--if Heimdall_Data.config.echoer.debug then
|
||||||
---@type string
|
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
|
||||||
local name = channels[i + 1]
|
--end
|
||||||
if id == channelId then
|
|
||||||
channelname = name
|
if not Heimdall_Data.config.echoer.enabled then
|
||||||
|
--if Heimdall_Data.config.echoer.debug then
|
||||||
|
-- print(string.format("[%s] Module disabled, ignoring message", ModuleName))
|
||||||
|
--end
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if channelname ~= Heimdall_Data.config.echoer.masterChannel then return end
|
local channelId = select(6, ...)
|
||||||
|
local _, channelname = GetChannelName(channelId)
|
||||||
|
local ok = false
|
||||||
|
for _, channel in pairs(Heimdall_Data.config.echoer.channels) do
|
||||||
|
if channel == channelname then
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
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.dump(Heimdall_Data.config.echoer)
|
||||||
|
end
|
||||||
|
|
||||||
if string.find(msg, "^" .. Heimdall_Data.config.echoer.prefix) then
|
if string.find(msg, "^" .. Heimdall_Data.config.echoer.prefix) then
|
||||||
local msg = string.sub(msg, string.len(Heimdall_Data.config.echoer.prefix) + 1)
|
if Heimdall_Data.config.echoer.debug then
|
||||||
SendChatMessage(msg, "SAY")
|
print(string.format("[%s] Found echo command in message: %s", ModuleName, msg))
|
||||||
end
|
end
|
||||||
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)
|
||||||
|
|
||||||
print("Heimdall - Echoer loaded")
|
if Heimdall_Data.config.echoer.debug then print(string.format("[%s] Module initialized", ModuleName)) end
|
||||||
end
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
@@ -1,35 +1,66 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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 not Heimdall_Data.config.emoter.enabled then return end
|
|
||||||
|
|
||||||
local channelId = select(6, ...)
|
---@class Emoter
|
||||||
local channelname = ""
|
shared.Emoter = {
|
||||||
---@type any[]
|
Init = function()
|
||||||
local channels = { GetChannelList() }
|
local frame = CreateFrame("Frame")
|
||||||
for i = 1, #channels, 2 do
|
frame:RegisterEvent("CHAT_MSG_CHANNEL")
|
||||||
---@type number
|
frame:SetScript("OnEvent", function(self, event, msg, sender, ...)
|
||||||
local id = channels[i]
|
--if Heimdall_Data.config.emoter.debug then
|
||||||
---@type string
|
-- print(string.format("[%s] Channel message received from: %s", ModuleName, sender))
|
||||||
local name = channels[i + 1]
|
--end
|
||||||
if id == channelId then
|
|
||||||
channelname = name
|
if not Heimdall_Data.config.emoter.enabled then
|
||||||
|
--if Heimdall_Data.config.emoter.debug then
|
||||||
|
-- print(string.format("[%s] Module disabled, ignoring message", ModuleName))
|
||||||
|
--end
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if channelname ~= Heimdall_Data.config.emoter.masterChannel then return end
|
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 string.find(msg, "^" .. Heimdall_Data.config.emoter.prefix) then
|
if Heimdall_Data.config.emoter.debug then
|
||||||
local emote = string.sub(msg, string.len(Heimdall_Data.config.emoter.prefix) + 1)
|
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
|
||||||
DoEmote(emote)
|
shared.dump(Heimdall_Data.config.emoter)
|
||||||
end
|
end
|
||||||
end)
|
|
||||||
print("Heimdall - Emoter loaded")
|
if string.find(msg, "^" .. Heimdall_Data.config.emoter.prefix) then
|
||||||
end
|
if Heimdall_Data.config.emoter.debug then
|
||||||
|
print(string.format("[%s] Found emote command in message: %s", ModuleName, msg))
|
||||||
|
end
|
||||||
|
local emote = string.sub(msg, string.len(Heimdall_Data.config.emoter.prefix) + 1)
|
||||||
|
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 print(string.format("[%s] Module initialized", ModuleName)) end
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
@@ -1,139 +1,275 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
local ModuleName = "Inviter"
|
||||||
|
|
||||||
---@diagnostic disable-next-line: missing-fields
|
---@class HeimdallInviterConfig
|
||||||
shared.Inviter = {}
|
---@field enabled boolean
|
||||||
function shared.Inviter.Init()
|
---@field debug boolean
|
||||||
---@type Timer
|
---@field channels string[]
|
||||||
local updateTimer = nil
|
---@field keyword string
|
||||||
|
---@field allAssist boolean
|
||||||
|
---@field agentsAssist boolean
|
||||||
|
---@field throttle number
|
||||||
|
---@field kickOffline boolean
|
||||||
|
---@field cleanupInterval number
|
||||||
|
---@field afkThreshold number
|
||||||
|
---@field listeningChannel table<string, boolean>
|
||||||
|
|
||||||
local function FixGroup()
|
---@class Inviter
|
||||||
if not IsInRaid() then ConvertToRaid() end
|
shared.Inviter = {
|
||||||
if Heimdall_Data.config.inviter.allAssist then SetEveryoneIsAssistant() end
|
Init = function()
|
||||||
--shared.dumpTable(Heimdall_Data.config.inviter)
|
-- Fallback for old config
|
||||||
if Heimdall_Data.config.inviter.agentsAssist then
|
if type(Heimdall_Data.config.inviter.listeningChannel) == "string" then
|
||||||
--shared.dumpTable(Heimdall_Data.config.agents)
|
Heimdall_Data.config.inviter.listeningChannel = {
|
||||||
for name, _ in pairs(Heimdall_Data.config.agents) do
|
[Heimdall_Data.config.inviter.listeningChannel] = true,
|
||||||
if UnitInParty(name)
|
}
|
||||||
and not UnitIsGroupLeader(name)
|
|
||||||
and not UnitIsRaidOfficer(name) then
|
|
||||||
print("Promoting " .. name .. " to assistant")
|
|
||||||
PromoteToAssistant(name, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
---@type Timer
|
||||||
|
local updateTimer = nil
|
||||||
|
|
||||||
local framePool = {}
|
local function FixGroup()
|
||||||
---@param name string
|
if Heimdall_Data.config.inviter.debug then
|
||||||
local function OverlayKickButtonElvUI(name)
|
print(string.format("[%s] Checking and fixing group configuration", ModuleName))
|
||||||
for group = 1, 8 do
|
|
||||||
for player = 1, 5 do
|
|
||||||
local button = _G[string.format("ElvUF_RaidGroup%dUnitButton%d", group, player)]
|
|
||||||
|
|
||||||
local unitName = button and button.unit and UnitName(button.unit)
|
|
||||||
if unitName == name then
|
|
||||||
local overlayButton = framePool[button.unit] or
|
|
||||||
CreateFrame("Button",
|
|
||||||
string.format("HeimdallKickButton%s", button.unit, button, "SecureActionButtonTemplate"))
|
|
||||||
framePool[button.unit] = overlayButton
|
|
||||||
|
|
||||||
overlayButton:SetSize(button.UNIT_WIDTH/2, button.UNIT_HEIGHT/2)
|
|
||||||
overlayButton:SetPoint("CENTER", button, "CENTER", 0, 0)
|
|
||||||
overlayButton:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
|
|
||||||
overlayButton:SetHighlightTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
|
|
||||||
overlayButton:SetPushedTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
|
|
||||||
overlayButton:SetDisabledTexture("Interface\\Buttons\\UI-GroupLoot-KickIcon")
|
|
||||||
overlayButton:SetAlpha(0.5)
|
|
||||||
overlayButton:Show()
|
|
||||||
overlayButton:SetScript("OnClick", function()
|
|
||||||
UninviteUnit(name)
|
|
||||||
overlayButton:Hide()
|
|
||||||
end)
|
|
||||||
-- button:SetAttribute("type", "macro")
|
|
||||||
-- button:SetAttribute("macrotext", "/kick " .. unit)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@type table<string, number>
|
if not IsInRaid() then
|
||||||
local groupMembers = {}
|
if Heimdall_Data.config.inviter.debug then
|
||||||
local function CleanGroups()
|
print(string.format("[%s] Converting party to raid", ModuleName))
|
||||||
if not Heimdall_Data.config.inviter.kickOffline then return end
|
end
|
||||||
print("Cleaning groups")
|
ConvertToRaid()
|
||||||
local now = GetTime()
|
end
|
||||||
for i = 1, 40 do
|
|
||||||
local unit = "raid" .. i
|
if Heimdall_Data.config.inviter.allAssist then
|
||||||
if UnitExists(unit) then
|
if Heimdall_Data.config.inviter.debug then
|
||||||
local name = UnitName(unit)
|
print(string.format("[%s] Setting all members to assistant", ModuleName))
|
||||||
if name then
|
end
|
||||||
-- When we load (into game) we want to consider everyone "online"
|
SetEveryoneIsAssistant()
|
||||||
-- In other words everyone we haven't seen before is "online" the first time we see them
|
end
|
||||||
-- 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 Heimdall_Data.config.inviter.agentsAssist then
|
||||||
groupMembers[name] = now
|
if Heimdall_Data.config.inviter.debug then
|
||||||
else
|
print(string.format("[%s] Processing agents for assistant promotion", ModuleName))
|
||||||
local online = UnitIsConnected(unit)
|
end
|
||||||
if online then
|
|
||||||
groupMembers[name] = now
|
shared.AgentTracker.ForEach(function(agent)
|
||||||
|
if UnitInParty(agent) and not UnitIsGroupLeader(agent) and not UnitIsRaidOfficer(agent) then
|
||||||
|
if Heimdall_Data.config.inviter.debug then
|
||||||
|
print(string.format("[%s] Promoting agent to assistant: %s", ModuleName, agent))
|
||||||
end
|
end
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
for name, time in pairs(groupMembers) do
|
|
||||||
if time < now - Heimdall_Data.config.inviter.afkThreshold then
|
---@type table<string, number>
|
||||||
print(string.format("Kicking %s for being offline", name))
|
local groupMembers = {}
|
||||||
-- Blyat this is protected...
|
local function CleanGroups()
|
||||||
-- UninviteUnit(name)
|
if not Heimdall_Data.config.inviter.kickOffline then return end
|
||||||
OverlayKickButtonElvUI(name)
|
print("Cleaning groups")
|
||||||
|
local now = GetTime()
|
||||||
|
for i = 1, 40 do
|
||||||
|
local unit = "raid" .. i
|
||||||
|
if UnitExists(unit) then
|
||||||
|
local name = UnitName(unit)
|
||||||
|
if name then
|
||||||
|
-- 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
|
||||||
|
-- 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
|
||||||
|
groupMembers[name] = now
|
||||||
|
else
|
||||||
|
local online = UnitIsConnected(unit)
|
||||||
|
if online then groupMembers[name] = now end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if not UnitInParty(name) then
|
local afkPlayers = {}
|
||||||
print(string.format("%s no longer in party", name))
|
for name, time in pairs(groupMembers) do
|
||||||
groupMembers[name] = nil
|
if not UnitInParty(name) then
|
||||||
|
print(string.format("%s no longer in party", name))
|
||||||
|
groupMembers[name] = nil
|
||||||
|
else
|
||||||
|
if time < now - Heimdall_Data.config.inviter.afkThreshold then
|
||||||
|
print(string.format("Kicking %s for being offline", name))
|
||||||
|
afkPlayers[#afkPlayers + 1] = name
|
||||||
|
-- Blyat this is protected...
|
||||||
|
-- UninviteUnit(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
OverlayKickButtons(afkPlayers)
|
||||||
end
|
end
|
||||||
end
|
local function Tick()
|
||||||
local function Tick()
|
CleanGroups()
|
||||||
CleanGroups()
|
C_Timer.NewTimer(Heimdall_Data.config.inviter.cleanupInterval, Tick, 1)
|
||||||
C_Timer.NewTimer(Heimdall_Data.config.inviter.cleanupInterval, Tick, 1)
|
end
|
||||||
end
|
Tick()
|
||||||
Tick()
|
|
||||||
|
|
||||||
local inviterGroupFrame = CreateFrame("Frame")
|
local groupRosterUpdateFrame = CreateFrame("Frame")
|
||||||
inviterGroupFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
|
groupRosterUpdateFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
|
||||||
inviterGroupFrame:SetScript("OnEvent", function(self, event, ...)
|
groupRosterUpdateFrame:SetScript("OnEvent", function(self, event, ...)
|
||||||
if not Heimdall_Data.config.inviter.enabled then return end
|
if Heimdall_Data.config.inviter.debug then
|
||||||
if not UnitIsGroupLeader("player") then return end
|
print(string.format("[%s] Event received: %s", ModuleName, event))
|
||||||
|
|
||||||
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 not Heimdall_Data.config.inviter.enabled then return end
|
|
||||||
local channelId = select(6, ...)
|
|
||||||
local channelname = ""
|
|
||||||
---@type any[]
|
|
||||||
local channels = { GetChannelList() }
|
|
||||||
for i = 1, #channels, 2 do
|
|
||||||
---@type number
|
|
||||||
local id = channels[i]
|
|
||||||
---@type string
|
|
||||||
local name = channels[i + 1]
|
|
||||||
if id == channelId then
|
|
||||||
channelname = name
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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 channelname ~= Heimdall_Data.config.inviter.listeningChannel then return end
|
if Heimdall_Data.config.inviter.debug then
|
||||||
if msg == Heimdall_Data.config.inviter.keyword then InviteUnit(sender) end
|
print(
|
||||||
end)
|
string.format(
|
||||||
|
"[%s] Module initialized - All assist: %s, Agents assist: %s",
|
||||||
print("Heimdall - Inviter loaded")
|
ModuleName,
|
||||||
end
|
tostring(Heimdall_Data.config.inviter.allAssist),
|
||||||
|
tostring(Heimdall_Data.config.inviter.agentsAssist)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
@@ -1,65 +1,101 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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
|
|
||||||
|
|
||||||
---@type table<string, stinky>
|
---@class Macroer
|
||||||
local recentStinkies = {}
|
shared.Macroer = {
|
||||||
|
Init = function()
|
||||||
|
local function FindOrCreateMacro(macroName)
|
||||||
|
if Heimdall_Data.config.macroer.debug then
|
||||||
|
print(string.format("[%s] Finding or creating macro: %s", ModuleName, macroName))
|
||||||
|
end
|
||||||
|
local idx = GetMacroIndexByName(macroName)
|
||||||
|
if idx == 0 then
|
||||||
|
if Heimdall_Data.config.macroer.debug then
|
||||||
|
print(string.format("[%s] Creating new macro: %s", ModuleName, macroName))
|
||||||
|
end
|
||||||
|
CreateMacro(macroName, "INV_Misc_QuestionMark", "")
|
||||||
|
end
|
||||||
|
idx = GetMacroIndexByName(macroName)
|
||||||
|
if Heimdall_Data.config.macroer.debug then print(string.format("[%s] Macro index: %d", ModuleName, idx)) end
|
||||||
|
return idx
|
||||||
|
end
|
||||||
|
|
||||||
local function FindOrCreateMacro(macroName)
|
---@param stinkies table<string, Stinky>
|
||||||
local idx = GetMacroIndexByName(macroName)
|
local function FixMacro(stinkies)
|
||||||
if idx == 0 then
|
if Heimdall_Data.config.macroer.debug then
|
||||||
CreateMacro(macroName, "INV_Misc_QuestionMark", "")
|
print(string.format("[%s] Fixing macro with %d stinkies", ModuleName, #stinkies))
|
||||||
end
|
end
|
||||||
idx = GetMacroIndexByName(macroName)
|
if not Heimdall_Data.config.macroer.enabled then
|
||||||
return idx
|
if Heimdall_Data.config.macroer.debug then
|
||||||
end
|
print(string.format("[%s] Module disabled, skipping macro update", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if InCombatLockdown() then
|
||||||
|
if Heimdall_Data.config.macroer.debug then
|
||||||
|
print(string.format("[%s] In combat, skipping macro update", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
---@param stinkies table<string, stinky>
|
local priorityMap = {}
|
||||||
local function FixMacro(stinkies)
|
for priority, className in ipairs(Heimdall_Data.config.macroer.priority) do
|
||||||
if not Heimdall_Data.config.macroer.enabled then return end
|
priorityMap[className] = priority
|
||||||
if InCombatLockdown() then return end
|
end
|
||||||
local priorityMap = {}
|
local minPriority = #Heimdall_Data.config.macroer.priority + 1
|
||||||
for priority, className in ipairs(Heimdall_Data.config.macroer.priority) do
|
|
||||||
priorityMap[className] = priority
|
|
||||||
end
|
|
||||||
local minPriority = #Heimdall_Data.config.macroer.priority + 1
|
|
||||||
|
|
||||||
local sortedStinkies = {}
|
local sortedStinkies = {}
|
||||||
for _, stinky in pairs(stinkies) do
|
for _, stinky in pairs(stinkies) do
|
||||||
table.insert(sortedStinkies, stinky)
|
if not shared.AgentTracker.IsAgent(stinky.name) then sortedStinkies[#sortedStinkies + 1] = stinky end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.sort(sortedStinkies, function(a, b)
|
if Heimdall_Data.config.macroer.debug then
|
||||||
local aPriority = priorityMap[a.class] or minPriority
|
print(string.format("[%s] Processing %d non-agent stinkies", ModuleName, #sortedStinkies))
|
||||||
local bPriority = priorityMap[b.class] or minPriority
|
end
|
||||||
return aPriority > bPriority
|
|
||||||
end)
|
|
||||||
|
|
||||||
local lines = { "/targetenemy" }
|
table.sort(sortedStinkies, function(a, b)
|
||||||
for _, stinky in pairs(sortedStinkies) do
|
local aPriority = priorityMap[a.class] or minPriority
|
||||||
if stinky.seenAt > GetTime() - 600 then
|
local bPriority = priorityMap[b.class] or minPriority
|
||||||
print(string.format("Macroing %s", stinky.name))
|
return aPriority > bPriority
|
||||||
lines[#lines + 1] = string.format("/tar %s", stinky.name)
|
end)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local idx = FindOrCreateMacro("HeimdallTarget")
|
if Heimdall_Data.config.macroer.debug then
|
||||||
local body = strjoin("\n", unpack(lines))
|
print(string.format("[%s] Sorted stinkies: %d", ModuleName, #sortedStinkies))
|
||||||
EditMacro(idx, "HeimdallTarget", "INV_Misc_QuestionMark", body)
|
shared.dump(sortedStinkies)
|
||||||
end
|
end
|
||||||
|
local lines = { "/targetenemy" }
|
||||||
|
for _, stinky in pairs(sortedStinkies) do
|
||||||
|
if stinky.seenAt > GetTime() - 600 then
|
||||||
|
if Heimdall_Data.config.macroer.debug then
|
||||||
|
print(string.format("[%s] Adding target macro for: %s", ModuleName, stinky.name))
|
||||||
|
end
|
||||||
|
lines[#lines + 1] = string.format("/tar %s", stinky.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
shared.stinkyTracker.stinkies:onChange(function(value)
|
local idx = FindOrCreateMacro("HeimdallTarget")
|
||||||
FixMacro(value)
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
end)
|
local body = strjoin("\n", unpack(lines))
|
||||||
|
if Heimdall_Data.config.macroer.debug then
|
||||||
|
print(string.format("[%s] Updating macro with %d lines", ModuleName, #lines))
|
||||||
|
end
|
||||||
|
EditMacro(idx, "HeimdallTarget", "INV_Misc_QuestionMark", body)
|
||||||
|
end
|
||||||
|
|
||||||
print("Heimdall - Macroer loaded")
|
shared.StinkyTracker.OnChange(function(stinkies)
|
||||||
end
|
if Heimdall_Data.config.macroer.debug then
|
||||||
|
print(string.format("[%s] Stinkies changed, updating macro", ModuleName))
|
||||||
|
shared.dump(stinkies)
|
||||||
|
end
|
||||||
|
FixMacro(stinkies)
|
||||||
|
end)
|
||||||
|
if Heimdall_Data.config.macroer.debug then print(string.format("[%s] Module initialized", ModuleName)) end
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
@@ -1,84 +1,176 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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 GetChannelId(channelName)
|
---@class HeimdallMessengerData
|
||||||
local channels = { GetChannelList() }
|
---@field queue ReactiveValue<table<string, Message>>
|
||||||
for i = 1, #channels, 2 do
|
---@field ticker Timer?
|
||||||
local id = channels[i]
|
|
||||||
local name = channels[i + 1]
|
|
||||||
if name == channelName then
|
|
||||||
return id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function FindOrJoinChannel(channelName, password)
|
---@class Message
|
||||||
local channelId = GetChannelName(channelName)
|
---@field message string
|
||||||
if channelId == 0 then
|
---@field channel string
|
||||||
print("Channel", tostring(channelName), "not found, joining")
|
---@field data string
|
||||||
if password then
|
|
||||||
JoinPermanentChannel(channelName, password)
|
|
||||||
else
|
|
||||||
JoinPermanentChannel(channelName)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
channelId = GetChannelName(channelName)
|
|
||||||
return channelId
|
|
||||||
end
|
|
||||||
|
|
||||||
---@diagnostic disable-next-line: missing-fields
|
---@class Messenger
|
||||||
if not shared.messenger then shared.messenger = {} end
|
shared.Messenger = {
|
||||||
if not shared.messenger.queue then shared.messenger.queue = {} end
|
---@param message Message
|
||||||
if not shared.messenger.ticker then
|
Enqueue = function(message) table.insert(shared.messenger.queue, message) end,
|
||||||
local function DoMessage()
|
Init = function()
|
||||||
if not Heimdall_Data.config.messenger.enabled then return end
|
shared.messenger = {
|
||||||
---@type Message
|
queue = ReactiveValue.new({}),
|
||||||
local message = shared.messenger.queue[1]
|
}
|
||||||
if not message then return end
|
|
||||||
if not message.message or message.message == "" then return end
|
|
||||||
if not message.channel or message.channel == "" then return end
|
|
||||||
|
|
||||||
if message.channel == "CHANNEL" and message.data and string.match(message.data, "%D") then
|
local function FindOrJoinChannel(channelName, password)
|
||||||
print("Channel presented as string:", message.data)
|
local channelId = GetChannelName(channelName)
|
||||||
local channelId = GetChannelName(message.data)
|
if channelId == 0 then
|
||||||
if channelId == 0 then
|
if Heimdall_Data.config.messenger.debug then
|
||||||
print("Channel not found, joining")
|
print(string.format("[%s] Channel not found, joining: %s", ModuleName, channelName))
|
||||||
channelId = FindOrJoinChannel(message.data)
|
end
|
||||||
|
if password then
|
||||||
|
JoinPermanentChannel(channelName, password)
|
||||||
|
else
|
||||||
|
JoinPermanentChannel(channelName)
|
||||||
end
|
end
|
||||||
print("Channel resolved to id", channelId)
|
|
||||||
message.data = tostring(channelId)
|
|
||||||
end
|
end
|
||||||
|
channelId = GetChannelName(channelName)
|
||||||
table.remove(shared.messenger.queue, 1)
|
if Heimdall_Data.config.messenger.debug then
|
||||||
if not message.message or message.message == "" then return end
|
print(string.format("[%s] Channel found with ID: %s (%s)", ModuleName, channelId, channelName))
|
||||||
if not message.channel or message.channel == "" then return end
|
end
|
||||||
if not message.data or message.data == "" then return end
|
return channelId
|
||||||
SendChatMessage(message.message, message.channel, nil, message.data)
|
|
||||||
end
|
end
|
||||||
local function Tick()
|
|
||||||
DoMessage()
|
if not shared.messenger.ticker then
|
||||||
shared.messenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.messenger.interval, Tick, 1)
|
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
|
end
|
||||||
Tick()
|
|
||||||
end
|
|
||||||
|
|
||||||
--C_Timer.NewTicker(2, function()
|
if Heimdall_Data.config.messenger.debug then
|
||||||
-- print("Q")
|
print(
|
||||||
-- table.insert(data.messenger.queue, {
|
string.format(
|
||||||
-- channel = "CHANNEL",
|
"[%s] Module initialized with interval: %s",
|
||||||
-- data = "Foobar",
|
ModuleName,
|
||||||
-- message = "TEST"
|
Heimdall_Data.config.messenger.interval
|
||||||
-- })
|
)
|
||||||
--end)
|
)
|
||||||
|
end
|
||||||
print("Heimdall - Messenger loaded")
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
end
|
end,
|
||||||
|
}
|
||||||
|
597
Modules/MinimapTagger.lua
Normal file
597
Modules/MinimapTagger.lua
Normal file
@@ -0,0 +1,597 @@
|
|||||||
|
local _, shared = ...
|
||||||
|
---@cast shared HeimdallShared
|
||||||
|
local ModuleName = "MinimapTagger"
|
||||||
|
|
||||||
|
---@class HeimdallMinimapTaggerConfig
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field debug boolean
|
||||||
|
---@field channels string[]
|
||||||
|
---@field throttle number
|
||||||
|
---@field scale number
|
||||||
|
---@field tagTTL number
|
||||||
|
---@field tagSound boolean
|
||||||
|
---@field tagSoundFile string
|
||||||
|
---@field tagSoundThrottle number
|
||||||
|
---@field tagTextureFile string
|
||||||
|
---@field alertTTL number
|
||||||
|
---@field alertSound boolean
|
||||||
|
---@field alertSoundFile string
|
||||||
|
---@field alertSoundThrottle number
|
||||||
|
---@field alertTextureFile string
|
||||||
|
---@field combatTTL number
|
||||||
|
---@field combatSound boolean
|
||||||
|
---@field combatSoundFile string
|
||||||
|
---@field combatSoundThrottle number
|
||||||
|
---@field combatTextureFile string
|
||||||
|
---@field helpTTL number
|
||||||
|
---@field helpSound boolean
|
||||||
|
---@field helpSoundFile string
|
||||||
|
---@field helpSoundThrottle number
|
||||||
|
---@field helpTextureFile string
|
||||||
|
|
||||||
|
local HeimdallRoot = "Interface\\AddOns\\Heimdall\\"
|
||||||
|
local SoundRoot = HeimdallRoot .. "Sounds\\"
|
||||||
|
local TextureRoot = HeimdallRoot .. "Texture\\"
|
||||||
|
--/run local a=GetChannelName("Agent")local b,c=GetPlayerMapPosition("player")b,c=b*100,c*100;local d=string.format("I need help at %s (%s) [%s](%2.2f, %2.2f)",GetZoneText(),GetSubZoneText(),GetCurrentMapAreaID(),b,c)SendChatMessage(d,"CHANNEL",nil,a)
|
||||||
|
|
||||||
|
---@class MinimapTagger
|
||||||
|
shared.MinimapTagger = {
|
||||||
|
Init = function()
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@param frame Frame
|
||||||
|
---@param scale number?
|
||||||
|
---@param ttl number?
|
||||||
|
local function PlantFrame(x, y, frame, scale, ttl)
|
||||||
|
if not BattlefieldMinimap then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] BattlefieldMinimap not found", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
scale = scale or 1
|
||||||
|
ttl = ttl or 1
|
||||||
|
local w, h = BattlefieldMinimap:GetSize()
|
||||||
|
w, h = w * BattlefieldMinimap:GetEffectiveScale(), h * BattlefieldMinimap:GetEffectiveScale()
|
||||||
|
local maxSize = w > h and w or h
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Minimap size: %d", ModuleName, maxSize))
|
||||||
|
print(string.format("[%s] Scale: %d", ModuleName, scale))
|
||||||
|
print(string.format("[%s] TTL: %d", ModuleName, ttl))
|
||||||
|
end
|
||||||
|
local iconSize = maxSize * 0.05
|
||||||
|
iconSize = iconSize * scale
|
||||||
|
|
||||||
|
x, y = x / 100, y / 100
|
||||||
|
-- Could do with how... I have no idea, but this seems more accurate than without
|
||||||
|
--x, y = x - 0.01, y - 0.01
|
||||||
|
local offsetx, offsety = w * x, h * y
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Alert position: %d, %d", ModuleName, x, y))
|
||||||
|
print(string.format("[%s] Alert offset: %d, %d", ModuleName, offsetx, offsety))
|
||||||
|
end
|
||||||
|
|
||||||
|
frame:Hide()
|
||||||
|
frame:SetSize(iconSize, iconSize)
|
||||||
|
frame:SetFrameStrata("HIGH")
|
||||||
|
frame:SetFrameLevel(100)
|
||||||
|
frame:SetPoint("CENTER", BattlefieldMinimap, "TOPLEFT", offsetx, -offsety)
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Alert frame created, OnUpdate hooked", ModuleName))
|
||||||
|
end
|
||||||
|
frame:SetScript("OnShow", function(self)
|
||||||
|
self:SetAlpha(1)
|
||||||
|
self.custom.busy = true
|
||||||
|
self.custom.progress = 0
|
||||||
|
self:SetScript("OnUpdate", function(selff, elapsed)
|
||||||
|
self.custom.progress = self.custom.progress + elapsed
|
||||||
|
local progress = self.custom.progress / ttl
|
||||||
|
-- if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
-- print(string.format("[%s] Alert progress%%: %f", ModuleName, progress))
|
||||||
|
-- print(string.format("[%s] Alert progress: %f", ModuleName, self.custom.progress))
|
||||||
|
-- print(string.format("[%s] Alert ttl: %d", ModuleName, Heimdall_Data.config.minimapTagger.ttl))
|
||||||
|
-- end
|
||||||
|
self:SetAlpha(1 - progress)
|
||||||
|
|
||||||
|
if progress >= 1 then
|
||||||
|
self:Hide()
|
||||||
|
self.custom.busy = false
|
||||||
|
self:SetScript("OnUpdate", nil)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
frame:Show()
|
||||||
|
end
|
||||||
|
|
||||||
|
--region Alert
|
||||||
|
---@type Frame[]
|
||||||
|
local alertFramePool = {}
|
||||||
|
local alertFramePoolMaxSize = 20
|
||||||
|
for i = 1, alertFramePoolMaxSize do
|
||||||
|
local frame = CreateFrame("Frame")
|
||||||
|
frame.custom = { busy = false }
|
||||||
|
local texture = frame:CreateTexture(nil, "ARTWORK")
|
||||||
|
texture:SetAllPoints(frame)
|
||||||
|
texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.alertTextureFile)
|
||||||
|
table.insert(alertFramePool, frame)
|
||||||
|
end
|
||||||
|
local muteAlertUntil = 0
|
||||||
|
---@param x number|nil
|
||||||
|
---@param y number|nil
|
||||||
|
---@param scale number?
|
||||||
|
---@param doTag boolean?
|
||||||
|
local function PlantAlert(x, y, scale, doTag)
|
||||||
|
if x == nil or y == nil then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Alert position is nil, ignoring", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if doTag == nil then doTag = true end
|
||||||
|
local frame = nil
|
||||||
|
for _, alertFrame in ipairs(alertFramePool) do
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
if not alertFrame.custom.busy then
|
||||||
|
frame = alertFrame
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not frame then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Alert frame pool is full and could not get frame", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.minimapTagger.alertSound then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Playing alert sound: %s",
|
||||||
|
ModuleName,
|
||||||
|
Heimdall_Data.config.minimapTagger.alertSoundFile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if muteAlertUntil > GetTime() then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Alert sound is muted until %d", ModuleName, muteAlertUntil))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
muteAlertUntil = GetTime() + Heimdall_Data.config.minimapTagger.alertSoundThrottle
|
||||||
|
local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.alertSoundFile, "Master")
|
||||||
|
if not ok and Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Failed to play alert sound: %s",
|
||||||
|
ModuleName,
|
||||||
|
Heimdall_Data.config.minimapTagger.alertSoundFile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.alertTTL) end
|
||||||
|
end
|
||||||
|
--endregion
|
||||||
|
|
||||||
|
--region Tag
|
||||||
|
---@type Frame[]
|
||||||
|
local tagFramePool = {}
|
||||||
|
local tagFramePoolMaxSize = 20
|
||||||
|
for i = 1, tagFramePoolMaxSize do
|
||||||
|
local frame = CreateFrame("Frame")
|
||||||
|
frame.custom = { busy = false }
|
||||||
|
local texture = frame:CreateTexture(nil, "ARTWORK")
|
||||||
|
texture:SetAllPoints(frame)
|
||||||
|
texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.tagTextureFile)
|
||||||
|
table.insert(tagFramePool, frame)
|
||||||
|
end
|
||||||
|
local muteTagUntil = 0
|
||||||
|
---@param x number|nil
|
||||||
|
---@param y number|nil
|
||||||
|
---@param scale number?
|
||||||
|
---@param doTag boolean?
|
||||||
|
local function PlantTag(x, y, scale, doTag)
|
||||||
|
if x == nil or y == nil then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Tag position is nil, ignoring", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if doTag == nil then doTag = true end
|
||||||
|
local frame = nil
|
||||||
|
for _, tagFrame in ipairs(tagFramePool) do
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
if not tagFrame.custom.busy then
|
||||||
|
frame = tagFrame
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not frame then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Tag frame pool is full and could not get frame", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.minimapTagger.tagSound then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Playing tag sound: %s",
|
||||||
|
ModuleName,
|
||||||
|
Heimdall_Data.config.minimapTagger.tagSoundFile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if muteTagUntil > GetTime() then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Tag sound is muted until %d", ModuleName, muteTagUntil))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
muteTagUntil = GetTime() + Heimdall_Data.config.minimapTagger.tagSoundThrottle
|
||||||
|
local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.tagSoundFile, "Master")
|
||||||
|
if not ok and Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Failed to play tag sound: %s",
|
||||||
|
ModuleName,
|
||||||
|
Heimdall_Data.config.minimapTagger.tagSoundFile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.tagTTL) end
|
||||||
|
end
|
||||||
|
--endregion
|
||||||
|
|
||||||
|
--region Combat
|
||||||
|
---@type Frame[]
|
||||||
|
local combatFramePool = {}
|
||||||
|
local combatFramePoolMaxSize = 20
|
||||||
|
for i = 1, combatFramePoolMaxSize do
|
||||||
|
local frame = CreateFrame("Frame")
|
||||||
|
frame.custom = { busy = false }
|
||||||
|
local texture = frame:CreateTexture(nil, "ARTWORK")
|
||||||
|
texture:SetAllPoints(frame)
|
||||||
|
texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.combatTextureFile)
|
||||||
|
table.insert(combatFramePool, frame)
|
||||||
|
end
|
||||||
|
local muteCombatUntil = 0
|
||||||
|
---@param x number|nil
|
||||||
|
---@param y number|nil
|
||||||
|
---@param scale number?
|
||||||
|
---@param doTag boolean?
|
||||||
|
local function PlantCombat(x, y, scale, doTag)
|
||||||
|
if x == nil or y == nil then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Combat position is nil, ignoring", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if doTag == nil then doTag = true end
|
||||||
|
local frame = nil
|
||||||
|
for _, combatFrame in ipairs(combatFramePool) do
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
if not combatFrame.custom.busy then
|
||||||
|
frame = combatFrame
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not frame then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Battle frame pool is full and could not get frame", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.minimapTagger.combatSound then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Playing combat sound: %s",
|
||||||
|
ModuleName,
|
||||||
|
Heimdall_Data.config.minimapTagger.combatSoundFile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if muteCombatUntil > GetTime() then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Combat sound is muted until %d", ModuleName, muteCombatUntil))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
muteCombatUntil = GetTime() + Heimdall_Data.config.minimapTagger.combatSoundThrottle
|
||||||
|
local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.combatSoundFile, "Master")
|
||||||
|
if not ok and Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Failed to play combat sound: %s",
|
||||||
|
ModuleName,
|
||||||
|
Heimdall_Data.config.minimapTagger.combatSoundFile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.combatTTL) end
|
||||||
|
end
|
||||||
|
--endregion
|
||||||
|
|
||||||
|
--region Help
|
||||||
|
---@type Frame[]
|
||||||
|
local helpFramePool = {}
|
||||||
|
local helpFramePoolMaxSize = 20
|
||||||
|
for i = 1, helpFramePoolMaxSize do
|
||||||
|
local frame = CreateFrame("Frame")
|
||||||
|
frame.custom = { busy = false }
|
||||||
|
local texture = frame:CreateTexture(nil, "ARTWORK")
|
||||||
|
texture:SetAllPoints(frame)
|
||||||
|
texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.helpTextureFile)
|
||||||
|
table.insert(helpFramePool, frame)
|
||||||
|
end
|
||||||
|
local muteHelpUntil = 0
|
||||||
|
---@param x number|nil
|
||||||
|
---@param y number|nil
|
||||||
|
---@param scale number?
|
||||||
|
---@param doTag boolean?
|
||||||
|
local function PlantHelp(x, y, scale, doTag)
|
||||||
|
if x == nil or y == nil then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Help position is nil, ignoring", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if doTag == nil then doTag = true end
|
||||||
|
local frame = nil
|
||||||
|
for _, helpFrame in ipairs(helpFramePool) do
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
if not helpFrame.custom.busy then
|
||||||
|
frame = helpFrame
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not frame then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Help frame pool is full and could not get frame", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.minimapTagger.helpSound then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Playing help sound: %s",
|
||||||
|
ModuleName,
|
||||||
|
Heimdall_Data.config.minimapTagger.helpSoundFile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if muteHelpUntil > GetTime() then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Help sound is muted until %d", ModuleName, muteHelpUntil))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
muteHelpUntil = GetTime() + Heimdall_Data.config.minimapTagger.helpSoundThrottle
|
||||||
|
local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.helpSoundFile, "Master")
|
||||||
|
if not ok and Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Failed to play help sound: %s",
|
||||||
|
ModuleName,
|
||||||
|
Heimdall_Data.config.minimapTagger.helpSoundFile
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.helpTTL) end
|
||||||
|
end
|
||||||
|
--endregion
|
||||||
|
|
||||||
|
local pauseUntil = 0
|
||||||
|
local frame = CreateFrame("Frame")
|
||||||
|
frame:RegisterEvent("WORLD_MAP_UPDATE")
|
||||||
|
frame:SetScript("OnEvent", function(self, event, addon)
|
||||||
|
if pauseUntil > GetTime() then return end
|
||||||
|
pauseUntil = GetTime() + 1
|
||||||
|
if not BattlefieldMinimap then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] BattlefieldMinimap not found", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not Heimdall_Data.config.minimapTagger.enabled then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] MinimapTagger is disabled", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local scale = Heimdall_Data.config.minimapTagger.scale
|
||||||
|
BattlefieldMinimap:SetScale(scale)
|
||||||
|
BattlefieldMinimap:SetMovable(true)
|
||||||
|
BattlefieldMinimap:EnableMouse(true)
|
||||||
|
BattlefieldMinimap:RegisterForDrag("LeftButton")
|
||||||
|
BattlefieldMinimap:SetScript("OnDragStart", function(selff) selff:StartMoving() end)
|
||||||
|
BattlefieldMinimap:SetScript("OnDragStop", function(selff) selff:StopMovingOrSizing() end)
|
||||||
|
BattlefieldMinimapBackground:Hide()
|
||||||
|
BattlefieldMinimapCloseButton:Hide()
|
||||||
|
BattlefieldMinimapCorner:Hide()
|
||||||
|
BattlefieldMinimap:HookScript("OnHide", function(selff)
|
||||||
|
for _, alertFrame in ipairs(alertFramePool) do
|
||||||
|
alertFrame:Hide()
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
alertFrame.custom.busy = false
|
||||||
|
end
|
||||||
|
for _, tagFrame in ipairs(tagFramePool) do
|
||||||
|
tagFrame:Hide()
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
tagFrame.custom.busy = false
|
||||||
|
end
|
||||||
|
-- What the fuck is this global?
|
||||||
|
for _, battleFrame in ipairs(battleFramePool) do
|
||||||
|
battleFrame:Hide()
|
||||||
|
battleFrame.custom.busy = false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
local chatFrame = CreateFrame("Frame")
|
||||||
|
chatFrame:RegisterEvent("CHAT_MSG_CHANNEL")
|
||||||
|
chatFrame: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.minimapTagger.enabled then
|
||||||
|
--if Heimdall_Data.config.echoer.debug then
|
||||||
|
-- print(string.format("[%s] Module disabled, ignoring message", ModuleName))
|
||||||
|
--end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local channelId = select(6, ...)
|
||||||
|
local _, channelname = GetChannelName(channelId)
|
||||||
|
local ok = false
|
||||||
|
for _, channel in pairs(Heimdall_Data.config.minimapTagger.channels) do
|
||||||
|
if channelname == channel then
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Ignoring message from non-master channel: %s, need %s",
|
||||||
|
ModuleName,
|
||||||
|
channelname,
|
||||||
|
Heimdall_Data.config.minimapTagger.masterChannel
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
|
||||||
|
shared.dump(Heimdall_Data.config.minimapTagger)
|
||||||
|
end
|
||||||
|
|
||||||
|
local doTag = true
|
||||||
|
local messageMapId = string.match(msg, "%[(%d+)%]") or 0
|
||||||
|
if messageMapId then messageMapId = tonumber(messageMapId) end
|
||||||
|
|
||||||
|
local currentMapId = GetCurrentMapAreaID()
|
||||||
|
if currentMapId ~= messageMapId then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(
|
||||||
|
string.format(
|
||||||
|
"[%s] Current map ID (%d) does not match message map ID (%d), ignoring message",
|
||||||
|
ModuleName,
|
||||||
|
currentMapId,
|
||||||
|
messageMapId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
doTag = false
|
||||||
|
end
|
||||||
|
|
||||||
|
--region Tag
|
||||||
|
if string.find(msg, "^I see") then
|
||||||
|
if Heimdall_Data.config.minimapTagger.tagTTL == 0 then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Tag TTL is 0, ignoring message: %s", ModuleName, msg))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)")
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Found alert position: %s, %s", ModuleName, tostring(x), tostring(y)))
|
||||||
|
end
|
||||||
|
if x and y then PlantTag(tonumber(x), tonumber(y), 2, doTag) end
|
||||||
|
end
|
||||||
|
--endregion
|
||||||
|
--region Combat
|
||||||
|
if string.find(msg, "^I am in combat with") then
|
||||||
|
if Heimdall_Data.config.minimapTagger.combatTTL == 0 then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Combat TTL is 0, ignoring message: %s", ModuleName, msg))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Found combat alert in message: %s", ModuleName, msg))
|
||||||
|
end
|
||||||
|
local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)")
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Found combat position: %s, %s", ModuleName, tostring(x), tostring(y)))
|
||||||
|
end
|
||||||
|
if x and y then PlantCombat(tonumber(x), tonumber(y), 2, doTag) end
|
||||||
|
end
|
||||||
|
--endregion
|
||||||
|
--region Death
|
||||||
|
if string.find(msg, " killed ") then
|
||||||
|
if Heimdall_Data.config.minimapTagger.alertTTL == 0 then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Alert TTL is 0, ignoring message: %s", ModuleName, msg))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Found death alert in message: %s", ModuleName, msg))
|
||||||
|
end
|
||||||
|
local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)")
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Found death position: %s, %s", ModuleName, tostring(x), tostring(y)))
|
||||||
|
end
|
||||||
|
if x and y then PlantAlert(tonumber(x), tonumber(y), 2, doTag) end
|
||||||
|
end
|
||||||
|
--endregion
|
||||||
|
--region Help
|
||||||
|
if string.find(msg, "I need help") then
|
||||||
|
if Heimdall_Data.config.minimapTagger.helpTTL == 0 then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Help TTL is 0, ignoring message: %s", ModuleName, msg))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Found help alert in message: %s", ModuleName, msg))
|
||||||
|
end
|
||||||
|
local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)")
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Found help position: %s, %s", ModuleName, tostring(x), tostring(y)))
|
||||||
|
end
|
||||||
|
if x and y then
|
||||||
|
x, y = tonumber(x), tonumber(y)
|
||||||
|
PlantHelp(x, y, 1, doTag)
|
||||||
|
---@diagnostic disable-next-line: undefined-global
|
||||||
|
if TomTom then
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Adding help waypoint to TomTom", ModuleName))
|
||||||
|
end
|
||||||
|
local areaId = string.match(msg, "%[(%d+)%]") or 0
|
||||||
|
if areaId then areaId = tonumber(areaId) end
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] Area ID: %s", ModuleName, tostring(areaId)))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: undefined-global
|
||||||
|
TomTom:AddMFWaypoint(areaId, nil, x / 100, y / 100, {
|
||||||
|
title = "Help " .. sender,
|
||||||
|
world = true,
|
||||||
|
from = "Heimdall",
|
||||||
|
crazy = true,
|
||||||
|
})
|
||||||
|
else
|
||||||
|
if Heimdall_Data.config.minimapTagger.debug then
|
||||||
|
print(string.format("[%s] No tomtom no waypoint", ModuleName))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--endregion
|
||||||
|
end)
|
||||||
|
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
85
Modules/Network.lua
Normal file
85
Modules/Network.lua
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
local _, shared = ...
|
||||||
|
---@cast shared HeimdallShared
|
||||||
|
local ModuleName = "Network"
|
||||||
|
|
||||||
|
---@class HeimdallNetworkConfig
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field debug boolean
|
||||||
|
---@field members string[]
|
||||||
|
---@field updateInterval number
|
||||||
|
|
||||||
|
---@class HeimdallNetworkData
|
||||||
|
---@field ticker Timer?
|
||||||
|
|
||||||
|
---@class Network
|
||||||
|
shared.Network = {
|
||||||
|
Init = function()
|
||||||
|
if not shared.network then shared.network = {} end
|
||||||
|
local updatePending = false
|
||||||
|
|
||||||
|
local function FriendListUpdate()
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
local friendsFrame = CreateFrame("Frame")
|
||||||
|
friendsFrame:RegisterEvent("FRIENDLIST_UPDATE")
|
||||||
|
friendsFrame:SetScript("OnEvent", function(self, event, ...) 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(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
204
Modules/NetworkMessenger.lua
Normal file
204
Modules/NetworkMessenger.lua
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
local _, shared = ...
|
||||||
|
---@cast shared HeimdallShared
|
||||||
|
local ModuleName = "NetworkMessenger"
|
||||||
|
|
||||||
|
---@class HeimdallNetworkMessengerConfig
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field debug boolean
|
||||||
|
---@field interval number
|
||||||
|
|
||||||
|
---@class HeimdallNetworkMessengerData
|
||||||
|
---@field queue table<string, Message>
|
||||||
|
---@field ticker Timer?
|
||||||
|
|
||||||
|
---@class NetworkMessenger
|
||||||
|
shared.NetworkMessenger = {
|
||||||
|
---@param message Message
|
||||||
|
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
|
||||||
|
---@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)
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
print(string.format("[%s] No network nodes found, wtf????", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- There should always be at least one network node ergo should always exist a leader
|
||||||
|
-- Because the us, the player, is also a node
|
||||||
|
--local networkLeader = shared.networkNodes[1]
|
||||||
|
--if source ~= networkLeader then
|
||||||
|
-- if Heimdall_Data.config.networkMessenger.debug then
|
||||||
|
-- print(string.format("[%s] Message from %s is not from the network leader (%s)", ModuleName, source,
|
||||||
|
-- networkLeader))
|
||||||
|
-- end
|
||||||
|
-- return
|
||||||
|
--end
|
||||||
|
|
||||||
|
local parts = shared.Split(message, "|")
|
||||||
|
if Heimdall_Data.config.networkMessenger.debug then
|
||||||
|
print(string.format("[%s] Received message parts:", ModuleName))
|
||||||
|
shared.dump(parts)
|
||||||
|
end
|
||||||
|
local command = strtrim(parts[1])
|
||||||
|
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, "|")
|
||||||
|
|
||||||
|
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, content, recipient))
|
||||||
|
end
|
||||||
|
SendAddonMessage(Heimdall_Data.config.addonPrefix, content, "WHISPER", recipient)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--/run Heimdall_Data.Test()
|
||||||
|
Heimdall_Data.Test = function()
|
||||||
|
local testmsg = {
|
||||||
|
channel = "W",
|
||||||
|
message = "Hi, mom!",
|
||||||
|
data = "Secundus",
|
||||||
|
}
|
||||||
|
for i = 1, 36 do
|
||||||
|
table.insert(shared.networkMessenger.queue, testmsg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
307
Modules/Noter.lua
Normal file
307
Modules/Noter.lua
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
local _, shared = ...
|
||||||
|
---@cast shared HeimdallShared
|
||||||
|
local ModuleName = "Noter"
|
||||||
|
|
||||||
|
---@class HeimdallNoterConfig
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field debug boolean
|
||||||
|
---@field channels string[]
|
||||||
|
---@field lastNotes number
|
||||||
|
|
||||||
|
---@class Note
|
||||||
|
---@field source string
|
||||||
|
---@field for string
|
||||||
|
---@field date string
|
||||||
|
---@field note string
|
||||||
|
|
||||||
|
---@class Noter
|
||||||
|
shared.Noter = {
|
||||||
|
Init = function()
|
||||||
|
-- ---Hopefully this will not be necessary
|
||||||
|
-- ---@param text string
|
||||||
|
-- ---@param size number
|
||||||
|
-- ---@return string[]
|
||||||
|
-- local function Partition(text, size)
|
||||||
|
-- local words = {}
|
||||||
|
-- for word in text:gmatch("[^,]+") do
|
||||||
|
-- words[#words + 1] = word
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- local ret = {}
|
||||||
|
-- local currentChunk = ""
|
||||||
|
|
||||||
|
-- for _, word in ipairs(words) do
|
||||||
|
-- if #currentChunk + #word + 1 <= size then
|
||||||
|
-- currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word)
|
||||||
|
-- else
|
||||||
|
-- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
|
||||||
|
-- currentChunk = word
|
||||||
|
-- end
|
||||||
|
-- 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
|
||||||
|
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
|
||||||
|
print(string.format("[%s] Range received for delete note: %s", ModuleName, range))
|
||||||
|
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
|
||||||
|
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.dump(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(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
92
Modules/Sniffer.lua
Normal file
92
Modules/Sniffer.lua
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
local _, shared = ...
|
||||||
|
---@cast shared HeimdallShared
|
||||||
|
local ModuleName = "Sniffer"
|
||||||
|
|
||||||
|
---@class HeimdallSnifferConfig
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field debug boolean
|
||||||
|
---@field channels string[]
|
||||||
|
---@field throttle number -- throttleTime in the original code, matching config name now
|
||||||
|
---@field zoneOverride string?
|
||||||
|
---@field stinky boolean
|
||||||
|
|
||||||
|
---@class Sniffer
|
||||||
|
shared.Sniffer = {
|
||||||
|
Init = function()
|
||||||
|
if Heimdall_Data.config.sniffer.debug then print(string.format("[%s] Module initializing", ModuleName)) end
|
||||||
|
local smellThrottle = {}
|
||||||
|
local SmellStinky = function(stinky)
|
||||||
|
if Heimdall_Data.config.sniffer.debug then
|
||||||
|
print(string.format("%s: SmellStinky", ModuleName))
|
||||||
|
shared.dump(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.throttle 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
|
||||||
|
local locale = shared.GetLocaleForChannel(channel)
|
||||||
|
local text = string.format(shared._L("snifferStinky", locale), stinky)
|
||||||
|
---@type Message
|
||||||
|
local msg = {
|
||||||
|
channel = "C",
|
||||||
|
data = channel,
|
||||||
|
message = text,
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|
||||||
|
local cleuFrame = CreateFrame("Frame")
|
||||||
|
cleuFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
|
||||||
|
cleuFrame:SetScript("OnEvent", function(self, event, ...)
|
||||||
|
if Heimdall_Data.config.sniffer.debug then
|
||||||
|
print(string.format("[%s] Received event: %s", ModuleName, event))
|
||||||
|
end
|
||||||
|
if not Heimdall_Data.config.sniffer.enabled then
|
||||||
|
if Heimdall_Data.config.sniffer.debug then
|
||||||
|
print(string.format("[%s] Module disabled, ignoring event", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local source, destination, err
|
||||||
|
source, err = CLEUParser.GetSourceName(...)
|
||||||
|
if Heimdall_Data.config.sniffer.debug then
|
||||||
|
print(string.format("[%s] Processing source: %s", ModuleName, source))
|
||||||
|
end
|
||||||
|
if err then
|
||||||
|
if Heimdall_Data.config.sniffer.debug then
|
||||||
|
print(string.format("[%s] Error parsing source: %s", ModuleName, err))
|
||||||
|
end
|
||||||
|
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))
|
||||||
|
end,
|
||||||
|
}
|
@@ -1,124 +1,238 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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.agents[name] then return false end
|
else
|
||||||
if Heimdall_Data.config.spotter.stinky then
|
return hp
|
||||||
if Heimdall_Data.config.stinkies[name] then return true end
|
end
|
||||||
end
|
|
||||||
if Heimdall_Data.config.spotter.alliance then
|
|
||||||
if faction == "Alliance" then return true end
|
|
||||||
end
|
|
||||||
if Heimdall_Data.config.spotter.hostile then
|
|
||||||
if hostile then return true end
|
|
||||||
end
|
|
||||||
return Heimdall_Data.config.spotter.everyone
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param unit string
|
|
||||||
---@return string?
|
|
||||||
local function NotifySpotted(unit)
|
|
||||||
if not unit then return string.format("Could not find unit %s", tostring(unit)) end
|
|
||||||
if not UnitIsPlayer(unit) then return nil end
|
|
||||||
|
|
||||||
local name = UnitName(unit)
|
|
||||||
if not name then return string.format("Could not find name for unit %s", tostring(unit)) end
|
|
||||||
|
|
||||||
local time = GetTime()
|
|
||||||
if throttleTable[name] and time - throttleTable[name] < Heimdall_Data.config.spotter.throttleTime then
|
|
||||||
return string.format("Throttled %s", tostring(name))
|
|
||||||
end
|
|
||||||
throttleTable[name] = time
|
|
||||||
|
|
||||||
local race = UnitRace(unit)
|
|
||||||
if not race then return string.format("Could not find race for unit %s", tostring(unit)) end
|
|
||||||
local faction = shared.raceMap[race]
|
|
||||||
if not faction then return string.format("Could not find faction for race %s", tostring(race)) end
|
|
||||||
|
|
||||||
local hostile = UnitCanAttack("player", unit)
|
|
||||||
local doNotify = ShouldNotify(unit, name, faction, hostile)
|
|
||||||
if not doNotify then return string.format("Not notifying for %s", tostring(name)) end
|
|
||||||
|
|
||||||
local hp = UnitHealth(unit)
|
|
||||||
if not hp then return string.format("Could not find hp 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
|
|
||||||
|
|
||||||
local class = UnitClass(unit)
|
|
||||||
if not class then return string.format("Could not find class for unit %s", tostring(unit)) end
|
|
||||||
|
|
||||||
local location = Heimdall_Data.config.spotter.zoneOverride
|
|
||||||
if not location or location == "" then
|
|
||||||
local zone = GetZoneText()
|
|
||||||
if not zone then return string.format("Could not find zone for unit %s", tostring(unit)) end
|
|
||||||
local subzone = GetSubZoneText()
|
|
||||||
if not subzone then subzone = "" end
|
|
||||||
location = string.format("%s (%s)", zone, subzone)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local x, y = GetPlayerMapPosition("player")
|
---@type table<string, number>
|
||||||
local stinky = Heimdall_Data.config.stinkies[name] or false
|
local throttleTable = {}
|
||||||
local text = string.format("I see (%s) %s/%s %s of race %s (%s) with health %s/%s at %s (%2.2f, %2.2f)",
|
|
||||||
hostile and "Hostile" or "Friendly",
|
|
||||||
name,
|
|
||||||
class,
|
|
||||||
stinky and string.format("(%s)", "!!!!") or "",
|
|
||||||
race,
|
|
||||||
faction,
|
|
||||||
FormatHP(hp),
|
|
||||||
FormatHP(maxHp),
|
|
||||||
location,
|
|
||||||
x * 100, y * 100)
|
|
||||||
|
|
||||||
---@type Message
|
---@param unit string
|
||||||
local msg = {
|
---@param name string
|
||||||
channel = "CHANNEL",
|
---@param faction string
|
||||||
data = Heimdall_Data.config.spotter.notifyChannel,
|
---@param hostile boolean
|
||||||
message = text
|
---@return boolean
|
||||||
}
|
---@return string? error
|
||||||
--shared.dumpTable(msg)
|
local function ShouldNotify(unit, name, faction, hostile)
|
||||||
table.insert(shared.messenger.queue, msg)
|
if Heimdall_Data.config.spotter.debug then
|
||||||
end
|
print(string.format("[%s] Checking notification criteria for %s (%s)", ModuleName, name, faction))
|
||||||
|
end
|
||||||
|
|
||||||
local frame = CreateFrame("Frame")
|
if shared.AgentTracker.IsAgent(name) then
|
||||||
frame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
|
if Heimdall_Data.config.spotter.debug then
|
||||||
frame:RegisterEvent("UNIT_TARGET")
|
print(string.format("[%s] Skipping agent: %s", ModuleName, name))
|
||||||
frame:SetScript("OnEvent", function(self, event, unit)
|
end
|
||||||
if not Heimdall_Data.config.spotter.enabled then return end
|
return false
|
||||||
if event == "UNIT_TARGET" then
|
end
|
||||||
unit = "target"
|
|
||||||
|
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
|
||||||
local err = NotifySpotted(unit)
|
|
||||||
if err then
|
|
||||||
print(string.format("Error notifying %s: %s", tostring(unit), tostring(err)))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
print("Heimdall - Spotter loaded")
|
---@param unit string
|
||||||
end
|
---@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
|
||||||
|
print(string.format("[%s] Ignoring non-player unit: %s", ModuleName, unit))
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = UnitName(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
|
||||||
|
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
|
||||||
|
return string.format("Throttled %s", tostring(name))
|
||||||
|
end
|
||||||
|
throttleTable[name] = time
|
||||||
|
|
||||||
|
local race = UnitRace(unit)
|
||||||
|
if not race then return string.format("Could not find race for unit %s", tostring(unit)) end
|
||||||
|
local faction = shared.raceMap[race]
|
||||||
|
if not faction then return string.format("Could not find faction for race %s", tostring(race)) end
|
||||||
|
if Heimdall_Data.config.spotter.debug then
|
||||||
|
print(string.format("[%s] Player %s is %s (%s)", ModuleName, name, race, faction))
|
||||||
|
end
|
||||||
|
|
||||||
|
local hostile = UnitCanAttack("player", unit)
|
||||||
|
if Heimdall_Data.config.spotter.debug then
|
||||||
|
print(string.format("[%s] Player %s is %s", ModuleName, name, hostile and "hostile" or "friendly"))
|
||||||
|
end
|
||||||
|
|
||||||
|
local doNotify = ShouldNotify(unit, name, faction, hostile)
|
||||||
|
if not doNotify then
|
||||||
|
if Heimdall_Data.config.spotter.debug then
|
||||||
|
print(string.format("[%s] Skipping notification for %s", ModuleName, name))
|
||||||
|
end
|
||||||
|
return string.format("Not notifying for %s", tostring(name))
|
||||||
|
end
|
||||||
|
|
||||||
|
local hp = UnitHealth(unit)
|
||||||
|
if not hp then return string.format("Could not find hp 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)
|
||||||
|
if not class then return string.format("Could not find class for unit %s", tostring(unit)) 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.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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if event == "UNIT_TARGET" then unit = "target" end
|
||||||
|
|
||||||
|
local err = NotifySpotted(unit)
|
||||||
|
if err then
|
||||||
|
if Heimdall_Data.config.spotter.debug then
|
||||||
|
print(string.format("[%s] Error processing unit %s: %s", ModuleName, unit, err))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if Heimdall_Data.config.spotter.debug then print(string.format("[%s] Module initialized", ModuleName)) end
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
83
Modules/StinkyCache.lua
Normal file
83
Modules/StinkyCache.lua
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
local _, shared = ...
|
||||||
|
---@cast shared HeimdallShared
|
||||||
|
local ModuleName = "StinkyCache"
|
||||||
|
|
||||||
|
---@class HeimdallStinkyCacheConfig
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field debug boolean
|
||||||
|
---@field commander string
|
||||||
|
---@field ttl number
|
||||||
|
|
||||||
|
---@class HeimdallStinkyCacheData
|
||||||
|
---@field stinkies table<string, {value: number, timestamp: number}>
|
||||||
|
|
||||||
|
---@class StinkyCache
|
||||||
|
shared.StinkyCache = {
|
||||||
|
Init = function()
|
||||||
|
shared.stinkyCache = {
|
||||||
|
stinkies = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
---@param name string
|
||||||
|
local function AskCommander(name)
|
||||||
|
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")
|
||||||
|
addonMessageFrame:RegisterEvent("CHAT_MSG_ADDON")
|
||||||
|
addonMessageFrame:SetScript("OnEvent", function(self, event, msg, sender, ...)
|
||||||
|
if sender == Heimdall_Data.config.stinkyCache.commander then
|
||||||
|
if Heimdall_Data.config.stinkyCache.debug then
|
||||||
|
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)
|
||||||
|
|
||||||
|
setmetatable(shared.stinkyCache.stinkies, {
|
||||||
|
__index = function(self, key)
|
||||||
|
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,
|
||||||
|
}
|
@@ -1,109 +1,364 @@
|
|||||||
local addonname, shared = ...
|
local _, shared = ...
|
||||||
---@cast shared HeimdallShared
|
---@cast shared HeimdallShared
|
||||||
---@cast addonname string
|
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
|
||||||
local stinkies = {}
|
---@field channels string[]
|
||||||
for name, class in string.gmatch(msg, whoRegex) do
|
|
||||||
stinkies[name] = {
|
---@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
|
||||||
|
print(string.format("[%s] Request to track stinky: %s (%s)", ModuleName, stinky.name, stinky.class))
|
||||||
|
end
|
||||||
|
local ignored = shared.stinkyTracker.ignored[stinky.name]
|
||||||
|
-- TODO: Add a config option for the ignored timeout
|
||||||
|
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",
|
||||||
}
|
}
|
||||||
end
|
stinkies[name] = stinky
|
||||||
return stinkies
|
if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
end
|
print(
|
||||||
local seeRegex = "I see %((%w+)%) ([^ -/]+)-?%w*/(%w+)"
|
string.format(
|
||||||
---@param msg string
|
"[%s] Found stinky in SEE: %s (%s) - %s at %s",
|
||||||
---@return table<string, stinky>
|
ModuleName,
|
||||||
local function ParseSee(msg)
|
name,
|
||||||
local stinkies = {}
|
class,
|
||||||
local aggression, name, class = string.match(msg, seeRegex)
|
aggression,
|
||||||
if not name or not class then
|
date("%H:%M:%S", time())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
shared.dump(stinkies)
|
||||||
|
end
|
||||||
return stinkies
|
return stinkies
|
||||||
end
|
end
|
||||||
local stinky = {
|
|
||||||
name = name,
|
local arrivedRegex = "([^ -/]+)-?%w*; c:([^;]+)"
|
||||||
class = class,
|
local arrivedRegexAlt = "([^ -/]+)-?%w*%(!!!!%); c:([^;]+)"
|
||||||
seenAt = GetTime(),
|
---@param msg string
|
||||||
hostile = aggression == "Hostile"
|
---@return table<string, Stinky>
|
||||||
}
|
local function ParseArrived(msg)
|
||||||
stinkies[name] = stinky
|
if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
return stinkies
|
print(string.format("%s: Parsing arrived message: %s", ModuleName, msg))
|
||||||
end
|
end
|
||||||
local arrivedRegex = "([^ -/]+)-?%w* of class (%w+)"
|
local stinkies = {}
|
||||||
local arrivedRegexAlt = "([^ -/]+)-?%w* %(!!!!%) of class (%w+)"
|
local name, class = string.match(msg, arrivedRegex)
|
||||||
---@param msg string
|
if not name or not class then
|
||||||
---@return table<string, stinky>
|
name, class = string.match(msg, arrivedRegexAlt)
|
||||||
local function ParseArrived(msg)
|
end
|
||||||
local stinkies = {}
|
if not name or not class then
|
||||||
local name, class = string.match(msg, arrivedRegex)
|
if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
if not name or not class then
|
print(string.format("%s: No valid stinky found in arrived message", ModuleName))
|
||||||
name, class = string.match(msg, arrivedRegexAlt)
|
end
|
||||||
end
|
return stinkies
|
||||||
if not name or not class then
|
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
|
||||||
return stinkies
|
return stinkies
|
||||||
end
|
end
|
||||||
local stinky = {
|
|
||||||
name = name,
|
|
||||||
class = class,
|
|
||||||
seenAt = GetTime(),
|
|
||||||
hostile = true
|
|
||||||
}
|
|
||||||
stinkies[name] = stinky
|
|
||||||
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 not Heimdall_Data.config.stinkyTracker.enabled then return end
|
--if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
local channelId = select(6, ...)
|
-- print(string.format("[%s] Event received: %s from %s", ModuleName, event, sender))
|
||||||
local _, channelname = GetChannelName(channelId)
|
--end
|
||||||
if channelname ~= Heimdall_Data.config.stinkyTracker.masterChannel then return end
|
if not Heimdall_Data.config.stinkyTracker.enabled then
|
||||||
|
--if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
if string.find(msg, "^who:") then
|
-- print(string.format("[%s] Module disabled, ignoring event", ModuleName))
|
||||||
local whoStinkies = ParseWho(msg)
|
--end
|
||||||
for name, stinky in pairs(whoStinkies) do
|
return
|
||||||
if stinky.hostile then
|
end
|
||||||
shared.stinkyTracker.stinkies[name] = stinky
|
local channelId = select(6, ...)
|
||||||
|
local _, channelname = GetChannelName(channelId)
|
||||||
|
local ok = false
|
||||||
|
for _, channel in pairs(Heimdall_Data.config.stinkyTracker.channels) do
|
||||||
|
if channel == channelname then
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
if not ok then
|
||||||
if string.find(msg, "^I see") then
|
if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
local seeStinkies = ParseSee(msg)
|
print(string.format("[%s] Ignoring message from non-master channel: %s", ModuleName, channelname))
|
||||||
for name, stinky in pairs(seeStinkies) do
|
|
||||||
if stinky.hostile then
|
|
||||||
shared.stinkyTracker.stinkies[name] = stinky
|
|
||||||
end
|
end
|
||||||
if not stinky.hostile then
|
return
|
||||||
|
end
|
||||||
|
if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
|
print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender))
|
||||||
|
shared.dump(Heimdall_Data.config.stinkyTracker)
|
||||||
|
end
|
||||||
|
|
||||||
|
local stinkies = {}
|
||||||
|
if string.find(msg, "^who:") then
|
||||||
|
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
|
||||||
|
if string.find(msg, "^I see") then
|
||||||
|
if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
|
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
|
||||||
|
if string.find(msg, "arrived to") or string.find(msg, "moved to") 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
|
||||||
|
if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
|
print(string.format("[%s] Added stinky: %s (%s)", ModuleName, name, stinky.class))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Log total stinky count after processing
|
||||||
|
if Heimdall_Data.config.stinkyTracker.debug 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
|
||||||
|
|
||||||
|
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
|
||||||
|
print(string.format("[%s] Removed agent from stinkies: %s", ModuleName, name))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end)
|
||||||
end
|
end)
|
||||||
if string.find(msg, " and guild ") then
|
|
||||||
local arrivedStinkies = ParseArrived(msg)
|
|
||||||
for name, stinky in pairs(arrivedStinkies) do
|
|
||||||
shared.stinkyTracker.stinkies[name] = stinky
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for name, stinky in pairs(shared.stinkyTracker.stinkies) do
|
|
||||||
if Heimdall_Data.config.agents[name] then
|
|
||||||
shared.stinkyTracker.stinkies[name] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
print("Heimdall - StinkyTracker loaded")
|
local targetFrame = CreateFrame("Frame")
|
||||||
end
|
targetFrame:RegisterEvent("UNIT_TARGET")
|
||||||
|
targetFrame:SetScript("OnEvent", function(self, event, unit)
|
||||||
|
if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
|
print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target"))
|
||||||
|
end
|
||||||
|
unit = "target"
|
||||||
|
|
||||||
|
if not Heimdall_Data.config.stinkyTracker.enabled then
|
||||||
|
if Heimdall_Data.config.stinkyTracker.debug then
|
||||||
|
print(string.format("[%s] Module disabled, ignoring event", ModuleName))
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = UnitName(unit)
|
||||||
|
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)) end
|
||||||
|
print(string.format("[%s] Module initialized", ModuleName))
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
1056
Modules/Whoer.lua
1056
Modules/Whoer.lua
File diff suppressed because it is too large
Load Diff
BIN
Sounds/MGSSpot.ogg
(Stored with Git LFS)
Normal file
BIN
Sounds/MGSSpot.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Sounds/MedicGangsterParadise.ogg
(Stored with Git LFS)
Normal file
BIN
Sounds/MedicGangsterParadise.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Sounds/OOF.ogg
(Stored with Git LFS)
Normal file
BIN
Sounds/OOF.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Sounds/StarScream.ogg
(Stored with Git LFS)
Normal file
BIN
Sounds/StarScream.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura1.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura1.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura10.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura10.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura100.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura100.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura101.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura101.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura102.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura102.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura103.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura103.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura104.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura104.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura105.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura105.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura106.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura106.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura107.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura107.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura108.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura108.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura109.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura109.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura11.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura11.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura110.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura110.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura111.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura111.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura112.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura112.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura113.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura113.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura114.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura114.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura115.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura115.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura116.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura116.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura117.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura117.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura118.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura118.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura119.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura119.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura12.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura12.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura120.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura120.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura121.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura121.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura122.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura122.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura123.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura123.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura124.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura124.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura125.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura125.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura126.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura126.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura127.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura127.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura128.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura128.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura129.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura129.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura13.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura13.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura130.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura130.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura131.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura131.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura132.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura132.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura133.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura133.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura134.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura134.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura135.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura135.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura136.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura136.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura137.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura137.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura138.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura138.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura139.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura139.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura14.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura14.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura140.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura140.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura141.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura141.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura142.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura142.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura143.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura143.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura144.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura144.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura145.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura145.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura15.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura15.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura16.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura16.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura17.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura17.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura18.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura18.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura19.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura19.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Texture/Aura2.tga
(Stored with Git LFS)
Normal file
BIN
Texture/Aura2.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user