Merge branch 'manaReplacementEffect' into 'master'

AbilityMana: better ReplacementEffect logic

Closes #1266

See merge request core-developers/forge!2650
This commit is contained in:
Michael Kamensky
2021-02-12 08:50:24 +00:00
90 changed files with 1163 additions and 878 deletions

View File

@@ -429,8 +429,7 @@ public class AiController {
byte color = MagicColor.fromName(c); byte color = MagicColor.fromName(c);
for (Card land : landList) { for (Card land : landList) {
for (final SpellAbility m : ComputerUtilMana.getAIPlayableMana(land)) { for (final SpellAbility m : ComputerUtilMana.getAIPlayableMana(land)) {
AbilityManaPart mp = m.getManaPart(); if (m.canProduce(MagicColor.toShortString(color))) {
if (mp.canProduce(MagicColor.toShortString(color), m)) {
return land; return land;
} }
} }
@@ -483,8 +482,7 @@ public class AiController {
return land; return land;
} }
for (final SpellAbility m : ComputerUtilMana.getAIPlayableMana(land)) { for (final SpellAbility m : ComputerUtilMana.getAIPlayableMana(land)) {
AbilityManaPart mp = m.getManaPart(); if (m.canProduce(MagicColor.toShortString(color))) {
if (mp.canProduce(MagicColor.toShortString(color), m)) {
return land; return land;
} }
} }

View File

@@ -2075,9 +2075,8 @@ public class ComputerUtil {
for (Card c : lands) { for (Card c : lands) {
for (SpellAbility sa : c.getManaAbilities()) { for (SpellAbility sa : c.getManaAbilities()) {
AbilityManaPart abmana = sa.getManaPart();
for (byte col : MagicColor.WUBRG) { for (byte col : MagicColor.WUBRG) {
if (abmana.canProduce(MagicColor.toLongString(col))) { if (sa.canProduce(MagicColor.toLongString(col))) {
numProducers.get(col).add(c); numProducers.get(col).add(c);
} }
} }

View File

@@ -10,11 +10,10 @@ import forge.card.mana.ManaAtom;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser; import forge.card.mana.ManaCostParser;
import forge.card.mana.ManaCostShard; import forge.card.mana.ManaCostShard;
import forge.game.CardTraitPredicates;
import forge.game.Game; import forge.game.Game;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.ability.AbilityKey; import forge.game.ability.*;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.*; import forge.game.card.*;
import forge.game.combat.CombatUtil; import forge.game.combat.CombatUtil;
import forge.game.cost.*; import forge.game.cost.*;
@@ -25,10 +24,13 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerPredicates; import forge.game.player.PlayerPredicates;
import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementLayer;
import forge.game.replacement.ReplacementType; import forge.game.replacement.ReplacementType;
import forge.game.spellability.AbilityManaPart; import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.AbilitySub; import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.MyRandom; import forge.util.MyRandom;
import forge.util.TextUtil; import forge.util.TextUtil;
@@ -317,6 +319,190 @@ public class ComputerUtilMana {
return null; return null;
} }
public static String predictManaReplacement(SpellAbility saPayment, Player ai, ManaCostShard toPay) {
Card hostCard = saPayment.getHostCard();
Game game = hostCard.getGame();
String manaProduced = toPay.isSnow() && hostCard.isSnow() ? "S" : GameActionUtil.generatedTotalMana(saPayment);
//String originalProduced = manaProduced;
final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
repParams.put(AbilityKey.Mana, manaProduced);
repParams.put(AbilityKey.Affected, hostCard);
repParams.put(AbilityKey.Player, ai);
repParams.put(AbilityKey.AbilityMana, saPayment); // RootAbility
// TODO Damping Sphere might replace later?
// add flags to replacementEffects to filter better?
List<ReplacementEffect> reList = game.getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other);
List<SpellAbility> replaceMana = Lists.newArrayList();
List<SpellAbility> replaceType = Lists.newArrayList();
List<SpellAbility> replaceAmount = Lists.newArrayList(); // currently only multi
// try to guess the color the mana gets replaced to
for (ReplacementEffect re : reList) {
SpellAbility o = re.getOverridingAbility();
if (o == null || o.getApi() != ApiType.ReplaceMana) {
continue;
}
// this one does replace the amount too
if (o.hasParam("ReplaceMana")) {
replaceMana.add(o);
} else if (o.hasParam("ReplaceType") || o.hasParam("ReplaceColor")) {
// this one replaces the color/type
// check if this one can be replaced into wanted mana shard
replaceType.add(o);
} else if (o.hasParam("ReplaceAmount")) {
replaceAmount.add(o);
}
}
// it is better to apply these ones first
if (!replaceMana.isEmpty()) {
for (SpellAbility saMana : replaceMana) {
// one of then has to Any
// one of then has to C
// one of then has to B
String m = saMana.getParam("ReplaceMana");
if ("Any".equals(m)) {
byte rs = MagicColor.GREEN;
for (byte c : MagicColor.WUBRGC) {
if (toPay.canBePaidWithManaOfColor(c)) {
rs = c;
break;
}
}
manaProduced = MagicColor.toShortString(rs);
} else {
manaProduced = m;
}
}
}
// then apply this one
if (!replaceType.isEmpty()) {
for (SpellAbility saMana : replaceAmount) {
Card card = saMana.getHostCard();
if (saMana.hasParam("ReplaceType")) {
// replace color and colorless
String color = saMana.getParam("ReplaceType");
if ("Any".equals(color)) {
byte rs = MagicColor.GREEN;
for (byte c : MagicColor.WUBRGC) {
if (toPay.canBePaidWithManaOfColor(c)) {
rs = c;
break;
}
}
color = MagicColor.toShortString(rs);
}
for (byte c : MagicColor.WUBRGC) {
String s = MagicColor.toShortString(c);
manaProduced = manaProduced.replace(s, color);
}
} else if (saMana.hasParam("ReplaceColor")) {
// replace color
String color = saMana.getParam("ReplaceColor");
if ("Chosen".equals(color)) {
if (card.hasChosenColor()) {
color = MagicColor.toShortString(card.getChosenColor());
}
}
if (saMana.hasParam("ReplaceOnly")) {
manaProduced = manaProduced.replace(saMana.getParam("ReplaceOnly"), color);
} else {
for (byte c : MagicColor.WUBRG) {
String s = MagicColor.toShortString(c);
manaProduced = manaProduced.replace(s, color);
}
}
}
}
}
// then multiply if able
if (!replaceAmount.isEmpty()) {
int totalAmount = 1;
for (SpellAbility saMana : replaceAmount) {
totalAmount *= Integer.valueOf(saMana.getParam("ReplaceAmount"));
}
manaProduced = StringUtils.repeat(manaProduced, " ", totalAmount);
}
return manaProduced;
}
public static String predictManafromSpellAbility(SpellAbility saPayment, Player ai, ManaCostShard toPay) {
Card hostCard = saPayment.getHostCard();
String manaProduced = predictManaReplacement(saPayment, ai, toPay);
String originalProduced = manaProduced;
if (originalProduced.isEmpty()) {
return manaProduced;
}
// Run triggers like Nissa
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(hostCard);
runParams.put(AbilityKey.Player, ai); // assuming AI would only ever gives itself mana
runParams.put(AbilityKey.AbilityMana, saPayment);
runParams.put(AbilityKey.Produced, manaProduced);
runParams.put(AbilityKey.Activator, ai);
for (Trigger tr : ai.getGame().getTriggerHandler().getActiveTrigger(TriggerType.TapsForMana, runParams)) {
SpellAbility trSA = tr.ensureAbility();
if (trSA == null) {
continue;
}
if (ApiType.Mana.equals(trSA.getApi())) {
int pAmount = trSA.hasParam("Amount") ? Integer.valueOf(trSA.getParam("Amount")) : 1;
String produced = trSA.getParam("Produced");
if (produced.equals("Chosen")) {
produced = MagicColor.toShortString(trSA.getHostCard().getChosenColor());
}
manaProduced += " " + StringUtils.repeat(produced, pAmount);
} else if (ApiType.ManaReflected.equals(trSA.getApi())) {
final String colorOrType = trSA.getParamOrDefault("ColorOrType", "Color");
// currently Color or Type, Type is colors + colorless
final String reflectProperty = trSA.getParam("ReflectProperty");
if (reflectProperty.equals("Produced") && !originalProduced.isEmpty()) {
// check if a colorless shard can be paid from the trigger
if (toPay.equals(ManaCostShard.COLORLESS) && colorOrType.equals("Type") && originalProduced.contains("C")) {
manaProduced += " " + "C";
} else if (originalProduced.length() == 1) {
// if length is only one, and it either is equal C == Type
if (colorOrType.equals("Type") || !originalProduced.equals("C")) {
manaProduced += " " + originalProduced;
}
} else {
// should it look for other shards too?
boolean found = false;
for (String s : originalProduced.split(" ")) {
if (colorOrType.equals("Type") || !s.equals("C") && toPay.canBePaidWithManaOfColor(MagicColor.fromName(s))) {
found = true;
manaProduced += " " + s;
break;
}
}
// no good mana found? just add the first generated color
if (!found) {
for (String s : originalProduced.split(" ")) {
if (colorOrType.equals("Type") || !s.equals("C")) {
manaProduced += " " + s;
break;
}
}
}
}
}
}
}
return manaProduced;
}
public static CardCollection getManaSourcesToPayCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) { public static CardCollection getManaSourcesToPayCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) {
CardCollection manaSources = new CardCollection(); CardCollection manaSources = new CardCollection();
@@ -392,23 +578,13 @@ public class ComputerUtilMana {
manaSources.add(saPayment.getHostCard()); manaSources.add(saPayment.getHostCard());
setExpressColorChoice(sa, ai, cost, toPay, saPayment); setExpressColorChoice(sa, ai, cost, toPay, saPayment);
String manaProduced = toPay.isSnow() ? "S" : GameActionUtil.generatedMana(saPayment); String manaProduced = predictManafromSpellAbility(saPayment, ai, toPay);
manaProduced = AbilityManaPart.applyManaReplacement(saPayment, manaProduced);
//System.out.println(manaProduced); //System.out.println(manaProduced);
payMultipleMana(cost, manaProduced, ai); payMultipleMana(cost, manaProduced, ai);
// remove from available lists // remove from available lists
/* Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
* Refactoring this code to sourcesForShards.values().removeIf((SpellAbility srcSa) -> srcSa.getHostCard().equals(saPayment.getHostCard()));
* causes Android build not to compile
* */
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
while (itSa.hasNext()) {
SpellAbility srcSa = itSa.next();
if (srcSa.getHostCard().equals(saPayment.getHostCard())) {
itSa.remove();
}
}
} }
handleOfferingsAI(sa, true, cost.isPaid()); handleOfferingsAI(sa, true, cost.isPaid());
@@ -443,6 +619,21 @@ public class ComputerUtilMana {
// Loop over mana needed // Loop over mana needed
while (!cost.isPaid()) { while (!cost.isPaid()) {
while (!cost.isPaid() && !manapool.isEmpty()) {
boolean found = false;
for (byte color : MagicColor.WUBRGC) {
if (manapool.tryPayCostWithColor(color, sa, cost)) {
found = true;
break;
}
}
if (!found) {
break;
}
}
if (cost.isPaid()) {
break;
}
toPay = getNextShardToPay(cost); toPay = getNextShardToPay(cost);
boolean lifeInsteadOfBlack = toPay.isBlack() && ai.hasKeyword("PayLifeInsteadOf:B"); boolean lifeInsteadOfBlack = toPay.isBlack() && ai.hasKeyword("PayLifeInsteadOf:B");
@@ -451,7 +642,8 @@ public class ComputerUtilMana {
if (hasConverge && if (hasConverge &&
(toPay == ManaCostShard.GENERIC || toPay == ManaCostShard.X)) { (toPay == ManaCostShard.GENERIC || toPay == ManaCostShard.X)) {
final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION; final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION;
for (final byte b : ColorSet.fromMask(unpaidColors)) { // try and pay other colors for converge for (final byte b : ColorSet.fromMask(unpaidColors)) {
// try and pay other colors for converge
final ManaCostShard shard = ManaCostShard.valueOf(b); final ManaCostShard shard = ManaCostShard.valueOf(b);
saList = sourcesForShards.get(shard); saList = sourcesForShards.get(shard);
if (saList != null && !saList.isEmpty()) { if (saList != null && !saList.isEmpty()) {
@@ -459,7 +651,8 @@ public class ComputerUtilMana {
break; break;
} }
} }
if (saList == null || saList.isEmpty()) { // failed to converge, revert to paying generic if (saList == null || saList.isEmpty()) {
// failed to converge, revert to paying generic
saList = sourcesForShards.get(toPay); saList = sourcesForShards.get(toPay);
hasConverge = false; hasConverge = false;
} }
@@ -539,23 +732,13 @@ public class ComputerUtilMana {
} }
} }
String manaProduced = toPay.isSnow() ? "S" : GameActionUtil.generatedMana(saPayment); String manaProduced = predictManafromSpellAbility(saPayment, ai, toPay);
manaProduced = AbilityManaPart.applyManaReplacement(saPayment, manaProduced);
//System.out.println(manaProduced); // System.out.println(manaProduced);
payMultipleMana(cost, manaProduced, ai); payMultipleMana(cost, manaProduced, ai);
// remove from available lists // remove from available lists
/* Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
* Refactoring this code to sourcesForShards.values().removeIf((SpellAbility srcSa) -> srcSa.getHostCard().equals(saPayment.getHostCard()));
* causes Android build not to compile
* */
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
while (itSa.hasNext()) {
SpellAbility srcSa = itSa.next();
if (srcSa.getHostCard().equals(saPayment.getHostCard())) {
itSa.remove();
}
}
} }
else { else {
final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment); final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment);
@@ -571,19 +754,9 @@ public class ComputerUtilMana {
// no need to remove abilities from resource map, // no need to remove abilities from resource map,
// once their costs are paid and consume resources, they can not be used again // once their costs are paid and consume resources, they can not be used again
if (hasConverge) { // hack to prevent converge re-using sources if (hasConverge) {
// remove from available lists // hack to prevent converge re-using sources
/* Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
* Refactoring this code to sourcesForShards.values().removeIf((SpellAbility srcSa) -> srcSa.getHostCard().equals(saPayment.getHostCard()));
* causes Android build not to compile
* */
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
while (itSa.hasNext()) {
SpellAbility srcSa = itSa.next();
if (srcSa.getHostCard().equals(saPayment.getHostCard())) {
itSa.remove();
}
}
} }
} }
} }
@@ -596,15 +769,6 @@ public class ComputerUtilMana {
// extraMana, sa.getHostCard(), sa.toUnsuppressedString(), StringUtils.join(paymentPlan, "\n\t")); // extraMana, sa.getHostCard(), sa.toUnsuppressedString(), StringUtils.join(paymentPlan, "\n\t"));
// } // }
// See if it's possible to pay with something that was left in the mana pool in corner cases,
// e.g. Gemstone Caverns with a Luck counter on it generating colored mana (which fails to be
// processed correctly on a per-ability basis, leaving floating mana in the pool)
if (!cost.isPaid() && !manapool.isEmpty()) {
for (byte color : MagicColor.WUBRGC) {
manapool.tryPayCostWithColor(color, sa, cost);
}
}
// The cost is still unpaid, so refund the mana and report // The cost is still unpaid, so refund the mana and report
if (!cost.isPaid()) { if (!cost.isPaid()) {
refundMana(manaSpentToPay, ai, sa); refundMana(manaSpentToPay, ai, sa);
@@ -641,7 +805,8 @@ public class ComputerUtilMana {
List<Mana> manaSpentToPay, final boolean hasConverge) { List<Mana> manaSpentToPay, final boolean hasConverge) {
// arrange all mana abilities by color produced. // arrange all mana abilities by color produced.
final ListMultimap<Integer, SpellAbility> manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, checkPlayable); final ListMultimap<Integer, SpellAbility> manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, checkPlayable);
if (manaAbilityMap.isEmpty()) { // no mana abilities, bailing out if (manaAbilityMap.isEmpty()) {
// no mana abilities, bailing out
refundMana(manaSpentToPay, ai, sa); refundMana(manaSpentToPay, ai, sa);
handleOfferingsAI(sa, test, cost.isPaid()); handleOfferingsAI(sa, test, cost.isPaid());
return null; return null;
@@ -652,14 +817,15 @@ public class ComputerUtilMana {
// select which abilities may be used for each shard // select which abilities may be used for each shard
ListMultimap<ManaCostShard, SpellAbility> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost); ListMultimap<ManaCostShard, SpellAbility> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost);
if (hasConverge) { // add extra colors for paying converge if (hasConverge) {
// add extra colors for paying converge
final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION; final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION;
for (final byte b : ColorSet.fromMask(unpaidColors)) { for (final byte b : ColorSet.fromMask(unpaidColors)) {
final ManaCostShard shard = ManaCostShard.valueOf(b); final ManaCostShard shard = ManaCostShard.valueOf(b);
if (!sourcesForShards.containsKey(shard)) { if (!sourcesForShards.containsKey(shard)) {
if (ai.getManaPool().canPayForShardWithColor(shard, b)) { if (ai.getManaPool().canPayForShardWithColor(shard, b)) {
for (SpellAbility saMana : manaAbilityMap.get((int)b)) { for (SpellAbility saMana : manaAbilityMap.get((int)b)) {
sourcesForShards.get(shard).add(sourcesForShards.get(shard).size(), saMana); sourcesForShards.get(shard).add(saMana);
} }
} }
} }
@@ -1026,7 +1192,7 @@ public class ComputerUtilMana {
choice = abMana.getExpressChoice(); choice = abMana.getExpressChoice();
abMana.clearExpressChoice(); abMana.clearExpressChoice();
byte colorMask = ManaAtom.fromName(choice); byte colorMask = ManaAtom.fromName(choice);
if (abMana.canProduce(choice, manaAb) && testCost.isAnyPartPayableWith(colorMask, ai.getManaPool())) { if (manaAb.canProduce(choice) && testCost.isAnyPartPayableWith(colorMask, ai.getManaPool())) {
choiceString.append(choice); choiceString.append(choice);
payMultipleMana(testCost, choice, ai); payMultipleMana(testCost, choice, ai);
continue; continue;
@@ -1387,20 +1553,6 @@ public class ComputerUtilMana {
final ListMultimap<Integer, SpellAbility> manaMap = ArrayListMultimap.create(); final ListMultimap<Integer, SpellAbility> manaMap = ArrayListMultimap.create();
final Game game = ai.getGame(); final Game game = ai.getGame();
List<ReplacementEffect> replacementEffects = new ArrayList<>();
for (final Player p : game.getPlayers()) {
for (final Card crd : p.getAllCards()) {
for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) {
if (replacementEffect.requirementsCheck(game)
&& replacementEffect.getMode() == ReplacementType.ProduceMana
&& replacementEffect.hasParam("ManaReplacement")
&& replacementEffect.zonesCheck(game.getZoneOf(crd))) {
replacementEffects.add(replacementEffect);
}
}
}
}
// Loop over all current available mana sources // Loop over all current available mana sources
for (final Card sourceCard : getAvailableManaSources(ai, checkPlayable)) { for (final Card sourceCard : getAvailableManaSources(ai, checkPlayable)) {
if (DEBUG_MANA_PAYMENT) { if (DEBUG_MANA_PAYMENT) {
@@ -1430,48 +1582,80 @@ public class ComputerUtilMana {
} }
manaMap.get(ManaAtom.GENERIC).add(m); // add to generic source list manaMap.get(ManaAtom.GENERIC).add(m); // add to generic source list
SpellAbility tail = m;
while (tail != null) {
AbilityManaPart mp = m.getManaPart(); AbilityManaPart mp = m.getManaPart();
if (mp != null && tail.metConditions()) {
// TODO Replacement Check currently doesn't work for reflected colors
// setup produce mana replacement effects // setup produce mana replacement effects
String origin = mp.getOrigProduced();
final Map<AbilityKey, Object> repParams = AbilityKey.newMap(); final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
repParams.put(AbilityKey.Mana, mp.getOrigProduced()); repParams.put(AbilityKey.Mana, origin);
repParams.put(AbilityKey.Affected, sourceCard); repParams.put(AbilityKey.Affected, sourceCard);
repParams.put(AbilityKey.Player, ai); repParams.put(AbilityKey.Player, ai);
repParams.put(AbilityKey.AbilityMana, m); repParams.put(AbilityKey.AbilityMana, m); // RootAbility
for (final ReplacementEffect replacementEffect : replacementEffects) { List<ReplacementEffect> reList = game.getReplacementHandler().getReplacementList(ReplacementType.ProduceMana, repParams, ReplacementLayer.Other);
if (replacementEffect.canReplace(repParams)) {
Card crd = replacementEffect.getHostCard();
String repType = crd.getSVar(replacementEffect.getParam("ManaReplacement"));
if (repType.contains("Chosen")) {
repType = TextUtil.fastReplace(repType, "Chosen", MagicColor.toShortString(crd.getChosenColor()));
}
mp.setManaReplaceType(repType);
}
}
if (reList.isEmpty()) {
Set<String> reflectedColors = CardUtil.getReflectableManaColors(m); Set<String> reflectedColors = CardUtil.getReflectableManaColors(m);
// find possible colors // find possible colors
if (mp.canProduce("W", m) || reflectedColors.contains(MagicColor.Constant.WHITE)) { for (byte color : MagicColor.WUBRG) {
manaMap.get(ManaAtom.WHITE).add(m); if (tail.canThisProduce(MagicColor.toShortString(color)) || reflectedColors.contains(MagicColor.toLongString(color))) {
manaMap.put((int)color, m);
} }
if (mp.canProduce("U", m) || reflectedColors.contains(MagicColor.Constant.BLUE)) {
manaMap.get(ManaAtom.BLUE).add(m);
} }
if (mp.canProduce("B", m) || reflectedColors.contains(MagicColor.Constant.BLACK)) { if (m.canThisProduce("C") || reflectedColors.contains(MagicColor.Constant.COLORLESS)) {
manaMap.get(ManaAtom.BLACK).add(m); manaMap.put(ManaAtom.COLORLESS, m);
} }
if (mp.canProduce("R", m) || reflectedColors.contains(MagicColor.Constant.RED)) { } else {
manaMap.get(ManaAtom.RED).add(m); // try to guess the color the mana gets replaced to
for (ReplacementEffect re : reList) {
SpellAbility o = re.getOverridingAbility();
String replaced = origin;
if (o == null || o.getApi() != ApiType.ReplaceMana) {
continue;
} }
if (mp.canProduce("G", m) || reflectedColors.contains(MagicColor.Constant.GREEN)) { if (o.hasParam("ReplaceMana")) {
manaMap.get(ManaAtom.GREEN).add(m); replaced = o.getParam("ReplaceMana");
} else if (o.hasParam("ReplaceType")) {
String color = o.getParam("ReplaceType");
for (byte c : MagicColor.WUBRGC) {
String s = MagicColor.toShortString(c);
replaced = replaced.replace(s, color);
} }
if (mp.canProduce("C", m) || reflectedColors.contains(MagicColor.Constant.COLORLESS)) { } else if (o.hasParam("ReplaceColor")) {
manaMap.get(ManaAtom.COLORLESS).add(m); String color = o.getParam("ReplaceColor");
if (o.hasParam("ReplaceOnly")) {
replaced = replaced.replace(o.getParam("ReplaceOnly"), color);
} else {
for (byte c : MagicColor.WUBRG) {
String s = MagicColor.toShortString(c);
replaced = replaced.replace(s, color);
} }
if (mp.isSnow()) { }
manaMap.get(ManaAtom.IS_SNOW).add(m); }
for (byte color : MagicColor.WUBRG) {
if ("Any".equals(replaced) || replaced.contains(MagicColor.toShortString(color))) {
manaMap.put((int)color, m);
}
}
if (replaced.contains("C")) {
manaMap.put(ManaAtom.COLORLESS, m);
}
}
}
}
tail = tail.getSubAbility();
}
if (m.getHostCard().isSnow()) {
manaMap.put(ManaAtom.IS_SNOW, m);
} }
if (DEBUG_MANA_PAYMENT) { if (DEBUG_MANA_PAYMENT) {
System.out.println("DEBUG_MANA_PAYMENT: groupSourcesByManaColor manaMap = " + manaMap); System.out.println("DEBUG_MANA_PAYMENT: groupSourcesByManaColor manaMap = " + manaMap);

View File

@@ -833,6 +833,9 @@ public class PlayerControllerAi extends PlayerController {
@Override @Override
public byte chooseColor(String message, SpellAbility sa, ColorSet colors) { public byte chooseColor(String message, SpellAbility sa, ColorSet colors) {
if (colors.countColors() < 2) {
return Iterables.getFirst(colors, MagicColor.WHITE);
}
// You may switch on sa.getApi() here and use sa.getParam("AILogic") // You may switch on sa.getApi() here and use sa.getParam("AILogic")
CardCollectionView hand = player.getCardsIn(ZoneType.Hand); CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
if (sa.getApi() == ApiType.Mana) { if (sa.getApi() == ApiType.Mana) {

View File

@@ -111,7 +111,7 @@ public abstract class SpellAbilityAi {
if (aiLogic.equals("CheckCondition")) { if (aiLogic.equals("CheckCondition")) {
SpellAbility saCopy = sa.copy(); SpellAbility saCopy = sa.copy();
saCopy.setActivatingPlayer(ai); saCopy.setActivatingPlayer(ai);
return saCopy.getConditions().areMet(saCopy); return saCopy.metConditions();
} }
return !("Never".equals(aiLogic)); return !("Never".equals(aiLogic));

View File

@@ -122,7 +122,7 @@ public class AnimateAi extends SpellAbilityAi {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final Game game = aiPlayer.getGame(); final Game game = aiPlayer.getGame();
final PhaseHandler ph = game.getPhaseHandler(); final PhaseHandler ph = game.getPhaseHandler();
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) { if (!sa.metConditions() && sa.getSubAbility() == null) {
return false; // what is this for? return false; // what is this for?
} }
if (!game.getStack().isEmpty() && game.getStack().peekAbility().getApi() == ApiType.Sacrifice) { if (!game.getStack().isEmpty() && game.getStack().peekAbility().getApi() == ApiType.Sacrifice) {

View File

@@ -304,10 +304,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
// don't play if the conditions aren't met, unless it would trigger a beneficial sub-condition // don't play if the conditions aren't met, unless it would trigger a beneficial sub-condition
if (!activateForCost && !sa.getConditions().areMet(sa)) { if (!activateForCost && !sa.metConditions()) {
final AbilitySub abSub = sa.getSubAbility(); final AbilitySub abSub = sa.getSubAbility();
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) { if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
if (!abSub.getConditions().areMet(abSub)) { if (!abSub.metConditions()) {
return false; return false;
} }
} else { } else {

View File

@@ -290,7 +290,7 @@ public class CountersPutAi extends SpellAbilityAi {
return doMoveCounterLogic(ai, sa, ph); return doMoveCounterLogic(ai, sa, ph);
} }
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) { if (!sa.metConditions() && sa.getSubAbility() == null) {
return false; return false;
} }

View File

@@ -146,7 +146,7 @@ public class LifeGainAi extends SpellAbilityAi {
} }
// don't play if the conditions aren't met, unless it would trigger a // don't play if the conditions aren't met, unless it would trigger a
// beneficial sub-condition // beneficial sub-condition
if (!activateForCost && !sa.getConditions().areMet(sa)) { if (!activateForCost && !sa.metConditions()) {
final AbilitySub abSub = sa.getSubAbility(); final AbilitySub abSub = sa.getSubAbility();
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) { if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
if (!abSub.getConditions().areMet(abSub)) { if (!abSub.getConditions().areMet(abSub)) {

View File

@@ -279,7 +279,7 @@ public class PermanentAi extends SpellAbilityAi {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final Cost cost = sa.getPayCosts(); final Cost cost = sa.getPayCosts();
if (sa.getConditions() != null && !sa.getConditions().areMet(sa)) { if (!sa.metConditions()) {
return false; return false;
} }

View File

@@ -2,8 +2,19 @@ package forge.game;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import forge.game.card.Card;
public class CardTraitPredicates { public class CardTraitPredicates {
public static final Predicate<CardTraitBase> isHostCard(final Card host) {
return new Predicate<CardTraitBase>() {
@Override
public boolean apply(final CardTraitBase sa) {
return host.equals(sa.getHostCard());
}
};
}
public static final Predicate<CardTraitBase> hasParam(final String name) { public static final Predicate<CardTraitBase> hasParam(final String name) {
return new Predicate<CardTraitBase>() { return new Predicate<CardTraitBase>() {
@Override @Override

View File

@@ -5,6 +5,7 @@ import forge.card.MagicColor;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardState; import forge.game.card.CardState;
import forge.game.cost.Cost;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
@@ -131,6 +132,9 @@ public class ForgeScript {
return !sa.isManaAbility(); return !sa.isManaAbility();
} else if (property.equals("withoutXCost")) { } else if (property.equals("withoutXCost")) {
return !sa.costHasManaX(); return !sa.costHasManaX();
} else if (property.equals("hasTapCost")) {
Cost cost = sa.getPayCosts();
return cost != null && cost.hasTapCost();
} else if (property.equals("Buyback")) { } else if (property.equals("Buyback")) {
return sa.isBuyBackAbility(); return sa.isBuyBackAbility();
} else if (property.equals("Cycling")) { } else if (property.equals("Cycling")) {

View File

@@ -17,7 +17,6 @@
*/ */
package forge.game; package forge.game;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@@ -252,6 +251,18 @@ public final class GameActionUtil {
} }
} }
if (sa.isManaAbility() && sa.isActivatedAbility() && activator.hasKeyword("Piracy") && source.isLand() && source.isInPlay() && !activator.equals(source.getController()) && sa.getPayCosts().hasTapCost()) {
SpellAbility newSA = sa.copy(activator);
// to bypass Activator restriction, set Activator to Player
sa.getRestrictions().setActivator("Player");
// extra Mana restriction to only Spells
for (AbilityManaPart mp : newSA.getAllManaParts()) {
mp.setExtraManaRestriction("Spell");
}
alternatives.add(newSA);
}
// below are for some special cases of activated abilities // below are for some special cases of activated abilities
if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) { if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) {
for (final KeywordInterface inst : source.getKeywords()) { for (final KeywordInterface inst : source.getKeywords()) {
@@ -277,7 +288,6 @@ public final class GameActionUtil {
newSA.setDescription(sb.toString()); newSA.setDescription(sb.toString());
alternatives.add(newSA); alternatives.add(newSA);
break;
} }
} }
@@ -556,44 +566,21 @@ public final class GameActionUtil {
return eff; return eff;
} }
private static boolean hasUrzaLands(final Player p) { public static String generatedTotalMana(final SpellAbility sa) {
final CardCollectionView landsControlled = p.getCardsIn(ZoneType.Battlefield); StringBuilder sb = new StringBuilder();
return Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Mine"))) SpellAbility tail = sa;
&& Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Power-Plant"))) while (tail != null) {
&& Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Tower"))); String value = generatedMana(tail);
if (!value.isEmpty() && !"0".equals(value)) {
sb.append(value).append(" ");
} }
tail = tail.getSubAbility();
public static int amountOfManaGenerated(final SpellAbility sa, boolean multiply) {
// Calculate generated mana here for stack description and resolving
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa) : 1;
AbilityManaPart abMana = sa.getManaPartRecursive();
if (sa.hasParam("Bonus")) {
// For mana abilities that get a bonus
// Bonus currently MULTIPLIES the base amount. Base Amounts should
// ALWAYS be Base
int bonus = 0;
if (sa.getParam("Bonus").equals("UrzaLands")) {
if (hasUrzaLands(sa.getActivatingPlayer())) {
bonus = Integer.parseInt(sa.getParam("BonusProduced"));
} }
return sb.toString().trim();
} }
amount += bonus;
}
if (!multiply || abMana.isAnyMana() || abMana.isComboMana() || abMana.isSpecialMana()) {
return amount;
} else {
// For cards that produce like {C}{R} vs cards that produce {R}{R}.
return abMana.mana().split(" ").length * amount;
}
}
public static String generatedMana(final SpellAbility sa) { public static String generatedMana(final SpellAbility sa) {
int amount = amountOfManaGenerated(sa, false); int amount = sa.amountOfManaGenerated(false);
AbilityManaPart abMana = sa.getManaPart(); AbilityManaPart abMana = sa.getManaPart();
String baseMana; String baseMana;

View File

@@ -77,4 +77,5 @@ public abstract class TriggerReplacementBase extends CardTraitBase implements II
this.overridingAbility = overridingAbility0; this.overridingAbility = overridingAbility0;
} }
abstract public SpellAbility ensureAbility();
} }

View File

@@ -1,9 +1,5 @@
package forge.game.ability; package forge.game.ability;
import forge.game.ability.effects.ChangeZoneAllEffect;
import forge.game.ability.effects.ChangeZoneEffect;
import forge.game.ability.effects.ManaEffect;
import forge.game.ability.effects.ManaReflectedEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.spellability.AbilityActivated; import forge.game.spellability.AbilityActivated;
@@ -15,8 +11,6 @@ import java.util.Map;
public class AbilityApiBased extends AbilityActivated { public class AbilityApiBased extends AbilityActivated {
private final SpellAbilityEffect effect; private final SpellAbilityEffect effect;
private static final long serialVersionUID = -4183793555528531978L;
public AbilityApiBased(ApiType api0, Card sourceCard, Cost abCost, TargetRestrictions tgt, Map<String, String> params0) { public AbilityApiBased(ApiType api0, Card sourceCard, Cost abCost, TargetRestrictions tgt, Map<String, String> params0) {
super(sourceCard, abCost, tgt); super(sourceCard, abCost, tgt);
originalMapParams.putAll(params0); originalMapParams.putAll(params0);
@@ -24,13 +18,12 @@ public class AbilityApiBased extends AbilityActivated {
api = api0; api = api0;
effect = api.getSpellEffect(); effect = api.getSpellEffect();
if (effect instanceof ManaEffect || effect instanceof ManaReflectedEffect) { if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) {
this.setManaPart(new AbilityManaPart(sourceCard, mapParams)); this.setManaPart(new AbilityManaPart(sourceCard, mapParams));
this.setUndoable(true); // will try at least this.setUndoable(true); // will try at least
} }
if (effect instanceof ChangeZoneEffect || effect instanceof ChangeZoneAllEffect) { if (api.equals(ApiType.ChangeZone) || api.equals(ApiType.ChangeZoneAll)) {
AbilityFactory.adjustChangeZoneTarget(mapParams, this); AbilityFactory.adjustChangeZoneTarget(mapParams, this);
} }
} }

View File

@@ -1388,7 +1388,7 @@ public class AbilityUtils {
); );
// check conditions // check conditions
if (sa.getConditions().areMet(sa)) { if (sa.metConditions()) {
if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) { if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) {
sa.resolve(); sa.resolve();
} }
@@ -1656,10 +1656,15 @@ public class AbilityUtils {
// Count$Kicked.<numHB>.<numNotHB> // Count$Kicked.<numHB>.<numNotHB>
if (sq[0].startsWith("Kicked")) { if (sq[0].startsWith("Kicked")) {
boolean kicked = ((SpellAbility)ctb).isKicked() || c.getKickerMagnitude() > 0; boolean kicked = sa.isKicked() || c.getKickerMagnitude() > 0;
return CardFactoryUtil.doXMath(Integer.parseInt(kicked ? sq[1] : sq[2]), expr, c); return CardFactoryUtil.doXMath(Integer.parseInt(kicked ? sq[1] : sq[2]), expr, c);
} }
// Count$UrzaLands.<numHB>.<numNotHB>
if (sq[0].startsWith("UrzaLands")) {
return CardFactoryUtil.doXMath(Integer.parseInt(sa.getActivatingPlayer().hasUrzaLands() ? sq[1] : sq[2]), expr, c);
}
//Count$SearchedLibrary.<DefinedPlayer> //Count$SearchedLibrary.<DefinedPlayer>
if (sq[0].contains("SearchedLibrary")) { if (sq[0].contains("SearchedLibrary")) {
int sum = 0; int sum = 0;

View File

@@ -135,6 +135,7 @@ public enum ApiType {
Repeat (RepeatEffect.class), Repeat (RepeatEffect.class),
RepeatEach (RepeatEachEffect.class), RepeatEach (RepeatEachEffect.class),
ReplaceEffect (ReplaceEffect.class), ReplaceEffect (ReplaceEffect.class),
ReplaceMana (ReplaceManaEffect.class),
ReplaceDamage (ReplaceDamageEffect.class), ReplaceDamage (ReplaceDamageEffect.class),
ReplaceSplitDamage (ReplaceSplitDamageEffect.class), ReplaceSplitDamage (ReplaceSplitDamageEffect.class),
RestartGame (RestartGameEffect.class), RestartGame (RestartGameEffect.class),

View File

@@ -2,10 +2,6 @@ package forge.game.ability;
import java.util.Map; import java.util.Map;
import forge.game.ability.effects.ChangeZoneAllEffect;
import forge.game.ability.effects.ChangeZoneEffect;
import forge.game.ability.effects.ManaEffect;
import forge.game.ability.effects.ManaReflectedEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.spellability.AbilityManaPart; import forge.game.spellability.AbilityManaPart;
@@ -28,11 +24,11 @@ public class SpellApiBased extends Spell {
// A spell is always intrinsic // A spell is always intrinsic
this.setIntrinsic(true); this.setIntrinsic(true);
if (effect instanceof ManaEffect || effect instanceof ManaReflectedEffect) { if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) {
this.setManaPart(new AbilityManaPart(sourceCard, mapParams)); this.setManaPart(new AbilityManaPart(sourceCard, mapParams));
} }
if (effect instanceof ChangeZoneEffect || effect instanceof ChangeZoneAllEffect) { if (api.equals(ApiType.ChangeZone) || api.equals(ApiType.ChangeZoneAll)) {
AbilityFactory.adjustChangeZoneTarget(mapParams, this); AbilityFactory.adjustChangeZoneTarget(mapParams, this);
} }
} }

View File

@@ -15,7 +15,6 @@ import forge.game.mana.Mana;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.AbilityManaPart; import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.Localizer; import forge.util.Localizer;
@@ -37,7 +36,6 @@ public class ManaEffect extends SpellAbilityEffect {
sa.setUndoable(sa.isAbility() && sa.isUndoable()); sa.setUndoable(sa.isAbility() && sa.isUndoable());
final List<Player> tgtPlayers = getTargetPlayers(sa); final List<Player> tgtPlayers = getTargetPlayers(sa);
final TargetRestrictions tgt = sa.getTargetRestrictions();
final boolean optional = sa.hasParam("Optional"); final boolean optional = sa.hasParam("Optional");
final Game game = sa.getActivatingPlayer().getGame(); final Game game = sa.getActivatingPlayer().getGame();
@@ -45,41 +43,21 @@ public class ManaEffect extends SpellAbilityEffect {
return; return;
} }
if (sa.hasParam("DoubleManaInPool")) {
for (final Player player : tgtPlayers) {
for (byte color : ManaAtom.MANATYPES) {
int amountColor = player.getManaPool().getAmountOfColor(color);
for (int i = 0; i < amountColor; i++) {
abMana.produceMana(MagicColor.toShortString(color), player, sa);
}
}
}
}
if (sa.hasParam("ProduceNoOtherMana")) {
return;
}
if (abMana.isComboMana()) {
for (Player p : tgtPlayers) { for (Player p : tgtPlayers) {
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(card, sa.getParam("Amount"), sa) : 1; if (sa.usesTargeting() && !p.canBeTargetedBy(sa)) {
if (tgt != null && !p.canBeTargetedBy(sa)) {
// Illegal target. Skip. // Illegal target. Skip.
continue; continue;
} }
Player activator = sa.getActivatingPlayer(); if (abMana.isComboMana()) {
int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(card, sa.getParam("Amount"), sa) : 1;
String express = abMana.getExpressChoice(); String express = abMana.getExpressChoice();
String[] colorsProduced = abMana.getComboColors().split(" "); String[] colorsProduced = abMana.getComboColors().split(" ");
final StringBuilder choiceString = new StringBuilder(); final StringBuilder choiceString = new StringBuilder();
ColorSet colorOptions = null; ColorSet colorOptions = ColorSet.fromNames(colorsProduced);
String[] colorsNeeded = express.isEmpty() ? null : express.split(" "); String[] colorsNeeded = express.isEmpty() ? null : express.split(" ");
if (!abMana.isAnyMana()) {
colorOptions = ColorSet.fromNames(colorsProduced);
} else {
colorOptions = ColorSet.fromNames(MagicColor.Constant.ONLY_COLORS);
}
boolean differentChoice = abMana.getOrigProduced().contains("Different"); boolean differentChoice = abMana.getOrigProduced().contains("Different");
ColorSet fullOptions = colorOptions; ColorSet fullOptions = colorOptions;
for (int nMana = 0; nMana < amount; nMana++) { for (int nMana = 0; nMana < amount; nMana++) {
@@ -93,10 +71,10 @@ public class ManaEffect extends SpellAbilityEffect {
// just use the first possible color. // just use the first possible color.
choice = colorsProduced[differentChoice ? nMana : 0]; choice = colorsProduced[differentChoice ? nMana : 0];
} else { } else {
byte chosenColor = activator.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, byte chosenColor = p.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa,
differentChoice ? fullOptions : colorOptions); differentChoice ? fullOptions : colorOptions);
if (chosenColor == 0) if (chosenColor == 0)
throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + activator + " color mana choice is empty for " + card.getName()); throw new RuntimeException("ManaEffect::resolve() /*combo mana*/ - " + p + " color mana choice is empty for " + card.getName());
fullOptions = ColorSet.fromMask(fullOptions.getMyColor() - chosenColor); fullOptions = ColorSet.fromMask(fullOptions.getMyColor() - chosenColor);
choice = MagicColor.toShortString(chosenColor); choice = MagicColor.toShortString(chosenColor);
@@ -116,18 +94,10 @@ public class ManaEffect extends SpellAbilityEffect {
return; return;
} }
game.action.nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", activator.getName(), choiceString), activator); game.getAction().nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), choiceString), p);
abMana.setExpressChoice(choiceString.toString()); abMana.setExpressChoice(choiceString.toString());
} }
}
else if (abMana.isAnyMana()) { else if (abMana.isAnyMana()) {
for (Player p : tgtPlayers) {
if (tgt != null && !p.canBeTargetedBy(sa)) {
// Illegal target. Skip.
continue;
}
Player act = sa.getActivatingPlayer();
// AI color choice is set in ComputerUtils so only human players need to make a choice // AI color choice is set in ComputerUtils so only human players need to make a choice
String colorsNeeded = abMana.getExpressChoice(); String colorsNeeded = abMana.getExpressChoice();
@@ -142,20 +112,14 @@ public class ManaEffect extends SpellAbilityEffect {
colorMenu = mask == 0 ? ColorSet.ALL_COLORS : ColorSet.fromMask(mask); colorMenu = mask == 0 ? ColorSet.ALL_COLORS : ColorSet.fromMask(mask);
byte val = p.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, colorMenu); byte val = p.getController().chooseColor(Localizer.getInstance().getMessage("lblSelectManaProduce"), sa, colorMenu);
if (0 == val) { if (0 == val) {
throw new RuntimeException("ManaEffect::resolve() /*any mana*/ - " + act + " color mana choice is empty for " + card.getName()); throw new RuntimeException("ManaEffect::resolve() /*any mana*/ - " + p + " color mana choice is empty for " + card.getName());
} }
choice = MagicColor.toShortString(val); choice = MagicColor.toShortString(val);
game.action.nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", act.getName(), choice), act); game.getAction().nofityOfValue(sa, card, Localizer.getInstance().getMessage("lblPlayerPickedChosen", p.getName(), choice), p);
abMana.setExpressChoice(choice); abMana.setExpressChoice(choice);
} }
}
else if (abMana.isSpecialMana()) { else if (abMana.isSpecialMana()) {
for (Player p : tgtPlayers) {
if (tgt != null && !p.canBeTargetedBy(sa)) {
// Illegal target. Skip.
continue;
}
String type = abMana.getOrigProduced().split("Special ")[1]; String type = abMana.getOrigProduced().split("Special ")[1];
@@ -177,7 +141,7 @@ public class ManaEffect extends SpellAbilityEffect {
if (cs.isMonoColor()) if (cs.isMonoColor())
sb.append(MagicColor.toShortString(s.getColorMask())); sb.append(MagicColor.toShortString(s.getColorMask()));
else /* (cs.isMulticolor()) */ { else /* (cs.isMulticolor()) */ {
byte chosenColor = sa.getActivatingPlayer().getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs); byte chosenColor = p.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
sb.append(MagicColor.toShortString(chosenColor)); sb.append(MagicColor.toShortString(chosenColor));
} }
} }
@@ -210,34 +174,36 @@ public class ManaEffect extends SpellAbilityEffect {
abMana.setExpressChoice(ColorSet.fromMask(colors)); abMana.setExpressChoice(ColorSet.fromMask(colors));
} else if (type.startsWith("EachColoredManaSymbol")) { } else if (type.startsWith("EachColoredManaSymbol")) {
final String res = type.split("_")[1]; final String res = type.split("_")[1];
final CardCollection list = AbilityUtils.getDefinedCards(card, res, sa);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (Card c : list) { for (Card c : AbilityUtils.getDefinedCards(card, res, sa)) {
String mana = c.getManaCost().toString(); for (ManaCostShard s : c.getManaCost()) {
for (int i = 0; i < mana.length(); i++) { ColorSet cs = ColorSet.fromMask(s.getColorMask());
char symbol = mana.charAt(i); if(cs.isColorless())
switch (symbol) { continue;
case 'W': sb.append(' ');
case 'U': if (cs.isMonoColor())
case 'B': sb.append(MagicColor.toShortString(s.getColorMask()));
case 'R': else /* (cs.isMulticolor()) */ {
case 'G': byte chosenColor = p.getController().chooseColor(Localizer.getInstance().getMessage("lblChooseSingleColorFromTarget", s.toString()), sa, cs);
sb.append(symbol).append(' '); sb.append(MagicColor.toShortString(chosenColor));
break;
} }
} }
} }
abMana.setExpressChoice(sb.toString().trim()); abMana.setExpressChoice(sb.toString().trim());
} else if (type.startsWith("DoubleManaInPool")) {
StringBuilder sb = new StringBuilder();
for (byte color : ManaAtom.MANATYPES) {
sb.append(StringUtils.repeat(MagicColor.toShortString(color), " ", p.getManaPool().getAmountOfColor(color))).append(" ");
}
abMana.setExpressChoice(sb.toString().trim());
} }
if (abMana.getExpressChoice().isEmpty()) { if (abMana.getExpressChoice().isEmpty()) {
System.out.println("AbilityFactoryMana::manaResolve() - special mana effect is empty for " + sa.getHostCard().getName()); System.out.println("AbilityFactoryMana::manaResolve() - special mana effect is empty for " + sa.getHostCard().getName());
} }
} }
}
for (final Player player : tgtPlayers) { abMana.produceMana(GameActionUtil.generatedMana(sa), p, sa);
abMana.produceMana(GameActionUtil.generatedMana(sa), player, sa);
} }
// Only clear express choice after mana has been produced // Only clear express choice after mana has been produced

View File

@@ -11,7 +11,6 @@ import forge.game.spellability.SpellAbility;
import forge.util.Localizer; import forge.util.Localizer;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -29,8 +28,7 @@ public class ManaReflectedEffect extends SpellAbilityEffect {
final Collection<String> colors = CardUtil.getReflectableManaColors(sa); final Collection<String> colors = CardUtil.getReflectableManaColors(sa);
final List<Player> tgtPlayers = getTargetPlayers(sa); for (final Player player : getTargetPlayers(sa)) {
for (final Player player : tgtPlayers) {
final String generated = generatedReflectedMana(sa, colors, player); final String generated = generatedReflectedMana(sa, colors, player);
ma.produceMana(generated, player, sa); ma.produceMana(generated, player, sa);
} }

View File

@@ -6,12 +6,14 @@ import forge.game.ability.AbilityKey;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import forge.game.Game; import forge.game.Game;
import forge.game.GameLogEntryType;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.replacement.ReplacementResult; import forge.game.replacement.ReplacementResult;
import forge.game.replacement.ReplacementType; import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.util.TextUtil;
public class ReplaceDamageEffect extends SpellAbilityEffect { public class ReplaceDamageEffect extends SpellAbilityEffect {
@@ -58,6 +60,12 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
} }
params.put(AbilityKey.DamageAmount, dmg); params.put(AbilityKey.DamageAmount, dmg);
// need to log Updated events there, or the log is wrong order
String message = sa.getReplacementEffect().toString();
if ( !StringUtils.isEmpty(message)) {
message = TextUtil.fastReplace(message, "CARDNAME", card.getName());
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
}
//try to call replacementHandler with new Params //try to call replacementHandler with new Params
ReplacementResult result = game.getReplacementHandler().run(event, params); ReplacementResult result = game.getReplacementHandler().run(event, params);

View File

@@ -3,9 +3,12 @@ package forge.game.ability.effects;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import forge.game.Game; import forge.game.Game;
import forge.game.GameLogEntryType;
import forge.game.GameObject; import forge.game.GameObject;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
@@ -16,6 +19,7 @@ import forge.game.player.Player;
import forge.game.replacement.ReplacementResult; import forge.game.replacement.ReplacementResult;
import forge.game.replacement.ReplacementType; import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.util.TextUtil;
public class ReplaceEffect extends SpellAbilityEffect { public class ReplaceEffect extends SpellAbilityEffect {
@@ -61,6 +65,13 @@ public class ReplaceEffect extends SpellAbilityEffect {
params.put(AbilityKey.EffectOnly, true); params.put(AbilityKey.EffectOnly, true);
} }
// need to log Updated events there, or the log is wrong order
String message = sa.getReplacementEffect().toString();
if ( !StringUtils.isEmpty(message)) {
message = TextUtil.fastReplace(message, "CARDNAME", card.getName());
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
}
//try to call replacementHandler with new Params //try to call replacementHandler with new Params
ReplacementResult result = game.getReplacementHandler().run(retype, params); ReplacementResult result = game.getReplacementHandler().run(retype, params);
switch (result) { switch (result) {

View File

@@ -0,0 +1,109 @@
package forge.game.ability.effects;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Maps;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.game.Game;
import forge.game.GameLogEntryType;
import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.replacement.ReplacementResult;
import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility;
import forge.util.TextUtil;
public class ReplaceManaEffect extends SpellAbilityEffect {
@Override
public void resolve(SpellAbility sa) {
final Card card = sa.getHostCard();
final Player player = sa.getActivatingPlayer();
final Game game = card.getGame();
// outside of Replacement Effect, unwanted result
if (!sa.isReplacementAbility()) {
return;
}
final ReplacementType event = sa.getReplacementEffect().getMode();
@SuppressWarnings("unchecked")
Map<AbilityKey, Object> originalParams = (Map<AbilityKey, Object>) sa.getReplacingObject(AbilityKey.OriginalParams);
Map<AbilityKey, Object> params = Maps.newHashMap(originalParams);
String replaced = (String)sa.getReplacingObject(AbilityKey.Mana);
if (sa.hasParam("ReplaceMana")) {
// replace type and amount
replaced = sa.getParam("ReplaceMana");
if ("Any".equals(replaced)) {
byte rs = MagicColor.GREEN;
rs = player.getController().chooseColor("Choose a color", sa, ColorSet.ALL_COLORS);
replaced = MagicColor.toShortString(rs);
}
} else if (sa.hasParam("ReplaceType")) {
// replace color and colorless
String color = sa.getParam("ReplaceType");
if ("Any".equals(color)) {
byte rs = MagicColor.GREEN;
rs = player.getController().chooseColor("Choose a color", sa, ColorSet.ALL_COLORS);
color = MagicColor.toShortString(rs);
}
for (byte c : MagicColor.WUBRGC) {
String s = MagicColor.toShortString(c);
replaced = replaced.replace(s, color);
}
} else if (sa.hasParam("ReplaceColor")) {
// replace color
String color = sa.getParam("ReplaceColor");
if ("Chosen".equals(color)) {
if (card.hasChosenColor()) {
color = MagicColor.toShortString(card.getChosenColor());
}
}
if (sa.hasParam("ReplaceOnly")) {
replaced = replaced.replace(sa.getParam("ReplaceOnly"), color);
} else {
for (byte c : MagicColor.WUBRG) {
String s = MagicColor.toShortString(c);
replaced = replaced.replace(s, color);
}
}
} else if (sa.hasParam("ReplaceAmount")) {
// replace amount = multiples
replaced = StringUtils.repeat(replaced, " ", Integer.valueOf(sa.getParam("ReplaceAmount")));
}
params.put(AbilityKey.Mana, replaced);
// need to log Updated events there, or the log is wrong order
String message = sa.getReplacementEffect().toString();
if ( !StringUtils.isEmpty(message)) {
message = TextUtil.fastReplace(message, "CARDNAME", card.getName());
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
}
//try to call replacementHandler with new Params
ReplacementResult result = game.getReplacementHandler().run(event, params);
switch (result) {
case NotReplaced:
case Updated: {
for (Map.Entry<AbilityKey, Object> e : params.entrySet()) {
originalParams.put(e.getKey(), e.getValue());
}
// effect was updated
originalParams.put(AbilityKey.ReplacementResult, ReplacementResult.Updated);
break;
}
default:
// effect was replaced with something else
originalParams.put(AbilityKey.ReplacementResult, result);
break;
}
}
}

View File

@@ -53,7 +53,7 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
if (card.getType().hasStringType("Effect") && prevent <= 0) { if (card.getType().hasStringType("Effect") && prevent <= 0) {
game.getAction().exile(card, null); game.getAction().exile(card, null);
} else if (!StringUtils.isNumeric(varValue)) { } else if (!StringUtils.isNumeric(varValue)) {
card.setSVar(varValue, "Number$" + prevent); sa.setSVar(varValue, "Number$" + prevent);
} }
Card sourceLKI = (Card) sa.getReplacingObject(AbilityKey.Source); Card sourceLKI = (Card) sa.getReplacingObject(AbilityKey.Source);

View File

@@ -2353,7 +2353,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
if (ab.getApi() == ApiType.ManaReflected) { if (ab.getApi() == ApiType.ManaReflected) {
colors.addAll(CardUtil.getReflectableManaColors(ab)); colors.addAll(CardUtil.getReflectableManaColors(ab));
} else { } else {
colors = CardUtil.canProduce(6, ab.getManaPart(), colors); colors = CardUtil.canProduce(6, ab, colors);
} }
} }
@@ -2364,7 +2364,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return true; return true;
} }
} else { } else {
if (mana.getManaPart().canProduce(MagicColor.toShortString(s))) { if (mana.canProduce(MagicColor.toShortString(s))) {
return true; return true;
} }
} }

View File

@@ -358,12 +358,12 @@ public class CardFactory {
// Name first so Senty has the Card name // Name first so Senty has the Card name
c.setName(face.getName()); c.setName(face.getName());
for (Entry<String, String> v : face.getVariables()) c.setSVar(v.getKey(), v.getValue());
for (String r : face.getReplacements()) c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true)); for (String r : face.getReplacements()) c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true));
for (String s : face.getStaticAbilities()) c.addStaticAbility(s); for (String s : face.getStaticAbilities()) c.addStaticAbility(s);
for (String t : face.getTriggers()) c.addTrigger(TriggerHandler.parseTrigger(t, c, true)); for (String t : face.getTriggers()) c.addTrigger(TriggerHandler.parseTrigger(t, c, true));
for (Entry<String, String> v : face.getVariables()) c.setSVar(v.getKey(), v.getValue());
// keywords not before variables // keywords not before variables
c.addIntrinsicKeywords(face.getKeywords(), false); c.addIntrinsicKeywords(face.getKeywords(), false);

View File

@@ -1475,7 +1475,7 @@ public class CardFactoryUtil {
for (Card card : otb) { for (Card card : otb) {
if (!card.isTapped() || !untappedOnly) { if (!card.isTapped() || !untappedOnly) {
for (SpellAbility ma : card.getManaAbilities()) { for (SpellAbility ma : card.getManaAbilities()) {
if (ma.getManaPart().canProduce(MagicColor.toShortString(color))) { if (ma.canProduce(MagicColor.toShortString(color))) {
uniqueColors++; uniqueColors++;
continue outer; continue outer;
} }

View File

@@ -912,7 +912,7 @@ public class CardProperty {
} else if (property.startsWith("canProduceManaColor")) { } else if (property.startsWith("canProduceManaColor")) {
final String color = property.split("canProduceManaColor ")[1]; final String color = property.split("canProduceManaColor ")[1];
for (SpellAbility ma : card.getManaAbilities()) { for (SpellAbility ma : card.getManaAbilities()) {
if (ma.getManaPart().canProduce(MagicColor.toShortString(color))) { if (ma.canProduce(MagicColor.toShortString(color))) {
return true; return true;
} }
} }

View File

@@ -391,7 +391,6 @@ public final class CardUtil {
final String colorOrType = sa.getParam("ColorOrType"); final String colorOrType = sa.getParam("ColorOrType");
// currently Color or Type, Type is colors + colorless // currently Color or Type, Type is colors + colorless
final String validCard = sa.getParam("Valid");
final String reflectProperty = sa.getParam("ReflectProperty"); final String reflectProperty = sa.getParam("ReflectProperty");
// Produce (Reflecting Pool) or Is (Meteor Crater) // Produce (Reflecting Pool) or Is (Meteor Crater)
@@ -400,8 +399,10 @@ public final class CardUtil {
maxChoices++; maxChoices++;
} }
CardCollection cards = null; CardCollection cards;
if (sa.hasParam("Valid")) {
final String validCard = sa.getParam("Valid");
// Reuse AF_Defined in a slightly different way // Reuse AF_Defined in a slightly different way
if (validCard.startsWith("Defined.")) { if (validCard.startsWith("Defined.")) {
cards = AbilityUtils.getDefinedCards(card, TextUtil.fastReplace(validCard, "Defined.", ""), abMana); cards = AbilityUtils.getDefinedCards(card, TextUtil.fastReplace(validCard, "Defined.", ""), abMana);
@@ -414,14 +415,14 @@ public final class CardUtil {
} }
// remove anything cards that is already in parents // remove anything cards that is already in parents
for (final Card p : parents) { cards.removeAll(parents);
cards.remove(p);
}
if ((cards.size() == 0) && !reflectProperty.equals("Produced")) { if (cards.isEmpty()) {
return colors; return colors;
} }
} else {
cards = new CardCollection();
}
if (reflectProperty.equals("Is")) { // Meteor Crater if (reflectProperty.equals("Is")) { // Meteor Crater
for (final Card card1 : cards) { for (final Card card1 : cards) {
// For each card, go through all the colors and if the card is that color, add // For each card, go through all the colors and if the card is that color, add
@@ -436,7 +437,7 @@ public final class CardUtil {
} }
} else if (reflectProperty.equals("Produced")) { } else if (reflectProperty.equals("Produced")) {
// Why is this name so similar to the one below? // Why is this name so similar to the one below?
final String producedColors = abMana instanceof AbilitySub ? (String) abMana.getRootAbility().getTriggeringObject(AbilityKey.Produced) : (String) abMana.getTriggeringObject(AbilityKey.Produced); final String producedColors = (String) abMana.getRootAbility().getTriggeringObject(AbilityKey.Produced);
for (final String col : MagicColor.Constant.ONLY_COLORS) { for (final String col : MagicColor.Constant.ONLY_COLORS) {
final String s = MagicColor.toShortString(col); final String s = MagicColor.toShortString(col);
if (producedColors.contains(s)) { if (producedColors.contains(s)) {
@@ -469,7 +470,7 @@ public final class CardUtil {
} }
continue; continue;
} }
colors = canProduce(maxChoices, ab.getManaPart(), colors); colors = canProduce(maxChoices, ab, colors);
if (!parents.contains(ab.getHostCard())) { if (!parents.contains(ab.getHostCard())) {
parents.add(ab.getHostCard()); parents.add(ab.getHostCard());
} }
@@ -486,19 +487,18 @@ public final class CardUtil {
return colors; return colors;
} }
public static Set<String> canProduce(final int maxChoices, final AbilityManaPart ab, public static Set<String> canProduce(final int maxChoices, final SpellAbility sa,
final Set<String> colors) { final Set<String> colors) {
if (ab == null) { if (sa == null) {
return colors; return colors;
} }
for (final String col : MagicColor.Constant.ONLY_COLORS) { for (final String col : MagicColor.Constant.ONLY_COLORS) {
final String s = MagicColor.toShortString(col); if (sa.canProduce(MagicColor.toShortString(col))) {
if (ab.canProduce(s)) {
colors.add(col); colors.add(col);
} }
} }
if (maxChoices == 6 && ab.canProduce("C")) { if (maxChoices == 6 && sa.canProduce("C")) {
colors.add(MagicColor.Constant.COLORLESS); colors.add(MagicColor.Constant.COLORLESS);
} }

View File

@@ -20,6 +20,8 @@ package forge.game.mana;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.card.ColorSet; import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.mana.IParserManaCost; import forge.card.mana.IParserManaCost;
@@ -105,11 +107,16 @@ public class ManaCostBeingPaid {
xCount = copy.xCount; xCount = copy.xCount;
totalCount = copy.totalCount; totalCount = copy.totalCount;
} }
@Override
public String toString() {
return "{x=" + xCount + " total=" + totalCount + "}";
}
} }
// holds Mana_Part objects // holds Mana_Part objects
// ManaPartColor is stored before ManaPartGeneric // ManaPartColor is stored before ManaPartGeneric
private final Map<ManaCostShard, ShardCount> unpaidShards = new HashMap<>(); private final Map<ManaCostShard, ShardCount> unpaidShards = Maps.newHashMap();
private Map<String, Integer> xManaCostPaidByColor; private Map<String, Integer> xManaCostPaidByColor;
private final String sourceRestriction; private final String sourceRestriction;
private byte sunburstMap = 0; private byte sunburstMap = 0;
@@ -124,7 +131,7 @@ public class ManaCostBeingPaid {
unpaidShards.put(m.getKey(), new ShardCount(m.getValue())); unpaidShards.put(m.getKey(), new ShardCount(m.getValue()));
} }
if (manaCostBeingPaid.xManaCostPaidByColor != null) { if (manaCostBeingPaid.xManaCostPaidByColor != null) {
xManaCostPaidByColor = new HashMap<>(manaCostBeingPaid.xManaCostPaidByColor); xManaCostPaidByColor = Maps.newHashMap(manaCostBeingPaid.xManaCostPaidByColor);
} }
sourceRestriction = manaCostBeingPaid.sourceRestriction; sourceRestriction = manaCostBeingPaid.sourceRestriction;
sunburstMap = manaCostBeingPaid.sunburstMap; sunburstMap = manaCostBeingPaid.sunburstMap;
@@ -503,7 +510,7 @@ public class ManaCostBeingPaid {
sc.xCount--; sc.xCount--;
String color = MagicColor.toShortString(colorMask); String color = MagicColor.toShortString(colorMask);
if (xManaCostPaidByColor == null) { if (xManaCostPaidByColor == null) {
xManaCostPaidByColor = new HashMap<>(); xManaCostPaidByColor = Maps.newHashMap();
} }
Integer xColor = xManaCostPaidByColor.get(color); Integer xColor = xManaCostPaidByColor.get(color);
if (xColor == null) { if (xColor == null) {
@@ -602,19 +609,7 @@ public class ManaCostBeingPaid {
} }
int nGeneric = getGenericManaAmount(); int nGeneric = getGenericManaAmount();
List<ManaCostShard> shards = new ArrayList<>(unpaidShards.keySet()); List<ManaCostShard> shards = Lists.newArrayList(unpaidShards.keySet());
// TODO Fix this. Should we really be changing Shards here?
if (false && pool != null) { //replace shards with generic mana if they can be paid with any color mana
for (int i = 0; i < shards.size(); i++) {
ManaCostShard shard = shards.get(i);
if (shard != ManaCostShard.GENERIC && pool.getPossibleColorUses(shard.getColorMask()) == ManaAtom.ALL_MANA_TYPES) {
nGeneric += unpaidShards.get(shard).totalCount;
shards.remove(i);
i--;
}
}
}
if (nGeneric > 0) { if (nGeneric > 0) {
if (nGeneric <= 20) { if (nGeneric <= 20) {

View File

@@ -166,26 +166,30 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
owner.updateManaForView(); owner.updateManaForView();
} }
private void removeMana(final Mana mana) { private boolean removeMana(final Mana mana) {
Collection<Mana> cm = floatingMana.get(mana.getColor()); if (floatingMana.remove(mana.getColor(), mana)) {
if (cm.remove(mana)) {
owner.updateManaForView(); owner.updateManaForView();
owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Removed, mana)); owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Removed, mana));
return true;
} }
return false;
} }
public final void payManaFromAbility(final SpellAbility saPaidFor, ManaCostBeingPaid manaCost, final SpellAbility saPayment) { public final void payManaFromAbility(final SpellAbility saPaidFor, ManaCostBeingPaid manaCost, final SpellAbility saPayment) {
// Mana restriction must be checked before this method is called // Mana restriction must be checked before this method is called
final List<SpellAbility> paidAbs = saPaidFor.getPayingManaAbilities(); final List<SpellAbility> paidAbs = saPaidFor.getPayingManaAbilities();
AbilityManaPart abManaPart = saPayment.getManaPartRecursive();
paidAbs.add(saPayment); // assumes some part on the mana produced by the ability will get used paidAbs.add(saPayment); // assumes some part on the mana produced by the ability will get used
for (final Mana mana : abManaPart.getLastManaProduced()) {
// need to get all mana from all ManaAbilities of the SpellAbility
for (AbilityManaPart mp : saPayment.getAllManaParts()) {
for (final Mana mana : mp.getLastManaProduced()) {
if (tryPayCostWithMana(saPaidFor, manaCost, mana, false)) { if (tryPayCostWithMana(saPaidFor, manaCost, mana, false)) {
saPaidFor.getPayingMana().add(0, mana); saPaidFor.getPayingMana().add(0, mana);
} }
} }
} }
}
public boolean tryPayCostWithColor(byte colorCode, SpellAbility saPaidFor, ManaCostBeingPaid manaCost) { public boolean tryPayCostWithColor(byte colorCode, SpellAbility saPaidFor, ManaCostBeingPaid manaCost) {
Mana manaFound = null; Mana manaFound = null;
@@ -216,8 +220,12 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
if (!manaCost.isNeeded(mana, this)) { if (!manaCost.isNeeded(mana, this)) {
return false; return false;
} }
// only pay mana into manaCost when the Mana could be removed from the Mana pool
// if the mana wasn't in the mana pool then something is wrong
if (!removeMana(mana)) {
return false;
}
manaCost.payMana(mana, this); manaCost.payMana(mana, this);
removeMana(mana);
return true; return true;
} }

View File

@@ -3528,4 +3528,11 @@ public class Player extends GameEntity implements Comparable<Player> {
public void resetCycledThisTurn() { public void resetCycledThisTurn() {
cycledThisTurn = 0; cycledThisTurn = 0;
} }
public boolean hasUrzaLands() {
final CardCollectionView landsControlled = getCardsIn(ZoneType.Battlefield);
return Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Mine")))
&& Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Power-Plant")))
&& Iterables.any(landsControlled, Predicates.and(CardPredicates.isType("Urza's"), CardPredicates.isType("Tower")));
}
} }

View File

@@ -31,10 +31,10 @@ public class ReplaceProduceMana extends ReplacementEffect {
*/ */
@Override @Override
public boolean canReplace(Map<AbilityKey, Object> runParams) { public boolean canReplace(Map<AbilityKey, Object> runParams) {
//Check for tapping
if (!hasParam("NoTapCheck")) { if (hasParam("ValidAbility")) {
final SpellAbility manaAbility = (SpellAbility) runParams.get(AbilityKey.AbilityMana); final SpellAbility manaAbility = (SpellAbility) runParams.get(AbilityKey.AbilityMana);
if (manaAbility == null || !manaAbility.getRootAbility().getPayCosts().hasTapCost()) { if (!matchesValid(manaAbility, getParam("ValidAbility").split(","), getHostCard())) {
return false; return false;
} }
} }
@@ -43,15 +43,23 @@ public class ReplaceProduceMana extends ReplacementEffect {
String full = getParam("ManaAmount"); String full = getParam("ManaAmount");
String operator = full.substring(0, 2); String operator = full.substring(0, 2);
String operand = full.substring(2); String operand = full.substring(2);
int intoperand = AbilityUtils.calculateAmount(getHostCard(), operand, this); int intoperand = AbilityUtils.calculateAmount(getHostCard(), operand, this);
int manaAmount = StringUtils.countMatches((String) runParams.get(AbilityKey.Mana), " ") + 1; int manaAmount = StringUtils.countMatches((String) runParams.get(AbilityKey.Mana), " ") + 1;
if (!Expressions.compare(manaAmount, operator, intoperand)) { if (!Expressions.compare(manaAmount, operator, intoperand)) {
return false; return false;
} }
} }
if (hasParam("ValidPlayer")) {
if (!matchesValid(runParams.get(AbilityKey.Player), getParam("ValidPlayer").split(","), getHostCard())) {
return false;
}
}
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), this.getHostCard())) { if (!matchesValid(runParams.get(AbilityKey.Affected), getParam("ValidCard").split(","), getHostCard())) {
return false; return false;
} }
} }
@@ -60,4 +68,7 @@ public class ReplaceProduceMana extends ReplacementEffect {
} }
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
sa.setReplacingObject(AbilityKey.Mana, runParams.get(AbilityKey.Mana));
}
} }

View File

@@ -19,6 +19,7 @@ package forge.game.replacement;
import forge.game.Game; import forge.game.Game;
import forge.game.TriggerReplacementBase; import forge.game.TriggerReplacementBase;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
@@ -271,4 +272,13 @@ public abstract class ReplacementEffect extends TriggerReplacementBase {
void setMode(ReplacementType mode) { void setMode(ReplacementType mode) {
this.mode = mode; this.mode = mode;
} }
public SpellAbility ensureAbility() {
SpellAbility sa = getOverridingAbility();
if (sa == null && hasParam("ReplaceWith")) {
sa = AbilityFactory.getAbility(getHostCard(), getParam("ReplaceWith"));
setOverridingAbility(sa);
}
return sa;
}
} }

View File

@@ -17,7 +17,6 @@
*/ */
package forge.game.replacement; package forge.game.replacement;
import forge.card.MagicColor;
import forge.game.Game; import forge.game.Game;
import forge.game.GameLogEntryType; import forge.game.GameLogEntryType;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
@@ -259,12 +258,17 @@ public class ReplacementHandler {
chosenRE.setHasRun(false); chosenRE.setHasRun(false);
hasRun.remove(chosenRE); hasRun.remove(chosenRE);
chosenRE.setOtherChoices(null); chosenRE.setOtherChoices(null);
// Updated Replacements need to be logged elsewhere because its otherwise in the wrong order
if (res != ReplacementResult.Updated) {
String message = chosenRE.getDescription(); String message = chosenRE.getDescription();
if ( !StringUtils.isEmpty(message)) if ( !StringUtils.isEmpty(message))
if (chosenRE.getHostCard() != null) { if (chosenRE.getHostCard() != null) {
message = TextUtil.fastReplace(message, "CARDNAME", chosenRE.getHostCard().getName()); message = TextUtil.fastReplace(message, "CARDNAME", chosenRE.getHostCard().getName());
} }
game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message); game.getGameLog().add(GameLogEntryType.EFFECT_REPLACED, message);
}
return res; return res;
} }
@@ -344,26 +348,12 @@ public class ReplacementHandler {
Player player = host.getController(); Player player = host.getController();
if (mapParams.containsKey("ManaReplacement")) {
final SpellAbility manaAb = (SpellAbility) runParams.get(AbilityKey.AbilityMana);
final Player player1 = (Player) runParams.get(AbilityKey.Player);
final String rep = (String) runParams.get(AbilityKey.Mana);
// Replaced mana type
final Card repHost = host;
String repType = repHost.getSVar(mapParams.get("ManaReplacement"));
if (repType.contains("Chosen") && repHost.hasChosenColor()) {
repType = TextUtil.fastReplace(repType, "Chosen", MagicColor.toShortString(repHost.getChosenColor()));
}
manaAb.getManaPart().setManaReplaceType(repType);
manaAb.getManaPart().produceMana(rep, player1, manaAb);
} else {
player.getController().playSpellAbilityNoStack(effectSA, true); player.getController().playSpellAbilityNoStack(effectSA, true);
// if the spellability is a replace effect then its some new logic // if the spellability is a replace effect then its some new logic
// if ReplacementResult is set in run params use that instead // if ReplacementResult is set in run params use that instead
if (runParams.containsKey(AbilityKey.ReplacementResult)) { if (runParams.containsKey(AbilityKey.ReplacementResult)) {
return (ReplacementResult) runParams.get(AbilityKey.ReplacementResult); return (ReplacementResult) runParams.get(AbilityKey.ReplacementResult);
} }
}
return ReplacementResult.Replaced; return ReplacementResult.Replaced;
} }
@@ -407,6 +397,10 @@ public class ReplacementHandler {
ret.setActiveZone(EnumSet.copyOf(ZoneType.listValueOf(activeZones))); ret.setActiveZone(EnumSet.copyOf(ZoneType.listValueOf(activeZones)));
} }
if (mapParams.containsKey("ReplaceWith")) {
ret.setOverridingAbility(AbilityFactory.getAbility(host, mapParams.get("ReplaceWith"), ret));
}
return ret; return ret;
} }
} }

View File

@@ -18,15 +18,18 @@
package forge.game.spellability; package forge.game.spellability;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.card.ColorSet; import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.mana.ManaAtom; import forge.card.mana.ManaAtom;
import forge.card.mana.ManaCostShard; import forge.card.mana.ManaCostShard;
import forge.game.Game;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.ability.ApiType;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardUtil;
import forge.game.mana.Mana; import forge.game.mana.Mana;
import forge.game.mana.ManaPool; import forge.game.mana.ManaPool;
import forge.game.player.Player; import forge.game.player.Player;
@@ -39,8 +42,6 @@ import org.apache.commons.lang3.StringUtils;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* <p> * <p>
@@ -57,6 +58,7 @@ public class AbilityManaPart implements java.io.Serializable {
private final String origProduced; private final String origProduced;
private String lastExpressChoice = ""; private String lastExpressChoice = "";
private final String manaRestrictions; private final String manaRestrictions;
private String extraManaRestrictions = "";
private final String cannotCounterSpell; private final String cannotCounterSpell;
private final String addsKeywords; private final String addsKeywords;
private final String addsKeywordsType; private final String addsKeywordsType;
@@ -64,7 +66,6 @@ public class AbilityManaPart implements java.io.Serializable {
private final String addsCounters; private final String addsCounters;
private final String triggersWhenSpent; private final String triggersWhenSpent;
private final boolean persistentMana; private final boolean persistentMana;
private String manaReplaceType;
private transient List<Mana> lastManaProduced = Lists.newArrayList(); private transient List<Mana> lastManaProduced = Lists.newArrayList();
@@ -94,7 +95,6 @@ public class AbilityManaPart implements java.io.Serializable {
this.addsCounters = params.get("AddsCounters"); this.addsCounters = params.get("AddsCounters");
this.triggersWhenSpent = params.get("TriggersWhenSpent"); this.triggersWhenSpent = params.get("TriggersWhenSpent");
this.persistentMana = (null != params.get("PersistentMana")) && "True".equalsIgnoreCase(params.get("PersistentMana")); this.persistentMana = (null != params.get("PersistentMana")) && "True".equalsIgnoreCase(params.get("PersistentMana"));
this.manaReplaceType = params.containsKey("ManaReplaceType") ? params.get("ManaReplaceType") : "";
} }
/** /**
@@ -121,14 +121,26 @@ public class AbilityManaPart implements java.io.Serializable {
public final void produceMana(final String produced, final Player player, SpellAbility sa) { public final void produceMana(final String produced, final Player player, SpellAbility sa) {
final Card source = this.getSourceCard(); final Card source = this.getSourceCard();
final ManaPool manaPool = player.getManaPool(); final ManaPool manaPool = player.getManaPool();
String afterReplace = applyManaReplacement(sa, produced); String afterReplace = produced;
SpellAbility root = sa == null ? null : sa.getRootAbility();
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(source); final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(source);
repParams.put(AbilityKey.Mana, produced); repParams.put(AbilityKey.Mana, afterReplace);
repParams.put(AbilityKey.Player, player); repParams.put(AbilityKey.Player, player);
repParams.put(AbilityKey.AbilityMana, sa); repParams.put(AbilityKey.AbilityMana, root);
if (player.getGame().getReplacementHandler().run(ReplacementType.ProduceMana, repParams) != ReplacementResult.NotReplaced) { repParams.put(AbilityKey.Activator, root == null ? null : root.getActivatingPlayer());
switch (player.getGame().getReplacementHandler().run(ReplacementType.ProduceMana, repParams)) {
case NotReplaced:
break;
case Updated:
afterReplace = (String) repParams.get(AbilityKey.Mana);
break;
default:
return; return;
} }
//clear lastProduced //clear lastProduced
this.lastManaProduced.clear(); this.lastManaProduced.clear();
@@ -154,14 +166,14 @@ public class AbilityManaPart implements java.io.Serializable {
// Run triggers // Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(source); final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(source);
runParams.put(AbilityKey.Player, player); runParams.put(AbilityKey.Player, player);
runParams.put(AbilityKey.AbilityMana, sa);
runParams.put(AbilityKey.Produced, afterReplace); runParams.put(AbilityKey.Produced, afterReplace);
runParams.put(AbilityKey.AbilityMana, root);
runParams.put(AbilityKey.Activator, root == null ? null : root.getActivatingPlayer());
player.getGame().getTriggerHandler().runTrigger(TriggerType.TapsForMana, runParams, false); player.getGame().getTriggerHandler().runTrigger(TriggerType.TapsForMana, runParams, false);
if (source.isLand()) { if (source.isLand() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost() ) {
player.setTappedLandForManaThisTurn(true); player.setTappedLandForManaThisTurn(true);
} }
// Clear Mana replacement
this.manaReplaceType = "";
} // end produceMana(String) } // end produceMana(String)
/** /**
@@ -256,7 +268,15 @@ public class AbilityManaPart implements java.io.Serializable {
* @return a {@link java.lang.String} object. * @return a {@link java.lang.String} object.
*/ */
public String getManaRestrictions() { public String getManaRestrictions() {
return this.manaRestrictions; return manaRestrictions;
}
public void setExtraManaRestriction(String str) {
this.extraManaRestrictions = str;
}
public boolean meetsManaRestrictions(final SpellAbility sa) {
return meetsManaRestrictions(sa, this.manaRestrictions) && meetsManaRestrictions(sa, this.extraManaRestrictions);
} }
/** /**
@@ -268,14 +288,14 @@ public class AbilityManaPart implements java.io.Serializable {
* a {@link forge.game.spellability.SpellAbility} object. * a {@link forge.game.spellability.SpellAbility} object.
* @return a boolean. * @return a boolean.
*/ */
public boolean meetsManaRestrictions(final SpellAbility sa) { public boolean meetsManaRestrictions(final SpellAbility sa, String restrictions) {
// No restrictions // No restrictions
if (this.manaRestrictions.isEmpty()) { if (restrictions.isEmpty()) {
return true; return true;
} }
// Loop over restrictions // Loop over restrictions
for (String restriction : this.manaRestrictions.split(",")) { for (String restriction : restrictions.split(",")) {
if (restriction.equals("nonSpell")) { if (restriction.equals("nonSpell")) {
return !sa.isSpell(); return !sa.isSpell();
} }
@@ -465,10 +485,6 @@ public class AbilityManaPart implements java.io.Serializable {
return this.getOrigProduced().contains("Special"); return this.getOrigProduced().contains("Special");
} }
public final boolean canProduce(final String s) {
return canProduce(s, null);
}
/** /**
* <p> * <p>
* canProduce. * canProduce.
@@ -493,24 +509,9 @@ public class AbilityManaPart implements java.io.Serializable {
if (isComboMana()) { if (isComboMana()) {
return getComboColors().contains(s); return getComboColors().contains(s);
} }
if (sa != null) {
return applyManaReplacement(sa, origProduced).contains(s);
}
return origProduced.contains(s); return origProduced.contains(s);
} }
/**
* <p>
* isBasic.
* </p>
*
* @return a boolean.
*/
public final boolean isBasic() {
return this.getOrigProduced().length() == 1 || this.getOrigProduced().contains("Any")
|| this.getOrigProduced().contains("Chosen");
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final boolean equals(final Object o) { public final boolean equals(final Object o) {
@@ -586,81 +587,59 @@ public class AbilityManaPart implements java.io.Serializable {
return this.persistentMana; return this.persistentMana;
} }
/** boolean abilityProducesManaColor(final SpellAbility am, final byte neededColor) {
* @return the manaReplaceType if (0 != (neededColor & ManaAtom.GENERIC)) {
*/ return true;
public String getManaReplaceType() {
return manaReplaceType;
} }
/** if (isAnyMana()) {
* setManaReplaceType. return true;
*/
public void setManaReplaceType(final String type) {
this.manaReplaceType = type;
} }
/**
* <p> // check for produce mana replacement effects - they mess this up, so just use the mana ability
* applyManaReplacement. final Card source = am.getHostCard();
* </p> final Player activator = am.getActivatingPlayer();
* @return a String final Game g = source.getGame();
*/ final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
public static String applyManaReplacement(final SpellAbility sa, final String original) { repParams.put(AbilityKey.Mana, getOrigProduced());
final Map<String, String> repMap = Maps.newHashMap(); repParams.put(AbilityKey.Affected, source);
final Player act = sa != null ? sa.getActivatingPlayer() : null; repParams.put(AbilityKey.Player, activator);
final String manaReplace = sa != null ? sa.getManaPart().getManaReplaceType(): ""; repParams.put(AbilityKey.AbilityMana, am.getRootAbility());
if (manaReplace.isEmpty()) {
if (act != null && act.getLandsPlayedThisTurn() > 0 && sa.hasParam("ReplaceIfLandPlayed")) { for (final Player p : g.getPlayers()) {
return sa.getParam("ReplaceIfLandPlayed"); for (final Card crd : p.getAllCards()) {
} for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) {
return original; if (replacementEffect.requirementsCheck(g)
} && replacementEffect.getMode() == ReplacementType.ProduceMana
if (manaReplace.startsWith("Any")) { && replacementEffect.canReplace(repParams)
// Replace any type and amount && replacementEffect.zonesCheck(g.getZoneOf(crd))) {
String replaced = manaReplace.split("->")[1]; return true;
if (replaced.equals("Any")) {
byte rs = MagicColor.GREEN;
if (act != null) {
rs = act.getController().chooseColor("Choose a color", sa, ColorSet.ALL_COLORS);
}
replaced = MagicColor.toShortString(rs);
}
return replaced;
}
final Pattern splitter = Pattern.compile("->");
// Replace any type
for (String part : manaReplace.split(" & ")) {
final String[] v = splitter.split(part, 2);
// TODO Colorless mana replacement is probably different now?
if (v[0].equals("Colorless")) {
repMap.put("[0-9][0-9]?", v.length > 1 ? v[1].trim() : "");
} else {
repMap.put(v[0], v.length > 1 ? v[1].trim() : "");
} }
} }
// Handle different replacement simultaneously
Pattern pattern = Pattern.compile(StringUtils.join(repMap.keySet().iterator(), "|"));
Matcher m = pattern.matcher(original);
StringBuffer sb = new StringBuffer();
while(m.find()) {
if (m.group().matches("[0-9][0-9]?")) {
final String rep = StringUtils.repeat(repMap.get("[0-9][0-9]?") + " ",
Integer.parseInt(m.group())).trim();
m.appendReplacement(sb, rep);
} else {
m.appendReplacement(sb, repMap.get(m.group()));
} }
} }
m.appendTail(sb);
String replaced = sb.toString(); if (am.getApi() == ApiType.ManaReflected) {
while (replaced.contains("Any")) { final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am);
byte rs = MagicColor.GREEN; for (final String color : reflectableColors) {
if (act != null) { if (0 != (neededColor & ManaAtom.fromName(color))) {
rs = act.getController().chooseColor("Choose a color", sa, ColorSet.ALL_COLORS); return true;
} }
replaced = replaced.replaceFirst("Any", MagicColor.toShortString(rs));
} }
return replaced; }
else {
// treat special mana if it always can be paid
if (isSpecialMana()) {
return true;
}
String colorsProduced = isComboMana() ? getComboColors() : mana();
for (final String color : colorsProduced.split(" ")) {
if (0 != (neededColor & ManaAtom.fromName(color))) {
return true;
}
}
}
return false;
} }
} // end class AbilityMana } // end class AbilityMana

View File

@@ -20,10 +20,6 @@ package forge.game.spellability;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.ability.SpellAbilityEffect; import forge.game.ability.SpellAbilityEffect;
import forge.game.ability.effects.ChangeZoneAllEffect;
import forge.game.ability.effects.ChangeZoneEffect;
import forge.game.ability.effects.ManaEffect;
import forge.game.ability.effects.ManaReflectedEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.cost.Cost; import forge.game.cost.Cost;
@@ -92,11 +88,11 @@ public final class AbilitySub extends SpellAbility implements java.io.Serializab
effect = api.getSpellEffect(); effect = api.getSpellEffect();
if (effect instanceof ManaEffect || effect instanceof ManaReflectedEffect) { if (api.equals(ApiType.Mana) || api.equals(ApiType.ManaReflected)) {
this.setManaPart(new AbilityManaPart(ca, mapParams)); this.setManaPart(new AbilityManaPart(ca, mapParams));
} }
if (effect instanceof ChangeZoneEffect || effect instanceof ChangeZoneAllEffect) { if (api.equals(ApiType.ChangeZone) || api.equals(ApiType.ChangeZoneAll)) {
AbilityFactory.adjustChangeZoneTarget(mapParams, this); AbilityFactory.adjustChangeZoneTarget(mapParams, this);
} }
} }

View File

@@ -237,19 +237,101 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
view.updateDescription(this); //description can change if host card does view.updateDescription(this); //description can change if host card does
} }
public boolean canThisProduce(final String s) {
AbilityManaPart mp = getManaPart();
if (mp != null && metConditions() && mp.canProduce(s, this)) {
return true;
}
return false;
}
public boolean canProduce(final String s) {
if (canThisProduce(s)) {
return true;
}
return this.subAbility != null ? this.subAbility.canProduce(s) : false;
}
public boolean isManaAbilityFor(SpellAbility saPaidFor, byte colorNeeded) {
// is root ability
if (this.getParent() == null) {
if (!canPlay()) {
return false;
}
if (isAbility() && getRestrictions().isInstantSpeed()) {
return false;
}
}
AbilityManaPart mp = getManaPart();
if (mp != null && metConditions() && mp.meetsManaRestrictions(saPaidFor) && mp.abilityProducesManaColor(this, colorNeeded)) {
return true;
}
return this.subAbility != null ? this.subAbility.isManaAbilityFor(saPaidFor, colorNeeded) : false;
}
public boolean isManaCannotCounter(SpellAbility saPaidFor) {
AbilityManaPart mp = getManaPart();
if (mp != null && metConditions() && mp.meetsManaRestrictions(saPaidFor) && mp.cannotCounterPaidWith(saPaidFor)) {
return true;
}
return this.subAbility != null ? this.subAbility.isManaCannotCounter(saPaidFor) : false;
}
public int amountOfManaGenerated(boolean multiply) {
int result = 0;
AbilityManaPart mp = getManaPart();
if (mp != null && metConditions()) {
int amount = hasParam("Amount") ? AbilityUtils.calculateAmount(getHostCard(), getParam("Amount"), this) : 1;
if (!multiply || mp.isAnyMana() || mp.isComboMana() || mp.isSpecialMana()) {
result += amount;
} else {
// For cards that produce like {C}{R} vs cards that produce {R}{R}.
result += mp.mana().split(" ").length * amount;
}
}
return result;
}
public int totalAmountOfManaGenerated(SpellAbility saPaidFor, boolean multiply) {
int result = 0;
AbilityManaPart mp = getManaPart();
if (mp != null && metConditions() && mp.meetsManaRestrictions(saPaidFor)) {
result += amountOfManaGenerated(multiply);
}
result += subAbility != null ? subAbility.totalAmountOfManaGenerated(saPaidFor, multiply) : 0;
return result;
}
public void setManaExpressChoice(ColorSet cs) {
AbilityManaPart mp = getManaPart();
if (mp != null) {
mp.setExpressChoice(cs);
}
if (subAbility != null) {
subAbility.setManaExpressChoice(cs);
}
}
public final AbilityManaPart getManaPart() { public final AbilityManaPart getManaPart() {
return manaPart; return manaPart;
} }
public final AbilityManaPart getManaPartRecursive() { public final List<AbilityManaPart> getAllManaParts() {
SpellAbility tail = this; AbilityManaPart mp = getManaPart();
while (tail != null) { if (mp == null && subAbility == null) {
if (tail.manaPart != null) { return ImmutableList.of();
return tail.manaPart;
} }
tail = tail.getSubAbility(); List<AbilityManaPart> result = Lists.newArrayList();
if (mp != null) {
result.add(mp);
} }
return null; if (subAbility != null) {
result.addAll(subAbility.getAllManaParts());
}
return result;
} }
public final boolean isManaAbility() { public final boolean isManaAbility() {
@@ -266,7 +348,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return false; return false;
} }
return getManaPartRecursive() != null; SpellAbility tail = this;
while (tail != null) {
if (tail.manaPart != null) {
return true;
}
tail = tail.getSubAbility();
}
return false;
} }
protected final void setManaPart(AbilityManaPart manaPart0) { protected final void setManaPart(AbilityManaPart manaPart0) {
@@ -464,6 +553,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
conditions = condition; conditions = condition;
} }
public boolean metConditions() {
return getConditions() != null && getConditions().areMet(this);
}
public List<Mana> getPayingMana() { public List<Mana> getPayingMana() {
return payingMana; return payingMana;
} }
@@ -1880,9 +1973,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public boolean tracksManaSpent() { public boolean tracksManaSpent() {
if (hostCard == null || hostCard.getRules() == null) { return false; } if (hostCard == null || hostCard.getRules() == null) { return false; }
if (hostCard.hasKeyword(Keyword.SUNBURST)) { if (isSpell() && hostCard.hasConverge()) {
return true; return true;
} }
String text = hostCard.getRules().getOracleText(); String text = hostCard.getRules().getOracleText();
if (isSpell() && text.contains("was spent to cast")) { if (isSpell() && text.contains("was spent to cast")) {
return true; return true;
@@ -2019,7 +2113,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
String mana = manaPart.mana(); String mana = manaPart.mana();
if (!mana.equals("Any")) { if (!mana.equals("Any")) {
score += mana.length(); score += mana.length();
if (!manaPart.canProduce("C")) { if (!canProduce("C")) {
// Producing colorless should produce a slightly lower score // Producing colorless should produce a slightly lower score
score += 1; score += 1;
} }

View File

@@ -169,13 +169,7 @@ public class TriggerHandler {
if (wt.getTriggers() != null) if (wt.getTriggers() != null)
continue; continue;
List<Trigger> trigger = Lists.newArrayList(); wt.setTriggers(getActiveTrigger(wt.getMode(), wt.getParams()));
for (final Trigger t : activeTriggers) {
if (canRunTrigger(t,wt.getMode(),wt.getParams())) {
trigger.add(t);
}
}
wt.setTriggers(trigger);
} }
} }
@@ -678,4 +672,14 @@ public class TriggerHandler {
return n; return n;
} }
public List<Trigger> getActiveTrigger(final TriggerType mode, final Map<AbilityKey, Object> runParams) {
List<Trigger> trigger = Lists.newArrayList();
for (final Trigger t : activeTriggers) {
if (canRunTrigger(t, mode, runParams)) {
trigger.add(t);
}
}
return trigger;
}
} }

View File

@@ -20,7 +20,6 @@ package forge.game.trigger;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.util.Localizer; import forge.util.Localizer;
@@ -68,25 +67,19 @@ public class TriggerTapsForMana extends Trigger {
} }
if (hasParam("ValidCard")) { if (hasParam("ValidCard")) {
final Card tapper = (Card) runParams.get(AbilityKey.Card); if (!matchesValid(runParams.get(AbilityKey.Card), getParam("ValidCard").split(","), getHostCard())) {
if (!tapper.isValid(getParam("ValidCard").split(","), this.getHostCard().getController(),
this.getHostCard(), null)) {
return false; return false;
} }
} }
if (hasParam("Player")) { if (hasParam("Player")) {
final Player player = (Player) runParams.get(AbilityKey.Player); if (!matchesValid(runParams.get(AbilityKey.Player), getParam("Player").split(","), getHostCard())) {
if (!player.isValid(getParam("Player").split(","), this.getHostCard().getController(), this.getHostCard(), null)) {
return false; return false;
} }
} }
if (hasParam("Activator")) { if (hasParam("Activator")) {
final SpellAbility sa = (SpellAbility) runParams.get(AbilityKey.AbilityMana); if (!matchesValid(runParams.get(AbilityKey.Activator), getParam("Activator").split(","), getHostCard())) {
if (sa == null) return false;
final Player activator = sa.getActivatingPlayer();
if (!activator.isValid(getParam("Activator").split(","), this.getHostCard().getController(), this.getHostCard(), null)) {
return false; return false;
} }
} }
@@ -113,7 +106,7 @@ public class TriggerTapsForMana extends Trigger {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) { public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card, AbilityKey.Player, AbilityKey.Produced); sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card, AbilityKey.Player, AbilityKey.Produced, AbilityKey.Activator);
} }
@Override @Override

View File

@@ -4,6 +4,5 @@ Types:Scheme
T:Mode$ SetInMotion | ValidCard$ Card.Self | Execute$ DarkEffect | TriggerZones$ Command | TriggerDescription$ When you set this scheme in motion, until your next turn, whenever a player taps a land for mana, that player adds one mana of any type that land produced. T:Mode$ SetInMotion | ValidCard$ Card.Self | Execute$ DarkEffect | TriggerZones$ Command | TriggerDescription$ When you set this scheme in motion, until your next turn, whenever a player taps a land for mana, that player adds one mana of any type that land produced.
SVar:DarkEffect:DB$ Effect | Name$ Dark Power Scheme | Duration$ UntilYourNextTurn | Triggers$ DarkPower | SVars$ DarkMana SVar:DarkEffect:DB$ Effect | Name$ Dark Power Scheme | Duration$ UntilYourNextTurn | Triggers$ DarkPower | SVars$ DarkMana
SVar:DarkPower:Mode$ TapsForMana | ValidCard$ Land | Execute$ DarkMana | TriggerZones$ Command | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced. SVar:DarkPower:Mode$ TapsForMana | ValidCard$ Land | Execute$ DarkMana | TriggerZones$ Command | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced.
SVar:DarkMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:DarkMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
SVar:Picture:https://downloads.cardforge.org/images/cards/ARC/A Display of My Dark Power.full.jpg
Oracle:When you set this scheme in motion, until your next turn, whenever a player taps a land for mana, that player adds one mana of any type that land produced. Oracle:When you set this scheme in motion, until your next turn, whenever a player taps a land for mana, that player adds one mana of any type that land produced.

