* Geode Golem - fixed duplicated commander tax on damage trigger (#7593);

This commit is contained in:
Oleg Agafonov 2021-02-22 16:11:13 +04:00
parent 650acf9e1e
commit f6c0f4c712
4 changed files with 230 additions and 84 deletions

View file

@ -4,7 +4,6 @@ import mage.ApprovingObject;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.Card;
@ -16,8 +15,6 @@ import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.util.ManaUtil;
import mage.watchers.common.CommanderPlaysCountWatcher;
import java.util.Set;
import java.util.UUID;
@ -37,8 +34,7 @@ public final class GeodeGolem extends CardImpl {
// Trample
this.addAbility(TrampleAbility.getInstance());
// Whenever Geode Golem deals combat damage to a player, you may
// cast your commander from the command zone without paying its mana cost.
// Whenever Geode Golem deals combat damage to a player, you may cast your commander from the command zone without paying its mana cost.
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new GeodeGolemEffect(), true));
}
@ -83,12 +79,11 @@ class GeodeGolemEffect extends OneShotEffect {
target.setNotTarget(true);
if (controller.canRespond()
&& controller.choose(Outcome.PlayForFree, new CardsImpl(commandersInCommandZone), target, game)) {
if (target.getFirstTarget() != null) {
selectedCommander = commandersInCommandZone.stream()
.filter(c -> c.getId().equals(target.getFirstTarget()))
.findFirst()
.orElse(null);
}
selectedCommander = commandersInCommandZone.stream()
.filter(c -> c.getId().equals(target.getFirstTarget()))
.findFirst()
.orElse(null);
}
}
@ -96,27 +91,17 @@ class GeodeGolemEffect extends OneShotEffect {
return false;
}
// PAY
// TODO: this is broken with the commander cost reduction effect
ManaCost cost = null;
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
int castCount = watcher.getPlaysCount(selectedCommander.getId());
if (castCount > 0) {
cost = ManaUtil.createManaCost(castCount * 2, false);
}
// CAST: as spell or as land
if (cost == null
|| cost.pay(source, game, source, controller.getId(), false, null)) {
if (selectedCommander.getSpellAbility() != null) { // TODO: can be broken with mdf cards (one side creature, one side land)?
game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), Boolean.TRUE);
Boolean commanderWasCast = controller.cast(controller.chooseAbilityForCast(selectedCommander, game, true),
game, true, new ApprovingObject(source, game));
game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), null);
return commanderWasCast;
} else {
return controller.playLand(selectedCommander, game, true);
}
// commander tax applies as additional cost
if (selectedCommander.getSpellAbility() != null) {
game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), Boolean.TRUE);
Boolean commanderWasCast = controller.cast(controller.chooseAbilityForCast(selectedCommander, game, true),
game, true, new ApprovingObject(source, game));
game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), null);
return commanderWasCast;
} else {
// play commander as land is xmage feature, but mtg rules for text "cast commander" doesn't allow that
// TODO: improve lands support for "cast your commander" (allow land play from mdf cards)?
return controller.playLand(selectedCommander, game, true);
}
}
return false;

View file

