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;