Added AbRoll and JumpRoll

Added auto_roll and roll_buttons to Game
Added log chaos
This commit is contained in:
Cal Corum 2024-11-17 14:57:54 -06:00
parent baffabfe4c
commit f7685ff0e3
10 changed files with 841 additions and 156 deletions

View File

@ -8,7 +8,8 @@ from discord.ext import commands, tasks
import pygsheets
from api_calls import db_get
from command_logic.logic_gameplay import advance_runners, bunts, complete_game, doubles, flyballs, get_lineups_from_sheets, checks_log_interaction, complete_play, hit_by_pitch, homeruns, is_game_over, popouts, show_defense_cards, singles, strikeouts, triples, undo_play, walks
from command_logic.logic_gameplay import advance_runners, bunts, chaos, complete_game, doubles, flyballs, get_lineups_from_sheets, checks_log_interaction, complete_play, get_scorebug_embed, hit_by_pitch, homeruns, is_game_over, popouts, show_defense_cards, singles, strikeouts, triples, undo_play, update_game_settings, walks
from dice import ab_roll
from exceptions import GameNotFoundException, TeamNotFoundException, PlayNotFoundException, GameException, log_exception
from helpers import DEFENSE_LITERAL, PD_PLAYERS_ROLE_NAME, get_channel, team_role, user_has_role, random_gif, random_from_list
@ -18,7 +19,7 @@ from in_game.game_helpers import PUBLIC_FIELDS_CATEGORY_NAME, legal_check
from in_game.gameplay_models import Lineup, Play, Session, engine, player_description, select, Game
from in_game.gameplay_queries import get_and_cache_position, get_channel_game_or_none, get_active_games_by_team, get_game_lineups, get_team_or_none, get_card_or_none
from utilities.buttons import Confirm, ask_confirm
from utilities.buttons import Confirm, ScorebugButtons, ask_confirm
CLASSIC_EMBED = True
@ -51,7 +52,9 @@ class Gameplay(commands.Cog):
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')
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!')
submit_game = await ask_confirm(
interaction=interaction,
@ -61,6 +64,7 @@ class Gameplay(commands.Cog):
)
if submit_game:
logger.info(f'post_play - is_game_over - {interaction.user.display_name} rejected game completion')
await complete_game(session, interaction, this_play)
return
else:
@ -74,22 +78,45 @@ class Gameplay(commands.Cog):
await self.post_play(session, interaction, this_play)
await interaction.channel.send(content=f'I let Cal know his bot is stupid')
elif buffer_message is not None:
scorebug_buttons, this_ab_roll = None, None
if this_play.game.roll_buttons and interaction.user.id in [this_play.game.away_team.gmid, this_play.game.home_team.gmid]:
scorebug_buttons = ScorebugButtons(this_play)
if this_play.on_base_code == 0 and this_play.game.auto_roll and not this_play.batter.team.is_ai:
this_ab_roll = ab_roll(this_play.batter.team, this_play.game, allow_chaos=False)
scorebug_buttons = None
scorebug_embed = await get_scorebug_embed(session, this_play.game, full_length=False, classic=CLASSIC_EMBED)
if this_ab_roll is not None and this_ab_roll.d_six_one > 3:
scorebug_embed.set_image(url=this_play.pitcher.player.pitcher_card_url)
if buffer_message is not None:
await interaction.edit_original_response(
content=buffer_message
)
await interaction.channel.send(
content=None,
embed=this_play.game.get_scorebug_embed(session, full_length=False, classic=CLASSIC_EMBED)
embed=scorebug_embed,
view=scorebug_buttons
)
else:
await interaction.edit_original_response(
content=None,
embed=this_play.game.get_scorebug_embed(session, full_length=False, classic=CLASSIC_EMBED)
)
embed=scorebug_embed,
view=scorebug_buttons
)
if this_ab_roll is not None:
await interaction.channel.send(
content=None,
embeds=this_ab_roll.embeds
)
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)
logger.info(f'Completed play {this_play.id}')
await self.post_play(session, interaction, next_play, buffer_message)
group_new_game = app_commands.Group(name='new-game', description='Start a new baseball game')
@ -272,7 +299,8 @@ class Gameplay(commands.Cog):
away_role = await team_role(interaction, away_team)
home_role = await team_role(interaction, home_team)
embed = this_game.get_scorebug_embed(session).clear_fields()
embed = await get_scorebug_embed(session, this_game)
embed.clear_fields()
embed.add_field(
name=f'{ai_team.abbrev} Lineup',
value=this_game.team_lineup(session, ai_team)
@ -296,7 +324,7 @@ class Gameplay(commands.Cog):
try:
await ctx.send(
content=None,
embed=this_game.get_scorebug_embed(session, full_length=True)
embed=await get_scorebug_embed(session, this_game, full_length=True)
)
except Exception as e:
logger.error(f'Unable to display scorebug while forcing game to end: {e}')
@ -385,7 +413,8 @@ class Gameplay(commands.Cog):
if batter.position != 'DH':
await get_and_cache_position(session, batter.card, batter.position)
await interaction.edit_original_response(content=None, embed=this_game.get_scorebug_embed(session))
this_play = this_game.initialize_play(session)
await self.post_play(session, interaction, this_play)
@app_commands.command(name='gamestate', description='Post the current game state')
async def gamestate_command(self, interaction: discord.Interaction, include_lineups: bool = False):
@ -399,10 +428,25 @@ class Gameplay(commands.Cog):
)
return
await interaction.edit_original_response(
content=None,
embed=this_game.get_scorebug_embed(session, full_length=include_lineups)
)
this_play = this_game.current_play_or_none(session)
await self.post_play(session, interaction, this_play)
@app_commands.command(name='settings-ingame', description='Change in-game settings')
@app_commands.describe(
roll_buttons='Display the "Roll AB" and "Check Jump" buttons along with the scorebug',
auto_roll='When there are no baserunners, automatically roll the next AB'
)
async def game_settings_command(self, interaction: discord.Interaction, roll_buttons: bool = None, auto_roll: bool = None):
with Session(engine) as session:
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='settings-ingame')
await interaction.edit_original_response(content=None, embed=await update_game_settings(
session,
interaction,
this_game,
roll_buttons=roll_buttons,
auto_roll=auto_roll
))
group_log = app_commands.Group(name='log', description='Log a play in this channel\'s game')
@ -427,10 +471,10 @@ class Gameplay(commands.Cog):
with Session(engine) as session:
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log single')
logger.info(f'log single {single_type} - this_play: {this_play}')
this_play = await singles(session, interaction, this_play, single_type)
logger.info(f'log single {single_type} - this_play: {this_play}')
await self.complete_and_post_play(session, interaction, this_play, buffer_message='Double logged' if ((this_play.on_first or this_play.on_second) and single_type == 'uncapped') else None)
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)
@ -438,12 +482,12 @@ class Gameplay(commands.Cog):
# await interaction.edit_original_response(content='Single logged')
# await interaction.channel.send(
# content=None,
# embed=this_play.game.get_scorebug_embed(session, full_length=False, classic=CLASSIC_EMBED)
# embed=await get_scorebug_embed(session, this_play.game, full_length=False, classic=CLASSIC_EMBED)
# )
# else:
# await interaction.edit_original_response(
# content=None,
# embed=this_play.game.get_scorebug_embed(session, full_length=False, classic=CLASSIC_EMBED)
# embed=await get_scorebug_embed(session, this_play.game, full_length=False, classic=CLASSIC_EMBED)
# )
@group_log.command(name='double', description='Doubles: **, ***, uncapped')
@ -515,6 +559,23 @@ class Gameplay(commands.Cog):
this_play = await hit_by_pitch(session, interaction, this_play)
await self.complete_and_post_play(session, interaction, this_play)
@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']):
with Session(engine) as session:
this_game, owner_team, this_play = await checks_log_interaction(session, interaction, command_name='log hit-by-pitch')
if this_play.on_base_code == 0:
await interaction.edit_original_response(
content=f'There cannot be chaos when the bases are empty.'
)
return
logger.info(f'log chaos - this_play: {this_play}')
this_play = await chaos(session, interaction, this_play, chaos_type)
await self.complete_and_post_play(session, interaction, this_play)
@group_log.command(name='bunt', description='Bunts: sacrifice, bad, popout, double-play, defense')
async def log_sac_bunt(self, interaction: discord.Interaction, bunt_type: Literal['sacrifice', 'bad', 'popout', 'double-play', 'defense']):
@ -548,6 +609,7 @@ class Gameplay(commands.Cog):
await self.post_play(session, interaction, this_play)
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: DEFENSE_LITERAL):
with Session(engine) as session:

View File

@ -107,11 +107,14 @@ class Owner(commands.Cog):
elif spec == '!':
fmt = await ctx.bot.tree.sync()
await ctx.send(f'Synced {len(fmt)} commands globally.')
ctx.bot.tree.clear_commands(guild=ctx.guild)
await ctx.send(f'Cleared all local commands.')
if len(fmt) > 0:
ctx.bot.tree.copy_global_to(guild=ctx.guild)
fmt = await ctx.bot.tree.sync(guild=ctx.guild)
await ctx.send(f'Synced global commands to this guild.')
ctx.bot.tree.clear_commands(guild=ctx.guild)
await ctx.send(f'Cleared all local commands.')
else:
fmt = await ctx.bot.tree.sync()

View File

@ -9,9 +9,9 @@ from typing import Literal
from api_calls import db_delete, db_get, db_post
from exceptions import *
from helpers import DEFENSE_LITERAL, get_channel
from helpers import DEFENSE_LITERAL, SBA_COLOR, get_channel
from in_game.game_helpers import legal_check
from in_game.gameplay_models import Game, Lineup, Team, Play
from in_game.gameplay_models import BattingCard, Game, Lineup, PositionRating, Team, Play
from in_game.gameplay_queries import get_and_cache_position, get_card_or_none, get_channel_game_or_none, get_db_ready_decisions, get_db_ready_plays, 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 utilities.buttons import ButtonOptions, Confirm, ask_confirm
from utilities.dropdown import DropdownOptions, DropdownView, SelectViewDefense
@ -33,6 +33,196 @@ AT_BASE = {
}
async def get_scorebug_embed(session: Session, this_game: Game, full_length: bool = True, classic: bool = True) -> discord.Embed:
gt_string = ' - Unlimited'
if this_game.game_type == 'minor-league':
gt_string = ' - Minor League'
elif this_game.game_type == 'major-league':
gt_string = ' - Major League'
elif this_game.game_type == 'hall-of-fame':
gt_string = ' - Hall of Fame'
elif 'gauntlet' in this_game.game_type:
gt_string = ' - Gauntlet'
elif 'flashback' in this_game.game_type:
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}')
embed = discord.Embed(
title=f'{this_game.away_team.sname} @ {this_game.home_team.sname}{gt_string}',
color=int(SBA_COLOR, 16)
)
curr_play = this_game.current_play_or_none(session)
if curr_play is None:
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')
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}\n'
if curr_play.on_second is not None:
runcard = curr_play.on_second.card.batterscouting.battingcard
baserunner_string += f'On Second: {curr_play.on_second.player.name_card_link('batting')}\nSteal: {steal_string(runcard)}, Run: {runcard.running}\n'
if curr_play.on_third is not None:
runcard = curr_play.on_third.card.batterscouting.battingcard
baserunner_string += f'On Third: {curr_play.on_third.player.name_card_link('batting')}\nSteal: {steal_string(runcard)}, Run: {runcard.running}\n'
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
)
embed.set_image(url=curr_play.batter.player.batter_card_url)
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 = this_game.home_team.gmname if this_game.ai_team == 'home' else this_game.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'{this_game.away_team.abbrev} Lineup',
value=this_game.team_lineup(session, this_game.away_team)
)
embed.add_field(
name=f'{this_game.home_team.abbrev} Lineup',
value=this_game.team_lineup(session, this_game.home_team)
)
else:
embed.add_field(
name=f'{this_game.away_team.abbrev} Lineup',
value=this_game.team_lineup(session, this_game.away_team)
)
embed.add_field(
name=f'{this_game.home_team.abbrev} Lineup',
value=this_game.team_lineup(session, this_game.home_team)
)
return embed
def get_obc(on_first = None, on_second = None, on_third = None) -> int:
if on_third is not None:
if on_second is not None:
@ -1193,6 +1383,46 @@ async def bunts(session: Session, interaction: discord.Interaction, this_play: P
return this_play
async def chaos(session: Session, interaction: discord.Interaction, this_play: Play, chaos_type: Literal['wild-pitch', 'passed-ball', 'balk', 'pickoff']):
"""
Commits this_play
"""
this_play.pa, this_play.ab = 0, 0
if chaos_type == 'wild-pitch':
this_play = advance_runners(session, this_play, 1)
this_play.rbi = 0
this_play.wild_pitch = 1
elif chaos_type == 'passed-ball':
this_play = advance_runners(session, this_play, 1)
this_play.rbi = 0
this_play.passed_ball = 1
elif chaos_type == 'balk':
this_play = advance_runners(session, this_play, 1)
this_play.rbi = 0
this_play.balk = 1
elif chaos_type == 'pickoff':
this_play = advance_runners(session, this_play, 0)
this_play.pick_off = 1
this_play.outs = 1
if this_play.on_third:
this_play.on_third_final = None
elif this_play.on_second:
this_play.on_second_final = None
elif this_play.on_first:
this_play.on_first_final = None
session.add(this_play)
session.commit()
session.refresh(this_play)
return this_play
def activate_last_play(session: Session, this_game: Game) -> Play:
p_query = session.exec(select(Play).where(Play.game == this_game).order_by(Play.id.desc()).limit(1)).all()
@ -1596,3 +1826,29 @@ async def complete_game(session: Session, interaction: discord.Interaction, this
session.commit()
logger.info(f'Just ended game {game_id}')
async def update_game_settings(session: Session, interaction: discord.Interaction, this_game: Game, roll_buttons: bool = None, auto_roll: bool = None) -> discord.Embed:
if roll_buttons is not None:
this_game.roll_buttons = roll_buttons
if auto_roll is not None:
this_game.auto_roll = auto_roll
session.add(this_game)
session.commit()
session.refresh(this_game)
this_team = this_game.away_team if this_game.away_team.gmid == interaction.user.id else this_game.home_team
embed = this_team.embed
embed.title = f'Game Settings - {this_team.lname}'
embed.add_field(
name='Roll Buttons',
value=f'{"ON" if this_game.roll_buttons else "OFF"}'
)
embed.add_field(
name='Auto Roll',
value=f'{"ON" if this_game.auto_roll else "OFF"}'
)
return embed

162
dice.py
View File

@ -1,13 +1,33 @@
import logging
import discord
import random
import pydantic
from helpers import INFIELD_X_CHART, OUTFIELD_X_CHART
from in_game.gameplay_models import Game, Team
logger = logging.getLogger('discord_app')
def get_dice_embed(team: dict = None, embed_title: str = None):
class DiceRoll(pydantic.BaseModel):
model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)
roll_message: str | None = None
embeds: list[discord.Embed] | None = None
class AbRoll(DiceRoll):
is_chaos: bool = False
d_six_one: int | None = None
d_six_two: int | None = None
d_six_three: int | None = None
d_twenty: int | None = None
def get_dice_embed(team: Team = None, embed_title: str = None):
if team:
embed = discord.Embed(
color=int(team["dice_color"], 16) if 'dice_color' in team else int(team["color"], 16)
color=int(team.color, 16)
)
else:
embed = discord.Embed(
@ -20,7 +40,7 @@ def get_dice_embed(team: dict = None, embed_title: str = None):
return embed
def sa_fielding_roll(pos_code: str, team: dict) -> [discord.Embed]:
def sa_fielding_roll(pos_code: str, team: dict) -> list[discord.Embed]:
"""
Make a Super Advanced fielding check.
"""
@ -622,40 +642,50 @@ def sa_fielding_roll(pos_code: str, team: dict) -> [discord.Embed]:
'#heading=h.fpjqmiv10r8l'
# Build range note
if d_twenty <= 2:
if d_twenty == 1:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'G3 ------SI1------\n' \
'G3# ------SI1------\n' \
'```\n'
elif d_twenty == 3:
elif d_twenty == 2:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'--G3--- ----SI1----\n' \
'G2# ------SI1------\n' \
'```\n'
elif d_twenty == 4:
elif d_twenty <= 4:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'----G3----- --SI1--\n' \
'G2# G3# ----SI1----\n' \
'```\n'
elif d_twenty <= 6:
elif d_twenty == 5:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'------G3------- SI1\n' \
'G2 --G3#-- --SI1--\n' \
'```\n'
elif d_twenty == 7:
elif d_twenty == 6:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'--------G3---------\n' \
'G1 G2# G3# --SI1--\n' \
'```\n'
elif d_twenty <= 9:
elif d_twenty <= 8:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'G2 ------G3-------\n' \
'G1 G2 --G3#-- SI1\n' \
'```\n'
elif d_twenty == 9:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'G1 G2 G3 --G3#--\n' \
'```\n'
elif d_twenty == 10:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'--G1--- G2 --G3#--\n' \
'```\n'
elif d_twenty <= 12:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'G1 G2 ----G3-----\n' \
'--G1--- G2 G3 G3#\n' \
'```\n'
elif d_twenty == 13:
range_note = '```\n' \
@ -670,12 +700,12 @@ def sa_fielding_roll(pos_code: str, team: dict) -> [discord.Embed]:
elif d_twenty <= 16:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'--G1--- ----G2-----\n' \
'----G1----- G2 G3\n' \
'```\n'
elif d_twenty <= 18:
range_note = '```\n' \
' 1 | 2 | 3 | 4 | 5\n' \
'----G1----- --G2---\n' \
'------G1------- G3\n' \
'```\n'
elif d_twenty == 19:
range_note = '```\n' \
@ -791,3 +821,99 @@ def frame_plate_check(team: dict, game_id: int):
roll_embed.set_footer(text="This result will be logged automatically")
return {'embed': roll_embed, 'is_walk': d_twenty <= this_ump['walk_d20']}
def ab_roll(this_team: Team, this_game: Game, allow_chaos: bool = True) -> AbRoll:
"""
Make an AB roll
"""
d_twenty = random.randint(1, 20)
d_twenty_two = random.randint(1, 20)
flag = None
if allow_chaos:
if d_twenty == 1:
flag = 'wild pitch'
elif d_twenty == 2:
if random.randint(1, 2) == 1:
flag = 'balk'
else:
flag = 'passed ball'
if flag:
roll_message = f'```md\nCheck {flag}```\n' \
f'{flag.title()} roll```md\n# {d_twenty_two}\nDetails: [1d20 ({d_twenty_two})]```\n'
embed = get_dice_embed(this_team, f'Chaos roll for the {this_team.sname}', roll_message)
embed.set_footer(
text='If the chaos roll fails, ignore future chaos until a new batter comes to the plate.'
)
# return {'is_chaos': True, 'embeds': [embed]}
return AbRoll(
roll_message=roll_message,
is_chaos=True,
embeds=[embed]
)
logger.info(f'Pre-AB roll')
this_roll = AbRoll(
d_six_one=random.randint(1, 6),
d_six_two=random.randint(1, 6),
d_six_three=random.randint(1, 6),
d_twenty=random.randint(1, 20)
)
logger.info(f'AB roll base: {this_roll}')
this_roll.roll_message = f'```md\n# {this_roll.d_six_one},{this_roll.d_six_two + this_roll.d_six_three},{this_roll.d_twenty}\nDetails:[1d6;2d6;1d20 ({this_roll.d_six_one} - {this_roll.d_six_two} {this_roll.d_six_three} - {this_roll.d_twenty})]```'
logger.info(f'AB roll with message: {this_roll}')
embed = get_dice_embed(this_team)
embed.add_field(
name=f'At bat roll for the {this_team.sname}',
value=this_roll.roll_message
)
this_roll.embeds = [embed]
# if d_six_one == 6 and d_six_two + d_six_three > 6:
# roll_message += f'\n**Check injury for pitcher injury rating {13 - d_six_two - d_six_three}**\n' \
# f'Oops! All injuries!'
logger.info(f'Game {this_game.id} | Team {this_team.id} ({this_team.abbrev}): {this_roll.roll_message}')
return this_roll
def jump_roll(this_team: Team, this_game: Game) -> list[discord.Embed]:
"""
Check for a baserunner's jump before stealing
"""
d_six_one = random.randint(1, 6)
d_six_two = random.randint(1, 6)
d_twenty = random.randint(1, 20)
d_twenty_two = random.randint(1, 20)
flag = None
if d_twenty == 1:
flag = 'pickoff'
elif d_twenty == 2:
flag = 'balk'
if not flag:
roll_message = f'```md\n# {d_six_one + d_six_two}\n'\
f'Details:[2d6 ({d_six_one} {d_six_two})]```'
else:
roll_message = f'```md\nCheck {flag}```\n' \
f'{flag.title()} roll```md\n# {d_twenty_two}\nDetails: [1d20 ({d_twenty_two})]```'
embed = get_dice_embed(this_team)
embed.add_field(
name=f'Jump check for the {this_team.sname}',
value=roll_message
)
logger.info(f'Game {this_game.id} | Team {this_team.id} ({this_team.abbrev}): {roll_message}')
return [embed]

View File

@ -106,6 +106,8 @@ class Game(SQLModel, table=True):
first_message: str | None = Field(default=None)
ai_team: str | None = Field(default=None)
game_type: str
roll_buttons: bool | None = Field(default=False)
auto_roll: bool | None = Field(default=True)
cardset_links: list[GameCardsetLink] = Relationship(back_populates='game', cascade_delete=True)
away_team: Team = Relationship(
@ -151,94 +153,191 @@ class Game(SQLModel, table=True):
else:
return None
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}')
# 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)
)
# 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)
# 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 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
)
# if curr_play is not None:
# embed.add_field(
# name='Game State',
# value=curr_play.scorebug_ascii,
# inline=False
# )
if classic:
embed.add_field(
name='Pitcher',
value=curr_play.pitcher.player.name_card_link('pitching')
)
embed.add_field(
name='Batter',
value=curr_play.batter.player.name_card_link('batting')
)
# def steal_string(batting_card: BattingCard) -> str:
# steal_string = '-/- (---)'
# if batting_card.steal_jump > 0:
# jump_chances = round(batting_card.steal_jump * 36)
baserunner_string = ''
if curr_play.on_first is not None:
baserunner_string += f'On First: {curr_play.on_first.player.name_card_link('batting')}\n'
if curr_play.on_second is not None:
baserunner_string += f'On Second: {curr_play.on_second.player.name_card_link('batting')}\n'
if curr_play.on_third is not None:
baserunner_string += f'On Third: {curr_play.on_third.player.name_card_link('batting')}'
logger.info(f'gameplay_models - Game.get_scorebug_embed - baserunner_string: {baserunner_string}')
# 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}')
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=curr_play.catcher.player.name_card_link('batter'))
# 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}'
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)
)
# embed.add_field(
# name='Pitcher',
# value=pit_string
# )
# embed.add_field(
# name='Batter',
# value=bat_string
# )
return embed
# 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):
"""
@ -655,6 +754,24 @@ class Player(PlayerBase, table=True):
def name_with_desc(self):
return f'{self.description} {self.name}'
@property
def batter_card_url(self):
if 'batting' in self.image:
return self.image
elif 'batting' in self.image2:
return self.image2
else:
return None
@property
def pitcher_card_url(self):
if 'pitching' in self.image:
return self.image
elif 'pitching' in self.image2:
return self.image2
else:
return None
def player_description(player: Player = None, player_dict: dict = None) -> str:
if player is None and player_dict is None:
@ -1110,7 +1227,14 @@ class Play(PlayBase, table=True):
@property
def could_walkoff(self) -> bool:
return False
if self.inning_half == 'bot' and self.on_third is not None:
runs_needed = self.away_score - self.home_score + 1
if runs_needed == 2 or (self.home_score - self.away_score == 9):
return True
else:
return False
"""

