diff --git a/.gitattributes b/.gitattributes index 9bed6e42fcb..9df6a326203 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13814,10 +13814,11 @@ src/main/java/forge/control/input/InputPayManaOfCostPayment.java -text src/main/java/forge/control/input/InputPayManaSimple.java svneol=native#text/plain src/main/java/forge/control/input/InputPayManaX.java -text src/main/java/forge/control/input/InputPayReturnCost.java -text -src/main/java/forge/control/input/InputPaySacCost.java -text src/main/java/forge/control/input/InputPayment.java -text -src/main/java/forge/control/input/InputSelectMany.java -text -src/main/java/forge/control/input/InputSelectManyCards.java -text +src/main/java/forge/control/input/InputSelectCards.java -text +src/main/java/forge/control/input/InputSelectCardsFromList.java -text +src/main/java/forge/control/input/InputSelectList.java -text +src/main/java/forge/control/input/InputSelectListBase.java -text src/main/java/forge/control/input/InputSynchronized.java -text src/main/java/forge/control/input/InputSyncronizedBase.java -text src/main/java/forge/control/input/package-info.java svneol=native#text/plain diff --git a/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java b/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java index 4000848d241..94cbbbf544d 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java @@ -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 diff --git a/src/main/java/forge/card/cardfactory/CardFactoryLands.java b/src/main/java/forge/card/cardfactory/CardFactoryLands.java index db970068b66..3e55363d4b1 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryLands.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryLands.java @@ -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) { diff --git a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java index 30c711189aa..99ae2c4e016 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java @@ -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 oppCreats = CardLists.filterControlledBy(creats, card.getController().getOpponent()); diff --git a/src/main/java/forge/control/input/InputPayDiscardCostWithCommands.java b/src/main/java/forge/control/input/InputPayDiscardCostWithCommands.java index d7c7b6313d5..d88ebc9dc19 100644 --- a/src/main/java/forge/control/input/InputPayDiscardCostWithCommands.java +++ b/src/main/java/forge/control/input/InputPayDiscardCostWithCommands.java @@ -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(); - } - } - - /** - *

- * unselectCard. - *

- * - * @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); } - } - - /** - *

- * executes paid commmand. - *

- * - */ - 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(); - } - - /** - *

- * executes unpaid commmand. - *

- * - */ - public void cancel() { - for (Card selected : this.discardCost.getList()) { - selected.setUsedToPay(false); - } - bPaid = false; - this.stop(); + this.showMessage(); } } diff --git a/src/main/java/forge/control/input/InputPaySacCost.java b/src/main/java/forge/control/input/InputPaySacCost.java deleted file mode 100644 index e380f00e0b7..00000000000 --- a/src/main/java/forge/control/input/InputPaySacCost.java +++ /dev/null @@ -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 . - */ -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 - -/** - *

- * Input_PayManaCost_Ability class. - *

- * - * @author Forge - * @version $Id: InputPayManaCostAbility.java 15673 2012-05-23 14:01:35Z ArsenalNut $ - */ -public class InputPaySacCost extends InputBase { - /** - * Constant serialVersionUID=2685832214529141991L. - */ - private static final long serialVersionUID = 2685832214529141991L; - - private int numChosen = 0; - private int numRequired = 0; - private List choiceList; - private CostSacrifice sacCost; - private SpellAbility ability; - private Command paid; - private Command unpaid; - - /** - *

- * Constructor for Input_PayManaCost_Ability. - *

- * - * @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(); - } - } - - /** - *

- * unselectCard. - *

- * - * @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(); - } - } - - /** - *

- * executes paid commmand. - *

- * - */ - 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(); - } - - /** - *

- * executes unpaid commmand. - *

- * - */ - public void cancel() { - this.stop(); - for (Card selected : this.sacCost.getList()) { - selected.setUsedToPay(false); - } - this.unpaid.execute(); - } -} diff --git a/src/main/java/forge/control/input/InputSelectCards.java b/src/main/java/forge/control/input/InputSelectCards.java new file mode 100644 index 00000000000..af9fd4a032b --- /dev/null +++ b/src/main/java/forge/control/input/InputSelectCards.java @@ -0,0 +1,37 @@ +package forge.control.input; + +import forge.Card; + +public abstract class InputSelectCards extends InputSelectListBase { + 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! + + } + +} diff --git a/src/main/java/forge/control/input/InputSelectCardsFromList.java b/src/main/java/forge/control/input/InputSelectCardsFromList.java new file mode 100644 index 00000000000..da88c150130 --- /dev/null +++ b/src/main/java/forge/control/input/InputSelectCardsFromList.java @@ -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 validChoices; + + public InputSelectCardsFromList(int min, int max, List validCards) { + super(min, max); + this.validChoices = validCards; + } + + @Override + protected final boolean isValidChoice(Card choice) { + return validChoices.contains(choice); + } + +} \ No newline at end of file diff --git a/src/main/java/forge/control/input/InputSelectList.java b/src/main/java/forge/control/input/InputSelectList.java new file mode 100644 index 00000000000..9d8aafe13e5 --- /dev/null +++ b/src/main/java/forge/control/input/InputSelectList.java @@ -0,0 +1,12 @@ +package forge.control.input; + +import java.util.List; + +/** + * TODO: Write javadoc for this type. + * + */ +public interface InputSelectList extends InputSynchronized { + boolean hasCancelled(); + List getSelected(); +} \ No newline at end of file diff --git a/src/main/java/forge/control/input/InputSelectMany.java b/src/main/java/forge/control/input/InputSelectListBase.java similarity index 58% rename from src/main/java/forge/control/input/InputSelectMany.java rename to src/main/java/forge/control/input/InputSelectListBase.java index e304a85405d..0e4c5908fe4 100644 --- a/src/main/java/forge/control/input/InputSelectMany.java +++ b/src/main/java/forge/control/input/InputSelectListBase.java @@ -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 extends InputBase { +public abstract class InputSelectListBase extends InputSyncronizedBase implements InputSelectList { private static final long serialVersionUID = -2305549394512889450L; - protected final List selected = new ArrayList(); + protected final List 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(); if (min > max) { throw new IllegalArgumentException("Min must not be greater than Max"); } @@ -35,7 +36,7 @@ public abstract class InputSelectMany 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 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 getSelected() { + return selected; } @Override @@ -77,8 +87,6 @@ public abstract class InputSelectMany 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 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 ; } } diff --git a/src/main/java/forge/control/input/InputSelectManyCards.java b/src/main/java/forge/control/input/InputSelectManyCards.java deleted file mode 100644 index 792ed5555a7..00000000000 --- a/src/main/java/forge/control/input/InputSelectManyCards.java +++ /dev/null @@ -1,17 +0,0 @@ -package forge.control.input; - -import forge.Card; - -public abstract class InputSelectManyCards extends InputSelectMany { - 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); - } -} diff --git a/src/main/java/forge/game/GameActionPlay.java b/src/main/java/forge/game/GameActionPlay.java index 88c3aabbc33..ba265b8888e 100644 --- a/src/main/java/forge/game/GameActionPlay.java +++ b/src/main/java/forge/game/GameActionPlay.java @@ -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 diff --git a/src/main/java/forge/game/phase/Upkeep.java b/src/main/java/forge/game/phase/Upkeep.java index bfe4e542816..53646b173a1 100644 --- a/src/main/java/forge/game/phase/Upkeep.java +++ b/src/main/java/forge/game/phase/Upkeep.java @@ -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 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 indestruct = CardLists.getKeyword(targets, "Indestructible"); diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index c63af118b8c..4cf8079b408 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -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 choosePermanentsToSacrifice(List validTargets, int amount, SpellAbility sa, boolean destroy, boolean isOptional) { - List result = new ArrayList(); + int max = Math.min(amount, validTargets.size()); + if (max == 0) + return new ArrayList(); - 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(); + else return inp.getSelected(); } @Override diff --git a/src/main/java/forge/game/zone/MagicStack.java b/src/main/java/forge/game/zone/MagicStack.java index a8f2e6b9e98..70d687f17e0 100644 --- a/src/main/java/forge/game/zone/MagicStack.java +++ b/src/main/java/forge/game/zone/MagicStack.java @@ -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 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 diff --git a/src/main/java/forge/gui/GuiDisplayUtil.java b/src/main/java/forge/gui/GuiDisplayUtil.java index 3c4ac39a8bb..543e5506373 100644 --- a/src/main/java/forge/gui/GuiDisplayUtil.java +++ b/src/main/java/forge/gui/GuiDisplayUtil.java @@ -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); + } + }); } diff --git a/src/main/java/forge/gui/match/nonsingleton/CField.java b/src/main/java/forge/gui/match/nonsingleton/CField.java index 93dbc735ce2..8a7dbd35b57 100644 --- a/src/main/java/forge/gui/match/nonsingleton/CField.java +++ b/src/main/java/forge/gui/match/nonsingleton/CField.java @@ -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 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 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); + } + } /** */