]> git.xonotic.org Git - xonotic/xonstat.git/blobdiff - xonstat/static/js/weaponCharts.js
Add Arc to the accuracy table.
[xonotic/xonstat.git] / xonstat / static / js / weaponCharts.js
index 8e32893b99eb531a70cedcc9e9dc0b4a9dbd9069..4a9d0571f088b561f96b8c19748f479af28849c2 100644 (file)
-var weapons = ["laser", "shotgun", "uzi", "grenadelauncher", "electro", "crylink",
-               "nex", "hagar", "rocketlauncher", "minstanex", "rifle",  "fireball",
-               "minelayer", "seeker", "tuba", "hlac", "hook", "porto"];
-
-var drawDamageChart = function(data) {
-  // the chart should fill the "damageChart" div
-  var width = document.getElementById("damageChart").offsetWidth;
-
-  // transform the dataset into something nvd3 can use
-  var transformedData = d3.nest()
-    .key(function(d) { return d.weapon_cd; }).entries(data.weapon_stats);
-
-  // transform games list into a map such that games[game_id] = linear sequence
-  var games = {};
-  data.games.forEach(function(v,i){ games[v] = i; });
-
-  // margin model
-  var margin = {top: 20, right: 30, bottom: 30, left: 60},
-      height = 300 - margin.top - margin.bottom;
-
-  width -= margin.left - margin.right;
-
-  // colors
-  var colors = d3.scale.category20().domain(weapons);
-  keyColor = function(d, i) {return colors(d.key)};
-
-  var chart;
-  nv.addGraph(function() {
-    chart = nv.models.stackedAreaChart()
-      .margin(margin)
-      .width(width)
-      .height(height)
-      .x(function(d) { return games[d.game_id] })
-      .y(function(d) { return d.actual })
-      .tooltip(function(key, x, y, e, graph) {
-        return '<h3>' + key + '</h3>' + '<p>' +  y + ' damage in game #' + x + '</p>'
-      })
-      .color(keyColor);
-
-    chart.xAxis
-      .axisLabel("Game ID")
-      .showMaxMin(false)
-      .ticks(5)
-      .tickFormat(function(d) { return data.games[d]; });
-
-    chart.yAxis
-      .tickFormat(d3.format(',02d'));
-
-    d3.select('#damageChartSVG')
-      .datum(transformedData)
-      .transition().duration(500).call(chart);
-
-    nv.utils.windowResize(chart.update);
-
-    return chart;
-  });
+// Colors assigned to the various weapons
+var weaponColors = {
+  "arc": "#b8e9ff", 
+  "laser": "#ff5933", 
+  "blaster": "#ff5933", 
+  "shotgun": "#1f77b4", 
+  "uzi": "#b9e659", 
+  "machinegun": "#b9e659", 
+  "grenadelauncher": "#ff2600", 
+  "mortar": "#ff2600", 
+  "minelayer": "#bfbf00", 
+  "electro": "#597fff",
+  "crylink": "#d940ff", 
+  "nex": "#00e6ff", 
+  "vortex": "#00e6ff", 
+  "hagar": "#d98059", 
+  "rocketlauncher": "#ffbf33", 
+  "devastator": "#ffbf33", 
+  "porto": "#7fff7f", 
+  "minstanex": "#d62728", 
+  "vaporizer": "#d62728", 
+  "hook": "#a5ffd8", 
+  "hlac": "#ffa533",
+  "seeker": "#ff5959", 
+  "rifle": "#9467bd", 
+  "tuba": "#d87f3f", 
+  "fireball": "#33ff33"
+};
+
+// Flatten the existing weaponstats JSON requests
+// to ease indexing
+var flatten = function(weaponData) {
+  flattened = {}
+
+  // each game is a key entry...
+  weaponData.games.forEach(function(e,i) { flattened[e] = {}; });
+
+  // ... with indexes by weapon_cd
+  weaponData.weapon_stats.forEach(function(e,i) { flattened[e.game_id][e.weapon_cd] = e; });
+
+  return flattened;
+}
+
+// Calculate the Y value for a given weapon stat
+function accuracyValue(gameWeaponStats, weapon) {
+  if (gameWeaponStats[weapon] == undefined) {
+    return null;
+  }
+  var ws = gameWeaponStats[weapon];
+  var pct = ws.fired > 0 ? Math.round((ws.hit / ws.fired) * 100) : 0;
+  
+  return pct;
+}
+
+// Calculate the tooltip text for a given weapon stat
+function accuracyTooltip(weapon, pct, averages) {
+  if (pct == null) {
+    return null;
+  }
+
+  var tt = weapon + ": " + pct.toString() + "%";
+  if (averages[weapon] != undefined) {
+    return tt + " (" + averages[weapon].toString() + "% average)"; 
+  }
+
+  return tt;
+}
+
+// Draw the accuracy chart in the "accuracyChart" div id
+function drawAccuracyChart(weaponData) {
+
+  var data = new google.visualization.DataTable();
+  data.addColumn('string', 'X');
+  data.addColumn('number', 'Shotgun');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'MG');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Vortex');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Vaporizer');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Rifle');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Arc');
+  data.addColumn({type: 'string', role: 'tooltip'});
+
+  var flattened = flatten(weaponData);
+
+  for(i in weaponData.games) {
+    var game_id = weaponData.games[i];
+    var sg = accuracyValue(flattened[game_id], "shotgun");
+    var sgTT = accuracyTooltip("shotgun", sg, weaponData.averages);
+    var mg = accuracyValue(flattened[game_id], "machinegun");
+    var mgTT = accuracyTooltip("machinegun", mg, weaponData.averages); 
+    var vortex = accuracyValue(flattened[game_id], "vortex");
+    var vortexTT = accuracyTooltip("vortex", vortex, weaponData.averages);
+    var mn = accuracyValue(flattened[game_id], "vaporizer");
+    var mnTT = accuracyTooltip("vaporizer", mn, weaponData.averages);
+    var rifle = accuracyValue(flattened[game_id], "rifle");
+    var rifleTT = accuracyTooltip("rifle", rifle, weaponData.averages); 
+    var arc = accuracyValue(flattened[game_id], "arc");
+    var arcTT = accuracyTooltip("arc", arc, weaponData.averages); 
+
+    data.addRow([game_id.toString(), sg, sgTT, mg, mgTT, vortex,
+            vortexTT, mn, mnTT, rifle, rifleTT, arc, arcTT]);
+  }
+
+  var options = {
+    backgroundColor: { fill: 'transparent' },
+    lineWidth: 2,
+    legend: { 
+      textStyle: { color: "#666" }
+    },
+    hAxis: {
+      title: 'Games',
+      textPosition: 'none',
+      titleTextStyle: { color: '#666' }
+    },
+    vAxis: {
+      title: 'Percentage',
+      titleTextStyle: { color: '#666' },
+      minValue: 0,
+      maxValue: 100,
+      baselineColor: '#333',
+      gridlineColor: '#333',
+      ticks: [20, 40, 60, 80, 100]
+    },
+    series: {
+      0: { color: weaponColors["shotgun"] },
+      1: { color: weaponColors["machinegun"] },
+      2: { color: weaponColors["vortex"] },
+      3: { color: weaponColors["vaporizer"] },
+      4: { color: weaponColors["rifle"] },
+      5: { color: weaponColors["arc"] }
+    }
+  };
+
+  var chart = new google.visualization.LineChart(document.getElementById('accuracyChart'));
+
+  // a click on a point sends you to that games' page
+  var accuracySelectHandler = function(e) {
+    var selection = chart.getSelection()[0];
+    if (selection != null && selection.row != null) {
+      var game_id = data.getFormattedValue(selection.row, 0);
+      window.location.href = "http://stats.xonotic.org/game/" + game_id.toString();
+    }
+  };
+  google.visualization.events.addListener(chart, 'select', accuracySelectHandler);
+
+  chart.draw(data, options);
+}
+
+// Calculate the damage Y value for a given weapon stat
+function damageValue(gameWeaponStats, weapon) {
+  if (gameWeaponStats[weapon] == undefined) {
+    return null;
+  }
+  return gameWeaponStats[weapon].actual;
+}
+
+// Calculate the damage tooltip text for a given weapon stat
+function damageTooltip(weapon, dmg) {
+  if (dmg == null) {
+    return null;
+  }
+  return weapon + ": " + dmg.toString() + " HP damage";
 }
 
