Merged changes from trunk to GuiRefactoring: 27239-27260

This commit is contained in:
elcnesh
2014-09-09 09:30:57 +00:00
parent 0222c64b62
commit ecdd2b2995
33 changed files with 1154 additions and 1019 deletions

View File

@@ -74,14 +74,12 @@ public class ComputerUtilMana {
manaSpent.clear(); manaSpent.clear();
} }
private static boolean payManaCost(final SpellAbility sa, final Player ai, final boolean test, final int extraMana, boolean checkPlayable) { private static boolean payManaCost(final SpellAbility sa, final Player ai, final boolean test, final int extraMana, boolean checkPlayable) {
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, test, extraMana); ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, test, extraMana);
return payManaCost(cost, sa, ai, test, checkPlayable); return payManaCost(cost, sa, ai, test, checkPlayable);
} }
private static class ManaProducingCard { private static class ManaProducingCard {
private CoreType cardType; private CoreType cardType;
private int manaCount; private int manaCount;
@@ -1169,10 +1167,11 @@ public class ComputerUtilMana {
* @since 1.0.15 * @since 1.0.15
*/ */
public static int determineLeftoverMana(final SpellAbility sa, final Player player) { public static int determineLeftoverMana(final SpellAbility sa, final Player player) {
for (int i = 1; i < 100; i++) for (int i = 1; i < 100; i++) {
if (!canPayManaCost(sa, player, i)) if (!canPayManaCost(sa, player, i)) {
return i - 1; return i - 1;
}
}
return 99; return 99;
} }

View File

@@ -1134,6 +1134,14 @@ public class ChangeZoneAi extends SpellAbilityAi {
if( fetchList.isEmpty() ) if( fetchList.isEmpty() )
return null; return null;
if (sa.hasParam("AILogic") && sa.getParam("AILogic").equals("NeverBounceItself")) {
Card source = sa.getHostCard();
if (fetchList.contains(source)) {
// For cards that should never be bounced back to hand with their own [e.g. triggered] abilities, such as guild lands.
fetchList.remove(source);
}
}
String type = sa.getParam("ChangeType"); String type = sa.getParam("ChangeType");
if (type == null) { if (type == null) {
type = "Card"; type = "Card";

View File

@@ -13,19 +13,19 @@ import forge.game.player.RegisteredPlayer;
public enum GameType { public enum GameType {
// deck composition rules, isPoolRestricted, can sideboard between matches // deck composition rules, isPoolRestricted, can sideboard between matches
Sealed (DeckFormat.Limited, true, true, true, "Sealed", null), Sealed (DeckFormat.Limited, true, true, true, "Sealed", "", null),
Draft (DeckFormat.Limited, true, true, true, "Draft", null), Draft (DeckFormat.Limited, true, true, true, "Draft", "", null),
Winston (DeckFormat.Limited, true, true, true, "Winston", null), Winston (DeckFormat.Limited, true, true, true, "Winston", "", null),
Gauntlet (DeckFormat.Limited, true, true, true, "Gauntlet", null), Gauntlet (DeckFormat.Limited, true, true, true, "Gauntlet", "", null),
Quest (DeckFormat.QuestDeck, true, true, false, "Quest", null), Quest (DeckFormat.QuestDeck, true, true, false, "Quest", "", null),
QuestDraft (DeckFormat.Limited, true, true, true, "Quest Draft", null), QuestDraft (DeckFormat.Limited, true, true, true, "Quest Draft", "", null),
Constructed (DeckFormat.Constructed, false, true, true, "Constructed", null), Constructed (DeckFormat.Constructed, false, true, true, "Constructed", "", null),
Vanguard (DeckFormat.Vanguard, true, true, true, "Vanguard", null), Vanguard (DeckFormat.Vanguard, true, true, true, "Vanguard", "Each player has a special \"Avatar\" card that affects the game.", null),
Commander (DeckFormat.Commander, false, false, false, "Commander", null), Commander (DeckFormat.Commander, false, false, false, "Commander", "Each player has a legendary \"General\" card which can be cast at any time and determines deck colors.", null),
Planechase (DeckFormat.Planechase, false, false, true, "Planechase", null), Planechase (DeckFormat.Planechase, false, false, true, "Planechase", "Plane cards apply global effects. Plane card changed when a player rolls \"Chaos\" on the planar die.", null),
Archenemy (DeckFormat.Archenemy, false, false, true, "Archenemy", null), Archenemy (DeckFormat.Archenemy, false, false, true, "Archenemy", "One player is the Archenemy and can play scheme cards.", null),
ArchenemyRumble (DeckFormat.Archenemy, false, false, true, "Archenemy Rumble", null), ArchenemyRumble (DeckFormat.Archenemy, false, false, true, "Archenemy Rumble", "All players are Archenemies and can play scheme cards.", null),
MomirBasic (DeckFormat.Constructed, false, false, false, "Momir Basic", new Function<RegisteredPlayer, Deck>() { MomirBasic (DeckFormat.Constructed, false, false, false, "Momir Basic", "Each player has a deck containing 60 basic lands and the Momir Vig avatar.", new Function<RegisteredPlayer, Deck>() {
@Override @Override
public Deck apply(RegisteredPlayer player) { public Deck apply(RegisteredPlayer player) {
Deck deck = new Deck(); Deck deck = new Deck();
@@ -43,15 +43,16 @@ public enum GameType {
private final DeckFormat deckFormat; private final DeckFormat deckFormat;
private final boolean isCardPoolLimited, canSideboard, addWonCardsMidGame; private final boolean isCardPoolLimited, canSideboard, addWonCardsMidGame;
private final String name; private final String name, description;
private final Function<RegisteredPlayer, Deck> deckAutoGenerator; private final Function<RegisteredPlayer, Deck> deckAutoGenerator;
GameType(DeckFormat deckFormat0, boolean isCardPoolLimited0, boolean canSideboard0, boolean addWonCardsMidgame0, String name0, Function<RegisteredPlayer, Deck> deckAutoGenerator0) { GameType(DeckFormat deckFormat0, boolean isCardPoolLimited0, boolean canSideboard0, boolean addWonCardsMidgame0, String name0, String description0, Function<RegisteredPlayer, Deck> deckAutoGenerator0) {
deckFormat = deckFormat0; deckFormat = deckFormat0;
isCardPoolLimited = isCardPoolLimited0; isCardPoolLimited = isCardPoolLimited0;
canSideboard = canSideboard0; canSideboard = canSideboard0;
addWonCardsMidGame = addWonCardsMidgame0; addWonCardsMidGame = addWonCardsMidgame0;
name = name0; name = name0;
description = description0;
deckAutoGenerator = deckAutoGenerator0; deckAutoGenerator = deckAutoGenerator0;
} }
@@ -104,4 +105,8 @@ public enum GameType {
public String toString() { public String toString() {
return name; return name;
} }
public String getDescription() {
return description;
}
} }

View File

@@ -2535,7 +2535,7 @@ public class Card extends GameEntity implements Comparable<Card> {
sb.indexOf("Storm (When you cast this spell, copy it for each spell cast before it this turn.") + 81, sb.indexOf("Storm (When you cast this spell, copy it for each spell cast before it this turn.") + 81,
" You may choose new targets for the copies."); " You may choose new targets for the copies.");
} }
} else if (keyword.contains("Replicate") && !sb.toString().contains("you paid its replicate cost.")) { } else if (keyword.startsWith("Replicate") && !sb.toString().contains("you paid its replicate cost.")) {
if (sb.toString().endsWith("\r\n\r\n")) { if (sb.toString().endsWith("\r\n\r\n")) {
sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n") + 3); sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n") + 3);
} }
@@ -4181,8 +4181,9 @@ public class Card extends GameEntity implements Comparable<Card> {
public final void addMultiKickerMagnitude(final int n) { this.multiKickerMagnitude += n; } public final void addMultiKickerMagnitude(final int n) { this.multiKickerMagnitude += n; }
public final void setKickerMagnitude(final int n) { this.multiKickerMagnitude = n; } public final void setKickerMagnitude(final int n) { this.multiKickerMagnitude = n; }
public final int getKickerMagnitude() { public final int getKickerMagnitude() {
if (this.multiKickerMagnitude > 0) if (this.multiKickerMagnitude > 0) {
return multiKickerMagnitude; return multiKickerMagnitude;
}
boolean hasK1 = costsPaid.contains(OptionalCost.Kicker1); boolean hasK1 = costsPaid.contains(OptionalCost.Kicker1);
return hasK1 == costsPaid.contains(OptionalCost.Kicker2) ? (hasK1 ? 2 : 0) : 1; return hasK1 == costsPaid.contains(OptionalCost.Kicker2) ? (hasK1 ? 2 : 0) : 1;
} }

View File

@@ -363,7 +363,6 @@ public class CardFactoryUtil {
card.setSVar(playSvar.toString(),playWithoutCost.toString()); card.setSVar(playSvar.toString(),playWithoutCost.toString());
} }
/** /**
* <p> * <p>
* multiplyCost. * multiplyCost.
@@ -392,7 +391,8 @@ public class CardFactoryUtil {
cost = multiplier * cost; cost = multiplier * cost;
tokenized[0] = "" + cost; tokenized[0] = "" + cost;
sb.append(tokenized[0]); sb.append(tokenized[0]);
} else { }
else {
if (tokenized[0].contains("<")) { if (tokenized[0].contains("<")) {
final String[] advCostPart = tokenized[0].split("<"); final String[] advCostPart = tokenized[0].split("<");
final String costVariable = advCostPart[1].split(">")[0]; final String costVariable = advCostPart[1].split(">")[0];
@@ -406,7 +406,8 @@ public class CardFactoryUtil {
} }
tokenized[0] = tokenized[0] + ">"; tokenized[0] = tokenized[0] + ">";
sb.append(tokenized[0]); sb.append(tokenized[0]);
} else { }
else {
for (int i = 0; i < multiplier; i++) { for (int i = 0; i < multiplier; i++) {
// tokenized[0] = tokenized[0] + " " + tokenized[0]; // tokenized[0] = tokenized[0] + " " + tokenized[0];
sb.append((" ")); sb.append((" "));
@@ -430,7 +431,8 @@ public class CardFactoryUtil {
tokenized[i] = tokenized[i] + ">"; tokenized[i] = tokenized[i] + ">";
sb.append((" ")); sb.append((" "));
sb.append(tokenized[i]); sb.append(tokenized[i]);
} else { }
else {
for (int j = 0; j < multiplier; j++) { for (int j = 0; j < multiplier; j++) {
// tokenized[i] = tokenized[i] + " " + tokenized[i]; // tokenized[i] = tokenized[i] + " " + tokenized[i];
sb.append((" ")); sb.append((" "));
@@ -457,7 +459,6 @@ public class CardFactoryUtil {
* @return a boolean. * @return a boolean.
*/ */
public static boolean isTargetStillValid(final SpellAbility ability, final Card target) { public static boolean isTargetStillValid(final SpellAbility ability, final Card target) {
Zone zone = target.getGame().getZoneOf(target); Zone zone = target.getGame().getZoneOf(target);
if (zone == null) { if (zone == null) {
return false; // for tokens that disappeared return false; // for tokens that disappeared
@@ -477,7 +478,8 @@ public class CardFactoryUtil {
if (!tgt.getZone().contains(zone.getZoneType())) { if (!tgt.getZone().contains(zone.getZoneType())) {
return false; return false;
} }
} else { }
else {
// If an Aura's target is removed before it resolves, the Aura // If an Aura's target is removed before it resolves, the Aura
// fizzles // fizzles
if (source.isAura() && !target.isInZone(ZoneType.Battlefield)) { if (source.isAura() && !target.isInZone(ZoneType.Battlefield)) {
@@ -561,7 +563,6 @@ public class CardFactoryUtil {
* @return a int. * @return a int.
*/ */
public static int countOccurrences(final String arg1, final String arg2) { public static int countOccurrences(final String arg1, final String arg2) {
int count = 0; int count = 0;
int index = 0; int index = 0;
while ((index = arg1.indexOf(arg2, index)) != -1) { while ((index = arg1.indexOf(arg2, index)) != -1) {
@@ -650,7 +651,9 @@ public class CardFactoryUtil {
} }
return doXMath(n, m, source); return doXMath(n, m, source);
} else if (l[0].startsWith("Lowest")) { }
if (l[0].startsWith("Lowest")) {
n = 99999; // if no players have fewer than 99999 valids, the game is frozen anyway n = 99999; // if no players have fewer than 99999 valids, the game is frozen anyway
for (final Player player : players) { for (final Player player : players) {
final int current = playerXProperty(player, s.replace("Lowest", ""), source); final int current = playerXProperty(player, s.replace("Lowest", ""), source);
@@ -669,6 +672,7 @@ public class CardFactoryUtil {
if (sq[0].equals("Amount")) { if (sq[0].equals("Amount")) {
return doXMath(players.size(), m, source); return doXMath(players.size(), m, source);
} }
if (sq[0].contains("DamageThisTurn")) { if (sq[0].contains("DamageThisTurn")) {
int totDmg = 0; int totDmg = 0;
for (Player p : players) { for (Player p : players) {
@@ -677,8 +681,9 @@ public class CardFactoryUtil {
return doXMath(totDmg, m, source); return doXMath(totDmg, m, source);
} }
if(players.size() > 0) if (players.size() > 0) {
return playerXProperty(players.get(0), s, source); return playerXProperty(players.get(0), s, source);
}
return doXMath(n, m, source); return doXMath(n, m, source);
} }
@@ -699,6 +704,7 @@ public class CardFactoryUtil {
cards = CardLists.getValidCards(cards, rest, player, source); cards = CardLists.getValidCards(cards, rest, player, source);
return doXMath(cards.size(), m, source); return doXMath(cards.size(), m, source);
} }
// count valid cards on the battlefield // count valid cards on the battlefield
if (l[0].startsWith("Valid ")) { if (l[0].startsWith("Valid ")) {
final String restrictions = l[0].substring(6); final String restrictions = l[0].substring(6);
@@ -819,8 +825,12 @@ public class CardFactoryUtil {
* @return a int. * @return a int.
*/ */
public static int xCount(final Card c, final String expression) { public static int xCount(final Card c, final String expression) {
if (StringUtils.isBlank(expression)) return 0; if (StringUtils.isBlank(expression)) {
if (StringUtils.isNumeric(expression)) return Integer.parseInt(expression); return 0;
}
if (StringUtils.isNumeric(expression)) {
return Integer.parseInt(expression);
}
final Player cc = c.getController(); final Player cc = c.getController();
final Game game = c.getGame(); final Game game = c.getGame();
@@ -834,9 +844,8 @@ public class CardFactoryUtil {
final String number = l[0].substring(7); final String number = l[0].substring(7);
if (number.equals("ChosenNumber")) { if (number.equals("ChosenNumber")) {
return doXMath(c.getChosenNumber(), m, c); return doXMath(c.getChosenNumber(), m, c);
} else {
return doXMath(Integer.parseInt(number), m, c);
} }
return doXMath(Integer.parseInt(number), m, c);
} }
if (l[0].startsWith("Count$")) { if (l[0].startsWith("Count$")) {
@@ -847,18 +856,17 @@ public class CardFactoryUtil {
return doXMath(xCount(c, c.getSVar(l[0].substring(5))), m, c); return doXMath(xCount(c, c.getSVar(l[0].substring(5))), m, c);
} }
if (l[0].startsWith("Controller$")) if (l[0].startsWith("Controller$")) {
return playerXProperty(cc, l[0].substring(11), c); return playerXProperty(cc, l[0].substring(11), c);
}
// Manapool // Manapool
if (l[0].startsWith("ManaPool")) { if (l[0].startsWith("ManaPool")) {
final String color = l[0].split(":")[1]; final String color = l[0].split(":")[1];
if (color.equals("All")) { if (color.equals("All")) {
return cc.getManaPool().totalMana(); return cc.getManaPool().totalMana();
} else {
return cc.getManaPool().getAmountOfColor(MagicColor.fromName(color));
} }
return cc.getManaPool().getAmountOfColor(MagicColor.fromName(color));
} }
// count valid cards in any specified zone/s // count valid cards in any specified zone/s
@@ -874,7 +882,9 @@ public class CardFactoryUtil {
return doXMath(cards.size(), m, c); return doXMath(cards.size(), m, c);
} }
if (l[0].startsWith("ImprintedCardManaCost") && !c.getImprinted().isEmpty()) return c.getImprinted().get(0).getCMC(); if (l[0].startsWith("ImprintedCardManaCost") && !c.getImprinted().isEmpty()) {
return c.getImprinted().get(0).getCMC();
}
if (l[0].startsWith("GreatestPower_")) { if (l[0].startsWith("GreatestPower_")) {
final String restriction = l[0].substring(14); final String restriction = l[0].substring(14);
@@ -903,7 +913,8 @@ public class CardFactoryUtil {
if (crd.getCMC(Card.SplitCMCMode.RightSplitCMC) > highest) { if (crd.getCMC(Card.SplitCMCMode.RightSplitCMC) > highest) {
highest = crd.getCMC(Card.SplitCMCMode.RightSplitCMC); highest = crd.getCMC(Card.SplitCMCMode.RightSplitCMC);
} }
} else { }
else {
if (crd.getCMC() > highest) { if (crd.getCMC() > highest) {
highest = crd.getCMC(); highest = crd.getCMC();
} }
@@ -983,7 +994,10 @@ public class CardFactoryUtil {
final String[] sq; final String[] sq;
sq = l[0].split("\\."); sq = l[0].split("\\.");
if (sq[0].contains("xPaid")) return doXMath(c.getXManaCostPaid(), m, c); if (sq[0].contains("xPaid")) {
return doXMath(c.getXManaCostPaid(), m, c);
}
if (sq[0].contains("xColorPaid")) { if (sq[0].contains("xColorPaid")) {
String[] attrs = sq[0].split(" "); String[] attrs = sq[0].split(" ");
String colors = ""; String colors = "";
@@ -994,21 +1008,41 @@ public class CardFactoryUtil {
} }
if (sq[0].equals("YouDrewThisTurn")) return doXMath(c.getController().getNumDrawnThisTurn(), m, c); if (sq[0].equals("YouDrewThisTurn")) {
return doXMath(c.getController().getNumDrawnThisTurn(), m, c);
}
if (sq[0].equals("FirstSpellTotalManaSpent")) return doXMath(c.getFirstSpellAbility().getTotalManaSpent(), m, c); if (sq[0].equals("FirstSpellTotalManaSpent")) {
if (sq[0].equals("StormCount")) return doXMath(game.getStack().getCardsCastThisTurn().size() - 1, m, c); return doXMath(c.getFirstSpellAbility().getTotalManaSpent(), m, c);
if (sq[0].equals("DamageDoneThisTurn")) return doXMath(c.getDamageDoneThisTurn(), m, c); }
if (sq[0].equals("BloodthirstAmount")) return doXMath(c.getController().getBloodthirstAmount(), m, c); if (sq[0].equals("StormCount")) {
if (sq[0].equals("RegeneratedThisTurn")) return doXMath(c.getRegeneratedThisTurn(), m, c); return doXMath(game.getStack().getCardsCastThisTurn().size() - 1, m, c);
}
if (sq[0].equals("DamageDoneThisTurn")) {
return doXMath(c.getDamageDoneThisTurn(), m, c);
}
if (sq[0].equals("BloodthirstAmount")) {
return doXMath(c.getController().getBloodthirstAmount(), m, c);
}
if (sq[0].equals("RegeneratedThisTurn")) {
return doXMath(c.getRegeneratedThisTurn(), m, c);
}
// TriggeringObjects // TriggeringObjects
if (sq[0].startsWith("Triggered")) return doXMath(xCount((Card) c.getTriggeringObject("Card"), sq[0].substring(9)), m, c); if (sq[0].startsWith("Triggered")) {
return doXMath(xCount((Card) c.getTriggeringObject("Card"), sq[0].substring(9)), m, c);
}
if (sq[0].contains("YourStartingLife")) return doXMath(cc.getStartingLife(), m, c); if (sq[0].contains("YourStartingLife")) {
return doXMath(cc.getStartingLife(), m, c);
}
if (sq[0].contains("YourLifeTotal")) return doXMath(cc.getLife(), m, c); if (sq[0].contains("YourLifeTotal")) {
if (sq[0].contains("OppGreatestLifeTotal")) return doXMath(cc.getOpponentsGreatestLifeTotal(), m, c); return doXMath(cc.getLife(), m, c);
}
if (sq[0].contains("OppGreatestLifeTotal")) {
return doXMath(cc.getOpponentsGreatestLifeTotal(), m, c);
}
if (sq[0].contains("OppsAtLifeTotal")) { if (sq[0].contains("OppsAtLifeTotal")) {
final int lifeTotal = xCount(c, sq[1]); final int lifeTotal = xCount(c, sq[1]);
int number = 0; int number = 0;
@@ -1032,8 +1066,12 @@ public class CardFactoryUtil {
} }
} }
if (sq[0].contains("LifeYouLostThisTurn")) return doXMath(cc.getLifeLostThisTurn(), m, c); if (sq[0].contains("LifeYouLostThisTurn")) {
if (sq[0].contains("LifeYouGainedThisTurn")) return doXMath(cc.getLifeGainedThisTurn(), m, c); return doXMath(cc.getLifeLostThisTurn(), m, c);
}
if (sq[0].contains("LifeYouGainedThisTurn")) {
return doXMath(cc.getLifeGainedThisTurn(), m, c);
}
if (sq[0].contains("LifeOppsLostThisTurn")) { if (sq[0].contains("LifeOppsLostThisTurn")) {
int lost = 0; int lost = 0;
for (Player opp : cc.getOpponents()) { for (Player opp : cc.getOpponents()) {
@@ -1042,18 +1080,34 @@ public class CardFactoryUtil {
return doXMath(lost, m, c); return doXMath(lost, m, c);
} }
if (sq[0].equals("TotalDamageDoneByThisTurn")) return doXMath(c.getTotalDamageDoneBy(), m, c); if (sq[0].equals("TotalDamageDoneByThisTurn")) {
if (sq[0].equals("TotalDamageReceivedThisTurn")) return doXMath(c.getTotalDamageRecievedThisTurn(), m, c); return doXMath(c.getTotalDamageDoneBy(), m, c);
}
if (sq[0].equals("TotalDamageReceivedThisTurn")) {
return doXMath(c.getTotalDamageRecievedThisTurn(), m, c);
}
if (sq[0].contains("YourPoisonCounters")) return doXMath(cc.getPoisonCounters(), m, c); if (sq[0].contains("YourPoisonCounters")) {
if (sq[0].contains("TotalOppPoisonCounters")) return doXMath(cc.getOpponentsTotalPoisonCounters(), m, c); return doXMath(cc.getPoisonCounters(), m, c);
}
if (sq[0].contains("TotalOppPoisonCounters")) {
return doXMath(cc.getOpponentsTotalPoisonCounters(), m, c);
}
if (sq[0].contains("YourDamageThisTurn")) return doXMath(cc.getAssignedDamage(), m, c); if (sq[0].contains("YourDamageThisTurn")) {
if (sq[0].contains("TotalOppDamageThisTurn")) return doXMath(cc.getOpponentsAssignedDamage(), m, c); return doXMath(cc.getAssignedDamage(), m, c);
if (sq[0].contains("MaxOppDamageThisTurn")) return doXMath(cc.getMaxOpponentAssignedDamage(), m, c); }
if (sq[0].contains("TotalOppDamageThisTurn")) {
return doXMath(cc.getOpponentsAssignedDamage(), m, c);
}
if (sq[0].contains("MaxOppDamageThisTurn")) {
return doXMath(cc.getMaxOpponentAssignedDamage(), m, c);
}
// Count$YourTypeDamageThisTurn Type // Count$YourTypeDamageThisTurn Type
if (sq[0].contains("YourTypeDamageThisTurn")) return doXMath(cc.getAssignedDamage(sq[0].split(" ")[1]), m, c); if (sq[0].contains("YourTypeDamageThisTurn")) {
return doXMath(cc.getAssignedDamage(sq[0].split(" ")[1]), m, c);
}
if (sq[0].contains("YourDamageSourcesThisTurn")) { if (sq[0].contains("YourDamageSourcesThisTurn")) {
Iterable<Card> allSrc = cc.getAssignedDamageSources(); Iterable<Card> allSrc = cc.getAssignedDamageSources();
String restriction = sq[0].split(" ")[1]; String restriction = sq[0].split(" ")[1];
@@ -1061,7 +1115,9 @@ public class CardFactoryUtil {
return doXMath(filtered.size(), m, c); return doXMath(filtered.size(), m, c);
} }
if (sq[0].contains("YourLandsPlayed")) return doXMath(cc.getNumLandsPlayed(), m, c); if (sq[0].contains("YourLandsPlayed")) {
return doXMath(cc.getNumLandsPlayed(), m, c);
}
// Count$TopOfLibraryCMC // Count$TopOfLibraryCMC
if (sq[0].contains("TopOfLibraryCMC")) { if (sq[0].contains("TopOfLibraryCMC")) {
@@ -1095,7 +1151,8 @@ public class CardFactoryUtil {
final List<Card> cards; final List<Card> cards;
if (sq[0].contains("ChromaSource")) { // Runs Chroma for passed in Source card if (sq[0].contains("ChromaSource")) { // Runs Chroma for passed in Source card
cards = Lists.newArrayList(c); cards = Lists.newArrayList(c);
} else { }
else {
cards = cc.getCardsIn(sourceZone); cards = cc.getCardsIn(sourceZone);
} }
@@ -1124,14 +1181,28 @@ public class CardFactoryUtil {
return doXMath(colorOcurrencices, m, c); return doXMath(colorOcurrencices, m, c);
} }
if (sq[0].contains("Hellbent")) return doXMath(Integer.parseInt(sq[cc.hasHellbent() ? 1 : 2]), m, c); if (sq[0].contains("Hellbent")) {
if (sq[0].contains("Metalcraft")) return doXMath(Integer.parseInt(sq[cc.hasMetalcraft() ? 1 : 2]), m, c); return doXMath(Integer.parseInt(sq[cc.hasHellbent() ? 1 : 2]), m, c);
if (sq[0].contains("FatefulHour")) return doXMath(Integer.parseInt(sq[cc.getLife() <= 5 ? 1 : 2]), m, c); }
if (sq[0].contains("Metalcraft")) {
return doXMath(Integer.parseInt(sq[cc.hasMetalcraft() ? 1 : 2]), m, c);
}
if (sq[0].contains("FatefulHour")) {
return doXMath(Integer.parseInt(sq[cc.getLife() <= 5 ? 1 : 2]), m, c);
}
if (sq[0].contains("Landfall")) return doXMath(Integer.parseInt(sq[cc.hasLandfall() ? 1 : 2]), m, c); if (sq[0].contains("Landfall")) {
if (sq[0].contains("Threshold")) return doXMath(Integer.parseInt(sq[cc.hasThreshold() ? 1 : 2]), m, c); return doXMath(Integer.parseInt(sq[cc.hasLandfall() ? 1 : 2]), m, c);
if (sq[0].startsWith("Kicked")) return doXMath(Integer.parseInt(sq[c.getKickerMagnitude() > 0 ? 1 : 2]), m, c); }
if (sq[0].startsWith("AltCost")) return doXMath(Integer.parseInt(sq[c.isOptionalCostPaid(OptionalCost.AltCost) ? 1 : 2]), m, c); if (sq[0].contains("Threshold")) {
return doXMath(Integer.parseInt(sq[cc.hasThreshold() ? 1 : 2]), m, c);
}
if (sq[0].startsWith("Kicked")) {
return doXMath(Integer.parseInt(sq[c.getKickerMagnitude() > 0 ? 1 : 2]), m, c);
}
if (sq[0].startsWith("AltCost")) {
return doXMath(Integer.parseInt(sq[c.isOptionalCostPaid(OptionalCost.AltCost) ? 1 : 2]), m, c);
}
// Count$wasCastFrom<Zone>.<true>.<false> // Count$wasCastFrom<Zone>.<true>.<false>
if (sq[0].startsWith("wasCastFrom")) { if (sq[0].startsWith("wasCastFrom")) {
@@ -1145,9 +1216,15 @@ public class CardFactoryUtil {
return doXMath(cl.size(), m, c); return doXMath(cl.size(), m, c);
} }
if (sq[0].contains("CardPower")) return doXMath(c.getNetAttack(), m, c); if (sq[0].contains("CardPower")) {
if (sq[0].contains("CardToughness")) return doXMath(c.getNetDefense(), m, c); return doXMath(c.getNetAttack(), m, c);
if (sq[0].contains("CardSumPT")) return doXMath((c.getNetAttack() + c.getNetDefense()), m, c); }
if (sq[0].contains("CardToughness")) {
return doXMath(c.getNetDefense(), m, c);
}
if (sq[0].contains("CardSumPT")) {
return doXMath((c.getNetAttack() + c.getNetDefense()), m, c);
}
// Count$SumPower_valid // Count$SumPower_valid
if (sq[0].contains("SumPower")) { if (sq[0].contains("SumPower")) {
@@ -1161,9 +1238,11 @@ public class CardFactoryUtil {
Card ce; Card ce;
if (sq[0].contains("Equipped") && c.isEquipping()) { if (sq[0].contains("Equipped") && c.isEquipping()) {
ce = c.getEquipping().get(0); ce = c.getEquipping().get(0);
} else if (sq[0].contains("Remembered")) { }
else if (sq[0].contains("Remembered")) {
ce = (Card) c.getRemembered().get(0); ce = (Card) c.getRemembered().get(0);
} else { }
else {
ce = c; ce = c;
} }
return doXMath(ce.getCMC(), m, c); return doXMath(ce.getCMC(), m, c);
@@ -1177,8 +1256,12 @@ public class CardFactoryUtil {
return Aggregates.sum(filteredCards, CardPredicates.Accessors.fnGetCmc); return Aggregates.sum(filteredCards, CardPredicates.Accessors.fnGetCmc);
} }
if (sq[0].contains("CardNumColors")) return doXMath(CardUtil.getColors(c).countColors(), m, c); if (sq[0].contains("CardNumColors")) {
if (sq[0].contains("ChosenNumber")) return doXMath(c.getChosenNumber(), m, c); return doXMath(CardUtil.getColors(c).countColors(), m, c);
}
if (sq[0].contains("ChosenNumber")) {
return doXMath(c.getChosenNumber(), m, c);
}
if (sq[0].contains("CardCounters")) { if (sq[0].contains("CardCounters")) {
// CardCounters.ALL to be used for Kinsbaile Borderguard and anything that cares about all counters // CardCounters.ALL to be used for Kinsbaile Borderguard and anything that cares about all counters
int count = 0; int count = 0;
@@ -1188,7 +1271,8 @@ public class CardFactoryUtil {
count += i; count += i;
} }
} }
} else { }
else {
count = c.getCounters(CounterType.getType(sq[1])); count = c.getCounters(CounterType.getType(sq[1]));
} }
return doXMath(count, m, c); return doXMath(count, m, c);
@@ -1208,12 +1292,19 @@ public class CardFactoryUtil {
return doXMath(cCount, m, c); return doXMath(cCount, m, c);
} }
if (sq[0].contains("CardTypes")) return doXMath(getCardTypesFromList(game.getCardsIn(ZoneType.smartValueOf(sq[1]))), m, c); if (sq[0].contains("CardTypes")) {
return doXMath(getCardTypesFromList(game.getCardsIn(ZoneType.smartValueOf(sq[1]))), m, c);
if (sq[0].contains("BushidoPoint")) return doXMath(c.getKeywordMagnitude("Bushido"), m, c); }
if (sq[0].contains("TimesKicked")) return doXMath(c.getKickerMagnitude(), m, c);
if (sq[0].contains("NumCounters")) return doXMath(c.getCounters(CounterType.getType(sq[1])), m, c);
if (sq[0].contains("BushidoPoint")) {
return doXMath(c.getKeywordMagnitude("Bushido"), m, c);
}
if (sq[0].contains("TimesKicked")) {
return doXMath(c.getKickerMagnitude(), m, c);
}
if (sq[0].contains("NumCounters")) {
return doXMath(c.getCounters(CounterType.getType(sq[1])), m, c);
}
// Count$IfMainPhase.<numMain>.<numNotMain> // 7/10 // Count$IfMainPhase.<numMain>.<numNotMain> // 7/10
if (sq[0].contains("IfMainPhase")) { if (sq[0].contains("IfMainPhase")) {
@@ -1272,7 +1363,8 @@ public class CardFactoryUtil {
if (workingCopy[0].contains("This")) { if (workingCopy[0].contains("This")) {
res = CardUtil.getThisTurnCast(validFilter, c); res = CardUtil.getThisTurnCast(validFilter, c);
} else { }
else {
res = CardUtil.getLastTurnCast(validFilter, c); res = CardUtil.getLastTurnCast(validFilter, c);
} }
@@ -1285,9 +1377,8 @@ public class CardFactoryUtil {
final List<Card> res = CardUtil.getThisTurnEntered(ZoneType.Graveyard, ZoneType.Battlefield, "Creature", c); final List<Card> res = CardUtil.getThisTurnEntered(ZoneType.Graveyard, ZoneType.Battlefield, "Creature", c);
if (res.size() > 0) { if (res.size() > 0) {
return doXMath(Integer.parseInt(sq[1]), m, c); return doXMath(Integer.parseInt(sq[1]), m, c);
} else {
return doXMath(Integer.parseInt(sq[2]), m, c);
} }
return doXMath(Integer.parseInt(sq[2]), m, c);
} }
if (sq[0].equals("YourTurns")) { if (sq[0].equals("YourTurns")) {
@@ -2009,7 +2100,8 @@ public class CardFactoryUtil {
final String text; final String text;
if (p.equals(originPlayer)) { if (p.equals(originPlayer)) {
text = "Commander Damage from own Commander: "; text = "Commander Damage from own Commander: ";
} else { }
else {
text = "Commander Damage to " + p.getName() + ": "; text = "Commander Damage to " + p.getName() + ": ";
} }
@@ -2036,32 +2128,28 @@ public class CardFactoryUtil {
// Cards with Cycling abilities // Cards with Cycling abilities
// -1 means keyword "Cycling" not found // -1 means keyword "Cycling" not found
if (hasKeyword(card, "Multikicker") != -1) { for (String keyword : card.getKeyword()) {
final int n = hasKeyword(card, "Multikicker"); if (keyword.startsWith("Multikicker")) {
if (n != -1) { final String[] k = keyword.split("kicker ");
final String parse = card.getKeyword().get(n).toString();
final String[] k = parse.split("kicker ");
final SpellAbility sa = card.getFirstSpellAbility(); final SpellAbility sa = card.getFirstSpellAbility();
sa.setMultiKickerManaCost(new ManaCost(new ManaCostParser(k[1]))); sa.setMultiKickerManaCost(new ManaCost(new ManaCostParser(k[1])));
sa.addAnnounceVar("Multikicker");
} }
else if (keyword.startsWith("Replicate")) {
card.getFirstSpellAbility().addAnnounceVar("Replicate");
} }
else if (keyword.startsWith("Fuse")) {
if(hasKeyword(card, "Fuse") != -1) {
card.getState(CardCharacteristicName.Original).getSpellAbility().add(AbilityFactory.buildFusedAbility(card)); card.getState(CardCharacteristicName.Original).getSpellAbility().add(AbilityFactory.buildFusedAbility(card));
} }
else if (keyword.startsWith("Evoke")) {
final int evokePos = hasKeyword(card, "Evoke"); card.addSpellAbility(makeEvokeSpell(card, keyword));
if (evokePos != -1) {
card.addSpellAbility(makeEvokeSpell(card, card.getKeyword().get(evokePos)));
} }
final int monstrousPos = hasKeyword(card, "Monstrosity"); else if (keyword.startsWith("Monstrosity")) {
if (monstrousPos != -1) { final String[] k = keyword.split(":");
final String parse = card.getKeyword().get(monstrousPos).toString();
final String[] k = parse.split(":");
final String magnitude = k[0].substring(12); final String magnitude = k[0].substring(12);
final String manacost = k[1]; final String manacost = k[1];
card.removeIntrinsicKeyword(parse); card.removeIntrinsicKeyword(keyword);
String ref = "X".equals(magnitude) ? " | References$ X" : ""; String ref = "X".equals(magnitude) ? " | References$ X" : "";
String counters = StringUtils.isNumeric(magnitude) String counters = StringUtils.isNumeric(magnitude)
@@ -2076,15 +2164,10 @@ public class CardFactoryUtil {
// add ability to instrinic strings so copies/clones create the ability also // add ability to instrinic strings so copies/clones create the ability also
card.getUnparsedAbilities().add(effect); card.getUnparsedAbilities().add(effect);
} }
else if (keyword.startsWith("Unearth")) {
card.removeIntrinsicKeyword(keyword);
if (hasKeyword(card, "Unearth") != -1) { final String[] k = keyword.split(":");
final int n = hasKeyword(card, "Unearth");
if (n != -1) {
final String parse = card.getKeyword().get(n).toString();
card.removeIntrinsicKeyword(parse);
final String[] k = parse.split(":");
final String manacost = k[1]; final String manacost = k[1];
String effect = "AB$ ChangeZone | Cost$ " + manacost + " | Defined$ Self" + String effect = "AB$ ChangeZone | Cost$ " + manacost + " | Defined$ Self" +
@@ -2110,16 +2193,11 @@ public class CardFactoryUtil {
// add ability to instrinic strings so copies/clones create the ability also // add ability to instrinic strings so copies/clones create the ability also
card.getUnparsedAbilities().add(effect); card.getUnparsedAbilities().add(effect);
} }
} // unearth else if (keyword.startsWith("Level up")) {
final int iLvlUp = hasKeyword(card, "Level up");
if (iLvlUp != -1) {
final String strLevelCost = card.getKeyword().get(iLvlUp);
final String strMaxLevel = card.getSVar("maxLevel"); final String strMaxLevel = card.getSVar("maxLevel");
card.removeIntrinsicKeyword(strLevelCost); card.removeIntrinsicKeyword(keyword);
final String[] k = strLevelCost.split(":"); final String[] k = keyword.split(":");
final String manacost = k[1]; final String manacost = k[1];
String effect = "AB$ PutCounter | Cost$ " + manacost + " | " + String effect = "AB$ PutCounter | Cost$ " + manacost + " | " +
@@ -2132,41 +2210,27 @@ public class CardFactoryUtil {
card.addSpellAbility(AbilityFactory.getAbility(effect, card)); card.addSpellAbility(AbilityFactory.getAbility(effect, card));
// add ability to instrinic strings so copies/clones create the ability also // add ability to instrinic strings so copies/clones create the ability also
card.getUnparsedAbilities().add(effect); card.getUnparsedAbilities().add(effect);
} // level up }
else if (keyword.startsWith("Cycling")) {
card.removeIntrinsicKeyword(keyword);
if (hasKeyword(card, "Cycling") != -1) { final String[] k = keyword.split(":");
final int n = hasKeyword(card, "Cycling");
if (n != -1) {
final String parse = card.getKeyword().get(n).toString();
card.removeIntrinsicKeyword(parse);
final String[] k = parse.split(":");
final String manacost = k[1]; final String manacost = k[1];
card.addSpellAbility(abilityCycle(card, manacost)); card.addSpellAbility(abilityCycle(card, manacost));
} }
} // Cycling else if (keyword.startsWith("TypeCycling")) {
card.removeIntrinsicKeyword(keyword);
while (hasKeyword(card, "TypeCycling") != -1) { final String[] k = keyword.split(":");
final int n = hasKeyword(card, "TypeCycling");
if (n != -1) {
final String parse = card.getKeyword().get(n).toString();
card.removeIntrinsicKeyword(parse);
final String[] k = parse.split(":");
final String type = k[1]; final String type = k[1];
final String manacost = k[2]; final String manacost = k[2];
card.addSpellAbility(abilityTypecycle(card, manacost, type)); card.addSpellAbility(abilityTypecycle(card, manacost, type));
} }
} // TypeCycling else if (keyword.startsWith("Transmute")) {
card.removeIntrinsicKeyword(keyword);
if (hasKeyword(card, "Transmute") != -1) { final String manacost = keyword.split(":")[1];
final int n = hasKeyword(card, "Transmute");
if (n != -1) {
final String parse = card.getKeyword().get(n);
card.removeIntrinsicKeyword(parse);
final String manacost = parse.split(":")[1];
final String sbTransmute = "AB$ ChangeZone | Cost$ " + manacost + " Discard<1/CARDNAME>" final String sbTransmute = "AB$ ChangeZone | Cost$ " + manacost + " Discard<1/CARDNAME>"
+ " | CostDesc$ Transmute " + ManaCostParser.parse(manacost)+ " | ActivationZone$ Hand" + " | CostDesc$ Transmute " + ManaCostParser.parse(manacost)+ " | ActivationZone$ Hand"
+ " | Origin$ Library | Destination$ Hand | ChangeType$ Card.cmcEQ" + card.getManaCost().getCMC() + " | Origin$ Library | Destination$ Hand | ChangeType$ Card.cmcEQ" + card.getManaCost().getCMC()
@@ -2179,18 +2243,13 @@ public class CardFactoryUtil {
card.addSpellAbility(abTransmute); card.addSpellAbility(abTransmute);
card.getUnparsedAbilities().add(sbTransmute); card.getUnparsedAbilities().add(sbTransmute);
} }
} // transmute else if (keyword.startsWith("Soulshift")) {
final String[] k = keyword.split(" ");
int shiftPos = hasKeyword(card, "Soulshift");
while (shiftPos != -1) {
final int n = shiftPos;
final String parse = card.getKeyword().get(n);
final String[] k = parse.split(" ");
final int manacost = Integer.parseInt(k[1]); final int manacost = Integer.parseInt(k[1]);
final String actualTrigger = "Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard" final String actualTrigger = "Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard"
+ "| OptionalDecider$ You | ValidCard$ Card.Self | Execute$ SoulshiftAbility" + "| OptionalDecider$ You | ValidCard$ Card.Self | Execute$ SoulshiftAbility"
+ "| TriggerController$ TriggeredCardController | TriggerDescription$ " + parse + "| TriggerController$ TriggeredCardController | TriggerDescription$ " + keyword
+ " (When this creature dies, you may return target Spirit card with converted mana cost " + " (When this creature dies, you may return target Spirit card with converted mana cost "
+ manacost + " or less from your graveyard to your hand.)"; + manacost + " or less from your graveyard to your hand.)";
final String abString = "DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand" final String abString = "DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand"
@@ -2198,15 +2257,11 @@ public class CardFactoryUtil {
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, card, true); final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, card, true);
card.addTrigger(parsedTrigger); card.addTrigger(parsedTrigger);
card.setSVar("SoulshiftAbility", abString); card.setSVar("SoulshiftAbility", abString);
shiftPos = hasKeyword(card, "Soulshift", n + 1); }
} // Soulshift else if (keyword.startsWith("Champion")) {
card.removeIntrinsicKeyword(keyword);
final int championPos = hasKeyword(card, "Champion"); final String[] k = keyword.split(":");
if (championPos != -1) {
String parse = card.getKeyword().get(championPos);
card.removeIntrinsicKeyword(parse);
final String[] k = parse.split(":");
final String[] valid = k[1].split(","); final String[] valid = k[1].split(",");
String desc = k.length > 2 ? k[2] : k[1]; String desc = k.length > 2 ? k[2] : k[1];
String article = Lang.startsWithVowel(desc) ? "an" : "a"; String article = Lang.startsWithVowel(desc) ? "an" : "a";
@@ -2248,21 +2303,16 @@ public class CardFactoryUtil {
card.setSVar("ChampionReturn", returnChampion); card.setSVar("ChampionReturn", returnChampion);
card.setSVar("ChampionSacrifice", subAb.toString()); card.setSVar("ChampionSacrifice", subAb.toString());
} }
else if (keyword.startsWith("If CARDNAME would be put into a graveyard "
if (card.hasKeyword("If CARDNAME would be put into a graveyard "
+ "from anywhere, reveal CARDNAME and shuffle it into its owner's library instead.")) { + "from anywhere, reveal CARDNAME and shuffle it into its owner's library instead.")) {
String replacement = "Event$ Moved | Destination$ Graveyard | ValidCard$ Card.Self | ReplaceWith$ GraveyardToLibrary"; String replacement = "Event$ Moved | Destination$ Graveyard | ValidCard$ Card.Self | ReplaceWith$ GraveyardToLibrary";
String ab = "DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Library | Defined$ ReplacedCard | Reveal$ True | Shuffle$ True"; String ab = "DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Library | Defined$ ReplacedCard | Reveal$ True | Shuffle$ True";
card.addReplacementEffect(ReplacementHandler.parseReplacement(replacement, card, true)); card.addReplacementEffect(ReplacementHandler.parseReplacement(replacement, card, true));
card.setSVar("GraveyardToLibrary", ab); card.setSVar("GraveyardToLibrary", ab);
} }
else if (keyword.startsWith("Echo")) {
final int echoPos = hasKeyword(card, "Echo"); final String[] k = keyword.split(":");
if (echoPos != -1) {
// card.removeIntrinsicKeyword(parse);
final String[] k = card.getKeyword().get(echoPos).split(":");
final String manacost = k[1]; final String manacost = k[1];
card.setEchoCost(manacost); card.setEchoCost(manacost);
@@ -2277,16 +2327,11 @@ public class CardFactoryUtil {
} }
}; };
card.addComesIntoPlayCommand(intoPlay); card.addComesIntoPlayCommand(intoPlay);
} // echo }
else if (keyword.startsWith("Suspend")) {
if (hasKeyword(card, "Suspend") != -1) { card.removeIntrinsicKeyword(keyword);
// Suspend:<TimeCounters>:<Cost>
final int n = hasKeyword(card, "Suspend");
if (n != -1) {
final String parse = card.getKeyword().get(n);
card.removeIntrinsicKeyword(parse);
card.setSuspend(true); card.setSuspend(true);
final String[] k = parse.split(":"); final String[] k = keyword.split(":");
final String timeCounters = k[1]; final String timeCounters = k[1];
final String cost = k[2]; final String cost = k[2];
@@ -2294,12 +2339,8 @@ public class CardFactoryUtil {
addSuspendUpkeepTrigger(card); addSuspendUpkeepTrigger(card);
addSuspendPlayTrigger(card); addSuspendPlayTrigger(card);
} }
} // Suspend else if (keyword.startsWith("Fading")) {
final String[] k = keyword.split(":");
if (hasKeyword(card, "Fading") != -1) {
final int n = hasKeyword(card, "Fading");
if (n != -1) {
final String[] k = card.getKeyword().get(n).split(":");
card.addIntrinsicKeyword("etbCounter:FADE:" + k[1] + ":no Condition:no desc"); card.addIntrinsicKeyword("etbCounter:FADE:" + k[1] + ":no Condition:no desc");
@@ -2316,12 +2357,8 @@ public class CardFactoryUtil {
final Trigger parsedUpkeepTrig = TriggerHandler.parseTrigger(upkeepTrig, card, true); final Trigger parsedUpkeepTrig = TriggerHandler.parseTrigger(upkeepTrig, card, true);
card.addTrigger(parsedUpkeepTrig); card.addTrigger(parsedUpkeepTrig);
} }
} // Fading else if (keyword.startsWith("Vanishing")) {
final String[] k = keyword.split(":");
if (hasKeyword(card, "Vanishing") != -1) {
final int n = hasKeyword(card, "Vanishing");
if (n != -1) {
final String[] k = card.getKeyword().get(n).split(":");
// etbcounter // etbcounter
card.addIntrinsicKeyword("etbCounter:TIME:" + k[1] + ":no Condition:no desc"); card.addIntrinsicKeyword("etbCounter:TIME:" + k[1] + ":no Condition:no desc");
// Remove Time counter trigger // Remove Time counter trigger
@@ -2344,33 +2381,13 @@ public class CardFactoryUtil {
final Trigger parsedSacTrigger = TriggerHandler.parseTrigger(sacTrig, card, true); final Trigger parsedSacTrigger = TriggerHandler.parseTrigger(sacTrig, card, true);
card.addTrigger(parsedSacTrigger); card.addTrigger(parsedSacTrigger);
} }
} // Vanishing else if (keyword.equals("Delve")) {
// AddCost
if (card.hasSVar("FullCost")) {
final SpellAbility sa1 = card.getFirstSpellAbility();
if (sa1 != null && sa1.isSpell()) {
sa1.setPayCosts(new Cost(card.getSVar("FullCost"), sa1.isAbility()));
}
}
// AltCost
String altCost = card.getSVar("AltCost");
if (StringUtils.isNotBlank(altCost)) {
final SpellAbility sa1 = card.getFirstSpellAbility();
if (sa1 != null && sa1.isSpell()) {
card.addSpellAbility(makeAltCostAbility(card, altCost, sa1));
}
}
if (card.hasKeyword("Delve")) {
card.getSpellAbilities().get(0).setDelve(true); card.getSpellAbilities().get(0).setDelve(true);
} }
else if (keyword.startsWith("Haunt")) {
if (card.hasStartOfKeyword("Haunt")) {
setupHauntSpell(card); setupHauntSpell(card);
} }
else if (keyword.equals("Provoke")) {
if (card.hasKeyword("Provoke")) {
final String actualTrigger = "Mode$ Attacks | ValidCard$ Card.Self | " final String actualTrigger = "Mode$ Attacks | ValidCard$ Card.Self | "
+ "OptionalDecider$ You | Execute$ ProvokeAbility | Secondary$ True | TriggerDescription$ " + "OptionalDecider$ You | Execute$ ProvokeAbility | Secondary$ True | TriggerDescription$ "
+ "When this attacks, you may have target creature defending player " + "When this attacks, you may have target creature defending player "
@@ -2383,9 +2400,8 @@ public class CardFactoryUtil {
card.setSVar("ProvokeAbility", abString); card.setSVar("ProvokeAbility", abString);
card.setSVar("ProvokeUntap", dbString); card.setSVar("ProvokeUntap", dbString);
} }
else if (keyword.equals("Living Weapon")) {
if (card.hasKeyword("Living Weapon")) { card.removeIntrinsicKeyword(keyword);
card.removeIntrinsicKeyword("Living Weapon");
final StringBuilder sbTrig = new StringBuilder(); final StringBuilder sbTrig = new StringBuilder();
sbTrig.append("Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | "); sbTrig.append("Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ");
@@ -2410,12 +2426,10 @@ public class CardFactoryUtil {
final Trigger etbTrigger = TriggerHandler.parseTrigger(sbTrig.toString(), card, true); final Trigger etbTrigger = TriggerHandler.parseTrigger(sbTrig.toString(), card, true);
card.addTrigger(etbTrigger); card.addTrigger(etbTrigger);
} }
else if (keyword.equals("Epic")) {
if (card.hasKeyword("Epic")) {
makeEpic(card); makeEpic(card);
} }
else if (keyword.equals("Soulbond")) {
if (card.hasKeyword("Soulbond")) {
// Setup ETB trigger for card with Soulbond keyword // Setup ETB trigger for card with Soulbond keyword
final String actualTriggerSelf = "Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | " final String actualTriggerSelf = "Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | "
+ "ValidCard$ Card.Self | Execute$ TrigBondOther | OptionalDecider$ You | " + "ValidCard$ Card.Self | Execute$ TrigBondOther | OptionalDecider$ You | "
@@ -2437,8 +2451,7 @@ public class CardFactoryUtil {
card.addTrigger(parsedTriggerOther); card.addTrigger(parsedTriggerOther);
card.setSVar("TrigBondSelf", abStringOther); card.setSVar("TrigBondSelf", abStringOther);
} }
else if (keyword.equals("Extort")) {
if (card.hasKeyword("Extort")) {
final String extortTrigger = "Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | " final String extortTrigger = "Mode$ SpellCast | ValidCard$ Card | ValidActivatingPlayer$ You | "
+ "TriggerZones$ Battlefield | Execute$ ExtortOpps | Secondary$ True" + "TriggerZones$ Battlefield | Execute$ ExtortOpps | Secondary$ True"
+ " | TriggerDescription$ Extort (Whenever you cast a spell, you may pay W/B. If you do, " + " | TriggerDescription$ Extort (Whenever you cast a spell, you may pay W/B. If you do, "
@@ -2452,8 +2465,7 @@ public class CardFactoryUtil {
card.setSVar("ExtortGainLife", dbString); card.setSVar("ExtortGainLife", dbString);
card.setSVar("AFLifeLost", "Number$0"); card.setSVar("AFLifeLost", "Number$0");
} }
else if (keyword.equals("Evolve")) {
if (card.hasKeyword("Evolve")) {
final String evolveTrigger = "Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | " final String evolveTrigger = "Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | "
+ " ValidCard$ Creature.YouCtrl+Other | EvolveCondition$ True | " + " ValidCard$ Creature.YouCtrl+Other | EvolveCondition$ True | "
+ "TriggerZones$ Battlefield | Execute$ EvolveAddCounter | Secondary$ True | " + "TriggerZones$ Battlefield | Execute$ EvolveAddCounter | Secondary$ True | "
@@ -2466,8 +2478,7 @@ public class CardFactoryUtil {
card.addTrigger(parsedTrigger); card.addTrigger(parsedTrigger);
card.setSVar("EvolveAddCounter", abString); card.setSVar("EvolveAddCounter", abString);
} }
else if (keyword.startsWith("Dredge")) {
if (card.hasStartOfKeyword("Dredge")) {
final int dredgeAmount = card.getKeywordMagnitude("Dredge"); final int dredgeAmount = card.getKeywordMagnitude("Dredge");
final String actualRep = "Event$ Draw | ActiveZones$ Graveyard | ValidPlayer$ You | " final String actualRep = "Event$ Draw | ActiveZones$ Graveyard | ValidPlayer$ You | "
@@ -2485,8 +2496,7 @@ public class CardFactoryUtil {
card.setSVar("DredgeCheckLib", checkSVar); card.setSVar("DredgeCheckLib", checkSVar);
card.addReplacementEffect(ReplacementHandler.parseReplacement(actualRep, card, true)); card.addReplacementEffect(ReplacementHandler.parseReplacement(actualRep, card, true));
} }
else if (keyword.startsWith("Tribute")) {
if (card.hasStartOfKeyword("Tribute")) {
final int tributeAmount = card.getKeywordMagnitude("Tribute"); final int tributeAmount = card.getKeywordMagnitude("Tribute");
final String actualRep = "Event$ Moved | Destination$ Battlefield | ValidCard$ Card.Self |" final String actualRep = "Event$ Moved | Destination$ Battlefield | ValidCard$ Card.Self |"
@@ -2502,8 +2512,7 @@ public class CardFactoryUtil {
card.setSVar("TributeMoveToPlay", moveToPlay); card.setSVar("TributeMoveToPlay", moveToPlay);
card.addReplacementEffect(ReplacementHandler.parseReplacement(actualRep, card, true)); card.addReplacementEffect(ReplacementHandler.parseReplacement(actualRep, card, true));
} }
else if (keyword.startsWith("Amplify")) {
if (card.hasStartOfKeyword("Amplify")) {
// find position of Amplify keyword // find position of Amplify keyword
final int ampPos = card.getKeywordPosition("Amplify"); final int ampPos = card.getKeywordPosition("Amplify");
final String[] ampString = card.getKeyword().get(ampPos).split(":"); final String[] ampString = card.getKeyword().get(ampPos).split(":");
@@ -2539,12 +2548,9 @@ public class CardFactoryUtil {
card.setSVar("AmpMagnitude", "SVar$Revealed/Times." + amplifyMagnitude); card.setSVar("AmpMagnitude", "SVar$Revealed/Times." + amplifyMagnitude);
card.setSVar("Revealed", "Remembered$Amount"); card.setSVar("Revealed", "Remembered$Amount");
} }
else if (keyword.startsWith("Equip")) {
if (card.hasStartOfKeyword("Equip")) {
// find position of Equip keyword
final int equipPos = card.getKeywordPosition("Equip");
// Check for additional params such as preferred AI targets // Check for additional params such as preferred AI targets
final String equipString = card.getKeyword().get(equipPos).substring(5); final String equipString = keyword.substring(5);
final String[] equipExtras = equipString.contains("|") ? equipString.split("\\|", 2) : null; final String[] equipExtras = equipString.contains("|") ? equipString.split("\\|", 2) : null;
// Get cost string // Get cost string
String equipCost = ""; String equipCost = "";
@@ -2575,10 +2581,8 @@ public class CardFactoryUtil {
// add ability to instrinic strings so copies/clones create the ability also // add ability to instrinic strings so copies/clones create the ability also
card.getUnparsedAbilities().add(abilityStr.toString()); card.getUnparsedAbilities().add(abilityStr.toString());
} }
else if (keyword.startsWith("Outlast")) {
if (card.hasStartOfKeyword("Outlast")) { final String outlastString = keyword.substring(7);
final int outlastPos = card.getKeywordPosition("Outlast");
final String outlastString = card.getKeyword().get(outlastPos).substring(7);
final String[] outlastExtras = outlastString.contains("|") ? outlastString.split("\\|", 2) : null; final String[] outlastExtras = outlastString.contains("|") ? outlastString.split("\\|", 2) : null;
// Get cost string // Get cost string
String outlastCost = ""; String outlastCost = "";
@@ -2608,10 +2612,8 @@ public class CardFactoryUtil {
// add ability to instrinic strings so copies/clones create the ability also // add ability to instrinic strings so copies/clones create the ability also
card.getUnparsedAbilities().add(abilityStr.toString()); card.getUnparsedAbilities().add(abilityStr.toString());
} }
else if (keyword.startsWith("Fortify")) {
if (card.hasStartOfKeyword("Fortify")) { final String equipString = keyword.substring(7);
final int equipPos = card.getKeywordPosition("Fortify");
final String equipString = card.getKeyword().get(equipPos).substring(7);
final String[] equipExtras = equipString.contains("|") ? equipString.split("\\|", 2) : null; final String[] equipExtras = equipString.contains("|") ? equipString.split("\\|", 2) : null;
// Get cost string // Get cost string
String equipCost = ""; String equipCost = "";
@@ -2640,15 +2642,13 @@ public class CardFactoryUtil {
// instantiate attach ability // instantiate attach ability
final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card); final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card);
card.addSpellAbility(sa); card.addSpellAbility(sa);
// add ability to instrinic strings so copies/clones create the ability also // add ability to intrinsic strings so copies/clones create the ability also
card.getUnparsedAbilities().add(abilityStr.toString()); card.getUnparsedAbilities().add(abilityStr.toString());
} }
else if (keyword.startsWith("Bestow")) {
if (card.hasStartOfKeyword("Bestow")) { final String[] params = keyword.split(":");
final int bestowPos = card.getKeywordPosition("Bestow");
final String[] params = card.getKeyword().get(bestowPos).split(":");
final String cost = params[1]; final String cost = params[1];
card.removeIntrinsicKeyword(card.getKeyword().get(bestowPos)); card.removeIntrinsicKeyword(keyword);
final StringBuilder sbAttach = new StringBuilder(); final StringBuilder sbAttach = new StringBuilder();
sbAttach.append("SP$ Attach | Cost$ "); sbAttach.append("SP$ Attach | Cost$ ");
@@ -2664,7 +2664,24 @@ public class CardFactoryUtil {
bestow.setBasicSpell(false); bestow.setBasicSpell(false);
card.addSpellAbility(bestow); card.addSpellAbility(bestow);
card.getUnparsedAbilities().add(sbAttach.toString()); card.getUnparsedAbilities().add(sbAttach.toString());
}
}
// AddCost
if (card.hasSVar("FullCost")) {
final SpellAbility sa1 = card.getFirstSpellAbility();
if (sa1 != null && sa1.isSpell()) {
sa1.setPayCosts(new Cost(card.getSVar("FullCost"), sa1.isAbility()));
}
}
// AltCost
String altCost = card.getSVar("AltCost");
if (StringUtils.isNotBlank(altCost)) {
final SpellAbility sa1 = card.getFirstSpellAbility();
if (sa1 != null && sa1.isSpell()) {
card.addSpellAbility(makeAltCostAbility(card, altCost, sa1));
}
} }
setupEtbKeywords(card); setupEtbKeywords(card);

View File

@@ -51,7 +51,7 @@ public final class CardUtil {
"Enchant", "Protection", "Cumulative upkeep", "Equip", "Buyback", "Enchant", "Protection", "Cumulative upkeep", "Equip", "Buyback",
"Cycling", "Echo", "Kicker", "Flashback", "Madness", "Morph", "Cycling", "Echo", "Kicker", "Flashback", "Madness", "Morph",
"Affinity", "Entwine", "Splice", "Ninjutsu", "Affinity", "Entwine", "Splice", "Ninjutsu",
"Transute", "Replicate", "Recover", "Suspend", "Aura swap", "Transmute", "Replicate", "Recover", "Suspend", "Aura swap",
"Fortify", "Transfigure", "Champion", "Evoke", "Prowl", "Fortify", "Transfigure", "Champion", "Evoke", "Prowl",
"Reinforce", "Unearth", "Level up", "Miracle", "Overload", "Reinforce", "Unearth", "Level up", "Miracle", "Overload",
"Scavenge", "Bestow", "Outlast").build(); "Scavenge", "Bestow", "Outlast").build();

View File

@@ -31,6 +31,8 @@ import forge.util.maps.MapToAmount;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
/** /**
* <p> * <p>
* ManaCostBeingPaid class. * ManaCostBeingPaid class.
@@ -215,6 +217,20 @@ public class ManaCostBeingPaid {
return unpaidShards.isEmpty(); return unpaidShards.isEmpty();
} }
public final void setXManaCostPaid(final int xPaid, final String xColor) {
int xCost = xPaid * cntX;
cntX = 0;
ManaCostShard increaseShard;
if (StringUtils.isEmpty(xColor)) {
increaseShard = ManaCostShard.COLORLESS;
}
else {
increaseShard = ManaCostShard.valueOf(MagicColor.fromName(xColor));
}
unpaidShards.add(increaseShard, xCost);
}
public final void increaseColorlessMana(final int manaToAdd) { public final void increaseColorlessMana(final int manaToAdd) {
increaseShard(ManaCostShard.COLORLESS, manaToAdd); increaseShard(ManaCostShard.COLORLESS, manaToAdd);
} }

View File

@@ -131,8 +131,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public final AbilityManaPart getManaPartRecursive() { public final AbilityManaPart getManaPartRecursive() {
SpellAbility tail = this; SpellAbility tail = this;
while (tail != null) { while (tail != null) {
if(tail.manaPart != null) if (tail.manaPart != null) {
return tail.manaPart; return tail.manaPart;
}
tail = tail.getSubAbility(); tail = tail.getSubAbility();
} }
return null; return null;
@@ -140,14 +141,17 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public final boolean isManaAbility() { public final boolean isManaAbility() {
// Check whether spell or ability first // Check whether spell or ability first
if (this.isSpell()) if (this.isSpell()) {
return false; return false;
}
// without a target // without a target
if (this.usesTargeting()) return false; if (this.usesTargeting()) { return false; }
if (getRestrictions() != null && getRestrictions().getPlaneswalker()) if (getRestrictions() != null && getRestrictions().getPlaneswalker()) {
return false; //Loyalty ability, not a mana ability. return false; //Loyalty ability, not a mana ability.
if (this.isWrapper() && ((WrappedAbility) this).getTrigger().getMode() != TriggerType.TapsForMana) }
if (this.isWrapper() && ((WrappedAbility) this).getTrigger().getMode() != TriggerType.TapsForMana) {
return false; return false;
}
return getManaPartRecursive() != null; return getManaPartRecursive() != null;
} }
@@ -157,7 +161,22 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
} }
public final String getSVar(final String name) { public final String getSVar(final String name) {
return sVars.get(name) != null ? sVars.get(name) : ""; String var = sVars.get(name);
if (var == null) {
var = "";
}
return var;
}
public final Integer getSVarInt(final String name) {
String var = sVars.get(name);
if (var != null) {
try {
return Integer.parseInt(var);
}
catch (Exception e) {}
}
return null;
} }
public final void setSVar(final String name, final String value) { public final void setSVar(final String name, final String value) {
@@ -298,18 +317,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public boolean isSpell() { return false; } public boolean isSpell() { return false; }
public boolean isAbility() { return true; } public boolean isAbility() { return true; }
/**
* <p>
* isMultiKicker.
* </p>
*
* @return a boolean.
*/
public boolean isMultiKicker() {
return this.multiKickerManaCost != null && !this.isAnnouncing("Multikicker");
}
/** /**
* <p> * <p>
* setIsMorphUp. * setIsMorphUp.
@@ -380,14 +387,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
} }
public String getParamOrDefault(String key, String defaultValue) { public String getParamOrDefault(String key, String defaultValue) {
return mapParams == null || !mapParams.containsKey(key) ? defaultValue : mapParams.get(key); return mapParams.containsKey(key) ? mapParams.get(key) : defaultValue;
} }
public String getParam(String key) { public String getParam(String key) {
return mapParams == null ? null : mapParams.get(key); return mapParams.get(key);
} }
public boolean hasParam(String key) { public boolean hasParam(String key) {
return mapParams == null ? false : mapParams.containsKey(key); return mapParams.containsKey(key);
} }
/** /**
@@ -395,10 +402,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
* @param mapParams * @param mapParams
*/ */
public void copyParamsToMap(Map<String, String> mapParams) { public void copyParamsToMap(Map<String, String> mapParams) {
if (null != this.mapParams) {
mapParams.putAll(this.mapParams); mapParams.putAll(this.mapParams);
} }
}
// If this is not null, then ability was made in a factory // If this is not null, then ability was made in a factory
public ApiType getApi() { public ApiType getApi() {
@@ -1127,9 +1132,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
} }
String[] validTgt = tr.getValidTgts(); String[] validTgt = tr.getValidTgts();
if (entity instanceof GameEntity && !((GameEntity) entity).isValid(validTgt, this.getActivatingPlayer(), this.getHostCard())) if (entity instanceof GameEntity && !((GameEntity) entity).isValid(validTgt, this.getActivatingPlayer(), this.getHostCard())) {
return false; return false;
} }
}
// Restrictions coming from target // Restrictions coming from target
return entity.canBeTargetedBy(this); return entity.canBeTargetedBy(this);
@@ -1298,7 +1304,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
while (null != parent.getParent()) { while (null != parent.getParent()) {
parent = parent.getParent(); parent = parent.getParent();
} }
return parent; return parent;
} }
@@ -1351,15 +1356,32 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
*/ */
public boolean isAnnouncing(String variable) { public boolean isAnnouncing(String variable) {
String announce = getParam("Announce"); String announce = getParam("Announce");
if (StringUtils.isBlank(announce)) return false; if (StringUtils.isBlank(announce)) { return false; }
String[] announcedOnes = TextUtil.split(announce, ','); String[] announcedOnes = TextUtil.split(announce, ',');
for (String a : announcedOnes) { for (String a : announcedOnes) {
if( a.trim().equalsIgnoreCase(variable)) if (a.trim().equalsIgnoreCase(variable)) {
return true; return true;
} }
}
return false; return false;
} }
public void addAnnounceVar(String variable) {
String announce = getParam("Announce");
if (StringUtils.isBlank(announce)) {
mapParams.put("Announce", variable);
return;
}
String[] announcedOnes = TextUtil.split(announce, ',');
for (String a : announcedOnes) {
if (a.trim().equalsIgnoreCase(variable)) {
return; //don't add announce variable that already exists
}
}
mapParams.put("Announce", announce + ";" + variable);
}
public boolean isXCost() { public boolean isXCost() {
CostPartMana cm = payCosts != null ? getPayCosts().getCostMana() : null; CostPartMana cm = payCosts != null ? getPayCosts().getCostMana() : null;
return cm != null && cm.getAmountOfX() > 0; return cm != null && cm.getAmountOfX() > 0;
@@ -1486,10 +1508,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
targetChosen.add(card); targetChosen.add(card);
final String desc; final String desc;
if (!card.isFaceDown()) { if (!card.isFaceDown()) {
desc = this.getHostCard().getName() + " - targeting " + card; desc = this.getHostCard().getName() + " - targeting " + card;
} else { }
else {
desc = this.getHostCard().getName() + " - targeting Morph(" + card.getUniqueNumber() + ")"; desc = this.getHostCard().getName() + " - targeting Morph(" + card.getUniqueNumber() + ")";
} }
this.setStackDescription(desc); this.setStackDescription(desc);
@@ -1532,7 +1554,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return ImmutableList.<Card>of(); return ImmutableList.<Card>of();
} }
public SpellAbility getSATargetingCard() { public SpellAbility getSATargetingCard() {
return targetChosen.isTargetingAnyCard() ? this : getParentTargetingCard(); return targetChosen.isTargetingAnyCard() ? this : getParentTargetingCard();
} }
@@ -1543,8 +1564,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
parent = ((WrappedAbility) parent).getWrappedAbility(); parent = ((WrappedAbility) parent).getWrappedAbility();
} }
while (parent != null) { while (parent != null) {
if (parent.targetChosen.isTargetingAnyCard()) if (parent.targetChosen.isTargetingAnyCard()) {
return parent; return parent;
}
parent = parent.getParent(); parent = parent.getParent();
} }
return null; return null;
@@ -1674,15 +1696,21 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
final String[] incR = restriction.split("\\.", 2); final String[] incR = restriction.split("\\.", 2);
if (incR[0].equals("Spell")) { if (incR[0].equals("Spell")) {
if (!this.isSpell()) if (!this.isSpell()) {
return false; return false;
} else if (incR[0].equals("Triggered")) { }
if (!this.isTrigger()) }
else if (incR[0].equals("Triggered")) {
if (!this.isTrigger()) {
return false; return false;
} else if (incR[0].equals("Activated")) { }
if (!(this instanceof AbilityActivated)) }
else if (incR[0].equals("Activated")) {
if (!(this instanceof AbilityActivated)) {
return false; return false;
} else { //not a spell/ability type }
}
else { //not a spell/ability type
return false; return false;
} }
@@ -1811,5 +1839,4 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
this.subAbility.setIntrinsic(i); this.subAbility.setIntrinsic(i);
} }
} }
} }

View File

@@ -243,11 +243,6 @@ public class WrappedAbility extends Ability implements ISpellAbility {
return sa.isFlashBackAbility(); return sa.isFlashBackAbility();
} }
@Override
public boolean isMultiKicker() {
return sa.isMultiKicker();
}
@Override @Override
public boolean isSpell() { public boolean isSpell() {
return sa.isSpell(); return sa.isSpell();

View File

@@ -332,27 +332,15 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
// TODO: make working triggered ability // TODO: make working triggered ability
sp.setTotalManaSpent(totManaSpent); sp.setTotalManaSpent(totManaSpent);
AbilityUtils.resolve(sp); AbilityUtils.resolve(sp);
} else { }
else {
for (OptionalCost s : sp.getOptionalCosts()) { for (OptionalCost s : sp.getOptionalCosts()) {
source.addOptionalCostPaid(s); source.addOptionalCostPaid(s);
} }
if (sp.isCopied()) { if (sp.isCopied()) {
si = this.push(sp); si = this.push(sp);
} else {
if (sp.isMultiKicker()) {
final Cost costMultikicker = new Cost(sp.getMultiKickerManaCost(), false);
boolean hasPaid = false;
do {
int mkMagnitude = source.getKickerMagnitude();
String prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", source, mkMagnitude );
hasPaid = activator.getController().payManaOptional(source, costMultikicker, sp, prompt, ManaPaymentPurpose.Multikicker);
if (hasPaid) {
source.addMultiKickerMagnitude(1);
totManaSpent += sp.getPayingMana().size();
// TODO: paying mana is replaced by multikicker cost, this should be fixed in the future
}
} while( hasPaid );
} }
else {
if (sp.isSpell() && source.isCreature() && Iterables.any(activator.getCardsIn(ZoneType.Battlefield), if (sp.isSpell() && source.isCreature() && Iterables.any(activator.getCardsIn(ZoneType.Battlefield),
CardPredicates.hasKeyword("As an additional cost to cast creature spells," + CardPredicates.hasKeyword("As an additional cost to cast creature spells," +
" you may pay any amount of mana. If you do, that creature enters " + " you may pay any amount of mana. If you do, that creature enters " +
@@ -394,10 +382,9 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
if (sp.isSpell() && (source.hasStartOfKeyword("Replicate") if (sp.isSpell() && (source.hasStartOfKeyword("Replicate")
|| ((source.isInstant() || source.isSorcery()) && Iterables.any(activator.getCardsIn(ZoneType.Battlefield), || ((source.isInstant() || source.isSorcery()) && Iterables.any(activator.getCardsIn(ZoneType.Battlefield),
CardPredicates.hasKeyword("Each instant and sorcery spell you cast has replicate. The replicate cost is equal to its mana cost."))))) { CardPredicates.hasKeyword("Each instant and sorcery spell you cast has replicate. The replicate cost is equal to its mana cost."))))) {
int magnitude = 0; Integer magnitude = sp.getSVarInt("Replicate");
// TODO: convert multikicker/replicate support in abCost so this if (magnitude == null) {
// doesn't happen here magnitude = 0;
final Cost costReplicate = new Cost(source.getManaCost(), false); final Cost costReplicate = new Cost(source.getManaCost(), false);
boolean hasPaid = false; boolean hasPaid = false;
int replicateCMC = source.getManaCost().getCMC(); int replicateCMC = source.getManaCost().getCMC();
@@ -409,14 +396,16 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
totManaSpent += replicateCMC; totManaSpent += replicateCMC;
} }
} while (hasPaid); } while (hasPaid);
}
// Replicate Trigger // Replicate Trigger
String effect = String.format("AB$ CopySpellAbility | Cost$ 0 | Defined$ SourceFirstSpell | Amount$ %d", magnitude); String effect = String.format("AB$ CopySpellAbility | Cost$ 0 | Defined$ SourceFirstSpell | Amount$ %d", magnitude);
SpellAbility sa = AbilityFactory.getAbility(effect, source); SpellAbility sa = AbilityFactory.getAbility(effect, source);
sa.setDescription("Replicate - " + source); sa.setDescription("Replicate - " + source);
sa.setTrigger(true); sa.setTrigger(true);
sa.setCopied(true);
addSimultaneousStackEntry(sa); addSimultaneousStackEntry(sa);
} }
} }
} }

View File

@@ -213,6 +213,9 @@ public class QuestPreferencesHandler extends SkinnedPanel {
pnlShop.add(new FLabel.Builder().text("Maximum Packs").build(), constraints2); pnlShop.add(new FLabel.Builder().text("Maximum Packs").build(), constraints2);
pnlShop.add(new PrefInput(QPref.SHOP_MAX_PACKS, ErrType.SHOP), constraints1); pnlShop.add(new PrefInput(QPref.SHOP_MAX_PACKS, ErrType.SHOP), constraints1);
pnlShop.add(new FLabel.Builder().text("Minimum Packs").build(), constraints2);
pnlShop.add(new PrefInput(QPref.SHOP_MIN_PACKS, ErrType.SHOP), constraints1);
pnlShop.add(new FLabel.Builder().text("Starting Packs").build(), constraints2); pnlShop.add(new FLabel.Builder().text("Starting Packs").build(), constraints2);
pnlShop.add(new PrefInput(QPref.SHOP_STARTING_PACKS, ErrType.SHOP), constraints1); pnlShop.add(new PrefInput(QPref.SHOP_STARTING_PACKS, ErrType.SHOP), constraints1);

View File

@@ -351,6 +351,9 @@ public enum VSubmenuQuestPrefs implements IVSubmenu<CSubmenuQuestPrefs> {
pnlShop.add(new FLabel.Builder().text("Maximum Packs").fontAlign(SwingConstants.RIGHT).build(), constraints2); pnlShop.add(new FLabel.Builder().text("Maximum Packs").fontAlign(SwingConstants.RIGHT).build(), constraints2);
pnlShop.add(new PrefInput(QPref.SHOP_MAX_PACKS, QuestPreferencesErrType.SHOP), constraints1); pnlShop.add(new PrefInput(QPref.SHOP_MAX_PACKS, QuestPreferencesErrType.SHOP), constraints1);
pnlShop.add(new FLabel.Builder().text("Minimum Packs").fontAlign(SwingConstants.RIGHT).build(), constraints2);
pnlShop.add(new PrefInput(QPref.SHOP_MIN_PACKS, QuestPreferencesErrType.SHOP), constraints1);
pnlShop.add(new FLabel.Builder().text("Starting Packs").fontAlign(SwingConstants.RIGHT).build(), constraints2); pnlShop.add(new FLabel.Builder().text("Starting Packs").fontAlign(SwingConstants.RIGHT).build(), constraints2);
pnlShop.add(new PrefInput(QPref.SHOP_STARTING_PACKS, QuestPreferencesErrType.SHOP), constraints1); pnlShop.add(new PrefInput(QPref.SHOP_STARTING_PACKS, QuestPreferencesErrType.SHOP), constraints1);

View File

@@ -54,7 +54,6 @@ public enum CSubmenuConstructed implements ICDoc, IMenuProvider {
*/ */
@Override @Override
public void update() { public void update() {
MenuUtil.setMenuProvider(this); MenuUtil.setMenuProvider(this);
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@@ -196,8 +195,25 @@ public enum CSubmenuConstructed implements ICDoc, IMenuProvider {
} }
} // Is it even possible anymore? I think current implementation assigns decks automatically. } // Is it even possible anymore? I think current implementation assigns decks automatically.
GameType autoGenerateVariant = null;
boolean isCommanderMatch = false;
if (!variantTypes.isEmpty()) {
isCommanderMatch = variantTypes.contains(GameType.Commander);
if (!isCommanderMatch) {
for (GameType variant : variantTypes) {
if (variant.isAutoGenerated()) {
autoGenerateVariant = variant;
break;
}
}
}
}
boolean checkLegality = FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY); boolean checkLegality = FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY);
if (checkLegality && !variantTypes.contains(GameType.Commander)) { //Commander deck replaces regular deck and is checked later
//Auto-generated decks don't need to be checked here
//Commander deck replaces regular deck and is checked later
if (checkLegality && autoGenerateVariant == null && !isCommanderMatch) {
for (final int i : view.getParticipants()) { for (final int i : view.getParticipants()) {
String name = view.getPlayerName(i); String name = view.getPlayerName(i);
String errMsg = GameType.Constructed.getDeckFormat().getDeckConformanceProblem(view.getDeckChooser(i).getPlayer().getDeck()); String errMsg = GameType.Constructed.getDeckFormat().getDeckConformanceProblem(view.getDeckChooser(i).getPlayer().getDeck());
@@ -220,9 +236,10 @@ public enum CSubmenuConstructed implements ICDoc, IMenuProvider {
if (variantTypes.isEmpty()) { if (variantTypes.isEmpty()) {
rp.setTeamNumber(view.getTeam(i)); rp.setTeamNumber(view.getTeam(i));
players.add(rp.setPlayer(lobbyPlayer)); players.add(rp.setPlayer(lobbyPlayer));
} else { }
else {
Deck deck = null; Deck deck = null;
boolean isCommanderMatch = variantTypes.contains(GameType.Commander); PaperCard vanguardAvatar = null;
if (isCommanderMatch) { if (isCommanderMatch) {
Object selected = view.getCommanderDeckLists().get(i).getSelectedValue(); Object selected = view.getCommanderDeckLists().get(i).getSelectedValue();
if (selected instanceof String) { if (selected instanceof String) {
@@ -231,7 +248,8 @@ public enum CSubmenuConstructed implements ICDoc, IMenuProvider {
if (sel.equals("Random") && comDecks.size() > 0) { if (sel.equals("Random") && comDecks.size() > 0) {
deck = Aggregates.random(comDecks); deck = Aggregates.random(comDecks);
} }
} else { }
else {
deck = (Deck) selected; deck = (Deck) selected;
} }
if (deck == null) { //Can be null if player deselects the list selection or chose Generate if (deck == null) { //Can be null if player deselects the list selection or chose Generate
@@ -245,13 +263,19 @@ public enum CSubmenuConstructed implements ICDoc, IMenuProvider {
} }
} }
} }
else if (autoGenerateVariant != null) {
deck = autoGenerateVariant.autoGenerateDeck(rp);
CardPool avatarPool = deck.get(DeckSection.Avatar);
if (avatarPool != null) {
vanguardAvatar = avatarPool.get(0);
}
}
// Initialise variables for other variants // Initialise variables for other variants
deck = deck == null ? rp.getDeck() : deck; deck = deck == null ? rp.getDeck() : deck;
Iterable<PaperCard> schemes = null; Iterable<PaperCard> schemes = null;
boolean playerIsArchenemy = view.isPlayerArchenemy(i); boolean playerIsArchenemy = view.isPlayerArchenemy(i);
Iterable<PaperCard> planes = null; Iterable<PaperCard> planes = null;
PaperCard vanguardAvatar = null;
//Archenemy //Archenemy
if (variantTypes.contains(GameType.ArchenemyRumble) if (variantTypes.contains(GameType.ArchenemyRumble)

View File

@@ -110,13 +110,12 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
// Variants frame and variables // Variants frame and variables
private final Set<GameType> appliedVariants = new TreeSet<GameType>(); private final Set<GameType> appliedVariants = new TreeSet<GameType>();
private final FPanel variantsPanel = new FPanel(new MigLayout("insets 10, gapx 10")); private final FPanel variantsPanel = new FPanel(new MigLayout("insets 10, gapx 10"));
private final FCheckBox vntVanguard = new FCheckBox("Vanguard"); private final VariantCheckBox vntVanguard = new VariantCheckBox(GameType.Vanguard);
private final FCheckBox vntCommander = new FCheckBox("Commander"); private final VariantCheckBox vntMomirBasic = new VariantCheckBox(GameType.MomirBasic);
private final FCheckBox vntPlanechase = new FCheckBox("Planechase"); private final VariantCheckBox vntCommander = new VariantCheckBox(GameType.Commander);
private final FCheckBox vntArchenemy = new FCheckBox("Archenemy"); private final VariantCheckBox vntPlanechase = new VariantCheckBox(GameType.Planechase);
private String archenemyType = "Classic"; private final VariantCheckBox vntArchenemy = new VariantCheckBox(GameType.Archenemy);
private final FComboBoxWrapper<String> comboArchenemy = new FComboBoxWrapper<String>(new String[]{ private final VariantCheckBox vntArchenemyRumble = new VariantCheckBox(GameType.ArchenemyRumble);
"Archenemy (Classic - One player is the Archenemy)", "Supervillan Rumble (All players are Archenemies)"});
// Player frame elements // Player frame elements
private final JPanel playersFrame = new JPanel(new MigLayout("insets 0, gap 0 5, wrap, hidemode 3")); private final JPanel playersFrame = new JPanel(new MigLayout("insets 0, gap 0 5, wrap, hidemode 3"));
@@ -160,22 +159,14 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
//////////////////// Variants Panel //////////////////// //////////////////// Variants Panel ////////////////////
// Populate and add variants panel
vntVanguard.addItemListener(iListenerVariants);
vntCommander.addItemListener(iListenerVariants);
vntPlanechase.addItemListener(iListenerVariants);
vntArchenemy.addItemListener(iListenerVariants);
comboArchenemy.setSelectedIndex(0);
comboArchenemy.setEnabled(vntArchenemy.isSelected());
comboArchenemy.addActionListener(aeComboListener);
variantsPanel.setOpaque(false); variantsPanel.setOpaque(false);
variantsPanel.add(newLabel("Variants:")); variantsPanel.add(newLabel("Variants:"));
variantsPanel.add(vntVanguard); variantsPanel.add(vntVanguard);
variantsPanel.add(vntMomirBasic);
variantsPanel.add(vntCommander); variantsPanel.add(vntCommander);
variantsPanel.add(vntPlanechase); variantsPanel.add(vntPlanechase);
variantsPanel.add(vntArchenemy); variantsPanel.add(vntArchenemy);
comboArchenemy.addTo(variantsPanel); variantsPanel.add(vntArchenemyRumble);
constructedFrame.add(new FScrollPane(variantsPanel, false, true, constructedFrame.add(new FScrollPane(variantsPanel, false, true,
ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
@@ -459,6 +450,8 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
container.validate(); container.validate();
container.repaint(); container.repaint();
} }
changePlayerFocus(playerWithFocus, currentGameMode);
} }
/** @return {@link javax.swing.JButton} */ /** @return {@link javax.swing.JButton} */
@@ -682,29 +675,62 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
}; };
public void updateVariantControlsVisibility() { public void updateVariantControlsVisibility() {
// Commander deck replaces basic deck, so hide that boolean isCommanderApplied = false;
deckLabel.setVisible(!appliedVariants.contains(GameType.Commander)); boolean isPlanechaseApplied = false;
deckBtn.setVisible(!appliedVariants.contains(GameType.Commander)); boolean isVanguardApplied = false;
cmdDeckSelectorBtn.setVisible(appliedVariants.contains(GameType.Commander)); boolean isArchenemyApplied = false;
cmdDeckEditor.setVisible(appliedVariants.contains(GameType.Commander)); boolean archenemyVisiblity = false;
cmdLabel.setVisible(appliedVariants.contains(GameType.Commander)); boolean isDeckBuildingAllowed = true;
for (GameType variant : appliedVariants) {
switch (variant) {
case Archenemy:
isArchenemyApplied = true;
if (playerIsArchenemy) {
archenemyVisiblity = true;
}
break;
case ArchenemyRumble:
archenemyVisiblity = true;
break;
case Commander:
isCommanderApplied = true;
isDeckBuildingAllowed = false; //Commander deck replaces basic deck, so hide that
break;
case Planechase:
isPlanechaseApplied = true;
break;
case Vanguard:
isVanguardApplied = true;
break;
default:
if (variant.isAutoGenerated()) {
isDeckBuildingAllowed = false;
}
break;
}
}
deckLabel.setVisible(isDeckBuildingAllowed);
deckBtn.setVisible(isDeckBuildingAllowed);
cmdDeckSelectorBtn.setVisible(isCommanderApplied);
cmdDeckEditor.setVisible(isCommanderApplied);
cmdLabel.setVisible(isCommanderApplied);
boolean archenemyVisiblity = appliedVariants.contains(GameType.ArchenemyRumble)
|| (appliedVariants.contains(GameType.Archenemy) && playerIsArchenemy);
scmDeckSelectorBtn.setVisible(archenemyVisiblity); scmDeckSelectorBtn.setVisible(archenemyVisiblity);
scmDeckEditor.setVisible(archenemyVisiblity); scmDeckEditor.setVisible(archenemyVisiblity);
scmLabel.setVisible(archenemyVisiblity); scmLabel.setVisible(archenemyVisiblity);
teamComboBox.setVisible(!appliedVariants.contains(GameType.Archenemy)); teamComboBox.setVisible(!isArchenemyApplied);
aeTeamComboBox.setVisible(appliedVariants.contains(GameType.Archenemy)); aeTeamComboBox.setVisible(isArchenemyApplied);
aeTeamComboBox.setEnabled(!(appliedVariants.contains(GameType.Archenemy) && playerIsArchenemy)); aeTeamComboBox.setEnabled(!(isArchenemyApplied && playerIsArchenemy));
pchDeckSelectorBtn.setVisible(appliedVariants.contains(GameType.Planechase)); pchDeckSelectorBtn.setVisible(isPlanechaseApplied);
pchDeckEditor.setVisible(appliedVariants.contains(GameType.Planechase)); pchDeckEditor.setVisible(isPlanechaseApplied);
pchLabel.setVisible(appliedVariants.contains(GameType.Planechase)); pchLabel.setVisible(isPlanechaseApplied);
vgdSelectorBtn.setVisible(appliedVariants.contains(GameType.Vanguard)); vgdSelectorBtn.setVisible(isVanguardApplied);
vgdLabel.setVisible(appliedVariants.contains(GameType.Vanguard)); vgdLabel.setVisible(isVanguardApplied);
} }
@Override @Override
@@ -779,7 +805,8 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
public void toggleIsPlayerArchenemy() { public void toggleIsPlayerArchenemy() {
if (appliedVariants.contains(GameType.Archenemy)) { if (appliedVariants.contains(GameType.Archenemy)) {
playerIsArchenemy = lastArchenemy == index; playerIsArchenemy = lastArchenemy == index;
} else { }
else {
playerIsArchenemy = appliedVariants.contains(GameType.ArchenemyRumble); playerIsArchenemy = appliedVariants.contains(GameType.ArchenemyRumble);
} }
updateVariantControlsVisibility(); updateVariantControlsVisibility();
@@ -793,7 +820,7 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
scmDeckSelectorBtn.setCommand(new Runnable() { scmDeckSelectorBtn.setCommand(new Runnable() {
@Override @Override
public void run() { public void run() {
currentGameMode = archenemyType.contains("Classic") ? GameType.Archenemy : GameType.ArchenemyRumble; currentGameMode = vntArchenemy.isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble;
scmDeckSelectorBtn.requestFocusInWindow(); scmDeckSelectorBtn.requestFocusInWindow();
changePlayerFocus(index, currentGameMode); changePlayerFocus(index, currentGameMode);
} }
@@ -802,7 +829,7 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
scmDeckEditor.setCommand(new UiCommand() { scmDeckEditor.setCommand(new UiCommand() {
@Override @Override
public void run() { public void run() {
currentGameMode = archenemyType.contains("Classic") ? GameType.Archenemy : GameType.ArchenemyRumble; currentGameMode = vntArchenemy.isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble;
Predicate<PaperCard> predSchemes = new Predicate<PaperCard>() { Predicate<PaperCard> predSchemes = new Predicate<PaperCard>() {
@Override @Override
public boolean apply(PaperCard arg0) { public boolean apply(PaperCard arg0) {
@@ -1139,36 +1166,45 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
///////////////////////////////////////////// /////////////////////////////////////////////
//========== Various listeners in build order //========== Various listeners in build order
/** This listener unlocks the relevant buttons for players @SuppressWarnings("serial")
* and enables/disables archenemy combobox as appropriate. */ private class VariantCheckBox extends FCheckBox {
private ItemListener iListenerVariants = new ItemListener() { private final GameType variantType;
private VariantCheckBox(GameType variantType0) {
super(variantType0.toString());
variantType = variantType0;
setToolTipText(variantType.getDescription());
addItemListener(new ItemListener() {
@Override @Override
public void itemStateChanged(ItemEvent arg0) { public void itemStateChanged(ItemEvent e) {
FCheckBox cb = (FCheckBox) arg0.getSource(); if (e.getStateChange() == ItemEvent.SELECTED) {
GameType variantType = null;
if (cb == vntVanguard) {
variantType = GameType.Vanguard;
}
else if (cb == vntCommander) {
variantType = GameType.Commander;
}
else if (cb == vntPlanechase) {
variantType = GameType.Planechase;
}
else if (cb == vntArchenemy) {
variantType = archenemyType.contains("Classic") ? GameType.Archenemy : GameType.ArchenemyRumble;
comboArchenemy.setEnabled(vntArchenemy.isSelected());
if (arg0.getStateChange() != ItemEvent.SELECTED) {
appliedVariants.remove(GameType.Archenemy);
appliedVariants.remove(GameType.ArchenemyRumble);
}
}
if (null != variantType) {
if (arg0.getStateChange() == ItemEvent.SELECTED) {
appliedVariants.add(variantType); appliedVariants.add(variantType);
currentGameMode = variantType; currentGameMode = variantType;
//ensure other necessary variants are unchecked
switch (variantType) {
case Archenemy:
vntArchenemyRumble.setSelected(false);
break;
case ArchenemyRumble:
vntArchenemy.setSelected(false);
break;
case Commander:
vntMomirBasic.setSelected(false);
break;
case Vanguard:
vntMomirBasic.setSelected(false);
break;
case MomirBasic:
vntCommander.setSelected(false);
vntVanguard.setSelected(false);
break;
default:
break;
}
} }
else { else {
appliedVariants.remove(variantType); appliedVariants.remove(variantType);
@@ -1176,36 +1212,15 @@ public enum VSubmenuConstructed implements IVSubmenu<CSubmenuConstructed> {
currentGameMode = GameType.Constructed; currentGameMode = GameType.Constructed;
} }
} }
}
for (PlayerPanel pp : playerPanels) { for (PlayerPanel pp : playerPanels) {
pp.toggleIsPlayerArchenemy(); pp.toggleIsPlayerArchenemy();
pp.updateVariantControlsVisibility();
} }
changePlayerFocus(playerWithFocus, currentGameMode); changePlayerFocus(playerWithFocus, currentGameMode);
} }
}; });
// Listens to the archenemy combo box
private ActionListener aeComboListener = new ActionListener() {
@SuppressWarnings("unchecked")
@Override
public void actionPerformed(ActionEvent e) {
FComboBox<String> cb = (FComboBox<String>)e.getSource();
archenemyType = (String)cb.getSelectedItem();
GameType mode = archenemyType.contains("Classic") ? GameType.Archenemy : GameType.ArchenemyRumble;
appliedVariants.remove(GameType.Archenemy);
appliedVariants.remove(GameType.ArchenemyRumble);
appliedVariants.add(mode);
currentGameMode = mode;
for (PlayerPanel pp : playerPanels) {
pp.toggleIsPlayerArchenemy();
pp.updateVariantControlsVisibility();
} }
changePlayerFocus(playerWithFocus, currentGameMode);
} }
};
private ActionListener nameListener = new ActionListener() { private ActionListener nameListener = new ActionListener() {
@Override @Override

View File

@@ -14,6 +14,15 @@ The details previously available by hovering over that rectangle will now appear
The dialog for the commander replacement effect will now display the commander's name. The dialog for the commander replacement effect will now display the commander's name.
- Momir Basic variant type -
Momir Basic is now available as its own variant option on the Constructed screen
For this format. each player will automatically be given a deck with 12 of each basic land and the Momir Vig avatar
- Choose value for X mana costs -
Now, when playing spells/abilities with X in its mana cost, you will now be prompted for a value for X prior to mana payment, ensuring the final mana cost is calculated properly from cost adjustment effects and allowing using the "Auto" button to pay the entire cost.
This also applies to spells with Replicate and Multikicker to allow picking the Replicate or Multikicker amount prior to paying the final mana cost.
- Auto-targeting support - - Auto-targeting support -
When playing spells and abilities with the text "target opponent", if you only have one opponent, you will no longer be asked to choose the opponent to target. When playing spells and abilities with the text "target opponent", if you only have one opponent, you will no longer be asked to choose the opponent to target.
When triggered abilities have only one valid target, that target will now be auto-selected. When triggered abilities have only one valid target, that target will now be auto-selected.

View File

@@ -4,7 +4,7 @@ Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ W U | SpellDescription$ Add {W}{U} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ W U | SpellDescription$ Add {W}{U} to your mana pool.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:DB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:DB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/azorius_chancery.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/azorius_chancery.jpg
Oracle:Azorius Chancery enters the battlefield tapped.\nWhen Azorius Chancery enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {W}{U} to your mana pool. Oracle:Azorius Chancery enters the battlefield tapped.\nWhen Azorius Chancery enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {W}{U} to your mana pool.

View File

@@ -4,7 +4,7 @@ Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ R W | SpellDescription$ Add {R}{W} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ R W | SpellDescription$ Add {R}{W} to your mana pool.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:DB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:DB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/boros_garrison.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/boros_garrison.jpg
Oracle:Boros Garrison enters the battlefield tapped.\nWhen Boros Garrison enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {R}{W} to your mana pool. Oracle:Boros Garrison enters the battlefield tapped.\nWhen Boros Garrison enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {R}{W} to your mana pool.

View File

@@ -4,7 +4,7 @@ Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ U B | SpellDescription$ Add {U}{B} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ U B | SpellDescription$ Add {U}{B} to your mana pool.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/dimir_aqueduct.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/dimir_aqueduct.jpg
Oracle:Dimir Aqueduct enters the battlefield tapped.\nWhen Dimir Aqueduct enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {U}{B} to your mana pool. Oracle:Dimir Aqueduct enters the battlefield tapped.\nWhen Dimir Aqueduct enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {U}{B} to your mana pool.

View File

@@ -4,7 +4,7 @@ Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ B G | SpellDescription$ Add {B}{G} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ B G | SpellDescription$ Add {B}{G} to your mana pool.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/golgari_rot_farm.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/golgari_rot_farm.jpg
Oracle:Golgari Rot Farm enters the battlefield tapped.\nWhen Golgari Rot Farm enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {B}{G} to your mana pool. Oracle:Golgari Rot Farm enters the battlefield tapped.\nWhen Golgari Rot Farm enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {B}{G} to your mana pool.

View File

@@ -4,7 +4,7 @@ Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ R G | SpellDescription$ Add {R}{G} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ R G | SpellDescription$ Add {R}{G} to your mana pool.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/gruul_turf.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/gruul_turf.jpg
Oracle:Gruul Turf enters the battlefield tapped.\nWhen Gruul Turf enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {R}{G} to your mana pool. Oracle:Gruul Turf enters the battlefield tapped.\nWhen Gruul Turf enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {R}{G} to your mana pool.

View File

@@ -4,7 +4,7 @@ Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ U R | SpellDescription$ Add {U}{R} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ U R | SpellDescription$ Add {U}{R} to your mana pool.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/izzet_boilerworks.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/izzet_boilerworks.jpg
Oracle:Izzet Boilerworks enters the battlefield tapped.\nWhen Izzet Boilerworks enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {U}{R} to your mana pool. Oracle:Izzet Boilerworks enters the battlefield tapped.\nWhen Izzet Boilerworks enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {U}{R} to your mana pool.

View File

@@ -3,7 +3,7 @@ ManaCost:no cost
Types:Land Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
A:AB$ Mana | Cost$ T | Produced$ W B | SpellDescription$ Add {W}{B} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ W B | SpellDescription$ Add {W}{B} to your mana pool.
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/orzhov_basilica.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/orzhov_basilica.jpg

View File

@@ -4,7 +4,7 @@ Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ B R | SpellDescription$ Add {B}{R} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ B R | SpellDescription$ Add {B}{R} to your mana pool.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/rakdos_carnarium.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/rakdos_carnarium.jpg
Oracle:Rakdos Carnarium enters the battlefield tapped.\nWhen Rakdos Carnarium enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {B}{R} to your mana pool. Oracle:Rakdos Carnarium enters the battlefield tapped.\nWhen Rakdos Carnarium enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {B}{R} to your mana pool.

View File

@@ -4,7 +4,7 @@ Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ G W | SpellDescription$ Add {G}{W} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ G W | SpellDescription$ Add {G}{W} to your mana pool.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/selesnya_sanctuary.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/selesnya_sanctuary.jpg
Oracle:Selesnya Sanctuary enters the battlefield tapped.\nWhen Selesnya Sanctuary enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {G}{W} to your mana pool. Oracle:Selesnya Sanctuary enters the battlefield tapped.\nWhen Selesnya Sanctuary enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {G}{W} to your mana pool.

View File

@@ -4,7 +4,7 @@ Types:Land
K:CARDNAME enters the battlefield tapped. K:CARDNAME enters the battlefield tapped.
A:AB$ Mana | Cost$ T | Produced$ G U | SpellDescription$ Add {G}{U} to your mana pool. A:AB$ Mana | Cost$ T | Produced$ G U | SpellDescription$ Add {G}{U} to your mana pool.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When CARDNAME enters the battlefield, return a land you control to its owner's hand.
SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl SVar:TrigReturn:AB$ChangeZone | Origin$ Battlefield | Destination$ Hand | Cost$ 0 | Hidden$ True | Mandatory$ True | ChangeType$ Land.YouCtrl | AILogic$ NeverBounceItself
SVar:NeedsToPlay:Land.Basic+YouCtrl SVar:NeedsToPlay:Land.Basic+YouCtrl
SVar:Picture:http://www.wizards.com/global/images/magic/general/simic_growth_chamber.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/simic_growth_chamber.jpg
Oracle:Simic Growth Chamber enters the battlefield tapped.\nWhen Simic Growth Chamber enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {G}{U} to your mana pool. Oracle:Simic Growth Chamber enters the battlefield tapped.\nWhen Simic Growth Chamber enters the battlefield, return a land you control to its owner's hand.\n{T}: Add {G}{U} to your mana pool.

View File

@@ -41,7 +41,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
private final Object zoneToRestore; private final Object zoneToRestore;
private boolean bPaid = false; private boolean bPaid = false;
private Boolean canPayManaCost = null; protected Boolean canPayManaCost = null;
private boolean locked = false; private boolean locked = false;
@@ -343,7 +343,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
return true; return true;
} }
private void runAsAi(Runnable proc) { protected void runAsAi(Runnable proc) {
player.runWithController(proc, new PlayerControllerAi(game, player, player.getOriginalLobbyPlayer())); player.runWithController(proc, new PlayerControllerAi(game, player, player.getOriginalLobbyPlayer()));
} }

View File

@@ -4,9 +4,7 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.FThreads; import forge.FThreads;
import forge.card.MagicColor;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostShard;
import forge.game.Game; import forge.game.Game;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.GameLogEntryType; import forge.game.GameLogEntryType;
@@ -27,7 +25,6 @@ import forge.game.zone.ZoneType;
import forge.match.input.InputPayMana; import forge.match.input.InputPayMana;
import forge.match.input.InputPayManaOfCostPayment; import forge.match.input.InputPayManaOfCostPayment;
import forge.match.input.InputPayManaSimple; import forge.match.input.InputPayManaSimple;
import forge.match.input.InputPayManaX;
import forge.match.input.InputSelectCardsFromList; import forge.match.input.InputSelectCardsFromList;
import forge.util.Lang; import forge.util.Lang;
import forge.util.gui.SGuiChoose; import forge.util.gui.SGuiChoose;
@@ -635,8 +632,9 @@ public class HumanPlay {
prompt = source + "\n" + promptCurrent; prompt = source + "\n" + promptCurrent;
} }
if( sourceAbility != null ) if (sourceAbility != null) {
sourceAbility.clearManaPaid(); sourceAbility.clearManaPaid();
}
boolean paid = p.getController().payManaCost(cost.getCostMana(), sourceAbility, prompt, false); boolean paid = p.getController().payManaCost(cost.getCostMana(), sourceAbility, prompt, false);
if (!paid) { if (!paid) {
p.getManaPool().refundManaPaid(sourceAbility); p.getManaPool().refundManaPaid(sourceAbility);
@@ -692,24 +690,32 @@ public class HumanPlay {
final Card source = ability.getHostCard(); final Card source = ability.getHostCard();
ManaCostBeingPaid toPay = new ManaCostBeingPaid(realCost, mc.getRestiction()); ManaCostBeingPaid toPay = new ManaCostBeingPaid(realCost, mc.getRestiction());
boolean xWasBilled = false;
String xInCard = source.getSVar("X"); String xInCard = source.getSVar("X");
if (mc.getAmountOfX() > 0 && !"Count$xPaid".equals(xInCard)) { // announce X will overwrite whatever was in card script if (mc.getAmountOfX() > 0 && !"Count$xPaid".equals(xInCard)) { // announce X will overwrite whatever was in card script
// this currently only works for things about Targeted object int xPaid = AbilityUtils.calculateAmount(source, "X", ability);
int xCost = AbilityUtils.calculateAmount(source, "X", ability) * mc.getAmountOfX(); toPay.setXManaCostPaid(xPaid, ability.getParam("XColor"));
byte xColor = MagicColor.fromName(ability.hasParam("XColor") ? ability.getParam("XColor") : "1"); source.setXManaCostPaid(xPaid);
toPay.increaseShard(ManaCostShard.valueOf(xColor), xCost);
xWasBilled = true;
}
int timesMultikicked = ability.getHostCard().getKickerMagnitude();
if ( timesMultikicked > 0 && ability.isAnnouncing("Multikicker")) {
ManaCost mkCost = ability.getMultiKickerManaCost();
for(int i = 0; i < timesMultikicked; i++)
toPay.addManaCost(mkCost);
} }
if( isActivatedSa ) int timesMultikicked = source.getKickerMagnitude();
if (timesMultikicked > 0 && ability.isAnnouncing("Multikicker")) {
ManaCost mkCost = ability.getMultiKickerManaCost();
for (int i = 0; i < timesMultikicked; i++) {
toPay.addManaCost(mkCost);
}
}
Integer replicate = ability.getSVarInt("Replicate");
if (replicate != null) {
ManaCost rCost = source.getManaCost();
for (int i = 0; i < replicate; i++) {
toPay.addManaCost(rCost);
}
}
if (isActivatedSa) {
ManaCostAdjustment.adjust(toPay, ability, false); ManaCostAdjustment.adjust(toPay, ability, false);
}
InputPayMana inpPayment; InputPayMana inpPayment;
if (ability.isOffering() && ability.getSacrificedAsOffering() == null) { if (ability.isOffering() && ability.getSacrificedAsOffering() == null) {
@@ -727,19 +733,6 @@ public class HumanPlay {
source.setColorsPaid(toPay.getColorsPaid()); source.setColorsPaid(toPay.getColorsPaid());
source.setSunburstValue(toPay.getSunburst()); source.setSunburstValue(toPay.getSunburst());
} }
if (mc.getAmountOfX() > 0) {
if (!ability.isAnnouncing("X") && !xWasBilled) {
source.setXManaCostPaid(0);
inpPayment = new InputPayManaX(controller, ability, mc.getAmountOfX(), mc.canXbe0());
inpPayment.showAndWait();
if (!inpPayment.isPaid()) {
return false;
}
} else {
int x = AbilityUtils.calculateAmount(source, "X", ability);
source.setXManaCostPaid(x);
}
}
// Handle convoke and offerings // Handle convoke and offerings
if (ability.isOffering() && ability.getSacrificedAsOffering() != null) { if (ability.isOffering() && ability.getSacrificedAsOffering() != null) {

View File

@@ -196,6 +196,14 @@ public class HumanPlaySpellAbility {
} }
private boolean announceValuesLikeX() { private boolean announceValuesLikeX() {
if (ability.isCopied()) { return true; } //don't re-announce for spell copies
boolean needX = true;
boolean allowZero = !ability.hasParam("XCantBe0");
CostPartMana manaCost = ability.getPayCosts().getCostMana();
PlayerController controller = ability.getActivatingPlayer().getController();
Card card = ability.getHostCard();
// Announcing Requirements like Choosing X or Multikicker // Announcing Requirements like Choosing X or Multikicker
// SA Params as comma delimited list // SA Params as comma delimited list
String announce = ability.getParam("Announce"); String announce = ability.getParam("Announce");
@@ -204,28 +212,39 @@ public class HumanPlaySpellAbility {
String varName = aVar.trim(); String varName = aVar.trim();
boolean isX = "X".equalsIgnoreCase(varName); boolean isX = "X".equalsIgnoreCase(varName);
CostPartMana manaCost = ability.getPayCosts().getCostMana(); if (isX) { needX = false; }
boolean allowZero = !ability.hasParam("XCantBe0") && (!isX || manaCost == null || manaCost.canXbe0());
Integer value = ability.getActivatingPlayer().getController().announceRequirements(ability, varName, allowZero); Integer value = controller.announceRequirements(ability, varName, allowZero && (!isX || manaCost == null || manaCost.canXbe0()));
if (value == null) { if (value == null) {
return false; return false;
} }
ability.setSVar(varName, value.toString()); ability.setSVar(varName, value.toString());
if ("Multikicker".equals(varName)) { if ("Multikicker".equals(varName)) {
ability.getHostCard().setKickerMagnitude(value); card.setKickerMagnitude(value);
} }
else { else {
ability.getHostCard().setSVar(varName, value.toString()); card.setSVar(varName, value.toString());
} }
} }
} }
if (needX && manaCost != null && manaCost.getAmountOfX() > 0) {
Integer value = controller.announceRequirements(ability, "X", allowZero && manaCost.canXbe0());
if (value == null) {
return false;
}
ability.setSVar("X", value.toString());
card.setSVar("X", value.toString());
}
return true; return true;
} }
private boolean announceType() {
// Announcing Requirements like choosing creature type or number // Announcing Requirements like choosing creature type or number
private boolean announceType() {
if (ability.isCopied()) { return true; } //don't re-announce for spell copies
String announce = ability.getParam("AnnounceType"); String announce = ability.getParam("AnnounceType");
PlayerController pc = ability.getActivatingPlayer().getController(); PlayerController pc = ability.getActivatingPlayer().getController();
if (announce != null) { if (announce != null) {

View File

@@ -665,11 +665,12 @@ public final class QuestUtilCards {
final int startPacks = this.qpref.getPrefInt(QPref.SHOP_STARTING_PACKS); final int startPacks = this.qpref.getPrefInt(QPref.SHOP_STARTING_PACKS);
final int winsForPack = this.qpref.getPrefInt(QPref.SHOP_WINS_FOR_ADDITIONAL_PACK); final int winsForPack = this.qpref.getPrefInt(QPref.SHOP_WINS_FOR_ADDITIONAL_PACK);
final int maxPacks = this.qpref.getPrefInt(QPref.SHOP_MAX_PACKS); final int maxPacks = this.qpref.getPrefInt(QPref.SHOP_MAX_PACKS);
final int minPacks = this.qpref.getPrefInt(QPref.SHOP_MIN_PACKS);
int level = this.qc.getAchievements().getLevel(); int level = this.qc.getAchievements().getLevel();
final int levelPacks = level > 0 ? startPacks / level : startPacks; final int levelPacks = level > 0 ? startPacks / level : startPacks;
final int winPacks = this.qc.getAchievements().getWin() / winsForPack; final int winPacks = this.qc.getAchievements().getWin() / winsForPack;
final int totalPacks = Math.min(levelPacks + winPacks, maxPacks); final int totalPacks = Math.min(Math.max(levelPacks + winPacks, minPacks), maxPacks);
SealedProduct.Template tpl = getShopBoosterTemplate(); SealedProduct.Template tpl = getShopBoosterTemplate();

View File

@@ -135,7 +135,8 @@ public class QuestPreferences extends PreferencesStore<QuestPreferences.QPref> i
WINS_UNLOCK_SET("20"), WINS_UNLOCK_SET("20"),
// Maximum amount of "Packs" opened by the Shop and available as singles // Maximum amount of "Packs" opened by the Shop and available as singles
SHOP_MAX_PACKS("6"), SHOP_MAX_PACKS("7"),
SHOP_MIN_PACKS("3"),
// Rarity distribution of Singles in an Opened Shop Pack // Rarity distribution of Singles in an Opened Shop Pack
SHOP_SINGLES_COMMON("7"), SHOP_SINGLES_COMMON("7"),
@@ -145,7 +146,7 @@ public class QuestPreferences extends PreferencesStore<QuestPreferences.QPref> i
// How many wins it takes to open an additional pack in the shop // How many wins it takes to open an additional pack in the shop
SHOP_WINS_FOR_ADDITIONAL_PACK("10"), SHOP_WINS_FOR_ADDITIONAL_PACK("10"),
// How many packs the shop start with. // How many packs the shop start with.
SHOP_STARTING_PACKS("4"); SHOP_STARTING_PACKS("5");
private final String strDefaultVal; private final String strDefaultVal;
@@ -313,7 +314,7 @@ public class QuestPreferences extends PreferencesStore<QuestPreferences.QPref> i
return "Value too large (maximum 15)."; return "Value too large (maximum 15).";
} }
break; break;
case SHOP_WINS_FOR_ADDITIONAL_PACK: case SHOP_MAX_PACKS: case SHOP_WINS_FOR_ADDITIONAL_PACK: case SHOP_MAX_PACKS: case SHOP_MIN_PACKS:
if (val < 1) { if (val < 1) {
return "Value too small (minimum 1)."; return "Value too small (minimum 1).";
} else if (val > 25) { } else if (val > 25) {

View File

@@ -99,12 +99,15 @@ public class SGuiChoose {
// Get Integer in range // Get Integer in range
public static Integer getInteger(final IGuiBase gui, final String message) { public static Integer getInteger(final IGuiBase gui, final String message) {
return getInteger(gui, message, 0, Integer.MAX_VALUE); return getInteger(gui, message, 0, Integer.MAX_VALUE, false);
} }
public static Integer getInteger(final IGuiBase gui, final String message, int min) { public static Integer getInteger(final IGuiBase gui, final String message, int min) {
return getInteger(gui, message, min, Integer.MAX_VALUE); return getInteger(gui, message, min, Integer.MAX_VALUE, false);
} }
public static Integer getInteger(final IGuiBase gui, final String message, int min, int max) { public static Integer getInteger(final IGuiBase gui, final String message, int min, int max) {
return getInteger(gui, message, min, max, false);
}
public static Integer getInteger(final IGuiBase gui, final String message, int min, int max, boolean sortDesc) {
if (max <= min) { return min; } //just return min if max <= min if (max <= min) { return min; } //just return min if max <= min
//force cutting off after 100 numbers at most //force cutting off after 100 numbers at most
@@ -117,9 +120,16 @@ public class SGuiChoose {
} }
final Integer[] choices = new Integer[count]; final Integer[] choices = new Integer[count];
if (sortDesc) {
for (int i = 0; i < count; i++) {
choices[count - i - 1] = Integer.valueOf(i + min);
}
}
else {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
choices[i] = Integer.valueOf(i + min); choices[i] = Integer.valueOf(i + min);
} }
}
return SGuiChoose.oneOrNone(gui, message, choices); return SGuiChoose.oneOrNone(gui, message, choices);
} }
public static Integer getInteger(final IGuiBase gui, final String message, int min, int max, int cutoff) { public static Integer getInteger(final IGuiBase gui, final String message, int min, int max, int cutoff) {