Initial commit to add a bunch of new roles
This commit is contained in:
parent
6717f61e6f
commit
23b2e755ca
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@ GSTATS_RATE_LIMIT = 15
|
||||
PSTATS_RATE_LIMIT = 15
|
||||
TIME_RATE_LIMIT = 60
|
||||
SHOTS_MULTIPLIER = .12 # ceil(shots_multiplier * len_players) = bullets given
|
||||
SHARPSHOOTER_MULTIPLIER = 0.06
|
||||
MIN_PLAYERS = 4
|
||||
MAX_PLAYERS = 21
|
||||
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_LIMIT_WARN = 400
|
||||
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
|
||||
WARN_IDLE_TIME = 180
|
||||
PART_GRACE_TIME = 30
|
||||
@ -38,6 +42,12 @@ GOAT_HERDER = True
|
||||
|
||||
SELF_LYNCH_ALLOWED = 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_COLOR = False
|
||||
@ -47,48 +57,97 @@ KILL_BOLD = False
|
||||
LOG_FILENAME = ""
|
||||
BARE_LOG_FILENAME = ""
|
||||
|
||||
# HIT MISS SUICIDE
|
||||
GUN_CHANCES = ( 5/7 , 1/7 , 1/7 )
|
||||
DRUNK_GUN_CHANCES = ( 2/7 , 3/7 , 2/7 )
|
||||
MANSLAUGHTER_CHANCE = 2/5 # ACCIDENTAL HEADSHOT (FATAL)
|
||||
# HIT MISS SUICIDE HEADSHOT
|
||||
GUN_CHANCES = ( 5/7 , 1/7 , 1/7 , 2/5 )
|
||||
WOLF_GUN_CHANCES = ( 5/7 , 1/7 , 1/7 , 2/5 )
|
||||
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
|
||||
GUARDIAN_ANGEL_DIES_CHANCE = 1/2
|
||||
GUARDIAN_ANGEL_DIES_CHANCE = 0
|
||||
BODYGUARD_DIES_CHANCE = 0
|
||||
DETECTIVE_REVEALED_CHANCE = 2/5
|
||||
SHARPSHOOTER_CHANCE = 1/5 # if sharpshooter is enabled, chance that a gunner will become a sharpshooter instead
|
||||
|
||||
#################################################################################################################
|
||||
# ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ##
|
||||
#################################################################################################################
|
||||
ROLES_GUIDE = { 4 : ( 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ##
|
||||
6 : ( 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ##
|
||||
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: ##
|
||||
#################################################################################################################
|
||||
AMNESIAC_NIGHTS = 3 # amnesiac gets to know their actual role on this night
|
||||
BUREAUCRAT_VOTES = 2 # bureaucrat votes count for this many normal votes
|
||||
|
||||
# DEATH PROTECTION REVEALING NARCOLEPSY SILENCE DESPERATION
|
||||
TOTEM_CHANCES = ( 1/6 , 1/6 , 1/6 , 1/6 , 1/6 , 1/6 )
|
||||
|
||||
GAME_MODES = {}
|
||||
AWAY = ['services.', 'services.int'] # cloaks of people who are away.
|
||||
SIMPLE_NOTIFY = [] # cloaks of people who !simple, who want everything /notice'd
|
||||
|
||||
ROLE_INDICES = {0 : "seer",
|
||||
1 : "wolf",
|
||||
2 : "cursed villager",
|
||||
3 : "village drunk",
|
||||
4 : "harlot",
|
||||
5 : "traitor",
|
||||
6 : "gunner",
|
||||
7 : "werecrow",
|
||||
8 : "guardian angel",
|
||||
9 : "detective"}
|
||||
# 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")
|
||||
DEFAULT_ROLE = "villager"
|
||||
ROLES_INDEX = ( 4 , 6 , 8 , 10 , 12 , 15 , 17 , 18 , 20 )
|
||||
ROLES_GUIDE = {# village roles
|
||||
"villager" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
|
||||
"seer" : ( 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ),
|
||||
"oracle" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
|
||||
"village drunk" : ( 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ),
|
||||
"harlot" : ( 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ),
|
||||
"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.",
|
||||
"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.",
|
||||
"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.")
|
||||
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
|
||||
|
||||
RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules")
|
||||
|
||||
# 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
|
||||
PING_IN = [] # cloaks of users who have opted in for ping
|
||||
@ -152,28 +214,13 @@ def game_mode(name):
|
||||
return decor
|
||||
|
||||
|
||||
CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"],
|
||||
"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
|
||||
# TODO: implement more game modes
|
||||
@game_mode("roles")
|
||||
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()
|
||||
lx = list(ROLES_GUIDE[None])
|
||||
pairs = arg.split(",")
|
||||
if not pairs:
|
||||
raise InvalidModeException("Invalid syntax for mode roles.")
|
||||
@ -184,16 +231,29 @@ class ChangedRolesMode(object):
|
||||
role, num = change
|
||||
try:
|
||||
num = int(num)
|
||||
try:
|
||||
lx[CHANGEABLE_ROLES[role.lower()]] = num
|
||||
except KeyError:
|
||||
if role.lower() in self.ROLES_GUIDE:
|
||||
self.ROLES_GUIDE[role.lower()] = tuple([num] * len(ROLES_INDEX))
|
||||
else:
|
||||
raise InvalidModeException(("The role \u0002{0}\u0002 "+
|
||||
"is not valid.").format(role))
|
||||
except ValueError:
|
||||
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
|
||||
|
||||
@ -221,7 +281,7 @@ with conn:
|
||||
c.execute('DROP TABLE IF EXISTS roles')
|
||||
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,))
|
||||
|
||||
|
||||
@ -229,11 +289,11 @@ with conn:
|
||||
'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, '+
|
||||
'UNIQUE(player, role))'))
|
||||
|
||||
|
||||
|
||||
c.execute(('CREATE TABLE IF NOT EXISTS gamestats (size SMALLINT, villagewins SMALLINT, ' +
|
||||
'wolfwins SMALLINT, totalgames SMALLINT, UNIQUE(size))'))
|
||||
|
||||
|
||||
|
||||
|
||||
if OPT_IN_PING:
|
||||
c.execute('CREATE TABLE IF NOT EXISTS ping (cloak text)')
|
||||
|
||||
@ -261,7 +321,7 @@ def add_simple_rolemsg(clk):
|
||||
def remove_ping(clk):
|
||||
with conn:
|
||||
c.execute('DELETE from ping where cloak=?', (clk,))
|
||||
|
||||
|
||||
def add_ping(clk):
|
||||
with conn:
|
||||
c.execute('INSERT into ping VALUES (?)', (clk,))
|
||||
@ -289,24 +349,24 @@ def update_role_stats(acc, role, won, iwon):
|
||||
def update_game_stats(size, winner):
|
||||
with conn:
|
||||
vwins, wwins, total = 0, 0, 0
|
||||
|
||||
|
||||
c.execute("SELECT villagewins, wolfwins, totalgames FROM gamestats "+
|
||||
"WHERE size=?", (size,))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
vwins, wwins, total = row
|
||||
|
||||
|
||||
if winner == "wolves":
|
||||
wwins += 1
|
||||
elif winner == "villagers":
|
||||
vwins += 1
|
||||
total += 1
|
||||
|
||||
|
||||
c.execute("INSERT OR REPLACE INTO gamestats VALUES (?,?,?,?)",
|
||||
(size, vwins, wwins, total))
|
||||
|
||||
|
||||
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)
|
||||
with conn:
|
||||
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,))
|
||||
player = c.fetchone()
|
||||
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))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
@ -333,7 +393,7 @@ def get_player_totals(acc):
|
||||
return "\u0002{0}\u0002's totals | {1}".format(player[0], ", ".join(role_totals))
|
||||
else:
|
||||
return "{0} has not played any games.".format(acc)
|
||||
|
||||
|
||||
def get_game_stats(size):
|
||||
with conn:
|
||||
for row in c.execute("SELECT * FROM gamestats WHERE size=?", (size,)):
|
||||
@ -352,7 +412,7 @@ def get_game_totals():
|
||||
if row:
|
||||
size_totals.append("\u0002{0}p\u0002: {1}".format(*row))
|
||||
total += row[1]
|
||||
|
||||
|
||||
if len(size_totals) == 0:
|
||||
return "No games have been played."
|
||||
else:
|
||||
|
Loading…
Reference in New Issue
Block a user