From 77d1f18ec75f6d6e662386a72bae195416477844 Mon Sep 17 00:00:00 2001 From: Noah Gleason Date: Fri, 22 Jun 2018 11:53:27 -0400 Subject: [PATCH 1/2] Start implementing Giant Oyster --- Mage.Sets/src/mage/cards/g/GiantOyster.java | 83 +++++++++++++++++++ Mage.Sets/src/mage/sets/Homelands.java | 1 + .../src/mage/sets/TimeSpiralTimeshifted.java | 1 + 3 files changed, 85 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/g/GiantOyster.java diff --git a/Mage.Sets/src/mage/cards/g/GiantOyster.java b/Mage.Sets/src/mage/cards/g/GiantOyster.java new file mode 100644 index 0000000000..1e5da3bc0d --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GiantOyster.java @@ -0,0 +1,83 @@ +package mage.cards.g; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SkipUntapOptionalAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DontUntapAsLongAsSourceTappedEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author noahg + */ +public final class GiantOyster extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("tapped creature"); + + static { + filter.add(new TappedPredicate()); + } + + public GiantOyster(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); + + this.subtype.add(SubType.OYSTER); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // You may choose not to untap Giant Oyster during your untap step. + this.addAbility(new SkipUntapOptionalAbility()); + + // {tap}: For as long as Giant Oyster remains tapped, target tapped creature doesn't untap during its controller's untap step, and at the beginning of each of your draw steps, put a -1/-1 counter on that creature. When Giant Oyster leaves the battlefield or becomes untapped, remove all -1/-1 counters from the creature. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DontUntapAsLongAsSourceTappedEffect(), new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + } + + public GiantOyster(final GiantOyster card) { + super(card); + } + + @Override + public GiantOyster copy() { + return new GiantOyster(this); + } +} + +class GiantOysterDrawStepDelayedTriggeredAbility extends DelayedTriggeredAbility { + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType().equals(GameEvent.EventType.DRAW_STEP_PRE); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getPlayerId().equals(getControllerId()) && game.getPermanent(getSourcePermanentIfItStillExists())){ + return true; + } + return false; + } + + @Override + public DelayedTriggeredAbility copy() { + return null; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Homelands.java b/Mage.Sets/src/mage/sets/Homelands.java index 5de1fb7d4e..219c8697bc 100644 --- a/Mage.Sets/src/mage/sets/Homelands.java +++ b/Mage.Sets/src/mage/sets/Homelands.java @@ -103,6 +103,7 @@ public final class Homelands extends ExpansionSet { cards.add(new SetCardInfo("Forget", 26, Rarity.RARE, mage.cards.f.Forget.class)); cards.add(new SetCardInfo("Funeral March", 48, Rarity.UNCOMMON, mage.cards.f.FuneralMarch.class)); cards.add(new SetCardInfo("Ghost Hounds", 49, Rarity.UNCOMMON, mage.cards.g.GhostHounds.class)); + cards.add(new SetCardInfo("Giant Oyster", 35, Rarity.UNCOMMON, mage.cards.g.GiantOyster.class)); cards.add(new SetCardInfo("Grandmother Sengir", 50, Rarity.RARE, mage.cards.g.GrandmotherSengir.class)); cards.add(new SetCardInfo("Greater Werewolf", 51, Rarity.UNCOMMON, mage.cards.g.GreaterWerewolf.class)); cards.add(new SetCardInfo("Hazduhr the Abbot", 8, Rarity.RARE, mage.cards.h.HazduhrTheAbbot.class)); diff --git a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java index c73f279605..da2fe23c4b 100644 --- a/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java +++ b/Mage.Sets/src/mage/sets/TimeSpiralTimeshifted.java @@ -67,6 +67,7 @@ public final class TimeSpiralTimeshifted extends ExpansionSet { cards.add(new SetCardInfo("Gaea's Liege", 78, Rarity.SPECIAL, mage.cards.g.GaeasLiege.class)); cards.add(new SetCardInfo("Gemstone Mine", 119, Rarity.RARE, mage.cards.g.GemstoneMine.class)); cards.add(new SetCardInfo("Ghost Ship", 21, Rarity.SPECIAL, mage.cards.g.GhostShip.class)); + cards.add(new SetCardInfo("Giant Oyster", 22, Rarity.SPECIAL, mage.cards.g.GiantOyster.class)); cards.add(new SetCardInfo("Goblin Snowman", 64, Rarity.UNCOMMON, mage.cards.g.GoblinSnowman.class)); cards.add(new SetCardInfo("Grinning Totem", 110, Rarity.SPECIAL, mage.cards.g.GrinningTotem.class)); cards.add(new SetCardInfo("Hail Storm", 79, Rarity.SPECIAL, mage.cards.h.HailStorm.class)); From f7e7db4edc0b1fc85e92de83158ca6e81f23c7c4 Mon Sep 17 00:00:00 2001 From: Noah Gleason Date: Fri, 22 Jun 2018 15:54:31 -0400 Subject: [PATCH 2/2] Fully implement Giant Oyster --- Mage.Sets/src/mage/cards/g/GiantOyster.java | 111 +++++++++++++++--- ...urNextDrawStepDelayedTriggeredAbility.java | 46 ++++++++ 2 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/g/GiantOyster.java b/Mage.Sets/src/mage/cards/g/GiantOyster.java index 1e5da3bc0d..05617d00dc 100644 --- a/Mage.Sets/src/mage/cards/g/GiantOyster.java +++ b/Mage.Sets/src/mage/cards/g/GiantOyster.java @@ -1,27 +1,33 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SkipUntapOptionalAbility; +import mage.abilities.common.delayed.AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DontUntapAsLongAsSourceTappedEffect; -import mage.abilities.effects.common.TapTargetEffect; -import mage.constants.SubType; +import mage.abilities.effects.common.RemoveDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.effects.common.counter.RemoveAllCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Zone; -import mage.filter.StaticFilters; +import mage.constants.*; +import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** * @@ -46,7 +52,8 @@ public final class GiantOyster extends CardImpl { this.addAbility(new SkipUntapOptionalAbility()); // {tap}: For as long as Giant Oyster remains tapped, target tapped creature doesn't untap during its controller's untap step, and at the beginning of each of your draw steps, put a -1/-1 counter on that creature. When Giant Oyster leaves the battlefield or becomes untapped, remove all -1/-1 counters from the creature. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DontUntapAsLongAsSourceTappedEffect(), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GiantOysterDontUntapAsLongAsSourceTappedEffect(), new TapSourceCost()); + ability.addEffect(new GiantOysterCreateDelayedTriggerEffects()); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); } @@ -61,23 +68,97 @@ public final class GiantOyster extends CardImpl { } } -class GiantOysterDrawStepDelayedTriggeredAbility extends DelayedTriggeredAbility { +class GiantOysterDontUntapAsLongAsSourceTappedEffect extends DontUntapAsLongAsSourceTappedEffect { + + public GiantOysterDontUntapAsLongAsSourceTappedEffect() { + super(); + staticText = "For as long as {source} remains tapped, target tapped creature doesn't untap during its controller's untap step"; + } + + public GiantOysterDontUntapAsLongAsSourceTappedEffect(final GiantOysterDontUntapAsLongAsSourceTappedEffect effect) { + super(effect); + } + + @Override + public GiantOysterDontUntapAsLongAsSourceTappedEffect copy() { + return new GiantOysterDontUntapAsLongAsSourceTappedEffect(this); + } +} + +class GiantOysterCreateDelayedTriggerEffects extends OneShotEffect { + + public GiantOysterCreateDelayedTriggerEffects() { + super(Outcome.Detriment); + this.staticText = "at the beginning of each of your draw steps, put a -1/-1 counter on that creature. When {this} leaves the battlefield or becomes untapped, remove all -1/-1 counters from the creature."; + } + + public GiantOysterCreateDelayedTriggerEffects(final GiantOysterCreateDelayedTriggerEffects effect) { + super(effect); + } + + @Override + public GiantOysterCreateDelayedTriggerEffects copy() { + return new GiantOysterCreateDelayedTriggerEffects(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent oyster = game.getPermanent(source.getSourceId()); + Permanent tappedCreature = game.getPermanent(source.getFirstTarget()); + if (oyster != null && tappedCreature != null) { + Effect addCountersEffect = new AddCountersTargetEffect(CounterType.M1M1.createInstance(1)); + addCountersEffect.setTargetPointer(getTargetPointer().getFixedTarget(game, source)); + DelayedTriggeredAbility drawStepAbility = new AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility(addCountersEffect, Duration.Custom, false); + drawStepAbility.setSourceObject(oyster, game); + drawStepAbility.setControllerId(source.getControllerId()); + UUID drawStepAbilityUUID = game.addDelayedTriggeredAbility(drawStepAbility, source); + + DelayedTriggeredAbility leaveUntapDelayedTriggeredAbility = new GiantOysterLeaveUntapDelayedTriggeredAbility(drawStepAbilityUUID); + leaveUntapDelayedTriggeredAbility.getEffects().get(0).setTargetPointer(new FixedTarget(tappedCreature, game)); + game.addDelayedTriggeredAbility(leaveUntapDelayedTriggeredAbility, source); + return true; + } + } + return false; + } +} + +class GiantOysterLeaveUntapDelayedTriggeredAbility extends DelayedTriggeredAbility { + + public GiantOysterLeaveUntapDelayedTriggeredAbility(UUID abilityToCancel) { + super(new RemoveAllCountersTargetEffect(CounterType.M1M1), Duration.EndOfGame, true, false); + this.addEffect(new RemoveDelayedTriggeredAbilityEffect(abilityToCancel)); + } + + public GiantOysterLeaveUntapDelayedTriggeredAbility(GiantOysterLeaveUntapDelayedTriggeredAbility ability) { + super(ability); + } @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType().equals(GameEvent.EventType.DRAW_STEP_PRE); + return event.getType().equals(GameEvent.EventType.UNTAPPED) || event.getType().equals(GameEvent.EventType.ZONE_CHANGE); } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getPlayerId().equals(getControllerId()) && game.getPermanent(getSourcePermanentIfItStillExists())){ + if (event.getType().equals(GameEvent.EventType.UNTAPPED) && event.getTargetId() != null + && event.getTargetId().equals(getSourceId())) { + System.out.println("Untapped"); return true; } - return false; + return event.getType().equals(GameEvent.EventType.ZONE_CHANGE) && event.getTargetId() != null + && event.getTargetId().equals(getSourceId()) && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD; } @Override - public DelayedTriggeredAbility copy() { - return null; + public GiantOysterLeaveUntapDelayedTriggeredAbility copy() { + return new GiantOysterLeaveUntapDelayedTriggeredAbility(this); + } + + @Override + public String getRule() { + return "When {this} leaves the battlefield or becomes untapped, remove all -1/-1 counters from the creature."; } } \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility.java new file mode 100644 index 0000000000..7fe37d4279 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/delayed/AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility.java @@ -0,0 +1,46 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.common.delayed; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author jeffwadsworth + */ +public class AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility extends DelayedTriggeredAbility { + + public AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility(Effect effect) { + this(effect, Duration.Custom, true); + } + + public AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility(Effect effect, Duration duration, boolean triggerOnlyOnce) { + super(effect, duration, triggerOnlyOnce); + } + + public AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility(AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility ability) { + super(ability); + } + + @Override + public AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility copy() { + return new AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DRAW_STEP_PRE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(this.controllerId); + } +} +