]> jfr.im git - irc/quakenet/lua-labspace.git/blame_incremental - labspace.lua
Fix: Don't call lower() until we verify command is not nil.
[irc/quakenet/lua-labspace.git] / labspace.lua
... / ...
CommitLineData
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
18local BOTNICK = "labspace"
19local BOTACCOUNT = "labspace"
20local BOTACCOUNTID = 5022574
21local HOMECHANNEL = "#labspace"
22local MINPLAYERS = 6
23local MAXPLAYERS = 30
24local DEBUG = false
25local DB = "labspace.db"
26-- Some settings.
27-- Minimum players for the idiot
28local MINPIDIOT = 10
29-- Minimum players for the alien
30local MINPALIEN = 9
31-- Minimum players for the force field generator
32local MINPFORCE = 8
33-- Maximum players left for the alien to be disabled
34local MAXLALIEN = 5
35
36local 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
50local ls_bot
51local ls_hlbot
52local ls_gamestate = {}
53local ls_db = {}
54local ls_lastsave = 0
55local ls_lastalivecheck = 0
56local ls_sched = Scheduler()
57
58function onload()
59 ls_dbload()
60 onconnect()
61end
62
63function onunload()
64 ls_dbsave()
65end
66
67function 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)
73end
74
75function 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
84end
85
86function 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
98end
99
100function 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
200end
201
202function 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
209end
210
211function 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
226end
227
228function 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
248end
249irc_onkickall = irc_onkick
250
251function 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
262end
263
264function 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
286end
287
288-- sends a debug message
289function ls_debug(channel, message)
290 if DEBUG then
291 irc_localchanmsg(ls_bot, channel, "DEBUG: " .. message)
292 end
293end
294
295-- sends a notice to the specified target
296function ls_notice(numeric, text)
297 irc_localnotice(ls_bot, numeric, text)
298end
299
300-- sends a message to the specified target
301function ls_chanmsg(channel, text)
302 irc_localchanmsg(ls_bot, channel, text)
303end
304
305-- formats the specified role identifier for output in a message
306function 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
320end
321
322-- formats the specified trait identifier for output in a message
323function 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
335end
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)
339function 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
363end
364
365-- formats a list of player names for output in a message (optionally
366-- revealing their roles and traits in the game)
367function 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
385end
386
387-- returns the current state of the game
388function ls_get_state(channel)
389 return ls_gamestate[channel]["state"]
390end
391
392function ls_get_startts(channel)
393 return ls_gamestate[channel]["startts"]
394end
395
396function ls_get_overloadts(channel)
397 return ls_gamestate[channel]["overloadts"]
398end
399
400-- gets the hidden role flag
401function ls_get_hiddenrole(channel)
402 return ls_gamestate[channel]["hiddenrole"]
403end
404
405-- gets the timeout for the current state
406function ls_get_timeout(channel)
407 return ls_gamestate[channel]["timeout"]
408end
409
410-- gets the delay for the current state
411function ls_get_delay(channel)
412 return ls_gamestate[channel]["delay"]
413end
414
415-- gets the ts when !hl was last used
416function ls_get_lasthl(channel)
417 return ls_gamestate[channel]["lasthl"]
418end
419
420-- gets whether the bot is enabled
421function ls_get_enabled(channel)
422 return ls_gamestate[channel]["enabled"]
423end
424
425-- returns true if the game state delay was exceeded, false otherwise
426function ls_delay_exceeded(channel)
427 return ls_get_delay(channel) < os.time()
428end
429
430function ls_get_waitcount(channel)
431 return ls_gamestate[channel]["waitcount"]
432end
433
434function ls_get_round(channel)
435 return ls_gamestate[channel]["round"]
436end
437
438-- sets the game state
439function ls_set_state(channel, state)
440 ls_gamestate[channel]["state"] = state
441
442 ls_set_timeout(channel, -1)
443 ls_set_delay(channel, 30)
444end
445
446function ls_set_startts(channel, startts)
447 ls_gamestate[channel]["startts"] = startts
448end
449
450function ls_set_overloadts(channel, overloadts)
451 ls_gamestate[channel]["overloadts"] = overloadts
452end
453
454-- sets the hiddenrole flag
455function ls_set_hiddenrole(channel, enabled)
456 ls_gamestate[channel]["hiddenrole"] = enabled
457end
458
459-- sets the game state timeout (in seconds)
460function 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
466end
467
468-- sets the game state delay (in seconds)
469function ls_set_delay(channel, delay)
470 ls_gamestate[channel]["delay"] = os.time() + delay
471 ls_debug(channel, "changed gamestate delay to " .. delay)
472end
473
474-- sets the !hl timestamp
475function ls_set_lasthl(channel, ts)
476 ls_gamestate[channel]["lasthl"] = ts
477end
478
479-- sets whether the bot is enabled
480function ls_set_enabled(channel, enabled)
481 ls_gamestate[channel]["enabled"] = enabled
482end
483
484function ls_set_waitcount(channel, count)
485 ls_gamestate[channel]["waitcount"] = count
486end
487
488function ls_set_round(channel, number)
489 ls_gamestate[channel]["round"] = number
490end
491
492-- returns true if the game state timeout was exceeded, false otherwise
493function ls_timeout_exceeded(channel)
494 local timeout = ls_get_timeout(channel)
495
496 return timeout ~= -1 and timeout < os.time()
497end
498
499-- returns true if there's a game in progress, false otherwise
500function ls_game_in_progress(channel)
501 return ls_get_state(channel) ~= "lobby"
502end
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
506function 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
514end
515
516function 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]
526end
527
528function 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
550end
551
552function 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]
573end
574
575function 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
589end
590
591function 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
620end
621
622function 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]
635end
636
637function 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
650end
651
652function ls_cmd_add(channel, numeric)
653 ls_add_player(channel, numeric)
654end
655
656function ls_cmd_remove(channel, numeric)
657 ls_remove_player(channel, numeric)
658end
659
660function 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
689end
690
691function 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)
705end
706
707function 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)
714end
715
716
717function 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
736end
737
738function 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
753end
754
755function 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)
758end
759
760function 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)
805end
806
807function 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.")
817end
818
819function 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.")
836end
837
838function 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)
961end
962
963function 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)
1032end
1033
1034function 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)
1079end
1080
1081function 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
1151end
1152
1153function 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)
1178end
1179
1180function round(num, idp)
1181 local mult = 10^(idp or 0)
1182 return math.floor(num * mult + 0.5) / mult
1183end
1184
1185function 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")
1334end
1335
1336function ls_cmd_msghelp(numeric, victim)
1337 ls_cmd_msgshowcommands(numeric, victim)
1338end
1339
1340function 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
1361end
1362
1363function 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)
1393end
1394
1395function 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)
1423end
1424
1425function 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.")
1446end
1447
1448function 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.")
1464end
1465
1466function 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.")
1497end
1498
1499function 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
1536end
1537
1538function 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
1548end
1549
1550function 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
1577end
1578
1579function 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")
1583end
1584
1585function 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
1595end
1596
1597function ls_dbload()
1598 ls_db = loadtable(basepath() .. "db/" .. DB)
1599
1600 if not ls_db then
1601 ls_db = ls_dbdefaults()
1602 end
1603end
1604
1605function 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)
1615end
1616
1617function ls_dbdefaults()
1618 local db = {}
1619 db.channels = { HOMECHANNEL }
1620
1621 return db
1622end
1623
1624function 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)
1686end
1687
1688function 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)
1696end
1697
1698function 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)
1706end
1707
1708function 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
1713end
1714
1715function 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
1759end
1760
1761function 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
1771end
1772
1773function ls_is_game_channel(channel)
1774 return ls_gamestate[channel]
1775end
1776
1777function 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"]
1783end
1784
1785function 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
1804end
1805
1806function 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
1814end
1815
1816function ls_get_trait(channel, numeric, trait)
1817 return ls_gamestate[channel]["players"][numeric]["traits"][trait]
1818end
1819
1820function ls_set_trait(channel, numeric, trait, enabled)
1821 ls_gamestate[channel]["players"][numeric]["traits"][trait] = enabled
1822end
1823
1824function ls_get_guarded(channel, numeric, guarded)
1825 return ls_gamestate[channel]["players"][numeric]["guarded"]
1826end
1827
1828function ls_set_guarded(channel, numeric, guarded)
1829 ls_gamestate[channel]["players"][numeric]["guarded"] = guarded
1830end
1831
1832function ls_get_note(channel, numeric)
1833 return ls_gamestate[channel]["players"][numeric]["note"]
1834end
1835
1836function ls_set_note(channel, numeric, note)
1837 ls_gamestate[channel]["players"][numeric]["note"] = note
1838end
1839
1840function ls_get_seen(channel, numeric)
1841 return ls_gamestate[channel]["players"][numeric]["seen"]
1842end
1843
1844function ls_set_seen(channel, numeric, seen)
1845 ls_gamestate[channel]["players"][numeric]["seen"] = seen
1846end
1847
1848function 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"]
1854end
1855
1856function 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
1894end
1895
1896function 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
1908end
1909
1910-- Returns (.votes, .votees, .missing_votes) as the current vote results
1911function 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
1949end
1950
1951function ls_get_active(channel, numeric)
1952 return ls_gamestate[channel]["players"][numeric]["active"]
1953end
1954
1955function ls_set_active(channel, numeric, active)
1956 ls_gamestate[channel]["players"][numeric]["active"] = active
1957end
1958
1959function ls_get_announced(channel, numeric)
1960 return ls_gamestate[channel]["players"][numeric]["announced"]
1961end
1962
1963function ls_set_announced(channel, numeric, announced)
1964 ls_gamestate[channel]["players"][numeric]["announced"] = announced
1965end
1966
1967function ls_pick_player(players)
1968 return players[math.random(table.getn(players))]
1969end
1970
1971function ls_number_scientists(numPlayers)
1972 return math.ceil((numPlayers - 2) / 5.0)
1973end
1974
1975function ls_number_investigators(numPlayers)
1976 return math.ceil((numPlayers - 5) / 6.0)
1977end
1978
1979function 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)
2110end
2111
2112function 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")
2125end
2126
2127-- makes sure people are not afk
2128function 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
2172end
2173
2174function 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
2184end
2185
2186function 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
2470end