View File

@ -204,23 +204,23 @@ sample_ratings_query = {
}
async def test_create_scouting(session: Session):
this_card = await get_card_or_none(session, card_id=1405)
# async def test_create_scouting(session: Session):
# this_card = await get_card_or_none(session, card_id=1405)
assert this_card.player.id == 395
assert this_card.team.id == 31
assert this_card.batterscouting.battingcard_id == sample_ratings_query["ratings"][0]['battingcard']['id']
# assert this_card.player.id == 395
# assert this_card.team.id == 31
# assert this_card.batterscouting.battingcard_id == sample_ratings_query["ratings"][0]['battingcard']['id']
# this_scouting = await get_batter_scouting_or_none(session, this_card)
# # this_scouting = await get_batter_scouting_or_none(session, this_card)
# assert this_scouting.battingcard_id == sample_ratings_query["ratings"][0]['battingcard']['id']
# # assert this_scouting.battingcard_id == sample_ratings_query["ratings"][0]['battingcard']['id']
# this_card = await get_card_or_none(session, card_id=1406)
# # this_card = await get_card_or_none(session, card_id=1406)
# assert this_card.player.id == 161
# assert this_card.team.id == 31
# # assert this_card.player.id == 161
# # assert this_card.team.id == 31
# this_scouting = await get_pitcher_scouting_or_none(session, this_card)
# # this_scouting = await get_pitcher_scouting_or_none(session, this_card)
# assert this_scouting.pitchingcard_id == 4294
# # assert this_scouting.pitchingcard_id == 4294

