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
if not tgt:
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)
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.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")
def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
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)
SEEN_DEFAULT = frozenset({"traitor", "hag", "sorcerer", "time lord", "villager", "cultist", "minion", "turncoat", "amnesiac",
"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!
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
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
if rol in var.TRUE_NEUTRAL_ROLES:
# 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:
# entranced players can't win with villager or wolf teams
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 plr.startswith("(dced)"):
if pentry["dced"]:
# You get NOTHING! You LOSE! Good DAY, sir!
won = False
iwon = False
@ -2581,8 +2583,6 @@ def stop_game(cli, winner="", abort=False, additional_winners=None, log=True):
iwon = True
elif winner == "succubi" and splr in var.ENTRANCED | var.ROLES["succubus"]:
iwon = True
elif winner == "everyone" and not survived:
iwon = True # nobody survived but an event made everyone win
elif not iwon:
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:
# remove the player from variables if they're in there
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 k in list(x):
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]
for k in list(var.OTHER_KILLS):
if var.OTHER_KILLS[k] == nick:
var.HUNTERS.discard(k)
pm(cli, k, messages["hunter_discard"])
del var.OTHER_KILLS[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
# 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
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)
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
@ -3558,21 +3551,6 @@ def rename_player(cli, prefix, nick):
var.DOCTORS, var.BITTEN_ROLES, var.LYCAN_ROLES, var.AMNESIAC_ROLES):
if prefix in dictvar.keys():
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'}}
for dictvar in (var.LOVERS, var.ORIGINAL_LOVERS, var.DULLAHAN_TARGETS):
kvp = []
@ -3596,7 +3574,7 @@ def rename_player(cli, prefix, nick):
b = nick
var.EXCHANGED_ROLES[idx] = (a, b)
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.DISEASED, var.TOBEDISEASED, var.RETRIBUTION, var.MISDIRECTED, var.TOBEMISDIRECTED,
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
var.GAMEPHASE = "day"
var.KILLS = {} # nicknames of kill victims (wolves only)
var.OTHER_KILLS = {} # other kill victims (hunter/vengeful ghost)
var.OTHER_KILLS = {} # other kill victims (vigilante/vengeful ghost)
var.KILLER = "" # nickname of who chose the victim
var.SEEN = set() # set of doomsayers that have had visions
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()):
if crow not in var.ROLES["werecrow"]:
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
(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.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))
else:
pm(cli, crow, messages["werecrow_failure"].format(target))
@ -4102,63 +4081,29 @@ def transition_day(cli, gameid=0):
var.NIGHT_TIMEDELTA += td
min, sec = td.seconds // 60, td.seconds % 60
found = defaultdict(int)
for v in var.KILLS.values():
for p in v:
found[p] += 1
maxc = 0
victims = []
bitten = []
# dict of victim: list of killers (for retribution totem)
killers = defaultdict(list)
# wolves targeted, others may have as well (needed for harlot visit and maybe other things)
bywolves = set()
# wolves and nobody else targeted (needed for lycan)
onlybywolves = set()
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 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)
# built-in logic runs at the following priorities:
# 1 = wolf kills
# 2 = non-wolf kills
# 3 = alpha wolf bite
# 4 = protections/fallen angel
# 5 = rearranging victim list (ensure bodyguard/harlot messages plays),
# fixing killers dict to have correct priority (wolf-side VG kills -> non-wolf kills -> wolf kills)
# Actually killing off the victims happens in transition_day_resolve
evt = Event("transition_day", {
"victims": [],
"killers": defaultdict(list),
"bywolves": set(),
"onlybywolves": set(),
"protected": {},
"bitten": []
})
evt.dispatch(cli, var)
victims = evt.data["victims"]
killers = evt.data["killers"]
bywolves = evt.data["bywolves"]
onlybywolves = evt.data["onlybywolves"]
protected = evt.data["protected"]
bitten = evt.data["bitten"]
wolfghostvictims = []
for k, d in var.OTHER_KILLS.items():
@ -4174,7 +4119,6 @@ def transition_day(cli, gameid=0):
for v in var.ENTRANCED_DYING:
var.DYING.add(v)
# clear list so that it doesn't pm hunter / ghost about being able to kill again
var.OTHER_KILLS = {}
for k, d in var.DEATH_TOTEM:
@ -4701,16 +4645,14 @@ def chk_nightdone(cli):
# TODO: alphabetize and/or arrange sensibly
pl = list_players()
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.HEXED, var.SHAMANS, var.CURSED, var.CHARMERS)))
nightroles = get_roles("harlot", "succubus", "bodyguard",
"guardian angel", "wolf", "werecrow", "alpha wolf",
"sorcerer", "hunter", "hag", "shaman", "crazed shaman",
"werekitten", "warlock", "piper", "wolf mystic",
"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
nightroles = get_roles("harlot", "succubus", "bodyguard", "guardian angel",
"sorcerer", "hag", "shaman", "crazed shaman",
"warlock", "piper", "vigilante", "doomsayer",
"prophet", "wolf shaman")
for ghost, against in var.VENGEFUL_GHOSTS.items():
if not against.startswith("!"):
@ -4734,22 +4676,11 @@ def chk_nightdone(cli):
actedcount += len(var.MATCHMAKERS | var.CLONED.keys())
nightroles.extend(get_roles("matchmaker", "clone"))
if var.DISEASED_WOLVES:
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:
if var.ALPHA_ENABLED:
# alphas both kill and bite if they're activated at night, so add them into the counts
nightroles.extend(get_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
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"]:
if ass not in var.TARGETED.keys() | var.SILENCED:
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():
t[0].cancel()
@ -4980,13 +4896,6 @@ def check_exchange(cli, actor, nick):
del var.SHAMANS[actor]
if actor in var.LASTGIVEN:
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"):
if actor in var.GUARDED:
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"):
if actor in var.OBSERVED:
del var.OBSERVED[actor]
if actor in var.KILLS:
del var.KILLS[actor]
elif actor_role == "harlot":
if actor in var.HVISITED:
if var.HVISITED[actor] is not None:
@ -5015,12 +4922,8 @@ def check_exchange(cli, actor, nick):
var.DOCTORS[nick] = var.DOCTORS.pop(actor)
elif actor_role == "alpha wolf":
var.ALPHA_WOLVES.discard(actor)
if actor in var.KILLS:
del var.KILLS[actor]
elif actor_role == "doomsayer":
var.SEEN.discard(actor)
if actor in var.KILLS:
del var.KILLS[actor]
elif actor_role == "warlock":
var.CURSED.discard(actor)
elif actor_role == "turncoat":
@ -5046,13 +4949,6 @@ def check_exchange(cli, actor, nick):
del var.SHAMANS[nick]
if nick in var.LASTGIVEN:
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"):
if nick in var.GUARDED:
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"):
if nick in var.OBSERVED:
del var.OBSERVED[nick]
if nick in var.KILLS:
del var.KILLS[nick]
elif nick_role == "harlot":
if nick in var.HVISITED:
if var.HVISITED[nick] is not None:
@ -5080,12 +4974,8 @@ def check_exchange(cli, actor, nick):
var.DOCTORS[actor] = var.DOCTORS.pop(nick)
elif nick_role == "alpha wolf":
var.ALPHA_WOLVES.discard(nick)
if nick in var.KILLS:
del var.KILLS[nick]
elif nick_role == "doomsayer":
var.SEEN.discard(nick)
if nick in var.KILLS:
del var.KILLS[nick]
elif nick_role == "warlock":
var.CURSED.discard(nick)
elif nick_role == "turncoat":
@ -5121,15 +5011,15 @@ def check_exchange(cli, actor, nick):
del var.LYCAN_ROLES[nick]
actor_rev_role = actor_role
if actor_role == "vengeful ghost":
if actor_role in var.HIDDEN_ROLES:
actor_rev_role = var.DEFAULT_ROLE
elif actor_role == "time lord":
elif actor_role in var.HIDDEN_VILLAGERS:
actor_rev_role = "villager"
nick_rev_role = nick_role
if nick_role == "vengeful ghost":
if nick_role in var.HIDDEN_ROLES:
nick_rev_role = var.DEFAULT_ROLE
elif actor_role == "time lord":
elif actor_role in var.HIDDEN_VILLAGERS:
nick_rev_role = "villager"
# 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
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
if var.PHASE != "night":
return
@ -5272,17 +5162,9 @@ def retract(cli, nick, chan, rest):
if var.OBSERVED.get(nick):
pm(cli, nick, (messages["werecrow_transformed"]))
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():
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():
if role not in var.WOLF_ROLES and nick in var.OTHER_KILLS.keys():
del var.OTHER_KILLS[nick]
if role == "hunter":
var.HUNTERS.remove(nick)
pm(cli, nick, messages["retracted_kill"])
elif role == "alpha wolf" and nick in var.BITE_PREFERENCES.keys():
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)
elif role == "alpha wolf" and var.ALPHA_ENABLED:
pm(cli, nick, messages["kill_bite_pending"])
else:
pm(cli, nick, messages["kill_pending"])
return
if var.PHASE != "day":
@ -5413,10 +5293,7 @@ def kill(cli, nick, chan, rest):
role = get_role(nick)
except KeyError:
role = None
wolfroles = var.WOLF_ROLES - {"wolf cub"}
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():
if role not in {"dullahan", "vigilante"} and nick not in var.VENGEFUL_GHOSTS.keys():
return
if nick in var.VENGEFUL_GHOSTS.keys() and var.VENGEFUL_GHOSTS[nick][0] == "!":
# ghost was driven away by retribution
@ -5425,31 +5302,11 @@ def kill(cli, nick, chan, rest):
# all their targets are dead
pm(cli, nick, messages["dullahan_targets_dead"])
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:
pm(cli, nick, messages["silenced"])
return
if role in wolfroles and var.DISEASED_WOLVES:
pm(cli, nick, messages["ill_wolves"])
return
pieces = re.split(" +",rest)
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)
if not victim:
@ -5457,15 +5314,8 @@ def kill(cli, nick, chan, rest):
if is_safe(nick, victim):
pm(cli, nick, messages["no_acting_on_succubus"].format("kill"))
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():
pm(cli, nick, messages["player_dead"])
else:
@ -5481,53 +5331,16 @@ def kill(cli, nick, chan, rest):
pm(cli, nick, messages["vengeful_ghost_villager"])
return
if role in wolfroles:
if in_wolflist(nick, victim) or (victim2 is not None and in_wolflist(nick, victim2)):
pm(cli, nick, messages["wolf_no_target_wolf"])
rv = choose_target(nick, victim)
if nick not in var.VENGEFUL_GHOSTS.keys():
if check_exchange(cli, nick, rv):
return
if var.ANGRY_WOLVES and victim2 is not None:
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)
var.OTHER_KILLS[nick] = rv
if victim2 != None:
msg = messages["wolf_target_multiple"].format(victim, victim2)
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"])
msg = messages["wolf_target"].format(victim)
pm(cli, nick, messages["player"].format(msg))
if in_wolflist(nick, nick):
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)))
debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, role, victim, get_role(victim)))
chk_nightdone(cli)
@ -5652,8 +5465,10 @@ def observe(cli, nick, chan, rest):
if check_exchange(cli, nick, victim):
return
var.OBSERVED[nick] = victim
if nick in var.KILLS.keys():
del var.KILLS[nick]
# temp hack, will do something better once crow is split off
from src.roles import wolf
if nick in wolf.KILLS:
del wolf.KILLS[nick]
if role == "werecrow":
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)
@ -5845,7 +5660,6 @@ def hvisit(cli, nick, chan, rest):
if var.OTHER_KILLS.get(victim) in var.ROLES["succubus"]:
pm(cli, victim, messages["no_kill_succubus"].format(var.OTHER_KILLS[victim]))
del var.OTHER_KILLS[victim]
var.HUNTERS.discard(victim)
if var.TARGETED.get(victim) in var.ROLES["succubus"]:
msg = messages["no_target_succubus"].format(var.TARGETED[victim])
del var.TARGETED[victim]
@ -5863,13 +5677,19 @@ def hvisit(cli, nick, chan, rest):
var.TOBESILENCED.remove(nick)
var.HEXED.remove(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"]:
if s in var.KILLS[victim]:
if s in wolf.KILLS[victim]:
pm(cli, victim, messages["no_kill_succubus"].format(nick))
var.KILLS[victim].remove(s)
if not var.KILLS[victim]:
del var.KILLS[victim]
wolf.KILLS[victim].remove(s)
if not wolf.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"]:
pm(cli, victim, messages["no_kill_succubus"].format(var.BITE_PREFERENCES[victim]))
del var.BITE_PREFERENCES[victim]
@ -6027,7 +5847,7 @@ def bite_cmd(cli, nick, chan, rest):
chk_nightdone(cli)
@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):
"""Decline to use your special power for that night."""
nickrole = get_role(nick)
@ -6040,17 +5860,7 @@ def pass_cmd(cli, nick, chan, rest):
cli.notice(nick, messages["silenced"])
return
if nickrole == "hunter":
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 nickrole == "harlot":
if var.HVISITED.get(nick):
pm(cli, nick, (messages["harlot_already_visited"]).format(var.HVISITED[nick]))
return
@ -6563,14 +6373,13 @@ def transition_night(cli):
var.TIMERS = {}
# Reset nighttime variables
var.KILLS = {}
var.GUARDED = {} # key = by whom, value = the person that is visited
var.KILLER = "" # nickname of who chose the victim
var.SEEN = set() # set of seers that have had visions
var.HEXED = set() # set of hags that have hexed
var.CURSED = set() # set of warlocks that have cursed
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.CHARMERS = set() # pipers who have charmed
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
continue
showrole = amnrole
if showrole == "time lord":
if showrole in var.HIDDEN_VILLAGERS:
showrole = "villager"
elif showrole == "vengeful ghost":
elif showrole in var.HIDDEN_ROLES:
showrole = var.DEFAULT_ROLE
n = ""
if showrole.startswith(("a", "e", "i", "o", "u")):
@ -6665,78 +6474,6 @@ def transition_night(cli):
# send PMs
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"]:
pl = ps[:]
@ -6896,18 +6633,6 @@ def transition_night(cli):
if role not in var.WOLFCHAT_ROLES:
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"]:
targets = list(var.DULLAHAN_TARGETS[dullahan])
for target in var.DEAD:
@ -7118,25 +6843,6 @@ def transition_night(cli):
pm(cli, minion, messages["minion_simple"])
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"]:
if blessed in var.PLAYERS and not is_user_simple(blessed):
pm(cli, blessed, messages["blessed_notify"])
@ -7360,10 +7066,8 @@ def start(cli, nick, chan, forced = False, restart = ""):
var.GUNNERS = {}
var.SEEN = set()
var.OBSERVED = {}
var.KILLS = {}
var.GUARDED = {}
var.HVISITED = {}
var.HUNTERS = set()
var.VENGEFUL_GHOSTS = {}
var.CLONED = {}
var.TARGETED = {}
@ -8201,9 +7905,9 @@ def myrole(cli, nick, chan, rest):
return
role = get_role(nick)
if role == "time lord":
if role in var.HIDDEN_VILLAGERS:
role = "villager"
elif role in ("amnesiac", "vengeful ghost"):
elif role in var.HIDDEN_ROLES:
role = var.DEFAULT_ROLE
evt = Event("myrole", {"role": role, "messages": []})