Initial commit to add a bunch of new roles

This commit is contained in:
Skizzerz 2014-07-12 13:10:44 -05:00
parent 6717f61e6f
commit 23b2e755ca
2 changed files with 403 additions and 384 deletions

View File

@ -525,6 +525,7 @@ def fleave(cli, nick, chann_, rest):
return return
cli.msg(chan, ("\u0002{0}\u0002 is forcing"+ cli.msg(chan, ("\u0002{0}\u0002 is forcing"+
" \u0002{1}\u0002 to leave.").format(nick, a)) " \u0002{1}\u0002 to leave.").format(nick, a))
if var.ROLE_REVEAL:
cli.msg(chan, "Say goodbye to the \02{0}\02.".format(var.get_role(a))) cli.msg(chan, "Say goodbye to the \02{0}\02.".format(var.get_role(a)))
if var.PHASE == "join": if var.PHASE == "join":
cli.msg(chan, ("New player count: \u0002{0}\u0002").format(len(var.list_players()) - 1)) cli.msg(chan, ("New player count: \u0002{0}\u0002").format(len(var.list_players()) - 1))
@ -591,7 +592,7 @@ def stats(cli, nick, chan, rest):
else: else:
cli.notice(nick, msg) cli.notice(nick, msg)
if var.PHASE == "join": if var.PHASE == "join" or not var.ROLE_REVEAL:
return return
message = [] message = []
@ -609,9 +610,9 @@ def stats(cli, nick, chan, rest):
if "seer" in rs: if "seer" in rs:
rs.remove("seer") rs.remove("seer")
rs.insert(1, "seer") rs.insert(1, "seer")
if "villager" in rs: if var.DEFAULT_ROLE in rs:
rs.remove("villager") rs.remove(var.DEFAULT_ROLE)
rs.append("villager") rs.append(var.DEFAULT_ROLE)
firstcount = len(var.ROLES[rs[0]]) firstcount = len(var.ROLES[rs[0]])
@ -826,15 +827,12 @@ def stop_game(cli, winner = ""):
roles_msg = [] roles_msg = []
var.ORIGINAL_ROLES["cursed villager"] = var.CURSED # A hack
var.ORIGINAL_ROLES["gunner"] = list(var.GUNNERS.keys())
lroles = list(var.ORIGINAL_ROLES.keys()) lroles = list(var.ORIGINAL_ROLES.keys())
lroles.remove("wolf") lroles.remove("wolf")
lroles.insert(0, "wolf") # picky, howl consistency lroles.insert(0, "wolf") # picky, howl consistency
for role in lroles: for role in lroles:
if len(var.ORIGINAL_ROLES[role]) == 0 or role == "villager": if len(var.ORIGINAL_ROLES[role]) == 0 or role == var.DEFAULT_ROLE:
continue continue
playersinrole = list(var.ORIGINAL_ROLES[role]) playersinrole = list(var.ORIGINAL_ROLES[role])
for i,plr in enumerate(playersinrole): for i,plr in enumerate(playersinrole):
@ -2402,16 +2400,11 @@ def cgamemode(cli, *args):
for arg in args: for arg in args:
modeargs = arg.split("=", 1) modeargs = arg.split("=", 1)
if len(modeargs) < 2: # no equal sign in the middle of the arg modeargs = map(strip, modeargs)
cli.msg(botconfig.CHANNEL, "Invalid syntax.")
return False
modeargs[0] = modeargs[0].strip()
if modeargs[0] in var.GAME_MODES.keys(): if modeargs[0] in var.GAME_MODES.keys():
md = modeargs.pop(0) md = modeargs.pop(0)
modeargs[0] = modeargs[0].strip()
try: try:
gm = var.GAME_MODES[md](modeargs[0]) gm = var.GAME_MODES[md](*modeargs)
for attr in dir(gm): for attr in dir(gm):
val = getattr(gm, attr) val = getattr(gm, attr)
if (hasattr(var, attr) and not callable(val) if (hasattr(var, attr) and not callable(val)
@ -2456,9 +2449,9 @@ def start(cli, nick, chann_, rest):
cli.msg(chan, "{0}: \u0002{1}\u0002 or more players are required to play.".format(nick, var.MIN_PLAYERS)) cli.msg(chan, "{0}: \u0002{1}\u0002 or more players are required to play.".format(nick, var.MIN_PLAYERS))
return return
for pcount in range(len(villagers), var.MIN_PLAYERS - 1, -1): for index in range(len(var.ROLES_INDEX), -1, -1):
addroles = var.ROLES_GUIDE.get(pcount) if var.ROLES_INDEX[index] < len(villagers):
if addroles: addroles = {k:v[index] for k,v in var.ROLES_GUIDE}
break break
else: else:
cli.msg(chan, "{0}: No game settings are defined for \u0002{1}\u0002 player games.".format(nick, len(villagers))) cli.msg(chan, "{0}: No game settings are defined for \u0002{1}\u0002 player games.".format(nick, len(villagers)))
@ -2471,10 +2464,8 @@ def start(cli, nick, chann_, rest):
if var.ORIGINAL_SETTINGS: # Custom settings if var.ORIGINAL_SETTINGS: # Custom settings
while True: while True:
wvs = (addroles[var.INDEX_OF_ROLE["wolf"]] + wvs = sum(addroles[r] for r in var.WOLFCHAT_ROLES)
addroles[var.INDEX_OF_ROLE["traitor"]]) if len(villagers) < (sum(addroles.values()) - sum([addroles[r] for r in var.TEMPLATE_RESTRICTIONS.keys()])):
if len(villagers) < (sum(addroles) - addroles[var.INDEX_OF_ROLE["gunner"]] -
addroles[var.INDEX_OF_ROLE["cursed villager"]]):
cli.msg(chan, "There are too few players in the "+ cli.msg(chan, "There are too few players in the "+
"game to use the custom roles.") "game to use the custom roles.")
elif not wvs: elif not wvs:
@ -2496,7 +2487,6 @@ def start(cli, nick, chann_, rest):
COMMANDS["start"] = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")] COMMANDS["start"] = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")]
var.ROLES = {} var.ROLES = {}
var.CURSED = []
var.GUNNERS = {} var.GUNNERS = {}
var.WOLF_GUNNERS = {} var.WOLF_GUNNERS = {}
var.SEEN = [] var.SEEN = []
@ -2505,10 +2495,8 @@ def start(cli, nick, chann_, rest):
var.GUARDED = {} var.GUARDED = {}
var.HVISITED = {} var.HVISITED = {}
villager_roles = ("gunner", "cursed villager") for role, count in addroles.items():
for i, count in enumerate(addroles): if role in var.TEMPLATE_RESTRICTIONS.keys():
role = var.ROLE_INDICES[i]
if role in villager_roles:
var.ROLES[role] = [None] * count var.ROLES[role] = [None] * count
continue # We deal with those later, see below continue # We deal with those later, see below
selected = random.sample(villagers, count) selected = random.sample(villagers, count)
@ -2516,43 +2504,32 @@ def start(cli, nick, chann_, rest):
for x in selected: for x in selected:
villagers.remove(x) villagers.remove(x)
# Now for the villager roles # Now for the templates
# Select cursed (just a villager) for template, restrictions in var.TEMPLATE_RESTRICTIONS.items():
if var.ROLES["cursed villager"]: if template == "sharpshooter":
possiblecursed = pl[:] continue # sharpshooter gets applied specially
for cannotbe in (var.ROLES["wolf"] + var.ROLES["werecrow"] +
var.ROLES["seer"]):
# traitor can be cursed
possiblecursed.remove(cannotbe)
var.CURSED = random.sample(possiblecursed, len(var.ROLES["cursed villager"]))
del var.ROLES["cursed villager"]
# Select gunner (also a villager)
if var.ROLES["gunner"]:
possible = pl[:] possible = pl[:]
for cannotbe in (var.ROLES["wolf"] + var.ROLES["werecrow"] + for cannotbe in [p for r in restrictions for p in var.ROLES[r]]:
var.ROLES["traitor"]): possible.removed(cannotbe)
possible.remove(cannotbe) var.ROLES[template] = random.sample(possible, len(var.ROLES[template]))
for csd in var.CURSED: # cursed cannot be gunner # Handle gunner
if csd in possible: cannot_be_sharpshooter = [p for r in var.TEMPLATE_RESTRICTIONS["sharpshooter"] for p in var.ROLES[r]]
possible.remove(csd) for gunner in var.GUNNER_LIST:
if gunner in var.ROLES["village drunk"]:
for gnr in random.sample(possible, len(var.ROLES["gunner"])): var.GUNNERS[gunner] = (var.DRUNK_SHOTS_MULTIPLIER * math.ceil(var.SHOTS_MULTIPLIER * len(pl)))
if gnr in var.ROLES["village drunk"]: elif gunner not in cannot_be_sharpshooter and random.random() <= var.SHARPSHOOTER_CHANCE:
var.GUNNERS[gnr] = (var.DRUNK_SHOTS_MULTIPLIER * var.GUNNERS[gunner] = math.ceil(var.SHARPSHOOTER_MULTIPLIER * len(pl))
math.ceil(var.SHOTS_MULTIPLIER * len(pl))) var.ROLES["gunner"].remove(gunner)
var.ROLES["sharpshooter"].append(gunner)
else: else:
var.GUNNERS[gnr] = math.ceil(var.SHOTS_MULTIPLIER * len(pl)) var.GUNNERS[gunner] = math.ceil(var.SHOTS_MULTIPLIER * len(pl))
del var.ROLES["gunner"]
var.SPECIAL_ROLES["goat herder"] = [] var.SPECIAL_ROLES["goat herder"] = []
if var.GOAT_HERDER: if var.GOAT_HERDER:
var.SPECIAL_ROLES["goat herder"] = [ nick ] var.SPECIAL_ROLES["goat herder"] = [ nick ]
var.ROLES["villager"] = villagers var.ROLES[var.DEFAULT_ROLE] = villagers
cli.msg(chan, ("{0}: Welcome to Werewolf, the popular detective/social party "+ cli.msg(chan, ("{0}: Welcome to Werewolf, the popular detective/social party "+
"game (a theme of Mafia).").format(", ".join(pl))) "game (a theme of Mafia).").format(", ".join(pl)))
@ -2586,15 +2563,8 @@ def start(cli, nick, chann_, rest):
for plr in var.ROLES[rol]: for plr in var.ROLES[rol]:
var.LOGGER.logBare(plr, "ROLE", rol) var.LOGGER.logBare(plr, "ROLE", rol)
if var.CURSED:
var.LOGGER.log("Cursed Villagers: "+", ".join(var.CURSED))
for plr in var.CURSED:
var.LOGGER.logBare(plr+" ROLE cursed villager")
if var.GUNNERS: if var.GUNNERS:
var.LOGGER.log("Villagers With Bullets: "+", ".join([x+"("+str(y)+")" for x,y in var.GUNNERS.items()])) var.LOGGER.log("Villagers With Bullets: "+", ".join([x+"("+str(y)+")" for x,y in var.GUNNERS.items()]))
for plr in var.GUNNERS:
var.LOGGER.logBare(plr, "ROLE gunner")
var.LOGGER.log("***") var.LOGGER.log("***")
@ -3033,10 +3003,13 @@ def myrole(cli, nick, chan, rest):
# Check for gun/bullets # Check for gun/bullets
if nick in var.GUNNERS and var.GUNNERS[nick]: if nick in var.GUNNERS and var.GUNNERS[nick]:
role = "gunner"
if nick in var.ROLES["sharpshooter"]:
role = "sharpshooter"
if var.GUNNERS[nick] == 1: if var.GUNNERS[nick] == 1:
pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format(var.GUNNERS[nick], "bullet")) pm(cli, nick, "You are a {0} and have a \02gun\02 with {1} {2}.".format(role, var.GUNNERS[nick], "bullet"))
else: else:
pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format(var.GUNNERS[nick], "bullets")) pm(cli, nick, "You are a {0} and have a \02gun\02 with {1} {2}.".format(role, var.GUNNERS[nick], "bullets"))
elif nick in var.WOLF_GUNNERS and var.WOLF_GUNNERS[nick]: elif nick in var.WOLF_GUNNERS and var.WOLF_GUNNERS[nick]:
if var.WOLF_GUNNERS[nick] == 1: if var.WOLF_GUNNERS[nick] == 1:
pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format(var.WOLF_GUNNERS[nick], "bullet")) pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format(var.WOLF_GUNNERS[nick], "bullet"))
@ -3286,9 +3259,6 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
def revroles(cli, nick, chan, rest): def revroles(cli, nick, chan, rest):
if var.PHASE != "none": if var.PHASE != "none":
cli.msg(chan, str(var.ROLES)) cli.msg(chan, str(var.ROLES))
if var.PHASE in ('night','day'):
cli.msg(chan, "Cursed: "+str(var.CURSED))
cli.msg(chan, "Gunners: "+str(list(var.GUNNERS.keys())))
@cmd("fgame", admin_only=True) @cmd("fgame", admin_only=True)
@ -3448,12 +3418,8 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
if who == botconfig.NICK or not who: if who == botconfig.NICK or not who:
cli.msg(chan, "No.") cli.msg(chan, "No.")
return return
if rol not in var.ROLES.keys(): if rol in var.ROLES.keys() or rol.startswith("gunner") or rol.startswith("sharpshooter"):
pl = var.list_players() if rol.startswith("gunner") or rol.startswith("sharpshooter"):
if var.PHASE not in ("night", "day"):
cli.msg(chan, "This is only allowed in game.")
return
if rol.startswith("gunner"):
rolargs = re.split(" +",rol, 1) rolargs = re.split(" +",rol, 1)
if len(rolargs) == 2 and rolargs[1].isdigit(): if len(rolargs) == 2 and rolargs[1].isdigit():
if len(rolargs[1]) < 7: if len(rolargs[1]) < 7:
@ -3462,20 +3428,13 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
else: else:
var.GUNNERS[who] = 999 var.GUNNERS[who] = 999
var.WOLF_GUNNERS[who] = 999 var.WOLF_GUNNERS[who] = 999
else: elif rol.startswith("gunner"):
var.GUNNERS[who] = math.ceil(var.SHOTS_MULTIPLIER * len(pl)) var.GUNNERS[who] = math.ceil(var.SHOTS_MULTIPLIER * len(pl))
if who not in pl: else:
var.ROLES["villager"].append(who) var.GUNNERS[who] = math.ceil(var.SHARPSHOOTER_MULTIPLIER * len(pl))
elif rol == "cursed villager":
if who not in var.CURSED:
var.CURSED.append(who)
if who not in pl:
var.ROLES["villager"].append(who)
else: else:
cli.msg(chan, "Not a valid role.") cli.msg(chan, "Not a valid role.")
return return
cli.msg(chan, "Operation successful.")
return
if who in var.list_players(): if who in var.list_players():
var.del_player(who) var.del_player(who)
var.ROLES[rol].append(who) var.ROLES[rol].append(who)

