diff --git a/cogs/gameplay.py b/cogs/gameplay.py index 6f28d85..9370b2f 100644 --- a/cogs/gameplay.py +++ b/cogs/gameplay.py @@ -656,14 +656,11 @@ class Gameplay(commands.Cog): confirmation_message='Got it!' ) - sp_view = starting_pitcher_dropdown_view(session, this_game, human_team, this_game.league_name, [interaction.user]) - await interaction.channel.send(content=f'### {human_team.lname} Starting Pitcher', view=sp_view) - + # Read the 9 field players from sheets (this will fail to initialize play without SP) try: - await asyncio.sleep(5) - this_play = await read_lineup( - session, - interaction, + await read_lineup( + session, + interaction, this_game=this_game, lineup_team=human_team, sheets_auth=self.sheets, @@ -671,16 +668,17 @@ class Gameplay(commands.Cog): league_name=this_game.game_type ) except LineupsMissingException as e: - logger.error(f'Attempting to start game, pausing for 5 seconds: {e}') - await asyncio.sleep(5) - - try: - this_play = this_game.current_play_or_none(session) - await self.post_play(session, interaction, this_play, buffer_message='Game on!') - except LineupsMissingException as e: - await interaction.channel.send( - content=f'Run `/gamestate` once you have selected a Starting Pitcher to get going!' - ) + # Expected - can't initialize play without SP yet + logger.info(f'Field player lineup read from sheets, waiting for SP selection: {e}') + + sp_view = starting_pitcher_dropdown_view(session, this_game, human_team, this_game.league_name, [interaction.user]) + await interaction.channel.send(content=f'### {human_team.lname} Starting Pitcher', view=sp_view) + + # Don't try to initialize play immediately - wait for user to select SP + # The play will be initialized when they run /gamestate + await interaction.channel.send( + content=f'Once you\'ve selected your Starting Pitcher, run `/gamestate` to get the game started!' + ) @group_new_game.command(name='exhibition', description='Start a new custom game against an AI') @app_commands.choices( diff --git a/gauntlets.py b/gauntlets.py index 590a33f..2c0696e 100644 --- a/gauntlets.py +++ b/gauntlets.py @@ -423,9 +423,12 @@ async def run_draft(interaction: discord.Interaction, main_team: Team, this_even if this_event['id'] in [1, 2]: max_counts['MVP'] = 2 elif this_event['id'] in [5, 6, 8, 9]: + # Handle draft_team as either Team object or dict + dt_season = draft_team.season if isinstance(draft_team, Team) else draft_team['season'] + dt_id = draft_team.id if isinstance(draft_team, Team) else draft_team['id'] g_query = await db_get( 'games', - params=[('season', draft_team.season), ('team1_id', draft_team.id), ('gauntlet_id', this_event['id'])] + params=[('season', dt_season), ('team1_id', dt_id), ('gauntlet_id', this_event['id'])] ) if g_query['count'] > 4: game_count = g_query['count'] @@ -779,6 +782,14 @@ async def run_draft(interaction: discord.Interaction, main_team: Team, this_even slot_params.extend(params) p_query = await db_get('players/random', params=slot_params) + # Fallback for Event 9 RP shortage + if this_event['id'] == 9 and x == 'RP' and p_query['count'] < 3: + logger.warning(f'Low RP count ({p_query["count"]}) in Event 9, expanding cardsets to 24, 25, 26') + fallback_params = [p for p in slot_params if p[0] != 'cardset_id'] + fallback_params.extend([('cardset_id', 24), ('cardset_id', 25), ('cardset_id', 26)]) + p_query = await db_get('players/random', params=fallback_params) + logger.info(f'Fallback query returned {p_query["count"]} RP options') + if p_query['count'] > 0: # test_player_list = '' # for z in p_query['players']: @@ -1720,10 +1731,13 @@ async def run_draft(interaction: discord.Interaction, main_team: Team, this_even raise KeyError(f'I gotta be honest - I shit the bed here and wasn\'t able to get you enough players to fill ' f'a team. I have to wipe this team, but please draft again after you tell Cal his bot sucks.') + # Handle draft_team as either Team object or dict + draft_team_id = draft_team.id if isinstance(draft_team, Team) else draft_team['id'] + this_pack = await db_post( 'packs/one', payload={ - 'team_id': draft_team.id, + 'team_id': draft_team_id, 'pack_type_id': 2, 'open_time': datetime.datetime.timestamp(datetime.datetime.now()) * 1000 } @@ -1731,13 +1745,13 @@ async def run_draft(interaction: discord.Interaction, main_team: Team, this_even await db_post( 'cards', payload={'cards': [ - {'player_id': x['player_id'], 'team_id': draft_team.id, 'pack_id': this_pack['id']} for x in all_players + {'player_id': x['player_id'], 'team_id': draft_team_id, 'pack_id': this_pack['id']} for x in all_players ]} ) await db_post( 'gauntletruns', payload={ - 'team_id': draft_team.id, + 'team_id': draft_team_id, 'gauntlet_id': this_event['id'] } ) diff --git a/utilities/dropdown.py b/utilities/dropdown.py index 3b7f134..dd17bf9 100644 --- a/utilities/dropdown.py +++ b/utilities/dropdown.py @@ -110,13 +110,13 @@ class SelectViewDefense(discord.ui.Select): class SelectStartingPitcher(discord.ui.Select): def __init__(self, this_game: Game, this_team: Team, session: Session, league_name: str, custom_id: str = MISSING, placeholder: str | None = None, options: List[SelectOption] = ..., responders: list[discord.User] = None) -> None: logger.info(f'Inside SelectStartingPitcher init function') - self.game = this_game - self.team = this_team - self.session = session + # Store IDs instead of objects to avoid session issues + self.game_id = this_game.id + self.team_id = this_team.id self.league_name = league_name self.responders = responders super().__init__(custom_id=custom_id, placeholder=placeholder, options=options) - + async def callback(self, interaction: discord.Interaction): if self.responders is not None and interaction.user not in self.responders: await interaction.response.send_message( @@ -127,52 +127,61 @@ class SelectStartingPitcher(discord.ui.Select): await interaction.response.defer(thinking=True) logger.info(f'SelectStartingPitcher - selection: {self.values[0]}') - # Get Human SP card - human_sp_card = await get_card_or_none(self.session, card_id=self.values[0]) - if human_sp_card is None: - log_exception(CardNotFoundException, f'Card ID {self.values[0]} not found') + # Create a new session for this callback + from in_game.gameplay_models import engine + with Session(engine) as session: + # Get fresh game and team objects + this_game = session.get(Game, self.game_id) + this_team = session.get(Team, self.team_id) - if human_sp_card.team_id != self.team.id: - logger.error(f'Card_id {self.values[0]} does not belong to {self.team.abbrev} in Game {self.game.id}') - await interaction.channel.send( - f'Uh oh. Card ID {self.values[0]} is {human_sp_card.player.name} and belongs to {human_sp_card.team.sname}. Will you double check that before we get started?' - ) - return + # Get Human SP card + human_sp_card = await get_card_or_none(session, card_id=self.values[0]) + if human_sp_card is None: + log_exception(CardNotFoundException, f'Card ID {self.values[0]} not found') - await get_position(self.session, human_sp_card, 'P') - - try: - legal_data = await legal_check([self.values[0]], difficulty_name=self.league_name) - if not legal_data['legal']: - await interaction.edit_original_response( - content=f'It looks like this is a Ranked Legal game and {human_sp_card.player.name_with_desc} is not legal in {self.league_name} games. You can start a new game once you pick a new SP.' + if human_sp_card.team_id != this_team.id: + logger.error(f'Card_id {self.values[0]} does not belong to {this_team.abbrev} in Game {this_game.id}') + await interaction.channel.send( + f'Uh oh. Card ID {self.values[0]} is {human_sp_card.player.name} and belongs to {human_sp_card.team.sname}. Will you double check that before we get started?' ) - return - except LegalityCheckNotRequired: - pass + return - human_sp_lineup = Lineup( - team_id=self.team.id, - player_id=human_sp_card.player.id, - card_id=self.values[0], - position='P', - batting_order=10, - is_fatigued=False, - game=self.game - ) - self.session.add(human_sp_lineup) - self.session.commit() + await get_position(session, human_sp_card, 'P') - logger.info(f'trying to delete interaction: {interaction}') - try: - # await interaction.delete_original_response() - await interaction.edit_original_response( - # content=f'The {self.team.lname} are starting **{human_sp_card.player.name_with_desc}**!\n\nRun `/set lineup` to import your lineup and `/gamestate` if you are ready to play.', - content=f'The {self.team.lname} are starting **{human_sp_card.player.name_with_desc}**!', - view=None + try: + legal_data = await legal_check([self.values[0]], difficulty_name=self.league_name) + if not legal_data['legal']: + await interaction.edit_original_response( + content=f'It looks like this is a Ranked Legal game and {human_sp_card.player.name_with_desc} is not legal in {self.league_name} games. You can start a new game once you pick a new SP.' + ) + return + except (LegalityCheckNotRequired, Exception) as e: + # Skip legality check if not required or if it fails + logger.info(f'Skipping legality check: {e}') + pass + + human_sp_lineup = Lineup( + team_id=this_team.id, + player_id=human_sp_card.player.id, + card_id=self.values[0], + position='P', + batting_order=10, + is_fatigued=False, + game=this_game ) - except Exception as e: - log_exception(e, 'Couldn\'t clean up after selecting sp') + session.add(human_sp_lineup) + session.commit() + + logger.info(f'trying to delete interaction: {interaction}') + try: + # await interaction.delete_original_response() + await interaction.edit_original_response( + # content=f'The {this_team.lname} are starting **{human_sp_card.player.name_with_desc}**!\n\nRun `/set lineup` to import your lineup and `/gamestate` if you are ready to play.', + content=f'The {this_team.lname} are starting **{human_sp_card.player.name_with_desc}**!', + view=None + ) + except Exception as e: + log_exception(e, 'Couldn\'t clean up after selecting sp') class SelectReliefPitcher(discord.ui.Select):