Stack and Priority System
Overview
The stack is a fundamental mechanic in Magic: The Gathering, representing the order in which spells and abilities resolve. This section documents the core implementation of the stack and priority system in Rummage.
The Stack
The stack is a last-in, first-out (LIFO) data structure that determines the order in which spells and abilities resolve during gameplay.
Stack Implementation
#![allow(unused)] fn main() { #[derive(Resource)] pub struct Stack { // The stack itself - a list of stack items in order pub items: Vec<StackItem>, // The currently resolving stack item, if any pub currently_resolving: Option<StackItem>, // Temporary storage for abilities that trigger during resolution pub pending_triggers: Vec<TriggeredAbility>, } #[derive(Clone, Debug)] pub struct StackItem { pub id: Entity, pub item_type: StackItemType, pub controller: Entity, pub source: Entity, pub targets: Vec<StackTarget>, pub effects: Vec<Effect>, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum StackItemType { Spell(CardType), Ability(AbilityType), } #[derive(Clone, Debug, PartialEq, Eq)] pub enum AbilityType { Activated, Triggered, Static, // Static abilities generally don't use the stack but are tracked here for targeting purposes Mana, // Mana abilities don't use the stack but may be tracked here } }
Stack Order and Resolution
The stack follows these core principles:
- The active player gets priority first in each phase and step
- When a player has priority, they can cast spells or activate abilities
- Each spell or ability goes on top of the stack
- After a spell or ability is put on the stack, all players get priority again
- When all players pass priority in succession, the top item of the stack resolves
- After resolution, the active player gets priority again
Stack Resolution System
#![allow(unused)] fn main() { pub fn resolve_stack( mut commands: Commands, mut stack: ResMut<Stack>, mut game_state: ResMut<GameState>, mut zone_transitions: EventWriter<ZoneTransitionEvent>, mut effect_events: EventWriter<EffectEvent>, ) { // Check if there's something to resolve if stack.items.is_empty() { return; } // Get the top item let item = stack.items.pop().unwrap(); stack.currently_resolving = Some(item.clone()); // Process based on item type match item.item_type { StackItemType::Spell(card_type) => { // Process spell resolution // Move card to appropriate zone (battlefield for permanents, graveyard for others) // ... }, StackItemType::Ability(ability_type) => { // Process ability resolution // ... } } // Process effects for effect in item.effects.iter() { effect_events.send(EffectEvent { effect: effect.clone(), source: item.source, controller: item.controller, targets: item.targets.clone(), }); } // Clear currently resolving stack.currently_resolving = None; // Check for triggered abilities that happened during resolution // ... } }
Priority System
The priority system determines which player can take actions at any given time. Priority passes in turn order, starting with the active player.
Priority Implementation
#![allow(unused)] fn main() { #[derive(Resource)] pub struct PrioritySystem { pub current_player: Entity, pub passed_players: HashSet<Entity>, pub active: bool, } #[derive(Event)] pub struct PriorityEvent { pub player: Entity, pub action: PriorityAction, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PriorityAction { Receive, Pass, TakeAction, } }
Priority Passing System
#![allow(unused)] fn main() { pub fn handle_priority( mut commands: Commands, mut priority: ResMut<PrioritySystem>, mut priority_events: EventReader<PriorityEvent>, game_state: Res<GameState>, stack: Res<Stack>, ) { for event in priority_events.iter() { match event.action { PriorityAction::Pass => { // Record that this player passed priority.passed_players.insert(event.player); // Determine next player let next_player = get_next_player_in_turn_order(event.player, &game_state); // Check if all players have passed if priority.passed_players.len() == game_state.player_order.len() { // Resolve top of stack or advance phase if !stack.items.is_empty() { // Resolve top of stack commands.add(resolve_stack_command()); } else { // Advance to next phase commands.add(advance_phase_command()); } // Reset priority system priority.passed_players.clear(); priority.current_player = game_state.active_player; } else { // Pass priority to next player priority.current_player = next_player; } }, PriorityAction::TakeAction => { // Clear passed players since someone took an action priority.passed_players.clear(); // After an action, active player gets priority priority.current_player = game_state.active_player; }, // Other priority actions... } } } }
Special Rules
Mana Abilities
Mana abilities don't use the stack and resolve immediately, meaning they cannot be responded to. This is handled separately from the regular stack system.
Split Second
Some cards have the "Split Second" ability, which means that while they're on the stack, players can't cast spells or activate abilities that aren't mana abilities.
Interrupts and Special Timing
Certain effects can interrupt the normal flow of gameplay, such as:
- Replacement effects
- Prevention effects
- State-based actions
- Special actions that don't use the stack
Format Extensions
Different formats may extend or modify the basic stack system:
- Commander Format: May have format-specific timing rules
- Multiplayer Formats: Must handle priority passing among multiple players
For format-specific stack and priority mechanics, see the respective format documentation.
Next: Stack Resolution