Get mobile game working again by implementing IGuiBase interface

This commit is contained in:
drdev
2014-04-10 23:49:18 +00:00
parent 22d1d5b1f0
commit 60574377d6
44 changed files with 406 additions and 3528 deletions

32
.gitattributes vendored
View File

@@ -1064,6 +1064,7 @@ forge-gui-mobile/libs/gdx-sources.jar -text
forge-gui-mobile/libs/gdx.jar -text
forge-gui-mobile/pom.xml -text
forge-gui-mobile/src/forge/Forge.java -text
forge-gui-mobile/src/forge/GuiMobile.java -text
forge-gui-mobile/src/forge/assets/CardFaceSymbols.java -text
forge-gui-mobile/src/forge/assets/FImage.java -text
forge-gui-mobile/src/forge/assets/FSkin.java -text
@@ -1129,37 +1130,8 @@ forge-gui-mobile/src/forge/screens/draft/DraftScreen.java -text
forge-gui-mobile/src/forge/screens/guantlet/GuantletScreen.java -text
forge-gui-mobile/src/forge/screens/home/HomeScreen.java -text
forge-gui-mobile/src/forge/screens/match/FControl.java -text
forge-gui-mobile/src/forge/screens/match/FControlGameEventHandler.java -text
forge-gui-mobile/src/forge/screens/match/FControlGamePlayback.java -text
forge-gui-mobile/src/forge/screens/match/InputSelectCard.java -text
forge-gui-mobile/src/forge/screens/match/MatchScreen.java -text
forge-gui-mobile/src/forge/screens/match/events/IUiEventVisitor.java -text
forge-gui-mobile/src/forge/screens/match/events/UiEvent.java -text
forge-gui-mobile/src/forge/screens/match/events/UiEventAttackerDeclared.java -text
forge-gui-mobile/src/forge/screens/match/events/UiEventBlockerAssigned.java -text
forge-gui-mobile/src/forge/screens/match/input/ButtonUtil.java -text
forge-gui-mobile/src/forge/screens/match/input/Input.java -text
forge-gui-mobile/src/forge/screens/match/input/InputAttack.java -text
forge-gui-mobile/src/forge/screens/match/input/InputBase.java -text
forge-gui-mobile/src/forge/screens/match/input/InputBlock.java -text
forge-gui-mobile/src/forge/screens/match/input/InputConfirm.java -text
forge-gui-mobile/src/forge/screens/match/input/InputConfirmMulligan.java -text
forge-gui-mobile/src/forge/screens/match/input/InputLockUI.java -text
forge-gui-mobile/src/forge/screens/match/input/InputPassPriority.java -text
forge-gui-mobile/src/forge/screens/match/input/InputPayMana.java -text
forge-gui-mobile/src/forge/screens/match/input/InputPayManaOfCostPayment.java -text
forge-gui-mobile/src/forge/screens/match/input/InputPayManaSimple.java -text
forge-gui-mobile/src/forge/screens/match/input/InputPayManaX.java -text
forge-gui-mobile/src/forge/screens/match/input/InputPlaybackControl.java -text
forge-gui-mobile/src/forge/screens/match/input/InputProliferate.java -text
forge-gui-mobile/src/forge/screens/match/input/InputProxy.java -text
forge-gui-mobile/src/forge/screens/match/input/InputQueue.java -text
forge-gui-mobile/src/forge/screens/match/input/InputSelectCardsForConvoke.java -text
forge-gui-mobile/src/forge/screens/match/input/InputSelectCardsFromList.java -text
forge-gui-mobile/src/forge/screens/match/input/InputSelectEntitiesFromList.java -text
forge-gui-mobile/src/forge/screens/match/input/InputSelectManyBase.java -text
forge-gui-mobile/src/forge/screens/match/input/InputSelectTargets.java -text
forge-gui-mobile/src/forge/screens/match/input/InputSynchronized.java -text
forge-gui-mobile/src/forge/screens/match/input/InputSyncronizedBase.java -text
forge-gui-mobile/src/forge/screens/match/views/VAssignDamage.java -text
forge-gui-mobile/src/forge/screens/match/views/VAvatar.java -text
forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java -text

View File

@@ -20,13 +20,6 @@
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<linkedResources>
<link>
<name>assets</name>
<type>2</type>
<locationURI>copy_PARENT/forge-gui/res</locationURI>
</link>
</linkedResources>
<variableList>
<variable>
<name>copy_PARENT</name>

View File

@@ -52,6 +52,7 @@ public class Forge implements ApplicationListener {
}
game = this;
clipboard = clipboard0;
GuiBase.setInterface(new GuiMobile());
}
@Override

View File

@@ -0,0 +1,311 @@
package forge;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.google.common.base.Function;
import forge.assets.FSkin;
import forge.assets.FSkinProp;
import forge.assets.FTextureImage;
import forge.assets.ISkinImage;
import forge.deck.Deck;
import forge.deck.FDeckViewer;
import forge.error.BugReporter;
import forge.events.UiEvent;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameType;
import forge.game.card.Card;
import forge.game.combat.Combat;
import forge.game.event.GameEventTurnBegan;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.LobbyPlayer;
import forge.game.player.Player;
import forge.game.player.RegisteredPlayer;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.interfaces.IGuiBase;
import forge.item.PaperCard;
import forge.match.input.InputQueue;
import forge.screens.match.FControl;
import forge.screens.match.views.VPhaseIndicator.PhaseLabel;
import forge.screens.match.winlose.ViewWinLose;
import forge.toolbox.FOptionPane;
import forge.toolbox.GuiChoose;
import forge.util.ThreadUtil;
public class GuiMobile implements IGuiBase {
@Override
public void invokeInEdtLater(Runnable proc) {
Gdx.app.postRunnable(proc);
}
@Override
public void invokeInEdtAndWait(final Runnable proc) {
if (isGuiThread()) {
// Just run in the current thread.
proc.run();
}
else {
//TODO
}
}
@Override
public boolean isGuiThread() {
return !ThreadUtil.isGameThread();
}
@Override
public String getInstallRoot() {
switch (Gdx.app.getType()) {
case Desktop:
return "../forge-gui/";
case Android:
break;
case Applet:
break;
case WebGL:
break;
case iOS:
break;
default:
break;
}
return "";
}
@Override
public String getAssetsDir() {
return "res/";
}
@Override
public boolean mayShowCard(Card card) {
return FControl.mayShowCard(card);
}
@Override
public void reportBug(String details) {
BugReporter.reportBug(details);
}
@Override
public void reportException(Throwable ex) {
BugReporter.reportException(ex);
}
@Override
public void reportException(Throwable ex, String message) {
BugReporter.reportException(ex, message);
}
@Override
public ISkinImage getUnskinnedIcon(String path) {
return new FTextureImage(new Texture(path));
}
@Override
public int showOptionDialog(String message, String title, FSkinProp icon, String[] options, int defaultOption) {
return FOptionPane.showOptionDialog(message, title, icon == null ? null : FSkin.getImages().get(icon), options, defaultOption);
}
@Override
public <T> T showInputDialog(String message, String title, FSkinProp icon, T initialInput, T[] inputOptions) {
return FOptionPane.showInputDialog(message, title, icon == null ? null : FSkin.getImages().get(icon), initialInput, inputOptions);
}
@Override
public <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices, final T selected, final Function<T, String> display) {
return GuiChoose.getChoices(message, min, max, choices, selected, display);
}
@Override
public <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax,
final List<T> sourceChoices, final List<T> destChoices, final Card referenceCard, final boolean sideboardingMode) {
return GuiChoose.order(title, top, remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices, referenceCard, sideboardingMode);
}
@Override
public void showCardList(final String title, final String message, final List<PaperCard> list) {
Deck deck = new Deck(title + " - " + message);
deck.getMain().addAllFlat(list);
FDeckViewer.show(deck);
}
@Override
public IButton getBtnOK() {
return FControl.getView().getPrompt().getBtnOk();
}
@Override
public IButton getBtnCancel() {
return FControl.getView().getPrompt().getBtnCancel();
}
@Override
public void focusButton(final IButton button) {
//not needed for mobile game
}
@Override
public void flashIncorrectAction() {
//SDisplayUtil.remind(VPrompt.SINGLETON_INSTANCE); //TODO
}
@Override
public void updatePhase() {
PhaseHandler pH = FControl.getGame().getPhaseHandler();
Player p = pH.getPlayerTurn();
PhaseType ph = pH.getPhase();
PhaseLabel lbl = FControl.getPlayerPanel(p).getPhaseIndicator().getLabel(ph);
FControl.resetAllPhaseButtons();
if (lbl != null) {
lbl.setActive(true);
}
}
@Override
public void updateTurn(final GameEventTurnBegan event, final Game game) {
//VField nextField = FControl.getFieldViewFor(event.turnOwner);
//SDisplayUtil.showTab(nextField);
FControl.getView().getPrompt().updateText(game);
}
@Override
public void updatePlayerControl() {
//TODO
}
@Override
public void finishGame() {
new ViewWinLose(FControl.getGame()).setVisible(true);
}
@Override
public void updateStack() {
FControl.getView().getStack().update();
}
@Override
public void startMatch(GameType gameType, List<RegisteredPlayer> players) {
FControl.startMatch(gameType, players);
}
@Override
public void setPanelSelection(Card c) {
//GuiUtils.setPanelSelection(c); //TODO
}
@Override
public SpellAbility getAbilityToPlay(List<SpellAbility> abilities, Object triggerEvent) {
if (abilities.isEmpty()) {
return null;
}
if (abilities.size() == 1) {
return abilities.get(0);
}
return GuiChoose.oneOrNone("Choose ability to play", abilities);
}
@Override
public void hear(LobbyPlayer player, String message) {
//FNetOverlay.SINGLETON_INSTANCE.addMessage(player.getName(), message); //TODO
}
@Override
public int getAvatarCount() {
if (FSkin.isLoaded()) {
return FSkin.getAvatars().size();
}
return 0;
}
@Override
public void fireEvent(UiEvent e) {
FControl.fireEvent(e);
}
@Override
public void setCard(Card card) {
FControl.setCard(card);
}
@Override
public void showCombat(Combat combat) {
FControl.showCombat(combat);
}
@Override
public void setUsedToPay(Card card, boolean b) {
FControl.setUsedToPay(card, b);
}
@Override
public void setHighlighted(Player player, boolean b) {
FControl.setHighlighted(player, b);
}
@Override
public void showPromptMessage(String message) {
FControl.showMessage(message);
}
@Override
public boolean stopAtPhase(Player playerTurn, PhaseType phase) {
return FControl.stopAtPhase(playerTurn, phase);
}
@Override
public InputQueue getInputQueue() {
return FControl.getInputQueue();
}
@Override
public Game getGame() {
return FControl.getGame();
}
@Override
public void updateZones(List<Pair<Player, ZoneType>> zonesToUpdate) {
FControl.updateZones(zonesToUpdate);
}
@Override
public void updateCards(Set<Card> cardsToUpdate) {
FControl.updateCards(cardsToUpdate);
}
@Override
public void updateManaPool(List<Player> manaPoolUpdate) {
FControl.updateManaPool(manaPoolUpdate);
}
@Override
public void updateLives(List<Player> livesUpdate) {
FControl.updateLives(livesUpdate);
}
@Override
public void endCurrentGame() {
FControl.endCurrentGame();
}
@Override
public Map<Card, Integer> getDamageToAssign(Card attacker, List<Card> blockers,
int damageDealt, GameEntity defender, boolean overrideOrder) {
return FControl.getDamageToAssign(attacker, blockers,
damageDealt, defender, overrideOrder);
}
}

View File

