Skip to main content

manabrew_engine/ability/effects/
time_travel_effect.rs

1use forge_foundation::ZoneType;
2
3use super::resolve_numeric_svar;
4use super::EffectContext;
5use crate::agent::BinaryChoiceKind;
6use crate::card::CounterType;
7use crate::ids::CardId;
8use crate::parsing::keys;
9
10/// `SP$ TimeTravel` — for chosen cards, add or remove a time counter.
11///
12/// Mirrors Java `TimeTravelEffect.java` binary choice semantics.
13/// Struct form of this effect so it can participate in the
14/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
15/// `TimeTravelEffect` class extending `SpellAbilityEffect`.
16#[manabrew_engine_macros::spell_effect(TimeTravelEffect)]
17fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
18    let controller = sa.activating_player;
19    let Some(source_id) = sa.source else { return };
20    let source_name = ctx.game.card(source_id).card_name.clone();
21
22    let amount = resolve_numeric_svar(ctx.game, sa, keys::AMOUNT, 1);
23    if amount <= 0 {
24        return;
25    }
26
27    for _ in 0..amount {
28        let mut valid: Vec<CardId> = Vec::new();
29        for &cid in ctx.game.cards_in_zone(ZoneType::Exile, controller) {
30            if ctx.game.card(cid).get_suspend_cost().is_some() {
31                valid.push(cid);
32            }
33        }
34        for &cid in ctx.game.cards_in_zone(ZoneType::Battlefield, controller) {
35            if ctx.game.card(cid).counter_count(&CounterType::Time) > 0 {
36                valid.push(cid);
37            }
38        }
39        if valid.is_empty() {
40            break;
41        }
42
43        let chosen = ctx.agents[controller.index()].choose_cards_for_effect(
44            controller,
45            &valid,
46            0,
47            valid.len(),
48        );
49        for cid in chosen {
50            let prompt = format!(
51                "Add or remove time counter on {}?",
52                ctx.game.card(cid).card_name
53            );
54            let add = ctx.agents[controller.index()].choose_binary(
55                controller,
56                &prompt,
57                BinaryChoiceKind::AddOrRemove,
58                None,
59                Some(source_id),
60                sa.api,
61            );
62            if add {
63                ctx.add_counter(
64                    cid,
65                    &CounterType::Time,
66                    1,
67                    sa,
68                    crate::event::RunParams {
69                        source_player: Some(controller),
70                        ..Default::default()
71                    },
72                );
73            } else {
74                ctx.game.card_mut(cid).remove_counter(&CounterType::Time, 1);
75                ctx.trigger_handler.run_trigger(
76                    crate::trigger::TriggerType::CounterRemoved,
77                    crate::event::RunParams {
78                        card: Some(cid),
79                        counter_type: Some("Time".to_string()),
80                        counter_amount: Some(1),
81                        cause_player: Some(controller),
82                        ..Default::default()
83                    },
84                    false,
85                );
86            }
87        }
88    }
89}