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.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).
2. **Sub-Events:**
- **Schaden:** `SWING_DAMAGE`, `SPELL_DAMAGE`, `RANGE_DAMAGE`, `SPELL_PERIODIC_DAMAGE`, `SPELL_BUILDING_DAMAGE`.
- Effektiver Schaden: `amount + absorbed` (Absorbe werden dem Treffer zugerechnet).
- Bedingung: `critical == true`.
- **Heilung:** `SPELL_HEAL`, `SPELL_PERIODIC_HEAL`, `SPELL_BUILDING_HEAL`.
- Effektive Heilung: `amount + overhealing` (Overheal wird berücksichtigt, damit große Heiler-Crits bei voller HP nicht fälschlich als "Low Crit" eingestuft werden).
- Bedingung: `critical == true`.
1. **Forever Secret Values Schutz:**
Parameter werden mit `issecretvalue(val)` geprüft. Handelt es sich um ein geschütztes Geheimnis oder `nil`, wird das Event verworfen.
2. **Krit-Filter:**
Nur Events mit `modifier == "CRITICAL"` und numerischem `amount > 0` werden akzeptiert.
3. **Schaden-Filter (Crucial Anti-Triplet):**
- Es wird ausschließlich `action == "WOUND"` und `unit == "target"` ausgewertet.
- Alle Pseudo-Einheiten wie `nameplate*`, `softenemy` etc. werden verworfen, da der Client denselben Treffer sonst 3-fach meldet.
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
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 -- Ja --> C["Hole bisherigen Rekord (Schaden/Heilung)"]
B -- Ja --> C{"action & unit Check"}
C --> D{"amount > currentRecord?"}
D -- Ja --> E["Neuer Rekord! Aktualisiere charData (maxRecord = amount)"]
D -- Nein --> F{"enableLowCrit UND currentRecord > 0 UND amount <= (currentRecord * threshold)?"}
C -- "WOUND & unit == 'target'" --> D["isDamage = true"]
C -- "HEAL & (target oder player ohne Self-Duplicate)" --> E["isHeal = true"]
C -- Andere Units/Actions --> End
F -- Ja --> G["Als Low-Crit markieren (Sound: low.mp3)"]
F -- Nein --> H["Als normaler Crit markieren (Sound: critX / healX)"]
D --> F["Löse school via SCHOOL_NAMES auf"]
E --> F
E --> I{"GetTime() - lastOutputTime < cooldown?"}
G --> I
H --> I
F --> G["Hole bisherigen Rekord (charData.maxDamage / maxHeal)"]
G --> H{"amount > currentRecord?"}
I -- Ja (im Cooldown) --> J["Ausgabe unterdrückt (Anti-Spam)"]
I -- Nein (Cooldown frei) --> K["lastOutputTime = GetTime()"]
H -- Ja --> I["Neuer Rekord! Aktualisiere charData (maxRecord = amount)"]
H -- Nein --> J{"enableLowCrit UND currentRecord > 0 UND amount <= threshold?"}
K --> L["PlaySoundFile(soundPath, soundChannel)"]
K --> M["DEFAULT_CHAT_FRAME:AddMessage(formatierte Nachricht)"]
J -- Ja --> K["Als Low-Crit markieren (Sound: low.mp3)"]
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)"]
```
---