21 Commits
Author SHA1 Message Date
Mquadrat 8a8c730f9b Update README.md 2026-09-08 22:26:28 +02:00
Mquadrat 52a0e84a33 Add files via upload 2026-09-08 22:24:17 +02:00
Mquadrat e99d32ba1e Add files via upload 2026-09-08 22:23:46 +02:00
Mquadrat 61b66b5145 Update README.md 2026-09-08 20:58:43 +02:00
Mquadrat 0051f43861 Update README.md 2026-09-08 20:36:40 +02:00
Mquadrat a408984895 Add files via upload 2026-09-08 20:36:03 +02:00
Mquadrat 1689e5cc0c Add files via upload 2026-09-08 20:10:10 +02:00
Mquadrat 6d1915e807 Add files via upload 2026-09-08 19:45:12 +02:00
Mquadrat 47181f0f17 Update README.md 2026-09-08 19:44:22 +02:00
Mquadrat 3a7ac1c851 Update CHANGELOG.md 2026-09-07 14:35:53 +02:00
Mquadrat 6c4f1d5b8f Update README.md 2026-09-07 14:25:09 +02:00
Mquadrat 529a7dcc14 Add files via upload 2026-09-07 14:23:29 +02:00
Mquadrat 254071d1f5 Add files via upload 2026-09-06 19:31:19 +02:00
QbiXibQ 23d3e45d0f Update README.md 2026-09-06 12:38:30 +02:00
QbiXibQ 4b36163446 Update README.md 2026-09-06 12:26:23 +02:00
QbiXibQ cf9a766745 Add files via upload 2026-09-06 12:25:49 +02:00
QbiXibQ c3f89148df Update README.md 2026-09-06 09:58:59 +02:00
QbiXibQ edf9a689e1 Update CHANGELOG.md 2026-09-06 09:47:45 +02:00
QbiXibQ 1c0424a74f Update README.md 2026-09-06 09:46:29 +02:00
QbiXibQ 0ab8b1a36c Add files via upload 2026-09-06 09:45:25 +02:00
QbiXibQ 09d96c95d2 Update README.md 2026-09-05 21:04:00 +02:00
7 changed files with 1969 additions and 647 deletions
+161 -3
View File
@@ -1,6 +1,164 @@
# Changelog # Changelog
## 1.4.3 (unreleased) ## 1.4.4.3 (2026-09-08)
Class gate, heal chains and a Smart Healing fix. The addon no longer loads its display for warriors, rogues and hunters, Smart Healing keeps its hands off heal over time spells, and downranking may now switch spell inside a heal chain.
### English
**New & Changed**
- **Smartcross option in UI.** Added a *Smartcross* checkbox under Smart Healing in the *Buttons* tab. It is automatically enabled/disabled and grayed out based on the Smart Healing master switch (also toggleable via `/fbp smartcross`).
- **Streamlined Smart Healing tooltip.** Overhauled the options tooltip for Smart Healing across all languages. Replaced the screen-filling block of text with a compact, readable bullet-point summary of key rules.
- **Priest heal chain cleaned.** Completely removed *Flash Heal* from the Priest heal chain (`Lesser Heal · Heal · Greater Heal`). Flash Heal is preserved as a dedicated fast emergency heal and is never swapped into from Greater Heal or Heal.
- **Downranking across spells.** Until now Smart Healing only ever lowered the rank of the spell on the button, so Greater Heal (rank 4) could become Greater Heal (rank 1) but never Lesser Heal (rank 3), even though that would have covered the deficit for a fraction of the mana. Every single-target direct heal of a class now forms a *heal chain*, and the whole chain is searched: Priest `Lesser Heal · Heal · Greater Heal`, Paladin `Flash of Light · Holy Light`, Shaman `Lesser Healing Wave · Healing Wave`, Druid `Healing Touch`. The candidate with the smallest expected heal that still covers the need wins.
- **The button never gets stronger.** Nothing that heals more than the rank you assigned is ever chosen, and a tie keeps the assigned spell. Note that the cast time can change: for a small deficit a Greater Heal click may come out as Lesser Heal, and Holy Light as Flash of Light.
- **What stays out of the chains.** Group heals (Prayer of Healing, Chain Heal), fast emergency heals (Flash Heal), HoTs (Renew, Rejuvenation, Regrowth), shields and cooldown spells (Holy Shock, Lay on Hands) are never swapped. The chains sit in `FBHealChains` and can be edited freely; German spell names are included, with special characters stored as byte sequences for both Latin-1 and UTF-8 clients.
- **New command `/fbp smartcross`.** Turns chain switching off and on, saved per character in `HealBox.SmartCross` (on by default). With it off, Smart Healing stays on the assigned spell and only lowers its rank. `/fbp` shows the state next to the Smart Healing line, and the command says so when Smart Healing itself is still off.
- **Class gate for warriors, rogues and hunters.** The addon is built for healing and buffing, and those three classes get nothing out of it: no heals, no heal prediction, and a mana ticker with either no mana to watch (rage, energy) or nobody to heal (hunter). Logging in on one of them now leaves the whole display switched off: no plates, no raid grid, no minimap button, no options window. All event handlers and `OnUpdate` loops of the core and of the raid, ticker and Smart Damage modules return immediately (`FBAddonSuppressed`), so the addon costs nothing while it sleeps. Two lines in the chat explain what happened and how to change it.
- **New command `/fbp forceload`.** Shows the addon on a blocked class anyway. It starts immediately, without `/reload`, and the setting is kept per character in `HealBox.ForceLoad`. The same command switches it back off. On every other class the command reports that it is not needed. While the addon sleeps, every other `/fbp` command answers with the same hint instead of reporting on a display that was never built.
- **Class detection.** Uses the client's English class token (`WARRIOR`, `ROGUE`, `HUNTER`) with a fallback to the displayed class name in five languages, plus a last resort over the power type (rage and energy are never mana classes).
- **New hook `Suppress`.** Modules hide their own frames when the core goes to sleep; the raid module hides the grid, the ticker hides its sparks.
- **Startup message moved.** The greeting and the modules' `Loaded` hooks now run on `ADDON_LOADED` / `VARIABLES_LOADED` instead of `FBHealBox_OnLoad`, so the saved language is applied and nothing is announced for a class that stays asleep.
- **Rank rules generalised.** The upper limit is no longer "never above the assigned rank" but "never more expected healing than the assigned rank", which is the same thing inside one spell and the right rule across spells. A rank whose tooltip cannot be read is now skipped instead of ending the search.
**Fixed**
- **Smart Healing no longer downranks heal over time spells.** Renew, Rejuvenation, Regrowth, Tranquility, Lifebloom, Wild Growth, Riptide and Earth Shield now always go out at the assigned rank, for every class. A HoT spreads its healing over many seconds, so the health missing at the moment of the click says nothing about which rank fits, and a downranked HoT keeps ticking too weakly for its whole duration. Mixed spells such as Regrowth count as HoTs as well.
- **HoT detection** The new `FBHealBox_IsHoT()` checks the name list `FBHoTSpells` (English and German names, umlauts stored as byte sequences for both Latin-1 and UTF-8 clients), the tooltip evaluation (`info.hot`), the spell watch (`hasHoT`) and the HoTs currently running from the combat log. The exception therefore also applies to spells that are not in the list, for instance on custom servers. In addition the per-rank check now rejects any rank with a HoT portion.
---
### Deutsch
**Neu & Geaendert**
- **Smartcross im Optionsfenster.** Checkbox unter Smart Healing im Reiter *Buttons* hinzugefügt. Sie steuert das zauberübergreifende Abrangen in Heilketten und wird dynamisch gesperrt und ausgegraut, wenn Smart Healing deaktiviert ist (weiterhin auch per `/fbp smartcross` schaltbar).
- **Gestraffter Smart-Healing-Tooltip.** Der bisherige riesige Fließtextblock im Optionsfenster wurde in allen Sprachen durch eine übersichtliche, stichpunktartige Zusammenfassung der Kernregeln ersetzt. Der Tooltip sprengt damit nicht mehr den Bildschirm.
- **Priester-Heilkette bereinigt.** *Blitzheilung* (*Flash Heal*) vollständig aus der Priesterkette entfernt (`Geringes Heilen · Heilen · Große Heilung`). Blitzheilung bleibt ein reiner schneller Notfallzauber und wird von Großer Heilung / Heilen nie ungewollt ausgewählt.
- **Abrangen ueber Zaubergrenzen.** Bisher senkte Smart Healing nur den Rang des Zaubers auf dem Button: Aus Große Heilung (Rang 4) konnte Große Heilung (Rang 1) werden, nie aber Geringes Heilen (Rang 3), obwohl das den Fehlbetrag für einen Bruchteil des Manas gedeckt hätte. Alle Einzelziel-Direktheilungen einer Klasse bilden nun eine *Heilkette*, und die ganze Kette wird durchsucht: Priester `Geringes Heilen · Heilen · Große Heilung`, Paladin `Blitz des Lichts · Heiliges Licht`, Schamane `Geringe Welle der Heilung · Welle der Heilung`, Druide `Heilende Berührung`. Gewonnen hat der Kandidat mit der kleinsten erwarteten Heilung, die den Bedarf noch deckt.
- **Der Button wird nie staerker.** Nie gewählt wird etwas, das mehr heilt als der belegte Rang; bei Gleichstand bleibt es beim belegten Zauber. Die Zauberzeit kann sich dabei ändern: Bei kleinem Fehlbetrag wird aus einem Klick auf Große Heilung ein Geringes Heilen, aus Heiligem Licht ein Blitz des Lichts.
- **Was draussen bleibt.** Gruppenheilungen (Gebet der Heilung, Kettenheilung), schnelle Notfallzauber (Blitzheilung), HoTs (Erneuerung, Verjüngung, Nachwachsen), Schilde und Zauber mit Abklingzeit (Heiliger Schock, Handauflegung) werden nie getauscht. Die Ketten stehen in `FBHealChains` und lassen sich frei bearbeiten; die deutschen Zaubernamen sind enthalten, Sonderzeichen als Bytefolge für Latin-1- und UTF-8-Clients.
- **Neuer Befehl `/fbp smartcross`.** Schaltet den Kettenwechsel aus und wieder ein, gespeichert je Charakter in `HealBox.SmartCross` (Standard an). Aus bleibt Smart Healing beim belegten Zauber und senkt nur dessen Rang. `/fbp` zeigt den Zustand neben der Smart-Healing-Zeile, und der Befehl weist darauf hin, wenn Smart Healing selbst noch aus ist.
- **Klassensperre fuer Krieger, Schurke und Jaeger.** Das Addon ist fürs Heilen und Buffen gebaut, diese drei Klassen haben nichts davon: keine Heilzauber, keine Heilvorhersage, und ein Mana-Ticker, der entweder kein Mana zu beobachten hat (Wut, Energie) oder niemanden zu heilen (Jäger). Wer sich damit einloggt, bekommt die Anzeige nun gar nicht mehr zu sehen: keine Plaketten, kein Raid-Raster, kein Minimap-Button, kein Optionsfenster. Sämtliche Event-Handler und `OnUpdate`-Schleifen des Kerns und der Module Raid, Ticker und Smart Damage steigen sofort aus (`FBAddonSuppressed`), das Addon kostet im Ruhezustand also nichts. Zwei Zeilen im Chat erklären, was passiert ist und wie man es ändert.
- **Neuer Befehl `/fbp forceload`.** Zeigt das Addon bei einer gesperrten Klasse trotzdem an. Es startet sofort, ohne `/reload`, und die Einstellung bleibt je Charakter in `HealBox.ForceLoad` gespeichert. Derselbe Befehl schaltet wieder aus. Bei jeder anderen Klasse meldet der Befehl, dass er nicht nötig ist. Solange das Addon ruht, antworten alle übrigen `/fbp`-Befehle mit demselben Hinweis, statt über eine Anzeige zu berichten, die nie aufgebaut wurde.
- **Klassenerkennung.** Über das englische Klassen-Token des Clients (`WARRIOR`, `ROGUE`, `HUNTER`), mit Rückfall auf den angezeigten Klassennamen in fünf Sprachen und als letzter Notnagel über den Energietyp (Wut und Energie sind nie Manaklassen).
- **Neuer Hook `Suppress`.** Module blenden ihre eigenen Rahmen aus, wenn der Kern schlafen geht: Das Raidmodul versteckt das Raster, der Ticker seine Funken.
- **Startmeldung verschoben.** Begrüßung und die `Loaded`-Hooks der Module laufen jetzt bei `ADDON_LOADED` / `VARIABLES_LOADED` statt in `FBHealBox_OnLoad`. So gilt die gespeicherte Sprache, und bei einer schlafenden Klasse meldet sich niemand.
- **Rangregel verallgemeinert.** Die Obergrenze heißt nicht mehr "nie über dem belegten Rang", sondern "nie mehr erwartete Heilung als der belegte Rang". Innerhalb eines Zaubers ist das dasselbe, über Zaubergrenzen hinweg ist es die richtige Regel. Ein Rang, dessen Tooltip sich nicht lesen lässt, wird jetzt übersprungen, statt die Suche zu beenden.
**Behoben**
- **Smart Healing rangt keine HoTs mehr ab.** Erneuerung, Verjüngung, Nachwachsen, Gelassenheit, Lebensblüte, Wildwuchs, Springflut und Erdschild gehen nun immer im belegten Rang raus, bei jeder Klasse. Ein HoT verteilt seine Heilung über viele Sekunden, das im Moment des Klicks fehlende Leben sagt also nichts darüber aus, welcher Rang passt, und ein abgerangter HoT tickt die volle Laufzeit zu schwach. Gemischte Zauber wie Nachwachsen zählen ebenfalls als HoT.
- **HoT Erkennung** Die neue Funktion `FBHealBox_IsHoT()` prüft die Namensliste `FBHoTSpells` (englische und deutsche Namen, Umlaute als Bytefolge für Latin-1- und UTF-8-Clients), die Tooltip-Auswertung (`info.hot`), die Zauberwache (`hasHoT`) und die gerade aus dem Combatlog laufenden HoTs. Die Ausnahme greift damit auch bei Zaubern, die nicht in der Liste stehen, etwa auf abweichenden Servern. Zusätzlich weist die Rangprüfung jeden Rang mit HoT-Anteil ab.
## 1.4.4.2 (2026-09-07)
Buff duration display update & bugfix. Replaces the 4-quadrant clock division for buff icons with a 32-step vertical duration wipe, offering much finer timer resolution, and fixes an <eof> syntax error when loading.
### English
**New & Changed**
- **32-step buff icon duration display.** The buff icons left of the health bar previously divided the remaining duration into 4 coarse quadrants (quarters). They now transition in 32 fine vertical steps (`FBBUFFICON_STEPS = 32`) from top to bottom. As a buff expires, the top portion is progressively desaturated and darkened in 32 increments. For a 30-minute buff like *Power Word: Fortitude* or *Divine Spirit*, the visual display now updates approximately every 56 seconds rather than once every 7.5 minutes.
- **Optimised texture usage.** Replaced the 4 quadrant textures and 4 wash textures per buff icon with a single dynamically cropped desaturated overlay (`ic.qTex`) and dark wash (`ic.wTex`), reducing texture objects by 75 % per buff icon while increasing visual granularity eightfold.
- **Shortened addon notes.** Better display in WoW-Launcher addon managers.
### Deutsch
**Neu & Geaendert**
- **32-Stufen-Ablaufanzeige fuer Buff-Icons.** Die Buff-Symbole links neben dem Lebensbalken teilten die Restlaufzeit bisher in 4 grobe Bloecke (Quadranten) auf. Dies wurde auf 32 feine Ablaufstufen (`FBBUFFICON_STEPS = 32`) von oben nach unten umgestellt. Bei ablaufender Restzeit wird das Icon in 32 Teilschritten von oben nach unten schrittweise entsaettigt (schwarz-weiss) und abgedunkelt. Bei einem 30-Minuten-Buff (z. B. *Machtwort: Seelenstaerke*) aktualisiert sich die visuelle Anzeige nun ca. alle 56 Sekunden statt nur alle 7,5 Minuten.
- **Ressourcenschonende Textur-Struktur.** Statt 4 separaten Quadranten-Texturen und 4 Abdunklungs-Overlays nutzt jedes Icon nun nur noch je eine dynamisch skalierte und beschnittene Overlay-Textur (`ic.qTex` und `ic.wTex`). Das spart 75 % der Texturobjekte pro Icon ein und erhoeht gleichzeitig die Anzeigegenauigkeit um das Achtfache.
- **Addon Beschreibung eingekuerzt** Fuer bessere Übersicht in WoW-Launchern.
## 1.4.4.1 (2026-09-06)
Buff tracking & class spells update. Fixes buff clock icons (Divine Spirit, etc.) not appearing next to the health bar, adds group buff alternate matching, and completes the spell selection lists across all healer classes with buffs, utility and rez spells.
### English
**Fixed**
- **Buff tracking & Tooltip scanning.** In Vanilla 1.12.1, `ClearLines()` on the hidden scan tooltip (`FBHealBoxScanTip`) does not clear hidden text lines. Because `FBPredict_TooltipText` did not check `fs:IsShown()`, leftover lines from previously scanned heal spells (e.g. "Heals a friendly target...") lingered and were read when inspecting buff tooltips like *Divine Spirit*. This erroneously flagged `info.isHeal = true`, skipping buff duration detection (`info.buff`) and causing *Divine Spirit* not to display its clock icon and timer in the plate UI.
- **Group buff alternate detection.** `FBPredict_BuildWatch` and `FBPredict_ScanUnit` now track group buff variants (`altTex`). When a group buff is applied or present (*Prayer of Fortitude*, *Prayer of Spirit*, *Gift of the Wild*, *Greater Blessings*), the addon correctly recognises the aura, confirms pending casts, and displays the buff clock icon.
- **Buff watch textures.** `FBLoadSpellData` now flags all spells in `FBBuffWatchSpells` as addon spells (`isHealBoxSpell = true`), ensuring their textures and ranks are gathered and monitored even if they are not yet assigned to a heal button.
- **Drag & drop instant sync.** Dropping a spell onto a button now immediately calls `FBPredict_BuildWatch()`, updating tracked buffs and predictions without requiring a `/reload`.
**New & Changed**
- **Expanded class spell lists.** Greatly expanded `Spell.Name` for all classes so that all relevant buffs, utility spells, dispels, and resurrections appear in the cascading spell menus:
- **Priest:** Added *Power Word: Fortitude*, *Prayer of Fortitude*, *Divine Spirit*, *Prayer of Spirit*, *Shadow Protection*, *Prayer of Shadow Protection*, *Fear Ward*, *Power Infusion*, *Resurrection*.
- **Druid:** Added *Mark of the Wild*, *Gift of the Wild*, *Thorns*, *Tranquility*, *Innervate*, *Rebirth*, *Cure Poison*.
- **Paladin:** Added all normal and Greater Blessings (*Wisdom, Might, Kings, Salvation, Light, Sanctuary*), *Blessing of Freedom*, *Blessing of Sacrifice*, *Redemption*, *Divine Intervention*.
- **Shaman:** Added *Ancestral Spirit*, *Purge*, *Water Walking*, *Water Breathing*, *Water Shield*.
- Added optional support for **Mage** (*Remove Lesser Curse*, *Arcane Intellect*, *Arcane Brilliance*, *Dampen Magic*, *Amplify Magic*) and **Warlock** (*Unending Breath*, *Detect Invisibility*).
- **Fear Ward in Buff Watch.** Added *Fear Ward* to `FBBuffWatchSpells` for Priests.
### Deutsch
**Behoben**
- **Buff-Erkennung & Tooltip-Scan.** Im 1.12.1-Client leert `ClearLines()` auf dem versteckten Scan-Tooltip (`FBHealBoxScanTip`) ausgeblendete Textzeilen nicht. Da `FBPredict_TooltipText` nicht pruefte, ob eine Zeile sichtbar ist (`fs:IsShown()`), wurden Textzeilen zuvor gescannter Heilzauber (z. B. „Heals a friendly target...") faelschlicherweise auch bei Buff-Tooltips wie *Goettlicher Willen* (*Divine Spirit*) mitgelesen. Dadurch wurde fehlerhaft `isHeal = true` gesetzt, was die Erkennung der Buff-Laufzeit (`info.buff`) verhinderte und dazu fuehrte, dass das Buff-Icon links am Lebensbalken nicht erschien.
- **Erkennung von Gruppen-Buffs.** `FBPredict_BuildWatch` und `FBPredict_ScanUnit` unterstuetzen nun Gruppenversionen (`altTex`). Gruppen-Buffs (*Gebet der Seelenstaerke*, *Gebet der Willenskraft*, *Gabe der Wildnis*, *Grosse Segen*) werden nun zuverlaessig bestaetigt und mit Laufzeit/Icon dargestellt.
- **Buff-Wache Texturen.** `FBLoadSpellData` markiert nun alle Zauber aus `FBBuffWatchSpells` automatisch als Addon-Zauber (`isHealBoxSpell = true`), sodass ihre Texturen und Daten auch dann geladen und getrackt werden, wenn sie noch auf keinem Button liegen.
- **Drag & Drop Sofort-Synchronisation.** Das Ablegen eines Zaubers auf einen Button ruft nun direkt `FBPredict_BuildWatch()` auf, sodass neue Buffs und Vorhersagen ohne `/reload` sofort aktiv sind.
**Neu & Geaendert**
- **Vollstaendige Klassen-Zauberlisten.** `Spell.Name` fuer alle Heilerklassen stark erweitert, sodass saemtliche Heiler-Buffs, Gruppen-Buffs, Hilfszauber und Wiederbelebungen in den Auswahlmenues verfuegbar sind:
- **Priester:** *Machtwort: Seelenstaerke*, *Gebet der Seelenstaerke*, *Goettlicher Willen*, *Gebet der Willenskraft*, *Schattenschutz*, *Gebet des Schattenschutzes*, *Furchtzauberschutz*, *Seele der Macht*, *Auferstehung* hinzugefuegt.
- **Druide:** *Mal der Wildnis*, *Gabe der Wildnis*, *Dornen*, *Gelassenheit*, *Anregen*, *Wiedergeburt*, *Vergiftung heilen* hinzugefuegt.
- **Paladin:** Alle normalen und Grossen Segen (*Weisheit, Macht, Koenige, Rettung, Licht, Schutz*), *Segen der Freiheit*, *Segen der Opferung*, *Erloesung*, *Goettliches Eingreifen* hinzugefuegt.
- **Schamane:** *Geist der Ahnen*, *Reinigen*, *Wasserwandeln*, *Wasseratmung*, *Wasserschild* hinzugefuegt.
- Zusaetzliche Unterstuetzung fuer **Magier** (*Geringen Fluch aufheben*, *Arkane Intelligenz*, *Arkane Brillanz*, *Magie daempfen*, *Magie verstaerken*) und **Hexenmeister** (*Unendlicher Atem*, *Unsichtbarkeit entdecken*) ergaenzt.
- **Furchtzauberschutz in Buff-Wache.** *Fear Ward* fuer Priester in `FBBuffWatchSpells` aufgenommen.
## 1.4.4 (2026-09-06)
Performance pass. No feature was added or changed; every visible behaviour is meant to be identical. A simulated five-second raid fight (40 players, aura and health events, mana changes, combat log, 60 frames per second) went from about 63,900 API and widget calls to about 14,450, a reduction of 77 %.
### English
**Changed (internals)**
- **Button states centralised.** The up to 260 heal buttons (10 plates x 10, 40 raid cells x 4) no longer carry their own `SPELL_UPDATE_USABLE` / `SPELL_UPDATE_COOLDOWN` handlers. One pass over the *visible* buttons queries usability and cooldown once per spell id and range once per button, and sets colours and cooldown sweeps only when the state changes. Several such events within one frame are merged into one pass.
- **Display caches.** Health text, bar maxima, bar values, bar colour and mana strip visibility are only written when they change. Dispellable debuffs are searched on `UNIT_AURA` only; `UNIT_HEALTH` and the prediction tick reuse the last result.
- **Aura scans shared.** A unit's buff textures are read once per frame and shared between the heal prediction and the buff watch. Buff names resolved through tooltip scans are remembered per texture, so a unit missing the watched buff no longer costs up to 32 tooltip scans per aura event. Raid units are only scanned when something depends on them (own cast pending, tracked HoT or shield, or a visible cell).
- **Cheaper ticks.** Spell timers use precomputed base spell names instead of string parsing per button per tick and skip units without tracked HoTs or shields. Buff icons reuse their work tables, iterate a presorted list instead of sorting per tick, and update on change or once a second instead of five times a second. The attacked-member check skips entirely while there is no hostile target and nothing is marked, and compares names before calling `UnitIsUnit`.
- **Event bursts coalesced.** `PARTY_MEMBERS_CHANGED`, `UNIT_PET` and `RAID_ROSTER_UPDATE` set a flag that is processed once in the next frame instead of rebuilding plates and grid for every event of a burst.
- **Mana ticker.** Enabled/mana/full checks moved from every frame to mana events and option changes; the spark is re-anchored only when it has moved at least half a pixel; sizes and visibility are cached.
- **Combat log.** Heal patterns run only for buff events, the absorb pattern only for hit events that contain the word absorbed. Smart Damage parses damage lines only while enabled and while there is a target being measured or an own cast waiting; its live target line is refreshed only while the options window is open.
- Raid test ghost animation frame is hidden outside the raid test instead of running an empty update every frame.
- **Client API compatibility.** `IsSpellInRange` and `IsUsableSpell` do not exist in the 1.12 client (they came with 2.0). All range and usability checks now go through wrappers that use them when a client offers them and otherwise fall back to 1.12 means: spell range from the tooltip against `UnitXP("distanceBetween")` where available, else `CheckInteractDistance` (28 m, beyond that undecided rather than red); mana cost from the tooltip against your current mana. Because clients differ (2.0 style `(id, "spell", unit)` versus the Turtle client's `(name, unit)`), the addon probes both signatures once at login with `pcall` and uses the one the client understands; a function that keeps throwing is switched off in favour of the fallback. `/fbp` reports the detected form. Without this the new central button pass crashed at login.
**Fixed**
- The old per-button event handler declared `this`, `event` and `arg1` as parameters, which shadowed the 1.12 globals; range colouring and cooldown sweeps on the buttons never worked in the game. They do now.
### Deutsch
**Geaendert (intern)**
- **Button-Zustaende zentral.** Die bis zu 260 Heil-Buttons (10 Plaketten x 10, 40 Raid-Zellen x 4) tragen keine eigenen `SPELL_UPDATE_USABLE`/`SPELL_UPDATE_COOLDOWN`-Handler mehr. Ein Durchgang ueber die *sichtbaren* Buttons fragt Nutzbarkeit und Cooldown je Zauber-ID einmal und die Reichweite je Button ab und setzt Farben und Cooldown-Uhren nur bei Zustandswechsel. Mehrere solche Events in einem Frame werden zu einem Durchgang zusammengefasst.
- **Anzeige-Zwischenspeicher.** Lebenstext, Balkenmaxima, Balkenwerte, Balkenfarbe und Sichtbarkeit des Manastreifens werden nur geschrieben, wenn sie sich aendern. Entfernbare Debuffs werden nur bei `UNIT_AURA` gesucht; `UNIT_HEALTH` und der Vorhersage-Tick nutzen den letzten Befund.
- **Aura-Scans gemeinsam.** Die Buff-Texturen einer Einheit werden je Frame einmal gelesen und von Heilvorhersage und Buff-Wache geteilt. Per Tooltip ermittelte Buffnamen werden je Textur gemerkt, sodass eine Einheit ohne den ueberwachten Buff nicht mehr bis zu 32 Tooltip-Scans je Aura-Event kostet. Raid-Einheiten werden nur gescannt, wenn etwas davon abhaengt (eigener Cast wartet, HoT oder Schild verfolgt, sichtbare Zelle).
- **Guenstigere Ticks.** Zauber-Timer nutzen vorberechnete Basisnamen statt String-Parsing je Button und Tick und ueberspringen Einheiten ohne verfolgten HoT oder Schild. Buff-Icons verwenden ihre Arbeitstabellen wieder, laufen ueber eine vorsortierte Liste statt je Tick zu sortieren und aktualisieren bei Aenderung oder einmal je Sekunde statt fuenfmal. Der Angegriffenen-Abgleich entfaellt ganz, solange kein feindliches Ziel besteht und nichts markiert ist, und vergleicht Namen, bevor `UnitIsUnit` gerufen wird.
- **Event-Salven zusammengefasst.** `PARTY_MEMBERS_CHANGED`, `UNIT_PET` und `RAID_ROSTER_UPDATE` setzen ein Kennzeichen, das im naechsten Frame einmal abgearbeitet wird, statt Plaketten und Raster bei jedem Event einer Salve neu aufzubauen.
- **Mana-Ticker.** Die Pruefungen an/Mana/voll laufen bei Mana-Events und Optionswechseln statt jeden Frame; der Funke wird nur neu verankert, wenn er sich mindestens einen halben Pixel bewegt hat; Groessen und Sichtbarkeit sind zwischengespeichert.
- **Combatlog.** Heilmuster laufen nur bei Buff-Events, das Absorb-Muster nur bei Treffer-Events, die das Wort absorbed enthalten. Smart Damage liest Schadenszeilen nur, solange es an ist und ein Ziel vermessen wird oder ein eigener Cast wartet; die Live-Zielzeile wird nur bei offenem Optionsfenster aktualisiert.
- Der Animationsframe der Raid-Testgeister ist ausserhalb des Raid-Tests ausgeblendet, statt jeden Frame leer zu laufen.
- **Client-API-Kompatibilitaet.** `IsSpellInRange` und `IsUsableSpell` gibt es im 1.12-Client nicht (sie kamen mit 2.0). Alle Reichweiten- und Nutzbarkeitspruefungen laufen jetzt ueber Wrapper, die sie nutzen, wenn ein Client sie anbietet, und sonst auf 1.12-Mittel zurueckfallen: Zauberreichweite aus dem Tooltip gegen `UnitXP("distanceBetween")`, wo vorhanden, sonst `CheckInteractDistance` (28 m, jenseits davon unentschieden statt rot); Manapreis aus dem Tooltip gegen das eigene Mana. Weil Clients sich unterscheiden (2.0-Form `(id, "spell", unit)` gegenueber der Turtle-Form `(name, unit)`), probiert das Addon beide Signaturen beim Login einmal per `pcall` aus und nutzt die verstandene; eine Funktion, die weiter Fehler wirft, wird zugunsten des Rueckfalls abgeschaltet. `/fbp` meldet die erkannte Form. Ohne das brach der neue zentrale Button-Durchgang beim Login ab.
**Behoben**
- Der alte Button-Handler deklarierte `this`, `event` und `arg1` als Parameter und verdeckte damit die 1.12-Globals; Reichweitenfaerbung und Cooldown-Uhr auf den Buttons haben im Spiel nie gearbeitet. Jetzt tun sie es.
## 1.4.3 (2026-09-05)
### English ### English
@@ -18,7 +176,7 @@
**Fixed** **Fixed**
- Own buffs were only read at login and on group changes: Vanilla fires `PLAYER_AURAS_CHANGED` for the player instead of `UNIT_AURA`. The addon now listens to it and re-reads your remaining time on every change, so recasting a buff on yourself resets its clock and cancelling it removes the icon. Also fixes confirmation of your own HoTs and shields on yourself. - Own buffs were only read at login and on group changes: Vanilla fires `PLAYER_AURAS_CHANGED` for the player instead of `UNIT_AURA`. The addon now listens to it and re-reads your remaining time on every change, and once a second in between (a refresh of a running buff fires no aura event at all), so recasting a buff on yourself resets its clock and cancelling it removes the icon. Refreshing a buff on someone else through a HealBox button is confirmed by the completed cast. Also fixes confirmation of your own HoTs and shields on yourself.
- Raid cell names and percentages were hidden behind the health bar since the bar levels became relative; the texts now live on their own layer above the bars. - Raid cell names and percentages were hidden behind the health bar since the bar levels became relative; the texts now live on their own layer above the bars.
- Buff icons stack two high left of the plate; elapsed clock quadrants are drawn dark on a layer above the icon so the split shows on every client. Raid cells use a 3 by 4 grid of 6 px icons (twelve slots); the new raid option *Show buffs* removes icons and strip together. `/fbp buffs` reports the grey count per icon. - Buff icons stack two high left of the plate; elapsed clock quadrants are drawn dark on a layer above the icon so the split shows on every client. Raid cells use a 3 by 4 grid of 6 px icons (twelve slots); the new raid option *Show buffs* removes icons and strip together. `/fbp buffs` reports the grey count per icon.
- Test modes extended: party ghost Brynn is dead, pet Bramble carries a disease, Dorn carries six and raid ghost 12 twelve buffs, raid ghost 27 a disease. Up to six buff icons per plate. - Test modes extended: party ghost Brynn is dead, pet Bramble carries a disease, Dorn carries six and raid ghost 12 twelve buffs, raid ghost 27 a disease. Up to six buff icons per plate.
@@ -48,7 +206,7 @@
**Behoben** **Behoben**
- Eigene Buffs wurden nur beim Login und bei Gruppenwechseln gelesen: Vanilla feuert fuer den Spieler `PLAYER_AURAS_CHANGED` statt `UNIT_AURA`. Das Addon hoert jetzt darauf und liest die eigene Restzeit bei jeder Aenderung neu, sodass ein Neucast auf dich selbst die Uhr zuruecksetzt und ein Wegklicken das Icon entfernt. Behebt auch die Bestaetigung eigener HoTs und Schilde auf dir selbst. - Eigene Buffs wurden nur beim Login und bei Gruppenwechseln gelesen: Vanilla feuert fuer den Spieler `PLAYER_AURAS_CHANGED` statt `UNIT_AURA`. Das Addon hoert jetzt darauf und liest die eigene Restzeit bei jeder Aenderung neu, und dazwischen einmal je Sekunde (ein Refresh eines laufenden Buffs feuert gar kein Aura-Event), sodass ein Neucast auf dich selbst die Uhr zuruecksetzt und ein Wegklicken das Icon entfernt. Ein Refresh auf jemand anderen ueber einen HealBox-Button gilt mit dem abgeschlossenen Cast als bestaetigt. Behebt auch die Bestaetigung eigener HoTs und Schilde auf dir selbst.
- Namen und Prozente der Raid-Zellen lagen seit den relativen Balken-Ebenen hinter dem Lebensbalken; die Texte liegen jetzt auf einer eigenen Ebene ueber den Balken. - Namen und Prozente der Raid-Zellen lagen seit den relativen Balken-Ebenen hinter dem Lebensbalken; die Texte liegen jetzt auf einer eigenen Ebene ueber den Balken.
- Buff-Icons stapeln sich zu zweit links neben der Plakette; abgelaufene Uhr-Quadranten werden dunkel auf einer Ebene ueber dem Icon gezeichnet, damit die Teilung auf jedem Client sichtbar ist. Raid-Zellen nutzen ein 3-mal-4-Raster aus 6-px-Icons (zwoelf Plaetze); die neue Raid-Option *Buffs anzeigen* entfernt Icons und Streifen gemeinsam. `/fbp buffs` nennt je Icon die Zahl grauer Quadranten. - Buff-Icons stapeln sich zu zweit links neben der Plakette; abgelaufene Uhr-Quadranten werden dunkel auf einer Ebene ueber dem Icon gezeichnet, damit die Teilung auf jedem Client sichtbar ist. Raid-Zellen nutzen ein 3-mal-4-Raster aus 6-px-Icons (zwoelf Plaetze); die neue Raid-Option *Buffs anzeigen* entfernt Icons und Streifen gemeinsam. `/fbp buffs` nennt je Icon die Zahl grauer Quadranten.
- Testmodi erweitert: Partygeist Brynn ist tot, Pet Bramble traegt eine Krankheit, Dorn traegt sechs und Raid-Geist 12 zwoelf Buffs, Raid-Geist 27 eine Krankheit. Bis zu sechs Buff-Icons je Plakette. - Testmodi erweitert: Partygeist Brynn ist tot, Pet Bramble traegt eine Krankheit, Dorn traegt sechs und Raid-Geist 12 zwoelf Buffs, Raid-Geist 27 eine Krankheit. Bis zu sechs Buff-Icons je Plakette.
+1223 -283
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,9 +1,9 @@
## Interface: 11200 ## Interface: 11200
## Title: Heal Box Vanilla |cFFFFFF00v1.4.3|r ## Title: Heal Box Vanilla |cFFFFFF00v1.4.4.3|r
## Notes: Party, pet and self heal display with quick-cast buttons for healers. Ported to client 1.12.1 (optional SuperWoW). Up to 10 buttons per row, mana bar inside the health bar, class colours, buff watch, range fading, adjustable spacing, test mode with ghost players, optional raid mode for 20 and 40 players, mana ticker with five-second rule, smart damage ranking for attack spells. Heal prediction for direct heals, HoT ticks and absorb shields, self-correcting from the combat log. HealComm sync with Puppeteer, pfUI and others. German/English. Original by Dourd, ported to Vanilla and extended 09/2026 by Mquadrat. ## Notes: Heal/raid frame addon like Healium, with quick-cast buttons, heal prediction, mana ticker and more.
## Notes-deDE: Party-, Begleiter- und Selbst-Heilanzeige mit Schnellzugriff-Buttons fuer Heiler. Portiert fuer Client 1.12.1 (optional SuperWoW). Bis zu 10 Buttons pro Reihe, Manabalken im Lebensbalken, Klassenfarben, Buff-Wache, Reichweiten-Fading, einstellbare Abstaende, Testmodus mit Geisterspielern, optionaler Raidmodus fuer 20 und 40 Spieler, Mana-Ticker mit Fuenf-Sekunden-Regel, Smart Damage fuer Angriffszauber. Heal-Prediction fuer Direktheilung, HoT-Ticks und Absorb-Schilde, selbstkorrigierend ueber den Combatlog. HealComm-Sync mit Puppeteer, pfUI und Co. Deutsch/Englisch. Original von Dourd, Vanilla-Portierung und Erweiterung 09/2026 von Mquadrat. ## Notes-deDE: Heal/Raidframe Addon aehnlich Healium, mit Schnellzugriff Buttons, Heal Prediction und Mana Ticker und mehr.
## Author: Dourd (Argent Dawn EU) | Vanilla-Port & Erweiterung 09/2026: Mquadrat ## Author: Dourd (Argent Dawn EU) | Vanilla-Port & Erweiterung 09/2026: Mquadrat
## Version: 1.4.3 ## Version: 1.4.4.3
## SavedVariablesPerCharacter: HealBox ## SavedVariablesPerCharacter: HealBox
## OptionalDeps: SuperWoW ## OptionalDeps: SuperWoW
+18 -1
View File
@@ -338,6 +338,10 @@ function FBDmg_ParseLine(msg)
end end
function FBDmg_OnCombatLog(msg) function FBDmg_OnCombatLog(msg)
-- Nur arbeiten, wenn Smart Damage an ist und es etwas zu lernen gibt
-- (Prozentziel wird vermessen oder ein eigener Cast wartet auf Treffer).
if (FBDmg_Cfg().Enabled ~= 1) then return; end
if (not FBDmgTargetName) and (not FBDmgLastCast) then return; end
local victim, amount, spell, who = FBDmg_ParseLine(msg); local victim, amount, spell, who = FBDmg_ParseLine(msg);
if (not victim) then return; end if (not victim) then return; end
FBDmg_NoteDamage(victim, amount); FBDmg_NoteDamage(victim, amount);
@@ -421,6 +425,9 @@ end
-- Vor-Hook auf UseAction. true, wenn das Modul selbst gecastet hat. -- Vor-Hook auf UseAction. true, wenn das Modul selbst gecastet hat.
function FBDmg_TryDownrank(slot, checkCursor, onSelf) function FBDmg_TryDownrank(slot, checkCursor, onSelf)
-- Klassensperre des Kerns (Krieger/Schurke/Jaeger ohne /fbp forceload):
-- die Aktionsleiste bleibt unberuehrt
if (FBAddonSuppressed) then return false; end
if (FBDmg_Cfg().Enabled ~= 1) or onSelf then return false; end if (FBDmg_Cfg().Enabled ~= 1) or onSelf then return false; end
if ((CursorHasSpell and CursorHasSpell()) or (CursorHasItem and CursorHasItem())) then return false; end if ((CursorHasSpell and CursorHasSpell()) or (CursorHasItem and CursorHasItem())) then return false; end
local name, rank = FBDmg_SlotSpell(slot); local name, rank = FBDmg_SlotSpell(slot);
@@ -460,6 +467,7 @@ for _, ev in ipairs({
}) do FBDmgFrame:RegisterEvent(ev); end }) do FBDmgFrame:RegisterEvent(ev); end
FBDmgFrame:SetScript("OnEvent", function() FBDmgFrame:SetScript("OnEvent", function()
if (FBAddonSuppressed) then return; end
if (event == "PLAYER_ENTERING_WORLD" or event == "SPELLS_CHANGED") then if (event == "PLAYER_ENTERING_WORLD" or event == "SPELLS_CHANGED") then
FBDmg_ScanSpells(); FBDmg_ScanSpells();
FBDmg_UpdateSpellText(); FBDmg_UpdateSpellText();
@@ -494,7 +502,10 @@ function FBDmg_SourceText()
end end
function FBDmg_UpdateSourceText() function FBDmg_UpdateSourceText()
if (FBDmgSourceText) then FBDmgSourceText:SetText(format(FBT("DMG_SRC"), FBDmg_SourceText())); end -- nur, wenn das Optionsfenster offen ist (sonst je UNIT_HEALTH des Ziels umsonst)
if (FBDmgSourceText and panel and panel:IsVisible()) then
FBDmgSourceText:SetText(format(FBT("DMG_SRC"), FBDmg_SourceText()));
end
end end
function FBDmg_UpdateSpellText() function FBDmg_UpdateSpellText()
@@ -562,6 +573,12 @@ function FBDmg_BuildOptions()
FBDmg_UpdateSourceText(); FBDmg_UpdateSourceText();
FBDmg_UpdateSpellText(); FBDmg_UpdateSpellText();
FBDmg_SyncOptions(); FBDmg_SyncOptions();
-- beim Oeffnen des Fensters die Zielzeile auffrischen (Vor-Hook auf OnShow)
local prevShow = panel:GetScript("OnShow");
panel:SetScript("OnShow", function()
if (prevShow) then prevShow(); end
FBDmg_UpdateSourceText();
end);
end end
function FBDmg_SyncOptions() function FBDmg_SyncOptions()
+123 -45
View File
@@ -725,6 +725,7 @@ end
-- ========================================================================== -- ==========================================================================
function FBRaid_UpdateRoster() function FBRaid_UpdateRoster()
FBRaidRosterDirty = false;
local cfg = FBRaid_Cfg(); local cfg = FBRaid_Cfg();
FBRaidUnitCell = {}; FBRaidUnitCell = {};
FBRaidMembers = 0; FBRaidMembers = 0;
@@ -737,6 +738,8 @@ function FBRaid_UpdateRoster()
if (FBRaidCells[g] and FBRaidCells[g][pos]) then if (FBRaidCells[g] and FBRaidCells[g][pos]) then
local c = FBRaidCells[g][pos]; local c = FBRaidCells[g][pos];
c.unit = nil; c.name = nil; c.ghost = nil; c.index = nil; c.unit = nil; c.name = nil; c.ghost = nil; c.index = nil;
c.dispelKnown = nil; c.lastText = nil; c.lastMax = nil; c.colorKey = nil; c.manaShown = nil; c.lastMpMax = nil;
c.vHp = nil; c.vShield = nil; c.vInc = nil; c.vMp = nil;
c:Hide(); c:Hide();
end end
end end
@@ -897,7 +900,35 @@ end
-- [ Zelleninhalt ] -- [ Zelleninhalt ]
-- ========================================================================== -- ==========================================================================
function FBRaid_UpdateCell(c) -- Zellenfarbe / Maxima / Text nur bei Aenderung setzen
function FBRaid_SetColor(c, key, r, g, b, a)
if (c.colorKey == key) then return; end
c.colorKey = key;
c.HealthBar:SetStatusBarColor(r, g, b, a);
end
function FBRaid_SetValues(c, hp, shieldTop, incTop)
if (c.vHp ~= hp) then c.vHp = hp; c.HealthBar:SetValue(hp); end
if (c.vShield ~= shieldTop) then c.vShield = shieldTop; c.ShieldBar:SetValue(shieldTop); end
if (c.vInc ~= incTop) then c.vInc = incTop; c.IncHealBar:SetValue(incTop); end
end
function FBRaid_SetMax(c, hpMax)
if (c.lastMax == hpMax) then return; end
c.lastMax = hpMax;
c.HealthBar:SetMinMaxValues(0, hpMax);
c.ShieldBar:SetMinMaxValues(0, hpMax);
c.IncHealBar:SetMinMaxValues(0, hpMax);
end
function FBRaid_SetText(c, text)
if (c.lastText == text) then return; end
c.lastText = text;
if (text == false) then c.HPText:Hide(); else c.HPText:SetText(text); c.HPText:Show(); end
end
-- auraChanged: true bei UNIT_AURA, nur dann wird der Debuff neu gesucht
function FBRaid_UpdateCell(c, auraChanged)
if (not c) or (not c.unit) or (not c:IsShown()) then return; end if (not c) or (not c.unit) or (not c:IsShown()) then return; end
local cfg = FBRaid_Cfg(); local cfg = FBRaid_Cfg();
local hp, hpMax = FBRaid_Health(c); local hp, hpMax = FBRaid_Health(c);
@@ -908,60 +939,59 @@ function FBRaid_UpdateCell(c)
local key = "STATE_DEAD"; local key = "STATE_DEAD";
if (state == "ghost") then key = "STATE_GHOST"; end if (state == "ghost") then key = "STATE_GHOST"; end
if (state == "offline") then key = "STATE_OFFLINE"; end if (state == "offline") then key = "STATE_OFFLINE"; end
c.HPText:SetText(FBT(key)); FBRaid_SetText(c, FBT(key));
c.HPText:Show(); FBRaid_SetMax(c, hpMax);
c.HealthBar:SetMinMaxValues(0, hpMax); c.HealthBar:SetValue(0); FBRaid_SetValues(c, 0, 0, 0);
c.HealthBar:SetStatusBarColor(0.5, 0.5, 0.5, 1); FBRaid_SetColor(c, "state", 0.5, 0.5, 0.5, 1);
c.ShieldBar:SetMinMaxValues(0, hpMax); c.ShieldBar:SetValue(0); if (c.manaShown ~= false) then c.manaShown = false; c.ManaBar:Hide(); end
c.IncHealBar:SetMinMaxValues(0, hpMax); c.IncHealBar:SetValue(0);
c.ManaBar:Hide();
return; return;
end end
-- Leben und Schichten -- Leben und Schichten
c.HealthBar:SetMinMaxValues(0, hpMax); FBRaid_SetMax(c, hpMax);
c.HealthBar:SetValue(hp);
local inc, shield = FBRaid_Incoming(c); local inc, shield = FBRaid_Incoming(c);
local shieldTop = math.min(hpMax, hp + shield); local shieldTop = hp + shield;
c.ShieldBar:SetMinMaxValues(0, hpMax); if (shieldTop > hpMax) then shieldTop = hpMax; end
c.ShieldBar:SetValue(shieldTop); local incTop = shieldTop + inc;
c.IncHealBar:SetMinMaxValues(0, hpMax); if (incTop > hpMax) then incTop = hpMax; end
c.IncHealBar:SetValue(math.min(hpMax, shieldTop + inc)); FBRaid_SetValues(c, hp, shieldTop, incTop);
-- HP-Text -- HP-Text
if (cfg.HPText == "percent") then if (cfg.HPText == "percent") then
c.HPText:SetText(format("%d%%", math.floor(hp / hpMax * 100))); FBRaid_SetText(c, math.floor(hp / hpMax * 100).."%");
c.HPText:Show();
elseif (cfg.HPText == "deficit") then elseif (cfg.HPText == "deficit") then
local def = hpMax - hp; local def = hpMax - hp;
if (def > 0) then c.HPText:SetText("-"..def); else c.HPText:SetText(""); end if (def > 0) then FBRaid_SetText(c, "-"..def); else FBRaid_SetText(c, ""); end
c.HPText:Show();
else else
c.HPText:Hide(); FBRaid_SetText(c, false);
end end
-- Mana -- Mana
local mp, mpMax, hasMana = FBRaid_Mana(c); local mp, mpMax, hasMana = FBRaid_Mana(c);
if (cfg.ManaBar == 1 and hasMana and mpMax > 0) then if (cfg.ManaBar == 1 and hasMana and mpMax > 0) then
c.ManaBar:SetMinMaxValues(0, mpMax); if (c.lastMpMax ~= mpMax) then c.lastMpMax = mpMax; c.ManaBar:SetMinMaxValues(0, mpMax); end
c.ManaBar:SetValue(mp); if (c.vMp ~= mp) then c.vMp = mp; c.ManaBar:SetValue(mp); end
c.ManaBar:Show(); if (c.manaShown ~= true) then c.manaShown = true; c.ManaBar:Show(); end
else elseif (c.manaShown ~= false) then
c.ManaBar:Hide(); c.manaShown = false; c.ManaBar:Hide();
end end
-- Farbe: Dispel schlaegt HP-Stand -- Farbe: Dispel schlaegt HP-Stand (Debuff nur bei Aura-Aenderung neu suchen)
if (auraChanged or (not c.dispelKnown) or c.ghost) then
c.dispelType = FBRaid_Dispel(c);
c.dispelKnown = true;
end
local frac = hp / hpMax; local frac = hp / hpMax;
local dtype = FBRaid_Dispel(c); local dtype = c.dispelType;
if (dtype and FBDispelColors[dtype]) then if (dtype and FBDispelColors[dtype]) then
local col = FBDispelColors[dtype]; local col = FBDispelColors[dtype];
c.HealthBar:SetStatusBarColor(col[1], col[2], col[3], col[4]); FBRaid_SetColor(c, dtype, col[1], col[2], col[3], col[4]);
elseif (frac > LowHP) then elseif (frac > LowHP) then
c.HealthBar:SetStatusBarColor(0, 1, 0, 1); FBRaid_SetColor(c, "green", 0, 1, 0, 1);
elseif (frac > VeryLowHP) then elseif (frac > VeryLowHP) then
c.HealthBar:SetStatusBarColor(1, 0.9, 0, 1); FBRaid_SetColor(c, "yellow", 1, 0.9, 0, 1);
else else
c.HealthBar:SetStatusBarColor(1, 0, 0, 1); FBRaid_SetColor(c, "red", 1, 0, 0, 1);
end end
end end
@@ -987,7 +1017,8 @@ end
-- Wer wird angegriffen (Hook "Aggro" des Kerns, tt = "targettarget" oder nil) -- Wer wird angegriffen (Hook "Aggro" des Kerns, tt = "targettarget" oder nil)
function FBRaid_CheckAggro(tt) function FBRaid_CheckAggro(tt)
if (not FBRaid_IsActive()) then return; end if (not FBRaid_IsActive()) then return false; end
local any = false;
for g = 1, FBRAID_GROUPS do for g = 1, FBRAID_GROUPS do
if (FBRaidCells[g]) then if (FBRaidCells[g]) then
for pos = 1, FBRAID_PER_GROUP do for pos = 1, FBRAID_PER_GROUP do
@@ -996,16 +1027,18 @@ function FBRaid_CheckAggro(tt)
local flag = false; local flag = false;
if (HealBox.AggroMark == 1 and c:IsShown()) then if (HealBox.AggroMark == 1 and c:IsShown()) then
if (c.ghost) then flag = (c.ghost.aggro == true); if (c.ghost) then flag = (c.ghost.aggro == true);
elseif (tt) then flag = FBHealBox_UnitIsAggro(c.unit, tt); end elseif (tt) then flag = FBHealBox_UnitIsAggroFast(c.unit, c.name, tt); end
end end
if (c.underAttack ~= flag) then if (c.underAttack ~= flag) then
c.underAttack = flag; c.underAttack = flag;
FBRaid_ApplyBorder(c); FBRaid_ApplyBorder(c);
end end
if (flag) then any = true; end
end end
end end
end end
end end
return any;
end end
-- Platz links neben jeder Zelle fuer die Buff-Icons (aussen links) -- Platz links neben jeder Zelle fuer die Buff-Icons (aussen links)
@@ -1042,13 +1075,17 @@ function FBRaid_UpdateSpellTimers(now)
for pos = 1, FBRAID_PER_GROUP do for pos = 1, FBRAID_PER_GROUP do
local c = FBRaidCells[g][pos]; local c = FBRaidCells[g][pos];
if (c) then if (c) then
local shown = on and c.unit and c:IsShown(); -- ohne verfolgten HoT/Schild auf der Einheit nur einmal ausblenden
local shown = on and c.unit and c:IsShown() and (c.ghost or FBHoTs[c.name] or FBShields[c.name]);
if (not shown and not c.timersShown) then shown = nil; end
c.timersShown = shown and true or false;
if (shown == nil) then shown = false; end
for i = 1, FBRAID_MAX_BUTTONS do for i = 1, FBRAID_MAX_BUTTONS do
local b = c.buttons[i]; local b = c.buttons[i];
if (shown and b:IsShown()) then if (shown and b:IsShown()) then
local text, color = FBHealBox_SpellTimerFor(b.spellName, c.name, now, c.ghost, i); local text, color = FBHealBox_SpellTimerFor(b.spellBase, c.name, now, c.ghost, i);
if (not text and b.spellNameR) then if (not text and b.spellBaseR) then
text, color = FBHealBox_SpellTimerFor(b.spellNameR, c.name, now, nil, i); text, color = FBHealBox_SpellTimerFor(b.spellBaseR, c.name, now, nil, i);
end end
FBHealBox_SetButtonTimer(b, text, color); FBHealBox_SetButtonTimer(b, text, color);
else else
@@ -1067,7 +1104,27 @@ function FBRaid_UpdateCooldowns()
for pos = 1, FBRAID_PER_GROUP do for pos = 1, FBRAID_PER_GROUP do
local c = FBRaidCells[g][pos]; local c = FBRaidCells[g][pos];
if (c) then if (c) then
for i = 1, FBRAID_MAX_BUTTONS do FBHealBox_UpdateButtonCooldown(c.buttons[i]); end for i = 1, FBRAID_MAX_BUTTONS do
local b = c.buttons[i];
b.cdKey = nil;
FBHealBox_UpdateButtonState(b, "SPELL_UPDATE_COOLDOWN");
end
end
end
end
end
end
-- Button-Zustaende der sichtbaren Zellen (Hook "ButtonStates")
function FBRaid_UpdateButtonStates(event)
if (not FBRaid_IsActive()) then return; end
local nBtn = FBRaid_Cfg().Buttons or 0;
for g = 1, FBRAID_GROUPS do
if (FBRaidCells[g]) then
for pos = 1, FBRAID_PER_GROUP do
local c = FBRaidCells[g][pos];
if (c and c.unit and c:IsShown()) then
for i = 1, nBtn do FBHealBox_UpdateButtonState(c.buttons[i], event); end
end end
end end
end end
@@ -1130,7 +1187,10 @@ function FBRaid_SyncButtons()
b.subIcon:Hide(); b.subIcon:Hide();
end end
end end
FBHealBox_UpdateButtonCooldown(b); b.cdKey = nil;
b.colorState = nil;
if (b.spellName) then b.spellBase = FBPredict_SplitCast(b.spellName); else b.spellBase = nil; end
if (b.spellNameR) then b.spellBaseR = FBPredict_SplitCast(b.spellNameR); else b.spellBaseR = nil; end
end end
end end
end end
@@ -1179,21 +1239,30 @@ FBRaidEventFrame:RegisterEvent("UNIT_MAXMANA");
FBRaidEventFrame:RegisterEvent("UNIT_DISPLAYPOWER"); FBRaidEventFrame:RegisterEvent("UNIT_DISPLAYPOWER");
FBRaidEventFrame:RegisterEvent("UNIT_AURA"); FBRaidEventFrame:RegisterEvent("UNIT_AURA");
FBRaidRosterDirty = false;
FBRaidEventFrame:SetScript("OnEvent", function() FBRaidEventFrame:SetScript("OnEvent", function()
if (FBAddonSuppressed) then return; end
if (event == "RAID_ROSTER_UPDATE" or event == "PARTY_MEMBERS_CHANGED" or event == "PLAYER_ENTERING_WORLD") then if (event == "RAID_ROSTER_UPDATE" or event == "PARTY_MEMBERS_CHANGED" or event == "PLAYER_ENTERING_WORLD") then
FBRaid_UpdateRoster(); -- Salven (mehrere Roster-Events je Frame beim Zonen) auf einen
FBRaid_SyncButtons(); -- Durchlauf im naechsten Frame zusammenfassen
FBRaid_RefreshBuffBorders(); FBRaidRosterDirty = true;
return; return;
end end
if (FBRaidTestMode > 0) then return; end if (FBRaidTestMode > 0) then return; end
local c = FBRaidUnitCell[arg1]; local c = FBRaidUnitCell[arg1];
if (not c) then return; end if (not c) then return; end
FBRaid_UpdateCell(c); FBRaid_UpdateCell(c, (event == "UNIT_AURA"));
if (event == "UNIT_AURA") then FBRaid_UpdateBuffBorder(c); end if (event == "UNIT_AURA") then FBRaid_UpdateBuffBorder(c); end
end); end);
FBRaidEventFrame:SetScript("OnUpdate", function() FBRaidEventFrame:SetScript("OnUpdate", function()
if (FBAddonSuppressed) then return; end
if (FBRaidRosterDirty) then
FBRaidRosterDirty = false;
FBRaid_UpdateRoster();
FBRaid_RefreshBuffBorders();
end
FBRaidTickAccum = FBRaidTickAccum + (arg1 or 0); FBRaidTickAccum = FBRaidTickAccum + (arg1 or 0);
if (FBRaidTickAccum < FBRAID_TICK) then return; end if (FBRaidTickAccum < FBRAID_TICK) then return; end
FBRaidTickAccum = 0; FBRaidTickAccum = 0;
@@ -1204,8 +1273,9 @@ end);
-- fuer den Raid-Test haengen wir uns an denselben Takt und fordern ihn an. -- fuer den Raid-Test haengen wir uns an denselben Takt und fordern ihn an.
FBRaidGhostAccum = 0; FBRaidGhostAccum = 0;
FBRaidEventFrame.ghostTicker = CreateFrame("Frame", nil, UIParent); FBRaidEventFrame.ghostTicker = CreateFrame("Frame", nil, UIParent);
FBRaidEventFrame.ghostTicker:Hide(); -- laeuft nur im Raid-Test (siehe FBRaid_SetTest)
FBRaidEventFrame.ghostTicker:SetScript("OnUpdate", function() FBRaidEventFrame.ghostTicker:SetScript("OnUpdate", function()
if (FBRaidTestMode == 0) then return; end if (FBAddonSuppressed) then return; end
FBRaidGhostAccum = FBRaidGhostAccum + (arg1 or 0); FBRaidGhostAccum = FBRaidGhostAccum + (arg1 or 0);
if (FBRaidGhostAccum < 0.2) then return; end if (FBRaidGhostAccum < 0.2) then return; end
FBRaidGhostAccum = 0; FBRaidGhostAccum = 0;
@@ -1223,6 +1293,8 @@ function FBRaid_SetTest(mode)
mode = tonumber(mode) or 0; mode = tonumber(mode) or 0;
if (mode ~= 20 and mode ~= 40) then mode = 0; end if (mode ~= 20 and mode ~= 40) then mode = 0; end
FBRaidTestMode = mode; FBRaidTestMode = mode;
-- Geister-Atmung nur im Test rechnen lassen
if (mode > 0) then FBRaidEventFrame.ghostTicker:Show(); else FBRaidEventFrame.ghostTicker:Hide(); end
if (mode > 0) then if (mode > 0) then
FBRaid_BuildTestGhosts(mode); FBRaid_BuildTestGhosts(mode);
if (HealBox.Active ~= 1) then HealBox.Active = 1; end if (HealBox.Active ~= 1) then HealBox.Active = 1; end
@@ -1451,6 +1523,11 @@ FBHealBox_RegisterHook("Loaded", function()
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00"..FBADDON_NAME..":|r "..FBT("RAID_LOADED")); DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00"..FBADDON_NAME..":|r "..FBT("RAID_LOADED"));
return true; return true;
end); end);
-- Klassensperre des Kerns: Raidfenster mit stilllegen
FBHealBox_RegisterHook("Suppress", function()
if (FBRaidFrame) then FBRaidFrame:Hide(); end
return true;
end);
FBHealBox_RegisterHook("Defaults", function() return FBRaid_ApplyDefaults(); end); FBHealBox_RegisterHook("Defaults", function() return FBRaid_ApplyDefaults(); end);
FBHealBox_RegisterHook("SyncOptions", function() return FBRaid_SyncOptions(); end); FBHealBox_RegisterHook("SyncOptions", function() return FBRaid_SyncOptions(); end);
FBHealBox_RegisterHook("ApplyLocale", function() return FBRaid_ApplyLocale(); end); FBHealBox_RegisterHook("ApplyLocale", function() return FBRaid_ApplyLocale(); end);
@@ -1476,9 +1553,10 @@ FBHealBox_RegisterHook("ButtonsChanged", function()
FBRaid_SyncButtons(); FBRaid_SyncButtons();
return true; return true;
end); end);
FBHealBox_RegisterHook("Aggro", function(tt) FBRaid_CheckAggro(tt); return true; end); FBHealBox_RegisterHook("Aggro", function(tt) return FBRaid_CheckAggro(tt); end);
FBHealBox_RegisterHook("SpellTimers", function(now) FBRaid_UpdateSpellTimers(now); return true; end); FBHealBox_RegisterHook("SpellTimers", function(now) FBRaid_UpdateSpellTimers(now); return true; end);
FBHealBox_RegisterHook("Cooldowns", function() FBRaid_UpdateCooldowns(); return true; end); FBHealBox_RegisterHook("Cooldowns", function() FBRaid_UpdateCooldowns(); return true; end);
FBHealBox_RegisterHook("ButtonStates", function(ev) FBRaid_UpdateButtonStates(ev); return true; end);
FBHealBox_RegisterHook("BuffIcons", function() FBRaid_UpdateBuffIcons(); return true; end); FBHealBox_RegisterHook("BuffIcons", function() FBRaid_UpdateBuffIcons(); return true; end);
FBHealBox_RegisterHook("Status", function() FBHealBox_RegisterHook("Status", function()
local cfg = FBRaid_Cfg(); local cfg = FBRaid_Cfg();
+47 -23
View File
@@ -317,6 +317,8 @@ end
-- Balken einsammeln: eigene Plakette und, falls vorhanden, eigene Raid-Zelle -- Balken einsammeln: eigene Plakette und, falls vorhanden, eigene Raid-Zelle
function FBTicker_CollectBars() function FBTicker_CollectBars()
-- alte Funken verstecken, Zwischenspeicher neu
FBTicker_HideAll();
FBTicker.sparks = {}; FBTicker.sparks = {};
if (FBHealBox1 and FBHealBox1.ManaBar) then if (FBHealBox1 and FBHealBox1.ManaBar) then
table.insert(FBTicker.sparks, { bar = FBHealBox1.ManaBar, spark = FBTicker_SparkFor(FBHealBox1.ManaBar) }); table.insert(FBTicker.sparks, { bar = FBHealBox1.ManaBar, spark = FBTicker_SparkFor(FBHealBox1.ManaBar) });
@@ -333,21 +335,22 @@ end
function FBTicker_HideAll() function FBTicker_HideAll()
for _, e in ipairs(FBTicker.sparks) do for _, e in ipairs(FBTicker.sparks) do
if (e.spark) then e.spark:Hide(); end if (e.spark and e.shown ~= false) then e.shown = false; e.spark:Hide(); end
end end
end end
function FBTicker_Draw() -- Torwaechter fuer das Zeichnen: wird nur bei Events und Optionswechseln
-- neu bewertet (Mana-Events, Gestaltwechsel, Schalter), nicht jeden Frame.
FBTicker.gate = false;
function FBTicker_UpdateGate()
local cfg = FBTicker_Cfg(); local cfg = FBTicker_Cfg();
if (cfg.Enabled ~= 1) or (HealBox.Active ~= 1) or (not FBTicker_PlayerHasMana()) then FBTicker.gate = (cfg.Enabled == 1) and (HealBox.Active == 1)
FBTicker_HideAll(); and FBTicker_PlayerHasMana() and (not FBTicker_ManaFull());
return; if (not FBTicker.gate) then FBTicker_HideAll(); end
end end
-- Volles Mana: nichts zu regenerieren, Funke aus
if (FBTicker_ManaFull()) then function FBTicker_Draw()
FBTicker_HideAll(); if (not FBTicker.gate) then return; end
return;
end
local frac, mode = FBTicker_Progress(GetTime()); local frac, mode = FBTicker_Progress(GetTime());
if (not frac) then if (not frac) then
FBTicker_HideAll(); FBTicker_HideAll();
@@ -355,7 +358,7 @@ function FBTicker_Draw()
end end
local col = FBTICK_COLOR_TICK; local col = FBTICK_COLOR_TICK;
if (mode == "fsr") then col = FBTICK_COLOR_FSR; end if (mode == "fsr") then col = FBTICK_COLOR_FSR; end
local w = cfg.SparkW or 2; local w = FBTicker_Cfg().SparkW or 2;
for _, e in ipairs(FBTicker.sparks) do for _, e in ipairs(FBTicker.sparks) do
local bar, s = e.bar, e.spark; local bar, s = e.bar, e.spark;
@@ -364,20 +367,29 @@ function FBTicker_Draw()
local bw = bar:GetWidth() or 0; local bw = bar:GetWidth() or 0;
local bh = bar:GetHeight() or 0; local bh = bar:GetHeight() or 0;
if (bw > w and bh > 0) then if (bw > w and bh > 0) then
s:SetWidth(w); if (e.w ~= w or e.h ~= bh) then
s:SetHeight(bh); e.w = w; e.h = bh;
s:SetWidth(w);
s:SetHeight(bh);
end
if (e.mode ~= mode) then if (e.mode ~= mode) then
e.mode = mode; e.mode = mode;
s:SetTexture(col[1], col[2], col[3], col[4]); s:SetTexture(col[1], col[2], col[3], col[4]);
end end
s:ClearAllPoints(); -- Anker nur setzen, wenn der Funke mindestens einen halben
s:SetPoint("LEFT", bar, "LEFT", frac * (bw - w), 0); -- Pixel weitergewandert ist
s:Show(); local x = frac * (bw - w);
if (not e.x) or (math.abs(x - e.x) >= 0.5) then
e.x = x;
s:ClearAllPoints();
s:SetPoint("LEFT", bar, "LEFT", x, 0);
end
if (not e.shown) then e.shown = true; s:Show(); end
else else
s:Hide(); if (e.shown) then e.shown = false; s:Hide(); end
end end
else elseif (e.shown) then
s:Hide(); e.shown = false; s:Hide();
end end
end end
end end
@@ -393,22 +405,25 @@ FBTickerFrame:RegisterEvent("UNIT_MAXMANA");
FBTickerFrame:RegisterEvent("UNIT_DISPLAYPOWER"); FBTickerFrame:RegisterEvent("UNIT_DISPLAYPOWER");
FBTickerFrame:RegisterEvent("PLAYER_ENTERING_WORLD"); FBTickerFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
FBTickerFrame:SetScript("OnEvent", function() FBTickerFrame:SetScript("OnEvent", function()
if (FBAddonSuppressed) then return; end
if (event == "PLAYER_ENTERING_WORLD") then if (event == "PLAYER_ENTERING_WORLD") then
FBTicker_Reset(); FBTicker_Reset();
FBTicker_CollectBars(); FBTicker_CollectBars();
FBTicker_UpdateGate();
return; return;
end end
if (arg1 ~= "player") then return; end if (arg1 ~= "player") then return; end
if (event == "UNIT_MANA") then if (event == "UNIT_MANA") then
FBTicker_OnManaChanged(); FBTicker_OnManaChanged();
if (FBTicker_ManaFull()) then FBTicker_HideAll(); end
elseif (event == "UNIT_DISPLAYPOWER") then elseif (event == "UNIT_DISPLAYPOWER") then
FBTicker_Reset(); FBTicker_Reset();
else else
FBTicker.lastMana = UnitMana("player"); FBTicker.lastMana = UnitMana("player");
end end
FBTicker_UpdateGate();
end); end);
FBTickerFrame:SetScript("OnUpdate", function() FBTickerFrame:SetScript("OnUpdate", function()
if (FBAddonSuppressed) then return; end
FBTicker_Draw(); FBTicker_Draw();
end); end);
@@ -463,6 +478,7 @@ function FBTicker_BuildOptions()
FBTickerEnabledCheck = FBHealBox_CreateCheck("FBHealBoxTickerEnabledCheck", tab, 40, y, "TICK_ENABLED", "TICK_ENABLED_TIP", function() FBTickerEnabledCheck = FBHealBox_CreateCheck("FBHealBoxTickerEnabledCheck", tab, 40, y, "TICK_ENABLED", "TICK_ENABLED_TIP", function()
FBTicker_Cfg().Enabled = FBTickerEnabledCheck:GetChecked() and 1 or 0; FBTicker_Cfg().Enabled = FBTickerEnabledCheck:GetChecked() and 1 or 0;
FBTicker_UpdateGate();
FBTicker_Draw(); FBTicker_Draw();
end); end);
FBTickerFSRCheck = FBHealBox_CreateCheck("FBHealBoxTickerFSRCheck", tab, 250, y, "TICK_FSR", "TICK_FSR_TIP", function() FBTickerFSRCheck = FBHealBox_CreateCheck("FBHealBoxTickerFSRCheck", tab, 250, y, "TICK_FSR", "TICK_FSR_TIP", function()
@@ -511,11 +527,18 @@ FBHealBox_RegisterHook("Loaded", function()
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00"..FBADDON_NAME..":|r "..FBT("TICK_LOADED")); DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00"..FBADDON_NAME..":|r "..FBT("TICK_LOADED"));
return true; return true;
end); end);
-- Klassensperre des Kerns: Funken ausknipsen
FBHealBox_RegisterHook("Suppress", function()
for _, e in ipairs(FBTicker.sparks or {}) do
if (e.spark) then e.spark:Hide(); end
end
return true;
end);
FBHealBox_RegisterHook("Defaults", function() return FBTicker_ApplyDefaults(); end); FBHealBox_RegisterHook("Defaults", function() return FBTicker_ApplyDefaults(); end);
FBHealBox_RegisterHook("SyncOptions", function() return FBTicker_SyncOptions(); end); FBHealBox_RegisterHook("SyncOptions", function() return FBTicker_SyncOptions(); end);
FBHealBox_RegisterHook("ApplyLocale", function() return FBTicker_ApplyLocale(); end); FBHealBox_RegisterHook("ApplyLocale", function() return FBTicker_ApplyLocale(); end);
FBHealBox_RegisterHook("UpdateNames", function() FBTicker_CollectBars(); return true; end); FBHealBox_RegisterHook("UpdateNames", function() FBTicker_CollectBars(); FBTicker_UpdateGate(); return true; end);
FBHealBox_RegisterHook("ActiveToggle", function() FBTicker_Draw(); return true; end); FBHealBox_RegisterHook("ActiveToggle", function() FBTicker_UpdateGate(); FBTicker_Draw(); return true; end);
FBHealBox_RegisterHook("Status", function() FBHealBox_RegisterHook("Status", function()
local state = FBT("TICK_UNSYNCED"); local state = FBT("TICK_UNSYNCED");
local nextIn = 0; local nextIn = 0;
@@ -535,6 +558,7 @@ FBHealBox_RegisterHook("Slash", function(msg)
local cfg = FBTicker_Cfg(); local cfg = FBTicker_Cfg();
if (cfg.Enabled == 1) then cfg.Enabled = 0; else cfg.Enabled = 1; end if (cfg.Enabled == 1) then cfg.Enabled = 0; else cfg.Enabled = 1; end
FBTicker_SyncOptions(); FBTicker_SyncOptions();
FBTicker_UpdateGate();
FBTicker_Draw(); FBTicker_Draw();
local key = "TICK_OFF"; local key = "TICK_OFF";
if (cfg.Enabled == 1) then key = "TICK_ON"; end if (cfg.Enabled == 1) then key = "TICK_ON"; end
+382 -277
View File
File diff suppressed because it is too large Load Diff