View File

@ -42,21 +42,21 @@ async def test_get_or_create_ai_card(session: Session):
assert new_card_2.id == 42
async def test_get_card_or_none(session: Session):
card_1 = session.get(Card, 1)
new_card_1 = await get_card_or_none(session, card_id=card_1.id)
# async def test_get_card_or_none(session: Session):
# card_1 = session.get(Card, 1)
# new_card_1 = await get_card_or_none(session, card_id=card_1.id)
assert card_1.created == new_card_1.created
# assert card_1.created == new_card_1.created
assert session.get(Player, 538) is None
assert session.get(Team, 55) is None
# assert session.get(Player, 538) is None
# assert session.get(Team, 55) is None
new_card_2 = await get_card_or_none(session, card_id=55555)
print(f'new_card_2: {new_card_2}\nplayer: {new_card_2.player}\nteam: {new_card_2.team}')
# new_card_2 = await get_card_or_none(session, card_id=55555)
# print(f'new_card_2: {new_card_2}\nplayer: {new_card_2.player}\nteam: {new_card_2.team}')
assert new_card_2 is not None
assert new_card_2.player_id == 538
assert new_card_2.team_id == 55
assert session.get(Player, 538) is not None
assert session.get(Team, 55) is not None
# assert new_card_2 is not None
# assert new_card_2.player_id == 538
# assert new_card_2.team_id == 55
# assert session.get(Player, 538) is not None
# assert session.get(Team, 55) is not None

