forked from External/mage
[FIN] Implement Gogo, Master of Mimicry (#13686)
* [FIN] Implement Gogo, Master of Mimicry * add test * fix text * move abstract method definition to interface where it belongs
This commit is contained in:
parent
70b1f23248
commit
56e9986b06
9 changed files with 149 additions and 0 deletions
88
Mage.Sets/src/mage/cards/g/GogoMasterOfMimicry.java
Normal file
88
Mage.Sets/src/mage/cards/g/GogoMasterOfMimicry.java
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
private List<Watcher> watchers = new ArrayList<>(); // access to it by GetWatchers only (it can be overridden by some abilities)
|
||||
private List<Ability> subAbilities = null;
|
||||
private boolean canFizzle = true; // for Gilded Drake
|
||||
private boolean canBeCopied = true;
|
||||
private TargetAdjuster targetAdjuster = null;
|
||||
private CostAdjuster costAdjuster = null;
|
||||
private List<Hint> 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()) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue