import asyncio import discord from datetime import datetime,timedelta from random import shuffle from .builder import parse_code class Game: """ Base class to run a single game of Werewolf """ def __new__(cls, game_code): game_code = ["DefaultWerewolf", "Villager", "Villager""] return Game(game_code) def __init__(self, game_code): self.roles = [] self.game_code = game_code if self.game_code: self.get_roles() self.players = [] self.day_vote = {} # ID, votes self.started = False self.game_over = False self.can_vote = False self.village_channel = None self.secret_channels = {} self.vote_groups = [] self.loop = asyncio.get_event_loop() async def setup(self, ctx): """ Runs the initial setup 1. Assign Roles 2. Create Channels 2a. Channel Permissions :eyes: 3. Check Initial role setup (including alerts) 4. Start game """ if len(self.players) != self.roles: ctx.send("Players does not match roles, cannot start") return False ############START Notify structure############ async def _cycle(self): """ Each event calls the next event _at_start() _at_day_start() _at_voted() _at_kill() _at_day_end() _at_night_begin() _at_night_end() and repeat with _at_morning_start() again """ await self._at_start(): async def _at_game_start(self): # ID 0 if self.game_over: return await self._notify(0) asyncio.sleep(60) await self._at_day_start() async def _at_day_start(self): # ID 1 if self.game_over: return await self._notify(1) self.can_vote = True asyncio.sleep(240) # 4 minute days await self._at_day_end() async def _at_voted(self, target): # ID 2 if self.game_over: return await self._notify(2, target) async def _at_kill(self, target): # ID 3 if self.game_over: return await self._notify(3, target) async def _at_hang(self, target): # ID 4 if self.game_over: return await self._notify(4, target) async def _at_day_end(self): # ID 5 if self.game_over: return await self._notify(5) self.can_vote = False asyncio.sleep(30) await self._at_night_start() async def _at_night_start(self): # ID 6 if self.game_over: return await self._notify(6) asyncio.sleep(120) # 2 minutes asyncio.sleep(90) # 1.5 minutes 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) asyncio.sleep(15) await self._at_day_start() async def _notify(self, event, data=None): for i in range(10): tasks = [] # Role priorities 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)) # VoteGroup priorities vote_order = [votes for votes in self.vote_groups if votes.action_list[event][1]==i] for vote_group in vote_order: tasks.append(asyncio.ensure_future(vote_group.on_event(event, data)) # self.loop.create_task(role.on_event(event)) self.loop.run_until_complete(asyncio.gather(*tasks)) # Run same-priority task simultaneously ############END Notify structure############ async def generate_targets(self, channel): embed=discord.Embed(title="Remaining Players") for i in range(len(self.players)): player = self.players[i] if player.alive: status="" else: 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, votegroup = None): # Create channel, set permissions, register votegroup if channel_id not in self.secret_channels: async def register_vote_group(self, channel_id, votegroup = None): # Register channel and assign passed votegroup to it if channel_id not in self.secret_channels: await self.register_channel(channel_id, votegroup(self)) async def join(self, member: discord.Member, channel: discord.Channel): """ Have a member join a game """ if self.started: await channel.send("**Game has already started!**") return if member in self.players: await channel.send("{} is already in the game!".format(member.mention)) return self.started.append(member) channel.send("{} has been added to the game, total players is **{}**".format(member.mention, len(self.players))) async def quit(self, member: discord.Member): """ Have a member quit a game """ player = await self.get_player_by_member(member) if not player: return "You're not in a game!" if self.started: await self.kill() if member in self.players: return "{} is already in the game!".format(member.mention) self.started.append(member) 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!") return if not player.alive: channel.send("Corpses can't vote") return if channel in self.secret_channels.values(): if channel == self.village_channel: if not self.can_vote: channel.send("Voting is not allowed right now") return try: target = self.players[id] except IndexError: target = None if target is None: channel.send("Not a valid target") return async def kill(self, target, source=None, method: str=None): """ Attempt to kill a target Source allows admin override """ async def get_roles(self, game_code=None): if game_code: self.game_code=game_code if not self.game_code: return False self.roles = await parse_code(self.game_code) if not self.roles: return False