View File

@@ -4,7 +4,6 @@ Types:Artifact
K:ETBReplacement:Other:ChooseColor K:ETBReplacement:Other:ChooseColor
SVar:ChooseColor:DB$ ChooseColor | Defined$ You | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a color. SVar:ChooseColor:DB$ ChooseColor | Defined$ You | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a color.
S:Mode$ Continuous | Affected$ Creature.ChosenColor+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control of the chosen color get +1/+1. S:Mode$ Continuous | Affected$ Creature.ChosenColor+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control of the chosen color get +1/+1.
T:Mode$ TapsForMana | ValidCard$ Land | Produced$ ChosenColor | NoTapCheck$ True | Player$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a land's ability adds one or more mana of the chosen color, add one additional mana of that color. T:Mode$ TapsForMana | ValidCard$ Land | Produced$ ChosenColor | NoTapCheck$ True | Player$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a lands ability causes you to add one or more mana of the chosen color, add one additional mana of that color.
SVar:TrigMana:DB$ Mana | Produced$ Chosen | Amount$ 1 | Defined$ TriggeredPlayer SVar:TrigMana:DB$ Mana | Produced$ Chosen | Amount$ 1 | Defined$ You
SVar:Picture:http://www.wizards.com/global/images/magic/general/caged_sun.jpg Oracle:As Caged Sun enters the battlefield, choose a color.\nCreatures you control of the chosen color get +1/+1.\nWhenever a lands ability causes you to add one or more mana of the chosen color, add one additional mana of that color.
Oracle:As Caged Sun enters the battlefield, choose a color.\nCreatures you control of the chosen color get +1/+1.\nWhenever a land's ability adds one or more mana of the chosen color, add one additional mana of that color.

View File

@@ -7,8 +7,8 @@ SVar:DBEffect:DB$ Effect | ReplacementEffects$ RepCurse | SVars$ ProduceColorles
SVar:TrigRamp:Mode$ TapsForMana | ValidCard$ Mountain | Execute$ TrigMana | Static$ True | TriggerZones$ Command | TriggerDescription$ Whenever a player taps a Mountain for mana, that player adds {R}. SVar:TrigRamp:Mode$ TapsForMana | ValidCard$ Mountain | Execute$ TrigMana | Static$ True | TriggerZones$ Command | TriggerDescription$ Whenever a player taps a Mountain for mana, that player adds {R}.
SVar:TrigMana:DB$ Mana | Produced$ R | Amount$ 1 | Defined$ TriggeredCardController SVar:TrigMana:DB$ Mana | Produced$ R | Amount$ 1 | Defined$ TriggeredCardController
SVar:STPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.Red | AddPower$ 1 | AddToughness$ 1 SVar:STPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.Red | AddPower$ 1 | AddToughness$ 1
SVar:RepCurse:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Mountain | ManaReplacement$ ProduceColorless | Description$ If a player taps a Mountain for mana, that Mountain produces colorless mana instead of any other type. SVar:RepCurse:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Mountain | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceColorless | Description$ If a player taps a Mountain for mana, that Mountain produces colorless mana instead of any other type.
SVar:ProduceColorless:R->1 & B->1 & U->1 & G->1 & W->1 SVar:ProduceColorless:DB$ ReplaceMana | ReplaceType$ C
SVar:STCurse:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.Red | AddPower$ -1 | AddToughness$ -1 SVar:STCurse:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.Red | AddPower$ -1 | AddToughness$ -1
SVar:X:Count$Valid Permanent SVar:X:Count$Valid Permanent
AI:RemoveDeck:All AI:RemoveDeck:All

