Add files via upload

This commit is contained in:
Mquadrat
2026-10-14 21:10:30 +02:00
committed by GitHub
parent c3a9d50181
commit cbf7040f2a
4 changed files with 483 additions and 130 deletions
+411 -110
View File
@@ -8,7 +8,9 @@
-- * Heilvorhersage fuer Direktheilung, HoT-Restticks und Absorb-Schilde, -- * Heilvorhersage fuer Direktheilung, HoT-Restticks und Absorb-Schilde,
-- selbstkorrigierend ueber den Combatlog -- selbstkorrigierend ueber den Combatlog
-- * HealComm-Sync mit Puppeteer, pfUI, Luna und Co. -- * HealComm-Sync mit Puppeteer, pfUI, Luna und Co.
-- * Lokalisierung Deutsch / Englisch, im Optionsfenster umschaltbar -- * Lokalisierung Deutsch, Englisch, Spanisch, Franzoesisch und
-- Italienisch (die drei letzten seit v1.4.3), im Optionsfenster
-- umschaltbar
-- * v1.4.1: Begleiter (Pets) als eigene Plaketten, Manabalken im -- * v1.4.1: Begleiter (Pets) als eigene Plaketten, Manabalken im
-- Lebensbalken, frei waehlbare Button- und Zeilenabstaende, Testmodus -- Lebensbalken, frei waehlbare Button- und Zeilenabstaende, Testmodus
-- mit Geisterspielern, gespeicherte Plattenposition, Klassenfarben, -- mit Geisterspielern, gespeicherte Plattenposition, Klassenfarben,
@@ -59,6 +61,11 @@
-- Reiter Extras, Hooks geschuetzt, HealComm im Schlachtfeld und mit -- Reiter Extras, Hooks geschuetzt, HealComm im Schlachtfeld und mit
-- Grenzen, Merktabellen begrenzt. Aufgeraeumt: toter Code, doppelte -- Grenzen, Merktabellen begrenzt. Aufgeraeumt: toter Code, doppelte
-- Helfer und Vorgaben, veraltete Texte. -- Helfer und Vorgaben, veraltete Texte.
-- * v1.4.7.1: Nachlese zum Review. Buff-Icons nur fuer Buffs auf den
-- Buttons, Einzel- und Gruppenbuff teilen sich ein Icon; Sichtlinie
-- ueberlebt Spamklicks waehrend eines Casts; +Heilung nach der
-- Basis-Zauberzeit vor Talenten; Crit-Zeilen schliessen ihren Cast ab;
-- Gebet der Heilung zeigt die Vorhersage auf der ganzen Gruppe.
-- --
-- Ehre wem Ehre gebuehrt: Aufbau, Namensplaketten und Grundidee stammen -- Ehre wem Ehre gebuehrt: Aufbau, Namensplaketten und Grundidee stammen
-- aus dem Original. -- aus dem Original.
@@ -209,7 +216,7 @@ for k, v in pairs(FBHealBoxDefaults) do HealBox[k] = FBHealBox_CopyDefault(v); e
-- feuert ADDON_LOADED fuer uns. -- feuert ADDON_LOADED fuer uns.
FBADDON_NAME = "Heal Box Vanilla"; FBADDON_NAME = "Heal Box Vanilla";
FBADDON_FOLDER = "FBHealBox"; FBADDON_FOLDER = "FBHealBox";
HealBoxVersion = "|cFFFFFF00v1.4.7|r"; HealBoxVersion = "|cFFFFFF00v1.4.7.1|r";
-- ========================================================================== -- ==========================================================================
-- [ Lokalisierung / Localization ] -- [ Lokalisierung / Localization ]
@@ -277,7 +284,7 @@ FBLocale["enUS"] = {
AGGRO = "Mark who is attacked", AGGRO = "Mark who is attacked",
AGGRO_TIP = "Red border on the plate or cell of the member your hostile target is currently targeting. Checked five times a second.", AGGRO_TIP = "Red border on the plate or cell of the member your hostile target is currently targeting. Checked five times a second.",
BUFFICONS = "Buff icons left of the bar", BUFFICONS = "Buff icons left of the bar",
BUFFICONS_TIP = "Buffs with a duration that sit on your buttons (Fortitude, Divine Spirit, Fear Ward, ...) appear as small icons on the outer left side of the plate while they are up. As the time runs out, the icon turns black and white and darker from the top down, in 32 small steps: full colour when fresh, top half grey at half time, top three quarters grey at a quarter left. Exact on yourself, counted from your own cast on others; a buff cast by someone else stays fully coloured (time unknown). /fbp buffs shows what is tracked.", BUFFICONS_TIP = "Buffs with a duration that sit on your buttons (Fortitude, Divine Spirit, Fear Ward, ...) appear as small icons on the outer left side of the plate while they are up. A buff and its group version (Fortitude and Prayer of Fortitude, Blessing and Greater Blessing) share one icon. As the time runs out, the icon turns black and white and darker from the top down, in 32 small steps: full colour when fresh, top half grey at half time, top three quarters grey at a quarter left. Exact on yourself, counted from your own cast on others; a buff cast by someone else stays fully coloured (time unknown). /fbp buffs shows what is tracked.",
TIMERS = "HoT and shield timers", TIMERS = "HoT and shield timers",
TIMERS_TIP = "Each button shows the remaining seconds of your own HoT or shield of that spell on that unit: green for HoTs, blue for the shield, red for Weakened Soul after the shield. Buffs with a duration are shown as icons in the health bar instead (see Buff icons).", TIMERS_TIP = "Each button shows the remaining seconds of your own HoT or shield of that spell on that unit: green for HoTs, blue for the shield, red for Weakened Soul after the shield. Buffs with a duration are shown as icons in the health bar instead (see Buff icons).",
DBG_SMARTRANK = "Auto-downrank: %s -> %s (missing %d, expected %d)", DBG_SMARTRANK = "Auto-downrank: %s -> %s (missing %d, expected %d)",
@@ -361,6 +368,7 @@ FBLocale["enUS"] = {
DBG_CAST = "Cast %s on %s: %d", DBG_CAST = "Cast %s on %s: %d",
DBG_TICK = "Tick corrected: %s = %d", DBG_TICK = "Tick corrected: %s = %d",
DBG_HEAL = "Heal %s = %d (estimate now %d)", DBG_HEAL = "Heal %s = %d (estimate now %d)",
DBG_HEAL_CRIT = "Heal %s = %d critical, not learned",
DBG_ABSORB = "Absorb %d on %s (%d left)", DBG_ABSORB = "Absorb %d on %s (%d left)",
DBG_SMART_HOT = "Smart Healing skipped: %s is a heal over time", DBG_SMART_HOT = "Smart Healing skipped: %s is a heal over time",
DBG_SMART_SKIP = "Smart Healing skipped: %s is a group heal or has a cooldown", DBG_SMART_SKIP = "Smart Healing skipped: %s is a group heal or has a cooldown",
@@ -435,7 +443,7 @@ FBLocale["deDE"] = {
AGGRO = "Angegriffenen markieren", AGGRO = "Angegriffenen markieren",
AGGRO_TIP = "Roter Rahmen auf der Plakette oder Zelle des Mitglieds, das dein feindliches Ziel gerade im Ziel hat. Fuenfmal je Sekunde geprueft.", AGGRO_TIP = "Roter Rahmen auf der Plakette oder Zelle des Mitglieds, das dein feindliches Ziel gerade im Ziel hat. Fuenfmal je Sekunde geprueft.",
BUFFICONS = "Buff-Icons links am Balken", BUFFICONS = "Buff-Icons links am Balken",
BUFFICONS_TIP = "Buffs mit Laufzeit, die auf deinen Buttons liegen (Seelenstaerke, Goettlicher Willen, Furchtzauberschutz, ...), erscheinen als kleine Icons aussen links neben der Plakette, solange sie wirken. Mit ablaufender Zeit wird das Icon in 32 kleinen Stufen von oben nach unten schwarzweiss und dunkler: frisch ganz farbig, bei halber Zeit die obere Haelfte grau, bei einem Viertel Rest die oberen drei Viertel grau. Exakt bei dir selbst, ab deinem eigenen Cast bei anderen; ein fremd gewirkter Buff bleibt ganz farbig (Zeit unbekannt). /fbp buffs zeigt, was verfolgt wird.", BUFFICONS_TIP = "Buffs mit Laufzeit, die auf deinen Buttons liegen (Seelenstaerke, Goettlicher Willen, Furchtzauberschutz, ...), erscheinen als kleine Icons aussen links neben der Plakette, solange sie wirken. Ein Buff und seine Gruppenversion (Seelenstaerke und Gebet der Seelenstaerke, Segen und Grosser Segen) teilen sich ein Icon. Mit ablaufender Zeit wird das Icon in 32 kleinen Stufen von oben nach unten schwarzweiss und dunkler: frisch ganz farbig, bei halber Zeit die obere Haelfte grau, bei einem Viertel Rest die oberen drei Viertel grau. Exakt bei dir selbst, ab deinem eigenen Cast bei anderen; ein fremd gewirkter Buff bleibt ganz farbig (Zeit unbekannt). /fbp buffs zeigt, was verfolgt wird.",
TIMERS = "HoT- und Schild-Timer", TIMERS = "HoT- und Schild-Timer",
TIMERS_TIP = "Jeder Button zeigt die Restsekunden deines eigenen HoTs oder Schilds dieses Zaubers auf dieser Einheit: gruen fuer HoTs, blau fuer den Schild, rot fuer Geschwaechte Seele nach dem Schild. Buffs mit Laufzeit erscheinen stattdessen als Icons im Lebensbalken (siehe Buff-Icons).", TIMERS_TIP = "Jeder Button zeigt die Restsekunden deines eigenen HoTs oder Schilds dieses Zaubers auf dieser Einheit: gruen fuer HoTs, blau fuer den Schild, rot fuer Geschwaechte Seele nach dem Schild. Buffs mit Laufzeit erscheinen stattdessen als Icons im Lebensbalken (siehe Buff-Icons).",
DBG_SMARTRANK = "Abrangen: %s -> %s (fehlend %d, erwartet %d)", DBG_SMARTRANK = "Abrangen: %s -> %s (fehlend %d, erwartet %d)",
@@ -519,6 +527,7 @@ FBLocale["deDE"] = {
DBG_CAST = "Cast %s auf %s: %d", DBG_CAST = "Cast %s auf %s: %d",
DBG_TICK = "Tick korrigiert: %s = %d", DBG_TICK = "Tick korrigiert: %s = %d",
DBG_HEAL = "Heilung %s = %d (Schaetzung jetzt %d)", DBG_HEAL = "Heilung %s = %d (Schaetzung jetzt %d)",
DBG_HEAL_CRIT = "Heilung %s = %d kritisch, nicht gelernt",
DBG_ABSORB = "Absorb %d auf %s (Rest %d)", DBG_ABSORB = "Absorb %d auf %s (Rest %d)",
DBG_SMART_HOT = "Smart Healing uebersprungen: %s ist Heilung ueber Zeit", DBG_SMART_HOT = "Smart Healing uebersprungen: %s ist Heilung ueber Zeit",
DBG_SMART_SKIP = "Smart Healing uebersprungen: %s ist eine Gruppenheilung oder hat eine Abklingzeit", DBG_SMART_SKIP = "Smart Healing uebersprungen: %s ist eine Gruppenheilung oder hat eine Abklingzeit",
@@ -618,7 +627,7 @@ FBLocale["esES"] = {
AGGRO = "Marcar al atacado", AGGRO = "Marcar al atacado",
AGGRO_TIP = "Borde rojo en la placa o celda del miembro al que tu objetivo hostil está apuntando. Se comprueba cinco veces por segundo.", AGGRO_TIP = "Borde rojo en la placa o celda del miembro al que tu objetivo hostil está apuntando. Se comprueba cinco veces por segundo.",
BUFFICONS = "Iconos de beneficios (izq.)", BUFFICONS = "Iconos de beneficios (izq.)",
BUFFICONS_TIP = "Los beneficios con duración que están en tus botones (Entereza, Espíritu divino, Custodia contra el miedo, ...) aparecen como pequeños iconos en el lado exterior izquierdo de la placa mientras están activos. A medida que se agota el tiempo, el icono se vuelve blanco y negro y más oscuro de arriba abajo, en 32 pequeños pasos: todo en color al principio, mitad superior gris a mitad de tiempo, tres cuartos superiores grises cuando queda un cuarto. Exacto en ti mismo, contado desde tu propio lanzamiento en los demás; un beneficio lanzado por otro se queda en color (tiempo desconocido). /fbp buffs muestra qué se sigue.", BUFFICONS_TIP = "Los beneficios con duración que están en tus botones (Entereza, Espíritu divino, Custodia contra el miedo, ...) aparecen como pequeños iconos en el lado exterior izquierdo de la placa mientras están activos. Un beneficio y su versión de grupo (Entereza y Rezo de entereza, Bendición y Bendición superior) comparten un icono. A medida que se agota el tiempo, el icono se vuelve blanco y negro y más oscuro de arriba abajo, en 32 pequeños pasos: todo en color al principio, mitad superior gris a mitad de tiempo, tres cuartos superiores grises cuando queda un cuarto. Exacto en ti mismo, contado desde tu propio lanzamiento en los demás; un beneficio lanzado por otro se queda en color (tiempo desconocido). /fbp buffs muestra qué se sigue.",
TIMERS = "Temporizadores HoT y escudo", TIMERS = "Temporizadores HoT y escudo",
TIMERS_TIP = "Cada botón muestra los segundos restantes de tu propio HoT o escudo de ese hechizo en esa unidad: verde para HoT, azul para el escudo, rojo para Alma debilitada tras el escudo. Los beneficios con duración se muestran como iconos en la barra de vida (ver Iconos de beneficios).", TIMERS_TIP = "Cada botón muestra los segundos restantes de tu propio HoT o escudo de ese hechizo en esa unidad: verde para HoT, azul para el escudo, rojo para Alma debilitada tras el escudo. Los beneficios con duración se muestran como iconos en la barra de vida (ver Iconos de beneficios).",
DBG_SMARTRANK = "Reducción de rango: %s -> %s (faltan %d, esperado %d)", DBG_SMARTRANK = "Reducción de rango: %s -> %s (faltan %d, esperado %d)",
@@ -696,6 +705,7 @@ FBLocale["esES"] = {
DBG_CAST = "Lanzamiento %s sobre %s: %d", DBG_CAST = "Lanzamiento %s sobre %s: %d",
DBG_TICK = "Pulso corregido: %s = %d", DBG_TICK = "Pulso corregido: %s = %d",
DBG_HEAL = "Curación %s = %d (estimación ahora %d)", DBG_HEAL = "Curación %s = %d (estimación ahora %d)",
DBG_HEAL_CRIT = "Curación %s = %d crítica, no se aprende",
DBG_ABSORB = "Absorción %d en %s (quedan %d)", DBG_ABSORB = "Absorción %d en %s (quedan %d)",
DBG_SMART_HOT = "Smart Healing omitido: %s es curación con el tiempo", DBG_SMART_HOT = "Smart Healing omitido: %s es curación con el tiempo",
DBG_SMART_SKIP = "Smart Healing omitido: %s es una curación de grupo o tiene reutilización", DBG_SMART_SKIP = "Smart Healing omitido: %s es una curación de grupo o tiene reutilización",
@@ -767,7 +777,7 @@ FBLocale["frFR"] = {
AGGRO = "Marquer la cible attaquée", AGGRO = "Marquer la cible attaquée",
AGGRO_TIP = "Bordure rouge sur la plaque ou la cellule du membre que votre cible hostile vise actuellement. Vérifié cinq fois par seconde.", AGGRO_TIP = "Bordure rouge sur la plaque ou la cellule du membre que votre cible hostile vise actuellement. Vérifié cinq fois par seconde.",
BUFFICONS = "Icônes de buffs (gauche)", BUFFICONS = "Icônes de buffs (gauche)",
BUFFICONS_TIP = "Les buffs à durée présents sur vos boutons (Robustesse, Esprit divin, Gardien de peur, ...) apparaissent sous forme de petites icônes sur le côté extérieur gauche de la plaque tant qu'ils sont actifs. Au fur et à mesure que le temps s'écoule, l'icône passe en noir et blanc et s'assombrit de haut en bas, en 32 petites étapes : entièrement en couleur au début, moitié supérieure grise à mi-temps, trois quarts supérieurs gris quand il reste un quart. Exact sur vous-même, compté depuis votre propre lancement sur les autres ; un buff lancé par quelqu'un d'autre reste en couleur (temps inconnu). /fbp buffs montre ce qui est suivi.", BUFFICONS_TIP = "Les buffs à durée présents sur vos boutons (Robustesse, Esprit divin, Gardien de peur, ...) apparaissent sous forme de petites icônes sur le côté extérieur gauche de la plaque tant qu'ils sont actifs. Un buff et sa version de groupe (Robustesse et Prière de robustesse, Bénédiction et Bénédiction supérieure) partagent une icône. Au fur et à mesure que le temps s'écoule, l'icône passe en noir et blanc et s'assombrit de haut en bas, en 32 petites étapes : entièrement en couleur au début, moitié supérieure grise à mi-temps, trois quarts supérieurs gris quand il reste un quart. Exact sur vous-même, compté depuis votre propre lancement sur les autres ; un buff lancé par quelqu'un d'autre reste en couleur (temps inconnu). /fbp buffs montre ce qui est suivi.",
TIMERS = "Minuteurs HoT et bouclier", TIMERS = "Minuteurs HoT et bouclier",
TIMERS_TIP = "Chaque bouton affiche les secondes restantes de votre propre HoT ou bouclier de ce sort sur cette unité : vert pour les HoT, bleu pour le bouclier, rouge pour Âme affaiblie après le bouclier. Les buffs à durée sont affichés sous forme d'icônes dans la barre de vie (voir Icônes de buffs).", TIMERS_TIP = "Chaque bouton affiche les secondes restantes de votre propre HoT ou bouclier de ce sort sur cette unité : vert pour les HoT, bleu pour le bouclier, rouge pour Âme affaiblie après le bouclier. Les buffs à durée sont affichés sous forme d'icônes dans la barre de vie (voir Icônes de buffs).",
DBG_SMARTRANK = "Rang abaissé : %s -> %s (manque %d, attendu %d)", DBG_SMARTRANK = "Rang abaissé : %s -> %s (manque %d, attendu %d)",
@@ -845,6 +855,7 @@ FBLocale["frFR"] = {
DBG_CAST = "Lancement %s sur %s : %d", DBG_CAST = "Lancement %s sur %s : %d",
DBG_TICK = "Tick corrigé : %s = %d", DBG_TICK = "Tick corrigé : %s = %d",
DBG_HEAL = "Soin %s = %d (estimation maintenant %d)", DBG_HEAL = "Soin %s = %d (estimation maintenant %d)",
DBG_HEAL_CRIT = "Soin %s = %d critique, non appris",
DBG_ABSORB = "Absorption %d sur %s (reste %d)", DBG_ABSORB = "Absorption %d sur %s (reste %d)",
DBG_SMART_HOT = "Smart Healing ignoré : %s est un soin sur la durée", DBG_SMART_HOT = "Smart Healing ignoré : %s est un soin sur la durée",
DBG_SMART_SKIP = "Smart Healing ignoré : %s est un soin de groupe ou a un temps de recharge", DBG_SMART_SKIP = "Smart Healing ignoré : %s est un soin de groupe ou a un temps de recharge",
@@ -916,7 +927,7 @@ FBLocale["itIT"] = {
AGGRO = "Segnala chi è attaccato", AGGRO = "Segnala chi è attaccato",
AGGRO_TIP = "Bordo rosso sulla targhetta o sulla cella del membro che il tuo bersaglio ostile sta puntando. Controllato cinque volte al secondo.", AGGRO_TIP = "Bordo rosso sulla targhetta o sulla cella del membro che il tuo bersaglio ostile sta puntando. Controllato cinque volte al secondo.",
BUFFICONS = "Icone benefici (sinistra)", BUFFICONS = "Icone benefici (sinistra)",
BUFFICONS_TIP = "I benefici con durata presenti sui tuoi pulsanti (Tempra, Spirito Divino, Protezione dalla Paura, ...) compaiono come piccole icone sul lato esterno sinistro della targhetta finché sono attivi. Man mano che il tempo scorre, l'icona diventa in bianco e nero e più scura dall'alto verso il basso, in 32 piccoli passi: tutta a colori all'inizio, metà superiore grigia a metà tempo, i tre quarti superiori grigi quando ne resta un quarto. Esatta su te stesso, contata dal tuo lancio sugli altri; un beneficio lanciato da altri resta a colori (tempo sconosciuto). /fbp buffs mostra cosa viene seguito.", BUFFICONS_TIP = "I benefici con durata presenti sui tuoi pulsanti (Tempra, Spirito Divino, Protezione dalla Paura, ...) compaiono come piccole icone sul lato esterno sinistro della targhetta finché sono attivi. Un beneficio e la sua versione di gruppo (Tempra e Preghiera della Tempra, Benedizione e Benedizione Superiore) condividono un'icona. Man mano che il tempo scorre, l'icona diventa in bianco e nero e più scura dall'alto verso il basso, in 32 piccoli passi: tutta a colori all'inizio, metà superiore grigia a metà tempo, i tre quarti superiori grigi quando ne resta un quarto. Esatta su te stesso, contata dal tuo lancio sugli altri; un beneficio lanciato da altri resta a colori (tempo sconosciuto). /fbp buffs mostra cosa viene seguito.",
TIMERS = "Timer HoT e scudo", TIMERS = "Timer HoT e scudo",
TIMERS_TIP = "Ogni pulsante mostra i secondi rimanenti del tuo HoT o scudo di quell'incantesimo su quell'unità: verde per gli HoT, blu per lo scudo, rosso per Anima Indebolita dopo lo scudo. I benefici con durata vengono mostrati come icone nella barra della salute (vedi Icone dei benefici).", TIMERS_TIP = "Ogni pulsante mostra i secondi rimanenti del tuo HoT o scudo di quell'incantesimo su quell'unità: verde per gli HoT, blu per lo scudo, rosso per Anima Indebolita dopo lo scudo. I benefici con durata vengono mostrati come icone nella barra della salute (vedi Icone dei benefici).",
DBG_SMARTRANK = "Rango ridotto: %s -> %s (mancano %d, previsti %d)", DBG_SMARTRANK = "Rango ridotto: %s -> %s (mancano %d, previsti %d)",
@@ -994,6 +1005,7 @@ FBLocale["itIT"] = {
DBG_CAST = "Lancio %s su %s: %d", DBG_CAST = "Lancio %s su %s: %d",
DBG_TICK = "Tick corretto: %s = %d", DBG_TICK = "Tick corretto: %s = %d",
DBG_HEAL = "Cura %s = %d (stima ora %d)", DBG_HEAL = "Cura %s = %d (stima ora %d)",
DBG_HEAL_CRIT = "Cura %s = %d critica, non appresa",
DBG_ABSORB = "Assorbimento %d su %s (restano %d)", DBG_ABSORB = "Assorbimento %d su %s (restano %d)",
DBG_SMART_HOT = "Smart Healing saltato: %s è una cura nel tempo", DBG_SMART_HOT = "Smart Healing saltato: %s è una cura nel tempo",
DBG_SMART_SKIP = "Smart Healing saltato: %s è una cura di gruppo o ha un tempo di recupero", DBG_SMART_SKIP = "Smart Healing saltato: %s è una cura di gruppo o ha un tempo di recupero",
@@ -2879,10 +2891,15 @@ end
-- verbraucht ein kleiner Rang die volle Abklingzeit. Zauber mit Abklingzeit -- verbraucht ein kleiner Rang die volle Abklingzeit. Zauber mit Abklingzeit
-- erkennt FBHealBox_SmartSkipped zusaetzlich am Tooltip; die Liste deckt die -- erkennt FBHealBox_SmartSkipped zusaetzlich am Tooltip; die Liste deckt die
-- bekannten Faelle ab, auch wenn ein Tooltip keine Abklingzeit nennt. -- bekannten Faelle ab, auch wenn ein Tooltip keine Abklingzeit nennt.
-- Die deutschen Namen stehen dabei wie in FBHoTSpells und FBHealChains. Sie
-- greifen erst, wenn auch der Tooltip gelesen werden kann; der Leser kennt
-- bisher nur englische Tooltips (siehe README, Fehlersuche).
FBSmartSkip = { FBSmartSkip = {
["Prayer of Healing"] = true, ["Chain Heal"] = true, ["Binding Heal"] = true, ["Prayer of Healing"] = true, ["Chain Heal"] = true, ["Binding Heal"] = true,
["Circle of Healing"] = true, ["Prayer of Mending"] = true, ["Holy Nova"] = true, ["Circle of Healing"] = true, ["Prayer of Mending"] = true, ["Holy Nova"] = true,
["Holy Shock"] = true, ["Lay on Hands"] = true, ["Holy Shock"] = true, ["Lay on Hands"] = true,
["Gebet der Heilung"] = true, ["Kettenheilung"] = true, ["Heilige Nova"] = true,
["Heiliger Schock"] = true, ["Handauflegung"] = true,
}; };
-- Laesst Smart Healing diesen Zauber in Ruhe? -- Laesst Smart Healing diesen Zauber in Ruhe?
@@ -3662,17 +3679,26 @@ end
FBLOSFlags = {}; -- [Name] = Ablaufzeit der Markierung (Fallback-Weg) FBLOSFlags = {}; -- [Name] = Ablaufzeit der Markierung (Fallback-Weg)
-- Kandidat fuer eine Sichtlinien-Meldung: das Ziel des letzten Klicks auf -- Kandidaten fuer eine Sichtlinien-Meldung. Die Sicht prueft der Server beim
-- einen Button, bis dessen Cast entschieden ist. Die Sicht prueft der Server -- Start eines Casts und noch einmal an seinem Ende. Deshalb zwei Plaetze:
-- beim Start und noch einmal am Ende eines Casts; laeuft der geklickte Zauber --
-- an, gilt das Ziel deshalb bis Castende plus Spielraum. Abgeraeumt wird es, -- FBLOSClick: das Ziel des letzten Klicks auf einen Button, bis der Server
-- sobald der Cast durchgeht (SPELLCAST_STOP), unterbrochen wird, ein anderer -- ihn beantwortet hat { name, spell, untilT }. Laeuft der geklickte Zauber
-- Zauber anlaeuft oder eine andere Fehlermeldung den Klick beantwortet. Eine -- mit Zauberzeit an, wandert das Ziel nach FBLOSCast. Ein Sofortzauber, der
-- spaetere Meldung (etwa nach einem Angriffszauber von der Aktionsleiste) -- durchgeht (SPELLCAST_STOP), ein anderer Zauber, der anlaeuft, oder eine
-- gehoert dann nicht mehr zu diesem Ziel. -- Fehlermeldung, die den Klick beantwortet, raeumt ihn ab.
FBLOSCandidate = nil; -- FBLOSCast: das Ziel des laufenden Zaubers mit Zauberzeit bis Castende plus
FBLOSCandidateSpell = nil; -- Spielraum { name, endT, untilT, seq }. Ein weiterer Klick waehrend des
FBLOSCandidateUntil = 0; -- Casts legt nur einen neuen FBLOSClick an, und seine Antwort ("Another
-- action is in progress") laesst den laufenden Cast in Ruhe. Bis 1.4.7 gab
-- es nur einen Platz: Ein Spamklick ueberschrieb das Ziel, seine
-- Fehlermeldung loeschte es, und die Sichtlinien-Meldung am Castende ging
-- verloren.
--
-- Eine spaetere Meldung (etwa nach einem Angriffszauber von der Aktionsleiste)
-- gehoert zu keinem der beiden und markiert niemanden.
FBLOSClick = nil;
FBLOSCast = nil;
FBLOS_ERROR_WINDOW = 1.0; -- Sek. Spielraum fuer die Antwort des Servers FBLOS_ERROR_WINDOW = 1.0; -- Sek. Spielraum fuer die Antwort des Servers
function FBLOS_HasUnitXP() function FBLOS_HasUnitXP()
@@ -3742,23 +3768,83 @@ function FBUnitLOSBlocked(unit)
return false; return false;
end end
-- Fehlermeldung "nicht in Sichtlinie": Ziel des gerade geklickten Buttons -- Klick auf einen Button: Ziel als Kandidat merken. Ist der laufende Cast
-- markieren. Frueher fiel die Zuordnung auf das letzte Button-Ziel der -- laut Zauberzeit schon vorbei, gehoert ein neuer Klick nicht mehr zu ihm;
-- letzten zwei Sekunden, das freundliche Ziel oder den Spieler selbst -- dessen Spielraum fuer eine spaete Meldung endet dann hier.
-- zurueck. Ein Angriffszauber ohne Sicht kurz nach einem Heilklick markierte function FBLOS_NoteClick(targetName, spell, now)
-- so den geheilten Mitspieler, ohne Heilklick die eigene Raidzelle. if (FBLOSCast and now > FBLOSCast.endT) then FBLOSCast = nil; end
FBLOSClick = { name = targetName, spell = spell, untilT = now + FBLOS_ERROR_WINDOW };
end
-- SPELLCAST_START: Laeuft der geklickte Zauber an, wird sein Ziel zum
-- Kandidaten des laufenden Casts; ein anderer Zauber gehoert nicht zu dem
-- Klick. Ein neuer Castbeginn beendet in jedem Fall den vorigen Cast.
function FBLOS_CastStart(spell, castMs)
FBLOSCast = nil;
local c = FBLOSClick;
FBLOSClick = nil;
if (c and spell == c.spell) then
local endT = GetTime() + (castMs or 0) / 1000;
FBLOSCast = { name = c.name, endT = endT, untilT = endT + FBLOS_ERROR_WINDOW, seq = FBPredictClickSeq };
end
end
-- SPELLCAST_DELAYED: Pushback schiebt das Castende hinaus
function FBLOS_CastDelayed(ms)
local c = FBLOSCast;
if (not c) then return; end
local d = (tonumber(ms) or 0) / 1000;
c.endT = c.endT + d;
c.untilT = c.untilT + d;
end
-- SPELLCAST_STOP (der Cast ist durchgegangen, die Sicht war da) und
-- SPELLCAST_INTERRUPTED: beide Kandidaten sind beantwortet
function FBLOS_Reset()
FBLOSClick = nil;
FBLOSCast = nil;
end
-- Fehlermeldung "nicht in Sichtlinie": das Ziel markieren, dem die Meldung
-- gilt. Frueher fiel die Zuordnung auf das letzte Button-Ziel der letzten
-- zwei Sekunden, das freundliche Ziel oder den Spieler selbst zurueck. Ein
-- Angriffszauber ohne Sicht kurz nach einem Heilklick markierte so den
-- geheilten Mitspieler, ohne Heilklick die eigene Raidzelle.
function FBLOS_OnError(msg) function FBLOS_OnError(msg)
if (not msg) then return; end if (not msg) then return; end
-- Jede Fehlermeldung beantwortet den letzten Klick. War es eine andere local now = GetTime();
-- (Reichweite, Mana, "Another action is in progress"), kommt fuer ihn local cast = FBLOSCast;
-- keine Sichtlinien-Meldung mehr. if (cast and now > cast.untilT) then cast = nil; FBLOSCast = nil; end
local name = FBLOSCandidate; local click = FBLOSClick;
FBLOSCandidate = nil; if (click and now > click.untilT) then click = nil; FBLOSClick = nil; end
local losText = SPELL_FAILED_LINE_OF_SIGHT or "Target not in line of sight"; local losText = SPELL_FAILED_LINE_OF_SIGHT or "Target not in line of sight";
if (msg ~= losText) then return; end
if (not name) or (GetTime() > FBLOSCandidateUntil) then return; end if (msg ~= losText) then
if (name == UnitName("player")) then return; end -- Eine andere Meldung (Reichweite, Mana, "Another action is in
FBLOSFlags[name] = GetTime() + FBLOS_TIMEOUT; -- progress") beantwortet den juengsten Versuch. Gab es seit dem
-- Castbeginn keinen neuen, ist das der laufende Cast; sonst der neue
-- Versuch, und der laufende Cast bleibt Kandidat fuer sein Ende.
if (cast and FBPredictClickSeq == cast.seq) then
FBLOSCast = nil;
else
FBLOSClick = nil;
end
return;
end
-- Waehrend ein Zauber mit Zauberzeit laeuft, lehnt der Client jeden
-- neuen Versuch mit "Another action is in progress" ab; eine
-- Sichtlinien-Meldung in dieser Zeit gilt deshalb dem laufenden Cast.
local name = nil;
if (cast) then
name = cast.name;
FBLOSCast = nil;
elseif (click) then
name = click.name;
FBLOSClick = nil;
end
if (not name) or (name == UnitName("player")) then return; end
FBLOSFlags[name] = now + FBLOS_TIMEOUT;
FBHealBox_CheckLOSAll(); FBHealBox_CheckLOSAll();
end end
@@ -3931,11 +4017,56 @@ function FBHealBox_SpellCastSeconds(id)
return FBSpellCastCache[id] or nil; return FBSpellCastCache[id] or nil;
end end
-- Basis-Zauberzeit vor Talenten. Der Anteil am +Heilung-Bonus haengt in
-- Vanilla an der Zauberzeit, die der Zauber ohne Talente hat. Der Tooltip
-- zeigt aber die verkuerzte Zeit: Mit Goettlichem Furor steht bei Grosser
-- Heilung "2.5 sec cast", mit Naturalist bei Heilender Beruehrung 3.0 statt
-- 3.5 Sekunden, mit Verbesserter Welle der Heilung 2.5 statt 3.0. Bis 1.4.7
-- rechnete das Addon mit der Tooltipzeit und unterschaetzte den Bonus
-- ungelernter Raenge dieser vier Zauber um ein Siebtel. Hier stehen die
-- Zauber, deren Zauberzeit ein Talent verkuerzt: eine Zahl gilt fuer alle
-- Raenge, eine Liste je Rang (hinter ihrem Ende gilt der letzte Wert).
-- Alles andere nimmt weiter die Zeit aus dem Tooltip.
FBBaseCastTime = {
["Heal"] = 3.0,
["Greater Heal"] = 3.0,
["Healing Touch"] = { 1.5, 2.0, 2.5, 3.0, 3.5 },
["Healing Wave"] = { 1.5, 2.0, 2.5, 3.0 },
-- deutsche Zaubernamen, Sonderzeichen einmal Latin-1, einmal UTF-8
["Heilen"] = 3.0,
["Gro\223e Heilung"] = 3.0,
["Gro\195\159e Heilung"] = 3.0,
["Heilende Ber\252hrung"] = { 1.5, 2.0, 2.5, 3.0, 3.5 },
["Heilende Ber\195\188hrung"] = { 1.5, 2.0, 2.5, 3.0, 3.5 },
["Welle der Heilung"] = { 1.5, 2.0, 2.5, 3.0 },
};
-- Rangnummer aus "Rank 3" / "Rang 3", oder nil
function FBHealBox_RankNumber(rank)
if (not rank) then return nil; end
local _, _, n = string.find(rank, "(%d+)");
return n and tonumber(n);
end
-- Zauberzeit fuer die Gewichtung des Bonus: Basiswert aus FBBaseCastTime,
-- sonst die Zeit aus dem Tooltip (0 = Instant, nil = unlesbar)
function FBHealBox_BaseCastSeconds(spellName, rank, bookID)
local base = spellName and FBBaseCastTime[spellName];
if (type(base) == "number") then return base; end
if (type(base) == "table") then
local n = FBHealBox_RankNumber(rank);
local last = base[table.getn(base)];
if (n and base[n]) then return base[n]; end
return last;
end
return FBHealBox_SpellCastSeconds(bookID);
end
-- Anteil des +Heilung-Werts, der auf diesen Zauber entfaellt -- Anteil des +Heilung-Werts, der auf diesen Zauber entfaellt
function FBHealBox_HealBonusFor(bookID) function FBHealBox_HealBonusFor(bookID, spellName, rank)
local bonus = FBHealBox_HealingBonus(); local bonus = FBHealBox_HealingBonus();
if (bonus <= 0) then return 0; end if (bonus <= 0) then return 0; end
local secs = FBHealBox_SpellCastSeconds(bookID); local secs = FBHealBox_BaseCastSeconds(spellName, rank, bookID);
if (not secs) then secs = 2.5; end -- Zauberzeit unlesbar: mittlerer Wert if (not secs) then secs = 2.5; end -- Zauberzeit unlesbar: mittlerer Wert
if (secs <= 0) then secs = 1.5; end -- Instants rechnen mit 1,5 Sekunden if (secs <= 0) then secs = 1.5; end -- Instants rechnen mit 1,5 Sekunden
if (secs > 3.5) then secs = 3.5; end if (secs > 3.5) then secs = 3.5; end
@@ -3948,21 +4079,28 @@ end
-- API verraet die Lernstufe eines Rangs nicht, deshalb stehen hier die Raenge -- API verraet die Lernstufe eines Rangs nicht, deshalb stehen hier die Raenge
-- der Einzelzielheilungen, die unter Stufe 20 liegen (Stufe je Rang). Ohne -- der Einzelzielheilungen, die unter Stufe 20 liegen (Stufe je Rang). Ohne
-- diesen Abschlag hielt Smart Healing kleine Raenge mit viel +Heilung fuer -- diesen Abschlag hielt Smart Healing kleine Raenge mit viel +Heilung fuer
-- deutlich staerker, als sie sind, und rangte zu tief ab. -- deutlich staerker, als sie sind, und rangte zu tief ab. Die deutschen
-- Namen stehen dabei wie in FBHealChains.
FBRankLearnLevel = { FBRankLearnLevel = {
["Lesser Heal"] = { 1, 4, 10 }, ["Lesser Heal"] = { 1, 4, 10 },
["Heal"] = { 16 }, ["Heal"] = { 16 },
["Healing Touch"] = { 1, 8, 14 }, ["Healing Touch"] = { 1, 8, 14 },
["Healing Wave"] = { 1, 6, 12, 18 }, ["Healing Wave"] = { 1, 6, 12, 18 },
["Holy Light"] = { 1, 6, 14 }, ["Holy Light"] = { 1, 6, 14 },
["Geringes Heilen"] = { 1, 4, 10 },
["Heilen"] = { 16 },
["Heilende Ber\252hrung"] = { 1, 8, 14 },
["Heilende Ber\195\188hrung"] = { 1, 8, 14 },
["Welle der Heilung"] = { 1, 6, 12, 18 },
["Heiliges Licht"] = { 1, 6, 14 },
}; };
-- Faktor auf den Bonusanteil eines Rangs (1 = voller Anteil) -- Faktor auf den Bonusanteil eines Rangs (1 = voller Anteil)
function FBHealBox_LowRankFactor(spellName, rank) function FBHealBox_LowRankFactor(spellName, rank)
local levels = spellName and FBRankLearnLevel[spellName]; local levels = spellName and FBRankLearnLevel[spellName];
if (not levels) or (not rank) then return 1; end if (not levels) then return 1; end
local _, _, n = string.find(rank, "(%d+)"); local n = FBHealBox_RankNumber(rank);
local lvl = n and levels[tonumber(n)]; local lvl = n and levels[n];
if (not lvl) or (lvl >= 20) then return 1; end if (not lvl) or (lvl >= 20) then return 1; end
return 1 - (20 - lvl) * 0.0375; return 1 - (20 - lvl) * 0.0375;
end end
@@ -3973,7 +4111,7 @@ function FBPredict_ExpectedDirect(spellName, rank, info, bookID)
local learned = FBPredict_Remembered("direct", spellName, rank); local learned = FBPredict_Remembered("direct", spellName, rank);
if (learned) then return learned; end if (learned) then return learned; end
if (not info) or (not info.direct) then return nil; end if (not info) or (not info.direct) then return nil; end
return info.direct + FBHealBox_HealBonusFor(bookID) * FBHealBox_LowRankFactor(spellName, rank); return info.direct + FBHealBox_HealBonusFor(bookID, spellName, rank) * FBHealBox_LowRankFactor(spellName, rank);
end end
-- Reichweite eines Zaubers in Metern oder nil. Sie steht im Tooltip rechts -- Reichweite eines Zaubers in Metern oder nil. Sie steht im Tooltip rechts
@@ -5017,6 +5155,8 @@ end
function FBHealBoxButtonsChanged() function FBHealBoxButtonsChanged()
FBHealBox_InvalidateRangeSpell(); FBHealBox_InvalidateRangeSpell();
-- Buff-Icons folgen der Belegung (nur Buffs auf den Buttons)
FBPredict_BuildBuffOrder();
local rightOn = (HealBox.RightClick == 1); local rightOn = (HealBox.RightClick == 1);
for p = 1, FBSlotCount do for p = 1, FBSlotCount do
local unit = FBPartyUnit[p]; local unit = FBPartyUnit[p];
@@ -5850,12 +5990,15 @@ function FBPredict_GetSpellInfo(bookID, spellName)
end end
-- Nach jedem Zauberbuch-Scan: was koennen die gelernten Zauber ueberhaupt? -- Nach jedem Zauberbuch-Scan: was koennen die gelernten Zauber ueberhaupt?
FBPredictWatchBuffOrder = {}; -- Buff-Zauber alphabetisch (fuer die Icon-Reihenfolge) FBPredictBuffSpells = {}; -- alle verfolgten Buffs mit Laufzeit (Zaubernamen, sortiert)
FBPredictWatchBuffOrder = {}; -- Buff-Familien mit Icon, sortiert (FBPredict_BuildBuffOrder)
FBPredictBuffFamily = {}; -- [Familie] = { Variante, ... } (Einzel- und Gruppenbuff)
FBPredictBuffShown = {}; -- [Zaubername] = Familie, wenn die Familie ein Icon bekommt
function FBPredict_BuildWatch() function FBPredict_BuildWatch()
FBPredictWatch = {}; FBPredictWatch = {};
FBPredictInfo = {}; FBPredictInfo = {};
FBPredictWatchBuffOrder = {}; FBPredictBuffSpells = {};
for spellName, ranks in pairs(FBPlayerSpells) do for spellName, ranks in pairs(FBPlayerSpells) do
local top = ranks[table.getn(ranks)]; local top = ranks[table.getn(ranks)];
@@ -5875,10 +6018,63 @@ function FBPredict_BuildWatch()
buffSecs = info.buff and info.buff.duration, buffSecs = info.buff and info.buff.duration,
icon = top.icon, icon = top.icon,
}; };
if (info.buff) then table.insert(FBPredictWatchBuffOrder, spellName); end if (info.buff) then table.insert(FBPredictBuffSpells, spellName); end
end
end
table.sort(FBPredictBuffSpells);
FBPredict_BuildBuffOrder();
end
-- Zaubernamen auf den Buttons: linke Seite immer, die rechte nur mit
-- eingeschaltetem Rechtsklick-Zauber (sonst traegt der Button sie nicht).
-- Gezaehlt werden alle zehn Belegungen, denn die Mini-Buttons im Raid
-- nutzen Button 1 bis 4 unabhaengig davon, wie viele die Plaketten zeigen.
function FBPredict_ButtonSpells()
local set = {};
local sides = { HealBox.SpellChoice };
if (HealBox.RightClick == 1) then table.insert(sides, HealBox.SpellChoiceR); end
for _, tbl in ipairs(sides) do
if (tbl) then
for i = 1, FBMaxButtonCount do
local cast = tbl[i];
if (type(cast) == "string") then
local base = FBPredict_SplitCast(cast);
if (base) then set[base] = true; end
end
end
end
end
return set;
end
-- Welche Buffs bekommen ein Icon? Nur die, die auf einem Button liegen, so
-- wie es Tooltip und README immer gesagt haben. Ein Buff und seine
-- Gruppenversion (FBBuffAlternates) bilden eine Familie mit einem einzigen
-- Icon; es zeigt die Variante, die die Einheit gerade traegt. Bis 1.4.7
-- bekam jeder gelernte Buff ein Icon, und weil Einzel- und Gruppenbuff
-- einander ueber die Textur des anderen fanden, sah ein Priester mit
-- Seelenstaerke und Gebet der Seelenstaerke jedes Fortitude-Icon doppelt.
-- Laeuft nach jedem Zauberbuch-Scan und nach jeder Umbelegung der Buttons.
function FBPredict_BuildBuffOrder()
FBPredictWatchBuffOrder = {};
FBPredictBuffFamily = {};
FBPredictBuffShown = {};
local onButtons = FBPredict_ButtonSpells();
for _, spellName in ipairs(FBPredictBuffSpells) do
local alt = FBBuffAlternates[spellName];
local fam = spellName;
if (alt and alt < spellName) then fam = alt; end
if (onButtons[spellName] or (alt and onButtons[alt])) then
if (not FBPredictBuffFamily[fam]) then
FBPredictBuffFamily[fam] = {};
table.insert(FBPredictWatchBuffOrder, fam);
end
table.insert(FBPredictBuffFamily[fam], spellName);
FBPredictBuffShown[spellName] = fam;
end end
end end
table.sort(FBPredictWatchBuffOrder); table.sort(FBPredictWatchBuffOrder);
FBBuffIconsDirty = true;
end end
-- Buff per eigenem Button auf ein Ziel neu gewirkt, das ihn schon traegt: -- Buff per eigenem Button auf ein Ziel neu gewirkt, das ihn schon traegt:
@@ -6111,11 +6307,44 @@ function FBGetHoTHeal(unitName)
return sum; return sum;
end end
-- Trifft ein Direktcast diese Einheit? Gruppenheilungen (FBCommGroupHeal)
-- tragen ihre Ziele in d.targets, alle anderen haben genau d.target.
function FBPredict_CastHits(d, unitName)
if (not d) or (not unitName) then return false; end
if (d.targets) then return (d.targets[unitName] == true); end
return (d.target == unitName);
end
-- Alle Ziele eines Direktcasts zum Neuzeichnen vormerken
function FBPredict_MarkCastDirty(d)
if (not d) then return; end
if (d.targets) then
for n in pairs(d.targets) do FBHealBox_MarkDirty(n); end
end
FBHealBox_MarkDirty(d.target);
end
-- Ziele einer Gruppenheilung: man selbst und die Gruppenmitglieder in
-- Reichweite. Im Schlachtzug sind party1 bis party4 die eigene Untergruppe,
-- genau die heilt Gebet der Heilung. Dieselbe Liste geht ueber HealComm raus.
function FBPredict_GroupTargets()
local list = {};
local me = UnitName("player");
if (me) then table.insert(list, me); end
for i = 1, 4 do
local u = "party"..i;
if (UnitExists(u) and CheckInteractDistance(u, 4)) then
local n = UnitName(u);
if (n) then table.insert(list, n); end
end
end
return list;
end
-- Laufender Direktcast auf diese Einheit -- Laufender Direktcast auf diese Einheit
function FBGetDirectHeal(unitName) function FBGetDirectHeal(unitName)
local d = FBPredictDirect; local d = FBPredictDirect;
if (not d) or (not unitName) then return 0; end if (not FBPredict_CastHits(d, unitName)) then return 0; end
if (d.target ~= unitName) then return 0; end
if (GetTime() > d.finish) then return 0; end if (GetTime() > d.finish) then return 0; end
return d.amount; return d.amount;
end end
@@ -6149,9 +6378,7 @@ function FBPredict_NoteCast(castString, targetName)
FBPredictClickSeq = FBPredictClickSeq + 1; FBPredictClickSeq = FBPredictClickSeq + 1;
local base, rank = FBPredict_SplitCast(castString); local base, rank = FBPredict_SplitCast(castString);
-- Ziel fuer eine moegliche Sichtlinien-Meldung zu genau diesem Klick -- Ziel fuer eine moegliche Sichtlinien-Meldung zu genau diesem Klick
FBLOSCandidate = targetName; FBLOS_NoteClick(targetName, base, now);
FBLOSCandidateSpell = base;
FBLOSCandidateUntil = now + FBLOS_ERROR_WINDOW;
-- Ziel und Rang braucht nur ein verfolgter Zauber. Jeder Klick ersetzt -- Ziel und Rang braucht nur ein verfolgter Zauber. Jeder Klick ersetzt
-- aber den vorigen, auch einer auf einen anderen Zauber. -- aber den vorigen, auch einer auf einen anderen Zauber.
@@ -6325,8 +6552,20 @@ function FBPredict_CastStart(spellName, castMs)
local amount = FBPredict_ExpectedDirect(spellName, rank, info, bookID) or info.direct; local amount = FBPredict_ExpectedDirect(spellName, rank, info, bookID) or info.direct;
FBLOS_Clear(target); -- Cast laeuft an: Sichtlinie ist da FBLOS_Clear(target); -- Cast laeuft an: Sichtlinie ist da
-- Gruppenheilung: Die Vorhersage gilt fuer die ganze eigene Gruppe in
-- Reichweite, nicht fuer die eine angeklickte Einheit. Bis 1.4.7 stand
-- sie nur dort, im Raid also womoeglich in einer fremden Gruppe, obwohl
-- HealComm schon die richtige Liste bekam.
local groupList, targets = nil, nil;
if (FBCommGroupHeal[spellName]) then
groupList = FBPredict_GroupTargets();
targets = {};
for _, n in ipairs(groupList) do targets[n] = true; end
end
FBPredictDirect = { FBPredictDirect = {
target = target, target = target,
targets = targets,
spell = spellName, spell = spellName,
rank = rank, rank = rank,
rankKnown = rankKnown, rankKnown = rankKnown,
@@ -6340,9 +6579,9 @@ function FBPredict_CastStart(spellName, castMs)
end end
-- an andere Heiler funken (Puppeteer & Co. lesen mit) -- an andere Heiler funken (Puppeteer & Co. lesen mit)
FBComm_SendHealStart(spellName, FBPredictDirect.target, amount, castMs); FBComm_SendHealStart(spellName, FBPredictDirect.target, amount, castMs, groupList);
FBHealBox_MarkDirty(FBPredictDirect.target); FBPredict_MarkCastDirty(FBPredictDirect);
end end
-- Castende. Nach einem erfolgreichen Cast (SPELLCAST_STOP) bleibt die -- Castende. Nach einem erfolgreichen Cast (SPELLCAST_STOP) bleibt die
@@ -6357,7 +6596,7 @@ function FBPredict_CastEnd(success)
d.ended = GetTime(); d.ended = GetTime();
FBPredictLastDirect = d; FBPredictLastDirect = d;
end end
FBHealBox_MarkDirty(d.target); FBPredict_MarkCastDirty(d);
end end
-- [ Aura-Scan: Anwendung bestaetigen, Wegfall erkennen ] -------------------- -- [ Aura-Scan: Anwendung bestaetigen, Wegfall erkennen ] --------------------
@@ -6408,20 +6647,23 @@ function FBPredict_ScanUnit(unit)
-- Buff-Laufzeiten: beim Spieler exakt aus der Spielerbuff-API, bei -- Buff-Laufzeiten: beim Spieler exakt aus der Spielerbuff-API, bei
-- anderen nur eigene Casts (oben); weg ist weg. Praesenz merken, damit -- anderen nur eigene Casts (oben); weg ist weg. Praesenz merken, damit
-- die Plakette auch fremde Buffs (ohne Uhr) zeigen kann. -- die Plakette auch fremde Buffs (ohne Uhr) zeigen kann. Jede Variante
-- zaehlt nur mit ihrer eigenen Textur; Einzel- und Gruppenbuff fasst erst
-- die Anzeige zusammen (FBPredict_BuildBuffOrder). Bis 1.4.7 galt der
-- Einzelbuff auch ueber die Textur des Gruppenbuffs als vorhanden und
-- umgekehrt, beide Icons erschienen nebeneinander.
if (not FBBuffPresent[name]) then FBBuffPresent[name] = {}; end if (not FBBuffPresent[name]) then FBBuffPresent[name] = {}; end
for spellName, w in pairs(FBPredictWatch) do for _, spellName in ipairs(FBPredictBuffSpells) do
if (w.hasBuff) then local w = FBPredictWatch[spellName];
local present = textures[w.tex] or (w.altTex and textures[w.altTex]); local present = textures[w.tex];
if (FBBuffPresent[name][spellName] ~= present) then FBBuffIconsDirty = true; end if (FBBuffPresent[name][spellName] ~= present) then FBBuffIconsDirty = true; end
FBBuffPresent[name][spellName] = present; FBBuffPresent[name][spellName] = present;
local tracked = FBBuffTimers[name] and FBBuffTimers[name][spellName]; local tracked = FBBuffTimers[name] and FBBuffTimers[name][spellName];
if (present and unit == "player") then if (present and unit == "player") then
local left = FBPredict_PlayerBuffTimeLeft(w.tex) or (w.altTex and FBPredict_PlayerBuffTimeLeft(w.altTex)); local left = FBPredict_PlayerBuffTimeLeft(w.tex);
if (left) then FBPredict_StartBuff(name, spellName, left); end if (left) then FBPredict_StartBuff(name, spellName, left); end
elseif (tracked and not present) then elseif (tracked and not present) then
FBBuffTimers[name][spellName] = nil; FBBuffTimers[name][spellName] = nil;
end
end end
end end
@@ -6508,13 +6750,22 @@ function FBPredict_InitPatterns()
-- "You gain %d health from your %s." -- "You gain %d health from your %s."
FBPredictPatHoTSelf = FBPredict_ToPattern(PERIODICAURAHEALSELFSELF, true) FBPredictPatHoTSelf = FBPredict_ToPattern(PERIODICAURAHEALSELFSELF, true)
or "^You gain (%d+) health from your (.+)%.$"; or "^You gain (%d+) health from your (.+)%.$";
-- "Your %s heals %s for %d." (Crits haben eine eigene Formulierung und -- "Your %s heals %s for %d."
-- laufen deshalb ins Leere, genau so gewollt)
FBPredictPatHealOther = FBPredict_ToPattern(HEALEDSELFOTHER, true) FBPredictPatHealOther = FBPredict_ToPattern(HEALEDSELFOTHER, true)
or "^Your (.+) heals (.+) for (%d+)%.$"; or "^Your (.+) heals (.+) for (%d+)%.$";
-- "Your %s heals you for %d." -- "Your %s heals you for %d."
FBPredictPatHealSelf = FBPredict_ToPattern(HEALEDSELFSELF, true) FBPredictPatHealSelf = FBPredict_ToPattern(HEALEDSELFSELF, true)
or "^Your (.+) heals you for (%d+)%.$"; or "^Your (.+) heals you for (%d+)%.$";
-- Crits: "Your %s critically heals %s for %d." und "... heals you for %d."
-- Sie werden vor den normalen Mustern geprueft, denn die passen auf diese
-- Zeilen ebenfalls, nur mit "Flash Heal critically" als Zaubername. Bis
-- 1.4.7 lief ein Crit deshalb ins Leere, und der Cast wartete weiter auf
-- seine Zeile: Die naechste Heilung desselben Zaubers auf dasselbe Ziel,
-- etwa ein kleiner Rang von der Aktionsleiste, wurde ihm zugeschrieben.
FBPredictPatCritOther = FBPredict_ToPattern(HEALEDCRITSELFOTHER, true)
or "^Your (.+) critically heals (.+) for (%d+)%.$";
FBPredictPatCritSelf = FBPredict_ToPattern(HEALEDCRITSELFSELF, true)
or "^Your (.+) critically heals you for (%d+)%.$";
-- " (%d absorbed)" -- " (%d absorbed)"
FBPredictPatAbsorb = FBPredict_ToPattern(ABSORB_TRAILER, false) FBPredictPatAbsorb = FBPredict_ToPattern(ABSORB_TRAILER, false)
or "%((%d+) absorbed%)"; or "%((%d+) absorbed%)";
@@ -6524,6 +6775,7 @@ function FBPredict_InitPatterns()
-- beiden gleich. Ein aus der Fremd-Vorlage geschnittenes " gains " wuerde -- beiden gleich. Ein aus der Fremd-Vorlage geschnittenes " gains " wuerde
-- die eigenen Ticks aussperren. -- die eigenen Ticks aussperren.
FBPRED_WORD_HEAL = FBPredict_PlainPart(HEALEDSELFOTHER, " heals "); FBPRED_WORD_HEAL = FBPredict_PlainPart(HEALEDSELFOTHER, " heals ");
FBPRED_WORD_CRIT = FBPredict_PlainPart(HEALEDCRITSELFOTHER, " critically heals ");
FBPRED_WORD_TICK = FBPredict_PlainPart(PERIODICAURAHEALSELFSELF, " health from your "); FBPRED_WORD_TICK = FBPredict_PlainPart(PERIODICAURAHEALSELFSELF, " health from your ");
end end
@@ -6604,22 +6856,35 @@ function FBPredict_OnTick(unitName, amount, spellName)
FBHealBox_MarkDirty(unitName); FBHealBox_MarkDirty(unitName);
end end
function FBPredict_OnDirectHeal(spellName, targetName, amount) -- Eine Zeile hat ein Ziel des Casts erreicht. Einzelziel: der Cast ist
-- erledigt (true). Gruppenheilung: nur dieses Ziel faellt aus der Liste,
-- erledigt ist der Cast erst, wenn alle Ziele ihre Zeile hatten.
function FBPredict_CastLanded(d, targetName)
if (not d.targets) then return true; end
d.targets[targetName] = nil;
return (next(d.targets) == nil);
end
-- crit = true: Die Zeile schliesst ihren Cast ab wie jede andere, ihr Betrag
-- wird aber nicht gelernt (er wuerde die Vorhersage dauerhaft aufblasen).
function FBPredict_OnDirectHeal(spellName, targetName, amount, crit)
if (not spellName) or (not amount) then return; end if (not spellName) or (not amount) then return; end
FBLOS_Clear(targetName); FBLOS_Clear(targetName);
local w = FBPredictWatch[spellName]; local w = FBPredictWatch[spellName];
if (not w) then return; end -- faengt auch die Crit-Formulierung ab if (not w) then return; end
-- Zu welchem Cast gehoert die Zeile? Meist steht sie erst nach dem -- Zu welchem Cast gehoert die Zeile? Meist steht sie erst nach dem
-- Castende (SPELLCAST_STOP) im Combatlog, dann wartet der Cast noch als -- Castende (SPELLCAST_STOP) im Combatlog, dann wartet der Cast noch als
-- FBPredictLastDirect. Kommt sie vorher, ist es der laufende Cast. Bis -- FBPredictLastDirect. Kommt sie vorher, ist es der laufende Cast. Bis
-- 1.4.6 zaehlte nur der laufende, die Selbstkorrektur der -- 1.4.6 zaehlte nur der laufende, die Selbstkorrektur der
-- Direktheilungen griff deshalb kaum. -- Direktheilungen griff deshalb kaum. Beim Castbeginn wird
-- FBPredictLastDirect bewusst nicht geleert: Die Zeile des vorigen Casts
-- kommt oft erst, wenn der naechste schon laeuft.
local d = FBPredictLastDirect; local d = FBPredictLastDirect;
local fromLast = (d ~= nil) and d.spell == spellName and d.target == targetName local fromLast = (d ~= nil) and d.spell == spellName and FBPredict_CastHits(d, targetName)
and (GetTime() - d.ended) <= FBPREDICT_CLICK_TIME; and (GetTime() - d.ended) <= FBPREDICT_CLICK_TIME;
if (fromLast) then if (fromLast) then
FBPredictLastDirect = nil; if (FBPredict_CastLanded(d, targetName)) then FBPredictLastDirect = nil; end
else else
d = FBPredictDirect; d = FBPredictDirect;
if (d and d.spell ~= spellName) then d = nil; end if (d and d.spell ~= spellName) then d = nil; end
@@ -6628,7 +6893,12 @@ function FBPredict_OnDirectHeal(spellName, targetName, amount)
-- Nur lernen, wenn der gecastete Rang gesichert ist (Button-Cast). -- Nur lernen, wenn der gecastete Rang gesichert ist (Button-Cast).
-- Ein Downrank von der Aktionsleiste wuerde sonst dem Maximalrang -- Ein Downrank von der Aktionsleiste wuerde sonst dem Maximalrang
-- zugeschrieben und die Vorhersage nach unten ziehen. -- zugeschrieben und die Vorhersage nach unten ziehen.
if (d and d.rankKnown) then if (d and d.rankKnown and crit) then
if (FBPredictDebug) then
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00[FBP]|r "..format(FBT("DBG_HEAL_CRIT"),
spellName, amount));
end
elseif (d and d.rankKnown) then
local rank = d.rank; local rank = d.rank;
-- Heilungen streuen innerhalb einer Spanne -> sanft einpendeln -- Heilungen streuen innerhalb einer Spanne -> sanft einpendeln
local old = FBPredict_Remembered("direct", spellName, rank); local old = FBPredict_Remembered("direct", spellName, rank);
@@ -6642,8 +6912,10 @@ function FBPredict_OnDirectHeal(spellName, targetName, amount)
end end
end end
if (not fromLast) and FBPredictDirect and (FBPredictDirect.target == targetName) then -- Zeile vor dem Castende: Der laufende Cast hat dieses Ziel erreicht
FBPredictDirect = nil; local run = FBPredictDirect;
if (not fromLast) and run and run.spell == spellName and FBPredict_CastHits(run, targetName) then
if (FBPredict_CastLanded(run, targetName)) then FBPredictDirect = nil; end
end end
FBHealBox_MarkDirty(targetName); FBHealBox_MarkDirty(targetName);
end end
@@ -6702,7 +6974,20 @@ function FBPredict_ParseCombat(event, msg)
-- Argument true = einfache Suche ohne Musterlogik). Fremde Heilungen und -- Argument true = einfache Suche ohne Musterlogik). Fremde Heilungen und
-- fremde Ticks fallen so nach einem Vergleich raus statt nach vier -- fremde Ticks fallen so nach einem Vergleich raus statt nach vier
-- Musterlaeufen; im Raid sind das die meisten Meldungen. -- Musterlaeufen; im Raid sind das die meisten Meldungen.
if (string.find(msg, FBPRED_WORD_HEAL, 1, true)) then if (string.find(msg, FBPRED_WORD_HEAL, 1, true) or string.find(msg, FBPRED_WORD_CRIT, 1, true)) then
-- Crits zuerst, sonst halten die normalen Muster "Flash Heal
-- critically" fuer den Zaubernamen
local _, _, spellC, amtC = string.find(msg, FBPredictPatCritSelf);
if (spellC and amtC) then
FBPredict_OnDirectHeal(spellC, UnitName("player"), tonumber(amtC), true);
return;
end
local _, _, spellD, whoD, amtD = string.find(msg, FBPredictPatCritOther);
if (spellD and whoD and amtD) then
FBPredict_OnDirectHeal(spellD, whoD, tonumber(amtD), true);
return;
end
-- Direktheilung auf mich: "Your Flash Heal heals you for 1240." -- Direktheilung auf mich: "Your Flash Heal heals you for 1240."
-- Zuerst pruefen: Das Muster fuer andere passt auf diese Zeile -- Zuerst pruefen: Das Muster fuer andere passt auf diese Zeile
-- ebenfalls und lieferte bis 1.4.6 "you" als Ziel. Die eigene -- ebenfalls und lieferte bis 1.4.6 "you" als Ziel. Die eigene
@@ -6799,19 +7084,13 @@ FBPredictFrame:SetScript("OnEvent", function()
-- Der geklickte Zauber laeuft an: Die Sicht kann noch am Castende -- Der geklickte Zauber laeuft an: Die Sicht kann noch am Castende
-- fehlen, sein Ziel bleibt bis dahin Kandidat. Ein anderer Zauber -- fehlen, sein Ziel bleibt bis dahin Kandidat. Ein anderer Zauber
-- gehoert nicht zu diesem Klick. -- gehoert nicht zu diesem Klick.
if (FBLOSCandidate) then FBLOS_CastStart(arg1, tonumber(arg2));
if (arg1 == FBLOSCandidateSpell) then
FBLOSCandidateUntil = GetTime() + (tonumber(arg2) or 0) / 1000 + FBLOS_ERROR_WINDOW;
else
FBLOSCandidate = nil;
end
end
FBPredict_CastStart(arg1, tonumber(arg2)); FBPredict_CastStart(arg1, tonumber(arg2));
elseif (event == "SPELLCAST_STOP") then elseif (event == "SPELLCAST_STOP") then
-- Erfolgreich beendet: HealComm-Empfaenger lassen den Eintrag -- Erfolgreich beendet: HealComm-Empfaenger lassen den Eintrag
-- selbst auslaufen, hier wird bewusst kein Healstop gefunkt. -- selbst auslaufen, hier wird bewusst kein Healstop gefunkt.
FBLOSCandidate = nil; -- Cast ging durch, die Sicht war da FBLOS_Reset(); -- Cast ging durch, die Sicht war da
FBPredict_ClickDone(); FBPredict_ClickDone();
FBPredict_ConfirmBuffRefresh(); FBPredict_ConfirmBuffRefresh();
FBPredict_CastEnd(true); FBPredict_CastEnd(true);
@@ -6819,15 +7098,13 @@ FBPredictFrame:SetScript("OnEvent", function()
elseif (event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED") then elseif (event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED") then
-- Nach FAILED kann die Sichtlinien-Meldung noch folgen, nach einer -- Nach FAILED kann die Sichtlinien-Meldung noch folgen, nach einer
-- Unterbrechung (Bewegung, Unterbrechungszauber) nicht mehr -- Unterbrechung (Bewegung, Unterbrechungszauber) nicht mehr
if (event == "SPELLCAST_INTERRUPTED") then FBLOSCandidate = nil; end if (event == "SPELLCAST_INTERRUPTED") then FBLOS_Reset(); end
FBPredict_ClickFailed(event == "SPELLCAST_INTERRUPTED"); FBPredict_ClickFailed(event == "SPELLCAST_INTERRUPTED");
if (FBPredictDirect) then FBComm_SendHealStop(); end if (FBPredictDirect) then FBComm_SendHealStop(); end
FBPredict_CastEnd(false); FBPredict_CastEnd(false);
elseif (event == "SPELLCAST_DELAYED") then elseif (event == "SPELLCAST_DELAYED") then
if (FBLOSCandidate and arg1) then if (arg1) then FBLOS_CastDelayed(arg1); end
FBLOSCandidateUntil = FBLOSCandidateUntil + (tonumber(arg1) or 0) / 1000;
end
if (FBPredictCastUntil > 0 and arg1) then if (FBPredictCastUntil > 0 and arg1) then
FBPredictCastUntil = FBPredictCastUntil + (tonumber(arg1) or 0) / 1000; FBPredictCastUntil = FBPredictCastUntil + (tonumber(arg1) or 0) / 1000;
end end
@@ -6990,7 +7267,7 @@ function FBPredict_Expire()
end end
if (FBPredictDirect and now > FBPredictDirect.finish) then if (FBPredictDirect and now > FBPredictDirect.finish) then
FBHealBox_MarkDirty(FBPredictDirect.target); FBPredict_MarkCastDirty(FBPredictDirect);
FBPredictDirect = nil; FBPredictDirect = nil;
dirty = true; dirty = true;
end end
@@ -7109,7 +7386,9 @@ end
-- [ Senden ] --------------------------------------------------------------- -- [ Senden ] ---------------------------------------------------------------
function FBComm_SendHealStart(spellName, target, amount, castMs) -- groupList: Ziele einer Gruppenheilung, wie sie FBPredict_CastStart schon
-- fuer die eigene Vorhersage bestimmt hat (sonst hier ermittelt)
function FBComm_SendHealStart(spellName, target, amount, castMs, groupList)
if (not FBComm_Enabled()) or (not target) then return; end if (not FBComm_Enabled()) or (not target) then return; end
amount = math.floor(amount or 0); amount = math.floor(amount or 0);
@@ -7118,12 +7397,9 @@ function FBComm_SendHealStart(spellName, target, amount, castMs)
if (FBCommGroupHeal[spellName]) then if (FBCommGroupHeal[spellName]) then
-- Gruppenheilung: alle Ziele in Reichweite mitschicken -- Gruppenheilung: alle Ziele in Reichweite mitschicken
local names = UnitName("player").."/"; local names = "";
for i = 1, 4 do for _, n in ipairs(groupList or FBPredict_GroupTargets()) do
local u = "party"..i; names = names..n.."/";
if (UnitExists(u) and CheckInteractDistance(u, 4)) then
names = names..UnitName(u).."/";
end
end end
FBCommSentGroup = true; FBCommSentGroup = true;
FBComm_Send("GrpHeal/"..amount.."/"..castMs.."/"..names); FBComm_Send("GrpHeal/"..amount.."/"..castMs.."/"..names);
@@ -7355,20 +7631,22 @@ SlashCmdList["FBHEALPREDICT"] = function(msg)
if (msg == "buffs") then if (msg == "buffs") then
-- Diagnose: ueberwachte Buffs und ihr Zustand auf dem Spieler -- Diagnose: ueberwachte Buffs und ihr Zustand auf dem Spieler
-- icon=ja: der Buff (oder seine Gruppenversion) liegt auf einem
-- Button und bekommt ein Icon; family nennt die gemeinsame Familie
local me = UnitName("player"); local me = UnitName("player");
local any = false; for _, spellName in ipairs(FBPredictBuffSpells) do
for spellName, w in pairs(FBPredictWatch) do local w = FBPredictWatch[spellName];
if (w.hasBuff) then local present = FBBuffPresent[me] and FBBuffPresent[me][spellName];
any = true; local bt = FBBuffTimers[me] and FBBuffTimers[me][spellName];
local present = FBBuffPresent[me] and FBBuffPresent[me][spellName]; local left = "-";
local bt = FBBuffTimers[me] and FBBuffTimers[me][spellName]; if (bt) then left = math.floor(bt.expires - GetTime()).."s"; end
local left = "-"; local fam = FBPredictBuffShown[spellName];
if (bt) then left = math.floor(bt.expires - GetTime()).."s"; end local iconInfo = " icon=no";
DEFAULT_CHAT_FRAME:AddMessage("|cFF00FFFF[FBP]|r buff "..spellName.." ("..(w.buffSecs or 0).."s) tex="..tostring(w.tex) if (fam) then iconInfo = " icon=yes family="..fam; end
.." present="..tostring(present ~= nil and present or false).." left="..left); DEFAULT_CHAT_FRAME:AddMessage("|cFF00FFFF[FBP]|r buff "..spellName.." ("..(w.buffSecs or 0).."s) tex="..tostring(w.tex)
end .." present="..tostring(present ~= nil and present or false).." left="..left..iconInfo);
end end
if (not any) then DEFAULT_CHAT_FRAME:AddMessage("|cFF00FFFF[FBP]|r no buff spells on the buttons (assign e.g. Power Word: Fortitude)"); end if (table.getn(FBPredictWatchBuffOrder) == 0) then DEFAULT_CHAT_FRAME:AddMessage("|cFF00FFFF[FBP]|r no buff spells on the buttons (assign e.g. Power Word: Fortitude)"); end
if (FBHealBox1 and FBHealBox1.buffIcons) then if (FBHealBox1 and FBHealBox1.buffIcons) then
for k, ic in ipairs(FBHealBox1.buffIcons) do for k, ic in ipairs(FBHealBox1.buffIcons) do
DEFAULT_CHAT_FRAME:AddMessage("|cFF00FFFF[FBP]|r icon "..k..": shown="..tostring(ic:IsShown() and true or false) DEFAULT_CHAT_FRAME:AddMessage("|cFF00FFFF[FBP]|r icon "..k..": shown="..tostring(ic:IsShown() and true or false)
@@ -7679,9 +7957,30 @@ function FBHealBox_SetBuffIconProgress(ic, remaining)
end end
end end
-- Welche Variante einer Buff-Familie zeigt das Icon? Die Einheit traegt in
-- Vanilla hoechstens eine davon, denn Einzel- und Gruppenbuff ersetzen
-- einander. Manche Paare teilen sich aber die Textur (Mal der Wildnis und
-- Gabe der Wildnis), dann gelten beide als vorhanden. Den Ausschlag gibt die
-- Uhr: eine Variante mit laufender Uhr, deren Dauer die Restzeit fasst, und
-- davon die kuerzeste. Ohne Uhr die erste vorhandene. nil = keine.
function FBHealBox_PickBuffVariant(variants, present, timers, now)
local first, best, bestSecs = nil, nil, nil;
for _, v in ipairs(variants) do
if (present[v]) then
if (not first) then first = v; end
local bt = timers and timers[v];
local secs = FBPredictWatch[v].buffSecs or 0;
if (bt and bt.expires > now and secs + 1 >= bt.expires - now) then
if (not best) or (secs < bestSecs) then best = v; bestSecs = secs; end
end
end
end
return best or first;
end
-- Zu zeigende Buffs einer Einheit in die Arbeits-Tabelle out schreiben -- Zu zeigende Buffs einer Einheit in die Arbeits-Tabelle out schreiben
-- (wiederverwendet, keine neuen Tabellen je Tick). Liefert die Anzahl. -- (wiederverwendet, keine neuen Tabellen je Tick). Liefert die Anzahl.
-- Reihenfolge: FBPredictWatchBuffOrder (einmal in BuildWatch sortiert). -- Reihenfolge: FBPredictWatchBuffOrder, je Familie ein Icon.
function FBHealBox_BuffIconList(unitName, g, out) function FBHealBox_BuffIconList(unitName, g, out)
local n = 0; local n = 0;
if (g) then if (g) then
@@ -7697,8 +7996,10 @@ function FBHealBox_BuffIconList(unitName, g, out)
local present = FBBuffPresent[unitName]; local present = FBBuffPresent[unitName];
if (not present) then return 0; end if (not present) then return 0; end
local timers = FBBuffTimers[unitName]; local timers = FBBuffTimers[unitName];
for _, spellName in ipairs(FBPredictWatchBuffOrder) do local now = GetTime();
if (present[spellName]) then for _, fam in ipairs(FBPredictWatchBuffOrder) do
local spellName = FBHealBox_PickBuffVariant(FBPredictBuffFamily[fam], present, timers, now);
if (spellName) then
local w = FBPredictWatch[spellName]; local w = FBPredictWatch[spellName];
local bt = timers and timers[spellName]; local bt = timers and timers[spellName];
n = n + 1; n = n + 1;
@@ -7773,7 +8074,7 @@ function FBPredict_RefreshPlayerBuffs(elapsed)
local me = UnitName("player"); local me = UnitName("player");
if (not me) or (not FBBuffPresent[me]) then return; end if (not me) or (not FBBuffPresent[me]) then return; end
local timers = FBBuffTimers[me]; local timers = FBBuffTimers[me];
for _, spellName in ipairs(FBPredictWatchBuffOrder) do for _, spellName in ipairs(FBPredictBuffSpells) do
if (FBBuffPresent[me][spellName]) then if (FBBuffPresent[me][spellName]) then
local w = FBPredictWatch[spellName]; local w = FBPredictWatch[spellName];
local left = FBPredict_PlayerBuffTimeLeft(w.tex); local left = FBPredict_PlayerBuffTimeLeft(w.tex);
+2 -2
View File
@@ -1,9 +1,9 @@
## Interface: 11200 ## Interface: 11200
## Title: Heal Box Vanilla |cFFFFFF00v1.4.7|r ## Title: Heal Box Vanilla |cFFFFFF00v1.4.7.1|r
## Notes: Heal/raid frame addon like Healium, with quick-cast buttons, heal prediction, mana ticker and more. ## 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. ## 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.7 ## Version: 1.4.7.1
## SavedVariablesPerCharacter: HealBox ## SavedVariablesPerCharacter: HealBox
# SuperWoW ist eine Client-Erweiterung, kein Addon. OptionalDeps wirkt darauf # SuperWoW ist eine Client-Erweiterung, kein Addon. OptionalDeps wirkt darauf
# nicht; das Addon erkennt SuperWoW zur Laufzeit. # nicht; das Addon erkennt SuperWoW zur Laufzeit.
+57 -18
View File
@@ -19,9 +19,10 @@
-- gemessen noch geschaetzt: Der Combatlog zeigt den Schaden der anderen -- gemessen noch geschaetzt: Der Combatlog zeigt den Schaden der anderen
-- Raidgruppen nicht vollstaendig, jede Messung fiele dort zu niedrig aus. -- Raidgruppen nicht vollstaendig, jede Messung fiele dort zu niedrig aus.
-- --
-- Schaden je Rang: Mindestschaden aus dem Tooltip ("86 to 98 Holy damage"), -- Schaden je Rang: Mindestschaden aus dem Tooltip ("86 to 98 Holy damage",
-- angehoben um den Bonus, den der kleinste eigene Treffer ohne Crit und -- "for 11 Fire damage", "causes 13 Arcane damage"), angehoben um den Bonus,
-- Teilwiderstand beweist (FBDmg_MinDamage). Entscheidung: -- den der kleinste eigene normale Treffer beweist (FBDmg_MinDamage).
-- Entscheidung:
-- Mindestschaden >= Ziel-Leben (bei Prozent: Obergrenze) x (1 + Aufschlag). -- Mindestschaden >= Ziel-Leben (bei Prozent: Obergrenze) x (1 + Aufschlag).
-- ========================================================================== -- ==========================================================================
@@ -39,7 +40,9 @@ FBDamageDefaults = {
}; };
-- Angriffszauber je Klasse (englische Zaubernamen wie im ganzen Addon). -- Angriffszauber je Klasse (englische Zaubernamen wie im ganzen Addon).
-- Kanalisierte, reine DoTs und Flaechenzauber bleiben draussen. -- Kanalisierte, reine DoTs und Flaechenzauber bleiben draussen, ebenso
-- Schuesse mit Waffenschaden (Aimed Shot: "increases ranged damage by 70").
-- Deren Schaden steht nicht im Tooltip, sondern haengt an der Waffe.
FBDamageSpells = { FBDamageSpells = {
Priest = { "Smite", "Holy Fire", "Mind Blast" }, Priest = { "Smite", "Holy Fire", "Mind Blast" },
Druid = { "Wrath", "Starfire", "Moonfire" }, Druid = { "Wrath", "Starfire", "Moonfire" },
@@ -47,7 +50,7 @@ FBDamageSpells = {
Paladin = { "Holy Shock", "Hammer of Wrath", "Exorcism", "Holy Wrath" }, Paladin = { "Holy Shock", "Hammer of Wrath", "Exorcism", "Holy Wrath" },
Mage = { "Fireball", "Frostbolt", "Fire Blast", "Scorch", "Pyroblast" }, Mage = { "Fireball", "Frostbolt", "Fire Blast", "Scorch", "Pyroblast" },
Warlock = { "Shadow Bolt", "Searing Pain", "Immolate", "Soul Fire", "Conflagrate" }, Warlock = { "Shadow Bolt", "Searing Pain", "Immolate", "Soul Fire", "Conflagrate" },
Hunter = { "Arcane Shot", "Aimed Shot" }, Hunter = { "Arcane Shot" },
}; };
-- ========================================================================== -- ==========================================================================
@@ -215,6 +218,17 @@ end
-- [ Zauberbuch: Raenge und Schaden ] -- [ Zauberbuch: Raenge und Schaden ]
-- ========================================================================== -- ==========================================================================
-- Fester Schaden in der Form "causes 13 Arcane damage" (Arcane Shot) oder
-- "causing 25 Fire damage immediately" (Flame Shock). Beide Tooltips nennen
-- weder "X to Y" noch "for X"; bis 1.4.7 fanden sich deshalb fuer den Jaeger
-- gar keine Zauber, und Flame Shock fehlte beim Schamanen.
function FBDmg_FindCauseAmount(txt)
if (not txt) then return nil; end
local _, _, n = string.find(txt, "[Cc]aus%a*%s+(%d+)%s+%a+%s+[Dd]amage");
if (n) then return tonumber(n); end
return nil;
end
function FBDmg_ScanSpells() function FBDmg_ScanSpells()
FBDmgSpellRanks = {}; FBDmgSpellRanks = {};
local wanted = {}; local wanted = {};
@@ -227,7 +241,7 @@ function FBDmg_ScanSpells()
local txt = FBPredict_TooltipText(i) or ""; local txt = FBPredict_TooltipText(i) or "";
local lo, hi = FBPredict_FindAmountRange(txt); local lo, hi = FBPredict_FindAmountRange(txt);
if (not lo) then if (not lo) then
lo = FBPredict_FindAmountSingle(txt); lo = FBPredict_FindAmountSingle(txt) or FBDmg_FindCauseAmount(txt);
hi = lo; hi = lo;
end end
local isDamage = (string.find(txt, "[Dd]amage") or string.find(txt, "[Ss]chaden")) ~= nil; local isDamage = (string.find(txt, "[Dd]amage") or string.find(txt, "[Ss]chaden")) ~= nil;
@@ -242,15 +256,24 @@ function FBDmg_ScanSpells()
FBDmgSpellsDirty = false; FBDmgSpellsDirty = false;
end end
-- Mindestschaden eines Rangs. Gelernt wird der kleinste eigene Treffer ohne -- Mindestschaden eines Rangs. Gelernt wird der kleinste eigene normale
-- Crit und Teilwiderstand. Jeder Treffer liegt hoechstens beim Hoechstwert -- Treffer (ohne Crit, Teilwiderstand, Absorb, Block und Verwundbarkeits-
-- des Tooltips plus Bonus, der Bonus ist also mindestens Treffer minus -- bonus). Jeder Treffer liegt hoechstens beim Hoechstwert des Tooltips plus
-- Hoechstwert, und so viel liegt sicher auch auf dem Minimum. Der kleinste -- Bonus, der Bonus ist also mindestens Treffer minus Hoechstwert, und so viel
-- Treffer, weil Verstaerker wie Curse of Shadow oder Power Infusion nur -- liegt sicher auch auf dem Minimum. Der kleinste Treffer, weil Verstaerker
-- manche Treffer heben; ein einzelner verstaerkter Treffer darf den Wert -- wie Curse of Shadow oder Power Infusion nur manche Treffer heben; ein
-- nicht dauerhaft hochziehen. Bis 1.4.6 galt der kleinste Treffer selbst als -- einzelner verstaerkter Treffer darf den Wert nicht dauerhaft hochziehen.
-- Mindestschaden; nach einem einzigen hohen Wurf lag er ueber dem echten -- Bis 1.4.6 galt der kleinste Treffer selbst als Mindestschaden; nach einem
-- Minimum, und der gewaehlte Rang toetete nicht. -- einzigen hohen Wurf lag er ueber dem echten Minimum, und der gewaehlte Rang
-- toetete nicht.
--
-- Abgezogen wird die Tooltipspanne genau einmal: Sie steckt im kleinsten
-- Treffer nur als unbekannter Wurf. Mit vielen Treffern rutscht der kleinste
-- Treffer an Minimum plus Bonus heran, die Grenze liegt dann bis zu einer
-- Spanne unter dem echten Minimum (bei Frostbolt Rang 11 sind das 40 von
-- rund 750 Punkten). Das ist der Preis dafuer, dass die Grenze bewiesen ist;
-- der groesste Treffer gaebe den genaueren Wert, aber ein einziger
-- verstaerkter Treffer machte ihn falsch.
function FBDmg_MinDamage(spell, entry) function FBDmg_MinDamage(spell, entry)
local learned = HealBox.DmgMemory and HealBox.DmgMemory[spell.."|"..entry.rank]; local learned = HealBox.DmgMemory and HealBox.DmgMemory[spell.."|"..entry.rank];
if (not learned) then return entry.min; end if (not learned) then return entry.min; end
@@ -400,6 +423,22 @@ function FBDmg_ParseLine(msg)
return nil; return nil;
end end
-- Nur ein normaler Treffer taugt zum Lernen. Crit und Verwundbarkeitsbonus
-- (" (+12 vulnerability bonus)") heben den Treffer an; ein angehobener erster
-- Treffer koennte den gelernten Mindestschaden ueber das echte Minimum
-- schieben, dann toetete der gewaehlte Rang nicht. Teilwiderstand, Absorb
-- und Block senken ihn nur, das ist sicher, aber kein Wurf des Zaubers: die
-- Grenze fiele ohne Not. Bis 1.4.7 wurden nur Crit und Teilwiderstand
-- ausgeschlossen.
FBDmgHitMarks = { "crits", "resisted", "absorbed", "blocked", "vulnerability" };
function FBDmg_IsPlainHit(msg)
for _, mark in ipairs(FBDmgHitMarks) do
if (string.find(msg, mark, 1, true)) then return false; end
end
return true;
end
function FBDmg_OnCombatLog(msg) function FBDmg_OnCombatLog(msg)
-- Nur arbeiten, wenn Smart Damage an ist und es etwas zu lernen gibt -- Nur arbeiten, wenn Smart Damage an ist und es etwas zu lernen gibt
-- (Prozentziel wird vermessen oder ein eigener Cast wartet auf Treffer). -- (Prozentziel wird vermessen oder ein eigener Cast wartet auf Treffer).
@@ -408,11 +447,11 @@ function FBDmg_OnCombatLog(msg)
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);
-- Kleinsten Treffer lernen (siehe FBDmg_MinDamage): eigener Zauber, kein -- Kleinsten Treffer lernen (siehe FBDmg_MinDamage): eigener Zauber, nur
-- Crit, kein Teilwiderstand -- ein normaler Treffer
if (who == "self" and spell and FBDmgLastCast and FBDmgLastCast.spell == spell if (who == "self" and spell and FBDmgLastCast and FBDmgLastCast.spell == spell
and (GetTime() - FBDmgLastCast.t) <= FBDMG_LASTCAST_SEC and (GetTime() - FBDmgLastCast.t) <= FBDMG_LASTCAST_SEC
and not string.find(msg, "crits") and not string.find(msg, "resisted")) then and FBDmg_IsPlainHit(msg)) then
local key = spell.."|"..(FBDmgLastCast.rank or ""); local key = spell.."|"..(FBDmgLastCast.rank or "");
if (not HealBox.DmgMemory) then HealBox.DmgMemory = {}; end if (not HealBox.DmgMemory) then HealBox.DmgMemory = {}; end
local old = HealBox.DmgMemory[key]; local old = HealBox.DmgMemory[key];
+13
View File
@@ -1261,6 +1261,10 @@ FBRaidEventFrame:RegisterEvent("UNIT_MANA");
FBRaidEventFrame:RegisterEvent("UNIT_MAXMANA"); FBRaidEventFrame:RegisterEvent("UNIT_MAXMANA");
FBRaidEventFrame:RegisterEvent("UNIT_DISPLAYPOWER"); FBRaidEventFrame:RegisterEvent("UNIT_DISPLAYPOWER");
FBRaidEventFrame:RegisterEvent("UNIT_AURA"); FBRaidEventFrame:RegisterEvent("UNIT_AURA");
-- Namen, die beim Laden noch als Unbekannt kamen, reichen nach. Bis 1.4.7
-- blieb eine solche Zelle bis zum naechsten Rosterwechsel falsch beschriftet
-- und war ueber ihren Namen (FBRaidNameCell, Vorhersage) nicht zu finden.
FBRaidEventFrame:RegisterEvent("UNIT_NAME_UPDATE");
-- Wut, Energie und Fokus (nur mit "Wut, Energie, Fokus zeigen" ausgewertet) -- Wut, Energie und Fokus (nur mit "Wut, Energie, Fokus zeigen" ausgewertet)
for ev in pairs(FBPowerEvents) do FBRaidEventFrame:RegisterEvent(ev); end for ev in pairs(FBPowerEvents) do FBRaidEventFrame:RegisterEvent(ev); end
@@ -1275,6 +1279,15 @@ FBRaidEventFrame:SetScript("OnEvent", function()
return; return;
end end
if (FBRaidTestMode > 0) then return; end if (FBRaidTestMode > 0) then return; end
if (event == "UNIT_NAME_UPDATE") then
-- Nur Raid-Einheiten, und nur wenn sich der Name wirklich geaendert
-- hat; der Umbau laeuft wie bei Rosterwechseln im naechsten Frame
if (arg1 and string.find(arg1, "^raid%d") and FBRaid_IsActive()) then
local cell = FBRaidUnitCell[arg1];
if (not cell) or (cell.name ~= UnitName(arg1)) then FBRaidRosterDirty = true; end
end
return;
end
if (FBPowerEvents[event] and HealBox.PowerBar ~= 1) then return; end if (FBPowerEvents[event] and HealBox.PowerBar ~= 1) then return; end
local c = FBRaidUnitCell[arg1]; local c = FBRaidUnitCell[arg1];
if (not c) then return; end if (not c) then return; end