Split blessed and cursed villager

This commit is contained in:
skizzerz 2016-09-13 16:35:54 -05:00
parent f5d510374a
commit 01dfdc7ac4
8 changed files with 281 additions and 144 deletions

View File

@ -241,6 +241,24 @@ def on_transition_night_end(evt, cli, var):
pm(cli, gangel, messages["guardian_simple"]) # !simple pm(cli, gangel, messages["guardian_simple"]) # !simple
pm(cli, gangel, "Players: " + ", ".join(pl)) pm(cli, gangel, "Players: " + ", ".join(pl))
@event_listener("assassinate")
def on_assassinate(evt, cli, var, nick, target, prot):
if prot == "angel" and var.GAMEPHASE == "night":
var.ACTIVE_PROTECTIONS[target].remove("angel")
evt.prevent_default = True
evt.stop_propagation = True
cli.msg(botconfig.CHANNEL, messages[evt.params.message_prefix + "angel"].format(nick, target))
elif prot == "bodyguard":
var.ACTIVE_PROTECTIONS[target].remove("bodyguard")
evt.prevent_default = True
evt.stop_propagation = True
for bg in var.ROLES["bodyguard"]:
if GUARDED.get(bg) == target:
cli.msg(botconfig.CHANNEL, messages[evt.params.message_prefix + "bodyguard"].format(nick, target, bg))
evt.params.del_player(cli, bg, True, end_game=False, killer_role=nickrole, deadlist=evt.params.deadlist, original=evt.params.original, ismain=False)
evt.data["pl"] = evt.params.refresh_pl(pl)
break
@event_listener("begin_day") @event_listener("begin_day")
def on_begin_day(evt, cli, var): def on_begin_day(evt, cli, var):
PASSED.clear() PASSED.clear()

91
src/roles/blessed.py Normal file
View File

@ -0,0 +1,91 @@
import re
import random
import itertools
import math
from collections import defaultdict
import botconfig
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
# TODO: some additional stuff with blessed villager has not been split yet,
# notably the interactions with assassin and mad scientist, need to split those out too
# as part of splitting assassin/MS (new events will be required)
@event_listener("transition_day", priority=4.3)
def on_transition_day(evt, cli, var):
pl = list_players()
vs = set(evt.data["victims"])
for v in pl:
if v in vs:
if v in var.DYING:
continue
if v in var.ROLES["blessed villager"]:
evt.data["numkills"][v] -= 1
if evt.data["numkills"][v] >= 0:
evt.data["killers"][v].pop(0)
if evt.data["numkills"][v] <= 0 and v not in evt.data["protected"]:
evt.data["protected"][v] = "blessing"
elif evt.data["numkills"][v] <= 0:
var.ACTIVE_PROTECTIONS[v].append("blessing")
elif v in var.ROLES["blessed villager"]:
var.ACTIVE_PROTECTIONS[v].append("blessing")
@event_listener("transition_day_resolve", priority=2)
def on_transition_day_resolve(evt, cli, var, victim):
# TODO: remove these checks once everything is split
# right now they're needed because otherwise protection may fire off even if the person isn't home
# that will not be an issue once everything is using the event
if victim in var.ROLES["harlot"] | var.ROLES["succubus"] and var.HVISITED.get(victim) and victim not in evt.data["dead"] and victim in evt.data["onlybywolves"]:
return
# END checks to remove
if evt.data["protected"].get(victim) == "blessing":
# don't play any special message for a blessed target, this means in a game with priest and monster it's not really possible
# for wolves to tell which is which. May want to change that in the future to be more obvious to wolves since there's not really
# any good reason to hide that info from them. In any case, we don't want to say the blessed person was attacked to the channel
evt.stop_propagation = True
evt.prevent_default = True
@event_listener("transition_night_end", priority=5)
def on_transition_night_end(evt, cli, var):
if var.FIRST_NIGHT or var.ALWAYS_PM_ROLE:
for blessed in var.ROLES["blessed villager"]:
if blessed in var.PLAYERS and not is_user_simple(blessed):
pm(cli, blessed, messages["blessed_notify"])
else:
pm(cli, blessed, messages["blessed_simple"])
@event_listener("desperation_totem")
def on_desperation(evt, cli, var, votee, target, prot):
if prot == "blessing":
var.ACTIVE_PROTECTIONS[target].remove("blessing")
evt.prevent_default = True
evt.stop_propagation = True
@event_listener("retribution_totem")
def on_retribution(evt, cli, var, victim, loser, prot):
if prot == "blessing":
var.ACTIVE_PROTECTIONS[target].remove("blessing")
evt.prevent_default = True
evt.stop_propagation = True
@event_listener("assassinate")
def on_assassinate(evt, cli, var, nick, target, prot):
if prot == "blessing":
var.ACTIVE_PROTECTIONS[target].remove("blessing")
evt.prevent_default = True
evt.stop_propagation = True
# don't message the channel whenever a blessing blocks a kill, but *do* let the killer know so they don't try to report it as a bug
pm(cli, nick, messages["assassin_fail_blessed"].format(target))
@event_listener("myrole")
def on_myrole(evt, cli, var, nick):
if nick in var.ROLES["blessed villager"]:
evt.data["messages"].append(messages["blessed_simple"])
# vim: set sw=4 expandtab:

