Revert "Revert last 3 commits - broken code"

This reverts commit 5e91556426.
This commit is contained in:
skizzerz 2016-08-10 10:34:24 -05:00
parent 5e91556426
commit 09d90fd7e2
7 changed files with 570 additions and 383 deletions

View File

@ -229,7 +229,8 @@ class VillagergameMode(GameMode):
tgt = hlt tgt = hlt
if not tgt: if not tgt:
tgt = random.choice(pl) tgt = random.choice(pl)
var.KILLS[botconfig.NICK] = [tgt] from src.roles import wolf
wolf.KILLS[botconfig.NICK] = [tgt]
@game_mode("foolish", minp = 8, maxp = 24, likelihood = 8) @game_mode("foolish", minp = 8, maxp = 24, likelihood = 8)
class FoolishMode(GameMode): class FoolishMode(GameMode):

151
src/roles/hunter.py Normal file
View File

@ -0,0 +1,151 @@
import re
import random
from collections import defaultdict
import src.settings as var
from src.utilities import *
from src import debuglog, errlog, plog
from src.decorators import cmd, event_listener
from src.messages import messages
from src.events import Event
KILLS = {} # type: Dict[str, str]
HUNTERS = set()
PASSED = set()
@cmd("kill", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("hunter",))
def hunter_kill(cli, nick, chan, rest):
"""Kill someone once per game."""
if nick in HUNTERS and nick not in KILLS:
pm(cli, nick, messages["hunter_already_killed"])
return
pieces = re.split(" +", rest)
victim = pieces[0]
orig = victim
evt = Event("targeted_command", {"target": victim, "misdirection": True, "exchange": True})
evt.dispatch(cli, var, "kill", nick, victim, frozenset({"detrimental"}))
if evt.prevent_default:
return
victim = evt.data["target"]
KILLS[nick] = victim
HUNTERS.add(nick)
PASSED.discard(nick)
msg = messages["wolf_target"].format(orig)
pm(cli, nick, messages["player"].format(msg))
debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, get_role(nick), victim, get_role(victim)))
chk_nightdone(cli)
@cmd("retract", "r", chan=False, pm=True, playing=True, phases=("night",), roles=("hunter",))
def hunter_retract(cli, nick, chan, rest):
"""Removes a hunter's kill selection."""
if nick not in KILLS and nick not in PASSED:
return
if nick in KILLS:
del KILLS[nick]
HUNTERS.discard(nick)
PASSED.discard(nick)
pm(cli, nick, messages["retracted_kill"])
@cmd("pass", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("hunter",))
def hunter_pass(cli, nick, chan, rest):
"""Do not use hunter's once-per-game kill tonight."""
if nick in HUNTERS and nick not in KILLS:
pm(cli, nick, messages["hunter_already_killed"])
return
if nick in KILLS:
del KILLS[nick]
HUNTERS.discard(nick)
PASSED.add(nick)
pm(cli, nick, messages["hunter_pass"])
@event_listener("del_player")
def on_del_player(evt, cli, var, nick, nickrole, nicktpls, lynched, end_game, death_triggers, killer_role, deadlist, original, ismain, refresh_pl):
for h,v in list(KILLS.items()):
if v == nick:
HUNTERS.discard(h)
PASSED.discard(h)
pm(cli, h, messages["hunter_discard"])
del KILLS[h]
elif h == nick:
del KILLS[h]
@event_listener("rename_player")
def on_rename(evt, cli, var, prefix, nick):
kvp = []
for a,b in KILLS.items():
if a == prefix:
a = nick
if b == prefix:
b = nick
kvp.append((a,b))
KILLS.update(kvp)
if prefix in KILLS:
del KILLS[prefix]
if prefix in HUNTERS:
HUNTERS.discard(prefix)
HUNTERS.add(nick)
if prefix in PASSED:
PASSED.discard(prefix)
PASSED.add(nick)
@event_listener("acted")
def on_acted(evt, cli, var, nick, sender):
if nick in KILLS:
evt.data["acted"] = True
@event_listener("transition_day", priority=2)
def on_transition_day(evt, cli, var):
for k, d in list(KILLS.items()):
evt.data["victims"].append(d)
evt.data["onlybywolves"].discard(d)
evt.data["killers"][d] = k
# important, otherwise our del_player listener lets hunter kill again
del KILLS[k]
@event_listener("exchange_roles")
def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
if actor in KILLS:
del KILLS[actor]
if nick in KILLS:
del KILLS[nick]
HUNTERS.discard(actor)
HUNTERS.discard(nick)
PASSED.discard(actor)
PASSED.discard(nick)
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, cli, var):
evt.data["actedcount"] += len(KILLS) + len(PASSED)
evt.data["nightroles"].extend([p for p in var.ROLES["hunter"] if p not in HUNTERS or p in KILLS])
@event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, cli, var):
ps = list_players()
for hunter in var.ROLES["hunter"]:
if hunter in HUNTERS:
continue #already killed
pl = ps[:]
random.shuffle(pl)
pl.remove(hunter)
if hunter in var.PLAYERS and not is_user_simple(hunter):
pm(cli, hunter, messages["hunter_notify"])
else:
pm(cli, hunter, messages["hunter_simple"])
pm(cli, hunter, "Players: " + ", ".join(pl))
@event_listener("begin_day")
def on_begin_day(evt, cli, var):
KILLS.clear()
PASSED.clear()
@event_listener("reset")
def on_reset(evt, var):
KILLS.clear()
PASSED.clear()
HUNTERS.clear()
# vim: set sw=4 expandtab:

View File

@ -82,6 +82,11 @@ def on_rename(evt, cli, var, prefix, nick):
SEEN.remove(prefix) SEEN.remove(prefix)
SEEN.add(nick) SEEN.add(nick)
@event_listener("acted")
def on_acted(evt, cli, var, nick, sender):
if nick in SEEN:
evt.data["acted"] = True
@event_listener("exchange_roles") @event_listener("exchange_roles")
def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role): def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
if actor_role in ("seer", "oracle", "augur"): if actor_role in ("seer", "oracle", "augur"):

52
src/roles/villager.py Normal file
View File

@ -0,0 +1,52 @@
import src.settings as var
from src.utilities import *
from src import debuglog, errlog, plog
from src.decorators import cmd, event_listener
from src.messages import messages
from src.events import Event
# handles villager and cultist
@event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, cli, var):
if var.FIRST_NIGHT or var.ALWAYS_PM_ROLE:
villroles = var.HIDDEN_VILLAGERS | {"villager"}
if var.DEFAULT_ROLE == "villager":
villroles |= var.HIDDEN_ROLES
villagers = list_players(villroles)
for villager in villagers:
if villager in var.PLAYERS and not is_user_simple(villager):
pm(cli, villager, messages["villager_notify"])
else:
pm(cli, villager, messages["villager_simple"])
cultroles = {"cultist"}
if var.DEFAULT_ROLE == "cultist":
cultroles |= var.HIDDEN_ROLES
cultists = list_players(cultroles)
for cultist in cultists:
if cultist in var.PLAYERS and not is_user_simple(cultist):
pm(cli, cultist, messages["cultist_notify"])
else:
pm(cli, cultist, messages["cultist_simple"])
# No listeners should register before this one
# This sets up the initial state, based on village/wolfteam/neutral affiliation
@event_listener("player_win", priority=0)
def on_player_win(evt, cli, var, nick, role, winner, survived):
# init won/iwon to False
evt.data["won"] = False
evt.data["iwon"] = False
if role in var.WOLFTEAM_ROLES or (var.DEFAULT_ROLE == "cultist" and role in var.HIDDEN_ROLES):
if winner == "wolves":
evt.data["won"] = True
evt.data["iwon"] = survived
elif role in var.TRUE_NEUTRAL_ROLES:
# handled in their individual files
pass
elif winner == "villagers":
evt.data["won"] = True
evt.data["iwon"] = survived
# vim: set sw=4 expandtab:

270
src/roles/wolf.py Normal file
View File

@ -0,0 +1,270 @@
import re
import random
from collections import defaultdict
import src.settings as var
from src.utilities import *
from src import debuglog, errlog, plog
from src.decorators import cmd, event_listener
from src.messages import messages
from src.events import Event
KILLS = {} # type: Dict[str, List[str]]
@cmd("kill", chan=False, pm=True, playing=True, phases=("night",))
def wolf_kill(cli, nick, chan, rest):
"""Kills one or more players as a wolf."""
role = get_role(nick)
# eventually cub will listen on targeted_command and block kills that way
if role not in var.WOLF_ROLES - {"wolf cub"}:
return
if nick in var.SILENCED:
pm(cli, nick, messages["silenced"])
return
if var.DISEASED_WOLVES:
pm(cli, nick, messages["ill_wolves"])
return
# eventually crow will listen on targeted_command and block kills that way
# (or more likely, that restriction will be lifted and crow can do both)
if role == "werecrow" and var.OBSERVED.get(nick):
pm(cli, nick, messages["werecrow_transformed_nokill"])
return
pieces = re.split(" +", rest)
victims = []
orig = []
num_kills = 1
if var.ANGRY_WOLVES:
num_kills = 2
i = 0
extra = 0
while i < num_kills + extra:
try:
victim = pieces[i]
except IndexError:
break
if victim.lower() == "and":
extra += 1
i += 1
victim = pieces[i]
victim = get_victim(cli, nick, victim, False)
if not victim:
return
if victim == nick:
pm(cli, nick, messages["no_suicide"])
return
if in_wolflist(nick, victim):
pm(cli, nick, messages["wolf_no_target_wolf"])
return
orig.append(victim)
evt = Event("targeted_command", {"target": victim, "misdirection": True, "exchange": True})
evt.dispatch(cli, var, "kill", nick, victim, frozenset({"detrimental"}))
if evt.prevent_default:
return
victim = evt.data["target"]
victims.append(victim)
i += 1
if len(set(victims)) < len(victims):
pm(cli, nick, messages["wolf_must_target_multiple"])
return
KILLS[nick] = victims
if len(orig) > 1:
# need to expand this eventually
msg = messages["wolf_target_multiple"].format(orig[0], orig[1])
pm(cli, nick, messages["player"].format(msg))
debuglog("{0} ({1}) KILL: {2} ({3}) and {4} ({5})".format(nick, role, victims[0], get_role(victims[0]), victims[1], get_role(victims[1])))
else:
msg = messages["wolf_target"].format(orig[0])
pm(cli, nick, messages["player"].format(msg))
if num_kills > 1:
pm(cli, nick, messages["wolf_target_second"])
debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, role, victims[0], get_role(victims[0])))
if in_wolflist(nick, nick):
relay_wolfchat_command(cli, nick, messages["wolfchat"].format(nick, msg), var.WOLF_ROLES, is_wolf_command=True, is_kill_command=True)
chk_nightdone(cli)
@cmd("retract", "r", chan=False, pm=True, playing=True, phases=("night",))
def wolf_retract(cli, nick, chan, rest):
"""Removes a wolf's kill selection."""
if nick not in KILLS:
return
del KILLS[nick]
pm(cli, nick, messages["retracted_kill"])
relay_wolfchat_command(cli, nick, messages["wolfchat_retracted_kill"].format(nick), var.WOLF_ROLES, is_wolf_command=True, is_kill_command=True)
@event_listener("del_player")
def on_del_player(evt, cli, var, nick, nickrole, nicktpls, lynched, end_game, death_triggers, killer_role, deadlist, original, ismain, refresh_pl):
for a,b in list(KILLS.items()):
for n in b:
if n == nick:
KILLS[a].remove(nick)
if a == nick or len(KILLS[a]) == 0:
del KILLS[a]
@event_listener("rename_player")
def on_rename(evt, cli, var, prefix, nick):
kvp = []
for a,b in KILLS.items():
nl = []
for n in b:
if n == prefix:
n = nick
nl.append(n)
if a == prefix:
a = nick
kvp.append((a,nl))
KILLS.update(kvp)
if prefix in KILLS:
del KILLS[prefix]
@event_listener("acted")
def on_acted(evt, cli, var, nick, sender):
if nick in KILLS:
evt.data["acted"] = True
@event_listener("transition_day", priority=1)
def on_transition_day(evt, cli, var):
# figure out wolf target
found = defaultdict(int)
# split off into event + wolfcub.py
num_kills = 1
if var.ANGRY_WOLVES:
num_kills = 2
for v in KILLS.values():
for p in v:
if p:
# kill target starting with ! is invalid
# right now nothing does this, but monster eventually will
if p[0] == "!":
continue
found[p] += 1
for i in range(num_kills):
maxc = 0
dups = []
for v, c in found.items():
if c > maxc:
maxc = c
dups = [v]
elif c == maxc:
dups.append(v)
if maxc and dups:
victim = random.choice(dups)
evt.data["victims"].append(victim)
evt.data["bywolves"].add(victim)
evt.data["onlybywolves"].add(victim)
# special key to let us know to randomly select a wolf in case of retribution totem
evt.data["killers"][victim].append("@wolves")
del found[victim]
# this should be moved to an event in kill, where monster prefixes their nick with !
# and fallen angel subsequently removes the ! prefix
if len(var.ROLES["fallen angel"]) == 0:
for monster in var.ROLES["monster"]:
if monster in victims:
evt.data["victims"].remove(monster)
evt.data["bywolves"].discard(monster)
evt.data["onlybywolves"].discard(monster)
@event_listener("exchange_roles")
def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
if actor in KILLS:
del KILLS[actor]
if nick in KILLS:
del KILLS[nick]
@event_listener("chk_nightdone", priority=3)
def on_chk_nightdone(evt, cli, var):
if not var.DISEASED_WOLVES:
evt.data["actedcount"] += len(KILLS)
# eventually wolf cub will remove itself from nightroles in wolfcub.py
evt.data["nightroles"].extend(list_players(var.WOLF_ROLES - {"wolf cub"}))
@event_listener("chk_nightdone", priority=20)
def on_chk_nightdone2(evt, cli, var):
if not evt.prevent_default and not var.DISEASED_WOLVES:
# flatten KILLS
kills = set()
for ls in KILLS.values():
kills.update(ls)
# check if wolves are actually agreeing
# allow len(kills) == 0 through as that means that crow was dumb and observed instead
if not var.ANGRY_WOLVES and len(kills) > 1:
evt.data["actedcount"] -= 1
elif var.ANGRY_WOLVES and (len(kills) == 1 or len(kills) > 2):
evt.data["actedcount"] -= 1
@event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, cli, var):
ps = list_players()
wolves = list_players(var.WOLFCHAT_ROLES)
for wolf in wolves:
# should make the cursed information an event that cursedvillager can then add to
# (e.g. an event to change what prefixes are sent with the role message, and a
# 2nd event to change information in parens in player list)
normal_notify = wolf in var.PLAYERS and not is_user_simple(wolf)
role = get_role(wolf)
cursed = "cursed " if wolf in var.ROLES["cursed villager"] else ""
if normal_notify:
msg = "{0}_notify".format(role.replace(" ", "_"))
cmsg = "cursed_" + msg
try:
if cursed:
try:
pm(cli, wolf, messages[cmsg])
except KeyError:
pm(cli, wolf, messages[msg].format(cursed))
else:
pm(cli, wolf, messages[msg].format(cursed))
except KeyError:
# catchall in case we forgot something above
an = 'n' if role.startswith(("a", "e", "i", "o", "u")) else ""
pm(cli, wolf, messages["undefined_role_notify"].format(an, role))
if len(wolves) > 1:
pm(cli, wolf, messages["wolfchat_notify"])
else:
an = "n" if cursed == "" and role.startswith(("a", "e", "i", "o", "u")) else ""
pm(cli, wolf, messages["wolf_simple"].format(an, cursed, role)) # !simple
pl = ps[:]
random.shuffle(pl)
pl.remove(wolf) # remove self from list
for i, player in enumerate(pl):
prole = get_role(player)
if prole in var.WOLFCHAT_ROLES:
cursed = ""
if player in var.ROLES["cursed villager"]:
cursed = "cursed "
pl[i] = "\u0002{0}\u0002 ({1}{2})".format(player, cursed, prole)
elif player in var.ROLES["cursed villager"]:
pl[i] = player + " (cursed)"
pm(cli, wolf, "Players: " + ", ".join(pl))
if var.DISEASED_WOLVES:
pm(cli, wolf, messages["ill_wolves"])
# TODO: split the following out into their own files (mystic, cub and alpha)
if role == "wolf mystic":
# if adding this info to !myrole, you will need to save off this count so that they can't get updated info until the next night
# # of special villagers = # of players - # of villagers - # of wolves - # of neutrals
numvills = len(ps) - len(list_players(var.WOLFTEAM_ROLES)) - len(list_players(("villager", "vengeful ghost", "time lord", "amnesiac", "lycan"))) - len(list_players(var.TRUE_NEUTRAL_ROLES))
pm(cli, wolf, messages["wolf_mystic_info"].format("are" if numvills != 1 else "is", numvills, "s" if numvills != 1 else ""))
if not var.DISEASED_WOLVES and var.ANGRY_WOLVES and role in var.WOLF_ROLES and role != "wolf cub":
pm(cli, wolf, messages["angry_wolves"])
if var.ALPHA_ENABLED and role == "alpha wolf" and wolf not in var.ALPHA_WOLVES:
pm(cli, wolf, messages["wolf_bite"])
@event_listener("begin_day")
def on_begin_day(evt, cli, var):
KILLS.clear()
@event_listener("reset")
def on_reset(evt, var):
KILLS.clear()
# vim: set sw=4 expandtab:

View File

@ -283,6 +283,10 @@ SEEN_WOLF = WOLF_ROLES | {"monster", "mad scientist", "succubus"}
# These are seen as the default role (or villager) when seen by seer (this overrides SEEN_WOLF) # These are seen as the default role (or villager) when seen by seer (this overrides SEEN_WOLF)
SEEN_DEFAULT = frozenset({"traitor", "hag", "sorcerer", "time lord", "villager", "cultist", "minion", "turncoat", "amnesiac", SEEN_DEFAULT = frozenset({"traitor", "hag", "sorcerer", "time lord", "villager", "cultist", "minion", "turncoat", "amnesiac",
"vengeful ghost", "lycan", "clone", "fool", "jester", "werekitten", "warlock", "piper", "demoniac"}) "vengeful ghost", "lycan", "clone", "fool", "jester", "werekitten", "warlock", "piper", "demoniac"})
# These roles are notified that they are villager
HIDDEN_VILLAGERS = frozenset({"time lord"})
# These roles are notified that they are the default role. They also win alongside the default role barring other role-specific win conds.
HIDDEN_ROLES = frozenset({"vengeful ghost", "amnesiac"})
# these totems are beneficial for the *receiving* person, but can be detrimental to someone else acting on the receiver! # these totems are beneficial for the *receiving* person, but can be detrimental to someone else acting on the receiver!
BENEFICIAL_TOTEMS = frozenset({"protection", "revealing", "desperation", "influence", "luck", "pestilence", "retribution"}) BENEFICIAL_TOTEMS = frozenset({"protection", "revealing", "desperation", "influence", "luck", "pestilence", "retribution"})

View File

