manabrew_engine/ability/effects/
meld_effect.rs1use forge_foundation::ZoneType;
9
10use super::{emit_zone_trigger, EffectContext};
11use crate::ids::CardId;
12use crate::parsing::keys;
13
14#[manabrew_engine_macros::spell_effect(MeldEffect)]
18fn resolve(ctx: &mut EffectContext, sa: &crate::spellability::SpellAbility) {
19 let Some(host_card_id) = sa.source else {
20 return;
21 };
22 let controller = sa.activating_player;
23
24 let prim_name = sa.ir.primary_text.as_deref().unwrap_or_default();
25 let sec_name = sa.ir.secondary_text.as_deref().unwrap_or_default();
26 let sec_type = sa.ir.secondary_type_text.as_deref().unwrap_or("Creature");
27
28 let candidates: Vec<CardId> = ctx
30 .game
31 .cards
32 .iter()
33 .filter(|c| {
34 c.zone == ZoneType::Battlefield
35 && c.controller == controller
36 && c.owner == controller
37 && c.card_name == sec_name
38 && super::matches_change_type(c, sec_type, &[])
39 })
40 .map(|c| c.id)
41 .collect();
42
43 if candidates.is_empty() {
44 return;
45 }
46
47 let secondary_id = if candidates.len() == 1 {
49 candidates[0]
50 } else {
51 ctx.agents[controller.index()].snapshot_state(ctx.game, ctx.mana_pools);
52 ctx.agents[controller.index()]
53 .choose_single_card_for_zone_change(
54 ctx.game,
55 controller,
56 &candidates,
57 "Choose a card to meld",
58 false,
59 )
60 .unwrap_or(candidates[0])
61 };
62
63 let cards_to_exile = vec![host_card_id, secondary_id];
65 let mut exiled = Vec::new();
66
67 for &card_id in &cards_to_exile {
68 if ctx
70 .game
71 .card(card_id)
72 .keywords
73 .contains_string_ignore_case("CantBeExiled")
74 {
75 continue;
76 }
77 let old_zone = ctx.game.card(card_id).zone;
78 ctx.move_card(card_id, ZoneType::Exile, controller);
79 emit_zone_trigger(ctx.trigger_handler, card_id, old_zone, ZoneType::Exile);
80 exiled.push(card_id);
81 }
82
83 if exiled.len() < 2 {
85 return;
86 }
87
88 let primary = exiled[0];
89 let secondary = exiled[1];
90
91 if ctx.game.card(primary).card_name != prim_name
93 || ctx.game.card(secondary).card_name != sec_name
94 {
95 return;
96 }
97
98 for &c in &[primary, secondary] {
100 if ctx.game.card(c).is_token || ctx.game.card(c).zone != ZoneType::Exile {
101 return;
102 }
103 }
104
105 ctx.game.card_mut(primary).set_transformed(true);
107 if let Some(other_name) = ctx
108 .game
109 .card(primary)
110 .other_part
111 .as_ref()
112 .map(|o| o.name.clone())
113 {
114 ctx.game.card_mut(primary).set_card_name(other_name);
115 }
116
117 ctx.game.card_mut(primary).melded_with.push(secondary);
119
120 if sa.is_tapped() {
122 ctx.game.card_mut(primary).set_tapped(true);
123 }
124
125 let old_zone = ctx.game.card(primary).zone;
127 ctx.game
128 .move_card(primary, ZoneType::Battlefield, controller);
129 ctx.trigger_handler
130 .register_active_trigger(ctx.game, primary);
131 emit_zone_trigger(
132 ctx.trigger_handler,
133 primary,
134 old_zone,
135 ZoneType::Battlefield,
136 );
137
138 let _ = super::add_to_combat(ctx, sa, primary, keys::ATTACKING);
139}