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 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 ` 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 ` 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))