From 9ac6d396a80792527a7c8ba8cec90c10e3dc46b1 Mon Sep 17 00:00:00 2001 From: Bobloy Date: Wed, 9 May 2018 09:42:02 -0400 Subject: [PATCH 1/6] Gotta be RedContext until next beta --- werewolf/builder.py | 12 +++++++----- werewolf/game.py | 5 +++-- werewolf/werewolf.py | 46 ++++++++++++++++++++++---------------------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/werewolf/builder.py b/werewolf/builder.py index c673e3f..fbf5a1c 100644 --- a/werewolf/builder.py +++ b/werewolf/builder.py @@ -6,6 +6,8 @@ import discord from discord.ext import commands # Import all roles here +from redbot.core import RedContext + from werewolf.roles.seer import Seer from werewolf.roles.vanillawerewolf import VanillaWerewolf from werewolf.roles.villager import Villager @@ -188,7 +190,7 @@ async def encode(roles, rand_roles): return out_code -async def next_group(ctx: commands.Context, pages: list, +async def next_group(ctx: RedContext, pages: list, controls: dict, message: discord.Message, page: int, timeout: float, emoji: str): perms = message.channel.permissions_for(ctx.guild.me) @@ -208,7 +210,7 @@ async def next_group(ctx: commands.Context, pages: list, page=page, timeout=timeout) -async def prev_group(ctx: commands.Context, pages: list, +async def prev_group(ctx: RedContext, pages: list, controls: dict, message: discord.Message, page: int, timeout: float, emoji: str): perms = message.channel.permissions_for(ctx.guild.me) @@ -275,7 +277,7 @@ class GameBuilder: self.rand_roles = [] setup() - async def build_game(self, ctx: commands.Context): + async def build_game(self, ctx: RedContext): new_controls = { '⏪': prev_group, "⬅": prev_page, @@ -293,7 +295,7 @@ class GameBuilder: out = await encode(self.code, self.rand_roles) return out - async def list_roles(self, ctx: commands.Context, pages: list, + async def list_roles(self, ctx: RedContext, pages: list, controls: dict, message: discord.Message, page: int, timeout: float, emoji: str): perms = message.channel.permissions_for(ctx.guild.me) @@ -308,7 +310,7 @@ class GameBuilder: return await menu(ctx, pages, controls, message=message, page=page, timeout=timeout) - async def select_page(self, ctx: commands.Context, pages: list, + async def select_page(self, ctx: RedContext, pages: list, controls: dict, message: discord.Message, page: int, timeout: float, emoji: str): perms = message.channel.permissions_for(ctx.guild.me) diff --git a/werewolf/game.py b/werewolf/game.py index 438659f..344f145 100644 --- a/werewolf/game.py +++ b/werewolf/game.py @@ -3,6 +3,7 @@ import random import discord from discord.ext import commands +from redbot.core import RedContext from werewolf.builder import parse_code from werewolf.player import Player @@ -77,7 +78,7 @@ class Game: # for c_data in self.p_channels.values(): # asyncio.ensure_future(c_data["channel"].delete("Werewolf game-over")) - async def setup(self, ctx: commands.Context): + async def setup(self, ctx: RedContext): """ Runs the initial setup @@ -674,7 +675,7 @@ class Game: async def get_day_target(self, target_id, source=None): return self.players[target_id] # ToDo check source - async def set_code(self, ctx: commands.Context, game_code): + async def set_code(self, ctx: RedContext, game_code): if game_code is not None: self.game_code = game_code await ctx.send("Code has been set") diff --git a/werewolf/werewolf.py b/werewolf/werewolf.py index 65e73c1..b53b463 100644 --- a/werewolf/werewolf.py +++ b/werewolf/werewolf.py @@ -1,6 +1,6 @@ import discord from discord.ext import commands -from redbot.core import Config, checks +from redbot.core import Config, checks, RedContext from redbot.core.bot import Red @@ -36,7 +36,7 @@ class Werewolf: del game @commands.command() - async def buildgame(self, ctx: commands.Context): + async def buildgame(self, ctx: RedContext): gb = GameBuilder() code = await gb.build_game(ctx) @@ -47,7 +47,7 @@ class Werewolf: @checks.guildowner() @commands.group() - async def wwset(self, ctx: commands.Context): + async def wwset(self, ctx: RedContext): """ Base command to adjust settings. Check help for command list. """ @@ -56,7 +56,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="list") - async def wwset_list(self, ctx: commands.Context): + async def wwset_list(self, ctx: RedContext): """ Lists current guild settings """ @@ -74,7 +74,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="role") - async def wwset_role(self, ctx: commands.Context, role: discord.Role=None): + async def wwset_role(self, ctx: RedContext, role: discord.Role=None): """ Assign the game role This role should not be manually assigned @@ -88,7 +88,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="category") - async def wwset_category(self, ctx: commands.Context, category_id=None): + async def wwset_category(self, ctx: RedContext, category_id=None): """ Assign the channel category """ @@ -105,7 +105,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="channel") - async def wwset_channel(self, ctx: commands.Context, channel: discord.TextChannel=None): + async def wwset_channel(self, ctx: RedContext, channel: discord.TextChannel=None): """ Assign the village channel """ @@ -118,7 +118,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="logchannel") - async def wwset_log_channel(self, ctx: commands.Context, channel: discord.TextChannel=None): + async def wwset_log_channel(self, ctx: RedContext, channel: discord.TextChannel=None): """ Assign the log channel """ @@ -130,7 +130,7 @@ class Werewolf: await ctx.send("Game Log Channel has been set to **{}**".format(channel.mention)) @commands.group() - async def ww(self, ctx: commands.Context): + async def ww(self, ctx: RedContext): """ Base command for this cog. Check help for the commands list. """ @@ -139,7 +139,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="new") - async def ww_new(self, ctx: commands.Context, game_code=None): + async def ww_new(self, ctx: RedContext, game_code=None): """ Create and join a new game of Werewolf """ @@ -151,7 +151,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="join") - async def ww_join(self, ctx: commands.Context): + async def ww_join(self, ctx: RedContext): """ Joins a game of Werewolf """ @@ -166,7 +166,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="code") - async def ww_code(self, ctx: commands.Context, code): + async def ww_code(self, ctx: RedContext, code): """ Adjust game code """ @@ -181,7 +181,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="quit") - async def ww_quit(self, ctx: commands.Context): + async def ww_quit(self, ctx: RedContext): """ Quit a game of Werewolf """ @@ -192,7 +192,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="start") - async def ww_start(self, ctx: commands.Context): + async def ww_start(self, ctx: RedContext): """ Checks number of players and attempts to start the game """ @@ -205,7 +205,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="stop") - async def ww_stop(self, ctx: commands.Context): + async def ww_stop(self, ctx: RedContext): """ Stops the current game """ @@ -223,7 +223,7 @@ class Werewolf: @commands.guild_only() @ww.command(name="vote") - async def ww_vote(self, ctx: commands.Context, target_id: int): + async def ww_vote(self, ctx: RedContext, target_id: int): """ Vote for a player by ID """ @@ -263,7 +263,7 @@ class Werewolf: await ctx.send("Nothing to vote for in this channel") @ww.command(name="choose") - async def ww_choose(self, ctx: commands.Context, data): + async def ww_choose(self, ctx: RedContext, data): """ Arbitrary decision making Handled by game+role @@ -285,7 +285,7 @@ class Werewolf: await game.choose(ctx, data) @ww.group(name="search") - async def ww_search(self, ctx: commands.Context): + async def ww_search(self, ctx: RedContext): """ Find custom roles by name, alignment, category, or ID """ @@ -293,7 +293,7 @@ class Werewolf: await ctx.send_help() @ww_search.command(name="name") - async def ww_search_name(self, ctx: commands.Context, *, name): + async def ww_search_name(self, ctx: RedContext, *, name): """Search for a role by name""" if name is not None: from_name = role_from_name(name) @@ -303,7 +303,7 @@ class Werewolf: await ctx.send("No roles containing that name were found") @ww_search.command(name="alignment") - async def ww_search_alignment(self, ctx: commands.Context, alignment: int): + async def ww_search_alignment(self, ctx: RedContext, alignment: int): """Search for a role by alignment""" if alignment is not None: from_alignment = role_from_alignment(alignment) @@ -313,7 +313,7 @@ class Werewolf: await ctx.send("No roles with that alignment were found") @ww_search.command(name="category") - async def ww_search_category(self, ctx: commands.Context, category: int): + async def ww_search_category(self, ctx: RedContext, category: int): """Search for a role by category""" if category is not None: pages = role_from_category(category) @@ -323,7 +323,7 @@ class Werewolf: await ctx.send("No roles in that category were found") @ww_search.command(name="index") - async def ww_search_index(self, ctx: commands.Context, idx: int): + async def ww_search_index(self, ctx: RedContext, idx: int): """Search for a role by ID""" if idx is not None: idx_embed = role_from_id(idx) @@ -332,7 +332,7 @@ class Werewolf: else: await ctx.send("Role ID not found") - async def _get_game(self, ctx: commands.Context, game_code=None): + async def _get_game(self, ctx: RedContext, game_code=None): guild: discord.Guild = ctx.guild if guild is None: From deb96a73b6b043e086086b780a376d1ecf772315 Mon Sep 17 00:00:00 2001 From: Bobloy Date: Wed, 9 May 2018 09:43:22 -0400 Subject: [PATCH 2/6] optimize imports and pep8 --- werewolf/builder.py | 9 ++------- werewolf/game.py | 1 - werewolf/player.py | 2 +- werewolf/roles/seer.py | 1 - werewolf/werewolf.py | 10 ++++------ 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/werewolf/builder.py b/werewolf/builder.py index fbf5a1c..eff1f00 100644 --- a/werewolf/builder.py +++ b/werewolf/builder.py @@ -3,15 +3,13 @@ from collections import defaultdict from random import choice import discord -from discord.ext import commands - # Import all roles here from redbot.core import RedContext +from redbot.core.utils.menus import menu, prev_page, next_page, close_menu from werewolf.roles.seer import Seer from werewolf.roles.vanillawerewolf import VanillaWerewolf from werewolf.roles.villager import Villager -from redbot.core.utils.menus import menu, prev_page, next_page, close_menu # All roles in this list for iterating @@ -120,8 +118,6 @@ async def parse_code(code, game): digits += 1 continue - - try: idx = int(built) except ValueError: @@ -146,7 +142,6 @@ async def parse_code(code, game): built = "" - return decode @@ -321,7 +316,7 @@ class GameBuilder: pass if page >= len(ROLE_LIST): - self.rand_roles.append(CATEGORY_COUNT[page-len(ROLE_LIST)]) + self.rand_roles.append(CATEGORY_COUNT[page - len(ROLE_LIST)]) else: self.code.append(page) diff --git a/werewolf/game.py b/werewolf/game.py index 344f145..1848fe6 100644 --- a/werewolf/game.py +++ b/werewolf/game.py @@ -2,7 +2,6 @@ import asyncio import random import discord -from discord.ext import commands from redbot.core import RedContext from werewolf.builder import parse_code diff --git a/werewolf/player.py b/werewolf/player.py index d1f9359..c84d87f 100644 --- a/werewolf/player.py +++ b/werewolf/player.py @@ -30,4 +30,4 @@ class Player: try: await self.member.send(message) # Lets do embeds later except discord.Forbidden: - await self.role.game.village_channel.send("Couldn't DM {}, uh oh".format(self.mention)) \ No newline at end of file + await self.role.game.village_channel.send("Couldn't DM {}, uh oh".format(self.mention)) diff --git a/werewolf/roles/seer.py b/werewolf/roles/seer.py index b005b9a..5c58250 100644 --- a/werewolf/roles/seer.py +++ b/werewolf/roles/seer.py @@ -16,7 +16,6 @@ class Seer(Role): description = "A mystic in search of answers in a chaotic town.\n" \ "Calls upon the cosmos to discern those of Lycan blood" - def __init__(self, game): super().__init__(game) # self.game = game diff --git a/werewolf/werewolf.py b/werewolf/werewolf.py index b53b463..3aee698 100644 --- a/werewolf/werewolf.py +++ b/werewolf/werewolf.py @@ -1,12 +1,11 @@ import discord from discord.ext import commands from redbot.core import Config, checks, RedContext - from redbot.core.bot import Red +from redbot.core.utils.menus import menu, DEFAULT_CONTROLS from werewolf.builder import GameBuilder, role_from_name, role_from_alignment, role_from_category, role_from_id from werewolf.game import Game -from redbot.core.utils.menus import menu, DEFAULT_CONTROLS class Werewolf: @@ -74,7 +73,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="role") - async def wwset_role(self, ctx: RedContext, role: discord.Role=None): + async def wwset_role(self, ctx: RedContext, role: discord.Role = None): """ Assign the game role This role should not be manually assigned @@ -105,7 +104,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="channel") - async def wwset_channel(self, ctx: RedContext, channel: discord.TextChannel=None): + async def wwset_channel(self, ctx: RedContext, channel: discord.TextChannel = None): """ Assign the village channel """ @@ -118,7 +117,7 @@ class Werewolf: @commands.guild_only() @wwset.command(name="logchannel") - async def wwset_log_channel(self, ctx: RedContext, channel: discord.TextChannel=None): + async def wwset_log_channel(self, ctx: RedContext, channel: discord.TextChannel = None): """ Assign the log channel """ @@ -388,4 +387,3 @@ class Werewolf: return False, None, None, None, None return True, role, category, channel, log_channel - From 8200ad51b14863cdd831a96ffdebf625e0c063c1 Mon Sep 17 00:00:00 2001 From: Bobloy Date: Wed, 9 May 2018 13:36:23 -0400 Subject: [PATCH 3/6] more shifting --- werewolf/roles/seer.py | 2 +- werewolf/roles/shifter.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/werewolf/roles/seer.py b/werewolf/roles/seer.py index 5c58250..f4bdcbd 100644 --- a/werewolf/roles/seer.py +++ b/werewolf/roles/seer.py @@ -61,7 +61,7 @@ class Seer(Role): return self.see_target = None await self.game.generate_targets(self.player.member) - await self.player.send_dm("**Pick a target to see tonight**\n") + await self.player.send_dm("**Pick a target to see tonight**") async def _at_night_end(self, data=None): if self.see_target is None: diff --git a/werewolf/roles/shifter.py b/werewolf/roles/shifter.py index 50973ef..1ca5bb2 100644 --- a/werewolf/roles/shifter.py +++ b/werewolf/roles/shifter.py @@ -59,6 +59,7 @@ class Shifter(Role): def __init__(self, game): super().__init__(game) + self.shift_target = None self.action_list = [ (self._at_game_start, 1), # (Action, Priority) (self._at_day_start, 0), @@ -74,7 +75,7 @@ class Shifter(Role): async def see_alignment(self, source=None): """ Interaction for investigative roles attempting - to see alignment (Village, Werewolf Other) + to see alignment (Village, Werewolf, Other) """ return "Other" @@ -90,10 +91,13 @@ class Shifter(Role): Interaction for investigative roles. More common to be able to deceive this action """ - return "MyRole" + return "Shifter" async def _at_night_start(self, data=None): await super()._at_night_start(data) + self.shift_target = None + await self.game.generate_targets(self.player.member) + await self.player.send_dm("**Pick a target to shift into**") async def _at_night_end(self, data=None): await super()._at_night_end(data) From c95b2e4ef29181fc4f98283372338a073c019419 Mon Sep 17 00:00:00 2001 From: Bobloy Date: Thu, 10 May 2018 14:00:19 -0400 Subject: [PATCH 4/6] Expand night powers Signed-off-by: Bobloy --- werewolf/night_powers.py | 18 ++++++++++++++++++ werewolf/roles/seer.py | 17 +++-------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/werewolf/night_powers.py b/werewolf/night_powers.py index ca35e8b..215e8eb 100644 --- a/werewolf/night_powers.py +++ b/werewolf/night_powers.py @@ -3,3 +3,21 @@ from werewolf.role import Role def night_immune(role: Role): role.player.alive = True + + +async def pick_target(role: Role, ctx, data): + if not role.player.alive: # FixMe: Game handles this? + await role.player.send_dm("You're already dead!") + return None + + target_id = int(data) + try: + target = role.game.players[target_id] + except IndexError: + target = None + + if target is None: + await ctx.send("Not a valid ID") + return None + + return target_id, target diff --git a/werewolf/roles/seer.py b/werewolf/roles/seer.py index f4bdcbd..63b62a2 100644 --- a/werewolf/roles/seer.py +++ b/werewolf/roles/seer.py @@ -1,3 +1,4 @@ +from werewolf.night_powers import pick_target from werewolf.role import Role @@ -83,19 +84,7 @@ class Seer(Role): async def choose(self, ctx, data): """Handle night actions""" - if not self.player.alive: # FixMe: Game handles this? - await self.player.send_dm("You're already dead!") - return - - target_id = int(data) - try: - target = self.game.players[target_id] - except IndexError: - target = None - - if target is None: - await ctx.send("Not a valid ID") - return + await super().choose(ctx, data) - self.see_target = target_id + self.see_target, target = await pick_target(self, ctx, data) await ctx.send("**You will attempt to see the role of {} tonight...**".format(target.member.display_name)) From 6a977000a28cf458720774a5ea26fd54a2d52baa Mon Sep 17 00:00:00 2001 From: bobloy Date: Thu, 10 May 2018 14:00:52 -0400 Subject: [PATCH 5/6] Additional type hints Signed-off-by: Bobloy --- werewolf/game.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/werewolf/game.py b/werewolf/game.py index 1848fe6..cc579ca 100644 --- a/werewolf/game.py +++ b/werewolf/game.py @@ -1,17 +1,23 @@ import asyncio import random +from typing import List, Any, Dict, Set, Union import discord from redbot.core import RedContext from werewolf.builder import parse_code from werewolf.player import Player +from werewolf.role import Role +from werewolf.votegroup import VoteGroup class Game: """ Base class to run a single game of Werewolf """ + vote_groups: Dict[str, VoteGroup] + roles: List[Role] + players: List[Player] default_secret_channel = { "channel": None, From 0d5353704061f6a1cf317caee419d155d4425342 Mon Sep 17 00:00:00 2001 From: Bobloy Date: Thu, 10 May 2018 14:04:12 -0400 Subject: [PATCH 6/6] Shifter power Signed-off-by: Bobloy --- werewolf/roles/shifter.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/werewolf/roles/shifter.py b/werewolf/roles/shifter.py index 1ca5bb2..d7ba956 100644 --- a/werewolf/roles/shifter.py +++ b/werewolf/roles/shifter.py @@ -1,3 +1,4 @@ +from werewolf.night_powers import pick_target from werewolf.role import Role @@ -101,7 +102,28 @@ class Shifter(Role): async def _at_night_end(self, data=None): await super()._at_night_end(data) + if self.shift_target is None: + if self.player.alive: + await self.player.send_dm("You will not use your powers tonight...") + return + target = await self.game.visit(self.shift_target, self.player) + if target and target.player.alive: + await target.role.assign_player(self.player) + await self.assign_player(target) + + # Roles have now been swapped + + await self.player.send_dm("Your role has been stolen...\n" + "You are now a **Shifter**.") + await self.player.send_dm(self.game_start_message) + + await target.send_dm(target.role.game_start_message) + else: + await self.player.send_dm("**Your shift failed...**") async def choose(self, ctx, data): """Handle night actions""" await super().choose(ctx, data) + + self.shift_target, target = await pick_target(self, ctx, data) + await ctx.send("**You will attempt to see the role of {} tonight...**".format(target.member.display_name))