diff --git a/Mage.Sets/src/mage/cards/g/GogoMasterOfMimicry.java b/Mage.Sets/src/mage/cards/g/GogoMasterOfMimicry.java new file mode 100644 index 00000000000..dbc2f0b5191 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GogoMasterOfMimicry.java @@ -0,0 +1,88 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterStackObject; +import mage.filter.common.FilterActivatedOrTriggeredAbility; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.target.common.TargetActivatedOrTriggeredAbility; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GogoMasterOfMimicry extends CardImpl { + + private static final FilterStackObject filter + = new FilterActivatedOrTriggeredAbility("activated or triggered ability you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public GogoMasterOfMimicry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // {X}{X}, {T}: Copy target activated or triggered ability you control X times. You may choose new targets for the copy. This ability can't be copied, and X can't be 0. + Ability ability = new SimpleActivatedAbility(new GogoMasterOfMimicryCopyEffect(), new ManaCostsImpl<>("{X}{X}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetActivatedOrTriggeredAbility(filter)); + CardUtil.castStream(ability.getCosts(), VariableManaCost.class).forEach(cost -> cost.setMinX(1)); + this.addAbility(ability.withCanBeCopied(false)); + } + + private GogoMasterOfMimicry(final GogoMasterOfMimicry card) { + super(card); + } + + @Override + public GogoMasterOfMimicry copy() { + return new GogoMasterOfMimicry(this); + } +} + +class GogoMasterOfMimicryCopyEffect extends OneShotEffect { + + GogoMasterOfMimicryCopyEffect() { + super(Outcome.Benefit); + staticText = "copy target activated or triggered ability you control X times. " + + "You may choose new targets for the copies. This ability can't be copied and X can't be 0"; + } + + private GogoMasterOfMimicryCopyEffect(final GogoMasterOfMimicryCopyEffect effect) { + super(effect); + } + + @Override + public GogoMasterOfMimicryCopyEffect copy() { + return new GogoMasterOfMimicryCopyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int amount = GetXValue.instance.calculate(game, source, this); + StackObject stackObject = game.getStack().getStackObject(getTargetPointer().getFirst(game, source)); + if (amount < 1 || stackObject == null) { + return false; + } + stackObject.createCopyOnStack(game, source, source.getControllerId(), true, amount); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index bcf73de44ac..5fcd0ba85ce 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -221,6 +221,8 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Giott, King of the Dwarves", 223, Rarity.UNCOMMON, mage.cards.g.GiottKingOfTheDwarves.class)); cards.add(new SetCardInfo("Gladiolus Amicitia", 224, Rarity.UNCOMMON, mage.cards.g.GladiolusAmicitia.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gladiolus Amicitia", 489, Rarity.UNCOMMON, mage.cards.g.GladiolusAmicitia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gogo, Master of Mimicry", 377, Rarity.MYTHIC, mage.cards.g.GogoMasterOfMimicry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gogo, Master of Mimicry", 54, Rarity.MYTHIC, mage.cards.g.GogoMasterOfMimicry.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gohn, Town of Ruin", 278, Rarity.COMMON, mage.cards.g.GohnTownOfRuin.class)); cards.add(new SetCardInfo("Gongaga, Reactor Town", 280, Rarity.COMMON, mage.cards.g.GongagaReactorTown.class)); cards.add(new SetCardInfo("Goobbue Gardener", 188, Rarity.COMMON, mage.cards.g.GoobbueGardener.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java index 4c01474b53c..77090668c95 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java @@ -1,6 +1,9 @@ package org.mage.test.cards.copy; import mage.abilities.MageSingleton; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.AdventureCard; import mage.cards.Card; @@ -957,4 +960,23 @@ public class CopySpellTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_COMBAT); execute(); } + + private static final String engine = "Lithoform Engine"; + + @Test + public void testAbilityCantBeCopied() { + addCustomCardWithAbility("activator", playerA, new SimpleActivatedAbility(new GainLifeEffect(1), new TapSourceCost()).withCanBeCopied(false)); + addCard(Zone.BATTLEFIELD, playerA, "Wastes", 2); + addCard(Zone.BATTLEFIELD, playerA, engine); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}", "stack ability ({T"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTapped(engine, true); + assertLife(playerA, 20 + 1); + } } diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index 512da5e120a..93e286a4f56 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -534,6 +534,10 @@ public interface Ability extends Controllable, Serializable { boolean canFizzle(); + Ability withCanBeCopied(boolean canBeCopied); + + boolean canBeCopied(); + /** * Adds a target adjuster to this ability. * If using a generic adjuster, only use after adding the blueprint target! diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 1b8223083ba..84d5ce45c48 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -83,6 +83,7 @@ public abstract class AbilityImpl implements Ability { private List watchers = new ArrayList<>(); // access to it by GetWatchers only (it can be overridden by some abilities) private List subAbilities = null; private boolean canFizzle = true; // for Gilded Drake + private boolean canBeCopied = true; private TargetAdjuster targetAdjuster = null; private CostAdjuster costAdjuster = null; private List hints = new ArrayList<>(); @@ -129,6 +130,7 @@ public abstract class AbilityImpl implements Ability { this.flavorWord = ability.flavorWord; this.sourceObjectZoneChangeCounter = ability.sourceObjectZoneChangeCounter; this.canFizzle = ability.canFizzle; + this.canBeCopied = ability.canBeCopied; this.targetAdjuster = ability.targetAdjuster; this.costAdjuster = ability.costAdjuster; this.hints = CardUtil.deepCopyObject(ability.hints); @@ -1733,6 +1735,17 @@ public abstract class AbilityImpl implements Ability { this.canFizzle = canFizzle; } + @Override + public boolean canBeCopied() { + return canBeCopied; + } + + @Override + public Ability withCanBeCopied(boolean canBeCopied) { + this.canBeCopied = canBeCopied; + return this; + } + @Override public AbilityImpl setTargetAdjuster(TargetAdjuster targetAdjuster) { if (targetAdjuster instanceof GenericTargetAdjuster && this.getTargets().isEmpty()) { diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 89c9b0e4e98..7cc905df6b6 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -1169,6 +1169,11 @@ public class Spell extends StackObjectImpl implements Card { game.fireEvent(new CopiedStackObjectEvent(this, spellCopy, newControllerId)); } + @Override + public boolean canBeCopied() { + return this.getSpellAbility().canBeCopied(); + } + @Override public boolean isAllCreatureTypes(Game game) { return card.isAllCreatureTypes(game); diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 422e3c79585..b79e974e14a 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -718,6 +718,16 @@ public class StackAbility extends StackObjectImpl implements Ability { throw new UnsupportedOperationException("Not supported."); } + @Override + public boolean canBeCopied() { + return ability.canBeCopied(); + } + + @Override + public Ability withCanBeCopied(boolean canBeCopied) { + throw new UnsupportedOperationException("Not supported."); + } + @Override public void createSingleCopy(UUID newControllerId, StackObjectCopyApplier applier, MageObjectReferencePredicate newTargetFilterPredicate, Game game, Ability source, boolean chooseNewTargets) { Ability newAbility = this.ability.copy(); diff --git a/Mage/src/main/java/mage/game/stack/StackObject.java b/Mage/src/main/java/mage/game/stack/StackObject.java index 7c183e78a29..fe53ba09a7b 100644 --- a/Mage/src/main/java/mage/game/stack/StackObject.java +++ b/Mage/src/main/java/mage/game/stack/StackObject.java @@ -40,6 +40,8 @@ public interface StackObject extends MageObject, Controllable { void createSingleCopy(UUID newControllerId, StackObjectCopyApplier applier, MageObjectReferencePredicate newTargetFilterPredicate, Game game, Ability source, boolean chooseNewTargets); + boolean canBeCopied(); + boolean isTargetChanged(); void setTargetChanged(boolean targetChanged); diff --git a/Mage/src/main/java/mage/game/stack/StackObjectImpl.java b/Mage/src/main/java/mage/game/stack/StackObjectImpl.java index 5bd3621b749..fdf38192f3a 100644 --- a/Mage/src/main/java/mage/game/stack/StackObjectImpl.java +++ b/Mage/src/main/java/mage/game/stack/StackObjectImpl.java @@ -154,6 +154,9 @@ public abstract class StackObjectImpl implements StackObject { @Override public void createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount, StackObjectCopyApplier applier) { + if (!this.canBeCopied()) { + return; + } GameEvent gameEvent = new CopyStackObjectEvent(source, this, newControllerId, amount); if (game.replaceEvent(gameEvent)) { return;