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/CostUntapType.java -text
src/main/java/forge/card/cost/CostUtil.java -text
src/main/java/forge/card/cost/InputPayCostBase.java -text
src/main/java/forge/card/cost/package-info.java svneol=native#text/plain
src/main/java/forge/card/mana/IParserManaCost.java -text
src/main/java/forge/card/mana/Mana.java svneol=native#text/plain
@@ -13808,25 +13809,27 @@ src/main/java/forge/control/Lobby.java -text
src/main/java/forge/control/RestartUtil.java -text
src/main/java/forge/control/bazaar/ControlStall.java -text
src/main/java/forge/control/bazaar/package-info.java svneol=native#text/plain
src/main/java/forge/control/input/Input.java svneol=native#text/plain
src/main/java/forge/control/input/Input.java -text
src/main/java/forge/control/input/InputAttack.java svneol=native#text/plain
src/main/java/forge/control/input/InputBase.java svneol=native#text/plain
src/main/java/forge/control/input/InputBlock.java svneol=native#text/plain
src/main/java/forge/control/input/InputCleanup.java svneol=native#text/plain
src/main/java/forge/control/input/InputControl.java svneol=native#text/plain
src/main/java/forge/control/input/InputLockUI.java -text
src/main/java/forge/control/input/InputMulligan.java svneol=native#text/plain
src/main/java/forge/control/input/InputPassPriority.java svneol=native#text/plain
src/main/java/forge/control/input/InputPayDiscardCost.java -text
src/main/java/forge/control/input/InputPayManaBase.java -text
src/main/java/forge/control/input/InputPayManaExecuteCommands.java svneol=native#text/plain
src/main/java/forge/control/input/InputPayManaOfCostPayment.java -text
src/main/java/forge/control/input/InputPayManaSimple.java svneol=native#text/plain
src/main/java/forge/control/input/InputPayManaX.java -text
src/main/java/forge/control/input/InputPayReturnCost.java -text
src/main/java/forge/control/input/InputPaySacCost.java -text
src/main/java/forge/control/input/InputSelectMany.java -text
src/main/java/forge/control/input/InputSelectManyCards.java -text
src/main/java/forge/control/input/InputSelectManyPlayers.java -text
src/main/java/forge/control/input/InputPayment.java -text
src/main/java/forge/control/input/InputSelectCards.java -text
src/main/java/forge/control/input/InputSelectCardsFromList.java -text
src/main/java/forge/control/input/InputSelectList.java -text
src/main/java/forge/control/input/InputSelectListBase.java -text
src/main/java/forge/control/input/InputSynchronized.java -text
src/main/java/forge/control/input/InputSyncronizedBase.java -text
src/main/java/forge/control/input/package-info.java svneol=native#text/plain
src/main/java/forge/control/package-info.java -text
src/main/java/forge/deck/CardCollections.java -text
@@ -13860,7 +13863,6 @@ src/main/java/forge/game/GameActionPlay.java -text
src/main/java/forge/game/GameActionUtil.java svneol=native#text/plain
src/main/java/forge/game/GameEndReason.java -text
src/main/java/forge/game/GameFormat.java -text
src/main/java/forge/game/GameInputUpdatesThread.java -text
src/main/java/forge/game/GameLossReason.java -text
src/main/java/forge/game/GameNew.java -text
src/main/java/forge/game/GameOutcome.java -text
@@ -14254,7 +14256,6 @@ src/main/java/forge/util/MyRandom.java svneol=native#text/plain
src/main/java/forge/util/PredicateString.java -text
src/main/java/forge/util/ReflectionUtil.java -text
src/main/java/forge/util/TextUtil.java -text
src/main/java/forge/util/ThreadUtil.java svneol=native#text/plain
src/main/java/forge/util/XmlUtil.java -text
src/main/java/forge/util/package-info.java -text
src/main/java/forge/util/storage/IStorage.java -text

8
.gitignore vendored
View File

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

View File