View File

@ -12,6 +12,7 @@ GSTATS_RATE_LIMIT = 15
PSTATS_RATE_LIMIT = 15 PSTATS_RATE_LIMIT = 15
TIME_RATE_LIMIT = 60 TIME_RATE_LIMIT = 60
SHOTS_MULTIPLIER = .12 # ceil(shots_multiplier * len_players) = bullets given SHOTS_MULTIPLIER = .12 # ceil(shots_multiplier * len_players) = bullets given
SHARPSHOOTER_MULTIPLIER = 0.06
MIN_PLAYERS = 4 MIN_PLAYERS = 4
MAX_PLAYERS = 21 MAX_PLAYERS = 21
DRUNK_SHOTS_MULTIPLIER = 3 DRUNK_SHOTS_MULTIPLIER = 3
@ -24,6 +25,9 @@ JOIN_TIME_LIMIT = 3600
SHORT_DAY_PLAYERS = 6 # Number of players left to have a short day SHORT_DAY_PLAYERS = 6 # Number of players left to have a short day
SHORT_DAY_LIMIT_WARN = 400 SHORT_DAY_LIMIT_WARN = 400
SHORT_DAY_LIMIT_CHANGE = 120 SHORT_DAY_LIMIT_CHANGE = 120
# If time lord is lynched, the day timer gets set to this instead
TIME_LORD_WARN = 60
TIME_LORD_CHANGE = 30
KILL_IDLE_TIME = 300 KILL_IDLE_TIME = 300
WARN_IDLE_TIME = 180 WARN_IDLE_TIME = 180
PART_GRACE_TIME = 30 PART_GRACE_TIME = 30
@ -38,6 +42,12 @@ GOAT_HERDER = True
SELF_LYNCH_ALLOWED = True SELF_LYNCH_ALLOWED = True
HIDDEN_TRAITOR = True HIDDEN_TRAITOR = True
VENGEFUL_GHOST_KNOWS_ROLES = True
WOLF_MAYOR = True
BODYGUARD_CAN_GUARD_SELF = True
START_WITH_DAY = False
WOLF_STEALS_GUN = True # at night, the wolf can steal steal the victim's bullets
ROLE_REVEAL = True
CARE_BOLD = False CARE_BOLD = False
CARE_COLOR = False CARE_COLOR = False
@ -47,48 +57,97 @@ KILL_BOLD = False
LOG_FILENAME = "" LOG_FILENAME = ""
BARE_LOG_FILENAME = "" BARE_LOG_FILENAME = ""
# HIT MISS SUICIDE # HIT MISS SUICIDE HEADSHOT
GUN_CHANCES = ( 5/7 , 1/7 , 1/7 ) GUN_CHANCES = ( 5/7 , 1/7 , 1/7 , 2/5 )
DRUNK_GUN_CHANCES = ( 2/7 , 3/7 , 2/7 ) WOLF_GUN_CHANCES = ( 5/7 , 1/7 , 1/7 , 2/5 )
MANSLAUGHTER_CHANCE = 2/5 # ACCIDENTAL HEADSHOT (FATAL) DRUNK_GUN_CHANCES = ( 2/7 , 3/7 , 2/7 , 2/5 )
SHARPSHOOTER_GUN_CHANCES = ( 1 , 0 , 0 , 1 )
GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 1/4 GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 1/4
GUARDIAN_ANGEL_DIES_CHANCE = 1/2 GUARDIAN_ANGEL_DIES_CHANCE = 0
BODYGUARD_DIES_CHANCE = 0
DETECTIVE_REVEALED_CHANCE = 2/5 DETECTIVE_REVEALED_CHANCE = 2/5
SHARPSHOOTER_CHANCE = 1/5 # if sharpshooter is enabled, chance that a gunner will become a sharpshooter instead
################################################################################################################# AMNESIAC_NIGHTS = 3 # amnesiac gets to know their actual role on this night
# ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ## BUREAUCRAT_VOTES = 2 # bureaucrat votes count for this many normal votes
#################################################################################################################
ROLES_GUIDE = { 4 : ( 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ## # DEATH PROTECTION REVEALING NARCOLEPSY SILENCE DESPERATION
6 : ( 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ## TOTEM_CHANCES = ( 1/6 , 1/6 , 1/6 , 1/6 , 1/6 , 1/6 )
8 : ( 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 ), ##
10 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 ), ##
12 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ), ##
15 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ), ##
17 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ##
18 : ( 1 , 3 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ##
20 : ( 1 , 4 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ##
None : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )} ##
#################################################################################################################
# Notes: ##
#################################################################################################################
GAME_MODES = {} GAME_MODES = {}
AWAY = ['services.', 'services.int'] # cloaks of people who are away. AWAY = ['services.', 'services.int'] # cloaks of people who are away.
SIMPLE_NOTIFY = [] # cloaks of people who !simple, who want everything /notice'd SIMPLE_NOTIFY = [] # cloaks of people who !simple, who want everything /notice'd
ROLE_INDICES = {0 : "seer", # TODO: move this to a game mode called "fixed" once we implement a way to randomize roles (and have that game mode be called "random")
1 : "wolf", DEFAULT_ROLE = "villager"
2 : "cursed villager", ROLES_INDEX = ( 4 , 6 , 8 , 10 , 12 , 15 , 17 , 18 , 20 )
3 : "village drunk", ROLES_GUIDE = {# village roles
4 : "harlot", "villager" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
5 : "traitor", "seer" : ( 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ),
6 : "gunner", "oracle" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
7 : "werecrow", "village drunk" : ( 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ),
8 : "guardian angel", "harlot" : ( 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ),
9 : "detective"} "guardian angel" : ( 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 ),
"bodyguard" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"detective" : ( 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 ),
"village elder" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"time lord" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"matchmaker" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"mad scientist" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"hunter" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"shaman" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
# wolf roles
"wolf" : ( 1 , 1 , 1 , 2 , 2 , 3 , 3 , 3 , 4 ),
"traitor" : ( 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ),
"werecrow" : ( 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 ),
"cultist" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"minion" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"hag" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"wolf cub" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"sorcerer" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
# neutral roles
"lycan" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"vengeful ghost" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"clone" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"crazed shaman" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"fool" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"monster" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
# templates
"cursed villager" : ( 0 , 1 , 1 , 1 , 1 , 1 , 1 , 2 , 2 ),
"gunner" : ( 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 ),
# NB: for sharpshooter, numbers can't be higher than gunner, since gunners get converted to sharpshooters. This is the MAX number of gunners that can be converted.
"sharpshooter" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"mayor" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"assassin" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"amnesiac" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
"bureaucrat" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
}
INDEX_OF_ROLE = dict((v,k) for k,v in ROLE_INDICES.items()) # Harlot dies when visiting, gunner kills when shooting, GA and bodyguard have a chance at dying when guarding
WOLF_ROLES = ["wolf", "werecrow", "wolf cub"]
# Access to wolfchat, and counted towards the # of wolves vs villagers when determining if a side has won
WOLFCHAT_ROLES = ["wolf", "traitor", "werecrow", "hag", "wolf cub", "sorceror"]
# Wins with the wolves, even if the roles are not necessarily wolves themselves
WOLFTEAM_ROLES = ["wolf", "traitor", "werecrow", "hag", "wolf cub", "sorceror", "minion", "cultist"]
# The roles in here are considered templates and will be applied on TOP of other roles. The restrictions are a list of roles that they CANNOT be applied to
# NB: if you want a template to apply to everyone, list it here but make the restrictions an empty list. Templates not listed here are considered full roles instead
TEMPLATE_RESTRICTIONS = {"cursed villager" : ["wolf", "wolf cub", "werecrow", "seer", "fool"],
"gunner" : ["wolf", "traitor", "werecrow", "hag", "wolf cub", "sorcerer", "minion", "cultist", "fool", "cursed villager"],
"sharpshooter" : ["wolf", "traitor", "werecrow", "hag", "wolf cub", "sorcerer", "minion", "cultist", "fool", "cursed villager"],
"mayor" : ["fool"],
"assassin" : ["seer", "harlot", "detective", "bodyguard", "guardian angel", "village drunk", "hunter", "shaman", "crazed shaman", "fool", "mayor"],
"amnesiac" : ["villager", "cultist"],
"bureaucrat" : [],
}
TEMPLATE_KEYS = {"cursed villager" : "CURSED",
"gunner" : "GUNNER_LIST",
"mayor" : "MAYORS",
"assassin" : "ASSASSINS",
"amnesiac" : "AMNESIACS",
"bureaucrat" : "BUREAUCRATS",
}
NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.", NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.",
"A pool of blood and wolf paw prints are found.", "A pool of blood and wolf paw prints are found.",
@ -98,14 +157,17 @@ LYNCH_MESSAGES = ("The villagers, after much debate, finally decide on lynching
"Despite protests, the mob drags their victim to the hanging tree. \u0002{0}\u0002 succumbs to the will of the horde, and is hanged. The villagers have killed a \u0002{1}\u0002.", "Despite protests, the mob drags their victim to the hanging tree. \u0002{0}\u0002 succumbs to the will of the horde, and is hanged. The villagers have killed a \u0002{1}\u0002.",
"Resigned to the inevitable, \u0002{0}\u0002 is led to the gallows. Once the twitching stops, it is discovered that the village lynched a \u0002{1}\u0002.", "Resigned to the inevitable, \u0002{0}\u0002 is led to the gallows. Once the twitching stops, it is discovered that the village lynched a \u0002{1}\u0002.",
"Before the rope is pulled, \u0002{0}\u0002, the \u0002{1}\u0002, throws a grenade at the mob. The grenade explodes early.") "Before the rope is pulled, \u0002{0}\u0002, the \u0002{1}\u0002, throws a grenade at the mob. The grenade explodes early.")
LYNCH_MESSAGES_NO_REVEAL = ("The villagers, after much debate, finally decide on lynching \u0002{0}\u0002.",
"Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002.",
"Despite protests, the mob drags their victim to the hanging tree. \u0002{0}\u0002 succumbs to the will of the horde, and is hanged.",
"Resigned to the inevitable, \u0002{0}\u0002 is led to the gallows.",
"Before the rope is pulled, \u0002{0}\u0002 throws a grenade at the mob. The grenade explodes early.")
import botconfig import botconfig
RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules") RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules")
# Other settings: # Other settings:
START_WITH_DAY = False
WOLF_STEALS_GUN = True # at night, the wolf can steal steal the victim's bullets
OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged
PING_IN = [] # cloaks of users who have opted in for ping PING_IN = [] # cloaks of users who have opted in for ping
@ -152,28 +214,13 @@ def game_mode(name):
return decor return decor
CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"], # TODO: implement more game modes
"wolves" : INDEX_OF_ROLE["wolf"],
"cursed" : INDEX_OF_ROLE["cursed villager"],
"drunks" : INDEX_OF_ROLE["village drunk"],
"harlots" : INDEX_OF_ROLE["harlot"],
"traitors" : INDEX_OF_ROLE["traitor"],
"gunners" : INDEX_OF_ROLE["gunner"],
"werecrows" : INDEX_OF_ROLE["werecrow"],
"angels" : INDEX_OF_ROLE["guardian angel"],
"detectives" : INDEX_OF_ROLE["detective"]}
# TODO: implement game modes
@game_mode("roles") @game_mode("roles")
class ChangedRolesMode(object): class ChangedRolesMode(object):
"""Example: !fgame roles=wolves:1,seers:0,angels:1""" """Example: !fgame roles=wolf:1,seer:0,guardian angel:1"""
def __init__(self, arg): def __init__(self, arg = ""):
self.ROLES_GUIDE = ROLES_GUIDE.copy() self.ROLES_GUIDE = ROLES_GUIDE.copy()
lx = list(ROLES_GUIDE[None])
pairs = arg.split(",") pairs = arg.split(",")
if not pairs: if not pairs:
raise InvalidModeException("Invalid syntax for mode roles.") raise InvalidModeException("Invalid syntax for mode roles.")
@ -184,16 +231,29 @@ class ChangedRolesMode(object):
role, num = change role, num = change
try: try:
num = int(num) num = int(num)
try: if role.lower() in self.ROLES_GUIDE:
lx[CHANGEABLE_ROLES[role.lower()]] = num self.ROLES_GUIDE[role.lower()] = tuple([num] * len(ROLES_INDEX))
except KeyError: else:
raise InvalidModeException(("The role \u0002{0}\u0002 "+ raise InvalidModeException(("The role \u0002{0}\u0002 "+
"is not valid.").format(role)) "is not valid.").format(role))
except ValueError: except ValueError:
raise InvalidModeException("A bad value was used in mode roles.") raise InvalidModeException("A bad value was used in mode roles.")
for k in ROLES_GUIDE.keys():
self.ROLES_GUIDE[k] = tuple(lx)
@game_mode("evilvillage")
class EvilVillageMode(object):
def __init__(self):
self.MIN_PLAYERS = 6
self.MAX_PLAYERS = 12
self.DEFAULT_ROLE = "cultist"
self.ROLES_INDEX = ( 6 , 10 )
self.ROLES_GUIDE = {# village roles
"oracle" : ( 1 , 1 ),
"shaman" : ( 1 , 1 ),
"bodyguard" : ( 0 , 1 ),
# wolf roles
"wolf" : ( 1 , 1 ),
"minion" : ( 0 , 1 ),
}
# Persistence # Persistence
@ -221,7 +281,7 @@ with conn:
c.execute('DROP TABLE IF EXISTS roles') c.execute('DROP TABLE IF EXISTS roles')
c.execute('CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)') c.execute('CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)')
for x in ["villager"]+list(ROLE_INDICES.values()): for x in list(ROLES_GUIDE.keys()):
c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,)) c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,))
@ -306,7 +366,7 @@ def update_game_stats(size, winner):
(size, vwins, wwins, total)) (size, vwins, wwins, total))
def get_player_stats(acc, role): def get_player_stats(acc, role):
if role.lower() not in ["villager"] + [v.lower() for k, v in ROLE_INDICES.items()]: if role.lower() not in [k.lower() for k in ROLES_GUIDE.keys()]:
return "No such role: {0}".format(role) return "No such role: {0}".format(role)
with conn: with conn:
c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,)) c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,))
@ -325,7 +385,7 @@ def get_player_totals(acc):
c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,)) c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,))
player = c.fetchone() player = c.fetchone()
if player: if player:
for role in ["villager"] + [v for k, v in ROLE_INDICES.items()]: for role in [k.lower() for k in ROLES_GUIDE.keys()]:
c.execute("SELECT totalgames FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", (acc, role)) c.execute("SELECT totalgames FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", (acc, role))
row = c.fetchone() row = c.fetchone()
if row: if row: