Skip to main content

manabrew_engine/ability/effects/
assemble_contraption_effect.rs

1//! AssembleContraption — assemble contraptions from the contraption deck (Unstable).
2//! Ported from Java's AssembleContraptionEffect: moves top card of contraption
3//! deck to battlefield, assigns a sprocket.
4
5use forge_foundation::ZoneType;
6
7use super::EffectContext;
8use crate::parsing::keys;
9use crate::replacement::replacement_handler::{apply_replacements, ReplacementEvent};
10use crate::replacement::ReplacementResult;
11
12/// Struct form of this effect so it can participate in the
13/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
14/// `AssembleContraptionEffect` class extending `SpellAbilityEffect`.
15#[manabrew_engine_macros::spell_effect(AssembleContraptionEffect)]
16fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
17    let source = match sa.source {
18        Some(s) => s,
19        None => return,
20    };
21
22    let amount = super::resolve_numeric_svar(ctx.game, sa, "Amount", 1).max(1);
23
24    let controller = sa.activating_player;
25
26    // Run AssembleContraption replacement effects before assembling.
27    let mut event = ReplacementEvent::AssembleContraption { player: controller };
28    let result = apply_replacements(ctx.game, &mut event);
29    if result == ReplacementResult::Skipped || result == ReplacementResult::Replaced {
30        return;
31    }
32
33    for _ in 0..amount {
34        let contraption = ctx
35            .game
36            .cards_in_zone(ZoneType::ContraptionDeck, controller)
37            .first()
38            .copied()
39            .or_else(|| {
40                // Compatibility fallback until full deck-section setup is wired.
41                ctx.game
42                    .cards
43                    .iter()
44                    .find(|c| {
45                        c.owner == controller
46                            && c.zone == ZoneType::Sideboard
47                            && c.type_line
48                                .subtypes
49                                .iter()
50                                .any(|s| s.eq_ignore_ascii_case("Contraption"))
51                    })
52                    .map(|c| c.id)
53            });
54
55        if let Some(card_id) = contraption {
56            let old_zone = ctx.game.card(card_id).zone;
57            ctx.game
58                .move_card(card_id, ZoneType::Battlefield, controller);
59            super::emit_zone_trigger(
60                ctx.trigger_handler,
61                card_id,
62                old_zone,
63                ZoneType::Battlefield,
64            );
65
66            // Assign a sprocket (1-3)
67            let sprocket = (ctx.rng.next_int(3) + 1).to_string();
68            ctx.game
69                .card_mut(card_id)
70                .svars
71                .insert("Sprocket".to_string(), sprocket);
72
73            if sa.param_is_true(keys::REMEMBER) {
74                ctx.game.card_mut(source).add_remembered_card(card_id);
75            }
76        }
77    }
78}