# X-Check Frontend Integration Guide
## Overview
X-Check defensive plays are now fully integrated into the WebSocket `play_resolved` event. When a play results in an X-Check (defensive position check), the event will include detailed resolution data that the UI should display to players.
**Phase**: 3E-Final - WebSocket Integration
**Date**: 2025-11-03
**Status**: ✅ Complete (Backend), ⏳ Pending (Frontend)
---
## What is X-Check?
X-Check is a defensive play resolution system where:
1. Ball is hit to a specific defensive position (SS, LF, 3B, etc.)
2. Server rolls 1d20 (defense range table) + 3d6 (error chart)
3. Defender's range and error ratings determine the outcome
4. Result can be an out, hit, or error with detailed breakdown
**Example**: Grounder to shortstop → SS range 4 → d20=12 → "Routine groundout" → Out recorded
---
## WebSocket Event Structure
### Event: `play_resolved`
This existing event now includes **optional** X-Check details when applicable.
**Full Event Structure**:
```javascript
{
// Standard fields (always present)
"game_id": "123e4567-e89b-12d3-a456-426614174000",
"play_number": 15,
"outcome": "x_check", // PlayOutcome enum value
"hit_location": "SS", // Position where ball was hit
"description": "X-Check SS: G2 → G2 + NO = groundball_b",
"outs_recorded": 1,
"runs_scored": 0,
"batter_result": null, // null = out, 1-4 = base reached
"runners_advanced": [],
"is_hit": false,
"is_out": true,
"is_walk": false,
"roll_id": "abc123def456",
// X-Check details (OPTIONAL - only present for defensive plays)
"x_check_details": {
// Defensive player info
"position": "SS", // SS, LF, 3B, etc.
"defender_id": 42, // Lineup ID of defender
"defender_range": 4, // 1-5 (adjusted for playing in)
"defender_error_rating": 12, // 0-25 (lower is better)
// Dice rolls
"d20_roll": 12, // 1-20 (defense table lookup)
"d6_roll": 10, // 3-18 (error chart lookup)
// Resolution steps
"base_result": "G2", // Initial result from defense table
"converted_result": "G2", // After SPD test and G2#/G3# conversion
"error_result": "NO", // Error type: NO, E1, E2, E3, RP
"final_outcome": "groundball_b", // Final PlayOutcome enum value
"hit_type": "g2_no_error", // Combined result string
// Optional: SPD test (if base_result was 'SPD')
"spd_test_roll": null, // 1-20 if SPD test was needed
"spd_test_target": null, // Target number for SPD test
"spd_test_passed": null // true/false if test was needed
}
}
```
---
## Frontend Implementation
### 1. Detecting X-Check Plays
```javascript
socket.on('play_resolved', (data) => {
// Check if this play was an X-Check
if (data.outcome === 'x_check' && data.x_check_details) {
// This is a defensive play - show X-Check UI
displayXCheckResult(data);
} else {
// Normal play - show standard result
displayStandardPlayResult(data);
}
});
```
### 2. Displaying X-Check Details
**Basic Display** (minimum viable):
```javascript
function displayXCheckResult(data) {
const xcheck = data.x_check_details;
console.log(`⚾ X-Check to ${xcheck.position}`);
console.log(` Defender Range: ${xcheck.defender_range}`);
console.log(` Rolls: d20=${xcheck.d20_roll}, 3d6=${xcheck.d6_roll}`);
console.log(` Result: ${xcheck.base_result} → ${xcheck.converted_result}`);
console.log(` Error: ${xcheck.error_result}`);
console.log(` Outcome: ${data.description}`);
// Update game UI
updateGameState({
outs: data.outs_recorded,
runs: data.runs_scored,
description: data.description
});
}
```
**Enhanced Display** (with animations):
```javascript
function displayXCheckResult(data) {
const xcheck = data.x_check_details;
// 1. Show fielder animation
animateFielder(xcheck.position, xcheck.defender_id);
// 2. Show dice rolls with delay
setTimeout(() => {
showDiceRoll('Defense Roll', xcheck.d20_roll);
showDiceRoll('Error Roll', xcheck.d6_roll);
}, 500);
// 3. Show defensive rating overlay
setTimeout(() => {
showDefenderStats({
position: xcheck.position,
range: xcheck.defender_range,
error_rating: xcheck.defender_error_rating,
defender_id: xcheck.defender_id
});
}, 1500);
// 4. Show result progression
setTimeout(() => {
showResultFlow({
base: xcheck.base_result,
converted: xcheck.converted_result,
error: xcheck.error_result,
final: data.description
});
}, 2500);
// 5. Update game state
setTimeout(() => {
updateGameState({
outs: data.outs_recorded,
runs: data.runs_scored,
description: data.description
});
}, 3500);
}
```
### 3. Defender Stats Display
Show defender's ratings when X-Check is triggered:
```javascript
function showDefenderStats(stats) {
const defender = getPlayerById(stats.defender_id);
return `