From fe1f6316b46acf765c07b1080ba1b46b715f61b4 Mon Sep 17 00:00:00 2001 From: Evan Shimizu Date: Thu, 27 Dec 2018 16:25:29 -0500 Subject: [PATCH] bunch of team stats for #256 --- js/database/summarize-team-data.js | 38 ++++++- js/game-data/detail-stat-string.js | 20 +++- js/match-detail.js | 32 +++--- js/teams.js | 55 +++++++++- js/util/formatters.js | 8 +- templates/teams-page.html | 160 +++++++++++++++++++++-------- 6 files changed, 242 insertions(+), 71 deletions(-) diff --git a/js/database/summarize-team-data.js b/js/database/summarize-team-data.js index 794804f..5cf0788 100644 --- a/js/database/summarize-team-data.js +++ b/js/database/summarize-team-data.js @@ -295,6 +295,9 @@ function summarizeTeamData(team, docs, HeroesTalents) { // stat aggregation for (let stat in match.teams[t].stats) { + if (stat === 'uptime') + continue; + if (stat === "structures") { for (let struct in match.teams[t].stats.structures) { if (!(struct in data.structures)) { @@ -317,7 +320,8 @@ function summarizeTeamData(team, docs, HeroesTalents) { data.structures[struct].gamesWithFirst += 1; } } - } else if (stat === "totals") { + } + else if (stat === "totals") { for (let total in match.teams[t].stats.totals) { if (!(total in data.stats.total)) { data.stats.total[total] = 0; @@ -338,7 +342,37 @@ function summarizeTeamData(team, docs, HeroesTalents) { ); data.stats.medianTmp[total].push(match.teams[t].stats.totals[total]); } - } else { + } + else if (stat === 'uptimeHistogram') { + // bif of extra work to format these to cleanly fit in app + for (let i = 0; i <= 5; i++) { + const statName = `pctWith${i}HeroesAlive`; + const time = match.teams[t].stats[stat][i] / match.length; + + // if undefined, set to 0 + if (!time) + time = 0; + + if (!(statName in data.stats.total)) { + data.stats.total[statName] = 0; + + data.stats.min[statName] = time; + data.stats.max[statName] = time; + data.stats.medianTmp[statName] = []; + } + data.stats.total[statName] += time; + data.stats.min[statName] = Math.min( + data.stats.min[statName], + time + ); + data.stats.max[statName] = Math.max( + data.stats.max[statName], + time + ); + data.stats.medianTmp[statName].push(time); + } + } + else { if (!(stat in data.stats.total)) { data.stats.total[stat] = 0; diff --git a/js/game-data/detail-stat-string.js b/js/game-data/detail-stat-string.js index e1ab636..8b74928 100644 --- a/js/game-data/detail-stat-string.js +++ b/js/game-data/detail-stat-string.js @@ -59,7 +59,7 @@ const DetailStatString = { 'mercUptimePercent' : 'Mercenary Uptime %', 'timeTo10' : 'Time to Level 10', 'timeTo20' : 'Time to Level 20', - 'avgTimeSpentDead' : 'Average Time Spent Dead', + 'avgTimeSpentDead' : 'Avg. Time Spent Dead', 'T1' : 'Time at Level 1', 'T2' : 'Time at Level 4', 'T3' : 'Time at Level 7', @@ -76,7 +76,23 @@ const DetailStatString = { 'CageUnlocksInterrupted' : 'Cage Unlocks Interrupted', 'SpellDamage' : 'Ability Damage', 'PhysicalDamage' : 'Physical Damage', - 'GardenSeedsCollectedByPlayer' : 'Garden Seeds Collected' + 'GardenSeedsCollectedByPlayer' : 'Garden Seeds Collected', + 'pctWith0HeroesAlive' : 'Game % 0 Heroes Alive', + 'pctWith1HeroesAlive' : 'Game % 1 Hero Alive', + 'pctWith2HeroesAlive' : 'Game % 2 Heroes Alive', + 'pctWith3HeroesAlive' : 'Game % 3 Heroes Alive', + 'pctWith4HeroesAlive' : 'Game % 4 Heroes Alive', + 'pctWith5HeroesAlive' : 'Game % 5 Heroes Alive', + 'wipes' : 'Wipes', + 'aces' : 'Aces', + 'timeWithHeroAdv' : 'Time with Hero Advantage', + 'avgHeroesAlive' : 'Avg. Heroes Alive', + 'passiveXPRate' : 'Passive XP/Second', + 'passiveXPDiff' : 'Passive XP % Gain', + 'passiveXPGain' : 'Passive XP Gained', + 'levelAdvTime' : 'Time with Level Advantage', + 'maxLevelAdv' : 'Max Level Advantage', + 'avgLevelAdv' : 'Avg. Level Advantage' }; module.exports = DetailStatString; diff --git a/js/match-detail.js b/js/match-detail.js index cfa4779..0e7b202 100644 --- a/js/match-detail.js +++ b/js/match-detail.js @@ -2432,14 +2432,14 @@ function updateTeamStats() { updateTeamStat(elem, 'team-time-to-10', formatSeconds(stats.timeTo10)); } else { - updateTeamStat(elem, 'team-time-to-10', 'N/A'); + updateTeamStat(elem, 'team-time-to-10', '-'); } if ('timeTo20' in stats) { updateTeamStat(elem, 'team-time-to-20', formatSeconds(stats.timeTo20)); } else { - updateTeamStat(elem, 'team-time-to-20', 'N/A'); + updateTeamStat(elem, 'team-time-to-20', '-'); } // mercs @@ -2449,41 +2449,41 @@ function updateTeamStats() { updateTeamStat(elem, 'forts-destroyed', stats.structures.Fort.destroyed ? stats.structures.Fort.destroyed : '0'); updateTeamStat(elem, 'forts-lost', stats.structures.Fort.lost); - updateTeamStat(elem, 'first-fort', stats.structures.Fort.destroyed === 0 ? 'N/A' : formatSeconds(stats.structures.Fort.first)); + updateTeamStat(elem, 'first-fort', stats.structures.Fort.destroyed === 0 ? '-' : formatSeconds(stats.structures.Fort.first)); if (matchDetailMatch.map === ReplayTypes.MapType.TowersOfDoom) { updateTeamStat(elem, 'wells-destroyed', stats.structures['Fort Well'].destroyed); updateTeamStat(elem, 'wells-lost', stats.structures['Fort Well'].lost); let hideWellTime = stats.structures['Fort Well'].destroyed === 0 - updateTeamStat(elem, 'first-well', hideWellTime ? 'N/A' : formatSeconds(stats.structures['Fort Well'].first)); + updateTeamStat(elem, 'first-well', hideWellTime ? '-' : formatSeconds(stats.structures['Fort Well'].first)); updateTeamStat(elem, 'towers-destroyed', stats.structures['Fort Tower'].destroyed); updateTeamStat(elem, 'towers-lost', stats.structures['Fort Tower'].lost); let hideTowerTime = stats.structures['Fort Tower'].destroyed === 0; - updateTeamStat(elem, 'first-tower', hideTowerTime ? 'N/A' : formatSeconds(stats.structures['Fort Tower'].first)); + updateTeamStat(elem, 'first-tower', hideTowerTime ? '-' : formatSeconds(stats.structures['Fort Tower'].first)); updateTeamStat(elem, 'keeps-destroyed', '0'); updateTeamStat(elem, 'keeps-lost', 0); - updateTeamStat(elem, 'first-keep', 'N/A'); + updateTeamStat(elem, 'first-keep', '-'); } else { updateTeamStat(elem, 'keeps-destroyed', stats.structures.Keep.destroyed ? stats.structures.Keep.destroyed : '0'); updateTeamStat(elem, 'keeps-lost', stats.structures.Keep.lost); - updateTeamStat(elem, 'first-keep', stats.structures.Keep.destroyed === 0 ? 'N/A' : formatSeconds(stats.structures.Keep.first)); + updateTeamStat(elem, 'first-keep', stats.structures.Keep.destroyed === 0 ? '-' : formatSeconds(stats.structures.Keep.first)); updateTeamStat(elem, 'wells-destroyed', stats.structures['Fort Well'].destroyed + stats.structures['Keep Well'].destroyed); updateTeamStat(elem, 'wells-lost', stats.structures['Fort Well'].lost + stats.structures['Keep Well'].lost); let hideWellTime = (stats.structures['Fort Well'].destroyed + stats.structures['Keep Well'].destroyed) === 0 - updateTeamStat(elem, 'first-well', hideWellTime ? 'N/A' : formatSeconds(Math.min(stats.structures['Fort Well'].first, stats.structures['Keep Well'].first))); + updateTeamStat(elem, 'first-well', hideWellTime ? '-' : formatSeconds(Math.min(stats.structures['Fort Well'].first, stats.structures['Keep Well'].first))); updateTeamStat(elem, 'towers-destroyed', stats.structures['Fort Tower'].destroyed + stats.structures['Keep Tower'].destroyed); updateTeamStat(elem, 'towers-lost', stats.structures['Fort Tower'].lost + stats.structures['Keep Tower'].lost); let hideTowerTime = (stats.structures['Fort Tower'].destroyed + stats.structures['Keep Tower'].destroyed) === 0; - updateTeamStat(elem, 'first-tower', hideTowerTime ? 'N/A' : formatSeconds(Math.min(stats.structures['Fort Tower'].first, stats.structures['Keep Tower'].first))); + updateTeamStat(elem, 'first-tower', hideTowerTime ? '-' : formatSeconds(Math.min(stats.structures['Fort Tower'].first, stats.structures['Keep Tower'].first))); } } } @@ -2504,13 +2504,13 @@ function renderV7TeamStats(elem, stats) { } // if not present, write empty else { - updateTeamStat(elem, 'team-hero-wipes', 'N/A'); - updateTeamStat(elem, 'team-hero-aces', 'N/A'); - updateTeamStat(elem, 'team-hero-alive', 'N/A'); - updateTeamStat(elem, 'team-hero-time-adv', 'N/A'); - updateTeamStat(elem, 'team-avg-level-adv', 'N/A'); - updateTeamStat(elem, 'team-max-level-adv', 'N/A'); - updateTeamStat(elem, 'team-time-level-adv', 'N/A'); + updateTeamStat(elem, 'team-hero-wipes', '-'); + updateTeamStat(elem, 'team-hero-aces', '-'); + updateTeamStat(elem, 'team-hero-alive', '-'); + updateTeamStat(elem, 'team-hero-time-adv', '-'); + updateTeamStat(elem, 'team-avg-level-adv', '-'); + updateTeamStat(elem, 'team-max-level-adv', '-'); + updateTeamStat(elem, 'team-time-level-adv', '-'); updateTeamStat(elem, 'team-passive-xp-rate', 20); updateTeamStat(elem, 'team-passive-xp-diff', 0); updateTeamStat(elem, 'team-passive-xp-total', 0); diff --git a/js/teams.js b/js/teams.js index 54db06e..ebc34be 100644 --- a/js/teams.js +++ b/js/teams.js @@ -321,6 +321,14 @@ function loadTeamData(team, matches, heroData) { $('#team-detail-stats .statistic[name="merc-uptime"] .value').text(formatSeconds(teamStats.stats.average.mercUptime)); $('#team-detail-stats .statistic[name="merc-uptime-percent"] .value').text(formatStat('pct', teamStats.stats.average.mercUptimePercent)); + updateTeamStat(elem, 'team-passive-rate', formatStat('passiveXPRate', teamStats.stats.average.passiveXPRate, true)); + updateTeamStat(elem, 'team-passive-gain', formatStat('passiveXPDiff', teamStats.stats.average.passiveXPDiff, true)); + updateTeamStat(elem, 'team-passive-total', formatStat('passiveXPGain', teamStats.stats.average.passiveXPGain, true)); + + updateTeamStat(elem, 'team-level-adv-time', formatStat('levelAdvTime', teamStats.stats.average.levelAdvTime, true)); + updateTeamStat(elem, 'team-avg-level-adv', formatStat('avgLevelAdv', teamStats.stats.average.avgLevelAdv, true)); + updateTeamStat(elem, 'team-avg-level-lead', formatStat('maxLevelAdv', teamStats.stats.average.maxLevelAdv, true)); + updateTeamStat(elem, 'T1', formatSeconds(teamStats.tierTimes.T1.average)); updateTeamStat(elem, 'T2', formatSeconds(teamStats.tierTimes.T2.average)); updateTeamStat(elem, 'T3', formatSeconds(teamStats.tierTimes.T3.average)); @@ -345,13 +353,19 @@ function loadTeamData(team, matches, heroData) { updateTeamStat(elem, 'wells-lost', teamStats.structures['Fort Well'].lost + teamStats.structures['Keep Well'].lost); let hideWellTime = teamStats.structures['Fort Well'].destroyed + teamStats.structures['Keep Well'].destroyed === 0 - updateTeamStat(elem, 'first-well', hideWellTime ? 'N/A' : formatSeconds(Math.min(teamStats.structures['Fort Well'].first / teamStats.structures['Fort Well'].gamesWithFirst, teamStats.structures['Keep Well'].first / teamStats.structures['Keep Well'].gamesWithFirst))); + updateTeamStat(elem, 'first-well', hideWellTime ? '--:--' : formatSeconds(Math.min( + teamStats.structures['Fort Well'].first / teamStats.structures['Fort Well'].gamesWithFirst, + teamStats.structures['Keep Well'].gamesWithFirst === 0 ? 1e10 : teamStats.structures['Keep Well'].first / teamStats.structures['Keep Well'].gamesWithFirst) + )); updateTeamStat(elem, 'towers-destroyed', teamStats.structures['Fort Tower'].destroyed + teamStats.structures['Keep Tower'].destroyed); updateTeamStat(elem, 'towers-lost', teamStats.structures['Fort Tower'].lost + teamStats.structures['Keep Tower'].lost); let hideTowerTime = teamStats.structures['Fort Tower'].destroyed + teamStats.structures['Keep Tower'].destroyed === 0; - updateTeamStat(elem, 'first-tower', hideTowerTime ? 'N/A' : formatSeconds(Math.min(teamStats.structures['Fort Tower'].first / teamStats.structures['Fort Tower'].gamesWithFirst, teamStats.structures['Keep Tower'].first / teamStats.structures['Keep Tower'].gamesWithFirst))); + updateTeamStat(elem, 'first-tower', hideTowerTime ? '--:--' : formatSeconds(Math.min( + teamStats.structures['Fort Tower'].first / teamStats.structures['Fort Tower'].gamesWithFirst, + teamStats.structures['Keep Tower'].gamesWithFirst === 0 ? 1e10 : teamStats.structures['Keep Tower'].first / teamStats.structures['Keep Tower'].gamesWithFirst) + )); elem = $('#team-damage-stats'); updateTeamStat(elem, 'hero-damage', formatStat('', teamStats.stats.average.HeroDamage, true)); @@ -373,6 +387,18 @@ function loadTeamData(team, matches, heroData) { updateTeamStat(elem, 'first-pick-pct', formatStat('pct', teamStats.firstPicks / teamStats.totalMatches)); updateTeamStat(elem, 'first-pick-win', formatStat('pct', teamStats.firstPicks === 0 ? 0 : teamStats.firstPickWins / teamStats.firstPicks)); + updateTeamStat(elem, 'team-aces', formatStat('aces', teamStats.stats.total.aces, true)); + updateTeamStat(elem, 'team-wipes', formatStat('wipes', teamStats.stats.total.wipes, true)); + + updateTeamStat(elem, 'team-time-hero-adv', formatSeconds(teamStats.stats.average.timeWithHeroAdv)); + updateTeamStat(elem, 'team-heroes-alive', formatStat('avgHeroesAlive', teamStats.stats.average.avgHeroesAlive, true)); + + updateTeamStat(elem, 'team-pct-0-hero', formatStat('pctWith0HeroesAlive', teamStats.stats.average.pctWith0HeroesAlive, true)); + updateTeamStat(elem, 'team-pct-1-hero', formatStat('pctWith1HeroesAlive', teamStats.stats.average.pctWith1HeroesAlive, true)); + updateTeamStat(elem, 'team-pct-2-hero', formatStat('pctWith2HeroesAlive', teamStats.stats.average.pctWith2HeroesAlive, true)); + updateTeamStat(elem, 'team-pct-3-hero', formatStat('pctWith3HeroesAlive', teamStats.stats.average.pctWith3HeroesAlive, true)); + updateTeamStat(elem, 'team-pct-4-hero', formatStat('pctWith4HeroesAlive', teamStats.stats.average.pctWith4HeroesAlive, true)); + updateTeamStat(elem, 'team-pct-5-hero', formatStat('pctWith5HeroesAlive', teamStats.stats.average.pctWith5HeroesAlive, true)); } catch (e) { // basically if a team has no people this will likely throw an exception. @@ -526,8 +552,16 @@ function getTeamCompareStats(teamStats, heroStats) { stats.avgLength = { name: 'Avg. Length', val: teamStats.matchLength.average, format: formatSeconds(teamStats.matchLength.average) }; stats.ppk = { name: 'People Per Kill (PPK)', val: teamStats.stats.average.PPK, format: formatStat('KDA', teamStats.stats.average.PPK) }; - stats.tt10 = { name: 'Avg. Time to 10', val: formatSeconds(teamStats.stats.average.timeTo10), format: formatSeconds(teamStats.stats.average.timeTo10) }; - stats.tt20 = { name: 'Avg. Time to 20', val: formatSeconds(teamStats.stats.average.timeTo20), format: formatSeconds(teamStats.stats.average.timeTo20) }; + stats.tt10 = { name: 'Avg. Time to 10', val: teamStats.stats.average.timeTo10, format: formatSeconds(teamStats.stats.average.timeTo10) }; + stats.tt20 = { name: 'Avg. Time to 20', val: teamStats.stats.average.timeTo20, format: formatSeconds(teamStats.stats.average.timeTo20) }; + + stats.passiveRate = { name: 'Passive XP/Second', val: teamStats.stats.average.passiveXPRate, format: formatStat('passiveXPRate', teamStats.stats.average.passiveXPRate, true) }; + stats.passiveGain = { name: 'Passive XP % Gain', val: teamStats.stats.average.passiveXPDiff, format: formatStat('passiveXPDiff', teamStats.stats.average.passiveXPDiff, true) }; + stats.passiveTotal = { name: 'Passive XP Gain', val: teamStats.stats.average.passiveXPRate, format: formatStat('passiveXPGain', teamStats.stats.average.passiveXPGain, true) }; + + stats.levelAdvTime = { name: 'Avg. Time w/ Level Adv.', val: teamStats.stats.average.levelAdvTime, format: formatSeconds(teamStats.stats.average.levelAdvTime) }; + stats.levelAdv = { name: 'Avg. Level Adv.', val: teamStats.stats.average.avgLevelAdv, format: formatStat('avgLevelAdv', teamStats.stats.average.avgLevelAdv, true) }; + stats.levelLead = { name: 'Avg. Max Level Lead', val: teamStats.stats.average.maxLevelAdv, format: formatSeconds('maxLevelAdv', teamStats.stats.average.maxLevelAdv, true) }; stats.mercs = { name: 'Mercenary Captures', val: teamStats.stats.average.mercCaptures, format: formatStat('mercCaptures', teamStats.stats.average.mercCaptures, true) }; stats.mercUptime = { name: 'Mercenary Uptime', val: teamStats.stats.average.mercUptime, format: formatSeconds(teamStats.stats.average.mercUptime) }; @@ -561,6 +595,19 @@ function getTeamCompareStats(teamStats, heroStats) { stats.silence = { name: 'Avg. Silence Time', val: teamStats.stats.average.TimeSilencingEnemyHeroes, format: formatSeconds(teamStats.stats.average.TimeSilencingEnemyHeroes) }; stats.stun = { name: 'Avg. Stun Time', val: teamStats.stats.average.TimeStunningEnemyHeroes, format: formatSeconds(teamStats.stats.average.TimeStunningEnemyHeroes) }; + stats.aces = { name : 'Aces', val: teamStats.stats.total.aces, format: formatStat('Aces', teamStats.stats.total.aces, true) }; + stats.wipes = { name : 'Wipes', val: teamStats.stats.total.wipes, format: formatStat('Wipes', teamStats.stats.total.wipes, true) }; + + stats.timeWithHeroAdv = { name : 'Avg. Time w/ Hero Adv.', val: teamStats.stats.average.timeWithHeroAdv, format: formatSeconds(teamStats.stats.average.timeWithHeroAdv) }; + stats.avgHeroesAlive = { name : 'Avg. Heroes Alive', val: teamStats.stats.average.avgHeroesAlive, format: formatStat('avgHeroesAlive', teamStats.stats.average.avgHeroesAlive, true) }; + + stats.pct0Hero = { name: 'Avg. % 0 Heroes Alive', val: teamStats.stats.average.pctWith0HeroesAlive, format: formatStat('pctWith0HeroesAlive', teamStats.stats.average.pctWith0HeroesAlive, true) }; + stats.pct1Hero = { name: 'Avg. % 1 Hero Alive', val: teamStats.stats.average.pctWith1HeroesAlive, format: formatStat('pctWith1HeroesAlive', teamStats.stats.average.pctWith1HeroesAlive, true) }; + stats.pct2Hero = { name: 'Avg. % 2 Heroes Alive', val: teamStats.stats.average.pctWith2HeroesAlive, format: formatStat('pctWith2HeroesAlive', teamStats.stats.average.pctWith2HeroesAlive, true) }; + stats.pct3Hero = { name: 'Avg. % 3 Heroes Alive', val: teamStats.stats.average.pctWith3HeroesAlive, format: formatStat('pctWith3HeroesAlive', teamStats.stats.average.pctWith3HeroesAlive, true) }; + stats.pct4Hero = { name: 'Avg. % 4 Heroes Alive', val: teamStats.stats.average.pctWith4HeroesAlive, format: formatStat('pctWith4HeroesAlive', teamStats.stats.average.pctWith4HeroesAlive, true) }; + stats.pct5Hero = { name: 'Avg. % 5 Heroes Alive', val: teamStats.stats.average.pctWith5HeroesAlive, format: formatStat('pctWith5HeroesAlive', teamStats.stats.average.pctWith5HeroesAlive, true) }; + return stats; } diff --git a/js/util/formatters.js b/js/util/formatters.js index 28eda44..5055fe7 100644 --- a/js/util/formatters.js +++ b/js/util/formatters.js @@ -26,7 +26,7 @@ function formatStat(field, val, allFixed = false) { if (val === undefined) return 0; - if (field === 'KillParticipation' || field === 'timeDeadPct' || field === 'mercUptimePercent' || field === 'pct') { + if (field.startsWith('pct') || field === 'KillParticipation' || field === 'timeDeadPct') { if (val > 1) { return `${val.toLocaleString(undefined, {maximumFractionDigits: 1})}%`; } @@ -36,9 +36,11 @@ function formatStat(field, val, allFixed = false) { } else if (field === 'KDA') return val.toLocaleString(undefined, { maximumFractionDigits: 1}); - else if (field.startsWith('Time') || field === 'OnFireTimeOnFire' || field === 'timeTo10' || - field === 'timeTo20' || field === 'mercUptime' || field === 'avgTimeSpentDead') + else if (field.startsWith('Time') || field.startsWith('time') || + field === 'OnFireTimeOnFire' || field === 'timeTo10' || field == 'levelAdvTime' || + field === 'timeTo20' || field === 'mercUptime' || field === 'avgTimeSpentDead') { return formatSeconds(val); + } else if (field === 'passiveXPDiff') { return `+${((val - 1) * 100).toLocaleString(undefined, { maximumFractionDigits: 1 })}%`; } diff --git a/templates/teams-page.html b/templates/teams-page.html index 2fda8fd..32ccb4f 100644 --- a/templates/teams-page.html +++ b/templates/teams-page.html @@ -176,41 +176,10 @@

