diff --git a/.gitattributes b/.gitattributes index 2fb644f330a..2a6ef378e66 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13708,6 +13708,7 @@ src/main/java/forge/card/cost/CostUnattach.java -text src/main/java/forge/card/cost/CostUntap.java -text src/main/java/forge/card/cost/CostUntapType.java -text src/main/java/forge/card/cost/CostUtil.java -text +src/main/java/forge/card/cost/InputPayCostBase.java -text src/main/java/forge/card/cost/package-info.java svneol=native#text/plain src/main/java/forge/card/mana/IParserManaCost.java -text src/main/java/forge/card/mana/Mana.java svneol=native#text/plain @@ -13808,25 +13809,27 @@ src/main/java/forge/control/Lobby.java -text src/main/java/forge/control/RestartUtil.java -text src/main/java/forge/control/bazaar/ControlStall.java -text src/main/java/forge/control/bazaar/package-info.java svneol=native#text/plain -src/main/java/forge/control/input/Input.java svneol=native#text/plain +src/main/java/forge/control/input/Input.java -text src/main/java/forge/control/input/InputAttack.java svneol=native#text/plain +src/main/java/forge/control/input/InputBase.java svneol=native#text/plain src/main/java/forge/control/input/InputBlock.java svneol=native#text/plain src/main/java/forge/control/input/InputCleanup.java svneol=native#text/plain src/main/java/forge/control/input/InputControl.java svneol=native#text/plain src/main/java/forge/control/input/InputLockUI.java -text src/main/java/forge/control/input/InputMulligan.java svneol=native#text/plain src/main/java/forge/control/input/InputPassPriority.java svneol=native#text/plain -src/main/java/forge/control/input/InputPayDiscardCost.java -text src/main/java/forge/control/input/InputPayManaBase.java -text src/main/java/forge/control/input/InputPayManaExecuteCommands.java svneol=native#text/plain 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/InputSelectMany.java -text -src/main/java/forge/control/input/InputSelectManyCards.java -text -src/main/java/forge/control/input/InputSelectManyPlayers.java -text +src/main/java/forge/control/input/InputPayment.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 src/main/java/forge/control/package-info.java -text src/main/java/forge/deck/CardCollections.java -text @@ -13860,7 +13863,6 @@ src/main/java/forge/game/GameActionPlay.java -text src/main/java/forge/game/GameActionUtil.java svneol=native#text/plain src/main/java/forge/game/GameEndReason.java -text src/main/java/forge/game/GameFormat.java -text -src/main/java/forge/game/GameInputUpdatesThread.java -text src/main/java/forge/game/GameLossReason.java -text src/main/java/forge/game/GameNew.java -text src/main/java/forge/game/GameOutcome.java -text @@ -14254,7 +14256,6 @@ src/main/java/forge/util/MyRandom.java svneol=native#text/plain src/main/java/forge/util/PredicateString.java -text src/main/java/forge/util/ReflectionUtil.java -text src/main/java/forge/util/TextUtil.java -text -src/main/java/forge/util/ThreadUtil.java svneol=native#text/plain src/main/java/forge/util/XmlUtil.java -text src/main/java/forge/util/package-info.java -text src/main/java/forge/util/storage/IStorage.java -text diff --git a/.gitignore b/.gitignore index d6ed37de835..f35545ac063 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,14 @@ /pom.xml.releaseBackup /pom.xml.tag /release.properties +res/*.log +res/PerSetTrackingResults +res/cardsfolder/*.bat +res/decks +res/layouts +res/pics* +res/pics_product /target +/test-output tools/PerSetTrackingResults tools/oracleScript.log diff --git a/src/main/java/forge/FThreads.java b/src/main/java/forge/FThreads.java index f35a94923bd..7e0f0bb9caa 100644 --- a/src/main/java/forge/FThreads.java +++ b/src/main/java/forge/FThreads.java @@ -3,25 +3,28 @@ package forge; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import javax.swing.SwingUtilities; -import forge.control.input.InputLockUI; +import forge.control.input.InputSynchronized; /** * TODO: Write javadoc for this type. * */ public class FThreads { - static { System.out.printf("(FThreads static ctor): Running on a machine with %d cpu core(s)%n", Runtime.getRuntime().availableProcessors() ); } + private FThreads() { } // no instances supposed + private final static ExecutorService threadPool = Executors.newCachedThreadPool(); - public static ExecutorService getCachedPool() { - return threadPool; - } + private static ExecutorService getCachedPool() { return threadPool; } + private final static ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(1); + private static ScheduledExecutorService getScheduledPool() { return scheduledPool; } // This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5 public final static ExecutorService getComputingPool(float loadFactor) { @@ -83,27 +86,42 @@ public class FThreads { } - public static void invokeInNewThread(Runnable proc) { - invokeInNewThread(proc, false); + public static void invokeInNewThread(Runnable toRun) { + getCachedPool().execute(toRun); } - private final static InputLockUI inpuptLock = new InputLockUI(); public static void invokeInNewThread(final Runnable proc, boolean lockUI) { Runnable toRun = proc; if( lockUI ) { // checkEDT("FThreads.invokeInNewthread", true) - Singletons.getModel().getMatch().getInput().setInput(inpuptLock); + Singletons.getModel().getMatch().getInput().lock(); toRun = new Runnable() { @Override public void run() { proc.run(); - // may try special unlock method here - Singletons.getModel().getMatch().getInput().resetInput(); + Singletons.getModel().getMatch().getInput().unlock(); } }; } - - getCachedPool().execute(toRun); + invokeInNewThread(toRun); + } + + public static void setInputAndWait(InputSynchronized input) { + Singletons.getModel().getMatch().getInput().setInput(input); + input.awaitLatchRelease(); } + /** + * TODO: Write javadoc for this method. + * @return + */ + public static boolean isEDT() { + return SwingUtilities.isEventDispatchThread(); + } + + + public static void delay(int milliseconds, Runnable inputUpdater) { + getScheduledPool().schedule(inputUpdater, milliseconds, TimeUnit.MILLISECONDS); + } + } diff --git a/src/main/java/forge/card/ability/AbilityUtils.java b/src/main/java/forge/card/ability/AbilityUtils.java index 3cd3126e23b..f00452e5e84 100644 --- a/src/main/java/forge/card/ability/AbilityUtils.java +++ b/src/main/java/forge/card/ability/AbilityUtils.java @@ -10,7 +10,6 @@ import org.apache.commons.lang3.StringUtils; import forge.Card; import forge.CardLists; import forge.CardUtil; -import forge.Command; import forge.Constant; import forge.CounterType; import forge.Singletons; @@ -1036,6 +1035,19 @@ public class AbilityUtils { AbilityUtils.resolveApiAbility(abSub, usedStack, game); } + private static void resolveApiAbility(final SpellAbility sa, boolean usedStack, final GameState game) { + // check conditions + if (sa.getConditions().areMet(sa)) { + if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) { + sa.resolve(); + } else { + handleUnlessCost(sa, usedStack, game); + return; + } + } + resolveSubAbilities(sa, usedStack, game); + } + private static void handleUnlessCost(final SpellAbility sa, final boolean usedStack, final GameState game) { final Card source = sa.getSourceCard(); String unlessCost = sa.getParam("UnlessCost"); @@ -1047,7 +1059,8 @@ public class AbilityUtils { final String resolveSubs = sa.getParam("UnlessResolveSubs"); // no value means 'Always' final boolean execSubsWhenPaid = "WhenPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs); final boolean execSubsWhenNotPaid = "WhenNotPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs); - + final boolean isSwitched = sa.hasParam("UnlessSwitched"); + // The cost if (unlessCost.equals("CardManaCost")) { unlessCost = source.getManaCost().toString(); @@ -1070,43 +1083,6 @@ public class AbilityUtils { //instead of just X for cards like Draco. } - final boolean isSwitched = sa.hasParam("UnlessSwitched"); - - Command paidCommand = new Command() { - private static final long serialVersionUID = 8094833091127334678L; - - @Override - public void execute() { - if (isSwitched && execSubsWhenNotPaid || execSubsWhenPaid) { - resolveSubAbilities(sa, usedStack, game); - } else if (usedStack) { - SpellAbility root = sa.getRootAbility(); - game.getStack().finishResolving(root, false); - } - } - }; - - Command unpaidCommand = new Command() { - private static final long serialVersionUID = 8094833091127334678L; - - @Override - public void execute() { - sa.resolve(); - if (isSwitched && execSubsWhenPaid || execSubsWhenNotPaid) { - resolveSubAbilities(sa, usedStack, game); - } else if (usedStack) { - SpellAbility root = sa.getRootAbility(); - game.getStack().finishResolving(root, false); - } - } - }; - - if (isSwitched) { - final Command dummy = paidCommand; - paidCommand = unpaidCommand; - unpaidCommand = dummy; - } - final Cost cost = new Cost(source, unlessCost, true); final Ability ability = new AbilityStatic(source, cost, null) { @Override @@ -1117,34 +1093,35 @@ public class AbilityUtils { boolean paid = false; for (Player payer : payers) { + ability.setActivatingPlayer(payer); + ability.setTarget(sa.getTarget()); if (payer.isComputer()) { - ability.setActivatingPlayer(payer); if (AbilityUtils.willAIPayForAbility(sa, payer, ability, paid, payers)) { - ability.setTarget(sa.getTarget()); ComputerUtil.playNoStack((AIPlayer) payer, ability, game); // Unless cost was payed - no resolve paid = true; } - } - } - boolean waitForInput = false; - for (Player payer : payers) { - if (payer.isHuman()) { + } else { // if it's paid by the AI already the human can pay, but it won't change anything - if (paid) { - unpaidCommand = paidCommand; - } - ability.setActivatingPlayer(payer); - ability.setTarget(sa.getTarget()); - GameActionUtil.payCostDuringAbilityResolve(payer, ability, cost, paidCommand, unpaidCommand, sa, game); - waitForInput = true; // wait for the human input - break; // multiple human players are not supported + paid |= GameActionUtil.payCostDuringAbilityResolve(payer, ability, cost, sa, game); } } - if (!waitForInput) { - Command toExecute = paid ? paidCommand : unpaidCommand; - toExecute.execute(); - } + if ( paid ^ isSwitched ) { + if (isSwitched && execSubsWhenNotPaid || execSubsWhenPaid) { + resolveSubAbilities(sa, usedStack, game); + } else if (usedStack) { + SpellAbility root = sa.getRootAbility(); + game.getStack().finishResolving(root, false); + } + } else { + sa.resolve(); + if (isSwitched && execSubsWhenPaid || execSubsWhenNotPaid) { + resolveSubAbilities(sa, usedStack, game); + } else if (usedStack) { + SpellAbility root = sa.getRootAbility(); + game.getStack().finishResolving(root, false); + } + } } /** @@ -1221,19 +1198,6 @@ public class AbilityUtils { return false; } - private static void resolveApiAbility(final SpellAbility sa, boolean usedStack, final GameState game) { - // check conditions - if (sa.getConditions().areMet(sa)) { - if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) { - sa.resolve(); - } else { - handleUnlessCost(sa, usedStack, game); - return; - } - } - resolveSubAbilities(sa, usedStack, game); - } - /** *

