reintegration of input-sync

This commit is contained in:
Maxmtg
2013-03-27 18:36:15 +00:00
91 changed files with 2615 additions and 4185 deletions

19
.gitattributes vendored
View File

@@ -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/CostUntap.java -text
src/main/java/forge/card/cost/CostUntapType.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/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/cost/package-info.java svneol=native#text/plain
src/main/java/forge/card/mana/IParserManaCost.java -text src/main/java/forge/card/mana/IParserManaCost.java -text
src/main/java/forge/card/mana/Mana.java svneol=native#text/plain 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/RestartUtil.java -text
src/main/java/forge/control/bazaar/ControlStall.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/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/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/InputBlock.java svneol=native#text/plain
src/main/java/forge/control/input/InputCleanup.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/InputControl.java svneol=native#text/plain
src/main/java/forge/control/input/InputLockUI.java -text 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/InputMulligan.java svneol=native#text/plain
src/main/java/forge/control/input/InputPassPriority.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/InputPayManaBase.java -text
src/main/java/forge/control/input/InputPayManaExecuteCommands.java svneol=native#text/plain 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/InputPayManaOfCostPayment.java -text
src/main/java/forge/control/input/InputPayManaSimple.java svneol=native#text/plain 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/InputPayManaX.java -text
src/main/java/forge/control/input/InputPayReturnCost.java -text src/main/java/forge/control/input/InputPayment.java -text
src/main/java/forge/control/input/InputPaySacCost.java -text src/main/java/forge/control/input/InputSelectCards.java -text
src/main/java/forge/control/input/InputSelectMany.java -text src/main/java/forge/control/input/InputSelectCardsFromList.java -text
src/main/java/forge/control/input/InputSelectManyCards.java -text src/main/java/forge/control/input/InputSelectList.java -text
src/main/java/forge/control/input/InputSelectManyPlayers.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/input/package-info.java svneol=native#text/plain
src/main/java/forge/control/package-info.java -text src/main/java/forge/control/package-info.java -text
src/main/java/forge/deck/CardCollections.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/GameActionUtil.java svneol=native#text/plain
src/main/java/forge/game/GameEndReason.java -text src/main/java/forge/game/GameEndReason.java -text
src/main/java/forge/game/GameFormat.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/GameLossReason.java -text
src/main/java/forge/game/GameNew.java -text src/main/java/forge/game/GameNew.java -text
src/main/java/forge/game/GameOutcome.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/PredicateString.java -text
src/main/java/forge/util/ReflectionUtil.java -text src/main/java/forge/util/ReflectionUtil.java -text
src/main/java/forge/util/TextUtil.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/XmlUtil.java -text
src/main/java/forge/util/package-info.java -text src/main/java/forge/util/package-info.java -text
src/main/java/forge/util/storage/IStorage.java -text src/main/java/forge/util/storage/IStorage.java -text

8
.gitignore vendored
View File

