2015-07-22 16:28:15 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Implements Gather Model
|
|
|
|
*
|
|
|
|
* Gather States
|
2015-07-24 13:34:02 +00:00
|
|
|
* - Gathering
|
|
|
|
* - Election (Electing leaders)
|
|
|
|
* - Selection (Selecting teams)
|
2015-07-22 16:28:15 +00:00
|
|
|
* - Done
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
var Gatherer = require("./gatherer");
|
2015-07-24 13:34:02 +00:00
|
|
|
var StateMachine = require("javascript-state-machine");
|
2015-07-22 16:28:15 +00:00
|
|
|
|
2015-07-31 15:06:21 +00:00
|
|
|
function Gather (options) {
|
2015-07-22 16:28:15 +00:00
|
|
|
if (!(this instanceof Gather)) {
|
2015-07-31 15:06:21 +00:00
|
|
|
return new Gather(options);
|
2015-07-22 16:28:15 +00:00
|
|
|
}
|
2015-08-11 11:59:19 +00:00
|
|
|
let noop = () => {};
|
2015-08-11 12:02:31 +00:00
|
|
|
this.onDone = (options && typeof options.onDone === 'function') ? options.onDone : noop;
|
|
|
|
this.onEvent = (options && typeof options.onEvent === 'function') ? options.onEvent : noop;
|
2015-07-24 13:34:02 +00:00
|
|
|
this.TEAM_SIZE = 6;
|
2015-07-22 16:28:15 +00:00
|
|
|
this.gatherers = [];
|
2015-07-31 16:20:52 +00:00
|
|
|
this.ELECTION_INTERVAL = 300000; // 5 mins
|
2015-07-31 15:03:09 +00:00
|
|
|
this.electionStartTime = null;
|
2015-07-24 13:34:02 +00:00
|
|
|
this.initState();
|
2015-07-22 16:28:15 +00:00
|
|
|
}
|
|
|
|
|
2015-08-11 11:55:04 +00:00
|
|
|
StateMachine.create({
|
|
|
|
target: Gather.prototype,
|
|
|
|
events: [
|
|
|
|
{ name: "initState", from: "none", to: "gathering" },
|
|
|
|
{ name: "addGatherer", from: "gathering", to: "election" },
|
|
|
|
{ name: "selectLeader", from: "election", to: "selection" },
|
|
|
|
{ name: "electionTimeout", from: "election", to: "selection" },
|
|
|
|
{ name: "confirmSelection", from: "selection", to: "done" },
|
|
|
|
{ name: "removeGatherer", from: ["gathering", "election", "selection"], to: "gathering" }
|
|
|
|
],
|
|
|
|
callbacks: {
|
|
|
|
// Callbacks for events
|
|
|
|
onafterevent: () => {
|
|
|
|
this.onEvent.call(this);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Gathering State
|
|
|
|
onbeforeaddGatherer: (event, from, to, user) => {
|
|
|
|
this.addUser(user);
|
|
|
|
if (this.gatherers.length !== 12) return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
// Election State
|
|
|
|
onbeforeselectLeader: (event, from, to, voter, candidate) => {
|
|
|
|
this.voteForLeader(voter, candidate);
|
|
|
|
if (this.leaderVotes().length !== 12) return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
onenterelection: () => {
|
|
|
|
// Setup timer for elections
|
2015-08-11 11:59:19 +00:00
|
|
|
let self = this;
|
2015-08-11 11:55:04 +00:00
|
|
|
self.electionStartTime = new Date();
|
|
|
|
setTimeout(() => {
|
|
|
|
if (self.can("electionTimeout")) {
|
|
|
|
self.electionTimeout();
|
|
|
|
}
|
|
|
|
}, self.ELECTION_INTERVAL);
|
|
|
|
},
|
|
|
|
|
|
|
|
onleaveelection: () => {
|
|
|
|
this.electionStartTime = null;
|
|
|
|
},
|
|
|
|
|
|
|
|
// Selection State
|
|
|
|
onenterselection: () => {
|
|
|
|
// Remove all leaders and teams
|
|
|
|
this.gatherers.forEach(gatherer => {
|
|
|
|
gatherer.leader = false;
|
|
|
|
gatherer.team = "lobby";
|
|
|
|
});
|
|
|
|
|
|
|
|
// Assign leaders based on vote
|
|
|
|
// 1st place alien comm
|
|
|
|
// 2nd place marine comm
|
2015-08-11 11:59:19 +00:00
|
|
|
let voteCount = {};
|
2015-08-11 11:55:04 +00:00
|
|
|
this.gatherers.forEach(gatherer => { voteCount[gatherer.id] = 0 });
|
|
|
|
this.leaderVotes().forEach(candidateId => { voteCount[candidateId]++ });
|
2015-08-11 11:59:19 +00:00
|
|
|
let rank = [];
|
|
|
|
for (let candidate in voteCount) {
|
2015-08-11 11:55:04 +00:00
|
|
|
rank.push({ candidate: candidate, count: voteCount[candidate] });
|
|
|
|
}
|
|
|
|
rank.sort((a, b) => {
|
|
|
|
return a.count - b.count;
|
|
|
|
});
|
|
|
|
this.assignAlienLeader(parseInt(rank.pop().candidate, 0));
|
|
|
|
this.assignMarineLeader(parseInt(rank.pop().candidate, 0));
|
|
|
|
},
|
|
|
|
|
|
|
|
onbeforeconfirmSelection: (event, from, to, leader) => {
|
|
|
|
return (this.aliens().length === this.TEAM_SIZE
|
|
|
|
&& this.marines().length === this.TEAM_SIZE);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Remove gatherer event
|
|
|
|
onbeforeremoveGatherer: (event, from, to, user) => {
|
|
|
|
// Cancel transition if no gatherers have been removed
|
|
|
|
let userCount = this.gatherers.length;
|
|
|
|
this.removeUser(user);
|
|
|
|
return (userCount > this.gatherers.length);
|
|
|
|
},
|
|
|
|
|
|
|
|
// On enter done
|
|
|
|
onenterdone: () => {
|
|
|
|
this.onDone.call(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.alienLeader = () => {
|
|
|
|
return this.gatherers.reduce((acc, gatherer) => {
|
2015-07-24 13:34:02 +00:00
|
|
|
if (gatherer.team === "alien" && gatherer.leader) acc.push(gatherer);
|
|
|
|
return acc;
|
|
|
|
}, []).pop();
|
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.marineLeader = () => {
|
|
|
|
return this.gatherers.reduce((acc, gatherer) => {
|
2015-07-24 13:34:02 +00:00
|
|
|
if (gatherer.team === "marine" && gatherer.leader) acc.push(gatherer);
|
|
|
|
return acc;
|
|
|
|
}, []).pop();
|
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.assignMarineLeader = id => {
|
|
|
|
this.modifyGatherer({id: id}, gatherer => {
|
2015-07-24 13:34:02 +00:00
|
|
|
gatherer.leader = true;
|
|
|
|
gatherer.team = "marine";
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.assignAlienLeader = id => {
|
|
|
|
this.modifyGatherer({id: id}, gatherer => {
|
2015-07-24 13:34:02 +00:00
|
|
|
gatherer.leader = true;
|
|
|
|
gatherer.team = "alien";
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.containsUser = user => {
|
|
|
|
return this.gatherers.some(gatherer => {
|
2015-07-23 13:36:51 +00:00
|
|
|
return gatherer.id === user.id;
|
|
|
|
});
|
|
|
|
};
|
2015-07-22 23:30:14 +00:00
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.addUser = user => {
|
2015-08-11 11:55:04 +00:00
|
|
|
if (this.containsUser(user)) return null;
|
2015-08-11 11:59:19 +00:00
|
|
|
let gatherer = new Gatherer(user);
|
2015-07-23 13:36:51 +00:00
|
|
|
this.gatherers.push(gatherer);
|
|
|
|
return gatherer;
|
2015-07-22 23:30:14 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.removeUser = user => {
|
|
|
|
this.gatherers = this.gatherers.filter(gatherer => user.id !== gatherer.id);
|
2015-07-22 23:30:14 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.modifyGatherer = (user, callback) => {
|
2015-08-11 11:55:04 +00:00
|
|
|
return this.gatherers
|
|
|
|
.filter(gatherer => gatherer.id === user.id)
|
|
|
|
.forEach(callback);
|
2015-07-24 13:34:02 +00:00
|
|
|
}
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.moveToMarine = user => {
|
2015-07-24 13:34:02 +00:00
|
|
|
if (this.marines().length >= this.TEAM_SIZE) return;
|
2015-08-10 23:44:54 +00:00
|
|
|
this.modifyGatherer(user, gatherer => gatherer.team = "marine");
|
2015-07-22 23:30:14 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.moveToAlien = user => {
|
2015-07-24 13:34:02 +00:00
|
|
|
if (this.aliens().length >= this.TEAM_SIZE) return;
|
2015-08-10 23:44:54 +00:00
|
|
|
this.modifyGatherer(user, gatherer => gatherer.team = "alien");
|
2015-07-23 13:36:51 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.moveToLobby = user => {
|
2015-08-11 11:55:04 +00:00
|
|
|
this.modifyGatherer(user, gatherer => gatherer.team = "lobby");
|
2015-07-23 13:36:51 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.retrieveGroup = team => {
|
|
|
|
return this.gatherers.filter(gatherer => gatherer.team === team);
|
|
|
|
};
|
2015-07-22 23:30:14 +00:00
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.lobby = () => {
|
2015-07-23 13:36:51 +00:00
|
|
|
return this.retrieveGroup("lobby");
|
2015-07-22 23:30:14 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.aliens = () => {
|
2015-07-23 13:36:51 +00:00
|
|
|
return this.retrieveGroup("alien");
|
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.marines = () => {
|
2015-07-23 13:36:51 +00:00
|
|
|
return this.retrieveGroup("marine");
|
|
|
|
};
|
|
|
|
|
2015-08-11 11:55:04 +00:00
|
|
|
Gather.prototype.electionTimer = () => {
|
|
|
|
return (this.electionStartTime === null) ? null : this.electionStartTime.toISOString();
|
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.toJson = () => {
|
2015-07-23 13:36:51 +00:00
|
|
|
return {
|
2015-07-24 15:01:56 +00:00
|
|
|
gatherers: this.gatherers,
|
2015-07-31 15:03:09 +00:00
|
|
|
state: this.current,
|
|
|
|
election: {
|
2015-08-11 11:55:04 +00:00
|
|
|
startTime: this.electionTimer(),
|
2015-07-31 15:03:09 +00:00
|
|
|
interval: this.ELECTION_INTERVAL
|
|
|
|
}
|
2015-07-23 13:36:51 +00:00
|
|
|
}
|
2015-07-24 13:34:02 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.voteForMap = (voter, mapId) => {
|
2015-08-11 11:55:04 +00:00
|
|
|
this.modifyGatherer(voter, gatherer => gatherer.mapVote = mapId);
|
2015-07-29 13:50:39 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.voteForServer = (voter, serverId) => {
|
2015-08-11 11:55:04 +00:00
|
|
|
this.modifyGatherer(voter, gatherer => gatherer.serverVote = serverId);
|
2015-07-29 13:50:39 +00:00
|
|
|
};
|
|
|
|
|
2015-07-24 13:34:02 +00:00
|
|
|
// Returns an array of IDs representing votes for leaders
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.leaderVotes = () => {
|
2015-08-11 11:59:19 +00:00
|
|
|
let self = this;
|
|
|
|
return self.gatherers
|
|
|
|
.map(gatherer => gatherer.leaderVote)
|
2015-08-10 23:44:54 +00:00
|
|
|
.filter(leaderId => typeof leaderId === 'number')
|
2015-08-11 11:59:19 +00:00
|
|
|
.filter(leaderId => self.containsUser({id: leaderId}));
|
2015-07-24 13:34:02 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.voteForLeader = (voter, candidate) => {
|
2015-08-11 11:55:04 +00:00
|
|
|
this.modifyGatherer(voter, gatherer => gatherer.voteForLeader(candidate));
|
2015-07-24 13:34:02 +00:00
|
|
|
};
|
|
|
|
|
2015-08-10 23:44:54 +00:00
|
|
|
Gather.prototype.getGatherer = (user) => {
|
2015-08-11 11:55:04 +00:00
|
|
|
return this.gatherers
|
|
|
|
.filter(gatherer => gatherer.id === user.id)
|
|
|
|
.pop() || null;
|
2015-07-28 15:54:29 +00:00
|
|
|
};
|
|
|
|
|
2015-07-24 13:34:02 +00:00
|
|
|
module.exports = Gather;
|