[VOC] Implemented Mirage Phalanx (#8654)

* Implemented Mirage Phalanx

* Changed based on first set of comments

* Reordered variables in alphabetical order

* Added support for removing abilities from a token before creating it.

* Fixed typo in CreateTokenCopyTargetEffect, should have been getAddedPermanents not getAddedPermanent (with the s).

* Updated how soulbond is removed from MiragePhalanx tokens

* Fixed missing fields in the copy constructor

* - Refactoring the exileTokensCreated function to exile all tokens as a single trigger.
- Changed Mirage Phalanx to use the new exileTokensCreated function
- changed ’ to '
This commit is contained in:
Alex Vasile 2022-02-05 20:38:12 -05:00 committed by GitHub
parent c68c7699c0
commit 5611f41949
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 250 additions and 74 deletions

View file

@ -112,7 +112,7 @@ class ArcaneArtisanCreateTokenEffect extends OneShotEffect {
} else {
tokensCreated = new HashSet<>();
}
for (Permanent perm : effect.getAddedPermanent()) {
for (Permanent perm : effect.getAddedPermanents()) {
if (perm != null) {
tokensCreated.add(perm.getId());
}

View file

@ -73,7 +73,7 @@ class CogworkAssemblerCreateTokenEffect extends OneShotEffect {
if (copiedPermanent != null) {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.ARTIFACT, true);
if (effect.apply(game, source)) {
for (Permanent copyPermanent : effect.getAddedPermanent()) {
for (Permanent copyPermanent : effect.getAddedPermanents()) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(copyPermanent, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -93,8 +93,8 @@ class DanceOfManyCreateTokenCopyEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect();
effect.setTargetPointer(new FixedTarget(permanent, game));
effect.apply(game, source);
game.getState().setValue(source.getSourceId() + "_token", effect.getAddedPermanent());
for (Permanent addedToken : effect.getAddedPermanent()) {
game.getState().setValue(source.getSourceId() + "_token", effect.getAddedPermanents());
for (Permanent addedToken : effect.getAddedPermanents()) {
Effect sacrificeEffect = new SacrificeTargetEffect("sacrifice Dance of Many");
sacrificeEffect.setTargetPointer(new FixedTarget(sourceObject, game));
LeavesBattlefieldTriggeredAbility triggerAbility = new LeavesBattlefieldTriggeredAbility(sacrificeEffect, false);

View file

@ -94,7 +94,7 @@ class DollhouseOfHorrorsEffect extends OneShotEffect {
effect.apply(game, source);
game.addEffect(new GainAbilityTargetEffect(
HasteAbility.getInstance(), Duration.EndOfTurn
).setTargetPointer(new FixedTargets(effect.getAddedPermanent(), game)), source);
).setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game)), source);
return true;
}
}

View file

@ -97,7 +97,7 @@ class DualNatureCreateTokenEffect extends OneShotEffect {
} else {
tokensCreated = new HashSet<>();
}
for (Permanent perm : effect.getAddedPermanent()) {
for (Permanent perm : effect.getAddedPermanents()) {
if (perm != null) {
tokensCreated.add(perm.getId());
}

View file

@ -71,7 +71,7 @@ class EchoChamberCreateTokenEffect extends OneShotEffect {
if (copiedPermanent != null) {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.CREATURE, true);
if (effect.apply(game, source)) {
for (Permanent copyPermanent : effect.getAddedPermanent()) {
for (Permanent copyPermanent : effect.getAddedPermanents()) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(copyPermanent, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -86,7 +86,7 @@ class FaerieArtisansEffect extends OneShotEffect {
if (effect.apply(game, source)) {
String oldTokens = (String) game.getState().getValue(source.getSourceId().toString() + source.getSourceObjectZoneChangeCounter());
StringBuilder sb = new StringBuilder();
for (Permanent permanent : effect.getAddedPermanent()) {
for (Permanent permanent : effect.getAddedPermanents()) {
if (sb.length() > 0) {
sb.append(';');
}

View file

@ -81,7 +81,7 @@ class FeldonOfTheThirdPathEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), CardType.ARTIFACT, true);
effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId())));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId());
sacrificeEffect.setTargetPointer(new FixedTarget(addedToken, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect);

View file

@ -83,7 +83,7 @@ class FelhideSpiritbinderEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.ENCHANTMENT, true);
effect.setTargetPointer(getTargetPointer());
if (effect.apply(game, source)) {
for (Permanent tokenPermanent : effect.getAddedPermanent()) {
for (Permanent tokenPermanent : effect.getAddedPermanents()) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -89,7 +89,7 @@ class FlamerushRiderEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true, 1, true, true);
effect.setTargetPointer(new FixedTarget(permanent, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
Effect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(addedToken, game));
new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), false).apply(game, source);

View file

@ -81,7 +81,7 @@ class FlameshadowConjuringEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true);
effect.setTargetPointer(getTargetPointer());
if (effect.apply(game, source)) {
for (Permanent tokenPermanent : effect.getAddedPermanent()) {
for (Permanent tokenPermanent : effect.getAddedPermanents()) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -80,7 +80,7 @@ class ForceProjectionEffect extends OneShotEffect {
// and gains "When this creature becomes the target of a spell, sacrifice it."
Effect sacrificeEffect = new SacrificeSourceEffect();
sacrificeEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanent().get(0), game));
sacrificeEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanents().get(0), game));
TriggeredAbility ability = new BecomesTargetTriggeredAbility(sacrificeEffect, new FilterSpell());
game.addTriggeredAbility(ability, null);

View file

@ -96,7 +96,7 @@ class GyrusWakerOfCorpsesEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true, 1, true, true);
effect.setTargetPointer(new FixedTarget(card, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
Effect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(addedToken, game));
new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), false).apply(game, source);

View file

@ -68,7 +68,7 @@ class HeatShimmerEffect extends OneShotEffect {
effect.setTargetPointer(new FixedTarget(permanent, game));
effect.apply(game, source);
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanent().get(0), game));
exileEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanents().get(0), game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;

View file

@ -114,7 +114,7 @@ class InallaArchmageRitualistEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true);
effect.setTargetPointer(getTargetPointer());
if (effect.apply(game, source)) {
for (Permanent tokenPermanent : effect.getAddedPermanent()) {
for (Permanent tokenPermanent : effect.getAddedPermanents()) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -88,7 +88,7 @@ class KikiJikiMirrorBreakerEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true);
effect.setTargetPointer(new FixedTarget(permanent, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId());
sacrificeEffect.setTargetPointer(new FixedTarget(addedToken.getId()));
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect), source);

View file

@ -77,7 +77,7 @@ class KindredChargeEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true);
effect.setTargetPointer(new FixedTarget(permanent, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
Effect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(addedToken, game));
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source);

View file

@ -77,7 +77,7 @@ class LittjaraMirrorlakeEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect();
effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game));
effect.apply(game, source);
for (Permanent permanent : effect.getAddedPermanent()) {
for (Permanent permanent : effect.getAddedPermanents()) {
if (permanent == null) {
continue;
}

View file

@ -182,7 +182,7 @@ class MimicVatCreateTokenEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true);
effect.setTargetPointer(new FixedTarget(card, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(addedToken, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -83,7 +83,7 @@ class MinionReflectorEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true);
effect.setTargetPointer(new FixedTarget(permanent, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
ContinuousEffect continuousEffect = new GainAbilityTargetEffect(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect(), TargetController.ANY, false), Duration.Custom);
continuousEffect.setTargetPointer(new FixedTarget(addedToken.getId()));
game.addEffect(continuousEffect, source);

View file

@ -0,0 +1,91 @@
package mage.cards.m;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfCombatTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityPairedEffect;
import mage.abilities.keyword.SoulbondAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
* @author Alex-Vasile
*/
public class MiragePhalanx extends CardImpl {
private static final String ruleText =
"As long as {this} is paired with another creature, each of those creatures has " +
"\"At the beginning of combat on your turn, create a token that's a copy of this creature, " +
"except it has haste and loses soulbond. " +
"Exile it at end of combat.\"";
public MiragePhalanx(UUID ownderId, CardSetInfo setInfo) {
super(ownderId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}");
this.addSubType(SubType.HUMAN);
this.addSubType(SubType.SOLDIER);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Soulbond
this.addAbility(new SoulbondAbility());
// As long as Mirage Phalanx is paired with another creature, each of those creatures has
// At the beginning of combat on your turn, create a token that's a copy of this creature,
// except it has haste and loses soulbond.
// Exile it at end of combat.
Ability ability = new BeginningOfCombatTriggeredAbility(new MiragePhalanxEffect(), TargetController.YOU, false);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityPairedEffect(ability, ruleText)));
}
private MiragePhalanx(final MiragePhalanx card) {
super(card);
}
@Override
public MiragePhalanx copy() {
return new MiragePhalanx(this);
}
}
class MiragePhalanxEffect extends OneShotEffect {
MiragePhalanxEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "create a token that's a copy of this creature, " +
"except it has haste and loses soulbond. " +
"Exile it at end of combat.";
}
private MiragePhalanxEffect(final MiragePhalanxEffect effect) { super(effect); }
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = source.getSourcePermanentOrLKI(game);
if (permanent == null) { return false; }
// It has haste
CreateTokenCopyTargetEffect tokenCopyEffect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true);
tokenCopyEffect.setTargetPointer(new FixedTarget(permanent, game));
// It loses soulbond
tokenCopyEffect.addAbilityClassesToRemoveFromTokens(SoulbondAbility.class);
// Create the token(s)
tokenCopyEffect.apply(game, source);
// Exile it at the end of combat
tokenCopyEffect.exileTokensCreatedAtEndOfCombat(game, source);
return !tokenCopyEffect.getAddedPermanents().isEmpty();
}
@Override
public Effect copy() { return new MiragePhalanxEffect(this); }
}

View file

@ -77,14 +77,14 @@ class MirrorMatchEffect extends OneShotEffect {
CombatGroup group = game.getCombat().findGroup(attacker.getId());
boolean isCreature = false;
if (group != null) {
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
if (addedToken.isCreature(game)) {
group.addBlockerToGroup(addedToken.getId(), attackerId, game);
isCreature = true;
}
}
ExileTargetEffect exileEffect = new ExileTargetEffect("Exile those tokens at end of combat");
exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanent(), game));
exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game));
game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source);
if (isCreature) {
group.pickBlockerOrder(attacker.getControllerId(), game);

View file

@ -85,7 +85,7 @@ class MirrorMockeryEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect();
effect.setTargetPointer(new FixedTarget(enchanted, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
if (addedToken != null) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(addedToken, game));

View file

@ -82,7 +82,7 @@ class MoltenEchoesEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true);
effect.setTargetPointer(getTargetPointer());
if (effect.apply(game, source)) {
for (Permanent tokenPermanent : effect.getAddedPermanent()) {
for (Permanent tokenPermanent : effect.getAddedPermanents()) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -105,7 +105,7 @@ class NacatlWarPrideEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(controller.getId(), null, false, count, true, true);
effect.setTargetPointer(new FixedTarget(origNactalWarPride, game));
effect.apply(game, source);
copies.addAll(effect.getAddedPermanent());
copies.addAll(effect.getAddedPermanents());
if (!copies.isEmpty()) {
FixedTargets fixedTargets = new FixedTargets(copies, game);

View file

@ -88,7 +88,7 @@ class NemesisTrapEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect();
effect.setTargetPointer(new FixedTarget(targetedCreature, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
Effect exileEffect = new ExileTargetEffect("Exile " + addedToken.getName() + " at the beginning of the next end step");
exileEffect.setTargetPointer(new FixedTarget(addedToken, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -91,7 +91,7 @@ class OffspringsRevengeEffect extends OneShotEffect {
effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1));
player.moveCards(card, Zone.EXILED, source, game);
effect.apply(game, source);
effect.getAddedPermanent().stream().forEach(permanent -> {
effect.getAddedPermanents().stream().forEach(permanent -> {
ContinuousEffect continuousEffect = new GainAbilityTargetEffect(
HasteAbility.getInstance(), Duration.UntilYourNextTurn
);

View file

@ -87,7 +87,7 @@ class SaheeliRaiCreateTokenEffect extends OneShotEffect {
if (copiedPermanent != null) {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.ARTIFACT, true);
if (effect.apply(game, source)) {
for (Permanent copyPermanent : effect.getAddedPermanent()) {
for (Permanent copyPermanent : effect.getAddedPermanents()) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(copyPermanent, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -75,7 +75,7 @@ class SeanceEffect extends OneShotEffect {
effect.setAdditionalSubType(SubType.SPIRIT);
effect.apply(game, source);
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanent(), game));
exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;

View file

@ -81,7 +81,7 @@ class SplinterTwinEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true);
effect.setTargetPointer(new FixedTarget(permanent, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
for (Permanent addedToken : effect.getAddedPermanents()) {
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(addedToken, game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);

View file

@ -77,7 +77,7 @@ class TwinflameCopyEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true);
effect.setTargetPointer(new FixedTarget(creature, game));
effect.apply(game, source);
toExile.addAll(effect.getAddedPermanent());
toExile.addAll(effect.getAddedPermanents());
}
}
ExileTargetEffect exileEffect = new ExileTargetEffect();

View file

@ -99,6 +99,7 @@ public final class CrimsonVowCommander extends ExpansionSet {
cards.add(new SetCardInfo("Midnight Arsonist", 27, Rarity.RARE, mage.cards.m.MidnightArsonist.class));
cards.add(new SetCardInfo("Midnight Clock", 108, Rarity.RARE, mage.cards.m.MidnightClock.class));
cards.add(new SetCardInfo("Millicent, Restless Revenant", 1, Rarity.MYTHIC, mage.cards.m.MillicentRestlessRevenant.class));
cards.add(new SetCardInfo("Mirage Phalanx", 35, Rarity.RARE, mage.cards.m.MiragePhalanx.class));
cards.add(new SetCardInfo("Mirror Entity", 94, Rarity.RARE, mage.cards.m.MirrorEntity.class));
cards.add(new SetCardInfo("Mob Rule", 147, Rarity.RARE, mage.cards.m.MobRule.class));
cards.add(new SetCardInfo("Molten Echoes", 148, Rarity.RARE, mage.cards.m.MoltenEchoes.class));

View file

@ -3,10 +3,14 @@ package mage.abilities.effects.common;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.Mode;
import mage.abilities.TriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.EffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.HasteAbility;
@ -16,43 +20,44 @@ import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.EmptyToken;
import mage.game.turn.Step;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.FixedTargets;
import mage.util.CardUtil;
import mage.util.functions.CopyApplier;
import mage.util.functions.EmptyCopyApplier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.*;
/**
* @author LevelX2
*/
public class CreateTokenCopyTargetEffect extends OneShotEffect {
private final UUID playerId;
private final CardType additionalCardType;
private boolean hasHaste;
private int number;
private final Set<Class<? extends Ability>> abilityClazzesToRemove;
private final List<Permanent> addedTokenPermanents;
private final List<Ability> additionalAbilities;
private final CardType additionalCardType;
private SubType additionalSubType;
private SubType onlySubType;
private final boolean tapped;
private final boolean attacking;
private final UUID attackedPlayer;
private final int tokenPower;
private final int tokenToughness;
private final boolean gainsFlying;
private final boolean attacking;
private boolean becomesArtifact;
private ObjectColor color;
private boolean useLKI = false;
private boolean isntLegendary = false;
private int startingLoyalty = -1;
private final List<Ability> additionalAbilities = new ArrayList();
private Permanent savedPermanent = null;
private CounterType counter;
private final boolean gainsFlying;
private boolean hasHaste;
private boolean isntLegendary = false;
private int number;
private int numberOfCounters;
private SubType onlySubType;
private final UUID playerId;
private final boolean tapped;
private Permanent savedPermanent = null;
private int startingLoyalty = -1;
private final int tokenPower;
private final int tokenToughness;
private boolean useLKI = false;
public CreateTokenCopyTargetEffect(boolean useLKI) {
this();
@ -111,27 +116,37 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
this.tokenPower = power;
this.tokenToughness = toughness;
this.gainsFlying = gainsFlying;
this.abilityClazzesToRemove = new HashSet<>();
this.additionalAbilities = new ArrayList<>();
}
public CreateTokenCopyTargetEffect(final CreateTokenCopyTargetEffect effect) {
super(effect);
this.playerId = effect.playerId;
this.additionalCardType = effect.additionalCardType;
this.hasHaste = effect.hasHaste;
this.abilityClazzesToRemove = new HashSet<>(effect.abilityClazzesToRemove);
this.addedTokenPermanents = new ArrayList<>(effect.addedTokenPermanents);
this.number = effect.number;
this.additionalAbilities = new ArrayList<>(effect.additionalAbilities);
this.additionalCardType = effect.additionalCardType;
this.additionalSubType = effect.additionalSubType;
this.onlySubType = effect.onlySubType;
this.tapped = effect.tapped;
this.attacking = effect.attacking;
this.attackedPlayer = effect.attackedPlayer;
this.tokenPower = effect.tokenPower;
this.tokenToughness = effect.tokenToughness;
this.gainsFlying = effect.gainsFlying;
this.attacking = effect.attacking;
this.becomesArtifact = effect.becomesArtifact;
this.color = effect.color;
this.useLKI = effect.useLKI;
this.counter = effect.counter;
this.gainsFlying = effect.gainsFlying;
this.hasHaste = effect.hasHaste;
this.isntLegendary = effect.isntLegendary;
this.number = effect.number;
this.numberOfCounters = effect.numberOfCounters;
this.onlySubType = effect.onlySubType;
this.playerId = effect.playerId;
this.savedPermanent = effect.savedPermanent;
this.startingLoyalty = effect.startingLoyalty;
this.tapped = effect.tapped;
this.tokenPower = effect.tokenPower;
this.tokenToughness = effect.tokenToughness;
this.useLKI = effect.useLKI;
}
@Override
@ -224,6 +239,25 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
}
additionalAbilities.stream().forEach(token::addAbility);
if (!this.abilityClazzesToRemove.isEmpty()) {
List<Ability> abilitiesToRemoveTmp = new ArrayList<>();
// Find the ones to remove
for (Ability ability : token.getAbilities()) {
if (this.abilityClazzesToRemove.contains(ability.getClass())) {
abilitiesToRemoveTmp.add(ability);
}
}
// Remove them
for (Ability ability : abilitiesToRemoveTmp) {
// Remove subabilities
token.removeAbilities(ability.getSubAbilities());
// Remove the ability
token.removeAbility(ability);
}
}
token.putOntoBattlefield(number, game, source, playerId == null ? source.getControllerId() : playerId, tapped, attacking, attackedPlayer);
for (UUID tokenId : token.getLastAddedTokenIds()) { // by cards like Doubling Season multiple tokens can be added to the battlefield
Permanent tokenPermanent = game.getPermanent(tokenId);
@ -280,7 +314,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
return sb.toString();
}
public List<Permanent> getAddedPermanent() {
public List<Permanent> getAddedPermanents() {
return addedTokenPermanents;
}
@ -321,26 +355,43 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
}
public void exileTokensCreatedAtNextEndStep(Game game, Ability source) {
for (Permanent tokenPermanent : addedTokenPermanents) {
ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD);
exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game));
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source);
}
this.exileTokensCreatedAtEndOf(game, source, PhaseStep.END_TURN);
}
public void exileTokensCreatedAtEndOfCombat(Game game, Ability source) {
for (Permanent tokenPermanent : addedTokenPermanents) {
this.exileTokensCreatedAtEndOf(game, source, PhaseStep.END_COMBAT);
}
ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD);
exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game));
game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source);
private void exileTokensCreatedAtEndOf(Game game, Ability source, PhaseStep phaseStepToExileCards) {
ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD);
exileEffect.setText("exile the token copies");
exileEffect.setTargetPointer(new FixedTargets(addedTokenPermanents, game));
DelayedTriggeredAbility exileAbility;
switch (phaseStepToExileCards) {
case END_TURN:
exileAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);
break;
case END_COMBAT:
exileAbility = new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect);
break;
default:
return;
}
game.addDelayedTriggeredAbility(exileAbility, source);
}
public void addAbilityClassesToRemoveFromTokens(Class<? extends Ability> clazz) {
this.abilityClazzesToRemove.add(clazz);
}
public void addAdditionalAbilities(Ability... abilities) {
Arrays.stream(abilities).forEach(this.additionalAbilities::add);
this.additionalAbilities.addAll(Arrays.asList(abilities));
}
public CreateTokenCopyTargetEffect setSavedPermanent(Permanent savedPermanent) {
this.savedPermanent = savedPermanent;
return this;

View file

@ -79,7 +79,7 @@ class MyriadEffect extends OneShotEffect {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(controller.getId(), null, false, 1, true, true, playerId);
effect.setTargetPointer(new FixedTarget(sourceObject, game));
effect.apply(game, source);
tokens.addAll(effect.getAddedPermanent());
tokens.addAll(effect.getAddedPermanents());
}
}
}

View file

@ -27,6 +27,10 @@ public interface Token extends MageObject {
void addAbility(Ability ability);
void removeAbility(Ability abilityToRemove);
void removeAbilities(List<Ability> abilitiesToRemove);
boolean putOntoBattlefield(int amount, Game game, Ability source);
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId);

View file

@ -130,6 +130,35 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
abilities.addAll(ability.getSubAbilities());
}
// Directly from PermanentImpl
@Override
public void removeAbility(Ability abilityToRemove) {
if (abilityToRemove == null) {
return;
}
// 112.10b Effects that remove an ability remove all instances of it.
List<Ability> toRemove = new ArrayList<>();
abilities.forEach(a -> {
if (a.isSameInstance(abilityToRemove)) {
toRemove.add(a);
}
});
// TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState
toRemove.forEach(r -> abilities.remove(r));
}
// Directly from PermanentImpl
@Override
public void removeAbilities(List<Ability> abilitiesToRemove) {
if (abilitiesToRemove == null) {
return;
}
abilitiesToRemove.forEach(a -> removeAbility(a));
}
@Override
public boolean putOntoBattlefield(int amount, Game game, Ability source) {
return this.putOntoBattlefield(amount, game, source, source.getControllerId());