Split off wolf (WIP)

This commit is contained in:
skizzerz 2016-08-09 17:10:56 -05:00
parent 3855c54c35
commit 97c0981e2a
4 changed files with 332 additions and 286 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):

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"):

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

@ -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]):
@ -3558,21 +3552,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 = []
@ -3827,7 +3806,6 @@ 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 (hunter/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
@ -4083,10 +4061,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 +4082,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():
@ -4701,16 +4647,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", "hunter", "hag", "shaman", "crazed shaman", "sorcerer", "hunter", "hag", "shaman", "crazed shaman",
"werekitten", "warlock", "piper", "wolf mystic", "warlock", "piper", "vigilante", "doomsayer",
"fallen angel", "vigilante", "doomsayer", "doomsayer", # NOT a mistake, doomsayer MUST be listed twice "prophet", "wolf shaman")
"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,12 +4678,7 @@ 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"]])
@ -4777,21 +4716,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,9 +4904,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": elif actor_role == "hunter":
if actor in var.OTHER_KILLS: if actor in var.OTHER_KILLS:
del var.OTHER_KILLS[actor] del var.OTHER_KILLS[actor]
@ -4995,8 +4916,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 +4934,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,9 +4961,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": elif nick_role == "hunter":
if nick in var.OTHER_KILLS: if nick in var.OTHER_KILLS:
del var.OTHER_KILLS[nick] del var.OTHER_KILLS[nick]
@ -5061,8 +4973,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 +4990,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":
@ -5275,11 +5181,7 @@ def retract(cli, nick, chan, rest):
elif role == "hunter" and nick in var.HUNTERS and nick not in var.OTHER_KILLS.keys(): elif role == "hunter" and nick in var.HUNTERS and nick not in var.OTHER_KILLS.keys():
return 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": if role == "hunter":
var.HUNTERS.remove(nick) var.HUNTERS.remove(nick)
@ -5291,8 +5193,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 +5313,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 {"hunter", "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
@ -5432,24 +5329,8 @@ def kill(cli, nick, chan, rest):
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 +5338,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,28 +5355,6 @@ 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:
if in_wolflist(nick, victim) or (victim2 is not None and in_wolflist(nick, victim2)):
pm(cli, nick, messages["wolf_no_target_wolf"])
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) rv = choose_target(nick, victim)
if nick not in var.VENGEFUL_GHOSTS.keys(): if nick not in var.VENGEFUL_GHOSTS.keys():
if check_exchange(cli, nick, rv): if check_exchange(cli, nick, rv):
@ -5512,21 +5364,9 @@ def kill(cli, nick, chan, rest):
var.HUNTERS.add(nick) var.HUNTERS.add(nick)
var.PASSED.discard(nick) var.PASSED.discard(nick)
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) msg = messages["wolf_target"].format(victim)
pm(cli, nick, messages["player"].format(msg)) 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):
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) chk_nightdone(cli)
@ -5652,8 +5492,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)
@ -5863,13 +5705,15 @@ 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
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 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]
@ -6563,7 +6407,6 @@ 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
@ -6665,78 +6508,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[:]
@ -7341,7 +7112,6 @@ 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.HUNTERS = set()