@@ -8,6 +8,14 @@
/pom.xml.releaseBackup /pom.xml.releaseBackup
/pom.xml.tag /pom.xml.tag
/release.properties /release.properties
res/*.log
res/PerSetTrackingResults
res/cardsfolder/*.bat
res/decks
res/layouts
res/pics*
res/pics_product
/target /target
/test-output
tools/PerSetTrackingResults tools/PerSetTrackingResults
tools/oracleScript.log tools/oracleScript.log

View File

@@ -3,25 +3,28 @@ package forge;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import forge.control.input.InputLockUI; import forge.control.input.InputSynchronized;
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
* *
*/ */
public class FThreads { public class FThreads {
static { static {
System.out.printf("(FThreads static ctor): Running on a machine with %d cpu core(s)%n", Runtime.getRuntime().availableProcessors() ); 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(); private final static ExecutorService threadPool = Executors.newCachedThreadPool();
public static ExecutorService getCachedPool() { private static ExecutorService getCachedPool() { return threadPool; }
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 // 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) { public final static ExecutorService getComputingPool(float loadFactor) {
@@ -83,27 +86,42 @@ public class FThreads {
} }
public static void invokeInNewThread(Runnable proc) { public static void invokeInNewThread(Runnable toRun) {
invokeInNewThread(proc, false); getCachedPool().execute(toRun);
} }
private final static InputLockUI inpuptLock = new InputLockUI();
public static void invokeInNewThread(final Runnable proc, boolean lockUI) { public static void invokeInNewThread(final Runnable proc, boolean lockUI) {
Runnable toRun = proc; Runnable toRun = proc;
if( lockUI ) { if( lockUI ) {
// checkEDT("FThreads.invokeInNewthread", true) // checkEDT("FThreads.invokeInNewthread", true)
Singletons.getModel().getMatch().getInput().setInput(inpuptLock); Singletons.getModel().getMatch().getInput().lock();
toRun = new Runnable() { toRun = new Runnable() {
@Override @Override
public void run() { public void run() {
proc.run(); proc.run();
// may try special unlock method here Singletons.getModel().getMatch().getInput().unlock();
Singletons.getModel().getMatch().getInput().resetInput();
} }
}; };
} }
invokeInNewThread(toRun);
}
getCachedPool().execute(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);
} }
} }

View File

@@ -10,7 +10,6 @@ import org.apache.commons.lang3.StringUtils;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardUtil; import forge.CardUtil;
import forge.Command;
import forge.Constant; import forge.Constant;
import forge.CounterType; import forge.CounterType;
import forge.Singletons; import forge.Singletons;
@@ -1036,6 +1035,19 @@ public class AbilityUtils {
AbilityUtils.resolveApiAbility(abSub, usedStack, game); 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) { private static void handleUnlessCost(final SpellAbility sa, final boolean usedStack, final GameState game) {
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
String unlessCost = sa.getParam("UnlessCost"); String unlessCost = sa.getParam("UnlessCost");
@@ -1047,6 +1059,7 @@ public class AbilityUtils {
final String resolveSubs = sa.getParam("UnlessResolveSubs"); // no value means 'Always' final String resolveSubs = sa.getParam("UnlessResolveSubs"); // no value means 'Always'
final boolean execSubsWhenPaid = "WhenPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs); final boolean execSubsWhenPaid = "WhenPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs);
final boolean execSubsWhenNotPaid = "WhenNotPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs); final boolean execSubsWhenNotPaid = "WhenNotPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs);
final boolean isSwitched = sa.hasParam("UnlessSwitched");
// The cost // The cost
if (unlessCost.equals("CardManaCost")) { if (unlessCost.equals("CardManaCost")) {
@@ -1070,43 +1083,6 @@ public class AbilityUtils {
//instead of just X for cards like Draco. //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 Cost cost = new Cost(source, unlessCost, true);
final Ability ability = new AbilityStatic(source, cost, null) { final Ability ability = new AbilityStatic(source, cost, null) {
@Override @Override
@@ -1117,34 +1093,35 @@ public class AbilityUtils {
boolean paid = false; boolean paid = false;
for (Player payer : payers) { for (Player payer : payers) {
if (payer.isComputer()) {
ability.setActivatingPlayer(payer); ability.setActivatingPlayer(payer);
if (AbilityUtils.willAIPayForAbility(sa, payer, ability, paid, payers)) {
ability.setTarget(sa.getTarget()); ability.setTarget(sa.getTarget());
if (payer.isComputer()) {
if (AbilityUtils.willAIPayForAbility(sa, payer, ability, paid, payers)) {
ComputerUtil.playNoStack((AIPlayer) payer, ability, game); // Unless cost was payed - no resolve ComputerUtil.playNoStack((AIPlayer) payer, ability, game); // Unless cost was payed - no resolve
paid = true; paid = true;
} }
} } else {
}
boolean waitForInput = false;
for (Player payer : payers) {
if (payer.isHuman()) {
// if it's paid by the AI already the human can pay, but it won't change anything // if it's paid by the AI already the human can pay, but it won't change anything
if (paid) { paid |= GameActionUtil.payCostDuringAbilityResolve(payer, ability, cost, sa, game);
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
}
}
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; 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);
}
/** /**
* <p> * <p>
* Parse non-mana X variables. * Parse non-mana X variables.

View File

@@ -26,7 +26,6 @@ import forge.card.cost.CostDiscard;
import forge.card.cost.CostPart; import forge.card.cost.CostPart;
import forge.card.spellability.AbilitySub; import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellPermanent;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.game.GlobalRuleChange; import forge.game.GlobalRuleChange;

View File

@@ -19,7 +19,6 @@
package forge.card.ability.ai; package forge.card.ability.ai;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Random; import java.util.Random;
import forge.Card; import forge.Card;
@@ -72,8 +71,7 @@ public class DrawAi extends SpellAbilityAi {
if (part instanceof CostDiscard) { if (part instanceof CostDiscard) {
CostDiscard cd = (CostDiscard) part; CostDiscard cd = (CostDiscard) part;
cd.decideAIPayment((AIPlayer) ai, sa, sa.getSourceCard(), null); cd.decideAIPayment((AIPlayer) ai, sa, sa.getSourceCard(), null);
List<Card> discards = cd.getList(); for (Card discard : cd.getList()) {
for (Card discard : discards) {
if (!ComputerUtil.isWorseThanDraw(ai, discard)) { if (!ComputerUtil.isWorseThanDraw(ai, discard)) {
return false; return false;
} }

View File

@@ -14,7 +14,7 @@ import forge.Singletons;
import forge.card.ability.SpellAbilityEffect; import forge.card.ability.SpellAbilityEffect;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.InputBase;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
@@ -45,7 +45,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
private static void resolveHuman(final SpellAbility sa) { private static void resolveHuman(final SpellAbility sa) {
final List<Card> unchosen = Lists.newArrayList(Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield)); final List<Card> unchosen = Lists.newArrayList(Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield));
final List<Player> players = new ArrayList<Player>(Singletons.getModel().getGame().getPlayers()); final List<Player> players = new ArrayList<Player>(Singletons.getModel().getGame().getPlayers());
Singletons.getModel().getMatch().getInput().setInput(new Input() { Singletons.getModel().getMatch().getInput().setInput(new InputBase() {
private static final long serialVersionUID = -1779224307654698954L; private static final long serialVersionUID = -1779224307654698954L;
@Override @Override

View File

@@ -29,6 +29,7 @@ import com.google.common.collect.Iterables;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.FThreads;
import forge.CardPredicates.Presets; import forge.CardPredicates.Presets;
import forge.Command; import forge.Command;
import forge.CounterType; import forge.CounterType;
@@ -44,8 +45,7 @@ import forge.card.spellability.SpellPermanent;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.card.trigger.Trigger; import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerHandler; import forge.card.trigger.TriggerHandler;
import forge.control.input.Input; import forge.control.input.InputSelectCards;
import forge.control.input.InputSelectManyCards;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCombat; import forge.game.ai.ComputerUtilCombat;
import forge.game.event.TokenCreatedEvent; import forge.game.event.TokenCreatedEvent;
@@ -477,43 +477,27 @@ public class CardFactoryCreatures {
private static void getCard_PhyrexianDreadnought(final Card card, final String cardName) { private static void getCard_PhyrexianDreadnought(final Card card, final String cardName) {
final Player player = card.getController(); final Player player = card.getController();
final Input target = new InputSelectManyCards(0, Integer.MAX_VALUE) { 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; private static final long serialVersionUID = 2698036349873486664L;
@Override @Override
public String getMessage() { public String getMessage() {
String toDisplay = cardName + " - Select any number of creatures to sacrifice. "; String toDisplay = cardName + " - Select any number of creatures to sacrifice. ";
toDisplay += "Currently, (" + selected.size() + ") selected with a total power of: " toDisplay += "Currently, (" + selected.size() + ") selected with a total power of: " + getTotalPower();
+ getTotalPower();
toDisplay += " Click OK when Done."; toDisplay += " Click OK when Done.";
return toDisplay; return toDisplay;
} }
@Override
protected boolean canCancelWithSomethingSelected() {
return true;
}
@Override
protected Input onCancel() {
Singletons.getModel().getGame().getAction().sacrifice(card, null);
return null;
}
@Override @Override
protected boolean isValidChoice(Card c) { protected boolean isValidChoice(Card c) {
Zone zone = Singletons.getModel().getGame().getZoneOf(c); Zone zone = Singletons.getModel().getGame().getZoneOf(c);
return c.isCreature() && zone.is(ZoneType.Battlefield, player); return c.isCreature() && zone.is(ZoneType.Battlefield, player);
} // selectCard() } // selectCard()
@Override
protected Input onDone() {
for (final Card sac : selected) {
Singletons.getModel().getGame().getAction().sacrifice(sac, null);
}
return null;
}
@Override @Override
protected boolean hasEnoughTargets() { protected boolean hasEnoughTargets() {
return getTotalPower() >= 12; return getTotalPower() >= 12;
@@ -528,10 +512,16 @@ public class CardFactoryCreatures {
} }
}; // Input }; // Input
final Ability sacOrSac = new Ability(card, ManaCost.NO_COST) { target.setCancelWithSelectedAllowed(true);
@Override FThreads.setInputAndWait(target);
public void resolve() { if(!target.hasCancelled()) {
if (player.isHuman()) { 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); Singletons.getModel().getMatch().getInput().setInput(target);
} }
} // end resolve } // end resolve

View File

@@ -24,9 +24,9 @@ import javax.swing.JOptionPane;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.Command; import forge.Command;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.control.input.Input; import forge.control.input.InputSelectCards;
import forge.control.input.InputSelectManyCards;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -46,7 +46,7 @@ class CardFactoryLands {
* TODO: Write javadoc for this type. * 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 String type;
private final Card card; private final Card card;
private static final long serialVersionUID = -2774066137824255680L; private static final long serialVersionUID = -2774066137824255680L;
@@ -74,23 +74,6 @@ class CardFactoryLands {
Zone zone = Singletons.getModel().getGame().getZoneOf(c); Zone zone = Singletons.getModel().getGame().getZoneOf(c);
return zone.is(ZoneType.Hand) && c.isType(type) && c.getController() == card.getController(); 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() { 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() } // execute()
private void revealCard(final Card c) { private void revealCard(final Card c) {

View File

@@ -30,12 +30,13 @@ import com.google.common.collect.Iterables;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.FThreads;
import forge.CardPredicates.Presets; import forge.CardPredicates.Presets;
import forge.Command;
import forge.Constant; import forge.Constant;
import forge.Singletons; import forge.Singletons;
import forge.card.CardType; import forge.card.CardType;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.mana.ManaCost;
import forge.card.spellability.Spell; import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.InputPayManaExecuteCommands; import forge.control.input.InputPayManaExecuteCommands;
@@ -460,22 +461,13 @@ public class CardFactorySorceries {
if (newCMC <= baseCMC) { if (newCMC <= baseCMC) {
game.getAction().moveToPlay(newArtifact[0]); game.getAction().moveToPlay(newArtifact[0]);
} else { } else {
final String diffCost = String.valueOf(newCMC - baseCMC); final int diffCost = newCMC - baseCMC;
Singletons.getModel().getMatch().getInput().setInput(new InputPayManaExecuteCommands(game, "Pay difference in artifacts CMC", diffCost, new Command() { InputPayManaExecuteCommands inp = new InputPayManaExecuteCommands(game, "Pay difference in artifacts CMC", ManaCost.get(diffCost));
private static final long serialVersionUID = -8729850321341068049L; FThreads.setInputAndWait(inp);
if ( inp.isPaid() )
@Override game.getAction().moveToPlay(newArtifact[0]);
public void execute() { else
Singletons.getModel().getGame().getAction().moveToPlay(newArtifact[0]); game.getAction().moveToGraveyard(newArtifact[0]);
}
}, new Command() {
private static final long serialVersionUID = -246036834856971935L;
@Override
public void execute() {
Singletons.getModel().getGame().getAction().moveToGraveyard(newArtifact[0]);
}
}));
} }
// finally, shuffle library // finally, shuffle library

View File

@@ -38,6 +38,7 @@ import forge.CardUtil;
import forge.Command; import forge.Command;
import forge.Constant; import forge.Constant;
import forge.CounterType; import forge.CounterType;
import forge.FThreads;
import forge.GameEntity; import forge.GameEntity;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityFactory;
@@ -63,7 +64,8 @@ import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerHandler; import forge.card.trigger.TriggerHandler;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.control.input.Input; 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.GameState;
import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
@@ -98,10 +100,10 @@ public class CardFactoryUtil {
* a {@link forge.CardList} object. * a {@link forge.CardList} object.
* @param message * @param message
* a {@link java.lang.String} object. * 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<Card> choices, final String message) { public static InputBase inputDestroyNoRegeneration(final List<Card> choices, final String message) {
final Input target = new Input() { final InputBase target = new InputBase() {
private static final long serialVersionUID = -6637588517573573232L; private static final long serialVersionUID = -6637588517573573232L;
@Override @Override
@@ -549,11 +551,11 @@ public class CardFactoryUtil {
* a {@link forge.CardList} object. * a {@link forge.CardList} object.
* @param paid * @param paid
* a {@link forge.Command} object. * 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<Card> choices, public static InputBase masterOfTheWildHuntInputTargetCreature(final SpellAbility spell, final List<Card> choices,
final Command paid) { final Command paid) {
final Input target = new Input() { final InputBase target = new InputBase() {
private static final long serialVersionUID = -1779224307654698954L; private static final long serialVersionUID = -1779224307654698954L;
@Override @Override
@@ -593,10 +595,10 @@ public class CardFactoryUtil {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
* @param card * @param card
* a {@link forge.Card} object. * 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) { public static InputBase modularInput(final SpellAbility ability, final Card card) {
final Input modularInput = new Input() { final InputBase modularInput = new InputBase() {
private static final long serialVersionUID = 2322926875771867901L; private static final long serialVersionUID = 2322926875771867901L;
@@ -2302,10 +2304,10 @@ public class CardFactoryUtil {
* a int. * a int.
* @param type * @param type
* a {@link java.lang.String} object. * 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) { 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 static final long serialVersionUID = -2167059918040912025L;
private final int stop = n; private final int stop = n;
@@ -3151,27 +3153,6 @@ public class CardFactoryUtil {
}; };
haunterDiesWork.setDescription(hauntDescription); 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) { final Ability haunterDiesSetup = new Ability(card, ManaCost.ZERO) {
@Override @Override
public void resolve() { public void resolve() {
@@ -3189,7 +3170,25 @@ public class CardFactoryUtil {
// need to do it this way because I don't know quite how to // need to do it this way because I don't know quite how to
// make TriggerHandler respect BeforePayMana. // make TriggerHandler respect BeforePayMana.
if (card.getController().isHuman()) { 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 { } else {
// AI choosing what to haunt // AI choosing what to haunt
final List<Card> oppCreats = CardLists.filterControlledBy(creats, card.getController().getOpponent()); final List<Card> oppCreats = CardLists.filterControlledBy(creats, card.getController().getOpponent());

View File

@@ -103,10 +103,11 @@ public class CostDamage extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 String amount = this.getAmount();
final Player activator = ability.getActivatingPlayer(); final Player activator = ability.getActivatingPlayer();
final int life = activator.getLife(); final int life = activator.getLife();
final Card source = ability.getSourceCard();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
@@ -124,11 +125,7 @@ public class CostDamage extends CostPart {
if (GuiDialog.confirm(source, sb.toString()) && activator.canPayLife(c)) { if (GuiDialog.confirm(source, sb.toString()) && activator.canPayLife(c)) {
activator.addDamage(c, source); activator.addDamage(c, source);
this.setLastPaidAmount(c);
payment.setPaidManaPart(this);
} else { } else {
payment.setCancel(true);
payment.getRequirements().finishPaying();
return false; return false;
} }
return true; return true;

View File

@@ -19,23 +19,20 @@ package forge.card.cost;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.Singletons; import forge.FThreads;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; 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.GameState;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
/** /**
* The Class CostDiscard. * The Class CostDiscard.
@@ -43,6 +40,8 @@ import forge.view.ButtonUtil;
public class CostDiscard extends CostPartWithList { public class CostDiscard extends CostPartWithList {
// Discard<Num/Type{/TypeDescription}> // Discard<Num/Type{/TypeDescription}>
// Inputs
/** /**
* Instantiates a new cost discard. * Instantiates a new cost discard.
* *
@@ -155,7 +154,7 @@ public class CostDiscard extends CostPartWithList {
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { 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()) { 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) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 Player activator = ability.getActivatingPlayer();
final Card source = ability.getSourceCard();
List<Card> handList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Hand)); List<Card> handList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Hand));
String discType = this.getType(); String discardType = this.getType();
final String amount = this.getAmount(); final String amount = this.getAmount();
this.resetList(); this.resetList();
@@ -178,25 +178,24 @@ public class CostDiscard extends CostPartWithList {
if (!handList.contains(source)) { if (!handList.contains(source)) {
return false; return false;
} }
activator.discard(source, ability); executePayment(ability, source);
payment.setPaidManaPart(this); return true;
//this.addToList(source); //this.addToList(source);
} else if (discType.equals("Hand")) { } else if (discardType.equals("Hand")) {
this.setList(handList); this.setList(handList);
activator.discardHand(ability); activator.discardHand(ability);
payment.setPaidManaPart(this); return true;
} else if (discType.equals("LastDrawn")) { } else if (discardType.equals("LastDrawn")) {
final Card lastDrawn = activator.getLastDrawnCard(); final Card lastDrawn = activator.getLastDrawnCard();
this.addToList(lastDrawn);
if (!handList.contains(lastDrawn)) { if (!handList.contains(lastDrawn)) {
return false; return false;
} }
activator.discard(lastDrawn, ability); executePayment(ability, lastDrawn);
payment.setPaidManaPart(this); return true;
} else { } else {
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (discType.equals("Random")) { if (discardType.equals("Random")) {
if (c == null) { if (c == null) {
final String sVar = ability.getSVar(amount); final String sVar = ability.getSVar(amount);
// Generalize this // Generalize this
@@ -208,9 +207,9 @@ public class CostDiscard extends CostPartWithList {
} }
this.setList(activator.discardRandom(c, ability)); this.setList(activator.discardRandom(c, ability));
payment.setPaidManaPart(this); return true;
} else { } else {
String type = new String(discType); String type = new String(discardType);
boolean sameName = false; boolean sameName = false;
if (type.contains("+WithSameName")) { if (type.contains("+WithSameName")) {
sameName = true; sameName = true;
@@ -243,14 +242,18 @@ public class CostDiscard extends CostPartWithList {
} }
} }
final Input inp = CostDiscard.inputDiscardCost(discType, handList, ability, payment, this, c); InputSelectCards inp = new InputSelectCardsFromList(c, c, handList);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); 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; return false;
} for(Card crd : inp.getSelected())
} executePayment(ability, crd);
this.addListToHash(ability, "Discarded");
return true; return true;
} }
}
}
/* /*
* (non-Javadoc) * (non-Javadoc)
@@ -306,109 +309,23 @@ public class CostDiscard extends CostPartWithList {
return this.getList() != null; 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 // Inputs
/**
* <p>
* input_discardCost.
* </p>
*
* @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<Card> 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<Card> 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()
} }

View File

@@ -20,19 +20,20 @@ package forge.card.cost;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance; import forge.card.spellability.SpellAbilityStackInstance;
import forge.control.input.Input; import forge.control.input.InputPayment;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.MagicStack;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
import forge.gui.GuiDialog; import forge.gui.GuiDialog;
@@ -49,19 +50,15 @@ public class CostExile extends CostPartWithList {
// ExileFromTop<Num/Type{/TypeDescription}> (of library) // ExileFromTop<Num/Type{/TypeDescription}> (of library)
// ExileSameGrave<Num/Type{/TypeDescription}> // ExileSameGrave<Num/Type{/TypeDescription}>
/** private static final class InputExileFrom extends InputPayCostBase {
* TODO: Write javadoc for this type.
*
*/
private static final class InputExileFrom extends Input {
private final SpellAbility sa; private final SpellAbility sa;
private final String type; private final String type;
private final int nNeeded; private final int nNeeded;
private final CostPayment payment;
private final CostExile part; private final CostExile part;
private static final long serialVersionUID = 734256837615635021L; private static final long serialVersionUID = 734256837615635021L;
private List<Card> typeList; private List<Card> typeList;
/** /**
* TODO: Write javadoc for Constructor. * TODO: Write javadoc for Constructor.
* @param sa * @param sa
@@ -70,11 +67,10 @@ public class CostExile extends CostPartWithList {
* @param payment * @param payment
* @param part * @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.sa = sa;
this.type = type; this.type = type;
this.nNeeded = nNeeded; this.nNeeded = nNeeded;
this.payment = payment;
this.part = part; this.part = part;
} }
@@ -96,8 +92,7 @@ public class CostExile extends CostPartWithList {
if (c != null) { if (c != null) {
this.typeList.remove(c); this.typeList.remove(c);
part.addToList(c); part.executePayment(sa, c);
Singletons.getModel().getGame().getAction().exile(c);
if (i == (nNeeded - 1)) { if (i == (nNeeded - 1)) {
this.done(); 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. * TODO: Write javadoc for this type.
* *
*/ */
private static final class InputExileFromSame extends Input { private static final class InputExileFromSame extends InputPayCostBase {
private final List<Card> list; private final List<Card> list;
private final CostExile part; private final CostExile part;
private final CostPayment payment;
private final SpellAbility sa;
private final int nNeeded; private final int nNeeded;
private final List<Player> payableZone; private final List<Player> payableZone;
private static final long serialVersionUID = 734256837615635021L; private static final long serialVersionUID = 734256837615635021L;
@@ -148,12 +125,9 @@ public class CostExile extends CostPartWithList {
* @param nNeeded * @param nNeeded
* @param payableZone * @param payableZone
*/ */
private InputExileFromSame(List<Card> list, CostExile part, CostPayment payment, SpellAbility sa, int nNeeded, private InputExileFromSame(List<Card> list, CostExile part, int nNeeded, List<Player> payableZone) {
List<Player> payableZone) {
this.list = list; this.list = list;
this.part = part; this.part = part;
this.payment = payment;
this.sa = sa;
this.nNeeded = nNeeded; this.nNeeded = nNeeded;
this.payableZone = payableZone; this.payableZone = payableZone;
} }
@@ -185,8 +159,7 @@ public class CostExile extends CostPartWithList {
if (c != null) { if (c != null) {
this.typeList.remove(c); this.typeList.remove(c);
part.addToList(c); part.executePayment(null, c);
Singletons.getModel().getGame().getAction().exile(c);
if (i == (nNeeded - 1)) { if (i == (nNeeded - 1)) {
this.done(); 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. * TODO: Write javadoc for this type.
* *
*/ */
private static final class InputExileFromStack extends Input { private static final class InputExileFromStack extends InputPayCostBase {
private final CostPayment payment;
private final SpellAbility sa; private final SpellAbility sa;
private final String type; private final String type;
private final int nNeeded; private final int nNeeded;
@@ -236,8 +193,7 @@ public class CostExile extends CostPartWithList {
* @param nNeeded * @param nNeeded
* @param part * @param part
*/ */
private InputExileFromStack(CostPayment payment, SpellAbility sa, String type, int nNeeded, CostExile part) { private InputExileFromStack(SpellAbility sa, String type, int nNeeded, CostExile part) {
this.payment = payment;
this.sa = sa; this.sa = sa;
this.type = type; this.type = type;
this.nNeeded = nNeeded; this.nNeeded = nNeeded;
@@ -252,10 +208,11 @@ public class CostExile extends CostPartWithList {
saList = new ArrayList<SpellAbility>(); saList = new ArrayList<SpellAbility>();
descList = new ArrayList<String>(); descList = new ArrayList<String>();
final MagicStack stack = sa.getActivatingPlayer().getGame().getStack();
for (int i = 0; i < Singletons.getModel().getGame().getStack().size(); i++) { for (int i = 0; i < stack.size(); i++) {
final Card stC = Singletons.getModel().getGame().getStack().peekAbility(i).getSourceCard(); final Card stC = stack.peekAbility(i).getSourceCard();
final SpellAbility stSA = Singletons.getModel().getGame().getStack().peekAbility(i).getRootAbility(); final SpellAbility stSA = stack.peekAbility(i).getRootAbility();
if (stC.isValid(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()) && stSA.isSpell()) { if (stC.isValid(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()) && stSA.isSpell()) {
this.saList.add(stSA); this.saList.add(stSA);
if (stC.isCopiedSpell()) { if (stC.isCopiedSpell()) {
@@ -277,47 +234,33 @@ public class CostExile extends CostPartWithList {
if (o != null) { if (o != null) {
final SpellAbility toExile = this.saList.get(descList.indexOf(o)); final SpellAbility toExile = this.saList.get(descList.indexOf(o));
final Card c = toExile.getSourceCard(); final Card c = toExile.getSourceCard();
this.saList.remove(toExile); this.saList.remove(toExile);
part.addToList(c);
if (!c.isCopiedSpell()) { if (!c.isCopiedSpell()) {
Singletons.getModel().getGame().getAction().exile(c); part.executePayment(sa, c);
} } else
part.addToList(c);
if (i == (nNeeded - 1)) { if (i == (nNeeded - 1)) {
this.done(); this.done();
} }
final SpellAbilityStackInstance si = Singletons.getModel().getGame().getStack().getInstanceFromSpellAbility(toExile); final SpellAbilityStackInstance si = stack.getInstanceFromSpellAbility(toExile);
Singletons.getModel().getGame().getStack().remove(si); stack.remove(si);
} else { } else {
this.cancel(); this.cancel();
break; 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. * 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 CostExile part;
private final CostPayment payment;
private final String type; private final String type;
private final int nNeeded; private final int nNeeded;
private final SpellAbility sa; private final SpellAbility sa;
@@ -333,9 +276,8 @@ public class CostExile extends CostPartWithList {
* @param nNeeded * @param nNeeded
* @param sa * @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.part = part;
this.payment = payment;
this.type = type; this.type = type;
this.nNeeded = nNeeded; this.nNeeded = nNeeded;
this.sa = sa; this.sa = sa;
@@ -366,17 +308,11 @@ public class CostExile extends CostPartWithList {
ButtonUtil.enableOnlyCancel(); ButtonUtil.enableOnlyCancel();
} }
@Override
public void selectButtonCancel() {
this.cancel();
}
@Override @Override
public void selectCard(final Card card) { public void selectCard(final Card card) {
if (this.typeList.contains(card)) { if (this.typeList.contains(card)) {
this.nExiles++; this.nExiles++;
part.addToList(card); part.executePayment(sa, card);
Singletons.getModel().getGame().getAction().exile(card);
this.typeList.remove(card); this.typeList.remove(card);
// in case nothing else to exile // in case nothing else to exile
if (this.nExiles == nNeeded) { 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. * TODO: Write javadoc for this type.
* *
*/ */
private static final class InputExileThis extends Input { private static final class InputExileThis extends InputPayCostBase {
private final CostPayment payment;
private final CostExile part; private final CostExile part;
private final SpellAbility sa; private final SpellAbility sa;
private static final long serialVersionUID = 678668673002725001L; private static final long serialVersionUID = 678668673002725001L;
@@ -418,30 +342,22 @@ public class CostExile extends CostPartWithList {
* @param part * @param part
* @param sa * @param sa
*/ */
private InputExileThis(CostPayment payment, CostExile part, SpellAbility sa) { private InputExileThis(CostExile part, SpellAbility sa) {
this.payment = payment;
this.part = part; this.part = part;
this.sa = sa; this.sa = sa;
} }
@Override @Override
public void showMessage() { public void showMessage() {
final Card card = sa.getSourceCard(); final Card card = sa.getSourceCard();
if ( sa.getActivatingPlayer().getZone(part.getFrom()).contains(card)) { if ( sa.getActivatingPlayer().getZone(part.getFrom()).contains(card)) {
boolean choice = GuiDialog.confirm(card, card.getName() + " - Exile?"); boolean choice = GuiDialog.confirm(card, card.getName() + " - Exile?");
if (choice) { if (choice) {
payment.getAbility().addCostToHashList(card, "Exiled"); part.executePayment(sa, card);
Singletons.getModel().getGame().getAction().exile(card); done();
part.addToList(card); return;
this.stop();
part.addListToHash(sa, "Exiled");
payment.paidCost(part);
} else {
this.stop();
payment.cancelCost();
} }
} }
cancel();
} }
} }
@@ -619,8 +535,9 @@ public class CostExile extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 String amount = this.getAmount();
final Card source = ability.getSourceCard();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
final Player activator = ability.getActivatingPlayer(); final Player activator = ability.getActivatingPlayer();
List<Card> list; List<Card> list;
@@ -632,11 +549,10 @@ public class CostExile extends CostPartWithList {
} }
if (this.getType().equals("All")) { if (this.getType().equals("All")) {
this.setList(list);
for (final Card card : 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); list = CardLists.getValidCards(list, this.getType().split(";"), activator, source);
if (c == null) { if (c == null) {
@@ -651,16 +567,16 @@ public class CostExile extends CostPartWithList {
} }
} }
Input target = null; InputPayment target = null;
if (this.payCostFromSource()) { 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)) { } 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)) { } 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)) { } else if (this.from.equals(ZoneType.Library)) {
// this does not create input // this does not create input
CostExile.exileFromTop(ability, this, payment, c); return exileFromTop(ability, c);
} else if (this.sameZone) { } else if (this.sameZone) {
List<Player> players = game.getPlayers(); List<Player> players = game.getPlayers();
List<Player> payableZone = new ArrayList<Player>(); List<Player> payableZone = new ArrayList<Player>();
@@ -672,13 +588,13 @@ public class CostExile extends CostPartWithList {
payableZone.add(p); payableZone.add(p);
} }
} }
target = new InputExileFromSame(list, this, payment, ability, c, payableZone); target = new InputExileFromSame(list, this, c, payableZone);
} else { } else {
target = new InputExileFrom(ability, this.getType(), c, payment, this); target = new InputExileFrom(ability, this.getType(), c, this);
} }
if ( null != target ) FThreads.setInputAndWait(target);
Singletons.getModel().getMatch().getInput().setInputInterrupt(target); return target.isPaid();
return false;
} }
/* /*
@@ -741,30 +657,43 @@ public class CostExile extends CostPartWithList {
* @param nNeeded * @param nNeeded
* the n needed * the n needed
*/ */
public static void exileFromTop(final SpellAbility sa, final CostExile part, final CostPayment payment, public boolean exileFromTop(final SpellAbility sa, final int nNeeded) {
final int nNeeded) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append("Exile ").append(nNeeded).append(" cards from the top of your library?"); sb.append("Exile ").append(nNeeded).append(" cards from the top of your library?");
final List<Card> list = sa.getActivatingPlayer().getCardsIn(ZoneType.Library, nNeeded); final List<Card> list = sa.getActivatingPlayer().getCardsIn(ZoneType.Library, nNeeded);
if (list.size() > nNeeded) { if (list.size() > nNeeded) {
// I don't believe this is possible // I don't believe this is possible
payment.cancelCost(); return false;
return;
} }
final boolean doExile = GuiDialog.confirm(sa.getSourceCard(), sb.toString()); final boolean doExile = GuiDialog.confirm(sa.getSourceCard(), sb.toString());
if (doExile) { if (doExile) {
final Iterator<Card> itr = list.iterator(); final Iterator<Card> itr = list.iterator();
while (itr.hasNext()) { while (itr.hasNext()) {
final Card c = itr.next(); executePayment(sa, itr.next());
part.addToList(c);
Singletons.getModel().getGame().getAction().exile(c);
} }
part.addListToHash(sa, "Exiled"); return true;
payment.paidCost(part);
} else { } 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";
}
} }

View File

@@ -137,7 +137,8 @@ public class CostGainLife extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 String amount = this.getAmount();
final Player activator = ability.getActivatingPlayer(); final Player activator = ability.getActivatingPlayer();
final int life = activator.getLife(); 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 if(cntPlayers == Integer.MAX_VALUE) { // applied to all players who can gain
for(Player opp: oppsThatCanGainLife) for(Player opp: oppsThatCanGainLife)
opp.gainLife(c, null); opp.gainLife(c, null);
payment.setPaidManaPart(this);
return true;
} }
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
@@ -174,16 +173,12 @@ public class CostGainLife extends CostPart {
for(int playersLeft = cntPlayers; playersLeft > 0; playersLeft--) { for(int playersLeft = cntPlayers; playersLeft > 0; playersLeft--) {
final Player chosenToGain = GuiChoose.oneOrNone(sb.toString(), oppsThatCanGainLife); final Player chosenToGain = GuiChoose.oneOrNone(sb.toString(), oppsThatCanGainLife);
if (null == chosenToGain) { if (null == chosenToGain) {
payment.setCancel(true);
payment.getRequirements().finishPaying();
return false; return false;
} else { } else {
final Player chosen = chosenToGain; final Player chosen = chosenToGain;
chosen.gainLife(c, null); chosen.gainLife(c, null);
} }
} }
payment.setPaidManaPart(this);
return true; return true;
} }

View File

@@ -126,9 +126,10 @@ public class CostMill extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 String amount = this.getAmount();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
final Card source = ability.getSourceCard();
final Player activator = ability.getActivatingPlayer(); final Player activator = ability.getActivatingPlayer();
if (c == null) { if (c == null) {
@@ -144,29 +145,22 @@ public class CostMill extends CostPartWithList {
if ((list == null) || (list.size() > c)) { if ((list == null) || (list.size() > c)) {
// I don't believe this is possible // I don't believe this is possible
payment.cancelCost();
return false; return false;
} }
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append("Mill ").append(c).append(" cards from your library?"); sb.append("Mill ").append(c).append(" cards from your library?");
final boolean doMill = GuiDialog.confirm(source, sb.toString()); if ( false == GuiDialog.confirm(source, sb.toString()) )
if (doMill) { return false;
this.resetList(); this.resetList();
final Iterator<Card> itr = list.iterator(); final Iterator<Card> itr = list.iterator();
while (itr.hasNext()) { while (itr.hasNext()) {
final Card card = itr.next(); final Card card = itr.next();
this.addToList(card); executePayment(ability, card);
Singletons.getModel().getGame().getAction().moveToGraveyard(card);
}
this.addListToHash(ability, "Milled");
payment.paidCost(this);
return false;
} else {
payment.cancelCost();
return false;
} }
return true;
} }
/* /*
@@ -195,4 +189,21 @@ public class CostMill extends CostPartWithList {
return sb.toString(); 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";
}
} }

View File

@@ -17,6 +17,7 @@
*/ */
package forge.card.cost; package forge.card.cost;
import forge.Card; import forge.Card;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
@@ -199,7 +200,7 @@ public abstract class CostPart {
* @param game * @param game
* @return true, if successful * @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) * (non-Javadoc)

View File

@@ -20,12 +20,12 @@ package forge.card.cost;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import forge.Card; import forge.Card;
import forge.Singletons; import forge.FThreads;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.control.input.InputPayManaOfCostPayment; import forge.control.input.InputPayManaOfCostPayment;
import forge.control.input.InputPayManaX; import forge.control.input.InputPayManaX;
import forge.control.input.InputPayment;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtilMana; import forge.game.ai.ComputerUtilMana;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
@@ -111,8 +111,8 @@ public class CostPartMana extends CostPart {
/** /**
* @return the xCantBe0 * @return the xCantBe0
*/ */
public boolean isxCantBe0() { public boolean canXbe0() {
return xCantBe0; return !xCantBe0;
} }
/** /**
@@ -206,7 +206,8 @@ public class CostPartMana extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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; int manaToAdd = 0;
if (!this.hasNoXManaCost()) { if (!this.hasNoXManaCost()) {
// if X cost is a defined value, other than xPaid // if X cost is a defined value, other than xPaid
@@ -215,20 +216,24 @@ public class CostPartMana extends CostPart {
manaToAdd = AbilityUtils.calculateAmount(source, "X", ability) * this.getXMana(); 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);
}
// We return false here because the Inputs set above should recall
// payment.payCosts() if (!"0".equals(this.getManaToPay()) || manaToAdd > 0) {
InputPayment inpPayment = new InputPayManaOfCostPayment(game, this, ability, manaToAdd);
FThreads.setInputAndWait(inpPayment);
if(!inpPayment.isPaid())
return false; 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;
}
/* /*
* (non-Javadoc) * (non-Javadoc)

View File

@@ -17,8 +17,9 @@
*/ */
package forge.card.cost; package forge.card.cost;
import java.util.ArrayList; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import forge.Card; import forge.Card;
import forge.CardUtil; import forge.CardUtil;
@@ -30,14 +31,16 @@ import forge.card.spellability.SpellAbility;
public abstract class CostPartWithList extends CostPart { public abstract class CostPartWithList extends CostPart {
/** The list. */ /** The list. */
private List<Card> list = null; private Set<Card> list = new HashSet<Card>();
// 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. * Gets the list.
* *
* @return the list * @return the list
*/ */
public final List<Card> getList() { public final Set<Card> getList() {
return this.list; return this.list;
} }
@@ -48,14 +51,15 @@ public abstract class CostPartWithList extends CostPart {
* the new list * the new list
*/ */
public final void setList(final List<Card> setList) { public final void setList(final List<Card> setList) {
this.list = setList; this.list.clear();
list.addAll(setList);
} }
/** /**
* Reset list. * Reset list.
*/ */
public final void resetList() { public final void resetList() {
this.setList(new ArrayList<Card>()); this.list.clear();
} }
/** /**
@@ -65,10 +69,7 @@ public abstract class CostPartWithList extends CostPart {
* the c * the c
*/ */
public final void addToList(final Card c) { public final void addToList(final Card c) {
if (this.getList() == null) { this.list.add(c);
this.resetList();
}
this.getList().add(c);
} }
/** /**
@@ -106,4 +107,12 @@ public abstract class CostPartWithList extends CostPart {
super(amount, type, description); super(amount, type, description);
this.resetList(); this.resetList();
} }
public abstract void executePayment(SpellAbility ability, Card targetCard);
/**
* TODO: Write javadoc for this method.
* @return
*/
public abstract String getHashForList();
} }

View File

@@ -123,7 +123,8 @@ public class CostPayLife extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 String amount = this.getAmount();
final Player activator = ability.getActivatingPlayer(); final Player activator = ability.getActivatingPlayer();
final int life = activator.getLife(); final int life = activator.getLife();
@@ -147,13 +148,9 @@ public class CostPayLife extends CostPart {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append(source.getName()).append(" - Pay ").append(c).append(" Life?"); 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); activator.payLife(c, null);
this.setLastPaidAmount(c);
payment.setPaidManaPart(this);
} else { } else {
payment.setCancel(true);
payment.getRequirements().finishPaying();
return false; return false;
} }
return true; return true;

View File

@@ -38,7 +38,6 @@ import forge.game.player.Player;
public class CostPayment { public class CostPayment {
private Cost cost = null; private Cost cost = null;
private SpellAbility ability = null; private SpellAbility ability = null;
private Card card = null;
private SpellAbilityRequirements req = null; private SpellAbilityRequirements req = null;
private boolean bCancel = false; private boolean bCancel = false;
private final ArrayList<CostPart> paidCostParts = new ArrayList<CostPart>(); private final ArrayList<CostPart> paidCostParts = new ArrayList<CostPart>();
@@ -74,7 +73,7 @@ public class CostPayment {
* @return a {@link forge.Card} object. * @return a {@link forge.Card} object.
*/ */
public final Card getCard() { 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) { public CostPayment(final Cost cost, final SpellAbility abil, final GameState game) {
this.cost = cost; this.cost = cost;
this.ability = abil; this.ability = abil;
this.card = abil.getSourceCard();
this.game = game; this.game = game;
} }
@@ -179,26 +177,15 @@ public class CostPayment {
* @param bPaid * @param bPaid
* the b paid * the b paid
*/ */
public final void setPaidManaPart(final CostPart part) { public final void setPaidPart(final CostPart part) {
this.paidCostParts.add(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). * Cancel cost (including CostPart for refunding).
*/ */
public final void cancelCost(final CostPart part) { public final void cancelCost(final CostPart part) {
this.setPaidManaPart(part); this.setPaidPart(part);
this.cancelCost(); this.cancelCost();
} }
@@ -207,7 +194,6 @@ public class CostPayment {
*/ */
public final void cancelCost() { public final void cancelCost() {
this.setCancel(true); this.setCancel(true);
this.req.finishPaying();
} }
/** /**
@@ -217,27 +203,22 @@ public class CostPayment {
* *
* @return a boolean. * @return a boolean.
*/ */
public final boolean payCost() { public void payCost() {
// Nothing actually ever checks this return value, is it needed?
if (this.bCancel) {
this.req.finishPaying();
return false;
}
for (final CostPart part : this.cost.getCostParts()) { for (final CostPart part : this.cost.getCostParts()) {
// This portion of the cost is already paid for, keep moving // This portion of the cost is already paid for, keep moving
if (this.paidCostParts.contains(part)) { if (this.paidCostParts.contains(part)) {
continue; continue;
} }
if (!part.payHuman(this.ability, this.card, this, game)) { if ( false == part.payHuman(getAbility(), game) ) {
return false; this.setCancel(true);
return;
} }
if( part instanceof CostPartWithList )
((CostPartWithList) part).addListToHash(ability, ((CostPartWithList) part).getHashForList());
setPaidPart(part);
} }
this.resetUndoList(); this.resetUndoList();
this.req.finishPaying();
return true;
} }
/** /**
@@ -278,7 +259,7 @@ public class CostPayment {
public final void cancelPayment() { public final void cancelPayment() {
for (final CostPart part : this.paidCostParts) { for (final CostPart part : this.paidCostParts) {
if (part.isUndoable()) { if (part.isUndoable()) {
part.refund(this.card); part.refund(this.getCard());
} }
} }

View File

@@ -18,14 +18,13 @@
package forge.card.cost; package forge.card.cost;
import java.util.List; import java.util.List;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CounterType; import forge.CounterType;
import forge.Singletons; import forge.FThreads;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.InputPayment;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
@@ -38,6 +37,69 @@ import forge.view.ButtonUtil;
* The Class CostPutCounter. * The Class CostPutCounter.
*/ */
public class CostPutCounter extends CostPartWithList { 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<Card> 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 // Put Counter doesn't really have a "Valid" portion of the cost
private final CounterType counter; private final CounterType counter;
private int lastPaidAmount = 0; private int lastPaidAmount = 0;
@@ -156,17 +218,14 @@ public class CostPutCounter extends CostPartWithList {
*/ */
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
Integer c = this.convertAmount(); Integer c = getNumberOfCounters(ability);
if (c == null) {
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
source.addCounter(this.getCounter(), c, false); executePayment(ability, source, c);
} else { } else {
// Put counter on chosen card // Put counter on chosen card
for (final Card card : this.getList()) { 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) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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) {
Integer c = this.convertAmount(); final Card source = ability.getSourceCard();
if (c == null) { Integer c = getNumberOfCounters(ability);
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
source.addCounter(this.getCounter(), c, false); executePayment(ability, source, c);
payment.setPaidManaPart(this);
this.addToList(source);
return true; return true;
} else { } else {
final Input inp = CostPutCounter.putCounterType(ability, this.getType(), payment, this, c); InputPayment inp = new InputPayCostPutCounter(this.getType(), this, c, ability);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); FThreads.setInputAndWait(inp);
return false; 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) * (non-Javadoc)
* *
@@ -211,13 +274,7 @@ public class CostPutCounter extends CostPartWithList {
this.addToList(source); this.addToList(source);
return true; return true;
} else { } else {
Integer c = this.convertAmount(); final List<Card> typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ai, source);
if (c == null) {
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
final List<Card> typeList =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ai, source);
Card card = null; Card card = null;
if (this.getType().equals("Creature.YouCtrl")) { if (this.getType().equals("Creature.YouCtrl")) {
@@ -230,83 +287,22 @@ public class CostPutCounter extends CostPartWithList {
return true; return true;
} }
/** /* (non-Javadoc)
* <p> * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
* returnType.
* </p>
*
* @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.
*/ */
public static Input putCounterType(final SpellAbility sa, final String type, final CostPayment payment, @Override
final CostPutCounter costPutCounter, final int nNeeded) { public void executePayment(SpellAbility ability, Card targetCard){
final Input target = new Input() { executePayment(ability, targetCard, 1);
private static final long serialVersionUID = 2685832214519141903L; }
private List<Card> typeList;
private int nPut = 0; public void executePayment(SpellAbility ability, Card targetCard, int c) {
targetCard.addCounter(this.getCounter(), c, false);
this.addToList(targetCard);
}
@Override @Override
public void showMessage() { public String getHashForList() {
if ((nNeeded == 0) || (nNeeded == this.nPut)) { return "CounterPut";
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;
} }
} }

View File

@@ -19,14 +19,13 @@ package forge.card.cost;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CounterType; import forge.CounterType;
import forge.Singletons; import forge.FThreads;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.InputPayment;
import forge.game.GameState; import forge.game.GameState;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
@@ -46,6 +45,142 @@ public class CostRemoveCounter extends CostPartWithList {
// Counter is tough), // Counter is tough),
// Quillspike, Rift Elemental, Sage of Fables, Spike Rogue // 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<Card> 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<Card> 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<Card>(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 final CounterType counter;
private int lastPaidAmount = 0; private int lastPaidAmount = 0;
private ZoneType zone; private ZoneType zone;
@@ -221,8 +356,9 @@ public class CostRemoveCounter extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 String amount = this.getAmount();
final Card source = ability.getSourceCard();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
int maxCounters = 0; int maxCounters = 0;
@@ -237,15 +373,14 @@ public class CostRemoveCounter extends CostPartWithList {
} }
} }
final InputPayment inp;
if (this.getZone().equals(ZoneType.Battlefield)) { if (this.getZone().equals(ZoneType.Battlefield)) {
final Input inp = CostRemoveCounter.removeCounterType(ability, this.getType(), payment, this, c); inp = new InputPayCostRemoveCounterType(c, ability, this.getType(), this);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); } else {
inp = new InputPayCostRemoveCounterFrom(this, this.getType(), ability, c);
} }
else { FThreads.setInputAndWait(inp);
final Input inp = CostRemoveCounter.removeCounterTypeFrom(ability, this.getType(), payment, this, c); return inp.isPaid();
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
}
return false;
} }
maxCounters = source.getCounters(this.counter); maxCounters = source.getCounters(this.counter);
@@ -263,17 +398,12 @@ public class CostRemoveCounter extends CostPartWithList {
} }
} }
if (maxCounters >= c) { if (maxCounters < c) return false;
this.addToList(source); this.addToList(source);
source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c)); source.setSVar("CostCountersRemoved", "Number$" + Integer.toString(c));
source.subtractCounter(this.counter, c); executePayment(ability, source, c);
this.setLastPaidAmount(c);
payment.setPaidManaPart(this);
} else {
payment.setCancel(true);
payment.getRequirements().finishPaying();
return false;
}
return true; return true;
} }
@@ -321,171 +451,23 @@ public class CostRemoveCounter extends CostPartWithList {
return true; return true;
} }
/** /* (non-Javadoc)
* <p> * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
* returnType.
* </p>
*
* @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 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<Card> typeList;
private int nRemove = 0;
@Override @Override
public void showMessage() { public void executePayment(SpellAbility ability, Card targetCard) {
if ((nNeeded == 0) || (nNeeded == this.nRemove)) { executePayment(ability, targetCard, 1);
this.done();
} }
final StringBuilder msg = new StringBuilder("Remove "); public void executePayment(SpellAbility ability, Card targetCard, int n) {
final int nLeft = nNeeded - this.nRemove; addToList(targetCard);
msg.append(nLeft).append(" "); targetCard.subtractCounter(getCounter(), n);
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();
} }
/* (non-Javadoc)
@Override * @see forge.card.cost.CostPartWithList#getHashForList()
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;
}
/**
* <p>
* returnType.
* </p>
*
* @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 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<Card> typeList;
private int nRemove = 0;
@Override @Override
public void showMessage() { public String getHashForList() {
if (nNeeded == 0) { return "CounterRemove";
this.done();
}
this.typeList = new ArrayList<Card>(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;
} }
} }

View File

@@ -19,22 +19,19 @@ package forge.card.cost;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.JOptionPane;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.Singletons; import forge.FThreads;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; 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.GameState;
import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI; import forge.gui.GuiDialog;
import forge.view.ButtonUtil;
/** /**
* The Class CostReturn. * The Class CostReturn.
@@ -123,7 +120,7 @@ public class CostReturn extends CostPartWithList {
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { 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()) { 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) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 String amount = this.getAmount();
final Card source = ability.getSourceCard();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
final Player activator = ability.getActivatingPlayer(); final Player activator = ability.getActivatingPlayer();
final List<Card> list = activator.getCardsIn(ZoneType.Battlefield); final List<Card> list = activator.getCardsIn(ZoneType.Battlefield);
@@ -150,11 +148,26 @@ public class CostReturn extends CostPartWithList {
} }
} }
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
final Input inp = CostReturn.returnThis(ability, payment, this); final Card card = ability.getSourceCard();
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); 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 { } else {
final Input inp = CostReturn.returnType(ability, this.getType(), payment, this, c); List<Card> validCards = CardLists.getValidCards(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard());
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
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)); this.setList(ComputerUtil.chooseReturnType(ai, this.getType(), source, ability.getTargetCard(), c));
if (this.getList() == null) { if (this.getList().isEmpty()) {
return false; return false;
} }
} }
return true; 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 // Inputs
/**
* <p>
* returnType.
* </p>
*
* @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<Card> 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<Card>(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()
/**
* <p>
* returnThis.
* </p>
*
* @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()
} }

View File

@@ -19,20 +19,19 @@ package forge.card.cost;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.InputPayment;
import forge.game.GameState; import forge.game.GameState;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
/** /**
@@ -41,6 +40,87 @@ import forge.view.ButtonUtil;
public class CostReveal extends CostPartWithList { public class CostReveal extends CostPartWithList {
// Reveal<Num/Type/TypeDescription> // Reveal<Num/Type/TypeDescription>
/**
* TODO: Write javadoc for this type.
*
*/
public static final class InputPayReveal extends InputPayCostBase {
private final CostReveal part;
private final String discType;
private final List<Card> 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<Card> 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<Card> 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. * Instantiates a new cost reveal.
* *
@@ -140,6 +220,8 @@ public class CostReveal extends CostPartWithList {
@Override @Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) { 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()); 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) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 Player activator = ability.getActivatingPlayer();
final Card source = ability.getSourceCard();
final String amount = this.getAmount(); final String amount = this.getAmount();
this.resetList(); this.resetList();
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
this.addToList(source); executePayment(ability, source);
payment.setPaidManaPart(this); return true;
} else if (this.getType().equals("Hand")) { } else if (this.getType().equals("Hand")) {
this.setList(new ArrayList<Card>(activator.getCardsIn(ZoneType.Hand))); for(Card c : activator.getCardsIn(ZoneType.Hand))
payment.setPaidManaPart(this); executePayment(ability, c);
return true;
} else { } else {
Integer num = this.convertAmount(); Integer num = this.convertAmount();
@@ -175,17 +259,13 @@ public class CostReveal extends CostPartWithList {
num = AbilityUtils.calculateAmount(source, amount, ability); num = AbilityUtils.calculateAmount(source, amount, ability);
} }
} }
if (num > 0) { if ( num == 0 ) return true;
final Input inp = CostReveal.inputRevealCost(this.getType(), handList, payment, this, ability, num);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); InputPayment inp = new InputPayReveal(this, this.getType(), handList, ability, num);
return false; FThreads.setInputAndWait(inp);
} else { return inp.isPaid();
payment.setPaidManaPart(this);
} }
} }
this.addListToHash(ability, "Revealed");
return true;
}
/* /*
* (non-Javadoc) * (non-Javadoc)
@@ -220,102 +300,24 @@ public class CostReveal extends CostPartWithList {
return sb.toString(); 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 // Inputs
/**
* <p>
* input_discardCost.
* </p>
*
* @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<Card> 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<Card> 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()
} }

View File

@@ -19,20 +19,18 @@ package forge.card.cost;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.JOptionPane;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.Singletons; import forge.FThreads;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.InputPayment;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiDialog;
import forge.gui.match.CMatchUI; import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
@@ -41,6 +39,70 @@ import forge.view.ButtonUtil;
*/ */
public class CostSacrifice extends CostPartWithList { 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<Card> 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<Card> 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. * 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) { public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
this.addListToHash(ability, "Sacrificed"); this.addListToHash(ability, "Sacrificed");
for (final Card c : this.getList()) { 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) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 String amount = this.getAmount();
final Card source = ability.getSourceCard();
final String type = this.getType(); final String type = this.getType();
final Player activator = ability.getActivatingPlayer(); final Player activator = ability.getActivatingPlayer();
List<Card> list = new ArrayList<Card>(activator.getCardsIn(ZoneType.Battlefield)); List<Card> list = new ArrayList<Card>(activator.getCardsIn(ZoneType.Battlefield));
@@ -155,32 +218,36 @@ public class CostSacrifice extends CostPartWithList {
} }
if (this.payCostFromSource()) { if (this.payCostFromSource()) {
final Input inp = CostSacrifice.sacrificeThis(ability, payment, this); if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) {
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); if (!GuiDialog.confirm(source, source.getName() + " - Sacrifice?"))
return false;
executePayment(ability, source);
return true;
}
} else if (amount.equals("All")) { } else if (amount.equals("All")) {
this.setList(list); this.setList(list);
CostSacrifice.sacrificeAll(ability, payment, this, list); // TODO Ask First
//this.addListToHash(ability, "Sacrificed"); for (final Card card : list) {
executePayment(ability, card);
}
return true; return true;
} else { } else {
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
final String sVar = ability.getSVar(amount);
// Generalize this // Generalize this
if (sVar.equals("XChoice")) { if (ability.getSVar(amount).equals("XChoice")) {
c = CostUtil.chooseXValue(source, ability, list.size()); c = CostUtil.chooseXValue(source, ability, list.size());
} else { } else {
c = AbilityUtils.calculateAmount(source, amount, ability); c = AbilityUtils.calculateAmount(source, amount, ability);
} }
} }
if (0 == c.intValue()) { if (0 == c.intValue()) {
payment.setPaidManaPart(this);
return true; return true;
} }
final Input inp = CostSacrifice.sacrificeFromList(ability, payment, this, list, c); InputPayment inp = new InputPayCostSacrificeFromList(this, ability, c, list);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); FThreads.setInputAndWait(inp);
return inp.isPaid();
} }
return false; return false;
} }
@@ -222,156 +289,23 @@ public class CostSacrifice extends CostPartWithList {
return true; return true;
} }
/* (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().sacrifice(targetCard, ability);
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "Sacrificed";
}
// Inputs // Inputs
/**
* <p>
* sacrificeAllType.
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* @param payment
* a {@link forge.card.cost.CostPayment} object.
* @param part
* TODO
* @param typeList
* TODO
*/
public static void sacrificeAll(final SpellAbility sa, final CostPayment payment, final CostPart part,
final List<Card> typeList) {
// TODO Ask First
for (final Card card : typeList) {
payment.getAbility().addCostToHashList(card, "Sacrificed");
Singletons.getModel().getGame().getAction().sacrifice(card, sa);
}
payment.setPaidManaPart(part);
}
/**
* <p>
* sacrificeFromList.
* </p>
*
* @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.
*/
public static Input sacrificeFromList(final SpellAbility sa, final CostPayment payment, final CostSacrifice part,
final List<Card> typeList, final int nNeeded) {
final Input target = new Input() {
private static final long serialVersionUID = 2685832214519141903L;
private int nSacrifices = 0;
@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 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()
/**
* <p>
* sacrificeThis.
* </p>
*
* @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()
} }

View File

@@ -93,13 +93,12 @@ public class CostTap extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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(), // if (!canPay(ability, source, ability.getActivatingPlayer(),
// payment.getCost())) // payment.getCost()))
// return false; // return false;
source.tap(); ability.getSourceCard().tap();
payment.setPaidManaPart(this);
return true; return true;
} }

View File

@@ -19,21 +19,20 @@ package forge.card.cost;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates.Presets; import forge.CardPredicates.Presets;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.InputPayment;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
/** /**
@@ -41,6 +40,66 @@ import forge.view.ButtonUtil;
*/ */
public class CostTapType extends CostPartWithList { 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<Card> 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<Card> 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<Card> 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. * Instantiates a new cost tap type.
* *
@@ -149,11 +208,12 @@ public class CostTapType extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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<Card> typeList = new ArrayList<Card>(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield)); List<Card> typeList = new ArrayList<Card>(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield));
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); typeList = CardLists.getValidCards(typeList, this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard());
typeList = CardLists.filter(typeList, Presets.UNTAPPED); typeList = CardLists.filter(typeList, Presets.UNTAPPED);
final String amount = this.getAmount(); final String amount = this.getAmount();
final Card source = ability.getSourceCard();
Integer c = this.convertAmount(); Integer c = this.convertAmount();
if (c == null) { if (c == null) {
final String sVar = ability.getSVar(amount); final String sVar = ability.getSVar(amount);
@@ -164,10 +224,9 @@ public class CostTapType extends CostPartWithList {
c = AbilityUtils.calculateAmount(source, amount, ability); c = AbilityUtils.calculateAmount(source, amount, ability);
} }
} }
InputPayment inp = new InputPayCostTapType(this, c, typeList);
final Input inp = CostTapType.inputTapXCost(this, typeList, ability, payment, c); FThreads.setInputAndWait(inp);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); return inp.isPaid();
return false;
} }
/* /*
@@ -205,83 +264,23 @@ public class CostTapType extends CostPartWithList {
return true; 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 // Inputs
/**
* <p>
* input_tapXCost.
* </p>
*
* @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<Card> 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<Card> 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()
} }

View File

@@ -114,23 +114,16 @@ public class CostUnattach extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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) {
this.resetList(); final Card source = ability.getSourceCard();
Player activator = ability.getActivatingPlayer(); Player activator = ability.getActivatingPlayer();
Card cardToUnattach = findCardToUnattach(source, activator, ability); Card cardToUnattach = findCardToUnattach(source, activator, ability);
if (cardToUnattach != null && GuiDialog.confirm(source, String.format("Unattach %s?", cardToUnattach.toString()))) { if (cardToUnattach != null && GuiDialog.confirm(source, String.format("Unattach %s?", cardToUnattach.toString()))) {
Card equippingCard = cardToUnattach.getEquipping().get(0); executePayment(ability, cardToUnattach);
cardToUnattach.unEquipCard(equippingCard); return true;
this.addToList(cardToUnattach);
this.addListToHash(ability, "Unattached");
payment.setPaidManaPart(this);
} else { } else {
payment.setCancel(true);
payment.getRequirements().finishPaying();
return false; return false;
} }
return true;
} }
private Card findCardToUnattach(final Card source, Player activator, SpellAbility ability) { private Card findCardToUnattach(final Card source, Player activator, SpellAbility ability) {
@@ -165,4 +158,18 @@ public class CostUnattach extends CostPartWithList {
this.resetList(); this.resetList();
return true; 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";
}
} }

View File

@@ -92,13 +92,12 @@ public class CostUntap extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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(), // if (!canPay(ability, source, ability.getActivatingPlayer(),
// payment.getCost())) // payment.getCost()))
// return false; // return false;
source.untap(); ability.getSourceCard().untap();
payment.setPaidManaPart(this);
return true; return true;
} }

View File

@@ -18,21 +18,20 @@
package forge.card.cost; package forge.card.cost;
import java.util.List; import java.util.List;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates.Presets; import forge.CardPredicates.Presets;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.InputPayment;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
/** /**
@@ -40,6 +39,71 @@ import forge.view.ButtonUtil;
*/ */
public class CostUntapType extends CostPartWithList { 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<Card> 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<Card> 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<Card> 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. * Instantiates a new cost untap type.
* *
@@ -162,12 +226,13 @@ public class CostUntapType extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment) * forge.Card, forge.card.cost.Cost_Payment)
*/ */
@Override @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 boolean untap = payment.getCost().hasUntapCost(); final boolean canUntapSource = false; // payment.getCost().hasUntapCost(); - only Crackleburr uses this
List<Card> typeList = Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield); List<Card> typeList = Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield);
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); typeList = CardLists.getValidCards(typeList, this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard());
typeList = CardLists.filter(typeList, Presets.TAPPED); typeList = CardLists.filter(typeList, Presets.TAPPED);
if (untap) { final Card source = ability.getSourceCard();
if (canUntapSource) {
typeList.remove(source); typeList.remove(source);
} }
final String amount = this.getAmount(); final String amount = this.getAmount();
@@ -181,10 +246,9 @@ public class CostUntapType extends CostPartWithList {
c = AbilityUtils.calculateAmount(source, amount, ability); c = AbilityUtils.calculateAmount(source, amount, ability);
} }
} }
InputPayment inp = new InputPayCostUntapY(c, typeList, this);
final Input inp = CostUntapType.inputUntapYCost(this, typeList, ability, payment, c); FThreads.setInputAndWait(inp);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp); return inp.isPaid();
return false;
} }
/* /*
@@ -225,86 +289,23 @@ public class CostUntapType extends CostPartWithList {
return true; 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 // Inputs
/**
* <p>
* input_untapYCost.
* </p>
*
* @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<Card> 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<Card> 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()
} }

View File

@@ -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; }
}

View File

@@ -44,6 +44,20 @@ public final class ManaCost implements Comparable<ManaCost> {
public static final ManaCost NO_COST = new ManaCost(-1); public static final ManaCost NO_COST = new ManaCost(-1);
public static final ManaCost ZERO = new ManaCost(0); public static final ManaCost ZERO = new ManaCost(0);
public static final ManaCost ONE = new ManaCost(1); 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 // pass mana cost parser here
private ManaCost(int cmc) { private ManaCost(int cmc) {

View File

@@ -42,6 +42,8 @@ public class ManaCostBeingPaid {
private final ArrayList<String> manaNeededToAvoidNegativeEffect = new ArrayList<String>(); private final ArrayList<String> manaNeededToAvoidNegativeEffect = new ArrayList<String>();
private final ArrayList<String> manaPaidToAvoidNegativeEffect = new ArrayList<String>(); private final ArrayList<String> manaPaidToAvoidNegativeEffect = new ArrayList<String>();
private final ManaCost originalCost;
// manaCost can be like "0", "3", "G", "GW", "10", "3 GW", "10 GW" // 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" // or "split hybrid mana" like "2/G 2/G", "2/B 2/B 2/B"
// "GW" can be paid with either G or W // "GW" can be paid with either G or W
@@ -59,6 +61,7 @@ public class ManaCostBeingPaid {
} }
public ManaCostBeingPaid(ManaCost manaCost) { public ManaCostBeingPaid(ManaCost manaCost) {
originalCost = manaCost;
if ( null == manaCost ) if ( null == manaCost )
return; return;
@@ -645,4 +648,8 @@ public class ManaCostBeingPaid {
public final ArrayList<String> getManaPaidToAvoidNegativeEffect() { public final ArrayList<String> getManaPaidToAvoidNegativeEffect() {
return this.manaPaidToAvoidNegativeEffect; return this.manaPaidToAvoidNegativeEffect;
} }
public ManaCost getStartingCost() {
return originalCost;
}
} }

View File

@@ -23,8 +23,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang.StringUtils;
import forge.Card; import forge.Card;
import forge.GameEntity; import forge.GameEntity;
import forge.Singletons; import forge.Singletons;
@@ -1682,25 +1680,4 @@ public abstract class SpellAbility implements ISpellAbility {
public void setCopied(boolean isCopied0) { public void setCopied(boolean isCopied0) {
this.isCopied = 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;
}
} }

View File

@@ -19,6 +19,8 @@ package forge.card.spellability;
import java.util.ArrayList; import java.util.ArrayList;
import org.apache.commons.lang3.StringUtils;
import forge.Card; import forge.Card;
import forge.CardCharacteristicName; import forge.CardCharacteristicName;
import forge.Singletons; import forge.Singletons;
@@ -44,67 +46,26 @@ public class SpellAbilityRequirements {
private Zone fromZone = null; private Zone fromZone = null;
private Integer zonePosition = null; private Integer zonePosition = null;
/**
* <p>
* Setter for the field <code>skipStack</code>.
* </p>
*
* @param bSkip
* a boolean.
*/
public final void setSkipStack(final boolean bSkip) { public final void setSkipStack(final boolean bSkip) {
this.skipStack = bSkip; this.skipStack = bSkip;
} }
/**
* <p>
* setFree.
* </p>
*
* @param bFree
* a boolean.
*/
public final void setFree(final boolean bFree) { public final void setFree(final boolean bFree) {
this.isFree = bFree; this.isFree = bFree;
} }
/**
* <p>
* Constructor for SpellAbility_Requirements.
* </p>
*
* @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) { public SpellAbilityRequirements(final SpellAbility sa, final TargetSelection ts, final CostPayment cp) {
this.ability = sa; this.ability = sa;
this.select = ts; this.select = ts;
this.payment = cp; this.payment = cp;
} }
/**
* <p>
* fillRequirements.
* </p>
*/
public final void fillRequirements() { public final void fillRequirements() {
this.fillRequirements(false); this.fillRequirements(false);
} }
/**
* <p>
* fillRequirements.
* </p>
*
* @param skipTargeting
* a boolean.
*/
public final void fillRequirements(final boolean skipTargeting) { public final void fillRequirements(final boolean skipTargeting) {
if ((this.ability instanceof Spell) && !this.bCasting) { if ((this.ability instanceof Spell) && !this.bCasting) {
// remove from hand // remove from hand
@@ -118,14 +79,13 @@ public class SpellAbilityRequirements {
} }
} }
// freeze Stack. No abilities should go onto the stack while I'm filling // freeze Stack. No abilities should go onto the stack while I'm filling requirements.
// requirements.
Singletons.getModel().getGame().getStack().freezeStack(); Singletons.getModel().getGame().getStack().freezeStack();
// Announce things like how many times you want to Multikick or the value of X // 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.select.setCancel(true);
this.finishedTargeting(); rollbackAbility();
return; return;
} }
@@ -134,20 +94,43 @@ public class SpellAbilityRequirements {
// (or trigger case where its already targeted) // (or trigger case where its already targeted)
if (!skipTargeting && (this.select.doesTarget() || (this.ability.getSubAbility() != null))) { if (!skipTargeting && (this.select.doesTarget() || (this.ability.getSubAbility() != null))) {
this.select.setRequirements(this); this.select.setRequirements(this);
this.select.resetTargets(); this.select.clearTargets();
this.select.chooseTargets(); this.select.chooseTargets();
} else { if (this.select.isCanceled()) {
this.needPayment(); rollbackAbility();
return;
} }
} }
/** // Payment
* <p> if (!this.isFree) {
* finishedTargeting. this.payment.setRequirements(this);
* </p> this.payment.changeCost();
*/ this.payment.payCost();
public final void finishedTargeting() { }
if (this.select.isCanceled()) {
if (this.payment.isCanceled()) {
rollbackAbility();
return;
}
else if (this.isFree || this.payment.isAllPaid()) {
if (this.skipStack) {
AbilityUtils.resolve(this.ability, false);
} else {
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();
}
}
private void rollbackAbility() {
// cancel ability during target choosing // cancel ability during target choosing
final Card c = this.ability.getSourceCard(); final Card c = this.ability.getSourceCard();
@@ -161,89 +144,46 @@ public class SpellAbilityRequirements {
Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition); Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition);
} }
this.select.resetTargets();
Singletons.getModel().getGame().getStack().removeFromFrozenStack(this.ability);
return;
} else {
this.needPayment();
}
}
/**
* <p>
* needPayment.
* </p>
*/
public final void needPayment() {
if (!this.isFree) {
this.startPaying();
} else {
this.finishPaying();
}
}
/**
* <p>
* startPaying.
* </p>
*/
public final void startPaying() {
this.payment.setRequirements(this);
this.payment.changeCost();
this.payment.payCost();
}
/**
* <p>
* finishPaying.
* </p>
*/
public final void finishPaying() {
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) { if (this.select != null) {
this.select.resetTargets(); this.select.clearTargets();
} }
this.ability.resetOnceResolved(); this.ability.resetOnceResolved();
this.payment.cancelPayment(); this.payment.cancelPayment();
Singletons.getModel().getGame().getStack().clearFrozen(); Singletons.getModel().getGame().getStack().clearFrozen();
} // Singletons.getModel().getGame().getStack().removeFromFrozenStack(this.ability);
else if (this.isFree || this.payment.isAllPaid()) {
if (this.skipStack) {
AbilityUtils.resolve(this.ability, false);
} else {
this.addAbilityToStack();
} }
this.select.resetTargets();
Singletons.getModel().getGame().getAction().checkStateEffects(); 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) {
* <p> if (!StringUtils.isBlank(ability.getStackDescription()))
* addAbilityToStack. return;
* </p>
*/
public final void addAbilityToStack() {
// For older abilities that don't setStackDescription set it here // For older abilities that don't setStackDescription set it here
if (this.ability.getStackDescription().equals("")) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append(this.ability.getSourceCard().getName()); sb.append(ability.getSourceCard().getName());
if (this.ability.getTarget() != null) { if (ability.getTarget() != null) {
final ArrayList<Object> targets = this.ability.getTarget().getTargets(); final ArrayList<Object> targets = ability.getTarget().getTargets();
if (targets.size() > 0) { if (targets.size() > 0) {
sb.append(" - Targeting "); sb.append(" - Targeting ");
for (final Object o : targets) { for (final Object o : targets) {
@@ -252,10 +192,6 @@ public class SpellAbilityRequirements {
} }
} }
this.ability.setStackDescription(sb.toString()); ability.setStackDescription(sb.toString());
}
this.ability.getActivatingPlayer().getManaPool().clearManaPaid(this.ability, false);
Singletons.getModel().getGame().getStack().addAndUnfreeze(this.ability);
} }
} }