@@ -2,7 +2,7 @@ package forge.assets;
import forge.Forge.Graphics;
public interface FImage {
public interface FImage extends ISkinImage {
float getWidth();
float getHeight();
void draw(Graphics g, float x, float y, float w, float h);

View File

@@ -7,7 +7,6 @@ import java.util.Map;
import org.apache.commons.lang3.text.WordUtils;
import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
@@ -17,17 +16,13 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
import forge.assets.FSkinImage.SourceFile;
import forge.model.FModel;
import forge.properties.ForgeConstants;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.screens.SplashScreen;
import forge.toolbox.FProgressBar;
public class FSkin {
private static final String
FILE_SKINS_DIR = "skins/",
FILE_AVATAR_SPRITE = "sprite_avatars.png",
DEFAULT_DIR = FILE_SKINS_DIR + "default/";
private static final Map<FSkinProp, FSkinImage> images = new HashMap<FSkinProp, FSkinImage>();
private static final Map<Integer, TextureRegion> avatars = new HashMap<Integer, TextureRegion>();
@@ -72,12 +67,12 @@ public class FSkin {
// Non-default (preferred) skin name and dir.
preferredName = skinName.toLowerCase().replace(' ', '_');
preferredDir = FILE_SKINS_DIR + preferredName + "/";
preferredDir = ForgeConstants.SKINS_DIR + preferredName + "/";
FSkinTexture.BG_TEXTURE.load(preferredDir, DEFAULT_DIR); //load background texture early for splash screen
FSkinTexture.BG_TEXTURE.load(preferredDir, ForgeConstants.DEFAULT_SKINS_DIR); //load background texture early for splash screen
if (splashScreen != null) {
final FileHandle f = Gdx.files.internal(preferredDir + "bg_splash.png");
final FileHandle f = Gdx.files.local(preferredDir + "bg_splash.png");
if (!f.exists()) {
if (!skinName.equals("default")) {
FSkin.loadLight("default", splashScreen);
@@ -138,12 +133,13 @@ public class FSkin {
//FView.SINGLETON_INSTANCE.setSplashProgessBarMessage("Processing image sprites: ", 5);
// Grab and test various sprite files.
final FileHandle f1 = Gdx.files.internal(DEFAULT_DIR + SourceFile.ICONS.getFilename());
final FileHandle f2 = Gdx.files.internal(preferredDir + SourceFile.ICONS.getFilename());
final FileHandle f3 = Gdx.files.internal(DEFAULT_DIR + SourceFile.FOILS.getFilename());
final FileHandle f4 = Gdx.files.internal(DEFAULT_DIR + FILE_AVATAR_SPRITE);
final FileHandle f5 = Gdx.files.internal(preferredDir + FILE_AVATAR_SPRITE);
final FileHandle f6 = Gdx.files.internal(DEFAULT_DIR + SourceFile.OLD_FOILS.getFilename());
String defaultDir = ForgeConstants.DEFAULT_SKINS_DIR;
final FileHandle f1 = Gdx.files.local(defaultDir + SourceFile.ICONS.getFilename());
final FileHandle f2 = Gdx.files.local(preferredDir + SourceFile.ICONS.getFilename());
final FileHandle f3 = Gdx.files.local(defaultDir + SourceFile.FOILS.getFilename());
final FileHandle f4 = Gdx.files.local(defaultDir + ForgeConstants.SPRITE_AVATARS_FILE);
final FileHandle f5 = Gdx.files.local(preferredDir + ForgeConstants.SPRITE_AVATARS_FILE);
final FileHandle f6 = Gdx.files.local(defaultDir + SourceFile.OLD_FOILS.getFilename());
try {
textures.put(f1.path(), new Texture(f1));
@@ -168,9 +164,9 @@ public class FSkin {
//load images
for (FSkinImage image : FSkinImage.values()) {
image.load(preferredDir, DEFAULT_DIR, textures, preferredIcons);
image.load(preferredDir, ForgeConstants.DEFAULT_SKINS_DIR, textures, preferredIcons);
}
FSkinTexture.BG_MATCH.load(preferredDir, DEFAULT_DIR);
FSkinTexture.BG_MATCH.load(preferredDir, ForgeConstants.DEFAULT_SKINS_DIR);
//assemble avatar textures
int counter = 0;
@@ -302,13 +298,7 @@ public class FSkin {
public static ArrayList<String> getSkinDirectoryNames() {
final ArrayList<String> mySkins = new ArrayList<String>();
final FileHandle dir;
if (Gdx.app.getType() == ApplicationType.Desktop) {
dir = Gdx.files.internal("./bin/" + FILE_SKINS_DIR); //needed to iterate over directory for Desktop
}
else {
dir = Gdx.files.internal(FILE_SKINS_DIR);
}
final FileHandle dir = Gdx.files.local(ForgeConstants.SKINS_DIR);
if (!dir.exists() || !dir.isDirectory()) {
System.err.println("FSkin > can't find skins directory!");
}

View File

@@ -22,6 +22,12 @@ import com.google.common.eventbus.Subscribe;
import forge.FThreads;
import forge.Forge;
import forge.card.CardCharacteristicName;
import forge.control.FControlGameEventHandler;
import forge.control.FControlGamePlayback;
import forge.events.IUiEventVisitor;
import forge.events.UiEvent;
import forge.events.UiEventAttackerDeclared;
import forge.events.UiEventBlockerAssigned;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameRules;
@@ -41,16 +47,12 @@ import forge.game.player.RegisteredPlayer;
import forge.game.trigger.TriggerType;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.match.input.InputProxy;
import forge.match.input.InputQueue;
import forge.model.FModel;
import forge.net.FServer;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.screens.match.events.IUiEventVisitor;
import forge.screens.match.events.UiEvent;
import forge.screens.match.events.UiEventAttackerDeclared;
import forge.screens.match.events.UiEventBlockerAssigned;
import forge.screens.match.input.InputProxy;
import forge.screens.match.input.InputQueue;
import forge.screens.match.views.VAssignDamage;
import forge.screens.match.views.VCardDisplayArea.CardAreaPanel;
import forge.screens.match.views.VPhaseIndicator;

View File

@@ -1,388 +0,0 @@
package forge.screens.match;
import com.google.common.eventbus.Subscribe;
import forge.FThreads;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.event.*;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.net.FServer;
import forge.screens.match.views.VPhaseIndicator.PhaseLabel;
import forge.screens.match.winlose.ViewWinLose;
import forge.toolbox.GuiChoose;
import forge.util.Lang;
import forge.util.maps.MapOfLists;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public FControlGameEventHandler() {
}
@Subscribe
public void receiveGameEvent(final GameEvent ev) {
ev.visit(this);
}
private final AtomicBoolean phaseUpdPlanned = new AtomicBoolean(false);
@Override
public Void visit(final GameEventTurnPhase ev) {
if (phaseUpdPlanned.getAndSet(true)) return null;
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() {
PhaseHandler pH = FControl.getGame().getPhaseHandler();
Player p = pH.getPlayerTurn();
PhaseType ph = pH.getPhase();
phaseUpdPlanned.set(false);
PhaseLabel lbl = FControl.getPlayerPanel(p).getPhaseIndicator().getLabel(ph);
FControl.resetAllPhaseButtons();
if (lbl != null) { lbl.setActive(true); }
} });
return null;
}
/* (non-Javadoc)
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventPlayerPriority)
*/
private final AtomicBoolean combatUpdPlanned = new AtomicBoolean(false);
@Override
public Void visit(GameEventPlayerPriority event) {
if (combatUpdPlanned.getAndSet(true)) { return null; }
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
combatUpdPlanned.set(false);
FControl.showCombat(FControl.getGame().getCombat());
}
});
return null;
}
private final AtomicBoolean turnUpdPlanned = new AtomicBoolean(false);
@Override
public Void visit(final GameEventTurnBegan event) {
if (turnUpdPlanned.getAndSet(true)) { return null; }
final Game game = FControl.getGame(); // to make sure control gets a correct game instance
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
/*VField nextField = FControl.getFieldViewFor(event.turnOwner);
SDisplayUtil.showTab(nextField);*/
turnUpdPlanned.set(false);
FControl.getView().getPrompt().updateText(game);
}
});
return null;
}
@Override
public Void visit(GameEventAnteCardsSelected ev) {
// Require EDT here?
List<Object> options = new ArrayList<Object>();
for (final Entry<Player, Card> kv : ((GameEventAnteCardsSelected) ev).cards.entries()) {
options.add(" -- From " + Lang.getPossesive(kv.getKey().getName()) + " deck --");
options.add(kv.getValue());
}
GuiChoose.one("These cards were chosen to ante", options);
return null;
}
@Override
public Void visit(GameEventPlayerControl ev) {
if (FControl.getGame().isGameOver()) {
return null;
}
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() {
/*FControl.initHandViews(FServer.getLobby().getGuiPlayer());
SLayoutIO.loadLayout(null);
VMatchUI.SINGLETON_INSTANCE.populate();
for (VHand h : VMatchUI.SINGLETON_INSTANCE.getHands()) {
h.getLayoutControl().updateHand();
}*/
} });
return null;
}
private final Runnable unlockGameThreadOnGameOver = new Runnable() {
@Override
public void run() {
FControl.getInputQueue().onGameOver(true); // this will unlock any game threads waiting for inputs to complete
}
};
@Override
public Void visit(GameEventGameOutcome ev) {
FThreads.invokeInEdtNowOrLater(unlockGameThreadOnGameOver);
return null;
}
@Override
public Void visit(GameEventGameFinished ev) {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
new ViewWinLose(FControl.getGame()).setVisible(true);
}
});
return null;
}
private final AtomicBoolean stackUpdPlanned = new AtomicBoolean(false);
private final Runnable updStack = new Runnable() {
@Override
public void run() {
stackUpdPlanned.set(false);
FControl.getView().getStack().update();
}
};
@Override
public Void visit(GameEventSpellAbilityCast event) {
if (!stackUpdPlanned.getAndSet(true)) {
FThreads.invokeInEdtNowOrLater(updStack);
}
return null;
}
@Override
public Void visit(GameEventSpellResolved event) {
if (!stackUpdPlanned.getAndSet(true)) {
FThreads.invokeInEdtNowOrLater(updStack);
}
return null;
}
@Override
public Void visit(GameEventSpellRemovedFromStack event) {
if (!stackUpdPlanned.getAndSet(true)) {
FThreads.invokeInEdtNowOrLater(updStack);
}
return null;
}
private final List<Pair<Player, ZoneType>> zonesToUpdate = new Vector<Pair<Player, ZoneType>>();
private final Runnable updZones = new Runnable() {
@Override public void run() {
synchronized (zonesToUpdate) {
FControl.updateZones(zonesToUpdate);
zonesToUpdate.clear();
}
}
};
@Override
public Void visit(GameEventZone event) {
if (event.player != null) {
// anything except stack will get here
updateZone(Pair.of(event.player, event.zoneType));
}
return null;
}
@Override
public Void visit(GameEventCardAttachment event) {
// TODO Auto-generated method stub
Game game = event.equipment.getGame();
PlayerZone zEq = (PlayerZone)game.getZoneOf(event.equipment);
if (event.oldEntiy instanceof Card) {
updateZone(game.getZoneOf((Card)event.oldEntiy));
}
if (event.newTarget instanceof Card) {
updateZone(game.getZoneOf((Card)event.newTarget));
}
return updateZone(zEq);
}
private Void updateZone(Zone z) {
return updateZone(Pair.of(z.getPlayer(), z.getZoneType()));
}
private Void updateZone(Pair<Player, ZoneType> kv) {
boolean needUpdate = false;
synchronized (zonesToUpdate) {
needUpdate = zonesToUpdate.isEmpty();
if (!zonesToUpdate.contains(kv)) {
zonesToUpdate.add(kv);
}
}
if (needUpdate) {
FThreads.invokeInEdtNowOrLater(updZones);
}
return null;
}
private final Set<Card> cardsToUpdate = new HashSet<Card>();
private final Runnable updCards = new Runnable() {
@Override
public void run() {
synchronized (cardsToUpdate) {
FControl.updateCards(cardsToUpdate);
cardsToUpdate.clear();
}
}
};
@Override
public Void visit(GameEventCardTapped event) {
return updateSingleCard(event.card);
}
@Override
public Void visit(GameEventCardPhased event) {
return updateSingleCard(event.card);
}
@Override
public Void visit(GameEventCardDamaged event) {
return updateSingleCard(event.card);
}
@Override
public Void visit(GameEventCardCounters event) {
return updateSingleCard(event.card);
}
@Override
public Void visit(GameEventBlockersDeclared event) { // This is to draw icons on blockers declared by AI
for (MapOfLists<Card, Card> kv : event.blockers.values()) {
for (Collection<Card> blockers : kv.values()) {
updateManyCards(blockers);
}
}
return super.visit(event);
}
@Override
public Void visit(GameEventAttackersDeclared event) {
// Skip redraw for GUI player?
if (event.player.getLobbyPlayer() == FServer.getLobby().getGuiPlayer()) {
return null;
}
// Update all attackers.
// Although they might have been updated when they were apped, there could be someone with vigilance, not redrawn yet.
updateManyCards(event.attackersMap.values());
return super.visit(event);
}
@Override
public Void visit(GameEventCombatEnded event) {
// This should remove sword/shield icons from combatants by the time game moves to M2
updateManyCards(event.attackers);
updateManyCards(event.blockers);
return null;
}
private Void updateSingleCard(Card c) {
boolean needUpdate = false;
synchronized (cardsToUpdate) {
needUpdate = cardsToUpdate.isEmpty();
if (!cardsToUpdate.contains(c)) {
cardsToUpdate.add(c);
}
}
if (needUpdate) {
FThreads.invokeInEdtNowOrLater(updCards);
}
return null;
}
private Void updateManyCards(Collection<Card> cc) {
boolean needUpdate = false;
synchronized (cardsToUpdate) {
needUpdate = cardsToUpdate.isEmpty();
cardsToUpdate.addAll(cc);
}
if (needUpdate) {
FThreads.invokeInEdtNowOrLater(updCards);
}
return null;
}
/* (non-Javadoc)
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventCardStatsChanged)
*/
@Override
public Void visit(GameEventCardStatsChanged event) {
// TODO Smart partial updates
return updateManyCards(event.cards);
}
// Update manapool
private final List<Player> manaPoolUpdate = new Vector<Player>();
private final Runnable updManaPool = new Runnable() {
@Override public void run() {
synchronized (manaPoolUpdate) {
FControl.updateManaPool(manaPoolUpdate);
manaPoolUpdate.clear();
}
}
};
@Override
public Void visit(GameEventManaPool event) {
boolean invokeUpdate = false;
synchronized (manaPoolUpdate) {
if (!manaPoolUpdate.contains(event.player)) {
invokeUpdate = manaPoolUpdate.isEmpty();
manaPoolUpdate.add(event.player);
}
}
if (invokeUpdate)
FThreads.invokeInEdtNowOrLater(updManaPool);
return null;
}
// Update lives counters
private final List<Player> livesUpdate = new Vector<Player>();
private final Runnable updLives = new Runnable() {
@Override public void run() {
synchronized (livesUpdate) {
FControl.updateLives(livesUpdate);
livesUpdate.clear();
}
}
};
@Override
public Void visit(GameEventPlayerLivesChanged event) {
boolean invokeUpdate = false;
synchronized (livesUpdate) {
if (!livesUpdate.contains(event.player)) {
invokeUpdate = livesUpdate.isEmpty();
livesUpdate.add(event.player);
}
}
if (invokeUpdate)
FThreads.invokeInEdtNowOrLater(updLives);
return null;
}
@Override
public Void visit(GameEventPlayerPoisoned event) {
boolean invokeUpdate = false;
synchronized (livesUpdate) {
if (!livesUpdate.contains(event.receiver)) {
invokeUpdate = livesUpdate.isEmpty();
livesUpdate.add(event.receiver);
}
}
if (invokeUpdate)
FThreads.invokeInEdtNowOrLater(updLives);
return null;
}
}

