def __create_droisoned_info(self): """Create drunk/poisoned information for the undertaker info""" executed_player = globvars.master_state.game.today_executed_player # If someone has been executed if executed_player: tb_townsfolk_all = BOTCUtils.get_role_list(TroubleBrewing, Townsfolk) tb_outsider_all = BOTCUtils.get_role_list(TroubleBrewing, Outsider) tb_minion_all = BOTCUtils.get_role_list(TroubleBrewing, Minion) tb_demon_all = BOTCUtils.get_role_list(TroubleBrewing, Demon) executed_player.role.set_new_social_self(executed_player) # The executed player has a good role. The droisoned undertaker will see a # bad role. if executed_player.role.social_self.is_good(): pool = tb_minion_all + tb_demon_all ret = random.choice(pool) return ret # The executed player has a bad role. The droisoned undertaker will see # a good role. else: pool = tb_townsfolk_all + tb_outsider_all pool = [character for character in pool if character.name != Undertaker().name] ret = random.choice(pool) return ret # If no one is executed, then send none else: return None
def exec_init_setup(self, townsfolk_obj_list, outsider_obj_list, minion_obj_list, demon_obj_list): """Add two outsiders to the setup, remove two townsfolks from the setup""" random.shuffle(townsfolk_obj_list) # Remove two townsfolks townsfolk_obj_list.pop() townsfolk_obj_list.pop() # If the single remaining townsfolk is a washerwoman, then remove it if len(townsfolk_obj_list) == 1: if townsfolk_obj_list[0].name == TBRole.washerwoman.value: tb_townsfolk_all = BOTCUtils.get_role_list(TroubleBrewing, Townsfolk) all_not_washer = [character for character in tb_townsfolk_all if character.name != TBRole.washerwoman.value] townsfolk_obj_list = [random.choice(all_not_washer)] tb_outsider_all = BOTCUtils.get_role_list(TroubleBrewing, Outsider) random.shuffle(tb_outsider_all) count = 0 for outsider in tb_outsider_all: if count >= 2: break else: if str(outsider) not in [str(role) for role in outsider_obj_list]: outsider_obj_list.append(outsider) count += 1 return [townsfolk_obj_list, outsider_obj_list, minion_obj_list, demon_obj_list]
async def info_add(self, ctx, *, args: str): invoking_player = BOTCUtils.get_player_from_id(ctx.author.id) args = re.split(' +', args) # Ensure that we have enough values in the list args += ['', ''] try: target_phase = int(args[0]) target_player = None args.pop(0) except ValueError: target_player = BOTCUtils.get_player_from_string(args[0]) if target_player is not None: args.pop(0) try: target_phase = int(args[0]) args.pop(0) except ValueError: target_phase = None # Remove the two dummy elements args.pop() args.pop() rem = ' '.join(args) if not rem.strip(): if target_phase is None: if target_player is None: raise commands.MissingRequiredArgument(Parameter('info', Parameter.POSITIONAL_ONLY)) else: # Added info was a player name rem = str(target_player) target_player = None else: # Added info was a number (empath for example) rem = str(target_phase) target_phase = None if target_phase is not None and target_phase > globvars.master_state.game._chrono.cycle: raise commands.BadArgument() if target_player is not None: globvars.master_state.game.note_manager.info(target_player, rem, invoking_player, target_phase) else: if globvars.master_state.game._chrono.phase == Phase.night: raise NotNight("Command is allowed during night phase only (BoTC)") globvars.master_state.game.note_manager.info(invoking_player, rem, None, target_phase) await ctx.send(info_add_feedback_str.format(BotEmoji.check))
async def exec_learn(self, ravenkeeper_player, learn_player): """Execute the learn action (dawn ability interaction)""" if DISABLE_DMS: return # Correct info if not ravenkeeper_player.is_droisoned(): learned_character_type = learn_player.role.social_self # Droisoned info else: real_character_type = learn_player.role.true_self # If the real character type is good if real_character_type.is_good(): tb_minion_all = BOTCUtils.get_role_list(TroubleBrewing, Minion) tb_demon_all = BOTCUtils.get_role_list(TroubleBrewing, Demon) pool = tb_minion_all + tb_demon_all learned_character_type = random.choice(pool) # If the real character type is bad else: tb_townsfolk_all = BOTCUtils.get_role_list( TroubleBrewing, Townsfolk) tb_outsider_all = BOTCUtils.get_role_list( TroubleBrewing, Outsider) pool = tb_townsfolk_all + tb_outsider_all learned_character_type = random.choice(pool) link = learned_character_type._art_link_cropped recipient = ravenkeeper_player.user # Add information to replay drunk = "Drunk " if ravenkeeper_player.role.true_self.name == Drunk( ).name else "" globvars.master_state.game.replay += f"- {recipient.name} ({drunk}Ravenkeeper) learns "\ f"that {learn_player.user.name} is {learned_character_type}\n" msg = f"***{recipient.name}#{recipient.discriminator}***, the **{self.name}**:" msg += "\n" msg += self.emoji + " " + self.instruction msg += "\n" msg += ravenkeeper_reply.format(learned_character_type.name) embed = discord.Embed(description=msg) embed.set_thumbnail(url=link) embed.set_footer(text=copyrights_str) embed.timestamp = datetime.datetime.utcnow() try: await recipient.send(embed=embed) except discord.Forbidden: pass
async def notes_add(self, ctx, *, args): invoking_player = BOTCUtils.get_player_from_id(ctx.author.id) args = re.split(' +', args) target_player = BOTCUtils.get_player_from_string(args[0]) if target_player is None: target_player = invoking_player note = ' '.join(args) else: args.pop(0) note = ' '.join(args) globvars.master_state.game.note_manager.note(target_player, note, invoking_player) await ctx.send(notes_add_feedback_str.format(BotEmoji.check))
def check_if_player_really_dead(ctx): """Check if the player is dead using real state""" player = BOTCUtils.get_player_from_id(ctx.author.id) if player.is_dead(): return True else: raise DeadOnlyCommand("Command reserved for Dead Players (BoTC)")
def check_if_player_really_alive(ctx): """Check if the player is alive using real state""" player = BOTCUtils.get_player_from_id(ctx.author.id) if player.is_alive(): return True else: raise AliveOnlyCommand("Command reserved for Alive Players (BoTC)")
def check_if_not_player(ctx): """Return true if user is not player""" player = BOTCUtils.get_player_from_id(ctx.author.id) if player: raise IsAPlayer("Command not allowed: user is a player (BoTC).") else: return True
async def read(self, ctx, *, read: PlayerParser()): """Read command usage: read <player> and <player> and... characters: fortune teller """ player = BOTCUtils.get_player_from_id(ctx.author.id) await player.role.ego_self.register_read(player, read)
async def kill(self, ctx, *, killed: PlayerParser()): """Kill command usage: kill <player> and <player> and... characters: imp """ player = BOTCUtils.get_player_from_id(ctx.author.id) await player.role.ego_self.register_kill(player, killed)
def exec_init_setup(self, townsfolk_obj_list, outsider_obj_list, minion_obj_list, demon_obj_list): """Add two outsiders to the setup, remove two townsfolks from the setup""" random.shuffle(townsfolk_obj_list) townsfolk_obj_list.pop() townsfolk_obj_list.pop() tb_outsider_all = BOTCUtils.get_role_list(TroubleBrewing, Outsider) random.shuffle(tb_outsider_all) count = 0 for outsider in tb_outsider_all: if count >= 2: break else: if str(outsider) not in [ str(role) for role in outsider_obj_list ]: outsider_obj_list.append(outsider) count += 1 return [ townsfolk_obj_list, outsider_obj_list, minion_obj_list, demon_obj_list ]
async def poison(self, ctx, *, poisoned: PlayerParser()): """Poison command usage: poison <player> and <player> and... characters: poisoner """ player = BOTCUtils.get_player_from_id(ctx.author.id) await player.role.ego_self.register_poison(player, poisoned)
async def whisper(self, ctx, recipient: PlayerConverter(), *, content: WhisperConverter()): """Whisper command""" player = BOTCUtils.get_player_from_id(ctx.author.id) try: msg = from_str.format( botutils.BotEmoji.unread_message, player.game_nametag, content ) await recipient.user.send(msg) except discord.Forbidden: msg = recipient_blocked.format(botutils.BotEmoji.warning_sign) await ctx.send(msg) else: msg = to_str.format( botutils.BotEmoji.whisper, recipient.game_nametag, content ) await ctx.send(msg) announcement_msg = whisper_announcement.format( botutils.BotEmoji.opened_letter, player.game_nametag, recipient.game_nametag, ) lobby_message = await botutils.send_lobby(announcement_msg) await asyncio.sleep(WHISPER_SHOW_TIME) await lobby_message.delete()
async def protect(self, ctx, *, protected: PlayerParser()): """Protect command usage: protect <player> and <player> and... characters: monk """ player = BOTCUtils.get_player_from_id(ctx.author.id) await player.role.ego_self.register_protect(player, protected)
async def learn(self, ctx, *, learned: PlayerParser()): """Learn command usage: learn <player> and <player> and... characters: ravenkeeper """ player = BOTCUtils.get_player_from_id(ctx.author.id) await player.role.ego_self.register_learn(player, learned)
async def slay(self, ctx, *, slain: PlayerParser()): """Slay command usage: slay <player> and <player> and... characters: slayer """ player = BOTCUtils.get_player_from_id(ctx.author.id) await player.role.ego_self.register_slay(player, slain)
async def serve(self, ctx, *, master: PlayerParser()): """Serve command: usage: serve <player> and <player> and... characters: butler """ player = BOTCUtils.get_player_from_id(ctx.author.id) await player.role.ego_self.register_serve(player, master)
async def process_night_ability(self, player): """Process night actions for the butler character. @player : the Butler player (Player object) """ phase = globvars.master_state.game._chrono.phase_id action = player.action_grid.retrieve_an_action(phase) # The butler has submitted an action. We call the execution function immediately if action: assert action.action_type == ActionTypes.serve, f"Wrong action type {action} in butler" targets = action.target_player master_player = targets[0] await self.exec_serve(player, master_player) # The butler has not submitted an action. We randomize the master for him, # DM him the choice of the master, and then call the execution function else: master_player = BOTCUtils.get_random_player_excluding(player) await self.exec_serve(player, master_player) msg = botutils.BotEmoji.butterfly msg += " " msg += action_assign.format(master_player.game_nametag) try: await player.user.send(msg) except discord.Forbidden: pass
async def send_n1_end_message(self, recipient): """Send two possible players for a particular minion character.""" player = BOTCUtils.get_player_from_id(recipient.id) if DISABLE_DMS: return # Dead players do not receive anything if not player.is_alive(): return link = self.registered_minion_type.art_link # Construct the message to send msg = f"***{recipient.name}#{recipient.discriminator}***, the **{self.name}**:" msg += "\n" msg += self.emoji + " " + self.instruction msg += "\n" msg += investigator_init.format(self.registered_minion_type.name) msg += "```basic\n" msg += f"{self.two_player_list[0].user.display_name} ({self.two_player_list[0].user.id})\n" msg += f"{self.two_player_list[1].user.display_name} ({self.two_player_list[1].user.id})\n" msg += "```" embed = discord.Embed(description=msg) embed.set_thumbnail(url=link) embed.set_footer(text=copyrights_str) embed.timestamp = datetime.datetime.utcnow() try: await recipient.send(embed=embed) except discord.Forbidden: pass
async def process_night_ability(self, player): """Process night actions for the zombuul character. @player : the Zombuul player (Player object) """ # We only do any of the following if the zombuul is alive. Otherwise skip everything. if player.is_alive(): phase = globvars.master_state.game._chrono.phase_id action = player.action_grid.retrieve_an_action(phase) # The zombuul has submitted an action. We call the execution function immediately if action: assert action.action_type == ActionTypes.kill, f"Wrong action type {action} in zombuul" targets = action.target_player killed_player = targets[0] await self.exec_kill(player, killed_player) # The zombuul has not submitted an action. We will randomize the action and make # the zombuul kill one random player that is not the zombuul. else: if player.is_alive(): killed_player = BOTCUtils.get_random_player_excluding( player) await self.exec_kill(player, killed_player) msg = botutils.BotEmoji.butterfly msg += " " msg += action_assign.format(killed_player.game_nametag) try: await player.user.send(msg) except discord.Forbidden: pass else: pass
async def send_regular_night_start_dm(self, recipient): """Send the query for night action for each regular night (not the first one)""" if DISABLE_DMS: return import globvars player = BOTCUtils.get_player_from_id(recipient.id) if player.is_alive(): # Construct the message to send msg = f"***{recipient.name}#{recipient.discriminator}***, the **{self.name}**:" msg += "\n" msg += self.emoji + " " + self.instruction msg += "\n" embed = discord.Embed(description=msg) embed.timestamp = datetime.datetime.utcnow() embed.set_footer(text=copyrights_str) msg2 = self.action msg2 += globvars.master_state.game.create_sitting_order_stats_string( ) embed.add_field(name=butterfly + " **「 Your Action 」**", value=msg2, inline=False) try: await recipient.send(embed=embed) except discord.Forbidden: pass
async def send_n1_end_message(self, recipient): """Send the number of pairs of evils sitting together.""" from botc.BOTCUtils import get_number_image player = BOTCUtils.get_player_from_id(recipient.id) # Dead players do not receive anything if not player.is_alive(): return # Poisoned info if player.is_droisoned(): evil_pair_count = self.__create_droisoned_info() # Good info else: evil_pair_count = self.get_nb_pairs_of_evils() link = get_number_image(evil_pair_count) msg = f"***{recipient.name}#{recipient.discriminator}***, the **{self.name}**:" msg += "\n" msg += self.emoji + " " + self.instruction msg += "\n" msg += chef_init.format(evil_pair_count) embed = discord.Embed(description=msg) embed.set_thumbnail(url=link) embed.set_footer(text=copyrights_str) embed.timestamp = datetime.datetime.utcnow() try: await recipient.send(embed=embed) except discord.Forbidden: pass
def get_demon_bluffs(self): """Get the list of 3 demon bluffs""" # 3 demon bluffs: 2 townsfolk characters + 1 outsider character # Exclusing all characters taken by other players, as well as the drunk's ego_self all_townsfolks = BOTCUtils.get_role_list(BadMoonRising, Townsfolk) all_outsiders = BOTCUtils.get_role_list(BadMoonRising, Outsider) taken_townsfolks = [ player.role.name for player in globvars.master_state.game.setup.townsfolks ] taken_outsiders = [ player.role.name for player in globvars.master_state.game.setup.outsiders ] possible_townsfolk_bluffs = [ character for character in all_townsfolks if character.name not in taken_townsfolks ] possible_outsider_bluffs = [ character for character in all_outsiders if character.name not in taken_outsiders ] random.shuffle(possible_townsfolk_bluffs) random.shuffle(possible_outsider_bluffs) # For the first two bluffs, we want a townsfolk, definitely bluff_1 = possible_townsfolk_bluffs.pop() bluff_2 = possible_townsfolk_bluffs.pop() # For the third bluff, if the outsider list is not empty, we will take an outsider. Otherwise # it's 40% chance outsider, 60% chance townsfolk if possible_outsider_bluffs: town_or_out = random.choices(["t", "o"], weights=[0.6, 0.4]) if town_or_out[0] == "t": bluff_3 = possible_townsfolk_bluffs.pop() else: bluff_3 = possible_outsider_bluffs.pop() else: bluff_3 = possible_townsfolk_bluffs.pop() globvars.logging.info( f">>> Zombull: Received three demon bluffs {bluff_1}, {bluff_2} and {bluff_3}." ) return (bluff_1, bluff_2, bluff_3)
async def getrole(self, ctx): player = BOTCUtils.get_player_from_id(ctx.author.id) if not isinstance(ctx.channel, discord.channel.DMChannel): await ctx.send(game_text["doc"]["getrole"]["feedback"].format( ctx.author.mention, BotEmoji.check)) await player.role.ego_self.send_opening_dm_embed(player.user)
def can_use_serve(user_id): """Return true if the user can use the command "serve" Characters that can serve: - Butler """ from botc.gamemodes.troublebrewing._utils import TBRole player = BOTCUtils.get_player_from_id(user_id) if player.role.ego_self.name in [TBRole.butler.value]: return True return False
def can_use_protect(user_id): """Return true if the user can use the command "protect" Characters that can poison: - Monk """ from botc.gamemodes.troublebrewing._utils import TBRole player = BOTCUtils.get_player_from_id(user_id) if player.role.ego_self.name in [TBRole.monk.value]: return True return False
def can_use_read(user_id): """Return true if the user can use the command "read" Characters that can poison: - Fortune teller """ from botc.gamemodes.troublebrewing._utils import TBRole player = BOTCUtils.get_player_from_id(user_id) if player.role.ego_self.name in [TBRole.fortuneteller.value]: return True return False
def check_if_is_player(ctx): """Return true if user is a player, and not in fleaved state""" player = BOTCUtils.get_player_from_id(ctx.author.id) if player: if player.is_fleaved(): raise NotAPlayer( "Command not allowed: user has quit the game (BoTC).") else: return True else: raise NotAPlayer("Command not allowed: user is not a player (BoTC).")
async def claim_remove(self, ctx, player: PlayerConverter = None): invoking_player = BOTCUtils.get_player_from_id(ctx.author.id) if player is None: if globvars.master_state.game._chrono.phase == Phase.night: raise NotNight("Command is allowed during night phase only (BoTC)") globvars.master_state.game.note_manager.rm_claim(invoking_player) await botutils.send_lobby(remove_claim_str.format(ctx.author.mention), delete_after = CLAIM_SHOW_TIME) else: globvars.master_state.game.note_manager.rm_claim(player, invoking_player) await ctx.send(private_remove_claim_str.format(BotEmoji.check))
async def notes_remove(self, ctx, player_or_id: Union[PlayerConverter, int], id: int = None): invoking_player = BOTCUtils.get_player_from_id(ctx.author.id) if isinstance(player_or_id, Player): if id is None: raise MissingRequiredArgument(Parameter('id', Parameter.POSITIONAL_ONLY)) else: id = player_or_id player_or_id = invoking_player globvars.master_state.game.note_manager.rm_note(player_or_id, id, invoking_player) await ctx.send(notes_remove_feedback_str.format(BotEmoji.check))