major-domo-legacy/cogs/gameplay.py
2023-02-25 18:39:17 -06:00

1257 lines
60 KiB
Python

import math
import discord
from discord import app_commands
from discord.ext import commands
from peewee import IntegrityError
from typing import Optional, Literal
import logging
from helpers import SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME, random_conf_gif, SBA_SEASON, PD_SEASON, LOGO, \
get_team_embed, Confirm, get_pos_abbrev, SBA_COLOR
from gameplay_helpers import *
from db_calls import get_one_team, get_one_player
from db_calls_gameplay import StratGame, StratPlay, StratLineup, get_one_game, pd_get_one_team, post_game, patch_game, \
get_game_team, post_lineups, pd_get_card_by_id, make_sub, get_player, player_link, get_team_lineups, \
get_current_play, post_play, get_one_lineup, advance_runners, patch_play, complete_play, get_batting_stats, \
get_pitching_stats, undo_play, get_latest_play, advance_one_runner, get_play_by_num
class Gameplay(commands.Cog):
def __init__(self, bot):
self.bot = bot
async def cog_command_error(self, ctx, error):
await ctx.send(f'{error}\n\nRun !help <command_name> to see the command requirements')
async def slash_error(self, ctx, error):
await ctx.send(f'{error}')
def get_scorebug(self, home_team, away_team, curr_play):
occupied = ''
unoccupied = ''
first_base = unoccupied if not curr_play.on_first else occupied
second_base = unoccupied if not curr_play.on_second else occupied
third_base = unoccupied if not curr_play.on_third else occupied
half = '' if curr_play.inning_half == 'Top' else ''
inning = f'{half} {curr_play.inning_num}'
outs = f'{curr_play.starting_outs} Out{"s" if curr_play.starting_outs != 1 else ""}'
game_string = f'```\n' \
f'{away_team["abbrev"]: ^4}{curr_play.away_score: ^3} {second_base}' \
f'{inning: >10}\n' \
f'{home_team["abbrev"]: ^4}{curr_play.home_score: ^3} {third_base} {first_base}' \
f'{outs: >8}\n```'
return game_string
async def get_game_state(self, game: StratGame) -> dict:
curr_play = get_current_play(game.id)
if curr_play is None:
latest_play = get_latest_play(game.id)
if not latest_play:
away_lineup = await get_team_lineups(game.id, game.away_team_id)
home_lineup = await get_team_lineups(game.id, game.home_team_id)
if len(away_lineup) == 0 or len(home_lineup) == 0:
game_state = {
'error': True,
'away_lineup': away_lineup,
'home_lineup': home_lineup
}
return game_state
else:
curr_play = post_play({
'game_id': game.id,
'play_num': 1,
'batter_id': get_one_lineup(game.id, game.away_team_id, batting_order=1).id,
'pitcher_id': get_one_lineup(game.id, game.home_team_id, position='P').id,
'on_base_code': 0,
'inning_half': 'Top',
'inning_num': 1,
'batting_order': 1,
'starting_outs': 0,
'away_score': 0,
'home_score': 0
})
else:
patch_play(latest_play.id, complete=False)
curr_play = latest_play
game_state = {'error': False, 'curr_play': curr_play}
away_team = await get_game_team(game, team_id=game.away_team_id)
home_team = await get_game_team(game, team_id=game.home_team_id)
game_state['away_team'] = away_team
game_state['home_team'] = home_team
scorebug = self.get_scorebug(home_team, away_team, game_state['curr_play'])
game_state['scorebug'] = scorebug
batter = await get_player(game, game_state['curr_play'].batter)
litmus = 0
try:
pitcher = await get_player(game, game_state['curr_play'].pitcher)
litmus = 1
catcher = await get_player(
game,
get_one_lineup(
game.id,
team_id=game.away_team_id if game_state['curr_play'].inning_half == 'Top' else game.home_team_id,
position='C'
)
)
except Exception as e:
logging.info(f'ERROR: {e} / TYPE: {type(e)}')
away_lineup = await get_team_lineups(game.id, game.away_team_id)
home_lineup = await get_team_lineups(game.id, game.home_team_id)
if litmus == 0:
error_message = f'Please sub in a pitcher to continue'
else:
error_message = f'Please sub in a catcher to continue'
game_state['error'] = True
game_state['error_message'] = error_message
game_state['away_lineup'] = away_lineup
game_state['home_lineup'] = home_lineup
return game_state
game_state['batter'] = batter
game_state['pitcher'] = pitcher
game_state['catcher'] = catcher
return game_state
async def get_game_state_embed(self, game: StratGame, full_length=True):
game_state = await self.get_game_state(game)
logging.info(f'game_state: {game_state}')
embed = get_team_embed(
f'{game_state["away_team"]["sname"]} @ {game_state["home_team"]["sname"]}',
team=game_state['home_team'],
thumbnail=False
)
if abs(game_state['curr_play'].home_score - game_state['curr_play'].away_score) >= 10:
embed.description = 'Mercy rule in effect'
if game.is_pd:
embed.set_footer(text=f'PD Season {PD_SEASON}', icon_url=LOGO)
embed.add_field(name='Game State', value=game_state['scorebug'], inline=False)
if game_state['error']:
# embed = discord.Embed(
# title='Current Lineups',
# color=int(SBA_COLOR, 16)
# )
embed.add_field(name='Away Team',
value=game_state['away_lineup'] if len(game_state['away_lineup']) else 'None, yet')
embed.add_field(name='Home Team',
value=game_state['home_lineup'] if len(game_state['home_lineup']) else 'None, yet')
if game_state['error_message']:
embed.set_footer(text=game_state['error_message'], icon_url=LOGO)
return embed
pitcher_string = f'{player_link(game, game_state["pitcher"])}'
batter_string = f'{player_link(game, game_state["batter"])}'
all_bat_stats = get_batting_stats(game.id, lineup_id=game_state['curr_play'].batter.id)
if len(all_bat_stats):
b_s = all_bat_stats[0]
batter_string += f'\n{b_s["pl_hit"]}-{b_s["pl_ab"]}'
for num, stat in [
(b_s['pl_double'], '2B'), (b_s['pl_triple'], '3B'), (b_s['pl_homerun'], 'HR'), (b_s['pl_rbi'], 'RBI'),
(b_s['pl_bb'], 'BB'), (b_s['pl_hbp'], 'HBP'), (b_s['pl_ibb'], 'IBB'), (b_s['pl_so'], 'K'),
(b_s['pl_gidp'], 'GIDP'), (b_s['pl_bpfo'], 'BPFO'), (b_s['pl_bplo'], 'BPLO')
]:
if num:
batter_string += f', {num if num > 1 else ""}{" " if num > 1 else ""}{stat}'
all_pit_stats = get_pitching_stats(game.id, lineup_id=game_state['curr_play'].pitcher.id)
if len(all_pit_stats):
p_s = all_pit_stats[0]
pitcher_string += f'\n{math.floor(p_s["pl_outs"]/3)}.{p_s["pl_outs"] % 3} IP'
for num, stat in [
(p_s['pl_runs'], 'R'), (p_s['pl_hit'], 'H'), (p_s['pl_homerun'], 'HR'), (p_s['pl_so'], 'K'),
(p_s['pl_bb'], 'BB'), (p_s['pl_hbp'], 'HBP'), (p_s['pl_wild_pitch'], 'WP'), (p_s['pl_balk'], 'BK'),
]:
if num:
pitcher_string += f', {num} {stat}'
if stat == 'R' and num != p_s['pl_eruns']:
pitcher_string += f', {p_s["pl_eruns"]} ER'
# embed.add_field(name='Matchup', value=f'Pitcher: \nvs\nBatter: ', inline=False)
embed.add_field(name='Pitcher', value=f'{pitcher_string}')
embed.add_field(name='Batter', value=f'{batter_string}')
embed.set_thumbnail(url=game_state["pitcher"]["image"])
embed.set_image(url=game_state["batter"]["image"])
baserunner_string = ''
if game_state['curr_play'].on_first:
runner = await get_player(game, game_state['curr_play'].on_first)
baserunner_string += f'On First: {player_link(game, runner)}\n'
if game_state['curr_play'].on_second:
runner = await get_player(game, game_state['curr_play'].on_second)
baserunner_string += f'On Second: {player_link(game, runner)}\n'
if game_state['curr_play'].on_third:
runner = await get_player(game, game_state['curr_play'].on_third)
baserunner_string += f'On Third: {player_link(game, runner)}\n'
embed.add_field(
name='Baserunners', value=baserunner_string if len(baserunner_string) > 0 else 'None', inline=False
)
if not full_length:
return embed
away_lineup = await get_team_lineups(game.id, game.away_team_id)
home_lineup = await get_team_lineups(game.id, game.home_team_id)
embed.add_field(name=f'{game_state["away_team"]["abbrev"]} Lineup', value=away_lineup)
embed.add_field(name=f'{game_state["home_team"]["abbrev"]} Lineup', value=home_lineup)
return embed
@commands.hybrid_command(name='newgame', help='Start a new baseball game')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def new_game_command(
self, ctx: commands.Context, away_team_abbrev: str, home_team_abbrev: str, week_num: int,
game_num: Optional[int] = None, is_pd: Optional[bool] = False):
conflict = get_one_game(channel_id=ctx.channel.id, active=True)
if conflict:
await ctx.send(f'Ope. There is already a game going on in this channel. Please wait for it to complete '
f'before starting a new one.')
return
if is_pd:
away_team = pd_get_one_team(away_team_abbrev)
home_team = pd_get_one_team(home_team_abbrev)
if ctx.author.id not in [away_team['gmid'], home_team['gmid']]:
await ctx.send('You can only start a new game if you GM one of the teams.')
return
else:
away_team = await get_one_team(away_team_abbrev)
home_team = await get_one_team(home_team_abbrev)
if ctx.author.id not in [away_team['gmid'], away_team['gmid2'], home_team['gmid'], home_team['gmid2']]:
await ctx.send('You can only start a new game if you GM one of the teams.')
return
post_game({
'away_team_id': away_team['id'],
'home_team_id': home_team['id'],
'week_num': week_num,
'game_num': game_num,
'channel_id': ctx.channel.id,
'active': True,
'is_pd': is_pd
})
await ctx.send(random_conf_gif())
@commands.hybrid_command(name='endgame', help='End game in this channel')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def end_game_command(self, ctx: commands.Context):
this_game = get_one_game(channel_id=ctx.channel.id, active=True)
if not this_game:
await ctx.send(f'Ope, I don\'t see a game in this channel.')
return
patch_game(this_game.id, active=False)
# TODO: create result sheet and submit stats
if this_game.is_pd:
# Build the statlines
# Submit the scorecard
# Post a notification to PD for rewards
pass
else:
# Send stats to sheets
# Post sheets link
pass
await ctx.send(random_conf_gif())
@app_commands.command(
name='setlineup',
description='Set starting lineup for game in this channel; Player Name for SBa games / Card ID for PD games',
)
@app_commands.describe(
order_1_player='SBa: Player Name; PD: Card #',
order_1_pos='C for Catcher, 1B for first base, P for pitcher, etc.',
order_2_player='SBa: Player Name; PD: Card #',
order_2_pos='C for Catcher, 1B for first base, P for pitcher, etc.',
order_3_player='SBa: Player Name; PD: Card #',
order_3_pos='C for Catcher, 1B for first base, P for pitcher, etc.',
order_4_player='SBa: Player Name; PD: Card #',
order_4_pos='C for Catcher, 1B for first base, P for pitcher, etc.',
order_5_player='SBa: Player Name; PD: Card #',
order_5_pos='C for Catcher, 1B for first base, P for pitcher, etc.',
order_6_player='SBa: Player Name; PD: Card #',
order_6_pos='C for Catcher, 1B for first base, P for pitcher, etc.',
order_7_player='SBa: Player Name; PD: Card #',
order_7_pos='C for Catcher, 1B for first base, P for pitcher, etc.',
order_8_player='SBa: Player Name; PD: Card #',
order_8_pos='C for Catcher, 1B for first base, P for pitcher, etc.',
order_9_player='SBa: Player Name; PD: Card #',
order_9_pos='C for Catcher, 1B for first base, P for pitcher, etc.',
order_10_player='SBa: Player Name; PD: Card #; only used with a DH',
order_10_pos='P for pitcher; only used with a DH'
)
@app_commands.checks.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def set_lineup_command(
self, interaction: discord.Interaction, team_abbrev: str, order_1_player: str, order_1_pos: str,
order_2_player: str, order_2_pos: str, order_3_player: str, order_3_pos: str, order_4_player: str,
order_4_pos: str, order_5_player: str, order_5_pos: str, order_6_player: str, order_6_pos: str,
order_7_player: str, order_7_pos: str, order_8_player: str, order_8_pos: str, order_9_player: str,
order_9_pos: str, order_10_player: Optional[str] = None, order_10_pos: Optional[str] = 'P'):
this_game = get_one_game(channel_id=interaction.channel_id, active=True)
if not this_game:
await interaction.response.send_message(f'I dont\'t see an active game in this channel!')
return
owner_team = await get_game_team(this_game, interaction.user.id)
lineup_team = await get_game_team(this_game, team_abbrev=team_abbrev)
if not owner_team['id'] in [this_game.away_team_id, this_game.home_team_id]:
await interaction.response.send_message('Bruh. Only GMs of the active teams can set lineups.')
return
if not lineup_team['id'] in [this_game.away_team_id, this_game.home_team_id]:
await interaction.response.send_message(f'I do not see {lineup_team["sname"]} in this game. Please check the team abbrev and '
f'try again')
return
existing_lineups = await get_team_lineups(this_game.id, lineup_team['id'])
if not existing_lineups or not len(existing_lineups):
await interaction.response.send_message(
f'It looks like the {lineup_team["sname"]} already have a lineup. Run `/substitution` to make changes.'
)
return
await interaction.response.defer()
all_lineups = []
all_pos = []
lineup_data = [
(order_1_player, order_1_pos), (order_2_player, order_2_pos), (order_3_player, order_3_pos),
(order_4_player, order_4_pos), (order_5_player, order_5_pos), (order_6_player, order_6_pos),
(order_7_player, order_7_pos), (order_8_player, order_8_pos), (order_9_player, order_9_pos),
(order_10_player, order_10_pos)
]
for index, pair in enumerate(lineup_data):
if not pair[0]:
break
if pair[1].upper() not in all_pos:
all_pos.append(pair[1].upper())
else:
raise SyntaxError(f'You have more than one {pair[1].upper()} in this lineup. Please update and set the '
f'lineup again.')
if this_game.is_pd:
this_card = pd_get_card_by_id(int(pair[0]))
if this_card['team']['id'] != lineup_team['id']:
raise SyntaxError(f'Easy there, champ. Looks like card ID {pair[0]} belongs to the '
f'{this_card["team"]["sname"]}. Try again with only cards you own.')
player_id = this_card['player']['id']
card_id = pair[0]
else:
this_player = await get_one_player(pair[0], season=SBA_SEASON)
if this_player['team']['id'] != lineup_team['id']:
raise SyntaxError(f'Easy there, champ. Looks like {pair[0]} is on '
f'{this_player["team"]["sname"]}. Try again with only your own players.')
player_id = this_player['id']
card_id = None
this_lineup = {
'game_id': this_game.id,
'team_id': lineup_team['id'],
'player_id': player_id,
'card_id': card_id,
'position': pair[1].upper(),
'batting_order': index + 1,
'after_play': 0
}
all_lineups.append(this_lineup)
logging.info(f'Setting lineup for {owner_team["sname"]} in {"PD" if this_game.is_pd else "SBa"} game')
post_lineups(all_lineups)
try:
await interaction.edit_original_response(content=None, embed=await self.get_game_state_embed(this_game))
except IntegrityError as e:
logging.info(f'Unable to pull game_state for game_id {this_game.id} until both lineups are in: {e}')
await interaction.response.send_message(f'Game state will be posted once both lineups are in')
return
@commands.hybrid_command(
name='substitution',
help='Make a lineup substitution; Player Name for SBa games / Card ID for PD games',
aliases=['sub']
)
@app_commands.describe(
new_player='For SBa game: enter the Player Name; for PD game: enter the Card ID (a number)')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def substitution_command(
self, ctx: commands.Context, team_abbrev: str, order_number: int, new_player: str, new_pos: str):
this_game = get_one_game(channel_id=ctx.channel.id, active=True)
if not this_game:
await ctx.send(f'I dont\'t see an active game in this channel!')
return
owner_team = await get_game_team(this_game, ctx.author.id)
lineup_team = await get_game_team(this_game, team_abbrev=team_abbrev)
if not owner_team['id'] in [this_game.away_team_id, this_game.home_team_id]:
await ctx.send('Bruh. Only GMs of the active teams can set lineups.')
return
if not lineup_team['id'] in [this_game.away_team_id, this_game.home_team_id]:
await ctx.send(f'I do not see {lineup_team["sname"]} in this game. Please check the team abbrev and '
f'try again')
return
if this_game.is_pd:
this_card = pd_get_card_by_id(int(new_player))
if this_card["team"]["id"] != lineup_team['id']:
raise SyntaxError(f'Easy there, champ. Looks like card ID {new_player} belongs to the '
f'{this_card["team"]["sname"]}. Try again with only cards you own.')
player_id = this_card['player']['id']
card_id = new_player
else:
this_player = await get_one_player(new_player, season=SBA_SEASON)
if this_player['team']['id'] != lineup_team['id']:
raise SyntaxError(f'Easy there, champ. Looks like {new_player} is on '
f'{this_player["team"]["sname"]}. Try again with only your own players.')
player_id = this_player['id']
card_id = None
curr_play = get_current_play(this_game.id)
this_lineup = {
'game_id': this_game.id,
'team_id': lineup_team['id'],
'player_id': player_id,
'card_id': card_id,
'position': new_pos,
'batting_order': order_number,
'after_play': curr_play.play_num - 1
}
make_sub(this_lineup)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game))
@commands.hybrid_command(name='gamestate', help='Post the current game state', aliases=['gs'])
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def game_state_command(self, ctx: commands.Context, include_lineups: bool = True):
this_game = get_one_game(channel_id=ctx.channel.id, active=True)
if not this_game:
await ctx.send(f'I dont\'t see an active game in this channel!')
return
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=include_lineups))
async def checks_log_play(self, ctx: commands.Context) -> (Optional[StratGame], Optional[dict], Optional[StratPlay]):
this_game = get_one_game(channel_id=ctx.channel.id, active=True)
# if not this_game:
# return False, False, False
owner_team = await get_game_team(this_game, ctx.author.id)
if not owner_team['id'] in [this_game.away_team_id, this_game.home_team_id]:
await ctx.send('Bruh. Only GMs of the active teams can log plays.')
# return this_game, False, False
this_play = get_current_play(this_game.id)
if this_play.locked:
await ctx.send(f'Looks like this play is already being advanced. Please wait to log the next play.')
if not this_play.pitcher:
await ctx.send(f'Please sub in a pitcher before logging a new play.')
this_play = None
return this_game, owner_team, this_play
@commands.hybrid_group(name='log-onbase', help='Log a base hit in this channel\'s game')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def log_onbase(self, ctx: commands.Context):
if ctx.invoked_subcommand is None:
await ctx.send('No play details listed. Type `/log-onbase` to see available commands.')
@log_onbase.command(name='single-wellhit', help='Batter to first; runners advance two bases',
aliases=['siwh', 'si**', '1b**', '1bwh'])
async def log_single_wh_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
single_wellhit(this_play)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='single-onestar', help='Batter to first; runners advance one base', aliases=['si*', '1b*'])
async def log_single_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
single_onestar(this_play)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='ballpark-single', help='Batter to first; runners advance one base',
aliases=['bpsi', 'bp1b'])
async def log_ballpark_single_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=1)
patch_play(this_play.id, pa=1, ab=1, hit=1, bp1b=1)
complete_play(this_play.id, batter_to_base=1)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='single-uncapped', help='Batter to first; runners may attempt to advance a second base',
aliases=['si', '1b'])
async def log_single_uncapped_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True, pa=1, ab=1, hit=1)
advance_runners(this_play.id, 1)
this_play = get_current_play(this_game.id)
batter_to_base = 1
logging.info(f'this_play: {this_play}')
# Handle runner starting at second
if this_play.on_second:
this_runner = await get_player(this_game, this_play.on_second)
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(f'Was {this_runner["name"]} sent home?', view=view)
await view.wait()
if view.value:
await question.delete()
view_two = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(content=f'Was {this_runner["name"]} safe at the plate?', view=view_two)
await view_two.wait()
if view_two.value:
advance_one_runner(this_play.id, from_base=2, num_bases=2)
this_play = patch_play(this_play.id, rbi=this_play.rbi + 1)
else:
this_play = patch_play(this_play.id, on_second_final=99, outs=this_play.outs + 1)
await question.delete()
else:
await question.delete()
logging.info(f'this_play: {this_play}')
# Handle runner starting at first
if this_play.on_first and (not this_play.on_second or this_play.on_second_final != 3):
this_runner = await get_player(this_game, this_play.on_first)
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(f'Was {this_runner["name"]} sent to third?', view=view)
await view.wait()
if view.value:
await question.delete()
view_two = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(content=f'Was {this_runner["name"]} safe at third?', view=view_two)
await view_two.wait()
if view_two.value:
advance_one_runner(this_play.id, from_base=1, num_bases=2)
this_play = get_current_play(this_game.id)
else:
this_play = patch_play(this_play.id, on_second_final=99, outs=1)
await question.delete()
else:
await question.delete()
logging.info(f'this_play: {this_play}')
# Handle batter runner if either runner from first or runner from second advanced
if (this_play.on_first and this_play.on_first_final != 2) or \
(this_play.on_second and this_play.on_second_final != 3):
batter = await get_player(this_game, this_play.batter)
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(content=f'Was {batter["name"]} sent to second?', view=view)
await view.wait()
if view.value:
await question.delete()
view_two = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(content=f'Was {batter["name"]} safe at second?', view=view_two)
await view_two.wait()
if view_two.value:
batter_to_base = 2
await question.delete()
else:
await question.delete()
logging.info(f'this_play: {this_play}')
complete_play(this_play.id, batter_to_base=batter_to_base)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='double-twostar', help='Batter to second; runners advance two bases',
aliases=['do**', '2b**'])
async def log_double_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
double_twostar(this_play)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='double-uncapped', help='Batter to second; runners may attempt to advance a third base',
aliases=['do', '2b'])
async def log_double_uncapped_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True, pa=1, ab=1, hit=1, double=1)
batter_to_base = 2
if this_play.on_first:
this_runner = await get_player(this_game, this_play.on_first)
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(f'Was {this_runner["name"]} sent home?', view=view)
await view.wait()
if view.value:
await question.delete()
view_two = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(content=f'Was {this_runner["name"]} safe at the plate?', view=view_two)
await view_two.wait()
if view_two.value:
advance_runners(this_play.id, num_bases=3)
else:
patch_play(this_play.id, on_first_final=99, outs=1)
await question.delete()
batter = await get_player(this_game, this_play.batter)
view_three = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(
content=f'Did {batter["name"]} attempt the advance to third?', view=view_three
)
await view_three.wait()
if view_three.value:
await question.delete()
view_four = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(content=f'Was {batter["name"]} safe at third?', view=view_four)
await view_four.wait()
if view_four.value:
batter_to_base = 3
await question.delete()
else:
advance_runners(this_play.id, num_bases=2)
await question.delete()
complete_play(this_play.id, batter_to_base=batter_to_base)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='double-threestar', help='Batter to second; runners advance three bases',
aliases=['dowh', 'do***'])
async def log_double_wh_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
double_threestar(this_play)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='triple', help='Batter to third; all runners score', aliases=['tr', '3b'])
async def log_triple_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
triple(this_play)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='homerun', help='Batter scores; all runners score', aliases=['hr', 'dong'])
async def log_homerun_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=4)
patch_play(this_play.id, pa=1, ab=1, hit=1, homerun=1)
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='ballpark-homerun', help='Batter scores; all runners score', aliases=['bp-hr', 'bp-dong'])
async def log_homerun_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=3)
patch_play(this_play.id, pa=1, ab=1, hit=1, homerun=1, bphr=1)
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='walk', help='Batter to first; runners advance if forced', aliases=['bb'])
async def log_walk_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=1, only_forced=True)
patch_play(this_play.id, pa=1, walk=1)
complete_play(this_play.id, batter_to_base=1)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='intentional-walk', help='Batter to first; runners advance if forced', aliases=['ibb'])
async def log_int_walk_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=1, only_forced=True)
patch_play(this_play.id, pa=1, ibb=1)
complete_play(this_play.id, batter_to_base=1)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_onbase.command(name='hit-by-pitch', help='Batter to first; runners advance if forced', aliases=['hbp'])
async def log_hit_by_pitch_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=1, only_forced=True)
patch_play(this_play.id, pa=1, hbp=1)
complete_play(this_play.id, batter_to_base=1)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@commands.hybrid_group(name='log-out', help='Log an out result in this channel\'s game')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def log_out(self, ctx: commands.Context):
if ctx.invoked_subcommand is None:
await ctx.send('No play details listed. Type `/log-out` to see available commands.')
@log_out.command(name='popout', help='Batter out; runners hold', aliases=['po'])
async def log_popout_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1)
advance_runners(this_play.id, num_bases=0)
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_out.command(name='strikeout', help='Batter out; runners hold', aliases=['so', 'k'])
async def log_strikeout_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1, so=1)
advance_runners(this_play.id, num_bases=0)
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_out.command(name='lineout', help='Batter out; runners hold', aliases=['lo'])
async def log_lineout_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1)
advance_runners(this_play.id, num_bases=0)
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_out.command(name='sac-bunt', help='Batter out; runners advance one base', aliases=['sacb', 'bunt'])
async def log_sac_bunt_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, num_bases=1)
patch_play(this_play.id, pa=1, sac=1, outs=1)
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_out.command(name='caught-stealing', help='One base runner is caught stealing', aliases=['cs'])
@app_commands.describe(
attempted_base='The base number the runner attempted to steal; 2 for 2nd, 3 for 3rd, 3 for home')
async def log_caught_stealing_command(self, ctx: commands.Context, attempted_base: int):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
catcher = get_one_lineup(
this_game.id, team_id=this_play.pitcher.team_id, position='C'
)
if attempted_base == 4 and this_play.on_third:
patch_play(
this_play.id, cs=1, on_third_final=99, runner_id=this_play.on_third.id,
catcher_id=catcher.id, outs=1
)
elif attempted_base == 3 and this_play.on_second:
if not this_play.on_third:
patch_play(
this_play.id, cs=1, on_second_final=99, runner_id=this_play.on_second.id,
catcher_id=catcher.id, outs=1
)
else:
this_runner = await get_player(this_game, this_play.on_second)
await ctx.send(f'Ope. Looks like {this_runner["name"]} is blocked by the runner at third.')
return
elif attempted_base == 2 and this_play.on_first:
if not this_play.on_second:
patch_play(
this_play.id, cs=1, on_first_final=99, runner_id=this_play.on_first.id,
catcher_id=catcher.id, outs=1
)
else:
this_runner = await get_player(this_game, this_play.on_first)
await ctx.send(f'Ope. Looks like {this_runner["name"]} is blocked by the runner at second.')
return
else:
await ctx.send(f'Uh oh - I don\'t see a runner there to steal the bag.')
return
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_out.command(name='flyball-a', help='Batter out; all runners advance', aliases=['flya'])
async def log_flyballa_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1)
if this_play.starting_outs < 2:
advance_runners(this_play.id, 1)
if this_play.on_third:
patch_play(this_play.id, ab=0)
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_out.command(name='flyball-b', help='Batter out; runner on third scores', aliases=['flyb'])
async def log_flyballb_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1)
advance_runners(this_play.id, num_bases=0)
if this_play.starting_outs < 2 and this_play.on_third:
patch_play(this_play.id, ab=0, rbi=1)
advance_one_runner(this_play.id, from_base=3, num_bases=1)
if this_play.starting_outs < 2 and this_play.on_second:
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send('Did the runner on second advance?', view=view)
await view.wait()
if view.value:
advance_one_runner(this_play.id, from_base=2, num_bases=1)
await question.edit(view=None)
else:
await question.delete()
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_out.command(name='flyball-bq', help='Batter out; runner on third may attempt to score', aliases=['flyb?'])
async def log_flyballbq_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1)
advance_runners(this_play.id, num_bases=0)
if this_play.starting_outs < 2 and this_play.on_third:
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send('Did the runner on third advance?', view=view)
await view.wait()
if view.value:
advance_one_runner(this_play.id, from_base=3, num_bases=1)
patch_play(this_play.id, ab=0, rbi=1)
await question.edit(view=None)
else:
await question.delete()
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_out.command(name='flyball-c', help='Batter out; no runners advance', aliases=['flyc'])
async def log_flyballc_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1)
advance_runners(this_play.id, num_bases=0)
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@commands.hybrid_group(name='log-play', help='Log a result in this channel\'s game')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def log_play(self, ctx: commands.Context):
if ctx.invoked_subcommand is None:
await ctx.send('No play details listed. Type `/log` to see available commands.')
@log_play.command(name='undo-play', help='Remove the most recent play from the log', aliases=['undo', 'rollback'])
async def log_undo_play_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
undo_play(this_play.id)
undo_play(get_current_play(this_game.id).id)
complete_play(get_current_play(this_game.id).id, batter_to_base=get_latest_play(this_game.id).batter_final)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_play.command(name='stolen-base', help='One base runner steals a base', aliases=['sb'])
@app_commands.describe(stolen_base='The base number stolen by the runner; 2 for 2nd, 3 for 3rd, 4 for home')
async def log_stolen_base_command(self, ctx: commands.Context, stolen_base: int):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
catcher = get_one_lineup(
this_game.id, team_id=this_play.pitcher.team_id, position='C'
)
if stolen_base == 4 and this_play.on_third:
patch_play(
this_play.id, sb=1, on_third_final=4, runner_id=this_play.on_third.id, catcher_id=catcher.id
)
elif stolen_base == 3 and this_play.on_second:
if not this_play.on_third:
patch_play(
this_play.id, sb=1, on_second_final=3, runner_id=this_play.on_second.id, catcher_id=catcher.id
)
else:
this_runner = await get_player(this_game, this_play.on_second)
await ctx.send(f'Ope. Looks like {this_runner["name"]} is blocked by the runner at third.')
patch_play(this_play.id, locked=False)
return
elif stolen_base == 2 and this_play.on_first:
if not this_play.on_second:
patch_play(
this_play.id, sb=1, on_first_final=2, runner_id=this_play.on_first.id, catcher_id=catcher.id
)
else:
this_runner = await get_player(this_game, this_play.on_first)
await ctx.send(f'Ope. Looks like {this_runner["name"]} is blocked by the runner at second.')
patch_play(this_play.id, locked=False)
return
else:
await ctx.send(f'Uh oh - I don\'t see a runner there to steal the bag.')
patch_play(this_play.id, locked=False)
return
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_play.command(name='wild-pitch', help='All runners advance one base', aliases=['wp'])
async def log_wild_pitch_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
advance_runners(this_play.id, 1)
patch_play(this_play.id, rbi=0, wp=1)
complete_play(this_play.id)
@log_play.command(name='passed-ball', help='All runners advance one base', aliases=['pb'])
async def log_passed_ball_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
advance_runners(this_play.id, 1)
patch_play(this_play.id, rbi=0, pb=1)
complete_play(this_play.id)
@log_play.command(name='balk', help='All runners advance one base', aliases=['bk'])
async def log_balk_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
advance_runners(this_play.id, 1)
patch_play(this_play.id, rbi=0, balk=1)
complete_play(this_play.id)
@log_play.command(name='pickoff', help='One baserunner is out on the basepaths', aliases=['pick'])
async def log_pickoff_command(self, ctx: commands.Context, from_base: int):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
if from_base == 3 and this_play.on_third:
patch_play(this_play.id, on_third_final=99, runner_id=this_play.on_third.id, outs=1)
elif from_base == 2 and this_play.on_second:
patch_play(this_play.id, on_second_final=99, runner_id=this_play.on_second.id, outs=1)
elif from_base == 1 and this_play.on_first:
patch_play(this_play.id, on_first_final=99, runner_id=this_play.on_first.id, outs=1)
else:
await ctx.send(f'Uh oh - I don\'t see a runner there to be picked off.')
patch_play(this_play.id, locked=False)
return
complete_play(this_play.id)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@commands.hybrid_group(name='show-card', help='Display an active player\'s card')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def show_player(self, ctx: commands.Context):
if ctx.invoked_subcommand is None:
await ctx.send('No player details listed. Type `/show-player` to see available commands.')
@show_player.command(name='defense', help='Display a defender\'s player card', aliases=['pick'])
async def show_defense_command(
self, ctx: commands.Context, position: Literal[
'Catcher', 'First Base', 'Second Base', 'Third Base', 'Shortstop', 'Left Field', 'Center Field',
'Right Field']):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
defender = await get_player(
game=this_game,
lineup_member=get_one_lineup(
this_game.id, team_id=this_play.pitcher.team_id, position=get_pos_abbrev(position)
)
)
embed = get_team_embed(f'{defender["team"]["sname"]} {position}', defender['team'])
embed.description = f'{defender["name"]}'
embed.set_image(url=defender['image'])
if this_game.is_pd:
embed.set_footer(text=f'PD Season {PD_SEASON}', icon_url=LOGO)
await ctx.send(content=None, embed=embed)
@log_out.command(name='groundball-a', help='Potential double play ground ball', aliases=['gba'])
async def log_groundballa_command(self, ctx: commands.Context):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
batter_to_base = None
patch_play(this_play.id, locked=True)
if this_play.starting_outs == 2 or this_play.on_base_code == 0:
patch_play(this_play.id, pa=1, ab=1, outs=1)
else:
if this_play.on_base_code == 1:
runner = await get_player(this_game, this_play.on_first)
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(f'Is {runner["name"]} out at second on the double play?', view=view)
await view.wait()
if view.value:
await question.delete()
patch_play(this_play.id, on_first_final=False, pa=1, ab=1, outs=2)
else:
await question.delete()
await ctx.send(f'Okay so it wasn\'t a gb A then? Go ahead and log a new play.')
patch_play(this_play.id, locked=False)
return
elif this_play.on_base_code == 7:
runner = await get_player(this_game, this_play.on_third)
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(f'Is {runner["name"]} out on the home-to-first double play?', view=view)
await view.wait()
if view.value:
await question.delete()
advance_runners(this_play.id, 1)
patch_play(this_play.id, on_third_final=False, pa=1, ab=1, outs=2, rbi=0)
else:
await question.delete()
runner = await get_player(this_game, this_play.on_second)
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(f'Is {runner["name"]} out at second on the double play?', view=view)
await view.wait()
if view.value():
await question.delete()
advance_runners(this_play.id, 1)
patch_play(this_play.id, on_first_final=False, pa=1, ab=1, outs=2, rbi=0)
else:
await question.delete()
await ctx.send(f'Okay so it wasn\'t a gb A then? Go ahead and log a new play.')
patch_play(this_play.id, locked=False)
return
else:
num_outs = 1
bases = ['third', 'second', 'first']
for count, x in enumerate([this_play.on_third, this_play.on_second, this_play.on_first]):
if x:
runner = await get_player(this_game, x)
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(
f'Did {runner["name"]} advance from {bases[count]} on the play?', view=view
)
await view.wait()
num_bases = 0
if view.value:
await question.delete()
advance_one_runner(this_play.id, from_base=3 - count, num_bases=1)
else:
await question.delete()
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(f'Was {runner["name"]} doubled off?', view=view)
await view.wait()
if view.value:
await question.delete()
if count == 0:
patch_play(this_play.id, on_third_final=False)
elif count == 1:
patch_play(this_play.id, on_second_final=False)
else:
patch_play(this_play.id, on_first_final=False)
num_outs += 1
else:
await question.delete()
if count == 0:
patch_play(this_play.id, on_third_final=3)
elif count == 1:
patch_play(this_play.id, on_second_final=2)
else:
patch_play(this_play.id, on_first_final=1)
if this_play.on_third:
batter = await get_player(this_game, this_play.batter)
view = Confirm(responders=[ctx.author], timeout=60)
question = await ctx.send(
f'Is {batter["name"]} out at first?', view=view
)
await view.wait()
if view.value:
await question.delete()
else:
await question.delete()
num_outs -= 1
batter_to_base = 1
patch_play(this_play.id, pa=1, ab=1, outs=num_outs)
complete_play(this_play.id, batter_to_base=batter_to_base)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
@log_play.command(name='xcheck', help='Defender makes an x-check')
async def log_xcheck_command(self, ctx: commands.Context, position: Literal[
'Catcher', 'First Base', 'Second Base', 'Third Base', 'Shortstop', 'Left Field', 'Center Field',
'Right Field'], hit_allowed: Literal['out', 'single*', 'single**', 'double**', 'double***', 'triple'],
error_allowed: Literal['out', '1 base', '2 bases', '3 bases']):
this_game, owner_team, this_play = await self.checks_log_play(ctx)
if False in (this_game, owner_team, this_play):
return
pos = get_pos_abbrev(position)
defender = get_one_lineup(
this_game.id, team_id=this_play.pitcher.team_id, position=pos
)
logging.info(f'defender: {defender}')
patch_play(this_play.id, defender_id=defender.id, error=1 if error_allowed != 'out' else 0, check_pos=pos)
# Not hit and no error
if hit_allowed == 'out' and error_allowed == 'out':
await ctx.send(f'Just logged the x-check! Please log the resulting play to continue (e.g. \'flyball-b\' or '
f'\'groundball-a\')')
return
# Hit and error
if hit_allowed != 'out' and error_allowed != 'out':
batter_to_base = 1
if hit_allowed == 'triple':
triple(this_play, comp_play=False)
elif 'double' in hit_allowed:
double_threestar(this_play, comp_play=False)
if error_allowed == '1 base':
batter_to_base = 3
elif error_allowed == '3 bases':
batter_to_base = 4
# 2 base error is the only one handled differently between doubles
elif hit_allowed == 'double***':
batter_to_base = 4
else:
batter_to_base = 3
# Both singles are handled the same
else:
single_wellhit(this_play, comp_play=False)
if error_allowed == '1 base':
batter_to_base = 2
else:
batter_to_base = 3
complete_play(this_play.id, batter_to_base=batter_to_base)
# Either hit or error
num_bases = None
if error_allowed == 'out':
if hit_allowed == 'single*':
single_onestar(this_play)
elif hit_allowed == 'single**':
single_wellhit(this_play)
elif hit_allowed == 'double**':
double_twostar(this_play)
elif hit_allowed == 'double***':
double_threestar(this_play)
elif hit_allowed == 'triple':
triple(this_play)
else:
if error_allowed == '1 base':
num_bases = 1
elif error_allowed == '2 bases':
num_bases = 2
elif error_allowed == '3 bases':
num_bases = 3
advance_runners(this_play.id, num_bases=num_bases, is_error=True)
complete_play(this_play.id, batter_to_base=num_bases)
await ctx.send(content=None, embed=await self.get_game_state_embed(this_game, full_length=False))
# TODO: ground balls
async def setup(bot):
await bot.add_cog(Gameplay(bot))