Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Effective healing and absorptions #192

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions config/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,36 @@ <h3 data-locale="config.format.significant_digit._">
min="0" step="1" max="2" value="0" />
</span>
</p>
<p class="control">
<label for="input-format-significant_digit-effective-pct"
data-locale="config.format.significant_digit.effective_pct">
% of total effective healing
</label>
<span class="input-group">
<label class="input-value"
for="input-format-significant_digit-effective_pct">
0
</label>
<input type="range" id="input-format-significant_digit-effective_pct"
data-config-key="format.significant_digit.effective_pct"
min="0" step="1" max="2" value="0" />
</span>
</p>
<p class="control">
<label for="input-format-significant_digit-absorb_pct"
data-locale="config.format.significant_digit.absorb_pct">
% of total absorb healing
</label>
<span class="input-group">
<label class="input-value"
for="input-format-significant_digit-absorb_pct">
0
</label>
<input type="range" id="input-format-significant_digit-absorb_pct"
data-config-key="format.significant_digit.absorb_pct"
min="0" step="1" max="2" value="0" />
</span>
</p>
</article>
<article>
<h3 data-locale="ui.config.general.footer-visibility._">
Expand Down
89 changes: 47 additions & 42 deletions overlay/css/table.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,48 +18,53 @@
flex-grow: 10;
flex-basis: 0%;
}
.flex-column-i-class { width: var(--_i-class)rem; } /* replace-hack only */
.flex-column-i-rank { width: var(--_i-rank)rem; }
.flex-column-i-owner { width: var(--_i-owner)rem; }
.flex-column-i-name { width: var(--_i-name)rem; }
.flex-column-deal-total { width: var(--_deal-total)rem; }
.flex-column-deal-per-second { width: var(--_deal-per_second)rem; }
.flex-column-deal-pct { width: var(--_deal-pct)rem; }
.flex-column-deal-failure { width: var(--_deal-failure)rem; }
.flex-column-deal-accuracy { width: var(--_deal-accuracy)rem; }
.flex-column-deal-swing { width: var(--_deal-swing)rem; }
.flex-column-deal-miss { width: var(--_deal-miss)rem; }
.flex-column-deal-hitfail { width: var(--_deal-hitfail)rem; }
.flex-column-deal-critical { width: var(--_deal-critical)rem; }
.flex-column-deal-direct { width: var(--_deal-direct)rem; }
.flex-column-deal-crit-direct { width: var(--_deal-crit_direct)rem; }
.flex-column-deal-crittypes { width: var(--_deal-crittypes)rem; }
.flex-column-deal-critpcts { width: var(--_deal-critpcts)rem; }
.flex-column-deal-max { width: var(--_deal-max)rem; }
.flex-column-deal-maxhit { width: var(--_deal-maxhit)rem; }
.flex-column-deal-maxskill { width: var(--_deal-maxskill)rem; }
.flex-column-deal-last10 { width: var(--_deal-last10)rem; }
.flex-column-deal-last30 { width: var(--_deal-last30)rem; }
.flex-column-deal-last60 { width: var(--_deal-last60)rem; }
.flex-column-deal-last180 { width: var(--_deal-last180)rem; }
.flex-column-tank-damage { width: var(--_tank-damage)rem; }
.flex-column-tank-parry { width: var(--_tank-parry)rem; }
.flex-column-tank-heal { width: var(--_tank-heal)rem; }
.flex-column-tank-block { width: var(--_tank-block)rem; }
.flex-column-tank-threat-delta { width: var(--_tank-threat_delta)rem; }
.flex-column-heal-per-second { width: var(--_heal-per_second)rem; }
.flex-column-heal-pct { width: var(--_heal-pct)rem; }
.flex-column-heal-total { width: var(--_heal-total)rem; }
.flex-column-heal-swing { width: var(--_heal-swing)rem; }
.flex-column-heal-over { width: var(--_heal-over)rem; }
.flex-column-heal-cure { width: var(--_heal-cure)rem; }
.flex-column-heal-critical { width: var(--_heal-critical)rem; }
.flex-column-heal-max { width: var(--_heal-max)rem; }
.flex-column-heal-maxhit { width: var(--_heal-maxhit)rem; }
.flex-column-heal-maxskill { width: var(--_heal-maxskill)rem; }
.flex-column-etc-powerdrain { width: var(--_etc-powerdrain)rem; }
.flex-column-etc-death { width: var(--_etc-death)rem; }
.flex-column-etc-powerheal { width: var(--_etc-powerheal)rem; }
.flex-column-i-class { width: var(--_i-class)rem; } /* replace-hack only */
.flex-column-i-rank { width: var(--_i-rank)rem; }
.flex-column-i-owner { width: var(--_i-owner)rem; }
.flex-column-i-name { width: var(--_i-name)rem; }
.flex-column-deal-total { width: var(--_deal-total)rem; }
.flex-column-deal-per-second { width: var(--_deal-per_second)rem; }
.flex-column-deal-pct { width: var(--_deal-pct)rem; }
.flex-column-deal-failure { width: var(--_deal-failure)rem; }
.flex-column-deal-accuracy { width: var(--_deal-accuracy)rem; }
.flex-column-deal-swing { width: var(--_deal-swing)rem; }
.flex-column-deal-miss { width: var(--_deal-miss)rem; }
.flex-column-deal-hitfail { width: var(--_deal-hitfail)rem; }
.flex-column-deal-critical { width: var(--_deal-critical)rem; }
.flex-column-deal-direct { width: var(--_deal-direct)rem; }
.flex-column-deal-crit-direct { width: var(--_deal-crit_direct)rem; }
.flex-column-deal-crittypes { width: var(--_deal-crittypes)rem; }
.flex-column-deal-critpcts { width: var(--_deal-critpcts)rem; }
.flex-column-deal-max { width: var(--_deal-max)rem; }
.flex-column-deal-maxhit { width: var(--_deal-maxhit)rem; }
.flex-column-deal-maxskill { width: var(--_deal-maxskill)rem; }
.flex-column-deal-last10 { width: var(--_deal-last10)rem; }
.flex-column-deal-last30 { width: var(--_deal-last30)rem; }
.flex-column-deal-last60 { width: var(--_deal-last60)rem; }
.flex-column-deal-last180 { width: var(--_deal-last180)rem; }
.flex-column-tank-damage { width: var(--_tank-damage)rem; }
.flex-column-tank-parry { width: var(--_tank-parry)rem; }
.flex-column-tank-heal { width: var(--_tank-heal)rem; }
.flex-column-tank-block { width: var(--_tank-block)rem; }
.flex-column-tank-threat-delta { width: var(--_tank-threat_delta)rem; }
.flex-column-heal-per-second { width: var(--_heal-per_second)rem; }
.flex-column-heal-pct { width: var(--_heal-pct)rem; }
.flex-column-heal-total { width: var(--_heal-total)rem; }
.flex-column-heal-effective-per-second { width: var(--_heal-effective_per_second)rem; }
.flex-column-heal-effective-pct { width: var(--_heal-effective_pct)rem; }
.flex-column-heal-effective-total { width: var(--_heal-effective_total)rem; }
.flex-column-heal-absorb-pct { width: var(--_heal-absorb_pct)rem; }
.flex-column-heal-absorb-total { width: var(--_heal-absorb_total)rem; }
.flex-column-heal-swing { width: var(--_heal-swing)rem; }
.flex-column-heal-over { width: var(--_heal-over)rem; }
.flex-column-heal-cure { width: var(--_heal-cure)rem; }
.flex-column-heal-critical { width: var(--_heal-critical)rem; }
.flex-column-heal-max { width: var(--_heal-max)rem; }
.flex-column-heal-maxhit { width: var(--_heal-maxhit)rem; }
.flex-column-heal-maxskill { width: var(--_heal-maxskill)rem; }
.flex-column-etc-powerdrain { width: var(--_etc-powerdrain)rem; }
.flex-column-etc-death { width: var(--_etc-death)rem; }
.flex-column-etc-powerheal { width: var(--_etc-powerheal)rem; }

