diff --git a/werewolf/Game.py b/werewolf/Game.py index b89537b..79ffe7d 100644 --- a/werewolf/Game.py +++ b/werewolf/Game.py @@ -4,6 +4,8 @@ import discord from datetime import datetime,timedelta +from random import shuffle + from .builder import parse_code class Game: @@ -19,7 +21,7 @@ class Game: self.get_roles() self.players = [] - self.start_vote = 0 + self.day_vote = {} # ID, votes self.started = False self.game_over = False @@ -56,7 +58,7 @@ class Game: _at_start() _at_day_start() - _at_vote() + _at_voted() _at_kill() _at_day_end() _at_night_begin() @@ -82,7 +84,7 @@ class Game: asyncio.sleep(240) # 4 minute days await self._at_day_end() - async def _at_vote(self, target): # ID 2 + async def _at_voted(self, target): # ID 2 if self.game_over: return await self._notify(2, target) @@ -129,14 +131,17 @@ class Game: async def _notify(self, event): for i in range(10): tasks = [] - role_order = [role for role in self.roles if role.priority==i] + role_order = [role for role in self.roles if role.action_list[event][1]==i] for role in role_action: tasks.append(asyncio.ensure_future(role.on_event(event)) # self.loop.create_task(role.on_event(event)) self.loop.run_until_complete(asyncio.gather(*tasks)) + # Run same-priority task simultaneously + + async def _generate_targets(self): - async def join(self, member: discord.Member): + async def join(self, member: discord.Member, channel: discord.Channel): """ Have a member join a game """ @@ -148,7 +153,7 @@ class Game: self.started.append(member) - out = "{} has been added to the game, total players is **{}**".format(member.mention, len(self.players)) + channel.send("{} has been added to the game, total players is **{}**".format(member.mention, len(self.players))) async def quit(self, member: discord.Member): """ @@ -167,8 +172,23 @@ class Game: self.started.append(member) - out = "{} has been added to the game, total players is **{}**".format(member.mention, len(self.players)) + channel.send("{} has been added to the game, total players is **{}**".format(member.mention, len(self.players))) + + async def vote(self, author, id, channel): + """ + Member attempts to cast a vote (usually to lynch) + """ + player = self._get_player(author) + if player is None: + channel.send("You're not in this game!") + + if not player.alive: + channel.send("Corpses can't vote") + + try: + target = self.players[id] + except IndexError async def get_roles(self, role_code=None): @@ -181,4 +201,5 @@ class Game: self.roles = await parse_code(self.role_code) if not self.roles: - return False \ No newline at end of file + return False + diff --git a/werewolf/Player.py b/werewolf/Player.py index f09d378..72b628f 100644 --- a/werewolf/Player.py +++ b/werewolf/Player.py @@ -12,7 +12,7 @@ class Player: def __init__(self, member: discord.Member): self.user = member self.role = None - self.id = -1 + self.id = None self.alive = True self.muted = False @@ -22,4 +22,5 @@ class Player: """ Give this player a role """ + role.player = self self.role = role diff --git a/werewolf/Role.py b/werewolf/Role.py index f461177..3ed44fd 100644 --- a/werewolf/Role.py +++ b/werewolf/Role.py @@ -8,40 +8,27 @@ class Role: """ Base Role class for werewolf game - Category enrollment guide as follows: - - Town: - 1: Random, 2: Investigative, 3: Protective, 4: Government, - 5: Killing, 6: Power (Special night action) - - Werewolf: - 11: Random, 12: Deception, 15: Killing, 16: Support - - Neutral: - 21: Benign, 22: Evil, 23: Killing - - - Example category: - category = [1, 5, 6] Could be Veteran - category = [1, 5] Could be Bodyguard - """ - - random_choice = True # Determines if it can be picked as a random - category = [0] # List of enrolled categories - priority = 0 # 0 is "No Action" - allignment = 0 # 1: Town, 2: Werewolf, 3: Neutral - private_channel_id = "" # No private channel - unique = False # Only one of this role per game - - def __init__(self): - self.player = None - self.blocked = False + Category enrollment guide as follows (category property): + Town: + 1: Random, 2: Investigative, 3: Protective, 4: Government, + 5: Killing, 6: Power (Special night action) + + Werewolf: + 11: Random, 12: Deception, 15: Killing, 16: Support + + Neutral: + 21: Benign, 22: Evil, 23: Killing - async def on_event(self, event, data): - """ - Action guide as follows: + Example category: + category = [1, 5, 6] Could be Veteran + category = [1, 5] Could be Bodyguard + category = [11, 16] Could be Werewolf Silencer + + + Action guide as follows (on_event function): _at_night_start + 0. No Action 1. Detain actions (Jailer/Kidnapper) 2. Group discussions and Pick targets @@ -52,30 +39,48 @@ class Role: 4. Non-disruptive actions (seer/silencer) 5. Disruptive actions (werewolf kill) 6. Role altering actions (Cult / Mason) - """ + """ + + rand_choice = True # Determines if it can be picked as a random + category = [0] # List of enrolled categories + allignment = 0 # 1: Town, 2: Werewolf, 3: Neutral + channel_id = "" # No private channel + unique = False # Only one of this role per game + 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) + ] + + def __init__(self): + self.player = None + self.blocked = False + self.properties = {} # Extra data for other roles (i.e. arsonist) + async def on_event(self, event, data): """ See Game class for event guide """ - action_list = [ - self._at_game_start(data), - self._at_day_start(data), - self._at_vote(data), - self._at_kill(data), - self._at_hang(data), - self._at_day_end(data), - self._at_night_start(data), - self._at_night_end(data) - ] - await action_list[event] + await 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 + + async def _see_role(self, source=None): + return async def _at_game_start(self, data=None): pass @@ -83,7 +88,7 @@ class Role: async def _at_day_start(self, data=None): pass - async def _at_vote(self, target=None): + async def _at_voted(self, target=None): pass async def _at_kill(self, target=None): diff --git a/werewolf/Werewolf.py b/werewolf/Werewolf.py index a397027..5ba16c1 100644 --- a/werewolf/Werewolf.py +++ b/werewolf/Werewolf.py @@ -34,7 +34,7 @@ class Werewolf: """ if ctx.invoked_subcommand is None: await ctx.send_help() - + @ww.command() async def join(self, ctx, role_code): """ @@ -47,8 +47,8 @@ class Werewolf: ctx.send("Please provide a role code to get started!") return - ctx.send(await game.join(ctx.author)) - + await game.join(ctx.author, ctx.channel) + @ww.command() async def quit(self, ctx): """ @@ -57,9 +57,24 @@ class Werewolf: game = self._get_game(ctx.guild) - out = await game.quit(ctx.author) + await game.quit(ctx.author, ctx.channel) + + @ww.command() + async def vote(self, ctx, id): + """ + Vote for a player by ID + """ + game = self._get_game(guild) + if not game: + ctx.send("No game running, cannot vote") - ctx.send(out) + # Game handles response now + channel = ctx.channel + if channel is game.village_channel: + await game.vote(ctx.author, id, channel) + + if channel in (c for id,c in game.secret_channels.items()): + await game.vote(ctx.author, id, channel) def _get_game(self, guild, role_code = None): if guild.id not in self.games: @@ -81,9 +96,7 @@ class Werewolf: author = message.author channel = message.channel guild = message.guild - game = self._get_game(guild) - if not game: - return + if channel is game.village_channel: