mirror of
https://github.com/correl/mage.git
synced 2024-11-15 03:00:16 +00:00
New commander types support:
* [KHM] fixed legendary status of mdf cards (closes #7370, #7404, #7465, #7481); * Game: added support of split cards as commander (signature spell); * Game: added support of adventure cards as commander; * Game: added support of modal double faces cards as commander;
This commit is contained in:
parent
bc72384f0d
commit
50e5809a79
9 changed files with 314 additions and 35 deletions
|
@ -65,7 +65,7 @@ public final class LiesaShroudOfDusk extends CardImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean commanderCost(Game game, Ability source, Ability abilityToModify) {
|
public boolean commanderCost(Game game, Ability source, Ability abilityToModify) {
|
||||||
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||||
int castCount = watcher.getPlaysCount(getId());
|
int castCount = watcher.getPlaysCount(getMainCard().getId());
|
||||||
if (castCount > 0) {
|
if (castCount > 0) {
|
||||||
abilityToModify.addCost(new PayLifeCost(2 * castCount));
|
abilityToModify.addCost(new PayLifeCost(2 * castCount));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package org.mage.test.cards.continuous;
|
package org.mage.test.cards.continuous;
|
||||||
|
|
||||||
|
import mage.abilities.dynamicvalue.common.CommanderCastCountValue;
|
||||||
import mage.abilities.keyword.FirstStrikeAbility;
|
import mage.abilities.keyword.FirstStrikeAbility;
|
||||||
|
import mage.cards.AdventureCard;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestCommander4Players;
|
import org.mage.test.serverside.base.CardTestCommander4Players;
|
||||||
|
|
||||||
|
@ -11,9 +14,10 @@ import org.mage.test.serverside.base.CardTestCommander4Players;
|
||||||
*/
|
*/
|
||||||
public class CommandersCastTest extends CardTestCommander4Players {
|
public class CommandersCastTest extends CardTestCommander4Players {
|
||||||
|
|
||||||
// Player order: A -> D -> C -> B
|
|
||||||
@Test
|
@Test
|
||||||
public void test_CastToBattlefieldOneTime() {
|
public void test_CastToBattlefieldOneTime() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander
|
addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
||||||
|
|
||||||
|
@ -32,6 +36,7 @@ public class CommandersCastTest extends CardTestCommander4Players {
|
||||||
@Test
|
@Test
|
||||||
public void test_CastToBattlefieldTwoTimes() {
|
public void test_CastToBattlefieldTwoTimes() {
|
||||||
// Player order: A -> D -> C -> B
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander
|
addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); // 2 + 4
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); // 2 + 4
|
||||||
//
|
//
|
||||||
|
@ -67,6 +72,8 @@ public class CommandersCastTest extends CardTestCommander4Players {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_PlayAsLandOneTime() {
|
public void test_PlayAsLandOneTime() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
addCard(Zone.COMMAND, playerA, "Academy Ruins", 1);
|
addCard(Zone.COMMAND, playerA, "Academy Ruins", 1);
|
||||||
|
|
||||||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins");
|
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins");
|
||||||
|
@ -314,4 +321,223 @@ public class CommandersCastTest extends CardTestCommander4Players {
|
||||||
|
|
||||||
assertCommandZoneCount(playerA, "Balduvian Bears", 1);
|
assertCommandZoneCount(playerA, "Balduvian Bears", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_SplitCard() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
|
// use case:
|
||||||
|
// cast left side
|
||||||
|
// return to command zone
|
||||||
|
// cast right side with commander cost
|
||||||
|
|
||||||
|
// Fire, {1}{R}
|
||||||
|
// Fire deals 2 damage divided as you choose among one or two target creatures and/or players.
|
||||||
|
// Ice, {1}{U}
|
||||||
|
// Tap target permanent.
|
||||||
|
// Draw a card.
|
||||||
|
addCard(Zone.COMMAND, playerA, "Fire // Ice", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||||
|
|
||||||
|
// both sides are playable
|
||||||
|
checkCommandCardCount("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire // Ice", 1);
|
||||||
|
checkPlayableAbility("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Fire", true);
|
||||||
|
checkPlayableAbility("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ice", true);
|
||||||
|
|
||||||
|
// turn 1 - cast left
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire");
|
||||||
|
addTargetAmount(playerA, playerB, 2);
|
||||||
|
setChoice(playerA, "Yes"); // return commander
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
// can't cast due commander cost added
|
||||||
|
checkCommandCardCount("after first cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire // Ice", 1);
|
||||||
|
checkPlayableAbility("after first cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Fire", false);
|
||||||
|
checkPlayableAbility("after first cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ice", false);
|
||||||
|
|
||||||
|
// turn 5 - can cost again
|
||||||
|
checkPlayableAbility("before second cast", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Fire", true);
|
||||||
|
checkPlayableAbility("before second cast", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ice", true);
|
||||||
|
// cast right and use all mana (4 = card + commander cost)
|
||||||
|
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Ice");
|
||||||
|
addTarget(playerA, "Mountain"); // tap target
|
||||||
|
setChoice(playerA, "Yes"); // move to commander
|
||||||
|
waitStackResolved(5, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
checkCommandCardCount("after second cast", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire // Ice", 1);
|
||||||
|
checkPlayableAbility("after second cast", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Fire", false);
|
||||||
|
checkPlayableAbility("after second cast", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ice", false);
|
||||||
|
// must used all mana
|
||||||
|
checkPermanentTapped("after second cast", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain", true, 2);
|
||||||
|
checkPermanentTapped("after second cast", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Island", true, 2);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(5, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AdventureCard() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
|
addCustomEffect_TargetDamage(playerA, 10); // kill creature
|
||||||
|
|
||||||
|
// use case:
|
||||||
|
// cast adventure spell from command zone and keep it in exile (inc next command cost)
|
||||||
|
// cast card from exile (do not inc next command cost)
|
||||||
|
// return commander to command zone
|
||||||
|
// cast as adventure spell (with x1 command cost)
|
||||||
|
|
||||||
|
// Curious Pair, creature, {1}{G}, 1/3
|
||||||
|
// Treats to Share, sorcery, {G}
|
||||||
|
// Create a Food token.
|
||||||
|
addCard(Zone.COMMAND, playerA, "Curious Pair", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||||
|
addCard(Zone.HAND, playerA, "Forest", 3); // for commander cost test
|
||||||
|
|
||||||
|
// commander tax: 0
|
||||||
|
// both sides are playable
|
||||||
|
checkCommandCardCount("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair", 1);
|
||||||
|
checkPlayableAbility("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", true);
|
||||||
|
checkPlayableAbility("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", true);
|
||||||
|
|
||||||
|
// cast adventure spell
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||||
|
setChoice(playerA, "Yes"); // return commander
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
checkCommandCardCount("after first cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair", 1);
|
||||||
|
checkPermanentCount("after first cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Food", 1);
|
||||||
|
// commander tax: 1x
|
||||||
|
// can't cast due commander cost added (we stil have 2x mana)
|
||||||
|
checkPlayableAbility("after first cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", false);
|
||||||
|
checkPlayableAbility("after first cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", false);
|
||||||
|
|
||||||
|
// commander tax: 1x
|
||||||
|
// play land number 1 and give extra {G}, so total 3x
|
||||||
|
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest");
|
||||||
|
checkPlayableAbility("after mana add", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", false);
|
||||||
|
checkPlayableAbility("after mana add", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", true);
|
||||||
|
|
||||||
|
|
||||||
|
// play adventure spell, but keep it
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||||
|
setChoice(playerA, "No"); // do not return commander
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
checkPermanentCount("after second cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Food", 2);
|
||||||
|
checkPlayableAbility("after second cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", false);
|
||||||
|
checkPlayableAbility("after second cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", false);
|
||||||
|
|
||||||
|
// wait next turn
|
||||||
|
// commander tax: 2x BUT it doesn't apply to exile zone (e.g. must use 2x mana instead 6x)
|
||||||
|
// it doesn't add commander tax too
|
||||||
|
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
|
||||||
|
waitStackResolved(5, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
checkPermanentCount("after exile cast", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair", 1);
|
||||||
|
checkPermanentTapped("after exile cast", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest", true, 4 - 2);
|
||||||
|
|
||||||
|
// return commander to command zone
|
||||||
|
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "target damage 10", "Curious Pair");
|
||||||
|
setChoice(playerA, "Yes"); // return to command zone
|
||||||
|
// can't cast - only {2} mana, but need {G} + {2} + {2}
|
||||||
|
checkPlayableAbility("after return 2", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", false);
|
||||||
|
checkPlayableAbility("after return 2", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", false);
|
||||||
|
|
||||||
|
// turn 9
|
||||||
|
// commander tax: 2x
|
||||||
|
// mana: {G}{G}{G}{G}
|
||||||
|
// can't cast adventure spell for {G} + {2} + {2}
|
||||||
|
// can't cast creature spell for {G}{G} + {2} + {2}
|
||||||
|
runCode("check commander tax 2x", 9, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||||
|
AdventureCard card = (AdventureCard) game.getCommanderCardsFromCommandZone(player).stream().findFirst().get();
|
||||||
|
Assert.assertEquals(2, CommanderCastCountValue.instance.calculate(game, card.getSpellAbility(), null));
|
||||||
|
Assert.assertEquals(2, CommanderCastCountValue.instance.calculate(game, card.getSpellCard().getSpellAbility(), null));
|
||||||
|
});
|
||||||
|
checkPlayableAbility("before last cast 1", 9, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", false);
|
||||||
|
checkPlayableAbility("before last cast 1", 9, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", false);
|
||||||
|
// play land number 2 - can play adventure spell
|
||||||
|
playLand(9, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest");
|
||||||
|
// commander tax: 2x
|
||||||
|
// mana: {G}{G}{G}{G}{G}
|
||||||
|
checkPlayableAbility("before last cast 2", 9, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", false);
|
||||||
|
checkPlayableAbility("before last cast 2", 9, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", true);
|
||||||
|
|
||||||
|
// turn 13
|
||||||
|
// play land number 3 - can play all parts
|
||||||
|
playLand(13, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest");
|
||||||
|
// commander tax: 2x
|
||||||
|
// mana: {G}{G}{G}{G}{G}{G}
|
||||||
|
checkPlayableAbility("before last cast 3", 13, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Curious Pair", true);
|
||||||
|
checkPlayableAbility("before last cast 3", 13, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Treats to Share", true);
|
||||||
|
// cast creature
|
||||||
|
castSpell(13, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
|
||||||
|
waitStackResolved(13, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
checkPermanentCount("after last cast", 13, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair", 1);
|
||||||
|
checkPermanentTapped("after last cast", 13, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest", true, 2 + 2 + 2);
|
||||||
|
runCode("check commander tax 3x", 13, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||||
|
AdventureCard card = (AdventureCard) game.getCard(game.getCommandersIds(player).stream().findFirst().get());
|
||||||
|
Assert.assertEquals(3, CommanderCastCountValue.instance.calculate(game, card.getSpellAbility(), null));
|
||||||
|
Assert.assertEquals(3, CommanderCastCountValue.instance.calculate(game, card.getSpellCard().getSpellAbility(), null));
|
||||||
|
});
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(13, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ModalDoubleFacesCard() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
|
// use case:
|
||||||
|
// cast left side as commander
|
||||||
|
// return to command zone
|
||||||
|
// cast right side as commander with commander cost
|
||||||
|
|
||||||
|
// Tergrid, God of Fright, {3}{B}{B}, creature
|
||||||
|
// Tergrid's Lantern, {3}{B}, artifact
|
||||||
|
addCard(Zone.COMMAND, playerA, "Tergrid, God of Fright", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
|
||||||
|
//
|
||||||
|
// Exile target creature or enchantment.
|
||||||
|
addCard(Zone.HAND, playerA, "Angelic Edict", 1); // {4}{W}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||||
|
|
||||||
|
// both sides are playable
|
||||||
|
checkCommandCardCount("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tergrid, God of Fright", 1);
|
||||||
|
checkPlayableAbility("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Tergrid, God of Fright", true);
|
||||||
|
checkPlayableAbility("before cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Tergrid's Lantern", true);
|
||||||
|
|
||||||
|
// cast left side
|
||||||
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 5);
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tergrid, God of Fright");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tergrid, God of Fright", 1);
|
||||||
|
|
||||||
|
// exile and return
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angelic Edict", "Tergrid, God of Fright");
|
||||||
|
setChoice(playerA, "Yes"); // return to command
|
||||||
|
|
||||||
|
// turn 5 - check commander cost
|
||||||
|
checkPlayableAbility("before second cast 1", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Tergrid, God of Fright", true);
|
||||||
|
checkPlayableAbility("before second cast 1", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Tergrid's Lantern", true);
|
||||||
|
|
||||||
|
// turn 5 - remove angelic's mana, so it can cast only one part
|
||||||
|
activateManaAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 5 - 1);
|
||||||
|
checkPlayableAbility("before second cast 2", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Tergrid, God of Fright", false);
|
||||||
|
checkPlayableAbility("before second cast 2", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Tergrid's Lantern", true);
|
||||||
|
|
||||||
|
// turn 5 - cast right side
|
||||||
|
castSpell(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Tergrid's Lantern");
|
||||||
|
waitStackResolved(5, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
checkPermanentCount("after second cast", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Tergrid's Lantern", 1);
|
||||||
|
// must used all mana
|
||||||
|
checkPermanentTapped("after second cast", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Swamp", true, 5);
|
||||||
|
checkPermanentTapped("after second cast", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Plains", true, 5);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(5, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,19 +239,29 @@ public class ModalDoubleFacesCardsTest extends CardTestPlayerBase {
|
||||||
// Akoum Teeth - land
|
// Akoum Teeth - land
|
||||||
addCard(Zone.HAND, playerA, "Akoum Warrior");
|
addCard(Zone.HAND, playerA, "Akoum Warrior");
|
||||||
|
|
||||||
|
// mdf and legendary
|
||||||
|
addCard(Zone.HAND, playerA, "Halvar, God of Battle");
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
assertAllCommandsUsed();
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
// stats in hand
|
Assert.assertEquals(2, getHandCards(playerA).size());
|
||||||
Assert.assertEquals(1, getHandCards(playerA).size());
|
|
||||||
Card card = getHandCards(playerA).get(0);
|
// stats in hand - normal
|
||||||
|
Card card = getHandCards(playerA).stream().filter(c -> CardUtil.haveSameNames(c, "Akoum Warrior", currentGame)).findFirst().get();
|
||||||
Assert.assertFalse("must be non land", card.isLand());
|
Assert.assertFalse("must be non land", card.isLand());
|
||||||
Assert.assertTrue("must be creature", card.isCreature());
|
Assert.assertTrue("must be creature", card.isCreature());
|
||||||
Assert.assertTrue("must be minotaur", card.hasSubtype(SubType.MINOTAUR, currentGame));
|
Assert.assertTrue("must be minotaur", card.hasSubtype(SubType.MINOTAUR, currentGame));
|
||||||
Assert.assertEquals("power", 4, card.getPower().getValue());
|
Assert.assertEquals("power", 4, card.getPower().getValue());
|
||||||
Assert.assertEquals("toughness", 5, card.getToughness().getValue());
|
Assert.assertEquals("toughness", 5, card.getToughness().getValue());
|
||||||
|
|
||||||
|
// stats in hand - mdf
|
||||||
|
card = getHandCards(playerA).stream().filter(c -> CardUtil.haveSameNames(c, "Halvar, God of Battle", currentGame)).findFirst().get();
|
||||||
|
Assert.assertTrue("must be legendary", card.isLegendary());
|
||||||
|
Assert.assertTrue("must be creature", card.isCreature());
|
||||||
|
Assert.assertTrue("must be god", card.hasSubtype(SubType.GOD, currentGame));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -5,8 +5,11 @@ import mage.abilities.Abilities;
|
||||||
import mage.abilities.AbilitiesImpl;
|
import mage.abilities.AbilitiesImpl;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.abilities.effects.common.DamageTargetEffect;
|
||||||
import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
|
import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
|
||||||
import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect;
|
import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
|
@ -29,6 +32,7 @@ import mage.server.util.ConfigWrapper;
|
||||||
import mage.server.util.PluginClassLoader;
|
import mage.server.util.PluginClassLoader;
|
||||||
import mage.server.util.config.GamePlugin;
|
import mage.server.util.config.GamePlugin;
|
||||||
import mage.server.util.config.Plugin;
|
import mage.server.util.config.Plugin;
|
||||||
|
import mage.target.common.TargetAnyTarget;
|
||||||
import mage.util.Copier;
|
import mage.util.Copier;
|
||||||
import org.apache.log4j.Level;
|
import org.apache.log4j.Level;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
@ -443,6 +447,22 @@ public abstract class MageTestPlayerBase {
|
||||||
new SimpleStaticAbility(effect)
|
new SimpleStaticAbility(effect)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add target damage ability that can be called by text: "target damage xxx"
|
||||||
|
*
|
||||||
|
* @param controller
|
||||||
|
* @param damageAmount
|
||||||
|
*/
|
||||||
|
protected void addCustomEffect_TargetDamage(TestPlayer controller, int damageAmount) {
|
||||||
|
Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(damageAmount).setText("target damage " + damageAmount), new ManaCostsImpl(""));
|
||||||
|
ability.addTarget(new TargetAnyTarget());
|
||||||
|
addCustomCardWithAbility(
|
||||||
|
"target damage " + damageAmount + " for " + controller.getName(),
|
||||||
|
controller,
|
||||||
|
ability
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom card with global abilities list to init (can contains abilities per card name)
|
// custom card with global abilities list to init (can contains abilities per card name)
|
||||||
|
|
|
@ -41,7 +41,12 @@ public class CommanderCostModification extends CostModificationEffectImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||||
return commander.getId().equals(abilityToModify.getSourceId())
|
Card cardToCheck = game.getCard(abilityToModify.getSourceId()); // split/mdf cards support
|
||||||
|
if (cardToCheck == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return commander.getId().equals(cardToCheck.getMainCard().getId())
|
||||||
&& (abilityToModify instanceof CastCommanderAbility
|
&& (abilityToModify instanceof CastCommanderAbility
|
||||||
|| abilityToModify instanceof PlayLandAsCommanderAbility);
|
|| abilityToModify instanceof PlayLandAsCommanderAbility);
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ public interface Card extends MageObject {
|
||||||
|
|
||||||
default boolean commanderCost(Game game, Ability source, Ability abilityToModify) {
|
default boolean commanderCost(Game game, Ability source, Ability abilityToModify) {
|
||||||
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||||
int castCount = watcher.getPlaysCount(getId());
|
int castCount = watcher.getPlaysCount(getMainCard().getId());
|
||||||
if (castCount > 0) {
|
if (castCount > 0) {
|
||||||
abilityToModify.getManaCostsToPay().add(ManaUtil.createManaCost(2 * castCount, false));
|
abilityToModify.getManaCostsToPay().add(ManaUtil.createManaCost(2 * castCount, false));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ import mage.util.CardUtil;
|
||||||
import mage.util.SubTypes;
|
import mage.util.SubTypes;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,6 +127,12 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<SuperType> getSuperType() {
|
||||||
|
// CardImpl's constructor can call some code on init, so you must check left/right before
|
||||||
|
// it's a bad workaround
|
||||||
|
return leftHalfCard != null ? leftHalfCard.getSuperType() : supertype;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ArrayList<CardType> getCardType() {
|
public ArrayList<CardType> getCardType() {
|
||||||
|
@ -154,11 +160,6 @@ public abstract class ModalDoubleFacesCard extends CardImpl {
|
||||||
return leftHalfCard.hasSubtype(subtype, game);
|
return leftHalfCard.hasSubtype(subtype, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public EnumSet<SuperType> getSuperType() {
|
|
||||||
return EnumSet.noneOf(SuperType.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Abilities<Ability> getAbilities() {
|
public Abilities<Ability> getAbilities() {
|
||||||
return getInnerAbilities(false);
|
return getInnerAbilities(false);
|
||||||
|
|
|
@ -12,7 +12,6 @@ import mage.abilities.text.TextPart;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.FrameStyle;
|
import mage.cards.FrameStyle;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SpellAbilityType;
|
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.SuperType;
|
import mage.constants.SuperType;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
@ -36,16 +35,30 @@ public class Commander implements CommandObject {
|
||||||
this.sourceObject = card;
|
this.sourceObject = card;
|
||||||
|
|
||||||
// replace spell ability by commander cast spell (to cast from command zone)
|
// replace spell ability by commander cast spell (to cast from command zone)
|
||||||
if (card.getSpellAbility() != null) {
|
|
||||||
abilities.add(new CastCommanderAbility(card, card.getSpellAbility()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace alternative spell abilities by commander cast spell (to cast from command zone)
|
|
||||||
for (Ability ability : card.getAbilities()) {
|
for (Ability ability : card.getAbilities()) {
|
||||||
if (ability instanceof SpellAbility) {
|
if (ability instanceof SpellAbility) {
|
||||||
SpellAbility spellAbility = (SpellAbility) ability;
|
SpellAbility spellAbility = (SpellAbility) ability;
|
||||||
if (spellAbility.getSpellAbilityType() == SpellAbilityType.BASE_ALTERNATE) {
|
switch (spellAbility.getSpellAbilityType()) {
|
||||||
|
case BASE:
|
||||||
|
case BASE_ALTERNATE:
|
||||||
|
case SPLIT:
|
||||||
|
case SPLIT_FUSED:
|
||||||
|
case SPLIT_LEFT:
|
||||||
|
case SPLIT_RIGHT:
|
||||||
|
case MODAL:
|
||||||
|
case MODAL_LEFT:
|
||||||
|
case MODAL_RIGHT:
|
||||||
|
case ADVENTURE_SPELL:
|
||||||
|
// can be used from command zone
|
||||||
abilities.add(new CastCommanderAbility(card, spellAbility));
|
abilities.add(new CastCommanderAbility(card, spellAbility));
|
||||||
|
break;
|
||||||
|
case FACE_DOWN_CREATURE: // dynamic added spell for alternative cost like cast as face down
|
||||||
|
case SPLICE: // only from hand
|
||||||
|
case SPLIT_AFTERMATH: // only from graveyard
|
||||||
|
// can't use from command zone
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Error, unknown spell type in commander card: " + spellAbility.getSpellAbilityType() + " from " + card.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.events.GameEvent.EventType;
|
import mage.game.events.GameEvent.EventType;
|
||||||
|
import mage.util.CardUtil;
|
||||||
import mage.watchers.Watcher;
|
import mage.watchers.Watcher;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -12,7 +13,7 @@ import java.util.*;
|
||||||
/**
|
/**
|
||||||
* Calcs commanders play count only from command zone (spell or land)
|
* Calcs commanders play count only from command zone (spell or land)
|
||||||
* Cards like Remand can put command to hand and cast it without commander tax increase
|
* Cards like Remand can put command to hand and cast it without commander tax increase
|
||||||
*
|
* <p>
|
||||||
* Warning, if your code can be called in non commander games then you must watcher in your ability
|
* Warning, if your code can be called in non commander games then you must watcher in your ability
|
||||||
* (example: you are using watcher in trigger, hint or effect, but do not checking another things like commander source or cost)
|
* (example: you are using watcher in trigger, hint or effect, but do not checking another things like commander source or cost)
|
||||||
*
|
*
|
||||||
|
@ -33,14 +34,17 @@ public class CommanderPlaysCountWatcher extends Watcher {
|
||||||
&& event.getType() != EventType.SPELL_CAST) {
|
&& event.getType() != EventType.SPELL_CAST) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// must control main cards (split/mdf cards support)
|
||||||
final UUID objectId;
|
final UUID objectId;
|
||||||
if (event.getType() == EventType.LAND_PLAYED) {
|
if (event.getType() == EventType.LAND_PLAYED) {
|
||||||
objectId = event.getTargetId();
|
objectId = CardUtil.getMainCardId(game, event.getTargetId());
|
||||||
} else if (event.getType() == EventType.SPELL_CAST) {
|
} else if (event.getType() == EventType.SPELL_CAST) {
|
||||||
objectId = event.getSourceId();
|
objectId = CardUtil.getMainCardId(game, event.getSourceId());
|
||||||
} else {
|
} else {
|
||||||
objectId = null;
|
objectId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isCommanderObject = game
|
boolean isCommanderObject = game
|
||||||
.getPlayerList()
|
.getPlayerList()
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -51,8 +55,8 @@ public class CommanderPlaysCountWatcher extends Watcher {
|
||||||
if (!isCommanderObject || event.getZone() != Zone.COMMAND) {
|
if (!isCommanderObject || event.getZone() != Zone.COMMAND) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
playsCount.putIfAbsent(event.getSourceId(), 0);
|
playsCount.putIfAbsent(objectId, 0);
|
||||||
playsCount.computeIfPresent(event.getSourceId(), (u, i) -> i + 1);
|
playsCount.computeIfPresent(objectId, (u, i) -> i + 1);
|
||||||
playerCount.putIfAbsent(event.getPlayerId(), 0);
|
playerCount.putIfAbsent(event.getPlayerId(), 0);
|
||||||
playerCount.compute(event.getPlayerId(), (u, i) -> i + 1);
|
playerCount.compute(event.getPlayerId(), (u, i) -> i + 1);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue