mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 04:08:01 +00:00
Make it so X mana costs are paid using a pre-mana payment announcement
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -16734,7 +16734,6 @@ forge-gui/src/main/java/forge/match/input/InputPassPriority.java -text
|
|||||||
forge-gui/src/main/java/forge/match/input/InputPayMana.java -text
|
forge-gui/src/main/java/forge/match/input/InputPayMana.java -text
|
||||||
forge-gui/src/main/java/forge/match/input/InputPayManaOfCostPayment.java -text
|
forge-gui/src/main/java/forge/match/input/InputPayManaOfCostPayment.java -text
|
||||||
forge-gui/src/main/java/forge/match/input/InputPayManaSimple.java -text
|
forge-gui/src/main/java/forge/match/input/InputPayManaSimple.java -text
|
||||||
forge-gui/src/main/java/forge/match/input/InputPayManaX.java -text
|
|
||||||
forge-gui/src/main/java/forge/match/input/InputPlaybackControl.java -text
|
forge-gui/src/main/java/forge/match/input/InputPlaybackControl.java -text
|
||||||
forge-gui/src/main/java/forge/match/input/InputProliferate.java -text
|
forge-gui/src/main/java/forge/match/input/InputProliferate.java -text
|
||||||
forge-gui/src/main/java/forge/match/input/InputProxy.java -text
|
forge-gui/src/main/java/forge/match/input/InputProxy.java -text
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import forge.util.maps.MapToAmount;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* ManaCostBeingPaid class.
|
* ManaCostBeingPaid class.
|
||||||
@@ -215,6 +217,20 @@ public class ManaCostBeingPaid {
|
|||||||
return unpaidShards.isEmpty();
|
return unpaidShards.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void setXManaCostPaid(final int xPaid, final String xColor) {
|
||||||
|
int xCost = xPaid * cntX;
|
||||||
|
cntX = 0;
|
||||||
|
|
||||||
|
ManaCostShard increaseShard;
|
||||||
|
if (StringUtils.isEmpty(xColor)) {
|
||||||
|
increaseShard = ManaCostShard.COLORLESS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
increaseShard = ManaCostShard.valueOf(MagicColor.fromName(xColor));
|
||||||
|
}
|
||||||
|
unpaidShards.add(increaseShard, xCost);
|
||||||
|
}
|
||||||
|
|
||||||
public final void increaseColorlessMana(final int manaToAdd) {
|
public final void increaseColorlessMana(final int manaToAdd) {
|
||||||
increaseShard(ManaCostShard.COLORLESS, manaToAdd);
|
increaseShard(ManaCostShard.COLORLESS, manaToAdd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,16 +13,15 @@ There will no longer be a black rectangle for "Commander effect" in the command
|
|||||||
The details previously available by hovering over that rectangle will now appear when hovering over the commander itself.
|
The details previously available by hovering over that rectangle will now appear when hovering over the commander itself.
|
||||||
The dialog for the commander replacement effect will now display the commander's name.
|
The dialog for the commander replacement effect will now display the commander's name.
|
||||||
|
|
||||||
|
|
||||||
- Momir Basic variant type -
|
- Momir Basic variant type -
|
||||||
Momir Basic is now available as its own variant option on the Constructed screen
|
Momir Basic is now available as its own variant option on the Constructed screen
|
||||||
For this format. each player will automatically be given a deck with 12 of each basic land and the Momir Vig avatar
|
For this format. each player will automatically be given a deck with 12 of each basic land and the Momir Vig avatar
|
||||||
|
|
||||||
|
|
||||||
- Auto-pay support for X mana costs -
|
- Choose value for X mana costs -
|
||||||
When prompted to choose a value for X when paying a mana cost, the left button will now as display "Auto" until you manually click a mana source
|
Now, when playing spells/abilities with X in its mana cost, you will now be prompted for a value for X prior to mana payment, ensuring the final mana cost is calculated properly from cost adjustment effects and allowing using the "Auto" button to pay the entire cost.
|
||||||
If you press Auto, you'll be prompted to pick a value for X, with it defaulting to the maximum value you can afford.
|
This also applies to spells with Replicate and Multikicker to allow picking the Replicate or Multikicker amount prior to paying the final mana cost.
|
||||||
If you press OK on that prompt, Forge will use AI logic to automatically pay whatever value for X you chose.
|
|
||||||
|
|
||||||
|
|
||||||
- Auto-targeting support -
|
- Auto-targeting support -
|
||||||
When playing spells and abilities with the text "target opponent", if you only have one opponent, you will no longer be asked to choose the opponent to target.
|
When playing spells and abilities with the text "target opponent", if you only have one opponent, you will no longer be asked to choose the opponent to target.
|
||||||
|
|||||||
@@ -1,184 +0,0 @@
|
|||||||
package forge.match.input;
|
|
||||||
|
|
||||||
import forge.ai.ComputerUtilMana;
|
|
||||||
import forge.card.ColorSet;
|
|
||||||
import forge.card.mana.ManaCost;
|
|
||||||
import forge.card.mana.ManaCostParser;
|
|
||||||
import forge.game.card.Card;
|
|
||||||
import forge.game.mana.Mana;
|
|
||||||
import forge.game.mana.ManaCostBeingPaid;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
|
||||||
import forge.util.Evaluator;
|
|
||||||
import forge.util.ITriggerEvent;
|
|
||||||
import forge.util.ThreadUtil;
|
|
||||||
import forge.util.gui.SGuiChoose;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class InputPayManaX extends InputPayMana {
|
|
||||||
private static final long serialVersionUID = -6900234444347364050L;
|
|
||||||
private int xPaid = 0;
|
|
||||||
private ArrayList<Mana> xPaidByColor = new ArrayList<>();
|
|
||||||
private byte colorsPaid;
|
|
||||||
private final String xCostStr;
|
|
||||||
private final ManaCost manaCostPerX;
|
|
||||||
private final boolean xCanBe0;
|
|
||||||
private boolean canceled = false;
|
|
||||||
private Integer max;
|
|
||||||
|
|
||||||
public InputPayManaX(final SpellAbility sa0, final int amountX, final boolean xCanBe0) {
|
|
||||||
super(sa0, sa0.getActivatingPlayer());
|
|
||||||
xPaid = 0;
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (int i = 0; i < amountX; i++) {
|
|
||||||
builder.append("{X}");
|
|
||||||
}
|
|
||||||
xCostStr = builder.toString();
|
|
||||||
|
|
||||||
if (saPaidFor.hasParam("XColor")) {
|
|
||||||
String xColor = saPaidFor.getParam("XColor");
|
|
||||||
if (amountX == 1) {
|
|
||||||
manaCostPerX = new ManaCost(new ManaCostParser(xColor));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
List<String> list = new ArrayList<String>(amountX);
|
|
||||||
for (int i = 0; i < amountX; i++) {
|
|
||||||
list.add(xColor);
|
|
||||||
}
|
|
||||||
manaCostPerX = new ManaCost(new ManaCostParser(StringUtils.join(list, ' ')));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
manaCostPerX = ManaCost.get(amountX);
|
|
||||||
}
|
|
||||||
manaCost = new ManaCostBeingPaid(manaCostPerX);
|
|
||||||
|
|
||||||
this.xCanBe0 = xCanBe0;
|
|
||||||
colorsPaid = saPaidFor.getHostCard().getColorsPaid(); // for effects like sunburst
|
|
||||||
canPayManaCost = true; //flag as true always since it doesn't need to be calculated using AI logic
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see forge.control.input.InputPayManaBase#isPaid()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean isPaid() {
|
|
||||||
//return !( xPaid == 0 && !costMana.canXbe0() || this.colorX.equals("") && !this.manaCost.toString().equals(strX) );
|
|
||||||
// return !( xPaid == 0 && !costMana.canXbe0()) && !(this.colorX.equals("") && !this.manaCost.toString().equals(strX));
|
|
||||||
return !canceled && (xPaid > 0 || xCanBe0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean supportAutoPay() {
|
|
||||||
return xPaid == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showMessage() {
|
|
||||||
if (isFinished()) { return; }
|
|
||||||
|
|
||||||
updateMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getMessage() {
|
|
||||||
StringBuilder msg = new StringBuilder("Pay " + xCostStr + ". X=" + xPaid + ".");
|
|
||||||
if (xPaid > 0) {
|
|
||||||
// Enable just cancel is full X value hasn't been paid for multiple X values
|
|
||||||
// or X is 0, and x can't be 0
|
|
||||||
ButtonUtil.update(isPaid(), true, true);
|
|
||||||
}
|
|
||||||
if (!xCanBe0) {
|
|
||||||
msg.append("\nX Can't be 0.");
|
|
||||||
}
|
|
||||||
return msg.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
|
|
||||||
// don't allow here the cards that produce only wrong colors
|
|
||||||
return activateManaAbility(card, this.manaCost);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onManaAbilityPaid() {
|
|
||||||
if (this.manaCost.isPaid()) {
|
|
||||||
this.colorsPaid |= manaCost.getColorsPaid();
|
|
||||||
this.manaCost = new ManaCostBeingPaid(manaCostPerX);
|
|
||||||
this.xPaid++;
|
|
||||||
this.xPaidByColor.add(saPaidFor.getPayingMana().get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final void onCancel() {
|
|
||||||
// If you hit cancel, isPaid needs to return false
|
|
||||||
this.canceled = true;
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final void onOk() {
|
|
||||||
if (supportAutoPay()) {
|
|
||||||
ThreadUtil.invokeInGameThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
int min = xCanBe0 ? 0 : 1;
|
|
||||||
if (max == null) {
|
|
||||||
//use AI utility to determine maximum possible value for X if that hasn't been determined yet
|
|
||||||
Evaluator<Integer> proc = new Evaluator<Integer>() {
|
|
||||||
@Override
|
|
||||||
public Integer evaluate() {
|
|
||||||
ManaCostBeingPaid cost = new ManaCostBeingPaid(manaCostPerX);
|
|
||||||
for (int i = 1; i < 100; i++) {
|
|
||||||
if (!ComputerUtilMana.canPayManaCost(cost, saPaidFor, player)) {
|
|
||||||
return i - 1;
|
|
||||||
}
|
|
||||||
cost.addManaCost(manaCostPerX);
|
|
||||||
}
|
|
||||||
return 99;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
runAsAi(proc);
|
|
||||||
max = proc.getResult();
|
|
||||||
}
|
|
||||||
Integer value = SGuiChoose.getInteger("Choose a value for X", min, max, true);
|
|
||||||
if (value != null) {
|
|
||||||
xPaid = value;
|
|
||||||
saPaidFor.getHostCard().setXManaCostPaid(xPaid);
|
|
||||||
if (xPaid > 0) {
|
|
||||||
final ManaCostBeingPaid cost = new ManaCostBeingPaid(manaCostPerX);
|
|
||||||
for (int i = 1; i < xPaid; i++) {
|
|
||||||
cost.addManaCost(manaCostPerX);
|
|
||||||
}
|
|
||||||
runAsAi(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ComputerUtilMana.payManaCost(cost, saPaidFor, player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
done();
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void done() {
|
|
||||||
final Card card = saPaidFor.getHostCard();
|
|
||||||
card.setXManaCostPaid(this.xPaid);
|
|
||||||
card.setXManaCostPaidByColor(this.xPaidByColor);
|
|
||||||
card.setColorsPaid(this.colorsPaid);
|
|
||||||
card.setSunburstValue(ColorSet.fromMask(this.colorsPaid).countColors());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,9 +4,7 @@ import com.google.common.base.Predicate;
|
|||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.FThreads;
|
import forge.FThreads;
|
||||||
import forge.card.MagicColor;
|
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.card.mana.ManaCostShard;
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameActionUtil;
|
import forge.game.GameActionUtil;
|
||||||
import forge.game.GameLogEntryType;
|
import forge.game.GameLogEntryType;
|
||||||
@@ -27,7 +25,6 @@ import forge.game.zone.ZoneType;
|
|||||||
import forge.match.input.InputPayMana;
|
import forge.match.input.InputPayMana;
|
||||||
import forge.match.input.InputPayManaOfCostPayment;
|
import forge.match.input.InputPayManaOfCostPayment;
|
||||||
import forge.match.input.InputPayManaSimple;
|
import forge.match.input.InputPayManaSimple;
|
||||||
import forge.match.input.InputPayManaX;
|
|
||||||
import forge.match.input.InputSelectCardsFromList;
|
import forge.match.input.InputSelectCardsFromList;
|
||||||
import forge.util.Lang;
|
import forge.util.Lang;
|
||||||
import forge.util.gui.SGuiChoose;
|
import forge.util.gui.SGuiChoose;
|
||||||
@@ -42,7 +39,6 @@ import java.util.Map;
|
|||||||
public class HumanPlay {
|
public class HumanPlay {
|
||||||
|
|
||||||
public HumanPlay() {
|
public HumanPlay() {
|
||||||
// TODO Auto-generated constructor stub
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -694,14 +690,12 @@ public class HumanPlay {
|
|||||||
final Card source = ability.getHostCard();
|
final Card source = ability.getHostCard();
|
||||||
ManaCostBeingPaid toPay = new ManaCostBeingPaid(realCost, mc.getRestiction());
|
ManaCostBeingPaid toPay = new ManaCostBeingPaid(realCost, mc.getRestiction());
|
||||||
|
|
||||||
boolean xWasBilled = false;
|
|
||||||
String xInCard = source.getSVar("X");
|
String xInCard = source.getSVar("X");
|
||||||
if (mc.getAmountOfX() > 0 && !"Count$xPaid".equals(xInCard)) { // announce X will overwrite whatever was in card script
|
if (mc.getAmountOfX() > 0 && !"Count$xPaid".equals(xInCard)) { // announce X will overwrite whatever was in card script
|
||||||
// this currently only works for things about Targeted object
|
// this currently only works for things about Targeted object
|
||||||
int xCost = AbilityUtils.calculateAmount(source, "X", ability) * mc.getAmountOfX();
|
int xPaid = AbilityUtils.calculateAmount(source, "X", ability);
|
||||||
byte xColor = MagicColor.fromName(ability.hasParam("XColor") ? ability.getParam("XColor") : "1");
|
toPay.setXManaCostPaid(xPaid, ability.getParam("XColor"));
|
||||||
toPay.increaseShard(ManaCostShard.valueOf(xColor), xCost);
|
source.setXManaCostPaid(xPaid);
|
||||||
xWasBilled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int timesMultikicked = source.getKickerMagnitude();
|
int timesMultikicked = source.getKickerMagnitude();
|
||||||
@@ -740,19 +734,6 @@ public class HumanPlay {
|
|||||||
source.setColorsPaid(toPay.getColorsPaid());
|
source.setColorsPaid(toPay.getColorsPaid());
|
||||||
source.setSunburstValue(toPay.getSunburst());
|
source.setSunburstValue(toPay.getSunburst());
|
||||||
}
|
}
|
||||||
if (mc.getAmountOfX() > 0) {
|
|
||||||
if (!ability.isAnnouncing("X") && !xWasBilled) {
|
|
||||||
source.setXManaCostPaid(0);
|
|
||||||
inpPayment = new InputPayManaX(ability, mc.getAmountOfX(), mc.canXbe0());
|
|
||||||
inpPayment.showAndWait();
|
|
||||||
if (!inpPayment.isPaid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int x = AbilityUtils.calculateAmount(source, "X", ability);
|
|
||||||
source.setXManaCostPaid(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle convoke and offerings
|
// Handle convoke and offerings
|
||||||
if (ability.isOffering() && ability.getSacrificedAsOffering() != null) {
|
if (ability.isOffering() && ability.getSacrificedAsOffering() != null) {
|
||||||
|
|||||||
@@ -195,20 +195,22 @@ public class HumanPlaySpellAbility {
|
|||||||
|
|
||||||
private boolean announceValuesLikeX() {
|
private boolean announceValuesLikeX() {
|
||||||
if (ability.isCopied()) { return true; } //don't re-announce for spell copies
|
if (ability.isCopied()) { return true; } //don't re-announce for spell copies
|
||||||
|
|
||||||
|
boolean needX = true;
|
||||||
|
boolean allowZero = !ability.hasParam("XCantBe0");
|
||||||
|
CostPartMana manaCost = ability.getPayCosts().getCostMana();
|
||||||
|
PlayerController controller = ability.getActivatingPlayer().getController();
|
||||||
|
Card card = ability.getHostCard();
|
||||||
|
|
||||||
// Announcing Requirements like Choosing X or Multikicker
|
// Announcing Requirements like Choosing X or Multikicker
|
||||||
// SA Params as comma delimited list
|
// SA Params as comma delimited list
|
||||||
String announce = ability.getParam("Announce");
|
String announce = ability.getParam("Announce");
|
||||||
if (announce != null) {
|
if (announce != null) {
|
||||||
PlayerController controller = ability.getActivatingPlayer().getController();
|
|
||||||
Card card = ability.getHostCard();
|
|
||||||
boolean allowZero = !ability.hasParam("XCantBe0");
|
|
||||||
CostPartMana manaCost = ability.getPayCosts().getCostMana();
|
|
||||||
|
|
||||||
for (String aVar : announce.split(",")) {
|
for (String aVar : announce.split(",")) {
|
||||||
String varName = aVar.trim();
|
String varName = aVar.trim();
|
||||||
|
|
||||||
boolean isX = "X".equalsIgnoreCase(varName);
|
boolean isX = "X".equalsIgnoreCase(varName);
|
||||||
|
if (isX) { needX = false; }
|
||||||
|
|
||||||
Integer value = controller.announceRequirements(ability, varName, allowZero && (!isX || manaCost == null || manaCost.canXbe0()));
|
Integer value = controller.announceRequirements(ability, varName, allowZero && (!isX || manaCost == null || manaCost.canXbe0()));
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
@@ -224,6 +226,16 @@ public class HumanPlaySpellAbility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needX && manaCost != null && manaCost.getAmountOfX() > 0) {
|
||||||
|
Integer value = controller.announceRequirements(ability, "X", allowZero && manaCost.canXbe0());
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ability.setSVar("X", value.toString());
|
||||||
|
card.setSVar("X", value.toString());
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user