const NEWPLAYER = { name: "", id: "", role: {}, reminders: [], isVoteless: false, isDead: false, pronouns: "" }; const state = () => ({ players: [], fabled: [], bluffs: [] }); const getters = { alive({ players }) { return players.filter(player => !player.isDead).length; }, nonTravelers({ players }) { const nonTravelers = players.filter( player => player.role.team !== "traveler" ); return Math.min(nonTravelers.length, 15); }, // calculate a Map of player => night order nightOrder({ players, fabled }) { const firstNight = [0]; const otherNight = [0]; players.forEach(({ role }) => { if (role.firstNight && !firstNight.includes(role.firstNight)) { firstNight.push(role.firstNight); } if (role.otherNight && !otherNight.includes(role.otherNight)) { otherNight.push(role.otherNight); } }); fabled.forEach(role => { if (role.firstNight && !firstNight.includes(role.firstNight)) { firstNight.push(role.firstNight); } if (role.otherNight && !otherNight.includes(role.otherNight)) { otherNight.push(role.otherNight); } }); firstNight.sort((a, b) => a - b); otherNight.sort((a, b) => a - b); const nightOrder = new Map(); players.forEach(player => { const first = Math.max(firstNight.indexOf(player.role.firstNight), 0); const other = Math.max(otherNight.indexOf(player.role.otherNight), 0); nightOrder.set(player, { first, other }); }); fabled.forEach(role => { const first = Math.max(firstNight.indexOf(role.firstNight), 0); const other = Math.max(otherNight.indexOf(role.otherNight), 0); nightOrder.set(role, { first, other }); }); return nightOrder; } }; const actions = { randomize({ state, commit }) { const players = state.players .map(a => [Math.random(), a]) .sort((a, b) => a[0] - b[0]) .map(a => a[1]); commit("set", players); }, clearRoles({ state, commit, rootState }) { let players; if (rootState.session.isSpectator) { players = state.players.map(player => { if (player.role.team !== "traveler") { player.role = {}; } player.reminders = []; return player; }); } else { players = state.players.map(({ name, id, pronouns }) => ({ ...NEWPLAYER, name, id, pronouns })); commit("setFabled", { fabled: [] }); } commit("set", players); commit("setBluff"); } }; const mutations = { clear(state) { state.players = []; state.bluffs = []; state.fabled = []; }, set(state, players = []) { state.players = players; }, /** The update mutation also has a property for isFromSockets this property can be addded to payload object for any mutations then can be used to prevent infinite loops when a property is able to be set from multiple different session on websockets. An example of this is in the sendPlayerPronouns and _updatePlayerPronouns in socket.js. */ update(state, { player, property, value }) { const index = state.players.indexOf(player); if (index >= 0) { state.players[index][property] = value; } }, add(state, name) { state.players.push({ ...NEWPLAYER, name }); }, remove(state, index) { state.players.splice(index, 1); }, swap(state, [from, to]) { [state.players[from], state.players[to]] = [ state.players[to], state.players[from] ]; // hack: "modify" the array so that Vue notices something changed state.players.splice(0, 0); }, move(state, [from, to]) { state.players.splice(to, 0, state.players.splice(from, 1)[0]); }, setBluff(state, { index, role } = {}) { if (index !== undefined) { state.bluffs.splice(index, 1, role); } else { state.bluffs = []; } }, setFabled(state, { index, fabled } = {}) { if (index !== undefined) { state.fabled.splice(index, 1); } else if (fabled) { if (!Array.isArray(fabled)) { state.fabled.push(fabled); } else { state.fabled = fabled; } } } }; export default { namespaced: true, state, getters, actions, mutations };