From 8b3064fc17dee9da191e0840a6d066bde87f3d3e Mon Sep 17 00:00:00 2001
From: Evan Kranzler <theelk801@gmail.com>
Date: Tue, 10 Nov 2020 20:31:54 -0500
Subject: [PATCH] [CMR] Implemented Malcolm, Keen-Eyed Navigator

---
 .../cards/m/MalcolmKeenEyedNavigator.java     | 106 ++++++++++++++++++
 Mage.Sets/src/mage/sets/CommanderLegends.java |   1 +
 Mage/src/main/java/mage/game/GameState.java   |  19 +++-
 .../mage/game/events/DamagedBatchEvent.java   |  45 ++++++++
 .../events/DamagedCreatureBatchEvent.java     |  11 ++
 .../events/DamagedPlaneswalkerBatchEvent.java |  11 ++
 .../game/events/DamagedPlayerBatchEvent.java  |  11 ++
 .../main/java/mage/game/events/GameEvent.java |   5 +-
 .../mage/game/permanent/PermanentImpl.java    |   8 +-
 .../main/java/mage/players/PlayerImpl.java    |   4 +-
 10 files changed, 213 insertions(+), 8 deletions(-)
 create mode 100644 Mage.Sets/src/mage/cards/m/MalcolmKeenEyedNavigator.java
 create mode 100644 Mage/src/main/java/mage/game/events/DamagedBatchEvent.java
 create mode 100644 Mage/src/main/java/mage/game/events/DamagedCreatureBatchEvent.java
 create mode 100644 Mage/src/main/java/mage/game/events/DamagedPlaneswalkerBatchEvent.java
 create mode 100644 Mage/src/main/java/mage/game/events/DamagedPlayerBatchEvent.java

diff --git a/Mage.Sets/src/mage/cards/m/MalcolmKeenEyedNavigator.java b/Mage.Sets/src/mage/cards/m/MalcolmKeenEyedNavigator.java
new file mode 100644
index 0000000000..6278dcc58d
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MalcolmKeenEyedNavigator.java
@@ -0,0 +1,106 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.abilities.TriggeredAbilityImpl;
+import mage.abilities.effects.common.CreateTokenEffect;
+import mage.abilities.keyword.FlyingAbility;
+import mage.abilities.keyword.PartnerAbility;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.Zone;
+import mage.game.Game;
+import mage.game.events.DamagedEvent;
+import mage.game.events.DamagedPlayerBatchEvent;
+import mage.game.events.GameEvent;
+import mage.game.permanent.Permanent;
+import mage.game.permanent.token.TreasureToken;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author TheElk801
+ */
+public final class MalcolmKeenEyedNavigator extends CardImpl {
+
+    public MalcolmKeenEyedNavigator(UUID ownerId, CardSetInfo setInfo) {
+        super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
+
+        this.addSuperType(SuperType.LEGENDARY);
+        this.subtype.add(SubType.SIREN);
+        this.subtype.add(SubType.PIRATE);
+        this.power = new MageInt(2);
+        this.toughness = new MageInt(2);
+
+        // Flying
+        this.addAbility(FlyingAbility.getInstance());
+
+        // Whenever one or more Pirates you control deal damage to your opponents, you create a Treasure token for each opponent dealt damage.
+        this.addAbility(new MalcolmKeenEyedNavigatorTriggeredAbility());
+
+        // Partner
+        this.addAbility(PartnerAbility.getInstance());
+    }
+
+    private MalcolmKeenEyedNavigator(final MalcolmKeenEyedNavigator card) {
+        super(card);
+    }
+
+    @Override
+    public MalcolmKeenEyedNavigator copy() {
+        return new MalcolmKeenEyedNavigator(this);
+    }
+}
+
+class MalcolmKeenEyedNavigatorTriggeredAbility extends TriggeredAbilityImpl {
+
+    MalcolmKeenEyedNavigatorTriggeredAbility() {
+        super(Zone.BATTLEFIELD, null);
+    }
+
+    private MalcolmKeenEyedNavigatorTriggeredAbility(final MalcolmKeenEyedNavigatorTriggeredAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public boolean checkEventType(GameEvent event, Game game) {
+        return event.getType() == GameEvent.EventType.DAMAGED_PLAYER_BATCH;
+    }
+
+    @Override
+    public boolean checkTrigger(GameEvent event, Game game) {
+        DamagedPlayerBatchEvent dEvent = (DamagedPlayerBatchEvent) event;
+        Set<UUID> opponents = new HashSet<>();
+        for (DamagedEvent damagedEvent : dEvent.getEvents()) {
+            Permanent permanent = game.getPermanent(damagedEvent.getSourceId());
+            if (permanent == null
+                    || !permanent.isControlledBy(getControllerId())
+                    || !permanent.hasSubtype(SubType.PIRATE, game)
+                    || !game.getOpponents(getControllerId()).contains(damagedEvent.getTargetId())) {
+                continue;
+            }
+            opponents.add(damagedEvent.getTargetId());
+        }
+        if (opponents.size() < 1) {
+            return false;
+        }
+        this.getEffects().clear();
+        this.addEffect(new CreateTokenEffect(new TreasureToken(), opponents.size()));
+        return true;
+    }
+
+    @Override
+    public MalcolmKeenEyedNavigatorTriggeredAbility copy() {
+        return new MalcolmKeenEyedNavigatorTriggeredAbility(this);
+    }
+
+    @Override
+    public String getRule() {
+        return "Whenever one or more Pirates you control deal damage to your opponents, " +
+                "you create a Treasure token for each opponent dealt damage.";
+    }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/CommanderLegends.java b/Mage.Sets/src/mage/sets/CommanderLegends.java
index f2e9fcba1b..b615a126ec 100644
--- a/Mage.Sets/src/mage/sets/CommanderLegends.java
+++ b/Mage.Sets/src/mage/sets/CommanderLegends.java
@@ -287,6 +287,7 @@ public final class CommanderLegends extends ExpansionSet {
         cards.add(new SetCardInfo("Magus of the Order", 242, Rarity.RARE, mage.cards.m.MagusOfTheOrder.class));
         cards.add(new SetCardInfo("Make a Stand", 32, Rarity.UNCOMMON, mage.cards.m.MakeAStand.class));
         cards.add(new SetCardInfo("Makeshift Munitions", 191, Rarity.COMMON, mage.cards.m.MakeshiftMunitions.class));
+        cards.add(new SetCardInfo("Malcolm, Keen-Eyed Navigator", 79, Rarity.UNCOMMON, mage.cards.m.MalcolmKeenEyedNavigator.class));
         cards.add(new SetCardInfo("Mana Confluence", 721, Rarity.MYTHIC, mage.cards.m.ManaConfluence.class));
         cards.add(new SetCardInfo("Mana Drain", 80, Rarity.MYTHIC, mage.cards.m.ManaDrain.class));
         cards.add(new SetCardInfo("Marath, Will of the Wild", 527, Rarity.MYTHIC, mage.cards.m.MarathWillOfTheWild.class));
diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java
index bb0379e2e2..7b135157ad 100644
--- a/Mage/src/main/java/mage/game/GameState.java
+++ b/Mage/src/main/java/mage/game/GameState.java
@@ -18,9 +18,7 @@ import mage.game.combat.CombatGroup;
 import mage.game.command.Command;
 import mage.game.command.CommandObject;
 import mage.game.command.Plane;
-import mage.game.events.GameEvent;
-import mage.game.events.ZoneChangeEvent;
-import mage.game.events.ZoneChangeGroupEvent;
+import mage.game.events.*;
 import mage.game.permanent.Battlefield;
 import mage.game.permanent.Permanent;
 import mage.game.permanent.PermanentToken;
@@ -732,6 +730,21 @@ public class GameState implements Serializable, Copyable<GameState> {
         return !simultaneousEvents.isEmpty();
     }
 
+    public void addSimultaneousDamage(DamagedEvent damagedEvent, Game game) {
+        boolean flag = false;
+        for (GameEvent event : simultaneousEvents) {
+            if ((event instanceof DamagedBatchEvent)
+                    && ((DamagedBatchEvent) event).getDamageClazz().isInstance(damagedEvent)) {
+                ((DamagedBatchEvent) event).addEvent(damagedEvent);
+                flag = true;
+                break;
+            }
+        }
+        if (!flag) {
+            addSimultaneousEvent(DamagedBatchEvent.makeEvent(damagedEvent), game);
+        }
+    }
+
     public void handleEvent(GameEvent event, Game game) {
         watchers.watch(event, game);
         delayed.checkTriggers(event, game);
diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java
new file mode 100644
index 0000000000..a5dad5832b
--- /dev/null
+++ b/Mage/src/main/java/mage/game/events/DamagedBatchEvent.java
@@ -0,0 +1,45 @@
+package mage.game.events;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author TheElk801
+ */
+public abstract class DamagedBatchEvent extends GameEvent {
+
+    private final Class<? extends DamagedEvent> damageClazz;
+    private final Set<DamagedEvent> events = new HashSet<>();
+
+    public DamagedBatchEvent(EventType type, Class<? extends DamagedEvent> damageClazz) {
+        super(EventType.DAMAGED_PLAYER_BATCH, null, null, null);
+        this.damageClazz = damageClazz;
+    }
+
+    public Set<DamagedEvent> getEvents() {
+        return events;
+    }
+
+    public void addEvent(DamagedEvent event) {
+        this.events.add(event);
+    }
+
+    public Class<? extends DamagedEvent> getDamageClazz() {
+        return damageClazz;
+    }
+
+    public static DamagedBatchEvent makeEvent(DamagedEvent damagedEvent) {
+        DamagedBatchEvent event = null;
+        if (damagedEvent instanceof DamagedPlayerEvent) {
+            event = new DamagedPlayerBatchEvent();
+            event.addEvent(damagedEvent);
+        } else if (damagedEvent instanceof DamagedCreatureEvent) {
+            event = new DamagedCreatureBatchEvent();
+            event.addEvent(damagedEvent);
+        } else if (damagedEvent instanceof DamagedPlaneswalkerEvent) {
+            event = new DamagedPlaneswalkerBatchEvent();
+            event.addEvent(damagedEvent);
+        }
+        return event;
+    }
+}
diff --git a/Mage/src/main/java/mage/game/events/DamagedCreatureBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedCreatureBatchEvent.java
new file mode 100644
index 0000000000..8f2c23d57a
--- /dev/null
+++ b/Mage/src/main/java/mage/game/events/DamagedCreatureBatchEvent.java
@@ -0,0 +1,11 @@
+package mage.game.events;
+
+/**
+ * @author TheElk801
+ */
+public class DamagedCreatureBatchEvent extends DamagedBatchEvent {
+
+    public DamagedCreatureBatchEvent() {
+        super(EventType.DAMAGED_CREATURE_BATCH, DamagedCreatureEvent.class);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/events/DamagedPlaneswalkerBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedPlaneswalkerBatchEvent.java
new file mode 100644
index 0000000000..17b981b43a
--- /dev/null
+++ b/Mage/src/main/java/mage/game/events/DamagedPlaneswalkerBatchEvent.java
@@ -0,0 +1,11 @@
+package mage.game.events;
+
+/**
+ * @author TheElk801
+ */
+public class DamagedPlaneswalkerBatchEvent extends DamagedBatchEvent {
+
+    public DamagedPlaneswalkerBatchEvent() {
+        super(EventType.DAMAGED_PLANESWALKER_BATCH, DamagedPlaneswalkerEvent.class);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/events/DamagedPlayerBatchEvent.java b/Mage/src/main/java/mage/game/events/DamagedPlayerBatchEvent.java
new file mode 100644
index 0000000000..c7f38a687d
--- /dev/null
+++ b/Mage/src/main/java/mage/game/events/DamagedPlayerBatchEvent.java
@@ -0,0 +1,11 @@
+package mage.game.events;
+
+/**
+ * @author TheElk801
+ */
+public class DamagedPlayerBatchEvent extends DamagedBatchEvent {
+
+    public DamagedPlayerBatchEvent() {
+        super(EventType.DAMAGED_PLAYER_BATCH, DamagedPlayerEvent.class);
+    }
+}
diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java
index 9dc99fbc7c..e6014fed0c 100644
--- a/Mage/src/main/java/mage/game/events/GameEvent.java
+++ b/Mage/src/main/java/mage/game/events/GameEvent.java
@@ -100,6 +100,7 @@ public class GameEvent implements Serializable {
          flag        true = comabat damage - other damage = false
          */
         DAMAGED_PLAYER,
+        DAMAGED_PLAYER_BATCH,
         DAMAGE_CAUSES_LIFE_LOSS,
         PLAYER_LIFE_CHANGE,
         GAIN_LIFE, GAINED_LIFE,
@@ -311,8 +312,8 @@ public class GameEvent implements Serializable {
          flag        not used for this event
          */
         OPTION_USED,
-        DAMAGE_CREATURE, DAMAGED_CREATURE,
-        DAMAGE_PLANESWALKER, DAMAGED_PLANESWALKER,
+        DAMAGE_CREATURE, DAMAGED_CREATURE, DAMAGED_CREATURE_BATCH,
+        DAMAGE_PLANESWALKER, DAMAGED_PLANESWALKER, DAMAGED_PLANESWALKER_BATCH,
         DESTROY_PERMANENT,
         /* DESTROY_PERMANENT_BY_LEGENDARY_RULE
          targetId    id of the permanent to destroy
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index 8f1818a89b..351dc8ffdd 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -980,7 +980,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
                     countersToRemove = getCounters(game).getCount(CounterType.LOYALTY);
                 }
                 removeCounters(CounterType.LOYALTY.getName(), countersToRemove, game);
-                game.fireEvent(new DamagedPlaneswalkerEvent(objectId, sourceId, controllerId, actualDamage, combat));
+                DamagedEvent damagedEvent = new DamagedPlaneswalkerEvent(objectId, sourceId, controllerId, actualDamage, combat);
+                game.fireEvent(damagedEvent);
+                game.getState().addSimultaneousDamage(damagedEvent, game);
                 return actualDamage;
             }
         }
@@ -1011,7 +1013,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
                 } else {
                     this.damage = CardUtil.addWithOverflowCheck(this.damage, actualDamage);
                 }
-                game.fireEvent(new DamagedCreatureEvent(objectId, sourceId, controllerId, actualDamage, combat));
+                DamagedEvent damagedEvent = new DamagedCreatureEvent(objectId, sourceId, controllerId, actualDamage, combat);
+                game.fireEvent(damagedEvent);
+                game.getState().addSimultaneousDamage(damagedEvent, game);
                 return actualDamage;
             }
         }
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index efed0eb952..14dedf50a1 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -2157,7 +2157,9 @@ public abstract class PlayerImpl implements Player, Serializable {
                             Player player = game.getPlayer(sourceControllerId);
                             new SquirrelToken().putOntoBattlefield(actualDamage, game, sourceId, player.getId());
                         }
-                        game.fireEvent(new DamagedPlayerEvent(playerId, sourceId, playerId, actualDamage, combatDamage));
+                        DamagedEvent damagedEvent = new DamagedPlayerEvent(playerId, sourceId, playerId, actualDamage, combatDamage);
+                        game.fireEvent(damagedEvent);
+                        game.getState().addSimultaneousDamage(damagedEvent, game);
                         return actualDamage;
                     }
                 }