# Injury Commands **Command Group:** `/injury` **Permission Required:** SBA Players role (for set-new and clear) **Subcommands:** roll, set-new, clear ## Overview The injury command family provides comprehensive player injury management for the SBA league. Team managers can roll for injuries using official Strat-o-Matic injury tables, record confirmed injuries, and clear injuries when players return. ## Commands ### `/injury roll` Roll for injury based on a player's injury rating using 3d6 dice and official injury tables. **Usage:** ``` /injury roll ``` **Parameters:** - `player_name` (required, autocomplete): Name of the player - uses smart autocomplete prioritizing your team's players **Injury Rating Format:** The player's `injury_rating` field contains both the games played and rating in format `#p##`: - **Format**: `1p70`, `4p50`, `2p65`, etc. - **First character**: Games played in current series (1-6) - **Remaining characters**: Injury rating (p70, p65, p60, p50, p40, p30, p20) **Examples:** - `1p70` = 1 game played, p70 rating - `4p50` = 4 games played, p50 rating - `2p65` = 2 games played, p65 rating **Dice Roll:** - Rolls 3d6 (3-18 range) - Automatically extracts games played and rating from player's injury_rating field - Looks up result in official Strat-o-Matic injury tables - Returns injury duration based on rating and games played **Possible Results:** - **OK**: No injury - **REM**: Remainder of game (batters) or Fatigued (pitchers) - **Number**: Games player will miss (1-24 games) **Example:** ``` /injury roll Mike Trout ``` **Response Fields:** - **Roll**: Total rolled and individual dice (e.g., "15 (3d6: 5 + 5 + 5)") - **Player**: Player name and position - **Injury Rating**: Full rating with parsed details (e.g., "4p50 (p50, 4 games)") - **Result**: Injury outcome (OK, REM, or number of games) - **Team**: Player's current team **Response Colors:** - **Green**: OK (no injury) - **Gold**: REM (remainder of game/fatigued) - **Orange**: Number of games (injury occurred) **Error Handling:** If a player's `injury_rating` is not in the correct format, an error message will be displayed: ``` Invalid Injury Rating Format {Player} has an invalid injury rating: `{rating}` Expected format: #p## (e.g., 1p70, 4p50) ``` --- ### `/injury set-new` Record a new injury for a player on your team. **Usage:** ``` /injury set-new ``` **Parameters:** - `player_name` (required): Name of the player to injure - `this_week` (required): Current week number (1-21, including playoffs) - `this_game` (required): Current game number (varies by week - see below) - `injury_games` (required): Total number of games player will be out **Week and Game Validation:** - **Regular Season (Weeks 1-18)**: Game number must be 1-4 - **Playoff Round 1 (Week 19)**: Game number must be 1-5 (best of 5 series) - **Playoff Round 2 (Week 20)**: Game number must be 1-7 (best of 7 series) - **Playoff Round 3 (Week 21)**: Game number must be 1-7 (best of 7 series) **Additional Validation:** - Player must exist in current season - Player cannot already have an active injury - Injury duration must be at least 1 game **Automatic Calculations:** The command automatically calculates: 1. Injury start date (adjusts for final game of series edge case) 2. Return date based on injury duration 3. Week rollover when games exceed the max for that week (4 regular season, 5-7 playoffs) **Examples:** Regular Season: ``` /injury set-new Mike Trout 5 2 4 ``` This records an injury occurring in week 5, game 2, with player out for 4 games (returns week 6, game 2). Playoffs: ``` /injury set-new Shohei Ohtani 19 3 2 ``` This records an injury in playoff round 1 (week 19), game 3, with player out for 2 games (returns week 19, game 5). ``` /injury set-new Aaron Judge 20 5 8 ``` This records an injury in playoff round 2 (week 20), game 5, with player out for 8 games (spans multiple weeks). **Response:** - Confirmation embed with injury details - Player's name, position, and team - Total games missed - Calculated return date --- ### `/injury clear` Clear a player's active injury and mark them as eligible to play. **Usage:** ``` /injury clear ``` **Parameters:** - `player_name` (required, autocomplete): Name of the player whose injury to clear - uses smart autocomplete prioritizing your team's players **Validation:** - Player must exist in current season - Player must have an active injury **User Flow:** 1. Command issued with player name 2. **Confirmation embed displayed** showing: - Player name and position - Team name and abbreviation - Expected return date - Total games missed - Team thumbnail (if available) 3. User prompted: "Is {Player Name} cleared to return?" 4. Two buttons presented: - **"Clear Injury"** → Proceeds with clearing the injury - **"Cancel"** → Cancels the operation 5. After confirmation, injury is cleared and success message displayed **Example:** ``` /injury clear Mike Trout ``` **Confirmation Embed:** - Title: Player name - Description: "Is **{Player Name}** cleared to return?" - Fields: Player info, team, expected return date, games missed - Buttons: "Clear Injury" / "Cancel" - Timeout: 3 minutes **Success Response (after confirmation):** - Confirmation that injury was cleared - Shows previous return date - Shows total games that were missed - Player's team information **Responders:** - Command issuer - Team GM(s) - can also confirm/cancel on behalf of team --- ## Date Format All injury dates use the format `w##g#`: - `w##` = Week number (zero-padded to 2 digits) - `g#` = Game number (1-4) **Examples:** - `w05g2` = Week 5, Game 2 - `w12g4` = Week 12, Game 4 - `w01g1` = Week 1, Game 1 ## Injury Calculation Logic ### Basic Calculation For an injury of N games starting at week W, game G: 1. **Calculate weeks and remaining games:** ``` out_weeks = floor(N / 4) out_games = N % 4 ``` 2. **Calculate return date:** ``` return_week = W + out_weeks return_game = G + 1 + out_games ``` 3. **Handle week rollover:** ``` if return_game > 4: return_week += 1 return_game -= 4 ``` ### Special Cases #### Game 4 Edge Case If injury occurs during game 4, the start date is adjusted: ``` start_week = W + 1 start_game = 1 ``` #### Examples **Example 1: Simple injury (same week)** - Current: Week 5, Game 1 - Injury: 2 games - Return: Week 5, Game 4 **Example 2: Week rollover** - Current: Week 5, Game 3 - Injury: 3 games - Return: Week 6, Game 3 **Example 3: Multi-week injury** - Current: Week 5, Game 2 - Injury: 8 games - Return: Week 7, Game 3 **Example 4: Game 4 start** - Current: Week 5, Game 4 - Injury: 2 games - Start: Week 6, Game 1 - Return: Week 6, Game 3 ## Database Schema ### Injury Model ```python class Injury(SBABaseModel): id: int # Injury ID season: int # Season number player_id: int # Player ID total_games: int # Total games player will be out start_week: int # Week injury started start_game: int # Game number injury started (1-4) end_week: int # Week player returns end_game: int # Game number player returns (1-4) is_active: bool # Whether injury is currently active ``` ### API Integration The commands interact with the following API endpoints: - `GET /api/v3/injuries` - Query injuries with filters - `POST /api/v3/injuries` - Create new injury record - `PATCH /api/v3/injuries/{id}` - Update injury (clear active status) - `PATCH /api/v3/players/{id}` - Update player's il_return field ## Service Layer ### InjuryService **Location:** `services/injury_service.py` **Key Methods:** - `get_active_injury(player_id, season)` - Get active injury for player - `get_injuries_by_player(player_id, season, active_only)` - Get all injuries for player - `get_injuries_by_team(team_id, season, active_only)` - Get team injuries - `create_injury(...)` - Create new injury record - `clear_injury(injury_id)` - Deactivate injury ## Permissions ### Required Roles **For `/injury check`:** - No role required (available to all users) **For `/injury set-new` and `/injury clear`:** - **SBA Players** role required - Configured via `SBA_PLAYERS_ROLE_NAME` environment variable ### Permission Checks The commands use `has_player_role()` method to verify user has appropriate role: ```python def has_player_role(self, interaction: discord.Interaction) -> bool: """Check if user has the SBA Players role.""" player_role = discord.utils.get( interaction.guild.roles, name=get_config().sba_players_role_name ) return player_role in interaction.user.roles if player_role else False ``` ## Error Handling ### Common Errors **Player Not Found:** ``` ❌ Player Not Found I did not find anybody named **{player_name}**. ``` **Already Injured:** ``` ❌ Already Injured Hm. It looks like {player_name} is already hurt. ``` **Not Injured:** ``` ❌ No Active Injury {player_name} isn't injured. ``` **Invalid Input:** ``` ❌ Invalid Input Game number must be between 1 and 4. ``` **Permission Denied:** ``` ❌ Permission Denied This command requires the **SBA Players** role. ``` ## Logging All injury commands use the `@logged_command` decorator for automatic logging: ```python @app_commands.command(name="check") @logged_command("/injury check") async def injury_check(self, interaction, player_name: str): # Command implementation ``` **Log Context:** - Command name - User ID and username - Player name - Season - Injury details (duration, dates) - Success/failure status **Example Log:** ```json { "level": "INFO", "command": "/injury set-new", "user_id": "123456789", "player_name": "Mike Trout", "season": 12, "injury_games": 4, "return_date": "w06g2", "message": "Injury set for Mike Trout" } ``` ## Testing ### Test Coverage **Location:** `tests/test_services_injury.py` **Test Categories:** 1. **Model Tests** (5 tests) - Injury model creation and properties 2. **Service Tests** (8 tests) - InjuryService CRUD operations with API mocking 3. **Roll Logic Tests** (8 tests) - Injury rating parsing, table lookup, and dice roll logic 4. **Calculation Tests** (5 tests) - Date calculation logic for injury duration **Total:** 26 comprehensive tests **Running Tests:** ```bash # Run all injury tests python -m pytest tests/test_services_injury.py -v # Run specific test class python -m pytest tests/test_services_injury.py::TestInjuryService -v python -m pytest tests/test_services_injury.py::TestInjuryRollLogic -v # Run with coverage python -m pytest tests/test_services_injury.py --cov=services.injury_service --cov=commands.injuries ``` ## Injury Roll Tables ### Table Structure The injury tables are based on official Strat-o-Matic rules with the following structure: **Ratings:** p70, p65, p60, p50, p40, p30, p20 (higher is better) **Games Played:** 1-6 games in current series **Roll:** 3d6 (results from 3-18) ### Rating Availability by Games Played Not all ratings are available for all games played combinations: - **1 game**: All ratings (p70-p20) - **2 games**: All ratings (p70-p20) - **3 games**: p65-p20 (p70 exempt) - **4 games**: p60-p20 (p70, p65 exempt) - **5 games**: p60-p20 (p70, p65 exempt) - **6 games**: p40-p20 (p70, p65, p60, p50 exempt) When a rating/games combination has no table, the result is automatically "OK" (no injury). ### Example Table (p65, 1 game): | Roll | Result | |------|--------| | 3 | 2 | | 4 | 2 | | 5 | OK | | 6 | REM | | 7 | 1 | | ... | ... | | 18 | 12 | ## UI/UX Design ### Embed Colors - **Roll (OK):** Green - No injury - **Roll (REM):** Gold - Remainder of game/Fatigued - **Roll (Injury):** Orange - Number of games - **Set New:** Success (green) - `EmbedTemplate.success()` - **Clear:** Success (green) - `EmbedTemplate.success()` - **Errors:** Error (red) - `EmbedTemplate.error()` ### Response Format All successful responses use Discord embeds with: - Clear title indicating action/status - Well-organized field layout - Team information when applicable - Consistent formatting for dates ## Integration with Player Model The Player model includes injury-related fields: ```python class Player(SBABaseModel): # ... other fields ... pitcher_injury: Optional[int] # Pitcher injury rating injury_rating: Optional[str] # General injury rating il_return: Optional[str] # Injured list return date (w##g#) ``` When an injury is set or cleared, the player's `il_return` field is automatically updated via PlayerService. ## Future Enhancements Possible improvements for future versions: 1. **Injury History** - View player's injury history for a season 2. **Team Injury Report** - List all injuries for a team 3. **Injury Notifications** - Automatic notifications when players return from injury 4. **Injury Statistics** - Track injury trends and statistics 5. **Injury Chart Image** - Display the official injury chart as an embed image ## Migration from Legacy ### Legacy Commands The legacy injury commands were located in: - `discord-app/cogs/players.py` - `set_injury_slash()` and `clear_injury_slash()` - `discord-app/cogs/players.py` - `injury_roll_slash()` with manual rating/games input ## Configuration The injury system uses configuration constants from `config.py` to validate week and game numbers: ### Season Structure ```python weeks_per_season: int = 18 # Regular season weeks games_per_week: int = 4 # Games per week in regular season playoff_weeks_per_season: int = 3 # Playoff weeks (19-21) playoff_round_one_games: int = 5 # Week 19: Best of 5 playoff_round_two_games: int = 7 # Week 20: Best of 7 playoff_round_three_games: int = 7 # Week 21: Best of 7 ``` These constants are used in: - **`views/modals.py`**: `InjuryRollModal` and `ClearInjuryModal` for week/game validation - **Validation Logic**: Ensures proper game number ranges based on week (regular season vs playoff rounds) ### Week/Game Limits The injury system automatically enforces proper limits based on the week: | Week Range | Season Phase | Max Games | Series Type | |------------|--------------|-----------|-------------| | 1-18 | Regular | 4 | N/A | | 19 | Playoff R1 | 5 | Best of 5 | | 20 | Playoff R2 | 7 | Best of 7 | | 21 | Playoff R3 | 7 | Best of 7 | ### Key Improvements 1. **Cleaner Command Structure:** Using GroupCog for organized subcommands (`/injury roll`, `/injury set-new`, `/injury clear`) 2. **Simplified Interface:** Single parameter for injury roll - games played automatically extracted from player data 3. **Smart Injury Ratings:** Automatically reads and parses player's injury rating from database 4. **Player Autocomplete:** Modern autocomplete with team prioritization for better UX 5. **Better Error Handling:** User-friendly error messages via EmbedTemplate with format validation 6. **Improved Logging:** Automatic logging via @logged_command decorator 7. **Service Layer:** Separated business logic from command handlers 8. **Type Safety:** Full type hints and Pydantic models 9. **Testability:** Comprehensive unit tests (26 tests) with mocked API calls 10. **Modern UI:** Consistent embed-based responses with color coding 11. **Official Tables:** Complete Strat-o-Matic injury tables built into the command 12. **Playoff Support:** Full support for injury rolls during playoff weeks with series-specific game limits (October 2025) ### Migration Details **Old:** `/injuryroll ` - Manual rating and games selection **New:** `/injury roll ` - Single parameter, automatic rating and games extraction from player's `injury_rating` field **Old:** `/setinjury ` **New:** `/injury set-new ` - Same functionality, better naming **Old:** `/clearinjury ` **New:** `/injury clear ` - Same functionality, better naming ### Database Field Update The `injury_rating` field format has changed to include games played: - **Old Format**: `p65`, `p70`, etc. (rating only) - **New Format**: `1p70`, `4p50`, `2p65`, etc. (games + rating) Players must have their `injury_rating` field updated to the new format for the `/injury roll` command to work. --- **Last Updated:** October 2025 **Version:** 2.0 **Status:** Active **Recent Updates:** - **October 2025**: Added playoff support with series-specific game validation (weeks 19-21)