From 4ac6e7d86cde39e288a5a13690a8d46bdeecc9a7 Mon Sep 17 00:00:00 2001 From: Evan Kranzler <theelk801@gmail.com> Date: Fri, 27 Apr 2018 15:12:11 -0400 Subject: [PATCH 1/3] Fixing saga implementation Related to #4875, still need to rework how the SBA removes the saga with respect to the final trigger --- .../mage/abilities/common/SagaAbility.java | 105 ++++++++---------- Mage/src/main/java/mage/cards/Card.java | 2 + .../mage/game/turn/PreCombatMainStep.java | 24 +++- 3 files changed, 72 insertions(+), 59 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/common/SagaAbility.java b/Mage/src/main/java/mage/abilities/common/SagaAbility.java index 6a37d2cdad..a856cd9f83 100644 --- a/Mage/src/main/java/mage/abilities/common/SagaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SagaAbility.java @@ -32,7 +32,6 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.Card; -import mage.cards.CardImpl; import mage.constants.SagaChapter; import mage.constants.Zone; import mage.counters.CounterType; @@ -47,17 +46,17 @@ import mage.game.stack.StackObject; * * @author LevelX2 */ -public class SagaAbility extends TriggeredAbilityImpl { +public class SagaAbility extends SimpleStaticAbility { private SagaChapter maxChapter; public SagaAbility(Card card, SagaChapter maxChapter) { - super(Zone.ALL, new AddCountersSourceEffect(CounterType.LORE.createInstance()), false); - this.usesStack = false; + super(Zone.ALL, new AddCountersSourceEffect(CounterType.LORE.createInstance())); this.maxChapter = maxChapter; this.setRuleVisible(false); - ((CardImpl) card).addAbility(new SagaAddCounterAbility(maxChapter)); - + Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LORE.createInstance())); + ability.setRuleVisible(false); + card.addAbility(ability); } public SagaAbility(final SagaAbility ability) { @@ -65,28 +64,13 @@ public class SagaAbility extends TriggeredAbilityImpl { this.maxChapter = ability.maxChapter; } - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getTargetId().equals(getSourceId()); - } - - @Override - public String getRule() { - return "When {this} enters the battlefield, " + super.getRule(); - } - public Ability addChapterEffect(Card card, SagaChapter chapter, Effect effect) { return addChapterEffect(card, chapter, chapter, effect); } public Ability addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effect effect) { ChapterTriggeredAbility ability = new ChapterTriggeredAbility(effect, fromChapter, toChapter); - ((CardImpl) card).addAbility(ability); + card.addAbility(ability); if (maxChapter == null || toChapter.getNumber() > maxChapter.getNumber()) { maxChapter = toChapter; } @@ -97,6 +81,11 @@ public class SagaAbility extends TriggeredAbilityImpl { return maxChapter; } + @Override + public String getRule() { + return "<i>(As this Saga enters and after your draw step, add a lore counter. Sacrifice after " + maxChapter.toString() + ".)</i> "; + } + @Override public SagaAbility copy() { return new SagaAbility(this); @@ -179,39 +168,39 @@ class ChapterTriggeredAbility extends TriggeredAbilityImpl { } } -class SagaAddCounterAbility extends TriggeredAbilityImpl { - - SagaChapter maxChapter; - - SagaAddCounterAbility(SagaChapter maxChapter) { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.LORE.createInstance()), false); - this.usesStack = false; - this.maxChapter = maxChapter; - } - - SagaAddCounterAbility(final SagaAddCounterAbility ability) { - super(ability); - this.maxChapter = ability.maxChapter; - } - - @Override - public SagaAddCounterAbility copy() { - return new SagaAddCounterAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.PRECOMBAT_MAIN_PHASE_PRE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.controllerId); - } - - @Override - public String getRule() { - return "<i>(As this Saga enters and after your draw step, add a lore counter. Sacrifice after " + maxChapter.toString() + ".)</i> "; - } - -} +//class SagaAddCounterAbility extends TriggeredAbilityImpl { +// +// SagaChapter maxChapter; +// +// SagaAddCounterAbility(SagaChapter maxChapter) { +// super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.LORE.createInstance()), false); +// this.usesStack = false; +// this.maxChapter = maxChapter; +// } +// +// SagaAddCounterAbility(final SagaAddCounterAbility ability) { +// super(ability); +// this.maxChapter = ability.maxChapter; +// } +// +// @Override +// public SagaAddCounterAbility copy() { +// return new SagaAddCounterAbility(this); +// } +// +// @Override +// public boolean checkEventType(GameEvent event, Game game) { +// return event.getType() == EventType.PRECOMBAT_MAIN_PHASE_PRE; +// } +// +// @Override +// public boolean checkTrigger(GameEvent event, Game game) { +// return event.getPlayerId().equals(this.controllerId); +// } +// +// @Override +// public String getRule() { +// return "<i>(As this Saga enters and after your draw step, add a lore counter. Sacrifice after " + maxChapter.toString() + ".)</i> "; +// } +// +//} diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index 7cff835114..4398096c06 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -157,6 +157,8 @@ public interface Card extends MageObject { Counters getCounters(GameState state); + void addAbility(Ability ability); + boolean addCounters(Counter counter, Ability source, Game game); boolean addCounters(Counter counter, Ability source, Game game, List<UUID> appliedEffects); diff --git a/Mage/src/main/java/mage/game/turn/PreCombatMainStep.java b/Mage/src/main/java/mage/game/turn/PreCombatMainStep.java index a3a8ba12e5..7d28c14c85 100644 --- a/Mage/src/main/java/mage/game/turn/PreCombatMainStep.java +++ b/Mage/src/main/java/mage/game/turn/PreCombatMainStep.java @@ -25,11 +25,17 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.game.turn; +import java.util.UUID; import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; /** * @@ -37,6 +43,12 @@ import mage.game.events.GameEvent.EventType; */ public class PreCombatMainStep extends Step { + private static final FilterPermanent filter = new FilterPermanent("Saga"); + + static { + filter.add(new SubtypePredicate(SubType.SAGA)); + } + public PreCombatMainStep() { super(PhaseStep.PRECOMBAT_MAIN, true); this.stepEvent = EventType.PRECOMBAT_MAIN_STEP; @@ -48,6 +60,16 @@ public class PreCombatMainStep extends Step { super(step); } + @Override + public void beginStep(Game game, UUID activePlayerId) { + super.beginStep(game, activePlayerId); + for (Permanent saga : game.getBattlefield().getAllActivePermanents(filter, activePlayerId, game)) { + if (saga != null) { + saga.addCounters(CounterType.LORE.createInstance(), null, game); + } + } + } + @Override public PreCombatMainStep copy() { return new PreCombatMainStep(this); From f2835685e96c884f86140480ad6a563498621ee7 Mon Sep 17 00:00:00 2001 From: Evan Kranzler <theelk801@gmail.com> Date: Fri, 27 Apr 2018 18:22:49 -0400 Subject: [PATCH 2/3] updated how Saga abilities are added still need to fix sacrificing works (#4875) --- .../src/mage/cards/c/ChainersTorment.java | 7 ++- .../src/mage/cards/p/PhyrexianScriptures.java | 14 +++--- .../src/mage/cards/s/SongOfFreyalise.java | 12 +++-- .../src/mage/cards/t/TheEldestReborn.java | 26 ++++++---- .../src/mage/cards/t/TheMirariConjecture.java | 33 ++++++++----- Mage.Sets/src/mage/cards/t/TimeOfIce.java | 11 +++-- .../src/mage/cards/t/TriumphOfGerrard.java | 27 ++++++++--- .../mage/abilities/common/SagaAbility.java | 48 +++++++++++++++---- .../java/mage/abilities/effects/Effects.java | 4 ++ .../main/java/mage/constants/SagaChapter.java | 12 +++++ Mage/src/main/java/mage/target/Targets.java | 5 ++ 11 files changed, 147 insertions(+), 52 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/ChainersTorment.java b/Mage.Sets/src/mage/cards/c/ChainersTorment.java index 446eae2366..90bc026ed5 100644 --- a/Mage.Sets/src/mage/cards/c/ChainersTorment.java +++ b/Mage.Sets/src/mage/cards/c/ChainersTorment.java @@ -30,6 +30,7 @@ package mage.cards.c; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DamagePlayersEffect; @@ -61,8 +62,10 @@ public class ChainersTorment extends CardImpl { SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); // I, II — Chainer's Torment deals 2 damage to each opponent and you gain 2 life. - Ability ability = sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, new DamagePlayersEffect(2, TargetController.OPPONENT)); - ability.addEffect(new GainLifeEffect(2).setText("and you gain 2 life")); + Effects effects = new Effects(); + effects.add(new DamagePlayersEffect(2, TargetController.OPPONENT)); + effects.add(new GainLifeEffect(2).setText("and you gain 2 life")); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, effects); // III — Create an X/X black Nightmare Horror creature token, where X is half your life total, rounded up. It deals X damage to you. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ChainersTormentEffect()); diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianScriptures.java b/Mage.Sets/src/mage/cards/p/PhyrexianScriptures.java index 8b284cfd37..c6c9ea787a 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianScriptures.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianScriptures.java @@ -28,9 +28,8 @@ package mage.cards.p; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SagaAbility; -import mage.abilities.effects.Effect; +import mage.abilities.effects.Effects; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect; @@ -74,11 +73,12 @@ public class PhyrexianScriptures extends CardImpl { SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); // I — Put a +1/+1 counter on up to one target creature. That creature becomes an artifact in addition to its other types. - Ability ability = sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new AddCountersTargetEffect(CounterType.P1P1.createInstance())); - ability.addTarget(new TargetCreaturePermanent(0, 1)); - Effect effect = new AddCardTypeTargetEffect(Duration.WhileOnBattlefield, CardType.ARTIFACT); - effect.setText("That creature becomes an artifact in addition to its other types"); - ability.addEffect(effect); + Effects effects = new Effects(); + effects.add(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + effects.add(new AddCardTypeTargetEffect(Duration.WhileOnBattlefield, CardType.ARTIFACT) + .setText("That creature becomes an artifact in addition to its other types") + ); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, effects, new TargetCreaturePermanent(0, 1)); // II — Destroy all nonartifact creatures. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new DestroyAllEffect(filter)); diff --git a/Mage.Sets/src/mage/cards/s/SongOfFreyalise.java b/Mage.Sets/src/mage/cards/s/SongOfFreyalise.java index 2935f0bd6f..92965b5001 100644 --- a/Mage.Sets/src/mage/cards/s/SongOfFreyalise.java +++ b/Mage.Sets/src/mage/cards/s/SongOfFreyalise.java @@ -28,8 +28,8 @@ package mage.cards.s; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.abilities.keyword.IndestructibleAbility; @@ -66,13 +66,15 @@ public class SongOfFreyalise extends CardImpl { ); // III — Put a +1/+1 counter on each creature you control. Those creatures gain vigilance, trample, and indestructible until end of turn. - Ability ability = sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new AddCountersAllEffect(CounterType.P1P1.createInstance(), FILTER_CONTROLLED_CREATURES)); - ability.addEffect(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, FILTER_CONTROLLED_CREATURES) + Effects effects = new Effects(); + effects.add(new AddCountersAllEffect(CounterType.P1P1.createInstance(), FILTER_CONTROLLED_CREATURES)); + effects.add(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, FILTER_CONTROLLED_CREATURES) .setText("Those creatures gain vigilance")); - ability.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, FILTER_CONTROLLED_CREATURES) + effects.add(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, FILTER_CONTROLLED_CREATURES) .setText(", trample")); - ability.addEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, FILTER_CONTROLLED_CREATURES) + effects.add(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, FILTER_CONTROLLED_CREATURES) .setText("and indestructible until end of turn")); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, SagaChapter.CHAPTER_III, effects); this.addAbility(sagaAbility); } diff --git a/Mage.Sets/src/mage/cards/t/TheEldestReborn.java b/Mage.Sets/src/mage/cards/t/TheEldestReborn.java index c019eb53b3..269ff46c41 100644 --- a/Mage.Sets/src/mage/cards/t/TheEldestReborn.java +++ b/Mage.Sets/src/mage/cards/t/TheEldestReborn.java @@ -28,7 +28,6 @@ package mage.cards.t; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SagaAbility; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.common.SacrificeOpponentsEffect; @@ -51,6 +50,15 @@ import mage.target.common.TargetCardInGraveyard; */ public class TheEldestReborn extends CardImpl { + private static final FilterCard filter = new FilterCard("creature or planeswalker card from a graveyard"); + + static { + filter.add(Predicates.or( + new CardTypePredicate(CardType.CREATURE), + new CardTypePredicate(CardType.PLANESWALKER) + )); + } + public TheEldestReborn(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{B}"); @@ -60,19 +68,21 @@ public class TheEldestReborn extends CardImpl { SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); // I — Each opponent sacrifices a creature or planeswalker. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, - new SacrificeOpponentsEffect(StaticFilters.FILTER_PERMANENT_CREATURE_OR_PLANESWALKER_A)); + new SacrificeOpponentsEffect(StaticFilters.FILTER_PERMANENT_CREATURE_OR_PLANESWALKER_A) + ); // II — Each opponent discards a card. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, - new DiscardEachPlayerEffect(TargetController.OPPONENT)); + new DiscardEachPlayerEffect(TargetController.OPPONENT) + ); // III — Put target creature or planeswalker card from a graveyard onto the battlefield under your control. - Ability ability = sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, SagaChapter.CHAPTER_III, new ReturnFromGraveyardToBattlefieldTargetEffect() - .setText("Put target creature or planeswalker card from a graveyard onto the battlefield under your control")); - FilterCard filter = new FilterCard("creature or planeswalker card from a graveyard"); - filter.add(Predicates.or(new CardTypePredicate(CardType.CREATURE), new CardTypePredicate(CardType.PLANESWALKER))); - ability.addTarget(new TargetCardInGraveyard(filter)); + .setText("Put target creature or planeswalker card from a graveyard onto the battlefield under your control"), + new TargetCardInGraveyard(filter) + ); this.addAbility(sagaAbility); } diff --git a/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java b/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java index c1fbcf8a47..7cbe6a3c45 100644 --- a/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java +++ b/Mage.Sets/src/mage/cards/t/TheMirariConjecture.java @@ -28,7 +28,6 @@ package mage.cards.t; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SagaAbility; import mage.abilities.effects.common.CopyTargetSpellEffect; @@ -55,6 +54,14 @@ import mage.target.targetpointer.FixedTarget; */ public class TheMirariConjecture extends CardImpl { + private static final FilterCard filterInstantCard = new FilterCard("instant card from your graveyard"); + private static final FilterCard filterSorceryCard = new FilterCard("sorcery card from your graveyard"); + + static { + filterInstantCard.add(new CardTypePredicate(CardType.INSTANT)); + filterSorceryCard.add(new CardTypePredicate(CardType.SORCERY)); + } + public TheMirariConjecture(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}"); @@ -62,16 +69,20 @@ public class TheMirariConjecture extends CardImpl { // <i>(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)</i> SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); - // I — Return target instant card from your graveyard to your hand. - FilterCard filterInstantCard = new FilterCard("instant card from your graveyard"); - filterInstantCard.add(new CardTypePredicate(CardType.INSTANT)); - Ability ability = sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new ReturnFromGraveyardToHandTargetEffect()); - ability.addTarget(new TargetCardInYourGraveyard(filterInstantCard)); - // II — Return target sorcery card from your graveyard to your hand. - FilterCard filterSorceryCard = new FilterCard("sorcery card from your graveyard"); - filterSorceryCard.add(new CardTypePredicate(CardType.SORCERY)); - ability = sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new ReturnFromGraveyardToHandTargetEffect()); - ability.addTarget(new TargetCardInYourGraveyard(filterSorceryCard)); + // I — Return target instant card from your graveyard to your hand. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, + new ReturnFromGraveyardToHandTargetEffect(), + new TargetCardInYourGraveyard(filterInstantCard) + ); + + // II — Return target sorcery card from your graveyard to your hand. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_II, + new ReturnFromGraveyardToHandTargetEffect(), + new TargetCardInYourGraveyard(filterSorceryCard) + ); + // III — Until end of turn, whenever you cast an instant or sorcery spell, copy it. You may choose new targets for the copy. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new CreateDelayedTriggeredAbilityEffect(new TheMirariConjectureDelayedTriggeredAbility())); this.addAbility(sagaAbility); diff --git a/Mage.Sets/src/mage/cards/t/TimeOfIce.java b/Mage.Sets/src/mage/cards/t/TimeOfIce.java index 6b0fffbe05..86eaa24dd6 100644 --- a/Mage.Sets/src/mage/cards/t/TimeOfIce.java +++ b/Mage.Sets/src/mage/cards/t/TimeOfIce.java @@ -32,6 +32,7 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SagaAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.Effects; import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.constants.SubType; @@ -75,9 +76,13 @@ public class TimeOfIce extends CardImpl { SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); // I, II — Tap target creature an opponent controls. It doesn't untap during its controller's untap step for as long as you control Time of Ice. - Ability ability = sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, new TapTargetEffect()); - ability.addEffect(new TimeOfIceEffect()); - ability.addTarget(new TargetCreaturePermanent(FILTER_OPPONENTS_PERMANENT_CREATURE)); + Effects effects = new Effects(); + effects.add(new TapTargetEffect()); + effects.add(new TimeOfIceEffect()); + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, effects, + new TargetCreaturePermanent(FILTER_OPPONENTS_PERMANENT_CREATURE) + ); // III — Return all tapped creatures to their owners' hands. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ReturnToHandFromBattlefieldAllEffect(filter)); diff --git a/Mage.Sets/src/mage/cards/t/TriumphOfGerrard.java b/Mage.Sets/src/mage/cards/t/TriumphOfGerrard.java index 432f4806f4..f59ef3a060 100644 --- a/Mage.Sets/src/mage/cards/t/TriumphOfGerrard.java +++ b/Mage.Sets/src/mage/cards/t/TriumphOfGerrard.java @@ -34,6 +34,7 @@ import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.FirstStrikeAbility; @@ -48,6 +49,7 @@ import mage.constants.SubType; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.target.Targets; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -64,14 +66,25 @@ public class TriumphOfGerrard extends CardImpl { // <i>(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)</i> SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_III); // I, II — Put a +1/+1 counter on target creature you control with the greatest power. - Ability ability = sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, new AddCountersTargetEffect(CounterType.P1P1.createInstance())); - ability.addTarget(new TriumphOfGerrardTargetCreature()); + sagaAbility.addChapterEffect( + this, + SagaChapter.CHAPTER_I, + SagaChapter.CHAPTER_II, + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + new TriumphOfGerrardTargetCreature() + ); // III — Target creature you control with the greatest power gains flying, first strike, and lifelink until end of turn. - ability = sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, - new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn).setText("Target creature you control with the greatest power gains flying")); - ability.addEffect(new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn).setText(", first strike")); - ability.addEffect(new GainAbilityTargetEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn).setText(", and lifelink until end of turn")); - ability.addTarget(new TriumphOfGerrardTargetCreature()); + Effects effects = new Effects(); + effects.add(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn) + .setText("Target creature you control with the greatest power gains flying")); + effects.add(new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn) + .setText(", first strike")); + effects.add(new GainAbilityTargetEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn) + .setText(", and lifelink until end of turn")); + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, SagaChapter.CHAPTER_III, + effects, new Targets(new TriumphOfGerrardTargetCreature()) + ); this.addAbility(sagaAbility); } diff --git a/Mage/src/main/java/mage/abilities/common/SagaAbility.java b/Mage/src/main/java/mage/abilities/common/SagaAbility.java index a856cd9f83..3c71708bc1 100644 --- a/Mage/src/main/java/mage/abilities/common/SagaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SagaAbility.java @@ -30,6 +30,7 @@ package mage.abilities.common; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.abilities.effects.Effects; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.Card; import mage.constants.SagaChapter; @@ -41,6 +42,8 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.game.stack.StackObject; +import mage.target.Target; +import mage.target.Targets; /** * @@ -64,17 +67,44 @@ public class SagaAbility extends SimpleStaticAbility { this.maxChapter = ability.maxChapter; } - public Ability addChapterEffect(Card card, SagaChapter chapter, Effect effect) { - return addChapterEffect(card, chapter, chapter, effect); + public void addChapterEffect(Card card, SagaChapter chapter, Effect effect) { + addChapterEffect(card, chapter, chapter, effect); } - public Ability addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effect effect) { - ChapterTriggeredAbility ability = new ChapterTriggeredAbility(effect, fromChapter, toChapter); - card.addAbility(ability); + public void addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effect effect) { + addChapterEffect(card, fromChapter, toChapter, new Effects(effect), (Target) null); + } + + public void addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effects effects) { + addChapterEffect(card, fromChapter, toChapter, effects, (Target) null); + } + + public void addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effect effect, Target target) { + addChapterEffect(card, fromChapter, toChapter, new Effects(effect), new Targets(target)); + } + + public void addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effects effects, Target target) { + addChapterEffect(card, fromChapter, toChapter, effects, new Targets(target)); + } + + public void addChapterEffect(Card card, SagaChapter fromChapter, SagaChapter toChapter, Effects effects, Targets targets) { + ChapterTriggeredAbility ability; + for (int i = fromChapter.getNumber(); i <= toChapter.getNumber(); i++) { + ability = new ChapterTriggeredAbility(null, SagaChapter.getChapter(i), toChapter); + for (Effect effect : effects) { + ability.addEffect(effect); + } + for (Target target : targets) { + ability.addTarget(target); + } + if (i > fromChapter.getNumber()) { + ability.setRuleVisible(false); + } + card.addAbility(ability); + } if (maxChapter == null || toChapter.getNumber() > maxChapter.getNumber()) { maxChapter = toChapter; } - return ability; } public SagaChapter getMaxChapter() { @@ -106,7 +136,6 @@ class ChapterTriggeredAbility extends TriggeredAbilityImpl { public ChapterTriggeredAbility(Effect effect, SagaChapter chapterFrom, SagaChapter chapterTo) { super(Zone.BATTLEFIELD, effect, false); this.chapterFrom = chapterFrom; - this.chapterTo = chapterTo; } public ChapterTriggeredAbility(final ChapterTriggeredAbility ability) { @@ -123,11 +152,12 @@ class ChapterTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getTargetId().equals(getSourceId()) && event.getData().equals(CounterType.LORE.getName())) { + int amountAdded = event.getAmount(); Permanent sourceSaga = game.getPermanentOrLKIBattlefield(getSourceId()); if (sourceSaga != null) { int loreCounters = sourceSaga.getCounters(game).getCount(CounterType.LORE); - return chapterFrom.getNumber() <= loreCounters - && chapterTo.getNumber() >= loreCounters; + return loreCounters - amountAdded < chapterFrom.getNumber() + && chapterFrom.getNumber() <= loreCounters; } } return false; diff --git a/Mage/src/main/java/mage/abilities/effects/Effects.java b/Mage/src/main/java/mage/abilities/effects/Effects.java index 5598970061..5ac81c5485 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effects.java +++ b/Mage/src/main/java/mage/abilities/effects/Effects.java @@ -44,6 +44,10 @@ public class Effects extends ArrayList<Effect> { public Effects() { } + public Effects(Effect effect) { + this.add(effect); + } + public Effects(final Effects effects) { for (Effect effect : effects) { this.add(effect.copy()); diff --git a/Mage/src/main/java/mage/constants/SagaChapter.java b/Mage/src/main/java/mage/constants/SagaChapter.java index 8eb2487abb..8afac6a674 100644 --- a/Mage/src/main/java/mage/constants/SagaChapter.java +++ b/Mage/src/main/java/mage/constants/SagaChapter.java @@ -53,4 +53,16 @@ public enum SagaChapter { return number; } + public static SagaChapter getChapter(int number) { + switch (number) { + case 1: + return CHAPTER_I; + case 2: + return CHAPTER_II; + case 3: + return CHAPTER_III; + default: + return null; + } + } } diff --git a/Mage/src/main/java/mage/target/Targets.java b/Mage/src/main/java/mage/target/Targets.java index 43ed15e295..502297f3c9 100644 --- a/Mage/src/main/java/mage/target/Targets.java +++ b/Mage/src/main/java/mage/target/Targets.java @@ -46,6 +46,10 @@ public class Targets extends ArrayList<Target> { public Targets() { } + public Targets(Target target) { + this.add(target); + } + public Targets(final Targets targets) { for (Target target : targets) { this.add(target.copy()); @@ -151,6 +155,7 @@ public class Targets extends ArrayList<Target> { } return null; } + public Targets copy() { return new Targets(this); } From ac97e4c600ee21cfa4ba2303c8500dbe7d01a28d Mon Sep 17 00:00:00 2001 From: Evan Kranzler <theelk801@gmail.com> Date: Mon, 14 May 2018 17:36:41 -0400 Subject: [PATCH 3/3] fixed Sagas being sacrificed before their trigger has left the stack --- .../java/mage/abilities/common/SagaAbility.java | 17 ++++++++++++----- Mage/src/main/java/mage/game/GameImpl.java | 17 +++++++++++++---- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/common/SagaAbility.java b/Mage/src/main/java/mage/abilities/common/SagaAbility.java index 3c71708bc1..3e377d3f43 100644 --- a/Mage/src/main/java/mage/abilities/common/SagaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SagaAbility.java @@ -28,6 +28,7 @@ package mage.abilities.common; import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; @@ -127,6 +128,10 @@ public class SagaAbility extends SimpleStaticAbility { } return false; } + + public static boolean isChapterAbility(TriggeredAbility ability) { + return ability instanceof ChapterTriggeredAbility; + } } class ChapterTriggeredAbility extends TriggeredAbilityImpl { @@ -134,8 +139,9 @@ class ChapterTriggeredAbility extends TriggeredAbilityImpl { SagaChapter chapterFrom, chapterTo; public ChapterTriggeredAbility(Effect effect, SagaChapter chapterFrom, SagaChapter chapterTo) { - super(Zone.BATTLEFIELD, effect, false); + super(Zone.ALL, effect, false); this.chapterFrom = chapterFrom; + this.chapterTo = chapterTo; } public ChapterTriggeredAbility(final ChapterTriggeredAbility ability) { @@ -153,12 +159,13 @@ class ChapterTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getTargetId().equals(getSourceId()) && event.getData().equals(CounterType.LORE.getName())) { int amountAdded = event.getAmount(); + int loreCounters = amountAdded; Permanent sourceSaga = game.getPermanentOrLKIBattlefield(getSourceId()); - if (sourceSaga != null) { - int loreCounters = sourceSaga.getCounters(game).getCount(CounterType.LORE); - return loreCounters - amountAdded < chapterFrom.getNumber() - && chapterFrom.getNumber() <= loreCounters; + if (sourceSaga != null) { // If it's entering the battlefield, it won't be found so we assume it had no counters + loreCounters = sourceSaga.getCounters(game).getCount(CounterType.LORE); } + return loreCounters - amountAdded < chapterFrom.getNumber() + && chapterFrom.getNumber() <= loreCounters; } return false; } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index bd5e17cc79..e8b31da5aa 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -2031,21 +2031,30 @@ public abstract class GameImpl implements Game, Serializable { } } } - // Remove Saga enchantment if last chapter is reached and chapter ability has left the stack + // 704.5s If the number of lore counters on a Saga permanent is greater than or equal to its final chapter number + // and it isn’t the source of a chapter ability that has triggered but not yet left the stack, that Saga’s controller sacrifices it. if (perm.hasSubtype(SubType.SAGA, this)) { for (Ability sagaAbility : perm.getAbilities()) { if (sagaAbility instanceof SagaAbility) { int maxChapter = ((SagaAbility) sagaAbility).getMaxChapter().getNumber(); if (maxChapter <= perm.getCounters(this).getCount(CounterType.LORE)) { - boolean noChapterAbilityOnStack = true; + boolean noChapterAbilityTriggeredOrOnStack = true; // Check chapter abilities on stack for (StackObject stackObject : getStack()) { if (stackObject.getSourceId().equals(perm.getId()) && SagaAbility.isChapterAbility(stackObject)) { - noChapterAbilityOnStack = false; + noChapterAbilityTriggeredOrOnStack = false; break; } } - if (noChapterAbilityOnStack) { + if (noChapterAbilityTriggeredOrOnStack) { + for (TriggeredAbility trigger : state.getTriggered(perm.getControllerId())) { + if (SagaAbility.isChapterAbility(trigger) && trigger.getSourceId().equals(perm.getId())) { + noChapterAbilityTriggeredOrOnStack = false; + break; + } + } + } + if (noChapterAbilityTriggeredOrOnStack) { // After the last chapter ability has left the stack, you'll sacrifice the Saga perm.sacrifice(perm.getId(), this); somethingHappened = true;