diff --git a/werewolf/__init__.py b/werewolf/__init__.py index 9430e4a..cefe6ad 100644 --- a/werewolf/__init__.py +++ b/werewolf/__init__.py @@ -2,4 +2,4 @@ from .werewolf import Werewolf def setup(bot): - bot.add_cog(Werewolf(bot)) \ No newline at end of file + bot.add_cog(Werewolf(bot)) diff --git a/werewolf/builder.py b/werewolf/builder.py index b10b4a9..6da7c84 100644 --- a/werewolf/builder.py +++ b/werewolf/builder.py @@ -7,7 +7,7 @@ from werewolf.roles.villager import Villager from werewolf.roles.seer import Seer # All roles in this list for iterating -role_list = [Villager, VanillaWerewolf] +role_list = [Villager, VanillaWerewolf] """ Example code: @@ -28,7 +28,7 @@ double digit position preempted by `-` async def parse_code(code): """Do the magic described above""" out = [] - decode = code.copy() # for now, pass exact names + decode = code.copy() # for now, pass exact names for role_id in decode: print(role_id) if role_id == "Villager": @@ -40,7 +40,7 @@ async def parse_code(code): else: # Fail to parse return None out.append(role) - + return out @@ -48,6 +48,6 @@ async def build_game(channel: discord.TextChannel): await channel.send("Not currently available") code = 12345678 - + await channel.send("Your game code is **`{}`**".format(code)) # Make this embeds diff --git a/werewolf/game.py b/werewolf/game.py index f19c702..52e6b47 100644 --- a/werewolf/game.py +++ b/werewolf/game.py @@ -13,18 +13,18 @@ class Game: """ default_secret_channel = { - "channel": None, - "players": [], - "votegroup": None # uninitialized VoteGroup - } - + "channel": None, + "players": [], + "votegroup": None # uninitialized VoteGroup + } + morning_messages = [ "**The sun rises on day {} in the village..**", "**Morning has arrived on day {}..**" - ] - + ] + day_vote_count = 3 - + # def __new__(cls, guild, game_code): # game_code = ["VanillaWerewolf", "Villager", "Villager"] # @@ -33,28 +33,28 @@ class Game: def __init__(self, guild, game_code): self.guild = guild self.game_code = ["VanillaWerewolf"] - + self.roles = [] - + self.players = [] - + self.day_vote = {} # author: target self.vote_totals = {} # id: total_votes - + self.started = False self.game_over = False self.can_vote = False self.used_votes = 0 - - self.day_time = False + + self.day_time = False self.day_count = 0 - + self.channel_category = None self.village_channel = None - + self.p_channels = {} # uses default_secret_channel self.vote_groups = {} # ID : VoteGroup() - + self.night_results = [] self.loop = asyncio.get_event_loop() @@ -76,58 +76,58 @@ class Game: await ctx.send("Player count does not match role count, cannot start") self.roles = [] return False - + await self.assign_roles() - + # Create category and channel with individual overwrites overwrite = { - self.guild.default_role: discord.PermissionOverwrite(read_messages=False, send_messages=True), - self.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True) - } + self.guild.default_role: discord.PermissionOverwrite(read_messages=False, send_messages=True), + self.guild.me: discord.PermissionOverwrite(read_messages=True, send_messages=True) + } self.channel_category = await self.guild.create_category("ww-game", overwrites=overwrite, reason="New game of " "werewolf") - + for player in self.players: overwrite[player.member] = discord.PermissionOverwrite(read_messages=True) - + self.village_channel = await self.guild.create_text_channel("Village Square", overwrites=overwrite, reason="New game of werewolf", category=self.channel_category) - + # Assuming everything worked so far print("Pre at_game_start") await self._at_game_start() # This will queue channels and votegroups to be made print("Post at_game_start") for channel_id in self.p_channels: - print("Channel id: "+channel_id) + print("Channel id: " + channel_id) overwrite = { self.guild.default_role: discord.PermissionOverwrite(read_messages=False), self.guild.me: discord.PermissionOverwrite(read_messages=True) - } - + } + for player in self.p_channels[channel_id]["players"]: overwrite[player.member] = discord.PermissionOverwrite(read_messages=True) - + channel = await self.guild.create_text_channel(channel_id, overwrites=overwrite, reason="Ww game secret channel", category=self.channel_category) - + self.p_channels[channel_id]["channel"] = channel - + if self.p_channels[channel_id]["votegroup"] is not None: vote_group = self.p_channels[channel_id]["votegroup"](self, channel) - + await vote_group.register_players(*self.p_channels[channel_id]["players"]) - + self.vote_groups[channel_id] = vote_group print("Pre-cycle") await asyncio.sleep(1) - asyncio.ensure_future(self._cycle()) # Start the loop - + asyncio.ensure_future(self._cycle()) # Start the loop + ############START Notify structure############ async def _cycle(self): """ @@ -147,14 +147,14 @@ class Game: await self._at_day_start() # Once cycle ends, this will trigger end_game await self._end_game() # Handle open channels - + async def _at_game_start(self): # ID 0 if self.game_over: return - + await self.village_channel.send( embed=discord.Embed(title="Game is starting, please wait for setup to complete")) - + await self._notify(0) async def _at_day_start(self): # ID 1 @@ -164,82 +164,82 @@ class Game: def check(): return not self.can_vote or not self.day_time or self.game_over - self.day_count += 1 - embed=discord.Embed(title=random.choice(self.morning_messages).format(self.day_count)) + self.day_count += 1 + embed = discord.Embed(title=random.choice(self.morning_messages).format(self.day_count)) for result in self.night_results: embed.add_field(name=result, value="________", inline=False) - + self.day_time = True - + self.night_results = [] # Clear for next day - + await self.village_channel.send(embed=embed) await self.generate_targets(self.village_channel) await self.day_perms(self.village_channel) await self._notify(1) - + await self._check_game_over() if self.game_over: return self.can_vote = True - + await asyncio.sleep(120) # 4 minute days if check(): return await self.village_channel.send(embed=discord.Embed(title="**Two minutes of daylight remain...**")) await asyncio.sleep(120) # 4 minute days - + # Need a loop here to wait for trial to end (can_vote?) - + if check(): return - + await self._at_day_end() - + async def _at_voted(self, target): # ID 2 if self.game_over: return data = {"player": target} await self._notify(2, data) - + self.used_votes += 1 self.can_vote = False await self.speech_perms(self.village_channel, target.member) await self.village_channel.send( "**{} will be put to trial and has 30 seconds to defend themselves**".format(target.mention)) - + await asyncio.sleep(30) - + await self.speech_perms(self.village_channel, target.member, undo=True) - + message = await self.village_channel.send( "Everyone will now vote whether to lynch {}\n" "👍 to save, 👎 to lynch\n" "*Majority rules, no-lynch on ties, " "vote both or neither to abstain, 15 seconds to vote*".format(target.mention)) - + await self.village_channel.add_reaction("👍") await self.village_channel.add_reaction("👎") - + await asyncio.sleep(15) - + reaction_list = message.reactions - + up_votes = sum(p.emoji == "👍" and not p.me for p in reaction_list) down_votes = sum(p.emoji == "👎" and not p.me for p in reaction_list) - + if len(down_votes) > len(up_votes): - embed=discord.Embed(title="Vote Results", color=0xff0000) + embed = discord.Embed(title="Vote Results", color=0xff0000) else: - embed=discord.Embed(title="Vote Results", color=0x80ff80) - + embed = discord.Embed(title="Vote Results", color=0x80ff80) + embed.add_field(name="👎", value="**{}**".format(len(up_votes)), inline=True) embed.add_field(name="👍", value="**{}**".format(len(down_votes)), inline=True) - + await self.village_channel.send(embed=embed) - + if len(down_votes) > len(up_votes): await self.village_channel.send("**Voted to lynch {}!**".format(target.mention)) await self.lynch(target) @@ -256,19 +256,19 @@ class Game: if not self.can_vote: await self._at_day_end() - + async def _at_kill(self, target): # ID 3 if self.game_over: return data = {"player": target} await self._notify(3, data) - + async def _at_hang(self, target): # ID 4 if self.game_over: return data = {"player": target} await self._notify(4, data) - + async def _at_day_end(self): # ID 5 await self._check_game_over() @@ -279,36 +279,36 @@ class Game: self.day_vote = {} self.vote_totals = {} self.day_time = False - + await self.night_perms(self.village_channel) - + await self.village_channel.send(embed=discord.Embed(title="**The sun sets on the village...**")) - + await self._notify(5) await asyncio.sleep(5) await self._at_night_start() - + async def _at_night_start(self): # ID 6 if self.game_over: return await self._notify(6) - + await asyncio.sleep(120) # 2 minutes await self.village_channel.send(embed=discord.Embed(title="**Two minutes of night remain...**")) - await asyncio.sleep(90) # 1.5 minutes + await asyncio.sleep(90) # 1.5 minutes await self.village_channel.send(embed=discord.Embed(title="**Thirty seconds until sunrise...**")) await asyncio.sleep(30) # .5 minutes - + await self._at_night_end() - + async def _at_night_end(self): # ID 7 if self.game_over: return await self._notify(7) - + await asyncio.sleep(15) await self._at_day_start() - + async def _at_visit(self, target, source): # ID 8 if self.game_over: return @@ -316,24 +316,24 @@ class Game: await self._notify(8, data) async def _notify(self, event, data=None): - for i in range(1,7): # action guide 1-6 (0 is no action) + for i in range(1, 7): # action guide 1-6 (0 is no action) tasks = [] # Role priorities - role_order = [role for role in self.roles if role.action_list[event][1]==i] + role_order = [role for role in self.roles if role.action_list[event][1] == i] for role in role_order: tasks.append(asyncio.ensure_future(role.on_event(event, data), loop=self.loop)) # VoteGroup priorities - vote_order = [vg for vg in self.vote_groups.values() if vg.action_list[event][1]==i] + vote_order = [vg for vg in self.vote_groups.values() if vg.action_list[event][1] == i] for vote_group in vote_order: tasks.append(asyncio.ensure_future(vote_group.on_event(event, data), loop=self.loop)) - if tasks: + if tasks: await asyncio.gather(*tasks) # Run same-priority task simultaneously ############END Notify structure############ async def generate_targets(self, channel): - embed=discord.Embed(title="Remaining Players") + embed = discord.Embed(title="Remaining Players") for i in range(len(self.players)): player = self.players[i] if player.alive: @@ -342,7 +342,7 @@ class Game: status = "*Dead*" embed.add_field(name="ID# **{}**".format(i), value="{} {}".format(status, player.member.display_name), inline=True) - + return await channel.send(embed=embed) async def register_channel(self, channel_id, role, votegroup=None): @@ -353,9 +353,9 @@ class Game: self.p_channels[channel_id] = self.default_secret_channel.copy() await asyncio.sleep(1) # This will have multiple calls - + self.p_channels[channel_id]["players"].append(role.player) - + if votegroup: self.p_channels[channel_id]["votegroup"] = votegroup @@ -365,23 +365,23 @@ class Game: """ if self.started: await channel.send("**Game has already started!**") - return - + return + if await self.get_player_by_member(member) is not None: await channel.send("{} is already in the game!".format(member.mention)) - return - + return + self.players.append(Player(member)) - + await channel.send("{} has been added to the game, " "total players is **{}**".format(member.mention, len(self.players))) - + async def quit(self, member: discord.Member, channel: discord.TextChannel = None): """ Have a member quit a game """ player = await self.get_player_by_member(member) - + if player is None: return "You're not in a game!" @@ -391,22 +391,22 @@ class Game: else: self.players = [player for player in self.players if player.member != member] await channel.send("{} chickened out, player count is now **{}**".format(member.mention, len(self.players))) - + async def choose(self, ctx, data): """ Arbitrary decision making Example: seer picking target to see """ player = await self.get_player_by_member(ctx.author) - + if player is None: await ctx.send("You're not in this game!") return - + if not player.alive: await ctx.send("**Corpses** can't vote...") return - + if player.role.blocked: await ctx.send("Something is preventing you from doing this...") return @@ -419,7 +419,7 @@ class Game: async def _visit(self, target, source): await target.role.visit(source) await self._at_visit(target, source) - + async def visit(self, target_id, source): """ Night visit target_id @@ -438,15 +438,15 @@ class Game: Also used in vote groups """ player = await self.get_player_by_member(author) - + if player is None: await channel.send("You're not in this game!") return - + if not player.alive: await channel.send("Corpses can't vote") return - + if channel == self.village_channel: if not self.can_vote: await channel.send("Voting is not allowed right now") @@ -462,11 +462,11 @@ class Game: target = self.players[target_id] except IndexError: target = None - + if target is None: await channel.send("Not a valid ID") return - + # Now handle village vote or send to votegroup if channel == self.village_channel: await self._village_vote(target, author, target_id) @@ -475,19 +475,19 @@ class Game: else: # Somehow previous check failed await channel.send("Cannot vote in this channel") return - + async def _village_vote(self, target, author, target_id): if author in self.day_vote: self.vote_totals[self.day_vote[author]] -= 1 - + self.day_vote[author] = target_id if target_id not in self.vote_totals: self.vote_totals[target_id] = 1 else: self.vote_totals[target_id] += 1 - + required_votes = len([player for player in self.players if player.alive]) // 7 + 2 - + if self.vote_totals[target_id] < required_votes: await self.village_channel.send("" "{} has voted to put {} to trial. " @@ -503,7 +503,7 @@ class Game: if method is not None: out = "**{ID}** - " + method return out.format(ID=target.id, target=target.member.display_name) - else: + else: return "**{ID}** - {target} was found dead".format(ID=target.id, target=target.member.display_name) async def _quit(self, player): @@ -517,29 +517,29 @@ class Game: await self.dead_perms(self.village_channel, player.member) # Add a punishment system for quitting games later - async def kill(self, target_id, source=None, method: str=None, novisit=False): + async def kill(self, target_id, source=None, method: str = None, novisit=False): """ Attempt to kill a target Source allows admin override Be sure to remove permissions appropriately Important to finish execution before triggering notify """ - + if source is None: target = self.players[target_id] elif self.day_time: target = self.get_day_target(target_id, source) else: target = await self.get_night_target(target_id, source) - if source is not None: + if source is not None: if source.role.blocked: # Do nothing if blocked, blocker handles text - return - + return + if not novisit: # Arsonist wouldn't visit before killing await self._visit(target, source) # Visit before killing - + if not target.protected: target.alive = False await target.kill(source) @@ -550,8 +550,8 @@ class Game: await self.dead_perms(self.village_channel, target.member) else: target.protected = False - - async def lynch(self, target_id): + + async def lynch(self, target_id): """ Attempt to lynch a target Important to finish execution before triggering notify @@ -561,67 +561,67 @@ class Game: await self._at_hang(target) if not target.alive: # Still dead after notifying await self.dead_perms(self.village_channel, target.member) - + async def get_night_target(self, target_id, source=None): return self.players[target_id] # For now - + async def get_day_target(self, target_id, source=None): return self.players[target_id] # For now async def get_roles(self, game_code=None): if game_code is not None: self.game_code = game_code - + if self.game_code is None: return False - + self.roles = await parse_code(self.game_code) - + if not self.roles: return False - + async def assign_roles(self): """len(self.roles) must == len(self.players)""" random.shuffle(self.roles) self.players.sort(key=lambda pl: pl.member.display_name.lower()) - + if len(self.roles) != len(self.players): await self.village_channel("Unhandled error - roles!=players") return False - + for idx, role in enumerate(self.roles): self.roles[idx] = role(self) await self.roles[idx].assign_player(self.players[idx]) # Sorted players, now assign id's await self.players[idx].assign_id(idx) - + async def get_player_by_member(self, member): for player in self.players: if player.member == member: return player return None - + async def dead_perms(self, channel, member): await channel.set_permissions(member, read_messages=True, send_message=False, add_reactions=False) - + async def night_perms(self, channel): await channel.set_permissions(self.guild.default_role, read_messages=False, send_messages=False) - + async def day_perms(self, channel): await channel.set_permissions(self.guild.default_role, read_messages=False) - + async def speech_perms(self, channel, member, undo=False): if undo: await channel.set_permissions(member, read_messages=True) else: await channel.set_permissions(self.guild.default_role, read_messages=False, send_messages=False) await channel.set_permissions(member, read_messages=True, send_messages=True) - + async def normal_perms(self, channel, member_list): await channel.set_permissions(self.guild.default_role, read_messages=False) for member in member_list: await channel.set_permissions(member, read_messages=True) - + async def _check_game_over(self): # ToDo pass diff --git a/werewolf/info.json b/werewolf/info.json index 2f58d46..d46e1d2 100644 --- a/werewolf/info.json +++ b/werewolf/info.json @@ -1,10 +1,23 @@ { - "author" : ["Bobloy"], - "bot_version" : [3,0,0], - "description" : "Customizable Werewolf Game", - "hidden" : false, - "install_msg" : "Thank you for installing Werewolf! Use [p]wwset to run inital setup", - "requirements" : [], - "short" : "Werewolf Game", - "tags" : ["mafia", "werewolf", "party", "fun", "game", "bobloy"] + "author": [ + "Bobloy" + ], + "bot_version": [ + 3, + 0, + 0 + ], + "description": "Customizable Werewolf Game", + "hidden": false, + "install_msg": "Thank you for installing Werewolf! Use [p]wwset to run inital setup", + "requirements": [], + "short": "Werewolf Game", + "tags": [ + "mafia", + "werewolf", + "party", + "fun", + "game", + "bobloy" + ] } \ No newline at end of file diff --git a/werewolf/player.py b/werewolf/player.py index 3f244c2..78710aa 100644 --- a/werewolf/player.py +++ b/werewolf/player.py @@ -11,7 +11,7 @@ class Player: self.mention = member.mention self.role = None self.id = None - + self.alive = True self.muted = False self.protected = False @@ -22,9 +22,9 @@ class Player: """ role.player = self self.role = role - + async def assign_id(self, target_id): self.id = target_id async def send_dm(self, message): - await self.member.send(message) # Lets do embeds later + await self.member.send(message) # Lets do embeds later diff --git a/werewolf/role.py b/werewolf/role.py index e8f4e76..64c78a4 100644 --- a/werewolf/role.py +++ b/werewolf/role.py @@ -35,24 +35,24 @@ class Role: 5. Disruptive actions (Killing) 6. Role altering actions (Cult / Mason) """ - + rand_choice = False # Determines if it can be picked as a random role (False for unusually disruptive roles) - category = [0] # List of enrolled categories (listed above) - alignment = 0 # 1: Town, 2: Werewolf, 3: Neutral - channel_id = "" # Empty for no private channel - unique = False # Only one of this role per game + category = [0] # List of enrolled categories (listed above) + alignment = 0 # 1: Town, 2: Werewolf, 3: Neutral + channel_id = "" # Empty for no private channel + unique = False # Only one of this role per game game_start_message = ( "Your role is **Default**\n" "You win by testing the game\n" "Lynch players during the day with `[p]ww vote `" - ) + ) def __init__(self, game): self.game = game self.player = None self.blocked = False self.properties = {} # Extra data for other roles (i.e. arsonist) - + self.action_list = [ (self._at_game_start, 1), # (Action, Priority) (self._at_day_start, 0), @@ -63,13 +63,13 @@ class Role: (self._at_night_start, 0), (self._at_night_end, 0), (self._at_visit, 0) - ] - + ] + async def on_event(self, event, data): """ See Game class for event guide """ - + await self.action_list[event][0](data) async def assign_player(self, player): @@ -80,7 +80,7 @@ class Role: player.role = self self.player = player - + async def get_alignment(self, source=None): """ Interaction for powerful access of alignment @@ -88,58 +88,58 @@ class Role: Unlikely to be able to deceive this """ return self.alignment - + async def see_alignment(self, source=None): """ Interaction for investigative roles attempting to see alignment (Village, Werewolf Other) """ return "Other" - + async def get_role(self, source=None): """ Interaction for powerful access of role Unlikely to be able to deceive this """ return "Default" - + async def see_role(self, source=None): """ Interaction for investigative roles. More common to be able to deceive this action """ return "Role" - + async def _at_game_start(self, data=None): if self.channel_id: await self.game.register_channel(self.channel_id, self) - - await self.player.send_dm(self.game_start_message) #Maybe embeds eventually - + + await self.player.send_dm(self.game_start_message) # Maybe embeds eventually + async def _at_day_start(self, data=None): pass - + async def _at_voted(self, data=None): pass - + async def _at_kill(self, data=None): pass - + async def _at_hang(self, data=None): pass - + async def _at_day_end(self, data=None): pass - + async def _at_night_start(self, data=None): pass - + async def _at_night_end(self, data=None): pass - + async def _at_visit(self, data=None): pass - + async def kill(self, source): """ Called when someone is trying to kill you! diff --git a/werewolf/roles/seer.py b/werewolf/roles/seer.py index efa5d1f..e0de559 100644 --- a/werewolf/roles/seer.py +++ b/werewolf/roles/seer.py @@ -1,18 +1,18 @@ from werewolf.role import Role + class Seer(Role): - rand_choice = False # Determines if it can be picked as a random role (False for unusually disruptive roles) - category = [1,2] # List of enrolled categories (listed above) - alignment = 1 # 1: Town, 2: Werewolf, 3: Neutral - channel_id = "" # Empty for no private channel - unique = False # Only one of this role per game - game_start_message=( - "Your role is **Seer**\n" - "You win by lynching all evil in the town\n" - "Lynch players during the day with `[p]ww vote `\n" - "Check for werewolves at night with `[p]ww choose `" - ) + category = [1, 2] # List of enrolled categories (listed above) + alignment = 1 # 1: Town, 2: Werewolf, 3: Neutral + channel_id = "" # Empty for no private channel + unique = False # Only one of this role per game + game_start_message = ( + "Your role is **Seer**\n" + "You win by lynching all evil in the town\n" + "Lynch players during the day with `[p]ww vote `\n" + "Check for werewolves at night with `[p]ww choose `" + ) def __init__(self, game): super().__init__() @@ -30,32 +30,32 @@ class Seer(Role): (self._at_day_end, 0), (self._at_night_start, 2), (self._at_night_end, 4) - ] - + ] + # async def on_event(self, event, data): - # """ - # See Game class for event guide - # """ - - # await self.action_list[event][0](data) - - + # """ + # See Game class for event guide + # """ + # + # await self.action_list[event][0](data) + # + # # async def assign_player(self, player): - # """ - # Give this role a player - # Can be used after the game has started (Cult, Mason, other role swap) - # """ - - # player.role = self - # self.player = player - + # """ + # Give this role a player + # Can be used after the game has started (Cult, Mason, other role swap) + # """ + # + # player.role = self + # self.player = player + # # async def get_alignment(self, source=None): - # """ - # Interaction for power access of team (Village, Werewolf, Other) - # Unlikely to be able to deceive this - # """ - # return self.alignment - + # """ + # Interaction for power access of team (Village, Werewolf, Other) + # Unlikely to be able to deceive this + # """ + # return self.alignment + async def see_alignment(self, source=None): """ Interaction for investigative roles attempting @@ -69,44 +69,43 @@ class Seer(Role): Unlikely to be able to deceive this """ return "Villager" - + async def _see_role(self, source=None): """ Interaction for investigative roles. More common to be able to deceive these roles """ return "Villager" - + # async def _at_game_start(self, data=None): - # pass - + # pass + # # async def _at_day_start(self, data=None): - # pass - + # pass + # # async def _at_voted(self, target=None): - # pass - + # pass + # # async def _at_kill(self, target=None): - # pass - + # pass + # # async def _at_hang(self, target=None): - # pass - + # pass + # # async def _at_day_end(self): - # pass - + # pass + async def _at_night_start(self, data=None): await self.game.generate_targets(self.player.member) await self.player.send_dm("{}\n**Pick a target to see tonight**\n") - - + async def _at_night_end(self, data=None): target = await self.game.visit(self.see_target) - + alignment = None if target: alignment = await target.see_alignment(self.player) - + if alignment == "Werewolf": out = "Your insight reveals this player to be a **Werewolf!**" else: @@ -115,23 +114,23 @@ class Seer(Role): await self.player.send_dm(out) # async def _at_visit(self, data=None): - # pass - + # pass + # # async def kill(self, source): - # """ - # Called when someone is trying to kill you! - # Can you do anything about it? - # self.alive is now set to False, set to True to stay alive - # """ - # pass - + # """ + # Called when someone is trying to kill you! + # Can you do anything about it? + # self.alive is now set to False, set to True to stay alive + # """ + # pass + # # async def visit(self, source): - # """ - # Called whenever a night action targets you - # Source is the player who visited you - # """ - # pass - + # """ + # Called whenever a night action targets you + # Source is the player who visited you + # """ + # pass + async def choose(self, ctx, data): """Handle night actions""" id = int(data) @@ -139,10 +138,10 @@ class Seer(Role): target = self.game.players[id] except IndexError: target = None - + if target is None: await ctx.send("Not a valid ID") return - + self.see_target = id await ctx.send("**You will attempt to see the role of {} tonight...**".format(target.member.display_name)) diff --git a/werewolf/roles/vanillawerewolf.py b/werewolf/roles/vanillawerewolf.py index 5c7de38..a846edf 100644 --- a/werewolf/roles/vanillawerewolf.py +++ b/werewolf/roles/vanillawerewolf.py @@ -4,10 +4,9 @@ from werewolf.votegroups.wolfvote import WolfVote class VanillaWerewolf(Role): - rand_choice = True category = [11, 15] - alignment = 2 # 1: Town, 2: Werewolf, 3: Neutral + alignment = 2 # 1: Town, 2: Werewolf, 3: Neutral channel_id = "werewolves" unique = False game_start_message = ( @@ -15,13 +14,11 @@ class VanillaWerewolf(Role): "You win by killing everyone else in the village\n" "Lynch players during the day with `[p]ww vote `\n" "Vote to kill players at night with `[p]ww vote `" - ) - - - + ) + def __init__(self, game): super().__init__(game) - + self.action_list = [ (self._at_game_start, 1), # (Action, Priority) (self._at_day_start, 0), @@ -32,98 +29,97 @@ class VanillaWerewolf(Role): (self._at_night_start, 0), (self._at_night_end, 0), (self._at_visit, 0) - ] - + ] + # async def on_event(self, event, data): - # """ - # See Game class for event guide - # """ - - # await self.action_list[event][0](data) - + # """ + # See Game class for event guide + # """ + + # await self.action_list[event][0](data) + # async def assign_player(self, player): - # """ - # Give this role a player - # Can be used after the game has started (Cult, Mason, role swap) - # """ - - # player.role = self - # self.player = player - + # """ + # Give this role a player + # Can be used after the game has started (Cult, Mason, role swap) + # """ + + # player.role = self + # self.player = player + # async def get_alignment(self, source=None): - # """ - # Interaction for power access of team (Village, Werewolf, Other) - # Unlikely to be able to deceive this - # """ - # return self.alignment - + # """ + # Interaction for power access of team (Village, Werewolf, Other) + # Unlikely to be able to deceive this + # """ + # return self.alignment + async def see_alignment(self, source=None): """ Interaction for investigative roles attempting to see team (Village, Werewolf Other) """ - return "Werewolf" - + return "Werewolf" + async def _get_role(self, source=None): """ Interaction for powerful access of role Unlikely to be able to deceive this """ return "Werewolf" - + async def _see_role(self, source=None): """ Interaction for investigative roles. More common to be able to deceive these roles """ return "Werewolf" - + async def _at_game_start(self, data=None): if self.channel_id: - print("Wolf has channel_id: "+self.channel_id) + print("Wolf has channel_id: " + self.channel_id) await self.game.register_channel(self.channel_id, self, WolfVote) # Add VoteGroup WolfVote - + await self.player.send_dm(self.game_start_message) - # async def _at_day_start(self, data=None): - # super()._at_day_start(data) - + # super()._at_day_start(data) + # async def _at_voted(self, data=None): - # super()._at_voted(data) - + # super()._at_voted(data) + # async def _at_kill(self, data=None): - # super()._at_kill(data) - + # super()._at_kill(data) + # async def _at_hang(self, data=None): - # super()._at_hang(data) - + # super()._at_hang(data) + # async def _at_day_end(self, data=None): - # super()._at_day_end(data) - + # super()._at_day_end(data) + # async def _at_night_start(self, data=None): - # super()._at_night_start(data) - + # super()._at_night_start(data) + # async def _at_night_end(self, data=None): - # super()._at_night_end(data) - + # super()._at_night_end(data) + # async def _at_visit(self, data=None): - # pass - + # pass + # async def kill(self, source): - # """ - # Called when someone is trying to kill you! - # Can you do anything about it? - # self.alive is now set to False, set to True to stay alive - # """ - # pass - + # """ + # Called when someone is trying to kill you! + # Can you do anything about it? + # self.alive is now set to False, set to True to stay alive + # """ + # pass + # async def visit(self, source): - # """ - # Called whenever a night action targets you - # Source is the player who visited you - # """ - # pass + # """ + # Called whenever a night action targets you + # Source is the player who visited you + # """ + # pass async def choose(self, ctx, data): """Handle night actions""" diff --git a/werewolf/roles/villager.py b/werewolf/roles/villager.py index 0b62292..5cf7f78 100644 --- a/werewolf/roles/villager.py +++ b/werewolf/roles/villager.py @@ -1,17 +1,17 @@ from werewolf.role import Role + class Villager(Role): - rand_choice = False # Determines if it can be picked as a random role (False for unusually disruptive roles) - category = [1] # List of enrolled categories (listed above) - alignment = 1 # 1: Town, 2: Werewolf, 3: Neutral - channel_id = "" # Empty for no private channel - unique = False # Only one of this role per game - game_start_message=( - "Your role is **Villager**\n" - "You win by lynching all evil in the town\n" - "Lynch players during the day with `[p]ww vote `" - ) + category = [1] # List of enrolled categories (listed above) + alignment = 1 # 1: Town, 2: Werewolf, 3: Neutral + channel_id = "" # Empty for no private channel + unique = False # Only one of this role per game + game_start_message = ( + "Your role is **Villager**\n" + "You win by lynching all evil in the town\n" + "Lynch players during the day with `[p]ww vote `" + ) def __init__(self, game): super().__init__() @@ -19,43 +19,43 @@ class Villager(Role): # self.player = None # self.blocked = False # self.properties = {} # Extra data for other roles (i.e. arsonist) - + # # self.action_list = [ - # (self._at_game_start, 0), # (Action, Priority) - # (self._at_day_start, 0), - # (self._at_voted, 0), - # (self._at_kill, 0), - # (self._at_hang, 0), - # (self._at_day_end, 0), - # (self._at_night_start, 0), - # (self._at_night_end, 0), - # (self._at_visit, 0) - # ] - + # (self._at_game_start, 0), # (Action, Priority) + # (self._at_day_start, 0), + # (self._at_voted, 0), + # (self._at_kill, 0), + # (self._at_hang, 0), + # (self._at_day_end, 0), + # (self._at_night_start, 0), + # (self._at_night_end, 0), + # (self._at_visit, 0) + # ] + # async def on_event(self, event, data): - # """ - # See Game class for event guide - # """ - - # await self.action_list[event][0](data) - - + # """ + # See Game class for event guide + # """ + # + # await self.action_list[event][0](data) + # + # # async def assign_player(self, player): - # """ - # Give this role a player - # Can be used after the game has started (Cult, Mason, other role swap) - # """ - - # player.role = self - # self.player = player - + # """ + # Give this role a player + # Can be used after the game has started (Cult, Mason, other role swap) + # """ + # + # player.role = self + # self.player = player + # # async def get_alignment(self, source=None): - # """ - # Interaction for power access of team (Village, Werewolf, Other) - # Unlikely to be able to deceive this - # """ - # return self.alignment - + # """ + # Interaction for power access of team (Village, Werewolf, Other) + # Unlikely to be able to deceive this + # """ + # return self.alignment + async def see_alignment(self, source=None): """ Interaction for investigative roles attempting @@ -69,56 +69,56 @@ class Villager(Role): Unlikely to be able to deceive this """ return "Villager" - + async def _see_role(self, source=None): """ Interaction for investigative roles. More common to be able to deceive these roles """ return "Villager" - + # async def _at_game_start(self, data=None): - # pass - + # pass + # # async def _at_day_start(self, data=None): - # pass - + # pass + # # async def _at_voted(self, target=None): - # pass - + # pass + # # async def _at_kill(self, target=None): - # pass - + # pass + # # async def _at_hang(self, target=None): - # pass - + # pass + # # async def _at_day_end(self): - # pass - + # pass + # # async def _at_night_start(self): - # pass - + # pass + # # async def _at_night_end(self): - # pass - + # pass + # # async def _at_visit(self, data=None): - # pass - + # pass + # # async def kill(self, source): - # """ - # Called when someone is trying to kill you! - # Can you do anything about it? - # self.alive is now set to False, set to True to stay alive - # """ - # pass - + # """ + # Called when someone is trying to kill you! + # Can you do anything about it? + # self.alive is now set to False, set to True to stay alive + # """ + # pass + # # async def visit(self, source): - # """ - # Called whenever a night action targets you - # Source is the player who visited you - # """ - # pass - + # """ + # Called whenever a night action targets you + # Source is the player who visited you + # """ + # pass + # # async def choose(self, ctx, data): - # """Handle night actions""" - # pass \ No newline at end of file + # """Handle night actions""" + # pass diff --git a/werewolf/votegroup.py b/werewolf/votegroup.py index d1d503d..8d07de6 100644 --- a/werewolf/votegroup.py +++ b/werewolf/votegroup.py @@ -3,9 +3,9 @@ class VoteGroup: Base VoteGroup class for werewolf game Handles secret channels and group decisions """ - - alignment = 0 # 1: Town, 2: Werewolf, 3: Neutral - channel_id = "" + + alignment = 0 # 1: Town, 2: Werewolf, 3: Neutral + channel_id = "" def __init__(self, game, channel): self.game = game @@ -13,7 +13,7 @@ class VoteGroup: self.players = [] self.vote_results = {} self.properties = {} # Extra data for other options - + self.action_list = [ (self._at_game_start, 0), # (Action, Priority) (self._at_day_start, 0), @@ -24,13 +24,13 @@ class VoteGroup: (self._at_night_start, 2), (self._at_night_end, 0), (self._at_visit, 0) - ] - + ] + async def on_event(self, event, data): """ See Game class for event guide """ - + await self.action_list[event][0](data) async def _at_game_start(self, data=None): @@ -38,41 +38,41 @@ class VoteGroup: async def _at_day_start(self, data=None): pass - + async def _at_voted(self, data=None): pass - + async def _at_kill(self, data=None): if data["player"] in self.players: self.players.pop(data["player"]) - + async def _at_hang(self, data=None): if data["player"] in self.players: self.players.pop(data["player"]) async def _at_day_end(self, data=None): pass - + async def _at_night_start(self, data=None): if self.channel is None: return await self.game.generate_targets(self.channel) - + async def _at_night_end(self, data=None): if self.channel is None: return - + target = None vote_list = list(self.vote_results.values()) - + if vote_list: target = max(set(vote_list), key=vote_list.count) - + if target: # Do what you voted on pass - + async def _at_visit(self, data=None): pass @@ -81,7 +81,7 @@ class VoteGroup: Extend players by passed list """ self.players.extend(players) - + async def remove_player(self, player): """ Remove a player from player list @@ -93,5 +93,5 @@ class VoteGroup: """ Receive vote from game """ - + self.vote_results[author.id] = id diff --git a/werewolf/votegroups/wolfvote.py b/werewolf/votegroups/wolfvote.py index 7eea7df..7d539e3 100644 --- a/werewolf/votegroups/wolfvote.py +++ b/werewolf/votegroups/wolfvote.py @@ -7,14 +7,14 @@ class WolfVote(VoteGroup): """ Werewolf implementation of base VoteGroup class """ - - alignment = 2 # 1: Town, 2: Werewolf, 3: Neutral - channel_id = "werewolves" + + alignment = 2 # 1: Town, 2: Werewolf, 3: Neutral + channel_id = "werewolves" kill_messages = [ "**{ID}** - {target} was mauled by wolves", "**{ID}** - {target} was found torn to shreds"] - + def __init__(self, game, channel): super().__init__(game, channel) # self.game = game @@ -35,9 +35,10 @@ class WolfVote(VoteGroup): (self._at_night_start, 2), (self._at_night_end, 5), # Kill priority (self._at_visit, 0) - ] - - # async def on_event(self, event, data): + ] + + # async def on_event(self, event, data): + # """ # See Game class for event guide # """ @@ -63,24 +64,24 @@ class WolfVote(VoteGroup): # # async def _at_day_end(self, data=None): # pass - + async def _at_night_start(self, data=None): if self.channel is None: return - + await self.game.generate_targets(self.channel) await self.channel.send(" ".join(player.mention for player in self.players)) self.killer = random.choice(self.players) - + await self.channel.send("{} has been selected as tonight's killer".format(self.killer.member.display_name)) - + async def _at_night_end(self, data=None): if self.channel is None: return - + target_id = None vote_list = list(self.vote_results.values()) - + if vote_list: target_id = max(set(vote_list), key=vote_list.count) @@ -90,7 +91,7 @@ class WolfVote(VoteGroup): await self.channel.send("**{} has left to complete the kill...**".format(self.killer.member.display_name)) else: await self.channel.send("**No kill will be attempted tonight...**") - + # async def _at_visit(self, data=None): # pass # @@ -111,7 +112,7 @@ class WolfVote(VoteGroup): """ Receive vote from game """ - + self.vote_results[author.id] = target_id - + await self.channel.send("{} has voted to kill {}".format(author.mention, target.member.display_name)) diff --git a/werewolf/werewolf.py b/werewolf/werewolf.py index eb441e6..8ca13bc 100644 --- a/werewolf/werewolf.py +++ b/werewolf/werewolf.py @@ -16,11 +16,11 @@ class Werewolf: default_global = {} default_guild = { "role": None - } + } self.config.register_global(**default_global) self.config.register_guild(**default_guild) - + self.games = {} # Active games stored here, id is per guild @commands.group() @@ -48,16 +48,16 @@ class Werewolf: """ if ctx.invoked_subcommand is None: await ctx.send_help() - + @commands.guild_only() @ww.command() async def new(self, ctx, game_code): """ Create and join a new game of Werewolf """ - + game = self._get_game(ctx.guild, game_code) - + if not game: await ctx.send("Failed to start a new game") else: @@ -69,26 +69,26 @@ class Werewolf: """ Joins a game of Werewolf """ - + game = self._get_game(ctx.guild) - + if not game: await ctx.send("No game to join!\nCreate a new one with `[p]ww new`") return await game.join(ctx.author, ctx.channel) - + @commands.guild_only() @ww.command() async def quit(self, ctx): """ Quit a game of Werewolf """ - + game = self._get_game(ctx.guild) - + await game.quit(ctx.author, ctx.channel) - + @commands.guild_only() @ww.command() async def start(self, ctx): @@ -98,9 +98,9 @@ class Werewolf: game = self._get_game(ctx.guild) if not game: await ctx.send("No game running, cannot start") - + await game.setup(ctx) - + @commands.guild_only() @ww.command() async def stop(self, ctx): @@ -110,10 +110,10 @@ class Werewolf: game = self._get_game(ctx.guild) if not game: await ctx.send("No game running, cannot stop") - + game.game_over = True - - @commands.guild_only() + + @commands.guild_only() @ww.command() async def vote(self, ctx, id: int): """ @@ -123,31 +123,31 @@ class Werewolf: id = int(id) except: id = None - + if id is None: await ctx.send("`id` must be an integer") return - + # if ctx.guild is None: - # # DM nonsense, find their game - # # If multiple games, panic - # for game in self.games.values(): - # if await game.get_player_by_member(ctx.author): - # break #game = game - # else: - # await ctx.send("You're not part of any werewolf game") - # return + # # DM nonsense, find their game + # # If multiple games, panic + # for game in self.games.values(): + # if await game.get_player_by_member(ctx.author): + # break #game = game + # else: + # await ctx.send("You're not part of any werewolf game") + # return # else: - + game = self._get_game(ctx.guild) - + if game is None: await ctx.send("No game running, cannot vote") return # Game handles response now channel = ctx.channel - if channel == game.village_channel: + if channel == game.village_channel: await game.vote(ctx.author, id, channel) elif channel in (c["channel"] for c in game.p_channels.values()): await game.vote(ctx.author, id, channel) @@ -165,7 +165,7 @@ class Werewolf: if ctx.guild is not None: await ctx.send("This action is only available in DM's") return - + # DM nonsense, find their game # If multiple games, panic for game in self.games.values(): @@ -176,7 +176,7 @@ class Werewolf: return await game.choose(ctx, data) - + def _get_game(self, guild, game_code=None): if guild is None: # Private message, can't get guild