.pet-merged .flex-column-i-owner {
display: none !important;
Expand Down
43 changes: 35 additions & 8 deletions overlay/lib/listen.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@

update(data) {
this.isActive = data.isActive
this.calculateHealingValues(data)
this.header = data.Encounter
this.data = toArray(data.Combatant)
this.calculateMax(data.Combatant)
}

get(sort, merged) {
Expand Down Expand Up @@ -93,21 +93,48 @@
}

sort(key, target) {
let d = (('+-'.indexOf(key[0]))+1 || 1) * 2 - 3
let k = SORTABLE[key.substr('+-'.indexOf(key[0]) >= 0)]
;(target || this.data).sort((a, b) => (pFloat(a[k]) - pFloat(b[k])) * d)
let order = (('+-'.indexOf(key[0]))+1 || 1) * 2 - 3 // 1:asc, -1:desc
let sort_by = SORTABLE[key.substr('+-'.indexOf(key[0]) >= 0)]

if (typeof sort_by == "string") {
(target || this.data).sort((a, b) => (pFloat(a[sort_by]) - pFloat(b[sort_by])) * order)
} else if (typeof sort_by == "function") {
(target || this.data).sort((a, b) => (sort_by(a) - sort_by(b)) * order)
}
if(target) return target
}

// Calculate additional healing values and store them in the Combatant or Encounter
calculateHealingValues(data) {
let rhealing_effective = 0;
let rabsorb_healing = 0;
for (let i in data.Combatant) {
let player = data.Combatant[i];
let effective = parseInt(player.healed) - parseInt(player.overHeal);
// Inject the calculated effective healing into the player data, while we're at it
player.effective_healing = effective;
Copy link
Owner

@hibiyasleep hibiyasleep Mar 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO actually, the term 'effective healing' should not be a main metric to compare between healers within party. I'm not saying including absorbs & shields is bad, but excluding overheals are.

Let's imagine a full party with WHM & AST that both healers are never talked & both doing almost max effort they can normally do.
On next raidwide AoE, someone needed to heal this or we all gonna die. WHM prepares Plenary Indulgence + Afflatus Rapture both has no casting, AST prepares Stellar Explosion + Horoscope Helios also almost instant both are.
So the AoE came, that two healers activates their skills in same time. who achieved lower Overheals, who made more Effective Heals? if WHM's 100ms faster, can we blame AST as 'they did nothing'? no.
(I made WHM & AST as example as they're what I main, but if we think of SGE, my friends are shouting 'I should do a heal to fill my MP', despite I can't know their truth as I never been SGE.)

Overheal is the problem of Negotiation in most cases, it doesn't mean they're underskilled.
and as Effective Healing excludes Overheal completely, so we need to think more on this topic.

ps: Ikegami already adds their shield on their gauge in naïve and implictly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the overhealing generated for this kind of specific moments would even out over the course of a fight, but I haven't healed in raids so I might be wrong.

I agree that overhealing is a normal part of healing, and you can't compare healers based this metric. Likewise,hps alone is not a good indicator of healer performance.
I think it's up to the user to understand and combine several metrics to evaluate performance (And in that case, more available metrics is better).
Plus, we already provide the overhealing %, so it's just a quick math for the user to calculate effective healing himself.

I wouldn't add it to the default columns, but I don't see the harm in having them available if someone wants them.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Providing effective/absorbed value is absolutely does not matter, and it should be, but using it as single main metric (so removing instead of adding overheal) would be problematic as it hides before decision.

Actual solution should be create 'unified healing gauge' as like other overlays do, but this is quite a headache for current codebase...
image
We would just add this for now & replace with this somewhen soon as possible.

rhealing_effective += effective;
rabsorb_healing += parseInt(player.absorbHeal)
}
// Inject into the Encounter data for later use
data.Encounter.rhealing_effective = rhealing_effective
data.Encounter.rabsorb_healing = rabsorb_healing
// Calculate the pct now that we have the total values
for (let i in data.Combatant) {
let player = data.Combatant[i];
player.effective_pct = player.effective_healing / rhealing_effective * 100
player.absorb_pct = player.absorbHeal / rabsorb_healing * 100
}
}

calculateMax(combatant) {
let max = {}

for(let k in SORTABLE) {
let v = SORTABLE[k]
max[k] = Math.max.apply(
Math, Object.keys(combatant).map(_ => combatant[_][v])
)
if (typeof v == "string")
max[k] = Math.max.apply(Math, Object.keys(combatant).map(_ => combatant[_][v]))
else if (typeof v == "function")
max[k] = Math.max.apply(Math, combatant.map(_ => v(_)))
}

return max
Expand Down
9 changes: 8 additions & 1 deletion share/lang/cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@
"damage": "Damage",
"hps": "HPS",
"accuracy": "命中率",
"critical": "伤害和治疗百分比"
"critical": "伤害和治疗百分比",
"effective_pct": "% of total effective healing",
"absorb_pct": "% of total absorb healing"
},
"thousands_separator": {
"_": "Thousands separator",
Expand Down Expand Up @@ -284,6 +286,11 @@
"per_second": ["HPS", "每秒治疗量"],
"pct": ["H%", "治疗比率%"],
"total": ["总治疗", "总治疗量"],
"effective_per_second" : ["Eff. HPS", "per Second effective"],
"effective_pct" : ["Eff. H%", "Effective healing %"],
"effective_total": ["Eff. H.", "Effective"],
"absorb_pct": ["Abs. %", "Absorption %"],
"absorb_total": ["Abs. H.", "Absorption"],
"over": ["过量%", "过量治疗"],
"swing": ["次数", "治疗次数"],
"critical": ["H暴%", "治疗暴击率%"],
Expand Down
9 changes: 8 additions & 1 deletion share/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@
"damage": "Damage",
"hps": "HPS",
"accuracy": "Fehlschlag (Verfehlt / Angriffe)",
"critical": "% der kritischen Treffer/Heilungen"
"critical": "% der kritischen Treffer/Heilungen",
"effective_pct": "% of total effective healing",
"absorb_pct": "% of total absorb healing"
},
"thousands_separator": {
"_": "Thousands separator",
Expand Down Expand Up @@ -284,6 +286,11 @@
"per_second": ["HPS", "Heilung pro Sekunde"],
"pct": ["H%", "% der Gesamtheilung"],
"total": ["H.Ges.", "Gesamtheilung"],
"effective_per_second" : ["Eff. HPS", "per Second effective"],
"effective_pct" : ["Eff. H%", "Effective healing %"],
"effective_total": ["Eff. H.", "Effective"],
"absorb_pct": ["Abs. %", "Absorption %"],
"absorb_total": ["Abs. H.", "Absorption"],
"over": ["OvrH", "Overheal"],
"swing": ["Heilungen", "Anzahl der Heilungen"],
"critical": ["HCrit", "Kritischer Trefferchance"],
Expand Down
9 changes: 8 additions & 1 deletion share/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@
"damage": "Damage",
"hps": "HPS",
"accuracy": "Failure (Miss / Swing)",
"critical": "% of Critical hits/heals"
"critical": "% of Critical hits/heals",
"effective_pct": "% of total effective healing",
"absorb_pct": "% of total absorb healing"
},
"thousands_separator": {
"_": "Thousands separator",
Expand Down Expand Up @@ -284,6 +286,11 @@
"per_second": ["HPS", "per Second"],
"pct": ["H%", "Heal %"],
"total": ["H.Tot", "Total"],
"effective_per_second" : ["Eff. HPS", "per Second effective"],
"effective_pct" : ["Eff. H%", "Effective healing %"],
"effective_total": ["Eff. H.", "Effective"],
"absorb_pct": ["Abs. %", "Absorption %"],
"absorb_total": ["Abs. H.", "Absorption"],
"over": ["OvH%", "Overheal"],
"swing": ["Swing", "Swings"],
"critical": ["HCrit", "Critical%"],
Expand Down
11 changes: 9 additions & 2 deletions share/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@
"damage": "Dégâts totaux",
"hps": "Soins par seconde (HPS)",
"accuracy": "Taux de précision",
"critical": "Taux de critique"
"critical": "Taux de critique",
"effective_pct": "% des soins effectifs",
"absorb_pct": "% d'absorption"
},
"thousands_separator": {
"_": "Séparateur de milliers",
Expand Down Expand Up @@ -282,8 +284,13 @@
"heal": {
"_": "Soin",
"per_second": ["HPS", "Par seconde"],
"pct": ["H%", "Soin %"],
"pct": ["H%", "% des soins"],
"total": ["H.Tot", "Total"],
"effective_per_second" : ["Eff. HPS", "Par seconde effectif"],
"effective_pct" : ["Eff. H%", "% des soins effectifs"],
"effective_total": ["Eff. H.", "Effectif"],
"absorb_pct": ["Abs. %", "% d'absorption"],
"absorb_total": ["Abs. H.", "Absorption"],
"over": ["OvH%", "Overheal"],
"swing": ["Esquive", "Esquives"],
"critical": ["HCrit", "Critique%"],
Expand Down
9 changes: 8 additions & 1 deletion share/lang/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@
"hps": "HPS",
"damage": "ダメージ値",
"accuracy": "命中率",
"critical": "クリティカル"
"critical": "クリティカル",
"effective_pct": "% of total effective healing",
"absorb_pct": "% of total absorb healing"
},
"thousands_separator": {
"_": "千単位の区切り文字",
Expand Down Expand Up @@ -284,6 +286,11 @@
"per_second": ["HPS", "HPS"],
"pct": ["H%", "寄与率"],
"total": ["H合", "合計"],
"effective_per_second" : ["Eff. HPS", "per Second effective"],
"effective_pct" : ["Eff. H%", "Effective healing %"],
"effective_total": ["Eff. H.", "Effective"],
"absorb_pct": ["Abs. %", "Absorption %"],
"absorb_total": ["Abs. H.", "Absorption"],
"over": ["Ovr+", "オーバーヒール"],
"swing": ["Swing", "詠唱回数"],
"critical": ["HCrit", "Critical%"],
Expand Down
9 changes: 8 additions & 1 deletion share/lang/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@
"hps": "HPS",
"damage": "대미지 값",
"accuracy": "실패율 (미스 수 / 타격 수)",
"critical": "극대화 비율"
"critical": "극대화 비율",
"effective_pct": "% of total effective healing",
"absorb_pct": "% of total absorb healing"
},
"thousands_separator": {
"_": "천 단위 구분자",
Expand Down Expand Up @@ -284,6 +286,11 @@
"per_second": ["HPS", "HPS"],
"pct": ["힐%", "기여율"],
"total": ["힐 합계", "합계"],
"effective_per_second" : ["Eff. HPS", "per Second effective"],
"effective_pct" : ["Eff. H%", "Effective healing %"],
"effective_total": ["Eff. H.", "Effective"],
"absorb_pct": ["Abs. %", "Absorption %"],
"absorb_total": ["Abs. H.", "Absorption"],
"over": ["Ovr+", "오버힐"],
"swing": ["타격", "타격 횟수"],
"critical": ["+극대", "극대화 비율"],
Expand Down
Loading