[Team Name]

-
- -
- - - - - - - - - - - -
StatTeamDiffAverage
-
-
-
Team Stats
-
First Pick
-
+
Avg. Hero Stats
+
###
First Pick %
@@ -219,9 +188,6 @@

[Team Name]

###
First Pick Win %
-
-
Damage
-
###
Hero Damage
@@ -231,6 +197,44 @@

[Team Name]

Damage Taken
+
Hero Advantages
+
+
+
###
+
Avg. Time w/ Hero Adv.
+
+
+
###
+
Avg. Heroes Alive
+
+
+
Average Hero Uptime %
+
+
+
###
+
0 Heroes
+
+
+
###
+
1 Hero
+
+
+
###
+
2 Heroes
+
+
+
###
+
3 Heroes
+
+
+
###
+
4 Heroes
+
+
+
###
+
5 Heroes
+
+
Siege
@@ -262,18 +266,26 @@

[Team Name]

Team Fight
-
+
+
+
###
+
Total Aces
+
+
+
###
+
Total Wipes
+
###
-
Team Fight Hero Damage
+
Hero Damage
###
-
Team Fight Damage Taken
+
Damage Taken
###
-
Team Fight Healing
+
Healing
CC
@@ -300,7 +312,7 @@

[Team Name]

Avg. Macro Stats
-
+
###
Time To 10
@@ -318,8 +330,38 @@

[Team Name]

Times Reached 20
+
Level Advantage
+
+
+
###
+
Avg. Time w/ Level Adv.
+
+
+
###
+
Avg. Level Advantage
+
+
+
###
+
Avg. Max Level Lead
+
+
+
Passive XP
+
+
+
###
+
Passive XP/second
+
+
+
###
+
Passive XP % Gain
+
+
+
###
+
Passive XP Gain
+
+
Avg. End of Game Level Differential
-
+
###
All
@@ -334,7 +376,7 @@

[Team Name]

Mercenaries
-
+
###
Captures
@@ -349,7 +391,7 @@

[Team Name]

Avg. Time Per Talent Tier
-
+
###
Level 1
@@ -377,6 +419,36 @@

[Team Name]

+
+ +
+ + + + + + + + + + + +
StatTeamDiffAverage
+
+
Structures