View File

@@ -1,176 +0,0 @@
package forge.screens.match;
import com.google.common.eventbus.Subscribe;
import forge.FThreads;
import forge.game.event.*;
import forge.screens.match.input.InputPlaybackControl;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean;
public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
private final InputPlaybackControl inputPlayback = new InputPlaybackControl(this);
private final AtomicBoolean paused = new AtomicBoolean(false);
private final CyclicBarrier gameThreadPauser = new CyclicBarrier(2);
public FControlGamePlayback() {
}
@Subscribe
public void receiveGameEvent(final GameEvent ev) { ev.visit(this); }
private int phasesDelay = 200;
private int combatDelay = 400;
private int castDelay = 400;
private int resolveDelay = 400;
private boolean fasterPlayback = false;
private void pauseForEvent(int delay) {
try {
Thread.sleep(fasterPlayback ? delay / 10 : delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e.printStackTrace();
}
}
@Override
public Void visit(GameEventBlockersDeclared event) {
pauseForEvent(combatDelay);
return super.visit(event);
}
/* (non-Javadoc)
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventTurnPhase)
*/
@Override
public Void visit(GameEventTurnPhase ev) {
boolean isUiToStop = FControl.stopAtPhase(ev.playerTurn, ev.phase);
switch(ev.phase) {
case COMBAT_END:
case COMBAT_DECLARE_ATTACKERS:
case COMBAT_DECLARE_BLOCKERS:
if (FControl.getGame().getPhaseHandler().inCombat()) {
pauseForEvent(combatDelay);
}
break;
default:
if (isUiToStop) {
pauseForEvent(phasesDelay);
}
break;
}
return null;
}
/* (non-Javadoc)
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventDuelFinished)
*/
@Override
public Void visit(GameEventGameFinished event) {
FControl.getInputQueue().removeInput(inputPlayback);
return null;
}
@Override
public Void visit(GameEventGameStarted event) {
FControl.getInputQueue().setInput(inputPlayback);
return null;
}
@Override
public Void visit(GameEventLandPlayed event) {
pauseForEvent(resolveDelay);
return super.visit(event);
}
@Override
public Void visit(final GameEventSpellResolved event) {
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { FControl.setCard(event.spell.getHostCard()); } });
pauseForEvent(resolveDelay);
return null;
}
/* (non-Javadoc)
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventSpellAbilityCast)
*/
@Override
public Void visit(final GameEventSpellAbilityCast event) {
FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { FControl.setCard(event.sa.getHostCard()); } });
pauseForEvent(castDelay);
return null;
}
/* (non-Javadoc)
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventPlayerPriority)
*/
@Override
public Void visit(GameEventPlayerPriority event) {
if (paused.get()) {
try {
inputPlayback.onGamePaused();
gameThreadPauser.await();
gameThreadPauser.reset();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
return null;
}
public void onGameStopRequested() {
paused.set(false);
if (gameThreadPauser.getNumberWaiting() != 0) {
releaseGameThread();
}
}
private void releaseGameThread() {
// just need to run another thread through the barrier... not edt preferrably :)
FControl.getGame().getAction().invoke(new Runnable() {
@Override
public void run() {
try {
gameThreadPauser.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e.printStackTrace();
}
}
});
}
public void resume() {
paused.set(false);
releaseGameThread();
}
public void pause() {
paused.set(true);
}
public void singleStep() {
releaseGameThread();
}
/**
* TODO: Write javadoc for this method.
* @param isFast
*/
public void setSpeed(boolean isFast) {
fasterPlayback = isFast;
}
}

View File

@@ -0,0 +1,41 @@
package forge.screens.match;
import java.util.ArrayList;
import java.util.List;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
import forge.match.input.Input;
import forge.match.input.InputPassPriority;
import forge.toolbox.FCardZoom;
import forge.toolbox.FCardZoom.ZoomController;
public class InputSelectCard {
private InputSelectCard() {
}
public static void selectCard(Card card, List<Card> orderedCards) {
Input currentInput = FControl.getInputQueue().getInput();
if (currentInput == null) { return; }
List<Card> orderedCardOptions = new ArrayList<Card>(orderedCards); //copy list to allow it being modified
if (currentInput instanceof InputPassPriority) {
FCardZoom.show("Select a spell/ability", card, orderedCardOptions, new ZoomController<SpellAbility>() {
@Override
public List<SpellAbility> getOptions(final Card card) {
return card.getAllPossibleAbilities(FControl.getCurrentPlayer(), true);
}
@Override
public boolean selectOption(final Card card, final SpellAbility option) {
FControl.getInputProxy().selectAbility(option);
return true; //TODO: Avoid hiding card zoom when selecting mana abilities
}
});
}
else {
FControl.getInputProxy().selectCard(card, null);
}
}
}

View File

@@ -1,6 +0,0 @@
package forge.screens.match.events;
public interface IUiEventVisitor<T> {
T visit(UiEventBlockerAssigned event);
T visit(UiEventAttackerDeclared event);
}

View File

@@ -1,7 +0,0 @@
package forge.screens.match.events;
public abstract class UiEvent {
public abstract <T> T visit(IUiEventVisitor<T> visitor);
}

View File

@@ -1,32 +0,0 @@
package forge.screens.match.events;
import forge.game.GameEntity;
import forge.game.card.Card;
/**
* TODO: Write javadoc for this type.
*
*/
public class UiEventAttackerDeclared extends UiEvent {
public final Card attacker;
public final GameEntity defender;
public UiEventAttackerDeclared(Card card, GameEntity currentDefender) {
attacker = card;
defender = currentDefender;
}
@Override
public <T> T visit(IUiEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return attacker.toString() + ( defender == null ? " removed from combat" : " declared to attack " + defender.getName() );
}
}

View File

@@ -1,21 +0,0 @@
package forge.screens.match.events;
import forge.game.card.Card;
public class UiEventBlockerAssigned extends UiEvent {
public final Card blocker;
public final Card attackerBeingBlocked;
public UiEventBlockerAssigned(Card card, Card currentAttacker) {
blocker = card;
attackerBeingBlocked = currentAttacker;
}
@Override
public <T> T visit(IUiEventVisitor<T> visitor) {
return visitor.visit(this);
}
}

View File

@@ -1,65 +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.screens.match.input;
import forge.screens.match.FControl;
import forge.toolbox.FButton;
/**
* Manages match UI OK/Cancel button enabling and focus
*/
public class ButtonUtil {
public static void setButtonText(String okLabel, String cancelLabel) {
getOk().setText(okLabel);
getCancel().setText(cancelLabel);
}
public static void reset() {
disableAll();
getOk().setText("OK");
getCancel().setText("Cancel");
}
public static void enableAll() {
getOk().setEnabled(true);
getCancel().setEnabled(true);
}
public static void disableAll() {
getOk().setEnabled(false);
getCancel().setEnabled(false);
}
public static void enableOnlyOk() {
getOk().setEnabled(true);
getCancel().setEnabled(false);
}
public static void enableOnlyCancel() {
getOk().setEnabled(false);
getCancel().setEnabled(true);
}
private static FButton getOk() {
return FControl.getView().getPrompt().getBtnOk();
}
private static FButton getCancel() {
return FControl.getView().getPrompt().getBtnCancel();
}
}

View File

@@ -1,24 +0,0 @@
package forge.screens.match.input;
import java.util.List;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public interface Input {
// showMessage() is always the first method called
void showMessageInitial();
void selectCard(final Card card, final List<Card> orderedCardOptions);
void selectAbility(final SpellAbility ab);
void selectPlayer(final Player player);
void selectButtonOK();
void selectButtonCancel();
}

View File

@@ -1,274 +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.screens.match.input;
import com.google.common.collect.Iterables;
import forge.game.GameEntity;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardPredicates;
import forge.game.combat.AttackingBand;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.screens.match.FControl;
import forge.screens.match.events.UiEventAttackerDeclared;
import forge.toolbox.FCardZoom;
import forge.toolbox.FCardZoom.ZoomController;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* InputAttack class.
* </p>
*
* @author Forge
* @version $Id: InputAttack.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public class InputAttack extends InputSyncronizedBase {
/** Constant <code>serialVersionUID=7849903731842214245L</code>. */
private static final long serialVersionUID = 7849903731842214245L;
private final Combat combat;
private final List<GameEntity> defenders;
private GameEntity currentDefender;
private final Player playerAttacks;
private final Player playerDeclares;
private AttackingBand activeBand = null;
public InputAttack(Player attacks, Player declares, Combat combat) {
this.playerAttacks = attacks;
this.playerDeclares = declares;
this.combat = combat;
this.defenders = combat.getDefenders();
}
/** {@inheritDoc} */
@Override
public final void showMessage() {
// TODO still seems to have some issues with multiple planeswalkers
ButtonUtil.enableOnlyOk();
setCurrentDefender(defenders.isEmpty() ? null : defenders.get(0));
if ( null == currentDefender ) {
System.err.println("InputAttack has no potential defenders!");
return; // should even throw here!
}
List<Card> possibleAttackers = playerAttacks.getCardsIn(ZoneType.Battlefield);
for (Card c : Iterables.filter(possibleAttackers, CardPredicates.Presets.CREATURES)) {
if (c.hasKeyword("CARDNAME attacks each turn if able.")) {
for(GameEntity def : defenders ) {
if( CombatUtil.canAttack(c, def, combat) ) {
combat.addAttacker(c, currentDefender);
FControl.fireEvent(new UiEventAttackerDeclared(c, currentDefender));
break;
}
}
} else if (c.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
final int i = c.getKeywordPosition("CARDNAME attacks specific player each combat if able");
final String defined = c.getKeyword().get(i).split(":")[1];
final Player player = AbilityUtils.getDefinedPlayers(c, defined, null).get(0);
if (player != null && CombatUtil.canAttack(c, player, combat)) {
combat.addAttacker(c, player);
FControl.fireEvent(new UiEventAttackerDeclared(c, player));
}
}
}
}
private void showCombat() {
// redraw sword icons
FControl.showCombat(combat);
}
/** {@inheritDoc} */
@Override
protected final void onOk() {
// TODO Add check to see if each must attack creature is attacking
// Propaganda costs could have been paid here.
setCurrentDefender(null); // remove highlights
activateBand(null);
stop();
}
@Override
protected final void onPlayerSelected(Player selected) {
if (defenders.contains(selected)) {
setCurrentDefender(selected);
}
else {
flashIncorrectAction(); // cannot attack that player
}
}
public enum Option {
DECLARE_AS_ATTACKER("Declare as Attacker"),
REMOVE_FROM_COMBAT("Remove from Combat"),
ATTACK_THIS_DEFENDER("Attack this Defender"),
ACTIVATE_BAND("Activate Band"),
JOIN_BAND("Join Band");
private String text;
private Option(String text0) {
text = text0;
}
public String toString() {
return text;
}
}
/** {@inheritDoc} */
@Override
protected final void onCardSelected(final Card card, final List<Card> orderedCardOptions) {
FCardZoom.show(FControl.getView().getPrompt().getMessage(),
card, orderedCardOptions, new ZoomController<Option>() {
@Override
public List<Option> getOptions(final Card card) {
List<Option> options = new ArrayList<Option>();
if (card.getController().isOpponentOf(playerAttacks)) {
if (defenders.contains(card)) { // planeswalker?
options.add(Option.ATTACK_THIS_DEFENDER);
}
}
else if (combat.getAttackers().contains(card)) {
if (!card.hasKeyword("CARDNAME attacks each turn if able.") &&
!card.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
options.add(Option.REMOVE_FROM_COMBAT);
}
if (combat.isAttacking(card, currentDefender)) {
// Activate band by selecting/deselecting a band member
if (activeBand == null) {
options.add(Option.ACTIVATE_BAND);
}
else if (!activeBand.getAttackers().contains(card) && activeBand.canJoinBand(card)) {
options.add(Option.JOIN_BAND);
}
}
}
else if (playerAttacks.getZone(ZoneType.Battlefield).contains(card) &&
CombatUtil.canAttack(card, currentDefender, combat)) {
options.add(Option.DECLARE_AS_ATTACKER);
if (activeBand != null && activeBand.canJoinBand(card)) {
options.add(Option.JOIN_BAND);
}
}
return options;
}
@Override
public boolean selectOption(final Card card, final Option option) {
boolean hideZoomView = false; //keep zoom open while declaring attackers by default
switch (option) {
case DECLARE_AS_ATTACKER:
if (combat.isAttacking(card)) {
combat.removeFromCombat(card);
}
declareAttacker(card);
showCombat();
break;
case REMOVE_FROM_COMBAT:
combat.removeFromCombat(card);
FControl.setUsedToPay(card, false);
showCombat();
activateBand(null); //When removing an attacker clear the attacking band
FControl.fireEvent(new UiEventAttackerDeclared(card, null));
break;
case ATTACK_THIS_DEFENDER:
setCurrentDefender(card);
hideZoomView = true; //don't keep zoom open if choosing defender
break;
case ACTIVATE_BAND:
activateBand(combat.getBandOfAttacker(card));
break;
case JOIN_BAND: //Join a band by selecting a non-active band member after activating a band
combat.removeFromCombat(card);
declareAttacker(card);
break;
}
showMessage();
return hideZoomView;
}
});
}
private void declareAttacker(final Card card) {
combat.addAttacker(card, currentDefender, this.activeBand);
this.activateBand(this.activeBand);
updateMessage();
FControl.fireEvent(new UiEventAttackerDeclared(card, currentDefender));
}
private final void setCurrentDefender(GameEntity def) {
currentDefender = def;
for( GameEntity ge: defenders ) {
if ( ge instanceof Card) {
FControl.setUsedToPay((Card)ge, ge == def);
}
else if (ge instanceof Player) {
FControl.setHighlighted((Player) ge, ge == def);
}
}
updateMessage();
// update UI
}
private final void activateBand(AttackingBand band) {
if (this.activeBand != null) {
for(Card card : this.activeBand.getAttackers()) {
FControl.setUsedToPay(card, false);
}
}
this.activeBand = band;
if (this.activeBand != null) {
for(Card card : this.activeBand.getAttackers()) {
FControl.setUsedToPay(card, true);
}
}
// update UI
}
private void updateMessage() {
StringBuilder sb = new StringBuilder();
sb.append(playerDeclares.getName()).append(", ");
sb.append(playerAttacks == playerDeclares ? "declare attackers." : "declare attackers for " + playerAttacks.getName()).append("\n");
sb.append("Selecting Creatures to Attack ").append(currentDefender).append("\n\n");
sb.append("To change the current defender, click on the player or planeswalker you wish to attack.\n");
sb.append("To attack as a band, click an attacking creature to activate its 'band', select another to join the band.");
showMessage(sb.toString());
}
}

View File

@@ -1,130 +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.screens.match.input;
import java.util.List;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.phase.PhaseHandler;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.screens.match.FControl;
import forge.toolbox.FCardZoom;
import forge.toolbox.FCardZoom.ZoomController;
/**
* <p>
* Abstract Input class.
* </p>
*
* @author Forge
* @version $Id: InputBase.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public abstract class InputBase implements java.io.Serializable, Input {
/** Constant <code>serialVersionUID=-6539552513871194081L</code>. */
private static final long serialVersionUID = -6539552513871194081L;
private boolean finished = false;
protected final boolean isFinished() { return finished; }
protected final void setFinished() {
FCardZoom.hideZoom(); //ensure zoom hidden when input finished
finished = true;
}
// showMessage() is always the first method called
@Override
public final void showMessageInitial() {
finished = false;
showMessage();
}
protected abstract void showMessage();
@Override
public final void selectPlayer(final Player player) {
if (isFinished()) { return; }
onPlayerSelected(player);
}
@Override
public void selectAbility(SpellAbility ab) { }
@Override
public final void selectButtonCancel() {
if (isFinished()) { return; }
onCancel();
}
@Override
public final void selectButtonOK() {
if (isFinished()) { return; }
onOk();
}
@Override
public final void selectCard(final Card card, final List<Card> orderedCardOptions) {
if (isFinished()) { return; }
onCardSelected(card, orderedCardOptions);
}
protected void onCardSelected(final Card card, final List<Card> orderedCardOptions) {
//for base input, just show zoom view with no options
FCardZoom.show(FControl.getView().getPrompt().getMessage(),
card, orderedCardOptions, new ZoomController<Object>() {
@Override
public List<Object> getOptions(Card card) {
return null;
}
@Override
public boolean selectOption(Card card, Object option) {
return true;
}
});
}
protected void onPlayerSelected(final Player p) {}
protected void onCancel() {}
protected void onOk() {}
// to remove need for CMatchUI dependence
protected final void showMessage(String message) {
FControl.showMessage(message);
}
protected final void flashIncorrectAction() {
FControl.getView().getPrompt().remind();
}
protected String getTurnPhasePriorityMessage(Game game) {
final PhaseHandler ph = game.getPhaseHandler();
final StringBuilder sb = new StringBuilder();
sb.append("Priority: ").append(ph.getPriorityPlayer()).append("\n");
sb.append("Turn ").append(ph.getTurn()).append(" (").append(ph.getPlayerTurn()).append(")\n");
sb.append("Phase: ").append(ph.getPhase().nameForUi).append("\n");
sb.append("Stack: ");
if (!game.getStack().isEmpty()) {
sb.append(game.getStack().size()).append(" to Resolve.");
} else {
sb.append("Empty");
}
sb.append("\n");
return sb.toString();
}
}