25
src/roles/cursed.py Normal file
View File

@ -0,0 +1,25 @@
import re
import random
import itertools
import math
from collections import defaultdict
import botconfig
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
@event_listener("see")
def on_see(evt, cli, var, nick, victim):
if nick in var.ROLES["cursed villager"]:
evt.data["role"] = "wolf"
@event_listener("wolflist")
def on_wolflist(evt, cli, var, nick, wolf):
if nick in var.ROLES["cursed villager"]:
evt.data["tags"].add("cursed")
# vim: set sw=4 expandtab:

View File

@ -1,7 +1,7 @@
import math import math
import re import re
import random import random
from collections import defaultdict from collections import defaultdict, deque
import src.settings as var import src.settings as var
from src.utilities import * from src.utilities import *
@ -77,26 +77,21 @@ def on_del_player(evt, cli, var, nick, nickrole, nicktpls, death_triggers):
targets = TARGETS[nick] & set(pl) targets = TARGETS[nick] & set(pl)
if targets: if targets:
target = random.choice(list(targets)) target = random.choice(list(targets))
if "totem" in var.ACTIVE_PROTECTIONS[target]: prots = deque(var.ACTIVE_PROTECTIONS[target])
var.ACTIVE_PROTECTIONS[target].remove("totem") aevt = Event("assassinate", {"pl": evt.data["pl"]},
cli.msg(botconfig.CHANNEL, messages["dullahan_die_totem"].format(nick, target)) del_player=evt.params.del_player,
elif "angel" in var.ACTIVE_PROTECTIONS[target]: deadlist=evt.params.deadlist,
var.ACTIVE_PROTECTIONS[target].remove("angel") original=evt.params.original,
cli.msg(botconfig.CHANNEL, messages["dullahan_die_angel"].format(nick, target)) refresh_pl=evt.params.refresh_pl,
elif "bodyguard" in var.ACTIVE_PROTECTIONS[target]: message_prefix="dullahan_die_")
var.ACTIVE_PROTECTIONS[target].remove("bodyguard") while len(prots) > 0:
for bg in var.ROLES["bodyguard"]: # an event can read the current active protection and cancel the totem
if var.GUARDED.get(bg) == target: # if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS
cli.msg(botconfig.CHANNEL, messages["dullahan_die_bodyguard"].format(nick, target, bg)) # so that it cannot be used again (if the protection is meant to be usable once-only)
evt.params.del_player(cli, bg, True, end_game=False, killer_role=nickrole, deadlist=evt.params.deadlist, original=evt.params.original, ismain=False) if not aevt.dispatch(cli, var, nick, target, prots[0]):
evt.data["pl"] = evt.params.refresh_pl(pl) evt.data["pl"] = aevt.data["pl"]
break return
elif "blessing" in var.ACTIVE_PROTECTIONS[target] or (var.GAMEPHASE == "day" and target in var.ROLES["blessed villager"]): prots.popleft()
if "blessing" in var.ACTIVE_PROTECTIONS[target]:
var.ACTIVE_PROTECTIONS[target].remove("blessing")
# don't message the channel whenever a blessing blocks a kill, but *do* let the dullahan know so they don't try to report it as a bug
pm(cli, nick, messages["assassin_fail_blessed"].format(target))
else:
if var.ROLE_REVEAL in ("on", "team"): if var.ROLE_REVEAL in ("on", "team"):
role = get_reveal_role(target) role = get_reveal_role(target)
an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" an = "n" if role.startswith(("a", "e", "i", "o", "u")) else ""