View File

@ -3,7 +3,7 @@ from sqlalchemy import delete as sadelete
from sqlalchemy.sql.functions import sum, count
from sqlmodel import Session, delete, func
from command_logic.logic_gameplay import complete_play, homeruns, is_game_over, singles, strikeouts, undo_play
from command_logic.logic_gameplay import complete_play, get_scorebug_embed, homeruns, is_game_over, singles, strikeouts, undo_play
from in_game.gameplay_models import Game, Lineup, GameCardsetLink, Play, Team, select
from in_game.gameplay_queries import get_channel_game_or_none, get_active_games_by_team, get_db_ready_decisions
from tests.factory import session_fixture
@ -109,15 +109,17 @@ def test_delete_game(session: Session):
assert len(bad_links) == 0
def test_get_scorebug(session: Session):
async def test_get_scorebug(session: Session):
game_1 = session.get(Game, 1)
scorebug = game_1.get_scorebug_embed(session)
# scorebug = game_1.get_scorebug_embed(session)
scorebug = await get_scorebug_embed(session, game_1)
assert scorebug.title == 'CornBelters @ Black Bears - Minor League'
assert scorebug.color.value == int('a6ce39', 16)
game_3 = session.get(Game, 3)
scorebug = game_3.get_scorebug_embed(session)
# scorebug = game_3.get_scorebug_embed(session)
scorebug = await get_scorebug_embed(session, game_3)
assert '0 Outs' in scorebug.fields[0].value

