Skip to content

Commit

Permalink
bunch of team stats for #256
Browse files Browse the repository at this point in the history
  • Loading branch information
ebshimizu committed Dec 27, 2018
1 parent 8db607f commit fe1f631
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 71 deletions.
38 changes: 36 additions & 2 deletions js/database/summarize-team-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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;
Expand All @@ -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;

Expand Down
20 changes: 18 additions & 2 deletions js/game-data/detail-stat-string.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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;
32 changes: 16 additions & 16 deletions js/match-detail.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)));
}
}
}
Expand All @@ -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);
Expand Down
55 changes: 51 additions & 4 deletions js/teams.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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));
Expand All @@ -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.
Expand Down Expand Up @@ -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) };
Expand Down Expand Up @@ -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;
}

Expand Down
8 changes: 5 additions & 3 deletions js/util/formatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -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})}%`;
}
Expand All @@ -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 })}%`;
}
Expand Down
Loading

0 comments on commit fe1f631

Please sign in to comment.