|
|
@ -62,6 +62,8 @@ class Game:
|
|
|
|
village: discord.TextChannel = None,
|
|
|
|
village: discord.TextChannel = None,
|
|
|
|
log_channel: discord.TextChannel = None,
|
|
|
|
log_channel: discord.TextChannel = None,
|
|
|
|
game_code=None,
|
|
|
|
game_code=None,
|
|
|
|
|
|
|
|
day_length=HALF_DAY_LENGTH * 2,
|
|
|
|
|
|
|
|
night_length=HALF_NIGHT_LENGTH * 2,
|
|
|
|
):
|
|
|
|
):
|
|
|
|
self.bot = bot
|
|
|
|
self.bot = bot
|
|
|
|
self.guild = guild
|
|
|
|
self.guild = guild
|
|
|
@ -73,6 +75,9 @@ class Game:
|
|
|
|
self.day_vote = {} # author: target
|
|
|
|
self.day_vote = {} # author: target
|
|
|
|
self.vote_totals = {} # id: total_votes
|
|
|
|
self.vote_totals = {} # id: total_votes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.half_day_length = day_length // 2
|
|
|
|
|
|
|
|
self.half_night_length = night_length // 2
|
|
|
|
|
|
|
|
|
|
|
|
self.started = False
|
|
|
|
self.started = False
|
|
|
|
self.game_over = False
|
|
|
|
self.game_over = False
|
|
|
|
self.any_votes_remaining = False
|
|
|
|
self.any_votes_remaining = False
|
|
|
@ -97,6 +102,7 @@ class Game:
|
|
|
|
|
|
|
|
|
|
|
|
self.loop = asyncio.get_event_loop()
|
|
|
|
self.loop = asyncio.get_event_loop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.cycle_task = None
|
|
|
|
self.action_queue = deque()
|
|
|
|
self.action_queue = deque()
|
|
|
|
self.current_action = None
|
|
|
|
self.current_action = None
|
|
|
|
self.listeners = {}
|
|
|
|
self.listeners = {}
|
|
|
@ -116,6 +122,13 @@ class Game:
|
|
|
|
# for c_data in self.p_channels.values():
|
|
|
|
# for c_data in self.p_channels.values():
|
|
|
|
# asyncio.ensure_future(c_data["channel"].delete("Werewolf game-over"))
|
|
|
|
# asyncio.ensure_future(c_data["channel"].delete("Werewolf game-over"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _prestart_status(self, ctx):
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
|
|
f"Currently **{len(self.players)} / {len(self.roles)}**\n"
|
|
|
|
|
|
|
|
f"Use `{ctx.prefix}ww code` to pick a game setup\n"
|
|
|
|
|
|
|
|
f"Use `{ctx.prefix}buildgame` to generate a new game"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def setup(self, ctx: commands.Context):
|
|
|
|
async def setup(self, ctx: commands.Context):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Runs the initial setup
|
|
|
|
Runs the initial setup
|
|
|
@ -127,15 +140,20 @@ class Game:
|
|
|
|
4. Start game
|
|
|
|
4. Start game
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
if self.game_code:
|
|
|
|
if self.game_code:
|
|
|
|
|
|
|
|
# Turn random roles into real roles
|
|
|
|
await self.get_roles(ctx)
|
|
|
|
await self.get_roles(ctx)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
await ctx.maybe_send_embed(
|
|
|
|
|
|
|
|
f"No game code has been assigned, cannot start\n{self._prestart_status(ctx)}"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
if len(self.players) != len(self.roles):
|
|
|
|
if len(self.players) != len(self.roles):
|
|
|
|
await ctx.maybe_send_embed(
|
|
|
|
await ctx.maybe_send_embed(
|
|
|
|
f"Player count does not match role count, cannot start\n"
|
|
|
|
f"Player count does not match role count, cannot start\n"
|
|
|
|
f"Currently **{len(self.players)} / {len(self.roles)}**\n"
|
|
|
|
f"{self._prestart_status(ctx)}"
|
|
|
|
f"Use `{ctx.prefix}ww code` to pick a game setup\n"
|
|
|
|
|
|
|
|
f"Use `{ctx.prefix}buildgame` to generate a new game"
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Clear the roles to be randomly generated again
|
|
|
|
self.roles = []
|
|
|
|
self.roles = []
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
@ -156,6 +174,7 @@ class Game:
|
|
|
|
self.roles = []
|
|
|
|
self.roles = []
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Check if the role is already in use. If so, cannot continue until removed
|
|
|
|
anyone_with_role = await anyone_has_role(self.guild.members, self.game_role)
|
|
|
|
anyone_with_role = await anyone_has_role(self.guild.members, self.game_role)
|
|
|
|
if anyone_with_role is not None:
|
|
|
|
if anyone_with_role is not None:
|
|
|
|
await ctx.maybe_send_embed(
|
|
|
|
await ctx.maybe_send_embed(
|
|
|
@ -164,6 +183,7 @@ class Game:
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Add the game role to those who are playing
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
for player in self.players:
|
|
|
|
for player in self.players:
|
|
|
|
await player.member.add_roles(*[self.game_role])
|
|
|
|
await player.member.add_roles(*[self.game_role])
|
|
|
@ -175,6 +195,7 @@ class Game:
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Randomly assign the roles in the game to the players
|
|
|
|
await self.assign_roles()
|
|
|
|
await self.assign_roles()
|
|
|
|
|
|
|
|
|
|
|
|
# Create category and channel with individual overwrites
|
|
|
|
# Create category and channel with individual overwrites
|
|
|
@ -219,14 +240,16 @@ class Game:
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self.save_perms[self.village_channel] = self.village_channel.overwrites
|
|
|
|
self.save_perms[self.village_channel] = self.village_channel.overwrites
|
|
|
|
try:
|
|
|
|
|
|
|
|
await self.village_channel.edit(
|
|
|
|
# Disable renaming channels, too easy to get rate limited.
|
|
|
|
name="🔵werewolf",
|
|
|
|
# try:
|
|
|
|
reason="(BOT) New game of werewolf",
|
|
|
|
# await self.village_channel.edit(
|
|
|
|
)
|
|
|
|
# name="🔵werewolf",
|
|
|
|
except discord.Forbidden as e:
|
|
|
|
# reason="(BOT) New game of werewolf",
|
|
|
|
log.exception("Unable to rename Game Channel")
|
|
|
|
# )
|
|
|
|
await ctx.maybe_send_embed("Unable to rename Game Channel, ignoring")
|
|
|
|
# except discord.Forbidden as e:
|
|
|
|
|
|
|
|
# log.exception("Unable to rename Game Channel")
|
|
|
|
|
|
|
|
# await ctx.maybe_send_embed("Unable to rename Game Channel, ignoring")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
for target, ow in overwrite.items():
|
|
|
|
for target, ow in overwrite.items():
|
|
|
@ -283,9 +306,9 @@ class Game:
|
|
|
|
self.vote_groups[channel_id] = vote_group
|
|
|
|
self.vote_groups[channel_id] = vote_group
|
|
|
|
|
|
|
|
|
|
|
|
log.debug("Pre-cycle")
|
|
|
|
log.debug("Pre-cycle")
|
|
|
|
await asyncio.sleep(0)
|
|
|
|
await asyncio.sleep(0) # Pass back to controller to avoid heartbeat issues
|
|
|
|
|
|
|
|
|
|
|
|
asyncio.create_task(self._cycle()) # Start the loop
|
|
|
|
self.cycle_task = asyncio.create_task(self._cycle()) # Start the loop
|
|
|
|
return True
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
# ###########START Notify structure############
|
|
|
|
# ###########START Notify structure############
|
|
|
@ -360,13 +383,15 @@ class Game:
|
|
|
|
self.any_votes_remaining = True
|
|
|
|
self.any_votes_remaining = True
|
|
|
|
|
|
|
|
|
|
|
|
# Now we sleep and let the day happen. Print the remaining daylight half way through
|
|
|
|
# Now we sleep and let the day happen. Print the remaining daylight half way through
|
|
|
|
await asyncio.sleep(HALF_DAY_LENGTH) # 4 minute days FixMe to 120 later
|
|
|
|
await asyncio.sleep(self.half_day_length)
|
|
|
|
if check():
|
|
|
|
if check():
|
|
|
|
return
|
|
|
|
return
|
|
|
|
await self.village_channel.send(
|
|
|
|
await self.village_channel.send(
|
|
|
|
embed=discord.Embed(title=f"*{HALF_DAY_LENGTH / 60} minutes of daylight remain...*")
|
|
|
|
embed=discord.Embed(
|
|
|
|
|
|
|
|
title=f"*{self.half_day_length/ 60} minutes of daylight remain...*"
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
await asyncio.sleep(HALF_DAY_LENGTH) # 4 minute days FixMe to 120 later
|
|
|
|
await asyncio.sleep(self.half_day_length)
|
|
|
|
|
|
|
|
|
|
|
|
# Need a loop here to wait for trial to end
|
|
|
|
# Need a loop here to wait for trial to end
|
|
|
|
while self.ongoing_vote:
|
|
|
|
while self.ongoing_vote:
|
|
|
@ -501,13 +526,13 @@ class Game:
|
|
|
|
|
|
|
|
|
|
|
|
await self._notify("at_night_start")
|
|
|
|
await self._notify("at_night_start")
|
|
|
|
|
|
|
|
|
|
|
|
await asyncio.sleep(HALF_NIGHT_LENGTH) # 2 minutes FixMe to 120 later
|
|
|
|
await asyncio.sleep(self.half_night_length)
|
|
|
|
await self.village_channel.send(
|
|
|
|
await self.village_channel.send(
|
|
|
|
embed=discord.Embed(title=f"**{HALF_NIGHT_LENGTH / 60} minutes of night remain...**")
|
|
|
|
embed=discord.Embed(
|
|
|
|
|
|
|
|
title=f"**{self.half_night_length / 60} minutes of night remain...**"
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
await asyncio.sleep(HALF_NIGHT_LENGTH) # 1.5 minutes FixMe to 90 later
|
|
|
|
await asyncio.sleep(self.half_night_length)
|
|
|
|
|
|
|
|
|
|
|
|
await asyncio.sleep(3) # .5 minutes FixMe to 30 Later
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.action_queue.append(self._at_night_end())
|
|
|
|
self.action_queue.append(self._at_night_end())
|
|
|
|
|
|
|
|
|
|
|
@ -553,7 +578,7 @@ class Game:
|
|
|
|
# ###########END Notify structure############
|
|
|
|
# ###########END Notify structure############
|
|
|
|
|
|
|
|
|
|
|
|
async def generate_targets(self, channel, with_roles=False):
|
|
|
|
async def generate_targets(self, channel, with_roles=False):
|
|
|
|
embed = discord.Embed(title="Remaining Players", description="[ID] - [Name]")
|
|
|
|
embed = discord.Embed(title="Remaining Players", description="ID || Name")
|
|
|
|
for i, player in enumerate(self.players):
|
|
|
|
for i, player in enumerate(self.players):
|
|
|
|
if player.alive:
|
|
|
|
if player.alive:
|
|
|
|
status = ""
|
|
|
|
status = ""
|
|
|
@ -561,13 +586,15 @@ class Game:
|
|
|
|
status = "*[Dead]*-"
|
|
|
|
status = "*[Dead]*-"
|
|
|
|
if with_roles or not player.alive:
|
|
|
|
if with_roles or not player.alive:
|
|
|
|
embed.add_field(
|
|
|
|
embed.add_field(
|
|
|
|
name=f"{i} - {status}{player.member.display_name}",
|
|
|
|
name=f"{i} || {status}{player.member.display_name}",
|
|
|
|
value=f"{player.role}",
|
|
|
|
value=f"{player.role}",
|
|
|
|
inline=False,
|
|
|
|
inline=False,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
embed.add_field(
|
|
|
|
embed.add_field(
|
|
|
|
name=f"{i} - {status}{player.member.display_name}", inline=False, value="____"
|
|
|
|
name=f"{i} || {status}{player.member.display_name}",
|
|
|
|
|
|
|
|
inline=False,
|
|
|
|
|
|
|
|
value="\N{Zero Width Space}",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
return await channel.send(embed=embed)
|
|
|
|
return await channel.send(embed=embed)
|
|
|
@ -859,7 +886,7 @@ class Game:
|
|
|
|
self.players.sort(key=lambda pl: pl.member.display_name.lower())
|
|
|
|
self.players.sort(key=lambda pl: pl.member.display_name.lower())
|
|
|
|
|
|
|
|
|
|
|
|
if len(self.roles) != len(self.players):
|
|
|
|
if len(self.roles) != len(self.players):
|
|
|
|
await self.village_channel.send("Unhandled error - roles!=players")
|
|
|
|
await self.village_channel.send("Unhandled error - roles != # players")
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
for idx, role in enumerate(self.roles):
|
|
|
|
for idx, role in enumerate(self.roles):
|
|
|
|