From 4548195be096bb24e8a71d64385b0b160acebe5d Mon Sep 17 00:00:00 2001
From: Sundava
Date: Wed, 23 Feb 2022 15:54:22 +0100
Subject: [PATCH] feat: Effective healing and absorptions
For #191
---
config/index.html | 30 +++++++++++++++
overlay/css/table.css | 89 +++++++++++++++++++++++--------------------
overlay/lib/listen.js | 44 +++++++++++++++++----
share/lang/cn.json | 9 ++++-
share/lang/de.json | 9 ++++-
share/lang/en.json | 9 ++++-
share/lang/fr.json | 11 +++++-
share/lang/ja.json | 9 ++++-
share/lang/ko.json | 9 ++++-
share/lib/config.js | 47 ++++++++++++++++++++++-
10 files changed, 208 insertions(+), 58 deletions(-)
diff --git a/config/index.html b/config/index.html
index 5968a11..3e895c2 100644
--- a/config/index.html
+++ b/config/index.html
@@ -410,6 +410,36 @@
min="0" step="1" max="2" value="0" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/overlay/css/table.css b/overlay/css/table.css
index 2221fbc..c61dbe1 100644
--- a/overlay/css/table.css
+++ b/overlay/css/table.css
@@ -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;
diff --git a/overlay/lib/listen.js b/overlay/lib/listen.js
index 23e3d39..b9d67de 100644
--- a/overlay/lib/listen.js
+++ b/overlay/lib/listen.js
@@ -13,6 +13,7 @@
SORTABLE[k] = o.v || o
})
+
class Data {
constructor(data) {
@@ -25,9 +26,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) {
@@ -93,21 +94,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;
+ 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
diff --git a/share/lang/cn.json b/share/lang/cn.json
index 07af0ad..db120e6 100644
--- a/share/lang/cn.json
+++ b/share/lang/cn.json
@@ -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",
@@ -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暴%", "治疗暴击率%"],
diff --git a/share/lang/de.json b/share/lang/de.json
index c631781..f261f98 100644
--- a/share/lang/de.json
+++ b/share/lang/de.json
@@ -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",
@@ -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"],
diff --git a/share/lang/en.json b/share/lang/en.json
index 0832780..a391fe2 100644
--- a/share/lang/en.json
+++ b/share/lang/en.json
@@ -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",
@@ -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%"],
diff --git a/share/lang/fr.json b/share/lang/fr.json
index b0b9f52..0727b7e 100644
--- a/share/lang/fr.json
+++ b/share/lang/fr.json
@@ -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",
@@ -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%"],
diff --git a/share/lang/ja.json b/share/lang/ja.json
index 2e155f1..4602615 100644
--- a/share/lang/ja.json
+++ b/share/lang/ja.json
@@ -218,7 +218,9 @@
"hps": "HPS",
"damage": "ダメージ値",
"accuracy": "命中率",
- "critical": "クリティカル"
+ "critical": "クリティカル",
+ "effective_pct": "% of total effective healing",
+ "absorb_pct": "% of total absorb healing"
},
"thousands_separator": {
"_": "千単位の区切り文字",
@@ -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%"],
diff --git a/share/lang/ko.json b/share/lang/ko.json
index 2e96715..93da066 100644
--- a/share/lang/ko.json
+++ b/share/lang/ko.json
@@ -218,7 +218,9 @@
"hps": "HPS",
"damage": "대미지 값",
"accuracy": "실패율 (미스 수 / 타격 수)",
- "critical": "극대화 비율"
+ "critical": "극대화 비율",
+ "effective_pct": "% of total effective healing",
+ "absorb_pct": "% of total absorb healing"
},
"thousands_separator": {
"_": "천 단위 구분자",
@@ -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": ["+극대", "극대화 비율"],
diff --git a/share/lib/config.js b/share/lib/config.js
index 7f22032..489f0ae 100644
--- a/share/lib/config.js
+++ b/share/lib/config.js
@@ -122,6 +122,11 @@ const CONFIG_DEFAULT = {
'_heal-per_second': 3,
'_heal-pct': 2,
'_heal-total': 4,
+ '_heal-effective_per_second': 3,
+ '_heal-effective_pct': 3,
+ '_heal-effective_total': 4,
+ '_heal-absorb_pct': 3,
+ '_heal-absorb_total': 4,
'_heal-swing': 2,
'_heal-over': 2,
'_heal-cure': 2,
@@ -167,7 +172,9 @@ const CONFIG_DEFAULT = {
damage: 0,
hps: 0,
accuracy: 0,
- critical: 0
+ critical: 0,
+ effective_pct: 0,
+ absorb_pct: 0
},
thousands_separator: '',
merge_pet: true,
@@ -230,6 +237,9 @@ const COLUMN_SORTABLE = [
'tank.heal',
'heal.per_second',
'heal.total',
+ 'heal.effective_per_second',
+ 'heal.effective_total',
+ 'heal.absorb_total',
'-etc.death'
]
const COLUMN_MERGEABLE = [
@@ -515,6 +525,41 @@ const COLUMN_INDEX = {
v: _ => _['OverHealPct'],
f: _ => _ && _.replace? _.replace('%', '%') : '---'
},
+ effective_per_second: {
+ v: _ => {
+ return _['enchps'] * (1 - parseInt(_['OverHealPct']) / 100)
+ },
+ f: (_, conf) => {
+ _ = pFloat(_)
+ return isNaN(_)?
+ '0'
+ : formatDps(_, conf.format, 'hps')
+ }
+ },
+ effective_pct: {
+ v: 'effective_pct',
+ f: (_, conf) => {
+ if(isNaN(_)) return '---'
+ else if(_ >= 100) return '100'
+ else return (_).toFixed(conf.format.significant_digit.effective_pct) + (conf.format.use_tailing_pct? '%' : '')
+ }
+ },
+ effective_total: {
+ v: 'effective_healing', // Calculated and injected Data.update()
+ f: (_, conf) => formatDps(_, conf.format, 'damage', true)
+ },
+ absorb_pct: {
+ v: 'absorb_pct',
+ f: (_, conf) => {
+ if(isNaN(_)) return '---'
+ else if(_ >= 100) return '100'
+ else return (_).toFixed(conf.format.significant_digit.absorb_pct) + (conf.format.use_tailing_pct? '%' : '')
+ }
+ },
+ absorb_total: {
+ v: 'absorbHeal',
+ f:(_, conf) => formatDps(_, conf.format, 'damage', true)
+ },
swing: 'heals',
critical: {
v: _ => (parseInt(_.critheals) || 0) / (parseInt(_.heals) || 1) * 100,