OptionalCosts (Buyback, Kicker, AltCost) moved to EnumSet (there was a string list)

intrinsic abilities renamed to unparsed ones,
getMultiKickerMagnitude is getKickerMagnitude, it returns 1 for simple Kickers now
Kicker syntax changed,
AltCost avaiable to check on triggers
This commit is contained in:
Maxmtg
2013-04-16 17:53:51 +00:00
parent 33ade082ab
commit 44554441ea
25 changed files with 363 additions and 540 deletions

1
.gitattributes vendored
View File

@@ -13839,6 +13839,7 @@ src/main/java/forge/card/spellability/AbilitySub.java svneol=native#text/plain
src/main/java/forge/card/spellability/AbilityTriggered.java svneol=native#text/plain
src/main/java/forge/card/spellability/HumanPlaySpellAbility.java svneol=native#text/plain
src/main/java/forge/card/spellability/ISpellAbility.java -text
src/main/java/forge/card/spellability/OptionalCost.java -text
src/main/java/forge/card/spellability/Spell.java svneol=native#text/plain
src/main/java/forge/card/spellability/SpellAbility.java svneol=native#text/plain
src/main/java/forge/card/spellability/SpellAbilityCondition.java svneol=native#text/plain

View File

@@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -51,6 +52,7 @@ import forge.card.replacement.ReplaceMoved;
import forge.card.replacement.ReplacementEffect;
import forge.card.replacement.ReplacementResult;
import forge.card.spellability.AbilityTriggered;
import forge.card.spellability.OptionalCost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellPermanent;
import forge.card.spellability.Target;
@@ -110,7 +112,6 @@ public class Card extends GameEntity implements Comparable<Card> {
// if this card is an Aura, what Entity is it enchanting?
private GameEntity enchanting = null;
private ArrayList<String> optionalAdditionalCostsPaid = null;
// changes by AF animate and continuous static effects - timestamp is the key of maps
private Map<Long, CardType> changedCardTypes = new ConcurrentSkipListMap<Long, CardType>();
@@ -182,7 +183,6 @@ public class Card extends GameEntity implements Comparable<Card> {
private int xManaCostPaid = 0;
private int multiKickerMagnitude = 0;
private int replicateMagnitude = 0;
private int sunburstValue = 0;
@@ -3892,23 +3892,14 @@ public class Card extends GameEntity implements Comparable<Card> {
return this.getNetAttack();
}
/**
* <p>
* addMultiKickerMagnitude.
* </p>
*
* @param n
* a int.
*/
public final void addMultiKickerMagnitude(final int n) {
this.multiKickerMagnitude += n;
}
public final void setMultiKickerMagnitude(final int n) {
this.multiKickerMagnitude = n;
}
public final int getMultiKickerMagnitude() {
return this.multiKickerMagnitude;
private int multiKickerMagnitude = 0;
public final void addMultiKickerMagnitude(final int n) { this.multiKickerMagnitude += n; }
public final void setKickerMagnitude(final int n) { this.multiKickerMagnitude = n; }
public final int getKickerMagnitude() {
if ( this.multiKickerMagnitude > 0 )
return multiKickerMagnitude;
boolean hasK1 = costsPaid.contains(OptionalCost.Kicker1);
return hasK1 == costsPaid.contains(OptionalCost.Kicker2) ? (hasK1 ? 2 : 0) : 1;
}
/**
@@ -4290,8 +4281,8 @@ public class Card extends GameEntity implements Comparable<Card> {
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<String> getIntrinsicAbilities() {
return this.getCharacteristics().getIntrinsicAbility();
public final List<String> getUnparsedAbilities() {
return this.getCharacteristics().getUnparsedAbilities();
}
/**
@@ -4328,8 +4319,8 @@ public class Card extends GameEntity implements Comparable<Card> {
* @param a
* a {@link java.util.ArrayList} object.
*/
public final void setIntrinsicAbilities(final ArrayList<String> a) {
this.getCharacteristics().setIntrinsicAbility(new ArrayList<String>(a));
public final void setIntrinsicAbilities(final List<String> a) {
this.getCharacteristics().setUnparsedAbilities(new ArrayList<String>(a));
}
/**
@@ -4358,7 +4349,7 @@ public class Card extends GameEntity implements Comparable<Card> {
*/
public final void addIntrinsicAbility(final String s) {
if (s.trim().length() != 0) {
this.getCharacteristics().getIntrinsicAbility().add(s);
this.getCharacteristics().getUnparsedAbilities().add(s);
}
}
@@ -4796,64 +4787,6 @@ public class Card extends GameEntity implements Comparable<Card> {
this.suspendCast = b;
}
/**
* <p>
* optionalAdditionalCostsPaid.
* </p>
*
* @param cost
* a String.
*/
public final void addOptionalAdditionalCostsPaid(final String cost) {
if (optionalAdditionalCostsPaid == null) {
optionalAdditionalCostsPaid = new ArrayList<String>();
}
this.optionalAdditionalCostsPaid.add(cost);
}
/**
* <p>
* optionalAdditionalCostsPaid.
* </p>
*/
public final void clearAdditionalCostsPaid() {
if (optionalAdditionalCostsPaid != null) {
optionalAdditionalCostsPaid.clear();
}
}
/**
* <p>
* isOptionalAdditionalCostsPaid.
* </p>
*
* @param cost
* a String.
* @return a boolean.
*/
public final boolean isOptionalAdditionalCostsPaid(final String cost) {
if (optionalAdditionalCostsPaid == null) {
return false;
}
for (String s : optionalAdditionalCostsPaid) {
if (s.startsWith(cost)) {
return true;
}
}
return false;
}
/**
* <p>
* isOptionalAdditionalCostsPaid.
* </p>
* @return an ArrayList<String>.
*
*/
public final ArrayList<String> getOptionalAdditionalCostsPaid() {
return this.optionalAdditionalCostsPaid;
}
/**
* Checks if is phased out.
*
@@ -6369,17 +6302,16 @@ public class Card extends GameEntity implements Comparable<Card> {
}
} else if (property.startsWith("kicked")) {
if (property.equals("kicked")) {
if (!this.isOptionalAdditionalCostsPaid("Kicker")) {
if (this.getKickerMagnitude() == 0) {
return false;
}
} else {
String s = "Kicker " + property.split("kicked ")[1];
if (!this.isOptionalAdditionalCostsPaid(s)) {
return false;
}
if ("1".equals(s) && !this.isOptionalCostPaid(OptionalCost.Kicker1)) return false;
if ("2".equals(s) && !this.isOptionalCostPaid(OptionalCost.Kicker2)) return false;
}
} else if (property.startsWith("notkicked")) {
if (this.isOptionalAdditionalCostsPaid("Kicker")) {
if (this.getKickerMagnitude() > 0) {
return false;
}
} else if (property.startsWith("evoked")) {
@@ -8317,15 +8249,13 @@ public class Card extends GameEntity implements Comparable<Card> {
public void setSplitStateToPlayAbility(SpellAbility sa) {
if( !isSplitCard() ) return; // just in case
// Split card support
List<SpellAbility> leftSplitAbilities = getState(CardCharacteristicName.LeftSplit).getSpellAbility();
List<SpellAbility> rightSplitAbilities = getState(CardCharacteristicName.RightSplit).getSpellAbility();
for (SpellAbility a : leftSplitAbilities) {
for (SpellAbility a : getState(CardCharacteristicName.LeftSplit).getSpellAbility()) {
if (sa == a || sa.getDescription().equals(String.format("%s (without paying its mana cost)", a.getDescription()))) {
setState(CardCharacteristicName.LeftSplit);
return;
}
}
for (SpellAbility a : rightSplitAbilities) {
for (SpellAbility a : getState(CardCharacteristicName.RightSplit).getSpellAbility()) {
if (sa == a || sa.getDescription().equals(String.format("%s (without paying its mana cost)", a.getDescription()))) {
setState(CardCharacteristicName.RightSplit);
return;
@@ -8337,4 +8267,11 @@ public class Card extends GameEntity implements Comparable<Card> {
throw new RuntimeException("Not found which part to choose for ability " + sa + " from card " + this);
}
// Optional costs paid
private final EnumSet<OptionalCost> costsPaid = EnumSet.noneOf(OptionalCost.class);
public void clearOptionalCostsPaid() { costsPaid.clear(); }
public void addOptionalCostPaid(OptionalCost cost) { costsPaid.add(cost); }
public Iterable<OptionalCost> getOptionalCostsPaid() { return costsPaid; }
public boolean isOptionalCostPaid(OptionalCost cost) { return costsPaid.contains(cost); }
} // end Card class

View File

@@ -46,7 +46,7 @@ public class CardCharacteristics {
private ArrayList<String> intrinsicKeyword = new ArrayList<String>();
private final List<SpellAbility> spellAbility = new ArrayList<SpellAbility>();
private final List<SpellAbility> manaAbility = new ArrayList<SpellAbility>();
private ArrayList<String> intrinsicAbility = new ArrayList<String>();
private List<String> unparsedAbilities = new ArrayList<String>();
private ArrayList<Trigger> triggers = new ArrayList<Trigger>();
private ArrayList<ReplacementEffect> replacementEffects = new ArrayList<ReplacementEffect>();
private ArrayList<StaticAbility> staticAbilities = new ArrayList<StaticAbility>();
@@ -215,18 +215,18 @@ public class CardCharacteristics {
*
* @return the intrinsicAbility
*/
public final ArrayList<String> getIntrinsicAbility() {
return this.intrinsicAbility;
public final List<String> getUnparsedAbilities() {
return this.unparsedAbilities;
}
/**
* Sets the intrinsic ability.
*
* @param intrinsicAbility0
* @param list
* the intrinsicAbility to set
*/
public final void setIntrinsicAbility(final ArrayList<String> intrinsicAbility0) {
this.intrinsicAbility = intrinsicAbility0;
public final void setUnparsedAbilities(final List<String> list) {
this.unparsedAbilities = list;
}
/**
@@ -408,7 +408,7 @@ public class CardCharacteristics {
// ArrayList<String> intrinsicKeyword : list of String objects so use copy constructor
this.intrinsicKeyword = new ArrayList<String>(source.getIntrinsicKeyword());
// ArrayList<String> intrinsicAbility : list of String objects so use copy constructor
this.intrinsicAbility = new ArrayList<String>(source.getIntrinsicAbility());
this.unparsedAbilities = new ArrayList<String>(source.getUnparsedAbilities());
// ArrayList<String> staticAbilityStrings : list of String objects so use copy constructor
this.staticAbilityStrings = new ArrayList<String>(source.getStaticAbilityStrings());
// String imageFilename = copy reference

View File

@@ -121,7 +121,7 @@ public final class AbilityFactory {
return abCost;
}
private static final SpellAbility getAbility(AbilityRecordType type, ApiType api, Map<String, String> mapParams, Cost abCost, Card hostCard) {
public static final SpellAbility getAbility(AbilityRecordType type, ApiType api, Map<String, String> mapParams, Cost abCost, Card hostCard) {
Target abTgt = mapParams.containsKey("ValidTgts") ? readTarget(hostCard, mapParams) : null;
@@ -350,7 +350,7 @@ public final class AbilityFactory {
if(!card.isSplitCard())
throw new IllegalStateException("Fuse ability may be built only on split cards");
final String strLeftAbility = card.getState(CardCharacteristicName.LeftSplit).getIntrinsicAbility().get(0);
final String strLeftAbility = card.getState(CardCharacteristicName.LeftSplit).getUnparsedAbilities().get(0);
Map<String, String> leftMap = getMapParams(strLeftAbility);
AbilityRecordType leftType = AbilityRecordType.getRecordType(leftMap);
ApiType leftApi = leftType.getApiTypeOf(leftMap);
@@ -358,7 +358,7 @@ public final class AbilityFactory {
leftMap.put("SpellDescription", "Fuse (you may cast both halves of this card from your hand).");
leftMap.put("ActivationZone", "Hand");
final String strRightAbility = card.getState(CardCharacteristicName.RightSplit).getIntrinsicAbility().get(0);
final String strRightAbility = card.getState(CardCharacteristicName.RightSplit).getUnparsedAbilities().get(0);
Map<String, String> rightMap = getMapParams(strRightAbility);
AbilityRecordType rightType = AbilityRecordType.getRecordType(leftMap);
ApiType rightApi = leftType.getApiTypeOf(rightMap);

View File

@@ -248,7 +248,7 @@ public class CloneEffect extends SpellAbilityEffect {
final String actualAbility = origSVars.get(s);
// final SpellAbility grantedAbility = newAF.getAbility(actualAbility, tgtCard);
// tgtCard.addSpellAbility(grantedAbility);
tgtCard.getIntrinsicAbilities().add(actualAbility);
tgtCard.getUnparsedAbilities().add(actualAbility);
}
}
}

View File

@@ -227,7 +227,7 @@ public class TokenEffect extends SpellAbilityEffect {
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c);
c.addSpellAbility(grantedAbility);
// added ability to intrinsic list so copies and clones work
c.getIntrinsicAbilities().add(actualAbility);
c.getUnparsedAbilities().add(actualAbility);
}
}
}

View File

@@ -40,6 +40,7 @@ import forge.card.mana.ManaCost;
import forge.card.replacement.ReplacementHandler;
import forge.card.spellability.AbilityActivated;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.OptionalCost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellPermanent;
import forge.card.spellability.Target;
@@ -191,11 +192,10 @@ public class CardFactory {
if (bCopyDetails) {
c.addXManaCostPaid(original.getXManaCostPaid());
c.addMultiKickerMagnitude(original.getMultiKickerMagnitude());
if (original.getOptionalAdditionalCostsPaid() != null) {
for (String cost : original.getOptionalAdditionalCostsPaid()) {
c.addOptionalAdditionalCostsPaid(cost);
}
c.setKickerMagnitude(original.getKickerMagnitude());
for (OptionalCost cost : original.getOptionalCostsPaid()) {
c.addOptionalCostPaid(cost);
}
c.addReplicateMagnitude(original.getReplicateMagnitude());
if (sa.isReplicate()) {
@@ -488,7 +488,7 @@ public class CardFactory {
to.setManaCost(from.getManaCost());
to.setColor(from.getColor());
to.setSVars(from.getSVars());
to.setIntrinsicAbilities(from.getIntrinsicAbilities());
to.setIntrinsicAbilities(from.getUnparsedAbilities());
to.setImageKey(from.getImageKey());
to.setTriggers(from.getTriggers());

View File

@@ -55,6 +55,7 @@ import forge.card.spellability.Ability;
import forge.card.spellability.AbilityActivated;
import forge.card.spellability.AbilityStatic;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.OptionalCost;
import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityRestriction;
@@ -1556,11 +1557,8 @@ public class CardFactoryUtil {
}
}
if (sq[0].startsWith("Kicked")) {
if (c.isOptionalAdditionalCostsPaid("Kicker")) {
return CardFactoryUtil.doXMath(Integer.parseInt(sq[1]), m, c);
} else {
return CardFactoryUtil.doXMath(Integer.parseInt(sq[2]), m, c);
}
int ix = c.getKickerMagnitude() > 0 ? 1 : 2;
return CardFactoryUtil.doXMath(Integer.parseInt(sq[ix]), m, c);
}
if (sq[0].contains("GraveyardWithGE20Cards")) {
@@ -1660,7 +1658,7 @@ public class CardFactoryUtil {
// Count$TimesKicked
if (sq[0].contains("TimesKicked")) {
return CardFactoryUtil.doXMath(c.getMultiKickerMagnitude(), m, c);
return CardFactoryUtil.doXMath(c.getKickerMagnitude(), m, c);
}
if (sq[0].contains("NumCounters")) {
final int num = c.getCounters(CounterType.getType(sq[1]));
@@ -2370,14 +2368,8 @@ public class CardFactoryUtil {
public static final void addAbilityFactoryAbilities(final Card card) {
// **************************************************
// AbilityFactory cards
final ArrayList<String> ia = card.getIntrinsicAbilities();
for (int i = 0; i < ia.size(); i++) {
// System.out.println(cardName);
final SpellAbility sa = AbilityFactory.getAbility(ia.get(i), card);
if (sa.hasParam("SetAsKicked")) {
sa.addOptionalAdditionalCosts("Kicker");
}
card.addSpellAbility(sa);
for (String rawAbility : card.getUnparsedAbilities()) {
card.addSpellAbility(AbilityFactory.getAbility(rawAbility, card));
}
}
@@ -2601,10 +2593,11 @@ public class CardFactoryUtil {
}
// AltCost
if (!card.getSVar("AltCost").equals("")) {
String altCost = card.getSVar("AltCost");
if (StringUtils.isNotBlank(altCost)) {
final SpellAbility sa1 = card.getFirstSpellAbility();
if (sa1 != null && sa1.isSpell()) {
card.addSpellAbility(makeAltCost(card, sa1));
card.addSpellAbility(makeAltCostAbility(card, altCost, sa1));
}
}
@@ -2779,7 +2772,7 @@ public class CardFactoryUtil {
final SpellAbility sa = AbilityFactory.getAbility(abilityStr.toString(), card);
card.addSpellAbility(sa);
// add ability to instrinic strings so copies/clones create the ability also
card.getIntrinsicAbilities().add(abilityStr.toString());
card.getUnparsedAbilities().add(abilityStr.toString());
}
setupEtbKeywords(card);
@@ -3066,63 +3059,26 @@ public class CardFactoryUtil {
* @param abilities
* @return
*/
private static SpellAbility makeAltCost(final Card card, final SpellAbility sa) {
String altCost = card.getSVar("AltCost");
final HashMap<String, String> mapParams = new HashMap<String, String>();
String altCostDescription = "";
final String[] altCosts = altCost.split("\\|");
for (int aCnt = 0; aCnt < altCosts.length; aCnt++) {
altCosts[aCnt] = altCosts[aCnt].trim();
}
for (final String altCost2 : altCosts) {
final String[] aa = altCost2.split("\\$");
for (int aaCnt = 0; aaCnt < aa.length; aaCnt++) {
aa[aaCnt] = aa[aaCnt].trim();
}
if (aa.length != 2) {
final StringBuilder sb = new StringBuilder();
sb.append("StaticEffectFactory Parsing Error: Split length of ");
sb.append(altCost2).append(" in ").append(card.getName()).append(" is not 2.");
throw new RuntimeException(sb.toString());
}
mapParams.put(aa[0], aa[1]);
}
altCost = mapParams.get("Cost");
if (mapParams.containsKey("Description")) {
altCostDescription = mapParams.get("Description");
}
private static SpellAbility makeAltCostAbility(final Card card, final String altCost, final SpellAbility sa) {
final Map<String, String> params = AbilityFactory.getMapParams(altCost);
final SpellAbility altCostSA = sa.copy();
final Cost abCost = new Cost(altCost, altCostSA.isAbility());
final Cost abCost = new Cost(params.get("Cost"), altCostSA.isAbility());
altCostSA.setPayCosts(abCost);
final StringBuilder sb = new StringBuilder();
if (!altCostDescription.equals("")) {
sb.append(altCostDescription);
} else {
sb.append("You may ").append(abCost.toStringAlt());
sb.append(" rather than pay ").append(card.getName()).append("'s mana cost.");
}
altCostSA.setBasicSpell(false);
altCostSA.addOptionalCost(OptionalCost.AltCost);
final SpellAbilityRestriction restriction = new SpellAbilityRestriction();
restriction.setRestrictions(mapParams);
if (!mapParams.containsKey("ActivationZone")) {
restriction.setRestrictions(params);
if (!params.containsKey("ActivationZone")) {
restriction.setZone(ZoneType.Hand);
}
altCostSA.setRestrictions(restriction);
altCostSA.setDescription(sb.toString());
altCostSA.setBasicSpell(false);
altCostSA.setAltCost(true);
final String costDescription = params.containsKey("Description") ? params.get("Description")
: String.format("You may %s rather than pay %s's mana cost.", abCost.toStringAlt(), card.getName());
altCostSA.setDescription(costDescription);
return altCostSA;
}
@@ -3186,14 +3142,7 @@ public class CardFactoryUtil {
* @return a int.
*/
public static final int hasKeyword(final Card c, final String k) {
final List<String> a = c.getKeyword();
for (int i = 0; i < a.size(); i++) {
if (a.get(i).startsWith(k)) {
return i;
}
}
return -1;
return hasKeyword(c, k, 0);
}
/**
@@ -3209,7 +3158,7 @@ public class CardFactoryUtil {
* a int.
* @return a int.
*/
static final int hasKeyword(final Card c, final String k, final int startPos) {
private static final int hasKeyword(final Card c, final String k, final int startPos) {
final List<String> a = c.getKeyword();
for (int i = startPos; i < a.size(); i++) {
if (a.get(i).startsWith(k)) {

View File

@@ -128,7 +128,7 @@ public class CostPartMana extends CostPart {
toPay.increaseShard(ManaCostShard.valueOf(xColor), xCost);
xWasBilled = true;
}
int timesMultikicked = ability.getSourceCard().getMultiKickerMagnitude();
int timesMultikicked = ability.getSourceCard().getKickerMagnitude();
if ( timesMultikicked > 0 && ability.isAnnouncing("Multikicker")) {
ManaCost mkCost = ability.getMultiKickerManaCost();
for(int i = 0; i < timesMultikicked; i++)

View File

@@ -168,7 +168,7 @@ public class HumanPlaySpellAbility {
ability.setSVar(varName, value.toString());
if( "Multikicker".equals(varName) ) {
ability.getSourceCard().setMultiKickerMagnitude(value);
ability.getSourceCard().setKickerMagnitude(value);
} else {
ability.getSourceCard().setSVar(varName, value.toString());
}

View File

@@ -0,0 +1,13 @@
package forge.card.spellability;
/**
* TODO: Write javadoc for this type.
*
*/
public enum OptionalCost {
Conspire,
Buyback,
Kicker1,
Kicker2,
AltCost, // used by prowl
}

View File

@@ -18,6 +18,7 @@
package forge.card.spellability;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -92,7 +93,6 @@ public abstract class SpellAbility implements ISpellAbility {
private final ArrayList<Mana> payingMana = new ArrayList<Mana>();
private final List<SpellAbility> paidAbilities = new ArrayList<SpellAbility>();
private List<String> optionalAdditionalCosts = new ArrayList<String>();
private HashMap<String, List<Card>> paidLists = new HashMap<String, List<Card>>();
@@ -109,7 +109,6 @@ public abstract class SpellAbility implements ISpellAbility {
private boolean undoable;
private boolean isCopied = false;
private boolean isAltCost = false;
public final AbilityManaPart getManaPart() {
return manaPart;
@@ -302,43 +301,6 @@ public abstract class SpellAbility implements ISpellAbility {
public boolean isSpell() { return false; }
public boolean isAbility() { return true; }
/**
* <p>
* isBuyBackAbility.
* </p>
*
* @return a boolean.
*/
public boolean isBuyBackAbility() {
return this.optionalAdditionalCosts.contains("Buyback");
}
/**
* <p>
* isKicked.
* </p>
*
* @return a boolean.
*/
public boolean isKicked() {
return isOptionalAdditionalCostPaid("Kicker");
}
/**
* <p>
* isOptionalAdditionalCostPaid.
* </p>
*
* @return a boolean.
*/
public boolean isOptionalAdditionalCostPaid(String cost) {
for (String s : this.optionalAdditionalCosts) {
if (s.startsWith(cost)) {
return true;
}
}
return false;
}
/**
* <p>
@@ -668,25 +630,35 @@ public abstract class SpellAbility implements ISpellAbility {
this.paidLists = new HashMap<String, List<Card>>();
}
private EnumSet<OptionalCost> optionalCosts = EnumSet.noneOf(OptionalCost.class);
/**
* @return the optionalAdditionalCosts
*/
public List<String> getOptionalAdditionalCosts() {
return optionalAdditionalCosts;
}
/**
* @param costs the optionalAdditionalCosts to set
*/
public final void setOptionalAdditionalCosts(List<String> costs) {
this.optionalAdditionalCosts = costs;
public Iterable<OptionalCost> getOptionalCosts() {
return optionalCosts;
}
/**
* @param cost the optionalAdditionalCost to add
*/
public final void addOptionalAdditionalCosts(String cost) {
this.optionalAdditionalCosts.add(cost);
public final void addOptionalCost(OptionalCost cost) {
// Optional costs are added to swallow copies of original SAs,
// Thus, to protect the original's set from changes, we make a copy right here.
this.optionalCosts = EnumSet.copyOf(optionalCosts);
this.optionalCosts.add(cost);
}
public boolean isBuyBackAbility() {
return isOptionalCostPaid(OptionalCost.Buyback);
}
public boolean isKicked() {
return isOptionalCostPaid(OptionalCost.Kicker1) || isOptionalCostPaid(OptionalCost.Kicker2);
}
public boolean isOptionalCostPaid(OptionalCost cost) {
SpellAbility saRoot = this.getRootAbility();
return saRoot.optionalCosts.contains(cost);
}
/**
@@ -1724,22 +1696,4 @@ public abstract class SpellAbility implements ISpellAbility {
CostPartMana cm = payCosts != null ? getPayCosts().getCostMana() : null;
return cm != null && cm.getAmountOfX() > 0;
}
/**
* @param isAltCost the isAltCost to set
*/
public void setAltCost(boolean isAltCost) {
this.isAltCost = isAltCost;
}
/**
* @return the isAltCost
*/
public boolean isAltCost() {
// only used in prowl, cannot distinguish the alt cost type currently
// TODO : support the altcost type
return isAltCost;
}
}

View File

@@ -78,11 +78,19 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
this.setHellbent(true);
}
if (value.equals("Kicked")) {
this.setKicked(true);
this.kicked = true;
}
if (value.equals("Kicked 1")) {
this.kicked1 = true;
}
if (value.equals("Kicked 2")) {
this.kicked2 = true;
}
if (value.equals("AllTargetsLegal")) {
this.setAllTargetsLegal(true);
}
if (value.equals("AltCost"))
this.altCostPaid = true;
}
if (params.containsKey("ConditionZone")) {
@@ -177,27 +185,15 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
+ " Did not have activator set in SpellAbility_Condition.checkConditions()");
}
if (this.isHellbent()) {
if (!activator.hasHellbent()) {
return false;
}
}
if (this.isThreshold()) {
if (!activator.hasThreshold()) {
return false;
}
}
if (this.isMetalcraft()) {
if (!activator.hasMetalcraft()) {
return false;
}
}
if (this.isKicked()) {
SpellAbility root = sa.getRootAbility();
if (!root.isKicked()) {
return false;
}
}
if (this.isHellbent() && !activator.hasHellbent()) return false;
if (this.isThreshold() && !activator.hasThreshold()) return false;
if (this.isMetalcraft() && !activator.hasMetalcraft()) return false;
if (this.kicked && !sa.isKicked()) return false;
if (this.kicked1 && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) return false;
if (this.kicked2 && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) return false;
if( this.altCostPaid && !sa.isOptionalCostPaid(OptionalCost.AltCost)) return false;
if (this.isAllTargetsLegal()) {
for (Card c : sa.getTarget().getTargetCards()) {
if (!CardFactoryUtil.isTargetStillValid(sa, c)) {
@@ -210,11 +206,11 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
return false;
}
if (this.isPlayerTurn() && !Singletons.getModel().getGame().getPhaseHandler().isPlayerTurn(activator)) {
if (this.isPlayerTurn() && !activator.getGame().getPhaseHandler().isPlayerTurn(activator)) {
return false;
}
if (this.isOpponentTurn() && !Singletons.getModel().getGame().getPhaseHandler().getPlayerTurn().isOpponentOf(activator)) {
if (this.isOpponentTurn() && !activator.getGame().getPhaseHandler().getPlayerTurn().isOpponentOf(activator)) {
return false;
}

View File

@@ -85,7 +85,7 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
if (value.split("Prowl").length > 1) {
prowlTypes.add(value.split("Prowl")[1]);
}
this.setProwl(prowlTypes);
this.setProwlTypes(prowlTypes);
}
}
@@ -352,11 +352,11 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
return false;
}
}
if (this.getProwl() != null && !this.getProwl().isEmpty()) {
if (this.getProwlTypes() != null && !this.getProwlTypes().isEmpty()) {
// only true if the activating player has damaged the opponent with
// one of the specified types
boolean prowlFlag = false;
for (final String type : this.getProwl()) {
for (final String type : this.getProwlTypes()) {
if (activator.hasProwl(type)) {
prowlFlag = true;
}

View File

@@ -70,7 +70,7 @@ public class SpellAbilityVariables {
this.metalcraft = sav.isThreshold();
this.hellbent = sav.isHellbent();
this.allTargetsLegal = sav.isAllTargetsLegal();
this.prowl = new ArrayList<String>(sav.getProwl());
this.prowlTypes = new ArrayList<String>(sav.getProwlTypes());
this.isPresent = sav.getIsPresent();
this.presentCompare = sav.getPresentCompare();
this.presentDefined = sav.getPresentDefined();
@@ -135,13 +135,10 @@ public class SpellAbilityVariables {
/** The hellbent. */
private boolean hellbent = false;
/** The Kicked. */
private boolean kicked = false;
private boolean allTargetsLegal = false;
/** The prowl. */
private ArrayList<String> prowl = new ArrayList<String>();
private ArrayList<String> prowlTypes = new ArrayList<String>();
/** The s is present. */
private String isPresent = null;
@@ -186,6 +183,8 @@ public class SpellAbilityVariables {
/** The chosen colors string. */
private String chosenColors = null;
/**
* <p>
* Setter for the field <code>notAllM12Empires</code>.
@@ -507,20 +506,11 @@ public class SpellAbilityVariables {
this.metalcraft = bMetalcraft;
}
/**
* @return the kicked
*/
public boolean isKicked() {
return kicked;
}
/**
* @param kicked the kicked to set
*/
public void setKicked(boolean kicked) {
this.kicked = kicked;
}
/** The Kicked. */
protected boolean kicked = false;
protected boolean kicked1 = false; // http://magiccards.info/query?q=o%3A%22kicker%22+not+o%3A%22multikicker%22+o%3A%22and%2For+{%22
protected boolean kicked2 = false; // Some spells have 2 kickers with different effects
protected boolean altCostPaid = false;
/**
* @return the allTargetsLegal
@@ -545,8 +535,8 @@ public class SpellAbilityVariables {
* @param types
* the new prowl
*/
public final void setProwl(final ArrayList<String> types) {
this.prowl = types;
public final void setProwlTypes(final ArrayList<String> types) {
this.prowlTypes = types;
}
// IsPresent for Valid battlefield stuff
@@ -760,8 +750,8 @@ public class SpellAbilityVariables {
*
* @return the prowl
*/
public final ArrayList<String> getProwl() {
return this.prowl;
public final ArrayList<String> getProwlTypes() {
return this.prowlTypes;
}
/**

View File

@@ -238,25 +238,13 @@ public class SpellPermanent extends Spell {
continue;
}
} else if (params.get("ValidCard").contains("kicked")) {
if (!params.get("ValidCard").contains("kicked ")) {
if (!sa.isKicked()) {
if (params.get("ValidCard").contains("kicked ")) { // want a specific kicker
String s = params.get("ValidCard").split("kicked ")[1];
if ( "1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
if ( "2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
} else if (!sa.isKicked()) {
continue;
}
} else {
String s = "Kicker " + params.get("ValidCard").split("kicked ")[1];
boolean rightKicker = false;
if (sa.getOptionalAdditionalCosts() != null) {
for (String string : sa.getOptionalAdditionalCosts()) {
if (string.startsWith(s)) {
rightKicker = true;
}
}
}
if (!rightKicker) {
continue;
}
}
}
}
@@ -335,24 +323,13 @@ public class SpellPermanent extends Spell {
continue;
}
} else if (params.get("ValidCard").contains("kicked")) {
if (!params.get("ValidCard").contains("kicked ")) {
if (!sa.isKicked()) {
if (params.get("ValidCard").contains("kicked ")) { // want a specific kicker
String s = params.get("ValidCard").split("kicked ")[1];
if ( "1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
if ( "2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
} else if (!sa.isKicked()) { // otherwise just any must be present
continue;
}
} else {
String s = "Kicker " + params.get("ValidCard").split("kicked ")[1];
boolean rightKicker = false;
if (sa.getOptionalAdditionalCosts() != null) {
for (String string : sa.getOptionalAdditionalCosts()) {
if (string.startsWith(s)) {
rightKicker = true;
}
}
}
if (!rightKicker) {
continue;
}
}
}
}

View File

@@ -497,28 +497,21 @@ public class StaticAbility {
}
}
if (params.containsKey("Condition")) {
if (params.get("Condition").equals("Threshold")) {
if (!controller.hasThreshold()) {
String condition = params.get("Condition");
if (null != condition) {
if (condition.equals("Threshold") && !controller.hasThreshold()) return false;
if (condition.equals("Hellbent") && !controller.hasHellbent()) return false;
if (condition.equals("Metalcraft") && !controller.hasMetalcraft()) return false;
if (condition.equals("PlayerTurn")) {
if (!controller.getGame().getPhaseHandler().isPlayerTurn(controller)) {
return false;
}
} else if (params.get("Condition").equals("Hellbent")) {
if (!controller.hasHellbent()) {
} else if (condition.equals("NotPlayerTurn")) {
if (controller.getGame().getPhaseHandler().isPlayerTurn(controller)) {
return false;
}
} else if (params.get("Condition").equals("Metalcraft")) {
if (!controller.hasMetalcraft()) {
return false;
}
} else if (params.get("Condition").equals("PlayerTurn")) {
if (!Singletons.getModel().getGame().getPhaseHandler().isPlayerTurn(controller)) {
return false;
}
} else if (params.get("Condition").equals("NotPlayerTurn")) {
if (Singletons.getModel().getGame().getPhaseHandler().isPlayerTurn(controller)) {
return false;
}
} else if (params.get("Condition").equals("PermanentOfEachColor")) {
} else if (condition.equals("PermanentOfEachColor")) {
if ((controller.getColoredCardsInPlay(Constant.Color.BLACK).isEmpty()
|| controller.getColoredCardsInPlay(Constant.Color.BLUE).isEmpty()
|| controller.getColoredCardsInPlay(Constant.Color.GREEN).isEmpty()
@@ -526,7 +519,7 @@ public class StaticAbility {
|| controller.getColoredCardsInPlay(Constant.Color.WHITE).isEmpty())) {
return false;
}
} else if (params.get("Condition").equals("FatefulHour")) {
} else if (condition.equals("FatefulHour")) {
if (controller.getLife() > 5) {
return false;
}

View File

@@ -26,6 +26,7 @@ import forge.Card;
import forge.Singletons;
import forge.card.TriggerReplacementBase;
import forge.card.spellability.Ability;
import forge.card.spellability.OptionalCost;
import forge.card.spellability.SpellAbility;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
@@ -315,8 +316,7 @@ public abstract class Trigger extends TriggerReplacementBase {
if ( !meetsCommonRequirements(getMapParams()))
return false;
if (this.getMapParams().containsKey("EvolveCondition")) {
if (this.getMapParams().get("EvolveCondition").equals("True")) {
if ("True".equals(getMapParams().get("EvolveCondition"))) {
final Card moved = (Card) runParams2.get("Card");
if (moved == null) {
return false;
@@ -330,8 +330,15 @@ public abstract class Trigger extends TriggerReplacementBase {
return false;
}
}
String condition = getMapParams().get("Condition");
if( "AltCost".equals(condition) ) {
final Card moved = (Card) runParams2.get("Card");
if( null != moved && !moved.isOptionalCostPaid(OptionalCost.AltCost))
return false;
}
return true;
}

View File

@@ -20,6 +20,7 @@ package forge.card.trigger;
import forge.Card;
import forge.Singletons;
import forge.card.cost.Cost;
import forge.card.spellability.OptionalCost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.game.player.Player;
@@ -76,7 +77,7 @@ public class TriggerSpellAbilityCast extends Trigger {
}
if (this.getMapParams().containsKey("AltCostSpellAbility")) {
if (!spellAbility.isAltCost()) {
if (!spellAbility.isOptionalCostPaid(OptionalCost.AltCost)) {
return false;
}
}
@@ -157,7 +158,7 @@ public class TriggerSpellAbilityCast extends Trigger {
}
if (this.getMapParams().containsKey("Conspire")) {
if (!spellAbility.isOptionalAdditionalCostPaid("Conspire")) {
if (!spellAbility.isOptionalCostPaid(OptionalCost.Conspire)) {
return false;
}
}

View File

@@ -291,7 +291,7 @@ public class GameAction {
}
} else if (zoneFrom.is(ZoneType.Exile) && !zoneTo.is(ZoneType.Battlefield)) {
// Pull from Eternity used on a suspended card
copied.clearAdditionalCostsPaid();
copied.clearOptionalCostsPaid();
if (copied.isFaceDown()) {
copied.turnFaceUp();
}
@@ -315,7 +315,7 @@ public class GameAction {
copied.removeHiddenExtrinsicKeyword(s);
}
}
copied.clearAdditionalCostsPaid();
copied.clearOptionalCostsPaid();
if (copied.isFaceDown()) {
copied.turnFaceUp();
}

View File

@@ -20,6 +20,8 @@ package forge.game;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Iterables;
@@ -36,6 +38,7 @@ import forge.CounterType;
import forge.FThreads;
import forge.Singletons;
import forge.card.ability.AbilityFactory;
import forge.card.ability.AbilityFactory.AbilityRecordType;
import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType;
import forge.card.cardfactory.CardFactoryUtil;
@@ -57,6 +60,7 @@ import forge.card.mana.ManaCost;
import forge.card.spellability.Ability;
import forge.card.spellability.AbilityManaPart;
import forge.card.spellability.AbilitySub;
import forge.card.spellability.OptionalCost;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityRestriction;
import forge.control.input.InputPayManaExecuteCommands;
@@ -72,6 +76,7 @@ import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.gui.GuiDialog;
import forge.sound.SoundEffectType;
import forge.util.TextUtil;
/**
@@ -1209,6 +1214,115 @@ public final class GameActionUtil {
return alternatives;
}
/**
* get optional additional costs.
*
* @param original
* the original sa
* @return an ArrayList<SpellAbility>.
*/
public static List<SpellAbility> getOptionalCosts(final SpellAbility original) {
final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
final Card source = original.getSourceCard();
abilities.add(original);
if (!original.isSpell()) {
return abilities;
}
// Buyback, Kicker
for (String keyword : source.getKeyword()) {
if (keyword.startsWith("AlternateAdditionalCost")) {
final List<SpellAbility> newAbilities = new ArrayList<SpellAbility>();
String[] costs = TextUtil.split(keyword, ':');
for (SpellAbility sa : abilities) {
final SpellAbility newSA = sa.copy();
newSA.setBasicSpell(false);
final Cost cost1 = new Cost(costs[1], false);
newSA.setDescription(sa.getDescription() + " (Additional cost " + cost1.toSimpleString() + ")");
newSA.setPayCosts(cost1.add(sa.getPayCosts()));
if (newSA.canPlay()) {
newAbilities.add(newSA);
}
//second option
final SpellAbility newSA2 = sa.copy();
newSA2.setBasicSpell(false);
final Cost cost2 = new Cost(costs[2], false);
newSA2.setDescription(sa.getDescription() + " (Additional cost " + cost2.toSimpleString() + ")");
newSA2.setPayCosts(cost2.add(sa.getPayCosts()));
if (newSA2.canPlay()) {
newAbilities.add(newAbilities.size(), newSA2);
}
}
abilities.clear();
abilities.addAll(newAbilities);
} else if (keyword.startsWith("Buyback")) {
for (int i = 0; i < abilities.size(); i++) {
final SpellAbility newSA = abilities.get(i).copy();
newSA.setBasicSpell(false);
newSA.setPayCosts(new Cost(keyword.substring(8), false).add(newSA.getPayCosts()));
newSA.setDescription(newSA.getDescription() + " (with Buyback)");
newSA.addOptionalCost(OptionalCost.Buyback);
if ( newSA.canPlay() )
abilities.add(++i, newSA);
}
} else if (keyword.startsWith("Kicker")) {
for (int i = 0; i < abilities.size(); i++) {
String[] sCosts = TextUtil.split(keyword.substring(7), ':');
int iUnKicked = i;
for(int j = 0; j < sCosts.length; j++) {
final SpellAbility newSA = abilities.get(iUnKicked).copy();
newSA.setBasicSpell(false);
final Cost cost = new Cost(sCosts[j], false);
newSA.setDescription(newSA.getDescription() + " (Kicker " + cost.toSimpleString() + ")");
newSA.setPayCosts(cost.add(newSA.getPayCosts()));
newSA.addOptionalCost(j == 0 ? OptionalCost.Kicker1 : OptionalCost.Kicker2);
if ( newSA.canPlay() )
abilities.add(++i, newSA);
}
if(sCosts.length == 2) { // case for both kickers - it's hardcoded since they never have more that 2 kickers
final SpellAbility newSA = abilities.get(iUnKicked).copy();
newSA.setBasicSpell(false);
final Cost cost1 = new Cost(sCosts[0], false);
final Cost cost2 = new Cost(sCosts[1], false);
newSA.setDescription(newSA.getDescription() + String.format(" (Both kickers: %s and %s)", cost1.toSimpleString(), cost2.toSimpleString()));
newSA.setPayCosts(cost2.add(cost1.add(newSA.getPayCosts())));
newSA.addOptionalCost(OptionalCost.Kicker1);
newSA.addOptionalCost(OptionalCost.Kicker2);
if ( newSA.canPlay() )
abilities.add(++i, newSA);
}
}
} else if (keyword.startsWith("Conspire")) {
for (int i = 0; i < abilities.size(); i++) {
final SpellAbility newSA = abilities.get(i).copy();
newSA.setBasicSpell(false);
final String conspireCost = "tapXType<2/Creature.SharesColorWith/untapped creature you control that shares a color with " + source.getName() + ">";
newSA.setPayCosts(new Cost(conspireCost, false).add(newSA.getPayCosts()));
newSA.setDescription(newSA.getDescription() + " (Conspire)");
newSA.addOptionalCost(OptionalCost.Conspire);
if ( newSA.canPlay() )
abilities.add(++i, newSA);
}
}
}
// Splice
final List<SpellAbility> newAbilities = new ArrayList<SpellAbility>();
for (SpellAbility sa : abilities) {
if( sa.isSpell() && sa.getSourceCard().isType("Arcane") && sa.getApi() != null ) {
newAbilities.addAll(GameActionUtil.getSpliceAbilities(sa));
}
}
abilities.addAll(newAbilities);
return abilities;
}
/**
* <p>
* getSpliceAbilities.
@@ -1219,35 +1333,41 @@ public final class GameActionUtil {
* @return an ArrayList<SpellAbility>.
* get abilities with all Splice options
*/
public static final ArrayList<SpellAbility> getSpliceAbilities(SpellAbility sa) {
private static final ArrayList<SpellAbility> getSpliceAbilities(SpellAbility sa) {
ArrayList<SpellAbility> newSAs = new ArrayList<SpellAbility>();
ArrayList<SpellAbility> allSAs = new ArrayList<SpellAbility>();
allSAs.add(sa);
ArrayList<SpellAbility> allSaCombinations = new ArrayList<SpellAbility>();
allSaCombinations.add(sa);
Card source = sa.getSourceCard();
if (!sa.isSpell() || !source.isType("Arcane") || sa.getApi() == null) {
return newSAs;
}
for (Card c : sa.getActivatingPlayer().getCardsIn(ZoneType.Hand)) {
if (c.equals(source)) {
continue;
}
String spliceKwCost = null;
for (String keyword : c.getKeyword()) {
if (!keyword.startsWith("Splice")) {
continue;
if (keyword.startsWith("Splice")) {
spliceKwCost = keyword.substring(19);
break;
}
String newSubSAString = c.getCharacteristics().getIntrinsicAbility().get(0);
newSubSAString = newSubSAString.replace("SP", "DB");
final AbilitySub newSubSA = (AbilitySub) AbilityFactory.getAbility(newSubSAString, c);
ArrayList<SpellAbility> addSAs = new ArrayList<SpellAbility>();
}
if( spliceKwCost == null )
continue;
Map<String, String> params = AbilityFactory.getMapParams(c.getCharacteristics().getUnparsedAbilities().get(0));
AbilityRecordType rc = AbilityRecordType.getRecordType(params);
ApiType api = rc.getApiTypeOf(params);
AbilitySub subAbility = (AbilitySub) AbilityFactory.getAbility(AbilityRecordType.SubAbility, api, params, null, c);
// Add the subability to all existing variants
for (SpellAbility s : allSAs) {
for (int i = 0; i < allSaCombinations.size(); ++i) {
//create a new spell copy
final SpellAbility newSA = s.copy();
final SpellAbility newSA = allSaCombinations.get(i).copy();
newSA.setBasicSpell(false);
newSA.setPayCosts(new Cost(keyword.substring(19), false).add(newSA.getPayCosts()));
newSA.setDescription(s.getDescription() + " (Splicing " + c + " onto it)");
newSA.setPayCosts(new Cost(spliceKwCost, false).add(newSA.getPayCosts()));
newSA.setDescription(newSA.getDescription() + " (Splicing " + c + " onto it)");
newSA.addSplicedCards(c);
// copy all subAbilities
@@ -1255,143 +1375,28 @@ public final class GameActionUtil {
while (child.getSubAbility() != null) {
AbilitySub newChild = child.getSubAbility().getCopy();
child.setSubAbility(newChild);
child.setActivatingPlayer(s.getActivatingPlayer());
child.setActivatingPlayer(newSA.getActivatingPlayer());
child = newChild;
}
//add the spliced ability to the end of the chain
child.setSubAbility(newSubSA);
child.setSubAbility(subAbility);
//set correct source and activating player to all the spliced abilities
child = newSubSA;
child = subAbility;
while (child != null) {
child.setSourceCard(source);
child.setActivatingPlayer(s.getActivatingPlayer());
child.setActivatingPlayer(newSA.getActivatingPlayer());
child = child.getSubAbility();
}
newSAs.add(0, newSA);
addSAs.add(newSA);
}
allSAs.addAll(addSAs);
break;
newSAs.add(newSA);
allSaCombinations.add(++i, newSA);
}
}
return newSAs;
}
/**
* get optional additional costs.
*
* @param original
* the original sa
* @return an ArrayList<SpellAbility>.
*/
public static List<SpellAbility> getOptionalAdditionalCosts(final SpellAbility original) {
final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
final List<SpellAbility> newAbilities = new ArrayList<SpellAbility>();
final Card source = original.getSourceCard();
abilities.add(original);
if (!original.isSpell()) {
return abilities;
}
// Buyback, Kicker
for (String keyword : source.getKeyword()) {
if (keyword.startsWith("Buyback")) {
for (SpellAbility sa : abilities) {
final SpellAbility newSA = sa.copy();
newSA.setBasicSpell(false);
newSA.setPayCosts(new Cost(keyword.substring(8), false).add(newSA.getPayCosts()));
newSA.setDescription(sa.getDescription() + " (with Buyback)");
ArrayList<String> newoacs = new ArrayList<String>(sa.getOptionalAdditionalCosts());
newoacs.add(keyword);
newSA.setOptionalAdditionalCosts(newoacs);
if (newSA.canPlay()) {
newAbilities.add(newSA);
}
}
abilities.addAll(0, newAbilities);
newAbilities.clear();
} else if (keyword.startsWith("Kicker")) {
for (SpellAbility sa : abilities) {
final SpellAbility newSA = sa.copy();
newSA.setBasicSpell(false);
final Cost cost = new Cost(keyword.substring(7), false);
newSA.setDescription(sa.getDescription() + " (Kicker " + cost.toSimpleString() + ")");
newSA.setPayCosts(cost.add(newSA.getPayCosts()));
ArrayList<String> newoacs = new ArrayList<String>(sa.getOptionalAdditionalCosts());
newoacs.add(keyword);
newSA.setOptionalAdditionalCosts(newoacs);
if (newSA.canPlay()) {
newAbilities.add(newSA);
}
}
abilities.addAll(0, newAbilities);
newAbilities.clear();
} else if (keyword.startsWith("AlternateAdditionalCost")) {
String costString1 = keyword.split(":")[1];
String costString2 = keyword.split(":")[2];
for (SpellAbility sa : abilities) {
final SpellAbility newSA = sa.copy();
newSA.setBasicSpell(false);
final Cost cost1 = new Cost(costString1, false);
newSA.setDescription(sa.getDescription() + " (Additional cost " + cost1.toSimpleString() + ")");
newSA.setPayCosts(cost1.add(sa.getPayCosts()));
newSA.setOptionalAdditionalCosts(new ArrayList<String>(sa.getOptionalAdditionalCosts()));
if (newSA.canPlay()) {
newAbilities.add(newSA);
}
//second option
final SpellAbility newSA2 = sa.copy();
newSA2.setBasicSpell(false);
final Cost cost2 = new Cost(costString2, false);
newSA2.setDescription(sa.getDescription() + " (Additional cost " + cost2.toSimpleString() + ")");
newSA2.setPayCosts(cost2.add(sa.getPayCosts()));
newSA2.setOptionalAdditionalCosts(new ArrayList<String>(sa.getOptionalAdditionalCosts()));
if (newSA2.canPlay()) {
newAbilities.add(newAbilities.size(), newSA2);
}
}
abilities.clear();
abilities.addAll(0, newAbilities);
newAbilities.clear();
} else if (keyword.startsWith("Conspire")) {
for (SpellAbility sa : abilities) {
final SpellAbility newSA = sa.copy();
newSA.setBasicSpell(false);
final String conspireCost = "tapXType<2/Creature.SharesColorWith/untapped creature you control"
+ " that shares a color with " + source.getName() + ">";
newSA.setPayCosts(new Cost(conspireCost, false).add(newSA.getPayCosts()));
newSA.setDescription(sa.getDescription() + " (Conspire)");
ArrayList<String> newoacs = new ArrayList<String>(sa.getOptionalAdditionalCosts());
newoacs.add(keyword);
newSA.setOptionalAdditionalCosts(newoacs);
if (newSA.canPlay()) {
newAbilities.add(newAbilities.size(), newSA);
}
}
abilities.addAll(0, newAbilities);
newAbilities.clear();
}
}
// Splice
for (SpellAbility sa : abilities) {
newAbilities.addAll(GameActionUtil.getSpliceAbilities(sa));
}
abilities.addAll(newAbilities);
newAbilities.clear();
return abilities;
}
/**
* <p>
* hasUrzaLands.

View File

@@ -173,7 +173,7 @@ public class AiController {
final List<SpellAbility> result = new ArrayList<SpellAbility>();
for (SpellAbility sa : newAbilities) {
sa.setActivatingPlayer(player);
result.addAll(GameActionUtil.getOptionalAdditionalCosts(sa));
result.addAll(GameActionUtil.getOptionalCosts(sa));
}
result.addAll(newAbilities);
return result;

View File

@@ -217,7 +217,7 @@ public class HumanPlayer extends Player {
*/
public SpellAbility chooseOptionalAdditionalCosts(final SpellAbility original) {
//final HashMap<String, SpellAbility> map = new HashMap<String, SpellAbility>();
final List<SpellAbility> abilities = GameActionUtil.getOptionalAdditionalCosts(original);
final List<SpellAbility> abilities = GameActionUtil.getOptionalCosts(original);
if (!original.isSpell()) {
return original;

View File

@@ -74,7 +74,7 @@ public class PlayerControllerHuman extends PlayerController {
* Uses GUI to learn which spell the player (human in our case) would like to play
*/
public SpellAbility getAbilityToPlay(List<SpellAbility> abilities) {
if (abilities.size() == 0) {
if (abilities.isEmpty()) {
return null;
} else if (abilities.size() == 1) {
return abilities.get(0);

View File

@@ -36,6 +36,7 @@ import forge.card.mana.ManaCost;
import forge.card.spellability.Ability;
import forge.card.spellability.AbilityStatic;
import forge.card.spellability.AbilityTriggered;
import forge.card.spellability.OptionalCost;
import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
@@ -369,10 +370,9 @@ public class MagicStack extends MyObservable {
game.getAction().checkStateEffects();
//GuiDisplayUtil.updateGUI();
} else {
if (sp.getOptionalAdditionalCosts() != null) {
for (String s : sp.getOptionalAdditionalCosts()) {
sp.getSourceCard().addOptionalAdditionalCostsPaid(s);
}
for (OptionalCost s : sp.getOptionalCosts()) {
sp.getSourceCard().addOptionalCostPaid(s);
}
if (sp.getSourceCard().isCopiedSpell()) {
this.push(sp);
@@ -384,7 +384,7 @@ public class MagicStack extends MyObservable {
if (activating.isHuman()) {
while(true) {
int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude();
int mkMagnitude = sa.getSourceCard().getKickerMagnitude();
String prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", sa.getSourceCard(), mkMagnitude );
InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(activating, prompt, sp.getMultiKickerManaCost());
FThreads.setInputAndWait(toSet);