mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Initial checkin for Puzzle Mode
This commit is contained in:
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -993,6 +993,11 @@ forge-gui-desktop/src/main/java/forge/screens/home/online/CSubmenuOnlineLobby.ja
|
|||||||
forge-gui-desktop/src/main/java/forge/screens/home/online/OnlineMenu.java -text
|
forge-gui-desktop/src/main/java/forge/screens/home/online/OnlineMenu.java -text
|
||||||
forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java -text
|
forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java -text
|
||||||
forge-gui-desktop/src/main/java/forge/screens/home/package-info.java -text
|
forge-gui-desktop/src/main/java/forge/screens/home/package-info.java -text
|
||||||
|
forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleCreate.java -text
|
||||||
|
forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleSolve.java -text
|
||||||
|
forge-gui-desktop/src/main/java/forge/screens/home/puzzle/PuzzleGameMenu.java -text
|
||||||
|
forge-gui-desktop/src/main/java/forge/screens/home/puzzle/VSubmenuPuzzleCreate.java -text
|
||||||
|
forge-gui-desktop/src/main/java/forge/screens/home/puzzle/VSubmenuPuzzleSolve.java -text
|
||||||
forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuChallenges.java -text
|
forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuChallenges.java -text
|
||||||
forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuDuels.java -text
|
forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuDuels.java -text
|
||||||
forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java -text
|
forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuQuestData.java -text
|
||||||
@@ -1347,6 +1352,7 @@ forge-gui-mobile/src/forge/screens/gauntlet/NewGauntletScreen.java -text
|
|||||||
forge-gui-mobile/src/forge/screens/home/HomeScreen.java -text
|
forge-gui-mobile/src/forge/screens/home/HomeScreen.java -text
|
||||||
forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java -text
|
forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java -text
|
||||||
forge-gui-mobile/src/forge/screens/home/NewGameMenu.java -text
|
forge-gui-mobile/src/forge/screens/home/NewGameMenu.java -text
|
||||||
|
forge-gui-mobile/src/forge/screens/home/puzzle/PuzzleScreen.java -text
|
||||||
forge-gui-mobile/src/forge/screens/limited/DraftingProcessScreen.java -text
|
forge-gui-mobile/src/forge/screens/limited/DraftingProcessScreen.java -text
|
||||||
forge-gui-mobile/src/forge/screens/limited/LoadDraftScreen.java -text
|
forge-gui-mobile/src/forge/screens/limited/LoadDraftScreen.java -text
|
||||||
forge-gui-mobile/src/forge/screens/limited/LoadSealedScreen.java -text
|
forge-gui-mobile/src/forge/screens/limited/LoadSealedScreen.java -text
|
||||||
@@ -18806,6 +18812,7 @@ forge-gui/res/music/menus/Evil[!!-~]March.mp3 -text
|
|||||||
forge-gui/res/music/menus/Heroic[!!-~]Age.mp3 -text
|
forge-gui/res/music/menus/Heroic[!!-~]Age.mp3 -text
|
||||||
forge-gui/res/music/menus/Lord[!!-~]of[!!-~]the[!!-~]Land.mp3 -text
|
forge-gui/res/music/menus/Lord[!!-~]of[!!-~]the[!!-~]Land.mp3 -text
|
||||||
forge-gui/res/music/menus/The[!!-~]Pyre.mp3 -text
|
forge-gui/res/music/menus/The[!!-~]Pyre.mp3 -text
|
||||||
|
forge-gui/res/puzzle/PS1.pzl -text
|
||||||
forge-gui/res/quest/bazaar/ape_pet_l1.txt -text
|
forge-gui/res/quest/bazaar/ape_pet_l1.txt -text
|
||||||
forge-gui/res/quest/bazaar/ape_pet_l2.txt -text
|
forge-gui/res/quest/bazaar/ape_pet_l2.txt -text
|
||||||
forge-gui/res/quest/bazaar/ape_pet_l3.txt -text
|
forge-gui/res/quest/bazaar/ape_pet_l3.txt -text
|
||||||
@@ -20844,6 +20851,8 @@ forge-gui/src/main/java/forge/player/package-info.java -text
|
|||||||
forge-gui/src/main/java/forge/properties/ForgeConstants.java -text
|
forge-gui/src/main/java/forge/properties/ForgeConstants.java -text
|
||||||
forge-gui/src/main/java/forge/properties/ForgePreferences.java svneol=native#text/plain
|
forge-gui/src/main/java/forge/properties/ForgePreferences.java svneol=native#text/plain
|
||||||
forge-gui/src/main/java/forge/properties/package-info.java svneol=native#text/plain
|
forge-gui/src/main/java/forge/properties/package-info.java svneol=native#text/plain
|
||||||
|
forge-gui/src/main/java/forge/puzzle/Puzzle.java -text
|
||||||
|
forge-gui/src/main/java/forge/puzzle/PuzzleIO.java -text
|
||||||
forge-gui/src/main/java/forge/quest/BoosterUtils.java svneol=native#text/plain
|
forge-gui/src/main/java/forge/quest/BoosterUtils.java svneol=native#text/plain
|
||||||
forge-gui/src/main/java/forge/quest/IQuestEvent.java -text
|
forge-gui/src/main/java/forge/quest/IQuestEvent.java -text
|
||||||
forge-gui/src/main/java/forge/quest/IQuestRewardCard.java -text
|
forge-gui/src/main/java/forge/quest/IQuestRewardCard.java -text
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.google.common.collect.Lists;
|
|||||||
|
|
||||||
import forge.card.CardStateName;
|
import forge.card.CardStateName;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.effects.DetachedCardEffect;
|
import forge.game.ability.effects.DetachedCardEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
@@ -31,7 +32,7 @@ import forge.util.collect.FCollectionView;
|
|||||||
public abstract class GameState {
|
public abstract class GameState {
|
||||||
private static final Map<ZoneType, String> ZONES = new HashMap<ZoneType, String>();
|
private static final Map<ZoneType, String> ZONES = new HashMap<ZoneType, String>();
|
||||||
static {
|
static {
|
||||||
ZONES.put(ZoneType.Battlefield, "play");
|
ZONES.put(ZoneType.Battlefield, "battlefield");
|
||||||
ZONES.put(ZoneType.Hand, "hand");
|
ZONES.put(ZoneType.Hand, "hand");
|
||||||
ZONES.put(ZoneType.Graveyard, "graveyard");
|
ZONES.put(ZoneType.Graveyard, "graveyard");
|
||||||
ZONES.put(ZoneType.Library, "library");
|
ZONES.put(ZoneType.Library, "library");
|
||||||
@@ -41,8 +42,15 @@ public abstract class GameState {
|
|||||||
|
|
||||||
private int humanLife = -1;
|
private int humanLife = -1;
|
||||||
private int computerLife = -1;
|
private int computerLife = -1;
|
||||||
|
private String humanCounters = "";
|
||||||
|
private String computerCounters = "";
|
||||||
|
|
||||||
private final Map<ZoneType, String> humanCardTexts = new EnumMap<ZoneType, String>(ZoneType.class);
|
private final Map<ZoneType, String> humanCardTexts = new EnumMap<ZoneType, String>(ZoneType.class);
|
||||||
private final Map<ZoneType, String> aiCardTexts = new EnumMap<ZoneType, String>(ZoneType.class);
|
private final Map<ZoneType, String> aiCardTexts = new EnumMap<ZoneType, String>(ZoneType.class);
|
||||||
|
|
||||||
|
private final Map<Integer, Card> idToCard = new HashMap<>();
|
||||||
|
private final Map<Card, Integer> cardToAttachId = new HashMap<>();
|
||||||
|
|
||||||
private String tChangePlayer = "NONE";
|
private String tChangePlayer = "NONE";
|
||||||
private String tChangePhase = "NONE";
|
private String tChangePhase = "NONE";
|
||||||
|
|
||||||
@@ -56,6 +64,14 @@ public abstract class GameState {
|
|||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(String.format("humanlife=%d\n", humanLife));
|
sb.append(String.format("humanlife=%d\n", humanLife));
|
||||||
sb.append(String.format("ailife=%d\n", computerLife));
|
sb.append(String.format("ailife=%d\n", computerLife));
|
||||||
|
|
||||||
|
if (!humanCounters.isEmpty()) {
|
||||||
|
sb.append(String.format("humancounters=%s\n", humanCounters));
|
||||||
|
}
|
||||||
|
if (!computerCounters.isEmpty()) {
|
||||||
|
sb.append(String.format("aicounters=%s\n", computerCounters));
|
||||||
|
}
|
||||||
|
|
||||||
sb.append(String.format("activeplayer=%s\n", tChangePlayer));
|
sb.append(String.format("activeplayer=%s\n", tChangePlayer));
|
||||||
sb.append(String.format("activephase=%s\n", tChangePhase));
|
sb.append(String.format("activephase=%s\n", tChangePhase));
|
||||||
appendCards(humanCardTexts, "human", sb);
|
appendCards(humanCardTexts, "human", sb);
|
||||||
@@ -65,7 +81,7 @@ public abstract class GameState {
|
|||||||
|
|
||||||
private void appendCards(Map<ZoneType, String> cardTexts, String categoryPrefix, StringBuilder sb) {
|
private void appendCards(Map<ZoneType, String> cardTexts, String categoryPrefix, StringBuilder sb) {
|
||||||
for (Entry<ZoneType, String> kv : cardTexts.entrySet()) {
|
for (Entry<ZoneType, String> kv : cardTexts.entrySet()) {
|
||||||
sb.append(String.format("%scardsin%s=%s\n", categoryPrefix, ZONES.get(kv.getKey()), kv.getValue()));
|
sb.append(String.format("%s%s=%s\n", categoryPrefix, ZONES.get(kv.getKey()), kv.getValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +98,9 @@ public abstract class GameState {
|
|||||||
}
|
}
|
||||||
humanLife = human.getLife();
|
humanLife = human.getLife();
|
||||||
computerLife = ai.getLife();
|
computerLife = ai.getLife();
|
||||||
|
humanCounters = countersToString(human.getCounters());
|
||||||
|
computerCounters = countersToString(ai.getCounters());
|
||||||
|
|
||||||
tChangePlayer = game.getPhaseHandler().getPlayerTurn() == ai ? "ai" : "human";
|
tChangePlayer = game.getPhaseHandler().getPlayerTurn() == ai ? "ai" : "human";
|
||||||
tChangePhase = game.getPhaseHandler().getPhase().toString();
|
tChangePhase = game.getPhaseHandler().getPhase().toString();
|
||||||
aiCardTexts.clear();
|
aiCardTexts.clear();
|
||||||
@@ -111,43 +130,61 @@ public abstract class GameState {
|
|||||||
newText.append(c.getPaperCard().getName());
|
newText.append(c.getPaperCard().getName());
|
||||||
}
|
}
|
||||||
if (c.isCommander()) {
|
if (c.isCommander()) {
|
||||||
newText.append("|IsCommander:True");
|
newText.append("|IsCommander");
|
||||||
}
|
}
|
||||||
if (zoneType == ZoneType.Battlefield) {
|
if (zoneType == ZoneType.Battlefield) {
|
||||||
if (c.isTapped()) {
|
if (c.isTapped()) {
|
||||||
newText.append("|Tapped:True");
|
newText.append("|Tapped");
|
||||||
}
|
}
|
||||||
if (c.isSick()) {
|
if (c.isSick()) {
|
||||||
newText.append("|SummonSick:True");
|
newText.append("|SummonSick");
|
||||||
}
|
}
|
||||||
if (c.isFaceDown()) {
|
if (c.isFaceDown()) {
|
||||||
newText.append("|FaceDown:True");
|
newText.append("|FaceDown");
|
||||||
|
if (c.isManifested()) {
|
||||||
|
newText.append(":Manifested");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Map<CounterType, Integer> counters = c.getCounters();
|
Map<CounterType, Integer> counters = c.getCounters();
|
||||||
if (!counters.isEmpty()) {
|
if (!counters.isEmpty()) {
|
||||||
newText.append("|Counters:");
|
newText.append("|Counters:");
|
||||||
boolean start = true;
|
newText.append(countersToString(counters));
|
||||||
for(Entry<CounterType, Integer> kv : counters.entrySet()) {
|
|
||||||
String str = kv.getKey().toString();
|
|
||||||
int count = kv.getValue();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
if (!start) {
|
|
||||||
newText.append(",");
|
|
||||||
}
|
|
||||||
newText.append(str);
|
|
||||||
start = false;
|
|
||||||
}
|
}
|
||||||
|
if (c.getEquipping() != null) {
|
||||||
|
newText.append("|Attaching:").append(c.getEquipping().getId());
|
||||||
|
} else if (c.getFortifying() != null) {
|
||||||
|
newText.append("|Attaching:").append(c.getFortifying().getId());
|
||||||
|
} else if (c.getEnchantingCard() != null) {
|
||||||
|
newText.append("|Attaching:").append(c.getEnchantingCard().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!c.getEnchantedBy(false).isEmpty() || !c.getEquippedBy(false).isEmpty() || !c.getFortifiedBy(false).isEmpty()) {
|
||||||
|
newText.append("|Id:").append(c.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cardTexts.put(zoneType, newText.toString());
|
cardTexts.put(zoneType, newText.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] parseLine(String line) {
|
private String countersToString(Map<CounterType, Integer> counters) {
|
||||||
|
boolean first = true;
|
||||||
|
StringBuilder counterString = new StringBuilder();
|
||||||
|
|
||||||
|
for(Entry<CounterType, Integer> kv : counters.entrySet()) {
|
||||||
|
if (!first) {
|
||||||
|
counterString.append(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
first = false;
|
||||||
|
counterString.append(String.format("%s=%d", kv.getKey().toString(), kv.getValue()));
|
||||||
|
}
|
||||||
|
return counterString.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] splitLine(String line) {
|
||||||
if (line.charAt(0) == '#') {
|
if (line.charAt(0) == '#') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final String[] tempData = line.split("=");
|
final String[] tempData = line.split("=", 2);
|
||||||
if (tempData.length >= 2) {
|
if (tempData.length >= 2) {
|
||||||
return tempData;
|
return tempData;
|
||||||
}
|
}
|
||||||
@@ -163,32 +200,91 @@ public abstract class GameState {
|
|||||||
|
|
||||||
String line;
|
String line;
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
String[] keyValue = parseLine(line);
|
parseLine(line);
|
||||||
if (keyValue == null) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parse(List<String> lines) {
|
||||||
|
for(String line : lines) {
|
||||||
|
parseLine(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void parseLine(String line) {
|
||||||
|
String[] keyValue = splitLine(line);
|
||||||
|
if (keyValue == null) return;
|
||||||
|
|
||||||
final String categoryName = keyValue[0].toLowerCase();
|
final String categoryName = keyValue[0].toLowerCase();
|
||||||
final String categoryValue = keyValue[1];
|
final String categoryValue = keyValue[1];
|
||||||
|
|
||||||
if (categoryName.equals("humanlife")) humanLife = Integer.parseInt(categoryValue);
|
if (categoryName.startsWith("active")) {
|
||||||
else if (categoryName.equals("ailife")) computerLife = Integer.parseInt(categoryValue);
|
if (categoryName.endsWith("player"))
|
||||||
|
tChangePlayer = categoryValue.trim().toLowerCase();
|
||||||
|
if (categoryName.endsWith("phase"))
|
||||||
|
tChangePhase = categoryValue.trim().toUpperCase();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
else if (categoryName.equals("activeplayer")) tChangePlayer = categoryValue.trim().toLowerCase();
|
boolean isHuman = categoryName.startsWith("human");
|
||||||
else if (categoryName.equals("activephase")) tChangePhase = categoryValue;
|
|
||||||
|
|
||||||
else if (categoryName.equals("humancardsinplay")) humanCardTexts.put(ZoneType.Battlefield, categoryValue);
|
if (categoryName.endsWith("life")) {
|
||||||
else if (categoryName.equals("aicardsinplay")) aiCardTexts.put(ZoneType.Battlefield, categoryValue);
|
if (isHuman)
|
||||||
else if (categoryName.equals("humancardsinhand")) humanCardTexts.put(ZoneType.Hand, categoryValue);
|
humanLife = Integer.parseInt(categoryValue);
|
||||||
else if (categoryName.equals("aicardsinhand")) aiCardTexts.put(ZoneType.Hand, categoryValue);
|
else
|
||||||
else if (categoryName.equals("humancardsingraveyard")) humanCardTexts.put(ZoneType.Graveyard, categoryValue);
|
computerLife = Integer.parseInt(categoryValue);
|
||||||
else if (categoryName.equals("aicardsingraveyard")) aiCardTexts.put(ZoneType.Graveyard, categoryValue);
|
}
|
||||||
else if (categoryName.equals("humancardsinlibrary")) humanCardTexts.put(ZoneType.Library, categoryValue);
|
|
||||||
else if (categoryName.equals("aicardsinlibrary")) aiCardTexts.put(ZoneType.Library, categoryValue);
|
else if (categoryName.endsWith("counters")) {
|
||||||
else if (categoryName.equals("humancardsinexile")) humanCardTexts.put(ZoneType.Exile, categoryValue);
|
if (isHuman)
|
||||||
else if (categoryName.equals("aicardsinexile")) aiCardTexts.put(ZoneType.Exile, categoryValue);
|
humanCounters = categoryValue;
|
||||||
else if (categoryName.equals("humancardsincommand")) humanCardTexts.put(ZoneType.Command, categoryValue);
|
else
|
||||||
else if (categoryName.equals("aicardsincommand")) aiCardTexts.put(ZoneType.Command, categoryValue);
|
computerCounters = categoryValue;
|
||||||
else System.out.println("Unknown key: " + categoryName);
|
}
|
||||||
|
|
||||||
|
else if (categoryName.endsWith("play") || categoryName.endsWith("battlefield")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanCardTexts.put(ZoneType.Battlefield, categoryValue);
|
||||||
|
else
|
||||||
|
aiCardTexts.put(ZoneType.Battlefield, categoryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (categoryName.endsWith("hand")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanCardTexts.put(ZoneType.Hand, categoryValue);
|
||||||
|
else
|
||||||
|
aiCardTexts.put(ZoneType.Hand, categoryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (categoryName.endsWith("graveyard")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanCardTexts.put(ZoneType.Graveyard, categoryValue);
|
||||||
|
else
|
||||||
|
aiCardTexts.put(ZoneType.Graveyard, categoryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (categoryName.equals("library")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanCardTexts.put(ZoneType.Library, categoryValue);
|
||||||
|
else
|
||||||
|
aiCardTexts.put(ZoneType.Library, categoryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (categoryName.equals("exile")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanCardTexts.put(ZoneType.Exile, categoryValue);
|
||||||
|
else
|
||||||
|
aiCardTexts.put(ZoneType.Exile, categoryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (categoryName.equals("command")) {
|
||||||
|
if (isHuman)
|
||||||
|
humanCardTexts.put(ZoneType.Command, categoryValue);
|
||||||
|
else
|
||||||
|
aiCardTexts.put(ZoneType.Command, categoryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
System.out.println("Unknown key: " + categoryName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,12 +292,27 @@ public abstract class GameState {
|
|||||||
game.getAction().invoke(new Runnable() {
|
game.getAction().invoke(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
applyGameOnThread(game);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void applyGameOnThread(final Game game) {
|
||||||
final Player human = game.getPlayers().get(0);
|
final Player human = game.getPlayers().get(0);
|
||||||
final Player ai = game.getPlayers().get(1);
|
final Player ai = game.getPlayers().get(1);
|
||||||
|
|
||||||
Player newPlayerTurn = tChangePlayer.equals("human") ? newPlayerTurn = human : tChangePlayer.equals("ai") ? newPlayerTurn = ai : null;
|
Player newPlayerTurn = tChangePlayer.equals("human") ? human : tChangePlayer.equals("ai") ? ai : null;
|
||||||
PhaseType newPhase = tChangePhase.trim().equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tChangePhase);
|
PhaseType newPhase = tChangePhase.equals("none") ? null : PhaseType.smartValueOf(tChangePhase);
|
||||||
|
|
||||||
|
// Set stack to resolving so things won't trigger/effects be checked right away
|
||||||
|
game.getStack().setResolving(true);
|
||||||
|
|
||||||
|
if (!humanCounters.isEmpty()) {
|
||||||
|
applyCountersToGameEntity(human, humanCounters);
|
||||||
|
}
|
||||||
|
if (!computerCounters.isEmpty()) {
|
||||||
|
applyCountersToGameEntity(ai, computerCounters);
|
||||||
|
}
|
||||||
game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn);
|
game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn);
|
||||||
|
|
||||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||||
@@ -211,12 +322,24 @@ public abstract class GameState {
|
|||||||
|
|
||||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||||
|
|
||||||
|
game.getStack().setResolving(false);
|
||||||
|
|
||||||
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
|
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
private void applyCountersToGameEntity(GameEntity entity, String counterString) {
|
||||||
|
entity.setCounters(new HashMap<CounterType, Integer>());
|
||||||
|
String[] allCounterStrings = counterString.split(",");
|
||||||
|
for (final String counterPair : allCounterStrings) {
|
||||||
|
String[] pair = counterPair.split("=", 2);
|
||||||
|
|
||||||
|
entity.addCounter(CounterType.valueOf(pair[0]), Integer.parseInt(pair[1]), false, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPlayerState(int life, Map<ZoneType, String> cardTexts, final Player p) {
|
private void setupPlayerState(int life, Map<ZoneType, String> cardTexts, final Player p) {
|
||||||
|
// Lock check static as we setup player state
|
||||||
|
|
||||||
Map<ZoneType, CardCollectionView> playerCards = new EnumMap<ZoneType, CardCollectionView>(ZoneType.class);
|
Map<ZoneType, CardCollectionView> playerCards = new EnumMap<ZoneType, CardCollectionView>(ZoneType.class);
|
||||||
for (Entry<ZoneType, String> kv : cardTexts.entrySet()) {
|
for (Entry<ZoneType, String> kv : cardTexts.entrySet()) {
|
||||||
String value = kv.getValue();
|
String value = kv.getValue();
|
||||||
@@ -245,7 +368,12 @@ public abstract class GameState {
|
|||||||
// var as-is.
|
// var as-is.
|
||||||
c.setCounters(new HashMap<CounterType, Integer>());
|
c.setCounters(new HashMap<CounterType, Integer>());
|
||||||
p.getZone(ZoneType.Hand).add(c);
|
p.getZone(ZoneType.Hand).add(c);
|
||||||
|
if (c.isAura()) {
|
||||||
p.getGame().getAction().moveToPlay(c);
|
p.getGame().getAction().moveToPlay(c);
|
||||||
|
} else {
|
||||||
|
p.getGame().getAction().moveToPlay(c);
|
||||||
|
}
|
||||||
|
|
||||||
c.setTapped(tapped);
|
c.setTapped(tapped);
|
||||||
c.setSickness(sickness);
|
c.setSickness(sickness);
|
||||||
c.setCounters(counters);
|
c.setCounters(counters);
|
||||||
@@ -254,6 +382,19 @@ public abstract class GameState {
|
|||||||
zone.setCards(kv.getValue());
|
zone.setCards(kv.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(Entry<Card, Integer> entry : cardToAttachId.entrySet()) {
|
||||||
|
Card attachedTo = idToCard.get(entry.getValue());
|
||||||
|
Card attacher = entry.getKey();
|
||||||
|
|
||||||
|
if (attacher.isEquipment()) {
|
||||||
|
attacher.equipCard(attachedTo);
|
||||||
|
} else if (attacher.isAura()) {
|
||||||
|
attacher.enchantEntity(attachedTo);
|
||||||
|
} else if (attacher.isFortified()) {
|
||||||
|
attacher.fortifyCard(attachedTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,7 +406,7 @@ public abstract class GameState {
|
|||||||
* an array of {@link java.lang.String} objects.
|
* an array of {@link java.lang.String} objects.
|
||||||
* @param player
|
* @param player
|
||||||
* a {@link forge.game.player.Player} object.
|
* a {@link forge.game.player.Player} object.
|
||||||
* @return a {@link forge.CardList} object.
|
* @return a {@link CardCollectionView} object.
|
||||||
*/
|
*/
|
||||||
private CardCollectionView processCardsForZone(final String[] data, final Player player) {
|
private CardCollectionView processCardsForZone(final String[] data, final Player player) {
|
||||||
final CardCollection cl = new CardCollection();
|
final CardCollection cl = new CardCollection();
|
||||||
@@ -286,22 +427,29 @@ public abstract class GameState {
|
|||||||
if (info.startsWith("Set:")) {
|
if (info.startsWith("Set:")) {
|
||||||
c.setSetCode(info.substring(info.indexOf(':') + 1));
|
c.setSetCode(info.substring(info.indexOf(':') + 1));
|
||||||
hasSetCurSet = true;
|
hasSetCurSet = true;
|
||||||
} else if (info.equalsIgnoreCase("Tapped:True")) {
|
}
|
||||||
|
else if (info.startsWith("Tapped")) {
|
||||||
c.tap();
|
c.tap();
|
||||||
} else if (info.startsWith("Counters:")) {
|
} else if (info.startsWith("Counters:")) {
|
||||||
final String[] counterStrings = info.substring(info.indexOf(':') + 1).split(",");
|
applyCountersToGameEntity(c, info.substring(info.indexOf(':') + 1));
|
||||||
for (final String counter : counterStrings) {
|
} else if (info.startsWith("SummonSick")) {
|
||||||
c.addCounter(CounterType.valueOf(counter), 1, true);
|
|
||||||
}
|
|
||||||
} else if (info.equalsIgnoreCase("SummonSick:True")) {
|
|
||||||
c.setSickness(true);
|
c.setSickness(true);
|
||||||
} else if (info.equalsIgnoreCase("FaceDown:True")) {
|
} else if (info.startsWith("FaceDown")) {
|
||||||
c.setState(CardStateName.FaceDown, true);
|
c.setState(CardStateName.FaceDown, true);
|
||||||
} else if (info.equalsIgnoreCase("IsCommander:True")) {
|
if (info.endsWith("Manifested")) {
|
||||||
|
c.setManifested(true);
|
||||||
|
}
|
||||||
|
} else if (info.startsWith("IsCommander")) {
|
||||||
// TODO: This doesn't seem to properly restore the ability to play the commander. Why?
|
// TODO: This doesn't seem to properly restore the ability to play the commander. Why?
|
||||||
c.setCommander(true);
|
c.setCommander(true);
|
||||||
player.setCommanders(Lists.newArrayList(c));
|
player.setCommanders(Lists.newArrayList(c));
|
||||||
player.getZone(ZoneType.Command).add(Player.createCommanderEffect(player.getGame(), c));
|
player.getZone(ZoneType.Command).add(Player.createCommanderEffect(player.getGame(), c));
|
||||||
|
} else if (info.startsWith("Id:")) {
|
||||||
|
int id = Integer.parseInt(info.substring(4));
|
||||||
|
idToCard.put(id, c);
|
||||||
|
} else if (info.startsWith("Attaching:")) {
|
||||||
|
int id = Integer.parseInt(info.substring(info.indexOf(':') + 1));
|
||||||
|
cardToAttachId.put(c, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,13 +94,15 @@ public enum DeckFormat {
|
|||||||
PlanarConquest ( Range.between(40, Integer.MAX_VALUE), Range.is(0), 1),
|
PlanarConquest ( Range.between(40, Integer.MAX_VALUE), Range.is(0), 1),
|
||||||
Vanguard ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
Vanguard ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
||||||
Planechase ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
Planechase ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
||||||
Archenemy ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4);
|
Archenemy ( Range.between(60, Integer.MAX_VALUE), Range.is(0), 4),
|
||||||
|
Puzzle ( Range.between(0, Integer.MAX_VALUE), Range.is(0), 4);
|
||||||
|
|
||||||
private final Range<Integer> mainRange;
|
private final Range<Integer> mainRange;
|
||||||
private final Range<Integer> sideRange; // null => no check
|
private final Range<Integer> sideRange; // null => no check
|
||||||
private final int maxCardCopies;
|
private final int maxCardCopies;
|
||||||
private final Predicate<CardRules> cardPoolFilter;
|
private final Predicate<CardRules> cardPoolFilter;
|
||||||
private final static String ADVPROCLAMATION = "Advantageous Proclamation";
|
private final static String ADVPROCLAMATION = "Advantageous Proclamation";
|
||||||
|
private final static String SOVREALM = "Sovereign's Realm";
|
||||||
|
|
||||||
private DeckFormat(Range<Integer> mainRange0, Range<Integer> sideRange0, int maxCardCopies0) {
|
private DeckFormat(Range<Integer> mainRange0, Range<Integer> sideRange0, int maxCardCopies0) {
|
||||||
this(mainRange0, sideRange0, maxCardCopies0, null);
|
this(mainRange0, sideRange0, maxCardCopies0, null);
|
||||||
@@ -168,11 +170,13 @@ public enum DeckFormat {
|
|||||||
|
|
||||||
int min = getMainRange().getMinimum();
|
int min = getMainRange().getMinimum();
|
||||||
int max = getMainRange().getMaximum();
|
int max = getMainRange().getMaximum();
|
||||||
|
boolean noBasicLands = false;
|
||||||
|
|
||||||
// Adjust minimum base on number of Advantageous Proclamation or similar cards
|
// Adjust minimum base on number of Advantageous Proclamation or similar cards
|
||||||
CardPool conspiracies = deck.get(DeckSection.Conspiracy);
|
CardPool conspiracies = deck.get(DeckSection.Conspiracy);
|
||||||
if (conspiracies != null) {
|
if (conspiracies != null) {
|
||||||
min -= (5 * conspiracies.countByName(ADVPROCLAMATION, true));
|
min -= (5 * conspiracies.countByName(ADVPROCLAMATION, false));
|
||||||
|
noBasicLands = conspiracies.countByName(SOVREALM, false) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasCommander()) { // 1 Commander, or 2 Partner Commanders
|
if (hasCommander()) { // 1 Commander, or 2 Partner Commanders
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package forge.game;
|
package forge.game;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
@@ -1434,6 +1435,10 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startGame(GameOutcome lastGameOutcome) {
|
public void startGame(GameOutcome lastGameOutcome) {
|
||||||
|
startGame(lastGameOutcome, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startGame(GameOutcome lastGameOutcome, Runnable startGameHook) {
|
||||||
Player first = determineFirstTurnPlayer(lastGameOutcome);
|
Player first = determineFirstTurnPlayer(lastGameOutcome);
|
||||||
|
|
||||||
GameType gameType = game.getRules().getGameType();
|
GameType gameType = game.getRules().getGameType();
|
||||||
@@ -1444,6 +1449,9 @@ public class GameAction {
|
|||||||
// Where there are none, it should bring up speed controls
|
// Where there are none, it should bring up speed controls
|
||||||
game.fireEvent(new GameEventGameStarted(gameType, first, game.getPlayers()));
|
game.fireEvent(new GameEventGameStarted(gameType, first, game.getPlayers()));
|
||||||
|
|
||||||
|
// Emissary's Plot
|
||||||
|
// runPreOpeningHandActions(first);
|
||||||
|
|
||||||
game.setAge(GameStage.Mulligan);
|
game.setAge(GameStage.Mulligan);
|
||||||
for (final Player p1 : game.getPlayers()) {
|
for (final Player p1 : game.getPlayers()) {
|
||||||
p1.drawCards(p1.getMaxHandSize());
|
p1.drawCards(p1.getMaxHandSize());
|
||||||
@@ -1453,8 +1461,9 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Choose starting hand for each player with multiple hands
|
// Choose starting hand for each player with multiple hands
|
||||||
|
if (game.getRules().getGameType() != GameType.Puzzle) {
|
||||||
performMulligans(first, game.getRules().hasAppliedVariant(GameType.Commander));
|
performMulligans(first, game.getRules().hasAppliedVariant(GameType.Commander));
|
||||||
|
}
|
||||||
if (game.isGameOver()) { break; } // conceded during "mulligan" prompt
|
if (game.isGameOver()) { break; } // conceded during "mulligan" prompt
|
||||||
|
|
||||||
game.setAge(GameStage.Play);
|
game.setAge(GameStage.Play);
|
||||||
@@ -1472,8 +1481,8 @@ public class GameAction {
|
|||||||
game.getTriggerHandler().runTrigger(TriggerType.NewGame, runParams, true);
|
game.getTriggerHandler().runTrigger(TriggerType.NewGame, runParams, true);
|
||||||
//</THIS CODE WILL WORK WITH PHASE = NULL>
|
//</THIS CODE WILL WORK WITH PHASE = NULL>
|
||||||
|
|
||||||
game.getPhaseHandler().startFirstTurn(first);
|
|
||||||
|
|
||||||
|
game.getPhaseHandler().startFirstTurn(first, startGameHook);
|
||||||
//after game ends, ensure Auto-Pass canceled for all players so it doesn't apply to next game
|
//after game ends, ensure Auto-Pass canceled for all players so it doesn't apply to next game
|
||||||
for (Player p : game.getRegisteredPlayers()) {
|
for (Player p : game.getRegisteredPlayers()) {
|
||||||
p.getController().autoPassCancel();
|
p.getController().autoPassCancel();
|
||||||
@@ -1487,15 +1496,20 @@ public class GameAction {
|
|||||||
// Only cut/coin toss if it's the first game of the match
|
// Only cut/coin toss if it's the first game of the match
|
||||||
Player goesFirst = null;
|
Player goesFirst = null;
|
||||||
|
|
||||||
|
if (game != null) {
|
||||||
|
if (game.getRules().getGameType().equals(GameType.Puzzle)) {
|
||||||
|
return game.getPlayers().get(0);
|
||||||
|
}
|
||||||
|
|
||||||
// 904.6: in Archenemy games the Archenemy goes first
|
// 904.6: in Archenemy games the Archenemy goes first
|
||||||
if (game != null && game.getRules().hasAppliedVariant(GameType.Archenemy)) {
|
if (game.getRules().hasAppliedVariant(GameType.Archenemy)) {
|
||||||
for (Player p : game.getPlayers()) {
|
for (Player p : game.getPlayers()) {
|
||||||
if (p.isArchenemy()) {
|
if (p.isArchenemy()) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Power Play - Each player with a Power Play in the CommandZone becomes the Starting Player
|
// Power Play - Each player with a Power Play in the CommandZone becomes the Starting Player
|
||||||
Set<Player> powerPlayers = Sets.newHashSet();
|
Set<Player> powerPlayers = Sets.newHashSet();
|
||||||
for (Card c : game.getCardsIn(ZoneType.Command)) {
|
for (Card c : game.getCardsIn(ZoneType.Command)) {
|
||||||
@@ -1629,6 +1643,31 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void runPreOpeningHandActions(final Player first) {
|
||||||
|
Player takesAction = first;
|
||||||
|
do {
|
||||||
|
//
|
||||||
|
List<Card> ploys = CardLists.filter(takesAction.getCardsIn(ZoneType.Command), new Predicate<Card>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(Card input) {
|
||||||
|
return input.getName().equals("Emissary's Ploy");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
int chosen = 1;
|
||||||
|
List<Integer> cmc = Lists.newArrayList(1, 2, 3);
|
||||||
|
|
||||||
|
for (Card c : ploys) {
|
||||||
|
if (!cmc.isEmpty()) {
|
||||||
|
chosen = takesAction.getController().chooseNumber(c.getSpellPermanent(), "Emissary's Ploy", cmc, c.getOwner());
|
||||||
|
cmc.remove((Object)chosen);
|
||||||
|
}
|
||||||
|
|
||||||
|
c.setChosenNumber(chosen);
|
||||||
|
}
|
||||||
|
takesAction = game.getNextPlayerAfter(takesAction);
|
||||||
|
} while (takesAction != first);
|
||||||
|
}
|
||||||
|
|
||||||
private void runOpeningHandActions(final Player first) {
|
private void runOpeningHandActions(final Player first) {
|
||||||
Player takesAction = first;
|
Player takesAction = first;
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
abstract public void setCounters(final Map<CounterType, Integer> allCounters);
|
abstract public void setCounters(final Map<CounterType, Integer> allCounters);
|
||||||
|
|
||||||
abstract public boolean canReceiveCounters(final CounterType type);
|
abstract public boolean canReceiveCounters(final CounterType type);
|
||||||
abstract protected void addCounter(final CounterType counterType, final int n, final boolean applyMultiplier, final boolean fireEvents);
|
abstract public void addCounter(final CounterType counterType, final int n, final boolean applyMultiplier, final boolean fireEvents);
|
||||||
abstract public void subtractCounter(final CounterType counterName, final int n);
|
abstract public void subtractCounter(final CounterType counterName, final int n);
|
||||||
abstract public void clearCounters();
|
abstract public void clearCounters();
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public enum GameType {
|
|||||||
Quest (DeckFormat.QuestDeck, true, true, false, "Quest", ""),
|
Quest (DeckFormat.QuestDeck, true, true, false, "Quest", ""),
|
||||||
QuestDraft (DeckFormat.Limited, true, true, true, "Quest Draft", ""),
|
QuestDraft (DeckFormat.Limited, true, true, true, "Quest Draft", ""),
|
||||||
PlanarConquest (DeckFormat.PlanarConquest, true, false, false, "Planar Conquest", ""),
|
PlanarConquest (DeckFormat.PlanarConquest, true, false, false, "Planar Conquest", ""),
|
||||||
|
Puzzle (DeckFormat.Puzzle, false, false, false, "Puzzle", "Solve a puzzle from the given game state"),
|
||||||
Constructed (DeckFormat.Constructed, false, true, true, "Constructed", ""),
|
Constructed (DeckFormat.Constructed, false, true, true, "Constructed", ""),
|
||||||
DeckManager (DeckFormat.Constructed, false, true, true, "Deck Manager", ""),
|
DeckManager (DeckFormat.Constructed, false, true, true, "Deck Manager", ""),
|
||||||
Vanguard (DeckFormat.Vanguard, true, true, true, "Vanguard", "Each player has a special \"Avatar\" card that affects the game."),
|
Vanguard (DeckFormat.Vanguard, true, true, true, "Vanguard", "Each player has a special \"Avatar\" card that affects the game."),
|
||||||
|
|||||||
@@ -80,6 +80,10 @@ public class Match {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startGame(final Game game) {
|
public void startGame(final Game game) {
|
||||||
|
startGame(game, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startGame(final Game game, Runnable startGameHook) {
|
||||||
prepareAllZones(game);
|
prepareAllZones(game);
|
||||||
if (rules.useAnte()) { // Deciding which cards go to ante
|
if (rules.useAnte()) { // Deciding which cards go to ante
|
||||||
Multimap<Player, Card> list = game.chooseCardsForAnte(rules.getMatchAnteRarity());
|
Multimap<Player, Card> list = game.chooseCardsForAnte(rules.getMatchAnteRarity());
|
||||||
@@ -92,7 +96,8 @@ public class Match {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GameOutcome lastOutcome = gamesPlayed.isEmpty() ? null : gamesPlayed.get(gamesPlayed.size() - 1);
|
GameOutcome lastOutcome = gamesPlayed.isEmpty() ? null : gamesPlayed.get(gamesPlayed.size() - 1);
|
||||||
game.getAction().startGame(lastOutcome);
|
|
||||||
|
game.getAction().startGame(lastOutcome, startGameHook);
|
||||||
|
|
||||||
if (rules.useAnte()) {
|
if (rules.useAnte()) {
|
||||||
executeAnte(game);
|
executeAnte(game);
|
||||||
|
|||||||
@@ -987,7 +987,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addCounter(final CounterType counterType, final int n, final boolean applyMultiplier, final boolean fireEvents) {
|
public void addCounter(final CounterType counterType, final int n, final boolean applyMultiplier, final boolean fireEvents) {
|
||||||
int addAmount = n;
|
int addAmount = n;
|
||||||
if(addAmount < 0) {
|
if(addAmount < 0) {
|
||||||
addAmount = 0; // As per rule 107.1b
|
addAmount = 0; // As per rule 107.1b
|
||||||
|
|||||||
@@ -878,6 +878,10 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
private final static boolean DEBUG_PHASES = false;
|
private final static boolean DEBUG_PHASES = false;
|
||||||
|
|
||||||
public void startFirstTurn(Player goesFirst) {
|
public void startFirstTurn(Player goesFirst) {
|
||||||
|
startFirstTurn(goesFirst, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startFirstTurn(Player goesFirst, Runnable startGameHook) {
|
||||||
StopWatch sw = new StopWatch();
|
StopWatch sw = new StopWatch();
|
||||||
|
|
||||||
if (phase != null) {
|
if (phase != null) {
|
||||||
@@ -891,6 +895,11 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
// don't even offer priority, because it's untap of 1st turn now
|
// don't even offer priority, because it's untap of 1st turn now
|
||||||
givePriorityToPlayer = false;
|
givePriorityToPlayer = false;
|
||||||
|
|
||||||
|
if (startGameHook != null) {
|
||||||
|
startGameHook.run();
|
||||||
|
givePriorityToPlayer = true;
|
||||||
|
}
|
||||||
|
|
||||||
// MAIN GAME LOOP
|
// MAIN GAME LOOP
|
||||||
while (!game.isGameOver()) {
|
while (!game.isGameOver()) {
|
||||||
if (givePriorityToPlayer) {
|
if (givePriorityToPlayer) {
|
||||||
|
|||||||
@@ -884,7 +884,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addCounter(CounterType counterType, int n, boolean applyMultiplier, boolean fireEvents) {
|
public void addCounter(CounterType counterType, int n, boolean applyMultiplier, boolean fireEvents) {
|
||||||
if (!canReceiveCounters(counterType)) {
|
if (!canReceiveCounters(counterType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import forge.screens.home.gauntlet.VSubmenuGauntletContests;
|
|||||||
import forge.screens.home.gauntlet.VSubmenuGauntletLoad;
|
import forge.screens.home.gauntlet.VSubmenuGauntletLoad;
|
||||||
import forge.screens.home.gauntlet.VSubmenuGauntletQuick;
|
import forge.screens.home.gauntlet.VSubmenuGauntletQuick;
|
||||||
import forge.screens.home.online.VSubmenuOnlineLobby;
|
import forge.screens.home.online.VSubmenuOnlineLobby;
|
||||||
|
import forge.screens.home.puzzle.VSubmenuPuzzleCreate;
|
||||||
|
import forge.screens.home.puzzle.VSubmenuPuzzleSolve;
|
||||||
import forge.screens.home.quest.VSubmenuChallenges;
|
import forge.screens.home.quest.VSubmenuChallenges;
|
||||||
import forge.screens.home.quest.VSubmenuDuels;
|
import forge.screens.home.quest.VSubmenuDuels;
|
||||||
import forge.screens.home.quest.VSubmenuQuestData;
|
import forge.screens.home.quest.VSubmenuQuestData;
|
||||||
@@ -71,6 +73,8 @@ public enum EDocID {
|
|||||||
HOME_ACHIEVEMENTS (VSubmenuAchievements.SINGLETON_INSTANCE),
|
HOME_ACHIEVEMENTS (VSubmenuAchievements.SINGLETON_INSTANCE),
|
||||||
HOME_AVATARS (VSubmenuAvatars.SINGLETON_INSTANCE),
|
HOME_AVATARS (VSubmenuAvatars.SINGLETON_INSTANCE),
|
||||||
HOME_UTILITIES (VSubmenuDownloaders.SINGLETON_INSTANCE),
|
HOME_UTILITIES (VSubmenuDownloaders.SINGLETON_INSTANCE),
|
||||||
|
HOME_PUZZLE_CREATE(VSubmenuPuzzleCreate.SINGLETON_INSTANCE),
|
||||||
|
HOME_PUZZLE_SOLVE(VSubmenuPuzzleSolve.SINGLETON_INSTANCE),
|
||||||
HOME_CONSTRUCTED (VSubmenuConstructed.SINGLETON_INSTANCE),
|
HOME_CONSTRUCTED (VSubmenuConstructed.SINGLETON_INSTANCE),
|
||||||
HOME_DRAFT (VSubmenuDraft.SINGLETON_INSTANCE),
|
HOME_DRAFT (VSubmenuDraft.SINGLETON_INSTANCE),
|
||||||
HOME_SEALED (VSubmenuSealed.SINGLETON_INSTANCE),
|
HOME_SEALED (VSubmenuSealed.SINGLETON_INSTANCE),
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package forge.screens.deckeditor.views;
|
package forge.screens.deckeditor.views;
|
||||||
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import forge.gui.framework.DragCell;
|
import forge.gui.framework.DragCell;
|
||||||
import forge.gui.framework.DragTab;
|
import forge.gui.framework.DragTab;
|
||||||
import forge.gui.framework.EDocID;
|
import forge.gui.framework.EDocID;
|
||||||
@@ -11,6 +8,9 @@ import forge.item.InventoryItem;
|
|||||||
import forge.itemmanager.ItemManager;
|
import forge.itemmanager.ItemManager;
|
||||||
import forge.itemmanager.ItemManagerContainer;
|
import forge.itemmanager.ItemManagerContainer;
|
||||||
import forge.screens.deckeditor.controllers.CCardCatalog;
|
import forge.screens.deckeditor.controllers.CCardCatalog;
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assembles Swing components of card catalog in deck editor.
|
* Assembles Swing components of card catalog in deck editor.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public enum EMenuGroup {
|
|||||||
SANCTIONED ("Sanctioned Formats"),
|
SANCTIONED ("Sanctioned Formats"),
|
||||||
ONLINE ("Online Multiplayer"),
|
ONLINE ("Online Multiplayer"),
|
||||||
QUEST ("Quest Mode"),
|
QUEST ("Quest Mode"),
|
||||||
|
PUZZLE ("Puzzle Mode"),
|
||||||
GAUNTLET ("Gauntlets"),
|
GAUNTLET ("Gauntlets"),
|
||||||
SETTINGS ("Game Settings");
|
SETTINGS ("Game Settings");
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import javax.swing.JPanel;
|
|||||||
import javax.swing.ScrollPaneConstants;
|
import javax.swing.ScrollPaneConstants;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
|
import forge.screens.home.puzzle.VSubmenuPuzzleCreate;
|
||||||
|
import forge.screens.home.puzzle.VSubmenuPuzzleSolve;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
import forge.Singletons;
|
import forge.Singletons;
|
||||||
import forge.assets.FSkinProp;
|
import forge.assets.FSkinProp;
|
||||||
@@ -56,6 +58,7 @@ import forge.screens.home.quest.VSubmenuQuestPrefs;
|
|||||||
import forge.screens.home.sanctioned.VSubmenuConstructed;
|
import forge.screens.home.sanctioned.VSubmenuConstructed;
|
||||||
import forge.screens.home.sanctioned.VSubmenuDraft;
|
import forge.screens.home.sanctioned.VSubmenuDraft;
|
||||||
import forge.screens.home.sanctioned.VSubmenuSealed;
|
import forge.screens.home.sanctioned.VSubmenuSealed;
|
||||||
|
import forge.screens.home.sanctioned.VSubmenuWinston;
|
||||||
import forge.screens.home.settings.VSubmenuAchievements;
|
import forge.screens.home.settings.VSubmenuAchievements;
|
||||||
import forge.screens.home.settings.VSubmenuAvatars;
|
import forge.screens.home.settings.VSubmenuAvatars;
|
||||||
import forge.screens.home.settings.VSubmenuDownloaders;
|
import forge.screens.home.settings.VSubmenuDownloaders;
|
||||||
@@ -132,6 +135,9 @@ public enum VHomeUI implements IVTopLevelUI {
|
|||||||
allSubmenus.add(VSubmenuGauntletLoad.SINGLETON_INSTANCE);
|
allSubmenus.add(VSubmenuGauntletLoad.SINGLETON_INSTANCE);
|
||||||
allSubmenus.add(VSubmenuGauntletContests.SINGLETON_INSTANCE);
|
allSubmenus.add(VSubmenuGauntletContests.SINGLETON_INSTANCE);
|
||||||
|
|
||||||
|
allSubmenus.add(VSubmenuPuzzleSolve.SINGLETON_INSTANCE);
|
||||||
|
allSubmenus.add(VSubmenuPuzzleCreate.SINGLETON_INSTANCE);
|
||||||
|
|
||||||
allSubmenus.add(VSubmenuPreferences.SINGLETON_INSTANCE);
|
allSubmenus.add(VSubmenuPreferences.SINGLETON_INSTANCE);
|
||||||
allSubmenus.add(VSubmenuAchievements.SINGLETON_INSTANCE);
|
allSubmenus.add(VSubmenuAchievements.SINGLETON_INSTANCE);
|
||||||
allSubmenus.add(VSubmenuAvatars.SINGLETON_INSTANCE);
|
allSubmenus.add(VSubmenuAvatars.SINGLETON_INSTANCE);
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package forge.screens.home.puzzle;
|
||||||
|
|
||||||
|
import forge.gui.framework.ICDoc;
|
||||||
|
import forge.menus.IMenuProvider;
|
||||||
|
import forge.menus.MenuUtil;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public enum CSubmenuPuzzleCreate implements ICDoc, IMenuProvider {
|
||||||
|
SINGLETON_INSTANCE;
|
||||||
|
|
||||||
|
private VSubmenuPuzzleCreate view = VSubmenuPuzzleCreate.SINGLETON_INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void register() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
MenuUtil.setMenuProvider(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JMenu> getMenus() {
|
||||||
|
final List<JMenu> menus = new ArrayList<JMenu>();
|
||||||
|
menus.add(PuzzleGameMenu.getMenu());
|
||||||
|
return menus;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package forge.screens.home.puzzle;
|
||||||
|
|
||||||
|
import forge.GuiBase;
|
||||||
|
import forge.UiCommand;
|
||||||
|
import forge.deck.Deck;
|
||||||
|
import forge.game.GameRules;
|
||||||
|
import forge.game.GameType;
|
||||||
|
import forge.game.player.RegisteredPlayer;
|
||||||
|
import forge.gauntlet.GauntletData;
|
||||||
|
import forge.gauntlet.GauntletIO;
|
||||||
|
import forge.gui.SOverlayUtils;
|
||||||
|
import forge.gui.framework.ICDoc;
|
||||||
|
import forge.match.HostedMatch;
|
||||||
|
import forge.menus.IMenuProvider;
|
||||||
|
import forge.menus.MenuUtil;
|
||||||
|
import forge.player.GamePlayerUtil;
|
||||||
|
import forge.puzzle.Puzzle;
|
||||||
|
import forge.puzzle.PuzzleIO;
|
||||||
|
import forge.quest.QuestUtil;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public enum CSubmenuPuzzleSolve implements ICDoc, IMenuProvider {
|
||||||
|
SINGLETON_INSTANCE;
|
||||||
|
|
||||||
|
private VSubmenuPuzzleSolve view = VSubmenuPuzzleSolve.SINGLETON_INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void register() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
view.getList().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
updateData();
|
||||||
|
view.getBtnStart().addActionListener(
|
||||||
|
new ActionListener() { @Override
|
||||||
|
public void actionPerformed(final ActionEvent e) { startPuzzleSolve(); } });
|
||||||
|
}
|
||||||
|
|
||||||
|
private final UiCommand cmdStart = new UiCommand() {
|
||||||
|
@Override public void run() {
|
||||||
|
startPuzzleSolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void updateData() {
|
||||||
|
final ArrayList<Puzzle> puzzles = PuzzleIO.loadPuzzles();
|
||||||
|
for(Puzzle p : puzzles) {
|
||||||
|
view.getModel().addElement(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update() {
|
||||||
|
MenuUtil.setMenuProvider(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JMenu> getMenus() {
|
||||||
|
final List<JMenu> menus = new ArrayList<JMenu>();
|
||||||
|
menus.add(PuzzleGameMenu.getMenu());
|
||||||
|
return menus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean startPuzzleSolve() {
|
||||||
|
final Puzzle selected = (Puzzle)view.getList().getSelectedValue();
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
SOverlayUtils.startGameOverlay();
|
||||||
|
SOverlayUtils.showOverlay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
|
||||||
|
hostedMatch.setStartGameHook(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public final void run() {
|
||||||
|
selected.applyToGame(hostedMatch.getGame());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<RegisteredPlayer> players = new ArrayList<RegisteredPlayer>();
|
||||||
|
final RegisteredPlayer human = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.getGuiPlayer());
|
||||||
|
human.setStartingHand(0);
|
||||||
|
players.add(human);
|
||||||
|
|
||||||
|
final RegisteredPlayer ai = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.createAiPlayer());
|
||||||
|
ai.setStartingHand(0);
|
||||||
|
players.add(ai);
|
||||||
|
|
||||||
|
GameRules rules = new GameRules(GameType.Puzzle);
|
||||||
|
rules.setGamesPerMatch(1);
|
||||||
|
hostedMatch.startMatch(rules, null, players, human, GuiBase.getInterface().getNewGuiGame());
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
SOverlayUtils.hideOverlay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package forge.screens.home.puzzle;
|
||||||
|
|
||||||
|
import forge.model.FModel;
|
||||||
|
import forge.properties.ForgePreferences;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
public class PuzzleGameMenu {
|
||||||
|
private PuzzleGameMenu() { }
|
||||||
|
|
||||||
|
private static ForgePreferences prefs = FModel.getPreferences();
|
||||||
|
|
||||||
|
public static JMenu getMenu() {
|
||||||
|
JMenu menu = new JMenu("Puzzle");
|
||||||
|
menu.setMnemonic(KeyEvent.VK_G);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package forge.screens.home.puzzle;
|
||||||
|
|
||||||
|
import forge.gui.framework.DragCell;
|
||||||
|
import forge.gui.framework.DragTab;
|
||||||
|
import forge.gui.framework.EDocID;
|
||||||
|
import forge.interfaces.IPlayerChangeListener;
|
||||||
|
import forge.match.GameLobby;
|
||||||
|
import forge.match.LocalLobby;
|
||||||
|
import forge.net.event.UpdateLobbyPlayerEvent;
|
||||||
|
import forge.screens.home.EMenuGroup;
|
||||||
|
import forge.screens.home.IVSubmenu;
|
||||||
|
import forge.screens.home.VHomeUI;
|
||||||
|
import forge.screens.home.VLobby;
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
public enum VSubmenuPuzzleCreate implements IVSubmenu<CSubmenuPuzzleCreate> {
|
||||||
|
SINGLETON_INSTANCE;
|
||||||
|
|
||||||
|
private DragCell parentCell;
|
||||||
|
private final DragTab tab = new DragTab("Puzzle Mode: Create");
|
||||||
|
|
||||||
|
private final GameLobby lobby = new LocalLobby();
|
||||||
|
private final VLobby vLobby = new VLobby(lobby);
|
||||||
|
|
||||||
|
VSubmenuPuzzleCreate() {
|
||||||
|
lobby.setListener(vLobby);
|
||||||
|
|
||||||
|
vLobby.setPlayerChangeListener(new IPlayerChangeListener() {
|
||||||
|
@Override public final void update(final int index, final UpdateLobbyPlayerEvent event) {
|
||||||
|
lobby.applyToSlot(index, event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
vLobby.update(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EMenuGroup getGroupEnum() {
|
||||||
|
return EMenuGroup.PUZZLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMenuTitle() {
|
||||||
|
return "Create";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EDocID getItemEnum() {
|
||||||
|
return EDocID.HOME_PUZZLE_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EDocID getDocumentID() {
|
||||||
|
return EDocID.HOME_PUZZLE_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DragTab getTabLabel() {
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CSubmenuPuzzleCreate getLayoutControl() {
|
||||||
|
return CSubmenuPuzzleCreate.SINGLETON_INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParentCell(DragCell cell0) {
|
||||||
|
this.parentCell = cell0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DragCell getParentCell() {
|
||||||
|
return this.parentCell;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void populate() {
|
||||||
|
final JPanel container = VHomeUI.SINGLETON_INSTANCE.getPnlDisplay();
|
||||||
|
|
||||||
|
container.removeAll();
|
||||||
|
container.setLayout(new MigLayout("insets 0, gap 0, wrap 1, ax right"));
|
||||||
|
vLobby.getLblTitle().setText("Puzzle Mode: Create");
|
||||||
|
container.add(vLobby.getLblTitle(), "w 80%, h 40px!, gap 0 0 15px 15px, span 2, al right, pushx");
|
||||||
|
|
||||||
|
|
||||||
|
if (container.isShowing()) {
|
||||||
|
container.validate();
|
||||||
|
container.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package forge.screens.home.puzzle;
|
||||||
|
|
||||||
|
import forge.gui.framework.DragCell;
|
||||||
|
import forge.gui.framework.DragTab;
|
||||||
|
import forge.gui.framework.EDocID;
|
||||||
|
import forge.interfaces.IPlayerChangeListener;
|
||||||
|
import forge.match.GameLobby;
|
||||||
|
import forge.match.LocalLobby;
|
||||||
|
import forge.net.event.UpdateLobbyPlayerEvent;
|
||||||
|
import forge.screens.home.*;
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
public enum VSubmenuPuzzleSolve implements IVSubmenu<CSubmenuPuzzleSolve> {
|
||||||
|
SINGLETON_INSTANCE;
|
||||||
|
|
||||||
|
private final JList puzzleList;
|
||||||
|
final DefaultListModel model = new DefaultListModel();
|
||||||
|
|
||||||
|
private final StartButton btnStart = new StartButton();
|
||||||
|
|
||||||
|
private DragCell parentCell;
|
||||||
|
private final DragTab tab = new DragTab("Puzzle Mode: Solve");
|
||||||
|
|
||||||
|
private final GameLobby lobby = new LocalLobby();
|
||||||
|
private final VLobby vLobby = new VLobby(lobby);
|
||||||
|
|
||||||
|
VSubmenuPuzzleSolve() {
|
||||||
|
puzzleList = new JList<>();
|
||||||
|
lobby.setListener(vLobby);
|
||||||
|
|
||||||
|
vLobby.setPlayerChangeListener(new IPlayerChangeListener() {
|
||||||
|
@Override public final void update(final int index, final UpdateLobbyPlayerEvent event) {
|
||||||
|
lobby.applyToSlot(index, event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
vLobby.update(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EMenuGroup getGroupEnum() {
|
||||||
|
return EMenuGroup.PUZZLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMenuTitle() {
|
||||||
|
return "Solve";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EDocID getItemEnum() {
|
||||||
|
return EDocID.HOME_PUZZLE_SOLVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EDocID getDocumentID() {
|
||||||
|
return EDocID.HOME_PUZZLE_SOLVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DragTab getTabLabel() {
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CSubmenuPuzzleSolve getLayoutControl() {
|
||||||
|
return CSubmenuPuzzleSolve.SINGLETON_INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParentCell(DragCell cell0) {
|
||||||
|
this.parentCell = cell0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DragCell getParentCell() {
|
||||||
|
return this.parentCell;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JList getList() {
|
||||||
|
return puzzleList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultListModel getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StartButton getBtnStart() {
|
||||||
|
return btnStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void populate() {
|
||||||
|
final JPanel container = VHomeUI.SINGLETON_INSTANCE.getPnlDisplay();
|
||||||
|
|
||||||
|
container.removeAll();
|
||||||
|
container.setLayout(new MigLayout("insets 0, gap 0, wrap 1, ax right"));
|
||||||
|
vLobby.getLblTitle().setText("Puzzle Mode: Solve");
|
||||||
|
container.add(vLobby.getLblTitle(), "w 80%, h 40px!, gap 0 0 15px 15px, span 2, al right, pushx");
|
||||||
|
puzzleList.setModel(model);
|
||||||
|
container.add(puzzleList, "w 80%, h 200px!, gap 0 0 0px 0px, span 2, al center");
|
||||||
|
container.add(btnStart, "w 98%!, ax center, gap 1% 0 20px 20px, span 2");
|
||||||
|
|
||||||
|
|
||||||
|
if (container.isShowing()) {
|
||||||
|
container.validate();
|
||||||
|
container.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import forge.properties.ForgePreferences.FPref;
|
|||||||
import forge.screens.FScreen;
|
import forge.screens.FScreen;
|
||||||
import forge.screens.constructed.ConstructedScreen;
|
import forge.screens.constructed.ConstructedScreen;
|
||||||
import forge.screens.gauntlet.NewGauntletScreen;
|
import forge.screens.gauntlet.NewGauntletScreen;
|
||||||
|
import forge.screens.home.puzzle.PuzzleScreen;
|
||||||
import forge.screens.limited.NewDraftScreen;
|
import forge.screens.limited.NewDraftScreen;
|
||||||
import forge.screens.limited.NewSealedScreen;
|
import forge.screens.limited.NewSealedScreen;
|
||||||
import forge.screens.planarconquest.NewConquestScreen;
|
import forge.screens.planarconquest.NewConquestScreen;
|
||||||
@@ -24,6 +25,7 @@ public class NewGameMenu extends FPopupMenu {
|
|||||||
BoosterDraft("Booster Draft", FSkinImage.HAND, NewDraftScreen.class),
|
BoosterDraft("Booster Draft", FSkinImage.HAND, NewDraftScreen.class),
|
||||||
SealedDeck("Sealed Deck", FSkinImage.PACK, NewSealedScreen.class),
|
SealedDeck("Sealed Deck", FSkinImage.PACK, NewSealedScreen.class),
|
||||||
QuestMode("Quest Mode", FSkinImage.QUEST_ZEP, NewQuestScreen.class),
|
QuestMode("Quest Mode", FSkinImage.QUEST_ZEP, NewQuestScreen.class),
|
||||||
|
PuzzleMode("Puzzle Mode", FSkinImage.QUEST_BOOK, PuzzleScreen.class),
|
||||||
PlanarConquest("Planar Conquest", FSkinImage.MULTIVERSE, NewConquestScreen.class),
|
PlanarConquest("Planar Conquest", FSkinImage.MULTIVERSE, NewConquestScreen.class),
|
||||||
Gauntlet("Gauntlet", FSkinImage.ALPHASTRIKE, NewGauntletScreen.class);
|
Gauntlet("Gauntlet", FSkinImage.ALPHASTRIKE, NewGauntletScreen.class);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package forge.screens.home.puzzle;
|
||||||
|
|
||||||
|
import forge.assets.FSkinFont;
|
||||||
|
import forge.screens.LaunchScreen;
|
||||||
|
import forge.screens.home.NewGameMenu;
|
||||||
|
import forge.toolbox.FLabel;
|
||||||
|
import forge.toolbox.FTextArea;
|
||||||
|
import forge.util.ThreadUtil;
|
||||||
|
import forge.util.Utils;
|
||||||
|
|
||||||
|
public class PuzzleScreen extends LaunchScreen {
|
||||||
|
private static final float PADDING = Utils.scale(10);
|
||||||
|
|
||||||
|
private final FTextArea lblDesc = add(new FTextArea(false,
|
||||||
|
"Puzzle Mode loads in a puzzle that you have to win in a predetermined time/way."));
|
||||||
|
|
||||||
|
public PuzzleScreen() {
|
||||||
|
super(null, NewGameMenu.getMenu());
|
||||||
|
|
||||||
|
lblDesc.setFont(FSkinFont.get(12));
|
||||||
|
lblDesc.setTextColor(FLabel.INLINE_LABEL_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLayoutAboveBtnStart(float startY, float width, float height) {
|
||||||
|
float x = PADDING;
|
||||||
|
float y = startY + PADDING;
|
||||||
|
float w = width - 2 * PADDING;
|
||||||
|
float h = height - y - PADDING;
|
||||||
|
lblDesc.setBounds(x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startMatch() {
|
||||||
|
ThreadUtil.invokeInGameThread(new Runnable() { //must run in game thread to prevent blocking UI thread
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Load selected puzzle
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
13
forge-gui/res/puzzle/PS1.pzl
Normal file
13
forge-gui/res/puzzle/PS1.pzl
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[metadata]
|
||||||
|
Name:Possibility Storm #1
|
||||||
|
URL:https://i.redd.it/wws1h03gy7ky.png
|
||||||
|
Goal:Win
|
||||||
|
Turns:1
|
||||||
|
[state]
|
||||||
|
ActivePlayer=Human
|
||||||
|
ActivePhase=Main1
|
||||||
|
HumanLife=20
|
||||||
|
AILife=9
|
||||||
|
HumanPlay=Key to the City; Ravenous Intruder; Ruinous Gremlin; Island; Island; Island; Island; Island; Mountain; Mountain
|
||||||
|
HumanHand=Welcome to the Fold; Chilling Grasp; Flame Lash; Confirm Suspicions
|
||||||
|
AIPlay=Workshop Assistant; Thriving Ibex
|
||||||
@@ -54,6 +54,7 @@ public class HostedMatch {
|
|||||||
private Match match;
|
private Match match;
|
||||||
private Game game;
|
private Game game;
|
||||||
private String title;
|
private String title;
|
||||||
|
private Runnable startGameHook = null;
|
||||||
private final List<PlayerControllerHuman> humanControllers = Lists.newArrayList();
|
private final List<PlayerControllerHuman> humanControllers = Lists.newArrayList();
|
||||||
private Map<RegisteredPlayer, IGuiGame> guis;
|
private Map<RegisteredPlayer, IGuiGame> guis;
|
||||||
private int humanCount;
|
private int humanCount;
|
||||||
@@ -62,7 +63,10 @@ public class HostedMatch {
|
|||||||
private final Map<PlayerControllerHuman, NextGameDecision> nextGameDecisions = Maps.newHashMap();
|
private final Map<PlayerControllerHuman, NextGameDecision> nextGameDecisions = Maps.newHashMap();
|
||||||
private boolean isMatchOver = false;
|
private boolean isMatchOver = false;
|
||||||
|
|
||||||
public HostedMatch() {
|
public HostedMatch() {}
|
||||||
|
|
||||||
|
public void setStartGameHook(Runnable hook) {
|
||||||
|
startGameHook = hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GameRules getDefaultRules(final GameType gameType) {
|
private static GameRules getDefaultRules(final GameType gameType) {
|
||||||
@@ -216,9 +220,8 @@ public class HostedMatch {
|
|||||||
playbackControl.setGame(game);
|
playbackControl.setGame(game);
|
||||||
game.subscribeToEvents(playbackControl);
|
game.subscribeToEvents(playbackControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually start the game!
|
// Actually start the game!
|
||||||
match.startGame(game);
|
match.startGame(game, startGameHook);
|
||||||
|
|
||||||
// After game is over...
|
// After game is over...
|
||||||
isMatchOver = match.isMatchOver();
|
isMatchOver = match.isMatchOver();
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ public final class ForgeConstants {
|
|||||||
public static final String MUSIC_DIR = RES_DIR + "music" + PATH_SEPARATOR;
|
public static final String MUSIC_DIR = RES_DIR + "music" + PATH_SEPARATOR;
|
||||||
public static final String LANG_DIR = RES_DIR + "languages" + PATH_SEPARATOR;
|
public static final String LANG_DIR = RES_DIR + "languages" + PATH_SEPARATOR;
|
||||||
public static final String EFFECTS_DIR = RES_DIR + "effects" + PATH_SEPARATOR;
|
public static final String EFFECTS_DIR = RES_DIR + "effects" + PATH_SEPARATOR;
|
||||||
|
public static final String PUZZLE_DIR = RES_DIR + "puzzle" + PATH_SEPARATOR;
|
||||||
|
|
||||||
|
|
||||||
private static final String QUEST_DIR = RES_DIR + "quest" + PATH_SEPARATOR;
|
private static final String QUEST_DIR = RES_DIR + "quest" + PATH_SEPARATOR;
|
||||||
public static final String QUEST_WORLD_DIR = QUEST_DIR + "world" + PATH_SEPARATOR;
|
public static final String QUEST_WORLD_DIR = QUEST_DIR + "world" + PATH_SEPARATOR;
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
|
|||||||
SUBMENU_ONLINE ("false"),
|
SUBMENU_ONLINE ("false"),
|
||||||
SUBMENU_GAUNTLET ("false"),
|
SUBMENU_GAUNTLET ("false"),
|
||||||
SUBMENU_QUEST ("false"),
|
SUBMENU_QUEST ("false"),
|
||||||
|
SUBMENU_PUZZLE("false"),
|
||||||
SUBMENU_SETTINGS ("false"),
|
SUBMENU_SETTINGS ("false"),
|
||||||
SUBMENU_UTILITIES ("false"),
|
SUBMENU_UTILITIES ("false"),
|
||||||
|
|
||||||
|
|||||||
104
forge-gui/src/main/java/forge/puzzle/Puzzle.java
Normal file
104
forge-gui/src/main/java/forge/puzzle/Puzzle.java
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package forge.puzzle;
|
||||||
|
|
||||||
|
import forge.ai.GameState;
|
||||||
|
import forge.game.Game;
|
||||||
|
import forge.game.ability.AbilityFactory;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.trigger.Trigger;
|
||||||
|
import forge.game.trigger.TriggerHandler;
|
||||||
|
import forge.game.zone.PlayerZone;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
|
import forge.item.IPaperCard;
|
||||||
|
import forge.item.InventoryItem;
|
||||||
|
import forge.model.FModel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Puzzle extends GameState implements InventoryItem {
|
||||||
|
String name;
|
||||||
|
String goal;
|
||||||
|
String url;
|
||||||
|
int turns;
|
||||||
|
|
||||||
|
public Puzzle(Map<String, List<String>> puzzleLines) {
|
||||||
|
loadMetaData(puzzleLines.get("metadata"));
|
||||||
|
loadGameState(puzzleLines.get("state"));
|
||||||
|
// Generate goal enforcement
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadMetaData(List<String> metadataLines) {
|
||||||
|
for(String line : metadataLines) {
|
||||||
|
String[] split = line.split(":");
|
||||||
|
if ("Name".equalsIgnoreCase(split[0])) {
|
||||||
|
this.name = split[1];
|
||||||
|
} else if ("Goal".equalsIgnoreCase(split[0])) {
|
||||||
|
this.goal = split[1];
|
||||||
|
} else if ("Url".equalsIgnoreCase(split[0])) {
|
||||||
|
this.url = split[1];
|
||||||
|
} else if ("Turns".equalsIgnoreCase(split[0])) {
|
||||||
|
this.turns = Integer.parseInt(split[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadGameState(List<String> stateLines) {
|
||||||
|
this.parse(stateLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPaperCard getPaperCard(final String cardName) {
|
||||||
|
return FModel.getMagicDb().getCommonCards().getCard(cardName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGoalEnforcement(Game game) {
|
||||||
|
Player human = null;
|
||||||
|
for(Player p : game.getPlayers()) {
|
||||||
|
if (p.getController().isGuiPlayer()) {
|
||||||
|
human = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Card goalCard = new Card(-1, game);
|
||||||
|
|
||||||
|
goalCard.setOwner(human);
|
||||||
|
goalCard.setImageKey("t:puzzle");
|
||||||
|
goalCard.setName("Puzzle Goal");
|
||||||
|
goalCard.addType("Effect");
|
||||||
|
|
||||||
|
{
|
||||||
|
final String loseTrig = "Mode$ Phase | Phase$ Cleanup | TriggerZones$ Command | Static$ True | " +
|
||||||
|
"ValidPlayer$ You | TriggerDescription$ At the beginning of your cleanup step, you lose the game.";
|
||||||
|
final String loseEff = "DB$ LosesGame | Defined$ You";
|
||||||
|
|
||||||
|
final Trigger loseTrigger = TriggerHandler.parseTrigger(loseTrig, goalCard, true);
|
||||||
|
|
||||||
|
loseTrigger.setOverridingAbility(AbilityFactory.getAbility(loseEff, goalCard));
|
||||||
|
goalCard.addTrigger(loseTrigger);
|
||||||
|
}
|
||||||
|
human.getZone(ZoneType.Command).add(goalCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applyGameOnThread(final Game game) {
|
||||||
|
super.applyGameOnThread(game);
|
||||||
|
addGoalEnforcement(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType() {
|
||||||
|
return "Puzzle";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getImageKey(boolean altState) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() { return name; }
|
||||||
|
}
|
||||||
50
forge-gui/src/main/java/forge/puzzle/PuzzleIO.java
Normal file
50
forge-gui/src/main/java/forge/puzzle/PuzzleIO.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package forge.puzzle;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import forge.properties.ForgeConstants;
|
||||||
|
import forge.util.FileSection;
|
||||||
|
import forge.util.FileUtil;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class PuzzleIO {
|
||||||
|
|
||||||
|
public static final String TXF_PROMPT = "[New Puzzle]";
|
||||||
|
public static final String SUFFIX_DATA = ".pzl";
|
||||||
|
|
||||||
|
public static ArrayList<Puzzle> loadPuzzles() {
|
||||||
|
String[] pList;
|
||||||
|
// get list of custom draft files
|
||||||
|
final File pFolder = new File(ForgeConstants.PUZZLE_DIR);
|
||||||
|
if (!pFolder.exists()) {
|
||||||
|
throw new RuntimeException("Puzzles : folder not found -- folder is " + pFolder.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pFolder.isDirectory()) {
|
||||||
|
throw new RuntimeException("Puzzles : not a folder -- " + pFolder.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
pList = pFolder.list();
|
||||||
|
|
||||||
|
ArrayList<Puzzle> puzzles = Lists.newArrayList();
|
||||||
|
for (final String element : pList) {
|
||||||
|
if (element.endsWith(SUFFIX_DATA)) {
|
||||||
|
final List<String> pfData = FileUtil.readFile(ForgeConstants.PUZZLE_DIR + element);
|
||||||
|
puzzles.add(new Puzzle(parsePuzzleSections(pfData)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return puzzles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Map<String, List<String>> parsePuzzleSections(List<String> pfData) {
|
||||||
|
return FileSection.parseSections(pfData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static File getPuzzleFile(final String name) {
|
||||||
|
return new File(ForgeConstants.PUZZLE_DIR, name + SUFFIX_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user