432 lines
16 KiB
Python
432 lines
16 KiB
Python
|
|
import logging
|
|
import discord
|
|
from sqlmodel import Session, select
|
|
from typing import Literal
|
|
|
|
from exceptions import *
|
|
from in_game.game_helpers import legal_check
|
|
from in_game.gameplay_models import Game, Lineup, Team, Play
|
|
from in_game.gameplay_queries import get_card_or_none, get_channel_game_or_none, get_one_lineup, get_team_or_none, get_players_last_pa
|
|
from utilities.buttons import ButtonOptions, Confirm
|
|
from utilities.embeds import image_embed
|
|
from utilities.pages import Pagination
|
|
|
|
|
|
def complete_play(session:Session, this_play: Play):
|
|
this_play.locked = False
|
|
this_play.complete = True
|
|
session.add(this_play)
|
|
session.commit()
|
|
|
|
|
|
async def get_lineups_from_sheets(session: Session, sheets, this_game: Game, this_team: Team, lineup_num: int, roster_num: int) -> list[Lineup]:
|
|
logging.debug(f'sheets: {sheets}')
|
|
|
|
this_sheet = sheets.open_by_key(this_team.gsheet)
|
|
logging.debug(f'this_sheet: {this_sheet}')
|
|
|
|
r_sheet = this_sheet.worksheet_by_title('My Rosters')
|
|
logging.debug(f'r_sheet: {r_sheet}')
|
|
|
|
if lineup_num == 1:
|
|
row_start = 9
|
|
row_end = 17
|
|
else:
|
|
row_start = 18
|
|
row_end = 26
|
|
|
|
if roster_num == 1:
|
|
l_range = f'H{row_start}:I{row_end}'
|
|
elif roster_num == 2:
|
|
l_range = f'J{row_start}:K{row_end}'
|
|
else:
|
|
l_range = f'L{row_start}:M{row_end}'
|
|
|
|
logging.debug(f'l_range: {l_range}')
|
|
raw_cells = r_sheet.range(l_range)
|
|
logging.debug(f'raw_cells: {raw_cells}')
|
|
|
|
try:
|
|
lineup_cells = [(row[0].value, int(row[1].value)) for row in raw_cells]
|
|
logging.debug(f'lineup_cells: {lineup_cells}')
|
|
except ValueError as e:
|
|
logging.error(f'Could not pull roster for {this_team.abbrev}: {e}')
|
|
raise ValueError(f'Uh oh. Looks like your roster might not be saved. I am reading blanks when I try to get the card IDs')
|
|
|
|
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 update and set the lineup again.')
|
|
|
|
this_card = await get_card_or_none(session, card_id=int(row[1]))
|
|
if this_card is None:
|
|
raise LookupError(
|
|
f'Your {row[0].upper()} has a Card ID of {int(row[1])} and I cannot find that card. Did you sell it by chance? Or maybe you sold a duplicate and the bot sold the one you were using?'
|
|
)
|
|
if this_card.team_id != this_team.id:
|
|
raise SyntaxError(f'Easy there, champ. Looks like card ID {row[1]} belongs to the {this_card.team.lname}. Try again with only cards you own.')
|
|
card_id = row[1]
|
|
card_ids.append(str(card_id))
|
|
|
|
this_lineup = Lineup(
|
|
position=row[0].upper(),
|
|
batting_order=index + 1,
|
|
game=this_game,
|
|
team=this_team,
|
|
player=this_card.player,
|
|
card=this_card
|
|
)
|
|
all_lineups.append(this_lineup)
|
|
|
|
legal_data = await legal_check([card_ids], difficulty_name=this_game.game_type)
|
|
logging.debug(f'legal_data: {legal_data}')
|
|
if not legal_data['legal']:
|
|
raise CardLegalityException(f'The following cards appear to be illegal for this game mode:\n{legal_data["error_string"]}')
|
|
|
|
if len(all_lineups) != 9:
|
|
raise Exception(f'I was only able to pull in {len(all_lineups)} batters from Sheets. Please check your saved lineup and try again.')
|
|
|
|
return all_lineups
|
|
|
|
|
|
async def checks_log_interaction(session: Session, interaction: discord.Interaction, command_name: str) -> tuple[Game, Team, Play]:
|
|
await interaction.response.defer(thinking=True)
|
|
this_game = get_channel_game_or_none(session, interaction.channel_id)
|
|
if this_game is None:
|
|
raise GameNotFoundException('I don\'t see an active game in this channel.')
|
|
|
|
owner_team = await get_team_or_none(session, gm_id=interaction.user.id)
|
|
if owner_team is None:
|
|
logging.exception(f'{command_name} command: No team found for GM ID {interaction.user.id}')
|
|
raise TeamNotFoundException(f'Do I know you? I cannot find your team.')
|
|
|
|
if 'gauntlet' in this_game.game_type:
|
|
gauntlet_abbrev = f'Gauntlet-{owner_team.abbrev}'
|
|
owner_team = await get_team_or_none(session, team_abbrev=gauntlet_abbrev)
|
|
if owner_team is None:
|
|
logging.exception(f'{command_name} command: No gauntlet team found with abbrev {gauntlet_abbrev}')
|
|
raise TeamNotFoundException(f'Hm, I was not able to find a gauntlet team for you.')
|
|
|
|
if not owner_team.id in [this_game.away_team_id, this_game.home_team_id]:
|
|
logging.exception(f'{interaction.user.display_name} tried to run a command in Game {this_game.id} when they aren\'t a GM in the game.')
|
|
raise TeamNotFoundException('Bruh. Only GMs of the active teams can log plays.')
|
|
|
|
this_play = this_game.current_play_or_none(session)
|
|
if this_play is None:
|
|
logging.error(f'{command_name} command: No play found for Game ID {this_game.id} - attempting to initialize play')
|
|
this_play = this_game.initialize_play(session)
|
|
|
|
return this_game, owner_team, this_play
|
|
|
|
|
|
def log_run_scored(session: Session, runner: Lineup, is_earned: bool = True):
|
|
last_ab = get_players_last_pa(session, lineup_member=runner)
|
|
last_ab.run = 1
|
|
last_ab.e_run = 1 if is_earned else 0
|
|
session.add(last_ab)
|
|
session.commit()
|
|
return True
|
|
|
|
|
|
def advance_runners(session: Session, this_play: Play, num_bases: int, is_error: bool = False, only_forced: bool = False):
|
|
this_play.rbi = 0
|
|
|
|
if only_forced:
|
|
if not this_play.on_first:
|
|
if this_play.on_second:
|
|
this_play.on_second_final = 2
|
|
if this_play.on_third:
|
|
this_play.on_third_final = 3
|
|
return
|
|
|
|
if this_play.on_second:
|
|
if this_play.on_third:
|
|
if num_bases > 0:
|
|
this_play.on_third_final = 4
|
|
log_run_scored(session, this_play.on_third)
|
|
this_play.rbi += 1 if not is_error else 0
|
|
|
|
if num_bases > 1:
|
|
this_play.on_second_final = 4
|
|
log_run_scored(session, this_play.on_second)
|
|
this_play.rbi += 1 if not is_error else 0
|
|
elif num_bases == 1:
|
|
this_play.on_second_final = 3
|
|
else:
|
|
this_play.on_second_final = 2
|
|
else:
|
|
if this_play.on_third:
|
|
this_play.on_third_final = 3
|
|
|
|
if num_bases > 2:
|
|
this_play.on_first_final = 4
|
|
log_run_scored(session, this_play.on_first)
|
|
this_play.rbi += 1 if not is_error else 0
|
|
elif num_bases == 2:
|
|
this_play.on_first_final = 3
|
|
elif num_bases == 1:
|
|
this_play.on_first_final = 2
|
|
else:
|
|
this_play.on_first_final = 1
|
|
|
|
else:
|
|
if this_play.on_third:
|
|
if num_bases > 0:
|
|
this_play.on_third_final = 4
|
|
log_run_scored(session, this_play.on_third)
|
|
this_play.rbi += 1 if not is_error else 0
|
|
else:
|
|
this_play.on_third_final = 3
|
|
|
|
if this_play.on_second:
|
|
if num_bases > 1:
|
|
this_play.on_second_final = 4
|
|
log_run_scored(session, this_play.on_second)
|
|
this_play.rbi += 1 if not is_error else 0
|
|
elif num_bases == 1:
|
|
this_play.on_second_final = 3
|
|
else:
|
|
this_play.on_second_final = 2
|
|
|
|
if this_play.on_first:
|
|
if num_bases > 2:
|
|
this_play.on_first_final = 4
|
|
log_run_scored(session, this_play.on_first)
|
|
this_play.rbi += 1 if not is_error else 0
|
|
elif num_bases == 2:
|
|
this_play.on_first_final = 3
|
|
elif num_bases == 1:
|
|
this_play.on_first_final = 2
|
|
else:
|
|
this_play.on_first_final = 1
|
|
|
|
if num_bases == 4:
|
|
this_play.batter_final = 4
|
|
this_play.rbi += 1
|
|
this_play.run = 1
|
|
|
|
|
|
async def show_outfield_cards(session: Session, interaction: discord.Interaction, this_play: Play):
|
|
lf = get_one_lineup(session, this_game=this_play.game, this_team=this_play.pitcher.team, position='LF')
|
|
cf = get_one_lineup(session, this_game=this_play.game, this_team=this_play.pitcher.team, position='CF')
|
|
rf = get_one_lineup(session, this_game=this_play.game, this_team=this_play.pitcher.team, position='RF')
|
|
this_team = this_play.pitcher.team
|
|
logging.debug(f'lf: {lf.player.name_with_desc}\n\ncf: {cf.player.name_with_desc}\n\nrf: {rf.player.name_with_desc}\n\nteam: {this_team.lname}')
|
|
|
|
view = Pagination([interaction.user], timeout=10)
|
|
view.left_button.label = f'Left Fielder'
|
|
view.left_button.style = discord.ButtonStyle.secondary
|
|
lf_embed = image_embed(
|
|
image_url=lf.player.image,
|
|
title=f'{this_team.sname} LF',
|
|
color=this_team.color,
|
|
desc=lf.player.name,
|
|
author_name=this_team.lname,
|
|
author_icon=this_team.logo
|
|
)
|
|
|
|
view.cancel_button.label = f'Center Fielder'
|
|
view.cancel_button.style = discord.ButtonStyle.blurple
|
|
cf_embed = image_embed(
|
|
image_url=cf.player.image,
|
|
title=f'{this_team.sname} CF',
|
|
color=this_team.color,
|
|
desc=cf.player.name,
|
|
author_name=this_team.lname,
|
|
author_icon=this_team.logo
|
|
)
|
|
|
|
view.right_button.label = f'Right Fielder'
|
|
view.right_button.style = discord.ButtonStyle.secondary
|
|
rf_embed = image_embed(
|
|
image_url=rf.player.image,
|
|
title=f'{this_team.sname} RF',
|
|
color=this_team.color,
|
|
desc=rf.player.name,
|
|
author_name=this_team.lname,
|
|
author_icon=this_team.logo
|
|
)
|
|
|
|
page_num = 1
|
|
embeds = [lf_embed, cf_embed, rf_embed]
|
|
|
|
msg = await interaction.channel.send(embed=embeds[page_num], view=view)
|
|
|
|
await view.wait()
|
|
|
|
if view.value:
|
|
if view.value == 'left':
|
|
page_num = 0
|
|
if view.value == 'cancel':
|
|
page_num = 1
|
|
if view.value == 'right':
|
|
page_num = 2
|
|
else:
|
|
await msg.edit(content=None, embed=embeds[page_num], view=None)
|
|
|
|
view.value = None
|
|
|
|
if page_num == 0:
|
|
view.left_button.style = discord.ButtonStyle.blurple
|
|
view.cancel_button.style = discord.ButtonStyle.secondary
|
|
view.right_button.style = discord.ButtonStyle.secondary
|
|
if page_num == 1:
|
|
view.left_button.style = discord.ButtonStyle.secondary
|
|
view.cancel_button.style = discord.ButtonStyle.blurple
|
|
view.right_button.style = discord.ButtonStyle.secondary
|
|
if page_num == 2:
|
|
view.left_button.style = discord.ButtonStyle.secondary
|
|
view.cancel_button.style = discord.ButtonStyle.secondary
|
|
view.right_button.style = discord.ButtonStyle.blurple
|
|
|
|
view.left_button.disabled = True
|
|
view.cancel_button.disabled = True
|
|
view.right_button.disabled = True
|
|
|
|
await msg.edit(content=None, embed=embeds[page_num], view=view)
|
|
return [lf, cf, rf][page_num]
|
|
|
|
|
|
async def flyballs(session: Session, interaction: discord.Interaction, this_game: Game, this_play: Play, flyball_type: Literal['a', 'ballpark', 'b', 'b?', 'c'], comp_play: bool = True) -> Play:
|
|
num_outs = 1
|
|
|
|
if flyball_type == 'a':
|
|
this_play.pa = 1
|
|
this_play.ab = 1
|
|
this_play.outs = 1
|
|
|
|
if this_play.starting_outs < 2:
|
|
advance_runners(session, this_play, num_bases=1)
|
|
|
|
if this_play.on_third:
|
|
this_play.ab = 0
|
|
|
|
session.add(this_play)
|
|
session.commit()
|
|
|
|
elif flyball_type == 'b' or flyball_type == 'ballpark':
|
|
this_play.pa, this_play.ab, this_play.outs = 1, 1, 1
|
|
this_play.bpfo = 1 if flyball_type == 'ballpark' else 0
|
|
advance_runners(session, this_play, num_bases=0)
|
|
|
|
if this_play.starting_outs < 2 and this_play.on_third:
|
|
this_play.ab = 0
|
|
this_play.rbi = 1
|
|
this_play.on_third_final = 4
|
|
log_run_scored(session, this_play.on_third)
|
|
|
|
if this_play.starting_outs < 2 and this_play.on_second:
|
|
logging.debug(f'calling of embed')
|
|
await show_outfield_cards(session, interaction, this_play)
|
|
logging.debug(f'done with of embed')
|
|
|
|
runner = this_play.on_second.player
|
|
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
|
|
|
|
if this_play.ai_is_batting:
|
|
tag_resp = this_play.managerai.tag_from_second(session, this_game)
|
|
q_text = f'{runner.name} will attempt to advance to third if the safe range is **{tag_resp.min_safe}+**, are they going?'
|
|
else:
|
|
q_text = f'Is {runner.name} attempting to tag up to third?'
|
|
|
|
question = await interaction.channel.send(
|
|
content=q_text,
|
|
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.name} tagging from second?', view=view
|
|
)
|
|
await view.wait()
|
|
|
|
if view.value:
|
|
await question.delete()
|
|
if view.value == 'Tagged Up':
|
|
this_play.on_second_final = 3
|
|
elif view.value == 'Out at 3rd':
|
|
num_outs += 1
|
|
this_play.on_second_final = None
|
|
this_play.outs = num_outs
|
|
else:
|
|
await question.delete()
|
|
else:
|
|
await question.delete()
|
|
|
|
session.add(this_play)
|
|
session.commit()
|
|
|
|
elif flyball_type == 'b?':
|
|
this_play.pa, this_play.ab, this_play.outs = 1, 1, 1
|
|
|
|
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')
|
|
|
|
runner = this_play.on_second.player
|
|
view = Confirm(responders=[interaction.user], timeout=60, label_type='yes')
|
|
|
|
if this_play.ai_is_batting:
|
|
tag_resp = this_play.managerai.tag_from_second(session, this_game)
|
|
q_text = f'{runner.name} will attempt to advance home if the safe range is **{tag_resp.min_safe}+**, are they going?'
|
|
else:
|
|
q_text = f'Is {runner.name} attempting to tag up and go home?'
|
|
|
|
question = await interaction.channel.send(
|
|
content=q_text,
|
|
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.name} thrown out?', view=view
|
|
)
|
|
await view.wait()
|
|
|
|
if view.value:
|
|
await question.delete()
|
|
num_outs += 1
|
|
this_play.on_third_final = 99
|
|
this_play.outs = num_outs
|
|
else:
|
|
await question.delete()
|
|
this_play.ab = 0
|
|
this_play.rbi = 1
|
|
this_play.on_third_final = 4
|
|
log_run_scored(session, this_play.on_third)
|
|
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)
|
|
|
|
session.refresh(this_play)
|
|
return this_play
|
|
|
|
|
|
|
|
|