mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git into questMode_wildOpponents
This commit is contained in:
0
forge-gui/src/main/config/create-dmg
Normal file → Executable file
0
forge-gui/src/main/config/create-dmg
Normal file → Executable file
@@ -6,23 +6,21 @@ public class GuiBase {
|
||||
private static IGuiBase guiInterface;
|
||||
private static boolean propertyConfig = true;
|
||||
private static boolean networkplay = false;
|
||||
private static boolean isAndroidport = false;
|
||||
private static boolean interrupted = false;
|
||||
|
||||
public static IGuiBase getInterface() {
|
||||
return guiInterface;
|
||||
}
|
||||
public static void setInterface(IGuiBase i0) {
|
||||
guiInterface = i0;
|
||||
}
|
||||
public static void enablePropertyConfig(boolean value) {
|
||||
propertyConfig = value;
|
||||
}
|
||||
public static boolean isNetworkplay() {
|
||||
return networkplay;
|
||||
}
|
||||
public static void setNetworkplay(boolean value) {
|
||||
networkplay = value;
|
||||
}
|
||||
public static boolean hasPropertyConfig() {
|
||||
return propertyConfig;
|
||||
}
|
||||
public static IGuiBase getInterface() { return guiInterface; }
|
||||
public static void setInterface(IGuiBase i0) { guiInterface = i0; }
|
||||
|
||||
public static void setIsAndroid(boolean value) { isAndroidport = value; }
|
||||
public static boolean isAndroid() { return isAndroidport; }
|
||||
|
||||
public static boolean isNetworkplay() { return networkplay; }
|
||||
public static void setNetworkplay(boolean value) { networkplay = value; }
|
||||
|
||||
public static boolean hasPropertyConfig() { return propertyConfig; }
|
||||
public static void enablePropertyConfig(boolean value) { propertyConfig = value; }
|
||||
|
||||
public static void setInterrupted(boolean value) { interrupted = value; }
|
||||
public static boolean isInterrupted() { return interrupted; }
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.ThreadUtil;
|
||||
import forge.util.XmlUtil;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public abstract class AchievementCollection implements Iterable<Achievement> {
|
||||
protected final Map<String, Achievement> achievements = Maps.newLinkedHashMap();
|
||||
@@ -85,7 +86,7 @@ public abstract class AchievementCollection implements Iterable<Achievement> {
|
||||
}
|
||||
|
||||
protected AchievementCollection(String name0, String filename0, boolean isLimitedFormat0, String path0) {
|
||||
name = name0;
|
||||
name = Localizer.getInstance().getMessage(name0);
|
||||
filename = filename0;
|
||||
isLimitedFormat = isLimitedFormat0;
|
||||
path = path0;
|
||||
|
||||
@@ -7,12 +7,14 @@ import forge.game.player.Player;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
public class AltWinAchievements extends AchievementCollection {
|
||||
public static final AltWinAchievements instance = new AltWinAchievements();
|
||||
|
||||
private AltWinAchievements() {
|
||||
super("Alternate Win Conditions", ForgeConstants.ACHIEVEMENTS_DIR + "altwin.xml", false, ForgeConstants.ALTWIN_ACHIEVEMENT_LIST_FILE);
|
||||
super("lblAlternateWinConditions", ForgeConstants.ACHIEVEMENTS_DIR + "altwin.xml", false, ForgeConstants.ALTWIN_ACHIEVEMENT_LIST_FILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,6 +42,9 @@ public class AltWinAchievements extends AchievementCollection {
|
||||
}
|
||||
|
||||
Achievement achievement = achievements.get(altWinCondition);
|
||||
if (achievement == null) {
|
||||
achievement = achievements.get("Emblem - " + altWinCondition); // indirectly winning through an emblem
|
||||
}
|
||||
if (achievement != null) {
|
||||
achievement.update(player);
|
||||
save();
|
||||
@@ -49,7 +54,7 @@ public class AltWinAchievements extends AchievementCollection {
|
||||
|
||||
private class AltWinAchievement extends ProgressiveAchievement {
|
||||
private AltWinAchievement(String cardName0, String displayName0, String flavorText0) {
|
||||
super(cardName0, displayName0, "Win a game with " + cardName0, flavorText0);
|
||||
super(CardTranslation.getTranslatedName(cardName0), displayName0, Localizer.getInstance().getMessage("lblWinGameWithCard", CardTranslation.getTranslatedName(cardName0)), flavorText0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,7 +69,7 @@ public class AltWinAchievements extends AchievementCollection {
|
||||
|
||||
@Override
|
||||
public String getNoun() {
|
||||
return "Win";
|
||||
return Localizer.getInstance().getMessage("lblWin");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public class ChallengeAchievements extends AchievementCollection {
|
||||
public static final ChallengeAchievements instance = new ChallengeAchievements();
|
||||
|
||||
private ChallengeAchievements() {
|
||||
super("Challenges", ForgeConstants.ACHIEVEMENTS_DIR + "challenges.xml", false);
|
||||
super("lblChallenges", ForgeConstants.ACHIEVEMENTS_DIR + "challenges.xml", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,7 +5,7 @@ import forge.properties.ForgeConstants;
|
||||
|
||||
public class ConstructedAchievements extends AchievementCollection {
|
||||
public ConstructedAchievements() {
|
||||
super("Constructed", ForgeConstants.ACHIEVEMENTS_DIR + "constructed.xml", false);
|
||||
super("lblConstructed", ForgeConstants.ACHIEVEMENTS_DIR + "constructed.xml", false);
|
||||
}
|
||||
|
||||
//add achievements that should appear at the bottom below core achievements for each game mode
|
||||
|
||||
@@ -4,7 +4,7 @@ import forge.properties.ForgeConstants;
|
||||
|
||||
public class DraftAchievements extends AchievementCollection {
|
||||
public DraftAchievements() {
|
||||
super("Booster Draft", ForgeConstants.ACHIEVEMENTS_DIR + "draft.xml", true);
|
||||
super("lblBoosterDraft", ForgeConstants.ACHIEVEMENTS_DIR + "draft.xml", true);
|
||||
}
|
||||
|
||||
//add achievements that should appear at the bottom below core achievements for each game mode
|
||||
|
||||
@@ -5,7 +5,7 @@ import forge.properties.ForgeConstants;
|
||||
|
||||
public class PlanarConquestAchievements extends AchievementCollection {
|
||||
public PlanarConquestAchievements() {
|
||||
super("Planar Conquest", ForgeConstants.ACHIEVEMENTS_DIR + "planar_conquest.xml", true);
|
||||
super("lblPlanarConquest", ForgeConstants.ACHIEVEMENTS_DIR + "planar_conquest.xml", true);
|
||||
}
|
||||
|
||||
//add achievements that should appear at the bottom below core achievements for each game mode
|
||||
|
||||
@@ -19,7 +19,7 @@ public class PlaneswalkerAchievements extends AchievementCollection {
|
||||
}
|
||||
|
||||
private PlaneswalkerAchievements() {
|
||||
super("Planeswalker Ultimates", ForgeConstants.ACHIEVEMENTS_DIR + "planeswalkers.xml", false, ForgeConstants.PLANESWALKER_ACHIEVEMENT_LIST_FILE);
|
||||
super("lblPlaneswalkerUltimates", ForgeConstants.ACHIEVEMENTS_DIR + "planeswalkers.xml", false, ForgeConstants.PLANESWALKER_ACHIEVEMENT_LIST_FILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,7 +4,7 @@ import forge.properties.ForgeConstants;
|
||||
|
||||
public class PuzzleAchievements extends AchievementCollection {
|
||||
public PuzzleAchievements() {
|
||||
super("Puzzle Mode", ForgeConstants.ACHIEVEMENTS_DIR + "puzzle.xml", false);
|
||||
super("lblPuzzleMode", ForgeConstants.ACHIEVEMENTS_DIR + "puzzle.xml", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,7 +4,7 @@ import forge.properties.ForgeConstants;
|
||||
|
||||
public class QuestAchievements extends AchievementCollection {
|
||||
public QuestAchievements() {
|
||||
super("Quest Mode", ForgeConstants.ACHIEVEMENTS_DIR + "quest.xml", false);
|
||||
super("lblQuestMode", ForgeConstants.ACHIEVEMENTS_DIR + "quest.xml", false);
|
||||
}
|
||||
|
||||
//add achievements that should appear at the bottom below core achievements for each game mode
|
||||
|
||||
@@ -4,7 +4,7 @@ import forge.properties.ForgeConstants;
|
||||
|
||||
public class SealedAchievements extends AchievementCollection {
|
||||
public SealedAchievements() {
|
||||
super("Sealed Deck", ForgeConstants.ACHIEVEMENTS_DIR + "sealed.xml", true);
|
||||
super("lblSealedDeck", ForgeConstants.ACHIEVEMENTS_DIR + "sealed.xml", true);
|
||||
}
|
||||
|
||||
//add achievements that should appear at the bottom below core achievements for each game mode
|
||||
|
||||
@@ -397,6 +397,7 @@ public enum FSkinProp {
|
||||
IMG_ABILITY_DOUBLE_STRIKE (new int[] {166, 2, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_FIRST_STRIKE (new int[] {248, 2, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_FEAR (new int[] {84, 412, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_FLASH (new int[] {166, 576, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_FLYING (new int[] {330, 2, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_HASTE (new int[] {412, 494, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_HEXPROOF (new int[] {412, 2, 80, 80}, PropType.ABILITY),
|
||||
|
||||
@@ -17,7 +17,6 @@ import forge.game.card.CardView;
|
||||
import forge.game.event.*;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.interfaces.IGuiGame;
|
||||
@@ -239,17 +238,20 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventSpellAbilityCast event) {
|
||||
needStackUpdate = true;
|
||||
processEvent();
|
||||
|
||||
final Runnable notifyStackAddition = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
matchController.notifyStackAddition(event);
|
||||
}
|
||||
};
|
||||
GuiBase.getInterface().invokeInEdtLater(notifyStackAddition);
|
||||
needStackUpdate = true;
|
||||
if(GuiBase.getInterface().isLibgdxPort()) {
|
||||
return processEvent(); //mobile port don't have notify stack addition like the desktop
|
||||
} else {
|
||||
processEvent();
|
||||
|
||||
final Runnable notifyStackAddition = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
matchController.notifyStackAddition(event);
|
||||
}
|
||||
};
|
||||
GuiBase.getInterface().invokeInEdtLater(notifyStackAddition);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -262,16 +264,19 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
@Override
|
||||
public Void visit(final GameEventSpellRemovedFromStack event) {
|
||||
needStackUpdate = true;
|
||||
processEvent();
|
||||
|
||||
final Runnable notifyStackAddition = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
matchController.notifyStackRemoval(event);
|
||||
}
|
||||
};
|
||||
GuiBase.getInterface().invokeInEdtLater(notifyStackAddition);
|
||||
|
||||
if(GuiBase.getInterface().isLibgdxPort()) {
|
||||
return processEvent(); //mobile port don't have notify stack addition like the desktop
|
||||
} else {
|
||||
processEvent();
|
||||
|
||||
final Runnable notifyStackAddition = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
matchController.notifyStackRemoval(event);
|
||||
}
|
||||
};
|
||||
GuiBase.getInterface().invokeInEdtLater(notifyStackAddition);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -288,7 +293,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
@Override
|
||||
public Void visit(final GameEventCardAttachment event) {
|
||||
final Game game = event.equipment.getGame();
|
||||
final PlayerZone zEq = (PlayerZone)game.getZoneOf(event.equipment);
|
||||
final Zone zEq = (Zone)game.getZoneOf(event.equipment);
|
||||
if (event.oldEntiy instanceof Card) {
|
||||
updateZone(game.getZoneOf((Card)event.oldEntiy));
|
||||
}
|
||||
@@ -356,19 +361,33 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventCardChangeZone event) {
|
||||
if(event.to.getZoneType() == ZoneType.Battlefield)
|
||||
refreshFieldUpdate = true;
|
||||
//pfps the change to the zones have already been performed with add and remove calls
|
||||
// this is only for playing a sound
|
||||
// updateZone(event.from);
|
||||
//return updateZone(event.to);
|
||||
return processEvent();
|
||||
public Void visit(final GameEventCombatUpdate event) {
|
||||
if (!GuiBase.isNetworkplay())
|
||||
return null; //not needed if single player only...
|
||||
|
||||
final CardCollection cards = new CardCollection();
|
||||
cards.addAll(event.attackers);
|
||||
cards.addAll(event.blockers);
|
||||
|
||||
refreshFieldUpdate = true;
|
||||
|
||||
processCards(cards, cardsRefreshDetails);
|
||||
return processCards(cards, cardsUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventCardChangeZone event) {
|
||||
if(GuiBase.getInterface().isLibgdxPort()) {
|
||||
updateZone(event.from);
|
||||
return updateZone(event.to);
|
||||
} else {
|
||||
return processEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventCardStatsChanged event) {
|
||||
refreshFieldUpdate = true;
|
||||
processCards(event.cards, cardsRefreshDetails);
|
||||
return processCards(event.cards, cardsUpdate);
|
||||
}
|
||||
@@ -393,16 +412,18 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventTokenStateUpdate event) {
|
||||
refreshFieldUpdate = true;
|
||||
processCards(event.cards, cardsRefreshDetails);
|
||||
return processCards(event.cards, cardsUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventShuffle event) {
|
||||
//pfps the change to the library has already been performed by a setCards call
|
||||
// this is only for playing a sound
|
||||
// return updateZone(event.player.getZone(ZoneType.Library));
|
||||
return processEvent();
|
||||
if (GuiBase.getInterface().isLibgdxPort()) {
|
||||
return updateZone(event.player.getZone(ZoneType.Library));
|
||||
} else {
|
||||
return processEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.interfaces.IComboBox;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import forge.util.gui.SOptionPane;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class DeckImportController {
|
||||
private final boolean replacingDeck;
|
||||
@@ -62,11 +63,12 @@ public class DeckImportController {
|
||||
}
|
||||
|
||||
public Deck accept() {
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
if (tokens.isEmpty()) { return null; }
|
||||
|
||||
if (replacingDeck) {
|
||||
final String warning = "This will replace the contents of your current deck with these cards.\n\nProceed?";
|
||||
if (!SOptionPane.showConfirmDialog(warning, "Replace Current Deck", "Replace", "Cancel")) {
|
||||
final String warning = localizer.getMessage("lblReplaceCurrentDeckConfirm");
|
||||
if (!SOptionPane.showConfirmDialog(warning, localizer.getMessage("lblReplaceCurrentDeck"), localizer.getMessage("lblReplace"), localizer.getMessage("lblCancel"))) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import forge.util.storage.StorageBase;
|
||||
|
||||
public class NetDeckCategory extends StorageBase<Deck> {
|
||||
public static final String PREFIX = "NET_DECK_";
|
||||
private static Map<String, NetDeckCategory> constructed, commander;
|
||||
private static Map<String, NetDeckCategory> constructed, commander, brawl;
|
||||
|
||||
private static Map<String, NetDeckCategory> loadCategories(String filename) {
|
||||
Map<String, NetDeckCategory> categories = new TreeMap<>();
|
||||
@@ -58,6 +58,12 @@ public class NetDeckCategory extends StorageBase<Deck> {
|
||||
}
|
||||
categories = commander;
|
||||
break;
|
||||
case Brawl:
|
||||
if (brawl == null) {
|
||||
brawl = loadCategories(ForgeConstants.NET_DECKS_BRAWL_LIST_FILE);
|
||||
}
|
||||
categories = brawl;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.model.FModel;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.util.BuildInfo;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.WaitCallback;
|
||||
import forge.util.gui.SOptionPane;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -28,6 +29,7 @@ public class AutoUpdater {
|
||||
private final String RELEASE_MAVEN_METADATA = "https://releases.cardforge.org/forge/forge-gui-desktop/maven-metadata.xml";
|
||||
private static final boolean VERSION_FROM_METADATA = true;
|
||||
private static final String TMP_DIR = "tmp/";
|
||||
private static final Localizer localizer = Localizer.getInstance();
|
||||
|
||||
public static String[] updateChannels = new String[]{ "none", "snapshot", "release"};
|
||||
|
||||
@@ -74,9 +76,9 @@ public class AutoUpdater {
|
||||
// TODO This doesn't work yet, because FSkin isn't loaded at the time.
|
||||
return false;
|
||||
} else if (updateChannel.equals("none")) {
|
||||
String message = "You haven't set an update channel. Do you want to check a channel now?";
|
||||
String message = localizer.getMessage("lblYouHaventSetUpdateChannel");
|
||||
List<String> options = ImmutableList.of("Cancel", "release", "snapshot");
|
||||
int option = SOptionPane.showOptionDialog(message, "Manual Check", null, options, 0);
|
||||
int option = SOptionPane.showOptionDialog(message, localizer.getMessage("lblManualCheck"), null, options, 0);
|
||||
if (option == 0) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -180,12 +182,9 @@ public class AutoUpdater {
|
||||
return downloadFromBrowser();
|
||||
}
|
||||
|
||||
String message = "A new version of Forge is available (" + version + ").\n" +
|
||||
"You are currently on version (" + buildVersion + ").\n\n" +
|
||||
"Would you like to update to the new version now?";
|
||||
|
||||
final List<String> options = ImmutableList.of("Update Now", "Update Later");
|
||||
if (SOptionPane.showOptionDialog(message, "New Version Available", null, options, 0) == 0) {
|
||||
String message = localizer.getMessage("lblNewVersionForgeAvailableUpdateConfirm", version, buildVersion);
|
||||
final List<String> options = ImmutableList.of(localizer.getMessage("lblUpdateNow"), localizer.getMessage("lblUpdateLater"));
|
||||
if (SOptionPane.showOptionDialog(message, localizer.getMessage("lblNewVersionAvailable"), null, options, 0) == 0) {
|
||||
return downloadFromForge();
|
||||
}
|
||||
|
||||
@@ -208,7 +207,7 @@ public class AutoUpdater {
|
||||
WaitCallback<Boolean> callback = new WaitCallback<Boolean>() {
|
||||
@Override
|
||||
public void run() {
|
||||
GuiBase.getInterface().download(new GuiDownloadZipService("Auto Updater", "Download the new version..", packageUrl, "tmp/", null, null) {
|
||||
GuiBase.getInterface().download(new GuiDownloadZipService("Auto Updater", localizer.getMessage("lblNewVersionDownloading"), packageUrl, "tmp/", null, null) {
|
||||
@Override
|
||||
public void downloadAndUnzip() {
|
||||
packagePath = download(version + "-upgrade.tar.bz2");
|
||||
@@ -240,7 +239,7 @@ public class AutoUpdater {
|
||||
}
|
||||
|
||||
private void restartForge() {
|
||||
if (isLoading || SOptionPane.showConfirmDialog("Forge has been downloaded. You should extract the package and restart Forge for the new version.", "Exit now?")) {
|
||||
if (isLoading || SOptionPane.showConfirmDialog(localizer.getMessage("lblForgeHasBeenUpdateRestartForgeToUseNewVersion"), localizer.getMessage("lblExitNowConfirm"))) {
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
@@ -257,6 +258,7 @@ public abstract class GuiDownloadService implements Runnable {
|
||||
for (Entry<String, String> kv : files.entrySet()) {
|
||||
boolean isJPG = true;
|
||||
boolean isLogged = false;
|
||||
boolean fullborder = false;
|
||||
if (cancel) {//stop prevent sleep
|
||||
GuiBase.getInterface().preventSystemSleep(false);
|
||||
break; }
|
||||
@@ -266,7 +268,7 @@ public abstract class GuiDownloadService implements Runnable {
|
||||
String url = kv.getValue();
|
||||
|
||||
String decodedKey = decodeURL(kv.getKey());
|
||||
final File fileDest = new File(decodedKey);
|
||||
File fileDest = new File(decodedKey);
|
||||
final String filePath = fileDest.getPath();
|
||||
final String subLastIndex = filePath.contains("pics") ? "\\pics\\" : "\\db\\";
|
||||
|
||||
@@ -287,10 +289,23 @@ public abstract class GuiDownloadService implements Runnable {
|
||||
conn.setInstanceFollowRedirects(false);
|
||||
}
|
||||
conn.connect();
|
||||
|
||||
|
||||
//if .full file is not found try fullborder
|
||||
if ((conn.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) && (url.contains(".full.jpg")))
|
||||
{
|
||||
fullborder = true;
|
||||
conn.disconnect();
|
||||
url = TextUtil.fastReplace(url, ".full.jpg", ".fullborder.jpg");
|
||||
imageUrl = new URL(url);
|
||||
conn = (HttpURLConnection) imageUrl.openConnection(p);
|
||||
conn.setInstanceFollowRedirects(false);
|
||||
conn.connect();
|
||||
}
|
||||
|
||||
// if file is not found and this is a JPG, give PNG a shot...
|
||||
if ((conn.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) && (url.endsWith(".jpg")))
|
||||
{
|
||||
fullborder = false;
|
||||
isJPG = false;
|
||||
conn.disconnect();
|
||||
if(url.contains("/images/")){
|
||||
@@ -298,12 +313,16 @@ public abstract class GuiDownloadService implements Runnable {
|
||||
System.out.println("File not found: .." + url.substring(url.lastIndexOf("/images/")+1));
|
||||
}
|
||||
url = url.substring(0,url.length() - 4) + ".png";
|
||||
imageUrl = new URL(url);
|
||||
imageUrl = new URL(TextUtil.fastReplace(url, ".fullborder.", ".full."));
|
||||
conn = (HttpURLConnection) imageUrl.openConnection(p);
|
||||
conn.setInstanceFollowRedirects(false);
|
||||
conn.connect();
|
||||
}
|
||||
|
||||
|
||||
if (fullborder) {
|
||||
fileDest = new File(TextUtil.fastReplace(decodedKey, ".full.jpg", ".fullborder.jpg"));
|
||||
}
|
||||
|
||||
switch (conn.getResponseCode()) {
|
||||
case HttpURLConnection.HTTP_OK:
|
||||
fos = new FileOutputStream(fileDest);
|
||||
|
||||
@@ -62,7 +62,7 @@ public interface IGuiGame {
|
||||
void updateLives(Iterable<PlayerView> livesUpdate);
|
||||
void setPanelSelection(CardView hostCard);
|
||||
SpellAbilityView getAbilityToPlay(CardView hostCard, List<SpellAbilityView> abilities, ITriggerEvent triggerEvent);
|
||||
Map<CardView, Integer> assignDamage(CardView attacker, List<CardView> blockers, int damage, GameEntityView defender, boolean overrideOrder);
|
||||
Map<CardView, Integer> assignCombatDamage(CardView attacker, List<CardView> blockers, int damage, GameEntityView defender, boolean overrideOrder);
|
||||
|
||||
void message(String message);
|
||||
void message(String message, String title);
|
||||
|
||||
@@ -26,10 +26,12 @@ import forge.game.keyword.Keyword;
|
||||
import forge.interfaces.IButton;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.item.SealedProduct;
|
||||
import forge.model.FModel;
|
||||
import forge.planarconquest.ConquestCommander;
|
||||
import forge.planarconquest.ConquestPlane;
|
||||
import forge.planarconquest.ConquestRegion;
|
||||
import forge.quest.QuestSpellShop;
|
||||
import forge.quest.QuestWorld;
|
||||
import forge.util.gui.SGuiChoose;
|
||||
import forge.util.gui.SOptionPane;
|
||||
@@ -215,6 +217,257 @@ public class AdvancedSearch {
|
||||
return cards.get(0) == input;
|
||||
}
|
||||
}),
|
||||
INVITEM_NAME("lblName", InventoryItem.class, FilterOperator.STRING_OPS, new StringEvaluator<InventoryItem>() {
|
||||
@Override
|
||||
protected String getItemValue(InventoryItem input) {
|
||||
return input.getName();
|
||||
}
|
||||
}),
|
||||
INVITEM_RULES_TEXT("lblRulesText", InventoryItem.class, FilterOperator.STRING_OPS, new StringEvaluator<InventoryItem>() {
|
||||
@Override
|
||||
protected String getItemValue(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return "";
|
||||
}
|
||||
return ((PaperCard)input).getRules().getOracleText();
|
||||
}
|
||||
}),
|
||||
INVITEM_KEYWORDS("lblKeywords", InventoryItem.class, FilterOperator.COLLECTION_OPS, new CustomListEvaluator<InventoryItem, Keyword>(Keyword.getAllKeywords()) {
|
||||
@Override
|
||||
protected Keyword getItemValue(InventoryItem input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<Keyword> getItemValues(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return Keyword.getKeywordSet((PaperCard)input);
|
||||
}
|
||||
}),
|
||||
INVITEM_SET("lblSet", InventoryItem.class, FilterOperator.SINGLE_LIST_OPS, new CustomListEvaluator<InventoryItem, CardEdition>(FModel.getMagicDb().getSortedEditions(), CardEdition.FN_GET_CODE) {
|
||||
@Override
|
||||
protected CardEdition getItemValue(InventoryItem input) {
|
||||
if (input instanceof PaperCard) {
|
||||
return FModel.getMagicDb().getEditions().get(((PaperCard)input).getEdition());
|
||||
} else if (input instanceof SealedProduct) {
|
||||
return FModel.getMagicDb().getEditions().get(((SealedProduct)input).getEdition());
|
||||
} else {
|
||||
return CardEdition.UNKNOWN;
|
||||
}
|
||||
}
|
||||
}),
|
||||
INVITEM_FORMAT("lblFormat", InventoryItem.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<InventoryItem, GameFormat>((List<GameFormat>)FModel.getFormats().getFilterList()) {
|
||||
@Override
|
||||
protected GameFormat getItemValue(InventoryItem input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<GameFormat> getItemValues(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return FModel.getFormats().getAllFormatsOfCard((PaperCard)input);
|
||||
}
|
||||
}),
|
||||
INVITEM_PLANE("lblPlane", InventoryItem.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<InventoryItem, ConquestPlane>(ImmutableList.copyOf(FModel.getPlanes())) {
|
||||
@Override
|
||||
protected ConquestPlane getItemValue(InventoryItem input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<ConquestPlane> getItemValues(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return ConquestPlane.getAllPlanesOfCard((PaperCard)input);
|
||||
}
|
||||
}),
|
||||
INVITEM_REGION("lblRegion", InventoryItem.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<InventoryItem, ConquestRegion>(ConquestRegion.getAllRegions()) {
|
||||
@Override
|
||||
protected ConquestRegion getItemValue(InventoryItem input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<ConquestRegion> getItemValues(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return ConquestRegion.getAllRegionsOfCard((PaperCard)input);
|
||||
}
|
||||
}),
|
||||
INVITEM_QUEST_WORLD("lblQuestWorld", InventoryItem.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<InventoryItem, QuestWorld>(ImmutableList.copyOf(FModel.getWorlds())) {
|
||||
@Override
|
||||
protected QuestWorld getItemValue(InventoryItem input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<QuestWorld> getItemValues(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return QuestWorld.getAllQuestWorldsOfCard(((PaperCard)input));
|
||||
}
|
||||
}),
|
||||
INVITEM_COLOR("lblColor", InventoryItem.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<InventoryItem>() {
|
||||
@Override
|
||||
protected MagicColor.Color getItemValue(InventoryItem input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<MagicColor.Color> getItemValues(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return ((PaperCard)input).getRules().getColor().toEnumSet();
|
||||
}
|
||||
}),
|
||||
INVITEM_COLOR_IDENTITY("lblColorIdentity", InventoryItem.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<InventoryItem>() {
|
||||
@Override
|
||||
protected MagicColor.Color getItemValue(InventoryItem input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<MagicColor.Color> getItemValues(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return ((PaperCard)input).getRules().getColorIdentity().toEnumSet();
|
||||
}
|
||||
}),
|
||||
INVITEM_COLOR_COUNT("lblColorCount", InventoryItem.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<InventoryItem>(0, 5) {
|
||||
@Override
|
||||
protected Integer getItemValue(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return 0;
|
||||
}
|
||||
return ((PaperCard)input).getRules().getColor().countColors();
|
||||
}
|
||||
}),
|
||||
INVITEM_TYPE("lblType", InventoryItem.class, FilterOperator.COMBINATION_OPS, new CustomListEvaluator<InventoryItem, String>(CardType.getCombinedSuperAndCoreTypes()) {
|
||||
@Override
|
||||
protected String getItemValue(InventoryItem input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<String> getItemValues(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
final CardType type = ((PaperCard)input).getRules().getType();
|
||||
final Set<String> types = new HashSet<>();
|
||||
for (Supertype t : type.getSupertypes()) {
|
||||
types.add(t.name());
|
||||
}
|
||||
for (CoreType t : type.getCoreTypes()) {
|
||||
types.add(t.name());
|
||||
}
|
||||
return types;
|
||||
}
|
||||
}),
|
||||
INVITEM_SUB_TYPE("lblSubtype", InventoryItem.class, FilterOperator.COMBINATION_OPS, new CustomListEvaluator<InventoryItem, String>(CardType.getSortedSubTypes()) {
|
||||
@Override
|
||||
protected String getItemValue(InventoryItem input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<String> getItemValues(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return (Set<String>)((PaperCard)input).getRules().getType().getSubtypes();
|
||||
}
|
||||
}),
|
||||
INVITEM_CMC("lblCMC", InventoryItem.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<InventoryItem>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return 0;
|
||||
}
|
||||
return ((PaperCard)input).getRules().getManaCost().getCMC();
|
||||
}
|
||||
}),
|
||||
INVITEM_GENERIC_COST("lblGenericCost", InventoryItem.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<InventoryItem>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return 0;
|
||||
}
|
||||
return ((PaperCard)input).getRules().getManaCost().getGenericCost();
|
||||
}
|
||||
}),
|
||||
INVITEM_POWER("lblPower", InventoryItem.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<InventoryItem>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return null;
|
||||
}
|
||||
CardRules rules = ((PaperCard)input).getRules();
|
||||
if (rules.getType().isCreature()) {
|
||||
return rules.getIntPower();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
INVITEM_TOUGHNESS("lblToughness", InventoryItem.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<InventoryItem>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return null;
|
||||
}
|
||||
CardRules rules = ((PaperCard)input).getRules();
|
||||
if (rules.getType().isCreature()) {
|
||||
return rules.getIntToughness();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
INVITEM_MANA_COST("lblManaCost", InventoryItem.class, FilterOperator.STRING_OPS, new StringEvaluator<InventoryItem>() {
|
||||
@Override
|
||||
protected String getItemValue(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return "";
|
||||
}
|
||||
return ((PaperCard)input).getRules().getManaCost().toString();
|
||||
}
|
||||
}),
|
||||
INVITEM_FIRST_PRINTING("lblFirstPrinting", InventoryItem.class, FilterOperator.BOOLEAN_OPS, new BooleanEvaluator<InventoryItem>() {
|
||||
@Override
|
||||
protected Boolean getItemValue(InventoryItem input) {
|
||||
List<PaperCard> cards = FModel.getMagicDb().getCommonCards().getAllCards(input.getName());
|
||||
if (cards.size() <= 1) { return true; }
|
||||
|
||||
Collections.sort(cards, FModel.getMagicDb().getEditions().CARD_EDITION_COMPARATOR);
|
||||
return cards.get(0) == input;
|
||||
}
|
||||
}),
|
||||
INVITEM_RARITY("lblRarity", InventoryItem.class, FilterOperator.SINGLE_LIST_OPS, new CustomListEvaluator<InventoryItem, CardRarity>(Arrays.asList(CardRarity.FILTER_OPTIONS), CardRarity.FN_GET_LONG_NAME, CardRarity.FN_GET_LONG_NAME) {
|
||||
@Override
|
||||
protected CardRarity getItemValue(InventoryItem input) {
|
||||
if (!(input instanceof PaperCard)) {
|
||||
return CardRarity.Special;
|
||||
}
|
||||
return ((PaperCard)input).getRarity();
|
||||
}
|
||||
}),
|
||||
INVITEM_BUY_PRICE("lblBuyPrice", InventoryItem.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<InventoryItem>(0, 10000000) {
|
||||
@Override
|
||||
protected Integer getItemValue(InventoryItem input) {
|
||||
return (Integer) QuestSpellShop.fnPriceGet.apply(new AbstractMap.SimpleEntry<InventoryItem, Integer>(input, 1));
|
||||
}
|
||||
}),
|
||||
INVITEM_SELL_PRICE("lblSellPrice", InventoryItem.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<InventoryItem>(0, 10000000) {
|
||||
@Override
|
||||
protected Integer getItemValue(InventoryItem input) {
|
||||
return (Integer) QuestSpellShop.fnPriceSellGet.apply(new AbstractMap.SimpleEntry<InventoryItem, Integer>(input, 1));
|
||||
}
|
||||
}),
|
||||
INVITEM_USED_IN_QUEST_DECKS("lblUsedInQuestDecks", InventoryItem.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<InventoryItem>(0, 100) {
|
||||
@Override
|
||||
protected Integer getItemValue(InventoryItem input) {
|
||||
return (Integer) Integer.parseInt((String) QuestSpellShop.fnDeckGet.apply(new AbstractMap.SimpleEntry<InventoryItem, Integer>(input, 1)));
|
||||
}
|
||||
}),
|
||||
DECK_NAME("lblName", DeckProxy.class, FilterOperator.STRING_OPS, new StringEvaluator<DeckProxy>() {
|
||||
@Override
|
||||
protected String getItemValue(DeckProxy input) {
|
||||
|
||||
@@ -15,9 +15,10 @@ import forge.deck.DeckProxy;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public enum GroupDef {
|
||||
COLOR("Color", getColorGroups(),
|
||||
COLOR("lblColor", getColorGroups(),
|
||||
new Function<Integer, ColumnDef>() {
|
||||
@Override
|
||||
public ColumnDef apply(final Integer groupIndex) {
|
||||
@@ -36,7 +37,7 @@ public enum GroupDef {
|
||||
return -1;
|
||||
}
|
||||
}),
|
||||
COLOR_IDENTITY("Color Identity", getColorGroups(),
|
||||
COLOR_IDENTITY("lblColorIdentity", getColorGroups(),
|
||||
new Function<Integer, ColumnDef>() {
|
||||
@Override
|
||||
public ColumnDef apply(final Integer groupIndex) {
|
||||
@@ -55,7 +56,7 @@ public enum GroupDef {
|
||||
return -1;
|
||||
}
|
||||
}),
|
||||
SET("Set", getSetGroups(),
|
||||
SET("lblSet", getSetGroups(),
|
||||
new Function<Integer, ColumnDef>() {
|
||||
@Override
|
||||
public ColumnDef apply(final Integer groupIndex) {
|
||||
@@ -74,7 +75,7 @@ public enum GroupDef {
|
||||
return -1;
|
||||
}
|
||||
}),
|
||||
DEFAULT("Default",
|
||||
DEFAULT("lblDefault",
|
||||
new String[] { "Creatures", "Spells", "Lands" },
|
||||
new Function<Integer, ColumnDef>() {
|
||||
@Override
|
||||
@@ -104,7 +105,7 @@ public enum GroupDef {
|
||||
}
|
||||
}),
|
||||
|
||||
CARD_TYPE("Type",
|
||||
CARD_TYPE("lblType",
|
||||
new String[] { "Planeswalker", "Creature", "Sorcery", "Instant", "Artifact", "Enchantment", "Land", "Tribal instant" },
|
||||
new Function<Integer, ColumnDef>() {
|
||||
@Override
|
||||
@@ -148,7 +149,7 @@ public enum GroupDef {
|
||||
return -1;
|
||||
}
|
||||
}),
|
||||
PW_DECK_SORT("Planeswalker Deck Sort",
|
||||
PW_DECK_SORT("lblPlaneswalkerDeckSort",
|
||||
new String[] { "Planeswalker", "Rares", "Creature", "Land", "Other Spells" },
|
||||
new Function<Integer, ColumnDef>() {
|
||||
@Override
|
||||
@@ -178,7 +179,7 @@ public enum GroupDef {
|
||||
return -1;
|
||||
}
|
||||
}),
|
||||
CARD_RARITY("Rarity",
|
||||
CARD_RARITY("lblRarity",
|
||||
new String[] { "Mythic Rares", "Rares", "Uncommons", "Commons", "Basic Lands" },
|
||||
new Function<Integer, ColumnDef>() {
|
||||
@Override
|
||||
@@ -210,7 +211,7 @@ public enum GroupDef {
|
||||
});
|
||||
|
||||
GroupDef(String name0, String[] groups0, Function<Integer, ColumnDef> fnGetPileByOverride0, Function<InventoryItem, Integer> fnGroupItem0) {
|
||||
this.name = name0;
|
||||
this.name = Localizer.getInstance().getMessage(name0);
|
||||
this.groups = groups0;
|
||||
this.fnGetPileByOverride = fnGetPileByOverride0;
|
||||
this.fnGroupItem = fnGroupItem0;
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import forge.item.InventoryItem;
|
||||
@@ -46,11 +47,11 @@ public final class ItemManagerModel<T extends InventoryItem> {
|
||||
|
||||
// same thing as above, it was copied to provide sorting (needed by table
|
||||
// views in deck editors)
|
||||
private final transient List<Entry<T, Integer>> itemsOrdered = new ArrayList<>();
|
||||
private final transient List<Entry<T, Integer>> itemsOrdered = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
protected transient boolean isListInSync = false;
|
||||
|
||||
public List<Entry<T, Integer>> getOrderedList() {
|
||||
public synchronized List<Entry<T, Integer>> getOrderedList() {
|
||||
if (!isListInSync) {
|
||||
rebuildOrderedList();
|
||||
}
|
||||
@@ -123,15 +124,16 @@ public final class ItemManagerModel<T extends InventoryItem> {
|
||||
}
|
||||
|
||||
public void refreshSort() {
|
||||
if (getOrderedList().isEmpty()) { return; }
|
||||
//fix newdeck editor not loading on Android if a user deleted unwanted sets on edition folder
|
||||
try { Collections.sort(getOrderedList(), new MyComparator()); }
|
||||
final List<Entry<T, Integer>> list = getOrderedList();
|
||||
if (list.isEmpty()) { return; }
|
||||
try { Collections.sort(list, new MyComparator()); }
|
||||
//fix NewDeck editor not loading on Android if a user deleted unwanted sets on edition folder
|
||||
catch (IllegalArgumentException ex) {}
|
||||
}
|
||||
|
||||
//Manages sorting orders for multiple depths of sorting
|
||||
public final class CascadeManager {
|
||||
private final List<ItemColumn> colsToSort = new ArrayList<>(3);
|
||||
private final List<ItemColumn> colsToSort = Collections.synchronizedList(new ArrayList<>(3));
|
||||
private Sorter sorter = null;
|
||||
|
||||
// Adds a column to sort cascade list.
|
||||
@@ -187,12 +189,15 @@ public final class ItemManagerModel<T extends InventoryItem> {
|
||||
private Sorter createSorter() {
|
||||
final List<ItemPoolSorter<InventoryItem>> oneColSorters = new ArrayList<>(maxSortDepth);
|
||||
|
||||
for (final ItemColumn col : colsToSort) {
|
||||
oneColSorters.add(new ItemPoolSorter<>(
|
||||
col.getFnSort(),
|
||||
col.getConfig().getSortState().equals(SortState.ASC)));
|
||||
synchronized (colsToSort) {
|
||||
final Iterator<ItemColumn> it = colsToSort.iterator();
|
||||
while (it.hasNext()) {
|
||||
final ItemColumn col = it.next();
|
||||
oneColSorters.add(new ItemPoolSorter<>(
|
||||
col.getFnSort(),
|
||||
col.getConfig().getSortState().equals(SortState.ASC)));
|
||||
}
|
||||
}
|
||||
|
||||
return new Sorter(oneColSorters);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
private final List<LimitedPlayer> players = new ArrayList<>();
|
||||
private LimitedPlayer localPlayer;
|
||||
|
||||
private boolean doublePickToStartRound = false;
|
||||
protected int nextBoosterGroup = 0;
|
||||
private int currentBoosterSize = 0;
|
||||
private int currentBoosterPick = 0;
|
||||
@@ -142,7 +143,11 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
this.product.add(block.getBooster(pp[i]));
|
||||
}
|
||||
} else {
|
||||
final IUnOpenedProduct product1 = block.getBooster(sets.get(0));
|
||||
// Only one set is chosen. If that set lets you draft 2 cards to start adjust draft settings now
|
||||
String setCode = sets.get(0);
|
||||
doublePickToStartRound = FModel.getMagicDb().getEditions().get(setCode).getDoublePickToStartRound();
|
||||
|
||||
final IUnOpenedProduct product1 = block.getBooster(setCode);
|
||||
|
||||
for (int i = 0; i < nPacks; i++) {
|
||||
this.product.add(product1);
|
||||
@@ -357,6 +362,10 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
public void passPacks() {
|
||||
// Alternate direction of pack passing
|
||||
int adjust = this.nextBoosterGroup % 2 == 1 ? 1 : -1;
|
||||
if (this.doublePickToStartRound && currentBoosterPick == 1) {
|
||||
adjust = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < N_PLAYERS; i++) {
|
||||
List<PaperCard> passingPack = this.players.get(i).passPack();
|
||||
|
||||
@@ -494,26 +503,28 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
}
|
||||
|
||||
private void recordDraftPick(final List<PaperCard> thisBooster, PaperCard c) {
|
||||
if (ForgePreferences.UPLOAD_DRAFT) {
|
||||
for (int i = 0; i < thisBooster.size(); i++) {
|
||||
final PaperCard cc = thisBooster.get(i);
|
||||
final String cnBk = cc.getName() + "|" + cc.getEdition();
|
||||
if (!ForgePreferences.UPLOAD_DRAFT) {
|
||||
return;
|
||||
}
|
||||
|
||||
float pickValue;
|
||||
if (cc.equals(c)) {
|
||||
pickValue = thisBooster.size()
|
||||
* (1f - (((float) this.currentBoosterPick / this.currentBoosterSize) * 2f));
|
||||
} else {
|
||||
pickValue = 0;
|
||||
}
|
||||
for (int i = 0; i < thisBooster.size(); i++) {
|
||||
final PaperCard cc = thisBooster.get(i);
|
||||
final String cnBk = cc.getName() + "|" + cc.getEdition();
|
||||
|
||||
if (!this.draftPicks.containsKey(cnBk)) {
|
||||
this.draftPicks.put(cnBk, pickValue);
|
||||
} else {
|
||||
final float curValue = this.draftPicks.get(cnBk);
|
||||
final float newValue = (curValue + pickValue) / 2;
|
||||
this.draftPicks.put(cnBk, newValue);
|
||||
}
|
||||
float pickValue;
|
||||
if (cc.equals(c)) {
|
||||
pickValue = thisBooster.size()
|
||||
* (1f - (((float) this.currentBoosterPick / this.currentBoosterSize) * 2f));
|
||||
} else {
|
||||
pickValue = 0;
|
||||
}
|
||||
|
||||
if (!this.draftPicks.containsKey(cnBk)) {
|
||||
this.draftPicks.put(cnBk, pickValue);
|
||||
} else {
|
||||
final float curValue = this.draftPicks.get(cnBk);
|
||||
final float newValue = (curValue + pickValue) / 2;
|
||||
this.draftPicks.put(cnBk, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +182,9 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
return true; //if not in game, card can be shown
|
||||
}
|
||||
if(GuiBase.getInterface().isLibgdxPort()){
|
||||
if(gameView.isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
if(spectator!=null) { //workaround fix!! this is needed on above code or it will
|
||||
gameControllers.remove(spectator); //bug the UI! remove spectator here since its must not be here...
|
||||
return true;
|
||||
@@ -217,6 +220,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
case Flipped:
|
||||
case Transformed:
|
||||
case Meld:
|
||||
case Modal:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -569,7 +573,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
for (int i = min; i <= cutoff; i++) {
|
||||
choices.add(Integer.valueOf(i));
|
||||
}
|
||||
choices.add("...");
|
||||
choices.add(Localizer.getInstance().getMessage("lblOtherInteger"));
|
||||
|
||||
final Object choice = oneOrNone(message, choices.build());
|
||||
if (choice instanceof Integer || choice == null) {
|
||||
@@ -581,12 +585,12 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
String prompt = "";
|
||||
if (min != Integer.MIN_VALUE) {
|
||||
if (max != Integer.MAX_VALUE) {
|
||||
prompt = localizer.getMessage("lblEnterNumberBetweenMinAndMax").replace("%min", String.valueOf(min)).replace("%max", String.valueOf(max));
|
||||
prompt = localizer.getMessage("lblEnterNumberBetweenMinAndMax", String.valueOf(min), String.valueOf(max));
|
||||
} else {
|
||||
prompt = localizer.getMessage("lblEnterNumberGreaterThanOrEqualsToMin").replace("%min", String.valueOf(min));
|
||||
prompt = localizer.getMessage("lblEnterNumberGreaterThanOrEqualsToMin", String.valueOf(min));
|
||||
}
|
||||
} else if (max != Integer.MAX_VALUE) {
|
||||
prompt = localizer.getMessage("lblEnterNumberLessThanOrEqualsToMax").replace("%max", String.valueOf(max));
|
||||
prompt = localizer.getMessage("lblEnterNumberLessThanOrEqualsToMax", String.valueOf(max));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -37,6 +36,7 @@ import forge.model.FModel;
|
||||
import forge.net.event.UpdateLobbyPlayerEvent;
|
||||
import forge.player.GamePlayerUtil;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.NameGenerator;
|
||||
import forge.util.gui.SOptionPane;
|
||||
|
||||
@@ -353,27 +353,27 @@ public abstract class GameLobby implements IHasGameType {
|
||||
}
|
||||
|
||||
if (activeSlots.size() < 2) {
|
||||
SOptionPane.showMessageDialog("At least two players are required to start a game.");
|
||||
SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblRequiredLeastTwoPlayerStartGame"));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isEnoughTeams()) {
|
||||
SOptionPane.showMessageDialog("There are not enough teams! Please adjust team allocations.");
|
||||
SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblNotEnoughTeams"));
|
||||
return null;
|
||||
}
|
||||
|
||||
for (final LobbySlot slot : activeSlots) {
|
||||
if (!slot.isReady() && slot.getType() != LobbySlotType.OPEN) {
|
||||
SOptionPane.showMessageDialog(TextUtil.concatNoSpace("Player ", slot.getName(), " is not ready"));
|
||||
SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblPlayerIsNotReady", slot.getName()));
|
||||
return null;
|
||||
}
|
||||
if (slot.getDeck() == null) {
|
||||
SOptionPane.showMessageDialog(TextUtil.concatNoSpace("Please specify a deck for ", slot.getName()));
|
||||
SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblPleaseSpecifyPlayerDeck", slot.getName()));
|
||||
return null;
|
||||
}
|
||||
if (hasVariant(GameType.Commander) || hasVariant(GameType.Oathbreaker) || hasVariant(GameType.TinyLeaders) || hasVariant(GameType.Brawl)) {
|
||||
if (!slot.getDeck().has(DeckSection.Commander)) {
|
||||
SOptionPane.showMessageDialog(TextUtil.concatNoSpace(slot.getName(), " doesn't have a commander"));
|
||||
SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblPlayerDoesntHaveCommander", slot.getName()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -410,7 +410,7 @@ public abstract class GameLobby implements IHasGameType {
|
||||
final String name = slot.getName();
|
||||
final String errMsg = GameType.Constructed.getDeckFormat().getDeckConformanceProblem(slot.getDeck());
|
||||
if (null != errMsg) {
|
||||
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Deck");
|
||||
SOptionPane.showErrorDialog(Localizer.getInstance().getMessage("lblPlayerDeckError", name, errMsg), Localizer.getInstance().getMessage("lblInvalidDeck"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -460,7 +460,7 @@ public abstract class GameLobby implements IHasGameType {
|
||||
if (checkLegality) {
|
||||
final String errMsg = commanderGameType.getDeckFormat().getDeckConformanceProblem(deck);
|
||||
if (errMsg != null) {
|
||||
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid " + commanderGameType + " Deck");
|
||||
SOptionPane.showErrorDialog(Localizer.getInstance().getMessage("lblPlayerDeckError", name, errMsg), Localizer.getInstance().getMessage("lblInvalidCommanderGameTypeDeck", commanderGameType));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -491,7 +491,7 @@ public abstract class GameLobby implements IHasGameType {
|
||||
if (checkLegality) {
|
||||
final String errMsg = DeckFormat.getSchemeSectionConformanceProblem(schemePool);
|
||||
if (null != errMsg) {
|
||||
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Scheme Deck");
|
||||
SOptionPane.showErrorDialog(Localizer.getInstance().getMessage("lblPlayerDeckError", name, errMsg), Localizer.getInstance().getMessage("lblInvalidSchemeDeck"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -504,7 +504,7 @@ public abstract class GameLobby implements IHasGameType {
|
||||
if (checkLegality) {
|
||||
final String errMsg = DeckFormat.getPlaneSectionConformanceProblem(planePool);
|
||||
if (null != errMsg) {
|
||||
SOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Planar Deck");
|
||||
SOptionPane.showErrorDialog(Localizer.getInstance().getMessage("lblPlayerDeckError", name, errMsg), Localizer.getInstance().getMessage("lblInvalidPlanarDeck"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -514,8 +514,7 @@ public abstract class GameLobby implements IHasGameType {
|
||||
//Vanguard
|
||||
if (variantTypes.contains(GameType.Vanguard)) {
|
||||
if (avatarPool == null || avatarPool.countAll() == 0) { //ERROR! null if avatar deselected on list
|
||||
SOptionPane.showMessageDialog("No Vanguard avatar selected for " + name
|
||||
+ ". Please choose one or disable the Vanguard variant");
|
||||
SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblNoSelectedVanguardAvatarForPlayer", name));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@ public class HostedMatch {
|
||||
|
||||
public void endCurrentGame() {
|
||||
if (game == null) { return; }
|
||||
boolean isMatchOver = game.getView().isMatchOver();
|
||||
|
||||
game = null;
|
||||
|
||||
@@ -304,7 +305,10 @@ public class HostedMatch {
|
||||
humanController.getGui().clearAutoYields();
|
||||
}
|
||||
|
||||
humanController.getGui().afterGameEnd();
|
||||
if (humanCount > 0) //conceded
|
||||
humanController.getGui().afterGameEnd();
|
||||
else if (!GuiBase.getInterface().isLibgdxPort()||!isMatchOver)
|
||||
humanController.getGui().afterGameEnd();
|
||||
}
|
||||
humanControllers.clear();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ public final class LobbySlot implements Serializable {
|
||||
private boolean isDevMode;
|
||||
private Deck deck;
|
||||
private ImmutableSet<AIOption> aiOptions;
|
||||
private String AvatarVanguard;
|
||||
private String SchemeDeckName;
|
||||
private String PlanarDeckName;
|
||||
private String DeckName;
|
||||
|
||||
public LobbySlot(final LobbySlotType type, final String name, final int avatarIndex, final int sleeveIndex, final int team, final boolean isArchenemy, final boolean isReady, final Set<AIOption> aiOptions) {
|
||||
this.type = type;
|
||||
@@ -79,6 +83,22 @@ public final class LobbySlot implements Serializable {
|
||||
} else if (oldDeck != null && data.getSection() != null && data.getCards() != null) {
|
||||
oldDeck.putSection(data.getSection(), data.getCards());
|
||||
}
|
||||
if (data.getSchemeDeckName() != null) {
|
||||
setSchemeDeckName(data.getSchemeDeckName());
|
||||
changed = true;
|
||||
}
|
||||
if (data.getAvatarVanguard() != null) {
|
||||
setAvatarVanguard(data.getAvatarVanguard());
|
||||
changed = true;
|
||||
}
|
||||
if (data.getPlanarDeckName() != null) {
|
||||
setPlanarDeckName(data.getPlanarDeckName());
|
||||
changed = true;
|
||||
}
|
||||
if (data.getDeckName() != null) {
|
||||
setDeckName(data.getDeckName());
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -116,6 +136,16 @@ public final class LobbySlot implements Serializable {
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
public String getSchemeDeckName() { return SchemeDeckName; }
|
||||
public String getAvatarVanguard() { return AvatarVanguard; }
|
||||
public String getPlanarDeckName() { return PlanarDeckName; }
|
||||
public String getDeckName() { return DeckName; }
|
||||
|
||||
public void setSchemeDeckName(String schemeDeckName) { this.SchemeDeckName = schemeDeckName; }
|
||||
public void setAvatarVanguard(String avatarVanguard) { this.AvatarVanguard = avatarVanguard; }
|
||||
public void setPlanarDeckName(String planarDeckName) { this.PlanarDeckName = planarDeckName; }
|
||||
public void setDeckName(String DeckName) { this.DeckName = DeckName; }
|
||||
|
||||
public boolean isArchenemy() {
|
||||
return isArchenemy;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.combat.AttackingBand;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.event.GameEventCombatUpdate;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerView;
|
||||
@@ -335,6 +336,9 @@ public class InputAttack extends InputSyncronizedBase {
|
||||
|
||||
updatePrompt();
|
||||
|
||||
if (combat != null)
|
||||
getController().getGame().fireEvent(new GameEventCombatUpdate(combat.getAttackers(), combat.getAllBlockers()));
|
||||
|
||||
getController().getGui().showCombat(); // redraw sword icons
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import forge.game.card.CardView;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.event.GameEventCombatChanged;
|
||||
import forge.game.event.GameEventCombatUpdate;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.player.PlayerControllerHuman;
|
||||
@@ -89,6 +90,9 @@ public class InputBlock extends InputSyncronizedBase {
|
||||
showMessage(message);
|
||||
}
|
||||
|
||||
if (combat != null)
|
||||
getController().getGame().fireEvent(new GameEventCombatUpdate(combat.getAttackers(), combat.getAllBlockers()));
|
||||
|
||||
getController().getGui().showCombat();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import forge.game.spellability.SpellAbilityView;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.ai.ComputerUtilMana;
|
||||
import forge.ai.PlayerControllerAi;
|
||||
@@ -92,15 +94,9 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
if (card.getManaAbilities().size() == 1) {
|
||||
activateManaAbility(card, card.getManaAbilities().get(0));
|
||||
} else {
|
||||
SpellAbilityView spellAbilityView;
|
||||
HashMap<SpellAbilityView, SpellAbility> spellAbilityViewMap = new HashMap<>();
|
||||
for (SpellAbility sa : card.getManaAbilities()) {
|
||||
spellAbilityViewMap.put(sa.getView(), sa);
|
||||
}
|
||||
List<SpellAbilityView> choices = new ArrayList<>(spellAbilityViewMap.keySet());
|
||||
spellAbilityView = getController().getGui().getAbilityToPlay(card.getView(), choices, triggerEvent);
|
||||
if (spellAbilityView != null) {
|
||||
activateManaAbility(card, spellAbilityViewMap.get(spellAbilityView));
|
||||
SpellAbility spellAbility = getController().getAbilityToPlay(card, Lists.newArrayList(card.getManaAbilities()), triggerEvent);
|
||||
if (spellAbility != null) {
|
||||
activateManaAbility(card, spellAbility);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -390,7 +386,11 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
}
|
||||
}
|
||||
else {
|
||||
String colorsProduced = m.isComboMana() ? m.getComboColors() : m.getOrigProduced();
|
||||
// treat special mana if it always can be paid
|
||||
if (m.isSpecialMana()) {
|
||||
return true;
|
||||
}
|
||||
String colorsProduced = m.isComboMana() ? m.getComboColors() : m.mana();
|
||||
for (final String color : colorsProduced.split(" ")) {
|
||||
if (0 != (neededColor & ManaAtom.fromName(color))) {
|
||||
return true;
|
||||
|
||||
@@ -1,161 +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.match.input;
|
||||
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.model.FModel;
|
||||
import forge.player.PlayerControllerHuman;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.Localizer;
|
||||
|
||||
//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 PlayerControllerHuman controller, final Game game, final SpellAbility sa, final ManaCostBeingPaid manaCostToPay) {
|
||||
super(controller, 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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onManaAbilityPaid() {
|
||||
if (this.manaCost.isPaid()) {
|
||||
this.originalCard.setSunburstValue(this.manaCost.getSunburst());
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected final void onPlayerSelected(final Player selected, final ITriggerEvent triggerEvent) {
|
||||
if (player == selected) {
|
||||
if (player.canPayLife(this.phyLifeToLose + 2)) {
|
||||
if (manaCost.payPhyrexian()) {
|
||||
this.phyLifeToLose += 2;
|
||||
} else {
|
||||
if (player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK)) {
|
||||
manaCost.decreaseShard(ManaCostShard.BLACK, 1);
|
||||
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, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@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();
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_DETAILED_SPELLDESC_IN_PROMPT)) {
|
||||
msg.append(saPaidFor.getStackDescription().replace("(Targeting ERROR)", "")).append("\n\n");
|
||||
}
|
||||
msg.append(localizer.getMessage("lblPayManaCost")).append(" ").append(this.manaCost.toString(false, player.getManaPool()));
|
||||
if (this.phyLifeToLose > 0) {
|
||||
msg.append(" ").append(String.format(localizer.getMessage("lblLifePaidForPhyrexianMana"), this.phyLifeToLose));
|
||||
}
|
||||
|
||||
boolean isLifeInsteadBlack = player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK);
|
||||
|
||||
if (manaCost.containsPhyrexianMana() || isLifeInsteadBlack) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (manaCost.containsPhyrexianMana() && !isLifeInsteadBlack) {
|
||||
sb.append(localizer.getMessage("lblClickOnYourLifeTotalToPayLifeForPhyrexianMana"));
|
||||
} else if (!manaCost.containsPhyrexianMana() && isLifeInsteadBlack) {
|
||||
sb.append(localizer.getMessage("lblClickOnYourLifeTotalToPayLifeForBlackMana"));
|
||||
} else if (manaCost.containsPhyrexianMana() && isLifeInsteadBlack) {
|
||||
sb.append(localizer.getMessage("lblClickOnYourLifeTotalToPayLifeForPhyrexianOrBlackMana"));
|
||||
}
|
||||
msg.append("\n(").append(sb).append(")");
|
||||
}
|
||||
|
||||
// has its own variant of checkIfPaid
|
||||
return msg.toString();
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class InputProxy implements Observer {
|
||||
}
|
||||
|
||||
private Card getCard(final CardView cardView) {
|
||||
return controller.getGame().getCard(cardView);
|
||||
return controller.getCard(cardView);
|
||||
}
|
||||
|
||||
public final String getActivateAction(final CardView cardView) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import forge.game.GameEntity;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -16,6 +17,7 @@ import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
@@ -148,6 +150,9 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO should use sa.canTarget(card) instead?
|
||||
// it doesn't have messages
|
||||
|
||||
//If the card is not a valid target
|
||||
if (!card.canBeTargetedBy(sa)) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this card (Shroud? Protection? Restrictions).");
|
||||
@@ -170,6 +175,35 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sa.hasParam("MaxTotalTargetCMC")) {
|
||||
int maxTotalCMC = tgt.getMaxTotalCMC(sa.getHostCard(), sa);
|
||||
if (maxTotalCMC > 0) {
|
||||
int soFar = Aggregates.sum(sa.getTargets().getTargetCards(), CardPredicates.Accessors.fnGetCmc);
|
||||
if (!sa.isTargeting(card)) {
|
||||
soFar += card.getCMC();
|
||||
}
|
||||
if (soFar > maxTotalCMC) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this card (CMC limit exceeded)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If all cards must have same controllers
|
||||
if (tgt.isSameController()) {
|
||||
final List<Player> targetedControllers = new ArrayList<>();
|
||||
for (final GameObject o : targetDepth.keySet()) {
|
||||
if (o instanceof Card) {
|
||||
final Player p = ((Card) o).getController();
|
||||
targetedControllers.add(p);
|
||||
}
|
||||
}
|
||||
if (!targetedControllers.isEmpty() && !targetedControllers.contains(card.getController())) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this card (must have same controller)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If all cards must have different controllers
|
||||
if (tgt.isDifferentControllers()) {
|
||||
final List<Player> targetedControllers = new ArrayList<>();
|
||||
@@ -185,13 +219,23 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
}
|
||||
}
|
||||
|
||||
// If all cards must have different CMC
|
||||
if (tgt.isDifferentCMC()) {
|
||||
final List<Integer> targetedCMCs = new ArrayList<>();
|
||||
for (final GameObject o : targetDepth.keySet()) {
|
||||
if (o instanceof Card) {
|
||||
final Integer cmc = ((Card) o).getCMC();
|
||||
targetedCMCs.add(cmc);
|
||||
}
|
||||
}
|
||||
if (targetedCMCs.contains(card.getCMC())) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this card (must have different CMC)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
showMessage(sa.getHostCard() + " - The selected card is not a valid choice to be targeted.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -319,7 +363,6 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
return tgt.isMaxTargetsChosen(sa.getHostCard(), sa) || ( tgt.getStillToDivide() == 0 && tgt.isDividedAsYouChoose());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
getController().getGui().clearSelectables();
|
||||
|
||||
49
forge-gui/src/main/java/forge/net/CObjectInputStream.java
Normal file
49
forge-gui/src/main/java/forge/net/CObjectInputStream.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package forge.net;
|
||||
|
||||
import io.netty.handler.codec.serialization.ClassResolver;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.io.StreamCorruptedException;
|
||||
|
||||
public class CObjectInputStream extends ObjectInputStream {
|
||||
private final ClassResolver classResolver;
|
||||
|
||||
CObjectInputStream(InputStream in, ClassResolver classResolver) throws IOException {
|
||||
super(in);
|
||||
this.classResolver = classResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
|
||||
int type = read();
|
||||
if (type < 0) {
|
||||
throw new EOFException();
|
||||
} else {
|
||||
switch(type) {
|
||||
case 0:
|
||||
return super.readClassDescriptor();
|
||||
case 1:
|
||||
String className = readUTF();
|
||||
Class<?> clazz = classResolver.resolve(className);
|
||||
return ObjectStreamClass.lookupAny(clazz);
|
||||
default:
|
||||
throw new StreamCorruptedException("Unexpected class descriptor type: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
|
||||
Class<?> clazz;
|
||||
try {
|
||||
clazz = classResolver.resolve(desc.getName());
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
clazz = super.resolveClass(desc);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
21
forge-gui/src/main/java/forge/net/CObjectOutputStream.java
Normal file
21
forge-gui/src/main/java/forge/net/CObjectOutputStream.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package forge.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class CObjectOutputStream extends ObjectOutputStream {
|
||||
static final int TYPE_THIN_DESCRIPTOR = 1;
|
||||
|
||||
CObjectOutputStream(OutputStream out) throws IOException {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException {
|
||||
//we only pass this and the decoder will lookup in the stream (faster method both mobile and desktop)
|
||||
write(TYPE_THIN_DESCRIPTOR);
|
||||
writeUTF(desc.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package forge.net;
|
||||
|
||||
import forge.GuiBase;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
import io.netty.handler.codec.serialization.ClassResolver;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.StreamCorruptedException;
|
||||
|
||||
public class CompatibleObjectDecoder extends LengthFieldBasedFrameDecoder {
|
||||
private final ClassResolver classResolver;
|
||||
|
||||
public CompatibleObjectDecoder(ClassResolver classResolver) {
|
||||
this(1048576, classResolver);
|
||||
}
|
||||
|
||||
public CompatibleObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
|
||||
super(maxObjectSize, 0, 4, 0, 4);
|
||||
this.classResolver = classResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
|
||||
ByteBuf frame = (ByteBuf)super.decode(ctx, in);
|
||||
if (frame == null) {
|
||||
return null;
|
||||
} else {
|
||||
ObjectInputStream ois = GuiBase.hasPropertyConfig() ?
|
||||
new ObjectInputStream(new LZ4BlockInputStream(new ByteBufInputStream(frame, true))):
|
||||
new CObjectInputStream(new LZ4BlockInputStream(new ByteBufInputStream(frame, true)),this.classResolver);
|
||||
|
||||
Object var5 = null;
|
||||
try {
|
||||
var5 = ois.readObject();
|
||||
} catch (StreamCorruptedException e) {
|
||||
System.err.println(String.format("Version Mismatch: %s", e.getMessage()));
|
||||
} finally {
|
||||
ois.close();
|
||||
}
|
||||
|
||||
return var5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package forge.net;
|
||||
|
||||
import forge.GuiBase;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class CompatibleObjectEncoder extends MessageToByteEncoder<Serializable> {
|
||||
private static final byte[] LENGTH_PLACEHOLDER = new byte[4];
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
|
||||
int startIdx = out.writerIndex();
|
||||
ByteBufOutputStream bout = new ByteBufOutputStream(out);
|
||||
ObjectOutputStream oout = null;
|
||||
|
||||
try {
|
||||
bout.write(LENGTH_PLACEHOLDER);
|
||||
oout = GuiBase.hasPropertyConfig() ? new ObjectOutputStream(new LZ4BlockOutputStream(bout)) : new CObjectOutputStream(new LZ4BlockOutputStream(bout));
|
||||
oout.writeObject(msg);
|
||||
oout.flush();
|
||||
} finally {
|
||||
if (oout != null) {
|
||||
oout.close();
|
||||
} else {
|
||||
bout.close();
|
||||
}
|
||||
}
|
||||
|
||||
int endIdx = out.writerIndex();
|
||||
out.setInt(startIdx, endIdx - startIdx - 4);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package forge.net;
|
||||
|
||||
import forge.GuiBase;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
import io.netty.handler.codec.serialization.ClassResolver;
|
||||
import org.mapdb.elsa.ElsaObjectInputStream;
|
||||
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
public class CustomObjectDecoder extends LengthFieldBasedFrameDecoder {
|
||||
private final ClassResolver classResolver;
|
||||
|
||||
public CustomObjectDecoder(ClassResolver classResolver) {
|
||||
this(1048576, classResolver);
|
||||
}
|
||||
|
||||
public CustomObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
|
||||
super(maxObjectSize, 0, 4, 0, 4);
|
||||
this.classResolver = classResolver;
|
||||
}
|
||||
|
||||
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
|
||||
ByteBuf frame = (ByteBuf) super.decode(ctx, in);
|
||||
if (frame == null) {
|
||||
return null;
|
||||
} else {
|
||||
if (GuiBase.hasPropertyConfig()){
|
||||
ElsaObjectInputStream ois = new ElsaObjectInputStream(new ByteBufInputStream(frame, true));
|
||||
|
||||
Object var5;
|
||||
try {
|
||||
var5 = ois.readObject();
|
||||
} finally {
|
||||
ois.close();
|
||||
}
|
||||
|
||||
return var5;
|
||||
}
|
||||
else {
|
||||
ObjectInputStream ois = new ObjectInputStream(new ByteBufInputStream(frame, true));
|
||||
|
||||
Object var5;
|
||||
try {
|
||||
var5 = ois.readObject();
|
||||
} finally {
|
||||
ois.close();
|
||||
}
|
||||
|
||||
return var5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int maxObjectsize = 10000000; //10megabyte???
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package forge.net;
|
||||
|
||||
import forge.GuiBase;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import org.mapdb.elsa.ElsaObjectOutputStream;
|
||||
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class CustomObjectEncoder extends MessageToByteEncoder<Serializable> {
|
||||
private static final byte[] LENGTH_PLACEHOLDER = new byte[4];
|
||||
|
||||
public CustomObjectEncoder() {
|
||||
}
|
||||
|
||||
protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
|
||||
int startIdx = out.writerIndex();
|
||||
ByteBufOutputStream bout = new ByteBufOutputStream(out);
|
||||
|
||||
if (GuiBase.hasPropertyConfig()){
|
||||
ElsaObjectOutputStream oout = null;
|
||||
try {
|
||||
bout.write(LENGTH_PLACEHOLDER);
|
||||
oout = new ElsaObjectOutputStream(bout);
|
||||
oout.writeObject(msg);
|
||||
oout.flush();
|
||||
} finally {
|
||||
if (oout != null) {
|
||||
oout.close();
|
||||
} else {
|
||||
bout.close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ObjectOutputStream oout = null;
|
||||
try {
|
||||
bout.write(LENGTH_PLACEHOLDER);
|
||||
oout = new ObjectOutputStream(bout);
|
||||
oout.writeObject(msg);
|
||||
oout.flush();
|
||||
} finally {
|
||||
if (oout != null) {
|
||||
oout.close();
|
||||
} else {
|
||||
bout.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int endIdx = out.writerIndex();
|
||||
out.setInt(startIdx, endIdx - startIdx - 4);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package forge.net;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.net.event.GuiGameEvent;
|
||||
import forge.net.event.ReplyEvent;
|
||||
import forge.util.gui.SOptionPane;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
@@ -25,6 +27,7 @@ public abstract class GameProtocolHandler<T> extends ChannelInboundHandlerAdapte
|
||||
|
||||
@Override
|
||||
public final void channelRead(final ChannelHandlerContext ctx, final Object msg) {
|
||||
final String[] catchedError = {""};
|
||||
System.out.println("Received: " + msg);
|
||||
if (msg instanceof ReplyEvent) {
|
||||
final ReplyEvent event = (ReplyEvent) msg;
|
||||
@@ -36,7 +39,9 @@ public abstract class GameProtocolHandler<T> extends ChannelInboundHandlerAdapte
|
||||
|
||||
final Method method = protocolMethod.getMethod();
|
||||
if (method == null) {
|
||||
throw new IllegalStateException(String.format("Method %s not found", protocolMethod.name()));
|
||||
//throw new IllegalStateException(String.format("Method %s not found", protocolMethod.name()));
|
||||
catchedError[0] += String.format("IllegalStateException: Method %s not found (GameProtocolHandler.java Line 43)\n", protocolMethod.name());
|
||||
System.err.println(String.format("Method %s not found", protocolMethod.name()));
|
||||
}
|
||||
|
||||
final Object[] args = event.getObjects();
|
||||
@@ -56,7 +61,9 @@ public abstract class GameProtocolHandler<T> extends ChannelInboundHandlerAdapte
|
||||
} catch (final IllegalAccessException | IllegalArgumentException e) {
|
||||
System.err.println(String.format("Unknown protocol method %s with %d args", methodName, args == null ? 0 : args.length));
|
||||
} catch (final InvocationTargetException e) {
|
||||
throw new RuntimeException(e.getTargetException());
|
||||
//throw new RuntimeException(e.getTargetException());
|
||||
catchedError[0] += (String.format("RuntimeException: %s (GameProtocolHandler.java Line 65)\n", e.getTargetException().toString()));
|
||||
System.err.println(e.getTargetException().toString());
|
||||
}
|
||||
} else {
|
||||
Serializable reply = null;
|
||||
@@ -70,8 +77,11 @@ public abstract class GameProtocolHandler<T> extends ChannelInboundHandlerAdapte
|
||||
}
|
||||
} catch (final IllegalAccessException | IllegalArgumentException e) {
|
||||
System.err.println(String.format("Unknown protocol method %s with %d args, replying with null", methodName, args == null ? 0 : args.length));
|
||||
} catch (final InvocationTargetException e) {
|
||||
throw new RuntimeException(e.getTargetException());
|
||||
} catch (final NullPointerException | InvocationTargetException e) {
|
||||
//throw new RuntimeException(e.getTargetException());
|
||||
catchedError[0] += e.toString();
|
||||
SOptionPane.showMessageDialog(catchedError[0], "Error", FSkinProp.ICO_WARNING);
|
||||
System.err.println(e.toString());
|
||||
}
|
||||
getRemote(ctx).send(new ReplyEvent(event.getId(), reply));
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@ import forge.net.client.FGameClient;
|
||||
public interface IOnlineLobby {
|
||||
ILobbyView setLobby(GameLobby lobby);
|
||||
void setClient(FGameClient client);
|
||||
void closeConn(String msg);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package forge.net;
|
||||
|
||||
import forge.match.LobbySlotType;
|
||||
import forge.properties.ForgeConstants;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.interfaces.IGuiGame;
|
||||
import forge.interfaces.ILobbyListener;
|
||||
import forge.interfaces.ILobbyView;
|
||||
@@ -24,12 +24,13 @@ import forge.player.GamePlayerUtil;
|
||||
import forge.properties.ForgeProfileProperties;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.gui.SOptionPane;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class NetConnectUtil {
|
||||
private NetConnectUtil() { }
|
||||
|
||||
public static String getServerUrl() {
|
||||
final String url = SOptionPane.showInputDialog("This feature is under active development.\nYou are likely to find bugs.\n\n - = * H E R E B E E L D R A Z I * = -\n\nEnter the URL of the server to join.\nLeave blank to host your own server.", "Connect to Server");
|
||||
final String url = SOptionPane.showInputDialog(Localizer.getInstance().getMessage("lblOnlineMultiplayerDest"), Localizer.getInstance().getMessage("lblConnectToServer"));
|
||||
if (url == null) { return null; }
|
||||
|
||||
//prompt user for player one name if needed
|
||||
@@ -101,7 +102,7 @@ public class NetConnectUtil {
|
||||
|
||||
view.update(true);
|
||||
|
||||
return new ChatMessage(null, String.format("Hosting on port %d.", port));
|
||||
return new ChatMessage(null, Localizer.getInstance().getMessage("lblHostingPortOnN", String.valueOf(port)));
|
||||
}
|
||||
|
||||
public static void copyHostedServerUrl() {
|
||||
@@ -116,13 +117,13 @@ public class NetConnectUtil {
|
||||
GuiBase.getInterface().copyToClipboard(internalAddress);
|
||||
}
|
||||
|
||||
String message = "Share the following URL with anyone who wishes to join your server. It has been copied to your clipboard for convenience.\n\n";
|
||||
String message = "";
|
||||
if (externalUrl != null) {
|
||||
message += externalUrl + "\n\nFor internal games, use the following URL: " + internalUrl;
|
||||
message = Localizer.getInstance().getMessage("lblShareURLToMakePlayerJoinServer", externalUrl, internalUrl);
|
||||
} else {
|
||||
message = "Forge was unable to determine your external IP!\n\n" + message + internalUrl;
|
||||
message = Localizer.getInstance().getMessage("lblForgeUnableDetermineYourExternalIP", message + internalUrl);
|
||||
}
|
||||
SOptionPane.showMessageDialog(message, "Server URL", SOptionPane.INFORMATION_ICON);
|
||||
SOptionPane.showMessageDialog(message, Localizer.getInstance().getMessage("lblServerURL"), SOptionPane.INFORMATION_ICON);
|
||||
}
|
||||
|
||||
public static ChatMessage join(final String url, final IOnlineLobby onlineLobby, final IOnlineChatInterface chatInterface) {
|
||||
@@ -145,8 +146,8 @@ public class NetConnectUtil {
|
||||
}
|
||||
@Override
|
||||
public final void close() {
|
||||
SOptionPane.showMessageDialog("Your connection to the host (" + url + ") was interrupted.", "Error", FSkinProp.ICO_WARNING);
|
||||
onlineLobby.setClient(null);
|
||||
GuiBase.setInterrupted(true);
|
||||
onlineLobby.closeConn(Localizer.getInstance().getMessage("lblYourConnectionToHostWasInterrupted", url));
|
||||
}
|
||||
@Override
|
||||
public ClientGameLobby getLobby() {
|
||||
@@ -178,9 +179,10 @@ public class NetConnectUtil {
|
||||
client.connect(hostname, port);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
//return a message to close the connection so we will not crash...
|
||||
return new ChatMessage(null, ForgeConstants.CLOSE_CONN_COMMAND);
|
||||
}
|
||||
|
||||
return new ChatMessage(null, String.format("Connected to %s:%d", hostname, port));
|
||||
return new ChatMessage(null, Localizer.getInstance().getMessage("lblConnectedIPPort", hostname, String.valueOf(port)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,9 @@ import forge.player.PlayerZoneUpdates;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.ReflectionUtil;
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -57,7 +54,7 @@ public enum ProtocolMethod {
|
||||
updateLives (Mode.SERVER, Void.TYPE, Iterable/*PlayerView*/.class),
|
||||
setPanelSelection (Mode.SERVER, Void.TYPE, CardView.class),
|
||||
getAbilityToPlay (Mode.SERVER, SpellAbilityView.class, CardView.class, List/*SpellAbilityView*/.class, ITriggerEvent.class),
|
||||
assignDamage (Mode.SERVER, Map.class, CardView.class, List/*CardView*/.class, Integer.TYPE, GameEntityView.class, Boolean.TYPE),
|
||||
assignCombatDamage (Mode.SERVER, Map.class, CardView.class, List/*CardView*/.class, Integer.TYPE, GameEntityView.class, Boolean.TYPE),
|
||||
message (Mode.SERVER, Void.TYPE, String.class, String.class),
|
||||
showErrorDialog (Mode.SERVER, Void.TYPE, String.class, String.class),
|
||||
showConfirmDialog (Mode.SERVER, Boolean.TYPE, String.class, String.class, String.class, String.class, Boolean.TYPE),
|
||||
@@ -68,7 +65,7 @@ public enum ProtocolMethod {
|
||||
order (Mode.SERVER, List.class, String.class, String.class, Integer.TYPE, Integer.TYPE, List.class, List.class, CardView.class, Boolean.TYPE),
|
||||
sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class, String.class),
|
||||
chooseSingleEntityForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, DelayedReveal.class, Boolean.TYPE),
|
||||
chooseEntitiesForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, Integer.TYPE, Integer.TYPE, DelayedReveal.class),
|
||||
chooseEntitiesForEffect(Mode.SERVER, List.class, String.class, List.class, Integer.TYPE, Integer.TYPE, DelayedReveal.class),
|
||||
manipulateCardList (Mode.SERVER, List.class, String.class, Iterable.class, Iterable.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE),
|
||||
setCard (Mode.SERVER, Void.TYPE, CardView.class),
|
||||
setSelectables (Mode.SERVER, Void.TYPE, Iterable/*CardView*/.class),
|
||||
@@ -159,36 +156,15 @@ public enum ProtocolMethod {
|
||||
}
|
||||
|
||||
public void checkArgs(final Object[] args) {
|
||||
if (GuiBase.hasPropertyConfig())
|
||||
return; //uses custom serializer for Android 8+..
|
||||
if(!GuiBase.hasPropertyConfig())
|
||||
return; //if the experimental network option is enabled, then check the args, else let the default decoder handle it
|
||||
|
||||
for (int iArg = 0; iArg < args.length; iArg++) {
|
||||
Object arg = null;
|
||||
Class<?> type = null;
|
||||
try {
|
||||
arg = args[iArg];
|
||||
if (this.args.length > iArg)
|
||||
type = this.args[iArg];
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException ex){ ex.printStackTrace(); }
|
||||
catch(ConcurrentModificationException ex) { ex.printStackTrace(); }
|
||||
if (arg != null)
|
||||
if (type != null)
|
||||
if (!ReflectionUtil.isInstance(arg, type)) {
|
||||
throw new InternalError(String.format("Protocol method %s: illegal argument (%d) of type %s, %s expected", name(), iArg, arg.getClass().getName(), type.getName()));
|
||||
}
|
||||
if (arg != null) {
|
||||
// attempt to Serialize each argument, this will throw an exception if it can't.
|
||||
try {
|
||||
byte[] serialized = SerializationUtils.serialize((Serializable) arg);
|
||||
SerializationUtils.deserialize(serialized);
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
// not sure why this one would be thrown, but it is
|
||||
// it also doesn't prevent things from working, so, log for now, pending full network rewrite
|
||||
ex.printStackTrace();
|
||||
} catch(ConcurrentModificationException ex) {
|
||||
// can't seem to avoid this from periodically happening
|
||||
ex.printStackTrace();
|
||||
}
|
||||
final Object arg = args[iArg];
|
||||
final Class<?> type = this.args[iArg];
|
||||
if (!ReflectionUtil.isInstance(arg, type)) {
|
||||
//throw new InternalError(String.format("Protocol method %s: illegal argument (%d) of type %s, %s expected", name(), iArg, arg.getClass().getName(), type.getName()));
|
||||
System.err.println(String.format("InternalError: Protocol method %s: illegal argument (%d) of type %s, %s expected (ProtocolMethod.java)", name(), iArg, arg.getClass().getName(), type.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,7 +175,8 @@ public enum ProtocolMethod {
|
||||
return;
|
||||
}
|
||||
if (!ReflectionUtil.isInstance(value, returnType)) {
|
||||
throw new IllegalStateException(String.format("Protocol method %s: illegal return object type %s returned by client, expected %s", name(), value.getClass().getName(), getReturnType().getName()));
|
||||
//throw new IllegalStateException(String.format("Protocol method %s: illegal return object type %s returned by client, expected %s", name(), value.getClass().getName(), getReturnType().getName()));
|
||||
System.err.println(String.format("IllegalStateException: Protocol method %s: illegal return object type %s returned by client, expected %s (ProtocolMethod.java)", name(), value.getClass().getName(), getReturnType().getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package forge.net.client;
|
||||
|
||||
import forge.net.CompatibleObjectDecoder;
|
||||
import forge.net.CompatibleObjectEncoder;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
@@ -26,8 +28,6 @@ import forge.net.event.IdentifiableNetEvent;
|
||||
import forge.net.event.LobbyUpdateEvent;
|
||||
import forge.net.event.MessageEvent;
|
||||
import forge.net.event.NetEvent;
|
||||
import io.netty.handler.codec.serialization.ObjectDecoder;
|
||||
import io.netty.handler.codec.serialization.ObjectEncoder;
|
||||
|
||||
public class FGameClient implements IToServer {
|
||||
|
||||
@@ -58,8 +58,8 @@ public class FGameClient implements IToServer {
|
||||
public void initChannel(final SocketChannel ch) throws Exception {
|
||||
final ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast(
|
||||
new ObjectEncoder(),
|
||||
new ObjectDecoder(9766*1024, ClassResolvers.cacheDisabled(null)),
|
||||
new CompatibleObjectEncoder(),
|
||||
new CompatibleObjectDecoder(9766*1024, ClassResolvers.cacheDisabled(null)),
|
||||
new MessageHandler(),
|
||||
new LobbyUpdateHandler(),
|
||||
new GameClientHandler(FGameClient.this));
|
||||
@@ -86,7 +86,8 @@ public class FGameClient implements IToServer {
|
||||
}
|
||||
|
||||
public void close() {
|
||||
channel.close();
|
||||
if (channel != null)
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,12 +40,12 @@ public class NetGameController implements IGameController {
|
||||
|
||||
@Override
|
||||
public void selectPlayer(final PlayerView playerView, final ITriggerEvent triggerEvent) {
|
||||
send(ProtocolMethod.selectPlayer, playerView, triggerEvent);
|
||||
send(ProtocolMethod.selectPlayer, playerView, null/*triggerEvent*/); //some platform don't have mousetriggerevent class or it will not allow them to click/tap
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean selectCard(final CardView cardView, final List<CardView> otherCardViewsToSelect, final ITriggerEvent triggerEvent) {
|
||||
send(ProtocolMethod.selectCard, cardView, otherCardViewsToSelect, triggerEvent);
|
||||
send(ProtocolMethod.selectCard, cardView, otherCardViewsToSelect, null/*triggerEvent*/); //some platform don't have mousetriggerevent class or it will not allow them to click/tap
|
||||
// Difference from local games! Always consider a card as successfully selected,
|
||||
// to avoid blocks where server and client wait for each other to respond.
|
||||
// Some cost in functionality but a huge gain in stability & speed.
|
||||
|
||||
@@ -25,6 +25,10 @@ public final class UpdateLobbyPlayerEvent implements NetEvent {
|
||||
private DeckSection section = null;
|
||||
private CardPool cards = null;
|
||||
private Set<AIOption> aiOptions = null;
|
||||
private String AvatarVanguard = null;
|
||||
private String SchemeDeckName = null;
|
||||
private String PlanarDeckName = null;
|
||||
private String DeckName = null;
|
||||
|
||||
|
||||
public static UpdateLobbyPlayerEvent create(final LobbySlotType type, final String name, final int avatarIndex, final int sleeveIndex, final int team, final boolean isArchenemy, final boolean isReady, final Set<AIOption> aiOptions) {
|
||||
@@ -53,17 +57,39 @@ public final class UpdateLobbyPlayerEvent implements NetEvent {
|
||||
public static UpdateLobbyPlayerEvent sleeveUpdate(final int index) {
|
||||
return new UpdateLobbyPlayerEvent(index, false);
|
||||
}
|
||||
private UpdateLobbyPlayerEvent(int index, boolean avatar) {
|
||||
public static UpdateLobbyPlayerEvent isReadyUpdate(final boolean isReady) {
|
||||
return new UpdateLobbyPlayerEvent(isReady);
|
||||
}
|
||||
public static UpdateLobbyPlayerEvent teamUpdate(int team) {
|
||||
return new UpdateLobbyPlayerEvent(team);
|
||||
}
|
||||
public static UpdateLobbyPlayerEvent setDeckSchemePlaneVanguard(final String DeckName, final String Scheme, final String Plane, final String Vanguard) {
|
||||
return new UpdateLobbyPlayerEvent(DeckName, Scheme, Plane, Vanguard);
|
||||
}
|
||||
private UpdateLobbyPlayerEvent(final int index, final boolean avatar) {
|
||||
if (avatar)
|
||||
this.avatarIndex = index;
|
||||
else
|
||||
this.sleeveIndex = index;
|
||||
}
|
||||
private UpdateLobbyPlayerEvent(final int team) {
|
||||
this.team = team;
|
||||
}
|
||||
private UpdateLobbyPlayerEvent(final String DeckName, final String Scheme, final String Plane, final String Vanguard) {
|
||||
this.SchemeDeckName = Scheme;
|
||||
this.PlanarDeckName = Plane;
|
||||
this.AvatarVanguard = Vanguard;
|
||||
this.DeckName = DeckName;
|
||||
}
|
||||
|
||||
private UpdateLobbyPlayerEvent(final Deck deck) {
|
||||
this.deck = deck;
|
||||
}
|
||||
|
||||
private UpdateLobbyPlayerEvent(final boolean isReady) {
|
||||
this.isReady = isReady;
|
||||
}
|
||||
|
||||
private UpdateLobbyPlayerEvent(final DeckSection section, final CardPool cards) {
|
||||
this.section = section;
|
||||
this.cards = cards;
|
||||
@@ -149,4 +175,8 @@ public final class UpdateLobbyPlayerEvent implements NetEvent {
|
||||
public Set<AIOption> getAiOptions() {
|
||||
return aiOptions == null ? null : Collections.unmodifiableSet(aiOptions);
|
||||
}
|
||||
public String getAvatarVanguard() { return AvatarVanguard; }
|
||||
public String getSchemeDeckName() { return SchemeDeckName; }
|
||||
public String getPlanarDeckName() { return PlanarDeckName; }
|
||||
public String getDeckName() { return DeckName; }
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import forge.interfaces.IGuiGame;
|
||||
import forge.interfaces.ILobbyListener;
|
||||
import forge.match.LobbySlot;
|
||||
import forge.match.LobbySlotType;
|
||||
import forge.net.CompatibleObjectDecoder;
|
||||
import forge.net.CompatibleObjectEncoder;
|
||||
import forge.net.event.LobbyUpdateEvent;
|
||||
import forge.net.event.LoginEvent;
|
||||
import forge.net.event.LogoutEvent;
|
||||
@@ -24,8 +26,6 @@ import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.serialization.ClassResolvers;
|
||||
import io.netty.handler.codec.serialization.ObjectDecoder;
|
||||
import io.netty.handler.codec.serialization.ObjectEncoder;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
@@ -99,8 +99,8 @@ public final class FServerManager {
|
||||
public final void initChannel(final SocketChannel ch) throws Exception {
|
||||
final ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(
|
||||
new ObjectEncoder(),
|
||||
new ObjectDecoder(9766*1024, ClassResolvers.cacheDisabled(null)),
|
||||
new CompatibleObjectEncoder(),
|
||||
new CompatibleObjectDecoder(9766*1024, ClassResolvers.cacheDisabled(null)),
|
||||
new MessageHandler(),
|
||||
new RegisterClientHandler(),
|
||||
new LobbyInputHandler(),
|
||||
@@ -180,6 +180,15 @@ public final class FServerManager {
|
||||
this.localLobby = lobby;
|
||||
}
|
||||
|
||||
public void unsetReady() {
|
||||
if (this.localLobby != null) {
|
||||
if (this.localLobby.getSlot(0) != null) {
|
||||
this.localLobby.getSlot(0).setIsReady(false);
|
||||
updateLobbyState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMatchActive() {
|
||||
return this.localLobby != null && this.localLobby.isMatchActive();
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class NetGuiGame extends AbstractGuiGame {
|
||||
return sender.sendAndWait(method, args);
|
||||
}
|
||||
|
||||
private void updateGameView() {
|
||||
public void updateGameView() {
|
||||
send(ProtocolMethod.setGameView, getGameView());
|
||||
}
|
||||
|
||||
@@ -191,12 +191,12 @@ public class NetGuiGame extends AbstractGuiGame {
|
||||
|
||||
@Override
|
||||
public SpellAbilityView getAbilityToPlay(final CardView hostCard, final List<SpellAbilityView> abilities, final ITriggerEvent triggerEvent) {
|
||||
return sendAndWait(ProtocolMethod.getAbilityToPlay, hostCard, abilities, triggerEvent);
|
||||
return sendAndWait(ProtocolMethod.getAbilityToPlay, hostCard, abilities, null/*triggerEvent*/); //someplatform don't have mousetriggerevent class or it will not allow them to click/tap
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<CardView, Integer> assignDamage(final CardView attacker, final List<CardView> blockers, final int damage, final GameEntityView defender, final boolean overrideOrder) {
|
||||
return sendAndWait(ProtocolMethod.assignDamage, attacker, blockers, damage, defender, overrideOrder);
|
||||
public Map<CardView, Integer> assignCombatDamage(final CardView attacker, final List<CardView> blockers, final int damage, final GameEntityView defender, final boolean overrideOrder) {
|
||||
return sendAndWait(ProtocolMethod.assignCombatDamage, attacker, blockers, damage, defender, overrideOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,7 +45,12 @@ public class ConquestRegion {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void clearArt() {
|
||||
art = null;
|
||||
}
|
||||
|
||||
public ISkinImage getArt() {
|
||||
clearArt(); //force clear this so it will be redrawn since loadingcache invalidates the cache every screen change
|
||||
if (art == null) {
|
||||
PaperCard pc = cardPool.getCard(artCardName);
|
||||
|
||||
|
||||
@@ -9,11 +9,14 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.card.CardType;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameEntityView;
|
||||
import forge.game.GameEntityViewMap;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
@@ -22,6 +25,7 @@ import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.cost.*;
|
||||
import forge.game.player.Player;
|
||||
@@ -35,6 +39,7 @@ import forge.match.input.InputSelectManyBase;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.gui.SGuiChoose;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
@@ -123,6 +128,23 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
}
|
||||
return PaymentDecision.card(randomSubset);
|
||||
}
|
||||
if (discardType.equals("DifferentNames")) {
|
||||
final CardCollection discarded = new CardCollection();
|
||||
while (c > 0) {
|
||||
final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, 1, 1, hand, ability);
|
||||
inp.setMessage(Localizer.getInstance().getMessage("lblSelectOneDifferentNameCardToDiscardAlreadyChosen") + discarded);
|
||||
inp.setCancelAllowed(true);
|
||||
inp.showAndWait();
|
||||
if (inp.hasCancelled()) {
|
||||
return null;
|
||||
}
|
||||
final Card first = inp.getFirstSelected();
|
||||
discarded.add(first);
|
||||
hand = CardLists.filter(hand, Predicates.not(CardPredicates.sharesNameWith(first)));
|
||||
c--;
|
||||
}
|
||||
return PaymentDecision.card(discarded);
|
||||
}
|
||||
if (discardType.contains("+WithSameName")) {
|
||||
final String type = TextUtil.fastReplace(discardType, "+WithSameName", "");
|
||||
hand = CardLists.getValidCards(hand, type.split(";"), player, source, ability);
|
||||
@@ -310,11 +332,12 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
if (nNeeded == 0) {
|
||||
return PaymentDecision.number(0);
|
||||
}
|
||||
final Game game = controller.getGame();
|
||||
final Player p = game.getPlayer(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblExileFromWhoseZone", cost.getFrom().getTranslatedName()), PlayerView.getCollection(payableZone)));
|
||||
if (p == null) {
|
||||
GameEntityViewMap<Player, PlayerView> gameCachePlayer = GameEntityView.getMap(payableZone);
|
||||
final PlayerView pv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblExileFromWhoseZone", cost.getFrom().getTranslatedName()), gameCachePlayer.getTrackableKeys());
|
||||
if (pv == null || !gameCachePlayer.containsKey(pv)) {
|
||||
return null;
|
||||
}
|
||||
final Player p = gameCachePlayer.get(pv);
|
||||
|
||||
final CardCollection typeList = CardLists.filter(list, CardPredicates.isOwner(p));
|
||||
final int count = typeList.size();
|
||||
@@ -322,8 +345,13 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
final CardCollection toExile = game.getCardList(controller.getGui().many(Localizer.getInstance().getMessage("lblExileFromZone", cost.getFrom().getTranslatedName()), Localizer.getInstance().getMessage("lblToBeExiled"), nNeeded, CardView.getCollection(typeList), null));
|
||||
return PaymentDecision.card(toExile);
|
||||
GameEntityViewMap<Card, CardView> gameCacheExile = GameEntityView.getMap(typeList);
|
||||
List<CardView> views = controller.getGui().many(
|
||||
Localizer.getInstance().getMessage("lblExileFromZone", cost.getFrom().getTranslatedName()),
|
||||
Localizer.getInstance().getMessage("lblToBeExiled"), nNeeded, gameCacheExile.getTrackableKeys(), null);
|
||||
List<Card> result = Lists.newArrayList();
|
||||
gameCacheExile.addToList(views, result);
|
||||
return PaymentDecision.card(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -395,20 +423,17 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
return PaymentDecision.card(list);
|
||||
}
|
||||
|
||||
private Card getCard(final CardView cardView) {
|
||||
return controller.getGame().getCard(cardView);
|
||||
}
|
||||
|
||||
private PaymentDecision exileFromMiscZone(final CostExile cost, final SpellAbility sa, final int nNeeded, final CardCollection typeList) {
|
||||
if (typeList.size() < nNeeded) { return null; }
|
||||
|
||||
GameEntityViewMap<Card, CardView> gameCacheCard = GameEntityView.getMap(typeList);
|
||||
|
||||
final CardCollection exiled = new CardCollection();
|
||||
for (int i = 0; i < nNeeded; i++) {
|
||||
final Card c = getCard(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblExileProgressFromZone", String.valueOf(i + 1), String.valueOf(nNeeded), cost.getFrom().getTranslatedName()), CardView.getCollection(typeList)));
|
||||
if (c == null) { return null; }
|
||||
final CardView cv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblExileProgressFromZone", String.valueOf(i + 1), String.valueOf(nNeeded), cost.getFrom().getTranslatedName()), gameCacheCard.getTrackableKeys());
|
||||
if (cv == null || !gameCacheCard.containsKey(cv)) { return null; }
|
||||
|
||||
typeList.remove(c);
|
||||
exiled.add(c);
|
||||
exiled.add(gameCacheCard.remove(cv));
|
||||
}
|
||||
return PaymentDecision.card(exiled);
|
||||
}
|
||||
@@ -438,12 +463,17 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
if (ability.isOptionalTrigger()) {
|
||||
min = 0;
|
||||
}
|
||||
final CardCollection choice = controller.getGame().getCardList(controller.getGui().many(Localizer.getInstance().getMessage("lblChooseAnExiledCardPutIntoGraveyard"), Localizer.getInstance().getMessage("lblToGraveyard"), min, c, CardView.getCollection(list), CardView.get(source)));
|
||||
|
||||
if (choice == null || choice.size() < c) {
|
||||
GameEntityViewMap<Card, CardView> gameCacheExile = GameEntityView.getMap(list);
|
||||
List<CardView> views = controller.getGui().many(
|
||||
Localizer.getInstance().getMessage("lblChooseAnExiledCardPutIntoGraveyard"),
|
||||
Localizer.getInstance().getMessage("lblToGraveyard"), min, c, CardView.getCollection(list), CardView.get(source));
|
||||
|
||||
if (views == null || views.size() < c) {
|
||||
return null;
|
||||
}
|
||||
return PaymentDecision.card(choice);
|
||||
List<Card> result = Lists.newArrayList();
|
||||
gameCacheExile.addToList(views, result);
|
||||
return PaymentDecision.card(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -495,14 +525,13 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
Integer c = cost.convertAmount();
|
||||
|
||||
if (c == null) {
|
||||
final String sVar = ability.getSVar(amount);
|
||||
// Generalize this
|
||||
if (sVar.equals("XChoice")) {
|
||||
c = chooseXValue(cost.getLKIList().size());
|
||||
} else {
|
||||
c = AbilityUtils.calculateAmount(source, amount, ability);
|
||||
}
|
||||
c = AbilityUtils.calculateAmount(source, amount, ability);
|
||||
}
|
||||
|
||||
if (!player.getController().confirmPayment(cost, Localizer.getInstance().getMessage("lblDoYouWantFlipNCoinAction", String.valueOf(c)), ability)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return PaymentDecision.number(c);
|
||||
}
|
||||
|
||||
@@ -555,11 +584,12 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
return PaymentDecision.players(oppsThatCanGainLife);
|
||||
}
|
||||
|
||||
final Player chosenToGain = controller.getGame().getPlayer(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblCardChooseAnOpponentToGainNLife", CardTranslation.getTranslatedName(source.getName()), String.valueOf(c)), PlayerView.getCollection(oppsThatCanGainLife)));
|
||||
if (chosenToGain == null) {
|
||||
GameEntityViewMap<Player, PlayerView> gameCachePlayer = GameEntityView.getMap(oppsThatCanGainLife);
|
||||
final PlayerView pv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblCardChooseAnOpponentToGainNLife", CardTranslation.getTranslatedName(source.getName()), String.valueOf(c)), gameCachePlayer.getTrackableKeys());
|
||||
if (pv == null || !gameCachePlayer.containsKey(pv)) {
|
||||
return null;
|
||||
}
|
||||
return PaymentDecision.players(Lists.newArrayList(chosenToGain));
|
||||
return PaymentDecision.players(Lists.newArrayList(gameCachePlayer.get(pv)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -568,19 +598,13 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
Integer c = cost.convertAmount();
|
||||
|
||||
if (c == null) {
|
||||
final String sVar = ability.getSVar(amount);
|
||||
// Generalize this
|
||||
if (sVar.equals("XChoice")) {
|
||||
c = chooseXValue(cost.getLKIList().size());
|
||||
} else {
|
||||
c = AbilityUtils.calculateAmount(source, amount, ability);
|
||||
}
|
||||
c = AbilityUtils.calculateAmount(source, amount, ability);
|
||||
}
|
||||
|
||||
if (!player.getController().confirmPayment(cost, Localizer.getInstance().getMessage("lblMillNCardsFromYourLibraryConfirm", String.valueOf(c)), ability)) {
|
||||
return null;
|
||||
}
|
||||
return PaymentDecision.card(player.getCardsIn(ZoneType.Library, c));
|
||||
return PaymentDecision.number(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -613,7 +637,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
@Override
|
||||
public PaymentDecision visit(final CostPayEnergy cost) {
|
||||
final String amount = cost.getAmount();
|
||||
final int energy = player.getCounters(CounterType.ENERGY);
|
||||
final int energy = player.getCounters(CounterEnumType.ENERGY);
|
||||
|
||||
Integer c = cost.convertAmount();
|
||||
if (c == null) {
|
||||
@@ -629,7 +653,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
}
|
||||
|
||||
if (player.canPayEnergy(c) &&
|
||||
player.getController().confirmPayment(cost, Localizer.getInstance().getMessage("lblPayEnergyConfirm", cost.toString(), String.valueOf(player.getCounters(CounterType.ENERGY)), "{E}"), ability)) {
|
||||
player.getController().confirmPayment(cost, Localizer.getInstance().getMessage("lblPayEnergyConfirm", cost.toString(), String.valueOf(player.getCounters(CounterEnumType.ENERGY)), "{E}"), ability)) {
|
||||
return PaymentDecision.number(c);
|
||||
}
|
||||
return null;
|
||||
@@ -694,13 +718,13 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
}
|
||||
|
||||
final CardCollection chosen = new CardCollection();
|
||||
GameEntityViewMap<Card, CardView> gameCacheCard = GameEntityView.getMap(typeList);
|
||||
for (int i = 0; i < nNeeded; i++) {
|
||||
final Card c = getCard(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblFromZonePutToLibrary", fromZone.getTranslatedName()), CardView.getCollection(typeList)));
|
||||
if (c == null) {
|
||||
final CardView cv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblFromZonePutToLibrary", fromZone.getTranslatedName()), gameCacheCard.getTrackableKeys());
|
||||
if (cv == null || !gameCacheCard.containsKey(cv)) {
|
||||
return null;
|
||||
}
|
||||
typeList.remove(c);
|
||||
chosen.add(c);
|
||||
chosen.add(gameCacheCard.remove(cv));
|
||||
}
|
||||
return PaymentDecision.card(chosen);
|
||||
}
|
||||
@@ -710,10 +734,12 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
return PaymentDecision.number(0);
|
||||
}
|
||||
|
||||
final Player p = controller.getGame().getPlayer(controller.getGui().oneOrNone(TextUtil.concatNoSpace(Localizer.getInstance().getMessage("lblPutCardsFromWhoseZone"), fromZone.getTranslatedName()), PlayerView.getCollection(payableZone)));
|
||||
if (p == null) {
|
||||
GameEntityViewMap<Player, PlayerView> gameCachePlayer = GameEntityView.getMap(payableZone);
|
||||
PlayerView pv = SGuiChoose.oneOrNone(TextUtil.concatNoSpace(Localizer.getInstance().getMessage("lblPutCardsFromWhoseZone"), fromZone.getTranslatedName()), gameCachePlayer.getTrackableKeys());
|
||||
if (pv == null || !gameCachePlayer.containsKey(pv)) {
|
||||
return null;
|
||||
}
|
||||
Player p = gameCachePlayer.get(pv);
|
||||
|
||||
final CardCollection typeList = CardLists.filter(list, CardPredicates.isOwner(p));
|
||||
if (typeList.size() < nNeeded) {
|
||||
@@ -721,13 +747,13 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
}
|
||||
|
||||
final CardCollection chosen = new CardCollection();
|
||||
GameEntityViewMap<Card, CardView> gameCacheCard = GameEntityView.getMap(typeList);
|
||||
for (int i = 0; i < nNeeded; i++) {
|
||||
final Card c = getCard(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblPutZoneCardsToLibrary", fromZone.getTranslatedName()), CardView.getCollection(typeList)));
|
||||
if (c == null) {
|
||||
final CardView cv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblPutZoneCardsToLibrary", fromZone.getTranslatedName()), gameCacheCard.getTrackableKeys());
|
||||
if (cv == null || !gameCacheCard.containsKey(cv)) {
|
||||
return null;
|
||||
}
|
||||
typeList.remove(c);
|
||||
chosen.add(c);
|
||||
chosen.add(gameCacheCard.remove(cv));
|
||||
}
|
||||
return PaymentDecision.card(chosen);
|
||||
}
|
||||
@@ -1068,15 +1094,14 @@ public class HumanCostDecision extends CostDecisionMakerBase {
|
||||
}
|
||||
|
||||
// Rift Elemental only - always removes 1 counter, so there will be no code for N counters.
|
||||
final List<CardView> suspended = Lists.newArrayList();
|
||||
for (final Card crd : validCards) {
|
||||
if (crd.getCounters(cost.counter) > 0) {
|
||||
suspended.add(CardView.get(crd));
|
||||
}
|
||||
GameEntityViewMap<Card, CardView> gameCacheSuspended = GameEntityView.getMap(CardLists.filter(validCards, CardPredicates.hasCounter(cost.counter)));
|
||||
|
||||
final CardView cv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblRemoveCountersFromAInZoneCard", cost.zone.getTranslatedName()), gameCacheSuspended.getTrackableKeys());
|
||||
if (cv == null || !gameCacheSuspended.containsKey(cv)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Card card = getCard(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblRemoveCountersFromAInZoneCard", cost.zone.getTranslatedName()), suspended));
|
||||
return null == card ? null : PaymentDecision.card(card, c);
|
||||
return PaymentDecision.card(gameCacheSuspended.get(cv), c);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,10 +6,11 @@ import forge.FThreads;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameEntityView;
|
||||
import forge.game.GameEntityViewMap;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.ability.effects.CharmEffect;
|
||||
import forge.game.ability.effects.FlipCoinEffect;
|
||||
import forge.game.card.*;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.cost.*;
|
||||
@@ -23,7 +24,6 @@ import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.match.input.InputPayMana;
|
||||
import forge.match.input.InputPayManaOfCostPayment;
|
||||
import forge.match.input.InputPayManaSimple;
|
||||
import forge.match.input.InputSelectCardsFromList;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.collect.FCollectionView;
|
||||
@@ -46,17 +46,20 @@ public class HumanPlay {
|
||||
* <p>
|
||||
* playSpellAbility.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sa
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
*/
|
||||
public final static boolean playSpellAbility(final PlayerControllerHuman controller, final Player p, SpellAbility sa) {
|
||||
FThreads.assertExecutedByEdt(false);
|
||||
|
||||
Card source = sa.getHostCard();
|
||||
|
||||
if (sa instanceof LandAbility) {
|
||||
sa.setActivatingPlayer(p);
|
||||
if (sa.canPlay()) {
|
||||
sa.resolve();
|
||||
p.getGame().updateLastStateForCard(source);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -64,7 +67,6 @@ public class HumanPlay {
|
||||
boolean castFaceDown = sa.isCastFaceDown();
|
||||
|
||||
sa.setActivatingPlayer(p);
|
||||
Card source = sa.getHostCard();
|
||||
boolean flippedToCast = sa instanceof Spell && source.isFaceDown();
|
||||
|
||||
source.setSplitStateToPlayAbility(sa);
|
||||
@@ -79,7 +81,7 @@ public class HumanPlay {
|
||||
}
|
||||
|
||||
if (flippedToCast && !castFaceDown) {
|
||||
source.turnFaceUp(false, false);
|
||||
source.forceTurnFaceUp();
|
||||
}
|
||||
|
||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||
@@ -92,34 +94,9 @@ public class HumanPlay {
|
||||
source.animateBestow();
|
||||
}
|
||||
|
||||
// Need to check PayCosts, and Ability + All SubAbilities for Target
|
||||
boolean newAbility = sa.getPayCosts() != null;
|
||||
SpellAbility ability = sa;
|
||||
while ((ability != null) && !newAbility) {
|
||||
final TargetRestrictions tgt = ability.getTargetRestrictions();
|
||||
|
||||
newAbility |= tgt != null;
|
||||
ability = ability.getSubAbility();
|
||||
}
|
||||
|
||||
// System.out.println("Playing:" + sa.getDescription() + " of " + sa.getHostCard() + " new = " + newAbility);
|
||||
if (newAbility) {
|
||||
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
if (!req.playAbility(true, false, false)) {
|
||||
if (flippedToCast && !castFaceDown) {
|
||||
source.turnFaceDown(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else if (payManaCostIfNeeded(controller, p, sa)) {
|
||||
if (sa.isSpell() && !source.isCopiedSpell()) {
|
||||
sa.setHostCard(p.getGame().getAction().moveToStack(source, sa));
|
||||
}
|
||||
|
||||
p.getGame().getStack().add(sa);
|
||||
} else {
|
||||
// Failed to pay costs, revert to original state
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
if (!req.playAbility(true, false, false)) {
|
||||
if (flippedToCast && !castFaceDown) {
|
||||
source.turnFaceDown(true);
|
||||
}
|
||||
@@ -131,7 +108,7 @@ public class HumanPlay {
|
||||
/**
|
||||
* choose optional additional costs. For HUMAN only
|
||||
* @param p
|
||||
*
|
||||
*
|
||||
* @param original
|
||||
* the original sa
|
||||
* @return an ArrayList<SpellAbility>.
|
||||
@@ -157,31 +134,11 @@ public class HumanPlay {
|
||||
//final List<SpellAbility> abilities = GameActionUtil.getOptionalCosts(original);
|
||||
}
|
||||
|
||||
private static boolean payManaCostIfNeeded(final PlayerControllerHuman controller, final Player p, final SpellAbility sa) {
|
||||
final ManaCostBeingPaid manaCost;
|
||||
if (sa.getHostCard().isCopiedSpell() && sa.isSpell()) {
|
||||
manaCost = new ManaCostBeingPaid(ManaCost.ZERO);
|
||||
}
|
||||
else {
|
||||
manaCost = new ManaCostBeingPaid(sa.getPayCosts().getTotalMana());
|
||||
CostAdjustment.adjust(manaCost, sa, null, false);
|
||||
}
|
||||
|
||||
boolean isPaid = manaCost.isPaid();
|
||||
|
||||
if (!isPaid) {
|
||||
InputPayManaSimple inputPay = new InputPayManaSimple(controller, p.getGame(), sa, manaCost);
|
||||
inputPay.showAndWait();
|
||||
isPaid = inputPay.isPaid();
|
||||
}
|
||||
return isPaid;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* playSpellAbilityForFree.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sa
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
*/
|
||||
@@ -191,33 +148,22 @@ public class HumanPlay {
|
||||
|
||||
source.setSplitStateToPlayAbility(sa);
|
||||
|
||||
if (sa.getPayCosts() != null) {
|
||||
if (!sa.isCopied()) {
|
||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||
CharmEffect.makeChoices(sa);
|
||||
}
|
||||
sa = AbilityUtils.addSpliceEffects(sa);
|
||||
if (!sa.isCopied()) {
|
||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||
CharmEffect.makeChoices(sa);
|
||||
}
|
||||
sa = AbilityUtils.addSpliceEffects(sa);
|
||||
}
|
||||
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
req.playAbility(mayChooseNewTargets, true, false);
|
||||
}
|
||||
else {
|
||||
if (sa.isSpell()) {
|
||||
final Card c = sa.getHostCard();
|
||||
if (!c.isCopiedSpell()) {
|
||||
sa.setHostCard(game.getAction().moveToStack(c, sa));
|
||||
}
|
||||
}
|
||||
game.getStack().add(sa);
|
||||
}
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
req.playAbility(mayChooseNewTargets, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* playSpellAbility_NoStack.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param sa
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
*/
|
||||
@@ -228,14 +174,8 @@ public class HumanPlay {
|
||||
public final static void playSpellAbilityNoStack(final PlayerControllerHuman controller, final Player player, final SpellAbility sa, boolean useOldTargets) {
|
||||
sa.setActivatingPlayer(player);
|
||||
|
||||
if (sa.getPayCosts() != null) {
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
|
||||
req.playAbility(!useOldTargets, false, true);
|
||||
}
|
||||
else if (payManaCostIfNeeded(controller, player, sa)) {
|
||||
AbilityUtils.resolve(sa);
|
||||
}
|
||||
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
|
||||
req.playAbility(!useOldTargets, false, true);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -345,7 +285,7 @@ public class HumanPlay {
|
||||
}
|
||||
|
||||
for (Player player : res) {
|
||||
player.drawCards(amount);
|
||||
player.drawCards(amount, sourceAbility);
|
||||
}
|
||||
}
|
||||
else if (part instanceof CostGainLife) {
|
||||
@@ -378,19 +318,25 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
CardCollectionView listmill = p.getCardsIn(ZoneType.Library, amount);
|
||||
((CostMill) part).executePayment(sourceAbility, listmill);
|
||||
((CostMill) part).payAsDecided(p, PaymentDecision.card(listmill), sourceAbility);
|
||||
}
|
||||
else if (part instanceof CostFlipCoin) {
|
||||
final int amount = getAmountFromPart(part, source, sourceAbility);
|
||||
if (!p.getController().confirmPayment(part, Localizer.getInstance().getMessage("lblDoYouWantFlipNCoinOrDoAction", String.valueOf(amount), orString), sourceAbility)) {
|
||||
if (!part.canPay(sourceAbility, p)) {
|
||||
return false;
|
||||
}
|
||||
final int n = FlipCoinEffect.getFilpMultiplier(p);
|
||||
for (int i = 0; i < amount; i++) {
|
||||
FlipCoinEffect.flipCoinCall(p, sourceAbility, n);
|
||||
}
|
||||
|
||||
PaymentDecision pd = part.accept(hcd);
|
||||
|
||||
if (pd == null)
|
||||
return false;
|
||||
else
|
||||
part.payAsDecided(p, pd, sourceAbility);
|
||||
}
|
||||
else if (part instanceof CostDamage) {
|
||||
if (!part.canPay(sourceAbility, p)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// not a pay life but damage!
|
||||
PaymentDecision pd = part.accept(hcd);
|
||||
|
||||
@@ -400,6 +346,10 @@ public class HumanPlay {
|
||||
part.payAsDecided(p, pd, sourceAbility);
|
||||
}
|
||||
else if (part instanceof CostPutCounter) {
|
||||
if (!part.canPay(sourceAbility, p)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PaymentDecision pd = part.accept(hcd);
|
||||
|
||||
if (pd == null)
|
||||
@@ -486,7 +436,7 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
|
||||
costExile.executePayment(sourceAbility, p.getCardsIn(ZoneType.Graveyard));
|
||||
costExile.payAsDecided(p, PaymentDecision.card(p.getCardsIn(ZoneType.Graveyard)), sourceAbility);
|
||||
}
|
||||
else {
|
||||
from = costExile.getFrom();
|
||||
@@ -500,20 +450,19 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
list = list.subList(0, nNeeded);
|
||||
costExile.executePayment(sourceAbility, list);
|
||||
costExile.payAsDecided(p, PaymentDecision.card(list), sourceAbility);
|
||||
} else {
|
||||
// replace this with input
|
||||
CardCollection newList = new CardCollection();
|
||||
GameEntityViewMap<Card, CardView> gameCacheList = GameEntityView.getMap(list);
|
||||
for (int i = 0; i < nNeeded; i++) {
|
||||
final Card c = p.getGame().getCard(SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblExileFromZone", from.getTranslatedName()), CardView.getCollection(list)));
|
||||
if (c == null) {
|
||||
final CardView cv = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblExileFromZone", from.getTranslatedName()), gameCacheList.getTrackableKeys());
|
||||
if (cv == null || !gameCacheList.containsKey(cv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list.remove(c);
|
||||
newList.add(c);
|
||||
newList.add(gameCacheList.remove(cv));
|
||||
}
|
||||
costExile.executePayment(sourceAbility, newList);
|
||||
costExile.payAsDecided(p, PaymentDecision.card(newList), sourceAbility);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -541,31 +490,32 @@ public class HumanPlay {
|
||||
payableZone.add(player);
|
||||
}
|
||||
}
|
||||
Player chosen = controller.getGame().getPlayer(SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblPutCardFromWhoseZone", from.getTranslatedName()), PlayerView.getCollection(payableZone)));
|
||||
if (chosen == null) {
|
||||
GameEntityViewMap<Player, PlayerView> gameCachePlayer = GameEntityView.getMap(payableZone);
|
||||
PlayerView pv = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblPutCardFromWhoseZone", from.getTranslatedName()), gameCachePlayer.getTrackableKeys());
|
||||
if (pv == null || !gameCachePlayer.containsKey(pv)) {
|
||||
return false;
|
||||
}
|
||||
Player chosen = gameCachePlayer.get(pv);
|
||||
|
||||
List<Card> typeList = CardLists.filter(list, CardPredicates.isOwner(chosen));
|
||||
|
||||
GameEntityViewMap<Card, CardView> gameCacheTypeList = GameEntityView.getMap(typeList);
|
||||
for (int i = 0; i < amount; i++) {
|
||||
if (typeList.isEmpty()) {
|
||||
if (gameCacheTypeList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Card c = p.getGame().getCard(SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblPutCardToLibrary"), CardView.getCollection(typeList)));
|
||||
|
||||
if (c != null) {
|
||||
typeList.remove(c);
|
||||
p.getGame().getAction().moveToLibrary(c, Integer.parseInt(((CostPutCardToLib) part).getLibPos()), null);
|
||||
}
|
||||
else {
|
||||
final CardView cv = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblPutCardToLibrary"), gameCacheTypeList.getTrackableKeys());
|
||||
if (cv == null || !gameCacheTypeList.containsKey(cv)) {
|
||||
return false;
|
||||
}
|
||||
final Card c = gameCacheTypeList.get(cv);
|
||||
|
||||
gameCacheTypeList.remove(c);
|
||||
p.getGame().getAction().moveToLibrary(c, Integer.parseInt(((CostPutCardToLib) part).getLibPos()), null);
|
||||
}
|
||||
}
|
||||
else { // Tainted Specter, Gurzigost, etc.
|
||||
boolean hasPaid = payCostPart(controller, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblPutIntoLibrary") + orString);
|
||||
boolean hasPaid = payCostPart(controller, p, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblPutIntoLibrary") + orString);
|
||||
if (!hasPaid) {
|
||||
return false;
|
||||
}
|
||||
@@ -582,13 +532,13 @@ public class HumanPlay {
|
||||
else if (part instanceof CostGainControl) {
|
||||
int amount = Integer.parseInt(part.getAmount());
|
||||
CardCollectionView list = CardLists.getValidCards(p.getGame().getCardsIn(ZoneType.Battlefield), part.getType(), p, source);
|
||||
boolean hasPaid = payCostPart(controller, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblGainControl") + orString);
|
||||
boolean hasPaid = payCostPart(controller, p, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblGainControl") + orString);
|
||||
if (!hasPaid) { return false; }
|
||||
}
|
||||
else if (part instanceof CostReturn) {
|
||||
CardCollectionView list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(controller, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblReturnToHand") + orString);
|
||||
boolean hasPaid = payCostPart(controller, p, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblReturnToHand") + orString);
|
||||
if (!hasPaid) { return false; }
|
||||
}
|
||||
else if (part instanceof CostDiscard) {
|
||||
@@ -597,11 +547,11 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
|
||||
((CostDiscard)part).executePayment(sourceAbility, p.getCardsIn(ZoneType.Hand));
|
||||
((CostDiscard)part).payAsDecided(p, PaymentDecision.card(p.getCardsIn(ZoneType.Hand)), sourceAbility);
|
||||
} else {
|
||||
CardCollectionView list = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(controller, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lbldiscard") + orString);
|
||||
boolean hasPaid = payCostPart(controller, p, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lbldiscard") + orString);
|
||||
if (!hasPaid) { return false; }
|
||||
}
|
||||
}
|
||||
@@ -609,14 +559,14 @@ public class HumanPlay {
|
||||
CostReveal costReveal = (CostReveal) part;
|
||||
CardCollectionView list = CardLists.getValidCards(p.getCardsIn(costReveal.getRevealFrom()), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(controller, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblReveal") + orString);
|
||||
boolean hasPaid = payCostPart(controller, p, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblReveal") + orString);
|
||||
if (!hasPaid) { return false; }
|
||||
}
|
||||
else if (part instanceof CostTapType) {
|
||||
CardCollectionView list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source);
|
||||
list = CardLists.filter(list, Presets.UNTAPPED);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(controller, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblTap") + orString);
|
||||
boolean hasPaid = payCostPart(controller, p, sourceAbility, (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblTap") + orString);
|
||||
if (!hasPaid) { return false; }
|
||||
}
|
||||
else if (part instanceof CostPartMana) {
|
||||
@@ -625,7 +575,7 @@ public class HumanPlay {
|
||||
}
|
||||
}
|
||||
else if (part instanceof CostPayEnergy) {
|
||||
CounterType counterType = CounterType.ENERGY;
|
||||
CounterType counterType = CounterType.get(CounterEnumType.ENERGY);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
|
||||
if (!part.canPay(sourceAbility, p)) {
|
||||
@@ -675,7 +625,7 @@ public class HumanPlay {
|
||||
return paid;
|
||||
}
|
||||
|
||||
private static boolean payCostPart(final PlayerControllerHuman controller, SpellAbility sourceAbility, CostPartWithList cpl, int amount, CardCollectionView list, String actionName) {
|
||||
private static boolean payCostPart(final PlayerControllerHuman controller, Player p, SpellAbility sourceAbility, CostPartWithList cpl, int amount, CardCollectionView list, String actionName) {
|
||||
if (list.size() < amount) { return false; } // unable to pay (not enough cards)
|
||||
|
||||
InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, amount, amount, list, sourceAbility);
|
||||
@@ -687,11 +637,8 @@ public class HumanPlay {
|
||||
return false;
|
||||
}
|
||||
|
||||
cpl.executePayment(sourceAbility, new CardCollection(inp.getSelected()));
|
||||
cpl.payAsDecided(p, PaymentDecision.card(inp.getSelected()), sourceAbility);
|
||||
|
||||
if (sourceAbility != null) {
|
||||
cpl.reportPaidCardsTo(sourceAbility);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -748,10 +695,10 @@ public class HumanPlay {
|
||||
if (mc.getAmountOfX() > 0 && !"Count$xPaid".equals(xInCard)) { // announce X will overwrite whatever was in card script
|
||||
int xPaid = AbilityUtils.calculateAmount(source, "X", ability);
|
||||
toPay.setXManaCostPaid(xPaid, ability.getParam("XColor"));
|
||||
source.setXManaCostPaid(xPaid);
|
||||
ability.setXManaCostPaid(xPaid);
|
||||
}
|
||||
else if (source.getXManaCostPaid() > 0) { //ensure pre-announced X value retained
|
||||
toPay.setXManaCostPaid(source.getXManaCostPaid(), ability.getParam("XColor"));
|
||||
else if (ability.getXManaCostPaid() != null) { //ensure pre-announced X value retained
|
||||
toPay.setXManaCostPaid(ability.getXManaCostPaid(), ability.getParam("XColor"));
|
||||
}
|
||||
|
||||
int timesMultikicked = source.getKickerMagnitude();
|
||||
|
||||
@@ -103,7 +103,7 @@ public class HumanPlaySpellAbility {
|
||||
// This is should happen earlier, before the Modal spell is chosen
|
||||
// Turn face-down card face up (except case of morph spell)
|
||||
if (ability.isSpell() && !ability.isCastFaceDown() && fromState == CardStateName.FaceDown) {
|
||||
c.turnFaceUp();
|
||||
c.turnFaceUp(null);
|
||||
}
|
||||
ability.setHostCard(game.getAction().moveToStack(c, ability));
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class HumanPlaySpellAbility {
|
||||
|
||||
ability = GameActionUtil.addExtraKeywordCost(ability);
|
||||
|
||||
Cost abCost = ability.getPayCosts() == null ? new Cost("0", ability.isAbility()) : ability.getPayCosts();
|
||||
Cost abCost = ability.getPayCosts();
|
||||
CostPayment payment = new CostPayment(abCost, ability);
|
||||
|
||||
// TODO Apply this to the SAStackInstance instead of the Player
|
||||
@@ -161,7 +161,6 @@ public class HumanPlaySpellAbility {
|
||||
// if a player failed to play madness cost, move the card to graveyard
|
||||
Card newCard = game.getAction().moveToGraveyard(c, null);
|
||||
newCard.setMadnessWithoutCast(true);
|
||||
newCard.setMadness(false);
|
||||
} else if (ability.getHostCard().isBestowed()) {
|
||||
ability.getHostCard().unanimateBestow();
|
||||
}
|
||||
@@ -214,7 +213,7 @@ public class HumanPlaySpellAbility {
|
||||
final FCollection<Player> candidates = AbilityUtils.getDefinedPlayers(source, currentAbility.getParam("TargetingPlayer"), currentAbility);
|
||||
// activator chooses targeting player
|
||||
targetingPlayer = ability.getActivatingPlayer().getController().chooseSingleEntityForEffect(
|
||||
candidates, currentAbility, "Choose the targeting player");
|
||||
candidates, currentAbility, "Choose the targeting player", null);
|
||||
} else {
|
||||
targetingPlayer = ability.getActivatingPlayer();
|
||||
}
|
||||
@@ -223,10 +222,10 @@ public class HumanPlaySpellAbility {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
final SpellAbility subAbility = currentAbility.getSubAbility();
|
||||
final AbilitySub subAbility = currentAbility.getSubAbility();
|
||||
if (subAbility != null) {
|
||||
// This is necessary for "TargetsWithDefinedController$ ParentTarget"
|
||||
((AbilitySub) subAbility).setParent(currentAbility);
|
||||
subAbility.setParent(currentAbility);
|
||||
}
|
||||
currentAbility = subAbility;
|
||||
} while (currentAbility != null);
|
||||
@@ -314,10 +313,10 @@ public class HumanPlaySpellAbility {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
card.setXManaCostPaid(value);
|
||||
ability.setXManaCostPaid(value);
|
||||
}
|
||||
} else if (manaCost.getMana().isZero() && ability.isSpell()) {
|
||||
card.setXManaCostPaid(0);
|
||||
ability.setXManaCostPaid(0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,12 +6,12 @@
|
||||
* 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/>.
|
||||
*/
|
||||
@@ -21,6 +21,8 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameEntityView;
|
||||
import forge.game.GameEntityViewMap;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardUtil;
|
||||
@@ -44,7 +46,7 @@ import java.util.Map;
|
||||
* <p>
|
||||
* Target_Selection class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id: TargetSelection.java 25148 2014-03-12 08:28:52Z swordshine $
|
||||
*/
|
||||
@@ -63,14 +65,18 @@ public class TargetSelection {
|
||||
|
||||
private boolean bTargetingDone = false;
|
||||
|
||||
private boolean isMandatory() {
|
||||
return ability.isMandatory() || getTgt().getMandatory();
|
||||
}
|
||||
|
||||
public final boolean chooseTargets(Integer numTargets) {
|
||||
final TargetRestrictions tgt = getTgt();
|
||||
final boolean canTarget = tgt != null && tgt.doesTarget();
|
||||
if (!canTarget) {
|
||||
throw new RuntimeException("TargetSelection.chooseTargets called for ability that does not target - " + ability);
|
||||
}
|
||||
|
||||
// Number of targets is explicitly set only if spell is being redirected (ex. Swerve or Redirect)
|
||||
|
||||
// Number of targets is explicitly set only if spell is being redirected (ex. Swerve or Redirect)
|
||||
final int minTargets = numTargets != null ? numTargets.intValue() : tgt.getMinTargets(ability.getHostCard(), ability);
|
||||
final int maxTargets = numTargets != null ? numTargets.intValue() : tgt.getMaxTargets(ability.getHostCard(), ability);
|
||||
//final int maxTotalCMC = tgt.getMaxTotalCMC(ability.getHostCard(), ability);
|
||||
@@ -95,14 +101,14 @@ public class TargetSelection {
|
||||
// Cancel ability if there aren't any valid Candidates
|
||||
return false;
|
||||
}
|
||||
if (tgt.getMandatory() && !hasCandidates && hasEnoughTargets) {
|
||||
if (isMandatory() && !hasCandidates && hasEnoughTargets) {
|
||||
// Mandatory target selection, that has no candidates but enough targets (Min == 0, but no choices)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
final List<ZoneType> zones = tgt.getZone();
|
||||
final boolean mandatory = tgt.getMandatory() && hasCandidates;
|
||||
|
||||
final boolean mandatory = isMandatory() && hasCandidates;
|
||||
|
||||
final boolean choiceResult;
|
||||
final boolean random = tgt.isRandomTarget();
|
||||
if (random) {
|
||||
@@ -167,7 +173,7 @@ public class TargetSelection {
|
||||
choiceResult = this.chooseCardFromList(validTargets, true, mandatory);
|
||||
}
|
||||
}
|
||||
// some inputs choose cards one-by-one and need to be called again
|
||||
// some inputs choose cards one-by-one and need to be called again
|
||||
return choiceResult && chooseTargets(numTargets);
|
||||
}
|
||||
|
||||
@@ -175,6 +181,8 @@ public class TargetSelection {
|
||||
// Send in a list of valid cards, and popup a choice box to target
|
||||
final Game game = ability.getActivatingPlayer().getGame();
|
||||
|
||||
GameEntityViewMap<Card, CardView> gameCacheChooseCard = GameEntityView.getMap(choices);
|
||||
|
||||
final List<CardView> crdsBattle = Lists.newArrayList();
|
||||
final List<CardView> crdsExile = Lists.newArrayList();
|
||||
final List<CardView> crdsGrave = Lists.newArrayList();
|
||||
@@ -228,7 +236,7 @@ public class TargetSelection {
|
||||
// is there a more elegant way of doing this?
|
||||
choicesFiltered.add(msgDone);
|
||||
}
|
||||
|
||||
|
||||
Object chosen = null;
|
||||
if (!choices.isEmpty() && mandatory) {
|
||||
chosen = controller.getGui().one(getTgt().getVTSelection(), choicesFiltered);
|
||||
@@ -245,7 +253,10 @@ public class TargetSelection {
|
||||
}
|
||||
|
||||
if (chosen instanceof CardView) {
|
||||
ability.getTargets().add(game.getCard((CardView) chosen));
|
||||
if (!gameCacheChooseCard.containsKey(chosen)) {
|
||||
return false;
|
||||
}
|
||||
ability.getTargets().add(gameCacheChooseCard.get((CardView) chosen));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -292,7 +303,7 @@ public class TargetSelection {
|
||||
if (madeChoice instanceof StackItemView) {
|
||||
ability.getTargets().add(stackItemViewCache.get(madeChoice).getSpellAbility(true));
|
||||
}
|
||||
else {// 'FINISH TARGETING' chosen
|
||||
else {// 'FINISH TARGETING' chosen
|
||||
bTargetingDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ public final class ForgeConstants {
|
||||
public static final String IMAGE_LIST_ACHIEVEMENTS_FILE = LISTS_DIR + "achievement-images.txt";
|
||||
public static final String NET_DECKS_LIST_FILE = LISTS_DIR + "net-decks.txt";
|
||||
public static final String NET_DECKS_COMMANDER_LIST_FILE = LISTS_DIR + "net-decks-commander.txt";
|
||||
public static final String NET_DECKS_BRAWL_LIST_FILE = LISTS_DIR + "net-decks-brawl.txt";
|
||||
|
||||
|
||||
public static final String CHANGES_FILE = ASSETS_DIR + "README.txt";
|
||||
public static final String CHANGES_FILE_NO_RELEASE = ASSETS_DIR + "CHANGES.txt";
|
||||
@@ -69,6 +71,7 @@ public final class ForgeConstants {
|
||||
public static final String LANG_DIR = RES_DIR + "languages" + PATH_SEPARATOR;
|
||||
public static final String EFFECTS_DIR = RES_DIR + "effects" + PATH_SEPARATOR;
|
||||
public static final String PUZZLE_DIR = RES_DIR + "puzzle" + PATH_SEPARATOR;
|
||||
public static final String TUTORIAL_DIR = RES_DIR + "tutorial" + PATH_SEPARATOR;
|
||||
public static final String DECK_GEN_DIR = RES_DIR + "deckgendecks" + PATH_SEPARATOR;
|
||||
|
||||
|
||||
@@ -237,6 +240,7 @@ public final class ForgeConstants {
|
||||
public static final String QUEST_PREFS_FILE = USER_PREFS_DIR + "quest.preferences";
|
||||
public static final String CONQUEST_PREFS_FILE = USER_PREFS_DIR + "conquest.preferences";
|
||||
public static final String ITEM_VIEW_PREFS_FILE = USER_PREFS_DIR + "item_view.preferences";
|
||||
public static final String CLOSE_CONN_COMMAND = "<<_EM_ESOLC_<<";
|
||||
|
||||
// data that has defaults in the program dir but overrides/additions in the user dir
|
||||
private static final String _DEFAULTS_DIR = RES_DIR + "defaults" + PATH_SEPARATOR;
|
||||
|
||||
@@ -84,6 +84,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
|
||||
UI_IMAGE_CACHE_MAXIMUM("400"),
|
||||
UI_OVERLAY_FOIL_EFFECT ("true"),
|
||||
UI_HIDE_REMINDER_TEXT ("false"),
|
||||
UI_SR_OPTIMIZE ("false"),
|
||||
UI_OPEN_PACKS_INDIV ("false"),
|
||||
UI_STACK_CREATURES ("false"),
|
||||
UI_UPLOAD_DRAFT ("false"),
|
||||
@@ -139,7 +140,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
|
||||
UI_ENABLE_PRELOAD_EXTENDED_ART("false"),
|
||||
UI_ENABLE_BORDER_MASKING("false"),
|
||||
UI_SHOW_FPS("false"),
|
||||
UI_USE_ELSA("false"),
|
||||
UI_NETPLAY_COMPAT("false"),
|
||||
UI_LOAD_UNKNOWN_CARDS("true"),
|
||||
UI_ALLOW_ORDER_GRAVEYARD_WHEN_NEEDED ("Never"),
|
||||
UI_DEFAULT_FONT_SIZE("12"),
|
||||
|
||||
@@ -16,10 +16,10 @@ public class PuzzleIO {
|
||||
public static final String SUFFIX_DATA = ".pzl";
|
||||
public static final String SUFFIX_COMPLETE = ".complete";
|
||||
|
||||
public static ArrayList<Puzzle> loadPuzzles() {
|
||||
public static ArrayList<Puzzle> loadPuzzles(String directory) {
|
||||
String[] pList;
|
||||
// get list of puzzles
|
||||
final File pFolder = new File(ForgeConstants.PUZZLE_DIR);
|
||||
final File pFolder = new File(directory);
|
||||
if (!pFolder.exists()) {
|
||||
throw new RuntimeException("Puzzles : folder not found -- folder is " + pFolder.getAbsolutePath());
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public class PuzzleIO {
|
||||
ArrayList<Puzzle> puzzles = Lists.newArrayList();
|
||||
for (final String element : pList) {
|
||||
if (element.endsWith(SUFFIX_DATA)) {
|
||||
final List<String> pfData = FileUtil.readFile(ForgeConstants.PUZZLE_DIR + element);
|
||||
final List<String> pfData = FileUtil.readFile(directory + element);
|
||||
|
||||
String filename = element.replace(SUFFIX_DATA, "");
|
||||
boolean completed = FileUtil.doesFileExist(ForgeConstants.USER_PUZZLE_DIR + element.replace(SUFFIX_DATA, SUFFIX_COMPLETE));
|
||||
@@ -48,9 +48,4 @@ public class PuzzleIO {
|
||||
public static Map<String, List<String>> parsePuzzleSections(List<String> pfData) {
|
||||
return FileSection.parseSections(pfData);
|
||||
}
|
||||
|
||||
|
||||
public static File getPuzzleFile(final String name) {
|
||||
return new File(ForgeConstants.PUZZLE_DIR, name + SUFFIX_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.util.List;
|
||||
* This class is used to store the Quest starting pool preferences.
|
||||
* (It could be expanded to store other Quest starting preferences as well,
|
||||
* in order to reduce the number of parameters that need to be passed to
|
||||
* QuestController.newGame from CSubmenuQuestData)
|
||||
* QuestController.newGame from CSubmenuQuestStart)
|
||||
*
|
||||
*/
|
||||
public final class StartingPoolPreferences {
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
package forge.quest;
|
||||
|
||||
import forge.util.Localizer;
|
||||
|
||||
public enum StartingPoolType {
|
||||
Complete("Unrestricted"),
|
||||
Sanctioned("Sanctioned format"),
|
||||
Casual("Casual/Historic format"),
|
||||
CustomFormat("Custom format"),
|
||||
Precon("Event or starter deck"),
|
||||
SealedDeck("My sealed deck"),
|
||||
DraftDeck("My draft deck"),
|
||||
Cube("Predefined cube");
|
||||
Complete("lblUnrestricted"),
|
||||
Sanctioned("lblSanctionedFormat"),
|
||||
Casual("lblCasualOrHistoricFormat"),
|
||||
CustomFormat("lblCustomFormat"),
|
||||
Precon("lblEventOrStartDeck"),
|
||||
SealedDeck("lblMySealedDeck"),
|
||||
DraftDeck("lblMyDraftDeck"),
|
||||
Cube("lblPredefinedCube");
|
||||
|
||||
private final String caption;
|
||||
|
||||
StartingPoolType(String caption0) {
|
||||
caption = caption0;
|
||||
caption = Localizer.getInstance().getMessage(caption0);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -296,6 +296,9 @@ public class QuestAchievements {
|
||||
catch(ArrayIndexOutOfBoundsException e) {
|
||||
return null;
|
||||
}
|
||||
catch(IndexOutOfBoundsException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import forge.game.event.GameEventCardSacrificed;
|
||||
import forge.game.event.GameEventCardTapped;
|
||||
import forge.game.event.GameEventFlipCoin;
|
||||
import forge.game.event.GameEventGameOutcome;
|
||||
import forge.game.event.GameEventGameStarted;
|
||||
import forge.game.event.GameEventLandPlayed;
|
||||
import forge.game.event.GameEventManaBurn;
|
||||
import forge.game.event.GameEventPlayerLivesChanged;
|
||||
@@ -47,6 +48,11 @@ public class EventVisualizer extends IGameEventVisitor.Base<SoundEffectType> imp
|
||||
this.player = lobbyPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoundEffectType visit(GameEventGameStarted event) {
|
||||
return SoundEffectType.StartDuel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoundEffectType visit(final GameEventCardDamaged event) { return SoundEffectType.Damage; }
|
||||
@Override
|
||||
|
||||
@@ -83,6 +83,7 @@ public enum SoundEffectType {
|
||||
ScriptedEffect("", false), // Plays the effect defined by SVar:SoundEffect
|
||||
Shuffle("shuffle.wav", false),
|
||||
Sorcery("sorcery.wav", false),
|
||||
StartDuel("start_duel.wav",false),
|
||||
Tap("tap.wav", false),
|
||||
Token("token.wav", true),
|
||||
Untap("untap.wav", true),
|
||||
|
||||
@@ -51,8 +51,12 @@ public class SOptionPane {
|
||||
}
|
||||
|
||||
public static boolean showConfirmDialog(final String message, final String title, final String yesButtonText, final String noButtonText, final boolean defaultYes) {
|
||||
return showConfirmDialog(message, title, yesButtonText, noButtonText, defaultYes, false);
|
||||
}
|
||||
|
||||
public static boolean showConfirmDialog(final String message, final String title, final String yesButtonText, final String noButtonText, final boolean defaultYes, final boolean noicon) {
|
||||
final List<String> options = ImmutableList.of(yesButtonText, noButtonText);
|
||||
final int reply = SOptionPane.showOptionDialog(message, title, QUESTION_ICON, options, defaultYes ? 0 : 1);
|
||||
final int reply = SOptionPane.showOptionDialog(message, title, noicon ? null : QUESTION_ICON, options, defaultYes ? 0 : 1);
|
||||
return (reply == 0);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user