You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Fox-V3/werewolf/werewolf.py

296 lines
9.4 KiB

import discord
from discord.ext import commands
from redbot.core import Config
from redbot.core import RedContext
from redbot.core.bot import Red
from werewolf.builder import GameBuilder
from werewolf.game import Game
class Werewolf:
"""
Base to host werewolf on a guild
"""
def __init__(self, bot: Red):
self.bot = bot
self.config = Config.get_conf(self, identifier=87101114101119111108102, force_registration=True)
default_global = {}
default_guild = {
"role_id": None,
"category_id": None,
"channel_id": None,
"log_channel_id": None
}
self.config.register_global(**default_global)
self.config.register_guild(**default_guild)
self.games = {} # Active games stored here, id is per guild
def __unload(self):
print("Unload called")
for game in self.games.values():
del game
@commands.command()
async def buildgame(self, ctx: RedContext):
gb = GameBuilder()
code = await gb.build_game(ctx)
if code is not None:
await ctx.send("Your game code is **{}**".format(code))
else:
await ctx.send("No code generated")
@commands.group()
async def wwset(self, ctx: RedContext):
"""
Base command to adjust settings. Check help for command list.
"""
if ctx.invoked_subcommand is None:
await ctx.send_help()
@commands.guild_only()
@wwset.command(name="role")
async def wwset_role(self, ctx: RedContext, role: discord.Role):
"""
Assign the game role
This role should not be manually assigned
"""
await self.config.guild(ctx.guild).role_id.set(role.id)
await ctx.send("Game role has been set to **{}**".format(role.name))
@commands.guild_only()
@wwset.command(name="category")
async def wwset_category(self, ctx: RedContext, category_id):
"""
Assign the channel category
"""
category = discord.utils.get(ctx.guild.categories, id=int(category_id))
if category is None:
await ctx.send("Category not found")
return
await self.config.guild(ctx.guild).category_id.set(category.id)
await ctx.send("Channel Category has been set to **{}**".format(category.name))
@commands.guild_only()
@wwset.command(name="channel")
async def wwset_channel(self, ctx: RedContext, channel: discord.TextChannel):
"""
Assign the village channel
"""
await self.config.guild(ctx.guild).channel_id.set(channel.id)
await ctx.send("Game Channel has been set to **{}**".format(channel.mention))
@commands.guild_only()
@wwset.command(name="logchannel")
async def wwset_log_channel(self, ctx: RedContext, channel: discord.TextChannel):
"""
Assign the log channel
"""
await self.config.guild(ctx.guild).log_channel_id.set(channel.id)
await ctx.send("Log Channel has been set to **{}**".format(channel.mention))
@commands.group()
async def ww(self, ctx: RedContext):
"""
Base command for this cog. Check help for the commands list.
"""
if ctx.invoked_subcommand is None:
await ctx.send_help()
@commands.guild_only()
@ww.command()
async def new(self, ctx: RedContext, game_code=None):
"""
Create and join a new game of Werewolf
"""
game = await self._get_game(ctx, game_code)
if not game:
await ctx.send("Failed to start a new game")
else:
await ctx.send("Game is ready to join! Use `[p]ww join`")
@commands.guild_only()
@ww.command()
async def join(self, ctx: RedContext):
"""
Joins a game of Werewolf
"""
game = await self._get_game(ctx)
if not game:
await ctx.send("No game to join!\nCreate a new one with `[p]ww new`")
return
await game.join(ctx.author, ctx.channel)
@commands.guild_only()
@ww.command()
async def code(self, ctx: RedContext, code):
"""
Adjust game code
"""
game = await self._get_game(ctx)
if not game:
await ctx.send("No game to join!\nCreate a new one with `[p]ww new`")
return
await game.set_code(ctx, code)
@commands.guild_only()
@ww.command()
async def quit(self, ctx: RedContext):
"""
Quit a game of Werewolf
"""
game = await self._get_game(ctx)
await game.quit(ctx.author, ctx.channel)
@commands.guild_only()
@ww.command()
async def start(self, ctx: RedContext):
"""
Checks number of players and attempts to start the game
"""
game = await self._get_game(ctx)
if not game:
await ctx.send("No game running, cannot start")
await game.setup(ctx)
@commands.guild_only()
@ww.command()
async def stop(self, ctx: RedContext):
"""
Stops the current game
"""
if ctx.guild is None:
# Private message, can't get guild
await ctx.send("Cannot start game from PM!")
return
if ctx.guild.id not in self.games or self.games[ctx.guild.id].game_over:
await ctx.send("No game to stop")
return
game = await self._get_game(ctx)
game.game_over = True
await ctx.send("Game has been stopped")
@commands.guild_only()
@ww.command()
async def vote(self, ctx: RedContext, target_id: int):
"""
Vote for a player by ID
"""
try:
target_id = int(target_id)
except ValueError:
target_id = None
if target_id is None:
await ctx.send("`id` must be an integer")
return
# if ctx.guild is None:
# # DM nonsense, find their game
# # If multiple games, panic
# for game in self.games.values():
# if await game.get_player_by_member(ctx.author):
# break #game = game
# else:
# await ctx.send("You're not part of any werewolf game")
# return
# else:
game = await self._get_game(ctx)
if game is None:
await ctx.send("No game running, cannot vote")
return
# Game handles response now
channel = ctx.channel
if channel == game.village_channel:
await game.vote(ctx.author, target_id, channel)
elif channel in (c["channel"] for c in game.p_channels.values()):
await game.vote(ctx.author, target_id, channel)
else:
await ctx.send("Nothing to vote for in this channel")
@ww.command()
async def choose(self, ctx: RedContext, data):
"""
Arbitrary decision making
Handled by game+role
Can be received by DM
"""
if ctx.guild is not None:
await ctx.send("This action is only available in DM's")
return
# DM nonsense, find their game
# If multiple games, panic
for game in self.games.values():
if await game.get_player_by_member(ctx.author):
break # game = game
else:
await ctx.send("You're not part of any werewolf game")
return
await game.choose(ctx, data)
async def _get_game(self, ctx: RedContext, game_code=None):
guild: discord.Guild = ctx.guild
if guild is None:
# Private message, can't get guild
await ctx.send("Cannot start game from PM!")
return None
if guild.id not in self.games or self.games[guild.id].game_over:
await ctx.send("Starting a new game...")
role = None
category = None
channel = None
log_channel = None
role_id = await self.config.guild(guild).role_id()
category_id = await self.config.guild(guild).category_id()
channel_id = await self.config.guild(guild).channel_id()
log_channel_id = await self.config.guild(guild).log_channel_id()
if role_id is not None:
role = discord.utils.get(guild.roles, id=role_id)
if role is None:
await ctx.send("Game Role is invalid, cannot start new game")
return None
if category_id is not None:
category = discord.utils.get(guild.categories, id=category_id)
if category is None:
await ctx.send("Game Category is invalid, cannot start new game")
return None
if channel_id is not None:
channel = discord.utils.get(guild.text_channels, id=channel_id)
if channel is None:
await ctx.send("Village Channel is invalid, cannot start new game")
return None
if log_channel_id is not None:
log_channel = discord.utils.get(guild.text_channels, id=log_channel_id)
if log_channel is None:
await ctx.send("Log Channel is invalid, cannot start new game")
return None
self.games[guild.id] = Game(guild, role, category, channel, log_channel, game_code)
return self.games[guild.id]
async def _game_start(self, game):
await game.start()