View File

@@ -1,172 +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.screens.match.input;
import java.util.ArrayList;
import java.util.List;
import forge.game.card.Card;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.screens.match.FControl;
import forge.screens.match.events.UiEventBlockerAssigned;
import forge.toolbox.FCardZoom;
import forge.toolbox.FOptionPane;
import forge.toolbox.FCardZoom.ZoomController;
/**
* <p>
* Input_Block class.
* </p>
*
* @author Forge
* @version $Id: InputBlock.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public class InputBlock extends InputSyncronizedBase {
/** Constant <code>serialVersionUID=6120743598368928128L</code>. */
private static final long serialVersionUID = 6120743598368928128L;
private Card currentAttacker = null;
// some cards may block several creatures at a time. (ex: Two-Headed Dragon, Vanguard's Shield)
private final Combat combat;
private final Player defender;
private final Player declarer;
/**
* TODO: Write javadoc for Constructor.
* @param priority
*/
public InputBlock(Player whoDeclares, Player whoDefends, Combat combat) {
defender = whoDefends;
declarer = whoDeclares;
this.combat = combat;
}
/** {@inheritDoc} */
@Override
protected final void showMessage() {
// could add "Reset Blockers" button
ButtonUtil.enableOnlyOk();
String prompt = declarer == defender ? "declare blockers." : "declare blockers for " + defender.getName();
final StringBuilder sb = new StringBuilder(declarer.getName());
sb.append(", ").append(prompt).append("\n\n");
if (this.currentAttacker == null) {
sb.append("To Block, click on your opponent's attacker first, then your blocker(s).\n");
sb.append("To cancel a block right-click on your blocker");
}
else {
final String attackerName = this.currentAttacker.isFaceDown() ? "Morph" : this.currentAttacker.getName();
sb.append("Select a creature to block ").append(attackerName).append(" (");
sb.append(this.currentAttacker.getUniqueNumber()).append("). ");
sb.append("To cancel a block right-click on your blocker");
}
showMessage(sb.toString());
FControl.showCombat(combat);
}
/** {@inheritDoc} */
@Override
public final void onOk() {
String blockErrors = CombatUtil.validateBlocks(combat, defender);
if( null == blockErrors ) {
// Done blocking
ButtonUtil.reset();
setCurrentAttacker(null);
stop();
}
else {
FOptionPane.showMessageDialog(blockErrors);
}
}
public enum Option {
DECLARE_AS_BLOCKER("Declare as Blocker"),
REMOVE_FROM_COMBAT("Remove from Combat"),
BLOCK_THIS_ATTACKER("Block this Attacker"),;
private String text;
private Option(String text0) {
text = text0;
}
public String toString() {
return text;
}
}
/** {@inheritDoc} */
@Override
public final void onCardSelected(final Card card, final List<Card> orderedCardOptions) {
FCardZoom.show(FControl.getView().getPrompt().getMessage(),
card, orderedCardOptions, new ZoomController<Option>() {
@Override
public List<Option> getOptions(final Card card) {
List<Option> options = new ArrayList<Option>();
if (combat.isAttacking(card)) {
options.add(Option.BLOCK_THIS_ATTACKER);
}
else if (card.getController() == defender) {
if (combat.isBlocking(card)) {
options.add(Option.REMOVE_FROM_COMBAT);
}
else if (currentAttacker != null && card.isCreature() &&
defender.getZone(ZoneType.Battlefield).contains(card) &&
CombatUtil.canBlock(currentAttacker, card, combat)) {
options.add(Option.DECLARE_AS_BLOCKER);
}
}
return options;
}
@Override
public boolean selectOption(final Card card, final Option option) {
switch (option) {
case DECLARE_AS_BLOCKER:
combat.addBlocker(currentAttacker, card);
FControl.fireEvent(new UiEventBlockerAssigned(card, currentAttacker));
break;
case REMOVE_FROM_COMBAT:
combat.removeFromCombat(card);
FControl.fireEvent(new UiEventBlockerAssigned(card, (Card)null));
break;
case BLOCK_THIS_ATTACKER:
setCurrentAttacker(card);
break;
}
showMessage();
return true; //hide zoom view after declaring blocker
}
});
}
private void setCurrentAttacker(Card card) {
currentAttacker = card;
for(Card c : combat.getAttackers()) {
FControl.setUsedToPay(c, card == c);
}
}
}

View File

@@ -1,81 +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.screens.match.input;
/**
* <p>
* InputConfirm class.
* </p>
*
* @author Forge
* @version $Id: InputConfirm.java 21647 2013-05-24 22:31:11Z Max mtg $
*/
public class InputConfirm extends InputSyncronizedBase {
private static final long serialVersionUID = -3591794991788531626L;
private final String message;
private final String yesButtonText;
private final String noButtonText;
private boolean result;
public InputConfirm(String message0) {
this(message0, "Yes", "No", true);
}
public InputConfirm(String message0, String yesButtonText0, String noButtonText0) {
this(message0, yesButtonText0, noButtonText0, true);
}
public InputConfirm(String message0, String yesButtonText0, String noButtonText0, boolean defaultYes0) {
this.message = message0;
this.yesButtonText = yesButtonText0;
this.noButtonText = noButtonText0;
result = defaultYes0;
}
/** {@inheritDoc} */
@Override
protected final void showMessage() {
ButtonUtil.setButtonText(this.yesButtonText, this.noButtonText);
ButtonUtil.enableAll();
showMessage(this.message);
}
/** {@inheritDoc} */
@Override
protected final void onOk() {
this.result = true;
done();
}
/** {@inheritDoc} */
@Override
protected final void onCancel() {
this.result = false;
done();
}
private void done() {
ButtonUtil.reset();
stop();
}
public final boolean getResult() {
return this.result;
}
}

View File

@@ -1,189 +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.screens.match.input;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.screens.match.FControl;
import forge.toolbox.FCardZoom;
import forge.toolbox.GuiDialog;
import forge.toolbox.FCardZoom.ZoomController;
import forge.util.Lang;
import forge.util.ThreadUtil;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* InputConfirmMulligan class.
* </p>
*
* @author Forge
* @version $Id: InputConfirmMulligan.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public class InputConfirmMulligan extends InputSyncronizedBase {
/** Constant <code>serialVersionUID=-8112954303001155622L</code>. */
private static final long serialVersionUID = -8112954303001155622L;
boolean keepHand = false;
final boolean isCommander;
private final List<Card> selected = new ArrayList<Card>();
private final Player player;
private final Player startingPlayer;
public InputConfirmMulligan(Player humanPlayer, Player startsGame, boolean commander) {
player = humanPlayer;
isCommander = commander;
startingPlayer = startsGame;
}
/** {@inheritDoc} */
@Override
public final void showMessage() {
Game game = player.getGame();
StringBuilder sb = new StringBuilder();
if (startingPlayer == player) {
sb.append(player).append(", you are going first!\n\n");
}
else {
sb.append(startingPlayer.getName()).append(" is going first.\n");
sb.append(player).append(", you are going ").append(Lang.getOrdinal(game.getPosition(player, startingPlayer))).append(".\n\n");
}
if (isCommander) {
ButtonUtil.setButtonText("Keep", "Exile");
ButtonUtil.enableOnlyOk();
sb.append("Will you keep your hand or choose some cards to exile those and draw one less card?");
}
else {
ButtonUtil.setButtonText("Keep", "Mulligan");
ButtonUtil.enableAll();
sb.append("Do you want to keep your hand?");
}
showMessage(sb.toString());
}
/** {@inheritDoc} */
@Override
protected final void onOk() {
keepHand = true;
done();
}
/** {@inheritDoc} */
@Override
protected final void onCancel() {
keepHand = false;
done();
}
private void done() {
ButtonUtil.reset();
if (isCommander) {
// Clear the "selected" icon after clicking the done button
for (Card c : this.selected) {
FControl.setUsedToPay(c, false);
}
}
stop();
}
volatile boolean cardSelectLocked = false;
@Override
protected void onCardSelected(final Card card, final List<Card> orderedCardOptions) { // the only place that would cause troubles - input is supposed only to confirm, not to fire abilities
if (cardSelectLocked) { return; }
FCardZoom.show(FControl.getView().getPrompt().getMessage(),
card, orderedCardOptions, new ZoomController<String>() {
@Override
public List<String> getOptions(final Card card) {
List<String> options = new ArrayList<String>();
if (player.getZone(ZoneType.Hand).contains(card)) {
if (card.getName().equals("Serum Powder")) {
options.add("Exile all the cards from your hand, then draw that many cards.");
}
else if (isCommander) {
if (selected.contains(card)) {
options.add("Select Card");
}
else {
options.add("Unselect Card");
}
}
}
return options;
}
@Override
public boolean selectOption(final Card card, final String option) {
if (option == "Exile all the cards from your hand, then draw that many cards.") {
if (GuiDialog.confirm(card, "This action cannot be undone. Proceed?")) {
cardSelectLocked = true;
ThreadUtil.invokeInGameThread(new Runnable() {
public void run() {
List<Card> hand = new ArrayList<Card>(card.getController().getCardsIn(ZoneType.Hand));
for (Card c : hand) {
player.getGame().getAction().exile(c);
}
card.getController().drawCards(hand.size());
cardSelectLocked = false;
}
});
}
return true;
}
if (isCommander) { // allow to choose cards for partial paris
if (selected.contains(card)) {
FControl.setUsedToPay(card, false);
selected.remove(card);
}
else {
FControl.setUsedToPay(card, true);
selected.add(card);
}
if (selected.isEmpty()) {
ButtonUtil.enableOnlyOk();
}
else {
ButtonUtil.enableAll();
}
return false; //keep zoom view open
}
return true;
}
});
}
public final boolean isKeepHand() {
return keepHand;
}
public List<Card> getSelectedCards() {
return selected;
}
}

View File

@@ -1,66 +0,0 @@
package forge.screens.match.input;
import forge.FThreads;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.screens.match.FControl;
import forge.util.ThreadUtil;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class InputLockUI implements Input {
private final AtomicInteger iCall = new AtomicInteger();
public InputLockUI(InputQueue inputQueue) {
}
public void showMessageInitial() {
int ixCall = 1 + iCall.getAndIncrement();
ThreadUtil.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.invokeInEdtLater(showMessageFromEdt);
}
};
private final Runnable showMessageFromEdt = new Runnable() {
@Override
public void run() {
ButtonUtil.disableAll();
showMessage("Waiting for actions...");
}
};
protected final boolean isActive() {
return FControl.getInputQueue().getInput() == this;
}
protected void showMessage(String message) {
FControl.showMessage(message);
}
@Override public void selectCard(final Card card, final List<Card> orderedCardOptions) {}
@Override public void selectAbility(SpellAbility ab) {}
@Override public void selectPlayer(Player player) {}
@Override public void selectButtonOK() {}
@Override public void selectButtonCancel() {}
}

View File

@@ -1,87 +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.screens.match.input;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.toolbox.FCardZoom;
import forge.toolbox.FCardZoom.ZoomController;
import java.util.List;
/**
* <p>
* Input_PassPriority class.
* </p>
*
* @author Forge
* @version $Id: InputPassPriority.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public class InputPassPriority extends InputSyncronizedBase {
/** Constant <code>serialVersionUID=-581477682214137181L</code>. */
private static final long serialVersionUID = -581477682214137181L;
private final Player player;
private SpellAbility chosenSa;
public InputPassPriority(Player human) {
player = human;
}
/** {@inheritDoc} */
@Override
public final void showMessage() {
showMessage(getTurnPhasePriorityMessage(player.getGame()));
chosenSa = null;
ButtonUtil.enableOnlyOk();
}
/** {@inheritDoc} */
@Override
protected final void onOk() {
stop();
}
public SpellAbility getChosenSa() {
return chosenSa;
}
@Override
protected void onCardSelected(final Card card, final List<Card> orderedCardOptions) {
FCardZoom.show("Select a spell/ability", card, orderedCardOptions, new ZoomController<SpellAbility>() {
@Override
public List<SpellAbility> getOptions(final Card card) {
return card.getAllPossibleAbilities(player, true);
}
@Override
public boolean selectOption(final Card card, final SpellAbility option) {
selectAbility(option);
return true; //TODO: Avoid hiding card zoom when selecting mana abilities
}
});
}
@Override
public void selectAbility(final SpellAbility ab) {
if (ab != null) {
chosenSa = ab;
stop();
}
}
}

View File

@@ -1,360 +0,0 @@
package forge.screens.match.input;
import forge.FThreads;
import forge.ai.ComputerUtilMana;
import forge.ai.PlayerControllerAi;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.mana.ManaAtom;
import forge.game.Game;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardUtil;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility;
import forge.player.HumanPlay;
import forge.screens.match.FControl;
import forge.toolbox.FCardZoom;
import forge.toolbox.FCardZoom.ZoomController;
import forge.util.Evaluator;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public abstract class InputPayMana extends InputSyncronizedBase {
private static final long serialVersionUID = -9133423708688480255L;
protected int phyLifeToLose = 0;
protected final Player player;
protected final Game game;
protected ManaCostBeingPaid manaCost;
protected final SpellAbility saPaidFor;
private boolean bPaid = false;
private Boolean canPayManaCost = null;
private boolean locked = false;
protected InputPayMana(SpellAbility saToPayFor, Player payer) {
this.player = payer;
this.game = player.getGame();
this.saPaidFor = saToPayFor;
}
@Override
protected final void onCardSelected(final Card card, final List<Card> orderedCardOptions) {
if (locked) {
System.err.print("Should wait till previous call to playAbility finishes.");
return;
}
FCardZoom.show(FControl.getView().getPrompt().getMessage(),
card, orderedCardOptions, new ZoomController<SpellAbility>() {
private byte colorCanUse, colorNeeded;
@Override
public List<SpellAbility> getOptions(final Card card) {
if (card.getController() != player || card.getManaAbility().isEmpty()) {
return null;
}
colorCanUse = 0;
colorNeeded = 0;
for (final byte color : MagicColor.WUBRG) {
if (manaCost.isAnyPartPayableWith(color, player.getManaPool())) { colorCanUse |= color; }
if (manaCost.needsColor(color, player.getManaPool())) { colorNeeded |= color; }
}
if (manaCost.isAnyPartPayableWith((byte) ManaAtom.COLORLESS, player.getManaPool()))
colorCanUse |= ManaAtom.COLORLESS;
if (colorCanUse == 0) { // no mana cost or something
return null;
}
// you can't remove unneeded abilities inside a for (am:abilities) loop :(
final String typeRes = manaCost.getSourceRestriction();
if (StringUtils.isNotBlank(typeRes) && !card.isType(typeRes)) {
return null;
}
boolean guessAbilityWithRequiredColors = true;
List<SpellAbility> abilities = new ArrayList<SpellAbility>();
for (SpellAbility ma : card.getManaAbility()) {
ma.setActivatingPlayer(player);
AbilityManaPart m = ma.getManaPartRecursive();
if (m == null || !ma.canPlay()) { continue; }
if (!abilityProducesManaColor(ma, m, colorCanUse)) { continue; }
if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) { continue; }
if (!m.meetsManaRestrictions(saPaidFor)) { continue; }
abilities.add(ma);
// skip express mana if the ability is not undoable or reusable
if (!ma.isUndoable() || !ma.getPayCosts().isRenewableResource() || ma.getSubAbility() != null) {
guessAbilityWithRequiredColors = false;
}
}
if (abilities.isEmpty()) {
return abilities;
}
// Store some information about color costs to help with any mana choices
if (colorNeeded == 0) { // only colorless left
if (saPaidFor.getHostCard() != null && saPaidFor.getHostCard().hasSVar("ManaNeededToAvoidNegativeEffect")) {
String[] negEffects = saPaidFor.getHostCard().getSVar("ManaNeededToAvoidNegativeEffect").split(",");
for (String negColor : negEffects) {
byte col = MagicColor.fromName(negColor);
colorCanUse |= col;
}
}
}
// If the card has any ability that tracks mana spent, skip express Mana choice
if (saPaidFor.tracksManaSpent()) {
colorCanUse = MagicColor.ALL_COLORS;
guessAbilityWithRequiredColors = false;
}
if (guessAbilityWithRequiredColors) {
// express Mana Choice
if (colorNeeded == 0) {
//avoid unnecessary prompt by pretending we need White
//for the sake of "Add one mana of any color" effects
colorNeeded = MagicColor.WHITE;
}
else {
final ArrayList<SpellAbility> colorMatches = new ArrayList<SpellAbility>();
for (SpellAbility sa : abilities) {
if (abilityProducesManaColor(sa, sa.getManaPartRecursive(), colorNeeded)) {
colorMatches.add(sa);
}
}
if (!colorMatches.isEmpty() && colorMatches.size() < abilities.size()) {
// leave behind only color matches
abilities = colorMatches;
}
}
}
return abilities;
}
@Override
public boolean selectOption(final Card card, final SpellAbility option) {
ColorSet colors = ColorSet.fromMask(0 == colorNeeded ? colorCanUse : colorNeeded);
option.getManaPartRecursive().setExpressChoice(colors);
Runnable proc = new Runnable() {
@Override
public void run() {
HumanPlay.playSpellAbility(option.getActivatingPlayer(), option);
player.getManaPool().payManaFromAbility(saPaidFor, manaCost, option);
onManaAbilityPaid();
onStateChanged();
}
};
locked = true;
game.getAction().invoke(proc);
return false;
}
});
}
public void useManaFromPool(byte colorCode) {
// find the matching mana in pool.
player.getManaPool().tryPayCostWithColor(colorCode, saPaidFor, manaCost);
onManaAbilityPaid();
showMessage();
}
/**
* <p>
* canMake. color is like "G", returns "Green".
* </p>
*
* @param am
* a {@link forge.card.spellability.AbilityMana} object.
* @param mana
* a {@link java.lang.String} object.
* @return a boolean.
*/
private static boolean abilityProducesManaColor(final SpellAbility am, AbilityManaPart m, final byte neededColor) {
if (0 != (neededColor & MagicColor.COLORLESS)) {
return true;
}
if (m.isAnyMana()) {
return true;
}
// check for produce mana replacement effects - they mess this up, so just use the mana ability
final Card source = am.getHostCard();
final Player activator = am.getActivatingPlayer();
final Game g = source.getGame();
final HashMap<String, Object> repParams = new HashMap<String, Object>();
repParams.put("Event", "ProduceMana");
repParams.put("Mana", m.getOrigProduced());
repParams.put("Affected", source);
repParams.put("Player", activator);
repParams.put("AbilityMana", am);
for (final Player p : g.getPlayers()) {
for (final Card crd : p.getAllCards()) {
for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) {
if (replacementEffect.requirementsCheck(g)
&& replacementEffect.canReplace(repParams)
&& replacementEffect.getMapParams().containsKey("ManaReplacement")
&& replacementEffect.zonesCheck(g.getZoneOf(crd))) {
return true;
}
}
}
}
if (am.getApi() == ApiType.ManaReflected) {
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am);
for (final String color : reflectableColors) {
if (0 != (neededColor & MagicColor.fromName(color))) {
return true;
}
}
}
else {
String colorsProduced = m.isComboMana() ? m.getComboColors() : m.getOrigProduced();
for (final String color : colorsProduced.split(" ")) {
if (0 != (neededColor & MagicColor.fromName(color))) {
return true;
}
if( (neededColor & ManaAtom.COLORLESS) != 0)
return true;
}
}
return false;
}
protected boolean isAlreadyPaid() {
if (manaCost.isPaid()) {
bPaid = true;
}
return bPaid;
}
protected boolean supportAutoPay() {
return true;
}
private void runAsAi(Runnable proc) {
this.player.runWithController(proc, new PlayerControllerAi(this.game, this.player, this.player.getOriginalLobbyPlayer()));
}
/** {@inheritDoc} */
@Override
protected void onOk() {
if (supportAutoPay()) {
//use AI utility to automatically pay mana cost if possible
final Runnable proc = new Runnable() {
@Override
public void run() {
ComputerUtilMana.payManaCost(manaCost, saPaidFor, player);
}
};
//must run in game thread as certain payment actions can only be automated there
game.getAction().invoke(new Runnable() {
@Override
public void run() {
runAsAi(proc);
onStateChanged();
}
});
}
}
protected void updateButtons() {
if (supportAutoPay()) {
ButtonUtil.setButtonText("Auto", "Cancel");
}
ButtonUtil.enableOnlyCancel();
}
protected final void updateMessage() {
locked = false;
if (supportAutoPay()) {
if (canPayManaCost == null) {
//use AI utility to determine if mana cost can be paid if that hasn't been determined yet
Evaluator<Boolean> proc = new Evaluator<Boolean>() {
@Override
public Boolean evaluate() {
return ComputerUtilMana.canPayManaCost(manaCost, saPaidFor, player);
}
};
runAsAi(proc);
canPayManaCost = proc.getResult();
}
if (canPayManaCost) {
ButtonUtil.enableAll(); //enabled Auto button if mana cost can be paid
}
}
showMessage(getMessage());
}
/** {@inheritDoc} */
@Override
protected final void onStop() {
if (supportAutoPay()) {
ButtonUtil.reset();
}
}
/** {@inheritDoc} */
@Override
public void showMessage() {
if (isFinished()) { return; }
updateButtons();
onStateChanged();
}
protected void onStateChanged() {
if (isAlreadyPaid()) {
done();
stop();
}
else {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
updateMessage();
}
});
}
}
protected void onManaAbilityPaid() {} // some inputs overload it
protected abstract void done();
protected abstract String getMessage();
@Override
public String toString() {
return String.format("PayManaBase %s left", manaCost.toString());
}
public boolean isPaid() { return bPaid; }
protected String messagePrefix;
public void setMessagePrefix(String prompt) {
// TODO Auto-generated method stub
messagePrefix = prompt;
}
}

View File

@@ -1,61 +0,0 @@
package forge.screens.match.input;
import forge.game.card.Card;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
public class InputPayManaOfCostPayment extends InputPayMana {
public InputPayManaOfCostPayment(ManaCostBeingPaid cost, SpellAbility spellAbility, Player payer) {
super(spellAbility, payer);
manaCost = cost;
}
private static final long serialVersionUID = 3467312982164195091L;
private int phyLifeToLose = 0;
@Override
protected final void onPlayerSelected(Player selected) {
if (player == selected) {
if (player.canPayLife(this.phyLifeToLose + 2) && manaCost.payPhyrexian()) {
this.phyLifeToLose += 2;
}
this.showMessage();
}
}
@Override
protected void done() {
final Card source = saPaidFor.getHostCard();
if (this.phyLifeToLose > 0) {
player.payLife(this.phyLifeToLose, source);
}
}
@Override
protected void onCancel() {
stop();
}
@Override
protected String getMessage() {
final String displayMana = manaCost.toString(false);
final StringBuilder msg = new StringBuilder();
if( messagePrefix != null )
msg.append(messagePrefix).append("\n");
msg.append("Pay Mana Cost: ").append(displayMana);
if (this.phyLifeToLose > 0) {
msg.append(" (");
msg.append(this.phyLifeToLose);
msg.append(" life paid for phyrexian mana)");
}
if (manaCost.containsPhyrexianMana()) {
msg.append("\n(Click on your life total to pay life for phyrexian mana.)");
}
return msg.toString();
}
}

View File

@@ -1,133 +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.screens.match.input;
import forge.card.mana.ManaCost;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
//pays the cost of a card played from the player's hand
//the card is removed from the players hand if the cost is paid
//CANNOT be used for ABILITIES
public class InputPayManaSimple extends InputPayMana {
// anything that uses this should be converted to Ability_Cost
/** Constant <code>serialVersionUID=3467312982164195091L</code>. */
private static final long serialVersionUID = 3467312982164195091L;
private final Card originalCard;
private final ManaCost originalManaCost;
public InputPayManaSimple(final Game game, final SpellAbility sa, final ManaCostBeingPaid manaCostToPay) {
super(sa, sa.getActivatingPlayer());
this.originalManaCost = manaCostToPay.toManaCost();
this.originalCard = sa.getHostCard();
if (sa.getHostCard().isCopiedSpell() && sa.isSpell()) {
this.manaCost = new ManaCostBeingPaid(ManaCost.ZERO);
game.getStack().add(this.saPaidFor);
}
else {
this.manaCost = manaCostToPay;
}
}
protected void onManaAbilityPaid() {
if (this.manaCost.isPaid()) {
this.originalCard.setSunburstValue(this.manaCost.getSunburst());
}
}
/** {@inheritDoc} */
@Override
protected final void onPlayerSelected(Player selected) {
if (player == selected) {
if (player.canPayLife(this.phyLifeToLose + 2) && manaCost.payPhyrexian()) {
this.phyLifeToLose += 2;
}
this.showMessage();
}
}
/**
* <p>
* done.
* </p>
*/
@Override
protected void done() {
this.originalCard.setSunburstValue(this.manaCost.getSunburst());
if (this.phyLifeToLose > 0) {
player.payLife(this.phyLifeToLose, this.originalCard);
}
if (!this.saPaidFor.getHostCard().isCopiedSpell()) {
if (this.saPaidFor.isSpell()) {
this.saPaidFor.setHostCard(game.getAction().moveToStack(this.originalCard));
}
}
}
/** {@inheritDoc} */
@Override
protected final void onCancel() {
player.getManaPool().refundManaPaid(this.saPaidFor);
// Update UI
this.stop();
}
/** {@inheritDoc} */
@Override
public final void showMessage() {
if (isFinished()) { return; }
updateButtons();
if (this.manaCost.isPaid() && !new ManaCostBeingPaid(this.originalManaCost).isPaid()) {
this.done();
this.stop();
}
else {
updateMessage();
}
}
/* (non-Javadoc)
* @see forge.control.input.InputPayManaBase#updateMessage()
*/
@Override
protected String getMessage() {
final StringBuilder msg = new StringBuilder("Pay Mana Cost: " + this.manaCost.toString());
if (this.phyLifeToLose > 0) {
msg.append(" (");
msg.append(this.phyLifeToLose);
msg.append(" life paid for phyrexian mana)");
}
if (this.manaCost.containsPhyrexianMana()) {
msg.append("\n(Click on your life total to pay life for phyrexian mana.)");
}
// has its own variant of checkIfPaid
return msg.toString();
}
}

View File

@@ -1,124 +0,0 @@
package forge.screens.match.input;
import forge.card.ColorSet;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser;
import forge.game.card.Card;
import forge.game.mana.Mana;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.spellability.SpellAbility;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
public class InputPayManaX extends InputPayMana {
private static final long serialVersionUID = -6900234444347364050L;
private int xPaid = 0;
private ArrayList<Mana> xPaidByColor = new ArrayList<>();
private byte colorsPaid;
private final ManaCost manaCostPerX;
private final boolean xCanBe0;
private boolean canceled = false;
public InputPayManaX(final SpellAbility sa0, final int amountX, final boolean xCanBe0) {
super(sa0, sa0.getActivatingPlayer());
xPaid = 0;
if (saPaidFor.hasParam("XColor")) {
String xColor = saPaidFor.getParam("XColor");
if (amountX == 1) {
manaCostPerX = new ManaCost(new ManaCostParser(xColor));
}
else {
List<String> list = new ArrayList<String>(amountX);
for (int i = 0; i < amountX; i++) {
list.add(xColor);
}
manaCostPerX = new ManaCost(new ManaCostParser(StringUtils.join(list, ' ')));
}
}
else {
manaCostPerX = ManaCost.get(amountX);
}
manaCost = new ManaCostBeingPaid(manaCostPerX);
this.xCanBe0 = xCanBe0;
colorsPaid = saPaidFor.getHostCard().getColorsPaid(); // for effects like sunburst
}
/* (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 !canceled && (xPaid > 0 || xCanBe0);
}
@Override
protected boolean supportAutoPay() {
return false;
}
@Override
public void showMessage() {
if (isFinished()) { return; }
updateMessage();
}
@Override
protected String getMessage() {
StringBuilder msg = new StringBuilder("Pay X Mana Cost for ");
msg.append(saPaidFor.getHostCard().getName()).append("\n").append(this.xPaid);
msg.append(" Paid so far.");
if (!xCanBe0) {
msg.append(" X Can't be 0.");
}
// Enable just cancel is full X value hasn't been paid for multiple X values
// or X is 0, and x can't be 0
if (!isPaid()) {
ButtonUtil.enableOnlyCancel();
}
else {
ButtonUtil.enableAll();
}
return msg.toString();
}
@Override
protected void onManaAbilityPaid() {
if (this.manaCost.isPaid()) {
this.colorsPaid |= manaCost.getColorsPaid();
this.manaCost = new ManaCostBeingPaid(manaCostPerX);
this.xPaid++;
this.xPaidByColor.add(saPaidFor.getPayingMana().get(0));
}
}
@Override
protected final void onCancel() {
// If you hit cancel, isPaid needs to return false
this.canceled = true;
this.stop();
}
@Override
protected final void onOk() {
done();
this.stop();
}
@Override
protected void done() {
final Card card = saPaidFor.getHostCard();
card.setXManaCostPaid(this.xPaid);
card.setXManaCostPaidByColor(this.xPaidByColor);
card.setColorsPaid(this.colorsPaid);
card.setSunburstValue(ColorSet.fromMask(this.colorsPaid).countColors());
}
}

View File

@@ -1,68 +0,0 @@
package forge.screens.match.input;
import forge.screens.match.FControl;
import forge.screens.match.FControlGamePlayback;
public class InputPlaybackControl extends InputSyncronizedBase implements InputSynchronized {
private static final long serialVersionUID = 7979208993306642072L;
FControlGamePlayback control;
private boolean isPaused = false;
private boolean isFast = false;
/**
* TODO: Write javadoc for Constructor.
* @param fControlGamePlayback
*/
public InputPlaybackControl(FControlGamePlayback fControlGamePlayback) {
control = fControlGamePlayback;
}
/* (non-Javadoc)
* @see forge.gui.input.InputBase#showMessage()
*/
@Override
protected void showMessage() {
setPause(false);
ButtonUtil.enableAll();
}
private void setPause(boolean pause) {
isPaused = pause;
if ( isPaused )
ButtonUtil.setButtonText("Resume", "Step");
else {
ButtonUtil.setButtonText("Pause", isFast ? "1x Speed" : "10x Faster");
showMessage("Press pause to pause game.");
}
}
public void onGamePaused() {
showMessage(getTurnPhasePriorityMessage(FControl.getGame()));
}
@Override
protected void onOk() {
if ( isPaused ) {
control.resume();
setPause(false);
} else {
control.pause();
setPause(true);
}
}
@Override
protected void onCancel() {
if ( isPaused ) {
control.singleStep();
} else {
isFast = !isFast;
control.setSpeed(isFast);
setPause(isPaused); // update message
}
}
}