@ -0,0 +1,205 @@
package org.mage.test.cards.single.c18;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestCommanderDuelBase;
/**
* @author JayDi85
*/
public class GeodeGolemTest extends CardTestCommanderDuelBase {
@Test
public void test_Normal() {
// Whenever Geode Golem deals combat damage to a player, you may
// cast your commander from the command zone without paying its mana cost.
addCard(Zone.BATTLEFIELD, playerA, "Geode Golem");
//
addCard(Zone.COMMAND, playerA, "Grizzly Bears"); // {1}{G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 10);
//
addCustomEffect_TargetDamage(playerA, 2);
checkCommandCardCount("before 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1);
// turn 1 - first cast
// attack and cast first time (free)
attack(1, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Grizzly Bears"); // commander choice
waitStackResolved(1, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Grizzly Bears", 1);
checkPermanentTapped("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Forest", true, 0);
//
// remove to command zone (0x tax)
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 2", "Grizzly Bears");
setChoice(playerA, "Yes"); // move to command zone
// turn 3 - second cast (1x tax)
attack(3, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Grizzly Bears"); // commander choice
waitStackResolved(3, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Grizzly Bears", 1);
checkPermanentTapped("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Forest", true, 2); // 1x tax
//
// remove to command zone
activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 2", "Grizzly Bears");
setChoice(playerA, "Yes"); // move to command zone
// turn 5 - third cast (2x tax)
attack(5, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Grizzly Bears"); // commander choice
waitStackResolved(5, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Grizzly Bears", 1);
checkPermanentTapped("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Forest", true, 2 * 2); // 2x tax
setStrictChooseMode(true);
setStopAt(5, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
assertPermanentCount(playerA, "Grizzly Bears", 1);
}
@Test
public void test_MDF_SingleSide() {
// Whenever Geode Golem deals combat damage to a player, you may
// cast your commander from the command zone without paying its mana cost.
addCard(Zone.BATTLEFIELD, playerA, "Geode Golem");
//
// Akoum Warrior {5}{R} - creature 4/5
// Akoum Teeth - land
addCard(Zone.COMMAND, playerA, "Akoum Warrior");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10);
//
addCustomEffect_TargetDamage(playerA, 5);
checkCommandCardCount("before 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1);
// turn 1 - first cast
// attack and cast first time (free)
attack(1, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Akoum Warrior"); // commander choice
waitStackResolved(1, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Akoum Warrior", 1);
checkPermanentTapped("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 0);
//
// remove to command zone (0x tax)
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 5", "Akoum Warrior");
setChoice(playerA, "Yes"); // move to command zone
// turn 3 - second cast (1x tax)
attack(3, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Akoum Warrior"); // commander choice
waitStackResolved(3, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Akoum Warrior", 1);
checkPermanentTapped("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2); // 1x tax
//
// remove to command zone
activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 5", "Akoum Warrior");
setChoice(playerA, "Yes"); // move to command zone
// turn 5 - third cast (2x tax)
attack(5, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Akoum Warrior"); // commander choice
waitStackResolved(5, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Akoum Warrior", 1);
checkPermanentTapped("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2 * 2); // 2x tax
setStrictChooseMode(true);
setStopAt(5, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
assertPermanentCount(playerA, "Akoum Warrior", 1);
}
@Test
public void test_MDF_BothSides() {
// Whenever Geode Golem deals combat damage to a player, you may
// cast your commander from the command zone without paying its mana cost.
addCard(Zone.BATTLEFIELD, playerA, "Geode Golem");
//
// Birgi, God of Storytelling {2}{R} - creature 3/3
// Harnfel, Horn of Bounty {4}{R} - artifact
addCard(Zone.COMMAND, playerA, "Birgi, God of Storytelling");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10);
//
addCustomEffect_TargetDamage(playerA, 3);
addCustomEffect_DestroyTarget(playerA);
checkCommandCardCount("before 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Birgi, God of Storytelling", 1);
// turn 1 - first cast, LEFT side
// attack and cast first time (free)
attack(1, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Birgi, God of Storytelling"); // commander choice
setChoice(playerA, "Cast Birgi, God of Storytelling"); // spell choice
waitStackResolved(1, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Birgi, God of Storytelling", 1);
checkPermanentTapped("after 1", 1, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 0);
//
// remove to command zone (0x tax)
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 3", "Birgi, God of Storytelling");
setChoice(playerA, "Yes"); // move to command zone
// turn 3 - second cast, LEFT side (1x tax)
attack(3, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Birgi, God of Storytelling"); // commander choice
setChoice(playerA, "Cast Birgi, God of Storytelling"); // spell choice
waitStackResolved(3, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Birgi, God of Storytelling", 1);
checkPermanentTapped("after 2", 3, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2); // 1x tax
//
// remove to command zone
activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target damage 3", "Birgi, God of Storytelling");
setChoice(playerA, "Yes"); // move to command zone
// turn 5 - third cast, RIGHT side (2x tax)
attack(5, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Birgi, God of Storytelling"); // commander choice
setChoice(playerA, "Cast Harnfel, Horn of Bounty"); // spell choice
waitStackResolved(5, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Harnfel, Horn of Bounty", 1);
checkPermanentTapped("after 3", 5, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2 * 2); // 2x tax
//
// remove to command zone
activateAbility(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "target destroy", "Harnfel, Horn of Bounty");
setChoice(playerA, "Yes"); // move to command zone
// turn 7 - fourth cast, RIGHT side (3x tax)
attack(7, playerA, "Geode Golem");
setChoice(playerA, "Yes"); // cast commander
setChoice(playerA, "Birgi, God of Storytelling"); // commander choice
setChoice(playerA, "Cast Harnfel, Horn of Bounty"); // spell choice
waitStackResolved(7, PhaseStep.COMBAT_DAMAGE);
checkPermanentCount("after 4", 7, PhaseStep.COMBAT_DAMAGE, playerA, "Harnfel, Horn of Bounty", 1);
checkPermanentTapped("after 4", 7, PhaseStep.COMBAT_DAMAGE, playerA, "Mountain", true, 2 * 3); // 3x tax
setStrictChooseMode(true);
setStopAt(7, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
assertPermanentCount(playerA, "Harnfel, Horn of Bounty", 1);
}
}

View file

@ -1,52 +0,0 @@
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 "";
}
}

View file

@ -181,6 +181,14 @@ public interface Card extends MageObject {
return getOwnerId().equals(controllerId);
}
/**
* Commander tax calculation. Can be change from {2} to life life cost (see Liesa, Shroud of Dusk)
*
* @param game
* @param source
* @param abilityToModify
* @return
*/
default boolean commanderCost(Game game, Ability source, Ability abilityToModify) {
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
int castCount = watcher.getPlaysCount(getMainCard().getId());