Skip to main content

manabrew_engine/ability/effects/
mutate_effect.rs

1//! Mutate — mutate onto a creature (Ikoria).
2//! Ported from Java's MutateEffect: merges the host card with a target creature,
3//! choosing which goes on top, combining abilities.
4
5use forge_foundation::ZoneType;
6
7use super::EffectContext;
8use crate::ability::ability_ir::DefinedRef;
9use crate::event::RunParams;
10use crate::trigger::TriggerType;
11
12/// Struct form of this effect so it can participate in the
13/// `SpellAbilityEffect` trait hierarchy — mirrors Java's
14/// `MutateEffect` class extending `SpellAbilityEffect`.
15#[manabrew_engine_macros::spell_effect(MutateEffect)]
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    // Get the target creature to mutate onto
23    let target = if let Some(target) = sa.target_chosen.target_card {
24        target
25    } else if let Some(def) = sa.defined_ref() {
26        if matches!(def, DefinedRef::SelfCard) {
27            return; // Can't mutate onto self
28        }
29        match ctx.game.card(source).remembered_cards.first() {
30            Some(&id) => id,
31            None => return,
32        }
33    } else {
34        return;
35    };
36
37    if ctx.game.card(target).zone != ZoneType::Battlefield {
38        return;
39    }
40
41    let controller = sa.activating_player;
42
43    // Choose whether host goes on top or bottom
44    // Auto-select: put host on top (agent would choose)
45    let put_on_top = true;
46
47    if put_on_top {
48        // Host card's characteristics become the merged creature's characteristics
49        // Copy name, P/T, types from host to target
50        let host_name = ctx.game.card(source).card_name.clone();
51        let host_power = ctx.game.card(source).base_power;
52        let host_toughness = ctx.game.card(source).base_toughness;
53        let host_types = ctx.game.card(source).type_line.clone();
54
55        ctx.game.card_mut(target).set_card_name(host_name);
56        ctx.game.card_mut(target).set_base_power(host_power);
57        ctx.game.card_mut(target).set_base_toughness(host_toughness);
58        ctx.game.card_mut(target).set_type_line(host_types);
59    }
60
61    // Copy all keywords from host to target (abilities merge)
62    let host_keywords = ctx.game.card(source).keywords.as_string_list();
63    for kw in host_keywords {
64        ctx.game.card_mut(target).add_intrinsic_keyword(&kw);
65    }
66
67    // Move host to "merged" zone (track via svar)
68    ctx.game
69        .card_mut(source)
70        .set_s_var("MergedTo", format!("{}", target.0));
71    ctx.game.card_mut(source).set_controller(controller);
72
73    // Track mutation count
74    let times_mutated = ctx
75        .game
76        .card(target)
77        .svars
78        .get("TimesMutated")
79        .and_then(|s| s.parse::<i32>().ok())
80        .unwrap_or(0);
81    ctx.game
82        .card_mut(target)
83        .set_s_var("TimesMutated", (times_mutated + 1).to_string());
84
85    // Move source card out of battlefield (merged zone representation)
86    let old_zone = ctx.game.card(source).zone;
87    ctx.move_card(source, ZoneType::Command, controller);
88
89    // Fire Mutates trigger
90    ctx.trigger_handler.run_trigger(
91        TriggerType::Mutates,
92        RunParams {
93            card: Some(target),
94            player: Some(controller),
95            ..Default::default()
96        },
97        false,
98    );
99
100    let _ = old_zone;
101}