19
tests/other/test_dice.py Normal file
View File

@ -0,0 +1,19 @@
import pytest
from sqlmodel import Session, select, func
from dice import ab_roll
from in_game.gameplay_models import Game
from tests.factory import session_fixture
def test_ab_roll(session: Session):
game_1 = session.get(Game, 1)
play_2 = game_1.initialize_play(session)
this_roll = ab_roll(game_1.away_team, game_1, allow_chaos=False)
assert this_roll.d_six_one is not None
assert this_roll.d_six_two is not None
assert this_roll.d_six_three is not None
assert this_roll.d_twenty is not None
assert this_roll.roll_message is not None

View File

@ -1,5 +1,12 @@
import logging
import discord
from typing import Literal
from typing import Coroutine, Literal
from dice import ab_roll, jump_roll
from in_game.gameplay_models import Game, Play, Team
logger = logging.getLogger('discord_app')
class Confirm(discord.ui.View):
def __init__(self, responders: list, timeout: float = 300.0, label_type: Literal['yes', 'confirm'] = 'confirm'):
@ -18,7 +25,11 @@ class Confirm(discord.ui.View):
@discord.ui.button(label='Confirm', style=discord.ButtonStyle.green)
async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user not in self.responders:
return
await interaction.response.send_message(
content='Get out of here',
ephemeral=True,
delete_after=10.0
)
self.value = True
self.clear_items()
@ -28,7 +39,11 @@ class Confirm(discord.ui.View):
@discord.ui.button(label='Cancel', style=discord.ButtonStyle.grey)
async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user not in self.responders:
return
await interaction.response.send_message(
content='Get out of here',
ephemeral=True,
delete_after=10.0
)
self.value = False
self.clear_items()
@ -68,7 +83,11 @@ class ButtonOptions(discord.ui.View):
@discord.ui.button(label='Option 1', style=discord.ButtonStyle.primary)
async def option1(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user not in self.responders:
return
await interaction.response.send_message(
content='Get out of here',
ephemeral=True,
delete_after=10.0
)
self.value = self.options[0]
self.clear_items()
@ -77,7 +96,11 @@ class ButtonOptions(discord.ui.View):
@discord.ui.button(label='Option 2', style=discord.ButtonStyle.primary)
async def option2(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user not in self.responders:
return
await interaction.response.send_message(
content='Get out of here',
ephemeral=True,
delete_after=10.0
)
self.value = self.options[1]
self.clear_items()
@ -86,7 +109,11 @@ class ButtonOptions(discord.ui.View):
@discord.ui.button(label='Option 3', style=discord.ButtonStyle.primary)
async def option3(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user not in self.responders:
return
await interaction.response.send_message(
content='Get out of here',
ephemeral=True,
delete_after=10.0
)
self.value = self.options[2]
self.clear_items()
@ -95,7 +122,11 @@ class ButtonOptions(discord.ui.View):
@discord.ui.button(label='Option 4', style=discord.ButtonStyle.primary)
async def option4(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user not in self.responders:
return
await interaction.response.send_message(
content='Get out of here',
ephemeral=True,
delete_after=10.0
)
self.value = self.options[3]
self.clear_items()
@ -104,7 +135,11 @@ class ButtonOptions(discord.ui.View):
@discord.ui.button(label='Option 5', style=discord.ButtonStyle.primary)
async def option5(self, interaction: discord.Interaction, button: discord.ui.Button):
if interaction.user not in self.responders:
return
await interaction.response.send_message(
content='Get out of here',
ephemeral=True,
delete_after=10.0
)
self.value = self.options[4]
self.clear_items()
@ -140,3 +175,61 @@ async def ask_confirm(interaction: discord.Interaction, question: str, label_typ
else:
await question.edit(content=question, view=None)
return False
class ScorebugButtons(discord.ui.View):
def __init__(self, play: Play, timeout: float = 30):
super().__init__(timeout=timeout)
self.value = None
self.batting_team = play.batter.team
self.pitching_team = play.pitcher.team
self.team = play.batter.team
self.play = play
self.had_chaos = False
if play.on_base_code == 0:
self.remove_item(self.button_jump)
async def interaction_check(self, interaction: discord.Interaction[discord.Client]) -> bool:
logger.info(f'user id: {interaction.user.id} / batting_team: {self.batting_team}')
if interaction.user.id == self.batting_team.gmid:
logger.info(f'User {interaction.user.id} rolling in Game {self.play.game.id}')
return True
elif self.batting_team.is_ai and interaction.user.id == self.pitching_team.gmid:
logger.info(f'User {interaction.user.id} rolling for AI in Game {self.play.game.id}')
return True
logger.info(f'User {interaction.user.id} rejected in Game {self.play.game.id}')
await interaction.response.send_message(
content='Get out of here',
ephemeral=True,
delete_after=5.0
)
return False
# async def on_timeout(self) -> Coroutine[Any, Any, None]:
# await self.interaction
@discord.ui.button(label='Roll AB', style=discord.ButtonStyle.primary)
async def button_ab(self, interaction: discord.Interaction, button: discord.ui.Button):
logger.info(f'User {interaction.user.id} rolling AB in Game {self.play.game.id}')
this_roll = ab_roll(self.team, self.play.game, allow_chaos=not self.had_chaos)
if this_roll.is_chaos:
self.had_chaos = True
else:
button.disabled = True
await interaction.channel.send(content=None, embeds=this_roll.embeds)
await interaction.response.edit_message(view=self)
@discord.ui.button(label='Check Jump', style=discord.ButtonStyle.secondary)
async def button_jump(self, interaction: discord.Interaction, button: discord.ui.Button):
logger.info(f'User {interaction.user.id} rolling jump in Game {self.play.game.id}')
this_roll = jump_roll(self.team, self.play.game)
button.disabled = True
await interaction.channel.send(content=None, embeds=this_roll)
await interaction.response.edit_message(view=self)