View File

@@ -1,99 +0,0 @@
package forge.screens.match.input;
import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.toolbox.GuiChoose;
import java.util.*;
import java.util.Map.Entry;
public final class InputProliferate extends InputSelectManyBase<GameEntity> {
private static final long serialVersionUID = -1779224307654698954L;
private Map<GameEntity, CounterType> chosenCounters = new HashMap<GameEntity, CounterType>();
public InputProliferate() {
super(1, Integer.MAX_VALUE);
allowUnselect = true;
}
protected String getMessage() {
StringBuilder sb = new StringBuilder("Choose permanents and/or players with counters on them to add one more counter of that type.");
sb.append("\n\nYou've selected so far:\n");
if (chosenCounters.isEmpty()) {
sb.append("(none)");
}
else {
for (Entry<GameEntity, CounterType> ge : chosenCounters.entrySet()) {
if (ge.getKey() instanceof Player) {
sb.append("* A poison counter to player ").append(ge.getKey()).append("\n");
}
else {
sb.append("* ").append(ge.getKey()).append(" -> ").append(ge.getValue()).append("counter\n");
}
}
}
return sb.toString();
}
@Override
protected void onCardSelected(final Card card, final List<Card> orderedCardOptions) {
if (!card.hasCounters()) {
return;
}
boolean entityWasSelected = chosenCounters.containsKey(card);
if (entityWasSelected) {
this.chosenCounters.remove(card);
}
else {
final List<CounterType> choices = new ArrayList<CounterType>();
for (final CounterType ct : CounterType.values()) {
if (card.getCounters(ct) > 0) {
choices.add(ct);
}
}
CounterType toAdd = choices.size() == 1 ? choices.get(0) : GuiChoose.one("Select counter type", choices);
chosenCounters.put(card, toAdd);
}
refresh();
}
@Override
protected final void onPlayerSelected(Player player) {
if (player.getPoisonCounters() == 0 || player.hasKeyword("You can't get poison counters")) {
return;
}
boolean entityWasSelected = chosenCounters.containsKey(player);
if (entityWasSelected) {
this.chosenCounters.remove(player);
} else
this.chosenCounters.put(player, null /* POISON counter is meant */);
refresh();
}
public Map<GameEntity, CounterType> getProliferationMap() {
return chosenCounters;
}
@Override
protected boolean hasEnoughTargets() { return true; }
@Override
protected boolean hasAllTargets() { return false; }
@Override
public Collection<GameEntity> getSelected() {
// TODO Auto-generated method stub
return chosenCounters.keySet();
}
}

View File

@@ -1,164 +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.screens.match.input;
import forge.FThreads;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.screens.match.FControl;
import forge.toolbox.FOptionPane;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.atomic.AtomicReference;
/**
* <p>
* GuiInput class.
* </p>
*
* @author Forge
* @version $Id: InputProxy.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public class InputProxy implements Observer {
/** The input. */
private AtomicReference<Input> input = new AtomicReference<Input>();
private Game game = null;
// private static final boolean DEBUG_INPUT = true; // false;
public void setGame(Game game0) {
game = game0;
FControl.getInputQueue().addObserver(this);
}
public boolean passPriority() {
Input inp = getInput();
if (inp != null && inp instanceof InputPassPriority) {
inp.selectButtonOK();
return true;
}
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
FOptionPane.showMessageDialog("Cannot pass priority at this time.");
}
});
return false;
}
@Override
public final void update(final Observable observable, final Object obj) {
final Input nextInput = FControl.getInputQueue().getActualInput(game);
/* if(DEBUG_INPUT)
System.out.printf("%s ... \t%s on %s, \tstack = %s%n",
FThreads.debugGetStackTraceItem(6, true), nextInput == null ? "null" : nextInput.getClass().getSimpleName(),
game.getPhaseHandler().debugPrintState(), Singletons.getControl().getInputQueue().printInputStack());
*/
this.input.set(nextInput);
Runnable showMessage = new Runnable() {
@Override public void run() {
Input current = getInput();
FControl.getInputQueue().syncPoint();
//System.out.printf("\t%s > showMessage @ %s/%s during %s%n", FThreads.debugGetCurrThreadId(), nextInput.getClass().getSimpleName(), current.getClass().getSimpleName(), game.getPhaseHandler().debugPrintState());
current.showMessageInitial();
}
};
FThreads.invokeInEdtLater(showMessage);
}
/**
* <p>
* selectButtonOK.
* </p>
*/
public final void selectButtonOK() {
Input inp = getInput();
if (inp != null) {
inp.selectButtonOK();
}
}
/**
* <p>
* selectButtonCancel.
* </p>
*/
public final void selectButtonCancel() {
Input inp = getInput();
if (inp != null) {
inp.selectButtonCancel();
}
}
/**
* <p>
* selectPlayer.
* </p>
*
* @param player
* a {@link forge.game.player.Player} object.
*/
public final void selectPlayer(final Player player) {
Input inp = getInput();
if (inp != null) {
inp.selectPlayer(player);
}
}
/**
* <p>
* selectCard.
* </p>
*
* @param card
* a {@link forge.game.card.Card} object.
* @param orderedCardOptions
*/
public final void selectCard(Card card, List<Card> orderedCardOptions) {
Input inp = getInput();
if (inp != null) {
inp.selectCard(card, orderedCardOptions);
}
}
public final void selectAbility(SpellAbility ab) {
Input inp = getInput();
if (inp != null) {
inp.selectAbility(ab);
}
}
/** {@inheritDoc} */
@Override
public final String toString() {
Input inp = getInput();
return null == inp ? "(null)" : inp.toString();
}
/** @return {@link forge.gui.InputProxy.InputBase} */
private Input getInput() {
return this.input.get();
}
}

View File

@@ -1,99 +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.screens.match.input;
import forge.game.Game;
import java.util.Observable;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
/**
* <p>
* InputControl class.
* </p>
*
* @author Forge
* @version $Id: InputQueue.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public class InputQueue extends Observable {
private final BlockingDeque<InputSynchronized> inputStack = new LinkedBlockingDeque<InputSynchronized>();
private final InputLockUI inputLock;
public InputQueue() {
inputLock = new InputLockUI(this);
}
public final void updateObservers() {
this.setChanged();
this.notifyObservers();
}
public final Input getInput() {
return inputStack.isEmpty() ? null : this.inputStack.peek();
}
public final void removeInput(Input inp) {
Input topMostInput = inputStack.isEmpty() ? null : inputStack.pop();
if (topMostInput != inp) {
throw new RuntimeException("Cannot remove input " + inp.getClass().getSimpleName() + " because it's not on top of stack. Stack = " + inputStack );
}
updateObservers();
}
/**
* <p>
* updateInput.
* </p>
*
* @return a {@link forge.gui.input.InputBase} object.
*/
public final Input getActualInput(Game game) {
Input topMost = inputStack.peek(); // incoming input to Control
if (topMost != null && !game.isGameOver()) {
return topMost;
}
return inputLock;
}
// only for debug purposes
public String printInputStack() {
return inputStack.toString();
}
public void setInput(InputSynchronized input) {
this.inputStack.push(input);
syncPoint();
this.updateObservers();
}
public void syncPoint() {
synchronized (inputLock) {
// acquire and release lock, so that actions from Game thread happen before EDT reads their results
}
}
public void onGameOver(boolean releaseAllInputs) {
for (InputSynchronized inp : inputStack) {
inp.relaseLatchWhenGameIsOver();
if (!releaseAllInputs) {
break;
}
}
}
}

View File

@@ -1,98 +0,0 @@
package forge.screens.match.input;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostShard;
import forge.game.card.Card;
import forge.game.card.CardUtil;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.Player;
import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public final class InputSelectCardsForConvoke extends InputSelectManyBase<Card> {
private static final long serialVersionUID = -1779224307654698954L;
private final Map<Card, ImmutablePair<Byte, ManaCostShard>> chosenCards = new HashMap<Card, ImmutablePair<Byte, ManaCostShard>>();
private final ManaCostBeingPaid remainingCost;
private final Player player;
public InputSelectCardsForConvoke(Player p, ManaCost cost, List<Card> untapped) {
super(1, Math.min(cost.getCMC(), untapped.size()));
remainingCost = new ManaCostBeingPaid(cost);
player = p;
allowUnselect = true;
}
protected String getMessage() {
return "Choose creatures to tap for convoke.\nRemaining mana cost is " + remainingCost.toString();
}
@Override
protected void onCardSelected(final Card card, final List<Card> orderedCardOptions) {
boolean entityWasSelected = chosenCards.containsKey(card);
if (entityWasSelected) {
ImmutablePair<Byte, ManaCostShard> color = this.chosenCards.remove(card);
remainingCost.increaseShard(color.right, 1);
onSelectStateChanged(card, false);
}
else {
byte chosenColor = player.getController().chooseColorAllowColorless("Convoke " + card.toString() + " for which color?", card, CardUtil.getColors(card));
if (remainingCost.getColorlessManaAmount() > 0 && (chosenColor == 0 || !remainingCost.needsColor(chosenColor, player.getManaPool()))) {
registerConvoked(card, ManaCostShard.COLORLESS, chosenColor);
} else {
for (ManaCostShard shard : remainingCost.getDistinctShards()) {
if (shard.canBePaidWithManaOfColor(chosenColor)) {
registerConvoked(card, shard, chosenColor);
return;
}
}
showMessage("The colors provided by " + card.toString() + " you've chosen cannot be used to decrease the manacost of " + remainingCost.toString());
flashIncorrectAction();
}
}
refresh();
}
private void registerConvoked(Card card, ManaCostShard shard, byte chosenColor) {
remainingCost.decreaseShard(shard, 1);
chosenCards.put(card, ImmutablePair.of(chosenColor, shard));
onSelectStateChanged(card, true);
}
@Override
protected final void onPlayerSelected(Player player) {
}
public Map<Card, ManaCostShard> getConvokeMap() {
Map<Card, ManaCostShard> result = new HashMap<Card, ManaCostShard>();
if( !hasCancelled() )
for(Entry<Card, ImmutablePair<Byte, ManaCostShard>> c : chosenCards.entrySet())
result.put(c.getKey(), c.getValue().right);
return result;
}
@Override
protected boolean hasEnoughTargets() { return true; }
@Override
protected boolean hasAllTargets() { return false; }
@Override
public Collection<Card> getSelected() {
// TODO Auto-generated method stub
return chosenCards.keySet();
}
}

View File

@@ -1,21 +0,0 @@
package forge.screens.match.input;
import forge.game.card.Card;
import java.util.Collection;
public class InputSelectCardsFromList extends InputSelectEntitiesFromList<Card> {
private static final long serialVersionUID = 6230360322294805986L;
public InputSelectCardsFromList(int cnt, Collection<Card> validCards) {
super(cnt, cnt, validCards); // to avoid hangs
}
public InputSelectCardsFromList(int min, int max, Collection<Card> validCards) {
super(min, max, validCards); // to avoid hangs
}
public InputSelectCardsFromList(Collection<Card> validCards) {
super(1, 1, validCards); // to avoid hangs
}
}

View File

@@ -1,75 +0,0 @@
package forge.screens.match.input;
import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.player.Player;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSelectManyBase<T> {
private static final long serialVersionUID = -6609493252672573139L;
private final Collection<T> validChoices;
protected final List<T> selected = new ArrayList<T>();
public InputSelectEntitiesFromList(int min, int max, Collection<T> validChoices) {
super(Math.min(min, validChoices.size()), Math.min(max, validChoices.size()));
this.validChoices = validChoices;
if ( min > validChoices.size() )
System.out.println(String.format("Trying to choose at least %d cards from a list with only %d cards!", min, validChoices.size()));
}
@Override
protected void onCardSelected(final Card c, final List<Card> orderedCardOptions) {
if (!selectEntity(c)) {
return;
}
refresh();
}
@Override
protected void onPlayerSelected(final Player p) {
if (!selectEntity(p)) {
return;
}
refresh();
}
public final Collection<T> getSelected() {
return selected;
}
@SuppressWarnings("unchecked")
protected boolean selectEntity(GameEntity c) {
if (!validChoices.contains(c)) {
return false;
}
boolean entityWasSelected = selected.contains(c);
if (entityWasSelected) {
if (!allowUnselect)
return false;
this.selected.remove(c);
}
else {
this.selected.add((T)c);
}
onSelectStateChanged(c, !entityWasSelected);
return true;
}
// might re-define later
protected boolean hasEnoughTargets() { return selected.size() >= min; }
protected boolean hasAllTargets() { return selected.size() >= max; }
protected String getMessage() {
return max == Integer.MAX_VALUE
? String.format(message, selected.size())
: String.format(message, max - selected.size());
}
}

