Wechsel auf UNIT_COMBAT Engine (WoW Forever Beta Interface 16001)

- Entferne deaktiviertes COMBAT_LOG_EVENT_UNFILTERED
- Implementiere UNIT_COMBAT mit Anti-Triplet Filter (unit == 'target')
- Doppel-Auslösungs-Schutz bei Selbstheilung
- Mapping von school auf lokalisierte Schadensschulen (SCHOOL_NAMES)
- Parameter-Validierung mit issecretvalue
- AGENT_GUIDE.md aktualisiert
This commit is contained in:
2026-09-30 10:09:23 +02:00
parent edc52fdd86
commit bf81a9f75a
2 changed files with 129 additions and 131 deletions

View File

@@ -74,45 +74,71 @@ CritTrackerDB = {
--- ---
---
## 4. Logik- & Entscheidungsfluss (Core Engine) ## 4. Logik- & Entscheidungsfluss (Core Engine)
### 4.1. Combat Log Filterung (`COMBAT_LOG_EVENT_UNFILTERED`) ### 4.1. Warum `UNIT_COMBAT` statt `COMBAT_LOG_EVENT_UNFILTERED`? (Root Cause)
Im "WoW Forever"-Client (Interface 16001 / 12.x Engine) ist die sogenannte Addon-Apokalypse aktiv:
- `COMBAT_LOG_EVENT_UNFILTERED` wurde von Blizzard für Addons **vollständig deaktiviert** und feuert niemals.
- Kritische Treffer und Heilungen müssen zwingend über das Event `UNIT_COMBAT` abgefangen werden.
Die Engine verwendet `CombatLogGetCurrentEventInfo()` mit folgenden Regeln: ### 4.2. Filterregeln & Mehrfachauslösungs-Schutz
`UNIT_COMBAT` feuert mit den Argumenten `(unit, action, modifier, amount, school)`.
1. **Source-Check:** Nur Events mit `sourceGUID == UnitGUID("player")` werden verarbeitet (Spieler-Treffer). 1. **Forever Secret Values Schutz:**
2. **Sub-Events:** Parameter werden mit `issecretvalue(val)` geprüft. Handelt es sich um ein geschütztes Geheimnis oder `nil`, wird das Event verworfen.
- **Schaden:** `SWING_DAMAGE`, `SPELL_DAMAGE`, `RANGE_DAMAGE`, `SPELL_PERIODIC_DAMAGE`, `SPELL_BUILDING_DAMAGE`. 2. **Krit-Filter:**
- Effektiver Schaden: `amount + absorbed` (Absorbe werden dem Treffer zugerechnet). Nur Events mit `modifier == "CRITICAL"` und numerischem `amount > 0` werden akzeptiert.
- Bedingung: `critical == true`. 3. **Schaden-Filter (Crucial Anti-Triplet):**
- **Heilung:** `SPELL_HEAL`, `SPELL_PERIODIC_HEAL`, `SPELL_BUILDING_HEAL`. - Es wird ausschließlich `action == "WOUND"` und `unit == "target"` ausgewertet.
- Effektive Heilung: `amount + overhealing` (Overheal wird berücksichtigt, damit große Heiler-Crits bei voller HP nicht fälschlich als "Low Crit" eingestuft werden). - Alle Pseudo-Einheiten wie `nameplate*`, `softenemy` etc. werden verworfen, da der Client denselben Treffer sonst 3-fach meldet.
- Bedingung: `critical == true`. 4. **Heilung-Filter (Anti-Selfheal-Duplicate):**
- Akzeptiert `action == "HEAL"` für `unit == "target"` oder `unit == "player"`.
- Bei Selbstheilung mit sich selbst im Ziel (`UnitIsUnit("player", "target")`) wird die Auswertung auf `unit == "target"` beschränkt, um doppelte Auslösungen zu verhindern.
5. **Schadensschulen-Mapping (`SCHOOL_NAMES`):**
Da `UNIT_COMBAT` keine konkreten Zaubernamen liefert, wird das Argument `school` auf die jeweilige Schadensart gemappt:
- `0` / `1` = Physisch / Physical
- `2` = Heilig / Holy
- `4` = Feuer / Fire
- `8` = Natur / Nature
- `16` = Frost / Frost
- `32` = Schatten / Shadow
- `64` = Arkan / Arcane
### 4.2. Ablaufdiagramm der Crit-Verarbeitung ### 4.3. Ablaufdiagramm der Crit-Verarbeitung
```mermaid ```mermaid
flowchart TD flowchart TD
A["Combat Log Crit Event (Player)"] --> B{"Addon aktiviert?"} A["Event: UNIT_COMBAT(unit, action, modifier, amount, school)"] --> B{"IsPublic? modifier == 'CRITICAL'?"}
B -- Nein --> End[Abbruch] B -- Nein --> End[Abbruch]
B -- Ja --> C["Hole bisherigen Rekord (Schaden/Heilung)"] B -- Ja --> C{"action & unit Check"}
C --> D{"amount > currentRecord?"} C -- "WOUND & unit == 'target'" --> D["isDamage = true"]
D -- Ja --> E["Neuer Rekord! Aktualisiere charData (maxRecord = amount)"] C -- "HEAL & (target oder player ohne Self-Duplicate)" --> E["isHeal = true"]
D -- Nein --> F{"enableLowCrit UND currentRecord > 0 UND amount <= (currentRecord * threshold)?"} C -- Andere Units/Actions --> End
F -- Ja --> G["Als Low-Crit markieren (Sound: low.mp3)"] D --> F["Löse school via SCHOOL_NAMES auf"]
F -- Nein --> H["Als normaler Crit markieren (Sound: critX / healX)"] E --> F
E --> I{"GetTime() - lastOutputTime < cooldown?"} F --> G["Hole bisherigen Rekord (charData.maxDamage / maxHeal)"]
G --> I G --> H{"amount > currentRecord?"}
H --> I
I -- Ja (im Cooldown) --> J["Ausgabe unterdrückt (Anti-Spam)"] H -- Ja --> I["Neuer Rekord! Aktualisiere charData (maxRecord = amount)"]
I -- Nein (Cooldown frei) --> K["lastOutputTime = GetTime()"] H -- Nein --> J{"enableLowCrit UND currentRecord > 0 UND amount <= threshold?"}
K --> L["PlaySoundFile(soundPath, soundChannel)"] J -- Ja --> K["Als Low-Crit markieren (Sound: low.mp3)"]
K --> M["DEFAULT_CHAT_FRAME:AddMessage(formatierte Nachricht)"] J -- Nein --> L["Als normaler Crit markieren (Sound: critX / healX)"]
I --> M{"GetTime() - lastOutputTime < cooldown?"}
K --> M
L --> M
M -- Ja (im Cooldown) --> N["Ausgabe unterdrückt (Anti-Spam)"]
M -- Nein (Cooldown frei) --> O["lastOutputTime = GetTime()"]
O --> P["PlaySoundFile(soundPath, soundChannel)"]
O --> Q["DEFAULT_CHAT_FRAME:AddMessage(formatierte Nachricht)"]
``` ```
--- ---

View File

@@ -1,6 +1,6 @@
--[[ --[[
CritTracker - Core Engine CritTracker - Core Engine (WoW Forever Edition)
World of Warcraft - Forever (12.x Beta / Interface 16001) Optimiert für Interface 16001 / UNIT_COMBAT Engine
]] ]]
local addonName, addonTable = ... local addonName, addonTable = ...
@@ -12,23 +12,45 @@ _G["CritTracker"] = CritTracker
-- Timing & State -- Timing & State
CritTracker.lastOutputTime = 0 CritTracker.lastOutputTime = 0
-- Defaults -- Standardeinstellungen
local defaultSettings = { local defaultSettings = {
enabled = true, enabled = true,
enableChat = true, enableChat = true,
enableSound = true, enableSound = true,
enableLowCrit = true, enableLowCrit = true,
lowThreshold = 30, -- 30% of highest recorded crit lowThreshold = 30, -- 30% des Rekords
cooldown = 5, -- 5 seconds output cooldown (1-30s) cooldown = 5, -- Cooldown in Sekunden
damageSound = "crit1", -- crit1, crit2, crit3, crit4, none damageSound = "crit1", -- crit1, crit2, crit3, crit4, none
healSound = "heal1", -- heal1, heal2, heal3, heal4, none healSound = "heal1", -- heal1, heal2, heal3, heal4, none
lowSound = "low", -- low, none lowSound = "low", -- low, none
soundChannel = "Master", -- Master, Dialog, SFX soundChannel = "Master", -- Master, Dialog, SFX
language = "auto", -- "auto", "German", "English" language = "auto", -- "auto", "German", "English"
announceMode = "all", -- "all" (subject to cooldown) or "records_only" announceMode = "all", -- "all" oder "records_only"
} }
-- Dynamic Localization Proxy -- Schadensschulen-Übersetzung für UNIT_COMBAT
local SCHOOL_NAMES = {
[0] = { deDE = "Physisch", enUS = "Physical" },
[1] = { deDE = "Physisch", enUS = "Physical" },
[2] = { deDE = "Heilig", enUS = "Holy" },
[4] = { deDE = "Feuer", enUS = "Fire" },
[8] = { deDE = "Natur", enUS = "Nature" },
[16] = { deDE = "Frost", enUS = "Frost" },
[32] = { deDE = "Schatten", enUS = "Shadow" },
[64] = { deDE = "Arkan", enUS = "Arcane" },
}
-- Schutz vor Forever Secret Values
local function IsPublic(val)
if type(val) == "nil" then return false end
if type(issecretvalue) == "function" then
local ok, secret = pcall(issecretvalue, val)
if ok and secret == true then return false end
end
return true
end
-- Lokalisierungs-Proxy
CritTracker.L = setmetatable({}, { CritTracker.L = setmetatable({}, {
__index = function(_, key) __index = function(_, key)
local lang = CritTracker:GetActiveLanguage() local lang = CritTracker:GetActiveLanguage()
@@ -44,7 +66,6 @@ CritTracker.L = setmetatable({}, {
end end
}) })
-- Language Resolution
function CritTracker:GetActiveLanguage() function CritTracker:GetActiveLanguage()
local setting = (self.db and self.db.language) or "auto" local setting = (self.db and self.db.language) or "auto"
if setting == "German" then if setting == "German" then
@@ -52,25 +73,18 @@ function CritTracker:GetActiveLanguage()
elseif setting == "English" then elseif setting == "English" then
return "English" return "English"
else else
-- Auto: German client -> German, all others -> English
local locale = GetLocale() local locale = GetLocale()
if locale == "deDE" then return (locale == "deDE") and "German" or "English"
return "German"
else
return "English"
end
end end
end end
-- Sound File Path
function CritTracker:GetSoundFilePath(soundKey) function CritTracker:GetSoundFilePath(soundKey)
local lang = self:GetActiveLanguage() local lang = self:GetActiveLanguage()
return "Interface\\AddOns\\CritTracker\\sounds\\" .. lang .. "\\" .. soundKey .. ".mp3" return "Interface\\AddOns\\CritTracker\\sounds\\" .. lang .. "\\" .. soundKey .. ".mp3"
end end
-- Play Sound
function CritTracker:PlaySound(soundKey) function CritTracker:PlaySound(soundKey)
if not self.db.enableSound or not soundKey or soundKey == "none" then if not self.db or not self.db.enableSound or not soundKey or soundKey == "none" then
return return
end end
local path = self:GetSoundFilePath(soundKey) local path = self:GetSoundFilePath(soundKey)
@@ -78,7 +92,6 @@ function CritTracker:PlaySound(soundKey)
PlaySoundFile(path, channel) PlaySoundFile(path, channel)
end end
-- Print Message (Visible strictly to the local player)
function CritTracker:Print(msg) function CritTracker:Print(msg)
if not msg then return end if not msg then return end
if DEFAULT_CHAT_FRAME then if DEFAULT_CHAT_FRAME then
@@ -88,7 +101,6 @@ function CritTracker:Print(msg)
end end
end end
-- Format Numbers with commas / dots
function CritTracker:FormatNumber(n) function CritTracker:FormatNumber(n)
if not n then return "0" end if not n then return "0" end
if BreakUpLargeNumbers then if BreakUpLargeNumbers then
@@ -99,7 +111,6 @@ function CritTracker:FormatNumber(n)
return left .. (num:reverse():gsub('(%d%d%d)', '%1.'):reverse()) .. right return left .. (num:reverse():gsub('(%d%d%d)', '%1.'):reverse()) .. right
end end
-- Character Data Accessor (strictly per character & realm)
function CritTracker:GetCharKey() function CritTracker:GetCharKey()
local name = UnitName("player") or "Unknown" local name = UnitName("player") or "Unknown"
local realm = GetRealmName() or "UnknownRealm" local realm = GetRealmName() or "UnknownRealm"
@@ -107,12 +118,8 @@ function CritTracker:GetCharKey()
end end
function CritTracker:GetCharData() function CritTracker:GetCharData()
if not CritTrackerDB then if not CritTrackerDB then CritTrackerDB = {} end
CritTrackerDB = {} if not CritTrackerDB.chars then CritTrackerDB.chars = {} end
end
if not CritTrackerDB.chars then
CritTrackerDB.chars = {}
end
local key = self:GetCharKey() local key = self:GetCharKey()
if not CritTrackerDB.chars[key] then if not CritTrackerDB.chars[key] then
CritTrackerDB.chars[key] = { CritTrackerDB.chars[key] = {
@@ -126,7 +133,6 @@ function CritTracker:GetCharData()
return CritTrackerDB.chars[key] return CritTrackerDB.chars[key]
end end
-- Reset Records for Current Character
function CritTracker:ResetRecords() function CritTracker:ResetRecords()
local charData = self:GetCharData() local charData = self:GetCharData()
charData.maxDamage = 0 charData.maxDamage = 0
@@ -143,7 +149,6 @@ function CritTracker:ResetRecords()
end end
end end
-- Level Up Handler
function CritTracker:OnLevelUp(newLevel) function CritTracker:OnLevelUp(newLevel)
local charData = self:GetCharData() local charData = self:GetCharData()
charData.maxDamage = 0 charData.maxDamage = 0
@@ -159,14 +164,9 @@ function CritTracker:OnLevelUp(newLevel)
end end
end end
-- Initialization
function CritTracker:OnInitialize() function CritTracker:OnInitialize()
if not CritTrackerDB then if not CritTrackerDB then CritTrackerDB = {} end
CritTrackerDB = {} if not CritTrackerDB.settings then CritTrackerDB.settings = {} end
end
if not CritTrackerDB.settings then
CritTrackerDB.settings = {}
end
for k, v in pairs(defaultSettings) do for k, v in pairs(defaultSettings) do
if CritTrackerDB.settings[k] == nil then if CritTrackerDB.settings[k] == nil then
CritTrackerDB.settings[k] = v CritTrackerDB.settings[k] = v
@@ -174,11 +174,9 @@ function CritTracker:OnInitialize()
end end
self.db = CritTrackerDB.settings self.db = CritTrackerDB.settings
-- Initialize Character
local charData = self:GetCharData() local charData = self:GetCharData()
local curLevel = UnitLevel("player") local curLevel = UnitLevel("player")
if charData.level and charData.level > 0 and curLevel > charData.level then if charData.level and charData.level > 0 and curLevel > charData.level then
-- Leveled up while offline / addon disabled
self:OnLevelUp(curLevel) self:OnLevelUp(curLevel)
else else
charData.level = curLevel charData.level = curLevel
@@ -189,8 +187,7 @@ function CritTracker:OnInitialize()
end end
end end
-- Main Crit Processing function CritTracker:ProcessCrit(isHeal, spellOrSchool, amount, destName)
function CritTracker:ProcessCrit(isHeal, spellName, amount, destName)
if not self.db or not self.db.enabled or not amount or amount <= 0 then if not self.db or not self.db.enabled or not amount or amount <= 0 then
return return
end end
@@ -203,19 +200,17 @@ function CritTracker:ProcessCrit(isHeal, spellName, amount, destName)
local thresholdPercent = self.db.lowThreshold or 30 local thresholdPercent = self.db.lowThreshold or 30
local thresholdValue = math.floor(currentRecord * (thresholdPercent / 100)) local thresholdValue = math.floor(currentRecord * (thresholdPercent / 100))
-- Check for Low Crit
if self.db.enableLowCrit and currentRecord > 0 and not isNewRecord and amount <= thresholdValue then if self.db.enableLowCrit and currentRecord > 0 and not isNewRecord and amount <= thresholdValue then
isLowCrit = true isLowCrit = true
end end
-- Update Record if higher
if isNewRecord then if isNewRecord then
if isHeal then if isHeal then
charData.maxHeal = amount charData.maxHeal = amount
charData.maxHealSpell = spellName or "" charData.maxHealSpell = spellOrSchool or ""
else else
charData.maxDamage = amount charData.maxDamage = amount
charData.maxDamageSpell = spellName or "" charData.maxDamageSpell = spellOrSchool or ""
end end
if self.UpdateOptionsDisplay then if self.UpdateOptionsDisplay then
@@ -223,19 +218,16 @@ function CritTracker:ProcessCrit(isHeal, spellName, amount, destName)
end end
end end
-- Cooldown Check for Audio and Text Output
local now = GetTime() local now = GetTime()
local cooldown = self.db.cooldown or 5 local cooldown = self.db.cooldown or 5
if (now - self.lastOutputTime) < cooldown then if (now - self.lastOutputTime) < cooldown then
-- Suppressed by Cooldown (Records are already preserved)
return return
end end
-- Determine Sound and Text
local soundKey = nil local soundKey = nil
local chatMsg = nil local chatMsg = nil
local destStr = (destName and destName ~= "") and string.format(self.L["TARGET_ON"], destName) or "" local destStr = (destName and destName ~= "") and string.format(self.L["TARGET_ON"], destName) or ""
local spellStr = (spellName and spellName ~= "") and spellName or self.L["UNKNOWN"] local spellStr = (spellOrSchool and spellOrSchool ~= "") and spellOrSchool or (isHeal and "Heilung" or "Treffer")
local amountStr = self:FormatNumber(amount) local amountStr = self:FormatNumber(amount)
local recordStr = self:FormatNumber(currentRecord) local recordStr = self:FormatNumber(currentRecord)
@@ -248,7 +240,6 @@ function CritTracker:ProcessCrit(isHeal, spellName, amount, destName)
local formatStr = isHeal and self.L["NEW_RECORD_HEAL"] or self.L["NEW_RECORD_DAMAGE"] local formatStr = isHeal and self.L["NEW_RECORD_HEAL"] or self.L["NEW_RECORD_DAMAGE"]
chatMsg = string.format(formatStr, destStr, spellStr, amountStr) chatMsg = string.format(formatStr, destStr, spellStr, amountStr)
else else
-- Normal Crit (between threshold and current record)
if self.db.announceMode == "all" then if self.db.announceMode == "all" then
soundKey = isHeal and (self.db.healSound or "heal1") or (self.db.damageSound or "crit1") soundKey = isHeal and (self.db.healSound or "heal1") or (self.db.damageSound or "crit1")
local formatStr = isHeal and self.L["NORMAL_CRIT_HEAL"] or self.L["NORMAL_CRIT_DAMAGE"] local formatStr = isHeal and self.L["NORMAL_CRIT_HEAL"] or self.L["NORMAL_CRIT_DAMAGE"]
@@ -256,7 +247,6 @@ function CritTracker:ProcessCrit(isHeal, spellName, amount, destName)
end end
end end
-- If something triggered, apply cooldown and output
if soundKey or chatMsg then if soundKey or chatMsg then
self.lastOutputTime = now self.lastOutputTime = now
@@ -270,74 +260,56 @@ function CritTracker:ProcessCrit(isHeal, spellName, amount, destName)
end end
end end
-- Combat Log Parser -- UNIT_COMBAT Parser
function CritTracker:OnCombatLogEvent() function CritTracker:OnUnitCombat(unit, action, modifier, amount, school)
local timestamp, subevent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, if not IsPublic(unit) or not IsPublic(action) or not IsPublic(modifier) or not IsPublic(amount) then
destGUID, destName, destFlags, destRaidFlags,
arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20,
arg21, arg22, arg23, arg24 = CombatLogGetCurrentEventInfo()
if not sourceGUID or sourceGUID ~= UnitGUID("player") then
return return
end end
if subevent == "SWING_DAMAGE" then if modifier ~= "CRITICAL" or type(amount) ~= "number" or amount <= 0 then
local amount = arg12 return
local overkill = arg13
local school = arg14
local resisted = arg15
local blocked = arg16
local absorbed = arg17
local critical = arg18
if critical and critical ~= 0 then
local totalDamage = (amount or 0) + (absorbed or 0)
self:ProcessCrit(false, self.L["MELEE_ATTACK"], totalDamage, destName)
end end
elseif subevent == "SPELL_DAMAGE" or subevent == "RANGE_DAMAGE" local isDamage = false
or subevent == "SPELL_PERIODIC_DAMAGE" or subevent == "SPELL_BUILDING_DAMAGE" then local isHeal = false
local spellId = arg12
local spellName = arg13
local spellSchool = arg14
local amount = arg15
local overkill = arg16
local school = arg17
local resisted = arg18
local blocked = arg19
local absorbed = arg20
local critical = arg21
if critical and critical ~= 0 then -- Schaden: Nur 'target' werten (verhindert Alias-Triplets durch nameplate1/softenemy)
local totalDamage = (amount or 0) + (absorbed or 0) if action == "WOUND" and unit == "target" then
self:ProcessCrit(false, spellName, totalDamage, destName) isDamage = true
end
elseif subevent == "SPELL_HEAL" or subevent == "SPELL_PERIODIC_HEAL" or subevent == "SPELL_BUILDING_HEAL" then -- Heilung: 'target' oder 'player'
local spellId = arg12 elseif action == "HEAL" then
local spellName = arg13 if unit == "target" then
local spellSchool = arg14 isHeal = true
local amount = arg15 elseif unit == "player" then
local overhealing = arg16 -- Verhindert doppelte Auslösung bei Ziel auf sich selbst
local absorbed = arg17 if not UnitIsUnit("player", "target") then
local critical = arg18 isHeal = true
if critical and critical ~= 0 then
local totalHeal = (amount or 0) + (overhealing or 0)
self:ProcessCrit(true, spellName, totalHeal, destName)
end end
end end
end
if not isDamage and not isHeal then return end
local targetName = UnitName(unit) or (isDamage and "Ziel" or "Spieler")
-- Schadensart / Schule auflösen
local langKey = (self:GetActiveLanguage() == "German") and "deDE" or "enUS"
local schoolName = (school and SCHOOL_NAMES[school] and SCHOOL_NAMES[school][langKey])
or (isHeal and (langKey == "deDE" and "Heilung" or "Heal") or (langKey == "deDE" and "Angriff" or "Attack"))
self:ProcessCrit(isHeal, schoolName, amount, targetName)
end end
-- Event Registration -- Event Registrierung
CritTracker:RegisterEvent("ADDON_LOADED") CritTracker:RegisterEvent("ADDON_LOADED")
CritTracker:RegisterEvent("PLAYER_LOGIN") CritTracker:RegisterEvent("PLAYER_LOGIN")
CritTracker:RegisterEvent("PLAYER_LEVEL_UP") CritTracker:RegisterEvent("PLAYER_LEVEL_UP")
CritTracker:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") CritTracker:RegisterEvent("UNIT_COMBAT")
CritTracker:SetScript("OnEvent", function(self, event, ...) CritTracker:SetScript("OnEvent", function(self, event, ...)
if event == "COMBAT_LOG_EVENT_UNFILTERED" then if event == "UNIT_COMBAT" then
self:OnCombatLogEvent() self:OnUnitCombat(...)
elseif event == "PLAYER_LEVEL_UP" then elseif event == "PLAYER_LEVEL_UP" then
local newLevel = ... local newLevel = ...
self:OnLevelUp(newLevel) self:OnLevelUp(newLevel)
@@ -357,7 +329,7 @@ CritTracker:SetScript("OnEvent", function(self, event, ...)
end end
end) end)
-- Slash Commands -- Slash Commands (/ctr & /crittracker)
SLASH_CRITTRACKER1 = "/crittracker" SLASH_CRITTRACKER1 = "/crittracker"
SLASH_CRITTRACKER2 = "/ctr" SLASH_CRITTRACKER2 = "/ctr"
SlashCmdList["CRITTRACKER"] = function(msg) SlashCmdList["CRITTRACKER"] = function(msg)
@@ -380,13 +352,13 @@ SlashCmdList["CRITTRACKER"] = function(msg)
elseif cmd == "test" then elseif cmd == "test" then
if arg == "heal" then if arg == "heal" then
CritTracker:PlaySound(CritTracker.db.healSound or "heal1") CritTracker:PlaySound(CritTracker.db.healSound or "heal1")
CritTracker:Print(string.format(CritTracker.L["NORMAL_CRIT_HEAL"], "", "Test Heal", CritTracker:FormatNumber(50000))) CritTracker:Print(string.format(CritTracker.L["NORMAL_CRIT_HEAL"], "", "Heilig", CritTracker:FormatNumber(50000)))
elseif arg == "low" then elseif arg == "low" then
CritTracker:PlaySound("low") CritTracker:PlaySound("low")
CritTracker:Print(string.format(CritTracker.L["LOW_CRIT_DAMAGE"], "", "Test Low Hit", CritTracker:FormatNumber(150), CritTracker.db.lowThreshold, CritTracker:FormatNumber(1000))) CritTracker:Print(string.format(CritTracker.L["LOW_CRIT_DAMAGE"], "", "Physisch", CritTracker:FormatNumber(150), CritTracker.db.lowThreshold, CritTracker:FormatNumber(1000)))
else else
CritTracker:PlaySound(CritTracker.db.damageSound or "crit1") CritTracker:PlaySound(CritTracker.db.damageSound or "crit1")
CritTracker:Print(string.format(CritTracker.L["NORMAL_CRIT_DAMAGE"], "", "Test Strike", CritTracker:FormatNumber(125000))) CritTracker:Print(string.format(CritTracker.L["NORMAL_CRIT_DAMAGE"], "", "Feuer", CritTracker:FormatNumber(125000)))
end end
else else
CritTracker:Print(CritTracker.L["SLASH_HELP_HEADER"]) CritTracker:Print(CritTracker.L["SLASH_HELP_HEADER"])