From 42ed14df525f18578906f85d52c417381b80dd8b Mon Sep 17 00:00:00 2001
From: Oleg Agafonov <jaydi85@gmail.com>
Date: Thu, 23 May 2019 12:40:45 +0400
Subject: [PATCH] * Commander: added support of lands as commander (#5795);

---
 Mage.Sets/src/mage/cards/m/Musician.java      | 66 ++---------------
 Mage.Sets/src/mage/cards/m/MythUnbound.java   | 31 ++++----
 Mage.Sets/src/mage/cards/o/OpalPalace.java    | 28 ++++----
 .../cards/continuous/CommandersCastTest.java  | 63 ++++++++++++++++
 .../test/commander/duel/OpalPalaceTest.java   | 11 ++-
 .../common/CastCommanderAbility.java          | 28 ++------
 .../common/PlayLandAsCommanderAbility.java    | 28 ++++++++
 .../costs/common/CommanderAdditionalCost.java | 34 +++++++++
 .../common/DynamicValueGenericManaCost.java   | 46 ++++++++++++
 .../common/CommanderPlaysCount.java           | 52 ++++++++++++++
 .../cost/CommanderCostModification.java       | 54 --------------
 .../keyword/CommanderStormAbility.java        |  8 +--
 .../java/mage/game/GameCommanderImpl.java     | 11 +--
 .../java/mage/game/GameTinyLeadersImpl.java   |  9 +--
 .../java/mage/game/command/Commander.java     | 24 +++++--
 .../main/java/mage/players/PlayerImpl.java    | 71 +++++++++++++++++--
 .../watchers/common/CommanderInfoWatcher.java | 16 +++--
 .../common/CommanderPlaysCountWatcher.java    | 62 ++++++++++++++++
 18 files changed, 441 insertions(+), 201 deletions(-)
 create mode 100644 Mage/src/main/java/mage/abilities/common/PlayLandAsCommanderAbility.java
 create mode 100644 Mage/src/main/java/mage/abilities/costs/common/CommanderAdditionalCost.java
 create mode 100644 Mage/src/main/java/mage/abilities/costs/common/DynamicValueGenericManaCost.java
 create mode 100644 Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java
 delete mode 100644 Mage/src/main/java/mage/abilities/effects/common/cost/CommanderCostModification.java
 create mode 100644 Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java

diff --git a/Mage.Sets/src/mage/cards/m/Musician.java b/Mage.Sets/src/mage/cards/m/Musician.java
index 4b99c7b004..886e9c8897 100644
--- a/Mage.Sets/src/mage/cards/m/Musician.java
+++ b/Mage.Sets/src/mage/cards/m/Musician.java
@@ -1,17 +1,12 @@
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.MageInt;
 import mage.abilities.Ability;
 import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
 import mage.abilities.common.SimpleActivatedAbility;
-import mage.abilities.costs.Cost;
-import mage.abilities.costs.CostImpl;
+import mage.abilities.costs.common.DynamicValueGenericManaCost;
 import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.costs.mana.GenericManaCost;
-import mage.constants.SubType;
 import mage.abilities.costs.mana.ManaCostsImpl;
-import mage.abilities.dynamicvalue.DynamicValue;
 import mage.abilities.dynamicvalue.common.CountersSourceCount;
 import mage.abilities.effects.Effect;
 import mage.abilities.effects.common.DestroySourceEffect;
@@ -21,17 +16,13 @@ import mage.abilities.effects.common.counter.AddCountersTargetEffect;
 import mage.abilities.keyword.CumulativeUpkeepAbility;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.TargetController;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.counters.CounterType;
-import mage.game.Game;
-import mage.players.Player;
 import mage.target.common.TargetCreaturePermanent;
 
+import java.util.UUID;
+
 /**
- *
  * @author jeffwadsworth
  */
 public final class Musician extends CardImpl {
@@ -51,8 +42,8 @@ public final class Musician extends CardImpl {
         Effect effect = new DoUnlessControllerPaysEffect(
                 new DestroySourceEffect(),
                 new DynamicValueGenericManaCost(
-                        new CountersSourceCount(
-                                CounterType.MUSIC)));
+                        new CountersSourceCount(CounterType.MUSIC),
+                        "{1} for each music counter on {this}"));
         effect.setText("destroy this creature unless you pay {1} for each music counter on it");
         Ability ability = new BeginningOfUpkeepTriggeredAbility(
                 Zone.BATTLEFIELD,
@@ -87,48 +78,3 @@ public final class Musician extends CardImpl {
     }
 }
 
-class DynamicValueGenericManaCost extends CostImpl {
-
-    DynamicValue amount;
-
-    public DynamicValueGenericManaCost(DynamicValue amount) {
-        this.amount = amount;
-        setText();
-    }
-
-    public DynamicValueGenericManaCost(DynamicValueGenericManaCost cost) {
-        super(cost);
-        this.amount = cost.amount;
-    }
-
-    @Override
-    public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
-        Player controller = game.getPlayer(controllerId);
-        if (controller == null) {
-            return false;
-        }
-        int convertedCost = amount.calculate(game, ability, null);
-        Cost cost = new GenericManaCost(convertedCost);
-        return cost.canPay(ability, sourceId, controllerId, game);
-    }
-
-    @Override
-    public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
-        Player controller = game.getPlayer(controllerId);
-        int convertedCost = amount.calculate(game, ability, null);
-        Cost cost = new GenericManaCost(convertedCost);
-        if (controller != null) {
-            paid = cost.pay(ability, game, sourceId, controllerId, noMana);
-        }
-        return paid;
-    }
-
-    @Override
-    public DynamicValueGenericManaCost copy() {
-        return new DynamicValueGenericManaCost(this);
-    }
-
-    private void setText() {
-        text = ("{1} for each music counter on {this}");
-    }
-}
diff --git a/Mage.Sets/src/mage/cards/m/MythUnbound.java b/Mage.Sets/src/mage/cards/m/MythUnbound.java
index 86e13abb32..6e23fdbbc8 100644
--- a/Mage.Sets/src/mage/cards/m/MythUnbound.java
+++ b/Mage.Sets/src/mage/cards/m/MythUnbound.java
@@ -1,7 +1,7 @@
 package mage.cards.m;
 
-import java.util.UUID;
 import mage.abilities.Ability;
+import mage.abilities.PlayLandAbility;
 import mage.abilities.SpellAbility;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.common.ZoneChangeAllTriggeredAbility;
@@ -9,22 +9,18 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect;
 import mage.abilities.effects.common.cost.CostModificationEffectImpl;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.TargetController;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.filter.FilterPermanent;
 import mage.filter.predicate.other.OwnerPredicate;
 import mage.filter.predicate.permanent.CommanderPredicate;
 import mage.game.Game;
-import mage.game.stack.Spell;
 import mage.players.Player;
 import mage.util.CardUtil;
+import mage.watchers.common.CommanderPlaysCountWatcher;
+
+import java.util.UUID;
 
 /**
- *
  * @author TheElk801
  */
 public final class MythUnbound extends CardImpl {
@@ -50,7 +46,7 @@ public final class MythUnbound extends CardImpl {
                 Zone.BATTLEFIELD, Zone.ALL, Zone.COMMAND,
                 new DrawCardSourceControllerEffect(1), filter,
                 "Whenever your commander is put into "
-                + "the command zone from anywhere, ", false
+                        + "the command zone from anywhere, ", false
         ));
     }
 
@@ -80,9 +76,10 @@ class MythUnboundCostReductionEffect extends CostModificationEffectImpl {
     public boolean apply(Game game, Ability source, Ability abilityToModify) {
         Ability spellAbility = abilityToModify;
         if (spellAbility != null) {
-            Integer amount = (Integer) game.getState().getValue(abilityToModify.getSourceId() + "_castCount");
-            if (amount != null && amount > 0) {
-                CardUtil.reduceCost(spellAbility, amount);
+            CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
+            int castCount = watcher.getPlaysCount(abilityToModify.getSourceId());
+            if (castCount > 0) {
+                CardUtil.reduceCost(spellAbility, castCount);
                 return true;
             }
         }
@@ -95,12 +92,10 @@ class MythUnboundCostReductionEffect extends CostModificationEffectImpl {
         if (player == null) {
             return false;
         }
-        if (abilityToModify instanceof SpellAbility) {
+
+        if (abilityToModify instanceof SpellAbility || abilityToModify instanceof PlayLandAbility) {
             if (abilityToModify.isControlledBy(source.getControllerId())) {
-                Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
-                if (spell != null) {
-                    return player.getCommandersIds().contains(spell.getSourceId());
-                }
+                return player.getCommandersIds().contains(abilityToModify.getSourceId());
             }
         }
         return false;
diff --git a/Mage.Sets/src/mage/cards/o/OpalPalace.java b/Mage.Sets/src/mage/cards/o/OpalPalace.java
index e010390c4f..92fa165d6a 100644
--- a/Mage.Sets/src/mage/cards/o/OpalPalace.java
+++ b/Mage.Sets/src/mage/cards/o/OpalPalace.java
@@ -1,9 +1,5 @@
-
 package mage.cards.o;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
 import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.costs.common.TapSourceCost;
@@ -14,11 +10,7 @@ import mage.abilities.mana.CommanderColorIdentityManaAbility;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
-import mage.constants.CardType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.constants.WatcherScope;
-import mage.constants.Zone;
+import mage.constants.*;
 import mage.counters.CounterType;
 import mage.game.Game;
 import mage.game.events.EntersTheBattlefieldEvent;
@@ -28,15 +20,19 @@ import mage.game.permanent.Permanent;
 import mage.game.stack.Spell;
 import mage.players.Player;
 import mage.watchers.Watcher;
+import mage.watchers.common.CommanderPlaysCountWatcher;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
 
 /**
- *
  * @author LevelX2
  */
 public final class OpalPalace extends CardImpl {
 
     public OpalPalace(UUID ownerId, CardSetInfo setInfo) {
-        super(ownerId,setInfo,new CardType[]{CardType.LAND},"");
+        super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
 
         // {T}: Add {C}.
         this.addAbility(new ColorlessManaAbility());
@@ -77,7 +73,7 @@ class OpalPalaceWatcher extends Watcher {
         this.originalId = watcher.originalId;
     }
 
-    public boolean manaUsedToCastCommander(UUID id){
+    public boolean manaUsedToCastCommander(UUID id) {
         return commanderId.contains(id);
     }
 
@@ -135,16 +131,16 @@ class OpalPalaceEntersBattlefieldEffect extends ReplacementEffectImpl {
     @Override
     public boolean applies(GameEvent event, Ability source, Game game) {
         OpalPalaceWatcher watcher = game.getState().getWatcher(OpalPalaceWatcher.class, source.getSourceId());
-        return watcher != null
-                && watcher.manaUsedToCastCommander(event.getTargetId());
+        return watcher != null && watcher.manaUsedToCastCommander(event.getTargetId());
     }
 
     @Override
     public boolean replaceEvent(GameEvent event, Ability source, Game game) {
         Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
         if (permanent != null) {
-            Integer castCount = (Integer) game.getState().getValue(permanent.getId() + "_castCount");
-            if (castCount != null && castCount > 0) {
+            CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
+            int castCount = watcher.getPlaysCount(permanent.getId());
+            if (castCount > 0) {
                 permanent.addCounters(CounterType.P1P1.createInstance(castCount), source, game);
             }
         }
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java
index 02d57293aa..fa09404e36 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java
@@ -27,6 +27,7 @@ public class CommandersCastTest extends CardTestCommander4Players {
 
         assertCommandZoneCount(playerA, "Balduvian Bears", 0);
         assertPermanentCount(playerA, "Balduvian Bears", 1);
+        assertTappedCount("Forest", true, 2);
     }
 
     @Test
@@ -62,5 +63,67 @@ public class CommandersCastTest extends CardTestCommander4Players {
         assertCommandZoneCount(playerA, "Balduvian Bears", 0);
         assertPermanentCount(playerA, "Balduvian Bears", 1);
         assertGraveyardCount(playerB, "Lightning Bolt", 1);
+        assertTappedCount("Forest", true, 2 + 4);
+    }
+
+    @Test
+    public void test_PlayAsLandOneTime() {
+        addCard(Zone.COMMAND, playerA, "Academy Ruins", 1);
+
+        showAvaileableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins");
+        //castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins");
+
+        setStopAt(1, PhaseStep.END_TURN);
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertCommandZoneCount(playerA, "Academy Ruins", 0);
+        assertPermanentCount(playerA, "Academy Ruins", 1);
+    }
+
+    @Test
+    public void test_PlayAsLandTwoTimes() {
+        // Player order: A -> D -> C -> B
+        addCard(Zone.COMMAND, playerA, "Academy Ruins", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); // 0 + 2
+        //
+        addCard(Zone.HAND, playerA, "Pillage", 1);
+        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+
+        // cast 1
+        playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins");
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPermanentCount("after play 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins", 1);
+
+        // destroy commander land
+        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pillage", "Academy Ruins");
+        setChoice(playerA, "Yes"); // put to command zone again
+        waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+        checkPermanentCount("after destroy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins", 0);
+
+        // remove unnecessary mana, only 2 forest need (workaround to remove random mana payments)
+        activateManaAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
+        activateManaAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
+        activateManaAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
+
+        // cast 2
+        playLand(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Academy Ruins");
+        waitStackResolved(5, PhaseStep.POSTCOMBAT_MAIN);
+        checkPermanentCount("after cast 2", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Academy Ruins", 1);
+
+        showBattlefield("end battlefield", 5, PhaseStep.END_TURN, playerA);
+
+        setStopAt(5, PhaseStep.END_TURN);
+        setStrictChooseMode(true);
+        execute();
+        assertAllCommandsUsed();
+
+        assertCommandZoneCount(playerA, "Academy Ruins", 0);
+        assertPermanentCount(playerA, "Academy Ruins", 1);
+        assertGraveyardCount(playerA, "Pillage", 1);
+        assertTappedCount("Forest", true, 2);
+        assertTappedCount("Mountain", true, 3);
     }
 }
diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java
index 1c4bd3ce18..8061ad716d 100644
--- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java
@@ -1,4 +1,3 @@
-
 package org.mage.test.commander.duel;
 
 import mage.constants.PhaseStep;
@@ -8,7 +7,6 @@ import org.junit.Test;
 import org.mage.test.serverside.base.CardTestCommanderDuelBase;
 
 /**
- *
  * @author LevelX2
  */
 public class OpalPalaceTest extends CardTestCommanderDuelBase {
@@ -29,10 +27,19 @@ public class OpalPalaceTest extends CardTestCommanderDuelBase {
         // equal to the number of times it's been cast from the command zone this game.
         addCard(Zone.BATTLEFIELD, playerA, "Opal Palace", 1);
 
+        showHand("hand", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        showCommand("command", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+        showAvaileableAbilities("abi", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
+
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}");
+        activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}");
+        setChoice(playerA, "Opal Palace"); // activate mana replace effect first (+3 counters)
         castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ob Nixilis of the Black Oath"); // {3}{B}{B}
 
+        setStrictChooseMode(true);
         setStopAt(1, PhaseStep.BEGIN_COMBAT);
         execute();
+        assertAllCommandsUsed();
 
         assertLife(playerA, 40);
         assertLife(playerB, 40);
diff --git a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java
index c31137dfe8..40aafbd62b 100644
--- a/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java
+++ b/Mage/src/main/java/mage/abilities/common/CastCommanderAbility.java
@@ -1,15 +1,13 @@
 package mage.abilities.common;
 
 import mage.abilities.SpellAbility;
-import mage.abilities.costs.CostsImpl;
+import mage.abilities.costs.common.CommanderAdditionalCost;
 import mage.cards.Card;
 import mage.constants.SpellAbilityType;
-import mage.constants.TimingRule;
 import mage.constants.Zone;
-import mage.game.Game;
 
 /**
- * @author Plopman
+ * @author Plopman, JayDi85
  */
 public class CastCommanderAbility extends SpellAbility {
 
@@ -20,9 +18,11 @@ public class CastCommanderAbility extends SpellAbility {
             this.getEffects().addAll(card.getSpellAbility().getEffects().copy());
             this.getTargets().addAll(card.getSpellAbility().getTargets().copy());
             this.timing = card.getSpellAbility().getTiming();
+
+            // extra cost
+            this.addCost(new CommanderAdditionalCost());
         } else {
-            this.costs = new CostsImpl<>();
-            this.timing = TimingRule.SORCERY;
+            throw new IllegalStateException("Cast commander ability must be used with spell ability only: " + card.getName());
         }
         this.usesStack = true;
         this.controllerId = card.getOwnerId();
@@ -33,22 +33,6 @@ public class CastCommanderAbility extends SpellAbility {
         super(ability);
     }
 
-    @Override
-    public boolean activate(Game game, boolean noMana) {
-        if (super.activate(game, noMana)) {
-            // save amount of times commander was cast
-            Integer castCount = (Integer) game.getState().getValue(sourceId + "_castCount");
-            if (castCount == null) {
-                castCount = 1;
-            } else {
-                castCount++;
-            }
-            game.getState().setValue(sourceId + "_castCount", castCount);
-            return true;
-        }
-        return false;
-    }
-
     @Override
     public CastCommanderAbility copy() {
         return new CastCommanderAbility(this);
diff --git a/Mage/src/main/java/mage/abilities/common/PlayLandAsCommanderAbility.java b/Mage/src/main/java/mage/abilities/common/PlayLandAsCommanderAbility.java
new file mode 100644
index 0000000000..a3ca4505c3
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/common/PlayLandAsCommanderAbility.java
@@ -0,0 +1,28 @@
+package mage.abilities.common;
+
+import mage.abilities.PlayLandAbility;
+import mage.abilities.costs.common.CommanderAdditionalCost;
+import mage.constants.Zone;
+
+/**
+ * @author JayDi85
+ */
+public class PlayLandAsCommanderAbility extends PlayLandAbility {
+
+    public PlayLandAsCommanderAbility(PlayLandAbility originalAbility) {
+        super(originalAbility);
+        zone = Zone.COMMAND;
+
+        // extra cost
+        this.addCost(new CommanderAdditionalCost());
+    }
+
+    private PlayLandAsCommanderAbility(PlayLandAsCommanderAbility ability) {
+        super(ability);
+    }
+
+    @Override
+    public PlayLandAsCommanderAbility copy() {
+        return new PlayLandAsCommanderAbility(this);
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/costs/common/CommanderAdditionalCost.java b/Mage/src/main/java/mage/abilities/costs/common/CommanderAdditionalCost.java
new file mode 100644
index 0000000000..08a4160e48
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/costs/common/CommanderAdditionalCost.java
@@ -0,0 +1,34 @@
+package mage.abilities.costs.common;
+
+import mage.abilities.Ability;
+import mage.abilities.dynamicvalue.common.CommanderPlaysCount;
+import mage.game.Game;
+
+/**
+ * @author JayDi85
+ */
+public class CommanderAdditionalCost extends DynamicValueGenericManaCost {
+
+    /*
+    903.8. A player may cast a commander they own from the command zone. A commander cast from the
+    command zone costs an additional {2} for each previous time the player casting it has cast it from
+    the command zone that game. This additional cost is informally known as the “commander tax.”
+     */
+
+    public CommanderAdditionalCost() {
+        super(new CommanderPlaysCount(2), "{2} for each previous time the player casting it has cast it from the command zone");
+    }
+
+    public CommanderAdditionalCost(final CommanderAdditionalCost cost) {
+        super(cost);
+    }
+
+    @Override
+    public CommanderAdditionalCost copy() {
+        return new CommanderAdditionalCost(this);
+    }
+
+    public boolean isEmptyPay(Ability ability, Game game) {
+        return amount.calculate(game, ability, null) == 0;
+    }
+}
\ No newline at end of file
diff --git a/Mage/src/main/java/mage/abilities/costs/common/DynamicValueGenericManaCost.java b/Mage/src/main/java/mage/abilities/costs/common/DynamicValueGenericManaCost.java
new file mode 100644
index 0000000000..8f97a60517
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/costs/common/DynamicValueGenericManaCost.java
@@ -0,0 +1,46 @@
+package mage.abilities.costs.common;
+
+import mage.abilities.Ability;
+import mage.abilities.costs.Cost;
+import mage.abilities.costs.CostImpl;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.game.Game;
+
+import java.util.UUID;
+
+public class DynamicValueGenericManaCost extends CostImpl {
+
+    DynamicValue amount;
+
+    public DynamicValueGenericManaCost(DynamicValue amount, String text) {
+        this.amount = amount;
+        setText(text);
+    }
+
+    public DynamicValueGenericManaCost(DynamicValueGenericManaCost cost) {
+        super(cost);
+        this.amount = cost.amount;
+    }
+
+    @Override
+    public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
+        int convertedCost = amount.calculate(game, ability, null);
+        Cost cost = new GenericManaCost(convertedCost);
+        return cost.canPay(ability, sourceId, controllerId, game);
+    }
+
+    @Override
+    public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
+        int convertedCost = amount.calculate(game, ability, null);
+        Cost cost = new GenericManaCost(convertedCost);
+        paid = cost.pay(ability, game, sourceId, controllerId, noMana);
+        return paid;
+    }
+
+    @Override
+    public DynamicValueGenericManaCost copy() {
+        return new DynamicValueGenericManaCost(this);
+    }
+
+}
diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java
new file mode 100644
index 0000000000..65051b3f2b
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CommanderPlaysCount.java
@@ -0,0 +1,52 @@
+package mage.abilities.dynamicvalue.common;
+
+import mage.abilities.Ability;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.effects.Effect;
+import mage.game.Game;
+import mage.watchers.common.CommanderPlaysCountWatcher;
+
+/**
+ * @author JayDi85
+ */
+public class CommanderPlaysCount implements DynamicValue {
+
+    private Integer multiplier;
+
+    public CommanderPlaysCount() {
+        this(1);
+    }
+
+    public CommanderPlaysCount(Integer multiplier) {
+        this.multiplier = multiplier;
+    }
+
+    public CommanderPlaysCount(final CommanderPlaysCount dynamicValue) {
+        this.multiplier = dynamicValue.multiplier;
+    }
+
+    @Override
+    public int calculate(Game game, Ability sourceAbility, Effect effect) {
+        CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
+        int value = 0;
+        if (watcher != null) {
+            value = watcher.getPlaysCount(sourceAbility.getSourceId());
+        }
+        return value * multiplier;
+    }
+
+    @Override
+    public CommanderPlaysCount copy() {
+        return new CommanderPlaysCount(this);
+    }
+
+    @Override
+    public String toString() {
+        return "X";
+    }
+
+    @Override
+    public String getMessage() {
+        return "";
+    }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CommanderCostModification.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CommanderCostModification.java
deleted file mode 100644
index cd9b3cfbbf..0000000000
--- a/Mage/src/main/java/mage/abilities/effects/common/cost/CommanderCostModification.java
+++ /dev/null
@@ -1,54 +0,0 @@
-
-package mage.abilities.effects.common.cost;
-
-import java.util.UUID;
-import mage.abilities.Ability;
-import mage.abilities.common.CastCommanderAbility;
-import mage.abilities.costs.mana.GenericManaCost;
-import mage.constants.CostModificationType;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import mage.game.Game;
-
-/**
- *
- * @author Plopman
- */
-//20130711
-/*903.10. A player may cast a commander he or she owns from the command zone.
- * Doing so costs that player an additional {2} for each previous time he or she cast that commander from the command zone that game.
- * */
-public class CommanderCostModification extends CostModificationEffectImpl {
-
-    private final UUID commanderId;
-
-    public CommanderCostModification(UUID commanderId) {
-        super(Duration.Custom, Outcome.Neutral, CostModificationType.INCREASE_COST);
-        this.commanderId = commanderId;
-    }
-
-    public CommanderCostModification(final CommanderCostModification effect) {
-        super(effect);
-        this.commanderId = effect.commanderId;
-    }
-
-    @Override
-    public boolean apply(Game game, Ability source, Ability abilityToModify) {
-        Integer castCount = (Integer) game.getState().getValue(commanderId + "_castCount");
-        if (castCount > 0) {
-            abilityToModify.getManaCostsToPay().add(new GenericManaCost(2 * castCount));
-        }
-        return true;
-
-    }
-
-    @Override
-    public boolean applies(Ability abilityToModify, Ability source, Game game) {
-        return abilityToModify instanceof CastCommanderAbility && abilityToModify.getSourceId().equals(commanderId);
-    }
-
-    @Override
-    public CommanderCostModification copy() {
-        return new CommanderCostModification(this);
-    }
-}
diff --git a/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java b/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java
index a40d3de2f5..fb0d3beb18 100644
--- a/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java
+++ b/Mage/src/main/java/mage/abilities/keyword/CommanderStormAbility.java
@@ -13,9 +13,9 @@ import mage.game.events.GameEvent.EventType;
 import mage.game.stack.Spell;
 import mage.game.stack.StackObject;
 import mage.players.Player;
+import mage.watchers.common.CommanderPlaysCountWatcher;
 
 /**
- *
  * @author Plopman
  */
 public class CommanderStormAbility extends TriggeredAbilityImpl {
@@ -83,9 +83,9 @@ class CommanderStormEffect extends OneShotEffect {
         if (player == null) {
             return false;
         }
-        stormCount = player.getCommandersIds().stream().map(
-                (commanderId) -> (Integer) game.getState().getValue(commanderId + "_castCount")
-        ).map((castCount) -> castCount).reduce(stormCount, Integer::sum);
+        stormCount = player.getCommandersIds().stream()
+                .map((commanderId) -> game.getState().getWatcher(CommanderPlaysCountWatcher.class).getPlaysCount(commanderId))
+                .reduce(stormCount, Integer::sum);
         if (stormCount == 0) {
             return true;
         }
diff --git a/Mage/src/main/java/mage/game/GameCommanderImpl.java b/Mage/src/main/java/mage/game/GameCommanderImpl.java
index 47a6ee79a4..715295c645 100644
--- a/Mage/src/main/java/mage/game/GameCommanderImpl.java
+++ b/Mage/src/main/java/mage/game/GameCommanderImpl.java
@@ -4,7 +4,6 @@ import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.InfoEffect;
 import mage.abilities.effects.common.continuous.CommanderReplacementEffect;
-import mage.abilities.effects.common.cost.CommanderCostModification;
 import mage.cards.Card;
 import mage.constants.MultiplayerAttackOption;
 import mage.constants.PhaseStep;
@@ -14,6 +13,7 @@ import mage.game.mulligan.Mulligan;
 import mage.game.turn.TurnMod;
 import mage.players.Player;
 import mage.watchers.common.CommanderInfoWatcher;
+import mage.watchers.common.CommanderPlaysCountWatcher;
 
 import java.util.Map;
 import java.util.UUID;
@@ -40,7 +40,11 @@ public abstract class GameCommanderImpl extends GameImpl {
 
     @Override
     protected void init(UUID choosingPlayerId) {
-        //Move commander to command zone
+
+        // plays watcher
+        state.addWatcher(new CommanderPlaysCountWatcher());
+
+        // move commanders to command zone
         for (UUID playerId : state.getPlayerList(startingPlayerId)) {
             Player player = getPlayer(playerId);
             if (player != null) {
@@ -62,6 +66,7 @@ public abstract class GameCommanderImpl extends GameImpl {
                 }
             }
         }
+
         super.init(choosingPlayerId);
         if (startingPlayerSkipsDraw) {
             state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW));
@@ -73,8 +78,6 @@ public abstract class GameCommanderImpl extends GameImpl {
         commander.moveToZone(Zone.COMMAND, null, this, true);
         commander.getAbilities().setControllerId(player.getId());
         ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary));
-        ability.addEffect(new CommanderCostModification(commander.getId()));
-        getState().setValue(commander.getId() + "_castCount", 0);
         CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), checkCommanderDamage);
         getState().addWatcher(watcher);
         watcher.addCardInfoToCommander(this);
diff --git a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java
index 48e1efa89f..f43c71e92b 100644
--- a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java
+++ b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java
@@ -5,7 +5,6 @@ import mage.abilities.Ability;
 import mage.abilities.common.SimpleStaticAbility;
 import mage.abilities.effects.common.InfoEffect;
 import mage.abilities.effects.common.continuous.CommanderReplacementEffect;
-import mage.abilities.effects.common.cost.CommanderCostModification;
 import mage.cards.Card;
 import mage.cards.CardImpl;
 import mage.cards.CardSetInfo;
@@ -16,6 +15,7 @@ import mage.game.mulligan.Mulligan;
 import mage.game.turn.TurnMod;
 import mage.players.Player;
 import mage.watchers.common.CommanderInfoWatcher;
+import mage.watchers.common.CommanderPlaysCountWatcher;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -42,7 +42,10 @@ public abstract class GameTinyLeadersImpl extends GameImpl {
 
     @Override
     protected void init(UUID choosingPlayerId) {
-        //Move tiny leader to command zone
+        // plays watcher
+        state.addWatcher(new CommanderPlaysCountWatcher());
+
+        // move tiny leader to command zone
         for (UUID playerId : state.getPlayerList(startingPlayerId)) {
             Player player = getPlayer(playerId);
             if (player != null) {
@@ -55,10 +58,8 @@ public abstract class GameTinyLeadersImpl extends GameImpl {
                     commander.moveToZone(Zone.COMMAND, null, this, true);
                     Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects"));
                     ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary));
-                    ability.addEffect(new CommanderCostModification(commander.getId()));
                     // Commander rule #4 was removed Jan. 18, 2016
                     // ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander)));
-                    getState().setValue(commander.getId() + "_castCount", 0);
                     CommanderInfoWatcher watcher = new CommanderInfoWatcher(commander.getId(), false);
                     getState().addWatcher(watcher);
                     watcher.addCardInfoToCommander(this);
diff --git a/Mage/src/main/java/mage/game/command/Commander.java b/Mage/src/main/java/mage/game/command/Commander.java
index 7163f8683e..ecf856a973 100644
--- a/Mage/src/main/java/mage/game/command/Commander.java
+++ b/Mage/src/main/java/mage/game/command/Commander.java
@@ -3,11 +3,9 @@ package mage.game.command;
 import mage.MageInt;
 import mage.MageObject;
 import mage.ObjectColor;
-import mage.abilities.Abilities;
-import mage.abilities.AbilitiesImpl;
-import mage.abilities.Ability;
-import mage.abilities.SpellAbility;
+import mage.abilities.*;
 import mage.abilities.common.CastCommanderAbility;
+import mage.abilities.common.PlayLandAsCommanderAbility;
 import mage.abilities.costs.mana.ManaCost;
 import mage.abilities.costs.mana.ManaCosts;
 import mage.abilities.text.TextPart;
@@ -34,9 +32,23 @@ public class Commander implements CommandObject {
 
     public Commander(Card card) {
         this.sourceObject = card;
-        abilities.add(new CastCommanderAbility(card));
+
+        // replace spell ability by commander cast spell (to cast from command zone)
+        if (card.getSpellAbility() != null) {
+            abilities.add(new CastCommanderAbility(card));
+        }
+
+        // replace play land with commander play land (to play from command zone)
         for (Ability ability : card.getAbilities()) {
-            if (!(ability instanceof SpellAbility)) {
+            if (ability instanceof PlayLandAbility) {
+                Ability newAbility = new PlayLandAsCommanderAbility((PlayLandAbility) ability);
+                abilities.add(newAbility);
+            }
+        }
+
+        // other abilities
+        for (Ability ability : card.getAbilities()) {
+            if (!(ability instanceof SpellAbility) && !(ability instanceof PlayLandAbility)) {
                 Ability newAbility = ability.copy();
                 abilities.add(newAbility);
             }
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index dce0116701..a131973ae7 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -8,9 +8,11 @@ import mage.Mana;
 import mage.abilities.*;
 import mage.abilities.ActivatedAbility.ActivationStatus;
 import mage.abilities.common.PassAbility;
+import mage.abilities.common.PlayLandAsCommanderAbility;
 import mage.abilities.common.WhileSearchingPlayFromLibraryAbility;
 import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility;
 import mage.abilities.costs.*;
+import mage.abilities.costs.common.CommanderAdditionalCost;
 import mage.abilities.costs.mana.ManaCost;
 import mage.abilities.costs.mana.ManaCosts;
 import mage.abilities.costs.mana.ManaCostsImpl;
@@ -1145,10 +1147,13 @@ public abstract class PlayerImpl implements Player, Serializable {
             }
         }
 
+        // warning, if you change code here then fix it in activateAbility too (play commander as land)
+
         //20091005 - 305.1
         if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) {
             // int bookmark = game.bookmarkState();
             game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
+
             if (moveCards(card, Zone.BATTLEFIELD, playLandAbility, game, false, false, false, null)) {
                 landsPlayed++;
                 game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
@@ -1252,10 +1257,60 @@ public abstract class PlayerImpl implements Player, Serializable {
             pass(game);
             return true;
         }
-        if (ability instanceof PlayLandAbility) {
-            Card card = game.getCard(ability.getSourceId());
+
+        Card card = game.getCard(ability.getSourceId());
+        if (ability instanceof PlayLandAsCommanderAbility) {
+            // LAND as commander:
+            // * first time - play without cost as land
+            // * second+ times -- cast as spell with cost and stack
+
+            // code from playLand
+
+            ActivationStatus activationStatus = ability.canActivate(this.playerId, game);
+            if (!activationStatus.canActivate() || !this.canPlayLand()) {
+                return false;
+            }
+            if (card == null) {
+                return false;
+            }
+
+            // workaround to find out empty pay in commander land
+            boolean isEmptyPay = true;
+            Costs<Cost> costs = ability.getCosts().copy();
+            for (Cost cost : costs) {
+                if (!(cost instanceof CommanderAdditionalCost) || !((CommanderAdditionalCost) cost).isEmptyPay(ability, game)) {
+                    isEmptyPay = false;
+                }
+            }
+
+            if (isEmptyPay) {
+                // play as land
+                result = playLand(card, game, false);
+            } else {
+                // cast as spell with cost, but with all land's restrictions and events like Damping Engine
+                // look at code in playLand
+                if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) {
+                    game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
+
+                    SpellAbility spellAbility = new SpellAbility(null, card.getName(), game.getState().getZone(card.getId()));
+                    spellAbility.addCost(costs);
+                    spellAbility.setControllerId(this.getId());
+                    spellAbility.setSourceId(card.getId());
+                    result = cast(spellAbility, game, false, activationStatus.getPermittingObject());
+
+                    if (result) {
+                        landsPlayed++;
+                        game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
+                    }
+                } else {
+                    result = false;
+                }
+            }
+        } else if (ability instanceof PlayLandAbility) {
+            // LAND as normal card: without cost and stack
             result = playLand(card, game, false);
         } else {
+            // ABILITY
             ActivationStatus activationStatus = ability.canActivate(this.playerId, game);
             if (!activationStatus.canActivate()) {
                 return false;
@@ -3030,6 +3085,7 @@ public abstract class PlayerImpl implements Player, Serializable {
             for (ConditionalMana conditionalMana : manaPool.getConditionalMana()) {
                 availableMana.addMana(conditionalMana);
             }
+
             if (hidden) {
                 for (Card card : hand.getUniqueCards(game)) {
                     for (Ability ability : card.getAbilities(game)) { // gets this activated ability from hand? (Morph?)
@@ -3058,6 +3114,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                     }
                 }
             }
+
             for (Card card : graveyard.getUniqueCards(game)) {
                 // Handle split cards in graveyard to support Aftermath
                 if (card instanceof SplitCard) {
@@ -3074,6 +3131,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                 getOtherUseableActivatedAbilities(card, Zone.GRAVEYARD, game, useable);
                 playable.addAll(useable.values());
             }
+
             for (ExileZone exile : game.getExile().getExileZones()) {
                 for (Card card : exile.getCards(game)) {
                     if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
@@ -3091,7 +3149,8 @@ public abstract class PlayerImpl implements Player, Serializable {
                     }
                 }
             }
-            // Check to play revealed cards
+
+            // check to play revealed cards
             for (Cards cards : game.getState().getRevealed().values()) {
                 for (Card card : cards.getCards(game)) {
                     if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
@@ -3103,6 +3162,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                     }
                 }
             }
+
             // check if it's possible to play the top card of a library
             for (UUID playerInRangeId : game.getState().getPlayersInRange(getId(), game)) {
                 Player player = game.getPlayer(playerInRangeId);
@@ -3119,6 +3179,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                     }
                 }
             }
+
             // eliminate duplicate activated abilities
             Map<String, Ability> playableActivated = new HashMap<>();
             for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
@@ -3127,6 +3188,7 @@ public abstract class PlayerImpl implements Player, Serializable {
                     playableActivated.putIfAbsent(ability.toString(), ability);
                 }
             }
+
             // activated abilities from stack objects
             for (StackObject stackObject : game.getState().getStack()) {
                 for (ActivatedAbility ability : stackObject.getAbilities().getActivatedAbilities(Zone.STACK)) {
@@ -3136,15 +3198,16 @@ public abstract class PlayerImpl implements Player, Serializable {
 
                 }
             }
+
             // activated abilities from objects in the command zone (emblems or commanders)
             for (CommandObject commandObject : game.getState().getCommand()) {
                 for (ActivatedAbility ability : commandObject.getAbilities().getActivatedAbilities(Zone.COMMAND)) {
                     if (ability.isControlledBy(getId()) && canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) {
                         playableActivated.put(ability.toString(), ability);
                     }
-
                 }
             }
+
             playable.addAll(playableActivated.values());
         }
 
diff --git a/Mage/src/main/java/mage/watchers/common/CommanderInfoWatcher.java b/Mage/src/main/java/mage/watchers/common/CommanderInfoWatcher.java
index 99e7996ac0..d72635b400 100644
--- a/Mage/src/main/java/mage/watchers/common/CommanderInfoWatcher.java
+++ b/Mage/src/main/java/mage/watchers/common/CommanderInfoWatcher.java
@@ -1,9 +1,5 @@
-
 package mage.watchers.common;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
 import mage.MageObject;
 import mage.cards.Card;
 import mage.constants.WatcherScope;
@@ -15,6 +11,10 @@ import mage.game.permanent.Permanent;
 import mage.players.Player;
 import mage.watchers.Watcher;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
 /* 20130711
  *903.14a A player that's been dealt 21 or more combat damage by the same commander
  * over the course of the game loses the game. (This is a state-based action. See rule 704.)
@@ -79,11 +79,13 @@ public class CommanderInfoWatcher extends Watcher {
         if (object != null) {
             StringBuilder sb = new StringBuilder();
             sb.append("<b>Commander</b>");
-            Integer castCount = (Integer) game.getState().getValue(sourceId + "_castCount");
-            if (castCount != null) {
-                sb.append(' ').append(castCount).append(castCount == 1 ? " time" : " times").append(" casted from the command zone.");
+            CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
+            int playsCount = watcher.getPlaysCount(sourceId);
+            if (playsCount > 0) {
+                sb.append(' ').append(playsCount).append(playsCount == 1 ? " time" : " times").append(" played from the command zone.");
             }
             this.addInfo(object, "Commander", sb.toString(), game);
+
             if (checkCommanderDamage) {
                 for (Map.Entry<UUID, Integer> entry : damageToPlayer.entrySet()) {
                     Player damagedPlayer = game.getPlayer(entry.getKey());
diff --git a/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java b/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java
new file mode 100644
index 0000000000..789cb2d2c8
--- /dev/null
+++ b/Mage/src/main/java/mage/watchers/common/CommanderPlaysCountWatcher.java
@@ -0,0 +1,62 @@
+package mage.watchers.common;
+
+import mage.constants.WatcherScope;
+import mage.game.Game;
+import mage.game.events.GameEvent;
+import mage.game.events.GameEvent.EventType;
+import mage.players.Player;
+import mage.watchers.Watcher;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Calcs commanders play count (spell or land)
+ *
+ * @author JayDi85
+ */
+public class CommanderPlaysCountWatcher extends Watcher {
+
+    private final Map<UUID, Integer> playsCount = new HashMap<>();
+
+    public CommanderPlaysCountWatcher() {
+        super(WatcherScope.GAME);
+    }
+
+    public CommanderPlaysCountWatcher(final CommanderPlaysCountWatcher watcher) {
+        super(watcher);
+        this.playsCount.putAll(watcher.playsCount);
+    }
+
+    @Override
+    public CommanderPlaysCountWatcher copy() {
+        return new CommanderPlaysCountWatcher(this);
+    }
+
+    @Override
+    public void watch(GameEvent event, Game game) {
+        if (event.getType() != EventType.LAND_PLAYED && event.getType() != EventType.SPELL_CAST) {
+            return;
+        }
+
+        UUID possibleCommanderId = event.getSourceId();
+        boolean isCommanderObject = false;
+        for (Player player : game.getPlayers().values()) {
+            if (player.getCommandersIds().contains(possibleCommanderId)) {
+                isCommanderObject = true;
+                break;
+            }
+        }
+
+        if (isCommanderObject) {
+            int count = playsCount.getOrDefault(possibleCommanderId, 0);
+            count++;
+            playsCount.put(possibleCommanderId, count);
+        }
+    }
+
+    public int getPlaysCount(UUID commanderId) {
+        return this.playsCount.getOrDefault(commanderId, 0);
+    }
+}