More WIP progress
This commit is contained in:
parent
8a42b87bd6
commit
3a6d3df374
@ -19,6 +19,14 @@ log = logging.getLogger("red.fox_v3.werewolf.game")
|
||||
HALF_DAY_LENGTH = 24 # FixMe: to 120 later for 4 minute days
|
||||
|
||||
|
||||
async def anyone_has_role(
|
||||
member_list: List[discord.Member], role: discord.Role
|
||||
) -> Union[None, discord.Member]:
|
||||
return await AsyncIter(member_list).find(
|
||||
lambda m: AsyncIter(m.roles).find(lambda r: r.id == role.id)
|
||||
)
|
||||
|
||||
|
||||
class Game:
|
||||
"""
|
||||
Base class to run a single game of Werewolf
|
||||
@ -129,6 +137,7 @@ class Game:
|
||||
self.roles = []
|
||||
return False
|
||||
|
||||
# If there's no game role, make the role and delete it later in `self.to_delete`
|
||||
if self.game_role is None:
|
||||
try:
|
||||
self.game_role = await ctx.guild.create_role(
|
||||
@ -144,14 +153,25 @@ class Game:
|
||||
)
|
||||
self.roles = []
|
||||
return False
|
||||
try:
|
||||
for player in self.players:
|
||||
await player.member.add_roles(*[self.game_role])
|
||||
except discord.Forbidden:
|
||||
await ctx.send(
|
||||
f"Unable to add role **{self.game_role.name}**\nBot is missing `manage_roles` permissions"
|
||||
)
|
||||
return False
|
||||
|
||||
anyone_with_role = await anyone_has_role(self.guild.members, self.game_role)
|
||||
if anyone_with_role is not None:
|
||||
await ctx.maybe_send_embed(
|
||||
f"{anyone_with_role.display_name} has the game role, "
|
||||
f"can't continue until no one has the role"
|
||||
)
|
||||
return False
|
||||
|
||||
try:
|
||||
for player in self.players:
|
||||
await player.member.add_roles(*[self.game_role])
|
||||
except discord.Forbidden:
|
||||
log.exception(f"Unable to add role **{self.game_role.name}**")
|
||||
await ctx.send(
|
||||
f"Unable to add role **{self.game_role.name}**\n"
|
||||
f"Bot is missing `manage_roles` permissions"
|
||||
)
|
||||
return False
|
||||
|
||||
await self.assign_roles()
|
||||
|
||||
@ -223,9 +243,10 @@ class Game:
|
||||
self.started = True
|
||||
# Assuming everything worked so far
|
||||
log.debug("Pre at_game_start")
|
||||
await self._at_game_start() # This will queue channels and votegroups to be made
|
||||
await self._at_game_start() # This will add votegroups to self.p_channels
|
||||
log.debug("Post at_game_start")
|
||||
for channel_id in self.p_channels:
|
||||
log.debug(f"Private channels: {self.p_channels}")
|
||||
for channel_id in self.p_channels.keys():
|
||||
log.debug("Setup Channel id: " + channel_id)
|
||||
overwrite = {
|
||||
self.guild.default_role: discord.PermissionOverwrite(read_messages=False),
|
||||
@ -251,6 +272,8 @@ class Game:
|
||||
|
||||
self.p_channels[channel_id]["channel"] = channel
|
||||
|
||||
self.to_delete.add(channel)
|
||||
|
||||
if self.p_channels[channel_id]["votegroup"] is not None:
|
||||
vote_group = self.p_channels[channel_id]["votegroup"](self, channel)
|
||||
|
||||
@ -259,8 +282,10 @@ class Game:
|
||||
self.vote_groups[channel_id] = vote_group
|
||||
|
||||
log.debug("Pre-cycle")
|
||||
await asyncio.sleep(1)
|
||||
await asyncio.ensure_future(self._cycle()) # Start the loop
|
||||
await asyncio.sleep(0)
|
||||
|
||||
asyncio.create_task(self._cycle()) # Start the loop
|
||||
return True
|
||||
|
||||
# ###########START Notify structure############
|
||||
async def _cycle(self):
|
||||
@ -553,13 +578,14 @@ class Game:
|
||||
try:
|
||||
await asyncio.sleep(1) # This will have multiple calls
|
||||
self.p_channels[channel_id]["players"].append(role.player)
|
||||
if votegroup is not None:
|
||||
self.p_channels[channel_id]["votegroup"] = votegroup
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
if votegroup is not None:
|
||||
self.p_channels[channel_id]["votegroup"] = votegroup
|
||||
|
||||
async def join(self, member: discord.Member, channel: discord.TextChannel):
|
||||
"""
|
||||
Have a member join a game
|
||||
@ -574,14 +600,15 @@ class Game:
|
||||
|
||||
self.players.append(Player(member))
|
||||
|
||||
if self.game_role is not None:
|
||||
try:
|
||||
await member.add_roles(*[self.game_role])
|
||||
except discord.Forbidden:
|
||||
await channel.send(
|
||||
f"Unable to add role **{self.game_role.name}**\n"
|
||||
f"Bot is missing `manage_roles` permissions"
|
||||
)
|
||||
# Add the role during setup, not before
|
||||
# if self.game_role is not None:
|
||||
# try:
|
||||
# await member.add_roles(*[self.game_role])
|
||||
# except discord.Forbidden:
|
||||
# await channel.send(
|
||||
# f"Unable to add role **{self.game_role.name}**\n"
|
||||
# f"Bot is missing `manage_roles` permissions"
|
||||
# )
|
||||
|
||||
await channel.send(
|
||||
f"{member.display_name} has been added to the game, "
|
||||
@ -908,7 +935,7 @@ class Game:
|
||||
# Remove game_role access for potential archiving for now
|
||||
reason = "(BOT) End of WW game"
|
||||
for obj in self.to_delete:
|
||||
log.debug(f"End_game: Deleting object {obj}")
|
||||
log.debug(f"End_game: Deleting object {obj.__repr__()}")
|
||||
await obj.delete(reason=reason)
|
||||
|
||||
try:
|
||||
@ -926,6 +953,17 @@ class Game:
|
||||
except (discord.HTTPException, discord.NotFound, discord.errors.NotFound):
|
||||
pass
|
||||
|
||||
for player in self.players:
|
||||
try:
|
||||
await player.member.remove_roles(*[self.game_role])
|
||||
except discord.Forbidden:
|
||||
log.exception(f"Unable to add remove **{self.game_role.name}**")
|
||||
# await ctx.send(
|
||||
# f"Unable to add role **{self.game_role.name}**\n"
|
||||
# f"Bot is missing `manage_roles` permissions"
|
||||
# )
|
||||
pass
|
||||
|
||||
# Optional dynamic channels/categories
|
||||
|
||||
def add_ww_listener(self, func, priority=0, name=None):
|
||||
|
@ -20,6 +20,9 @@ class Player:
|
||||
self.muted = False
|
||||
self.protected = False
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}({self.member})"
|
||||
|
||||
async def assign_role(self, role):
|
||||
"""
|
||||
Give this player a role
|
||||
|
@ -73,7 +73,7 @@ class Role(WolfListener):
|
||||
self.properties = {} # Extra data for other roles (i.e. arsonist)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__class__.__name__
|
||||
return f"{self.__class__.__name__}({self.player.__repr__()})"
|
||||
|
||||
async def assign_player(self, player):
|
||||
"""
|
||||
@ -84,6 +84,8 @@ class Role(WolfListener):
|
||||
player.role = self
|
||||
self.player = player
|
||||
|
||||
log.debug(f"Assigned {self} to {player}")
|
||||
|
||||
async def get_alignment(self, source=None):
|
||||
"""
|
||||
Interaction for powerful access of alignment
|
||||
|
@ -15,6 +15,7 @@ log = logging.getLogger("red.fox_v3.werewolf.role.seer")
|
||||
|
||||
class Seer(Role):
|
||||
rand_choice = True
|
||||
town_balance = 4
|
||||
category = [
|
||||
CATEGORY_TOWN_RANDOM,
|
||||
CATEGORY_TOWN_INVESTIGATIVE,
|
||||
|
@ -22,21 +22,6 @@ class VanillaWerewolf(Role):
|
||||
"Vote to kill players at night with `[p]ww vote <ID>`"
|
||||
)
|
||||
|
||||
def __init__(self, game):
|
||||
super().__init__(game)
|
||||
|
||||
# self.action_list = [
|
||||
# (self._at_game_start, 1), # (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 see_alignment(self, source=None):
|
||||
"""
|
||||
Interaction for investigative roles attempting
|
||||
|
@ -7,12 +7,9 @@ log = logging.getLogger("red.fox_v3.werewolf.role.villager")
|
||||
|
||||
|
||||
class Villager(Role):
|
||||
|
||||
# Determines if it can be picked as a random role (False for unusually disruptive roles)
|
||||
rand_choice = True
|
||||
|
||||
town_balance = 1
|
||||
|
||||
category = [CATEGORY_TOWN_RANDOM] # List of enrolled categories (listed above)
|
||||
alignment = ALIGNMENT_TOWN # 1: Town, 2: Werewolf, 3: Neutral
|
||||
channel_id = "" # Empty for no private channel
|
||||
@ -23,9 +20,6 @@ class Villager(Role):
|
||||
"Lynch players during the day with `[p]ww vote <ID>`"
|
||||
)
|
||||
|
||||
def __init__(self, game):
|
||||
super().__init__(game)
|
||||
|
||||
async def see_alignment(self, source=None):
|
||||
"""
|
||||
Interaction for investigative roles attempting
|
||||
|
@ -75,7 +75,6 @@ class VoteGroup(WolfListener):
|
||||
|
||||
if not self.players:
|
||||
# TODO: Confirm deletion
|
||||
self.game.to_delete.add(self)
|
||||
pass
|
||||
|
||||
async def vote(self, target, author, target_id):
|
||||
|
@ -1,9 +1,11 @@
|
||||
import logging
|
||||
from typing import List, Union
|
||||
|
||||
import discord
|
||||
from redbot.core import Config, checks, commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.commands import Cog
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
|
||||
|
||||
from werewolf.builder import (
|
||||
@ -18,6 +20,14 @@ from werewolf.game import Game
|
||||
log = logging.getLogger("red.fox_v3.werewolf")
|
||||
|
||||
|
||||
async def anyone_has_role(
|
||||
member_list: List[discord.Member], role: discord.Role
|
||||
) -> Union[None, discord.Member]:
|
||||
return await AsyncIter(member_list).find(
|
||||
lambda m: AsyncIter(m.roles).find(lambda r: r.id == role.id)
|
||||
)
|
||||
|
||||
|
||||
class Werewolf(Cog):
|
||||
"""
|
||||
Base to host werewolf on a guild
|
||||
@ -189,12 +199,15 @@ class Werewolf(Cog):
|
||||
return
|
||||
|
||||
await game.join(ctx.author, ctx.channel)
|
||||
await ctx.tick()
|
||||
|
||||
@commands.guild_only()
|
||||
@ww.command(name="code")
|
||||
async def ww_code(self, ctx: commands.Context, code):
|
||||
"""
|
||||
Adjust game code
|
||||
Adjusts the game code.
|
||||
|
||||
See `[p]buildgame` to generate a new code
|
||||
"""
|
||||
|
||||
game = await self._get_game(ctx)
|
||||
@ -204,6 +217,7 @@ class Werewolf(Cog):
|
||||
return
|
||||
|
||||
await game.set_code(ctx, code)
|
||||
await ctx.tick()
|
||||
|
||||
@commands.guild_only()
|
||||
@ww.command(name="quit")
|
||||
@ -215,6 +229,7 @@ class Werewolf(Cog):
|
||||
game = await self._get_game(ctx)
|
||||
|
||||
await game.quit(ctx.author, ctx.channel)
|
||||
await ctx.tick()
|
||||
|
||||
@commands.guild_only()
|
||||
@ww.command(name="start")
|
||||
@ -229,6 +244,8 @@ class Werewolf(Cog):
|
||||
if not await game.setup(ctx):
|
||||
pass # ToDo something?
|
||||
|
||||
await ctx.tick()
|
||||
|
||||
@commands.guild_only()
|
||||
@ww.command(name="stop")
|
||||
async def ww_stop(self, ctx: commands.Context):
|
||||
@ -245,6 +262,7 @@ class Werewolf(Cog):
|
||||
|
||||
game = await self._get_game(ctx)
|
||||
game.game_over = True
|
||||
await game.current_action.cancel()
|
||||
await ctx.send("Game has been stopped")
|
||||
|
||||
@commands.guild_only()
|
||||
@ -358,7 +376,7 @@ class Werewolf(Cog):
|
||||
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: commands.Context, game_code=None) -> Union[Game, None]:
|
||||
guild: discord.Guild = getattr(ctx, "guild", None)
|
||||
|
||||
if guild is None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user