Skip to main content

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

FieldTypeDescription
actionstringAlways "SUBSTITUTION"
queuestringThe name/identifier of the queue
channelstringDiscord channel ID where the event occurred
guildstringDiscord guild (server) ID
player_subbed_outPlayerPlayer who was removed from the match
player_subbed_inPlayerPlayer 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:

  1. Original Player:

    • May receive partial credit
    • Mark as "did not complete"
    • No MMR change (or reduced)
  2. 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
}