manabrew-engine 0.6.0

Magic: The Gathering rules engine — a Rust port of Forge
Documentation
use std::collections::BTreeMap;

use crate::agent::types::PlayOption;
use crate::card::Card;
use crate::ids::CardId;
use crate::ids::PlayerId;
use crate::player::actions::{
    ActivateAbilityAction, ActivateManaAction, CastSpellAction, PassPriorityAction, PlayerAction,
    UndoManaAction,
};
use crate::replacement::parse_replacement_effect;
use crate::staticability::parse_static_ability;
use crate::trigger::{parse_trigger, Trigger};
use forge_foundation::{CardTypeLine, ColorSet, ManaCost};

pub fn build_priority_actions(
    playable: &[PlayOption],
    tappable_lands: &[CardId],
    untappable_lands: &[CardId],
    activatable: &[(CardId, usize)],
) -> Vec<PlayerAction> {
    let mut actions = Vec::with_capacity(
        playable.len() + tappable_lands.len() + untappable_lands.len() + activatable.len() + 1,
    );
    actions.extend(
        playable
            .iter()
            .map(|option| PlayerAction::from(CastSpellAction { play: *option })),
    );
    actions.extend(
        tappable_lands
            .iter()
            .map(|card_id| PlayerAction::from(ActivateManaAction { card_id: *card_id })),
    );
    actions.extend(
        untappable_lands
            .iter()
            .map(|card_id| PlayerAction::from(UndoManaAction { card_id: *card_id })),
    );
    actions.extend(activatable.iter().map(|(card_id, ability_index)| {
        PlayerAction::from(ActivateAbilityAction {
            card_id: *card_id,
            ability_index: *ability_index,
        })
    }));
    actions.push(PassPriorityAction.into());
    actions
}

pub fn new_player_effect_card(
    owner: PlayerId,
    name: impl Into<String>,
    set_code: Option<String>,
) -> Card {
    let mut effect = Card::new(
        CardId(0),
        name.into(),
        owner,
        CardTypeLine::parse("Effect"),
        ManaCost::parse("0"),
        ColorSet::COLORLESS,
        None,
        None,
        vec![],
        vec![],
    );
    effect.set_controller(owner);
    effect.set_code = set_code;
    effect
}

pub fn add_static_ability(effect: &mut Card, raw: &str) -> bool {
    parse_static_ability(raw)
        .map(|ability| effect.add_static_ability(ability))
        .unwrap_or(false)
}

pub fn add_trigger_ability<I, K, V>(effect: &mut Card, raw: &str, svars: I) -> bool
where
    I: IntoIterator<Item = (K, V)>,
    K: Into<String>,
    V: Into<String>,
{
    let mut changed = false;
    for (key, value) in svars {
        effect.set_s_var(key, value);
    }
    let mut next_trigger_id = 0;
    if let Some(trigger) = parse_trigger(raw, &mut next_trigger_id) {
        changed |= effect.add_trigger(trigger);
    }
    changed
}

pub fn add_trigger(effect: &mut Card, trigger: Trigger) -> bool {
    effect.add_trigger(trigger)
}

pub fn add_replacement_effect(effect: &mut Card, raw: &str) -> bool {
    parse_replacement_effect(raw)
        .map(|replacement| effect.add_replacement_effect(replacement))
        .unwrap_or(false)
}

pub fn add_spell_ability(
    effect: &mut Card,
    key: impl Into<String>,
    raw: impl Into<String>,
) -> bool {
    let key = key.into();
    let raw = raw.into();
    effect.set_s_var(&key, &raw);
    true
}

pub fn set_svars(effect: &mut Card, svars: BTreeMap<String, String>) {
    effect.set_svars_map(svars);
}