paper-dynasty-discord/cogs/gameplay_legacy.py
Cal Corum 512efe98c4 db_calls -> api_calls
gameplay_db -> gameplay_models
new-game campaign in progress
added Player model
2024-10-12 18:22:13 -05:00

4720 lines
218 KiB
Python

import asyncio
import logging
import discord
import math
import os
from in_game import ai_manager
import dice
import gauntlets
from discord import app_commands
from discord.app_commands import Choice
from discord.ext import commands, tasks
from peewee import IntegrityError
from typing import Literal, Optional
from dice import sa_fielding_roll
from helpers import SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME, random_conf_gif, SBA_SEASON, PD_SEASON, IMAGES, \
get_pos_abbrev, SBA_COLOR, get_roster_lineups, give_packs, send_to_channel, \
get_channel, team_role, get_cal_user, ButtonOptions, get_ratings_guide, \
get_team_by_owner, player_desc, player_pcard, player_bcard, get_team_embed, Confirm, get_sheets, Dropdown, \
SELECT_CARDSET_OPTIONS, DropdownView
from in_game.ai_manager import check_pitching_sub
from in_game.game_helpers import single_onestar, single_wellhit, double_twostar, double_threestar, triple, \
runner_on_first, runner_on_second, runner_on_third, gb_result_1, gb_result_2, gb_result_3, gb_result_4, \
gb_result_5, gb_result_6, gb_result_7, gb_result_8, gb_result_9, gb_result_10, gb_result_11, gb_result_12, \
gb_result_13, gb_decide, show_outfield_cards, legal_check, get_pitcher
from api_calls import db_get, db_patch, db_post, db_delete, get_team_by_abbrev
from db_calls_gameplay import StratGame, StratPlay, post_game, patch_game, get_game_team, post_lineups, 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, count_team_games, \
get_pitching_decisions, get_or_create_bullpen, get_active_games, patch_lineup, \
get_plays, get_manager, get_one_game, load_ai, ai_batting, undo_subs, get_dbready_plays
class Gameplay(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.batter_ratings = None
self.pitcher_ratings = None
self.live_scoreboard.start()
@tasks.loop(minutes=3)
async def live_scoreboard(self):
guild = self.bot.get_guild(int(os.environ.get('GUILD_ID')))
score_channel = discord.utils.get(guild.text_channels, name='live-pd-scores')
player_role = discord.utils.get(guild.roles, name=PD_PLAYERS_ROLE_NAME)
active_games = get_active_games(6)
if len(active_games) == 0:
await score_channel.set_permissions(player_role, read_messages=False)
return
try:
embed = get_team_embed('Live Scoreboard')
valid_scores = False
for x in active_games:
await asyncio.sleep(1)
gs = await self.get_game_state(x)
if not gs['error']:
valid_scores = True
channel = guild.get_channel(gs["channel_id"])
g_message = gs['scorebug']
g_message += f'Pitcher: {gs["pitcher"]["p_name"]}\n' \
f'Batter: {gs["batter"]["p_name"]}\n' \
f'Location: {channel.mention if channel is not None else "Your Butt"}\n' \
f'-------------------------'
gt_string = 'Unlimited'
if x.game_type == 'minor-league':
gt_string = 'Minor League'
elif x.game_type == 'major-league':
gt_string = 'Major League'
elif x.game_type == 'hall-of-fame':
gt_string = 'Hall of Fame'
elif 'gauntlet' in x.game_type:
gt_string = 'Gauntlet'
elif x.game_type == 'flashback':
gt_string = 'Flashback'
elif 'exhibition' in x.game_type:
gt_string = 'Exhibition'
embed.add_field(
name=f'{gs["away_team"]["sname"]} @ {gs["home_team"]["sname"]} - {gt_string}',
value=g_message,
inline=False
)
if valid_scores:
async for message in score_channel.history(limit=25):
await message.delete()
await score_channel.set_permissions(player_role, read_messages=True)
await score_channel.send(content=None, embed=embed)
else:
await score_channel.set_permissions(player_role, read_messages=False)
return
except Exception as e:
logging.error(f'Could not update live_scoreboard: {e}')
@live_scoreboard.before_loop
async def before_live_scoreboard(self):
await self.bot.wait_until_ready()
@tasks.loop(hours=36)
async def update_ratings_guides(self):
guild = self.bot.get_guild(int(os.environ.get('GUILD_ID')))
if not guild:
logging.error(f'Cannot access guild; pausing ratings guide for 20 seconds')
await asyncio.sleep(20)
guild = self.bot.get_guild(int(os.environ.get('GUILD_ID')))
if not guild:
logging.error(f'Still cannot access guild; trying again in 1 minutes')
await asyncio.sleep(60)
guild = self.bot.get_guild(int(os.environ.get('GUILD_ID')))
if not guild:
logging.error(f'Still cannot access guild; dueces')
return
data = get_ratings_guide(get_sheets(self.bot))
if data['valid']:
self.batter_ratings = data['batter_ratings']
self.pitcher_ratings = data['pitcher_ratings']
else:
logging.error(f'gameplay - pulled bad ratings guide data')
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[:1600]}')
async def post_stratgame(self, this_game: StratGame, forfeit: bool = False):
return await db_post('games', payload={
'season': this_game.season,
'game_type': this_game.game_type,
'away_team_id': this_game.away_team_id,
'home_team_id': this_game.home_team_id,
'week': this_game.week_num,
'ranked': this_game.ranked,
'short_game': this_game.short_game,
'forfeit': forfeit
})
async def post_allplays(self, this_game: StratGame, final_game_id: int):
all_plays = get_dbready_plays(this_game.id, db_game_id=final_game_id)
await asyncio.sleep(0.5)
return await db_post(
'plays',
payload={'plays': all_plays},
timeout=15
)
async def post_decisions(self, this_game: StratGame, final_game_id: int):
all_dec = get_pitching_decisions(this_game, final_game_id)
await asyncio.sleep(0.5)
return await db_post(
'decisions',
payload={'decisions': all_dec},
timeout=5
)
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 ''
if curr_play.game.active:
inning = f'{half} {curr_play.inning_num}'
outs = f'{curr_play.starting_outs} Out{"s" if curr_play.starting_outs != 1 else ""}'
else:
inning = f'F/{curr_play.inning_num if curr_play.inning_half == "Bot" else curr_play.inning_num - 1}'
outs = ''
game_string = f'```\n' \
f'{away_team["abbrev"].replace("Gauntlet-", ""): ^4}{curr_play.away_score: ^3} {second_base}' \
f'{inning: >10}\n' \
f'{home_team["abbrev"].replace("Gauntlet-", ""): ^4}{curr_play.home_score: ^3} {third_base} ' \
f'{first_base}{outs: >8}\n```'
return game_string
async def post_rewards(self, winning_team: dict, losing_team: dict, this_game: StratGame):
wr_query = await db_get(
'gamerewards', params=[('name', f'{"Short" if this_game.short_game else "Full"} Game Win')])
lr_query = await db_get(
'gamerewards', params=[('name', f'{"Short" if this_game.short_game else "Full"} Game Loss')])
if not wr_query['count'] or not lr_query['count']:
raise KeyError(f'Game Rewards were not found. Leaving this game active.')
human_team = winning_team if losing_team['is_ai'] else losing_team
ai_team = winning_team if winning_team['is_ai'] else losing_team
win_reward = wr_query['gamerewards'][0]
loss_reward = lr_query['gamerewards'][0]
win_string = f'1x {win_reward["pack_type"]["name"]} Pack\n'
# Post Team Choice packs
if this_game.ai_team is not None and not this_game.short_game and 'gauntlet' not in this_game.game_type and \
losing_team['is_ai']:
g_query = await db_get(
'games',
params=[
('team1_id', human_team['id']), ('game_type', this_game.game_type), ('season', this_game.season)
]
)
wins = 0
for x in g_query['games']:
if (x['away_score'] > x['home_score'] and x['away_team']['id'] == human_team['id']) or (
x['home_score'] > x['away_score'] and x['home_team']['id'] == human_team['id']):
wins += 1
async def post_tc_pack():
await db_post(
'packs/one',
payload={
'team_id': human_team['id'],
'pack_type_id': 8,
'pack_team_id': losing_team['id']
}
)
if g_query['count'] > 0:
if this_game.game_type == 'minor-league' and wins % 6 == 0:
await post_tc_pack()
win_string += f'1x {losing_team["abbrev"]} Team Choice pack\n'
elif this_game.game_type == 'major-league' and wins % 4 == 0:
await post_tc_pack()
win_string += f'1x {losing_team["abbrev"]} Team Choice pack\n'
elif this_game.game_type == 'hall-of-fame' and wins % 2 == 0:
await post_tc_pack()
win_string += f'1x {losing_team["abbrev"]} Team Choice pack\n'
win_string += f'{win_reward["money"]}\n'
loss_string = f'{loss_reward["money"]}\n'
# Post rewards
if 'gauntlet' in this_game.game_type:
if 'Gauntlet' in winning_team['abbrev']:
t_query = await db_get('teams', params=[('abbrev', winning_team['abbrev'].split('-')[1])])
winning_team = t_query['teams'][0]
if 'Gauntlet' in losing_team['abbrev']:
t_query = await db_get('teams', params=[('abbrev', losing_team['abbrev'].split('-')[1])])
losing_team = t_query['teams'][0]
await give_packs(winning_team, num_packs=1, pack_type=win_reward['pack_type'])
await db_post(f'teams/{winning_team["id"]}/money/{win_reward["money"]}')
await db_post(f'teams/{losing_team["id"]}/money/{loss_reward["money"]}')
data = {
'win_string': win_string,
'loss_string': loss_string
}
return data
async def get_game_state(self, game: StratGame) -> dict:
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)
curr_play = get_current_play(game.id)
if curr_play is None:
away_lineup = await get_team_lineups(game.id, game.away_team_id)
home_lineup = await get_team_lineups(game.id, game.home_team_id)
logging.info(f'away_lineup: {away_lineup}')
logging.info(f'home_lineup: {home_lineup}')
if len(away_lineup) < 200 or len(home_lineup) < 200:
game_state = {
'error': True,
'away_lineup': away_lineup,
'home_lineup': home_lineup,
'away_team': away_team,
'home_team': home_team
}
logging.error(f'One ore more lineups not submitted in Game {game.id}\n\ngame_state: {game_state}')
return game_state
else:
logging.info(f'looking for home ({game.home_team_id}) pitcher in Game {game.id}')
pitcher = get_one_lineup(game.id, team_id=game.home_team_id, position='P')
logging.debug(f'pitcher: {pitcher}')
curr_play = post_play({
'game_id': game.id,
'play_num': 1,
'batter_id': get_one_lineup(game.id, team_id=game.away_team_id, batting_order=1).id,
'pitcher_id': pitcher.id if pitcher else None,
'on_base_code': 0,
'inning_half': 'Top',
'inning_num': 1,
'batting_order': 1,
'starting_outs': 0,
'away_score': 0,
'home_score': 0
})
game_state = {'error': False, 'curr_play': curr_play, 'away_team': away_team, '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:
if not game_state['curr_play'].pitcher:
p_search = get_one_lineup(
game.id,
team_id=game.away_team_id if game_state['curr_play'].inning_half == 'Bot' else game.home_team_id,
position='P'
)
if p_search:
patch_play(game_state['curr_play'].id, pitcher_id=p_search.id)
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 == 'Bot' else game.home_team_id,
position='C'
)
)
except Exception as e:
logging.error(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
game_state['channel_id'] = game.channel_id
if curr_play.inning_half == 'Top':
game_state['pitcher']['team'] = home_team
game_state['catcher']['team'] = home_team
game_state['batter']['team'] = away_team
else:
game_state['pitcher']['team'] = away_team
game_state['catcher']['team'] = away_team
game_state['batter']['team'] = home_team
logging.debug(f'game_state: {game_state}')
return game_state
async def initialize_play_plus_embed(self, game: StratGame, full_length=True, for_liveboard=False):
game_state = await self.get_game_state(game)
logging.debug(f'game_state: {game_state}')
gt_string = ' - Unlimited'
if game.game_type == 'minor-league':
gt_string = ' - Minor League'
elif game.game_type == 'major-league':
gt_string = ' - Major League'
elif game.game_type == 'hall-of-fame':
gt_string = ' - Hall of Fame'
elif 'gauntlet' in game.game_type:
gt_string = ' - Gauntlet'
elif 'flashback' in game.game_type:
gt_string = ' - Flashback'
elif 'exhibition' in game.game_type:
gt_string = ' - Exhibition'
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 game_state['away_lineup'] else 'None, yet')
embed.add_field(name='Home Team',
value=game_state['home_lineup'] if game_state['home_lineup'] else 'None, yet')
if 'error_message' in game_state:
embed.set_footer(text=game_state['error_message'], icon_url=IMAGES['logo'])
return embed
logging.debug(f'no errors')
pitching_sub = None
ai_note = ''
gm_name = ''
fatigue = await ai_manager.is_pitcher_fatigued(game_state['curr_play'])
logging.debug(f'do AI stuff')
# If an AI team is playing
if True in [game_state['pitcher']['team']['is_ai'], game_state['batter']['team']['is_ai']]:
logging.debug(f'Checking AI stuff')
# AI Team is pitching
if game_state['pitcher']['team']['is_ai']:
if fatigue:
pitching_sub = await check_pitching_sub(game_state['curr_play'], game_state['pitcher']['team'])
if pitching_sub is not None:
game_state = await self.get_game_state(game)
ai_data = await ai_manager.pitching_ai_note(game_state['curr_play'], game_state['pitcher'])
logging.debug(f'ai_data: {ai_data}')
ai_note = ai_data['note']
gm_name = ai_data['gm_name']
# AI Team is batting
if game_state['batter']['team']['is_ai']:
# embed.set_thumbnail(url=player_pcard(game_state["pitcher"]))
ai_data = ai_manager.batting_ai_note(game_state['curr_play'], game_state['batter'])
ai_note = ai_data['note']
gm_name = ai_data['gm_name']
if pitching_sub is not None or (fatigue and pitching_sub is None) or abs(
game_state['curr_play'].home_score - game_state['curr_play'].away_score) >= 10:
color = discord.Colour.red()
else:
color = int(SBA_COLOR, 16)
embed = discord.Embed(
title=f'{game_state["away_team"]["sname"]} @ {game_state["home_team"]["sname"]}{gt_string}',
color=color
)
logging.info(f'got embed')
footer_text = f'Paper Dynasty Season {PD_SEASON}'
if game.short_game:
footer_text += f' - Reminder: all pitchers have POW(1) in 3-inning games'
embed.set_footer(text=footer_text, icon_url=IMAGES['logo'])
embed.add_field(name='Game State', value=game_state['scorebug'], inline=False)
embed.set_thumbnail(url=player_pcard(game_state['pitcher']))
logging.info(f'check mercy')
if abs(game_state['curr_play'].home_score - game_state['curr_play'].away_score) >= 10:
embed.description = '***Mercy rule in effect***'
logging.info(f'set pitcher string')
pitcher_string = f'{player_link(game, game_state["pitcher"])}'
batter_string = f'{game_state["curr_play"].batter.batting_order}. {player_link(game, game_state["batter"])} - ' \
f'{game_state["curr_play"].batter.position}'
logging.info(f'pull bat stats')
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}'
logging.info(f'pull pitcher stats')
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'
if fatigue and pitching_sub is None:
pitcher_string += f'\n***F A T I G U E D***'
logging.info(f'set embed pitcher/batter')
embed.add_field(name='Pitcher', value=f'{pitcher_string}')
embed.add_field(name='Batter', value=f'{batter_string}')
embed.set_image(url=player_bcard(game_state['batter']))
logging.info(f'get baserunners')
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'
logging.info(f'set baserunners')
if len(baserunner_string) > 0:
embed.add_field(name=' ', value=' ', inline=False)
embed.add_field(
name='Baserunners', value=baserunner_string
)
embed.add_field(
name='Catcher', value=f'{player_link(game, game_state["catcher"])}'
)
else:
embed.add_field(
name='Baserunners', value='None', inline=False
)
if len(ai_note) > 0:
embed.add_field(
name=f'{gm_name} will...',
value=ai_note,
inline=False
)
if pitching_sub is not None:
embed.add_field(
name='SUBSTITUTION',
value=f'The {game_state["pitcher"]["team"]["sname"]} have brought in '
f'**{player_desc(pitching_sub)}** to pitch'
)
logging.info(f'if not full length: return embed: {embed}')
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
async def groundballs_old(
self, interaction, this_game, this_play: StratPlay, groundball_type: str, comp_play: bool = True):
batter_to_base = None
bases = ['third', 'second', 'first']
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 groundball_type == 'a':
if this_play.on_base_code == 1:
patch_play(this_play.id, on_first_final=False, pa=1, ab=1, outs=2)
elif this_play.on_base_code == 4:
if this_play.starting_outs == 1:
patch_play(this_play.id, pa=1, ab=1, outs=2)
else:
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is the double play at second and first?', view=view
)
await view.wait()
if view.value:
await question.delete()
patch_play(
this_play.id,
pa=1,
ab=1,
outs=2,
on_second_final=3,
on_first_final=False,
)
else:
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is the double play at third and second?', view=view
)
await view.wait()
if view.value:
await question.delete()
patch_play(
this_play.id,
pa=1,
ab=1,
outs=2,
on_second_final=False,
on_first_final=False
)
batter_to_base = 1
else:
await question.edit(
content=f'Hmm...not sure what else this could be. If you expected a different '
f'result, let Cal know.',
view=None
)
return
elif this_play.on_base_code == 7:
if this_play.starting_outs == 1:
patch_play(this_play.id, pa=1, ab=1, outs=2)
else:
runner = await get_player(this_game, this_play.on_third)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is {runner["p_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()
advance_runners(this_play.id, 1)
patch_play(this_play.id, on_first_final=False, pa=1, ab=1, outs=2, rbi=0)
else:
num_outs = 1
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=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} sent from {bases[count]} on the play?', view=view
)
await view.wait()
num_bases = 0
if view.value:
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} thrown out?', 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=4)
elif count == 1:
patch_play(this_play.id, on_second_final=3)
else:
patch_play(this_play.id, on_first_final=2)
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=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is {batter["p_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)
if num_outs + this_play.starting_outs == 3:
advance_runners(this_play.id, 0)
elif groundball_type == 'b':
if this_play.on_base_code == 3 or this_play.on_base_code == 6 or this_play.on_base_code == 2:
if this_play.on_base_code == 2:
runner = await get_player(this_game, this_play.on_second)
from_base = 'second'
else:
runner = await get_player(this_game, this_play.on_third)
from_base = 'third'
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} sent from {from_base} on the play?', view=view
)
await view.wait()
if view.value:
advance_runners(this_play.id, 1)
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} thrown out?', view=view
)
await view.wait()
if view.value:
await question.delete()
if from_base == 'third':
patch_play(this_play.id, on_third_final=False)
# from second
else:
patch_play(this_play.id, on_second_final=False)
else:
await question.delete()
else:
await question.delete()
advance_runners(this_play.id, 0)
patch_play(this_play.id, pa=1, ab=1, outs=1)
elif this_play.on_base_code == 1 or this_play.on_base_code == 4:
advance_runners(this_play.id, 1)
patch_play(this_play.id, on_first_final=False, pa=1, ab=1, outs=1)
batter_to_base = 1
else:
num_outs = 0
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=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} sent from {bases[count]} on the play?', view=view
)
await view.wait()
num_bases = 0
if view.value:
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} thrown out?', 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()
advance_one_runner(this_play.id, from_base=3 - count, num_bases=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 num_outs == 0:
batter = await get_player(this_game, this_play.batter)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is {batter["p_name"]} out at first?', view=view
)
await view.wait()
if view.value:
await question.delete()
num_outs += 1
else:
await question.delete()
await interaction.edit_original_response(
content=f'Okay so it wasn\'t a gb B then? Go ahead and log a new play.'
)
patch_play(this_play.id, locked=False)
return
else:
batter_to_base = 1
patch_play(this_play.id, pa=1, ab=1, outs=num_outs)
elif groundball_type == 'c':
if this_play.on_base_code == 7:
runner = await get_player(this_game, this_play.on_third)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is {runner["p_name"]} forced out at home?', 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=1, rbi=0)
batter_to_base = 1
else:
await question.delete()
advance_runners(this_play.id, 1)
patch_play(this_play.id, pa=1, ab=1, outs=1)
elif this_play.on_third:
num_outs = 0
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=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} sent from {bases[count]} on the play?', view=view
)
await view.wait()
if view.value:
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} thrown out?', 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=4)
elif count == 1:
patch_play(this_play.id, on_second_final=3)
else:
patch_play(this_play.id, on_first_final=2)
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 not num_outs:
num_outs += 1
logging.debug(f'should be patching the gb C now...')
patch_play(this_play.id, pa=1, ab=1, outs=num_outs)
else:
advance_runners(this_play.id, 1)
patch_play(this_play.id, pa=1, ab=1, outs=1)
if comp_play:
complete_play(this_play.id, batter_to_base=batter_to_base)
async def groundballs(
self, interaction, this_game, this_play: StratPlay, groundball_type: str, comp_play: bool = True):
batter_to_base = None
if this_play.starting_outs == 2 or this_play.on_base_code == 0:
patch_play(this_play.id, pa=1, ab=1, outs=1)
elif this_play.starting_outs == 1 and groundball_type == 'a' and this_play.on_base_code == 1:
patch_play(this_play.id, pa=1, ab=1, outs=2, on_first_final=False)
else:
playing_in = False
if runner_on_third(this_play):
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was the defender playing in?', view=view
)
await view.wait()
if view.value:
playing_in = True
else:
playing_in = False
await question.delete()
logging.info(f'playing_in: {playing_in} / obc: {this_play.on_base_code} / gb_type: {groundball_type}')
if not playing_in:
if groundball_type == 'a':
if this_play.on_base_code == 0:
batter_to_base = gb_result_1(this_play)
elif this_play.on_base_code in [1, 4, 5, 7]:
batter_to_base = gb_result_2(this_play)
elif this_play.on_base_code in [3, 6]:
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was the ball hit to either the 2B or SS?', view=view
)
await view.wait()
if view.value:
to_mif = True
else:
to_mif = False
await question.delete()
batter_to_base = gb_result_5(this_play, to_mif)
else:
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was the ball hit to either the 1B or 2B?', view=view
)
await view.wait()
if view.value:
to_right_side = True
else:
to_right_side = False
await question.delete()
batter_to_base = gb_result_6(this_play, to_right_side)
elif groundball_type == 'b':
if this_play.on_base_code == 0:
batter_to_base = gb_result_1(this_play)
elif this_play.on_base_code in [1, 4, 5, 7]:
batter_to_base = gb_result_4(this_play)
elif this_play.on_base_code in [3, 6]:
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was the ball hit to either the 2B or SS?', view=view
)
await view.wait()
if view.value:
to_mif = True
else:
to_mif = False
await question.delete()
batter_to_base = gb_result_5(this_play, to_mif)
else:
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was the ball hit to either the 1B or 2B?', view=view
)
await view.wait()
if view.value:
to_right_side = True
else:
to_right_side = False
await question.delete()
batter_to_base = gb_result_6(this_play, to_right_side)
else:
if this_play.on_base_code == 0:
batter_to_base = gb_result_1(this_play)
else:
batter_to_base = gb_result_3(this_play)
else:
if groundball_type == 'a':
if this_play.on_base_code == 7:
batter_to_base = gb_result_10(this_play)
else:
batter_to_base = gb_result_7(this_play)
elif groundball_type == 'b':
if this_play.on_base_code == 7:
batter_to_base = gb_result_11(this_play)
elif this_play.on_base_code == 5:
batter_to_base = gb_result_9(this_play)
else:
batter_to_base = gb_result_1(this_play)
else:
if this_play.on_base_code == 7:
batter_to_base = gb_result_11(this_play)
else:
batter_to_base = gb_result_8(this_play)
if comp_play:
complete_play(this_play.id, batter_to_base=batter_to_base)
async def flyballs(self, interaction, this_game, this_play, flyball_type, comp_play: bool = True):
num_outs = 1
if flyball_type == 'a':
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)
elif flyball_type == 'b' or flyball_type == 'ballpark':
patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1, bpfo=1 if flyball_type == 'ballpark' else 0)
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:
logging.debug(f'calling of embed')
await show_outfield_cards(interaction, this_play)
logging.debug(f'done with of embed')
ai_hint = ''
if this_game.ai_team and ai_batting(this_game, this_play):
ai_hint = f'*The runner will {get_manager(this_game).tag_from_second(this_play.starting_outs + 1)}*'
runner = await get_player(this_game, this_play.on_second)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} sent from second on the play?\n\n{ai_hint}', view=view
)
await view.wait()
if view.value:
await question.delete()
view = ButtonOptions(
responders=[interaction.user], timeout=60,
labels=['Tagged Up', 'Hold at 2nd', 'Out at 3rd', None, None]
)
question = await interaction.channel.send(
f'What was the result of {runner["p_name"]} tagging from second?', view=view
)
await view.wait()
if view.value:
await question.delete()
if view.value == 'Tagged Up':
advance_one_runner(this_play.id, from_base=2, num_bases=1)
elif view.value == 'Out at 3rd':
num_outs += 1
patch_play(this_play.id, on_second_final=False, outs=num_outs)
else:
await question.delete()
else:
await question.delete()
elif flyball_type == 'b?':
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:
logging.debug(f'calling of embed')
await show_outfield_cards(interaction, this_play)
logging.debug(f'done with of embed')
ai_hint = ''
if ai_batting(this_game, this_play):
ai_hint = f'*The runner will {get_manager(this_game).tag_from_third(this_play.starting_outs + 1)}*'
runner = await get_player(this_game, this_play.on_third)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} sent from third on the play?\n\n{ai_hint}', view=view
)
await view.wait()
if view.value:
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was {runner["p_name"]} thrown out?', view=view
)
await view.wait()
if view.value:
await question.delete()
patch_play(this_play.id, on_third_final=False)
num_outs += 1
patch_play(this_play.id, outs=num_outs)
else:
await question.delete()
advance_one_runner(this_play.id, from_base=3, num_bases=1)
else:
await question.delete()
elif flyball_type == 'c':
patch_play(this_play.id, locked=True, pa=1, ab=1, outs=1)
advance_runners(this_play.id, num_bases=0)
if comp_play:
complete_play(this_play.id)
group_new_game = app_commands.Group(name='new-game', description='Start a new baseball game')
@group_new_game.command(name='mlb-campaign', description='Start a new MLB Campaign game against an AI')
@app_commands.describe(
sp_card_id='Light gray number to the left of the pitcher\'s name on your depth chart')
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
async def new_game_campaign_command(
self, interaction: discord.Interaction,
league: Literal['Minor League', 'Flashback', 'Major League', 'Hall of Fame'],
away_team_abbrev: str, home_team_abbrev: str, num_innings: Literal[9, 3], sp_card_id: int):
await interaction.response.defer()
conflict = get_one_game(channel_id=interaction.channel.id, active=True)
if conflict:
await interaction.edit_original_response(
content=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
try:
if interaction.channel.category.name != 'Public Fields':
await interaction.response.send_message(
f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything '
f'pops up?'
)
return
except Exception as e:
logging.error(f'Could not check channel category: {e}')
away_team = await get_team_by_abbrev(away_team_abbrev)
home_team = await get_team_by_abbrev(home_team_abbrev)
if not away_team:
await interaction.edit_original_response(
content=f'Sorry, I don\'t know who **{away_team_abbrev.upper()}** is.'
)
return
if not home_team:
await interaction.edit_original_response(
content=f'Sorry, I don\'t know who **{home_team_abbrev.upper()}** is.'
)
return
# if True in [away_team['is_ai'], home_team['is_ai']] and is_ranked:
# await interaction.edit_original_response(
# content=f'Sorry, ranked games can only be played against human teams. Run `/new-game` again with '
# f'`is_ranked` set to False to play against the '
# f'{away_team["sname"] if away_team["is_ai"] else home_team["sname"]}.'
# )
# return
for x in [away_team, home_team]:
if not x['is_ai']:
conflict = count_team_games(x['id'])
if conflict['count']:
await interaction.edit_original_response(
content=f'Ope. The {x["sname"]} are already playing over in '
f'{interaction.guild.get_channel(conflict["games"][0]["channel_id"]).mention}'
)
return
current = await db_get('current')
week_num = current['week']
# logging.debug(f'away: {away_team} / home: {home_team} / week: {week_num} / ranked: {is_ranked}')
logging.debug(f'away: {away_team} / home: {home_team} / week: {week_num}')
if not away_team['is_ai'] and not home_team['is_ai']:
logging.error(f'MLB Campaign game between {away_team["abbrev"]} and {home_team["abbrev"]} has no AI')
await interaction.edit_original_response(
content=f'I don\'t see an AI team in this MLB Campaign game. Run `/new-game mlb-campaign` again with '
f'an AI for a campaign game or `/new-game <ranked / unlimited>` for a human game.'
)
return
ai_team = away_team if away_team['is_ai'] else home_team
human_team = away_team if home_team['is_ai'] else home_team
if interaction.user.id not in [away_team['gmid'], home_team['gmid']]:
await interaction.edit_original_response(
content='You can only start a new game if you GM one of the teams.'
)
return
if 'Minor' in league:
league_name = 'minor-league'
elif 'Flashback' in league:
can_play = False
for x in interaction.user.roles:
if x.name == 'PD - Major League':
can_play = True
if not can_play:
await interaction.edit_original_response(
content=f'Ope. Looks like you haven\'t completed the Minor League campaign, yet!\n\n'
f'To play **Flashback** games, you need to defeat all 30 MLB teams in the Minor League '
f'campaign. You can see your progress with `/record`.\n\n'
f'If you have completed the Minor League campaign, go ping Cal to get your new role!')
return
league_name = 'flashback'
elif 'Major' in league:
can_play = False
for x in interaction.user.roles:
if x.name == 'PD - Major League':
can_play = True
if not can_play:
await interaction.edit_original_response(
content=f'Ope. Looks like you haven\'t received the **PD - Major League** role, yet!\n\n'
f'To play **Major League** games, you need to defeat all 30 MLB teams in the Minor League '
f'campaign. You can see your progress with `/record`.\n\n'
f'If you have completed the Minor League campaign, go ping Cal to get your new role!')
return
league_name = 'major-league'
else:
can_play = False
for x in interaction.user.roles:
if x.name == 'PD - Hall of Fame':
can_play = True
if not can_play:
await interaction.edit_original_response(
content=f'Ope. Looks like you haven\'t received the **PD - Hall of Fame** role, yet!\n\n'
f'To play **Hall of Fame** games, you need to defeat all 30 MLB teams in the Minor League '
f'and Major League campaign. You can see your progress with `/record`.\n\n'
f'If you have completed the Major League campaign, go ping Cal to get your new role!')
return
league_name = 'hall-of-fame'
this_game = post_game({
'away_team_id': away_team['id'],
'home_team_id': home_team['id'],
'week_num': week_num,
'channel_id': interaction.channel.id,
'active': True,
'is_pd': True,
'ranked': False,
'season': current['season'],
'short_game': True if num_innings == 3 else False,
'game_type': league_name
})
logging.info(
f'Game {this_game.id} ({league_name}) between {away_team_abbrev.upper()} and '
f'{home_team_abbrev.upper()} is posted!'
)
away_role = await team_role(interaction, away_team)
home_role = await team_role(interaction, home_team)
all_lineups = []
# Get Human SP
human_sp_card = await db_get(f'cards', object_id=sp_card_id)
if human_sp_card['team']['id'] != human_team['id']:
logging.error(
f'Card_id {sp_card_id} does not belong to {human_team["abbrev"]} in Game {this_game.id}'
)
patch_game(this_game.id, active=False)
await interaction.channel.send(
f'Uh oh. Card ID {sp_card_id} is {human_team["player"]["p_name"]} and belongs to '
f'{human_sp_card["team"]["sname"]}. Will you double check that before we get started?')
return
if this_game.game_type in ['major-league', 'hall-of-fame']:
l_check = await legal_check([human_sp_card['id']], 'ranked')
if not l_check['legal']:
patch_game(this_game.id, active=False)
await interaction.edit_original_response(
content=f'It looks like this is a Ranked Legal game and {player_desc(human_sp_card["player"])} is '
f'not legal in Ranked. You can start a new game once you pick a new SP.'
)
return
if this_game.game_type == 'flashback':
l_check = await legal_check([human_sp_card['id']], 'flashback')
if not l_check['legal']:
patch_game(this_game.id, active=False)
await interaction.edit_original_response(
content=f'It looks like this is a Flashback game and {player_desc(human_sp_card["player"])} is '
f'not legal in Flashback. You can start a new game once you pick a new SP.'
)
return
all_lineups.append({
'game_id': this_game.id,
'team_id': human_team['id'],
'player_id': human_sp_card['player']['player_id'],
'card_id': sp_card_id,
'position': 'P',
'batting_order': 10,
'after_play': 0
})
# Get AI Starting Pitcher
try:
await interaction.edit_original_response(
content=f'Now to decide on a Starting Pitcher...'
)
if ai_team['id'] == this_game.away_team_id:
patch_game(this_game.id, away_roster_num=69, ai_team='away')
else:
patch_game(this_game.id, home_roster_num=69, ai_team='home')
# starter = starting_pitcher(ai_team, self.bot, True if home_team['is_ai'] else False)
starter = await ai_manager.get_starting_pitcher(
ai_team,
this_game.id,
True if home_team['is_ai'] else False,
league_name
)
all_lineups.append(starter)
ai_sp = await db_get('players', object_id=starter['player_id'])
this_card = await db_get(f'cards', object_id=starter['card_id'])
await interaction.channel.send(
content=f'The {ai_team["sname"]} are starting **{player_desc(this_card["player"])}**:\n\n'
f'{player_pcard(this_card["player"])}'
)
except Exception as e:
patch_game(this_game.id, active=False)
logging.error(f'could not start an AI game with {ai_team["sname"]}: {e}')
await interaction.edit_original_response(
content=f'Looks like the {ai_team["sname"]} rotation didn\'t come through clearly. I\'ll sort '
f'this out with {ai_team["gmname"]} and {get_cal_user(interaction).mention}. I\'ll end '
f'this game - why don\'t you play against somebody else for now?'
)
return
# Get AI Lineup
try:
await interaction.edit_original_response(
content=f'I am getting a lineup card from the {ai_team["sname"]}...'
)
logging.info(f'new-game - calling lineup for {ai_team["abbrev"]}')
batters = await ai_manager.build_lineup(
ai_team, this_game.id, league_name, sp_name=ai_sp['p_name']
)
all_lineups.extend(batters)
logging.info(f'new-game - got lineup for {ai_team["abbrev"]}')
except Exception as e:
patch_game(this_game.id, active=False)
logging.error(f'could not start an AI game with {ai_team["sname"]}: {e}')
await interaction.edit_original_response(
content=f'Looks like the {ai_team["sname"]} lineup card didn\'t come through clearly. I\'ll sort '
f'this out with {ai_team["gmname"]} and {get_cal_user(interaction).mention}. I\'ll end '
f'this game - why don\'t you play against somebody else for now?'
)
return
logging.debug(f'Setting lineup for {ai_team["sname"]} in PD game')
logging.debug(f'lineups: {all_lineups}')
post_lineups(all_lineups)
await interaction.channel.send(
content=f'{away_role.mention} @ {home_role.mention} is set!\n\n'
f'Go ahead and set lineups with the `/read-lineup` command!',
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
return
@group_new_game.command(name='ranked', description='Start a new Ranked game against another human')
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
async def new_game_ranked_command(
self, interaction: discord.Interaction, away_team_abbrev: str, home_team_abbrev: str,
num_innings: Literal[9, 3]):
await interaction.response.defer()
conflict = get_one_game(channel_id=interaction.channel.id, active=True)
if conflict:
await interaction.edit_original_response(
content=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
try:
if interaction.channel.category.name != 'Public Fields':
await interaction.response.send_message(
f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything '
f'pops up?'
)
return
except Exception as e:
logging.error(f'Could not check channel category: {e}')
away_team = await get_team_by_abbrev(away_team_abbrev)
home_team = await get_team_by_abbrev(home_team_abbrev)
if not away_team:
await interaction.edit_original_response(
content=f'Sorry, I don\'t know who **{away_team_abbrev.upper()}** is.'
)
return
if not home_team:
await interaction.edit_original_response(
content=f'Sorry, I don\'t know who **{home_team_abbrev.upper()}** is.'
)
return
if away_team['is_ai'] or home_team['is_ai']:
logging.error(f'Ranked game between {away_team["abbrev"]} and {home_team["abbrev"]} has an AI')
await interaction.edit_original_response(
content=f'Only human vs human games can be ranked - run `/new-game` again and double-check the '
f'game type you want! If you have questions, feel free to post up in #paper-dynasty-chat'
)
return
for x in [away_team, home_team]:
if not x['is_ai']:
conflict = count_team_games(x['id'])
if conflict['count']:
await interaction.edit_original_response(
content=f'Ope. The {x["sname"]} are already playing over in '
f'{interaction.guild.get_channel(conflict["games"][0]["channel_id"]).mention}'
)
return
current = await db_get('current')
week_num = current['week']
logging.debug(f'away: {away_team} / home: {home_team} / week: {week_num} / ranked: True')
if interaction.user.id not in [away_team['gmid'], home_team['gmid']]:
await interaction.edit_original_response(
content='You can only start a new game if you GM one of the teams.'
)
return
this_game = post_game({
'away_team_id': away_team['id'],
'home_team_id': home_team['id'],
'week_num': week_num,
'channel_id': interaction.channel.id,
'active': True,
'is_pd': True,
'ranked': True,
'season': current['season'],
'short_game': True if num_innings == 3 else False,
'game_type': 'ranked'
})
logging.info(
f'Game {this_game.id} between {away_team_abbrev.upper()} and {home_team_abbrev.upper()} is posted!'
)
away_role = await team_role(interaction, away_team)
home_role = await team_role(interaction, home_team)
await interaction.channel.send(
content=f'{away_role.mention} @ {home_role.mention} is set!\n\n'
f'Go ahead and set lineups with the `/read-lineup` command!',
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
return
@group_new_game.command(name='unlimited', description='Start a new Unlimited game against another human')
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
async def new_game_unlimited_command(
self, interaction: discord.Interaction, away_team_abbrev: str, home_team_abbrev: str,
num_innings: Literal[9, 3]):
await interaction.response.defer()
conflict = get_one_game(channel_id=interaction.channel.id, active=True)
if conflict:
await interaction.edit_original_response(
content=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
try:
if interaction.channel.category.name != 'Public Fields':
await interaction.response.send_message(
f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything '
f'pops up?'
)
return
except Exception as e:
logging.error(f'Could not check channel category: {e}')
away_team = await get_team_by_abbrev(away_team_abbrev)
home_team = await get_team_by_abbrev(home_team_abbrev)
if not away_team:
await interaction.edit_original_response(
content=f'Sorry, I don\'t know who **{away_team_abbrev.upper()}** is.'
)
return
if not home_team:
await interaction.edit_original_response(
content=f'Sorry, I don\'t know who **{home_team_abbrev.upper()}** is.'
)
return
if away_team['is_ai'] or home_team['is_ai']:
logging.error(f'Ranked game between {away_team["abbrev"]} and {home_team["abbrev"]} has an AI')
await interaction.edit_original_response(
content=f'This command is for human v human games - run `/new-game` again and double-check the '
f'game type you want! If you have questions, feel free to post up in #paper-dynasty-chat'
)
return
for x in [away_team, home_team]:
if not x['is_ai']:
conflict = count_team_games(x['id'])
if conflict['count']:
await interaction.edit_original_response(
content=f'Ope. The {x["sname"]} are already playing over in '
f'{interaction.guild.get_channel(conflict["games"][0]["channel_id"]).mention}'
)
return
current = await db_get('current')
week_num = current['week']
logging.debug(f'away: {away_team} / home: {home_team} / week: {week_num} / ranked: True')
if interaction.user.id not in [away_team['gmid'], home_team['gmid']]:
await interaction.edit_original_response(
content='You can only start a new game if you GM one of the teams.'
)
return
this_game = post_game({
'away_team_id': away_team['id'],
'home_team_id': home_team['id'],
'week_num': week_num,
'channel_id': interaction.channel.id,
'active': True,
'is_pd': True,
'ranked': False,
'season': current['season'],
'short_game': True if num_innings == 3 else False,
'game_type': 'unlimited'
})
logging.info(
f'Game {this_game.id} between {away_team_abbrev.upper()} and {home_team_abbrev.upper()} is posted!'
)
away_role = await team_role(interaction, away_team)
home_role = await team_role(interaction, home_team)
await interaction.channel.send(
content=f'{away_role.mention} @ {home_role.mention} is set!\n\n'
f'Go ahead and set lineups with the `/read-lineup` command!',
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
return
@group_new_game.command(name='gauntlet', description='Start a new Gauntlet game against an AI')
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
async def new_game_gauntlet_command(
self, interaction: discord.Interaction, event_name: Literal['2024 Season', 'Super Ultra Championship'],
sp_card_id: int):
await interaction.response.defer()
conflict = get_one_game(channel_id=interaction.channel.id, active=True)
if conflict:
await interaction.edit_original_response(
content=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
try:
if interaction.channel.category.name != 'Public Fields':
await interaction.response.send_message(
f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything '
f'pops up?'
)
return
except Exception as e:
logging.error(f'Could not check channel category: {e}')
current = await db_get('current')
week_num = current['week']
e_query = await db_get('events', params=[("name", event_name), ("active", True)])
if e_query['count'] == 0:
await interaction.edit_original_response(
content=f'It looks like the {event_name} has ended! Cal should really remove it from this list.'
)
return
this_event = e_query['events'][0]
if interaction.user.id == 258104532423147520:
main_team = await get_team_by_abbrev('SKB')
else:
main_team = await get_team_by_owner(interaction.user.id)
team = await get_team_by_abbrev(f'Gauntlet-{main_team["abbrev"]}')
if not main_team:
await interaction.edit_original_response(
content=f'I don\'t see a team for you, yet. You can sign up with the `/newteam` command!'
)
return
if not team:
await interaction.edit_original_response(
content=f'I don\'t see an active run for you. You can get started with the `/gauntlets start` command!'
)
return
conflict = count_team_games(team['id'])
if conflict['count']:
await interaction.edit_original_response(
content=f'Ope. The {team["sname"]} are already playing over in '
f'{interaction.guild.get_channel(conflict["games"][0]["channel_id"]).mention}'
)
return
# Get Gauntlet run
r_query = await db_get(
'gauntletruns',
params=[('team_id', team['id']), ('gauntlet_id', this_event['id']), ('is_active', True)]
)
if r_query['count'] == 0:
await interaction.edit_original_response(
content=f'I don\'t see an active run for you. If you would like to start a new one, run '
f'`/gauntlets start {this_event["name"]}` and we can get you started in no time!'
)
return
this_run = r_query['runs'][0]
# If not new or after draft, create new AI game
is_home = gauntlets.is_home_team(team, this_event, this_run)
opponent = await gauntlets.get_opponent(team, this_event, this_run)
if opponent is None:
await interaction.edit_original_response(
content=f'Yike. I\'m not sure who your next opponent is. {get_cal_user(interaction)} help plz!'
)
return
else:
logging.info(f'opponent: {opponent}')
game_code = gauntlets.get_game_code(team, this_event, this_run)
this_game = post_game({
'away_team_id': opponent['id'] if is_home else team['id'],
'home_team_id': team['id'] if is_home else opponent['id'],
'week_num': week_num,
'channel_id': interaction.channel.id,
'active': True,
'is_pd': True,
'ranked': False,
'season': current['season'],
'short_game': False,
'game_type': game_code
})
logging.info(
f'Game {this_game.id} between {team["abbrev"]} and {opponent["abbrev"]} is posted!'
)
t_role = await team_role(interaction, main_team)
all_lineups = []
# Get Human SP
human_sp_card = await db_get(f'cards', object_id=sp_card_id)
if human_sp_card['team']['id'] != team['id']:
logging.error(
f'Card_id {sp_card_id} does not belong to {team["abbrev"]} in Game {this_game.id}'
)
await interaction.channel.send(
f'Uh oh. Card ID {sp_card_id} is {team["player"]["p_name"]} and belongs to '
f'{human_sp_card["team"]["sname"]}. Will you double check that before we get started?')
return
logging.info(f'Appending Human SP ({human_sp_card["player"]["p_name"]}) to all_lineups')
all_lineups.append({
'game_id': this_game.id,
'team_id': team['id'],
'player_id': human_sp_card['player']['player_id'],
'card_id': sp_card_id,
'position': 'P',
'batting_order': 10,
'after_play': 0
})
# Get AI Starting Pitcher
try:
logging.info(f'Getting SP for {opponent["abbrev"]}')
await interaction.edit_original_response(
content=f'Now to decide on a Starting Pitcher...'
)
if opponent['id'] == this_game.away_team_id:
patch_game(this_game.id, away_roster_num=69, ai_team='away')
else:
patch_game(this_game.id, home_roster_num=69, ai_team='home')
starter = await gauntlets.get_starting_pitcher(opponent, this_game, this_event, this_run)
all_lineups.append(starter)
ai_sp = await db_get('players', object_id=starter['player_id'])
this_card = await db_get(f'cards', object_id=starter['card_id'])
await interaction.channel.send(
content=f'The {opponent["sname"]} are starting **{player_desc(this_card["player"])}**:\n\n'
f'{this_card["player"]["image"]}'
)
except Exception as e:
patch_game(this_game.id, active=False)
logging.error(f'could not start an AI game with {opponent["sname"]}: {e}')
await interaction.edit_original_response(
content=f'Looks like the {opponent["sname"]} rotation didn\'t come through clearly. I\'ll sort '
f'this out with {opponent["gmname"]} and {get_cal_user(interaction).mention}. I\'ll end '
f'this game - why don\'t you play against somebody else for now?'
)
return
# Get AI Lineup
try:
await interaction.edit_original_response(
content=f'I am getting a lineup card from the {opponent["sname"]}...'
)
logging.info(f'new-game - calling lineup for {opponent["abbrev"]}')
batters = await gauntlets.build_lineup(opponent, this_game, this_event, ai_sp["p_name"])
all_lineups.extend(batters)
logging.info(f'new-game-gauntlet - got lineup for {opponent["abbrev"]}')
except Exception as e:
patch_game(this_game.id, active=False)
logging.error(f'could not start a gauntlet game with {opponent["sname"]}: {e}')
await interaction.edit_original_response(
content=f'Looks like the {opponent["sname"]} lineup card didn\'t come through clearly. I\'ll sort '
f'this out with {opponent["gmname"]} and {get_cal_user(interaction).mention}. I\'ll end '
f'this game - why don\'t you play against somebody else for now?'
)
return
logging.debug(f'Setting lineup for {opponent["sname"]} in PD Gauntlet game {game_code}')
post_lineups(all_lineups)
await interaction.channel.send(
content=f'Game {gauntlets.games_played(this_run) + 1} of the run is set!\n\n'
f'Go ahead and set lineups with the `/read-lineup` command!',
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
return
@group_new_game.command(name='exhibition', description='Start a new custom game against an AI')
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
async def new_game_exhibition_command(
self, interaction: discord.Interaction, away_team_abbrev: str, home_team_abbrev: str, sp_card_id: int,
num_innings: Literal[9, 3] = 9,
cardsets: Literal['Minor League', 'Major League', 'Hall of Fame', 'Flashback', 'Custom'] = 'Custom'):
await interaction.response.defer()
conflict = get_one_game(channel_id=interaction.channel.id, active=True)
if conflict:
await interaction.edit_original_response(
content=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
try:
if interaction.channel.category.name != 'Public Fields':
await interaction.response.send_message(
f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything '
f'pops up?'
)
return
except Exception as e:
logging.error(f'Could not check channel category: {e}')
away_team = await get_team_by_abbrev(away_team_abbrev)
home_team = await get_team_by_abbrev(home_team_abbrev)
if not away_team:
await interaction.edit_original_response(
content=f'Sorry, I don\'t know who **{away_team_abbrev.upper()}** is.'
)
return
if not home_team:
await interaction.edit_original_response(
content=f'Sorry, I don\'t know who **{home_team_abbrev.upper()}** is.'
)
return
for x in [away_team, home_team]:
if not x['is_ai']:
conflict = count_team_games(x['id'])
if conflict['count']:
await interaction.edit_original_response(
content=f'Ope. The {x["sname"]} are already playing over in '
f'{interaction.guild.get_channel(conflict["games"][0]["channel_id"]).mention}'
)
return
current = await db_get('current')
week_num = current['week']
# logging.debug(f'away: {away_team} / home: {home_team} / week: {week_num} / ranked: {is_ranked}')
logging.debug(f'away: {away_team} / home: {home_team} / week: {week_num}')
if not away_team['is_ai'] and not home_team['is_ai']:
logging.error(f'Exhibition game between {away_team["abbrev"]} and {home_team["abbrev"]} has no AI')
await interaction.edit_original_response(
content=f'I don\'t see an AI team in this Exhibition game. Run `/new-game mlb-campaign` again with '
f'an AI for a campaign game or `/new-game <ranked / unlimited>` for a human game.'
)
return
ai_team = away_team if away_team['is_ai'] else home_team
human_team = away_team if home_team['is_ai'] else home_team
if interaction.user.id not in [away_team['gmid'], home_team['gmid']]:
await interaction.edit_original_response(
content='You can only start a new game if you GM one of the teams.'
)
return
league_name = 'exhibition'
this_game = post_game({
'away_team_id': away_team['id'],
'home_team_id': home_team['id'],
'week_num': week_num,
'channel_id': interaction.channel.id,
'active': True,
'is_pd': True,
'ranked': False,
'season': current['season'],
'short_game': True if num_innings == 3 else False,
'game_type': league_name
})
logging.info(
f'Game {this_game.id} ({league_name}) between {away_team_abbrev.upper()} and '
f'{home_team_abbrev.upper()} is posted!'
)
away_role = await team_role(interaction, away_team)
home_role = await team_role(interaction, home_team)
all_lineups = [] # Get Human SP
human_sp_card = await db_get(f'cards', object_id=sp_card_id)
if human_sp_card['team']['id'] != human_team['id']:
logging.error(
f'Card_id {sp_card_id} does not belong to {human_team["abbrev"]} in Game {this_game.id}'
)
patch_game(this_game.id, active=False)
await interaction.channel.send(
f'Uh oh. Card ID {sp_card_id} is {human_sp_card["player"]["p_name"]} and belongs to '
f'{human_sp_card["team"]["sname"]}. Will you double check that before we get started?')
return
all_lineups.append({
'game_id': this_game.id,
'team_id': human_team['id'],
'player_id': human_sp_card['player']['player_id'],
'card_id': sp_card_id,
'position': 'P',
'batting_order': 10,
'after_play': 0
})
async def get_ai_sp_roster(interaction, this_game, ai_team, home_team, league_name, all_lineups):
# Get AI Starting Pitcher
try:
await interaction.channel.send(
content=f'Now to decide on a Starting Pitcher...'
)
if ai_team['id'] == this_game.away_team_id:
patch_game(this_game.id, away_roster_num=69, ai_team='away')
else:
patch_game(this_game.id, home_roster_num=69, ai_team='home')
starter = await ai_manager.get_starting_pitcher(
ai_team,
this_game.id,
True if home_team['is_ai'] else False,
league_name
)
all_lineups.append(starter)
ai_sp = await db_get('players', object_id=starter['player_id'])
this_card = await db_get(f'cards', object_id=starter['card_id'])
await interaction.channel.send(
content=f'The {ai_team["sname"]} are starting **{player_desc(this_card["player"])}**:\n\n'
f'{player_pcard(this_card["player"])}'
)
except Exception as e:
patch_game(this_game.id, active=False)
logging.error(f'could not start an AI game with {ai_team["sname"]}: {e}')
await interaction.channel.send(
content=f'Looks like the {ai_team["sname"]} rotation didn\'t come through clearly. I\'ll sort '
f'this out with {ai_team["gmname"]} and {get_cal_user(interaction).mention}. I\'ll end '
f'this game - why don\'t you play against somebody else for now?'
)
raise KeyError(f'A Starting Pitcher could not be found for the {ai_team["lname"]}.')
# Get AI Lineup
try:
await interaction.channel.send(
content=f'I am getting a lineup card from the {ai_team["sname"]}...'
)
logging.info(f'new-game - calling lineup for {ai_team["abbrev"]}')
batters = await ai_manager.build_lineup(
ai_team, this_game.id, league_name, sp_name=ai_sp['p_name']
)
all_lineups.extend(batters)
logging.info(f'new-game - got lineup for {ai_team["abbrev"]}')
except Exception as e:
patch_game(this_game.id, active=False)
logging.error(f'could not start an AI game with {ai_team["sname"]}: {e}')
await interaction.edit_original_response(
content=f'Looks like the {ai_team["sname"]} lineup card didn\'t come through clearly. I\'ll sort '
f'this out with {ai_team["gmname"]} and {get_cal_user(interaction).mention}. I\'ll end '
f'this game - why don\'t you play against somebody else for now?'
)
return
logging.debug(f'Setting lineup for {ai_team["sname"]} in PD game')
logging.debug(f'lineups: {all_lineups}')
post_lineups(all_lineups)
if cardsets in ['Minor League', 'Major League', 'Hall of Fame', 'Flashback']:
if cardsets == 'Minor League':
cardset_ids = '17,8'
backup_cardset_ids = '13'
elif cardsets == 'Major League':
cardset_ids = '17,18,13,11,7,8'
backup_cardset_ids = '9,3'
elif cardsets == 'Hall of Fame':
all_c = [str(x) for x in range(1, 20)]
cardset_ids = f'{",".join(all_c)}'
backup_cardset_ids = None
else:
# Flashback cardsets
cardset_ids = '11,7,6,12'
backup_cardset_ids = '13,5'
this_game = patch_game(this_game.id, cardset_ids=cardset_ids, backup_cardset_ids=backup_cardset_ids)
await get_ai_sp_roster(interaction, this_game, ai_team, home_team, league_name, all_lineups)
await interaction.channel.send(
content=f'{away_role.mention} @ {home_role.mention} is set!\n\n'
f'Go ahead and set lineups with the `/read-lineup` command!',
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
else:
async def my_callback(interaction: discord.Interaction, values):
cardset_ids = ','.join(values)
patch_game(this_game.id, cardset_ids=cardset_ids)
# await interaction.response.send_message(
# f'Your selection{"s are" if len(values) > 1 else " is"}: {", ".join(values)}')
await get_ai_sp_roster(interaction, this_game, ai_team, home_team, league_name, all_lineups)
await interaction.channel.send(
content=f'{away_role.mention} @ {home_role.mention} is set!\n\n'
f'Go ahead and set lineups with the `/read-lineup` command!',
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
my_dropdown = Dropdown(
option_list=SELECT_CARDSET_OPTIONS,
placeholder='Select up to 8 cardsets to include',
callback=my_callback,
max_values=len(SELECT_CARDSET_OPTIONS)
)
view = DropdownView([my_dropdown])
await interaction.edit_original_response(
content=None,
view=view
)
return
@commands.command(name='force-endgame', help='Mod: Force a game to end without stats')
@commands.is_owner()
async def force_end_game_command(self, ctx: commands.Context):
this_game = get_one_game(channel_id=ctx.channel.id, active=True)
try:
await ctx.send(content=None, embed=await self.initialize_play_plus_embed(this_game))
except Exception as e:
logging.error(f'could not post game state embed: {e}')
question = await ctx.send(
f'Something is very borked here and I can\'t post the embed. Imma nuke this game now...'
)
patch_game(this_game.id, active=False)
await question.edit(content='Done and dusted.', view=None)
return
view = Confirm(responders=[ctx.author], timeout=60, label_type='yes')
question = await ctx.send(f'Should I nuke this game?', view=view)
await view.wait()
if view.value:
patch_game(this_game.id, active=False)
await question.edit(content='It\'s gone.', view=None)
else:
await question.edit(content='It stays.', view=None)
@commands.command(name='check-decisions', help='Mod: Calculate pitching decisions of current game')
@commands.is_owner()
async def check_decisions_command(self, ctx: commands.Context):
this_game = get_one_game(channel_id=ctx.channel.id, active=True)
get_pitching_decisions(this_game, this_game.id)
await ctx.send(random_conf_gif())
@app_commands.command(name='end-game', description='End game in this channel')
@app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME)
async def end_game_command(self, interaction: discord.Interaction):
await interaction.response.defer()
this_game = get_one_game(channel_id=interaction.channel.id, active=True)
if not this_game:
await interaction.edit_original_response(content='Ope, I don\'t see a game in this channel.')
return
logging.info(f'Ending Game {this_game.id}')
response = await interaction.edit_original_response(content=f'Let\'s see what we\'ve got here...')
owner_team = await get_game_team(this_game, interaction.user.id)
gauntlet_team = None
if 'gauntlet' in this_game.game_type:
gauntlet_team = await get_game_team(this_game, team_abbrev=f'Gauntlet-{owner_team["abbrev"]}')
if not {owner_team['id'], gauntlet_team['id']}.intersection(
[this_game.away_team_id, this_game.home_team_id]):
await interaction.edit_original_response(content='Bruh. Only GMs of the active teams can end games.')
return
elif not owner_team['id'] in [this_game.away_team_id, this_game.home_team_id] and \
interaction.user.id != self.bot.owner_id:
await interaction.edit_original_response(content='Bruh. Only GMs of the active teams can end games.')
return
latest_play = get_latest_play(this_game.id)
if latest_play is None:
await self.post_stratgame(this_game, forfeit=True)
await send_to_channel(
self.bot, 'pd-network-news',
f'The **{owner_team["lname"]}** made an oopsie-poopsie and had to call off their game down in '
f'{interaction.channel.mention}')
this_game = patch_game(this_game.id, active=False)
await interaction.edit_original_response(
content='Roger dodger - it is game over.'
)
return
valid_end = False
logging.debug(f'latest play: {latest_play}')
if not this_game.short_game:
if latest_play.starting_outs == 0:
logging.debug(f'no outs')
if latest_play.inning_half.lower() == 'top':
logging.debug(f'top of inning')
if latest_play.inning_num > 9 and latest_play.away_score != latest_play.home_score:
logging.debug(f'after the ninth and not tied')
valid_end = True
if abs(latest_play.home_score - latest_play.away_score) >= 10:
logging.debug(f'not after ninth, but mercy')
valid_end = True
if latest_play.inning_half.lower() == 'bot':
if (latest_play.home_score > latest_play.away_score) and latest_play.inning_num >= 9:
logging.debug(f'bottom half and home team winning')
valid_end = True
if abs(latest_play.home_score - latest_play.away_score) >= 10:
logging.debug(f'bottom half and mercy')
valid_end = True
elif abs(latest_play.home_score - latest_play.away_score) >= 10 and latest_play.inning_half.lower() == 'bot':
logging.info(f'bottom half and it is a mercy')
valid_end = True
elif latest_play.inning_num >= 9 and latest_play.inning_half.lower() == 'bot' and \
latest_play.home_score > latest_play.away_score:
valid_end = True
else:
if latest_play.starting_outs == 0:
logging.debug(f'no outs')
if latest_play.inning_half.lower() == 'top':
logging.debug(f'top of inning')
if latest_play.inning_num > 3 and latest_play.away_score != latest_play.home_score:
logging.debug(f'after the ninth and not tied')
valid_end = True
if abs(latest_play.home_score - latest_play.away_score) >= 10:
logging.debug(f'not after ninth, but mercy')
valid_end = True
if latest_play.inning_half.lower() == 'bot':
if (latest_play.home_score > latest_play.away_score) and latest_play.inning_num >= 3:
logging.debug(f'bottom half and home team winning')
valid_end = True
if abs(latest_play.home_score - latest_play.away_score) >= 10:
logging.debug(f'bottom half and mercy')
valid_end = True
elif abs(latest_play.home_score - latest_play.away_score) >= 10 and latest_play.inning_half.lower() == 'bot':
logging.info(f'bottom half and it is a mercy')
valid_end = True
elif latest_play.inning_num >= 3 and latest_play.inning_half.lower() == 'bot' and \
latest_play.home_score > latest_play.away_score:
valid_end = True
# valid_end = True # TODO: REMOVE THIS BEFORE GO-LIVE
if not valid_end:
view = Confirm(responders=[interaction.user])
question = await interaction.channel.send(
'It doesn\'t look like this game is over, yet. I can end it, but no rewards will be paid out and '
'you will take the L.\n\n'
'Should I end this game?',
view=view
)
await view.wait()
if view.value:
await question.delete()# New database driven stat submission
failure = False
final_game = await self.post_stratgame(this_game)
# Send Plays to db
try:
resp = await self.post_allplays(this_game, final_game['id'])
if not resp:
failure = True
except Exception as e:
logging.error(f'end-game - Could not post plays: {e}')
failure = True
# Send Decisions to db
try:
resp = await self.post_decisions(this_game, final_game['id'])
if not resp:
failure = True
except Exception as e:
logging.error(f'end-game - Could not post decisions: {e}')
failure = True
if failure:
try:
await db_delete(f'decisions/game', object_id=final_game["id"])
except Exception as e:
logging.error(f'could not delete decisions')
try:
await db_delete(f'plays/game', object_id=final_game["id"])
except Exception as e:
logging.error(f'could not delete plays')
try:
await db_delete(f'games', object_id=final_game["id"])
except Exception as e:
logging.error(f'could not delete game')
await interaction.channel.send(
content=f'That did not go well and I wasn\'t able to submit this game. I recommend pinging Cal so he '
f'can fix this.'
)
return
await send_to_channel(
self.bot, 'pd-network-news',
f'The **{owner_team["lname"]}** had to cut out early from their game down in '
f'{interaction.channel.mention}')
this_game = patch_game(this_game.id, active=False)
await interaction.edit_original_response(
content='Roger dodger - it is game over.'
)
return
else:
await question.edit(
content='It stays.', view=None
)
return
await response.edit(content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False))
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.edit_original_response(content=f'Should I end this game?', view=view)
await view.wait()
if view.value:
await question.edit(content='I\'ll tally the scorecard now...', view=None)
else:
await question.edit(content='It stays.', view=None)
return
# New database driven stat submission
failure = False
final_game = await self.post_stratgame(this_game)
# Send Plays to db
try:
resp = await self.post_allplays(this_game, final_game['id'])
if not resp:
failure = True
except Exception as e:
logging.error(f'end-game - Could not post plays: {e}')
failure = True
# Send Decisions to db
try:
resp = await self.post_decisions(this_game, final_game['id'])
if not resp:
failure = True
except Exception as e:
logging.error(f'end-game - Could not post decisions: {e}')
failure = True
if failure:
try:
await db_delete(f'decisions/game', object_id=final_game["id"])
except Exception as e:
logging.error(f'could not delete decisions')
try:
await db_delete(f'plays/game', object_id=final_game["id"])
except Exception as e:
logging.error(f'could not delete plays')
try:
await db_delete(f'games', object_id=final_game["id"])
except Exception as e:
logging.error(f'could not delete game')
await interaction.channel.send(
content=f'That did not go well and I wasn\'t able to submit this game. I recommend pinging Cal so he '
f'can fix this.'
)
return
gs = await db_get(f'plays/game-summary/{final_game["id"]}')
await db_patch('games', object_id=gs['game']['id'],
params=[('away_score', gs['runs']['away']), ('home_score', gs['runs']['home'])])
away_team = gs['teams']['away']
home_team = gs['teams']['home']
winning_team = away_team if latest_play.away_score > latest_play.home_score else home_team
losing_team = away_team if latest_play.away_score < latest_play.home_score else home_team
# Post Game Rewards
r_data = await self.post_rewards(winning_team, losing_team, this_game)
win_reward = r_data['win_string']
loss_reward = r_data['loss_string']
# Post a notification to PD
logging.debug(f'getting inning')
inning = f'{latest_play.inning_num if latest_play.inning_half == "Bot" else latest_play.inning_num - 1}'
embed = get_team_embed(
f'{away_team["lname"]} {latest_play.away_score} @ {latest_play.home_score} {home_team["lname"]} - F/'
f'{inning}',
winning_team
)
logging.debug(f'setting location')
embed.add_field(
name='Location',
value=f'{interaction.guild.get_channel(this_game.channel_id).mention}'
)
embed.add_field(name='Game ID', value=f'{final_game["id"]}')
logging.debug(f'getting league name')
if this_game.game_type == 'major-league':
game_des = 'Major League'
elif this_game.game_type == 'minor-league':
game_des = 'Minor League'
elif this_game.game_type == 'hall-of-fame':
game_des = 'Hall of Fame'
elif this_game.game_type == 'flashback':
game_des = 'Flashback'
elif this_game.ranked:
game_des = 'Ranked'
elif 'gauntlet' in this_game.game_type:
game_des = 'Gauntlet'
else:
game_des = 'Unlimited'
embed.description = f'Score Report - {game_des} ' \
f'{"- 3-Inning Game" if this_game.short_game else " - 9-Inning Game"}'
logging.debug(f'building box score')
embed.add_field(
name='Box Score',
value=f'```\n'
f'Team | R | H | E |\n'
f'{away_team["abbrev"].replace("Gauntlet-", ""): <4} | {gs["runs"]["away"]: >2} | '
f'{gs["hits"]["away"]: >2} | {gs["errors"]["away"]: >2} |\n'
f'{home_team["abbrev"].replace("Gauntlet-", ""): <4} | {gs["runs"]["home"]: >2} | '
f'{gs["hits"]["home"]: >2} | {gs["errors"]["home"]: >2} |\n'
f'\n```',
inline=False
)
logging.debug(f'getting potg string')
tp = gs["top-players"][0]
potg_string = f'{player_desc(tp["player"])} - '
if 'hr' in tp:
potg_string += f'{tp["hit"]}-{tp["ab"]}'
if tp['hr'] > 0:
num = f'{tp["hr"]} ' if tp["hr"] > 1 else ""
potg_string += f', {num}HR'
if tp['triple'] > 0:
num = f'{tp["triple"]} ' if tp["triple"] > 1 else ""
potg_string += f', {num}3B'
if tp['double'] > 0:
num = f'{tp["double"]} ' if tp["double"] > 1 else ""
potg_string += f', {num}2B'
if tp['run'] > 0:
potg_string += f', {tp["run"]} R'
if tp['rbi'] > 0:
potg_string += f', {tp["rbi"]} RBI'
else:
potg_string = f'{player_desc(tp["player"])} - {tp["ip"]} IP, {tp["run"]} R'
if tp['run'] != tp['e_run']:
potg_string += f' ({tp["e_run"]} ER)'
potg_string += f', {tp["hit"]} H, {tp["so"]} K'
potg_string += f', {tp["re24"]:.2f} re24'
embed.add_field(
name='Player of the Game',
value=potg_string,
inline=False
)
logging.info(f'potg: {potg_string}')
logging.debug(f'getting pitcher string')
pit_string = f'Win: {gs["pitchers"]["win"]["p_name"]}\n' \
f'Loss: {gs["pitchers"]["loss"]["p_name"]}\n'
if gs['pitchers']['save'] is not None:
pit_string += f'Save: {player_desc(gs["pitchers"]["save"])}'
embed.add_field(
name=f'Pitching',
value=pit_string,
)
def name_list(raw_list: list) -> str:
logging.info(f'raw_list: {raw_list}')
player_dict = {}
for x in raw_list:
if x['player_id'] not in player_dict:
player_dict[x['player_id']] = x
data_dict = {}
for x in raw_list:
if x['player_id'] not in data_dict:
data_dict[x['player_id']] = 1
else:
data_dict[x['player_id']] += 1
r_string = ''
logging.info(f'players: {player_dict} / data: {data_dict}')
first = True
for p_id in data_dict:
r_string += f'{", " if not first else ""}{player_dict[p_id]["p_name"]}'
if data_dict[p_id] > 1:
r_string += f' {data_dict[p_id]}'
first = False
return r_string
logging.info(f'getting running string')
if len(gs['running']['sb']) + len(gs['running']['csc']) > 0:
run_string = ''
if len(gs['running']['sb']) > 0:
run_string += f'SB: {name_list(gs["running"]["sb"])}\n'
if len(gs['running']['csc']) > 0:
run_string += f'CSc: {name_list(gs["running"]["csc"])}'
embed.add_field(
name=f'Baserunning',
value=run_string
)
logging.info(f'getting xbh string')
if len(gs['xbh']['2b']) + len(gs['xbh']['3b']) + len(gs['xbh']['hr']) > 0:
bat_string = ''
if len(gs['xbh']['2b']) > 0:
bat_string += f'2B: {name_list(gs["xbh"]["2b"])}\n'
if len(gs['xbh']['3b']) > 0:
bat_string += f'3B: {name_list(gs["xbh"]["3b"])}\n'
if len(gs['xbh']['hr']) > 0:
bat_string += f'HR: {name_list(gs["xbh"]["hr"])}\n'
else:
bat_string = 'Oops! All bitches! No XBH from either team.'
logging.info(f'building embed')
embed.add_field(
name='Batting',
value=bat_string,
inline=False
)
embed.add_field(
name=f'{winning_team["abbrev"]} Rewards',
value=win_reward
)
embed.add_field(
name=f'{losing_team["abbrev"]} Rewards',
value=loss_reward
)
embed.add_field(
name='Highlights',
value=f'Please share the highlights in {get_channel(interaction, "pd-news-ticker").mention}!',
inline=False
)
logging.info(f'sending scorebug')
await send_to_channel(self.bot, 'pd-network-news', embed=embed)
# Gauntlet results and reward
if gauntlet_team is not None:
await gauntlets.post_result(
int(this_game.game_type.split('-')[3]),
is_win=winning_team['gmid'] == gauntlet_team['gmid'],
this_team=gauntlet_team,
bot=self.bot,
channel=interaction.channel
)
this_run = await db_get('gauntletruns', object_id=int(this_game.game_type.split('-')[3]))
if this_run['losses'] == 2:
await send_to_channel(
bot=self.bot,
channel_name='pd-network-news',
content=f'The {gauntlet_team["lname"]} won {this_run["wins"]} games before failing in the '
f'{this_run["gauntlet"]["name"]} gauntlet.',
embed=None
)
patch_game(this_game.id, active=False)
logging.info(f'Game {this_game.id} is complete')
if gauntlet_team is None:
await interaction.channel.send(
content=f'Good game! Go share the highlights in '
f'{get_channel(interaction, "pd-news-ticker").mention}!'
)
"""
END OF THE NEW GAME END
"""
# away_team = await db_get('teams', object_id=this_game.away_team_id)
# home_team = await db_get('teams', object_id=this_game.home_team_id)
#
# away_stats = {
# # 'p_lines': get_pitching_stats(this_game.id, team_id=away_team['id']),
# 'p_lines': [],
# 'b_lines': get_batting_stats(this_game.id, team_id=away_team['id']),
# 'f_lines': get_fielding_stats(this_game.id, team_id=away_team['id'])
# }
# home_stats = {
# # 'p_lines': get_pitching_stats(this_game.id, team_id=home_team['id']),
# 'p_lines': [],
# 'b_lines': get_batting_stats(this_game.id, team_id=home_team['id']),
# 'f_lines': get_fielding_stats(this_game.id, team_id=home_team['id']),
# # 'score': away_stats['p_lines'][0]['tm_runs']
# }
#
# logging.debug(f'away_stats: {away_stats}\n\nhome_stats: {home_stats}')
#
# away_pitchers = await get_team_lineups(
# this_game.id, team_id=away_team['id'], inc_inactive=True, pitchers_only=True, as_string=False
# )
# for line in away_pitchers:
# try:
# # logging.info(f'line: {line}')
# this_stats = get_pitching_stats(this_game.id, lineup_id=line.id)
# # logging.info(f'away / this_stats: {this_stats}')
# away_stats['p_lines'].extend(this_stats)
# if 'score' not in home_stats:
# # logging.info(f'score not in home_stats')
# home_stats['score'] = this_stats[0]['pl_runs']
# else:
# # logging.info(f'score is in home_stats')
# home_stats['score'] += this_stats[0]['pl_runs']
# if 'hits' not in home_stats:
# # logging.info(f'hits not in home_stats')
# home_stats['hits'] = this_stats[0]['pl_hit']
# else:
# # logging.info(f'hits is in home_stats')
# home_stats['hits'] += this_stats[0]['pl_hit']
# except Exception as e:
# bad_player = await get_player(this_game, line)
# logging.error(f'Unable to process stats for card_id {line.card_id} in Game {this_game.id}: '
# f'{type(e)}: {e}')
# await interaction.edit_original_response(
# content=f'I was not able to process stats for {bad_player["name"]} - '
# f'{get_cal_user(interaction).mention} help!')
#
# home_pitchers = await get_team_lineups(
# this_game.id, team_id=home_team['id'], inc_inactive=True, pitchers_only=True, as_string=False
# )
# for line in home_pitchers:
# try:
# # logging.info(f'line: {line}')
# this_stats = get_pitching_stats(this_game.id, lineup_id=line.id)
# # logging.info(f'home / this_stats: {this_stats}')
# home_stats['p_lines'].extend(this_stats)
# if 'score' not in away_stats:
# # logging.info(f'score not in away_stats')
# away_stats['score'] = this_stats[0]['pl_runs']
# else:
# # logging.info(f'score is in away_stats')
# away_stats['score'] += this_stats[0]['pl_runs']
# if 'hits' not in away_stats:
# # logging.info(f'hits not in away_stats')
# away_stats['hits'] = this_stats[0]['pl_hit']
# else:
# # logging.info(f'hits is in away_stats')
# away_stats['hits'] += this_stats[0]['pl_hit']
# except Exception as e:
# bad_player = await get_player(this_game, line)
# logging.error(f'Unable to process stats for card_id {line.card_id} in Game {this_game.id}: '
# f'{type(e)}: {e}')
# await interaction.edit_original_response(
# content=f'I was not able to process stats for {bad_player["name"]} - '
# f'{get_cal_user(interaction).mention} help!'
# )
#
# logging.debug(f'finished tallying pitcher stats')
#
# # away_stats['score'] = home_stats['p_lines'][0]['tm_runs']
# try:
# decisions = get_pitching_decisions(this_game)
# except AttributeError as e:
# logging.error(f'Could not pull decisions for Game {this_game.id}: {e}')
# await interaction.edit_original_response(
# content=f'I was not able to calculate the Winning and Losing Pitcher for this game. Is the game '
# f'ending early? {get_cal_user(interaction).mention} can probably help.'
# )
# return
# logging.debug(f'decisions: {decisions}')
#
# winning_team = away_team if away_stats['score'] > home_stats['score'] else home_team
# losing_team = away_team if away_stats['score'] < home_stats['score'] else home_team
#
# # Post Game Rewards
# r_data = await self.post_rewards(winning_team, losing_team, this_game)
# win_reward = r_data['win_string']
# loss_reward = r_data['loss_string']
#
# # Check for Gauntlet game so rewards go to main team
# if gauntlet_team is not None:
# if winning_team['gmid'] == gauntlet_team['gmid']:
# winning_team = owner_team
# else:
# losing_team = owner_team
#
# logging.debug(f'away_stats (in /endgame function)\n\n{away_stats}')
# logging.debug(f'home_stats (in /endgame function)\n\n{home_stats}')
# logging.debug(f'winning_team: {winning_team}\nlosing_team: {losing_team}')
#
# logging.debug(f'Time to build statlines and submit the scorecard for this PD game!')
# # Post result
# success = await db_post(
# 'results',
# payload={
# 'away_team_id': this_game.away_team_id,
# 'home_team_id': this_game.home_team_id,
# 'away_score': away_stats['score'],
# 'home_score': home_stats['score'],
# 'away_team_ranking': away_team['ranking'],
# 'home_team_ranking': home_team['ranking'],
# 'scorecard': f'Bot Game {this_game.id}',
# 'week': this_game.week_num,
# 'season': this_game.season,
# 'ranked': this_game.ranked,
# 'short_game': this_game.short_game,
# 'game_type': this_game.game_type
# }
# )
# # Submit the stats
# batter_stats = []
# pitcher_stats = []
# doubles = ''
# triples = ''
# homers = ''
# s_bases = ''
# caught_s = ''
#
# for line in [*away_stats['b_lines'], *home_stats['b_lines']]:
# if line['pl_double']:
# if len(doubles):
# doubles += ', '
# card = await db_get("cards", object_id=line["card_id"])
# doubles += f'{card["player"]["p_name"]}' \
# f'{" " if line["pl_double"] > 1 else ""}' \
# f'{line["pl_double"] if line["pl_double"] > 1 else ""}'
# if line['pl_triple']:
# if len(triples):
# triples += ', '
# card = await db_get("cards", object_id=line["card_id"])
# triples += f'{card["player"]["p_name"]}' \
# f'{" " if line["pl_triple"] > 1 else ""}' \
# f'{line["pl_triple"] if line["pl_triple"] > 1 else ""}'
# if line['pl_homerun']:
# if len(homers):
# homers += ', '
# card = await db_get("cards", object_id=line["card_id"])
# homers += f'{card["player"]["p_name"]}' \
# f'{" " if line["pl_homerun"] > 1 else ""}' \
# f'{line["pl_homerun"] if line["pl_homerun"] > 1 else ""}'
# if line['pl_sb']:
# if len(s_bases):
# s_bases += ', '
# card = await db_get("cards", object_id=line["card_id"])
# s_bases += f'{card["player"]["p_name"]}' \
# f'{" " if line["pl_sb"] > 1 else ""}' \
# f'{line["pl_sb"] if line["pl_sb"] > 1 else ""}'
# batter_stats.append(
# {
# 'card_id': line['card_id'],
# 'team_id': line['team_id'],
# 'roster_num':
# this_game.away_roster_num if this_game.away_team_id == line['team_id']
# else this_game.home_roster_num,
# 'vs_team_id':
# home_team['id'] if this_game.away_team_id == line['team_id']
# else away_team['id'],
# 'pos': line['pos'],
# 'pa': line['pl_pa'],
# 'ab': line['pl_ab'],
# 'run': line['pl_run'],
# 'rbi': line['pl_rbi'],
# 'hit': line['pl_hit'],
# 'double': line['pl_double'],
# 'triple': line['pl_triple'],
# 'hr': line['pl_homerun'],
# 'bb': line['pl_bb'],
# 'so': line['pl_so'],
# 'hbp': line['pl_hbp'],
# 'sac': line['pl_sac'],
# 'ibb': line['pl_ibb'],
# 'gidp': line['pl_gidp'],
# 'sb': line['pl_sb'],
# 'cs': line['pl_cs'],
# 'bphr': line['pl_bphr'],
# 'bpfo': line['pl_bpfo'],
# 'bp1b': line['pl_bp1b'],
# 'bplo': line['pl_bplo'],
# 'week': this_game.week_num,
# 'season': this_game.season,
# 'game_id': this_game.id,
# }
# )
#
# for line in [*away_stats['f_lines'], *home_stats['f_lines']]:
# if line['pl_csc']:
# if len(caught_s):
# caught_s += ', '
# card = await db_get("cards", object_id=line["card_id"])
# caught_s += f'{card["player"]["p_name"]}' \
# f'{" " if line["pl_csc"] > 1 else ""}' \
# f'{line["pl_csc"] if line["pl_csc"] > 1 else ""}'
# batter_stats.append(
# {
# 'card_id': line['card_id'],
# 'team_id': line['team_id'],
# 'roster_num':
# this_game.away_roster_num if this_game.away_team_id == line['team_id']
# else this_game.home_roster_num,
# 'vs_team_id':
# home_team['id'] if this_game.away_team_id == line['team_id']
# else away_team['id'],
# 'pos': line['pos'],
# 'xch': line['pl_xch'],
# 'xhit': line['pl_xhit'],
# 'error': line['pl_error'],
# 'pb': line['pl_pb'],
# 'sbc': line['pl_sbc'],
# 'csc': line['pl_csc'],
# 'week': this_game.week_num,
# 'season': this_game.season,
# 'game_id': this_game.id
# }
# )
#
# for line in [*away_stats['p_lines'], *home_stats['p_lines']]:
# pitcher_stats.append(
# {
# 'card_id': line['card_id'],
# 'team_id': line['team_id'],
# 'roster_num':
# this_game.away_roster_num if this_game.away_team_id == line['team_id']
# else this_game.home_roster_num,
# 'vs_team_id':
# home_team['id'] if this_game.away_team_id == line['team_id']
# else away_team['id'],
# 'ip': (math.floor(line['pl_outs'] / 3) * 1.0) + ((line['pl_outs'] % 3) / 3.0),
# 'hit': line['pl_hit'],
# 'run': line['pl_runs'],
# 'erun': line['pl_eruns'],
# 'so': line['pl_so'],
# 'bb': line['pl_bb'],
# 'hbp': line['pl_hbp'],
# 'wp': line['pl_wild_pitch'],
# 'balk': line['pl_balk'],
# 'hr': line['pl_homerun'],
# 'ir': 0,
# 'irs': 0,
# 'gs': 1 if line['card_id'] in decisions['starters'] else 0,
# 'win': 1 if line['card_id'] == decisions['winner'] else 0,
# 'loss': 1 if line['card_id'] == decisions['loser'] else 0,
# 'hold': 1 if line['card_id'] in decisions['holds'] else 0,
# 'sv': 1 if line['card_id'] == decisions['save'] else 0,
# 'bsv': 1 if line['card_id'] in decisions['b_save'] else 0,
# 'week': this_game.week_num,
# 'season': this_game.season,
# 'game_id': this_game.id
# }
# )
#
# doubles += '\n' if len(doubles) else ''
# triples += '\n' if len(triples) else ''
# homers += '\n' if len(homers) else ''
# s_bases += '\n' if len(s_bases) else ''
# caught_s += '\n' if len(caught_s) else ''
#
# await db_post('batstats', payload={'stats': batter_stats})
# await db_post('pitstats', payload={'stats': pitcher_stats})
#
# # Post a notification to PD
# last_play = get_current_play(this_game.id)
# inning = f'{last_play.inning_num if last_play.inning_half == "Bot" else last_play.inning_num - 1}'
# embed = get_team_embed(
# f'{away_team["lname"]} {away_stats["score"]} @ {home_stats["score"]} {home_team["lname"]} - F/'
# f'{inning}',
# winning_team
# )
#
# if this_game.game_type == 'major-league':
# game_des = 'Major League'
# elif this_game.game_type == 'minor-league':
# game_des = 'Minor League'
# elif this_game.game_type == 'hall-of-fame':
# game_des = 'Hall of Fame'
# elif this_game.ranked:
# game_des = 'Ranked'
# elif 'gauntlet' in this_game.game_type:
# game_des = 'Gauntlet'
# else:
# game_des = 'Unlimited'
# embed.description = f'Score Report - {game_des} ' \
# f'{"- 3-Inning Game" if this_game.short_game else " - 9-Inning Game"}'
# embed.add_field(
# name='Box Score',
# value=get_final_scorebug(away_team, home_team, away_stats, home_stats),
# inline=False
# )
# embed.add_field(
# name='Location',
# value=f'{interaction.guild.get_channel(this_game.channel_id).mention}'
# )
# wc_query = await db_get("cards", object_id=decisions["winner"])
# lc_query = await db_get("cards", object_id=decisions["loser"])
# if decisions["save"]:
# sv_query = await db_get("cards", object_id=decisions["save"])
# embed.add_field(
# name='Pitching',
# value=f'Win: {wc_query["player"]["p_name"]}\n'
# f'Loss: {lc_query["player"]["p_name"]}\n'
# f'{"Save: " if decisions["save"] else ""}'
# f'{sv_query["player"]["p_name"] if decisions["save"] else ""}',
# inline=False
# )
# if len(doubles) + len(triples) + len(homers) > 0:
# embed.add_field(
# name='Batting',
# value=f'{"2B: " if len(doubles) else ""}{doubles if len(doubles) else ""}'
# f'{"3B: " if len(triples) else ""}{triples if len(triples) else ""}'
# f'{"HR: " if len(homers) else ""}{homers if len(homers) else ""}',
# inline=False
# )
# if len(s_bases) + len(caught_s) > 0:
# embed.add_field(
# name='Baserunning',
# value=f'{"SB: " if len(s_bases) else ""}{s_bases if len(s_bases) else ""}'
# f'{"CSc: " if len(caught_s) else ""}{caught_s if len(caught_s) else ""}',
# inline=False
# )
# embed.add_field(
# name=f'{winning_team["abbrev"]} Rewards',
# value=win_reward
# )
# embed.add_field(
# name=f'{losing_team["abbrev"]} Rewards',
# value=loss_reward
# )
# embed.add_field(
# name='Highlights',
# value=f'Please share the highlights in {get_channel(interaction, "pd-news-ticker").mention}!',
# inline=False
# )
# await send_to_channel(self.bot, 'pd-network-news', embed=embed)
#
# # Gauntlet results and reward
# if gauntlet_team is not None:
# await gauntlets.post_result(
# int(this_game.game_type.split('-')[3]),
# is_win=winning_team['gmid'] == gauntlet_team['gmid'],
# this_team=gauntlet_team,
# bot=self.bot,
# channel=interaction.channel
# )
#
# this_run = await db_get('gauntletruns', object_id=int(this_game.game_type.split('-')[3]))
# if this_run['losses'] == 2:
# await send_to_channel(
# bot=self.bot,
# channel_name='pd-network-news',
# content=f'The {gauntlet_team["lname"]} won {this_run["wins"]} games before failing in the '
# f'{this_run["gauntlet"]["name"]} gauntlet.',
# embed=None
# )
#
# patch_game(this_game.id, active=False)
#
# logging.info(f'Game {this_game.id} is complete')
# if gauntlet_team is None:
# await interaction.edit_original_response(
# content=f'Good game! Go share the highlights in '
# f'{get_channel(interaction, "pd-news-ticker").mention}!'
# )
@app_commands.command(
name='read-lineup',
description='Import a saved lineup directly from the team sheet for PD games'
)
@app_commands.describe(
roster='Which roster to pull from your sheet?',
lineup='Which handedness lineup are you using?'
)
@app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME)
async def read_lineup_command(
self, interaction: discord.Interaction, roster: Literal['Primary', 'Secondary', 'Ranked'],
lineup: Literal['v Right', 'v Left']):
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
if not this_game.is_pd:
await interaction.response.send_message(f'Lineup imports are only supported for PD games right now.'
f'Please use the `/setlineup` command.')
return
lineup_team = await get_game_team(this_game, interaction.user.id)
logging.debug(f'read_lineup_command - lineup_team: {lineup_team}')
if 'gauntlet' in this_game.game_type:
lineup_team = await get_game_team(this_game, team_abbrev=f'Gauntlet-{lineup_team["abbrev"]}')
if not lineup_team['id'] in [this_game.away_team_id, this_game.home_team_id]:
logging.info(f'{interaction.user.display_name} tried to run a command in Game {this_game.id} when they '
f'aren\'t a GM in the game.')
await interaction.edit_original_response(content='Bruh. Only GMs of the active teams can pull lineups.')
return
existing_lineups = await get_team_lineups(this_game.id, lineup_team['id'], as_string=False)
logging.debug(f'read_lineup_command - existing_lineups:\n{existing_lineups}')
if len(existing_lineups) > 1:
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.send_message(f'Let\'s put this lineup card together...')
if lineup == 'v Right':
l_num = 1
else:
l_num = 2
if roster == 'Primary':
roster_num = 1
elif roster == 'Secondary':
roster_num = 2
else:
roster_num = 3
lineup_cells = get_roster_lineups(lineup_team, self.bot, roster_num, l_num)
all_lineups = []
all_pos = []
card_ids = []
for index, row in enumerate(lineup_cells):
if '' in row:
break
if row[0].upper() not in all_pos:
all_pos.append(row[0].upper())
else:
raise SyntaxError(f'You have more than one {row[0].upper()} in this lineup. Please '
f'update and set the lineup again.')
this_card = await db_get(f'cards', object_id=int(row[1]))
if this_card is None:
raise LookupError(
f'Your {row[0].upper()} has a Card ID or {int(row[1])} and I cannot find that card. Did you sell '
f'that card by chance? Or did you sell a duplicate and the bot sold the one you were using?'
)
if this_card['team']['id'] != lineup_team['id']:
raise SyntaxError(f'Easy there, champ. Looks like card ID {row[1]} belongs to the '
f'{this_card["team"]["sname"]}. Try again with only cards you own.')
player_id = this_card['player']['player_id']
card_id = row[1]
card_ids.append(str(card_id))
this_lineup = {
'game_id': this_game.id,
'team_id': lineup_team['id'],
'player_id': player_id,
'card_id': card_id,
'position': row[0].upper(),
'batting_order': index + 1,
'after_play': 0
}
all_lineups.append(this_lineup)
if lineup_team['id'] == this_game.away_team_id:
patch_game(this_game.id, away_roster_num=roster_num)
else:
patch_game(this_game.id, home_roster_num=roster_num)
# Check roster legality
if this_game.game_type in ['major-league', 'hall-of-fame']:
l_check = await legal_check(card_ids, 'ranked')
if not l_check['legal']:
await interaction.edit_original_response(
content=f'It looks like this is a Ranked Legal game and I see the following cards as illegal. '
f'Please adjust your lineup and re-submit!\n\n'
f'- {l_check["error_string"]}'
)
return
if this_game.game_type == 'flashback':
l_check = await legal_check(card_ids, 'flashback')
if not l_check['legal']:
patch_game(this_game.id, active=False)
await interaction.edit_original_response(
content=f'It looks like this is a Flashback game and I see the following cards as illegal. '
f'Please adjust your lineup and re-submit!\n\n'
f'- {l_check["error_string"]}'
)
return
logging.debug(f'Setting lineup for {lineup_team["sname"]} in PD game')
post_lineups(all_lineups)
try:
await interaction.channel.send(content=None, embed=await self.initialize_play_plus_embed(this_game))
except IntegrityError as e:
logging.debug(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.command(name='get-bullpen', help='Mod: Sync an AI bullpen')
@commands.is_owner()
async def get_bullpen_command(self, ctx: commands.Context, team_id):
team = await db_get('teams', object_id=team_id)
if not team:
await ctx.send(f'I did not find a team with id {team_id}')
return
await ctx.send(f'I\'m getting bullpen data from {team["gmname"]}...')
get_or_create_bullpen(team, self.bot)
await ctx.send(f'Got it!')
group_substitution = app_commands.Group(name='substitute', description='Make a substitution in active game')
@group_substitution.command(name='batter', description='Make a batter substitution')
@app_commands.describe(
batting_order='Batting order of new player',
new_player='Enter the Card ID (a number)')
@commands.has_any_role(SBA_PLAYERS_ROLE_NAME, PD_PLAYERS_ROLE_NAME)
async def sub_batter_command(
self, interaction: discord.Interaction, new_player: int, batting_order: Literal[
'this-spot', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
new_pos: Literal['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'DH', 'PH', 'PR']):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
this_card = await db_get(f'cards', object_id=int(new_player))
if this_card["team"]["id"] != owner_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']['player_id']
card_id = new_player
if this_game.game_type in ['major-league', 'hall-of-fame', 'tens']:
legality = await db_post(f'cards/legal-check/{this_game.game_type}?card_id={new_player}')
if legality['count'] > 0:
il_string = "\n- ".join(legality['bad_cards'])
await interaction.edit_original_response(
content=f'It looks like this is a {this_game.game_type.replace("-", " ").title()} game and I see '
f'the following cards as illegal. Please take another look and re-submit!\n\n'
f'- {il_string}'
)
return
batting_order = int(batting_order) if batting_order != 'this-spot' else this_play.batting_order
# Check for simple position change
in_lineup = get_one_lineup(this_game.id, team_id=owner_team['id'], active=True, batting_order=batting_order)
if in_lineup.card_id == int(card_id):
post_pos = patch_lineup(in_lineup.id, position=new_pos)
else:
this_lineup = {
'game_id': this_game.id,
'team_id': owner_team['id'],
'player_id': player_id,
'card_id': card_id,
'position': new_pos.upper(),
'batting_order': batting_order,
'after_play': this_play.play_num - 1 if this_play else 0
}
make_sub(this_lineup)
await interaction.edit_original_response(
content=None,
embed=await self.initialize_play_plus_embed(this_game)
)
@group_substitution.command(name='pitcher', description='Make a pitching change')
@app_commands.describe(
new_player='Enter the Card ID (a number)',
batting_order='Only enter this if you are forfeiting the DH')
@commands.has_any_role(PD_PLAYERS_ROLE_NAME)
async def sub_pitcher_command(
self, interaction: discord.Interaction, new_player: int,
batting_order: Literal['this-spot', '1', '2', '3', '4', '5', '6', '7', '8', '9'] = None):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
this_card = await db_get(f'cards', object_id=int(new_player))
if this_card["team"]["id"] != owner_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']['player_id']
card_id = new_player
new_post = False
# Check for simple position change
in_lineup = get_one_lineup(this_game.id, team_id=owner_team['id'], active=True, card_id=card_id)
if in_lineup is not None:
new_pitcher = patch_lineup(in_lineup.id, position='P')
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
q_text = f'It looks like you are forfeiting the DH by moving {player_desc(this_card["player"])} to ' \
f'the mound. __You will not be able to undo this sub even with the undo-play command__ so real ' \
f'quick: are you sure?'
question = await interaction.channel.send(q_text, view=view)
await view.wait()
if view.value:
await question.edit(content=f'~~{q_text}~~\nFuck the DH \U0000270A', view=None)
else:
await question.delete()
await interaction.edit_original_response(content=f'~~{q_text}~~\nI will hold off for now.')
await interaction.channel.send(
content=None,
embed=await self.initialize_play_plus_embed(this_game)
)
return
old_pitcher = get_pitcher(this_game, this_play)
patch_lineup(old_pitcher.id, active=False)
patch_play(this_play.id, pitcher_id=new_pitcher.id)
this_play.pitcher = new_pitcher
else:
if this_play is None:
after_play = 0
old_pit_order = 10
if batting_order is None:
batting_order = 10
else:
batting_order = int(batting_order)
else:
if this_play.pitcher.team_id != owner_team['id']:
await interaction.edit_original_response(
content='It looks like your team is batting right now - '
'please make this sub once you take the field.')
return
old_pit_order = this_play.pitcher.batting_order
if batting_order is None:
batting_order = old_pit_order
else:
batting_order = int(batting_order)
after_play = this_play.play_num - 1
this_lineup = {
'game_id': this_game.id,
'team_id': owner_team['id'],
'player_id': player_id,
'card_id': card_id,
'position': 'P',
'batting_order': batting_order,
'after_play': after_play
}
make_sub(this_lineup)
if old_pit_order != batting_order:
patch_lineup(this_play.pitcher.id, active=False)
if new_post:
await interaction.channel.send(
content=None,
embed=await self.initialize_play_plus_embed(this_game)
)
else:
await interaction.edit_original_response(
content=None,
embed=await self.initialize_play_plus_embed(this_game)
)
# @group_substitution.command(name='forfeit-dh', description='Have the pitcher bat in the DH spot')
# @commands.has_any_role(PD_PLAYERS_ROLE_NAME)
# async def sub_forfeitdh_command(self, interaction: discord.Interaction):
# this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
# if False in (this_game, owner_team, this_play):
# return
#
@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
response = await ctx.send(f'Building the scorebug now...')
await response.edit(content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=include_lineups))
async def checks_log_interaction(self, interaction: discord.Interaction, block_rollback: Optional[bool] = False) \
-> tuple[Optional[StratGame], Optional[dict], Optional[StratPlay]]:
await interaction.response.defer()
this_game = get_one_game(channel_id=interaction.channel.id, active=True)
if not this_game:
await interaction.edit_original_response(content='I don\'t see a game in this channel.')
return False, False, False
owner_team = await get_game_team(this_game, interaction.user.id)
if 'gauntlet' in this_game.game_type:
owner_team = await get_game_team(this_game, team_abbrev=f'Gauntlet-{owner_team["abbrev"]}')
if not owner_team['id'] in [this_game.away_team_id, this_game.home_team_id]:
logging.info(f'{interaction.user.display_name} tried to run a command in Game {this_game.id} when they '
f'aren\'t a GM in the game.')
await interaction.edit_original_response(content='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 is not None:
# Allow specific commands to not rollback the play (e.g. /show-card)
if this_play.locked and not block_rollback:
logging.info(f'{interaction.user.display_name} tried to run a command in Game {this_game.id} while the '
f'play was locked.')
undo_play(this_play.id)
await interaction.edit_original_response(
content=f'Looks like your game got hung up there for a second. I just rolled it back a play to '
f'release it. If you have any more issues, let Cal know.'
)
this_play = get_current_play(this_game.id)
if not this_play.pitcher:
logging.info(f'{interaction.user.display_name} tried to run a command in Game {this_game.id} without a '
f'pitcher in the lineup.')
await interaction.edit_original_response(content=f'Please sub in a pitcher before logging a new play.')
this_play = None
return this_game, owner_team, this_play
group_log = app_commands.Group(name='log', description='Log a play in this channel\'s game')
@group_log.command(name='ai-pitcher-sub', description='Run automated substitution when the AI wants a change')
async def ai_pitcher_sub(self, interaction: discord.Interaction):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
away_team = await get_game_team(this_game, team_id=this_game.away_team_id)
home_team = await get_game_team(this_game, team_id=this_game.home_team_id)
ai_team = away_team if owner_team['id'] == home_team['id'] else home_team
if this_play.batter.team_id == ai_team['id']:
await interaction.edit_original_response(
content=f'It looks like the {ai_team["sname"]} are batting now. If they need a sub, please do so when '
f'they are back on defense.'
)
return
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Whoa there, cowpoke. The AI can automatically make pitcher subs now and will let pitchers cook in some '
f'cases. Are you sure you want to manually run a sub for them?',
view=view
)
await view.wait()
if view.value:
await question.edit(
content=f'~~Whoa there, cowpoke. The AI can automatically make pitcher subs now and will let pitchers '
f'cook in some cases. Are you sure you want to manually run a sub for them?~~\n\nI let Cal '
f'know his stupid AI isn\'t working.',
view=None
)
await send_to_channel(
self.bot,
'commissioners-office',
content=f'{interaction.user.display_name} just ran a manual sub here: {question.jump_url}')
else:
await question.delete()
await interaction.edit_original_response(
content='Jkjkjk, we will let the ai decide when to make subs.',
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
return
curr_pitcher = get_one_lineup(this_game.id, team_id=ai_team['id'], position='P')
pit_plays = get_plays(this_game.id, curr_pitcher.id)
logging.debug(f'pit_plays for sub in Game {this_game.id}: {pit_plays}')
if pit_plays['count'] < 2:
pitcher = await get_player(this_game, curr_pitcher)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'It doesn\'t look like {pitcher["p_name"]} is fatigued - are you sure they should be subbed out?',
view=view
)
await view.wait()
if view.value:
await question.delete()
await interaction.channel.send(
content=f'Thanks! I let {get_cal_user(interaction).mention} know there was an issue with '
f'{ai_team["abbrev"]} pitchers.'
)
else:
await question.delete()
await interaction.edit_original_response(content=f'Okay, I\'ll leave them in!')
return
# new_pitcher = await next_pitcher(this_play, ai_team, self.bot)
# logging.debug(f'new_pitcher: {new_pitcher}')
#
# this_lineup = {
# 'game_id': this_game.id,
# 'team_id': ai_team['id'],
# 'player_id': new_pitcher['player']['player_id'],
# 'card_id': new_pitcher['card_id'],
# 'position': 'P',
# 'batting_order': 10,
# 'after_play': this_play.play_num - 1
# }
# make_sub(this_lineup)
used_pitchers = await get_team_lineups(
game_id=this_play.game.id, team_id=ai_team['id'], inc_inactive=True, as_string=False
)
make_sub(await ai_manager.get_relief_pitcher(this_play, ai_team, this_game.game_type))
await interaction.edit_original_response(
content=None,
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='flyball', description='Flyballs: a, b, ballpark, bq, c')
async def log_flyball(
self, interaction: discord.Interaction, flyball_type: Literal['a', 'b', 'ballpark', 'b?', 'c']):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
await self.flyballs(interaction, this_game, this_play, flyball_type)
if flyball_type not in ['a', 'c'] and this_play.on_base_code > 0:
await interaction.edit_original_response(content=f'Flyball has been logged')
await interaction.channel.send(
content=None,
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
else:
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='groundball', description='Groundballs: a, b, c')
async def log_groundball(self, interaction: discord.Interaction, groundball_type: Literal['a', 'b', 'c']):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
await self.groundballs(interaction, this_game, this_play, groundball_type)
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='single', description='Singles: *, **, ballpark, uncapped')
async def log_single(
self, interaction: discord.Interaction, single_type: Literal['*', '**', 'ballpark', 'uncapped']):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
if single_type == '**':
single_wellhit(this_play)
elif single_type == '*':
single_onestar(this_play)
elif single_type == 'ballpark':
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)
elif single_type == 'uncapped':
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
if this_play.on_base_code in [1, 2, 4, 5, 6, 7]:
ai_manager = get_manager(this_game)
ai_is_batting = ai_batting(this_game, this_play)
def_team = await db_get('teams', object_id=this_play.pitcher.team_id)
to_bases = [None, None, 'to second', 'to third', 'home']
at_bases = [None, None, 'at second', 'at third', 'at home']
if this_play.on_second:
lead_runner = await get_player(this_game, this_play.on_second)
lead_base = 4
if this_play.on_first:
trail_runner = await get_player(this_game, this_play.on_first)
trail_base = 3
else:
trail_runner = await get_player(this_game, this_play.batter)
trail_base = 2
else:
lead_runner = await get_player(this_game, this_play.on_first)
lead_base = 3
trail_runner = await get_player(this_game, this_play.batter)
trail_base = 2
ai_hint = ''
if this_game.ai_team and ai_is_batting:
ai_hint = f'*The runner will ' \
f'{ai_manager.uncapped_advance(lead_base, this_play.starting_outs)}*'
logging.debug(f'calling of embed')
await show_outfield_cards(interaction, this_play)
logging.debug(f'done with of embed')
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is {lead_runner["p_name"]} being sent {to_bases[lead_base]}?\n\n{ai_hint}', view=view
)
await view.wait()
if view.value:
await question.delete()
ai_hint = ''
if this_game.ai_team and not ai_is_batting:
ai_hint = f'*The defense will ' \
f'{ai_manager.throw_lead_runner(lead_base, this_play.starting_outs)}*'
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is the defense throwing {to_bases[lead_base]}?\n\n{ai_hint}', view=view
)
await view.wait()
if view.value:
await question.delete()
ai_hint = ''
if this_game.ai_team and ai_is_batting:
ai_hint = f'*The runner will ' \
f'{ai_manager.trail_advance(trail_base, this_play.starting_outs, True if lead_base == 4 else False)}*'
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is {trail_runner["p_name"]} being sent {to_bases[trail_base]}?\n\n{ai_hint}', view=view
)
await view.wait()
if view.value:
await question.delete()
ai_hint = ''
if this_game.ai_team and not ai_is_batting:
ai_hint = f'*The defense will ' \
f'{ai_manager.throw_which_runner(lead_base, this_play.starting_outs)}*'
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
view.confirm.label = 'Home Plate' if lead_base == 4 else 'Third Base'
view.cancel.label = 'Third Base' if trail_base == 3 else 'Second Base'
question = await interaction.channel.send(
f'Is the throw going {to_bases[lead_base]} or {to_bases[trail_base]}?\n\n{ai_hint}',
view=view
)
await view.wait()
# Throw to lead runner
if view.value:
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
content=f'Was {lead_runner["p_name"]} thrown out {at_bases[lead_base]}?', view=view
)
await view.wait()
if view.value:
await question.delete()
advance_runners(this_play.id, 2)
batter_to_base = 2
if lead_base == 4:
patch_play(this_play.id, on_second_final=False, outs=1)
else:
patch_play(this_play.id, on_first_final=False, outs=1)
else:
await question.delete()
advance_runners(this_play.id, 2)
batter_to_base = 2
# Throw to trail runner
else:
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
content=f'Was {trail_runner["p_name"]} thrown out {at_bases[trail_base]}?', view=view
)
await view.wait()
if view.value:
await question.delete()
advance_runners(this_play.id, 2)
if trail_base == 3:
patch_play(this_play.id, on_first_final=False, outs=1)
batter_to_base = 2
else:
patch_play(this_play.id, outs=1)
batter_to_base = None
else:
await question.delete()
advance_runners(this_play.id, 2)
batter_to_base = 2
else:
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
content=f'Was {lead_runner["p_name"]} thrown out {at_bases[lead_base]}?', view=view
)
await view.wait()
if view.value:
await question.delete()
if lead_base == 4:
patch_play(this_play.id, on_second_final=False, outs=1)
else:
patch_play(this_play.id, on_first_final=False, outs=1)
else:
await question.delete()
advance_one_runner(this_play.id, from_base=lead_base - 2, num_bases=2)
else:
await question.delete()
advance_one_runner(this_play.id, from_base=lead_base - 2, num_bases=2)
else:
await question.delete()
logging.debug(f'post process batter runner on_second_final: {this_play.on_second_final}')
complete_play(this_play.id, batter_to_base=batter_to_base)
if single_type == 'uncapped':
await interaction.edit_original_response(content=f'Uncapped single has been logged')
await interaction.channel.send(
content=None,
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
else:
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='frame-pitch', description=f'Walk/strikeout split; determined by home plate umpire')
async def log_frame_check(self, interaction: discord.Interaction):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
frame_result = dice.frame_plate_check(owner_team, this_game.id)
logging.info(f'Frame Result:\n{frame_result}')
await interaction.edit_original_response(
content=None,
embed=frame_result['embed']
)
if frame_result['is_walk']:
advance_runners(this_play.id, num_bases=1, only_forced=True)
patch_play(this_play.id, pa=1, walk=1)
batter_to_base = 1
else:
patch_play(this_play.id, pa=1, ab=1, outs=1, so=1)
advance_runners(this_play.id, 0)
batter_to_base = None
complete_play(this_play.id, batter_to_base=batter_to_base)
await interaction.channel.send(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='double', description='Doubles: **, ***, uncapped')
async def log_double(self, interaction: discord.Interaction, double_type: Literal['**', '***', 'uncapped']):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
if double_type == '**':
double_twostar(this_play)
elif double_type == '***':
double_threestar(this_play)
elif double_type == 'uncapped':
advance_runners(this_play.id, 2)
this_play = patch_play(this_play.id, locked=True, pa=1, ab=1, hit=1, double=1)
batter_to_base = 2
ai_is_batting = ai_batting(this_game, this_play)
this_manager = get_manager(this_game)
if this_play.on_first:
ai_hint = ''
if this_game.ai_team and ai_is_batting:
if this_play.on_first:
ai_hint = f'*The runner will attempt to advance*'
else:
ai_hint = f'*The runner will ' \
f'{this_manager.uncapped_advance(4, this_play.starting_outs)}*'
logging.debug(f'calling of embed')
await show_outfield_cards(interaction, this_play)
logging.debug(f'done with of embed')
runner_to_home = await get_player(this_game, this_play.on_first)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is {runner_to_home["p_name"]} being sent home?\n\n{ai_hint}', view=view
)
await view.wait()
if view.value:
await question.delete()
ai_hint = ''
if this_game.ai_team and not ai_is_batting:
ai_hint = f'*The defense will {this_manager.throw_lead_runner(4, this_play.starting_outs)}*'
# Throw for lead runner?
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is the defense throwing home?\n\n{ai_hint}', view=view
)
await view.wait()
# Yes: Send Trail runner?
if view.value:
await question.delete()
ai_hint = ''
if this_game.ai_team and ai_is_batting:
ai_hint = f'*The trail runner will ' \
f'{this_manager.trail_advance(3, this_play.starting_outs, True)}*'
batter_runner = await get_player(this_game, this_play.batter)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is {batter_runner["p_name"]} being sent to third?\n\n{ai_hint}', view=view
)
await view.wait()
# Yes: Throw lead or trail?
if view.value:
await question.delete()
ai_hint = ''
if this_game.ai_team and not ai_is_batting:
ai_hint = f'*The defense will ' \
f'{this_manager.throw_which_runner(4, this_play.starting_outs)}*'
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
view.confirm.label = 'Home Plate'
view.cancel.label = 'Third Base'
question = await interaction.channel.send(
f'Is the throw going to home plate or to third base?\n\n{ai_hint}', view=view
)
await view.wait()
# Throw home
if view.value:
batter_to_base = 3
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
content=f'Was {runner_to_home["p_name"]} thrown out at the plate?', view=view
)
await view.wait()
# Out at the plate
if view.value:
await question.delete()
patch_play(this_play.id, on_first_final=False, outs=1)
# Safe at the plate
else:
await question.delete()
advance_runners(this_play.id, 3)
# Throw to third
else:
await question.delete()
advance_runners(this_play.id, num_bases=3)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
content=f'Was {batter_runner["p_name"]} thrown out at third?', view=view
)
await view.wait()
# Out at third
if view.value:
await question.delete()
batter_to_base = None
advance_runners(this_play.id, num_bases=3)
patch_play(this_play.id, outs=1)
# Safe at home and third
else:
batter_to_base = 3
await question.delete()
# No: Safe at home?
else:
await question.delete()
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
content=f'Was {runner_to_home["p_name"]} thrown out at the plate?', view=view
)
await view.wait()
# Out at the plate
if view.value:
await question.delete()
patch_play(this_play.id, on_first_final=False, outs=1)
# Safe at the plate
else:
await question.delete()
advance_runners(this_play.id, num_bases=3)
# No: End of play
else:
await question.delete()
advance_runners(this_play.id, num_bases=3)
else:
await question.delete()
complete_play(this_play.id, batter_to_base=batter_to_base)
if double_type == 'uncapped':
await interaction.edit_original_response(content=f'Uncapped double has been logged')
await interaction.channel.send(
content=None,
embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
else:
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='triple', description='Triples: no sub-types')
async def log_triple(self, interaction: discord.Interaction):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
triple(this_play)
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='homerun', description='Home Runs: ballpark, no-doubt')
async def log_homerun(self, interaction: discord.Interaction, homerun_type: Literal['ballpark', 'no-doubt']):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
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)
if homerun_type == 'ballpark':
patch_play(this_play.id, pa=1, ab=1, hit=1, homerun=1, bphr=1)
elif homerun_type == 'no-doubt':
patch_play(this_play.id, pa=1, ab=1, hit=1, homerun=1)
complete_play(this_play.id, batter_to_base=4)
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='walk', description='Walks: unintentional, intentional')
async def log_walk(self, interaction: discord.Interaction, walk_type: Literal['unintentional', 'intentional']):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
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)
if walk_type == 'unintentional':
patch_play(this_play.id, pa=1, walk=1)
elif walk_type == 'intentional':
patch_play(this_play.id, pa=1, ibb=1)
complete_play(this_play.id, batter_to_base=1)
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='chaos', description='Chaos: wild-pitch, passed-ball, balk, pickoff')
async def log_chaos(self, interaction: discord.Interaction,
chaos_type: Literal['wild-pitch', 'passed-ball', 'balk', 'pickoff']):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, 0)
if chaos_type == 'wild-pitch':
advance_runners(this_play.id, 1)
patch_play(this_play.id, rbi=0, wp=1)
elif chaos_type == 'passed-ball':
advance_runners(this_play.id, 1)
patch_play(this_play.id, rbi=0, pb=1)
elif chaos_type == 'balk':
advance_runners(this_play.id, 1)
patch_play(this_play.id, rbi=0, balk=1)
elif chaos_type == 'pickoff':
# Get from base
runner_flag = False
patch_play(this_play.id, pick=1)
if this_play.on_base_code in [1, 2, 3]:
if this_play.on_first:
patch_play(this_play.id, on_first_final=False, runner_id=this_play.on_first.id, outs=1)
elif this_play.on_second:
patch_play(this_play.id, on_second_final=False, runner_id=this_play.on_second.id, outs=1)
elif this_play.on_third:
patch_play(this_play.id, on_third_final=False, runner_id=this_play.on_third.id, outs=1)
else:
if this_play.on_first:
this_runner = await get_player(this_game, this_play.on_first)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(f'Was {this_runner["p_name"]} picked off?', view=view)
await view.wait()
if view.value:
patch_play(this_play.id, on_first_final=False, runner_id=this_play.on_first.id, outs=1)
runner_flag = True
await question.delete()
if this_play.on_second and not runner_flag:
this_runner = await get_player(this_game, this_play.on_second)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(f'Was {this_runner["p_name"]} picked off?', view=view)
await view.wait()
if view.value:
patch_play(this_play.id, on_second_final=False, runner_id=this_play.on_second.id, outs=1)
runner_flag = True
await question.delete()
if this_play.on_third and not runner_flag:
this_runner = await get_player(this_game, this_play.on_third)
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(f'Was {this_runner["p_name"]} picked off?', view=view)
await view.wait()
if view.value:
patch_play(this_play.id, on_third_final=False, runner_id=this_play.on_third.id, outs=1)
runner_flag = True
await question.delete()
if not runner_flag:
await interaction.edit_original_response(content=f'So I guess nobody got picked off?')
patch_play(this_play.id, locked=False)
return
complete_play(this_play.id)
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='stealing', description='Running: stolen-base, caught-stealing')
@app_commands.describe(to_base='Base the runner is advancing to; 2 for 2nd, 3 for 3rd, 4 for Home')
async def log_stealing(
self, interaction: discord.Interaction,
running_type: Literal['stolen-base', 'caught-stealing', 'steal-plus-overthrow'],
to_base: Literal[2, 3, 4]):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
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'
)
advance_runners(this_play.id, 0)
if running_type == 'stolen-base':
if to_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
)
if this_play.on_second:
advance_one_runner(this_play.id, 2, 1)
if this_play.on_first:
advance_one_runner(this_play.id, 1, 1)
elif to_base == 3 and this_play.on_second:
if not this_play.on_third:
advance_runners(this_play.id, 1, is_error=True)
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 interaction.edit_original_response(
content=f'Ope. Looks like {this_runner["p_name"]} is blocked by the runner at third.'
)
patch_play(this_play.id, locked=False)
return
if this_play.on_first:
advance_one_runner(
this_play.id, from_base=1, num_bases=1
)
elif to_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 interaction.edit_original_response(
content=f'Ope. Looks like {this_runner["p_name"]} is blocked by the runner at second.'
)
patch_play(this_play.id, locked=False)
return
else:
await interaction.edit_original_response(
content=f'Uh oh - I don\'t see a runner there to steal the bag.'
)
patch_play(this_play.id, locked=False)
return
if running_type == 'steal-plus-overthrow':
if to_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
)
if this_play.on_second:
advance_one_runner(this_play.id, 2, 2)
if this_play.on_first:
advance_one_runner(this_play.id, 1, 2)
elif to_base == 3 and this_play.on_second:
advance_runners(this_play.id, 1)
if not this_play.on_third:
patch_play(
this_play.id, sb=1, on_second_final=4, runner_id=this_play.on_second.id, catcher_id=catcher.id
)
else:
this_runner = await get_player(this_game, this_play.on_second)
await interaction.edit_original_response(
content=f'Ope. Looks like {this_runner["p_name"]} is blocked by the runner at third.'
)
patch_play(this_play.id, locked=False)
return
elif to_base == 2 and this_play.on_first:
if not this_play.on_second:
advance_runners(this_play.id, 1)
patch_play(
this_play.id, sb=1, on_first_final=3, runner_id=this_play.on_first.id, catcher_id=catcher.id
)
else:
this_runner = await get_player(this_game, this_play.on_first)
await interaction.edit_original_response(
content=f'Ope. Looks like {this_runner["p_name"]} is blocked by the runner at second.'
)
patch_play(this_play.id, locked=False)
return
else:
await interaction.edit_original_response(
content=f'Uh oh - I don\'t see a runner there to steal the bag.'
)
patch_play(this_play.id, locked=False)
return
patch_play(this_play.id, error=1)
elif running_type == 'caught-stealing':
if to_base == 4 and this_play.on_third:
patch_play(
this_play.id, cs=1, on_third_final=False, runner_id=this_play.on_third.id,
catcher_id=catcher.id, outs=1
)
if this_play.on_second:
advance_one_runner(this_play.id, 2, 1)
if this_play.on_first:
advance_one_runner(this_play.id, 1, 1)
elif to_base == 3 and this_play.on_second:
if not this_play.on_third:
advance_runners(this_play.id, 1)
patch_play(
this_play.id, cs=1, on_second_final=False, 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 interaction.edit_original_response(
content=f'Ope. Looks like {this_runner["p_name"]} is blocked by the runner at third.'
)
patch_play(this_play.id, locked=False)
return
elif to_base == 2 and this_play.on_first:
if not this_play.on_second:
patch_play(
this_play.id, cs=1, on_first_final=False, 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 interaction.edit_original_response(
content=f'Ope. Looks like {this_runner["p_name"]} is blocked by the runner at second.'
)
patch_play(this_play.id, locked=False)
return
else:
await interaction.edit_original_response(
content=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 interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='strikeout', description='Strikeout')
async def log_strikeout(self, interaction: discord.Interaction):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
patch_play(this_play.id, pa=1, ab=1, outs=1, so=1)
advance_runners(this_play.id, 0)
complete_play(this_play.id)
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='popout', description='Popout')
async def log_popout(self, interaction: discord.Interaction):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
patch_play(this_play.id, pa=1, ab=1, outs=1)
advance_runners(this_play.id, 0)
complete_play(this_play.id)
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='lineout', description='Lineouts: one out, ballpark, max outs')
async def log_lineout(
self, interaction: discord.Interaction, lineout_type: Literal['one-out', 'ballpark', 'max-outs']):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=True)
advance_runners(this_play.id, 0)
if lineout_type == 'one-out' or this_play.starting_outs == 2 or this_play.on_base_code == 0:
patch_play(this_play.id, pa=1, ab=1, outs=1)
elif lineout_type == 'ballpark':
patch_play(this_play.id, pa=1, ab=1, outs=1, bplo=1)
elif lineout_type == 'max-outs':
bases = ['third', 'second', 'first']
num_outs = 1
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=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Is {runner["p_name"]} out at {bases[count]} on the play?', 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)
patch_play(this_play.id, pa=1, ab=1, outs=num_outs)
complete_play(this_play.id)
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='hit-by-pitch', description='Batter to first; runners advance if forced')
async def log_hit_by_pitch_command(self, interaction: discord.Interaction):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=False)
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 interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='sac-bunt', description='Batter out; runners advance one base')
async def log_sac_bunt_command(self, interaction: discord.Interaction):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=False)
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 interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='undo-play', description='Remove the most recent play from the log')
async def log_undo_play_command(self, interaction: discord.Interaction):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=False)
try:
logging.debug(f'Undoing play {this_play.id} in Game {this_game.id}: Batter {this_play.batter}')
undo_play(this_game.id)
except AttributeError as e:
logging.error(f'Could not undo play {this_play.id} in game {this_game.id}')
try:
last_completed = get_latest_play(this_game.id)
logging.debug(f'Undoing play {last_completed.id} in Game {this_game.id}')
undo_play(this_game.id)
except AttributeError as e:
logging.error(f'Could not undo second play in game {this_game.id}')
latest_play = get_latest_play(this_game.id)
logging.debug(f'Latest completed play is Play {latest_play.id}; batter_to_base: {latest_play.batter_final}')
undo_subs(this_game, latest_play.play_num)
await interaction.edit_original_response(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
@group_log.command(name='xcheck', description='Defender makes an x-check')
@app_commands.choices(position=[
Choice(name='Pitcher', value='P'),
Choice(name='Catcher', value='C'),
Choice(name='First Base', value='1B'),
Choice(name='Second Base', value='2B'),
Choice(name='Third Base', value='3B'),
Choice(name='Shortstop', value='SS'),
Choice(name='Left Field', value='LF'),
Choice(name='Center Field', value='CF'),
Choice(name='Right Field', value='RF'),
])
async def log_xcheck_command(self, interaction: discord.Interaction, position: Choice[str]):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
patch_play(this_play.id, locked=False)
def_team_id = this_game.away_team_id if this_play.inning_half == 'Bot' else this_game.home_team_id
def_team = await db_get('teams', object_id=def_team_id)
d_lineup = get_one_lineup(this_game.id, team_id=this_play.pitcher.team_id, position=position.value)
defender = await get_player(this_game, d_lineup)
patch_play(this_play.id, defender_id=d_lineup.id, check_pos=position.value)
defender_embed = get_team_embed(f'{def_team["sname"]} {position.value}', def_team)
defender_embed.description = f'{defender["p_name"]}'
defender_embed.set_image(url=defender['image'])
embeds = [defender_embed]
dice_embeds = sa_fielding_roll(position.value, def_team)
embeds.extend(dice_embeds)
all_embeds = [x for x in embeds if x is not None]
await interaction.edit_original_response(
content=None,
embeds=all_embeds
)
hit_allowed, error_allowed, chaos_allowed = None, None, None
view = Confirm([interaction.user], label_type='yes')
question = await interaction.channel.send(f'Did {defender["p_name"]} give up a hit?', view=view)
await view.wait()
if view.value:
await question.delete()
view = ButtonOptions(
responders=[interaction.user],
labels=['single*', 'single**', 'double**', 'double***', 'triple']
)
question = await interaction.channel.send(f'Which hit did {defender["p_name"]} allow?', view=view)
await view.wait()
if not view.value:
await question.edit(f'Hmm...you keep thinking on it and get back to me when you\'re ready.')
return
else:
await question.delete()
hit_allowed = view.value
else:
await question.delete()
hit_allowed = 'out'
view = Confirm([interaction.user], label_type='yes')
question = await interaction.channel.send(f'Did {defender["p_name"]} give up an error or rare play?', view=view)
await view.wait()
if view.value:
await question.delete()
view = ButtonOptions(
responders=[interaction.user],
labels=['1 base', '2 bases', '3 bases', 'Rare Play', None]
)
question = await interaction.channel.send(f'What kind of error did {defender["p_name"]} allow?', view=view)
await view.wait()
if not view.value:
await question.edit(f'Hmm...you keep thinking on it and get back to me when you\'re ready.')
return
else:
await question.delete()
error_allowed = view.value
else:
await question.delete()
error_allowed = 'no error'
# Not hit and no error
if hit_allowed == 'out' and error_allowed == 'no error':
if this_play.on_base_code == 0:
patch_play(this_play.id, pa=1, ab=1, outs=1)
advance_runners(this_play.id, 0)
complete_play(this_play.id)
else:
if position.value not in ['LF', 'CF', 'RF']:
view = ButtonOptions(
responders=[interaction.user],
labels=['G1', 'G2', 'G3', None if position.value != 'C' else 'FO',
None if position.value != 'C' else 'PO']
)
question = await interaction.channel.send(f'What was the result of the play?', view=view)
logging.info(f'obc: {this_play.on_base_code}')
await view.wait()
logging.info(f'gameplay - view: {view} / view.value: {view.value}')
gb_type = view.value
logging.info(f'gameplay - gb_type: {gb_type}')
if not view.value:
await question.edit('Okay, we can try this again later.')
else:
await question.delete()
playing_in = False
batter_to_base = None
if runner_on_third(this_play) and gb_type not in ['FO', 'PO']:
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
question = await interaction.channel.send(
f'Was the defender playing in?', view=view
)
await view.wait()
if view.value:
playing_in = True
else:
playing_in = False
await question.delete()
logging.info(f'bot playing_in: {playing_in} / view.value: {view.value} / gb_type: {gb_type}')
if gb_type == 'G1':
if (not playing_in and this_play.on_base_code == 0) or \
(playing_in and (this_play.on_base_code in [3, 6])):
batter_to_base = gb_result_1(this_play)
elif playing_in and this_play.on_base_code == 5:
batter_to_base = gb_result_7(this_play)
elif (not playing_in and this_play.on_base_code in [1, 5, 7]) or \
(playing_in and (this_play.on_base_code in [1, 4])):
batter_to_base = gb_result_2(this_play)
elif not playing_in and this_play.on_base_code in [3, 6]:
batter_to_base = gb_result_3(this_play)
elif this_play.on_base_code == 2:
if position.value == '3B':
this_def = '3b'
elif position.value in ['1B', '2B']:
this_def = '1b-2b'
else:
this_def = 'ss-p-c'
batter_to_base = await gb_result_12(this_play, this_def, interaction)
elif playing_in and this_play.on_base_code == 7:
batter_to_base = gb_result_10(this_play)
elif not playing_in and this_play.on_base_code == 4:
if position.value in ['C', '3B']:
this_def = 'c-3b'
else:
this_def = 'else'
batter_to_base = gb_result_13(this_play, this_def)
elif gb_type == 'G2':
if (not playing_in and this_play.on_base_code == 0) or \
(playing_in and this_play.on_base_code in [3, 6]):
batter_to_base = gb_result_1(this_play)
elif playing_in and this_play.on_base_code == 5:
batter_to_base = gb_result_7(this_play)
elif (not playing_in and this_play.on_base_code in [1, 4, 5, 7]) or \
(playing_in and (this_play.on_base_code in [1, 4])):
batter_to_base = gb_result_4(this_play)
elif not playing_in and this_play.on_base_code in [3, 6]:
if position.value in ['SS', '2B']:
to_mif = True
else:
to_mif = False
batter_to_base = gb_result_5(this_play, to_mif)
elif playing_in and this_play.on_base_code == 7:
batter_to_base = gb_result_11(this_play)
elif this_play.on_base_code == 2:
if position.value == '3B':
this_def = '3b'
elif position.value in ['1B', '2B']:
this_def = '1b-2b'
else:
this_def = 'ss-p-c'
batter_to_base = await gb_result_12(this_play, this_def, interaction)
elif gb_type == 'G3':
if not playing_in and this_play.on_base_code == 0:
batter_to_base = gb_result_1(this_play)
elif playing_in and this_play.on_base_code == 5:
batter_to_base = gb_result_7(this_play)
elif playing_in and this_play.on_base_code == 7:
batter_to_base = gb_result_11(this_play)
elif not playing_in and this_play.on_base_code == 2:
if position.value == '3B':
this_def = '3b'
elif position.value in ['1B', '2B']:
this_def = '1b-2b'
else:
this_def = 'ss-p-c'
batter_to_base = await gb_result_12(this_play, this_def, interaction)
elif playing_in and this_play.on_base_code in [3, 6]:
batter_to_base = await gb_decide(
this_play, interaction, await get_player(this_play.game, this_play.on_third), 3
)
else:
batter_to_base = gb_result_3(this_play)
else:
logging.info(f'no match; log out')
patch_play(this_play.id, pa=1, ab=1, outs=1)
advance_runners(this_play.id, 0)
complete_play(this_play.id, batter_to_base)
else:
view = ButtonOptions(responders=[interaction.user], labels=['F1', 'F2', 'F3', None, None])
question = await interaction.channel.send(f'What was the result of the play?', view=view)
await view.wait()
if not view.value:
await question.delete()
await interaction.channel.send(
content=f'Just logged the x-check! Please log the resulting play to continue (e.g. '
f'\'flyball-b\' or \'flyball-c\')'
)
patch_play(this_play.id, locked=False)
return
else:
await question.delete()
if view.value == 'F1':
fly_code = 'a'
elif view.value == 'F2':
fly_code = 'b'
else:
fly_code = 'c'
await self.flyballs(interaction, this_game, this_play, fly_code)
# Hit and error
elif hit_allowed != 'out' and error_allowed != 'no error':
patch_play(this_play.id, error=1)
if hit_allowed == 'triple':
triple(this_play, comp_play=False)
advance_runners(this_play.id, num_bases=4)
batter_to_base = 4
elif 'double' in hit_allowed:
double_threestar(this_play, comp_play=False)
if error_allowed == 'Rare Play':
patch_play(this_play.id, outs=1)
batter_to_base = None
elif 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
elif hit_allowed == 'single**':
single_wellhit(this_play, comp_play=False)
if error_allowed == 'Rare Play':
patch_play(this_play.id, outs=1)
batter_to_base = None
elif error_allowed == '1 base':
batter_to_base = 2
else:
advance_runners(this_play.id, 3)
batter_to_base = 3
else:
single_onestar(this_play, False)
if error_allowed == '2 bases':
advance_runners(this_play.id, 3, True)
batter_to_base = 3
elif error_allowed == '1 base' or (error_allowed == 'Rare Play' and not runner_on_first(this_play)):
single_wellhit(this_play, False)
batter_to_base = 2
patch_play(this_play.id, error=True)
else:
batter_to_base = 1
complete_play(this_play.id, batter_to_base=batter_to_base)
# Either hit or error
else:
num_bases = None
if error_allowed == 'no error':
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)
elif error_allowed == 'Rare Play':
if position.value not in ['LF', 'CF', 'RF']:
view = ButtonOptions(
responders=[interaction.user],
labels=['G1', 'G2', 'G3', None if position.value != 'C' else 'FO',
None if position.value != 'C' else 'PO']
)
question = await interaction.channel.send(f'What was the result of the play?', view=view)
logging.info(f'obc: {this_play.on_base_code}')
await view.wait()
logging.info(f'gameplay - view: {view} / view.value: {view.value}')
gb_type = view.value
logging.info(f'gameplay - gb_type: {gb_type}')
if not view.value:
await question.edit('Okay, we can try this again later.')
else:
await question.delete()
if gb_type == 'G1':
if runner_on_first(this_play):
await self.groundballs(interaction, this_game, this_play, 'b')
else:
await self.groundballs(interaction, this_game, this_play, 'a')
elif gb_type == 'G2':
if this_play.on_base_code != 0:
await self.groundballs(interaction, this_game, this_play, 'c')
else:
await self.groundballs(interaction, this_game, this_play, 'b')
elif gb_type == 'G3':
if this_play.on_base_code != 0:
await single_onestar(this_play)
else:
await self.groundballs(interaction, this_game, this_play, 'c')
elif gb_type == 'PO':
single_onestar(this_play)
elif gb_type == 'FO':
advance_runners(this_play.id, num_bases=1, only_forced=True)
patch_play(this_play.id, pa=1, ab=1, hit=0, error=1)
complete_play(this_play.id, batter_to_base=1)
else:
view = ButtonOptions(responders=[interaction.user], labels=['F1', 'F2', 'F3', None, None])
question = await interaction.channel.send(f'What was the result of the play?', view=view)
await view.wait()
if not view.value:
await question.delete()
await interaction.channel.send(
content=f'Just logged the x-check! Please log the resulting play to continue (e.g. '
f'\'flyball-b\' or \'flyball-c\')'
)
patch_play(this_play.id, locked=False)
return
else:
await question.delete()
if view.value == 'F1':
fly_code = 'a'
elif view.value == 'F2':
fly_code = 'b'
else:
fly_code = 'c'
if this_play.starting_outs == 2 or this_play.on_base_code == 0:
await self.flyballs(interaction, this_game, this_play, fly_code)
else:
if fly_code == 'a':
patch_play(this_play.id, pa=1, ab=1, outs=1)
advance_runners(this_play.id, 2)
if this_play.on_third or this_play.on_second:
patch_play(this_play.id, ab=0)
elif fly_code == 'b':
if runner_on_third(this_play):
patch_play(this_play.id, pa=1, ab=1, outs=2, on_third_final=False)
else:
await self.flyballs(interaction, this_game, this_play, fly_code, comp_play=False)
elif fly_code == 'c':
patch_play(this_play.id, pa=1, ab=1, outs=2)
if runner_on_third(this_play):
patch_play(this_play.id, on_third_final=False)
elif runner_on_second(this_play):
patch_play(this_play.id, on_second_final=False)
else:
patch_play(this_play.id, on_first_final=False)
complete_play(this_play.id)
else:
if position.value == 'C':
view = ButtonOptions(
responders=[interaction.user],
labels=['G1/G2/G3/SPD', 'FO', 'PO']
)
question = await interaction.channel.send(f'What was the result of the play?', view=view)
await view.wait()
if not view.value:
await question.edit(
content=f'Hmm...you keep thinking on it and get back to me when you\'re ready.',
view=None
)
return
elif view.value == 'FO':
await question.edit(content=f'**The ball dropped foul; batter swings again.**', view=None)
patch_play(this_play.id, defender_id=False, check_pos='false')
await interaction.channel.send(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
return
else:
await question.delete()
patch_play(this_play.id, error=1)
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)
patch_play(this_play.id, locked=False)
await interaction.channel.send(
content=None, embed=await self.initialize_play_plus_embed(this_game, full_length=False)
)
# @group_log.command(name='xcheck', description='Defender makes an x-check')
# async def log_xcheck_command(self, interaction: discord.Interaction, position: Literal[
# 'Pitcher', '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['no error', '1 base', '2 bases', '3 bases']):
# this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
# if False in (this_game, owner_team, this_play):
# return
#
# patch_play(this_play.id, locked=False)
#
# 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 != 'no-error' else 0, check_pos=pos)
#
# # Not hit and no error
# if hit_allowed == 'out' and error_allowed == 'no error':
# await interaction.edit_original_response(
# content=f'Just logged the x-check! Please log the resulting play to continue (e.g. \'flyball-b\' or '
# f'\'groundball-a\')'
# )
# patch_play(this_play.id, locked=False)
# return
#
# # Hit and error
# elif hit_allowed != 'out' and error_allowed != 'no error':
# if hit_allowed == 'triple':
# triple(this_play, comp_play=False)
# batter_to_base = 4
# 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
# else:
# num_bases = None
# if error_allowed == 'no error':
# 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)
#
# patch_play(this_play.id, locked=False)
# await interaction.edit_original_response(
# content=None, embed=await self.get_game_state_embed(this_game, full_length=False)
# )
group_show = app_commands.Group(name='show-card', description='Display the player card for an active player')
@group_show.command(name='defense', description='Display a defender\'s player card')
async def show_defense_command(
self, interaction: discord.Interaction, 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_interaction(interaction, block_rollback=True)
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)
)
)
def_team_id = this_game.away_team_id if this_play.inning_half == 'Bot' else this_game.home_team_id
def_team = await db_get('teams', object_id=def_team_id)
embed = get_team_embed(f'{def_team["sname"]} {position}', def_team)
embed.description = f'{defender["p_name"]}'
embed.set_image(url=defender['image'])
if this_game.is_pd:
embed.set_footer(text=f'Paper Dynasty Season {PD_SEASON}', icon_url=IMAGES['logo'])
await interaction.edit_original_response(content=None, embed=embed)
@group_show.command(name='lineup', description='Display a batting team member\'s player card')
async def show_batting_team_command(
self, interaction: discord.Interaction, batting_order: Optional[int] = None,
relative_order: Optional[Literal['on-deck', 'in-the-hole']] = None):
this_game, owner_team, this_play = await self.checks_log_interaction(interaction)
if False in (this_game, owner_team, this_play):
return
if batting_order:
lineup_member = get_one_lineup(
this_game.id, team_id=this_play.batter.team_id, batting_order=batting_order
)
elif relative_order:
modifier = 1 if relative_order == 'on-deck' else 2
b_order = this_play.batter.batting_order + modifier
if b_order == 10:
b_order = 1
if b_order == 11:
b_order = 2
lineup_member = get_one_lineup(
this_game.id, team_id=this_play.batter.team_id, batting_order=b_order
)
else:
await interaction.edit_original_response(
content=f'You need to select either a batting order or relative order.'
)
patch_play(this_play.id, locked=False)
return
this_player = await get_player(game=this_game, lineup_member=lineup_member)
embed = get_team_embed(f'{this_player["team"]["sname"]} #{lineup_member.batting_order} Batter',
this_player['team'])
embed.description = f'{this_player["name"]}'
embed.set_image(url=this_player['image'])
if this_game.is_pd:
embed.set_footer(text=f'Paper Dynasty Season {PD_SEASON}', icon_url=IMAGES['logo'])
await interaction.edit_original_response(content=None, embed=embed)
@commands.command(name='load-ai', hidden=True)
@commands.is_owner()
async def load_ai_command(self, ctx):
await ctx.send(f'{load_ai()}')
# @commands.command(name='pentest', hidden=True)
# @commands.is_owner()
# async def pen_test_command(self, ctx, team_id: int = 3):
# this_pen = get_or_create_bullpen({'id': team_id}, self.bot)
# await ctx.send(f'{this_pen}')
async def setup(bot):
await bot.add_cog(Gameplay(bot))