loupgarou.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. function LoupGarou(config) {
  2. this.config = config;
  3. }
  4. const ROLES = {
  5. WOLF: "WOLF",
  6. VILLAGER: "VILLAGER",
  7. CUPIDON: "CUPIDON", // OK
  8. HUNTER: "HUNTER",
  9. WITCH: "WITCH",
  10. SEER: "SEER", // OK
  11. GIRL: "GIRL"
  12. };
  13. const ROLE_REPARTITION = [
  14. [ ROLES.WOLF, ROLES.WOLF, ROLES.VILLAGER ],
  15. [ ROLES.WOLF, ROLES.VILLAGER, ROLES.VILLAGER, ROLES.VILLAGER ],
  16. [ ROLES.WOLF, ROLES.WOLF, ROLES.VILLAGER, ROLES.VILLAGER, ROLES.HUNTER ],
  17. [ ROLES.WOLF, ROLES.WOLF, ROLES.VILLAGER, ROLES.VILLAGER, ROLES.HUNTER, ROLES.WITCH ]
  18. ];
  19. var i =-1;
  20. const STEP = {
  21. NOT_STARTED: -4,
  22. REGISTRATION: -3,
  23. SET_ROLES: -2,
  24. SELECT_CUPIDON: -1,
  25. NIGHT: ++i,
  26. NIGHT_SEER: ++i,
  27. NIGHT_GIRL: ++i,
  28. NIGHT_LG: ++i,
  29. NIGHT_WITCH: ++i,
  30. DAY_LOVER: ++i,
  31. DAY_HUNTER: ++i,
  32. DAY: ++i,
  33. DAY_VILLA: ++i,
  34. DAY_EVENING: ++i
  35. };
  36. const MAX_STEP = STEP.DAY_EVENING;
  37. const langMng = [
  38. {
  39. unregisterLeft: function(who) { return `${who} n'est plus inscrit au Loup-Garou`; },
  40. killedHunter: function(who) { return `Le chasseur tire sur ${who} dans son dernier souffle et le tue !`; },
  41. killedWolf: function(who) { return `${who} a servi d'en-cas aux loups-garous !`; },
  42. killedLeft: function(who) { return `${who} souffre du raj-quit et en est éliminé...`; },
  43. killedLove: function(who, lover) { return `Dans un élan de chagrin, ${who} decide de rejoindre ${lover} dans sa tombe. RIP !`; },
  44. killedVillager: function(who) { return `Vous avez décidé de voter contre ${who} et le tuez sans le moindre remord.`; },
  45. join: function(who) { return `${who} est inscrit.e a la prochaine partie !`; },
  46. inviteWolfRoom: function() { return `Invitation des Loups Garous sur le salon de nuit.`; },
  47. inviteWolfRoomPrivate: function(salon) { return "Merci de rejoindre le salon " +salon; },
  48. dayNoDead: function() { return "Il fait jour et le village se réveille et personne n'est mort."; },
  49. dayOneDead: function() { return "Il fait jour et le village se réveille en découvrant un corps sans vie près de la fontaine."; },
  50. dayMultiDead: function() { return "Il fait jour et le village se réveille en découvrant avec stupeur et tremblement une pile de corps au centre de la place du marché."; },
  51. startWolfVotes: function() { return "Vous allez devoir manger quelqu'un cette nuit ! Pour voter contre un innocent tapez !vote <pseudo>"; },
  52. startVotes: function() { return "Vous allez devoir trouver les loups garous et les éliminer. Pour voter contre quelqu'un tape : !vote <pseudo>"; },
  53. cupidonMessage: function(players) { return "Tu vas pouvoir choisir un couple d'amoureux. Pour envoyer ta fleche, tapes les pseudos des deux personnes a coupler. Tu as le choix entre: " +players.join(", "); },
  54. setNoLove: function() { return "Tu n'as planté ta flèche dans personne. Il n'y aura donc pas d'amoureux cette partie."; },
  55. setLoveWith: function(p1, p2) { return `Tu as planté ta flèche et choisi ${p1} et ${p2}`; },
  56. inLoveWith: function(other) { return `Cupidon a envoyé sa flèche sur toi et ton amoureux est ${other}`; },
  57. remainingPseudos: function(nicks) { return "Vous avez le choix entre : " +nicks.join(", "); },
  58. noVote: function() { return "Pas de vote pour l'instant..."; },
  59. seerMessage: function(players) { return "Tu vas pouvoir decouvrir le role d'un joueur. Tapes juste le pseudo du joueur qui t'interesse. Tu as le choix entre: " +players.join(", "); },
  60. seerTimeout: function() { return "Il est tard et tu vas te coucher. Tu pourras reessayer demain."; },
  61. wolfTimeout: function() { return "Les Loups-Garous ne se sont pas nourris ce soir et sont tous morts de faim !"; },
  62. loverWins: function() { return "Les amoureux gagnent la partie !"; },
  63. wolfWins: function() { return "Les loup-garous gagnent la partie !"; },
  64. villagerWins: function() { return "Les villageois gagnent la partie !"; },
  65. congrats: function(nickList) { return nickList.length == 1 ? ("Félicitations au gagnant " +nickList[0]) : ("Félicitations aux gagnants " +nickList.join(", ")); },
  66. waitingCupidon: function(timeSec) { return `En attente de cupidon (${timeSec} secondes)`; },
  67. waitingSeer: function(timeSec) { return `En attente de la voyante (${timeSec} secondes)`; },
  68. waitingWolf: function(timeSec) { return `En attente des loup-garous (${timeSec} secondes)`; },
  69. currentVotes: function() { return "Etat des votes:"; },
  70. whoiswhat: {
  71. "WOLF": function(who) { return `${who} est un Loup-Garou.`; },
  72. "VILLAGER": function(who) { return `${who} est un simple villageois.`; },
  73. "CUPIDON": function(who) { return `${who} est un cupidon.`; },
  74. "HUNTER": function(who) { return `${who} est un chasseur.`; },
  75. "WITCH": function(who) { return `${who} est une sorcière.`; },
  76. "SEER": function(who) { return `${who} est une voyante.`; },
  77. "GIRL": function(who) { return `${who} est une petite fille.`; },
  78. },
  79. whowaswhat: {
  80. "WOLF": function(who) { return `${who} était un Loup-Garou !`; },
  81. "VILLAGER": function(who) { return `${who} était un simple villageois !`; },
  82. "CUPIDON": function(who) { return `${who} était un cupidon !`; },
  83. "HUNTER": function(who) { return `${who} était un chasseur !`; },
  84. "WITCH": function(who) { return `${who} était une sorcière !`; },
  85. "SEER": function(who) { return `${who} était une voyante !`; },
  86. "GIRL": function(who) { return `${who} était une petite fille !`; },
  87. },
  88. advRole: {
  89. "WOLF": "Vous êtes un Loup-Garou ! Votre objectif est d'éliminer tous les innocents (ceux qui ne sont pas Loups Garous). Chaque nuit, vous vous réunissez entre compères Loups pour décider d'une victime à éliminer.",
  90. "VILLAGER": "Vous êtes un Villageois ! Votre objectif est d'éliminer tous les Loups Garous. Vous ne disposez d'aucun pouvoir particulier : uniquement votre perspicacité et votre force de persuasion.",
  91. "CUPIDON": "Vous êtes Cupidon ! Votre objectif est d'éliminer tous les Loups Garous. Dès le début de la partie, vous devez former un couple de deux joueurs. Leur objectif sera de survivre ensemble, car si l'un d'eux meurt, l'autre se suicidera.",
  92. "HUNTER": "Vous êtes le chasseur ! Votre objectif est d'éliminer tous les Loups Garous. A votre mort, vous pouvez éliminer un joueur en utilisant votre dernière balle.",
  93. "WITCH": "Vous êtes la sorciere ! Votre objectif est d'éliminer tous les Loups Garous. Vous disposez de deux potions : une potion de vie pour sauver la victime des Loups et une potion de mort pour assassiner quelqu'un.",
  94. "SEER": "Vous êtes la voyante ! Votre objectif est d'éliminer tous les Loups Garous. Chaque nuit, vous pouvez espionner un joueur et découvrir sa véritable identité.",
  95. "GIRL": "Vous êtes la petite fille ! Votre objectif est d'éliminer tous les Loups Garous. Chaque nuit, vous pouvez espionner les loups et tenter de découvrir leurs identité."
  96. },
  97. }
  98. ];
  99. const KILL_REASON = {
  100. HUNTER: 0,
  101. WOLF: 1,
  102. LEFT: 2,
  103. LOVE: 3,
  104. VILLAGER: 4,
  105. HUNGER: 5
  106. }
  107. function Player(lg, name) {
  108. this.loupgarou = lg;
  109. this.dead = null;
  110. this.killReason = null;
  111. this.role = null;
  112. this.lover = null;
  113. this.name = name;
  114. this.reminder = null;
  115. this.jobDone = null;
  116. }
  117. Player.prototype.rename = function(newNick) { this.name = newNick; };
  118. Player.prototype.kill = function(reason, msg) {
  119. this.dead = reason;
  120. this.killReason = msg;
  121. this.loupgarou.onKilled(this);
  122. };
  123. VoteEngine = function(lg) {
  124. this.loupgarou = lg;
  125. this.players = null;
  126. this.canBeVoted = null;
  127. };
  128. VoteEngine.prototype.init = function(canVote, canBeVoted) {
  129. this.players = {};
  130. this.canBeVoted = {};
  131. canVote.forEach(i => this.players[i.toLowerCase()] = false);
  132. canBeVoted.forEach(i => this.canBeVoted[i.toLowerCase()] = true);
  133. };
  134. VoteEngine.prototype.vote = function(channel, player, text) {
  135. words = text.trim().toLowerCase().split(/\s/, 3);
  136. if (words[0] !== "!vote")
  137. return;
  138. if (!player ||
  139. !words[1] ||
  140. this.players[player] === undefined ||
  141. this.canBeVoted[words[1]] === undefined)
  142. return;
  143. this.players[player] = { against: words[1], reason: words[2] || null };
  144. if (this.canBeVoted[words[1]] === true)
  145. this.canBeVoted[words[1]] = Date.now();
  146. this.loupgarou.onVote(channel, player, words[1]);
  147. };
  148. LoupGarou.prototype.onVote = function(channel, vote, voteAgainst) {
  149. const votingPlayer = this.players[vote],
  150. votedPlayer = this.players[voteAgainst];
  151. if (votingPlayer && votedPlayer)
  152. this.bot.sendMsg(channel, `${votingPlayer.name} vote contre ${votedPlayer.name}`);
  153. }
  154. VoteEngine.prototype.hasVotes = function() {
  155. for (var i in this.players)
  156. if (this.players[i])
  157. return true;
  158. return false;
  159. };
  160. VoteEngine.prototype.getVotes = function() {
  161. var votedByName = {},
  162. voted = [];
  163. for (var i in this.players) {
  164. if (this.players[i] !== false) {
  165. var arr = votedByName[this.players[i].against];
  166. if (!arr)
  167. voted.push(arr = votedByName[this.players[i].against] = {name: this.players[i].against, from: []});
  168. arr.from.push(i);
  169. }
  170. }
  171. return voted.sort((i, j) => j.from.length -i.from.length);
  172. };
  173. VoteEngine.prototype.getVote = function() {
  174. var voted = {};
  175. for (var i in this.players) {
  176. if (this.players[i] !== undefined) {
  177. var arr = voted[this.players[i].against] = (voted[this.players[i].against] || []);
  178. arr.push(this.players[i].reason);
  179. }
  180. }
  181. var result = null,
  182. max = 0,
  183. time = null;
  184. for (var i in voted) {
  185. if (voted[i].length > max || (voted[i].length === max && (time === null || time > this.canBeVoted[i]))) {
  186. result = i;
  187. max = voted[i].max;
  188. time = this.canBeVoted[i];
  189. }
  190. }
  191. console.log(voted[i]);
  192. return result;
  193. };
  194. LoupGarou.prototype.init = function(bot, chanName) {
  195. this.room = chanName;
  196. this.bot = bot;
  197. this.users = {};
  198. this.players = {};
  199. this.stepListeners = {};
  200. this.timeInStep = 0;
  201. this.currentStep = STEP.NOT_STARTED;
  202. this.currentScenario = langMng[0];
  203. this.nightChannelPersons = {};
  204. this.justDead = [];
  205. this.voteEngine = new VoteEngine(this);
  206. for (var i in STEP) this.stepListeners[STEP[i]] = [];
  207. this.stepListeners[STEP.NOT_STARTED].push(function() { this.bot.setModes(this.room, "-m"); this.bot.endGame(this); });
  208. this.stepListeners[STEP.REGISTRATION].push(LoupGarou.prototype.onRegistration);
  209. this.stepListeners[STEP.SET_ROLES].push(LoupGarou.prototype.startGame);
  210. this.stepListeners[STEP.SELECT_CUPIDON].push(LoupGarou.prototype.onCupidon);
  211. this.stepListeners[STEP.NIGHT].push(function() { this.bot.sendMsg(this.room, "C'est la nuit, crack crack boum"); this.nextStep(); }); // FIXME this.currentScenario....
  212. this.stepListeners[STEP.NIGHT_SEER].push(LoupGarou.prototype.onSeer);
  213. this.stepListeners[STEP.NIGHT_GIRL].push(LoupGarou.prototype.onGirl);
  214. this.stepListeners[STEP.NIGHT_LG].push(LoupGarou.prototype.onWolf);
  215. this.stepListeners[STEP.NIGHT_WITCH].push(LoupGarou.prototype.onWitch);
  216. this.stepListeners[STEP.DAY_LOVER].push(LoupGarou.prototype.onLovers);
  217. this.stepListeners[STEP.DAY_HUNTER].push(LoupGarou.prototype.onHunter);
  218. this.stepListeners[STEP.DAY].push(LoupGarou.prototype.onDay);
  219. this.stepListeners[STEP.DAY].push(LoupGarou.prototype.cleanCorpses);
  220. this.stepListeners[STEP.DAY].push(LoupGarou.prototype.checkEndOfGameAndContinue);
  221. this.stepListeners[STEP.DAY_VILLA].push(LoupGarou.prototype.onVillagers);
  222. this.stepListeners[STEP.DAY_EVENING].push(LoupGarou.prototype.onEvening);
  223. this.stepListeners[STEP.DAY_EVENING].push(LoupGarou.prototype.checkEndOfGameAndContinue);
  224. var self = this;
  225. this.bot.bot.addListener("pm", (from, text) => {
  226. self.onPm(from, text);
  227. });
  228. }
  229. LoupGarou.prototype.getName = function(){ return "loupgarou"; };
  230. LoupGarou.prototype.onActivate = function(){
  231. this.currentScenario = langMng[Math.floor(Math.random() *langMng.length)];
  232. this.setStep(STEP.REGISTRATION);
  233. console.info("Starting Loup-garou");
  234. //this.bot.setModes(this.config.privateChannel, "+iKmnpt"); FIXME
  235. this.bot.setModes(this.config.privateChannel, "+Kmnpt");
  236. for (var i in this.nightChannelPersons)
  237. this.bot.kick(this.config.privateChannel, i);
  238. };
  239. LoupGarou.prototype.onAddMode = function(user, mode) {};
  240. LoupGarou.prototype.onRemMode = function(user, mode) {};
  241. LoupGarou.prototype.onRename = function(oldNick, newNick) {
  242. this.users[newNick.toLowerCase()] = this.users[oldNick.toLowerCase()];
  243. delete this.users[oldNick.toLowerCase()];
  244. var p = this.players[newNick.toLowerCase()] = this.players[oldNick.toLowerCase()];
  245. delete this.players[oldNick.toLowerCase()];
  246. if (this.nightChannelPersons[oldNick.toLowerCase()]) {
  247. this.nightChannelPersons[newNick.toLowerCase()] = this.nightChannelPersons[oldNick.toLowerCase()];
  248. delete this.nightChannelPersons[oldNick.toLowerCase()];
  249. }
  250. p && p.rename(newNick);
  251. };
  252. LoupGarou.prototype.onNickPart = function(nick) {
  253. delete this.users[nick.toLowerCase()];
  254. var p = this.players[nick.toLowerCase()];
  255. if (p) {
  256. if (this.currentStep > STEP.REGISTRATION) {
  257. p.kill(KILL_REASON.LEFT, this.currentScenario.killedLeft(p.name));
  258. } else {
  259. this.bot.sendMsg(this.room, this.currentScenario.unregisterLeft(p.name));
  260. delete this.players[nick.toLowerCase()];
  261. }
  262. }
  263. };
  264. LoupGarou.prototype.onNameList = function(nicks) {
  265. this.users = {};
  266. for (var i in nicks) {
  267. var u = this.users[i.toLowerCase()] = (this.users[i.toLowerCase()] || this.bot.createUser(i));
  268. u.setModeChar(nicks[i]);
  269. }
  270. };
  271. LoupGarou.prototype.onSelfJoin = function() {
  272. // Preparing night channel
  273. var _this = this;
  274. this.nightChannelPersons = this.nightChannelPersons || {};
  275. this.bot.bot.addListener("join" +_this.config.privateChannel, nick => {
  276. if (_this.bot.name.toLowerCase() !== nick.toLowerCase()) {
  277. _this.nightChannelPersons[nick.toLowerCase()] = true;
  278. if (_this.currentStep === STEP.NIGHT_LG)
  279. _this.bot.voice(_this.config.privateChannel, nick);
  280. }
  281. });
  282. this.bot.bot.addListener("names" +_this.config.privateChannel, nicks => {
  283. for (var nick in nicks)
  284. if (this.bot.name.toLowerCase() !== nick.toLowerCase())
  285. _this.nightChannelPersons[nick.toLowerCase()] = true;
  286. });
  287. this.bot.bot.addListener("part" +_this.config.privateChannel, nick => {
  288. delete _this.nightChannelPersons[nick.toLowerCase()];
  289. });
  290. this.bot.bot.addListener("kick" +_this.config.privateChannel, nick => {
  291. delete _this.nightChannelPersons[nick.toLowerCase()];
  292. });
  293. this.bot.bot.addListener("message" +_this.config.privateChannel, (user, text) => {
  294. var player = this.players[user];
  295. if (this.currentStep === STEP.NIGHT_LG && player && player.dead === null)
  296. this.voteEngine.vote(_this.config.privateChannel, player.name.toLowerCase(), text);
  297. });
  298. this.bot.bot.join(this.config.privateChannel);
  299. this.bot.setModes(this.room, "-m");
  300. };
  301. LoupGarou.prototype.onJoin = function(nick) {
  302. this.users[nick.toLowerCase()] = this.users[nick.toLowerCase()] || this.bot.createUser(nick);
  303. };
  304. LoupGarou.prototype.delaySetStep = function() {
  305. LoupGarou.prototype.setStep.delay = null;
  306. this.setStep(LoupGarou.prototype.setStep.nextStep);
  307. };
  308. LoupGarou.prototype.setStep = function(newStep) {
  309. if (LoupGarou.prototype.setStep.recursive === true) {
  310. LoupGarou.prototype.setStep.nextStep = newStep;
  311. if (!LoupGarou.prototype.setStep.delay)
  312. LoupGarou.prototype.setStep.delay = setTimeout(LoupGarou.prototype.delaySetStep.bind(this), 250);
  313. return;
  314. }
  315. LoupGarou.prototype.nextStep.messageDelayed = [];
  316. LoupGarou.prototype.setStep.recursive = true;
  317. this.timeInStep = Date.now() / 1000;
  318. this.clearReminder();
  319. this.currentStep = newStep;
  320. this.stepListeners[newStep] && this.stepListeners[newStep].some(i => i.call(this), this);
  321. LoupGarou.prototype.setStep.recursive = false;
  322. };
  323. LoupGarou.prototype.nextStep = function() {
  324. LoupGarou.prototype.nextStep.messageDelayed.forEach(i => this.bot.sendMsg(i[0], i[1]), this);
  325. this.setStep(this.currentStep == MAX_STEP ? STEP.NIGHT : (this.currentStep +1));
  326. };
  327. LoupGarou.prototype.nextStep.messageDelayed = [];
  328. LoupGarou.prototype.onMessage = function(user, message){
  329. switch (this.currentStep) {
  330. case STEP.REGISTRATION:
  331. if (message === "!loupgarou") {
  332. if (this.players[user.toLowerCase()]) {
  333. this.bot.sendMsg(this.room, `${user}, tu es déjà inscrit au Loup-Garou`);
  334. this.bot.voice(this.room, user);
  335. return;
  336. }
  337. var p = this.players[user.toLowerCase()] = new Player(this, user);
  338. this.bot.sendMsg(this.room, this.currentScenario.join(p.name));
  339. this.bot.voice(this.room, p.name);
  340. }
  341. }
  342. };
  343. LoupGarou.prototype.devoiceAll = function() {
  344. this.bot.devoice(this.room, Object.keys(this.users));
  345. }
  346. LoupGarou.prototype.voiceAll = function() {
  347. var players = [];
  348. for (var i in this.players)
  349. this.players[i].dead === null && players.push(this.players[i].name);
  350. this.bot.voice(this.room, players);
  351. }
  352. LoupGarou.prototype.clearReminder = function() {
  353. if (this.currentReminder)
  354. clearInterval(this.currentReminder);
  355. }
  356. LoupGarou.prototype.setReminder = function(cb) {
  357. this.clearReminder();
  358. if (this.config.reminderInterval > 0)
  359. this.currentReminder = setInterval(cb.bind(this), this.config.reminderInterval * 1000);
  360. }
  361. LoupGarou.prototype.onRegistration = function() {
  362. this.players = {};
  363. this.bot.sendMsg(this.room, "Pour vous inscrire à la prochaine partie de Loup Garou, tapez !loupgarou !");
  364. this.devoiceAll();
  365. this.bot.setModes(this.room, "-m");
  366. setTimeout((function() {this.setStep(STEP.SET_ROLES);}).bind(this), this.config.registrationDelay * 1000);
  367. this.setReminder(function() {
  368. var names = [];
  369. for (var i in this.players) names.push(this.players[i].name);
  370. this.bot.sendMsg(this.room, "Pour vous inscrire à la prochaine partie de Loup Garou avec " +names.join(", ") +", tapez !loupgarou ! Prochaine partie dans " +Math.round((this.config.registrationDelay -(Date.now() / 1000) +this.timeInStep)) +" secondes");
  371. });
  372. };
  373. LoupGarou.prototype.doSetupRoles = function(roleList) {
  374. roleList = roleList.sort(() => Math.random() -0.5);
  375. var i =0;
  376. for (var p in this.players) {
  377. var player = this.players[p];
  378. player.role = roleList[i];
  379. this.bot.sendMsg(player.name, this.currentScenario.advRole[player.role]);
  380. ++i;
  381. }
  382. };
  383. LoupGarou.prototype.setupRoles = function() {
  384. var nbPlayers = Object.keys(this.players).length;
  385. if (!ROLE_REPARTITION.some(roles => {
  386. if (roles.length === nbPlayers) {
  387. this.doSetupRoles(roles);
  388. return true;
  389. }
  390. }, this)) {
  391. // Error: not enought players !
  392. this.bot.sendMsg(this.room, "Il n'y a pas assez de personnes inscrites pour pouvoir jouer au Loup-Garou. Fin de la partie.");
  393. this.devoiceAll();
  394. console.info("Loup-Garou: not enought player");
  395. this.setStep(STEP.NOT_STARTED);
  396. return false;
  397. }
  398. return true;
  399. };
  400. LoupGarou.prototype.debug = function() {
  401. var debug = {};
  402. for (var i in this.players) debug[this.players[i].name] = {
  403. dead: this.players[i].dead,
  404. role: this.players[i].role
  405. };
  406. console.log(debug);
  407. };
  408. LoupGarou.prototype.isWolf = function(p) {
  409. return p.role === ROLES.WOLF;
  410. };
  411. LoupGarou.prototype.startGame = function() {
  412. // Setup roles
  413. if (!this.setupRoles())
  414. return;
  415. this.bot.setModes(this.room, "+m");
  416. this.devoiceAll();
  417. this.debug();
  418. // Invite wolfs to the private channel
  419. this.bot.sendMsg(this.room, this.currentScenario.inviteWolfRoom());
  420. for (var i in this.players) if (this.isWolf(this.players[i])) {
  421. var p = this.players[i];
  422. this.bot.sendMsg(p.name, this.currentScenario.inviteWolfRoomPrivate(this.config.privateChannel));
  423. this.bot.invite(this.config.privateChannel, p.name);
  424. }
  425. this.nextStep(); // cupidon
  426. };
  427. LoupGarou.prototype.withRole = function(r) {
  428. var result = [];
  429. for (var i in this.players)
  430. if (this.players[i].role === r && this.players[i].dead === null)
  431. result.push(this.players[i]);
  432. return result;
  433. };
  434. LoupGarou.prototype.setLovers = function(cupidon, p1, p2) {
  435. if (this.currentStep !== STEP.SELECT_CUPIDON)
  436. return;
  437. p1.lover = p2;
  438. p2.lover = p1;
  439. this.bot.sendMsg(cupidon.name, this.currentScenario.setLoveWith(p1.name, p2.name));
  440. if (p1 !== cupidon) LoupGarou.prototype.nextStep.messageDelayed.push([p1.name, this.currentScenario.inLoveWith(p2.name)]);
  441. if (p2 !== cupidon) LoupGarou.prototype.nextStep.messageDelayed.push([p2.name, this.currentScenario.inLoveWith(p1.name)]);
  442. };
  443. LoupGarou.prototype.aliveNicks = function() {
  444. var playerNames = [];
  445. for (var i in this.players)
  446. playerNames.push(this.players[i].name);
  447. return playerNames;
  448. };
  449. LoupGarou.prototype.aliveNotWolfNicks = function() {
  450. var playerNames = [];
  451. for (var i in this.players)
  452. if (!this.isWolf(this.players[i]))
  453. playerNames.push(this.players[i].name);
  454. return playerNames;
  455. };
  456. LoupGarou.prototype.sendCupidonMsg = function (targets) {
  457. var msg = this.currentScenario.cupidonMessage(this.aliveNicks());
  458. targets.forEach(i => this.bot.sendMsg(i.name, msg), this);
  459. };
  460. LoupGarou.prototype.onCupidonInternal = function(player, msg) {
  461. msg = msg.split(" ");
  462. if (msg.length === 2 && this.players[msg[0].trim().toLowerCase()] && this.players[msg[1].trim().toLowerCase()]) {
  463. var p1 = this.players[msg[0].trim().toLowerCase()],
  464. p2 = this.players[msg[1].trim().toLowerCase()];
  465. if (p1 && p2) {
  466. this.setLovers(player, p1, p2);
  467. player.jobDone = true;
  468. return;
  469. }
  470. }
  471. this.sendCupidonMsg([player]);
  472. };
  473. LoupGarou.prototype.onCupidon = function() {
  474. var players = this.withRole(ROLES.CUPIDON);
  475. if (players.length) {
  476. this.bot.sendMsg(this.room, this.currentScenario.waitingCupidon(this.config.cupidonDelay));
  477. this.sendCupidonMsg(players);
  478. setTimeout((function() {
  479. players.forEach(p => p.jobDone === null && this.bot.sendMsg(p.name, this.currentScenario.setNoLove()), this);
  480. this.nextStep();
  481. }).bind(this), this.config.cupidonDelay * 1000);
  482. }
  483. else
  484. this.nextStep();
  485. };
  486. LoupGarou.prototype.onSeerInternal = function(player, msg) {
  487. var req = msg.trim().toLowerCase();
  488. if (this.players[req] && this.players[req] !== player) {
  489. this.bot.sendMsg(player.name, this.currentScenario.whoiswhat[this.players[req].role](this.players[req].name));
  490. player.jobDone = true;
  491. } else {
  492. this.bot.sendMsg(player.name, this.currentScenario.seerMessage(this.aliveNicks().filter(nick => nick !== player.name)));
  493. }
  494. };
  495. LoupGarou.prototype.onSeer = function() {
  496. var players = this.withRole(ROLES.SEER);
  497. if (players.length) {
  498. this.bot.sendMsg(this.room, this.currentScenario.waitingSeer(this.config.seerDelay));
  499. players.forEach(i => {
  500. this.bot.sendMsg(i.name, this.currentScenario.seerMessage(this.aliveNicks().filter(nick => nick !== i.name)));
  501. }, this);
  502. setTimeout((function() {
  503. players.forEach(p => {
  504. if (p.jobDone === null)
  505. this.bot.sendMsg(p.name, this.currentScenario.seerTimeout());
  506. p.jobDone = null;
  507. }, this);
  508. this.nextStep();
  509. }).bind(this), this.config.cupidonDelay * 1000);
  510. } else {
  511. this.nextStep();
  512. }
  513. };
  514. LoupGarou.prototype.onGirlInternal = function(player) {
  515. //FIXME ...
  516. this.bot.sendMsg(this.room, "Attente petite fille"); // FIXME this.currentScenario
  517. this.nextStep();
  518. };
  519. LoupGarou.prototype.onGirl = function() {
  520. var players = this.withRole(ROLES.GIRL);
  521. if (players.length) {
  522. this.onGirlInternal(players[0]);
  523. } else {
  524. this.nextStep();
  525. }
  526. };
  527. LoupGarou.prototype.sendCurrentVotes = function(channel) {
  528. if (this.voteEngine.hasVotes()) {
  529. this.bot.sendMsg(channel, this.currentScenario.currentVotes());
  530. this.voteEngine.getVotes().forEach(i => {
  531. this.bot.sendMsg(channel, i.name +' : ' +i.from.join(", "));
  532. }, this);
  533. }
  534. else
  535. this.bot.sendMsg(channel, this.currentScenario.noVote());
  536. };
  537. LoupGarou.prototype.onWolf = function() {
  538. var players = this.withRole(ROLES.WOLF);
  539. if (players.length) {
  540. this.bot.voice(this.config.privateChannel, Object.keys(this.nightChannelPersons));
  541. this.bot.sendMsg(this.config.privateChannel, this.currentScenario.startWolfVotes());
  542. this.bot.sendMsg(this.config.privateChannel, this.currentScenario.remainingPseudos(this.aliveNotWolfNicks()));
  543. this.voteEngine.init(players.map(i => i.name), this.aliveNotWolfNicks());
  544. this.setReminder(function() {
  545. this.bot.sendMsg(this.room, this.currentScenario.waitingWolf(Math.round((this.config.wolfDelay -(Date.now() / 1000) +this.timeInStep))));
  546. this.bot.sendMsg(this.config.privateChannel, this.currentScenario.remainingPseudos(this.aliveNotWolfNicks()));
  547. this.sendCurrentVotes(this.config.privateChannel);
  548. });
  549. setTimeout((function() {
  550. this.bot.devoice(this.config.privateChannel, Object.keys(this.nightChannelPersons));
  551. if (!this.voteEngine.hasVotes()) {
  552. var msg = this.currentScenario.wolfTimeout();
  553. this.bot.sendMsg(this.room, msg);
  554. players.forEach(i => {
  555. this.bot.sendMsg(this.room, this.currentScenario.whowaswhat[i.role](i.name))
  556. i.kill(KILL_REASON.HUNGER, msg);
  557. });
  558. this.checkEndOfGameAndContinue(); // Will end game
  559. return;
  560. } else {
  561. var p = this.players[this.voteEngine.getVote()];
  562. p.kill(KILL_REASON.WOLF, this.currentScenario.killedWolf(p.name));
  563. this.bot.sendMsg(this.room, this.current);
  564. this.nextStep();
  565. }
  566. }).bind(this), this.config.wolfDelay * 1000);
  567. } else {
  568. this.nextStep();
  569. }
  570. };
  571. LoupGarou.prototype.onVillagers = function() {
  572. var remainingPlayers = [];
  573. for (var i in this.players)
  574. if (this.players[i].dead === null)
  575. remainingPlayers.push(this.players[i].name);
  576. this.bot.sendMsg(this.room, this.currentScenario.startVotes());
  577. this.bot.sendMsg(this.room, this.currentScenario.remainingPseudos(remainingPlayers.sort()));
  578. this.voiceAll();
  579. this.nextStep();
  580. };
  581. LoupGarou.prototype.onEvening = function() {
  582. this.devoiceAll();
  583. var anyone = [];
  584. for (var i in this.players) anyone.push(this.players[i]);
  585. anyone = anyone.sort(() => Math.random() -.5)[0];
  586. anyone.kill(KILL_REASON.VILLAGER, this.currentScenario.killedVillager(anyone.name));
  587. };
  588. LoupGarou.prototype.onWitchInternal = function(player) {
  589. //FIXME ...
  590. this.bot.sendMsg(this.room, "Attente sorciere"); // FIXME this.currentScenario
  591. this.nextStep();
  592. };
  593. LoupGarou.prototype.onWitch = function() {
  594. var players = this.withRole(ROLES.WITCH);
  595. if (players.length) {
  596. this.onWitchInternal(players[0]);
  597. } else {
  598. this.nextStep();
  599. }
  600. };
  601. LoupGarou.prototype.onLovers = function() {
  602. for (var i =0, len = this.justDead.length; i < len; ++i)
  603. if (this.justDead[i].lover)
  604. this.justDead[i].lover.kill(KILL_REASON.LOVE, this.currentScenario.killedLove(this.justDead[i].lover.name, this.justDead[i].name));
  605. this.nextStep();
  606. };
  607. LoupGarou.prototype.onHunter = function() {
  608. for (var i =0, len = this.justDead.length; i < len; ++i)
  609. if (this.justDead[i].role === ROLES.HUNTER)
  610. this.bot.sendMsg(this.justDead[i].name, "!pan"); // FIXME this.currentScenario...
  611. this.nextStep();
  612. };
  613. LoupGarou.prototype.cleanCorpses = function() {
  614. this.justDead = [];
  615. };
  616. LoupGarou.prototype.checkEndOfGameAndContinue = function() {
  617. if (this.checkEndOfGame())
  618. this.setStep(STEP.NOT_STARTED);
  619. else
  620. this.nextStep();
  621. };
  622. LoupGarou.prototype.checkEndOfGame = function() {
  623. var alive = [];
  624. for (var i in this.players) this.players[i].dead === null && alive.push(this.players[i]);
  625. console.log("alive => ", alive.map(i => i.name));
  626. if (alive.length === 2 && alive[0].lover === alive[1]) {
  627. this.bot.sendMsg(this.room, this.currentScenario.loverWins());
  628. this.bot.sendMsg(this.room, this.currentScenario.congrats(alive.map(i => i.name)));
  629. return true;
  630. }
  631. var liveWolfCount = alive.filter(i => this.isWolf(i), this).length;
  632. if (liveWolfCount === alive.length) {
  633. this.bot.sendMsg(this.room, this.currentScenario.wolfWins());
  634. this.bot.sendMsg(this.room, this.currentScenario.congrats(alive.map(i => i.name)));
  635. return true;
  636. }
  637. if(!liveWolfCount) {
  638. this.bot.sendMsg(this.room, this.currentScenario.villagerWins());
  639. this.bot.sendMsg(this.room, this.currentScenario.congrats(alive.map(i => i.name)));
  640. return true;
  641. }
  642. return false;
  643. };
  644. LoupGarou.prototype.advertiseDeath = function(player) {
  645. this.bot.sendMsg(this.room, player.killReason +" " +this.currentScenario.whowaswhat[player.role](player.name));
  646. };
  647. LoupGarou.prototype.onKilled = function(player) {
  648. if (this.currentStep < STEP.DAY)
  649. this.justDead.push(player);
  650. else
  651. this.advertiseDeath(player);
  652. };
  653. LoupGarou.prototype.onDay = function() {
  654. if (this.justDead.length === 0)
  655. this.bot.sendMsg(this.room, this.currentScenario.dayNoDead());
  656. else if (this.justDead.length === 1)
  657. this.bot.sendMsg(this.room, this.currentScenario.dayOneDead());
  658. else
  659. this.bot.sendMsg(this.room, this.currentScenario.dayMultiDead());
  660. this.justDead.forEach(i => this.advertiseDeath(i), this);
  661. };
  662. LoupGarou.prototype.onPm = function(from, text) {
  663. if (this.currentStep === STEP.NOT_STARTED)
  664. return;
  665. var player = this.players[from.toLowerCase()];
  666. if (!player)
  667. return;
  668. if (this.currentStep === STEP.SELECT_CUPIDON && player.role === ROLES.CUPIDON && player.jobDone === null)
  669. this.onCupidonInternal(player, text);
  670. else if (this.currentStep === STEP.NIGHT_SEER && player.role === ROLES.SEER && player.jobDone === null)
  671. this.onSeerInternal(player, text);
  672. };
  673. module.exports = LoupGarou;