mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Merge branch 'forcepay' into 'master'
Support mandatory mana payments Closes #2045 See merge request core-developers/forge!5972
This commit is contained in:
@@ -264,7 +264,6 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
return res.isEmpty() ? null : PaymentDecision.card(res);
|
return res.isEmpty() ? null : PaymentDecision.card(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PaymentDecision visit(CostGainLife cost) {
|
public PaymentDecision visit(CostGainLife cost) {
|
||||||
final List<Player> oppsThatCanGainLife = Lists.newArrayList();
|
final List<Player> oppsThatCanGainLife = Lists.newArrayList();
|
||||||
@@ -282,7 +281,6 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
return PaymentDecision.players(oppsThatCanGainLife);
|
return PaymentDecision.players(oppsThatCanGainLife);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PaymentDecision visit(CostMill cost) {
|
public PaymentDecision visit(CostMill cost) {
|
||||||
int c = cost.getAbilityAmount(ability);
|
int c = cost.getAbilityAmount(ability);
|
||||||
@@ -681,7 +679,6 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
toRemove += removeCounter(table, prefs, CounterEnumType.LORE, c - toRemove);
|
toRemove += removeCounter(table, prefs, CounterEnumType.LORE, c - toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO add logic to remove positive counters?
|
// TODO add logic to remove positive counters?
|
||||||
if (c > toRemove && cost.counter != null) {
|
if (c > toRemove && cost.counter != null) {
|
||||||
// TODO add logic for Ooze Flux, should probably try to make a token as big as possible
|
// TODO add logic for Ooze Flux, should probably try to make a token as big as possible
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import forge.game.zone.ZoneType;
|
|||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -67,16 +66,6 @@ public class ComputerUtilMana {
|
|||||||
return payManaCost(cost, sa, ai, test, checkPlayable, effect);
|
return payManaCost(cost, sa, ai, test, checkPlayable, effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void refundMana(List<Mana> manaSpent, Player ai, SpellAbility sa) {
|
|
||||||
if (sa.getHostCard() != null) {
|
|
||||||
sa.getHostCard().setCanCounter(true);
|
|
||||||
}
|
|
||||||
for (final Mana m : manaSpent) {
|
|
||||||
ai.getManaPool().addMana(m);
|
|
||||||
}
|
|
||||||
manaSpent.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the number of colors used for payment for Converge
|
* Return the number of colors used for payment for Converge
|
||||||
*/
|
*/
|
||||||
@@ -598,7 +587,7 @@ public class ComputerUtilMana {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get a mana of this type from floating, bail if none available
|
// get a mana of this type from floating, bail if none available
|
||||||
final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), (byte) -1, cost.getXManaCostPaidByColor());
|
final Mana mana = CostPayment.getMana(ai, part, sa, cost.getSourceRestriction(), (byte) -1, cost.getXManaCostPaidByColor());
|
||||||
if (mana != null) {
|
if (mana != null) {
|
||||||
if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana, false)) {
|
if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana, false)) {
|
||||||
manaSpentToPay.add(0, mana);
|
manaSpentToPay.add(0, mana);
|
||||||
@@ -609,18 +598,16 @@ public class ComputerUtilMana {
|
|||||||
|
|
||||||
if (cost.isPaid()) {
|
if (cost.isPaid()) {
|
||||||
// refund any mana taken from mana pool when test
|
// refund any mana taken from mana pool when test
|
||||||
refundMana(manaSpentToPay, ai, sa);
|
ManaPool.refundMana(manaSpentToPay, ai, sa);
|
||||||
|
CostPayment.handleOfferings(sa, true, cost.isPaid());
|
||||||
handleOfferingsAI(sa, true, cost.isPaid());
|
|
||||||
return manaSources;
|
return manaSources;
|
||||||
}
|
}
|
||||||
|
|
||||||
// arrange all mana abilities by color produced.
|
// arrange all mana abilities by color produced.
|
||||||
final ListMultimap<Integer, SpellAbility> manaAbilityMap = groupSourcesByManaColor(ai, true);
|
final ListMultimap<Integer, SpellAbility> manaAbilityMap = groupSourcesByManaColor(ai, true);
|
||||||
if (manaAbilityMap.isEmpty()) {
|
if (manaAbilityMap.isEmpty()) {
|
||||||
refundMana(manaSpentToPay, ai, sa);
|
ManaPool.refundMana(manaSpentToPay, ai, sa);
|
||||||
|
CostPayment.handleOfferings(sa, true, cost.isPaid());
|
||||||
handleOfferingsAI(sa, true, cost.isPaid());
|
|
||||||
return manaSources;
|
return manaSources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,9 +653,8 @@ public class ComputerUtilMana {
|
|||||||
Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
|
Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOfferingsAI(sa, true, cost.isPaid());
|
CostPayment.handleOfferings(sa, true, cost.isPaid());
|
||||||
|
ManaPool.refundMana(manaSpentToPay, ai, sa);
|
||||||
refundMana(manaSpentToPay, ai, sa);
|
|
||||||
|
|
||||||
return manaSources;
|
return manaSources;
|
||||||
} // getManaSourcesToPayCost()
|
} // getManaSourcesToPayCost()
|
||||||
@@ -681,7 +667,7 @@ public class ComputerUtilMana {
|
|||||||
List<Mana> manaSpentToPay = test ? new ArrayList<>() : sa.getPayingMana();
|
List<Mana> manaSpentToPay = test ? new ArrayList<>() : sa.getPayingMana();
|
||||||
List<SpellAbility> paymentList = Lists.newArrayList();
|
List<SpellAbility> paymentList = Lists.newArrayList();
|
||||||
|
|
||||||
if (payManaCostFromPool(cost, sa, ai, test, manaSpentToPay)) {
|
if (ManaPool.payManaCostFromPool(cost, sa, ai, test, manaSpentToPay)) {
|
||||||
return true; // paid all from floating mana
|
return true; // paid all from floating mana
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -870,7 +856,7 @@ public class ComputerUtilMana {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOfferingsAI(sa, test, cost.isPaid());
|
CostPayment.handleOfferings(sa, test, cost.isPaid());
|
||||||
|
|
||||||
// if (DEBUG_MANA_PAYMENT) {
|
// if (DEBUG_MANA_PAYMENT) {
|
||||||
// System.err.printf("%s > [%s] payment has %s (%s +%d) for (%s) %s:%n\t%s%n%n",
|
// System.err.printf("%s > [%s] payment has %s (%s +%d) for (%s) %s:%n\t%s%n%n",
|
||||||
@@ -880,7 +866,7 @@ public class ComputerUtilMana {
|
|||||||
|
|
||||||
// The cost is still unpaid, so refund the mana and report
|
// The cost is still unpaid, so refund the mana and report
|
||||||
if (!cost.isPaid()) {
|
if (!cost.isPaid()) {
|
||||||
refundMana(manaSpentToPay, ai, sa);
|
ManaPool.refundMana(manaSpentToPay, ai, sa);
|
||||||
if (test) {
|
if (test) {
|
||||||
resetPayment(paymentList);
|
resetPayment(paymentList);
|
||||||
return false;
|
return false;
|
||||||
@@ -891,7 +877,7 @@ public class ComputerUtilMana {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (test) {
|
if (test) {
|
||||||
refundMana(manaSpentToPay, ai, sa);
|
ManaPool.refundMana(manaSpentToPay, ai, sa);
|
||||||
resetPayment(paymentList);
|
resetPayment(paymentList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,8 +915,8 @@ public class ComputerUtilMana {
|
|||||||
final ListMultimap<Integer, SpellAbility> manaAbilityMap = groupSourcesByManaColor(ai, checkPlayable);
|
final ListMultimap<Integer, SpellAbility> manaAbilityMap = groupSourcesByManaColor(ai, checkPlayable);
|
||||||
if (manaAbilityMap.isEmpty()) {
|
if (manaAbilityMap.isEmpty()) {
|
||||||
// no mana abilities, bailing out
|
// no mana abilities, bailing out
|
||||||
refundMana(manaSpentToPay, ai, sa);
|
ManaPool.refundMana(manaSpentToPay, ai, sa);
|
||||||
handleOfferingsAI(sa, test, cost.isPaid());
|
CostPayment.handleOfferings(sa, test, cost.isPaid());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (DEBUG_MANA_PAYMENT) {
|
if (DEBUG_MANA_PAYMENT) {
|
||||||
@@ -968,156 +954,6 @@ public class ComputerUtilMana {
|
|||||||
return sourcesForShards;
|
return sourcesForShards;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the given mana cost can be paid from floating mana.
|
|
||||||
* @param cost mana cost to pay for
|
|
||||||
* @param sa ability to pay for
|
|
||||||
* @param ai activating player
|
|
||||||
* @param test actual payment is made if this is false
|
|
||||||
* @param manaSpentToPay list of mana spent
|
|
||||||
* @return whether the floating mana is sufficient to pay the cost fully
|
|
||||||
*/
|
|
||||||
private static boolean payManaCostFromPool(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai,
|
|
||||||
final boolean test, List<Mana> manaSpentToPay) {
|
|
||||||
final boolean hasConverge = sa.getHostCard().hasConverge();
|
|
||||||
List<ManaCostShard> unpaidShards = cost.getUnpaidShards();
|
|
||||||
Collections.sort(unpaidShards); // most difficult shards must come first
|
|
||||||
for (ManaCostShard part : unpaidShards) {
|
|
||||||
if (part != ManaCostShard.X) {
|
|
||||||
if (cost.isPaid()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a mana of this type from floating, bail if none available
|
|
||||||
final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), hasConverge ? cost.getColorsPaid() : -1, cost.getXManaCostPaidByColor());
|
|
||||||
if (mana != null) {
|
|
||||||
if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana, test)) {
|
|
||||||
manaSpentToPay.add(0, mana);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cost.isPaid()) {
|
|
||||||
// refund any mana taken from mana pool when test
|
|
||||||
if (test) {
|
|
||||||
refundMana(manaSpentToPay, ai, sa);
|
|
||||||
}
|
|
||||||
handleOfferingsAI(sa, test, cost.isPaid());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* getManaFrom.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param saBeingPaidFor
|
|
||||||
* a {@link forge.game.spellability.SpellAbility} object.
|
|
||||||
* @return a {@link forge.game.mana.Mana} object.
|
|
||||||
*/
|
|
||||||
private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor,
|
|
||||||
String restriction, final byte colorsPaid, Map<String, Integer> xManaCostPaidByColor) {
|
|
||||||
final List<Pair<Mana, Integer>> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard,
|
|
||||||
saBeingPaidFor, restriction, colorsPaid, xManaCostPaidByColor);
|
|
||||||
|
|
||||||
// Exclude border case
|
|
||||||
if (weightedOptions.isEmpty()) {
|
|
||||||
return null; // There is no matching mana in the pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// select equal weight possibilities
|
|
||||||
List<Mana> manaChoices = new ArrayList<>();
|
|
||||||
int bestWeight = Integer.MIN_VALUE;
|
|
||||||
for (Pair<Mana, Integer> option : weightedOptions) {
|
|
||||||
int thisWeight = option.getRight();
|
|
||||||
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 (manaChoices.size() == 1) {
|
|
||||||
return manaChoices.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we are simulating mana payment for the human controller, use the first mana available (and avoid prompting the human player)
|
|
||||||
if (!(ai.getController() instanceof PlayerControllerAi)) {
|
|
||||||
return manaChoices.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let them choose then
|
|
||||||
return ai.getController().chooseManaFromPool(manaChoices);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Pair<Mana, Integer>> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard,
|
|
||||||
final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid, Map<String, Integer> xManaCostPaidByColor) {
|
|
||||||
final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<>();
|
|
||||||
for (final Mana thisMana : manapool) {
|
|
||||||
if (shard == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(MagicColor.toShortString(thisMana.getColor()), xManaCostPaidByColor)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!manapool.canPayForShardWithColor(shard, thisMana.getColor())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thisMana.getManaAbility() != null && !thisMana.getManaAbility().meetsSpellAndShardRestrictions(saBeingPaidFor, shard, thisMana.getColor())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean canPay = manapool.canPayForShardWithColor(shard, thisMana.getColor());
|
|
||||||
if (!canPay || (shard.isSnow() && !thisMana.isSnow())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(restriction) && !thisMana.getSourceCard().getType().hasStringType(restriction)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int weight = 0;
|
|
||||||
if (colorsPaid == -1) {
|
|
||||||
// prefer colorless mana to spend
|
|
||||||
weight += thisMana.isColorless() ? 5 : 0;
|
|
||||||
} else {
|
|
||||||
// get more colors for converge
|
|
||||||
weight += (thisMana.getColor() | colorsPaid) != colorsPaid ? 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 weightedOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setExpressColorChoice(final SpellAbility sa, final Player ai, ManaCostBeingPaid cost,
|
private static void setExpressColorChoice(final SpellAbility sa, final Player ai, ManaCostBeingPaid cost,
|
||||||
ManaCostShard toPay, SpellAbility saPayment) {
|
ManaCostShard toPay, SpellAbility saPayment) {
|
||||||
|
|
||||||
@@ -1935,26 +1771,6 @@ public class ComputerUtilMana {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void handleOfferingsAI(final SpellAbility sa, boolean test, boolean costIsPaid) {
|
|
||||||
if (sa.isOffering() && sa.getSacrificedAsOffering() != null) {
|
|
||||||
final Card offering = sa.getSacrificedAsOffering();
|
|
||||||
offering.setUsedToPay(false);
|
|
||||||
if (costIsPaid && !test) {
|
|
||||||
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, false, null, null);
|
|
||||||
}
|
|
||||||
sa.resetSacrificedAsOffering();
|
|
||||||
}
|
|
||||||
if (sa.isEmerge() && sa.getSacrificedAsEmerge() != null) {
|
|
||||||
final Card emerge = sa.getSacrificedAsEmerge();
|
|
||||||
emerge.setUsedToPay(false);
|
|
||||||
if (costIsPaid && !test) {
|
|
||||||
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, false, null, null);
|
|
||||||
}
|
|
||||||
sa.resetSacrificedAsEmerge();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Matches list of creatures to shards in mana cost for convoking.
|
* Matches list of creatures to shards in mana cost for convoking.
|
||||||
* @param cost cost of convoked ability
|
* @param cost cost of convoked ability
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.card.MagicColor;
|
import forge.card.MagicColor;
|
||||||
|
import forge.card.mana.ManaAtom;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
@@ -153,7 +154,7 @@ public abstract class GameState {
|
|||||||
sb.append(TextUtil.concatNoSpace("humanmanapool=", humanManaPool, "\n"));
|
sb.append(TextUtil.concatNoSpace("humanmanapool=", humanManaPool, "\n"));
|
||||||
}
|
}
|
||||||
if (!computerManaPool.isEmpty()) {
|
if (!computerManaPool.isEmpty()) {
|
||||||
sb.append(TextUtil.concatNoSpace("aimanapool=", humanManaPool, "\n"));
|
sb.append(TextUtil.concatNoSpace("aimanapool=", computerManaPool, "\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append(TextUtil.concatNoSpace("activeplayer=", tChangePlayer, "\n"));
|
sb.append(TextUtil.concatNoSpace("activeplayer=", tChangePlayer, "\n"));
|
||||||
@@ -710,7 +711,7 @@ public abstract class GameState {
|
|||||||
|
|
||||||
private String processManaPool(ManaPool manaPool) {
|
private String processManaPool(ManaPool manaPool) {
|
||||||
StringBuilder mana = new StringBuilder();
|
StringBuilder mana = new StringBuilder();
|
||||||
for (final byte c : MagicColor.WUBRGC) {
|
for (final byte c : ManaAtom.MANATYPES) {
|
||||||
int amount = manaPool.getAmountOfColor(c);
|
int amount = manaPool.getAmountOfColor(c);
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
mana.append(MagicColor.toShortString(c)).append(" ");
|
mana.append(MagicColor.toShortString(c)).append(" ");
|
||||||
|
|||||||
@@ -1081,6 +1081,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
boolean noManaCost = tgtSA.hasParam("WithoutManaCost");
|
boolean noManaCost = tgtSA.hasParam("WithoutManaCost");
|
||||||
if (tgtSA instanceof Spell) { // Isn't it ALWAYS a spell?
|
if (tgtSA instanceof Spell) { // Isn't it ALWAYS a spell?
|
||||||
Spell spell = (Spell) tgtSA;
|
Spell spell = (Spell) tgtSA;
|
||||||
|
// TODO if mandatory AI is only forced to use mana when it's already in the pool
|
||||||
if (tgtSA.checkRestrictions(brains.getPlayer()) && (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional)) {
|
if (tgtSA.checkRestrictions(brains.getPlayer()) && (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional)) {
|
||||||
if (noManaCost) {
|
if (noManaCost) {
|
||||||
return ComputerUtil.playSpellAbilityWithoutPayingManaCost(player, tgtSA, getGame());
|
return ComputerUtil.playSpellAbilityWithoutPayingManaCost(player, tgtSA, getGame());
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ public class DiscardAi extends SpellAbilityAi {
|
|||||||
} else if (!opp.canDiscardBy(sa, true)) { // e.g. Tamiyo, Collector of Tales
|
} else if (!opp.canDiscardBy(sa, true)) { // e.g. Tamiyo, Collector of Tales
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// TODO when DiscardValid is used and opponent plays with hand revealed, check if he has matching cards
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
if (sa.canTarget(opp)) {
|
if (sa.canTarget(opp)) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
|||||||
@@ -325,6 +325,10 @@ public class PlayEffect extends SpellAbilityEffect {
|
|||||||
tgtSA = tgtSA.copyWithDefinedCost(abCost);
|
tgtSA = tgtSA.copyWithDefinedCost(abCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!optional) {
|
||||||
|
tgtSA.getPayCosts().setMandatory(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (sa.hasParam("PlayReduceCost")) {
|
if (sa.hasParam("PlayReduceCost")) {
|
||||||
// for Kefnet only can reduce colorless cost
|
// for Kefnet only can reduce colorless cost
|
||||||
String reduce = sa.getParam("PlayReduceCost");
|
String reduce = sa.getParam("PlayReduceCost");
|
||||||
|
|||||||
@@ -186,6 +186,9 @@ public class Cost implements Serializable {
|
|||||||
public final boolean isMandatory() {
|
public final boolean isMandatory() {
|
||||||
return this.isMandatory;
|
return this.isMandatory;
|
||||||
}
|
}
|
||||||
|
public final void setMandatory(boolean b) {
|
||||||
|
isMandatory = b;
|
||||||
|
}
|
||||||
|
|
||||||
public final boolean isAbility() {
|
public final boolean isAbility() {
|
||||||
return this.isAbility;
|
return this.isAbility;
|
||||||
|
|||||||
@@ -17,15 +17,25 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game.cost;
|
package forge.game.cost;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import forge.card.MagicColor;
|
||||||
|
import forge.card.mana.ManaCostShard;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.mana.Mana;
|
||||||
import forge.game.mana.ManaConversionMatrix;
|
import forge.game.mana.ManaConversionMatrix;
|
||||||
|
import forge.game.mana.ManaCostBeingPaid;
|
||||||
|
import forge.game.mana.ManaPool;
|
||||||
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
@@ -221,4 +231,132 @@ public class CostPayment extends ManaConversionMatrix {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* getManaFrom.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param saBeingPaidFor
|
||||||
|
* a {@link forge.game.spellability.SpellAbility} object.
|
||||||
|
* @return a {@link forge.game.mana.Mana} object.
|
||||||
|
*/
|
||||||
|
public static Mana getMana(final Player player, final ManaCostShard shard, final SpellAbility saBeingPaidFor,
|
||||||
|
String restriction, final byte colorsPaid, Map<String, Integer> xManaCostPaidByColor) {
|
||||||
|
final List<Pair<Mana, Integer>> weightedOptions = selectManaToPayFor(player.getManaPool(), shard,
|
||||||
|
saBeingPaidFor, restriction, colorsPaid, xManaCostPaidByColor);
|
||||||
|
|
||||||
|
// Exclude border case
|
||||||
|
if (weightedOptions.isEmpty()) {
|
||||||
|
return null; // There is no matching mana in the pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// select equal weight possibilities
|
||||||
|
List<Mana> manaChoices = new ArrayList<>();
|
||||||
|
int bestWeight = Integer.MIN_VALUE;
|
||||||
|
for (Pair<Mana, Integer> option : weightedOptions) {
|
||||||
|
int thisWeight = option.getRight();
|
||||||
|
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 (manaChoices.size() == 1) {
|
||||||
|
return manaChoices.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are simulating mana payment for the human controller, use the first mana available (and avoid prompting the human player)
|
||||||
|
if (!player.getController().isAI()) {
|
||||||
|
return manaChoices.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let them choose then
|
||||||
|
return player.getController().chooseManaFromPool(manaChoices);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Pair<Mana, Integer>> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard,
|
||||||
|
final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid, Map<String, Integer> xManaCostPaidByColor) {
|
||||||
|
final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<>();
|
||||||
|
for (final Mana thisMana : manapool) {
|
||||||
|
if (shard == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(MagicColor.toShortString(thisMana.getColor()), xManaCostPaidByColor)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manapool.canPayForShardWithColor(shard, thisMana.getColor())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thisMana.getManaAbility() != null && !thisMana.getManaAbility().meetsSpellAndShardRestrictions(saBeingPaidFor, shard, thisMana.getColor())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canPay = manapool.canPayForShardWithColor(shard, thisMana.getColor());
|
||||||
|
if (!canPay || (shard.isSnow() && !thisMana.isSnow())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(restriction) && !thisMana.getSourceCard().getType().hasStringType(restriction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int weight = 0;
|
||||||
|
if (colorsPaid == -1) {
|
||||||
|
// prefer colorless mana to spend
|
||||||
|
weight += thisMana.isColorless() ? 5 : 0;
|
||||||
|
} else {
|
||||||
|
// get more colors for converge
|
||||||
|
weight += (thisMana.getColor() | colorsPaid) != colorsPaid ? 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 weightedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleOfferings(final SpellAbility sa, boolean test, boolean costIsPaid) {
|
||||||
|
if (sa.isOffering() && sa.getSacrificedAsOffering() != null) {
|
||||||
|
final Card offering = sa.getSacrificedAsOffering();
|
||||||
|
offering.setUsedToPay(false);
|
||||||
|
if (costIsPaid && !test) {
|
||||||
|
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, false, null, null);
|
||||||
|
}
|
||||||
|
sa.resetSacrificedAsOffering();
|
||||||
|
}
|
||||||
|
if (sa.isEmerge() && sa.getSacrificedAsEmerge() != null) {
|
||||||
|
final Card emerge = sa.getSacrificedAsEmerge();
|
||||||
|
emerge.setUsedToPay(false);
|
||||||
|
if (costIsPaid && !test) {
|
||||||
|
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, false, null, null);
|
||||||
|
}
|
||||||
|
sa.resetSacrificedAsEmerge();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
package forge.game.mana;
|
package forge.game.mana;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -33,6 +34,7 @@ import forge.card.mana.ManaCostShard;
|
|||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GlobalRuleChange;
|
import forge.game.GlobalRuleChange;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
|
import forge.game.cost.CostPayment;
|
||||||
import forge.game.event.EventValueChangeType;
|
import forge.game.event.EventValueChangeType;
|
||||||
import forge.game.event.GameEventManaPool;
|
import forge.game.event.GameEventManaPool;
|
||||||
import forge.game.event.GameEventZone;
|
import forge.game.event.GameEventZone;
|
||||||
@@ -289,6 +291,16 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void refundMana(List<Mana> manaSpent, Player player, SpellAbility sa) {
|
||||||
|
if (sa.getHostCard() != null) {
|
||||||
|
sa.getHostCard().setCanCounter(true);
|
||||||
|
}
|
||||||
|
for (final Mana m : manaSpent) {
|
||||||
|
player.getManaPool().addMana(m);
|
||||||
|
}
|
||||||
|
manaSpent.clear();
|
||||||
|
}
|
||||||
|
|
||||||
public final void refundManaPaid(final SpellAbility sa) {
|
public final void refundManaPaid(final SpellAbility sa) {
|
||||||
// Send all mana back to your mana pool, before accounting for it.
|
// Send all mana back to your mana pool, before accounting for it.
|
||||||
final List<Mana> manaPaid = sa.getPayingMana();
|
final List<Mana> manaPaid = sa.getPayingMana();
|
||||||
@@ -342,6 +354,47 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
|||||||
return shard.canBePaidWithManaOfColor((byte)0);
|
return shard.canBePaidWithManaOfColor((byte)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given mana cost can be paid from floating mana.
|
||||||
|
* @param cost mana cost to pay for
|
||||||
|
* @param sa ability to pay for
|
||||||
|
* @param player activating player
|
||||||
|
* @param test actual payment is made if this is false
|
||||||
|
* @param manaSpentToPay list of mana spent
|
||||||
|
* @return whether the floating mana is sufficient to pay the cost fully
|
||||||
|
*/
|
||||||
|
public static boolean payManaCostFromPool(final ManaCostBeingPaid cost, final SpellAbility sa, final Player player,
|
||||||
|
final boolean test, List<Mana> manaSpentToPay) {
|
||||||
|
final boolean hasConverge = sa.getHostCard().hasConverge();
|
||||||
|
List<ManaCostShard> unpaidShards = cost.getUnpaidShards();
|
||||||
|
Collections.sort(unpaidShards); // most difficult shards must come first
|
||||||
|
for (ManaCostShard part : unpaidShards) {
|
||||||
|
if (part != ManaCostShard.X) {
|
||||||
|
if (cost.isPaid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a mana of this type from floating, bail if none available
|
||||||
|
final Mana mana = CostPayment.getMana(player, part, sa, cost.getSourceRestriction(), hasConverge ? cost.getColorsPaid() : -1, cost.getXManaCostPaidByColor());
|
||||||
|
if (mana != null) {
|
||||||
|
if (player.getManaPool().tryPayCostWithMana(sa, cost, mana, test)) {
|
||||||
|
manaSpentToPay.add(0, mana);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cost.isPaid()) {
|
||||||
|
// refund any mana taken from mana pool when test
|
||||||
|
if (test) {
|
||||||
|
refundMana(manaSpentToPay, player, sa);
|
||||||
|
}
|
||||||
|
CostPayment.handleOfferings(sa, test, cost.isPaid());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Mana> iterator() {
|
public Iterator<Mana> iterator() {
|
||||||
return floatingMana.values().iterator();
|
return floatingMana.values().iterator();
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
|||||||
protected ManaCostBeingPaid manaCost;
|
protected ManaCostBeingPaid manaCost;
|
||||||
protected final SpellAbility saPaidFor;
|
protected final SpellAbility saPaidFor;
|
||||||
protected boolean effect;
|
protected boolean effect;
|
||||||
|
protected boolean mandatory = false;
|
||||||
private final boolean wasFloatingMana;
|
private final boolean wasFloatingMana;
|
||||||
private final Queue<Card> delaySelectCards = new LinkedList<>();
|
private final Queue<Card> delaySelectCards = new LinkedList<>();
|
||||||
|
|
||||||
@@ -406,9 +407,9 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
|||||||
|
|
||||||
protected void updateButtons() {
|
protected void updateButtons() {
|
||||||
if (supportAutoPay()) {
|
if (supportAutoPay()) {
|
||||||
getController().getGui().updateButtons(getOwner(), Localizer.getInstance().getMessage("lblAuto"), Localizer.getInstance().getMessage("lblCancel"), false, true, false);
|
getController().getGui().updateButtons(getOwner(), Localizer.getInstance().getMessage("lblAuto"), Localizer.getInstance().getMessage("lblCancel"), false, !mandatory, false);
|
||||||
} else {
|
} else {
|
||||||
getController().getGui().updateButtons(getOwner(), "", Localizer.getInstance().getMessage("lblCancel"), false, true, false);
|
getController().getGui().updateButtons(getOwner(), "", Localizer.getInstance().getMessage("lblCancel"), false, !mandatory, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +431,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
|||||||
canPayManaCost = proc.getResult();
|
canPayManaCost = proc.getResult();
|
||||||
}
|
}
|
||||||
if (canPayManaCost) { //enabled Auto button if mana cost can be paid
|
if (canPayManaCost) { //enabled Auto button if mana cost can be paid
|
||||||
getController().getGui().updateButtons(getOwner(), Localizer.getInstance().getMessage("lblAuto"), Localizer.getInstance().getMessage("lblCancel"), true, true, true);
|
getController().getGui().updateButtons(getOwner(), Localizer.getInstance().getMessage("lblAuto"), Localizer.getInstance().getMessage("lblCancel"), true, !mandatory, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showMessage(getMessage(), saPaidFor.getView());
|
showMessage(getMessage(), saPaidFor.getView());
|
||||||
@@ -447,8 +448,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
|||||||
if (isAlreadyPaid()) {
|
if (isAlreadyPaid()) {
|
||||||
done();
|
done();
|
||||||
stop();
|
stop();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
package forge.gamemodes.match.input;
|
package forge.gamemodes.match.input;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import forge.card.mana.ManaAtom;
|
import forge.card.mana.ManaAtom;
|
||||||
import forge.card.mana.ManaCostShard;
|
import forge.card.mana.ManaCostShard;
|
||||||
|
import forge.game.mana.Mana;
|
||||||
import forge.game.mana.ManaConversionMatrix;
|
import forge.game.mana.ManaConversionMatrix;
|
||||||
import forge.game.mana.ManaCostBeingPaid;
|
import forge.game.mana.ManaCostBeingPaid;
|
||||||
|
import forge.game.mana.ManaPool;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.localinstance.properties.ForgePreferences;
|
import forge.localinstance.properties.ForgePreferences;
|
||||||
@@ -20,13 +25,20 @@ public class InputPayManaOfCostPayment extends InputPayMana {
|
|||||||
extraMatrix = matrix;
|
extraMatrix = matrix;
|
||||||
applyMatrix();
|
applyMatrix();
|
||||||
|
|
||||||
|
// CR 118.3c forced cast must use pool mana
|
||||||
|
// TODO this introduces a small risk for illegal payments if the human "wastes" enough mana for abilities like Doubling Cube
|
||||||
|
if (spellAbility.getPayCosts().isMandatory()) {
|
||||||
|
List<Mana> refund = new ArrayList<>();
|
||||||
|
mandatory = ManaPool.payManaCostFromPool(new ManaCostBeingPaid(cost), spellAbility, payer, true, refund);
|
||||||
|
ManaPool.refundMana(refund, payer, spellAbility);
|
||||||
|
}
|
||||||
|
|
||||||
// Set Mana cost being paid for SA to be able to reference it later
|
// Set Mana cost being paid for SA to be able to reference it later
|
||||||
player.pushPaidForSA(saPaidFor);
|
player.pushPaidForSA(saPaidFor);
|
||||||
saPaidFor.setManaCostBeingPaid(manaCost);
|
saPaidFor.setManaCostBeingPaid(manaCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final long serialVersionUID = 3467312982164195091L;
|
private static final long serialVersionUID = 3467312982164195091L;
|
||||||
//private int phyLifeToLose = 0;
|
|
||||||
private ManaConversionMatrix extraMatrix;
|
private ManaConversionMatrix extraMatrix;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
private final SpellAbility ability;
|
private final SpellAbility ability;
|
||||||
private final Card source;
|
private final Card source;
|
||||||
private String orString = null;
|
private String orString = null;
|
||||||
|
private boolean mandatory;
|
||||||
|
|
||||||
public HumanCostDecision(final PlayerControllerHuman controller, final Player p, final SpellAbility sa, final boolean effect, final Card source) {
|
public HumanCostDecision(final PlayerControllerHuman controller, final Player p, final SpellAbility sa, final boolean effect, final Card source) {
|
||||||
this(controller, p, sa, effect, source, null);
|
this(controller, p, sa, effect, source, null);
|
||||||
@@ -56,6 +57,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
super(p, effect);
|
super(p, effect);
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
ability = sa;
|
ability = sa;
|
||||||
|
mandatory = sa.getPayCosts().isMandatory();
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.orString = orString;
|
this.orString = orString;
|
||||||
}
|
}
|
||||||
@@ -84,7 +86,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (discardType.equals("Hand")) {
|
if (discardType.equals("Hand")) {
|
||||||
if (!controller.confirmPayment(cost, Localizer.getInstance().getMessage("lblDoYouWantDiscardYourHand"), ability)) {
|
if (!mandatory && !controller.confirmPayment(cost, Localizer.getInstance().getMessage("lblDoYouWantDiscardYourHand"), ability)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (hand.size() > 1 && ability.getActivatingPlayer() != null) {
|
if (hand.size() > 1 && ability.getActivatingPlayer() != null) {
|
||||||
@@ -167,7 +169,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
|
|
||||||
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, hand, ability);
|
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, hand, ability);
|
||||||
inp.setMessage(Localizer.getInstance().getMessage("lblSelectNMoreTargetTypeCardToDiscard", "%d", cost.getDescriptiveType()));
|
inp.setMessage(Localizer.getInstance().getMessage("lblSelectNMoreTargetTypeCardToDiscard", "%d", cost.getDescriptiveType()));
|
||||||
inp.setCancelAllowed(true);
|
inp.setCancelAllowed(!mandatory);
|
||||||
inp.showAndWait();
|
inp.showAndWait();
|
||||||
if (inp.hasCancelled() || inp.getSelected().size() != c) {
|
if (inp.hasCancelled() || inp.getSelected().size() != c) {
|
||||||
return null;
|
return null;
|
||||||
@@ -258,7 +260,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
if (cost.from == ZoneType.Battlefield || cost.from == ZoneType.Hand) {
|
if (cost.from == ZoneType.Battlefield || cost.from == ZoneType.Hand) {
|
||||||
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, list, ability);
|
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, list, ability);
|
||||||
inp.setMessage(Localizer.getInstance().getMessage("lblExileNCardsFromYourZone", "%d", cost.getFrom().getTranslatedName()));
|
inp.setMessage(Localizer.getInstance().getMessage("lblExileNCardsFromYourZone", "%d", cost.getFrom().getTranslatedName()));
|
||||||
inp.setCancelAllowed(true);
|
inp.setCancelAllowed(!mandatory);
|
||||||
inp.showAndWait();
|
inp.showAndWait();
|
||||||
return inp.hasCancelled() ? null : PaymentDecision.card(inp.getSelected());
|
return inp.hasCancelled() ? null : PaymentDecision.card(inp.getSelected());
|
||||||
}
|
}
|
||||||
@@ -379,7 +381,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
origin.add(cost.from);
|
origin.add(cost.from);
|
||||||
final CardCollection exiled = new CardCollection();
|
final CardCollection exiled = new CardCollection();
|
||||||
|
|
||||||
final List<Card> chosen = controller.chooseCardsForZoneChange(ZoneType.Exile, origin, sa, typeList, 0,
|
final List<Card> chosen = controller.chooseCardsForZoneChange(ZoneType.Exile, origin, sa, typeList, mandatory ? nNeeded : 0,
|
||||||
nNeeded, null, cost.toString(), null);
|
nNeeded, null, cost.toString(), null);
|
||||||
|
|
||||||
exiled.addAll(chosen);
|
exiled.addAll(chosen);
|
||||||
@@ -539,7 +541,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
public PaymentDecision visit(final CostPayLife cost) {
|
public PaymentDecision visit(final CostPayLife cost) {
|
||||||
Integer c = cost.getAbilityAmount(ability);
|
Integer c = cost.getAbilityAmount(ability);
|
||||||
|
|
||||||
if (ability.getPayCosts().isMandatory()) {
|
if (mandatory) {
|
||||||
return PaymentDecision.number(c);
|
return PaymentDecision.number(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,7 +673,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
|
|
||||||
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, 1, 1, typeList, ability);
|
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, 1, 1, typeList, ability);
|
||||||
inp.setMessage(Localizer.getInstance().getMessage("lblPutNTypeCounterOnTarget", String.valueOf(c), cost.getCounter().getName(), cost.getDescriptiveType()));
|
inp.setMessage(Localizer.getInstance().getMessage("lblPutNTypeCounterOnTarget", String.valueOf(c), cost.getCounter().getName(), cost.getDescriptiveType()));
|
||||||
inp.setCancelAllowed(true);
|
inp.setCancelAllowed(!mandatory);
|
||||||
inp.showAndWait();
|
inp.showAndWait();
|
||||||
|
|
||||||
if (inp.hasCancelled()) {
|
if (inp.hasCancelled()) {
|
||||||
@@ -695,7 +697,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
cost.getType().split(";"), player, source, ability);
|
cost.getType().split(";"), player, source, ability);
|
||||||
|
|
||||||
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, validCards, ability);
|
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, validCards, ability);
|
||||||
inp.setCancelAllowed(true);
|
inp.setCancelAllowed(!mandatory);
|
||||||
inp.setMessage(Localizer.getInstance().getMessage("lblNTypeCardsToHand", "%d", cost.getDescriptiveType()));
|
inp.setMessage(Localizer.getInstance().getMessage("lblNTypeCardsToHand", "%d", cost.getDescriptiveType()));
|
||||||
inp.showAndWait();
|
inp.showAndWait();
|
||||||
if (inp.hasCancelled()) {
|
if (inp.hasCancelled()) {
|
||||||
@@ -765,7 +767,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
inp = new InputSelectCardsFromList(controller, num, num, hand, ability);
|
inp = new InputSelectCardsFromList(controller, num, num, hand, ability);
|
||||||
inp.setMessage(Localizer.getInstance().getMessage("lblSelectNMoreTypeCardsTpReveal", "%d", cost.getDescriptiveType()));
|
inp.setMessage(Localizer.getInstance().getMessage("lblSelectNMoreTypeCardsTpReveal", "%d", cost.getDescriptiveType()));
|
||||||
}
|
}
|
||||||
inp.setCancelAllowed(true);
|
inp.setCancelAllowed(!mandatory);
|
||||||
inp.showAndWait();
|
inp.showAndWait();
|
||||||
if (inp.hasCancelled()) {
|
if (inp.hasCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -981,7 +983,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
|
|
||||||
if (cost.payCostFromSource()) {
|
if (cost.payCostFromSource()) {
|
||||||
if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) {
|
if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) {
|
||||||
return ability.getPayCosts().isMandatory() || controller.confirmPayment(cost, Localizer.getInstance().getMessage("lblSacrificeCardConfirm", CardTranslation.getTranslatedName(source.getName())), ability) ? PaymentDecision.card(source) : null;
|
return mandatory || controller.confirmPayment(cost, Localizer.getInstance().getMessage("lblSacrificeCardConfirm", CardTranslation.getTranslatedName(source.getName())), ability) ? PaymentDecision.card(source) : null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1007,7 +1009,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
}
|
}
|
||||||
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, list, ability);
|
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, list, ability);
|
||||||
inp.setMessage(Localizer.getInstance().getMessage("lblSelectATargetToSacrifice", cost.getDescriptiveType(), "%d"));
|
inp.setMessage(Localizer.getInstance().getMessage("lblSelectATargetToSacrifice", cost.getDescriptiveType(), "%d"));
|
||||||
inp.setCancelAllowed(true);
|
inp.setCancelAllowed(!mandatory);
|
||||||
inp.showAndWait();
|
inp.showAndWait();
|
||||||
if (inp.hasCancelled()) {
|
if (inp.hasCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -1116,7 +1118,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, typeList, ability);
|
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, c, c, typeList, ability);
|
||||||
inp.setCancelAllowed(true);
|
inp.setCancelAllowed(!mandatory);
|
||||||
inp.setMessage(Localizer.getInstance().getMessage("lblSelectATargetToTap", cost.getDescriptiveType(), "%d"));
|
inp.setMessage(Localizer.getInstance().getMessage("lblSelectATargetToTap", cost.getDescriptiveType(), "%d"));
|
||||||
inp.showAndWait();
|
inp.showAndWait();
|
||||||
if (inp.hasCancelled()) {
|
if (inp.hasCancelled()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user