2020-12-03 18:54:11 +13:00
|
|
|
#
|
|
|
|
# Cog based off of Redjumpman's Russian Roulette
|
|
|
|
# https://github.com/Redjumpman/Jumper-Plugins/blob/a5a55e3968cb366bf257cb0e886a1c30588e85ef/russianroulette/russianroulette.py
|
|
|
|
#
|
|
|
|
from redbot.core import bank, commands, checks, Config
|
2020-12-04 14:21:16 +13:00
|
|
|
import asyncio, contextlib, discord, random, shlex
|
2020-12-03 18:54:11 +13:00
|
|
|
|
|
|
|
|
|
|
|
class Shootout(commands.Cog):
|
|
|
|
default_config = {
|
|
|
|
"cost": 50,
|
2020-12-04 14:21:16 +13:00
|
|
|
"Messages": ["Shoot!", "Draw!", "Fire!", "Kill!"],
|
2020-12-03 18:54:11 +13:00
|
|
|
"Session": {"Pot": 0, "Players": [], "Active": False},
|
2020-12-04 14:21:16 +13:00
|
|
|
"Times": {"lobby": 60, "delay": 10, "fuzzy": 3},
|
|
|
|
"Victory": [
|
|
|
|
"{} has won the shootout! {} {} has been deposited to their account!",
|
|
|
|
"Looks like {} is the fastest shot in the server! They were given the pot of {} {}",
|
|
|
|
"{} must practice. That draw time was insane! The pot of {} {} belongs to them.",
|
|
|
|
"{} fires their gun, and all of their enemies fall to the ground! They claim the bounty of {} {}.",
|
|
|
|
],
|
2020-12-03 18:54:11 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, bot):
|
|
|
|
super().__init__()
|
|
|
|
self.bot = bot
|
|
|
|
self.config = Config.get_conf(self, identifier=163490961253466112, force_registration=True)
|
|
|
|
self.config.register_guild(**self.default_config)
|
|
|
|
|
|
|
|
async def red_delete_data_for_user(self, **kwargs):
|
|
|
|
"""Nothing to delete."""
|
|
|
|
return
|
|
|
|
|
|
|
|
async def game_checks(self, ctx, settings) -> bool:
|
|
|
|
if bool(settings["Session"]["Active"]):
|
|
|
|
await ctx.send("There's already a shootout! Wait for them to finish!")
|
|
|
|
return False
|
|
|
|
if ctx.author.id in settings["Session"]["Players"]:
|
|
|
|
await ctx.send("You're already waiting for the shootout!")
|
|
|
|
return False
|
|
|
|
try:
|
|
|
|
await bank.withdraw_credits(ctx.author, settings["cost"]) # <- Might raise a ValueError!
|
|
|
|
return True
|
|
|
|
except ValueError:
|
|
|
|
currency = await bank.get_currency_name(ctx.guild)
|
|
|
|
await ctx.send("Insufficient funds! This game requires {} {}".format(settings["cost"], currency))
|
|
|
|
return False
|
|
|
|
|
|
|
|
async def add_player(self, ctx, cost):
|
|
|
|
current_pot = await self.config.guild(ctx.guild).Session.Pot()
|
|
|
|
await self.config.guild(ctx.guild).Session.Pot.set(value=(current_pot + cost))
|
|
|
|
|
|
|
|
async with self.config.guild(ctx.guild).Session.Players() as players:
|
|
|
|
players.append(ctx.author.id)
|
|
|
|
num_players = len(players)
|
|
|
|
|
|
|
|
if num_players == 1:
|
2020-12-04 14:21:16 +13:00
|
|
|
wait = await self.config.guild(ctx.guild).Times.lobby()
|
2020-12-03 18:54:11 +13:00
|
|
|
await ctx.send(
|
|
|
|
"{0.author.mention} is gathering players for a shootout!\n"
|
|
|
|
"Type `{0.prefix}shootout` to join in!\n"
|
|
|
|
"The round will start in {1} seconds.".format(ctx, wait)
|
|
|
|
)
|
|
|
|
await asyncio.sleep(wait)
|
|
|
|
await self.start_game(ctx)
|
|
|
|
else:
|
|
|
|
await ctx.send("{} joined the shootout!".format(ctx.author.mention))
|
|
|
|
|
|
|
|
async def start_game(self, ctx):
|
2020-12-04 14:21:16 +13:00
|
|
|
session = await self.config.guild(ctx.guild).Session.all()
|
|
|
|
players = [ctx.guild.get_member(player) for player in session["Players"]]
|
2020-12-03 18:54:11 +13:00
|
|
|
filtered_players = [player for player in players if isinstance(player, discord.Member)]
|
|
|
|
if len(filtered_players) < 2:
|
|
|
|
try:
|
2020-12-04 14:21:16 +13:00
|
|
|
await bank.deposit_credits(ctx.author, session["Pot"])
|
2020-12-03 18:54:11 +13:00
|
|
|
except BalanceTooHigh as e:
|
|
|
|
await bank.set_balance(ctx.author, e.max_balance)
|
|
|
|
await ctx.send("A shootout with yourself means just shooting at some bottles. For that, no charge.")
|
|
|
|
await self.config.guild(ctx.guild).Session.clear()
|
|
|
|
return
|
2020-12-04 14:21:16 +13:00
|
|
|
listen_message = await self.get_random_draw_message(ctx)
|
2020-12-03 18:54:11 +13:00
|
|
|
await self.config.guild(ctx.guild).Session.Active.set(True)
|
|
|
|
await ctx.send(
|
2020-12-04 14:21:16 +13:00
|
|
|
'A shootout is about to start! When I say "Draw!", type `{}` to shoot your weapon! First person who shoots wins!'.format(
|
2020-12-03 18:54:11 +13:00
|
|
|
listen_message
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2020-12-04 14:21:16 +13:00
|
|
|
await asyncio.sleep((await self.get_random_wait_time(ctx)))
|
|
|
|
|
|
|
|
await ctx.send("Draw!")
|
|
|
|
|
2020-12-03 18:54:11 +13:00
|
|
|
def check(m):
|
|
|
|
return m.content.lower() == listen_message.lower() and m.channel == ctx.channel and m.author in players
|
|
|
|
|
|
|
|
message = await self.bot.wait_for("message", check=check)
|
|
|
|
winner = message.author
|
|
|
|
await self.end_game(ctx, winner)
|
|
|
|
|
|
|
|
async def end_game(self, ctx, winner: discord.Member):
|
|
|
|
currency = await bank.get_currency_name(ctx.guild)
|
|
|
|
total = await self.config.guild(ctx.guild).Session.Pot()
|
|
|
|
try:
|
|
|
|
await bank.deposit_credits(winner, total)
|
|
|
|
except BalanceTooHigh as e:
|
|
|
|
await bank.set_balance(winner, e.max_balance)
|
2020-12-04 14:21:16 +13:00
|
|
|
await ctx.send((await self.get_random_win_message(ctx)).format(winner.mention, total, currency))
|
2020-12-03 18:54:11 +13:00
|
|
|
await self.config.guild(ctx.guild).Session.clear()
|
|
|
|
|
2020-12-04 14:21:16 +13:00
|
|
|
async def get_random_draw_message(self, ctx) -> str:
|
|
|
|
return random.choice((await self.config.guild(ctx.guild).Messages()))
|
|
|
|
|
|
|
|
async def get_random_win_message(self, ctx) -> str:
|
|
|
|
return random.choice((await self.config.guild(ctx.guild).Victory()))
|
|
|
|
|
|
|
|
async def get_random_wait_time(self, ctx) -> int:
|
|
|
|
delay = await self.config.guild(ctx.guild).Times.delay()
|
|
|
|
fuzzy = await self.config.guild(ctx.guild).Times.fuzzy()
|
|
|
|
return random.randint(delay - fuzzy, delay + fuzzy)
|
|
|
|
|
2020-12-03 18:54:11 +13:00
|
|
|
@commands.command()
|
|
|
|
@commands.guild_only()
|
|
|
|
async def shootout(self, ctx):
|
|
|
|
"""Start or join a shootout!
|
|
|
|
|
|
|
|
A shootout is a game where players can join by putting up money into the pot (if configured).
|
|
|
|
|
|
|
|
The bot will send a message to chat saying to begin and what message to type to win.
|
|
|
|
|
|
|
|
First person who types the message wins the entire pot! (Even if that pot is 0).
|
|
|
|
|
|
|
|
The game will not start if no other players have joined, and any cost will be refunded.
|
|
|
|
|
|
|
|
There is no maximum number of players.
|
|
|
|
"""
|
|
|
|
settings = await self.config.guild(ctx.guild).all()
|
|
|
|
if await self.game_checks(ctx, settings):
|
|
|
|
await self.add_player(ctx, settings["cost"])
|
|
|
|
|
|
|
|
@commands.group()
|
|
|
|
@commands.guild_only()
|
|
|
|
@checks.mod_or_permissions(manage_guild=True)
|
|
|
|
async def soset(self, ctx):
|
|
|
|
"""Change settings to Shootout"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
@soset.command(name="bet")
|
|
|
|
async def soset_bet(self, ctx, amount: int):
|
|
|
|
"""Sets the bet amount for a shootout"""
|
|
|
|
if amount < 0:
|
|
|
|
return await ctx.send("Get your negativity out of here!")
|
|
|
|
await self.config.guild(ctx.guild).cost.set(amount)
|
|
|
|
await ctx.tick()
|
|
|
|
return
|
|
|
|
|
2020-12-04 14:21:16 +13:00
|
|
|
@soset.command(name="draw")
|
|
|
|
async def soset_draw(self, ctx, *, draws: str):
|
|
|
|
"""Sets the message that the bot listens for during shootout games.
|
|
|
|
One will randomly be selected per game.
|
|
|
|
"""
|
|
|
|
|
|
|
|
await self.config.guild(ctx.guild).Messages.set(shlex.split(draws))
|
2020-12-03 18:54:11 +13:00
|
|
|
await ctx.tick()
|
|
|
|
return
|
|
|
|
|
2020-12-04 14:21:16 +13:00
|
|
|
@soset.command(name="victory")
|
|
|
|
async def soset_victory(self, ctx, *, victories: str):
|
|
|
|
"""Sets the random victory messages the bot displays when someone wins.
|
|
|
|
Use full string in quotation marks to break up the messages.
|
|
|
|
|
|
|
|
Failure to use quotation marks will set victory messages with a single word.
|
|
|
|
**This WILL cause errors during `[p]shootout` command execution!**
|
|
|
|
"""
|
|
|
|
|
|
|
|
await self.config.guild(ctx.guild).Victory.set(shlex.split(victories))
|
|
|
|
await ctx.tick()
|
|
|
|
return
|
|
|
|
|
|
|
|
@soset.command(name="lobby")
|
2020-12-03 18:54:11 +13:00
|
|
|
async def soset_wait(self, ctx, wait: int):
|
2020-12-04 14:21:16 +13:00
|
|
|
"""Sets the wait time until the shootout starts after player 1 runs the command.
|
|
|
|
Also known as the lobby time.
|
|
|
|
"""
|
|
|
|
if wait < 0:
|
|
|
|
return await ctx.send("Get your negativity out of here!")
|
|
|
|
if wait > 120:
|
|
|
|
if await self.confirm_long_wait_time(ctx):
|
|
|
|
await self.config.guild(ctx.guild).Times.lobby.set(wait)
|
|
|
|
await ctx.tick()
|
|
|
|
else:
|
|
|
|
return await ctx.send("Cancelling setting.")
|
|
|
|
else:
|
|
|
|
await self.config.guild(ctx.guild).Times.lobby.set(wait)
|
|
|
|
await ctx.tick()
|
|
|
|
|
|
|
|
@soset.command(name="delay")
|
|
|
|
async def soset_delay(self, ctx, wait: int):
|
|
|
|
"""Sets the delay time between when the bot displays the message to shoot, and when it accepts messages.
|
|
|
|
This is also known as the delay time."""
|
2020-12-03 18:54:11 +13:00
|
|
|
if wait < 0:
|
|
|
|
return await ctx.send("Get your negativity out of here!")
|
2020-12-04 14:21:16 +13:00
|
|
|
fuzzy = await self.config.guild(ctx.guild).Times.fuzzy()
|
|
|
|
if (wait - fuzzy) < 0:
|
|
|
|
return await ctx.send(
|
|
|
|
f"New delay value would set delay - fuzzy value less than 0. With currency fuzzy value, delay needs to be at least {fuzzy}"
|
|
|
|
)
|
|
|
|
if wait > 120:
|
|
|
|
if await self.confirm_long_wait_time(ctx):
|
|
|
|
await self.config.guild(ctx.guild).Times.delay.set(wait)
|
|
|
|
await ctx.tick()
|
|
|
|
else:
|
|
|
|
return await ctx.send("Cancelling setting.")
|
|
|
|
await self.config.guild(ctx.guild).Times.delay.set(wait)
|
2020-12-03 18:54:11 +13:00
|
|
|
await ctx.tick()
|
|
|
|
|
2020-12-04 14:21:16 +13:00
|
|
|
@soset.command(name="fuzzy")
|
|
|
|
async def soset_fuzzy(self, ctx, wait: int):
|
|
|
|
"""Sets the fuzzy time to set the randomness between when the bot sends the shoot message and when it accepts messages.
|
|
|
|
This is also known as the fuzzy time.
|
|
|
|
|
|
|
|
Specifically, the fuzzy time is used to determine the min and max value of the wait time. The actual wait time is a random number between `delay - fuzzy` and `delay + fuzzy`.
|
|
|
|
|
|
|
|
```python
|
|
|
|
return random.randomint(delay - fuzzy, delay + fuzzy)
|
|
|
|
```
|
|
|
|
"""
|
|
|
|
if wait < 0:
|
|
|
|
return await ctx.send("Get your negativity out of here!")
|
|
|
|
delay = await self.config.guild(ctx.guild).Times.delay()
|
|
|
|
if (delay - wait) < 0:
|
|
|
|
return await ctx.send(
|
|
|
|
f"New fuzzy value would set delay - fuzzy value less than 0. With current delay value, fuzzy needs to be at least {delay}"
|
|
|
|
)
|
|
|
|
if (delay + wait) > 120:
|
|
|
|
if await self.confirm_long_wait_time(ctx):
|
|
|
|
await self.config.guild(ctx.guild).Times.fuzzy.set(wait)
|
|
|
|
else:
|
|
|
|
return await ctx.send("Cancelling setting.")
|
|
|
|
await self.config.guild(ctx.guild).Times.fuzzy.set(wait)
|
|
|
|
await ctx.tick()
|
|
|
|
|
|
|
|
async def confirm_long_wait_time(self, ctx) -> bool:
|
|
|
|
query = await ctx.send(
|
|
|
|
"Long wait times can cause users to leave the game and the bot to potentially experience issues. If you want to set this anyway, type `yes`."
|
|
|
|
)
|
|
|
|
try:
|
|
|
|
|
|
|
|
def check(m):
|
|
|
|
return m.channel == ctx.channel and m.author == ctx.author
|
|
|
|
|
|
|
|
message = await self.bot.wait_for("message", check=check, timeout=15)
|
|
|
|
if message.content.lower() == "yes":
|
|
|
|
with contextlib.suppress(discord.Forbidden):
|
|
|
|
await message.delete()
|
|
|
|
await query.delete()
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
await query.delete()
|
|
|
|
return False
|
|
|
|
except asyncio.TimeoutError:
|
|
|
|
await query.delete()
|
|
|
|
return False
|
|
|
|
|
|
|
|
@soset.group(name="admin")
|
2020-12-03 18:54:11 +13:00
|
|
|
@commands.guild_only()
|
|
|
|
@checks.admin_or_permissions(administrator=True)
|
2020-12-04 14:21:16 +13:00
|
|
|
async def soset_admin(self, ctx):
|
|
|
|
"""Perform administrator only commands. Useful debugging commands provided.
|
|
|
|
|
|
|
|
**These commands require the Administrator permission node.**"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
@soset_admin.command(name="reset")
|
|
|
|
async def soset_admin_reset(self, ctx):
|
|
|
|
"""Clears all session data. Useful for if you break something.
|
|
|
|
**THIS COMMAND CLEARS ALL SESSION DATA. DO NOT USE WHILE A GAME IS IN SESSION, OR IT WILL BE DESTROYED.**
|
|
|
|
"""
|
2020-12-03 18:54:11 +13:00
|
|
|
await self.config.guild(ctx.guild).Session.clear()
|
|
|
|
await ctx.tick()
|
|
|
|
|
2020-12-04 14:21:16 +13:00
|
|
|
@soset_admin.command(name="dump")
|
|
|
|
async def soset_admin_dump(self, ctx):
|
|
|
|
"""Dumps the entire config for this guild."""
|
2020-12-03 18:54:11 +13:00
|
|
|
await ctx.send("{}".format(await self.config.guild(ctx.guild).all()))
|