mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Merge remote-tracking branch 'upstream/master' into donot_translation_token
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
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
cd "`dirname \"$0\"`"
|
||||
java -Xmx1024m -jar $project.build.finalName$
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
cd "`dirname \"$0\"`"
|
||||
java -Xmx1024m -jar $project.build.finalName$
|
||||
@@ -6,23 +6,36 @@ 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;
|
||||
private static String deviceName = "";
|
||||
private static String androidRelease = "";
|
||||
private static int androidAPI = 0;
|
||||
private static int deviceRAM = 0;
|
||||
|
||||
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 void setDeviceInfo(String DeviceName, String AndroidName, int AndroidAPI, int RAM) {
|
||||
deviceName = DeviceName;
|
||||
androidRelease = AndroidName;
|
||||
androidAPI = AndroidAPI;
|
||||
deviceRAM = RAM;
|
||||
}
|
||||
public static String getDeviceName() { return deviceName; }
|
||||
public static String getAndroidRelease() { return androidRelease; }
|
||||
public static int getAndroidAPILevel() { return androidAPI; }
|
||||
public static int getDeviceRAM() { return deviceRAM; }
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ import org.w3c.dom.NodeList;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameType;
|
||||
import forge.game.Match;
|
||||
import forge.game.player.Player;
|
||||
import forge.interfaces.IComboBox;
|
||||
import forge.model.FModel;
|
||||
@@ -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();
|
||||
@@ -44,7 +45,7 @@ public abstract class AchievementCollection implements Iterable<Achievement> {
|
||||
return;
|
||||
}
|
||||
|
||||
final Game game = controller.getGame();
|
||||
final Match match = controller.getMatch();
|
||||
final Player player = controller.getPlayer();
|
||||
|
||||
//update all achievements for GUI player after game finished
|
||||
@@ -53,16 +54,16 @@ public abstract class AchievementCollection implements Iterable<Achievement> {
|
||||
ThreadUtil.invokeInGameThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doUpdateAllAchievements(game, player);
|
||||
doUpdateAllAchievements(match, player);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
doUpdateAllAchievements(game, player);
|
||||
doUpdateAllAchievements(match, player);
|
||||
}
|
||||
}
|
||||
|
||||
private static void doUpdateAllAchievements(final Game game, final Player player) {
|
||||
FModel.getAchievements(game.getRules().getGameType()).updateAll(player);
|
||||
private static void doUpdateAllAchievements(final Match match, final Player player) {
|
||||
FModel.getAchievements(match.getRules().getGameType()).updateAll(player);
|
||||
AltWinAchievements.instance.updateAll(player);
|
||||
PlaneswalkerAchievements.instance.updateAll(player);
|
||||
ChallengeAchievements.instance.updateAll(player);
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,17 @@ package forge.achievement;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameType;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class ArcaneMaster extends Achievement {
|
||||
public ArcaneMaster() {
|
||||
super("ArcaneMaster", "Arcane Master", "Win a game without casting", Integer.MAX_VALUE,
|
||||
"more than 3 spells", 3,
|
||||
"more than 2 spells", 2,
|
||||
"more than 1 spell", 1,
|
||||
"any spells", 0);
|
||||
super("ArcaneMaster", Localizer.getInstance().getMessage("lblArcaneMaster"),
|
||||
Localizer.getInstance().getMessage("lblWinGameWithOutCasting"), Integer.MAX_VALUE,
|
||||
Localizer.getInstance().getMessage("lblMore3Spells"), 3,
|
||||
Localizer.getInstance().getMessage("lblMore2Spells"), 2,
|
||||
Localizer.getInstance().getMessage("lblMore1Spell"), 1,
|
||||
Localizer.getInstance().getMessage("lblAnySpells"), 0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -26,6 +29,6 @@ public class ArcaneMaster extends Achievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Spell";
|
||||
return Localizer.getInstance().getMessage("lblSpell");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,19 @@ import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.GameLossReason;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class Blackjack extends Achievement {
|
||||
private static final int THRESHOLD = 21;
|
||||
|
||||
public Blackjack(int silver0, int gold0, int mythic0) {
|
||||
super("Blackjack", "Blackjack", "Win a game from your commander dealing", 0,
|
||||
String.format("%d combat damage", THRESHOLD), THRESHOLD,
|
||||
String.format("%d combat damage", silver0), silver0,
|
||||
String.format("%d combat damage", gold0), gold0,
|
||||
String.format("%d combat damage", mythic0), mythic0);
|
||||
super("Blackjack", Localizer.getInstance().getMessage("lblBlackjack"),
|
||||
Localizer.getInstance().getMessage("lblWinGameFromYourCommanderDealing"), 0,
|
||||
Localizer.getInstance().getMessage("lblNCombatDamage", String.valueOf(THRESHOLD)), THRESHOLD,
|
||||
Localizer.getInstance().getMessage("lblNCombatDamage", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblNCombatDamage", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblNCombatDamage", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,7 +43,7 @@ public class Blackjack extends Achievement {
|
||||
|
||||
@Override
|
||||
public String getNoun() {
|
||||
return "Damage";
|
||||
return Localizer.getInstance().getMessage("lblDamage");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,12 +5,13 @@ import forge.game.Game;
|
||||
import forge.game.GameType;
|
||||
import forge.game.player.Player;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.Localizer;
|
||||
|
||||
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
|
||||
@@ -24,8 +25,14 @@ public class ChallengeAchievements extends AchievementCollection {
|
||||
add(new NoSpells());
|
||||
add(new NoLands());
|
||||
add(new Domain());
|
||||
add("Chromatic", "Chromatic", "Win a game after casting a 5 color spell", "With great color requirements comes great power.");
|
||||
add("Epic", "Epic", "Win a game after resolving a spell with the Epic keyword", "When it's the last spell you ever cast, you better make it count!");
|
||||
add("Chromatic", Localizer.getInstance().getMessage("lblChromatic"),
|
||||
Localizer.getInstance().getMessage("lblWinGameAfterCasting5CSpell"),
|
||||
Localizer.getInstance().getMessage("lblGreatColorComesPower")
|
||||
);
|
||||
add("Epic", Localizer.getInstance().getMessage("lblEpic"),
|
||||
Localizer.getInstance().getMessage("lblWinGameAfterResolvingWithEpicSpell"),
|
||||
Localizer.getInstance().getMessage("lblWhenItsYouLastSpellBetterMakeCount")
|
||||
);
|
||||
}
|
||||
|
||||
private void add(String key0, String displayName0, String description0, String flavorText0) {
|
||||
@@ -39,7 +46,7 @@ public class ChallengeAchievements extends AchievementCollection {
|
||||
|
||||
@Override
|
||||
protected final String getNoun() {
|
||||
return "Win";
|
||||
return Localizer.getInstance().getMessage("lblWin");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +61,7 @@ public class ChallengeAchievements extends AchievementCollection {
|
||||
|
||||
public static abstract class DeckChallengeAchievement extends ChallengeAchievement {
|
||||
protected DeckChallengeAchievement(String key0, String displayName0, String condition0, String flavorText0) {
|
||||
super(key0, displayName0, "Win a game using a deck " + condition0, flavorText0);
|
||||
super(key0, displayName0, Localizer.getInstance().getMessage("lblWinGameUsingTargetDeck", condition0), flavorText0);
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
@@ -3,14 +3,17 @@ package forge.achievement;
|
||||
import forge.game.Game;
|
||||
import forge.game.player.GameLossReason;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class DeckedOut extends Achievement {
|
||||
public DeckedOut(int silver0, int gold0, int mythic0) {
|
||||
super("DeckedOut", "Decked Out", "Win a game from opponent", Integer.MAX_VALUE,
|
||||
"drawing into an empty library", Integer.MAX_VALUE - 1,
|
||||
String.format("drawing into an empty library by turn %d", silver0), silver0,
|
||||
String.format("drawing into an empty library by turn %d", gold0), gold0,
|
||||
String.format("drawing into an empty library by turn %d", mythic0), mythic0);
|
||||
super("DeckedOut", Localizer.getInstance().getMessage("lblDeckedOut"),
|
||||
Localizer.getInstance().getMessage("lblWinGameFromOpponent"), Integer.MAX_VALUE,
|
||||
Localizer.getInstance().getMessage("lblDrawingEmptyLibrary"), Integer.MAX_VALUE - 1,
|
||||
Localizer.getInstance().getMessage("lblDrawingEmptyLibraryByNTurn", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblDrawingEmptyLibraryByNTurn", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblDrawingEmptyLibraryByNTurn", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,10 +9,14 @@ import forge.game.zone.ZoneType;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class Domain extends ProgressiveAchievement {
|
||||
public Domain() {
|
||||
super("Domain", "Domain", "Win a game with one of each basic land on the battlefield", "It's nice being able to cast anything you want.");
|
||||
super("Domain", Localizer.getInstance().getMessage("lblDomain"),
|
||||
Localizer.getInstance().getMessage("lblWinGameWithOneOfEachBasicLandOnTheBattlefield"),
|
||||
Localizer.getInstance().getMessage("lblAbleToCastAnytingNiceBeing")
|
||||
);
|
||||
}
|
||||
|
||||
private HashMap<String, String> basicLandMap = new HashMap<String, String>() {
|
||||
@@ -51,6 +55,6 @@ public class Domain extends ProgressiveAchievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Win";
|
||||
return Localizer.getInstance().getMessage("lblWin");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,14 +2,17 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class GameWinStreak extends StreakAchievement {
|
||||
public GameWinStreak(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("GameWinStreak", "Game Win Streak", null,
|
||||
String.format("Win %d games in a row", bronze0), bronze0,
|
||||
String.format("Win %d games in a row", silver0), silver0,
|
||||
String.format("Win %d games in a row", gold0), gold0,
|
||||
String.format("Win %d games in a row", mythic0), mythic0);
|
||||
super("GameWinStreak",
|
||||
Localizer.getInstance().getMessage("lblGameWinStreak"), null,
|
||||
Localizer.getInstance().getMessage("lblWinNGamesInARow", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblWinNGamesInARow", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblWinNGamesInARow", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblWinNGamesInARow", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,14 +3,17 @@ package forge.achievement;
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class Hellbent extends Achievement {
|
||||
public Hellbent() {
|
||||
super("Hellbent", "Hellbent", "Win a game with no cards in your", 0,
|
||||
"hand", 1,
|
||||
"hand or library", 2,
|
||||
"hand, library, or graveyard", 3,
|
||||
"hand, library, graveyard, or battlefield", 4);
|
||||
super("Hellbent", Localizer.getInstance().getMessage("lblHellbent"),
|
||||
Localizer.getInstance().getMessage("lblWinGameWithNoCardsInYour"), 0,
|
||||
Localizer.getInstance().getMessage("lblHandZone"), 1,
|
||||
Localizer.getInstance().getMessage("lblhandOrlibrary"), 2,
|
||||
Localizer.getInstance().getMessage("lblHandOrLibraryOrGraveyard"), 3,
|
||||
Localizer.getInstance().getMessage("lblHandOrLibraryOrGraveyardOrBattlefield"), 4
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,14 +2,17 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class LifeToSpare extends Achievement {
|
||||
public LifeToSpare(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("LifeToSpare", "Life to Spare", "Win a game with", 0,
|
||||
String.format("%d life more than you started with", bronze0), bronze0,
|
||||
String.format("%d life more than you started with", silver0), silver0,
|
||||
String.format("%d life more than you started with", gold0), gold0,
|
||||
String.format("%d life more than you started with", mythic0), mythic0);
|
||||
super("LifeToSpare", Localizer.getInstance().getMessage("lblLifeToSpare"),
|
||||
Localizer.getInstance().getMessage("lblWinGameWith"), 0,
|
||||
Localizer.getInstance().getMessage("lblMoreThanStartedLifeN", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblMoreThanStartedLifeN", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblMoreThanStartedLifeN", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblMoreThanStartedLifeN", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -25,7 +28,7 @@ public class LifeToSpare extends Achievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Life";
|
||||
return Localizer.getInstance().getMessage("lblLife");
|
||||
}
|
||||
@Override
|
||||
protected boolean pluralizeNoun() {
|
||||
|
||||
@@ -5,14 +5,17 @@ import forge.game.GameType;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class ManaFlooded extends Achievement {
|
||||
public ManaFlooded(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("ManaFlooded", "Mana Flooded", "Win a game with at least", 0,
|
||||
String.format("%d lands on the battlefield", bronze0), bronze0,
|
||||
String.format("%d lands on the battlefield", silver0), silver0,
|
||||
String.format("%d lands on the battlefield", gold0), gold0,
|
||||
String.format("%d lands on the battlefield", mythic0), mythic0);
|
||||
super("ManaFlooded", Localizer.getInstance().getMessage("lblManaFlooded"),
|
||||
Localizer.getInstance().getMessage("lblWinGameWithLeast"), 0,
|
||||
Localizer.getInstance().getMessage("lblNLandOnTheBattlefield", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblNLandOnTheBattlefield", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblNLandOnTheBattlefield", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblNLandOnTheBattlefield", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -34,6 +37,6 @@ public class ManaFlooded extends Achievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Land";
|
||||
return Localizer.getInstance().getMessage("lblLand");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,17 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class ManaScrewed extends Achievement {
|
||||
public ManaScrewed() {
|
||||
super("ManaScrewed", "Mana Screwed", "Win a game despite playing only", Integer.MAX_VALUE,
|
||||
"3 lands", 3,
|
||||
"2 lands", 2,
|
||||
"1 land", 1,
|
||||
"0 lands", 0);
|
||||
super("ManaScrewed", Localizer.getInstance().getMessage("lblManaScrewed"),
|
||||
Localizer.getInstance().getMessage("lblWinGameOnlyPlaing"), Integer.MAX_VALUE,
|
||||
Localizer.getInstance().getMessage("lblNLands", String.valueOf(3)), 3,
|
||||
Localizer.getInstance().getMessage("lblNLands", String.valueOf(2)), 2,
|
||||
Localizer.getInstance().getMessage("lblNLands", String.valueOf(1)), 1,
|
||||
Localizer.getInstance().getMessage("lblNLands", String.valueOf(0)), 0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -22,7 +25,7 @@ public class ManaScrewed extends Achievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Land";
|
||||
return Localizer.getInstance().getMessage("lblLand");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,14 +2,16 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class MatchWinStreak extends StreakAchievement {
|
||||
public MatchWinStreak(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("MatchWinStreak", "Match Win Streak", null,
|
||||
String.format("Win %d matches in a row", bronze0), bronze0,
|
||||
String.format("Win %d matches in a row", silver0), silver0,
|
||||
String.format("Win %d matches in a row", gold0), gold0,
|
||||
String.format("Win %d matches in a row", mythic0), mythic0);
|
||||
super("MatchWinStreak",Localizer.getInstance().getMessage("lblMatchWinStreak"), null,
|
||||
Localizer.getInstance().getMessage("lblWinNMatchesInARow", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblWinNMatchesInARow", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblWinNMatchesInARow", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblWinNMatchesInARow", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,14 +2,16 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class NeedForSpeed extends Achievement {
|
||||
public NeedForSpeed(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("NeedForSpeed", "Need for Speed", null, Integer.MAX_VALUE,
|
||||
String.format("Win a game by turn %d", bronze0), bronze0,
|
||||
String.format("Win a game by turn %d", silver0), silver0,
|
||||
String.format("Win a game by turn %d", gold0), gold0,
|
||||
String.format("Win a game by turn %d", mythic0), mythic0);
|
||||
super("NeedForSpeed", Localizer.getInstance().getMessage("lblNeedForSpeed"), null, Integer.MAX_VALUE,
|
||||
Localizer.getInstance().getMessage("lblWinGameByNTurn", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblWinGameByNTurn", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblWinGameByNTurn", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblWinGameByNTurn", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -22,7 +24,7 @@ public class NeedForSpeed extends Achievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Turn";
|
||||
return Localizer.getInstance().getMessage("lblTurn");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,10 +5,13 @@ import java.util.Map.Entry;
|
||||
import forge.achievement.ChallengeAchievements.DeckChallengeAchievement;
|
||||
import forge.deck.Deck;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class NoCreatures extends DeckChallengeAchievement {
|
||||
public NoCreatures() {
|
||||
super("NoCreatures", "No Creatures", "with no creatures", "I'm not really an animal person.");
|
||||
super("NoCreatures", Localizer.getInstance().getMessage("lblNoCreatures"),
|
||||
Localizer.getInstance().getMessage("lblWithNoCreatures"), Localizer.getInstance().getMessage("lblIMNotReallyAnimalPerson")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,10 +5,14 @@ import java.util.Map.Entry;
|
||||
import forge.achievement.ChallengeAchievements.DeckChallengeAchievement;
|
||||
import forge.deck.Deck;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class NoLands extends DeckChallengeAchievement {
|
||||
public NoLands() {
|
||||
super("NoLands", "No Lands", "with no lands", "I prefer mana from more artificial sources.");
|
||||
super("NoLands", Localizer.getInstance().getMessage("lblNoLands"),
|
||||
Localizer.getInstance().getMessage("lblWithNoLands"),
|
||||
Localizer.getInstance().getMessage("lblIMorePreferManaFromArtificial")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,10 +6,14 @@ import forge.achievement.ChallengeAchievements.DeckChallengeAchievement;
|
||||
import forge.card.CardType;
|
||||
import forge.deck.Deck;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class NoSpells extends DeckChallengeAchievement {
|
||||
public NoSpells() {
|
||||
super("NoSpells", "No Spells", "with only creatures and lands", "I let my army do the talking.");
|
||||
super("NoSpells", Localizer.getInstance().getMessage("lblNoSpells"),
|
||||
Localizer.getInstance().getMessage("lblWithOnlyCreaturesAndLands"),
|
||||
Localizer.getInstance().getMessage("lblILetMyArmyTalking")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,14 +2,17 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class Overkill extends Achievement {
|
||||
public Overkill(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("Overkill", "Overkill", "Win a game with opponent at", 0,
|
||||
String.format("%d life", bronze0), bronze0,
|
||||
String.format("%d life", silver0), silver0,
|
||||
String.format("%d life", gold0), gold0,
|
||||
String.format("%d life", mythic0), mythic0);
|
||||
super("Overkill", Localizer.getInstance().getMessage("lblOverkill"),
|
||||
Localizer.getInstance().getMessage("lblWinGameWithOppentAt"), 0,
|
||||
Localizer.getInstance().getMessage("lblNLife", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblNLife", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblNLife", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblNLife", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -25,7 +28,7 @@ public class Overkill extends Achievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Life";
|
||||
return Localizer.getInstance().getMessage("lblLife");
|
||||
}
|
||||
@Override
|
||||
protected boolean pluralizeNoun() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,6 +8,8 @@ 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 PlaneswalkerAchievements extends AchievementCollection {
|
||||
public static final PlaneswalkerAchievements instance = new PlaneswalkerAchievements();
|
||||
@@ -17,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
|
||||
@@ -49,7 +51,7 @@ public class PlaneswalkerAchievements extends AchievementCollection {
|
||||
|
||||
private class PlaneswalkerUltimate extends ProgressiveAchievement {
|
||||
private PlaneswalkerUltimate(String cardName0, String displayName0, String flavorText0) {
|
||||
super(cardName0, displayName0, "Win a game after activating " + cardName0 + "'s ultimate", flavorText0);
|
||||
super(cardName0, displayName0, Localizer.getInstance().getMessage("lblWinGameAfterActivatingCardUltimate", CardTranslation.getTranslatedName(cardName0)), flavorText0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,7 +66,7 @@ public class PlaneswalkerAchievements extends AchievementCollection {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Win";
|
||||
return Localizer.getInstance().getMessage("lblWin");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ package forge.achievement;
|
||||
import forge.game.Game;
|
||||
import forge.game.player.GameLossReason;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class Poisoned extends Achievement {
|
||||
private static final int THRESHOLD = 10;
|
||||
|
||||
public Poisoned(int silver0, int gold0, int mythic0) {
|
||||
super("Poisoned", "Poisoned", "Win a game by giving opponent", 0,
|
||||
String.format("%d poison counters", THRESHOLD), THRESHOLD,
|
||||
String.format("%d poison counters", silver0), silver0,
|
||||
String.format("%d poison counters", gold0), gold0,
|
||||
String.format("%d poison counters", mythic0), mythic0);
|
||||
super("Poisoned", Localizer.getInstance().getMessage("lblPoisoned"),
|
||||
Localizer.getInstance().getMessage("lblWinGameByGivingOppoent"), 0,
|
||||
Localizer.getInstance().getMessage("lblNPoisonCounters", String.valueOf(THRESHOLD)), THRESHOLD,
|
||||
Localizer.getInstance().getMessage("lblNPoisonCounters", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblNPoisonCounters", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblNPoisonCounters", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,6 +31,6 @@ public class Poisoned extends Achievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Counter";
|
||||
return Localizer.getInstance().getMessage("lblCounter");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,14 +2,17 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class RagsToRiches extends Achievement {
|
||||
public RagsToRiches() {
|
||||
super("RagsToRiches", "Rags to Riches", "Win a game after mulliganing to", 7,
|
||||
"4 cards", 4,
|
||||
"3 cards", 3,
|
||||
"2 cards", 2,
|
||||
"1 card", 1);
|
||||
super("RagsToRiches", Localizer.getInstance().getMessage("lblRagsToRiches"),
|
||||
Localizer.getInstance().getMessage("lblWinGameAfterMulliganingTo"), 7,
|
||||
Localizer.getInstance().getMessage("lblNCards", String.valueOf(4)), 4,
|
||||
Localizer.getInstance().getMessage("lblNCards", String.valueOf(3)), 3,
|
||||
Localizer.getInstance().getMessage("lblNCards", String.valueOf(2)), 2,
|
||||
Localizer.getInstance().getMessage("lblNCards", String.valueOf(1)), 1
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -22,6 +25,6 @@ public class RagsToRiches extends Achievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Card";
|
||||
return Localizer.getInstance().getMessage("lblCard");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,14 +2,17 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class StormChaser extends Achievement {
|
||||
public StormChaser(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("StormChaser", "Storm Chaser", "Win a game after casting", 0,
|
||||
String.format("%d spells in a single turn", bronze0), bronze0,
|
||||
String.format("%d spells in a single turn", silver0), silver0,
|
||||
String.format("%d spells in a single turn", gold0), gold0,
|
||||
String.format("%d spells in a single turn", mythic0), mythic0);
|
||||
super("StormChaser", Localizer.getInstance().getMessage("lblStormChaser"),
|
||||
Localizer.getInstance().getMessage("lblWinGameAfterCasting"), 0,
|
||||
Localizer.getInstance().getMessage("lblNSpellInSingleTurn", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblNSpellInSingleTurn", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblNSpellInSingleTurn", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblNSpellInSingleTurn", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -22,6 +25,6 @@ public class StormChaser extends Achievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Spell";
|
||||
return Localizer.getInstance().getMessage("lblSpell");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class TotalGameWins extends ProgressiveAchievement {
|
||||
public TotalGameWins(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("TotalGameWins", "Total Game Wins", null,
|
||||
String.format("Win %d games", bronze0), bronze0,
|
||||
String.format("Win %d games", silver0), silver0,
|
||||
String.format("Win %d games", gold0), gold0,
|
||||
String.format("Win %d games", mythic0), mythic0);
|
||||
super("TotalGameWins", Localizer.getInstance().getMessage("lblTotalGameWins"), null,
|
||||
Localizer.getInstance().getMessage("lblWinNGames", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblWinNGames", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblWinNGames", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblWinNGames", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -19,6 +21,6 @@ public class TotalGameWins extends ProgressiveAchievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Game";
|
||||
return Localizer.getInstance().getMessage("lblGame");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class TotalMatchWins extends ProgressiveAchievement {
|
||||
public TotalMatchWins(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("TotalMatchWins", "Total Match Wins", null,
|
||||
String.format("Win %d matches", bronze0), bronze0,
|
||||
String.format("Win %d matches", silver0), silver0,
|
||||
String.format("Win %d matches", gold0), gold0,
|
||||
String.format("Win %d matches", mythic0), mythic0);
|
||||
super("TotalMatchWins", Localizer.getInstance().getMessage("lblTotalMatchWins"), null,
|
||||
Localizer.getInstance().getMessage("lblWinNMatches", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblWinNMatches", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblWinNMatches", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblWinNMatches", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -19,6 +21,6 @@ public class TotalMatchWins extends ProgressiveAchievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Match";
|
||||
return Localizer.getInstance().getMessage("lblMatch");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ package forge.achievement;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class TotalPuzzlesSolved extends ProgressiveAchievement {
|
||||
public TotalPuzzlesSolved(int bronze0, int silver0, int gold0, int mythic0) {
|
||||
super("TotalPuzzlesSolved", "Total Puzzles Solved", null,
|
||||
String.format(bronze0 == 1 ? "Solve a puzzle" : "Solve %d puzzles", bronze0), bronze0,
|
||||
String.format("Solve %d puzzles", silver0), silver0,
|
||||
String.format("Solve %d puzzles", gold0), gold0,
|
||||
String.format("Solve %d puzzles", mythic0), mythic0);
|
||||
super("TotalPuzzlesSolved", Localizer.getInstance().getMessage("lblTotalPuzzlesSolved"), null,
|
||||
Localizer.getInstance().getMessage("lblSolveNPuzzles", String.valueOf(bronze0)), bronze0,
|
||||
Localizer.getInstance().getMessage("lblSolveNPuzzles", String.valueOf(silver0)), silver0,
|
||||
Localizer.getInstance().getMessage("lblSolveNPuzzles", String.valueOf(gold0)), gold0,
|
||||
Localizer.getInstance().getMessage("lblSolveNPuzzles", String.valueOf(mythic0)), mythic0
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -19,7 +21,7 @@ public class TotalPuzzlesSolved extends ProgressiveAchievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Puzzle";
|
||||
return Localizer.getInstance().getMessage("lblPuzzle");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,17 +3,17 @@ package forge.achievement;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameType;
|
||||
import forge.game.player.Player;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class VariantWins extends ProgressiveAchievement {
|
||||
private GameType variant;
|
||||
|
||||
public VariantWins(GameType variant0, int silver0, int gold0, int mythic0) {
|
||||
super(variant0.name(), variant0.toString(), null,
|
||||
"Win " + Lang.nounWithAmount(1, variant0.toString() + " game"), 1,
|
||||
"Win " + Lang.nounWithAmount(silver0, variant0.toString() + " game"), silver0,
|
||||
"Win " + Lang.nounWithAmount(gold0, variant0.toString() + " game"), gold0,
|
||||
"Win " + Lang.nounWithAmount(mythic0, variant0.toString() + " game"), mythic0);
|
||||
Localizer.getInstance().getMessage("lblWinNVariantGame", 1, variant0.toString()), 1,
|
||||
Localizer.getInstance().getMessage("lblWinNVariantGame", silver0, variant0.toString()), silver0,
|
||||
Localizer.getInstance().getMessage("lblWinNVariantGame", gold0, variant0.toString()), gold0,
|
||||
Localizer.getInstance().getMessage("lblWinNVariantGame", mythic0, variant0.toString()), mythic0);
|
||||
variant = variant0;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,6 @@ public class VariantWins extends ProgressiveAchievement {
|
||||
|
||||
@Override
|
||||
protected String getNoun() {
|
||||
return "Win";
|
||||
return Localizer.getInstance().getMessage("lblWin");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +48,22 @@ public enum FSkinProp {
|
||||
|
||||
//zone images
|
||||
IMG_ZONE_HAND (new int[] {280, 40, 40, 40}, PropType.IMAGE),
|
||||
IMG_HDZONE_HAND (new int[] {2, 136, 128, 128}, PropType.BUTTONS),
|
||||
|
||||
IMG_ZONE_LIBRARY (new int[] {280, 0, 40, 40}, PropType.IMAGE),
|
||||
IMG_HDZONE_LIBRARY (new int[] {132, 136, 128, 128}, PropType.BUTTONS),
|
||||
|
||||
IMG_ZONE_EXILE (new int[] {320, 40, 40, 40}, PropType.IMAGE),
|
||||
IMG_HDZONE_EXILE (new int[] {262, 136, 128, 128}, PropType.BUTTONS),
|
||||
|
||||
IMG_ZONE_FLASHBACK (new int[] {280, 80, 40, 40}, PropType.IMAGE),
|
||||
IMG_HDZONE_FLASHBACK (new int[] {262, 6, 128, 128}, PropType.BUTTONS),
|
||||
|
||||
IMG_ZONE_GRAVEYARD (new int[] {320, 0, 40, 40}, PropType.IMAGE),
|
||||
IMG_HDZONE_GRAVEYARD (new int[] {132, 6, 128, 128}, PropType.BUTTONS),
|
||||
|
||||
IMG_HDZONE_MANAPOOL (new int[] {2, 6, 128, 128}, PropType.BUTTONS),
|
||||
|
||||
IMG_ZONE_POISON (new int[] {320, 80, 40, 40}, PropType.IMAGE),
|
||||
|
||||
//mana images
|
||||
@@ -170,6 +182,16 @@ public enum FSkinProp {
|
||||
ICO_ARCSON (new int[] {320, 800, 80, 80}, PropType.ICON),
|
||||
ICO_ARCSHOVER (new int[] {400, 800, 80, 80}, PropType.ICON),
|
||||
|
||||
//choice-search-misc
|
||||
ICO_HDCHOICE (new int[] {2, 1792, 128, 128}, PropType.BUTTONS),
|
||||
ICO_HDSIDEBOARD (new int[] {132, 1792, 128, 128}, PropType.BUTTONS),
|
||||
ICO_HDPREFERENCE (new int[] {262, 1792, 128, 128}, PropType.BUTTONS),
|
||||
ICO_HDIMPORT (new int[] {2, 1922, 128, 128}, PropType.BUTTONS),
|
||||
ICO_HDEXPORT (new int[] {132, 1922, 128, 128}, PropType.BUTTONS),
|
||||
ICO_HDYIELD (new int[] {262, 1922, 128, 128}, PropType.BUTTONS),
|
||||
ICO_BLANK (new int[] {2, 2, 2, 2}, PropType.ICON), //safe coords, lower than 2 will cause crash on desktop
|
||||
IMG_LANDLOGO (new int[] {84, 822, 80, 80}, PropType.MANAICONS),
|
||||
|
||||
//quest icons
|
||||
ICO_QUEST_ZEP (new int[] {0, 480, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_GEAR (new int[] {80, 480, 80, 80}, PropType.ICON),
|
||||
@@ -192,31 +214,82 @@ public enum FSkinProp {
|
||||
ICO_QUEST_MINUS (new int[] {560, 640, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_PLUS (new int[] {480, 640, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_PLUSPLUS (new int[] {480, 720, 80, 80}, PropType.ICON),
|
||||
ICO_QUEST_BIG_ELIXIR (new int[] {0, 880, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_BREW (new int[] {160, 880, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_BM (new int[] {320, 880, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_STAKES (new int[] {480, 880, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_HOUSE (new int[] {0, 1040, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_COIN (new int[] {160, 1040, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_BOOK (new int[] {320, 1040, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_MAP (new int[] {480, 1040, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_ZEP (new int[] {0, 1200, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_CHARM (new int[] {160, 1200, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_BOOTS (new int[] {320, 1200, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_SHIELD (new int[] {480, 1200, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_ARMOR (new int[] {0, 1360, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_AXE (new int[] {160, 1360, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_SWORD (new int[] {320, 1360, 160, 160}, PropType.ICON),
|
||||
ICO_QUEST_BIG_BAG (new int[] {480, 1360, 160, 160}, PropType.ICON),
|
||||
|
||||
//menu icon
|
||||
ICO_MENU_GALAXY (new int[] {0, 1520, 80, 80}, PropType.ICON),
|
||||
ICO_MENU_STATS (new int[] {80, 1520, 80, 80}, PropType.ICON),
|
||||
ICO_MENU_PUZZLE (new int[] {160, 1520, 80, 80}, PropType.ICON),
|
||||
ICO_MENU_GAUNTLET (new int[] {240, 1520, 80, 80}, PropType.ICON),
|
||||
ICO_MENU_SEALED (new int[] {320, 1520, 80, 80}, PropType.ICON),
|
||||
ICO_MENU_DRAFT (new int[] {400, 1520, 80, 80}, PropType.ICON),
|
||||
ICO_MENU_CONSTRUCTED (new int[] {480, 1520, 80, 80}, PropType.ICON),
|
||||
|
||||
//interface icons
|
||||
ICO_QUESTION (new int[] {560, 800, 32, 32}, PropType.ICON),
|
||||
ICO_INFORMATION (new int[] {592, 800, 32, 32}, PropType.ICON),
|
||||
ICO_WARNING (new int[] {560, 832, 32, 32}, PropType.ICON),
|
||||
ICO_ERROR (new int[] {592, 832, 32, 32}, PropType.ICON),
|
||||
|
||||
ICO_DELETE (new int[] {640, 480, 20, 20}, PropType.ICON),
|
||||
ICO_HDDELETE (new int[] {392, 134, 64, 64}, PropType.BUTTONS),
|
||||
|
||||
ICO_DELETE_OVER (new int[] {660, 480, 20, 20}, PropType.ICON),
|
||||
|
||||
ICO_EDIT (new int[] {640, 500, 20, 20}, PropType.ICON),
|
||||
ICO_HDEDIT (new int[] {392, 200, 64, 64}, PropType.BUTTONS),
|
||||
|
||||
ICO_EDIT_OVER (new int[] {660, 500, 20, 20}, PropType.ICON),
|
||||
|
||||
ICO_OPEN (new int[] {660, 520, 20, 20}, PropType.ICON),
|
||||
ICO_HDOPEN (new int[] {392, 68, 64, 64}, PropType.BUTTONS),
|
||||
|
||||
ICO_MINUS (new int[] {660, 620, 20, 20}, PropType.ICON),
|
||||
ICO_HDMINUS (new int[] {391, 1506, 64, 64}, PropType.BUTTONS),
|
||||
|
||||
ICO_NEW (new int[] {660, 540, 20, 20}, PropType.ICON),
|
||||
|
||||
ICO_PLUS (new int[] {660, 600, 20, 20}, PropType.ICON),
|
||||
ICO_HDPLUS (new int[] {391, 1572, 64, 64}, PropType.BUTTONS),
|
||||
|
||||
ICO_PRINT (new int[] {660, 640, 20, 20}, PropType.ICON),
|
||||
|
||||
ICO_SAVE (new int[] {660, 560, 20, 20}, PropType.ICON),
|
||||
ICO_HDSAVE (new int[] {391, 1704, 64, 64}, PropType.BUTTONS),
|
||||
ICO_SAVEAS (new int[] {660, 580, 20, 20}, PropType.ICON),
|
||||
ICO_HDSAVEAS (new int[] {391, 1638, 64, 64}, PropType.BUTTONS),
|
||||
|
||||
ICO_CLOSE (new int[] {640, 640, 20, 20}, PropType.ICON),
|
||||
ICO_LIST (new int[] {640, 660, 20, 20}, PropType.ICON),
|
||||
ICO_CARD_IMAGE (new int[] {660, 660, 20, 20}, PropType.ICON),
|
||||
|
||||
ICO_FOLDER (new int[] {640, 680, 20, 20}, PropType.ICON),
|
||||
ICO_HDFOLDER (new int[] {392, 2, 64, 64}, PropType.BUTTONS),
|
||||
|
||||
ICO_SEARCH (new int[] {660, 680, 20, 20}, PropType.ICON),
|
||||
ICO_HDSEARCH (new int[] {391, 1374, 64, 64}, PropType.BUTTONS),
|
||||
|
||||
ICO_UNKNOWN (new int[] {0, 720, 80, 80}, PropType.ICON),
|
||||
ICO_LOGO (new int[] {480, 0, 200, 200}, PropType.ICON),
|
||||
|
||||
ICO_FLIPCARD (new int[] {400, 0, 80, 120}, PropType.ICON),
|
||||
ICO_HDFLIPCARD (new int[] {2, 1268, 387, 500}, PropType.BUTTONS),
|
||||
|
||||
ICO_FAVICON (new int[] {0, 640, 80, 80}, PropType.ICON),
|
||||
ICO_LOCK (new int[] {620, 800, 48, 48}, PropType.ICON),
|
||||
|
||||
@@ -229,14 +302,18 @@ public enum FSkinProp {
|
||||
IMG_CUR_TAB (new int[] {644, 764, 32, 32}, PropType.IMAGE),
|
||||
|
||||
//editor images
|
||||
IMG_STAR_OUTINE (new int[] {640, 460, 20, 20}, PropType.IMAGE),
|
||||
IMG_STAR_OUTLINE (new int[] {640, 460, 20, 20}, PropType.IMAGE),
|
||||
IMG_HDSTAR_OUTLINE (new int[] {391, 1308, 64, 64}, PropType.BUTTONS),
|
||||
IMG_STAR_FILLED (new int[] {660, 460, 20, 20}, PropType.IMAGE),
|
||||
IMG_HDSTAR_FILLED (new int[] {391, 1440, 64, 64}, PropType.BUTTONS),
|
||||
|
||||
IMG_ARTIFACT (new int[] {412, 658, 80, 80}, PropType.MANAICONS),
|
||||
IMG_CREATURE (new int[] {2, 740, 80, 80}, PropType.MANAICONS),
|
||||
IMG_ENCHANTMENT (new int[] {84, 740, 80, 80}, PropType.MANAICONS),
|
||||
IMG_INSTANT (new int[] {166, 740, 80, 80}, PropType.MANAICONS),
|
||||
IMG_LAND (new int[] {248, 740, 80, 80}, PropType.MANAICONS),
|
||||
IMG_MULTI (new int[] {80, 720, 40, 40}, PropType.IMAGE),
|
||||
IMG_HDMULTI (new int[] {2, 822, 80, 80}, PropType.MANAICONS),
|
||||
IMG_PLANESWALKER (new int[] {330, 740, 80, 80}, PropType.MANAICONS),
|
||||
IMG_PACK (new int[] {80, 760, 40, 40}, PropType.IMAGE),
|
||||
IMG_SORCERY (new int[] {412, 740, 80, 80}, PropType.MANAICONS),
|
||||
@@ -284,6 +361,28 @@ public enum FSkinProp {
|
||||
IMG_BTN_DISABLED_LEFT (new int[] {80, 200, 40, 40}, PropType.ICON),
|
||||
IMG_BTN_DISABLED_CENTER (new int[] {120, 200, 1, 40}, PropType.ICON),
|
||||
IMG_BTN_DISABLED_RIGHT (new int[] {160, 200, 40, 40}, PropType.ICON),
|
||||
//hd buttons
|
||||
IMG_HDBTN_START_UP (new int[] {2, 2, 588, 312}, PropType.BTNSTART),
|
||||
IMG_HDBTN_START_OVER (new int[] {1183, 2, 588, 312}, PropType.BTNSTART),
|
||||
IMG_HDBTN_START_DOWN (new int[] {593, 2, 588, 312}, PropType.BTNSTART),
|
||||
IMG_HDBTN_UP_LEFT (new int[] {2, 266, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_UP_CENTER (new int[] {162, 266, 1, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_UP_RIGHT (new int[] {322, 266, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_OVER_LEFT (new int[] {2, 433, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_OVER_CENTER (new int[] {162, 433, 1, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_OVER_RIGHT (new int[] {322, 433, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_DOWN_LEFT (new int[] {2, 600, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_DOWN_CENTER (new int[] {162, 600, 1, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_DOWN_RIGHT (new int[] {322, 600, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_FOCUS_LEFT (new int[] {2, 767, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_FOCUS_CENTER (new int[] {162, 767, 1, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_FOCUS_RIGHT (new int[] {322, 767, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_TOGGLE_LEFT (new int[] {2, 934, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_TOGGLE_CENTER (new int[] {162, 934, 1, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_TOGGLE_RIGHT (new int[] {322, 934, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_DISABLED_LEFT (new int[] {2, 1101, 160, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_DISABLED_CENTER (new int[] {162, 1101, 1, 165}, PropType.BUTTONS),
|
||||
IMG_HDBTN_DISABLED_RIGHT (new int[] {322, 1101, 160, 165}, PropType.BUTTONS),
|
||||
|
||||
IMG_FAV1 (new int[] {0, 0, 100, 100}, PropType.FAVICON),
|
||||
IMG_FAV2 (new int[] {100, 0, 100, 100}, PropType.FAVICON),
|
||||
@@ -293,18 +392,22 @@ public enum FSkinProp {
|
||||
IMG_FAVNONE (new int[] {500, 0, 100, 100}, PropType.FAVICON),
|
||||
|
||||
IMG_QUEST_DRAFT_DECK (new int[] {0, 0, 680, 475}, PropType.IMAGE),
|
||||
|
||||
//COMMANDER
|
||||
IMG_ABILITY_COMMANDER (new int[] {330, 576, 80, 80}, PropType.ABILITY),
|
||||
//Ability Icons
|
||||
IMG_ABILITY_DEATHTOUCH (new int[] {2, 2, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_DEFENDER (new int[] {84, 2, 80, 80}, PropType.ABILITY),
|
||||
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),
|
||||
IMG_ABILITY_HORSEMANSHIP (new int[] {2, 576, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_INDESTRUCTIBLE (new int[] {2, 84, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_INTIMIDATE (new int[] {166, 412, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_LANDWALK (new int[] {248, 576, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_LIFELINK (new int[] {84, 84, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_MENACE (new int[] {166, 84, 80, 80}, PropType.ABILITY),
|
||||
IMG_ABILITY_REACH (new int[] {248, 330, 80, 80}, PropType.ABILITY),
|
||||
@@ -378,6 +481,8 @@ public enum FSkinProp {
|
||||
TROPHY,
|
||||
ABILITY,
|
||||
BORDERS,
|
||||
BUTTONS,
|
||||
BTNSTART,
|
||||
MANAICONS,
|
||||
PLANAR_CONQUEST,
|
||||
FAVICON
|
||||
|
||||
@@ -285,7 +285,10 @@ public class CardDetailUtil {
|
||||
if (card.getCloneOrigin() == null)
|
||||
needTranslation = false;
|
||||
}
|
||||
String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null);
|
||||
final String text = !card.isSplitCard() ?
|
||||
card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null) :
|
||||
card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null );
|
||||
|
||||
|
||||
// LEVEL [0-9]+-[0-9]+
|
||||
// LEVEL [0-9]+\+
|
||||
@@ -398,6 +401,9 @@ public class CardDetailUtil {
|
||||
}
|
||||
area.append("(chosen type: ");
|
||||
area.append(card.getChosenType());
|
||||
if (!card.getChosenType2().isEmpty()) {
|
||||
area.append(", ").append(card.getChosenType2());
|
||||
}
|
||||
area.append(")");
|
||||
}
|
||||
|
||||
@@ -421,6 +427,14 @@ public class CardDetailUtil {
|
||||
area.append(")");
|
||||
}
|
||||
|
||||
// chosen number
|
||||
if (!card.getChosenNumber().isEmpty()) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n");
|
||||
}
|
||||
area.append("(chosen number: ").append(card.getChosenNumber()).append(")");
|
||||
}
|
||||
|
||||
// chosen player
|
||||
if (card.getChosenPlayer() != null) {
|
||||
if (area.length() != 0) {
|
||||
@@ -447,6 +461,9 @@ public class CardDetailUtil {
|
||||
area.append("Hidden");
|
||||
} else {
|
||||
area.append(card.getNamedCard());
|
||||
if (!card.getNamedCard2().isEmpty()) {
|
||||
area.append(", ").append(card.getNamedCard2());
|
||||
}
|
||||
}
|
||||
area.append(")");
|
||||
}
|
||||
@@ -563,6 +580,14 @@ public class CardDetailUtil {
|
||||
area.append("Current Storm Count: ").append(gameView.getStormCount());
|
||||
}
|
||||
}
|
||||
|
||||
//show owner if being controlled by a different player
|
||||
if (card.getOwner() != card.getController()) {
|
||||
if (area.length() != 0) {
|
||||
area.append("\n\n");
|
||||
}
|
||||
area.append("Owner: ").append(card.getOwner().toString());
|
||||
}
|
||||
return area.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ public final class CardScriptParser {
|
||||
"IsTargetingSource", "sharesPermanentTypeWith",
|
||||
"canProduceSameManaTypeWith", "SecondSpellCastThisTurn",
|
||||
"ThisTurnCast", "withFlashback", "tapped", "untapped", "faceDown",
|
||||
"faceUp", "hasLevelUp", "DrawnThisTurn",
|
||||
"faceUp", "hasLevelUp", "DrawnThisTurn", "notDrawnThisTurn",
|
||||
"enteredBattlefieldThisTurn", "notEnteredBattlefieldThisTurn",
|
||||
"firstTurnControlled", "notFirstTurnControlled",
|
||||
"startedTheTurnUntapped", "attackedOrBlockedSinceYourLastUpkeep",
|
||||
@@ -446,7 +446,7 @@ public final class CardScriptParser {
|
||||
"hasActivatedAbility", "hasManaAbility",
|
||||
"hasNonManaActivatedAbility", "NoAbilities", "HasCounters",
|
||||
"wasNotCast", "ChosenType", "IsNotChosenType", "IsCommander",
|
||||
"IsRenowned", "IsNotRenowned");
|
||||
"IsNotCommander","IsRenowned", "IsNotRenowned");
|
||||
private static final Set<String> VALID_EXCLUSIVE_STARTSWITH = ImmutableSortedSet
|
||||
.of("named", "notnamed", "OwnedBy", "ControlledBy",
|
||||
"ControllerControls", "AttachedTo", "EnchantedBy",
|
||||
|
||||
@@ -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;
|
||||
@@ -199,7 +198,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
final List<CardView> options = Lists.newArrayList();
|
||||
for (final Entry<Player, Card> kv : ev.cards.entries()) {
|
||||
//use fake card so real cards appear with proper formatting
|
||||
final CardView fakeCard = new CardView(-1, null, " -- From " + Lang.getPossesive(kv.getKey().getName()) + " deck --");
|
||||
final CardView fakeCard = new CardView(-1, null, " -- From " + Lang.getInstance().getPossesive(kv.getKey().getName()) + " deck --");
|
||||
options.add(fakeCard);
|
||||
options.add(kv.getValue().getView());
|
||||
}
|
||||
@@ -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,34 @@ 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);
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventSubgameEnd event) {
|
||||
if (event.maingame != null) {
|
||||
for (Player p : event.maingame.getPlayers()) {
|
||||
updateZone(p, ZoneType.Battlefield);
|
||||
updateZone(p, ZoneType.Hand);
|
||||
updateZone(p, ZoneType.Graveyard);
|
||||
updateZone(p, ZoneType.Exile);
|
||||
updateZone(p, ZoneType.Command);
|
||||
}
|
||||
};
|
||||
GuiBase.getInterface().invokeInEdtLater(notifyStackAddition);
|
||||
|
||||
return processEvent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -288,7 +308,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 +376,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);
|
||||
}
|
||||
@@ -377,25 +411,35 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
public Void visit(final GameEventPlayerStatsChanged event) {
|
||||
final CardCollection cards = new CardCollection();
|
||||
for (final Player p : event.players) {
|
||||
cards.addAll(p.getAllCards());
|
||||
if (event.updateCards) {
|
||||
cards.addAll(p.getAllCards());
|
||||
}
|
||||
processPlayer(p, livesUpdate);
|
||||
}
|
||||
|
||||
return processCards(cards, cardsRefreshDetails);
|
||||
}
|
||||
|
||||
public Void visit(final GameEventLandPlayed event) {
|
||||
processPlayer(event.player, livesUpdate);
|
||||
matchController.handleLandPlayed(event.land);
|
||||
return processCard(event.land, cardsRefreshDetails);
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
@@ -26,7 +26,7 @@ public final class CardRelationMatrixGenerator {
|
||||
|
||||
public static HashMap<String,HashMap<String,List<Map.Entry<PaperCard,Integer>>>> cardPools = new HashMap<>();
|
||||
|
||||
public static Map<String, Map<String,List<List<String>>>> ldaPools = new HashMap();
|
||||
public static Map<String, Map<String,List<List<String>>>> ldaPools = new HashMap<>();
|
||||
/**
|
||||
To ensure that only cards with at least 14 connections (as 14*4+4=60) are included in the card based deck
|
||||
generation pools
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package forge.deck;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import forge.card.CardSplitType;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
@@ -260,6 +264,29 @@ public class DeckProxy implements InventoryItem {
|
||||
return highestRarity;
|
||||
}
|
||||
|
||||
public PaperCard getHighestCMCCard() {
|
||||
PaperCard key = null;
|
||||
Map<PaperCard, Integer> keyCMC = new HashMap<>(64);
|
||||
|
||||
for (final Entry <PaperCard, Integer> pc : getDeck().getAllCardsInASinglePool()) {
|
||||
if (pc.getKey().getRules().getManaCost() != null) {
|
||||
if (pc.getKey().getRules().getSplitType() != CardSplitType.Split)
|
||||
keyCMC.put(pc.getKey(),pc.getKey().getRules().getManaCost().getCMC());
|
||||
}
|
||||
}
|
||||
|
||||
if (!keyCMC.isEmpty()) {
|
||||
int max = Collections.max(keyCMC.values());
|
||||
//get any max cmc
|
||||
for (Entry<PaperCard, Integer> entry : keyCMC.entrySet()) {
|
||||
if (entry.getValue()==max) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public Set<GameFormat> getFormats() {
|
||||
if (formats == null) {
|
||||
formats = FModel.getFormats().getAllFormatsOfDeck(getDeck());
|
||||
@@ -373,6 +400,15 @@ public class DeckProxy implements InventoryItem {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Iterable<DeckProxy> getAllCommanderPreconDecks() {
|
||||
return getAllCommanderPreconDecks(null);
|
||||
}
|
||||
public static Iterable<DeckProxy> getAllCommanderPreconDecks(final Predicate<Deck> filter) {
|
||||
final List<DeckProxy> result = new ArrayList<DeckProxy>();
|
||||
addDecksRecursivelly("Commander Precon", GameType.Commander, result, "", FModel.getDecks().getCommanderPrecons(), filter);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Iterable<DeckProxy> getAllTinyLeadersDecks() {
|
||||
return getAllTinyLeadersDecks(null);
|
||||
}
|
||||
@@ -588,6 +624,45 @@ public class DeckProxy implements InventoryItem {
|
||||
return decks;
|
||||
}
|
||||
|
||||
public static List<DeckProxy> getNetArchiveStandardDecks(final NetDeckArchiveStandard category) {
|
||||
final List<DeckProxy> decks = new ArrayList<>();
|
||||
if (category != null) {
|
||||
addDecksRecursivelly("Constructed", GameType.Constructed, decks, "", category, null);
|
||||
}
|
||||
return decks;
|
||||
}
|
||||
|
||||
public static List<DeckProxy> getNetArchiveModernDecks(final NetDeckArchiveModern category) {
|
||||
final List<DeckProxy> decks = new ArrayList<>();
|
||||
if (category != null) {
|
||||
addDecksRecursivelly("Constructed", GameType.Constructed, decks, "", category, null);
|
||||
}
|
||||
return decks;
|
||||
}
|
||||
public static List<DeckProxy> getNetArchivePioneerDecks(final NetDeckArchivePioneer category) {
|
||||
final List<DeckProxy> decks = new ArrayList<>();
|
||||
if (category != null) {
|
||||
addDecksRecursivelly("Constructed", GameType.Constructed, decks, "", category, null);
|
||||
}
|
||||
return decks;
|
||||
}
|
||||
|
||||
public static List<DeckProxy> getNetArchiveLegacyDecks(final NetDeckArchiveLegacy category) {
|
||||
final List<DeckProxy> decks = new ArrayList<>();
|
||||
if (category != null) {
|
||||
addDecksRecursivelly("Constructed", GameType.Constructed, decks, "", category, null);
|
||||
}
|
||||
return decks;
|
||||
}
|
||||
|
||||
public static List<DeckProxy> getNetArchiveVintageDecks(final NetDeckArchiveVintage category) {
|
||||
final List<DeckProxy> decks = new ArrayList<>();
|
||||
if (category != null) {
|
||||
addDecksRecursivelly("Constructed", GameType.Constructed, decks, "", category, null);
|
||||
}
|
||||
return decks;
|
||||
}
|
||||
|
||||
public static CardEdition getDefaultLandSet(Deck deck) {
|
||||
List<CardEdition> availableEditions = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ public enum DeckType {
|
||||
DRAFT_DECK("lblDraftDecks"),
|
||||
SEALED_DECK("lblSealedDecks"),
|
||||
PRECONSTRUCTED_DECK("lblPreconstructedDecks"),
|
||||
PRECON_COMMANDER_DECK("lblPreconCommanderDecks"),
|
||||
QUEST_OPPONENT_DECK("lblQuestOpponentDecks"),
|
||||
COLOR_DECK("lblRandomColorDecks"),
|
||||
STANDARD_CARDGEN_DECK("lblRandomStandardArchetypeDecks"),
|
||||
@@ -29,7 +30,12 @@ public enum DeckType {
|
||||
THEME_DECK("lblRandomThemeDecks"),
|
||||
RANDOM_DECK("lblRandomDecks"),
|
||||
NET_DECK("lblNetDecks"),
|
||||
NET_COMMANDER_DECK("lblNetCommanderDecks");
|
||||
NET_COMMANDER_DECK("lblNetCommanderDecks"),
|
||||
NET_ARCHIVE_STANDARD_DECK("lblNetArchiveStandardDecks"),
|
||||
NET_ARCHIVE_PIONEER_DECK("lblNetArchivePioneerDecks"),
|
||||
NET_ARCHIVE_MODERN_DECK("lblNetArchiveModernDecks"),
|
||||
NET_ARCHIVE_LEGACY_DECK("lblNetArchiveLegacyDecks"),
|
||||
NET_ARCHIVE_VINTAGE_DECK("lblNetArchiveVintageDecks");
|
||||
|
||||
public static DeckType[] ConstructedOptions;
|
||||
public static DeckType[] CommanderOptions;
|
||||
@@ -50,7 +56,12 @@ public enum DeckType {
|
||||
DeckType.MODERN_COLOR_DECK,
|
||||
DeckType.THEME_DECK,
|
||||
DeckType.RANDOM_DECK,
|
||||
DeckType.NET_DECK
|
||||
DeckType.NET_DECK,
|
||||
DeckType.NET_ARCHIVE_STANDARD_DECK,
|
||||
DeckType.NET_ARCHIVE_PIONEER_DECK,
|
||||
DeckType.NET_ARCHIVE_MODERN_DECK,
|
||||
DeckType.NET_ARCHIVE_LEGACY_DECK,
|
||||
DeckType.NET_ARCHIVE_VINTAGE_DECK
|
||||
};
|
||||
} else {
|
||||
ConstructedOptions = new DeckType[]{
|
||||
@@ -62,7 +73,13 @@ public enum DeckType {
|
||||
DeckType.MODERN_COLOR_DECK,
|
||||
DeckType.THEME_DECK,
|
||||
DeckType.RANDOM_DECK,
|
||||
DeckType.NET_DECK
|
||||
DeckType.NET_DECK,
|
||||
DeckType.NET_ARCHIVE_STANDARD_DECK,
|
||||
DeckType.NET_ARCHIVE_PIONEER_DECK,
|
||||
DeckType.NET_ARCHIVE_MODERN_DECK,
|
||||
DeckType.NET_ARCHIVE_LEGACY_DECK,
|
||||
DeckType.NET_ARCHIVE_VINTAGE_DECK
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -70,6 +87,7 @@ public enum DeckType {
|
||||
if (FModel.isdeckGenMatrixLoaded()) {
|
||||
CommanderOptions = new DeckType[]{
|
||||
DeckType.COMMANDER_DECK,
|
||||
DeckType.PRECON_COMMANDER_DECK,
|
||||
DeckType.RANDOM_COMMANDER_DECK,
|
||||
DeckType.RANDOM_CARDGEN_COMMANDER_DECK,
|
||||
DeckType.RANDOM_DECK,
|
||||
@@ -78,6 +96,7 @@ public enum DeckType {
|
||||
}else{
|
||||
CommanderOptions = new DeckType[]{
|
||||
DeckType.COMMANDER_DECK,
|
||||
DeckType.PRECON_COMMANDER_DECK,
|
||||
DeckType.RANDOM_COMMANDER_DECK,
|
||||
DeckType.RANDOM_DECK,
|
||||
DeckType.NET_COMMANDER_DECK
|
||||
|
||||
@@ -156,6 +156,9 @@ public class DeckgenUtil {
|
||||
//}
|
||||
}
|
||||
|
||||
//remove any cards not valid in format
|
||||
selectedCards = Lists.newArrayList(Iterables.filter(selectedCards, format.getFilterPrinted()));
|
||||
|
||||
List<PaperCard> toRemove = new ArrayList<>();
|
||||
|
||||
//randomly remove cards
|
||||
@@ -252,6 +255,9 @@ public class DeckgenUtil {
|
||||
//}
|
||||
}
|
||||
|
||||
//remove any cards not valid in format
|
||||
selectedCards = Lists.newArrayList(Iterables.filter(selectedCards, format.getFilterPrinted()));
|
||||
|
||||
List<PaperCard> toRemove = new ArrayList<>();
|
||||
|
||||
//randomly remove cards
|
||||
|
||||
122
forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java
Normal file
122
forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package forge.deck;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.deck.io.DeckSerializer;
|
||||
import forge.deck.io.DeckStorage;
|
||||
import forge.download.GuiDownloadZipService;
|
||||
import forge.game.GameType;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.WaitCallback;
|
||||
import forge.util.gui.SGuiChoose;
|
||||
import forge.util.storage.StorageBase;
|
||||
|
||||
public class NetDeckArchiveLegacy extends StorageBase<Deck> {
|
||||
public static final String PREFIX = "NET_ARCHIVE_LEGACY_DECK";
|
||||
private static Map<String, NetDeckArchiveLegacy> constructed, commander, brawl;
|
||||
|
||||
private static Map<String, NetDeckArchiveLegacy> loadCategories(String filename) {
|
||||
Map<String, NetDeckArchiveLegacy> categories = new TreeMap<>();
|
||||
if (FileUtil.doesFileExist(filename)) {
|
||||
List<String> lines = FileUtil.readFile(filename);
|
||||
for (String line : lines) {
|
||||
int idx = line.indexOf('|');
|
||||
if (idx != -1) {
|
||||
String name = line.substring(0, idx).trim();
|
||||
String url = line.substring(idx + 1).trim();
|
||||
categories.put(name, new NetDeckArchiveLegacy(name, url));
|
||||
}
|
||||
}
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
|
||||
public static NetDeckArchiveLegacy selectAndLoad(GameType gameType) {
|
||||
return selectAndLoad(gameType, null);
|
||||
}
|
||||
public static NetDeckArchiveLegacy selectAndLoad(GameType gameType, String name) {
|
||||
Map<String, NetDeckArchiveLegacy> categories;
|
||||
switch (gameType) {
|
||||
case Constructed:
|
||||
case Gauntlet:
|
||||
if (constructed == null) {
|
||||
constructed = loadCategories(ForgeConstants.NET_ARCHIVE_LEGACY_DECKS_LIST_FILE);
|
||||
}
|
||||
categories = constructed;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
NetDeckArchiveLegacy category = categories.get(name);
|
||||
if (category != null && category.map.isEmpty()) {
|
||||
//if name passed in, try to load decks from current cached files
|
||||
File downloadDir = new File(category.getFullPath());
|
||||
if (downloadDir.exists()) {
|
||||
for (File file : downloadDir.listFiles(DeckStorage.DCK_FILE_FILTER)) {
|
||||
Deck deck = DeckSerializer.fromFile(file);
|
||||
if (deck != null) {
|
||||
category.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
final NetDeckArchiveLegacy c = SGuiChoose.oneOrNone("Select a Net Deck Archive Legacy category", categories.values());
|
||||
if (c == null) { return null; }
|
||||
|
||||
if (c.map.isEmpty()) { //only download decks once per session
|
||||
WaitCallback<Boolean> callback = new WaitCallback<Boolean>() {
|
||||
@Override
|
||||
public void run() {
|
||||
String downloadLoc = c.getFullPath();
|
||||
GuiBase.getInterface().download(new GuiDownloadZipService(c.getName(), "decks", c.getUrl(), downloadLoc, downloadLoc, null) {
|
||||
@Override
|
||||
protected void copyInputStream(InputStream in, String outPath) throws IOException {
|
||||
super.copyInputStream(in, outPath);
|
||||
|
||||
Deck deck = DeckSerializer.fromFile(new File(outPath));
|
||||
if (deck != null) {
|
||||
c.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
if (!callback.invokeAndWait()) { return null; } //wait for download to finish
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private final String url;
|
||||
|
||||
private NetDeckArchiveLegacy(String name0, String url0) {
|
||||
super(name0, ForgeConstants.DECK_NET_ARCHIVE_DIR + name0, new HashMap<>());
|
||||
url = url0;
|
||||
}
|
||||
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getDeckType() {
|
||||
return "Net Archive Legacy Decks - " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
121
forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java
Normal file
121
forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java
Normal file
@@ -0,0 +1,121 @@
|
||||
package forge.deck;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.deck.io.DeckSerializer;
|
||||
import forge.deck.io.DeckStorage;
|
||||
import forge.download.GuiDownloadZipService;
|
||||
import forge.game.GameType;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.WaitCallback;
|
||||
import forge.util.gui.SGuiChoose;
|
||||
import forge.util.storage.StorageBase;
|
||||
|
||||
public class NetDeckArchiveModern extends StorageBase<Deck> {
|
||||
public static final String PREFIX = "NET_ARCHIVE_MODERN_DECK";
|
||||
private static Map<String, NetDeckArchiveModern> constructed, commander, brawl;
|
||||
|
||||
private static Map<String, NetDeckArchiveModern> loadCategories(String filename) {
|
||||
Map<String, NetDeckArchiveModern> categories = new TreeMap<>();
|
||||
if (FileUtil.doesFileExist(filename)) {
|
||||
List<String> lines = FileUtil.readFile(filename);
|
||||
for (String line : lines) {
|
||||
int idx = line.indexOf('|');
|
||||
if (idx != -1) {
|
||||
String name = line.substring(0, idx).trim();
|
||||
String url = line.substring(idx + 1).trim();
|
||||
categories.put(name, new NetDeckArchiveModern(name, url));
|
||||
}
|
||||
}
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
|
||||
public static NetDeckArchiveModern selectAndLoad(GameType gameType) {
|
||||
return selectAndLoad(gameType, null);
|
||||
}
|
||||
public static NetDeckArchiveModern selectAndLoad(GameType gameType, String name) {
|
||||
Map<String, NetDeckArchiveModern> categories;
|
||||
switch (gameType) {
|
||||
case Constructed:
|
||||
case Gauntlet:
|
||||
if (constructed == null) {
|
||||
constructed = loadCategories(ForgeConstants.NET_ARCHIVE_MODERN_DECKS_LIST_FILE);
|
||||
}
|
||||
categories = constructed;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
NetDeckArchiveModern category = categories.get(name);
|
||||
if (category != null && category.map.isEmpty()) {
|
||||
//if name passed in, try to load decks from current cached files
|
||||
File downloadDir = new File(category.getFullPath());
|
||||
if (downloadDir.exists()) {
|
||||
for (File file : downloadDir.listFiles(DeckStorage.DCK_FILE_FILTER)) {
|
||||
Deck deck = DeckSerializer.fromFile(file);
|
||||
if (deck != null) {
|
||||
category.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
final NetDeckArchiveModern c = SGuiChoose.oneOrNone("Select a Net Deck Archive Modern category", categories.values());
|
||||
if (c == null) { return null; }
|
||||
|
||||
if (c.map.isEmpty()) { //only download decks once per session
|
||||
WaitCallback<Boolean> callback = new WaitCallback<Boolean>() {
|
||||
@Override
|
||||
public void run() {
|
||||
String downloadLoc = c.getFullPath();
|
||||
GuiBase.getInterface().download(new GuiDownloadZipService(c.getName(), "decks", c.getUrl(), downloadLoc, downloadLoc, null) {
|
||||
@Override
|
||||
protected void copyInputStream(InputStream in, String outPath) throws IOException {
|
||||
super.copyInputStream(in, outPath);
|
||||
|
||||
Deck deck = DeckSerializer.fromFile(new File(outPath));
|
||||
if (deck != null) {
|
||||
c.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
if (!callback.invokeAndWait()) { return null; } //wait for download to finish
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private final String url;
|
||||
|
||||
private NetDeckArchiveModern(String name0, String url0) {
|
||||
super(name0, ForgeConstants.DECK_NET_ARCHIVE_DIR + name0, new HashMap<>());
|
||||
url = url0;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getDeckType() {
|
||||
return "Net Archive Modern Decks - " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
121
forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java
Normal file
121
forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java
Normal file
@@ -0,0 +1,121 @@
|
||||
package forge.deck;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.deck.io.DeckSerializer;
|
||||
import forge.deck.io.DeckStorage;
|
||||
import forge.download.GuiDownloadZipService;
|
||||
import forge.game.GameType;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.WaitCallback;
|
||||
import forge.util.gui.SGuiChoose;
|
||||
import forge.util.storage.StorageBase;
|
||||
|
||||
public class NetDeckArchivePioneer extends StorageBase<Deck> {
|
||||
public static final String PREFIX = "NET_ARCHIVE_PIONEER_DECK";
|
||||
private static Map<String, NetDeckArchivePioneer> constructed, commander, brawl;
|
||||
|
||||
private static Map<String, NetDeckArchivePioneer> loadCategories(String filename) {
|
||||
Map<String, NetDeckArchivePioneer> categories = new TreeMap<>();
|
||||
if (FileUtil.doesFileExist(filename)) {
|
||||
List<String> lines = FileUtil.readFile(filename);
|
||||
for (String line : lines) {
|
||||
int idx = line.indexOf('|');
|
||||
if (idx != -1) {
|
||||
String name = line.substring(0, idx).trim();
|
||||
String url = line.substring(idx + 1).trim();
|
||||
categories.put(name, new NetDeckArchivePioneer(name, url));
|
||||
}
|
||||
}
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
|
||||
public static NetDeckArchivePioneer selectAndLoad(GameType gameType) {
|
||||
return selectAndLoad(gameType, null);
|
||||
}
|
||||
public static NetDeckArchivePioneer selectAndLoad(GameType gameType, String name) {
|
||||
Map<String, NetDeckArchivePioneer> categories;
|
||||
switch (gameType) {
|
||||
case Constructed:
|
||||
case Gauntlet:
|
||||
if (constructed == null) {
|
||||
constructed = loadCategories(ForgeConstants.NET_ARCHIVE_PIONEER_DECKS_LIST_FILE);
|
||||
}
|
||||
categories = constructed;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
NetDeckArchivePioneer category = categories.get(name);
|
||||
if (category != null && category.map.isEmpty()) {
|
||||
//if name passed in, try to load decks from current cached files
|
||||
File downloadDir = new File(category.getFullPath());
|
||||
if (downloadDir.exists()) {
|
||||
for (File file : downloadDir.listFiles(DeckStorage.DCK_FILE_FILTER)) {
|
||||
Deck deck = DeckSerializer.fromFile(file);
|
||||
if (deck != null) {
|
||||
category.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
final NetDeckArchivePioneer c = SGuiChoose.oneOrNone("Select a Net Deck Archive Pioneer category", categories.values());
|
||||
if (c == null) { return null; }
|
||||
|
||||
if (c.map.isEmpty()) { //only download decks once per session
|
||||
WaitCallback<Boolean> callback = new WaitCallback<Boolean>() {
|
||||
@Override
|
||||
public void run() {
|
||||
String downloadLoc = c.getFullPath();
|
||||
GuiBase.getInterface().download(new GuiDownloadZipService(c.getName(), "decks", c.getUrl(), downloadLoc, downloadLoc, null) {
|
||||
@Override
|
||||
protected void copyInputStream(InputStream in, String outPath) throws IOException {
|
||||
super.copyInputStream(in, outPath);
|
||||
|
||||
Deck deck = DeckSerializer.fromFile(new File(outPath));
|
||||
if (deck != null) {
|
||||
c.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
if (!callback.invokeAndWait()) { return null; } //wait for download to finish
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private final String url;
|
||||
|
||||
private NetDeckArchivePioneer(String name0, String url0) {
|
||||
super(name0, ForgeConstants.DECK_NET_ARCHIVE_DIR + name0, new HashMap<>());
|
||||
url = url0;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getDeckType() {
|
||||
return "Net Archive Pioneer Decks - " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
122
forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java
Normal file
122
forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package forge.deck;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.deck.io.DeckSerializer;
|
||||
import forge.deck.io.DeckStorage;
|
||||
import forge.download.GuiDownloadZipService;
|
||||
import forge.game.GameType;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.WaitCallback;
|
||||
import forge.util.gui.SGuiChoose;
|
||||
import forge.util.storage.StorageBase;
|
||||
|
||||
public class NetDeckArchiveStandard extends StorageBase<Deck> {
|
||||
public static final String PREFIX = "NET_ARCHIVE_STANDARD_DECK";
|
||||
private static Map<String, NetDeckArchiveStandard> constructed, commander, brawl;
|
||||
|
||||
private static Map<String, NetDeckArchiveStandard> loadCategories(String filename) {
|
||||
Map<String, NetDeckArchiveStandard> categories = new TreeMap<>();
|
||||
if (FileUtil.doesFileExist(filename)) {
|
||||
List<String> lines = FileUtil.readFile(filename);
|
||||
for (String line : lines) {
|
||||
int idx = line.indexOf('|');
|
||||
if (idx != -1) {
|
||||
String name = line.substring(0, idx).trim();
|
||||
String url = line.substring(idx + 1).trim();
|
||||
categories.put(name, new NetDeckArchiveStandard(name, url));
|
||||
}
|
||||
}
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
|
||||
public static NetDeckArchiveStandard selectAndLoad(GameType gameType) {
|
||||
return selectAndLoad(gameType, null);
|
||||
}
|
||||
public static NetDeckArchiveStandard selectAndLoad(GameType gameType, String name) {
|
||||
Map<String, NetDeckArchiveStandard> categories;
|
||||
switch (gameType) {
|
||||
case Constructed:
|
||||
case Gauntlet:
|
||||
if (constructed == null) {
|
||||
constructed = loadCategories(ForgeConstants.NET_ARCHIVE_STANDARD_DECKS_LIST_FILE);
|
||||
}
|
||||
categories = constructed;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
NetDeckArchiveStandard category = categories.get(name);
|
||||
if (category != null && category.map.isEmpty()) {
|
||||
//if name passed in, try to load decks from current cached files
|
||||
File downloadDir = new File(category.getFullPath());
|
||||
if (downloadDir.exists()) {
|
||||
for (File file : downloadDir.listFiles(DeckStorage.DCK_FILE_FILTER)) {
|
||||
Deck deck = DeckSerializer.fromFile(file);
|
||||
if (deck != null) {
|
||||
category.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
final NetDeckArchiveStandard c = SGuiChoose.oneOrNone("Select a Net Deck Archive Standard category", categories.values());
|
||||
if (c == null) { return null; }
|
||||
|
||||
if (c.map.isEmpty()) { //only download decks once per session
|
||||
WaitCallback<Boolean> callback = new WaitCallback<Boolean>() {
|
||||
@Override
|
||||
public void run() {
|
||||
String downloadLoc = c.getFullPath();
|
||||
GuiBase.getInterface().download(new GuiDownloadZipService(c.getName(), "decks", c.getUrl(), downloadLoc, downloadLoc, null) {
|
||||
@Override
|
||||
protected void copyInputStream(InputStream in, String outPath) throws IOException {
|
||||
super.copyInputStream(in, outPath);
|
||||
|
||||
Deck deck = DeckSerializer.fromFile(new File(outPath));
|
||||
if (deck != null) {
|
||||
c.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
if (!callback.invokeAndWait()) { return null; } //wait for download to finish
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private final String url;
|
||||
|
||||
private NetDeckArchiveStandard(String name0, String url0) {
|
||||
super(name0, ForgeConstants.DECK_NET_ARCHIVE_DIR + name0, new HashMap<>());
|
||||
url = url0;
|
||||
}
|
||||
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getDeckType() {
|
||||
return "Net Archive Standard Decks - " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
122
forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java
Normal file
122
forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package forge.deck;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.deck.io.DeckSerializer;
|
||||
import forge.deck.io.DeckStorage;
|
||||
import forge.download.GuiDownloadZipService;
|
||||
import forge.game.GameType;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.WaitCallback;
|
||||
import forge.util.gui.SGuiChoose;
|
||||
import forge.util.storage.StorageBase;
|
||||
|
||||
public class NetDeckArchiveVintage extends StorageBase<Deck> {
|
||||
public static final String PREFIX = "NET_ARCHIVE_VINTAGE_DECK";
|
||||
private static Map<String, NetDeckArchiveVintage> constructed, commander, brawl;
|
||||
|
||||
private static Map<String, NetDeckArchiveVintage> loadCategories(String filename) {
|
||||
Map<String, NetDeckArchiveVintage> categories = new TreeMap<>();
|
||||
if (FileUtil.doesFileExist(filename)) {
|
||||
List<String> lines = FileUtil.readFile(filename);
|
||||
for (String line : lines) {
|
||||
int idx = line.indexOf('|');
|
||||
if (idx != -1) {
|
||||
String name = line.substring(0, idx).trim();
|
||||
String url = line.substring(idx + 1).trim();
|
||||
categories.put(name, new NetDeckArchiveVintage(name, url));
|
||||
}
|
||||
}
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
|
||||
public static NetDeckArchiveVintage selectAndLoad(GameType gameType) {
|
||||
return selectAndLoad(gameType, null);
|
||||
}
|
||||
public static NetDeckArchiveVintage selectAndLoad(GameType gameType, String name) {
|
||||
Map<String, NetDeckArchiveVintage> categories;
|
||||
switch (gameType) {
|
||||
case Constructed:
|
||||
case Gauntlet:
|
||||
if (constructed == null) {
|
||||
constructed = loadCategories(ForgeConstants.NET_ARCHIVE_VINTAGE_DECKS_LIST_FILE);
|
||||
}
|
||||
categories = constructed;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
NetDeckArchiveVintage category = categories.get(name);
|
||||
if (category != null && category.map.isEmpty()) {
|
||||
//if name passed in, try to load decks from current cached files
|
||||
File downloadDir = new File(category.getFullPath());
|
||||
if (downloadDir.exists()) {
|
||||
for (File file : downloadDir.listFiles(DeckStorage.DCK_FILE_FILTER)) {
|
||||
Deck deck = DeckSerializer.fromFile(file);
|
||||
if (deck != null) {
|
||||
category.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
final NetDeckArchiveVintage c = SGuiChoose.oneOrNone("Select a Net Deck Archive Vintage category", categories.values());
|
||||
if (c == null) { return null; }
|
||||
|
||||
if (c.map.isEmpty()) { //only download decks once per session
|
||||
WaitCallback<Boolean> callback = new WaitCallback<Boolean>() {
|
||||
@Override
|
||||
public void run() {
|
||||
String downloadLoc = c.getFullPath();
|
||||
GuiBase.getInterface().download(new GuiDownloadZipService(c.getName(), "decks", c.getUrl(), downloadLoc, downloadLoc, null) {
|
||||
@Override
|
||||
protected void copyInputStream(InputStream in, String outPath) throws IOException {
|
||||
super.copyInputStream(in, outPath);
|
||||
|
||||
Deck deck = DeckSerializer.fromFile(new File(outPath));
|
||||
if (deck != null) {
|
||||
c.map.put(deck.getName(), deck);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
if (!callback.invokeAndWait()) { return null; } //wait for download to finish
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private final String url;
|
||||
|
||||
private NetDeckArchiveVintage(String name0, String url0) {
|
||||
super(name0, ForgeConstants.DECK_NET_ARCHIVE_DIR + name0, new HashMap<>());
|
||||
url = url0;
|
||||
}
|
||||
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getDeckType() {
|
||||
return "Net Archive Vintage Decks - " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
246
forge-gui/src/main/java/forge/download/AutoUpdater.java
Normal file
246
forge-gui/src/main/java/forge/download/AutoUpdater.java
Normal file
@@ -0,0 +1,246 @@
|
||||
package forge.download;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.GuiBase;
|
||||
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;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class AutoUpdater {
|
||||
private final String SNAPSHOT_VERSION_INDEX = "https://snapshots.cardforge.org/";
|
||||
private final String SNAPSHOT_VERSION_URL = "https://snapshots.cardforge.org/version.txt";
|
||||
private final String SNAPSHOT_PACKAGE = "https://snapshots.cardforge.org/latest/";
|
||||
private final String RELEASE_VERSION_URL = "https://releases.cardforge.org/forge/forge-gui-desktop/version.txt";
|
||||
private final String RELEASE_PACKAGE = "https://releases.cardforge.org/latest/";
|
||||
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"};
|
||||
|
||||
private boolean isLoading;
|
||||
private String updateChannel;
|
||||
private String version;
|
||||
private String buildVersion;
|
||||
private String versionUrlString;
|
||||
private String packageUrl;
|
||||
private String packagePath;
|
||||
|
||||
public AutoUpdater(boolean loading) {
|
||||
// What do I need? Preferences? Splashscreen? UI? Skins?
|
||||
isLoading = loading;
|
||||
updateChannel = FModel.getPreferences().getPref(ForgePreferences.FPref.AUTO_UPDATE);
|
||||
buildVersion = BuildInfo.getVersionString();
|
||||
}
|
||||
|
||||
public boolean attemptToUpdate() {
|
||||
if (!verifyUpdateable()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (downloadUpdate()) {
|
||||
extractAndRestart();
|
||||
}
|
||||
} catch(IOException | URISyntaxException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void extractAndRestart() {
|
||||
extractUpdate();
|
||||
restartForge();
|
||||
}
|
||||
|
||||
private boolean verifyUpdateable() {
|
||||
if (buildVersion.contains("GIT")) {
|
||||
//return false;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
// TODO This doesn't work yet, because FSkin isn't loaded at the time.
|
||||
return false;
|
||||
} else if (updateChannel.equals("none")) {
|
||||
String message = localizer.getMessage("lblYouHaventSetUpdateChannel");
|
||||
List<String> options = ImmutableList.of("Cancel", "release", "snapshot");
|
||||
int option = SOptionPane.showOptionDialog(message, localizer.getMessage("lblManualCheck"), null, options, 0);
|
||||
if (option < 1) {
|
||||
return false;
|
||||
} else {
|
||||
updateChannel = options.get(option);
|
||||
}
|
||||
}
|
||||
|
||||
if (buildVersion.contains("SNAPSHOT")) {
|
||||
if (!updateChannel.equals("snapshot")) {
|
||||
System.out.println("Snapshot build versions must use snapshot update channel to work");
|
||||
return false;
|
||||
}
|
||||
|
||||
versionUrlString = SNAPSHOT_VERSION_URL;
|
||||
packageUrl = SNAPSHOT_PACKAGE;
|
||||
} else {
|
||||
versionUrlString = RELEASE_VERSION_URL;
|
||||
packageUrl = RELEASE_PACKAGE;
|
||||
}
|
||||
|
||||
// Check the internet connection
|
||||
if (!testNetConnection()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Download appropriate version file
|
||||
return compareBuildWithLatestChannelVersion();
|
||||
}
|
||||
|
||||
private boolean testNetConnection() {
|
||||
try (Socket socket = new Socket()) {
|
||||
InetSocketAddress address = new InetSocketAddress("releases.cardforge.org", 443);
|
||||
socket.connect(address, 1000);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false; // Either timeout or unreachable or failed DNS lookup.
|
||||
}
|
||||
}
|
||||
|
||||
private boolean compareBuildWithLatestChannelVersion() {
|
||||
try {
|
||||
retrieveVersion();
|
||||
|
||||
if (StringUtils.isEmpty(version) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buildVersion.equals(version)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
// If version doesn't match, it's assummably newer.
|
||||
return true;
|
||||
}
|
||||
|
||||
private void retrieveVersion() throws MalformedURLException {
|
||||
if (VERSION_FROM_METADATA) {
|
||||
if (updateChannel.equals("release")) {
|
||||
extractVersionFromMavenRelease();
|
||||
} else {
|
||||
extractVersionFromSnapshotIndex();
|
||||
}
|
||||
} else {
|
||||
URL versionUrl = new URL(versionUrlString);
|
||||
version = FileUtil.readFileToString(versionUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private void extractVersionFromSnapshotIndex() throws MalformedURLException {
|
||||
URL metadataUrl = new URL(SNAPSHOT_VERSION_INDEX);
|
||||
String index = FileUtil.readFileToString(metadataUrl);
|
||||
|
||||
System.out.println(index);
|
||||
Pattern p = Pattern.compile(">forge-(.*SNAPSHOT)");
|
||||
Matcher m = p.matcher(index);
|
||||
while(m.find()){
|
||||
version = m.group(1);
|
||||
}
|
||||
}
|
||||
|
||||
private void extractVersionFromMavenRelease() throws MalformedURLException {
|
||||
URL metadataUrl = new URL(RELEASE_MAVEN_METADATA);
|
||||
String xml = FileUtil.readFileToString(metadataUrl);
|
||||
|
||||
Pattern p = Pattern.compile("<release>(.*)</release>");
|
||||
Matcher m = p.matcher(xml);
|
||||
while(m.find()){
|
||||
version = m.group(1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean downloadUpdate() throws URISyntaxException, IOException {
|
||||
// TODO Change the "auto" to be more auto.
|
||||
if (isLoading) {
|
||||
// We need to preload enough of a Skins to show a dialog and a button if we're in loading
|
||||
// splashScreen.prepareForDialogs();
|
||||
return downloadFromBrowser();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean downloadFromBrowser() throws URISyntaxException, IOException {
|
||||
final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
|
||||
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
|
||||
// Linking directly there will auto download, but won't auto-update
|
||||
desktop.browse(new URI(packageUrl));
|
||||
return true;
|
||||
} else {
|
||||
System.out.println("Download latest version: " + packageUrl);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean downloadFromForge() {
|
||||
WaitCallback<Boolean> callback = new WaitCallback<Boolean>() {
|
||||
@Override
|
||||
public void run() {
|
||||
GuiBase.getInterface().download(new GuiDownloadZipService("Auto Updater", localizer.getMessage("lblNewVersionDownloading"), packageUrl, "tmp/", null, null) {
|
||||
@Override
|
||||
public void downloadAndUnzip() {
|
||||
packagePath = download(version + "-upgrade.tar.bz2");
|
||||
if (packagePath != null) {
|
||||
extractAndRestart();
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
SwingUtilities.invokeLater(callback);
|
||||
//
|
||||
return false;
|
||||
}
|
||||
|
||||
private void extractUpdate() {
|
||||
// TODOD Something like https://stackoverflow.com/questions/315618/how-do-i-extract-a-tar-file-in-java
|
||||
final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
|
||||
if (desktop != null) {
|
||||
try {
|
||||
desktop.open(new File(packagePath).getParentFile());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
System.out.println(packagePath);
|
||||
}
|
||||
}
|
||||
|
||||
private void restartForge() {
|
||||
if (isLoading || SOptionPane.showConfirmDialog(localizer.getMessage("lblForgeHasBeenUpdateRestartForgeToUseNewVersion"), localizer.getMessage("lblExitNowConfirm"))) {
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.ImageUtil;
|
||||
import forge.util.TextUtil;
|
||||
import forge.ImageKeys;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
@@ -63,21 +63,8 @@ public class GuiDownloadPicturesLQ extends GuiDownloadService {
|
||||
private void addDLObject(final PaperCard c, final boolean backFace) {
|
||||
final String imageKey = ImageUtil.getImageKey(c, backFace, false);
|
||||
final String destPath = ForgeConstants.CACHE_CARD_PICS_DIR + imageKey + ".jpg";
|
||||
final String setcode2 = StaticData.instance().getEditions().getCode2ByCode(c.getEdition());
|
||||
String modifier = !imageKey.contains(".full") ? ".fullborder" : "";
|
||||
final String fullborderPath = ForgeConstants.CACHE_CARD_PICS_DIR + setcode2 + ForgeConstants.PATH_SEPARATOR + TextUtil.fastReplace(imageKey,".full",".fullborder") + modifier + ".jpg";
|
||||
final File existingFB = new File (fullborderPath);
|
||||
final File existingFB2 = new File (TextUtil.fastReplace(fullborderPath, "1.fullborder", ".fullborder"));
|
||||
|
||||
if (existingFB.exists()) {
|
||||
return; //don't download equivalent full image with an existing fullborder image
|
||||
}
|
||||
|
||||
if (existingFB2.exists()) {
|
||||
return; //check 2
|
||||
}
|
||||
|
||||
if (existingImages.contains(imageKey + ".jpg")) {
|
||||
if (ImageKeys.getImageFile(imageKey) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,9 +268,9 @@ 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\\";
|
||||
final String subLastIndex = filePath.contains("pics") ? "\\pics\\" : filePath.contains("skins") ? "\\"+FileUtil.getParent(filePath)+"\\" : "\\db\\";
|
||||
|
||||
System.out.println(count + "/" + totalCount + " - .." + filePath.substring(filePath.lastIndexOf(subLastIndex)+1));
|
||||
|
||||
@@ -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);
|
||||
@@ -390,8 +409,11 @@ public abstract class GuiDownloadService implements Runnable {
|
||||
protected abstract Map<String, String> getNeededFiles();
|
||||
|
||||
protected static void addMissingItems(Map<String, String> list, String nameUrlFile, String dir) {
|
||||
addMissingItems(list, nameUrlFile, dir, false);
|
||||
}
|
||||
protected static void addMissingItems(Map<String, String> list, String nameUrlFile, String dir, boolean includeParent) {
|
||||
for (Pair<String, String> nameUrlPair : FileUtil.readNameUrlFile(nameUrlFile)) {
|
||||
File f = new File(dir, decodeURL(nameUrlPair.getLeft()));
|
||||
File f = new File(includeParent? dir+FileUtil.getParent(nameUrlPair.getRight()) : dir , decodeURL(nameUrlPair.getLeft()));
|
||||
//System.out.println(f.getAbsolutePath());
|
||||
if (!f.exists()) {
|
||||
list.put(f.getAbsolutePath(), nameUrlPair.getRight());
|
||||
|
||||
23
forge-gui/src/main/java/forge/download/GuiDownloadSkins.java
Normal file
23
forge-gui/src/main/java/forge/download/GuiDownloadSkins.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package forge.download;
|
||||
|
||||
import forge.properties.ForgeConstants;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class GuiDownloadSkins extends GuiDownloadService {
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "Download Skins";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Map<String, String> getNeededFiles() {
|
||||
// read all card names and urls
|
||||
final Map<String, String> urls = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
addMissingItems(urls, ForgeConstants.SKINS_LIST_FILE, ForgeConstants.CACHE_SKINS_DIR, true);
|
||||
|
||||
return urls;
|
||||
}
|
||||
}
|
||||
@@ -73,72 +73,7 @@ public class GuiDownloadZipService extends GuiDownloadService {
|
||||
String zipFilename = download("temp.zip");
|
||||
if (zipFilename == null) { return; }
|
||||
|
||||
//if assets.zip downloaded successfully, unzip into destination folder
|
||||
try {
|
||||
GuiBase.getInterface().preventSystemSleep(true); //prevent system from going into sleep mode while unzipping
|
||||
|
||||
if (deleteFolder != null) {
|
||||
final File deleteDir = new File(deleteFolder);
|
||||
if (deleteDir.exists()) {
|
||||
//attempt to delete previous res directory if to be rebuilt
|
||||
progressBar.reset();
|
||||
progressBar.setDescription("Deleting old " + desc + "...");
|
||||
if (deleteFolder.equals(destFolder)) { //move zip file to prevent deleting it
|
||||
final String oldZipFilename = zipFilename;
|
||||
zipFilename = deleteDir.getParentFile().getAbsolutePath() + File.separator + "temp.zip";
|
||||
Files.move(new File(oldZipFilename), new File(zipFilename));
|
||||
}
|
||||
FileUtil.deleteDirectory(deleteDir);
|
||||
}
|
||||
}
|
||||
|
||||
final ZipFile zipFile = new ZipFile(zipFilename);
|
||||
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
|
||||
progressBar.reset();
|
||||
progressBar.setPercentMode(true);
|
||||
progressBar.setDescription("Extracting " + desc);
|
||||
progressBar.setMaximum(zipFile.size());
|
||||
|
||||
FileUtil.ensureDirectoryExists(destFolder);
|
||||
|
||||
int count = 0;
|
||||
int failedCount = 0;
|
||||
while (entries.hasMoreElements()) {
|
||||
if (cancel) { break; }
|
||||
|
||||
try {
|
||||
final ZipEntry entry = entries.nextElement();
|
||||
|
||||
final String path = destFolder + File.separator + entry.getName();
|
||||
if (entry.isDirectory()) {
|
||||
new File(path).mkdir();
|
||||
progressBar.setValue(++count);
|
||||
continue;
|
||||
}
|
||||
copyInputStream(zipFile.getInputStream(entry), path);
|
||||
progressBar.setValue(++count);
|
||||
filesExtracted++;
|
||||
}
|
||||
catch (final Exception e) { //don't quit out completely if an entry is not UTF-8
|
||||
progressBar.setValue(++count);
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (failedCount > 0) {
|
||||
Log.error("Downloading " + desc, failedCount + " " + desc + " could not be extracted");
|
||||
}
|
||||
|
||||
zipFile.close();
|
||||
new File(zipFilename).delete();
|
||||
}
|
||||
catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
GuiBase.getInterface().preventSystemSleep(false);
|
||||
}
|
||||
extract(zipFilename);
|
||||
}
|
||||
|
||||
public String download(final String filename) {
|
||||
@@ -211,6 +146,75 @@ public class GuiDownloadZipService extends GuiDownloadService {
|
||||
}
|
||||
}
|
||||
|
||||
public void extract(String zipFilename) {
|
||||
//if assets.zip downloaded successfully, unzip into destination folder
|
||||
try {
|
||||
GuiBase.getInterface().preventSystemSleep(true); //prevent system from going into sleep mode while unzipping
|
||||
|
||||
if (deleteFolder != null) {
|
||||
final File deleteDir = new File(deleteFolder);
|
||||
if (deleteDir.exists()) {
|
||||
//attempt to delete previous res directory if to be rebuilt
|
||||
progressBar.reset();
|
||||
progressBar.setDescription("Deleting old " + desc + "...");
|
||||
if (deleteFolder.equals(destFolder)) { //move zip file to prevent deleting it
|
||||
final String oldZipFilename = zipFilename;
|
||||
zipFilename = deleteDir.getParentFile().getAbsolutePath() + File.separator + "temp.zip";
|
||||
Files.move(new File(oldZipFilename), new File(zipFilename));
|
||||
}
|
||||
FileUtil.deleteDirectory(deleteDir);
|
||||
}
|
||||
}
|
||||
|
||||
final ZipFile zipFile = new ZipFile(zipFilename);
|
||||
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
|
||||
progressBar.reset();
|
||||
progressBar.setPercentMode(true);
|
||||
progressBar.setDescription("Extracting " + desc);
|
||||
progressBar.setMaximum(zipFile.size());
|
||||
|
||||
FileUtil.ensureDirectoryExists(destFolder);
|
||||
|
||||
int count = 0;
|
||||
int failedCount = 0;
|
||||
while (entries.hasMoreElements()) {
|
||||
if (cancel) { break; }
|
||||
|
||||
try {
|
||||
final ZipEntry entry = entries.nextElement();
|
||||
|
||||
final String path = destFolder + File.separator + entry.getName();
|
||||
if (entry.isDirectory()) {
|
||||
new File(path).mkdir();
|
||||
progressBar.setValue(++count);
|
||||
continue;
|
||||
}
|
||||
copyInputStream(zipFile.getInputStream(entry), path);
|
||||
progressBar.setValue(++count);
|
||||
filesExtracted++;
|
||||
}
|
||||
catch (final Exception e) { //don't quit out completely if an entry is not UTF-8
|
||||
progressBar.setValue(++count);
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (failedCount > 0) {
|
||||
Log.error("Downloading " + desc, failedCount + " " + desc + " could not be extracted");
|
||||
}
|
||||
|
||||
zipFile.close();
|
||||
new File(zipFilename).delete();
|
||||
}
|
||||
catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
GuiBase.getInterface().preventSystemSleep(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void copyInputStream(final InputStream in, final String outPath) throws IOException {
|
||||
final byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
|
||||
@@ -29,6 +29,7 @@ import forge.GuiBase;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.util.gui.SOptionPane;
|
||||
import forge.util.Localizer;
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.event.BreadcrumbBuilder;
|
||||
|
||||
@@ -42,11 +43,11 @@ import io.sentry.event.BreadcrumbBuilder;
|
||||
public class BugReporter {
|
||||
private static final int STACK_OVERFLOW_MAX_MESSAGE_LEN = 16 * 1024;
|
||||
|
||||
public static final String REPORT = "Report";
|
||||
public static final String SAVE = "Save";
|
||||
public static final String DISCARD = "Discard";
|
||||
public static final String EXIT = "Exit";
|
||||
public static final String SENTRY = "Submit bug reports automatically";
|
||||
public static final String REPORT = Localizer.getInstance().getMessage("lblReport");
|
||||
public static final String SAVE = Localizer.getInstance().getMessage("lblSave");
|
||||
public static final String DISCARD = Localizer.getInstance().getMessage("lblDiscardError");
|
||||
public static final String EXIT = Localizer.getInstance().getMessage("lblExit");
|
||||
public static final String SENTRY = Localizer.getInstance().getMessage("lblAutoSubmitBugReports");
|
||||
|
||||
private static Throwable exception;
|
||||
private static String message;
|
||||
@@ -93,7 +94,7 @@ public class BugReporter {
|
||||
if (isSentryEnabled()) {
|
||||
sendSentry();
|
||||
} else {
|
||||
GuiBase.getInterface().showBugReportDialog("Report a crash", sb.toString(), true);
|
||||
GuiBase.getInterface().showBugReportDialog(Localizer.getInstance().getMessage("lblReportCrash"), sb.toString(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +129,7 @@ public class BugReporter {
|
||||
if (isSentryEnabled()) {
|
||||
sendSentry();
|
||||
} else {
|
||||
GuiBase.getInterface().showBugReportDialog("Report a bug", message, false);
|
||||
GuiBase.getInterface().showBugReportDialog(Localizer.getInstance().getMessage("btnReportBug"), message, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,8 +149,8 @@ public class BugReporter {
|
||||
try (BufferedWriter bw = new BufferedWriter(new FileWriter(f))){
|
||||
bw.write(text);
|
||||
} catch (final IOException ex) {
|
||||
SOptionPane.showMessageDialog("There was an error during saving. Sorry!\n" + ex,
|
||||
"Error saving file", SOptionPane.ERROR_ICON);
|
||||
SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblThereErrorWasDuringSaving", ex),
|
||||
Localizer.getInstance().getMessage("lblErrorSavingFile"), SOptionPane.ERROR_ICON);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public interface IGuiBase {
|
||||
String showFileDialog(String title, String defaultDir);
|
||||
File getSaveFile(File defaultFile);
|
||||
void download(GuiDownloadService service, Callback<Boolean> callback);
|
||||
void refreshSkin();
|
||||
void showCardList(String title, String message, List<PaperCard> list);
|
||||
boolean showBoxedProduct(String title, String message, List<PaperCard> list);
|
||||
PaperCard chooseCard(String title, String message, List<PaperCard> list);
|
||||
|
||||
@@ -11,6 +11,7 @@ import forge.assets.FSkinProp;
|
||||
import forge.deck.CardPool;
|
||||
import forge.game.GameEntityView;
|
||||
import forge.game.GameView;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.event.GameEventSpellAbilityCast;
|
||||
import forge.game.event.GameEventSpellRemovedFromStack;
|
||||
@@ -22,6 +23,7 @@ import forge.game.spellability.SpellAbilityView;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.item.PaperCard;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
|
||||
@@ -51,6 +53,7 @@ public interface IGuiGame {
|
||||
void updateStack();
|
||||
void notifyStackAddition(final GameEventSpellAbilityCast event);
|
||||
void notifyStackRemoval(final GameEventSpellRemovedFromStack event);
|
||||
void handleLandPlayed(Card land);
|
||||
Iterable<PlayerZoneUpdate> tempShowZones(PlayerView controller, Iterable<PlayerZoneUpdate> zonesToUpdate);
|
||||
void hideZones(PlayerView controller, Iterable<PlayerZoneUpdate> zonesToUpdate);
|
||||
void updateZones(Iterable<PlayerZoneUpdate> zonesToUpdate);
|
||||
@@ -62,7 +65,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);
|
||||
@@ -107,7 +110,6 @@ public interface IGuiGame {
|
||||
* @return null if choices is missing, empty, or if the users' choices are
|
||||
* empty; otherwise, returns the first item in the List returned by
|
||||
* getChoices.
|
||||
* @see #getChoices(String, int, int, Object...)
|
||||
*/
|
||||
<T> T oneOrNone(String message, List<T> choices);
|
||||
|
||||
@@ -158,8 +160,8 @@ public interface IGuiGame {
|
||||
|
||||
void setCard(CardView card);
|
||||
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
|
||||
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
|
||||
void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor);
|
||||
PlayerZoneUpdates openZones(PlayerView controller, Collection<ZoneType> zones, Map<PlayerView, Object> players);
|
||||
void restoreOldZones(PlayerView playerView, PlayerZoneUpdates playerZoneUpdates);
|
||||
void setHighlighted(PlayerView pv, boolean b);
|
||||
void setUsedToPay(CardView card, boolean value);
|
||||
void setSelectables(final Iterable<CardView> cards);
|
||||
|
||||
@@ -26,30 +26,48 @@ 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;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
|
||||
public class AdvancedSearch {
|
||||
public enum FilterOption {
|
||||
NONE("(none)", null, null, null),
|
||||
CARD_NAME("Name", PaperCard.class, FilterOperator.STRING_OPS, new StringEvaluator<PaperCard>() {
|
||||
NONE("lblNone", null, null, null),
|
||||
CARD_NAME("lblName", PaperCard.class, FilterOperator.STRINGS_OPS, new StringEvaluator<PaperCard>() {
|
||||
@Override
|
||||
protected String getItemValue(PaperCard input) {
|
||||
return input.getName();
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<String> getItemValues(PaperCard input) {
|
||||
Set<String> names = new HashSet<>();
|
||||
names.add(input.getName());
|
||||
names.add(CardTranslation.getTranslatedName(input.getName()));
|
||||
return names;
|
||||
}
|
||||
}),
|
||||
CARD_RULES_TEXT("Rules Text", PaperCard.class, FilterOperator.STRING_OPS, new StringEvaluator<PaperCard>() {
|
||||
CARD_RULES_TEXT("lblRulesText", PaperCard.class, FilterOperator.STRINGS_OPS, new StringEvaluator<PaperCard>() {
|
||||
@Override
|
||||
protected String getItemValue(PaperCard input) {
|
||||
return input.getRules().getOracleText();
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
}
|
||||
@Override
|
||||
protected Set<String> getItemValues(PaperCard input) {
|
||||
Set<String> names = new HashSet<>();
|
||||
names.add(input.getRules().getOracleText());
|
||||
names.add(CardTranslation.getTranslatedOracle(input.getName()));
|
||||
return names;
|
||||
}
|
||||
}),
|
||||
CARD_KEYWORDS("Keywords", PaperCard.class, FilterOperator.COLLECTION_OPS, new CustomListEvaluator<PaperCard, Keyword>(Keyword.getAllKeywords()) {
|
||||
CARD_KEYWORDS("lblKeywords", PaperCard.class, FilterOperator.COLLECTION_OPS, new CustomListEvaluator<PaperCard, Keyword>(Keyword.getAllKeywords()) {
|
||||
@Override
|
||||
protected Keyword getItemValue(PaperCard input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -59,13 +77,13 @@ public class AdvancedSearch {
|
||||
return Keyword.getKeywordSet(input);
|
||||
}
|
||||
}),
|
||||
CARD_SET("Set", PaperCard.class, FilterOperator.SINGLE_LIST_OPS, new CustomListEvaluator<PaperCard, CardEdition>(FModel.getMagicDb().getSortedEditions(), CardEdition.FN_GET_CODE) {
|
||||
CARD_SET("lblSet", PaperCard.class, FilterOperator.SINGLE_LIST_OPS, new CustomListEvaluator<PaperCard, CardEdition>(FModel.getMagicDb().getSortedEditions(), CardEdition.FN_GET_CODE) {
|
||||
@Override
|
||||
protected CardEdition getItemValue(PaperCard input) {
|
||||
return FModel.getMagicDb().getEditions().get(input.getEdition());
|
||||
}
|
||||
}),
|
||||
CARD_FORMAT("Format", PaperCard.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<PaperCard, GameFormat>((List<GameFormat>)FModel.getFormats().getFilterList()) {
|
||||
CARD_FORMAT("lblFormat", PaperCard.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<PaperCard, GameFormat>((List<GameFormat>)FModel.getFormats().getFilterList()) {
|
||||
@Override
|
||||
protected GameFormat getItemValue(PaperCard input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -75,7 +93,7 @@ public class AdvancedSearch {
|
||||
return FModel.getFormats().getAllFormatsOfCard(input);
|
||||
}
|
||||
}),
|
||||
CARD_PLANE("Plane", PaperCard.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<PaperCard, ConquestPlane>(ImmutableList.copyOf(FModel.getPlanes())) {
|
||||
CARD_PLANE("lblPlane", PaperCard.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<PaperCard, ConquestPlane>(ImmutableList.copyOf(FModel.getPlanes())) {
|
||||
@Override
|
||||
protected ConquestPlane getItemValue(PaperCard input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -85,7 +103,7 @@ public class AdvancedSearch {
|
||||
return ConquestPlane.getAllPlanesOfCard(input);
|
||||
}
|
||||
}),
|
||||
CARD_REGION("Region", PaperCard.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<PaperCard, ConquestRegion>(ConquestRegion.getAllRegions()) {
|
||||
CARD_REGION("lblRegion", PaperCard.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<PaperCard, ConquestRegion>(ConquestRegion.getAllRegions()) {
|
||||
@Override
|
||||
protected ConquestRegion getItemValue(PaperCard input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -95,7 +113,7 @@ public class AdvancedSearch {
|
||||
return ConquestRegion.getAllRegionsOfCard(input);
|
||||
}
|
||||
}),
|
||||
CARD_QUEST_WORLD("Quest World", PaperCard.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<PaperCard, QuestWorld>(ImmutableList.copyOf(FModel.getWorlds())) {
|
||||
CARD_QUEST_WORLD("lblQuestWorld", PaperCard.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<PaperCard, QuestWorld>(ImmutableList.copyOf(FModel.getWorlds())) {
|
||||
@Override
|
||||
protected QuestWorld getItemValue(PaperCard input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -105,7 +123,7 @@ public class AdvancedSearch {
|
||||
return QuestWorld.getAllQuestWorldsOfCard(input);
|
||||
}
|
||||
}),
|
||||
CARD_COLOR("Color", PaperCard.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<PaperCard>() {
|
||||
CARD_COLOR("lblColor", PaperCard.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<PaperCard>() {
|
||||
@Override
|
||||
protected MagicColor.Color getItemValue(PaperCard input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -115,7 +133,7 @@ public class AdvancedSearch {
|
||||
return input.getRules().getColor().toEnumSet();
|
||||
}
|
||||
}),
|
||||
CARD_COLOR_IDENTITY("Color Identity", PaperCard.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<PaperCard>() {
|
||||
CARD_COLOR_IDENTITY("lblColorIdentity", PaperCard.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<PaperCard>() {
|
||||
@Override
|
||||
protected MagicColor.Color getItemValue(PaperCard input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -125,13 +143,13 @@ public class AdvancedSearch {
|
||||
return input.getRules().getColorIdentity().toEnumSet();
|
||||
}
|
||||
}),
|
||||
CARD_COLOR_COUNT("Color Count", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 5) {
|
||||
CARD_COLOR_COUNT("lblColorCount", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 5) {
|
||||
@Override
|
||||
protected Integer getItemValue(PaperCard input) {
|
||||
return input.getRules().getColor().countColors();
|
||||
}
|
||||
}),
|
||||
CARD_TYPE("Type", PaperCard.class, FilterOperator.COMBINATION_OPS, new CustomListEvaluator<PaperCard, String>(CardType.getCombinedSuperAndCoreTypes()) {
|
||||
CARD_TYPE("lblType", PaperCard.class, FilterOperator.COMBINATION_OPS, new CustomListEvaluator<PaperCard, String>(CardType.getCombinedSuperAndCoreTypes()) {
|
||||
@Override
|
||||
protected String getItemValue(PaperCard input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -149,7 +167,7 @@ public class AdvancedSearch {
|
||||
return types;
|
||||
}
|
||||
}),
|
||||
CARD_SUB_TYPE("Subtype", PaperCard.class, FilterOperator.COMBINATION_OPS, new CustomListEvaluator<PaperCard, String>(CardType.getSortedSubTypes()) {
|
||||
CARD_SUB_TYPE("lblSubtype", PaperCard.class, FilterOperator.COMBINATION_OPS, new CustomListEvaluator<PaperCard, String>(CardType.getSortedSubTypes()) {
|
||||
@Override
|
||||
protected String getItemValue(PaperCard input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -159,19 +177,19 @@ public class AdvancedSearch {
|
||||
return (Set<String>)input.getRules().getType().getSubtypes();
|
||||
}
|
||||
}),
|
||||
CARD_CMC("CMC", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 20) {
|
||||
CARD_CMC("lblCMC", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(PaperCard input) {
|
||||
return input.getRules().getManaCost().getCMC();
|
||||
}
|
||||
}),
|
||||
CARD_GENERIC_COST("Generic Cost", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 20) {
|
||||
CARD_GENERIC_COST("lblGenericCost", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(PaperCard input) {
|
||||
return input.getRules().getManaCost().getGenericCost();
|
||||
}
|
||||
}),
|
||||
CARD_POWER("Power", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 20) {
|
||||
CARD_POWER("lblPower", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(PaperCard input) {
|
||||
CardRules rules = input.getRules();
|
||||
@@ -181,7 +199,7 @@ public class AdvancedSearch {
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
CARD_TOUGHNESS("Toughness", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 20) {
|
||||
CARD_TOUGHNESS("lblToughness", PaperCard.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<PaperCard>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(PaperCard input) {
|
||||
CardRules rules = input.getRules();
|
||||
@@ -191,19 +209,19 @@ public class AdvancedSearch {
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
CARD_MANA_COST("Mana Cost", PaperCard.class, FilterOperator.STRING_OPS, new StringEvaluator<PaperCard>() {
|
||||
CARD_MANA_COST("lblManaCost", PaperCard.class, FilterOperator.STRING_OPS, new StringEvaluator<PaperCard>() {
|
||||
@Override
|
||||
protected String getItemValue(PaperCard input) {
|
||||
return input.getRules().getManaCost().toString();
|
||||
}
|
||||
}),
|
||||
CARD_RARITY("Rarity", PaperCard.class, FilterOperator.SINGLE_LIST_OPS, new CustomListEvaluator<PaperCard, CardRarity>(Arrays.asList(CardRarity.FILTER_OPTIONS), CardRarity.FN_GET_LONG_NAME, CardRarity.FN_GET_LONG_NAME) {
|
||||
CARD_RARITY("lblRarity", PaperCard.class, FilterOperator.SINGLE_LIST_OPS, new CustomListEvaluator<PaperCard, CardRarity>(Arrays.asList(CardRarity.FILTER_OPTIONS), CardRarity.FN_GET_LONG_NAME, CardRarity.FN_GET_LONG_NAME) {
|
||||
@Override
|
||||
protected CardRarity getItemValue(PaperCard input) {
|
||||
return input.getRarity();
|
||||
}
|
||||
}),
|
||||
CARD_FIRST_PRINTING("First Printing", PaperCard.class, FilterOperator.BOOLEAN_OPS, new BooleanEvaluator<PaperCard>() {
|
||||
CARD_FIRST_PRINTING("lblFirstPrinting", PaperCard.class, FilterOperator.BOOLEAN_OPS, new BooleanEvaluator<PaperCard>() {
|
||||
@Override
|
||||
protected Boolean getItemValue(PaperCard input) {
|
||||
List<PaperCard> cards = FModel.getMagicDb().getCommonCards().getAllCards(input.getName());
|
||||
@@ -213,25 +231,276 @@ public class AdvancedSearch {
|
||||
return cards.get(0) == input;
|
||||
}
|
||||
}),
|
||||
DECK_NAME("Name", DeckProxy.class, FilterOperator.STRING_OPS, new StringEvaluator<DeckProxy>() {
|
||||
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) {
|
||||
return input.getName();
|
||||
}
|
||||
}),
|
||||
DECK_FOLDER("Folder", DeckProxy.class, FilterOperator.STRING_OPS, new StringEvaluator<DeckProxy>() {
|
||||
DECK_FOLDER("lblFolder", DeckProxy.class, FilterOperator.STRING_OPS, new StringEvaluator<DeckProxy>() {
|
||||
@Override
|
||||
protected String getItemValue(DeckProxy input) {
|
||||
return input.getPath();
|
||||
}
|
||||
}),
|
||||
DECK_FAVORITE("Favorite", DeckProxy.class, FilterOperator.BOOLEAN_OPS, new BooleanEvaluator<DeckProxy>() {
|
||||
DECK_FAVORITE("ttFavorite", DeckProxy.class, FilterOperator.BOOLEAN_OPS, new BooleanEvaluator<DeckProxy>() {
|
||||
@Override
|
||||
protected Boolean getItemValue(DeckProxy input) {
|
||||
return input.isFavoriteDeck();
|
||||
}
|
||||
}),
|
||||
DECK_FORMAT("Format", DeckProxy.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<DeckProxy, GameFormat>((List<GameFormat>)FModel.getFormats().getFilterList()) {
|
||||
DECK_FORMAT("lblFormat", DeckProxy.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<DeckProxy, GameFormat>((List<GameFormat>)FModel.getFormats().getFilterList()) {
|
||||
@Override
|
||||
protected GameFormat getItemValue(DeckProxy input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -241,7 +510,7 @@ public class AdvancedSearch {
|
||||
return input.getExhaustiveFormats();
|
||||
}
|
||||
}),
|
||||
DECK_QUEST_WORLD("Quest World", DeckProxy.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<DeckProxy, QuestWorld>(ImmutableList.copyOf(FModel.getWorlds())) {
|
||||
DECK_QUEST_WORLD("lblQuestWorld", DeckProxy.class, FilterOperator.MULTI_LIST_OPS, new CustomListEvaluator<DeckProxy, QuestWorld>(ImmutableList.copyOf(FModel.getWorlds())) {
|
||||
@Override
|
||||
protected QuestWorld getItemValue(DeckProxy input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -251,7 +520,7 @@ public class AdvancedSearch {
|
||||
return QuestWorld.getAllQuestWorldsOfDeck(input.getDeck());
|
||||
}
|
||||
}),
|
||||
DECK_COLOR("Color", DeckProxy.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<DeckProxy>() {
|
||||
DECK_COLOR("lblColor", DeckProxy.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<DeckProxy>() {
|
||||
@Override
|
||||
protected MagicColor.Color getItemValue(DeckProxy input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -261,7 +530,7 @@ public class AdvancedSearch {
|
||||
return input.getColor().toEnumSet();
|
||||
}
|
||||
}),
|
||||
DECK_COLOR_IDENTITY("Color Identity", DeckProxy.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<DeckProxy>() {
|
||||
DECK_COLOR_IDENTITY("lblColorIdentity", DeckProxy.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<DeckProxy>() {
|
||||
@Override
|
||||
protected MagicColor.Color getItemValue(DeckProxy input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -271,25 +540,25 @@ public class AdvancedSearch {
|
||||
return input.getColorIdentity().toEnumSet();
|
||||
}
|
||||
}),
|
||||
DECK_COLOR_COUNT("Color Count", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(0, 5) {
|
||||
DECK_COLOR_COUNT("lblColorCount", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(0, 5) {
|
||||
@Override
|
||||
protected Integer getItemValue(DeckProxy input) {
|
||||
return input.getColor().countColors();
|
||||
}
|
||||
}),
|
||||
DECK_AVERAGE_CMC("Average CMC", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(0, 20) {
|
||||
DECK_AVERAGE_CMC("lblAverageCMC", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(DeckProxy input) {
|
||||
return input.getAverageCMC();
|
||||
}
|
||||
}),
|
||||
DECK_MAIN("Main Deck", DeckProxy.class, FilterOperator.DECK_CONTENT_OPS, new DeckContentEvaluator<DeckProxy>() {
|
||||
DECK_MAIN("lblMainDeck", DeckProxy.class, FilterOperator.DECK_CONTENT_OPS, new DeckContentEvaluator<DeckProxy>() {
|
||||
@Override
|
||||
protected Map<String, Integer> getItemValue(DeckProxy input) {
|
||||
return input.getDeck().getMain().toNameLookup();
|
||||
}
|
||||
}),
|
||||
DECK_SIDEBOARD("Sideboard", DeckProxy.class, FilterOperator.DECK_CONTENT_OPS, new DeckContentEvaluator<DeckProxy>() {
|
||||
DECK_SIDEBOARD("lblSideboard", DeckProxy.class, FilterOperator.DECK_CONTENT_OPS, new DeckContentEvaluator<DeckProxy>() {
|
||||
@Override
|
||||
protected Map<String, Integer> getItemValue(DeckProxy input) {
|
||||
CardPool sideboard = input.getDeck().get(DeckSection.Sideboard);
|
||||
@@ -299,31 +568,31 @@ public class AdvancedSearch {
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
DECK_MAIN_SIZE("Main Deck Size", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(40, 250) {
|
||||
DECK_MAIN_SIZE("lblMainDeckSize", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(40, 250) {
|
||||
@Override
|
||||
protected Integer getItemValue(DeckProxy input) {
|
||||
return input.getMainSize();
|
||||
}
|
||||
}),
|
||||
DECK_SIDE_SIZE("Sideboard Size", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(0, 15) {
|
||||
DECK_SIDE_SIZE("lblSideboardSize", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(0, 15) {
|
||||
@Override
|
||||
protected Integer getItemValue(DeckProxy input) {
|
||||
return Math.min(input.getSideSize(), 0);
|
||||
}
|
||||
}),
|
||||
COMMANDER_NAME("Name", ConquestCommander.class, FilterOperator.STRING_OPS, new StringEvaluator<ConquestCommander>() {
|
||||
COMMANDER_NAME("lblName", ConquestCommander.class, FilterOperator.STRING_OPS, new StringEvaluator<ConquestCommander>() {
|
||||
@Override
|
||||
protected String getItemValue(ConquestCommander input) {
|
||||
return input.getName();
|
||||
}
|
||||
}),
|
||||
COMMANDER_ORIGIN("Origin", ConquestCommander.class, FilterOperator.SINGLE_LIST_OPS, new CustomListEvaluator<ConquestCommander, ConquestPlane>(ImmutableList.copyOf(FModel.getPlanes())) {
|
||||
COMMANDER_ORIGIN("lblOrigin", ConquestCommander.class, FilterOperator.SINGLE_LIST_OPS, new CustomListEvaluator<ConquestCommander, ConquestPlane>(ImmutableList.copyOf(FModel.getPlanes())) {
|
||||
@Override
|
||||
protected ConquestPlane getItemValue(ConquestCommander input) {
|
||||
return input.getOriginPlane();
|
||||
}
|
||||
}),
|
||||
COMMANDER_COLOR("Color", ConquestCommander.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<ConquestCommander>() {
|
||||
COMMANDER_COLOR("lblColor", ConquestCommander.class, FilterOperator.COMBINATION_OPS, new ColorEvaluator<ConquestCommander>() {
|
||||
@Override
|
||||
protected MagicColor.Color getItemValue(ConquestCommander input) {
|
||||
throw new RuntimeException("getItemValues should be called instead");
|
||||
@@ -333,25 +602,25 @@ public class AdvancedSearch {
|
||||
return input.getCard().getRules().getColorIdentity().toEnumSet();
|
||||
}
|
||||
}),
|
||||
COMMANDER_COLOR_COUNT("Color Count", ConquestCommander.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<ConquestCommander>(0, 5) {
|
||||
COMMANDER_COLOR_COUNT("lblColorCount", ConquestCommander.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<ConquestCommander>(0, 5) {
|
||||
@Override
|
||||
protected Integer getItemValue(ConquestCommander input) {
|
||||
return input.getCard().getRules().getColorIdentity().countColors();
|
||||
}
|
||||
}),
|
||||
COMMANDER_DECK_AVERAGE_CMC("Deck Average CMC", ConquestCommander.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<ConquestCommander>(0, 20) {
|
||||
COMMANDER_DECK_AVERAGE_CMC("lblDeckAverageCMC", ConquestCommander.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<ConquestCommander>(0, 20) {
|
||||
@Override
|
||||
protected Integer getItemValue(ConquestCommander input) {
|
||||
return DeckProxy.getAverageCMC(input.getDeck());
|
||||
}
|
||||
}),
|
||||
COMMANDER_DECK_CONTENTS("Deck Contents", ConquestCommander.class, FilterOperator.DECK_CONTENT_OPS, new DeckContentEvaluator<ConquestCommander>() {
|
||||
COMMANDER_DECK_CONTENTS("lblDeckContents", ConquestCommander.class, FilterOperator.DECK_CONTENT_OPS, new DeckContentEvaluator<ConquestCommander>() {
|
||||
@Override
|
||||
protected Map<String, Integer> getItemValue(ConquestCommander input) {
|
||||
return input.getDeck().getMain().toNameLookup();
|
||||
}
|
||||
}),
|
||||
COMMANDER_DECK_SIZE("Deck Size", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(40, 250) {
|
||||
COMMANDER_DECK_SIZE("lblDeckSize", DeckProxy.class, FilterOperator.NUMBER_OPS, new NumericEvaluator<DeckProxy>(40, 250) {
|
||||
@Override
|
||||
protected Integer getItemValue(DeckProxy input) {
|
||||
return input.getMainSize();
|
||||
@@ -364,7 +633,7 @@ public class AdvancedSearch {
|
||||
private final FilterEvaluator<? extends InventoryItem, ?> evaluator;
|
||||
|
||||
FilterOption(String name0, Class<? extends InventoryItem> type0, FilterOperator[] operatorOptions0, FilterEvaluator<? extends InventoryItem, ?> evaluator0) {
|
||||
name = name0;
|
||||
name = Localizer.getInstance().getMessage(name0);
|
||||
type = type0;
|
||||
operatorOptions = operatorOptions0;
|
||||
evaluator = evaluator0;
|
||||
@@ -378,7 +647,7 @@ public class AdvancedSearch {
|
||||
|
||||
public enum FilterOperator {
|
||||
//Boolean operators
|
||||
IS_TRUE("is true", "%1$s is true", FilterValueCount.ZERO, new OperatorEvaluator<Boolean>() {
|
||||
IS_TRUE("lblIsTrue", "%1$s is true", FilterValueCount.ZERO, new OperatorEvaluator<Boolean>() {
|
||||
@Override
|
||||
public boolean apply(Boolean input, List<Boolean> values) {
|
||||
if (input != null) {
|
||||
@@ -387,7 +656,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
IS_FALSE("is false", "%1$s is false", FilterValueCount.ZERO, new OperatorEvaluator<Boolean>() {
|
||||
IS_FALSE("lblIsFalse", "%1$s is false", FilterValueCount.ZERO, new OperatorEvaluator<Boolean>() {
|
||||
@Override
|
||||
public boolean apply(Boolean input, List<Boolean> values) {
|
||||
if (input != null) {
|
||||
@@ -398,7 +667,7 @@ public class AdvancedSearch {
|
||||
}),
|
||||
|
||||
//Numeric operators
|
||||
EQUALS("=", "%1$s = %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
EQUALS("lblEqual", "%1$s = %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
@Override
|
||||
public boolean apply(Integer input, List<Integer> values) {
|
||||
if (input != null) {
|
||||
@@ -407,7 +676,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
NOT_EQUALS("<>", "%1$s <> %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
NOT_EQUALS("lblNotEqual", "%1$s <> %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
@Override
|
||||
public boolean apply(Integer input, List<Integer> values) {
|
||||
if (input != null) {
|
||||
@@ -416,7 +685,7 @@ public class AdvancedSearch {
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
GREATER_THAN(">", "%1$s > %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
GREATER_THAN("lblGreaterThan", "%1$s > %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
@Override
|
||||
public boolean apply(Integer input, List<Integer> values) {
|
||||
if (input != null) {
|
||||
@@ -425,7 +694,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
LESS_THAN("<", "%1$s < %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
LESS_THAN("lblLessThan", "%1$s < %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
@Override
|
||||
public boolean apply(Integer input, List<Integer> values) {
|
||||
if (input != null) {
|
||||
@@ -434,7 +703,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
GT_OR_EQUAL(">=", "%1$s >= %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
GT_OR_EQUAL("lblGreaterThanOrEqual", "%1$s >= %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
@Override
|
||||
public boolean apply(Integer input, List<Integer> values) {
|
||||
if (input != null) {
|
||||
@@ -443,7 +712,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
LT_OR_EQUAL("<=", "%1$s <= %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
LT_OR_EQUAL("lblLessThanOrEqual", "%1$s <= %2$d", FilterValueCount.ONE, new OperatorEvaluator<Integer>() {
|
||||
@Override
|
||||
public boolean apply(Integer input, List<Integer> values) {
|
||||
if (input != null) {
|
||||
@@ -452,7 +721,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
BETWEEN_INCLUSIVE("<=|<=", "%2$d <= %1$s <= %3$d", FilterValueCount.TWO, new OperatorEvaluator<Integer>() {
|
||||
BETWEEN_INCLUSIVE("lblBetweenInclusive", "%2$d <= %1$s <= %3$d", FilterValueCount.TWO, new OperatorEvaluator<Integer>() {
|
||||
@Override
|
||||
public boolean apply(Integer input, List<Integer> values) {
|
||||
if (input != null) {
|
||||
@@ -462,7 +731,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
BETWEEN_EXCLUSIVE("<|<", "%2$d < %1$s < %3$d", FilterValueCount.TWO, new OperatorEvaluator<Integer>() {
|
||||
BETWEEN_EXCLUSIVE("lblBetweenExclusive", "%2$d < %1$s < %3$d", FilterValueCount.TWO, new OperatorEvaluator<Integer>() {
|
||||
@Override
|
||||
public boolean apply(Integer input, List<Integer> values) {
|
||||
if (input != null) {
|
||||
@@ -474,7 +743,7 @@ public class AdvancedSearch {
|
||||
}),
|
||||
|
||||
//String operators
|
||||
CONTAINS("contains", "%1$s contains '%2$s'", FilterValueCount.ONE, new OperatorEvaluator<String>() {
|
||||
CONTAINS("lblContains", "%1$s contains '%2$s'", FilterValueCount.ONE, new OperatorEvaluator<String>() {
|
||||
@Override
|
||||
public boolean apply(String input, List<String> values) {
|
||||
if (input != null) {
|
||||
@@ -482,8 +751,20 @@ public class AdvancedSearch {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean apply(Set<String> inputs, List<String> values) {
|
||||
if (inputs != null && !inputs.isEmpty() && !values.isEmpty()) {
|
||||
for (String input : inputs) {
|
||||
if (apply(input, values)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
STARTS_WITH("starts with", "%1$s starts with '%2$s'", FilterValueCount.ONE, new OperatorEvaluator<String>() {
|
||||
STARTS_WITH("lblStartsWith", "%1$s starts with '%2$s'", FilterValueCount.ONE, new OperatorEvaluator<String>() {
|
||||
@Override
|
||||
public boolean apply(String input, List<String> values) {
|
||||
if (input != null) {
|
||||
@@ -491,8 +772,20 @@ public class AdvancedSearch {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean apply(Set<String> inputs, List<String> values) {
|
||||
if (inputs != null && !inputs.isEmpty() && !values.isEmpty()) {
|
||||
for (String input : inputs) {
|
||||
if (apply(input, values)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
ENDS_WITH("ends with", "%1$s ends with '%2$s'", FilterValueCount.ONE, new OperatorEvaluator<String>() {
|
||||
ENDS_WITH("lblEndsWith", "%1$s ends with '%2$s'", FilterValueCount.ONE, new OperatorEvaluator<String>() {
|
||||
@Override
|
||||
public boolean apply(String input, List<String> values) {
|
||||
if (input != null) {
|
||||
@@ -500,10 +793,22 @@ public class AdvancedSearch {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean apply(Set<String> inputs, List<String> values) {
|
||||
if (inputs != null && !inputs.isEmpty() && !values.isEmpty()) {
|
||||
for (String input : inputs) {
|
||||
if (apply(input, values)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
|
||||
//Custom list operators
|
||||
IS_EXACTLY("is exactly", "%1$s is %2$s", FilterValueCount.MANY, new OperatorEvaluator<Object>() {
|
||||
IS_EXACTLY("lblIsExactly", "%1$s is %2$s", FilterValueCount.MANY, new OperatorEvaluator<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object input, List<Object> values) {
|
||||
if (input != null && values.size() == 1) {
|
||||
@@ -524,7 +829,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
IS_ANY("is any of", "%1$s is %2$s", FilterValueCount.MANY_OR, new OperatorEvaluator<Object>() {
|
||||
IS_ANY("lblIsAnyOf", "%1$s is %2$s", FilterValueCount.MANY_OR, new OperatorEvaluator<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object input, List<Object> values) {
|
||||
if (input != null) {
|
||||
@@ -548,7 +853,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
CONTAINS_ANY("contains any of", "%1$s contains %2$s", FilterValueCount.MANY_OR, new OperatorEvaluator<Object>() {
|
||||
CONTAINS_ANY("lblContainsAnyOf", "%1$s contains %2$s", FilterValueCount.MANY_OR, new OperatorEvaluator<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object input, List<Object> values) {
|
||||
if (input != null) {
|
||||
@@ -572,7 +877,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
CONTAINS_ALL("contains all of", "%1$s contains %2$s", FilterValueCount.MANY_AND, new OperatorEvaluator<Object>() {
|
||||
CONTAINS_ALL("lblContainsAllOf", "%1$s contains %2$s", FilterValueCount.MANY_AND, new OperatorEvaluator<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object input, List<Object> values) {
|
||||
if (input != null && values.size() == 1) {
|
||||
@@ -593,7 +898,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
CONTAIN_ANY("contain any of", "%1$s contain %2$s", FilterValueCount.MANY_OR, new OperatorEvaluator<Object>() {
|
||||
CONTAIN_ANY("lblContainAnyOf", "%1$s contain %2$s", FilterValueCount.MANY_OR, new OperatorEvaluator<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object input, List<Object> values) {
|
||||
throw new RuntimeException("shouldn't be called with a single input");
|
||||
@@ -610,7 +915,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
CONTAIN_ALL("contain all of", "%1$s contain %2$s", FilterValueCount.MANY_AND, new OperatorEvaluator<Object>() {
|
||||
CONTAIN_ALL("lblContainAllOf", "%1$s contain %2$s", FilterValueCount.MANY_AND, new OperatorEvaluator<Object>() {
|
||||
@Override
|
||||
public boolean apply(Object input, List<Object> values) {
|
||||
throw new RuntimeException("shouldn't be called with a single input");
|
||||
@@ -630,7 +935,7 @@ public class AdvancedSearch {
|
||||
}),
|
||||
|
||||
//Deck content operators
|
||||
CONTAINS_CARD("contains card", "%1$s contains %2$s", FilterValueCount.ONE, new OperatorEvaluator<Map<PaperCard, Integer>>() {
|
||||
CONTAINS_CARD("lblContainsCard", "%1$s contains %2$s", FilterValueCount.ONE, new OperatorEvaluator<Map<PaperCard, Integer>>() {
|
||||
@Override
|
||||
public boolean apply(Map<PaperCard, Integer> input, List<Map<PaperCard, Integer>> values) {
|
||||
if (input != null) {
|
||||
@@ -640,7 +945,7 @@ public class AdvancedSearch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
CONTAINS_X_COPIES_OF_CARD("contains X copies of card", "%1$s contains %3$d %2$s", FilterValueCount.TWO, new OperatorEvaluator<Map<PaperCard, Integer>>() {
|
||||
CONTAINS_X_COPIES_OF_CARD("lblContainsXCopiesCard", "%1$s contains %3$d %2$s", FilterValueCount.TWO, new OperatorEvaluator<Map<PaperCard, Integer>>() {
|
||||
@Override
|
||||
public boolean apply(Map<PaperCard, Integer> input, List<Map<PaperCard, Integer>> values) {
|
||||
if (input != null) {
|
||||
@@ -660,6 +965,9 @@ public class AdvancedSearch {
|
||||
public static final FilterOperator[] STRING_OPS = new FilterOperator[] {
|
||||
CONTAINS, STARTS_WITH, ENDS_WITH
|
||||
};
|
||||
public static final FilterOperator[] STRINGS_OPS = new FilterOperator[] {
|
||||
CONTAINS, STARTS_WITH, ENDS_WITH
|
||||
};
|
||||
public static final FilterOperator[] SINGLE_LIST_OPS = new FilterOperator[] {
|
||||
IS_ANY
|
||||
};
|
||||
@@ -681,7 +989,7 @@ public class AdvancedSearch {
|
||||
private final OperatorEvaluator<?> evaluator;
|
||||
|
||||
FilterOperator(String caption0, String formatStr0, FilterValueCount valueCount0, OperatorEvaluator<?> evaluator0) {
|
||||
caption = caption0;
|
||||
caption = Localizer.getInstance().getMessage(caption0);
|
||||
formatStr = formatStr0;
|
||||
valueCount = valueCount0;
|
||||
evaluator = evaluator0;
|
||||
@@ -714,27 +1022,32 @@ public class AdvancedSearch {
|
||||
@SuppressWarnings("unchecked")
|
||||
public final Filter<T> createFilter(FilterOption option, FilterOperator operator) {
|
||||
final List<V> values = getValues(option, operator);
|
||||
if (values == null || values.isEmpty()) { return null; }
|
||||
if (values == null || values.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String caption = getCaption(values, option, operator);
|
||||
|
||||
final OperatorEvaluator<V> evaluator = (OperatorEvaluator<V>)operator.evaluator;
|
||||
Predicate<T> predicate;
|
||||
if (option.operatorOptions == FilterOperator.MULTI_LIST_OPS || option.operatorOptions == FilterOperator.COMBINATION_OPS || option.operatorOptions == FilterOperator.COLLECTION_OPS) {
|
||||
predicate = new Predicate<T>() {
|
||||
@Override
|
||||
public boolean apply(T input) {
|
||||
return evaluator.apply(getItemValues(input), values);
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
predicate = new Predicate<T>() {
|
||||
@Override
|
||||
public boolean apply(T input) {
|
||||
return evaluator.apply(getItemValue(input), values);
|
||||
}
|
||||
};
|
||||
final OperatorEvaluator<V> evaluator = (OperatorEvaluator<V>) operator.evaluator;
|
||||
Predicate<T> predicate = new Predicate<T>() {
|
||||
@Override
|
||||
public boolean apply(T input) {
|
||||
return evaluator.apply(getItemValue(input), values);
|
||||
}
|
||||
};
|
||||
;
|
||||
final FilterOperator[][] manyValueOperators = { FilterOperator.MULTI_LIST_OPS,
|
||||
FilterOperator.COMBINATION_OPS, FilterOperator.COLLECTION_OPS, FilterOperator.STRINGS_OPS };
|
||||
for (FilterOperator[] oper : manyValueOperators) {
|
||||
if (option.operatorOptions == oper) {
|
||||
predicate = new Predicate<T>() {
|
||||
@Override
|
||||
public boolean apply(T input) {
|
||||
return evaluator.apply(getItemValues(input), values);
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new Filter<>(option, operator, caption, predicate);
|
||||
}
|
||||
@@ -930,7 +1243,7 @@ public class AdvancedSearch {
|
||||
|
||||
Integer amount = -1;
|
||||
if (operator == FilterOperator.CONTAINS_X_COPIES_OF_CARD) { //prompt for quantity if needed
|
||||
amount = SGuiChoose.getInteger("How many copies of " + card.getName() + "?", 0, 4);
|
||||
amount = SGuiChoose.getInteger(Localizer.getInstance().getMessage("lblHowManyCopiesOfN", CardTranslation.getTranslatedName(card.getName())), 0, 4);
|
||||
if (amount == null) { return null; }
|
||||
}
|
||||
|
||||
@@ -967,7 +1280,7 @@ public class AdvancedSearch {
|
||||
options.add(opt);
|
||||
}
|
||||
}
|
||||
option = SGuiChoose.oneOrNone("Select a filter type", options, defaultOption, null);
|
||||
option = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblSelectAFilterType"), options, defaultOption, null);
|
||||
if (option == null) { return editFilter; }
|
||||
}
|
||||
else {
|
||||
@@ -979,7 +1292,7 @@ public class AdvancedSearch {
|
||||
final FilterOperator operator;
|
||||
if (option.operatorOptions.length > 1) {
|
||||
final FilterOperator defaultOperator = option == defaultOption ? editFilter.operator : null;
|
||||
operator = SGuiChoose.oneOrNone("Select an operator for " + option.name, option.operatorOptions, defaultOperator, null);
|
||||
operator = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblSelectOperatorFor", option.name), option.operatorOptions, defaultOperator, null);
|
||||
if (operator == null) { return editFilter; }
|
||||
}
|
||||
else {
|
||||
@@ -1049,7 +1362,7 @@ public class AdvancedSearch {
|
||||
}
|
||||
|
||||
public static class Model<T extends InventoryItem> {
|
||||
private static final String EMPTY_FILTER_TEXT = "Select Filter...";
|
||||
private static final String EMPTY_FILTER_TEXT = Localizer.getInstance().getMessage("lblSelectingFilter");
|
||||
|
||||
private final List<Object> expression = new ArrayList<>();
|
||||
private final List<IFilterControl<T>> controls = new ArrayList<>();
|
||||
@@ -1162,7 +1475,7 @@ public class AdvancedSearch {
|
||||
//the first time the user selects keywords, preload keywords for all cards
|
||||
Runnable preloadTask = Keyword.getPreloadTask();
|
||||
if (preloadTask != null) {
|
||||
GuiBase.getInterface().runBackgroundTask("Loading keywords...", preloadTask);
|
||||
GuiBase.getInterface().runBackgroundTask(Localizer.getInstance().getMessage("lblLoadingKeywords"), preloadTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import forge.itemmanager.ItemColumnConfig.SortState;
|
||||
import forge.limited.DraftRankCache;
|
||||
import forge.model.FModel;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.CardTranslation;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Map.Entry;
|
||||
@@ -57,12 +58,16 @@ public enum ColumnDef {
|
||||
new Function<Entry<InventoryItem, Integer>, Comparable<?>>() {
|
||||
@Override
|
||||
public Comparable<?> apply(final Entry<InventoryItem, Integer> from) {
|
||||
if (from.getKey() instanceof PaperCard)
|
||||
return toSortableName(from.getKey().toString());
|
||||
return toSortableName(from.getKey().getName());
|
||||
}
|
||||
},
|
||||
new Function<Entry<? extends InventoryItem, Integer>, Object>() {
|
||||
@Override
|
||||
public Object apply(final Entry<? extends InventoryItem, Integer> from) {
|
||||
if (from.getKey() instanceof PaperCard)
|
||||
return from.getKey().toString();
|
||||
return from.getKey().getName();
|
||||
}
|
||||
}),
|
||||
@@ -86,13 +91,13 @@ public enum ColumnDef {
|
||||
new Function<Entry<InventoryItem, Integer>, Comparable<?>>() {
|
||||
@Override
|
||||
public Comparable<?> apply(final Entry<InventoryItem, Integer> from) {
|
||||
return toType(from.getKey());
|
||||
return CardTranslation.getTranslatedType(from.getKey().getName(),toType(from.getKey()));
|
||||
}
|
||||
},
|
||||
new Function<Entry<? extends InventoryItem, Integer>, Object>() {
|
||||
@Override
|
||||
public Object apply(final Entry<? extends InventoryItem, Integer> from) {
|
||||
return toType(from.getKey());
|
||||
return CardTranslation.getTranslatedType(from.getKey().getName(),toType(from.getKey()));
|
||||
}
|
||||
}),
|
||||
/**The mana cost column.*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -100,6 +100,16 @@ public enum ItemManagerConfig {
|
||||
null, null, 3, 0),
|
||||
NET_DECKS(SColumnUtil.getDecksDefaultColumns(false, false), false, false, false,
|
||||
null, null, 3, 0),
|
||||
NET_ARCHIVE_STANDARD_DECKS(SColumnUtil.getDecksDefaultColumns(false, false), false, false, false,
|
||||
null, null, 3, 0),
|
||||
NET_ARCHIVE_PIONEER_DECKS(SColumnUtil.getDecksDefaultColumns(false, false), false, false, false,
|
||||
null, null, 3, 0),
|
||||
NET_ARCHIVE_MODERN_DECKS(SColumnUtil.getDecksDefaultColumns(false, false), false, false, false,
|
||||
null, null, 3, 0),
|
||||
NET_ARCHIVE_LEGACY_DECKS(SColumnUtil.getDecksDefaultColumns(false, false), false, false, false,
|
||||
null, null, 3, 0),
|
||||
NET_ARCHIVE_VINTAGE_DECKS(SColumnUtil.getDecksDefaultColumns(false, false), false, false, false,
|
||||
null, null, 3, 0),
|
||||
SIDEBOARD(SColumnUtil.getDeckEditorDefaultColumns(), false, false, true,
|
||||
GroupDef.DEFAULT, ColumnDef.CMC, 3, 0);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.itemmanager;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import forge.GuiBase;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.assets.IHasSkinProp;
|
||||
import forge.card.CardRules;
|
||||
@@ -35,7 +36,8 @@ public final class SItemManagerUtil {
|
||||
RED (FSkinProp.IMG_MANA_R, CardRulesPredicates.Presets.IS_RED, "lblRedcards"),
|
||||
GREEN (FSkinProp.IMG_MANA_G, CardRulesPredicates.Presets.IS_GREEN, "lblGreencards"),
|
||||
COLORLESS (FSkinProp.IMG_MANA_COLORLESS, CardRulesPredicates.Presets.IS_COLORLESS, "lblColorlesscards"),
|
||||
MULTICOLOR (FSkinProp.IMG_MULTI, CardRulesPredicates.Presets.IS_MULTICOLOR, "lblMulticolorcards"),
|
||||
MULTICOLOR (GuiBase.getInterface().isLibgdxPort() ? FSkinProp.IMG_HDMULTI :
|
||||
FSkinProp.IMG_MULTI, CardRulesPredicates.Presets.IS_MULTICOLOR, "lblMulticolorcards"),
|
||||
|
||||
PACK_OR_DECK (FSkinProp.IMG_PACK, null, "lblPackordeck"),
|
||||
LAND (FSkinProp.IMG_LAND, CardRulesPredicates.Presets.IS_LAND, "lblLands"),
|
||||
@@ -60,7 +62,8 @@ public final class SItemManagerUtil {
|
||||
DECK_RED (FSkinProp.IMG_MANA_R, null, "lblReddecks"),
|
||||
DECK_GREEN (FSkinProp.IMG_MANA_G, null, "lblGreendecks"),
|
||||
DECK_COLORLESS (FSkinProp.IMG_MANA_COLORLESS, null, "lblColorlessdecks"),
|
||||
DECK_MULTICOLOR (FSkinProp.IMG_MULTI, null, "lblMulticolordecks"),
|
||||
DECK_MULTICOLOR (GuiBase.getInterface().isLibgdxPort() ? FSkinProp.IMG_HDMULTI :
|
||||
FSkinProp.IMG_MULTI, null, "lblMulticolordecks"),
|
||||
|
||||
FOIL_OLD (FSkinProp.FOIL_11, null, "lblOldstyleFoilcards"),
|
||||
FOIL_NEW (FSkinProp.FOIL_01, null, "lblNewstyleFoilcards"),
|
||||
|
||||
@@ -20,7 +20,6 @@ package forge.limited;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.StaticData;
|
||||
import forge.card.CardEdition;
|
||||
import forge.deck.CardPool;
|
||||
@@ -41,7 +40,6 @@ import forge.util.gui.SGuiChoose;
|
||||
import forge.util.gui.SOptionPane;
|
||||
import forge.util.storage.IStorage;
|
||||
import forge.util.Localizer;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.io.File;
|
||||
@@ -57,6 +55,7 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
private final List<LimitedPlayer> players = new ArrayList<>();
|
||||
private LimitedPlayer localPlayer;
|
||||
|
||||
private String doublePickDuringDraft = ""; // "FirstPick" or "Always"
|
||||
protected int nextBoosterGroup = 0;
|
||||
private int currentBoosterSize = 0;
|
||||
private int currentBoosterPick = 0;
|
||||
@@ -142,7 +141,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);
|
||||
doublePickDuringDraft = FModel.getMagicDb().getEditions().get(setCode).getDoublePickDuringDraft();
|
||||
|
||||
final IUnOpenedProduct product1 = block.getBooster(setCode);
|
||||
|
||||
for (int i = 0; i < nPacks; i++) {
|
||||
this.product.add(product1);
|
||||
@@ -176,34 +179,40 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
break;
|
||||
|
||||
case Chaos:
|
||||
/**
|
||||
* A chaos draft consists of boosters from many different sets.
|
||||
* Default settings are boosters from all sets with a booster size of 15 cards.
|
||||
* Alternatively, the sets can be restricted to a format like Modern or to a theme.
|
||||
* Examples for themes: sets that take place on a certain plane, core sets, masters sets,
|
||||
* or sets that share a mechanic.
|
||||
*/
|
||||
// Get chaos draft themes
|
||||
final List<ThemedChaosDraft> themes = new ArrayList<>();
|
||||
final IStorage<ThemedChaosDraft> themeStorage = FModel.getThemedChaosDrafts();
|
||||
for (final ThemedChaosDraft theme : themeStorage) {
|
||||
themes.add(theme);
|
||||
}
|
||||
Collections.sort(themes); // sort for user interface
|
||||
// Ask user to select theme
|
||||
final String dialogQuestion = Localizer.getInstance().getMessage("lblChooseChaosTheme");
|
||||
final ThemedChaosDraft theme = SGuiChoose.oneOrNone(dialogQuestion, themes);
|
||||
if (theme == null) {
|
||||
return false; // abort if no theme is selected
|
||||
}
|
||||
// Filter all sets by theme restrictions
|
||||
final Predicate<CardEdition> themeFilter = theme.getEditionFilter();
|
||||
final CardEdition.Collection allEditions = StaticData.instance().getEditions();
|
||||
final Iterable<CardEdition> chaosDraftEditions = Iterables.filter(allEditions.getOrderedEditions(), new Predicate<CardEdition>() {
|
||||
@Override
|
||||
public boolean apply(final CardEdition cardEdition) {
|
||||
boolean isExpansion = cardEdition.getType().equals(CardEdition.Type.EXPANSION);
|
||||
boolean isCoreSet = cardEdition.getType().equals(CardEdition.Type.CORE);
|
||||
boolean isReprintSet = cardEdition.getType().equals(CardEdition.Type.REPRINT);
|
||||
if (isExpansion || isCoreSet || isReprintSet) {
|
||||
// Only allow sets with 15 cards in booster packs
|
||||
if (cardEdition.hasBoosterTemplate()) {
|
||||
final List<Pair<String, Integer>> slots = cardEdition.getBoosterTemplate().getSlots();
|
||||
int boosterSize = 0;
|
||||
for (Pair<String, Integer> slot : slots) {
|
||||
boosterSize += slot.getRight();
|
||||
}
|
||||
return boosterSize == 15;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Randomize order of sets
|
||||
List<CardEdition> shuffled = Lists.newArrayList(chaosDraftEditions);
|
||||
Collections.shuffle(shuffled);
|
||||
|
||||
final Supplier<List<PaperCard>> ChaosDraftSupplier = new ChaosBoosterSupplier(shuffled);
|
||||
|
||||
final Iterable<CardEdition> chaosDraftEditions = Iterables.filter(
|
||||
allEditions.getOrderedEditions(),
|
||||
themeFilter);
|
||||
// Add chaos "boosters" as special suppliers
|
||||
final Supplier<List<PaperCard>> ChaosDraftSupplier;
|
||||
try {
|
||||
ChaosDraftSupplier = new ChaosBoosterSupplier(chaosDraftEditions);
|
||||
} catch(IllegalArgumentException e) {
|
||||
System.out.println(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
this.product.add(ChaosDraftSupplier);
|
||||
}
|
||||
@@ -220,7 +229,11 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
final BoosterDraft draft = new BoosterDraft(draftType);
|
||||
|
||||
for (String booster : boosters) {
|
||||
draft.product.add(block.getBooster(booster));
|
||||
try {
|
||||
draft.product.add(block.getBooster(booster));
|
||||
} catch (Exception ex) {
|
||||
System.err.println("Booster Draft Error: "+ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
IBoosterDraft.LAND_SET_CODE[0] = block.getLandSet();
|
||||
@@ -349,7 +362,7 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
public Deck[] getDecks() {
|
||||
Deck[] decks = new Deck[7];
|
||||
for (int i = 1; i < N_PLAYERS; i++) {
|
||||
decks[i - 1] = ((LimitedPlayerAI) this.players.get(i)).buildDeck();
|
||||
decks[i - 1] = ((LimitedPlayerAI) this.players.get(i)).buildDeck(IBoosterDraft.LAND_SET_CODE[0] != null ? IBoosterDraft.LAND_SET_CODE[0].getCode() : null);
|
||||
}
|
||||
return decks;
|
||||
}
|
||||
@@ -357,6 +370,13 @@ public class BoosterDraft implements IBoosterDraft {
|
||||
public void passPacks() {
|
||||
// Alternate direction of pack passing
|
||||
int adjust = this.nextBoosterGroup % 2 == 1 ? 1 : -1;
|
||||
if ("FirstPick".equals(this.doublePickDuringDraft) && currentBoosterPick == 1) {
|
||||
adjust = 0;
|
||||
} else if (currentBoosterPick % 2 == 1 && "Always".equals(this.doublePickDuringDraft)) {
|
||||
// This may not work with Conspiracy cards that mess with the draft
|
||||
adjust = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < N_PLAYERS; i++) {
|
||||
List<PaperCard> passingPack = this.players.get(i).passPack();
|
||||
|
||||
@@ -494,26 +514,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
|
||||
if(FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.FILTERED_HANDS)){
|
||||
baseLandParameter--;
|
||||
}
|
||||
landsNeeded = new Double((baseLandParameter + 3.14f * avCMC) * targetSize/60f).intValue();
|
||||
landsNeeded = Double.valueOf((baseLandParameter + 3.14f * avCMC) * targetSize/60f).intValue();
|
||||
if (logToConsole) {
|
||||
System.out.println("Required lands from linear regression : " + avCMC + " cmc, needed: " + landsNeeded);
|
||||
}
|
||||
@@ -425,8 +425,6 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
|
||||
|
||||
@Override
|
||||
public boolean apply(CardRules subject) {
|
||||
ManaCost mc = subject.getManaCost();
|
||||
boolean generic = mc.isPureGeneric();
|
||||
return ((allowedColor.containsAllColorsFrom(subject.getColorIdentity().getColor())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ public class LimitedPlayerAI extends LimitedPlayer {
|
||||
return bestPick;
|
||||
}
|
||||
|
||||
public Deck buildDeck() {
|
||||
public Deck buildDeck(String landSetCode) {
|
||||
CardPool section = deck.getOrCreate(DeckSection.Sideboard);
|
||||
return new BoosterDeckBuilder(section.toFlatList(), deckCols).buildDeck();
|
||||
return new BoosterDeckBuilder(section.toFlatList(), deckCols).buildDeck(landSetCode);
|
||||
}
|
||||
}
|
||||
|
||||
218
forge-gui/src/main/java/forge/limited/ThemedChaosDraft.java
Normal file
218
forge-gui/src/main/java/forge/limited/ThemedChaosDraft.java
Normal file
@@ -0,0 +1,218 @@
|
||||
package forge.limited;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
import forge.card.CardEdition;
|
||||
import forge.game.GameFormat;
|
||||
import forge.model.FModel;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.storage.StorageReaderFile;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Themed chaos draft allows limiting the pool of available random boosters for a draft to a certain theme.
|
||||
*/
|
||||
public class ThemedChaosDraft implements Comparable<ThemedChaosDraft> {
|
||||
private final String tag;
|
||||
private final String label;
|
||||
private final int orderNumber;
|
||||
|
||||
/**
|
||||
* @param tag Tag name used in edition files.
|
||||
* @param label Label used in user interface.
|
||||
* @param orderNumber Number used to order entries in user interface.
|
||||
*/
|
||||
public ThemedChaosDraft(String tag, String label, int orderNumber) {
|
||||
this.tag = tag;
|
||||
this.label = label;
|
||||
this.orderNumber = orderNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return theme tag
|
||||
*/
|
||||
public String getTag() { return tag; }
|
||||
|
||||
/**
|
||||
* @return theme label
|
||||
*/
|
||||
public String getLabel() { return label; }
|
||||
|
||||
/**
|
||||
* @return theme order number
|
||||
*/
|
||||
public int getOrderNumber() { return orderNumber; }
|
||||
|
||||
/**
|
||||
* @return Predicate to sort out editions not belonging to the chaos draft theme
|
||||
*/
|
||||
public Predicate<CardEdition> getEditionFilter() {
|
||||
Predicate<CardEdition> filter;
|
||||
switch(tag) {
|
||||
case "DEFAULT":
|
||||
filter = DEFAULT_FILTER;
|
||||
break;
|
||||
case "MODERN":
|
||||
case "PIONEER":
|
||||
case "STANDARD":
|
||||
filter = getFormatFilter(tag);
|
||||
break;
|
||||
default:
|
||||
filter = themedFilter;
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter to select editions by ChaosDraftThemes tag defined in edition files.
|
||||
* Tag must be defined in res/blockdata/chaosdraftthemes.txt
|
||||
*/
|
||||
private final Predicate<CardEdition> themedFilter = new Predicate<CardEdition>() {
|
||||
@Override
|
||||
public boolean apply(final CardEdition cardEdition) {
|
||||
String[] themes = cardEdition.getChaosDraftThemes();
|
||||
for (String theme : themes) {
|
||||
if (tag.equals(theme)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param formatName format to filter by, currently supported: MODERN, PIONEER, STANDARD
|
||||
* @return Filter to select editions belonging to a certain constructed format.
|
||||
*/
|
||||
private Predicate<CardEdition> getFormatFilter(String formatName) {
|
||||
GameFormat.Collection formats = FModel.getFormats();
|
||||
GameFormat format;
|
||||
switch(formatName) {
|
||||
case "MODERN":
|
||||
format = formats.getModern();
|
||||
break;
|
||||
case "PIONEER":
|
||||
format = formats.getPioneer();
|
||||
break;
|
||||
case "STANDARD":
|
||||
default:
|
||||
format = formats.getStandard();
|
||||
}
|
||||
return new Predicate<CardEdition>() {
|
||||
@Override
|
||||
public boolean apply(final CardEdition cardEdition){
|
||||
return DEFAULT_FILTER.apply(cardEdition) && format.isSetLegal(cardEdition.getCode());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Default filter that only allows actual sets that were printed as 15-card boosters
|
||||
*/
|
||||
private static final Predicate<CardEdition> DEFAULT_FILTER = new Predicate<CardEdition>() {
|
||||
@Override
|
||||
public boolean apply(final CardEdition cardEdition) {
|
||||
boolean isExpansion = cardEdition.getType().equals(CardEdition.Type.EXPANSION);
|
||||
boolean isCoreSet = cardEdition.getType().equals(CardEdition.Type.CORE);
|
||||
boolean isReprintSet = cardEdition.getType().equals(CardEdition.Type.REPRINT);
|
||||
if (isExpansion || isCoreSet || isReprintSet) {
|
||||
// Only allow sets with 15 cards in booster packs
|
||||
if (cardEdition.hasBoosterTemplate()) {
|
||||
final List<Pair<String, Integer>> slots = cardEdition.getBoosterTemplate().getSlots();
|
||||
int boosterSize = 0;
|
||||
for (Pair<String, Integer> slot : slots) {
|
||||
boosterSize += slot.getRight();
|
||||
}
|
||||
return boosterSize == 15;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + ((this.tag == null) ? 0 : this.tag.hashCode());
|
||||
result = (prime * result) + ((this.label == null) ? 0 : this.label.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Comparable#compareTo(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(ThemedChaosDraft other) {
|
||||
return (this.orderNumber != other.orderNumber)
|
||||
? this.orderNumber - other.orderNumber
|
||||
: this.label.compareTo(other.label);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ThemedChaosDraft other = (ThemedChaosDraft) obj;
|
||||
if (!this.label.equals(other.label)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.tag.equals(other.tag)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final Function<ThemedChaosDraft, String> FN_GET_TAG = new Function<ThemedChaosDraft, String>() {
|
||||
@Override
|
||||
public String apply(ThemedChaosDraft themedChaosBooster) {
|
||||
return themedChaosBooster.getTag();
|
||||
}
|
||||
};
|
||||
|
||||
public static class Reader extends StorageReaderFile<ThemedChaosDraft> {
|
||||
public Reader(String pathname) {
|
||||
super(pathname, ThemedChaosDraft.FN_GET_TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThemedChaosDraft read(String line, int idx) {
|
||||
final String[] sParts = TextUtil.splitWithParenthesis(line, ',', 3);
|
||||
int orderNumber = Integer.parseInt(sParts[0].trim(), 10);
|
||||
String tag = sParts[1].trim();
|
||||
String label = sParts[2].trim();
|
||||
return new ThemedChaosDraft(tag, label, orderNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,6 @@ import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.util.Localizer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -20,8 +18,10 @@ import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.GuiBase;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.game.GameView;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.event.GameEventSpellAbilityCast;
|
||||
@@ -31,9 +31,11 @@ import forge.interfaces.IGameController;
|
||||
import forge.interfaces.IGuiGame;
|
||||
import forge.interfaces.IMayViewCards;
|
||||
import forge.model.FModel;
|
||||
import forge.player.PlayerControllerHuman;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.trackable.TrackableTypes;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
private PlayerView currentPlayer = null;
|
||||
@@ -41,6 +43,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
private final Map<PlayerView, IGameController> gameControllers = Maps.newHashMap();
|
||||
private final Map<PlayerView, IGameController> originalGameControllers = Maps.newHashMap();
|
||||
private boolean gamePause = false;
|
||||
private boolean ignoreConcedeChain = false;
|
||||
|
||||
public final boolean hasLocalPlayers() {
|
||||
return !gameControllers.isEmpty();
|
||||
@@ -182,8 +185,16 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
return true; //if not in game, card can be shown
|
||||
}
|
||||
if(GuiBase.getInterface().isLibgdxPort()){
|
||||
if(gameView != null && 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...
|
||||
for (Map.Entry<PlayerView, IGameController> e : gameControllers.entrySet()) {
|
||||
if (e.getValue().equals(spectator)) {
|
||||
gameControllers.remove(e.getKey());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
try{
|
||||
@@ -217,7 +228,13 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
case Flipped:
|
||||
case Transformed:
|
||||
case Meld:
|
||||
case Modal:
|
||||
return true;
|
||||
case Adventure:
|
||||
if (cv.isFaceDown()) {
|
||||
return getCurrentPlayer() == null || cv.canFaceDownBeShownToAny(getLocalPlayers());
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -266,6 +283,16 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
}
|
||||
public boolean isGamePaused() { return gamePause; }
|
||||
public void setgamePause(boolean pause) { gamePause = pause; }
|
||||
public void pauseMatch() {
|
||||
IGameController controller = spectator;
|
||||
if(controller != null && !isGamePaused())
|
||||
controller.selectButtonOk();
|
||||
}
|
||||
public void resumeMatch() {
|
||||
IGameController controller = spectator;
|
||||
if(controller != null && isGamePaused())
|
||||
controller.selectButtonOk();
|
||||
}
|
||||
|
||||
/** Concede game, bring up WinLose UI. */
|
||||
public boolean concede() {
|
||||
@@ -273,23 +300,42 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
return true;
|
||||
}
|
||||
if (hasLocalPlayers()) {
|
||||
if (showConfirmDialog(Localizer.getInstance().getMessage("lblConcedeCurrentGame"), Localizer.getInstance().getMessage("lblConcedeTitle"), Localizer.getInstance().getMessage("lblConcede"), Localizer.getInstance().getMessage("lblCancel"))) {
|
||||
for (final IGameController c : getOriginalGameControllers()) {
|
||||
// Concede each player on this Gui (except mind-controlled players)
|
||||
c.concede();
|
||||
boolean concedeNeeded = false;
|
||||
// check if anyone still needs to confirm
|
||||
for (final IGameController c : getOriginalGameControllers()) {
|
||||
if (c instanceof PlayerControllerHuman) {
|
||||
if (((PlayerControllerHuman) c).getPlayer().getOutcome() == null) {
|
||||
concedeNeeded = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (concedeNeeded) {
|
||||
if (showConfirmDialog(Localizer.getInstance().getMessage("lblConcedeCurrentGame"), Localizer.getInstance().getMessage("lblConcedeTitle"), Localizer.getInstance().getMessage("lblConcede"), Localizer.getInstance().getMessage("lblCancel"))) {
|
||||
for (final IGameController c : getOriginalGameControllers()) {
|
||||
// Concede each player on this Gui (except mind-controlled players)
|
||||
c.concede();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return !ignoreConcedeChain;
|
||||
}
|
||||
if (gameView.isGameOver()) {
|
||||
// Don't immediately close, wait for win/lose screen
|
||||
return false;
|
||||
} else {
|
||||
for (PlayerView player : getLocalPlayers()){
|
||||
if (!player.isAI()){
|
||||
}
|
||||
else {
|
||||
// since the nextGameDecision might come from somewhere else it will try and concede too
|
||||
ignoreConcedeChain = true;
|
||||
for (PlayerView player : getLocalPlayers()) {
|
||||
if (!player.isAI()) {
|
||||
getGameController(player).nextGameDecision(NextGameDecision.QUIT);
|
||||
}
|
||||
}
|
||||
ignoreConcedeChain = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -352,11 +398,14 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
return autoPassUntilEndOfTurn.contains(player);
|
||||
}
|
||||
|
||||
private final Timer awaitNextInputTimer = new Timer();
|
||||
private Timer awaitNextInputTimer;
|
||||
private TimerTask awaitNextInputTask;
|
||||
|
||||
@Override
|
||||
public final void awaitNextInput() {
|
||||
if (awaitNextInputTimer == null) {
|
||||
awaitNextInputTimer = new Timer("awaitNextInputTimer Game:" + this.gameView.getId() + " Player:" + this.currentPlayer.getLobbyPlayerName());
|
||||
}
|
||||
//delay updating prompt to await next input briefly so buttons don't flicker disabled then enabled
|
||||
awaitNextInputTask = new TimerTask() {
|
||||
@Override
|
||||
@@ -384,6 +433,9 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
|
||||
@Override
|
||||
public final void cancelAwaitNextInput() {
|
||||
if (awaitNextInputTimer == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (awaitNextInputTimer) { //ensure task doesn't reset awaitNextInputTask during this block
|
||||
if (awaitNextInputTask != null) {
|
||||
try {
|
||||
@@ -569,7 +621,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 +633,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) {
|
||||
@@ -615,6 +667,9 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
|
||||
@Override
|
||||
public <T> List<T> many(final String title, final String topCaption, final int min, final int max, final List<T> sourceChoices, final CardView c) {
|
||||
if (max == 1) {
|
||||
return getChoices(title, min, max, sourceChoices);
|
||||
}
|
||||
final int m2 = min >= 0 ? sourceChoices.size() - min : -1;
|
||||
final int m1 = max >= 0 ? sourceChoices.size() - max : -1;
|
||||
return order(title, topCaption, m1, m2, sourceChoices, null, c, false);
|
||||
@@ -705,5 +760,17 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
public void notifyStackRemoval(GameEventSpellRemovedFromStack event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleLandPlayed(Card land) {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterGameEnd() {
|
||||
if (awaitNextInputTimer != null) {
|
||||
awaitNextInputTimer.cancel();
|
||||
awaitNextInputTimer = null;
|
||||
}
|
||||
}
|
||||
// End of Choice code
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import forge.game.GameRules;
|
||||
import forge.game.GameType;
|
||||
import forge.game.GameView;
|
||||
import forge.game.Match;
|
||||
import forge.game.event.*;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.player.RegisteredPlayer;
|
||||
@@ -68,6 +69,7 @@ public class HostedMatch {
|
||||
private final MatchUiEventVisitor visitor = new MatchUiEventVisitor();
|
||||
private final Map<PlayerControllerHuman, NextGameDecision> nextGameDecisions = Maps.newHashMap();
|
||||
private boolean isMatchOver = false;
|
||||
public int subGameCount = 0;
|
||||
|
||||
public HostedMatch() {}
|
||||
|
||||
@@ -126,6 +128,8 @@ public class HostedMatch {
|
||||
title = TextUtil.concatNoSpace("Multiplayer Game (", String.valueOf(sortedPlayers.size()), " players)");
|
||||
}
|
||||
this.match = new Match(gameRules, sortedPlayers, title);
|
||||
this.match.subscribeToEvents(SoundSystem.instance);
|
||||
this.match.subscribeToEvents(visitor);
|
||||
startGame();
|
||||
}
|
||||
|
||||
@@ -136,8 +140,7 @@ public class HostedMatch {
|
||||
|
||||
public void restartMatch() {
|
||||
endCurrentGame();
|
||||
this.match = new Match(match.getRules(), match.getPlayers(), this.title);
|
||||
startGame();
|
||||
startMatch(match.getRules(), null, match.getPlayers(), this.guis);
|
||||
}
|
||||
|
||||
public void startGame() {
|
||||
@@ -149,7 +152,7 @@ public class HostedMatch {
|
||||
if (game.getRules().getGameType() == GameType.Quest) {
|
||||
final QuestController qc = FModel.getQuest();
|
||||
// Reset new list when the Match round starts, not when each game starts
|
||||
if (game.getMatch().getPlayedGames().isEmpty()) {
|
||||
if (game.getMatch().getOutcomes().isEmpty()) {
|
||||
qc.getCards().resetNewList();
|
||||
}
|
||||
game.subscribeToEvents(qc); // this one listens to player's mulligans ATM
|
||||
@@ -295,6 +298,7 @@ public class HostedMatch {
|
||||
|
||||
public void endCurrentGame() {
|
||||
if (game == null) { return; }
|
||||
boolean isMatchOver = game.getView().isMatchOver();
|
||||
|
||||
game = null;
|
||||
|
||||
@@ -304,7 +308,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();
|
||||
}
|
||||
@@ -327,7 +334,7 @@ public class HostedMatch {
|
||||
return isMatchOver;
|
||||
}
|
||||
|
||||
private final class MatchUiEventVisitor implements IUiEventVisitor<Void> {
|
||||
private final class MatchUiEventVisitor extends IGameEventVisitor.Base<Void> implements IUiEventVisitor<Void> {
|
||||
@Override
|
||||
public Void visit(final UiEventBlockerAssigned event) {
|
||||
for (final PlayerControllerHuman humanController : humanControllers) {
|
||||
@@ -354,6 +361,77 @@ public class HostedMatch {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventSubgameStart event) {
|
||||
subGameCount++;
|
||||
event.subgame.subscribeToEvents(SoundSystem.instance);
|
||||
event.subgame.subscribeToEvents(visitor);
|
||||
|
||||
final GameView gameView = event.subgame.getView();
|
||||
|
||||
Runnable switchGameView = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (final Player p : event.subgame.getPlayers()) {
|
||||
if (p.getController() instanceof PlayerControllerHuman) {
|
||||
final PlayerControllerHuman humanController = (PlayerControllerHuman) p.getController();
|
||||
final IGuiGame gui = guis.get(p.getRegisteredPlayer());
|
||||
humanController.setGui(gui);
|
||||
gui.setGameView(null);
|
||||
gui.setGameView(gameView);
|
||||
gui.setOriginalGameController(p.getView(), humanController);
|
||||
gui.openView(new TrackableCollection<>(p.getView()));
|
||||
gui.setGameView(null);
|
||||
gui.setGameView(gameView);
|
||||
event.subgame.subscribeToEvents(new FControlGameEventHandler(humanController));
|
||||
gui.message(event.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (GuiBase.getInterface().isLibgdxPort())
|
||||
GuiBase.getInterface().invokeInEdtNow(switchGameView);
|
||||
else
|
||||
GuiBase.getInterface().invokeInEdtAndWait(switchGameView);
|
||||
|
||||
//ensure opponents set properly
|
||||
for (final Player p : event.subgame.getPlayers()) {
|
||||
p.updateOpponentsForView();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventSubgameEnd event) {
|
||||
final GameView gameView = event.maingame.getView();
|
||||
Runnable switchGameView = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (final Player p : event.maingame.getPlayers()) {
|
||||
if (p.getController() instanceof PlayerControllerHuman) {
|
||||
final PlayerControllerHuman humanController = (PlayerControllerHuman) p.getController();
|
||||
final IGuiGame gui = guis.get(p.getRegisteredPlayer());
|
||||
gui.setGameView(null);
|
||||
gui.setGameView(gameView);
|
||||
gui.setOriginalGameController(p.getView(), humanController);
|
||||
gui.openView(new TrackableCollection<>(p.getView()));
|
||||
gui.setGameView(null);
|
||||
gui.setGameView(gameView);
|
||||
gui.updatePhase();
|
||||
gui.message(event.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (GuiBase.getInterface().isLibgdxPort())
|
||||
GuiBase.getInterface().invokeInEdtNow(switchGameView);
|
||||
else
|
||||
GuiBase.getInterface().invokeInEdtAndWait(switchGameView);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void receiveEvent(final UiEvent evt) {
|
||||
try {
|
||||
@@ -363,6 +441,16 @@ public class HostedMatch {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void receiveGameEvent(final GameEvent evt) {
|
||||
try {
|
||||
evt.visit(this);
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addNextGameDecision(final PlayerControllerHuman controller, final NextGameDecision decision) {
|
||||
|
||||
@@ -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;
|
||||
@@ -268,7 +269,7 @@ public class InputAttack extends InputSyncronizedBase {
|
||||
combat.addAttacker(card, currentDefender, activeBand);
|
||||
activateBand(activeBand);
|
||||
|
||||
card.getGame().fireEvent(new UiEventAttackerDeclared(
|
||||
card.getGame().getMatch().fireEvent(new UiEventAttackerDeclared(
|
||||
CardView.get(card),
|
||||
GameEntityView.get(currentDefender)));
|
||||
}
|
||||
@@ -279,7 +280,7 @@ public class InputAttack extends InputSyncronizedBase {
|
||||
// When removing an attacker clear the attacking band
|
||||
activateBand(null);
|
||||
|
||||
card.getGame().fireEvent(new UiEventAttackerDeclared(
|
||||
card.getGame().getMatch().fireEvent(new UiEventAttackerDeclared(
|
||||
CardView.get(card), null));
|
||||
return true;
|
||||
}
|
||||
@@ -327,7 +328,7 @@ public class InputAttack extends InputSyncronizedBase {
|
||||
|
||||
private void updateMessage() {
|
||||
Localizer localizer = Localizer.getInstance();
|
||||
String message = localizer.getMessage("lblSelectAttackCreatures") + currentDefender + localizer.getMessage("lblSelectAttackTarget");
|
||||
String message = localizer.getMessage("lblSelectAttackCreatures") + " " + currentDefender + " " + localizer.getMessage("lblSelectAttackTarget");
|
||||
if (potentialBanding) {
|
||||
message += localizer.getMessage("lblSelectBandingTarget");
|
||||
}
|
||||
@@ -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;
|
||||
@@ -85,10 +86,13 @@ public class InputBlock extends InputSyncronizedBase {
|
||||
}
|
||||
else {
|
||||
String attackerName = currentAttacker.isFaceDown() ? localizer.getMessage("lblMorph") : currentAttacker.getName() + " (" + currentAttacker.getId() + ")";
|
||||
String message = localizer.getMessage("lblSelectBlocker") + attackerName + localizer.getMessage("lblOrSelectBlockTarget");
|
||||
String message = localizer.getMessage("lblSelectBlocker") + attackerName + " " + localizer.getMessage("lblOrSelectBlockTarget");
|
||||
showMessage(message);
|
||||
}
|
||||
|
||||
if (combat != null)
|
||||
getController().getGame().fireEvent(new GameEventCombatUpdate(combat.getAttackers(), combat.getAllBlockers()));
|
||||
|
||||
getController().getGui().showCombat();
|
||||
}
|
||||
|
||||
@@ -118,7 +122,7 @@ public class InputBlock extends InputSyncronizedBase {
|
||||
boolean isCorrectAction = false;
|
||||
if (triggerEvent != null && triggerEvent.getButton() == 3 && card.getController() == defender) {
|
||||
combat.removeFromCombat(card);
|
||||
card.getGame().fireEvent(new UiEventBlockerAssigned(CardView.get(card), null));
|
||||
card.getGame().getMatch().fireEvent(new UiEventBlockerAssigned(CardView.get(card), null));
|
||||
isCorrectAction = true;
|
||||
}
|
||||
else {
|
||||
@@ -133,14 +137,14 @@ public class InputBlock extends InputSyncronizedBase {
|
||||
if (combat.isBlocking(card, currentAttacker)) {
|
||||
//if creature already blocking current attacker, remove blocker from combat
|
||||
combat.removeBlockAssignment(currentAttacker, card);
|
||||
card.getGame().fireEvent(new UiEventBlockerAssigned(CardView.get(card), null));
|
||||
card.getGame().getMatch().fireEvent(new UiEventBlockerAssigned(CardView.get(card), null));
|
||||
isCorrectAction = true;
|
||||
}
|
||||
else {
|
||||
isCorrectAction = CombatUtil.canBlock(currentAttacker, card, combat);
|
||||
if (isCorrectAction) {
|
||||
combat.addBlocker(currentAttacker, card);
|
||||
card.getGame().fireEvent(new UiEventBlockerAssigned(
|
||||
card.getGame().getMatch().fireEvent(new UiEventBlockerAssigned(
|
||||
CardView.get(card),
|
||||
CardView.get(currentAttacker)));
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
|
||||
}
|
||||
else {
|
||||
sb.append(startingPlayer.getName()).append(" ").append(localizer.getMessage("lblIsGoingFirst")).append(".\n");
|
||||
sb.append(player).append(", ").append(localizer.getMessage("lblYouAreGoing")).append(" ").append(Lang.getOrdinal(game.getPosition(player, startingPlayer))).append(".\n\n");
|
||||
sb.append(player).append(", ").append(localizer.getMessage("lblYouAreGoing")).append(" ").append(Lang.getInstance().getOrdinal(game.getPosition(player, startingPlayer))).append(".\n\n");
|
||||
}
|
||||
|
||||
getController().getGui().updateButtons(getOwner(), localizer.getMessage("lblKeep"), localizer.getMessage("lblMulligan"), true, true, true);
|
||||
|
||||
@@ -4,7 +4,6 @@ import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerView;
|
||||
@@ -17,10 +16,8 @@ public class InputLockUI implements Input {
|
||||
private final AtomicInteger iCall = new AtomicInteger();
|
||||
|
||||
private final InputQueue inputQueue;
|
||||
private final Game game;
|
||||
private final PlayerControllerHuman controller;
|
||||
public InputLockUI(final Game game0, final InputQueue inputQueue0, final PlayerControllerHuman controller) {
|
||||
game = game0;
|
||||
public InputLockUI(final InputQueue inputQueue0, final PlayerControllerHuman controller) {
|
||||
inputQueue = inputQueue0;
|
||||
this.controller = controller;
|
||||
}
|
||||
@@ -90,7 +87,7 @@ public class InputLockUI implements Input {
|
||||
@Override
|
||||
public void selectButtonCancel() {
|
||||
//cancel auto pass for all players
|
||||
for (final Player player : game.getPlayers()) {
|
||||
for (final Player player : controller.getGame().getPlayers()) {
|
||||
player.getController().autoPassCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public class InputLondonMulligan extends InputSyncronizedBase {
|
||||
public final void showMessage() {
|
||||
final Localizer localizer = Localizer.getInstance();
|
||||
final Game game = player.getGame();
|
||||
game.getView().updateIsMulligan(true);
|
||||
int cardsLeft = toReturn - selected.size();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -79,6 +80,7 @@ public class InputLondonMulligan extends InputSyncronizedBase {
|
||||
|
||||
private void done() {
|
||||
resetCardHighlights();
|
||||
getController().getGame().getView().updateIsMulligan(false);
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ package forge.match.input;
|
||||
import java.util.*;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityKey;
|
||||
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;
|
||||
@@ -16,14 +16,11 @@ import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.AbilityManaPart;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.player.HumanPlay;
|
||||
@@ -75,7 +72,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
// Mobile Forge allows to tap cards underneath the current card even if the current one is tapped
|
||||
if (otherCardsToSelect != null) {
|
||||
for (Card c : otherCardsToSelect) {
|
||||
for (SpellAbility sa : c.getManaAbilities()) {
|
||||
for (SpellAbility sa : getAllManaAbilities(c)) {
|
||||
if (sa.canPlay()) {
|
||||
delaySelectCards.add(c);
|
||||
break;
|
||||
@@ -83,33 +80,48 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!card.getManaAbilities().isEmpty() && activateManaAbility(card)) {
|
||||
if (!getAllManaAbilities(card).isEmpty() && activateManaAbility(card)) {
|
||||
return true;
|
||||
}
|
||||
return activateDelayedCard();
|
||||
} else {
|
||||
List<SpellAbility> manaAbilities = getAllManaAbilities(card);
|
||||
// Desktop Forge floating menu functionality
|
||||
if (card.getManaAbilities().size() == 1) {
|
||||
activateManaAbility(card, card.getManaAbilities().get(0));
|
||||
if (manaAbilities.size() == 1) {
|
||||
activateManaAbility(card, manaAbilities.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, manaAbilities, triggerEvent);
|
||||
if (spellAbility != null) {
|
||||
activateManaAbility(card, spellAbility);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected List<SpellAbility> getAllManaAbilities(Card card) {
|
||||
List<SpellAbility> result = Lists.newArrayList();
|
||||
for (SpellAbility sa : card.getManaAbilities()) {
|
||||
result.add(sa);
|
||||
result.addAll(GameActionUtil.getAlternativeCosts(sa, player));
|
||||
}
|
||||
final Collection<SpellAbility> toRemove = Lists.newArrayListWithCapacity(result.size());
|
||||
for (final SpellAbility sa : result) {
|
||||
sa.setActivatingPlayer(player);
|
||||
// fix things like retrace
|
||||
// check only if SA can't be cast normally
|
||||
if (sa.canPlay(true)) {
|
||||
continue;
|
||||
}
|
||||
toRemove.add(sa);
|
||||
}
|
||||
result.removeAll(toRemove);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivateAction(Card card) {
|
||||
for (SpellAbility sa : card.getManaAbilities()) {
|
||||
for (SpellAbility sa : getAllManaAbilities(card)) {
|
||||
if (sa.canPlay()) {
|
||||
return "pay mana with card";
|
||||
}
|
||||
@@ -139,6 +151,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public List<SpellAbility> getUsefulManaAbilities(Card card) {
|
||||
List<SpellAbility> abilities = new ArrayList<>();
|
||||
|
||||
@@ -155,7 +168,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
if (manaCost.isAnyPartPayableWith((byte) ManaAtom.GENERIC, player.getManaPool())) {
|
||||
colorCanUse |= ManaAtom.GENERIC;
|
||||
}
|
||||
if (colorCanUse == 0) { // no mana cost or something
|
||||
if (colorCanUse == 0) { // no mana cost or something
|
||||
return abilities;
|
||||
}
|
||||
|
||||
@@ -164,15 +177,10 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
return abilities;
|
||||
}
|
||||
|
||||
for (SpellAbility ma : card.getManaAbilities()) {
|
||||
for (SpellAbility ma : getAllManaAbilities(card)) {
|
||||
ma.setActivatingPlayer(player);
|
||||
AbilityManaPart m = ma.getManaPartRecursive();
|
||||
if (m == null || !ma.canPlay()) { continue; }
|
||||
if (!abilityProducesManaColor(ma, m, colorCanUse)) { continue; }
|
||||
if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) { continue; }
|
||||
if (!m.meetsManaRestrictions(saPaidFor)) { continue; }
|
||||
|
||||
abilities.add(ma);
|
||||
if (ma.isManaAbilityFor(saPaidFor, colorCanUse))
|
||||
abilities.add(ma);
|
||||
}
|
||||
return abilities;
|
||||
}
|
||||
@@ -193,11 +201,8 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
System.err.print("Should wait till previous call to playAbility finishes.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// make sure computer's lands aren't selected
|
||||
if (card.getController() != player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte colorCanUse = 0;
|
||||
byte colorNeeded = 0;
|
||||
@@ -210,106 +215,96 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
colorCanUse |= ManaAtom.GENERIC;
|
||||
}
|
||||
|
||||
if (colorCanUse == 0) { // no mana cost or something
|
||||
if (colorCanUse == 0) { // no mana cost or something
|
||||
return false;
|
||||
}
|
||||
|
||||
HashMap<SpellAbilityView, SpellAbility> abilitiesMap = new HashMap<>();
|
||||
// you can't remove unneeded abilities inside a for (am:abilities) loop :(
|
||||
|
||||
final String typeRes = manaCost.getSourceRestriction();
|
||||
if (StringUtils.isNotBlank(typeRes) && !card.getType().hasStringType(typeRes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean guessAbilityWithRequiredColors = true;
|
||||
int amountOfMana = -1;
|
||||
for (SpellAbility ma : card.getManaAbilities()) {
|
||||
ma.setActivatingPlayer(player);
|
||||
|
||||
AbilityManaPart m = ma.getManaPartRecursive();
|
||||
if (m == null || !ma.canPlay()) { continue; }
|
||||
if (!abilityProducesManaColor(ma, m, colorCanUse)) { continue; }
|
||||
if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) { continue; }
|
||||
if (!m.meetsManaRestrictions(saPaidFor)) { continue; }
|
||||
|
||||
// If Mana Abilities produce differing amounts of mana, let the player choose
|
||||
int maAmount = GameActionUtil.amountOfManaGenerated(ma, true);
|
||||
if (amountOfMana == -1) {
|
||||
amountOfMana = maAmount;
|
||||
} else {
|
||||
if (amountOfMana != maAmount) {
|
||||
guessAbilityWithRequiredColors = false;
|
||||
}
|
||||
}
|
||||
|
||||
abilitiesMap.put(ma.getView(), ma);
|
||||
|
||||
// skip express mana if the ability is not undoable or reusable
|
||||
if (!ma.isUndoable() || !ma.getPayCosts().isRenewableResource() || ma.getSubAbility() != null) {
|
||||
guessAbilityWithRequiredColors = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (abilitiesMap.isEmpty() || (chosenAbility != null && !abilitiesMap.containsKey(chosenAbility.getView()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store some information about color costs to help with any mana choices
|
||||
if (colorNeeded == 0) { // only colorless left
|
||||
if (saPaidFor.getHostCard() != null && saPaidFor.getHostCard().hasSVar("ManaNeededToAvoidNegativeEffect")) {
|
||||
String[] negEffects = saPaidFor.getHostCard().getSVar("ManaNeededToAvoidNegativeEffect").split(",");
|
||||
for (String negColor : negEffects) {
|
||||
byte col = ManaAtom.fromName(negColor);
|
||||
colorCanUse |= col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the card has any ability that tracks mana spent, skip express Mana choice
|
||||
if (saPaidFor.tracksManaSpent()) {
|
||||
colorCanUse = ColorSet.ALL_COLORS.getColor();
|
||||
guessAbilityWithRequiredColors = false;
|
||||
}
|
||||
|
||||
boolean choice = true;
|
||||
boolean isPayingGeneric = false;
|
||||
if (guessAbilityWithRequiredColors) {
|
||||
// express Mana Choice
|
||||
if (colorNeeded == 0) {
|
||||
choice = false;
|
||||
//avoid unnecessary prompt by pretending we need White
|
||||
//for the sake of "Add one mana of any color" effects
|
||||
colorNeeded = MagicColor.WHITE;
|
||||
isPayingGeneric = true; // for further processing
|
||||
}
|
||||
else {
|
||||
final HashMap<SpellAbilityView, SpellAbility> colorMatches = new HashMap<>();
|
||||
for (SpellAbility sa : abilitiesMap.values()) {
|
||||
if (abilityProducesManaColor(sa, sa.getManaPartRecursive(), colorNeeded)) {
|
||||
colorMatches.put(sa.getView(), sa);
|
||||
}
|
||||
}
|
||||
|
||||
if (colorMatches.isEmpty()) {
|
||||
// can only match colorless just grab the first and move on.
|
||||
// This is wrong. Sometimes all abilities aren't created equal
|
||||
choice = false;
|
||||
}
|
||||
else if (colorMatches.size() < abilitiesMap.size()) {
|
||||
// leave behind only color matches
|
||||
abilitiesMap = colorMatches;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exceptions for cards that have conditional abilities which are better handled manually
|
||||
if (card.getName().equals("Cavern of Souls") && isPayingGeneric) {
|
||||
choice = true;
|
||||
}
|
||||
|
||||
final SpellAbility chosen;
|
||||
if (chosenAbility == null) {
|
||||
HashMap<SpellAbilityView, SpellAbility> abilitiesMap = new HashMap<>();
|
||||
// you can't remove unneeded abilities inside a for (am:abilities) loop :(
|
||||
|
||||
final String typeRes = manaCost.getSourceRestriction();
|
||||
if (StringUtils.isNotBlank(typeRes) && !card.getType().hasStringType(typeRes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean guessAbilityWithRequiredColors = true;
|
||||
int amountOfMana = -1;
|
||||
for (SpellAbility ma : getAllManaAbilities(card)) {
|
||||
ma.setActivatingPlayer(player);
|
||||
|
||||
if (!ma.isManaAbilityFor(saPaidFor, colorCanUse)) { continue; }
|
||||
|
||||
// If Mana Abilities produce differing amounts of mana, let the player choose
|
||||
int maAmount = ma.totalAmountOfManaGenerated(saPaidFor, true);
|
||||
if (amountOfMana == -1) {
|
||||
amountOfMana = maAmount;
|
||||
} else {
|
||||
if (amountOfMana != maAmount) {
|
||||
guessAbilityWithRequiredColors = false;
|
||||
}
|
||||
}
|
||||
|
||||
abilitiesMap.put(ma.getView(), ma);
|
||||
|
||||
// skip express mana if the ability is not undoable or reusable
|
||||
if (!ma.isUndoable() || !ma.getPayCosts().isRenewableResource() || ma.getSubAbility() != null
|
||||
|| ma.isManaCannotCounter(saPaidFor)) {
|
||||
guessAbilityWithRequiredColors = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (abilitiesMap.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store some information about color costs to help with any mana choices
|
||||
if (colorNeeded == 0) { // only colorless left
|
||||
if (saPaidFor.getHostCard() != null && saPaidFor.getHostCard().hasSVar("ManaNeededToAvoidNegativeEffect")) {
|
||||
String[] negEffects = saPaidFor.getHostCard().getSVar("ManaNeededToAvoidNegativeEffect").split(",");
|
||||
for (String negColor : negEffects) {
|
||||
byte col = ManaAtom.fromName(negColor);
|
||||
colorCanUse |= col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the card has any ability that tracks mana spent, skip express Mana choice
|
||||
if (saPaidFor.tracksManaSpent()) {
|
||||
colorCanUse = ColorSet.ALL_COLORS.getColor();
|
||||
guessAbilityWithRequiredColors = false;
|
||||
}
|
||||
|
||||
boolean choice = true;
|
||||
if (guessAbilityWithRequiredColors) {
|
||||
// express Mana Choice
|
||||
if (colorNeeded == 0) {
|
||||
choice = false;
|
||||
//avoid unnecessary prompt by pretending we need White
|
||||
//for the sake of "Add one mana of any color" effects
|
||||
colorNeeded = MagicColor.WHITE;
|
||||
}
|
||||
else {
|
||||
final HashMap<SpellAbilityView, SpellAbility> colorMatches = new HashMap<>();
|
||||
for (SpellAbility sa : abilitiesMap.values()) {
|
||||
if (sa.isManaAbilityFor(saPaidFor, colorNeeded)) {
|
||||
colorMatches.put(sa.getView(), sa);
|
||||
}
|
||||
}
|
||||
|
||||
if (colorMatches.isEmpty()) {
|
||||
// can only match colorless just grab the first and move on.
|
||||
// This is wrong. Sometimes all abilities aren't created equal
|
||||
choice = false;
|
||||
}
|
||||
else if (colorMatches.size() < abilitiesMap.size()) {
|
||||
// leave behind only color matches
|
||||
abilitiesMap = colorMatches;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<SpellAbilityView> choices = new ArrayList<>(abilitiesMap.keySet());
|
||||
chosen = abilitiesMap.size() > 1 && choice ? abilitiesMap.get(getController().getGui().one(Localizer.getInstance().getMessage("lblChooseManaAbility"), choices)) : abilitiesMap.get(choices.get(0));
|
||||
} else {
|
||||
@@ -321,14 +316,13 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
// Filter the colors for the express choice so that only actually producible colors can be chosen
|
||||
int producedColorMask = 0;
|
||||
for (final byte color : ManaAtom.MANATYPES) {
|
||||
if (chosen.getManaPartRecursive().getOrigProduced().contains(MagicColor.toShortString(color))
|
||||
&& colors.hasAnyColor(color)) {
|
||||
if (chosen.canProduce(MagicColor.toShortString(color)) && colors.hasAnyColor(color)) {
|
||||
producedColorMask |= color;
|
||||
}
|
||||
}
|
||||
ColorSet producedAndNeededColors = ColorSet.fromMask(producedColorMask);
|
||||
|
||||
chosen.getManaPartRecursive().setExpressChoice(producedAndNeededColors);
|
||||
chosen.setManaExpressChoice(producedAndNeededColors);
|
||||
|
||||
// System.out.println("Chosen sa=" + chosen + " of " + chosen.getHostCard() + " to pay mana");
|
||||
|
||||
@@ -337,8 +331,19 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
@Override
|
||||
public void run() {
|
||||
if (HumanPlay.playSpellAbility(getController(), chosen.getActivatingPlayer(), chosen)) {
|
||||
player.getManaPool().payManaFromAbility(saPaidFor, InputPayMana.this.manaCost, chosen);
|
||||
final List<AbilityManaPart> manaAbilities = chosen.getAllManaParts();
|
||||
boolean restrictionsMet = true;
|
||||
|
||||
for (AbilityManaPart sa : manaAbilities) {
|
||||
if (!sa.meetsManaRestrictions(saPaidFor)) {
|
||||
restrictionsMet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (restrictionsMet) {
|
||||
player.getManaPool().payManaFromAbility(saPaidFor, InputPayMana.this.manaCost, chosen);
|
||||
}
|
||||
onManaAbilityPaid();
|
||||
onStateChanged();
|
||||
}
|
||||
@@ -348,58 +353,6 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean abilityProducesManaColor(final SpellAbility am, AbilityManaPart m, final byte neededColor) {
|
||||
if (0 != (neededColor & ManaAtom.GENERIC)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m.isAnyMana()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check for produce mana replacement effects - they mess this up, so just use the mana ability
|
||||
final Card source = am.getHostCard();
|
||||
final Player activator = am.getActivatingPlayer();
|
||||
final Game g = source.getGame();
|
||||
final Map<AbilityKey, Object> repParams = AbilityKey.newMap();
|
||||
repParams.put(AbilityKey.Mana, m.getOrigProduced());
|
||||
repParams.put(AbilityKey.Affected, source);
|
||||
repParams.put(AbilityKey.Player, activator);
|
||||
repParams.put(AbilityKey.AbilityMana, am);
|
||||
|
||||
for (final Player p : g.getPlayers()) {
|
||||
for (final Card crd : p.getAllCards()) {
|
||||
for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) {
|
||||
if (replacementEffect.requirementsCheck(g)
|
||||
&& replacementEffect.getMode() == ReplacementType.ProduceMana
|
||||
&& replacementEffect.canReplace(repParams)
|
||||
&& replacementEffect.hasParam("ManaReplacement")
|
||||
&& replacementEffect.zonesCheck(g.getZoneOf(crd))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (am.getApi() == ApiType.ManaReflected) {
|
||||
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am);
|
||||
for (final String color : reflectableColors) {
|
||||
if (0 != (neededColor & ManaAtom.fromName(color))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
String colorsProduced = m.isComboMana() ? m.getComboColors() : m.getOrigProduced();
|
||||
for (final String color : colorsProduced.split(" ")) {
|
||||
if (0 != (neededColor & ManaAtom.fromName(color))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isAlreadyPaid() {
|
||||
if (manaCost.isPaid()) {
|
||||
bPaid = 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) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.Observable;
|
||||
import java.util.concurrent.BlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameView;
|
||||
import forge.player.PlayerControllerHuman;
|
||||
|
||||
/**
|
||||
@@ -34,10 +34,10 @@ import forge.player.PlayerControllerHuman;
|
||||
*/
|
||||
public class InputQueue extends Observable {
|
||||
private final BlockingDeque<InputSynchronized> inputStack = new LinkedBlockingDeque<>();
|
||||
private final Game game;
|
||||
private final GameView gameView;
|
||||
|
||||
public InputQueue(final Game game, final InputProxy inputProxy) {
|
||||
this.game = game;
|
||||
public InputQueue(final GameView gameView, final InputProxy inputProxy) {
|
||||
this.gameView = gameView;
|
||||
addObserver(inputProxy);
|
||||
}
|
||||
|
||||
@@ -64,10 +64,10 @@ public class InputQueue extends Observable {
|
||||
|
||||
public final Input getActualInput(final PlayerControllerHuman controller) {
|
||||
final Input topMost = inputStack.peek(); // incoming input to Control
|
||||
if (topMost != null && !game.isGameOver()) {
|
||||
if (topMost != null && !gameView.isGameOver()) {
|
||||
return topMost;
|
||||
}
|
||||
return new InputLockUI(game, this, controller);
|
||||
return new InputLockUI(this, controller);
|
||||
} // getInput()
|
||||
|
||||
// only for debug purposes
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
package forge.match.input;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.player.PlayerControllerHuman;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.FThreads;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.collect.FCollectionView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSelectManyBase<T> {
|
||||
private static final long serialVersionUID = -6609493252672573139L;
|
||||
@@ -31,31 +30,32 @@ public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSele
|
||||
}
|
||||
|
||||
public InputSelectEntitiesFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView<T> validChoices0, final SpellAbility sa0) {
|
||||
super(controller, Math.min(min, validChoices0.size()), Math.min(max, validChoices0.size()),sa0);
|
||||
super(controller, Math.min(min, validChoices0.size()), Math.min(max, validChoices0.size()), sa0);
|
||||
validChoices = validChoices0;
|
||||
if (min > validChoices.size()) { // pfps does this really do anything useful??
|
||||
System.out.println(String.format("Trying to choose at least %d things from a list with only %d things!", min, validChoices.size()));
|
||||
}
|
||||
ArrayList<CardView> vCards = new ArrayList<>();
|
||||
for ( T c : validChoices0 ) {
|
||||
if ( c instanceof Card ) {
|
||||
vCards.add(((Card)c).getView()) ;
|
||||
}
|
||||
}
|
||||
getController().getGui().setSelectables(vCards);
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final GameEntity c : validChoices) {
|
||||
final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null ;
|
||||
if ( cz != null ) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(),cz.getZoneType()));
|
||||
}
|
||||
}
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
getController().getGui().updateZones(zonesToUpdate);
|
||||
zonesShown = getController().getGui().tempShowZones(controller.getPlayer().getView(),zonesToUpdate);
|
||||
ArrayList<CardView> vCards = new ArrayList<>();
|
||||
for (T c : validChoices0) {
|
||||
if (c instanceof Card) {
|
||||
vCards.add(((Card) c).getView());
|
||||
}
|
||||
});
|
||||
}
|
||||
getController().getGui().setSelectables(vCards);
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final GameEntity c : validChoices) {
|
||||
final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null;
|
||||
if (cz != null) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(), cz.getZoneType()));
|
||||
}
|
||||
}
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getController().getGui().updateZones(zonesToUpdate);
|
||||
zonesShown = getController().getGui().tempShowZones(controller.getPlayer().getView(), zonesToUpdate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,30 +1,35 @@
|
||||
package forge.match.input;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.FThreads;
|
||||
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;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.model.FModel;
|
||||
import forge.player.PlayerControllerHuman;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.TextUtil;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.FThreads;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
private final List<Card> choices;
|
||||
@@ -32,31 +37,36 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
private final Map<GameEntity, Integer> targetDepth = new HashMap<>();
|
||||
private final TargetRestrictions tgt;
|
||||
private final SpellAbility sa;
|
||||
private final Collection<Integer> divisionValues;
|
||||
private Card lastTarget = null;
|
||||
private boolean bCancel = false;
|
||||
private boolean bOk = false;
|
||||
private final boolean mandatory;
|
||||
private Predicate<GameObject> filter;
|
||||
private static final long serialVersionUID = -1091595663541356356L;
|
||||
|
||||
public final boolean hasCancelled() { return bCancel; }
|
||||
public final boolean hasPressedOk() { return bOk; }
|
||||
|
||||
public InputSelectTargets(final PlayerControllerHuman controller, final List<Card> choices, final SpellAbility sa, final boolean mandatory) {
|
||||
public InputSelectTargets(final PlayerControllerHuman controller, final List<Card> choices, final SpellAbility sa, final boolean mandatory, Collection<Integer> divisionValues, Predicate<GameObject> filter) {
|
||||
super(controller);
|
||||
this.choices = choices;
|
||||
this.tgt = sa.getTargetRestrictions();
|
||||
this.sa = sa;
|
||||
this.mandatory = mandatory;
|
||||
controller.getGui().setSelectables(CardView.getCollection(choices));
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final Card c : choices) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(c.getZone().getPlayer().getView(),c.getZone().getZoneType()));
|
||||
}
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
controller.getGui().updateZones(zonesToUpdate);
|
||||
this.divisionValues = divisionValues;
|
||||
this.filter = filter;
|
||||
controller.getGui().setSelectables(CardView.getCollection(choices));
|
||||
final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
for (final Card c : choices) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(c.getZone().getPlayer().getView(), c.getZone().getZoneType()));
|
||||
}
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
controller.getGui().updateZones(zonesToUpdate);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,8 +105,8 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
sb.append(sa.getUniqueTargets());
|
||||
}
|
||||
|
||||
final int maxTargets = tgt.getMaxTargets(sa.getHostCard(), sa);
|
||||
final int targeted = sa.getTargets().getNumTargeted();
|
||||
final int maxTargets = sa.getMaxTargets();
|
||||
final int targeted = sa.getTargets().size();
|
||||
if(maxTargets > 1) {
|
||||
sb.append(TextUtil.concatNoSpace("\n(", String.valueOf(maxTargets - targeted), " more can be targeted)"));
|
||||
}
|
||||
@@ -106,10 +116,10 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
"(Targeting ERROR)", "");
|
||||
showMessage(message, sa.getView());
|
||||
|
||||
if (tgt.isDividedAsYouChoose() && tgt.getMinTargets(sa.getHostCard(), sa) == 0 && sa.getTargets().getNumTargeted() == 0) {
|
||||
if (sa.isDividedAsYouChoose() && sa.getMinTargets() == 0 && sa.getTargets().size() == 0) {
|
||||
// extra logic for Divided with min targets = 0, should only work if num targets are 0 too
|
||||
getController().getGui().updateButtons(getOwner(), true, true, false);
|
||||
} else if (!tgt.isMinTargetsChosen(sa.getHostCard(), sa) || tgt.isDividedAsYouChoose()) {
|
||||
} else if (!sa.isMinTargetChosen() || sa.isDividedAsYouChoose()) {
|
||||
// If reached Minimum targets, enable OK button
|
||||
if (mandatory && tgt.hasCandidates(sa, true)) {
|
||||
// Player has to click on a target
|
||||
@@ -148,6 +158,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 +183,55 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the cards share a card type
|
||||
if (tgt.isWithSameCardType() && lastTarget != null && !card.sharesCardTypeWith(lastTarget)) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this card (should share a Card type)");
|
||||
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 (sa.hasParam("MaxTotalTargetPower")) {
|
||||
int maxTotalPower = tgt.getMaxTotalPower(sa.getHostCard(), sa);
|
||||
if (maxTotalPower > 0) {
|
||||
int soFar = Aggregates.sum(sa.getTargets().getTargetCards(), CardPredicates.Accessors.fnGetNetPower);
|
||||
if (!sa.isTargeting(card)) {
|
||||
soFar += card.getNetPower();
|
||||
}
|
||||
if (soFar > maxTotalPower) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this card (power limit exceeded)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If all cards must have different controllers
|
||||
if (tgt.isDifferentControllers()) {
|
||||
final List<Player> targetedControllers = new ArrayList<>();
|
||||
@@ -185,50 +247,31 @@ 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;
|
||||
}
|
||||
|
||||
if (tgt.isDividedAsYouChoose()) {
|
||||
final int stillToDivide = tgt.getStillToDivide();
|
||||
int allocatedPortion = 0;
|
||||
// allow allocation only if the max targets isn't reached and there are more candidates
|
||||
if ((sa.getTargets().getNumTargeted() + 1 < tgt.getMaxTargets(sa.getHostCard(), sa))
|
||||
&& (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) {
|
||||
final ImmutableList.Builder<Integer> choices = ImmutableList.builder();
|
||||
for (int i = 1; i <= stillToDivide; i++) {
|
||||
choices.add(Integer.valueOf(i));
|
||||
}
|
||||
String apiBasedMessage = "Distribute how much to ";
|
||||
if (sa.getApi() == ApiType.DealDamage) {
|
||||
apiBasedMessage = "Select how much damage to deal to ";
|
||||
}
|
||||
else if (sa.getApi() == ApiType.PreventDamage) {
|
||||
apiBasedMessage = "Select how much damage to prevent to ";
|
||||
}
|
||||
else if (sa.getApi() == ApiType.PutCounter) {
|
||||
apiBasedMessage = "Select how many counters to distribute to ";
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(apiBasedMessage);
|
||||
sb.append(card.toString());
|
||||
final Integer chosen = getController().getGui().oneOrNone(sb.toString(), choices.build());
|
||||
if (chosen == null) {
|
||||
return true; //still return true since there was a valid choice
|
||||
}
|
||||
allocatedPortion = chosen;
|
||||
if (sa.isDividedAsYouChoose()) {
|
||||
Boolean val = onDividedAsYouChoose(card);
|
||||
if (val != null) {
|
||||
return val;
|
||||
}
|
||||
else { // otherwise assign the rest of the damage/protection
|
||||
allocatedPortion = stillToDivide;
|
||||
}
|
||||
tgt.setStillToDivide(stillToDivide - allocatedPortion);
|
||||
tgt.addDividedAllocation(card, allocatedPortion);
|
||||
}
|
||||
addTarget(card);
|
||||
return true;
|
||||
@@ -251,16 +294,59 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.hasLost()) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this player - already lost.");
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO return the correct reason to display
|
||||
if (!sa.canTarget(player)) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this player (Hexproof? Protection? Restrictions?).");
|
||||
return;
|
||||
}
|
||||
if (filter != null && !filter.apply(player)) {
|
||||
showMessage(sa.getHostCard() + " - Cannot target this player (Hexproof? Protection? Restrictions?).");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tgt.isDividedAsYouChoose()) {
|
||||
final int stillToDivide = tgt.getStillToDivide();
|
||||
if (sa.isDividedAsYouChoose()) {
|
||||
Boolean val = onDividedAsYouChoose(player);
|
||||
if (val != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
addTarget(player);
|
||||
}
|
||||
|
||||
protected Boolean onDividedAsYouChoose(GameObject go) {
|
||||
if (divisionValues != null) {
|
||||
if (divisionValues.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String apiBasedMessage = "Distribute how much to ";
|
||||
if (sa.getApi() == ApiType.DealDamage) {
|
||||
apiBasedMessage = "Select how much damage to deal to ";
|
||||
}
|
||||
else if (sa.getApi() == ApiType.PreventDamage) {
|
||||
apiBasedMessage = "Select how much damage to prevent to ";
|
||||
}
|
||||
else if (sa.getApi() == ApiType.PutCounter) {
|
||||
apiBasedMessage = "Select how many counters to distribute to ";
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(apiBasedMessage);
|
||||
sb.append(go.toString());
|
||||
final Integer chosen = getController().getGui().oneOrNone(sb.toString(), Lists.newArrayList(divisionValues));
|
||||
if (chosen == null) {
|
||||
return true; //still return true since there was a valid choice
|
||||
}
|
||||
divisionValues.remove(chosen);
|
||||
sa.addDividedAllocation(go, chosen);
|
||||
} else {
|
||||
final int stillToDivide = sa.getStillToDivide();
|
||||
int allocatedPortion = 0;
|
||||
// allow allocation only if the max targets isn't reached and there are more candidates
|
||||
if ((sa.getTargets().getNumTargeted() + 1 < tgt.getMaxTargets(sa.getHostCard(), sa)) && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) {
|
||||
if ((sa.getTargets().size() + 1 < sa.getMaxTargets()) && (tgt.getNumCandidates(sa, true) - 1 > 0) && stillToDivide > 1) {
|
||||
final ImmutableList.Builder<Integer> choices = ImmutableList.builder();
|
||||
for (int i = 1; i <= stillToDivide; i++) {
|
||||
choices.add(Integer.valueOf(i));
|
||||
@@ -273,19 +359,18 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(apiBasedMessage);
|
||||
sb.append(player.getName());
|
||||
sb.append(go.toString());
|
||||
final Integer chosen = getController().getGui().oneOrNone(sb.toString(), choices.build());
|
||||
if (null == chosen) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
allocatedPortion = chosen;
|
||||
} else { // otherwise assign the rest of the damage/protection
|
||||
allocatedPortion = stillToDivide;
|
||||
}
|
||||
tgt.setStillToDivide(stillToDivide - allocatedPortion);
|
||||
tgt.addDividedAllocation(player, allocatedPortion);
|
||||
sa.addDividedAllocation(go, allocatedPortion);
|
||||
}
|
||||
addTarget(player);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addTarget(final GameEntity ge) {
|
||||
@@ -316,10 +401,9 @@ public final class InputSelectTargets extends InputSyncronizedBase {
|
||||
}
|
||||
|
||||
private boolean hasAllTargets() {
|
||||
return tgt.isMaxTargetsChosen(sa.getHostCard(), sa) || ( tgt.getStillToDivide() == 0 && tgt.isDividedAsYouChoose());
|
||||
return sa.isMaxTargetChosen() || (sa.isDividedAsYouChoose() && sa.getStillToDivide() == 0);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
getController().getGui().clearSelectables();
|
||||
|
||||
@@ -41,6 +41,7 @@ public class CardCollections {
|
||||
private IStorage<Deck> scheme;
|
||||
private IStorage<Deck> plane;
|
||||
private IStorage<Deck> commander;
|
||||
private IStorage<Deck> commanderPrecons;
|
||||
private IStorage<Deck> oathbreaker;
|
||||
private IStorage<Deck> tinyLeaders;
|
||||
private IStorage<Deck> brawl;
|
||||
@@ -121,6 +122,14 @@ public class CardCollections {
|
||||
return oathbreaker;
|
||||
}
|
||||
|
||||
public IStorage<Deck> getCommanderPrecons() {
|
||||
if (commanderPrecons == null) {
|
||||
commanderPrecons = new StorageImmediatelySerialized<Deck>("Commander Precon decks",
|
||||
new DeckStorage(new File(ForgeConstants.COMMANDER_PRECON_DIR), ForgeConstants.QUEST_PRECON_DIR));
|
||||
}
|
||||
return commanderPrecons;
|
||||
}
|
||||
|
||||
public IStorage<Deck> getTinyLeaders() {
|
||||
if (tinyLeaders == null) {
|
||||
tinyLeaders = new StorageImmediatelySerialized<>("Tiny Leaders decks",
|
||||
|
||||
@@ -28,6 +28,7 @@ import forge.card.CardType;
|
||||
import forge.deck.CardArchetypeLDAGenerator;
|
||||
import forge.deck.CardRelationMatrixGenerator;
|
||||
import forge.deck.io.DeckPreferences;
|
||||
import forge.download.AutoUpdater;
|
||||
import forge.game.GameFormat;
|
||||
import forge.game.GameType;
|
||||
import forge.game.card.CardUtil;
|
||||
@@ -36,6 +37,7 @@ import forge.gauntlet.GauntletData;
|
||||
import forge.interfaces.IProgressBar;
|
||||
import forge.itemmanager.ItemManagerConfig;
|
||||
import forge.limited.GauntletMini;
|
||||
import forge.limited.ThemedChaosDraft;
|
||||
import forge.planarconquest.ConquestController;
|
||||
import forge.planarconquest.ConquestPlane;
|
||||
import forge.planarconquest.ConquestPreferences;
|
||||
@@ -50,6 +52,7 @@ import forge.quest.data.QuestPreferences;
|
||||
import forge.tournament.TournamentData;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.Lang;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.storage.IStorage;
|
||||
import forge.util.storage.StorageBase;
|
||||
@@ -90,11 +93,20 @@ public final class FModel {
|
||||
|
||||
private static IStorage<CardBlock> blocks;
|
||||
private static IStorage<CardBlock> fantasyBlocks;
|
||||
private static IStorage<ThemedChaosDraft> themedChaosDrafts;
|
||||
private static IStorage<ConquestPlane> planes;
|
||||
private static IStorage<QuestWorld> worlds;
|
||||
private static GameFormat.Collection formats;
|
||||
|
||||
public static void initialize(final IProgressBar progressBar, Function<ForgePreferences, Void> adjustPrefs) {
|
||||
//init version to log
|
||||
System.out.println("Forge v." + GuiBase.getInterface().getCurrentVersion() + " (" + GuiBase.getInterface() + ")");
|
||||
//Device
|
||||
if (GuiBase.isAndroid()) //todo get device on other mobile platforms
|
||||
System.out.println(GuiBase.getDeviceName() + " (RAM: " + GuiBase.getDeviceRAM() + "MB, Android " + GuiBase.getAndroidRelease() + " API Level " + GuiBase.getAndroidAPILevel() + ")");
|
||||
else
|
||||
System.out.println(System.getProperty("os.name") + " (" + System.getProperty("os.version") + " " + System.getProperty("os.arch") + ")");
|
||||
|
||||
ImageKeys.initializeDirs(
|
||||
ForgeConstants.CACHE_CARD_PICS_DIR, ForgeConstants.CACHE_CARD_PICS_SUBDIR,
|
||||
ForgeConstants.CACHE_TOKEN_PICS_DIR, ForgeConstants.CACHE_ICON_PICS_DIR,
|
||||
@@ -115,9 +127,9 @@ public final class FModel {
|
||||
throw new RuntimeException(exn);
|
||||
}
|
||||
|
||||
Lang.createInstance(FModel.getPreferences().getPref(FPref.UI_LANGUAGE));
|
||||
Localizer.getInstance().initialize(FModel.getPreferences().getPref(FPref.UI_LANGUAGE), ForgeConstants.LANG_DIR);
|
||||
|
||||
//load card database
|
||||
final ProgressObserver progressBarBridge = (progressBar == null) ?
|
||||
ProgressObserver.emptyObserver : new ProgressObserver() {
|
||||
@Override
|
||||
@@ -143,11 +155,16 @@ public final class FModel {
|
||||
}
|
||||
};
|
||||
|
||||
if (new AutoUpdater(true).attemptToUpdate()) {
|
||||
//
|
||||
}
|
||||
|
||||
//load card database
|
||||
final CardStorageReader reader = new CardStorageReader(ForgeConstants.CARD_DATA_DIR, progressBarBridge,
|
||||
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
|
||||
final CardStorageReader tokenReader = new CardStorageReader(ForgeConstants.TOKEN_DATA_DIR, progressBarBridge,
|
||||
FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY));
|
||||
magicDb = new StaticData(reader, tokenReader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR);
|
||||
magicDb = new StaticData(reader, tokenReader, ForgeConstants.EDITIONS_DIR, ForgeConstants.BLOCK_DATA_DIR, FModel.getPreferences().getPrefBoolean(FPref.UI_LOAD_UNKNOWN_CARDS));
|
||||
CardTranslation.preloadTranslation(preferences.getPref(FPref.UI_LANGUAGE), ForgeConstants.LANG_DIR);
|
||||
|
||||
//create profile dirs if they don't already exist
|
||||
@@ -182,6 +199,7 @@ public final class FModel {
|
||||
questPreferences = new QuestPreferences();
|
||||
conquestPreferences = new ConquestPreferences();
|
||||
fantasyBlocks = new StorageBase<>("Custom blocks", new CardBlock.Reader(ForgeConstants.BLOCK_DATA_DIR + "fantasyblocks.txt", magicDb.getEditions()));
|
||||
themedChaosDrafts = new StorageBase<>("Themed Chaos Drafts", new ThemedChaosDraft.Reader(ForgeConstants.BLOCK_DATA_DIR + "chaosdraftthemes.txt"));
|
||||
planes = new StorageBase<>("Conquest planes", new ConquestPlane.Reader(ForgeConstants.CONQUEST_PLANES_DIR + "planes.txt"));
|
||||
Map<String, QuestWorld> standardWorlds = new QuestWorld.Reader(ForgeConstants.QUEST_WORLD_DIR + "worlds.txt").readAll();
|
||||
Map<String, QuestWorld> customWorlds = new QuestWorld.Reader(ForgeConstants.USER_QUEST_WORLD_DIR + "customworlds.txt").readAll();
|
||||
@@ -220,8 +238,6 @@ public final class FModel {
|
||||
achievements.put(GameType.Quest, new QuestAchievements());
|
||||
achievements.put(GameType.PlanarConquest, new PlanarConquestAchievements());
|
||||
achievements.put(GameType.Puzzle, new PuzzleAchievements());
|
||||
|
||||
|
||||
|
||||
//preload AI profiles
|
||||
AiProfileUtil.loadAllProfiles(ForgeConstants.AI_PROFILE_DIR);
|
||||
@@ -380,6 +396,10 @@ public final class FModel {
|
||||
return fantasyBlocks;
|
||||
}
|
||||
|
||||
public static IStorage<ThemedChaosDraft> getThemedChaosDrafts() {
|
||||
return themedChaosDrafts;
|
||||
}
|
||||
|
||||
public static TournamentData getTournamentData() { return tournamentData; }
|
||||
|
||||
public static void setTournamentData(TournamentData tournamentData) { FModel.tournamentData = tournamentData; }
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
package forge.net;
|
||||
|
||||
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;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import forge.GuiBase;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.deck.CardPool;
|
||||
@@ -22,10 +14,15 @@ import forge.game.spellability.SpellAbilityView;
|
||||
import forge.interfaces.IGameController;
|
||||
import forge.interfaces.IGuiGame;
|
||||
import forge.match.NextGameDecision;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.ReflectionUtil;
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The methods that can be sent through this protocol.
|
||||
@@ -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,15 +65,15 @@ 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),
|
||||
clearSelectables (Mode.SERVER),
|
||||
refreshField (Mode.SERVER),
|
||||
// TODO case "setPlayerAvatar":
|
||||
openZones (Mode.SERVER, Boolean.TYPE, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
|
||||
restoreOldZones (Mode.SERVER, Void.TYPE, Map/*PlayerView,Object*/.class),
|
||||
openZones (Mode.SERVER, PlayerZoneUpdates.class, PlayerView.class, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
|
||||
restoreOldZones (Mode.SERVER, Void.TYPE, PlayerView.class, PlayerZoneUpdates.class),
|
||||
isUiSetToSkipPhase (Mode.SERVER, Boolean.TYPE, PlayerView.class, PhaseType.class),
|
||||
setRememberedActions(Mode.SERVER, Void.TYPE),
|
||||
nextRememberedAction(Mode.SERVER, Void.TYPE),
|
||||
@@ -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,7 +1,7 @@
|
||||
package forge.net.client;
|
||||
|
||||
import forge.net.CustomObjectDecoder;
|
||||
import forge.net.CustomObjectEncoder;
|
||||
import forge.net.CompatibleObjectDecoder;
|
||||
import forge.net.CompatibleObjectEncoder;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
@@ -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 CustomObjectEncoder(),
|
||||
new CustomObjectDecoder(CustomObjectDecoder.maxObjectsize, 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,8 +6,8 @@ import forge.interfaces.IGuiGame;
|
||||
import forge.interfaces.ILobbyListener;
|
||||
import forge.match.LobbySlot;
|
||||
import forge.match.LobbySlotType;
|
||||
import forge.net.CustomObjectDecoder;
|
||||
import forge.net.CustomObjectEncoder;
|
||||
import forge.net.CompatibleObjectDecoder;
|
||||
import forge.net.CompatibleObjectEncoder;
|
||||
import forge.net.event.LobbyUpdateEvent;
|
||||
import forge.net.event.LoginEvent;
|
||||
import forge.net.event.LogoutEvent;
|
||||
@@ -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 CustomObjectEncoder(),
|
||||
new CustomObjectDecoder(CustomObjectDecoder.maxObjectsize, 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();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user