View File

@@ -2,9 +2,8 @@ Name:Contamination
ManaCost:2 B ManaCost:2 B
Types:Enchantment Types:Enchantment
K:UpkeepCost:Sac<1/Creature> K:UpkeepCost:Sac<1/Creature>
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ManaReplacement$ ProduceB | Description$ If a land is tapped for mana, it produces {B} instead of any other type and amount. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceB | Description$ If a land is tapped for mana, it produces {B} instead of any other type and amount.
SVar:ProduceB:Any->B SVar:ProduceB:DB$ ReplaceMana | ReplaceMana$ B
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/contamination.jpg
Oracle:At the beginning of your upkeep, sacrifice Contamination unless you sacrifice a creature.\nIf a land is tapped for mana, it produces {B} instead of any other type and amount. Oracle:At the beginning of your upkeep, sacrifice Contamination unless you sacrifice a creature.\nIf a land is tapped for mana, it produces {B} instead of any other type and amount.

View File

@@ -3,7 +3,6 @@ ManaCost:3 B
Types:Creature Spirit Types:Creature Spirit
PT:2/2 PT:2/2
K:Extort K:Extort
T:Mode$ TapsForMana | ValidCard$ Swamp.YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a Swamp for mana, add an additional {B}. T:Mode$ TapsForMana | ValidCard$ Swamp | Activator$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a Swamp for mana, add an additional {B}.
SVar:TrigMana:DB$Mana | Produced$ B | Amount$ 1 SVar:TrigMana:DB$ Mana | Produced$ B | Amount$ 1
SVar:Picture:http://www.wizards.com/global/images/magic/general/crypt_ghast.jpg
Oracle:Extort (Whenever you cast a spell, you may pay {W/B}. If you do, each opponent loses 1 life and you gain that much life.)\nWhenever you tap a Swamp for mana, add an additional {B}. Oracle:Extort (Whenever you cast a spell, you may pay {W/B}. If you do, each opponent loses 1 life and you gain that much life.)\nWhenever you tap a Swamp for mana, add an additional {B}.

View File

@@ -1,10 +1,9 @@
Name:Damping Sphere Name:Damping Sphere
ManaCost:2 ManaCost:2
Types:Artifact Types:Artifact
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ManaAmount$ GE2 | ManaReplacement$ ProduceC | Description$ If a land is tapped for two or more mana, it produces {C} instead of any other type and amount. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ValidAbility$ Activated.hasTapCost | ManaAmount$ GE2 | ReplaceWith$ ProduceC | Description$ If a land is tapped for two or more mana, it produces {C} instead of any other type and amount.
SVar:ProduceC:Any->C SVar:ProduceC:DB$ ReplaceMana | ReplaceMana$ C
S:Mode$ RaiseCost | Activator$ Player | Type$ Spell | Amount$ X | AffectedAmount$ True | Description$ Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn. S:Mode$ RaiseCost | Activator$ Player | Type$ Spell | Amount$ X | AffectedAmount$ True | Description$ Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn.
SVar:X:Count$ThisTurnCast_Card.YouCtrl SVar:X:Count$ThisTurnCast_Card.YouCtrl
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/damping_sphere.jpg
Oracle:If a land is tapped for two or more mana, it produces {C} instead of any other type and amount.\nEach spell a player casts costs {1} more to cast for each other spell that player has cast this turn. Oracle:If a land is tapped for two or more mana, it produces {C} instead of any other type and amount.\nEach spell a player casts costs {1} more to cast for each other spell that player has cast this turn.

View File

@@ -2,9 +2,8 @@ Name:Deep Water
ManaCost:U U ManaCost:U U
Types:Enchantment Types:Enchantment
A:AB$ Effect | Cost$ U | ReplacementEffects$ ReplaceU | SVars$ ProduceU | SpellDescription$ Until end of turn, if you tap a land you control for mana, it produces {U} instead of any other type. A:AB$ Effect | Cost$ U | ReplacementEffects$ ReplaceU | SVars$ ProduceU | SpellDescription$ Until end of turn, if you tap a land you control for mana, it produces {U} instead of any other type.
SVar:ReplaceU:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Land.YouCtrl | ManaReplacement$ ProduceU | Description$ If you tap a land you control for mana, it produces U instead of any other type. SVar:ReplaceU:Event$ ProduceMana | ActiveZones$ Command | ValidPlayer$ You | ValidCard$ Land.YouCtrl | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceU | Description$ If you tap a land you control for mana, it produces U instead of any other type.
SVar:ProduceU:C->U & B->U & R->U & G->U & W->U SVar:ProduceU:DB$ ReplaceMana | ReplaceType$ U
AI:RemoveDeck:All AI:RemoveDeck:All
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/deep_water.jpg
Oracle:{U}: Until end of turn, if you tap a land you control for mana, it produces {U} instead of any other type. Oracle:{U}: Until end of turn, if you tap a land you control for mana, it produces {U} instead of any other type.

View File

@@ -3,6 +3,5 @@ ManaCost:3 G G
Types:Enchantment Types:Enchantment
K:Flash K:Flash
T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
SVar:Picture:http://www.wizards.com/global/images/magic/general/dictate_of_karametra.jpg
Oracle:Flash\nWhenever a player taps a land for mana, that player adds one mana of any type that land produced. Oracle:Flash\nWhenever a player taps a land for mana, that player adds one mana of any type that land produced.

View File

@@ -1,7 +1,6 @@
Name:Doubling Cube Name:Doubling Cube
ManaCost:2 ManaCost:2
Types:Artifact Types:Artifact
A:AB$ Mana | Cost$ 3 T | DoubleManaInPool$ True | ProduceNoOtherMana$ True | SpellDescription$ Double the amount of each type of unspent mana you have. A:AB$ Mana | Cost$ 3 T | Produced$ Special DoubleManaInPool | SpellDescription$ Double the amount of each type of unspent mana you have.
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/doubling_cube.jpg
Oracle:{3}, {T}: Double the amount of each type of unspent mana you have. Oracle:{3}, {T}: Double the amount of each type of unspent mana you have.

View File

@@ -2,7 +2,7 @@ Name:Eloren Wilds
ManaCost:no cost ManaCost:no cost
Types:Plane Shandalar Types:Plane Shandalar
T:Mode$ TapsForMana | ValidCard$ Permanent | Execute$ TrigMana | TriggerZones$ Command | Static$ True | TriggerDescription$ Whenever a player taps a permanent for mana, that player adds one mana of any type that permanent produced. T:Mode$ TapsForMana | ValidCard$ Permanent | Execute$ TrigMana | TriggerZones$ Command | Static$ True | TriggerDescription$ Whenever a player taps a permanent for mana, that player adds one mana of any type that permanent produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, target player can't cast spells until a player planeswalks. T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, target player can't cast spells until a player planeswalks.
SVar:RolledChaos:DB$ Effect | ValidTgts$ Player | IsCurse$ True | Name$ Eloren Wilds Effect | StaticAbilities$ STCantCast | Triggers$ TrigPlaneswalk | SVars$ ExileSelf | RememberObjects$ Targeted | Duration$ Permanent SVar:RolledChaos:DB$ Effect | ValidTgts$ Player | IsCurse$ True | Name$ Eloren Wilds Effect | StaticAbilities$ STCantCast | Triggers$ TrigPlaneswalk | SVars$ ExileSelf | RememberObjects$ Targeted | Duration$ Permanent
SVar:STCantCast:Mode$ CantBeCast | EffectZone$ Command | ValidCard$ Card | Caster$ Player.IsRemembered | Description$ Target player can't cast spells until a player planeswalks. SVar:STCantCast:Mode$ CantBeCast | EffectZone$ Command | ValidCard$ Card | Caster$ Player.IsRemembered | Description$ Target player can't cast spells until a player planeswalks.

View File

@@ -4,12 +4,11 @@ Types:Artifact
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigExile | OptionalDecider$ You | TriggerDescription$ Imprint — When CARDNAME enters the battlefield, you may exile target land you control. T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigExile | OptionalDecider$ You | TriggerDescription$ Imprint — When CARDNAME enters the battlefield, you may exile target land you control.
SVar:TrigExile:DB$ ChangeZone | Imprint$ True | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Land.YouCtrl | TgtPrompt$ Select a target land you control | AILogic$ ExtraplanarLens SVar:TrigExile:DB$ ChangeZone | Imprint$ True | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Land.YouCtrl | TgtPrompt$ Select a target land you control | AILogic$ ExtraplanarLens
T:Mode$ TapsForMana | ValidCard$ Land.sharesNameWith Imprinted | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a land with the same name as the exiled card is tapped for mana, its controller adds one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Land.sharesNameWith Imprinted | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a land with the same name as the exiled card is tapped for mana, its controller adds one mana of any type that land produced.
SVar:TrigMana:DB$ ManaReflected | Valid$ Defined.Triggered | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredCardController
T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True T:Mode$ ChangesZone | Origin$ Battlefield | ValidCard$ Card.Self | Destination$ Any | Execute$ DBCleanup | Static$ True
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSource | Origin$ Exile | Execute$ DBForget | Static$ True T:Mode$ ChangesZone | ValidCard$ Card.IsImprinted+ExiledWithSource | Origin$ Exile | Execute$ DBForget | Static$ True
SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard SVar:DBForget:DB$ Pump | ForgetImprinted$ TriggeredCard
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/extraplanar_lens.jpg
Oracle:Imprint — When Extraplanar Lens enters the battlefield, you may exile target land you control.\nWhenever a land with the same name as the exiled card is tapped for mana, its controller adds one mana of any type that land produced. Oracle:Imprint — When Extraplanar Lens enters the battlefield, you may exile target land you control.\nWhenever a land with the same name as the exiled card is tapped for mana, its controller adds one mana of any type that land produced.

View File

@@ -3,9 +3,8 @@ ManaCost:1 W
Types:Sorcery Types:Sorcery
A:SP$ Effect | Cost$ 1 W | ReplacementEffects$ FDRep | StaticAbilities$ FDManaConvertion | SVars$ ProduceW | SubAbility$ DBDraw | SpellDescription$ Until end of turn, spells and abilities you control that would add colored mana add that much white mana instead. Until end of turn, you may spend white mana as though it were mana of any color. Draw a card. A:SP$ Effect | Cost$ 1 W | ReplacementEffects$ FDRep | StaticAbilities$ FDManaConvertion | SVars$ ProduceW | SubAbility$ DBDraw | SpellDescription$ Until end of turn, spells and abilities you control that would add colored mana add that much white mana instead. Until end of turn, you may spend white mana as though it were mana of any color. Draw a card.
SVar:DBDraw:DB$ Draw | NumCards$ 1 SVar:DBDraw:DB$ Draw | NumCards$ 1
SVar:FDRep:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Card.YouCtrl | NoTapCheck$ True | ManaReplacement$ ProduceW | Description$ Spells and abilities you control that would add colored mana add that much white mana instead. SVar:FDRep:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Card.YouCtrl | ReplaceWith$ ProduceW | Description$ Spells and abilities you control that would add colored mana add that much white mana instead.
SVar:ProduceW:R->W & B->W & U->W & G->W SVar:ProduceW:DB$ ReplaceMana | ReplaceColor$ W
SVar:FDManaConvertion:Mode$ Continuous | EffectZone$ Command | Affected$ You | ManaColorConversion$ Additive | WhiteConversion$ Color | Description$ You may spend white mana as though it were mana of any color. SVar:FDManaConvertion:Mode$ Continuous | EffectZone$ Command | Affected$ You | ManaColorConversion$ Additive | WhiteConversion$ Color | Description$ You may spend white mana as though it were mana of any color.
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/false_dawn.jpg
Oracle:Until end of turn, spells and abilities you control that would add colored mana add that much white mana instead. Until end of turn, you may spend white mana as though it were mana of any color.\nDraw a card. Oracle:Until end of turn, spells and abilities you control that would add colored mana add that much white mana instead. Until end of turn, you may spend white mana as though it were mana of any color.\nDraw a card.

View File

@@ -2,7 +2,7 @@ Name:Forsaken Monument
ManaCost:5 ManaCost:5
Types:Legendary Artifact Types:Legendary Artifact
S:Mode$ Continuous | Affected$ Creature.Colorless+YouCtrl | AddPower$ 2 | AddToughness$ 2 | Description$ Colorless creatures you control get +2/+2. S:Mode$ Continuous | Affected$ Creature.Colorless+YouCtrl | AddPower$ 2 | AddToughness$ 2 | Description$ Colorless creatures you control get +2/+2.
T:Mode$ TapsForMana | ValidCard$ Permanent.YouCtrl | Produced$ C | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a permanent for {C}, add an additional {C}. T:Mode$ TapsForMana | ValidCard$ Permanent | Activator$ You | Produced$ C | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a permanent for {C}, add an additional {C}.
SVar:TrigMana:DB$ Mana | Produced$ C SVar:TrigMana:DB$ Mana | Produced$ C
T:Mode$ SpellCast | ValidCard$ Card.Colorless | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever you cast a colorless spell, you gain 2 life. T:Mode$ SpellCast | ValidCard$ Card.Colorless | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever you cast a colorless spell, you gain 2 life.
SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 SVar:TrigGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2

View File

@@ -4,8 +4,7 @@ Types:World Enchantment
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player | Execute$ TrigChoose | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of each player's upkeep, that player chooses a color. Until end of turn, lands tapped for mana produce mana of the chosen color instead of any other color. T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player | Execute$ TrigChoose | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of each player's upkeep, that player chooses a color. Until end of turn, lands tapped for mana produce mana of the chosen color instead of any other color.
SVar:TrigChoose:DB$ ChooseColor | Defined$ TriggeredPlayer | AILogic$ MostProminentInActivePlayerHand | SubAbility$ DBEffect SVar:TrigChoose:DB$ ChooseColor | Defined$ TriggeredPlayer | AILogic$ MostProminentInActivePlayerHand | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | ReplacementEffects$ ReplaceChosen | SVars$ ProduceChosen SVar:DBEffect:DB$ Effect | ReplacementEffects$ ReplaceChosen | SVars$ ProduceChosen
SVar:ReplaceChosen:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Land | ManaReplacement$ ProduceChosen | Description$ Lands tapped for mana produce mana of the chosen color instead of any other color. SVar:ReplaceChosen:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Land | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceChosen | Description$ Lands tapped for mana produce mana of the chosen color instead of any other color.
SVar:ProduceChosen:C->Chosen & U->Chosen & B->Chosen & R->Chosen & G->Chosen & W->Chosen SVar:ProduceChosen:DB$ ReplaceMana | ReplaceColor$ Chosen
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/hall_of_gemstone.jpg
Oracle:At the beginning of each player's upkeep, that player chooses a color. Until end of turn, lands tapped for mana produce mana of the chosen color instead of any other color. Oracle:At the beginning of each player's upkeep, that player chooses a color. Until end of turn, lands tapped for mana produce mana of the chosen color instead of any other color.

View File

@@ -3,8 +3,8 @@ ManaCost:G
Types:Creature Human Spellshaper Types:Creature Human Spellshaper
PT:1/1 PT:1/1
A:AB$ Effect | Cost$ G T Discard<1/Card> | ReplacementEffects$ HarvestReplacement | SVars$ HarvestProduce | References$ HarvestReplacement,HarvestProduce | AILogic$ Never | Stackable$ False | SpellDescription$ Until end of turn, if you tap a land for mana, it produces one mana of a color of your choice instead of any other type and amount. A:AB$ Effect | Cost$ G T Discard<1/Card> | ReplacementEffects$ HarvestReplacement | SVars$ HarvestProduce | References$ HarvestReplacement,HarvestProduce | AILogic$ Never | Stackable$ False | SpellDescription$ Until end of turn, if you tap a land for mana, it produces one mana of a color of your choice instead of any other type and amount.
SVar:HarvestReplacement:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Land.YouCtrl | ManaReplacement$ HarvestProduce | Description$ If you tap a land for mana, it produces one mana of a color of your choice instead of any other type and amount. SVar:HarvestReplacement:Event$ ProduceMana | ActiveZones$ Command | ValidPlayer$ You | ValidCard$ Land | ValidAbility$ Activated.hasTapCost | ReplaceWith$ HarvestProduce | Description$ If you tap a land for mana, it produces one mana of a color of your choice instead of any other type and amount.
SVar:HarvestProduce:Any->Any SVar:HarvestProduce:DB$ ReplaceMana | ReplaceMana$ Any
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/harvest_mage.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/harvest_mage.jpg

View File

@@ -2,7 +2,5 @@ Name:Heartbeat of Spring
ManaCost:2 G ManaCost:2 G
Types:Enchantment Types:Enchantment
T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/heartbeat_of_spring.jpg
Oracle:Whenever a player taps a land for mana, that player adds one mana of any type that land produced. Oracle:Whenever a player taps a land for mana, that player adds one mana of any type that land produced.

View File

