mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
InputSelectCards made synchronous, select cards to sacrifice uses that input instead on GuiChoose...
Added check to MagicStack add to prevent spells being added by EDT.
This commit is contained in:
@@ -29,6 +29,7 @@ import com.google.common.collect.Iterables;
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.CardPredicates;
|
||||
import forge.FThreads;
|
||||
import forge.CardPredicates.Presets;
|
||||
import forge.Command;
|
||||
import forge.CounterType;
|
||||
@@ -44,8 +45,7 @@ import forge.card.spellability.SpellPermanent;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.card.trigger.Trigger;
|
||||
import forge.card.trigger.TriggerHandler;
|
||||
import forge.control.input.InputBase;
|
||||
import forge.control.input.InputSelectManyCards;
|
||||
import forge.control.input.InputSelectCards;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.event.TokenCreatedEvent;
|
||||
@@ -477,59 +477,51 @@ public class CardFactoryCreatures {
|
||||
private static void getCard_PhyrexianDreadnought(final Card card, final String cardName) {
|
||||
final Player player = card.getController();
|
||||
|
||||
final InputBase target = new InputSelectManyCards(0, Integer.MAX_VALUE) {
|
||||
private static final long serialVersionUID = 2698036349873486664L;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String toDisplay = cardName + " - Select any number of creatures to sacrifice. ";
|
||||
toDisplay += "Currently, (" + selected.size() + ") selected with a total power of: "
|
||||
+ getTotalPower();
|
||||
toDisplay += " Click OK when Done.";
|
||||
return toDisplay;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canCancelWithSomethingSelected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancel() {
|
||||
Singletons.getModel().getGame().getAction().sacrifice(card, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidChoice(Card c) {
|
||||
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
|
||||
return c.isCreature() && zone.is(ZoneType.Battlefield, player);
|
||||
} // selectCard()
|
||||
|
||||
@Override
|
||||
protected void onDone() {
|
||||
for (final Card sac : selected) {
|
||||
Singletons.getModel().getGame().getAction().sacrifice(sac, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasEnoughTargets() {
|
||||
return getTotalPower() >= 12;
|
||||
};
|
||||
|
||||
private int getTotalPower() {
|
||||
int sum = 0;
|
||||
for (final Card c : selected) {
|
||||
sum += c.getNetAttack();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}; // Input
|
||||
|
||||
final Ability sacOrSac = new Ability(card, ManaCost.NO_COST) {
|
||||
@Override
|
||||
public void resolve() {
|
||||
if (player.isHuman()) {
|
||||
final InputSelectCards target = new InputSelectCards(0, Integer.MAX_VALUE) {
|
||||
private static final long serialVersionUID = 2698036349873486664L;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String toDisplay = cardName + " - Select any number of creatures to sacrifice. ";
|
||||
toDisplay += "Currently, (" + selected.size() + ") selected with a total power of: " + getTotalPower();
|
||||
toDisplay += " Click OK when Done.";
|
||||
return toDisplay;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidChoice(Card c) {
|
||||
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
|
||||
return c.isCreature() && zone.is(ZoneType.Battlefield, player);
|
||||
} // selectCard()
|
||||
|
||||
@Override
|
||||
protected boolean hasEnoughTargets() {
|
||||
return getTotalPower() >= 12;
|
||||
};
|
||||
|
||||
private int getTotalPower() {
|
||||
int sum = 0;
|
||||
for (final Card c : selected) {
|
||||
sum += c.getNetAttack();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}; // Input
|
||||
|
||||
target.setCancelWithSelectedAllowed(true);
|
||||
FThreads.setInputAndWait(target);
|
||||
if(!target.hasCancelled()) {
|
||||
for (final Card sac : target.getSelected()) {
|
||||
Singletons.getModel().getGame().getAction().sacrifice(sac, null);
|
||||
}
|
||||
} else {
|
||||
Singletons.getModel().getGame().getAction().sacrifice(card, null);
|
||||
}
|
||||
|
||||
Singletons.getModel().getMatch().getInput().setInput(target);
|
||||
}
|
||||
} // end resolve
|
||||
|
||||
@@ -24,8 +24,9 @@ import javax.swing.JOptionPane;
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.Command;
|
||||
import forge.FThreads;
|
||||
import forge.Singletons;
|
||||
import forge.control.input.InputSelectManyCards;
|
||||
import forge.control.input.InputSelectCards;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -45,7 +46,7 @@ class CardFactoryLands {
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
private static final class InputRevealCardType extends InputSelectManyCards {
|
||||
private static final class InputRevealCardType extends InputSelectCards {
|
||||
private final String type;
|
||||
private final Card card;
|
||||
private static final long serialVersionUID = -2774066137824255680L;
|
||||
@@ -73,21 +74,6 @@ class CardFactoryLands {
|
||||
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
|
||||
return zone.is(ZoneType.Hand) && c.isType(type) && c.getController() == card.getController();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDone() {
|
||||
if (selected.isEmpty()) {
|
||||
onCancel();
|
||||
}
|
||||
|
||||
String cardName = selected.get(0).getName();
|
||||
JOptionPane.showMessageDialog(null, "Revealed card: " + cardName, cardName, JOptionPane.PLAIN_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
card.setTapped(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +212,16 @@ class CardFactoryLands {
|
||||
}
|
||||
|
||||
public void humanExecute() {
|
||||
Singletons.getModel().getMatch().getInput().setInput(new InputRevealCardType(0, 1, type, card));
|
||||
InputSelectCards inp = new InputRevealCardType(0, 1, type, card);
|
||||
FThreads.setInputAndWait(inp);
|
||||
|
||||
if ( inp.hasCancelled() || inp.getSelected().isEmpty() ) {
|
||||
card.setTapped(true);
|
||||
} else {
|
||||
String cardName = inp.getSelected().get(0).getName();
|
||||
JOptionPane.showMessageDialog(null, "Revealed card: " + cardName, cardName, JOptionPane.PLAIN_MESSAGE);
|
||||
}
|
||||
|
||||
} // execute()
|
||||
|
||||
private void revealCard(final Card c) {
|
||||
|
||||
@@ -38,6 +38,7 @@ import forge.CardUtil;
|
||||
import forge.Command;
|
||||
import forge.Constant;
|
||||
import forge.CounterType;
|
||||
import forge.FThreads;
|
||||
import forge.GameEntity;
|
||||
import forge.Singletons;
|
||||
import forge.card.ability.AbilityFactory;
|
||||
@@ -64,7 +65,7 @@ import forge.card.trigger.TriggerHandler;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.control.input.Input;
|
||||
import forge.control.input.InputBase;
|
||||
import forge.control.input.InputSelectManyCards;
|
||||
import forge.control.input.InputSelectCards;
|
||||
import forge.game.GameState;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
@@ -3152,26 +3153,6 @@ public class CardFactoryUtil {
|
||||
};
|
||||
haunterDiesWork.setDescription(hauntDescription);
|
||||
|
||||
final InputSelectManyCards target = new InputSelectManyCards(1, 1) {
|
||||
private static final long serialVersionUID = 1981791992623774490L;
|
||||
|
||||
@Override
|
||||
protected void onDone() {
|
||||
haunterDiesWork.setTargetCard(selected.get(0));
|
||||
Singletons.getModel().getGame().getStack().add(haunterDiesWork);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidChoice(Card c) {
|
||||
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
|
||||
if (!zone.is(ZoneType.Battlefield) || !c.isCreature()) {
|
||||
return false;
|
||||
}
|
||||
return c.canBeTargetedBy(haunterDiesWork);
|
||||
}
|
||||
};
|
||||
target.setMessage("Choose target creature to haunt.");
|
||||
|
||||
final Ability haunterDiesSetup = new Ability(card, ManaCost.ZERO) {
|
||||
@Override
|
||||
public void resolve() {
|
||||
@@ -3189,7 +3170,25 @@ public class CardFactoryUtil {
|
||||
// need to do it this way because I don't know quite how to
|
||||
// make TriggerHandler respect BeforePayMana.
|
||||
if (card.getController().isHuman()) {
|
||||
Singletons.getModel().getMatch().getInput().setInput(target);
|
||||
|
||||
final InputSelectCards target = new InputSelectCards(1, 1) {
|
||||
private static final long serialVersionUID = 1981791992623774490L;
|
||||
@Override
|
||||
protected boolean isValidChoice(Card c) {
|
||||
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
|
||||
if (!zone.is(ZoneType.Battlefield) || !c.isCreature()) {
|
||||
return false;
|
||||
}
|
||||
return c.canBeTargetedBy(haunterDiesWork);
|
||||
}
|
||||
};
|
||||
target.setMessage("Choose target creature to haunt.");
|
||||
|
||||
FThreads.setInputAndWait(target);
|
||||
if (!target.hasCancelled()) {
|
||||
haunterDiesWork.setTargetCard(target.getSelected().get(0));
|
||||
Singletons.getModel().getGame().getStack().add(haunterDiesWork);
|
||||
}
|
||||
} else {
|
||||
// AI choosing what to haunt
|
||||
final List<Card> oppCreats = CardLists.filterControlledBy(creats, card.getController().getOpponent());
|
||||
|
||||
@@ -25,7 +25,6 @@ import forge.card.cardfactory.CardFactoryUtil;
|
||||
import forge.card.cost.CostDiscard;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.view.ButtonUtil;
|
||||
@@ -108,74 +107,40 @@ public class InputPayDiscardCostWithCommands extends InputSyncronizedBase implem
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void selectButtonCancel() {
|
||||
this.cancel();
|
||||
for (Card selected : this.discardCost.getList()) {
|
||||
selected.setUsedToPay(false);
|
||||
}
|
||||
this.bPaid = false;
|
||||
this.stop();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void selectButtonOK() {
|
||||
this.done();
|
||||
for (Card selected : this.discardCost.getList()) {
|
||||
selected.setUsedToPay(false);
|
||||
Singletons.getControl().getPlayer().discard(selected, this.ability);
|
||||
}
|
||||
this.discardCost.addListToHash(this.ability, "Discarded");
|
||||
this.bPaid = true;
|
||||
this.stop();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void selectCard(final Card card) {
|
||||
if (this.choiceList.contains(card) && this.numChosen < numRequired) {
|
||||
this.numChosen++;
|
||||
this.discardCost.addToList(card);
|
||||
card.setUsedToPay(true);
|
||||
this.choiceList.remove(card);
|
||||
this.showMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* unselectCard.
|
||||
* </p>
|
||||
*
|
||||
* @param card
|
||||
* a {@link forge.Card} object.
|
||||
* @param zone
|
||||
* a {@link forge.game.zone.PlayerZone} object.
|
||||
*/
|
||||
public void unselectCard(final Card card, final PlayerZone zone) {
|
||||
if (this.discardCost.getList().contains(card)) {
|
||||
this.numChosen--;
|
||||
card.setUsedToPay(false);
|
||||
this.discardCost.getList().remove(card);
|
||||
this.choiceList.add(card);
|
||||
this.showMessage();
|
||||
}
|
||||
else if (this.choiceList.contains(card) && this.numChosen < numRequired) {
|
||||
this.numChosen++;
|
||||
this.discardCost.addToList(card);
|
||||
card.setUsedToPay(true);
|
||||
this.choiceList.remove(card);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* executes paid commmand.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public void done() {
|
||||
for (Card selected : this.discardCost.getList()) {
|
||||
selected.setUsedToPay(false);
|
||||
Singletons.getControl().getPlayer().discard(selected, this.ability);
|
||||
}
|
||||
this.discardCost.addListToHash(ability, "Discarded");
|
||||
bPaid = true;
|
||||
this.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* executes unpaid commmand.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public void cancel() {
|
||||
for (Card selected : this.discardCost.getList()) {
|
||||
selected.setUsedToPay(false);
|
||||
}
|
||||
bPaid = false;
|
||||
this.stop();
|
||||
this.showMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2011 Forge Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.control.input;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.Command;
|
||||
import forge.Singletons;
|
||||
import forge.card.cardfactory.CardFactoryUtil;
|
||||
import forge.card.cost.CostSacrifice;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.view.ButtonUtil;
|
||||
|
||||
//if cost is paid, Command.execute() is called
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Input_PayManaCost_Ability class.
|
||||
* </p>
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: InputPayManaCostAbility.java 15673 2012-05-23 14:01:35Z ArsenalNut $
|
||||
*/
|
||||
public class InputPaySacCost extends InputBase {
|
||||
/**
|
||||
* Constant <code>serialVersionUID=2685832214529141991L</code>.
|
||||
*/
|
||||
private static final long serialVersionUID = 2685832214529141991L;
|
||||
|
||||
private int numChosen = 0;
|
||||
private int numRequired = 0;
|
||||
private List<Card> choiceList;
|
||||
private CostSacrifice sacCost;
|
||||
private SpellAbility ability;
|
||||
private Command paid;
|
||||
private Command unpaid;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for Input_PayManaCost_Ability.
|
||||
* </p>
|
||||
*
|
||||
* @param cost
|
||||
* a {@link forge.card.cost.CostSacrifice} object.
|
||||
* @param sa
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
* @param paidCommand
|
||||
* a {@link forge.Command} object.
|
||||
* @param unpaidCommand
|
||||
* a {@link forge.Command} object.
|
||||
*/
|
||||
public InputPaySacCost(final CostSacrifice cost, final SpellAbility sa, final Command paidCommand,
|
||||
final Command unpaidCommand) {
|
||||
final Card source = sa.getSourceCard();
|
||||
|
||||
this.ability = sa;
|
||||
this.sacCost = cost;
|
||||
Player human = Singletons.getControl().getPlayer();
|
||||
this.choiceList = CardLists.getValidCards(human.getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), human, source);
|
||||
String amountString = cost.getAmount();
|
||||
this.numRequired = amountString.matches("[0-9][0-9]?") ? Integer.parseInt(amountString)
|
||||
: CardFactoryUtil.xCount(source, source.getSVar(amountString));
|
||||
this.paid = paidCommand;
|
||||
this.unpaid = unpaidCommand;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void showMessage() {
|
||||
final StringBuilder msg = new StringBuilder("Sacrifice ");
|
||||
final int nLeft = this.numRequired - this.numChosen;
|
||||
msg.append(nLeft).append(" ");
|
||||
msg.append(this.sacCost.getDescriptiveType());
|
||||
if (nLeft > 1) {
|
||||
msg.append("s");
|
||||
}
|
||||
if (!this.sacCost.getList().isEmpty()) {
|
||||
msg.append("\r\nSelected:\r\n");
|
||||
for (Card selected : this.sacCost.getList()) {
|
||||
msg.append(selected + "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString());
|
||||
if (nLeft > 0) {
|
||||
ButtonUtil.enableOnlyCancel();
|
||||
}
|
||||
else {
|
||||
ButtonUtil.enableAllFocusOk();
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void selectButtonCancel() {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void selectButtonOK() {
|
||||
this.done();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void selectCard(final Card card) {
|
||||
if (this.choiceList.contains(card) && this.numChosen < numRequired) {
|
||||
this.numChosen++;
|
||||
this.sacCost.addToList(card);
|
||||
card.setUsedToPay(true);
|
||||
this.choiceList.remove(card);
|
||||
this.showMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* unselectCard.
|
||||
* </p>
|
||||
*
|
||||
* @param card
|
||||
* a {@link forge.Card} object.
|
||||
* @param zone
|
||||
* a {@link forge.game.zone.PlayerZone} object.
|
||||
*/
|
||||
public void unselectCard(final Card card, final PlayerZone zone) {
|
||||
if (this.sacCost.getList().contains(card)) {
|
||||
this.numChosen--;
|
||||
card.setUsedToPay(false);
|
||||
this.sacCost.getList().remove(card);
|
||||
this.choiceList.add(card);
|
||||
this.showMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* executes paid commmand.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public void done() {
|
||||
this.stop();
|
||||
// actually sacrifice the cards
|
||||
for (Card selected : this.sacCost.getList()) {
|
||||
selected.setUsedToPay(false);
|
||||
Singletons.getModel().getGame().getAction().sacrifice(selected, this.ability);
|
||||
}
|
||||
this.sacCost.addListToHash(ability, "Sacrificed");
|
||||
this.paid.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* executes unpaid commmand.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
public void cancel() {
|
||||
this.stop();
|
||||
for (Card selected : this.sacCost.getList()) {
|
||||
selected.setUsedToPay(false);
|
||||
}
|
||||
this.unpaid.execute();
|
||||
}
|
||||
}
|
||||
37
src/main/java/forge/control/input/InputSelectCards.java
Normal file
37
src/main/java/forge/control/input/InputSelectCards.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package forge.control.input;
|
||||
|
||||
import forge.Card;
|
||||
|
||||
public abstract class InputSelectCards extends InputSelectListBase<Card> {
|
||||
private static final long serialVersionUID = -6609493252672573139L;
|
||||
|
||||
protected InputSelectCards(int min, int max) {
|
||||
|
||||
super(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void selectCard(final Card c) {
|
||||
selectEntity(c);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.control.input.InputSelectListBase#onSelectStateChanged(forge.GameEntity, boolean)
|
||||
*/
|
||||
@Override
|
||||
protected void onSelectStateChanged(Card c, boolean newState) {
|
||||
c.setUsedToPay(newState); // UI supports card highlighting though this abstraction-breaking mechanism
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.control.input.InputSyncronizedBase#afterStop()
|
||||
*/
|
||||
@Override
|
||||
protected void afterStop() {
|
||||
for(Card c : selected)
|
||||
c.setUsedToPay(false);
|
||||
super.afterStop(); // It's ultimatelly important to keep call to super class!
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package forge.control.input;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import forge.Card;
|
||||
|
||||
public class InputSelectCardsFromList extends InputSelectCards {
|
||||
private static final long serialVersionUID = 6230360322294805986L;
|
||||
|
||||
private final List<Card> validChoices;
|
||||
|
||||
public InputSelectCardsFromList(int min, int max, List<Card> validCards) {
|
||||
super(min, max);
|
||||
this.validChoices = validCards;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean isValidChoice(Card choice) {
|
||||
return validChoices.contains(choice);
|
||||
}
|
||||
|
||||
}
|
||||
12
src/main/java/forge/control/input/InputSelectList.java
Normal file
12
src/main/java/forge/control/input/InputSelectList.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package forge.control.input;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public interface InputSelectList<T> extends InputSynchronized {
|
||||
boolean hasCancelled();
|
||||
List<T> getSelected();
|
||||
}
|
||||
@@ -7,22 +7,23 @@ import forge.GameEntity;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.view.ButtonUtil;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public abstract class InputSelectMany<T extends GameEntity> extends InputBase {
|
||||
public abstract class InputSelectListBase<T extends GameEntity> extends InputSyncronizedBase implements InputSelectList<T> {
|
||||
|
||||
private static final long serialVersionUID = -2305549394512889450L;
|
||||
|
||||
protected final List<T> selected = new ArrayList<T>();
|
||||
protected final List<T> selected;
|
||||
protected boolean bCancelled = false;
|
||||
protected final int min;
|
||||
protected final int max;
|
||||
|
||||
public boolean allowUnselect = false;
|
||||
private boolean allowCancelWithNotEmptyList = false;
|
||||
|
||||
private String message = "Source-Card-Name - Select %d more card(s)";
|
||||
|
||||
protected InputSelectMany(int min, int max) {
|
||||
|
||||
|
||||
protected InputSelectListBase(int min, int max) {
|
||||
selected = new ArrayList<T>();
|
||||
if (min > max) {
|
||||
throw new IllegalArgumentException("Min must not be greater than Max");
|
||||
}
|
||||
@@ -35,7 +36,7 @@ public abstract class InputSelectMany<T extends GameEntity> extends InputBase {
|
||||
String msgToShow = getMessage();
|
||||
CMatchUI.SINGLETON_INSTANCE.showMessage(msgToShow);
|
||||
|
||||
boolean canCancel = (min == 0 && selected.isEmpty()) || canCancelWithSomethingSelected();
|
||||
boolean canCancel = (min == 0 && selected.isEmpty()) || isCancelWithSelectedAllowed();
|
||||
boolean canOk = hasEnoughTargets();
|
||||
|
||||
if (canOk && canCancel) {
|
||||
@@ -65,9 +66,18 @@ public abstract class InputSelectMany<T extends GameEntity> extends InputBase {
|
||||
|
||||
@Override
|
||||
public final void selectButtonCancel() {
|
||||
bCancelled = true;
|
||||
this.stop();
|
||||
// for a next use
|
||||
selected.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean hasCancelled() {
|
||||
return bCancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<T> getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,8 +87,6 @@ public abstract class InputSelectMany<T extends GameEntity> extends InputBase {
|
||||
// if it does, uncomment the 5 lines below, use them as method body
|
||||
|
||||
this.stop();
|
||||
// for a next use
|
||||
selected.clear();
|
||||
}
|
||||
|
||||
public void setMessage(String message0) {
|
||||
@@ -86,27 +94,41 @@ public abstract class InputSelectMany<T extends GameEntity> extends InputBase {
|
||||
}
|
||||
|
||||
// must define these
|
||||
protected abstract void onDone();
|
||||
protected abstract boolean isValidChoice(T choice);
|
||||
|
||||
// might re-define later
|
||||
protected void onCancel() {}
|
||||
protected boolean canCancelWithSomethingSelected() { return false; }
|
||||
protected boolean hasEnoughTargets() { return selected.size() >= min; }
|
||||
protected boolean hasAllTargets() { return selected.size() >= max; }
|
||||
protected void onSelectStateChanged(T c, boolean newState) {} // Select card inputs may highlight selected cards with this method
|
||||
|
||||
protected void selectEntity(T c) {
|
||||
|
||||
if (selected.contains(c) || !isValidChoice(c)) {
|
||||
if (!isValidChoice(c)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected.add(c);
|
||||
this.showMessage();
|
||||
|
||||
if ( selected.contains(c) ) {
|
||||
if ( allowUnselect ) {
|
||||
this.selected.remove(c);
|
||||
onSelectStateChanged(c, false);
|
||||
}
|
||||
} else {
|
||||
this.selected.add(c);
|
||||
onSelectStateChanged(c, true);
|
||||
}
|
||||
|
||||
if (hasAllTargets()) {
|
||||
selectButtonOK();
|
||||
} else {
|
||||
this.showMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public final boolean isUnselectAllowed() { return allowUnselect; }
|
||||
public final void setUnselectAllowed(boolean allow) { this.allowUnselect = allow; }
|
||||
|
||||
public final boolean isCancelWithSelectedAllowed() { return allowCancelWithNotEmptyList; }
|
||||
public final void setCancelWithSelectedAllowed(boolean allow) { this.allowCancelWithNotEmptyList = allow ; }
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package forge.control.input;
|
||||
|
||||
import forge.Card;
|
||||
|
||||
public abstract class InputSelectManyCards extends InputSelectMany<Card> {
|
||||
private static final long serialVersionUID = -6609493252672573139L;
|
||||
|
||||
protected InputSelectManyCards(int min, int max) {
|
||||
|
||||
super(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void selectCard(final Card c) {
|
||||
selectEntity(c);
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,7 @@ public class GameActionPlay {
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
*/
|
||||
public final void playSpellAbilityWithoutPayingManaCost(final SpellAbility sa) {
|
||||
FThreads.checkEDT("GameActionPlay.playSpellAbilityWithoutPayingManaCost", false);
|
||||
final Card source = sa.getSourceCard();
|
||||
setSplitCardState(source, sa); // Split card support
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import forge.card.spellability.AbilityStatic;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.control.input.InputBase;
|
||||
import forge.control.input.InputPayManaExecuteCommands;
|
||||
import forge.control.input.InputSelectManyCards;
|
||||
import forge.control.input.InputSelectCards;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameState;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
@@ -460,26 +460,20 @@ public class Upkeep extends Phase {
|
||||
@Override
|
||||
public void resolve() {
|
||||
final List<Card> targets = CardLists.getTargetableCards(abyssGetTargets, this);
|
||||
final InputBase chooseArt = new InputSelectManyCards(1, 1) {
|
||||
private static final long serialVersionUID = 4820011040853968644L;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return abyss.getName() + " - Select one nonartifact creature to destroy";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidChoice(Card choice) {
|
||||
return choice.isCreature() && !choice.isArtifact() && canTarget(choice) && choice.getController() == player;
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onDone() {
|
||||
game.getAction().destroyNoRegeneration(selected.get(0));
|
||||
}
|
||||
};
|
||||
if (player.isHuman() && targets.size() > 0) {
|
||||
Singletons.getModel().getMatch().getInput().setInput(chooseArt); // Input
|
||||
final InputSelectCards chooseArt = new InputSelectCards(1, 1) {
|
||||
private static final long serialVersionUID = 4820011040853968644L;
|
||||
@Override
|
||||
protected boolean isValidChoice(Card choice) {
|
||||
return choice.isCreature() && !choice.isArtifact() && canTarget(choice) && choice.getController() == player;
|
||||
};
|
||||
};
|
||||
chooseArt.setMessage(abyss.getName() + " - Select one nonartifact creature to destroy");
|
||||
FThreads.setInputAndWait(chooseArt); // Input
|
||||
if (!chooseArt.hasCancelled()) {
|
||||
game.getAction().destroyNoRegeneration(chooseArt.getSelected().get(0));
|
||||
}
|
||||
|
||||
} else { // computer
|
||||
|
||||
final List<Card> indestruct = CardLists.getKeyword(targets, "Indestructible");
|
||||
|
||||
@@ -11,12 +11,15 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
import forge.Card;
|
||||
import forge.FThreads;
|
||||
import forge.GameEntity;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.control.input.Input;
|
||||
import forge.control.input.InputBlock;
|
||||
import forge.control.input.InputCleanup;
|
||||
import forge.control.input.InputPassPriority;
|
||||
import forge.control.input.InputSelectCards;
|
||||
import forge.control.input.InputSelectCardsFromList;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
@@ -212,26 +215,18 @@ public class PlayerControllerHuman extends PlayerController {
|
||||
*/
|
||||
@Override
|
||||
public List<Card> choosePermanentsToSacrifice(List<Card> validTargets, int amount, SpellAbility sa, boolean destroy, boolean isOptional) {
|
||||
List<Card> result = new ArrayList<Card>();
|
||||
int max = Math.min(amount, validTargets.size());
|
||||
if (max == 0)
|
||||
return new ArrayList<Card>();
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
if (validTargets.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
Card c;
|
||||
if (isOptional) {
|
||||
c = GuiChoose.oneOrNone("Select a card to sacrifice", validTargets);
|
||||
} else {
|
||||
c = GuiChoose.one("Select a card to sacrifice", validTargets);
|
||||
}
|
||||
if (c != null) {
|
||||
result.add(c);
|
||||
validTargets.remove(c);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
InputSelectCards inp = new InputSelectCardsFromList(isOptional ? 0 : amount, max, validTargets);
|
||||
// TODO: Either compose a message here, or pass it as parameter from caller.
|
||||
inp.setMessage("Select cards to sacrifice");
|
||||
|
||||
FThreads.setInputAndWait(inp);
|
||||
if( inp.hasCancelled() )
|
||||
return new ArrayList<Card>();
|
||||
else return inp.getSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,7 +29,6 @@ import forge.CardLists;
|
||||
import forge.CardPredicates;
|
||||
import forge.FThreads;
|
||||
import forge.CardPredicates.Presets;
|
||||
import forge.Command;
|
||||
import forge.Singletons;
|
||||
import forge.card.ability.AbilityUtils;
|
||||
import forge.card.cardfactory.CardFactory;
|
||||
@@ -393,6 +392,7 @@ public class MagicStack extends MyObservable {
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
*/
|
||||
public final void add(final SpellAbility sp) {
|
||||
FThreads.checkEDT("MagicStack.add", false);
|
||||
final ArrayList<TargetChoices> chosenTargets = sp.getAllTargetChoices();
|
||||
|
||||
if (sp.isManaAbility()) { // Mana Abilities go straight through
|
||||
@@ -530,17 +530,15 @@ public class MagicStack extends MyObservable {
|
||||
|
||||
if (activating.isHuman()) {
|
||||
sa.getSourceCard().addMultiKickerMagnitude(-1);
|
||||
final Command paidCommand = new Command() {
|
||||
private static final long serialVersionUID = -6037161763374971106L;
|
||||
|
||||
final Runnable paidCommand = new Runnable() {
|
||||
@Override
|
||||
public void execute() {
|
||||
public void run() {
|
||||
abilityIncreaseMultikicker.resolve();
|
||||
|
||||
final ManaCostBeingPaid manaCost = MagicStack.this.getMultiKickerSpellCostChange(abilityIncreaseMultikicker);
|
||||
|
||||
if (manaCost.isPaid()) {
|
||||
this.execute();
|
||||
this.run();
|
||||
} else {
|
||||
String prompt;
|
||||
int mkCostPaid = game.getActionPlay().getCostCuttingGetMultiKickerManaCostPaid();
|
||||
@@ -555,13 +553,13 @@ public class MagicStack extends MyObservable {
|
||||
InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(game, prompt, manaCost.toString());
|
||||
FThreads.setInputAndWait(toSet);
|
||||
if ( toSet.isPaid() ) {
|
||||
this.execute();
|
||||
this.run();
|
||||
} else
|
||||
MagicStack.this.push(sa);
|
||||
}
|
||||
}
|
||||
};
|
||||
paidCommand.execute();
|
||||
paidCommand.run();
|
||||
} else {
|
||||
// computer
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ import forge.CardPredicates;
|
||||
import forge.CardUtil;
|
||||
import forge.Constant;
|
||||
import forge.CounterType;
|
||||
import forge.FThreads;
|
||||
import forge.Singletons;
|
||||
import forge.card.CardType;
|
||||
import forge.card.spellability.AbilityManaPart;
|
||||
@@ -611,7 +612,7 @@ public final class GuiDisplayUtil {
|
||||
return;
|
||||
}
|
||||
|
||||
Card forgeCard = c.toForgeCard(p);
|
||||
final Card forgeCard = c.toForgeCard(p);
|
||||
|
||||
final GameState game = Singletons.getModel().getGame();
|
||||
if (forgeCard.getType().contains("Land")) {
|
||||
@@ -628,9 +629,14 @@ public final class GuiDisplayUtil {
|
||||
return; // happens if cancelled
|
||||
}
|
||||
|
||||
sa.setActivatingPlayer(p);
|
||||
game.getAction().moveToHand(forgeCard); // this is really needed
|
||||
game.getActionPlay().playSpellAbilityWithoutPayingManaCost(sa);
|
||||
FThreads.invokeInNewThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sa.setActivatingPlayer(p);
|
||||
game.getAction().moveToHand(forgeCard); // this is really needed
|
||||
game.getActionPlay().playSpellAbilityWithoutPayingManaCost(sa);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,9 +41,6 @@ import forge.control.input.Input;
|
||||
import forge.control.input.InputAttack;
|
||||
import forge.control.input.InputBlock;
|
||||
import forge.control.input.InputPayManaBase;
|
||||
import forge.control.input.InputPayManaExecuteCommands;
|
||||
import forge.control.input.InputPayManaSimple;
|
||||
import forge.control.input.InputPaySacCost;
|
||||
import forge.game.GameState;
|
||||
import forge.game.phase.CombatUtil;
|
||||
import forge.game.player.Player;
|
||||
@@ -56,6 +53,7 @@ import forge.gui.framework.ICDoc;
|
||||
import forge.gui.match.CMatchUI;
|
||||
import forge.gui.match.controllers.CMessage;
|
||||
import forge.gui.toolbox.FLabel;
|
||||
import forge.view.arcane.CardPanel;
|
||||
|
||||
/**
|
||||
* Controls Swing components of a player's field instance.
|
||||
@@ -390,54 +388,53 @@ public class CField implements ICDoc {
|
||||
|
||||
final Input input = CMessage.SINGLETON_INSTANCE.getInputControl().getInput();
|
||||
|
||||
if (c != null && c.isInZone(ZoneType.Battlefield)) {
|
||||
if (c.isTapped() && (input instanceof InputPayManaSimple || input instanceof InputPayManaExecuteCommands)) {
|
||||
final forge.view.arcane.CardPanel cardPanel = CField.this.view.getTabletop().getCardPanel(c.getUniqueNumber());
|
||||
for (final forge.view.arcane.CardPanel cp : cardPanel.getAttachedPanels()) {
|
||||
if (cp.getCard().isUntapped()) {
|
||||
break;
|
||||
}
|
||||
if (c == null || !c.isInZone(ZoneType.Battlefield)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Why does CField filter cards here? That's Input's responsibility to detect incorrect choices!
|
||||
if (c.isTapped() && input instanceof InputPayManaBase) {
|
||||
final CardPanel cardPanel = CField.this.view.getTabletop().getCardPanel(c.getUniqueNumber());
|
||||
for (final CardPanel cp : cardPanel.getAttachedPanels()) {
|
||||
if (cp.getCard().isUntapped()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final List<Card> att = Singletons.getModel().getGame().getCombat().getAttackerList();
|
||||
if ((c.isTapped() || c.hasSickness() || (c.hasKeyword("Vigilance") && att.contains(c))) && (input instanceof InputAttack)) {
|
||||
final forge.view.arcane.CardPanel cardPanel = CField.this.view.getTabletop().getCardPanel(
|
||||
c.getUniqueNumber());
|
||||
for (final forge.view.arcane.CardPanel cp : cardPanel.getAttachedPanels()) {
|
||||
if (cp.getCard().isUntapped() && !cp.getCard().hasSickness()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.isMetaDown()) {
|
||||
if (att.contains(c) && (input instanceof InputAttack)
|
||||
&& !c.hasKeyword("CARDNAME attacks each turn if able.")) {
|
||||
c.untap();
|
||||
Singletons.getModel().getGame().getCombat().removeFromCombat(c);
|
||||
CombatUtil.showCombat();
|
||||
} else if (input instanceof InputBlock) {
|
||||
if (c.getController() == Singletons.getControl().getPlayer() ) {
|
||||
Singletons.getModel().getGame().getCombat().removeFromCombat(c);
|
||||
}
|
||||
((InputBlock) input).removeFromAllBlocking(c);
|
||||
CombatUtil.showCombat();
|
||||
}
|
||||
else if (input instanceof InputPaySacCost) {
|
||||
((InputPaySacCost) input).unselectCard(c, Singletons.getControl().getPlayer().getZone(ZoneType.Battlefield));
|
||||
}
|
||||
} else {
|
||||
//Yosei, the Morning Star required cards to be chosen on computer side
|
||||
//earlier it was enforced that cards must be in player zone
|
||||
//this can potentially break some other functionality
|
||||
//(tapping lands works ok but some custom cards may not...)
|
||||
|
||||
|
||||
//in weird case card has no controller revert to default behaviour
|
||||
input.selectCard(c);
|
||||
}
|
||||
}
|
||||
|
||||
final List<Card> att = Singletons.getModel().getGame().getCombat().getAttackerList();
|
||||
if ((c.isTapped() || c.hasSickness() || (c.hasKeyword("Vigilance") && att.contains(c))) && (input instanceof InputAttack)) {
|
||||
final CardPanel cardPanel = CField.this.view.getTabletop().getCardPanel(c.getUniqueNumber());
|
||||
for (final CardPanel cp : cardPanel.getAttachedPanels()) {
|
||||
if (cp.getCard().isUntapped() && !cp.getCard().hasSickness()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.isMetaDown()) {
|
||||
if (att.contains(c) && input instanceof InputAttack && !c.hasKeyword("CARDNAME attacks each turn if able.")) {
|
||||
c.untap();
|
||||
Singletons.getModel().getGame().getCombat().removeFromCombat(c);
|
||||
CombatUtil.showCombat();
|
||||
} else if (input instanceof InputBlock) {
|
||||
if (c.getController() == Singletons.getControl().getPlayer() ) {
|
||||
Singletons.getModel().getGame().getCombat().removeFromCombat(c);
|
||||
}
|
||||
((InputBlock) input).removeFromAllBlocking(c);
|
||||
CombatUtil.showCombat();
|
||||
}
|
||||
} else {
|
||||
//Yosei, the Morning Star required cards to be chosen on computer side
|
||||
//earlier it was enforced that cards must be in player zone
|
||||
//this can potentially break some other functionality
|
||||
//(tapping lands works ok but some custom cards may not...)
|
||||
|
||||
|
||||
//in weird case card has no controller revert to default behaviour
|
||||
input.selectCard(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** */
|
||||
|
||||
Reference in New Issue
Block a user