View File

@@ -20,15 +20,16 @@ package forge.card.spellability;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType; 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.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
@@ -44,6 +45,174 @@ import forge.view.ButtonUtil;
* @version $Id$ * @version $Id$
*/ */
public class TargetSelection { public class TargetSelection {
/**
* TODO: Write javadoc for this type.
*
*/
public final class InputSelectTargets extends InputSyncronizedBase {
private final TargetSelection select;
private final List<Card> choices;
private final ArrayList<Object> 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<Card> choices, ArrayList<Object> 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 Target target = null;
private SpellAbility ability = null; private SpellAbility ability = null;
private Card card = null; private Card card = null;
@@ -98,6 +267,7 @@ public class TargetSelection {
} }
private boolean bCancel = false; private boolean bCancel = false;
private boolean bTargetingDone = false;
/** /**
* <p> * <p>
@@ -130,20 +300,6 @@ public class TargetSelection {
return this.subSelection.isCanceled(); return this.subSelection.isCanceled();
} }
private boolean bDoneTarget = false;
/**
* <p>
* setDoneTarget.
* </p>
*
* @param done
* a boolean.
*/
public final void setDoneTarget(final boolean done) {
this.bDoneTarget = done;
}
/** /**
* <p> * <p>
* Constructor for Target_Selection. * Constructor for Target_Selection.
@@ -179,7 +335,7 @@ public class TargetSelection {
* resetTargets. * resetTargets.
* </p> * </p>
*/ */
public final void resetTargets() { public final void clearTargets() {
if (this.target != null) { if (this.target != null) {
this.target.resetTargets(); this.target.resetTargets();
this.target.calculateStillToDivide(this.ability.getParam("DividedAsYouChoose"), this.getCard(), this.ability); this.target.calculateStillToDivide(this.ability.getParam("DividedAsYouChoose"), this.getCard(), this.ability);
@@ -195,24 +351,25 @@ public class TargetSelection {
*/ */
public final boolean chooseTargets() { public final boolean chooseTargets() {
// if not enough targets chosen, reset and cancel Ability // 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.bCancel = true;
this.req.finishedTargeting();
return false; 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.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(); final AbilitySub abSub = this.ability.getSubAbility();
if (abSub == null) { if (abSub == null) {
// if no more SubAbilities finish targeting // if no more SubAbilities finish targeting
this.req.finishedTargeting();
return true; return true;
} else { } else {
// Has Sub Ability // Has Sub Ability
this.subSelection = new TargetSelection(abSub.getTarget(), abSub); this.subSelection = new TargetSelection(abSub.getTarget(), abSub);
this.subSelection.setRequirements(this.req); this.subSelection.setRequirements(this.req);
this.subSelection.resetTargets(); this.subSelection.clearTargets();
return this.subSelection.chooseTargets(); 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)) { if (!this.target.hasCandidates(this.ability, true) && !this.target.isMinTargetsChosen(this.card, this.ability)) {
// Cancel ability if there aren't any valid Candidates // Cancel ability if there aren't any valid Candidates
this.bCancel = true; this.bCancel = true;
this.req.finishedTargeting();
return false; return false;
} }
this.chooseValidInput(); this.chooseValidInput();
if ( !bCancel )
return chooseTargets();
return false; return false;
} }
@@ -362,178 +520,14 @@ public class TargetSelection {
} }
if (zone.contains(ZoneType.Battlefield) && zone.size() == 1) { 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 { } else {
this.chooseCardFromList(choices, true, mandatory); this.chooseCardFromList(choices, true, mandatory);
} }
} // input_targetValid } // input_targetValid
// List<Card> choices are the only cards the user can successful select
/**
* <p>
* input_targetSpecific.
* </p>
*
* @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<Card> choices, final boolean targeted, final boolean mandatory,
final ArrayList<Object> 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()
/** /**
* <p> * <p>
* chooseCardFromList. * chooseCardFromList.
@@ -624,7 +618,7 @@ public class TargetSelection {
if (!c.equals(divBattlefield) && !c.equals(divExile) && !c.equals(divGrave) if (!c.equals(divBattlefield) && !c.equals(divExile) && !c.equals(divGrave)
&& !c.equals(divLibrary) && !c.equals(divStack)) { && !c.equals(divLibrary) && !c.equals(divStack)) {
if (c.equals(dummy)) { if (c.equals(dummy)) {
this.setDoneTarget(true); bTargetingDone = true;
} else { } else {
tgt.addTarget(c); tgt.addTarget(c);
} }
@@ -632,8 +626,6 @@ public class TargetSelection {
} else { } else {
this.setCancel(true); this.setCancel(true);
} }
this.chooseTargets();
} }
/** /**
@@ -675,7 +667,7 @@ public class TargetSelection {
if (madeChoice != null) { if (madeChoice != null) {
if (madeChoice.equals(doneDummy)) { if (madeChoice.equals(doneDummy)) {
this.setDoneTarget(true); bTargetingDone = true;
} else { } else {
tgt.addTarget(map.get(madeChoice)); tgt.addTarget(map.get(madeChoice));
} }
@@ -683,8 +675,6 @@ public class TargetSelection {
select.setCancel(true); select.setCancel(true);
} }
} }
select.chooseTargets();
} }
// TODO The following three functions are Utility functions for // TODO The following three functions are Utility functions for

View File

@@ -156,6 +156,7 @@ public enum FControl {
this.shortcuts = KeyboardShortcuts.attachKeyboardShortcuts(); this.shortcuts = KeyboardShortcuts.attachKeyboardShortcuts();
this.display = FView.SINGLETON_INSTANCE.getLpnDocument(); this.display = FView.SINGLETON_INSTANCE.getLpnDocument();
FSkin.setProgessBarMessage("About to load current quest.");
// Preload quest data if present // Preload quest data if present
final File dirQuests = new File(NewConstants.QUEST_SAVE_DIR); final File dirQuests = new File(NewConstants.QUEST_SAVE_DIR);
final String questname = Singletons.getModel().getQuestPreferences().getPref(QPref.CURRENT_QUEST); final String questname = Singletons.getModel().getQuestPreferences().getPref(QPref.CURRENT_QUEST);
@@ -164,6 +165,7 @@ public enum FControl {
Singletons.getModel().getQuest().load(QuestDataIO.loadData(data)); Singletons.getModel().getQuest().load(QuestDataIO.loadData(data));
} }
FSkin.setProgessBarMessage("Will load AI profiles now.");
// Preload AI profiles // Preload AI profiles
AiProfileUtil.loadAllProfiles(); AiProfileUtil.loadAllProfiles();
@@ -178,6 +180,7 @@ public enum FControl {
FView.SINGLETON_INSTANCE.getLpnDocument().addMouseListener(SOverflowUtil.getHideOverflowListener()); FView.SINGLETON_INSTANCE.getLpnDocument().addMouseListener(SOverflowUtil.getHideOverflowListener());
FView.SINGLETON_INSTANCE.getLpnDocument().addComponentListener(SResizingUtil.getWindowResizeListener()); FView.SINGLETON_INSTANCE.getLpnDocument().addComponentListener(SResizingUtil.getWindowResizeListener());
FSkin.setProgessBarMessage("Opening main window...");
SwingUtilities.invokeLater(new Runnable() { @Override SwingUtilities.invokeLater(new Runnable() { @Override
public void run() { Singletons.getView().initialize(); } }); public void run() { Singletons.getView().initialize(); } });
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package forge.control.input; package forge.control.input;
import forge.Card; import forge.Card;
import forge.Singletons;
import forge.game.player.Player; import forge.game.player.Player;
import forge.gui.match.CMatchUI;
/** /**
* <p> * TODO: Write javadoc for this type.
* Abstract Input class.
* </p>
* *
* @author Forge
* @version $Id$
*/ */
public abstract class Input implements java.io.Serializable { public interface Input {
/** Constant <code>serialVersionUID=-6539552513871194081L</code>. */
private static final long serialVersionUID = -6539552513871194081L;
// showMessage() is always the first method called // showMessage() is always the first method called
/** void showMessage();
* <p>
* showMessage.
* </p>
*/
public void showMessage() {
CMatchUI.SINGLETON_INSTANCE.showMessage("Blank Input");
}
/** void selectCard(Card c);
* <p>
* selectCard.
* </p>
*
* @param c
* a {@link forge.Card} object.
*/
public void selectCard(final Card c) {
}
/** void selectPlayer(Player player);
* <p>
* selectPlayer.
* </p>
*
* @param player
* a {@link forge.game.player.Player} object.
*/
public void selectPlayer(final Player player) {
}
/** void selectButtonOK();
* <p>
* selectButtonOK.
* </p>
*/
public void selectButtonOK() {
}
/** void selectButtonCancel();
* <p>
* selectButtonCancel.
* </p>
*/
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)
/**
* <p>
* stop.
* </p>
*/
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
/**
* <p>
* stopSetNext.
* </p>
*
* @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() {
} //;
} }

View File

@@ -24,9 +24,9 @@ import com.google.common.collect.Iterables;
import forge.Card; import forge.Card;
import forge.CardPredicates; import forge.CardPredicates;
import forge.Singletons; import forge.Singletons;
import forge.game.GameState;
import forge.game.phase.CombatUtil; import forge.game.phase.CombatUtil;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.framework.SDisplayUtil; import forge.gui.framework.SDisplayUtil;
@@ -42,10 +42,16 @@ import forge.view.ButtonUtil;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public class InputAttack extends Input { public class InputAttack extends InputBase {
/** Constant <code>serialVersionUID=7849903731842214245L</code>. */ /** Constant <code>serialVersionUID=7849903731842214245L</code>. */
private static final long serialVersionUID = 7849903731842214245L; private static final long serialVersionUID = 7849903731842214245L;
private final GameState game;
public InputAttack(GameState game0) {
game = game0;
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void showMessage() { public final void showMessage() {
@@ -53,7 +59,7 @@ public class InputAttack extends Input {
ButtonUtil.enableOnlyOk(); ButtonUtil.enableOnlyOk();
final Object o = Singletons.getModel().getGame().getCombat().nextDefender(); final Object o = game.getCombat().nextDefender();
if (o == null) { if (o == null) {
return; return;
} }
@@ -64,13 +70,13 @@ public class InputAttack extends Input {
CMatchUI.SINGLETON_INSTANCE.showMessage(sb.toString()); 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 // Nothing left to attack, has to attack this defender
List<Card> possibleAttackers = Singletons.getControl().getPlayer().getCardsIn(ZoneType.Battlefield); List<Card> possibleAttackers = Singletons.getControl().getPlayer().getCardsIn(ZoneType.Battlefield);
for (Card c : Iterables.filter(possibleAttackers, CardPredicates.Presets.CREATURES)) { 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()) { && !c.isAttacking()) {
Singletons.getModel().getGame().getCombat().addAttacker(c); game.getCombat().addAttacker(c);
} }
} }
} }
@@ -79,36 +85,36 @@ public class InputAttack extends Input {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void selectButtonOK() { public final void selectButtonOK() {
if (!Singletons.getModel().getGame().getCombat().getAttackers().isEmpty()) { if (!game.getCombat().getAttackers().isEmpty()) {
Singletons.getModel().getGame().getPhaseHandler().setCombat(true); game.getPhaseHandler().setCombat(true);
} }
if (Singletons.getModel().getGame().getCombat().getRemainingDefenders() != 0) { if (game.getCombat().getRemainingDefenders() != 0) {
Singletons.getModel().getGame().getPhaseHandler().repeatPhase(); game.getPhaseHandler().repeatPhase();
} }
Singletons.getModel().getGame().getPhaseHandler().setPlayersPriorityPermission(false); game.getPhaseHandler().setPlayersPriorityPermission(false);
Singletons.getModel().getMatch().getInput().resetInput(); Singletons.getModel().getMatch().getInput().updateObservers();
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void selectCard(final Card card) { public final void selectCard(final Card card) {
if (card.isAttacking() || card.getController().isComputer()) { if (card.isAttacking() || card.getController() != Singletons.getControl().getPlayer()) {
return; return;
} }
final Player human = Singletons.getControl().getPlayer(); final Player human = Singletons.getControl().getPlayer();
Zone zone = Singletons.getModel().getGame().getZoneOf(card); Zone zone = game.getZoneOf(card);
if (zone.is(ZoneType.Battlefield, human) 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 // TODO add the propaganda code here and remove it in
// Phase.nextPhase() // Phase.nextPhase()
// if (!CombatUtil.checkPropagandaEffects(card)) // if (!CombatUtil.checkPropagandaEffects(card))
// return; // return;
Singletons.getModel().getGame().getCombat().addAttacker(card); game.getCombat().addAttacker(card);
// just to make sure the attack symbol is marked // just to make sure the attack symbol is marked
human.getZone(ZoneType.Battlefield).updateObservers(); human.getZone(ZoneType.Battlefield).updateObservers();
@@ -118,25 +124,4 @@ public class InputAttack extends Input {
SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE);
} }
} // selectCard() } // selectCard()
/**
* <p>
* unselectCard.
* </p>
*
* @param card
* a {@link forge.Card} object.
* @param zone
* a {@link forge.game.zone.PlayerZone} object.
*/
public void unselectCard(final Card card, final PlayerZone zone) {
}
/* (non-Javadoc)
* @see forge.control.input.Input#isClassUpdated()
*/
@Override
public void isClassUpdated() {
}
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package forge.control.input;
import forge.Card;
import forge.Singletons;
import forge.game.player.Player;
import forge.gui.match.CMatchUI;
/**
* <p>
* Abstract Input class.
* </p>
*
* @author Forge
* @version $Id$
*/
public abstract class InputBase implements java.io.Serializable, Input {
/** Constant <code>serialVersionUID=-6539552513871194081L</code>. */
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() { }
}

View File

@@ -42,7 +42,7 @@ import forge.view.ButtonUtil;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public class InputBlock extends Input { public class InputBlock extends InputBase {
/** Constant <code>serialVersionUID=6120743598368928128L</code>. */ /** Constant <code>serialVersionUID=6120743598368928128L</code>. */
private static final long serialVersionUID = 6120743598368928128L; private static final long serialVersionUID = 6120743598368928128L;
@@ -144,11 +144,4 @@ public class InputBlock extends Input {
this.showMessage(); this.showMessage();
} // selectCard() } // selectCard()
/* (non-Javadoc)
* @see forge.control.input.Input#isClassUpdated()
*/
@Override
public void isClassUpdated() {
}
} }

View File

@@ -34,7 +34,7 @@ import forge.view.ButtonUtil;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public class InputCleanup extends Input { public class InputCleanup extends InputBase {
/** Constant <code>serialVersionUID=-4164275418971547948L</code>. */ /** Constant <code>serialVersionUID=-4164275418971547948L</code>. */
private static final long serialVersionUID = -4164275418971547948L; private static final long serialVersionUID = -4164275418971547948L;
private final GameState game; private final GameState game;
@@ -82,18 +82,4 @@ public class InputCleanup extends Input {
} }
} }
} // selectCard() } // selectCard()
/**
* <p>
* AI_CleanupDiscard.
* </p>
*/
/* (non-Javadoc)
* @see forge.control.input.Input#isClassUpdated()
*/
@Override
public void isClassUpdated() {
}
} }

View File

@@ -25,7 +25,6 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerController; import forge.game.player.PlayerController;
import forge.game.zone.MagicStack; import forge.game.zone.MagicStack;
import forge.gui.match.controllers.CMessage;
import forge.util.MyObservable; import forge.util.MyObservable;
/** /**
@@ -40,21 +39,7 @@ public class InputControl extends MyObservable implements java.io.Serializable {
/** Constant <code>serialVersionUID=3955194449319994301L</code>. */ /** Constant <code>serialVersionUID=3955194449319994301L</code>. */
private static final long serialVersionUID = 3955194449319994301L; private static final long serialVersionUID = 3955194449319994301L;
private Input input;
private final Stack<Input> inputStack = new Stack<Input>(); private final Stack<Input> inputStack = new Stack<Input>();
private final Stack<Input> urgentInputStack = new Stack<Input>();
private final transient GameState game;
/**
* TODO Write javadoc for Constructor.
*
* @param fModel
* the f model
*/
public InputControl(final GameState game0) {
this.game = game0;
}
/** /**
* <p> * <p>
@@ -62,46 +47,12 @@ public class InputControl extends MyObservable implements java.io.Serializable {
* </p> * </p>
* *
* @param in * @param in
* a {@link forge.control.input.Input} object. * a {@link forge.control.input.InputBase} object.
*/ */
public final void setInput(final Input in) { public final void setInput(final Input in) {
boolean isInputEmpty = this.input == null || this.input instanceof InputPassPriority;
//System.out.println(in.getClass().getName()); //System.out.println(in.getClass().getName());
if (!this.game.getStack().isResolving() && isInputEmpty) { this.inputStack.push(in);
this.input = in; // System.out.print("Current: " + input + "; Stack = " + inputStack);
} else {
this.inputStack.add(in);
}
this.updateObservers();
}
/**
* <p>
* Setter for the field <code>input</code>.
* </p>
*
* @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);
}
/**
* <p>
* changeInput.
* </p>
*
* @param in
* a {@link forge.control.input.Input} object.
*/
private void changeInput(final Input in) {
this.input = in;
this.updateObservers(); this.updateObservers();
} }
@@ -110,10 +61,10 @@ public class InputControl extends MyObservable implements java.io.Serializable {
* Getter for the field <code>input</code>. * Getter for the field <code>input</code>.
* </p> * </p>
* *
* @return a {@link forge.control.input.Input} object. * @return a {@link forge.control.input.InputBase} object.
*/ */
public final Input getInput() { 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 {
* </p> * </p>
*/ */
public final void clearInput() { public final void clearInput() {
this.input = null;
this.inputStack.clear(); this.inputStack.clear();
this.updateObservers();
} }
@@ -135,19 +86,34 @@ public class InputControl extends MyObservable implements java.io.Serializable {
* @param update * @param update
* a boolean. * a boolean.
*/ */
public final void resetInput() { public final void removeInput(Input inp) {
this.input = null; 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(); this.updateObservers();
} }
public final boolean isEmpty() {
return inputStack.isEmpty();
}
/** /**
* <p> * <p>
* updateInput. * updateInput.
* </p> * </p>
* *
* @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() ) if ( !game.hasMulliganned() )
return new InputMulligan(); return new InputMulligan();
@@ -157,36 +123,11 @@ public class InputControl extends MyObservable implements java.io.Serializable {
final Player priority = handler.getPriorityPlayer(); final Player priority = handler.getPriorityPlayer();
final MagicStack stack = game.getStack(); final MagicStack stack = game.getStack();
// TODO this resolving portion needs more work, but fixes Death Cloud // TODO this resolving portion needs more work, but fixes Death Cloud
// issues // 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 if (!this.inputStack.isEmpty()) { // incoming input to Control
this.changeInput(this.inputStack.pop()); return this.inputStack.peek();
return this.input;
}
if (handler.hasPhaseEffects()) {
// Handle begin phase stuff, then start back from the top
handler.handleBeginPhase();
return this.getActualInput();
} }
// If the Phase we're in doesn't allow for Priority, return null to move // 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(); stack.freezeStack();
if (playerTurn.isHuman() && !playerTurn.getController().mayAutoPass(phase)) { if (playerTurn.isHuman() && !playerTurn.getController().mayAutoPass(phase)) {
game.getCombat().initiatePossibleDefenders(playerTurn.getOpponents()); game.getCombat().initiatePossibleDefenders(playerTurn.getOpponents());
return new InputAttack(); return new InputAttack(game);
} }
break; break;
@@ -236,7 +177,7 @@ public class InputControl extends MyObservable implements java.io.Serializable {
// priority // priority
boolean prioritySkip = pc.mayAutoPass(phase) || pc.isUiSetToSkipPhase(playerTurn, phase); boolean prioritySkip = pc.mayAutoPass(phase) || pc.isUiSetToSkipPhase(playerTurn, phase);
if (this.game.getStack().isEmpty() && prioritySkip) { if (game.getStack().isEmpty() && prioritySkip) {
pc.passPriority(); pc.passPriority();
return null; return null;
} else } else
@@ -245,19 +186,23 @@ public class InputControl extends MyObservable implements java.io.Serializable {
return pc.getDefaultInput(); return pc.getDefaultInput();
} // getInput() } // getInput()
public final void setNewInput(GameState game) { /**
PhaseHandler ph = game.getPhaseHandler(); * TODO: Write javadoc for this method.
*/
final Input tmp = getActualInput(); private final static InputLockUI inputLock = new InputLockUI();
//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()); public void lock() {
//System.out.println(message); setInput(inputLock);
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();
} }
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);
}
// only for debug purposes
public String printInputStack() {
return inputStack.toString();
} }
} // InputControl } // InputControl

View File

@@ -1,18 +1,51 @@
package forge.control.input; package forge.control.input;
import forge.gui.match.CMatchUI; import java.util.concurrent.atomic.AtomicInteger;
import forge.FThreads;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
/** /**
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
* *
*/ */
public class InputLockUI extends Input { public class InputLockUI extends InputBase {
private static final long serialVersionUID = 5777143577098597374L; private static final long serialVersionUID = 5777143577098597374L;
private final AtomicInteger iCall = new AtomicInteger();
public void showMessage() { public void showMessage() {
ButtonUtil.disableAll(); int ixCall = 1 + iCall.getAndIncrement();
CMatchUI.SINGLETON_INSTANCE.showMessage("Waiting for actions..."); 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...");
}
};
} }

View File

@@ -49,7 +49,7 @@ import forge.view.ButtonUtil;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public class InputMulligan extends Input { public class InputMulligan extends InputBase {
/** Constant <code>serialVersionUID=-8112954303001155622L</code>. */ /** Constant <code>serialVersionUID=-8112954303001155622L</code>. */
private static final long serialVersionUID = -8112954303001155622L; private static final long serialVersionUID = -8112954303001155622L;
@@ -174,7 +174,6 @@ public class InputMulligan extends Input {
game.setMulliganned(true); game.setMulliganned(true);
Singletons.getModel().getMatch().getInput().clearInput(); Singletons.getModel().getMatch().getInput().clearInput();
Singletons.getModel().getMatch().getInput().resetInput();
} }
@Override @Override
@@ -192,8 +191,4 @@ public class InputMulligan extends Input {
SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE);
} }
} }
@Override
public void isClassUpdated() {
}
} }

