def hvisit(var, wrapper, message): """Entrance a player, converting them to your team.""" if VISITED.get(wrapper.source): wrapper.send(messages["succubus_already_visited"].format(VISITED[wrapper.source])) return target = get_target(var, wrapper, re.split(" +", message)[0], not_self_message="succubus_not_self") if not target: return target = try_misdirection(var, wrapper.source, target) if try_exchange(var, wrapper.source, target): return VISITED[wrapper.source] = target PASSED.discard(wrapper.source) if target not in get_all_players(("succubus",)): ENTRANCED.add(target) wrapper.send(messages["succubus_target_success"].format(target)) else: wrapper.send(messages["harlot_success"].format(target)) if wrapper.source is not target: if target not in get_all_players(("succubus",)): target.send(messages["notify_succubus_target"].format(wrapper.source)) else: target.send(messages["harlot_success"].format(wrapper.source)) revt = Event("succubus_visit", {}) revt.dispatch(var, wrapper.source, target) debuglog("{0} (succubus) VISIT: {1} ({2})".format(wrapper.source, target, get_main_role(target)))
def on_transition_night_end(evt, var): for monster in get_all_players(("monster",)): add_protection(var, monster, protector=None, protector_role="monster", scope=Wolf) if monster.prefers_simple(): monster.send(messages["monster_simple"]) else: monster.send(messages["monster_notify"])
def on_myrole(evt, var, user): if user in get_all_players(("minion",)): wolves = [] for wolfrole in Wolf: for player in var.ORIGINAL_ROLES[wolfrole]: wolves.append(player.nick) evt.data["messages"].append(messages["original_wolves"].format(", ".join(wolves)))
def on_transition_night_end(evt, var): for gunner in get_all_players((rolename,)): if GUNNERS[gunner]: if gunner.prefers_simple(): # gunner and sharpshooter share the same key for simple gunner.send(messages["gunner_simple"].format(rolename, GUNNERS[gunner], "s" if GUNNERS[gunner] > 1 else "")) else: gunner.send(messages["{0}_notify".format(rolename)].format(botconfig.CMD_CHAR, GUNNERS[gunner], "s" if GUNNERS[gunner] > 1 else ""))
def on_gun_chances(evt, var, user, role): if role != "sharpshooter" and user in get_all_players(("village drunk",)): hit, miss, headshot = var.DRUNK_GUN_CHANCES evt.data["hit"] = hit evt.data["miss"] = miss evt.data["headshot"] = headshot evt.stop_processing = True
def on_assassin_target(evt, var, assassin, players): if evt.data["target"] is None and assassin in get_all_players(("village drunk",)): evt.data["target"] = random.choice(players) message = messages["drunken_assassin_notification"].format(evt.data["target"]) if not assassin.prefers_simple(): message += messages["assassin_info"] assassin.send(message)
def curse(var, wrapper, message): target = get_target(var, wrapper, re.split(" +", message)[0]) if not target: return if target in get_all_players(("cursed villager",)): wrapper.pm(messages["target_already_cursed"].format(target)) return # There may actually be valid strategy in cursing other wolfteam members, # but for now it is not allowed. If someone seems suspicious and shows as # villager across multiple nights, safes can use that as a tell that the # person is likely wolf-aligned. if is_known_wolf_ally(var, wrapper.source, target): wrapper.pm(messages["no_curse_wolf"]) return orig = target target = try_misdirection(var, wrapper.source, target) if try_exchange(var, wrapper.source, target): return CURSED[wrapper.source] = target PASSED.discard(wrapper.source) wrapper.pm(messages["curse_success"].format(orig)) send_wolfchat_message(var, wrapper.source, messages["curse_success_wolfchat"].format(wrapper.source, orig), {"warlock"}, role="warlock", command="curse") debuglog("{0} (warlock) CURSE: {1} ({2})".format(wrapper.source, target, get_main_role(target)))
def on_transition_night_end(evt, var): pl = set() ctr = Counter() for t in types: cat = cats.get(t) players = get_players(cat) pl.update(players) ctr[t] += len(players) values = [] plural = True for name in types: keyname = "mystic_" + name.lower().replace(" ", "_") l = ctr[name] if l: if not values and l == 1: plural = False else: l = "no" values.append("\u0002{0}\u0002 {1}{2}".format(l, messages[keyname], "" if l == 1 else "s")) if len(values) > 2: value = " and ".join((", ".join(values[:-1]), values[-1])) else: value = " and ".join(values) msg = messages["mystic_info"].format("are" if plural else "is", value, " still", "") for mystic in get_all_players((rolename,)): LAST_COUNT[mystic] = (value, plural) if send_role: to_send = "{0}_{1}".format(role, ("simple" if mystic.prefers_simple() else "notify")) mystic.send(messages[to_send]) mystic.send(msg)
def on_transition_night_end(evt, var): if get_all_players(("doomsayer",)): status.add_lycanthropy_scope(var, All) # any role can transform if ds is in play for lycan in LYCANS.values(): status.add_lycanthropy(var, lycan) LYCANS.clear() SICK.clear()
def on_transition_night_end(evt, var): if not ENABLED: return can_bite = get_all_players(("alpha wolf",)) - ALPHAS if can_bite: for alpha in can_bite: alpha.queue_message(messages["wolf_bite"]) alpha.send_messages()
def on_get_role_metadata(evt, var, kind): if kind == "night_kills" and ENABLED: # biting someone has a chance of killing them instead of turning # and it can be guarded against, so it's close enough to a kill by that measure can_bite = get_all_players(("alpha wolf",)) - ALPHAS evt.data["alpha wolf"] = len(can_bite) elif kind == "role_categories": evt.data["alpha wolf"] = {"Wolf", "Wolfchat", "Wolfteam", "Killer", "Nocturnal"}
def on_transition_day_begin(evt, var): pl = get_players() for mm in get_all_players(("matchmaker",)): if mm not in MATCHMAKERS: lovers = random.sample(pl, 2) MATCHMAKERS.add(mm) _set_lovers(*lovers) mm.send(messages["random_matchmaker"])
def on_transition_night_end(evt, var): for blessed in get_all_players(("blessed villager",)): status.add_protection(var, blessed, blessed, "blessed villager") if var.NIGHT_COUNT == 1 or var.ALWAYS_PM_ROLE: to_send = "blessed_notify" if blessed.prefers_simple(): to_send = "blessed_simple" blessed.send(messages[to_send])
def on_transition_night_end(evt, var): wolves = get_players(get_wolfchat_roles(var)) for child in get_all_players(("wild child",)): if child in wolves: continue if child.prefers_simple(): child.send(messages["wild_child_simple"]) else: child.send(messages["wild_child_notify"])
def on_transition_night_end(evt, var): for harlot in get_all_players(("harlot",)): pl = get_players() random.shuffle(pl) pl.remove(harlot) to_send = "harlot_info" if harlot.prefers_simple(): to_send = "harlot_simple" harlot.send(messages[to_send], messages["players_list"].format(", ".join(p.nick for p in pl)), sep="\n")
def on_transition_night_end(evt, var): for ms in get_all_players(("mad scientist",)): pl = get_players() target1, target2 = _get_targets(var, pl, ms) to_send = "mad_scientist_notify" if ms.prefers_simple(): to_send = "mad_scientist_simple" ms.send(messages[to_send].format(target1, target2))
def on_del_player(evt, var, player, all_roles, death_triggers): del IDOLS[:player:] if not death_triggers: return for child in get_all_players(("wild child",)): if IDOLS.get(child) is player: # Change their main role to wolf change_role(var, child, get_main_role(child), "wolf", message="wild_child_idol_died") var.ROLES["wild child"].add(child)
def on_transition_night_end(evt, var): ps = get_players() for vigilante in get_all_players(("vigilante",)): pl = ps[:] random.shuffle(pl) pl.remove(vigilante) to_send = "vigilante_notify" if vigilante.prefers_simple(): to_send = "vigilante_simple" vigilante.send(messages[to_send], messages["players_list"].format(", ".join(p.nick for p in pl)), sep="\n")
def on_transition_day_resolve_end(evt, var, victims): for gangel in get_all_players(("guardian angel",)): if GUARDED.get(gangel) in get_players(Wolf) and gangel not in evt.data["dead"]: r = random.random() if r < var.GUARDIAN_ANGEL_DIES_CHANCE: if var.ROLE_REVEAL == "on": evt.data["message"][gangel].append(messages["guardian_angel_protected_wolf"].format(gangel)) else: # off and team evt.data["message"][gangel].append(messages["guardian_angel_protected_wolf_no_reveal"].format(gangel)) evt.data["dead"].append(gangel)
def on_transition_night_end(evt, var): lycans = get_all_players(("lycan",)) if lycans: add_lycanthropy_scope(var, {"lycan"}) for lycan in lycans: add_lycanthropy(var, lycan) if lycan.prefers_simple(): lycan.send(messages["lycan_simple"]) else: lycan.send(messages["lycan_notify"])
def on_transition_night_end(evt, var): ps = set(get_players()) - CHARMED for piper in get_all_players(("piper",)): pl = list(ps) random.shuffle(pl) pl.remove(piper) to_send = "piper_notify" if piper.prefers_simple(): to_send = "piper_simple" piper.send(messages[to_send], messages["players_list"].format(", ".join(p.nick for p in pl)), sep="\n")
def on_transition_night_begin(evt, var): global STATS_FLAG if var.NIGHT_COUNT == var.AMNESIAC_NIGHTS: amnesiacs = get_all_players(("amnesiac",)) if amnesiacs and not var.HIDDEN_AMNESIAC: STATS_FLAG = True for amn in amnesiacs: role = change_role(var, amn, "amnesiac", ROLES[amn], message="amnesia_clear") debuglog("{0} REMEMBER: {1}".format(amn, role))
def on_transition_night_end(evt, var): for minion in get_all_players(("minion",)): if minion in RECEIVED_INFO and not var.ALWAYS_PM_ROLE: continue if minion.prefers_simple(): to_send = "minion_simple" else: to_send = "minion_notify" minion.send(messages[to_send]) minion.send(wolf_list(var)) RECEIVED_INFO.add(minion)
def on_transition_night_end(evt, var): ps = get_players() for doctor in get_all_players(("doctor",)): if DOCTORS[doctor]: # has immunizations remaining pl = ps[:] random.shuffle(pl) if doctor.prefers_simple(): doctor.send(messages["doctor_simple"]) else: doctor.send(messages["doctor_notify"]) doctor.send(messages["doctor_immunizations"].format(DOCTORS[doctor], "s" if DOCTORS[doctor] > 1 else ""))
def on_transition_day_begin(evt, var): if not var.START_WITH_DAY or not var.FIRST_DAY: for child in get_all_players(("wild child",)): if child not in IDOLS: players = get_players() players.remove(child) if players: idol = random.choice(players) IDOLS[child] = idol child.send(messages["wild_child_random_idol"].format(idol)) idol_role = get_main_role(idol) debuglog("{0} (wild child) IDOLIZE RANDOM: {1} ({2})".format(child, idol, idol_role))
def on_transition_night_end(evt, var): ps = get_players() for hunter in get_all_players(("hunter",)): if hunter in HUNTERS: continue # already killed pl = ps[:] random.shuffle(pl) pl.remove(hunter) to_send = "hunter_notify" if hunter.prefers_simple(): to_send = "hunter_simple" hunter.send(messages[to_send], messages["players_list"].format(", ".join(p.nick for p in pl)), sep="\n")
def on_transition_night_end(evt, var): ps = get_players() for mm in get_all_players(("matchmaker",)): if mm in MATCHMAKERS and not var.ALWAYS_PM_ROLE: continue pl = ps[:] random.shuffle(pl) if mm.prefers_simple(): mm.send(messages["matchmaker_simple"]) else: mm.send(messages["matchmaker_notify"]) mm.send("Players: " + ", ".join(p.nick for p in pl))
def on_transition_day_resolve_end(evt, var, victims): for bodyguard in DYING: evt.data["message"][bodyguard].clear() DYING.clear() for bodyguard in get_all_players(("bodyguard",)): if GUARDED.get(bodyguard) in get_players(Wolf) and bodyguard not in evt.data["dead"]: r = random.random() if r < var.BODYGUARD_DIES_CHANCE: if var.ROLE_REVEAL == "on": evt.data["message"][bodyguard].append(messages["bodyguard_protected_wolf"].format(bodyguard)) else: # off and team evt.data["message"][bodyguard].append(messages["bodyguard_protection"].format(bodyguard)) evt.data["dead"].append(bodyguard)
def on_lynch(evt, var, votee, voters): global VOTED if votee in get_all_players(("fool",)): # ends game immediately, with fool as only winner # hardcode "fool" as the role since game is ending due to them being lynched, # so we want to show "fool" even if it's a template lmsg = random.choice(messages["lynch_reveal"]).format(votee, "", "fool") VOTED = votee channels.Main.send(lmsg) from src.wolfgame import chk_win chk_win(winner="fool") evt.prevent_default = True evt.stop_processing = True
def on_transition_night_end(evt, var): for turncoat in get_all_players(("turncoat",)): # they start out as unsided, but can change n1 if turncoat not in TURNCOATS: TURNCOATS[turncoat] = ("none", -1) if turncoat.prefers_simple(): turncoat.send(messages["turncoat_simple"].format(TURNCOATS[turncoat][0])) else: message = messages["turncoat_notify"] if TURNCOATS[turncoat][0] != "none": message += messages["turncoat_current_team"].format(TURNCOATS[turncoat][0]) else: message += messages["turncoat_no_team"] turncoat.send(message)
def on_chk_nightdone(evt, var): nevt = Event("wolf_numkills", {"numkills": 1, "message": ""}) nevt.dispatch(var) num_kills = nevt.data["numkills"] wofls = [ x for x in get_all_players(Wolf & Killer) if not is_silent(var, x) ] if not num_kills or not wofls: return fake = users.FakeUser.from_nick("@WolvesAgree@") evt.data["nightroles"].extend(wofls) evt.data["acted"].extend(KILLS) evt.data["nightroles"].append(fake) kills = set() for ls in KILLS.values(): kills.update(ls) # check if wolves are actually agreeing if len(kills) == num_kills: evt.data["acted"].append(fake)
def on_transition_night_end(evt, var): for ass in get_all_players(("assassin",)): if ass in TARGETED: continue # someone already targeted pl = get_players() random.shuffle(pl) pl.remove(ass) ass_evt = Event("assassin_target", {"target": None}) ass_evt.dispatch(var, ass, pl) if ass_evt.data["target"] is not None: TARGETED[ass] = ass_evt.data["target"] PREV_ACTED.add(ass) else: if ass.prefers_simple(): ass.send(messages["assassin_simple"]) else: ass.send(messages["assassin_notify"]) ass.send(messages["players_list"].format(", ".join(p.nick for p in pl)))
def investigate(var, wrapper, message): """Investigate a player to determine their exact role.""" if wrapper.source in INVESTIGATED: wrapper.send(messages["already_investigated"]) return target = get_target(var, wrapper, re.split(" +", message)[0], not_self_message="no_investigate_self") if target is None: return target = try_misdirection(var, wrapper.source, target) if try_exchange(var, wrapper.source, target): return targrole = get_main_role(target) evt = Event("investigate", {"role": targrole}) evt.dispatch(var, wrapper.source, target) targrole = evt.data["role"] INVESTIGATED.add(wrapper.source) wrapper.send(messages["investigate_success"].format(target, targrole)) debuglog("{0} (detective) ID: {1} ({2})".format(wrapper.source, target, targrole)) if random.random() < var.DETECTIVE_REVEALED_CHANCE: # a 2/5 chance (should be changeable in settings) # The detective's identity is compromised! wcroles = Wolfchat if var.RESTRICT_WOLFCHAT & var.RW_REM_NON_WOLVES: if var.RESTRICT_WOLFCHAT & var.RW_TRAITOR_NON_WOLF: wcroles = Wolf else: wcroles = Wolf | {"traitor"} wolves = get_all_players(wcroles) if wolves: for wolf in wolves: wolf.queue_message(messages["detective_reveal"].format(wrapper.source)) wolf.send_messages() debuglog("{0} (detective) PAPER DROP".format(wrapper.source))
def on_del_player(evt, var, user, mainrole, allroles, death_triggers): global ALL_SUCC_IDLE if "succubus" not in allroles: return if user in VISITED: # if it's night, also unentrance the person they visited if var.PHASE == "night" and var.GAMEPHASE == "night": if VISITED[user] in ENTRANCED: ENTRANCED.discard(VISITED[user]) VISITED[user].send(messages["entranced_revert_win"]) del VISITED[user] # if all succubi idled out (every last one of them), un-entrance people # death_triggers is False for an idle-out, so we use that to determine which it is if death_triggers: ALL_SUCC_IDLE = False if not get_all_players(("succubus",)): entranced_alive = ENTRANCED.difference(evt.params.deadlist).intersection(evt.data["pl"]) if ALL_SUCC_IDLE: while ENTRANCED: e = ENTRANCED.pop() e.send(messages["entranced_revert_win"])
def on_transition_night_begin(evt, var): global STATS_FLAG if var.NIGHT_COUNT == var.AMNESIAC_NIGHTS: amnesiacs = get_all_players(("amnesiac", )) if amnesiacs and not var.HIDDEN_AMNESIAC: STATS_FLAG = True for amn in amnesiacs: role = ROLES[amn] change_role(amn, "amnesiac", role) evt = Event("new_role", {}) evt.dispatch(var, amn, role) if var.FIRST_NIGHT: # we don't need to tell them twice if they remember right away continue showrole = role if showrole in var.HIDDEN_VILLAGERS: showrole = "villager" elif showrole in var.HIDDEN_ROLES: showrole = var.DEFAULT_ROLE a = "a" if showrole.startswith(("a", "e", "i", "o", "u")): a = "an" amn.send(messages["amnesia_clear"].format(a, showrole)) if is_known_wolf_ally(amn, amn): if role in var.WOLF_ROLES: relay_wolfchat_command(amn.client, amn.nick, messages["amnesia_wolfchat"].format( amn, showrole), var.WOLF_ROLES, is_wolf_command=True, is_kill_command=True) else: relay_wolfchat_command( amn.client, amn.nick, messages["amnesia_wolfchat"].format(amn, showrole), var.WOLFCHAT_ROLES) debuglog("{0} REMEMBER: {1} as {2}".format(amn, role, showrole))
def curse(var, wrapper, message): target = get_target(var, wrapper, re.split(" +", message)[0]) if not target: return if target in get_all_players(("cursed villager", )): wrapper.pm(messages["target_already_cursed"].format(target)) return # There may actually be valid strategy in cursing other wolfteam members, # but for now it is not allowed. If someone seems suspicious and shows as # villager across multiple nights, safes can use that as a tell that the # person is likely wolf-aligned. if is_known_wolf_ally(var, wrapper.source, target): wrapper.pm(messages["no_curse_wolf"]) return evt = Event("targeted_command", { "target": target, "exchange": True, "misdirection": True }) if not evt.dispatch(var, wrapper.source, target): return CURSED[wrapper.source] = evt.data["target"] PASSED.discard(wrapper.source) wrapper.pm(messages["curse_success"].format(target)) send_wolfchat_message(var, wrapper.source, messages["curse_success_wolfchat"].format( wrapper.source, target), {"warlock"}, role="warlock", command="curse") debuglog("{0} (warlock) CURSE: {1} ({2})".format( wrapper.source, evt.data["target"], get_main_role(evt.data["target"])))
def on_transition_night_end(evt, var): wolves = get_all_players(Wolfchat) # roles allowed to talk in wolfchat talkroles = get_talking_roles(var) # condition imposed on talking in wolfchat (only during day/night, or no talking) # 0 = no talking # 1 = normal # 2 = only during day # 3 = only during night wccond = 1 if var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT: if var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY: wccond = 0 else: wccond = 2 elif var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY: wccond = 3 for wolf in wolves: can_talk = len(get_all_roles(wolf) & talkroles) > 0 if len(wolves) > 1 and can_talk and wccond > 0: wolf.send(messages["wolfchat_notify_{0}".format(wccond)])
def on_del_player(evt, var, player, all_roles, death_triggers): global ALL_SUCC_IDLE if "succubus" not in all_roles: return if player in VISITED: # if it's night, also unentrance the person they visited if var.PHASE == "night" and var.GAMEPHASE == "night": if VISITED[player] in ENTRANCED: ENTRANCED.discard(VISITED[player]) VISITED[player].send(messages["entranced_revert_win"]) del VISITED[player] PASSED.discard(player) FORCE_PASSED.discard(player) # if all succubi idled out (every last one of them), un-entrance people # death_triggers is False for an idle-out, so we use that to determine which it is if death_triggers: ALL_SUCC_IDLE = False if ALL_SUCC_IDLE and not get_all_players(("succubus", )): while ENTRANCED: e = ENTRANCED.pop() e.send(messages["entranced_revert_win"])
def prolong_night(self, var, gameid, transition_day): nspecials = len(get_all_players(("seer", "harlot", "shaman", "crazed shaman"))) rand = random.gauss(5, 1.5) if rand <= 0 and nspecials > 0: transition_day(gameid=gameid) else: # rejig the night ending timer to cleanup what we're doing # NOTE: this relies on implementation details in CPython and may not work across python versions # or with other python implementations. If this becomes problematic, we may want to use our own timer impl # rather than rely on threading.Timer; we can mostly just lift what CPython has for that if "night" in self.saved_timers: oldid = self.saved_timers["night"][0].kwargs["gameid"] self.saved_timers["night"][0].kwargs = { "var": var, "gameid": oldid, "transition_day": transition_day } self.saved_timers["night"][0].function = self.prolong_night_end # bootstrap villagergame delay timer current = time.time() delay = abs(rand) t = threading.Timer(delay, self.prolong_night_end, kwargs={"var": var, "gameid": gameid, "transition_day": transition_day}) self.saved_timers["villagergame"] = (t, current, delay) # restart saved timers (they were cancelled before prolong_night was called) for name, t in self.saved_timers.items(): elapsed = current - t[1] remaining = t[2] - elapsed if remaining > 0: new_timer = threading.Timer(remaining, t[0].function, t[0].args, t[0].kwargs) var.TIMERS[name] = (new_timer, t[1], t[2]) new_timer.daemon = True new_timer.start() self.saved_timers = {}
def on_del_player(evt, var, player, all_roles, death_triggers): # clone happens regardless of death_triggers being true or not if var.PHASE not in var.GAME_PHASES: return clones = get_all_players(("clone", )) mainrole = evt.params.main_role for clone in clones: if clone in CLONED: target = CLONED[clone] if player is target: # clone is cloning target, so clone becomes target's role # clone does NOT get any of target's templates (gunner/assassin/etc.) del CLONED[clone] mainrole = change_role(var, clone, "clone", mainrole, inherit_from=target) # if a clone is cloning a clone, clone who the old clone cloned if mainrole == "clone" and player in CLONED: if CLONED[player] is clone: clone.send(messages["forever_aclone"].format(player)) else: CLONED[clone] = CLONED[player] clone.send(messages["clone_success"].format( CLONED[clone])) debuglog("{0} (clone) CLONE: {1} ({2})".format( clone, CLONED[clone], get_main_role(CLONED[clone]))) debuglog("{0} (clone) CLONE DEAD PLAYER: {1} ({2})".format( clone, target, mainrole)) del CLONED[:player:] CAN_ACT.discard(player) ACTED.discard(player)
def on_transition_night_end(evt, var): ps = get_players() for gangel in get_all_players(("guardian angel", )): pl = ps[:] random.shuffle(pl) gself = messages["guardian_self_notification"] if not var.GUARDIAN_ANGEL_CAN_GUARD_SELF: pl.remove(gangel) gself = "" if gangel in LASTGUARDED: if LASTGUARDED[gangel] in pl: pl.remove(LASTGUARDED[gangel]) chance = math.floor(var.GUARDIAN_ANGEL_DIES_CHANCE * 100) warning = "" if chance > 0: warning = messages["bodyguard_death_chance"].format(chance) to_send = "guardian_angel_notify" if gangel.prefers_simple(): to_send = "guardian_angel_simple" gangel.send(messages[to_send].format(warning, gself), messages["players_list"].format(", ".join(p.nick for p in pl)), sep="\n")
def on_transition_day(evt, var): # figure out wolf target found = defaultdict(int) nevt = Event("wolf_numkills", {"numkills": 1}) nevt.dispatch(var) num_kills = nevt.data["numkills"] for v in KILLS.values(): for p in v: 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: target = random.choice(dups) evt.data["victims"].append(target) evt.data["bywolves"].add(target) evt.data["onlybywolves"].add(target) # special key to let us know to randomly select a wolf in case of retribution totem evt.data["killers"][target].append("@wolves") del found[target] # when monster is split, add protection to them if in onlybywolves # fallen angel will then remove that protection # TODO: when monster is split off if var.ROLES["fallen angel"]: for monster in get_all_players(("monster",)): if monster in evt.data["victims"]: evt.data["victims"].remove(monster) evt.data["bywolves"].discard(monster) evt.data["onlybywolves"].discard(monster)
def on_transition_day_begin(evt, var): # Select random totem recipients if shamans didn't act pl = get_players() for shaman in get_all_players(("wolf shaman", )): if is_silent(var, shaman): continue ps = pl[:] for given in itertools.chain.from_iterable(LASTGIVEN[shaman].values()): if given in ps: ps.remove(given) for given in itertools.chain.from_iterable(SHAMANS[shaman].values()): if given in ps: ps.remove(given) for totem, count in TOTEMS[shaman].items(): mustgive = count - len(SHAMANS[shaman][totem]) for i in range(mustgive): if ps: target = random.choice(ps) ps.remove(target) dispatcher = MessageDispatcher(shaman, shaman) given = give_totem(var, dispatcher, target, totem, key="shaman_success_random_known", role="wolf shaman") if given: send_wolfchat_message( var, shaman, messages["shaman_wolfchat"].format(shaman, target), ("wolf shaman", ), role="wolf shaman", command="totem") SHAMANS[shaman][totem].append(given[0])
def on_transition_night_end(evt, var): for child in get_all_players(("wild child", )): to_send = "child_notify" if child.prefers_simple(): to_send = "child_simple" child.send(messages[to_send])
def on_chk_nightdone(evt, var): if var.FIRST_NIGHT: evt.data["actedcount"] += len(IDOLS.keys()) evt.data["nightroles"].extend(get_all_players(("wild child", )))
def on_chk_nightdone(evt, var): if not ENABLED: return can_act = get_all_players(("alpha wolf",)) - ALPHAS evt.data["acted"].extend(BITTEN) evt.data["nightroles"].extend(can_act)
def on_harlot_visit(evt, var, harlot, victim): if victim in get_all_players(("succubus",)): harlot.send(messages["notify_succubus_target"].format(victim)) victim.send(messages["succubus_harlot_success"].format(harlot)) ENTRANCED.add(harlot)
def on_chk_nightdone(evt, var): evt.data["actedcount"] += len(VISITED) + len(PASSED) evt.data["nightroles"].extend(get_all_players(("succubus",)))
def on_chk_nightdone(evt, var): evt.data["actedcount"] += len(CURSED) + len(PASSED) evt.data["nightroles"].extend(get_all_players(("warlock", )))
def on_chk_nightdone(evt, var): evt.data["nightroles"].extend(get_all_players(("prophet", ))) evt.data["actedcount"] += len(PRAYED)
def on_try_protection(evt, var, target, attacker, attacker_role, reason): if attacker_role == "wolf" and get_all_players(("fallen angel",)): status.remove_all_protections(var, target, attacker=None, attacker_role="fallen angel", reason="fallen_angel") evt.prevent_default = True
def on_succubus_visit(evt, var, succubus, target): if target in KILLS and KILLS[target] in get_all_players(("succubus",)): target.send(messages["no_kill_succubus"].format(KILLS[target])) del KILLS[target]
def on_chk_nightdone(evt, var): evt.data["actedcount"] += len(KILLS) + len(PASSED) evt.data["nightroles"].extend(get_all_players(("vigilante",)))
def on_chk_nightdone(evt, var): evt.data["acted"].extend(OBSERVED) evt.data["nightroles"].extend(get_all_players(("werecrow", )))
def on_transition_night_end(evt, var): for demoniac in get_all_players(("demoniac", )): if demoniac.prefers_simple(): demoniac.send(messages["demoniac_simple"]) else: demoniac.send(messages["demoniac_notify"])
def pray(var, wrapper, message): """Receive divine visions of who has a role.""" if wrapper.source in PRAYED: wrapper.pm(messages["already_prayed"]) return what = re.split(" +", message)[0] if not what: wrapper.pm(messages["not_enough_parameters"]) return # complete this as a match with other roles (so "cursed" can match "cursed villager" for instance) role = complete_one_match( what.lower(), {p for p in var.ROLE_GUIDE if p not in var.TEMPLATE_RESTRICTIONS}) if role is None and what.lower() in var.ROLE_ALIASES: role = var.ROLE_ALIASES[what.lower()] if role in var.TEMPLATE_RESTRICTIONS: # allow only main roles role = None if role is None: # typo, let them fix it wrapper.pm(messages["specific_invalid_role"].format(what)) return # get a list of all roles actually in the game, including roles that amnesiacs will be turning into # (amnesiacs are special since they're also listed as amnesiac; that way a prophet can see both who the # amnesiacs themselves are as well as what they'll become) pl = get_players() from src.roles.amnesiac import ROLES as amn_roles valid_roles = {r for p, r in amn_roles.items() if p in pl}.union(var.MAIN_ROLES.values()) PRAYED.add(wrapper.source) if role in valid_roles: # this sees through amnesiac, so the amnesiac's final role counts as their role # also, if we're the only person with that role, say so people = set(get_all_players((role, ))) | { p for p, r in amn_roles.items() if p in pl and r == role } if len(people) == 1 and wrapper.source in people: wrapper.pm(messages["vision_only_role_self"].format(role)) PRAYED.add(wrapper.source) debuglog("{0} (prophet) PRAY {1} - ONLY".format( wrapper.source, role)) return target = random.choice(list(people)) part = random.sample([p for p in pl if p is not wrapper.source], len(pl) // 3) if target not in part: part[0] = target random.shuffle(part) part = [p.nick for p in part] an = "" if role.startswith(("a", "e", "i", "o", "u")): an = "n" key = "vision_players" if len(part) == 1: key = "vision_role" if len(part) > 2: msg = "{0}, and {1}".format(", ".join(part[:-1]), part[-1]) else: msg = " and ".join(part) wrapper.pm(messages[key].format(role, an, msg)) debuglog("{0} (prophet) PRAY {1} ({2})".format(wrapper.source, role, target)) else: # role is not in this game, this still counts as a successful activation of the power! wrapper.pm(messages["vision_none"].format(plural(role))) debuglog("{0} (prophet) PRAY {1} - NONE".format(wrapper.source, role))
def on_transition_night_end(evt, var): for pht in get_all_players(("prophet", )): if pht.prefers_simple(): pht.send(messages["prophet_simple"]) else: pht.send(messages["prophet_notify"])
def on_visit(evt, var, visitor_role, visitor, visited): if visitor_role == "succubus": succubi = get_all_players(("succubus", )) if visited in TARGETS and TARGETS[visited].intersection(succubi): TARGETS[visited].difference_update(succubi) visited.send(messages["dullahan_no_kill_succubus"])
def on_chk_nightdone(evt, var): evt.data["actedcount"] += len(KILLS) + len(PASSED) hunter_users = get_all_players(("hunter", )) evt.data["nightroles"].extend( [p for p in hunter_users if p not in HUNTERS or p in KILLS])
def on_chk_nightdone(evt, var): evt.data["actedcount"] += len(HEXED) evt.data["nightroles"].extend(get_all_players(("hag", )))