// Copyright 2019-2020 Campbell Crowley. All rights reserved. // Author: Campbell Crowley (dev@campbellcrowley.com) const Action = require('./Action.js'); const ChannelAction = require('./ChannelAction.js'); const MemberAction = require('./MemberAction.js'); /** * @description Handles firing of actions from {@see HungryGames~ActionStore}. * * @memberof HungryGames * @inner */ class ActionManager { /** * @description Call when a day is started. * @public * @static * @param {HungryGames} hg HG context events are firing from. * @param {HungryGames~GuildGame} game Game context events are firing in. */ static dayStart(hg, game) { ActionManager._triggerAll(hg, game, game.actions.dayStart); } /** * @description Call when a day has ended. * @public * @static * @param {HungryGames} hg HG context events are firing from. * @param {HungryGames~GuildGame} game Game context events are firing in. */ static dayEnd(hg, game) { const list = game.actions.dayEnd; const alive = game.actions.dayPlayerAlive; const dead = game.actions.dayPlayerDead; const wounded = game.actions.dayPlayerWounded; ActionManager._endTrigger(hg, game, list, alive, dead, wounded); } /** * @description Call when day state is updated. This handles the start and end * events as well as all players affected. * @public * @static * @param {HungryGames} hg HG context events are firing from. * @param {HungryGames~GuildGame} game Game context events are firing in. */ static stepped(hg, game) { ActionManager._triggerAll(hg, game, game.actions.eventInstant); const death = game.actions.eventPlayerDeath; const revive = game.actions.eventPlayerRevive; const wound = game.actions.eventPlayerWound; const heal = game.actions.eventPlayerHealed; const killed = game.actions.eventPlayerKilled; const gain = game.actions.eventPlayerGainWeapon; const lose = game.actions.eventPlayerLoseWeapon; const use = game.actions.eventPlayerUseWeapon; const none = game.actions.eventPlayerUnAffected; const day = game.currentGame.day; const guild = hg._parent.client.guilds.resolve(game.id); const evt = day.events[day.state]; if (evt) { const go = function(member, group, weapons = {}) { game.actions.eventPlayerAffected.forEach( (el) => el.trigger(hg, game, member)); let action = none; let unaffected = true; switch (group.outcome) { case 'dies': action = death; unaffected = false; break; case 'wounded': action = wound; unaffected = false; break; case 'thrives': action = heal; unaffected = false; break; case 'revived': action = revive; unaffected = false; break; } if (!unaffected) { action.forEach((el) => el.trigger(hg, game, member)); } if (group.killer) { action = killed; unaffected = false; action.forEach((el) => el.trigger(hg, game, member)); } if (weapons) { unaffected = false; let pos = false; let neg = false; let non = false; for (const w of Object.values(weapons)) { let act = null; if (w > 0 && !pos) { act = gain; pos = true; } else if (w < 0 && !neg) { act = lose; neg = true; } else if (w == 0 && !non) { act = use; non = true; } if (act) act.forEach((el) => el.trigger(hg, game, member)); if (pos && neg && non) break; } unaffected = !pos && !neg && !non; } if (unaffected) action.forEach((el) => el.trigger(hg, game, member)); }; if (!guild) return; let sameConsumer = false; evt.icons.forEach((el) => { if (el.id.startsWith('NPC')) return; const member = guild.members.resolve(el.id); const group = el.settings.victim ? evt.victim : evt.attacker; const weapons = {}; group.weapons.forEach((w) => weapons[w.name] = w.count); sameConsumer = sameConsumer || evt.consumer === el.id; if (el.settings.attacker && evt.consumer) { evt.consumes.forEach((w) => { if (!weapons[w.name]) weapons[w.name] = -w.count; weapons[w.name] -= w.count; }); } if (!member) { guild.members.fetch(el.id) .then((mem) => go(mem, group, weapons)) .catch((err) => { console.error( 'Unable to fetch member for action:', err.message, err.path); }); } else { go(member, group, weapons); } }); if (evt.consumer && !sameConsumer && !evt.consumer.startsWith('NPC')) { const weapons = {}; const member = guild.members.resolve(evt.consumer); evt.consumes.forEach((el) => weapons[el.name] = -el.count); if (!member) { guild.members.fetch(evt.consumer) .then((mem) => go(mem, {outcome: 'nothing'}, weapons)); } else { go(member, {outcome: 'nothing'}, weapons); } } } } /** * @description Call when game is started. * @public * @static * @param {HungryGames} hg HG context events are firing from. * @param {HungryGames~GuildGame} game Game context events are firing in. */ static gameStart(hg, game) { ActionManager._triggerAll(hg, game, game.actions.gameStart); } /** * @description Call when game has ended. * @public * @static * @param {HungryGames} hg HG context events are firing from. * @param {HungryGames~GuildGame} game Game context events are firing in. */ static gameEnd(hg, game) { const list = game.actions.gameEnd.slice(); const alive = game.actions.gamePlayerAlive; const dead = game.actions.gamePlayerDead; const wounded = game.actions.gamePlayerWounded; // const hasPatron = // game.currentGame.includedUsers.find((el) => el.settings.isPatron); // if (!hasPatron) { let max = 0; list.forEach((el) => max = Math.max(max, el.delay)); max += 2000; // const patreonAction = new Action.SendMessageAction( // 'If you enjoy SpikeyBot, please consider supporting it on Patreon: ' // + // '<https://www.patreon.com/campbellcrowley>'); // const patreonAction = new Action.SendMessageAction( // '⚠️SpikeyBot is shutting down January 1, 2021⚠️\n' + // 'More info on my Discord server.'); // patreonAction.delay = max; // list.push(patreonAction); // } ActionManager._endTrigger(hg, game, list, alive, dead, wounded); let winList = []; if (game.options.teamSize > 0 && game.options.teammatesCollaborate === 'always') { const team = game.currentGame.teams.find((team) => team.numAlive > 0); if (team) winList = team.players; } else { const player = game.currentGame.includedUsers.find((el) => el.living); if (player) winList.push(player.id); } const win = game.actions.gamePlayerWin; const lose = game.actions.gamePlayerLose; const guild = hg._parent.client.guilds.resolve(game.id); game.currentGame.includedUsers.forEach((player) => { if (player.isNPC) return; const member = guild.members.resolve(player.id); if (!member) return; if (winList.includes(player.id)) { win.forEach((el) => el.trigger(hg, game, member)); } else { lose.forEach((el) => el.trigger(hg, game, member)); } }); } /** * @description Call when game has been aborted. * @public * @static * @param {HungryGames} hg HG context events are firing from. * @param {HungryGames~GuildGame} game Game context events are firing in. */ static gameAbort(hg, game) { ActionManager._triggerAll(hg, game, game.actions.gameAbort); } /** * @description Trigger all given actions. * @private * @static * @param {HungryGames} hg HG context events are firing from. * @param {HungryGames~GuildGame} game Game context events are firing in. * @param {HungryGames~Action[]} list List of events to fire all of. */ static _triggerAll(hg, game, list) { const channel = hg._parent.client.channels.resolve(game.outputChannel); const guild = hg._parent.client.guilds.resolve(game.id); list.forEach((el) => { if (el instanceof MemberAction) { game.currentGame.includedUsers.forEach((player) => { if (player.isNPC) return; const member = guild.members.resolve(player.id); if (!member) return; el.trigger(hg, game, member); }); } else if (el instanceof ChannelAction) { if (channel) el.trigger(hg, game, channel); } else if (el instanceof Action) { el.trigger(hg, game); } }); } /** * @description Trigger all actions for the end of something. * @private * @static * @param {HungryGames} hg HG context events are firing from. * @param {HungryGames~GuildGame} game Game context events are firing in. * @param {HungryGames~Action[]} list List of events to fire all of. * @param {HungryGames~MemberAction[]} alive List of events to fire for all * living players. * @param {HungryGames~MemberAction[]} dead List of events to fire for all * dead players. * @param {HungryGames~MemberAction[]} wounded List of events to fire for all * wounded players. */ static _endTrigger(hg, game, list, alive, dead, wounded) { ActionManager._triggerAll(hg, game, list); const guild = hg._parent.client.guilds.resolve(game.id); game.includedUsers.forEach((player) => { if (player.isNPC) return; const member = guild.members.resolve(player.id); if (!member) return; if (!player.living) { dead.forEach((el) => el.trigger(hg, game, member)); } else { alive.forEach((el) => el.trigger(hg, game, member)); if (player.state === 'wounded') { wounded.forEach((el) => el.trigger(hg, game, member)); } } }); } } module.exports = ActionManager;