mirror of
https://github.com/correl/mage.git
synced 2024-12-26 03:00:11 +00:00
Merge origin/master
This commit is contained in:
commit
f065315b2f
5 changed files with 395 additions and 12 deletions
|
@ -0,0 +1,300 @@
|
||||||
|
package org.mage.test.cards.cost.modification;
|
||||||
|
|
||||||
|
import mage.abilities.keyword.HexproofAbility;
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Battlefield Thaumaturge:
|
||||||
|
* Creature - Human Wizard
|
||||||
|
* Each instant and sorcery spell you cast costs {1} less to cast for each creature it targets.
|
||||||
|
* Heroic - Whenever you cast a spell that targets Battlefield Thaumaturge, Battlefield Thaumaturge gains hexproof until end of turn.
|
||||||
|
*
|
||||||
|
* @author Simown
|
||||||
|
*/
|
||||||
|
public class BattlefieldThaumaturgeTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleTargetReduction() {
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Battlefield Thaumaturge");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island");
|
||||||
|
addCard(Zone.HAND, playerA, "Lightning Strike");
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Akroan Skyguard");
|
||||||
|
|
||||||
|
// Lightning Strike - {1}{R} - Lightning Strike deals 3 damage to target creature or player.
|
||||||
|
// Because Battlefield Thaumaturge is on the battlefield, and the creature is targeted by the
|
||||||
|
// Lightning Strike it will be payable with {R}.
|
||||||
|
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Strike", "Akroan Skyguard");
|
||||||
|
setStopAt(2, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
// PlayerA still has the Battlefield Thaumaturge
|
||||||
|
assertPermanentCount(playerA, "Battlefield Thaumaturge", 1);
|
||||||
|
// The Akroan Skyguard has been killed by the Lightning Strike
|
||||||
|
assertPermanentCount(playerB, "Akroan Skyguard", 0);
|
||||||
|
assertGraveyardCount(playerB, "Akroan Skyguard", 1);
|
||||||
|
|
||||||
|
// Check {R} has been used to pay, and the other land remains untapped
|
||||||
|
assertTappedCount("Mountain", true, 1);
|
||||||
|
assertTappedCount("Island", false, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStriveTargetingReduction1() {
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Battlefield Thaumaturge");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Pharika's Chosen");
|
||||||
|
addCard(Zone.HAND, playerA, "Silence the Believers");
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Battlewise Hoplite");
|
||||||
|
/**
|
||||||
|
* Silence the Believers - {2}{B}{B}
|
||||||
|
* Exile any number of target creatures and all Auras attached to them
|
||||||
|
* Strive - Silence the Believers costs {2}{B} more to cast for each target beyond the first.
|
||||||
|
* Targetting a creature on both sides of the battlefield.
|
||||||
|
*/
|
||||||
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Silence the Believers", "Pharika's Chosen^Battlewise Hoplite");
|
||||||
|
setStopAt(2, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
// Both creatures were exiled
|
||||||
|
assertExileCount("Pharika's Chosen", 1);
|
||||||
|
assertExileCount("Battlewise Hoplite", 1);
|
||||||
|
// Not still on the battlefield or in the graveyard
|
||||||
|
assertPermanentCount(playerA, "Pharika's Chosen", 0);
|
||||||
|
assertPermanentCount(playerB, "Battlewise Hoplite", 0);
|
||||||
|
assertGraveyardCount(playerA, "Pharika's Chosen", 0);
|
||||||
|
assertGraveyardCount(playerB, "Battlewise Hoplite", 0);
|
||||||
|
|
||||||
|
/* Cost to exile 2 permanents will be:
|
||||||
|
* + {2}{B}{B} for the base spell
|
||||||
|
* + {2}{B} for an additional target
|
||||||
|
* - {2} for Battlefield Thaumaturge cost reducing effect
|
||||||
|
* = {2}{B}{B}{B} to pay.
|
||||||
|
*/
|
||||||
|
// Check 3 Swamps have been tapped to pay the reduced cost
|
||||||
|
assertTappedCount("Swamp", true, 3);
|
||||||
|
// And 2 Forests for the colorless mana
|
||||||
|
assertTappedCount("Forest", true, 2);
|
||||||
|
// And the rest of the Forests remain untapped
|
||||||
|
assertTappedCount("Forest", false, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStriveTargetingReduction2() {
|
||||||
|
|
||||||
|
String [] creatures = {"Battlefield Thaumaturge", "Agent of Horizons", "Blood-Toll Harpy", "Anvilwrought Raptor", "Fleshmad Steed" };
|
||||||
|
for(String creature : creatures) {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, creature);
|
||||||
|
}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
|
||||||
|
addCard(Zone.HAND, playerA, "Launch the Fleet");
|
||||||
|
|
||||||
|
// Launch the Fleet - {W}
|
||||||
|
// Strive - Launch the Fleet costs {1} more to cast for each target beyond the first.
|
||||||
|
// Until end of turn, any number of target creatures each gain "Whenever this creature attacks, put a 1/1 white Soldier token onto the battlefield tapped and attacking."
|
||||||
|
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Launch the Fleet", createTargetingString(creatures));
|
||||||
|
for(String creature : creatures) {
|
||||||
|
attack(3, playerA, creature);
|
||||||
|
}
|
||||||
|
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
// 5 initial creatures + 5 soldier tokens + 6 lands
|
||||||
|
assertPermanentCount(playerA, 16);
|
||||||
|
// The initial creatures exist
|
||||||
|
for(String creature : creatures) {
|
||||||
|
assertPermanentCount(playerA, creature, 1);
|
||||||
|
}
|
||||||
|
// Each has a solider token generated while attacking
|
||||||
|
assertPermanentCount(playerA, "Soldier", 5);
|
||||||
|
// Battlefield Thaumaturge will have hexproof from heroic trigger
|
||||||
|
Permanent battlefieldThaumaturge = getPermanent("Battlefield Thaumaturge", playerA.getId());
|
||||||
|
Assert.assertTrue("Battlefield Thaumaturge must have hexproof", battlefieldThaumaturge.getAbilities().contains(HexproofAbility.getInstance()));
|
||||||
|
|
||||||
|
assertLife(playerA, 20);
|
||||||
|
// 5 initial creatures + 5 soldier tokens => 16 damage
|
||||||
|
assertLife(playerB, 4);
|
||||||
|
/* Cost to have 5 attackers generate soldier tokens
|
||||||
|
* + {W} for the base spell
|
||||||
|
* + {4} for an additional targets
|
||||||
|
* - {4} for Battlefield Thaumaturge cost reducing effect (reduce {1} per target)
|
||||||
|
* = {W} to pay.
|
||||||
|
*/
|
||||||
|
assertTappedCount("Plains", true, 1);
|
||||||
|
// No other mana has been tapped to pay costs
|
||||||
|
assertTappedCount("Island", false, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVariableCostReduction() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Battlefield Thaumaturge");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||||
|
addCard(Zone.HAND, playerA, "Curse of the Swine");
|
||||||
|
|
||||||
|
String [] opponentsCreatures = {"Fleecemane Lion", "Sedge Scorpion", "Boon Satyr", "Bronze Sable"};
|
||||||
|
for(String creature: opponentsCreatures) {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Curse of the Swine - {X}{U}{U}
|
||||||
|
* Exile X target creatures. For each creature exiled this way,
|
||||||
|
* its controller puts a 2/2 green Boar creature token onto the battlefield
|
||||||
|
*/
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of the Swine");
|
||||||
|
setChoice(playerA, "X=4");
|
||||||
|
addTarget(playerA, createTargetingString(opponentsCreatures));
|
||||||
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
// All the opponents creatures have been exiled from the battlefield
|
||||||
|
for(String creature: opponentsCreatures) {
|
||||||
|
assertPermanentCount(playerB, creature, 0);
|
||||||
|
assertGraveyardCount(playerB, creature, 0);
|
||||||
|
assertExileCount(creature, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All 4 creatures have been replaced by boars
|
||||||
|
assertPermanentCount(playerB, "Boar", 4);
|
||||||
|
|
||||||
|
/* Cost to target 4 permanents will be:
|
||||||
|
* + {4}{U}{U} for the base spell with X = 4
|
||||||
|
* - {4} for Battlefield Thaumaturge cost reducing effect as 4 creatures are targetted
|
||||||
|
* = {U}{U} to pay.
|
||||||
|
*/
|
||||||
|
// Check 2 islands have been tapped to pay the reduced cost
|
||||||
|
assertTappedCount("Island", true, 2);
|
||||||
|
// And the rest of the lands remain untapped
|
||||||
|
assertTappedCount("Plains", false, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMutipleTargetReduction() {
|
||||||
|
|
||||||
|
String [] playerACreatures = {"Battlefield Thaumaturge", "Sedge Scorpion", "Boon Satyr"};
|
||||||
|
String [] playerBCreatures = {"Agent of Horizons", "Blood-Toll Harpy", "Anvilwrought Raptor"};
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
|
||||||
|
// Creatures for player A
|
||||||
|
for(String creature: playerACreatures) {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, creature);
|
||||||
|
}
|
||||||
|
addCard(Zone.HAND, playerA, "Descent of the Dragons");
|
||||||
|
// Creatures for player B
|
||||||
|
for(String creature: playerBCreatures) {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, creature);
|
||||||
|
}
|
||||||
|
/* Descent of the Dragons - {4}{R}{R}
|
||||||
|
* Destroy any number of target creatures.
|
||||||
|
* For each creature destroyed this way, its controller puts a 4/4 red Dragon creature token with flying onto the battlefield.
|
||||||
|
* Battlefield Thaumaturge should reduce the cost of the spell when cast, before he is destroyed and replaced with a dragon.
|
||||||
|
*/
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Descent of the Dragons", createTargetingString(playerACreatures) + "^" + createTargetingString(playerBCreatures));
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
// All creatures have been put in the graveyard
|
||||||
|
for(String creature: playerACreatures) {
|
||||||
|
assertPermanentCount(playerA, creature, 0);
|
||||||
|
assertGraveyardCount(playerA, creature, 1);
|
||||||
|
}
|
||||||
|
for(String creature: playerBCreatures) {
|
||||||
|
assertPermanentCount(playerB, creature, 0);
|
||||||
|
assertGraveyardCount(playerB, creature, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And each player has 3 dragons
|
||||||
|
assertPermanentCount(playerA, "Dragon", 3);
|
||||||
|
assertPermanentCount(playerB, "Dragon", 3);
|
||||||
|
|
||||||
|
/* Cost to target 6 creatures will be
|
||||||
|
* + {4}{R}{R} for the fixed cost base spell
|
||||||
|
* - {4} for Battlefield Thaumaturge cost reducing effect
|
||||||
|
* each creature targeted will reduce the cost by {1} so the cost
|
||||||
|
* can only be reduced by {4} maximum using Battlefield Thaumaturge
|
||||||
|
* even though 6 creatures are targeted.
|
||||||
|
* = {R}{R} to pay.
|
||||||
|
*/
|
||||||
|
// Check 2 mountains have been tapped to pay the reduced cost
|
||||||
|
assertTappedCount("Mountain", true, 2);
|
||||||
|
// And the rest of the lands remain untapped
|
||||||
|
assertTappedCount("Swamp", false, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTargetNonCreature() {
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Battlefield Thaumaturge");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||||
|
addCard(Zone.HAND, playerA, "Fade into Antiquity");
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Heliod, God of the Sun");
|
||||||
|
|
||||||
|
// Fade into Antiquity - Sorcery - {2}{G} - Exile target artifact or enchantment.
|
||||||
|
// Battlefield Thaumaturge only reduces the cost instants and sorceries where the target is a creature
|
||||||
|
// No cost reduction for targeting an enchantment (devotion is too low for Heliod to be a creature)
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fade into Antiquity", "Heliod, God of the Sun");
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
// PlayerA still has the Battlefield Thaumaturge
|
||||||
|
assertPermanentCount(playerA, "Battlefield Thaumaturge", 1);
|
||||||
|
// Heliod has been exiled
|
||||||
|
assertPermanentCount(playerB, "Heliod, God of the Sun", 0);
|
||||||
|
assertGraveyardCount(playerB, "Heliod, God of the Sun", 0);
|
||||||
|
assertExileCount("Heliod, God of the Sun", 1);
|
||||||
|
|
||||||
|
// Expect full amount paid, i.e. all lands are tapped. Cost was not reduced by Battlefield Thaumaturge, no creature was targeted.
|
||||||
|
assertTappedCount("Forest", true, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTargetWithAura() {
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Battlefield Thaumaturge");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||||
|
addCard(Zone.HAND, playerA, "Spectra Ward");
|
||||||
|
|
||||||
|
// Spectra Ward - {3}{W}{W} - Aura
|
||||||
|
// Enchanted creature gets +2/+2 and has protection from all colors. This effect doesn't remove auras.
|
||||||
|
// Battlefield Thaumaturge only reduces the cost instants and sorceries targeting creatures.
|
||||||
|
// No cost reduction for targeting a creature with an Aura.
|
||||||
|
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Spectra Ward", "Battlefield Thaumaturge");
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, "Battlefield Thaumaturge", 1);
|
||||||
|
assertPermanentCount(playerA, "Spectra Ward", 1);
|
||||||
|
// Battlefield Thaumaturge will have hexproof from heroic trigger
|
||||||
|
Permanent battlefieldThaumaturge = getPermanent("Battlefield Thaumaturge", playerA.getId());
|
||||||
|
Assert.assertTrue("Battlefield Thaumaturge must have hexproof", battlefieldThaumaturge.getAbilities().contains(HexproofAbility.getInstance()));
|
||||||
|
|
||||||
|
// No cost reduction from Battlefield Thaumaturge, full amount paid
|
||||||
|
assertTappedCount("Plains", true, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createTargetingString(String [] targets) {
|
||||||
|
StringBuilder targetBuilder = new StringBuilder();
|
||||||
|
for (String target : targets) {
|
||||||
|
if (targetBuilder.length() > 0) {
|
||||||
|
targetBuilder.append('^');
|
||||||
|
}
|
||||||
|
targetBuilder.append(target);
|
||||||
|
}
|
||||||
|
System.out.println(targetBuilder.toString());
|
||||||
|
return targetBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -711,6 +711,28 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
Assert.assertEquals("(Battlefield) Tapped state is not equal (" + cardName + ")", tapped, found.isTapped());
|
Assert.assertEquals("(Battlefield) Tapped state is not equal (" + cardName + ")", tapped, found.isTapped());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert whether X permanents of the same name are tapped or not.
|
||||||
|
*
|
||||||
|
* @param cardName Name of the permanent that should be checked.
|
||||||
|
* @param tapped Whether the permanent is tapped or not
|
||||||
|
* @param count The amount of this permanents that should be tapped
|
||||||
|
*/
|
||||||
|
public void assertTappedCount(String cardName, boolean tapped, int count) throws AssertionError {
|
||||||
|
int tappedAmount = 0;
|
||||||
|
Permanent found = null;
|
||||||
|
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents()) {
|
||||||
|
if (permanent.getName().equals(cardName)) {
|
||||||
|
if(permanent.isTapped() == tapped) {
|
||||||
|
tappedAmount++;
|
||||||
|
}
|
||||||
|
found = permanent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.assertNotNull("There is no such permanent on the battlefield, cardName=" + cardName, found);
|
||||||
|
Assert.assertEquals("(Battlefield) " + count + " permanents (" + cardName + ") are not " + ((tapped) ? "" : "un") + "tapped.", count, tappedAmount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assert whether a permanent is attacking or not
|
* Assert whether a permanent is attacking or not
|
||||||
*
|
*
|
||||||
|
|
|
@ -66,6 +66,22 @@ public class ManaUtilTest extends CardTestPlayerBase {
|
||||||
testManaToPayVsLand("{W/R}{R/G}", "Sacred Foundry", 2, 2); // can't auto choose to pay
|
testManaToPayVsLand("{W/R}{R/G}", "Sacred Foundry", 2, 2); // can't auto choose to pay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testManaCondensing() {
|
||||||
|
Assert.assertEquals("{5}{W}", ManaUtil.condenseManaCostString(("{1}{1}{1}{2}{W}")));
|
||||||
|
Assert.assertEquals("{4}{B}{B}", ManaUtil.condenseManaCostString("{2}{B}{2}{B}"));
|
||||||
|
Assert.assertEquals("{6}{R}{R}{R}{U}", ManaUtil.condenseManaCostString("{R}{1}{R}{2}{R}{3}{U}"));
|
||||||
|
Assert.assertEquals("{5}{B}{U}{W}", ManaUtil.condenseManaCostString("{1}{B}{W}{4}{U}"));
|
||||||
|
Assert.assertEquals("{8}{B}{G}{G}{U}", ManaUtil.condenseManaCostString("{1}{G}{1}{2}{3}{G}{B}{U}{1}"));
|
||||||
|
Assert.assertEquals("{3}{R}{U}", ManaUtil.condenseManaCostString("{3}{R}{U}"));
|
||||||
|
Assert.assertEquals("{10}", ManaUtil.condenseManaCostString("{1}{2}{3}{4}"));
|
||||||
|
Assert.assertEquals("{B}{G}{R}{U}{W}", ManaUtil.condenseManaCostString("{B}{G}{R}{U}{W}"));
|
||||||
|
Assert.assertEquals("{R}{R}", ManaUtil.condenseManaCostString("{R}{R}"));
|
||||||
|
Assert.assertEquals("{U}", ManaUtil.condenseManaCostString("{U}"));
|
||||||
|
Assert.assertEquals("{2}", ManaUtil.condenseManaCostString("{2}"));
|
||||||
|
Assert.assertEquals("", ManaUtil.condenseManaCostString(""));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common way to test ManaUtil.tryToAutoPay
|
* Common way to test ManaUtil.tryToAutoPay
|
||||||
*
|
*
|
||||||
|
@ -133,4 +149,5 @@ public class ManaUtilTest extends CardTestPlayerBase {
|
||||||
}
|
}
|
||||||
return useableAbilities;
|
return useableAbilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import mage.constants.Outcome;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
|
import mage.util.ManaUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -50,7 +51,7 @@ public class StriveAbility extends SimpleStaticAbility {
|
||||||
private final String striveCost;
|
private final String striveCost;
|
||||||
|
|
||||||
public StriveAbility(String manaString) {
|
public StriveAbility(String manaString) {
|
||||||
super(Zone.STACK, new StriveCostIncreasementEffect(new ManaCostsImpl(manaString)));
|
super(Zone.STACK, new StriveCostIncreasingEffect(new ManaCostsImpl(manaString)));
|
||||||
setRuleAtTheTop(true);
|
setRuleAtTheTop(true);
|
||||||
this.striveCost = manaString;
|
this.striveCost = manaString;
|
||||||
}
|
}
|
||||||
|
@ -71,29 +72,32 @@ public class StriveAbility extends SimpleStaticAbility {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StriveCostIncreasementEffect extends CostModificationEffectImpl {
|
class StriveCostIncreasingEffect extends CostModificationEffectImpl {
|
||||||
|
|
||||||
private ManaCostsImpl striveCosts = null;
|
private ManaCostsImpl striveCosts = null;
|
||||||
|
|
||||||
public StriveCostIncreasementEffect(ManaCostsImpl striveCosts) {
|
public StriveCostIncreasingEffect(ManaCostsImpl striveCosts) {
|
||||||
super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.INCREASE_COST);
|
super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.INCREASE_COST);
|
||||||
this.striveCosts = striveCosts;
|
this.striveCosts = striveCosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected StriveCostIncreasementEffect(StriveCostIncreasementEffect effect) {
|
protected StriveCostIncreasingEffect(StriveCostIncreasingEffect effect) {
|
||||||
super(effect);
|
super(effect);
|
||||||
this.striveCosts = effect.striveCosts;
|
this.striveCosts = effect.striveCosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||||
// Target target = abilityToModify.getTargets().get(0);
|
|
||||||
for (Target target : abilityToModify.getTargets()) {
|
for (Target target : abilityToModify.getTargets()) {
|
||||||
if (target.getMaxNumberOfTargets() == Integer.MAX_VALUE) {
|
if (target.getMaxNumberOfTargets() == Integer.MAX_VALUE) {
|
||||||
int additionalTargets = target.getTargets().size() - 1;
|
int additionalTargets = target.getTargets().size() - 1;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
for (int i = 0; i < additionalTargets; i++) {
|
for (int i = 0; i < additionalTargets; i++) {
|
||||||
abilityToModify.getManaCostsToPay().add(striveCosts.copy());
|
// Build up a string of strive costs for each target
|
||||||
|
sb.append(striveCosts.getText());
|
||||||
}
|
}
|
||||||
|
String finalCost = ManaUtil.condenseManaCostString(sb.toString());
|
||||||
|
abilityToModify.getManaCostsToPay().add(new ManaCostsImpl(finalCost));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +110,7 @@ class StriveCostIncreasementEffect extends CostModificationEffectImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StriveCostIncreasementEffect copy() {
|
public StriveCostIncreasingEffect copy() {
|
||||||
return new StriveCostIncreasementEffect(this);
|
return new StriveCostIncreasingEffect(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package mage.util;
|
package mage.util;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.Mana;
|
import mage.Mana;
|
||||||
import mage.ManaSymbol;
|
import mage.ManaSymbol;
|
||||||
|
@ -379,4 +377,46 @@ public class ManaUtil {
|
||||||
return unpaid.getText();
|
return unpaid.getText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Converts a collection of mana symbols into a single condensed string
|
||||||
|
* e.g.
|
||||||
|
* {1}{1}{1}{1}{1}{W} = {5}{W}
|
||||||
|
* {2}{B}{2}{B}{2}{B} = {6}{B}{B}{B}
|
||||||
|
* {1}{2}{R}{U}{1}{1} = {5}{R}{U}
|
||||||
|
* {B}{G}{R} = {B}{G}{R}
|
||||||
|
* */
|
||||||
|
public static String condenseManaCostString(String rawCost) {
|
||||||
|
int total = 0;
|
||||||
|
int index = 0;
|
||||||
|
// Split the string in to an array of numbers and colored mana symbols
|
||||||
|
String[] splitCost = rawCost.replace("{", "").replace("}", " ").split(" ");
|
||||||
|
// Sort alphabetically which will push1 the numbers to the front before the colored mana symbols
|
||||||
|
Arrays.sort(splitCost);
|
||||||
|
for (String c : splitCost) {
|
||||||
|
// If the string is a representation of a number
|
||||||
|
if(c.matches("\\d+")) {
|
||||||
|
total += Integer.parseInt(c);
|
||||||
|
} else {
|
||||||
|
// First non-number we see we can finish as they are sorted
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
int splitCostLength = splitCost.length;
|
||||||
|
// No need to add {total} to the mana cost if total == 0
|
||||||
|
int shift = (total > 0) ? 1 : 0;
|
||||||
|
String [] finalCost = new String[shift + splitCostLength - index];
|
||||||
|
// Account for no colourless mana symbols seen
|
||||||
|
if(total > 0) {
|
||||||
|
finalCost[0] = String.valueOf(total);
|
||||||
|
}
|
||||||
|
System.arraycopy(splitCost, index, finalCost, shift, splitCostLength - index);
|
||||||
|
// Combine the cost back as a mana string
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for(String s: finalCost) {
|
||||||
|
sb.append("{" + s + "}");
|
||||||
|
}
|
||||||
|
// Return the condensed string
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue