Commander-Specific Cards
This document details the implementation of cards designed specifically for the Commander format.
Overview
Commander has become one of Magic: The Gathering's most popular formats, leading to the creation of cards specifically designed for it. These cards include:
- Cards that explicitly reference the command zone or commanders
- Cards designed to support multiplayer politics
- Cards with mechanics only found in Commander products
- Cards that interact with format-specific rules
Card Categories
Command Zone Interaction Cards
These cards directly interact with the command zone or commanders:
Command Zone Access
#![allow(unused)] fn main() { /// Component for effects that can access the command zone #[derive(Component)] pub struct CommandZoneAccessEffect { /// The type of interaction with the command zone pub interaction_type: CommandZoneInteraction, } /// Types of command zone interactions #[derive(Debug, Clone, PartialEq, Eq)] pub enum CommandZoneInteraction { /// Cast a card from the command zone (e.g., Command Beacon) CastFromZone, /// Return a card to the command zone (e.g., Leadership Vacuum) ReturnToZone, /// Copy a commander (e.g., Sakashima of a Thousand Faces) CopyCommander, /// Modify commander properties (e.g., Nevermore naming a commander) ModifyCommander, } }
Example implementation of Command Beacon:
#![allow(unused)] fn main() { pub fn create_command_beacon() -> impl Bundle { ( CardName("Command Beacon".to_string()), CardType::Land, EntersTapped(false), ActivatedAbility { id: AbilityId::new(), cost: AbilityCost::Sacrifice(SacrificeCost::Self_), effect: Box::new(CommandBeaconEffect), timing_restriction: TimingRestriction::Sorcery, zone_restriction: ZoneRestriction::OnBattlefield, }, ) } /// Implementation of Command Beacon's effect #[derive(Debug, Clone)] pub struct CommandBeaconEffect; impl AbilityEffect for CommandBeaconEffect { fn resolve(&self, world: &mut World, ability_ctx: &mut AbilityContext) { let source = ability_ctx.source; let controller = ability_ctx.controller; // Find commander in command zone if let Some(commander) = find_commander_in_command_zone(world, controller) { // Move commander to hand world.send_event(ZoneTransitionEvent { entity: commander, from: Zone::Command, to: Zone::Hand, cause: TransitionCause::Ability { source, ability_name: "Command Beacon".to_string(), }, }); } } } }
Commander Cost Reduction
Cards that reduce or modify the cost of casting commanders:
#![allow(unused)] fn main() { /// Component for effects that modify commander costs #[derive(Component)] pub struct CommanderCostModifier { /// How the cost is modified pub modification: CostModification, /// Which commanders this applies to (all or specific) pub target: CommanderTarget, } /// Implementation of Emerald Medallion (reduces cost of green spells) pub fn create_emerald_medallion() -> impl Bundle { ( CardName("Emerald Medallion".to_string()), CardType::Artifact, StaticAbility { id: AbilityId::new(), effect: Box::new(ColorCostReductionEffect(Color::Green)), condition: StaticCondition::Always, }, ) } }
Multiplayer Politics Cards
Cards designed for multiplayer political interactions:
#![allow(unused)] fn main() { /// Component for voting cards #[derive(Component)] pub struct VotingMechanic { /// The options players can vote for pub options: Vec<String>, /// What happens based on voting results pub resolution: VoteResolution, } /// Implementation of Council's Judgment pub fn create_councils_judgment() -> impl Bundle { ( CardName("Council's Judgment".to_string()), CardType::Sorcery, ManaCost::parse("{1}{W}{W}"), VotingMechanic { options: vec!["Exile".to_string()], // Each nonland permanent is an option resolution: VoteResolution::ExileMostVotes, }, ) } /// System that handles voting pub fn handle_voting( mut commands: Commands, mut vote_events: EventReader<VoteEvent>, mut vote_results: EventWriter<VoteResultEvent>, voting_cards: Query<(Entity, &VotingMechanic, &Owner)>, players: Query<Entity, With<Player>>, ) { // Implementation details... } }
Commander-Specific Mechanics
Several mechanics were introduced specifically for Commander products:
Lieutenant
Cards with abilities that trigger if you control your commander:
#![allow(unused)] fn main() { /// Component for Lieutenant abilities #[derive(Component)] pub struct Lieutenant { /// The effect that happens when you control your commander pub effect: Box<dyn LieutenantEffect>, } /// Implementation of Thunderfoot Baloth pub fn create_thunderfoot_baloth() -> impl Bundle { ( CardName("Thunderfoot Baloth".to_string()), CardType::Creature, CreatureType(vec!["Beast".to_string()]), Power(5), Toughness(5), Lieutenant { effect: Box::new(ThunderfootBoostEffect), }, ) } /// System that checks for Lieutenant conditions pub fn check_lieutenant_condition( mut commands: Commands, lieutenants: Query<(Entity, &Lieutenant, &Controller)>, commanders: Query<(Entity, &Commander, &Controller)>, mut effect_events: EventWriter<LieutenantEffectEvent>, ) { // For each lieutenant... for (lieutenant_entity, lieutenant, controller) in lieutenants.iter() { // Check if controller controls their commander let controls_commander = commanders .iter() .any(|(_, _, cmd_controller)| cmd_controller.0 == controller.0); // Apply or remove effect based on condition effect_events.send(LieutenantEffectEvent { lieutenant: lieutenant_entity, active: controls_commander, }); } } }
Join Forces
Cards that allow all players to contribute mana to a spell:
#![allow(unused)] fn main() { /// Component for Join Forces mechanic #[derive(Component)] pub struct JoinForces { /// What happens based on mana contributed pub effect: Box<dyn JoinForcesEffect>, /// Which players can contribute pub contributor_restriction: ContributorRestriction, } /// Implementation of Minds Aglow pub fn create_minds_aglow() -> impl Bundle { ( CardName("Minds Aglow".to_string()), CardType::Sorcery, ManaCost::parse("{U}"), JoinForces { effect: Box::new(MindsAglowDrawEffect), contributor_restriction: ContributorRestriction::All, }, ) } }
Monarch
The Monarch mechanic introduces a special designation that players can claim, providing card advantage:
#![allow(unused)] fn main() { /// Component marking a player as the Monarch #[derive(Component)] pub struct Monarch; /// Resource tracking the current monarch #[derive(Resource)] pub struct MonarchState { /// The current monarch, if any pub current_monarch: Option<Entity>, /// When the monarchy was last changed pub last_changed: Option<f32>, } /// System that handles the Monarch end-of-turn trigger pub fn monarch_end_step_trigger( time: Res<Time>, monarch_state: Res<MonarchState>, mut turn_events: EventReader<EndStepEvent>, mut card_draw_events: EventWriter<DrawCardEvent>, ) { for event in turn_events.read() { if let Some(monarch) = monarch_state.current_monarch { if event.player == monarch { // Monarch draws a card at end of their turn card_draw_events.send(DrawCardEvent { player: monarch, amount: 1, source: None, reason: DrawReason::Ability("Monarch".to_string()), }); } } } } /// System that transfers the Monarch when combat damage is dealt to them pub fn monarch_combat_damage_transfer( mut commands: Commands, mut monarch_state: ResMut<MonarchState>, mut combat_damage_events: EventReader<CombatDamageEvent>, players: Query<Entity, With<Player>>, ) { for event in combat_damage_events.read() { if let Some(current_monarch) = monarch_state.current_monarch { if event.defending_player == current_monarch { // Transfer monarchy to attacking player if let Some(monarch_component) = commands.get_entity(current_monarch) { monarch_component.remove::<Monarch>(); } if let Some(new_monarch) = commands.get_entity(event.attacking_player_controller) { new_monarch.insert(Monarch); monarch_state.current_monarch = Some(event.attacking_player_controller); monarch_state.last_changed = Some(time.elapsed_seconds()); } } } } } }
Myriad
Myriad creates token copies attacking each opponent:
#![allow(unused)] fn main() { /// Component for the Myriad ability #[derive(Component)] pub struct Myriad; /// System that handles Myriad triggers pub fn handle_myriad_attacks( mut commands: Commands, mut declare_attacker_events: EventReader<DeclareAttackerEvent>, myriad_creatures: Query<Entity, With<Myriad>>, players: Query<Entity, With<Player>>, controllers: Query<&Controller>, ) { for event in declare_attacker_events.read() { if myriad_creatures.contains(event.attacker) { let attacker_controller = controllers.get(event.attacker).map(|c| c.0).unwrap_or_default(); // Create token copies attacking each other opponent for potential_defender in players.iter() { // Skip the attacking player and the already-targeted defender if potential_defender == attacker_controller || potential_defender == event.defender { continue; } // Create a token copy attacking this opponent let token = commands.spawn(( // Copy relevant components from original // Add attacking status to new opponent AttackingStatus { defending_player: potential_defender, }, TokenCopy { original: event.attacker, // Myriad tokens are exiled at end of combat exile_at_end_of_combat: true, }, )).id(); // More token setup... } } } } }
Melee
Melee gives a bonus based on how many opponents were attacked:
#![allow(unused)] fn main() { /// Component for the Melee ability #[derive(Component)] pub struct Melee { /// Bonus per opponent attacked pub bonus: i32, } /// System that calculates Melee bonuses pub fn calculate_melee_bonuses( mut commands: Commands, mut declare_attackers_step_events: EventReader<DeclareAttackersStepEvent>, melee_creatures: Query<(Entity, &Melee, &Controller)>, mut attacking_status: Query<&AttackingStatus>, players: Query<Entity, With<Player>>, ) { for event in declare_attackers_step_events.read() { // For each creature with Melee... for (melee_entity, melee, controller) in melee_creatures.iter() { // Count distinct opponents attacked let opponents_attacked = attacking_status .iter() .filter(|status| { // Only count attacks from controller's creatures if let Ok(attacker_controller) = controllers.get(status.attacker) { attacker_controller.0 == controller.0 && status.defending_player != controller.0 } else { false } }) .map(|status| status.defending_player) .collect::<HashSet<Entity>>() .len(); // Apply Melee bonus based on opponents attacked commands.entity(melee_entity).insert(MeleeBoost { power_bonus: melee.bonus * opponents_attacked as i32, toughness_bonus: melee.bonus * opponents_attacked as i32, expires_at: ExpiryTiming::EndOfTurn, }); } } } }
Goad
Goad forces creatures to attack players other than you:
#![allow(unused)] fn main() { /// Component marking a creature as Goaded #[derive(Component)] pub struct Goaded { /// The player who applied the goad effect pub goaded_by: Entity, /// When the goad effect expires pub expires_at: ExpiryTiming, } /// System that enforces Goad restrictions during attacks pub fn enforce_goad_restrictions( goaded_creatures: Query<(Entity, &Goaded)>, mut attack_validation_events: EventReader<ValidateAttackEvent>, mut attack_response_events: EventWriter<AttackValidationResponse>, ) { for event in attack_validation_events.read() { if let Ok((_, goaded)) = goaded_creatures.get(event.attacker) { // If attacking the player who goaded this creature, attack is invalid if event.defender == goaded.goaded_by { attack_response_events.send(AttackValidationResponse { attacker: event.attacker, defender: event.defender, is_valid: false, reason: "This creature is goaded and must attack a different player if able".to_string(), }); } } } } /// System that enforces Goad requirements to attack if able pub fn enforce_goad_attack_requirement( goaded_creatures: Query<(Entity, &Goaded, &CanAttack, &Controller)>, mut attack_requirement_events: EventReader<AttackRequirementCheckEvent>, mut attack_required_events: EventWriter<AttackRequiredEvent>, players: Query<Entity, With<Player>>, ) { for event in attack_requirement_events.read() { for (goaded_entity, goaded, can_attack, controller) in goaded_creatures.iter() { // If creature can attack and is controlled by current player if can_attack.0 && controller.0 == event.attacking_player { // Find valid attack targets (not the player who goaded) let valid_targets: Vec<Entity> = players .iter() .filter(|player| *player != goaded.goaded_by && *player != controller.0) .collect(); // If there are valid targets, this creature must attack if !valid_targets.is_empty() { attack_required_events.send(AttackRequiredEvent { creature: goaded_entity, valid_targets, }); } } } } } }
Commander-Specific Cycles and Card Groups
Medallion Cycle
The Medallion cycle (Ruby Medallion, Sapphire Medallion, etc.) reduces costs for spells of specific colors:
#![allow(unused)] fn main() { pub fn create_ruby_medallion() -> impl Bundle { ( CardName("Ruby Medallion".to_string()), CardType::Artifact, StaticAbility { id: AbilityId::new(), effect: Box::new(ColorCostReductionEffect(Color::Red)), condition: StaticCondition::Always, }, ) } }
Commander's Plate
A special equipment that gives protection from colors outside your commander's color identity:
#![allow(unused)] fn main() { pub fn create_commanders_plate() -> impl Bundle { ( CardName("Commander's Plate".to_string()), CardType::Artifact, ArtifactType(vec!["Equipment".to_string()]), EquipCost(ManaCost::parse("{3}")), StaticAbility { id: AbilityId::new(), effect: Box::new(CommandersPlateEffect), condition: StaticCondition::IsEquipped, }, ) } #[derive(Debug, Clone)] pub struct CommandersPlateEffect; impl StaticEffect for CommandersPlateEffect { fn apply(&self, world: &mut World, source: Entity, target: Entity) { // Get equipped creature's controller let controller = world.get::<Controller>(target).unwrap().0; // Get controller's commanders' color identity let commander_identity = get_commander_color_identity(world, controller); // Grant protection from colors outside that identity let protection_colors = ALL_COLORS.iter() .filter(|color| !commander_identity.contains(**color)) .copied() .collect(); world.entity_mut(target).insert(Protection { protection_from: ProtectionType::Colors(protection_colors), source, }); // Also grant stat boosts world.entity_mut(target).insert(StatBoost { power: 3, toughness: 3, source, }); } } }
Testing Commander-Specific Cards
Testing commander-specific cards requires special test fixtures and scenarios:
#![allow(unused)] fn main() { #[test] fn test_command_beacon() { let mut app = App::new(); setup_test_game(&mut app); let player = app.world.spawn(Player).id(); // Create a commander with tax let commander = app.world.spawn(( CardName("Test Commander".to_string()), Commander, InCommandZone(player), CommanderCastCount(2), // Commander has been cast twice before )).id(); // Create Command Beacon let beacon = app.world.spawn(create_command_beacon()).id(); // Use Command Beacon's ability use_ability(beacon, player, &mut app.world); // Verify commander moved to hand assert!(has_component::<InHand>(&app.world, commander)); assert!(!has_component::<InCommandZone>(&app.world, commander)); // Verify commander can be cast without tax let cast_cost = calculate_commander_cast_cost(&app.world, commander, player); assert_eq!(cast_cost, commander_base_cost(&app.world, commander)); } }
UI Considerations
Commander-specific cards require special UI treatment:
- Command zone interactions need visual clarity
- Political mechanics need multiplayer-aware UI elements
- Special effects like Monarch need distinctive visual indicators
#![allow(unused)] fn main() { /// UI component for displaying monarchy status #[derive(Component)] pub struct MonarchyIndicator { pub entity: Entity, } /// System to update monarchy UI pub fn update_monarchy_ui( monarch_state: Res<MonarchState>, mut indicators: Query<(&mut Visibility, &MonarchyIndicator)>, ) { for (mut visibility, indicator) in indicators.iter_mut() { visibility.is_visible = monarch_state.current_monarch == Some(indicator.entity); } } }
Commander Preconstructed Deck Integration
Our system includes support for preconstructed Commander decks:
#![allow(unused)] fn main() { /// Resource containing preconstructed Commander deck definitions #[derive(Resource)] pub struct PreconstructedDecks { pub decks: HashMap<String, PreconstructedDeckData>, } /// Data for a preconstructed Commander deck #[derive(Clone, Debug)] pub struct PreconstructedDeckData { /// Deck name pub name: String, /// The set/product the deck is from pub product: String, /// Year released pub year: u32, /// Primary commander pub commander: String, /// Secondary commander/partner (if any) pub partner: Option<String>, /// List of all cards in the deck pub card_list: Vec<String>, /// Deck color identity pub color_identity: ColorIdentity, /// Deck theme or strategy pub theme: String, } /// Function to load all preconstructed Commander decks pub fn load_preconstructed_decks() -> PreconstructedDecks { // Load deck definitions from files // ... } }
Conclusion
Commander-specific cards are a vital part of the format's identity. By implementing these cards correctly, we ensure that our game engine provides an authentic Commander experience. The implementation must balance rules accuracy with performance considerations, especially for complex political mechanics in multiplayer games.