diff --git a/crates/engine/src/parser/oracle_effect/subject.rs b/crates/engine/src/parser/oracle_effect/subject.rs index afaea1a751..57cf3a51b6 100644 --- a/crates/engine/src/parser/oracle_effect/subject.rs +++ b/crates/engine/src/parser/oracle_effect/subject.rs @@ -2533,7 +2533,7 @@ pub(super) fn parse_subject_application( is_optional: false, }); } - // CR 303.4b + CR 702.5a + CR 701.17a (issues #5947, #5271): "enchanted + // CR 303.4b + CR 303.4m + CR 702.5a (issues #5947, #5271): "enchanted // player" / "enchanted opponent" name the Aura's attached player host — // `AttachedTo`, not a Typed EnchantedBy filter (which is object-only). // The opponent qualifier constrains attachment when the Aura enters; its diff --git a/crates/engine/tests/integration/issue_5271_overencumbered_tokens.rs b/crates/engine/tests/integration/issue_5271_overencumbered_tokens.rs new file mode 100644 index 0000000000..b3e69cee66 --- /dev/null +++ b/crates/engine/tests/integration/issue_5271_overencumbered_tokens.rs @@ -0,0 +1,51 @@ +use engine::game::game_object::AttachTarget; +use engine::game::scenario::{GameScenario, P0, P1}; +use engine::types::mana::ManaCost; +use engine::types::phase::Phase; + +const OVERENCUMBERED_ORACLE: &str = "Enchant opponent\n\ +When this Aura enters, enchanted opponent creates a Clue token, a Food token, and a Junk token.\n\ +At the beginning of combat on enchanted opponent's turn, that player may pay {1} for each artifact they control. If they don't, creatures can't attack this combat."; + +/// CR 111.2 + CR 303.4b + CR 608.2c: a player Aura's ETB resolves its +/// shared-verb token list for the enchanted opponent, not the Aura's controller. +#[test] +fn issue_5271_overencumbered_etb_gives_every_token_to_enchanted_opponent() { + let mut scenario = GameScenario::new(); + scenario.at_phase(Phase::PreCombatMain); + let aura = { + let mut builder = scenario.add_creature_to_hand(P0, "Overencumbered", 0, 0); + builder + .as_enchantment() + .with_subtypes(vec!["Aura", "Curse"]) + .with_mana_cost(ManaCost::zero()) + .from_oracle_text_with_keywords(&["Enchant opponent"], OVERENCUMBERED_ORACLE); + builder.id() + }; + + let mut runner = scenario.build(); + runner.cast(aura).target_player(P1).resolve(); + assert_eq!( + runner.state().objects.get(&aura).unwrap().attached_to, + Some(AttachTarget::Player(P1)), + "the resolving Aura must attach to its chosen opponent before its ETB resolves" + ); + runner.advance_until_stack_empty(); + + for token_name in ["Clue", "Food", "Junk"] { + let token = runner + .state() + .objects + .values() + .find(|object| object.name == token_name && object.is_token) + .unwrap_or_else(|| panic!("Overencumbered must create a {token_name} token")); + assert_eq!( + token.owner, P1, + "{token_name} must be owned by the enchanted opponent" + ); + assert_eq!( + token.controller, P1, + "{token_name} must be controlled by the enchanted opponent" + ); + } +} diff --git a/crates/engine/tests/integration/main.rs b/crates/engine/tests/integration/main.rs index 08895c900c..db44946329 100644 --- a/crates/engine/tests/integration/main.rs +++ b/crates/engine/tests/integration/main.rs @@ -599,6 +599,7 @@ mod issue_5247_kinetic_ooze_conditional_multi_target; mod issue_5252_additional_sacrifice_after_mana_abilities; mod issue_5254_declaration_in_stone_clue_owner; mod issue_5268_invincible_iron_man; +mod issue_5271_overencumbered_tokens; mod issue_5274_call_the_spirit_dragons; mod issue_5279_grave_sifter; mod issue_5282_nissa_ultimate_emblem_search;