mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
AI & Human mana payment, ManaPool, ManaCostBeingPaid refactored.
The game uses a byte with flags set instead of color names in more cases.
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
package forge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -192,7 +193,12 @@ public final class CardUtil {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static Set<String> getReflectableManaColors(final SpellAbility abMana, final SpellAbility sa,
|
||||
// a nice entry point with minimum parameters
|
||||
public static Set<String> getReflectableManaColors(final SpellAbility sa) {
|
||||
return getReflectableManaColors(sa, sa, new HashSet<String>(), new ArrayList<Card>());
|
||||
}
|
||||
|
||||
private static Set<String> getReflectableManaColors(final SpellAbility abMana, final SpellAbility sa,
|
||||
Set<String> colors, final List<Card> parents) {
|
||||
// Here's the problem with reflectable Mana. If more than one is out,
|
||||
// they need to Reflect each other,
|
||||
@@ -266,14 +272,14 @@ public final class CardUtil {
|
||||
colors.add(Constant.Color.COLORLESS);
|
||||
}
|
||||
} else if (reflectProperty.equals("Produce")) {
|
||||
final ArrayList<SpellAbility> abilities = new ArrayList<SpellAbility>();
|
||||
final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
|
||||
for (final Card c : cards) {
|
||||
abilities.addAll(c.getManaAbility());
|
||||
}
|
||||
// currently reflected mana will ignore other reflected mana
|
||||
// abilities
|
||||
|
||||
final ArrayList<SpellAbility> reflectAbilities = new ArrayList<SpellAbility>();
|
||||
final List<SpellAbility> reflectAbilities = new ArrayList<SpellAbility>();
|
||||
|
||||
for (final SpellAbility ab : abilities) {
|
||||
if (maxChoices == colors.size()) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import forge.card.cost.Cost;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.game.GameState;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.ai.ComputerUtilMana;
|
||||
import forge.game.player.Player;
|
||||
@@ -63,7 +62,7 @@ public class CounterAi extends SpellAbilityAi {
|
||||
|
||||
if (unlessCost != null && !unlessCost.endsWith(">")) {
|
||||
// Is this Usable Mana Sources? Or Total Available Mana?
|
||||
final int usableManaSources = ComputerUtilCard.getUsableManaSources(ai.getOpponent());
|
||||
final int usableManaSources = ComputerUtilMana.getAvailableMana(ai.getOpponent(), true).size();
|
||||
int toPay = 0;
|
||||
boolean setPayX = false;
|
||||
if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) {
|
||||
@@ -132,7 +131,7 @@ public class CounterAi extends SpellAbilityAi {
|
||||
final Card source = sa.getSourceCard();
|
||||
if (unlessCost != null) {
|
||||
// Is this Usable Mana Sources? Or Total Available Mana?
|
||||
final int usableManaSources = ComputerUtilCard.getUsableManaSources(ai.getOpponent());
|
||||
final int usableManaSources = ComputerUtilMana.getAvailableMana(ai.getOpponent(), true).size();
|
||||
int toPay = 0;
|
||||
boolean setPayX = false;
|
||||
if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) {
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package forge.card.ability.effects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardUtil;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.ability.AbilityUtils;
|
||||
@@ -27,7 +24,7 @@ public class ManaReflectedEffect extends SpellAbilityEffect {
|
||||
AbilityManaPart ma = sa.getManaPart();
|
||||
sa.setUndoable(sa.isAbility() && sa.isUndoable());
|
||||
|
||||
final Collection<String> colors = CardUtil.getReflectableManaColors(sa, sa, new HashSet<String>(), new ArrayList<Card>());
|
||||
final Collection<String> colors = CardUtil.getReflectableManaColors(sa);
|
||||
|
||||
final List<Player> tgtPlayers = getTargetPlayers(sa);
|
||||
for (final Player player : tgtPlayers) {
|
||||
|
||||
@@ -1133,7 +1133,7 @@ public class CardFactoryUtil {
|
||||
if (color.equals("All")) {
|
||||
return cc.getManaPool().totalMana();
|
||||
} else {
|
||||
return cc.getManaPool().getAmountOfColor(color);
|
||||
return cc.getManaPool().getAmountOfColor(MagicColor.fromName(color));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public class CostDamage extends CostPart {
|
||||
*/
|
||||
@Override
|
||||
public final void payAI(final PaymentDecision decision, final Player ai, SpellAbility ability, Card source) {
|
||||
ability.getActivatingPlayer().addDamage(decision.c, source);
|
||||
ai.addDamage(decision.c, source);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import forge.Card;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.GameState;
|
||||
import forge.game.player.Player;
|
||||
@@ -178,10 +177,6 @@ public class CostPayment {
|
||||
final Card source = this.ability.getSourceCard();
|
||||
final List<CostPart> parts = this.cost.getCostParts();
|
||||
|
||||
if (this.getCost().getCostMana() == null) {
|
||||
parts.add(new CostPartMana(ManaCost.ZERO, null, false));
|
||||
}
|
||||
|
||||
Map<Class<? extends CostPart>, PaymentDecision> decisions = new HashMap<Class<? extends CostPart>, PaymentDecision>();
|
||||
|
||||
// Set all of the decisions before attempting to pay anything
|
||||
|
||||
@@ -31,43 +31,19 @@ import forge.card.spellability.AbilityManaPart;
|
||||
* @version $Id$
|
||||
*/
|
||||
public class Mana {
|
||||
private byte color;
|
||||
private Card sourceCard = null;
|
||||
private AbilityManaPart manaAbility = null;
|
||||
private boolean hasRestrictions = false;
|
||||
private boolean pumpCounterMagic = false;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for Mana.
|
||||
* </p>
|
||||
*
|
||||
* @param col
|
||||
* a {@link java.lang.String} object.
|
||||
* @param source
|
||||
* a {@link forge.Card} object.
|
||||
* @param manaAbility
|
||||
* a {@link forge.card.spellability.AbilityMana} object
|
||||
*/
|
||||
public Mana(final String col, final Card source, final AbilityManaPart manaAbility) {
|
||||
this.color = MagicColor.fromName(col);
|
||||
if (manaAbility != null) {
|
||||
this.manaAbility = manaAbility;
|
||||
if (!manaAbility.getManaRestrictions().isEmpty()) {
|
||||
this.hasRestrictions = true;
|
||||
}
|
||||
if (manaAbility.cannotCounterPaidWith()) {
|
||||
this.pumpCounterMagic = true;
|
||||
}
|
||||
}
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.sourceCard = source;
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + color;
|
||||
result = prime * result + (hasRestrictions ? 1231 : 1237);
|
||||
result = prime * result + ((manaAbility == null) ? 0 : manaAbility.hashCode());
|
||||
result = prime * result + (pumpCounterMagic ? 1231 : 1237);
|
||||
result = prime * result + ((sourceCard == null) ? 0 : sourceCard.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Mana)) {
|
||||
@@ -88,6 +64,45 @@ public class Mana {
|
||||
return mp == mp2 || mp.getManaRestrictions().equals(mp2.getManaRestrictions());
|
||||
}
|
||||
|
||||
|
||||
private byte color;
|
||||
private Card sourceCard = null;
|
||||
private AbilityManaPart manaAbility = null;
|
||||
private boolean hasRestrictions = false;
|
||||
private boolean pumpCounterMagic = false;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for Mana.
|
||||
* </p>
|
||||
*
|
||||
* @param col
|
||||
* a {@link java.lang.String} object.
|
||||
* @param source
|
||||
* a {@link forge.Card} object.
|
||||
* @param manaAbility
|
||||
* a {@link forge.card.spellability.AbilityMana} object
|
||||
*/
|
||||
public Mana(final byte color, final Card source, final AbilityManaPart manaAbility) {
|
||||
this.color = color;
|
||||
if (manaAbility != null) {
|
||||
this.manaAbility = manaAbility;
|
||||
if (!manaAbility.getManaRestrictions().isEmpty()) {
|
||||
this.hasRestrictions = true;
|
||||
}
|
||||
if (manaAbility.cannotCounterPaidWith()) {
|
||||
this.pumpCounterMagic = true;
|
||||
}
|
||||
}
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.sourceCard = source;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* toString.
|
||||
|
||||
@@ -266,7 +266,7 @@ public final class ManaCost implements Comparable<ManaCost> {
|
||||
|
||||
public boolean canBePaidWithAvaliable(ColorSet color) {
|
||||
for (ManaCostShard shard : shards) {
|
||||
if (!shard.canBePaidWithAvaliable(color)) {
|
||||
if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(color.getColor())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,20 @@
|
||||
package forge.card.mana;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.CardPredicates;
|
||||
import forge.CardUtil;
|
||||
import forge.Constant;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
@@ -38,6 +40,9 @@ import forge.game.GameState;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.maps.EnumMapToAmount;
|
||||
import forge.util.maps.MapToAmount;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -48,7 +53,7 @@ import forge.gui.GuiChoose;
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ManaCostBeingPaid {
|
||||
private class ManaCostBeingPaidIterator implements IParserManaCost {
|
||||
private class ManaCostBeingPaidIterator implements IParserManaCost, Iterator<ManaCostShard> {
|
||||
private Iterator<ManaCostShard> mch;
|
||||
private ManaCostShard nextShard = null;
|
||||
private int remainingShards = 0;
|
||||
@@ -101,7 +106,7 @@ public class ManaCostBeingPaid {
|
||||
|
||||
// holds Mana_Part objects
|
||||
// ManaPartColor is stored before ManaPartColorless
|
||||
private final HashMap<ManaCostShard, Integer> unpaidShards = new HashMap<ManaCostShard, Integer>();
|
||||
private final MapToAmount<ManaCostShard> unpaidShards = new EnumMapToAmount<ManaCostShard>(ManaCostShard.class);
|
||||
private byte sunburstMap = 0;
|
||||
private int cntX = 0;
|
||||
private final String sourceRestriction;
|
||||
@@ -132,7 +137,7 @@ public class ManaCostBeingPaid {
|
||||
if (shard == ManaCostShard.X) {
|
||||
cntX++;
|
||||
} else {
|
||||
increaseShard(shard, 1);
|
||||
unpaidShards.add(shard);
|
||||
}
|
||||
}
|
||||
increaseColorlessMana(manaCost.getGenericCost());
|
||||
@@ -149,45 +154,12 @@ public class ManaCostBeingPaid {
|
||||
return ColorSet.fromMask(sunburstMap).countColors();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getColorsPaid.
|
||||
* </p>
|
||||
*
|
||||
* @return a String.
|
||||
*/
|
||||
|
||||
public final byte getColorsPaid() {
|
||||
return sunburstMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getUnpaidPhyrexianMana.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link java.util.ArrayList} object.
|
||||
*/
|
||||
private List<ManaCostShard> getUnpaidPhyrexianMana() {
|
||||
ArrayList<ManaCostShard> res = new ArrayList<ManaCostShard>();
|
||||
for (final Entry<ManaCostShard, Integer> part : this.unpaidShards.entrySet()) {
|
||||
|
||||
if (!part.getKey().isPhyrexian()) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < part.getValue(); i++) {
|
||||
res.add(part.getKey());
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* containsPhyrexianMana.
|
||||
* </p>
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean containsPhyrexianMana() {
|
||||
for (ManaCostShard shard : unpaidShards.keySet()) {
|
||||
if (shard.isPhyrexian()) {
|
||||
@@ -197,92 +169,50 @@ public class ManaCostBeingPaid {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* payPhyrexian.
|
||||
* </p>
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean payPhyrexian() {
|
||||
final List<ManaCostShard> phy = this.getUnpaidPhyrexianMana();
|
||||
|
||||
if (phy.size() > 0) {
|
||||
Integer cnt = unpaidShards.get(phy.get(0));
|
||||
if (cnt <= 1) {
|
||||
unpaidShards.remove(phy.get(0));
|
||||
} else {
|
||||
unpaidShards.put(phy.get(0), Integer.valueOf(cnt - 1));
|
||||
ManaCostShard phy = null;
|
||||
for(ManaCostShard mcs : unpaidShards.keySet()) {
|
||||
if( mcs.isPhyrexian() ) {
|
||||
phy = mcs;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (phy == null )
|
||||
return false;
|
||||
|
||||
decreaseShard(phy, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// takes a Short Color and returns true if it exists in the mana cost.
|
||||
// Easier for split costs
|
||||
/**
|
||||
* <p>
|
||||
* isColor.
|
||||
* </p>
|
||||
*
|
||||
* @param color
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isColor(final String color) {
|
||||
//if ( "1".equals(color) ) return getColorlessManaAmount() > 0;
|
||||
if (color.matches("^\\d+$")) {
|
||||
return getColorlessManaAmount() > 0;
|
||||
}
|
||||
|
||||
public final boolean needsColor(final byte colorMask) {
|
||||
for (ManaCostShard shard : unpaidShards.keySet()) {
|
||||
if (shard == ManaCostShard.COLORLESS)
|
||||
continue;
|
||||
if (shard.isOr2Colorless()) {
|
||||
if ((shard.getColorMask() & colorMask) != 0 )
|
||||
return true;
|
||||
} else if (shard.canBePaidWithManaOfColor(colorMask))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String ss = shard.toString();
|
||||
if (ss.contains(color)) {
|
||||
// isNeeded(String) still used by the Computer, might have problems activating Snow abilities
|
||||
public final boolean isAnyPartPayableWith(byte colorMask) {
|
||||
for (ManaCostShard shard : unpaidShards.keySet()) {
|
||||
if (shard.canBePaidWithManaOfColor(colorMask)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// isNeeded(String) still used by the Computer, might have problems
|
||||
// activating Snow abilities
|
||||
/**
|
||||
* <p>
|
||||
* isNeeded.
|
||||
* </p>
|
||||
*
|
||||
* @param mana
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isNeeded(String mana) {
|
||||
if (mana.length() > 1) {
|
||||
mana = MagicColor.toShortString(mana);
|
||||
}
|
||||
for (ManaCostShard shard : unpaidShards.keySet()) {
|
||||
if (canBePaidWith(shard, mana)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isNeeded.
|
||||
* </p>
|
||||
*
|
||||
* @param paid
|
||||
* a {@link forge.card.mana.Mana} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isNeeded(final Mana paid) {
|
||||
for (ManaCostShard shard : unpaidShards.keySet()) {
|
||||
|
||||
if (canBePaidWith(shard, paid)) {
|
||||
return true;
|
||||
}
|
||||
@@ -290,30 +220,11 @@ public class ManaCostBeingPaid {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isPaid.
|
||||
* </p>
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
|
||||
public final boolean isPaid() {
|
||||
return unpaidShards.isEmpty();
|
||||
} // isPaid()
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* payMana.
|
||||
* </p>
|
||||
*
|
||||
* @param mana
|
||||
* a {@link forge.card.mana.Mana} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean payMana(final Mana mana) {
|
||||
return this.addMana(mana);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* payMultipleMana.
|
||||
@@ -323,65 +234,35 @@ public class ManaCostBeingPaid {
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final void payMultipleMana(String mana) {
|
||||
String[] manas = mana.split(" ");
|
||||
for (String manaPart : manas) {
|
||||
if (manaPart.matches("[0-9]+")) {
|
||||
final int amount = Integer.parseInt(manaPart);
|
||||
for (int i = 0; i < amount; i++) {
|
||||
this.payMana(Constant.Color.COLORLESS);
|
||||
public final String payMultipleMana(String mana) {
|
||||
List<String> unused = new ArrayList<>(4);
|
||||
for (String manaPart : TextUtil.split(mana, ' ')) {
|
||||
if (StringUtils.isNumeric(manaPart)) {
|
||||
for(int i = Integer.parseInt(manaPart); i > 0; i--) {
|
||||
boolean wasNeeded = this.payMana("1");
|
||||
if(!wasNeeded) {
|
||||
unused.add(Integer.toString(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.payMana(forge.card.MagicColor.toLongString(manaPart));
|
||||
String color = MagicColor.toShortString(manaPart);
|
||||
boolean wasNeeded = this.payMana(color);
|
||||
if(!wasNeeded)
|
||||
unused.add(color);
|
||||
}
|
||||
}
|
||||
return unused.isEmpty() ? null : StringUtils.join(unused, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* payMana.
|
||||
* </p>
|
||||
*
|
||||
* @param color
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean payMana(String color) {
|
||||
color = MagicColor.toShortString(color);
|
||||
return this.addMana(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* increaseColorlessMana.
|
||||
* </p>
|
||||
*
|
||||
* @param manaToAdd
|
||||
* a int.
|
||||
*/
|
||||
public final void increaseColorlessMana(final int manaToAdd) {
|
||||
increaseShard(ManaCostShard.COLORLESS, manaToAdd);
|
||||
}
|
||||
|
||||
public final void increaseShard(final ManaCostShard shard, final int toAdd) {
|
||||
if (toAdd <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer cnt = unpaidShards.get(shard);
|
||||
unpaidShards.put(shard, Integer.valueOf(cnt == null || cnt == 0 ? toAdd : toAdd + cnt));
|
||||
unpaidShards.add(shard, toAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* decreaseColorlessMana
|
||||
* </p>
|
||||
* .
|
||||
*
|
||||
* @param manaToSubtract
|
||||
* an int. The amount of colorless mana to subtract from the
|
||||
* cost.Used by Delve.
|
||||
*/
|
||||
public final void decreaseColorlessMana(final int manaToSubtract) {
|
||||
decreaseShard(ManaCostShard.COLORLESS, manaToSubtract);
|
||||
}
|
||||
@@ -391,26 +272,15 @@ public class ManaCostBeingPaid {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer genericCnt = unpaidShards.get(shard);
|
||||
if (null == genericCnt || genericCnt - manaToSubtract <= 0) {
|
||||
unpaidShards.remove(shard);
|
||||
} else {
|
||||
unpaidShards.put(shard, Integer.valueOf(genericCnt - manaToSubtract));
|
||||
if (!unpaidShards.containsKey(shard)) {
|
||||
System.err.println("Tried to substract a " + shard.toString() + " shard that is not present in this ManaCostBeingPaid");
|
||||
return;
|
||||
}
|
||||
unpaidShards.substract(shard, manaToSubtract);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getColorlessManaAmount
|
||||
* </p>
|
||||
* Returns how much colorless mana must be paid to pay the cost.Used by
|
||||
* Delve AI.
|
||||
*
|
||||
* @return an int.
|
||||
*/
|
||||
public final int getColorlessManaAmount() {
|
||||
Integer genericCnt = unpaidShards.get(ManaCostShard.COLORLESS);
|
||||
return genericCnt == null ? 0 : genericCnt;
|
||||
return unpaidShards.count(ManaCostShard.COLORLESS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,40 +292,73 @@ public class ManaCostBeingPaid {
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean addMana(final String mana) {
|
||||
if (!this.isNeeded(mana)) {
|
||||
System.out.println("ManaCost : addMana() error, mana not needed - " + mana);
|
||||
public final boolean payMana(final String mana) {
|
||||
final byte colorMask = MagicColor.fromName(mana);
|
||||
if (!this.isAnyPartPayableWith(colorMask)) {
|
||||
//System.out.println("ManaCost : addMana() error, mana not needed - " + mana);
|
||||
return false;
|
||||
//throw new RuntimeException("ManaCost : addMana() error, mana not needed - " + mana);
|
||||
}
|
||||
byte colorMask = MagicColor.fromName(mana);
|
||||
|
||||
Predicate<ManaCostShard> predCanBePaid = new Predicate<ManaCostShard>() {
|
||||
@Override public boolean apply(ManaCostShard ms) {
|
||||
return ms.canBePaidWithManaOfColor(colorMask);
|
||||
}
|
||||
};
|
||||
|
||||
return tryPayMana(colorMask, Iterables.filter(unpaidShards.keySet(), predCanBePaid));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* addMana.
|
||||
* </p>
|
||||
*
|
||||
* @param mana
|
||||
* a {@link forge.card.mana.Mana} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean payMana(final Mana mana) {
|
||||
if (!this.isNeeded(mana)) {
|
||||
throw new RuntimeException("ManaCost : addMana() error, mana not needed - " + mana);
|
||||
}
|
||||
|
||||
Predicate<ManaCostShard> predCanBePaid = new Predicate<ManaCostShard>() {
|
||||
@Override public boolean apply(ManaCostShard ms) {
|
||||
return canBePaidWith(ms, mana);
|
||||
}
|
||||
};
|
||||
|
||||
return tryPayMana(mana.getColorCode(), Iterables.filter(unpaidShards.keySet(), predCanBePaid));
|
||||
}
|
||||
|
||||
private boolean tryPayMana(final byte colorMask, Iterable<ManaCostShard> payableShards) {
|
||||
ManaCostShard choice = null;
|
||||
for (ManaCostShard toPay : unpaidShards.keySet()) {
|
||||
if (canBePaidWith(toPay, mana)) {
|
||||
// if m is a better to pay than choice
|
||||
if (choice == null) {
|
||||
choice = toPay;
|
||||
continue;
|
||||
}
|
||||
if (isFirstChoiceBetter(toPay, choice, colorMask)) {
|
||||
choice = toPay;
|
||||
}
|
||||
for (ManaCostShard toPay : payableShards) {
|
||||
// if m is a better to pay than choice
|
||||
if (choice == null) {
|
||||
choice = toPay;
|
||||
continue;
|
||||
}
|
||||
if (isFirstChoiceBetter(toPay, choice, colorMask)) {
|
||||
choice = toPay;
|
||||
}
|
||||
} // for
|
||||
if (choice == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
decreaseShard(choice, 1);
|
||||
if (choice.isOr2Colorless() && choice.getColorMask() != colorMask ) {
|
||||
this.increaseColorlessMana(1);
|
||||
}
|
||||
|
||||
|
||||
this.sunburstMap |= colorMask;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isFirstChoiceBetter(ManaCostShard s1, ManaCostShard s2, byte b) {
|
||||
return getPayPriority(s1, b) > getPayPriority(s2, b);
|
||||
private boolean isFirstChoiceBetter(ManaCostShard s1, ManaCostShard s2, byte colorMask) {
|
||||
return getPayPriority(s1, colorMask) > getPayPriority(s2, colorMask);
|
||||
}
|
||||
|
||||
private int getPayPriority(ManaCostShard bill, byte paymentColor) {
|
||||
@@ -472,66 +375,16 @@ public class ManaCostBeingPaid {
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
private boolean canBePaidWith(ManaCostShard shard, Mana mana) {
|
||||
if (shard.isSnow() && mana.isSnow()) {
|
||||
return true;
|
||||
}
|
||||
//System.err.println(String.format("ManaPaid: paying for %s with %s" , shard, mana));
|
||||
// debug here even more;
|
||||
return canBePaidWith(shard, MagicColor.toShortString(mana.getColor()));
|
||||
}
|
||||
|
||||
private boolean canBePaidWith(ManaCostShard shard, String mana) {
|
||||
// most debug here!!
|
||||
String sShard = shard.toString();
|
||||
boolean res = "1".equals(sShard) || sShard.contains(mana) || shard.isOr2Colorless();
|
||||
//System.out.println(String.format("Str: paying for %s with %s => %d" , shard, mana, res ? 1 : 0));
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* addMana.
|
||||
* </p>
|
||||
*
|
||||
* @param mana
|
||||
* a {@link forge.card.mana.Mana} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean addMana(final Mana mana) {
|
||||
if (!this.isNeeded(mana)) {
|
||||
throw new RuntimeException("ManaCost : addMana() error, mana not needed - " + mana);
|
||||
}
|
||||
|
||||
ManaCostShard choice = null;
|
||||
for (ManaCostShard toPay : unpaidShards.keySet()) {
|
||||
if (canBePaidWith(toPay, mana)) {
|
||||
// if m is a better to pay than choice
|
||||
if (choice == null) {
|
||||
choice = toPay;
|
||||
continue;
|
||||
}
|
||||
if (isFirstChoiceBetter(toPay, choice, mana.getColorCode())) {
|
||||
choice = toPay;
|
||||
}
|
||||
}
|
||||
} // for
|
||||
if (choice == null) {
|
||||
if (shard.isSnow() && !mana.isSnow()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
decreaseShard(choice, 1);
|
||||
if (choice.isOr2Colorless() && choice.getColorMask() != mana.getColorCode() ) {
|
||||
this.increaseColorlessMana(1);
|
||||
}
|
||||
|
||||
this.sunburstMap |= mana.getColorCode();
|
||||
return true;
|
||||
|
||||
byte color = mana.getColorCode();
|
||||
return shard.canBePaidWithManaOfColor(color);
|
||||
}
|
||||
|
||||
public final void combineManaCost(final ManaCost extra) {
|
||||
@@ -545,10 +398,6 @@ public class ManaCostBeingPaid {
|
||||
increaseColorlessMana(extra.getGenericCost());
|
||||
}
|
||||
|
||||
public final void combineManaCost(final String extra) {
|
||||
combineManaCost(new ManaCost(new ManaCostParser(extra)));
|
||||
}
|
||||
|
||||
/**
|
||||
* To string.
|
||||
*
|
||||
@@ -615,17 +464,24 @@ public class ManaCostBeingPaid {
|
||||
return new ManaCost(new ManaCostBeingPaidIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>xcounter</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return a int.
|
||||
*/
|
||||
public final int getXcounter() {
|
||||
return cntX;
|
||||
}
|
||||
|
||||
|
||||
public final List<ManaCostShard> getUnpaidShards() {
|
||||
List<ManaCostShard> result = new ArrayList<ManaCostShard>();
|
||||
for(Entry<ManaCostShard, Integer> kv : unpaidShards.entrySet()) {
|
||||
for(int i = kv.getValue().intValue(); i > 0; i--) {
|
||||
result.add(kv.getKey());
|
||||
}
|
||||
}
|
||||
for(int i = cntX; i > 0; i--) {
|
||||
result.add(ManaCostShard.X);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* removeColorlessMana.
|
||||
@@ -782,5 +638,13 @@ public class ManaCostBeingPaid {
|
||||
|
||||
public String getSourceRestriction() {
|
||||
return sourceRestriction;
|
||||
}
|
||||
}
|
||||
|
||||
public Iterable<ManaCostShard> getDistinctShards() {
|
||||
return unpaidShards.keySet();
|
||||
}
|
||||
|
||||
public int getUnpaidShards(ManaCostShard key) {
|
||||
return unpaidShards.count(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,21 +17,14 @@
|
||||
*/
|
||||
package forge.card.mana;
|
||||
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.util.BinaryUtil;
|
||||
|
||||
/**
|
||||
* The Class CardManaCostShard.
|
||||
*/
|
||||
public enum ManaCostShard {
|
||||
public enum ManaCostShard implements Comparable<ManaCostShard> {
|
||||
// declaration order matters! Place the shards that offer least ways to be paid for first
|
||||
|
||||
|
||||
COLORLESS(ManaAtom.COLORLESS, "1"),
|
||||
|
||||
X(ManaAtom.IS_X, "X"),
|
||||
S(ManaAtom.IS_SNOW, "S"),
|
||||
|
||||
/* Pure colors */
|
||||
WHITE(ManaAtom.WHITE, "W"),
|
||||
BLUE(ManaAtom.BLUE, "U"),
|
||||
@@ -51,6 +44,17 @@ public enum ManaCostShard {
|
||||
BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"),
|
||||
RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"),
|
||||
|
||||
/* Or 2 colorless */
|
||||
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
|
||||
U2(ManaAtom.BLUE | ManaAtom.OR_2_COLORLESS, "2/U", "2U"),
|
||||
B2(ManaAtom.BLACK | ManaAtom.OR_2_COLORLESS, "2/B", "2B"),
|
||||
R2(ManaAtom.RED | ManaAtom.OR_2_COLORLESS, "2/R", "2R"),
|
||||
G2(ManaAtom.GREEN | ManaAtom.OR_2_COLORLESS, "2/G", "2G"),
|
||||
|
||||
// Snow and colorless
|
||||
S(ManaAtom.IS_SNOW, "S"),
|
||||
COLORLESS(ManaAtom.COLORLESS, "1"),
|
||||
|
||||
/* Phyrexian */
|
||||
PW(ManaAtom.WHITE | ManaAtom.OR_2_LIFE, "W/P", "PW"),
|
||||
PU(ManaAtom.BLUE | ManaAtom.OR_2_LIFE, "U/P", "PU"),
|
||||
@@ -58,13 +62,7 @@ public enum ManaCostShard {
|
||||
PR(ManaAtom.RED | ManaAtom.OR_2_LIFE, "R/P", "PR"),
|
||||
PG(ManaAtom.GREEN | ManaAtom.OR_2_LIFE, "G/P", "PG"),
|
||||
|
||||
/* Or 2 colorless */
|
||||
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
|
||||
U2(ManaAtom.BLUE | ManaAtom.OR_2_COLORLESS, "2/U", "2U"),
|
||||
B2(ManaAtom.BLACK | ManaAtom.OR_2_COLORLESS, "2/B", "2B"),
|
||||
R2(ManaAtom.RED | ManaAtom.OR_2_COLORLESS, "2/R", "2R"),
|
||||
G2(ManaAtom.GREEN | ManaAtom.OR_2_COLORLESS, "2/G", "2G");
|
||||
|
||||
X(ManaAtom.IS_X, "X");
|
||||
|
||||
private final int shard;
|
||||
|
||||
@@ -275,23 +273,6 @@ public enum ManaCostShard {
|
||||
public boolean isOr2Colorless() {
|
||||
return (this.shard & ManaAtom.OR_2_COLORLESS) != 0;
|
||||
}
|
||||
/**
|
||||
* TODO: Can pay for this shard with unlimited mana of given color combination?
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
public boolean canBePaidWithAvaliable(ColorSet color) {
|
||||
// can pay with life?
|
||||
if (this.isPhyrexian()) {
|
||||
return true;
|
||||
}
|
||||
// can pay with any color?
|
||||
if (this.isOr2Colorless()) {
|
||||
return true;
|
||||
}
|
||||
// either colored part is empty, or there are same colors in shard and mana source
|
||||
return (COLORS_SUPERPOSITION & this.shard) == 0 || (color.getColor() & this.shard) > 0;
|
||||
}
|
||||
|
||||
public boolean canBePaidWithManaOfColor(byte colorCode) {
|
||||
return this.isOr2Colorless() || (COLORS_SUPERPOSITION & this.shard) == 0 || (colorCode & this.shard) > 0;
|
||||
|
||||
@@ -18,21 +18,20 @@
|
||||
package forge.card.mana;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import forge.Constant;
|
||||
import com.google.common.base.Supplier;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.spellability.AbilityManaPart;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.GlobalRuleChange;
|
||||
import forge.game.player.Player;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.util.maps.MapOfLists;
|
||||
import forge.util.maps.TreeMapOfLists;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -43,14 +42,14 @@ import forge.gui.GuiChoose;
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ManaPool {
|
||||
// current paying moved to SpellAbility
|
||||
|
||||
private final ArrayList<Mana> floatingMana = new ArrayList<Mana>();
|
||||
private final int[] floatingTotals = new int[6]; // WUBRGC
|
||||
private final int[] floatingSnowTotals = new int[6]; // WUBRGC
|
||||
private final static Supplier<List<Mana>> listFactory = new Supplier<List<Mana>>(){
|
||||
@Override public List<Mana> get() { return new ArrayList<Mana>(); }
|
||||
};
|
||||
|
||||
private final MapOfLists<Byte, Mana> floatingMana = new TreeMapOfLists<Byte, Mana>(listFactory);
|
||||
|
||||
/** Constant <code>map</code>. */
|
||||
private static final Map<String, Integer> MAP = new HashMap<String, Integer>();
|
||||
private final Player owner;
|
||||
|
||||
/**
|
||||
@@ -63,91 +62,15 @@ public class ManaPool {
|
||||
*/
|
||||
public ManaPool(final Player player) {
|
||||
owner = player;
|
||||
ManaPool.MAP.put(Constant.Color.WHITE, 0);
|
||||
ManaPool.MAP.put(Constant.Color.BLUE, 1);
|
||||
ManaPool.MAP.put(Constant.Color.BLACK, 2);
|
||||
ManaPool.MAP.put(Constant.Color.RED, 3);
|
||||
ManaPool.MAP.put(Constant.Color.GREEN, 4);
|
||||
ManaPool.MAP.put(Constant.Color.COLORLESS, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* calculatManaTotals for the Player panel.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
private void calculateManaTotals() {
|
||||
for (int i = 0; i < floatingTotals.length; i++) {
|
||||
floatingTotals[i] = 0;
|
||||
floatingSnowTotals[i] = 0;
|
||||
}
|
||||
|
||||
for (final Mana m : this.floatingMana) {
|
||||
if (m.isSnow()) {
|
||||
floatingSnowTotals[ManaPool.MAP.get(m.getColor())]++;
|
||||
} else {
|
||||
floatingTotals[ManaPool.MAP.get(m.getColor())]++;
|
||||
}
|
||||
}
|
||||
public final int getAmountOfColor(final byte color) {
|
||||
Collection<Mana> ofColor = floatingMana.get(color);
|
||||
return ofColor == null ? 0 : ofColor.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getAmountOfColor.
|
||||
* </p>
|
||||
*
|
||||
* @param color
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a int.
|
||||
*/
|
||||
public final int getAmountOfColor(final String color) {
|
||||
if (color.equals(Constant.Color.SNOW)) {
|
||||
// If looking for Snow mana return total Snow
|
||||
int total = 0;
|
||||
for (int i : this.floatingSnowTotals) {
|
||||
total += i;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// If looking for Color/Colorless total Snow and non-Snow
|
||||
int i = ManaPool.MAP.get(color);
|
||||
return this.floatingTotals[i] + this.floatingSnowTotals[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isEmpty.
|
||||
* </p>
|
||||
*
|
||||
* @return a boolean.
|
||||
*/
|
||||
private boolean isEmpty() {
|
||||
return this.floatingMana.size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* addManaToPool.
|
||||
* </p>
|
||||
*
|
||||
* @param pool
|
||||
* a {@link java.util.ArrayList} object.
|
||||
* @param mana
|
||||
* a {@link forge.card.mana.Mana} object.
|
||||
*/
|
||||
private void addManaToPool(final ArrayList<Mana> pool, final Mana mana) {
|
||||
pool.add(mana);
|
||||
if (pool.equals(this.floatingMana)) {
|
||||
int i = ManaPool.MAP.get(mana.getColor());
|
||||
if (mana.isSnow()) {
|
||||
this.floatingSnowTotals[i]++;
|
||||
}
|
||||
else {
|
||||
this.floatingTotals[i]++;
|
||||
}
|
||||
}
|
||||
private void addMana(final Mana mana) {
|
||||
floatingMana.add(mana.getColorCode(), mana);
|
||||
owner.updateObservers();
|
||||
}
|
||||
|
||||
@@ -159,9 +82,9 @@ public class ManaPool {
|
||||
* @param manaList
|
||||
* a {@link java.util.ArrayList} object.
|
||||
*/
|
||||
public final void addManaToFloating(final ArrayList<Mana> manaList) {
|
||||
public final void add(final Iterable<Mana> manaList) {
|
||||
for (final Mana m : manaList) {
|
||||
this.addManaToPool(this.floatingMana, m);
|
||||
this.addMana(m);
|
||||
}
|
||||
owner.getGame().getAction().checkStateEffects();
|
||||
owner.updateObservers();
|
||||
@@ -175,36 +98,24 @@ public class ManaPool {
|
||||
* </p>
|
||||
*/
|
||||
public final int clearPool(boolean isEndOfPhase) {
|
||||
int numRemoved = 0;
|
||||
// isEndOfPhase parameter: true = end of phase, false = mana drain effect
|
||||
if (this.floatingMana.isEmpty()) { return 0; }
|
||||
|
||||
if (isEndOfPhase && owner.getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.manapoolsDontEmpty)) {
|
||||
return numRemoved;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.floatingMana.isEmpty()) {
|
||||
this.calculateManaTotals();
|
||||
//this.owner.updateObservers();
|
||||
return numRemoved;
|
||||
}
|
||||
int numRemoved = 0;
|
||||
boolean keepGreenMana = isEndOfPhase && this.owner.hasKeyword("Green mana doesn't empty from your mana pool as steps and phases end.");
|
||||
|
||||
if (isEndOfPhase && this.owner.hasKeyword("Green mana doesn't empty from your mana pool as steps and phases end.")) {
|
||||
// Omnath in play, clear all non-green mana
|
||||
int i = 0;
|
||||
while (i < this.floatingMana.size()) {
|
||||
if (this.floatingMana.get(i).isColor(Constant.Color.GREEN)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
numRemoved++;
|
||||
this.floatingMana.remove(i);
|
||||
}
|
||||
} else {
|
||||
numRemoved = this.floatingMana.size();
|
||||
this.floatingMana.clear();
|
||||
Set<Byte> keys = floatingMana.keySet();
|
||||
if ( keepGreenMana )
|
||||
keys.remove(Byte.valueOf(MagicColor.GREEN));
|
||||
|
||||
for(Byte b : keys) {
|
||||
numRemoved += floatingMana.get(b).size();
|
||||
floatingMana.get(b).clear();
|
||||
}
|
||||
this.calculateManaTotals();
|
||||
//this.owner.updateObservers();
|
||||
|
||||
return numRemoved;
|
||||
}
|
||||
|
||||
@@ -221,166 +132,86 @@ public class ManaPool {
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
* @return a {@link forge.card.mana.Mana} object.
|
||||
*/
|
||||
private Mana getMana(final String manaStr, final SpellAbility saBeingPaidFor, String restriction) {
|
||||
final ArrayList<Mana> pool = this.floatingMana;
|
||||
|
||||
//System.out.format("ManaStr='%s' ...", manaStr);
|
||||
ManaCostShard shard = ManaCostShard.parseNonGeneric(manaStr);
|
||||
//System.out.format("Shard=%s (%d)", shard.toString(), shard.getColorMask() );
|
||||
//System.out.println();
|
||||
|
||||
// What are the available options?
|
||||
final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<Pair<Mana, Integer>>();
|
||||
for (final Mana thisMana : pool) {
|
||||
|
||||
if (!thisMana.getManaAbility().meetsManaRestrictions(saBeingPaidFor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean canPay = shard.canBePaidWithManaOfColor(thisMana.getColorCode());
|
||||
if (!canPay || (shard.isSnow() && !thisMana.isSnow())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if( StringUtils.isNotBlank(restriction) && !thisMana.getSourceCard().isType(restriction) )
|
||||
continue;
|
||||
|
||||
// prefer colorless mana to spend
|
||||
int weight = thisMana.isColorless() ? 5 : 0;
|
||||
|
||||
// prefer restricted mana to spend
|
||||
if (thisMana.isRestricted()) {
|
||||
weight += 2;
|
||||
}
|
||||
|
||||
// Spend non-snow mana first
|
||||
if (!thisMana.isSnow()) {
|
||||
weight += 1;
|
||||
}
|
||||
|
||||
weightedOptions.add(Pair.of(thisMana, weight));
|
||||
}
|
||||
private Mana getMana(final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction) {
|
||||
final List<Pair<Mana, Integer>> weightedOptions = selectManaToPayFor(shard, saBeingPaidFor, restriction);
|
||||
|
||||
// Exclude border case
|
||||
if (weightedOptions.isEmpty()) {
|
||||
|
||||
return null; // There is no matching mana in the pool
|
||||
}
|
||||
|
||||
// have at least one option at this moment
|
||||
int maxWeight = Integer.MIN_VALUE;
|
||||
int equalWeights = 0;
|
||||
Mana toPay = null;
|
||||
// select equal weight possibilities
|
||||
List<Mana> manaChoices = new ArrayList<Mana>();
|
||||
int bestWeight = Integer.MIN_VALUE;
|
||||
for (Pair<Mana, Integer> option : weightedOptions) {
|
||||
int thisWeight = option.getRight();
|
||||
if (thisWeight > maxWeight) {
|
||||
maxWeight = thisWeight;
|
||||
equalWeights = 1;
|
||||
toPay = option.getLeft();
|
||||
} else if (thisWeight == maxWeight) {
|
||||
equalWeights++;
|
||||
Mana thisMana = option.getLeft();
|
||||
|
||||
if (thisWeight > bestWeight) {
|
||||
manaChoices.clear();
|
||||
bestWeight = thisWeight;
|
||||
}
|
||||
|
||||
if (thisWeight == bestWeight) {
|
||||
// add only distinct Mana-s
|
||||
boolean haveDuplicate = false;
|
||||
for(Mana m : manaChoices) {
|
||||
if(m.equals(thisMana) ) {
|
||||
haveDuplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!haveDuplicate)
|
||||
manaChoices.add(thisMana);
|
||||
}
|
||||
}
|
||||
|
||||
// got an only one best option?
|
||||
if (equalWeights == 1) {
|
||||
return toPay;
|
||||
if (manaChoices.size() == 1) {
|
||||
return manaChoices.get(0);
|
||||
}
|
||||
|
||||
// select equal weight possibilities
|
||||
List<Mana> options = new ArrayList<Mana>();
|
||||
for (Pair<Mana, Integer> option : weightedOptions) {
|
||||
int thisWeight = option.getRight();
|
||||
if (maxWeight == thisWeight) {
|
||||
options.add(option.getLeft());
|
||||
}
|
||||
}
|
||||
// Let them choose then
|
||||
return owner.getController().chooseManaFromPool(manaChoices);
|
||||
}
|
||||
|
||||
// if the options are equal, there is no difference on which to spend
|
||||
toPay = options.get(0);
|
||||
boolean allAreEqual = true;
|
||||
for (int i = 1; i < options.size(); i++) {
|
||||
if (!toPay.equals(options.get(i))) {
|
||||
|
||||
allAreEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allAreEqual) {
|
||||
return toPay;
|
||||
}
|
||||
|
||||
// Not found a good one - then let them choose
|
||||
final List<Mana> manaChoices = options;
|
||||
Mana payment = null;
|
||||
|
||||
final int[] normalMana = { 0, 0, 0, 0, 0, 0 };
|
||||
final int[] snowMana = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
// loop through manaChoices adding
|
||||
for (final Mana m : manaChoices) {
|
||||
if (m.isSnow()) {
|
||||
snowMana[ManaPool.MAP.get(m.getColor())]++;
|
||||
} else {
|
||||
normalMana[ManaPool.MAP.get(m.getColor())]++;
|
||||
}
|
||||
}
|
||||
|
||||
int totalMana = 0;
|
||||
final ArrayList<String> alChoice = new ArrayList<String>();
|
||||
for (int i = 0; i < normalMana.length; i++) {
|
||||
totalMana += normalMana[i];
|
||||
totalMana += snowMana[i];
|
||||
if (normalMana[i] > 0) {
|
||||
alChoice.add(Constant.Color.COLORS.get(i) + "(" + normalMana[i] + ")");
|
||||
}
|
||||
if (snowMana[i] > 0) {
|
||||
alChoice.add("{S}" + Constant.Color.COLORS.get(i) + "(" + snowMana[i] + ")");
|
||||
}
|
||||
}
|
||||
|
||||
if (alChoice.size() == 1) {
|
||||
payment = manaChoices.get(0);
|
||||
return payment;
|
||||
}
|
||||
|
||||
int numColorless = 0;
|
||||
if (StringUtils.isNumeric(manaStr)) {
|
||||
numColorless = Integer.parseInt(manaStr);
|
||||
}
|
||||
if (numColorless >= totalMana) {
|
||||
payment = manaChoices.get(0);
|
||||
return payment;
|
||||
}
|
||||
|
||||
Object o;
|
||||
|
||||
if (this.owner.isHuman()) {
|
||||
o = GuiChoose.oneOrNone("Pay Mana from Mana Pool", alChoice);
|
||||
} else {
|
||||
o = alChoice.get(0); // owner is computer
|
||||
}
|
||||
|
||||
if (o != null) {
|
||||
String ch = o.toString();
|
||||
final boolean grabSnow = ch.startsWith("{S}");
|
||||
ch = ch.replace("{S}", "");
|
||||
|
||||
ch = ch.substring(0, ch.indexOf("("));
|
||||
|
||||
for (final Mana m : manaChoices) {
|
||||
if (m.isColor(ch) && (!grabSnow || (grabSnow && m.isSnow()))) {
|
||||
if (payment == null) {
|
||||
payment = m;
|
||||
} else if (payment.isSnow() && !m.isSnow()) {
|
||||
payment = m;
|
||||
}
|
||||
private List<Pair<Mana, Integer>> selectManaToPayFor(final ManaCostShard shard, final SpellAbility saBeingPaidFor,
|
||||
String restriction) {
|
||||
final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<Pair<Mana, Integer>>();
|
||||
for (final Byte manaKey : this.floatingMana.keySet()) {
|
||||
if(!shard.canBePaidWithManaOfColor(manaKey.byteValue()))
|
||||
continue;
|
||||
|
||||
for(final Mana thisMana : this.floatingMana.get(manaKey)) {
|
||||
if (!thisMana.getManaAbility().meetsManaRestrictions(saBeingPaidFor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean canPay = shard.canBePaidWithManaOfColor(thisMana.getColorCode());
|
||||
if (!canPay || (shard.isSnow() && !thisMana.isSnow())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if( StringUtils.isNotBlank(restriction) && !thisMana.getSourceCard().isType(restriction) )
|
||||
continue;
|
||||
|
||||
// prefer colorless mana to spend
|
||||
int weight = thisMana.isColorless() ? 5 : 0;
|
||||
|
||||
// prefer restricted mana to spend
|
||||
if (thisMana.isRestricted()) {
|
||||
weight += 2;
|
||||
}
|
||||
|
||||
// Spend non-snow mana first
|
||||
if (!thisMana.isSnow()) {
|
||||
weight += 1;
|
||||
}
|
||||
|
||||
weightedOptions.add(Pair.of(thisMana, weight));
|
||||
}
|
||||
}
|
||||
|
||||
return payment;
|
||||
return weightedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -393,87 +224,27 @@ public class ManaPool {
|
||||
* @param choice
|
||||
* a {@link forge.card.mana.Mana} object.
|
||||
*/
|
||||
private void removeManaFrom(final ArrayList<Mana> pool, final Mana choice) {
|
||||
if (choice != null && pool.contains(choice)) {
|
||||
pool.remove(choice);
|
||||
if (pool.equals(this.floatingMana)) {
|
||||
int i = ManaPool.MAP.get(choice.getColor());
|
||||
if (choice.isSnow()) {
|
||||
this.floatingSnowTotals[i]--;
|
||||
}
|
||||
else {
|
||||
this.floatingTotals[i]--;
|
||||
}
|
||||
}
|
||||
private void removeMana(final Mana mana) {
|
||||
Collection<Mana> cm = floatingMana.get(mana.getColorCode());
|
||||
if (cm.remove(mana)) {
|
||||
owner.updateObservers();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* payManaFromPool.
|
||||
* </p>
|
||||
*
|
||||
* @param saBeingPaidFor
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
* @param manaCost
|
||||
* a {@link forge.card.mana.ManaCostBeingPaid} object.
|
||||
* @return a {@link forge.card.mana.ManaCostBeingPaid} object.
|
||||
*/
|
||||
public final void payManaFromPool(final SpellAbility saBeingPaidFor, ManaCostBeingPaid manaCost) {
|
||||
|
||||
// paying from Mana Pool
|
||||
if (manaCost.isPaid() || this.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ArrayList<Mana> manaPaid = saBeingPaidFor.getPayingMana();
|
||||
|
||||
List<String> splitCost = Arrays.asList(manaCost.toString().replace("X ", "").replace("P", "").split(" "));
|
||||
Collections.reverse(splitCost); // reverse to pay colorful parts first with matching-color mana while it lasts.
|
||||
for(String part : splitCost) {
|
||||
int loops = StringUtils.isNumeric(part) ? Integer.parseInt(part) : 1;
|
||||
for(int i = 0; i < loops; i++ ) {
|
||||
final Mana mana = this.getMana(part, saBeingPaidFor, manaCost.getSourceRestriction());
|
||||
if (mana != null) {
|
||||
manaCost.payMana(mana);
|
||||
manaPaid.add(mana);
|
||||
this.removeManaFrom(this.floatingMana, mana);
|
||||
if (mana.addsNoCounterMagic() && saBeingPaidFor.getSourceCard() != null) {
|
||||
saBeingPaidFor.getSourceCard().setCanCounter(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* payManaFromPool.
|
||||
* </p>
|
||||
*
|
||||
* @param saBeingPaidFor
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
* @param manaCost
|
||||
* a {@link forge.card.mana.ManaCostBeingPaid} object.
|
||||
* @param manaStr
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a {@link forge.card.mana.ManaCostBeingPaid} object.
|
||||
*/
|
||||
public final void payManaFromPool(final SpellAbility saBeingPaidFor, final ManaCostBeingPaid manaCost, final String manaStr) {
|
||||
if (manaStr.trim().equals("") || manaCost.isPaid()) {
|
||||
public final void payManaFromPool(final SpellAbility saBeingPaidFor, final ManaCostBeingPaid manaCost, final ManaCostShard manaShard) {
|
||||
if (manaCost.isPaid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get a mana of this type from floating, bail if none available
|
||||
final Mana mana = this.getMana(manaStr, saBeingPaidFor, manaCost.getSourceRestriction());
|
||||
final Mana mana = this.getMana(manaShard, saBeingPaidFor, manaCost.getSourceRestriction());
|
||||
if (mana == null) {
|
||||
return; // no matching mana in the pool
|
||||
}
|
||||
else if (manaCost.isNeeded(mana)) {
|
||||
manaCost.payMana(mana);
|
||||
saBeingPaidFor.getPayingMana().add(mana);
|
||||
this.removeManaFrom(this.floatingMana, mana);
|
||||
this.removeMana( mana);
|
||||
if (mana.addsNoCounterMagic() && saBeingPaidFor.getSourceCard() != null) {
|
||||
saBeingPaidFor.getSourceCard().setCanCounter(false);
|
||||
}
|
||||
@@ -508,11 +279,11 @@ public class ManaPool {
|
||||
}
|
||||
|
||||
paidAbs.add(ma); // assumes some part on the mana produced by the ability will get used
|
||||
for (final Mana mana : abManaPart.getLastProduced()) {
|
||||
for (final Mana mana : abManaPart.getLastManaProduced()) {
|
||||
if (manaCost.isNeeded(mana)) {
|
||||
manaCost.payMana(mana);
|
||||
manaPaid.add(mana);
|
||||
this.removeManaFrom(this.floatingMana, mana);
|
||||
this.removeMana(mana);
|
||||
if (mana.addsNoCounterMagic() && sa.getSourceCard() != null) {
|
||||
sa.getSourceCard().setCanCounter(false);
|
||||
}
|
||||
@@ -551,33 +322,35 @@ public class ManaPool {
|
||||
ability.getSourceCard().setCanCounter(true);
|
||||
}
|
||||
for (final Mana m : manaPaid) {
|
||||
this.addManaToPool(this.floatingMana, m);
|
||||
this.addMana(m);
|
||||
}
|
||||
}
|
||||
|
||||
manaPaid.clear();
|
||||
this.calculateManaTotals();
|
||||
this.owner.updateObservers();
|
||||
}
|
||||
|
||||
|
||||
private boolean accountFor(final SpellAbility sa, final AbilityManaPart ma) {
|
||||
final ArrayList<Mana> manaPaid = sa.getPayingMana();
|
||||
final List<Mana> manaPaid = sa.getPayingMana();
|
||||
|
||||
if ((manaPaid.size() == 0) && (this.floatingMana.size() == 0)) {
|
||||
if (manaPaid.isEmpty() && this.floatingMana.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ArrayList<Mana> removePaying = new ArrayList<Mana>();
|
||||
final ArrayList<Mana> removeFloating = new ArrayList<Mana>();
|
||||
|
||||
|
||||
boolean manaNotAccountedFor = false;
|
||||
// loop over mana produced by mana ability
|
||||
for (Mana mana : ma.getLastProduced()) {
|
||||
for (Mana mana : ma.getLastManaProduced()) {
|
||||
Collection<Mana> poolLane = this.floatingMana.get(mana.getColorCode());
|
||||
|
||||
if (manaPaid.contains(mana)) {
|
||||
removePaying.add(mana);
|
||||
}
|
||||
else if (this.floatingMana.contains(mana)) {
|
||||
else if (poolLane != null && poolLane.contains(mana)) {
|
||||
removeFloating.add(mana);
|
||||
}
|
||||
else {
|
||||
@@ -593,10 +366,10 @@ public class ManaPool {
|
||||
}
|
||||
|
||||
for (int k = 0; k < removePaying.size(); k++) {
|
||||
this.removeManaFrom(manaPaid, removePaying.get(k));
|
||||
manaPaid.remove(removePaying.get(k));
|
||||
}
|
||||
for (int k = 0; k < removeFloating.size(); k++) {
|
||||
this.removeManaFrom(this.floatingMana, removeFloating.get(k));
|
||||
this.removeMana(removeFloating.get(k));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import forge.Card;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.Mana;
|
||||
import forge.card.mana.ManaPool;
|
||||
@@ -44,7 +47,7 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
private String origProduced;
|
||||
private String lastExpressChoice = "";
|
||||
private String manaRestrictions = "";
|
||||
private transient ArrayList<Mana> lastProduced = new ArrayList<Mana>();
|
||||
private transient ArrayList<Mana> lastManaProduced = new ArrayList<Mana>();
|
||||
|
||||
/** The canceled. */
|
||||
private boolean canceled = false;
|
||||
@@ -106,22 +109,20 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
final ManaPool manaPool = player.getManaPool();
|
||||
|
||||
//clear lastProduced
|
||||
this.lastProduced.clear();
|
||||
this.lastManaProduced.clear();
|
||||
|
||||
// loop over mana produced string
|
||||
for (final String c : produced.split(" ")) {
|
||||
try {
|
||||
int colorlessAmount = Integer.parseInt(c);
|
||||
for (int i = 0; i < colorlessAmount; i++) {
|
||||
this.lastProduced.add(new Mana(c, source, this));
|
||||
if(StringUtils.isNumeric(c))
|
||||
for(int i = Integer.parseInt(c); i > 0; i--) {
|
||||
this.lastManaProduced.add(new Mana((byte)0, source, this));
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
this.lastProduced.add(new Mana(c, source, this));
|
||||
}
|
||||
else
|
||||
this.lastManaProduced.add(new Mana(MagicColor.fromName(c), source, this));
|
||||
}
|
||||
|
||||
// add the mana produced to the mana pool
|
||||
manaPool.addManaToFloating(this.lastProduced);
|
||||
manaPool.add(this.lastManaProduced);
|
||||
|
||||
// Run triggers
|
||||
final HashMap<String, Object> runParams = new HashMap<String, Object>();
|
||||
@@ -228,6 +229,16 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
this.lastExpressChoice = s;
|
||||
}
|
||||
|
||||
public void setExpressChoice(ColorSet cs) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(cs.hasBlack()) sb.append("B ");
|
||||
if(cs.hasBlue()) sb.append("U ");
|
||||
if(cs.hasWhite()) sb.append("W ");
|
||||
if(cs.hasRed()) sb.append("R ");
|
||||
if(cs.hasGreen()) sb.append("G ");
|
||||
this.lastExpressChoice = cs.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>lastAnyChoice</code>.
|
||||
@@ -256,8 +267,8 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public ArrayList<Mana> getLastProduced() {
|
||||
return this.lastProduced;
|
||||
public ArrayList<Mana> getLastManaProduced() {
|
||||
return this.lastManaProduced;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package forge.control.input;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardUtil;
|
||||
import forge.Constant;
|
||||
import forge.FThreads;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.ability.ApiType;
|
||||
import forge.card.mana.ManaCostBeingPaid;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.card.spellability.AbilityManaPart;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.GameState;
|
||||
@@ -71,34 +71,26 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I
|
||||
* a {@link java.lang.String} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
private static boolean canMake(final SpellAbility am, final String mana) {
|
||||
if (mana.contains("1")) {
|
||||
private static boolean canMake(final SpellAbility am, final byte colorMask) {
|
||||
if (colorMask == 0) {
|
||||
return true;
|
||||
}
|
||||
AbilityManaPart m = am.getManaPart();
|
||||
if (mana.contains("S") && m.isSnow()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m.isAnyMana()) {
|
||||
return true;
|
||||
}
|
||||
if (am.getApi() == ApiType.ManaReflected) {
|
||||
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am, am, new HashSet<String>(), new ArrayList<Card>());
|
||||
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am);
|
||||
for (final String color : reflectableColors) {
|
||||
if (mana.contains(MagicColor.toShortString(color))) {
|
||||
if (0 != (colorMask & MagicColor.fromName(color))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String[] colorsProduced;
|
||||
if (m.isComboMana()) {
|
||||
colorsProduced = m.getComboColors().split(" ");
|
||||
}
|
||||
else {
|
||||
colorsProduced = m.getOrigProduced().split(" ");
|
||||
}
|
||||
for (final String color : colorsProduced) {
|
||||
if (mana.contains(color)) {
|
||||
String colorsProduced = m.isComboMana() ? m.getComboColors() : m.getOrigProduced();
|
||||
for (final String color : colorsProduced.split(" ")) {
|
||||
if (0 != (colorMask & MagicColor.fromName(color))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -119,7 +111,7 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I
|
||||
protected void useManaFromPool(byte colorCode) { useManaFromPool(colorCode, manaCost); }
|
||||
protected void useManaFromPool(byte colorCode, ManaCostBeingPaid manaCost) {
|
||||
// Convert Color to short String
|
||||
player.getManaPool().payManaFromPool(saPaidFor, manaCost, MagicColor.toShortString(colorCode));
|
||||
player.getManaPool().payManaFromPool(saPaidFor, manaCost, ManaCostShard.parseNonGeneric(MagicColor.toShortString(colorCode)));
|
||||
|
||||
onManaAbilityPlayed(null);
|
||||
showMessage();
|
||||
@@ -144,21 +136,15 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I
|
||||
return;
|
||||
}
|
||||
|
||||
final StringBuilder cneeded = new StringBuilder();
|
||||
final StringBuilder colorRequired = new StringBuilder();
|
||||
boolean choice = true;
|
||||
boolean skipExpress = false;
|
||||
|
||||
for (final String color : Constant.Color.MANA_COLORS) {
|
||||
String shortColor = MagicColor.toShortString(color);
|
||||
if (manaCost.isNeeded(color)) {
|
||||
cneeded.append(shortColor);
|
||||
}
|
||||
if (manaCost.isColor(shortColor)) {
|
||||
colorRequired.append(shortColor);
|
||||
}
|
||||
byte colorCanUse = 0;
|
||||
byte colorNeeded = 0;
|
||||
|
||||
for (final byte color : MagicColor.WUBRG) {
|
||||
if (manaCost.isAnyPartPayableWith(color)) colorCanUse |= color;
|
||||
if (manaCost.needsColor(color)) colorNeeded |= color;
|
||||
}
|
||||
|
||||
boolean canUseColorless = manaCost.isAnyPartPayableWith((byte)0);
|
||||
|
||||
List<SpellAbility> abilities = new ArrayList<SpellAbility>();
|
||||
// you can't remove unneeded abilities inside a for(am:abilities) loop :(
|
||||
|
||||
@@ -166,108 +152,62 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I
|
||||
if( StringUtils.isNotBlank(typeRes) && !card.isType(typeRes))
|
||||
return;
|
||||
|
||||
boolean guessAbilityWithRequiredColors = true;
|
||||
for (SpellAbility ma : card.getManaAbility()) {
|
||||
ma.setActivatingPlayer(player);
|
||||
|
||||
AbilityManaPart m = null;
|
||||
SpellAbility tail = ma;
|
||||
while (m == null && tail != null) {
|
||||
m = tail.getManaPart();
|
||||
tail = tail.getSubAbility();
|
||||
}
|
||||
if (m == null) {
|
||||
continue;
|
||||
} else if (!ma.canPlay()) {
|
||||
continue;
|
||||
} else if (!InputPayManaBase.canMake(ma, cneeded.toString())) {
|
||||
continue;
|
||||
} else if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) {
|
||||
continue;
|
||||
} else if (!m.meetsManaRestrictions(saPaidFor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m == null || !ma.canPlay()) continue;
|
||||
if (!canUseColorless && !InputPayManaBase.canMake(ma, colorCanUse)) continue;
|
||||
if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) continue;
|
||||
if (!m.meetsManaRestrictions(saPaidFor)) continue;
|
||||
|
||||
abilities.add(ma);
|
||||
|
||||
if (!skipExpress) {
|
||||
// skip express mana if the ability is not undoable or reusable
|
||||
if (!ma.isUndoable()) {
|
||||
skipExpress = true;
|
||||
continue;
|
||||
} else if (!ma.getPayCosts().isRenewableResource()) {
|
||||
skipExpress = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// skip express mana if the ability is not undoable or reusable
|
||||
if (!ma.isUndoable() || !ma.getPayCosts().isRenewableResource())
|
||||
guessAbilityWithRequiredColors = false;
|
||||
}
|
||||
|
||||
if (abilities.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Store some information about color costs to help with any mana choices
|
||||
String colorsNeeded = colorRequired.toString();
|
||||
if ("1".equals(colorsNeeded)) { // only colorless left
|
||||
if (saPaidFor.getSourceCard() != null
|
||||
&& saPaidFor.getSourceCard().hasSVar("ManaNeededToAvoidNegativeEffect")) {
|
||||
colorsNeeded = "";
|
||||
if (colorNeeded == 0) { // only colorless left
|
||||
if (saPaidFor.getSourceCard() != null && saPaidFor.getSourceCard().hasSVar("ManaNeededToAvoidNegativeEffect")) {
|
||||
String[] negEffects = saPaidFor.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").split(",");
|
||||
for (String negColor : negEffects) {
|
||||
// convert long color strings to short color strings
|
||||
if (negColor.length() > 1) {
|
||||
negColor = MagicColor.toShortString(negColor);
|
||||
}
|
||||
if (!colorsNeeded.contains(negColor)) {
|
||||
colorsNeeded = colorsNeeded.concat(negColor);
|
||||
}
|
||||
byte col = MagicColor.fromName(negColor);
|
||||
colorCanUse |= col;
|
||||
}
|
||||
}
|
||||
else {
|
||||
colorsNeeded = "W";
|
||||
}
|
||||
}
|
||||
else {
|
||||
// remove colorless from colors needed
|
||||
colorsNeeded = colorsNeeded.replace("1", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// If the card has sunburst or any other ability that tracks mana spent,
|
||||
// skip express Mana choice
|
||||
if (saPaidFor.getSourceCard() != null
|
||||
&& saPaidFor.getSourceCard().hasKeyword("Sunburst") && saPaidFor.isSpell()) {
|
||||
colorsNeeded = "WUBRG";
|
||||
skipExpress = true;
|
||||
if (saPaidFor.getSourceCard() != null && saPaidFor.getSourceCard().hasKeyword("Sunburst") && saPaidFor.isSpell()) {
|
||||
colorCanUse = MagicColor.ALL_COLORS;
|
||||
guessAbilityWithRequiredColors = false;
|
||||
}
|
||||
|
||||
if (!skipExpress) {
|
||||
boolean choice = true;
|
||||
if (guessAbilityWithRequiredColors) {
|
||||
// express Mana Choice
|
||||
final ArrayList<SpellAbility> colorMatches = new ArrayList<SpellAbility>();
|
||||
for (final SpellAbility am : abilities) {
|
||||
AbilityManaPart m = am.getManaPart();
|
||||
if (am.getApi() == ApiType.ManaReflected) {
|
||||
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am, am, new HashSet<String>(), new ArrayList<Card>());
|
||||
for (final String color : reflectableColors) {
|
||||
if (manaCost.isColor(color)) {
|
||||
// checking if color
|
||||
colorMatches.add(am);
|
||||
}
|
||||
}
|
||||
} else if (m.isAnyMana()) {
|
||||
colorMatches.add(am);
|
||||
} else {
|
||||
String[] colorsProduced;
|
||||
if (m.isComboMana()) {
|
||||
colorsProduced = m.getComboColors().split(" ");
|
||||
}
|
||||
else {
|
||||
colorsProduced = m.getOrigProduced().split(" ");
|
||||
}
|
||||
for (final String color : colorsProduced) {
|
||||
if (manaCost.isColor(color)) {
|
||||
// checking if color
|
||||
colorMatches.add(am);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (SpellAbility sa : abilities) {
|
||||
if (abilityProducesManaOfColor(sa, colorNeeded))
|
||||
colorMatches.add(sa);
|
||||
}
|
||||
|
||||
|
||||
if (colorMatches.isEmpty()) {
|
||||
// can only match colorless just grab the first and move on.
|
||||
@@ -286,7 +226,7 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I
|
||||
}
|
||||
|
||||
// save off color needed for use by any mana and reflected mana
|
||||
subchosen.getManaPart().setExpressChoice(colorsNeeded);
|
||||
subchosen.getManaPart().setExpressChoice(ColorSet.fromMask(colorCanUse));
|
||||
|
||||
// System.out.println("Chosen sa=" + chosen + " of " + chosen.getSourceCard() + " to pay mana");
|
||||
Runnable proc = new Runnable() {
|
||||
@@ -299,6 +239,30 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I
|
||||
FThreads.invokeInNewThread(proc, true);
|
||||
// EDT that removes lockUI from input stack will call our showMessage() method
|
||||
}
|
||||
|
||||
|
||||
private static boolean abilityProducesManaOfColor(SpellAbility am, byte neededColor) {
|
||||
AbilityManaPart m = am.getManaPart();
|
||||
if (am.getApi() == ApiType.ManaReflected) {
|
||||
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am);
|
||||
for (final String color : reflectableColors) {
|
||||
if ((neededColor & MagicColor.fromName(color)) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (m.isAnyMana()) {
|
||||
return true;
|
||||
} else {
|
||||
String colorsProduced = m.isComboMana() ? m.getComboColors() : m.getOrigProduced();
|
||||
for (final String color : colorsProduced.split(" ")) {
|
||||
if ((neededColor & MagicColor.fromName(color)) != 0) {
|
||||
// checking if color
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onManaAbilityPlayed(final SpellAbility saPaymentSrc) {
|
||||
if ( saPaymentSrc != null) // null comes when they've paid from pool
|
||||
|
||||
@@ -22,6 +22,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -1082,17 +1084,12 @@ public final class GameActionUtil {
|
||||
sb.append(baseMana);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
// if baseMana is an integer(colorless), just multiply amount
|
||||
// and baseMana
|
||||
final int base = Integer.parseInt(baseMana);
|
||||
sb.append(base * amount);
|
||||
} catch (final NumberFormatException e) {
|
||||
for (int i = 0; i < amount; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(baseMana);
|
||||
if(StringUtils.isNumeric(baseMana)) {
|
||||
sb.append(amount * Integer.parseInt(baseMana));
|
||||
} else {
|
||||
sb.append(baseMana);
|
||||
for (int i = 1; i < amount; i++) {
|
||||
sb.append(" ").append(baseMana);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public class ComputerUtil {
|
||||
if (unless != null && !unless.endsWith(">")) {
|
||||
final int amount = AbilityUtils.calculateAmount(source, unless, sa);
|
||||
|
||||
final int usableManaSources = ComputerUtilCard.getUsableManaSources(ai.getOpponent());
|
||||
final int usableManaSources = ComputerUtilMana.getAvailableMana(ai.getOpponent(), true).size();
|
||||
|
||||
// If the Unless isn't enough, this should be less likely to be used
|
||||
if (amount > usableManaSources) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.CardPrinted;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
@@ -784,31 +783,6 @@ public class ComputerUtilCard {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getUsableManaSources.
|
||||
* </p>
|
||||
*
|
||||
* @param player
|
||||
* a {@link forge.game.player.Player} object.
|
||||
* @return a int.
|
||||
*/
|
||||
public static int getUsableManaSources(final Player player) {
|
||||
List<Card> list = CardLists.filter(player.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
for (final SpellAbility am : ComputerUtilMana.getAIPlayableMana(c)) {
|
||||
am.setActivatingPlayer(player);
|
||||
if (am.canPlay()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package forge.game.ai;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.CardUtil;
|
||||
@@ -19,6 +21,7 @@ import forge.card.ability.ApiType;
|
||||
import forge.card.cardfactory.CardFactoryUtil;
|
||||
import forge.card.cost.Cost;
|
||||
import forge.card.cost.CostPayment;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostBeingPaid;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
@@ -30,6 +33,10 @@ import forge.card.spellability.SpellAbility;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.maps.CollectionSuppliers;
|
||||
import forge.util.maps.EnumMapOfLists;
|
||||
import forge.util.maps.MapOfLists;
|
||||
import forge.util.maps.TreeMapOfLists;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
@@ -59,206 +66,286 @@ public class ComputerUtilMana {
|
||||
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, test, extraMana);
|
||||
|
||||
final Card card = sa.getSourceCard();
|
||||
// Make mana needed to avoid negative effect a mandatory cost for the AI
|
||||
|
||||
final String[] negEffects = card.getSVar("ManaNeededToAvoidNegativeEffect").split(",");
|
||||
int amountAdded = 0;
|
||||
for (int nStr = 0; nStr < negEffects.length; nStr++) {
|
||||
// convert long color strings to short color strings
|
||||
if (negEffects[nStr].length() > 1) {
|
||||
negEffects[nStr] = MagicColor.toShortString(negEffects[nStr]);
|
||||
}
|
||||
// make mana mandatory for AI
|
||||
if (!cost.isColor(negEffects[nStr]) && cost.getColorlessManaAmount() > amountAdded) {
|
||||
cost.combineManaCost(negEffects[nStr]);
|
||||
amountAdded++;
|
||||
}
|
||||
|
||||
adjustManaCostToAvoidNegEffects(cost, card);
|
||||
|
||||
final ManaPool manapool = ai.getManaPool();
|
||||
List<ManaCostShard> unpaidShards = cost.getUnpaidShards();
|
||||
Collections.sort(unpaidShards); // most difficult shards must come first
|
||||
for(ManaCostShard part : unpaidShards) {
|
||||
if( part != ManaCostShard.X)
|
||||
manapool.payManaFromPool(sa, cost, part);
|
||||
}
|
||||
|
||||
// TODO: should it be an error condition if amountAdded is greater
|
||||
// than the colorless in the original cost? (ArsenalNut - 120102)
|
||||
// adjust colorless amount to account for added mana
|
||||
cost.decreaseColorlessMana(amountAdded);
|
||||
|
||||
|
||||
final ManaPool manapool = ai.getManaPool();
|
||||
|
||||
manapool.payManaFromPool(sa, cost);
|
||||
|
||||
if (cost.isPaid()) {
|
||||
// refund any mana taken from mana pool when test
|
||||
manapool.clearManaPaid(sa, test);
|
||||
return true;
|
||||
}
|
||||
|
||||
// get map of mana abilities
|
||||
final Map<String, List<SpellAbility>> manaAbilityMap = ComputerUtilMana.mapManaSources(ai, checkPlayable);
|
||||
// initialize ArrayList list for mana needed
|
||||
final List<List<SpellAbility>> partSources = new ArrayList<List<SpellAbility>>();
|
||||
final List<Integer> partPriority = new ArrayList<Integer>();
|
||||
final String[] costParts = cost.toString().replace("X ", "").split(" ");
|
||||
boolean foundAllSources = ComputerUtilMana.findManaSources(ai, manaAbilityMap, partSources, partPriority, costParts);
|
||||
if (!foundAllSources) {
|
||||
if (!test) {
|
||||
// real payment should not arrive here
|
||||
throw new RuntimeException("ComputerUtil : payManaCost() cost was not paid for " + sa.getSourceCard());
|
||||
}
|
||||
manapool.clearManaPaid(sa, test); // refund any mana taken from mana pool
|
||||
// arrange all mana abilities by color produced.
|
||||
final MapOfLists<Integer, SpellAbility> manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, checkPlayable);
|
||||
if ( manaAbilityMap.isEmpty() ) {
|
||||
manapool.clearManaPaid(sa, test);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create array to keep track of sources used
|
||||
final ArrayList<Card> usedSources = new ArrayList<Card>();
|
||||
// this is to prevent errors for mana sources that have abilities that
|
||||
// cost mana.
|
||||
usedSources.add(sa.getSourceCard());
|
||||
|
||||
// select which abilities may be used for each shard
|
||||
Map<ManaCostShard, Collection<SpellAbility>> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost);
|
||||
|
||||
// Loop over mana needed
|
||||
int nPriority = 0;
|
||||
List<String> negEffectPaid = new ArrayList<String>();
|
||||
while (nPriority < partPriority.size()) {
|
||||
final int nPart = partPriority.get(nPriority);
|
||||
final ManaCostBeingPaid costPart = new ManaCostBeingPaid(costParts[nPart]);
|
||||
// Loop over mana abilities that can be used to current mana cost part
|
||||
for (final SpellAbility ma : partSources.get(nPart)) {
|
||||
final Card sourceCard = ma.getSourceCard();
|
||||
|
||||
// Check if source has already been used
|
||||
if (usedSources.contains(sourceCard)) {
|
||||
continue;
|
||||
ManaCostShard toPay = null;
|
||||
while (!cost.isPaid()) {
|
||||
toPay = getNextShardToPay(cost, sourcesForShards);
|
||||
|
||||
Collection<SpellAbility> saList = sourcesForShards.get(toPay);
|
||||
List<SpellAbility> payableSources = new ArrayList<SpellAbility>();
|
||||
if( saList != null ) {
|
||||
for (final SpellAbility ma : saList) {
|
||||
if( canPayShardWithSpellAbility(toPay, ai, ma, sa, checkPlayable || !test ) ) {
|
||||
payableSources.add(ma);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if AI can still play this mana ability
|
||||
ma.setActivatingPlayer(ai);
|
||||
// if the AI can't pay the additional costs skip the mana ability
|
||||
if (ma.getPayCosts() != null && checkPlayable) {
|
||||
if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) {
|
||||
}
|
||||
|
||||
if( payableSources.isEmpty() ) {
|
||||
if(!toPay.isPhyrexian() || !ai.canPayLife(2))
|
||||
break; // cannot pay
|
||||
|
||||
cost.payPhyrexian();
|
||||
if( !test )
|
||||
ai.payLife(2, sa.getSourceCard());
|
||||
continue;
|
||||
}
|
||||
|
||||
// choose the best SA.
|
||||
SpellAbility saPayment = payableSources.get(0);
|
||||
|
||||
setExpressColorChoice(sa, ai, cost, toPay, saPayment);
|
||||
|
||||
if ( test ) {
|
||||
String manaProduced = toPay.isSnow() ? "S" : GameActionUtil.generatedMana(saPayment);
|
||||
//System.out.println(manaProduced);
|
||||
/* String remainder = */ cost.payMultipleMana(manaProduced);
|
||||
// add it to mana pool?
|
||||
|
||||
// remove from available lists
|
||||
for(Collection<SpellAbility> kv : sourcesForShards.values()) {
|
||||
kv.remove(saPayment);
|
||||
}
|
||||
} else {
|
||||
if (saPayment.getPayCosts() != null) {
|
||||
final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment);
|
||||
if (!pay.payComputerCosts(ai, ai.getGame())) {
|
||||
continue;
|
||||
}
|
||||
} else if (sourceCard.isTapped() && checkPlayable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AbilityManaPart m = ma.getManaPart();
|
||||
// Check for mana restrictions
|
||||
if (!m.meetsManaRestrictions(sa)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String manaProduced;
|
||||
// Check if paying snow mana
|
||||
if ("S".equals(costParts[nPart])) {
|
||||
manaProduced = "S";
|
||||
} else {
|
||||
if (m.isComboMana()) {
|
||||
String colorChoice = costParts[nPart];
|
||||
m.setExpressChoice(colorChoice);
|
||||
colorChoice = ComputerUtilMana.getComboManaChoice(ai, ma, sa, cost);
|
||||
m.setExpressChoice(colorChoice);
|
||||
} else if (ma.getApi() == ApiType.ManaReflected) {
|
||||
if (CardUtil.getReflectableManaColors(ma, ma, new HashSet<String>(), new ArrayList<Card>()).contains(MagicColor.toLongString(costParts[nPart]))) {
|
||||
m.setExpressChoice(costParts[nPart]);
|
||||
} else {
|
||||
m.setExpressChoice("0");
|
||||
}
|
||||
}
|
||||
// check if ability produces any color
|
||||
else if (m.isAnyMana()) {
|
||||
String colorChoice = costParts[nPart];
|
||||
// Check for
|
||||
// 1) Colorless
|
||||
// 2) Split e.g. 2/G
|
||||
// 3) Hybrid e.g. UG
|
||||
if (costParts[nPart].matches("[0-9]+")) {
|
||||
colorChoice = "W";
|
||||
for (int n = 0; n < negEffects.length; n++) {
|
||||
if (!negEffectPaid.contains(negEffects[n])) {
|
||||
colorChoice = negEffects[n];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (costParts[nPart].contains("2/")) {
|
||||
colorChoice = costParts[nPart].replace("2/", "");
|
||||
} else if (costParts[nPart].length() > 1) {
|
||||
colorChoice = costParts[nPart].substring(0, 1);
|
||||
for (int n = 0; n < negEffects.length; n++) {
|
||||
if (costParts[nPart].contains(negEffects[n]) && !negEffectPaid.contains(negEffects[n])) {
|
||||
colorChoice = negEffects[n];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m.setExpressChoice(colorChoice);
|
||||
}
|
||||
// get produced mana
|
||||
manaProduced = GameActionUtil.generatedMana(ma);
|
||||
if (manaProduced.matches("0")) {
|
||||
continue;
|
||||
}
|
||||
System.err.println("Ability " + saPayment + " from " + saPayment.getSourceCard() + " had NULL as payCost");
|
||||
saPayment.getSourceCard().tap();
|
||||
}
|
||||
|
||||
// add source card to used list
|
||||
usedSources.add(sourceCard);
|
||||
negEffectPaid.add(manaProduced);
|
||||
costPart.payMultipleMana(manaProduced);
|
||||
|
||||
if (!test) {
|
||||
// Pay additional costs
|
||||
if (ma.getPayCosts() != null) {
|
||||
final CostPayment pay = new CostPayment(ma.getPayCosts(), ma);
|
||||
if (!pay.payComputerCosts(ai, ai.getGame())) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
sourceCard.tap();
|
||||
}
|
||||
// resolve mana ability
|
||||
//ma.resolve();
|
||||
AbilityUtils.resolve(ma, false);
|
||||
// subtract mana from mana pool
|
||||
manapool.payManaFromAbility(sa, cost, ma);
|
||||
} else {
|
||||
cost.payMultipleMana(manaProduced);
|
||||
|
||||
AbilityUtils.resolve(saPayment, false);
|
||||
// subtract mana from mana pool
|
||||
manapool.payManaFromAbility(sa, cost, saPayment);
|
||||
|
||||
// no need remove abilities from resource map, once their costs are paid and consume resources, they can not be used again
|
||||
}
|
||||
}
|
||||
|
||||
manapool.clearManaPaid(sa, test);
|
||||
if(!cost.isPaid()) {
|
||||
if( test )
|
||||
return false;
|
||||
else
|
||||
throw new RuntimeException("ComputerUtil : payManaCost() cost was not paid for " + sa.getSourceCard().getName() + ". Didn't find what to pay for " + toPay);
|
||||
}
|
||||
|
||||
|
||||
// if (sa instanceof Spell_Permanent) // should probably add this
|
||||
sa.getSourceCard().setColorsPaid(cost.getColorsPaid());
|
||||
sa.getSourceCard().setSunburstValue(cost.getSunburst());
|
||||
return true;
|
||||
} // payManaCost()
|
||||
|
||||
|
||||
private static void setExpressColorChoice(final SpellAbility sa, final Player ai, ManaCostBeingPaid cost,
|
||||
ManaCostShard toPay, SpellAbility saPayment) {
|
||||
if ( saPayment.getManaPart().isComboMana() )
|
||||
getComboManaChoice(ai, saPayment, sa, cost);
|
||||
else if (saPayment.getApi() == ApiType.ManaReflected) {
|
||||
System.out.println("Evaluate reflected mana of: " + saPayment.getSourceCard());
|
||||
Set<String> reflected = CardUtil.getReflectableManaColors(saPayment);
|
||||
|
||||
for(byte c : MagicColor.WUBRG) {
|
||||
if (toPay.canBePaidWithManaOfColor(c) && reflected.contains(MagicColor.toLongString(c))) {
|
||||
saPayment.getManaPart().setExpressChoice(MagicColor.toShortString(c));
|
||||
return;
|
||||
}
|
||||
// check if cost part is paid
|
||||
if (costPart.isPaid() || cost.isPaid()) {
|
||||
}
|
||||
} else if ( saPayment.getManaPart().isAnyMana()) {
|
||||
byte colorChoice = 0;
|
||||
if (toPay.isOr2Colorless())
|
||||
colorChoice = toPay.getColorMask();
|
||||
else for( byte c : MagicColor.WUBRG ) {
|
||||
if ( toPay.canBePaidWithManaOfColor(c)) {
|
||||
colorChoice = c;
|
||||
break;
|
||||
}
|
||||
} // end of mana ability loop
|
||||
|
||||
if (!costPart.isPaid() || cost.isPaid()) {
|
||||
break;
|
||||
} else {
|
||||
nPriority++;
|
||||
}
|
||||
} // end of cost parts loop
|
||||
|
||||
//check for phyrexian mana
|
||||
if (!cost.isPaid() && cost.containsPhyrexianMana() && ai.getLife() > 5 && ai.canPayLife(2)) {
|
||||
cost.payPhyrexian();
|
||||
if (!test) {
|
||||
ai.payLife(2, sa.getSourceCard());
|
||||
saPayment.getManaPart().setExpressChoice(MagicColor.toShortString(colorChoice));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canPayShardWithSpellAbility(ManaCostShard toPay, Player ai, SpellAbility ma, SpellAbility sa, boolean checkCosts) {
|
||||
final Card sourceCard = ma.getSourceCard();
|
||||
|
||||
if (toPay.isSnow() && !sourceCard.isSnow() ) return false;
|
||||
|
||||
AbilityManaPart m = ma.getManaPart();
|
||||
if (!m.meetsManaRestrictions(sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( checkCosts ) {
|
||||
// Check if AI can still play this mana ability
|
||||
ma.setActivatingPlayer(ai);
|
||||
if (ma.getPayCosts() != null ) { // if the AI can't pay the additional costs skip the mana ability
|
||||
if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) {
|
||||
return false;
|
||||
}
|
||||
} else if (sourceCard.isTapped() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
manapool.clearManaPaid(sa, test);
|
||||
// check if paid
|
||||
if (cost.isPaid()) {
|
||||
// if (sa instanceof Spell_Permanent) // should probably add this
|
||||
sa.getSourceCard().setColorsPaid(cost.getColorsPaid());
|
||||
sa.getSourceCard().setSunburstValue(cost.getSunburst());
|
||||
return true;
|
||||
|
||||
if (m.isComboMana()) {
|
||||
for(String s : m.getComboColors().split(" ")) {
|
||||
if ( "Any".equals(s) || toPay.canBePaidWithManaOfColor(MagicColor.fromName(s)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
} else if (ma.getApi() == ApiType.ManaReflected) {
|
||||
Set<String> reflected = CardUtil.getReflectableManaColors(ma);
|
||||
|
||||
for(byte c : MagicColor.WUBRG) {
|
||||
if (toPay.canBePaidWithManaOfColor(c) && reflected.contains(MagicColor.toLongString(c))) {
|
||||
m.setExpressChoice(MagicColor.toShortString(c));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!test) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("ComputerUtil : payManaCost() cost was not paid for ");
|
||||
sb.append(sa.getSourceCard().getName());
|
||||
throw new RuntimeException(sb.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost, Map<ManaCostShard, Collection<SpellAbility>> sourcesForShards) {
|
||||
// mind the priorities
|
||||
// * Pay mono-colored first,
|
||||
// * Pay 2/C with matching colors
|
||||
// * pay hybrids
|
||||
// * pay phyrexian, keep mana for colorless
|
||||
// * pay colorless
|
||||
|
||||
for(ManaCostShard s : cost.getDistinctShards()) { // should check in which order EnumMap enumerates keys. If it's same as enum member declaration, nothing else needs to be done.
|
||||
return s;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void adjustManaCostToAvoidNegEffects(ManaCostBeingPaid cost, final Card card) {
|
||||
// Make mana needed to avoid negative effect a mandatory cost for the AI
|
||||
for (String manaPart : card.getSVar("ManaNeededToAvoidNegativeEffect").split(",")) {
|
||||
// convert long color strings to short color strings
|
||||
byte mask = MagicColor.fromName(manaPart);
|
||||
|
||||
// make mana mandatory for AI
|
||||
if (!cost.needsColor(mask) && cost.getColorlessManaAmount() > 0) {
|
||||
ManaCostShard shard = ManaCostShard.valueOf(mask);
|
||||
cost.increaseShard(shard, 1);
|
||||
cost.decreaseColorlessMana(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getComboManaChoice.
|
||||
* </p>
|
||||
*
|
||||
* @param abMana
|
||||
* a {@link forge.card.spellability.AbilityMana} object.
|
||||
* @param saRoot
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
* @param cost
|
||||
* a {@link forge.card.mana.ManaCostBeingPaid} object.
|
||||
* @return String
|
||||
*/
|
||||
private static void getComboManaChoice(final Player ai, final SpellAbility manaAb, final SpellAbility saRoot, final ManaCostBeingPaid cost) {
|
||||
|
||||
return false;
|
||||
final StringBuilder choiceString = new StringBuilder();
|
||||
final Card source = manaAb.getSourceCard();
|
||||
final AbilityManaPart abMana = manaAb.getManaPart();
|
||||
|
||||
} // payManaCost()
|
||||
if (abMana.isComboMana()) {
|
||||
int amount = manaAb.hasParam("Amount") ? AbilityUtils.calculateAmount(source, manaAb.getParam("Amount"), saRoot) : 1;
|
||||
final ManaCostBeingPaid testCost = new ManaCostBeingPaid(cost.toString().replace("X ", ""));
|
||||
final String[] comboColors = abMana.getComboColors().split(" ");
|
||||
for (int nMana = 1; nMana <= amount; nMana++) {
|
||||
String choice = "";
|
||||
// Use expressChoice first
|
||||
if (!abMana.getExpressChoice().isEmpty()) {
|
||||
choice = abMana.getExpressChoice();
|
||||
abMana.clearExpressChoice();
|
||||
byte colorMask = MagicColor.fromName(choice);
|
||||
if (abMana.canProduce(choice) && testCost.isAnyPartPayableWith(colorMask)) {
|
||||
choiceString.append(choice);
|
||||
testCost.payMultipleMana(choice);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// check colors needed for cost
|
||||
if (!testCost.isPaid()) {
|
||||
// Loop over combo colors
|
||||
for (String color : comboColors) {
|
||||
if (testCost.isAnyPartPayableWith(MagicColor.fromName(color))) {
|
||||
testCost.payMultipleMana(color);
|
||||
if (nMana != 1) {
|
||||
choiceString.append(" ");
|
||||
}
|
||||
choiceString.append(color);
|
||||
choice = color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!choice.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// check if combo mana can produce most common color in hand
|
||||
String commonColor = ComputerUtilCard.getMostProminentColor(ai.getCardsIn(
|
||||
ZoneType.Hand));
|
||||
if (!commonColor.isEmpty() && abMana.getComboColors().contains(MagicColor.toShortString(commonColor))) {
|
||||
choice = MagicColor.toShortString(commonColor);
|
||||
}
|
||||
else {
|
||||
// default to first color
|
||||
choice = comboColors[0];
|
||||
}
|
||||
if (nMana != 1) {
|
||||
choiceString.append(" ");
|
||||
}
|
||||
choiceString.append(choice);
|
||||
}
|
||||
}
|
||||
if (choiceString.toString().isEmpty()) {
|
||||
choiceString.append("0");
|
||||
}
|
||||
|
||||
abMana.setExpressChoice(choiceString.toString());
|
||||
}
|
||||
|
||||
|
||||
// TODO: this code is disconnected now, it was moved here from MagicStack, where X cost is not processed any more
|
||||
public static void computerPayX(final SpellAbility sa, Player player, int xCost) {
|
||||
@@ -297,134 +384,37 @@ public class ComputerUtilMana {
|
||||
* @param foundAllSources
|
||||
* @return Were all mana sources found?
|
||||
*/
|
||||
private static boolean findManaSources(final Player ai, final Map<String, List<SpellAbility>> manaAbilityMap,
|
||||
final List<List<SpellAbility>> partSources, final List<Integer> partPriority,
|
||||
final String[] costParts) {
|
||||
final String[] shortColors = { "W", "U", "B", "R", "G" };
|
||||
boolean foundAllSources;
|
||||
if (manaAbilityMap.isEmpty()) {
|
||||
foundAllSources = false;
|
||||
} else {
|
||||
foundAllSources = true;
|
||||
// loop over cost parts
|
||||
for (int nPart = 0; nPart < costParts.length; nPart++) {
|
||||
final List<SpellAbility> srcFound = new ArrayList<SpellAbility>();
|
||||
// Test for:
|
||||
// 1) Colorless
|
||||
// 2) Split e.g. 2/G
|
||||
// 3) Hybrid e.g. U/G
|
||||
// defaults to single short color
|
||||
if (costParts[nPart].matches("[0-9]+")) { // Colorless
|
||||
srcFound.addAll(manaAbilityMap.get("1"));
|
||||
} else if (costParts[nPart].contains("2/")) { // Split
|
||||
final String colorKey = costParts[nPart].replace("2/", "");
|
||||
// add specified color sources first
|
||||
if (manaAbilityMap.containsKey(colorKey)) {
|
||||
srcFound.addAll(manaAbilityMap.get(colorKey));
|
||||
}
|
||||
// add other available colors
|
||||
for (final String color : shortColors) {
|
||||
if (!colorKey.contains(color)) {
|
||||
// Is source available?
|
||||
if (manaAbilityMap.containsKey(color)) {
|
||||
srcFound.addAll(manaAbilityMap.get(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (costParts[nPart].contains("P")) { // Phyrexian
|
||||
String newPart = costParts[nPart].replace("/P", "");
|
||||
if (manaAbilityMap.containsKey(newPart)) {
|
||||
srcFound.addAll(manaAbilityMap.get(newPart));
|
||||
} else if (ai.getLife() > 8) { //Pay with life
|
||||
partSources.add(nPart, srcFound);
|
||||
continue;
|
||||
}
|
||||
} else if (costParts[nPart].length() > 1) { // Hybrid
|
||||
final String firstColor = costParts[nPart].substring(0, 1);
|
||||
final String secondColor = costParts[nPart].substring(2);
|
||||
final boolean foundFirst = manaAbilityMap.containsKey(firstColor);
|
||||
final boolean foundSecond = manaAbilityMap.containsKey(secondColor);
|
||||
if (foundFirst || foundSecond) {
|
||||
if (!foundFirst) {
|
||||
srcFound.addAll(manaAbilityMap.get(secondColor));
|
||||
} else if (!foundSecond) {
|
||||
srcFound.addAll(manaAbilityMap.get(firstColor));
|
||||
} else if (manaAbilityMap.get(firstColor).size() > manaAbilityMap.get(secondColor).size()) {
|
||||
srcFound.addAll(manaAbilityMap.get(firstColor));
|
||||
srcFound.addAll(manaAbilityMap.get(secondColor));
|
||||
} else {
|
||||
srcFound.addAll(manaAbilityMap.get(secondColor));
|
||||
srcFound.addAll(manaAbilityMap.get(firstColor));
|
||||
}
|
||||
}
|
||||
} else { // single color
|
||||
if (manaAbilityMap.containsKey(costParts[nPart])) {
|
||||
srcFound.addAll(manaAbilityMap.get(costParts[nPart]));
|
||||
}
|
||||
}
|
||||
|
||||
// add sources to array lists
|
||||
partSources.add(nPart, srcFound);
|
||||
// add to sorted priority list
|
||||
if (srcFound.size() > 0) {
|
||||
int i;
|
||||
for (i = 0; i < partPriority.size(); i++) {
|
||||
if (srcFound.size() <= partSources.get(i).size()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
partPriority.add(i, nPart);
|
||||
} else {
|
||||
foundAllSources = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundAllSources;
|
||||
}
|
||||
private static MapOfLists<ManaCostShard, SpellAbility> groupAndOrderToPayShards(final Player ai, final MapOfLists<Integer, SpellAbility> manaAbilityMap, final ManaCostBeingPaid cost) {
|
||||
MapOfLists<ManaCostShard, SpellAbility> res = new EnumMapOfLists<ManaCostShard, SpellAbility>(ManaCostShard.class, CollectionSuppliers.<SpellAbility>hashSets());
|
||||
|
||||
private static void increaseManaCostByX(SpellAbility sa, ManaCostBeingPaid cost, int xValue) {
|
||||
String manaXColor = sa.hasParam("XColor") ? sa.getParam("XColor") : "";
|
||||
if (manaXColor.isEmpty()) {
|
||||
cost.increaseColorlessMana(xValue);
|
||||
} else {
|
||||
if (manaXColor.equals("B")) {
|
||||
cost.increaseShard(ManaCostShard.BLACK, xValue);
|
||||
} else if (manaXColor.equals("G")) {
|
||||
cost.increaseShard(ManaCostShard.GREEN, xValue);
|
||||
} else if (manaXColor.equals("R")) {
|
||||
cost.increaseShard(ManaCostShard.RED, xValue);
|
||||
} else if (manaXColor.equals("U")) {
|
||||
cost.increaseShard(ManaCostShard.BLUE, xValue);
|
||||
} else if (manaXColor.equals("W")) {
|
||||
cost.increaseShard(ManaCostShard.WHITE, xValue);
|
||||
|
||||
/* Max: Never seen these options in real cards
|
||||
} else if (manaXColor.contains("B") && manaXColor.contains("G")) {
|
||||
cost.increaseShard(ManaCostShard.BG, xValue);
|
||||
} else if (manaXColor.contains("B") && manaXColor.contains("R")) {
|
||||
cost.increaseShard(ManaCostShard.BR, xValue);
|
||||
} else if (manaXColor.contains("R") && manaXColor.contains("G")) {
|
||||
cost.increaseShard(ManaCostShard.RG, xValue);
|
||||
} else if (manaXColor.contains("U") && manaXColor.contains("B")) {
|
||||
cost.increaseShard(ManaCostShard.UB, xValue);
|
||||
} else if (manaXColor.contains("U") && manaXColor.contains("G")) {
|
||||
cost.increaseShard(ManaCostShard.UG, xValue);
|
||||
} else if (manaXColor.contains("U") && manaXColor.contains("R")) {
|
||||
cost.increaseShard(ManaCostShard.UR, xValue);
|
||||
} else if (manaXColor.contains("W") && manaXColor.contains("B")) {
|
||||
cost.increaseShard(ManaCostShard.WB, xValue);
|
||||
} else if (manaXColor.contains("W") && manaXColor.contains("G")) {
|
||||
cost.increaseShard(ManaCostShard.WG, xValue);
|
||||
} else if (manaXColor.contains("W") && manaXColor.contains("R")) {
|
||||
cost.increaseShard(ManaCostShard.WR, xValue);
|
||||
} else if (manaXColor.contains("W") && manaXColor.contains("U")) {
|
||||
cost.increaseShard(ManaCostShard.WU, xValue);
|
||||
*/
|
||||
}
|
||||
// loop over cost parts
|
||||
for (ManaCostShard shard : cost.getDistinctShards() ) {
|
||||
if ( shard == ManaCostShard.S ) {
|
||||
res.put(shard, manaAbilityMap.get(ManaAtom.IS_SNOW));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (shard.isOr2Colorless()) {
|
||||
Integer colorKey = Integer.valueOf(shard.getColorMask());
|
||||
if (manaAbilityMap.containsKey(colorKey) )
|
||||
res.addAll(shard, manaAbilityMap.get(colorKey));
|
||||
if (manaAbilityMap.containsKey(ManaAtom.COLORLESS) )
|
||||
res.addAll(shard, manaAbilityMap.get(ManaAtom.COLORLESS));
|
||||
continue;
|
||||
}
|
||||
|
||||
for(Entry<Integer, Collection<SpellAbility>> kv : manaAbilityMap.entrySet()) {
|
||||
if( shard.canBePaidWithManaOfColor(kv.getKey().byteValue()) )
|
||||
res.addAll(shard, kv.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (cost.getColorlessManaAmount() > 0 && manaAbilityMap.containsKey(ManaAtom.COLORLESS))
|
||||
res.addAll(ManaCostShard.COLORLESS, manaAbilityMap.get(ManaAtom.COLORLESS));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the ManaCost for the given SpellAbility.
|
||||
* @param sa
|
||||
@@ -459,7 +449,9 @@ public class ComputerUtilMana {
|
||||
}
|
||||
}
|
||||
|
||||
increaseManaCostByX(sa, cost, manaToAdd);
|
||||
String manaXColor = sa.getParam("XColor");
|
||||
ManaCostShard shardToGrow = ManaCostShard.parseNonGeneric(manaXColor == null ? "1" : manaXColor);
|
||||
cost.increaseShard(shardToGrow, manaToAdd);
|
||||
|
||||
if (!test) {
|
||||
card.setXManaCostPaid(manaToAdd / cost.getXcounter());
|
||||
@@ -470,24 +462,19 @@ public class ComputerUtilMana {
|
||||
}
|
||||
|
||||
//This method is currently used by AI to estimate human's available mana
|
||||
private static List<Card> getAvailableMana(final Player ai, final boolean checkPlayable) {
|
||||
public static List<Card> getAvailableMana(final Player ai, final boolean checkPlayable) {
|
||||
final List<Card> list = ai.getCardsIn(ZoneType.Battlefield);
|
||||
list.addAll(ai.getCardsIn(ZoneType.Hand));
|
||||
final List<Card> manaSources = CardLists.filter(list, new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(final Card c) {
|
||||
if (checkPlayable) {
|
||||
for (final SpellAbility am : getAIPlayableMana(c)) {
|
||||
am.setActivatingPlayer(ai);
|
||||
if (am.canPlay()) {
|
||||
return true;
|
||||
}
|
||||
for (final SpellAbility am : getAIPlayableMana(c)) {
|
||||
am.setActivatingPlayer(ai);
|
||||
if (am.canPlay() || !checkPlayable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}); // CardListFilter
|
||||
|
||||
@@ -507,8 +494,7 @@ public class ComputerUtilMana {
|
||||
// 2. Search for mana sources that have a certain number of abilities
|
||||
// 3. Use lands that produce any color many
|
||||
// 4. all other sources (creature, costs, drawback, etc.)
|
||||
for (int i = 0; i < manaSources.size(); i++) {
|
||||
final Card card = manaSources.get(i);
|
||||
for (Card card : manaSources) {
|
||||
|
||||
if (card.isCreature() || card.isEnchanted()) {
|
||||
otherManaSources.add(card);
|
||||
@@ -588,27 +574,12 @@ public class ComputerUtilMana {
|
||||
|
||||
|
||||
//This method is currently used by AI to estimate mana available to human
|
||||
private static Map<String, List<SpellAbility>> mapManaSources(final Player ai, boolean checkPlayable) {
|
||||
final Map<String, List<SpellAbility>> manaMap = new HashMap<String, List<SpellAbility>>();
|
||||
|
||||
final List<SpellAbility> whiteSources = new ArrayList<SpellAbility>();
|
||||
final List<SpellAbility> blueSources = new ArrayList<SpellAbility>();
|
||||
final List<SpellAbility> blackSources = new ArrayList<SpellAbility>();
|
||||
final List<SpellAbility> redSources = new ArrayList<SpellAbility>();
|
||||
final List<SpellAbility> greenSources = new ArrayList<SpellAbility>();
|
||||
final List<SpellAbility> colorlessSources = new ArrayList<SpellAbility>();
|
||||
final List<SpellAbility> snowSources = new ArrayList<SpellAbility>();
|
||||
|
||||
// Get list of current available mana sources
|
||||
final List<Card> manaSources = getAvailableMana(ai, checkPlayable);
|
||||
|
||||
// Loop over all mana sources
|
||||
for (int i = 0; i < manaSources.size(); i++) {
|
||||
final Card sourceCard = manaSources.get(i);
|
||||
final ArrayList<SpellAbility> manaAbilities = getAIPlayableMana(sourceCard);
|
||||
|
||||
// Loop over all mana abilities for a source
|
||||
for (final SpellAbility m : manaAbilities) {
|
||||
private static MapOfLists<Integer, SpellAbility> groupSourcesByManaColor(final Player ai, boolean checkPlayable) {
|
||||
final MapOfLists<Integer, SpellAbility> manaMap = new TreeMapOfLists<Integer, SpellAbility>(CollectionSuppliers.<SpellAbility>arrayLists());
|
||||
|
||||
// Loop over all current available mana sources
|
||||
for (final Card sourceCard : getAvailableMana(ai, checkPlayable)) {
|
||||
for (final SpellAbility m : getAIPlayableMana(sourceCard)) {
|
||||
m.setActivatingPlayer(ai);
|
||||
if (!m.canPlay() && checkPlayable) {
|
||||
continue;
|
||||
@@ -621,60 +592,32 @@ public class ComputerUtilMana {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// add to colorless source list
|
||||
colorlessSources.add(m);
|
||||
|
||||
|
||||
manaMap.add(ManaAtom.COLORLESS, m); // add to colorless source list
|
||||
|
||||
Set<String> reflectedColors = CardUtil.getReflectableManaColors(m);
|
||||
// find possible colors
|
||||
if (m.getManaPart().canProduce("W")
|
||||
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.WHITE)) {
|
||||
whiteSources.add(m);
|
||||
if (m.getManaPart().canProduce("W") || reflectedColors.contains(Constant.Color.WHITE)) {
|
||||
manaMap.add(ManaAtom.WHITE, m);
|
||||
}
|
||||
if (m.getManaPart().canProduce("U")
|
||||
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.BLUE)) {
|
||||
blueSources.add(m);
|
||||
if (m.getManaPart().canProduce("U") || reflectedColors.contains(Constant.Color.BLUE)) {
|
||||
manaMap.add(ManaAtom.BLUE, m);
|
||||
}
|
||||
if (m.getManaPart().canProduce("B")
|
||||
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.BLACK)) {
|
||||
blackSources.add(m);
|
||||
if (m.getManaPart().canProduce("B") || reflectedColors.contains(Constant.Color.BLACK)) {
|
||||
manaMap.add(ManaAtom.BLACK, m);
|
||||
}
|
||||
if (m.getManaPart().canProduce("R")
|
||||
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.RED)) {
|
||||
redSources.add(m);
|
||||
if (m.getManaPart().canProduce("R") || reflectedColors.contains(Constant.Color.RED)) {
|
||||
manaMap.add(ManaAtom.RED, m);
|
||||
}
|
||||
if (m.getManaPart().canProduce("G")
|
||||
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.GREEN)) {
|
||||
greenSources.add(m);
|
||||
if (m.getManaPart().canProduce("G") || reflectedColors.contains(Constant.Color.GREEN)) {
|
||||
manaMap.add(ManaAtom.GREEN, m);
|
||||
}
|
||||
if (m.getManaPart().isSnow()) {
|
||||
snowSources.add(m);
|
||||
manaMap.add(ManaAtom.IS_SNOW, m);
|
||||
}
|
||||
} // end of mana abilities loop
|
||||
} // end of mana sources loop
|
||||
|
||||
// Add sources
|
||||
if (!whiteSources.isEmpty()) {
|
||||
manaMap.put("W", whiteSources);
|
||||
}
|
||||
if (!blueSources.isEmpty()) {
|
||||
manaMap.put("U", blueSources);
|
||||
}
|
||||
if (!blackSources.isEmpty()) {
|
||||
manaMap.put("B", blackSources);
|
||||
}
|
||||
if (!redSources.isEmpty()) {
|
||||
manaMap.put("R", redSources);
|
||||
}
|
||||
if (!greenSources.isEmpty()) {
|
||||
manaMap.put("G", greenSources);
|
||||
}
|
||||
if (!colorlessSources.isEmpty()) {
|
||||
manaMap.put("1", colorlessSources);
|
||||
}
|
||||
if (!snowSources.isEmpty()) {
|
||||
manaMap.put("S", snowSources);
|
||||
}
|
||||
|
||||
|
||||
return manaMap;
|
||||
}
|
||||
|
||||
@@ -704,83 +647,6 @@ public class ComputerUtilMana {
|
||||
return xMana;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getComboManaChoice.
|
||||
* </p>
|
||||
*
|
||||
* @param abMana
|
||||
* a {@link forge.card.spellability.AbilityMana} object.
|
||||
* @param saRoot
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
* @param cost
|
||||
* a {@link forge.card.mana.ManaCostBeingPaid} object.
|
||||
* @return String
|
||||
*/
|
||||
public static String getComboManaChoice(final Player ai, final SpellAbility manaAb, final SpellAbility saRoot, final ManaCostBeingPaid cost) {
|
||||
|
||||
final StringBuilder choiceString = new StringBuilder();
|
||||
|
||||
|
||||
final Card source = manaAb.getSourceCard();
|
||||
final AbilityManaPart abMana = manaAb.getManaPart();
|
||||
|
||||
if (abMana.isComboMana()) {
|
||||
int amount = manaAb.hasParam("Amount") ? AbilityUtils.calculateAmount(source, manaAb.getParam("Amount"), saRoot) : 1;
|
||||
final ManaCostBeingPaid testCost = new ManaCostBeingPaid(cost.toString().replace("X ", ""));
|
||||
final String[] comboColors = abMana.getComboColors().split(" ");
|
||||
for (int nMana = 1; nMana <= amount; nMana++) {
|
||||
String choice = "";
|
||||
// Use expressChoice first
|
||||
if (!abMana.getExpressChoice().isEmpty()) {
|
||||
choice = abMana.getExpressChoice();
|
||||
abMana.clearExpressChoice();
|
||||
if (abMana.canProduce(choice) && testCost.isNeeded(choice)) {
|
||||
choiceString.append(choice);
|
||||
testCost.payMultipleMana(choice);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// check colors needed for cost
|
||||
if (!testCost.isPaid()) {
|
||||
// Loop over combo colors
|
||||
for (String color : comboColors) {
|
||||
if (testCost.isNeeded(color)) {
|
||||
testCost.payMultipleMana(color);
|
||||
if (nMana != 1) {
|
||||
choiceString.append(" ");
|
||||
}
|
||||
choiceString.append(color);
|
||||
choice = color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!choice.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// check if combo mana can produce most common color in hand
|
||||
String commonColor = ComputerUtilCard.getMostProminentColor(ai.getCardsIn(
|
||||
ZoneType.Hand));
|
||||
if (!commonColor.isEmpty() && abMana.getComboColors().contains(MagicColor.toShortString(commonColor))) {
|
||||
choice = MagicColor.toShortString(commonColor);
|
||||
}
|
||||
else {
|
||||
// default to first color
|
||||
choice = comboColors[0];
|
||||
}
|
||||
if (nMana != 1) {
|
||||
choiceString.append(" ");
|
||||
}
|
||||
choiceString.append(choice);
|
||||
}
|
||||
}
|
||||
if (choiceString.toString().isEmpty()) {
|
||||
choiceString.append("0");
|
||||
}
|
||||
return choiceString.toString();
|
||||
}
|
||||
|
||||
// Returns basic mana abilities plus "reflected mana" abilities
|
||||
/**
|
||||
* <p>
|
||||
@@ -796,13 +662,12 @@ public class ComputerUtilMana {
|
||||
// if a mana ability has a mana cost the AI will miscalculate
|
||||
// if there is a parent ability the AI can't use it
|
||||
final Cost cost = a.getPayCosts();
|
||||
if (!cost.hasNoManaCost()
|
||||
|| (a.getApi() != ApiType.Mana && a.getApi() != ApiType.ManaReflected)) {
|
||||
if (!cost.hasNoManaCost() || (a.getApi() != ApiType.Mana && a.getApi() != ApiType.ManaReflected)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AbilityManaPart am = a.getManaPart();
|
||||
if (am.isBasic() && !res.contains(a)) {
|
||||
//AbilityManaPart am = a.getManaPart();
|
||||
if (/*am.isBasic() && */!res.contains(a)) {
|
||||
res.add(a);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
import forge.Card;
|
||||
import forge.GameEntity;
|
||||
import forge.card.mana.Mana;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.control.input.Input;
|
||||
@@ -126,4 +127,5 @@ public abstract class PlayerController {
|
||||
public abstract void playMadness(SpellAbility madness);
|
||||
public abstract List<Card> chooseCardsToDelve(int colorLessAmount, List<Card> grave);
|
||||
public abstract List<Card> chooseCardsToDiscardUnlessType(int min, List<Card> hand, String param, SpellAbility sa);
|
||||
public abstract Mana chooseManaFromPool(List<Mana> manaChoices);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.CardPredicates;
|
||||
import forge.GameEntity;
|
||||
import forge.card.mana.Mana;
|
||||
import forge.card.spellability.Spell;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.Target;
|
||||
@@ -318,4 +319,10 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mana chooseManaFromPool(List<Mana> manaChoices) {
|
||||
return manaChoices.get(0); // no brains used
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import forge.Card;
|
||||
import forge.FThreads;
|
||||
import forge.GameEntity;
|
||||
import forge.card.mana.Mana;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.card.spellability.TargetSelection;
|
||||
@@ -36,6 +37,7 @@ import forge.gui.GuiDialog;
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.item.CardPrinted;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
|
||||
/**
|
||||
@@ -500,4 +502,19 @@ public class PlayerControllerHuman extends PlayerController {
|
||||
return target.getSelected();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.game.player.PlayerController#chooseManaFromPool(java.util.List)
|
||||
*/
|
||||
@Override
|
||||
public Mana chooseManaFromPool(List<Mana> manaChoices) {
|
||||
List<String> options = new ArrayList<String>();
|
||||
for(int i = 0; i < manaChoices.size(); i++) {
|
||||
Mana m = manaChoices.get(i);
|
||||
options.add(String.format("%d. %s mana from %s", 1+i, m.getColor(), m.getSourceCard() ));
|
||||
}
|
||||
String chosen = GuiChoose.one("Pay Mana from Mana Pool", options);
|
||||
String idx = TextUtil.split(chosen, '.')[0];
|
||||
return manaChoices.get(Integer.parseInt(idx)-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -384,7 +384,7 @@ public class VField implements IVDoc<CField> {
|
||||
public void updateManaPool(final Player p0) {
|
||||
ManaPool m = p0.getManaPool();
|
||||
for(Pair<FLabel, Byte> label : manaLabels)
|
||||
label.getKey().setText(Integer.toString(m.getAmountOfColor(MagicColor.toLongString(label.getRight()))));
|
||||
label.getKey().setText(Integer.toString(m.getAmountOfColor(label.getRight())));
|
||||
}
|
||||
|
||||
//========= Retrieval methods
|
||||
|
||||
@@ -31,6 +31,8 @@ import java.util.Map.Entry;
|
||||
*/
|
||||
public class ItemPool<T extends InventoryItem> extends ItemPoolView<T> {
|
||||
|
||||
|
||||
|
||||
// Constructors here
|
||||
/**
|
||||
*
|
||||
|
||||
86
src/main/java/forge/util/maps/EnumMapToAmount.java
Normal file
86
src/main/java/forge/util/maps/EnumMapToAmount.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package forge.util.maps;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class EnumMapToAmount<T extends Enum<T>> extends EnumMap<T, Integer> implements MapToAmount<T> {
|
||||
private static final long serialVersionUID = -4749796492075359368L;
|
||||
|
||||
|
||||
public EnumMapToAmount(Class<T> keyType) {
|
||||
super(keyType);
|
||||
}
|
||||
|
||||
public EnumMapToAmount(EnumMap<T, ? extends Integer> m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
public EnumMapToAmount(Map<T, ? extends Integer> m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T item) {
|
||||
add(item, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T item, int amount) {
|
||||
if (amount <= 0) return; // throw an exception maybe?
|
||||
Integer cur = get(item);
|
||||
int newVal = cur == null ? amount : amount + cur.intValue();
|
||||
put(item, Integer.valueOf(newVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(Iterable<T> item) {
|
||||
for(T i : item) add(i, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean substract(T item) {
|
||||
return substract(item, 1);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean substract(T item, int amount) {
|
||||
Integer cur = get(item);
|
||||
if( cur == null ) return false;
|
||||
int newVal = cur.intValue() - amount;
|
||||
if(newVal > 0)
|
||||
put(item, Integer.valueOf(newVal));
|
||||
else
|
||||
remove(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void substractAll(Iterable<T> item) {
|
||||
for(T i : item) substract(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countAll() {
|
||||
int c = 0;
|
||||
for(java.util.Map.Entry<T, Integer> kv : this.entrySet()) {
|
||||
c += kv.getValue().intValue();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int count(T item) {
|
||||
Integer cur = get(item);
|
||||
return cur == null ? 0 : cur.intValue();
|
||||
}
|
||||
|
||||
}
|
||||
18
src/main/java/forge/util/maps/MapToAmount.java
Normal file
18
src/main/java/forge/util/maps/MapToAmount.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package forge.util.maps;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public interface MapToAmount<K> extends Map<K, Integer> {
|
||||
void add(K item);
|
||||
void add(K item, int amount);
|
||||
void addAll(Iterable<K> item);
|
||||
boolean substract(K item);
|
||||
boolean substract(K item, int amount);
|
||||
void substractAll(Iterable<K> item);
|
||||
int countAll();
|
||||
int count(K item); // just unboxes and returns zero instead of null
|
||||
}
|
||||
85
src/main/java/forge/util/maps/TreeMapToAmount.java
Normal file
85
src/main/java/forge/util/maps/TreeMapToAmount.java
Normal file
@@ -0,0 +1,85 @@
|
||||
package forge.util.maps;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class TreeMapToAmount<T> extends TreeMap<T, Integer> implements MapToAmount<T> {
|
||||
private static final long serialVersionUID = 5001205073403776022L;
|
||||
|
||||
public TreeMapToAmount() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TreeMapToAmount(Comparator<? super T> comparator) {
|
||||
super(comparator);
|
||||
}
|
||||
|
||||
public TreeMapToAmount(Map<? extends T, ? extends Integer> m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
public TreeMapToAmount(SortedMap<T, ? extends Integer> m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void add(T item) {
|
||||
add(item, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T item, int amount) {
|
||||
if (amount <= 0) return; // throw an exception maybe?
|
||||
Integer cur = get(item);
|
||||
int newVal = cur == null ? amount : amount + cur.intValue();
|
||||
put(item, Integer.valueOf(newVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(Iterable<T> item) {
|
||||
for(T i : item) add(i, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean substract(T item) {
|
||||
return substract(item, 1);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean substract(T item, int amount) {
|
||||
Integer cur = get(item);
|
||||
if( cur == null ) return false;
|
||||
int newVal = cur.intValue() - amount;
|
||||
if(newVal > 0)
|
||||
put(item, Integer.valueOf(newVal));
|
||||
else
|
||||
remove(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void substractAll(Iterable<T> item) {
|
||||
for(T i : item) substract(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countAll() {
|
||||
int c = 0;
|
||||
for(java.util.Map.Entry<T, Integer> kv : this.entrySet()) {
|
||||
c += kv.getValue().intValue();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int count(T item) {
|
||||
Integer cur = get(item);
|
||||
return cur == null ? 0 : cur.intValue();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user