View File

@ -33,7 +33,7 @@ def see(cli, nick, chan, rest):
victimrole = get_role(victim) victimrole = get_role(victim)
if role != "augur": if role != "augur":
if (victimrole in var.SEEN_WOLF and victimrole not in var.SEEN_DEFAULT) or victim in var.ROLES["cursed villager"]: if (victimrole in var.SEEN_WOLF and victimrole not in var.SEEN_DEFAULT):
victimrole = "wolf" victimrole = "wolf"
elif victimrole in var.SEEN_DEFAULT: elif victimrole in var.SEEN_DEFAULT:
victimrole = var.DEFAULT_ROLE victimrole = var.DEFAULT_ROLE

View File

@ -1,7 +1,7 @@
import re import re
import random import random
import itertools import itertools
from collections import defaultdict from collections import defaultdict, deque
import botconfig import botconfig
import src.settings as var import src.settings as var
@ -275,11 +275,16 @@ def on_chk_decision_lynch5(evt, cli, var, voters):
if votee in DESPERATION: if votee in DESPERATION:
# Also kill the very last person to vote them, unless they voted themselves last in which case nobody else dies # Also kill the very last person to vote them, unless they voted themselves last in which case nobody else dies
target = voters[-1] target = voters[-1]
# TODO: instead of desperation_totem event to accomodate blessed villager, base on var.ACTIVE_PROTECTIONS if target != votee:
# this means that prot totem, GA, and bodyguard would have a shot to block this too prots = deque(var.ACTIVE_PROTECTIONS[target])
# and so that blessed villager doesn't double-dip (like if they were targeted that past night) while len(prots) > 0:
# an event can read the current active protection and cancel the totem
# if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS
# so that it cannot be used again (if the protection is meant to be usable once-only)
desp_evt = Event("desperation_totem", {}) desp_evt = Event("desperation_totem", {})
if target != votee and desp_evt.dispatch(cli, var, votee, target) and target not in var.ROLES["blessed villager"]: if not desp_evt.dispatch(cli, var, votee, target, prots[0]):
return
prots.popleft()
if var.ROLE_REVEAL in ("on", "team"): if var.ROLE_REVEAL in ("on", "team"):
r1 = get_reveal_role(target) r1 = get_reveal_role(target)
an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else ""
@ -464,8 +469,17 @@ def on_transition_day_resolve6(evt, cli, var, victim):
evt.data["message"].extend(ret_evt.data["message"]) evt.data["message"].extend(ret_evt.data["message"])
if loser in evt.data["dead"] or victim == loser: if loser in evt.data["dead"] or victim == loser:
loser = None loser = None
# TODO: when blessed is split off, roll that check into retribution_kill if loser is not None:
if loser is not None and loser not in var.ROLES["blessed villager"]: prots = deque(var.ACTIVE_PROTECTIONS[loser])
while len(prots) > 0:
# an event can read the current active protection and cancel the totem
# if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS
# so that it cannot be used again (if the protection is meant to be usable once-only)
ret_evt = Event("retribution_totem", {"message": []})
if not ret_evt.dispatch(cli, var, victim, loser, prots[0]):
evt.data["message"].extend(ret_evt.data["message"])
return
prots.popleft()
evt.data["dead"].append(loser) evt.data["dead"].append(loser)
if var.ROLE_REVEAL in ("on", "team"): if var.ROLE_REVEAL in ("on", "team"):
role = get_reveal_role(loser) role = get_reveal_role(loser)
@ -551,6 +565,14 @@ def on_lynch(evt, cli, var, nick):
pm(cli, nick, messages["totem_narcolepsy"]) pm(cli, nick, messages["totem_narcolepsy"])
evt.prevent_default = True evt.prevent_default = True
@event_listener("assassinate")
def on_assassinate(evt, cli, var, nick, target, prot):
if prot == "totem":
var.ACTIVE_PROTECTIONS[target].remove("totem")
evt.prevent_default = True
evt.stop_propagation = True
cli.msg(botconfig.CHANNEL, messages[evt.params.message_prefix + "totem"].format(nick, target))
@event_listener("myrole") @event_listener("myrole")
def on_myrole(evt, cli, var, nick): def on_myrole(evt, cli, var, nick):
role = evt.data["role"] role = evt.data["role"]

