]>
Commit | Line | Data |
---|---|---|
1 | -- labspace 1.0 | |
2 | -- Copyright (C) 2011 Gunnar Beutner | |
3 | -- | |
4 | -- This program is free software; you can redistribute it and/or | |
5 | -- modify it under the terms of the GNU General Public License | |
6 | -- as published by the Free Software Foundation; either version 2 | |
7 | -- of the License, or (at your option) any later version. | |
8 | -- | |
9 | -- This program is distributed in the hope that it will be useful, | |
10 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | -- GNU General Public License for more details. | |
13 | -- | |
14 | -- You should have received a copy of the GNU General Public License | |
15 | -- along with this program; if not, write to the Free Software | |
16 | -- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
17 | ||
18 | local BOTNICK = "labspace" | |
19 | local BOTACCOUNT = "labspace" | |
20 | local BOTACCOUNTID = 5022574 | |
21 | local HOMECHANNEL = "#labspace" | |
22 | local MINPLAYERS = 6 | |
23 | local MAXPLAYERS = 30 | |
24 | local DEBUG = false | |
25 | local DB = "labspace.db" | |
26 | -- Some settings. | |
27 | -- Minimum players for the idiot | |
28 | local MINPIDIOT = 10 | |
29 | -- Minimum players for the alien | |
30 | local MINPALIEN = 9 | |
31 | -- Minimum players for the force field generator | |
32 | local MINPFORCE = 8 | |
33 | -- Maximum players left for the alien to be disabled | |
34 | local MAXLALIEN = 5 | |
35 | ||
36 | local KILLMESSAGES = { | |
37 | "was brutally murdered.", | |
38 | "was vaporized by the scientist's death ray.", | |
39 | "slipped into a coma after drinking their poison-laced morning coffee.", | |
40 | "was crushed to death by a 5-ton boulder.", | |
41 | "couldn't escape from the scientist's killbot army.", | |
42 | "was torn apart by a group of ferocious labrats.", | |
43 | "was sucked into an artificial black hole.", | |
44 | "took a bath in concentrated sulfuric acid.", | |
45 | "'s house was leveled by an orbital ion cannon.", | |
46 | "got baked and there was no cake.", | |
47 | "was pushed into a portal leading to the sun." | |
48 | } | |
49 | ||
50 | local ls_bot | |
51 | local ls_hlbot | |
52 | local ls_gamestate = {} | |
53 | local ls_db = {} | |
54 | local ls_lastsave = 0 | |
55 | local ls_lastalivecheck = 0 | |
56 | local ls_sched = Scheduler() | |
57 | ||
58 | function onload() | |
59 | ls_dbload() | |
60 | onconnect() | |
61 | end | |
62 | ||
63 | function onunload() | |
64 | ls_dbsave() | |
65 | end | |
66 | ||
67 | function onconnect() | |
68 | ls_bot = irc_localregisteruserid(BOTNICK, "labspace", "brought.to.you.by.science", "For science!", BOTACCOUNT, BOTACCOUNTID, "+iXr", gamehandler) | |
69 | ls_join_channels() | |
70 | ||
71 | ls_hlbot = irc_localregisteruser("hl-" .. BOTNICK, "will.spam", "for.food", "Got some change?", "labspace-hl", "+iX", highlighthandler) | |
72 | irc_localjoin(ls_hlbot, HOMECHANNEL) | |
73 | end | |
74 | ||
75 | function ls_join_channels() | |
76 | local channel = irctolower(HOMECHANNEL) | |
77 | ls_add_channel(channel) | |
78 | ||
79 | for _, channel in pairs(ls_db.channels) do | |
80 | if not ls_is_game_channel(channel) then | |
81 | ls_add_channel(channel) | |
82 | end | |
83 | end | |
84 | end | |
85 | ||
86 | function ls_split_message(message) | |
87 | message, _ = message:gsub("^ +", "") | |
88 | message, _ = message:gsub(" +", " ") | |
89 | message, _ = message:gsub(" +$", "") | |
90 | ||
91 | local tokens = {} | |
92 | ||
93 | for token in string.gmatch(message, "%S+") do | |
94 | table.insert(tokens, token) | |
95 | end | |
96 | ||
97 | return tokens | |
98 | end | |
99 | ||
100 | function gamehandler(target, revent, ...) | |
101 | if revent == "irc_onchanmsg" then | |
102 | local numeric, channel, message = ... | |
103 | ||
104 | channel = irctolower(channel) | |
105 | ||
106 | if not ls_is_game_channel(channel) then | |
107 | return | |
108 | end | |
109 | ||
110 | ls_keepalive(channel, numeric) | |
111 | ||
112 | local tokens = ls_split_message(message) | |
113 | local command = tokens[1] | |
114 | ||
115 | if command then | |
116 | command = command:lower() | |
117 | ||
118 | if command == "!add" then | |
119 | ls_cmd_add(channel, numeric) | |
120 | elseif command == "!remove" then | |
121 | ls_cmd_remove(channel, numeric) | |
122 | elseif command == "!wait" then | |
123 | ls_cmd_wait(channel, numeric) | |
124 | elseif command == "!start" then | |
125 | ls_cmd_start(channel, numeric) | |
126 | elseif command == "!status" then | |
127 | ls_cmd_status(channel, numeric) | |
128 | elseif command == "!help" then | |
129 | ls_cmd_help(channel, numeric) | |
130 | elseif command == "!hl" then | |
131 | ls_cmd_hl(channel, numeric) | |
132 | elseif command == "!enable" then | |
133 | ls_cmd_enable(channel, numeric) | |
134 | elseif command == "!disable" then | |
135 | ls_cmd_disable(channel, numeric) | |
136 | end | |
137 | ||
138 | ls_flush_modes(channel) | |
139 | end | |
140 | elseif revent == "irc_onmsg" or revent == "irc_onnotice" then | |
141 | local numeric, message = ... | |
142 | ||
143 | local tokens = ls_split_message(message) | |
144 | ||
145 | local command = tokens[1]:lower() | |
146 | local argument = tokens[2] | |
147 | ||
148 | if command then | |
149 | if command == "kill" then | |
150 | ls_cmd_kill(numeric, argument) | |
151 | elseif command == "investigate" then | |
152 | ls_cmd_investigate(numeric, argument) | |
153 | elseif command == "vote" then | |
154 | ls_cmd_vote(numeric, argument) | |
155 | elseif command == "guard" then | |
156 | ls_cmd_guard(numeric, argument) | |
157 | elseif command == "note" then | |
158 | local message | |
159 | ||
160 | if table.getn(tokens) > 1 then | |
161 | message = table.concat(tokens, " ", 2) | |
162 | end | |
163 | ||
164 | ls_cmd_note(numeric, message) | |
165 | elseif command == "stats" then | |
166 | ls_cmd_stats(numeric, argument) | |
167 | elseif command == "help" then | |
168 | ls_cmd_msghelp(numeric, argument) | |
169 | elseif command == "showcommands" then | |
170 | ls_cmd_msgshowcommands(numeric, argument) | |
171 | elseif command == "smite" and onstaff(numeric) then | |
172 | ls_cmd_smite(numeric, argument) | |
173 | elseif command == "killgame" and onstaff(numeric) then | |
174 | ls_cmd_killgame(numeric, argument) | |
175 | elseif command == "killmessage" and onstaff(numeric) then | |
176 | local message | |
177 | ||
178 | if table.getn(tokens) > 2 then | |
179 | message = table.concat(tokens, " ", 3) | |
180 | end | |
181 | ||
182 | ls_cmd_killmessage(numeric, argument, message) | |
183 | elseif command == "addchan" and ontlz(numeric) then | |
184 | ls_cmd_addchan(numeric, argument) | |
185 | elseif command == "delchan" and ontlz(numeric) then | |
186 | ls_cmd_delchan(numeric, argument) | |
187 | elseif command == "bomb" and ontlz(numeric) then | |
188 | ls_cmd_bomb(numeric, argument) | |
189 | else | |
190 | ls_notice(numeric, "Not sure which command you're looking for, try /msg " .. BOTNICK .. " showcommands.") | |
191 | end | |
192 | end | |
193 | elseif revent == "irc_onkilled" then | |
194 | ls_bot = nil | |
195 | ls_gamestate = {} | |
196 | elseif revent == "irc_onkillreconnect" then | |
197 | ls_bot = target | |
198 | ls_join_channels() | |
199 | end | |
200 | end | |
201 | ||
202 | function highlighthandler(target, revent, ...) | |
203 | if revent == "irc_onkilled" then | |
204 | ls_hlbot = nil | |
205 | elseif revent == "irc_onkillreconnect" then | |
206 | ls_hlbot = target | |
207 | irc_localjoin(ls_hlbot, HOMECHANNEL) | |
208 | end | |
209 | end | |
210 | ||
211 | function irc_onpart(channel, numeric, message) | |
212 | channel = irctolower(channel) | |
213 | ||
214 | if not ls_is_game_channel(channel) then | |
215 | return | |
216 | end | |
217 | ||
218 | if ls_get_role(channel, numeric) then | |
219 | if ls_get_role(channel, numeric) ~= "lobby" then | |
220 | ls_incr_stats_user(numeric, "killed_suicide") | |
221 | end | |
222 | ||
223 | ls_remove_player(channel, numeric) | |
224 | ls_advance_state(channel) | |
225 | end | |
226 | end | |
227 | ||
228 | function irc_onkick(channel, kicked_numeric, kicker_numeric, message) | |
229 | channel = irctolower(channel) | |
230 | ||
231 | if not ls_is_game_channel(channel) then | |
232 | return | |
233 | end | |
234 | ||
235 | if ls_bot == kicked_numeric then | |
236 | ls_remove_channel(channel) | |
237 | return | |
238 | end | |
239 | ||
240 | if ls_get_role(channel, kicked_numeric) then | |
241 | if ls_get_role(channel, numeric) ~= "lobby" then | |
242 | ls_incr_stats_user(numeric, "killed_suicide") | |
243 | end | |
244 | ||
245 | ls_remove_player(channel, kicked_numeric) | |
246 | ls_advance_state(channel) | |
247 | end | |
248 | end | |
249 | irc_onkickall = irc_onkick | |
250 | ||
251 | function irc_onquit(numeric) | |
252 | for channel, _ in pairs(ls_gamestate) do | |
253 | if ls_get_role(channel, numeric) then | |
254 | if ls_get_role(channel, numeric) ~= "lobby" then | |
255 | ls_incr_stats_user(numeric, "killed_suicide") | |
256 | end | |
257 | ||
258 | ls_remove_player(channel, numeric) | |
259 | ls_advance_state(channel) | |
260 | end | |
261 | end | |
262 | end | |
263 | ||
264 | function ontick() | |
265 | for channel, _ in pairs(ls_gamestate) do | |
266 | ls_advance_state(channel, true) | |
267 | ls_flush_modes(channel) | |
268 | end | |
269 | ||
270 | if ls_lastalivecheck < os.time() - 30 then | |
271 | ls_lastalivecheck = os.time() | |
272 | ||
273 | for channel, _ in pairs(ls_gamestate) do | |
274 | ls_check_alive(channel) | |
275 | end | |
276 | end | |
277 | ||
278 | for channel, _ in pairs(ls_gamestate) do | |
279 | ls_check_shield(channel) | |
280 | end | |
281 | ||
282 | if ls_lastsave < os.time() - 60 then | |
283 | ls_lastsave = os.time() | |
284 | ls_dbsave() | |
285 | end | |
286 | end | |
287 | ||
288 | -- sends a debug message | |
289 | function ls_debug(channel, message) | |
290 | if DEBUG then | |
291 | irc_localchanmsg(ls_bot, channel, "DEBUG: " .. message) | |
292 | end | |
293 | end | |
294 | ||
295 | -- sends a notice to the specified target | |
296 | function ls_notice(numeric, text) | |
297 | irc_localnotice(ls_bot, numeric, text) | |
298 | end | |
299 | ||
300 | -- sends a message to the specified target | |
301 | function ls_chanmsg(channel, text) | |
302 | irc_localchanmsg(ls_bot, channel, text) | |
303 | end | |
304 | ||
305 | -- formats the specified role identifier for output in a message | |
306 | function ls_format_role(role) | |
307 | if role == "scientist" then | |
308 | return "Mad Scientist" | |
309 | elseif role == "investigator" then | |
310 | return "Investigator" | |
311 | elseif role == "citizen" then | |
312 | return "Citizen" | |
313 | elseif role == "idiot" then | |
314 | return "Village Idiot" | |
315 | elseif role == "lobby" then | |
316 | return "Lobby" | |
317 | else | |
318 | return "Unknown Role" | |
319 | end | |
320 | end | |
321 | ||
322 | -- formats the specified trait identifier for output in a message | |
323 | function ls_format_trait(trait) | |
324 | if trait == "teleporter" then | |
325 | return "Portal Device" | |
326 | elseif trait == "infested" then | |
327 | return "Alien Parasite" | |
328 | elseif trait == "force" then | |
329 | return "Force Field Generator" | |
330 | elseif trait == "note" then | |
331 | return "Pen & Paper" | |
332 | else | |
333 | return "Unknown Trait" | |
334 | end | |
335 | end | |
336 | ||
337 | -- formats the specified player name for output in a message (optionally | |
338 | -- revealing that player's role and their traits in the game) | |
339 | function ls_format_player(channel, numeric, reveal_role, reveal_traits) | |
340 | local nick = irc_getnickbynumeric(numeric) | |
341 | local result = "\002" .. nick.nick .. "\002" | |
342 | ||
343 | if reveal_role then | |
344 | if ls_get_hiddenrole(channel) then | |
345 | result = result .. " (???)" | |
346 | return result | |
347 | end | |
348 | ||
349 | result = result .. " (" .. ls_format_role(ls_get_role(channel, numeric)) | |
350 | ||
351 | if reveal_traits then | |
352 | for _, trait in pairs(ls_get_traits(channel, numeric)) do | |
353 | if ls_get_trait(channel, numeric, trait) then | |
354 | result = result .. ", " .. ls_format_trait(trait) | |
355 | end | |
356 | end | |
357 | end | |
358 | ||
359 | result = result .. ")" | |
360 | end | |
361 | ||
362 | return result | |
363 | end | |
364 | ||
365 | -- formats a list of player names for output in a message (optionally | |
366 | -- revealing their roles and traits in the game) | |
367 | function ls_format_players(channel, numerics, reveal_role, reveal_traits, no_and) | |
368 | local i = 0 | |
369 | local result = "" | |
370 | ||
371 | for _, numeric in pairs(numerics) do | |
372 | if i ~= 0 then | |
373 | if not no_and and i == table.getn(numerics) - 1 then | |
374 | result = result .. " and " | |
375 | else | |
376 | result = result .. ", " | |
377 | end | |
378 | end | |
379 | ||
380 | result = result .. ls_format_player(channel, numeric, reveal_role, reveal_traits) | |
381 | i = i + 1 | |
382 | end | |
383 | ||
384 | return result | |
385 | end | |
386 | ||
387 | -- returns the current state of the game | |
388 | function ls_get_state(channel) | |
389 | return ls_gamestate[channel]["state"] | |
390 | end | |
391 | ||
392 | function ls_get_startts(channel) | |
393 | return ls_gamestate[channel]["startts"] | |
394 | end | |
395 | ||
396 | function ls_get_overloadts(channel) | |
397 | return ls_gamestate[channel]["overloadts"] | |
398 | end | |
399 | ||
400 | -- gets the hidden role flag | |
401 | function ls_get_hiddenrole(channel) | |
402 | return ls_gamestate[channel]["hiddenrole"] | |
403 | end | |
404 | ||
405 | -- gets the timeout for the current state | |
406 | function ls_get_timeout(channel) | |
407 | return ls_gamestate[channel]["timeout"] | |
408 | end | |
409 | ||
410 | -- gets the delay for the current state | |
411 | function ls_get_delay(channel) | |
412 | return ls_gamestate[channel]["delay"] | |
413 | end | |
414 | ||
415 | -- gets the ts when !hl was last used | |
416 | function ls_get_lasthl(channel) | |
417 | return ls_gamestate[channel]["lasthl"] | |
418 | end | |
419 | ||
420 | -- gets whether the bot is enabled | |
421 | function ls_get_enabled(channel) | |
422 | return ls_gamestate[channel]["enabled"] | |
423 | end | |
424 | ||
425 | -- returns true if the game state delay was exceeded, false otherwise | |
426 | function ls_delay_exceeded(channel) | |
427 | return ls_get_delay(channel) < os.time() | |
428 | end | |
429 | ||
430 | function ls_get_waitcount(channel) | |
431 | return ls_gamestate[channel]["waitcount"] | |
432 | end | |
433 | ||
434 | function ls_get_round(channel) | |
435 | return ls_gamestate[channel]["round"] | |
436 | end | |
437 | ||
438 | -- sets the game state | |
439 | function ls_set_state(channel, state) | |
440 | ls_gamestate[channel]["state"] = state | |
441 | ||
442 | ls_set_timeout(channel, -1) | |
443 | ls_set_delay(channel, 30) | |
444 | end | |
445 | ||
446 | function ls_set_startts(channel, startts) | |
447 | ls_gamestate[channel]["startts"] = startts | |
448 | end | |
449 | ||
450 | function ls_set_overloadts(channel, overloadts) | |
451 | ls_gamestate[channel]["overloadts"] = overloadts | |
452 | end | |
453 | ||
454 | -- sets the hiddenrole flag | |
455 | function ls_set_hiddenrole(channel, enabled) | |
456 | ls_gamestate[channel]["hiddenrole"] = enabled | |
457 | end | |
458 | ||
459 | -- sets the game state timeout (in seconds) | |
460 | function ls_set_timeout(channel, timeout) | |
461 | if timeout == -1 then | |
462 | ls_gamestate[channel]["timeout"] = -1 | |
463 | else | |
464 | ls_gamestate[channel]["timeout"] = os.time() + timeout | |
465 | end | |
466 | end | |
467 | ||
468 | -- sets the game state delay (in seconds) | |
469 | function ls_set_delay(channel, delay) | |
470 | ls_gamestate[channel]["delay"] = os.time() + delay | |
471 | ls_debug(channel, "changed gamestate delay to " .. delay) | |
472 | end | |
473 | ||
474 | -- sets the !hl timestamp | |
475 | function ls_set_lasthl(channel, ts) | |
476 | ls_gamestate[channel]["lasthl"] = ts | |
477 | end | |
478 | ||
479 | -- sets whether the bot is enabled | |
480 | function ls_set_enabled(channel, enabled) | |
481 | ls_gamestate[channel]["enabled"] = enabled | |
482 | end | |
483 | ||
484 | function ls_set_waitcount(channel, count) | |
485 | ls_gamestate[channel]["waitcount"] = count | |
486 | end | |
487 | ||
488 | function ls_set_round(channel, number) | |
489 | ls_gamestate[channel]["round"] = number | |
490 | end | |
491 | ||
492 | -- returns true if the game state timeout was exceeded, false otherwise | |
493 | function ls_timeout_exceeded(channel) | |
494 | local timeout = ls_get_timeout(channel) | |
495 | ||
496 | return timeout ~= -1 and timeout < os.time() | |
497 | end | |
498 | ||
499 | -- returns true if there's a game in progress, false otherwise | |
500 | function ls_game_in_progress(channel) | |
501 | return ls_get_state(channel) ~= "lobby" | |
502 | end | |
503 | ||
504 | -- returns the name of the channel the specified nick is playing on | |
505 | -- if the nick isn't playing any games nil is returned instead | |
506 | function ls_chan_for_numeric(numeric) | |
507 | for channel, _ in pairs(ls_gamestate) do | |
508 | if ls_get_role(channel, numeric) then | |
509 | return channel | |
510 | end | |
511 | end | |
512 | ||
513 | return nil | |
514 | end | |
515 | ||
516 | function ls_get_stats_channel(channel, key) | |
517 | if not ls_db.stats_channel then | |
518 | return 0 | |
519 | end | |
520 | ||
521 | if not ls_db.stats_channel[channel] then | |
522 | return 0 | |
523 | end | |
524 | ||
525 | return ls_db.stats_channel[channel][key] | |
526 | end | |
527 | ||
528 | function ls_incr_stats_channel(channel, key, num) | |
529 | if not ls_db.stats_channel then | |
530 | ls_db.stats_channel = {} | |
531 | end | |
532 | ||
533 | if not ls_db.stats_channel[channel] then | |
534 | ls_db.stats_channel[channel] = {} | |
535 | end | |
536 | ||
537 | local value = ls_db.stats_channel[channel][key] | |
538 | ||
539 | if not value then | |
540 | value = 0 | |
541 | end | |
542 | ||
543 | if num then | |
544 | value = value + num | |
545 | else | |
546 | value = value + 1 | |
547 | end | |
548 | ||
549 | ls_db.stats_channel[channel][key] = value | |
550 | end | |
551 | ||
552 | function ls_get_stats_user(numeric, key) | |
553 | if not ls_db.stats_user then | |
554 | return 0 | |
555 | end | |
556 | ||
557 | local nick = irc_getnickbynumeric(numeric) | |
558 | local accountid = nick.accountid | |
559 | ||
560 | if not accountid then | |
561 | accountid = -1 | |
562 | end | |
563 | ||
564 | if not ls_db.stats_user[accountid] then | |
565 | return 0 | |
566 | end | |
567 | ||
568 | if not ls_db.stats_user[accountid][key] then | |
569 | return 0 | |
570 | end | |
571 | ||
572 | return ls_db.stats_user[accountid][key] | |
573 | end | |
574 | ||
575 | function ls_get_stats_user_aggregate(key) | |
576 | if not ls_db.stats_user then | |
577 | return 0 | |
578 | end | |
579 | ||
580 | local value = 0 | |
581 | ||
582 | for accountid, _ in pairs(ls_db.stats_user) do | |
583 | if ls_db.stats_user[accountid][key] then | |
584 | value = value + ls_db.stats_user[accountid][key] | |
585 | end | |
586 | end | |
587 | ||
588 | return value | |
589 | end | |
590 | ||
591 | function ls_incr_stats_user(numeric, key, num) | |
592 | local nick = irc_getnickbynumeric(numeric) | |
593 | local accountid = nick.accountid | |
594 | ||
595 | if not accountid then | |
596 | accountid = -1 | |
597 | end | |
598 | ||
599 | if not ls_db.stats_user then | |
600 | ls_db.stats_user = {} | |
601 | end | |
602 | ||
603 | if not ls_db.stats_user[accountid] then | |
604 | ls_db.stats_user[accountid] = {} | |
605 | end | |
606 | ||
607 | local value = ls_db.stats_user[accountid][key] | |
608 | ||
609 | if not value then | |
610 | value = 0 | |
611 | end | |
612 | ||
613 | if num then | |
614 | value = value + num | |
615 | else | |
616 | value = value + 1 | |
617 | end | |
618 | ||
619 | ls_db.stats_user[accountid][key] = value | |
620 | end | |
621 | ||
622 | function ls_get_killmessage(numeric) | |
623 | if not ls_db.killmessages then | |
624 | return nil | |
625 | end | |
626 | ||
627 | local nick = irc_getnickbynumeric(numeric) | |
628 | local accountid = nick.accountid | |
629 | ||
630 | if not accountid then | |
631 | return nil | |
632 | end | |
633 | ||
634 | return ls_db.killmessages[accountid] | |
635 | end | |
636 | ||
637 | function ls_set_killmessage(numeric, message) | |
638 | local nick = irc_getnickbynumeric(numeric) | |
639 | local accountid = nick.accountid | |
640 | ||
641 | if not accountid then | |
642 | return | |
643 | end | |
644 | ||
645 | if not ls_db.killmessages then | |
646 | ls_db.killmessages = {} | |
647 | end | |
648 | ||
649 | ls_db.killmessages[accountid] = message | |
650 | end | |
651 | ||
652 | function ls_cmd_add(channel, numeric) | |
653 | ls_add_player(channel, numeric) | |
654 | end | |
655 | ||
656 | function ls_cmd_remove(channel, numeric) | |
657 | ls_remove_player(channel, numeric) | |
658 | end | |
659 | ||
660 | function ls_cmd_wait(channel, numeric) | |
661 | if ls_game_in_progress(channel) then | |
662 | ls_notice(numeric, "Sorry, there's no lobby at the moment.") | |
663 | return | |
664 | end | |
665 | ||
666 | if table.getn(ls_get_players(channel)) >= MINPLAYERS and not onstaff(numeric) then | |
667 | local count = ls_get_waitcount(channel) | |
668 | ||
669 | if count >= 2 then | |
670 | ls_notice(numeric, "Sorry, the timeout can only be extended twice per game.") | |
671 | return | |
672 | end | |
673 | ||
674 | ls_set_waitcount(channel, count + 1) | |
675 | end | |
676 | ||
677 | if not ls_get_role(channel, numeric) then | |
678 | ls_notice(numeric, "Sorry, you need to be in the lobby to use this command.") | |
679 | return | |
680 | end | |
681 | ||
682 | ls_set_timeout(channel, 120) | |
683 | ls_set_delay(channel, 45) | |
684 | ||
685 | ls_chanmsg(channel, "Lobby timeout was reset - waiting for another 120 seconds.") | |
686 | if table.getn(ls_get_players(channel)) >= MINPLAYERS then | |
687 | ls_chanmsg(channel, "To start the game immediately please use !start") | |
688 | end | |
689 | end | |
690 | ||
691 | function ls_cmd_start(channel, numeric) | |
692 | if ls_game_in_progress(channel) then | |
693 | ls_notice(numeric, "Sorry, there's no lobby at the moment.") | |
694 | return | |
695 | end | |
696 | ||
697 | if not ls_get_role(channel, numeric) then | |
698 | ls_notice(numeric, "Sorry, you need to be in the lobby to use this command.") | |
699 | return | |
700 | end | |
701 | ||
702 | ls_advance_state(channel) | |
703 | ||
704 | ls_flush_modes(channel) | |
705 | end | |
706 | ||
707 | function ls_cmd_status(channel, numeric) | |
708 | if not ls_get_role(channel, numeric) then | |
709 | ls_notice(numeric, "Sorry, you need to be in the lobby to use this command.") | |
710 | return | |
711 | end | |
712 | ||
713 | ls_show_status(channel) | |
714 | end | |
715 | ||
716 | ||
717 | function ls_show_status(channel) | |
718 | ls_chanmsg(channel, "Players: " .. ls_format_players(channel, ls_get_players(channel))) | |
719 | ||
720 | if ls_game_in_progress(channel) then | |
721 | if not ls_get_hiddenrole(channel) then | |
722 | ls_chanmsg(channel, "Roles: " .. | |
723 | table.getn(ls_get_players(channel, "scientist")) .. "x " .. ls_format_role("scientist") .. ", " .. | |
724 | table.getn(ls_get_players(channel, "investigator")) .. "x " .. ls_format_role("investigator") .. ", " .. | |
725 | table.getn(ls_get_players(channel, "idiot")) .. "x " .. ls_format_role("idiot") .. ", " .. | |
726 | table.getn(ls_get_players(channel, "citizen")) .. "x " .. ls_format_role("citizen")) | |
727 | end | |
728 | ||
729 | if ls_get_state(channel) == "vote" then | |
730 | local voteresult = ls_get_vote_result(channel, false) | |
731 | if table.getn(voteresult.votees) > 0 then | |
732 | ls_show_votes(channel, voteresult, false) | |
733 | end | |
734 | end | |
735 | end | |
736 | end | |
737 | ||
738 | function ls_show_votes(channel, voteresult, final) | |
739 | local prefix = "" | |
740 | ||
741 | if final then | |
742 | prefix = "Final" | |
743 | else | |
744 | prefix = "Current" | |
745 | end | |
746 | ||
747 | if table.getn(voteresult.votees) > 0 then | |
748 | ls_chanmsg(channel, prefix .. " votes: " .. ls_format_votes(voteresult.votes, voteresult.votees)) | |
749 | if not final and table.getn(voteresult.missing_votes) > 0 then | |
750 | ls_chanmsg(channel, "Participants that still need to vote: " .. ls_format_players(channel, voteresult.missing_votes)) | |
751 | end | |
752 | end | |
753 | end | |
754 | ||
755 | function ls_cmd_help(channel, numeric) | |
756 | ls_notice(numeric, "Read the guide at http://goo.gl/XUyPf") | |
757 | ls_notice(numeric, "If you have further questions, feel free to ask in " .. HOMECHANNEL) | |
758 | end | |
759 | ||
760 | function ls_cmd_hl(channel, numeric) | |
761 | if ls_game_in_progress(channel) then | |
762 | ls_notice(numeric, "Sorry, there's no lobby at the moment.") | |
763 | return | |
764 | end | |
765 | ||
766 | if not ls_get_role(channel, numeric) then | |
767 | ls_notice(numeric, "Sorry, you need to be in the lobby to use this command.") | |
768 | return | |
769 | end | |
770 | ||
771 | if ls_get_lasthl(channel) > os.time() - 300 then | |
772 | ls_notice(numeric, "Sorry, you can only use that command once every 5 minute.") | |
773 | return | |
774 | end | |
775 | ||
776 | if string.lower(channel) ~= string.lower(HOMECHANNEL) then | |
777 | ls_notice(numeric, "Sorry, you can't use this command here.") | |
778 | return | |
779 | end | |
780 | ||
781 | ls_set_lasthl(channel, os.time()) | |
782 | ||
783 | local numerics = {} | |
784 | ||
785 | for nick in channelusers_iter(channel, { nickpusher.numeric }) do | |
786 | local numeric = nick[1] | |
787 | ||
788 | if not ls_get_role(channel, numeric) then | |
789 | table.insert(numerics, numeric) | |
790 | end | |
791 | ||
792 | if table.getn(numerics) > 10 then | |
793 | irc_localchanmsg(ls_hlbot, channel, "HL: " .. ls_format_players(channel, numerics, false, false, true)) | |
794 | numerics = {} | |
795 | end | |
796 | end | |
797 | ||
798 | if table.getn(numerics) > 0 then | |
799 | irc_localchanmsg(ls_hlbot, channel, "HL: " .. ls_format_players(channel, numerics, false, false, true)) | |
800 | end | |
801 | ls_chanmsg(channel, "Lobby timeout was reset - waiting for another 120 seconds.") | |
802 | ||
803 | ls_set_timeout(channel, 120) | |
804 | ls_set_delay(channel, 45) | |
805 | end | |
806 | ||
807 | function ls_cmd_enable(channel, numeric) | |
808 | local chanuser = irc_getuserchanmodes(numeric, channel) | |
809 | ||
810 | if (not chanuser or not chanuser.opped) and not onstaff(numeric) then | |
811 | ls_notice(numeric, "You need to be opped to use this command.") | |
812 | return | |
813 | end | |
814 | ||
815 | ls_set_enabled(channel, true) | |
816 | ls_notice(numeric, "Game has been enabled.") | |
817 | end | |
818 | ||
819 | function ls_cmd_disable(channel, numeric) | |
820 | local chanuser = irc_getuserchanmodes(numeric, channel) | |
821 | ||
822 | if (not chanuser or not chanuser.opped) and not onstaff(numeric) then | |
823 | ls_notice(numeric, "You need to be opped to use this command.") | |
824 | return | |
825 | end | |
826 | ||
827 | if ls_game_in_progress(channel) then | |
828 | ls_chanmsg(channel, ls_format_player(channel, numeric) .. " disabled the game.") | |
829 | end | |
830 | ||
831 | ls_stop_game(channel) | |
832 | ls_flush_modes(channel) | |
833 | ||
834 | ls_set_enabled(channel, false) | |
835 | ls_notice(numeric, "Game has been disabled.") | |
836 | end | |
837 | ||
838 | function ls_cmd_kill(numeric, victim) | |
839 | if not victim then | |
840 | ls_notice(numeric, "Syntax: kill <nick>") | |
841 | return | |
842 | end | |
843 | ||
844 | local channel = ls_chan_for_numeric(numeric) | |
845 | ||
846 | if not channel then | |
847 | ls_notice(numeric, "You haven't joined any game lobby.") | |
848 | return | |
849 | end | |
850 | ||
851 | if ls_get_role(channel, numeric) ~= "scientist" then | |
852 | ls_notice(numeric, "You need to be a scientist to use this command.") | |
853 | return | |
854 | end | |
855 | ||
856 | if ls_get_state(channel) ~= "kill" then | |
857 | ls_notice(numeric, "Sorry, you can't use this command right now.") | |
858 | return | |
859 | end | |
860 | ||
861 | if not ls_get_active(channel, numeric) then | |
862 | ls_notice(numeric, "Sorry, it's not your turn to choose a victim.") | |
863 | return | |
864 | end | |
865 | ||
866 | local victimnick = irc_getnickbynick(victim) | |
867 | ||
868 | if not victimnick then | |
869 | ls_notice(numeric, "Sorry, I don't know who that is.") | |
870 | return | |
871 | end | |
872 | ||
873 | local victimnumeric = victimnick.numeric | |
874 | ||
875 | if not ls_get_role(channel, victimnumeric) then | |
876 | ls_notice(numeric, "Sorry, " .. ls_format_player(channel, victimnumeric) .. " isn't playing the game.") | |
877 | return | |
878 | end | |
879 | ||
880 | if math.random(100) > 85 then | |
881 | ls_incr_stats_user(numeric, "failed_chance") | |
882 | ls_incr_stats_user(victimnumeric, "survived_chance") | |
883 | ||
884 | ls_chanmsg(channel, "The scientists' attack was not successful tonight. Nobody died.") | |
885 | elseif ls_get_guarded(channel, victimnumeric) then | |
886 | for _, player in pairs(ls_get_players(channel)) do | |
887 | ls_set_trait(channel, player, "force", false) | |
888 | end | |
889 | ||
890 | ls_notice(victimnumeric, "You are no longer being protected by a \002force field\002.") | |
891 | ls_set_guarded(channel, victimnumeric, false) | |
892 | ||
893 | ls_incr_stats_user(numeric, "failed_guarded") | |
894 | ls_incr_stats_user(victimnumeric, "survived_guarded") | |
895 | ||
896 | ls_chanmsg(channel, "The attack on " .. ls_format_player(channel, victimnumeric) .. " was deflected by a force field. The force field generator has now run out of power.") | |
897 | elseif ls_get_trait(channel, victimnumeric, "infested") then | |
898 | ls_devoice_player(channel, numeric) | |
899 | ls_devoice_player(channel, victimnumeric) | |
900 | ||
901 | ls_chanmsg(channel, "An alien bursts out of " .. ls_format_player(channel, victimnumeric, true) .. "'s chest just as " .. ls_format_player(channel, numeric, true) .. " was about to murder them, killing them both.") | |
902 | ||
903 | if ls_get_trait(channel, victimnumeric, "note") then | |
904 | ls_chanmsg(channel, ls_format_player(channel, victimnumeric) .. " seems to have had a note on him but there are teeth marks and unintelligible blotches of ink all over it.") | |
905 | end | |
906 | ||
907 | ls_incr_stats_user(numeric, "kill_infested") | |
908 | ls_incr_stats_user(numeric, "killed_scientist") | |
909 | ls_incr_stats_user(victimnumeric, "kill_scientist") | |
910 | ls_incr_stats_user(victimnumeric, "killed_infested") | |
911 | ||
912 | ls_remove_player(channel, numeric, true) | |
913 | ls_remove_player(channel, victimnumeric, true) | |
914 | else | |
915 | ls_devoice_player(channel, victimnumeric) | |
916 | ||
917 | if numeric == victimnumeric then | |
918 | ls_incr_stats_user(numeric, "killed_suicide") | |
919 | ||
920 | ls_chanmsg(channel, ls_format_player(channel, victimnumeric, true) .. " committed suicide.") | |
921 | else | |
922 | ls_incr_stats_user(numeric, "kill_scientist") | |
923 | ls_incr_stats_user(numeric, "killed_scientist") | |
924 | ||
925 | if ls_get_role(channel, victimnumeric) == "scientist" then | |
926 | ls_chanmsg(channel, ls_format_player(channel, victimnumeric, true) .. " was brutally murdered. Oops.") | |
927 | ||
928 | if ls_get_note(channel, victimnumeric) then | |
929 | ls_chanmsg(channel, "He seems to have left us a note: " .. ls_get_note(channel, victimnumeric)) | |
930 | end | |
931 | else | |
932 | local killmessage = KILLMESSAGES[math.random(table.getn(KILLMESSAGES))] | |
933 | ||
934 | local custom_killmessage = ls_get_killmessage(victimnumeric) | |
935 | ||
936 | if custom_killmessage and math.random(100) > 66 then | |
937 | killmessage = custom_killmessage | |
938 | end | |
939 | ||
940 | local space = " " | |
941 | ||
942 | if string.sub(killmessage, 1, 1) == "'" then | |
943 | space = "" | |
944 | end | |
945 | ||
946 | ls_chanmsg(channel, ls_format_player(channel, victimnumeric, true) .. space .. killmessage) | |
947 | end | |
948 | end | |
949 | ||
950 | if ls_get_note(channel, victimnumeric) then | |
951 | ls_chanmsg(channel, "The citizens find a note next to the body: " .. ls_get_note(channel, victimnumeric)) | |
952 | end | |
953 | ||
954 | ls_remove_player(channel, victimnumeric, true) | |
955 | end | |
956 | ||
957 | ls_set_state(channel, "investigate") | |
958 | ls_advance_state(channel) | |
959 | ||
960 | ls_flush_modes(channel) | |
961 | end | |
962 | ||
963 | function ls_cmd_investigate(numeric, victim) | |
964 | if not victim then | |
965 | ls_notice(numeric, "Syntax: investigate <nick>") | |
966 | return | |
967 | end | |
968 | ||
969 | local channel = ls_chan_for_numeric(numeric) | |
970 | ||
971 | if not channel then | |
972 | ls_notice(numeric, "You haven't joined any game lobby.") | |
973 | return | |
974 | end | |
975 | ||
976 | if ls_get_role(channel, numeric) ~= "investigator" then | |
977 | ls_notice(numeric, "You need to be an investigator to use this command.") | |
978 | return | |
979 | end | |
980 | ||
981 | if ls_get_state(channel) ~= "investigate" then | |
982 | ls_notice(numeric, "Sorry, you can't use this command right now.") | |
983 | return | |
984 | end | |
985 | ||
986 | if not ls_get_active(channel, numeric) then | |
987 | ls_notice(numeric, "Sorry, it's not your turn to choose an investigation target.") | |
988 | return | |
989 | end | |
990 | ||
991 | local victimnick = irc_getnickbynick(victim) | |
992 | ||
993 | if not victimnick then | |
994 | ls_notice(numeric, "Sorry, I don't know who that is.") | |
995 | return | |
996 | end | |
997 | ||
998 | local victimnumeric = victimnick.numeric | |
999 | ||
1000 | if not ls_get_role(channel, victimnumeric) then | |
1001 | ls_notice(numeric, "Sorry, " .. ls_format_player(channel, victimnumeric) .. " isn't playing the game.") | |
1002 | return | |
1003 | end | |
1004 | ||
1005 | local investigators = ls_get_players(channel, "investigator") | |
1006 | ||
1007 | for _, investigator in pairs(investigators) do | |
1008 | if investigator ~= numeric then | |
1009 | ls_notice(investigator, ls_format_player(channel, numeric) .. " investigated " .. ls_format_player(channel, victimnumeric) .. ". Their role is: " .. ls_format_role(ls_get_role(channel, victimnumeric))) | |
1010 | end | |
1011 | end | |
1012 | ||
1013 | if math.random(100) > 85 then | |
1014 | ls_incr_stats_user(numeric, "investigate_revealed") | |
1015 | ||
1016 | ls_chanmsg(channel, ls_format_player(channel, numeric) .. "'s fine detective work reveals " .. ls_format_player(channel, victimnumeric) .. "'s role: " .. ls_format_role(ls_get_role(channel, victimnumeric))) | |
1017 | end | |
1018 | ||
1019 | ls_incr_stats_user(numeric, "investigate_" .. ls_get_role(channel, victimnumeric)) | |
1020 | ls_incr_stats_user(victimnumeric, "investigate_target") | |
1021 | ||
1022 | if numeric == victimnumeric then | |
1023 | ls_notice(numeric, "You're the investigator. Excellent detective work!") | |
1024 | else | |
1025 | ls_notice(numeric, ls_format_player(channel, victimnumeric) .. "'s role is: " .. ls_format_role(ls_get_role(channel, victimnumeric))) | |
1026 | end | |
1027 | ||
1028 | ls_set_state(channel, "vote") | |
1029 | ls_advance_state(channel) | |
1030 | ||
1031 | ls_flush_modes(channel) | |
1032 | end | |
1033 | ||
1034 | function ls_cmd_vote(numeric, victim) | |
1035 | if not victim then | |
1036 | ls_notice(numeric, "Syntax: vote <nick>") | |
1037 | return | |
1038 | end | |
1039 | ||
1040 | local channel = ls_chan_for_numeric(numeric) | |
1041 | ||
1042 | if not channel then | |
1043 | ls_notice(numeric, "You haven't joined any game lobby.") | |
1044 | return | |
1045 | end | |
1046 | ||
1047 | if ls_get_state(channel) ~= "vote" then | |
1048 | ls_notice(numeric, "Sorry, you can't use this command right now.") | |
1049 | return | |
1050 | end | |
1051 | ||
1052 | local victimnick = irc_getnickbynick(victim) | |
1053 | ||
1054 | if not victimnick then | |
1055 | ls_notice(numeric, "Sorry, I don't know who that is.") | |
1056 | return | |
1057 | end | |
1058 | ||
1059 | local victimnumeric = victimnick.numeric | |
1060 | ||
1061 | if not ls_get_role(channel, victimnumeric) then | |
1062 | ls_notice(numeric, "Sorry, " .. ls_format_player(channel, victimnumeric) .. " isn't playing the game.") | |
1063 | return | |
1064 | end | |
1065 | ||
1066 | if ls_get_vote(channel, numeric) == victimnumeric then | |
1067 | ls_notice(numeric, "You already voted for " .. ls_format_player(channel, victimnumeric) .. ".") | |
1068 | return | |
1069 | end | |
1070 | ||
1071 | ls_keepalive(channel, numeric) | |
1072 | ||
1073 | ls_set_vote(channel, numeric, victimnumeric) | |
1074 | ls_notice(numeric, "Done.") | |
1075 | ||
1076 | ls_advance_state(channel) | |
1077 | ||
1078 | ls_flush_modes(channel) | |
1079 | end | |
1080 | ||
1081 | function ls_cmd_guard(numeric, victim) | |
1082 | if not victim then | |
1083 | ls_notice(numeric, "Syntax: guard <nick>") | |
1084 | return | |
1085 | end | |
1086 | ||
1087 | local channel = ls_chan_for_numeric(numeric) | |
1088 | ||
1089 | if not channel then | |
1090 | ls_notice(numeric, "You haven't joined any game lobby.") | |
1091 | return | |
1092 | end | |
1093 | ||
1094 | if not ls_get_trait(channel, numeric, "force") then | |
1095 | ls_notice(numeric, "Sorry, you need the force field generator to use this command.") | |
1096 | return | |
1097 | end | |
1098 | ||
1099 | local victimnick = irc_getnickbynick(victim) | |
1100 | ||
1101 | if not victimnick then | |
1102 | ls_notice(numeric, "Sorry, I don't know who that is.") | |
1103 | return | |
1104 | end | |
1105 | ||
1106 | local victimnumeric = victimnick.numeric | |
1107 | ||
1108 | if not ls_get_role(channel, victimnumeric) then | |
1109 | ls_notice(numeric, "Sorry, " .. ls_format_player(channel, victimnumeric) .. " isn't playing the game.") | |
1110 | return | |
1111 | end | |
1112 | ||
1113 | local target | |
1114 | ||
1115 | if victimnumeric == numeric then | |
1116 | target = "yourself" | |
1117 | else | |
1118 | target = ls_format_player(channel, victimnumeric) | |
1119 | end | |
1120 | ||
1121 | for _, player in pairs(ls_get_players(channel)) do | |
1122 | if ls_get_guarded(channel, player) then | |
1123 | if player == victimnumeric then | |
1124 | ls_notice(numeric, "You are already protecting " .. target .. ".") | |
1125 | return | |
1126 | end | |
1127 | ||
1128 | local previous_Target | |
1129 | ||
1130 | if player == numeric then | |
1131 | previous_target = "yourself" | |
1132 | else | |
1133 | previous_target = ls_format_player(channel, player) | |
1134 | end | |
1135 | ||
1136 | ls_notice(numeric, "You are no longer protecting " .. previous_target .. ".") | |
1137 | ||
1138 | if numeric ~= player then | |
1139 | ls_notice(player, "You are no longer being protected by a \002force field\002.") | |
1140 | end | |
1141 | end | |
1142 | ||
1143 | ls_set_guarded(channel, player, (player == victimnumeric)) | |
1144 | end | |
1145 | ||
1146 | ls_notice(victimnumeric, "A field of energy envelops you. You are now protected by a \002force field\002.") | |
1147 | ||
1148 | if numeric ~= victimnumeric then | |
1149 | ls_notice(numeric, "You are now protecting " .. target .. ".") | |
1150 | end | |
1151 | end | |
1152 | ||
1153 | function ls_cmd_note(numeric, message) | |
1154 | if not message then | |
1155 | ls_notice(numeric, "Syntax: note <message>") | |
1156 | return | |
1157 | end | |
1158 | ||
1159 | local channel = ls_chan_for_numeric(numeric) | |
1160 | ||
1161 | if not channel then | |
1162 | ls_notice(numeric, "You haven't joined any game lobby.") | |
1163 | return | |
1164 | end | |
1165 | ||
1166 | if not ls_get_trait(channel, numeric, "note") then | |
1167 | ls_notice(numeric, "Sorry, you need a piece of paper to use this command.") | |
1168 | return | |
1169 | end | |
1170 | ||
1171 | if ls_get_note(channel, numeric) then | |
1172 | ls_notice(numeric, "You've run out of space on your piece of paper. Maybe in your next life.") | |
1173 | return | |
1174 | end | |
1175 | ||
1176 | ls_set_note(channel, numeric, message) | |
1177 | ls_notice(numeric, "You scribble on your piece of paper: " .. message) | |
1178 | end | |
1179 | ||
1180 | function round(num, idp) | |
1181 | local mult = 10^(idp or 0) | |
1182 | return math.floor(num * mult + 0.5) / mult | |
1183 | end | |
1184 | ||
1185 | function ls_cmd_stats(numeric, victim) | |
1186 | local getter | |
1187 | ||
1188 | if victim == "--all" then | |
1189 | local channel = ls_chan_for_numeric(numeric) | |
1190 | ||
1191 | if channel and ls_get_role(channel, numeric) ~= "lobby" then | |
1192 | ls_notice(numeric, "Sorry, you can't view aggregated statistics while you're playing the game.") | |
1193 | return | |
1194 | end | |
1195 | ||
1196 | getter = function(key) | |
1197 | return ls_get_stats_user_aggregate(key) | |
1198 | end | |
1199 | ||
1200 | ls_notice(numeric, "Showing aggregated statistics for all users.") | |
1201 | else | |
1202 | local victimnumeric | |
1203 | ||
1204 | if victim then | |
1205 | local victimnick = irc_getnickbynick(victim) | |
1206 | ||
1207 | if not victimnick then | |
1208 | ls_notice(numeric, "Sorry, I don't know who that is. Please specify a valid nick or try --all.") | |
1209 | return | |
1210 | end | |
1211 | ||
1212 | if not victimnick.accountid then | |
1213 | ls_notice(numeric, "Sorry, that user is not authenticated with Q.") | |
1214 | return | |
1215 | end | |
1216 | ||
1217 | for channel, _ in pairs(ls_gamestate) do | |
1218 | local role = ls_get_role(channel, victimnick.numeric) | |
1219 | ||
1220 | if role and role ~= "lobby" then | |
1221 | ls_notice(numeric, "Sorry, you can't view statistics for a user who is currently playing a game.") | |
1222 | return | |
1223 | end | |
1224 | end | |
1225 | ||
1226 | victimnumeric = victimnick.numeric | |
1227 | ||
1228 | ls_notice(numeric, "Showing statistics for '" .. victimnick.nick .. "'") | |
1229 | else | |
1230 | local victimnick = irc_getnickbynumeric(numeric) | |
1231 | ||
1232 | if not victimnick.accountid then | |
1233 | ls_notice(numeric, "Sorry, you are not authenticated with Q.") | |
1234 | return | |
1235 | end | |
1236 | ||
1237 | victimnumeric = numeric | |
1238 | ||
1239 | ls_notice(numeric, "Showing statistics for yourself.") | |
1240 | end | |
1241 | ||
1242 | getter = function(key) | |
1243 | return ls_get_stats_user(victimnumeric, key) | |
1244 | end | |
1245 | end | |
1246 | ||
1247 | local time = getter("game_time") | |
1248 | ||
1249 | if time > 3600 then | |
1250 | timeinfo = round(getter("game_time") / 3600, 2) .. " hours" | |
1251 | elseif time > 60 then | |
1252 | timeinfo = round(getter("game_time") / 60, 2) .. " minutes" | |
1253 | else | |
1254 | timeinfo = getter("game_time") .. " seconds" | |
1255 | end | |
1256 | ||
1257 | ls_notice(numeric, "Game time: " .. timeinfo) | |
1258 | ||
1259 | ls_notice(numeric, "Roles: " .. | |
1260 | getter("role_scientist") .. "x " .. ls_format_role("scientist") .. ", " .. | |
1261 | getter("role_investigator") .. "x " .. ls_format_role("investigator") .. ", " .. | |
1262 | getter("role_idiot") .. "x " .. ls_format_role("idiot") .. ", " .. | |
1263 | getter("role_citizen") .. "x " .. ls_format_role("citizen")) | |
1264 | ||
1265 | ls_notice(numeric, "Traits: " .. | |
1266 | getter("trait_teleporter") .. "x " .. ls_format_trait("teleporter") .. ", " .. | |
1267 | getter("trait_infested") .. "x " .. ls_format_trait("infested") .. ", " .. | |
1268 | getter("trait_force") .. "x " .. ls_format_trait("force") .. ", " .. | |
1269 | getter("trait_note") .. "x " .. ls_format_trait("note")) | |
1270 | ||
1271 | ls_notice(numeric, "Won games as: " .. | |
1272 | getter("won_scientist") .. "x " .. ls_format_role("scientist") .. ", " .. | |
1273 | getter("won_investigator") .. "x " .. ls_format_role("investigator") .. ", " .. | |
1274 | getter("won_idiot") .. "x " .. ls_format_role("idiot") .. ", " .. | |
1275 | getter("won_citizen") .. "x " .. ls_format_role("citizen")) | |
1276 | ||
1277 | ls_notice(numeric, "Survived attacks by: " .. | |
1278 | getter("survived_chance") .. "x chance, " .. | |
1279 | getter("survived_guarded") .. "x being guarded with a force field") | |
1280 | ||
1281 | ls_notice(numeric, "Survived rounds: " .. getter("survived_round")) | |
1282 | ||
1283 | ls_notice(numeric, "Active role: " .. | |
1284 | getter("active_scientist") .. "x " .. ls_format_role("scientist") .. ", " .. | |
1285 | getter("active_investigator") .. "x " .. ls_format_role("investigator")) | |
1286 | ||
1287 | ls_notice(numeric, "Inactive role: " .. | |
1288 | getter("inactive_scientist") .. "x " .. ls_format_role("scientist") .. ", " .. | |
1289 | getter("inactive_investigator") .. "x " .. ls_format_role("investigator")) | |
1290 | ||
1291 | ls_notice(numeric, "Kills: " .. | |
1292 | getter("kill_scientist") .. "x scientist attack, " .. | |
1293 | getter("kill_infested") .. "x infestation attack, " .. | |
1294 | getter("kill_tied") .. "x teams tied, " .. | |
1295 | getter("kill_smite") .. "x lightning, " .. | |
1296 | getter("kill_bomb") .. "x bomb") | |
1297 | ||
1298 | ls_notice(numeric, "Failed kills: " .. | |
1299 | getter("failed_chance") .. "x by chance, " .. | |
1300 | getter("failed_guarded") .. "x because target was guarded by a force field") | |
1301 | ||
1302 | ls_notice(numeric, "Deaths: " .. | |
1303 | getter("killed_scientist") .. "x scientist attack, " .. | |
1304 | getter("killed_infested") .. "x infestation attack, " .. | |
1305 | getter("killed_lynch") .. "x lynched, " .. | |
1306 | getter("killed_suicide") .. "x suicide, " .. | |
1307 | getter("killed_tied") .. "x teams tied, " .. | |
1308 | getter("killed_afk") .. "x AFK, " .. | |
1309 | getter("killed_smite") .. "x lightning, " .. | |
1310 | getter("killed_bomb") .. "x bomb") | |
1311 | ||
1312 | ls_notice(numeric, "Revealed investigations: " .. getter("investigate_revealed")) | |
1313 | ||
1314 | ls_notice(numeric, "Investigated: " .. | |
1315 | getter("investigate_scientist") .. "x " .. ls_format_role("scientist") .. ", " .. | |
1316 | getter("investigate_investigator") .. "x " .. ls_format_role("investigator") .. ", " .. | |
1317 | getter("investigate_citizen") .. "x " .. ls_format_role("citizen")) | |
1318 | ||
1319 | ls_notice(numeric, "Was investigated: " .. getter("investigate_target")) | |
1320 | ||
1321 | ls_notice(numeric, "Votes by target team: " .. | |
1322 | getter("vote_team") .. "x own team, " .. | |
1323 | getter("vote_enemy") .. "x enemy team") | |
1324 | ||
1325 | ls_notice(numeric, "Votes by target role: " .. | |
1326 | getter("vote_scientist") .. "x " .. ls_format_role("scientist") .. ", " .. | |
1327 | getter("vote_investigator") .. "x " .. ls_format_role("investigator") .. ", " .. | |
1328 | getter("vote_idiot") .. "x " .. ls_format_role("idiot") .. ", " .. | |
1329 | getter("vote_citizen") .. "x " .. ls_format_role("citizen")) | |
1330 | ||
1331 | ls_notice(numeric, "Portal Device usage: " .. | |
1332 | getter("teleporter_activated") .. "x success (" .. getter("teleporter_intact") .. "x retained, " .. getter("teleporter_destroyed") .. "x destroyed), " .. | |
1333 | getter("teleporter_failed") .. "x failed") | |
1334 | end | |
1335 | ||
1336 | function ls_cmd_msghelp(numeric, victim) | |
1337 | ls_cmd_msgshowcommands(numeric, victim) | |
1338 | end | |
1339 | ||
1340 | function ls_cmd_msgshowcommands(numeric, victim) | |
1341 | ls_notice(numeric, "Commands available to you:") | |
1342 | ls_notice(numeric, "guard - Guards somebody.") | |
1343 | ls_notice(numeric, "help - Get help.") | |
1344 | ls_notice(numeric, "investigate - Investigate somebody.") | |
1345 | ls_notice(numeric, "kill - Kill somebody.") | |
1346 | ls_notice(numeric, "showcommands - Show this list.") | |
1347 | ls_notice(numeric, "stats - View stats about the game.") | |
1348 | ls_notice(numeric, "vote - Vote for somebody.") | |
1349 | ||
1350 | if onstaff(numeric) or ontlz(numeric) then | |
1351 | ls_notice(numeric, "smite - Remove someone from a game.") | |
1352 | ls_notice(numeric, "killgame - Cancel a game.") | |
1353 | ls_notice(numeric, "killmessage - View or set someone's custom kill message.") | |
1354 | end | |
1355 | ||
1356 | if ontlz(numeric) then | |
1357 | ls_notice(numeric, "addchan - Adds me to a channel.") | |
1358 | ls_notice(numeric, "delchan - Removes me from a channel.") | |
1359 | ls_notice(numeric, "bomb - Someone set us up the bomb.") | |
1360 | end | |
1361 | end | |
1362 | ||
1363 | function ls_cmd_smite(numeric, victim) | |
1364 | if not victim then | |
1365 | ls_notice(numeric, "Syntax: smite <nick>") | |
1366 | return | |
1367 | end | |
1368 | ||
1369 | local victimnick = irc_getnickbynick(victim) | |
1370 | ||
1371 | if not victimnick then | |
1372 | ls_notice(numeric, "Sorry, I don't know who that is.") | |
1373 | return | |
1374 | end | |
1375 | ||
1376 | local victimnumeric = victimnick.numeric | |
1377 | local channel = ls_chan_for_numeric(victimnumeric) | |
1378 | ||
1379 | if not channel then | |
1380 | ls_notice(numeric, "Sorry, " .. victimnick.nick .. " isn't playing the game.") | |
1381 | return | |
1382 | end | |
1383 | ||
1384 | ls_incr_stats_user(numeric, "kill_smite") | |
1385 | ls_incr_stats_user(victimnumeric, "killed_smite") | |
1386 | ||
1387 | ls_chanmsg(channel, ls_format_player(channel, victimnumeric, true, true) .. " was struck by lightning.") | |
1388 | ls_remove_player(channel, victimnumeric, true) | |
1389 | ||
1390 | ls_advance_state(channel) | |
1391 | ||
1392 | ls_flush_modes(channel) | |
1393 | end | |
1394 | ||
1395 | function ls_cmd_killgame(numeric, channel) | |
1396 | if not channel then | |
1397 | ls_notice(numeric, "Syntax: killgame <channel>") | |
1398 | return | |
1399 | end | |
1400 | ||
1401 | channel = irctolower(channel) | |
1402 | ||
1403 | if not ls_is_game_channel(channel) then | |
1404 | ls_notice(numeric, "I'm not on that channel.") | |
1405 | return | |
1406 | end | |
1407 | ||
1408 | if table.getn(ls_get_players(channel)) == 0 then | |
1409 | ls_notice(numeric, "There's nobody playing the game.") | |
1410 | return | |
1411 | end | |
1412 | ||
1413 | ls_incr_stats_user(numeric, "kill_bomb", table.getn(ls_get_players(channel))) | |
1414 | ||
1415 | for _, player in pairs(ls_get_players(channel)) do | |
1416 | ls_incr_stats_user(player, "killed_bomb") | |
1417 | end | |
1418 | ||
1419 | ls_chanmsg(channel, ls_format_player(channel, numeric) .. " set us up the bomb. Game over.") | |
1420 | ls_stop_game(channel) | |
1421 | ||
1422 | ls_flush_modes(channel) | |
1423 | end | |
1424 | ||
1425 | function ls_cmd_addchan(numeric, channel) | |
1426 | if not channel then | |
1427 | ls_notice(numeric, "Syntax: addchan <#channel>") | |
1428 | return | |
1429 | end | |
1430 | ||
1431 | channel = irctolower(channel) | |
1432 | ||
1433 | if not irc_getchaninfo(channel) then | |
1434 | ls_notice(numeric, "The specified channel does not exist.") | |
1435 | return | |
1436 | end | |
1437 | ||
1438 | if ls_is_game_channel(channel) then | |
1439 | ls_notice(numeric, "The bot is already on that channel.") | |
1440 | return | |
1441 | end | |
1442 | ||
1443 | ls_add_channel(channel) | |
1444 | ||
1445 | ls_notice(numeric, "Done.") | |
1446 | end | |
1447 | ||
1448 | function ls_cmd_delchan(numeric, channel) | |
1449 | if not channel then | |
1450 | ls_notice(numeric, "Syntax: delchan <#channel>") | |
1451 | return | |
1452 | end | |
1453 | ||
1454 | channel = irctolower(channel) | |
1455 | ||
1456 | if not ls_is_game_channel(channel) then | |
1457 | ls_notice(numeric, "The bot is not on that channel.") | |
1458 | return | |
1459 | end | |
1460 | ||
1461 | ls_remove_channel(channel, true) | |
1462 | ||
1463 | ls_notice(numeric, "Done.") | |
1464 | end | |
1465 | ||
1466 | function ls_cmd_bomb(numeric, channel) | |
1467 | if not channel then | |
1468 | ls_notice(numeric, "Syntax: bomb <#channel>") | |
1469 | return | |
1470 | end | |
1471 | ||
1472 | channel = irctolower(channel) | |
1473 | ||
1474 | if not ls_is_game_channel(channel) then | |
1475 | ls_notice(numeric, "The bot is not on that channel.") | |
1476 | return | |
1477 | end | |
1478 | ||
1479 | ls_chanmsg(channel, " xxxxxx") | |
1480 | ls_chanmsg(channel, " x x \\ /") | |
1481 | ls_chanmsg(channel, " x xxxxxx*") | |
1482 | ls_chanmsg(channel, " xxxxxxxxxxxx / \\") | |
1483 | ls_chanmsg(channel, " xxxxxxxxxxxx") | |
1484 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxx") | |
1485 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxxxx") | |
1486 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxxxxxx") | |
1487 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxxxxxx") | |
1488 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxxxxxx") | |
1489 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxxxxxx") | |
1490 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxxxxxx") | |
1491 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxxxxxx") | |
1492 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxxxx") | |
1493 | ls_chanmsg(channel, " xxxxxxxxxxxxxxxx") | |
1494 | ls_chanmsg(channel, " xxxxxxxxxxxx") | |
1495 | ||
1496 | ls_notice(numeric, "Done.") | |
1497 | end | |
1498 | ||
1499 | function ls_cmd_killmessage(numeric, victim, message) | |
1500 | if not victim then | |
1501 | ls_notice(numeric, "Syntax: killmessage <nick> ?message?") | |
1502 | return | |
1503 | end | |
1504 | ||
1505 | local victimnick = irc_getnickbynick(victim) | |
1506 | ||
1507 | if not victimnick then | |
1508 | ls_notice(numeric, "Sorry, I don't know who that is.") | |
1509 | return | |
1510 | end | |
1511 | ||
1512 | if not victimnick.accountid then | |
1513 | ls_notice(numeric, "Sorry, that user is not authenticated with Q.") | |
1514 | return | |
1515 | end | |
1516 | ||
1517 | local victimnumeric = victimnick.numeric | |
1518 | ||
1519 | if not message then | |
1520 | local current_message = ls_get_killmessage(victimnumeric) | |
1521 | ||
1522 | if not current_message then | |
1523 | ls_notice(numeric, victimnick.nick .. " does not have a custom kill message.") | |
1524 | else | |
1525 | ls_notice(numeric, "Current custom kill message for " .. victimnick.nick .. ": " .. current_message) | |
1526 | end | |
1527 | else | |
1528 | if message == "REMOVE" then | |
1529 | ls_set_killmessage(victimnumeric, nil) | |
1530 | ls_notice(numeric, "Custom kill message for " .. victimnick.nick .. " was removed.") | |
1531 | else | |
1532 | ls_set_killmessage(victimnumeric, message) | |
1533 | ls_notice(numeric, "Custom kill message for " .. victimnick.nick .. " was set to: " .. message) | |
1534 | end | |
1535 | end | |
1536 | end | |
1537 | ||
1538 | function ls_keepalive(channel, numeric) | |
1539 | if ls_get_role(channel, numeric) then | |
1540 | ls_set_seen(channel, numeric, os.time()) | |
1541 | end | |
1542 | ||
1543 | -- extend lobby timeout if we don't have enough players yet | |
1544 | if ls_get_state(channel) == "lobby" and table.getn(ls_get_players(channel)) < MINPLAYERS then | |
1545 | ls_set_delay(channel, 90) | |
1546 | ls_set_timeout(channel, 150) | |
1547 | end | |
1548 | end | |
1549 | ||
1550 | function ls_timer_announce_players(channel) | |
1551 | ls_gamestate[channel]["announce_timer"] = nil | |
1552 | ||
1553 | local new_players = {} | |
1554 | ||
1555 | for _, numeric in pairs(ls_get_players(channel)) do | |
1556 | if not ls_get_announced(channel, numeric) then | |
1557 | table.insert(new_players, numeric) | |
1558 | ls_set_announced(channel, numeric, true) | |
1559 | ls_voice_player(channel, numeric) | |
1560 | end | |
1561 | end | |
1562 | ||
1563 | ls_flush_modes(channel) | |
1564 | ||
1565 | if table.getn(new_players) > 0 then | |
1566 | local count = table.getn(ls_get_players(channel)) | |
1567 | local subject | |
1568 | ||
1569 | if count ~= 1 then | |
1570 | subject = "players" | |
1571 | else | |
1572 | subject = "player" | |
1573 | end | |
1574 | ||
1575 | ls_chanmsg(channel, ls_format_players(channel, new_players) .. " joined the game (" .. count .. " " .. subject .. " in the lobby).") | |
1576 | end | |
1577 | end | |
1578 | ||
1579 | function ls_add_channel(channel) | |
1580 | ls_gamestate[channel] = { players = {}, state = "lobby", timeout = -1, delay = os.time() + 30, waitcount = 0, lasthl = 0, hiddenrole = false, enabled = true } | |
1581 | irc_localjoin(ls_bot, channel) | |
1582 | irc_localsimplechanmode(ls_bot, channel, "-m") | |
1583 | end | |
1584 | ||
1585 | function ls_remove_channel(channel, part) | |
1586 | if ls_gamestate[channel]["announce_timer"] then | |
1587 | ls_sched:remove(ls_gamestate[channel]["announce_timer"]) | |
1588 | end | |
1589 | ||
1590 | ls_gamestate[channel] = nil | |
1591 | ||
1592 | if part then | |
1593 | irc_localpart(ls_bot, channel) | |
1594 | end | |
1595 | end | |
1596 | ||
1597 | function ls_dbload() | |
1598 | ls_db = loadtable(basepath() .. "db/" .. DB) | |
1599 | ||
1600 | if not ls_db then | |
1601 | ls_db = ls_dbdefaults() | |
1602 | end | |
1603 | end | |
1604 | ||
1605 | function ls_dbsave() | |
1606 | local channels = {} | |
1607 | ||
1608 | for channel, _ in pairs(ls_gamestate) do | |
1609 | table.insert(channels, channel) | |
1610 | end | |
1611 | ||
1612 | ls_db.channels = channels | |
1613 | ||
1614 | savetable(basepath() .. "db/" .. DB, ls_db) | |
1615 | end | |
1616 | ||
1617 | function ls_dbdefaults() | |
1618 | local db = {} | |
1619 | db.channels = { HOMECHANNEL } | |
1620 | ||
1621 | return db | |
1622 | end | |
1623 | ||
1624 | function ls_add_player(channel, numeric, forced) | |
1625 | local role = ls_get_role(channel, numeric) | |
1626 | ||
1627 | if role then | |
1628 | ls_chanmsg(channel, "\001ACTION slaps " .. ls_format_player(channel, numeric) .. "\001") | |
1629 | return | |
1630 | end | |
1631 | ||
1632 | if not forced then | |
1633 | if not ls_get_enabled(channel) then | |
1634 | ls_notice(numeric, "Sorry, the game is currently disabled.") | |
1635 | return | |
1636 | end | |
1637 | ||
1638 | if ls_game_in_progress(channel) then | |
1639 | ls_notice(numeric, "Sorry, you can't join the game right now.") | |
1640 | return | |
1641 | end | |
1642 | ||
1643 | local chanuser = irc_getuserchanmodes(numeric, channel) | |
1644 | ||
1645 | if not chanuser then | |
1646 | ls_notice(numeric, "Sorry, you must be on the channel to use this command.") | |
1647 | return | |
1648 | end | |
1649 | ||
1650 | if chanuser.opped then | |
1651 | ls_notice(numeric, "You must not be opped to use this command.") | |
1652 | return | |
1653 | end | |
1654 | ||
1655 | if table.getn(ls_get_players(channel)) >= MAXPLAYERS then | |
1656 | ls_notice(numeric, "Sorry, the game's lobby is full.") | |
1657 | return | |
1658 | end | |
1659 | ||
1660 | if ls_chan_for_numeric(numeric) then | |
1661 | ls_notice(numeric, "Sorry, you can't play on multiple channels at once.") | |
1662 | return | |
1663 | end | |
1664 | end | |
1665 | ||
1666 | ls_set_role(channel, numeric, "lobby") | |
1667 | ls_set_seen(channel, numeric, os.time()) | |
1668 | ls_set_note(channel, numeric, nil) | |
1669 | ||
1670 | if not forced then | |
1671 | ls_set_announced(channel, numeric, false) | |
1672 | ||
1673 | if ls_gamestate[channel]["announce_timer"] then | |
1674 | ls_sched:remove(ls_gamestate[channel]["announce_timer"]) | |
1675 | end | |
1676 | ls_gamestate[channel]["announce_timer"] = ls_sched:add(5, ls_timer_announce_players, channel) | |
1677 | ||
1678 | ls_notice(numeric, "You were added to the lobby.") | |
1679 | else | |
1680 | ls_set_announced(channel, numeric, true) | |
1681 | ls_voice_player(channel, numeric) | |
1682 | end | |
1683 | ||
1684 | ls_set_delay(channel, 30) | |
1685 | ls_set_timeout(channel, 90) | |
1686 | end | |
1687 | ||
1688 | function ls_voice_player(channel, numeric) | |
1689 | if not ls_gamestate[channel]["modes"] then | |
1690 | ls_gamestate[channel]["modes"] = {} | |
1691 | end | |
1692 | ||
1693 | table.insert(ls_gamestate[channel]["modes"], true) | |
1694 | table.insert(ls_gamestate[channel]["modes"], "v") | |
1695 | table.insert(ls_gamestate[channel]["modes"], numeric) | |
1696 | end | |
1697 | ||
1698 | function ls_devoice_player(channel, numeric) | |
1699 | if not ls_gamestate[channel]["modes"] then | |
1700 | ls_gamestate[channel]["modes"] = {} | |
1701 | end | |
1702 | ||
1703 | table.insert(ls_gamestate[channel]["modes"], false) | |
1704 | table.insert(ls_gamestate[channel]["modes"], "v") | |
1705 | table.insert(ls_gamestate[channel]["modes"], numeric) | |
1706 | end | |
1707 | ||
1708 | function ls_flush_modes(channel) | |
1709 | if ls_gamestate[channel]["modes"] then | |
1710 | irc_localovmode(ls_bot, channel, ls_gamestate[channel]["modes"]) | |
1711 | ls_gamestate[channel]["modes"] = nil | |
1712 | end | |
1713 | end | |
1714 | ||
1715 | function ls_remove_player(channel, numeric, forced) | |
1716 | local role = ls_get_role(channel, numeric) | |
1717 | ||
1718 | if not role then | |
1719 | return | |
1720 | end | |
1721 | ||
1722 | if role ~= "lobby" then | |
1723 | ls_incr_stats_user(numeric, "game_time", os.time() - ls_get_startts(channel)) | |
1724 | end | |
1725 | ||
1726 | local announced = ls_get_announced(channel, numeric) | |
1727 | ||
1728 | local force_field = ls_get_trait(channel, numeric, "force") | |
1729 | ||
1730 | ls_set_role(channel, numeric, nil) | |
1731 | ||
1732 | ls_devoice_player(channel, numeric) | |
1733 | ||
1734 | for _, player in pairs(ls_get_players(channel)) do | |
1735 | if ls_get_vote(channel, player) == numeric then | |
1736 | ls_set_vote(channel, player, nil) | |
1737 | end | |
1738 | ||
1739 | if force_field and ls_get_guarded(channel, player) then | |
1740 | ls_notice(player, "You are no longer being protected by a \002force field\002.") | |
1741 | ls_set_guarded(channel, player, false) | |
1742 | end | |
1743 | end | |
1744 | ||
1745 | if not forced then | |
1746 | if announced then | |
1747 | if ls_game_in_progress(channel) then | |
1748 | ls_chanmsg(channel, ls_format_player(channel, numeric) .. " committed suicide. Goodbye, cruel world.") | |
1749 | else | |
1750 | ls_chanmsg(channel, ls_format_player(channel, numeric) .. " left the game (" .. table.getn(ls_get_players(channel)) .. " players in the lobby).") | |
1751 | end | |
1752 | end | |
1753 | ||
1754 | ls_notice(numeric, "You were removed from the lobby.") | |
1755 | ||
1756 | ls_set_delay(channel, 30) | |
1757 | ls_set_timeout(channel, 90) | |
1758 | end | |
1759 | end | |
1760 | ||
1761 | function ls_get_players(channel, role) | |
1762 | local players = {} | |
1763 | ||
1764 | for player, _ in pairs(ls_gamestate[channel]["players"]) do | |
1765 | if not role or ls_get_role(channel, player) == role then | |
1766 | table.insert(players, player) | |
1767 | end | |
1768 | end | |
1769 | ||
1770 | return players | |
1771 | end | |
1772 | ||
1773 | function ls_is_game_channel(channel) | |
1774 | return ls_gamestate[channel] | |
1775 | end | |
1776 | ||
1777 | function ls_get_role(channel, numeric) | |
1778 | if not ls_gamestate[channel]["players"][numeric] then | |
1779 | return nil | |
1780 | end | |
1781 | ||
1782 | return ls_gamestate[channel]["players"][numeric]["role"] | |
1783 | end | |
1784 | ||
1785 | function ls_set_role(channel, numeric, role) | |
1786 | if not ls_gamestate[channel]["players"][numeric] or role == "lobby" then | |
1787 | ls_gamestate[channel]["players"][numeric] = { | |
1788 | active = false, | |
1789 | announced = false, | |
1790 | traits = {}, | |
1791 | guarded = false | |
1792 | } | |
1793 | end | |
1794 | ||
1795 | if role then | |
1796 | ls_gamestate[channel]["players"][numeric]["role"] = role | |
1797 | else | |
1798 | ls_gamestate[channel]["players"][numeric] = nil | |
1799 | end | |
1800 | ||
1801 | if role and role ~= "lobby" then | |
1802 | ls_notice(numeric, "Your role for this round is '" .. ls_format_role(role) .. "'.") | |
1803 | end | |
1804 | end | |
1805 | ||
1806 | function ls_get_traits(channel, numeric) | |
1807 | local traits = {} | |
1808 | ||
1809 | for trait, _ in pairs(ls_gamestate[channel]["players"][numeric]["traits"]) do | |
1810 | table.insert(traits, trait) | |
1811 | end | |
1812 | ||
1813 | return traits | |
1814 | end | |
1815 | ||
1816 | function ls_get_trait(channel, numeric, trait) | |
1817 | return ls_gamestate[channel]["players"][numeric]["traits"][trait] | |
1818 | end | |
1819 | ||
1820 | function ls_set_trait(channel, numeric, trait, enabled) | |
1821 | ls_gamestate[channel]["players"][numeric]["traits"][trait] = enabled | |
1822 | end | |
1823 | ||
1824 | function ls_get_guarded(channel, numeric, guarded) | |
1825 | return ls_gamestate[channel]["players"][numeric]["guarded"] | |
1826 | end | |
1827 | ||
1828 | function ls_set_guarded(channel, numeric, guarded) | |
1829 | ls_gamestate[channel]["players"][numeric]["guarded"] = guarded | |
1830 | end | |
1831 | ||
1832 | function ls_get_note(channel, numeric) | |
1833 | return ls_gamestate[channel]["players"][numeric]["note"] | |
1834 | end | |
1835 | ||
1836 | function ls_set_note(channel, numeric, note) | |
1837 | ls_gamestate[channel]["players"][numeric]["note"] = note | |
1838 | end | |
1839 | ||
1840 | function ls_get_seen(channel, numeric) | |
1841 | return ls_gamestate[channel]["players"][numeric]["seen"] | |
1842 | end | |
1843 | ||
1844 | function ls_set_seen(channel, numeric, seen) | |
1845 | ls_gamestate[channel]["players"][numeric]["seen"] = seen | |
1846 | end | |
1847 | ||
1848 | function ls_get_vote(channel, numeric) | |
1849 | if not ls_gamestate[channel]["players"][numeric] then | |
1850 | return nil | |
1851 | end | |
1852 | ||
1853 | return ls_gamestate[channel]["players"][numeric]["vote"] | |
1854 | end | |
1855 | ||
1856 | function ls_set_vote(channel, numeric, votenumeric) | |
1857 | if ls_get_vote(channel, numeric) == votenumeric then | |
1858 | return | |
1859 | end | |
1860 | ||
1861 | if votenumeric then | |
1862 | local count = 0 | |
1863 | for _, player in pairs(ls_get_players(channel)) do | |
1864 | if ls_get_vote(channel, player) == votenumeric then | |
1865 | count = count + 1 | |
1866 | end | |
1867 | end | |
1868 | ||
1869 | -- increase count for this new vote | |
1870 | count = count + 1 | |
1871 | ||
1872 | local plural_s | |
1873 | ||
1874 | if count ~= 1 then | |
1875 | plural_s = "s" | |
1876 | else | |
1877 | plural_s = "" | |
1878 | end | |
1879 | ||
1880 | if numeric ~= votenumeric then | |
1881 | if ls_get_vote(channel, numeric) then | |
1882 | ls_chanmsg(channel, ls_format_player(channel, numeric) .. " changed their vote to " .. ls_format_player(channel, votenumeric) .. " (" .. count .. " vote" .. plural_s .. ").") | |
1883 | else | |
1884 | ls_chanmsg(channel, ls_format_player(channel, numeric) .. " voted for " .. ls_format_player(channel, votenumeric) .. " (" .. count .. " vote" .. plural_s .. ").") | |
1885 | end | |
1886 | else | |
1887 | ls_chanmsg(channel, ls_format_player(channel, numeric) .. " voted for himself. Oops! (" .. count .. " vote" .. plural_s .. ")") | |
1888 | end | |
1889 | end | |
1890 | ||
1891 | if ls_gamestate[channel]["players"][numeric] then | |
1892 | ls_gamestate[channel]["players"][numeric]["vote"] = votenumeric | |
1893 | end | |
1894 | end | |
1895 | ||
1896 | function ls_format_votes(votes, votees) | |
1897 | local message = "" | |
1898 | ||
1899 | for _, votee in pairs(votees) do | |
1900 | if message ~= "" then | |
1901 | message = message .. ", " | |
1902 | end | |
1903 | ||
1904 | message = message .. votes[votee] .. "x " .. ls_format_player(channel, votee) | |
1905 | end | |
1906 | ||
1907 | return message | |
1908 | end | |
1909 | ||
1910 | -- Returns (.votes, .votees, .missing_votes) as the current vote results | |
1911 | function ls_get_vote_result(channel, countscore) | |
1912 | local result = { votes = {}, votees = {}, missing_votes = {} } | |
1913 | ||
1914 | for _, player in pairs(ls_get_players(channel)) do | |
1915 | local vote = ls_get_vote(channel, player) | |
1916 | ||
1917 | if vote then | |
1918 | if countscore then | |
1919 | if (ls_get_role(channel, player) == "scientist" and ls_get_role(channel, vote) == "scientist") or (ls_get_role(channel, player) ~= "scientist" and ls_get_role(channel, vote) ~= "scientist") then | |
1920 | ls_incr_stats_user(player, "vote_team") | |
1921 | else | |
1922 | ls_incr_stats_user(player, "vote_enemy") | |
1923 | end | |
1924 | ||
1925 | ls_incr_stats_user(player, "vote_" .. ls_get_role(channel, vote)) | |
1926 | end | |
1927 | ||
1928 | if not result.votes[vote] then | |
1929 | result.votes[vote] = 0 | |
1930 | table.insert(result.votees, vote) | |
1931 | end | |
1932 | result.votes[vote] = result.votes[vote] + 1 | |
1933 | else | |
1934 | table.insert(result.missing_votes, player) | |
1935 | end | |
1936 | ||
1937 | end | |
1938 | ||
1939 | local function votecomp(v1, v2) | |
1940 | if result.votes[v1] > result.votes[v2] then | |
1941 | return true | |
1942 | end | |
1943 | return false | |
1944 | end | |
1945 | ||
1946 | table.sort(result.votees, votecomp) | |
1947 | ||
1948 | return result | |
1949 | end | |
1950 | ||
1951 | function ls_get_active(channel, numeric) | |
1952 | return ls_gamestate[channel]["players"][numeric]["active"] | |
1953 | end | |
1954 | ||
1955 | function ls_set_active(channel, numeric, active) | |
1956 | ls_gamestate[channel]["players"][numeric]["active"] = active | |
1957 | end | |
1958 | ||
1959 | function ls_get_announced(channel, numeric) | |
1960 | return ls_gamestate[channel]["players"][numeric]["announced"] | |
1961 | end | |
1962 | ||
1963 | function ls_set_announced(channel, numeric, announced) | |
1964 | ls_gamestate[channel]["players"][numeric]["announced"] = announced | |
1965 | end | |
1966 | ||
1967 | function ls_pick_player(players) | |
1968 | return players[math.random(table.getn(players))] | |
1969 | end | |
1970 | ||
1971 | function ls_number_scientists(numPlayers) | |
1972 | return math.ceil((numPlayers - 2) / 5.0) | |
1973 | end | |
1974 | ||
1975 | function ls_number_investigators(numPlayers) | |
1976 | return math.ceil((numPlayers - 5) / 6.0) | |
1977 | end | |
1978 | ||
1979 | function ls_start_game(channel) | |
1980 | local players = ls_get_players(channel) | |
1981 | ||
1982 | irc_localsimplechanmode(ls_bot, channel, "+m") | |
1983 | ||
1984 | for nick in channelusers_iter(channel, { nickpusher.numeric }) do | |
1985 | local numeric = nick[1] | |
1986 | ||
1987 | if ls_get_role(channel, numeric) then | |
1988 | ls_voice_player(channel, numeric) | |
1989 | ls_set_seen(channel, numeric, os.time()) | |
1990 | else | |
1991 | ls_devoice_player(channel, numeric) | |
1992 | end | |
1993 | end | |
1994 | ||
1995 | ls_chanmsg(channel, "Starting the game...") | |
1996 | ||
1997 | ls_incr_stats_channel(channel, "game_count") | |
1998 | ls_set_startts(channel, os.time()) | |
1999 | ls_set_overloadts(channel, nil) | |
2000 | ls_set_round(channel, 0) | |
2001 | ls_set_hiddenrole(channel, false) | |
2002 | ||
2003 | for _, player in pairs(players) do | |
2004 | ls_set_role(channel, player, "lobby") | |
2005 | ls_keepalive(channel, player) | |
2006 | end | |
2007 | ||
2008 | local players_count = table.getn(players) | |
2009 | local scientists_count = 0 | |
2010 | local scientists_needed = ls_number_scientists(players_count) | |
2011 | ||
2012 | -- pick scientists | |
2013 | while scientists_count < scientists_needed do | |
2014 | local scientist_index = math.random(table.getn(players)) | |
2015 | ls_set_role(channel, table.remove(players, scientist_index), "scientist") | |
2016 | scientists_count = scientists_count + 1 | |
2017 | end | |
2018 | ||
2019 | -- notify scientists about each other | |
2020 | for _, scientist in pairs(ls_get_players(channel, "scientist")) do | |
2021 | for _, scientist_notify in pairs(ls_get_players(channel, "scientist")) do | |
2022 | if scientist ~= scientist_notify then | |
2023 | ls_notice(scientist_notify, ls_format_player(channel, scientist) .. " is also a scientist.") | |
2024 | end | |
2025 | end | |
2026 | end | |
2027 | ||
2028 | local investigators_count = 0 | |
2029 | local investigators_needed = ls_number_investigators(players_count) | |
2030 | ||
2031 | -- pick investigators | |
2032 | while investigators_count < investigators_needed do | |
2033 | local investigator_index = math.random(table.getn(players)) | |
2034 | ls_set_role(channel, table.remove(players, investigator_index), "investigator") | |
2035 | investigators_count = investigators_count + 1 | |
2036 | end | |
2037 | ||
2038 | -- notify investigators about each other | |
2039 | for _, investigator in pairs(ls_get_players(channel, "investigator")) do | |
2040 | for _, investigator_notify in pairs(ls_get_players(channel, "investigator")) do | |
2041 | if investigator ~= investigator_notify then | |
2042 | ls_notice(investigator_notify, ls_format_player(channel, investigator) .. " is also an investigator.") | |
2043 | end | |
2044 | end | |
2045 | end | |
2046 | ||
2047 | -- one village idiot is plenty, the game becomes hell otherwise, but don't | |
2048 | -- generate one if there are few players because you need time to figure that | |
2049 | -- role out | |
2050 | if players_count >= MINPIDIOT then | |
2051 | local idiot_index = math.random(table.getn(players)) | |
2052 | ls_set_role(channel, table.remove(players, idiot_index), "idiot") | |
2053 | end | |
2054 | ||
2055 | -- rest of the players are citizens | |
2056 | for _, player in pairs(players) do | |
2057 | ls_set_role(channel, player, "citizen") | |
2058 | end | |
2059 | ||
2060 | for _, player in pairs(ls_get_players(channel)) do | |
2061 | ls_incr_stats_user(player, "role_" .. ls_get_role(channel, player)) | |
2062 | end | |
2063 | ||
2064 | -- give someone the force field generator | |
2065 | if players_count >= MINPFORCE then | |
2066 | local force_owner = players[math.random(table.getn(players))] | |
2067 | ls_set_trait(channel, force_owner, "force", true) | |
2068 | ls_incr_stats_user(force_owner, "trait_force") | |
2069 | ls_set_guarded(channel, force_owner, true) | |
2070 | ls_notice(force_owner, "You've found the \002force field generator\002. Use /notice " .. BOTNICK .. " guard <nick> to protect someone.") | |
2071 | ls_notice(force_owner, "You are currently protecting yourself.") | |
2072 | end | |
2073 | ||
2074 | -- make someone infested | |
2075 | if players_count >= MINPALIEN then | |
2076 | local infested_player = players[math.random(table.getn(players))] | |
2077 | ls_set_trait(channel, infested_player, "infested", true) | |
2078 | ls_incr_stats_user(infested_player, "trait_infested") | |
2079 | ls_notice(infested_player, "You're infested with an \002alien parasite\002.") | |
2080 | end | |
2081 | ||
2082 | -- give someone the teleporter | |
2083 | local teleporter_candidates | |
2084 | ||
2085 | if math.random(100) > 75 then | |
2086 | teleporter_candidates = ls_get_players(channel) | |
2087 | else | |
2088 | teleporter_candidates = ls_get_players(channel, "scientist") | |
2089 | end | |
2090 | ||
2091 | -- give someone the note | |
2092 | local note_owner = players[math.random(table.getn(players))] | |
2093 | ls_set_trait(channel, note_owner, "note", true) | |
2094 | ls_incr_stats_user(note_owner, "trait_note") | |
2095 | ls_notice(note_owner, "You've found a \002piece of paper\002 and a pen. Use /notice " .. BOTNICK .. " note <message> to write a message.") | |
2096 | ||
2097 | local teleporter_owner = teleporter_candidates[math.random(table.getn(teleporter_candidates))] | |
2098 | ls_set_trait(channel, teleporter_owner, "teleporter", true) | |
2099 | ls_incr_stats_user(teleporter_owner, "trait_teleporter") | |
2100 | ls_notice(teleporter_owner, "You've found the \002portal device\002.") | |
2101 | ||
2102 | ls_set_state(channel, "kill") | |
2103 | ls_show_status(channel) | |
2104 | ||
2105 | if math.random(100) > 90 then | |
2106 | ls_set_hiddenrole(channel, true) | |
2107 | end | |
2108 | ||
2109 | ls_advance_state(channel) | |
2110 | end | |
2111 | ||
2112 | function ls_stop_game(channel) | |
2113 | if ls_get_state(channel) ~= "lobby" then | |
2114 | ls_incr_stats_channel(channel, "game_time", os.time() - ls_get_startts(channel)) | |
2115 | end | |
2116 | ||
2117 | ls_set_state(channel, "lobby") | |
2118 | ls_set_waitcount(channel, 0) | |
2119 | ||
2120 | for _, player in pairs(ls_get_players(channel)) do | |
2121 | ls_remove_player(channel, player, true) | |
2122 | end | |
2123 | ||
2124 | irc_localsimplechanmode(ls_bot, channel, "-m") | |
2125 | end | |
2126 | ||
2127 | -- makes sure people are not afk | |
2128 | function ls_check_alive(channel) | |
2129 | local timeout | |
2130 | ||
2131 | if not ls_game_in_progress(channel) then | |
2132 | timeout = 300 | |
2133 | else | |
2134 | timeout = 120 | |
2135 | end | |
2136 | ||
2137 | local dead_players = {} | |
2138 | local idle_players = {} | |
2139 | ||
2140 | for _, player in pairs(ls_get_players(channel)) do | |
2141 | local seen = ls_get_seen(channel, player) | |
2142 | ||
2143 | if seen then | |
2144 | if seen < os.time() - timeout then | |
2145 | table.insert(dead_players, player) | |
2146 | elseif seen < os.time() - timeout / 3 - 30 then | |
2147 | table.insert(idle_players, player) | |
2148 | end | |
2149 | end | |
2150 | end | |
2151 | ||
2152 | if table.getn(dead_players) > 0 then | |
2153 | local verb | |
2154 | ||
2155 | if table.getn(dead_players) ~= 1 then | |
2156 | verb = "seem" | |
2157 | else | |
2158 | verb = "seems" | |
2159 | end | |
2160 | ||
2161 | ls_chanmsg(channel, ls_format_players(channel, dead_players, true, true) .. " " .. verb .. " to be dead (AFK).") | |
2162 | ||
2163 | for _, player in pairs(dead_players) do | |
2164 | ls_incr_stats_user(player, "killed_afk") | |
2165 | ls_remove_player(channel, player, true) | |
2166 | end | |
2167 | end | |
2168 | ||
2169 | if table.getn(idle_players) > 0 then | |
2170 | ls_chanmsg(channel, "Hi " .. ls_format_players(channel, idle_players) .. ", please say something if you're still alive.") | |
2171 | end | |
2172 | end | |
2173 | ||
2174 | function ls_check_shield(channel) | |
2175 | -- shield generator overload | |
2176 | if ls_get_overloadts(channel) and ls_get_overloadts(channel) < os.time() then | |
2177 | for _, player in pairs(ls_get_players(channel)) do | |
2178 | if ls_get_guarded(channel, player) and ls_get_trait(channel, player, "force") then | |
2179 | ls_chanmsg(channel, ls_format_player(channel, player, true) .. "'s shield generator blew up.") | |
2180 | ls_remove_player(channel, player, true) | |
2181 | end | |
2182 | end | |
2183 | end | |
2184 | end | |
2185 | ||
2186 | function ls_advance_state(channel, delayed) | |
2187 | if delayed and not ls_delay_exceeded(channel) then | |
2188 | return | |
2189 | end | |
2190 | ||
2191 | ls_debug(channel, "ls_advance_state") | |
2192 | ||
2193 | ls_set_delay(channel, 30) | |
2194 | ||
2195 | local players = ls_get_players(channel) | |
2196 | local scientists = ls_get_players(channel, "scientist") | |
2197 | local investigators = ls_get_players(channel, "investigator") | |
2198 | ||
2199 | -- game start condition | |
2200 | if not ls_game_in_progress(channel) then | |
2201 | if table.getn(players) < MINPLAYERS then | |
2202 | if table.getn(players) > 0 then | |
2203 | if ls_timeout_exceeded(channel) then | |
2204 | ls_chanmsg(channel, "Lobby was closed because there aren't enough players.") | |
2205 | ls_stop_game(channel) | |
2206 | else | |
2207 | ls_chanmsg(channel, "Game will start when there are at least " .. MINPLAYERS .. " players.") | |
2208 | end | |
2209 | end | |
2210 | else | |
2211 | ls_start_game(channel) | |
2212 | end | |
2213 | ||
2214 | return | |
2215 | end | |
2216 | ||
2217 | -- winning condition when everyone is dead | |
2218 | if table.getn(players) == 0 then | |
2219 | ls_chanmsg(channel, "Everyone is dead.") | |
2220 | ls_stop_game(channel) | |
2221 | return | |
2222 | end | |
2223 | ||
2224 | -- winning condition for scientists | |
2225 | if table.getn(scientists) >= table.getn(players) - table.getn(scientists) then | |
2226 | local losers = {} | |
2227 | for _, player in pairs(players) do | |
2228 | if ls_get_role(channel, player) == "scientist" then | |
2229 | ls_incr_stats_user(player, "kill_tied") | |
2230 | ls_incr_stats_user(player, "won_scientist") | |
2231 | else | |
2232 | table.insert(losers, player) | |
2233 | ls_incr_stats_user(player, "killed_tied") | |
2234 | end | |
2235 | end | |
2236 | ||
2237 | ls_set_hiddenrole(channel, false) | |
2238 | ls_chanmsg(channel, "There are equal to or more scientists than citizens. Science wins again: " .. ls_format_players(channel, scientists, true, true) .. ". They slaughter the surviving citizens: " .. ls_format_players(channel, losers, true, true) .. ".") | |
2239 | ls_stop_game(channel) | |
2240 | return | |
2241 | end | |
2242 | ||
2243 | -- winning condition for citizen | |
2244 | if table.getn(scientists) == 0 then | |
2245 | for _, player in pairs(players) do | |
2246 | ls_incr_stats_user(player, "won_" .. ls_get_role(channel, player)) | |
2247 | end | |
2248 | ||
2249 | ls_set_hiddenrole(channel, false) | |
2250 | ls_chanmsg(channel, "All scientists have been eliminated. The citizens win this round: " .. ls_format_players(channel, players, true, true)) | |
2251 | ls_stop_game(channel) | |
2252 | return | |
2253 | end | |
2254 | ||
2255 | -- make sure there's progress towards the game's end | |
2256 | local state = ls_get_state(channel) | |
2257 | local timeout = ls_get_timeout(channel) | |
2258 | ||
2259 | if state == "kill" then | |
2260 | if timeout == -1 then | |
2261 | local active_scientist = scientists[math.random(table.getn(scientists))] | |
2262 | ||
2263 | for _, scientist in pairs(scientists) do | |
2264 | if scientist == active_scientist then | |
2265 | ls_set_active(channel, scientist, true) | |
2266 | ls_incr_stats_user(scientist, "active_scientist") | |
2267 | ls_notice(scientist, "It's your turn to select a citizen to kill. Use /notice " .. BOTNICK .. " kill <nick> to kill someone.") | |
2268 | else | |
2269 | ls_set_active(channel, scientist, false) | |
2270 | ls_incr_stats_user(scientist, "inactive_scientist") | |
2271 | ls_notice(scientist, ls_format_player(channel, active_scientist) .. " is choosing a victim.") | |
2272 | end | |
2273 | end | |
2274 | ||
2275 | local round = ls_get_round(channel) + 1 | |
2276 | ls_set_round(channel, round) | |
2277 | ||
2278 | if round > 1 then | |
2279 | for _, player in pairs(players) do | |
2280 | ls_incr_stats_user(player, "survived_round") | |
2281 | end | |
2282 | end | |
2283 | ||
2284 | if round == 2 then | |
2285 | for _, player in pairs(players) do | |
2286 | if ls_get_guarded(channel, player) and ls_get_trait(channel, player, "force") then | |
2287 | ls_notice(player, "You feel your shield generator overheating, you may want to do something about this, just as a hint... The display reads in menacingly red letters: 15 seconds remain.") | |
2288 | end | |
2289 | end | |
2290 | ||
2291 | ls_set_overloadts(channel, os.time() + 15) | |
2292 | end | |
2293 | ||
2294 | -- Check if we need to disable the alien. | |
2295 | if table.getn(ls_get_players(channel)) <= MAXLALIEN then | |
2296 | -- Remove the alien, if there is no alien in the game, nothing happens. | |
2297 | for _, player in pairs(players) do | |
2298 | if ls_get_trait(channel, player, "infested") then | |
2299 | ls_set_trait(channel, player, "infested", false) | |
2300 | ls_chanmsg(channel, "\002WARNING\002: The mad scientist has engaged parasite killing defenses.") | |
2301 | ls_notice(player, "You get a warm fuzzy feeling and suddenly you notice that your \002alien parasite\002 is gone!") | |
2302 | end | |
2303 | end | |
2304 | end | |
2305 | ||
2306 | local roundinfo = "Round #" .. round | |
2307 | ||
2308 | if table.getn(scientists) > 1 then | |
2309 | ls_chanmsg(channel, roundinfo .. ": The citizens are asleep while the mad scientists are choosing a target.") | |
2310 | else | |
2311 | ls_chanmsg(channel, roundinfo .. ": The citizens are asleep while the mad scientist is choosing a target.") | |
2312 | end | |
2313 | ||
2314 | ls_set_timeout(channel, 120) | |
2315 | elseif ls_timeout_exceeded(channel) then | |
2316 | ls_chanmsg(channel, "The scientists failed to set their alarm clocks. Nobody dies tonight.") | |
2317 | ls_set_state(channel, "investigate") | |
2318 | ls_advance_state(channel) | |
2319 | else | |
2320 | ls_chanmsg(channel, "The scientists still need to pick someone to kill.") | |
2321 | end | |
2322 | end | |
2323 | ||
2324 | if state == "investigate" then | |
2325 | ||
2326 | if table.getn(investigators) == 0 and not ls_get_hiddenrole(channel) then | |
2327 | ls_set_state(channel, "vote") | |
2328 | ls_advance_state(channel) | |
2329 | return | |
2330 | end | |
2331 | ||
2332 | if timeout == -1 then | |
2333 | if table.getn(investigators) > 0 then | |
2334 | local active_investigator = investigators[math.random(table.getn(investigators))] | |
2335 | ||
2336 | for _, investigator in pairs(investigators) do | |
2337 | if investigator == active_investigator then | |
2338 | ls_set_active(channel, investigator, true) | |
2339 | ls_incr_stats_user(investigator, "active_investigator") | |
2340 | ls_notice(investigator, "You need to choose someone to investigate: /notice " .. BOTNICK .. " investigate <nick>") | |
2341 | else | |
2342 | ls_set_active(channel, investigator, false) | |
2343 | ls_incr_stats_user(investigator, "inactive_investigator") | |
2344 | ls_notice(investigator, "Another investigator is choosing a target.") | |
2345 | end | |
2346 | end | |
2347 | end | |
2348 | ||
2349 | if not ls_get_hiddenrole(channel) then | |
2350 | if table.getn(investigators) > 1 then | |
2351 | ls_chanmsg(channel, "It's now up to the investigators to find the mad scientists.") | |
2352 | else | |
2353 | ls_chanmsg(channel, "It's now up to the investigator to find the mad scientists.") | |
2354 | end | |
2355 | ||
2356 | ls_set_timeout(channel, 120) | |
2357 | else | |
2358 | ls_chanmsg(channel, "An unknown force has frozen space and time to investigate these suspicious deaths.") | |
2359 | ls_set_timeout(channel, 10 + math.random(10)) | |
2360 | end | |
2361 | ||
2362 | elseif ls_timeout_exceeded(channel) then | |
2363 | if not ls_get_hiddenrole(channel) then | |
2364 | ls_chanmsg(channel, "Looks like the investigator is still firmly asleep.") | |
2365 | end | |
2366 | ls_set_state(channel, "vote") | |
2367 | ls_advance_state(channel) | |
2368 | else | |
2369 | if not ls_get_hiddenrole(channel) then | |
2370 | ls_chanmsg(channel, "The investigator still needs to do their job.") | |
2371 | end | |
2372 | end | |
2373 | end | |
2374 | ||
2375 | if state == "vote" then | |
2376 | local voteresult = ls_get_vote_result(channel, true) | |
2377 | ||
2378 | if timeout == -1 then | |
2379 | for _, player in pairs(players) do | |
2380 | ls_set_vote(channel, player, nil) | |
2381 | end | |
2382 | ||
2383 | ls_chanmsg(channel, "It's now up to the citizens to vote who to lynch (via /notice " .. BOTNICK .. " vote <nick>).") | |
2384 | ls_set_timeout(channel, 120) | |
2385 | elseif ls_timeout_exceeded(channel) or table.getn(voteresult.missing_votes) == 0 then | |
2386 | local message_suffix, candidates | |
2387 | ||
2388 | if table.getn(voteresult.votees) > 0 then | |
2389 | ls_show_votes(channel, voteresult, true) | |
2390 | ||
2391 | local most_votes = voteresult.votes[voteresult.votees[1]] | |
2392 | candidates = {} | |
2393 | ||
2394 | for _, votee in pairs(voteresult.votees) do | |
2395 | if voteresult.votes[votee] == most_votes then | |
2396 | table.insert(candidates, votee) | |
2397 | end | |
2398 | end | |
2399 | ||
2400 | message_suffix = "was lynched by the angry mob." | |
2401 | else | |
2402 | candidates = players | |
2403 | message_suffix = "was hit by a stray high-energy laser beam." | |
2404 | end | |
2405 | ||
2406 | local victim_index = math.random(table.getn(candidates)) | |
2407 | local victim = candidates[victim_index] | |
2408 | local teleporter = ls_get_trait(channel, victim, "teleporter") | |
2409 | ||
2410 | if teleporter and math.random(100) > 50 then | |
2411 | ls_notice(victim, "You press the button to activate the \002portal device\002... and you jump in the newly created portal!") | |
2412 | ls_incr_stats_user(victim, "teleporter_activated") | |
2413 | ls_chanmsg(channel, ls_format_player(channel, victim) .. " used their portal device to escape the angry mob.") | |
2414 | ls_notice(victim, "You safely arrive at the other end of the portal.") | |
2415 | ||
2416 | if math.random(100) > 50 then | |
2417 | ls_set_trait(channel, victim, "teleporter", false) | |
2418 | ls_incr_stats_user(victim, "teleporter_destroyed") | |
2419 | ls_notice(victim, "Your \002portal device\002 was accidently left on the other side of the portal and it has already closed.") | |
2420 | else | |
2421 | ls_incr_stats_user(victim, "teleporter_intact") | |
2422 | ls_notice(channel, victim, "You check your \002portal device\002 after the escape and it seems to work fine.") | |
2423 | end | |
2424 | else | |
2425 | if teleporter then | |
2426 | ls_incr_stats_user(victim, "teleporter_failed") | |
2427 | ls_notice(victim, "You press the button to activate the \002portal device\002... but nothing happens! Perhaps more testing at Aperture Science is required.") | |
2428 | end | |
2429 | ||
2430 | ls_incr_stats_user(victim, "killed_lynch") | |
2431 | ls_devoice_player(channel, victim) | |
2432 | ||
2433 | ls_chanmsg(channel, ls_format_player(channel, victim, true) .. " " .. message_suffix) | |
2434 | ||
2435 | if ls_get_note(channel, victim) then | |
2436 | ls_chanmsg(channel, "While preparing the body for the funeral a note is found in the victim's jacket: " .. ls_get_note(channel, victim)) | |
2437 | end | |
2438 | ||
2439 | -- idiot winning condition | |
2440 | -- Loss by tie is impossible at this point. | |
2441 | -- Keeping table even if there's only one idiot in case someone decides | |
2442 | -- we do need more idiots after all. | |
2443 | if ls_get_role(channel, victim) == "idiot" then | |
2444 | local losers = {} | |
2445 | local idiots = {} | |
2446 | for _, player in pairs(players) do | |
2447 | if ls_get_role(channel, player) == "idiot" then | |
2448 | table.insert(idiots, player) | |
2449 | ls_incr_stats_user(player, "won_idiot") | |
2450 | else | |
2451 | table.insert(losers, player) | |
2452 | end | |
2453 | end | |
2454 | ||
2455 | ls_set_hiddenrole(channel, false) | |
2456 | ls_chanmsg(channel, "The village idiot supreme by sheer idiocy once more: " .. ls_format_players(channel, idiots, true, true) .. ". The remaining people cheer so joyfully that they combust spontaneously: " .. ls_format_players(channel, losers, true, true) .. ". The Village Idiot wins!") | |
2457 | ls_stop_game(channel) | |
2458 | return | |
2459 | end | |
2460 | ||
2461 | ls_remove_player(channel, victim, true) | |
2462 | end | |
2463 | ||
2464 | ls_set_state(channel, "kill") | |
2465 | ls_advance_state(channel) | |
2466 | elseif delayed then | |
2467 | ls_show_votes(channel, voteresult, false) | |
2468 | end | |
2469 | end | |
2470 | end |