@@ -3,25 +3,28 @@ package forge;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.SwingUtilities;
import forge.control.input.InputLockUI;
import forge.control.input.InputSynchronized;
/**
* TODO: Write javadoc for this type.
*
*/
public class FThreads {
static {
System.out.printf("(FThreads static ctor): Running on a machine with %d cpu core(s)%n", Runtime.getRuntime().availableProcessors() );
}
private FThreads() { } // no instances supposed
private final static ExecutorService threadPool = Executors.newCachedThreadPool();
public static ExecutorService getCachedPool() {
return threadPool;
}
private static ExecutorService getCachedPool() { return threadPool; }
private final static ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(1);
private static ScheduledExecutorService getScheduledPool() { return scheduledPool; }
// This pool is designed to parallel CPU or IO intensive tasks like parse cards or download images, assuming a load factor of 0.5
public final static ExecutorService getComputingPool(float loadFactor) {
@@ -83,27 +86,42 @@ public class FThreads {
}
public static void invokeInNewThread(Runnable proc) {
invokeInNewThread(proc, false);
public static void invokeInNewThread(Runnable toRun) {
getCachedPool().execute(toRun);
}
private final static InputLockUI inpuptLock = new InputLockUI();
public static void invokeInNewThread(final Runnable proc, boolean lockUI) {
Runnable toRun = proc;
if( lockUI ) {
// checkEDT("FThreads.invokeInNewthread", true)
Singletons.getModel().getMatch().getInput().setInput(inpuptLock);
Singletons.getModel().getMatch().getInput().lock();
toRun = new Runnable() {
@Override
public void run() {
proc.run();
// may try special unlock method here
Singletons.getModel().getMatch().getInput().resetInput();
Singletons.getModel().getMatch().getInput().unlock();
}
};
}
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.CardLists;
import forge.CardUtil;
import forge.Command;
import forge.Constant;
import forge.CounterType;
import forge.Singletons;
@@ -1036,6 +1035,19 @@ public class AbilityUtils {
AbilityUtils.resolveApiAbility(abSub, usedStack, game);
}
private static void resolveApiAbility(final SpellAbility sa, boolean usedStack, final GameState game) {
// check conditions
if (sa.getConditions().areMet(sa)) {
if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) {
sa.resolve();
} else {
handleUnlessCost(sa, usedStack, game);
return;
}
}
resolveSubAbilities(sa, usedStack, game);
}
private static void handleUnlessCost(final SpellAbility sa, final boolean usedStack, final GameState game) {
final Card source = sa.getSourceCard();
String unlessCost = sa.getParam("UnlessCost");
@@ -1047,6 +1059,7 @@ public class AbilityUtils {
final String resolveSubs = sa.getParam("UnlessResolveSubs"); // no value means 'Always'
final boolean execSubsWhenPaid = "WhenPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs);
final boolean execSubsWhenNotPaid = "WhenNotPaid".equals(resolveSubs) || StringUtils.isBlank(resolveSubs);
final boolean isSwitched = sa.hasParam("UnlessSwitched");
// The cost
if (unlessCost.equals("CardManaCost")) {
@@ -1070,43 +1083,6 @@ public class AbilityUtils {
//instead of just X for cards like Draco.
}
final boolean isSwitched = sa.hasParam("UnlessSwitched");
Command paidCommand = new Command() {
private static final long serialVersionUID = 8094833091127334678L;
@Override
public void execute() {
if (isSwitched && execSubsWhenNotPaid || execSubsWhenPaid) {
resolveSubAbilities(sa, usedStack, game);
} else if (usedStack) {
SpellAbility root = sa.getRootAbility();
game.getStack().finishResolving(root, false);
}
}
};
Command unpaidCommand = new Command() {
private static final long serialVersionUID = 8094833091127334678L;
@Override
public void execute() {
sa.resolve();
if (isSwitched && execSubsWhenPaid || execSubsWhenNotPaid) {
resolveSubAbilities(sa, usedStack, game);
} else if (usedStack) {
SpellAbility root = sa.getRootAbility();
game.getStack().finishResolving(root, false);
}
}
};
if (isSwitched) {
final Command dummy = paidCommand;
paidCommand = unpaidCommand;
unpaidCommand = dummy;
}
final Cost cost = new Cost(source, unlessCost, true);
final Ability ability = new AbilityStatic(source, cost, null) {
@Override
@@ -1117,34 +1093,35 @@ public class AbilityUtils {
boolean paid = false;
for (Player payer : payers) {
ability.setActivatingPlayer(payer);
ability.setTarget(sa.getTarget());
if (payer.isComputer()) {
ability.setActivatingPlayer(payer);
if (AbilityUtils.willAIPayForAbility(sa, payer, ability, paid, payers)) {
ability.setTarget(sa.getTarget());
ComputerUtil.playNoStack((AIPlayer) payer, ability, game); // Unless cost was payed - no resolve
paid = true;
}
}
}
boolean waitForInput = false;
for (Player payer : payers) {
if (payer.isHuman()) {
} else {
// if it's paid by the AI already the human can pay, but it won't change anything
if (paid) {
unpaidCommand = paidCommand;
}
ability.setActivatingPlayer(payer);
ability.setTarget(sa.getTarget());
GameActionUtil.payCostDuringAbilityResolve(payer, ability, cost, paidCommand, unpaidCommand, sa, game);
waitForInput = true; // wait for the human input
break; // multiple human players are not supported
paid |= GameActionUtil.payCostDuringAbilityResolve(payer, ability, cost, sa, game);
}
}
if (!waitForInput) {
Command toExecute = paid ? paidCommand : unpaidCommand;
toExecute.execute();
}
if ( paid ^ isSwitched ) {
if (isSwitched && execSubsWhenNotPaid || execSubsWhenPaid) {
resolveSubAbilities(sa, usedStack, game);
} else if (usedStack) {
SpellAbility root = sa.getRootAbility();
game.getStack().finishResolving(root, false);
}
} else {
sa.resolve();
if (isSwitched && execSubsWhenPaid || execSubsWhenNotPaid) {
resolveSubAbilities(sa, usedStack, game);
} else if (usedStack) {
SpellAbility root = sa.getRootAbility();
game.getStack().finishResolving(root, false);
}
}
}
/**
@@ -1221,19 +1198,6 @@ public class AbilityUtils {
return false;
}
private static void resolveApiAbility(final SpellAbility sa, boolean usedStack, final GameState game) {
// check conditions
if (sa.getConditions().areMet(sa)) {
if (sa.isWrapper() || StringUtils.isBlank(sa.getParam("UnlessCost"))) {
sa.resolve();
} else {
handleUnlessCost(sa, usedStack, game);
return;
}
}
resolveSubAbilities(sa, usedStack, game);
}
/**
* <p>
* Parse non-mana X variables.

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ import forge.Singletons;
import forge.card.ability.SpellAbilityEffect;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.control.input.InputBase;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
@@ -45,7 +45,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
private static void resolveHuman(final SpellAbility sa) {
final List<Card> unchosen = Lists.newArrayList(Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield));
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;
@Override

View File

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

View File

@@ -24,9 +24,9 @@ import javax.swing.JOptionPane;
import forge.Card;
import forge.CardLists;
import forge.Command;
import forge.FThreads;
import forge.Singletons;
import forge.control.input.Input;
import forge.control.input.InputSelectManyCards;
import forge.control.input.InputSelectCards;
import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
@@ -46,7 +46,7 @@ class CardFactoryLands {
* TODO: Write javadoc for this type.
*
*/
private static final class InputRevealCardType extends InputSelectManyCards {
private static final class InputRevealCardType extends InputSelectCards {
private final String type;
private final Card card;
private static final long serialVersionUID = -2774066137824255680L;
@@ -74,23 +74,6 @@ class CardFactoryLands {
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
return zone.is(ZoneType.Hand) && c.isType(type) && c.getController() == card.getController();
}
@Override
protected Input onDone() {
if (selected.isEmpty()) {
return onCancel();
}
String cardName = selected.get(0).getName();
JOptionPane.showMessageDialog(null, "Revealed card: " + cardName, cardName, JOptionPane.PLAIN_MESSAGE);
return null;
}
@Override
public Input onCancel() {
card.setTapped(true);
return null;
}
}
/**
@@ -229,7 +212,16 @@ class CardFactoryLands {
}
public void humanExecute() {
Singletons.getModel().getMatch().getInput().setInput(new InputRevealCardType(0, 1, type, card));
InputSelectCards inp = new InputRevealCardType(0, 1, type, card);
FThreads.setInputAndWait(inp);
if ( inp.hasCancelled() || inp.getSelected().isEmpty() ) {
card.setTapped(true);
} else {
String cardName = inp.getSelected().get(0).getName();
JOptionPane.showMessageDialog(null, "Revealed card: " + cardName, cardName, JOptionPane.PLAIN_MESSAGE);
}
} // execute()
private void revealCard(final Card c) {

View File

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

View File

@@ -38,6 +38,7 @@ import forge.CardUtil;
import forge.Command;
import forge.Constant;
import forge.CounterType;
import forge.FThreads;
import forge.GameEntity;
import forge.Singletons;
import forge.card.ability.AbilityFactory;
@@ -63,7 +64,8 @@ import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerHandler;
import forge.card.trigger.TriggerType;
import forge.control.input.Input;
import forge.control.input.InputSelectManyCards;
import forge.control.input.InputBase;
import forge.control.input.InputSelectCards;
import forge.game.GameState;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard;
@@ -98,10 +100,10 @@ public class CardFactoryUtil {
* a {@link forge.CardList} object.
* @param message
* a {@link java.lang.String} object.
* @return a {@link forge.control.input.Input} object.
* @return a {@link forge.control.input.InputBase} object.
*/
public static Input inputDestroyNoRegeneration(final List<Card> choices, final String message) {
final Input target = new Input() {
public static InputBase inputDestroyNoRegeneration(final List<Card> choices, final String message) {
final InputBase target = new InputBase() {
private static final long serialVersionUID = -6637588517573573232L;
@Override
@@ -549,11 +551,11 @@ public class CardFactoryUtil {
* a {@link forge.CardList} object.
* @param paid
* a {@link forge.Command} object.
* @return a {@link forge.control.input.Input} object.
* @return a {@link forge.control.input.InputBase} object.
*/
public static Input masterOfTheWildHuntInputTargetCreature(final SpellAbility spell, final List<Card> choices,
public static InputBase masterOfTheWildHuntInputTargetCreature(final SpellAbility spell, final List<Card> choices,
final Command paid) {
final Input target = new Input() {
final InputBase target = new InputBase() {
private static final long serialVersionUID = -1779224307654698954L;
@Override
@@ -593,10 +595,10 @@ public class CardFactoryUtil {
* a {@link forge.card.spellability.SpellAbility} object.
* @param card
* a {@link forge.Card} object.
* @return a {@link forge.control.input.Input} object.
* @return a {@link forge.control.input.InputBase} object.
*/
public static Input modularInput(final SpellAbility ability, final Card card) {
final Input modularInput = new Input() {
public static InputBase modularInput(final SpellAbility ability, final Card card) {
final InputBase modularInput = new InputBase() {
private static final long serialVersionUID = 2322926875771867901L;
@@ -2302,10 +2304,10 @@ public class CardFactoryUtil {
* a int.
* @param type
* a {@link java.lang.String} object.
* @return a {@link forge.control.input.Input} object.
* @return a {@link forge.control.input.InputBase} object.
*/
public static Input inputUntapUpToNType(final int n, final String type) {
final Input untap = new Input() {
final Input untap = new InputBase() {
private static final long serialVersionUID = -2167059918040912025L;
private final int stop = n;
@@ -3151,27 +3153,6 @@ public class CardFactoryUtil {
};
haunterDiesWork.setDescription(hauntDescription);
final InputSelectManyCards target = new InputSelectManyCards(1, 1) {
private static final long serialVersionUID = 1981791992623774490L;
@Override
protected Input onDone() {
haunterDiesWork.setTargetCard(selected.get(0));
Singletons.getModel().getGame().getStack().add(haunterDiesWork);
return null;
}
@Override
protected boolean isValidChoice(Card c) {
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
if (!zone.is(ZoneType.Battlefield) || !c.isCreature()) {
return false;
}
return c.canBeTargetedBy(haunterDiesWork);
}
};
target.setMessage("Choose target creature to haunt.");
final Ability haunterDiesSetup = new Ability(card, ManaCost.ZERO) {
@Override
public void resolve() {
@@ -3189,7 +3170,25 @@ public class CardFactoryUtil {
// need to do it this way because I don't know quite how to
// make TriggerHandler respect BeforePayMana.
if (card.getController().isHuman()) {
Singletons.getModel().getMatch().getInput().setInput(target);
final InputSelectCards target = new InputSelectCards(1, 1) {
private static final long serialVersionUID = 1981791992623774490L;
@Override
protected boolean isValidChoice(Card c) {
Zone zone = Singletons.getModel().getGame().getZoneOf(c);
if (!zone.is(ZoneType.Battlefield) || !c.isCreature()) {
return false;
}
return c.canBeTargetedBy(haunterDiesWork);
}
};
target.setMessage("Choose target creature to haunt.");
FThreads.setInputAndWait(target);
if (!target.hasCancelled()) {
haunterDiesWork.setTargetCard(target.getSelected().get(0));
Singletons.getModel().getGame().getStack().add(haunterDiesWork);
}
} else {
// AI choosing what to haunt
final List<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)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final String amount = this.getAmount();
final Player activator = ability.getActivatingPlayer();
final int life = activator.getLife();
final Card source = ability.getSourceCard();
Integer c = this.convertAmount();
if (c == null) {
@@ -124,11 +125,7 @@ public class CostDamage extends CostPart {
if (GuiDialog.confirm(source, sb.toString()) && activator.canPayLife(c)) {
activator.addDamage(c, source);
this.setLastPaidAmount(c);
payment.setPaidManaPart(this);
} else {
payment.setCancel(true);
payment.getRequirements().finishPaying();
return false;
}
return true;

View File

@@ -19,23 +19,20 @@ package forge.card.cost;
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.Singletons;
import forge.FThreads;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.control.input.InputSelectCards;
import forge.control.input.InputSelectCardsFromList;
import forge.game.GameState;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
/**
* The Class CostDiscard.
@@ -43,6 +40,8 @@ import forge.view.ButtonUtil;
public class CostDiscard extends CostPartWithList {
// Discard<Num/Type{/TypeDescription}>
// Inputs
/**
* Instantiates a new cost discard.
*
@@ -155,7 +154,7 @@ public class CostDiscard extends CostPartWithList {
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
for (final Card c : this.getList()) {
ai.discard(c, ability);
executePayment(ability, c);
}
}
@@ -167,10 +166,11 @@ public class CostDiscard extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final Player activator = ability.getActivatingPlayer();
final Card source = ability.getSourceCard();
List<Card> handList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Hand));
String discType = this.getType();
String discardType = this.getType();
final String amount = this.getAmount();
this.resetList();
@@ -178,25 +178,24 @@ public class CostDiscard extends CostPartWithList {
if (!handList.contains(source)) {
return false;
}
activator.discard(source, ability);
payment.setPaidManaPart(this);
executePayment(ability, source);
return true;
//this.addToList(source);
} else if (discType.equals("Hand")) {
} else if (discardType.equals("Hand")) {
this.setList(handList);
activator.discardHand(ability);
payment.setPaidManaPart(this);
} else if (discType.equals("LastDrawn")) {
return true;
} else if (discardType.equals("LastDrawn")) {
final Card lastDrawn = activator.getLastDrawnCard();
this.addToList(lastDrawn);
if (!handList.contains(lastDrawn)) {
return false;
}
activator.discard(lastDrawn, ability);
payment.setPaidManaPart(this);
executePayment(ability, lastDrawn);
return true;
} else {
Integer c = this.convertAmount();
if (discType.equals("Random")) {
if (discardType.equals("Random")) {
if (c == null) {
final String sVar = ability.getSVar(amount);
// Generalize this
@@ -208,9 +207,9 @@ public class CostDiscard extends CostPartWithList {
}
this.setList(activator.discardRandom(c, ability));
payment.setPaidManaPart(this);
return true;
} else {
String type = new String(discType);
String type = new String(discardType);
boolean sameName = false;
if (type.contains("+WithSameName")) {
sameName = true;
@@ -243,13 +242,17 @@ public class CostDiscard extends CostPartWithList {
}
}
final Input inp = CostDiscard.inputDiscardCost(discType, handList, ability, payment, this, c);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
return false;
InputSelectCards inp = new InputSelectCardsFromList(c, c, handList);
inp.setMessage("Select %d more " + getDescriptiveType() + " to discard.");
//InputPayment inp = new InputPayCostDiscard(ability, handList, this, c, discardType);
FThreads.setInputAndWait(inp);
if( inp.hasCancelled() || inp.getSelected().size() != c)
return false;
for(Card crd : inp.getSelected())
executePayment(ability, crd);
return true;
}
}
this.addListToHash(ability, "Discarded");
return true;
}
/*
@@ -306,109 +309,23 @@ public class CostDiscard extends CostPartWithList {
return this.getList() != null;
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
this.addToList(targetCard);
targetCard.getController().discard(targetCard, ability);
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "Discarded";
}
// Inputs
/**
* <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.Iterator;
import java.util.List;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.FThreads;
import forge.Singletons;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.control.input.Input;
import forge.control.input.InputPayment;
import forge.game.GameState;
import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.MagicStack;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.gui.GuiDialog;
@@ -49,19 +50,15 @@ public class CostExile extends CostPartWithList {
// ExileFromTop<Num/Type{/TypeDescription}> (of library)
// ExileSameGrave<Num/Type{/TypeDescription}>
/**
* TODO: Write javadoc for this type.
*
*/
private static final class InputExileFrom extends Input {
private static final class InputExileFrom extends InputPayCostBase {
private final SpellAbility sa;
private final String type;
private final int nNeeded;
private final CostPayment payment;
private final CostExile part;
private static final long serialVersionUID = 734256837615635021L;
private List<Card> typeList;
/**
* TODO: Write javadoc for Constructor.
* @param sa
@@ -70,11 +67,10 @@ public class CostExile extends CostPartWithList {
* @param payment
* @param part
*/
private InputExileFrom(SpellAbility sa, String type, int nNeeded, CostPayment payment, CostExile part) {
private InputExileFrom(SpellAbility sa, String type, int nNeeded, CostExile part) {
this.sa = sa;
this.type = type;
this.nNeeded = nNeeded;
this.payment = payment;
this.part = part;
}
@@ -96,8 +92,7 @@ public class CostExile extends CostPartWithList {
if (c != null) {
this.typeList.remove(c);
part.addToList(c);
Singletons.getModel().getGame().getAction().exile(c);
part.executePayment(sa, c);
if (i == (nNeeded - 1)) {
this.done();
}
@@ -107,33 +102,15 @@ public class CostExile extends CostPartWithList {
}
}
}
@Override
public void selectButtonCancel() {
this.cancel();
}
public void done() {
this.stop();
part.addListToHash(sa, "Exiled");
payment.paidCost(part);
}
public void cancel() {
this.stop();
payment.cancelCost();
}
}
/**
* TODO: Write javadoc for this type.
*
*/
private static final class InputExileFromSame extends Input {
private static final class InputExileFromSame extends InputPayCostBase {
private final List<Card> list;
private final CostExile part;
private final CostPayment payment;
private final SpellAbility sa;
private final int nNeeded;
private final List<Player> payableZone;
private static final long serialVersionUID = 734256837615635021L;
@@ -148,12 +125,9 @@ public class CostExile extends CostPartWithList {
* @param nNeeded
* @param payableZone
*/
private InputExileFromSame(List<Card> list, CostExile part, CostPayment payment, SpellAbility sa, int nNeeded,
List<Player> payableZone) {
private InputExileFromSame(List<Card> list, CostExile part, int nNeeded, List<Player> payableZone) {
this.list = list;
this.part = part;
this.payment = payment;
this.sa = sa;
this.nNeeded = nNeeded;
this.payableZone = payableZone;
}
@@ -185,8 +159,7 @@ public class CostExile extends CostPartWithList {
if (c != null) {
this.typeList.remove(c);
part.addToList(c);
Singletons.getModel().getGame().getAction().exile(c);
part.executePayment(null, c);
if (i == (nNeeded - 1)) {
this.done();
}
@@ -196,30 +169,14 @@ public class CostExile extends CostPartWithList {
}
}
}
@Override
public void selectButtonCancel() {
this.cancel();
}
public void done() {
this.stop();
part.addListToHash(sa, "Exiled");
payment.paidCost(part);
}
public void cancel() {
this.stop();
payment.cancelCost();
}
}
/**
* TODO: Write javadoc for this type.
*
*/
private static final class InputExileFromStack extends Input {
private final CostPayment payment;
private static final class InputExileFromStack extends InputPayCostBase {
private final SpellAbility sa;
private final String type;
private final int nNeeded;
@@ -236,8 +193,7 @@ public class CostExile extends CostPartWithList {
* @param nNeeded
* @param part
*/
private InputExileFromStack(CostPayment payment, SpellAbility sa, String type, int nNeeded, CostExile part) {
this.payment = payment;
private InputExileFromStack(SpellAbility sa, String type, int nNeeded, CostExile part) {
this.sa = sa;
this.type = type;
this.nNeeded = nNeeded;
@@ -252,10 +208,11 @@ public class CostExile extends CostPartWithList {
saList = new ArrayList<SpellAbility>();
descList = new ArrayList<String>();
final MagicStack stack = sa.getActivatingPlayer().getGame().getStack();
for (int i = 0; i < Singletons.getModel().getGame().getStack().size(); i++) {
final Card stC = Singletons.getModel().getGame().getStack().peekAbility(i).getSourceCard();
final SpellAbility stSA = Singletons.getModel().getGame().getStack().peekAbility(i).getRootAbility();
for (int i = 0; i < stack.size(); i++) {
final Card stC = stack.peekAbility(i).getSourceCard();
final SpellAbility stSA = stack.peekAbility(i).getRootAbility();
if (stC.isValid(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()) && stSA.isSpell()) {
this.saList.add(stSA);
if (stC.isCopiedSpell()) {
@@ -277,47 +234,33 @@ public class CostExile extends CostPartWithList {
if (o != null) {
final SpellAbility toExile = this.saList.get(descList.indexOf(o));
final Card c = toExile.getSourceCard();
this.saList.remove(toExile);
part.addToList(c);
if (!c.isCopiedSpell()) {
Singletons.getModel().getGame().getAction().exile(c);
}
part.executePayment(sa, c);
} else
part.addToList(c);
if (i == (nNeeded - 1)) {
this.done();
}
final SpellAbilityStackInstance si = Singletons.getModel().getGame().getStack().getInstanceFromSpellAbility(toExile);
Singletons.getModel().getGame().getStack().remove(si);
final SpellAbilityStackInstance si = stack.getInstanceFromSpellAbility(toExile);
stack.remove(si);
} else {
this.cancel();
break;
}
}
}
@Override
public void selectButtonCancel() {
this.cancel();
}
public void done() {
this.stop();
part.addListToHash(sa, "Exiled");
payment.paidCost(part);
}
public void cancel() {
this.stop();
payment.cancelCost();
}
}
/**
* TODO: Write javadoc for this type.
*
*/
private static final class InputExileType extends Input {
private static final class InputExileType extends InputPayCostBase {
private final CostExile part;
private final CostPayment payment;
private final String type;
private final int nNeeded;
private final SpellAbility sa;
@@ -333,9 +276,8 @@ public class CostExile extends CostPartWithList {
* @param nNeeded
* @param sa
*/
private InputExileType(CostExile part, CostPayment payment, String type, int nNeeded, SpellAbility sa) {
private InputExileType(CostExile part, String type, int nNeeded, SpellAbility sa) {
this.part = part;
this.payment = payment;
this.type = type;
this.nNeeded = nNeeded;
this.sa = sa;
@@ -366,17 +308,11 @@ public class CostExile extends CostPartWithList {
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
this.cancel();
}
@Override
public void selectCard(final Card card) {
if (this.typeList.contains(card)) {
this.nExiles++;
part.addToList(card);
Singletons.getModel().getGame().getAction().exile(card);
part.executePayment(sa, card);
this.typeList.remove(card);
// in case nothing else to exile
if (this.nExiles == nNeeded) {
@@ -389,25 +325,13 @@ public class CostExile extends CostPartWithList {
}
}
}
public void done() {
this.stop();
part.addListToHash(sa, "Exiled");
payment.paidCost(part);
}
public void cancel() {
this.stop();
payment.cancelCost();
}
}
/**
* TODO: Write javadoc for this type.
*
*/
private static final class InputExileThis extends Input {
private final CostPayment payment;
private static final class InputExileThis extends InputPayCostBase {
private final CostExile part;
private final SpellAbility sa;
private static final long serialVersionUID = 678668673002725001L;
@@ -418,30 +342,22 @@ public class CostExile extends CostPartWithList {
* @param part
* @param sa
*/
private InputExileThis(CostPayment payment, CostExile part, SpellAbility sa) {
this.payment = payment;
private InputExileThis(CostExile part, SpellAbility sa) {
this.part = part;
this.sa = sa;
}
@Override
public void showMessage() {
final Card card = sa.getSourceCard();
if ( sa.getActivatingPlayer().getZone(part.getFrom()).contains(card)) {
boolean choice = GuiDialog.confirm(card, card.getName() + " - Exile?");
if (choice) {
payment.getAbility().addCostToHashList(card, "Exiled");
Singletons.getModel().getGame().getAction().exile(card);
part.addToList(card);
this.stop();
part.addListToHash(sa, "Exiled");
payment.paidCost(part);
} else {
this.stop();
payment.cancelCost();
part.executePayment(sa, card);
done();
return;
}
}
cancel();
}
}
@@ -619,8 +535,9 @@ public class CostExile extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final String amount = this.getAmount();
final Card source = ability.getSourceCard();
Integer c = this.convertAmount();
final Player activator = ability.getActivatingPlayer();
List<Card> list;
@@ -632,11 +549,10 @@ public class CostExile extends CostPartWithList {
}
if (this.getType().equals("All")) {
this.setList(list);
for (final Card card : list) {
Singletons.getModel().getGame().getAction().exile(card);
executePayment(ability, card);
}
payment.paidCost(this);
return true;
}
list = CardLists.getValidCards(list, this.getType().split(";"), activator, source);
if (c == null) {
@@ -651,16 +567,16 @@ public class CostExile extends CostPartWithList {
}
}
Input target = null;
InputPayment target = null;
if (this.payCostFromSource()) {
target = new InputExileThis(payment, this, ability);
target = new InputExileThis(this, ability);
} else if (this.from.equals(ZoneType.Battlefield) || this.from.equals(ZoneType.Hand)) {
target = new InputExileType(this, payment, this.getType(), c, ability);
target = new InputExileType(this, this.getType(), c, ability);
} else if (this.from.equals(ZoneType.Stack)) {
target = new InputExileFromStack(payment, ability, this.getType(), c, this);
target = new InputExileFromStack(ability, this.getType(), c, this);
} else if (this.from.equals(ZoneType.Library)) {
// this does not create input
CostExile.exileFromTop(ability, this, payment, c);
return exileFromTop(ability, c);
} else if (this.sameZone) {
List<Player> players = game.getPlayers();
List<Player> payableZone = new ArrayList<Player>();
@@ -672,13 +588,13 @@ public class CostExile extends CostPartWithList {
payableZone.add(p);
}
}
target = new InputExileFromSame(list, this, payment, ability, c, payableZone);
target = new InputExileFromSame(list, this, c, payableZone);
} else {
target = new InputExileFrom(ability, this.getType(), c, payment, this);
target = new InputExileFrom(ability, this.getType(), c, this);
}
if ( null != target )
Singletons.getModel().getMatch().getInput().setInputInterrupt(target);
return false;
FThreads.setInputAndWait(target);
return target.isPaid();
}
/*
@@ -741,30 +657,43 @@ public class CostExile extends CostPartWithList {
* @param nNeeded
* the n needed
*/
public static void exileFromTop(final SpellAbility sa, final CostExile part, final CostPayment payment,
final int nNeeded) {
public boolean exileFromTop(final SpellAbility sa, final int nNeeded) {
final StringBuilder sb = new StringBuilder();
sb.append("Exile ").append(nNeeded).append(" cards from the top of your library?");
final List<Card> list = sa.getActivatingPlayer().getCardsIn(ZoneType.Library, nNeeded);
if (list.size() > nNeeded) {
// I don't believe this is possible
payment.cancelCost();
return;
return false;
}
final boolean doExile = GuiDialog.confirm(sa.getSourceCard(), sb.toString());
if (doExile) {
final Iterator<Card> itr = list.iterator();
while (itr.hasNext()) {
final Card c = itr.next();
part.addToList(c);
Singletons.getModel().getGame().getAction().exile(c);
executePayment(sa, itr.next());
}
part.addListToHash(sa, "Exiled");
payment.paidCost(part);
return true;
} else {
payment.cancelCost();
return false;
}
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
ability.getActivatingPlayer().getGame().getAction().exile(targetCard);
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
// TODO Auto-generated method stub
return "Exiled";
}
}

View File

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

View File

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

View File

@@ -17,6 +17,7 @@
*/
package forge.card.cost;
import forge.Card;
import forge.card.spellability.SpellAbility;
import forge.game.GameState;
@@ -199,7 +200,7 @@ public abstract class CostPart {
* @param game
* @return true, if successful
*/
public abstract boolean payHuman(SpellAbility ability, Card source, CostPayment payment, GameState game);
public abstract boolean payHuman(SpellAbility ability, GameState game);
/*
* (non-Javadoc)

View File

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

View File

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

View File

@@ -123,7 +123,8 @@ public class CostPayLife extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final Card source = ability.getSourceCard();
final String amount = this.getAmount();
final Player activator = ability.getActivatingPlayer();
final int life = activator.getLife();
@@ -147,13 +148,9 @@ public class CostPayLife extends CostPart {
final StringBuilder sb = new StringBuilder();
sb.append(source.getName()).append(" - Pay ").append(c).append(" Life?");
if (GuiDialog.confirm(source, sb.toString()) && activator.canPayLife(c)) {
if (activator.canPayLife(c) && GuiDialog.confirm(source, sb.toString())) {
activator.payLife(c, null);
this.setLastPaidAmount(c);
payment.setPaidManaPart(this);
} else {
payment.setCancel(true);
payment.getRequirements().finishPaying();
return false;
}
return true;

View File

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

View File

@@ -18,14 +18,13 @@
package forge.card.cost;
import java.util.List;
import forge.Card;
import forge.CardLists;
import forge.CounterType;
import forge.Singletons;
import forge.FThreads;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.control.input.InputPayment;
import forge.game.GameState;
import forge.game.ai.ComputerUtilCard;
import forge.game.player.AIPlayer;
@@ -38,6 +37,69 @@ import forge.view.ButtonUtil;
* The Class CostPutCounter.
*/
public class CostPutCounter extends CostPartWithList {
/**
* TODO: Write javadoc for this type.
*
*/
public static final class InputPayCostPutCounter extends InputPayCostBase {
private final String type;
private final CostPutCounter costPutCounter;
private final int nNeeded;
private final SpellAbility sa;
private static final long serialVersionUID = 2685832214519141903L;
private List<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
private final CounterType counter;
private int lastPaidAmount = 0;
@@ -156,17 +218,14 @@ public class CostPutCounter extends CostPartWithList {
*/
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
Integer c = this.convertAmount();
if (c == null) {
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
Integer c = getNumberOfCounters(ability);
if (this.payCostFromSource()) {
source.addCounter(this.getCounter(), c, false);
executePayment(ability, source, c);
} else {
// Put counter on chosen card
for (final Card card : this.getList()) {
card.addCounter(this.getCounter(), 1, false);
executePayment(ability, card);
}
}
}
@@ -179,24 +238,28 @@ public class CostPutCounter extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
Integer c = this.convertAmount();
if (c == null) {
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final Card source = ability.getSourceCard();
Integer c = getNumberOfCounters(ability);
if (this.payCostFromSource()) {
source.addCounter(this.getCounter(), c, false);
payment.setPaidManaPart(this);
this.addToList(source);
executePayment(ability, source, c);
return true;
} else {
final Input inp = CostPutCounter.putCounterType(ability, this.getType(), payment, this, c);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
return false;
InputPayment inp = new InputPayCostPutCounter(this.getType(), this, c, ability);
FThreads.setInputAndWait(inp);
return inp.isPaid();
}
}
private Integer getNumberOfCounters(final SpellAbility ability) {
Integer c = this.convertAmount();
if (c == null) {
c = AbilityUtils.calculateAmount(ability.getSourceCard(), this.getAmount(), ability);
}
return c;
}
/*
* (non-Javadoc)
*
@@ -211,13 +274,7 @@ public class CostPutCounter extends CostPartWithList {
this.addToList(source);
return true;
} else {
Integer c = this.convertAmount();
if (c == null) {
c = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
}
final List<Card> typeList =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ai, source);
final List<Card> typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ai, source);
Card card = null;
if (this.getType().equals("Creature.YouCtrl")) {
@@ -230,83 +287,22 @@ public class CostPutCounter extends CostPartWithList {
return true;
}
/**
* <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 costPutCounter
* TODO
* @param nNeeded
* the n needed
* @return a {@link forge.control.input.Input} object.
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
public static Input putCounterType(final SpellAbility sa, final String type, final CostPayment payment,
final CostPutCounter costPutCounter, final int nNeeded) {
final Input target = new Input() {
private static final long serialVersionUID = 2685832214519141903L;
private List<Card> typeList;
private int nPut = 0;
@Override
public void executePayment(SpellAbility ability, Card targetCard){
executePayment(ability, targetCard, 1);
}
@Override
public void showMessage() {
if ((nNeeded == 0) || (nNeeded == this.nPut)) {
this.done();
}
public void executePayment(SpellAbility ability, Card targetCard, int c) {
targetCard.addCounter(this.getCounter(), c, false);
this.addToList(targetCard);
}
final StringBuilder msg = new StringBuilder("Put ");
final int nLeft = nNeeded - this.nPut;
msg.append(nLeft).append(" ");
msg.append(costPutCounter.getCounter()).append(" on ");
msg.append(costPutCounter.getDescriptiveType());
if (nLeft > 1) {
msg.append("s");
}
this.typeList = CardLists.getValidCards(sa.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard());
CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString());
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
this.cancel();
}
@Override
public void selectCard(final Card card) {
if (this.typeList.contains(card)) {
this.nPut++;
costPutCounter.addToList(card);
card.addCounter(costPutCounter.getCounter(), 1, false);
if (nNeeded == this.nPut) {
this.done();
} else {
this.showMessage();
}
}
}
public void done() {
this.stop();
payment.paidCost(costPutCounter);
}
public void cancel() {
this.stop();
costPutCounter.addListToHash(sa, "CounterPut");
payment.cancelCost();
}
};
return target;
@Override
public String getHashForList() {
return "CounterPut";
}
}

View File

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

View File

@@ -19,22 +19,19 @@ package forge.card.cost;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
import forge.Card;
import forge.CardLists;
import forge.Singletons;
import forge.FThreads;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.control.input.InputSelectCards;
import forge.control.input.InputSelectCardsFromList;
import forge.game.GameState;
import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
import forge.gui.GuiDialog;
/**
* The Class CostReturn.
@@ -123,7 +120,7 @@ public class CostReturn extends CostPartWithList {
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
for (final Card c : this.getList()) {
Singletons.getModel().getGame().getAction().moveToHand(c);
executePayment(ability, c);
}
}
@@ -135,8 +132,9 @@ public class CostReturn extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final String amount = this.getAmount();
final Card source = ability.getSourceCard();
Integer c = this.convertAmount();
final Player activator = ability.getActivatingPlayer();
final List<Card> list = activator.getCardsIn(ZoneType.Battlefield);
@@ -150,13 +148,28 @@ public class CostReturn extends CostPartWithList {
}
}
if (this.payCostFromSource()) {
final Input inp = CostReturn.returnThis(ability, payment, this);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
final Card card = ability.getSourceCard();
if (card.getController() == ability.getActivatingPlayer() && card.isInPlay()) {
boolean confirm = GuiDialog.confirm(card, card.getName() + " - Return to Hand?");
if (confirm) {
executePayment(ability, card);
}
return confirm;
}
} else {
final Input inp = CostReturn.returnType(ability, this.getType(), payment, this, c);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
List<Card> validCards = CardLists.getValidCards(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield), this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard());
InputSelectCards inp = new InputSelectCardsFromList(c, c, validCards);
inp.setMessage("Return %d " + this.getType() + " " + this.getType() + " card(s) to hand");
FThreads.setInputAndWait(inp);
if (inp.hasCancelled())
return false;
for(Card crd : inp.getSelected())
executePayment(ability, crd);
return true;
}
return false;
return false;
}
/*
@@ -178,140 +191,31 @@ public class CostReturn extends CostPartWithList {
}
this.setList(ComputerUtil.chooseReturnType(ai, this.getType(), source, ability.getTargetCard(), c));
if (this.getList() == null) {
if (this.getList().isEmpty()) {
return false;
}
}
return true;
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
ability.getActivatingPlayer().getGame().getAction().moveToHand(targetCard);
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "Returned";
}
// Inputs
/**
* <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.List;
import forge.Card;
import forge.CardLists;
import forge.FThreads;
import forge.Singletons;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.control.input.InputPayment;
import forge.game.GameState;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
/**
@@ -41,6 +40,87 @@ import forge.view.ButtonUtil;
public class CostReveal extends CostPartWithList {
// Reveal<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.
*
@@ -140,6 +220,8 @@ public class CostReveal extends CostPartWithList {
@Override
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
GuiChoose.oneOrNone("Revealed cards:", this.getList());
for(Card c: getList()) // should not throw concurrent modification here - no items should be added.
executePayment(ability, c);
}
/*
@@ -150,17 +232,19 @@ public class CostReveal extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final Player activator = ability.getActivatingPlayer();
final Card source = ability.getSourceCard();
final String amount = this.getAmount();
this.resetList();
if (this.payCostFromSource()) {
this.addToList(source);
payment.setPaidManaPart(this);
executePayment(ability, source);
return true;
} else if (this.getType().equals("Hand")) {
this.setList(new ArrayList<Card>(activator.getCardsIn(ZoneType.Hand)));
payment.setPaidManaPart(this);
for(Card c : activator.getCardsIn(ZoneType.Hand))
executePayment(ability, c);
return true;
} else {
Integer num = this.convertAmount();
@@ -175,16 +259,12 @@ public class CostReveal extends CostPartWithList {
num = AbilityUtils.calculateAmount(source, amount, ability);
}
}
if (num > 0) {
final Input inp = CostReveal.inputRevealCost(this.getType(), handList, payment, this, ability, num);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
return false;
} else {
payment.setPaidManaPart(this);
}
if ( num == 0 ) return true;
InputPayment inp = new InputPayReveal(this, this.getType(), handList, ability, num);
FThreads.setInputAndWait(inp);
return inp.isPaid();
}
this.addListToHash(ability, "Revealed");
return true;
}
/*
@@ -220,102 +300,24 @@ public class CostReveal extends CostPartWithList {
return sb.toString();
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
// write code to actually reveal card
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "Revealed";
}
// Inputs
/**
* <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.List;
import javax.swing.JOptionPane;
import forge.Card;
import forge.CardLists;
import forge.Singletons;
import forge.FThreads;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.control.input.InputPayment;
import forge.game.GameState;
import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.GuiDialog;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
@@ -41,6 +39,70 @@ import forge.view.ButtonUtil;
*/
public class CostSacrifice extends CostPartWithList {
/**
* TODO: Write javadoc for this type.
*
*/
public static final class InputPayCostSacrificeFromList extends InputPayCostBase {
private final CostSacrifice part;
private final SpellAbility sa;
private final int nNeeded;
private final List<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.
*
@@ -132,7 +194,7 @@ public class CostSacrifice extends CostPartWithList {
public final void payAI(final AIPlayer ai, final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
this.addListToHash(ability, "Sacrificed");
for (final Card c : this.getList()) {
Singletons.getModel().getGame().getAction().sacrifice(c, ability);
executePayment(ability, c);
}
}
@@ -144,8 +206,9 @@ public class CostSacrifice extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final String amount = this.getAmount();
final Card source = ability.getSourceCard();
final String type = this.getType();
final Player activator = ability.getActivatingPlayer();
List<Card> list = new ArrayList<Card>(activator.getCardsIn(ZoneType.Battlefield));
@@ -155,32 +218,36 @@ public class CostSacrifice extends CostPartWithList {
}
if (this.payCostFromSource()) {
final Input inp = CostSacrifice.sacrificeThis(ability, payment, this);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
if (source.getController() == ability.getActivatingPlayer() && source.isInPlay()) {
if (!GuiDialog.confirm(source, source.getName() + " - Sacrifice?"))
return false;
executePayment(ability, source);
return true;
}
} else if (amount.equals("All")) {
this.setList(list);
CostSacrifice.sacrificeAll(ability, payment, this, list);
//this.addListToHash(ability, "Sacrificed");
// TODO Ask First
for (final Card card : list) {
executePayment(ability, card);
}
return true;
} else {
Integer c = this.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(amount);
// Generalize this
if (sVar.equals("XChoice")) {
if (ability.getSVar(amount).equals("XChoice")) {
c = CostUtil.chooseXValue(source, ability, list.size());
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
if (0 == c.intValue()) {
payment.setPaidManaPart(this);
return true;
}
final Input inp = CostSacrifice.sacrificeFromList(ability, payment, this, list, c);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
InputPayment inp = new InputPayCostSacrificeFromList(this, ability, c, list);
FThreads.setInputAndWait(inp);
return inp.isPaid();
}
return false;
}
@@ -222,156 +289,23 @@ public class CostSacrifice extends CostPartWithList {
return true;
}
// Inputs
/**
* <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
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
public static void sacrificeAll(final SpellAbility sa, final CostPayment payment, final CostPart part,
final List<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);
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
this.addToList(targetCard);
ability.getActivatingPlayer().getGame().getAction().sacrifice(targetCard, ability);
}
/**
* <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.
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
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 String getHashForList() {
return "Sacrificed";
}
@Override
public void showMessage() {
if (nNeeded == 0) {
this.done();
}
// Inputs
final StringBuilder msg = new StringBuilder("Sacrifice ");
final int nLeft = nNeeded - this.nSacrifices;
msg.append(nLeft).append(" ");
msg.append(part.getDescriptiveType());
if (nLeft > 1) {
msg.append("s");
}
CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString());
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
this.cancel();
}
@Override
public void selectCard(final Card card) {
if (typeList.contains(card)) {
this.nSacrifices++;
part.addToList(card);
Singletons.getModel().getGame().getAction().sacrifice(card, sa);
typeList.remove(card);
// in case nothing else to sacrifice
if (this.nSacrifices == nNeeded) {
this.done();
} else if (typeList.size() == 0) {
// happen
this.cancel();
} else {
this.showMessage();
}
}
}
public void done() {
this.stop();
part.addListToHash(sa, "Sacrificed");
payment.paidCost(part);
}
public void cancel() {
this.stop();
payment.cancelCost();
}
};
return target;
} // sacrificeType()
/**
* <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)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
// if (!canPay(ability, source, ability.getActivatingPlayer(),
// payment.getCost()))
// return false;
source.tap();
payment.setPaidManaPart(this);
ability.getSourceCard().tap();
return true;
}

View File

@@ -19,21 +19,20 @@ package forge.card.cost;
import java.util.ArrayList;
import java.util.List;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates.Presets;
import forge.FThreads;
import forge.Singletons;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.control.input.InputPayment;
import forge.game.GameState;
import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
/**
@@ -41,6 +40,66 @@ import forge.view.ButtonUtil;
*/
public class CostTapType extends CostPartWithList {
/**
* TODO: Write javadoc for this type.
*
*/
public static final class InputPayCostTapType extends InputPayCostBase {
private final CostTapType tapType;
private final int nCards;
private final List<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.
*
@@ -149,11 +208,12 @@ public class CostTapType extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
List<Card> typeList = new ArrayList<Card>(ability.getActivatingPlayer().getCardsIn(ZoneType.Battlefield));
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard());
typeList = CardLists.filter(typeList, Presets.UNTAPPED);
final String amount = this.getAmount();
final Card source = ability.getSourceCard();
Integer c = this.convertAmount();
if (c == null) {
final String sVar = ability.getSVar(amount);
@@ -164,10 +224,9 @@ public class CostTapType extends CostPartWithList {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
final Input inp = CostTapType.inputTapXCost(this, typeList, ability, payment, c);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
return false;
InputPayment inp = new InputPayCostTapType(this, c, typeList);
FThreads.setInputAndWait(inp);
return inp.isPaid();
}
/*
@@ -205,83 +264,23 @@ public class CostTapType extends CostPartWithList {
return true;
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
targetCard.tap();
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "Tapped";
}
// Inputs
/**
* <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)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
this.resetList();
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final Card source = ability.getSourceCard();
Player activator = ability.getActivatingPlayer();
Card cardToUnattach = findCardToUnattach(source, activator, ability);
if (cardToUnattach != null && GuiDialog.confirm(source, String.format("Unattach %s?", cardToUnattach.toString()))) {
Card equippingCard = cardToUnattach.getEquipping().get(0);
cardToUnattach.unEquipCard(equippingCard);
this.addToList(cardToUnattach);
this.addListToHash(ability, "Unattached");
payment.setPaidManaPart(this);
executePayment(ability, cardToUnattach);
return true;
} else {
payment.setCancel(true);
payment.getRequirements().finishPaying();
return false;
}
return true;
}
private Card findCardToUnattach(final Card source, Player activator, SpellAbility ability) {
@@ -165,4 +158,18 @@ public class CostUnattach extends CostPartWithList {
this.resetList();
return true;
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
targetCard.unEquipCard(targetCard.getEquipping().get(0));
this.addToList(targetCard);
}
@Override
public String getHashForList() {
return "Unattached";
}
}

View File

@@ -92,13 +92,12 @@ public class CostUntap extends CostPart {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
public final boolean payHuman(final SpellAbility ability, final GameState game) {
// if (!canPay(ability, source, ability.getActivatingPlayer(),
// payment.getCost()))
// return false;
source.untap();
payment.setPaidManaPart(this);
ability.getSourceCard().untap();
return true;
}

View File

@@ -18,21 +18,20 @@
package forge.card.cost;
import java.util.List;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates.Presets;
import forge.FThreads;
import forge.Singletons;
import forge.card.ability.AbilityUtils;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.control.input.InputPayment;
import forge.game.GameState;
import forge.game.ai.ComputerUtil;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
/**
@@ -40,6 +39,71 @@ import forge.view.ButtonUtil;
*/
public class CostUntapType extends CostPartWithList {
/**
* TODO: Write javadoc for this type.
*
*/
public static final class InputPayCostUntapY extends InputPayCostBase {
private final int nCards;
private final List<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.
*
@@ -162,12 +226,13 @@ public class CostUntapType extends CostPartWithList {
* forge.Card, forge.card.cost.Cost_Payment)
*/
@Override
public final boolean payHuman(final SpellAbility ability, final Card source, final CostPayment payment, final GameState game) {
final boolean untap = payment.getCost().hasUntapCost();
public final boolean payHuman(final SpellAbility ability, final GameState game) {
final boolean canUntapSource = false; // payment.getCost().hasUntapCost(); - only Crackleburr uses this
List<Card> typeList = Singletons.getModel().getGame().getCardsIn(ZoneType.Battlefield);
typeList = CardLists.getValidCards(typeList, this.getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard());
typeList = CardLists.filter(typeList, Presets.TAPPED);
if (untap) {
final Card source = ability.getSourceCard();
if (canUntapSource) {
typeList.remove(source);
}
final String amount = this.getAmount();
@@ -181,10 +246,9 @@ public class CostUntapType extends CostPartWithList {
c = AbilityUtils.calculateAmount(source, amount, ability);
}
}
final Input inp = CostUntapType.inputUntapYCost(this, typeList, ability, payment, c);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
return false;
InputPayment inp = new InputPayCostUntapY(c, typeList, this);
FThreads.setInputAndWait(inp);
return inp.isPaid();
}
/*
@@ -225,86 +289,23 @@ public class CostUntapType extends CostPartWithList {
return true;
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card)
*/
@Override
public void executePayment(SpellAbility ability, Card targetCard) {
addToList(targetCard);
targetCard.untap();
}
/* (non-Javadoc)
* @see forge.card.cost.CostPartWithList#getHashForList()
*/
@Override
public String getHashForList() {
return "Untapped";
}
// Inputs
/**
* <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 ZERO = new ManaCost(0);
public static final ManaCost ONE = new ManaCost(1);
public static final ManaCost TWO = new ManaCost(2);
public static final ManaCost THREE = new ManaCost(3);
public static final ManaCost FOUR = new ManaCost(4);
public static ManaCost get(int cntColorless) {
switch (cntColorless) {
case 0: return ZERO;
case 1: return ONE;
case 2: return TWO;
case 3: return THREE;
case 4: return FOUR;
}
return cntColorless > 0 ? new ManaCost(cntColorless) : NO_COST;
}
// pass mana cost parser here
private ManaCost(int cmc) {

View File

@@ -42,6 +42,8 @@ public class ManaCostBeingPaid {
private final ArrayList<String> manaNeededToAvoidNegativeEffect = 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"
// or "split hybrid mana" like "2/G 2/G", "2/B 2/B 2/B"
// "GW" can be paid with either G or W
@@ -59,6 +61,7 @@ public class ManaCostBeingPaid {
}
public ManaCostBeingPaid(ManaCost manaCost) {
originalCost = manaCost;
if ( null == manaCost )
return;
@@ -645,4 +648,8 @@ public class ManaCostBeingPaid {
public final ArrayList<String> getManaPaidToAvoidNegativeEffect() {
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.Set;
import org.apache.commons.lang.StringUtils;
import forge.Card;
import forge.GameEntity;
import forge.Singletons;
@@ -1682,25 +1680,4 @@ public abstract class SpellAbility implements ISpellAbility {
public void setCopied(boolean isCopied0) {
this.isCopied = isCopied0;
}
public boolean announceRequirements() {
// Announcing Requirements like Choosing X or Multikicker
// SA Params as comma delimited list
String announce = this.getParam("Announce");
if (announce != null) {
String[] announceVars = announce.split(",");
for(String aVar : announceVars) {
String value = this.getActivatingPlayer().getController().announceRequirements(this, aVar);
if (value == null || !StringUtils.isNumeric(value)) {
return false;
} else if (this.getPayCosts().getCostMana() != null && this.getPayCosts().getCostMana().isxCantBe0()
&& Integer.parseInt(value) == 0) {
return false;
}
this.setSVar(aVar, "Number$" + value);
this.getSourceCard().setSVar(aVar, "Number$" + value);
}
}
return true;
}
}

View File

@@ -19,6 +19,8 @@ package forge.card.spellability;
import java.util.ArrayList;
import org.apache.commons.lang3.StringUtils;
import forge.Card;
import forge.CardCharacteristicName;
import forge.Singletons;
@@ -44,67 +46,26 @@ public class SpellAbilityRequirements {
private Zone fromZone = null;
private Integer zonePosition = null;
/**
* <p>
* Setter for the field <code>skipStack</code>.
* </p>
*
* @param bSkip
* a boolean.
*/
public final void setSkipStack(final boolean bSkip) {
this.skipStack = bSkip;
}
/**
* <p>
* setFree.
* </p>
*
* @param bFree
* a boolean.
*/
public final void setFree(final boolean 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) {
this.ability = sa;
this.select = ts;
this.payment = cp;
}
/**
* <p>
* fillRequirements.
* </p>
*/
public final void fillRequirements() {
this.fillRequirements(false);
}
/**
* <p>
* fillRequirements.
* </p>
*
* @param skipTargeting
* a boolean.
*/
public final void fillRequirements(final boolean skipTargeting) {
if ((this.ability instanceof Spell) && !this.bCasting) {
// remove from hand
@@ -118,14 +79,13 @@ public class SpellAbilityRequirements {
}
}
// freeze Stack. No abilities should go onto the stack while I'm filling
// requirements.
// freeze Stack. No abilities should go onto the stack while I'm filling requirements.
Singletons.getModel().getGame().getStack().freezeStack();
// Announce things like how many times you want to Multikick or the value of X
if (!this.ability.announceRequirements()) {
if (!this.announceRequirements()) {
this.select.setCancel(true);
this.finishedTargeting();
rollbackAbility();
return;
}
@@ -134,128 +94,104 @@ public class SpellAbilityRequirements {
// (or trigger case where its already targeted)
if (!skipTargeting && (this.select.doesTarget() || (this.ability.getSubAbility() != null))) {
this.select.setRequirements(this);
this.select.resetTargets();
this.select.clearTargets();
this.select.chooseTargets();
} else {
this.needPayment();
}
}
/**
* <p>
* finishedTargeting.
* </p>
*/
public final void finishedTargeting() {
if (this.select.isCanceled()) {
// cancel ability during target choosing
final Card c = this.ability.getSourceCard();
// split cards transform back to full form if targeting is canceled
if (c.isSplitCard()) {
c.setState(CardCharacteristicName.Original);
if (this.select.isCanceled()) {
rollbackAbility();
return;
}
if (this.bCasting && !c.isCopiedSpell()) { // and not a copy
// add back to where it came from
Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition);
}
this.select.resetTargets();
Singletons.getModel().getGame().getStack().removeFromFrozenStack(this.ability);
return;
} else {
this.needPayment();
}
}
/**
* <p>
* needPayment.
* </p>
*/
public final void needPayment() {
// Payment
if (!this.isFree) {
this.startPaying();
} else {
this.finishPaying();
this.payment.setRequirements(this);
this.payment.changeCost();
this.payment.payCost();
}
}
/**
* <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) {
this.select.resetTargets();
}
this.ability.resetOnceResolved();
this.payment.cancelPayment();
Singletons.getModel().getGame().getStack().clearFrozen();
rollbackAbility();
return;
}
else if (this.isFree || this.payment.isAllPaid()) {
if (this.skipStack) {
AbilityUtils.resolve(this.ability, false);
} else {
this.addAbilityToStack();
this.enusureAbilityHasDescription(this.ability);
this.ability.getActivatingPlayer().getManaPool().clearManaPaid(this.ability, false);
Singletons.getModel().getGame().getStack().addAndUnfreeze(this.ability);
}
this.select.resetTargets();
// Warning about this - resolution may come in another thread, and it would still need its targets
this.select.clearTargets();
Singletons.getModel().getGame().getAction().checkStateEffects();
}
}
/**
* <p>
* addAbilityToStack.
* </p>
*/
public final void addAbilityToStack() {
// For older abilities that don't setStackDescription set it here
if (this.ability.getStackDescription().equals("")) {
final StringBuilder sb = new StringBuilder();
sb.append(this.ability.getSourceCard().getName());
if (this.ability.getTarget() != null) {
final ArrayList<Object> targets = this.ability.getTarget().getTargets();
if (targets.size() > 0) {
sb.append(" - Targeting ");
for (final Object o : targets) {
sb.append(o.toString()).append(" ");
}
}
}
private void rollbackAbility() {
// cancel ability during target choosing
final Card c = this.ability.getSourceCard();
this.ability.setStackDescription(sb.toString());
// split cards transform back to full form if targeting is canceled
if (c.isSplitCard()) {
c.setState(CardCharacteristicName.Original);
}
this.ability.getActivatingPlayer().getManaPool().clearManaPaid(this.ability, false);
Singletons.getModel().getGame().getStack().addAndUnfreeze(this.ability);
if (this.bCasting && !c.isCopiedSpell()) { // and not a copy
// add back to where it came from
Singletons.getModel().getGame().getAction().moveTo(this.fromZone, c, this.zonePosition);
}
if (this.select != null) {
this.select.clearTargets();
}
this.ability.resetOnceResolved();
this.payment.cancelPayment();
Singletons.getModel().getGame().getStack().clearFrozen();
// Singletons.getModel().getGame().getStack().removeFromFrozenStack(this.ability);
}
public boolean announceRequirements() {
// Announcing Requirements like Choosing X or Multikicker
// SA Params as comma delimited list
String announce = ability.getParam("Announce");
if (announce != null) {
for(String aVar : announce.split(",")) {
String value = ability.getActivatingPlayer().getController().announceRequirements(ability, aVar);
if (value == null || !StringUtils.isNumeric(value)) {
return false;
} else if (ability.getPayCosts().getCostMana() != null && !ability.getPayCosts().getCostMana().canXbe0()
&& Integer.parseInt(value) == 0) {
return false;
}
ability.setSVar(aVar, "Number$" + value);
ability.getSourceCard().setSVar(aVar, "Number$" + value);
}
}
return true;
}
private void enusureAbilityHasDescription(SpellAbility ability) {
if (!StringUtils.isBlank(ability.getStackDescription()))
return;
// For older abilities that don't setStackDescription set it here
final StringBuilder sb = new StringBuilder();
sb.append(ability.getSourceCard().getName());
if (ability.getTarget() != null) {
final ArrayList<Object> targets = ability.getTarget().getTargets();
if (targets.size() > 0) {
sb.append(" - Targeting ");
for (final Object o : targets) {
sb.append(o.toString()).append(" ");
}
}
}
ability.setStackDescription(sb.toString());
}
}

View File

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

View File

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

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

View File

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

View File

@@ -34,7 +34,7 @@ import forge.view.ButtonUtil;
* @author Forge
* @version $Id$
*/
public class InputCleanup extends Input {
public class InputCleanup extends InputBase {
/** Constant <code>serialVersionUID=-4164275418971547948L</code>. */
private static final long serialVersionUID = -4164275418971547948L;
private final GameState game;
@@ -82,18 +82,4 @@ public class InputCleanup extends Input {
}
}
} // 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.PlayerController;
import forge.game.zone.MagicStack;
import forge.gui.match.controllers.CMessage;
import forge.util.MyObservable;
/**
@@ -40,21 +39,7 @@ public class InputControl extends MyObservable implements java.io.Serializable {
/** Constant <code>serialVersionUID=3955194449319994301L</code>. */
private static final long serialVersionUID = 3955194449319994301L;
private Input 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>
@@ -62,46 +47,12 @@ public class InputControl extends MyObservable implements java.io.Serializable {
* </p>
*
* @param in
* a {@link forge.control.input.Input} object.
* a {@link forge.control.input.InputBase} object.
*/
public final void setInput(final Input in) {
boolean isInputEmpty = this.input == null || this.input instanceof InputPassPriority;
//System.out.println(in.getClass().getName());
if (!this.game.getStack().isResolving() && isInputEmpty) {
this.input = in;
} else {
this.inputStack.add(in);
}
this.updateObservers();
}
/**
* <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.inputStack.push(in);
// System.out.print("Current: " + input + "; Stack = " + inputStack);
this.updateObservers();
}
@@ -110,10 +61,10 @@ public class InputControl extends MyObservable implements java.io.Serializable {
* Getter for the field <code>input</code>.
* </p>
*
* @return a {@link forge.control.input.Input} object.
* @return a {@link forge.control.input.InputBase} object.
*/
public final Input getInput() {
return this.input;
return inputStack.isEmpty() ? null : this.inputStack.peek();
}
/**
@@ -122,8 +73,8 @@ public class InputControl extends MyObservable implements java.io.Serializable {
* </p>
*/
public final void clearInput() {
this.input = null;
this.inputStack.clear();
this.updateObservers();
}
@@ -135,19 +86,34 @@ public class InputControl extends MyObservable implements java.io.Serializable {
* @param update
* a boolean.
*/
public final void resetInput() {
this.input = null;
public final void removeInput(Input inp) {
Input topMostInput = inputStack.isEmpty() ? null : inputStack.pop();
// StackTraceElement[] trace = Thread.currentThread().getStackTrace();
// System.out.printf("%s > Remove input %s -- called from %s%n", FThreads.isEDT() ? "EDT" : "TRD", topMostInput, trace[2].toString());
// if( trace[2].toString().contains("InputBase.stop"))
// for(StackTraceElement se : trace) {
// System.out.println(se.toString());
// }
if( topMostInput != inp )
throw new RuntimeException("Inputs adding/removal into stack is imbalanced! Check your code again!");
this.updateObservers();
}
public final boolean isEmpty() {
return inputStack.isEmpty();
}
/**
* <p>
* updateInput.
* </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() )
return new InputMulligan();
@@ -157,36 +123,11 @@ public class InputControl extends MyObservable implements java.io.Serializable {
final Player priority = handler.getPriorityPlayer();
final MagicStack stack = game.getStack();
// TODO this resolving portion needs more work, but fixes Death Cloud
// issues
if (this.urgentInputStack.size() > 0) {
if (this.input != null) {
return this.input;
}
// if an SA is resolving, only change input for something that is
// part of the resolving SA
this.changeInput(this.urgentInputStack.pop());
return this.input;
}
if (stack.isResolving()) {
return null;
}
if (this.input != null) {
return this.input;
}
if (!this.inputStack.isEmpty()) { // incoming input to Control
this.changeInput(this.inputStack.pop());
return this.input;
}
if (handler.hasPhaseEffects()) {
// Handle begin phase stuff, then start back from the top
handler.handleBeginPhase();
return this.getActualInput();
return this.inputStack.peek();
}
// If the Phase we're in doesn't allow for Priority, return null to move
@@ -205,7 +146,7 @@ public class InputControl extends MyObservable implements java.io.Serializable {
stack.freezeStack();
if (playerTurn.isHuman() && !playerTurn.getController().mayAutoPass(phase)) {
game.getCombat().initiatePossibleDefenders(playerTurn.getOpponents());
return new InputAttack();
return new InputAttack(game);
}
break;
@@ -236,7 +177,7 @@ public class InputControl extends MyObservable implements java.io.Serializable {
// priority
boolean prioritySkip = pc.mayAutoPass(phase) || pc.isUiSetToSkipPhase(playerTurn, phase);
if (this.game.getStack().isEmpty() && prioritySkip) {
if (game.getStack().isEmpty() && prioritySkip) {
pc.passPriority();
return null;
} else
@@ -245,19 +186,23 @@ public class InputControl extends MyObservable implements java.io.Serializable {
return pc.getDefaultInput();
} // getInput()
public final void setNewInput(GameState game) {
PhaseHandler ph = game.getPhaseHandler();
/**
* TODO: Write javadoc for this method.
*/
private final static InputLockUI inputLock = new InputLockUI();
public void lock() {
setInput(inputLock);
}
final Input tmp = getActualInput();
//String message = String.format("%s's %s, priority of %s [%sP] input is %s", ph.getPlayerTurn(), ph.getPhase(), ph.getPriorityPlayer(), ph.isPlayerPriorityAllowed() ? "+" : "-", tmp == null ? "null" : tmp.getClass().getSimpleName());
//System.out.println(message);
if (tmp != null) {
//System.out.println(ph.getPlayerTurn() + "'s " + ph.getPhase() + ", priority of " + ph.getPriorityPlayer() + " @ input is " + tmp.getClass().getName() );
CMessage.SINGLETON_INSTANCE.getInputControl().setInput(tmp);
} else if (!ph.isPlayerPriorityAllowed()) {
// System.out.println("cannot have priority, forced to pass");
ph.getPriorityPlayer().getController().passPriority();
}
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

View File

@@ -1,18 +1,51 @@
package forge.control.input;
import forge.gui.match.CMatchUI;
import java.util.concurrent.atomic.AtomicInteger;
import forge.FThreads;
import forge.view.ButtonUtil;
/**
* TODO: Write javadoc for this type.
*
*/
public class InputLockUI extends Input {
public class InputLockUI extends InputBase {
private static final long serialVersionUID = 5777143577098597374L;
private final AtomicInteger iCall = new AtomicInteger();
public void showMessage() {
ButtonUtil.disableAll();
CMatchUI.SINGLETON_INSTANCE.showMessage("Waiting for actions...");
int ixCall = 1 + iCall.getAndIncrement();
FThreads.delay(500, new InputUpdater(ixCall));
}
@Override
public String toString() {
return "lockUI";
}
private class InputUpdater implements Runnable {
final int ixCall;
public InputUpdater(final int idxCall) {
ixCall = idxCall;
}
@Override
public void run() {
if ( ixCall != iCall.get() || !isActive()) // cancel the message if it's not from latest call or input is gone already
return;
FThreads.invokeInEDT(showMessageFromEdt);
}
};
private final Runnable showMessageFromEdt = new Runnable() {
@Override
public void run() {
ButtonUtil.disableAll();
showMessage("Waiting for actions...");
}
};
}

View File

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

View File

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

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

View File

@@ -17,17 +17,12 @@
*/
package forge.control.input;
import forge.Card;
import forge.Command;
import forge.Singletons;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostBeingPaid;
import forge.card.spellability.SpellAbility;
import forge.game.GameState;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.framework.SDisplayUtil;
import forge.gui.match.CMatchUI;
import forge.gui.match.views.VMessage;
import forge.view.ButtonUtil;
//if cost is paid, Command.execute() is called
@@ -40,19 +35,17 @@ import forge.view.ButtonUtil;
* @author Forge
* @version $Id$
*/
public class InputPayManaExecuteCommands extends InputPayManaBase {
public class InputPayManaExecuteCommands extends InputPayManaBase implements InputPayment {
/**
* Constant <code>serialVersionUID=3836655722696348713L</code>.
*/
private static final long serialVersionUID = 3836655722696348713L;
private String originalManaCost;
private ManaCost originalManaCost;
private String message = "";
private SpellAbility fakeAbility;
private Command paidCommand;
private Command unpaidCommand;
private boolean bPaid = false;
public boolean isPaid() { return bPaid; }
// only used for X costs:
private boolean showOnlyOKButton = false;
@@ -72,8 +65,8 @@ public class InputPayManaExecuteCommands extends InputPayManaBase {
* @param unpaidCommand2
* a {@link forge.Command} object.
*/
public InputPayManaExecuteCommands(final GameState game, final String prompt, final String manaCost2, final Command paidCommand2, final Command unpaidCommand2) {
this(game, prompt, manaCost2, paidCommand2, unpaidCommand2, false);
public InputPayManaExecuteCommands(final GameState game, final String prompt, final ManaCost manaCost2) {
this(game, prompt, manaCost2, false);
}
/**
@@ -92,25 +85,19 @@ public class InputPayManaExecuteCommands extends InputPayManaBase {
* @param showOKButton
* a boolean.
*/
public InputPayManaExecuteCommands(final GameState game, final String prompt, final String manaCost2, final Command paid, final Command unpaid, final boolean showOKButton) {
super(game);
this.fakeAbility = new SpellAbility(null) {
public InputPayManaExecuteCommands(final GameState game, final String prompt, final ManaCost manaCost2, final boolean showOKButton) {
super(game, new SpellAbility(null) {
@Override
public void resolve() {
}
public void resolve() {}
@Override
public boolean canPlay() {
return false;
}
};
public boolean canPlay() { return false; }
});
this.originalManaCost = manaCost2;
this.phyLifeToLose = 0;
this.message = prompt;
this.manaCost = new ManaCostBeingPaid(this.originalManaCost);
this.paidCommand = paid;
this.unpaidCommand = unpaid;
this.showOnlyOKButton = showOKButton;
}
@@ -138,39 +125,24 @@ public class InputPayManaExecuteCommands extends InputPayManaBase {
}
}
/** {@inheritDoc} */
@Override
public final void selectCard(final Card card) {
// only tap card if the mana is needed
this.manaCost = activateManaAbility(this.fakeAbility, card, this.manaCost);
if (card.getManaAbility().isEmpty() || card.isInZone(ZoneType.Hand)) {
SDisplayUtil.remind(VMessage.SINGLETON_INSTANCE);
}
if (this.manaCost.isPaid()) {
this.done();
} else {
this.showMessage();
}
}
private void done() {
protected void done() {
if (this.phyLifeToLose > 0) {
Singletons.getControl().getPlayer().payLife(this.phyLifeToLose, null);
}
this.paidCommand.execute();
this.resetManaCost();
Singletons.getControl().getPlayer().getManaPool().clearManaPaid(this.fakeAbility, false);
Singletons.getControl().getPlayer().getManaPool().clearManaPaid(this.saPaidFor, false);
bPaid = true;
this.stop();
}
/** {@inheritDoc} */
@Override
public final void selectButtonCancel() {
this.unpaidCommand.execute();
this.resetManaCost();
Singletons.getControl().getPlayer().getManaPool().refundManaPaid(this.fakeAbility, true);
Singletons.getControl().getPlayer().getManaPool().refundManaPaid(this.saPaidFor, true);
bPaid = false;
this.stop();
}
@@ -178,8 +150,8 @@ public class InputPayManaExecuteCommands extends InputPayManaBase {
@Override
public final void selectButtonOK() {
if (this.showOnlyOKButton) {
bPaid = false;
this.stop();
this.unpaidCommand.execute();
}
}
@@ -201,24 +173,7 @@ public class InputPayManaExecuteCommands extends InputPayManaBase {
msg.append("\n(Click on your life total to pay life for phyrexian mana.)");
}
CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString());
showMessage(msg.toString());
checkIfAlredyPaid();
}
/* (non-Javadoc)
* @see forge.control.input.InputMana#selectManaPool()
*/
@Override
public void selectManaPool(String color) {
this.manaCost = activateManaAbility(color, this.fakeAbility, this.manaCost);
if (this.manaCost.isPaid()) {
this.done();
} else {
this.showMessage();
}
}
@Override public void isClassUpdated() {
}
}

View File

@@ -3,62 +3,23 @@ package forge.control.input;
import forge.Card;
import forge.Singletons;
import forge.card.cost.CostPartMana;
import forge.card.cost.CostPayment;
import forge.card.mana.ManaCostBeingPaid;
import forge.card.spellability.SpellAbility;
import forge.game.GameState;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
public class InputPayManaOfCostPayment extends InputPayManaBase {
private final CostPartMana costMana;
// I would kill the one who made 2 classes like above
private final String originalManaCost;
private final SpellAbility sa;
private final int manaToAdd;
private final CostPayment payment;
public InputPayManaOfCostPayment(final GameState game, CostPartMana costMana, SpellAbility spellAbility, final CostPayment payment, int toAdd) {
super(game);
public InputPayManaOfCostPayment(final GameState game, CostPartMana costMana, SpellAbility spellAbility, int toAdd) {
super(game, spellAbility);
manaCost = new ManaCostBeingPaid(costMana.getManaToPay());
manaCost.increaseColorlessMana(toAdd);
this.costMana = costMana;
originalManaCost = costMana.getMana();
sa = spellAbility;
manaToAdd = toAdd;
this.payment = payment;
}
private static final long serialVersionUID = 3467312982164195091L;
private int phyLifeToLose = 0;
private void resetManaCost() {
this.manaCost = new ManaCostBeingPaid(this.originalManaCost);
this.phyLifeToLose = 0;
}
@Override
public void selectCard(final Card card) {
// prevent cards from tapping themselves if ability is a
// tapability, although it should already be tapped
this.manaCost = activateManaAbility(sa, card, this.manaCost);
if (this.manaCost.isPaid()) {
this.done();
} else if (Singletons.getModel().getMatch().getInput().getInput() == this) {
this.showMessage();
}
}
@Override
public void selectPlayer(final Player player) {
if (player == whoPays) {
@@ -70,65 +31,34 @@ public class InputPayManaOfCostPayment extends InputPayManaBase {
}
}
private void done() {
final Card source = sa.getSourceCard();
@Override
protected void done() {
final Card source = saPaidFor.getSourceCard();
if (this.phyLifeToLose > 0) {
Singletons.getControl().getPlayer().payLife(this.phyLifeToLose, source);
}
source.setColorsPaid(this.manaCost.getColorsPaid());
source.setSunburstValue(this.manaCost.getSunburst());
this.resetManaCost();
this.stop();
if (costMana.hasNoXManaCost() || (manaToAdd > 0)) {
payment.paidCost(costMana);
} else {
source.setXManaCostPaid(0);
final Input inp = new InputPayManaX(game, sa, payment, costMana);
Singletons.getModel().getMatch().getInput().setInputInterrupt(inp);
}
// If this is a spell with convoke, re-tap all creatures used
// for it.
// This is done to make sure Taps triggers go off at the right
// time
// (i.e. AFTER cost payment, they are tapped previously as well
// so that
// any mana tapabilities can't be used in payment as well as
// being tapped for convoke)
if (sa.getTappedForConvoke() != null) {
for (final Card c : sa.getTappedForConvoke()) {
c.setTapped(false);
c.tap();
}
sa.clearTappedForConvoke();
}
// If this is a spell with convoke, re-tap all creatures used for it.
// This is done to make sure Taps triggers go off at the right time
// (i.e. AFTER cost payment, they are tapped previously as well so that
// any mana tapabilities can't be used in payment as well as being tapped for convoke)
handleConvokedCards(false);
stop();
}
@Override
public void selectButtonCancel() {
// If we're paying for a spell with convoke, untap all creatures
// used for it.
if (sa.getTappedForConvoke() != null) {
for (final Card c : sa.getTappedForConvoke()) {
c.setTapped(false);
}
sa.clearTappedForConvoke();
}
this.stop();
this.resetManaCost();
payment.cancelCost();
Singletons.getControl().getPlayer().getZone(ZoneType.Battlefield).updateObservers();
handleConvokedCards(true);
stop();
}
@Override
public void showMessage() {
ButtonUtil.enableOnlyCancel();
final String displayMana = manaCost.toString().replace("X", "").trim();
CMatchUI.SINGLETON_INSTANCE.showMessage("Pay Mana Cost: " + displayMana);
final StringBuilder msg = new StringBuilder("Pay Mana Cost: " + displayMana);
if (this.phyLifeToLose > 0) {
@@ -141,23 +71,7 @@ public class InputPayManaOfCostPayment extends InputPayManaBase {
msg.append("\n(Click on your life total to pay life for phyrexian mana.)");
}
CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString());
if (manaCost.isPaid()) {
this.done();
}
}
@Override
public void selectManaPool(String color) {
manaCost = InputPayManaBase.activateManaAbility(color, sa, this.manaCost);
if (this.manaCost.isPaid()) {
this.done();
} else if (Singletons.getModel().getMatch().getInput().getInput() == this) {
this.showMessage();
}
}
@Override public void isClassUpdated() {
showMessage(msg.toString());
checkIfAlredyPaid();
}
}

View File

@@ -18,13 +18,11 @@
package forge.control.input;
import forge.Card;
import forge.Singletons;
import forge.card.mana.ManaCostBeingPaid;
import forge.card.spellability.SpellAbility;
import forge.game.GameState;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.match.CMatchUI;
import forge.view.ButtonUtil;
//pays the cost of a card played from the player's hand
@@ -35,61 +33,21 @@ public class InputPayManaSimple extends InputPayManaBase {
/** Constant <code>serialVersionUID=3467312982164195091L</code>. */
private static final long serialVersionUID = 3467312982164195091L;
private boolean skipStack;
private final SpellAbility spell;
private final Card originalCard;
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) {
super(game);
super(game, sa);
this.originalManaCost = manaCostToPay.toString(); // Change
this.originalCard = sa.getSourceCard();
this.spell = sa;
if (sa.getSourceCard().isCopiedSpell() && sa.isSpell()) {
this.manaCost = new ManaCostBeingPaid("0");
game.getStack().add(this.spell);
game.getStack().add(this.saPaidFor);
} else {
this.manaCost = manaCostToPay;
}
}
/**
@@ -102,24 +60,9 @@ public class InputPayManaSimple extends InputPayManaBase {
this.phyLifeToLose = 0;
}
/** {@inheritDoc} */
@Override
public final void selectCard(final Card card) {
// this is a hack, to prevent lands being able to use mana to pay their
// own abilities from cards like
// Kher Keep, Pendelhaven, Blinkmoth Nexus, and Mikokoro, Center of the
// Sea, ....
this.manaCost = activateManaAbility(this.spell, card, this.manaCost);
// only show message if this is the active input
if (Singletons.getModel().getMatch().getInput().getInput() == this) {
this.showMessage();
}
protected void onManaAbilityPaid() {
if (this.manaCost.isPaid()) {
this.originalCard.setSunburstValue(this.manaCost.getSunburst());
this.done();
}
}
@@ -142,59 +85,32 @@ public class InputPayManaSimple extends InputPayManaBase {
* done.
* </p>
*/
private void done() {
@Override
protected void done() {
if (this.phyLifeToLose > 0) {
whoPays.payLife(this.phyLifeToLose, this.originalCard);
}
if (this.spell.getSourceCard().isCopiedSpell()) {
Singletons.getModel().getMatch().getInput().resetInput();
} else {
whoPays.getManaPool().clearManaPaid(this.spell, false);
if (!this.saPaidFor.getSourceCard().isCopiedSpell()) {
whoPays.getManaPool().clearManaPaid(this.saPaidFor, false);
this.resetManaCost();
if (this.spell.isSpell()) {
this.spell.setSourceCard(game.getAction().moveToStack(this.originalCard));
if (this.saPaidFor.isSpell()) {
this.saPaidFor.setSourceCard(game.getAction().moveToStack(this.originalCard));
}
if (this.skipStack) {
this.spell.resolve();
} else {
game.getStack().add(this.spell);
}
Singletons.getModel().getMatch().getInput().resetInput();
// If this is a spell with convoke, re-tap all creatures used for
// it.
// This is done to make sure Taps triggers go off at the right time
// (i.e. AFTER cost payment, they are tapped previously as well so
// that
// any mana tapabilities can't be used in payment as well as being
// tapped for convoke)
if (this.spell.getTappedForConvoke() != null) {
for (final Card c : this.spell.getTappedForConvoke()) {
c.setTapped(false);
c.tap();
}
this.spell.clearTappedForConvoke();
}
handleConvokedCards(false);
}
stop();
}
/** {@inheritDoc} */
@Override
public final void selectButtonCancel() {
// If this is a spell with convoke, untap all creatures used for it.
if (this.spell.getTappedForConvoke() != null) {
for (final Card c : this.spell.getTappedForConvoke()) {
c.setTapped(false);
}
this.spell.clearTappedForConvoke();
}
handleConvokedCards(true);
this.resetManaCost();
whoPays.getManaPool().refundManaPaid(this.spell, true);
whoPays.getManaPool().refundManaPaid(this.saPaidFor, true);
whoPays.getZone(ZoneType.Battlefield).updateObservers(); // DO
this.stop();
@@ -216,32 +132,12 @@ public class InputPayManaSimple extends InputPayManaBase {
msg.append("\n(Click on your life total to pay life for phyrexian mana.)");
}
CMatchUI.SINGLETON_INSTANCE.showMessage(msg.toString());
// has its own variant of checkIfPaid
showMessage(msg.toString());
if (this.manaCost.isPaid() && !new ManaCostBeingPaid(this.originalManaCost).isPaid()) {
this.originalCard.setSunburstValue(this.manaCost.getSunburst());
this.done();
}
}
/* (non-Javadoc)
* @see forge.control.input.InputMana#selectManaPool(String)
*/
@Override
public void selectManaPool(String color) {
this.manaCost = InputPayManaBase.activateManaAbility(color, this.spell, this.manaCost);
// only show message if this is the active input
if (Singletons.getModel().getMatch().getInput().getInput() == this) {
this.showMessage();
}
if (this.manaCost.isPaid()) {
this.originalCard.setSunburstValue(this.manaCost.getSunburst());
this.done();
}
}
@Override public void isClassUpdated() {
}
}

View File

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

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

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 org.apache.commons.lang.time.StopWatch;
import forge.deck.io.DeckGroupSerializer;
import forge.deck.io.DeckSerializer;
import forge.deck.io.OldDeckParser;
import forge.gui.toolbox.FSkin;
import forge.properties.NewConstants;
import forge.util.storage.IStorage;
import forge.util.storage.StorageImmediatelySerialized;
@@ -44,6 +47,9 @@ public class CardCollections {
* @param file the file
*/
public CardCollections() {
FSkin.setProgessBarMessage("Loading decks");
StopWatch sw = new StopWatch();
sw.start();
this.constructed = new StorageImmediatelySerialized<Deck>(new DeckSerializer(new File(NewConstants.DECK_CONSTRUCTED_DIR), true));
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)));
@@ -51,8 +57,10 @@ public class CardCollections {
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)));
System.out.printf("Read decks: %d constructed, %d sealed, %d draft, %d cubes, %d scheme, %d planar.%n", constructed.size(), sealed.size(), draft.size(), cube.size(), scheme.size(), plane.size());
sw.stop();
System.out.printf("Read decks (%d ms): %d constructed, %d sealed, %d draft, %d cubes, %d scheme, %d planar.%n", sw.getTime(), constructed.size(), sealed.size(), draft.size(), cube.size(), scheme.size(), plane.size());
// int sum = constructed.size() + sealed.size() + draft.size() + cube.size() + scheme.size() + plane.size();
// FSkin.setProgessBarMessage(String.format("Loaded %d decks in %f sec", sum, sw.getTime() / 1000f ));
// remove this after most people have been switched to new layout
final OldDeckParser oldParser = new OldDeckParser(this.constructed, this.draft, this.sealed, this.cube);
oldParser.tryParse();

View File

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

View File

@@ -2,7 +2,6 @@ package forge.game;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.Lists;
import forge.Card;
@@ -10,6 +9,7 @@ import forge.CardCharacteristicName;
import forge.CardColor;
import forge.CardLists;
import forge.CardPredicates;
import forge.FThreads;
import forge.card.MagicColor;
import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType;
@@ -23,7 +23,6 @@ import forge.card.spellability.SpellAbilityRequirements;
import forge.card.spellability.Target;
import forge.card.spellability.TargetSelection;
import forge.card.staticability.StaticAbility;
import forge.control.input.InputControl;
import forge.control.input.InputPayManaSimple;
import forge.game.ai.ComputerUtilCard;
import forge.game.player.Player;
@@ -37,17 +36,12 @@ import forge.gui.GuiChoose;
public class GameActionPlay {
private final GameState game;
private InputControl matchInput;
public GameActionPlay(final GameState game0) {
game = game0;
}
void setMatchInput(InputControl input) {
this.matchInput = input; // TODO: Add 0 to parameter's name.
}
public final void playCardWithoutManaCost(final Card c, Player player) {
final List<SpellAbility> choices = c.getBasicSpells();
// TODO add Buyback, Kicker, ... , spells here
@@ -71,6 +65,7 @@ public class GameActionPlay {
* a {@link forge.card.spellability.SpellAbility} object.
*/
public final void playSpellAbilityWithoutPayingManaCost(final SpellAbility sa) {
FThreads.checkEDT("GameActionPlay.playSpellAbilityWithoutPayingManaCost", false);
final Card source = sa.getSourceCard();
setSplitCardState(source, sa); // Split card support
@@ -358,6 +353,7 @@ public class GameActionPlay {
* a {@link forge.card.spellability.SpellAbility} object.
*/
public final void playSpellAbility(SpellAbility sa, Player activator) {
FThreads.checkEDT("Player.playSpellAbility", false);
sa.setActivatingPlayer(activator);
final Card source = sa.getSourceCard();
@@ -385,6 +381,7 @@ public class GameActionPlay {
ability = ability.getSubAbility();
}
// System.out.println("Playing:" + sa.getDescription() + " of " + sa.getSourceCard() + " new = " + newAbility);
if (newAbility) {
final TargetSelection ts = new TargetSelection(sa.getTarget(), sa);
CostPayment payment = null;
@@ -403,14 +400,18 @@ public class GameActionPlay {
} else {
manaCost = this.getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost()));
}
if (!manaCost.isPaid()) {
FThreads.setInputAndWait(new InputPayManaSimple(game, sa, manaCost));
}
if (manaCost.isPaid()) {
if (sa.isSpell() && !source.isCopiedSpell()) {
sa.setSourceCard(game.getAction().moveToStack(source));
}
game.getStack().add(sa);
} else {
matchInput.setInput(new InputPayManaSimple(game, sa, manaCost));
}
}
}
@@ -446,60 +447,19 @@ public class GameActionPlay {
} else {
manaCost = this.getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost()));
}
if( !manaCost.isPaid() ) {
FThreads.setInputAndWait(new InputPayManaSimple(game, sa, getSpellCostChange(sa, new ManaCostBeingPaid(sa.getManaCost()))));
}
if (manaCost.isPaid()) {
AbilityUtils.resolve(sa, false);
return;
} else {
matchInput.setInput(new InputPayManaSimple(game, sa, true));
}
}
}
/** The Cost cutting_ get multi kicker mana cost paid. */
private int costCuttingGetMultiKickerManaCostPaid = 0;
/** The Cost cutting_ get multi kicker mana cost paid_ colored. */
private String costCuttingGetMultiKickerManaCostPaidColored = "";
/**
* Gets the cost cutting get multi kicker mana cost paid.
*
* @return the costCuttingGetMultiKickerManaCostPaid
*/
public int getCostCuttingGetMultiKickerManaCostPaid() {
return this.costCuttingGetMultiKickerManaCostPaid;
}
/**
* Sets the cost cutting get multi kicker mana cost paid.
*
* @param costCuttingGetMultiKickerManaCostPaid0
* the costCuttingGetMultiKickerManaCostPaid to set
*/
public void setCostCuttingGetMultiKickerManaCostPaid(final int costCuttingGetMultiKickerManaCostPaid0) {
this.costCuttingGetMultiKickerManaCostPaid = costCuttingGetMultiKickerManaCostPaid0;
}
/**
* Gets the cost cutting get multi kicker mana cost paid colored.
*
* @return the costCuttingGetMultiKickerManaCostPaidColored
*/
public String getCostCuttingGetMultiKickerManaCostPaidColored() {
return this.costCuttingGetMultiKickerManaCostPaidColored;
}
/**
* Sets the cost cutting get multi kicker mana cost paid colored.
*
* @param costCuttingGetMultiKickerManaCostPaidColored0
* the costCuttingGetMultiKickerManaCostPaidColored to set
*/
public void setCostCuttingGetMultiKickerManaCostPaidColored(
final String costCuttingGetMultiKickerManaCostPaidColored0) {
this.costCuttingGetMultiKickerManaCostPaidColored = costCuttingGetMultiKickerManaCostPaidColored0;
}
/**
* Gets the convokable colors.
*

View File

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

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.
* @param players2
* @param match0
* @param input
*/
public GameState(Iterable<LobbyPlayer> players2, GameType t, MatchController match0) { /* no more zones to map here */
type = t;

View File

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

View File

@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
@@ -44,6 +45,7 @@ import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellPermanent;
import forge.game.GameActionUtil;
import forge.game.GameState;
import forge.game.phase.PhaseType;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
@@ -762,5 +764,97 @@ public class AiController {
}
return null;
}
public void onPriorityRecieved() {
final PhaseType phase = game.getPhaseHandler().getPhase();
if (game.getStack().size() > 0) {
playSpellAbilities(game);
} else {
switch(phase) {
case CLEANUP:
if ( game.getPhaseHandler().getPlayerTurn() == player ) {
final int size = player.getCardsIn(ZoneType.Hand).size();
if (!player.isUnlimitedHandSize()) {
int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize());
final List<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 forge.Card;
import forge.control.input.Input;
import forge.Singletons;
import forge.control.input.InputBase;
import forge.game.GameState;
import forge.game.phase.CombatUtil;
import forge.game.player.Player;
@@ -12,7 +13,7 @@ import forge.game.player.Player;
* TODO: Write javadoc for this type.
*
*/
public class AiInputBlock extends Input {
public class AiInputBlock extends InputBase {
private final GameState game;
/**
@@ -38,6 +39,7 @@ public class AiInputBlock extends Input {
CombatUtil.orderMultipleCombatants(game.getCombat());
game.getPhaseHandler().setPlayersPriorityPermission(false);
stop();
// was not added to stack, so will be replaced by plain update
Singletons.getModel().getMatch().getInput().updateObservers();
}
}

View File

@@ -17,18 +17,8 @@
*/
package forge.game.ai;
import java.util.List;
import com.esotericsoftware.minlog.Log;
import forge.Card;
import forge.card.spellability.SpellAbility;
import forge.control.input.Input;
import forge.game.GameState;
import forge.game.phase.PhaseType;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.FThreads;
import forge.control.input.InputBase;
/**
* <p>
@@ -38,13 +28,11 @@ import forge.game.zone.ZoneType;
* @author Forge
* @version $Id$
*/
public class AiInputCommon extends Input {
public class AiInputCommon extends InputBase {
/** Constant <code>serialVersionUID=-3091338639571662216L</code>. */
private static final long serialVersionUID = -3091338639571662216L;
private final AiController computer;
private final AIPlayer player;
private final GameState game;
/**
* <p>
@@ -56,15 +44,13 @@ public class AiInputCommon extends Input {
*/
public AiInputCommon(final AiController iComputer) {
this.computer = iComputer;
player = computer.getPlayer();
this.game = computer.getGame();
}
/** {@inheritDoc} */
@Override
public final void showMessage() {
// should not think when the game is over
if (game.isGameOver()) {
if (computer.getGame().isGameOver()) {
return;
}
@@ -76,104 +62,15 @@ public class AiInputCommon extends Input {
* \"Detailed Error Trace\" to the Forge forum.");
*/
final PhaseType phase = game.getPhaseHandler().getPhase();
FThreads.invokeInNewThread(aiActions, true);
if (game.getStack().size() > 0) {
playSpellAbilities(game);
} else {
switch(phase) {
case CLEANUP:
if ( game.getPhaseHandler().getPlayerTurn() == player ) {
final int size = player.getCardsIn(ZoneType.Hand).size();
if (!player.isUnlimitedHandSize()) {
int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize());
final List<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();
/**
* 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 Runnable aiActions = new Runnable() {
final List<Card> att = game.getCombat().getAttackers();
if (!att.isEmpty()) {
game.getPhaseHandler().setCombat(true);
@Override
public void run() {
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());
if (c.getController().isHuman()) {
GameActionUtil.payCostDuringAbilityResolve(c.getController(), ability, attackCost, paidCommand, unpaidCommand, null, game);
if ( GameActionUtil.payCostDuringAbilityResolve(c.getController(), ability, attackCost, null, game) ) {
if (!crd.hasKeyword("Vigilance")) { crd.tap(); }
} else {
game.getCombat().removeFromCombat(crd);
}
} else { // computer
if (ComputerUtilCost.canPayCost(ability, c.getController())) {
ComputerUtil.playNoStack((AIPlayer)c.getController(), ability, game);
@@ -1194,10 +1168,9 @@ public class CombatUtil {
// during Declare_Attackers
game.getCombat().removeFromCombat(crd);
}
if (bLast) {
PhaseUtil.handleAttackingTriggers();
}
}
if (bLast)
PhaseUtil.handleAttackingTriggers();
}
}

View File

@@ -24,6 +24,7 @@ import java.util.Stack;
import com.esotericsoftware.minlog.Log;
import forge.Card;
import forge.FThreads;
import forge.Singletons;
import forge.card.trigger.TriggerType;
import forge.game.GameState;
@@ -734,15 +735,25 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
nextPhase();
return;
} else if (!game.getStack().hasSimultaneousStackEntries()) {
game.getStack().resolveStack();
Runnable proc = new Runnable(){
@Override public void run() {
game.getStack().resolveStack();
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
}
};
if ( FThreads.isEDT() )
FThreads.invokeInNewThread(proc, true);
else
proc.run();
}
} else {
// pass the priority to other player
this.pPlayerPriority = nextPlayer;
Singletons.getModel().getMatch().getInput().resetInput();
Singletons.getModel().getMatch().getInput().updateObservers();
}
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
}
/**
@@ -817,4 +828,9 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
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.GameEntity;
import forge.Singletons;
import forge.control.input.Input;
import forge.control.input.InputBase;
import forge.game.GameState;
import forge.game.ai.ComputerUtilCard;
import forge.game.player.Player;
@@ -210,7 +210,7 @@ public class Untap extends Phase {
landList.get(0).untap();
}
} else {
final Input target = new Input() {
final InputBase target = new InputBase() {
private static final long serialVersionUID = 6653677835629939465L;
@Override
@@ -250,7 +250,7 @@ public class Untap extends Phase {
ComputerUtilCard.getBestArtifactAI(artList).untap();
}
} else {
final Input target = new Input() {
final InputBase target = new InputBase() {
private static final long serialVersionUID = 5555427219659889707L;
@Override
@@ -290,7 +290,7 @@ public class Untap extends Phase {
creatures.get(0).untap();
}
} else {
final Input target = new Input() {
final InputBase target = new InputBase() {
private static final long serialVersionUID = 5555427219659889707L;
@Override

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,21 +22,18 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Stack;
import com.esotericsoftware.minlog.Log;
import forge.Card;
import forge.CardLists;
import forge.CardPredicates;
import forge.FThreads;
import forge.CardPredicates.Presets;
import forge.Command;
import forge.Singletons;
import forge.card.ability.AbilityUtils;
import forge.card.cardfactory.CardFactory;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostBeingPaid;
import forge.card.mana.ManaCostParser;
import forge.card.spellability.Ability;
import forge.card.spellability.AbilityStatic;
import forge.card.spellability.AbilityTriggered;
@@ -48,7 +45,7 @@ import forge.card.spellability.TargetChoices;
import forge.card.spellability.TargetSelection;
import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerType;
import forge.control.input.Input;
import forge.control.input.InputBase;
import forge.control.input.InputPayManaExecuteCommands;
import forge.game.GameActionUtil;
import forge.game.GameState;
@@ -295,95 +292,6 @@ public class MagicStack extends MyObservable {
}
}
/**
* <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>
@@ -394,6 +302,7 @@ public class MagicStack extends MyObservable {
* a {@link forge.card.spellability.SpellAbility} object.
*/
public final void add(final SpellAbility sp) {
FThreads.checkEDT("MagicStack.add", false);
final ArrayList<TargetChoices> chosenTargets = sp.getAllTargetChoices();
if (sp.isManaAbility()) { // Mana Abilities go straight through
@@ -478,48 +387,36 @@ public class MagicStack extends MyObservable {
} else if (sp.isXCost()) {
// TODO: convert any X costs to use abCost so it happens earlier
final SpellAbility sa = sp;
final ManaCost mc = new ManaCost( new ManaCostParser(Integer.toString(sa.getXManaCost())));
final Ability ability = new Ability(sp.getSourceCard(), mc) {
@Override
public void resolve() {
final Card crd = this.getSourceCard();
crd.addXManaCostPaid(1);
}
};
final Command unpaidCommand = new Command() {
private static final long serialVersionUID = -3342222770086269767L;
@Override
public void execute() {
MagicStack.this.push(sa);
}
};
final Command paidCommand = new Command() {
private static final long serialVersionUID = -2224875229611007788L;
@Override
public void execute() {
ability.resolve();
final Card crd = sa.getSourceCard();
Singletons.getModel().getMatch().getInput().setInput(
new InputPayManaExecuteCommands(game, "Pay X cost for " + crd.getName() + " (X="
+ crd.getXManaCostPaid() + ")\r\n", ability.getManaCost().toString(), this, unpaidCommand,
true));
}
};
final Card crd = sa.getSourceCard();
final int xCost = sa.getXManaCost();
Player player = sp.getSourceCard().getController();
if (player.isHuman()) {
Singletons.getModel().getMatch().getInput().setInput(
new InputPayManaExecuteCommands(game, "Pay X cost for " + sp.getSourceCard().getName() + " (X="
+ crd.getXManaCostPaid() + ")\r\n", ability.getManaCost().toString(), paidCommand,
unpaidCommand, true));
final Runnable payNextX = new Runnable() {
@Override
public void run() {
final Card crd = sa.getSourceCard();
String message = "Pay X cost for " + crd.getName() + " (X=" + crd.getXManaCostPaid() + ")\r\n";
InputPayManaExecuteCommands inp = new InputPayManaExecuteCommands(game, message, ManaCost.get(xCost), true);
FThreads.setInputAndWait(inp);
if ( inp.isPaid() ) {
crd.addXManaCostPaid(1);
this.run();
} else
MagicStack.this.push(sa);
}
};
payNextX.run();
} else {
// computer
final int neededDamage = CardFactoryUtil.getNeededXDamage(sa);
final Ability ability = new Ability(sp.getSourceCard(), ManaCost.get(xCost)) {
@Override
public void resolve() {
final Card crd = this.getSourceCard();
crd.addXManaCostPaid(1);
}
};
while (ComputerUtilCost.canPayCost(ability, player) && (neededDamage != sa.getSourceCard().getXManaCostPaid())) {
ComputerUtil.playNoStack((AIPlayer)player, ability, game);
@@ -532,59 +429,38 @@ public class MagicStack extends MyObservable {
// both X and multi is not supported yet
final SpellAbility sa = sp;
final Ability ability = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) {
final Ability abilityIncreaseMultikicker = new Ability(sp.getSourceCard(), sp.getMultiKickerManaCost()) {
@Override
public void resolve() {
this.getSourceCard().addMultiKickerMagnitude(1);
}
};
final Command unpaidCommand = new Command() {
private static final long serialVersionUID = -3342222770086269767L;
@Override
public void execute() {
MagicStack.this.push(sa);
}
};
final Command paidCommand = new Command() {
private static final long serialVersionUID = -6037161763374971106L;
@Override
public void execute() {
ability.resolve();
final ManaCostBeingPaid manaCost = MagicStack.this.getMultiKickerSpellCostChange(ability);
if (manaCost.isPaid()) {
this.execute();
} else {
String prompt;
int mkCostPaid = game.getActionPlay().getCostCuttingGetMultiKickerManaCostPaid();
String mkCostPaidColored = game.getActionPlay().getCostCuttingGetMultiKickerManaCostPaidColored();
int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude();
if ((mkCostPaid == 0) && mkCostPaidColored.equals("")) {
prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", sa.getSourceCard(), mkMagnitude );
} else {
prompt = String.format("Multikicker for %s\r\nMana in Reserve: %s %s\r\nTimes Kicked: %d", sa.getSourceCard(),
(mkCostPaid != 0) ? Integer.toString(mkCostPaid) : "", mkCostPaidColored, mkMagnitude);
}
Input toSet = new InputPayManaExecuteCommands(game, prompt, manaCost.toString(), this, unpaidCommand);
Singletons.getModel().getMatch().getInput().setInput(toSet);
}
}
};
Player activating = sp.getActivatingPlayer();
if (activating.isHuman()) {
sa.getSourceCard().addMultiKickerMagnitude(-1);
paidCommand.execute();
final Runnable paidCommand = new Runnable() {
@Override
public void run() {
abilityIncreaseMultikicker.resolve();
int mkMagnitude = sa.getSourceCard().getMultiKickerMagnitude();
String prompt = String.format("Multikicker for %s\r\nTimes Kicked: %d\r\n", sa.getSourceCard(), mkMagnitude );
InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(game, prompt, sp.getMultiKickerManaCost());
FThreads.setInputAndWait(toSet);
if ( toSet.isPaid() ) {
this.run();
} else
MagicStack.this.push(sa);
}
};
paidCommand.run();
} else {
// computer
while (ComputerUtilCost.canPayCost(ability, activating)) {
ComputerUtil.playNoStack((AIPlayer)activating, ability, game);
while (ComputerUtilCost.canPayCost(abilityIncreaseMultikicker, activating)) {
ComputerUtil.playNoStack((AIPlayer)activating, abilityIncreaseMultikicker, game);
}
this.push(sa);
@@ -603,38 +479,27 @@ public class MagicStack extends MyObservable {
}
};
final Command unpaidCommand = new Command() {
private static final long serialVersionUID = -3180458633098297855L;
@Override
public void execute() {
for (int i = 0; i < sp.getSourceCard().getReplicateMagnitude(); i++) {
CardFactory.copySpellontoStack(sp.getSourceCard(), sp.getSourceCard(), sp, false);
}
}
};
final Command paidCommand = new Command() {
private static final long serialVersionUID = 132624005072267304L;
@Override
public void execute() {
ability.resolve();
final ManaCostBeingPaid manaCost = MagicStack.this.getReplicateSpellCostChange(ability);
if (manaCost.isPaid()) {
this.execute();
} else {
String prompt = String.format("Replicate for %s\r\nTimes Replicated: %d\r\n", sa.getSourceCard(), sa.getSourceCard().getReplicateMagnitude());
Input toSet = new InputPayManaExecuteCommands(game, prompt, manaCost.toString(), this, unpaidCommand);
Singletons.getModel().getMatch().getInput().setInput(toSet);
}
}
};
Player controller = sp.getSourceCard().getController();
if (controller.isHuman()) {
sa.getSourceCard().addReplicateMagnitude(-1);
paidCommand.execute();
final Runnable addMagnitude = new Runnable() {
@Override
public void run() {
ability.resolve();
String prompt = String.format("Replicate for %s\r\nTimes Replicated: %d\r\n", sa.getSourceCard(), sa.getSourceCard().getReplicateMagnitude());
InputPayManaExecuteCommands toSet = new InputPayManaExecuteCommands(game, prompt, sp.getReplicateManaCost());
FThreads.setInputAndWait(toSet);
if ( toSet.isPaid() ) {
this.run();
} else {
for (int i = 0; i < sp.getSourceCard().getReplicateMagnitude(); i++) {
CardFactory.copySpellontoStack(sp.getSourceCard(), sp.getSourceCard(), sp, false);
}
}
}
};
addMagnitude.run();
} else {
// computer
while (ComputerUtilCost.canPayCost(ability, controller)) {
@@ -829,17 +694,18 @@ public class MagicStack extends MyObservable {
final Card source = sa.getSourceCard();
curResolvingCard = source;
if (this.hasFizzled(sa, source, false)) { // Fizzle
boolean thisHasFizzled = this.hasFizzled(sa, source, false);
String messageForLog = thisHasFizzled ? source.getName() + " ability fizzles." : sa.getStackDescription();
game.getGameLog().add("ResolveStack", messageForLog, 2);
if (thisHasFizzled) { // Fizzle
// TODO: Spell fizzles, what's the best way to alert player?
Log.debug(source.getName() + " ability fizzles.");
game.getGameLog().add("ResolveStack", source.getName() + " ability fizzles.", 2);
this.finishResolving(sa, true);
} else if (sa.getApi() != null) {
game.getGameLog().add("ResolveStack", sa.getStackDescription(), 2);
AbilityUtils.handleRemembering(sa);
AbilityUtils.resolve(sa, true);
} else {
game.getGameLog().add("ResolveStack", sa.getStackDescription(), 2);
sa.resolve();
this.finishResolving(sa, false);
// do creatures ETB from here?
@@ -867,7 +733,7 @@ public class MagicStack extends MyObservable {
if (creats.size() != 0) {
haunterDiesWork.setDescription("");
final Input target = new Input() {
final InputBase target = new InputBase() {
private static final long serialVersionUID = 1981791992623774490L;
@Override
@@ -1290,26 +1156,18 @@ public class MagicStack extends MyObservable {
ComputerUtil.playStack(sa, (AIPlayer) activePlayer, game);
}
} else {
// If only one, just add as necessary
if (activePlayerSAs.size() == 1) {
SpellAbility next = activePlayerSAs.get(0);
List<SpellAbility> orderedSAs = activePlayerSAs;
if (activePlayerSAs.size() > 1) { // give a dual list form to create instead of needing to do it one at a time
orderedSAs = GuiChoose.order("Select order for Simultaneous Spell Abilities", "Resolve first", 0, activePlayerSAs, null, null);
}
int size = orderedSAs.size();
for (int i = size - 1; i >= 0; i--) {
SpellAbility next = orderedSAs.get(i);
if (next.isTrigger()) {
game.getActionPlay().playSpellAbility(next, activePlayer);
} else {
this.add(next);
}
} else {
// Otherwise, gave a dual list form to create instead of needing to do it one at a time
List<SpellAbility> orderedSAs = GuiChoose.order("Select order for Simultaneous Spell Abilities", "Resolve first", 0, activePlayerSAs, null, null);
int size = orderedSAs.size();
for (int i = size - 1; i >= 0; i--) {
SpellAbility next = orderedSAs.get(i);
if (next.isTrigger()) {
game.getActionPlay().playSpellAbility(next, activePlayer);
} else {
this.add(next);
}
}
}
}

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) {
if (null == choices || 0 == choices.size()) {
if (null == choices || choices.isEmpty()) {
if (0 == min) {
return new ArrayList<T>();
} else {

View File

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

View File

@@ -21,10 +21,12 @@ import java.util.Observable;
import java.util.Observer;
import forge.Card;
import forge.Singletons;
import forge.FThreads;
import forge.control.input.Input;
import forge.game.GameState;
import forge.game.MatchController;
import forge.game.phase.PhaseHandler;
import forge.game.player.Player;
import forge.view.ButtonUtil;
/**
* <p>
@@ -38,36 +40,48 @@ public class InputProxy implements Observer {
/** The input. */
private Input input;
private boolean valid = false;
private MatchController match = null;
public void setMatch(MatchController matchController) {
match = matchController;
}
@Override
public final synchronized void update(final Observable observable, final Object obj) {
ButtonUtil.disableAll();
valid = false;
this.input = null;
final GameState game = match.getCurrentGame();
final PhaseHandler ph = game.getPhaseHandler();
Singletons.getModel().getMatch().getInput().setNewInput(Singletons.getModel().getGame());
}
/**
* <p>
* 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
}
//System.out.print((FThreads.isEDT() ? "EDT > " : "TRD > ") + ph.debugPrintState());
if ( match.getInput().isEmpty() && ph.hasPhaseEffects()) {
//System.out.println(" handle begin phase");
FThreads.invokeInNewThread(new Runnable() {
@Override public void run() {
ph.handleBeginPhase();
update(observable, obj);
}
}, true);
return;
}
final Input nextInput = match.getInput().getActualInput(game);
//System.out.printf(" input is %s \t stack = %s%n", nextInput == null ? "null" : nextInput.getClass().getSimpleName(), match.getInput().printInputStack());
if (nextInput != null) {
this.input = nextInput;
FThreads.invokeInEDT(new Runnable() { @Override public void run() { nextInput.showMessage(); } });
} else if (!ph.isPlayerPriorityAllowed()) {
ph.getPriorityPlayer().getController().passPriority();
}
}
/**
* <p>
* selectButtonOK.
* </p>
*/
public final void selectButtonOK() {
this.getInput().selectButtonOK();
if ( null == input ) return;
input.selectButtonOK();
}
/**
@@ -76,7 +90,8 @@ public class InputProxy implements Observer {
* </p>
*/
public final void selectButtonCancel() {
this.getInput().selectButtonCancel();
if ( null == input ) return;
input.selectButtonCancel();
}
/**
@@ -88,7 +103,8 @@ public class InputProxy implements Observer {
* a {@link forge.game.player.Player} object.
*/
public final void selectPlayer(final Player player) {
this.getInput().selectPlayer(player);
if ( null == input ) return;
input.selectPlayer(player);
}
/**
@@ -102,22 +118,19 @@ public class InputProxy implements Observer {
* a {@link forge.game.zone.PlayerZone} object.
*/
public final void selectCard(final Card card) {
this.getInput().selectCard(card);
if ( null == input ) return;
input.selectCard(card);
}
/** {@inheritDoc} */
@Override
public final String toString() {
return this.getInput().toString();
if ( null == input ) return "(null)";
return this.input.toString();
}
/** @return {@link forge.gui.InputProxy.Input} */
/** @return {@link forge.gui.InputProxy.InputBase} */
public Input getInput() {
return this.input;
}
public synchronized boolean isValid() {
return valid;
}
}

View File

@@ -23,8 +23,8 @@ import java.util.List;
import java.util.Map;
import javax.swing.ImageIcon;
import forge.Card;
import forge.FThreads;
import forge.GameEntity;
import forge.ImageCache;
import forge.Singletons;
@@ -181,7 +181,8 @@ public enum CMatchUI {
* @param blockers &emsp; {@link forge.CardList}
* @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) {
return new HashMap<Card, Integer>();
}
@@ -194,8 +195,15 @@ public enum CMatchUI {
return res;
}
VAssignDamage v = new VAssignDamage(attacker, blockers, damage, defender);
return v.getDamageMap();
final Object[] result = { null }; // how else can I extract a value from EDT thread?
FThreads.invokeInEDTAndWait(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
VAssignDamage v = new VAssignDamage(attacker, blockers, damage, defender);
result[0] = v.getDamageMap();
}});
return (Map<Card, Integer>)result[0];
}
/**

View File

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

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

View File

@@ -117,11 +117,12 @@ public enum FModel {
}
// initialize log file
final File logFile = new File(NewConstants.LOG_FILE);
final boolean deleteSucceeded = logFile.delete();
File logFile = new File(NewConstants.LOG_FILE);
if (logFile.exists() && !deleteSucceeded && (logFile.length() != 0)) {
throw new IllegalStateException("Could not delete existing logFile:" + logFile.getAbsolutePath());
int i = 0;
while (logFile.exists() && !logFile.delete()) {
String pathname = logFile.getPath().replaceAll("[0-9]{0,2}.log$", String.valueOf(i++) + ".log");
logFile = new File(pathname);
}
try {
@@ -400,6 +401,7 @@ public enum FModel {
/**
* TODO: Write javadoc for this method.
* @param players
* @param input
*/
public GameState newGame(Iterable<LobbyPlayer> players, GameType type, final MatchController 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.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JScrollPane;
import forge.Card;
import forge.FThreads;
import forge.view.arcane.util.Animation;
import forge.view.arcane.util.CardPanelMouseListener;
@@ -497,7 +499,27 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen
* @param newList
* an array of {@link forge.Card} objects.
*/
private final AtomicBoolean wantRedraw = new AtomicBoolean(false);
public void setupPlayZone() {
boolean wasSet = wantRedraw.getAndSet(true);
if(wasSet) return;
FThreads.invokeInEDT(new Runnable() {
@Override
public void run() {
try { // user won't notice, but the requests coming in that interval won't trigger re-draw
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
wantRedraw.set(false);
final List<Card> modelshot = new ArrayList<Card>(model); // I am afraid of ConcurrentModificationExceptions
setupPlayZone(modelshot);
}
});
}
public void setupPlayZone(final List<Card> model) {
List<Card> oldCards, toDelete;
oldCards = new ArrayList<Card>();
for (final CardPanel cpa : getCardPanels()) {