37 Commits
Author SHA1 Message Date
Mquadrat fac63b2d19 Add files via upload 2026-10-07 22:48:10 +02:00
Mquadrat b6db62d7dc Update README.md 2026-10-06 10:09:24 +02:00
Mquadrat 934e0047b0 Add files via upload 2026-10-06 10:08:02 +02:00
Mquadrat d0a3bbfa9b Update README.md 2026-10-05 18:52:09 +02:00
Mquadrat 86cfea6b76 Add files via upload 2026-10-05 18:50:57 +02:00
Mquadrat 38c3accf14 Update README.md 2026-09-12 09:24:18 +02:00
Mquadrat e6f98eb65a Update README.md 2026-09-12 09:23:48 +02:00
Mquadrat 12e0f0dfe0 Update README.md 2026-09-12 09:23:14 +02:00
Mquadrat 08a4abbfe5 Add files via upload 2026-09-12 09:22:48 +02:00
Mquadrat 87075c8c54 Update README.md 2026-09-11 19:38:22 +02:00
Mquadrat f0704bbd5c Update README.md 2026-09-11 19:33:07 +02:00
Mquadrat 4d961b4c4a Add files via upload 2026-09-11 19:32:29 +02:00
Mquadrat 46bfcd966b Update README.md 2026-09-11 19:18:59 +02:00
Mquadrat 28564b230f Update CHANGELOG.md 2026-09-11 19:17:26 +02:00
Mquadrat 2b57864a47 Add files via upload 2026-09-11 19:16:52 +02:00
Mquadrat 728a3ab5c6 Update README.md 2026-09-09 08:14:06 +02:00
Mquadrat f527200b77 Update CHANGELOG.md 2026-09-09 08:07:19 +02:00
Mquadrat be2844dc87 Update README.md 2026-09-09 08:04:16 +02:00
Mquadrat 4a40166251 Update README.md 2026-09-09 08:03:29 +02:00
Mquadrat f3de187efc Add files via upload 2026-09-09 08:02:31 +02:00
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
7 changed files with 2580 additions and 574 deletions
+275 -1
View File
@@ -1,6 +1,280 @@
# Changelog
## 1.4.4 (2026-09-05)
## 1.4.6 (2026-10-06)
Performance pass over the display loop, with no change in behaviour: only the units that actually changed are redrawn, and two loops that ran empty five times a second now stop early.
### English
**Performance**
- **Only the units that changed are redrawn.** The 0.2 second pass used to set a single dirty flag: if any heal over time ticked anywhere, everything was redrawn, which in a full raid meant forty cells plus ten plates although exactly one unit had changed. The pass now collects the affected unit names in a reused table and hands them to `FBHealBox_RefreshUnitsByName`, and the raid module picks up the same list through the new `RefreshNames` hook. With several HoTs running this turns three to five full sweeps a second into at most one plus a handful of single-unit updates.
- **A full sweep stays as a safety net.** At most once a second, and only while something is actually changing, the old full refresh still runs, so a name that does not match a plate for any reason can never leave a stale bar behind for longer than that.
- **Empty leftovers are cleared.** When the last heal over time or the last foreign heal on a unit expires, the now-empty sub-table is removed instead of being left behind. Several places read `FBHoTs[name]` as "is anything running on this unit", and an empty table answered yes.
- **Raid spell timers skip cells with nothing to show.** The intent to skip was already in the code but the result was thrown away two lines later, so the button loop ran anyway: in a full raid that was 160 do-nothing calls five times a second. The cell is now skipped outright, exactly as the plates already did.
- **Spell timers get an early out.** When no heal over time and no shield of yours is running anywhere, the plate loop no longer asks every slot for its unit and name five times a second; it only looks where a timer still has to be cleared. Classes without HoTs and the time between fights benefit.
**Checked and left alone**
- The three `OnUpdate` loops do no unnecessary work. The prediction checks three dirty flags and two counters per frame, the mana ticker leaves at its gate (a full mana bar means no pass at all), and the raid ghost ticker is hidden outside test mode. Event handling filters unit events through a lookup table rather than walking every frame.
- The global cooldown has been drawn as a sweep since 1.4.5.1, which means every visible button gets a fresh sweep after every cast. That is the intended behaviour and the animation runs in the client, not in Lua, so it stays. Should it ever feel heavy in a dense raid, `FBCD_SHOW_MIN` set to 2 limits the sweep to real cooldowns again.
### Deutsch
**Leistung**
- **Nur noch die Einheiten neu zeichnen, bei denen sich etwas geaendert hat.** Der 0,2-Sekunden-Takt setzte bisher ein einziges Dirty-Flag: Lief irgendwo ein HoT-Tick ab, wurde alles neu gezeichnet, im Vierzigerraid also vierzig Zellen und zehn Plaketten, obwohl sich bei genau einer Einheit etwas geaendert hatte. Der Takt sammelt die betroffenen Namen jetzt in einer wiederverwendeten Tabelle und reicht sie an `FBHealBox_RefreshUnitsByName`; das Raidmodul bekommt dieselbe Liste ueber den neuen Hook `RefreshNames`. Mit mehreren laufenden HoTs werden aus drei bis fuenf vollen Durchlaeufen je Sekunde hoechstens einer plus eine Handvoll Einzel-Updates.
- **Der volle Durchlauf bleibt als Sicherheitsnetz.** Hoechstens einmal je Sekunde, und nur solange sich ueberhaupt etwas aendert, laeuft er weiterhin. Ein Name, der aus irgendeinem Grund zu keiner Plakette passt, kann so nie laenger als eine Sekunde einen alten Balken stehen lassen.
- **Leere Reste werden weggeraeumt.** Laeuft der letzte HoT oder die letzte fremde Heilung auf einer Einheit ab, verschwindet die nun leere Untertabelle, statt liegen zu bleiben. Mehrere Stellen lesen `FBHoTs[name]` als "laeuft da ueberhaupt etwas", und eine leere Tabelle hat darauf mit ja geantwortet.
- **Raid-Zaubertimer ueberspringen Zellen ohne Anzeige.** Die Absicht zu ueberspringen stand schon im Code, das Ergebnis wurde aber zwei Zeilen spaeter wieder verworfen, sodass die Buttonschleife trotzdem lief: im Vierzigerraid 160 Leeraufrufe fuenfmal je Sekunde. Die Zelle wird jetzt wirklich uebersprungen, genau wie es die Plaketten laengst machen.
- **Zaubertimer steigen frueher aus.** Laeuft nirgends ein eigener HoT und kein eigener Schild, fragt die Plakettenschleife nicht mehr fuenfmal je Sekunde jeden Slot nach Einheit und Namen, sondern schaut nur noch dort hinein, wo ein Timer wegzuraeumen ist. Davon profitieren Klassen ohne HoTs und die Zeit zwischen den Kaempfen.
**Geprueft und so gelassen**
- Die drei `OnUpdate`-Schleifen arbeiten nicht unnoetig. Die Vorhersage prueft je Frame drei Dirty-Flags und zwei Zaehler, der Mana-Ticker steigt am Torwaechter aus (voller Manabalken heisst gar kein Durchlauf), und der Geister-Ticker im Raid ist ausserhalb des Testmodus versteckt. Die Ereignisverarbeitung filtert Unit-Events ueber eine Nachschlagetabelle, statt je Frame zu suchen.
- Der globale Cooldown laeuft seit 1.4.5.1 als Uhr mit, das heisst nach jedem Zauber bekommt jeder sichtbare Button eine neue Uhr gesetzt. Das ist das gewollte Verhalten, und die Animation laeuft im Client, nicht in Lua, deshalb bleibt es so. Sollte es im dichten Raid doch schwer wirken, beschraenkt `FBCD_SHOW_MIN` auf 2 die Uhr wieder auf echte Abklingzeiten.
## 1.4.5.3 (2026-10-05)
A readability option suggested by a user: the area behind the bars can be given a solid background, so the game world no longer shows through where health is missing.
### English
**New**
- **Bar background.** New slider *Bar background* on the *General* tab, 0 to 100 per cent in steps of 5. At 0, which is the default, nothing changes: the plate stays see-through and grass, sky or stone show through the empty part of the health bar. Turned up, a quiet dark grey surface sits behind the bars, so the green of the health bar stands on a fixed background. At 100 the plate is opaque. The shade is `FBBAR_BG_COLOR` (0.15 grey) and the setting is kept per character in `HealBox.BarBG`.
- **Where it sits.** The texture hangs on the lowest of the four stacked bars, the incoming-heal bar, on the `BACKGROUND` layer. That puts it under the prediction, the shield, the health and the mana bar, and it is shown and hidden together with them, so nothing is left behind on a plate that is currently empty or hidden by the attach mode. At 0 per cent no texture is set at all, rather than a fully transparent one.
- **Raid cells need nothing.** They already have their own backdrop at 80 per cent black, so there is no world shining through there; the slider deliberately only touches the plates.
**Thanks**
- The idea and a working patch came from a user who finds the translucent bar hard to read. The patch used `WHITE8X8` plus `SetVertexColor`; the version shipped here uses `SetTexture(r, g, b, a)`, which creates a solid colour directly in 1.12 and is what the mana strip already does. Same result, no texture file involved.
### Deutsch
**Neu**
- **Balkenhintergrund.** Neuer Regler *Balkenhintergrund* im Reiter *Allgemein*, 0 bis 100 Prozent in Fuenferschritten. Bei 0, dem Standard, aendert sich nichts: Die Plakette bleibt durchscheinend, durch den leeren Teil des Lebensbalkens sieht man Gras, Himmel oder Stein. Hochgedreht liegt eine ruhige dunkelgraue Flaeche hinter den Balken, das Gruen des Lebensbalkens steht dann auf festem Grund. Bei 100 ist die Plakette undurchsichtig. Der Grundton ist `FBBAR_BG_COLOR` (0,15 Grau), die Einstellung bleibt je Charakter in `HealBox.BarBG` gespeichert.
- **Wo die Flaeche haengt.** Die Textur sitzt auf der untersten der vier gestapelten Balken, der Heilvorhersage, auf der Ebene `BACKGROUND`. Damit liegt sie unter Vorhersage, Schild, Leben und Mana, und sie wird zusammen mit ihnen ein- und ausgeblendet; auf einer leeren oder im Anheftmodus versteckten Plakette bleibt also nichts stehen. Bei 0 Prozent wird gar keine Textur gesetzt statt einer voellig durchsichtigen.
- **Raidzellen brauchen nichts.** Sie haben bereits ihren eigenen Hintergrund mit 80 Prozent Schwarz, dort scheint keine Welt durch; der Regler fasst bewusst nur die Plaketten an.
**Dank**
- Idee und ein lauffaehiger Patch kamen von einem Nutzer, dem der durchscheinende Balken schlecht lesbar ist. Der Patch benutzte `WHITE8X8` mit `SetVertexColor`; hier ausgeliefert wird `SetTexture(r, g, b, a)`, das in 1.12 direkt eine Vollfarbe erzeugt und genau das ist, was der Manastreifen ohnehin schon macht. Gleiches Ergebnis, ohne Texturdatei.
## 1.4.5.2 (2026-09-12)
One fix: a running HoT no longer pulls Smart Healing down a rank.
### English
**Fixed**
- **A HoT on the target no longer talks Smart Healing into a lower rank.** The full healing still owed by a running HoT was subtracted from the deficit as if it had already landed, so a ticking Renew could turn a Heal rank 4 into a Lesser Heal. Only healing that arrives right away is deducted now, meaning direct heals and spells reported over HealComm, roughly one to three seconds out; HoTs stay out of the calculation because they spread their healing over up to fifteen seconds. They are of course still shown on the bar, and the rule that HoTs are never downranked themselves is untouched.
### Deutsch
**Behoben**
- **Ein HoT auf dem Ziel redet Smart Healing keinen kleineren Rang mehr ein.** Die noch offene Heilung eines laufenden HoT wurde vom Fehlbetrag abgezogen, als waere sie schon angekommen; ein tickendes Erneuerung konnte so aus einem Heilen Rang 4 ein Geringes Heilen machen. Abgezogen wird jetzt nur noch Heilung, die gleich ankommt, also Direktheilungen und ueber HealComm gemeldete Zauber, grob ein bis drei Sekunden; HoTs bleiben aus der Rechnung heraus, weil sie ihre Heilung ueber bis zu fuenfzehn Sekunden verteilen. Angezeigt werden sie auf dem Balken selbstverstaendlich weiterhin, und die Regel, dass HoTs selbst nie abgerangt werden, bleibt unberuehrt.
## 1.4.5.1 (2026-09-11)
Button and ticker fixes: the mana ticker spark moves smoothly again, and the global cooldown no longer greys out every button but runs as a sweep instead.
### English
**Fixed**
- **The mana ticker spark moves smoothly again.** The spark was only redrawn once it had travelled half a pixel. That sounds frugal, but it crosses the bar in five seconds, roughly 20 pixels per second, so at 60 frames it advanced on every second frame at best and, depending on rounding, sometimes after one frame and sometimes after two. That uneven step was the stutter, and the fixed 0.03 second redraw step introduced in 1.4.5 made it worse. The threshold is now effectively gone (0.05 px) and the spark is drawn every frame again, which doubles its motion from 30 to 60 steps per second with an even gap.
- **Drawing every frame still costs nothing.** Each spark remembers the width and height of its bar and re-reads them four times a second (`FBTICK_GEOM_STEP`) instead of asking the frame on every pass, and the gate check sits in front of it all: with a full mana bar there is no pass at all.
- **The global cooldown no longer darkens every button.** After casting anything, the client reports every spell as unusable for the length of the global cooldown. Taken literally, that turned all icons dark grey for a second and a half after every cast, as if nothing were available at all. A running cooldown no longer than the global one (`FBCD_MIN_DURATION`) is no longer treated as a reason to darken a button. Blue for missing mana, red for out of range and grey for a spell that really cannot be cast, for instance in the wrong shapeshift form, all work as before. This affects the plates and the raid cells alike, both go through `FBHealBox_ButtonUsable`.
- **It runs as a sweep instead.** Not showing it at all was the other half of the inconsistency: an instant like Renew or Abolish Disease left every button untouched, while Power Word: Shield, which has a real cooldown in 1.12, showed one. The sweep is the calm way to show a short wait, so after every cast you watch it run out on all buttons, the way the action bars do it.
- **Two thresholds instead of one.** The display threshold moved into its own constant `FBCD_SHOW_MIN` (0, show every running cooldown) and is no longer tied to `FBCD_MIN_DURATION` (2), which now only answers whether a cooldown is a reason to darken a button. The two were the same value before, which is why showing the global cooldown and not darkening for it could not be had at the same time. Setting `FBCD_SHOW_MIN` to 2 restores the quiet display. A cooldown is also only shown when one is really running (`cd[1] > 0`), and the sweep starts in the very next frame because the button pass is not throttled.
### Deutsch
**Behoben**
- **Der Funke im Manabalken laeuft wieder rund.** Neu gezeichnet wurde er erst, wenn er einen halben Pixel weitergewandert war. Das klingt sparsam, er braucht fuer den Balken aber fuenf Sekunden, also rund 20 Pixel je Sekunde: Bei 60 Bildern kam er damit bestenfalls in jedem zweiten Frame voran und je nach Rundung mal nach einem, mal nach zwei Frames. Genau dieser ungleiche Schritt war das Ruckeln, und der feste Zeichentakt von 0,03 Sekunden aus 1.4.5 hat es verstaerkt. Die Schwelle ist jetzt praktisch weg (0,05 px) und gezeichnet wird wieder in jedem Frame, das verdoppelt die Bewegung von 30 auf 60 gleichmaessige Schritte je Sekunde.
- **Das Zeichnen je Frame kostet trotzdem nichts.** Jeder Funke merkt sich Breite und Hoehe seines Balkens und liest sie viermal je Sekunde nach (`FBTICK_GEOM_STEP`), statt sie bei jedem Durchlauf zu erfragen, und der Torwaechter sitzt weiterhin davor: Bei vollem Manabalken laeuft gar kein Durchlauf.
- **Der globale Cooldown dunkelt nicht mehr alle Buttons ab.** Nach jedem gewirkten Zauber meldet der Client fuer die Dauer des globalen Cooldowns saemtliche Zauber als nicht nutzbar. Woertlich genommen wurden dadurch nach jedem Zauber anderthalb Sekunden lang alle Symbole dunkelgrau, als waere gar nichts mehr verfuegbar. Eine laufende Abklingzeit, die nicht laenger ist als der globale Cooldown (`FBCD_MIN_DURATION`), gilt jetzt nicht mehr als Grund zum Abdunkeln. Blau fuer fehlendes Mana, Rot fuer ausser Reichweite und Grau fuer einen Zauber, der wirklich nicht geht, etwa in der falschen Gestalt, arbeiten unveraendert weiter. Das gilt fuer Plaketten und Raidzellen gleichermassen, beide laufen ueber `FBHealBox_ButtonUsable`.
- **Stattdessen laeuft er als Uhr mit.** Ihn gar nicht zu zeigen war die andere Haelfte der Ungereimtheit: Ein Instant wie Erneuerung oder Krankheit aufheben liess alle Buttons unberuehrt, waehrend Machtwort: Schild, das in 1.12 eine echte Abklingzeit hat, eine Uhr zeigte. Die Uhr ist die ruhige Art, eine kurze Wartezeit anzuzeigen: Nach jedem Zauber laeuft sie auf allen Buttons ab, so wie es die Aktionsleisten machen.
- **Zwei Schwellen statt einer.** Die Anzeigeschwelle sitzt jetzt in einer eigenen Konstante `FBCD_SHOW_MIN` (0, also jede laufende Abklingzeit zeigen) und haengt nicht mehr an `FBCD_MIN_DURATION` (2), die nur noch beantwortet, ob eine Abklingzeit ein Grund zum Abdunkeln ist. Vorher war beides derselbe Wert, deshalb liess sich "globalen Cooldown zeigen" und "wegen ihm nicht abdunkeln" nicht gleichzeitig haben. Wer `FBCD_SHOW_MIN` auf 2 setzt, hat die ruhige Anzeige zurueck. Gezeigt wird ausserdem nur, wenn wirklich eine Abklingzeit laeuft (`cd[1] > 0`), und die Uhr startet im naechsten Frame, weil der Button-Durchgang nicht gedrosselt ist.
## 1.4.5 (2026-09-08)
Three additions around the frames and the prediction: Blizzard's party frames can go away, the resource bar is no longer reserved for mana, and gear healing is counted where an API can supply it. Plus a performance pass with no change in behaviour.
### English
**New & Changed**
- **Hide Blizzard party frames.** New switch on the *General* tab that hides `PartyMemberFrame1` to `4` while you are grouped, leaving only the Heal Box plates. Off by default. Blizzard shows those frames again on every roster change, so the addon hooks their `OnShow` once instead of hiding them a single time; a frame that reappears is hidden again in the same moment.
- **Mutually exclusive with the attach mode.** *Default party frames* docks the plates onto exactly those frames, so the two options can never both be on. Whichever one is ticked disables and greys out the other (`FBHealBox_UpdatePartyExclusion`), and `FBHealBox_HideBlizzPartyActive()` re-checks the same rule in code for saved settings that were edited by hand. Switching the option off brings the frames back immediately, and so does the class gate: an addon that stays asleep for warriors, rogues and hunters returns the frames as well.
- **Rage, energy and focus in the resource bar.** The thin strip under the health bar used to be reserved for mana and stayed empty for everyone else. New switch *Show rage, energy, focus* on the *General* tab gives every unit its own resource in its usual colour, from the new table `FBPOWER_COLORS`: mana blue, rage red, focus orange, energy yellow. `FBUnitMana()` now returns the power type as a fourth value, and both the plates and the raid cells colour their bar from it. Off by default, and it still needs the mana bar to be on.
- **Equipment healing bonus in the prediction.** Vanilla spell tooltips show the naked base value; the bonus from your gear is missing. Learned values from the combat log carry it automatically, so the gap only affected ranks you had never cast. `FBHealBox_ProbeHealBonus()` now looks for `GetSpellBonusHealing`, `GetSpellBonusHeal` and `GetHealingBonus`, first as globals and then inside a `ClassicAPI` table, and `FBPredict_ExpectedDirect()` adds the share that belongs to the spell.
- **Weighted the vanilla way.** The bonus counts as cast time divided by 3.5, capped at 3.5 seconds, with instants counting as 1.5 and an unreadable cast time as 2.5. Cast time is read once per spellbook slot from the tooltip (`FBHealBox_SpellCastSeconds`, right-hand side of line 2), so a 3 second Greater Heal takes 600 of a +700 bonus and a 1.5 second Flash Heal takes 300. Without a suitable API the bonus stays 0 and nothing about the old behaviour changes.
- **New command `/fbp healbonus`.** Turns the equipment bonus off and on, saved per character in `HealBox.HealBonus` (on by default). `/fbp` reports the value found and whether it is being applied; the command says so when no API supplies a bonus at all.
**Performance (no change in behaviour)**
- **Own buff timers read once per frame.** `FBPredict_PlayerBuffTimeLeft` used to walk the whole player buff list once per watched buff, calling `strupper` on every texture along the way: with six watches and thirty buffs that was around 200 API calls and 200 throwaway strings a second. The list is now read once per frame into a texture-to-seconds map (`FBPredict_ScanPlayerBuffTimes`), the way `FBHealBox_UnitBuffs` already worked, and every watch is served from it. Texture paths are upper-cased once and remembered (`FBHealBox_UpperTex`), which also removes the same allocation from the unit buff scan.
- **Range and line of sight in raid mode are spread over several ticks.** `FBRaid_Tick` used to check up to 40 cells in one frame every half second. It now walks a quarter of the groups per tick at a quarter of the interval (`FBRAID_TICK_SLICES`), so a full sweep still takes the same half second while the peak load per frame drops to a quarter.
- **`pcall` per sweep instead of per unit.** Range, distance and line of sight went through a protected call for every single unit, up to 160 a second in a full raid. The first successful call now marks the form as safe and the individual calls go straight through, while the sweep as a whole stays protected: one `pcall` per pass in `FBHealBox_CheckRangeAll`, `FBHealBox_CheckLOSAll` and `FBRaid_Tick`. A client that throws on the very first call keeps the old per-call protection and its fallback path.
- **Self-healing if an API throws late.** Should a sweep throw after the form had already proven itself, `FBHealBox_ApiFailed()` puts all three queries back to the protected single call, where the error is caught and the fallback takes over as before. The frame is never torn open, and at worst one pass is skipped.
- **Raid health text no longer builds strings it throws away.** The percentage and deficit texts were formatted before the change check, so every `UNIT_HEALTH` in a 40 man raid produced a string that was usually discarded immediately. The cell now compares the number and only formats on a real change, the way the plates always did.
- **Cheap pre-test before the combat log patterns.** Up to four anchored patterns ran on every message from twenty registered events. A plain substring test now filters first, with the words cut out of the client's own message templates so it works in every language. The tick test deliberately comes from the self template, because "You gain" and "%s gains" differ in the verb but share what follows.
- **Equipment bonus read once instead of per candidate.** With heal chains a single Smart Healing click asked the API for the bonus up to fifteen times. The value is now remembered and dropped again on `UNIT_INVENTORY_CHANGED` or a spellbook rebuild.
- **Cast time comes free with the spell scan.** `FBHealBox_SpellCastSeconds` used to build its own tooltip on a cache miss, which could mean a dozen tooltip builds in the middle of combat on the first click. `FBPredict_TooltipText` now picks the cast time off the right-hand side while the tooltip is standing anyway and fills the cache; the standalone scan remains only as a fallback. The cast time is deliberately kept out of the parsed text, because a number like 2.5 would confuse the shield duration and heal amount patterns.
- **Smaller things.** The range spell of the first assigned button is remembered instead of being searched over ten slots on every range check (invalidated on rebinding and on a spellbook rebuild). The ticker redraws on a fixed 0.03 second step instead of every frame (taken back in 1.4.5.1, it made the spark stutter). Blizzard's party frames are re-evaluated in the same collection point as the name updates instead of on every event of a burst. Two `or {}` fallbacks in loops no longer allocate a throwaway table.
### Deutsch
**Neu & Geaendert**
- **Blizzards Gruppenfenster ausblenden.** Neuer Schalter im Reiter *Allgemein*, der `PartyMemberFrame1` bis `4` versteckt, solange du in einer Gruppe bist, sodass nur die Plaketten der Heal Box uebrig bleiben. Standardmaessig aus. Blizzard zeigt diese Frames bei jeder Gruppenaenderung neu an, deshalb haengt sich das Addon einmalig in deren `OnShow` statt sie nur einmal zu verstecken; ein Frame, der wieder auftaucht, verschwindet im selben Moment erneut.
- **Schliesst sich mit dem Anheftmodus aus.** *Standard-Gruppenfenster* haengt die Plaketten genau an diese Frames, beide Optionen koennen also nie zusammen an sein. Was angehakt ist, sperrt und graut das andere aus (`FBHealBox_UpdatePartyExclusion`), und `FBHealBox_HideBlizzPartyActive()` prueft dieselbe Regel im Code noch einmal nach, fuer von Hand bearbeitete gespeicherte Werte. Ausschalten holt die Frames sofort zurueck, ebenso die Klassensperre: Ein Addon, das fuer Krieger, Schurken und Jaeger schlaeft, gibt die Frames mit zurueck.
- **Wut, Energie und Fokus im Ressourcenbalken.** Der schmale Streifen unter dem Lebensbalken war dem Mana vorbehalten und blieb bei allen anderen leer. Der neue Schalter *Wut, Energie, Fokus zeigen* im Reiter *Allgemein* gibt jeder Einheit ihre eigene Ressource in der gewohnten Farbe, aus der neuen Tabelle `FBPOWER_COLORS`: Mana blau, Wut rot, Fokus orange, Energie gelb. `FBUnitMana()` liefert die Energieart jetzt als vierten Rueckgabewert, Plaketten und Raidzellen faerben ihren Balken danach. Standardmaessig aus, und der Manabalken muss weiterhin an sein.
- **Heilbonus der Ausruestung in der Vorhersage.** Vanilla-Tooltips zeigen den nackten Grundwert, der Bonus der Ausruestung fehlt darin. Gelernte Werte aus dem Combatlog tragen ihn von selbst, die Luecke betraf also nur Raenge, die nie gewirkt wurden. `FBHealBox_ProbeHealBonus()` sucht jetzt nach `GetSpellBonusHealing`, `GetSpellBonusHeal` und `GetHealingBonus`, erst als globale Funktion, dann in einer `ClassicAPI`-Tabelle, und `FBPredict_ExpectedDirect()` schlaegt den Anteil auf, der auf den Zauber entfaellt.
- **Gewichtet wie in Vanilla ueblich.** Der Bonus zaehlt mit Zauberzeit geteilt durch 3,5, gedeckelt bei 3,5 Sekunden, Instants rechnen mit 1,5 und eine unlesbare Zauberzeit mit 2,5. Die Zauberzeit wird einmal je Zauberbuchplatz aus dem Tooltip gelesen (`FBHealBox_SpellCastSeconds`, rechte Seite der zweiten Zeile). Eine Grosse Heilung mit 3 Sekunden nimmt von +700 Heilung also 600 mit, eine Blitzheilung mit 1,5 Sekunden 300. Ohne passende API bleibt der Bonus 0 und am bisherigen Verhalten aendert sich nichts.
- **Neuer Befehl `/fbp healbonus`.** Schaltet den Ausruestungsbonus aus und wieder ein, gespeichert je Charakter in `HealBox.HealBonus` (Standard an). `/fbp` nennt den gefundenen Wert und ob er angewandt wird; der Befehl sagt es, wenn ueberhaupt keine API einen Bonus liefert.
**Leistung (ohne Funktionsaenderung)**
- **Eigene Bufflaufzeiten einmal je Frame.** `FBPredict_PlayerBuffTimeLeft` lief bisher je ueberwachtem Buff einmal durch die gesamte Buffliste und rief dabei fuer jede Textur `strupper` auf: bei sechs Wachen und dreissig Buffs rund 200 API-Aufrufe und 200 Wegwerf-Strings je Sekunde. Die Liste wird jetzt einmal je Frame in eine Tabelle Textur nach Sekunden gelesen (`FBPredict_ScanPlayerBuffTimes`), genau wie es `FBHealBox_UnitBuffs` schon vormacht, und alle Wachen bedienen sich daraus. Texturpfade werden einmal gross geschrieben und gemerkt (`FBHealBox_UpperTex`), was dieselbe Zuteilung auch aus dem Buffscan der Einheiten nimmt.
- **Reichweite und Sichtlinie im Raid auf mehrere Ticks verteilt.** `FBRaid_Tick` pruefte alle halbe Sekunde bis zu 40 Zellen in einem einzigen Frame. Jetzt kommt je Tick ein Viertel der Gruppen dran, dafuer viermal so oft (`FBRAID_TICK_SLICES`): Der volle Durchlauf dauert weiterhin eine halbe Sekunde, die Spitzenlast je Frame sinkt auf ein Viertel.
- **`pcall` je Durchlauf statt je Einheit.** Reichweite, Abstand und Sichtlinie liefen fuer jede einzelne Einheit durch einen geschuetzten Aufruf, im Vierzigerraid bis zu 160 je Sekunde. Der erste erfolgreiche Aufruf erklaert die Form jetzt fuer sicher und die Einzelaufrufe gehen direkt hinein, der Durchlauf als Ganzes bleibt aber geschuetzt: ein `pcall` je Durchgang in `FBHealBox_CheckRangeAll`, `FBHealBox_CheckLOSAll` und `FBRaid_Tick`. Ein Client, der schon beim ersten Aufruf wirft, behaelt den geschuetzten Einzelaufruf samt Rueckfallweg.
- **Selbstheilung, wenn eine API doch spaet wirft.** Wirft ein Durchlauf, obwohl die Form sich vorher bewaehrt hatte, stellt `FBHealBox_ApiFailed()` alle drei Abfragen auf den geschuetzten Einzelaufruf zurueck, wo der Fehler abgefangen wird und der Rueckfall wie frueher greift. Der Frame wird nie aufgerissen, schlimmstenfalls faellt ein Durchgang aus.
- **Kein HP-Text im Raid, der gleich wieder weggeworfen wird.** Prozent- und Deficit-Text wurden vor dem Vergleich gebaut, jedes `UNIT_HEALTH` im Vierzigerraid erzeugte also einen String, der meist sofort verfiel. Die Zelle vergleicht jetzt die Zahl und formatiert nur bei echter Aenderung, so wie es die Plaketten immer schon tun.
- **Billiger Vortest vor den Combatlog-Mustern.** Bei jeder Meldung aus zwanzig registrierten Ereignissen liefen bis zu vier verankerte Muster. Ein einfacher Textvergleich filtert jetzt vorweg, mit Wortstuecken, die aus den Meldungsvorlagen des Clients geschnitten werden und damit in jeder Sprache passen. Der Tick-Vortest kommt bewusst aus der Selbst-Vorlage, weil sich "You gain" und "%s gains" im Verb unterscheiden, im Rest aber gleich sind.
- **Ausruestungsbonus einmal statt je Kandidat.** Mit Heilketten fragte ein einziger Smart-Healing-Klick die API bis zu fuenfzehnmal nach dem Bonus. Der Wert wird jetzt gemerkt und bei `UNIT_INVENTORY_CHANGED` oder einem Neuaufbau des Zauberbuchs wieder verworfen.
- **Zauberzeit faellt beim Auslesen der Zauber mit ab.** `FBHealBox_SpellCastSeconds` baute bei einem Fehlgriff einen eigenen Tooltip, was beim ersten Klick mitten im Kampf ein Dutzend Tooltip-Aufbauten bedeuten konnte. `FBPredict_TooltipText` liest die Zauberzeit jetzt nebenbei von der rechten Seite ab, solange der Tooltip ohnehin steht, und fuellt den Zwischenspeicher; der eigene Scan bleibt nur als Rueckfall. Der Wert kommt bewusst nicht in den ausgewerteten Text, weil eine Zahl wie 2.5 die Muster fuer Schilddauer und Heilbetrag durcheinanderbraechte.
- **Kleinigkeiten.** Der Reichweiten-Zauber des ersten belegten Buttons wird gemerkt, statt bei jeder Pruefung ueber zehn Plaetze zu suchen (verworfen beim Umbelegen und beim Neuaufbau des Zauberbuchs). Der Ticker zeichnet in einem festen Takt von 0,03 Sekunden statt in jedem Frame (in 1.4.5.1 zurueckgenommen, der Funke ruckelte dadurch). Blizzards Gruppenfenster werden im selben Sammelpunkt wie die Namen neu bewertet statt bei jedem Ereignis einer Salve. Zwei `or {}`-Rueckfaelle in Schleifen legen keine Wegwerf-Tabelle mehr an.
## 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 %.
+1697 -265
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -1,9 +1,9 @@
## Interface: 11200
## Title: Heal Box Vanilla |cFFFFFF00v1.4.4|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-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.
## Title: Heal Box Vanilla |cFFFFFF00v1.4.6.1|r
## Notes: Heal/raid frame addon like Healium, with quick-cast buttons, heal prediction, mana ticker and more.
## 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
## Version: 1.4.4
## Version: 1.4.6.1
## SavedVariablesPerCharacter: HealBox
## OptionalDeps: SuperWoW
+4
View File
@@ -425,6 +425,9 @@ end
-- Vor-Hook auf UseAction. true, wenn das Modul selbst gecastet hat.
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 ((CursorHasSpell and CursorHasSpell()) or (CursorHasItem and CursorHasItem())) then return false; end
local name, rank = FBDmg_SlotSpell(slot);
@@ -464,6 +467,7 @@ for _, ev in ipairs({
}) do FBDmgFrame:RegisterEvent(ev); end
FBDmgFrame:SetScript("OnEvent", function()
if (FBAddonSuppressed) then return; end
if (event == "PLAYER_ENTERING_WORLD" or event == "SPELLS_CHANGED") then
FBDmg_ScanSpells();
FBDmg_UpdateSpellText();
+107 -26
View File
@@ -28,7 +28,8 @@ FBRAID_MAX = 40;
FBRAID_GROUPS = 8;
FBRAID_PER_GROUP = 5;
FBRAID_MAX_BUTTONS = 4;
FBRAID_TICK = 0.5; -- Sek.: Reichweite und Sichtlinie
FBRAID_TICK = 0.5; -- Sek.: voller Durchlauf Reichweite und Sichtlinie
FBRAID_TICK_SLICES = 4; -- in so viele Haeppchen zerlegt (je 2 Gruppen)
FBRAID_MANA_H = 3; -- px Manastreifen in der Zelle
FBRAID_LOS_SIZE = 10; -- px Sichtlinien-Auge in der Zelle
FBRAID_HEADER_H = 12; -- px Gruppenkopf
@@ -289,6 +290,7 @@ FBRaidTestMode = 0; -- 0 | 20 | 40
FBRaidTestGhosts = {}; -- [i] = Geist-Datensatz (Testmodus)
FBRaidTickAccum = 0;
FBRaidDragging = false;
FBRaidAppliedScale = nil; -- zuletzt gesetzte Skalierung (FBRaid_LayoutAll)
function FBRaid_Cfg()
if (not HealBox.Raid) then FBRaid_ApplyDefaults(); end
@@ -437,8 +439,14 @@ end
function FBRaid_Mana(cell)
local g = cell.ghost;
if (g) then
if (not g.hasMana) then return 0, 0, false; end
return math.floor(g.mpMax * g.mp), g.mpMax, true;
if (not g.hasMana) then
-- Wut, Energie und Fokus nur, wenn der Kern sie zeigen soll
if (HealBox.PowerBar == 1) and (g.power) and (g.mpMax) then
return math.floor(g.mpMax * (g.mp or 0)), g.mpMax, true, g.power;
end
return 0, 0, false, nil;
end
return math.floor(g.mpMax * g.mp), g.mpMax, true, 0;
end
return FBUnitMana(cell.unit);
end
@@ -657,7 +665,12 @@ function FBRaid_CreateCell(g, pos)
if (FBDragSpell) then
local side = "L";
if (arg1 == "RightButton" or IsShiftKeyDown()) then side = "R"; end
if (FBHealBox_DropSpell(i, side)) then return; end
-- Nicht die Schleifenvariable i verwenden: Lua 5.0 (Client
-- 1.12) teilt sie zwischen allen hier erzeugten Closures, alle
-- vier Buttons saehen ihren Endwert und legten den Zauber
-- auf Button 4. b ist dagegen je Durchlauf eine neue lokale
-- Variable.
if (FBHealBox_DropSpell(b.btnIndex, side)) then return; end
end
if (FBRaidTestMode > 0) then
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00"..FBADDON_NAME..":|r "..FBT("RAID_TEST_CLICK"));
@@ -738,7 +751,7 @@ function FBRaid_UpdateRoster()
if (FBRaidCells[g] and FBRaidCells[g][pos]) then
local c = FBRaidCells[g][pos];
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.dispelKnown = nil; c.lastText = nil; c.lastPct = nil; c.lastDef = nil; c.lastMax = nil; c.colorKey = nil; c.manaShown = nil; c.lastMpMax = nil; c.powerType = nil;
c.vHp = nil; c.vShield = nil; c.vInc = nil; c.vMp = nil;
c:Hide();
end
@@ -820,7 +833,16 @@ function FBRaid_LayoutAll()
local titleH = 0;
if (cfg.ShowTitle == 1) then titleH = FBRAID_TITLE_H + 2; FBRaidFrame.Title:Show(); else FBRaidFrame.Title:Hide(); end
FBRaidFrame:SetScale(cfg.Scale or 1);
-- SetScale wertet die Ankerversaetze im neuen Massstab aus. Ohne erneutes
-- Verankern wanderte das Raster beim Ziehen am Regler und sprang erst
-- beim naechsten Rosterwechsel zurueck. Nur bei echter Aenderung, und
-- nie waehrend das Raster gerade gezogen wird.
local scale = cfg.Scale or 1;
if (FBRaidAppliedScale ~= scale) then
FBRaidAppliedScale = scale;
FBRaidFrame:SetScale(scale);
if (not FBRaidDragging) then FBRaid_RestorePosition(); end
end
local perRow = cfg.GroupsPerRow;
if (perRow < 1) then perRow = 1; end
@@ -940,6 +962,7 @@ function FBRaid_UpdateCell(c, auraChanged)
if (state == "ghost") then key = "STATE_GHOST"; end
if (state == "offline") then key = "STATE_OFFLINE"; end
FBRaid_SetText(c, FBT(key));
c.lastPct = nil; c.lastDef = nil; -- Zahlenspeicher gilt nicht mehr
FBRaid_SetMax(c, hpMax);
FBRaid_SetValues(c, 0, 0, 0);
FBRaid_SetColor(c, "state", 0.5, 0.5, 0.5, 1);
@@ -956,19 +979,29 @@ function FBRaid_UpdateCell(c, auraChanged)
if (incTop > hpMax) then incTop = hpMax; end
FBRaid_SetValues(c, hp, shieldTop, incTop);
-- HP-Text
-- HP-Text. Verglichen wird die Zahl, nicht der fertige Text: sonst baut
-- jedes UNIT_HEALTH einen String, der meist sofort wieder verworfen wird.
if (cfg.HPText == "percent") then
FBRaid_SetText(c, math.floor(hp / hpMax * 100).."%");
local pct = math.floor(hp / hpMax * 100);
if (c.lastPct ~= pct) then
c.lastPct = pct; c.lastDef = nil;
FBRaid_SetText(c, pct.."%");
end
elseif (cfg.HPText == "deficit") then
local def = hpMax - hp;
if (def > 0) then FBRaid_SetText(c, "-"..def); else FBRaid_SetText(c, ""); end
if (c.lastDef ~= def) then
c.lastDef = def; c.lastPct = nil;
if (def > 0) then FBRaid_SetText(c, "-"..def); else FBRaid_SetText(c, ""); end
end
else
c.lastPct = nil; c.lastDef = nil;
FBRaid_SetText(c, false);
end
-- Mana
local mp, mpMax, hasMana = FBRaid_Mana(c);
local mp, mpMax, hasMana, ptype = FBRaid_Mana(c);
if (cfg.ManaBar == 1 and hasMana and mpMax > 0) then
FBHealBox_SetPowerColor(c.ManaBar, c, ptype);
if (c.lastMpMax ~= mpMax) then c.lastMpMax = mpMax; c.ManaBar:SetMinMaxValues(0, mpMax); end
if (c.vMp ~= mp) then c.vMp = mp; c.ManaBar:SetValue(mp); end
if (c.manaShown ~= true) then c.manaShown = true; c.ManaBar:Show(); end
@@ -1075,21 +1108,25 @@ function FBRaid_UpdateSpellTimers(now)
for pos = 1, FBRAID_PER_GROUP do
local c = FBRaidCells[g][pos];
if (c) then
-- 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
local b = c.buttons[i];
if (shown and b:IsShown()) then
local text, color = FBHealBox_SpellTimerFor(b.spellBase, c.name, now, c.ghost, i);
if (not text and b.spellBaseR) then
text, color = FBHealBox_SpellTimerFor(b.spellBaseR, c.name, now, nil, i);
local shown = on and c.unit and c:IsShown() and (c.ghost or FBHoTs[c.name] or FBShields[c.name]
or (FBWeakenedSoul and FBWeakenedSoul[c.name]));
-- Nichts anzuzeigen und nichts mehr wegzuraeumen: Zelle
-- ueberspringen. Vorher lief die Buttonschleife auch dann
-- durch, im Vierzigerraid 160 Leeraufrufe fuenfmal je
-- Sekunde. Der Kern macht es bei den Plaketten genauso.
if (shown or c.timersShown) then
c.timersShown = (shown and true) or false;
for i = 1, FBRAID_MAX_BUTTONS do
local b = c.buttons[i];
if (shown and b:IsShown()) then
local text, color = FBHealBox_SpellTimerFor(b.spellBase, c.name, now, c.ghost, i);
if (not text and b.spellBaseR) then
text, color = FBHealBox_SpellTimerFor(b.spellBaseR, c.name, now, nil, i);
end
FBHealBox_SetButtonTimer(b, text, color);
else
FBHealBox_SetButtonTimer(b, nil);
end
FBHealBox_SetButtonTimer(b, text, color);
else
FBHealBox_SetButtonTimer(b, nil);
end
end
end
@@ -1199,10 +1236,30 @@ function FBRaid_SyncButtons()
end
-- Reichweite, Sichtlinie und Geister-Atmung im Takt
-- Reichweite und Sichtlinie kosten je Zelle einen Aufruf ins Spiel hinein.
-- Bei acht Gruppen waeren das in einem Rutsch bis zu 80 Abfragen in einem
-- einzigen Frame. Deshalb wandert der Durchlauf in Haeppchen: je Tick nur
-- ein Teil der Gruppen, nach FBRAID_TICK_SLICES Ticks sind alle einmal dran.
-- Die Taktdauer ist entsprechend kuerzer, der volle Durchlauf dauert also
-- weiterhin FBRAID_TICK Sekunden. force = true macht alles auf einmal.
FBRaidTickSlice = 0;
-- Der Durchlauf laeuft geschuetzt, aber nur einmal je Tick statt einmal je
-- Zelle: im Vierzigerraid ein pcall alle 0,125 Sekunden statt achtzig je
-- Sekunde. Wirft eine Abfrage doch, gehen die Direktaufrufe zurueck in den
-- geschuetzten Einzelaufruf und der Rueckfallweg greift wieder.
function FBRaid_Tick(force)
if (not FBRaid_IsActive()) then return; end
local slice = FBRaidTickSlice;
FBRaidTickSlice = math.mod(FBRaidTickSlice + 1, FBRAID_TICK_SLICES);
if (not pcall(FBRaid_TickSweep, slice, force)) then
if (FBHealBox_ApiFailed) then FBHealBox_ApiFailed(); end
end
end
function FBRaid_TickSweep(slice, force)
for g = 1, FBRAID_GROUPS do
if (FBRaidCells[g]) then
if (FBRaidCells[g]) and (force or (math.mod(g - 1, FBRAID_TICK_SLICES) == slice)) then
for pos = 1, FBRAID_PER_GROUP do
local c = FBRaidCells[g][pos];
if (c and c.unit and c:IsShown()) then
@@ -1242,6 +1299,7 @@ FBRaidEventFrame:RegisterEvent("UNIT_AURA");
FBRaidRosterDirty = false;
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
-- Salven (mehrere Roster-Events je Frame beim Zonen) auf einen
-- Durchlauf im naechsten Frame zusammenfassen
@@ -1256,13 +1314,14 @@ FBRaidEventFrame:SetScript("OnEvent", function()
end);
FBRaidEventFrame:SetScript("OnUpdate", function()
if (FBAddonSuppressed) then return; end
if (FBRaidRosterDirty) then
FBRaidRosterDirty = false;
FBRaid_UpdateRoster();
FBRaid_RefreshBuffBorders();
end
FBRaidTickAccum = FBRaidTickAccum + (arg1 or 0);
if (FBRaidTickAccum < FBRAID_TICK) then return; end
if (FBRaidTickAccum < (FBRAID_TICK / FBRAID_TICK_SLICES)) then return; end
FBRaidTickAccum = 0;
FBRaid_Tick(false);
end);
@@ -1273,6 +1332,7 @@ FBRaidGhostAccum = 0;
FBRaidEventFrame.ghostTicker = CreateFrame("Frame", nil, UIParent);
FBRaidEventFrame.ghostTicker:Hide(); -- laeuft nur im Raid-Test (siehe FBRaid_SetTest)
FBRaidEventFrame.ghostTicker:SetScript("OnUpdate", function()
if (FBAddonSuppressed) then return; end
FBRaidGhostAccum = FBRaidGhostAccum + (arg1 or 0);
if (FBRaidGhostAccum < 0.2) then return; end
FBRaidGhostAccum = 0;
@@ -1520,6 +1580,11 @@ FBHealBox_RegisterHook("Loaded", function()
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00"..FBADDON_NAME..":|r "..FBT("RAID_LOADED"));
return true;
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("SyncOptions", function() return FBRaid_SyncOptions(); end);
FBHealBox_RegisterHook("ApplyLocale", function() return FBRaid_ApplyLocale(); end);
@@ -1537,6 +1602,22 @@ FBHealBox_RegisterHook("ActiveToggle", function()
FBRaid_UpdateVisibility();
return true;
end);
-- Nur die Zellen anfassen, deren Einheit sich geaendert hat. Der Kern
-- schickt die Namensliste mit; eine Zelle, die nicht drinsteht, kostet
-- einen Tabellenzugriff statt einer vollen Aktualisierung.
FBHealBox_RegisterHook("RefreshNames", function(names)
if (not FBRaid_IsActive()) or (not names) then return true; end
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.name and names[c.name]) then FBRaid_UpdateCell(c); end
end
end
end
return true;
end);
FBHealBox_RegisterHook("RefreshAllBars", function()
FBRaid_RefreshAll();
return true;
+39 -6
View File
@@ -351,7 +351,8 @@ end
function FBTicker_Draw()
if (not FBTicker.gate) then return; end
local frac, mode = FBTicker_Progress(GetTime());
local now = GetTime();
local frac, mode = FBTicker_Progress(now);
if (not frac) then
FBTicker_HideAll();
return;
@@ -364,8 +365,17 @@ function FBTicker_Draw()
local bar, s = e.bar, e.spark;
if (s and bar) then
if (bar:IsShown()) then
local bw = bar:GetWidth() or 0;
local bh = bar:GetHeight() or 0;
-- Breite und Hoehe aendern sich nur beim Umbauen der Anzeige.
-- Sie werden deshalb viermal je Sekunde nachgelesen und
-- dazwischen gemerkt; so kostet das Zeichnen je Frame fast
-- nichts mehr und darf wieder in jedem Frame laufen.
if (not e.geomT) or ((now - e.geomT) >= FBTICK_GEOM_STEP) then
e.geomT = now;
e.bw = bar:GetWidth() or 0;
e.bh = bar:GetHeight() or 0;
end
local bw = e.bw or 0;
local bh = e.bh or 0;
if (bw > w and bh > 0) then
if (e.w ~= w or e.h ~= bh) then
e.w = w; e.h = bh;
@@ -376,10 +386,16 @@ function FBTicker_Draw()
e.mode = mode;
s:SetTexture(col[1], col[2], col[3], col[4]);
end
-- Anker nur setzen, wenn der Funke mindestens einen halben
-- Pixel weitergewandert ist
-- Anker setzen, sobald sich die Position ueberhaupt
-- geaendert hat. Eine Schwelle von einem halben Pixel
-- klingt sparsam, der Funke wandert aber nur rund 20 px
-- je Sekunde: bei 60 Bildern kam er damit nur in jedem
-- zweiten Frame voran, und je nach Rundung mal nach einem,
-- mal nach zwei Frames. Genau das sah ruckelig aus. Die
-- Feinrundung auf ganze Bildpunkte macht ohnehin die
-- Oberflaeche selbst.
local x = frac * (bw - w);
if (not e.x) or (math.abs(x - e.x) >= 0.5) then
if (not e.x) or (math.abs(x - e.x) >= 0.05) then
e.x = x;
s:ClearAllPoints();
s:SetPoint("LEFT", bar, "LEFT", x, 0);
@@ -405,6 +421,7 @@ FBTickerFrame:RegisterEvent("UNIT_MAXMANA");
FBTickerFrame:RegisterEvent("UNIT_DISPLAYPOWER");
FBTickerFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
FBTickerFrame:SetScript("OnEvent", function()
if (FBAddonSuppressed) then return; end
if (event == "PLAYER_ENTERING_WORLD") then
FBTicker_Reset();
FBTicker_CollectBars();
@@ -421,7 +438,16 @@ FBTickerFrame:SetScript("OnEvent", function()
end
FBTicker_UpdateGate();
end);
-- Gezeichnet wird in jedem Frame. Ein fester Takt spart zwar Durchlaeufe,
-- macht die Bewegung aber sichtbar stufig: der Funke braucht fuer den Balken
-- fuenf Sekunden, jede ausgelassene Position sieht man sofort. Teuer ist das
-- Zeichnen nicht mehr, weil die Balkenmasse gemerkt werden und der Ausstieg
-- ueber das Gate davor sitzt (voller Manabalken = gar kein Durchlauf).
FBTICK_GEOM_STEP = 0.25; -- Sek.: so oft werden Breite und Hoehe nachgelesen
FBTickerFrame:SetScript("OnUpdate", function()
if (FBAddonSuppressed) then return; end
if (not FBTicker.gate) then return; end
FBTicker_Draw();
end);
@@ -525,6 +551,13 @@ FBHealBox_RegisterHook("Loaded", function()
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00"..FBADDON_NAME..":|r "..FBT("TICK_LOADED"));
return true;
end);
-- Klassensperre des Kerns: Funken ausknipsen
FBHealBox_RegisterHook("Suppress", function()
for _, e in ipairs(FBTicker.sparks) do
if (e.spark) then e.spark:Hide(); end
end
return true;
end);
FBHealBox_RegisterHook("Defaults", function() return FBTicker_ApplyDefaults(); end);
FBHealBox_RegisterHook("SyncOptions", function() return FBTicker_SyncOptions(); end);
FBHealBox_RegisterHook("ApplyLocale", function() return FBTicker_ApplyLocale(); end);
+454 -272
View File
File diff suppressed because it is too large Load Diff