@ -2486,6 +2486,19 @@ def stop_game(cli, winner="", abort=False, additional_winners=None, log=True):
won = False won = False
iwon = False iwon = False
survived = list_players()
if not pentry["dced"]:
evt = Event("player_win", {"won": won, "iwon": iwon, "special": pentry["special"]})
evt.dispatch(cli, var, splr, rol, winner, splr in survived)
won = evt.data["won"]
iwon = evt.data["iwon"]
# ensure that it is a) a list, and b) a copy (so it can't be mutated out from under us later)
pentry["special"] = list(evt.data["special"])
# special-case everyone for after the event
if winner == "everyone":
iwon = True
# determine if this player's team won # determine if this player's team won
if rol in var.TRUE_NEUTRAL_ROLES: if rol in var.TRUE_NEUTRAL_ROLES:
# most true neutral roles never have a team win, only individual wins. Exceptions to that are here # most true neutral roles never have a team win, only individual wins. Exceptions to that are here
@ -2499,19 +2512,8 @@ def stop_game(cli, winner="", abort=False, additional_winners=None, log=True):
elif winner != "succubi" and splr in var.ENTRANCED: elif winner != "succubi" and splr in var.ENTRANCED:
# entranced players can't win with villager or wolf teams # entranced players can't win with villager or wolf teams
won = False won = False
elif rol in ("amnesiac", "vengeful ghost") and splr not in var.VENGEFUL_GHOSTS:
if var.DEFAULT_ROLE == "villager" and winner == "villagers":
won = True
elif var.DEFAULT_ROLE == "cultist" and winner == "wolves":
won = True
elif rol in var.WOLFTEAM_ROLES:
if winner == "wolves":
won = True
elif winner == "villagers":
won = True
survived = list_players() if pentry["dced"]:
if plr.startswith("(dced)"):
# You get NOTHING! You LOSE! Good DAY, sir! # You get NOTHING! You LOSE! Good DAY, sir!
won = False won = False
iwon = False iwon = False
@ -2581,8 +2583,6 @@ def stop_game(cli, winner="", abort=False, additional_winners=None, log=True):
iwon = True iwon = True
elif winner == "succubi" and splr in var.ENTRANCED | var.ROLES["succubus"]: elif winner == "succubi" and splr in var.ENTRANCED | var.ROLES["succubus"]:
iwon = True iwon = True
elif winner == "everyone" and not survived:
iwon = True # nobody survived but an event made everyone win
elif not iwon: elif not iwon:
iwon = won and splr in survived # survived, team won = individual win iwon = won and splr in survived # survived, team won = individual win
@ -3171,12 +3171,6 @@ def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death
if var.PHASE in var.GAME_PHASES: if var.PHASE in var.GAME_PHASES:
# remove the player from variables if they're in there # remove the player from variables if they're in there
if ret: if ret:
for a,b in list(var.KILLS.items()):
for n in b: #var.KILLS can have 2 kills in a list
if n == nick:
var.KILLS[a].remove(nick)
if a == nick or len(var.KILLS[a]) == 0:
del var.KILLS[a]
for x in (var.OBSERVED, var.HVISITED, var.GUARDED, var.TARGETED, var.LASTGUARDED, var.LASTGIVEN, var.LASTHEXED): for x in (var.OBSERVED, var.HVISITED, var.GUARDED, var.TARGETED, var.LASTGUARDED, var.LASTGIVEN, var.LASTHEXED):
for k in list(x): for k in list(x):
if nick in (k, x[k]): if nick in (k, x[k]):
@ -3187,7 +3181,6 @@ def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death
del x[k] del x[k]
for k in list(var.OTHER_KILLS): for k in list(var.OTHER_KILLS):
if var.OTHER_KILLS[k] == nick: if var.OTHER_KILLS[k] == nick:
var.HUNTERS.discard(k)
pm(cli, k, messages["hunter_discard"]) pm(cli, k, messages["hunter_discard"])
del var.OTHER_KILLS[k] del var.OTHER_KILLS[k]
elif nick == k: elif nick == k:
@ -3203,7 +3196,7 @@ def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death
# remove players from night variables # remove players from night variables
# the dicts are handled above, these are the lists of who has acted which is used to determine whether night should end # the dicts are handled above, these are the lists of who has acted which is used to determine whether night should end
# if these aren't cleared properly night may end prematurely # if these aren't cleared properly night may end prematurely
for x in (var.SEEN, var.PASSED, var.HUNTERS, var.HEXED, var.MATCHMAKERS, var.CURSED, var.CHARMERS): for x in (var.SEEN, var.PASSED, var.HEXED, var.MATCHMAKERS, var.CURSED, var.CHARMERS):
x.discard(nick) x.discard(nick)
if var.PHASE == "day" and not forced_death and ret: # didn't die from lynching if var.PHASE == "day" and not forced_death and ret: # didn't die from lynching
var.VOTES.pop(nick, None) # Delete other people's votes on the player var.VOTES.pop(nick, None) # Delete other people's votes on the player
@ -3558,21 +3551,6 @@ def rename_player(cli, prefix, nick):
var.DOCTORS, var.BITTEN_ROLES, var.LYCAN_ROLES, var.AMNESIAC_ROLES): var.DOCTORS, var.BITTEN_ROLES, var.LYCAN_ROLES, var.AMNESIAC_ROLES):
if prefix in dictvar.keys(): if prefix in dictvar.keys():
dictvar[nick] = dictvar.pop(prefix) dictvar[nick] = dictvar.pop(prefix)
# Looks like {'jacob2': ['5'], '7': ['3']}
for dictvar in (var.KILLS,):
kvp = []
for a,b in dictvar.items():
nl = []
for n in b:
if n == prefix:
n = nick
nl.append(n)
if a == prefix:
a = nick
kvp.append((a,nl))
dictvar.update(kvp)
if prefix in dictvar.keys():
del dictvar[prefix]
# Looks like {'6': {'jacob3'}, 'jacob3': {'6'}} # Looks like {'6': {'jacob3'}, 'jacob3': {'6'}}
for dictvar in (var.LOVERS, var.ORIGINAL_LOVERS, var.DULLAHAN_TARGETS): for dictvar in (var.LOVERS, var.ORIGINAL_LOVERS, var.DULLAHAN_TARGETS):
kvp = [] kvp = []
@ -3596,7 +3574,7 @@ def rename_player(cli, prefix, nick):
b = nick b = nick
var.EXCHANGED_ROLES[idx] = (a, b) var.EXCHANGED_ROLES[idx] = (a, b)
for setvar in (var.SEEN, var.HEXED, var.ASLEEP, var.DESPERATE, var.REVEALED, var.SILENCED, var.TOBESILENCED, for setvar in (var.SEEN, var.HEXED, var.ASLEEP, var.DESPERATE, var.REVEALED, var.SILENCED, var.TOBESILENCED,
var.REVEALED_MAYORS, var.MATCHMAKERS, var.HUNTERS, var.PASSED, var.JESTERS, var.AMNESIACS, var.REVEALED_MAYORS, var.MATCHMAKERS, var.PASSED, var.JESTERS, var.AMNESIACS,
var.INFLUENTIAL, var.LYCANTHROPES, var.TOBELYCANTHROPES, var.LUCKY, var.TOBELUCKY, var.SICK, var.INFLUENTIAL, var.LYCANTHROPES, var.TOBELYCANTHROPES, var.LUCKY, var.TOBELUCKY, var.SICK,
var.DISEASED, var.TOBEDISEASED, var.RETRIBUTION, var.MISDIRECTED, var.TOBEMISDIRECTED, var.DISEASED, var.TOBEDISEASED, var.RETRIBUTION, var.MISDIRECTED, var.TOBEMISDIRECTED,
var.EXCHANGED, var.IMMUNIZED, var.CURED_LYCANS, var.ALPHA_WOLVES, var.CURSED, var.CHARMERS, var.EXCHANGED, var.IMMUNIZED, var.CURED_LYCANS, var.ALPHA_WOLVES, var.CURSED, var.CHARMERS,
@ -3827,8 +3805,7 @@ def begin_day(cli):
# Reset nighttime variables # Reset nighttime variables
var.GAMEPHASE = "day" var.GAMEPHASE = "day"
var.KILLS = {} # nicknames of kill victims (wolves only) var.OTHER_KILLS = {} # other kill victims (vigilante/vengeful ghost)
var.OTHER_KILLS = {} # other kill victims (hunter/vengeful ghost)
var.KILLER = "" # nickname of who chose the victim var.KILLER = "" # nickname of who chose the victim
var.SEEN = set() # set of doomsayers that have had visions var.SEEN = set() # set of doomsayers that have had visions
var.HEXED = set() # set of hags that have silenced others var.HEXED = set() # set of hags that have silenced others
@ -4083,10 +4060,12 @@ def transition_day(cli, gameid=0):
for crow, target in iter(var.OBSERVED.items()): for crow, target in iter(var.OBSERVED.items()):
if crow not in var.ROLES["werecrow"]: if crow not in var.ROLES["werecrow"]:
continue continue
evt = Event("night_acted", {"acted": False})
evt.dispatch(cli, var, target, crow)
if ((target in var.HVISITED and var.HVISITED[target]) or target in var.SEEN or target in var.SHAMANS or if ((target in var.HVISITED and var.HVISITED[target]) or target in var.SEEN or target in var.SHAMANS or
(target in var.GUARDED and var.GUARDED[target]) or target in var.OTHER_KILLS or (target in var.GUARDED and var.GUARDED[target]) or target in var.OTHER_KILLS or
(target in var.PRAYED and var.PRAYED[target][0] > 0) or target in var.CHARMERS or (target in var.PRAYED and var.PRAYED[target][0] > 0) or target in var.CHARMERS or
target in var.OBSERVED or target in var.KILLS or target in var.HEXED or target in var.CURSED): target in var.OBSERVED or target in var.HEXED or target in var.CURSED or evt.data["acted"]):
pm(cli, crow, messages["werecrow_success"].format(target)) pm(cli, crow, messages["werecrow_success"].format(target))
else: else:
pm(cli, crow, messages["werecrow_failure"].format(target)) pm(cli, crow, messages["werecrow_failure"].format(target))
@ -4102,63 +4081,29 @@ def transition_day(cli, gameid=0):
var.NIGHT_TIMEDELTA += td var.NIGHT_TIMEDELTA += td
min, sec = td.seconds // 60, td.seconds % 60 min, sec = td.seconds // 60, td.seconds % 60
found = defaultdict(int) # built-in logic runs at the following priorities:
for v in var.KILLS.values(): # 1 = wolf kills
for p in v: # 2 = non-wolf kills
found[p] += 1 # 3 = alpha wolf bite
# 4 = protections/fallen angel
maxc = 0 # 5 = rearranging victim list (ensure bodyguard/harlot messages plays),
victims = [] # fixing killers dict to have correct priority (wolf-side VG kills -> non-wolf kills -> wolf kills)
bitten = [] # Actually killing off the victims happens in transition_day_resolve
# dict of victim: list of killers (for retribution totem) evt = Event("transition_day", {
killers = defaultdict(list) "victims": [],
# wolves targeted, others may have as well (needed for harlot visit and maybe other things) "killers": defaultdict(list),
bywolves = set() "bywolves": set(),
# wolves and nobody else targeted (needed for lycan) "onlybywolves": set(),
onlybywolves = set() "protected": {},
"bitten": []
dups = [] })
for v, c in found.items(): evt.dispatch(cli, var)
if c > maxc: victims = evt.data["victims"]
maxc = c killers = evt.data["killers"]
dups = [v] bywolves = evt.data["bywolves"]
elif c == maxc: onlybywolves = evt.data["onlybywolves"]
dups.append(v) protected = evt.data["protected"]
bitten = evt.data["bitten"]
if maxc and dups:
victim = random.choice(dups)
victims.append(victim)
bywolves.add(victim)
onlybywolves.add(victim)
# special key to let us know to randomly select a wolf
killers[victim].append("@wolves")
if victims and var.ANGRY_WOLVES:
# they got a 2nd kill
del found[victims[0]]
maxc = 0
dups = []
for v, c in found.items():
if c > maxc:
maxc = c
dups = [v]
elif c == maxc:
dups.append(v)
if maxc and dups:
victim = random.choice(dups)
victims.append(victim)
bywolves.add(victim)
onlybywolves.add(victim)
# special key to let us know to randomly select a wolf
killers[victim].append("@wolves")
if len(var.ROLES["fallen angel"]) == 0:
for monster in var.ROLES["monster"]:
if monster in victims:
victims.remove(monster)
bywolves.discard(monster)
onlybywolves.discard(monster)
wolfghostvictims = [] wolfghostvictims = []
for k, d in var.OTHER_KILLS.items(): for k, d in var.OTHER_KILLS.items():
@ -4174,7 +4119,6 @@ def transition_day(cli, gameid=0):
for v in var.ENTRANCED_DYING: for v in var.ENTRANCED_DYING:
var.DYING.add(v) var.DYING.add(v)
# clear list so that it doesn't pm hunter / ghost about being able to kill again
var.OTHER_KILLS = {} var.OTHER_KILLS = {}
for k, d in var.DEATH_TOTEM: for k, d in var.DEATH_TOTEM:
@ -4701,16 +4645,14 @@ def chk_nightdone(cli):
# TODO: alphabetize and/or arrange sensibly # TODO: alphabetize and/or arrange sensibly
pl = list_players() pl = list_players()
spl = set(pl) spl = set(pl)
actedcount = sum(map(len, (var.SEEN, var.HVISITED, var.GUARDED, var.KILLS, actedcount = sum(map(len, (var.SEEN, var.HVISITED, var.GUARDED,
var.OTHER_KILLS, var.PASSED, var.OBSERVED, var.OTHER_KILLS, var.PASSED, var.OBSERVED,
var.HEXED, var.SHAMANS, var.CURSED, var.CHARMERS))) var.HEXED, var.SHAMANS, var.CURSED, var.CHARMERS)))
nightroles = get_roles("harlot", "succubus", "bodyguard", nightroles = get_roles("harlot", "succubus", "bodyguard", "guardian angel",
"guardian angel", "wolf", "werecrow", "alpha wolf", "sorcerer", "hag", "shaman", "crazed shaman",
"sorcerer", "hunter", "hag", "shaman", "crazed shaman", "warlock", "piper", "vigilante", "doomsayer",
"werekitten", "warlock", "piper", "wolf mystic", "prophet", "wolf shaman")
"fallen angel", "vigilante", "doomsayer", "doomsayer", # NOT a mistake, doomsayer MUST be listed twice
"prophet", "wolf shaman", "wolf shaman") # wolf shaman also must be listed twice
for ghost, against in var.VENGEFUL_GHOSTS.items(): for ghost, against in var.VENGEFUL_GHOSTS.items():
if not against.startswith("!"): if not against.startswith("!"):
@ -4734,22 +4676,11 @@ def chk_nightdone(cli):
actedcount += len(var.MATCHMAKERS | var.CLONED.keys()) actedcount += len(var.MATCHMAKERS | var.CLONED.keys())
nightroles.extend(get_roles("matchmaker", "clone")) nightroles.extend(get_roles("matchmaker", "clone"))
if var.DISEASED_WOLVES: if var.ALPHA_ENABLED:
nightroles = [p for p in nightroles if p not in list_players(var.WOLF_ROLES - {"wolf cub", "werecrow", "doomsayer"})]
# only remove 1 doomsayer instance to indicate they cannot kill but can still see
for p in var.ROLES["doomsayer"]:
nightroles.remove(p)
elif var.ALPHA_ENABLED:
# alphas both kill and bite if they're activated at night, so add them into the counts # alphas both kill and bite if they're activated at night, so add them into the counts
nightroles.extend(get_roles("alpha wolf")) nightroles.extend(get_roles("alpha wolf"))
actedcount += len([p for p in var.ALPHA_WOLVES if p in var.ROLES["alpha wolf"]]) actedcount += len([p for p in var.ALPHA_WOLVES if p in var.ROLES["alpha wolf"]])
for p in var.HUNTERS:
# only remove one instance of their name if they have used hunter ability, in case they have templates
# the OTHER_KILLS check ensures we only remove them if they acted in a *previous* night
if p in var.ROLES["hunter"] and p not in var.OTHER_KILLS:
nightroles.remove(p)
# but remove all instances of their name if they are silenced # but remove all instances of their name if they are silenced
nightroles = [p for p in nightroles if p not in var.SILENCED] nightroles = [p for p in nightroles if p not in var.SILENCED]
@ -4777,21 +4708,6 @@ def chk_nightdone(cli):
for ass in var.ROLES["assassin"]: for ass in var.ROLES["assassin"]:
if ass not in var.TARGETED.keys() | var.SILENCED: if ass not in var.TARGETED.keys() | var.SILENCED:
return return
if not var.DISEASED_WOLVES:
# flatten var.KILLS
kills = set()
for ls in var.KILLS.values():
if not isinstance(ls, str):
kills.update(ls)
else:
kills.add(ls)
# check if wolves are actually agreeing
# allow len(kills) == 0 through as that means that crow was dumb and observed instead
# of killingor alpha wolf was alone and chose to bite instead of kill
if not var.ANGRY_WOLVES and len(kills) > 1:
return
elif var.ANGRY_WOLVES and (len(kills) == 1 or len(kills) > 2):
return
for x, t in var.TIMERS.items(): for x, t in var.TIMERS.items():
t[0].cancel() t[0].cancel()
@ -4980,13 +4896,6 @@ def check_exchange(cli, actor, nick):
del var.SHAMANS[actor] del var.SHAMANS[actor]
if actor in var.LASTGIVEN: if actor in var.LASTGIVEN:
del var.LASTGIVEN[actor] del var.LASTGIVEN[actor]
elif actor_role in var.WOLF_ROLES - {"werecrow", "wolf cub", "alpha wolf", "doomsayer"}:
if actor in var.KILLS:
del var.KILLS[actor]
elif actor_role == "hunter":
if actor in var.OTHER_KILLS:
del var.OTHER_KILLS[actor]
var.HUNTERS.discard(actor)
elif actor_role in ("bodyguard", "guardian angel"): elif actor_role in ("bodyguard", "guardian angel"):
if actor in var.GUARDED: if actor in var.GUARDED:
pm(cli, var.GUARDED.pop(actor), messages["protector_disappeared"]) pm(cli, var.GUARDED.pop(actor), messages["protector_disappeared"])
@ -4995,8 +4904,6 @@ def check_exchange(cli, actor, nick):
elif actor_role in ("werecrow", "sorcerer"): elif actor_role in ("werecrow", "sorcerer"):
if actor in var.OBSERVED: if actor in var.OBSERVED:
del var.OBSERVED[actor] del var.OBSERVED[actor]
if actor in var.KILLS:
del var.KILLS[actor]
elif actor_role == "harlot": elif actor_role == "harlot":
if actor in var.HVISITED: if actor in var.HVISITED:
if var.HVISITED[actor] is not None: if var.HVISITED[actor] is not None:
@ -5015,12 +4922,8 @@ def check_exchange(cli, actor, nick):
var.DOCTORS[nick] = var.DOCTORS.pop(actor) var.DOCTORS[nick] = var.DOCTORS.pop(actor)
elif actor_role == "alpha wolf": elif actor_role == "alpha wolf":
var.ALPHA_WOLVES.discard(actor) var.ALPHA_WOLVES.discard(actor)
if actor in var.KILLS:
del var.KILLS[actor]
elif actor_role == "doomsayer": elif actor_role == "doomsayer":
var.SEEN.discard(actor) var.SEEN.discard(actor)
if actor in var.KILLS:
del var.KILLS[actor]
elif actor_role == "warlock": elif actor_role == "warlock":
var.CURSED.discard(actor) var.CURSED.discard(actor)
elif actor_role == "turncoat": elif actor_role == "turncoat":
@ -5046,13 +4949,6 @@ def check_exchange(cli, actor, nick):
del var.SHAMANS[nick] del var.SHAMANS[nick]
if nick in var.LASTGIVEN: if nick in var.LASTGIVEN:
del var.LASTGIVEN[nick] del var.LASTGIVEN[nick]
elif nick_role in var.WOLF_ROLES - {"werecrow", "wolf cub", "alpha wolf", "doomsayer"}:
if nick in var.KILLS:
del var.KILLS[nick]
elif nick_role == "hunter":
if nick in var.OTHER_KILLS:
del var.OTHER_KILLS[nick]
var.HUNTERS.discard(nick)
elif nick_role in ("bodyguard", "guardian angel"): elif nick_role in ("bodyguard", "guardian angel"):
if nick in var.GUARDED: if nick in var.GUARDED:
pm(cli, var.GUARDED.pop(nick), messages["protector_disappeared"]) pm(cli, var.GUARDED.pop(nick), messages["protector_disappeared"])
@ -5061,8 +4957,6 @@ def check_exchange(cli, actor, nick):
elif nick_role in ("werecrow", "sorcerer"): elif nick_role in ("werecrow", "sorcerer"):
if nick in var.OBSERVED: if nick in var.OBSERVED:
del var.OBSERVED[nick] del var.OBSERVED[nick]
if nick in var.KILLS:
del var.KILLS[nick]
elif nick_role == "harlot": elif nick_role == "harlot":
if nick in var.HVISITED: if nick in var.HVISITED:
if var.HVISITED[nick] is not None: if var.HVISITED[nick] is not None:
@ -5080,12 +4974,8 @@ def check_exchange(cli, actor, nick):
var.DOCTORS[actor] = var.DOCTORS.pop(nick) var.DOCTORS[actor] = var.DOCTORS.pop(nick)
elif nick_role == "alpha wolf": elif nick_role == "alpha wolf":
var.ALPHA_WOLVES.discard(nick) var.ALPHA_WOLVES.discard(nick)
if nick in var.KILLS:
del var.KILLS[nick]
elif nick_role == "doomsayer": elif nick_role == "doomsayer":
var.SEEN.discard(nick) var.SEEN.discard(nick)
if nick in var.KILLS:
del var.KILLS[nick]
elif nick_role == "warlock": elif nick_role == "warlock":
var.CURSED.discard(nick) var.CURSED.discard(nick)
elif nick_role == "turncoat": elif nick_role == "turncoat":
@ -5121,15 +5011,15 @@ def check_exchange(cli, actor, nick):
del var.LYCAN_ROLES[nick] del var.LYCAN_ROLES[nick]
actor_rev_role = actor_role actor_rev_role = actor_role
if actor_role == "vengeful ghost": if actor_role in var.HIDDEN_ROLES:
actor_rev_role = var.DEFAULT_ROLE actor_rev_role = var.DEFAULT_ROLE
elif actor_role == "time lord": elif actor_role in var.HIDDEN_VILLAGERS:
actor_rev_role = "villager" actor_rev_role = "villager"
nick_rev_role = nick_role nick_rev_role = nick_role
if nick_role == "vengeful ghost": if nick_role in var.HIDDEN_ROLES:
nick_rev_role = var.DEFAULT_ROLE nick_rev_role = var.DEFAULT_ROLE
elif actor_role == "time lord": elif actor_role in var.HIDDEN_VILLAGERS:
nick_rev_role = "villager" nick_rev_role = "villager"
# don't say who, since misdirection/luck totem may have switched it # don't say who, since misdirection/luck totem may have switched it
@ -5264,7 +5154,7 @@ def retract(cli, nick, chan, rest):
if chan == nick: # PM, use different code if chan == nick: # PM, use different code
role = get_role(nick) role = get_role(nick)
if role not in var.WOLF_ROLES - {"wolf cub"} and role != "hunter" and nick not in var.VENGEFUL_GHOSTS.keys(): if role not in var.WOLF_ROLES - {"wolf cub"} and nick not in var.VENGEFUL_GHOSTS.keys():
return return
if var.PHASE != "night": if var.PHASE != "night":
return return
@ -5272,17 +5162,9 @@ def retract(cli, nick, chan, rest):
if var.OBSERVED.get(nick): if var.OBSERVED.get(nick):
pm(cli, nick, (messages["werecrow_transformed"])) pm(cli, nick, (messages["werecrow_transformed"]))
return return
elif role == "hunter" and nick in var.HUNTERS and nick not in var.OTHER_KILLS.keys():
return
if role in var.WOLF_ROLES and nick in var.KILLS.keys(): if role not in var.WOLF_ROLES and nick in var.OTHER_KILLS.keys():
del var.KILLS[nick]
pm(cli, nick, messages["retracted_kill"])
relay_wolfchat_command(cli, nick, messages["wolfchat_retracted_kill"].format(nick), var.WOLF_ROLES - {"wolf cub"}, is_wolf_command=True, is_kill_command=True)
elif role not in var.WOLF_ROLES and nick in var.OTHER_KILLS.keys():
del var.OTHER_KILLS[nick] del var.OTHER_KILLS[nick]
if role == "hunter":
var.HUNTERS.remove(nick)
pm(cli, nick, messages["retracted_kill"]) pm(cli, nick, messages["retracted_kill"])
elif role == "alpha wolf" and nick in var.BITE_PREFERENCES.keys(): elif role == "alpha wolf" and nick in var.BITE_PREFERENCES.keys():
del var.BITE_PREFERENCES[nick] del var.BITE_PREFERENCES[nick]
@ -5291,8 +5173,6 @@ def retract(cli, nick, chan, rest):
relay_wolfchat_command(cli, nick, messages["wolfchat_no_bite"].format(nick), ("alpha wolf",), is_wolf_command=True) relay_wolfchat_command(cli, nick, messages["wolfchat_no_bite"].format(nick), ("alpha wolf",), is_wolf_command=True)
elif role == "alpha wolf" and var.ALPHA_ENABLED: elif role == "alpha wolf" and var.ALPHA_ENABLED:
pm(cli, nick, messages["kill_bite_pending"]) pm(cli, nick, messages["kill_bite_pending"])
else:
pm(cli, nick, messages["kill_pending"])
return return
if var.PHASE != "day": if var.PHASE != "day":
@ -5413,10 +5293,7 @@ def kill(cli, nick, chan, rest):
role = get_role(nick) role = get_role(nick)
except KeyError: except KeyError:
role = None role = None
wolfroles = var.WOLF_ROLES - {"wolf cub"} if role not in {"dullahan", "vigilante"} and nick not in var.VENGEFUL_GHOSTS.keys():
if role in var.WOLFCHAT_ROLES and role not in wolfroles:
return # they do this a lot.
if role not in wolfroles | {"hunter", "dullahan", "vigilante"} and nick not in var.VENGEFUL_GHOSTS.keys():
return return
if nick in var.VENGEFUL_GHOSTS.keys() and var.VENGEFUL_GHOSTS[nick][0] == "!": if nick in var.VENGEFUL_GHOSTS.keys() and var.VENGEFUL_GHOSTS[nick][0] == "!":
# ghost was driven away by retribution # ghost was driven away by retribution
@ -5425,31 +5302,11 @@ def kill(cli, nick, chan, rest):
# all their targets are dead # all their targets are dead
pm(cli, nick, messages["dullahan_targets_dead"]) pm(cli, nick, messages["dullahan_targets_dead"])
return return
if role == "hunter" and nick in var.HUNTERS and nick not in var.OTHER_KILLS:
# they are a hunter and did not kill this night (if they killed this night, this allows them to switch)
pm(cli, nick, messages["hunter_already_killed"])
return
if nick in var.SILENCED: if nick in var.SILENCED:
pm(cli, nick, messages["silenced"]) pm(cli, nick, messages["silenced"])
return return
if role in wolfroles and var.DISEASED_WOLVES:
pm(cli, nick, messages["ill_wolves"])
return
pieces = re.split(" +",rest) pieces = re.split(" +",rest)
victim = pieces[0] victim = pieces[0]
victim2 = None
if role in wolfroles and var.ANGRY_WOLVES:
if len(pieces) > 1:
if len(pieces) > 2 and pieces[1].lower() == "and":
victim2 = pieces[2]
else:
victim2 = pieces[1]
else:
victim2 = None
if role == "werecrow": # Check if flying to observe
if var.OBSERVED.get(nick):
pm(cli, nick, (messages["werecrow_transformed_nokill"]))
return
victim = get_victim(cli, nick, victim, False) victim = get_victim(cli, nick, victim, False)
if not victim: if not victim:
@ -5457,15 +5314,8 @@ def kill(cli, nick, chan, rest):
if is_safe(nick, victim): if is_safe(nick, victim):
pm(cli, nick, messages["no_acting_on_succubus"].format("kill")) pm(cli, nick, messages["no_acting_on_succubus"].format("kill"))
return return
if victim2 != None:
victim2 = get_victim(cli, nick, victim2, False)
if not victim2:
return
if is_safe(nick, victim2):
pm(cli, nick, messages["no_acting_on_succubus"].format("kill"))
return
if victim == nick or victim2 == nick: if victim == nick:
if nick in var.VENGEFUL_GHOSTS.keys(): if nick in var.VENGEFUL_GHOSTS.keys():
pm(cli, nick, messages["player_dead"]) pm(cli, nick, messages["player_dead"])
else: else:
@ -5481,53 +5331,16 @@ def kill(cli, nick, chan, rest):
pm(cli, nick, messages["vengeful_ghost_villager"]) pm(cli, nick, messages["vengeful_ghost_villager"])
return return
if role in wolfroles: rv = choose_target(nick, victim)
if in_wolflist(nick, victim) or (victim2 is not None and in_wolflist(nick, victim2)): if nick not in var.VENGEFUL_GHOSTS.keys():
pm(cli, nick, messages["wolf_no_target_wolf"]) if check_exchange(cli, nick, rv):
return return
if var.ANGRY_WOLVES and victim2 is not None: var.OTHER_KILLS[nick] = rv
if victim == victim2:
pm(cli, nick, messages["wolf_must_target_multiple"])
return
else:
rv = choose_target(nick, victim)
rv2 = choose_target(nick, victim2)
if check_exchange(cli, nick, rv):
return
if check_exchange(cli, nick, rv2):
return
var.KILLS[nick] = [rv, rv2]
else:
rv = choose_target(nick, victim)
if check_exchange(cli, nick, rv):
return
var.KILLS[nick] = [rv]
else:
rv = choose_target(nick, victim)
if nick not in var.VENGEFUL_GHOSTS.keys():
if check_exchange(cli, nick, rv):
return
var.OTHER_KILLS[nick] = rv
if role == "hunter":
var.HUNTERS.add(nick)
var.PASSED.discard(nick)
if victim2 != None: msg = messages["wolf_target"].format(victim)
msg = messages["wolf_target_multiple"].format(victim, victim2) pm(cli, nick, messages["player"].format(msg))
pm(cli, nick, messages["player"].format(msg))
else:
msg = messages["wolf_target"].format(victim)
pm(cli, nick, messages["player"].format(msg))
if var.ANGRY_WOLVES and role in wolfroles:
pm(cli, nick, messages["wolf_target_second"])
if in_wolflist(nick, nick): debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, role, victim, get_role(victim)))
relay_wolfchat_command(cli, nick, messages["wolfchat"].format(nick, msg), var.WOLF_ROLES - {"wolf cub"}, is_wolf_command=True, is_kill_command=True)
if victim2:
debuglog("{0} ({1}) KILL: {2} and {3} ({4})".format(nick, role, victim, victim2, get_role(victim2)))
else:
debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, role, victim, get_role(victim)))
chk_nightdone(cli) chk_nightdone(cli)
@ -5652,8 +5465,10 @@ def observe(cli, nick, chan, rest):
if check_exchange(cli, nick, victim): if check_exchange(cli, nick, victim):
return return
var.OBSERVED[nick] = victim var.OBSERVED[nick] = victim
if nick in var.KILLS.keys(): # temp hack, will do something better once crow is split off
del var.KILLS[nick] from src.roles import wolf
if nick in wolf.KILLS:
del wolf.KILLS[nick]
if role == "werecrow": if role == "werecrow":
pm(cli, nick, messages["werecrow_observe_success"].format(victim)) pm(cli, nick, messages["werecrow_observe_success"].format(victim))
relay_wolfchat_command(cli, nick, messages["wolfchat_observe"].format(nick, victim), ("werecrow",), is_wolf_command=True) relay_wolfchat_command(cli, nick, messages["wolfchat_observe"].format(nick, victim), ("werecrow",), is_wolf_command=True)
@ -5845,7 +5660,6 @@ def hvisit(cli, nick, chan, rest):
if var.OTHER_KILLS.get(victim) in var.ROLES["succubus"]: if var.OTHER_KILLS.get(victim) in var.ROLES["succubus"]:
pm(cli, victim, messages["no_kill_succubus"].format(var.OTHER_KILLS[victim])) pm(cli, victim, messages["no_kill_succubus"].format(var.OTHER_KILLS[victim]))
del var.OTHER_KILLS[victim] del var.OTHER_KILLS[victim]
var.HUNTERS.discard(victim)
if var.TARGETED.get(victim) in var.ROLES["succubus"]: if var.TARGETED.get(victim) in var.ROLES["succubus"]:
msg = messages["no_target_succubus"].format(var.TARGETED[victim]) msg = messages["no_target_succubus"].format(var.TARGETED[victim])
del var.TARGETED[victim] del var.TARGETED[victim]
@ -5863,13 +5677,19 @@ def hvisit(cli, nick, chan, rest):
var.TOBESILENCED.remove(nick) var.TOBESILENCED.remove(nick)
var.HEXED.remove(victim) var.HEXED.remove(victim)
del var.LASTHEXED[victim] del var.LASTHEXED[victim]
if set(var.KILLS.get(victim, ())) & var.ROLES["succubus"]: # temp hack, will do something better once succubus is split off
from src.roles import wolf, hunter
if set(wolf.KILLS.get(victim, ())) & var.ROLES["succubus"]:
for s in var.ROLES["succubus"]: for s in var.ROLES["succubus"]:
if s in var.KILLS[victim]: if s in wolf.KILLS[victim]:
pm(cli, victim, messages["no_kill_succubus"].format(nick)) pm(cli, victim, messages["no_kill_succubus"].format(nick))
var.KILLS[victim].remove(s) wolf.KILLS[victim].remove(s)
if not var.KILLS[victim]: if not wolf.KILLS[victim]:
del var.KILLS[victim] del wolf.KILLS[victim]
if hunter.KILLS.get(victim) in var.ROLES["succubus"]:
pm(cli, victim, messages["no_kill_succubus"].format(hunter.KILLS[victim]))
del hunter.KILLS[victim]
hunter.HUNTERS.discard(victim)
if var.BITE_PREFERENCES.get(victim) in var.ROLES["succubus"]: if var.BITE_PREFERENCES.get(victim) in var.ROLES["succubus"]:
pm(cli, victim, messages["no_kill_succubus"].format(var.BITE_PREFERENCES[victim])) pm(cli, victim, messages["no_kill_succubus"].format(var.BITE_PREFERENCES[victim]))
del var.BITE_PREFERENCES[victim] del var.BITE_PREFERENCES[victim]
@ -6027,7 +5847,7 @@ def bite_cmd(cli, nick, chan, rest):
chk_nightdone(cli) chk_nightdone(cli)
@cmd("pass", chan=False, pm=True, playing=True, phases=("night",), @cmd("pass", chan=False, pm=True, playing=True, phases=("night",),
roles=("hunter", "harlot", "bodyguard", "guardian angel", "turncoat", "warlock", "piper", "succubus", "vigilante")) roles=("harlot", "bodyguard", "guardian angel", "turncoat", "warlock", "piper", "succubus", "vigilante"))
def pass_cmd(cli, nick, chan, rest): def pass_cmd(cli, nick, chan, rest):
"""Decline to use your special power for that night.""" """Decline to use your special power for that night."""
nickrole = get_role(nick) nickrole = get_role(nick)
@ -6040,17 +5860,7 @@ def pass_cmd(cli, nick, chan, rest):
cli.notice(nick, messages["silenced"]) cli.notice(nick, messages["silenced"])
return return
if nickrole == "hunter": if nickrole == "harlot":
if nick in var.OTHER_KILLS.keys():
del var.OTHER_KILLS[nick]
var.HUNTERS.remove(nick)
if nick in var.HUNTERS:
pm(cli, nick, messages["hunter_already_killed"])
return
pm(cli, nick, messages["hunter_pass"])
var.PASSED.add(nick)
elif nickrole == "harlot":
if var.HVISITED.get(nick): if var.HVISITED.get(nick):
pm(cli, nick, (messages["harlot_already_visited"]).format(var.HVISITED[nick])) pm(cli, nick, (messages["harlot_already_visited"]).format(var.HVISITED[nick]))
return return
@ -6563,14 +6373,13 @@ def transition_night(cli):
var.TIMERS = {} var.TIMERS = {}
# Reset nighttime variables # Reset nighttime variables
var.KILLS = {}
var.GUARDED = {} # key = by whom, value = the person that is visited var.GUARDED = {} # key = by whom, value = the person that is visited
var.KILLER = "" # nickname of who chose the victim var.KILLER = "" # nickname of who chose the victim
var.SEEN = set() # set of seers that have had visions var.SEEN = set() # set of seers that have had visions
var.HEXED = set() # set of hags that have hexed var.HEXED = set() # set of hags that have hexed
var.CURSED = set() # set of warlocks that have cursed var.CURSED = set() # set of warlocks that have cursed
var.SHAMANS = {} var.SHAMANS = {}
var.PASSED = set() # set of hunters that have chosen not to kill var.PASSED = set()
var.OBSERVED = {} # those whom werecrows have observed var.OBSERVED = {} # those whom werecrows have observed
var.CHARMERS = set() # pipers who have charmed var.CHARMERS = set() # pipers who have charmed
var.HVISITED = {} var.HVISITED = {}
@ -6638,9 +6447,9 @@ def transition_night(cli):
if var.FIRST_NIGHT: # we don't need to tell them twice if they remember right away if var.FIRST_NIGHT: # we don't need to tell them twice if they remember right away
continue continue
showrole = amnrole showrole = amnrole
if showrole == "time lord": if showrole in var.HIDDEN_VILLAGERS:
showrole = "villager" showrole = "villager"
elif showrole == "vengeful ghost": elif showrole in var.HIDDEN_ROLES:
showrole = var.DEFAULT_ROLE showrole = var.DEFAULT_ROLE
n = "" n = ""
if showrole.startswith(("a", "e", "i", "o", "u")): if showrole.startswith(("a", "e", "i", "o", "u")):
@ -6665,78 +6474,6 @@ def transition_night(cli):
# send PMs # send PMs
ps = list_players() ps = list_players()
wolves = list_players(var.WOLFCHAT_ROLES)
for wolf in wolves:
normal_notify = wolf in var.PLAYERS and not is_user_simple(wolf)
role = get_role(wolf)
cursed = "cursed " if wolf in var.ROLES["cursed villager"] else ""
if normal_notify:
if role == "wolf":
pm(cli, wolf, messages["wolf_notify"])
elif role == "traitor":
if cursed:
pm(cli, wolf, messages["cursed_traitor_notify"])
else:
pm(cli, wolf, messages["traitor_notify"])
elif role == "werecrow":
pm(cli, wolf, messages["werecrow_notify"])
elif role == "hag":
pm(cli, wolf, messages["hag_notify"].format(cursed))
elif role == "sorcerer":
pm(cli, wolf, messages["sorcerer_notify"].format(cursed))
elif role == "wolf cub":
pm(cli, wolf, messages["wolf_cub_notify"])
elif role == "alpha wolf":
pm(cli, wolf, messages["alpha_wolf_notify"])
elif role == "werekitten":
pm(cli, wolf, messages["werekitten_notify"])
elif role == "warlock":
pm(cli, wolf, messages["warlock_notify"].format(cursed))
elif role == "wolf mystic":
pm(cli, wolf, messages["wolf_mystic_notify"])
elif role == "wolf shaman":
pm(cli, wolf, messages["wolf_shaman_notify"])
elif role == "fallen angel":
pm(cli, wolf, messages["fallen_angel_notify"])
elif role == "doomsayer":
pm(cli, wolf, messages["doomsayer_notify"])
else:
# catchall in case we forgot something above
an = 'n' if role.startswith(("a", "e", "i", "o", "u")) else ""
pm(cli, wolf, messages["undefined_role_notify"].format(an, role))
if len(wolves) > 1:
pm(cli, wolf, messages["wolfchat_notify"])
else:
an = "n" if cursed == "" and role.startswith(("a", "e", "i", "o", "u")) else ""
pm(cli, wolf, messages["wolf_simple"].format(an, cursed, role)) # !simple
pl = ps[:]
random.shuffle(pl)
pl.remove(wolf) # remove self from list
for i, player in enumerate(pl):
prole = get_role(player)
if prole in var.WOLFCHAT_ROLES:
cursed = ""
if player in var.ROLES["cursed villager"]:
cursed = "cursed "
pl[i] = "\u0002{0}\u0002 ({1}{2})".format(player, cursed, prole)
elif player in var.ROLES["cursed villager"]:
pl[i] = player + " (cursed)"
pm(cli, wolf, "Players: " + ", ".join(pl))
if role == "wolf mystic":
# if adding this info to !myrole, you will need to save off this count so that they can't get updated info until the next night
# # of special villagers = # of players - # of villagers - # of wolves - # of neutrals
numvills = len(ps) - len(list_players(var.WOLFTEAM_ROLES)) - len(list_players(("villager", "vengeful ghost", "time lord", "amnesiac", "lycan"))) - len(list_players(var.TRUE_NEUTRAL_ROLES))
pm(cli, wolf, messages["wolf_mystic_info"].format("are" if numvills != 1 else "is", numvills, "s" if numvills != 1 else ""))
if var.DISEASED_WOLVES:
pm(cli, wolf, messages["ill_wolves"])
elif var.ANGRY_WOLVES and role in var.WOLF_ROLES and role != "wolf cub":
pm(cli, wolf, messages["angry_wolves"])
if var.ALPHA_ENABLED and role == "alpha wolf" and wolf not in var.ALPHA_WOLVES:
pm(cli, wolf, messages["wolf_bite"])
for harlot in var.ROLES["harlot"]: for harlot in var.ROLES["harlot"]:
pl = ps[:] pl = ps[:]
@ -6896,18 +6633,6 @@ def transition_night(cli):
if role not in var.WOLFCHAT_ROLES: if role not in var.WOLFCHAT_ROLES:
pm(cli, shaman, "Players: " + ", ".join(pl)) pm(cli, shaman, "Players: " + ", ".join(pl))
for hunter in var.ROLES["hunter"]:
if hunter in var.HUNTERS:
continue #already killed
pl = ps[:]
random.shuffle(pl)
pl.remove(hunter)
if hunter in var.PLAYERS and not is_user_simple(hunter):
pm(cli, hunter, messages["hunter_notify"])
else:
pm(cli, hunter, messages["hunter_simple"])
pm(cli, hunter, "Players: " + ", ".join(pl))
for dullahan in var.ROLES["dullahan"]: for dullahan in var.ROLES["dullahan"]:
targets = list(var.DULLAHAN_TARGETS[dullahan]) targets = list(var.DULLAHAN_TARGETS[dullahan])
for target in var.DEAD: for target in var.DEAD:
@ -7118,25 +6843,6 @@ def transition_night(cli):
pm(cli, minion, messages["minion_simple"]) pm(cli, minion, messages["minion_simple"])
pm(cli, minion, "Wolves: " + ", ".join(wolves)) pm(cli, minion, "Wolves: " + ", ".join(wolves))
villagers = copy.copy(var.ROLES["villager"])
villagers |= var.ROLES["time lord"]
if var.DEFAULT_ROLE == "villager":
villagers |= var.ROLES["vengeful ghost"] | var.ROLES["amnesiac"]
for villager in villagers:
if villager in var.PLAYERS and not is_user_simple(villager):
pm(cli, villager, messages["villager_notify"])
else:
pm(cli, villager, messages["villager_simple"])
cultists = copy.copy(var.ROLES["cultist"])
if var.DEFAULT_ROLE == "cultist":
cultists |= var.ROLES["vengeful ghost"] | var.ROLES["amnesiac"]
for cultist in cultists:
if cultist in var.PLAYERS and not is_user_simple(cultist):
pm(cli, cultist, messages["cultist_notify"])
else:
pm(cli, cultist, messages["cultist_simple"])
for blessed in var.ROLES["blessed villager"]: for blessed in var.ROLES["blessed villager"]:
if blessed in var.PLAYERS and not is_user_simple(blessed): if blessed in var.PLAYERS and not is_user_simple(blessed):
pm(cli, blessed, messages["blessed_notify"]) pm(cli, blessed, messages["blessed_notify"])
@ -7360,10 +7066,8 @@ def start(cli, nick, chan, forced = False, restart = ""):
var.GUNNERS = {} var.GUNNERS = {}
var.SEEN = set() var.SEEN = set()
var.OBSERVED = {} var.OBSERVED = {}
var.KILLS = {}
var.GUARDED = {} var.GUARDED = {}
var.HVISITED = {} var.HVISITED = {}
var.HUNTERS = set()
var.VENGEFUL_GHOSTS = {} var.VENGEFUL_GHOSTS = {}
var.CLONED = {} var.CLONED = {}
var.TARGETED = {} var.TARGETED = {}
@ -8201,9 +7905,9 @@ def myrole(cli, nick, chan, rest):
return return
role = get_role(nick) role = get_role(nick)
if role == "time lord": if role in var.HIDDEN_VILLAGERS:
role = "villager" role = "villager"
elif role in ("amnesiac", "vengeful ghost"): elif role in var.HIDDEN_ROLES:
role = var.DEFAULT_ROLE role = var.DEFAULT_ROLE
evt = Event("myrole", {"role": role, "messages": []}) evt = Event("myrole", {"role": role, "messages": []})