commit
77262cea75
36
src/events.py
Normal file
36
src/events.py
Normal file
@ -0,0 +1,36 @@
|
||||
# event system
|
||||
|
||||
EVENT_CALLBACKS = {}
|
||||
|
||||
def add_listener(event, callback, priority = 5):
|
||||
if event not in EVENT_CALLBACKS:
|
||||
EVENT_CALLBACKS[event] = []
|
||||
|
||||
if (priority, callback) not in EVENT_CALLBACKS[event]:
|
||||
EVENT_CALLBACKS[event].append((priority, callback))
|
||||
EVENT_CALLBACKS[event].sort(key = lambda x: x[0])
|
||||
|
||||
def remove_listener(event, callback, priority = 5):
|
||||
if event in EVENT_CALLBACKS and (priority, callback) in EVENT_CALLBACKS[event]:
|
||||
EVENT_CALLBACKS[event].remove((priority, callback))
|
||||
|
||||
|
||||
class Event:
|
||||
def __init__(self, name, data):
|
||||
self.stop_processing = False
|
||||
self.prevent_default = False
|
||||
self.name = name
|
||||
self.data = data
|
||||
|
||||
def dispatch(self, *args):
|
||||
if self.name not in EVENT_CALLBACKS:
|
||||
return
|
||||
|
||||
for item in list(EVENT_CALLBACKS[self.name]):
|
||||
item[1](self, *args)
|
||||
if self.stop_processing:
|
||||
break
|
||||
|
||||
return not self.prevent_default
|
||||
|
||||
# vim: set expandtab:sw=4:ts=4:
|
@ -1,5 +1,6 @@
|
||||
from collections import defaultdict
|
||||
import math
|
||||
from src import events
|
||||
|
||||
PING_WAIT = 300 # Seconds
|
||||
PING_MIN_WAIT = 30 # How long !start has to wait after a !ping
|
||||
@ -521,28 +522,58 @@ class MadMode(object):
|
||||
"assassin" : ( 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 ),
|
||||
})
|
||||
|
||||
# evilvillage is broken, disable for now
|
||||
#@game_mode("evilvillage", minp = 6, maxp = 18)
|
||||
@game_mode("evilvillage", minp = 6, maxp = 18, likelihood = 1)
|
||||
class EvilVillageMode(object):
|
||||
"""Majority of the village is wolf aligned, safes must secretly try to kill the wolves."""
|
||||
def __init__(self):
|
||||
self.DEFAULT_ROLE = "cultist"
|
||||
self.ROLE_INDEX = ( 6 , 10 , 15 )
|
||||
self.DEFAULT_SEEN_AS_VILL = False
|
||||
self.ABSTAIN_ENABLED = False
|
||||
self.ROLE_INDEX = ( 6 , 8 , 10 , 15 )
|
||||
self.ROLE_GUIDE = reset_roles(self.ROLE_INDEX)
|
||||
self.ROLE_GUIDE.update({# village roles
|
||||
"oracle" : ( 1 , 1 , 0 ),
|
||||
"seer" : ( 0 , 0 , 1 ),
|
||||
"guardian angel" : ( 0 , 1 , 1 ),
|
||||
"shaman" : ( 1 , 1 , 1 ),
|
||||
"hunter" : ( 0 , 0 , 1 ),
|
||||
"villager" : ( 0 , 0 , 1 ),
|
||||
"oracle" : ( 0 , 1 , 1 , 0 ),
|
||||
"seer" : ( 0 , 0 , 0 , 1 ),
|
||||
"guardian angel" : ( 0 , 0 , 1 , 1 ),
|
||||
"shaman" : ( 0 , 0 , 0 , 1 ),
|
||||
"hunter" : ( 1 , 1 , 1 , 1 ),
|
||||
"villager" : ( 0 , 0 , 0 , 1 ),
|
||||
# wolf roles
|
||||
"wolf" : ( 1 , 1 , 2 ),
|
||||
"minion" : ( 0 , 1 , 1 ),
|
||||
"wolf" : ( 1 , 1 , 1 , 2 ),
|
||||
"minion" : ( 0 , 0 , 1 , 1 ),
|
||||
# neutral roles
|
||||
"fool" : ( 0 , 1 , 1 ),
|
||||
"fool" : ( 0 , 0 , 1 , 1 ),
|
||||
# templates
|
||||
"cursed villager" : ( 0 , 1 , 1 , 1 ),
|
||||
})
|
||||
|
||||
def startup(self):
|
||||
events.add_listener("chk_win", self.chk_win, 1)
|
||||
|
||||
def teardown(self):
|
||||
events.remove_listener("chk_win", self.chk_win, 1)
|
||||
|
||||
def chk_win(self, evt, var, lpl, lwolves, lrealwolves):
|
||||
lsafes = len(var.list_players(["oracle", "seer", "guardian angel", "shaman", "hunter", "villager"]))
|
||||
evt.stop_processing = True
|
||||
|
||||
try:
|
||||
if lrealwolves == 0:
|
||||
evt.data["winner"] = "villagers"
|
||||
evt.data["message"] = ("Game over! All the wolves are dead! The villagers " +
|
||||
"round up the remaining cultists, hang them, and live " +
|
||||
"happily ever after.")
|
||||
elif lsafes == 0:
|
||||
evt.data["winner"] = "wolves"
|
||||
evt.data["message"] = ("Game over! All the villagers are dead! The cultists rejoice " +
|
||||
"with their wolf buddies and start plotting to take over the " +
|
||||
"next village.")
|
||||
elif evt.data["winner"][0] != "@":
|
||||
evt.data["winner"] = None
|
||||
except TypeError: # means that evt.data["winner"] isn't a string or something else subscriptable
|
||||
evt.data["winner"] = None
|
||||
|
||||
|
||||
@game_mode("classic", minp = 7, maxp = 21, likelihood = 4)
|
||||
class ClassicMode(object):
|
||||
"""Classic game mode from before all the changes."""
|
||||
|
@ -40,6 +40,10 @@ from src import logger
|
||||
import urllib.request
|
||||
import sqlite3
|
||||
|
||||
# done this way so that events is accessible in !eval (useful for debugging)
|
||||
from src import events
|
||||
Event = events.Event
|
||||
|
||||
debuglog = logger("debug.log", write=False, display=False) # will be True if in debug mode
|
||||
errlog = logger("errors.log")
|
||||
plog = logger(None) #use this instead of print so that logs have timestamps
|
||||
@ -80,6 +84,7 @@ var.PINGING_IFS = False
|
||||
var.TIMERS = {}
|
||||
|
||||
var.ORIGINAL_SETTINGS = {}
|
||||
var.CURRENT_GAMEMODE = {"name": "default"}
|
||||
|
||||
var.LAST_SAID_TIME = {}
|
||||
|
||||
@ -371,6 +376,10 @@ def pm(cli, target, message): # message either privmsg or notice, depending on
|
||||
cli.msg(target, message)
|
||||
|
||||
def reset_settings():
|
||||
if hasattr(var.CURRENT_GAMEMODE, "teardown") and callable(var.CURRENT_GAMEMODE.teardown):
|
||||
var.CURRENT_GAMEMODE.teardown()
|
||||
|
||||
var.CURRENT_GAMEMODE = {"name": "default"}
|
||||
for attr in list(var.ORIGINAL_SETTINGS.keys()):
|
||||
setattr(var, attr, var.ORIGINAL_SETTINGS[attr])
|
||||
dict.clear(var.ORIGINAL_SETTINGS)
|
||||
@ -412,7 +421,6 @@ def reset():
|
||||
var.PINGED_ALREADY_ACCS = []
|
||||
var.NO_LYNCH = []
|
||||
var.FGAMED = False
|
||||
var.CURRENT_GAMEMODE = "default"
|
||||
var.GAMEMODE_VOTES = {} #list of players who have used !game
|
||||
|
||||
reset_settings()
|
||||
@ -1516,7 +1524,7 @@ def stats(cli, nick, chan, rest):
|
||||
else:
|
||||
cli.notice(nick, msg)
|
||||
|
||||
if var.PHASE == "join" or not var.ROLE_REVEAL or var.GAME_MODES[var.CURRENT_GAMEMODE][4]:
|
||||
if var.PHASE == "join" or not var.ROLE_REVEAL or var.GAME_MODES[var.CURRENT_GAMEMODE.name][4]:
|
||||
return
|
||||
|
||||
message = []
|
||||
@ -2148,7 +2156,7 @@ def stop_game(cli, winner = "", abort = False):
|
||||
if won or iwon:
|
||||
winners.append(splr)
|
||||
|
||||
var.update_game_stats(var.CURRENT_GAMEMODE, len(survived) + len(var.DEAD), winner)
|
||||
var.update_game_stats(var.CURRENT_GAMEMODE.name, len(survived) + len(var.DEAD), winner)
|
||||
|
||||
# spit out the list of winners
|
||||
winners.sort()
|
||||
@ -2226,6 +2234,8 @@ def chk_win(cli, end_game = True):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
winner = None
|
||||
message = ""
|
||||
if lpl < 1:
|
||||
message = "Game over! There are no players remaining."
|
||||
winner = "none"
|
||||
@ -2265,8 +2275,15 @@ def chk_win(cli, end_game = True):
|
||||
elif lrealwolves == 0:
|
||||
chk_traitor(cli)
|
||||
return chk_win(cli, end_game)
|
||||
else:
|
||||
|
||||
event = Event("chk_win", {"winner": winner, "message": message})
|
||||
event.dispatch(var, lpl, lwolves, lrealwolves)
|
||||
winner = event.data["winner"]
|
||||
message = event.data["message"]
|
||||
|
||||
if winner is None:
|
||||
return False
|
||||
|
||||
if end_game:
|
||||
players = []
|
||||
if winner == "monsters":
|
||||
@ -5353,7 +5370,7 @@ def transition_night(cli):
|
||||
debuglog(elder, "ELDER DEATH")
|
||||
|
||||
if var.FIRST_NIGHT and chk_win(cli, end_game=False): # prevent game from ending as soon as it begins (useful for the random game mode)
|
||||
start(cli, botconfig.NICK, botconfig.CHANNEL, restart=var.CURRENT_GAMEMODE)
|
||||
start(cli, botconfig.NICK, botconfig.CHANNEL, restart=var.CURRENT_GAMEMODE.name)
|
||||
return
|
||||
|
||||
# game ended from bitten / amnesiac turning, narcolepsy totem expiring, or other weirdness
|
||||
@ -5864,13 +5881,16 @@ def cgamemode(cli, arg):
|
||||
md = modeargs.pop(0)
|
||||
try:
|
||||
gm = var.GAME_MODES[md][0](*modeargs)
|
||||
if hasattr(gm, "startup") and callable(gm.startup):
|
||||
gm.startup()
|
||||
for attr in dir(gm):
|
||||
val = getattr(gm, attr)
|
||||
if (hasattr(var, attr) and not callable(val)
|
||||
and not attr.startswith("_")):
|
||||
var.ORIGINAL_SETTINGS[attr] = getattr(var, attr)
|
||||
setattr(var, attr, val)
|
||||
var.CURRENT_GAMEMODE = md
|
||||
gm.name = md
|
||||
var.CURRENT_GAMEMODE = gm
|
||||
return True
|
||||
except var.InvalidModeException as e:
|
||||
cli.msg(botconfig.CHANNEL, "Invalid mode: "+str(e))
|
||||
@ -6119,7 +6139,7 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
|
||||
if not restart:
|
||||
cli.msg(chan, ("{0}: Welcome to Werewolf, the popular detective/social party "+
|
||||
"game (a theme of Mafia). Using the \002{1}\002 game mode.").format(", ".join(pl), var.CURRENT_GAMEMODE))
|
||||
"game (a theme of Mafia). Using the \002{1}\002 game mode.").format(", ".join(pl), var.CURRENT_GAMEMODE.name))
|
||||
cli.mode(chan, "+m")
|
||||
|
||||
var.ORIGINAL_ROLES = copy.deepcopy(var.ROLES) # Make a copy
|
||||
@ -6876,15 +6896,15 @@ def listroles(cli, nick, chan, rest):
|
||||
rest = re.split(" +", rest.strip(), 1)
|
||||
|
||||
#message if this game mode has been disabled
|
||||
if (not len(rest[0]) or rest[0].isdigit()) and var.GAME_MODES[var.CURRENT_GAMEMODE][4]:
|
||||
txt += " {0}: {1}roles was disabled for the {2} game mode.".format(nick, botconfig.CMD_CHAR, var.CURRENT_GAMEMODE)
|
||||
if (not len(rest[0]) or rest[0].isdigit()) and var.GAME_MODES[var.CURRENT_GAMEMODE.name][4]:
|
||||
txt += " {0}: {1}roles was disabled for the {2} game mode.".format(nick, botconfig.CMD_CHAR, var.CURRENT_GAMEMODE.name)
|
||||
rest = []
|
||||
roleindex = {}
|
||||
#prepend player count if called without any arguments
|
||||
elif not len(rest[0]) and pl > 0:
|
||||
txt += " {0}: There {1} \u0002{2}\u0002 playing.".format(nick, "is" if pl == 1 else "are", pl)
|
||||
if var.PHASE in ["night", "day"]:
|
||||
txt += " Using the {0} game mode.".format(var.CURRENT_GAMEMODE)
|
||||
txt += " Using the {0} game mode.".format(var.CURRENT_GAMEMODE.name)
|
||||
|
||||
#read game mode to get roles for
|
||||
elif len(rest[0]) and not rest[0].isdigit():
|
||||
@ -7091,7 +7111,7 @@ def game_stats(cli, nick, chan, rest):
|
||||
cli.notice(nick, "Wait until the game is over to view stats.")
|
||||
return
|
||||
|
||||
gamemode = var.CURRENT_GAMEMODE
|
||||
gamemode = var.CURRENT_GAMEMODE.name
|
||||
gamesize = None
|
||||
rest = rest.split()
|
||||
# Check for gamemode
|
||||
|
Loading…
x
Reference in New Issue
Block a user