// Copyright 2018-2019 Campbell Crowley. All rights reserved. // Author: Campbell Crowley (dev@campbellcrowley.com) const StatGroup = require('./StatGroup.js'); /** * @description Manages stats and leaderboard information for all of HG. * @memberof HungryGames * @inner */ class StatManager { /** * @description Constructor. * @param {HungryGames~GuildGame} game Parent game. */ constructor(game) { /** * @description Parent game to reference by default. * @public * @type {HungryGames~GuildGame} * @constant */ this.game = game; this.parseDay = this.parseDay.bind(this); } /** * @description Update stats based on the current day data of the given game. * @public */ parseDay() { const game = this.game; const current = game && game.currentGame; const events = current && current.day && current.day.events; if (!events || !Array.isArray(events)) { throw new Error('GuildGame does not have event data to parse.'); } else if (events.length == 0) { return; } const lifetime = new StatGroup(game, 'global'); const previous = new StatGroup(game, 'previous'); const group = game.statGroup ? new StatGroup(game, game.statGroup) : null; if (current.day.num == 0) previous.reset(); const inc = function(...args) { try { lifetime.increment(...args); previous.increment(...args); if (group) group.increment(...args); } catch (err) { console.error(err); } }; for (const e of events) { if (!e || !e.icons) continue; for (let i = 0; i < e.icons.length; i++) { const id = e.icons[i].id; if (!id) continue; const outcome = e.icons[i].settings.victim ? e.victim.outcome : e.attacker.outcome; switch (outcome) { case 'dies': inc(id, 'deaths'); break; case 'wounded': inc(id, 'wounds'); break; case 'thrives': inc(id, 'heals'); break; case 'revived': inc(id, 'revives'); break; } } } const aliveTeams = game.options.teamSize ? current.teams.filter((t) => t.numAlive > 0) : []; const collab = game.options.teammatesCollaborate == 'always' || (game.options.teammatesCollaborate == 'untilend' && aliveTeams.length > 1); const winners = (collab && aliveTeams.length == 1) ? aliveTeams[0].players : (current.numAlive == 1 ? [current.includedUsers.find((el) => el.living).id] : []); const ended = winners.length > 0 || current.numAlive == 0; for (const p of current.includedUsers) { const id = p.id; if (!id) continue; if (p.living) { inc(id, 'daysAlive'); if (p.state === 'wounded') { inc(id, 'daysWounded'); } } else { inc(id, 'daysDead'); } if (ended) { inc(id, 'kills', p.kills); if (winners.includes(id)) { inc(id, 'wins'); } else { inc(id, 'losses'); } } } } /** * @description Fetch a {@HungryGames~StatGroup} reference. * @public * @param {string} [id='global'] The ID of the group to fetch. * @param {Function} cb Callback with optional error argument, otherwise * second argument is the group reference. */ fetchGroup(id = 'global', cb) { if (typeof cb !== 'function') { cb = id; if (typeof cb !== 'function') { throw new TypeError('Callback must be a function'); } id = 'global'; } if (typeof id !== 'string') { cb(new TypeError('ID must be a string')); } else if (StatGroup.exists(this.game, id)) { cb(null, new StatGroup(this.game, id)); } else { cb(new Error('Group doesn\'t exist')); } } /** * @description Create a new stat group. * @public * @param {HGStatMetadata} [metadata] Metadata to store with stat group. * @param {Function} [cb] Callback function once completed. Only argument is * the created group. */ createGroup(metadata, cb) { if (typeof metadata === 'function') { cb = metadata; metadata = null; } const created = new StatGroup(this.game, metadata); if (typeof cb === 'function') cb(created); } /** * @description Fetch IDs of all created groups. * @public * @param {Function} cb Callback with optional error argument, otherwise * second argument is array of IDs as strings. */ fetchGroupList(cb) { StatGroup.fetchList(this.game, cb); } } module.exports = StatManager;