View File

@ -230,14 +230,16 @@ def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
prole = get_role(player) prole = get_role(player)
if player == nick: if player == nick:
prole = actor_role prole = actor_role
wevt = Event("wolflist", {"tags": set()})
wevt.dispatch(cli, var, player, actor)
tags = " ".join(wevt.data["tags"])
if prole in wcroles: if prole in wcroles:
cursed = "" if tags:
if player in var.ROLES["cursed villager"]: tags += " "
cursed = "cursed " pl[i] = "\u0002{0}\u0002 ({1}{2})".format(player, tags, prole)
pl[i] = "\u0002{0}\u0002 ({1}{2})".format(player, cursed, prole)
notify.append(player) notify.append(player)
elif player in var.ROLES["cursed villager"]: elif tags:
pl[i] = player + " (cursed)" pl[i] = "{0} ({1})".format(player, tags)
mass_privmsg(cli, notify, messages["players_exchanged_roles"].format(nick, actor)) mass_privmsg(cli, notify, messages["players_exchanged_roles"].format(nick, actor))
evt.data["actor_messages"].append("Players: " + ", ".join(pl)) evt.data["actor_messages"].append("Players: " + ", ".join(pl))
@ -256,14 +258,16 @@ def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
prole = get_role(player) prole = get_role(player)
if player == actor: if player == actor:
prole = nick_role prole = nick_role
wevt = Event("wolflist", {"tags": set()})
wevt.dispatch(cli, var, player, nick)
tags = " ".join(wevt.data["tags"])
if prole in wcroles: if prole in wcroles:
cursed = "" if tags:
if player in var.ROLES["cursed villager"]: tags += " "
cursed = "cursed " pl[i] = "\u0002{0}\u0002 ({1}{2})".format(player, tags, prole)
pl[i] = "\u0002{0}\u0002 ({1}{2})".format(player, cursed, prole)
notify.append(player) notify.append(player)
elif player in var.ROLES["cursed villager"]: elif tags:
pl[i] = player + " (cursed)" pl[i] = "{0} ({1})".format(player, tags)
mass_privmsg(cli, notify, messages["players_exchanged_roles"].format(actor, nick)) mass_privmsg(cli, notify, messages["players_exchanged_roles"].format(actor, nick))
evt.data["nick_messages"].append("Players: " + ", ".join(pl)) evt.data["nick_messages"].append("Players: " + ", ".join(pl))
@ -333,34 +337,36 @@ def on_transition_night_end(evt, cli, var):
talkroles = var.WOLF_ROLES | {"traitor"} talkroles = var.WOLF_ROLES | {"traitor"}
for wolf in wolves: 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) normal_notify = wolf in var.PLAYERS and not is_user_simple(wolf)
role = get_role(wolf) role = get_role(wolf)
cursed = "cursed " if wolf in var.ROLES["cursed villager"] and role in wcroles else "" wevt = Event("wolflist", {"tags": set()})
tags = ""
if role in wcroles:
wevt.dispatch(cli, var, wolf, wolf)
tags = " ".join(wevt.data["tags"])
if normal_notify: if normal_notify:
msg = "{0}_notify".format(role.replace(" ", "_")) msg = "{0}_notify".format(role.replace(" ", "_"))
cmsg = "cursed_" + msg cmsg = "cursed_" + msg
if "cursed" in wevt.data["tags"]:
try: try:
if cursed: tags2 = " ".join(wevt.data["tags"] - {"cursed"})
try: pm(cli, wolf, messages[cmsg].format(tags2))
pm(cli, wolf, messages[cmsg])
except KeyError: except KeyError:
pm(cli, wolf, messages[msg].format(cursed)) pm(cli, wolf, messages[msg].format(tags))
else: else:
pm(cli, wolf, messages[msg].format(cursed)) pm(cli, wolf, messages[msg].format(tags))
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 and wccond is not None and role in talkroles: if len(wolves) > 1 and wccond is not None and role in talkroles:
pm(cli, wolf, messages["wolfchat_notify"].format(wccond)) pm(cli, wolf, messages["wolfchat_notify"].format(wccond))
else: else:
an = "n" if cursed == "" and role.startswith(("a", "e", "i", "o", "u")) else "" an = ""
pm(cli, wolf, messages["wolf_simple"].format(an, cursed, role)) # !simple if tags:
if tags.startswith(("a", "e", "i", "o", "u")):
an = "n"
elif role.startswith(("a", "e", "i", "o", "u")):
an = "n"
pm(cli, wolf, messages["wolf_simple"].format(an, tags, role)) # !simple
pl = ps[:] pl = ps[:]
random.shuffle(pl) random.shuffle(pl)
@ -368,14 +374,17 @@ def on_transition_night_end(evt, cli, var):
if role in wcroles: if role in wcroles:
for i, player in enumerate(pl): for i, player in enumerate(pl):
prole = get_role(player) prole = get_role(player)
wevt.data["tags"] = set()
wevt.dispatch(cli, var, player, wolf)
tags = " ".join(wevt.data["tags"])
if prole in wcroles: if prole in wcroles:
cursed = "" if tags:
if player in var.ROLES["cursed villager"]: tags += " "
cursed = "cursed " pl[i] = "\u0002{0}\u0002 ({1}{2})".format(player, tags, prole)
pl[i] = "\u0002{0}\u0002 ({1}{2})".format(player, cursed, prole) elif tags:
elif player in var.ROLES["cursed villager"]: pl[i] = "{0} ({1})".format(player, tags)
pl[i] = player + " (cursed)"
elif role == "warlock": elif role == "warlock":
# warlock specifically only sees cursed if they're not in wolfchat
for i, player in enumerate(pl): for i, player in enumerate(pl):
if player in var.ROLES["cursed villager"]: if player in var.ROLES["cursed villager"]:
pl[i] = player + " (cursed)" pl[i] = player + " (cursed)"