View File

@@ -1,101 +0,0 @@
package forge.screens.match.input;
import com.google.common.collect.Iterables;
import forge.game.GameEntity;
import forge.game.card.Card;
import forge.screens.match.FControl;
import java.util.Collection;
public abstract class InputSelectManyBase<T extends GameEntity> extends InputSyncronizedBase {
private static final long serialVersionUID = -2305549394512889450L;
protected boolean bCancelled = false;
protected final int min;
protected final int max;
protected boolean allowUnselect = false;
protected boolean allowCancel = false;
protected String message = "Source-Card-Name - Select %d more card(s)";
protected InputSelectManyBase(int min, int max) {
if (min > max) {
throw new IllegalArgumentException("Min must not be greater than Max");
}
this.min = min;
this.max = max;
}
protected void refresh() {
if (hasAllTargets()) {
selectButtonOK();
}
else {
this.showMessage();
}
}
protected abstract boolean hasEnoughTargets();
protected abstract boolean hasAllTargets();
protected abstract String getMessage();
@Override
public final void showMessage() {
showMessage(getMessage());
boolean canCancel = allowCancel;
boolean canOk = hasEnoughTargets();
if (canOk && canCancel) { ButtonUtil.enableAll(); }
if (!canOk && canCancel) { ButtonUtil.enableOnlyCancel(); }
if (canOk && !canCancel) { ButtonUtil.enableOnlyOk(); }
if (!canOk && !canCancel) { ButtonUtil.disableAll(); }
}
@Override
protected final void onCancel() {
bCancelled = true;
this.getSelected().clear();
this.stop();
afterStop();
}
public final boolean hasCancelled() {
return bCancelled;
}
public abstract Collection<T> getSelected();
public T getFirstSelected() { return Iterables.getFirst(getSelected(), null); }
@Override
protected final void onOk() {
this.stop();
afterStop();
}
public void setMessage(String message0) {
this.message = message0;
}
protected void onSelectStateChanged(GameEntity c, boolean newState) {
if (c instanceof Card) {
FControl.setUsedToPay((Card)c, newState); // UI supports card highlighting though this abstraction-breaking mechanism
}
}
protected void afterStop() {
for (GameEntity c : getSelected()) {
if (c instanceof Card) {
FControl.setUsedToPay((Card)c, false);
}
}
}
public final boolean isUnselectAllowed() { return allowUnselect; }
public final void setUnselectAllowed(boolean allow) { this.allowUnselect = allow; }
public final void setCancelAllowed(boolean allow) { this.allowCancel = allow ; }
}

View File

@@ -1,265 +0,0 @@
package forge.screens.match.input;
import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.screens.match.FControl;
import forge.toolbox.GuiChoose;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public final class InputSelectTargets extends InputSyncronizedBase {
private final List<Card> choices;
// some cards can be targeted several times (eg: distribute damage as you choose)
private final Map<GameEntity, Integer> targetDepth = new HashMap<GameEntity, Integer>();
private final TargetRestrictions tgt;
private final SpellAbility sa;
private Card lastTarget = null;
private boolean bCancel = false;
private boolean bOk = false;
private final boolean mandatory;
private static final long serialVersionUID = -1091595663541356356L;
public final boolean hasCancelled() { return bCancel; }
public final boolean hasPressedOk() { return bOk; }
/**
* TODO: Write javadoc for Constructor.
* @param select
* @param choices
* @param req
* @param alreadyTargeted
* @param targeted
* @param tgt
* @param sa
* @param mandatory
*/
public InputSelectTargets(List<Card> choices, SpellAbility sa, boolean mandatory) {
this.choices = choices;
this.tgt = sa.getTargetRestrictions();
this.sa = sa;
this.mandatory = mandatory;
}
@Override
public void showMessage() {
final StringBuilder sb = new StringBuilder();
sb.append("Targeted:\n");
for (final Entry<GameEntity, Integer> o : targetDepth.entrySet()) {
sb.append(o.getKey());
if( o.getValue() > 1 )
sb.append(" (").append(o.getValue()).append(" times)");
sb.append("\n");
}
if (!sa.getUniqueTargets().isEmpty()) {
sb.append("Parent Targeted:");
sb.append(sa.getUniqueTargets()).append("\n");
}
sb.append(sa.getHostCard() + " - " + tgt.getVTSelection());
int maxTargets = tgt.getMaxTargets(sa.getHostCard(), sa);
int targeted = sa.getTargets().getNumTargeted();
if(maxTargets > 1)
sb.append("\n(").append(maxTargets - targeted).append(" more can be targeted)");
showMessage(sb.toString());
// If reached Minimum targets, enable OK button
if (!tgt.isMinTargetsChosen(sa.getHostCard(), 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.enableAll();
}
}
}
@Override
protected final void onCancel() {
bCancel = true;
this.done();
}
@Override
protected final void onOk() {
bOk = true;
this.done();
}
@Override
protected final void onCardSelected(final Card card, final List<Card> orderedCardOptions) {
if (!tgt.isUniqueTargets() && targetDepth.containsKey(card)) {
return;
}
// leave this in temporarily, there some seriously wrong things going on here
// Can be targeted doesn't check if the target is a valid type, only if a card is generally "targetable"
if (!card.canBeTargetedBy(sa)) {
showMessage(sa.getHostCard() + " - Cannot target this card (Shroud? Protection? Restrictions).");
return;
}
// If all cards must be from the same zone
if (tgt.isSingleZone() && lastTarget != null && !card.getController().equals(lastTarget.getController())) {
showMessage(sa.getHostCard() + " - Cannot target this card (not in the same zone)");
return;
}
// If all cards must be from different zones
if (tgt.isDifferentZone() && lastTarget != null && !card.getController().equals(lastTarget.getController().getOpponent())) {
showMessage(sa.getHostCard() + " - Cannot target this card (not in different zones)");
return;
}
// If the cards can't share a creature type
if (tgt.isWithoutSameCreatureType() && lastTarget != null && card.sharesCreatureTypeWith(lastTarget)) {
showMessage(sa.getHostCard() + " - Cannot target this card (should not share a creature type)");
return;
}
// If all cards must have different controllers
if (tgt.isDifferentControllers()) {
final List<Player> targetedControllers = new ArrayList<Player>();
for (GameObject o : targetDepth.keySet()) {
if (o instanceof Card) {
Player p = ((Card) o).getController();
targetedControllers.add(p);
}
}
if (targetedControllers.contains(card.getController())) {
showMessage(sa.getHostCard() + " - Cannot target this card (must have different controllers)");
return;
}
}
if (!choices.contains(card)) {
if (card.isPlaneswalker() && sa.getApi() == ApiType.DealDamage) {
showMessage(sa.getHostCard() + " - To deal an opposing Planeswalker direct damage, target its controller and then redirect the damage on resolution.");
} else {
showMessage(sa.getHostCard() + " - The selected card is not a valid choice to be targeted.");
}
return;
}
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 ((sa.getTargets().getNumTargeted() + 1 < tgt.getMaxTargets(sa.getHostCard(), 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);
}
addTarget(card);
} // selectCard()
@Override
protected final void onPlayerSelected(Player player) {
if (!tgt.isUniqueTargets() && targetDepth.containsKey(player)) {
return;
}
if (!sa.canTarget(player)) {
showMessage(sa.getHostCard() + " - Cannot target this player (Hexproof? Protection? Restrictions?).");
return;
}
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 ((sa.getTargets().getNumTargeted() + 1 < tgt.getMaxTargets(sa.getHostCard(), 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);
}
addTarget(player);
}
private void addTarget(GameEntity ge) {
sa.getTargets().add(ge);
if (ge instanceof Card) {
FControl.setUsedToPay((Card) ge, true);
}
Integer val = targetDepth.get(ge);
targetDepth.put(ge, val == null ? Integer.valueOf(1) : Integer.valueOf(val.intValue() + 1) );
if (hasAllTargets()) {
bOk = true;
this.done();
}
else {
this.showMessage();
}
}
private void done() {
for (GameEntity c : targetDepth.keySet()) {
if (c instanceof Card) {
FControl.setUsedToPay((Card)c, false);
}
}
this.stop();
}
private boolean hasAllTargets() {
return tgt.isMaxTargetsChosen(sa.getHostCard(), sa) || ( tgt.getStillToDivide() == 0 && tgt.isDividedAsYouChoose());
}
}

View File

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

View File

@@ -1,52 +0,0 @@
package forge.screens.match.input;
import forge.FThreads;
import forge.error.BugReporter;
import forge.screens.match.FControl;
import java.util.concurrent.CountDownLatch;
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.assertExecutedByEdt(false);
try{
cdlDone.await();
} catch (InterruptedException e) {
BugReporter.reportException(e);
}
}
public final void relaseLatchWhenGameIsOver() {
cdlDone.countDown();
}
public void showAndWait() {
FControl.getInputQueue().setInput(this);
awaitLatchRelease();
}
protected final void stop() {
onStop();
// ensure input won't accept any user actions.
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
setFinished();
}
});
// thread irrelevant
FControl.getInputQueue().removeInput(InputSyncronizedBase.this);
cdlDone.countDown();
}
protected void onStop() { }
}

View File

@@ -24,7 +24,7 @@ public class VAvatar extends FDisplayObject {
@Override
public boolean tap(float x, float y, int count) {
FControl.getInputProxy().selectPlayer(player);
FControl.getInputProxy().selectPlayer(player, null);
return true;
}

View File

@@ -7,7 +7,7 @@ import java.util.Map;
import forge.FThreads;
import forge.game.card.Card;
import forge.screens.match.FControl;
import forge.screens.match.InputSelectCard;
import forge.toolbox.FCardPanel;
public abstract class VCardDisplayArea extends VDisplayArea {
@@ -166,10 +166,28 @@ public abstract class VCardDisplayArea extends VDisplayArea {
return attachedPanels;
}
public enum AttackOption {
DECLARE_AS_ATTACKER("Declare as Attacker"),
REMOVE_FROM_COMBAT("Remove from Combat"),
ATTACK_THIS_DEFENDER("Attack this Defender"),
ACTIVATE_BAND("Activate Band"),
JOIN_BAND("Join Band");
private String text;
private AttackOption(String text0) {
text = text0;
}
public String toString() {
return text;
}
}
@Override
public boolean tap(float x, float y, int count) {
if (displayArea != null) {
FControl.getInputProxy().selectCard(getCard(), new ArrayList<Card>(displayArea.orderedCards)); //copy list to allow it being modified
InputSelectCard.selectCard(getCard(), displayArea.orderedCards);
}
return true;
}

View File

@@ -22,6 +22,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.item.IPaperCard;
import forge.item.PaperCard;
import forge.match.input.InputSelectCardsFromList;
import forge.menu.FDropDownMenu;
import forge.menu.FMenuItem;
import forge.model.FModel;
@@ -31,7 +32,6 @@ import forge.properties.ForgeConstants;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.screens.match.FControl;
import forge.screens.match.input.InputSelectCardsFromList;
import forge.toolbox.FEvent;
import forge.toolbox.GuiChoose;
import forge.toolbox.GuiDialog;

View File

@@ -13,10 +13,10 @@ import forge.assets.FSkinColor.Colors;
import forge.card.MagicColor;
import forge.game.mana.ManaPool;
import forge.game.player.Player;
import forge.match.input.Input;
import forge.match.input.InputPayMana;
import forge.net.FServer;
import forge.screens.match.FControl;
import forge.screens.match.input.Input;
import forge.screens.match.input.InputPayMana;
import forge.toolbox.FDisplayObject;
public class VManaPool extends VDisplayArea {

View File

@@ -262,7 +262,7 @@ public class GuiChoose {
return order("Sideboard", "Main Deck", -1, -1, sideboard, deck, null, true);
}
private static <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax,
public static <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax,
final List<T> sourceChoices, final List<T> destChoices, final Card referenceCard, final boolean sideboardingMode) {
// An input box for handling the order of choices.

View File

@@ -1,28 +1,22 @@
package forge.match.input;
import java.awt.event.MouseEvent;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import java.awt.event.MouseEvent;
/**
* TODO: Write javadoc for this type.
*
*/
public interface Input {
// showMessage() is always the first method called
void showMessageInitial();
void selectCard(Card c, MouseEvent triggerEvent);
void selectAbility(SpellAbility ab);
void selectPlayer(Player player, MouseEvent triggerEven);
void selectPlayer(Player player, MouseEvent triggerEvent);
void selectButtonOK();
void selectButtonCancel();
}