Multiplayer Turn Handling

Overview

Commander's multiplayer nature introduces special considerations for turn management, particularly regarding turn order, player elimination, and simultaneous effects. This document outlines how the turn system handles these multiplayer-specific scenarios.

Player Order Management

Turn order in Commander follows a clockwise rotation from a randomly determined starting player. The turn order is maintained in the TurnManager:

#![allow(unused)]
fn main() {
#[derive(Resource)]
pub struct TurnManager {
    // Other fields...
    pub player_order: Vec<Entity>,
}
}

At game initialization, the player order is randomized:

#![allow(unused)]
fn main() {
fn initialize_turn_order(
    mut commands: Commands,
    player_query: Query<Entity, With<Player>>,
) {
    let mut player_entities: Vec<Entity> = player_query.iter().collect();
    // Randomize the order
    player_entities.shuffle(&mut thread_rng());
    
    commands.insert_resource(TurnManager {
        player_order: player_entities,
        // Initialize other fields...
    });
}
}

Player Elimination Handling

When a player is eliminated from the game, the turn system must adjust the turn order and potentially the active player and priority indicators:

#![allow(unused)]
fn main() {
fn handle_player_elimination(
    mut commands: Commands,
    mut elimination_events: EventReader<PlayerEliminationEvent>,
    mut turn_manager: ResMut<TurnManager>,
) {
    for event in elimination_events.read() {
        let eliminated_player = event.player;
        
        // Remove player from turn order
        if let Some(pos) = turn_manager.player_order.iter().position(|&p| p == eliminated_player) {
            turn_manager.player_order.remove(pos);
            
            // Adjust current active and priority indices if needed
            if pos <= turn_manager.active_player_index {
                turn_manager.active_player_index = 
                    (turn_manager.active_player_index - 1) % turn_manager.player_order.len();
            }
            
            if pos <= turn_manager.priority_player_index {
                turn_manager.priority_player_index = 
                    (turn_manager.priority_player_index - 1) % turn_manager.player_order.len();
            }
        }
    }
}
}

Simultaneous Effects

In Commander, many effects can happen simultaneously across multiple players. The system handles these according to Magic's APNAP (Active Player, Non-Active Player) rule:

#![allow(unused)]
fn main() {
fn handle_simultaneous_effects(
    turn_manager: Res<TurnManager>,
    mut simultaneous_events: EventReader<SimultaneousEffectEvent>,
    mut commands: Commands,
) {
    let mut effect_queue = Vec::new();
    
    // Collect all simultaneous effects
    for event in simultaneous_events.read() {
        effect_queue.push((event.player, event.effect.clone()));
    }
    
    // Sort by APNAP order
    effect_queue.sort_by(|(player_a, _), (player_b, _)| {
        let active_player = turn_manager.player_order[turn_manager.active_player_index];
        
        if *player_a == active_player {
            return Ordering::Less;
        }
        if *player_b == active_player {
            return Ordering::Greater;
        }
        
        // For non-active players, compare positions in turn order
        let pos_a = turn_manager.player_order.iter().position(|&p| p == *player_a).unwrap();
        let pos_b = turn_manager.player_order.iter().position(|&p| p == *player_b).unwrap();
        pos_a.cmp(&pos_b)
    });
    
    // Process effects in sorted order
    for (player, effect) in effect_queue {
        // Process each effect...
    }
}
}

Multi-Player Variants

The turn system supports different Commander variants:

  • Free-for-All: Standard Commander with turn order passing clockwise
  • Two-Headed Giant: Teams share turns and life totals
  • Star: Five players with win conditions based on eliminating opposing "colors"
  • Archenemy: One player versus all others, with the Archenemy having access to scheme cards

Each variant may modify the TurnManager initialization and behavior:

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GameVariant {
    FreeForAll,
    TwoHeadedGiant,
    Star,
    Archenemy,
    // Other variants
}

// The variant affects turn handling
pub fn initialize_turn_manager(
    variant: GameVariant,
    players: Vec<Entity>,
) -> TurnManager {
    match variant {
        GameVariant::TwoHeadedGiant => {
            // Team members share turns
            // ...
        },
        GameVariant::Archenemy => {
            // Archenemy goes first
            // ...
        },
        _ => {
            // Standard initialization
            // ...
        }
    }
}
}

Multiplayer-Specific Considerations

The turn system accounts for multiplayer-specific mechanics:

  • Range of Influence: Limited in some variants (like Star)
  • Shared Team Turns: In variants like Two-Headed Giant
  • Table Politics: Support for game actions like voting
  • Monarch: Special designation that passes between players
  • Initiative: Tracking which player has the initiative in Undercity dungeon scenarios