View File

@@ -18,10 +18,10 @@
package forge.control.input; package forge.control.input;
import forge.Card; import forge.Card;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.FControl; import forge.control.FControl;
import forge.game.GameState;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.gui.GuiDisplayUtil; import forge.gui.GuiDisplayUtil;
@@ -38,7 +38,7 @@ import forge.view.ButtonUtil;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public class InputPassPriority extends Input { public class InputPassPriority extends InputBase {
/** Constant <code>serialVersionUID=-581477682214137181L</code>. */ /** Constant <code>serialVersionUID=-581477682214137181L</code>. */
private static final long serialVersionUID = -581477682214137181L; private static final long serialVersionUID = -581477682214137181L;
@@ -80,17 +80,20 @@ public class InputPassPriority extends Input {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void selectCard(final Card card) { public final void selectCard(final Card card) {
Player player = Singletons.getControl().getPlayer(); final Player player = Singletons.getControl().getPlayer();
GameState game = Singletons.getModel().getGame(); final SpellAbility ab = player.getController().getAbilityToPlay(player.getGame().getAbilitesOfCard(card, player));
SpellAbility ab = player.getController().getAbilityToPlay(game.getAbilitesOfCard(card, player));
if ( null != ab) { if ( null != ab) {
Runnable execAbility = new Runnable() {
@Override
public void run() {
player.playSpellAbility(card, ab); player.playSpellAbility(card, ab);
} }
};
FThreads.invokeInNewThread(execAbility, true);
}
else { else {
SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE); SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE);
} }
} // selectCard() } // selectCard()
@Override public void isClassUpdated() {
}
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package forge.control.input;
import java.util.List;
import forge.Card;
import forge.CardLists;
import forge.Command;
import forge.Singletons;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.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
/**
* <p>
* Input_PayManaCost_Ability class.
* </p>
*
* @author Forge
* @version $Id: InputPayManaCostAbility.java 15673 2012-05-23 14:01:35Z ArsenalNut $
*/
public class InputPayDiscardCost extends Input {
/**
* Constant <code>serialVersionUID=2685832214529141991L</code>.
*/
private static final long serialVersionUID = 5634078561074764401L;
private int numChosen = 0;
private int numRequired = 0;
private List<Card> choiceList;
private CostDiscard discardCost;
private SpellAbility ability;
private Command paid;
private Command unpaid;
/**
* <p>
* Constructor for Input_PayManaCost_Ability.
* </p>
*
* @param cost
* a {@link forge.card.cost.CostSacrifice} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* @param paidCommand
* a {@link forge.Command} object.
* @param unpaidCommand
* a {@link forge.Command} object.
*/
public 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();
}
}
/**
* <p>
* unselectCard.
* </p>
*
* @param card
* a {@link forge.Card} object.
* @param zone
* a {@link forge.game.zone.PlayerZone} object.
*/
public void unselectCard(final Card card, final PlayerZone zone) {
if (this.discardCost.getList().contains(card)) {
this.numChosen--;
card.setUsedToPay(false);
this.discardCost.getList().remove(card);
this.choiceList.add(card);
this.showMessage();
}
}
/**
* <p>
* executes paid commmand.
* </p>
*
*/
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();
}
/**
* <p>
* executes unpaid commmand.
* </p>
*
*/
public void cancel() {
this.stop();
for (Card selected : this.discardCost.getList()) {
selected.setUsedToPay(false);
}
this.unpaid.execute();
}
@Override public void isClassUpdated() {
}
}

View File

