SUBSTITUTION
Triggered when a player is substituted with another player during a match.
Example Payload
{
"action": "SUBSTITUTION",
"queue": "valorant",
"channel": "1234567890123456789",
"guild": "9876543210987654321",
"player_subbed_out": {
"name": "Player1#1234",
"id": "111222333444555666",
"mmr": 1500,
"role": "Duelist",
"team_num": 0,
"top_role_index": 0,
"ign": "PlayerIGN1",
"timestamp": 1707523200.0,
"pulled_from": null
},
"player_subbed_in": {
"name": "Player5#7890",
"id": "555666777888999000",
"mmr": 1510,
"role": "Duelist",
"team_num": 0,
"top_role_index": 0,
"ign": "PlayerIGN5",
"timestamp": 1707524000.0,
"pulled_from": null
}
}
Payload Fields
| Field | Type | Description |
|---|---|---|
action | string | Always "SUBSTITUTION" |
queue | string | The name/identifier of the queue |
channel | string | Discord channel ID where the event occurred |
guild | string | Discord guild (server) ID |
player_subbed_out | Player | Player who was removed from the match |
player_subbed_in | Player | Player who replaced them |
tip
The substitute player inherits the same team_num and role as the player they're replacing.
When This Event Fires
This webhook is sent when:
- A moderator uses the substitute command
- A player is replaced due to disconnect/AFK
- An emergency substitution is made mid-match
Use Cases
- Notify players of substitutions
- Log substitution history
- Track which players frequently sub in/out
- Update match participant records
- Adjust statistics for substituted players
- Send alerts to team members
Implementation Example
def handle_substitution(data):
out_player = data['player_subbed_out']
in_player = data['player_subbed_in']
queue_name = data['queue']
print(f"Substitution in {queue_name}")
print(f"OUT: {out_player['name']} (MMR: {out_player['mmr']})")
print(f"IN: {in_player['name']} (MMR: {in_player['mmr']})")
# Example: Log substitution
log_substitution(
queue=queue_name,
out_player_id=out_player['id'],
in_player_id=in_player['id'],
team=in_player['team_num']
)
# Example: Send notification
notify_team(
team_num=in_player['team_num'],
message=f"{out_player['name']} was replaced by {in_player['name']}"
)
Tracking Match Participation
Handle substitutions when tracking match results:
def update_match_participants(match_id, data):
# Remove original player from active participants
remove_participant(match_id, data['player_subbed_out']['id'])
# Add substitute player to active participants
add_participant(
match_id=match_id,
player_id=data['player_subbed_in']['id'],
team=data['player_subbed_in']['team_num'],
substituted_at=time.time()
)
# Mark original player as substituted
mark_as_substituted(
match_id=match_id,
player_id=data['player_subbed_out']['id'],
reason='substitution'
)
MMR Considerations
def handle_substitution_mmr(data):
out_player = data['player_subbed_out']
in_player = data['player_subbed_in']
# Check if MMR is similar
mmr_diff = abs(out_player['mmr'] - in_player['mmr'])
if mmr_diff > 100:
print(f"Warning: Large MMR difference ({mmr_diff})")
print("This may affect match balance")
# Log team balance impact
log_balance_change(
old_mmr=out_player['mmr'],
new_mmr=in_player['mmr'],
team=in_player['team_num']
)
Statistics Handling
When a player is substituted, consider:
-
Original Player:
- May receive partial credit
- Mark as "did not complete"
- No MMR change (or reduced)
-
Substitute Player:
- Receives credit for match result
- Full or partial MMR change
- Track "substitute" status
def calculate_substitute_stats(match_result, substitution_data):
sub_player_id = substitution_data['player_subbed_in']['id']
# Give full credit if substitute played majority of match
# Give partial credit otherwise
if substitution_data['timestamp'] < match_start + (match_duration / 2):
credit = 1.0 # Full credit
else:
credit = 0.5 # Partial credit
return {
'player_id': sub_player_id,
'result': match_result,
'credit': credit,
'was_substitute': True
}
Related Events
- TEAMS_CREATED - Original teams before substitution
- MATCH_COMPLETED - Final match result with substitutes
- MATCH_CANCELLED - Match cancelled after substitution