-var drawAccuracyChart = function(data) {
-  // the chart should fill the "accuracyChart" div
-  var width = document.getElementById("accuracyChart").offsetWidth;
-
-  // get rid of empty values
-  data.weapon_stats = data.weapon_stats.filter(function(e){ return e.fired > 0; });
-
-  // transform the dataset into something nvd3 can use
-  var transformedData = d3.nest()
-    .key(function(d) { return d.weapon_cd; }).entries(data.weapon_stats);
-
-  // transform games list into a map such that games[game_id] = linear sequence
-  var games = {};
-  data.games.forEach(function(v,i){ games[v] = i; });
-
-  // margin model
-  var margin = {top: 20, right: 30, bottom: 30, left: 40},
-      height = 300 - margin.top - margin.bottom;
-
-  width -= margin.left - margin.right;
-
-  // colors
-  var colors = d3.scale.category20().domain(weapons);
-  keyColor = function(d, i) {return colors(d.key)};
-
-  var chart;
-  nv.addGraph(function() {
-    chart = nv.models.lineChart()
-      .margin(margin)
-      .width(width)
-      .height(height)
-      .forceY([0,1])
-      .x(function(d) { return games[d.game_id] })
-      .y(function(d) {
-        if(d.fired > 0) {
-          return d.hit/d.fired;
-        } else {
-          return 0;
-        }
-      })
-      .color(keyColor);
-
-    chart.xAxis
-      .axisLabel("Game ID")
-      .showMaxMin(false)
-      .ticks(5)
-      .tickFormat(function(d) { return data.games[d]; });
-
-    var yScale = d3.scale.linear().domain([0,1]).range([0,height]);
-    chart.yAxis
-      .axisLabel('% Accuracy')
-      .tickFormat(d3.format('2%'));
-
-    d3.select('#accuracyChartSVG')
-      .datum(transformedData)
-      .transition().duration(500).call(chart);
-
-    nv.utils.windowResize(chart.update);
-
-    return chart;
-  });
+// Draw the damage chart into the "damageChart" div id
+function drawDamageChart(weaponData) {
+
+  var data = new google.visualization.DataTable();
+  data.addColumn('string', 'X');
+  data.addColumn('number', 'Shotgun');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Machine Gun');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Vortex');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Rifle');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Mortar');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Electro');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Crylink');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Hagar');
+  data.addColumn({type: 'string', role: 'tooltip'});
+  data.addColumn('number', 'Devastator');
+  data.addColumn({type: 'string', role: 'tooltip'});
+
+  var flattened = flatten(weaponData);
+
+  for(i in weaponData.games) {
+    var game_id = weaponData.games[i];
+    var sg = damageValue(flattened[game_id], "shotgun");
+    var sgTT = damageTooltip("shotgun", sg);
+    var mg = damageValue(flattened[game_id], "machinegun");
+    var mgTT = damageTooltip("machinegun", mg); 
+    var vortex = damageValue(flattened[game_id], "vortex");
+    var vortexTT = damageTooltip("vortex", vortex);
+    var mn = damageValue(flattened[game_id], "vaporizer");
+    var mnTT = damageTooltip("vaporizer", mn);
+    var rifle = damageValue(flattened[game_id], "rifle");
+    var rifleTT = damageTooltip("rifle", rifle); 
+    var mortar = damageValue(flattened[game_id], "mortar");
+    var mortarTT = damageTooltip("mortar", mortar);
+    var electro = damageValue(flattened[game_id], "electro");
+    var electroTT = damageTooltip("electro", electro); 
+    var crylink = damageValue(flattened[game_id], "crylink");
+    var crylinkTT = damageTooltip("crylink", crylink);
+    var hagar = damageValue(flattened[game_id], "hagar");
+    var hagarTT = damageTooltip("hagar", hagar);
+    var rl = damageValue(flattened[game_id], "devastator");
+    var rlTT = damageTooltip("devastator", rl); 
+
+    data.addRow([
+      game_id.toString(), 
+      sg, sgTT,
+      mg, mgTT,
+      vortex, vortexTT, 
+      rifle, rifleTT,
+      mortar, mortarTT,
+      electro, electroTT,
+      crylink, crylinkTT,
+      hagar, hagarTT,
+      rl, rlTT
+    ]);
+  }
+
+  var options = {
+    backgroundColor: { fill: 'transparent' },
+    legend: { 
+      position: 'top', 
+      maxLines: 3,
+      textStyle: { color: "#666" }
+    },
+    vAxis: {
+      title: 'HP Damage',  
+      titleTextStyle: {color: '#666'},
+      baselineColor: '#333',
+      gridlineColor: '#333',
+    },
+    hAxis: {
+      title: 'Games',
+      textPosition: 'none',
+      titleTextStyle: { color: '#666' },
+    },
+    isStacked: true,
+    series: {
+      0: { color: weaponColors["shotgun"] },
+      1: { color: weaponColors["machinegun"] },
+      2: { color: weaponColors["vortex"] },
+      3: { color: weaponColors["rifle"] },
+      4: { color: weaponColors["mortar"] },
+      5: { color: weaponColors["electro"] },
+      6: { color: weaponColors["crylink"] },
+      7: { color: weaponColors["hagar"] },
+      8: { color: weaponColors["devastator"] }
+    }
+  };
+
+  var chart = new google.visualization.ColumnChart(document.getElementById('damageChart'));
+
+  // a click on a point sends you to that game's page
+  var damageSelectHandler = function(e) {
+    var selection = chart.getSelection()[0];
+    if (selection != null && selection.row != null) {
+      var game_id = data.getFormattedValue(selection.row, 0);
+      window.location.href = "http://stats.xonotic.org/game/" + game_id.toString();
+    }
+  };
+  google.visualization.events.addListener(chart, 'select', damageSelectHandler);
+
+  chart.draw(data, options);
 }