From 4757356bc2ece8ae9d848e07939471db9e6c192a Mon Sep 17 00:00:00 2001
From: theelk801 <theelk801@gmail.com>
Date: Fri, 28 Apr 2023 19:25:34 -0400
Subject: [PATCH] [ONC] Implement Staff of the Storyteller

---
 .../mage/cards/s/StaffOfTheStoryteller.java   | 85 +++++++++++++++++++
 .../sets/PhyrexiaAllWillBeOneCommander.java   |  1 +
 .../main/java/mage/counters/CounterType.java  |  1 +
 .../mage/game/events/CreatedTokensEvent.java  | 44 ++++++++++
 .../main/java/mage/game/events/GameEvent.java |  2 +-
 .../mage/game/permanent/token/TokenImpl.java  |  5 ++
 6 files changed, 137 insertions(+), 1 deletion(-)
 create mode 100644 Mage.Sets/src/mage/cards/s/StaffOfTheStoryteller.java
 create mode 100644 Mage/src/main/java/mage/game/events/CreatedTokensEvent.java

diff --git a/Mage.Sets/src/mage/cards/s/StaffOfTheStoryteller.java b/Mage.Sets/src/mage/cards/s/StaffOfTheStoryteller.java
new file mode 100644
index 0000000000..16234ce47e
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/s/StaffOfTheStoryteller.java
@@ -0,0 +1,85 @@
+package mage.cards.s;
+
+import mage.abilities.Ability;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.RemoveCountersSourceCost;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.effects.common.DrawCardSourceControllerEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Zone;
+import mage.counters.CounterType;
+import mage.game.Game;
+import mage.game.events.CreatedTokensEvent;
+import mage.game.events.GameEvent;
+import mage.game.permanent.token.SpiritWhiteToken;
+
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class StaffOfTheStoryteller extends CardImpl {
+
+    public StaffOfTheStoryteller(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}");
+
+        // When Staff of the Storyteller enters the battlefield, create a 1/1 white Spirit creature token with flying.
+        this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken())));
+
+        // Whenever you create one or more creature tokens, put a story counter on Staff of the Storyteller.
+        this.addAbility(new StaffOfTheStorytellerTriggeredAbility());
+
+        // {W}, {T}, Remove a story counter from Staff of the Storyteller: Draw a card.
+        Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{W}"));
+        ability.addCost(new TapSourceCost());
+        ability.addCost(new RemoveCountersSourceCost(CounterType.STORY.createInstance()));
+        this.addAbility(ability);
+    }
+
+    private StaffOfTheStoryteller(final StaffOfTheStoryteller card) {
+        super(card);
+    }
+
+    @Override
+    public StaffOfTheStoryteller copy() {
+        return new StaffOfTheStoryteller(this);
+    }
+}
+
+class StaffOfTheStorytellerTriggeredAbility extends TriggeredAbilityImpl {
+
+    StaffOfTheStorytellerTriggeredAbility() {
+        super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORY.createInstance()));
+        setTriggerPhrase("Whenever you create one or more creature tokens, ");
+    }
+
+    private StaffOfTheStorytellerTriggeredAbility(final StaffOfTheStorytellerTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public StaffOfTheStorytellerTriggeredAbility copy() {
+        return new StaffOfTheStorytellerTriggeredAbility(this);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.CREATED_TOKENS;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        return isControlledBy(event.getPlayerId())
+                && ((CreatedTokensEvent) event)
+                .getCreatedTokens()
+                .stream()
+                .anyMatch(p -> p.isCreature(game));
+    }
+}
diff --git a/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOneCommander.java b/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOneCommander.java
index e2dbcfd4cc..4f3a7ea631 100644
--- a/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOneCommander.java
+++ b/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOneCommander.java
@@ -152,6 +152,7 @@ public final class PhyrexiaAllWillBeOneCommander extends ExpansionSet {
         cards.add(new SetCardInfo("Sol Ring", 140, Rarity.UNCOMMON, mage.cards.s.SolRing.class));
         cards.add(new SetCardInfo("Solemn Simulacrum", 141, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class));
         cards.add(new SetCardInfo("Soul-Guide Lantern", 142, Rarity.UNCOMMON, mage.cards.s.SoulGuideLantern.class));
+        cards.add(new SetCardInfo("Staff of the Storyteller", 10, Rarity.RARE, mage.cards.s.StaffOfTheStoryteller.class));
         cards.add(new SetCardInfo("Sungrass Prairie", 166, Rarity.RARE, mage.cards.s.SungrassPrairie.class));
         cards.add(new SetCardInfo("Swords to Plowshares", 89, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class));
         cards.add(new SetCardInfo("Synthesis Pod", 23, Rarity.RARE, mage.cards.s.SynthesisPod.class));
diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java
index 2a37084e8b..09a88fb663 100644
--- a/Mage/src/main/java/mage/counters/CounterType.java
+++ b/Mage/src/main/java/mage/counters/CounterType.java
@@ -180,6 +180,7 @@ public enum CounterType {
     SPORE("spore"),
     STASH("stash"),
     STORAGE("storage"),
+    STORY("story"),
     STRIFE("strife"),
     STUDY("study"),
     STUN("stun"),
diff --git a/Mage/src/main/java/mage/game/events/CreatedTokensEvent.java b/Mage/src/main/java/mage/game/events/CreatedTokensEvent.java
new file mode 100644
index 0000000000..d725956887
--- /dev/null
+++ b/Mage/src/main/java/mage/game/events/CreatedTokensEvent.java
@@ -0,0 +1,44 @@
+package mage.game.events;
+
+import com.google.common.collect.Sets;
+import mage.abilities.Ability;
+import mage.game.Controllable;
+import mage.game.Game;
+import mage.game.permanent.PermanentToken;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+/**
+ * @author TheElk801
+ */
+public class CreatedTokensEvent extends GameEvent {
+
+    private final Set<PermanentToken> createdTokens = new HashSet<>();
+
+    private CreatedTokensEvent(UUID playerId, Set<PermanentToken> createdTokens, Ability source) {
+        super(EventType.CREATED_TOKENS, playerId, source, playerId);
+        this.createdTokens.addAll(createdTokens);
+    }
+
+    public static void addEvents(Set<PermanentToken> allAddedTokens, Ability source, Game game) {
+        allAddedTokens
+                .stream()
+                .collect(Collectors.toMap(
+                        Controllable::getControllerId,
+                        Collections::singleton,
+                        Sets::union
+                ))
+                .entrySet()
+                .stream()
+                .map(entry -> new CreatedTokensEvent(entry.getKey(), entry.getValue(), source))
+                .forEach(game::addSimultaneousEvent);
+    }
+
+    public Set<PermanentToken> getCreatedTokens() {
+        return createdTokens;
+    }
+}
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index fba41fedf6..f9ee0714fe 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -462,7 +462,7 @@ public class GameEvent implements Serializable {
          flag        not used for this event
          */
         GAINED_CONTROL,
-        CREATE_TOKEN, CREATED_TOKEN,
+        CREATE_TOKEN, CREATED_TOKEN, CREATED_TOKENS,
         /* REGENERATE
          targetId    id of the creature to regenerate
          sourceId    sourceId of the effect doing the regeneration
diff --git a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
index 7fb1e35e8d..2b18d30c8f 100644
--- a/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/token/TokenImpl.java
@@ -18,6 +18,7 @@ import mage.game.Game;
 import mage.game.command.CommandObject;
 import mage.game.events.CreateTokenEvent;
 import mage.game.events.CreatedTokenEvent;
+import mage.game.events.CreatedTokensEvent;
 import mage.game.events.ZoneChangeEvent;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.PermanentToken;
@@ -249,6 +250,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
             return;
         }
 
+        Set<PermanentToken> allAddedTokens = new HashSet<>();
         for (Map.Entry<Token, Integer> entry : event.getTokens().entrySet()) {
             Token token = entry.getKey();
             int amount = entry.getValue();
@@ -306,6 +308,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
                 game.addSimultaneousEvent(zccEvent);
                 if (permanent instanceof PermanentToken && created) {
                     game.addSimultaneousEvent(new CreatedTokenEvent(source, (PermanentToken) permanent));
+                    allAddedTokens.add((PermanentToken) permanent);
                 }
 
                 // if token was created (not a spell copy) handle auras coming into the battlefield
@@ -382,6 +385,8 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
                 }
             }
         }
+        CreatedTokensEvent.addEvents(allAddedTokens, source, game);
+
         game.getState().applyEffects(game); // Needed to do it here without LKIReset i.e. do get SwordOfTheMeekTest running correctly.
     }