def on_transition_day_resolve_end(evt, var, victims): for victim in list(evt.data["dead"]): if GUNNERS.get(victim) and "@wolves" in evt.data["killers"][victim]: if random.random() < var.GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE: # pick a random wolf to be shot wolfset = [wolf for wolf in get_players(Wolf) if wolf not in evt.data["dead"]] if wolfset: deadwolf = random.choice(wolfset) if var.ROLE_REVEAL in ("on", "team"): evt.data["message"][victim].append(messages["gunner_killed_wolf_overnight"].format(victim, deadwolf, get_reveal_role(deadwolf))) else: evt.data["message"][victim].append(messages["gunner_killed_wolf_overnight_no_reveal"].format(victim, deadwolf)) evt.data["dead"].append(deadwolf) evt.data["killers"][deadwolf].append(victim) GUNNERS[victim] -= 1 # deduct the used bullet if var.WOLF_STEALS_GUN and GUNNERS[victim]: # might have used up the last bullet or something possible = get_players(Wolfchat) random.shuffle(possible) for looter in possible: if looter not in evt.data["dead"]: break else: return # no live wolf, nothing to do here GUNNERS[looter] = GUNNERS.get(looter, 0) + 1 del GUNNERS[victim] var.ROLES[rolename].add(looter) looter.send(messages["wolf_gunner"].format(victim))
def on_player_win(evt, var, player, role, winner, survived): from src.roles.fool import VOTED if player in LOVERS: evt.data["special"].append("lover") if winner == "lovers" and player in LOVERS: evt.data["iwon"] = True elif player in LOVERS and survived and LOVERS[player].intersection(get_players()): for lvr in LOVERS[player]: if lvr not in get_players(): # cannot win with dead lover (lover idled out) continue lover_role = get_main_role(lvr) if singular(winner) not in Win_Stealer: evt.data["iwon"] = True break elif winner == "fool": if lvr is VOTED: evt.data["iwon"] = True break elif singular(winner) in Win_Stealer and lover_role == singular(winner): evt.data["iwon"] = True break
def on_transition_night_end(evt, var): chances = var.CURRENT_GAMEMODE.TOTEM_CHANCES max_totems = sum(x["wolf shaman"] for x in chances.values()) ps = get_players() shamans = get_players(("wolf shaman",)) for s in list(LASTGIVEN): if s not in shamans: del LASTGIVEN[s] for shaman in shamans: pl = ps[:] random.shuffle(pl) if LASTGIVEN.get(shaman): if LASTGIVEN[shaman] in pl: pl.remove(LASTGIVEN[shaman]) target = 0 rand = random.random() * max_totems for t in chances: target += chances[t]["wolf shaman"] if rand <= target: TOTEMS[shaman] = t break if shaman.prefers_simple(): # Message about role was sent with wolfchat shaman.send(messages["totem_simple"].format(TOTEMS[shaman])) else: totem = TOTEMS[shaman] tmsg = messages["shaman_totem"].format(totem) tmsg += messages[totem + "_totem"] shaman.send(tmsg)
def on_new_role(evt, var, player, old_role): wcroles = get_wolfchat_roles(var) if old_role is None: # initial role assignment; don't do all the logic below about notifying other wolves and such if evt.data["role"] in wcroles: evt.data["in_wolfchat"] = True return sayrole = evt.data["role"] if sayrole in Hidden: sayrole = var.HIDDEN_ROLE an = "n" if sayrole.startswith(("a", "e", "i", "o", "u")) else "" if player in KILLS: del KILLS[player] if old_role not in wcroles and evt.data["role"] in wcroles: # a new wofl has joined the party, give them tummy rubs and the wolf list # and let the other wolves know to break out the confetti and villager steaks wofls = get_players(wcroles) evt.data["in_wolfchat"] = True if wofls: new_wolves = [] for wofl in wofls: wofl.queue_message(messages["wolfchat_new_member"].format(player, an, sayrole)) wofl.send_messages() else: return # no other wolves, nothing else to do pl = get_players() if player in pl: pl.remove(player) random.shuffle(pl) pt = [] wevt = Event("wolflist", {"tags": set()}) for p in pl: prole = get_main_role(p) wevt.data["tags"].clear() wevt.dispatch(var, p, player) tags = " ".join(wevt.data["tags"]) if prole in wcroles: if tags: tags += " " pt.append("\u0002{0}\u0002 ({1}{2})".format(p, tags, prole)) elif tags: pt.append("{0} ({1})".format(p, tags)) else: pt.append(p.nick) evt.data["messages"].append(messages["players_list"].format(", ".join(pt))) if var.PHASE == "night" and evt.data["role"] in Wolf & Killer: # inform the new wolf that they can kill and stuff nevt = Event("wolf_numkills", {"numkills": 1, "message": ""}) nevt.dispatch(var) if not nevt.data["numkills"] and nevt.data["message"]: evt.data["messages"].append(messages[nevt.data["message"]])
def on_begin_day(evt, var): # Refund failed bites for alpha, target in BITTEN.items(): if alpha in get_players() and target not in get_players(Wolf): alpha.send(messages["alpha_bite_failure"].format(target)) else: alpha.send(messages["alpha_bite_success"].format(target)) ALPHAS.add(alpha) BITTEN.clear()
def on_swap_role_state(evt, var, actor, target, role): if role == "wild child": IDOLS[actor], IDOLS[target] = IDOLS[target], IDOLS[actor] if IDOLS[actor] in get_players(): evt.data["actor_messages"].append(messages["wild_child_idol"].format(IDOLS[actor])) else: # The King is dead, long live the King! change_role(var, actor, "wild child", "wolf", message="wild_child_idol_died") var.ROLES["wild child"].add(actor) if IDOLS[target] in get_players(): evt.data["target_messages"].append(messages["wild_child_idol"].format(IDOLS[target])) else: change_role(var, target, "wild child", "wolf", message="wild_child_idol_died") var.ROLES["wild child"].add(target)
def transition_day(self, evt, var): # 30% chance we kill a safe, otherwise kill at random # when killing safes, go after seer, then harlot, then shaman pl = get_players() tgt = None seer = None hlt = None hvst = None shmn = None if len(var.ROLES["seer"]) == 1: seer = list(var.ROLES["seer"])[0] if len(var.ROLES["harlot"]) == 1: hlt = list(var.ROLES["harlot"])[0] from src.roles import harlot hvst = harlot.VISITED.get(hlt) if hvst is not None: pl.remove(hlt) if len(var.ROLES["shaman"]) == 1: shmn = list(var.ROLES["shaman"])[0] if random.random() < 0.3: if seer: tgt = seer elif hvst: tgt = hvst elif shmn: tgt = shmn elif hlt and not hvst: tgt = hlt if not tgt: tgt = random.choice(pl) from src.roles.helper import wolves wolves.KILLS[users.Bot] = [tgt]
def on_del_player(evt, var, player, all_roles, death_triggers): for h, v in list(KILLS.items()): if v is player: h.send(messages["hunter_discard"]) del KILLS[h] elif h is player: del KILLS[h] if death_triggers and "dullahan" in all_roles: pl = get_players() with TARGETS[player].intersection(pl) as targets: if targets: target = random.choice(list(targets)) protected = try_protection(var, target, player, "dullahan", "dullahan_die") if protected is not None: channels.Main.send(*protected) return if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(target) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" channels.Main.send(messages["dullahan_die_success"].format(player, target, an, role)) else: channels.Main.send(messages["dullahan_die_success_noreveal"].format(player, target)) debuglog("{0} (dullahan) DULLAHAN ASSASSINATE: {1} ({2})".format(player, target, get_main_role(target))) add_dying(var, target, "dullahan", "dullahan_die")
def send_wolfchat_message(var, user, message, roles, *, role=None, command=None): if role not in Wolf & Killer and var.RESTRICT_WOLFCHAT & var.RW_NO_INTERACTION: return if command not in _kill_cmds and var.RESTRICT_WOLFCHAT & var.RW_ONLY_KILL_CMD: if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT: return if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY: return if not is_known_wolf_ally(var, user, user): return wcroles = get_wolfchat_roles(var) if var.RESTRICT_WOLFCHAT & var.RW_ONLY_SAME_CMD: if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT: wcroles = roles if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY: wcroles = roles wcwolves = get_players(wcroles) wcwolves.remove(user) player = None for player in wcwolves: player.queue_message(message) for player in var.SPECTATING_WOLFCHAT: player.queue_message("[wolfchat] " + message) if player is not None: player.send_messages()
def on_gun_chances(evt, var, user, role): if user in get_players(get_wolfchat_roles(var)): hit, miss, headshot = var.WOLF_GUN_CHANCES evt.data["hit"] = hit evt.data["miss"] = miss evt.data["headshot"] = headshot evt.stop_processing = True
def vg_kill(var, wrapper, message): """Take revenge on someone each night after you die.""" if GHOSTS[wrapper.source][0] == "!": return target = get_target(var, wrapper, re.split(" +", message)[0]) if not target: return if target is wrapper.source: wrapper.pm(messages["player_dead"]) return wolves = get_players(var.WOLFTEAM_ROLES) if GHOSTS[wrapper.source] == "wolves" and target not in wolves: wrapper.pm(messages["vengeful_ghost_wolf"]) return elif GHOSTS[wrapper.source] == "villagers" and target in wolves: wrapper.pm(messages["vengeful_ghost_villager"]) return orig = target evt = Event("targeted_command", {"target": target.nick, "misdirection": True, "exchange": False}) evt.dispatch(wrapper.source.client, var, "kill", wrapper.source.nick, target.nick, frozenset({"detrimental"})) if evt.prevent_default: return target = users._get(evt.data["target"]) # FIXME KILLS[wrapper.source] = target wrapper.pm(messages["player_kill"].format(orig)) debuglog("{0} (vengeful ghost) KILL: {1} ({2})".format(wrapper.source.nick, target, get_main_role(target))) chk_nightdone(wrapper.source.client)
def consecrate(var, wrapper, message): """Consecrates a corpse, putting its spirit to rest and preventing other unpleasant things from happening.""" alive = get_players() targ = re.split(" +", message)[0] if not targ: wrapper.pm(messages["not_enough_parameters"]) return dead = set(var.ALL_PLAYERS) - set(alive) target, _ = users.complete_match(targ, dead) if target is None: wrapper.pm(messages["consecrate_fail"].format(targ)) return # we have a target, so mark them as consecrated, right now all this does is silence a VG for a night # but other roles that do stuff after death or impact dead players should have functionality here as well # (for example, if there was a role that could raise corpses as undead somethings, this would prevent that from working) # regardless if this has any actual effect or not, it still removes the priest from being able to vote evt = Event("consecrate", {}) evt.dispatch(var, wrapper.source, target) wrapper.pm(messages["consecrate_success"].format(target)) debuglog("{0} (priest) CONSECRATE: {1}".format(wrapper.source, target)) add_absent(var, wrapper.source, "consecrating") from src.votes import chk_decision from src.wolfgame import chk_win if not chk_win(): # game didn't immediately end due to marking as absent, see if we should force through a lynch chk_decision(var)
def send_wolfchat_message(var, user, message, roles, *, role=None, command=None): if role not in CAN_KILL and var.RESTRICT_WOLFCHAT & var.RW_NO_INTERACTION: return if command not in _kill_cmds and var.RESTRICT_WOLFCHAT & var.RW_ONLY_KILL_CMD: if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT: return if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY: return if not is_known_wolf_ally(var, user, user): return wcroles = var.WOLFCHAT_ROLES if var.RESTRICT_WOLFCHAT & var.RW_ONLY_SAME_CMD: if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT: wcroles = roles if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY: wcroles = roles elif var.RESTRICT_WOLFCHAT & var.RW_REM_NON_WOLVES: if var.RESTRICT_WOLFCHAT & var.RW_TRAITOR_NON_WOLF: wcroles = var.WOLF_ROLES else: wcroles = var.WOLF_ROLES | {"traitor"} wcwolves = get_players(wcroles) wcwolves.remove(user) player = None for player in wcwolves: player.queue_message(message) for player in var.SPECTATING_WOLFCHAT: player.queue_message("[wolfchat] " + message) if player is not None: player.send_messages()
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 vg_kill(var, wrapper, message): """Take revenge on someone each night after you die.""" if GHOSTS[wrapper.source][0] == "!": return target = get_target(var, wrapper, re.split(" +", message)[0]) if not target: return if target is wrapper.source: wrapper.pm(messages["player_dead"]) return wolves = get_players(Wolfteam) if GHOSTS[wrapper.source] == "wolf" and target not in wolves: wrapper.pm(messages["vengeful_ghost_wolf"]) return elif GHOSTS[wrapper.source] == "villager" and target in wolves: wrapper.pm(messages["vengeful_ghost_villager"]) return orig = target target = try_misdirection(var, wrapper.source, target) KILLS[wrapper.source] = target wrapper.pm(messages["player_kill"].format(orig)) debuglog("{0} (vengeful ghost) KILL: {1} ({2})".format( wrapper.source, target, get_main_role(target)))
def on_revealroles(evt, var, wrapper): # print out lovers pl = get_players() done = {} lovers = [] for lover1, lset in LOVERS.items(): if lover1 not in pl: continue for lover2 in lset: # check if already said the pairing if (lover1 in done and lover2 in done[lover1]) or ( lover2 in done and lover1 in done[lover2]): continue if lover2 not in pl: continue lovers.append("{0}/{1}".format(lover1, lover2)) if lover1 in done: done[lover1].append(lover2) else: done[lover1] = [lover2] if len(lovers) == 1 or len(lovers) == 2: evt.data["output"].append("\u0002lovers\u0002: {0}".format( " and ".join(lovers))) elif len(lovers) > 2: evt.data["output"].append("\u0002lovers\u0002: {0}, and {1}".format( ", ".join(lovers[0:-1]), lovers[-1]))
def on_transition_day(evt, var): # now that all protections are finished, add people back to onlybywolves # if they're down to 1 active kill and wolves were a valid killer victims = set(get_players()) & set(evt.data["victims"]) - var.DYING for v in victims: if evt.data["numkills"][v] == 1 and v in evt.data["bywolves"]: evt.data["onlybywolves"].add(v) if len(var.ROLES["fallen angel"]) > 0: for p, t in list(evt.data["protected"].items()): if p in evt.data["bywolves"]: if p in evt.data["protected"]: p.send(messages["fallen_angel_deprotect"]) # let other roles do special things when we bypass their guards killer = random.choice( list(get_all_players(("fallen angel", )))) fevt = Event("fallen_angel_guard_break", evt.data) fevt.dispatch(var, p, killer) if p in evt.data["protected"]: del evt.data["protected"][p] if p in var.ACTIVE_PROTECTIONS: del var.ACTIVE_PROTECTIONS[p.nick] # mark kill as performed by a random FA # this is important as there may otherwise be no killers if every kill was blocked evt.data["killers"][p].append(killer)
def dullahan_kill(var, wrapper, message): """Kill someone at night as a dullahan until everyone on your list is dead.""" if not TARGETS[wrapper.source] & set(get_players()): wrapper.pm(messages["dullahan_targets_dead"]) return target = get_target(var, wrapper, re.split(" +", message)[0]) if not target: return if target is wrapper.source: wrapper.pm(messages["no_suicide"]) return orig = target evt = Event("targeted_command", {"target": target.nick, "misdirection": True, "exchange": True}) evt.dispatch(wrapper.client, var, "kill", wrapper.source.nick, target.nick, frozenset({"detrimental"})) if evt.prevent_default: return target = users._get(evt.data["target"]) # FIXME: Need to fix once targeted_command uses the new API KILLS[wrapper.source] = target wrapper.pm(messages["player_kill"].format(orig)) debuglog("{0} (dullahan) KILL: {1} ({2})".format(wrapper.source, target, get_role(target.nick))) chk_nightdone(wrapper.client)
def happy_fun_times(self, evt, var, player, all_roles, death_triggers): if death_triggers and evt.params.main_role == "priest": channels.Main.send(messages["sleepy_priest_death"]) turn_chance = 3 / 4 mapping = { "seer": "doomsayer", "harlot": "succubus", "cultist": "demoniac" } for old, new in mapping.items(): turn = [ p for p in get_players((old, )) if random.random() < turn_chance ] for t in turn: # messages: sleepy_doomsayer_turn, sleepy_succubus_turn, sleepy_demoniac_turn change_role(var, t, old, new, message="sleepy_{0}_turn".format(new)) newstats = set() for rs in var.ROLE_STATS: d = Counter(dict(rs)) newstats.add(rs) if old in d and d[old] >= 1: d[old] -= 1 d[new] += 1 newstats.add(frozenset(d.items())) var.ROLE_STATS = frozenset(newstats)
def on_transition_day(evt, var): pl = get_players() vs = set(evt.data["victims"]) for v in pl: if v in vs: if v in var.DYING: continue for g in get_all_players(("guardian angel", )): if GUARDED.get(g.nick) == v.nick: 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] = "angel" elif evt.data["numkills"][v] <= 0: var.ACTIVE_PROTECTIONS[v.nick].append("angel") for g in get_all_players(("bodyguard", )): if GUARDED.get(g.nick) == v.nick: 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] = "bodyguard" elif evt.data["numkills"][v] <= 0: var.ACTIVE_PROTECTIONS[v.nick].append("bodyguard") else: for g in var.ROLES["guardian angel"]: if GUARDED.get(g.nick) == v.nick: var.ACTIVE_PROTECTIONS[v.nick].append("angel") for g in var.ROLES["bodyguard"]: if GUARDED.get(g.nick) == v.nick: var.ACTIVE_PROTECTIONS[v.nick].append("bodyguard")
def get_lovers(): lovers = [] pl = get_players() for lover in LOVERS: done = None for i, lset in enumerate(lovers): if lover in pl and lover in lset: if done is not None: # plot twist! two clusters turn out to be linked! done.update(lset) for lvr in LOVERS[lover]: if lvr in pl: done.add(lvr) lset.clear() continue for lvr in LOVERS[lover]: if lvr in pl: lset.add(lvr) done = lset if done is None and lover in pl: lovers.append(set()) lovers[-1].add(lover) for lvr in LOVERS[lover]: if lvr in pl: lovers[-1].add(lvr) while set() in lovers: lovers.remove(set()) return lovers
def add_dying(var, player: User, killer_role: str, reason: str, *, death_triggers: bool = True) -> bool: """ Mark a player as dying. :param var: The game state :param player: The player to kill off :param killer_role: The role which is responsible for killing player; must be an actual role :param reason: The reason the player is being killed off, for stats tracking purposes :param death_triggers: Whether or not to run role logic that triggers on death; players who die due to quitting or idling out have this set to False :returns: True if the player was successfully marked as dying, or False if they are already dying or dead """ t = time.time() # ensure that the reaper thread doesn't smash things against the gameplay thread when running this # (eventually the reaper thread will just pass messages to the main thread via the asyncio event loop and these locks would therefore be unnecessary) with var.GRAVEYARD_LOCK: # FIXME if not var.GAME_ID or var.GAME_ID > t: # either game ended, or a new game has started return False if player in DYING or player not in get_players(): return False DYING[player] = (killer_role, reason, death_triggers) return True
def do_nightmare(self, var, target, night): if var.PHASE != "night" or var.NIGHT_COUNT != night: return if target not in get_players(): return self.having_nightmare.clear() self.having_nightmare.append(target) target.send(messages["sleepy_nightmare_begin"]) target.send(messages["sleepy_nightmare_navigate"]) self.correct = [None, None, None] self.fake1 = [None, None, None] self.fake2 = [None, None, None] directions = ["n", "e", "s", "w"] self.step = 0 self.prev_direction = None opposite = {"n": "s", "e": "w", "s": "n", "w": "e"} for i in range(3): corrdir = directions[:] f1dir = directions[:] f2dir = directions[:] if i > 0: corrdir.remove(opposite[self.correct[i - 1]]) f1dir.remove(opposite[self.fake1[i - 1]]) f2dir.remove(opposite[self.fake2[i - 1]]) else: corrdir.remove("s") f1dir.remove("s") f2dir.remove("s") self.correct[i] = random.choice(corrdir) self.fake1[i] = random.choice(f1dir) self.fake2[i] = random.choice(f2dir) self.prev_direction = "n" self.start_direction = "n" self.on_path = set() self.nightmare_step()
def wait(var, wrapper, message): """Increase the wait time until !start can be used.""" if wrapper.target is not channels.Main: return pl = get_players() with WAIT_LOCK: global WAIT_TOKENS, WAIT_LAST wait_check_time = time.time() WAIT_TOKENS += (wait_check_time - WAIT_LAST) / var.WAIT_TB_DELAY WAIT_LAST = wait_check_time WAIT_TOKENS = min(WAIT_TOKENS, var.WAIT_TB_BURST) now = datetime.now() if ((LAST_WAIT and wrapper.source in LAST_WAIT and LAST_WAIT[wrapper.source] + timedelta(seconds=var.WAIT_RATE_LIMIT) > now) or WAIT_TOKENS < 1): wrapper.pm(messages["command_ratelimited"]) return LAST_WAIT[wrapper.source] = now WAIT_TOKENS -= 1 if now > var.CAN_START_TIME: var.CAN_START_TIME = now + timedelta(seconds=var.EXTRA_WAIT) else: var.CAN_START_TIME += timedelta(seconds=var.EXTRA_WAIT) wrapper.send(messages["wait_time_increase"].format( wrapper.source, var.EXTRA_WAIT))
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 add_lycanthropy(var, target, prefix="lycan"): """Effect the target with lycanthropy. Fire the add_lycanthropy event.""" if target in LYCANTHROPES: return if target in get_players() and Event("add_lycanthropy", {}).dispatch(var, target): LYCANTHROPES[target] = prefix
def add_disease(var, target): """Effect the target with disease. Fire the add_disease event.""" if target in DISEASED or target not in get_players(): return if Event("add_disease", {}).dispatch(var, target): DISEASED.add(target)
def wait(var, wrapper, message): """Increase the wait time until !start can be used.""" if wrapper.target is not channels.Main: return pl = get_players() with WAIT_LOCK: global WAIT_TOKENS, WAIT_LAST wait_check_time = time.time() WAIT_TOKENS += (wait_check_time - WAIT_LAST) / var.WAIT_TB_DELAY WAIT_LAST = wait_check_time WAIT_TOKENS = min(WAIT_TOKENS, var.WAIT_TB_BURST) now = datetime.now() if ((LAST_WAIT and wrapper.source in LAST_WAIT and LAST_WAIT[wrapper.source] + timedelta(seconds=var.WAIT_RATE_LIMIT) > now) or WAIT_TOKENS < 1): wrapper.pm(messages["command_ratelimited"]) return LAST_WAIT[wrapper.source] = now WAIT_TOKENS -= 1 if now > var.CAN_START_TIME: var.CAN_START_TIME = now + timedelta(seconds=var.EXTRA_WAIT) else: var.CAN_START_TIME += timedelta(seconds=var.EXTRA_WAIT) wrapper.send(messages["wait_time_increase"].format(wrapper.source, var.EXTRA_WAIT))
def on_new_role(evt, var, user, old_role): if old_role == "sharpshooter": if evt.data["role"] != "sharpshooter": del GUNNERS[user] elif evt.data["role"] == "sharpshooter": GUNNERS[user] = math.ceil(var.SHARPSHOOTER_MULTIPLIER * len(get_players()))
def on_del_player(evt, var, player, all_roles, death_triggers): if player in TARGETED.values(): for x, y in list(TARGETED.items()): if y is player: del TARGETED[x] PREV_ACTED.discard(x) if death_triggers and "assassin" in all_roles and player in TARGETED: target = TARGETED[player] del TARGETED[player] PREV_ACTED.discard(player) if target in get_players(): protected = try_protection(var, target, player, "assassin", "assassin_fail") if protected is not None: channels.Main.send(*protected) return if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(target) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" message = messages["assassin_success"].format(player, target, an, role) else: message = messages["assassin_success_no_reveal"].format(player, target) channels.Main.send(message) debuglog("{0} (assassin) ASSASSINATE: {1} ({2})".format(player, target, get_main_role(target))) add_dying(var, target, killer_role=evt.params.main_role, reason="assassin")
def on_revealroles(evt, var, user, role): if role == "mad scientist": pl = get_players() target1, target2 = _get_targets(var, pl, user) evt.data["special_case"].append( messages["mad_scientist_revealroles_targets"].format( target1, target2))
def fwait(var, wrapper, message): """Force an increase (or decrease) in wait time. Can be used with a number of seconds to wait.""" pl = get_players() msg = re.split(" +", message.strip(), 1)[0] if msg and (msg.isdigit() or (msg[0] == "-" and msg[1:].isdigit())): extra = int(msg) else: extra = var.EXTRA_WAIT now = datetime.now() extra = max(-900, min(900, extra)) if now > var.CAN_START_TIME: var.CAN_START_TIME = now + timedelta(seconds=extra) else: var.CAN_START_TIME += timedelta(seconds=extra) if extra >= 0: wrapper.send(messages["forced_wait_time_increase"].format( wrapper.source, abs(extra), "s" if extra != 1 else "")) else: wrapper.send(messages["forced_wait_time_decrease"].format( wrapper.source, abs(extra), "s" if extra != -1 else ""))
def dullahan_kill(var, wrapper, message): """Kill someone at night as a dullahan until everyone on your list is dead.""" if not TARGETS[wrapper.source] & set(get_players()): wrapper.pm(messages["dullahan_targets_dead"]) return target = get_target(var, wrapper, re.split(" +", message)[0], not_self_message="no_suicide") if not target: return orig = target evt = Event("targeted_command", { "target": target, "misdirection": True, "exchange": True }) evt.dispatch(var, wrapper.source, target) if evt.prevent_default: return target = evt.data["target"] KILLS[wrapper.source] = target wrapper.pm(messages["player_kill"].format(orig)) debuglog("{0} (dullahan) KILL: {1} ({2})".format(wrapper.source, target, get_main_role(target)))
def on_transition_day_begin(self, evt, var): ps = get_players() for p in ps: if get_main_role(p) in Wolfteam: continue # wolf shamans can't starve if self.totem_tracking[p] > 0: # if sustenance totem made it through, fully feed player self.hunger_levels[p] = 0 elif self.totem_tracking[p] < 0: # if hunger totem made it through, fast-track player to starvation if self.hunger_levels[p] < 3: self.hunger_levels[p] = 3 # apply natural hunger self.hunger_levels[p] += 1 if self.hunger_levels[p] >= 5: # if they hit 5, they die of starvation # if there are less VGs than alive wolf shamans, they become a wendigo as well self.maybe_make_wendigo(var, p) add_dying(var, p, killer_role="villager", reason="boreal_starvation") elif self.hunger_levels[p] >= 3: # if they are at 3 or 4, alert them that they are hungry p.send(messages["boreal_hungry"]) self.totem_tracking.clear()
def on_transition_night_end(evt, var): for seer in get_all_players((rolename,)): pl = get_players() random.shuffle(pl) pl.remove(seer) # remove self from list seer.send(messages["seer_info_general"].format(rolename), messages[rolename + "_info"]) seer.send(messages["players_list"].format(pl))
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_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 harlot in get_all_players(("harlot", )): pl = get_players() random.shuffle(pl) pl.remove(harlot) harlot.send(messages["harlot_notify"], messages["players_list"].format(pl), sep="\n")
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) doctor.send(messages["doctor_notify"]) doctor.send(messages["doctor_immunizations"].format(DOCTORS[doctor]))
def add_vote_weight(var, target : users.User, amount : int = 1) -> None: """Make the target's votes as having more weight.""" if target not in get_players(): return WEIGHT[target] = WEIGHT.get(target, 1) + amount if WEIGHT[target] == 1: del WEIGHT[target]
def add_vote_weight(var, target: users.User, amount: int = 1) -> None: """Make the target's votes as having more weight.""" if target not in get_players(): return WEIGHT[target] = WEIGHT.get(target, 1) + amount if WEIGHT[target] == 1: del WEIGHT[target]
def setup_nightmares(self, evt, var): if random.random() < 1 / 5: with var.WARNING_LOCK: t = threading.Timer( 60, self.do_nightmare, (var, random.choice(get_players()), var.NIGHT_COUNT)) t.daemon = True t.start()
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): 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 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_transition_day_begin(evt, var): # Select random totem recipients if shamans didn't act pl = get_players() for shaman in get_players(("shaman",)): if shaman not in SHAMANS and not is_silent(var, shaman): ps = pl[:] if shaman in LASTGIVEN: if LASTGIVEN[shaman] in ps: ps.remove(LASTGIVEN[shaman]) if ps: target = random.choice(ps) dispatcher = MessageDispatcher(shaman, shaman) SHAMANS[shaman] = give_totem(var, dispatcher, target, prefix=messages["random_totem_prefix"], role="shaman", msg=" of {0}".format(TOTEMS[shaman])) else: LASTGIVEN[shaman] = None elif shaman not in SHAMANS: LASTGIVEN[shaman] = None
def _get_target(target): """Internal helper for try_misdirection. Return one target over.""" pl = get_players() index = pl.index(target) if random.randint(0, 1): index -= 2 if index == len(pl) - 1: # last item index = -1 return pl[index+1]
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): 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 add_protection(var, target, protector, protector_role, scope=All): """Add a protection to the target affecting the relevant scope.""" if target not in get_players(): return if target not in PROTECTIONS: PROTECTIONS[target] = DefaultUserDict(list) prot_entry = (scope, protector_role) PROTECTIONS[target][protector].append(prot_entry)
def on_transition_night_end(evt, var): ps = get_players() for inv in var.ROLES["investigator"]: pl = ps[:] random.shuffle(pl) pl.remove(inv) to_send = "investigator_notify" if inv.prefers_simple(): to_send = "investigator_simple" inv.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 = 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_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 ""))