@@ -1,14 +1,12 @@
package forge.control.input; package forge.control.input;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import forge.Card; import forge.Card;
import forge.CardUtil; import forge.CardUtil;
import forge.Constant; import forge.Constant;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.ability.ApiType; import forge.card.ability.ApiType;
@@ -18,13 +16,16 @@ import forge.card.spellability.AbilityManaPart;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
import forge.gui.framework.SDisplayUtil;
import forge.gui.match.views.VMessage;
/** /**
* TODO: Write javadoc for this type. * 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; private static final long serialVersionUID = -9133423708688480255L;
@@ -33,19 +34,30 @@ public abstract class InputPayManaBase extends Input {
protected final Player whoPays; protected final Player whoPays;
protected final GameState game; protected final GameState game;
protected ManaCostBeingPaid manaCost; 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.game = game;
this.whoPays = Singletons.getControl().getPlayer(); this.whoPays = saToPayFor.getActivatingPlayer();
this.saPaidFor = saToPayFor;
} }
/** /** {@inheritDoc} */
* <p> @Override
* selectManaPool. public void selectCard(final Card card) {
* </p> if (card.getManaAbility().isEmpty() || card.isInZone(ZoneType.Hand)) {
* @param color a String that represents the Color the mana is coming from SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE);
*/ return;
public abstract void selectManaPool(String color); }
// only tap card if the mana is needed
activateManaAbility(card, this.manaCost);
}
public void selectManaPool(String color) {
useManaFromPool(color, this.manaCost);
}
/** /**
* <p> * <p>
@@ -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 * @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(); ManaPool mp = Singletons.getControl().getPlayer().getManaPool();
// Convert Color to short String // Convert Color to short String
@@ -112,7 +124,10 @@ public abstract class InputPayManaBase extends Input {
manaStr = CardUtil.getShortColor(color); manaStr = CardUtil.getShortColor(color);
} }
return mp.payManaFromPool(saBeingPaidFor, manaCost, manaStr); this.manaCost = mp.payManaFromPool(saPaidFor, manaCost, manaStr);
onManaAbilityPlayed(null);
showMessage();
} }
/** /**
@@ -128,10 +143,10 @@ public abstract class InputPayManaBase extends Input {
* a {@link forge.card.mana.ManaCostBeingPaid} object. * a {@link forge.card.mana.ManaCostBeingPaid} object.
* @return 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 // make sure computer's lands aren't selected
if (card.getController() != whoPays) { if (card.getController() != whoPays) {
return manaCost; return;
} }
@@ -170,7 +185,7 @@ public abstract class InputPayManaBase extends Input {
continue; continue;
} else if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) { } else if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) {
continue; continue;
} else if (!m.meetsManaRestrictions(sa)) { } else if (!m.meetsManaRestrictions(saPaidFor)) {
continue; continue;
} }
@@ -185,16 +200,16 @@ public abstract class InputPayManaBase extends Input {
} }
} }
if (abilities.isEmpty()) { if (abilities.isEmpty()) {
return manaCost; return;
} }
// Store some information about color costs to help with any mana choices // Store some information about color costs to help with any mana choices
String colorsNeeded = colorRequired.toString(); String colorsNeeded = colorRequired.toString();
if ("1".equals(colorsNeeded)) { // only colorless left if ("1".equals(colorsNeeded)) { // only colorless left
if (sa.getSourceCard() != null if (saPaidFor.getSourceCard() != null
&& !sa.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").equals("")) { && !saPaidFor.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").equals("")) {
colorsNeeded = ""; colorsNeeded = "";
String[] negEffects = sa.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").split(","); String[] negEffects = saPaidFor.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").split(",");
for (String negColor : negEffects) { for (String negColor : negEffects) {
// convert long color strings to short color strings // convert long color strings to short color strings
if (negColor.length() > 1) { 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, // If the card has sunburst or any other ability that tracks mana spent,
// skip express Mana choice // skip express Mana choice
if (sa.getSourceCard() != null if (saPaidFor.getSourceCard() != null
&& sa.getSourceCard().hasKeyword("Sunburst") && sa.isSpell()) { && saPaidFor.getSourceCard().hasKeyword("Sunburst") && saPaidFor.isSpell()) {
colorsNeeded = "WUBRG"; colorsNeeded = "WUBRG";
skipExpress = true; 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. // can only match colorless just grab the first and move on.
choice = false; choice = false;
} else if (colorMatches.size() < abilities.size()) { } else if (colorMatches.size() < abilities.size()) {
@@ -264,15 +279,7 @@ public abstract class InputPayManaBase extends Input {
} }
} }
SpellAbility chosen = abilities.get(0); final SpellAbility chosen = abilities.size() > 1 && choice ? GuiChoose.one("Choose mana ability", abilities) : abilities.get(0);
if ((1 < abilities.size()) && choice) {
final Map<String, SpellAbility> ability = new HashMap<String, SpellAbility>();
for (final SpellAbility am : abilities) {
ability.put(am.toString(), am);
}
chosen = GuiChoose.one("Choose mana ability", abilities);
}
SpellAbility subchosen = chosen; SpellAbility subchosen = chosen;
while(subchosen.getManaPart() == null) 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 // save off color needed for use by any mana and reflected mana
subchosen.getManaPart().setExpressChoice(colorsNeeded); subchosen.getManaPart().setExpressChoice(colorsNeeded);
Player p = chosen.getActivatingPlayer(); // System.out.println("Chosen sa=" + chosen + " of " + chosen.getSourceCard() + " to pay mana");
Singletons.getModel().getGame().getActionPlay().playSpellAbility(chosen, p); Runnable proc = new Runnable() {
@Override
manaCost = p.getManaPool().payManaFromAbility(sa, manaCost, chosen); public void run() {
final Player p = chosen.getActivatingPlayer();
//AllZone.getHumanPlayer().getZone(ZoneType.Battlefield).updateObservers(); p.getGame().getActionPlay().playSpellAbility(chosen, p);
// DO NOT REMOVE THIS, otherwise the cards don't always tap (copied) onManaAbilityPlayed(chosen);
return manaCost;
} }
};
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; }
} }

View File

@@ -17,17 +17,12 @@
*/ */
package forge.control.input; package forge.control.input;
import forge.Card;
import forge.Command;
import forge.Singletons; import forge.Singletons;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostBeingPaid;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
import forge.game.player.Player; 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; import forge.view.ButtonUtil;
//if cost is paid, Command.execute() is called //if cost is paid, Command.execute() is called
@@ -40,19 +35,17 @@ import forge.view.ButtonUtil;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public class InputPayManaExecuteCommands extends InputPayManaBase { public class InputPayManaExecuteCommands extends InputPayManaBase implements InputPayment {
/** /**
* Constant <code>serialVersionUID=3836655722696348713L</code>. * Constant <code>serialVersionUID=3836655722696348713L</code>.
*/ */
private static final long serialVersionUID = 3836655722696348713L; private static final long serialVersionUID = 3836655722696348713L;
private String originalManaCost; private ManaCost originalManaCost;
private String message = ""; private String message = "";
private SpellAbility fakeAbility;
private boolean bPaid = false;
private Command paidCommand; public boolean isPaid() { return bPaid; }
private Command unpaidCommand;
// only used for X costs: // only used for X costs:
private boolean showOnlyOKButton = false; private boolean showOnlyOKButton = false;
@@ -72,8 +65,8 @@ public class InputPayManaExecuteCommands extends InputPayManaBase {
* @param unpaidCommand2 * @param unpaidCommand2
* a {@link forge.Command} object. * a {@link forge.Command} object.
*/ */
public InputPayManaExecuteCommands(final GameState game, final String prompt, final String manaCost2, final Command paidCommand2, final Command unpaidCommand2) { public InputPayManaExecuteCommands(final GameState game, final String prompt, final ManaCost manaCost2) {
this(game, prompt, manaCost2, paidCommand2, unpaidCommand2, false); this(game, prompt, manaCost2, false);
} }
/** /**
@@ -92,25 +85,19 @@ public class InputPayManaExecuteCommands extends InputPayManaBase {
* @param showOKButton * @param showOKButton
* a boolean. * a boolean.
*/ */
public InputPayManaExecuteCommands(final GameState game, final String prompt, final String manaCost2, final Command paid, final Command unpaid, final boolean showOKButton) { public InputPayManaExecuteCommands(final GameState game, final String prompt, final ManaCost manaCost2, final boolean showOKButton) {
super(game); super(game, new SpellAbility(null) {
this.fakeAbility = new SpellAbility(null) {
@Override @Override
public void resolve() { public void resolve() {}
}
@Override @Override
public boolean canPlay() { public boolean canPlay() { return false; }
return false; });
}
};
this.originalManaCost = manaCost2; this.originalManaCost = manaCost2;
this.phyLifeToLose = 0; this.phyLifeToLose = 0;
this.message = prompt; this.message = prompt;
this.manaCost = new ManaCostBeingPaid(this.originalManaCost); this.manaCost = new ManaCostBeingPaid(this.originalManaCost);
this.paidCommand = paid;
this.unpaidCommand = unpaid;
this.showOnlyOKButton = showOKButton; this.showOnlyOKButton = showOKButton;
} }
@@ -138,39 +125,24 @@ public class InputPayManaExecuteCommands extends InputPayManaBase {
} }
} }
/** {@inheritDoc} */
@Override @Override
public final void selectCard(final Card card) { protected void done() {
// 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() {
if (this.phyLifeToLose > 0) { if (this.phyLifeToLose > 0) {
Singletons.getControl().getPlayer().payLife(this.phyLifeToLose, null); Singletons.getControl().getPlayer().payLife(this.phyLifeToLose, null);
} }
this.paidCommand.execute();
this.resetManaCost(); this.resetManaCost();
Singletons.getControl().getPlayer().getManaPool().clearManaPaid(this.fakeAbility, false); Singletons.getControl().getPlayer().getManaPool().clearManaPaid(this.saPaidFor, false);
bPaid = true;
this.stop(); this.stop();
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void selectButtonCancel() { public final void selectButtonCancel() {
this.unpaidCommand.execute();
this.resetManaCost(); this.resetManaCost();
Singletons.getControl().getPlayer().getManaPool().refundManaPaid(this.fakeAbility, true); Singletons.getControl().getPlayer().getManaPool().refundManaPaid(this.saPaidFor, true);
bPaid = false;
this.stop(); this.stop();
} }
@@ -178,8 +150,8 @@ public class InputPayManaExecuteCommands extends InputPayManaBase {
@Override @Override
public final void selectButtonOK() { public final void selectButtonOK() {
if (this.showOnlyOKButton) { if (this.showOnlyOKButton) {
bPaid = false;
this.stop(); 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.)"); 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() {
}
} }

View File

@@ -3,62 +3,23 @@ package forge.control.input;
import forge.Card; import forge.Card;
import forge.Singletons; import forge.Singletons;
import forge.card.cost.CostPartMana; import forge.card.cost.CostPartMana;
import forge.card.cost.CostPayment;
import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostBeingPaid;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
public class InputPayManaOfCostPayment extends InputPayManaBase { public class InputPayManaOfCostPayment extends InputPayManaBase {
private final CostPartMana costMana; public InputPayManaOfCostPayment(final GameState game, CostPartMana costMana, SpellAbility spellAbility, int toAdd) {
super(game, spellAbility);
// 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);
manaCost = new ManaCostBeingPaid(costMana.getManaToPay()); manaCost = new ManaCostBeingPaid(costMana.getManaToPay());
manaCost.increaseColorlessMana(toAdd); manaCost.increaseColorlessMana(toAdd);
this.costMana = costMana;
originalManaCost = costMana.getMana();
sa = spellAbility;
manaToAdd = toAdd;
this.payment = payment;
} }
private static final long serialVersionUID = 3467312982164195091L; private static final long serialVersionUID = 3467312982164195091L;
private int phyLifeToLose = 0; 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 @Override
public void selectPlayer(final Player player) { public void selectPlayer(final Player player) {
if (player == whoPays) { if (player == whoPays) {
@@ -70,65 +31,34 @@ public class InputPayManaOfCostPayment extends InputPayManaBase {
} }
} }
private void done() { @Override
final Card source = sa.getSourceCard(); protected void done() {
final Card source = saPaidFor.getSourceCard();
if (this.phyLifeToLose > 0) { if (this.phyLifeToLose > 0) {
Singletons.getControl().getPlayer().payLife(this.phyLifeToLose, source); Singletons.getControl().getPlayer().payLife(this.phyLifeToLose, source);
} }
source.setColorsPaid(this.manaCost.getColorsPaid()); source.setColorsPaid(this.manaCost.getColorsPaid());
source.setSunburstValue(this.manaCost.getSunburst()); source.setSunburstValue(this.manaCost.getSunburst());
this.resetManaCost();
this.stop();
if (costMana.hasNoXManaCost() || (manaToAdd > 0)) { // If this is a spell with convoke, re-tap all creatures used for it.
payment.paidCost(costMana); // This is done to make sure Taps triggers go off at the right time
} else { // (i.e. AFTER cost payment, they are tapped previously as well so that
source.setXManaCostPaid(0); // any mana tapabilities can't be used in payment as well as being tapped for convoke)
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();
}
handleConvokedCards(false);
stop();
} }
@Override @Override
public void selectButtonCancel() { public void selectButtonCancel() {
// If we're paying for a spell with convoke, untap all creatures handleConvokedCards(true);
// used for it. stop();
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();
} }
@Override @Override
public void showMessage() { public void showMessage() {
ButtonUtil.enableOnlyCancel(); ButtonUtil.enableOnlyCancel();
final String displayMana = manaCost.toString().replace("X", "").trim(); 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); final StringBuilder msg = new StringBuilder("Pay Mana Cost: " + displayMana);
if (this.phyLifeToLose > 0) { 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.)"); msg.append("\n(Click on your life total to pay life for phyrexian mana.)");
} }
CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); showMessage(msg.toString());
if (manaCost.isPaid()) { checkIfAlredyPaid();
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() {
} }
} }

View File

@@ -18,13 +18,11 @@
package forge.control.input; package forge.control.input;
import forge.Card; import forge.Card;
import forge.Singletons;
import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostBeingPaid;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
//pays the cost of a card played from the player's hand //pays the cost of a card played from the player's hand
@@ -35,61 +33,21 @@ public class InputPayManaSimple extends InputPayManaBase {
/** Constant <code>serialVersionUID=3467312982164195091L</code>. */ /** Constant <code>serialVersionUID=3467312982164195091L</code>. */
private static final long serialVersionUID = 3467312982164195091L; private static final long serialVersionUID = 3467312982164195091L;
private boolean skipStack;
private final SpellAbility spell;
private final Card originalCard; private final Card originalCard;
private final String originalManaCost; private final String originalManaCost;
/**
* <p>
* Constructor for Input_PayManaCost.
* </p>
*
* @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;
}
/**
* <p>
* Constructor for Input_PayManaCost.
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
*/
public InputPayManaSimple(final GameState game, final SpellAbility sa) {
this(game, sa, new ManaCostBeingPaid(sa.getManaCost()));
}
/**
* <p>
* Constructor for Input_PayManaCost.
* </p>
*
* @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) { public InputPayManaSimple(final GameState game, final SpellAbility sa, final ManaCostBeingPaid manaCostToPay) {
super(game); super(game, sa);
this.originalManaCost = manaCostToPay.toString(); // Change this.originalManaCost = manaCostToPay.toString(); // Change
this.originalCard = sa.getSourceCard(); this.originalCard = sa.getSourceCard();
this.spell = sa;
if (sa.getSourceCard().isCopiedSpell() && sa.isSpell()) { if (sa.getSourceCard().isCopiedSpell() && sa.isSpell()) {
this.manaCost = new ManaCostBeingPaid("0"); this.manaCost = new ManaCostBeingPaid("0");
game.getStack().add(this.spell); game.getStack().add(this.saPaidFor);
} else { } else {
this.manaCost = manaCostToPay; this.manaCost = manaCostToPay;
} }
} }
/** /**
@@ -102,24 +60,9 @@ public class InputPayManaSimple extends InputPayManaBase {
this.phyLifeToLose = 0; this.phyLifeToLose = 0;
} }
/** {@inheritDoc} */ protected void onManaAbilityPaid() {
@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();
}
if (this.manaCost.isPaid()) { if (this.manaCost.isPaid()) {
this.originalCard.setSunburstValue(this.manaCost.getSunburst()); this.originalCard.setSunburstValue(this.manaCost.getSunburst());
this.done();
} }
} }
@@ -142,59 +85,32 @@ public class InputPayManaSimple extends InputPayManaBase {
* done. * done.
* </p> * </p>
*/ */
private void done() { @Override
protected void done() {
if (this.phyLifeToLose > 0) { if (this.phyLifeToLose > 0) {
whoPays.payLife(this.phyLifeToLose, this.originalCard); whoPays.payLife(this.phyLifeToLose, this.originalCard);
} }
if (this.spell.getSourceCard().isCopiedSpell()) { if (!this.saPaidFor.getSourceCard().isCopiedSpell()) {
Singletons.getModel().getMatch().getInput().resetInput(); whoPays.getManaPool().clearManaPaid(this.saPaidFor, false);
} else {
whoPays.getManaPool().clearManaPaid(this.spell, false);
this.resetManaCost(); this.resetManaCost();
if (this.spell.isSpell()) { if (this.saPaidFor.isSpell()) {
this.spell.setSourceCard(game.getAction().moveToStack(this.originalCard)); this.saPaidFor.setSourceCard(game.getAction().moveToStack(this.originalCard));
} }
if (this.skipStack) { handleConvokedCards(false);
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 stop();
// 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();
}
}
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void selectButtonCancel() { public final void selectButtonCancel() {
// If this is a spell with convoke, untap all creatures used for it. handleConvokedCards(true);
if (this.spell.getTappedForConvoke() != null) {
for (final Card c : this.spell.getTappedForConvoke()) {
c.setTapped(false);
}
this.spell.clearTappedForConvoke();
}
this.resetManaCost(); this.resetManaCost();
whoPays.getManaPool().refundManaPaid(this.spell, true); whoPays.getManaPool().refundManaPaid(this.saPaidFor, true);
whoPays.getZone(ZoneType.Battlefield).updateObservers(); // DO whoPays.getZone(ZoneType.Battlefield).updateObservers(); // DO
this.stop(); 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.)"); 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()) { if (this.manaCost.isPaid() && !new ManaCostBeingPaid(this.originalManaCost).isPaid()) {
this.originalCard.setSunburstValue(this.manaCost.getSunburst()); this.originalCard.setSunburstValue(this.manaCost.getSunburst());
this.done(); 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() {
}
} }

View File

@@ -1,14 +1,10 @@
package forge.control.input; package forge.control.input;
import forge.Card; import forge.Card;
import forge.Singletons;
import forge.card.cost.CostPartMana; import forge.card.cost.CostPartMana;
import forge.card.cost.CostPayment;
import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostBeingPaid;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
public class InputPayManaX extends InputPayManaBase { public class InputPayManaX extends InputPayManaBase {
@@ -18,49 +14,60 @@ public class InputPayManaX extends InputPayManaBase {
private final String strX; private final String strX;
private String colorsPaid; private String colorsPaid;
private final CostPartMana costMana; 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); super(game, sa0);
sa = sa0;
payment = payment0;
xPaid = 0; xPaid = 0;
colorX = sa.hasParam("XColor") ? sa.getParam("XColor") : ""; colorX = saPaidFor.hasParam("XColor") ? saPaidFor.getParam("XColor") : "";
colorsPaid = sa.getSourceCard().getColorsPaid(); colorsPaid = saPaidFor.getSourceCard().getColorsPaid();
costMana = costMana0; costMana = costMana0;
strX = Integer.toString(costMana.getXMana()); strX = Integer.toString(costMana.getXMana());
manaCost = new ManaCostBeingPaid(strX); 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 @Override
public void showMessage() { public void showMessage() {
if ((xPaid == 0 && costMana.isxCantBe0()) || (this.colorX.equals("")
&& !this.manaCost.toString().equals(strX))) {
ButtonUtil.enableOnlyCancel();
// only cancel if partially paid an X value // only cancel if partially paid an X value
// or X is 0, and x can't be 0 // or X is 0, and x can't be 0
if (!isPaid()) {
ButtonUtil.enableOnlyCancel();
} else { } else {
ButtonUtil.enableAllFocusOk(); ButtonUtil.enableAllFocusOk();
} }
StringBuilder msg = new StringBuilder("Pay X Mana Cost for "); 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."); msg.append(" Paid so far.");
if (costMana.isxCantBe0()) { if (!costMana.canXbe0()) {
msg.append(" X Can't be 0."); msg.append(" X Can't be 0.");
} }
CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString()); showMessage(msg.toString());
} }
// selectCard // selectCard
@Override @Override
public void selectCard(final Card card) { public void selectCard(final Card card) {
this.manaCost = activateManaAbility(sa, card, // don't allow here the cards that produce only wrong colors
this.colorX.isEmpty() ? this.manaCost : new ManaCostBeingPaid(this.colorX)); activateManaAbility(card, this.colorX.isEmpty() ? this.manaCost : new ManaCostBeingPaid(this.colorX));
}
@Override
protected void onManaAbilityPaid() {
if (this.manaCost.isPaid()) { if (this.manaCost.isPaid()) {
if (!this.colorsPaid.contains(this.manaCost.getColorsPaid())) { if (!this.colorsPaid.contains(this.manaCost.getColorsPaid())) {
this.colorsPaid += this.manaCost.getColorsPaid(); this.colorsPaid += this.manaCost.getColorsPaid();
@@ -68,49 +75,30 @@ public class InputPayManaX extends InputPayManaBase {
this.manaCost = new ManaCostBeingPaid(strX); this.manaCost = new ManaCostBeingPaid(strX);
this.xPaid++; this.xPaid++;
} }
if (Singletons.getModel().getMatch().getInput().getInput() == this) {
this.showMessage();
}
} }
@Override @Override
public void selectButtonCancel() { public void selectButtonCancel() {
this.stop(); this.stop();
payment.cancelCost();
Singletons.getControl().getPlayer().getZone(ZoneType.Battlefield).updateObservers();
} }
@Override @Override
public void selectButtonOK() { public void selectButtonOK() {
this.stop(); done();
payment.getCard().setXManaCostPaid(this.xPaid);
payment.paidCost(costMana);
payment.getCard().setColorsPaid(this.colorsPaid);
payment.getCard().setSunburstValue(this.colorsPaid.length());
} }
@Override @Override
public void selectManaPool(String color) { public void selectManaPool(String color) {
this.manaCost = activateManaAbility(color, sa, useManaFromPool(color, this.colorX.isEmpty() ? this.manaCost : new ManaCostBeingPaid(this.colorX));
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();
}
}
/* (non-Javadoc)
* @see forge.control.input.Input#isClassUpdated()
*/
@Override @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();
} }
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package forge.control.input;
import java.util.List;
import forge.Card;
import forge.CardLists;
import forge.Command;
import forge.Singletons;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.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
/**
* <p>
* Input_PayManaCost_Ability class.
* </p>
*
* @author Forge
* @version $Id: InputPayManaCostAbility.java 15673 2012-05-23 14:01:35Z ArsenalNut $
*/
public class InputPayReturnCost extends Input {
/**
* Constant <code>serialVersionUID=2685832214529141991L</code>.
*/
private static final long serialVersionUID = 8701146064257627671L;
private int numChosen = 0;
private int numRequired = 0;
private List<Card> choiceList;
private CostReturn returnCost;
private SpellAbility ability;
private Command paid;
private Command unpaid;
/**
* <p>
* Constructor for InputPayReturnCost.
* </p>
*
* @param cost
* a {@link forge.card.cost.CostSacrifice} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* @param paidCommand
* a {@link forge.Command} object.
* @param unpaidCommand
* a {@link forge.Command} object.
*/
public 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();
}
}
/**
* <p>
* unselectCard.
* </p>
*
* @param card
* a {@link forge.Card} object.
* @param zone
* a {@link forge.game.zone.PlayerZone} object.
*/
public void unselectCard(final Card card, final PlayerZone zone) {
if (this.returnCost.getList().contains(card)) {
this.numChosen--;
card.setUsedToPay(false);
this.returnCost.getList().remove(card);
this.choiceList.add(card);
this.showMessage();
}
}
/**
* <p>
* executes paid commmand.
* </p>
*
*/
public void done() {
this.stop();
// actually sacrifice the cards
for (Card selected : this.returnCost.getList()) {
selected.setUsedToPay(false);
Singletons.getModel().getGame().getAction().moveTo(ZoneType.Hand, selected);
}
this.returnCost.addListToHash(ability, "Returned");
this.paid.execute();
}
/**
* <p>
* executes unpaid commmand.
* </p>
*
*/
public void cancel() {
this.stop();
for (Card selected : this.returnCost.getList()) {
selected.setUsedToPay(false);
}
this.unpaid.execute();
}
@Override public void isClassUpdated() {
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package forge.control.input;
import java.util.List;
import forge.Card;
import forge.CardLists;
import forge.Command;
import forge.Singletons;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.CostSacrifice;
import forge.card.spellability.SpellAbility;
import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
//if cost is paid, Command.execute() is called
/**
* <p>
* Input_PayManaCost_Ability class.
* </p>
*
* @author Forge
* @version $Id: InputPayManaCostAbility.java 15673 2012-05-23 14:01:35Z ArsenalNut $
*/
public class InputPaySacCost extends Input {
/**
* Constant <code>serialVersionUID=2685832214529141991L</code>.
*/
private static final long serialVersionUID = 2685832214529141991L;
private int numChosen = 0;
private int numRequired = 0;
private List<Card> choiceList;
private CostSacrifice sacCost;
private SpellAbility ability;
private Command paid;
private Command unpaid;
/**
* <p>
* Constructor for Input_PayManaCost_Ability.
* </p>
*
* @param cost
* a {@link forge.card.cost.CostSacrifice} object.
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* @param paidCommand
* a {@link forge.Command} object.
* @param unpaidCommand
* a {@link forge.Command} object.
*/
public InputPaySacCost(final CostSacrifice cost, final SpellAbility sa, final Command paidCommand,
final Command unpaidCommand) {
final Card source = sa.getSourceCard();
this.ability = sa;
this.sacCost = cost;
Player human = Singletons.getControl().getPlayer();
this.choiceList = CardLists.getValidCards(human.getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), human, source);
String amountString = cost.getAmount();
this.numRequired = amountString.matches("[0-9][0-9]?") ? Integer.parseInt(amountString)
: CardFactoryUtil.xCount(source, source.getSVar(amountString));
this.paid = paidCommand;
this.unpaid = unpaidCommand;
}
/** {@inheritDoc} */
@Override
public void showMessage() {
final StringBuilder msg = new StringBuilder("Sacrifice ");
final int nLeft = this.numRequired - this.numChosen;
msg.append(nLeft).append(" ");
msg.append(this.sacCost.getDescriptiveType());
if (nLeft > 1) {
msg.append("s");
}
if (!this.sacCost.getList().isEmpty()) {
msg.append("\r\nSelected:\r\n");
for (Card selected : this.sacCost.getList()) {
msg.append(selected + "\r\n");
}
}
CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString());
if (nLeft > 0) {
ButtonUtil.enableOnlyCancel();
}
else {
ButtonUtil.enableAllFocusOk();
}
}
/** {@inheritDoc} */
@Override
public void selectButtonCancel() {
this.cancel();
}
/** {@inheritDoc} */
@Override
public void selectButtonOK() {
this.done();
}
/** {@inheritDoc} */
@Override
public void selectCard(final Card card) {
if (this.choiceList.contains(card) && this.numChosen < numRequired) {
this.numChosen++;
this.sacCost.addToList(card);
card.setUsedToPay(true);
this.choiceList.remove(card);
this.showMessage();
}
}
/**
* <p>
* unselectCard.
* </p>
*
* @param card
* a {@link forge.Card} object.
* @param zone
* a {@link forge.game.zone.PlayerZone} object.
*/
public void unselectCard(final Card card, final PlayerZone zone) {
if (this.sacCost.getList().contains(card)) {
this.numChosen--;
card.setUsedToPay(false);
this.sacCost.getList().remove(card);
this.choiceList.add(card);
this.showMessage();
}
}
/**
* <p>
* executes paid commmand.
* </p>
*
*/
public void done() {
this.stop();
// actually sacrifice the cards
for (Card selected : this.sacCost.getList()) {
selected.setUsedToPay(false);
Singletons.getModel().getGame().getAction().sacrifice(selected, this.ability);
}
this.sacCost.addListToHash(ability, "Sacrificed");
this.paid.execute();
}
/**
* <p>
* executes unpaid commmand.
* </p>
*
*/
public void cancel() {
this.stop();
for (Card selected : this.sacCost.getList()) {
selected.setUsedToPay(false);
}
this.unpaid.execute();
}
@Override public void isClassUpdated() {
}
}

View File

@@ -0,0 +1,5 @@
package forge.control.input;
public interface InputPayment extends InputSynchronized {
boolean isPaid();
}

View File

@@ -0,0 +1,37 @@
package forge.control.input;
import forge.Card;
public abstract class InputSelectCards extends InputSelectListBase<Card> {
private static final long serialVersionUID = -6609493252672573139L;
protected InputSelectCards(int min, int max) {
super(min, max);
}
@Override
public final void selectCard(final Card c) {
selectEntity(c);
}
/* (non-Javadoc)
* @see forge.control.input.InputSelectListBase#onSelectStateChanged(forge.GameEntity, boolean)
*/
@Override
protected void onSelectStateChanged(Card c, boolean newState) {
c.setUsedToPay(newState); // UI supports card highlighting though this abstraction-breaking mechanism
}
/* (non-Javadoc)
* @see forge.control.input.InputSyncronizedBase#afterStop()
*/
@Override
protected void afterStop() {
for(Card c : selected)
c.setUsedToPay(false);
super.afterStop(); // It's ultimatelly important to keep call to super class!
}
}

View File

@@ -0,0 +1,22 @@
package forge.control.input;
import java.util.List;
import forge.Card;
public class InputSelectCardsFromList extends InputSelectCards {
private static final long serialVersionUID = 6230360322294805986L;
private final List<Card> validChoices;
public InputSelectCardsFromList(int min, int max, List<Card> validCards) {
super(min, max);
this.validChoices = validCards;
}
@Override
protected final boolean isValidChoice(Card choice) {
return validChoices.contains(choice);
}
}

View File

@@ -0,0 +1,12 @@
package forge.control.input;
import java.util.List;
/**
* TODO: Write javadoc for this type.
*
*/
public interface InputSelectList<T> extends InputSynchronized {
boolean hasCancelled();
List<T> getSelected();
}

View File

@@ -7,22 +7,23 @@ import forge.GameEntity;
import forge.gui.match.CMatchUI; import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil; import forge.view.ButtonUtil;
/** public abstract class InputSelectListBase<T extends GameEntity> extends InputSyncronizedBase implements InputSelectList<T> {
* TODO: Write javadoc for this type.
*
*/
public abstract class InputSelectMany<T extends GameEntity> extends Input {
private static final long serialVersionUID = -2305549394512889450L; private static final long serialVersionUID = -2305549394512889450L;
protected final List<T> selected = new ArrayList<T>(); protected final List<T> selected;
protected boolean bCancelled = false;
protected final int min; protected final int min;
protected final int max; protected final int max;
public boolean allowUnselect = false;
private boolean allowCancelWithNotEmptyList = false;
private String message = "Source-Card-Name - Select %d more card(s)"; private String message = "Source-Card-Name - Select %d more card(s)";
protected InputSelectMany(int min, int max) {
protected InputSelectListBase(int min, int max) {
selected = new ArrayList<T>();
if (min > max) { if (min > max) {
throw new IllegalArgumentException("Min must not be greater than Max"); throw new IllegalArgumentException("Min must not be greater than Max");
} }
@@ -35,7 +36,7 @@ public abstract class InputSelectMany<T extends GameEntity> extends Input {
String msgToShow = getMessage(); String msgToShow = getMessage();
CMatchUI.SINGLETON_INSTANCE.showMessage(msgToShow); CMatchUI.SINGLETON_INSTANCE.showMessage(msgToShow);
boolean canCancel = (min == 0 && selected.isEmpty()) || canCancelWithSomethingSelected(); boolean canCancel = (min == 0 && selected.isEmpty()) || isCancelWithSelectedAllowed();
boolean canOk = hasEnoughTargets(); boolean canOk = hasEnoughTargets();
if (canOk && canCancel) { if (canOk && canCancel) {
@@ -65,20 +66,18 @@ public abstract class InputSelectMany<T extends GameEntity> extends Input {
@Override @Override
public final void selectButtonCancel() { public final void selectButtonCancel() {
// this.stop(); bCancelled = true;
Input next = onCancel(); // might add ability to stack from here
// if ( next != null ) {
// Singletons.getModel().getMatch().getInput().setInput(next);
// }
if (null == next) {
this.stop(); this.stop();
} else {
this.stopSetNext(next);
} }
// for a next use @Override
selected.clear(); public final boolean hasCancelled() {
return bCancelled;
}
@Override
public final List<T> getSelected() {
return selected;
} }
@Override @Override
@@ -87,24 +86,7 @@ public abstract class InputSelectMany<T extends GameEntity> extends Input {
// if an ability is put on stack before this input is stopped; // 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 // 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(); this.stop();
} else {
this.stopSetNext(next);
}
// for a next use
selected.clear();
}
@Override
public void isClassUpdated() {
} }
public void setMessage(String message0) { public void setMessage(String message0) {
@@ -112,27 +94,41 @@ public abstract class InputSelectMany<T extends GameEntity> extends Input {
} }
// must define these // must define these
protected abstract Input onDone();
protected abstract boolean isValidChoice(T choice); protected abstract boolean isValidChoice(T choice);
// might re-define later // might re-define later
protected Input onCancel() { return null; }
protected boolean canCancelWithSomethingSelected() { return false; }
protected boolean hasEnoughTargets() { return selected.size() >= min; } protected boolean hasEnoughTargets() { return selected.size() >= min; }
protected boolean hasAllTargets() { return selected.size() >= max; } 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) { protected void selectEntity(T c) {
if (!isValidChoice(c)) {
if (selected.contains(c) || !isValidChoice(c)) {
return; return;
} }
if ( selected.contains(c) ) {
if ( allowUnselect ) {
this.selected.remove(c);
onSelectStateChanged(c, false);
}
} else {
this.selected.add(c); this.selected.add(c);
this.showMessage(); onSelectStateChanged(c, true);
}
if (hasAllTargets()) { if (hasAllTargets()) {
selectButtonOK(); 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 ; }
} }

View File

@@ -1,17 +0,0 @@
package forge.control.input;
import forge.Card;
public abstract class InputSelectManyCards extends InputSelectMany<Card> {
private static final long serialVersionUID = -6609493252672573139L;
protected InputSelectManyCards(int min, int max) {
super(min, max);
}
@Override
public final void selectCard(final Card c) {
selectEntity(c);
}
}

View File

@@ -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<Player> {
private static final long serialVersionUID = -8209690791522735L;
protected final Function<List<Player>, Input> onComplete;
private final Predicate<Player> allowedFilter;
public InputSelectManyPlayers(final Predicate<Player> allowedRule, int min, int max, final Function<List<Player>, 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);
}
}

View File

@@ -0,0 +1,6 @@
package forge.control.input;
public interface InputSynchronized extends Input {
public void awaitLatchRelease();
}

View File

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

View File

@@ -19,9 +19,12 @@ package forge.deck;
import java.io.File; import java.io.File;
import org.apache.commons.lang.time.StopWatch;
import forge.deck.io.DeckGroupSerializer; import forge.deck.io.DeckGroupSerializer;
import forge.deck.io.DeckSerializer; import forge.deck.io.DeckSerializer;
import forge.deck.io.OldDeckParser; import forge.deck.io.OldDeckParser;
import forge.gui.toolbox.FSkin;
import forge.properties.NewConstants; import forge.properties.NewConstants;
import forge.util.storage.IStorage; import forge.util.storage.IStorage;
import forge.util.storage.StorageImmediatelySerialized; import forge.util.storage.StorageImmediatelySerialized;
@@ -44,6 +47,9 @@ public class CardCollections {
* @param file the file * @param file the file
*/ */
public CardCollections() { public CardCollections() {
FSkin.setProgessBarMessage("Loading decks");
StopWatch sw = new StopWatch();
sw.start();
this.constructed = new StorageImmediatelySerialized<Deck>(new DeckSerializer(new File(NewConstants.DECK_CONSTRUCTED_DIR), true)); this.constructed = new StorageImmediatelySerialized<Deck>(new DeckSerializer(new File(NewConstants.DECK_CONSTRUCTED_DIR), true));
this.draft = new StorageImmediatelySerialized<DeckGroup>(new DeckGroupSerializer(new File(NewConstants.DECK_DRAFT_DIR))); this.draft = new StorageImmediatelySerialized<DeckGroup>(new DeckGroupSerializer(new File(NewConstants.DECK_DRAFT_DIR)));
this.sealed = new StorageImmediatelySerialized<DeckGroup>(new DeckGroupSerializer(new File(NewConstants.DECK_SEALED_DIR))); this.sealed = new StorageImmediatelySerialized<DeckGroup>(new DeckGroupSerializer(new File(NewConstants.DECK_SEALED_DIR)));
@@ -51,8 +57,10 @@ public class CardCollections {
this.scheme = new StorageImmediatelySerialized<Deck>(new DeckSerializer(new File(NewConstants.DECK_SCHEME_DIR))); this.scheme = new StorageImmediatelySerialized<Deck>(new DeckSerializer(new File(NewConstants.DECK_SCHEME_DIR)));
this.plane = new StorageImmediatelySerialized<Deck>(new DeckSerializer(new File(NewConstants.DECK_PLANE_DIR))); this.plane = new StorageImmediatelySerialized<Deck>(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 // remove this after most people have been switched to new layout
final OldDeckParser oldParser = new OldDeckParser(this.constructed, this.draft, this.sealed, this.cube); final OldDeckParser oldParser = new OldDeckParser(this.constructed, this.draft, this.sealed, this.cube);
oldParser.tryParse(); oldParser.tryParse();

View File

@@ -486,24 +486,6 @@ public class GameAction {
final String recoverCost = recoverable.getKeyword().get(recoverable.getKeywordPosition("Recover")).split(":")[1]; final String recoverCost = recoverable.getKeyword().get(recoverable.getKeywordPosition("Recover")).split(":")[1];
final Cost cost = new Cost(recoverable, recoverCost, true); 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) { final SpellAbility abRecover = new AbilityActivated(recoverable, cost, null) {
private static final long serialVersionUID = 8858061639236920054L; private static final long serialVersionUID = 8858061639236920054L;
@@ -535,8 +517,10 @@ public class GameAction {
Player p = recoverable.getController(); Player p = recoverable.getController();
if (p.isHuman()) { if (p.isHuman()) {
GameActionUtil.payCostDuringAbilityResolve(p, abRecover, abRecover.getPayCosts(), if ( GameActionUtil.payCostDuringAbilityResolve(p, abRecover, abRecover.getPayCosts(), null, game) )
paidCommand, unpaidCommand, null, game); moveToHand(recoverable);
else
exile(recoverable);
} else { // computer } else { // computer
if (ComputerUtilCost.canPayCost(abRecover, p)) { if (ComputerUtilCost.canPayCost(abRecover, p)) {
ComputerUtil.playNoStack((AIPlayer)p, abRecover, game); ComputerUtil.playNoStack((AIPlayer)p, abRecover, game);

View File

@@ -2,7 +2,6 @@ package forge.game;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.Card; import forge.Card;
@@ -10,6 +9,7 @@ import forge.CardCharacteristicName;
import forge.CardColor; import forge.CardColor;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.FThreads;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType; import forge.card.ability.ApiType;
@@ -23,7 +23,6 @@ import forge.card.spellability.SpellAbilityRequirements;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.card.spellability.TargetSelection; import forge.card.spellability.TargetSelection;
import forge.card.staticability.StaticAbility; import forge.card.staticability.StaticAbility;
import forge.control.input.InputControl;
import forge.control.input.InputPayManaSimple; import forge.control.input.InputPayManaSimple;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
import forge.game.player.Player; import forge.game.player.Player;
@@ -37,17 +36,12 @@ import forge.gui.GuiChoose;
public class GameActionPlay { public class GameActionPlay {
private final GameState game; private final GameState game;
private InputControl matchInput;
public GameActionPlay(final GameState game0) { public GameActionPlay(final GameState game0) {
game = 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) { public final void playCardWithoutManaCost(final Card c, Player player) {
final List<SpellAbility> choices = c.getBasicSpells(); final List<SpellAbility> choices = c.getBasicSpells();
// TODO add Buyback, Kicker, ... , spells here // TODO add Buyback, Kicker, ... , spells here
@@ -71,6 +65,7 @@ public class GameActionPlay {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
*/ */
public final void playSpellAbilityWithoutPayingManaCost(final SpellAbility sa) { public final void playSpellAbilityWithoutPayingManaCost(final SpellAbility sa) {
FThreads.checkEDT("GameActionPlay.playSpellAbilityWithoutPayingManaCost", false);
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
setSplitCardState(source, sa); // Split card support setSplitCardState(source, sa); // Split card support
@@ -358,6 +353,7 @@ public class GameActionPlay {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
*/ */
public final void playSpellAbility(SpellAbility sa, Player activator) { public final void playSpellAbility(SpellAbility sa, Player activator) {
FThreads.checkEDT("Player.playSpellAbility", false);
sa.setActivatingPlayer(activator); sa.setActivatingPlayer(activator);
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
@@ -385,6 +381,7 @@ public class GameActionPlay {
ability = ability.getSubAbility(); ability = ability.getSubAbility();
} }
// System.out.println("Playing:" + sa.getDescription() + " of " + sa.getSourceCard() + " new = " + newAbility);
if (newAbility) { if (newAbility) {
final TargetSelection ts = new TargetSelection(sa.getTarget(), sa); final TargetSelection ts = new TargetSelection(sa.getTarget(), sa);
CostPayment payment = null; CostPayment payment = null;
@@ -403,14 +400,18 @@ public class GameActionPlay {
} else { } else {
manaCost = this.getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost())); manaCost = this.getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost()));
} }
if (!manaCost.isPaid()) {
FThreads.setInputAndWait(new InputPayManaSimple(game, sa, manaCost));
}
if (manaCost.isPaid()) { if (manaCost.isPaid()) {
if (sa.isSpell() && !source.isCopiedSpell()) { if (sa.isSpell() && !source.isCopiedSpell()) {
sa.setSourceCard(game.getAction().moveToStack(source)); sa.setSourceCard(game.getAction().moveToStack(source));
} }
game.getStack().add(sa); game.getStack().add(sa);
} else {
matchInput.setInput(new InputPayManaSimple(game, sa, manaCost));
} }
} }
} }
@@ -446,60 +447,19 @@ public class GameActionPlay {
} else { } else {
manaCost = this.getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost())); 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()) { if (manaCost.isPaid()) {
AbilityUtils.resolve(sa, false); 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. * Gets the convokable colors.
* *

View File

@@ -20,7 +20,6 @@ package forge.game;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -34,6 +33,7 @@ import forge.CardUtil;
import forge.Command; import forge.Command;
import forge.Constant; import forge.Constant;
import forge.CounterType; import forge.CounterType;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityFactory;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
@@ -57,10 +57,10 @@ import forge.card.spellability.AbilityManaPart;
import forge.card.spellability.AbilitySub; import forge.card.spellability.AbilitySub;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityRestriction; import forge.card.spellability.SpellAbilityRestriction;
import forge.control.input.Input;
import forge.control.input.InputPayDiscardCost;
import forge.control.input.InputPayManaExecuteCommands; 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.CardDamagedEvent;
import forge.game.event.LifeLossEvent; import forge.game.event.LifeLossEvent;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
@@ -367,29 +367,21 @@ public final class GameActionUtil {
ripple.execute(); 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);
}
/** /**
* <p> * TODO: Write javadoc for this method.
* payManaDuringAbilityResolve. * @param part
* </p> * @param source
* * @param sourceAbility
* @param message * @return
* 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.
*/ */
public static void payManaDuringAbilityResolve(final String message, final ManaCost spellManaCost, final Command paid, private static int getAmountFromPartX(CostPart part, Card source, SpellAbility sourceAbility) {
final Command unpaid) { String amountString = part.getAmount();
// temporarily disable the Resolve flag, so the user can payMana for the return StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : CardFactoryUtil.xCount(source, source.getSVar(amountString));
// 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);
} }
/** /**
@@ -407,8 +399,7 @@ public final class GameActionUtil {
* a {@link forge.Command} object. * a {@link forge.Command} object.
* @param sourceAbility TODO * @param sourceAbility TODO
*/ */
public static void payCostDuringAbilityResolve(final Player p, final SpellAbility ability, final Cost cost, final Command paid, public static boolean payCostDuringAbilityResolve(final Player p, final SpellAbility ability, final Cost cost, SpellAbility sourceAbility, final GameState game) {
final Command unpaid, SpellAbility sourceAbility, final GameState game) {
final Card source = ability.getSourceCard(); final Card source = ability.getSourceCard();
final List<CostPart> parts = cost.getCostParts(); final List<CostPart> parts = cost.getCostParts();
ArrayList<CostPart> remainingParts = new ArrayList<CostPart>(cost.getCostParts()); ArrayList<CostPart> remainingParts = new ArrayList<CostPart>(cost.getCostParts());
@@ -416,53 +407,40 @@ public final class GameActionUtil {
if (!parts.isEmpty()) { if (!parts.isEmpty()) {
costPart = parts.get(0); costPart = parts.get(0);
} }
String orString = ""; final String orString = sourceAbility == null ? "" : " (or: " + sourceAbility.getStackDescription() + ")";
if (sourceAbility != null) {
orString = " (or: " + sourceAbility.getStackDescription() + ")";
}
if (parts.isEmpty() || costPart.getAmount().equals("0")) { if (parts.isEmpty() || costPart.getAmount().equals("0")) {
if (GuiDialog.confirm(source, "Do you want to pay 0?" + orString)) { return GuiDialog.confirm(source, "Do you want to pay 0?" + orString);
paid.execute();
} else {
unpaid.execute();
}
return;
} }
boolean hasPaid = true; boolean hasPaid = true;
//the following costs do not need inputs //the following costs do not need inputs
for (CostPart part : parts) { for (CostPart part : parts) {
if (part instanceof CostPayLife) { boolean dontRemove = false;
String amountString = part.getAmount();
final int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) if (part instanceof CostPayLife) {
: 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)) { if (p.canPayLife(amount) && GuiDialog.confirm(source, "Do you want to pay " + amount + " life?" + orString)) {
p.payLife(amount, null); p.payLife(amount, null);
} else { } else {
hasPaid = false; hasPaid = false;
break; break;
} }
remainingParts.remove(part);
} }
else if (part instanceof CostDamage) { else if (part instanceof CostDamage) {
String amountString = part.getAmount(); int amount = getAmountFromPartX(part, source, sourceAbility);
final int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString)
: CardFactoryUtil.xCount(source, source.getSVar(amountString));
if (p.canPayLife(amount) && GuiDialog.confirm(source, "Do you want " + source + " to deal " + amount + " damage to you?")) { if (p.canPayLife(amount) && GuiDialog.confirm(source, "Do you want " + source + " to deal " + amount + " damage to you?")) {
p.addDamage(amount, source); p.addDamage(amount, source);
} else { } else {
hasPaid = false; hasPaid = false;
break; break;
} }
remainingParts.remove(part);
} }
else if (part instanceof CostPutCounter) { else if (part instanceof CostPutCounter) {
String amountString = part.getAmount();
CounterType counterType = ((CostPutCounter) part).getCounter(); CounterType counterType = ((CostPutCounter) part).getCounter();
int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) int amount = getAmountFromPartX(part, source, sourceAbility);
: CardFactoryUtil.xCount(source, source.getSVar(amountString));
String plural = amount > 1 ? "s" : ""; String plural = amount > 1 ? "s" : "";
if (GuiDialog.confirm(source, "Do you want to put " + amount + " " + counterType.getName() if (GuiDialog.confirm(source, "Do you want to put " + amount + " " + counterType.getName()
+ " counter" + plural + " on " + source + "?")) { + " counter" + plural + " on " + source + "?")) {
@@ -470,7 +448,7 @@ public final class GameActionUtil {
source.addCounter(counterType, amount, false); source.addCounter(counterType, amount, false);
} else { } else {
hasPaid = false; 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); + counterType.getName() + " counters put on it.", 2);
break; break;
} }
@@ -478,14 +456,11 @@ public final class GameActionUtil {
hasPaid = false; hasPaid = false;
break; break;
} }
remainingParts.remove(part);
} }
else if (part instanceof CostRemoveCounter) { else if (part instanceof CostRemoveCounter) {
String amountString = part.getAmount();
CounterType counterType = ((CostRemoveCounter) part).getCounter(); CounterType counterType = ((CostRemoveCounter) part).getCounter();
int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) int amount = getAmountFromPartX(part, source, sourceAbility);
: CardFactoryUtil.xCount(source, source.getSVar(amountString));
String plural = amount > 1 ? "s" : ""; String plural = amount > 1 ? "s" : "";
if (part.canPay(sourceAbility, source, p, cost, game) if (part.canPay(sourceAbility, source, p, cost, game)
&& GuiDialog.confirm(source, "Do you want to remove " + amount + " " + counterType.getName() && GuiDialog.confirm(source, "Do you want to remove " + amount + " " + counterType.getName()
@@ -495,7 +470,6 @@ public final class GameActionUtil {
hasPaid = false; hasPaid = false;
break; break;
} }
remainingParts.remove(part);
} }
else if (part instanceof CostExile) { 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?")) { if (GuiDialog.confirm(source, "Do you want to exile all cards in your graveyard?")) {
List<Card> cards = new ArrayList<Card>(p.getCardsIn(ZoneType.Graveyard)); List<Card> cards = new ArrayList<Card>(p.getCardsIn(ZoneType.Graveyard));
for (final Card card : cards) { for (final Card card : cards) {
Singletons.getModel().getGame().getAction().exile(card); p.getGame().getAction().exile(card);
} }
} else { } else {
hasPaid = false; hasPaid = false;
break; break;
} }
remainingParts.remove(part);
} else { } else {
CostExile costExile = (CostExile) part; CostExile costExile = (CostExile) part;
ZoneType from = costExile.getFrom(); ZoneType from = costExile.getFrom();
List<Card> list = CardLists.getValidCards(p.getCardsIn(from), part.getType().split(";"), p, source); List<Card> list = CardLists.getValidCards(p.getCardsIn(from), part.getType().split(";"), p, source);
final int nNeeded = AbilityUtils.calculateAmount(source, part.getAmount(), ability); final int nNeeded = AbilityUtils.calculateAmount(source, part.getAmount(), ability);
if (list.size() >= nNeeded) { if (list.size() < nNeeded) {
hasPaid = false;
break;
}
for (int i = 0; i < nNeeded; i++) { for (int i = 0; i < nNeeded; i++) {
final Card c = GuiChoose.oneOrNone("Exile from " + from, list); final Card c = GuiChoose.oneOrNone("Exile from " + from, list);
if (c != null) { if (c != null) {
list.remove(c); list.remove(c);
Singletons.getModel().getGame().getAction().exile(c); p.getGame().getAction().exile(c);
} else { } else {
hasPaid = false; hasPaid = false;
break; break;
} }
} }
} else {
hasPaid = false;
break;
}
} }
} }
@@ -550,73 +521,84 @@ public final class GameActionUtil {
GuiUtils.clearPanelSelections(); GuiUtils.clearPanelSelections();
GuiUtils.setPanelSelection(source); GuiUtils.setPanelSelection(source);
if (!GuiDialog.confirm(source, "Do you want to pay the sacrifice cost?")) { List<Card> toSac = p.getController().choosePermanentsToSacrifice(list, amount, ability, false, true);
if ( toSac.size() != amount ) {
hasPaid = false; hasPaid = false;
break; break;
} }
for(Card c : toSac) {
p.getGame().getAction().sacrifice(c, ability);
}
}
for (int i = 0; i < amount; i++) { else if (part instanceof CostReturn) {
if (list.isEmpty()) { List<Card> 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; hasPaid = false;
break; break;
} }
Object o = GuiChoose.one("Select a card to sacrifice", list); ((CostReturn)part).addListToHash(ability, "Returned");
if (o != null) { for(Card c : inp.getSelected()) {
final Card c = (Card) o; p.getGame().getAction().moveTo(ZoneType.Hand, c);
Singletons.getModel().getGame().getAction().sacrifice(c, ability);
list.remove(c);
} }
} }
else if (part instanceof CostDiscard) {
List<Card> 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); remainingParts.remove(part);
} }
else if (part instanceof CostPartMana && ((CostPartMana) part).getManaToPay().equals("0")) {
remainingParts.remove(part);
}
}
GuiUtils.clearPanelSelections(); GuiUtils.clearPanelSelections();
if (!hasPaid) {
unpaid.execute();
return;
}
if (remainingParts.isEmpty()) { if (remainingParts.isEmpty()) {
paid.execute(); return true;
return;
} }
if (remainingParts.size() > 1) { 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); 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 InputPayment toSet = new InputPayManaExecuteCommands(game, source + "\r\n", ability.getManaCost());
// at some point in time, it's possible to restore the InputPaySacCost-based input FThreads.setInputAndWait(toSet);
// interface for sacrifice costs (instead of the menu-based one above). return toSet.isPaid();
//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);
}
} }
// not restricted to combat damage, not restricted to dealing damage to // not restricted to combat damage, not restricted to dealing damage to

View File

@@ -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;
}
}
}
}

View File

@@ -95,6 +95,7 @@ public class GameState {
* Constructor. * Constructor.
* @param players2 * @param players2
* @param match0 * @param match0
* @param input
*/ */
public GameState(Iterable<LobbyPlayer> players2, GameType t, MatchController match0) { /* no more zones to map here */ public GameState(Iterable<LobbyPlayer> players2, GameType t, MatchController match0) { /* no more zones to map here */
type = t; type = t;

View File

@@ -129,10 +129,8 @@ public class MatchController {
public void startRound() { public void startRound() {
// Deal with circular dependencies here // Deal with circular dependencies here
input = new InputControl();
currentGame = Singletons.getModel().newGame(players.keySet(),gameType, this); currentGame = Singletons.getModel().newGame(players.keySet(),gameType, this);
input = new InputControl(currentGame);
currentGame.getActionPlay().setMatchInput(input);
Map<Player, PlayerStartConditions> startConditions = new HashMap<Player, PlayerStartConditions>(); Map<Player, PlayerStartConditions> startConditions = new HashMap<Player, PlayerStartConditions>();
for (Player p : currentGame.getPlayers()) { for (Player p : currentGame.getPlayers()) {
@@ -167,9 +165,11 @@ public class MatchController {
SDisplayUtil.showTab(EDocID.REPORT_LOG.getDoc()); SDisplayUtil.showTab(EDocID.REPORT_LOG.getDoc());
InputProxy inputControl = CMessage.SINGLETON_INSTANCE.getInputControl(); InputProxy inputControl = CMessage.SINGLETON_INSTANCE.getInputControl();
inputControl.setMatch(this);
input.addObserver(inputControl); input.addObserver(inputControl);
currentGame.getStack().addObserver(inputControl); currentGame.getStack().addObserver(inputControl);
currentGame.getPhaseHandler().addObserver(inputControl); currentGame.getPhaseHandler().addObserver(inputControl);
currentGame.getGameLog().addObserver(CLog.SINGLETON_INSTANCE); currentGame.getGameLog().addObserver(CLog.SINGLETON_INSTANCE);
currentGame.getStack().addObserver(CStack.SINGLETON_INSTANCE); currentGame.getStack().addObserver(CStack.SINGLETON_INSTANCE);
// some observers are set in CMatchUI.initMatch // some observers are set in CMatchUI.initMatch
@@ -178,8 +178,7 @@ public class MatchController {
GameNew.newGame(this, startConditions, currentGame, canRandomFoil); GameNew.newGame(this, startConditions, currentGame, canRandomFoil);
getInput().clearInput(); getInput().clearInput();
getInput().resetInput(); //getInput().setNewInput(currentGame);
getInput().setNewInput(currentGame);
// Thread thGame = new GameInputUpdatesThread(this, currentGame); // Thread thGame = new GameInputUpdatesThread(this, currentGame);

View File

@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -44,6 +45,7 @@ import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellPermanent; import forge.card.spellability.SpellPermanent;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.GameState; import forge.game.GameState;
import forge.game.phase.PhaseType;
import forge.game.player.AIPlayer; import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -762,5 +764,97 @@ public class AiController {
} }
return null; 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<Card> 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<Card> 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<Card> 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 );
}
} }

View File

@@ -3,7 +3,8 @@ package forge.game.ai;
import java.util.List; import java.util.List;
import forge.Card; import forge.Card;
import forge.control.input.Input; import forge.Singletons;
import forge.control.input.InputBase;
import forge.game.GameState; import forge.game.GameState;
import forge.game.phase.CombatUtil; import forge.game.phase.CombatUtil;
import forge.game.player.Player; import forge.game.player.Player;
@@ -12,7 +13,7 @@ import forge.game.player.Player;
* TODO: Write javadoc for this type. * TODO: Write javadoc for this type.
* *
*/ */
public class AiInputBlock extends Input { public class AiInputBlock extends InputBase {
private final GameState game; private final GameState game;
/** /**
@@ -38,6 +39,7 @@ public class AiInputBlock extends Input {
CombatUtil.orderMultipleCombatants(game.getCombat()); CombatUtil.orderMultipleCombatants(game.getCombat());
game.getPhaseHandler().setPlayersPriorityPermission(false); game.getPhaseHandler().setPlayersPriorityPermission(false);
stop(); // was not added to stack, so will be replaced by plain update
Singletons.getModel().getMatch().getInput().updateObservers();
} }
} }

View File

@@ -17,18 +17,8 @@
*/ */
package forge.game.ai; package forge.game.ai;
import java.util.List; import forge.FThreads;
import forge.control.input.InputBase;
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;
/** /**
* <p> * <p>
@@ -38,13 +28,11 @@ import forge.game.zone.ZoneType;
* @author Forge * @author Forge
* @version $Id$ * @version $Id$
*/ */
public class AiInputCommon extends Input { public class AiInputCommon extends InputBase {
/** Constant <code>serialVersionUID=-3091338639571662216L</code>. */ /** Constant <code>serialVersionUID=-3091338639571662216L</code>. */
private static final long serialVersionUID = -3091338639571662216L; private static final long serialVersionUID = -3091338639571662216L;
private final AiController computer; private final AiController computer;
private final AIPlayer player;
private final GameState game;
/** /**
* <p> * <p>
@@ -56,15 +44,13 @@ public class AiInputCommon extends Input {
*/ */
public AiInputCommon(final AiController iComputer) { public AiInputCommon(final AiController iComputer) {
this.computer = iComputer; this.computer = iComputer;
player = computer.getPlayer();
this.game = computer.getGame();
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void showMessage() { public final void showMessage() {
// should not think when the game is over // should not think when the game is over
if (game.isGameOver()) { if (computer.getGame().isGameOver()) {
return; return;
} }
@@ -76,104 +62,15 @@ public class AiInputCommon extends Input {
* \"Detailed Error Trace\" to the Forge forum."); * \"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<Card> 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(); } // getMessage();
/** final Runnable aiActions = new Runnable() {
* 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<Card> att = game.getCombat().getAttackers(); @Override
if (!att.isEmpty()) { public void run() {
game.getPhaseHandler().setCombat(true); computer.onPriorityRecieved();
} }
};
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<Card> landsWannaPlay = computer.getLandsToPlay();
while(landsWannaPlay != null && !landsWannaPlay.isEmpty() && player.canPlayLand(null)) {
Card land = computer.chooseBestLandToPlay(landsWannaPlay);
landsWannaPlay.remove(land);
player.playLand(land);
game.getPhaseHandler().setPriority(player);
}
}
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() { }
} }

View File

@@ -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()); ability.setActivatingPlayer(c.getController());
if (c.getController().isHuman()) { 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 } else { // computer
if (ComputerUtilCost.canPayCost(ability, c.getController())) { if (ComputerUtilCost.canPayCost(ability, c.getController())) {
ComputerUtil.playNoStack((AIPlayer)c.getController(), ability, game); ComputerUtil.playNoStack((AIPlayer)c.getController(), ability, game);
@@ -1194,12 +1168,11 @@ public class CombatUtil {
// during Declare_Attackers // during Declare_Attackers
game.getCombat().removeFromCombat(crd); game.getCombat().removeFromCombat(crd);
} }
if (bLast) { }
if (bLast)
PhaseUtil.handleAttackingTriggers(); PhaseUtil.handleAttackingTriggers();
} }
} }
}
}
/** /**
* <p> * <p>

View File

@@ -24,6 +24,7 @@ import java.util.Stack;
import com.esotericsoftware.minlog.Log; import com.esotericsoftware.minlog.Log;
import forge.Card; import forge.Card;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.game.GameState; import forge.game.GameState;
@@ -734,15 +735,25 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
nextPhase(); nextPhase();
return; return;
} else if (!game.getStack().hasSimultaneousStackEntries()) { } else if (!game.getStack().hasSimultaneousStackEntries()) {
Runnable proc = new Runnable(){
@Override public void run() {
game.getStack().resolveStack(); game.getStack().resolveStack();
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
}
};
if ( FThreads.isEDT() )
FThreads.invokeInNewThread(proc, true);
else
proc.run();
} }
} else { } else {
// pass the priority to other player // pass the priority to other player
this.pPlayerPriority = nextPlayer; this.pPlayerPriority = nextPlayer;
Singletons.getModel().getMatch().getInput().resetInput(); Singletons.getModel().getMatch().getInput().updateObservers();
} }
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
} }
/** /**
@@ -817,4 +828,9 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
this.planarDiceRolledthisTurn++; this.planarDiceRolledthisTurn++;
} }
public String debugPrintState() {
return String.format("%s's %s, priority of %s [%sP]", getPlayerTurn(), getPhase(), getPriorityPlayer(), isPlayerPriorityAllowed() ? "+" : "-");
}
} }

View File

@@ -30,7 +30,7 @@ import forge.CardPredicates.Presets;
import forge.CounterType; import forge.CounterType;
import forge.GameEntity; import forge.GameEntity;
import forge.Singletons; import forge.Singletons;
import forge.control.input.Input; import forge.control.input.InputBase;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCard;
import forge.game.player.Player; import forge.game.player.Player;
@@ -210,7 +210,7 @@ public class Untap extends Phase {
landList.get(0).untap(); landList.get(0).untap();
} }
} else { } else {
final Input target = new Input() { final InputBase target = new InputBase() {
private static final long serialVersionUID = 6653677835629939465L; private static final long serialVersionUID = 6653677835629939465L;
@Override @Override
@@ -250,7 +250,7 @@ public class Untap extends Phase {
ComputerUtilCard.getBestArtifactAI(artList).untap(); ComputerUtilCard.getBestArtifactAI(artList).untap();
} }
} else { } else {
final Input target = new Input() { final InputBase target = new InputBase() {
private static final long serialVersionUID = 5555427219659889707L; private static final long serialVersionUID = 5555427219659889707L;
@Override @Override
@@ -290,7 +290,7 @@ public class Untap extends Phase {
creatures.get(0).untap(); creatures.get(0).untap();
} }
} else { } else {
final Input target = new Input() { final InputBase target = new InputBase() {
private static final long serialVersionUID = 5555427219659889707L; private static final long serialVersionUID = 5555427219659889707L;
@Override @Override

View File

@@ -21,14 +21,13 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.FThreads;
import forge.CardPredicates.Presets; import forge.CardPredicates.Presets;
import forge.Command;
import forge.CounterType; import forge.CounterType;
import forge.Singletons; import forge.Singletons;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
@@ -39,8 +38,9 @@ import forge.card.spellability.Ability;
import forge.card.spellability.AbilityManaPart; import forge.card.spellability.AbilityManaPart;
import forge.card.spellability.AbilityStatic; import forge.card.spellability.AbilityStatic;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.InputBase;
import forge.control.input.InputSelectManyCards; import forge.control.input.InputPayManaExecuteCommands;
import forge.control.input.InputSelectCards;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtil;
@@ -174,18 +174,6 @@ public class Upkeep extends Phase {
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
final Card c = list.get(i); final Card c = list.get(i);
if (c.hasStartOfKeyword("(Echo unpaid)")) { 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 Ability blankAbility = Upkeep.BlankAbility(c, c.getEchoCost());
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
@@ -198,7 +186,9 @@ public class Upkeep extends Phase {
Player controller = c.getController(); Player controller = c.getController();
if (controller.isHuman()) { if (controller.isHuman()) {
Cost cost = new Cost(c, c.getEchoCost().trim(), true); 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 } else { // computer
if (ComputerUtilCost.canPayCost(blankAbility, controller)) { if (ComputerUtilCost.canPayCost(blankAbility, controller)) {
ComputerUtil.playNoStack((AIPlayer)controller, blankAbility, game); ComputerUtil.playNoStack((AIPlayer)controller, blankAbility, game);
@@ -279,44 +269,34 @@ public class Upkeep extends Phase {
final String[] k = ability.split(" pay "); final String[] k = ability.split(" pay ");
final ManaCost upkeepCost = new ManaCost(new ManaCostParser(k[1])); final ManaCost upkeepCost = new ManaCost(new ManaCostParser(k[1]));
final Command unpaidCommand = new Command() { final String sb = "Upkeep for " + c;
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 Ability upkeepAbility = new Ability(c, ManaCost.ZERO) { final Ability upkeepAbility = new Ability(c, ManaCost.ZERO) {
@Override @Override
public void resolve() { public void resolve() {
final boolean isUpkeepPaid;
if (controller.isHuman()) { 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 } 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); ComputerUtil.playNoStack((AIPlayer)controller, aiPaid, game);
} else { }
}
if( !isUpkeepPaid ) {
if (c.getName().equals("Cosmic Horror")) { if (c.getName().equals("Cosmic Horror")) {
controller.addDamage(7, c); controller.addDamage(7, c);
} }
game.getAction().destroy(c); game.getAction().destroy(c);
} }
}
} }
}; };
upkeepAbility.setActivatingPlayer(controller); upkeepAbility.setActivatingPlayer(controller);
upkeepAbility.setStackDescription(sb.toString()); upkeepAbility.setStackDescription(sb);
upkeepAbility.setDescription(sb.toString()); upkeepAbility.setDescription(sb);
game.getStack().addSimultaneousStackEntry(upkeepAbility); game.getStack().addSimultaneousStackEntry(upkeepAbility);
} // destroy } // destroy
@@ -341,18 +321,6 @@ public class Upkeep extends Phase {
} }
final String upkeepCost = cost; 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); final Ability blankAbility = Upkeep.BlankAbility(c, upkeepCost);
blankAbility.setActivatingPlayer(controller); blankAbility.setActivatingPlayer(controller);
@@ -360,11 +328,11 @@ public class Upkeep extends Phase {
@Override @Override
public void resolve() { public void resolve() {
if (controller.isHuman()) { if (controller.isHuman()) {
GameActionUtil.payCostDuringAbilityResolve(controller, blankAbility, blankAbility.getPayCosts(), if ( !GameActionUtil.payCostDuringAbilityResolve(controller, blankAbility, blankAbility.getPayCosts(), this, game))
paidCommand, unpaidCommand, this, game); game.getAction().sacrifice(c, null);
} else { // computer } else { // computer
if (ComputerUtilCost.shouldPayCost(controller, c, upkeepCost) && ComputerUtilCost.canPayCost(blankAbility, controller)) { 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 { } else {
game.getAction().sacrifice(c, null); game.getAction().sacrifice(c, null);
} }
@@ -386,34 +354,25 @@ public class Upkeep extends Phase {
final String[] l = k[1].split(" pay "); final String[] l = k[1].split(" pay ");
final ManaCost upkeepCost = new ManaCost(new ManaCostParser(l[1])); final ManaCost upkeepCost = new ManaCost(new ManaCostParser(l[1]));
final Command unpaidCommand = new Command() { final String sb = "Damage upkeep for " + c;
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 Ability upkeepAbility = new Ability(c, ManaCost.ZERO) { final Ability upkeepAbility = new Ability(c, ManaCost.ZERO) {
@Override @Override
public void resolve() { public void resolve() {
boolean isUpkeepPaid = false;
if (controller.isHuman()) { 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 } else { // computers
if (ComputerUtilCost.canPayCost(aiPaid, controller) final Ability aiPaid = Upkeep.BlankAbility(c, upkeepCost.toString());
&& (ComputerUtilCombat.predictDamageTo(controller, upkeepDamage, c, false) > 0)) { if (ComputerUtilCost.canPayCost(aiPaid, controller) && ComputerUtilCombat.predictDamageTo(controller, upkeepDamage, c, false) > 0) {
ComputerUtil.playNoStack((AIPlayer)controller, aiPaid, game); ComputerUtil.playNoStack((AIPlayer)controller, aiPaid, game);
} else { isUpkeepPaid = true;
controller.addDamage(upkeepDamage, c);
} }
} }
if (!isUpkeepPaid) {
controller.addDamage(upkeepDamage, c);
}
} }
}; };
upkeepAbility.setActivatingPlayer(controller); upkeepAbility.setActivatingPlayer(controller);
@@ -478,27 +437,20 @@ public class Upkeep extends Phase {
@Override @Override
public void resolve() { public void resolve() {
final List<Card> targets = CardLists.getTargetableCards(abyssGetTargets, this); final List<Card> targets = CardLists.getTargetableCards(abyssGetTargets, this);
final Input chooseArt = new InputSelectManyCards(1, 1) { if (player.isHuman() && targets.size() > 0) {
final InputSelectCards chooseArt = new InputSelectCards(1, 1) {
private static final long serialVersionUID = 4820011040853968644L; private static final long serialVersionUID = 4820011040853968644L;
@Override
public String getMessage() {
return abyss.getName() + " - Select one nonartifact creature to destroy";
}
@Override @Override
protected boolean isValidChoice(Card choice) { protected boolean isValidChoice(Card choice) {
return choice.isCreature() && !choice.isArtifact() && canTarget(choice) && choice.getController() == player; 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) { chooseArt.setMessage(abyss.getName() + " - Select one nonartifact creature to destroy");
Singletons.getModel().getMatch().getInput().setInput(chooseArt); // Input FThreads.setInputAndWait(chooseArt); // Input
if (!chooseArt.hasCancelled()) {
game.getAction().destroyNoRegeneration(chooseArt.getSelected().get(0));
}
} else { // computer } else { // computer
final List<Card> indestruct = CardLists.getKeyword(targets, "Indestructible"); final List<Card> indestruct = CardLists.getKeyword(targets, "Indestructible");
@@ -617,9 +569,7 @@ public class Upkeep extends Phase {
final Player player = game.getPhaseHandler().getPlayerTurn(); final Player player = game.getPhaseHandler().getPlayerTurn();
final List<Card> cards = player.getCardsIn(ZoneType.Battlefield, "Demonic Hordes"); final List<Card> cards = player.getCardsIn(ZoneType.Battlefield, "Demonic Hordes");
for (int i = 0; i < cards.size(); i++) { for (final Card c : cards) {
final Card c = cards.get(i);
final Ability cost = new Ability(c, new ManaCost(new ManaCostParser("B B B"))) { final Ability cost = new Ability(c, new ManaCost(new ManaCostParser("B B B"))) {
@Override @Override
@@ -627,7 +577,7 @@ public class Upkeep extends Phase {
} }
}; // end cost ability }; // end cost ability
final Ability noPay = new Ability(c, ManaCost.ZERO) { final Ability unpaidHordesAb = new Ability(c, ManaCost.ZERO) {
@Override @Override
public void resolve() { public void resolve() {
final List<Card> playerLand = player.getLandsInPlay(); final List<Card> playerLand = player.getLandsInPlay();
@@ -648,16 +598,14 @@ public class Upkeep extends Phase {
final Player cp = c.getController(); final Player cp = c.getController();
if (cp.isHuman()) { if (cp.isHuman()) {
final String question = "Pay Demonic Hordes upkeep cost?";
if (GuiDialog.confirm(c, question)) {
final Ability pay = new Ability(c, ManaCost.ZERO) { final Ability pay = new Ability(c, ManaCost.ZERO) {
@Override @Override
public void resolve() { public void resolve() {
if (game.getZoneOf(c).is(ZoneType.Battlefield)) { if (game.getZoneOf(c).is(ZoneType.Battlefield)) {
final StringBuilder coststring = new StringBuilder(); InputPayManaExecuteCommands inp = new InputPayManaExecuteCommands(game, "Pay Demonic Hordes upkeep cost", cost.getManaCost() /*, true */);
coststring.append("Pay cost for ").append(c).append("\r\n"); FThreads.setInputAndWait(inp);
GameActionUtil.payManaDuringAbilityResolve(coststring.toString(), cost.getManaCost(), if ( !inp.isPaid() )
Command.BLANK, Command.BLANK); unpaidHordesAb.resolve();
} }
} // end resolve() } // end resolve()
}; // end pay ability }; // end pay ability
@@ -665,19 +613,9 @@ public class Upkeep extends Phase {
pay.setDescription("Demonic Hordes - Upkeep Cost"); pay.setDescription("Demonic Hordes - Upkeep Cost");
game.getStack().addSimultaneousStackEntry(pay); 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);
}
} // end human } // end human
else { // computer else { // computer
noPay.setActivatingPlayer(cp); unpaidHordesAb.setActivatingPlayer(cp);
if (ComputerUtilCost.canPayCost(cost, (AIPlayer) cp)) { if (ComputerUtilCost.canPayCost(cost, (AIPlayer) cp)) {
final Ability computerPay = new Ability(c, ManaCost.ZERO) { final Ability computerPay = new Ability(c, ManaCost.ZERO) {
@Override @Override
@@ -689,8 +627,8 @@ public class Upkeep extends Phase {
game.getStack().addSimultaneousStackEntry(computerPay); game.getStack().addSimultaneousStackEntry(computerPay);
} else { } else {
noPay.setStackDescription("Demonic Hordes - Upkeep Cost"); unpaidHordesAb.setStackDescription("Demonic Hordes - Upkeep Cost");
game.getStack().addSimultaneousStackEntry(noPay); game.getStack().addSimultaneousStackEntry(unpaidHordesAb);
} }
} // end computer } // end computer
@@ -1061,7 +999,7 @@ public class Upkeep extends Phase {
list.remove(toTap); list.remove(toTap);
} }
} else { } else {
Singletons.getModel().getMatch().getInput().setInput(new Input() { Singletons.getModel().getMatch().getInput().setInput(new InputBase() {
private static final long serialVersionUID = 5313424586016061612L; private static final long serialVersionUID = 5313424586016061612L;
@Override @Override

View File

@@ -39,6 +39,7 @@ import forge.CardPredicates.Presets;
import forge.CardUtil; import forge.CardUtil;
import forge.Constant.Preferences; import forge.Constant.Preferences;
import forge.CounterType; import forge.CounterType;
import forge.FThreads;
import forge.GameEntity; import forge.GameEntity;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityFactory;
@@ -1624,6 +1625,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
*/ */
protected final void doDiscard(final Card c, final SpellAbility sa) { protected final void doDiscard(final Card c, final SpellAbility sa) {
FThreads.checkEDT("Player.doDiscard", false);
// TODO: This line should be moved inside CostPayment somehow // TODO: This line should be moved inside CostPayment somehow
/*if (sa != null) { /*if (sa != null) {
sa.addCostToHashList(c, "Discarded"); sa.addCostToHashList(c, "Discarded");
@@ -1846,6 +1848,7 @@ public abstract class Player extends GameEntity implements Comparable<Player> {
* a {@link forge.Card} object. * a {@link forge.Card} object.
*/ */
public final void playLand(final Card land) { public final void playLand(final Card land) {
FThreads.checkEDT("Player.playSpellAbility", false);
if (this.canPlayLand(land)) { if (this.canPlayLand(land)) {
land.setController(this, 0); land.setController(this, 0);
game.getAction().moveTo(this.getZone(ZoneType.Battlefield), land); game.getAction().moveTo(this.getZone(ZoneType.Battlefield), land);

View File

@@ -11,12 +11,15 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card; import forge.Card;
import forge.FThreads;
import forge.GameEntity; import forge.GameEntity;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.Input;
import forge.control.input.InputBlock; import forge.control.input.InputBlock;
import forge.control.input.InputCleanup; import forge.control.input.InputCleanup;
import forge.control.input.InputPassPriority; import forge.control.input.InputPassPriority;
import forge.control.input.InputSelectCards;
import forge.control.input.InputSelectCardsFromList;
import forge.deck.CardPool; import forge.deck.CardPool;
import forge.deck.Deck; import forge.deck.Deck;
import forge.deck.DeckSection; import forge.deck.DeckSection;
@@ -212,26 +215,18 @@ public class PlayerControllerHuman extends PlayerController {
*/ */
@Override @Override
public List<Card> choosePermanentsToSacrifice(List<Card> validTargets, int amount, SpellAbility sa, boolean destroy, boolean isOptional) { public List<Card> choosePermanentsToSacrifice(List<Card> validTargets, int amount, SpellAbility sa, boolean destroy, boolean isOptional) {
List<Card> result = new ArrayList<Card>(); int max = Math.min(amount, validTargets.size());
if (max == 0)
return new ArrayList<Card>();
for (int i = 0; i < amount; i++) { InputSelectCards inp = new InputSelectCardsFromList(isOptional ? 0 : amount, max, validTargets);
if (validTargets.isEmpty()) { // TODO: Either compose a message here, or pass it as parameter from caller.
break; inp.setMessage("Select %d card(s) to sacrifice");
}
Card c; FThreads.setInputAndWait(inp);
if (isOptional) { if( inp.hasCancelled() )
c = GuiChoose.oneOrNone("Select a card to sacrifice", validTargets); return new ArrayList<Card>();
} else { else return inp.getSelected();
c = GuiChoose.one("Select a card to sacrifice", validTargets);
}
if (c != null) {
result.add(c);
validTargets.remove(c);
} else {
return result;
}
}
return result;
} }
@Override @Override
@@ -321,10 +316,19 @@ public class PlayerControllerHuman extends PlayerController {
@Override @Override
public List<Card> chooseCardsToDiscardFrom(Player p, SpellAbility sa, List<Card> valid, int minDiscard) { public List<Card> chooseCardsToDiscardFrom(Player p, SpellAbility sa, List<Card> valid, int minDiscard) {
if ( p != getPlayer() ) {
int cntToKeepInHand = minDiscard == 0 ? -1 : valid.size() - minDiscard; int cntToKeepInHand = minDiscard == 0 ? -1 : valid.size() - minDiscard;
return GuiChoose.order("Choose cards to Discard", "Discarded", cntToKeepInHand, valid, null, null); 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 @Override
public Card chooseCardToDredge(List<Card> dredgers) { public Card chooseCardToDredge(List<Card> dredgers) {
if (GuiDialog.confirm(null, "Do you want to dredge?", false)) { if (GuiDialog.confirm(null, "Do you want to dredge?", false)) {

View File

@@ -24,6 +24,7 @@ import forge.CardLists;
import forge.Singletons; import forge.Singletons;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.Input;
import forge.control.input.InputBase;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI; import forge.gui.match.CMatchUI;
@@ -54,12 +55,12 @@ public final class PlayerUtil {
* a {@link java.lang.String} object. * a {@link java.lang.String} object.
* @param sa * @param sa
* a {@link forge.card.spellability.SpellAbility} object. * 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 * @since 1.0.15
*/ */
public static Input inputDiscardNumUnless(final int nCards, final String uType, final SpellAbility sa) { public static Input inputDiscardNumUnless(final int nCards, final String uType, final SpellAbility sa) {
final SpellAbility sp = sa; final SpellAbility sp = sa;
final Input target = new Input() { final Input target = new InputBase() {
private static final long serialVersionUID = 8822292413831640944L; private static final long serialVersionUID = 8822292413831640944L;
private int n = 0; private int n = 0;
@@ -111,12 +112,12 @@ public final class PlayerUtil {
* a int. * a int.
* @param sa * @param sa
* a {@link forge.card.spellability.SpellAbility} object. * 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 * @since 1.0.15
*/ */
public static Input inputDiscard(final int nCards, final SpellAbility sa) { public static Input inputDiscard(final int nCards, final SpellAbility sa) {
final SpellAbility sp = sa; final SpellAbility sp = sa;
final Input target = new Input() { final Input target = new InputBase() {
private static final long serialVersionUID = -329993322080934435L; private static final long serialVersionUID = -329993322080934435L;
private int n = 0; private int n = 0;
@@ -162,7 +163,7 @@ public final class PlayerUtil {
* a int. * a int.
* @param type * @param type
* a {@link java.lang.String} object. * 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 * @since 1.0.15
*/ */
public static Input inputSacrificePermanents(final int nCards, final String type) { public static Input inputSacrificePermanents(final int nCards, final String type) {
@@ -182,11 +183,11 @@ public final class PlayerUtil {
* a {@link forge.CardList} object. * a {@link forge.CardList} object.
* @param message * @param message
* a {@link java.lang.String} object. * 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 * @since 1.0.15
*/ */
public static Input inputSacrificePermanentsFromList(final int nCards, final List<Card> list, final String message) { public static Input inputSacrificePermanentsFromList(final int nCards, final List<Card> list, final String message) {
final Input target = new Input() { final Input target = new InputBase() {
private static final long serialVersionUID = 1981791992623774490L; private static final long serialVersionUID = 1981791992623774490L;
private int n = 0; private int n = 0;

View File

@@ -22,21 +22,18 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Stack; import java.util.Stack;
import com.esotericsoftware.minlog.Log; import com.esotericsoftware.minlog.Log;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.FThreads;
import forge.CardPredicates.Presets; import forge.CardPredicates.Presets;
import forge.Command;
import forge.Singletons; import forge.Singletons;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.cardfactory.CardFactory; import forge.card.cardfactory.CardFactory;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostBeingPaid;
import forge.card.mana.ManaCostParser;
import forge.card.spellability.Ability; import forge.card.spellability.Ability;
import forge.card.spellability.AbilityStatic; import forge.card.spellability.AbilityStatic;
import forge.card.spellability.AbilityTriggered; import forge.card.spellability.AbilityTriggered;
@@ -48,7 +45,7 @@ import forge.card.spellability.TargetChoices;
import forge.card.spellability.TargetSelection; import forge.card.spellability.TargetSelection;
import forge.card.trigger.Trigger; import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.control.input.Input; import forge.control.input.InputBase;
import forge.control.input.InputPayManaExecuteCommands; import forge.control.input.InputPayManaExecuteCommands;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.GameState; import forge.game.GameState;
@@ -295,95 +292,6 @@ public class MagicStack extends MyObservable {
} }
} }
/**
* <p>
* getMultiKickerSpellCostChange.
* </p>
*
* @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
/**
* <p>
* getReplicateSpellCostChange.
* </p>
*
* @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;
}
/** /**
* <p> * <p>
@@ -394,6 +302,7 @@ public class MagicStack extends MyObservable {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
*/ */
public final void add(final SpellAbility sp) { public final void add(final SpellAbility sp) {
FThreads.checkEDT("MagicStack.add", false);
final ArrayList<TargetChoices> chosenTargets = sp.getAllTargetChoices(); final ArrayList<TargetChoices> chosenTargets = sp.getAllTargetChoices();
if (sp.isManaAbility()) { // Mana Abilities go straight through if (sp.isManaAbility()) { // Mana Abilities go straight through
@@ -478,8 +387,30 @@ public class MagicStack extends MyObservable {
} else if (sp.isXCost()) { } else if (sp.isXCost()) {
// TODO: convert any X costs to use abCost so it happens earlier // TODO: convert any X costs to use abCost so it happens earlier
final SpellAbility sa = sp; final SpellAbility sa = sp;
final ManaCost mc = new ManaCost( new ManaCostParser(Integer.toString(sa.getXManaCost()))); final int xCost = sa.getXManaCost();
final Ability ability = new Ability(sp.getSourceCard(), mc) { Player player = sp.getSourceCard().getController();
if (player.isHuman()) {
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 @Override
public void resolve() { public void resolve() {
final Card crd = this.getSourceCard(); final Card crd = this.getSourceCard();
@@ -487,40 +418,6 @@ public class MagicStack extends MyObservable {
} }
}; };
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();
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));
} else {
// computer
final int neededDamage = CardFactoryUtil.getNeededXDamage(sa);
while (ComputerUtilCost.canPayCost(ability, player) && (neededDamage != sa.getSourceCard().getXManaCostPaid())) { while (ComputerUtilCost.canPayCost(ability, player) && (neededDamage != sa.getSourceCard().getXManaCostPaid())) {
ComputerUtil.playNoStack((AIPlayer)player, ability, game); ComputerUtil.playNoStack((AIPlayer)player, ability, game);
} }
@@ -532,59 +429,38 @@ public class MagicStack extends MyObservable {
// both X and multi is not supported yet // both X and multi is not supported yet
final SpellAbility sa = sp; final SpellAbility sa = sp;
final Ability ability = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) { final Ability abilityIncreaseMultikicker = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) {
@Override @Override
public void resolve() { public void resolve() {
this.getSourceCard().addMultiKickerMagnitude(1); 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(); Player activating = sp.getActivatingPlayer();
if (activating.isHuman()) { if (activating.isHuman()) {
sa.getSourceCard().addMultiKickerMagnitude(-1); 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 { } else {
// computer // computer
while (ComputerUtilCost.canPayCost(ability, activating)) { while (ComputerUtilCost.canPayCost(abilityIncreaseMultikicker, activating)) {
ComputerUtil.playNoStack((AIPlayer)activating, ability, game); ComputerUtil.playNoStack((AIPlayer)activating, abilityIncreaseMultikicker, game);
} }
this.push(sa); 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(); Player controller = sp.getSourceCard().getController();
if (controller.isHuman()) { if (controller.isHuman()) {
sa.getSourceCard().addReplicateMagnitude(-1); 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 { } else {
// computer // computer
while (ComputerUtilCost.canPayCost(ability, controller)) { while (ComputerUtilCost.canPayCost(ability, controller)) {
@@ -829,17 +694,18 @@ public class MagicStack extends MyObservable {
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
curResolvingCard = source; 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? // TODO: Spell fizzles, what's the best way to alert player?
Log.debug(source.getName() + " ability fizzles."); Log.debug(source.getName() + " ability fizzles.");
game.getGameLog().add("ResolveStack", source.getName() + " ability fizzles.", 2);
this.finishResolving(sa, true); this.finishResolving(sa, true);
} else if (sa.getApi() != null) { } else if (sa.getApi() != null) {
game.getGameLog().add("ResolveStack", sa.getStackDescription(), 2);
AbilityUtils.handleRemembering(sa); AbilityUtils.handleRemembering(sa);
AbilityUtils.resolve(sa, true); AbilityUtils.resolve(sa, true);
} else { } else {
game.getGameLog().add("ResolveStack", sa.getStackDescription(), 2);
sa.resolve(); sa.resolve();
this.finishResolving(sa, false); this.finishResolving(sa, false);
// do creatures ETB from here? // do creatures ETB from here?
@@ -867,7 +733,7 @@ public class MagicStack extends MyObservable {
if (creats.size() != 0) { if (creats.size() != 0) {
haunterDiesWork.setDescription(""); haunterDiesWork.setDescription("");
final Input target = new Input() { final InputBase target = new InputBase() {
private static final long serialVersionUID = 1981791992623774490L; private static final long serialVersionUID = 1981791992623774490L;
@Override @Override
@@ -1290,17 +1156,10 @@ public class MagicStack extends MyObservable {
ComputerUtil.playStack(sa, (AIPlayer) activePlayer, game); ComputerUtil.playStack(sa, (AIPlayer) activePlayer, game);
} }
} else { } else {
// If only one, just add as necessary List<SpellAbility> orderedSAs = activePlayerSAs;
if (activePlayerSAs.size() == 1) { if (activePlayerSAs.size() > 1) { // give a dual list form to create instead of needing to do it one at a time
SpellAbility next = activePlayerSAs.get(0); orderedSAs = GuiChoose.order("Select order for Simultaneous Spell Abilities", "Resolve first", 0, activePlayerSAs, null, null);
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<SpellAbility> orderedSAs = GuiChoose.order("Select order for Simultaneous Spell Abilities", "Resolve first", 0, activePlayerSAs, null, null);
int size = orderedSAs.size(); int size = orderedSAs.size();
for (int i = size - 1; i >= 0; i--) { for (int i = size - 1; i >= 0; i--) {
SpellAbility next = orderedSAs.get(i); SpellAbility next = orderedSAs.get(i);
@@ -1311,7 +1170,6 @@ public class MagicStack extends MyObservable {
} }
} }
} }
}
} }

View File

@@ -94,7 +94,7 @@ public class GuiChoose {
} }
public static <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices) { public static <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices) {
if (null == choices || 0 == choices.size()) { if (null == choices || choices.isEmpty()) {
if (0 == min) { if (0 == min) {
return new ArrayList<T>(); return new ArrayList<T>();
} else { } else {

View File

@@ -44,6 +44,7 @@ import forge.CardPredicates;
import forge.CardUtil; import forge.CardUtil;
import forge.Constant; import forge.Constant;
import forge.CounterType; import forge.CounterType;
import forge.FThreads;
import forge.Singletons; import forge.Singletons;
import forge.card.CardType; import forge.card.CardType;
import forge.card.spellability.AbilityManaPart; import forge.card.spellability.AbilityManaPart;
@@ -611,7 +612,7 @@ public final class GuiDisplayUtil {
return; return;
} }
Card forgeCard = c.toForgeCard(p); final Card forgeCard = c.toForgeCard(p);
final GameState game = Singletons.getModel().getGame(); final GameState game = Singletons.getModel().getGame();
if (forgeCard.getType().contains("Land")) { if (forgeCard.getType().contains("Land")) {
@@ -628,10 +629,15 @@ public final class GuiDisplayUtil {
return; // happens if cancelled return; // happens if cancelled
} }
FThreads.invokeInNewThread(new Runnable() {
@Override
public void run() {
sa.setActivatingPlayer(p); sa.setActivatingPlayer(p);
game.getAction().moveToHand(forgeCard); // this is really needed game.getAction().moveToHand(forgeCard); // this is really needed
game.getActionPlay().playSpellAbilityWithoutPayingManaCost(sa); game.getActionPlay().playSpellAbilityWithoutPayingManaCost(sa);
} }
});
}
} }

View File

@@ -21,10 +21,12 @@ import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import forge.Card; import forge.Card;
import forge.Singletons; import forge.FThreads;
import forge.control.input.Input; 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.game.player.Player;
import forge.view.ButtonUtil;
/** /**
* <p> * <p>
@@ -38,36 +40,48 @@ public class InputProxy implements Observer {
/** The input. */ /** The input. */
private Input input; private Input input;
private boolean valid = false; private MatchController match = null;
public void setMatch(MatchController matchController) {
match = matchController;
}
@Override @Override
public final synchronized void update(final Observable observable, final Object obj) { public final synchronized void update(final Observable observable, final Object obj) {
ButtonUtil.disableAll(); this.input = null;
valid = false; 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);
* <p> return;
* Setter for the field <code>input</code>.
* </p>
*
* @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
} }
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();
}
}
/** /**
* <p> * <p>
* selectButtonOK. * selectButtonOK.
* </p> * </p>
*/ */
public final void selectButtonOK() { public final void selectButtonOK() {
this.getInput().selectButtonOK(); if ( null == input ) return;
input.selectButtonOK();
} }
/** /**
@@ -76,7 +90,8 @@ public class InputProxy implements Observer {
* </p> * </p>
*/ */
public final void selectButtonCancel() { 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. * a {@link forge.game.player.Player} object.
*/ */
public final void selectPlayer(final Player player) { 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. * a {@link forge.game.zone.PlayerZone} object.
*/ */
public final void selectCard(final Card card) { public final void selectCard(final Card card) {
this.getInput().selectCard(card); if ( null == input ) return;
input.selectCard(card);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final String toString() { 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() { public Input getInput() {
return this.input; return this.input;
} }
public synchronized boolean isValid() {
return valid;
}
} }

View File

@@ -23,8 +23,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import forge.Card; import forge.Card;
import forge.FThreads;
import forge.GameEntity; import forge.GameEntity;
import forge.ImageCache; import forge.ImageCache;
import forge.Singletons; import forge.Singletons;
@@ -181,7 +181,8 @@ public enum CMatchUI {
* @param blockers &emsp; {@link forge.CardList} * @param blockers &emsp; {@link forge.CardList}
* @param damage &emsp; int * @param damage &emsp; int
*/ */
public Map<Card, Integer> getDamageToAssign(final Card attacker, final List<Card> blockers, final int damage, GameEntity defender) { @SuppressWarnings("unchecked")
public Map<Card, Integer> getDamageToAssign(final Card attacker, final List<Card> blockers, final int damage, final GameEntity defender) {
if (damage <= 0) { if (damage <= 0) {
return new HashMap<Card, Integer>(); return new HashMap<Card, Integer>();
} }
@@ -194,8 +195,15 @@ public enum CMatchUI {
return res; return res;
} }
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); VAssignDamage v = new VAssignDamage(attacker, blockers, damage, defender);
return v.getDamageMap(); result[0] = v.getDamageMap();
}});
return (Map<Card, Integer>)result[0];
} }
/** /**

View File

@@ -32,6 +32,7 @@ import forge.Card;
import forge.CardCharacteristicName; import forge.CardCharacteristicName;
import forge.Command; import forge.Command;
import forge.Constant; import forge.Constant;
import forge.FThreads;
import forge.Constant.Preferences; import forge.Constant.Preferences;
import forge.Singletons; import forge.Singletons;
import forge.card.cardfactory.CardFactory; import forge.card.cardfactory.CardFactory;
@@ -41,10 +42,6 @@ import forge.control.input.Input;
import forge.control.input.InputAttack; import forge.control.input.InputAttack;
import forge.control.input.InputBlock; import forge.control.input.InputBlock;
import forge.control.input.InputPayManaBase; 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.phase.CombatUtil;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.PlayerZone; import forge.game.zone.PlayerZone;
@@ -56,6 +53,7 @@ import forge.gui.framework.ICDoc;
import forge.gui.match.CMatchUI; import forge.gui.match.CMatchUI;
import forge.gui.match.controllers.CMessage; import forge.gui.match.controllers.CMessage;
import forge.gui.toolbox.FLabel; import forge.gui.toolbox.FLabel;
import forge.view.arcane.CardPanel;
/** /**
* Controls Swing components of a player's field instance. * Controls Swing components of a player's field instance.
@@ -144,6 +142,7 @@ public class CField implements ICDoc {
private final Observer observerPlay = new Observer() { private final Observer observerPlay = new Observer() {
@Override @Override
public void update(final Observable a, final Object b) { public void update(final Observable a, final Object b) {
//FThreads.checkEDT("observerPlay.update", true);
CField.this.view.getTabletop().setupPlayZone(); CField.this.view.getTabletop().setupPlayZone();
} }
}; };
@@ -344,10 +343,11 @@ public class CField implements ICDoc {
if ( CField.this.player != CField.this.playerViewer ) if ( CField.this.player != CField.this.playerViewer )
return; return;
final GameState game = Singletons.getModel().getGame(); final SpellAbility ab = player.getController().getAbilityToPlay(player.getGame().getAbilitesOfCard(c, player));
SpellAbility ab = player.getController().getAbilityToPlay(game.getAbilitesOfCard(c, player));
if ( null != ab) { if ( null != ab) {
FThreads.invokeInNewThread(new Runnable(){ @Override public void run(){
player.playSpellAbility(c, ab); player.playSpellAbility(c, ab);
}});
} }
} }
}.actionPerformed(null); }.actionPerformed(null);
@@ -390,10 +390,14 @@ public class CField implements ICDoc {
final Input input = CMessage.SINGLETON_INSTANCE.getInputControl().getInput(); final Input input = CMessage.SINGLETON_INSTANCE.getInputControl().getInput();
if (c != null && c.isInZone(ZoneType.Battlefield)) { if (c == null || !c.isInZone(ZoneType.Battlefield)) {
if (c.isTapped() && (input instanceof InputPayManaSimple || input instanceof InputPayManaExecuteCommands)) { return;
final forge.view.arcane.CardPanel cardPanel = CField.this.view.getTabletop().getCardPanel(c.getUniqueNumber()); }
for (final forge.view.arcane.CardPanel cp : cardPanel.getAttachedPanels()) {
// 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()) { if (cp.getCard().isUntapped()) {
break; break;
} }
@@ -402,9 +406,8 @@ public class CField implements ICDoc {
final List<Card> att = Singletons.getModel().getGame().getCombat().getAttackerList(); final List<Card> att = Singletons.getModel().getGame().getCombat().getAttackerList();
if ((c.isTapped() || c.hasSickness() || (c.hasKeyword("Vigilance") && att.contains(c))) && (input instanceof InputAttack)) { 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( final CardPanel cardPanel = CField.this.view.getTabletop().getCardPanel(c.getUniqueNumber());
c.getUniqueNumber()); for (final CardPanel cp : cardPanel.getAttachedPanels()) {
for (final forge.view.arcane.CardPanel cp : cardPanel.getAttachedPanels()) {
if (cp.getCard().isUntapped() && !cp.getCard().hasSickness()) { if (cp.getCard().isUntapped() && !cp.getCard().hasSickness()) {
break; break;
} }
@@ -412,8 +415,7 @@ public class CField implements ICDoc {
} }
if (e.isMetaDown()) { if (e.isMetaDown()) {
if (att.contains(c) && (input instanceof InputAttack) if (att.contains(c) && input instanceof InputAttack && !c.hasKeyword("CARDNAME attacks each turn if able.")) {
&& !c.hasKeyword("CARDNAME attacks each turn if able.")) {
c.untap(); c.untap();
Singletons.getModel().getGame().getCombat().removeFromCombat(c); Singletons.getModel().getGame().getCombat().removeFromCombat(c);
CombatUtil.showCombat(); CombatUtil.showCombat();
@@ -424,10 +426,7 @@ public class CField implements ICDoc {
((InputBlock) input).removeFromAllBlocking(c); ((InputBlock) input).removeFromAllBlocking(c);
CombatUtil.showCombat(); CombatUtil.showCombat();
} }
else if (input instanceof InputPaySacCost) { } else if ( input != null ){
((InputPaySacCost) input).unselectCard(c, Singletons.getControl().getPlayer().getZone(ZoneType.Battlefield));
}
} else {
//Yosei, the Morning Star required cards to be chosen on computer side //Yosei, the Morning Star required cards to be chosen on computer side
//earlier it was enforced that cards must be in player zone //earlier it was enforced that cards must be in player zone
//this can potentially break some other functionality //this can potentially break some other functionality
@@ -437,7 +436,7 @@ public class CField implements ICDoc {
//in weird case card has no controller revert to default behaviour //in weird case card has no controller revert to default behaviour
input.selectCard(c); input.selectCard(c);
} }
}
} }
/** */ /** */

View File

@@ -459,6 +459,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 * Loads two sprites: the default (which should be a complete
* collection of all symbols) and the preferred (which may be * collection of all symbols) and the preferred (which may be
@@ -487,19 +507,12 @@ public enum FSkin {
if (FSkin.preferredName.isEmpty()) { FSkin.loadLight("default"); } if (FSkin.preferredName.isEmpty()) { FSkin.loadLight("default"); }
// Everything OK? // Everything OK?
final FProgressBar barProgress = FView.SINGLETON_INSTANCE.getSplash().getProgressBar();
SwingUtilities.invokeLater(new Runnable() { final FProgressBar barProgress = FView.SINGLETON_INSTANCE.getSplash().getProgressBar();
@Override setProgessBarMessage("Processing image sprites: ", 4);
public void run() {
barProgress.reset();
barProgress.setShowETA(false);
barProgress.setDescription("Processing image sprites: ");
}
});
// Grab and test various sprite files. // Grab and test various sprite files.
barProgress.setMaximum(4);
final File f1 = new File(DEFAULT_DIR + FILE_ICON_SPRITE); final File f1 = new File(DEFAULT_DIR + FILE_ICON_SPRITE);
final File f2 = new File(preferredDir + FILE_ICON_SPRITE); final File f2 = new File(preferredDir + FILE_ICON_SPRITE);
final File f3 = new File(DEFAULT_DIR + FILE_FOIL_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)); UIManager.put("Table.alternateRowColor", new Color(240, 240, 240));
// Images loaded; can start UI init. // Images loaded; can start UI init.
SwingUtilities.invokeLater(new Runnable() { setProgessBarMessage("Creating display components.");
@Override
public void run() {
barProgress.setShowETA(false);
barProgress.setShowCount(false);
barProgress.setDescription("Creating display components.");
}
});
// Clear references to buffered images // Clear references to buffered images
FSkin.bimDefaultSprite.flush(); FSkin.bimDefaultSprite.flush();

View File

@@ -117,11 +117,12 @@ public enum FModel {
} }
// initialize log file // initialize log file
final File logFile = new File(NewConstants.LOG_FILE); File logFile = new File(NewConstants.LOG_FILE);
final boolean deleteSucceeded = logFile.delete();
if (logFile.exists() && !deleteSucceeded && (logFile.length() != 0)) { int i = 0;
throw new IllegalStateException("Could not delete existing logFile:" + logFile.getAbsolutePath()); while (logFile.exists() && !logFile.delete()) {
String pathname = logFile.getPath().replaceAll("[0-9]{0,2}.log$", String.valueOf(i++) + ".log");
logFile = new File(pathname);
} }
try { try {
@@ -400,6 +401,7 @@ public enum FModel {
/** /**
* TODO: Write javadoc for this method. * TODO: Write javadoc for this method.
* @param players * @param players
* @param input
*/ */
public GameState newGame(Iterable<LobbyPlayer> players, GameType type, final MatchController match0) { public GameState newGame(Iterable<LobbyPlayer> players, GameType type, final MatchController match0) {
gameState = new GameState(players,type, match0); gameState = new GameState(players,type, match0);

View File

@@ -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 <B>try to use SwingUtilities.invokeLater instead whenever
* feasible.</B>
*
* 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
}
}
}
}

View File

@@ -24,10 +24,12 @@ import java.awt.event.MouseEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import forge.Card; import forge.Card;
import forge.FThreads;
import forge.view.arcane.util.Animation; import forge.view.arcane.util.Animation;
import forge.view.arcane.util.CardPanelMouseListener; import forge.view.arcane.util.CardPanelMouseListener;
@@ -497,7 +499,27 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
* @param newList * @param newList
* an array of {@link forge.Card} objects. * an array of {@link forge.Card} objects.
*/ */
private final AtomicBoolean wantRedraw = new AtomicBoolean(false);
public void setupPlayZone() { 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<Card> modelshot = new ArrayList<Card>(model); // I am afraid of ConcurrentModificationExceptions
setupPlayZone(modelshot);
}
});
}
public void setupPlayZone(final List<Card> model) {
List<Card> oldCards, toDelete; List<Card> oldCards, toDelete;
oldCards = new ArrayList<Card>(); oldCards = new ArrayList<Card>();
for (final CardPanel cpa : getCardPanels()) { for (final CardPanel cpa : getCardPanels()) {