From 074a4e64111b58fb7d7911f1385aa46e85464c88 Mon Sep 17 00:00:00 2001 From: Daniel Eberhard Date: Wed, 14 Dec 2022 14:05:01 +0100 Subject: [PATCH] Added card "Dyad Force Transfer" and its abilities and effects --- Mage.Sets/src/mage/cards/d/Droidsmith.java | 2 +- .../src/mage/cards/d/DyadForceTransfer.java | 33 +++- .../GainControlAndUntapTargetEffect.java | 159 ++++++++++++++++++ .../main/java/mage/filter/StaticFilters.java | 12 +- .../common/FilterNoncreaturePermanent.java | 33 ++++ 5 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAndUntapTargetEffect.java create mode 100644 Mage/src/main/java/mage/filter/common/FilterNoncreaturePermanent.java diff --git a/Mage.Sets/src/mage/cards/d/Droidsmith.java b/Mage.Sets/src/mage/cards/d/Droidsmith.java index 60468c6d52..f5ecb475a3 100644 --- a/Mage.Sets/src/mage/cards/d/Droidsmith.java +++ b/Mage.Sets/src/mage/cards/d/Droidsmith.java @@ -53,7 +53,7 @@ public class Droidsmith extends CardImpl { } @Override - public Card copy() { + public Droidsmith copy() { return new Droidsmith(this); } } diff --git a/Mage.Sets/src/mage/cards/d/DyadForceTransfer.java b/Mage.Sets/src/mage/cards/d/DyadForceTransfer.java index 325352cc10..c73fa70f53 100644 --- a/Mage.Sets/src/mage/cards/d/DyadForceTransfer.java +++ b/Mage.Sets/src/mage/cards/d/DyadForceTransfer.java @@ -1,4 +1,35 @@ package mage.cards.d; -public class DyadForceTransfer { +import mage.abilities.effects.common.continuous.GainControlAndUntapTargetEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +public class DyadForceTransfer extends CardImpl { + public DyadForceTransfer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + //Gain control of target noncreature permanent until end of turn. Untap it. + this.getSpellAbility().addEffect(new GainControlAndUntapTargetEffect(Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetPermanent(1, 1, + StaticFilters.FILTER_PERMANENT_NON_CREATURE)); + + //Scry 3. + this.getSpellAbility().addEffect(new ScryEffect(3, true)); + } + + public DyadForceTransfer(DyadForceTransfer card) { + super(card); + } + + @Override + public DyadForceTransfer copy() { + return new DyadForceTransfer(this); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAndUntapTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAndUntapTargetEffect.java new file mode 100644 index 0000000000..68cea91cac --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainControlAndUntapTargetEffect.java @@ -0,0 +1,159 @@ +package mage.abilities.effects.common.continuous; + +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.Mode; +import mage.abilities.condition.Condition; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author Merlingilb + */ +public class GainControlAndUntapTargetEffect extends ContinuousEffectImpl { + + protected UUID controllingPlayerId; + private boolean fixedControl; + private boolean firstControlChange = true; + private final Condition condition; + + public GainControlAndUntapTargetEffect(Duration duration) { + this(duration, false, null); + } + + /** + * @param duration + * @param fixedControl Controlling player is fixed even if the controller of + * the ability changes later + */ + public GainControlAndUntapTargetEffect(Duration duration, boolean fixedControl) { + this(duration, fixedControl, null); + } + + /** + * @param duration + * @param controllingPlayerId Player that controls the target creature + */ + public GainControlAndUntapTargetEffect(Duration duration, UUID controllingPlayerId) { + this(duration, true, controllingPlayerId); + + } + + public GainControlAndUntapTargetEffect(Duration duration, boolean fixedControl, UUID controllingPlayerId) { + this(duration, fixedControl, controllingPlayerId, null); + } + + public GainControlAndUntapTargetEffect(Duration duration, boolean fixedControl, UUID controllingPlayerId, Condition condition) { + super(duration, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); + this.controllingPlayerId = controllingPlayerId; + this.fixedControl = fixedControl; + this.condition = condition; + } + + public GainControlAndUntapTargetEffect(final GainControlAndUntapTargetEffect effect) { + super(effect); + this.controllingPlayerId = effect.controllingPlayerId; + this.fixedControl = effect.fixedControl; + this.condition = effect.condition; + } + + @Override + public GainControlAndUntapTargetEffect copy() { + return new GainControlAndUntapTargetEffect(this); + } + + @Override + public void init(Ability source, Game game) { + if (this.controllingPlayerId == null && fixedControl) { + this.controllingPlayerId = source.getControllerId(); + } + super.init(source, game); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + discard(); // controller no longer exists + return false; + } + boolean oneTargetStillExists = false; + for (UUID permanentId : getTargetPointer().getTargets(game, source)) { + Permanent permanent = game.getPermanent(permanentId); + if (permanent == null) { + continue; + } + oneTargetStillExists = true; + if (permanent.isControlledBy(controllingPlayerId)) { + permanent.untap(game); + continue; + } + boolean controlChanged = false; + if (controllingPlayerId != null) { + if (permanent.changeControllerId(controllingPlayerId, game, source)) { + permanent.untap(game); + controlChanged = true; + } + } else { + if (permanent.changeControllerId(source.getControllerId(), game, source)) { + permanent.untap(game); + controlChanged = true; + } + } + if (source instanceof ActivatedAbility + && firstControlChange && !controlChanged) { + // If it was not possible to get control of target permanent by the activated ability the first time it took place + // the effect failed (e.g. because of Guardian Beast) and must be discarded + // This does not handle correctly multiple targets at once + discard(); + } + if (condition != null && !condition.apply(game, source)) { + discard(); + } + } + // no valid target exists and the controller is no longer in the game, effect can be discarded + if (!oneTargetStillExists || !controller.isInGame()) { + discard(); + } + firstControlChange = false; + return true; + } + + @Override + public String getText(Mode mode) { + if (!staticText.isEmpty()) { + return staticText; + } + + if (mode.getTargets().isEmpty()) { + return "gain control of target permanent until end of turn. Untap it."; + } + + Target target = mode.getTargets().get(0); + StringBuilder sb = new StringBuilder("gain control of "); + if (target.getMaxNumberOfTargets() > 1) { + if (target.getNumberOfTargets() < target.getMaxNumberOfTargets()) { + sb.append("up to "); + } + sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" target "); + } else if (!target.getTargetName().startsWith("another")) { + sb.append("target "); + } + sb.append(mode.getTargets().get(0).getTargetName()); + if (!duration.toString().isEmpty()) { + sb.append(' ').append(duration.toString()); + } + sb.append("and untap it"); + return sb.toString(); + } +} diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index 0379138ba8..bd5c176832 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -468,6 +468,12 @@ public final class StaticFilters { FILTER_OPPONENTS_PERMANENT_NON_LAND.setLockedFilter(true); } + public static final FilterNoncreaturePermanent FILTER_PERMANENT_NON_CREATURE = new FilterNoncreaturePermanent(); + + static { + FILTER_PERMANENT_NON_CREATURE.setLockedFilter(true); + } + public static final FilterCreaturePermanent FILTER_OPPONENTS_PERMANENT_CREATURE = new FilterOpponentsCreaturePermanent(); static { @@ -771,21 +777,21 @@ public final class StaticFilters { FILTER_SPELL_CREATURE.setLockedFilter(true); } - public static final FilterSpell FILTER_SPELL_NON_CREATURE = (FilterSpell) new FilterSpell("noncreature spell"); + public static final FilterSpell FILTER_SPELL_NON_CREATURE = new FilterSpell("noncreature spell"); static { FILTER_SPELL_NON_CREATURE.add(Predicates.not(CardType.CREATURE.getPredicate())); FILTER_SPELL_NON_CREATURE.setLockedFilter(true); } - public static final FilterSpell FILTER_SPELLS_NON_CREATURE = (FilterSpell) new FilterSpell("noncreature spells"); + public static final FilterSpell FILTER_SPELLS_NON_CREATURE = new FilterSpell("noncreature spells"); static { FILTER_SPELLS_NON_CREATURE.add(Predicates.not(CardType.CREATURE.getPredicate())); FILTER_SPELLS_NON_CREATURE.setLockedFilter(true); } - public static final FilterSpell FILTER_SPELL_A_NON_CREATURE = (FilterSpell) new FilterSpell("a noncreature spell"); + public static final FilterSpell FILTER_SPELL_A_NON_CREATURE = new FilterSpell("a noncreature spell"); static { FILTER_SPELL_A_NON_CREATURE.add(Predicates.not(CardType.CREATURE.getPredicate())); diff --git a/Mage/src/main/java/mage/filter/common/FilterNoncreaturePermanent.java b/Mage/src/main/java/mage/filter/common/FilterNoncreaturePermanent.java new file mode 100644 index 0000000000..c13fae6727 --- /dev/null +++ b/Mage/src/main/java/mage/filter/common/FilterNoncreaturePermanent.java @@ -0,0 +1,33 @@ + + +package mage.filter.common; + +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; + + +/** + * @author Merlingilb + */ +public class FilterNoncreaturePermanent extends FilterPermanent { + + public FilterNoncreaturePermanent() { + this("noncreature permanent"); + } + + public FilterNoncreaturePermanent(String name) { + super(name); + this.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public FilterNoncreaturePermanent(final FilterNoncreaturePermanent filter) { + super(filter); + } + + @Override + public FilterNoncreaturePermanent copy() { + return new FilterNoncreaturePermanent(this); + } + +}