'points_played': 0,
'playing_time_ms': 0,
+ // For efficiency.
+ 'offensive_points_completed': 0,
+ 'offensive_points_won': 0,
+ 'defensive_points_completed': 0,
+ 'defensive_points_won': 0,
+
'offensive_soft_plus': 0,
'offensive_soft_minus': 0,
'defensive_soft_plus': 0,
let offense = null;
let puller = null;
let pull_started = null;
+ let last_pull_was_ours = null; // Effectively whether we're playing an O or D point (not affected by turnovers).
let point_num = 0;
for (const [q,p] of Object.entries(players)) {
p.on_field_since = null;
live_since = t;
} else if (type === 'goal' || type === 'their_goal' || type === 'stoppage') {
for (const [q,p] of Object.entries(players)) {
- if (p.on_field_since !== null) {
- if (p.last_point_seen !== point_num) {
- // In case the player did nothing this point,
- // not even subbing in.
- p.last_point_seen = point_num;
- ++p.points_played;
+ if (p.on_field_since === null) {
+ continue;
+ }
+ if (p.last_point_seen !== point_num) {
+ // In case the player did nothing this point,
+ // not even subbing in.
+ p.last_point_seen = point_num;
+ ++p.points_played;
+ }
+ attribute_player_time(p, t, live_since);
+
+ if (last_pull_was_ours === true) { // D point.
+ ++p.defensive_points_completed;
+ if (type === 'goal') {
+ ++p.defensive_points_won;
+ }
+ } else if (last_pull_was_ours === false) { // O point.
+ ++p.offensive_points_completed;
+ if (type === 'goal') {
+ ++p.offensive_points_won;
}
- attribute_player_time(p, t, live_since);
}
}
live_since = null;
} else if (type === 'set_offense' || type === 'their_goal' || type === 'their_throwaway' || type === 'defense' || type === 'interception') {
offense = true;
}
+ if (type === 'pull') {
+ last_pull_was_ours = true;
+ } else if (type === 'their_pull') {
+ last_pull_was_ours = false;
+ } else if (type === 'set_offense' && last_pull_was_ours === null) {
+ // set_offense could either be “changed to offense for some reason
+ // we could not express”, or “we started in the middle of a point,
+ // and we are offense”. We assume that if we already saw the pull,
+ // it's the former, and if not, it's the latter; thus, the === null
+ // test above. (It could also be “we set offense before the pull,
+ // so that we get the right button enabled”, in which case it will
+ // be overwritten by the next pull/their_pull event anyway.)
+ last_pull_was_ours = false;
+ } else if (type === 'set_defense' && last_pull_was_ours === null) {
+ // Similar.
+ last_pull_was_ours = true;
+ } else if (type === 'goal' || type === 'their_goal') {
+ last_pull_was_ours = null;
+ }
// Event management
if (type === 'catch' || type === 'goal') {
add_cell(header, 'th', 'Player');
add_cell(header, 'th', '+/-');
add_cell(header, 'th', 'Soft +/-');
+ add_cell(header, 'th', 'O efficiency');
+ add_cell(header, 'th', 'D efficiency');
add_cell(header, 'th', 'Points played');
add_cell(header, 'th', 'Time played');
rows.push(header);
let row = document.createElement('tr');
let pm = p.goals + p.assists + p.hockey_assists + p.defenses - p.throwaways - p.drops;
let soft_pm = p.offensive_soft_plus + p.defensive_soft_plus - p.offensive_soft_minus - p.defensive_soft_minus;
+ let o_efficiency = (p.offensive_points_won / p.offensive_points_completed) * 2 - 1;
+ let d_efficiency = (p.defensive_points_won / p.defensive_points_completed) * 2 - 1;
add_cell(row, 'td', p.name); // TODO: number?
add_cell(row, 'td', pm > 0 ? ('+' + pm) : pm);
add_cell(row, 'td', soft_pm > 0 ? ('+' + soft_pm) : soft_pm);
+ add_cell(row, 'td', p.offensive_points_completed > 0 ? o_efficiency.toFixed(2) : 'N/A');
+ add_cell(row, 'td', p.defensive_points_completed > 0 ? d_efficiency.toFixed(2) : 'N/A');
add_cell(row, 'td', p.points_played);
add_cell(row, 'td', Math.floor(p.playing_time_ms / 60000) + ' min');
rows.push(row);