View File

@ -36,7 +36,7 @@ import threading
import time import time
import traceback import traceback
import urllib.request import urllib.request
from collections import defaultdict from collections import defaultdict, deque
from datetime import datetime, timedelta from datetime import datetime, timedelta
from oyoyo.parse import parse_nick from oyoyo.parse import parse_nick
@ -1420,14 +1420,17 @@ def stats(cli, nick, chan, rest):
if role in badguys: if role in badguys:
for i, player in enumerate(ps): for i, player in enumerate(ps):
prole = get_role(player) prole = get_role(player)
wevt = Event("wolflist", {"tags": set()})
wevt.dispatch(cli, var, player, nick)
tags = " ".join(wevt.data["tags"])
if prole in badguys: if prole in badguys:
cursed = "" if tags:
if player in var.ROLES["cursed villager"]: tags += " "
cursed = "cursed " ps[i] = "\u0002{0}\u0002 ({1}{2})".format(player, tags, prole)
ps[i] = "\u0002{0}\u0002 ({1}{2})".format(player, cursed, prole) elif tags:
elif player in var.ROLES["cursed villager"]: ps[i] = "{0} ({1})".format(player, tags)
ps[i] = player + " (cursed)"
elif role == "warlock": elif role == "warlock":
# warlock not in wolfchat explicitly only sees cursed
for i, player in enumerate(pl): for i, player in enumerate(pl):
if player in var.ROLES["cursed villager"]: if player in var.ROLES["cursed villager"]:
ps[i] = player + " (cursed)" ps[i] = player + " (cursed)"
@ -2823,10 +2826,12 @@ def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death
random.shuffle(wolves) random.shuffle(wolves)
for i, wolf in enumerate(wolves): for i, wolf in enumerate(wolves):
wolfrole = get_role(wolf) wolfrole = get_role(wolf)
cursed = "" wevt = Event("wolflist", {"tags": set()})
if wolf in var.ROLES["cursed villager"]: wevt.dispatch(cli, var, wolf, clone)
cursed = "cursed " tags = " ".join(wevt.data["tags"])
wolves[i] = "\u0002{0}\u0002 ({1}{2})".format(wolf, cursed, wolfrole) if tags:
tags += " "
wolves[i] = "\u0002{0}\u0002 ({1}{2})".format(wolf, tags, wolfrole)
if len(wolves): if len(wolves):
pm(cli, clone, "Wolves: " + ", ".join(wolves)) pm(cli, clone, "Wolves: " + ", ".join(wolves))
@ -2862,32 +2867,28 @@ def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death
if nick in var.TARGETED: if nick in var.TARGETED:
target = var.TARGETED[nick] target = var.TARGETED[nick]
del var.TARGETED[nick] del var.TARGETED[nick]
if target != None and target in pl: if target is not None and target in pl:
# TODO: split this off into an event so that individual roles can handle it as needed prots = deque(var.ACTIVE_PROTECTIONS[target])
if "totem" in var.ACTIVE_PROTECTIONS[target] and nickrole != "fallen angel": aevt = Event("assassinate", {"pl": pl},
var.ACTIVE_PROTECTIONS[target].remove("totem") del_player=del_player,
message = messages["assassin_fail_totem"].format(nick, target) deadlist=deadlist,
cli.msg(botconfig.CHANNEL, message) original=original,
elif "angel" in var.ACTIVE_PROTECTIONS[target] and nickrole != "fallen angel": refresh_pl=refresh_pl,
var.ACTIVE_PROTECTIONS[target].remove("angel") message_prefix="assassin_fail_")
message = messages["assassin_fail_angel"].format(nick, target) while len(prots) > 0:
cli.msg(botconfig.CHANNEL, message) # FA bypasses all protection (TODO: split off)
elif "bodyguard" in var.ACTIVE_PROTECTIONS[target] and nickrole != "fallen angel": # when split instead of setting prots to [] will need to stop_propagation but NOT prevent_default
var.ACTIVE_PROTECTIONS[target].remove("bodyguard") if nickrole == "fallen angel":
from src.roles import angel prots = []
for ga in var.ROLES["bodyguard"]:
if angel.GUARDED.get(ga) == target:
message = messages["assassin_fail_bodyguard"].format(nick, target, ga)
cli.msg(botconfig.CHANNEL, message)
del_player(cli, ga, True, end_game=False, killer_role=nickrole, deadlist=deadlist, original=original, ismain=False)
pl = refresh_pl(pl)
break break
elif "blessing" in var.ACTIVE_PROTECTIONS[target] or (var.GAMEPHASE == "day" and target in var.ROLES["blessed villager"]): # an event can read the current active protection and cancel the totem
if "blessing" in var.ACTIVE_PROTECTIONS[target]: # if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS
var.ACTIVE_PROTECTIONS[target].remove("blessing") # so that it cannot be used again (if the protection is meant to be usable once-only)
# don't message the channel whenever a blessing blocks a kill, but *do* let the assassin know so they don't try to report it as a bug if not aevt.dispatch(cli, var, nick, target, prots[0]):
pm(cli, nick, messages["assassin_fail_blessed"].format(target)) pl = aevt.data["pl"]
else: break
prots.popleft()
if len(prots) == 0:
if var.ROLE_REVEAL in ("on", "team"): if var.ROLE_REVEAL in ("on", "team"):
role = get_reveal_role(target) role = get_reveal_role(target)
an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" an = "n" if role.startswith(("a", "e", "i", "o", "u")) else ""
@ -3691,7 +3692,6 @@ def begin_day(cli):
var.LUCKY = set() var.LUCKY = set()
var.DISEASED = set() var.DISEASED = set()
var.MISDIRECTED = set() var.MISDIRECTED = set()
var.ACTIVE_PROTECTIONS = defaultdict(list)
var.ENTRANCED_DYING = set() var.ENTRANCED_DYING = set()
var.DYING = set() var.DYING = set()
@ -3862,6 +3862,7 @@ def transition_day(cli, gameid=0):
# 2 = non-wolf kills # 2 = non-wolf kills
# 3 = fixing killers dict to have correct priority (wolf-side VG kills -> non-wolf kills -> wolf kills) # 3 = fixing killers dict to have correct priority (wolf-side VG kills -> non-wolf kills -> wolf kills)
# 4 = protections/fallen angel # 4 = protections/fallen angel
# 4.1 = shaman, 4.2 = bodyguard/GA, 4.3 = blessed villager, 4.8 = fallen angel
# 5 = alpha wolf bite, other custom events that trigger after all protection stuff is resolved # 5 = alpha wolf bite, other custom events that trigger after all protection stuff is resolved
# 6 = rearranging victim list (ensure bodyguard/harlot messages plays), # 6 = rearranging victim list (ensure bodyguard/harlot messages plays),
# fixing killers dict priority again (in case step 4 or 5 added to it) # fixing killers dict priority again (in case step 4 or 5 added to it)
@ -3898,25 +3899,14 @@ def transition_day(cli, gameid=0):
# Logic out stacked kills and protections. If we get down to 1 kill remaining that is valid and the victim is in bywolves, # Logic out stacked kills and protections. If we get down to 1 kill remaining that is valid and the victim is in bywolves,
# we re-add them to onlybywolves to indicate that the other kill attempts were guarded against (and the wolf kill is what went through) # we re-add them to onlybywolves to indicate that the other kill attempts were guarded against (and the wolf kill is what went through)
# If protections >= kills, we keep track of which protection message to show (prot totem > GA > bodyguard > blessing) # If protections >= kills, we keep track of which protection message to show (prot totem > GA > bodyguard > blessing)
# TODO: split out adding people back to onlybywolves as part of splitting off FA
pl = list_players() pl = list_players()
for v in pl: for v in pl:
if v in victims_set: if v in victims_set:
if v in var.DYING: if v in var.DYING:
continue # bypass protections continue # dying by themselves, not killed by wolves
if v in var.ROLES["blessed villager"]:
numkills[v] -= 1
if numkills[v] >= 0:
killers[v].pop(0)
if numkills[v] <= 0 and v not in protected:
protected[v] = "blessing"
elif numkills[v] <= 0:
var.ACTIVE_PROTECTIONS[v].append("blessing")
if numkills[v] == 1 and v in bywolves: if numkills[v] == 1 and v in bywolves:
onlybywolves.add(v) onlybywolves.add(v)
else:
# player wasn't targeted, but apply protections on them
if v in var.ROLES["blessed villager"]:
var.ACTIVE_PROTECTIONS[v].append("blessing")
fallenkills = set() fallenkills = set()
brokentotem = set() brokentotem = set()
@ -4145,11 +4135,6 @@ def transition_day(cli, gameid=0):
if victim not in revt.data["bitten"]: if victim not in revt.data["bitten"]:
revt.data["message"].append(messages["target_not_home"]) revt.data["message"].append(messages["target_not_home"])
revt.data["novictmsg"] = False revt.data["novictmsg"] = False
elif revt.data["protected"].get(victim) == "blessing":
# don't play any special message for a blessed target, this means in a game with priest and monster it's not really possible
# for wolves to tell which is which. May want to change that in the future to be more obvious to wolves since there's not really
# any good reason to hide that info from them. In any case, we don't want to say the blessed person was attacked to the channel
continue
elif (victim in var.ROLES["lycan"] or victim in var.LYCANTHROPES) and victim in revt.data["onlybywolves"] and victim not in var.IMMUNIZED: elif (victim in var.ROLES["lycan"] or victim in var.LYCANTHROPES) and victim in revt.data["onlybywolves"] and victim not in var.IMMUNIZED:
vrole = get_role(victim) vrole = get_role(victim)
if vrole not in var.WOLFCHAT_ROLES: if vrole not in var.WOLFCHAT_ROLES:
@ -4166,10 +4151,12 @@ def transition_day(cli, gameid=0):
for i, wolf in enumerate(wolves): for i, wolf in enumerate(wolves):
pm(cli, wolf, messages["lycan_wc_notification"].format(victim)) pm(cli, wolf, messages["lycan_wc_notification"].format(victim))
role = get_role(wolf) role = get_role(wolf)
cursed = "" wevt = Event("wolflist", {"tags": set()})
if wolf in var.ROLES["cursed villager"]: wevt.dispatch(cli, var, wolf, victim)
cursed = "cursed " tags = " ".join(wevt.data["tags"])
wolves[i] = "\u0002{0}\u0002 ({1}{2})".format(wolf, cursed, role) if tags:
tags += " "
wolves[i] = "\u0002{0}\u0002 ({1}{2})".format(wolf, tags, role)
pm(cli, victim, "Wolves: " + ", ".join(wolves)) pm(cli, victim, "Wolves: " + ", ".join(wolves))
revt.data["novictmsg"] = False revt.data["novictmsg"] = False
@ -6052,12 +6039,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))
for blessed in var.ROLES["blessed villager"]:
if blessed in var.PLAYERS and not is_user_simple(blessed):
pm(cli, blessed, messages["blessed_notify"])
else:
pm(cli, blessed, messages["blessed_simple"])
for g in var.GUNNERS.keys(): for g in var.GUNNERS.keys():
if g not in ps: if g not in ps:
continue continue
@ -7116,10 +7097,6 @@ def myrole(cli, nick, chan, rest):
if nick in var.ROLES["assassin"] and nick not in var.ROLES["amnesiac"]: if nick in var.ROLES["assassin"] and nick not in var.ROLES["amnesiac"]:
pm(cli, nick, messages["assassin_role_info"].format(messages["assassin_targeting"].format(var.TARGETED[nick]) if nick in var.TARGETED else "")) pm(cli, nick, messages["assassin_role_info"].format(messages["assassin_targeting"].format(var.TARGETED[nick]) if nick in var.TARGETED else ""))
# Remind blessed villager of their role
if nick in var.ROLES["blessed villager"]:
pm(cli, nick, messages["blessed_simple"])
# Remind prophet of their role, in sleepy mode only where it is hacked into a template instead of a role # Remind prophet of their role, in sleepy mode only where it is hacked into a template instead of a role
if "prophet" in var.TEMPLATE_RESTRICTIONS and nick in var.ROLES["prophet"]: if "prophet" in var.TEMPLATE_RESTRICTIONS and nick in var.ROLES["prophet"]:
pm(cli, nick, messages["prophet_simple"]) pm(cli, nick, messages["prophet_simple"])