From 7bc5105c1f6679d2a713ef073b0f621f19573cce Mon Sep 17 00:00:00 2001
From: PurpleCrowbar <26198472+PurpleCrowbar@users.noreply.github.com>
Date: Sat, 14 Oct 2023 04:28:29 +0100
Subject: [PATCH] Implement Time Travel mechanic (#11299)
Implement [WHO] Wibbly-wobbly, Timey-wimey
---
.../mage/cards/w/WibblywobblyTimeywimey.java | 34 ++++++
Mage.Sets/src/mage/sets/DoctorWho.java | 1 +
.../common/counter/TimeTravelEffect.java | 107 ++++++++++++++++++
.../TargetPermanentOrSuspendedCard.java | 8 +-
4 files changed, 148 insertions(+), 2 deletions(-)
create mode 100644 Mage.Sets/src/mage/cards/w/WibblywobblyTimeywimey.java
create mode 100644 Mage/src/main/java/mage/abilities/effects/common/counter/TimeTravelEffect.java
diff --git a/Mage.Sets/src/mage/cards/w/WibblywobblyTimeywimey.java b/Mage.Sets/src/mage/cards/w/WibblywobblyTimeywimey.java
new file mode 100644
index 00000000000..da3e6071316
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/w/WibblywobblyTimeywimey.java
@@ -0,0 +1,34 @@
+package mage.cards.w;
+
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.counter.TimeTravelEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+
+import java.util.UUID;
+
+/**
+ * @author PurpleCrowbar
+ */
+public final class WibblywobblyTimeywimey extends CardImpl {
+
+ public WibblywobblyTimeywimey(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}");
+
+ // Time travel. (For each suspended card you own and each permanent you control with a time counter on it, you may add or remove a time counter.)
+ this.getSpellAbility().addEffect(new TimeTravelEffect());
+
+ // Draw a card.
+ this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
"));
+ }
+
+ private WibblywobblyTimeywimey(final WibblywobblyTimeywimey card) {
+ super(card);
+ }
+
+ @Override
+ public WibblywobblyTimeywimey copy() {
+ return new WibblywobblyTimeywimey(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/sets/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java
index a4b5dd40877..99c1feb14b3 100644
--- a/Mage.Sets/src/mage/sets/DoctorWho.java
+++ b/Mage.Sets/src/mage/sets/DoctorWho.java
@@ -203,6 +203,7 @@ public final class DoctorWho extends ExpansionSet {
cards.add(new SetCardInfo("Waterlogged Grove", 331, Rarity.RARE, mage.cards.w.WaterloggedGrove.class));
cards.add(new SetCardInfo("Wayfarer's Bauble", 256, Rarity.COMMON, mage.cards.w.WayfarersBauble.class));
cards.add(new SetCardInfo("Wedding Ring", 213, Rarity.MYTHIC, mage.cards.w.WeddingRing.class));
+ cards.add(new SetCardInfo("Wibbly-wobbly, Timey-wimey", 62, Rarity.COMMON, mage.cards.w.WibblywobblyTimeywimey.class));
cards.add(new SetCardInfo("Wound Reflection", 223, Rarity.RARE, mage.cards.w.WoundReflection.class));
cards.add(new SetCardInfo("Yasmin Khan", 7, Rarity.RARE, mage.cards.y.YasminKhan.class));
}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/TimeTravelEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/TimeTravelEffect.java
new file mode 100644
index 00000000000..32b6708e765
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/counter/TimeTravelEffect.java
@@ -0,0 +1,107 @@
+package mage.abilities.effects.common.counter;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.Card;
+import mage.constants.Outcome;
+import mage.constants.TargetController;
+import mage.counters.CounterType;
+import mage.filter.common.FilterPermanentOrSuspendedCard;
+import mage.filter.predicate.Predicates;
+import mage.filter.predicate.mageobject.CardIdPredicate;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.Target;
+import mage.target.common.TargetPermanentOrSuspendedCard;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @author PurpleCrowbar
+ */
+public class TimeTravelEffect extends OneShotEffect {
+
+ public TimeTravelEffect() {
+ this("", true);
+ }
+
+ public TimeTravelEffect(boolean showAbilityHint) {
+ this("", showAbilityHint);
+ }
+
+ public TimeTravelEffect(String afterText, boolean showAbilityHint) {
+ super(Outcome.Benefit);
+ staticText = "time travel" + afterText;
+ if (showAbilityHint) {
+ staticText += ". (For each suspended card you own and each permanent you control with a time counter on it, you may add or remove a time counter.)";
+ }
+ }
+
+ public TimeTravelEffect(TimeTravelEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
+ if (controller == null) {
+ return false;
+ }
+
+ FilterPermanentOrSuspendedCard filter = new FilterPermanentOrSuspendedCard("permanent you control with a time counter or suspended card you own to ADD a time counter to");
+ filter.getPermanentFilter().add(TargetController.YOU.getControllerPredicate());
+ filter.getPermanentFilter().add(CounterType.TIME.getPredicate());
+ filter.getCardFilter().add(TargetController.YOU.getOwnerPredicate());
+
+ Target target = new TargetPermanentOrSuspendedCard(filter, true, 0, Integer.MAX_VALUE);
+ Map options = new HashMap<>();
+ options.put("UI.right.btn.text", "Done");
+ controller.choose(Outcome.Benefit, target, source, game, options);
+
+ // Adding time counters
+ for (UUID chosen : target.getTargets()) {
+ Permanent permanent = game.getPermanent(chosen);
+ if (permanent != null) {
+ permanent.addCounters(CounterType.TIME.createInstance(), source.getControllerId(), source, game);
+ game.informPlayers(permanent.getName() + " had a time counter added to it.");
+ filter.getPermanentFilter().add(Predicates.not(new CardIdPredicate(chosen)));
+ continue;
+ }
+ Card card = game.getCard(chosen);
+ if (card != null) {
+ card.addCounters(CounterType.TIME.createInstance(), source.getControllerId(), source, game);
+ game.informPlayers(card.getName() + " had a time counter added to it.");
+ filter.getCardFilter().add(Predicates.not(new CardIdPredicate(chosen)));
+ }
+ }
+
+ // Removing time counters
+ filter.setMessage("permanent you control with a time counter or suspended card you own to REMOVE a time counter from");
+ target = new TargetPermanentOrSuspendedCard(filter, true, 0, Integer.MAX_VALUE);
+ controller.choose(Outcome.Benefit, target, source, game, options);
+ for (UUID chosen : target.getTargets()) {
+ Permanent permanent = game.getPermanent(chosen);
+ if (permanent != null) {
+ permanent.removeCounters(CounterType.TIME.createInstance(), source, game);
+ game.informPlayers(permanent.getName() + " had a time counter removed from it.");
+ continue;
+ }
+ Card card = game.getCard(chosen);
+ if (card != null) {
+ card.removeCounters(CounterType.TIME.createInstance(), source, game);
+ game.informPlayers(card.getName() + " had a time counter removed from it.");
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public TimeTravelEffect copy() {
+ return new TimeTravelEffect(this);
+ }
+}
diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java
index 1458a8812d4..61ceadcdd16 100644
--- a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java
+++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java
@@ -26,12 +26,16 @@ public class TargetPermanentOrSuspendedCard extends TargetImpl {
}
public TargetPermanentOrSuspendedCard(FilterPermanentOrSuspendedCard filter, boolean notTarget) {
+ this(filter, notTarget, 1, 1);
+ }
+
+ public TargetPermanentOrSuspendedCard(FilterPermanentOrSuspendedCard filter, boolean notTarget, int minTargets, int maxTargets) {
super(notTarget);
this.filter = filter;
this.zone = Zone.ALL;
this.targetName = filter.getMessage();
- this.minNumberOfTargets = 1;
- this.maxNumberOfTargets = 1;
+ this.minNumberOfTargets = minTargets;
+ this.maxNumberOfTargets = maxTargets;
}
protected TargetPermanentOrSuspendedCard(final TargetPermanentOrSuspendedCard target) {