Batter subs plus cleanup

This commit is contained in:
Cal Corum 2024-12-23 10:09:11 -06:00
parent f9ced5cb9e
commit 7f6472bbc6
7 changed files with 549 additions and 300 deletions

View File

@ -11,9 +11,10 @@ import pygsheets
from sqlmodel import or_
from api_calls import db_get
from command_logic.logic_gameplay import advance_runners, bunts, chaos, complete_game, doubles, flyballs, frame_checks, get_full_roster_from_sheets, get_lineups_from_sheets, checks_log_interaction, complete_play, get_scorebug_embed, groundballs, hit_by_pitch, homeruns, is_game_over, lineouts, manual_end_game, popouts, read_lineup, show_defense_cards, singles, starting_pitcher_dropdown_view, steals, strikeouts, triples, undo_play, update_game_settings, walks, xchecks
from command_logic.logic_gameplay import advance_runners, bunts, chaos, complete_game, doubles, flyballs, frame_checks, get_full_roster_from_sheets, get_lineups_from_sheets, checks_log_interaction, complete_play, get_scorebug_embed, groundballs, hit_by_pitch, homeruns, is_game_over, lineouts, manual_end_game, popouts, read_lineup, show_defense_cards, singles, starting_pitcher_dropdown_view, steals, strikeouts, sub_batter_dropdown_view, triples, undo_play, update_game_settings, walks, xchecks, activate_last_play
from dice import ab_roll
from exceptions import GameNotFoundException, GoogleSheetsException, TeamNotFoundException, PlayNotFoundException, GameException, log_exception
import gauntlets
from helpers import DEFENSE_LITERAL, PD_PLAYERS_ROLE_NAME, get_channel, team_role, user_has_role, random_gif, random_from_list
# from in_game import ai_manager
@ -55,8 +56,16 @@ class Gameplay(commands.Cog):
logger.error(msg=error, stack_info=True)
await ctx.send(f'{error[:1600]}')
async def post_play(self, session: Session, interaction: discord.Interaction, this_play: Play, buffer_message: str = None):
logger.info(f'post_play - Posting new play')
async def post_play(self, session: Session, interaction: discord.Interaction, this_play: Play, buffer_message: str = None, full_length: bool = False):
logger.info(f'post_play - Posting new play: {this_play}')
if this_play is None:
logger.info(f'this_play is None, searching for game in channel {interaction.channel.id}')
this_game = get_channel_game_or_none(session, interaction.channel.id)
this_play = activate_last_play(session, this_game)
if this_play is None:
log_exception(PlayNotFoundException, f'Attempting to display gamestate, but cannot find current play')
if is_game_over(this_play):
logger.info(f'Game {this_play.game.id} seems to be over')
await interaction.edit_original_response(content=f'Looks like this one is over!')
@ -83,39 +92,51 @@ class Gameplay(commands.Cog):
await interaction.channel.send(content=f'I let Cal know his bot is stupid')
scorebug_buttons, this_ab_roll = None, None
scorebug_embed = await get_scorebug_embed(session, this_play.game, full_length=False, classic=CLASSIC_EMBED)
scorebug_embed = await get_scorebug_embed(session, this_play.game, full_length=full_length, classic=CLASSIC_EMBED)
if this_play.game.roll_buttons and interaction.user.id in [this_play.game.away_team.gmid, this_play.game.home_team.gmid]:
logger.info(f'Including scorebug buttons')
scorebug_buttons = ScorebugButtons(this_play, scorebug_embed)
if this_play.on_base_code == 0 and this_play.game.auto_roll and not this_play.batter.team.is_ai and not this_play.is_new_inning:
logger.info(f'Rolling ab')
this_ab_roll = ab_roll(this_play.batter.team, this_play.game, allow_chaos=False)
scorebug_buttons = None
if this_ab_roll is not None and this_ab_roll.d_six_one > 3:
logger.info(f'Setting embed image to pitcher')
scorebug_embed.set_image(url=this_play.pitcher.player.pitcher_card_url)
if buffer_message is not None:
logger.info(f'Posting buffered message')
await interaction.edit_original_response(
content=buffer_message
)
await interaction.channel.send(
sb_message = await interaction.channel.send(
content=None,
embed=scorebug_embed,
view=scorebug_buttons
)
else:
await interaction.edit_original_response(
logger.info(f'Posting unbuffered message')
sb_message = await interaction.edit_original_response(
content=None,
embed=scorebug_embed,
view=scorebug_buttons
)
if this_ab_roll is not None:
logger.info(f'Posting ab roll')
await interaction.channel.send(
content=None,
embeds=this_ab_roll.embeds
)
if scorebug_buttons is not None:
logger.info(f'Posting scorebug buttons roll')
await scorebug_buttons.wait()
if not scorebug_buttons.value:
await sb_message.edit(view=None)
async def complete_and_post_play(self, session: Session, interaction: discord.Interaction, this_play: Play, buffer_message: str = None):
next_play = complete_play(session, this_play)
@ -293,7 +314,7 @@ class Gameplay(commands.Cog):
)
# Get pitchers from rosterlinks
done = await get_full_roster_from_sheets(session, interaction, self.sheets, this_game, human_team, roster.value)
done = await get_full_roster_from_sheets(session, interaction, self.sheets, this_game, human_team, int(roster.value))
logger.info(f'done: {done}')
if done:
sp_view = starting_pitcher_dropdown_view(session, this_game, human_team)
@ -303,7 +324,120 @@ class Gameplay(commands.Cog):
content=f'{away_role.mention} @ {home_role.mention} is set!\n\n'
f'Go ahead and set lineups with the `/read-lineup` command!',
embed=embed
)
)
@group_new_game.command(name='gauntlet', description='Start a new Gauntlet game against an AI')
@app_commands.checks.has_any_role(PD_PLAYERS_ROLE_NAME)
async def new_game_gauntlet_command(self, interaction: discord.Interaction):
await interaction.response.defer()
with Session(engine) as session:
conflict = get_channel_game_or_none(session, interaction.channel_id)
if conflict is not None:
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
if interaction.channel.category is None or interaction.channel.category.name != PUBLIC_FIELDS_CATEGORY_NAME:
await interaction.edit_original_response(
content=f'Why don\'t you head down to one of the Public Fields that way other humans can help if anything pops up?'
)
return
main_team = await get_team_or_none(
session,
gm_id=interaction.user.id
)
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
this_team = await get_team_or_none(
session,
team_abbrev=f'Gauntlet-{main_team.abbrev}'
)
if not this_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
e_query = await db_get('events', params=[('active', True)])
if e_query['count'] == 0:
await interaction.edit_original_response(
content=f'Hm. It looks like there aren\'t any active gauntlets. What do we even pay Cal for?'
)
return
elif e_query['count'] == 1:
this_event = e_query['events'][0]
r_query = await db_get(
'gauntletruns',
params=[('team_id', this_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]
else:
r_query = await db_get(
'gauntletruns',
params=[('team_id', this_team['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 {e_query["events"][0]["name"]}` and we can get you started in no time!'
)
return
else:
this_run = r_query['runs'][0]
this_event = r_query['runs'][0]['gauntlet']
# If not new or after draft, create new AI game
is_home = gauntlets.is_home_team(this_team, this_event, this_run)
opponent = await gauntlets.get_opponent(this_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. Plz ping the shit out of Cal!'
)
return
else:
logger.info(f'opponent: {opponent}')
current = await db_get('current')
game_code = gauntlets.get_game_code(this_team, this_event, this_run)
this_game = Game(
away_team_id=opponent.id if is_home else this_team.id,
home_team_id=this_team.id if is_home else opponent.id,
channel_id=interaction.channel_id,
season=current['season'],
week=current['week'],
first_message=None if interaction.message is None else interaction.message.channel.id,
ai_team='away' if is_home else 'home',
game_type=game_code
)
logger.info(
f'Game {this_game.id} between {this_team.abbrev} and {opponent.abbrev} is posted!'
)
t_role = await team_role(interaction, main_team)
await interaction.edit_original_response(
content=f'Creating this game for {t_role.mention}:\n{this_game}'
)
@commands.command(name='force-endgame', help='Mod: Force a game to end without stats')
async def force_end_game_command(self, ctx: commands.Context):
@ -332,7 +466,8 @@ class Gameplay(commands.Cog):
# if view.value:
if nuke_game:
session.delete(this_game)
this_game.active = False
session.add(this_game)
session.commit()
await ctx.channel.send(content=random_gif(random_from_list(['i killed it', 'deed is done', 'gone forever'])))
else:
@ -419,6 +554,23 @@ class Gameplay(commands.Cog):
# await interaction.edit_original_response(content='Let\'s see, I didn\'t think this game was over...')
await manual_end_game(session, interaction, this_game, current_play=this_play)
group_substitution = app_commands.Group(name='substitute', description='Make a substitution in active game')
@group_substitution.command(name='batter', description='Make a batter substitution')
async def sub_batter_command(self, interaction: discord.Interaction, batting_order: Literal['this-spot', '1', '2', '3', '4', '5', '6', '7', '8', '9'] = 'this-spot'):
with Session(engine) as session:
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='substitute batter')
if batting_order == 'this-spot':
this_order = this_play.batting_order
else:
this_order = int(batting_order)
logger.info(f'sub batter - this_play: {this_play}')
bat_view = sub_batter_dropdown_view(session, this_game, owner_team, this_order)
await interaction.edit_original_response(content=f'### {owner_team.lname} Substitution', view=bat_view)
group_log = app_commands.Group(name='log', description='Log a play in this channel\'s game')
@group_log.command(name='flyball', description='Flyballs: a, b, ballpark, bq, c')
@ -472,20 +624,6 @@ class Gameplay(commands.Cog):
await self.complete_and_post_play(session, interaction, this_play, buffer_message='Single logged' if ((this_play.on_first or this_play.on_second) and single_type == 'uncapped') else None)
# complete_play(session, this_play)
# if ((this_play.on_first or this_play.on_second) and single_type == 'uncapped'):
# await interaction.edit_original_response(content='Single logged')
# await interaction.channel.send(
# content=None,
# embed=await get_scorebug_embed(session, this_play.game, full_length=False, classic=CLASSIC_EMBED)
# )
# else:
# await interaction.edit_original_response(
# content=None,
# embed=await get_scorebug_embed(session, this_play.game, full_length=False, classic=CLASSIC_EMBED)
# )
@group_log.command(name='double', description='Doubles: **, ***, uncapped')
async def log_double(self, interaction: discord.Interaction, double_type: Literal['**', '***', 'uncapped']):
with Session(engine) as session:

View File

@ -15,10 +15,10 @@ from exceptions import *
from helpers import DEFENSE_LITERAL, SBA_COLOR, get_channel
from in_game.game_helpers import legal_check
from in_game.gameplay_models import BattingCard, Game, Lineup, PositionRating, RosterLink, Team, Play
from in_game.gameplay_queries import get_position, get_available_pitchers, get_card_or_none, get_channel_game_or_none, get_db_ready_decisions, get_db_ready_plays, get_game_lineups, get_last_team_play, get_one_lineup, get_player_id_from_dict, get_player_name_from_dict, get_player_or_none, get_sorted_lineups, get_team_or_none, get_players_last_pa, post_game_rewards
from in_game.gameplay_queries import get_available_batters, get_position, get_available_pitchers, get_card_or_none, get_channel_game_or_none, get_db_ready_decisions, get_db_ready_plays, get_game_lineups, get_last_team_play, get_one_lineup, get_player_id_from_dict, get_player_name_from_dict, get_player_or_none, get_sorted_lineups, get_team_or_none, get_players_last_pa, post_game_rewards
from in_game.managerai_responses import DefenseResponse
from utilities.buttons import ButtonOptions, Confirm, ask_confirm
from utilities.dropdown import DropdownView, SelectStartingPitcher, SelectViewDefense
from utilities.dropdown import DropdownView, SelectBatterSub, SelectStartingPitcher, SelectViewDefense
from utilities.embeds import image_embed
from utilities.pages import Pagination
@ -51,7 +51,7 @@ async def get_scorebug_embed(session: Session, this_game: Game, full_length: boo
gt_string = ' - Flashback'
elif 'exhibition' in this_game.game_type:
gt_string = ' - Exhibition'
logger.info(f'gameplay_models - Game.get_scorebug_embed - this_game: {this_game} / gt_string: {gt_string}')
logger.info(f'get_scorebug_embed - this_game: {this_game} / gt_string: {gt_string}')
embed = discord.Embed(
title=f'{this_game.away_team.sname} @ {this_game.home_team.sname}{gt_string}',
@ -64,7 +64,7 @@ async def get_scorebug_embed(session: Session, this_game: Game, full_length: boo
try:
curr_play = this_game.initialize_play(session)
except LineupsMissingException as e:
logger.debug(f'gameplay_models - Game.get_scorebug_embed - Could not initialize play')
logger.debug(f'get_scorebug_embed - Could not initialize play')
if curr_play is not None:
embed.add_field(
@ -72,6 +72,7 @@ async def get_scorebug_embed(session: Session, this_game: Game, full_length: boo
value=curr_play.scorebug_ascii,
inline=False
)
logger.info(f'curr_play: {curr_play}')
def steal_string(batting_card: BattingCard) -> str:
steal_string = '-/- (---)'
@ -244,6 +245,20 @@ def starting_pitcher_dropdown_view(session: Session, this_game: Game, human_team
return DropdownView(dropdown_objects=[sp_selection])
def sub_batter_dropdown_view(session: Session, this_game: Game, human_team: Team, batting_order: int):
batters = get_available_batters(session, this_game, human_team)
logger.info(f'batters: {batters}')
bat_selection = SelectBatterSub(
this_game=this_game,
this_team=human_team,
session=session,
batting_order=batting_order,
options=[SelectOption(label=f'{x.batterscouting.battingcard.hand.upper()} | {x.player.name_with_desc}', value=x.id) for x in batters],
placeholder='Select your Sub'
)
return DropdownView(dropdown_objects=[bat_selection])
async def read_lineup(session: Session, interaction: discord.Interaction, this_game: Game, lineup_team: Team, sheets_auth, lineup: Choice[str], league_name: str):
"""
Commits lineups and rosterlinks
@ -368,10 +383,12 @@ def complete_play(session:Session, this_play: Play):
"""
Commits this_play and new_play
"""
logger.info(f'Completing play {this_play.id} in game {this_play.game.id}')
nso = this_play.starting_outs + this_play.outs
runs_scored = 0
on_first, on_second, on_third = None, None, None
logger.info(f'Running bulk checks')
is_go_ahead = False
if nso >= 3:
switch_sides = True
@ -435,12 +452,14 @@ def complete_play(session:Session, this_play: Play):
obc = get_obc(on_first, on_second, on_third)
logger.info(f'Calculating re24')
this_play.re24 = get_re24(this_play, runs_scored, new_obc=obc, new_starting_outs=nso)
if nbo > 9:
nbo = 1
new_batter = get_one_lineup(session, this_play.game, new_batter_team, batting_order=nbo)
logger.info(f'new_batter: {new_batter}')
new_play = Play(
game=this_play.game,
@ -468,6 +487,10 @@ def complete_play(session:Session, this_play: Play):
this_play.wpa = get_wpa(this_play, new_play)
this_play.locked = False
this_play.complete = True
logger.info(f'this_play: {this_play}')
logger.info(f'new_play: {new_play}')
session.add(this_play)
session.add(new_play)
session.commit()
@ -2069,22 +2092,22 @@ async def xchecks(session: Session, interaction: discord.Interaction, this_play:
elif hit_result == 'G1':
if this_play.on_first is not None and this_play.starting_outs < 2:
this_play = await gb_letter(session, interaction, this_play, 'B', position=this_play.check_pos, defender_is_in=defender_is_in)
this_play = await gb_letter(session, interaction, this_play, 'B', position=this_play.check_pos, defender_is_in=playing_in)
else:
this_play = await gb_letter(session, interaction, this_play, 'A', position=this_play.check_pos, defender_is_in=defender_is_in)
this_play = await gb_letter(session, interaction, this_play, 'A', position=this_play.check_pos, defender_is_in=playing_in)
elif hit_result == 'G2':
if this_play.on_base_code > 0:
this_play = await gb_letter(session, interaction, this_play, 'C', position=this_play.check_pos, defender_is_in=defender_is_in)
this_play = await gb_letter(session, interaction, this_play, 'C', position=this_play.check_pos, defender_is_in=playing_in)
else:
this_play = await gb_letter(session, interaction, this_play, 'B', position=this_play.check_pos, defender_is_in=defender_is_in)
this_play = await gb_letter(session, interaction, this_play, 'B', position=this_play.check_pos, defender_is_in=playing_in)
elif hit_result == 'G3':
if this_play.on_base_code > 0:
this_play = await singles(session, interaction, this_play, '*')
else:
this_play = await gb_letter(session, interaction, this_play, 'C', position=this_play.check_pos, defender_is_in=defender_is_in)
this_play = await gb_letter(session, interaction, this_play, 'C', position=this_play.check_pos, defender_is_in=playing_in)
elif hit_result == 'SPD':
this_play = singles(session, interaction, this_play, '*')
@ -2136,29 +2159,29 @@ async def xchecks(session: Session, interaction: discord.Interaction, this_play:
to_right_side = position in ['1B', '2B']
if 'G3' in hit_result:
if this_play.on_base_code == 2 and not defender_is_in:
if this_play.on_base_code == 2 and not playing_in:
this_play = await gb_result(session, interaction, this_play, 12)
elif defender_is_in and this_play.on_base_code == 5:
elif playing_in and this_play.on_base_code == 5:
this_play = await gb_result(session, interaction, this_play, 7, to_mif, to_right_side)
elif defender_is_in and this_play.on_base_code in [3, 6]:
elif playing_in and this_play.on_base_code in [3, 6]:
this_play = await gb_decide(session, interaction=interaction, this_play=this_play)
elif defender_is_in and this_play.on_base_code == 7:
elif playing_in and this_play.on_base_code == 7:
this_play = await gb_result(session, interaction, this_play, 11)
else:
this_play = await gb_result(session, interaction, this_play, 3)
elif 'G2' in hit_result:
if this_play.on_base_code == 7 and defender_is_in:
if this_play.on_base_code == 7 and playing_in:
this_play = await gb_result(session, interaction, this_play, 11)
elif not defender_is_in and this_play.on_base_code in [3, 6]:
elif not playing_in and this_play.on_base_code in [3, 6]:
this_play = await gb_result(session, interaction, this_play, 5, to_mif=to_mif)
elif defender_is_in and this_play.on_base_code in [3, 5, 6]:
elif playing_in and this_play.on_base_code in [3, 5, 6]:
this_play = await gb_result(session, interaction, this_play, 1)
elif this_play.on_base_code == 2:
@ -2168,16 +2191,16 @@ async def xchecks(session: Session, interaction: discord.Interaction, this_play:
this_play = await gb_result(session, interaction, this_play, 4)
elif 'G1' in hit_result:
if this_play.on_base_code == 7 and defender_is_in:
if this_play.on_base_code == 7 and playing_in:
this_play = await gb_result(session, interaction, this_play, 10)
elif not defender_is_in and this_play.on_base_code == 4:
elif not playing_in and this_play.on_base_code == 4:
this_play = await gb_result(session, interaction, this_play, 13)
elif not defender_is_in and this_play.on_base_code in [3, 6]:
elif not playing_in and this_play.on_base_code in [3, 6]:
this_play = await gb_result(session, interaction, this_play, 3)
elif defender_is_in and this_play.on_base_code in [3, 5, 6]:
elif playing_in and this_play.on_base_code in [3, 5, 6]:
this_play = await gb_result(session, interaction, this_play, 1)
elif this_play.on_base_code == 2:
@ -2264,8 +2287,10 @@ async def xchecks(session: Session, interaction: discord.Interaction, this_play:
def activate_last_play(session: Session, this_game: Game) -> Play:
logger.info(f'Pulling last play to complete and advance')
p_query = session.exec(select(Play).where(Play.game == this_game).order_by(Play.id.desc()).limit(1)).all()
logger.info(f'last play: {p_query[0].id}')
this_play = complete_play(session, p_query[0])
return this_play
@ -2273,6 +2298,7 @@ def activate_last_play(session: Session, this_game: Game) -> Play:
def undo_play(session: Session, this_play: Play):
this_game = this_play.game
after_play_min = max(1, this_play.play_num - 2)
last_two_plays = session.exec(select(Play).where(Play.game == this_game).order_by(Play.id.desc()).limit(2)).all()
@ -2286,12 +2312,27 @@ def undo_play(session: Session, this_play: Play):
last_two_ids = [last_two_plays[0].id, last_two_plays[1].id]
logger.warning(f'Deleting plays: {last_two_ids}')
session.exec(delete(Play).where(Play.id.in_(last_two_ids)))
new_player_ids = []
new_players = session.exec(select(Lineup).where(Lineup.game == this_game, Lineup.after_play >= after_play_min)).all()
logger.info(f'Subs to roll back: {new_players}')
for lineup in new_players:
new_players.append(lineup.id)
old_player = session.get(Lineup, lineup.replacing_id)
old_player.active = True
session.add(old_player)
logger.warning(f'Deleting lineup IDs: {new_player_ids}')
session.exec(delete(Lineup).where(Lineup.id.in_(new_player_ids)))
session.commit()
try:
logger.info(f'Attempting to initialize play for Game {this_game.id}...')
this_play = this_game.initialize_play(session)
logger.info(f'Initialized play: {this_play.id}')
except PlayInitException:
logger.info(f'Plays found, attempting to active the last play')
this_play = activate_last_play(session, this_game)
logger.info(f'Re-activated play: {this_play.id}')

View File

@ -163,192 +163,6 @@ class Game(SQLModel, table=True):
return this_play[0]
else:
return None
# async def get_scorebug_embed(self, session: Session, full_length: bool = True, classic: bool = True) -> discord.Embed:
# gt_string = ' - Unlimited'
# if self.game_type == 'minor-league':
# gt_string = ' - Minor League'
# elif self.game_type == 'major-league':
# gt_string = ' - Major League'
# elif self.game_type == 'hall-of-fame':
# gt_string = ' - Hall of Fame'
# elif 'gauntlet' in self.game_type:
# gt_string = ' - Gauntlet'
# elif 'flashback' in self.game_type:
# gt_string = ' - Flashback'
# elif 'exhibition' in self.game_type:
# gt_string = ' - Exhibition'
# logger.info(f'gameplay_models - Game.get_scorebug_embed - this_game: {self} / gt_string: {gt_string}')
# embed = discord.Embed(
# title=f'{self.away_team.sname} @ {self.home_team.sname}{gt_string}',
# color=int(SBA_COLOR, 16)
# )
# curr_play = self.current_play_or_none(session)
# if curr_play is None:
# try:
# curr_play = self.initialize_play(session)
# except LineupsMissingException as e:
# logger.debug(f'gameplay_models - Game.get_scorebug_embed - Could not initialize play')
# if curr_play is not None:
# embed.add_field(
# name='Game State',
# value=curr_play.scorebug_ascii,
# inline=False
# )
# def steal_string(batting_card: BattingCard) -> str:
# steal_string = '-/- (---)'
# if batting_card.steal_jump > 0:
# jump_chances = round(batting_card.steal_jump * 36)
# if jump_chances == 6:
# good_jump = 7
# elif jump_chances == 5:
# good_jump = 6
# elif jump_chances == 4:
# good_jump = 5
# elif jump_chances == 3:
# good_jump = 4
# elif jump_chances == 2:
# good_jump = 3
# elif jump_chances == 1:
# good_jump = 2
# elif jump_chances == 7:
# good_jump = '4,5'
# elif jump_chances == 8:
# good_jump = '4,6'
# elif jump_chances == 9:
# good_jump = '3-5'
# elif jump_chances == 10:
# good_jump = '2-5'
# elif jump_chances == 11:
# good_jump = '6,7'
# elif jump_chances == 12:
# good_jump = '4-6'
# elif jump_chances == 13:
# good_jump = '2,4-6'
# elif jump_chances == 14:
# good_jump = '3-6'
# elif jump_chances == 15:
# good_jump = '2-6'
# elif jump_chances == 16:
# good_jump = '2,5-6'
# elif jump_chances == 17:
# good_jump = '3,5-6'
# elif jump_chances == 18:
# good_jump = '4-6'
# elif jump_chances == 19:
# good_jump = '2,4-7'
# elif jump_chances == 20:
# good_jump = '3-7'
# elif jump_chances == 21:
# good_jump = '2-7'
# elif jump_chances == 22:
# good_jump = '2-7,12'
# elif jump_chances == 23:
# good_jump = '2-7,11'
# elif jump_chances == 24:
# good_jump = '2,4-8'
# elif jump_chances == 25:
# good_jump = '3-8'
# elif jump_chances == 26:
# good_jump = '2-8'
# elif jump_chances == 27:
# good_jump = '2-8,12'
# elif jump_chances == 28:
# good_jump = '2-8,11'
# elif jump_chances == 29:
# good_jump = '3-9'
# elif jump_chances == 30:
# good_jump = '2-9'
# elif jump_chances == 31:
# good_jump = '2-9,12'
# elif jump_chances == 32:
# good_jump = '2-9,11'
# elif jump_chances == 33:
# good_jump = '2-10'
# elif jump_chances == 34:
# good_jump = '3-11'
# elif jump_chances == 35:
# good_jump = '2-11'
# else:
# good_jump = '2-12'
# steal_string = f'{"*" if batting_card.steal_auto else ""}{good_jump}/- ({batting_card.steal_high}-{batting_card.steal_low})'
# return steal_string
# baserunner_string = ''
# if curr_play.on_first is not None:
# runcard = curr_play.on_first.card.batterscouting.battingcard
# baserunner_string += f'On First: {curr_play.on_first.player.name_card_link('batting')}\nSteal: {steal_string(runcard)}, Run: {runcard.running}'
# if curr_play.on_second is not None:
# baserunner_string += f'On Second: {curr_play.on_second.player.name_card_link('batting')}\nSteal: {steal_string(runcard)}, Run: {runcard.running}'
# if curr_play.on_third is not None:
# baserunner_string += f'On Third: {curr_play.on_third.player.name_card_link('batting')}\nSteal: {steal_string(runcard)}, Run: {runcard.running}'
# logger.info(f'gameplay_models - Game.get_scorebug_embed - baserunner_string: {baserunner_string}')
# pit_string = f'{curr_play.pitcher.player.name_card_link('pitching')}'
# bat_string = f'{curr_play.batter.player.name_card_link('batting')}'
# if len(baserunner_string) > 0:
# pitchingcard = curr_play.pitcher.card.pitcherscouting.pitchingcard
# pit_string += f'\nHold: {pitchingcard.hold}, WP: {pitchingcard.wild_pitch}, Bk: {pitchingcard.balk}'
# # battingcard = curr_play.batter.card.batterscouting.battingcard
# # bat_string += f'\nBunt: {battingcard.bunting}, HnR: {battingcard.hit_and_run}'
# embed.add_field(
# name='Pitcher',
# value=pit_string
# )
# embed.add_field(
# name='Batter',
# value=bat_string
# )
# if len(baserunner_string) > 0:
# embed.add_field(name=' ', value=' ', inline=False)
# embed.add_field(name='Baserunners', value=baserunner_string)
# c_query = session.exec(select(PositionRating).where(PositionRating.player_id == curr_play.catcher.card.player.id, PositionRating.position == 'C', PositionRating.variant == curr_play.catcher.card.variant)).all()
# if len(c_query) > 0:
# catcher_rating = c_query[0]
# else:
# log_exception(PositionNotFoundException, f'No catcher rating found for {curr_play.catcher.card.player.name}')
# cat_string = f'{curr_play.catcher.player.name_card_link('batter')}\nArm: {catcher_rating.arm}'
# embed.add_field(name='Catcher', value=cat_string)
# ai_note = curr_play.ai_note
# logger.info(f'gameplay_models - Game.get_scorebug_embed - ai_note: {ai_note}')
# if len(ai_note) > 0:
# gm_name = self.home_team.gmname if self.ai_team == 'home' else self.away_team.gmname
# embed.add_field(name=f'{gm_name} will...', value=ai_note, inline=False)
# else:
# embed.add_field(name=' ', value=' ', inline=False)
# if full_length:
# embed.add_field(
# name=f'{self.away_team.abbrev} Lineup',
# value=self.team_lineup(session, self.away_team)
# )
# embed.add_field(
# name=f'{self.home_team.abbrev} Lineup',
# value=self.team_lineup(session, self.home_team)
# )
# else:
# embed.add_field(
# name=f'{self.away_team.abbrev} Lineup',
# value=self.team_lineup(session, self.away_team)
# )
# embed.add_field(
# name=f'{self.home_team.abbrev} Lineup',
# value=self.team_lineup(session, self.home_team)
# )
# return embed
def initialize_play(self, session: Session):
"""
@ -722,6 +536,9 @@ class ManagerAi(ManagerAiBase, table=True):
this_resp.outfield_in = True
this_resp.infield_in = True
this_resp.ai_note += f'- play the outfield and infield in'
elif this_play.on_first and this_play.starting_outs == 1:
this_resp.corners_in = True
this_resp.ai_note += f'- play the corners in\n'
elif abs(this_play.away_score - this_play.home_score) <= 3:
this_resp.infield_in = True
this_resp.ai_note += f'- play the whole infield in\n'

View File

@ -129,17 +129,19 @@ async def get_team_or_none(
async def get_player_or_none(session: Session, player_id: int, skip_cache: bool = False) -> Player | None:
logger.info(f'gameplay_models - get_player_or_none - player_id: {player_id}')
logger.info(f'gameplay_models - get_player_or_none - player_id: {player_id} / skip_cache: {skip_cache}')
if not skip_cache:
this_player = session.get(Player, player_id)
if this_player is not None:
logger.info(f'we found a cached player: {this_player} / created: {this_player.created}')
logger.info(f'we found a cached player: {this_player}\ncreated: {this_player.created}')
tdelta = datetime.datetime.now() - this_player.created
logger.debug(f'tdelta: {tdelta}')
logger.info(f'tdelta: {tdelta}')
if tdelta.total_seconds() < CACHE_LIMIT:
logger.info(f'returning this player')
return this_player
else:
logger.warning('Deleting old player record')
session.delete(this_player)
session.commit()
@ -164,37 +166,29 @@ async def get_player_or_none(session: Session, player_id: int, skip_cache: bool
async def get_batter_scouting_or_none(session: Session, card: Card, skip_cache: bool = False) -> BatterScouting | None:
logger.info(f'Getting batting scouting for card ID #{card.id}: {card.player.name_with_desc}')
logger.info(f'Getting batting scouting for card ID #{card.id}: {card.player.name_with_desc} / skip_cache: {skip_cache}')
s_query = await db_get(f'battingcardratings/player/{card.player.id}', none_okay=False)
if s_query['count'] != 2:
log_exception(DatabaseError, f'Scouting for {card.player.name_with_desc} was not found.')
this_scouting = session.get(BattingCard, s_query['ratings'][0]['battingcard']['id'])
this_scouting = session.exec(select(BatterScouting).where(BatterScouting.battingcard_id == s_query['ratings'][0]['battingcard']['id'])).all()
logger.info(f'this_scouting: {this_scouting}')
# this_scouting = session.get(BatterScouting, s_query['ratings'][0]['battingcard']['id'])
if this_scouting is not None:
if len(this_scouting) > 0:
logger.info(f'we found a cached scouting: {this_scouting} / created {this_scouting.created}')
tdelta = datetime.datetime.now() - this_scouting.created
logger.debug(f'tdelta: {tdelta}')
if tdelta.total_seconds() < CACHE_LIMIT:
return this_scouting
else:
session.delete(this_scouting.battingcard)
session.delete(this_scouting.ratings_vl)
session.delete(this_scouting.ratings_vr)
session.delete(this_scouting)
session.commit()
# if not skip_cache and card.batterscouting is not None:
# this_scouting = session.get(BatterScouting, card.batterscouting.id)
# if this_scouting is not None:
# logger.info(f'we found a cached scouting: {this_scouting} / created {this_scouting.created}')
# tdelta = datetime.datetime.now() - this_scouting.created
# logger.debug(f'tdelta: {tdelta}')
# if tdelta.total_seconds() < CACHE_LIMIT:
# return this_scouting
# else:
# session.delete(this_scouting)
# session.commit()
def cache_scouting(batting_card: dict, ratings_vr: dict, ratings_vl: dict) -> BatterScouting:
valid_bc = BattingCardBase.model_validate(batting_card, from_attributes=True)
db_bc = BattingCard.model_validate(valid_bc)
@ -212,8 +206,10 @@ async def get_batter_scouting_or_none(session: Session, card: Card, skip_cache:
)
session.add(db_scouting)
logger.info(f'caching scouting')
session.commit()
session.refresh(db_scouting)
logger.info(f'scouting id: {db_scouting.id} / battingcard: {db_scouting.battingcard.id} / vL: {db_scouting.ratings_vl.id} / vR: {db_scouting.ratings_vr.id}')
return db_scouting
return cache_scouting(
@ -222,16 +218,6 @@ async def get_batter_scouting_or_none(session: Session, card: Card, skip_cache:
ratings_vl=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'L' else s_query['ratings'][1]
)
# s_query = await db_get(f'battingcardratings/player/{card.player.id}', none_okay=False)
# if s_query['count'] == 2:
# return cache_scouting(
# batting_card=s_query['ratings'][0]['battingcard'],
# ratings_vr=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'R' else s_query['ratings'][1],
# ratings_vl=s_query['ratings'][0] if s_query['ratings'][0]['vs_hand'] == 'L' else s_query['ratings'][1]
# )
# return None
async def get_pitcher_scouting_or_none(session: Session, card: Card, skip_cache: bool = False) -> PitcherScouting | None:
logger.info(f'Getting pitching scouting for card ID #{card.id}: {card.player.name_with_desc}')
@ -240,9 +226,11 @@ async def get_pitcher_scouting_or_none(session: Session, card: Card, skip_cache:
if s_query['count'] != 2:
log_exception(DatabaseError, f'Scouting for {card.player.name_with_desc} was not found.')
this_scouting = session.get(PitcherScouting, s_query['ratings'][0]['pitchingcard']['id'])
this_scouting = session.exec(select(PitcherScouting).where(PitcherScouting.pitchingcard_id == s_query['ratings'][0]['pitchingcard']['id'])).all()
logger.info(f'this_scouting: {this_scouting}')
# this_scouting = session.get(PitcherScouting, s_query['ratings'][0]['pitchingcard']['id'])
if this_scouting is not None:
if len(this_scouting) > 0:
logger.info(f'we found a cached scouting: {this_scouting} / created {this_scouting.created}')
tdelta = datetime.datetime.now() - this_scouting.created
logger.debug(f'tdelta: {tdelta}')
@ -251,6 +239,9 @@ async def get_pitcher_scouting_or_none(session: Session, card: Card, skip_cache:
return this_scouting
else:
session.delete(this_scouting.pitchingcard)
session.delete(this_scouting.ratings_vl)
session.delete(this_scouting.ratings_vr)
session.delete(this_scouting)
session.commit()
@ -271,8 +262,10 @@ async def get_pitcher_scouting_or_none(session: Session, card: Card, skip_cache:
)
session.add(db_scouting)
logger.info(f'caching scouting')
session.commit()
session.refresh(db_scouting)
logger.info(f'scouting id: {db_scouting.id} / pitching: {db_scouting.pitchingcard.id} / vL: {db_scouting.ratings_vl.id} / vR: {db_scouting.ratings_vr.id}')
return db_scouting
scouting = cache_scouting(
@ -394,7 +387,7 @@ async def shared_get_scouting(session: Session, this_card: Card, which: Literal[
async def get_position(session: Session, this_card: Card, position: Literal['P', 'C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF'], skip_cache: bool = False) -> PositionRating:
logger.info(f'Pulling position rating for {this_card.player.name_with_desc} at {position}')
logger.info(f'Pulling position rating for {this_card.player.name_with_desc} at {position} / skip_cache: {skip_cache}')
if not skip_cache:
this_pos = session.exec(select(PositionRating).where(PositionRating.player_id == this_card.player.id, PositionRating.position == position, PositionRating.variant == this_card.variant)).all()
logger.info(f'Ratings found: {len(this_pos)}')
@ -435,7 +428,7 @@ async def get_position(session: Session, this_card: Card, position: Literal['P',
async def get_or_create_ai_card(session: Session, player: Player, team: Team, skip_cache: bool = False, dev_mode: bool = False) -> Card:
logger.info(f'Getting or creating card for {player.name_with_desc} on the {team.sname}')
logger.info(f'Getting or creating card for {player.name_with_desc} on the {team.sname} / skip_cache: {skip_cache}')
if not team.is_ai:
err = f'Cannot create AI cards for human teams'
logger.error(f'gameplay_models - get_or_create_ai_card: {err}')
@ -453,6 +446,7 @@ async def get_or_create_ai_card(session: Session, player: Player, team: Team, sk
if tdelta.total_seconds() < CACHE_LIMIT:
return this_card
else:
logger.info(f'deleting card record')
session.delete(this_card)
session.commit()
@ -520,7 +514,7 @@ async def get_or_create_ai_card(session: Session, player: Player, team: Team, sk
async def get_card_or_none(session: Session, card_id: int, skip_cache: bool = False) -> Card | None:
logger.info(f'Getting card {card_id}')
logger.info(f'Getting card {card_id} / skip_cache: {skip_cache}')
if not skip_cache:
this_card = session.get(Card, card_id)
@ -886,8 +880,10 @@ async def post_game_rewards(session: Session, winning_team: Team, losing_team: T
def get_available_subs(session: Session, this_game: Game, this_team: Team) -> list[Card]:
logger.info(f'Getting all available subs')
team_lineups = session.exec(select(Lineup).where(Lineup.game == this_game, Lineup.team == this_team)).all()
used_card_ids = [x.card.id for x in team_lineups]
logger.info(f'USED CARD IDS: {used_card_ids}')
all_roster_links = session.exec(select(RosterLink).where(RosterLink.game == this_game, RosterLink.team == this_team)).all()

View File

@ -41,11 +41,11 @@ class DefenseResponse(AiResponse):
corners_in: bool = False
def defender_in(self, position: str):
if self.infield_in and position in ['C', '1B', '2B', '3B', 'SS', 'P']:
if self.infield_in and (position in ['C', '1B', '2B', '3B', 'SS', 'P']):
return True
elif self.corners_in and position in ['C', '1B', '3B', 'P']:
elif self.corners_in and (position in ['C', '1B', '3B', 'P']):
return True
elif self.outfield_in and position in ['LF', 'CF', 'RF']:
elif self.outfield_in and (position in ['LF', 'CF', 'RF']):
return True
return False

View File

@ -3,6 +3,7 @@ import discord
from typing import Coroutine, Literal
from dice import ab_roll, jump_roll
from exceptions import *
from in_game.gameplay_models import Game, Play, Team
@ -51,34 +52,59 @@ class Confirm(discord.ui.View):
class ButtonOptions(discord.ui.View):
def __init__(self, responders: list, timeout: float = 300.0, labels=None):
def __init__(self, labels: list[str], responders: list, timeout: float = 300.0, disable_chosen: bool = False):
logger.info(f'ButtonOptions - labels: {labels} / responders: {responders} / timeout: {timeout} / disable_chosen: {disable_chosen}')
super().__init__(timeout=timeout)
if not isinstance(responders, list):
raise TypeError('responders must be a list')
if len(labels) > 5 or len(labels) < 1:
log_exception(ValueError, 'ButtonOptions support between 1 and 5 options')
self.value = None
self.responders = responders
self.options = labels
self.disable_chosen = disable_chosen
# if len(labels) == 5:
# for count, x in enumerate(labels):
# if count == 0:
# self.option1.label = x
# if x is None or x.lower() == 'na' or x == 'N/A':
# self.remove_item(self.option1)
# if count == 1:
# self.option2.label = x
# if x is None or x.lower() == 'na' or x == 'N/A':
# self.remove_item(self.option2)
# if count == 2:
# self.option3.label = x
# if x is None or x.lower() == 'na' or x == 'N/A':
# self.remove_item(self.option3)
# if count == 3:
# self.option4.label = x
# if x is None or x.lower() == 'na' or x == 'N/A':
# self.remove_item(self.option4)
# if count == 4:
# self.option5.label = x
# if x is None or x.lower() == 'na' or x == 'N/A':
# self.remove_item(self.option5)
# else:
all_options = [self.option1, self.option2, self.option3, self.option4, self.option5]
logger.info(f'all_options: {all_options}')
for count, x in enumerate(labels):
if count == 0:
self.option1.label = x
if x is None or x.lower() == 'na' or x == 'N/A':
self.remove_item(self.option1)
if count == 1:
self.option2.label = x
if x is None or x.lower() == 'na' or x == 'N/A':
self.remove_item(self.option2)
if count == 2:
self.option3.label = x
if x is None or x.lower() == 'na' or x == 'N/A':
self.remove_item(self.option3)
if count == 3:
self.option4.label = x
if x is None or x.lower() == 'na' or x == 'N/A':
self.remove_item(self.option4)
if count == 4:
self.option5.label = x
if x is None or x.lower() == 'na' or x == 'N/A':
self.remove_item(self.option5)
if x is None or x.lower() == 'na' or x.lower() == 'n/a':
self.remove_item(all_options[count])
else:
all_options[count].label = x
if len(labels) < 2:
self.remove_item(self.option2)
if len(labels) < 3:
self.remove_item(self.option3)
if len(labels) < 4:
self.remove_item(self.option4)
if len(labels) < 5:
self.remove_item(self.option5)
@discord.ui.button(label='Option 1', style=discord.ButtonStyle.primary)
async def option1(self, interaction: discord.Interaction, button: discord.ui.Button):
@ -89,9 +115,11 @@ class ButtonOptions(discord.ui.View):
delete_after=10.0
)
self.value = self.options[0]
self.clear_items()
self.stop()
if self.disable_chosen:
button.disabled = True
self.value = self.options[0]
await interaction.edit_original_response(view=self)
@discord.ui.button(label='Option 2', style=discord.ButtonStyle.primary)
async def option2(self, interaction: discord.Interaction, button: discord.ui.Button):
@ -102,9 +130,11 @@ class ButtonOptions(discord.ui.View):
delete_after=10.0
)
self.value = self.options[1]
self.clear_items()
self.stop()
if self.disable_chosen:
button.disabled = True
self.value = self.options[1]
await interaction.edit_original_response(view=self)
@discord.ui.button(label='Option 3', style=discord.ButtonStyle.primary)
async def option3(self, interaction: discord.Interaction, button: discord.ui.Button):
@ -115,9 +145,11 @@ class ButtonOptions(discord.ui.View):
delete_after=10.0
)
self.value = self.options[2]
self.clear_items()
self.stop()
if self.disable_chosen:
button.disabled = True
self.value = self.options[2]
await interaction.edit_original_response(view=self)
@discord.ui.button(label='Option 4', style=discord.ButtonStyle.primary)
async def option4(self, interaction: discord.Interaction, button: discord.ui.Button):
@ -128,9 +160,11 @@ class ButtonOptions(discord.ui.View):
delete_after=10.0
)
self.value = self.options[3]
self.clear_items()
self.stop()
if self.disable_chosen:
button.disabled = True
self.value = self.options[3]
await interaction.edit_original_response(view=self)
@discord.ui.button(label='Option 5', style=discord.ButtonStyle.primary)
async def option5(self, interaction: discord.Interaction, button: discord.ui.Button):
@ -141,9 +175,11 @@ class ButtonOptions(discord.ui.View):
delete_after=10.0
)
self.value = self.options[4]
self.clear_items()
self.stop()
if self.disable_chosen:
button.disabled = True
self.value = self.options[4]
await interaction.edit_original_response(view=self)
async def ask_confirm(interaction: discord.Interaction, question: str, label_type: Literal['yes', 'confirm'] = 'confirm', timeout: int = 60, delete_question: bool = True, custom_confirm_label: str = None, custom_cancel_label: str = None, embed: discord.Embed = None, delete_embed: bool = False) -> bool:
@ -177,6 +213,87 @@ async def ask_confirm(interaction: discord.Interaction, question: str, label_typ
return False
async def ask_with_buttons(interaction: discord.Interaction, button_options: list[str], question: str = None, timeout: int = 60, delete_question: bool = True, embeds: list[discord.Embed] = None, delete_embeds: bool = False, edit_original_interaction: bool = False, none_okay: bool = False) -> str:
"""
Returns text of button pressed
"""
logger.info(f'ask_with_buttons - button_options: {button_options} / question: {question} / timeout: {timeout} / delete_question: {delete_question} / embeds: {embeds} / delete_embeds: {delete_embeds} / edit_original_transaction: {edit_original_interaction}')
if question is None and embeds is None:
log_exception(KeyError, 'At least one of question or embed must be provided')
view = ButtonOptions(
responders=[interaction.user],
timeout=timeout,
labels=button_options
)
logger.info(f'view: {view}')
# if edit_original_interaction:
# logger.info(f'editing message')
# q_message = await interaction.edit_original_response(
# content=question,
# view=view,
# embeds=embeds
# )
# logger.info(f'edited')
# else:
# logger.info(f'posting message')
# q_message = await interaction.channel.send(
# content=question,
# view=view,
# embeds=embeds
# )
logger.info(f'posting message')
q_message = await interaction.channel.send(
content=question,
view=view,
embeds=embeds
)
await view.wait()
if view.value:
return_val = view.value
else:
return_val = None
if question is not None and embeds is not None:
logger.info(f'checking for deletion with question and embeds')
if delete_question and delete_embeds:
logger.info(f'delete it all')
await q_message.delete()
elif delete_question:
logger.info(f'delete question')
await q_message.edit(
content=None
)
elif delete_embeds:
logger.info(f'delete embeds')
await q_message.edit(
embeds=None
)
elif return_val is None:
logger.info(f'remove view')
await q_message.edit(
view=None
)
elif (question is not None and delete_question) or (embeds is not None and delete_embeds):
logger.info(f'deleting message')
await q_message.delete()
elif return_val is None:
logger.info(f'No reponse, remove view')
await q_message.edit(
view=None
)
if return_val is not None or none_okay:
return return_val
log_exception(ButtonOptionNotChosen, 'Selecting an option is mandatory')
class ScorebugButtons(discord.ui.View):
def __init__(self, play: Play, embed: discord.Embed, timeout: float = 30):
super().__init__(timeout=timeout)

View File

@ -6,10 +6,11 @@ from discord import SelectOption
from discord.utils import MISSING
from sqlmodel import Session
from exceptions import CardNotFoundException, log_exception
from exceptions import CardNotFoundException, PlayNotFoundException, log_exception
from in_game.game_helpers import legal_check
from in_game.gameplay_models import Game, Lineup, Play, Team
from in_game.gameplay_queries import get_position, get_card_or_none
from in_game.gameplay_queries import get_one_lineup, get_position, get_card_or_none
from utilities.buttons import ask_confirm
logger = logging.getLogger('discord_app')
@ -143,6 +144,145 @@ class SelectStartingPitcher(discord.ui.Select):
view=None
)
class SelectSubPosition(discord.ui.Select):
def __init__(self, session: Session, this_lineup: Lineup, custom_id = ..., placeholder = None, options: List[SelectOption] = ...):
self.session = session
self.this_lineup = this_lineup
super().__init__(custom_id=custom_id, placeholder=placeholder, min_values=1, max_values=1, options=options, disabled=False)
async def callback(self, interaction: discord.Interaction):
logger.info(f'Setting sub position to {self.values[0]}')
await interaction.edit_original_response(view=None)
if self.values[0] == 'PH':
await interaction.channel.send(content=f'Their position is set to Pinch Hitter.')
return
else:
await get_position(self.session, self.this_lineup.card_id, position=self.values[0])
self.this_lineup.position = self.values[0]
for option in self.options:
if option.value == self.values[0]:
this_label = option.label
self.this_lineup.position = self.values[0]
self.session.add(self.this_lineup)
self.session.commit()
await interaction.channel.send(content=f'Their position is set to {this_label}.')
class SelectBatterSub(discord.ui.Select):
def __init__(self, this_game: Game, this_team: Team, session: Session, batting_order: int, custom_id: str = MISSING, placeholder: str | None = None, options: List[SelectOption] = ...):
logger.info(f'Inside SelectBatterSub init function')
self.game = this_game
self.team = this_team
self.session = session
# self.league_name = league_name
self.batting_order = batting_order
super().__init__(custom_id=custom_id, placeholder=placeholder, min_values=1, max_values=1, options=options)
async def callback(self, interaction: discord.Interaction):
await interaction.response.defer()
logger.info(f'Setting batter sub to Card ID: {self.values[0]}')
# Get Human batter card
human_batter_card = await get_card_or_none(self.session, card_id=self.values[0])
if human_batter_card is None:
log_exception(CardNotFoundException, f'Card ID {self.values[0]} not found')
if human_batter_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_batter_card.player.name} and belongs to {human_batter_card.team.sname}. Will you double check that before we get started?'
)
return
# 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_batter_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
this_play = self.game.current_play_or_none(self.session)
if this_play is None:
log_exception(PlayNotFoundException, 'Play not found during substitution')
logger.info(f'this_play: {this_play}')
last_lineup = get_one_lineup(
session=self.session,
this_game=self.game,
this_team=self.team,
active=True,
batting_order=self.batting_order
)
same_position = await ask_confirm(
interaction,
question=f'Will **{human_batter_card.player.name}** replace {last_lineup.player.name} as the {last_lineup.position}?',
label_type='yes'
)
if same_position:
position = last_lineup.position
pos_text = ''
view = None
else:
pos_dict_list = {
'Pinch Hitter': 'PH',
'Catcher': 'C',
'First Base': '1B',
'Second Base': '2B',
'Third Base': '3B',
'Shortstop': 'SS',
'Left Field': 'LF',
'Center Field': 'CF',
'Right Field': 'RF',
'Pinch Runner': 'PR',
'Pitcher': 'P'
}
position = 'PH'
pos_text = 'What position will they play?'
options=[SelectSubPosition(label=f'{x}', value=pos_dict_list[x], default=x=='Pinch Hitter') for x in pos_dict_list]
view = DropdownView(dropdown_objects=options)
last_lineup.active = False
self.session.add(last_lineup)
logger.info(f'Set {last_lineup.card.player.name_with_desc} as inactive')
human_bat_lineup = Lineup(
team=self.team,
player=human_batter_card.player,
card=human_batter_card,
position=position,
batting_order=self.batting_order,
game=self.game,
after_play=max(this_play.play_num - 1, 0),
replacing_id=last_lineup.id
)
logger.info(f'new lineup: {human_bat_lineup}')
self.session.add(human_bat_lineup)
# self.session.commit()
try:
logger.info(f'Inserted {human_bat_lineup.card.player.name_with_desc} in the {self.batting_order} spot')
this_play.batter = human_bat_lineup
this_play.batter_pos = position
except Exception as e:
logger.error(e, exc_info=True, stack_info=True)
self.session.add(this_play)
self.session.commit()
await interaction.edit_original_response(
content=f'{human_batter_card.player.name_with_desc} has entered in the {self.batting_order} spot. {pos_text}',
view=view
)