From 09eff190dba21a1baeac76ec434375385b0d647b Mon Sep 17 00:00:00 2001
From: Brandon Ly
Date: Mon, 12 Apr 2021 05:21:14 -0400
Subject: [PATCH] refactor: V1 Refactor (#18)
* feat: setup class modules
* feat: create base map class
* feat: create base game class
* feat: create base match class
* feat: string representation of map based on state
* feat: update names for classes
* create: smash stage class
* docs: docstring for game constructor
* feat: setup smash game and player classes
* feat: override equals method for player
* feat: add bot context for veto to match
* feat: create stagelist object
* feat: move map pool to child classes
* feat: veto method for stagelist
* refactor: move stagelist methods to game class
* refactor: updated smash stages for new model
* fix: spacing of game title
* refactor: move stage veto back to stagelist
* docs: settings page documentation
* fix: removed bot stuff from match
Match.py should only be backend api, no need to put discord stuff in it.
* docs(smash): add docstrings and comments to all
* refactor(bot): cleared old files
* refactor(coinflip): remove coinflip animation
Removed due to avoid clogging up the discord rate limit
* feat(smash): generates blank games on match creation
* refactor(smash): remove match base class
* fix(smash): seed selection through match
* refactor(smash): moved embed generator to match class
* refactor: removed base classes
* feat: rewrote all smash vetos
* feat: use specific channel for match creation
do this instead of restricting channels, its a lot easier
* feat: coinflip supports two player pings
* feat(val): valorant map class
* feat(val): game and map pool object
* feat(val): match object started
* fix(smash): players not swapping on p2 win
* feat(val): bo3 veto
* feat(val): bo5 veto
* refactor: split veto command into multiple
* refactor: removed old un-needed files
* docs: update README.md
* docs: fix README.md links
---
README.md | 152 ++++++++++-
bot.py | 493 +++++++++++++-----------------------
settings.py | 104 ++++++--
{veto => smash}/__init__.py | 0
smash/game.py | 73 ++++++
smash/match.py | 193 ++++++++++++++
smash/player.py | 32 +++
smash/stage.py | 51 ++++
smash/stagelist.py | 93 +++++++
utils/checks.py | 51 ++++
utils/embeds.py | 88 ++-----
utils/message_generators.py | 77 ------
utils/player_utils.py | 60 ++---
valorant/__init__.py | 0
valorant/game.py | 76 ++++++
valorant/map.py | 46 ++++
valorant/map_pool.py | 34 +++
valorant/match.py | 302 ++++++++++++++++++++++
veto/smash.py | 237 -----------------
veto/valorant.py | 320 -----------------------
20 files changed, 1408 insertions(+), 1074 deletions(-)
rename {veto => smash}/__init__.py (100%)
create mode 100644 smash/game.py
create mode 100644 smash/match.py
create mode 100644 smash/player.py
create mode 100644 smash/stage.py
create mode 100644 smash/stagelist.py
create mode 100644 utils/checks.py
delete mode 100644 utils/message_generators.py
create mode 100644 valorant/__init__.py
create mode 100644 valorant/game.py
create mode 100644 valorant/map.py
create mode 100644 valorant/map_pool.py
create mode 100644 valorant/match.py
delete mode 100644 veto/smash.py
delete mode 100644 veto/valorant.py
diff --git a/README.md b/README.md
index 6a495c5..caf809e 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,149 @@
-# Vaughan Esports Veto Bot
+
+
+
+
+
+
-Currently designed for smash stage veto according to Vaughan Esports' SSBU
-rulebook.
+
Vaughan Esports Tournament Bot
-REFACTOR In progress, docs for getting started and hosting it yourself will be
-updated once the refactor is complete. Check the refactor branch for updates.
\ No newline at end of file
+
+ Home of Vaughan Esports' very own Discord Bot used to run our events!
+
+
+ View Demo
+ ·
+ Report Bug
+ ·
+ Request Feature
+
+
+
+
+
+
+
+ Table of Contents
+
+ -
+ About The Project
+
+
+ -
+ Getting Started
+
+
+ - Usage
+ - Roadmap
+ - License
+ - Contact
+
+
+
+
+
+
+
+## About The Project
+
+Tournament rules and rulesets are long can get pretty annoying to read and keep
+up with, especially with our regular monthly events. Having a dedicated bot to
+handle the processes for veto'ing and selecting maps helps keeps players
+engaged and following the rules!
+
+The bot walks all players through the veto process and has a multitude of
+commands to help players and teams setup their matches.
+
+### Built With
+
+* [Discord.py](https://github.com/Rapptz/discord.py)
+* [Rich](https://github.com/willmcgugan/rich)
+
+
+
+## Getting Started
+
+Follow the steps below if you'd like to host your own instance our the bot.
+
+### Prerequisites
+
+- Python 3.8 or higher (May work on 3.6 and 3.7 but untested)
+- Discord Developer Account
+
+### Installation
+
+1. Get your Discord bot API Key (
+ Instructions [here](https://discordpy.readthedocs.io/en/latest/discord.html))
+2. Clone the repo
+ ```sh
+ git clone https://github.com/Vaughan-Esports/VE-Tourney-Bot.git
+ ```
+3. Install Python packages
+ ```sh
+ pip install -r requirements.txt
+ ```
+4. Create a file called `.env` amd put your API key in like so:
+ ```dotenv
+ BOT_TOKEN=YOUR_TOKEN
+ ```
+
+### Configuration
+
+Everything is configurable from `settings.py`
+
+
+
+
+## Usage
+
+Its main commands are
+
+- `ve!coinflip @opponent`
+ - Flips a coin
+- `ve!match @opponent`
+ - Starts a private chat between you and your opponent
+- `ve!smash {best-of} @opponent`
+ - Starts a smash veto with your opponent (Best of can be 3 or 5)
+- `ve!val {best-of} @opponent`
+ - Starts a VALORANT veto with your opponent (Best of can be 1, 3, or 5)
+- `ve!close`
+ - Closes a private chat between you and your opponent
+- `ve!purge`
+ - Purges all closed channels
+
+Commands can also be run with pings to both players so that TO's may manually
+set up commands for players (e.g `ve!match @player1 @player2`)
+
+
+
+
+
+## Roadmap
+
+- osu! Veto
+- League of Legends ARAM Roll
+- Logging
+- MongoDB Support (V2)
+- Dynamic Veto (Easy config of veto from a single file) (V3)
+
+
+
+## License
+
+Distributed under the MIT License. See `LICENSE` for more information.
+
+
+
+
+
+## Contact
+
+Your Name - [@brndnly](https://twitter.com/brndnly) - email@example.com
+
+Project
+Link: [https://github.com/Vaughan-Esports/VE-Tourney-Bot](https://github.com/Vaughan-Esports/VE-Tourney-Bot)
diff --git a/bot.py b/bot.py
index 68065cf..88c4feb 100644
--- a/bot.py
+++ b/bot.py
@@ -5,298 +5,132 @@
from discord.ext import commands
from discord.ext.commands import MissingPermissions
+from settings import active_channels_id, inactive_channels_id
+from settings import guild_id, TO_role_id, match_creation_channel_id
+from settings import prefix, description, tourney_name
+from settings import smash_example, valorant_example
+from smash.match import Match as SmashMatch
+from smash.player import Player
from utils import embeds, player_utils
-from utils.message_generators import *
-from veto import smash, valorant
+from valorant.match import Match as ValMatch
intents = discord.Intents.default()
-allowed_mentions = discord.AllowedMentions(everyone=False, users=True,
+allowed_mentions = discord.AllowedMentions(everyone=False,
+ users=True,
roles=True)
-bot = discord.ext.commands.Bot(command_prefix=prefix, intents=intents,
- description=description, case_insensitive=True,
+bot = discord.ext.commands.Bot(command_prefix=prefix,
+ intents=intents,
+ description=description,
+ case_insensitive=True,
allowed_mentions=allowed_mentions)
+
BOT_TOKEN = os.getenv('BOT_TOKEN')
-@bot.command()
-async def veto(ctx, game=None, series_length=None, opponent=None):
+@bot.command(aliases=['ssbu'])
+async def smash(ctx, series_length=None, opponent=None):
"""
- Starts a veto lobby with your opponent
+ Starts a Smash Ult. veto with your opponent
"""
- # let user know if they're missing a parameter
- if game is None or series_length is None or opponent is None:
- text = "Initiate a veto with `ve!veto {game} {series_length} @{" \
- "opponent}` "
- await ctx.send(embed=await embeds.missing_param_error(text))
- elif ctx.channel.id in restricted_channels_ids:
+ # guild and category objects
+ guild = bot.get_guild(guild_id)
+ active_category = guild.get_channel(active_channels_id)
+
+ # check if a valid place to start matches
+ if ctx.channel not in active_category.text_channels or \
+ ctx.channel.id == match_creation_channel_id:
text = "You can't do that here! Invoke a match chat first with " \
"`ve!match {@opponent}`"
await ctx.send(embed=await embeds.missing_permission_error(text))
- # smash best of 3 veto
- elif game.lower() == 'smash' and series_length.lower() == 'bo3':
- # starting/loading embed
- main_msg = await ctx.send(embed=await embeds.starting())
+ # let user know if they're missing a parameter
+ elif series_length is None or opponent is None:
+ text = "Initiate a veto with `ve!veto {game} " \
+ "{best-of (3 or 5)} @{opponent}` "
+ await ctx.send(embed=await embeds.missing_param_error(text))
- # player
+ # SMASH VETO
+ elif series_length == '3' or series_length == '5':
+ # get players
player1, player2 = await player_utils.get_players(ctx)
- # send first veto embed
- embed = await embeds.smash_veto(player1, player2, 3)
- await main_msg.edit(embed=embed)
+ # initialize game
+ match = SmashMatch(Player(player1),
+ Player(player2),
+ int(series_length))
# run veto with catch statement in case of time out
try:
- # HIGHER SEED SELECTION
- await ctx.send(f"{newline}Player 1 (the higher seed) say `me`")
-
- # checks which user says me
- def playerCheck(message):
- return message.content.lower() == 'me' \
- and message.channel == ctx.channel
-
- msg = await bot.wait_for('message', check=playerCheck, timeout=300)
-
- # changes player order if player 2 said they were first seed
- if msg.author == player2:
- player1 = player2
- player2 = ctx.author
-
- # notifies of veto starts
- await ctx.send(f"Starting veto with {player1.mention} as "
- f"**Player 1** and {player2.mention} "
- f"as **Player 2** in 5 seconds...")
-
- # delete all messages and begin veto after 5 seconds
+ # run seed selection
+ await player_utils.seed_selection(ctx, bot, match)
await asyncio.sleep(5)
- await ctx.channel.purge(after=main_msg)
-
- # FIRST GAME VETO PROCESS
- # cross out all counterpick stages as they are not valid for
- # first veto
- embed.set_field_at(2, name="Counterpick Stages",
- value=counters_message(counters))
- await main_msg.edit(embed=embed)
-
- # run initial game veto
- main_msg, embed, player1, player2, p1_dsr_stage, p2_dsr_stage = \
- await smash.initial(ctx, bot, main_msg, player1, player2,
- embed)
-
- # SECOND GAME VETO PROCESS
- main_msg, embed, player1, player2, p1_dsr_stage, p2_dsr_stage = \
- await smash.nonInitial(ctx, bot, main_msg, player1, player2,
- p1_dsr_stage, p2_dsr_stage, embed,
- 2, 4, 5)
-
- # THIRD GAME VETO PROCESS
- # exits if a DSR stage list is longer than 1
- if len(p1_dsr_stage) > 1 or len(p2_dsr_stage) > 1:
- # reset messages
- await ctx.channel.purge(after=main_msg)
-
- # cross out all of game 3
- embed.set_field_at(6, name='~~` Game '
- '3 `~~',
- value='~~**Winner:**~~', inline=False)
- embed.set_field_at(7, name="Starter Stages",
- value=starters_message(starters))
- embed.set_field_at(8, name="Counterpick Stages",
- value=counters_message(counters))
- await main_msg.edit(embed=embed)
-
- # final message
- await ctx.send("GG!")
- return
-
- # runs non initial veto with player 1's DSR start on removed stages
- await smash.nonInitial(ctx, bot, main_msg, player1, player2,
- p1_dsr_stage, p2_dsr_stage, embed, 3, 7, 8)
- # final message
- await ctx.send("GG!")
+
+ # run veto's
+ while match.winner is None:
+ await match.veto(ctx, bot)
# if the veto times out
except asyncio.TimeoutError:
- # purge all messages after original message
- await ctx.channel.purge(after=main_msg)
-
# get error embed and edit original message
- error_embed = await embeds.timeout_error()
- await main_msg.edit(embed=error_embed)
-
- # elif smash bo5
- # FIXME LATER - LMAO I COPY PASTED THIS SHIT FROM BO3, DEF NOT OPTIMIZED
- elif game.lower() == 'smash' and series_length.lower() == 'bo5':
- # starting/loading embed
- main_msg = await ctx.send(embed=await embeds.starting())
-
- # player objects
- player1, player2 = await player_utils.get_players(ctx)
-
- # send first veto embed
- embed = await embeds.smash_veto(player1, player2, 5)
- await main_msg.edit(embed=embed)
-
- # run veto with catch statement in case of time out
- try:
- # HIGHER SEED SELECTION
- await ctx.send(f"{newline}Player 1 (the higher seed) say `me`")
+ await ctx.send(embed=await embeds.timeout_error())
- # checks which user says me
- def playerCheck(message):
- return message.content.lower() == 'me' \
- and message.channel == ctx.channel
-
- msg = await bot.wait_for('message', check=playerCheck, timeout=300)
-
- # changes player order if player 2 said they were first seed
- if msg.author == player2:
- player1 = player2
- player2 = ctx.author
-
- # notifies of veto starts
- await ctx.send(f"Starting veto with {player1.mention} as "
- f"**Player 1** and {player2.mention} "
- f"as **Player 2** in 5 seconds...")
+ else:
+ text = f"Matches must either be a best of 3 or 5.\n\n" \
+ f"Example: {smash_example}"
+ await ctx.send(embed=await embeds.invalid_param_error(text))
- # delete all messages and begin veto after 5 seconds
- await asyncio.sleep(5)
- await ctx.channel.purge(after=main_msg)
-
- # FIRST GAME VETO PROCESS
- # cross out all counterpick stages as they are not valid for
- # first veto
- embed.set_field_at(2, name="Counterpick Stages",
- value=counters_message(counters))
- await main_msg.edit(embed=embed)
-
- # run initial game veto
- main_msg, embed, player1, player2, p1_dsr_stage, p2_dsr_stage = \
- await smash.initial(ctx, bot, main_msg, player1, player2,
- embed)
-
- # SECOND GAME VETO PROCESS
- main_msg, embed, player1, player2, p1_dsr_stage, p2_dsr_stage = \
- await smash.nonInitial(ctx, bot, main_msg, player1, player2,
- p1_dsr_stage, p2_dsr_stage, embed,
- 2, 4, 5)
-
- # THIRD GAME VETO PROCESS
- main_msg, embed, player1, player2, p1_dsr_stage, p2_dsr_stage = \
- await smash.nonInitial(ctx, bot, main_msg, player1, player2,
- p1_dsr_stage, p2_dsr_stage, embed,
- 3, 7, 8)
-
- # FOURTH GAME VETO PROCESS
- # exits if a DSR stage list is longer than 2
- if len(p1_dsr_stage) > 2 or len(p2_dsr_stage) > 2:
- # reset messages
- await ctx.channel.purge(after=main_msg)
-
- # cross out all of game 4
- embed.set_field_at(9, name='~~` Game '
- '4 `~~',
- value='~~**Winner:**~~', inline=False)
- embed.set_field_at(10, name="Starter Stages",
- value=starters_message(starters))
- embed.set_field_at(11, name="Counterpick Stages",
- value=counters_message(counters))
- await main_msg.edit(embed=embed)
-
- # cross out all of game 5
- embed.set_field_at(12, name='~~` '
- 'Game 5 '
- ' `~~',
- value='~~**Winner:**~~', inline=False)
- embed.set_field_at(13, name="Starter Stages",
- value=starters_message(starters))
- embed.set_field_at(14, name="Counterpick Stages",
- value=counters_message(counters))
- await main_msg.edit(embed=embed)
-
- # send final message and return
- await ctx.send('GG!')
- return
-
- # run veto for game 4
- main_msg, embed, player1, player2, p1_dsr_stage, p2_dsr_stage = \
- await smash.nonInitial(ctx, bot, main_msg, player1, player2,
- p1_dsr_stage, p2_dsr_stage, embed,
- 4, 10, 11)
-
- # FIFTH GAME VETO PROCESS
- # exits if a DSR stage list is longer than 2
- if len(p1_dsr_stage) > 2 or len(p2_dsr_stage) > 2:
- # reset messages
- await ctx.channel.purge(after=main_msg)
-
- # cross out all of game 5
- embed.set_field_at(12, name='~~` '
- 'Game 5 '
- ' `~~',
- value='~~**Winner:**~~', inline=False)
- embed.set_field_at(13, name="Starter Stages",
- value=starters_message(starters))
- embed.set_field_at(14, name="Counterpick Stages",
- value=counters_message(counters))
- await main_msg.edit(embed=embed)
-
- # send final message and return
- await ctx.send('GG!')
- return
-
- # run veto for game 5
- main_msg, embed, player1, player2, p1_dsr_stage, p2_dsr_stage = \
- await smash.nonInitial(ctx, bot, main_msg, player1, player2,
- p1_dsr_stage, p2_dsr_stage, embed,
- 5, 13, 14)
-
- # final message
- await ctx.send('GG!')
- # if the veto times out
- except asyncio.TimeoutError:
- # purge all messages after original message
- await ctx.channel.purge(after=main_msg)
+@bot.command(aliases=['valorant'])
+async def val(ctx, series_length=None, opponent=None):
+ """
+ Runs a VALORANT veto with your opponent
+ """
+ # guild and category objects
+ guild = bot.get_guild(guild_id)
+ active_category = guild.get_channel(active_channels_id)
- # get error embed and edit original message
- error_embed = await embeds.timeout_error()
- await main_msg.edit(embed=error_embed)
+ # check if a valid place to start matches
+ if ctx.channel not in active_category.text_channels or \
+ ctx.channel.id == match_creation_channel_id:
+ text = "You can't do that here! Invoke a match chat first with " \
+ "`ve!match {@opponent}`"
+ await ctx.send(embed=await embeds.missing_permission_error(text))
- elif 'val' in game.lower():
- main_msg = await ctx.send(embed=await embeds.starting())
+ # let user know if they're missing a parameter
+ elif series_length is None or opponent is None:
+ text = "Initiate a veto with `ve!veto {game} " \
+ "{best-of (3 or 5)} @{opponent}` "
+ await ctx.send(embed=await embeds.missing_param_error(text))
- # player objects
+ elif series_length == '1' or series_length == '3' or series_length == '5':
+ # get players
player1, player2 = await player_utils.get_players(ctx)
- games = int(series_length[2])
+ # coinflip to determine seeding
+ player1, player2 = await player_utils.coinflip(ctx, player1, player2)
+ # initialize game
+ match = ValMatch(player1, player2, int(series_length))
- # send first veto embed
- embed = await embeds.valorant_veto(player1, player2, games)
- await main_msg.edit(embed=embed)
+ # start delay
+ await ctx.send(f"Starting veto with {player1.mention} as "
+ f"**Captain 1** and {player2.mention} "
+ f"as **Captain 2** in 5 seconds...")
+ await asyncio.sleep(5)
# run veto
try:
- # best of 1 veto
- if games == 1:
- embed, main_msg = await valorant.bo1(ctx, bot, main_msg,
- player1, player2, embed)
- await ctx.send('GG!')
- # best of 3 veto
- elif games == 3:
- embed, main_msg = await valorant.bo3(ctx, bot, main_msg,
- player1, player2, embed)
- await ctx.send('GG!')
+ await match.veto(ctx, bot)
# if the veto times out
except asyncio.TimeoutError:
- # purge all messages after original message
- await ctx.channel.purge(after=main_msg)
-
# get error embed and edit original message
- error_embed = await embeds.timeout_error()
- await main_msg.edit(embed=error_embed)
+ await ctx.send(embed=await embeds.timeout_error())
+
+ else:
+ text = f"Matches must either be a best of 1, 3, or 5.\n\n" \
+ f"Example: {valorant_example}"
+ await ctx.send(embed=await embeds.invalid_param_error(text))
@bot.command()
@@ -304,68 +138,83 @@ async def match(ctx, opponent=None):
"""
Creates a private text channel between you and your opponent(s)
"""
+ # checks that this it he correct channel to use
+ if ctx.channel.id != match_creation_channel_id:
+ # get match creation channel object
+ match_channel = bot.get_channel(match_creation_channel_id)
+
+ # tell user they have to do it over in match creation channel
+ text = f"You can't do that here! Invoke a match chat first over at " \
+ f"{match_channel.mention}"
+ await ctx.send(embed=await embeds.missing_permission_error(text))
+
# let user know if they didn't enter an opponent
- if opponent is None:
+ elif opponent is None:
text = "Initiate a match chat with `ve!match @{opponent}`"
await ctx.send(embed=await embeds.missing_param_error(text))
return
- # run command if they have proper arguments
- # send initial starting message
- main_msg = await ctx.send(embed=await embeds.starting())
+ else:
+ # run command if they have proper arguments
+ # send initial starting message
+ main_msg = await ctx.send(embed=await embeds.starting())
- # player objects
- player1, player2 = await player_utils.get_players(ctx)
+ # player objects
+ player1, player2 = await player_utils.get_players(ctx)
- # guild and category objects
- guild = bot.get_guild(guild_id)
- active_category = guild.get_channel(active_channels_id)
+ # guild and category objects
+ guild = bot.get_guild(guild_id)
+ active_category = guild.get_channel(active_channels_id)
- # game coordinator role
- game_coordinator = guild.get_role(TO_role_id)
-
- # overwrites for the match channel
- overwrites = {
- player1: discord.PermissionOverwrite(add_reactions=True,
- read_messages=True,
- send_messages=True,
- external_emojis=True,
- attach_files=True,
- embed_links=True),
-
- player2: discord.PermissionOverwrite(add_reactions=True,
- read_messages=True,
- send_messages=True,
- external_emojis=True,
- attach_files=True,
- embed_links=True),
-
- game_coordinator: discord.PermissionOverwrite(add_reactions=True,
- read_messages=True,
- send_messages=True,
- external_emojis=True,
- attach_files=True,
- embed_links=True),
- guild.default_role: discord.PermissionOverwrite(send_messages=False)
- }
-
- # create channel
- name = f"{player1.name} vs {player2.name}"
- topic = f"{tourney_name}: {player1.name} vs {player2.name}"
- reason = "User invoked tourney match channel"
- match_channel = await guild.create_text_channel(name,
- category=active_category,
- topic=topic,
- reason=reason,
- overwrites=overwrites)
-
- # send message linking to channel
- await main_msg.edit(embed=await embeds.match_started(match_channel))
-
- # send instructions into the channel
- await match_channel.send("Once both sides are ready, invoke the veto "
- "process with `ve!veto {game} "
- "{series_length} {@opponent}`")
+ # game coordinator role
+ game_coordinator = guild.get_role(TO_role_id)
+
+ # overwrites for the match channel
+ overwrites = {
+ player1: discord.PermissionOverwrite(add_reactions=True,
+ read_messages=True,
+ send_messages=True,
+ external_emojis=True,
+ attach_files=True,
+ embed_links=True),
+
+ player2: discord.PermissionOverwrite(add_reactions=True,
+ read_messages=True,
+ send_messages=True,
+ external_emojis=True,
+ attach_files=True,
+ embed_links=True),
+
+ game_coordinator: discord.PermissionOverwrite(add_reactions=True,
+ read_messages=True,
+ send_messages=True,
+ external_emojis=True,
+ attach_files=True,
+ embed_links=True),
+ guild.default_role: discord.PermissionOverwrite(
+ send_messages=False,
+ read_messages=True)
+ }
+
+ # create channel
+ name = f"{player1.name} vs {player2.name}"
+ topic = f"{tourney_name}: {player1.name} vs {player2.name}"
+ reason = "User invoked tourney match channel"
+ match_channel = await \
+ guild.create_text_channel(name,
+ category=active_category,
+ topic=topic,
+ reason=reason,
+ overwrites=overwrites)
+
+ # send message linking to channel
+ await main_msg.edit(embed=await embeds.match_started(match_channel))
+
+ # send instructions into the channel
+ await match_channel.send("Once both sides are ready, invoke the veto "
+ "process with `ve!veto {game}"
+ "{best-of (3 or 5)} {@opponent}`. "
+ "For example: `ve!veto smash 3 @Harry`")
@bot.command()
@@ -373,38 +222,56 @@ async def close(ctx):
"""
Moves the channel to the inactive category
"""
- # message placeholder
- main_msg = await ctx.send(embed=await embeds.starting())
+ # guild and category objects
+ guild = bot.get_guild(guild_id)
+ active_category = guild.get_channel(active_channels_id)
- if ctx.channel.id in restricted_channels_ids:
+ # let user know the channel isn't an active match channel
+ if ctx.channel not in active_category.text_channels:
text = "You can't do that here! You can only close channels in the " \
"active matches category."
- await main_msg.edit(embed=await embeds.missing_permission_error(text))
+ await ctx.send(embed=await embeds.missing_permission_error(text))
+
+ # let user know they can't close this channel
+ elif ctx.channel.id == match_creation_channel_id:
+ text = "You can't close this channel! It's not a match channel :smile:"
+ await ctx.send(embed=await embeds.missing_permission_error(text))
else:
+ # notifies users of archived channel
+ await ctx.send(embed=await embeds.match_archived())
+
# guild and category objects
guild = bot.get_guild(guild_id)
inactive_category = guild.get_channel(inactive_channels_id)
+ # game coordinator role
+ game_coordinator = guild.get_role(TO_role_id)
# overwrites for the match channel
overwrites = {
guild.default_role: discord.PermissionOverwrite(
- send_messages=False)
+ send_messages=False,
+ read_messages=True),
+ game_coordinator: discord.PermissionOverwrite(add_reactions=True,
+ read_messages=True,
+ send_messages=True,
+ external_emojis=True,
+ attach_files=True,
+ embed_links=True)
}
await ctx.channel.edit(category=inactive_category,
overwrites=overwrites)
- # notifies users of archived channel
- await main_msg.edit(embed=await embeds.match_archived())
-@bot.command()
+@bot.command(aliases=['flip'])
async def coinflip(ctx, opponent=None):
if opponent is None:
text = "You need to specify an opponent!"
await ctx.send(embed=await embeds.missing_param_error(text))
else:
- await player_utils.coinflip(ctx, ctx.author, ctx.message.mentions[0])
+ player1, player2 = await player_utils.get_players(ctx)
+ await player_utils.coinflip(ctx, player1, player2)
@bot.command()
diff --git a/settings.py b/settings.py
index 589aeb1..af9976b 100644
--- a/settings.py
+++ b/settings.py
@@ -1,32 +1,98 @@
# embed settings
tourney_name = "December 2020 Smash Ult. Monthly"
-footer_note = "Ping Brandon for help."
+footer_note = "© Brandon Ly 2021"
footer_icon = "https://vaughanesports.org/assets/Vaughan%20Esports%20Logo.png"
-rulebook_url = "https://vaughanesports.org/rules"
-newline = "_ _\n"
+rulebook_url = "https://vaughanesports.org/rules" # url to rulebook
+newline = "_ _\n" # dont touch me
+
+# one of these should be on at least
+cross_map_on_veto = True # crosses out the stage when veto
+hide_map_on_veto = True # covers stage in spoiler tag when veto
# bot settings
prefix = "ve!"
description = "Tournament Bot for Vaughan Esports"
# smash stages
-starters = ["Battlefield", "Small Battlefield", "Pokemon Stadium 2",
- "Town And City", "Final Destination"]
-counters = ["Kalos Pokemon League", "Lylat Cruise", "Yoshi's Story",
- "Smashville"]
+# abbreviations: https://www.ssbwiki.com/List_of_abbreviations#Stages
+stages = [
+ {'name': 'Battlefield',
+ 'starter': True,
+ 'aliases': [
+ 'bf'
+ ]},
+ {'name': 'Small Battlefield',
+ 'starter': True,
+ 'aliases': [
+ 'sbf',
+ 'small bf'
+ ]},
+ {'name': 'Pokemon Stadium 2',
+ 'starter': True,
+ 'aliases': [
+ 'ps2'
+ ]},
+ {'name': 'Town And City',
+ 'starter': True,
+ 'aliases': [
+ 'tan',
+ 'town',
+ 't&c',
+ 'city',
+ 'tac',
+ 'tnc',
+ 'tc'
+ ]},
+ {'name': 'Final Destination',
+ 'starter': True,
+ 'aliases': [
+ 'fd',
+ 'final d'
+ ]},
+ {'name': 'Kalos Pokemon League',
+ 'starter': False,
+ 'aliases': [
+ 'kalos',
+ 'kpl'
+ ]},
+ {'name': 'Lylat Cruise',
+ 'starter': False,
+ 'aliases': [
+ 'lylat',
+ 'lc'
+ ]},
+ {'name': 'Yoshi\'s Story',
+ 'starter': False,
+ 'aliases': [
+ 'ys',
+ 'yoshis',
+ 'yoshi\'s',
+ 'yoshi'
+ ]},
+ {'name': 'Smashville',
+ 'starter': False,
+ 'aliases': [
+ 'sv',
+ 'smashv',
+ 'ville'
+ ]}
+]
# valorant maps
-maps = ["Bind", "Split", "Haven", "Ascent", "Icebox"]
+val_maps = ["Bind", "Split", "Haven", "Ascent", "Icebox"]
# tourney categories
-active_channels_id = 777443551478153216
-inactive_channels_id = 777443021654196264
-guild_id = 688141732507942918
-TO_role_id = 709560702225874976
-
-# last two are test channel
-restricted_channels_ids = [688534418939445315, 712960670735400991,
- 778693693569368084, 703347224985337897,
- 771131053519011860, 697935838096654347,
- 688943624553365568, 688141732507942926,
- 773550892263014400, 775542351640002600]
+active_channels_id = 777421991031734292
+inactive_channels_id = 777422048943013908
+guild_id = 762532363695292455
+TO_role_id = 822710142985830410
+
+# channel where matches can be started
+match_creation_channel_id = 828867315256000513
+
+# timeout settings (in seconds)
+veto_timeout = 1800
+
+# example commands (discord formatting)
+smash_example = '`ve!smash 3 @Brandon`'
+valorant_example = '`ve!val 1 @Brandon`'
diff --git a/veto/__init__.py b/smash/__init__.py
similarity index 100%
rename from veto/__init__.py
rename to smash/__init__.py
diff --git a/smash/game.py b/smash/game.py
new file mode 100644
index 0000000..e4a6a3d
--- /dev/null
+++ b/smash/game.py
@@ -0,0 +1,73 @@
+from smash.stagelist import StageList
+
+
+class Game:
+ """
+ Represents a Smash game
+ """
+
+ def __init__(self, game_num: int):
+ self.name = f"` " \
+ f"Game {game_num + 1}" \
+ f" `"
+
+ self.selected_stage = None
+ self.winner = None
+
+ self.stagelist: StageList = StageList()
+
+ # auto veto counters if first game
+ if game_num == 0:
+ for x in range(len(self.stagelist.counters)):
+ self.stagelist.counters[x].veto = True
+
+ def starters_embed(self) -> str:
+ """
+ Generate embed string for stagelist starters
+ :return:
+ """
+ message = ""
+ # loop through start stage names
+ for x in range(len(self.stagelist.starters)):
+ # append each name to a new line in the message
+ message = f'{message}{self.stagelist.starters[x]}\n'
+ # return message string
+ return message
+
+ def counters_embed(self) -> str:
+ """
+ Generate embed string for stagelist counters
+ :return:
+ """
+ message = ""
+ # loop through counter stage names
+ for x in range(len(self.stagelist.counters)):
+ # append each name to a new line in the message
+ message = f'{message}{self.stagelist.counters[x]}\n'
+ # return message string
+ return message
+
+ def winner_embed(self) -> str:
+ """
+ Generate embed string for winner line
+ :return:
+ """
+ message = "**Winner:**"
+ if self.winner is None:
+ return f"{message} TBD"
+ else:
+ return f"{message} {self.winner.mention}"
+
+ def choose_stage(self, stage: str):
+ """
+ Tries to choose a stage
+ :param stage: name/alias of stage to chose
+ """
+ self.selected_stage = self.stagelist.choose(stage)
+
+ def veto_stage(self, stage: str):
+ """
+ Tries to veto a stage
+ :param stage: name/alias of stage to veto
+ """
+ self.stagelist.veto(stage)
diff --git a/smash/match.py b/smash/match.py
new file mode 100644
index 0000000..64892b7
--- /dev/null
+++ b/smash/match.py
@@ -0,0 +1,193 @@
+import asyncio
+from typing import List
+
+import discord
+from discord.ext import commands
+
+from settings import rulebook_url, tourney_name, footer_icon, footer_note, \
+ veto_timeout, newline
+from smash.game import Game
+from smash.player import Player
+from utils.checks import playerCheck, stageCheck
+
+
+class Match:
+ """
+ Represents a Smash match
+ """
+
+ def __init__(self, player1: Player, player2: Player, num_of_games: int):
+ # players
+ self.player1 = player1
+ self.player2 = player2
+ self.winner = None
+
+ # match data
+ self.games: List[Game] = []
+ self.name: str = f"{tourney_name}: {player1.name} vs {player2.name}"
+ self.description: str = f"{self.player1.mention} vs " \
+ f"{self.player2.mention} " \
+ f"\nThe rulebook can be found " \
+ f"[here]({rulebook_url})"
+
+ # match state
+ self.num_of_games: int = num_of_games
+ self.current_game: int = 0
+
+ # generate first game
+ self.games.append(Game(0))
+
+ @property
+ def embed(self):
+ title = f"Smash Ultimate Best-of-{self.num_of_games} Veto"
+ embed = discord.Embed(title=title,
+ description=self.description,
+ color=discord.Colour.gold())
+ # loop through max games times and generate embed fields
+ for x in range(len(self.games)):
+ embed.add_field(name=self.games[x].name,
+ value=self.games[x].winner_embed(),
+ inline=False)
+ # x - 1 because its using index num
+ embed.add_field(name="__Starter Stages__",
+ value=self.games[x].starters_embed(),
+ inline=True)
+ embed.add_field(name="__Counterpick Stages__",
+ value=self.games[x].counters_embed(),
+ inline=True)
+ # set footer
+ embed.set_footer(icon_url=footer_icon,
+ text=f"{tourney_name} | {footer_note}")
+
+ return embed
+
+ async def veto(self, ctx: discord.ext.commands.Context,
+ bot: discord.ext.commands.Bot):
+ # FIGURING OUT GAME EMBED INDEXES (ONLY IF U WANT TO REWRITE):
+ # GAME NUM * 3 = embed title index, just + 1 and + 2 for stage lists
+ # IF FINISH EARLY LOOP FROM CURRENT_GAME TO NUM_OF_GAMES - 1
+ # AND CROSS OUT LINE
+
+ # add DSR stages to the game if on game 3 or higher
+ if self.current_game >= 2:
+ for x in range(len(self.player2.dsr)):
+ self.games[self.current_game].veto_stage(
+ self.player2.dsr[x].name)
+
+ # send first embed
+ await ctx.send(embed=self.embed)
+
+ # initial veto
+ if self.current_game == 0:
+ # loop through 4 stage veto's/selection
+ for x in range(4):
+ # check which message to send
+ if x == 0:
+ await ctx.send(f"{self.player1.mention}, veto a starter.")
+ if x == 1:
+ await ctx.send(f"{self.player2.mention}, veto a starter.")
+ if x == 2:
+ await ctx.send(
+ f"{self.player2.mention}, veto another starter.")
+ if x == 3:
+ await ctx.send(
+ f"{self.player1.mention}, select the stage from "
+ f"the remaining starters.")
+
+ # wait for players stage choice
+ msg = await bot.wait_for('message',
+ check=stageCheck(ctx, self),
+ timeout=veto_timeout)
+
+ # if on stage selection
+ if x == 3:
+ # choose the stage
+ self.games[self.current_game].choose_stage(msg.content)
+ # if on veto still
+ else:
+ # veto the stage
+ self.games[self.current_game].veto_stage(msg.content)
+
+ # regenerate and send embed
+ await ctx.send(embed=self.embed)
+
+ # subsequent veto
+ else:
+ # loop through 3 stage veto's/selection process
+ for x in range(3):
+ if x == 0:
+ await ctx.send(
+ f"{self.player1.mention}, veto a stage.")
+ elif x == 1:
+ await ctx.send(
+ f"{self.player1.mention}, veto another stage.")
+ elif x == 2:
+ await ctx.send(
+ f"{self.player2.mention}, select the stage.")
+
+ # wait for players stage choice
+ msg = await bot.wait_for('message',
+ check=stageCheck(ctx, self),
+ timeout=veto_timeout)
+
+ # if on stage selection
+ if x == 2:
+ # choose the stage
+ self.games[self.current_game].choose_stage(msg.content)
+ # if on veto still
+ else:
+ # veto the stage
+ self.games[self.current_game].veto_stage(msg.content)
+
+ # regenerate and send embed
+ await ctx.send(embed=self.embed)
+
+ # get winner
+ await ctx.send(f"{newline}GLHF! Once finished, "
+ f"the winner should say `me`.")
+ msg = await bot.wait_for('message',
+ check=playerCheck(ctx),
+ timeout=veto_timeout)
+
+ # swap player 1 if player 2 was winner
+ if msg.author.id == self.player2.id:
+ self.swap_players()
+
+ # add stage to winner dsr list
+ self.player1.dsr.append(
+ self.games[self.current_game].selected_stage)
+
+ # set the game winner
+ self.games[self.current_game].winner = self.player1
+ self.player1.wins += 1
+
+ # check if the match has a winner
+ if self.player1.wins >= (self.num_of_games // 2) + 1:
+ self.winner = self.player1
+ await ctx.send(embed=self.embed)
+ await ctx.send('GG!')
+
+ # else prep for the next game veto
+ else:
+ # move to next game
+ self.current_game += 1
+
+ # add games
+ self.games.append(Game(self.current_game))
+
+ # setup next game
+ await ctx.send(embed=self.embed)
+ await ctx.send('Starting next game veto...')
+ await asyncio.sleep(3)
+
+ def swap_players(self):
+ """
+ Swap player1 and player2
+ """
+ # swap player objects
+ p1 = self.player2
+ p2 = self.player1
+
+ # set new players
+ self.player1 = p1
+ self.player2 = p2
diff --git a/smash/player.py b/smash/player.py
new file mode 100644
index 0000000..49ab254
--- /dev/null
+++ b/smash/player.py
@@ -0,0 +1,32 @@
+from typing import List
+
+import discord
+
+from smash.stage import Stage
+
+
+class Player:
+ """
+ Represents a player in a Smash match
+ """
+
+ def __init__(self, user: discord.Member):
+ """
+ Constructor method for a Smash Player
+ :param user: Discord user to grab info from
+ """
+ self.name = user.display_name
+ self.mention = user.mention
+ self.id = user.id
+ self.wins = 0
+
+ # list of DSR stages
+ self.dsr: List[Stage] = []
+
+ def __eq__(self, other) -> bool:
+ """
+ Compares self to discord member
+ :param other: Discord member to check
+ :return: boolean for whether they are the same
+ """
+ return self.id == other.id
diff --git a/smash/stage.py b/smash/stage.py
new file mode 100644
index 0000000..11b0fee
--- /dev/null
+++ b/smash/stage.py
@@ -0,0 +1,51 @@
+from string import capwords
+from typing import List
+
+from settings import hide_map_on_veto, cross_map_on_veto
+
+
+class Stage:
+ """
+ Represents a Smash stage
+ """
+
+ def __init__(self, name: str, starter: bool, aliases: List[str] = None):
+ """
+ Constructor Method
+ :param name: name of the stage
+ :param starter: if stage is a starter
+ :param aliases: alias names for the stage
+ """
+ self.name = name
+ self.veto = False
+ self.chosen = False
+ self.starter = starter
+ self.aliases = aliases
+
+ def __str__(self) -> str:
+ """
+ String representation of a map
+ :return:
+ """
+ if self.veto:
+ name = self.name
+ # surround in cross out if crossing
+ if cross_map_on_veto:
+ name = f'~~{name}~~'
+ # surround in spoiler tags if hiding
+ if hide_map_on_veto:
+ name = f'||{name}||'
+ # return the name of the stage
+ return name
+ elif self.chosen:
+ return f'⮕**{self.name}**'
+ else:
+ return self.name
+
+ def __eq__(self, stage: str) -> bool:
+ """
+ Compares a string of the stage name to its name and aliases
+ :param stage: string name of the stage
+ :return: boolean if matching
+ """
+ return capwords(stage) == self.name or stage.lower() in self.aliases
diff --git a/smash/stagelist.py b/smash/stagelist.py
new file mode 100644
index 0000000..e14f4e4
--- /dev/null
+++ b/smash/stagelist.py
@@ -0,0 +1,93 @@
+from typing import List
+
+from settings import stages
+from smash.stage import Stage
+
+
+class StageList:
+ """
+ Represents a list of smash stages
+ """
+
+ def __init__(self):
+ # stage lists
+ self.starters: List[Stage] = []
+ self.counters: List[Stage] = []
+
+ # loops through the stages from settings
+ for x in range(len(stages)):
+ # if starter bool = true
+ if stages[x]['starter']:
+ # create new stage from info and add to starters
+ self.starters.append(Stage(stages[x]['name'],
+ stages[x]['starter'],
+ stages[x]['aliases']))
+ # if starter bool = false
+ else:
+ # create new stage from info and add to counters
+ self.counters.append(Stage(stages[x]['name'],
+ stages[x]['starter'],
+ stages[x]['aliases']))
+
+ def veto(self, stage: str) -> Stage:
+ """
+ Tries to veto a stage
+ :param stage: name/alias of stage to veto
+ :return: the stage object that was veto'd
+ """
+ # search for stage in starters
+ for x in range(len(self.starters)):
+ if self.starters[x] == stage:
+ # if matching, change the state of the stage
+ self.starters[x].veto = True
+ return self.starters[x]
+
+ # search through counters
+ for x in range(len(self.counters)):
+ if self.counters[x] == stage:
+ # if matching, change the state of the stage
+ self.counters[x].veto = True
+ return self.counters[x]
+
+ def choose(self, stage: str) -> Stage:
+ """
+ Chooses a stage
+ :param stage: name/alias of stage to veto
+ :return: the stage object that was chosen
+ """
+ # search for stage in starters
+ for x in range(len(self.starters)):
+ if self.starters[x] == stage:
+ # if matching, change the state of the stage
+ self.starters[x].chosen = True
+ return self.starters[x]
+
+ # search through counters
+ for x in range(len(self.counters)):
+ if self.counters[x] == stage:
+ # if matching, change the state of the stage
+ self.counters[x].chosen = True
+ return self.counters[x]
+
+ def __contains__(self, stage: str):
+ """
+ Check's if a string is the name of a stage
+ :param stage:
+ :return:
+ """
+ # search for stage in starters
+ for x in range(len(self.starters)):
+ if self.starters[x] == stage and \
+ not self.starters[x].veto and \
+ not self.starters[x].chosen:
+ return True
+
+ # search through counters
+ for x in range(len(self.counters)):
+ if self.counters[x] == stage and \
+ not self.counters[x].veto and \
+ not self.counters[x].chosen:
+ return True
+
+ # returns false if it wasn't successful
+ return False
diff --git a/utils/checks.py b/utils/checks.py
new file mode 100644
index 0000000..f0b8dcc
--- /dev/null
+++ b/utils/checks.py
@@ -0,0 +1,51 @@
+def playerCheck(ctx):
+ def func(message):
+ """Checks if a player said 'me' """
+ return message.content.lower() == 'me' \
+ and message.channel == ctx.channel
+
+ return func
+
+
+def stageCheck(ctx, match):
+ """
+ Check if the stage name is valid
+ :param ctx: discord context to check for channel
+ :param match: match to check stagelist and game from
+ :return:
+ """
+
+ def func(message):
+ return message.content in match.games[match.current_game].stagelist \
+ and message.channel == ctx.channel
+
+ return func
+
+
+def mapCheck(ctx, match):
+ """
+ Check if the stage name is valid
+ :param ctx: discord context to check for channel
+ :param match: match to check stagelist and game from
+ :return:
+ """
+
+ def func(message):
+ return message.content in match.games[match.current_game].map_pool \
+ and message.channel == ctx.channel
+
+ return func
+
+
+def sideCheck(ctx):
+ """
+ Check if valid valorant side
+ :param ctx: discord context to check for channel
+ :return:
+ """
+
+ def func(message):
+ return ('att' in message.content or 'def' in message.content) and \
+ message.channel == ctx.channel
+
+ return func
diff --git a/utils/embeds.py b/utils/embeds.py
index b61a485..1224782 100644
--- a/utils/embeds.py
+++ b/utils/embeds.py
@@ -1,4 +1,6 @@
-from utils.message_generators import *
+import discord
+
+from settings import tourney_name, footer_note, footer_icon
async def starting():
@@ -38,6 +40,19 @@ async def missing_param_error(error_message: str):
return embed
+async def invalid_param_error(error_message: str):
+ # generate embed with red colour
+ embed = discord.Embed(color=discord.Colour.red())
+ # set title field
+ embed.add_field(name=f"Invalid Parameter Error", value=error_message)
+ # set footer
+ embed.set_footer(icon_url=footer_icon,
+ text=f"{tourney_name} | {footer_note}")
+
+ # return finished embed
+ return embed
+
+
async def missing_permission_error(error_message: str):
# generate embed with red colour
embed = discord.Embed(color=discord.Colour.red())
@@ -93,79 +108,12 @@ async def purged():
return embed
-async def flipping_coin(dot):
- # generate embed with blue colour
- embed = discord.Embed(colour=discord.Colour.blue())
- # set starting field
- embed.set_author(name=f"Flipping coin{'.' * dot}")
-
- # return finished embed
- return embed
-
-
async def coinflip_winner(winner: discord.User):
# generate embed with green colour
embed = discord.Embed(colour=discord.Colour.green())
# set starting field
- embed.set_author(name=f"{winner.name} won the coinflip!")
-
- # return finished embed
- return embed
-
-
-async def smash_veto(player1: discord.User, player2: discord.User,
- max_games: int):
- # generate embed
- desc = f"{player1.mention} vs {player2.mention} " \
- f"\nThe rulebook can be found [here]({rulebook_url})"
- embed = discord.Embed(title=f"Smash Ultimate Best-of-{max_games} Veto",
- description=desc,
- color=discord.Colour.gold())
-
- # loop through max games times and generate embed fields
- for x in range(1, max_games + 1):
- embed.add_field(
- name=f"` Game {x} "
- f" `",
- value="**Winner:** TBD", inline=False)
- embed.add_field(name="Starter Stages", value=starters_message(),
- inline=True)
- embed.add_field(name="Counterpick Stages",
- value=counters_message(), inline=True)
-
- # set footer
- embed.set_footer(icon_url=footer_icon,
- text=f"{tourney_name} | {footer_note}")
-
- # return finished embed
- return embed
-
-
-async def valorant_veto(player1: discord.User, player2: discord.User,
- max_games: int):
- # generate embed
- desc = f"**Captains:** {player1.mention}, {player2.mention} " \
- f"\nThe rulebook can be found [here]({rulebook_url})"
- embed = discord.Embed(title=f"VALORANT Best-of-{max_games} Veto",
- description=desc,
- color=discord.Colour.gold())
-
- # loop through max games times and generate embed fields
- for x in range(1, max_games + 1):
- embed.add_field(
- name=f"` Game {x} "
- f" `",
- value="**Winner:** TBD", inline=False)
- embed.add_field(name="Maps", value=valorant_maps_message(),
- inline=True)
-
- embed.add_field(name="Starting Sides", value=f"**Attack:** TBD "
- f"\n**Defense:** TBD",
- inline=True)
-
- # set footer
- embed.set_footer(icon_url=footer_icon,
- text=f"{tourney_name} | {footer_note}")
+ embed.add_field(name="Coinflip",
+ value=f"{winner.mention} won the coinflip!")
# return finished embed
return embed
diff --git a/utils/message_generators.py b/utils/message_generators.py
deleted file mode 100644
index 3ab00cc..0000000
--- a/utils/message_generators.py
+++ /dev/null
@@ -1,77 +0,0 @@
-import discord
-
-from settings import *
-
-
-def map_list_message(map_list: list, removed_maps: list, selected_map: str):
- """
- Base map list message generator
- :param map_list: string list of map names
- :param removed_maps: string list of removed maps
- :param selected_map: string name of the selected map
- :return: output message for embed
- """
- # blank string
- message = ""
- # loop through starter stages
- for x in range(len(map_list)):
- # if removed maps is none then don't worry about striking out
- if removed_maps is not None:
- # bolds the map if its selected
- if map_list[x] == selected_map:
- message = f"{message}⮕**{map_list[x]}**\n"
- # crosses map out if its in the removed_maps list
- elif map_list[x] in removed_maps:
- message = f"{message}~~{map_list[x]}~~\n"
- # else concatenate the new map name regularly
- else:
- message = f"{message}{map_list[x]}\n"
- # concatenate the new map name regularly
- else:
- message = f"{message}{map_list[x]}\n"
- return message
-
-
-def starters_message(removed_stages=None, selected_stage=None):
- """
- Generates the smash starter stages list
- :param selected_stage: string name of the stage that is selected
- :param removed_stages: list of veto'd stages (exact spellings)
- :return: string for embed value
- """
- return map_list_message(starters, removed_stages, selected_stage)
-
-
-def counters_message(removed_stages=None, selected_stage=None):
- """
- Generates the smash counterpick stages list
- :param selected_stage: string name of the stage that is selected
- :param removed_stages: list of veto'd stages (exact spellings)
- :return: string for embed value
- """
- return map_list_message(counters, removed_stages, selected_stage)
-
-
-def valorant_maps_message(removed_maps=None, selected_map=None):
- """
- Generates the VALORANT map list
- :param removed_maps:
- :param selected_map:
- :return: string embed value
- """
- return map_list_message(maps, removed_maps, selected_map)
-
-
-def valorant_sides_message(p1: discord.User, p2: discord.User,
- attack: bool):
- """
- Generates starting side message for VALORANT Veto
- :param p1 player 1 user
- :param p2 player 2 user
- :param attack boolean for if player1 chose attack
- :return: string embed value
- """
- if attack:
- return f"**Attack:** {p1.mention} \n**Defense:** {p2.mention}"
- else:
- return f"**Attack:** {p2.mention} \n**Defense:** {p1.mention}"
diff --git a/utils/player_utils.py b/utils/player_utils.py
index 00bbb1b..2a6a1ad 100644
--- a/utils/player_utils.py
+++ b/utils/player_utils.py
@@ -1,32 +1,12 @@
-import asyncio
import random
import discord
from discord.ext import commands
+from settings import newline
+from smash.player import Player
from utils import embeds
-
-
-async def swap_players(player1: discord.User, player2: discord.User,
- player1_dsr: list, player2_dsr: list):
- """
- Swap players for smash veto with tracking for DSR'd stages
- :param player1: player 1
- :param player2: player 2
- :param player1_dsr: player 1's DSR stage(s)
- :param player2_dsr: player 2's DSR stage(s)
- :return: new player1 and player2
- """
- # swap player objects
- p1 = player2
- p2 = player1
-
- # swap player DSR's
- p1_dsr = player2_dsr
- p2_dsr = player1_dsr
-
- # return new player 1 and player 2
- return p1, p2, p1_dsr, p2_dsr
+from utils.checks import playerCheck
async def get_players(ctx: discord.ext.commands.Context):
@@ -65,15 +45,6 @@ async def coinflip(ctx: discord.ext.commands.Context, p1: discord.User,
player1 = None
player2 = None
- flip = await embeds.flipping_coin(5)
- coin_msg = await ctx.send(embed=flip)
-
- # coinflip animation
- for n in range(1, 5):
- await asyncio.sleep(0.7)
- flip = await embeds.flipping_coin(n)
- await coin_msg.edit(embed=flip)
-
# pick random
num = random.randrange(0, 101)
if num % 2 == 0:
@@ -82,6 +53,29 @@ async def coinflip(ctx: discord.ext.commands.Context, p1: discord.User,
elif num % 2 == 1:
player1 = p2
player2 = p1
- await coin_msg.edit(embed=await embeds.coinflip_winner(player1))
+ await ctx.send(embed=await embeds.coinflip_winner(player1))
return player1, player2
+
+
+async def seed_selection(ctx: discord.ext.commands.Context,
+ bot: discord.ext.commands.Bot,
+ match):
+ # HIGHER SEED SELECTION
+ await ctx.send(f"{newline}Player 1 (the higher seed) say `me`")
+
+ msg = await bot.wait_for('message', check=playerCheck(ctx), timeout=300)
+
+ # placeholders
+ p1 = match.player1
+ p2 = match.player2
+
+ # changes player order if player 2 said they were first seed
+ if Player(msg.author) == match.player2:
+ match.player1 = p2
+ match.player2 = p1
+
+ # notifies of veto starts
+ await ctx.send(f"Starting veto with {match.player1.mention} as "
+ f"**Player 1** and {match.player2.mention} "
+ f"as **Player 2** in 5 seconds...")
diff --git a/valorant/__init__.py b/valorant/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/valorant/game.py b/valorant/game.py
new file mode 100644
index 0000000..e97664b
--- /dev/null
+++ b/valorant/game.py
@@ -0,0 +1,76 @@
+from valorant.map_pool import MapPool
+
+
+class Game:
+ """
+ Represents a VALORANT game
+ """
+
+ def __init__(self, game_num: int):
+ self.name = f"` " \
+ f"Game {game_num + 1}" \
+ f" `"
+
+ self.selected_stage = None
+ self.winner = None
+ self.map_pool = MapPool()
+
+ self.att_start = None
+ self.def_start = None
+
+ def map_embed(self) -> str:
+ """
+ Generate embed string for discord embeds
+ :return: string
+ """
+ message = ""
+ # loop through map names
+ for val_map in self.map_pool.maps:
+ # append each map name to a newline
+ message = f'{message}{val_map}\n'
+ # return message string
+ return message
+
+ def sides_embed(self) -> str:
+ """
+ Generate embed string for discord embeds
+ :return: string
+ """
+ if self.att_start is not None and self.def_start is not None:
+ return f"**Attack:** {self.att_start.mention} \n" \
+ f"**Defense:** {self.def_start.mention}"
+ else:
+ return f"**Attack:** TBD \n" \
+ f"**Defense:** TBD"
+
+ def veto_map(self, name: str):
+ """
+ Tries to veto a map
+ :param name:
+ :return: map that was veto'd
+ """
+ for x in range(len(self.map_pool.maps)):
+ if self.map_pool.maps[x] == name:
+ # if matching change th state of the map and return it
+ self.map_pool.maps[x].veto = True
+ return self.map_pool.maps[x]
+
+ def choose(self, name: str):
+ """
+ Tries to choose a map, veto's all maps that aren't chosen
+ :param name:
+ """
+ for x in range(len(self.map_pool.maps)):
+ if self.map_pool.maps[x] == name:
+ # if matching change th state of the map and return it
+ self.map_pool.maps[x].chosen = True
+ # veto map if not the chosen one
+ self.map_pool.maps[x].veto = True
+
+ def choose_last(self):
+ """
+ Chooses the last map that isn't veto'd
+ """
+ for val_map in self.map_pool.maps:
+ if not val_map.veto:
+ val_map.chosen = True
diff --git a/valorant/map.py b/valorant/map.py
new file mode 100644
index 0000000..1ac140a
--- /dev/null
+++ b/valorant/map.py
@@ -0,0 +1,46 @@
+from string import capwords
+
+from settings import hide_map_on_veto, cross_map_on_veto
+
+
+class Map:
+ """
+ Represents a VALORANT Map
+ """
+
+ def __init__(self, name: str):
+ """
+ Constructor method
+ :param name: name of the map
+ """
+ self.name = name
+ self.veto = False
+ self.chosen = False
+
+ def __str__(self) -> str:
+ """
+ String representation of the map
+ :return: string of map
+ """
+ if self.chosen:
+ return f'⮕**{self.name}**'
+ elif self.veto:
+ name = self.name
+ # surround in cross out if crossing
+ if cross_map_on_veto:
+ name = f'~~{name}~~'
+ # surround in spoiler tags if hiding
+ if hide_map_on_veto:
+ name = f'||{name}||'
+ # return the name of the stage
+ return name
+ else:
+ return self.name
+
+ def __eq__(self, other):
+ """
+ Compares a string of the map name to its name
+ :param other: string name of other map
+ :return: boolean if matching
+ """
+ return capwords(other) == self.name
diff --git a/valorant/map_pool.py b/valorant/map_pool.py
new file mode 100644
index 0000000..45deca3
--- /dev/null
+++ b/valorant/map_pool.py
@@ -0,0 +1,34 @@
+from typing import List
+
+from settings import val_maps
+from valorant.map import Map
+
+
+class MapPool:
+ """
+ Represents a VALORANT Map Pool
+ """
+
+ def __init__(self):
+ # map list
+ self.maps: List[Map] = []
+
+ # loops through the map from settings
+ for val_map in val_maps:
+ self.maps.append(Map(val_map))
+
+ def __contains__(self, item):
+ """
+ Check if a string is the name of a stage
+ :param item: string of map name
+ :return:
+ """
+ # search through map for match
+ for val_map in self.maps:
+ if val_map == item and \
+ not val_map.veto and \
+ not val_map.chosen:
+ return True
+
+ # else return false if it wasn't found
+ return False
diff --git a/valorant/match.py b/valorant/match.py
new file mode 100644
index 0000000..1ffcfa3
--- /dev/null
+++ b/valorant/match.py
@@ -0,0 +1,302 @@
+from typing import List
+
+import discord
+from discord.ext import commands
+
+from settings import tourney_name, rulebook_url, footer_icon, footer_note, \
+ veto_timeout
+from utils.checks import mapCheck, sideCheck
+from valorant.game import Game
+
+
+class Match:
+ """
+ Represents a VALORANT Match
+ """
+
+ def __init__(self,
+ captain1: discord.Member,
+ captain2: discord.Member,
+ num_of_games: int):
+ # captains
+ self.captain1 = captain1
+ self.captain2 = captain2
+ self.winner = None
+ # match data
+ self.games: List[Game] = []
+ self.name: str = f"{tourney_name}: {captain1.name} vs {captain2.name}"
+ self.description: str = f"{self.captain1.mention} vs " \
+ f"{self.captain2.mention} " \
+ f"\nThe rulebook can be found " \
+ f"[here]({rulebook_url})"
+
+ # match state
+ self.num_of_games: int = num_of_games
+ self.current_game: int = 0
+
+ # generate games
+ for x in range(num_of_games):
+ self.games.append(Game(x))
+
+ @property
+ def embed(self):
+ title = f"VALORANT Best-of-{self.num_of_games} Veto"
+ embed = discord.Embed(title=title,
+ description=self.description,
+ color=discord.Colour.gold())
+ # loop through max games times and generate embed fields
+ for x in range(len(self.games)):
+ embed.add_field(name=self.games[x].name,
+ value='_ _',
+ inline=False)
+ # x - 1 because its using index num
+ embed.add_field(name="__Maps__",
+ value=self.games[x].map_embed(),
+ inline=True)
+ embed.add_field(name="__Sides__",
+ value=self.games[x].sides_embed(),
+ inline=True)
+ # set footer
+ embed.set_footer(icon_url=footer_icon,
+ text=f"{tourney_name} | {footer_note}")
+
+ return embed
+
+ async def veto(self, ctx: discord.ext.commands.Context,
+ bot: discord.ext.commands.Bot):
+ # send first embed
+ await ctx.send(embed=self.embed)
+
+ # Bo1 VETO
+ if self.num_of_games == 1:
+ for x in range(5):
+ if x == 0:
+ await ctx.send(
+ f"{self.captain1.mention} veto a map.")
+ elif x == 1:
+ await ctx.send(
+ f"{self.captain2.mention} veto a map.")
+ elif x == 2:
+ await ctx.send(
+ f"{self.captain1.mention} veto another map.")
+ elif x == 3:
+ await ctx.send(
+ f"{self.captain2.mention} veto another map.")
+ elif x == 4:
+ # select last map
+ self.games[self.current_game].choose_last()
+
+ # resend embed
+ await ctx.send(embed=self.embed)
+
+ await ctx.send(
+ f"{self.captain1.mention} what is your "
+ f"preferred starting side?")
+
+ # if veto'ing
+ if x <= 3:
+ msg = await bot.wait_for('message',
+ check=mapCheck(ctx, self),
+ timeout=veto_timeout)
+ # veto the map
+ self.games[self.current_game].veto_map(msg.content)
+
+ # otherwise select side
+ else:
+ # run side selection
+ await self.sideSelection(ctx, bot, 1)
+
+ # resend embed
+ await ctx.send(embed=self.embed)
+
+ # Bo3 VETO
+ elif self.num_of_games == 3:
+ for x in range(6):
+ if x == 0:
+ await ctx.send(
+ f"{self.captain1.mention} select a map for game 1.")
+ elif x == 1:
+ await ctx.send(
+ f"{self.captain2.mention} what is your "
+ f"preferred starting side for game 1?")
+ await self.sideSelection(ctx, bot, 2)
+ # update current game
+ self.current_game += 1
+ # refresh embed
+ await ctx.send(embed=self.embed)
+ # skip to next step
+ continue
+
+ elif x == 2:
+ await ctx.send(
+ f"{self.captain2.mention} select a map for game 2.")
+ elif x == 3:
+ await ctx.send(
+ f"{self.captain1.mention} what is your "
+ f"preferred starting side for game 2?")
+ await self.sideSelection(ctx, bot, 1)
+ # update current game
+ self.current_game += 1
+ # refresh embed
+ await ctx.send(embed=self.embed)
+ # skip to next step
+ continue
+
+ elif x == 4:
+ await ctx.send(
+ f"{self.captain1.mention} please veto a map.")
+ elif x == 5:
+ await ctx.send(
+ f"{self.captain2.mention} please veto a map.")
+
+ msg = await bot.wait_for('message',
+ check=mapCheck(ctx, self),
+ timeout=veto_timeout)
+
+ if x == 0 or x == 2:
+ # choose map for these games
+ self.games[self.current_game].choose(msg.content)
+ # loop through and veto from rest (chosen takes over veto)
+ for game in self.games:
+ game.veto_map(msg.content)
+
+ # FIXME: idk some way to do this better but it s 4am now xd
+ elif x == 4 or x == 5:
+ # veto the map from all games
+ for game in self.games:
+ game.veto_map(msg.content)
+
+ if x == 5:
+ # choose last map
+ self.games[self.current_game].choose_last()
+
+ # refresh embed
+ await ctx.send(embed=self.embed)
+
+ # side selection for last game
+ await ctx.send(
+ f"{self.captain1.mention} what is your "
+ f"preferred starting side for game 3?")
+ await self.sideSelection(ctx, bot, 1)
+
+ # refresh embed
+ await ctx.send(embed=self.embed)
+
+ # BO5 VETO
+ # FIXME: most definitely can be better, but its 4am xd
+ elif self.num_of_games == 5:
+ for x in range(9):
+ # GAME 1
+ if x == 0:
+ await ctx.send(
+ f"{self.captain1.mention} select a map for game 1.")
+ elif x == 1:
+ await ctx.send(
+ f"{self.captain2.mention} what is your "
+ f"preferred starting side for game 1?")
+ await self.sideSelection(ctx, bot, 2)
+ # update current game
+ self.current_game += 1
+ # refresh embed
+ await ctx.send(embed=self.embed)
+ # skip to next step
+ continue
+
+ # GAME 2
+ elif x == 2:
+ await ctx.send(
+ f"{self.captain2.mention} select a map for game 2.")
+ elif x == 3:
+ await ctx.send(
+ f"{self.captain1.mention} what is your "
+ f"preferred starting side for game 2?")
+ await self.sideSelection(ctx, bot, 1)
+ # update current game
+ self.current_game += 1
+ # refresh embed
+ await ctx.send(embed=self.embed)
+ # skip to next step
+ continue
+
+ # GAME 3
+ elif x == 4:
+ await ctx.send(
+ f"{self.captain1.mention} select a map for game 3.")
+ elif x == 5:
+ await ctx.send(
+ f"{self.captain2.mention} what is your "
+ f"preferred starting side for game 3?")
+ await self.sideSelection(ctx, bot, 2)
+ # update current game
+ self.current_game += 1
+ # refresh embed
+ await ctx.send(embed=self.embed)
+ # skip to next step
+ continue
+
+ # GAME 4
+ elif x == 6:
+ await ctx.send(
+ f"{self.captain2.mention} select a map for game 4.")
+ elif x == 7:
+ await ctx.send(
+ f"{self.captain1.mention} what is your "
+ f"preferred starting side for game 4?")
+ await self.sideSelection(ctx, bot, 1)
+ # update current game
+ self.current_game += 1
+ # refresh embed
+ await ctx.send(embed=self.embed)
+ # skip to next step
+ continue
+
+ if x % 2 == 0 and x != 8:
+ msg = await bot.wait_for('message',
+ check=mapCheck(ctx, self),
+ timeout=veto_timeout)
+ # choose map for these games
+ self.games[self.current_game].choose(msg.content)
+ # loop through and veto from rest (chosen takes over veto)
+ for game in self.games:
+ game.veto_map(msg.content)
+
+ # FIXME: idk some way to do this better but it s 4am now xd
+ elif x == 8:
+ # choose last map
+ self.games[self.current_game].choose_last()
+
+ # refresh embed
+ await ctx.send(embed=self.embed)
+
+ # side selection for last game
+ await ctx.send(
+ f"{self.captain1.mention} what is your "
+ f"preferred starting side for game 5?")
+ await self.sideSelection(ctx, bot, 1)
+
+ # refresh embed
+ await ctx.send(embed=self.embed)
+
+ await ctx.send("GLHF! Don't forget to report your scores afterwards.")
+
+ async def sideSelection(self, ctx, bot, chooser):
+ # get side message
+ msg = await bot.wait_for('message',
+ check=sideCheck(ctx),
+ timeout=veto_timeout)
+
+ # set proper captain pings
+ if 'att' in msg.content:
+ if chooser == 1:
+ self.games[self.current_game].att_start = self.captain1
+ self.games[self.current_game].def_start = self.captain2
+ else:
+ self.games[self.current_game].att_start = self.captain2
+ self.games[self.current_game].def_start = self.captain1
+ elif 'def' in msg.content:
+ if chooser == 2:
+ self.games[self.current_game].att_start = self.captain1
+ self.games[self.current_game].def_start = self.captain2
+ else:
+ self.games[self.current_game].att_start = self.captain2
+ self.games[self.current_game].def_start = self.captain1
diff --git a/veto/smash.py b/veto/smash.py
deleted file mode 100644
index f92cf80..0000000
--- a/veto/smash.py
+++ /dev/null
@@ -1,237 +0,0 @@
-import asyncio
-from string import capwords
-
-import discord
-from discord.ext import commands
-
-from utils import player_utils
-from utils.message_generators import *
-
-# combine lists for all stages list
-all_stages = starters + counters
-
-
-async def initial(ctx: discord.ext.commands.Context,
- bot: discord.ext.commands.Bot, main_message: discord.Message,
- p1: discord.User, p2: discord.User, emb: discord.Embed):
- """
- Runs an initial smash veto
- :param ctx: context for the message
- :param bot: bot user to use
- :param main_message: original message to edit
- :param p1: player1 user
- :param p2: player2 user
- :param emb: embed to edit
- :return:
- """
-
- # player check function
- def playerCheck(message):
- return message.content.lower() == 'me' and message.channel == ctx.channel
-
- # stage check function
- def stageCheck(message):
- return capwords(message.content) in all_stages \
- and capwords(message.content) not in removed_stages \
- and message.channel == ctx.channel
-
- # setup stages
- removed_stages = []
- removed_stages = removed_stages + counters
- p1_dsr_stage = []
- p2_dsr_stage = []
-
- # setup players
- player1 = p1
- player2 = p2
-
- # more setup
- main_msg = main_message
- embed = emb
-
- # loop through 4 stage veto's/selection
- for x in range(4):
- # check which message to send
- if x == 0:
- await ctx.send(f"{player1.mention} please veto a starter.")
- if x == 1:
- await ctx.send(f"{player2.mention} please veto a starter.")
- if x == 2:
- await ctx.send(f"{player2.mention} please veto another starter.")
- if x == 3:
- await ctx.send(f"{player1.mention} please select the stage from "
- f"the remaining starters.")
-
- # wait for players stage choice
- msg = await bot.wait_for('message', check=stageCheck, timeout=300)
-
- # remove messages sent after the embed
- await ctx.channel.purge(after=main_msg)
-
- # if stage selection
- if x == 3:
- # highlight selected stage
- value = starters_message(removed_stages, capwords(msg.content))
- embed.set_field_at(1, name="Starter Stages",
- value=value)
-
- # sets DSR stage to selected stage (will wipe losers DSR later)
- p1_dsr_stage.append(capwords(msg.content))
- p2_dsr_stage.append(capwords(msg.content))
- # otherwise edit game 1 embed to remove the stage
- else:
- # add stage to removed list
- removed_stages.append(capwords(msg.content))
- # wipe out stage from embed
- value = starters_message(removed_stages)
- embed.set_field_at(1, name="Starter Stages", value=value)
-
- # edit original message with new embed
- await main_msg.edit(embed=embed)
-
- # get winner of the match
- await ctx.send(f"{newline}GLHF! Once finished, "
- f"the winner should say `me`.")
- msg = await bot.wait_for('message', check=playerCheck, timeout=1800)
-
- # switch player 1 to winner and player 2 to loser
- if msg.author == player2:
- player1, player2, p1_dsr_stage, p2_dsr_stage = \
- await player_utils.swap_players(player1, player2,
- p1_dsr_stage, p2_dsr_stage)
-
- # remove stage from losers DSR list
- p2_dsr_stage.pop()
-
- # edit game 3 embed message
- embed.set_field_at(0, name="` Game 1 "
- " `",
- value=f"**Winner:** {player1.mention}", inline=False)
- await main_msg.edit(embed=embed)
-
- return main_msg, embed, player1, player2, p1_dsr_stage, p2_dsr_stage
-
-
-async def nonInitial(ctx: discord.ext.commands.Context,
- bot: discord.ext.commands.Bot,
- main_message: discord.Message,
- p1: discord.User, p2: discord.User,
- p1_dsr: list, p2_dsr: list,
- emb: discord.Embed,
- game_num: int, starter_index: int, counter_index: int):
- """
- Runs a nonInitial smash veto process
- :param ctx: context for the message
- :param bot: bot user to use
- :param main_message: original message to edit
- :param p1: player1 user
- :param p2: player2 user
- :param p1_dsr: player1's DSR'd stages
- :param p2_dsr: player2's DSR'd stages
- :param emb: embed to edit
- :param game_num: game number in the set
- :param starter_index: index of start stages field in the embed
- :param counter_index: index of counterpick stages field in the embed
- """
-
- # setup stuff
- main_msg = main_message
- embed = emb
-
- # player check function
- def playerCheck(message):
- return message.content.lower() == 'me' \
- and message.channel == ctx.channel
-
- # stage check function
- def stageCheck(message):
- return capwords(message.content) in all_stages \
- and capwords(message.content) not in removed_stages \
- and message.channel == ctx.channel
-
- # notify of veto and reset channel after 3 seconds
- await ctx.send(f"Starting Game {game_num} veto...")
- await asyncio.sleep(3)
- await ctx.channel.purge(after=main_msg)
-
- # setup stages
- removed_stages = []
- p1_dsr_stage = p1_dsr
- p2_dsr_stage = p2_dsr
- removed_stages = removed_stages + p2_dsr_stage
-
- # setup players
- player1 = p1
- player2 = p2
-
- # update embed for DSR'd stage
- value1 = starters_message(removed_stages)
- value2 = counters_message(removed_stages)
- embed.set_field_at(starter_index, name="Starter Stages", value=value1)
- embed.set_field_at(counter_index, name="Counterpick Stages", value=value2)
- await main_msg.edit(embed=embed)
-
- # loop through 3 stage veto's/selection
- for x in range(3):
- if x == 0:
- await ctx.send(f"{player1.mention} please veto a stage.")
- elif x == 1:
- await ctx.send(f"{player1.mention} please veto another stage.")
- elif x == 2:
- await ctx.send(f"{player2.mention} please select the stage.")
-
- # wait for players stage choice
- msg = await bot.wait_for('message', check=stageCheck, timeout=300)
-
- # remove messages sent after the embed
- await ctx.channel.purge(after=main_msg)
-
- # if on stage selection
- if x == 2:
- # highlight selected stage
- value = starters_message(removed_stages, capwords(msg.content))
- embed.set_field_at(starter_index, name="Starter Stages",
- value=value)
- # highlight selected stage
- value = counters_message(removed_stages, capwords(msg.content))
- embed.set_field_at(counter_index, name="Counterpick Stages",
- value=value)
-
- # append to both DSR lists (to be removed when winner is decided
- p1_dsr_stage.append(capwords(msg.content))
- p2_dsr_stage.append(capwords(msg.content))
- else:
- # add stage to removed list
- removed_stages.append(capwords(msg.content))
- # redraw starter stages list
- value = starters_message(removed_stages)
- embed.set_field_at(starter_index, name="Starter Stages",
- value=value)
- # redraw counterpick stages list
- embed.set_field_at(counter_index, name="Counterpick Stages",
- value=counters_message(removed_stages))
-
- # edit original message with new embed
- await main_msg.edit(embed=embed)
-
- # get winner of the match
- await ctx.send(f"{newline}GLHF! Once finished, the winner should say `me`.")
- msg = await bot.wait_for('message', check=playerCheck, timeout=1800)
-
- # switch player 1 to winner and player 2 to loser
- if msg.author == player2:
- player1, player2, p1_dsr_stage, p2_dsr_stage = \
- await player_utils.swap_players(player1, player2,
- p1_dsr_stage, p2_dsr_stage)
-
- # remove stage from losers DSR list
- p2_dsr_stage.pop()
-
- # edit game embed message
- embed.set_field_at(starter_index - 1,
- name=f"` Game {game_num} "
- f" `",
- value=f"**Winner:** {player1.mention}", inline=False)
- await main_msg.edit(embed=embed)
-
- return main_msg, embed, player1, player2, p1_dsr_stage, p2_dsr_stage
diff --git a/veto/valorant.py b/veto/valorant.py
deleted file mode 100644
index 1d6fa45..0000000
--- a/veto/valorant.py
+++ /dev/null
@@ -1,320 +0,0 @@
-import asyncio
-from string import capwords
-
-from discord.ext import commands
-
-from utils import player_utils
-from utils.message_generators import *
-
-
-async def bo1(ctx: discord.ext.commands.Context, bot: discord.ext.commands.Bot,
- main_message: discord.Message,
- p1: discord.User, p2: discord.User, emb: discord.Embed):
- # setup stuff
- main_msg = main_message
- embed = emb
-
- # player check function
- def playerCheck(message):
- return message.content.lower() == 'me' \
- and message.channel == ctx.channel
-
- # stage check function
- def mapCheck(message):
- return capwords(message.content) in maps \
- and capwords(message.content) not in removed_maps \
- and message.channel == ctx.channel
-
- # setup players
- player1, player2 = await player_utils.coinflip(ctx, p1, p2)
- await asyncio.sleep(2)
-
- # maps
- removed_maps = []
-
- # notifies of veto starts
- await ctx.send(f"Starting veto with {player1.mention} as **Team A** and "
- f"{player2.mention} as **Team B** in 5 seconds...")
-
- # delete all messages and begin veto after 5 seconds
- await asyncio.sleep(5)
- await ctx.channel.purge(after=main_msg)
-
- for x in range(4):
- if x == 0:
- await ctx.send(f"{player1.mention} please veto a map.")
- elif x == 1:
- await ctx.send(f"{player2.mention} please veto a map.")
- elif x == 2:
- await ctx.send(f"{player1.mention} please veto another map.")
- elif x == 3:
- await ctx.send(f"{player2.mention} please veto another map.")
- elif x == 4:
- await ctx.send(f"{player1.mention} what is your "
- f"preferred starting side?")
-
- # wait for players stage choice
- msg = await bot.wait_for('message', check=mapCheck, timeout=300)
-
- # remove messages sent after the embed
- await ctx.channel.purge(after=main_msg)
-
- # if not last map then strike out
- if x != 3:
- # strike out map
- removed_maps.append(capwords(msg.content))
- embed.set_field_at(1, name="Maps",
- value=valorant_maps_message(removed_maps))
- else:
- removed_maps.append(capwords(msg.content))
- # highlight last map
- selected_map = ""
- for val_map in maps:
- if val_map not in removed_maps:
- selected_map = val_map
- embed.set_field_at(1, name="Maps",
- value=valorant_maps_message(removed_maps,
- selected_map))
-
- # edit original message with new embed
- await main_msg.edit(embed=embed)
-
- # side selection
- await sideSelection(ctx, bot, player1, player2, embed, 2, True)
-
- # edit original message with new embed
- await main_msg.edit(embed=embed)
-
- # get winner of the match
- await ctx.send(
- f"{newline}GLHF! Once finished, the winner should say `me`.")
- msg = await bot.wait_for('message', check=playerCheck, timeout=1800)
-
- # update winner embed
- embed.set_field_at(0,
- name=f"` Game 1 "
- f" `",
- value=f"**Winner:** {msg.author.mention}", inline=False)
- await main_msg.edit(embed=embed)
-
- return main_msg, embed
-
-
-async def bo3(ctx: discord.ext.commands.Context, bot: discord.ext.commands.Bot,
- main_message: discord.Message,
- p1: discord.User, p2: discord.User, emb: discord.Embed):
- # setup stuff
- main_msg = main_message
- embed = emb
-
- # player check function
- def playerCheck(message):
- return message.content.lower() == 'me' \
- and message.channel == ctx.channel
-
- # stage check function
- def mapCheck(message):
- return capwords(message.content) in maps \
- and capwords(message.content) not in removed_maps \
- and message.channel == ctx.channel
-
- # setup players
- player1, player2 = await player_utils.coinflip(ctx, p1, p2)
- await asyncio.sleep(1)
-
- # maps
- removed_maps = []
-
- # notifies of veto starts
- await ctx.send(
- f"Starting veto with {player1.mention} as **Team A** "
- f"and {player2.mention} as **Team B** in 5 seconds...")
-
- # delete all messages and begin veto after 5 seconds
- await asyncio.sleep(5)
- await ctx.channel.purge(after=main_msg)
-
- for x in range(6):
- if x == 0:
- await ctx.send(
- f"{player1.mention} please select a map for game 1.")
- elif x == 1:
- await sideSelection(ctx, bot, player1, player2, embed, 2, False)
-
- # remove messages sent after the embed
- await ctx.channel.purge(after=main_msg)
-
- # edit original message with new embed
- await main_msg.edit(embed=embed)
-
- continue
- elif x == 2:
- await ctx.send(
- f"{player2.mention} please select a map for game 2.")
- elif x == 3:
- await sideSelection(ctx, bot, player1, player2, embed, 5, True)
-
- # remove messages sent after the embed
- await ctx.channel.purge(after=main_msg)
-
- # edit original message with new embed
- await main_msg.edit(embed=embed)
-
- continue
- elif x == 4:
- await ctx.send(f"{player1.mention} please veto a map.")
- elif x == 5:
- await ctx.send(f"{player2.mention} please veto a map.")
-
- # wait for players stage choice
- msg = await bot.wait_for('message', check=mapCheck, timeout=300)
-
- # remove messages sent after the embed
- await ctx.channel.purge(after=main_msg)
-
- # if not last map then strike out
- # FIXME LATER - definitely could do this more efficiently but its
- # 3 am and im tired bruh
- if x == 0:
- selected_map = capwords(msg.content)
- removed_maps.append(selected_map)
- # select map and strike out rest
- embed.set_field_at(1, name="Maps",
- value=valorant_maps_message(maps,
- selected_map))
-
- # strike out for next two games as well
- embed.set_field_at(4, name="Maps",
- value=valorant_maps_message(removed_maps))
- embed.set_field_at(7, name="Maps",
- value=valorant_maps_message(removed_maps))
-
- elif x == 2:
- selected_map = capwords(msg.content)
- removed_maps.append(selected_map)
- # select map and strike out rest
- embed.set_field_at(4, name="Maps",
- value=valorant_maps_message(maps,
- selected_map))
-
- # strike out for final game as well
- embed.set_field_at(7, name="Maps",
- value=valorant_maps_message(removed_maps))
-
- elif x == 4:
- # strike out map
- removed_maps.append(capwords(msg.content))
- embed.set_field_at(7, name="Maps",
- value=valorant_maps_message(removed_maps))
- elif x == 5:
- removed_maps.append(capwords(msg.content))
- # highlight last map
- selected_map = ""
- for val_map in maps:
- if val_map not in removed_maps:
- selected_map = val_map
- embed.set_field_at(7, name="Maps",
- value=valorant_maps_message(removed_maps,
- selected_map))
-
- # edit original message with new embed
- await main_msg.edit(embed=embed)
-
- # side selection
- await sideSelection(ctx, bot, player1, player2, embed, 8, True)
-
- # edit original message with new embed
- await main_msg.edit(embed=embed)
-
- # winner tracker
- p1_wins = 0
- p2_wins = 0
- # for loop for each games winners
- for x in range(3):
-
- # get winner of the match
- await ctx.send(
- f"{newline}You may play now play game {x + 1}. GLHF! Once finished, "
- f"the winner should say `me`.")
- msg = await bot.wait_for('message', check=playerCheck, timeout=1800)
-
- if msg.author == player1:
- p1_wins += 1
- elif msg.author == player2:
- p2_wins += 1
-
- # update winner embed
- # if winner
- if p1_wins == 2 or p2_wins == 2:
- embed.set_field_at(x * 3,
- name=f"~~` Game {x + 1}"
- f" `~~",
- value=f"~~**Winner:** TBD~~",
- inline=False)
- embed.set_field_at(x * 3 + 1, name="Maps",
- value=valorant_maps_message(maps))
- embed.set_field_at(x * 3 + 2, name="Starting Sides",
- value=f"~~**Attack:** TBD "
- f"\n**Defense:** TBD,~~")
- await main_msg.edit(embed=embed)
-
- continue
-
- # otherwise update normally
- embed.set_field_at(x * 3,
- name=f"` Game {x + 1} "
- f" `",
- value=f"**Winner:** {msg.author.mention}",
- inline=False)
- await main_msg.edit(embed=embed)
-
- # remove messages sent after the embed
- await ctx.channel.purge(after=main_msg)
-
- await ctx.send('GG!')
-
- return main_msg, embed
-
-
-async def sideSelection(ctx: discord.ext.commands.Context,
- bot: discord.ext.commands.Bot,
- player1: discord.User, player2: discord.User,
- embed: discord.Embed, embed_index: int, p1_pick: bool):
- # side check function
- def sideCheck(message):
- return 'att' in message.content or 'def' in message.content \
- and message.channel == ctx.channel
-
- # side selection
- if p1_pick:
- await ctx.send(f"{player1.mention} what is your "
- f"preferred starting side?")
- msg = await bot.wait_for('message', check=sideCheck, timeout=300)
- if 'att' in msg.content:
- embed.set_field_at(embed_index, name="Starting Sides",
- value=valorant_sides_message(player1,
- player2,
- True),
- inline=True)
- elif 'def' in msg.content:
- embed.set_field_at(embed_index, name="Starting Sides",
- value=valorant_sides_message(player1,
- player2,
- False),
- inline=True)
- else:
- await ctx.send(f"{player2.mention} what is your "
- f"preferred starting side?")
- msg = await bot.wait_for('message', check=sideCheck, timeout=300)
- if 'att' in msg.content:
- embed.set_field_at(embed_index, name="Starting Sides",
- value=valorant_sides_message(player1,
- player2,
- False),
- inline=True)
- elif 'def' in msg.content:
- embed.set_field_at(embed_index, name="Starting Sides",
- value=valorant_sides_message(player1,
- player2,
- True),
- inline=True)