def startup(self): super().startup() self.having_nightmare = UserList() cmd_params = dict(chan=False, pm=True, playing=True, phases=("night", ), users=self.having_nightmare) self.north_cmd = command("north", "n", **cmd_params)(functools.partial( self.move, "n")) self.east_cmd = command("east", "e", **cmd_params)(functools.partial( self.move, "e")) self.south_cmd = command("south", "s", **cmd_params)(functools.partial( self.move, "s")) self.west_cmd = command("west", "w", **cmd_params)(functools.partial( self.move, "w"))
def wolf_kill(var, wrapper, message): """Kills one or more players as a wolf.""" role = get_main_role(wrapper.source) # eventually cub will listen on targeted_command and block kills that way if var.DISEASED_WOLVES: wrapper.pm(messages["ill_wolves"]) return pieces = re.split(" +", message) targets = [] orig = [] nevt = Event("wolf_numkills", {"numkills": 1}) nevt.dispatch(var) num_kills = nevt.data["numkills"] if len(pieces) < num_kills: wrapper.pm(messages["wolf_must_target_multiple"]) return targs = iter(pieces) # allow random words to follow the initial line without issue for i in range(num_kills): target = get_target(var, wrapper, next(targs, None), not_self_message="no_suicide") if target is None: return if is_known_wolf_ally(var, wrapper.source, target): wrapper.pm(messages["wolf_no_target_wolf"]) return if target in orig: wrapper.pm(messages["wolf_must_target_multiple"]) return orig.append(target) evt = Event("targeted_command", {"target": target, "misdirection": True, "exchange": True}) if not evt.dispatch(var, wrapper.source, target): return target = evt.data["target"] targets.append(target) KILLS[wrapper.source] = UserList(targets) if len(orig) > 1: # TODO: Expand this so we can support arbitrarily many kills (instead of just one or two) wrapper.pm(messages["player_kill_multiple"].format(*orig)) msg = messages["wolfchat_kill_multiple"].format(wrapper.source, *orig) debuglog("{0} ({1}) KILL: {2} ({4}) and {3} ({5})".format(wrapper.source, role, *targets, get_main_role(targets[0]), get_main_role(targets[1]))) else: wrapper.pm(messages["player_kill"].format(orig[0])) msg = messages["wolfchat_kill"].format(wrapper.source, orig[0]) debuglog("{0} ({1}) KILL: {2} ({3})".format(wrapper.source, role, targets[0], get_main_role(targets[0]))) send_wolfchat_message(var, wrapper.source, msg, var.WOLF_ROLES, role=role, command="kill")
def wolf_kill(var, wrapper, message): """Kill one or more players as a wolf.""" # verify this user can actually kill if not get_all_roles(wrapper.source) & Wolf & Killer: return pieces = re.split(" +", message) targets = [] orig = [] nevt = Event("wolf_numkills", {"numkills": 1, "message": ""}) nevt.dispatch(var) num_kills = nevt.data["numkills"] if not num_kills: if nevt.data["message"]: wrapper.pm(messages[nevt.data["message"]]) return if len(pieces) < num_kills: wrapper.pm(messages["wolf_must_target_multiple"]) return for targ in pieces[:num_kills]: target = get_target(var, wrapper, targ, not_self_message="no_suicide") if target is None: return if is_known_wolf_ally(var, wrapper.source, target): wrapper.pm(messages["wolf_no_target_wolf"]) return if target in orig: wrapper.pm(messages["wolf_must_target_multiple"]) return orig.append(target) target = try_misdirection(var, wrapper.source, target) if try_exchange(var, wrapper.source, target): return targets.append(target) KILLS[wrapper.source] = UserList(targets) if len(orig) > 1: wrapper.pm(messages["player_kill_multiple"].format(orig)) msg = messages["wolfchat_kill_multiple"].format(wrapper.source, orig) debuglog("{0} KILL: {1} ({3}) and {2} ({4})".format(wrapper.source, targets[0], targets[1], get_main_role(targets[0]), get_main_role(targets[1]))) else: wrapper.pm(messages["player_kill"].format(orig[0])) msg = messages["wolfchat_kill"].format(wrapper.source, orig[0]) debuglog("{0} KILL: {1} ({2})".format(wrapper.source, targets[0], get_main_role(targets[0]))) send_wolfchat_message(var, wrapper.source, msg, Wolf, role="wolf", command="kill")
def on_transition_day_begin2(evt, var): LASTGIVEN.clear() for shaman, given in SHAMANS.items(): for totem, targets in given.items(): for target in targets: victim = RETARGET[shaman].get(target, target) if not victim: continue if totem == "death": # this totem stacks if shaman not in DEATH: DEATH[shaman] = UserList() DEATH[shaman].append(victim) elif totem == "protection": # this totem stacks PROTECTION.append(victim) elif totem == "revealing": REVEALING.add(victim) elif totem == "narcolepsy": NARCOLEPSY.add(victim) elif totem == "silence": SILENCE.add(victim) elif totem == "desperation": DESPERATION.add(victim) elif totem == "impatience": # this totem stacks IMPATIENCE.append(victim) elif totem == "pacifism": # this totem stacks PACIFISM.append(victim) elif totem == "influence": INFLUENCE.add(victim) elif totem == "exchange": EXCHANGE.add(victim) elif totem == "lycanthropy": LYCANTHROPY.add(victim) elif totem == "luck": LUCK.add(victim) elif totem == "pestilence": PESTILENCE.add(victim) elif totem == "retribution": RETRIBUTION.add(victim) elif totem == "misdirection": MISDIRECTION.add(victim) elif totem == "deceit": DECEIT.add(victim) else: event = Event("apply_totem", {}) event.dispatch(var, rolename, totem, shaman, victim) if target is not victim: shaman.send(messages["totem_retarget"].format( victim, target)) LASTGIVEN[shaman][totem].append(victim) havetotem.append(victim)
def give_totem(var, wrapper, target, prefix, role, msg): """Give a totem to a player. Return the value of SHAMANS[user].""" orig_target = target orig_role = get_main_role(orig_target) target = try_misdirection(var, wrapper.source, target) if try_exchange(var, wrapper.source, target): return targrole = get_main_role(target) wrapper.send(messages["shaman_success"].format(prefix, msg, orig_target)) debuglog("{0} ({1}) TOTEM: {2} ({3}) as {4} ({5})".format( wrapper.source, role, target, targrole, orig_target, orig_role)) return UserList((target, orig_target))
def lynch(var, wrapper, message): """Use this to vote for a candidate to be lynched.""" if not message: show_votes.func(var, wrapper, message) return if wrapper.private: return msg = re.split(" +", message)[0].strip() can_vote_bot = var.CURRENT_GAMEMODE.can_vote_bot(var) voted = get_target(var, wrapper, msg, allow_self=var.SELF_LYNCH_ALLOWED, allow_bot=can_vote_bot, not_self_message="no_self_lynch") if not voted: return if try_absent(var, wrapper.source): return ABSTAINS.discard(wrapper.source) for votee in list(VOTES): # remove previous vote if votee is voted and wrapper.source in VOTES[votee]: break if wrapper.source in VOTES[votee]: VOTES[votee].remove(wrapper.source) if not VOTES.get(votee) and votee is not voted: del VOTES[votee] break if voted not in VOTES: VOTES[voted] = UserList() if wrapper.source not in VOTES[voted]: VOTES[voted].append(wrapper.source) channels.Main.send(messages["player_vote"].format( wrapper.source, voted)) global LAST_VOTES LAST_VOTES = None # reset chk_decision(var)
def on_transition_day_begin2(evt, var): for shaman, (victim, target) in SHAMANS.items(): totem = TOTEMS[shaman] if totem == "death": # this totem stacks if shaman not in DEATH: DEATH[shaman] = UserList() DEATH[shaman].append(victim) elif totem == "protection": # this totem stacks PROTECTION.append(victim) elif totem == "revealing": REVEALING.add(victim) elif totem == "narcolepsy": NARCOLEPSY.add(victim) elif totem == "silence": SILENCE.add(victim) elif totem == "desperation": DESPERATION.add(victim) elif totem == "impatience": # this totem stacks IMPATIENCE.append(victim) elif totem == "pacifism": # this totem stacks PACIFISM.append(victim) elif totem == "influence": INFLUENCE.add(victim) elif totem == "exchange": EXCHANGE.add(victim) elif totem == "lycanthropy": LYCANTHROPY.add(victim) elif totem == "luck": LUCK.add(victim) elif totem == "pestilence": PESTILENCE.add(victim) elif totem == "retribution": RETRIBUTION.add(victim) elif totem == "misdirection": MISDIRECTION.add(victim) elif totem == "deceit": DECEIT.add(victim) # other totem types possibly handled in an earlier event, # as such there is no else: clause here if target is not victim: shaman.send(messages["totem_retarget"].format(victim)) LASTGIVEN[shaman] = victim havetotem.extend(filter(None, LASTGIVEN.values()))
def give_totem(var, wrapper, target, prefix, role, msg): """Give a totem to a player. Return the value of SHAMANS[user].""" orig_target = target orig_role = get_main_role(orig_target) evt = Event("targeted_command", { "target": target, "misdirection": True, "exchange": True }) if not evt.dispatch(var, wrapper.source, target): return target = evt.data["target"] targrole = get_main_role(target) wrapper.send(messages["shaman_success"].format(prefix, msg, orig_target)) debuglog("{0} ({1}) TOTEM: {2} ({3}) as {4} ({5})".format( wrapper.source, role, target, targrole, orig_target, orig_role)) return UserList((target, orig_target))
class SleepyMode(GameMode): """A small village has become the playing ground for all sorts of supernatural beings.""" def __init__(self, arg=""): super().__init__(arg) self.ROLE_GUIDE = { 10: [ "wolf", "werecrow", "traitor", "cultist", "seer", "prophet", "priest", "dullahan", "cursed villager", "blessed villager" ], 12: ["wolf(2)", "vigilante"], 15: ["wolf(3)", "detective", "vengeful ghost"], 18: ["wolf(4)", "harlot", "monster"], 21: ["wolf(5)", "village drunk", "monster(2)", "gunner"], } # Make sure priest is always prophet AND blessed, and that drunk is always gunner self.SECONDARY_ROLES["blessed villager"] = ["priest"] self.SECONDARY_ROLES["prophet"] = ["priest"] self.SECONDARY_ROLES["gunner"] = ["village drunk"] self.EVENTS = { "dullahan_targets": EventListener(self.dullahan_targets), "transition_night_begin": EventListener(self.setup_nightmares), "chk_nightdone": EventListener(self.prolong_night), "transition_day_begin": EventListener(self.nightmare_kill), "del_player": EventListener(self.happy_fun_times), "revealroles": EventListener(self.on_revealroles) } def startup(self): super().startup() self.having_nightmare = UserList() cmd_params = dict(chan=False, pm=True, playing=True, phases=("night", ), users=self.having_nightmare) self.north_cmd = command("north", "n", **cmd_params)(functools.partial( self.move, "n")) self.east_cmd = command("east", "e", **cmd_params)(functools.partial( self.move, "e")) self.south_cmd = command("south", "s", **cmd_params)(functools.partial( self.move, "s")) self.west_cmd = command("west", "w", **cmd_params)(functools.partial( self.move, "w")) def teardown(self): from src import decorators super().teardown() def remove_command(name, command): if len(decorators.COMMANDS[name]) > 1: decorators.COMMANDS[name].remove(command) else: del decorators.COMMANDS[name] remove_command("north", self.north_cmd) remove_command("n", self.north_cmd) remove_command("east", self.east_cmd) remove_command("e", self.east_cmd) remove_command("south", self.south_cmd) remove_command("s", self.south_cmd) remove_command("west", self.west_cmd) remove_command("w", self.west_cmd) self.having_nightmare.clear() def dullahan_targets(self, evt, var, dullahan, max_targets): evt.data["targets"].update(var.ROLES["priest"]) 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() @handle_error 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 nightmare_step(self): if self.prev_direction == "n": directions = "north, east, and west" elif self.prev_direction == "e": directions = "north, east, and south" elif self.prev_direction == "s": directions = "east, south, and west" elif self.prev_direction == "w": directions = "north, south, and west" if self.step == 0: self.having_nightmare[0].send( messages["sleepy_nightmare_0"].format(directions)) elif self.step == 1: self.having_nightmare[0].send( messages["sleepy_nightmare_1"].format(directions)) elif self.step == 2: self.having_nightmare[0].send( messages["sleepy_nightmare_2"].format(directions)) elif self.step == 3: if "correct" in self.on_path: self.having_nightmare[0].send( messages["sleepy_nightmare_wake"]) self.having_nightmare.clear() elif "fake1" in self.on_path: self.having_nightmare[0].send( messages["sleepy_nightmare_fake_1"]) self.step = 0 self.on_path = set() self.prev_direction = self.start_direction self.nightmare_step() elif "fake2" in self.on_path: self.having_nightmare[0].send( messages["sleepy_nightmare_fake_2"]) self.step = 0 self.on_path = set() self.prev_direction = self.start_direction self.nightmare_step() def move(self, direction, var, wrapper, message): opposite = {"n": "s", "e": "w", "s": "n", "w": "e"} if self.prev_direction == opposite[direction]: wrapper.pm(messages["sleepy_nightmare_invalid_direction"]) return advance = False if ("correct" in self.on_path or self.step == 0) and self.correct[self.step] == direction: self.on_path.add("correct") advance = True else: self.on_path.discard("correct") if ("fake1" in self.on_path or self.step == 0) and self.fake1[self.step] == direction: self.on_path.add("fake1") advance = True else: self.on_path.discard("fake1") if ("fake2" in self.on_path or self.step == 0) and self.fake2[self.step] == direction: self.on_path.add("fake2") advance = True else: self.on_path.discard("fake2") if advance: self.step += 1 self.prev_direction = direction else: self.step = 0 self.on_path = set() self.prev_direction = self.start_direction wrapper.pm(messages["sleepy_nightmare_restart"]) self.nightmare_step() def prolong_night(self, evt, var): if self.having_nightmare: evt.data["actedcount"] = -1 def nightmare_kill(self, evt, var): if self.having_nightmare and self.having_nightmare[0] in get_players(): add_dying(var, self.having_nightmare[0], "bot", "night_kill") self.having_nightmare[0].send(messages["sleepy_nightmare_death"]) del self.having_nightmare[0] def happy_fun_times(self, evt, var, player, all_roles, death_triggers): if death_triggers: if evt.params.main_role == "priest": turn_chance = 3 / 4 seers = [ p for p in get_players(("seer", )) if random.random() < turn_chance ] harlots = [ p for p in get_players(("harlot", )) if random.random() < turn_chance ] cultists = [ p for p in get_players(("cultist", )) if random.random() < turn_chance ] channels.Main.send(messages["sleepy_priest_death"]) for seer in seers: change_role(var, seer, "seer", "doomsayer", message="sleepy_doomsayer_turn") for harlot in harlots: change_role(var, harlot, "harlot", "succubus", message="sleepy_succubus_turn") for cultist in cultists: change_role(var, cultist, "cultist", "demoniac", message="sleepy_demoniac_turn") def on_revealroles(self, evt, var, wrapper): if self.having_nightmare: evt.data["output"].append( "\u0002having nightmare\u0002: {0}".format( self.having_nightmare[0]))
# role knows about the totems they receive. This parameter is keyword-only # # 4. Implement the "transition_day_begin" and "transition_night_end" events to give # # out totems if the shaman didn't act, and send night messages, respectively. # # Implementation of the "get_role_metadata" event with the "role_categories" # # kind is also necessary for the bot to know that the role exists at all. You # # may look at existing shamans for reference. If your shaman isn't a wolf role, # # the "lycanthropy_role" kind should also be implemented as follows: # # evt.data[role] = {"role": "wolf shaman", "prefix": "shaman"} # # You will also need to implement your own "give" command; see existing # # shamans for reference, or ask for help in our development channel. # # # # It is generally unneeded to modify this file to add new totems or shaman roles # ##################################################################################### DEATH = UserDict() # type: Dict[users.User, List[users.User]] PROTECTION = UserList() # type: List[users.User] REVEALING = UserSet() # type: Set[users.User] NARCOLEPSY = UserSet() # type: Set[users.User] SILENCE = UserSet() # type: Set[users.User] DESPERATION = UserSet() # type: Set[users.User] IMPATIENCE = UserList() # type: List[users.User] PACIFISM = UserList() # type: List[users.User] INFLUENCE = UserSet() # type: Set[users.User] EXCHANGE = UserSet() # type: Set[users.User] LYCANTHROPY = UserSet() # type: Set[users.User] LUCK = UserSet() # type: Set[users.User] PESTILENCE = UserSet() # type: Set[users.User] RETRIBUTION = UserSet() # type: Set[users.User] MISDIRECTION = UserSet() # type: Set[users.User] DECEIT = UserSet() # type: Set[users.User]
# role knows about the totems they receive. This parameter is keyword-only # # 4. Implement the "transition_day_begin" and "transition_night_end" events to give # # out totems if the shaman didn't act, and send night messages, respectively. # # Implementation of the "get_role_metadata" event with the "role_categories" # # kind is also necessary for the bot to know that the role exists at all. You # # may look at existing shamans for reference. If your shaman isn't a wolf role, # # the "lycanthropy_role" kind should also be implemented as follows: # # evt.data[role] = {"role": "wolf shaman", "prefix": "shaman"} # # You will also need to implement your own "give" command; see existing # # shamans for reference, or ask for help in our development channel. # # # # It is generally unneeded to modify this file to add new totems or shaman roles # ##################################################################################### DEATH = UserDict() # type: UserDict[users.User, UserList] PROTECTION = UserList() REVEALING = UserSet() NARCOLEPSY = UserSet() SILENCE = UserSet() DESPERATION = UserSet() IMPATIENCE = UserList() PACIFISM = UserList() INFLUENCE = UserSet() EXCHANGE = UserSet() LYCANTHROPY = UserSet() LUCK = UserSet() PESTILENCE = UserSet() RETRIBUTION = UserSet() MISDIRECTION = UserSet() DECEIT = UserSet()
def wolf_kill(var, wrapper, message): """Kill one or more players as a wolf.""" pieces = re.split(" +", message) targets = [] orig = [] nevt = Event("wolf_numkills", {"numkills": 1, "message": ""}) nevt.dispatch(var) num_kills = nevt.data["numkills"] if not num_kills: if nevt.data["message"]: wrapper.pm(messages[nevt.data["message"]]) return if len(pieces) < num_kills: wrapper.pm(messages["wolf_must_target_multiple"]) return for targ in pieces[:num_kills]: target = get_target(var, wrapper, targ, not_self_message="no_suicide") if target is None: return if is_known_wolf_ally(var, wrapper.source, target): wrapper.pm(messages["wolf_no_target_wolf"]) return if target in orig: wrapper.pm(messages["wolf_must_target_multiple"]) return orig.append(target) target = try_misdirection(var, wrapper.source, target) if try_exchange(var, wrapper.source, target): return targets.append(target) KILLS[wrapper.source] = UserList(targets) if len(orig) > 1: # TODO: Expand this so we can support arbitrarily many kills (instead of just one or two) wrapper.pm(messages["player_kill_multiple"].format(*orig)) msg = messages["wolfchat_kill_multiple"].format( wrapper.source, *orig) debuglog("{0} ({1}) KILL: {2} ({4}) and {3} ({5})".format( wrapper.source, rolename, *targets, get_main_role(targets[0]), get_main_role(targets[1]))) else: wrapper.pm(messages["player_kill"].format(orig[0])) msg = messages["wolfchat_kill"].format(wrapper.source, orig[0]) debuglog("{0} ({1}) KILL: {2} ({3})".format( wrapper.source, rolename, targets[0], get_main_role(targets[0]))) send_wolfchat_message(var, wrapper.source, msg, Wolf, role=rolename, command="kill")