Repository: magefree/mage (Java)

Issue:
[[Sanctuary Blade]]'s first ability incorrectly causes a trigger when it becomes attached
This issue was found during researching abilities to implement [[Paleontologist's Pick-Axe // Dinosaur Headdress]] (#11677).

[[Sanctuary Blade]] has three abilities:
- As Sanctuary Blade becomes attached to a creature, choose a color.
- Equipped creature gets +2/+0 and has protection from the last chosen color.
- Equip {3}

The first ability has the wording "as {this} becomes attached...". This is different from "when {this} becomes attached...". "When" abilities cause triggers, whereas "as" abilities resolve while the event is happening. See rule 603.6d:

> 603.6d Some permanents have text that reads “[This permanent] enters the battlefield with . . . ,” “As [this permanent] enters the battlefield . . . ,” “[This permanent] enters the battlefield as . . . ,” or “[This permanent] enters the battlefield tapped.” Such text is a static ability—not a triggered ability—whose effect occurs as part of the event that puts the permanent onto the battlefield.

And rule 614.1c:

> 614.1c Effects that read “[This permanent] enters the battlefield with . . . ,” “As [this permanent] enters the battlefield . . . ,” or “[This permanent] enters the battlefield as . . . “ are replacement effects.

Currently, the first ability of Sanctuary Blade causes a separate trigger to appear on the stack when it is attached to a creature. This does not comply with the current rules. It should be implemented in a similar fashion to `mage.abilities.common.AsEntersBattlefieldAbility` (and its associated effect `mage.abilities.effects.EntersBattlefieldEffect`).


Discussion: **[Paleontologist's Pick-Axe // Dinosaur Headdress](https://cards.scryfall.io/normal/front/e/0/e08c0cf4-3669-4a9a-aa12-7e8fe0f063ad.jpg?1699304213)** - [(Gatherer)](https://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=642483&printed=false) [(Scryfall)](https://scryfall.com/card/lcc/16/paleontologists-pick-axe-dinosaur-headdress?utm_source=api) [(EDHREC)](https://edhrec.com/route/?cc=Paleontologist%27s+Pick-Axe)
> {2}
> Artifact — Equipment
> Whenever equipped creature attacks, draw a card, then discard a card.
> Equip {1}
Craft with one or more creatures {5} ({5}, Exile this artifact, Exile one or more creatures you control and/or creature cards from your graveyard: Return this card transformed under its owner's control. Craft only as a sorcery.)
> :arrows_counterclockwise:
> Artifact — Equipment
> When Dinosaur Headdress enters the battlefield, attach it to target creature you control.
> As Dinosaur Headdress becomes attached to a creature, choose an exiled creature card used to craft Dinosaur Headdress.
Equipped creature is a copy of the last chosen card.
Equip {2}

**[Sanctuary Blade](https://cards.scryfall.io/normal/front/c/9/c9cf2480-10cd-4200-a47e-cc7f49182692.jpg?1591319770)** - [(Gatherer)](https://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=484916&printed=false) [(Scryfall)](https://scryfall.com/card/c20/69/sanctuary-blade?utm_source=api) [(EDHREC)](https://edhrec.com/route/?cc=Sanctuary+Blade)
> {2}
> Artifact — Equipment
> As Sanctuary Blade becomes attached to a creature, choose a color.
> Equipped creature gets +2/+0 and has protection from the last chosen color.
Equip {3}



Fix (patch):
diff --git a/Mage.Sets/src/mage/cards/s/SanctuaryBlade.java b/Mage.Sets/src/mage/cards/s/SanctuaryBlade.java
index 0f86849a2e9c..f75e2ff4e38b 100644
--- a/Mage.Sets/src/mage/cards/s/SanctuaryBlade.java
+++ b/Mage.Sets/src/mage/cards/s/SanctuaryBlade.java
@@ -1,6 +1,6 @@
 package mage.cards.s;
 
-import mage.abilities.common.AttachedToCreatureSourceTriggeredAbility;
+import mage.abilities.common.AsBecomesAttachedToCreatureSourceAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.mana.GenericManaCost;
 import mage.abilities.effects.Effect;
@@ -25,11 +25,11 @@ public final class SanctuaryBlade extends CardImpl {
 
     public SanctuaryBlade(UUID ownerId, CardSetInfo setInfo) {
         super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
-        
+
         this.subtype.add(SubType.EQUIPMENT);
 
         // As Sanctuary Blade becomes attached to a creature, choose a color.
-        this.addAbility(new AttachedToCreatureSourceTriggeredAbility(new ChooseColorEffect(Outcome.Benefit), false));
+        this.addAbility(new AsBecomesAttachedToCreatureSourceAbility(new ChooseColorEffect(Outcome.Benefit), "choose a color."));
 
         // Equipped creature gets +2/+0 and has protection from the last chosen color.
         Effect boostEffect = new BoostEquippedEffect(2, 0);
diff --git a/Mage/src/main/java/mage/abilities/common/AsBecomesAttachedToCreatureSourceAbility.java b/Mage/src/main/java/mage/abilities/common/AsBecomesAttachedToCreatureSourceAbility.java
new file mode 100644
index 000000000000..f22b09fcb2ba
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/AsBecomesAttachedToCreatureSourceAbility.java
@@ -0,0 +1,52 @@
+package mage.abilities.common;
+
+import mage.abilities.StaticAbility;
+import mage.abilities.effects.BecomesAttachedToCreatureSourceEffect;
+import mage.abilities.effects.Effect;
+import mage.constants.Zone;
+
+/**
+ * Based on {@link mage.abilities.common.AsEntersBattlefieldAbility}.
+ * This allows rule wording such as "as {this} becomes attached to a creature..."
+ * For this, there should not be a trigger, as in the case of the wording "when..."
+ * As per rule 603.6d, this should be a static ability.
+ * See [[Sanctuary Blade]].
+ *
+ * @author DominionSpy
+ */
+public class AsBecomesAttachedToCreatureSourceAbility extends StaticAbility {
+
+    public AsBecomesAttachedToCreatureSourceAbility(Effect effect) {
+        this(effect, null);
+    }
+
+    public AsBecomesAttachedToCreatureSourceAbility(Effect effect, String text) {
+        super(Zone.BATTLEFIELD, new BecomesAttachedToCreatureSourceEffect(effect, null, text));
+    }
+
+    protected AsBecomesAttachedToCreatureSourceAbility(final AsBecomesAttachedToCreatureSourceAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public AsBecomesAttachedToCreatureSourceAbility copy() {
+        return new AsBecomesAttachedToCreatureSourceAbility(this);
+    }
+
+    @Override
+    public void addEffect(Effect effect) {
+        if (!getEffects().isEmpty()) {
+            Effect attachEffect = this.getEffects().get(0);
+            if (attachEffect instanceof BecomesAttachedToCreatureSourceEffect) {
+                ((BecomesAttachedToCreatureSourceEffect) attachEffect).addEffect(effect);
+                return;
+            }
+        }
+        super.addEffect(effect);
+    }
+
+    @Override
+    public String getRule() {
+        return "As {this} becomes attached to a creature, " + super.getRule();
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/BecomesAttachedToCreatureSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/BecomesAttachedToCreatureSourceEffect.java
new file mode 100644
index 000000000000..db9b8ece34dd
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/BecomesAttachedToCreatureSourceEffect.java
@@ -0,0 +1,86 @@
+package mage.abilities.effects;
+
+import mage.abilities.Ability;
+import mage.abilities.Mode;
+import mage.abilities.condition.Condition;
+import mage.constants.Duration;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+
+/**
+ * Based on {@link EntersBattlefieldEffect}.
+ * This allows rule wording such as "as {this} becomes attached to a creature..."
+ * For this, there should not be a trigger, as in the case of the wording "when..."
+ * As per rule 614.1c, this should be a replacement effect.
+ * See [[Sanctuary Blade]].
+ *
+ * @author DominionSpy
+ */
+public class BecomesAttachedToCreatureSourceEffect extends ReplacementEffectImpl {
+
+    protected Effects baseEffects = new Effects();
+    protected String text;
+    protected Condition condition;
+
+    public BecomesAttachedToCreatureSourceEffect(Effect baseEffect) {
+        this(baseEffect, "");
+    }
+
+    public BecomesAttachedToCreatureSourceEffect(Effect baseEffect, String text) {
+        this(baseEffect, null, text);
+    }
+
+    public BecomesAttachedToCreatureSourceEffect(Effect baseEffect, Condition condition, String text) {
+        super(Duration.WhileOnBattlefield, baseEffect.getOutcome(), false);
+        this.baseEffects.add(baseEffect);
+        this.text = text;
+        this.condition = condition;
+    }
+
+    protected BecomesAttachedToCreatureSourceEffect(final BecomesAttachedToCreatureSourceEffect effect) {
+        super(effect);
+        this.baseEffects = effect.baseEffects.copy();
+        this.text = effect.text;
+        this.condition = effect.condition;
+    }
+
+    @Override
+    public BecomesAttachedToCreatureSourceEffect copy() {
+        return new BecomesAttachedToCreatureSourceEffect(this);
+    }
+
+    public void addEffect(Effect effect) {
+        baseEffects.add(effect);
+    }
+
+    @Override
+    public boolean checksEventType(GameEvent event, Game game) {
+        return GameEvent.EventType.ATTACH == event.getType();
+    }
+
+    @Override
+    public boolean applies(GameEvent event, Ability source, Game game) {
+        if (event.getSourceId().equals(source.getSourceId())) {
+            return condition == null || condition.apply(game, source);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean replaceEvent(GameEvent event, Ability source, Game game) {
+        for (Effect effect : baseEffects) {
+            if (effect instanceof ContinuousEffect) {
+                game.addEffect((ContinuousEffect) effect, source);
+            } else {
+                effect.setValue("appliedEffects", event.getAppliedEffects());
+                effect.apply(game, source);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String getText(Mode mode) {
+        return (text == null || text.isEmpty()) ? baseEffects.getText(mode) : text;
+    }
+}