@@ -2,9 +2,8 @@ Name:Infernal Darkness
ManaCost:2 B B ManaCost:2 B B
Types:Enchantment Types:Enchantment
K:Cumulative upkeep:B PayLife<1>:Pay {B} and 1 life. K:Cumulative upkeep:B PayLife<1>:Pay {B} and 1 life.
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ManaReplacement$ ProduceB | Description$ If a land is tapped for mana, it produces {B} instead of any other type. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceB | Description$ If a land is tapped for mana, it produces {B} instead of any other type.
SVar:ProduceB:C->B & U->B & R->B & G->B & W->B SVar:ProduceB:DB$ ReplaceMana | ReplaceType$ B
AI:RemoveDeck:All AI:RemoveDeck:All
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/infernal_darkness.jpg
Oracle:Cumulative upkeep—Pay {B} and 1 life. (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nIf a land is tapped for mana, it produces {B} instead of any other type. Oracle:Cumulative upkeep—Pay {B} and 1 life. (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nIf a land is tapped for mana, it produces {B} instead of any other type.

View File

@@ -3,7 +3,6 @@ ManaCost:3 G
Types:Creature Elf Druid Types:Creature Elf Druid
PT:1/3 PT:1/3
T:Mode$ TapsForMana | ValidCard$ Mountain,Forest,Plains | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a Mountain, Forest, or Plains for mana, that player adds one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Mountain,Forest,Plains | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a Mountain, Forest, or Plains for mana, that player adds one mana of any type that land produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/keeper_of_progenitus.jpg
Oracle:Whenever a player taps a Mountain, Forest, or Plains for mana, that player adds one mana of any type that land produced. Oracle:Whenever a player taps a Mountain, Forest, or Plains for mana, that player adds one mana of any type that land produced.

View File

@@ -2,7 +2,7 @@ Name:Kinnan, Bonder Prodigy
ManaCost:G U ManaCost:G U
Types:Legendary Creature Human Druid Types:Legendary Creature Human Druid
PT:2/2 PT:2/2
T:Mode$ TapsForMana | ValidCard$ Permanent.nonLand+YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced. T:Mode$ TapsForMana | ValidCard$ Permanent.nonLand | Activator$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ You
A:AB$ Dig | Cost$ 5 G U | ForceRevealToController$ True | DigNum$ 5 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Creature.nonHuman | DestinationZone$ Battlefield | RestRandomOrder$ True | SpellDescription$ Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order. A:AB$ Dig | Cost$ 5 G U | ForceRevealToController$ True | DigNum$ 5 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Creature.nonHuman | DestinationZone$ Battlefield | RestRandomOrder$ True | SpellDescription$ Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order.
Oracle:Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced.\n{5}{G}{U}: Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order. Oracle:Whenever you tap a nonland permanent for mana, add one mana of any type that permanent produced.\n{5}{G}{U}: Look at the top five cards of your library. You may put a non-Human creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order.

View File

@@ -2,6 +2,5 @@ Name:Mana Flare
ManaCost:2 R ManaCost:2 R
Types:Enchantment Types:Enchantment
T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
SVar:Picture:http://www.wizards.com/global/images/magic/general/mana_flare.jpg
Oracle:Whenever a player taps a land for mana, that player adds one mana of any type that land produced. Oracle:Whenever a player taps a land for mana, that player adds one mana of any type that land produced.

View File

@@ -1,7 +1,6 @@
Name:Mana Reflection Name:Mana Reflection
ManaCost:4 G G ManaCost:4 G G
Types:Enchantment Types:Enchantment
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Permanent.YouCtrl | ManaReplacement$ ProduceTwice | Description$ If you tap a permanent for mana, it produces twice as much of that mana instead. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidPlayer$ You | ValidCard$ Permanent | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceTwice | Description$ If you tap a permanent for mana, it produces twice as much of that mana instead.
SVar:ProduceTwice:C->C C & R->R R & B->B B & U->U U & G->G G & W->W W SVar:ProduceTwice:DB$ ReplaceMana | ReplaceAmount$ 2
SVar:Picture:http://www.wizards.com/global/images/magic/general/mana_reflection.jpg
Oracle:If you tap a permanent for mana, it produces twice as much of that mana instead. Oracle:If you tap a permanent for mana, it produces twice as much of that mana instead.

View File

@@ -2,7 +2,6 @@ Name:Mirari's Wake
ManaCost:3 G W ManaCost:3 G W
Types:Enchantment Types:Enchantment
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control get +1/+1. S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control get +1/+1.
T:Mode$ TapsForMana | ValidCard$ Land.YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Land | Activator$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced.
SVar:TrigMana:DB$ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ You
SVar:Picture:http://www.wizards.com/global/images/magic/general/miraris_wake.jpg
Oracle:Creatures you control get +1/+1.\nWhenever you tap a land for mana, add one mana of any type that land produced. Oracle:Creatures you control get +1/+1.\nWhenever you tap a land for mana, add one mana of any type that land produced.

View File

@@ -2,8 +2,8 @@ Name:Mirri
ManaCost:no cost ManaCost:no cost
Types:Vanguard Types:Vanguard
HandLifeModifier:+0/+5 HandLifeModifier:+0/+5
R:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Land.Basic+YouCtrl | ManaReplacement$ ProduceAny | Description$ If a basic land you control is tapped for mana, it produces mana of a color of your choice instead of any other type. R:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Land.Basic+YouCtrl | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceAny | Description$ If a basic land you control is tapped for mana, it produces mana of a color of your choice instead of any other type.
SVar:ProduceAny:C->Any & B->Any & R->Any & G->Any & W->Any & U->Any SVar:ProduceAny:DB$ ReplaceMana | ReplaceType$ Any
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:Picture:https://downloads.cardforge.org/images/cards/VAN/Mirri.full.jpg SVar:Picture:https://downloads.cardforge.org/images/cards/VAN/Mirri.full.jpg
Oracle:Hand +0, life +5\nIf a basic land you control is tapped for mana, it produces mana of a color of your choice instead of any other type. Oracle:Hand +0, life +5\nIf a basic land you control is tapped for mana, it produces mana of a color of your choice instead of any other type.

View File

@@ -3,17 +3,16 @@ ManaCost:5
Types:Artifact Types:Artifact
Text:If tapped for mana, Plains produce {R}, Islands produce {G}, Swamps produce {W}, Mountains produce {U}, and Forests produce {B} instead of any other type. Text:If tapped for mana, Plains produce {R}, Islands produce {G}, Swamps produce {W}, Mountains produce {U}, and Forests produce {B} instead of any other type.
K:Cumulative upkeep:3 K:Cumulative upkeep:3
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Plains | ManaReplacement$ ProduceR | Secondary$ True | Description$ If tapped for mana, Plains produce R. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Plains | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceR | Secondary$ True | Description$ If tapped for mana, Plains produce R.
SVar:ProduceR:C->R & B->R & U->R & G->R & W->R SVar:ProduceR:DB$ ReplaceMana | ReplaceType$ R
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Island | ManaReplacement$ ProduceG | Secondary$ True | Description$ If tapped for mana, Islands produce G. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Island | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceG | Secondary$ True | Description$ If tapped for mana, Islands produce G.
SVar:ProduceG:C->G & B->G & U->G & R->G & W->G SVar:ProduceG:DB$ ReplaceMana | ReplaceType$ G
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Swamp | ManaReplacement$ ProduceW | Secondary$ True | Description$ If tapped for mana, Swamps produce W. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Swamp | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceW | Secondary$ True | Description$ If tapped for mana, Swamps produce W.
SVar:ProduceW:C->W & B->W & U->W & R->W & G->W SVar:ProduceW:DB$ ReplaceMana | ReplaceType$ W
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Mountain | ManaReplacement$ ProduceU | Secondary$ True | Description$ If tapped for mana, Mountains produce U. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Mountain | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceU | Secondary$ True | Description$ If tapped for mana, Mountains produce U.
SVar:ProduceU:C->U & B->U & G->U & R->U & W->U SVar:ProduceU:DB$ ReplaceMana | ReplaceType$ U
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Forest | ManaReplacement$ ProduceB | Secondary$ True | Description$ If tapped for mana, Forests produce B. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Forest | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceB | Secondary$ True | Description$ If tapped for mana, Forests produce B.
SVar:ProduceB:C->B & G->B & U->B & R->B & W->B SVar:ProduceB:DB$ ReplaceMana | ReplaceType$ B
AI:RemoveDeck:All AI:RemoveDeck:All
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/naked_singularity.jpg
Oracle:Cumulative upkeep {3} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nIf tapped for mana, Plains produce {R}, Islands produce {G}, Swamps produce {W}, Mountains produce {U}, and Forests produce {B} instead of any other type. Oracle:Cumulative upkeep {3} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nIf tapped for mana, Plains produce {R}, Islands produce {G}, Swamps produce {W}, Mountains produce {U}, and Forests produce {B} instead of any other type.

View File

@@ -3,6 +3,6 @@ ManaCost:3 R G
Types:Legendary Creature Centaur Druid Types:Legendary Creature Centaur Druid
PT:5/5 PT:5/5
S:Mode$ CantBeCast | ValidCard$ Card.nonCreature | Caster$ You | Description$ You can't cast noncreature spells. S:Mode$ CantBeCast | ValidCard$ Card.nonCreature | Caster$ You | Description$ You can't cast noncreature spells.
T:Mode$ TapsForMana | ValidCard$ Land.YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Land | Activator$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced.
SVar:TrigMana:DB$ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ You
Oracle:You can't cast noncreature spells.\nWhenever you tap a land for mana, add one mana of any type that land produced. Oracle:You can't cast noncreature spells.\nWhenever you tap a land for mana, add one mana of any type that land produced.

View File

@@ -3,7 +3,6 @@ ManaCost:4 B B
Types:Creature Vampire Shade Types:Creature Vampire Shade
PT:4/4 PT:4/4
A:AB$ Pump | Cost$ B | NumAtt$ +1 | NumDef$ +1 | SpellDescription$ CARDNAME gets +1/+1 until end of turn. A:AB$ Pump | Cost$ B | NumAtt$ +1 | NumDef$ +1 | SpellDescription$ CARDNAME gets +1/+1 until end of turn.
T:Mode$ TapsForMana | ValidCard$ Swamp.YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a Swamp for mana, add an additional {B}. T:Mode$ TapsForMana | ValidCard$ Swamp | Activator$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a Swamp for mana, add an additional {B}.
SVar:TrigMana:DB$Mana | Produced$ B | Amount$ 1 SVar:TrigMana:DB$ Mana | Produced$ B | Amount$ 1
SVar:Picture:http://www.wizards.com/global/images/magic/general/nirkana_revenant.jpg
Oracle:Whenever you tap a Swamp for mana, add an additional {B}.\n{B}: Nirkana Revenant gets +1/+1 until end of turn. Oracle:Whenever you tap a Swamp for mana, add an additional {B}.\n{B}: Nirkana Revenant gets +1/+1 until end of turn.

View File

@@ -2,8 +2,8 @@ Name:Nissa, Who Shakes the World
ManaCost:3 G G ManaCost:3 G G
Types:Legendary Planeswalker Nissa Types:Legendary Planeswalker Nissa
Loyalty:5 Loyalty:5
T:Mode$ TapsForMana | ValidCard$ Forest.YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a Forest for mana, add an additional {G}. T:Mode$ TapsForMana | ValidCard$ Forest | Activator$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a Forest for mana, add an additional {G}.
SVar:TrigMana:DB$Mana | Produced$ G | Amount$ 1 SVar:TrigMana:DB$ Mana | Produced$ G | Amount$ 1
A:AB$ PutCounter | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | CounterType$ P1P1 | CounterNum$ 3 | ValidTgts$ Land.nonCreature+YouCtrl | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target noncreature land you control | SubAbility$ DBUntap | SpellDescription$ Put three +1/+1 counters on up to one target noncreature land you control. Untap it. It becomes a 0/0 Elemental creature with vigilance and haste that's still a land. A:AB$ PutCounter | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | CounterType$ P1P1 | CounterNum$ 3 | ValidTgts$ Land.nonCreature+YouCtrl | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target noncreature land you control | SubAbility$ DBUntap | SpellDescription$ Put three +1/+1 counters on up to one target noncreature land you control. Untap it. It becomes a 0/0 Elemental creature with vigilance and haste that's still a land.
SVar:DBUntap:DB$ Untap | Defined$ Targeted | SubAbility$ DBAnimate SVar:DBUntap:DB$ Untap | Defined$ Targeted | SubAbility$ DBAnimate
SVar:DBAnimate:DB$ Animate | Defined$ Targeted | Power$ 0 | Toughness$ 0 | Types$ Creature,Elemental | Keywords$ Vigilance & Haste | Permanent$ True SVar:DBAnimate:DB$ Animate | Defined$ Targeted | Power$ 0 | Toughness$ 0 | Types$ Creature,Elemental | Keywords$ Vigilance & Haste | Permanent$ True

View File

@@ -3,6 +3,6 @@ ManaCost:4 G G G
Types:Enchantment Creature Elemental Types:Enchantment Creature Elemental
PT:5/5 PT:5/5
K:Trample K:Trample
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Permanent.YouCtrl | ManaReplacement$ ProduceThrice | Description$ If you tap a permanent for mana, it produces three times as much of that mana instead. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Permanent.YouCtrl | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceThrice | Description$ If you tap a permanent for mana, it produces three times as much of that mana instead.
SVar:ProduceThrice:C->C C C & R->R R R & B->B B B & U->U U U & G->G G G & W->W W W SVar:ProduceThrice:DB$ ReplaceMana | ReplaceAmount$ 3
Oracle:Trample\nIf you tap a permanent for mana, it produces three times as much of that mana instead. Oracle:Trample\nIf you tap a permanent for mana, it produces three times as much of that mana instead.

View File

@@ -3,7 +3,6 @@ ManaCost:1 R G
Types:Enchantment Types:Enchantment
T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigDmg | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced, and CARDNAME deals 1 damage to them. T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigDmg | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced, and CARDNAME deals 1 damage to them.
SVar:TrigDmg:DB$ DealDamage | Defined$ TriggeredCardController | NumDmg$ 1 | SubAbility$ DBMana SVar:TrigDmg:DB$ DealDamage | Defined$ TriggeredCardController | NumDmg$ 1 | SubAbility$ DBMana
SVar:DBMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredCardController SVar:DBMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/overabundance.jpg
Oracle:Whenever a player taps a land for mana, that player adds one mana of any type that land produced, and Overabundance deals 1 damage to them. Oracle:Whenever a player taps a land for mana, that player adds one mana of any type that land produced, and Overabundance deals 1 damage to them.

View File

@@ -2,9 +2,8 @@ Name:Pale Moon
ManaCost:1 U ManaCost:1 U
Types:Instant Types:Instant
A:SP$ Effect | Cost$ 1 U | ReplacementEffects$ ReplaceColorless | SVars$ ProduceColorless | SpellDescription$ Until end of turn, if a player taps a nonbasic land for mana, it produces colorless mana instead of any other type. A:SP$ Effect | Cost$ 1 U | ReplacementEffects$ ReplaceColorless | SVars$ ProduceColorless | SpellDescription$ Until end of turn, if a player taps a nonbasic land for mana, it produces colorless mana instead of any other type.
SVar:ReplaceColorless:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Land.nonBasic | ManaReplacement$ ProduceColorless | Description$ If a player taps a nonbasic land for mana, it produces colorless mana instead of any other type. SVar:ReplaceColorless:Event$ ProduceMana | ActiveZones$ Command | ValidCard$ Land.nonBasic | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceColorless | Description$ If a player taps a nonbasic land for mana, it produces colorless mana instead of any other type.
SVar:ProduceColorless:U->1 & B->1 & R->1 & G->1 & W->1 SVar:ProduceColorless:DB$ ReplaceMana | ReplaceType$ C
AI:RemoveDeck:All AI:RemoveDeck:All
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/pale_moon.jpg
Oracle:Until end of turn, if a player taps a nonbasic land for mana, it produces colorless mana instead of any other type. Oracle:Until end of turn, if a player taps a nonbasic land for mana, it produces colorless mana instead of any other type.

View File

@@ -1,8 +1,8 @@
Name:Pulse of Llanowar Name:Pulse of Llanowar
ManaCost:3 G ManaCost:3 G
Types:Enchantment Types:Enchantment
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land.Basic+YouCtrl | ManaReplacement$ ProduceAny | Description$ If a basic land you control is tapped for mana, it produces mana of a color of your choice instead of any other type. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land.Basic+YouCtrl | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceAny | Description$ If a basic land you control is tapped for mana, it produces mana of a color of your choice instead of any other type.
SVar:ProduceAny:C->Any & R->Any & B->Any & U->Any & G->Any & W->Any SVar:ProduceAny:DB$ ReplaceMana | ReplaceType$ Any
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/pulse_of_llanowar.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/pulse_of_llanowar.jpg

View File

@@ -2,9 +2,9 @@ Name:Quarum Trench Gnomes
ManaCost:3 R ManaCost:3 R
Types:Creature Gnome Types:Creature Gnome
PT:1/1 PT:1/1
A:AB$ Animate | Cost$ T | ValidTgts$ Plains | IsCurse$ True | TgtPrompt$ Choose target plains | Replacements$ QuarumReplacement | sVars$ QuarumProduce | Permanent$ True | StackDescription$ If target {c:Targeted} is tapped for mana, it produces colorless mana instead of white mana. (This effect lasts indefinitely.) | SpellDescription$ If target Plains is tapped for mana, it produces colorless mana instead of white mana. (This effect lasts indefinitely.) A:AB$ Effect | Cost$ T | ValidTgts$ Plains | IsCurse$ True | TgtPrompt$ Choose target plains | RememberObjects$ Targeted | ForgetOnMoved$ Battlefield | Duration$ Permanent | ReplacementEffects$ QuarumReplacement | SVars$ QuarumProduce | StackDescription$ If target {c:Targeted} is tapped for mana, it produces colorless mana instead of white mana. (This effect lasts indefinitely.) | SpellDescription$ If target Plains is tapped for mana, it produces colorless mana instead of white mana. (This effect lasts indefinitely.)
SVar:QuarumReplacement:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Card.Plains+Self | ManaReplacement$ QuarumProduce | Description$ If CARDNAME is tapped for mana, it produces colorless mana instead of white mana. SVar:QuarumReplacement:Event$ ProduceMana | ValidCard$ Card.IsRemembered | ReplaceWith$ QuarumProduce | Description$ If this Land is tapped for mana, it produces colorless mana instead of white mana.
SVar:QuarumProduce:W->1 SVar:QuarumProduce:DB$ ReplaceMana | ReplaceColor$ C | ReplaceOnly$ W
AI:RemoveDeck:All AI:RemoveDeck:All
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/quarum_trench_gnomes.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/quarum_trench_gnomes.jpg

View File

@@ -3,16 +3,15 @@ ManaCost:U U U
Types:Enchantment Types:Enchantment
Text:If tapped for mana, Plains produce {R}, Swamps produce {G}, Mountains produce {W}, and Forests produce {B} instead of any other type. Text:If tapped for mana, Plains produce {R}, Swamps produce {G}, Mountains produce {W}, and Forests produce {B} instead of any other type.
K:Cumulative upkeep:1 U U K:Cumulative upkeep:1 U U
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Plains | ManaReplacement$ ProduceR | Secondary$ True | Description$ If tapped for mana, Plains produce R. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Plains | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceR | Secondary$ True | Description$ If tapped for mana, Plains produce R.
SVar:ProduceR:C->R & B->R & U->R & G->R & W->R SVar:ProduceR:DB$ ReplaceMana | ReplaceType$ R
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Swamp | ManaReplacement$ ProduceG | Secondary$ True | Description$ If tapped for mana, Swamps produce G. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Swamp | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceG | Secondary$ True | Description$ If tapped for mana, Swamps produce G.
SVar:ProduceG:C->G & B->G & U->G & R->G & W->G SVar:ProduceG:DB$ ReplaceMana | ReplaceType$ G
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Mountain | ManaReplacement$ ProduceW | Secondary$ True | Description$ If tapped for mana, Mountains produce U. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Mountain | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceW | Secondary$ True | Description$ If tapped for mana, Mountains produce U.
SVar:ProduceW:C->W & B->W & G->W & R->W & U->W SVar:ProduceW:DB$ ReplaceMana | ReplaceType$ W
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Forest | ManaReplacement$ ProduceB | Secondary$ True | Description$ If tapped for mana, Forests produce B. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Forest | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceB | Secondary$ True | Description$ If tapped for mana, Forests produce B.
SVar:ProduceB:C->B & G->B & U->B & R->B & W->B SVar:ProduceB:DB$ ReplaceMana | ReplaceType$ B
AI:RemoveDeck:All AI:RemoveDeck:All
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/reality_twist.jpg
Oracle:Cumulative upkeep {1}{U}{U} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nIf tapped for mana, Plains produce {R}, Swamps produce {G}, Mountains produce {W}, and Forests produce {B} instead of any other type. Oracle:Cumulative upkeep {1}{U}{U} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nIf tapped for mana, Plains produce {R}, Swamps produce {G}, Mountains produce {W}, and Forests produce {B} instead of any other type.

View File

@@ -5,7 +5,6 @@ PT:5/5
K:Trample K:Trample
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMonarch | TriggerDescription$ When CARDNAME enters the battlefield, you become the monarch. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMonarch | TriggerDescription$ When CARDNAME enters the battlefield, you become the monarch.
SVar:TrigMonarch:DB$ BecomeMonarch | Defined$ You SVar:TrigMonarch:DB$ BecomeMonarch | Defined$ You
T:Mode$ TapsForMana | ValidCard$ Land.YouCtrl | CheckDefinedPlayer$ You.isMonarch | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana while you're the monarch, add an additional one mana of any color. T:Mode$ TapsForMana | ValidCard$ Land | Activator$ You | CheckDefinedPlayer$ You.isMonarch | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana while you're the monarch, add an additional one mana of any color.
SVar:TrigMana:DB$ Mana | Produced$ Combo Any | Amount$ 1 | AILogic$ MostProminentInComputerHand SVar:TrigMana:DB$ Mana | Produced$ Combo Any | Amount$ 1 | AILogic$ MostProminentInComputerHand
SVar:Picture:http://www.wizards.com/global/images/magic/general/regal_behemoth.jpg
Oracle:Trample\nWhen Regal Behemoth enters the battlefield, you become the monarch.\nWhenever you tap a land for mana while you're the monarch, add an additional one mana of any color. Oracle:Trample\nWhen Regal Behemoth enters the battlefield, you become the monarch.\nWhenever you tap a land for mana while you're the monarch, add an additional one mana of any color.

View File

@@ -2,10 +2,9 @@ Name:Ritual of Subdual
ManaCost:4 G G ManaCost:4 G G
Types:Enchantment Types:Enchantment
K:Cumulative upkeep:2 K:Cumulative upkeep:2
R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ManaReplacement$ ProduceColorless | Description$ If a land is tapped for mana, it produces colorless mana instead of any other type. R:Event$ ProduceMana | ActiveZones$ Battlefield | ValidCard$ Land | ValidAbility$ Activated.hasTapCost | ReplaceWith$ ProduceColorless | Description$ If a land is tapped for mana, it produces colorless mana instead of any other type.
SVar:ProduceColorless:B->1 & U->1 & R->1 & G->1 & W->1 SVar:ProduceColorless:DB$ ReplaceMana | ReplaceType$ C
AI:RemoveDeck:All AI:RemoveDeck:All
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/ritual_of_subdual.jpg
Oracle:Cumulative upkeep {2} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nIf a land is tapped for mana, it produces colorless mana instead of any other type. Oracle:Cumulative upkeep {2} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nIf a land is tapped for mana, it produces colorless mana instead of any other type.

View File

@@ -1,8 +1,9 @@
Name:River of Tears Name:River of Tears
ManaCost:no cost ManaCost:no cost
Types:Land Types:Land
A:AB$ Mana | Cost$ T | Produced$ U | ReplaceIfLandPlayed$ B | SpellDescription$ Add {U}. If you played a land this turn, add {B} instead. A:AB$ Mana | Cost$ T | Produced$ U | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ0 | References$ X | SubAbility$ ManaB | SpellDescription$ Add {U}. If you played a land this turn, add {B} instead.
SVar:ManaB:DB$ Mana | Produced$ B | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X
AI:RemoveDeck:Random AI:RemoveDeck:Random
DeckHints:Color$Blue|Black DeckHints:Color$Blue|Black
SVar:Picture:http://www.wizards.com/global/images/magic/general/river_of_tears.jpg SVar:X:Count$YourLandsPlayed
Oracle:{T}: Add {U}. If you played a land this turn, add {B} instead. Oracle:{T}: Add {U}. If you played a land this turn, add {B} instead.

View File

@@ -5,7 +5,6 @@ PT:2/3
A:AB$ SetState | Cost$ Reveal<1/Hand> | Defined$ Self | Mode$ Flip | ConditionCheckSVar$ CheckHandLand | ConditionSVarCompare$ GE7 | AILogic$ CheckCondition | References$ CheckHandLand | SpellDescription$ If you have seven or more land cards in your hand, flip CARDNAME. A:AB$ SetState | Cost$ Reveal<1/Hand> | Defined$ Self | Mode$ Flip | ConditionCheckSVar$ CheckHandLand | ConditionSVarCompare$ GE7 | AILogic$ CheckCondition | References$ CheckHandLand | SpellDescription$ If you have seven or more land cards in your hand, flip CARDNAME.
SVar:CheckHandLand:Count$ValidHand Land.YouCtrl SVar:CheckHandLand:Count$ValidHand Land.YouCtrl
AlternateMode:Flip AlternateMode:Flip
SVar:Picture:http://www.wizards.com/global/images/magic/general/sasaya_orochi_ascendant.jpg
Oracle:Reveal your hand: If you have seven or more land cards in your hand, flip Sasaya, Orochi Ascendant. Oracle:Reveal your hand: If you have seven or more land cards in your hand, flip Sasaya, Orochi Ascendant.
ALTERNATE ALTERNATE
@@ -17,7 +16,6 @@ Types:Legendary Enchantment
T:Mode$ TapsForMana | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigMana | Static$ True | TriggerDescription$ Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigMana | Static$ True | TriggerDescription$ Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana of any type that land produced.
SVar:TrigMana:DB$ Pump | RememberObjects$ TriggeredCard | SubAbility$ DBRepeat SVar:TrigMana:DB$ Pump | RememberObjects$ TriggeredCard | SubAbility$ DBRepeat
SVar:DBRepeat:DB$ RepeatEach | UseImprinted$ True | RepeatCards$ Land.YouCtrl+IsNotRemembered+sharesNameWith Remembered | RepeatSubAbility$ DBManaReflect | SubAbility$ DBCleanup SVar:DBRepeat:DB$ RepeatEach | UseImprinted$ True | RepeatCards$ Land.YouCtrl+IsNotRemembered+sharesNameWith Remembered | RepeatSubAbility$ DBManaReflect | SubAbility$ DBCleanup
SVar:DBManaReflect:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Imprinted | ReflectProperty$ Produced | Defined$ You SVar:DBManaReflect:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ You
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:Picture:http://www.wizards.com/global/images/magic/general/sasayas_essence.jpg
Oracle:Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana of any type that land produced. Oracle:Whenever a land you control is tapped for mana, for each other land you control with the same name, add one mana of any type that land produced.

View File

@@ -4,8 +4,7 @@ Types:Creature Elemental Cat
PT:0/0 PT:0/0
K:etbCounter:P1P1:7 K:etbCounter:P1P1:7
K:Trample K:Trample
T:Mode$ TapsForMana | ValidCard$ Land.YouCtrl | Execute$ TrigRemoveCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you tap a land for mana, remove a +1/+1 counter from CARDNAME. T:Mode$ TapsForMana | ValidCard$ Land | Activator$ You | Execute$ TrigRemoveCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you tap a land for mana, remove a +1/+1 counter from CARDNAME.
SVar:TrigRemoveCounter:DB$RemoveCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 SVar:TrigRemoveCounter:DB$ RemoveCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/savage_firecat.jpg
Oracle:Trample\nSavage Firecat enters the battlefield with seven +1/+1 counters on it.\nWhenever you tap a land for mana, remove a +1/+1 counter from Savage Firecat. Oracle:Trample\nSavage Firecat enters the battlefield with seven +1/+1 counters on it.\nWhenever you tap a land for mana, remove a +1/+1 counter from Savage Firecat.

View File

@@ -9,8 +9,7 @@ SVar:Y:ReplaceCount$TokenNum/Twice
SVar:Z:ReplaceCount$CounterNum/Twice SVar:Z:ReplaceCount$CounterNum/Twice
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, until end of turn, whenever you tap a land for mana, add one mana of any type that land produced. T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, until end of turn, whenever you tap a land for mana, add one mana of any type that land produced.
SVar:RolledChaos:DB$ Effect | AILogic$ Always | Triggers$ TrigTapForMana | SVars$ TrigMana SVar:RolledChaos:DB$ Effect | AILogic$ Always | Triggers$ TrigTapForMana | SVars$ TrigMana
SVar:TrigTapForMana:Mode$ TapsForMana | TriggerZones$ Command | ValidCard$ Land.YouCtrl | Execute$ TrigMana | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced. SVar:TrigTapForMana:Mode$ TapsForMana | TriggerZones$ Command | ValidCard$ Land | Activator$ You | Execute$ TrigMana | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ You
SVar:Picture:http://www.wizards.com/global/images/magic/general/selesnya_loft_gardens.jpg
SVar:AIRollPlanarDieParams:Mode$ Always | MinTurn$ 1 | RollInMain1$ True SVar:AIRollPlanarDieParams:Mode$ Always | MinTurn$ 1 | RollInMain1$ True
Oracle:If an effect would create one or more tokens, it creates twice that many of those tokens instead.\nIf an effect would put one or more counters on a permanent, it puts twice that many of those counters on that permanent instead.\nWhenever you roll {CHAOS}, until end of turn, whenever you tap a land for mana, add one mana of any type that land produced. Oracle:If an effect would create one or more tokens, it creates twice that many of those tokens instead.\nIf an effect would put one or more counters on a permanent, it puts twice that many of those counters on that permanent instead.\nWhenever you roll {CHAOS}, until end of turn, whenever you tap a land for mana, add one mana of any type that land produced.

View File

@@ -2,8 +2,7 @@ Name:Sisay
ManaCost:no cost ManaCost:no cost
Types:Vanguard Types:Vanguard
HandLifeModifier:-2/-3 HandLifeModifier:-2/-3
T:Mode$ TapsForMana | TriggerZones$ Command | ValidCard$ Land.YouCtrl | Execute$ TrigMana | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced. T:Mode$ TapsForMana | TriggerZones$ Command | ValidCard$ Land | Activator$ You | Execute$ TrigMana | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ You
AI:RemoveDeck:All AI:RemoveDeck:All
SVar:Picture:https://downloads.cardforge.org/images/cards/VAN/Sisay.full.jpg
Oracle:Hand -2, life -3\nWhenever you tap a land for mana, add one mana of any type that land produced. Oracle:Hand -2, life -3\nWhenever you tap a land for mana, add one mana of any type that land produced.

View File

@@ -1,7 +1,7 @@
Name:Urza's Mine Name:Urza's Mine
ManaCost:no cost ManaCost:no cost
Types:Land Urza's Mine Types:Land Urza's Mine
A:AB$ Mana | Cost$ T | Produced$ C | Bonus$ UrzaLands | BonusProduced$ 1 | SpellDescription$ Add {C}. If you control an Urza's Power-Plant and an Urza's Tower, add {C}{C} instead. A:AB$ Mana | Cost$ T | Produced$ C | Amount$ UrzaAmount | References$ UrzaAmount | SpellDescription$ Add {C}. If you control an Urza's Power-Plant and an Urza's Tower, add {C}{C} instead.
SVar:UrzaAmount:Count$UrzaLands.2.1
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/urzas_mine.jpg
Oracle:{T}: Add {C}. If you control an Urza's Power-Plant and an Urza's Tower, add {C}{C} instead. Oracle:{T}: Add {C}. If you control an Urza's Power-Plant and an Urza's Tower, add {C}{C} instead.

View File

@@ -1,7 +1,7 @@
Name:Urza's Power Plant Name:Urza's Power Plant
ManaCost:no cost ManaCost:no cost
Types:Land Urza's Power-Plant Types:Land Urza's Power-Plant
A:AB$ Mana | Cost$ T | Produced$ C | Bonus$ UrzaLands | BonusProduced$ 1 | SpellDescription$ Add {C}. If you control an Urza's Mine and an Urza's Tower, add {C}{C} instead. A:AB$ Mana | Cost$ T | Produced$ C | Amount$ UrzaAmount | References$ UrzaAmount | SpellDescription$ Add {C}. If you control an Urza's Mine and an Urza's Tower, add {C}{C} instead.
SVar:UrzaAmount:Count$UrzaLands.2.1
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/urzas_power_plant.jpg
Oracle:{T}: Add {C}. If you control an Urza's Mine and an Urza's Tower, add {C}{C} instead. Oracle:{T}: Add {C}. If you control an Urza's Mine and an Urza's Tower, add {C}{C} instead.

View File

@@ -1,7 +1,7 @@
Name:Urza's Tower Name:Urza's Tower
ManaCost:no cost ManaCost:no cost
Types:Land Urza's Tower Types:Land Urza's Tower
A:AB$ Mana | Cost$ T | Produced$ C | Bonus$ UrzaLands | BonusProduced$ 2 | SpellDescription$ Add {C}. If you control an Urza's Mine and an Urza's Power-Plant, add {C}{C}{C} instead. A:AB$ Mana | Cost$ T | Produced$ C | Amount$ UrzaAmount | References$ UrzaAmount | SpellDescription$ Add {C}. If you control an Urza's Mine and an Urza's Power-Plant, add {C}{C}{C} instead.
SVar:UrzaAmount:Count$UrzaLands.3.1
AI:RemoveDeck:Random AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/urzas_tower.jpg
Oracle:{T}: Add {C}. If you control an Urza's Mine and an Urza's Power-Plant, add {C}{C}{C} instead. Oracle:{T}: Add {C}. If you control an Urza's Mine and an Urza's Power-Plant, add {C}{C}{C} instead.

View File

@@ -0,0 +1,6 @@
Name:Piracy
ManaCost:U U
Types:Sorcery
A:SP$ Effect | Cost$ U U | StaticAbilities$ STPiracy | AINoRecursiveCheck$ True | SpellDescription$ Until end of turn, you may tap lands you dont control for mana. Spend this mana only to cast spells.
SVar:STPiracy:Mode$ Continuous | Affected$ You | AddKeyword$ Piracy | Description$ Until end of turn, you may tap lands you dont control for mana. Spend this mana only to cast spells.
Oracle:Until end of turn, you may tap lands you dont control for mana. Spend this mana only to cast spells.

View File

@@ -3,8 +3,8 @@ ManaCost:6 G G
Types:Legendary Creature Praetor Types:Legendary Creature Praetor
PT:7/6 PT:7/6
K:Trample K:Trample
T:Mode$ TapsForMana | ValidCard$ Land.YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Land | Activator$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ You SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ You
T:Mode$ TapsForMana | ValidCard$ Land.OppCtrl | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever an opponent taps a land for mana, that land doesn't untap during its controller's next untap step. T:Mode$ TapsForMana | ValidCard$ Land.OppCtrl | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever an opponent taps a land for mana, that land doesn't untap during its controller's next untap step.
SVar:TrigPump:DB$ Pump | Defined$ TriggeredCard | Permanent$ True | KW$ HIDDEN This card doesn't untap during your next untap step. SVar:TrigPump:DB$ Pump | Defined$ TriggeredCard | Permanent$ True | KW$ HIDDEN This card doesn't untap during your next untap step.
Oracle:Trample\nWhenever you tap a land for mana, add one mana of any type that land produced.\nWhenever an opponent taps a land for mana, that land doesn't untap during its controller's next untap step. Oracle:Trample\nWhenever you tap a land for mana, add one mana of any type that land produced.\nWhenever an opponent taps a land for mana, that land doesn't untap during its controller's next untap step.

View File

@@ -2,7 +2,7 @@ Name:Winter's Night
ManaCost:R G W ManaCost:R G W
Types:World Enchantment Types:World Enchantment
T:Mode$ TapsForMana | ValidCard$ Land.Snow | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a snow land for mana, that player adds one mana of any type that land produced. That land doesn't untap during its controller's next untap step. T:Mode$ TapsForMana | ValidCard$ Land.Snow | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a snow land for mana, that player adds one mana of any type that land produced. That land doesn't untap during its controller's next untap step.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer | SubAbility$ DBPump SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator | SubAbility$ DBPump
SVar:DBPump:DB$ Pump | Defined$ TriggeredCard | Permanent$ True | KW$ HIDDEN This card doesn't untap during your next untap step. SVar:DBPump:DB$ Pump | Defined$ TriggeredCard | Permanent$ True | KW$ HIDDEN This card doesn't untap during your next untap step.
AI:RemoveDeck:All AI:RemoveDeck:All
AI:RemoveDeck:Random AI:RemoveDeck:Random

View File

@@ -1,9 +1,8 @@
Name:Zendikar Resurgent Name:Zendikar Resurgent
ManaCost:5 G G ManaCost:5 G G
Types:Enchantment Types:Enchantment
T:Mode$ TapsForMana | ValidCard$ Land.YouCtrl | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced. (The types of mana are white, blue, black, red, green, and colorless.) T:Mode$ TapsForMana | ValidCard$ Land | Activator$ You | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever you tap a land for mana, add one mana of any type that land produced. (The types of mana are white, blue, black, red, green, and colorless.)
SVar:TrigMana:DB$ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ You
T:Mode$ SpellCast | ValidCard$ Creature | ValidActivatingPlayer$ You | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a creature spell, draw a card. T:Mode$ SpellCast | ValidCard$ Creature | ValidActivatingPlayer$ You | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a creature spell, draw a card.
SVar:TrigDraw:DB$Draw | Defined$ You | NumCards$ 1 SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1
SVar:Picture:http://www.wizards.com/global/images/magic/general/zendikar_resurgent.jpg
Oracle:Whenever you tap a land for mana, add one mana of any type that land produced. (The types of mana are white, blue, black, red, green, and colorless.)\nWhenever you cast a creature spell, draw a card. Oracle:Whenever you tap a land for mana, add one mana of any type that land produced. (The types of mana are white, blue, black, red, green, and colorless.)\nWhenever you cast a creature spell, draw a card.

View File

@@ -3,6 +3,5 @@ ManaCost:3 R G
Types:Creature Beast Types:Creature Beast
PT:7/5 PT:7/5
T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced. T:Mode$ TapsForMana | ValidCard$ Land | Execute$ TrigMana | TriggerZones$ Battlefield | Static$ True | TriggerDescription$ Whenever a player taps a land for mana, that player adds one mana of any type that land produced.
SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | Valid$ Defined.Triggered | ReflectProperty$ Produced | Defined$ TriggeredPlayer SVar:TrigMana:DB$ ManaReflected | ColorOrType$ Type | ReflectProperty$ Produced | Defined$ TriggeredActivator
SVar:Picture:http://www.wizards.com/global/images/magic/general/zhur_taa_ancient.jpg
Oracle:Whenever a player taps a land for mana, that player adds one mana of any type that land produced. Oracle:Whenever a player taps a land for mana, that player adds one mana of any type that land produced.

View File

@@ -3,8 +3,6 @@ package forge.match.input;
import java.util.*; import java.util.*;
import forge.GuiBase; import forge.GuiBase;
import forge.game.GameActionUtil;
import forge.game.ability.AbilityKey;
import forge.game.spellability.SpellAbilityView; import forge.game.spellability.SpellAbilityView;
import forge.util.TextUtil; import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -18,15 +16,11 @@ import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.mana.ManaAtom; import forge.card.mana.ManaAtom;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.ApiType; import forge.game.GameActionUtil;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardUtil;
import forge.game.mana.ManaCostBeingPaid; import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementType;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.player.HumanPlay; import forge.player.HumanPlay;
import forge.player.PlayerControllerHuman; import forge.player.PlayerControllerHuman;
@@ -77,7 +71,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
// Mobile Forge allows to tap cards underneath the current card even if the current one is tapped // Mobile Forge allows to tap cards underneath the current card even if the current one is tapped
if (otherCardsToSelect != null) { if (otherCardsToSelect != null) {
for (Card c : otherCardsToSelect) { for (Card c : otherCardsToSelect) {
for (SpellAbility sa : c.getManaAbilities()) { for (SpellAbility sa : getAllManaAbilities(c)) {
if (sa.canPlay()) { if (sa.canPlay()) {
delaySelectCards.add(c); delaySelectCards.add(c);
break; break;
@@ -85,16 +79,17 @@ public abstract class InputPayMana extends InputSyncronizedBase {
} }
} }
} }
if (!card.getManaAbilities().isEmpty() && activateManaAbility(card)) { if (!getAllManaAbilities(card).isEmpty() && activateManaAbility(card)) {
return true; return true;
} }
return activateDelayedCard(); return activateDelayedCard();
} else { } else {
List<SpellAbility> manaAbilities = getAllManaAbilities(card);
// Desktop Forge floating menu functionality // Desktop Forge floating menu functionality
if (card.getManaAbilities().size() == 1) { if (manaAbilities.size() == 1) {
activateManaAbility(card, card.getManaAbilities().get(0)); activateManaAbility(card, manaAbilities.get(0));
} else { } else {
SpellAbility spellAbility = getController().getAbilityToPlay(card, Lists.newArrayList(card.getManaAbilities()), triggerEvent); SpellAbility spellAbility = getController().getAbilityToPlay(card, manaAbilities, triggerEvent);
if (spellAbility != null) { if (spellAbility != null) {
activateManaAbility(card, spellAbility); activateManaAbility(card, spellAbility);
} }
@@ -103,9 +98,29 @@ public abstract class InputPayMana extends InputSyncronizedBase {
} }
} }
protected List<SpellAbility> getAllManaAbilities(Card card) {
List<SpellAbility> result = Lists.newArrayList();
for (SpellAbility sa : card.getManaAbilities()) {
result.add(sa);
result.addAll(GameActionUtil.getAlternativeCosts(sa, player));
}
final Collection<SpellAbility> toRemove = Lists.newArrayListWithCapacity(result.size());
for (final SpellAbility sa : result) {
sa.setActivatingPlayer(player);
// fix things like retrace
// check only if SA can't be cast normally
if (sa.canPlay(true)) {
continue;
}
toRemove.add(sa);
}
result.removeAll(toRemove);
return result;
}
@Override @Override
public String getActivateAction(Card card) { public String getActivateAction(Card card) {
for (SpellAbility sa : card.getManaAbilities()) { for (SpellAbility sa : getAllManaAbilities(card)) {
if (sa.canPlay()) { if (sa.canPlay()) {
return "pay mana with card"; return "pay mana with card";
} }
@@ -135,6 +150,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
return false; return false;
} }
@Deprecated
public List<SpellAbility> getUsefulManaAbilities(Card card) { public List<SpellAbility> getUsefulManaAbilities(Card card) {
List<SpellAbility> abilities = new ArrayList<>(); List<SpellAbility> abilities = new ArrayList<>();
@@ -160,14 +176,9 @@ public abstract class InputPayMana extends InputSyncronizedBase {
return abilities; return abilities;
} }
for (SpellAbility ma : card.getManaAbilities()) { for (SpellAbility ma : getAllManaAbilities(card)) {
ma.setActivatingPlayer(player); ma.setActivatingPlayer(player);
AbilityManaPart m = ma.getManaPartRecursive(); if (ma.isManaAbilityFor(saPaidFor, colorCanUse))
if (m == null || !ma.canPlay()) { continue; }
if (!abilityProducesManaColor(ma, m, colorCanUse)) { continue; }
if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) { continue; }
if (!m.meetsManaRestrictions(saPaidFor)) { continue; }
abilities.add(ma); abilities.add(ma);
} }
return abilities; return abilities;
@@ -191,9 +202,6 @@ public abstract class InputPayMana extends InputSyncronizedBase {
} }
// make sure computer's lands aren't selected // make sure computer's lands aren't selected
if (card.getController() != player) {
return false;
}
byte colorCanUse = 0; byte colorCanUse = 0;
byte colorNeeded = 0; byte colorNeeded = 0;
@@ -210,6 +218,8 @@ public abstract class InputPayMana extends InputSyncronizedBase {
return false; return false;
} }
final SpellAbility chosen;
if (chosenAbility == null) {
HashMap<SpellAbilityView, SpellAbility> abilitiesMap = new HashMap<>(); HashMap<SpellAbilityView, SpellAbility> abilitiesMap = new HashMap<>();
// you can't remove unneeded abilities inside a for (am:abilities) loop :( // you can't remove unneeded abilities inside a for (am:abilities) loop :(
@@ -220,17 +230,13 @@ public abstract class InputPayMana extends InputSyncronizedBase {
boolean guessAbilityWithRequiredColors = true; boolean guessAbilityWithRequiredColors = true;
int amountOfMana = -1; int amountOfMana = -1;
for (SpellAbility ma : card.getManaAbilities()) { for (SpellAbility ma : getAllManaAbilities(card)) {
ma.setActivatingPlayer(player); ma.setActivatingPlayer(player);
AbilityManaPart m = ma.getManaPartRecursive(); if (!ma.isManaAbilityFor(saPaidFor, colorCanUse)) { continue; }
if (m == null || !ma.canPlay()) { continue; }
if (!abilityProducesManaColor(ma, m, colorCanUse)) { continue; }
if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) { continue; }
if (!m.meetsManaRestrictions(saPaidFor)) { continue; }
// If Mana Abilities produce differing amounts of mana, let the player choose // If Mana Abilities produce differing amounts of mana, let the player choose
int maAmount = GameActionUtil.amountOfManaGenerated(ma, true); int maAmount = ma.totalAmountOfManaGenerated(saPaidFor, true);
if (amountOfMana == -1) { if (amountOfMana == -1) {
amountOfMana = maAmount; amountOfMana = maAmount;
} else { } else {
@@ -242,12 +248,13 @@ public abstract class InputPayMana extends InputSyncronizedBase {
abilitiesMap.put(ma.getView(), ma); abilitiesMap.put(ma.getView(), ma);
// skip express mana if the ability is not undoable or reusable // skip express mana if the ability is not undoable or reusable
if (!ma.isUndoable() || !ma.getPayCosts().isRenewableResource() || ma.getSubAbility() != null) { if (!ma.isUndoable() || !ma.getPayCosts().isRenewableResource() || ma.getSubAbility() != null
|| ma.isManaCannotCounter(saPaidFor)) {
guessAbilityWithRequiredColors = false; guessAbilityWithRequiredColors = false;
} }
} }
if (abilitiesMap.isEmpty() || (chosenAbility != null && !abilitiesMap.containsKey(chosenAbility.getView()))) { if (abilitiesMap.isEmpty()) {
return false; return false;
} }
@@ -269,7 +276,6 @@ public abstract class InputPayMana extends InputSyncronizedBase {
} }
boolean choice = true; boolean choice = true;
boolean isPayingGeneric = false;
if (guessAbilityWithRequiredColors) { if (guessAbilityWithRequiredColors) {
// express Mana Choice // express Mana Choice
if (colorNeeded == 0) { if (colorNeeded == 0) {
@@ -277,12 +283,11 @@ public abstract class InputPayMana extends InputSyncronizedBase {
//avoid unnecessary prompt by pretending we need White //avoid unnecessary prompt by pretending we need White
//for the sake of "Add one mana of any color" effects //for the sake of "Add one mana of any color" effects
colorNeeded = MagicColor.WHITE; colorNeeded = MagicColor.WHITE;
isPayingGeneric = true; // for further processing
} }
else { else {
final HashMap<SpellAbilityView, SpellAbility> colorMatches = new HashMap<>(); final HashMap<SpellAbilityView, SpellAbility> colorMatches = new HashMap<>();
for (SpellAbility sa : abilitiesMap.values()) { for (SpellAbility sa : abilitiesMap.values()) {
if (abilityProducesManaColor(sa, sa.getManaPartRecursive(), colorNeeded)) { if (sa.isManaAbilityFor(saPaidFor, colorNeeded)) {
colorMatches.put(sa.getView(), sa); colorMatches.put(sa.getView(), sa);
} }
} }
@@ -299,13 +304,6 @@ public abstract class InputPayMana extends InputSyncronizedBase {
} }
} }
// Exceptions for cards that have conditional abilities which are better handled manually
if (card.getName().equals("Cavern of Souls") && isPayingGeneric) {
choice = true;
}
final SpellAbility chosen;
if (chosenAbility == null) {
ArrayList<SpellAbilityView> choices = new ArrayList<>(abilitiesMap.keySet()); ArrayList<SpellAbilityView> choices = new ArrayList<>(abilitiesMap.keySet());
chosen = abilitiesMap.size() > 1 && choice ? abilitiesMap.get(getController().getGui().one(Localizer.getInstance().getMessage("lblChooseManaAbility"), choices)) : abilitiesMap.get(choices.get(0)); chosen = abilitiesMap.size() > 1 && choice ? abilitiesMap.get(getController().getGui().one(Localizer.getInstance().getMessage("lblChooseManaAbility"), choices)) : abilitiesMap.get(choices.get(0));
} else { } else {
@@ -317,14 +315,13 @@ public abstract class InputPayMana extends InputSyncronizedBase {
// Filter the colors for the express choice so that only actually producible colors can be chosen // Filter the colors for the express choice so that only actually producible colors can be chosen
int producedColorMask = 0; int producedColorMask = 0;
for (final byte color : ManaAtom.MANATYPES) { for (final byte color : ManaAtom.MANATYPES) {
if (chosen.getManaPartRecursive().getOrigProduced().contains(MagicColor.toShortString(color)) if (chosen.canProduce(MagicColor.toShortString(color)) && colors.hasAnyColor(color)) {
&& colors.hasAnyColor(color)) {
producedColorMask |= color; producedColorMask |= color;
} }
} }
ColorSet producedAndNeededColors = ColorSet.fromMask(producedColorMask); ColorSet producedAndNeededColors = ColorSet.fromMask(producedColorMask);
chosen.getManaPartRecursive().setExpressChoice(producedAndNeededColors); chosen.setManaExpressChoice(producedAndNeededColors);
// System.out.println("Chosen sa=" + chosen + " of " + chosen.getHostCard() + " to pay mana"); // System.out.println("Chosen sa=" + chosen + " of " + chosen.getHostCard() + " to pay mana");
@@ -344,62 +341,6 @@ public abstract class InputPayMana extends InputSyncronizedBase {
return true; return true;
} }
private static boolean abilityProducesManaColor(final SpellAbility am, AbilityManaPart m, final byte neededColor) {
if (0 != (neededColor & ManaAtom.GENERIC)) {
return true;
}
if (m.isAnyMana()) {
return true;
}
// check for produce mana replacement effects - they mess this up, so just use the mana ability
final Card source = am.getHostCard();
final Player activator = am.getActivatingPlayer();
final Game g = source.getGame();
final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
repParams.put(AbilityKey.Mana, m.getOrigProduced());
repParams.put(AbilityKey.Affected, source);
repParams.put(AbilityKey.Player, activator);
repParams.put(AbilityKey.AbilityMana, am);
for (final Player p : g.getPlayers()) {
for (final Card crd : p.getAllCards()) {
for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) {
if (replacementEffect.requirementsCheck(g)
&& replacementEffect.getMode() == ReplacementType.ProduceMana
&& replacementEffect.canReplace(repParams)
&& replacementEffect.hasParam("ManaReplacement")
&& replacementEffect.zonesCheck(g.getZoneOf(crd))) {
return true;
}
}
}
}
if (am.getApi() == ApiType.ManaReflected) {
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am);
for (final String color : reflectableColors) {
if (0 != (neededColor & ManaAtom.fromName(color))) {
return true;
}
}
}
else {
// treat special mana if it always can be paid
if (m.isSpecialMana()) {
return true;
}
String colorsProduced = m.isComboMana() ? m.getComboColors() : m.mana();
for (final String color : colorsProduced.split(" ")) {
if (0 != (neededColor & ManaAtom.fromName(color))) {
return true;
}
}
}
return false;
}
protected boolean isAlreadyPaid() { protected boolean isAlreadyPaid() {
if (manaCost.isPaid()) { if (manaCost.isPaid()) {
bPaid = true; bPaid = true;

View File

@@ -33,6 +33,7 @@ import forge.game.event.GameEventTokenCreated;
import forge.game.event.GameEventTurnEnded; import forge.game.event.GameEventTurnEnded;
import forge.game.event.GameEventZone; import forge.game.event.GameEventZone;
import forge.game.event.IGameEventVisitor; import forge.game.event.IGameEventVisitor;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.TextUtil; import forge.util.TextUtil;
@@ -208,11 +209,14 @@ public class EventVisualizer extends IGameEventVisitor.Base<SoundEffectType> imp
// I want to get all real colors this land can produce - no interest in colorless or devoid // I want to get all real colors this land can produce - no interest in colorless or devoid
StringBuilder fullManaColors = new StringBuilder(); StringBuilder fullManaColors = new StringBuilder();
for (final SpellAbility sa : land.getManaAbilities()) { for (final SpellAbility sa : land.getManaAbilities()) {
String currManaColor = sa.getManaPartRecursive().getOrigProduced(); for (AbilityManaPart mp : sa.getAllManaParts()) {
String currManaColor = mp.getOrigProduced();
if(!"C".equals(currManaColor)) { if(!"C".equals(currManaColor)) {
fullManaColors.append(currManaColor); fullManaColors.append(currManaColor);
} }
} }
}
// No interest if "colors together" or "alternative colors" - only interested in colors themselves // No interest if "colors together" or "alternative colors" - only interested in colors themselves
fullManaColors = new StringBuilder(TextUtil.fastReplace(fullManaColors.toString()," ", "")); fullManaColors = new StringBuilder(TextUtil.fastReplace(fullManaColors.toString()," ", ""));