* Parse non-mana X variables. diff --git a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java index d0aaa3ff350..d7a73922bf8 100644 --- a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java +++ b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java @@ -26,7 +26,6 @@ import forge.card.cost.CostDiscard; import forge.card.cost.CostPart; import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; -import forge.card.spellability.SpellPermanent; import forge.card.spellability.Target; import forge.card.trigger.TriggerType; import forge.game.GlobalRuleChange; diff --git a/src/main/java/forge/card/ability/ai/DrawAi.java b/src/main/java/forge/card/ability/ai/DrawAi.java index 896dbc28131..c77833996f0 100644 --- a/src/main/java/forge/card/ability/ai/DrawAi.java +++ b/src/main/java/forge/card/ability/ai/DrawAi.java @@ -19,7 +19,6 @@ package forge.card.ability.ai; import java.util.ArrayList; -import java.util.List; import java.util.Random; import forge.Card; @@ -72,8 +71,7 @@ public class DrawAi extends SpellAbilityAi { if (part instanceof CostDiscard) { CostDiscard cd = (CostDiscard) part; cd.decideAIPayment((AIPlayer) ai, sa, sa.getSourceCard(), null); - List discards = cd.getList(); - for (Card discard : discards) { + for (Card discard : cd.getList()) { if (!ComputerUtil.isWorseThanDraw(ai, discard)) { return false; } diff --git a/src/main/java/forge/card/ability/effects/CountersProliferateEffect.java b/src/main/java/forge/card/ability/effects/CountersProliferateEffect.java index 908f28930bb..9979716d4da 100644 --- a/src/main/java/forge/card/ability/effects/CountersProliferateEffect.java +++ b/src/main/java/forge/card/ability/effects/CountersProliferateEffect.java @@ -14,7 +14,7 @@ import forge.Singletons; import forge.card.ability.SpellAbilityEffect; import forge.card.cardfactory.CardFactoryUtil; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; +import forge.control.input.InputBase; import forge.game.player.Player; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; @@ -45,7 +45,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect { private static void resolveHuman(final SpellAbility sa) { final List unchosen = Lists.newArrayList(Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield)); final List players = new ArrayList(Singletons.getModel().getGame().getPlayers()); - Singletons.getModel().getMatch().getInput().setInput(new Input() { + Singletons.getModel().getMatch().getInput().setInput(new InputBase() { private static final long serialVersionUID = -1779224307654698954L; @Override diff --git a/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java b/src/main/java/forge/card/cardfactory/CardFactoryCreatures.java index 40856e3aa3c..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.Input; -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,61 +477,51 @@ public class CardFactoryCreatures { private static void getCard_PhyrexianDreadnought(final Card card, final String cardName) { final Player player = card.getController(); - final Input 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 Input onCancel() { - Singletons.getModel().getGame().getAction().sacrifice(card, null); - return 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 Input onDone() { - for (final Card sac : selected) { - Singletons.getModel().getGame().getAction().sacrifice(sac, null); - } - return 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 6064b2dcfe3..3e55363d4b1 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryLands.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryLands.java @@ -24,9 +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.Input; -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; @@ -46,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; @@ -74,23 +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 Input onDone() { - if (selected.isEmpty()) { - return onCancel(); - } - - String cardName = selected.get(0).getName(); - JOptionPane.showMessageDialog(null, "Revealed card: " + cardName, cardName, JOptionPane.PLAIN_MESSAGE); - return null; - } - - @Override - public Input onCancel() { - card.setTapped(true); - return null; - } } /** @@ -229,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/CardFactorySorceries.java b/src/main/java/forge/card/cardfactory/CardFactorySorceries.java index d79de33be2d..72d2b6dbd3b 100644 --- a/src/main/java/forge/card/cardfactory/CardFactorySorceries.java +++ b/src/main/java/forge/card/cardfactory/CardFactorySorceries.java @@ -30,12 +30,13 @@ 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.Constant; import forge.Singletons; import forge.card.CardType; import forge.card.cost.Cost; +import forge.card.mana.ManaCost; import forge.card.spellability.Spell; import forge.card.spellability.SpellAbility; import forge.control.input.InputPayManaExecuteCommands; @@ -460,22 +461,13 @@ public class CardFactorySorceries { if (newCMC <= baseCMC) { game.getAction().moveToPlay(newArtifact[0]); } else { - final String diffCost = String.valueOf(newCMC - baseCMC); - Singletons.getModel().getMatch().getInput().setInput(new InputPayManaExecuteCommands(game, "Pay difference in artifacts CMC", diffCost, new Command() { - private static final long serialVersionUID = -8729850321341068049L; - - @Override - public void execute() { - Singletons.getModel().getGame().getAction().moveToPlay(newArtifact[0]); - } - }, new Command() { - private static final long serialVersionUID = -246036834856971935L; - - @Override - public void execute() { - Singletons.getModel().getGame().getAction().moveToGraveyard(newArtifact[0]); - } - })); + final int diffCost = newCMC - baseCMC; + InputPayManaExecuteCommands inp = new InputPayManaExecuteCommands(game, "Pay difference in artifacts CMC", ManaCost.get(diffCost)); + FThreads.setInputAndWait(inp); + if ( inp.isPaid() ) + game.getAction().moveToPlay(newArtifact[0]); + else + game.getAction().moveToGraveyard(newArtifact[0]); } // finally, shuffle library diff --git a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java index 981093bb98d..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; @@ -63,7 +64,8 @@ import forge.card.trigger.Trigger; import forge.card.trigger.TriggerHandler; import forge.card.trigger.TriggerType; import forge.control.input.Input; -import forge.control.input.InputSelectManyCards; +import forge.control.input.InputBase; +import forge.control.input.InputSelectCards; import forge.game.GameState; import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtilCard; @@ -98,10 +100,10 @@ public class CardFactoryUtil { * a {@link forge.CardList} object. * @param message * a {@link java.lang.String} object. - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. */ - public static Input inputDestroyNoRegeneration(final List choices, final String message) { - final Input target = new Input() { + public static InputBase inputDestroyNoRegeneration(final List choices, final String message) { + final InputBase target = new InputBase() { private static final long serialVersionUID = -6637588517573573232L; @Override @@ -549,11 +551,11 @@ public class CardFactoryUtil { * a {@link forge.CardList} object. * @param paid * a {@link forge.Command} object. - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. */ - public static Input masterOfTheWildHuntInputTargetCreature(final SpellAbility spell, final List choices, + public static InputBase masterOfTheWildHuntInputTargetCreature(final SpellAbility spell, final List choices, final Command paid) { - final Input target = new Input() { + final InputBase target = new InputBase() { private static final long serialVersionUID = -1779224307654698954L; @Override @@ -593,10 +595,10 @@ public class CardFactoryUtil { * a {@link forge.card.spellability.SpellAbility} object. * @param card * a {@link forge.Card} object. - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. */ - public static Input modularInput(final SpellAbility ability, final Card card) { - final Input modularInput = new Input() { + public static InputBase modularInput(final SpellAbility ability, final Card card) { + final InputBase modularInput = new InputBase() { private static final long serialVersionUID = 2322926875771867901L; @@ -2302,10 +2304,10 @@ public class CardFactoryUtil { * a int. * @param type * a {@link java.lang.String} object. - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. */ public static Input inputUntapUpToNType(final int n, final String type) { - final Input untap = new Input() { + final Input untap = new InputBase() { private static final long serialVersionUID = -2167059918040912025L; private final int stop = n; @@ -3151,27 +3153,6 @@ public class CardFactoryUtil { }; haunterDiesWork.setDescription(hauntDescription); - final InputSelectManyCards target = new InputSelectManyCards(1, 1) { - private static final long serialVersionUID = 1981791992623774490L; - - @Override - protected Input onDone() { - haunterDiesWork.setTargetCard(selected.get(0)); - Singletons.getModel().getGame().getStack().add(haunterDiesWork); - return null; - } - - @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/card/cost/CostDamage.java b/src/main/java/forge/card/cost/CostDamage.java index e94787936e9..7f290b3705d 100644 --- a/src/main/java/forge/card/cost/CostDamage.java +++ b/src/main/java/forge/card/cost/CostDamage.java @@ -103,10 +103,11 @@ public class CostDamage extends CostPart { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { final String amount = this.getAmount(); final Player activator = ability.getActivatingPlayer(); final int life = activator.getLife(); + final Card source = ability.getSourceCard(); Integer c = this.convertAmount(); if (c == null) { @@ -124,11 +125,7 @@ public class CostDamage extends CostPart { if (GuiDialog.confirm(source, sb.toString()) && activator.canPayLife(c)) { activator.addDamage(c, source); - this.setLastPaidAmount(c); - payment.setPaidManaPart(this); } else { - payment.setCancel(true); - payment.getRequirements().finishPaying(); return false; } return true; diff --git a/src/main/java/forge/card/cost/CostDiscard.java b/src/main/java/forge/card/cost/CostDiscard.java index 727f61c5abc..13f38299899 100644 --- a/src/main/java/forge/card/cost/CostDiscard.java +++ b/src/main/java/forge/card/cost/CostDiscard.java @@ -19,23 +19,20 @@ package forge.card.cost; import java.util.ArrayList; import java.util.List; - import com.google.common.base.Predicate; import forge.Card; import forge.CardLists; import forge.CardPredicates; -import forge.Singletons; +import forge.FThreads; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; +import forge.control.input.InputSelectCards; +import forge.control.input.InputSelectCardsFromList; import forge.game.GameState; import forge.game.player.AIPlayer; import forge.game.player.Player; -import forge.game.zone.Zone; import forge.game.zone.ZoneType; -import forge.gui.match.CMatchUI; -import forge.view.ButtonUtil; /** * The Class CostDiscard. @@ -43,6 +40,8 @@ import forge.view.ButtonUtil; public class CostDiscard extends CostPartWithList { // Discard + // Inputs + /** * Instantiates a new cost discard. * @@ -155,7 +154,7 @@ public class CostDiscard extends CostPartWithList { @Override public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { for (final Card c : this.getList()) { - ai.discard(c, ability); + executePayment(ability, c); } } @@ -167,10 +166,11 @@ public class CostDiscard extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { final Player activator = ability.getActivatingPlayer(); + final Card source = ability.getSourceCard(); List handList = new ArrayList(activator.getCardsIn(ZoneType.Hand)); - String discType = this.getType(); + String discardType = this.getType(); final String amount = this.getAmount(); this.resetList(); @@ -178,25 +178,24 @@ public class CostDiscard extends CostPartWithList { if (!handList.contains(source)) { return false; } - activator.discard(source, ability); - payment.setPaidManaPart(this); + executePayment(ability, source); + return true; //this.addToList(source); - } else if (discType.equals("Hand")) { + } else if (discardType.equals("Hand")) { this.setList(handList); activator.discardHand(ability); - payment.setPaidManaPart(this); - } else if (discType.equals("LastDrawn")) { + return true; + } else if (discardType.equals("LastDrawn")) { final Card lastDrawn = activator.getLastDrawnCard(); - this.addToList(lastDrawn); if (!handList.contains(lastDrawn)) { return false; } - activator.discard(lastDrawn, ability); - payment.setPaidManaPart(this); + executePayment(ability, lastDrawn); + return true; } else { Integer c = this.convertAmount(); - if (discType.equals("Random")) { + if (discardType.equals("Random")) { if (c == null) { final String sVar = ability.getSVar(amount); // Generalize this @@ -208,9 +207,9 @@ public class CostDiscard extends CostPartWithList { } this.setList(activator.discardRandom(c, ability)); - payment.setPaidManaPart(this); + return true; } else { - String type = new String(discType); + String type = new String(discardType); boolean sameName = false; if (type.contains("+WithSameName")) { sameName = true; @@ -243,13 +242,17 @@ public class CostDiscard extends CostPartWithList { } } - final Input inp = CostDiscard.inputDiscardCost(discType, handList, ability, payment, this, c); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - return false; + InputSelectCards inp = new InputSelectCardsFromList(c, c, handList); + inp.setMessage("Select %d more " + getDescriptiveType() + " to discard."); + //InputPayment inp = new InputPayCostDiscard(ability, handList, this, c, discardType); + FThreads.setInputAndWait(inp); + if( inp.hasCancelled() || inp.getSelected().size() != c) + return false; + for(Card crd : inp.getSelected()) + executePayment(ability, crd); + return true; } } - this.addListToHash(ability, "Discarded"); - return true; } /* @@ -306,109 +309,23 @@ public class CostDiscard extends CostPartWithList { return this.getList() != null; } + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) + */ + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + this.addToList(targetCard); + targetCard.getController().discard(targetCard, ability); + } + + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() + */ + @Override + public String getHashForList() { + return "Discarded"; + } + // Inputs - /** - *

- * input_discardCost. - *

- * - * @param discType - * a {@link java.lang.String} object. - * @param handList - * a {@link forge.CardList} object. - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param part - * TODO - * @param nNeeded - * a int. - * - * @return a {@link forge.control.input.Input} object. - */ - public static Input inputDiscardCost(final String discType, final List handList, final SpellAbility sa, - final CostPayment payment, final CostDiscard part, final int nNeeded) { - final SpellAbility sp = sa; - final Input target = new Input() { - private static final long serialVersionUID = -329993322080934435L; - - private int nDiscard = 0; - private boolean sameName = discType.contains("WithSameName"); - - @Override - public void showMessage() { - if (nNeeded == 0) { - this.done(); - } - - if (sa.getActivatingPlayer().getZone(ZoneType.Hand).isEmpty()) { - this.stop(); - } - final StringBuilder type = new StringBuilder(""); - if (!discType.equals("Card")) { - type.append(" ").append(discType); - } - final StringBuilder sb = new StringBuilder(); - sb.append("Select a "); - sb.append(part.getDescriptiveType()); - sb.append(" to discard."); - if (nNeeded > 1) { - sb.append(" You have "); - sb.append(nNeeded - this.nDiscard); - sb.append(" remaining."); - } - CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); - if (nNeeded > 0) { - ButtonUtil.enableOnlyCancel(); - } - } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - @Override - public void selectCard(final Card card) { - Zone zone = Singletons.getModel().getGame().getZoneOf(card); - if (zone.is(ZoneType.Hand) && handList.contains(card)) { - if (!sameName || part.getList().isEmpty() - || part.getList().get(0).getName().equals(card.getName())) { - // send in List for Typing - card.getController().discard(card, sp); - part.addToList(card); - handList.remove(card); - this.nDiscard++; - - // in case no more cards in hand - if (this.nDiscard == nNeeded) { - this.done(); - } else if (sa.getActivatingPlayer().getZone(ZoneType.Hand).size() == 0) { - // really - // shouldn't - // happen - this.cancel(); - } else { - this.showMessage(); - } - } - } - } - - public void cancel() { - this.stop(); - payment.cancelCost(); - } - - public void done() { - this.stop(); - part.addListToHash(sp, "Discarded"); - payment.paidCost(part); - } - }; - - return target; - } // input_discard() } diff --git a/src/main/java/forge/card/cost/CostExile.java b/src/main/java/forge/card/cost/CostExile.java index b339110f65b..9f1e1fef2c0 100644 --- a/src/main/java/forge/card/cost/CostExile.java +++ b/src/main/java/forge/card/cost/CostExile.java @@ -20,19 +20,20 @@ package forge.card.cost; import java.util.ArrayList; import java.util.Iterator; import java.util.List; - import forge.Card; import forge.CardLists; import forge.CardPredicates; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbilityStackInstance; -import forge.control.input.Input; +import forge.control.input.InputPayment; import forge.game.GameState; import forge.game.ai.ComputerUtil; import forge.game.player.AIPlayer; import forge.game.player.Player; +import forge.game.zone.MagicStack; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; import forge.gui.GuiDialog; @@ -49,18 +50,14 @@ public class CostExile extends CostPartWithList { // ExileFromTop (of library) // ExileSameGrave - /** - * TODO: Write javadoc for this type. - * - */ - private static final class InputExileFrom extends Input { + private static final class InputExileFrom extends InputPayCostBase { private final SpellAbility sa; private final String type; private final int nNeeded; - private final CostPayment payment; private final CostExile part; private static final long serialVersionUID = 734256837615635021L; private List typeList; + /** * TODO: Write javadoc for Constructor. @@ -70,11 +67,10 @@ public class CostExile extends CostPartWithList { * @param payment * @param part */ - private InputExileFrom(SpellAbility sa, String type, int nNeeded, CostPayment payment, CostExile part) { + private InputExileFrom(SpellAbility sa, String type, int nNeeded, CostExile part) { this.sa = sa; this.type = type; this.nNeeded = nNeeded; - this.payment = payment; this.part = part; } @@ -96,8 +92,7 @@ public class CostExile extends CostPartWithList { if (c != null) { this.typeList.remove(c); - part.addToList(c); - Singletons.getModel().getGame().getAction().exile(c); + part.executePayment(sa, c); if (i == (nNeeded - 1)) { this.done(); } @@ -107,33 +102,15 @@ public class CostExile extends CostPartWithList { } } } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - public void done() { - this.stop(); - part.addListToHash(sa, "Exiled"); - payment.paidCost(part); - } - - public void cancel() { - this.stop(); - payment.cancelCost(); - } } /** * TODO: Write javadoc for this type. * */ - private static final class InputExileFromSame extends Input { + private static final class InputExileFromSame extends InputPayCostBase { private final List list; private final CostExile part; - private final CostPayment payment; - private final SpellAbility sa; private final int nNeeded; private final List payableZone; private static final long serialVersionUID = 734256837615635021L; @@ -148,12 +125,9 @@ public class CostExile extends CostPartWithList { * @param nNeeded * @param payableZone */ - private InputExileFromSame(List list, CostExile part, CostPayment payment, SpellAbility sa, int nNeeded, - List payableZone) { + private InputExileFromSame(List list, CostExile part, int nNeeded, List payableZone) { this.list = list; this.part = part; - this.payment = payment; - this.sa = sa; this.nNeeded = nNeeded; this.payableZone = payableZone; } @@ -185,8 +159,7 @@ public class CostExile extends CostPartWithList { if (c != null) { this.typeList.remove(c); - part.addToList(c); - Singletons.getModel().getGame().getAction().exile(c); + part.executePayment(null, c); if (i == (nNeeded - 1)) { this.done(); } @@ -196,30 +169,14 @@ public class CostExile extends CostPartWithList { } } } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - public void done() { - this.stop(); - part.addListToHash(sa, "Exiled"); - payment.paidCost(part); - } - - public void cancel() { - this.stop(); - payment.cancelCost(); - } } /** * TODO: Write javadoc for this type. * */ - private static final class InputExileFromStack extends Input { - private final CostPayment payment; + private static final class InputExileFromStack extends InputPayCostBase { + private final SpellAbility sa; private final String type; private final int nNeeded; @@ -236,8 +193,7 @@ public class CostExile extends CostPartWithList { * @param nNeeded * @param part */ - private InputExileFromStack(CostPayment payment, SpellAbility sa, String type, int nNeeded, CostExile part) { - this.payment = payment; + private InputExileFromStack(SpellAbility sa, String type, int nNeeded, CostExile part) { this.sa = sa; this.type = type; this.nNeeded = nNeeded; @@ -252,10 +208,11 @@ public class CostExile extends CostPartWithList { saList = new ArrayList(); descList = new ArrayList(); + final MagicStack stack = sa.getActivatingPlayer().getGame().getStack(); - for (int i = 0; i < Singletons.getModel().getGame().getStack().size(); i++) { - final Card stC = Singletons.getModel().getGame().getStack().peekAbility(i).getSourceCard(); - final SpellAbility stSA = Singletons.getModel().getGame().getStack().peekAbility(i).getRootAbility(); + for (int i = 0; i < stack.size(); i++) { + final Card stC = stack.peekAbility(i).getSourceCard(); + final SpellAbility stSA = stack.peekAbility(i).getRootAbility(); if (stC.isValid(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()) && stSA.isSpell()) { this.saList.add(stSA); if (stC.isCopiedSpell()) { @@ -277,47 +234,33 @@ public class CostExile extends CostPartWithList { if (o != null) { final SpellAbility toExile = this.saList.get(descList.indexOf(o)); final Card c = toExile.getSourceCard(); + + this.saList.remove(toExile); - part.addToList(c); if (!c.isCopiedSpell()) { - Singletons.getModel().getGame().getAction().exile(c); - } + part.executePayment(sa, c); + } else + part.addToList(c); + if (i == (nNeeded - 1)) { this.done(); } - final SpellAbilityStackInstance si = Singletons.getModel().getGame().getStack().getInstanceFromSpellAbility(toExile); - Singletons.getModel().getGame().getStack().remove(si); + final SpellAbilityStackInstance si = stack.getInstanceFromSpellAbility(toExile); + stack.remove(si); } else { this.cancel(); break; } } } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - public void done() { - this.stop(); - part.addListToHash(sa, "Exiled"); - payment.paidCost(part); - } - - public void cancel() { - this.stop(); - payment.cancelCost(); - } } /** * TODO: Write javadoc for this type. * */ - private static final class InputExileType extends Input { + private static final class InputExileType extends InputPayCostBase { private final CostExile part; - private final CostPayment payment; private final String type; private final int nNeeded; private final SpellAbility sa; @@ -333,9 +276,8 @@ public class CostExile extends CostPartWithList { * @param nNeeded * @param sa */ - private InputExileType(CostExile part, CostPayment payment, String type, int nNeeded, SpellAbility sa) { + private InputExileType(CostExile part, String type, int nNeeded, SpellAbility sa) { this.part = part; - this.payment = payment; this.type = type; this.nNeeded = nNeeded; this.sa = sa; @@ -366,17 +308,11 @@ public class CostExile extends CostPartWithList { ButtonUtil.enableOnlyCancel(); } - @Override - public void selectButtonCancel() { - this.cancel(); - } - @Override public void selectCard(final Card card) { if (this.typeList.contains(card)) { this.nExiles++; - part.addToList(card); - Singletons.getModel().getGame().getAction().exile(card); + part.executePayment(sa, card); this.typeList.remove(card); // in case nothing else to exile if (this.nExiles == nNeeded) { @@ -389,25 +325,13 @@ public class CostExile extends CostPartWithList { } } } - - public void done() { - this.stop(); - part.addListToHash(sa, "Exiled"); - payment.paidCost(part); - } - - public void cancel() { - this.stop(); - payment.cancelCost(); - } } /** * TODO: Write javadoc for this type. * */ - private static final class InputExileThis extends Input { - private final CostPayment payment; + private static final class InputExileThis extends InputPayCostBase { private final CostExile part; private final SpellAbility sa; private static final long serialVersionUID = 678668673002725001L; @@ -418,30 +342,22 @@ public class CostExile extends CostPartWithList { * @param part * @param sa */ - private InputExileThis(CostPayment payment, CostExile part, SpellAbility sa) { - this.payment = payment; + private InputExileThis(CostExile part, SpellAbility sa) { this.part = part; this.sa = sa; } - @Override public void showMessage() { final Card card = sa.getSourceCard(); if ( sa.getActivatingPlayer().getZone(part.getFrom()).contains(card)) { - boolean choice = GuiDialog.confirm(card, card.getName() + " - Exile?"); if (choice) { - payment.getAbility().addCostToHashList(card, "Exiled"); - Singletons.getModel().getGame().getAction().exile(card); - part.addToList(card); - this.stop(); - part.addListToHash(sa, "Exiled"); - payment.paidCost(part); - } else { - this.stop(); - payment.cancelCost(); + part.executePayment(sa, card); + done(); + return; } } + cancel(); } } @@ -619,8 +535,9 @@ public class CostExile extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { final String amount = this.getAmount(); + final Card source = ability.getSourceCard(); Integer c = this.convertAmount(); final Player activator = ability.getActivatingPlayer(); List list; @@ -632,11 +549,10 @@ public class CostExile extends CostPartWithList { } if (this.getType().equals("All")) { - this.setList(list); for (final Card card : list) { - Singletons.getModel().getGame().getAction().exile(card); + executePayment(ability, card); } - payment.paidCost(this); + return true; } list = CardLists.getValidCards(list, this.getType().split(";"), activator, source); if (c == null) { @@ -651,16 +567,16 @@ public class CostExile extends CostPartWithList { } } - Input target = null; + InputPayment target = null; if (this.payCostFromSource()) { - target = new InputExileThis(payment, this, ability); + target = new InputExileThis(this, ability); } else if (this.from.equals(ZoneType.Battlefield) || this.from.equals(ZoneType.Hand)) { - target = new InputExileType(this, payment, this.getType(), c, ability); + target = new InputExileType(this, this.getType(), c, ability); } else if (this.from.equals(ZoneType.Stack)) { - target = new InputExileFromStack(payment, ability, this.getType(), c, this); + target = new InputExileFromStack(ability, this.getType(), c, this); } else if (this.from.equals(ZoneType.Library)) { // this does not create input - CostExile.exileFromTop(ability, this, payment, c); + return exileFromTop(ability, c); } else if (this.sameZone) { List players = game.getPlayers(); List payableZone = new ArrayList(); @@ -672,13 +588,13 @@ public class CostExile extends CostPartWithList { payableZone.add(p); } } - target = new InputExileFromSame(list, this, payment, ability, c, payableZone); + target = new InputExileFromSame(list, this, c, payableZone); } else { - target = new InputExileFrom(ability, this.getType(), c, payment, this); + target = new InputExileFrom(ability, this.getType(), c, this); } - if ( null != target ) - Singletons.getModel().getMatch().getInput().setInputInterrupt(target); - return false; + FThreads.setInputAndWait(target); + return target.isPaid(); + } /* @@ -741,30 +657,43 @@ public class CostExile extends CostPartWithList { * @param nNeeded * the n needed */ - public static void exileFromTop(final SpellAbility sa, final CostExile part, final CostPayment payment, - final int nNeeded) { + public boolean exileFromTop(final SpellAbility sa, final int nNeeded) { final StringBuilder sb = new StringBuilder(); sb.append("Exile ").append(nNeeded).append(" cards from the top of your library?"); final List list = sa.getActivatingPlayer().getCardsIn(ZoneType.Library, nNeeded); if (list.size() > nNeeded) { // I don't believe this is possible - payment.cancelCost(); - return; + return false; } final boolean doExile = GuiDialog.confirm(sa.getSourceCard(), sb.toString()); if (doExile) { final Iterator itr = list.iterator(); while (itr.hasNext()) { - final Card c = itr.next(); - part.addToList(c); - Singletons.getModel().getGame().getAction().exile(c); + executePayment(sa, itr.next()); } - part.addListToHash(sa, "Exiled"); - payment.paidCost(part); + return true; } else { - payment.cancelCost(); + return false; } } + + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) + */ + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + addToList(targetCard); + ability.getActivatingPlayer().getGame().getAction().exile(targetCard); + } + + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() + */ + @Override + public String getHashForList() { + // TODO Auto-generated method stub + return "Exiled"; + } } diff --git a/src/main/java/forge/card/cost/CostGainLife.java b/src/main/java/forge/card/cost/CostGainLife.java index 1419a568205..f48fb1508ab 100644 --- a/src/main/java/forge/card/cost/CostGainLife.java +++ b/src/main/java/forge/card/cost/CostGainLife.java @@ -137,7 +137,8 @@ public class CostGainLife extends CostPart { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { + final Card source = ability.getSourceCard(); final String amount = this.getAmount(); final Player activator = ability.getActivatingPlayer(); final int life = activator.getLife(); @@ -163,8 +164,6 @@ public class CostGainLife extends CostPart { if(cntPlayers == Integer.MAX_VALUE) { // applied to all players who can gain for(Player opp: oppsThatCanGainLife) opp.gainLife(c, null); - payment.setPaidManaPart(this); - return true; } final StringBuilder sb = new StringBuilder(); @@ -174,16 +173,12 @@ public class CostGainLife extends CostPart { for(int playersLeft = cntPlayers; playersLeft > 0; playersLeft--) { final Player chosenToGain = GuiChoose.oneOrNone(sb.toString(), oppsThatCanGainLife); if (null == chosenToGain) { - payment.setCancel(true); - payment.getRequirements().finishPaying(); return false; } else { final Player chosen = chosenToGain; chosen.gainLife(c, null); } } - - payment.setPaidManaPart(this); return true; } diff --git a/src/main/java/forge/card/cost/CostMill.java b/src/main/java/forge/card/cost/CostMill.java index 65919ae541c..54e1da824bf 100644 --- a/src/main/java/forge/card/cost/CostMill.java +++ b/src/main/java/forge/card/cost/CostMill.java @@ -126,9 +126,10 @@ public class CostMill extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { final String amount = this.getAmount(); Integer c = this.convertAmount(); + final Card source = ability.getSourceCard(); final Player activator = ability.getActivatingPlayer(); if (c == null) { @@ -144,29 +145,22 @@ public class CostMill extends CostPartWithList { if ((list == null) || (list.size() > c)) { // I don't believe this is possible - payment.cancelCost(); return false; } final StringBuilder sb = new StringBuilder(); sb.append("Mill ").append(c).append(" cards from your library?"); - final boolean doMill = GuiDialog.confirm(source, sb.toString()); - if (doMill) { - this.resetList(); - final Iterator itr = list.iterator(); - while (itr.hasNext()) { - final Card card = itr.next(); - this.addToList(card); - Singletons.getModel().getGame().getAction().moveToGraveyard(card); - } - this.addListToHash(ability, "Milled"); - payment.paidCost(this); - return false; - } else { - payment.cancelCost(); + if ( false == GuiDialog.confirm(source, sb.toString()) ) return false; + + this.resetList(); + final Iterator itr = list.iterator(); + while (itr.hasNext()) { + final Card card = itr.next(); + executePayment(ability, card); } + return true; } /* @@ -195,4 +189,21 @@ public class CostMill extends CostPartWithList { return sb.toString(); } + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) + */ + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + this.addToList(targetCard); + ability.getActivatingPlayer().getGame().getAction().moveToGraveyard(targetCard); + } + + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() + */ + @Override + public String getHashForList() { + return "Milled"; + } + } diff --git a/src/main/java/forge/card/cost/CostPart.java b/src/main/java/forge/card/cost/CostPart.java index 675d151f993..0c9edb55ed6 100644 --- a/src/main/java/forge/card/cost/CostPart.java +++ b/src/main/java/forge/card/cost/CostPart.java @@ -17,6 +17,7 @@ */ package forge.card.cost; + import forge.Card; import forge.card.spellability.SpellAbility; import forge.game.GameState; @@ -199,8 +200,8 @@ public abstract class CostPart { * @param game * @return true, if successful */ - public abstract boolean payHuman(SpellAbility ability, Card source, CostPayment payment, GameState game); - + public abstract boolean payHuman(SpellAbility ability, GameState game); + /* * (non-Javadoc) * diff --git a/src/main/java/forge/card/cost/CostPartMana.java b/src/main/java/forge/card/cost/CostPartMana.java index 6d91123d5d3..948c5012f6b 100644 --- a/src/main/java/forge/card/cost/CostPartMana.java +++ b/src/main/java/forge/card/cost/CostPartMana.java @@ -20,12 +20,12 @@ package forge.card.cost; import com.google.common.base.Strings; import forge.Card; -import forge.Singletons; +import forge.FThreads; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; import forge.control.input.InputPayManaOfCostPayment; import forge.control.input.InputPayManaX; +import forge.control.input.InputPayment; import forge.game.GameState; import forge.game.ai.ComputerUtilMana; import forge.game.player.AIPlayer; @@ -111,8 +111,8 @@ public class CostPartMana extends CostPart { /** * @return the xCantBe0 */ - public boolean isxCantBe0() { - return xCantBe0; + public boolean canXbe0() { + return !xCantBe0; } /** @@ -206,7 +206,8 @@ public class CostPartMana extends CostPart { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { + final Card source = ability.getSourceCard(); int manaToAdd = 0; if (!this.hasNoXManaCost()) { // if X cost is a defined value, other than xPaid @@ -215,19 +216,23 @@ public class CostPartMana extends CostPart { manaToAdd = AbilityUtils.calculateAmount(source, "X", ability) * this.getXMana(); } } - if (!this.getManaToPay().equals("0") || (manaToAdd > 0)) { - final Input inp = new InputPayManaOfCostPayment(game, this, ability, payment, manaToAdd); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - } else if (this.getXMana() > 0) { - final Input inp = new InputPayManaX(game, ability, payment, this); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - } else { - payment.paidCost(this); - } + + + if (!"0".equals(this.getManaToPay()) || manaToAdd > 0) { + InputPayment inpPayment = new InputPayManaOfCostPayment(game, this, ability, manaToAdd); + FThreads.setInputAndWait(inpPayment); + if(!inpPayment.isPaid()) + return false; + } + if (this.getXMana() > 0) { + source.setXManaCostPaid(0); + InputPayment inpPayment = new InputPayManaX(game, ability, this); + FThreads.setInputAndWait(inpPayment); + if(!inpPayment.isPaid()) + return false; + } + return true; - // We return false here because the Inputs set above should recall - // payment.payCosts() - return false; } /* diff --git a/src/main/java/forge/card/cost/CostPartWithList.java b/src/main/java/forge/card/cost/CostPartWithList.java index 6eca887197f..3a5274dfed4 100644 --- a/src/main/java/forge/card/cost/CostPartWithList.java +++ b/src/main/java/forge/card/cost/CostPartWithList.java @@ -17,8 +17,9 @@ */ package forge.card.cost; -import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import forge.Card; import forge.CardUtil; @@ -30,14 +31,16 @@ import forge.card.spellability.SpellAbility; public abstract class CostPartWithList extends CostPart { /** The list. */ - private List list = null; + private Set list = new HashSet(); + // set is here because executePayment() adds card to list, while ai's decide payment does the same thing. + // set allows to avoid duplication /** * Gets the list. * * @return the list */ - public final List getList() { + public final Set getList() { return this.list; } @@ -48,14 +51,15 @@ public abstract class CostPartWithList extends CostPart { * the new list */ public final void setList(final List setList) { - this.list = setList; + this.list.clear(); + list.addAll(setList); } /** * Reset list. */ public final void resetList() { - this.setList(new ArrayList()); + this.list.clear(); } /** @@ -65,10 +69,7 @@ public abstract class CostPartWithList extends CostPart { * the c */ public final void addToList(final Card c) { - if (this.getList() == null) { - this.resetList(); - } - this.getList().add(c); + this.list.add(c); } /** @@ -91,7 +92,7 @@ public abstract class CostPartWithList extends CostPart { */ public CostPartWithList() { } - + /** * Instantiates a new cost part with list. * @@ -106,4 +107,12 @@ public abstract class CostPartWithList extends CostPart { super(amount, type, description); this.resetList(); } + + public abstract void executePayment(SpellAbility ability, Card targetCard); + + /** + * TODO: Write javadoc for this method. + * @return + */ + public abstract String getHashForList(); } diff --git a/src/main/java/forge/card/cost/CostPayLife.java b/src/main/java/forge/card/cost/CostPayLife.java index 0b6c08eee92..391f3de9af7 100644 --- a/src/main/java/forge/card/cost/CostPayLife.java +++ b/src/main/java/forge/card/cost/CostPayLife.java @@ -123,7 +123,8 @@ public class CostPayLife extends CostPart { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { + final Card source = ability.getSourceCard(); final String amount = this.getAmount(); final Player activator = ability.getActivatingPlayer(); final int life = activator.getLife(); @@ -147,13 +148,9 @@ public class CostPayLife extends CostPart { final StringBuilder sb = new StringBuilder(); sb.append(source.getName()).append(" - Pay ").append(c).append(" Life?"); - if (GuiDialog.confirm(source, sb.toString()) && activator.canPayLife(c)) { + if (activator.canPayLife(c) && GuiDialog.confirm(source, sb.toString())) { activator.payLife(c, null); - this.setLastPaidAmount(c); - payment.setPaidManaPart(this); } else { - payment.setCancel(true); - payment.getRequirements().finishPaying(); return false; } return true; diff --git a/src/main/java/forge/card/cost/CostPayment.java b/src/main/java/forge/card/cost/CostPayment.java index 3de175c6130..a24d60b970b 100644 --- a/src/main/java/forge/card/cost/CostPayment.java +++ b/src/main/java/forge/card/cost/CostPayment.java @@ -38,7 +38,6 @@ import forge.game.player.Player; public class CostPayment { private Cost cost = null; private SpellAbility ability = null; - private Card card = null; private SpellAbilityRequirements req = null; private boolean bCancel = false; private final ArrayList paidCostParts = new ArrayList(); @@ -74,7 +73,7 @@ public class CostPayment { * @return a {@link forge.Card} object. */ public final Card getCard() { - return this.card; + return this.ability.getSourceCard(); } /** @@ -135,7 +134,6 @@ public class CostPayment { public CostPayment(final Cost cost, final SpellAbility abil, final GameState game) { this.cost = cost; this.ability = abil; - this.card = abil.getSourceCard(); this.game = game; } @@ -179,26 +177,15 @@ public class CostPayment { * @param bPaid * the b paid */ - public final void setPaidManaPart(final CostPart part) { + public final void setPaidPart(final CostPart part) { this.paidCostParts.add(part); } - /** - * Paid cost. - * - * @param part - * the part - */ - public final void paidCost(final CostPart part) { - this.setPaidManaPart(part); - this.payCost(); - } - /** * Cancel cost (including CostPart for refunding). */ public final void cancelCost(final CostPart part) { - this.setPaidManaPart(part); + this.setPaidPart(part); this.cancelCost(); } @@ -207,7 +194,6 @@ public class CostPayment { */ public final void cancelCost() { this.setCancel(true); - this.req.finishPaying(); } /** @@ -217,27 +203,22 @@ public class CostPayment { * * @return a boolean. */ - public final boolean payCost() { - // Nothing actually ever checks this return value, is it needed? - if (this.bCancel) { - this.req.finishPaying(); - return false; - } - + public void payCost() { for (final CostPart part : this.cost.getCostParts()) { // This portion of the cost is already paid for, keep moving if (this.paidCostParts.contains(part)) { continue; } - if (!part.payHuman(this.ability, this.card, this, game)) { - return false; + if ( false == part.payHuman(getAbility(), game) ) { + this.setCancel(true); + return; } + if( part instanceof CostPartWithList ) + ((CostPartWithList) part).addListToHash(ability, ((CostPartWithList) part).getHashForList()); + setPaidPart(part); } - this.resetUndoList(); - this.req.finishPaying(); - return true; } /** @@ -278,7 +259,7 @@ public class CostPayment { public final void cancelPayment() { for (final CostPart part : this.paidCostParts) { if (part.isUndoable()) { - part.refund(this.card); + part.refund(this.getCard()); } } diff --git a/src/main/java/forge/card/cost/CostPutCounter.java b/src/main/java/forge/card/cost/CostPutCounter.java index b3c380d7664..4d2aeb8b6c5 100644 --- a/src/main/java/forge/card/cost/CostPutCounter.java +++ b/src/main/java/forge/card/cost/CostPutCounter.java @@ -18,14 +18,13 @@ package forge.card.cost; import java.util.List; - import forge.Card; import forge.CardLists; import forge.CounterType; -import forge.Singletons; +import forge.FThreads; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; +import forge.control.input.InputPayment; import forge.game.GameState; import forge.game.ai.ComputerUtilCard; import forge.game.player.AIPlayer; @@ -38,6 +37,69 @@ import forge.view.ButtonUtil; * The Class CostPutCounter. */ public class CostPutCounter extends CostPartWithList { + /** + * TODO: Write javadoc for this type. + * + */ + public static final class InputPayCostPutCounter extends InputPayCostBase { + private final String type; + private final CostPutCounter costPutCounter; + private final int nNeeded; + private final SpellAbility sa; + private static final long serialVersionUID = 2685832214519141903L; + private List typeList; + private int nPut = 0; + + /** + * TODO: Write javadoc for Constructor. + * @param type + * @param costPutCounter + * @param nNeeded + * @param payment + * @param sa + */ + public InputPayCostPutCounter(String type, CostPutCounter costPutCounter, int nNeeded, SpellAbility sa) { + this.type = type; + this.costPutCounter = costPutCounter; + this.nNeeded = nNeeded; + this.sa = sa; + } + + @Override + public void showMessage() { + if ((nNeeded == 0) || (nNeeded == this.nPut)) { + this.done(); + } + + final StringBuilder msg = new StringBuilder("Put "); + final int nLeft = nNeeded - this.nPut; + msg.append(nLeft).append(" "); + msg.append(costPutCounter.getCounter()).append(" on "); + + msg.append(costPutCounter.getDescriptiveType()); + if (nLeft > 1) { + msg.append("s"); + } + + this.typeList = CardLists.getValidCards(sa.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); + CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectCard(final Card card) { + if (this.typeList.contains(card)) { + this.nPut++; + costPutCounter.executePayment(sa, card); + if (nNeeded == this.nPut) { + this.done(); + } else { + this.showMessage(); + } + } + } + } + // Put Counter doesn't really have a "Valid" portion of the cost private final CounterType counter; private int lastPaidAmount = 0; @@ -156,17 +218,14 @@ public class CostPutCounter extends CostPartWithList { */ @Override public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { - Integer c = this.convertAmount(); - if (c == null) { - c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); - } + Integer c = getNumberOfCounters(ability); if (this.payCostFromSource()) { - source.addCounter(this.getCounter(), c, false); + executePayment(ability, source, c); } else { // Put counter on chosen card for (final Card card : this.getList()) { - card.addCounter(this.getCounter(), 1, false); + executePayment(ability, card); } } } @@ -179,24 +238,28 @@ public class CostPutCounter extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { - Integer c = this.convertAmount(); - if (c == null) { - c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); - } + public final boolean payHuman(final SpellAbility ability, final GameState game) { + final Card source = ability.getSourceCard(); + Integer c = getNumberOfCounters(ability); if (this.payCostFromSource()) { - source.addCounter(this.getCounter(), c, false); - payment.setPaidManaPart(this); - this.addToList(source); + executePayment(ability, source, c); return true; } else { - final Input inp = CostPutCounter.putCounterType(ability, this.getType(), payment, this, c); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - return false; + InputPayment inp = new InputPayCostPutCounter(this.getType(), this, c, ability); + FThreads.setInputAndWait(inp); + return inp.isPaid(); } } + private Integer getNumberOfCounters(final SpellAbility ability) { + Integer c = this.convertAmount(); + if (c == null) { + c = AbilityUtils.calculateAmount(ability.getSourceCard(), this.getAmount(), ability); + } + return c; + } + /* * (non-Javadoc) * @@ -211,13 +274,7 @@ public class CostPutCounter extends CostPartWithList { this.addToList(source); return true; } else { - Integer c = this.convertAmount(); - if (c == null) { - c = AbilityUtils.calculateAmount(source, this.getAmount(), ability); - } - - final List typeList = - CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ai, source); + final List typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ai, source); Card card = null; if (this.getType().equals("Creature.YouCtrl")) { @@ -230,83 +287,22 @@ public class CostPutCounter extends CostPartWithList { return true; } - /** - *

- * returnType. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param type - * a {@link java.lang.String} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param costPutCounter - * TODO - * @param nNeeded - * the n needed - * @return a {@link forge.control.input.Input} object. + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ - public static Input putCounterType(final SpellAbility sa, final String type, final CostPayment payment, - final CostPutCounter costPutCounter, final int nNeeded) { - final Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - private List typeList; - private int nPut = 0; + @Override + public void executePayment(SpellAbility ability, Card targetCard){ + executePayment(ability, targetCard, 1); + } + + public void executePayment(SpellAbility ability, Card targetCard, int c) { + targetCard.addCounter(this.getCounter(), c, false); + this.addToList(targetCard); + } - @Override - public void showMessage() { - if ((nNeeded == 0) || (nNeeded == this.nPut)) { - this.done(); - } - final StringBuilder msg = new StringBuilder("Put "); - final int nLeft = nNeeded - this.nPut; - msg.append(nLeft).append(" "); - msg.append(costPutCounter.getCounter()).append(" on "); - - msg.append(costPutCounter.getDescriptiveType()); - if (nLeft > 1) { - msg.append("s"); - } - - this.typeList = CardLists.getValidCards(sa.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); - CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - @Override - public void selectCard(final Card card) { - if (this.typeList.contains(card)) { - this.nPut++; - costPutCounter.addToList(card); - card.addCounter(costPutCounter.getCounter(), 1, false); - - if (nNeeded == this.nPut) { - this.done(); - } else { - this.showMessage(); - } - } - } - - public void done() { - this.stop(); - payment.paidCost(costPutCounter); - } - - public void cancel() { - this.stop(); - costPutCounter.addListToHash(sa, "CounterPut"); - payment.cancelCost(); - } - }; - - return target; + @Override + public String getHashForList() { + return "CounterPut"; } } diff --git a/src/main/java/forge/card/cost/CostRemoveCounter.java b/src/main/java/forge/card/cost/CostRemoveCounter.java index dee934b4e69..c802168f551 100644 --- a/src/main/java/forge/card/cost/CostRemoveCounter.java +++ b/src/main/java/forge/card/cost/CostRemoveCounter.java @@ -19,14 +19,13 @@ package forge.card.cost; import java.util.ArrayList; import java.util.List; - import forge.Card; import forge.CardLists; import forge.CounterType; -import forge.Singletons; +import forge.FThreads; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; +import forge.control.input.InputPayment; import forge.game.GameState; import forge.game.player.AIPlayer; import forge.game.player.Player; @@ -46,6 +45,142 @@ public class CostRemoveCounter extends CostPartWithList { // Counter is tough), // Quillspike, Rift Elemental, Sage of Fables, Spike Rogue + /** + * TODO: Write javadoc for this type. + * + */ + public static final class InputPayCostRemoveCounterType extends InputPayCostBase { + private final int nNeeded; + private final SpellAbility sa; + private final String type; + private final CostRemoveCounter costRemoveCounter; + private static final long serialVersionUID = 2685832214519141903L; + private List typeList; + private int nRemove = 0; + + /** + * TODO: Write javadoc for Constructor. + * @param payment + * @param nNeeded + * @param sa + * @param type + * @param costRemoveCounter + */ + public InputPayCostRemoveCounterType(int nNeeded, SpellAbility sa, String type, CostRemoveCounter costRemoveCounter) { + this.nNeeded = nNeeded; + this.sa = sa; + this.type = type; + this.costRemoveCounter = costRemoveCounter; + } + + @Override + public void showMessage() { + if ((nNeeded == 0) || (nNeeded == this.nRemove)) { + this.done(); + } + + final StringBuilder msg = new StringBuilder("Remove "); + final int nLeft = nNeeded - this.nRemove; + msg.append(nLeft).append(" "); + msg.append(costRemoveCounter.getCounter().getName()).append(" counters from "); + msg.append(costRemoveCounter.getDescriptiveType()); + + this.typeList = CardLists.getValidCards(sa.getActivatingPlayer().getCardsIn(costRemoveCounter.getZone()), type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); + + // TODO Tabulate typelist vs nNeeded to see if there are enough counters to remove + + CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectCard(final Card card) { + if (this.typeList.contains(card)) { + if (card.getCounters(costRemoveCounter.getCounter()) > 0) { + this.nRemove++; + costRemoveCounter.executePayment(sa, card); + + if (nNeeded == this.nRemove) { + this.done(); + } else { + this.showMessage(); + } + } + } + } + } + + /** + * TODO: Write javadoc for this type. + * + */ + public static final class InputPayCostRemoveCounterFrom extends InputPayCostBase { + private final CostRemoveCounter costRemoveCounter; + private final String type; + private final SpellAbility sa; + private final int nNeeded; + private static final long serialVersionUID = 734256837615635021L; + private List typeList; + private int nRemove = 0; + + /** + * TODO: Write javadoc for Constructor. + * @param costRemoveCounter + * @param type + * @param sa + * @param nNeeded + * @param payment + */ + public InputPayCostRemoveCounterFrom(CostRemoveCounter costRemoveCounter, String type, SpellAbility sa, int nNeeded) { + + this.costRemoveCounter = costRemoveCounter; + this.type = type; + this.sa = sa; + this.nNeeded = nNeeded; + + } + + @Override + public void showMessage() { + if (nNeeded == 0) { + this.done(); + } + + this.typeList = new ArrayList(sa.getActivatingPlayer().getCardsIn(costRemoveCounter.getZone())); + this.typeList = CardLists.getValidCards(this.typeList, type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); + + for (int i = 0; i < nNeeded; i++) { + if (this.typeList.isEmpty()) { + this.cancel(); + } + + final Card o = GuiChoose.oneOrNone("Remove counter(s) from a card in " + costRemoveCounter.getZone(), this.typeList); + + if (o != null) { + final Card card = o; + + if (card.getCounters(costRemoveCounter.getCounter()) > 0) { + this.nRemove++; + costRemoveCounter.executePayment(sa, card); + + if (card.getCounters(costRemoveCounter.getCounter()) == 0) { + this.typeList.remove(card); + } + + if (nNeeded == this.nRemove) { + this.done(); + } else { + this.showMessage(); + } + } + } else { + this.cancel(); + break; + } + } + } + } + private final CounterType counter; private int lastPaidAmount = 0; private ZoneType zone; @@ -221,8 +356,9 @@ public class CostRemoveCounter extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { final String amount = this.getAmount(); + final Card source = ability.getSourceCard(); Integer c = this.convertAmount(); int maxCounters = 0; @@ -237,15 +373,14 @@ public class CostRemoveCounter extends CostPartWithList { } } + final InputPayment inp; if (this.getZone().equals(ZoneType.Battlefield)) { - final Input inp = CostRemoveCounter.removeCounterType(ability, this.getType(), payment, this, c); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); + inp = new InputPayCostRemoveCounterType(c, ability, this.getType(), this); + } else { + inp = new InputPayCostRemoveCounterFrom(this, this.getType(), ability, c); } - else { - final Input inp = CostRemoveCounter.removeCounterTypeFrom(ability, this.getType(), payment, this, c); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - } - return false; + FThreads.setInputAndWait(inp); + return inp.isPaid(); } maxCounters = source.getCounters(this.counter); @@ -263,17 +398,12 @@ public class CostRemoveCounter extends CostPartWithList { } } - if (maxCounters >= c) { - this.addToList(source); - source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c)); - source.subtractCounter(this.counter, c); - this.setLastPaidAmount(c); - payment.setPaidManaPart(this); - } else { - payment.setCancel(true); - payment.getRequirements().finishPaying(); - return false; - } + if (maxCounters < c) return false; + + + this.addToList(source); + source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c)); + executePayment(ability, source, c); return true; } @@ -321,171 +451,23 @@ public class CostRemoveCounter extends CostPartWithList { return true; } - /** - *

- * returnType. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param type - * a {@link java.lang.String} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param costRemoveCounter - * TODO - * @param nNeeded - * the n needed - * @return a {@link forge.control.input.Input} object. + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ - public static Input removeCounterType(final SpellAbility sa, final String type, final CostPayment payment, - final CostRemoveCounter costRemoveCounter, final int nNeeded) { - final Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - private List typeList; - private int nRemove = 0; - - @Override - public void showMessage() { - if ((nNeeded == 0) || (nNeeded == this.nRemove)) { - this.done(); - } - - final StringBuilder msg = new StringBuilder("Remove "); - final int nLeft = nNeeded - this.nRemove; - msg.append(nLeft).append(" "); - msg.append(costRemoveCounter.getCounter().getName()).append(" counters from "); - msg.append(costRemoveCounter.getDescriptiveType()); - - this.typeList = CardLists.getValidCards(sa.getActivatingPlayer().getCardsIn(costRemoveCounter.getZone()), type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); - - // TODO Tabulate typelist vs nNeeded to see if there are enough counters to remove - - CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - @Override - public void selectCard(final Card card) { - if (this.typeList.contains(card)) { - if (card.getCounters(costRemoveCounter.getCounter()) > 0) { - this.nRemove++; - costRemoveCounter.addToList(card); - card.subtractCounter(costRemoveCounter.getCounter(), 1); - - if (nNeeded == this.nRemove) { - this.done(); - } else { - this.showMessage(); - } - } - } - } - - public void done() { - this.stop(); - costRemoveCounter.addListToHash(sa, "CounterRemove"); - payment.paidCost(costRemoveCounter); - } - - public void cancel() { - this.stop(); - costRemoveCounter.addListToHash(sa, "CounterRemove"); - payment.cancelCost(costRemoveCounter); - } - }; - - return target; + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + executePayment(ability, targetCard, 1); } - /** - *

- * returnType. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param type - * a {@link java.lang.String} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param costRemoveCounter - * TODO - * @param nNeeded - * the n needed - * @return a {@link forge.control.input.Input} object. + public void executePayment(SpellAbility ability, Card targetCard, int n) { + addToList(targetCard); + targetCard.subtractCounter(getCounter(), n); + } + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() */ - public static Input removeCounterTypeFrom(final SpellAbility sa, final String type, final CostPayment payment, - final CostRemoveCounter costRemoveCounter, final int nNeeded) { - final Input target = new Input() { - private static final long serialVersionUID = 734256837615635021L; - private List typeList; - private int nRemove = 0; - - @Override - public void showMessage() { - if (nNeeded == 0) { - this.done(); - } - - this.typeList = new ArrayList(sa.getActivatingPlayer().getCardsIn(costRemoveCounter.getZone())); - this.typeList = CardLists.getValidCards(this.typeList, type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); - - for (int i = 0; i < nNeeded; i++) { - if (this.typeList.size() == 0) { - this.cancel(); - } - - final Card o = GuiChoose - .oneOrNone("Remove counter(s) from a card in " + costRemoveCounter.getZone(), this.typeList); - - if (o != null) { - final Card card = o; - - if (card.getCounters(costRemoveCounter.getCounter()) > 0) { - this.nRemove++; - costRemoveCounter.addToList(card); - card.subtractCounter(costRemoveCounter.getCounter(), 1); - - if (card.getCounters(costRemoveCounter.getCounter()) == 0) { - this.typeList.remove(card); - } - - if (nNeeded == this.nRemove) { - this.done(); - } else { - this.showMessage(); - } - } - } else { - this.cancel(); - break; - } - } - } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - public void done() { - this.stop(); - costRemoveCounter.addListToHash(sa, "CounterRemove"); - payment.paidCost(costRemoveCounter); - } - - public void cancel() { - this.stop(); - costRemoveCounter.addListToHash(sa, "CounterRemove"); - payment.cancelCost(); - } - }; - return target; + @Override + public String getHashForList() { + return "CounterRemove"; } } diff --git a/src/main/java/forge/card/cost/CostReturn.java b/src/main/java/forge/card/cost/CostReturn.java index 5849a50a64c..f350104dc04 100644 --- a/src/main/java/forge/card/cost/CostReturn.java +++ b/src/main/java/forge/card/cost/CostReturn.java @@ -19,22 +19,19 @@ package forge.card.cost; import java.util.ArrayList; import java.util.List; - -import javax.swing.JOptionPane; - import forge.Card; import forge.CardLists; -import forge.Singletons; +import forge.FThreads; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; +import forge.control.input.InputSelectCards; +import forge.control.input.InputSelectCardsFromList; import forge.game.GameState; import forge.game.ai.ComputerUtil; import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.ZoneType; -import forge.gui.match.CMatchUI; -import forge.view.ButtonUtil; +import forge.gui.GuiDialog; /** * The Class CostReturn. @@ -123,7 +120,7 @@ public class CostReturn extends CostPartWithList { @Override public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { for (final Card c : this.getList()) { - Singletons.getModel().getGame().getAction().moveToHand(c); + executePayment(ability, c); } } @@ -135,8 +132,9 @@ public class CostReturn extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { final String amount = this.getAmount(); + final Card source = ability.getSourceCard(); Integer c = this.convertAmount(); final Player activator = ability.getActivatingPlayer(); final List list = activator.getCardsIn(ZoneType.Battlefield); @@ -150,13 +148,28 @@ public class CostReturn extends CostPartWithList { } } if (this.payCostFromSource()) { - final Input inp = CostReturn.returnThis(ability, payment, this); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); + final Card card = ability.getSourceCard(); + if (card.getController() == ability.getActivatingPlayer() && card.isInPlay()) { + boolean confirm = GuiDialog.confirm(card, card.getName() + " - Return to Hand?"); + if (confirm) { + executePayment(ability, card); + } + return confirm; + } } else { - final Input inp = CostReturn.returnType(ability, this.getType(), payment, this, c); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); + List validCards = CardLists.getValidCards(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); + + InputSelectCards inp = new InputSelectCardsFromList(c, c, validCards); + inp.setMessage("Return %d " + this.getType() + " " + this.getType() + " card(s) to hand"); + FThreads.setInputAndWait(inp); + if (inp.hasCancelled()) + return false; + + for(Card crd : inp.getSelected()) + executePayment(ability, crd); + return true; } - return false; + return false; } /* @@ -178,140 +191,31 @@ public class CostReturn extends CostPartWithList { } this.setList(ComputerUtil.chooseReturnType(ai, this.getType(), source, ability.getTargetCard(), c)); - if (this.getList() == null) { + if (this.getList().isEmpty()) { return false; } } return true; } + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) + */ + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + addToList(targetCard); + ability.getActivatingPlayer().getGame().getAction().moveToHand(targetCard); + } + + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() + */ + @Override + public String getHashForList() { + return "Returned"; + } + // Inputs - /** - *

- * returnType. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param type - * a {@link java.lang.String} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param part - * TODO - * @param nNeeded - * the n needed - * @return a {@link forge.control.input.Input} object. - */ - public static Input returnType(final SpellAbility sa, final String type, final CostPayment payment, - final CostReturn part, final int nNeeded) { - final Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - private List typeList; - private int nReturns = 0; - @Override - public void showMessage() { - if (nNeeded == 0) { - this.done(); - } - - final StringBuilder msg = new StringBuilder("Return "); - final int nLeft = nNeeded - this.nReturns; - msg.append(nLeft).append(" "); - msg.append(type); - if (nLeft > 1) { - msg.append("s"); - } - - this.typeList = new ArrayList(sa.getActivatingPlayer().getCardsIn(ZoneType.Battlefield)); - this.typeList = CardLists.getValidCards(this.typeList, type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); - CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - @Override - public void selectCard(final Card card) { - if (this.typeList.contains(card)) { - this.nReturns++; - part.addToList(card); - Singletons.getModel().getGame().getAction().moveToHand(card); - this.typeList.remove(card); - // in case nothing else to return - if (this.nReturns == nNeeded) { - this.done(); - } else if (this.typeList.size() == 0) { - // happen - this.cancel(); - } else { - this.showMessage(); - } - } - } - - public void done() { - this.stop(); - part.addListToHash(sa, "Returned"); - payment.paidCost(part); - } - - public void cancel() { - this.stop(); - payment.cancelCost(); - } - }; - - return target; - } // returnType() - - /** - *

- * returnThis. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param part - * TODO - * @return a {@link forge.control.input.Input} object. - */ - public static Input returnThis(final SpellAbility sa, final CostPayment payment, final CostReturn part) { - final Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - - @Override - public void showMessage() { - final Card card = sa.getSourceCard(); - if (card.getController().isHuman() && card.isInPlay()) { - final StringBuilder sb = new StringBuilder(); - sb.append(card.getName()); - sb.append(" - Return to Hand?"); - final Object[] possibleValues = { "Yes", "No" }; - final Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, possibleValues, - possibleValues[0]); - if (choice.equals(0)) { - part.addToList(card); - Singletons.getModel().getGame().getAction().moveToHand(card); - this.stop(); - part.addListToHash(sa, "Returned"); - payment.paidCost(part); - } else { - this.stop(); - payment.cancelCost(); - } - } - } - }; - - return target; - } // input_sacrifice() } diff --git a/src/main/java/forge/card/cost/CostReveal.java b/src/main/java/forge/card/cost/CostReveal.java index 42060d82b55..a4920553bd0 100644 --- a/src/main/java/forge/card/cost/CostReveal.java +++ b/src/main/java/forge/card/cost/CostReveal.java @@ -19,20 +19,19 @@ package forge.card.cost; import java.util.ArrayList; import java.util.List; - import forge.Card; import forge.CardLists; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; +import forge.control.input.InputPayment; import forge.game.GameState; import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; -import forge.gui.match.CMatchUI; import forge.view.ButtonUtil; /** @@ -41,6 +40,87 @@ import forge.view.ButtonUtil; public class CostReveal extends CostPartWithList { // Reveal + /** + * TODO: Write javadoc for this type. + * + */ + public static final class InputPayReveal extends InputPayCostBase { + private final CostReveal part; + private final String discType; + private final List handList; + private final SpellAbility sa; + private final int nNeeded; + private static final long serialVersionUID = -329993322080934435L; + private int nReveal = 0; + + + /** + * TODO: Write javadoc for Constructor. + * @param part + * @param discType + * @param handList + * @param sa + * @param payment + * @param nNeeded + */ + public InputPayReveal(CostReveal part, String discType, List handList, SpellAbility sa, int nNeeded) { + this.part = part; + this.discType = discType; + this.handList = handList; + this.sa = sa; + this.nNeeded = nNeeded; + } + + @Override + public void showMessage() { + if (nNeeded == 0) { + this.done(); + } + + /*if (handList.size() + this.nReveal < nNeeded) { + this.stop(); + }*/ + final StringBuilder type = new StringBuilder(""); + if (!discType.equals("Card")) { + type.append(" ").append(discType); + } + final StringBuilder sb = new StringBuilder(); + sb.append("Select a "); + sb.append(part.getDescriptiveType()); + sb.append(" to reveal."); + if (nNeeded > 1) { + sb.append(" You have "); + sb.append(nNeeded - this.nReveal); + sb.append(" remaining."); + } + showMessage(sb.toString()); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectCard(final Card card) { + Zone zone = Singletons.getModel().getGame().getZoneOf(card); + if (zone.is(ZoneType.Hand) && handList.contains(card)) { + // send in List for Typing + handList.remove(card); + part.executePayment(sa, card); + this.nReveal++; + + // in case no more cards in hand + if (this.nReveal == nNeeded) { + this.done(); + } else if (sa.getActivatingPlayer().getZone(ZoneType.Hand).size() == 0) { + // really + // shouldn't + // happen + this.cancel(); + } else { + this.showMessage(); + } + } + } + } + /** * Instantiates a new cost reveal. * @@ -140,6 +220,8 @@ public class CostReveal extends CostPartWithList { @Override public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { GuiChoose.oneOrNone("Revealed cards:", this.getList()); + for(Card c: getList()) // should not throw concurrent modification here - no items should be added. + executePayment(ability, c); } /* @@ -150,17 +232,19 @@ public class CostReveal extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { final Player activator = ability.getActivatingPlayer(); + final Card source = ability.getSourceCard(); final String amount = this.getAmount(); this.resetList(); if (this.payCostFromSource()) { - this.addToList(source); - payment.setPaidManaPart(this); + executePayment(ability, source); + return true; } else if (this.getType().equals("Hand")) { - this.setList(new ArrayList(activator.getCardsIn(ZoneType.Hand))); - payment.setPaidManaPart(this); + for(Card c : activator.getCardsIn(ZoneType.Hand)) + executePayment(ability, c); + return true; } else { Integer num = this.convertAmount(); @@ -175,16 +259,12 @@ public class CostReveal extends CostPartWithList { num = AbilityUtils.calculateAmount(source, amount, ability); } } - if (num > 0) { - final Input inp = CostReveal.inputRevealCost(this.getType(), handList, payment, this, ability, num); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - return false; - } else { - payment.setPaidManaPart(this); - } + if ( num == 0 ) return true; + + InputPayment inp = new InputPayReveal(this, this.getType(), handList, ability, num); + FThreads.setInputAndWait(inp); + return inp.isPaid(); } - this.addListToHash(ability, "Revealed"); - return true; } /* @@ -220,102 +300,24 @@ public class CostReveal extends CostPartWithList { return sb.toString(); } + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) + */ + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + addToList(targetCard); + // write code to actually reveal card + } + + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() + */ + @Override + public String getHashForList() { + return "Revealed"; + } + // Inputs - /** - *

- * input_discardCost. - *

- * - * @param discType - * a {@link java.lang.String} object. - * @param handList - * a {@link forge.CardList} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param part - * TODO - * @param sa - * TODO - * @param nNeeded - * a int. - * @return a {@link forge.control.input.Input} object. - */ - public static Input inputRevealCost(final String discType, final List handList, final CostPayment payment, - final CostReveal part, final SpellAbility sa, final int nNeeded) { - final Input target = new Input() { - private static final long serialVersionUID = -329993322080934435L; - - private int nReveal = 0; - - @Override - public void showMessage() { - if (nNeeded == 0) { - this.done(); - } - - /*if (handList.size() + this.nReveal < nNeeded) { - this.stop(); - }*/ - final StringBuilder type = new StringBuilder(""); - if (!discType.equals("Card")) { - type.append(" ").append(discType); - } - final StringBuilder sb = new StringBuilder(); - sb.append("Select a "); - sb.append(part.getDescriptiveType()); - sb.append(" to reveal."); - if (nNeeded > 1) { - sb.append(" You have "); - sb.append(nNeeded - this.nReveal); - sb.append(" remaining."); - } - CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - @Override - public void selectCard(final Card card) { - Zone zone = Singletons.getModel().getGame().getZoneOf(card); - if (zone.is(ZoneType.Hand) && handList.contains(card)) { - // send in List for Typing - handList.remove(card); - part.addToList(card); - this.nReveal++; - - // in case no more cards in hand - if (this.nReveal == nNeeded) { - this.done(); - } else if (sa.getActivatingPlayer().getZone(ZoneType.Hand).size() == 0) { - // really - // shouldn't - // happen - this.cancel(); - } else { - this.showMessage(); - } - } - } - - public void cancel() { - this.stop(); - payment.cancelCost(); - } - - public void done() { - this.stop(); - // "Inform" AI of the revealed cards - part.addListToHash(sa, "Revealed"); - payment.paidCost(part); - } - }; - - return target; - } // input_discard() } diff --git a/src/main/java/forge/card/cost/CostSacrifice.java b/src/main/java/forge/card/cost/CostSacrifice.java index 75d1994e5f1..01c3156e397 100644 --- a/src/main/java/forge/card/cost/CostSacrifice.java +++ b/src/main/java/forge/card/cost/CostSacrifice.java @@ -19,20 +19,18 @@ package forge.card.cost; import java.util.ArrayList; import java.util.List; - -import javax.swing.JOptionPane; - import forge.Card; import forge.CardLists; -import forge.Singletons; +import forge.FThreads; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; +import forge.control.input.InputPayment; import forge.game.GameState; import forge.game.ai.ComputerUtil; import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.ZoneType; +import forge.gui.GuiDialog; import forge.gui.match.CMatchUI; import forge.view.ButtonUtil; @@ -41,6 +39,70 @@ import forge.view.ButtonUtil; */ public class CostSacrifice extends CostPartWithList { + /** + * TODO: Write javadoc for this type. + * + */ + public static final class InputPayCostSacrificeFromList extends InputPayCostBase { + private final CostSacrifice part; + private final SpellAbility sa; + private final int nNeeded; + private final List typeList; + private static final long serialVersionUID = 2685832214519141903L; + private int nSacrifices = 0; + + /** + * TODO: Write javadoc for Constructor. + * @param part + * @param sa + * @param nNeeded + * @param payment + * @param typeList + */ + public InputPayCostSacrificeFromList(CostSacrifice part, SpellAbility sa, int nNeeded, List typeList) { + this.part = part; + this.sa = sa; + this.nNeeded = nNeeded; + this.typeList = typeList; + } + + @Override + public void showMessage() { + if (nNeeded == 0) { + this.done(); + } + + final StringBuilder msg = new StringBuilder("Sacrifice "); + final int nLeft = nNeeded - this.nSacrifices; + msg.append(nLeft).append(" "); + msg.append(part.getDescriptiveType()); + if (nLeft > 1) { + msg.append("s"); + } + + CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectCard(final Card card) { + if (typeList.contains(card)) { + this.nSacrifices++; + part.executePayment(sa, card); + typeList.remove(card); + // in case nothing else to sacrifice + if (this.nSacrifices == nNeeded) { + this.done(); + } else if (typeList.isEmpty()) { + // happen + this.cancel(); + } else { + this.showMessage(); + } + } + } + } + /** * Instantiates a new cost sacrifice. * @@ -132,7 +194,7 @@ public class CostSacrifice extends CostPartWithList { public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { this.addListToHash(ability, "Sacrificed"); for (final Card c : this.getList()) { - Singletons.getModel().getGame().getAction().sacrifice(c, ability); + executePayment(ability, c); } } @@ -144,8 +206,9 @@ public class CostSacrifice extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { final String amount = this.getAmount(); + final Card source = ability.getSourceCard(); final String type = this.getType(); final Player activator = ability.getActivatingPlayer(); List list = new ArrayList(activator.getCardsIn(ZoneType.Battlefield)); @@ -155,32 +218,36 @@ public class CostSacrifice extends CostPartWithList { } if (this.payCostFromSource()) { - final Input inp = CostSacrifice.sacrificeThis(ability, payment, this); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); + if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) { + if (!GuiDialog.confirm(source, source.getName() + " - Sacrifice?")) + return false; + executePayment(ability, source); + return true; + } } else if (amount.equals("All")) { this.setList(list); - CostSacrifice.sacrificeAll(ability, payment, this, list); - //this.addListToHash(ability, "Sacrificed"); + // TODO Ask First + for (final Card card : list) { + executePayment(ability, card); + } return true; } else { Integer c = this.convertAmount(); if (c == null) { - final String sVar = ability.getSVar(amount); // Generalize this - if (sVar.equals("XChoice")) { + if (ability.getSVar(amount).equals("XChoice")) { c = CostUtil.chooseXValue(source, ability, list.size()); } else { c = AbilityUtils.calculateAmount(source, amount, ability); } } if (0 == c.intValue()) { - payment.setPaidManaPart(this); return true; } - final Input inp = CostSacrifice.sacrificeFromList(ability, payment, this, list, c); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); + InputPayment inp = new InputPayCostSacrificeFromList(this, ability, c, list); + FThreads.setInputAndWait(inp); + return inp.isPaid(); } - return false; } @@ -222,156 +289,23 @@ public class CostSacrifice extends CostPartWithList { return true; } - // Inputs - - /** - *

- * sacrificeAllType. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param part - * TODO - * @param typeList - * TODO + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ - public static void sacrificeAll(final SpellAbility sa, final CostPayment payment, final CostPart part, - final List typeList) { - // TODO Ask First - for (final Card card : typeList) { - payment.getAbility().addCostToHashList(card, "Sacrificed"); - Singletons.getModel().getGame().getAction().sacrifice(card, sa); - } - - payment.setPaidManaPart(part); + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + this.addToList(targetCard); + ability.getActivatingPlayer().getGame().getAction().sacrifice(targetCard, ability); } - /** - *

- * sacrificeFromList. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param part - * TODO - * @param typeList - * TODO - * @param nNeeded - * the n needed - * @return a {@link forge.control.input.Input} object. + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() */ - public static Input sacrificeFromList(final SpellAbility sa, final CostPayment payment, final CostSacrifice part, - final List typeList, final int nNeeded) { - final Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - private int nSacrifices = 0; + @Override + public String getHashForList() { + return "Sacrificed"; + } - @Override - public void showMessage() { - if (nNeeded == 0) { - this.done(); - } + // Inputs - final StringBuilder msg = new StringBuilder("Sacrifice "); - final int nLeft = nNeeded - this.nSacrifices; - msg.append(nLeft).append(" "); - msg.append(part.getDescriptiveType()); - if (nLeft > 1) { - msg.append("s"); - } - - CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - @Override - public void selectCard(final Card card) { - if (typeList.contains(card)) { - this.nSacrifices++; - part.addToList(card); - Singletons.getModel().getGame().getAction().sacrifice(card, sa); - typeList.remove(card); - // in case nothing else to sacrifice - if (this.nSacrifices == nNeeded) { - this.done(); - } else if (typeList.size() == 0) { - // happen - this.cancel(); - } else { - this.showMessage(); - } - } - } - - public void done() { - this.stop(); - part.addListToHash(sa, "Sacrificed"); - payment.paidCost(part); - } - - public void cancel() { - this.stop(); - - payment.cancelCost(); - } - }; - - return target; - } // sacrificeType() - - /** - *

- * sacrificeThis. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param part - * TODO - * @return a {@link forge.control.input.Input} object. - */ - public static Input sacrificeThis(final SpellAbility sa, final CostPayment payment, final CostSacrifice part) { - final Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - - @Override - public void showMessage() { - final Card card = sa.getSourceCard(); - if (card.getController().isHuman() && card.isInPlay()) { - final StringBuilder sb = new StringBuilder(); - sb.append(card.getName()); - sb.append(" - Sacrifice?"); - final Object[] possibleValues = { "Yes", "No" }; - final Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, possibleValues, - possibleValues[0]); - if (choice.equals(0)) { - part.addToList(card); - part.addListToHash(sa, "Sacrificed"); - Singletons.getModel().getGame().getAction().sacrifice(card, sa); - this.stop(); - payment.paidCost(part); - } else { - this.stop(); - payment.cancelCost(); - } - } - } - }; - - return target; - } // input_sacrifice() } diff --git a/src/main/java/forge/card/cost/CostTap.java b/src/main/java/forge/card/cost/CostTap.java index 43ba105933f..0a1e19c2fd4 100644 --- a/src/main/java/forge/card/cost/CostTap.java +++ b/src/main/java/forge/card/cost/CostTap.java @@ -93,13 +93,12 @@ public class CostTap extends CostPart { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { // if (!canPay(ability, source, ability.getActivatingPlayer(), // payment.getCost())) // return false; - source.tap(); - payment.setPaidManaPart(this); + ability.getSourceCard().tap(); return true; } diff --git a/src/main/java/forge/card/cost/CostTapType.java b/src/main/java/forge/card/cost/CostTapType.java index c08f2660054..c9ea3369ce0 100644 --- a/src/main/java/forge/card/cost/CostTapType.java +++ b/src/main/java/forge/card/cost/CostTapType.java @@ -19,21 +19,20 @@ package forge.card.cost; import java.util.ArrayList; import java.util.List; - import forge.Card; import forge.CardLists; import forge.CardPredicates.Presets; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; +import forge.control.input.InputPayment; import forge.game.GameState; import forge.game.ai.ComputerUtil; import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.Zone; import forge.game.zone.ZoneType; -import forge.gui.match.CMatchUI; import forge.view.ButtonUtil; /** @@ -41,6 +40,66 @@ import forge.view.ButtonUtil; */ public class CostTapType extends CostPartWithList { + /** + * TODO: Write javadoc for this type. + * + */ + public static final class InputPayCostTapType extends InputPayCostBase { + private final CostTapType tapType; + private final int nCards; + private final List cardList; + private static final long serialVersionUID = 6438988130447851042L; + private int nTapped = 0; + + /** + * TODO: Write javadoc for Constructor. + * @param sa + * @param tapType + * @param nCards + * @param cardList + * @param payment + */ + public InputPayCostTapType(CostTapType tapType, int nCards, List cardList) { + this.tapType = tapType; + this.nCards = nCards; + this.cardList = cardList; + } + + @Override + public void showMessage() { + + final int left = nCards - this.nTapped; + showMessage("Select a " + tapType.getDescription() + " to tap (" + left + " left)"); + ButtonUtil.enableOnlyCancel(); + if (nCards == 0) { + this.done(); + } + } + + + + @Override + public void selectCard(final Card card) { + Zone zone = Singletons.getModel().getGame().getZoneOf(card); + if (zone.is(ZoneType.Battlefield) && cardList.contains(card) && card.isUntapped()) { + // send in List for Typing + tapType.executePayment(null, card); + cardList.remove(card); + + this.nTapped++; + + if (this.nTapped == nCards) { + this.done(); + } else if (cardList.size() == 0) { + // happen + this.cancel(); + } else { + this.showMessage(); + } + } + } + } + /** * Instantiates a new cost tap type. * @@ -149,11 +208,12 @@ public class CostTapType extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { List typeList = new ArrayList(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield)); typeList = CardLists.getValidCards(typeList, this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); typeList = CardLists.filter(typeList, Presets.UNTAPPED); final String amount = this.getAmount(); + final Card source = ability.getSourceCard(); Integer c = this.convertAmount(); if (c == null) { final String sVar = ability.getSVar(amount); @@ -164,10 +224,9 @@ public class CostTapType extends CostPartWithList { c = AbilityUtils.calculateAmount(source, amount, ability); } } - - final Input inp = CostTapType.inputTapXCost(this, typeList, ability, payment, c); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - return false; + InputPayment inp = new InputPayCostTapType(this, c, typeList); + FThreads.setInputAndWait(inp); + return inp.isPaid(); } /* @@ -205,83 +264,23 @@ public class CostTapType extends CostPartWithList { return true; } + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) + */ + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + addToList(targetCard); + targetCard.tap(); + } + + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() + */ + @Override + public String getHashForList() { + return "Tapped"; + } + // Inputs - /** - *

- * input_tapXCost. - *

- * - * @param tapType - * the tap type - * @param cardList - * a {@link forge.CardList} object. - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param nCards - * a int. - * @return a {@link forge.control.input.Input} object. - */ - public static Input inputTapXCost(final CostTapType tapType, final List cardList, final SpellAbility sa, - final CostPayment payment, final int nCards) { - final Input target = new Input() { - - private static final long serialVersionUID = 6438988130447851042L; - private int nTapped = 0; - - @Override - public void showMessage() { - - final int left = nCards - this.nTapped; - CMatchUI.SINGLETON_INSTANCE - .showMessage("Select a " + tapType.getDescription() + " to tap (" + left + " left)"); - ButtonUtil.enableOnlyCancel(); - if (nCards == 0) { - this.done(); - } - } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - @Override - public void selectCard(final Card card) { - Zone zone = Singletons.getModel().getGame().getZoneOf(card); - if (zone.is(ZoneType.Battlefield) && cardList.contains(card) && card.isUntapped()) { - // send in List for Typing - card.tap(); - tapType.addToList(card); - cardList.remove(card); - - this.nTapped++; - - if (this.nTapped == nCards) { - this.done(); - } else if (cardList.size() == 0) { - // happen - this.cancel(); - } else { - this.showMessage(); - } - } - } - - public void cancel() { - this.stop(); - payment.cancelCost(); - } - - public void done() { - this.stop(); - tapType.addListToHash(sa, "Tapped"); - payment.paidCost(tapType); - } - }; - - return target; - } // input_tapXCost() } diff --git a/src/main/java/forge/card/cost/CostUnattach.java b/src/main/java/forge/card/cost/CostUnattach.java index cec1332bbab..df8b5cfd548 100644 --- a/src/main/java/forge/card/cost/CostUnattach.java +++ b/src/main/java/forge/card/cost/CostUnattach.java @@ -114,23 +114,16 @@ public class CostUnattach extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { - this.resetList(); + public final boolean payHuman(final SpellAbility ability, final GameState game) { + final Card source = ability.getSourceCard(); Player activator = ability.getActivatingPlayer(); Card cardToUnattach = findCardToUnattach(source, activator, ability); if (cardToUnattach != null && GuiDialog.confirm(source, String.format("Unattach %s?", cardToUnattach.toString()))) { - Card equippingCard = cardToUnattach.getEquipping().get(0); - cardToUnattach.unEquipCard(equippingCard); - this.addToList(cardToUnattach); - this.addListToHash(ability, "Unattached"); - payment.setPaidManaPart(this); + executePayment(ability, cardToUnattach); + return true; } else { - payment.setCancel(true); - payment.getRequirements().finishPaying(); return false; } - - return true; } private Card findCardToUnattach(final Card source, Player activator, SpellAbility ability) { @@ -165,4 +158,18 @@ public class CostUnattach extends CostPartWithList { this.resetList(); return true; } + + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) + */ + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + targetCard.unEquipCard(targetCard.getEquipping().get(0)); + this.addToList(targetCard); + } + + @Override + public String getHashForList() { + return "Unattached"; + } } diff --git a/src/main/java/forge/card/cost/CostUntap.java b/src/main/java/forge/card/cost/CostUntap.java index 04bbc79d314..d3355e9bc49 100644 --- a/src/main/java/forge/card/cost/CostUntap.java +++ b/src/main/java/forge/card/cost/CostUntap.java @@ -92,13 +92,12 @@ public class CostUntap extends CostPart { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { + public final boolean payHuman(final SpellAbility ability, final GameState game) { // if (!canPay(ability, source, ability.getActivatingPlayer(), // payment.getCost())) // return false; - source.untap(); - payment.setPaidManaPart(this); + ability.getSourceCard().untap(); return true; } diff --git a/src/main/java/forge/card/cost/CostUntapType.java b/src/main/java/forge/card/cost/CostUntapType.java index 57bffb896f0..d33a89556e3 100644 --- a/src/main/java/forge/card/cost/CostUntapType.java +++ b/src/main/java/forge/card/cost/CostUntapType.java @@ -18,21 +18,20 @@ package forge.card.cost; import java.util.List; - import forge.Card; import forge.CardLists; import forge.CardPredicates.Presets; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; +import forge.control.input.InputPayment; import forge.game.GameState; import forge.game.ai.ComputerUtil; import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.Zone; import forge.game.zone.ZoneType; -import forge.gui.match.CMatchUI; import forge.view.ButtonUtil; /** @@ -40,6 +39,71 @@ import forge.view.ButtonUtil; */ public class CostUntapType extends CostPartWithList { + /** + * TODO: Write javadoc for this type. + * + */ + public static final class InputPayCostUntapY extends InputPayCostBase { + private final int nCards; + private final List cardList; + private final CostUntapType untapType; + private static final long serialVersionUID = -7151144318287088542L; + private int nUntapped = 0; + + + /** + * TODO: Write javadoc for Constructor. + * @param nCards + * @param cardList + * @param untapType + * @param sa + * @param payment + */ + public InputPayCostUntapY(int nCards, List cardList, CostUntapType untapType) { + this.nCards = nCards; + this.cardList = cardList; + this.untapType = untapType; + } + + @Override + public void showMessage() { + if (nCards == 0) { + this.done(); + } + + if (cardList.size() == 0) { + this.stop(); + } + + final int left = nCards - this.nUntapped; + showMessage("Select a " + untapType.getDescription() + " to untap (" + left + " left)"); + ButtonUtil.enableOnlyCancel(); + } + + + + @Override + public void selectCard(final Card card) { + Zone zone = Singletons.getModel().getGame().getZoneOf(card); + if (zone.is(ZoneType.Battlefield) && cardList.contains(card) && card.isTapped()) { + // send in List for Typing + card.untap(); + untapType.addToList(card); + cardList.remove(card); + + this.nUntapped++; + + if (this.nUntapped == nCards) { + this.done(); + } else if (cardList.size() == 0) { + this.cancel(); + } else { + this.showMessage(); + } + } + } + } + /** * Instantiates a new cost untap type. * @@ -162,12 +226,13 @@ public class CostUntapType extends CostPartWithList { * forge.Card, forge.card.cost.Cost_Payment) */ @Override - public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { - final boolean untap = payment.getCost().hasUntapCost(); + public final boolean payHuman(final SpellAbility ability, final GameState game) { + final boolean canUntapSource = false; // payment.getCost().hasUntapCost(); - only Crackleburr uses this List typeList = Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield); typeList = CardLists.getValidCards(typeList, this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); typeList = CardLists.filter(typeList, Presets.TAPPED); - if (untap) { + final Card source = ability.getSourceCard(); + if (canUntapSource) { typeList.remove(source); } final String amount = this.getAmount(); @@ -181,10 +246,9 @@ public class CostUntapType extends CostPartWithList { c = AbilityUtils.calculateAmount(source, amount, ability); } } - - final Input inp = CostUntapType.inputUntapYCost(this, typeList, ability, payment, c); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - return false; + InputPayment inp = new InputPayCostUntapY(c, typeList, this); + FThreads.setInputAndWait(inp); + return inp.isPaid(); } /* @@ -225,86 +289,23 @@ public class CostUntapType extends CostPartWithList { return true; } + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) + */ + @Override + public void executePayment(SpellAbility ability, Card targetCard) { + addToList(targetCard); + targetCard.untap(); + } + + /* (non-Javadoc) + * @see forge.card.cost.CostPartWithList#getHashForList() + */ + @Override + public String getHashForList() { + return "Untapped"; + } + // Inputs - /** - *

- * input_untapYCost. - *

- * - * @param untapType - * the untap type - * @param cardList - * a {@link forge.CardList} object. - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param payment - * a {@link forge.card.cost.CostPayment} object. - * @param nCards - * a int. - * @return a {@link forge.control.input.Input} object. - */ - public static Input inputUntapYCost(final CostUntapType untapType, final List cardList, final SpellAbility sa, - final CostPayment payment, final int nCards) { - final Input target = new Input() { - - private static final long serialVersionUID = -7151144318287088542L; - private int nUntapped = 0; - - @Override - public void showMessage() { - if (nCards == 0) { - this.done(); - } - - if (cardList.size() == 0) { - this.stop(); - } - - final int left = nCards - this.nUntapped; - CMatchUI.SINGLETON_INSTANCE - .showMessage("Select a " + untapType.getDescription() + " to untap (" + left + " left)"); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - this.cancel(); - } - - @Override - public void selectCard(final Card card) { - Zone zone = Singletons.getModel().getGame().getZoneOf(card); - if (zone.is(ZoneType.Battlefield) && cardList.contains(card) && card.isTapped()) { - // send in List for Typing - card.untap(); - untapType.addToList(card); - cardList.remove(card); - - this.nUntapped++; - - if (this.nUntapped == nCards) { - this.done(); - } else if (cardList.size() == 0) { - this.cancel(); - } else { - this.showMessage(); - } - } - } - - public void cancel() { - this.stop(); - payment.cancelCost(); - } - - public void done() { - this.stop(); - untapType.addListToHash(sa, "Untapped"); - payment.paidCost(untapType); - } - }; - - return target; - } // input_untapYCost() } diff --git a/src/main/java/forge/card/cost/InputPayCostBase.java b/src/main/java/forge/card/cost/InputPayCostBase.java new file mode 100644 index 00000000000..00847b3c488 --- /dev/null +++ b/src/main/java/forge/card/cost/InputPayCostBase.java @@ -0,0 +1,29 @@ +package forge.card.cost; + +import forge.control.input.InputPayment; +import forge.control.input.InputSyncronizedBase; + +/** + * TODO: Write javadoc for this type. + * + */ +abstract class InputPayCostBase extends InputSyncronizedBase implements InputPayment { + private static final long serialVersionUID = -2967434867139585579L; + boolean bPaid = false; + + @Override + final public void selectButtonCancel() { + this.cancel(); + } + + final protected void done() { + bPaid = true; + this.stop(); + } + + final public void cancel() { + this.stop(); + } + + final public boolean isPaid() { return bPaid; } +} \ No newline at end of file diff --git a/src/main/java/forge/card/mana/ManaCost.java b/src/main/java/forge/card/mana/ManaCost.java index 2fab180f2ab..cf3f7d7e695 100644 --- a/src/main/java/forge/card/mana/ManaCost.java +++ b/src/main/java/forge/card/mana/ManaCost.java @@ -44,7 +44,21 @@ public final class ManaCost implements Comparable { public static final ManaCost NO_COST = new ManaCost(-1); public static final ManaCost ZERO = new ManaCost(0); public static final ManaCost ONE = new ManaCost(1); + public static final ManaCost TWO = new ManaCost(2); + public static final ManaCost THREE = new ManaCost(3); + public static final ManaCost FOUR = new ManaCost(4); + public static ManaCost get(int cntColorless) { + switch (cntColorless) { + case 0: return ZERO; + case 1: return ONE; + case 2: return TWO; + case 3: return THREE; + case 4: return FOUR; + } + return cntColorless > 0 ? new ManaCost(cntColorless) : NO_COST; + } + // pass mana cost parser here private ManaCost(int cmc) { this.hasNoCost = cmc < 0; diff --git a/src/main/java/forge/card/mana/ManaCostBeingPaid.java b/src/main/java/forge/card/mana/ManaCostBeingPaid.java index 9cb5a7ef730..224309c1312 100644 --- a/src/main/java/forge/card/mana/ManaCostBeingPaid.java +++ b/src/main/java/forge/card/mana/ManaCostBeingPaid.java @@ -41,6 +41,8 @@ public class ManaCostBeingPaid { private int cntX = 0; private final ArrayList manaNeededToAvoidNegativeEffect = new ArrayList(); private final ArrayList manaPaidToAvoidNegativeEffect = new ArrayList(); + + private final ManaCost originalCost; // manaCost can be like "0", "3", "G", "GW", "10", "3 GW", "10 GW" // or "split hybrid mana" like "2/G 2/G", "2/B 2/B 2/B" @@ -59,6 +61,7 @@ public class ManaCostBeingPaid { } public ManaCostBeingPaid(ManaCost manaCost) { + originalCost = manaCost; if ( null == manaCost ) return; @@ -645,4 +648,8 @@ public class ManaCostBeingPaid { public final ArrayList getManaPaidToAvoidNegativeEffect() { return this.manaPaidToAvoidNegativeEffect; } + + public ManaCost getStartingCost() { + return originalCost; + } } diff --git a/src/main/java/forge/card/spellability/SpellAbility.java b/src/main/java/forge/card/spellability/SpellAbility.java index c21d1ef742c..9988f964f09 100644 --- a/src/main/java/forge/card/spellability/SpellAbility.java +++ b/src/main/java/forge/card/spellability/SpellAbility.java @@ -23,8 +23,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.commons.lang.StringUtils; - import forge.Card; import forge.GameEntity; import forge.Singletons; @@ -1682,25 +1680,4 @@ public abstract class SpellAbility implements ISpellAbility { public void setCopied(boolean isCopied0) { this.isCopied = isCopied0; } - - public boolean announceRequirements() { - // Announcing Requirements like Choosing X or Multikicker - // SA Params as comma delimited list - String announce = this.getParam("Announce"); - if (announce != null) { - String[] announceVars = announce.split(","); - for(String aVar : announceVars) { - String value = this.getActivatingPlayer().getController().announceRequirements(this, aVar); - if (value == null || !StringUtils.isNumeric(value)) { - return false; - } else if (this.getPayCosts().getCostMana() != null && this.getPayCosts().getCostMana().isxCantBe0() - && Integer.parseInt(value) == 0) { - return false; - } - this.setSVar(aVar, "Number$" + value); - this.getSourceCard().setSVar(aVar, "Number$" + value); - } - } - return true; - } } diff --git a/src/main/java/forge/card/spellability/SpellAbilityRequirements.java b/src/main/java/forge/card/spellability/SpellAbilityRequirements.java index 9978017329f..15f4c28b9ea 100644 --- a/src/main/java/forge/card/spellability/SpellAbilityRequirements.java +++ b/src/main/java/forge/card/spellability/SpellAbilityRequirements.java @@ -19,6 +19,8 @@ package forge.card.spellability; import java.util.ArrayList; +import org.apache.commons.lang3.StringUtils; + import forge.Card; import forge.CardCharacteristicName; import forge.Singletons; @@ -44,67 +46,26 @@ public class SpellAbilityRequirements { private Zone fromZone = null; private Integer zonePosition = null; - /** - *

- * Setter for the field skipStack. - *

- * - * @param bSkip - * a boolean. - */ + public final void setSkipStack(final boolean bSkip) { this.skipStack = bSkip; } - - /** - *

- * setFree. - *

- * - * @param bFree - * a boolean. - */ + public final void setFree(final boolean bFree) { this.isFree = bFree; } - - /** - *

- * Constructor for SpellAbility_Requirements. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param ts - * a {@link forge.card.spellability.TargetSelection} object. - * @param cp - * a {@link forge.card.cost.CostPayment} object. - */ public SpellAbilityRequirements(final SpellAbility sa, final TargetSelection ts, final CostPayment cp) { this.ability = sa; this.select = ts; this.payment = cp; } - /** - *

- * fillRequirements. - *

- */ public final void fillRequirements() { this.fillRequirements(false); } - /** - *

- * fillRequirements. - *

- * - * @param skipTargeting - * a boolean. - */ public final void fillRequirements(final boolean skipTargeting) { if ((this.ability instanceof Spell) && !this.bCasting) { // remove from hand @@ -118,14 +79,13 @@ public class SpellAbilityRequirements { } } - // freeze Stack. No abilities should go onto the stack while I'm filling - // requirements. + // freeze Stack. No abilities should go onto the stack while I'm filling requirements. Singletons.getModel().getGame().getStack().freezeStack(); // Announce things like how many times you want to Multikick or the value of X - if (!this.ability.announceRequirements()) { + if (!this.announceRequirements()) { this.select.setCancel(true); - this.finishedTargeting(); + rollbackAbility(); return; } @@ -134,128 +94,104 @@ public class SpellAbilityRequirements { // (or trigger case where its already targeted) if (!skipTargeting && (this.select.doesTarget() || (this.ability.getSubAbility() != null))) { this.select.setRequirements(this); - this.select.resetTargets(); + this.select.clearTargets(); this.select.chooseTargets(); - } else { - this.needPayment(); - } - } - - /** - *

- * finishedTargeting. - *

- */ - public final void finishedTargeting() { - if (this.select.isCanceled()) { - // cancel ability during target choosing - final Card c = this.ability.getSourceCard(); - - // split cards transform back to full form if targeting is canceled - if (c.isSplitCard()) { - c.setState(CardCharacteristicName.Original); + if (this.select.isCanceled()) { + rollbackAbility(); + return; } - - if (this.bCasting && !c.isCopiedSpell()) { // and not a copy - // add back to where it came from - Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition); - } - - this.select.resetTargets(); - Singletons.getModel().getGame().getStack().removeFromFrozenStack(this.ability); - return; - } else { - this.needPayment(); } - } - - /** - *

- * needPayment. - *

- */ - public final void needPayment() { + + // Payment if (!this.isFree) { - this.startPaying(); - } else { - this.finishPaying(); - } - } - - /** - *

- * startPaying. - *

- */ - public final void startPaying() { - this.payment.setRequirements(this); - this.payment.changeCost(); - this.payment.payCost(); - } - - /** - *

- * finishPaying. - *

- */ - public final void finishPaying() { + this.payment.setRequirements(this); + this.payment.changeCost(); + this.payment.payCost(); + } + if (this.payment.isCanceled()) { - final Card c = this.ability.getSourceCard(); - - // split cards transform back to full form if mana cost is not paid - if (c.isSplitCard()) { - c.setState(CardCharacteristicName.Original); - } - - if (this.bCasting && !c.isCopiedSpell()) { // and not a copy - // add back to Previous Zone - Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition); - } - - if (this.select != null) { - this.select.resetTargets(); - } - - this.ability.resetOnceResolved(); - this.payment.cancelPayment(); - Singletons.getModel().getGame().getStack().clearFrozen(); + rollbackAbility(); + return; } + else if (this.isFree || this.payment.isAllPaid()) { if (this.skipStack) { AbilityUtils.resolve(this.ability, false); } else { - this.addAbilityToStack(); - } - this.select.resetTargets(); + this.enusureAbilityHasDescription(this.ability); + this.ability.getActivatingPlayer().getManaPool().clearManaPaid(this.ability, false); + Singletons.getModel().getGame().getStack().addAndUnfreeze(this.ability); + } + + // Warning about this - resolution may come in another thread, and it would still need its targets + this.select.clearTargets(); Singletons.getModel().getGame().getAction().checkStateEffects(); } } - /** - *

- * addAbilityToStack. - *

- */ - public final void addAbilityToStack() { - // For older abilities that don't setStackDescription set it here - if (this.ability.getStackDescription().equals("")) { - final StringBuilder sb = new StringBuilder(); - sb.append(this.ability.getSourceCard().getName()); - if (this.ability.getTarget() != null) { - final ArrayList targets = this.ability.getTarget().getTargets(); - if (targets.size() > 0) { - sb.append(" - Targeting "); - for (final Object o : targets) { - sb.append(o.toString()).append(" "); - } - } - } + private void rollbackAbility() { + // cancel ability during target choosing + final Card c = this.ability.getSourceCard(); - this.ability.setStackDescription(sb.toString()); + // split cards transform back to full form if targeting is canceled + if (c.isSplitCard()) { + c.setState(CardCharacteristicName.Original); } - this.ability.getActivatingPlayer().getManaPool().clearManaPaid(this.ability, false); - Singletons.getModel().getGame().getStack().addAndUnfreeze(this.ability); + if (this.bCasting && !c.isCopiedSpell()) { // and not a copy + // add back to where it came from + Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition); + } + + if (this.select != null) { + this.select.clearTargets(); + } + + this.ability.resetOnceResolved(); + this.payment.cancelPayment(); + Singletons.getModel().getGame().getStack().clearFrozen(); + // Singletons.getModel().getGame().getStack().removeFromFrozenStack(this.ability); + } + + + public boolean announceRequirements() { + // Announcing Requirements like Choosing X or Multikicker + // SA Params as comma delimited list + String announce = ability.getParam("Announce"); + if (announce != null) { + for(String aVar : announce.split(",")) { + String value = ability.getActivatingPlayer().getController().announceRequirements(ability, aVar); + if (value == null || !StringUtils.isNumeric(value)) { + return false; + } else if (ability.getPayCosts().getCostMana() != null && !ability.getPayCosts().getCostMana().canXbe0() + && Integer.parseInt(value) == 0) { + return false; + } + ability.setSVar(aVar, "Number$" + value); + ability.getSourceCard().setSVar(aVar, "Number$" + value); + } + } + return true; + } + + private void enusureAbilityHasDescription(SpellAbility ability) { + if (!StringUtils.isBlank(ability.getStackDescription())) + return; + + // For older abilities that don't setStackDescription set it here + final StringBuilder sb = new StringBuilder(); + sb.append(ability.getSourceCard().getName()); + if (ability.getTarget() != null) { + final ArrayList targets = ability.getTarget().getTargets(); + if (targets.size() > 0) { + sb.append(" - Targeting "); + for (final Object o : targets) { + sb.append(o.toString()).append(" "); + } + } + } + + ability.setStackDescription(sb.toString()); } } diff --git a/src/main/java/forge/card/spellability/TargetSelection.java b/src/main/java/forge/card/spellability/TargetSelection.java index 03286649df0..f07ef9fc1af 100644 --- a/src/main/java/forge/card/spellability/TargetSelection.java +++ b/src/main/java/forge/card/spellability/TargetSelection.java @@ -20,15 +20,16 @@ package forge.card.spellability; import java.util.ArrayList; import java.util.HashMap; import java.util.List; - import com.google.common.base.Predicate; import forge.Card; import forge.CardLists; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; -import forge.control.input.Input; +import forge.control.input.InputSynchronized; +import forge.control.input.InputSyncronizedBase; import forge.game.player.Player; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; @@ -44,6 +45,174 @@ import forge.view.ButtonUtil; * @version $Id$ */ public class TargetSelection { + /** + * TODO: Write javadoc for this type. + * + */ + public final class InputSelectTargets extends InputSyncronizedBase { + private final TargetSelection select; + private final List choices; + private final ArrayList alreadyTargeted; + private final boolean targeted; + private final Target tgt; + private final SpellAbility sa; + private final boolean mandatory; + private static final long serialVersionUID = -1091595663541356356L; + + /** + * TODO: Write javadoc for Constructor. + * @param select + * @param choices + * @param req + * @param alreadyTargeted + * @param targeted + * @param tgt + * @param sa + * @param mandatory + */ + public InputSelectTargets(TargetSelection select, List choices, ArrayList alreadyTargeted, boolean targeted, Target tgt, SpellAbility sa, boolean mandatory) { + this.select = select; + this.choices = choices; + this.alreadyTargeted = alreadyTargeted; + this.targeted = targeted; + this.tgt = tgt; + this.sa = sa; + this.mandatory = mandatory; + } + + @Override + public void showMessage() { + final StringBuilder sb = new StringBuilder(); + sb.append("Targeted: "); + for (final Object o : alreadyTargeted) { + sb.append(o).append(" "); + } + sb.append(tgt.getTargetedString()); + sb.append("\n"); + sb.append(tgt.getVTSelection()); + + CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); + + // If reached Minimum targets, enable OK button + if (!tgt.isMinTargetsChosen(sa.getSourceCard(), sa) || tgt.isDividedAsYouChoose()) { + if (mandatory && tgt.hasCandidates(sa, true)) { + // Player has to click on a target + ButtonUtil.disableAll(); + } else { + ButtonUtil.enableOnlyCancel(); + } + } else { + if (mandatory && tgt.hasCandidates(sa, true)) { + // Player has to click on a target or ok + ButtonUtil.enableOnlyOk(); + } else { + ButtonUtil.enableAllFocusOk(); + } + } + } + + @Override + public void selectButtonCancel() { + select.setCancel(true); + this.done(); + } + + @Override + public void selectButtonOK() { + this.done(); + } + + @Override + public void selectCard(final Card card) { + // leave this in temporarily, there some seriously wrong things + // going on here + if (targeted && !card.canBeTargetedBy(sa)) { + CMatchUI.SINGLETON_INSTANCE.showMessage("Cannot target this card (Shroud? Protection? Restrictions?)."); + } else if (choices.contains(card)) { + if (tgt.isDividedAsYouChoose()) { + final int stillToDivide = tgt.getStillToDivide(); + int allocatedPortion = 0; + // allow allocation only if the max targets isn't reached and there are more candidates + if ((tgt.getNumTargeted() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) + && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { + final Integer[] choices = new Integer[stillToDivide]; + for (int i = 1; i <= stillToDivide; i++) { + choices[i - 1] = i; + } + String apiBasedMessage = "Distribute how much to "; + if (sa.getApi() == ApiType.DealDamage) { + apiBasedMessage = "Select how much damage to deal to "; + } else if (sa.getApi() == ApiType.PreventDamage) { + apiBasedMessage = "Select how much damage to prevent to "; + } else if (sa.getApi() == ApiType.PutCounter) { + apiBasedMessage = "Select how many counters to distribute to "; + } + final StringBuilder sb = new StringBuilder(); + sb.append(apiBasedMessage); + sb.append(card.toString()); + Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); + if (null == chosen) { + return; + } + allocatedPortion = chosen; + } else { // otherwise assign the rest of the damage/protection + allocatedPortion = stillToDivide; + } + tgt.setStillToDivide(stillToDivide - allocatedPortion); + tgt.addDividedAllocation(card, allocatedPortion); + } + tgt.addTarget(card); + this.done(); + } + } // selectCard() + + @Override + public void selectPlayer(final Player player) { + if (alreadyTargeted.contains(player)) { + return; + } + + if (sa.canTarget(player)) { + if (tgt.isDividedAsYouChoose()) { + final int stillToDivide = tgt.getStillToDivide(); + int allocatedPortion = 0; + // allow allocation only if the max targets isn't reached and there are more candidates + if ((alreadyTargeted.size() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) + && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { + final Integer[] choices = new Integer[stillToDivide]; + for (int i = 1; i <= stillToDivide; i++) { + choices[i - 1] = i; + } + String apiBasedMessage = "Distribute how much to "; + if (sa.getApi() == ApiType.DealDamage) { + apiBasedMessage = "Select how much damage to deal to "; + } else if (sa.getApi() == ApiType.PreventDamage) { + apiBasedMessage = "Select how much damage to prevent to "; + } + final StringBuilder sb = new StringBuilder(); + sb.append(apiBasedMessage); + sb.append(player.getName()); + Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); + if (null == chosen) { + return; + } + allocatedPortion = chosen; + } else { // otherwise assign the rest of the damage/protection + allocatedPortion = stillToDivide; + } + tgt.setStillToDivide(stillToDivide - allocatedPortion); + tgt.addDividedAllocation(player, allocatedPortion); + } + tgt.addTarget(player); + this.done(); + } + } + + void done() { + this.stop(); + } + } + private Target target = null; private SpellAbility ability = null; private Card card = null; @@ -98,6 +267,7 @@ public class TargetSelection { } private boolean bCancel = false; + private boolean bTargetingDone = false; /** *

@@ -130,20 +300,6 @@ public class TargetSelection { return this.subSelection.isCanceled(); } - private boolean bDoneTarget = false; - - /** - *

- * setDoneTarget. - *

- * - * @param done - * a boolean. - */ - public final void setDoneTarget(final boolean done) { - this.bDoneTarget = done; - } - /** *

* Constructor for Target_Selection. @@ -179,7 +335,7 @@ public class TargetSelection { * resetTargets. *

*/ - public final void resetTargets() { + public final void clearTargets() { if (this.target != null) { this.target.resetTargets(); this.target.calculateStillToDivide(this.ability.getParam("DividedAsYouChoose"), this.getCard(), this.ability); @@ -195,24 +351,25 @@ public class TargetSelection { */ public final boolean chooseTargets() { // if not enough targets chosen, reset and cancel Ability - if (this.bCancel || (this.bDoneTarget && !this.target.isMinTargetsChosen(this.card, this.ability))) { + if (this.bCancel || (this.bTargetingDone && !this.target.isMinTargetsChosen(this.card, this.ability))) { this.bCancel = true; - this.req.finishedTargeting(); return false; - } else if (!this.doesTarget() || (this.bDoneTarget && this.target.isMinTargetsChosen(this.card, this.ability)) + } + + if (!this.doesTarget() + || this.bTargetingDone && this.target.isMinTargetsChosen(this.card, this.ability) || this.target.isMaxTargetsChosen(this.card, this.ability) - || (this.target.isDividedAsYouChoose() && this.target.getStillToDivide() == 0)) { + || this.target.isDividedAsYouChoose() && this.target.getStillToDivide() == 0) { final AbilitySub abSub = this.ability.getSubAbility(); if (abSub == null) { // if no more SubAbilities finish targeting - this.req.finishedTargeting(); return true; } else { // Has Sub Ability this.subSelection = new TargetSelection(abSub.getTarget(), abSub); this.subSelection.setRequirements(this.req); - this.subSelection.resetTargets(); + this.subSelection.clearTargets(); return this.subSelection.chooseTargets(); } } @@ -220,11 +377,12 @@ public class TargetSelection { if (!this.target.hasCandidates(this.ability, true) && !this.target.isMinTargetsChosen(this.card, this.ability)) { // Cancel ability if there aren't any valid Candidates this.bCancel = true; - this.req.finishedTargeting(); return false; } this.chooseValidInput(); + if ( !bCancel ) + return chooseTargets(); return false; } @@ -362,178 +520,14 @@ public class TargetSelection { } if (zone.contains(ZoneType.Battlefield) && zone.size() == 1) { - Singletons.getModel().getMatch().getInput().setInput(this.inputTargetSpecific(choices, true, mandatory, objects)); + InputSynchronized inp = new InputSelectTargets(this, choices, objects, true, this.target, this.ability, mandatory); + FThreads.setInputAndWait(inp); + bTargetingDone = !bCancel; } else { this.chooseCardFromList(choices, true, mandatory); } } // input_targetValid - // List choices are the only cards the user can successful select - /** - *

- * input_targetSpecific. - *

- * - * @param choices - * a {@link forge.CardList} object. - * @param targeted - * a boolean. - * @param mandatory - * a boolean. - * @param alreadyTargeted - * the already targeted - * @return a {@link forge.control.input.Input} object. - */ - public final Input inputTargetSpecific(final List choices, final boolean targeted, final boolean mandatory, - final ArrayList alreadyTargeted) { - final SpellAbility sa = this.ability; - final TargetSelection select = this; - final Target tgt = this.target; - final SpellAbilityRequirements req = this.req; - - final Input target = new Input() { - private static final long serialVersionUID = -1091595663541356356L; - - @Override - public void showMessage() { - final StringBuilder sb = new StringBuilder(); - sb.append("Targeted: "); - for (final Object o : alreadyTargeted) { - sb.append(o).append(" "); - } - sb.append(tgt.getTargetedString()); - sb.append("\n"); - sb.append(tgt.getVTSelection()); - - CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); - - // If reached Minimum targets, enable OK button - if (!tgt.isMinTargetsChosen(sa.getSourceCard(), sa) || tgt.isDividedAsYouChoose()) { - if (mandatory && tgt.hasCandidates(sa, true)) { - // Player has to click on a target - ButtonUtil.disableAll(); - } else { - ButtonUtil.enableOnlyCancel(); - } - } else { - if (mandatory && tgt.hasCandidates(sa, true)) { - // Player has to click on a target or ok - ButtonUtil.enableOnlyOk(); - } else { - ButtonUtil.enableAllFocusOk(); - } - } - } - - @Override - public void selectButtonCancel() { - select.setCancel(true); - this.stop(); - req.finishedTargeting(); - } - - @Override - public void selectButtonOK() { - select.setDoneTarget(true); - this.done(); - } - - @Override - public void selectCard(final Card card) { - // leave this in temporarily, there some seriously wrong things - // going on here - if (targeted && !card.canBeTargetedBy(sa)) { - CMatchUI.SINGLETON_INSTANCE.showMessage("Cannot target this card (Shroud? Protection? Restrictions?)."); - } else if (choices.contains(card)) { - if (tgt.isDividedAsYouChoose()) { - final int stillToDivide = tgt.getStillToDivide(); - int allocatedPortion = 0; - // allow allocation only if the max targets isn't reached and there are more candidates - if ((tgt.getNumTargeted() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) - && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { - final Integer[] choices = new Integer[stillToDivide]; - for (int i = 1; i <= stillToDivide; i++) { - choices[i - 1] = i; - } - String apiBasedMessage = "Distribute how much to "; - if (sa.getApi() == ApiType.DealDamage) { - apiBasedMessage = "Select how much damage to deal to "; - } else if (sa.getApi() == ApiType.PreventDamage) { - apiBasedMessage = "Select how much damage to prevent to "; - } else if (sa.getApi() == ApiType.PutCounter) { - apiBasedMessage = "Select how many counters to distribute to "; - } - final StringBuilder sb = new StringBuilder(); - sb.append(apiBasedMessage); - sb.append(card.toString()); - Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); - if (null == chosen) { - return; - } - allocatedPortion = chosen; - } else { // otherwise assign the rest of the damage/protection - allocatedPortion = stillToDivide; - } - tgt.setStillToDivide(stillToDivide - allocatedPortion); - tgt.addDividedAllocation(card, allocatedPortion); - } - tgt.addTarget(card); - this.done(); - } - } // selectCard() - - @Override - public void selectPlayer(final Player player) { - if (alreadyTargeted.contains(player)) { - return; - } - - if (sa.canTarget(player)) { - if (tgt.isDividedAsYouChoose()) { - final int stillToDivide = tgt.getStillToDivide(); - int allocatedPortion = 0; - // allow allocation only if the max targets isn't reached and there are more candidates - if ((alreadyTargeted.size() + 1 < tgt.getMaxTargets(sa.getSourceCard(), sa)) - && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) { - final Integer[] choices = new Integer[stillToDivide]; - for (int i = 1; i <= stillToDivide; i++) { - choices[i - 1] = i; - } - String apiBasedMessage = "Distribute how much to "; - if (sa.getApi() == ApiType.DealDamage) { - apiBasedMessage = "Select how much damage to deal to "; - } else if (sa.getApi() == ApiType.PreventDamage) { - apiBasedMessage = "Select how much damage to prevent to "; - } - final StringBuilder sb = new StringBuilder(); - sb.append(apiBasedMessage); - sb.append(player.getName()); - Integer chosen = GuiChoose.oneOrNone(sb.toString(), choices); - if (null == chosen) { - return; - } - allocatedPortion = chosen; - } else { // otherwise assign the rest of the damage/protection - allocatedPortion = stillToDivide; - } - tgt.setStillToDivide(stillToDivide - allocatedPortion); - tgt.addDividedAllocation(player, allocatedPortion); - } - tgt.addTarget(player); - this.done(); - } - } - - void done() { - this.stop(); - - select.chooseTargets(); - } - }; - - return target; - } // input_targetSpecific() - /** *

* chooseCardFromList. @@ -624,7 +618,7 @@ public class TargetSelection { if (!c.equals(divBattlefield) && !c.equals(divExile) && !c.equals(divGrave) && !c.equals(divLibrary) && !c.equals(divStack)) { if (c.equals(dummy)) { - this.setDoneTarget(true); + bTargetingDone = true; } else { tgt.addTarget(c); } @@ -632,8 +626,6 @@ public class TargetSelection { } else { this.setCancel(true); } - - this.chooseTargets(); } /** @@ -675,7 +667,7 @@ public class TargetSelection { if (madeChoice != null) { if (madeChoice.equals(doneDummy)) { - this.setDoneTarget(true); + bTargetingDone = true; } else { tgt.addTarget(map.get(madeChoice)); } @@ -683,8 +675,6 @@ public class TargetSelection { select.setCancel(true); } } - - select.chooseTargets(); } // TODO The following three functions are Utility functions for diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index c6922f5cc6e..15ccf0a43cd 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -155,7 +155,8 @@ public enum FControl { this.shortcuts = KeyboardShortcuts.attachKeyboardShortcuts(); this.display = FView.SINGLETON_INSTANCE.getLpnDocument(); - + + FSkin.setProgessBarMessage("About to load current quest."); // Preload quest data if present final File dirQuests = new File(NewConstants.QUEST_SAVE_DIR); final String questname = Singletons.getModel().getQuestPreferences().getPref(QPref.CURRENT_QUEST); @@ -164,6 +165,7 @@ public enum FControl { Singletons.getModel().getQuest().load(QuestDataIO.loadData(data)); } + FSkin.setProgessBarMessage("Will load AI profiles now."); // Preload AI profiles AiProfileUtil.loadAllProfiles(); @@ -178,6 +180,7 @@ public enum FControl { FView.SINGLETON_INSTANCE.getLpnDocument().addMouseListener(SOverflowUtil.getHideOverflowListener()); FView.SINGLETON_INSTANCE.getLpnDocument().addComponentListener(SResizingUtil.getWindowResizeListener()); + FSkin.setProgessBarMessage("Opening main window..."); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Singletons.getView().initialize(); } }); } diff --git a/src/main/java/forge/control/input/Input.java b/src/main/java/forge/control/input/Input.java index 0491a22212e..4d479e66eb8 100644 --- a/src/main/java/forge/control/input/Input.java +++ b/src/main/java/forge/control/input/Input.java @@ -1,128 +1,23 @@ -/* - * 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 forge.Card; -import forge.Singletons; -import forge.game.player.Player; -import forge.gui.match.CMatchUI; - -/** - *

- * Abstract Input class. - *

- * - * @author Forge - * @version $Id$ - */ -public abstract class Input implements java.io.Serializable { - /** Constant serialVersionUID=-6539552513871194081L. */ - private static final long serialVersionUID = -6539552513871194081L; - - // showMessage() is always the first method called - /** - *

- * showMessage. - *

- */ - public void showMessage() { - CMatchUI.SINGLETON_INSTANCE.showMessage("Blank Input"); - } - - /** - *

- * selectCard. - *

- * - * @param c - * a {@link forge.Card} object. - */ - public void selectCard(final Card c) { - } - - /** - *

- * selectPlayer. - *

- * - * @param player - * a {@link forge.game.player.Player} object. - */ - public void selectPlayer(final Player player) { - } - - /** - *

- * selectButtonOK. - *

- */ - public void selectButtonOK() { - } - - /** - *

- * selectButtonCancel. - *

- */ - public void selectButtonCancel() { - } - - // helper methods, since they are used alot - // to be used by anything in CardFactory like SetTargetInput - // NOT TO BE USED by Input_Main or any of the "regular" Inputs objects that - // are not set using AllZone.getInputControl().setInput(Input) - /** - *

- * stop. - *

- */ - public final void stop() { - // clears a "temp" Input like Input_PayManaCost if there is one - Singletons.getModel().getMatch().getInput().resetInput(); - - } - - // exits the "current" Input and sets the next Input - /** - *

- * stopSetNext. - *

- * - * @param in - * a {@link forge.control.input.Input} object. - */ - public final void stopSetNext(final Input in) { - this.stop(); - Singletons.getModel().getMatch().getInput().setInput(in); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "blank"; - } // returns the Input name like "EmptyStack" - - - /** - * This method is used to mark old descendants of Input - * TODO: Write javadoc for this method. - */ - public /*abstract */void isClassUpdated() { - } //; - -} +package forge.control.input; + +import forge.Card; +import forge.game.player.Player; + +/** + * TODO: Write javadoc for this type. + * + */ +public interface Input { + + // showMessage() is always the first method called + void showMessage(); + + void selectCard(Card c); + + void selectPlayer(Player player); + + void selectButtonOK(); + + void selectButtonCancel(); + +} \ No newline at end of file diff --git a/src/main/java/forge/control/input/InputAttack.java b/src/main/java/forge/control/input/InputAttack.java index 74f47b2acd6..5a6d05d1631 100644 --- a/src/main/java/forge/control/input/InputAttack.java +++ b/src/main/java/forge/control/input/InputAttack.java @@ -24,9 +24,9 @@ import com.google.common.collect.Iterables; import forge.Card; import forge.CardPredicates; import forge.Singletons; +import forge.game.GameState; import forge.game.phase.CombatUtil; import forge.game.player.Player; -import forge.game.zone.PlayerZone; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.gui.framework.SDisplayUtil; @@ -42,10 +42,16 @@ import forge.view.ButtonUtil; * @author Forge * @version $Id$ */ -public class InputAttack extends Input { +public class InputAttack extends InputBase { /** Constant serialVersionUID=7849903731842214245L. */ private static final long serialVersionUID = 7849903731842214245L; + private final GameState game; + public InputAttack(GameState game0) { + game = game0; + } + + /** {@inheritDoc} */ @Override public final void showMessage() { @@ -53,7 +59,7 @@ public class InputAttack extends Input { ButtonUtil.enableOnlyOk(); - final Object o = Singletons.getModel().getGame().getCombat().nextDefender(); + final Object o = game.getCombat().nextDefender(); if (o == null) { return; } @@ -64,13 +70,13 @@ public class InputAttack extends Input { CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); - if (Singletons.getModel().getGame().getCombat().getRemainingDefenders() == 0) { + if (game.getCombat().getRemainingDefenders() == 0) { // Nothing left to attack, has to attack this defender List possibleAttackers = Singletons.getControl().getPlayer().getCardsIn(ZoneType.Battlefield); for (Card c : Iterables.filter(possibleAttackers, CardPredicates.Presets.CREATURES)) { - if (c.hasKeyword("CARDNAME attacks each turn if able.") && CombatUtil.canAttack(c, Singletons.getModel().getGame().getCombat()) + if (c.hasKeyword("CARDNAME attacks each turn if able.") && CombatUtil.canAttack(c, game.getCombat()) && !c.isAttacking()) { - Singletons.getModel().getGame().getCombat().addAttacker(c); + game.getCombat().addAttacker(c); } } } @@ -79,36 +85,36 @@ public class InputAttack extends Input { /** {@inheritDoc} */ @Override public final void selectButtonOK() { - if (!Singletons.getModel().getGame().getCombat().getAttackers().isEmpty()) { - Singletons.getModel().getGame().getPhaseHandler().setCombat(true); + if (!game.getCombat().getAttackers().isEmpty()) { + game.getPhaseHandler().setCombat(true); } - if (Singletons.getModel().getGame().getCombat().getRemainingDefenders() != 0) { - Singletons.getModel().getGame().getPhaseHandler().repeatPhase(); + if (game.getCombat().getRemainingDefenders() != 0) { + game.getPhaseHandler().repeatPhase(); } - Singletons.getModel().getGame().getPhaseHandler().setPlayersPriorityPermission(false); - Singletons.getModel().getMatch().getInput().resetInput(); + game.getPhaseHandler().setPlayersPriorityPermission(false); + Singletons.getModel().getMatch().getInput().updateObservers(); } /** {@inheritDoc} */ @Override public final void selectCard(final Card card) { - if (card.isAttacking() || card.getController().isComputer()) { + if (card.isAttacking() || card.getController() != Singletons.getControl().getPlayer()) { return; } final Player human = Singletons.getControl().getPlayer(); - Zone zone = Singletons.getModel().getGame().getZoneOf(card); + Zone zone = game.getZoneOf(card); if (zone.is(ZoneType.Battlefield, human) - && CombatUtil.canAttack(card, Singletons.getModel().getGame().getCombat())) { + && CombatUtil.canAttack(card, game.getCombat())) { // TODO add the propaganda code here and remove it in // Phase.nextPhase() // if (!CombatUtil.checkPropagandaEffects(card)) // return; - Singletons.getModel().getGame().getCombat().addAttacker(card); + game.getCombat().addAttacker(card); // just to make sure the attack symbol is marked human.getZone(ZoneType.Battlefield).updateObservers(); @@ -118,25 +124,4 @@ public class InputAttack extends Input { SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); } } // selectCard() - - /** - *

- * 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) { - - } - - /* (non-Javadoc) - * @see forge.control.input.Input#isClassUpdated() - */ - @Override - public void isClassUpdated() { - } } diff --git a/src/main/java/forge/control/input/InputBase.java b/src/main/java/forge/control/input/InputBase.java new file mode 100644 index 00000000000..f835e028ab3 --- /dev/null +++ b/src/main/java/forge/control/input/InputBase.java @@ -0,0 +1,67 @@ +/* + * 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 forge.Card; +import forge.Singletons; +import forge.game.player.Player; +import forge.gui.match.CMatchUI; + +/** + *

+ * Abstract Input class. + *

+ * + * @author Forge + * @version $Id$ + */ +public abstract class InputBase implements java.io.Serializable, Input { + /** Constant serialVersionUID=-6539552513871194081L. */ + private static final long serialVersionUID = -6539552513871194081L; + + // showMessage() is always the first method called + @Override + public abstract void showMessage(); + + @Override + public void selectCard(final Card c) { } + @Override + public void selectPlayer(final Player player) { } + @Override + public void selectButtonOK() { } + @Override + public void selectButtonCancel() { } + + // to remove need for CMatchUI dependence + protected void showMessage(String message) { + CMatchUI.SINGLETON_INSTANCE.showMessage(message); + } + + // Removes this input from the stack and releases any latches (in synchronous imports) + protected final void stop() { + // clears a "temp" Input like Input_PayManaCost if there is one + Singletons.getModel().getMatch().getInput().removeInput(this); + afterStop(); // sync inputs will release their latch there + } + + protected final boolean isActive() { + return Singletons.getModel().getMatch().getInput().getInput() == this; + } + + protected void afterStop() { } +} \ No newline at end of file diff --git a/src/main/java/forge/control/input/InputBlock.java b/src/main/java/forge/control/input/InputBlock.java index b370740db37..e2f172403bf 100644 --- a/src/main/java/forge/control/input/InputBlock.java +++ b/src/main/java/forge/control/input/InputBlock.java @@ -42,7 +42,7 @@ import forge.view.ButtonUtil; * @author Forge * @version $Id$ */ -public class InputBlock extends Input { +public class InputBlock extends InputBase { /** Constant serialVersionUID=6120743598368928128L. */ private static final long serialVersionUID = 6120743598368928128L; @@ -144,11 +144,4 @@ public class InputBlock extends Input { this.showMessage(); } // selectCard() - - /* (non-Javadoc) - * @see forge.control.input.Input#isClassUpdated() - */ - @Override - public void isClassUpdated() { - } } diff --git a/src/main/java/forge/control/input/InputCleanup.java b/src/main/java/forge/control/input/InputCleanup.java index a478cca5575..a8d30e9e8d2 100644 --- a/src/main/java/forge/control/input/InputCleanup.java +++ b/src/main/java/forge/control/input/InputCleanup.java @@ -34,7 +34,7 @@ import forge.view.ButtonUtil; * @author Forge * @version $Id$ */ -public class InputCleanup extends Input { +public class InputCleanup extends InputBase { /** Constant serialVersionUID=-4164275418971547948L. */ private static final long serialVersionUID = -4164275418971547948L; private final GameState game; @@ -82,18 +82,4 @@ public class InputCleanup extends Input { } } } // selectCard() - - /** - *

- * AI_CleanupDiscard. - *

- */ - - - /* (non-Javadoc) - * @see forge.control.input.Input#isClassUpdated() - */ - @Override - public void isClassUpdated() { - } } diff --git a/src/main/java/forge/control/input/InputControl.java b/src/main/java/forge/control/input/InputControl.java index 2d45e142391..a2be674d7e9 100644 --- a/src/main/java/forge/control/input/InputControl.java +++ b/src/main/java/forge/control/input/InputControl.java @@ -25,7 +25,6 @@ import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.player.PlayerController; import forge.game.zone.MagicStack; -import forge.gui.match.controllers.CMessage; import forge.util.MyObservable; /** @@ -40,21 +39,7 @@ public class InputControl extends MyObservable implements java.io.Serializable { /** Constant serialVersionUID=3955194449319994301L. */ private static final long serialVersionUID = 3955194449319994301L; - private Input input; - private final Stack inputStack = new Stack(); - private final Stack urgentInputStack = new Stack(); - - private final transient GameState game; - /** - * TODO Write javadoc for Constructor. - * - * @param fModel - * the f model - */ - public InputControl(final GameState game0) { - this.game = game0; - } /** *

@@ -62,46 +47,12 @@ public class InputControl extends MyObservable implements java.io.Serializable { *

* * @param in - * a {@link forge.control.input.Input} object. + * a {@link forge.control.input.InputBase} object. */ public final void setInput(final Input in) { - boolean isInputEmpty = this.input == null || this.input instanceof InputPassPriority; //System.out.println(in.getClass().getName()); - if (!this.game.getStack().isResolving() && isInputEmpty) { - this.input = in; - } else { - this.inputStack.add(in); - } - this.updateObservers(); - } - - /** - *

- * Setter for the field input. - *

- * - * @param in - * a {@link forge.control.input.Input} object. - * @param bAddToResolving - * a boolean. - */ - public final void setInputInterrupt(final Input in) { - // Make this - final Input old = this.input; - this.urgentInputStack.add(old); - this.changeInput(in); - } - - /** - *

- * changeInput. - *

- * - * @param in - * a {@link forge.control.input.Input} object. - */ - private void changeInput(final Input in) { - this.input = in; + this.inputStack.push(in); + // System.out.print("Current: " + input + "; Stack = " + inputStack); this.updateObservers(); } @@ -110,10 +61,10 @@ public class InputControl extends MyObservable implements java.io.Serializable { * Getter for the field input. *

* - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. */ public final Input getInput() { - return this.input; + return inputStack.isEmpty() ? null : this.inputStack.peek(); } /** @@ -122,8 +73,8 @@ public class InputControl extends MyObservable implements java.io.Serializable { *

*/ public final void clearInput() { - this.input = null; this.inputStack.clear(); + this.updateObservers(); } @@ -135,19 +86,34 @@ public class InputControl extends MyObservable implements java.io.Serializable { * @param update * a boolean. */ - public final void resetInput() { - this.input = null; + public final void removeInput(Input inp) { + Input topMostInput = inputStack.isEmpty() ? null : inputStack.pop(); + +// StackTraceElement[] trace = Thread.currentThread().getStackTrace(); +// System.out.printf("%s > Remove input %s -- called from %s%n", FThreads.isEDT() ? "EDT" : "TRD", topMostInput, trace[2].toString()); +// if( trace[2].toString().contains("InputBase.stop")) +// for(StackTraceElement se : trace) { +// System.out.println(se.toString()); +// } + + if( topMostInput != inp ) + throw new RuntimeException("Inputs adding/removal into stack is imbalanced! Check your code again!"); + this.updateObservers(); } + public final boolean isEmpty() { + return inputStack.isEmpty(); + } + /** *

* updateInput. *

* - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. */ - public final Input getActualInput() { + public final Input getActualInput(GameState game) { if ( !game.hasMulliganned() ) return new InputMulligan(); @@ -157,36 +123,11 @@ public class InputControl extends MyObservable implements java.io.Serializable { final Player priority = handler.getPriorityPlayer(); final MagicStack stack = game.getStack(); + // TODO this resolving portion needs more work, but fixes Death Cloud // issues - if (this.urgentInputStack.size() > 0) { - if (this.input != null) { - return this.input; - } - - // if an SA is resolving, only change input for something that is - // part of the resolving SA - this.changeInput(this.urgentInputStack.pop()); - return this.input; - } - - if (stack.isResolving()) { - return null; - } - - if (this.input != null) { - return this.input; - } - if (!this.inputStack.isEmpty()) { // incoming input to Control - this.changeInput(this.inputStack.pop()); - return this.input; - } - - if (handler.hasPhaseEffects()) { - // Handle begin phase stuff, then start back from the top - handler.handleBeginPhase(); - return this.getActualInput(); + return this.inputStack.peek(); } // If the Phase we're in doesn't allow for Priority, return null to move @@ -205,7 +146,7 @@ public class InputControl extends MyObservable implements java.io.Serializable { stack.freezeStack(); if (playerTurn.isHuman() && !playerTurn.getController().mayAutoPass(phase)) { game.getCombat().initiatePossibleDefenders(playerTurn.getOpponents()); - return new InputAttack(); + return new InputAttack(game); } break; @@ -236,7 +177,7 @@ public class InputControl extends MyObservable implements java.io.Serializable { // priority boolean prioritySkip = pc.mayAutoPass(phase) || pc.isUiSetToSkipPhase(playerTurn, phase); - if (this.game.getStack().isEmpty() && prioritySkip) { + if (game.getStack().isEmpty() && prioritySkip) { pc.passPriority(); return null; } else @@ -245,19 +186,23 @@ public class InputControl extends MyObservable implements java.io.Serializable { return pc.getDefaultInput(); } // getInput() - public final void setNewInput(GameState game) { - PhaseHandler ph = game.getPhaseHandler(); + /** + * TODO: Write javadoc for this method. + */ + private final static InputLockUI inputLock = new InputLockUI(); + public void lock() { + setInput(inputLock); + } + + public void unlock() { + if ( inputStack.isEmpty() || inputStack.peek() != inputLock ) + throw new RuntimeException("Trying to unlock input which is not locked! Do check when your threads terminate!"); + removeInput(inputLock); + } - final Input tmp = getActualInput(); - //String message = String.format("%s's %s, priority of %s [%sP] input is %s", ph.getPlayerTurn(), ph.getPhase(), ph.getPriorityPlayer(), ph.isPlayerPriorityAllowed() ? "+" : "-", tmp == null ? "null" : tmp.getClass().getSimpleName()); - //System.out.println(message); - if (tmp != null) { - //System.out.println(ph.getPlayerTurn() + "'s " + ph.getPhase() + ", priority of " + ph.getPriorityPlayer() + " @ input is " + tmp.getClass().getName() ); - CMessage.SINGLETON_INSTANCE.getInputControl().setInput(tmp); - } else if (!ph.isPlayerPriorityAllowed()) { - // System.out.println("cannot have priority, forced to pass"); - ph.getPriorityPlayer().getController().passPriority(); - } + // only for debug purposes + public String printInputStack() { + return inputStack.toString(); } } // InputControl diff --git a/src/main/java/forge/control/input/InputLockUI.java b/src/main/java/forge/control/input/InputLockUI.java index cd76856a653..2fb4c2eba7e 100644 --- a/src/main/java/forge/control/input/InputLockUI.java +++ b/src/main/java/forge/control/input/InputLockUI.java @@ -1,18 +1,51 @@ package forge.control.input; -import forge.gui.match.CMatchUI; +import java.util.concurrent.atomic.AtomicInteger; + +import forge.FThreads; import forge.view.ButtonUtil; /** * TODO: Write javadoc for this type. * */ -public class InputLockUI extends Input { +public class InputLockUI extends InputBase { private static final long serialVersionUID = 5777143577098597374L; + private final AtomicInteger iCall = new AtomicInteger(); + public void showMessage() { - ButtonUtil.disableAll(); - CMatchUI.SINGLETON_INSTANCE.showMessage("Waiting for actions..."); + int ixCall = 1 + iCall.getAndIncrement(); + FThreads.delay(500, new InputUpdater(ixCall)); } + + @Override + public String toString() { + return "lockUI"; + } + + private class InputUpdater implements Runnable { + final int ixCall; + + public InputUpdater(final int idxCall) { + ixCall = idxCall; + } + + @Override + public void run() { + if ( ixCall != iCall.get() || !isActive()) // cancel the message if it's not from latest call or input is gone already + return; + FThreads.invokeInEDT(showMessageFromEdt); + } + }; + + private final Runnable showMessageFromEdt = new Runnable() { + + @Override + public void run() { + ButtonUtil.disableAll(); + showMessage("Waiting for actions..."); + } + }; } diff --git a/src/main/java/forge/control/input/InputMulligan.java b/src/main/java/forge/control/input/InputMulligan.java index 1874503549f..ed7b64ffab9 100644 --- a/src/main/java/forge/control/input/InputMulligan.java +++ b/src/main/java/forge/control/input/InputMulligan.java @@ -49,7 +49,7 @@ import forge.view.ButtonUtil; * @author Forge * @version $Id$ */ -public class InputMulligan extends Input { +public class InputMulligan extends InputBase { /** Constant serialVersionUID=-8112954303001155622L. */ private static final long serialVersionUID = -8112954303001155622L; @@ -174,7 +174,6 @@ public class InputMulligan extends Input { game.setMulliganned(true); Singletons.getModel().getMatch().getInput().clearInput(); - Singletons.getModel().getMatch().getInput().resetInput(); } @Override @@ -192,8 +191,4 @@ public class InputMulligan extends Input { SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); } } - - @Override - public void isClassUpdated() { - } } diff --git a/src/main/java/forge/control/input/InputPassPriority.java b/src/main/java/forge/control/input/InputPassPriority.java index 1647022f8cd..4fe8b4bcf64 100644 --- a/src/main/java/forge/control/input/InputPassPriority.java +++ b/src/main/java/forge/control/input/InputPassPriority.java @@ -18,10 +18,10 @@ package forge.control.input; import forge.Card; +import forge.FThreads; import forge.Singletons; import forge.card.spellability.SpellAbility; import forge.control.FControl; -import forge.game.GameState; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.gui.GuiDisplayUtil; @@ -38,7 +38,7 @@ import forge.view.ButtonUtil; * @author Forge * @version $Id$ */ -public class InputPassPriority extends Input { +public class InputPassPriority extends InputBase { /** Constant serialVersionUID=-581477682214137181L. */ private static final long serialVersionUID = -581477682214137181L; @@ -80,17 +80,20 @@ public class InputPassPriority extends Input { /** {@inheritDoc} */ @Override public final void selectCard(final Card card) { - Player player = Singletons.getControl().getPlayer(); - GameState game = Singletons.getModel().getGame(); - SpellAbility ab = player.getController().getAbilityToPlay(game.getAbilitesOfCard(card, player)); + final Player player = Singletons.getControl().getPlayer(); + final SpellAbility ab = player.getController().getAbilityToPlay(player.getGame().getAbilitesOfCard(card, player)); if ( null != ab) { - player.playSpellAbility(card, ab); + Runnable execAbility = new Runnable() { + @Override + public void run() { + player.playSpellAbility(card, ab); + } + }; + + FThreads.invokeInNewThread(execAbility, true); } else { SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); } } // selectCard() - - @Override public void isClassUpdated() { - } } diff --git a/src/main/java/forge/control/input/InputPayDiscardCost.java b/src/main/java/forge/control/input/InputPayDiscardCost.java deleted file mode 100644 index c10d0e80f25..00000000000 --- a/src/main/java/forge/control/input/InputPayDiscardCost.java +++ /dev/null @@ -1,190 +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.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; - -//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 InputPayDiscardCost extends Input { - /** - * Constant serialVersionUID=2685832214529141991L. - */ - private static final long serialVersionUID = 5634078561074764401L; - - private int numChosen = 0; - private int numRequired = 0; - private List choiceList; - private CostDiscard discardCost; - 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 InputPayDiscardCost(final CostDiscard cost, final SpellAbility sa, final Command paidCommand, - final Command unpaidCommand) { - final Card source = sa.getSourceCard(); - final Player human = Singletons.getControl().getPlayer(); - - this.ability = sa; - this.discardCost = cost; - this.choiceList = CardLists.getValidCards(human.getCardsIn(ZoneType.Hand), 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("Discard "); - final int nLeft = this.numRequired - this.numChosen; - msg.append(nLeft).append(" "); - msg.append(this.discardCost.getDescriptiveType()); - if (nLeft > 1) { - msg.append("s"); - } - if (!this.discardCost.getList().isEmpty()) { - msg.append("\r\nSelected:\r\n"); - for (Card selected : this.discardCost.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.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(); - } - } - - /** - *

- * executes paid commmand. - *

- * - */ - public void done() { - this.stop(); - for (Card selected : this.discardCost.getList()) { - selected.setUsedToPay(false); - Singletons.getControl().getPlayer().discard(selected, this.ability); - } - this.discardCost.addListToHash(ability, "Discarded"); - this.paid.execute(); - } - - /** - *

- * executes unpaid commmand. - *

- * - */ - public void cancel() { - this.stop(); - for (Card selected : this.discardCost.getList()) { - selected.setUsedToPay(false); - } - this.unpaid.execute(); - } - - @Override public void isClassUpdated() { - } -} diff --git a/src/main/java/forge/control/input/InputPayManaBase.java b/src/main/java/forge/control/input/InputPayManaBase.java index 0a350da4bc4..29d43729c83 100644 --- a/src/main/java/forge/control/input/InputPayManaBase.java +++ b/src/main/java/forge/control/input/InputPayManaBase.java @@ -1,14 +1,12 @@ package forge.control.input; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; - import forge.Card; import forge.CardUtil; import forge.Constant; +import forge.FThreads; import forge.Singletons; import forge.card.MagicColor; import forge.card.ability.ApiType; @@ -18,13 +16,16 @@ import forge.card.spellability.AbilityManaPart; import forge.card.spellability.SpellAbility; import forge.game.GameState; import forge.game.player.Player; +import forge.game.zone.ZoneType; import forge.gui.GuiChoose; +import forge.gui.framework.SDisplayUtil; +import forge.gui.match.views.VMessage; /** * TODO: Write javadoc for this type. * */ -public abstract class InputPayManaBase extends Input { +public abstract class InputPayManaBase extends InputSyncronizedBase implements InputPayment { private static final long serialVersionUID = -9133423708688480255L; @@ -33,19 +34,30 @@ public abstract class InputPayManaBase extends Input { protected final Player whoPays; protected final GameState game; protected ManaCostBeingPaid manaCost; + protected final SpellAbility saPaidFor; - protected InputPayManaBase(final GameState game) { + boolean bPaid = false; + + protected InputPayManaBase(final GameState game, SpellAbility saToPayFor) { this.game = game; - this.whoPays = Singletons.getControl().getPlayer(); + this.whoPays = saToPayFor.getActivatingPlayer(); + this.saPaidFor = saToPayFor; } - /** - *

- * selectManaPool. - *

- * @param color a String that represents the Color the mana is coming from - */ - public abstract void selectManaPool(String color); + /** {@inheritDoc} */ + @Override + public void selectCard(final Card card) { + if (card.getManaAbility().isEmpty() || card.isInZone(ZoneType.Hand)) { + SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); + return; + } + // only tap card if the mana is needed + activateManaAbility(card, this.manaCost); + } + + public void selectManaPool(String color) { + useManaFromPool(color, this.manaCost); + } /** *

@@ -103,7 +115,7 @@ public abstract class InputPayManaBase extends Input { * * @return ManaCost the amount of mana remaining to be paid after the mana is activated */ - protected static ManaCostBeingPaid activateManaAbility(String color, final SpellAbility saBeingPaidFor, ManaCostBeingPaid manaCost) { + protected void useManaFromPool(String color, ManaCostBeingPaid manaCost) { ManaPool mp = Singletons.getControl().getPlayer().getManaPool(); // Convert Color to short String @@ -111,8 +123,11 @@ public abstract class InputPayManaBase extends Input { if (!color.equalsIgnoreCase("Colorless")) { manaStr = CardUtil.getShortColor(color); } + + this.manaCost = mp.payManaFromPool(saPaidFor, manaCost, manaStr); - return mp.payManaFromPool(saBeingPaidFor, manaCost, manaStr); + onManaAbilityPlayed(null); + showMessage(); } /** @@ -128,10 +143,10 @@ public abstract class InputPayManaBase extends Input { * a {@link forge.card.mana.ManaCostBeingPaid} object. * @return a {@link forge.card.mana.ManaCostBeingPaid} object. */ - protected ManaCostBeingPaid activateManaAbility(final SpellAbility sa, final Card card, ManaCostBeingPaid manaCost) { + protected void activateManaAbility(final Card card, ManaCostBeingPaid manaCost) { // make sure computer's lands aren't selected if (card.getController() != whoPays) { - return manaCost; + return; } @@ -170,7 +185,7 @@ public abstract class InputPayManaBase extends Input { continue; } else if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) { continue; - } else if (!m.meetsManaRestrictions(sa)) { + } else if (!m.meetsManaRestrictions(saPaidFor)) { continue; } @@ -185,16 +200,16 @@ public abstract class InputPayManaBase extends Input { } } if (abilities.isEmpty()) { - return manaCost; + return; } // Store some information about color costs to help with any mana choices String colorsNeeded = colorRequired.toString(); if ("1".equals(colorsNeeded)) { // only colorless left - if (sa.getSourceCard() != null - && !sa.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").equals("")) { + if (saPaidFor.getSourceCard() != null + && !saPaidFor.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").equals("")) { colorsNeeded = ""; - String[] negEffects = sa.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").split(","); + String[] negEffects = saPaidFor.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").split(","); for (String negColor : negEffects) { // convert long color strings to short color strings if (negColor.length() > 1) { @@ -216,8 +231,8 @@ public abstract class InputPayManaBase extends Input { // If the card has sunburst or any other ability that tracks mana spent, // skip express Mana choice - if (sa.getSourceCard() != null - && sa.getSourceCard().hasKeyword("Sunburst") && sa.isSpell()) { + if (saPaidFor.getSourceCard() != null + && saPaidFor.getSourceCard().hasKeyword("Sunburst") && saPaidFor.isSpell()) { colorsNeeded = "WUBRG"; skipExpress = true; } @@ -255,7 +270,7 @@ public abstract class InputPayManaBase extends Input { } } - if ((colorMatches.size() == 0)) { + if (colorMatches.isEmpty()) { // can only match colorless just grab the first and move on. choice = false; } else if (colorMatches.size() < abilities.size()) { @@ -264,15 +279,7 @@ public abstract class InputPayManaBase extends Input { } } - SpellAbility chosen = abilities.get(0); - if ((1 < abilities.size()) && choice) { - final Map ability = new HashMap(); - for (final SpellAbility am : abilities) { - ability.put(am.toString(), am); - } - chosen = GuiChoose.one("Choose mana ability", abilities); - } - + final SpellAbility chosen = abilities.size() > 1 && choice ? GuiChoose.one("Choose mana ability", abilities) : abilities.get(0); SpellAbility subchosen = chosen; while(subchosen.getManaPart() == null) { @@ -282,14 +289,53 @@ public abstract class InputPayManaBase extends Input { // save off color needed for use by any mana and reflected mana subchosen.getManaPart().setExpressChoice(colorsNeeded); - Player p = chosen.getActivatingPlayer(); - Singletons.getModel().getGame().getActionPlay().playSpellAbility(chosen, p); - - manaCost = p.getManaPool().payManaFromAbility(sa, manaCost, chosen); - - //AllZone.getHumanPlayer().getZone(ZoneType.Battlefield).updateObservers(); - // DO NOT REMOVE THIS, otherwise the cards don't always tap (copied) - return manaCost; - + // System.out.println("Chosen sa=" + chosen + " of " + chosen.getSourceCard() + " to pay mana"); + Runnable proc = new Runnable() { + @Override + public void run() { + final Player p = chosen.getActivatingPlayer(); + p.getGame().getActionPlay().playSpellAbility(chosen, p); + onManaAbilityPlayed(chosen); + } + }; + FThreads.invokeInNewThread(proc, true); + // EDT that removes lockUI from input stack will call our showMessage() method } + + public void onManaAbilityPlayed(final SpellAbility saPaymentSrc) { + if ( saPaymentSrc != null) // null comes when they've paid from pool + this.manaCost = whoPays.getManaPool().payManaFromAbility(saPaidFor, manaCost, saPaymentSrc); + + onManaAbilityPaid(); + if ( saPaymentSrc != null ) + whoPays.getZone(ZoneType.Battlefield).updateObservers(); + } + + protected final void checkIfAlredyPaid() { + if (manaCost.isPaid()) { + bPaid = true; + done(); + } + } + + protected void onManaAbilityPaid() {} // some inputs overload it + protected abstract void done(); + + @Override + public String toString() { + return String.format("PayManaBase %s (out of %s)", manaCost.toString(), manaCost.getStartingCost() ); + } + + protected void handleConvokedCards(boolean isCancelled) { + if (saPaidFor.getTappedForConvoke() != null) { + for (final Card c : saPaidFor.getTappedForConvoke()) { + c.setTapped(false); + if (!isCancelled) + c.tap(); // that will tap cards with all the triggers, it's no good to call this from EDT + } + saPaidFor.clearTappedForConvoke(); + } + } + + public boolean isPaid() { return bPaid; } } diff --git a/src/main/java/forge/control/input/InputPayManaExecuteCommands.java b/src/main/java/forge/control/input/InputPayManaExecuteCommands.java index 3b07ae81cd0..ed09164bf3d 100644 --- a/src/main/java/forge/control/input/InputPayManaExecuteCommands.java +++ b/src/main/java/forge/control/input/InputPayManaExecuteCommands.java @@ -17,17 +17,12 @@ */ package forge.control.input; -import forge.Card; -import forge.Command; import forge.Singletons; +import forge.card.mana.ManaCost; import forge.card.mana.ManaCostBeingPaid; import forge.card.spellability.SpellAbility; import forge.game.GameState; import forge.game.player.Player; -import forge.game.zone.ZoneType; -import forge.gui.framework.SDisplayUtil; -import forge.gui.match.CMatchUI; -import forge.gui.match.views.VMessage; import forge.view.ButtonUtil; //if cost is paid, Command.execute() is called @@ -40,19 +35,17 @@ import forge.view.ButtonUtil; * @author Forge * @version $Id$ */ -public class InputPayManaExecuteCommands extends InputPayManaBase { +public class InputPayManaExecuteCommands extends InputPayManaBase implements InputPayment { /** * Constant serialVersionUID=3836655722696348713L. */ private static final long serialVersionUID = 3836655722696348713L; - private String originalManaCost; + private ManaCost originalManaCost; private String message = ""; - private SpellAbility fakeAbility; - - private Command paidCommand; - private Command unpaidCommand; + private boolean bPaid = false; + public boolean isPaid() { return bPaid; } // only used for X costs: private boolean showOnlyOKButton = false; @@ -72,8 +65,8 @@ public class InputPayManaExecuteCommands extends InputPayManaBase { * @param unpaidCommand2 * a {@link forge.Command} object. */ - public InputPayManaExecuteCommands(final GameState game, final String prompt, final String manaCost2, final Command paidCommand2, final Command unpaidCommand2) { - this(game, prompt, manaCost2, paidCommand2, unpaidCommand2, false); + public InputPayManaExecuteCommands(final GameState game, final String prompt, final ManaCost manaCost2) { + this(game, prompt, manaCost2, false); } /** @@ -92,25 +85,19 @@ public class InputPayManaExecuteCommands extends InputPayManaBase { * @param showOKButton * a boolean. */ - public InputPayManaExecuteCommands(final GameState game, final String prompt, final String manaCost2, final Command paid, final Command unpaid, final boolean showOKButton) { - super(game); - this.fakeAbility = new SpellAbility(null) { + public InputPayManaExecuteCommands(final GameState game, final String prompt, final ManaCost manaCost2, final boolean showOKButton) { + super(game, new SpellAbility(null) { @Override - public void resolve() { - } + public void resolve() {} @Override - public boolean canPlay() { - return false; - } - }; + public boolean canPlay() { return false; } + }); this.originalManaCost = manaCost2; this.phyLifeToLose = 0; this.message = prompt; this.manaCost = new ManaCostBeingPaid(this.originalManaCost); - this.paidCommand = paid; - this.unpaidCommand = unpaid; this.showOnlyOKButton = showOKButton; } @@ -138,39 +125,24 @@ public class InputPayManaExecuteCommands extends InputPayManaBase { } } - /** {@inheritDoc} */ @Override - public final void selectCard(final Card card) { - // only tap card if the mana is needed - this.manaCost = activateManaAbility(this.fakeAbility, card, this.manaCost); - - if (card.getManaAbility().isEmpty() || card.isInZone(ZoneType.Hand)) { - SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); - } - - if (this.manaCost.isPaid()) { - this.done(); - } else { - this.showMessage(); - } - } - - private void done() { + protected void done() { if (this.phyLifeToLose > 0) { Singletons.getControl().getPlayer().payLife(this.phyLifeToLose, null); } - this.paidCommand.execute(); this.resetManaCost(); - Singletons.getControl().getPlayer().getManaPool().clearManaPaid(this.fakeAbility, false); + Singletons.getControl().getPlayer().getManaPool().clearManaPaid(this.saPaidFor, false); + bPaid = true; this.stop(); } /** {@inheritDoc} */ @Override public final void selectButtonCancel() { - this.unpaidCommand.execute(); + this.resetManaCost(); - Singletons.getControl().getPlayer().getManaPool().refundManaPaid(this.fakeAbility, true); + Singletons.getControl().getPlayer().getManaPool().refundManaPaid(this.saPaidFor, true); + bPaid = false; this.stop(); } @@ -178,8 +150,8 @@ public class InputPayManaExecuteCommands extends InputPayManaBase { @Override public final void selectButtonOK() { if (this.showOnlyOKButton) { + bPaid = false; this.stop(); - this.unpaidCommand.execute(); } } @@ -201,24 +173,7 @@ public class InputPayManaExecuteCommands extends InputPayManaBase { msg.append("\n(Click on your life total to pay life for phyrexian mana.)"); } - CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); + showMessage(msg.toString()); + checkIfAlredyPaid(); } - - /* (non-Javadoc) - * @see forge.control.input.InputMana#selectManaPool() - */ - @Override - public void selectManaPool(String color) { - this.manaCost = activateManaAbility(color, this.fakeAbility, this.manaCost); - - if (this.manaCost.isPaid()) { - this.done(); - } else { - this.showMessage(); - } - } - - @Override public void isClassUpdated() { - } - } diff --git a/src/main/java/forge/control/input/InputPayManaOfCostPayment.java b/src/main/java/forge/control/input/InputPayManaOfCostPayment.java index 56b90128fdb..b241ebf074a 100644 --- a/src/main/java/forge/control/input/InputPayManaOfCostPayment.java +++ b/src/main/java/forge/control/input/InputPayManaOfCostPayment.java @@ -3,62 +3,23 @@ package forge.control.input; import forge.Card; import forge.Singletons; import forge.card.cost.CostPartMana; -import forge.card.cost.CostPayment; import forge.card.mana.ManaCostBeingPaid; import forge.card.spellability.SpellAbility; import forge.game.GameState; import forge.game.player.Player; -import forge.game.zone.ZoneType; -import forge.gui.match.CMatchUI; import forge.view.ButtonUtil; public class InputPayManaOfCostPayment extends InputPayManaBase { - private final CostPartMana costMana; - - // I would kill the one who made 2 classes like above - private final String originalManaCost; - private final SpellAbility sa; - private final int manaToAdd; - private final CostPayment payment; - - public InputPayManaOfCostPayment(final GameState game, CostPartMana costMana, SpellAbility spellAbility, final CostPayment payment, int toAdd) { - super(game); + public InputPayManaOfCostPayment(final GameState game, CostPartMana costMana, SpellAbility spellAbility, int toAdd) { + super(game, spellAbility); manaCost = new ManaCostBeingPaid(costMana.getManaToPay()); manaCost.increaseColorlessMana(toAdd); - - this.costMana = costMana; - originalManaCost = costMana.getMana(); - sa = spellAbility; - manaToAdd = toAdd; - this.payment = payment; - } private static final long serialVersionUID = 3467312982164195091L; - - - private int phyLifeToLose = 0; - private void resetManaCost() { - this.manaCost = new ManaCostBeingPaid(this.originalManaCost); - this.phyLifeToLose = 0; - } - - @Override - public void selectCard(final Card card) { - // prevent cards from tapping themselves if ability is a - // tapability, although it should already be tapped - this.manaCost = activateManaAbility(sa, card, this.manaCost); - - if (this.manaCost.isPaid()) { - this.done(); - } else if (Singletons.getModel().getMatch().getInput().getInput() == this) { - this.showMessage(); - } - } - @Override public void selectPlayer(final Player player) { if (player == whoPays) { @@ -70,65 +31,34 @@ public class InputPayManaOfCostPayment extends InputPayManaBase { } } - private void done() { - final Card source = sa.getSourceCard(); + @Override + protected void done() { + final Card source = saPaidFor.getSourceCard(); if (this.phyLifeToLose > 0) { Singletons.getControl().getPlayer().payLife(this.phyLifeToLose, source); } source.setColorsPaid(this.manaCost.getColorsPaid()); source.setSunburstValue(this.manaCost.getSunburst()); - this.resetManaCost(); - this.stop(); - if (costMana.hasNoXManaCost() || (manaToAdd > 0)) { - payment.paidCost(costMana); - } else { - source.setXManaCostPaid(0); - final Input inp = new InputPayManaX(game, sa, payment, costMana); - Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); - } - - // If this is a spell with convoke, re-tap all creatures used - // for it. - // This is done to make sure Taps triggers go off at the right - // time - // (i.e. AFTER cost payment, they are tapped previously as well - // so that - // any mana tapabilities can't be used in payment as well as - // being tapped for convoke) - - if (sa.getTappedForConvoke() != null) { - for (final Card c : sa.getTappedForConvoke()) { - c.setTapped(false); - c.tap(); - } - sa.clearTappedForConvoke(); - } + // If this is a spell with convoke, re-tap all creatures used for it. + // This is done to make sure Taps triggers go off at the right time + // (i.e. AFTER cost payment, they are tapped previously as well so that + // any mana tapabilities can't be used in payment as well as being tapped for convoke) + handleConvokedCards(false); + stop(); } - + @Override public void selectButtonCancel() { - // If we're paying for a spell with convoke, untap all creatures - // used for it. - if (sa.getTappedForConvoke() != null) { - for (final Card c : sa.getTappedForConvoke()) { - c.setTapped(false); - } - sa.clearTappedForConvoke(); - } - - this.stop(); - this.resetManaCost(); - payment.cancelCost(); - Singletons.getControl().getPlayer().getZone(ZoneType.Battlefield).updateObservers(); + handleConvokedCards(true); + stop(); } @Override public void showMessage() { ButtonUtil.enableOnlyCancel(); final String displayMana = manaCost.toString().replace("X", "").trim(); - CMatchUI.SINGLETON_INSTANCE.showMessage("Pay Mana Cost: " + displayMana); final StringBuilder msg = new StringBuilder("Pay Mana Cost: " + displayMana); if (this.phyLifeToLose > 0) { @@ -141,23 +71,7 @@ public class InputPayManaOfCostPayment extends InputPayManaBase { msg.append("\n(Click on your life total to pay life for phyrexian mana.)"); } - CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); - if (manaCost.isPaid()) { - this.done(); - } - } - - @Override - public void selectManaPool(String color) { - manaCost = InputPayManaBase.activateManaAbility(color, sa, this.manaCost); - - if (this.manaCost.isPaid()) { - this.done(); - } else if (Singletons.getModel().getMatch().getInput().getInput() == this) { - this.showMessage(); - } - } - - @Override public void isClassUpdated() { + showMessage(msg.toString()); + checkIfAlredyPaid(); } } diff --git a/src/main/java/forge/control/input/InputPayManaSimple.java b/src/main/java/forge/control/input/InputPayManaSimple.java index c4cdbc52b98..712f0ebfe4d 100644 --- a/src/main/java/forge/control/input/InputPayManaSimple.java +++ b/src/main/java/forge/control/input/InputPayManaSimple.java @@ -18,13 +18,11 @@ package forge.control.input; import forge.Card; -import forge.Singletons; import forge.card.mana.ManaCostBeingPaid; import forge.card.spellability.SpellAbility; import forge.game.GameState; import forge.game.player.Player; import forge.game.zone.ZoneType; -import forge.gui.match.CMatchUI; import forge.view.ButtonUtil; //pays the cost of a card played from the player's hand @@ -35,61 +33,21 @@ public class InputPayManaSimple extends InputPayManaBase { /** Constant serialVersionUID=3467312982164195091L. */ private static final long serialVersionUID = 3467312982164195091L; - private boolean skipStack; - private final SpellAbility spell; private final Card originalCard; private final String originalManaCost; - /** - *

- * Constructor for Input_PayManaCost. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param noStack - * a boolean. - */ - public InputPayManaSimple(final GameState game, final SpellAbility sa, final boolean noStack) { - this(game, sa, game.getActionPlay().getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost()))); - this.skipStack = noStack; - } - - /** - *

- * Constructor for Input_PayManaCost. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - */ - public InputPayManaSimple(final GameState game, final SpellAbility sa) { - this(game, sa, new ManaCostBeingPaid(sa.getManaCost())); - } - - /** - *

- * Constructor for Input_PayManaCost. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * - * @param manaCostToPay - * a {@link forge.card.mana.ManaCostBeingPaid} object. - */ public InputPayManaSimple(final GameState game, final SpellAbility sa, final ManaCostBeingPaid manaCostToPay) { - super(game); + super(game, sa); this.originalManaCost = manaCostToPay.toString(); // Change this.originalCard = sa.getSourceCard(); - this.spell = sa; if (sa.getSourceCard().isCopiedSpell() && sa.isSpell()) { this.manaCost = new ManaCostBeingPaid("0"); - game.getStack().add(this.spell); + game.getStack().add(this.saPaidFor); } else { this.manaCost = manaCostToPay; } + } /** @@ -102,24 +60,9 @@ public class InputPayManaSimple extends InputPayManaBase { this.phyLifeToLose = 0; } - /** {@inheritDoc} */ - @Override - public final void selectCard(final Card card) { - // this is a hack, to prevent lands being able to use mana to pay their - // own abilities from cards like - // Kher Keep, Pendelhaven, Blinkmoth Nexus, and Mikokoro, Center of the - // Sea, .... - - this.manaCost = activateManaAbility(this.spell, card, this.manaCost); - - // only show message if this is the active input - if (Singletons.getModel().getMatch().getInput().getInput() == this) { - this.showMessage(); - } - + protected void onManaAbilityPaid() { if (this.manaCost.isPaid()) { this.originalCard.setSunburstValue(this.manaCost.getSunburst()); - this.done(); } } @@ -142,59 +85,32 @@ public class InputPayManaSimple extends InputPayManaBase { * done. *

*/ - private void done() { + @Override + protected void done() { if (this.phyLifeToLose > 0) { whoPays.payLife(this.phyLifeToLose, this.originalCard); } - if (this.spell.getSourceCard().isCopiedSpell()) { - Singletons.getModel().getMatch().getInput().resetInput(); - } else { - whoPays.getManaPool().clearManaPaid(this.spell, false); + if (!this.saPaidFor.getSourceCard().isCopiedSpell()) { + whoPays.getManaPool().clearManaPaid(this.saPaidFor, false); this.resetManaCost(); - if (this.spell.isSpell()) { - this.spell.setSourceCard(game.getAction().moveToStack(this.originalCard)); + if (this.saPaidFor.isSpell()) { + this.saPaidFor.setSourceCard(game.getAction().moveToStack(this.originalCard)); } - if (this.skipStack) { - this.spell.resolve(); - } else { - game.getStack().add(this.spell); - } - Singletons.getModel().getMatch().getInput().resetInput(); - - // If this is a spell with convoke, re-tap all creatures used for - // it. - // This is done to make sure Taps triggers go off at the right time - // (i.e. AFTER cost payment, they are tapped previously as well so - // that - // any mana tapabilities can't be used in payment as well as being - // tapped for convoke) - - if (this.spell.getTappedForConvoke() != null) { - for (final Card c : this.spell.getTappedForConvoke()) { - c.setTapped(false); - c.tap(); - } - this.spell.clearTappedForConvoke(); - } + handleConvokedCards(false); } + + stop(); } /** {@inheritDoc} */ @Override public final void selectButtonCancel() { - // If this is a spell with convoke, untap all creatures used for it. - if (this.spell.getTappedForConvoke() != null) { - for (final Card c : this.spell.getTappedForConvoke()) { - c.setTapped(false); - } - this.spell.clearTappedForConvoke(); - } - + handleConvokedCards(true); this.resetManaCost(); - whoPays.getManaPool().refundManaPaid(this.spell, true); + whoPays.getManaPool().refundManaPaid(this.saPaidFor, true); whoPays.getZone(ZoneType.Battlefield).updateObservers(); // DO this.stop(); @@ -216,32 +132,12 @@ public class InputPayManaSimple extends InputPayManaBase { msg.append("\n(Click on your life total to pay life for phyrexian mana.)"); } - CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); + // has its own variant of checkIfPaid + showMessage(msg.toString()); if (this.manaCost.isPaid() && !new ManaCostBeingPaid(this.originalManaCost).isPaid()) { this.originalCard.setSunburstValue(this.manaCost.getSunburst()); this.done(); } } - - /* (non-Javadoc) - * @see forge.control.input.InputMana#selectManaPool(String) - */ - @Override - public void selectManaPool(String color) { - this.manaCost = InputPayManaBase.activateManaAbility(color, this.spell, this.manaCost); - - // only show message if this is the active input - if (Singletons.getModel().getMatch().getInput().getInput() == this) { - this.showMessage(); - } - - if (this.manaCost.isPaid()) { - this.originalCard.setSunburstValue(this.manaCost.getSunburst()); - this.done(); - } - } - - @Override public void isClassUpdated() { - } } diff --git a/src/main/java/forge/control/input/InputPayManaX.java b/src/main/java/forge/control/input/InputPayManaX.java index 9edb2ec4090..50bd5efce55 100644 --- a/src/main/java/forge/control/input/InputPayManaX.java +++ b/src/main/java/forge/control/input/InputPayManaX.java @@ -1,14 +1,10 @@ package forge.control.input; import forge.Card; -import forge.Singletons; import forge.card.cost.CostPartMana; -import forge.card.cost.CostPayment; import forge.card.mana.ManaCostBeingPaid; import forge.card.spellability.SpellAbility; import forge.game.GameState; -import forge.game.zone.ZoneType; -import forge.gui.match.CMatchUI; import forge.view.ButtonUtil; public class InputPayManaX extends InputPayManaBase { @@ -18,49 +14,60 @@ public class InputPayManaX extends InputPayManaBase { private final String strX; private String colorsPaid; private final CostPartMana costMana; - private final CostPayment payment; - private final SpellAbility sa; - public InputPayManaX(final GameState game, final SpellAbility sa0, final CostPayment payment0, final CostPartMana costMana0) + public InputPayManaX(final GameState game, final SpellAbility sa0, final CostPartMana costMana0) { - super(game); - sa = sa0; - payment = payment0; + super(game, sa0); + xPaid = 0; - colorX = sa.hasParam("XColor") ? sa.getParam("XColor") : ""; - colorsPaid = sa.getSourceCard().getColorsPaid(); + colorX = saPaidFor.hasParam("XColor") ? saPaidFor.getParam("XColor") : ""; + colorsPaid = saPaidFor.getSourceCard().getColorsPaid(); costMana = costMana0; strX = Integer.toString(costMana.getXMana()); manaCost = new ManaCostBeingPaid(strX); } + /* (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 ( xPaid > 0 || costMana.canXbe0()) && (!this.colorX.equals("") || this.manaCost.toString().equals(strX)); + } + @Override public void showMessage() { - if ((xPaid == 0 && costMana.isxCantBe0()) || (this.colorX.equals("") - && !this.manaCost.toString().equals(strX))) { + // only cancel if partially paid an X value + // or X is 0, and x can't be 0 + if (!isPaid()) { ButtonUtil.enableOnlyCancel(); - // only cancel if partially paid an X value - // or X is 0, and x can't be 0 } else { ButtonUtil.enableAllFocusOk(); } StringBuilder msg = new StringBuilder("Pay X Mana Cost for "); - msg.append(sa.getSourceCard().getName()).append("\n").append(this.xPaid); + msg.append(saPaidFor.getSourceCard().getName()).append("\n").append(this.xPaid); msg.append(" Paid so far."); - if (costMana.isxCantBe0()) { + if (!costMana.canXbe0()) { msg.append(" X Can't be 0."); } - CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); + showMessage(msg.toString()); } // selectCard @Override public void selectCard(final Card card) { - this.manaCost = activateManaAbility(sa, card, - this.colorX.isEmpty() ? this.manaCost : new ManaCostBeingPaid(this.colorX)); + // don't allow here the cards that produce only wrong colors + activateManaAbility(card, this.colorX.isEmpty() ? this.manaCost : new ManaCostBeingPaid(this.colorX)); + } + + + @Override + protected void onManaAbilityPaid() { if (this.manaCost.isPaid()) { if (!this.colorsPaid.contains(this.manaCost.getColorsPaid())) { this.colorsPaid += this.manaCost.getColorsPaid(); @@ -68,49 +75,30 @@ public class InputPayManaX extends InputPayManaBase { this.manaCost = new ManaCostBeingPaid(strX); this.xPaid++; } - - if (Singletons.getModel().getMatch().getInput().getInput() == this) { - this.showMessage(); - } } @Override public void selectButtonCancel() { this.stop(); - payment.cancelCost(); - Singletons.getControl().getPlayer().getZone(ZoneType.Battlefield).updateObservers(); } @Override public void selectButtonOK() { - this.stop(); - payment.getCard().setXManaCostPaid(this.xPaid); - payment.paidCost(costMana); - payment.getCard().setColorsPaid(this.colorsPaid); - payment.getCard().setSunburstValue(this.colorsPaid.length()); + done(); } @Override public void selectManaPool(String color) { - this.manaCost = activateManaAbility(color, sa, - this.colorX.isEmpty() ? this.manaCost : new ManaCostBeingPaid(this.colorX)); - if (this.manaCost.isPaid()) { - if (!this.colorsPaid.contains(this.manaCost.getColorsPaid())) { - this.colorsPaid += this.manaCost.getColorsPaid(); - } - this.manaCost = new ManaCostBeingPaid(strX); - this.xPaid++; - } - - if (Singletons.getModel().getMatch().getInput().getInput() == this) { - this.showMessage(); - } + useManaFromPool(color, this.colorX.isEmpty() ? this.manaCost : new ManaCostBeingPaid(this.colorX)); } - /* (non-Javadoc) - * @see forge.control.input.Input#isClassUpdated() - */ + @Override - public void isClassUpdated() { + protected void done() { + final Card card = saPaidFor.getSourceCard(); + card.setXManaCostPaid(this.xPaid); + card.setColorsPaid(this.colorsPaid); + card.setSunburstValue(this.colorsPaid.length()); + this.stop(); } } diff --git a/src/main/java/forge/control/input/InputPayReturnCost.java b/src/main/java/forge/control/input/InputPayReturnCost.java deleted file mode 100644 index 7793f08336e..00000000000 --- a/src/main/java/forge/control/input/InputPayReturnCost.java +++ /dev/null @@ -1,191 +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.CostReturn; -import forge.card.spellability.SpellAbility; -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 InputPayReturnCost extends Input { - /** - * Constant serialVersionUID=2685832214529141991L. - */ - private static final long serialVersionUID = 8701146064257627671L; - - private int numChosen = 0; - private int numRequired = 0; - private List choiceList; - private CostReturn returnCost; - private SpellAbility ability; - private Command paid; - private Command unpaid; - - /** - *

- * Constructor for InputPayReturnCost. - *

- * - * @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 InputPayReturnCost(final CostReturn cost, final SpellAbility sa, final Command paidCommand, - final Command unpaidCommand) { - final Card source = sa.getSourceCard(); - - this.ability = sa; - this.returnCost = cost; - this.choiceList = CardLists.getValidCards(Singletons.getControl().getPlayer().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), Singletons.getControl().getPlayer(), 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("Return "); - final int nLeft = this.numRequired - this.numChosen; - msg.append(nLeft).append(" "); - msg.append(this.returnCost.getDescriptiveType()); - if (nLeft > 1) { - msg.append("s"); - } - msg.append(" to your hand"); - if (!this.returnCost.getList().isEmpty()) { - msg.append("\r\nSelected:\r\n"); - for (Card selected : this.returnCost.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.returnCost.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.returnCost.getList().contains(card)) { - this.numChosen--; - card.setUsedToPay(false); - this.returnCost.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.returnCost.getList()) { - selected.setUsedToPay(false); - Singletons.getModel().getGame().getAction().moveTo(ZoneType.Hand, selected); - } - this.returnCost.addListToHash(ability, "Returned"); - this.paid.execute(); - } - - /** - *

- * executes unpaid commmand. - *

- * - */ - public void cancel() { - this.stop(); - for (Card selected : this.returnCost.getList()) { - selected.setUsedToPay(false); - } - this.unpaid.execute(); - } - - @Override public void isClassUpdated() { - } - -} 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 8141755d7ae..00000000000 --- a/src/main/java/forge/control/input/InputPaySacCost.java +++ /dev/null @@ -1,191 +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 Input { - /** - * 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(); - } - - @Override public void isClassUpdated() { - } -} diff --git a/src/main/java/forge/control/input/InputPayment.java b/src/main/java/forge/control/input/InputPayment.java new file mode 100644 index 00000000000..b166bf70fcf --- /dev/null +++ b/src/main/java/forge/control/input/InputPayment.java @@ -0,0 +1,5 @@ +package forge.control.input; + +public interface InputPayment extends InputSynchronized { + boolean isPaid(); +} \ No newline at end of file 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 59% rename from src/main/java/forge/control/input/InputSelectMany.java rename to src/main/java/forge/control/input/InputSelectListBase.java index b4f9f497150..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 Input { +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 Input { 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,20 +66,18 @@ public abstract class InputSelectMany extends Input { @Override public final void selectButtonCancel() { - // this.stop(); - Input next = onCancel(); // might add ability to stack from here - // if ( next != null ) { - // Singletons.getModel().getMatch().getInput().setInput(next); - // } + bCancelled = true; + this.stop(); + } - if (null == next) { - this.stop(); - } else { - this.stopSetNext(next); - } + @Override + public final boolean hasCancelled() { + return bCancelled; + } - // for a next use - selected.clear(); + @Override + public final List getSelected() { + return selected; } @Override @@ -87,24 +86,7 @@ public abstract class InputSelectMany extends Input { // if an ability is put on stack before this input is stopped; // if it does, uncomment the 5 lines below, use them as method body - // this.stop(); - Input next = onDone(); // might add ability to stack from here - // if ( next != null ) { - // Singletons.getModel().getMatch().getInput().setInput(next); - // } - - if (null == next) { - this.stop(); - } else { - this.stopSetNext(next); - } - - // for a next use - selected.clear(); - } - - @Override - public void isClassUpdated() { + this.stop(); } public void setMessage(String message0) { @@ -112,27 +94,41 @@ public abstract class InputSelectMany extends Input { } // must define these - protected abstract Input onDone(); protected abstract boolean isValidChoice(T choice); // might re-define later - protected Input onCancel() { return null; } - 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/control/input/InputSelectManyPlayers.java b/src/main/java/forge/control/input/InputSelectManyPlayers.java deleted file mode 100644 index e0a8634f07e..00000000000 --- a/src/main/java/forge/control/input/InputSelectManyPlayers.java +++ /dev/null @@ -1,40 +0,0 @@ -package forge.control.input; - -import java.util.List; - -import com.google.common.base.Function; -import com.google.common.base.Predicate; - -import forge.game.player.Player; - -public class InputSelectManyPlayers extends InputSelectMany { - private static final long serialVersionUID = -8209690791522735L; - - protected final Function, Input> onComplete; - private final Predicate allowedFilter; - - public InputSelectManyPlayers(final Predicate allowedRule, int min, int max, final Function, Input> onDone) { - - super(min, max); - allowedFilter = allowedRule; - onComplete = onDone; - } - - - @Override - public void selectPlayer(final Player p) { - selectEntity(p); - } - - protected boolean isValidChoice(Player choice) { - if (allowedFilter != null && !allowedFilter.apply(choice)) { - return false; - } - return true; - } - - @Override - protected Input onDone() { - return onComplete.apply(selected); - } -} diff --git a/src/main/java/forge/control/input/InputSynchronized.java b/src/main/java/forge/control/input/InputSynchronized.java new file mode 100644 index 00000000000..c89fa472aee --- /dev/null +++ b/src/main/java/forge/control/input/InputSynchronized.java @@ -0,0 +1,6 @@ +package forge.control.input; + +public interface InputSynchronized extends Input { + + public void awaitLatchRelease(); +} diff --git a/src/main/java/forge/control/input/InputSyncronizedBase.java b/src/main/java/forge/control/input/InputSyncronizedBase.java new file mode 100644 index 00000000000..6b975f19ec7 --- /dev/null +++ b/src/main/java/forge/control/input/InputSyncronizedBase.java @@ -0,0 +1,31 @@ +package forge.control.input; + +import java.util.concurrent.CountDownLatch; + +import forge.FThreads; +import forge.error.BugReporter; + +public abstract class InputSyncronizedBase extends InputBase implements InputSynchronized { + private static final long serialVersionUID = 8756177361251703052L; + + private final CountDownLatch cdlDone; + + public InputSyncronizedBase() { + cdlDone = new CountDownLatch(1); + } + + public void awaitLatchRelease() { + FThreads.checkEDT("InputSyncronizedBase.awaitLatchRelease", false); + try{ + cdlDone.await(); + } catch (InterruptedException e) { + BugReporter.reportException(e); + } + } + + + @Override + protected void afterStop() { + cdlDone.countDown(); + } +} \ No newline at end of file diff --git a/src/main/java/forge/deck/CardCollections.java b/src/main/java/forge/deck/CardCollections.java index d669de66eb2..ae30b624c1b 100644 --- a/src/main/java/forge/deck/CardCollections.java +++ b/src/main/java/forge/deck/CardCollections.java @@ -19,9 +19,12 @@ package forge.deck; import java.io.File; +import org.apache.commons.lang.time.StopWatch; + import forge.deck.io.DeckGroupSerializer; import forge.deck.io.DeckSerializer; import forge.deck.io.OldDeckParser; +import forge.gui.toolbox.FSkin; import forge.properties.NewConstants; import forge.util.storage.IStorage; import forge.util.storage.StorageImmediatelySerialized; @@ -44,6 +47,9 @@ public class CardCollections { * @param file the file */ public CardCollections() { + FSkin.setProgessBarMessage("Loading decks"); + StopWatch sw = new StopWatch(); + sw.start(); this.constructed = new StorageImmediatelySerialized(new DeckSerializer(new File(NewConstants.DECK_CONSTRUCTED_DIR), true)); this.draft = new StorageImmediatelySerialized(new DeckGroupSerializer(new File(NewConstants.DECK_DRAFT_DIR))); this.sealed = new StorageImmediatelySerialized(new DeckGroupSerializer(new File(NewConstants.DECK_SEALED_DIR))); @@ -51,8 +57,10 @@ public class CardCollections { this.scheme = new StorageImmediatelySerialized(new DeckSerializer(new File(NewConstants.DECK_SCHEME_DIR))); this.plane = new StorageImmediatelySerialized(new DeckSerializer(new File(NewConstants.DECK_PLANE_DIR))); - System.out.printf("Read decks: %d constructed, %d sealed, %d draft, %d cubes, %d scheme, %d planar.%n", constructed.size(), sealed.size(), draft.size(), cube.size(), scheme.size(), plane.size()); - + sw.stop(); + System.out.printf("Read decks (%d ms): %d constructed, %d sealed, %d draft, %d cubes, %d scheme, %d planar.%n", sw.getTime(), constructed.size(), sealed.size(), draft.size(), cube.size(), scheme.size(), plane.size()); +// int sum = constructed.size() + sealed.size() + draft.size() + cube.size() + scheme.size() + plane.size(); +// FSkin.setProgessBarMessage(String.format("Loaded %d decks in %f sec", sum, sw.getTime() / 1000f )); // remove this after most people have been switched to new layout final OldDeckParser oldParser = new OldDeckParser(this.constructed, this.draft, this.sealed, this.cube); oldParser.tryParse(); diff --git a/src/main/java/forge/game/GameAction.java b/src/main/java/forge/game/GameAction.java index 1f9f173ab54..b96c156b628 100644 --- a/src/main/java/forge/game/GameAction.java +++ b/src/main/java/forge/game/GameAction.java @@ -486,24 +486,6 @@ public class GameAction { final String recoverCost = recoverable.getKeyword().get(recoverable.getKeywordPosition("Recover")).split(":")[1]; final Cost cost = new Cost(recoverable, recoverCost, true); - final Command paidCommand = new Command() { - private static final long serialVersionUID = -6357156873861051845L; - - @Override - public void execute() { - moveToHand(recoverable); - } - }; - - final Command unpaidCommand = new Command() { - private static final long serialVersionUID = -7354791599039157375L; - - @Override - public void execute() { - exile(recoverable); - } - }; - final SpellAbility abRecover = new AbilityActivated(recoverable, cost, null) { private static final long serialVersionUID = 8858061639236920054L; @@ -535,8 +517,10 @@ public class GameAction { Player p = recoverable.getController(); if (p.isHuman()) { - GameActionUtil.payCostDuringAbilityResolve(p, abRecover, abRecover.getPayCosts(), - paidCommand, unpaidCommand, null, game); + if ( GameActionUtil.payCostDuringAbilityResolve(p, abRecover, abRecover.getPayCosts(), null, game) ) + moveToHand(recoverable); + else + exile(recoverable); } else { // computer if (ComputerUtilCost.canPayCost(abRecover, p)) { ComputerUtil.playNoStack((AIPlayer)p, abRecover, game); diff --git a/src/main/java/forge/game/GameActionPlay.java b/src/main/java/forge/game/GameActionPlay.java index 4de5f530440..174c721db0a 100644 --- a/src/main/java/forge/game/GameActionPlay.java +++ b/src/main/java/forge/game/GameActionPlay.java @@ -2,7 +2,6 @@ package forge.game; import java.util.ArrayList; import java.util.List; - import com.google.common.collect.Lists; import forge.Card; @@ -10,6 +9,7 @@ import forge.CardCharacteristicName; import forge.CardColor; import forge.CardLists; import forge.CardPredicates; +import forge.FThreads; import forge.card.MagicColor; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; @@ -23,7 +23,6 @@ import forge.card.spellability.SpellAbilityRequirements; import forge.card.spellability.Target; import forge.card.spellability.TargetSelection; import forge.card.staticability.StaticAbility; -import forge.control.input.InputControl; import forge.control.input.InputPayManaSimple; import forge.game.ai.ComputerUtilCard; import forge.game.player.Player; @@ -37,17 +36,12 @@ import forge.gui.GuiChoose; public class GameActionPlay { private final GameState game; - private InputControl matchInput; public GameActionPlay(final GameState game0) { game = game0; } - void setMatchInput(InputControl input) { - this.matchInput = input; // TODO: Add 0 to parameter's name. - } - public final void playCardWithoutManaCost(final Card c, Player player) { final List choices = c.getBasicSpells(); // TODO add Buyback, Kicker, ... , spells here @@ -71,6 +65,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 @@ -358,6 +353,7 @@ public class GameActionPlay { * a {@link forge.card.spellability.SpellAbility} object. */ public final void playSpellAbility(SpellAbility sa, Player activator) { + FThreads.checkEDT("Player.playSpellAbility", false); sa.setActivatingPlayer(activator); final Card source = sa.getSourceCard(); @@ -385,6 +381,7 @@ public class GameActionPlay { ability = ability.getSubAbility(); } + // System.out.println("Playing:" + sa.getDescription() + " of " + sa.getSourceCard() + " new = " + newAbility); if (newAbility) { final TargetSelection ts = new TargetSelection(sa.getTarget(), sa); CostPayment payment = null; @@ -403,15 +400,19 @@ public class GameActionPlay { } else { manaCost = this.getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost())); } + + if (!manaCost.isPaid()) { + FThreads.setInputAndWait(new InputPayManaSimple(game, sa, manaCost)); + } + + if (manaCost.isPaid()) { if (sa.isSpell() && !source.isCopiedSpell()) { sa.setSourceCard(game.getAction().moveToStack(source)); } game.getStack().add(sa); - } else { - matchInput.setInput(new InputPayManaSimple(game, sa, manaCost)); - } + } } } @@ -446,60 +447,19 @@ public class GameActionPlay { } else { manaCost = this.getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost())); } + + if( !manaCost.isPaid() ) { + FThreads.setInputAndWait(new InputPayManaSimple(game, sa, getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost())))); + } + if (manaCost.isPaid()) { AbilityUtils.resolve(sa, false); - return; - } else { - matchInput.setInput(new InputPayManaSimple(game, sa, true)); } + } } - /** The Cost cutting_ get multi kicker mana cost paid. */ - private int costCuttingGetMultiKickerManaCostPaid = 0; - /** The Cost cutting_ get multi kicker mana cost paid_ colored. */ - private String costCuttingGetMultiKickerManaCostPaidColored = ""; - - /** - * Gets the cost cutting get multi kicker mana cost paid. - * - * @return the costCuttingGetMultiKickerManaCostPaid - */ - public int getCostCuttingGetMultiKickerManaCostPaid() { - return this.costCuttingGetMultiKickerManaCostPaid; - } - - /** - * Sets the cost cutting get multi kicker mana cost paid. - * - * @param costCuttingGetMultiKickerManaCostPaid0 - * the costCuttingGetMultiKickerManaCostPaid to set - */ - public void setCostCuttingGetMultiKickerManaCostPaid(final int costCuttingGetMultiKickerManaCostPaid0) { - this.costCuttingGetMultiKickerManaCostPaid = costCuttingGetMultiKickerManaCostPaid0; - } - - /** - * Gets the cost cutting get multi kicker mana cost paid colored. - * - * @return the costCuttingGetMultiKickerManaCostPaidColored - */ - public String getCostCuttingGetMultiKickerManaCostPaidColored() { - return this.costCuttingGetMultiKickerManaCostPaidColored; - } - - /** - * Sets the cost cutting get multi kicker mana cost paid colored. - * - * @param costCuttingGetMultiKickerManaCostPaidColored0 - * the costCuttingGetMultiKickerManaCostPaidColored to set - */ - public void setCostCuttingGetMultiKickerManaCostPaidColored( - final String costCuttingGetMultiKickerManaCostPaidColored0) { - this.costCuttingGetMultiKickerManaCostPaidColored = costCuttingGetMultiKickerManaCostPaidColored0; - } - /** * Gets the convokable colors. * diff --git a/src/main/java/forge/game/GameActionUtil.java b/src/main/java/forge/game/GameActionUtil.java index d0183522413..dfbcc7edb9c 100644 --- a/src/main/java/forge/game/GameActionUtil.java +++ b/src/main/java/forge/game/GameActionUtil.java @@ -20,7 +20,6 @@ package forge.game; import java.util.ArrayList; import java.util.HashMap; import java.util.List; - import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Iterables; @@ -34,6 +33,7 @@ import forge.CardUtil; import forge.Command; import forge.Constant; import forge.CounterType; +import forge.FThreads; import forge.Singletons; import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityUtils; @@ -57,10 +57,10 @@ import forge.card.spellability.AbilityManaPart; import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbilityRestriction; -import forge.control.input.Input; -import forge.control.input.InputPayDiscardCost; import forge.control.input.InputPayManaExecuteCommands; -import forge.control.input.InputPayReturnCost; +import forge.control.input.InputPayment; +import forge.control.input.InputSelectCards; +import forge.control.input.InputSelectCardsFromList; import forge.game.event.CardDamagedEvent; import forge.game.event.LifeLossEvent; import forge.game.player.AIPlayer; @@ -367,29 +367,21 @@ public final class GameActionUtil { ripple.execute(); } + private static int getAmountFromPart(CostPart part, Card source, SpellAbility sourceAbility) { + String amountString = part.getAmount(); + return StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : AbilityUtils.calculateAmount(source, amountString, sourceAbility); + } + /** - *

- * payManaDuringAbilityResolve. - *

- * - * @param message - * a {@link java.lang.String} object. - * @param spellManaCost - * a {@link java.lang.String} object. - * @param paid - * a {@link forge.Command} object. - * @param unpaid - * a {@link forge.Command} object. + * TODO: Write javadoc for this method. + * @param part + * @param source + * @param sourceAbility + * @return */ - public static void payManaDuringAbilityResolve(final String message, final ManaCost spellManaCost, final Command paid, - final Command unpaid) { - // temporarily disable the Resolve flag, so the user can payMana for the - // resolving Ability - GameState game = Singletons.getModel().getGame(); - final boolean bResolving = game.getStack().isResolving(); - game.getStack().setResolving(false); - Singletons.getModel().getMatch().getInput().setInput(new InputPayManaExecuteCommands(game, message, spellManaCost.toString(), paid, unpaid)); - game.getStack().setResolving(bResolving); + private static int getAmountFromPartX(CostPart part, Card source, SpellAbility sourceAbility) { + String amountString = part.getAmount(); + return StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : CardFactoryUtil.xCount(source, source.getSVar(amountString)); } /** @@ -407,8 +399,7 @@ public final class GameActionUtil { * a {@link forge.Command} object. * @param sourceAbility TODO */ - public static void payCostDuringAbilityResolve(final Player p, final SpellAbility ability, final Cost cost, final Command paid, - final Command unpaid, SpellAbility sourceAbility, final GameState game) { + public static boolean payCostDuringAbilityResolve(final Player p, final SpellAbility ability, final Cost cost, SpellAbility sourceAbility, final GameState game) { final Card source = ability.getSourceCard(); final List parts = cost.getCostParts(); ArrayList remainingParts = new ArrayList(cost.getCostParts()); @@ -416,53 +407,40 @@ public final class GameActionUtil { if (!parts.isEmpty()) { costPart = parts.get(0); } - String orString = ""; - if (sourceAbility != null) { - orString = " (or: " + sourceAbility.getStackDescription() + ")"; - } + final String orString = sourceAbility == null ? "" : " (or: " + sourceAbility.getStackDescription() + ")"; + if (parts.isEmpty() || costPart.getAmount().equals("0")) { - if (GuiDialog.confirm(source, "Do you want to pay 0?" + orString)) { - paid.execute(); - } else { - unpaid.execute(); - } - return; + return GuiDialog.confirm(source, "Do you want to pay 0?" + orString); } + boolean hasPaid = true; //the following costs do not need inputs for (CostPart part : parts) { + boolean dontRemove = false; + if (part instanceof CostPayLife) { - String amountString = part.getAmount(); - - final int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) - : AbilityUtils.calculateAmount(source, amountString, sourceAbility); + final int amount = getAmountFromPart(part, source, sourceAbility); if (p.canPayLife(amount) && GuiDialog.confirm(source, "Do you want to pay " + amount + " life?" + orString)) { p.payLife(amount, null); } else { hasPaid = false; break; } - remainingParts.remove(part); } else if (part instanceof CostDamage) { - String amountString = part.getAmount(); - final int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) - : CardFactoryUtil.xCount(source, source.getSVar(amountString)); + int amount = getAmountFromPartX(part, source, sourceAbility); if (p.canPayLife(amount) && GuiDialog.confirm(source, "Do you want " + source + " to deal " + amount + " damage to you?")) { p.addDamage(amount, source); } else { hasPaid = false; break; } - remainingParts.remove(part); } else if (part instanceof CostPutCounter) { - String amountString = part.getAmount(); CounterType counterType = ((CostPutCounter) part).getCounter(); - int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) - : CardFactoryUtil.xCount(source, source.getSVar(amountString)); + int amount = getAmountFromPartX(part, source, sourceAbility); String plural = amount > 1 ? "s" : ""; if (GuiDialog.confirm(source, "Do you want to put " + amount + " " + counterType.getName() + " counter" + plural + " on " + source + "?")) { @@ -470,7 +448,7 @@ public final class GameActionUtil { source.addCounter(counterType, amount, false); } else { hasPaid = false; - Singletons.getModel().getGame().getGameLog().add("ResolveStack", "Trying to pay upkeep for " + source + " but it can't have " + p.getGame().getGameLog().add("ResolveStack", "Trying to pay upkeep for " + source + " but it can't have " + counterType.getName() + " counters put on it.", 2); break; } @@ -478,14 +456,11 @@ public final class GameActionUtil { hasPaid = false; break; } - remainingParts.remove(part); } else if (part instanceof CostRemoveCounter) { - String amountString = part.getAmount(); CounterType counterType = ((CostRemoveCounter) part).getCounter(); - int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) - : CardFactoryUtil.xCount(source, source.getSVar(amountString)); + int amount = getAmountFromPartX(part, source, sourceAbility); String plural = amount > 1 ? "s" : ""; if (part.canPay(sourceAbility, source, p, cost, game) && GuiDialog.confirm(source, "Do you want to remove " + amount + " " + counterType.getName() @@ -495,7 +470,6 @@ public final class GameActionUtil { hasPaid = false; break; } - remainingParts.remove(part); } else if (part instanceof CostExile) { @@ -503,35 +477,32 @@ public final class GameActionUtil { if (GuiDialog.confirm(source, "Do you want to exile all cards in your graveyard?")) { List cards = new ArrayList(p.getCardsIn(ZoneType.Graveyard)); for (final Card card : cards) { - Singletons.getModel().getGame().getAction().exile(card); + p.getGame().getAction().exile(card); } } else { hasPaid = false; break; } - remainingParts.remove(part); } else { CostExile costExile = (CostExile) part; ZoneType from = costExile.getFrom(); List list = CardLists.getValidCards(p.getCardsIn(from), part.getType().split(";"), p, source); final int nNeeded = AbilityUtils.calculateAmount(source, part.getAmount(), ability); - if (list.size() >= nNeeded) { - for (int i = 0; i < nNeeded; i++) { - - final Card c = GuiChoose.oneOrNone("Exile from " + from, list); - - if (c != null) { - list.remove(c); - Singletons.getModel().getGame().getAction().exile(c); - } else { - hasPaid = false; - break; - } - } - } else { + if (list.size() < nNeeded) { hasPaid = false; break; } + + for (int i = 0; i < nNeeded; i++) { + final Card c = GuiChoose.oneOrNone("Exile from " + from, list); + if (c != null) { + list.remove(c); + p.getGame().getAction().exile(c); + } else { + hasPaid = false; + break; + } + } } } @@ -550,73 +521,84 @@ public final class GameActionUtil { GuiUtils.clearPanelSelections(); GuiUtils.setPanelSelection(source); - if (!GuiDialog.confirm(source, "Do you want to pay the sacrifice cost?")) { + List toSac = p.getController().choosePermanentsToSacrifice(list, amount, ability, false, true); + if ( toSac.size() != amount ) { hasPaid = false; break; } - - for (int i = 0; i < amount; i++) { - if (list.isEmpty()) { - hasPaid = false; - break; - } - Object o = GuiChoose.one("Select a card to sacrifice", list); - if (o != null) { - final Card c = (Card) o; - - Singletons.getModel().getGame().getAction().sacrifice(c, ability); - - list.remove(c); - } + for(Card c : toSac) { + p.getGame().getAction().sacrifice(c, ability); + } + } + + else if (part instanceof CostReturn) { + List choiceList = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType().split(";"), p, source); + int amount = getAmountFromPartX(part, source, sourceAbility); + + InputSelectCards inp = new InputSelectCardsFromList(amount, amount, choiceList); + inp.setMessage("Select %d card(s) to return to hand"); + inp.setCancelWithSelectedAllowed(true); + + FThreads.setInputAndWait(inp); + if( inp.hasCancelled() || inp.getSelected().size() != amount) { + hasPaid = false; + break; + } + ((CostReturn)part).addListToHash(ability, "Returned"); + for(Card c : inp.getSelected()) { + p.getGame().getAction().moveTo(ZoneType.Hand, c); } - remainingParts.remove(part); } - else if (part instanceof CostPartMana && ((CostPartMana) part).getManaToPay().equals("0")) { - remainingParts.remove(part); + else if (part instanceof CostDiscard) { + List choiceList = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), part.getType().split(";"), p, source); + int amount = getAmountFromPartX(part, source, sourceAbility); + + InputSelectCards inp = new InputSelectCardsFromList(amount, amount, choiceList); + inp.setMessage("Select %d card(s) to discard"); + inp.setCancelWithSelectedAllowed(true); + + FThreads.setInputAndWait(inp); + if( inp.hasCancelled() || inp.getSelected().size() != amount) { + hasPaid = false; + break; + } + ((CostDiscard)part).addListToHash(ability, "Discarded"); + for(Card c : inp.getSelected()) { + p.discard(c, ability); + } } + + else if (part instanceof CostPartMana ) { + if (!((CostPartMana) part).getManaToPay().equals("0")) // non-zero costs require input + dontRemove = true; + } else + throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - An unhandled type of cost has ocurred: " + part.getClass()); + + + if ( !hasPaid ) + return false; + + if( !dontRemove ) + remainingParts.remove(part); } GuiUtils.clearPanelSelections(); - if (!hasPaid) { - unpaid.execute(); - return; - } if (remainingParts.isEmpty()) { - paid.execute(); - return; + return true; } if (remainingParts.size() > 1) { - throw new RuntimeException("GameActionUtil::payCostDuringAbilityResolve - Too many payment types - " + source); + throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - Too many payment types - " + source); } costPart = remainingParts.get(0); + // check this is a mana cost + if (!(costPart instanceof CostPartMana )) + throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - The remaining payment type is not Mana."); - //TODO: if a full-featured algorithm to chain together input-based costs is implemented - // at some point in time, it's possible to restore the InputPaySacCost-based input - // interface for sacrifice costs (instead of the menu-based one above). - - //the following costs need inputs and can't be combined at the moment - Input toSet = null; - if (costPart instanceof CostReturn) { - toSet = new InputPayReturnCost((CostReturn) costPart, ability, paid, unpaid); - } - else if (costPart instanceof CostDiscard) { - toSet = new InputPayDiscardCost((CostDiscard) costPart, ability, paid, unpaid); - } - else if (costPart instanceof CostPartMana) { - toSet = new InputPayManaExecuteCommands(game, source + "\r\n", ability.getManaCost().toString(), paid, unpaid); - } - - - if (toSet != null) { - // temporarily disable the Resolve flag, so the user can payMana for the - // resolving Ability - final boolean bResolving = Singletons.getModel().getGame().getStack().isResolving(); - Singletons.getModel().getGame().getStack().setResolving(false); - Singletons.getModel().getMatch().getInput().setInput(toSet); - Singletons.getModel().getGame().getStack().setResolving(bResolving); - } + InputPayment toSet = new InputPayManaExecuteCommands(game, source + "\r\n", ability.getManaCost()); + FThreads.setInputAndWait(toSet); + return toSet.isPaid(); } // not restricted to combat damage, not restricted to dealing damage to diff --git a/src/main/java/forge/game/GameInputUpdatesThread.java b/src/main/java/forge/game/GameInputUpdatesThread.java deleted file mode 100644 index ae3fb61336e..00000000000 --- a/src/main/java/forge/game/GameInputUpdatesThread.java +++ /dev/null @@ -1,41 +0,0 @@ -package forge.game; - -import forge.error.BugReporter; -import forge.gui.match.controllers.CMessage; - -/** - * TODO: Write javadoc for this type. - * - */ -public final class GameInputUpdatesThread extends Thread { - private final MatchController match; - private final GameState game; - private boolean wasChangedRecently; - - /** - * TODO: Write javadoc for Constructor. - * @param match - * @param game - */ - public GameInputUpdatesThread(MatchController match, GameState game) { - this.match = match; - this.game = game; - } - - public void run(){ - while(!game.isGameOver()) { - boolean needsNewInput = CMessage.SINGLETON_INSTANCE.getInputControl().isValid() == false; - if ( needsNewInput ) { - match.getInput().setNewInput(game); - wasChangedRecently = true; - } - try { - Thread.sleep(wasChangedRecently ? 2 : 40); - wasChangedRecently = false; - } catch (InterruptedException e) { - BugReporter.reportException(e); - break; - } - } - } -} \ No newline at end of file diff --git a/src/main/java/forge/game/GameState.java b/src/main/java/forge/game/GameState.java index 3c646142dfd..395469436ae 100644 --- a/src/main/java/forge/game/GameState.java +++ b/src/main/java/forge/game/GameState.java @@ -95,6 +95,7 @@ public class GameState { * Constructor. * @param players2 * @param match0 + * @param input */ public GameState(Iterable players2, GameType t, MatchController match0) { /* no more zones to map here */ type = t; diff --git a/src/main/java/forge/game/MatchController.java b/src/main/java/forge/game/MatchController.java index 16256cec3e9..f94a1666756 100644 --- a/src/main/java/forge/game/MatchController.java +++ b/src/main/java/forge/game/MatchController.java @@ -129,10 +129,8 @@ public class MatchController { public void startRound() { // Deal with circular dependencies here + input = new InputControl(); currentGame = Singletons.getModel().newGame(players.keySet(),gameType, this); - input = new InputControl(currentGame); - currentGame.getActionPlay().setMatchInput(input); - Map startConditions = new HashMap(); for (Player p : currentGame.getPlayers()) { @@ -167,9 +165,11 @@ public class MatchController { SDisplayUtil.showTab(EDocID.REPORT_LOG.getDoc()); InputProxy inputControl = CMessage.SINGLETON_INSTANCE.getInputControl(); + inputControl.setMatch(this); input.addObserver(inputControl); currentGame.getStack().addObserver(inputControl); currentGame.getPhaseHandler().addObserver(inputControl); + currentGame.getGameLog().addObserver(CLog.SINGLETON_INSTANCE); currentGame.getStack().addObserver(CStack.SINGLETON_INSTANCE); // some observers are set in CMatchUI.initMatch @@ -178,8 +178,7 @@ public class MatchController { GameNew.newGame(this, startConditions, currentGame, canRandomFoil); getInput().clearInput(); - getInput().resetInput(); - getInput().setNewInput(currentGame); + //getInput().setNewInput(currentGame); // Thread thGame = new GameInputUpdatesThread(this, currentGame); diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index fac40936c61..4b36151e2dd 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import com.esotericsoftware.minlog.Log; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; @@ -44,6 +45,7 @@ import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellPermanent; import forge.game.GameActionUtil; import forge.game.GameState; +import forge.game.phase.PhaseType; import forge.game.player.AIPlayer; import forge.game.player.Player; import forge.game.zone.ZoneType; @@ -762,5 +764,97 @@ public class AiController { } return null; } + + public void onPriorityRecieved() { + final PhaseType phase = game.getPhaseHandler().getPhase(); + + if (game.getStack().size() > 0) { + playSpellAbilities(game); + } else { + switch(phase) { + case CLEANUP: + if ( game.getPhaseHandler().getPlayerTurn() == player ) { + final int size = player.getCardsIn(ZoneType.Hand).size(); + + if (!player.isUnlimitedHandSize()) { + int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize()); + final List toDiscard = player.getAi().getCardsToDiscard(max, (String[])null, null); + for (int i = 0; i < toDiscard.size(); i++) { + player.discard(toDiscard.get(i), null); + } + game.getStack().chooseOrderOfSimultaneousStackEntryAll(); + } + } + break; + + case COMBAT_DECLARE_ATTACKERS: + declareAttackers(); + break; + + case MAIN1: + case MAIN2: + Log.debug("Computer " + phase.toString()); + playLands(); + // fall through is intended + default: + playSpellAbilities(game); + break; + } + } + player.getController().passPriority(); + } + + + private void declareAttackers() { + // 12/2/10(sol) the decision making here has moved to getAttackers() + game.setCombat(new AiAttackController(player, player.getOpponent()).getAttackers()); + + final List att = game.getCombat().getAttackers(); + if (!att.isEmpty()) { + game.getPhaseHandler().setCombat(true); + } + + for (final Card element : att) { + // tapping of attackers happens after Propaganda is paid for + final StringBuilder sb = new StringBuilder(); + sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker."); + Log.debug(sb.toString()); + } + + player.getZone(ZoneType.Battlefield).updateObservers(); + + game.getPhaseHandler().setPlayersPriorityPermission(false); + + // ai is about to attack, cancel all phase skipping + for (Player p : game.getPlayers()) { + p.getController().autoPassCancel(); + } + } + + private void playLands() { + final Player player = getPlayer(); + List landsWannaPlay = getLandsToPlay(); + + while(landsWannaPlay != null && !landsWannaPlay.isEmpty() && player.canPlayLand(null)) { + Card land = chooseBestLandToPlay(landsWannaPlay); + landsWannaPlay.remove(land); + player.playLand(land); + game.getPhaseHandler().setPriority(player); + } + } + + private void playSpellAbilities(final GameState game) + { + SpellAbility sa; + do { + sa = getSpellAbilityToPlay(); + if ( sa == null ) break; + //System.out.println("Playing sa: " + sa); + if (!ComputerUtil.handlePlayingSpellAbility(player, sa, game)) { + break; + } + } while ( sa != null ); + } + } diff --git a/src/main/java/forge/game/ai/AiInputBlock.java b/src/main/java/forge/game/ai/AiInputBlock.java index bf3bc2abb70..341818aa4a8 100644 --- a/src/main/java/forge/game/ai/AiInputBlock.java +++ b/src/main/java/forge/game/ai/AiInputBlock.java @@ -3,7 +3,8 @@ package forge.game.ai; import java.util.List; import forge.Card; -import forge.control.input.Input; +import forge.Singletons; +import forge.control.input.InputBase; import forge.game.GameState; import forge.game.phase.CombatUtil; import forge.game.player.Player; @@ -12,7 +13,7 @@ import forge.game.player.Player; * TODO: Write javadoc for this type. * */ -public class AiInputBlock extends Input { +public class AiInputBlock extends InputBase { private final GameState game; /** @@ -38,6 +39,7 @@ public class AiInputBlock extends Input { CombatUtil.orderMultipleCombatants(game.getCombat()); game.getPhaseHandler().setPlayersPriorityPermission(false); - stop(); + // was not added to stack, so will be replaced by plain update + Singletons.getModel().getMatch().getInput().updateObservers(); } } diff --git a/src/main/java/forge/game/ai/AiInputCommon.java b/src/main/java/forge/game/ai/AiInputCommon.java index a2f23c97f38..6af07ce9997 100644 --- a/src/main/java/forge/game/ai/AiInputCommon.java +++ b/src/main/java/forge/game/ai/AiInputCommon.java @@ -17,18 +17,8 @@ */ package forge.game.ai; -import java.util.List; - -import com.esotericsoftware.minlog.Log; - -import forge.Card; -import forge.card.spellability.SpellAbility; -import forge.control.input.Input; -import forge.game.GameState; -import forge.game.phase.PhaseType; -import forge.game.player.AIPlayer; -import forge.game.player.Player; -import forge.game.zone.ZoneType; +import forge.FThreads; +import forge.control.input.InputBase; /** *

@@ -38,13 +28,11 @@ import forge.game.zone.ZoneType; * @author Forge * @version $Id$ */ -public class AiInputCommon extends Input { +public class AiInputCommon extends InputBase { /** Constant serialVersionUID=-3091338639571662216L. */ private static final long serialVersionUID = -3091338639571662216L; private final AiController computer; - private final AIPlayer player; - private final GameState game; /** *

@@ -56,15 +44,13 @@ public class AiInputCommon extends Input { */ public AiInputCommon(final AiController iComputer) { this.computer = iComputer; - player = computer.getPlayer(); - this.game = computer.getGame(); } /** {@inheritDoc} */ @Override public final void showMessage() { // should not think when the game is over - if (game.isGameOver()) { + if (computer.getGame().isGameOver()) { return; } @@ -76,104 +62,15 @@ public class AiInputCommon extends Input { * \"Detailed Error Trace\" to the Forge forum."); */ - final PhaseType phase = game.getPhaseHandler().getPhase(); + FThreads.invokeInNewThread(aiActions, true); - if (game.getStack().size() > 0) { - playSpellAbilities(game); - } else { - switch(phase) { - case CLEANUP: - if ( game.getPhaseHandler().getPlayerTurn() == player ) { - final int size = player.getCardsIn(ZoneType.Hand).size(); - - if (!player.isUnlimitedHandSize()) { - int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize()); - final List toDiscard = player.getAi().getCardsToDiscard(max, (String[])null, null); - for (int i = 0; i < toDiscard.size(); i++) { - player.discard(toDiscard.get(i), null); - } - game.getStack().chooseOrderOfSimultaneousStackEntryAll(); - } - } - break; - - case COMBAT_DECLARE_ATTACKERS: - declareAttackers(); - break; - - case MAIN1: - case MAIN2: - Log.debug("Computer " + phase.toString()); - playLands(); - // fall through is intended - default: - playSpellAbilities(game); - break; - } - } - player.getController().passPriority(); } // getMessage(); - /** - * TODO: Write javadoc for this method. - */ - private void declareAttackers() { - // 12/2/10(sol) the decision making here has moved to getAttackers() - game.setCombat(new AiAttackController(player, player.getOpponent()).getAttackers()); - - final List att = game.getCombat().getAttackers(); - if (!att.isEmpty()) { - game.getPhaseHandler().setCombat(true); - } - - for (final Card element : att) { - // tapping of attackers happens after Propaganda is paid for - final StringBuilder sb = new StringBuilder(); - sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker."); - Log.debug(sb.toString()); - } - - player.getZone(ZoneType.Battlefield).updateObservers(); - - game.getPhaseHandler().setPlayersPriorityPermission(false); - - // ai is about to attack, cancel all phase skipping - for (Player p : game.getPlayers()) { - p.getController().autoPassCancel(); - } - } - - /** - * TODO: Write javadoc for this method. - */ - private void playLands() { - final Player player = computer.getPlayer(); - List landsWannaPlay = computer.getLandsToPlay(); + final Runnable aiActions = new Runnable() { - while(landsWannaPlay != null && !landsWannaPlay.isEmpty() && player.canPlayLand(null)) { - Card land = computer.chooseBestLandToPlay(landsWannaPlay); - landsWannaPlay.remove(land); - player.playLand(land); - game.getPhaseHandler().setPriority(player); + @Override + public void run() { + computer.onPriorityRecieved(); } - } - - protected void playSpellAbilities(final GameState game) - { - SpellAbility sa; - do { - sa = computer.getSpellAbilityToPlay(); - if ( sa == null ) break; - //System.out.println("Playing sa: " + sa); - if (!ComputerUtil.handlePlayingSpellAbility(player, sa, game)) { - break; - } - } while ( sa != null ); - } - - - /* (non-Javadoc) - * @see forge.control.input.Input#isClassUpdated() - */ - @Override public void isClassUpdated() { } + }; } diff --git a/src/main/java/forge/game/phase/CombatUtil.java b/src/main/java/forge/game/phase/CombatUtil.java index ca05584df4a..d78e99e53df 100644 --- a/src/main/java/forge/game/phase/CombatUtil.java +++ b/src/main/java/forge/game/phase/CombatUtil.java @@ -1150,39 +1150,13 @@ public class CombatUtil { } }; - final Command unpaidCommand = new Command() { - - private static final long serialVersionUID = -6483405139208343935L; - - @Override - public void execute() { - game.getCombat().removeFromCombat(crd); - - if (bLast) { - PhaseUtil.handleAttackingTriggers(); - } - } - }; - - final Command paidCommand = new Command() { - private static final long serialVersionUID = -8303368287601871955L; - - @Override - public void execute() { - // if Propaganda is paid, tap this card - if (!crd.hasKeyword("Vigilance")) { - crd.tap(); - } - - if (bLast) { - PhaseUtil.handleAttackingTriggers(); - } - } - }; - ability.setActivatingPlayer(c.getController()); if (c.getController().isHuman()) { - GameActionUtil.payCostDuringAbilityResolve(c.getController(), ability, attackCost, paidCommand, unpaidCommand, null, game); + if ( GameActionUtil.payCostDuringAbilityResolve(c.getController(), ability, attackCost, null, game) ) { + if (!crd.hasKeyword("Vigilance")) { crd.tap(); } + } else { + game.getCombat().removeFromCombat(crd); + } } else { // computer if (ComputerUtilCost.canPayCost(ability, c.getController())) { ComputerUtil.playNoStack((AIPlayer)c.getController(), ability, game); @@ -1194,10 +1168,9 @@ public class CombatUtil { // during Declare_Attackers game.getCombat().removeFromCombat(crd); } - if (bLast) { - PhaseUtil.handleAttackingTriggers(); - } } + if (bLast) + PhaseUtil.handleAttackingTriggers(); } } diff --git a/src/main/java/forge/game/phase/PhaseHandler.java b/src/main/java/forge/game/phase/PhaseHandler.java index a871a13feec..e6561528d27 100644 --- a/src/main/java/forge/game/phase/PhaseHandler.java +++ b/src/main/java/forge/game/phase/PhaseHandler.java @@ -24,6 +24,7 @@ import java.util.Stack; import com.esotericsoftware.minlog.Log; import forge.Card; +import forge.FThreads; import forge.Singletons; import forge.card.trigger.TriggerType; import forge.game.GameState; @@ -399,7 +400,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { // This line fixes Combat Damage triggers not going off when they should game.getStack().unfreezeStack(); - + // UNTAP if (this.getPhase() != PhaseType.UNTAP) { // during untap @@ -734,15 +735,25 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { nextPhase(); return; } else if (!game.getStack().hasSimultaneousStackEntries()) { - game.getStack().resolveStack(); + Runnable proc = new Runnable(){ + @Override public void run() { + game.getStack().resolveStack(); + game.getStack().chooseOrderOfSimultaneousStackEntryAll(); + } + }; + + if ( FThreads.isEDT() ) + FThreads.invokeInNewThread(proc, true); + else + proc.run(); } } else { // pass the priority to other player this.pPlayerPriority = nextPlayer; - Singletons.getModel().getMatch().getInput().resetInput(); + Singletons.getModel().getMatch().getInput().updateObservers(); } - game.getStack().chooseOrderOfSimultaneousStackEntryAll(); + } /** @@ -816,5 +827,10 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { public void incPlanarDiceRolledthisTurn() { this.planarDiceRolledthisTurn++; } + + public String debugPrintState() { + return String.format("%s's %s, priority of %s [%sP]", getPlayerTurn(), getPhase(), getPriorityPlayer(), isPlayerPriorityAllowed() ? "+" : "-"); + } + } diff --git a/src/main/java/forge/game/phase/Untap.java b/src/main/java/forge/game/phase/Untap.java index 04eb31b37b0..df70fb7bcc3 100644 --- a/src/main/java/forge/game/phase/Untap.java +++ b/src/main/java/forge/game/phase/Untap.java @@ -30,7 +30,7 @@ import forge.CardPredicates.Presets; import forge.CounterType; import forge.GameEntity; import forge.Singletons; -import forge.control.input.Input; +import forge.control.input.InputBase; import forge.game.GameState; import forge.game.ai.ComputerUtilCard; import forge.game.player.Player; @@ -210,7 +210,7 @@ public class Untap extends Phase { landList.get(0).untap(); } } else { - final Input target = new Input() { + final InputBase target = new InputBase() { private static final long serialVersionUID = 6653677835629939465L; @Override @@ -250,7 +250,7 @@ public class Untap extends Phase { ComputerUtilCard.getBestArtifactAI(artList).untap(); } } else { - final Input target = new Input() { + final InputBase target = new InputBase() { private static final long serialVersionUID = 5555427219659889707L; @Override @@ -290,7 +290,7 @@ public class Untap extends Phase { creatures.get(0).untap(); } } else { - final Input target = new Input() { + final InputBase target = new InputBase() { private static final long serialVersionUID = 5555427219659889707L; @Override diff --git a/src/main/java/forge/game/phase/Upkeep.java b/src/main/java/forge/game/phase/Upkeep.java index ed5623d075d..76d349ab92e 100644 --- a/src/main/java/forge/game/phase/Upkeep.java +++ b/src/main/java/forge/game/phase/Upkeep.java @@ -21,14 +21,13 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - import com.google.common.base.Predicate; import forge.Card; import forge.CardLists; import forge.CardPredicates; +import forge.FThreads; import forge.CardPredicates.Presets; -import forge.Command; import forge.CounterType; import forge.Singletons; import forge.card.cardfactory.CardFactoryUtil; @@ -39,8 +38,9 @@ import forge.card.spellability.Ability; import forge.card.spellability.AbilityManaPart; import forge.card.spellability.AbilityStatic; import forge.card.spellability.SpellAbility; -import forge.control.input.Input; -import forge.control.input.InputSelectManyCards; +import forge.control.input.InputBase; +import forge.control.input.InputPayManaExecuteCommands; +import forge.control.input.InputSelectCards; import forge.game.GameActionUtil; import forge.game.GameState; import forge.game.ai.ComputerUtil; @@ -174,18 +174,6 @@ public class Upkeep extends Phase { for (int i = 0; i < list.size(); i++) { final Card c = list.get(i); if (c.hasStartOfKeyword("(Echo unpaid)")) { - - final Command paidCommand = Command.BLANK; - - final Command unpaidCommand = new Command() { - private static final long serialVersionUID = -7354791599039157375L; - - @Override - public void execute() { - game.getAction().sacrifice(c, null); - } - }; - final Ability blankAbility = Upkeep.BlankAbility(c, c.getEchoCost()); final StringBuilder sb = new StringBuilder(); @@ -198,7 +186,9 @@ public class Upkeep extends Phase { Player controller = c.getController(); if (controller.isHuman()) { Cost cost = new Cost(c, c.getEchoCost().trim(), true); - GameActionUtil.payCostDuringAbilityResolve(controller, blankAbility, cost, paidCommand, unpaidCommand, null, game); + if ( !GameActionUtil.payCostDuringAbilityResolve(controller, blankAbility, cost, null, game) ) + game.getAction().sacrifice(c, null);; + } else { // computer if (ComputerUtilCost.canPayCost(blankAbility, controller)) { ComputerUtil.playNoStack((AIPlayer)controller, blankAbility, game); @@ -279,44 +269,34 @@ public class Upkeep extends Phase { final String[] k = ability.split(" pay "); final ManaCost upkeepCost = new ManaCost(new ManaCostParser(k[1])); - final Command unpaidCommand = new Command() { - private static final long serialVersionUID = 8942537892273123542L; - - @Override - public void execute() { - if (c.getName().equals("Cosmic Horror")) { - controller.addDamage(7, c); - } - game.getAction().destroy(c); - } - }; - - final Command paidCommand = Command.BLANK; - - final Ability aiPaid = Upkeep.BlankAbility(c, upkeepCost.toString()); - - final StringBuilder sb = new StringBuilder(); - sb.append("Upkeep for ").append(c).append("\n"); + final String sb = "Upkeep for " + c; final Ability upkeepAbility = new Ability(c, ManaCost.ZERO) { @Override public void resolve() { + final boolean isUpkeepPaid; if (controller.isHuman()) { - GameActionUtil.payManaDuringAbilityResolve(sb.toString(), upkeepCost, paidCommand, unpaidCommand); + InputPayManaExecuteCommands inp = new InputPayManaExecuteCommands(game, sb, upkeepCost); + FThreads.setInputAndWait(inp); + isUpkeepPaid = inp.isPaid(); } else { // computer - if (ComputerUtilCost.canPayCost(aiPaid, controller) && !c.hasKeyword("Indestructible")) { + Ability aiPaid = Upkeep.BlankAbility(c, upkeepCost.toString()); + isUpkeepPaid = ComputerUtilCost.canPayCost(aiPaid, controller) && !c.hasKeyword("Indestructible"); + if (isUpkeepPaid) { ComputerUtil.playNoStack((AIPlayer)controller, aiPaid, game); - } else { - if (c.getName().equals("Cosmic Horror")) { - controller.addDamage(7, c); - } - game.getAction().destroy(c); } } + if( !isUpkeepPaid ) { + if (c.getName().equals("Cosmic Horror")) { + controller.addDamage(7, c); + } + game.getAction().destroy(c); + } + } }; upkeepAbility.setActivatingPlayer(controller); - upkeepAbility.setStackDescription(sb.toString()); - upkeepAbility.setDescription(sb.toString()); + upkeepAbility.setStackDescription(sb); + upkeepAbility.setDescription(sb); game.getStack().addSimultaneousStackEntry(upkeepAbility); } // destroy @@ -341,18 +321,6 @@ public class Upkeep extends Phase { } final String upkeepCost = cost; - - final Command unpaidCommand = new Command() { - private static final long serialVersionUID = 5612348769167529102L; - - @Override - public void execute() { - game.getAction().sacrifice(c, null); - } - }; - - final Command paidCommand = Command.BLANK; - final Ability blankAbility = Upkeep.BlankAbility(c, upkeepCost); blankAbility.setActivatingPlayer(controller); @@ -360,11 +328,11 @@ public class Upkeep extends Phase { @Override public void resolve() { if (controller.isHuman()) { - GameActionUtil.payCostDuringAbilityResolve(controller, blankAbility, blankAbility.getPayCosts(), - paidCommand, unpaidCommand, this, game); + if ( !GameActionUtil.payCostDuringAbilityResolve(controller, blankAbility, blankAbility.getPayCosts(), this, game)) + game.getAction().sacrifice(c, null); } else { // computer if (ComputerUtilCost.shouldPayCost(controller, c, upkeepCost) && ComputerUtilCost.canPayCost(blankAbility, controller)) { - ComputerUtil.playNoStack((AIPlayer)controller, blankAbility, game); + ComputerUtil.playNoStack((AIPlayer)controller, blankAbility, game); // this makes AI pay } else { game.getAction().sacrifice(c, null); } @@ -386,34 +354,25 @@ public class Upkeep extends Phase { final String[] l = k[1].split(" pay "); final ManaCost upkeepCost = new ManaCost(new ManaCostParser(l[1])); - final Command unpaidCommand = new Command() { - private static final long serialVersionUID = 1238166187561501928L; - - @Override - public void execute() { - controller.addDamage(upkeepDamage, c); - } - }; - - final Command paidCommand = Command.BLANK; - - final Ability aiPaid = Upkeep.BlankAbility(c, upkeepCost.toString()); - - final StringBuilder sb = new StringBuilder(); - sb.append("Damage upkeep for ").append(c).append("\n"); + final String sb = "Damage upkeep for " + c; final Ability upkeepAbility = new Ability(c, ManaCost.ZERO) { @Override public void resolve() { + boolean isUpkeepPaid = false; if (controller.isHuman()) { - GameActionUtil.payManaDuringAbilityResolve(sb.toString(), upkeepCost, paidCommand, unpaidCommand); + InputPayManaExecuteCommands inp = new InputPayManaExecuteCommands(game, sb, upkeepCost); + FThreads.setInputAndWait(inp); + isUpkeepPaid = inp.isPaid(); } else { // computers - if (ComputerUtilCost.canPayCost(aiPaid, controller) - && (ComputerUtilCombat.predictDamageTo(controller, upkeepDamage, c, false) > 0)) { + final Ability aiPaid = Upkeep.BlankAbility(c, upkeepCost.toString()); + if (ComputerUtilCost.canPayCost(aiPaid, controller) && ComputerUtilCombat.predictDamageTo(controller, upkeepDamage, c, false) > 0) { ComputerUtil.playNoStack((AIPlayer)controller, aiPaid, game); - } else { - controller.addDamage(upkeepDamage, c); + isUpkeepPaid = true; } } + if (!isUpkeepPaid) { + controller.addDamage(upkeepDamage, c); + } } }; upkeepAbility.setActivatingPlayer(controller); @@ -478,27 +437,20 @@ public class Upkeep extends Phase { @Override public void resolve() { final List targets = CardLists.getTargetableCards(abyssGetTargets, this); - final Input 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 Input onDone() { - game.getAction().destroyNoRegeneration(selected.get(0)); - return null; - } - }; 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"); @@ -617,9 +569,7 @@ public class Upkeep extends Phase { final Player player = game.getPhaseHandler().getPlayerTurn(); final List cards = player.getCardsIn(ZoneType.Battlefield, "Demonic Hordes"); - for (int i = 0; i < cards.size(); i++) { - - final Card c = cards.get(i); + for (final Card c : cards) { final Ability cost = new Ability(c, new ManaCost(new ManaCostParser("B B B"))) { @Override @@ -627,7 +577,7 @@ public class Upkeep extends Phase { } }; // end cost ability - final Ability noPay = new Ability(c, ManaCost.ZERO) { + final Ability unpaidHordesAb = new Ability(c, ManaCost.ZERO) { @Override public void resolve() { final List playerLand = player.getLandsInPlay(); @@ -648,36 +598,24 @@ public class Upkeep extends Phase { final Player cp = c.getController(); if (cp.isHuman()) { - final String question = "Pay Demonic Hordes upkeep cost?"; - if (GuiDialog.confirm(c, question)) { - final Ability pay = new Ability(c, ManaCost.ZERO) { - @Override - public void resolve() { - if (game.getZoneOf(c).is(ZoneType.Battlefield)) { - final StringBuilder coststring = new StringBuilder(); - coststring.append("Pay cost for ").append(c).append("\r\n"); - GameActionUtil.payManaDuringAbilityResolve(coststring.toString(), cost.getManaCost(), - Command.BLANK, Command.BLANK); - } - } // end resolve() - }; // end pay ability - pay.setStackDescription("Demonic Hordes - Upkeep Cost"); - pay.setDescription("Demonic Hordes - Upkeep Cost"); + final Ability pay = new Ability(c, ManaCost.ZERO) { + @Override + public void resolve() { + if (game.getZoneOf(c).is(ZoneType.Battlefield)) { + InputPayManaExecuteCommands inp = new InputPayManaExecuteCommands(game, "Pay Demonic Hordes upkeep cost", cost.getManaCost() /*, true */); + FThreads.setInputAndWait(inp); + if ( !inp.isPaid() ) + unpaidHordesAb.resolve(); + } + } // end resolve() + }; // end pay ability + pay.setStackDescription("Demonic Hordes - Upkeep Cost"); + pay.setDescription("Demonic Hordes - Upkeep Cost"); - game.getStack().addSimultaneousStackEntry(pay); - - } // end choice - else { - final StringBuilder sb = new StringBuilder(); - sb.append(c.getName()).append(" - is tapped and you must sacrifice a land of opponent's choice"); - noPay.setStackDescription(sb.toString()); - - game.getStack().addSimultaneousStackEntry(noPay); - - } + game.getStack().addSimultaneousStackEntry(pay); } // end human else { // computer - noPay.setActivatingPlayer(cp); + unpaidHordesAb.setActivatingPlayer(cp); if (ComputerUtilCost.canPayCost(cost, (AIPlayer) cp)) { final Ability computerPay = new Ability(c, ManaCost.ZERO) { @Override @@ -689,8 +627,8 @@ public class Upkeep extends Phase { game.getStack().addSimultaneousStackEntry(computerPay); } else { - noPay.setStackDescription("Demonic Hordes - Upkeep Cost"); - game.getStack().addSimultaneousStackEntry(noPay); + unpaidHordesAb.setStackDescription("Demonic Hordes - Upkeep Cost"); + game.getStack().addSimultaneousStackEntry(unpaidHordesAb); } } // end computer @@ -1061,7 +999,7 @@ public class Upkeep extends Phase { list.remove(toTap); } } else { - Singletons.getModel().getMatch().getInput().setInput(new Input() { + Singletons.getModel().getMatch().getInput().setInput(new InputBase() { private static final long serialVersionUID = 5313424586016061612L; @Override diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index e4d5fff86bd..518c151a401 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -39,6 +39,7 @@ import forge.CardPredicates.Presets; import forge.CardUtil; import forge.Constant.Preferences; import forge.CounterType; +import forge.FThreads; import forge.GameEntity; import forge.Singletons; import forge.card.ability.AbilityFactory; @@ -1624,6 +1625,7 @@ public abstract class Player extends GameEntity implements Comparable { * a {@link forge.card.spellability.SpellAbility} object. */ protected final void doDiscard(final Card c, final SpellAbility sa) { + FThreads.checkEDT("Player.doDiscard", false); // TODO: This line should be moved inside CostPayment somehow /*if (sa != null) { sa.addCostToHashList(c, "Discarded"); @@ -1846,6 +1848,7 @@ public abstract class Player extends GameEntity implements Comparable { * a {@link forge.Card} object. */ public final void playLand(final Card land) { + FThreads.checkEDT("Player.playSpellAbility", false); if (this.canPlayLand(land)) { land.setController(this, 0); game.getAction().moveTo(this.getZone(ZoneType.Battlefield), land); diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index c63af118b8c..d3e31f50810 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 %d card(s) to sacrifice"); + + FThreads.setInputAndWait(inp); + if( inp.hasCancelled() ) + return new ArrayList(); + else return inp.getSelected(); } @Override @@ -321,8 +316,17 @@ public class PlayerControllerHuman extends PlayerController { @Override public List chooseCardsToDiscardFrom(Player p, SpellAbility sa, List valid, int minDiscard) { - int cntToKeepInHand = minDiscard == 0 ? -1 : valid.size() - minDiscard; - return GuiChoose.order("Choose cards to Discard", "Discarded", cntToKeepInHand, valid, null, null); + if ( p != getPlayer() ) { + int cntToKeepInHand = minDiscard == 0 ? -1 : valid.size() - minDiscard; + return GuiChoose.order("Choose cards to Discard", "Discarded", cntToKeepInHand, valid, null, null); + } + + int max = minDiscard == 0 ? Integer.MAX_VALUE : minDiscard; + InputSelectCards inp = new InputSelectCardsFromList(minDiscard, max, valid); + inp.setCancelWithSelectedAllowed(false); + inp.setMessage("Discard %d cards"); + FThreads.setInputAndWait(inp); + return inp.getSelected(); } @Override diff --git a/src/main/java/forge/game/player/PlayerUtil.java b/src/main/java/forge/game/player/PlayerUtil.java index a81a4bf4627..5f09094af51 100644 --- a/src/main/java/forge/game/player/PlayerUtil.java +++ b/src/main/java/forge/game/player/PlayerUtil.java @@ -24,6 +24,7 @@ import forge.CardLists; import forge.Singletons; import forge.card.spellability.SpellAbility; import forge.control.input.Input; +import forge.control.input.InputBase; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.gui.match.CMatchUI; @@ -54,12 +55,12 @@ public final class PlayerUtil { * a {@link java.lang.String} object. * @param sa * a {@link forge.card.spellability.SpellAbility} object. - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. * @since 1.0.15 */ public static Input inputDiscardNumUnless(final int nCards, final String uType, final SpellAbility sa) { final SpellAbility sp = sa; - final Input target = new Input() { + final Input target = new InputBase() { private static final long serialVersionUID = 8822292413831640944L; private int n = 0; @@ -111,12 +112,12 @@ public final class PlayerUtil { * a int. * @param sa * a {@link forge.card.spellability.SpellAbility} object. - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. * @since 1.0.15 */ public static Input inputDiscard(final int nCards, final SpellAbility sa) { final SpellAbility sp = sa; - final Input target = new Input() { + final Input target = new InputBase() { private static final long serialVersionUID = -329993322080934435L; private int n = 0; @@ -162,7 +163,7 @@ public final class PlayerUtil { * a int. * @param type * a {@link java.lang.String} object. - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. * @since 1.0.15 */ public static Input inputSacrificePermanents(final int nCards, final String type) { @@ -182,11 +183,11 @@ public final class PlayerUtil { * a {@link forge.CardList} object. * @param message * a {@link java.lang.String} object. - * @return a {@link forge.control.input.Input} object. + * @return a {@link forge.control.input.InputBase} object. * @since 1.0.15 */ public static Input inputSacrificePermanentsFromList(final int nCards, final List list, final String message) { - final Input target = new Input() { + final Input target = new InputBase() { private static final long serialVersionUID = 1981791992623774490L; private int n = 0; diff --git a/src/main/java/forge/game/zone/MagicStack.java b/src/main/java/forge/game/zone/MagicStack.java index dac8c592511..9dd58156ddf 100644 --- a/src/main/java/forge/game/zone/MagicStack.java +++ b/src/main/java/forge/game/zone/MagicStack.java @@ -22,21 +22,18 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Stack; - import com.esotericsoftware.minlog.Log; import forge.Card; 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; import forge.card.cardfactory.CardFactoryUtil; import forge.card.mana.ManaCost; -import forge.card.mana.ManaCostBeingPaid; -import forge.card.mana.ManaCostParser; import forge.card.spellability.Ability; import forge.card.spellability.AbilityStatic; import forge.card.spellability.AbilityTriggered; @@ -48,7 +45,7 @@ import forge.card.spellability.TargetChoices; import forge.card.spellability.TargetSelection; import forge.card.trigger.Trigger; import forge.card.trigger.TriggerType; -import forge.control.input.Input; +import forge.control.input.InputBase; import forge.control.input.InputPayManaExecuteCommands; import forge.game.GameActionUtil; import forge.game.GameState; @@ -295,95 +292,6 @@ public class MagicStack extends MyObservable { } } - /** - *

- * getMultiKickerSpellCostChange. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @return a {@link forge.card.mana.ManaCostBeingPaid} object. - */ - public final ManaCostBeingPaid getMultiKickerSpellCostChange(final SpellAbility sa) { - final int max = 25; - final String[] numbers = new String[max]; - for (int no = 0; no < max; no++) { - numbers[no] = String.valueOf(no); - } - - ManaCostBeingPaid manaCost = new ManaCostBeingPaid(sa.getManaCost()); - String mana = manaCost.toString(); - - int multiKickerPaid = game.getActionPlay().getCostCuttingGetMultiKickerManaCostPaid(); - - String numberManaCost = " "; - - if (mana.toString().length() == 1) { - numberManaCost = mana.toString().substring(0, 1); - } else if (mana.toString().length() == 0) { - numberManaCost = "0"; // Should Never Occur - } else { - numberManaCost = mana.toString().substring(0, 2); - } - - numberManaCost = numberManaCost.trim(); - - for (int check = 0; check < max; check++) { - if (numberManaCost.equals(numbers[check])) { - - if ((check - multiKickerPaid) < 0) { - multiKickerPaid = multiKickerPaid - check; - game.getActionPlay().setCostCuttingGetMultiKickerManaCostPaid(multiKickerPaid); - mana = mana.replaceFirst(String.valueOf(check), "0"); - } else { - mana = mana.replaceFirst(String.valueOf(check), String.valueOf(check - multiKickerPaid)); - multiKickerPaid = 0; - game.getActionPlay().setCostCuttingGetMultiKickerManaCostPaid(multiKickerPaid); - } - } - mana = mana.trim(); - if (mana.equals("")) { - mana = "0"; - } - manaCost = new ManaCostBeingPaid(mana); - } - final String colorCut = game.getActionPlay().getCostCuttingGetMultiKickerManaCostPaidColored(); - - for (int colorCutIx = 0; colorCutIx < colorCut.length(); colorCutIx++) { - if ("WUGRB".contains(colorCut.substring(colorCutIx, colorCutIx + 1)) - && !mana.equals(mana.replaceFirst((colorCut.substring(colorCutIx, colorCutIx + 1)), ""))) { - mana = mana.replaceFirst(colorCut.substring(colorCutIx, colorCutIx + 1), ""); - - game.getActionPlay().setCostCuttingGetMultiKickerManaCostPaidColored( - game.getActionPlay().getCostCuttingGetMultiKickerManaCostPaidColored() - .replaceFirst(colorCut.substring(colorCutIx, colorCutIx + 1), "")); - - mana = mana.trim(); - if (mana.equals("")) { - mana = "0"; - } - manaCost = new ManaCostBeingPaid(mana); - } - } - - return manaCost; - } - - // TODO: this may be able to use a straight copy of MultiKicker cost change - /** - *

- * getReplicateSpellCostChange. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @return a {@link forge.card.mana.ManaCostBeingPaid} object. - */ - public final ManaCostBeingPaid getReplicateSpellCostChange(final SpellAbility sa) { - final ManaCostBeingPaid manaCost = new ManaCostBeingPaid(sa.getManaCost()); - // String Mana = manaCost.toString(); - return manaCost; - } /** *

@@ -394,6 +302,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 @@ -478,49 +387,37 @@ public class MagicStack extends MyObservable { } else if (sp.isXCost()) { // TODO: convert any X costs to use abCost so it happens earlier final SpellAbility sa = sp; - final ManaCost mc = new ManaCost( new ManaCostParser(Integer.toString(sa.getXManaCost()))); - final Ability ability = new Ability(sp.getSourceCard(), mc) { - @Override - public void resolve() { - final Card crd = this.getSourceCard(); - crd.addXManaCostPaid(1); - } - }; - - final Command unpaidCommand = new Command() { - private static final long serialVersionUID = -3342222770086269767L; - - @Override - public void execute() { - MagicStack.this.push(sa); - } - }; - - final Command paidCommand = new Command() { - private static final long serialVersionUID = -2224875229611007788L; - - @Override - public void execute() { - ability.resolve(); - final Card crd = sa.getSourceCard(); - Singletons.getModel().getMatch().getInput().setInput( - new InputPayManaExecuteCommands(game, "Pay X cost for " + crd.getName() + " (X=" - + crd.getXManaCostPaid() + ")\r\n", ability.getManaCost().toString(), this, unpaidCommand, - true)); - } - }; - - final Card crd = sa.getSourceCard(); + final int xCost = sa.getXManaCost(); Player player = sp.getSourceCard().getController(); if (player.isHuman()) { - Singletons.getModel().getMatch().getInput().setInput( - new InputPayManaExecuteCommands(game, "Pay X cost for " + sp.getSourceCard().getName() + " (X=" - + crd.getXManaCostPaid() + ")\r\n", ability.getManaCost().toString(), paidCommand, - unpaidCommand, true)); + final Runnable payNextX = new Runnable() { + @Override + public void run() { + + final Card crd = sa.getSourceCard(); + + String message = "Pay X cost for " + crd.getName() + " (X=" + crd.getXManaCostPaid() + ")\r\n"; + InputPayManaExecuteCommands inp = new InputPayManaExecuteCommands(game, message, ManaCost.get(xCost), true); + FThreads.setInputAndWait(inp); + if ( inp.isPaid() ) { + crd.addXManaCostPaid(1); + this.run(); + } else + MagicStack.this.push(sa); + } + }; + payNextX.run(); } else { // computer final int neededDamage = CardFactoryUtil.getNeededXDamage(sa); - + final Ability ability = new Ability(sp.getSourceCard(), ManaCost.get(xCost)) { + @Override + public void resolve() { + final Card crd = this.getSourceCard(); + crd.addXManaCostPaid(1); + } + }; + while (ComputerUtilCost.canPayCost(ability, player) && (neededDamage != sa.getSourceCard().getXManaCostPaid())) { ComputerUtil.playNoStack((AIPlayer)player, ability, game); } @@ -532,59 +429,38 @@ public class MagicStack extends MyObservable { // both X and multi is not supported yet final SpellAbility sa = sp; - final Ability ability = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) { + final Ability abilityIncreaseMultikicker = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) { @Override public void resolve() { this.getSourceCard().addMultiKickerMagnitude(1); } }; - final Command unpaidCommand = new Command() { - private static final long serialVersionUID = -3342222770086269767L; - - @Override - public void execute() { - MagicStack.this.push(sa); - } - }; - - final Command paidCommand = new Command() { - private static final long serialVersionUID = -6037161763374971106L; - - @Override - public void execute() { - ability.resolve(); - - final ManaCostBeingPaid manaCost = MagicStack.this.getMultiKickerSpellCostChange(ability); - - if (manaCost.isPaid()) { - this.execute(); - } else { - String prompt; - int mkCostPaid = game.getActionPlay().getCostCuttingGetMultiKickerManaCostPaid(); - String mkCostPaidColored = game.getActionPlay().getCostCuttingGetMultiKickerManaCostPaidColored(); - int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude(); - if ((mkCostPaid == 0) && mkCostPaidColored.equals("")) { - prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", sa.getSourceCard(), mkMagnitude ); - } else { - prompt = String.format("Multikicker for %s\r\nMana in Reserve: %s %s\r\nTimes Kicked: %d", sa.getSourceCard(), - (mkCostPaid != 0) ? Integer.toString(mkCostPaid) : "", mkCostPaidColored, mkMagnitude); - } - Input toSet = new InputPayManaExecuteCommands(game, prompt, manaCost.toString(), this, unpaidCommand); - Singletons.getModel().getMatch().getInput().setInput(toSet); - } - } - }; Player activating = sp.getActivatingPlayer(); if (activating.isHuman()) { sa.getSourceCard().addMultiKickerMagnitude(-1); - paidCommand.execute(); + final Runnable paidCommand = new Runnable() { + @Override + public void run() { + abilityIncreaseMultikicker.resolve(); + int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude(); + String prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", sa.getSourceCard(), mkMagnitude ); + InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(game, prompt, sp.getMultiKickerManaCost()); + FThreads.setInputAndWait(toSet); + if ( toSet.isPaid() ) { + this.run(); + } else + MagicStack.this.push(sa); + + } + }; + paidCommand.run(); } else { // computer - while (ComputerUtilCost.canPayCost(ability, activating)) { - ComputerUtil.playNoStack((AIPlayer)activating, ability, game); + while (ComputerUtilCost.canPayCost(abilityIncreaseMultikicker, activating)) { + ComputerUtil.playNoStack((AIPlayer)activating, abilityIncreaseMultikicker, game); } this.push(sa); @@ -603,38 +479,27 @@ public class MagicStack extends MyObservable { } }; - final Command unpaidCommand = new Command() { - private static final long serialVersionUID = -3180458633098297855L; - - @Override - public void execute() { - for (int i = 0; i < sp.getSourceCard().getReplicateMagnitude(); i++) { - CardFactory.copySpellontoStack(sp.getSourceCard(), sp.getSourceCard(), sp, false); - } - } - }; - - final Command paidCommand = new Command() { - private static final long serialVersionUID = 132624005072267304L; - - @Override - public void execute() { - ability.resolve(); - final ManaCostBeingPaid manaCost = MagicStack.this.getReplicateSpellCostChange(ability); - if (manaCost.isPaid()) { - this.execute(); - } else { - String prompt = String.format("Replicate for %s\r\nTimes Replicated: %d\r\n", sa.getSourceCard(), sa.getSourceCard().getReplicateMagnitude()); - Input toSet = new InputPayManaExecuteCommands(game, prompt, manaCost.toString(), this, unpaidCommand); - Singletons.getModel().getMatch().getInput().setInput(toSet); - } - } - }; - + Player controller = sp.getSourceCard().getController(); if (controller.isHuman()) { sa.getSourceCard().addReplicateMagnitude(-1); - paidCommand.execute(); + final Runnable addMagnitude = new Runnable() { + @Override + public void run() { + ability.resolve(); + String prompt = String.format("Replicate for %s\r\nTimes Replicated: %d\r\n", sa.getSourceCard(), sa.getSourceCard().getReplicateMagnitude()); + InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(game, prompt, sp.getReplicateManaCost()); + FThreads.setInputAndWait(toSet); + if ( toSet.isPaid() ) { + this.run(); + } else { + for (int i = 0; i < sp.getSourceCard().getReplicateMagnitude(); i++) { + CardFactory.copySpellontoStack(sp.getSourceCard(), sp.getSourceCard(), sp, false); + } + } + } + }; + addMagnitude.run(); } else { // computer while (ComputerUtilCost.canPayCost(ability, controller)) { @@ -829,17 +694,18 @@ public class MagicStack extends MyObservable { final Card source = sa.getSourceCard(); curResolvingCard = source; - if (this.hasFizzled(sa, source, false)) { // Fizzle + + boolean thisHasFizzled = this.hasFizzled(sa, source, false); + String messageForLog = thisHasFizzled ? source.getName() + " ability fizzles." : sa.getStackDescription(); + game.getGameLog().add("ResolveStack", messageForLog, 2); + if (thisHasFizzled) { // Fizzle // TODO: Spell fizzles, what's the best way to alert player? Log.debug(source.getName() + " ability fizzles."); - game.getGameLog().add("ResolveStack", source.getName() + " ability fizzles.", 2); this.finishResolving(sa, true); } else if (sa.getApi() != null) { - game.getGameLog().add("ResolveStack", sa.getStackDescription(), 2); AbilityUtils.handleRemembering(sa); AbilityUtils.resolve(sa, true); } else { - game.getGameLog().add("ResolveStack", sa.getStackDescription(), 2); sa.resolve(); this.finishResolving(sa, false); // do creatures ETB from here? @@ -867,7 +733,7 @@ public class MagicStack extends MyObservable { if (creats.size() != 0) { haunterDiesWork.setDescription(""); - final Input target = new Input() { + final InputBase target = new InputBase() { private static final long serialVersionUID = 1981791992623774490L; @Override @@ -1290,26 +1156,18 @@ public class MagicStack extends MyObservable { ComputerUtil.playStack(sa, (AIPlayer) activePlayer, game); } } else { - // If only one, just add as necessary - if (activePlayerSAs.size() == 1) { - SpellAbility next = activePlayerSAs.get(0); + List orderedSAs = activePlayerSAs; + if (activePlayerSAs.size() > 1) { // give a dual list form to create instead of needing to do it one at a time + orderedSAs = GuiChoose.order("Select order for Simultaneous Spell Abilities", "Resolve first", 0, activePlayerSAs, null, null); + } + int size = orderedSAs.size(); + for (int i = size - 1; i >= 0; i--) { + SpellAbility next = orderedSAs.get(i); if (next.isTrigger()) { game.getActionPlay().playSpellAbility(next, activePlayer); } else { this.add(next); } - } else { - // Otherwise, gave a dual list form to create instead of needing to do it one at a time - List orderedSAs = GuiChoose.order("Select order for Simultaneous Spell Abilities", "Resolve first", 0, activePlayerSAs, null, null); - int size = orderedSAs.size(); - for (int i = size - 1; i >= 0; i--) { - SpellAbility next = orderedSAs.get(i); - if (next.isTrigger()) { - game.getActionPlay().playSpellAbility(next, activePlayer); - } else { - this.add(next); - } - } } } diff --git a/src/main/java/forge/gui/GuiChoose.java b/src/main/java/forge/gui/GuiChoose.java index 7009f07346c..714165b3d88 100644 --- a/src/main/java/forge/gui/GuiChoose.java +++ b/src/main/java/forge/gui/GuiChoose.java @@ -94,7 +94,7 @@ public class GuiChoose { } public static List getChoices(final String message, final int min, final int max, final Collection choices) { - if (null == choices || 0 == choices.size()) { + if (null == choices || choices.isEmpty()) { if (0 == min) { return new ArrayList(); } else { 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/InputProxy.java b/src/main/java/forge/gui/InputProxy.java index cef0ac2573b..f56bc619f10 100644 --- a/src/main/java/forge/gui/InputProxy.java +++ b/src/main/java/forge/gui/InputProxy.java @@ -21,10 +21,12 @@ import java.util.Observable; import java.util.Observer; import forge.Card; -import forge.Singletons; +import forge.FThreads; import forge.control.input.Input; +import forge.game.GameState; +import forge.game.MatchController; +import forge.game.phase.PhaseHandler; import forge.game.player.Player; -import forge.view.ButtonUtil; /** *

@@ -38,36 +40,48 @@ public class InputProxy implements Observer { /** The input. */ private Input input; - private boolean valid = false; + private MatchController match = null; + public void setMatch(MatchController matchController) { + match = matchController; + } + @Override public final synchronized void update(final Observable observable, final Object obj) { - ButtonUtil.disableAll(); - valid = false; + this.input = null; + final GameState game = match.getCurrentGame(); + final PhaseHandler ph = game.getPhaseHandler(); - Singletons.getModel().getMatch().getInput().setNewInput(Singletons.getModel().getGame()); + //System.out.print((FThreads.isEDT() ? "EDT > " : "TRD > ") + ph.debugPrintState()); + if ( match.getInput().isEmpty() && ph.hasPhaseEffects()) { + //System.out.println(" handle begin phase"); + FThreads.invokeInNewThread(new Runnable() { + @Override public void run() { + ph.handleBeginPhase(); + update(observable, obj); + } + }, true); + return; + } + + final Input nextInput = match.getInput().getActualInput(game); + //System.out.printf(" input is %s \t stack = %s%n", nextInput == null ? "null" : nextInput.getClass().getSimpleName(), match.getInput().printInputStack()); + + if (nextInput != null) { + this.input = nextInput; + FThreads.invokeInEDT(new Runnable() { @Override public void run() { nextInput.showMessage(); } }); + } else if (!ph.isPlayerPriorityAllowed()) { + ph.getPriorityPlayer().getController().passPriority(); + } } - /** - *

- * Setter for the field input. - *

- * - * @param in - * a {@link forge.control.input.Input} object. - */ - public final synchronized void setInput(final Input in) { - valid = true; - this.input = in; - this.input.showMessage(); // this call may invalidate the input by the time it returns - } - /** *

* selectButtonOK. *

*/ public final void selectButtonOK() { - this.getInput().selectButtonOK(); + if ( null == input ) return; + input.selectButtonOK(); } /** @@ -76,7 +90,8 @@ public class InputProxy implements Observer { *

*/ public final void selectButtonCancel() { - this.getInput().selectButtonCancel(); + if ( null == input ) return; + input.selectButtonCancel(); } /** @@ -88,7 +103,8 @@ public class InputProxy implements Observer { * a {@link forge.game.player.Player} object. */ public final void selectPlayer(final Player player) { - this.getInput().selectPlayer(player); + if ( null == input ) return; + input.selectPlayer(player); } /** @@ -102,22 +118,19 @@ public class InputProxy implements Observer { * a {@link forge.game.zone.PlayerZone} object. */ public final void selectCard(final Card card) { - this.getInput().selectCard(card); + if ( null == input ) return; + input.selectCard(card); } /** {@inheritDoc} */ @Override public final String toString() { - return this.getInput().toString(); + if ( null == input ) return "(null)"; + return this.input.toString(); } - /** @return {@link forge.gui.InputProxy.Input} */ + /** @return {@link forge.gui.InputProxy.InputBase} */ public Input getInput() { return this.input; } - - - public synchronized boolean isValid() { - return valid; - } } diff --git a/src/main/java/forge/gui/match/CMatchUI.java b/src/main/java/forge/gui/match/CMatchUI.java index 9691b220ff0..745952db40a 100644 --- a/src/main/java/forge/gui/match/CMatchUI.java +++ b/src/main/java/forge/gui/match/CMatchUI.java @@ -23,8 +23,8 @@ import java.util.List; import java.util.Map; import javax.swing.ImageIcon; - import forge.Card; +import forge.FThreads; import forge.GameEntity; import forge.ImageCache; import forge.Singletons; @@ -181,7 +181,8 @@ public enum CMatchUI { * @param blockers   {@link forge.CardList} * @param damage   int */ - public Map getDamageToAssign(final Card attacker, final List blockers, final int damage, GameEntity defender) { + @SuppressWarnings("unchecked") + public Map getDamageToAssign(final Card attacker, final List blockers, final int damage, final GameEntity defender) { if (damage <= 0) { return new HashMap(); } @@ -194,8 +195,15 @@ public enum CMatchUI { return res; } - VAssignDamage v = new VAssignDamage(attacker, blockers, damage, defender); - return v.getDamageMap(); + final Object[] result = { null }; // how else can I extract a value from EDT thread? + FThreads.invokeInEDTAndWait(new Runnable() { + @Override + public void run() { + // TODO Auto-generated method stub + VAssignDamage v = new VAssignDamage(attacker, blockers, damage, defender); + result[0] = v.getDamageMap(); + }}); + return (Map)result[0]; } /** diff --git a/src/main/java/forge/gui/match/nonsingleton/CField.java b/src/main/java/forge/gui/match/nonsingleton/CField.java index 93dbc735ce2..6b3a0561f9e 100644 --- a/src/main/java/forge/gui/match/nonsingleton/CField.java +++ b/src/main/java/forge/gui/match/nonsingleton/CField.java @@ -32,6 +32,7 @@ import forge.Card; import forge.CardCharacteristicName; import forge.Command; import forge.Constant; +import forge.FThreads; import forge.Constant.Preferences; import forge.Singletons; import forge.card.cardfactory.CardFactory; @@ -41,10 +42,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; import forge.game.zone.PlayerZone; @@ -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. @@ -144,6 +142,7 @@ public class CField implements ICDoc { private final Observer observerPlay = new Observer() { @Override public void update(final Observable a, final Object b) { + //FThreads.checkEDT("observerPlay.update", true); CField.this.view.getTabletop().setupPlayZone(); } }; @@ -344,10 +343,11 @@ public class CField implements ICDoc { if ( CField.this.player != CField.this.playerViewer ) return; - final GameState game = Singletons.getModel().getGame(); - SpellAbility ab = player.getController().getAbilityToPlay(game.getAbilitesOfCard(c, player)); + final SpellAbility ab = player.getController().getAbilityToPlay(player.getGame().getAbilitesOfCard(c, player)); if ( null != ab) { - player.playSpellAbility(c, ab); + FThreads.invokeInNewThread(new Runnable(){ @Override public void run(){ + player.playSpellAbility(c, ab); + }}); } } }.actionPerformed(null); @@ -390,54 +390,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 if ( input != null ){ + //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); + } + } /** */ diff --git a/src/main/java/forge/gui/toolbox/FSkin.java b/src/main/java/forge/gui/toolbox/FSkin.java index 88f723e3f15..50c3273e930 100644 --- a/src/main/java/forge/gui/toolbox/FSkin.java +++ b/src/main/java/forge/gui/toolbox/FSkin.java @@ -458,6 +458,26 @@ public enum FSkin { } } } + + public static void setProgessBarMessage(final String message) { + setProgessBarMessage(message, 0); + } + public static void setProgessBarMessage(final String message, final int cnt) { + final FProgressBar barProgress = FView.SINGLETON_INSTANCE.getSplash().getProgressBar(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if ( cnt > 0 ) { + barProgress.reset(); + barProgress.setMaximum(4); + } + barProgress.setShowETA(false); + barProgress.setShowCount(cnt > 0); + barProgress.setDescription(message); + + } + }); + } /** * Loads two sprites: the default (which should be a complete @@ -487,19 +507,12 @@ public enum FSkin { if (FSkin.preferredName.isEmpty()) { FSkin.loadLight("default"); } // Everything OK? + final FProgressBar barProgress = FView.SINGLETON_INSTANCE.getSplash().getProgressBar(); + setProgessBarMessage("Processing image sprites: ", 4); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - barProgress.reset(); - barProgress.setShowETA(false); - barProgress.setDescription("Processing image sprites: "); - } - }); // Grab and test various sprite files. - barProgress.setMaximum(4); final File f1 = new File(DEFAULT_DIR + FILE_ICON_SPRITE); final File f2 = new File(preferredDir + FILE_ICON_SPRITE); final File f3 = new File(DEFAULT_DIR + FILE_FOIL_SPRITE); @@ -567,14 +580,7 @@ public enum FSkin { UIManager.put("Table.alternateRowColor", new Color(240, 240, 240)); // Images loaded; can start UI init. - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - barProgress.setShowETA(false); - barProgress.setShowCount(false); - barProgress.setDescription("Creating display components."); - } - }); + setProgessBarMessage("Creating display components."); // Clear references to buffered images FSkin.bimDefaultSprite.flush(); diff --git a/src/main/java/forge/model/FModel.java b/src/main/java/forge/model/FModel.java index 5a40205d8e5..9f381e38fc2 100644 --- a/src/main/java/forge/model/FModel.java +++ b/src/main/java/forge/model/FModel.java @@ -117,11 +117,12 @@ public enum FModel { } // initialize log file - final File logFile = new File(NewConstants.LOG_FILE); - final boolean deleteSucceeded = logFile.delete(); + File logFile = new File(NewConstants.LOG_FILE); - if (logFile.exists() && !deleteSucceeded && (logFile.length() != 0)) { - throw new IllegalStateException("Could not delete existing logFile:" + logFile.getAbsolutePath()); + int i = 0; + while (logFile.exists() && !logFile.delete()) { + String pathname = logFile.getPath().replaceAll("[0-9]{0,2}.log$", String.valueOf(i++) + ".log"); + logFile = new File(pathname); } try { @@ -400,6 +401,7 @@ public enum FModel { /** * TODO: Write javadoc for this method. * @param players + * @param input */ public GameState newGame(Iterable players, GameType type, final MatchController match0) { gameState = new GameState(players,type, match0); diff --git a/src/main/java/forge/util/ThreadUtil.java b/src/main/java/forge/util/ThreadUtil.java deleted file mode 100644 index 4abc7967ae9..00000000000 --- a/src/main/java/forge/util/ThreadUtil.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * The files in the directory "net/slightlymagic/braids" and in all subdirectories of it (the "Files") are - * Copyright 2011 Braids Cabal-Conjurer. They are available under either Forge's - * main license (the GNU Public License; see LICENSE.txt in Forge's top directory) - * or under the Apache License, as explained below. - * - * The Files are additionally licensed under the Apache License, Version 2.0 (the - * "Apache License"); you may not use the files in this directory except in - * compliance with one of its two licenses. You may obtain a copy of the Apache - * License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Apache License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Apache License for the specific language governing permissions and - * limitations under the Apache License. - * - */ -package forge.util; - -import java.lang.reflect.InvocationTargetException; - -import javax.swing.SwingUtilities; - -/** - * Some general-purpose functions. - */ -public final class ThreadUtil { - - /** - * Invoke the given Runnable in an Event Dispatch Thread and wait for it to - * finish; but try to use SwingUtilities.invokeLater instead whenever - * feasible. - * - * Exceptions generated by SwingUtilities.invokeAndWait (if used), are - * rethrown as RuntimeExceptions. - * - * @param proc - * the Runnable to run - * @see javax.swing.SwingUtilities#invokeLater(Runnable) - */ - public static void invokeInEventDispatchThreadAndWait(final Runnable proc) { - // by - // Braids - // on - // 8/18/11 - // 11:19 - // PM - if (SwingUtilities.isEventDispatchThread()) { - // Just run in the current thread. - proc.run(); - } else { - try { - SwingUtilities.invokeAndWait(proc); - } catch (final InterruptedException exn) { - throw new RuntimeException(exn); - // 11:19 PM - } catch (final InvocationTargetException exn) { - throw new RuntimeException(exn); - // 11:19 PM - } - } - } - - -} diff --git a/src/main/java/forge/view/arcane/PlayArea.java b/src/main/java/forge/view/arcane/PlayArea.java index 24fb2530a25..3c2648e55eb 100644 --- a/src/main/java/forge/view/arcane/PlayArea.java +++ b/src/main/java/forge/view/arcane/PlayArea.java @@ -24,10 +24,12 @@ import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.JScrollPane; import forge.Card; +import forge.FThreads; import forge.view.arcane.util.Animation; import forge.view.arcane.util.CardPanelMouseListener; @@ -497,7 +499,27 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen * @param newList * an array of {@link forge.Card} objects. */ + private final AtomicBoolean wantRedraw = new AtomicBoolean(false); public void setupPlayZone() { + boolean wasSet = wantRedraw.getAndSet(true); + if(wasSet) return; + FThreads.invokeInEDT(new Runnable() { + @Override + public void run() { + try { // user won't notice, but the requests coming in that interval won't trigger re-draw + Thread.sleep(20); + } catch (InterruptedException e) { + e.printStackTrace(); + } + wantRedraw.set(false); + final List modelshot = new ArrayList(model); // I am afraid of ConcurrentModificationExceptions + setupPlayZone(modelshot); + } + }); + } + + + public void setupPlayZone(final List model) { List oldCards, toDelete; oldCards = new ArrayList(); for (final CardPanel cpa : getCardPanels()) {