mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
- Initial checkin for Tournaments with a (currently disabled) flag to make use of it in Quest Draft
This commit is contained in:
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -19076,6 +19076,16 @@ forge-gui/src/main/java/forge/sound/MusicPlaylist.java -text
|
||||
forge-gui/src/main/java/forge/sound/NoSoundClip.java -text
|
||||
forge-gui/src/main/java/forge/sound/SoundEffectType.java -text
|
||||
forge-gui/src/main/java/forge/sound/SoundSystem.java -text
|
||||
forge-gui/src/main/java/forge/tournament/TournamentData.java -text
|
||||
forge-gui/src/main/java/forge/tournament/TournamentIO.java -text
|
||||
forge-gui/src/main/java/forge/tournament/TournamentUtil.java -text
|
||||
forge-gui/src/main/java/forge/tournament/TournamentWinLoseController.java -text
|
||||
forge-gui/src/main/java/forge/tournament/system/AbstractTournament.java -text
|
||||
forge-gui/src/main/java/forge/tournament/system/TournamentBracket.java -text
|
||||
forge-gui/src/main/java/forge/tournament/system/TournamentPairing.java -text
|
||||
forge-gui/src/main/java/forge/tournament/system/TournamentPlayer.java -text
|
||||
forge-gui/src/main/java/forge/tournament/system/TournamentRoundRobin.java -text
|
||||
forge-gui/src/main/java/forge/tournament/system/TournamentSwiss.java -text
|
||||
forge-gui/src/main/java/forge/util/Callback.java -text
|
||||
forge-gui/src/main/java/forge/util/Evaluator.java -text
|
||||
forge-gui/src/main/java/forge/util/GuiDisplayUtil.java -text
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.screens.home.quest;
|
||||
|
||||
import com.beust.jcommander.internal.Lists;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import forge.GuiBase;
|
||||
import forge.Singletons;
|
||||
@@ -36,6 +37,9 @@ import forge.toolbox.FOptionPane;
|
||||
import forge.toolbox.FSkin;
|
||||
import forge.toolbox.FSkin.SkinImage;
|
||||
import forge.toolbox.JXButtonPanel;
|
||||
import forge.tournament.system.TournamentBracket;
|
||||
import forge.tournament.system.TournamentPairing;
|
||||
import forge.tournament.system.TournamentPlayer;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.*;
|
||||
@@ -438,13 +442,17 @@ public enum CSubmenuQuestDraft implements ICDoc {
|
||||
}
|
||||
|
||||
private void updateTournamentActive() {
|
||||
|
||||
final VSubmenuQuestDraft view = VSubmenuQuestDraft.SINGLETON_INSTANCE;
|
||||
|
||||
if (FModel.getQuest().getAchievements().getCurrentDraft() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (QuestDraftUtils.TOURNAMENT_TOGGLE) {
|
||||
updateTournamentActiveForBracket();
|
||||
return;
|
||||
}
|
||||
|
||||
final VSubmenuQuestDraft view = VSubmenuQuestDraft.SINGLETON_INSTANCE;
|
||||
|
||||
for (int i = 0; i < 15; i++) {
|
||||
|
||||
String playerID = FModel.getQuest().getAchievements().getCurrentDraft().getStandings()[i];
|
||||
@@ -493,6 +501,82 @@ public enum CSubmenuQuestDraft implements ICDoc {
|
||||
|
||||
}
|
||||
|
||||
private void updateTournamentBoxLabel(String playerID, int iconID, int box, boolean first) {
|
||||
final VSubmenuQuestDraft view = VSubmenuQuestDraft.SINGLETON_INSTANCE;
|
||||
|
||||
SkinImage icon = FSkin.getAvatars().get(iconID);
|
||||
|
||||
if (icon == null) {
|
||||
icon = FSkin.getAvatars().get(0);
|
||||
}
|
||||
|
||||
if (first) {
|
||||
view.getLblsMatchups()[box].setPlayerOne(playerID, icon);
|
||||
} else {
|
||||
view.getLblsMatchups()[box].setPlayerTwo(playerID, icon);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTournamentActiveForBracket() {
|
||||
final VSubmenuQuestDraft view = VSubmenuQuestDraft.SINGLETON_INSTANCE;
|
||||
|
||||
QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
|
||||
TournamentBracket bracket = draft.getBracket();
|
||||
|
||||
if (bracket == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Combine finished pairings with active round pairings
|
||||
List<TournamentPairing> allPairings = Lists.newArrayList();
|
||||
allPairings.addAll(bracket.getCompletedPairings());
|
||||
allPairings.addAll(bracket.getActivePairings());
|
||||
|
||||
int count = 0;
|
||||
int playerCount = 0;
|
||||
int lastWinner = 0;
|
||||
for(TournamentPairing tp : allPairings) {
|
||||
boolean first = true;
|
||||
String playerID = "Undetermined";
|
||||
int iconID = 0;
|
||||
for(TournamentPlayer player : tp.getPairedPlayers()) {
|
||||
if (player.getIndex() == -1) {
|
||||
playerID = FModel.getPreferences().getPref(FPref.PLAYER_NAME);
|
||||
if (FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",").length > 0) {
|
||||
iconID = Integer.parseInt(FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",")[0]);
|
||||
}
|
||||
}
|
||||
else{
|
||||
playerID = player.getPlayer().getName();
|
||||
iconID = player.getIndex();
|
||||
}
|
||||
updateTournamentBoxLabel(playerID, iconID, count, first);
|
||||
|
||||
if (tp.getWinner() != null && tp.getWinner().equals(player)) {
|
||||
// Temporarily fill in winner box
|
||||
lastWinner = playerCount;
|
||||
updateTournamentBoxLabel(player.getPlayer().getName(), player.getIndex(), playerCount/4 + 4, count%2 == 0);
|
||||
}
|
||||
first = false;
|
||||
playerCount++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (!bracket.isTournamentOver()) {
|
||||
for (int i = lastWinner/2+9 ; i < 15; i++) {
|
||||
String playerID = "Undetermined";
|
||||
int iconID = GuiBase.getInterface().getAvatarCount() - 1;
|
||||
updateTournamentBoxLabel(playerID, iconID, i / 2, i%2 == 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (draft.playerHasMatchesLeft()) {
|
||||
view.getBtnLeaveTournament().setText("Leave Tournament");
|
||||
} else {
|
||||
view.getBtnLeaveTournament().setText("Collect Prizes");
|
||||
}
|
||||
}
|
||||
|
||||
public void setCompletedDraft(final DeckGroup finishedDraft) {
|
||||
|
||||
QuestDraftUtils.completeDraft(finishedDraft);
|
||||
@@ -587,7 +671,6 @@ public enum CSubmenuQuestDraft implements ICDoc {
|
||||
|
||||
gui = GuiBase.getInterface().getNewGuiGame();
|
||||
QuestDraftUtils.startNextMatch(gui);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ public class QuestDraftWinLose extends ControlWinLose {
|
||||
final String winner = lastGame.getWinningPlayerName();
|
||||
quest.getAchievements().getCurrentDraft().setWinner(winner);
|
||||
quest.save();
|
||||
VSubmenuQuestDraft.SINGLETON_INSTANCE.populate();
|
||||
}
|
||||
|
||||
if (!gameHadHumanPlayer) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.quest;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.GuiBase;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckGroup;
|
||||
@@ -12,6 +13,9 @@ import forge.match.HostedMatch;
|
||||
import forge.model.FModel;
|
||||
import forge.player.GamePlayerUtil;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.tournament.system.TournamentBracket;
|
||||
import forge.tournament.system.TournamentPairing;
|
||||
import forge.tournament.system.TournamentPlayer;
|
||||
import forge.util.storage.IStorage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -19,6 +23,7 @@ import java.util.List;
|
||||
|
||||
public class QuestDraftUtils {
|
||||
private static final List<DraftMatchup> matchups = new ArrayList<>();
|
||||
public static boolean TOURNAMENT_TOGGLE = false;
|
||||
|
||||
public static boolean matchInProgress = false;
|
||||
private static boolean waitForUserInput = false;
|
||||
@@ -42,11 +47,11 @@ public class QuestDraftUtils {
|
||||
}
|
||||
|
||||
public static String getDeckLegality() {
|
||||
final String message = GameType.QuestDraft.getDeckFormat().getDeckConformanceProblem(FModel.getQuest().getAssets().getDraftDeckStorage().get(QuestEventDraft.DECK_NAME).getHumanDeck());
|
||||
if (message != null && FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
||||
return message;
|
||||
if (!FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
||||
return GameType.QuestDraft.getDeckFormat().getDeckConformanceProblem(FModel.getQuest().getAssets().getDraftDeckStorage().get(QuestEventDraft.DECK_NAME).getHumanDeck());
|
||||
}
|
||||
|
||||
private static int getPreviousMatchup(final int position) {
|
||||
@@ -81,11 +86,35 @@ public class QuestDraftUtils {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void startNextMatchInTournament(final IGuiGame gui) {
|
||||
final QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
|
||||
TournamentBracket bracket = draft.getBracket();
|
||||
|
||||
TournamentPairing pairing = bracket.getNextPairing();
|
||||
if (pairing == null) {
|
||||
if (bracket.isTournamentOver()) {
|
||||
// Somehow tournament didn't end on its own?
|
||||
} else {
|
||||
// Generate next round and regrab the next pairing
|
||||
bracket.generateActivePairings();
|
||||
pairing = bracket.getNextPairing();
|
||||
}
|
||||
}
|
||||
|
||||
updateFromTournament(gui);
|
||||
}
|
||||
|
||||
public static void startNextMatch(final IGuiGame gui) {
|
||||
if (TOURNAMENT_TOGGLE) {
|
||||
startNextMatchInTournament(gui);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!matchups.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If matchups isn't empty I don't get here. Right?
|
||||
matchups.clear();
|
||||
|
||||
final QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
|
||||
@@ -130,29 +159,19 @@ public class QuestDraftUtils {
|
||||
|
||||
//If no previous matches need doing, start the next round as normal
|
||||
if (!foundMatchups) {
|
||||
// Fall through to avoid repetition
|
||||
switch (currentSet) {
|
||||
case 7:
|
||||
addMatchup(0, 1, draft);
|
||||
addMatchup(2, 3, draft);
|
||||
addMatchup(4, 5, draft);
|
||||
addMatchup(6, 7, draft);
|
||||
break;
|
||||
case 8:
|
||||
addMatchup(2, 3, draft);
|
||||
addMatchup(4, 5, draft);
|
||||
addMatchup(6, 7, draft);
|
||||
break;
|
||||
case 9:
|
||||
addMatchup(4, 5, draft);
|
||||
addMatchup(6, 7, draft);
|
||||
break;
|
||||
case 10:
|
||||
addMatchup(6, 7, draft);
|
||||
break;
|
||||
case 11:
|
||||
addMatchup(8, 9, draft);
|
||||
addMatchup(10, 11, draft);
|
||||
break;
|
||||
case 12:
|
||||
addMatchup(10, 11, draft);
|
||||
break;
|
||||
@@ -205,7 +224,22 @@ public class QuestDraftUtils {
|
||||
matchups.add(matchup);
|
||||
}
|
||||
|
||||
private static GameRules createQuestDraftRuleset() {
|
||||
final GameRules rules = new GameRules(GameType.QuestDraft);
|
||||
rules.setPlayForAnte(false);
|
||||
rules.setMatchAnteRarity(false);
|
||||
rules.setGamesPerMatch(3);
|
||||
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
|
||||
rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE));
|
||||
return rules;
|
||||
}
|
||||
|
||||
public static void update(final IGuiGame gui) {
|
||||
if (TOURNAMENT_TOGGLE) {
|
||||
updateFromTournament(gui);
|
||||
return;
|
||||
}
|
||||
|
||||
if (matchups.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -226,17 +260,71 @@ public class QuestDraftUtils {
|
||||
waitForUserInput = false;
|
||||
}
|
||||
|
||||
final GameRules rules = new GameRules(GameType.QuestDraft);
|
||||
rules.setPlayForAnte(false);
|
||||
rules.setMatchAnteRarity(false);
|
||||
rules.setGamesPerMatch(3);
|
||||
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
|
||||
rules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE));
|
||||
GameRules rules = createQuestDraftRuleset();
|
||||
|
||||
final HostedMatch newMatch = GuiBase.getInterface().hostMatch();
|
||||
newMatch.startMatch(rules, null, nextMatch.matchStarter, nextMatch.humanPlayer, GuiBase.getInterface().getNewGuiGame());
|
||||
}
|
||||
|
||||
private static List<RegisteredPlayer> registerTournamentPlayers(TournamentPairing pairing, QuestEventDraft draft, DeckGroup decks) {
|
||||
List<RegisteredPlayer> registered = Lists.newArrayList();
|
||||
for (TournamentPlayer pl : pairing.getPairedPlayers()) {
|
||||
if (pl.getIndex() == -1) {
|
||||
registered.add(new RegisteredPlayer(decks.getHumanDeck()).setPlayer(pl.getPlayer()));
|
||||
} else {
|
||||
registered.add(new RegisteredPlayer(decks.getAiDecks().get(pl.getIndex())).setPlayer(pl.getPlayer()));
|
||||
}
|
||||
}
|
||||
return registered;
|
||||
}
|
||||
|
||||
private static void updateFromTournament(final IGuiGame gui) {
|
||||
// If Human involved launch into a UI, if not show a "Simulating" screen. And simulate the game off-thread
|
||||
if (waitForUserInput || matchInProgress || gui == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
gui.enableOverlay();
|
||||
final QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
|
||||
if (draft == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TournamentBracket bracket = draft.getBracket();
|
||||
TournamentPairing pairing = bracket.getNextPairing();
|
||||
|
||||
if (pairing == null) {
|
||||
// bracket.isTournamentOver()
|
||||
|
||||
gui.disableOverlay();
|
||||
return;
|
||||
}
|
||||
|
||||
matchInProgress = true;
|
||||
final DeckGroup decks = FModel.getQuest().getAssets().getDraftDeckStorage().get(QuestEventDraft.DECK_NAME);
|
||||
|
||||
boolean waitForUserInput = pairing.hasPlayer(GamePlayerUtil.getGuiPlayer());
|
||||
GameRules rules = createQuestDraftRuleset();
|
||||
|
||||
List<RegisteredPlayer> registered = registerTournamentPlayers(pairing, draft, decks);
|
||||
RegisteredPlayer registeredHuman = null;
|
||||
|
||||
if (waitForUserInput) {
|
||||
final HostedMatch newMatch = GuiBase.getInterface().hostMatch();
|
||||
for(RegisteredPlayer rp : registered) {
|
||||
if (rp.getPlayer().equals(GamePlayerUtil.getGuiPlayer())) {
|
||||
registeredHuman = rp;
|
||||
}
|
||||
}
|
||||
newMatch.startMatch(rules, null, registered, registeredHuman, GuiBase.getInterface().getNewGuiGame());
|
||||
} else {
|
||||
// TODO Show a "Simulating Dialog" and simulate off-thread. Temporary replication of code for now
|
||||
gui.disableOverlay();
|
||||
final HostedMatch newMatch = GuiBase.getInterface().hostMatch();
|
||||
newMatch.startMatch(rules, null, registered, registeredHuman, GuiBase.getInterface().getNewGuiGame());
|
||||
}
|
||||
}
|
||||
|
||||
public static void continueMatches(final IGuiGame gui) {
|
||||
waitForUserInput = false;
|
||||
update(gui);
|
||||
@@ -246,6 +334,11 @@ public class QuestDraftUtils {
|
||||
matchInProgress = false;
|
||||
waitForUserInput = false;
|
||||
matchups.clear();
|
||||
if (TOURNAMENT_TOGGLE) {
|
||||
final QuestEventDraft draft = FModel.getQuest().getAchievements().getCurrentDraft();
|
||||
TournamentBracket bracket = draft.getBracket();
|
||||
bracket.endTournament();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DraftMatchup {
|
||||
|
||||
@@ -33,6 +33,9 @@ import forge.model.FModel;
|
||||
import forge.player.GamePlayerUtil;
|
||||
import forge.quest.data.QuestPreferences.QPref;
|
||||
import forge.quest.io.ReadPriceList;
|
||||
import forge.tournament.system.TournamentBracket;
|
||||
import forge.tournament.system.TournamentPairing;
|
||||
import forge.tournament.system.TournamentPlayer;
|
||||
import forge.util.NameGenerator;
|
||||
import forge.util.storage.IStorage;
|
||||
|
||||
@@ -79,6 +82,8 @@ public class QuestEventDraft {
|
||||
public static final String UNDETERMINED = "quest_draft_undetermined_place";
|
||||
public static final String HUMAN = "quest_draft_human_place";
|
||||
public static final String DECK_NAME = "Tournament Deck";
|
||||
public static final int TOTAL_ROUNDS = 3;
|
||||
public static final int PLAYERS_IN_PAIRING = 2;
|
||||
|
||||
private static transient final ReadPriceList PRICE_LIST_READER = new ReadPriceList();
|
||||
private static transient final Map<String, Integer> MAP_PRICES = PRICE_LIST_READER.getPriceList();
|
||||
@@ -90,6 +95,8 @@ public class QuestEventDraft {
|
||||
private int entryFee = 0;
|
||||
|
||||
private String[] standings = new String[15];
|
||||
private TournamentBracket bracket;
|
||||
|
||||
private String[] aiNames = new String[7];
|
||||
|
||||
private int[] aiIcons = new int[7];
|
||||
@@ -169,6 +176,10 @@ public class QuestEventDraft {
|
||||
age--;
|
||||
}
|
||||
|
||||
public TournamentBracket getBracket() { return bracket; }
|
||||
|
||||
public void setBracket(TournamentBracket brckt) { bracket = brckt; }
|
||||
|
||||
public void saveToRegularDraft() {
|
||||
final String tournamentName = FModel.getQuest().getName() + " Tournament Deck " + new SimpleDateFormat("EEE d MMM yyyy HH-mm-ss").format(new Date());
|
||||
final DeckGroup original = FModel.getQuest().getDraftDecks().get(QuestEventDraft.DECK_NAME);
|
||||
@@ -208,6 +219,18 @@ public class QuestEventDraft {
|
||||
|
||||
public void setWinner(final String playerName) {
|
||||
|
||||
if (QuestDraftUtils.TOURNAMENT_TOGGLE) {
|
||||
TournamentPairing pairing = bracket.getNextPairing();
|
||||
for(TournamentPlayer player : pairing.getPairedPlayers()) {
|
||||
if (player.getPlayer().getName().equals(playerName)) {
|
||||
pairing.setWinner(player);
|
||||
bracket.reportMatchCompletion(pairing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Since this updates the brackets that we're still pseudo-using let it fall through
|
||||
}
|
||||
|
||||
boolean isHumanPlayer = true;
|
||||
|
||||
for (final String name : aiNames) {
|
||||
@@ -245,37 +268,7 @@ public class QuestEventDraft {
|
||||
|
||||
}
|
||||
|
||||
switch (playerIndex) {
|
||||
case 0:
|
||||
case 1:
|
||||
standings[8] = standings[playerIndex];
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
standings[9] = standings[playerIndex];
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
standings[10] = standings[playerIndex];
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
standings[11] = standings[playerIndex];
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
standings[12] = standings[playerIndex];
|
||||
break;
|
||||
case 10:
|
||||
case 11:
|
||||
standings[13] = standings[playerIndex];
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
standings[14] = standings[playerIndex];
|
||||
break;
|
||||
}
|
||||
|
||||
standings[playerIndex/2+8] = standings[playerIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -529,6 +522,10 @@ public class QuestEventDraft {
|
||||
|
||||
public boolean playerHasMatchesLeft() {
|
||||
|
||||
if (QuestDraftUtils.TOURNAMENT_TOGGLE) {
|
||||
return !bracket.isTournamentOver() && bracket.isPlayerRemaining(-1);
|
||||
}
|
||||
|
||||
int playerIndex = -1;
|
||||
for (int i = standings.length - 1; i >= 0; i--) {
|
||||
if (standings[i].equals(HUMAN)) {
|
||||
@@ -581,6 +578,13 @@ public class QuestEventDraft {
|
||||
}
|
||||
|
||||
public int getPlayerPlacement() {
|
||||
// "1st Place" and "2nd Place" are accurate
|
||||
// "3rd Place" is the two remaining players who won in the first round
|
||||
// "4th Place" is anyone who lost in the first round
|
||||
|
||||
if (QuestDraftUtils.TOURNAMENT_TOGGLE) {
|
||||
return 5 - bracket.getFurthestRound(-1);
|
||||
}
|
||||
|
||||
int playerIndex = -1;
|
||||
for (int i = standings.length - 1; i >= 0; i--) {
|
||||
@@ -896,6 +900,7 @@ public class QuestEventDraft {
|
||||
|
||||
Collections.shuffle(players);
|
||||
|
||||
// Initialize tournament
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
event.standings[i] = players.get(i);
|
||||
}
|
||||
@@ -930,6 +935,8 @@ public class QuestEventDraft {
|
||||
|
||||
}
|
||||
|
||||
event.bracket = createBracketFromStandings(event.standings, event.aiNames, event.aiIcons);
|
||||
|
||||
return event;
|
||||
|
||||
}
|
||||
@@ -1040,4 +1047,49 @@ public class QuestEventDraft {
|
||||
|
||||
}
|
||||
|
||||
public static TournamentBracket createBracketFromStandings(String[] standings, String[] aiNames, int[] aiIcons) {
|
||||
TournamentBracket bracket = new TournamentBracket(TOTAL_ROUNDS, PLAYERS_IN_PAIRING);
|
||||
bracket.setContinualPairing(false);
|
||||
|
||||
int roundParticipants = (int)(Math.pow(PLAYERS_IN_PAIRING, TOTAL_ROUNDS));
|
||||
int i;
|
||||
|
||||
// Initialize Players
|
||||
for(i = 0; i < roundParticipants; i++) {
|
||||
if (standings[i].equals(HUMAN)) {
|
||||
bracket.addTournamentPlayer(GamePlayerUtil.getGuiPlayer());
|
||||
} else {
|
||||
int idx = Integer.valueOf(standings[i]) - 1;
|
||||
bracket.addTournamentPlayer(GamePlayerUtil.createAiPlayer(aiNames[idx], aiIcons[idx]), idx);
|
||||
}
|
||||
}
|
||||
|
||||
bracket.setInitialized(true);
|
||||
bracket.generateActivePairings();
|
||||
while(i < 14) {
|
||||
TournamentPairing pairing = bracket.getNextPairing();
|
||||
if (pairing == null) {
|
||||
// No pairings available. Generate next round and continue
|
||||
bracket.generateActivePairings();
|
||||
pairing = bracket.getNextPairing();
|
||||
// Pairing really shouldn't be null at this point
|
||||
}
|
||||
if (standings[i].equals(UNDETERMINED)) {
|
||||
// Bracket now up to date!
|
||||
break;
|
||||
} else {
|
||||
int idx = standings[i].equals(HUMAN) ? -1 : Integer.valueOf(standings[i]) - 1;
|
||||
pairing.setWinnerByIndex(idx);
|
||||
bracket.reportMatchCompletion(pairing);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i == 14) {
|
||||
// Tournament has ended! Do I have to do anything?
|
||||
System.out.println("Tournament done...");
|
||||
}
|
||||
|
||||
return bracket;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +284,14 @@ public class QuestAchievements {
|
||||
FModel.getQuest().getDraftDecks().delete(QuestEventDraft.DECK_NAME);
|
||||
return null;
|
||||
}
|
||||
return drafts.get(currentDraft);
|
||||
|
||||
try {
|
||||
return drafts.get(currentDraft);
|
||||
}
|
||||
catch(ArrayIndexOutOfBoundsException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int getCurrentDraftIndex() {
|
||||
|
||||
@@ -198,6 +198,10 @@ public class QuestDataIO {
|
||||
QuestDataIO.setFinalField(QuestData.class, "matchLength", newData, 3);
|
||||
}
|
||||
|
||||
if (saveVersion < 11) {
|
||||
// Migrate DraftTournaments to use new Tournament class
|
||||
}
|
||||
|
||||
final QuestAssets qS = newData.getAssets();
|
||||
final QuestAchievements qA = newData.getAchievements();
|
||||
|
||||
@@ -499,7 +503,6 @@ public class QuestDataIO {
|
||||
QuestEventDraftContainer drafts = (QuestEventDraftContainer) source;
|
||||
|
||||
for (QuestEventDraft draft : drafts) {
|
||||
|
||||
writer.startNode("draft");
|
||||
|
||||
writer.startNode("title");
|
||||
@@ -534,6 +537,11 @@ public class QuestDataIO {
|
||||
}
|
||||
writer.endNode();
|
||||
|
||||
// TODO Save bracket instead of standings
|
||||
//writer.startNode("bracket");
|
||||
//draft.getBracket().exportToXML(writer);
|
||||
//writer.endNode();
|
||||
|
||||
writer.startNode("aiNames");
|
||||
i = 0;
|
||||
for (String name : draft.getAINames()) {
|
||||
@@ -575,13 +583,13 @@ public class QuestDataIO {
|
||||
while (reader.hasMoreChildren()) {
|
||||
|
||||
reader.moveDown();
|
||||
|
||||
// TODO Add Tournament
|
||||
String draftName = null;
|
||||
String boosterConfiguration = null;
|
||||
int entryFee = 1500;
|
||||
int age = 15;
|
||||
String block = null;
|
||||
String[] standings = new String[15];
|
||||
String[] standings = null;
|
||||
String[] aiNames = new String[7];
|
||||
int[] aiIcons = new int[7];
|
||||
boolean started = false;
|
||||
@@ -604,6 +612,8 @@ public class QuestDataIO {
|
||||
block = reader.getValue();
|
||||
break;
|
||||
case "standings":
|
||||
// TODO Leaving for older quest datas, but will convert immediately to bracket
|
||||
standings = new String[15];
|
||||
int i = 0;
|
||||
while (reader.hasMoreChildren()) {
|
||||
reader.moveDown();
|
||||
@@ -611,6 +621,9 @@ public class QuestDataIO {
|
||||
reader.moveUp();
|
||||
}
|
||||
break;
|
||||
case "bracket":
|
||||
// TODO For newer quest datas, that store brackets
|
||||
break;
|
||||
case "aiNames":
|
||||
int ii = 0;
|
||||
while (reader.hasMoreChildren()) {
|
||||
@@ -643,7 +656,11 @@ public class QuestDataIO {
|
||||
draft.setBoosterConfiguration(boosterConfiguration);
|
||||
draft.setEntryFee(entryFee);
|
||||
draft.setBlock(block);
|
||||
// TODO Stop setting standings once Bracket is loading from IO
|
||||
draft.setStandings(standings);
|
||||
if (standings != null) {
|
||||
draft.setBracket(QuestEventDraft.createBracketFromStandings(standings, aiNames, aiIcons));
|
||||
}
|
||||
draft.setAINames(aiNames);
|
||||
draft.setAIIcons(aiIcons);
|
||||
draft.setStarted(started);
|
||||
|
||||
148
forge-gui/src/main/java/forge/tournament/TournamentData.java
Normal file
148
forge-gui/src/main/java/forge/tournament/TournamentData.java
Normal file
@@ -0,0 +1,148 @@
|
||||
package forge.tournament;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamOmitField;
|
||||
import forge.GuiBase;
|
||||
import forge.deck.Deck;
|
||||
import forge.game.GameType;
|
||||
import forge.game.player.RegisteredPlayer;
|
||||
import forge.match.HostedMatch;
|
||||
import forge.properties.ForgeConstants;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class TournamentData {
|
||||
@XStreamOmitField
|
||||
private String name; // set based on the the filename on load
|
||||
|
||||
private transient HostedMatch hostedMatch = null;
|
||||
|
||||
private int completed;
|
||||
private String timestamp;
|
||||
private List<String> eventRecords = new ArrayList<String>();
|
||||
private List<String> eventNames = new ArrayList<String>();
|
||||
private Deck userDeck;
|
||||
private List<Deck> decks;
|
||||
|
||||
public TournamentData() {
|
||||
}
|
||||
|
||||
public void setName(String name0) {
|
||||
name = name0;
|
||||
}
|
||||
|
||||
public void rename(final String newName) {
|
||||
File newpath = new File(ForgeConstants.GAUNTLET_DIR.userPrefLoc, newName + ".dat");
|
||||
File oldpath = new File(ForgeConstants.GAUNTLET_DIR.userPrefLoc, name + ".dat");
|
||||
oldpath.renameTo(newpath);
|
||||
|
||||
name = newName;
|
||||
TournamentIO.saveTournament(this);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
if (name.startsWith(TournamentIO.PREFIX_LOCKED)) { //trim locked prefix if needed
|
||||
return name.substring(TournamentIO.PREFIX_LOCKED.length());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public void stamp() {
|
||||
final DateFormat dateFormat = new SimpleDateFormat("MM-dd-yy, H:m");
|
||||
timestamp = dateFormat.format(new Date()).toString();
|
||||
}
|
||||
|
||||
/** Resets a Tournament data to an unplayed state, then stamps and saves. */
|
||||
public void reset() {
|
||||
completed = 0;
|
||||
stamp();
|
||||
eventRecords.clear();
|
||||
|
||||
for (int i = 0; i < decks.size(); i++) {
|
||||
eventRecords.add("");
|
||||
}
|
||||
|
||||
TournamentIO.saveTournament(this);
|
||||
}
|
||||
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setCompleted(final int i0) {
|
||||
completed = i0;
|
||||
}
|
||||
|
||||
public int getCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
public void setUserDeck(final Deck d0) {
|
||||
userDeck = d0;
|
||||
}
|
||||
|
||||
public Deck getUserDeck() {
|
||||
return userDeck;
|
||||
}
|
||||
|
||||
public List<String> getDeckNames() {
|
||||
final List<String> names = new ArrayList<String>();
|
||||
for (final Deck d : decks) { names.add(d.getName()); }
|
||||
return names;
|
||||
}
|
||||
|
||||
public void setEventRecords(final List<String> records0) {
|
||||
eventRecords = records0;
|
||||
}
|
||||
|
||||
public List<String> getEventRecords() {
|
||||
return eventRecords;
|
||||
}
|
||||
|
||||
public void setEventNames(final List<String> names0) {
|
||||
eventNames = names0;
|
||||
}
|
||||
|
||||
public List<String> getEventNames() {
|
||||
return eventNames;
|
||||
}
|
||||
|
||||
public void setDecks(final List<Deck> decks0) {
|
||||
decks = decks0;
|
||||
}
|
||||
|
||||
public List<Deck> getDecks() {
|
||||
return decks;
|
||||
}
|
||||
|
||||
public void startRound(final List<RegisteredPlayer> players, final RegisteredPlayer human) {
|
||||
hostedMatch = GuiBase.getInterface().hostMatch();
|
||||
hostedMatch.startMatch(GameType.Tournament, null, players, human, GuiBase.getInterface().getNewGuiGame());
|
||||
}
|
||||
|
||||
public void nextRound(final List<RegisteredPlayer> players, final RegisteredPlayer human) {
|
||||
if (hostedMatch == null) {
|
||||
throw new IllegalStateException("Cannot advance round when no match has been hosted.");
|
||||
}
|
||||
|
||||
hostedMatch.endCurrentGame();
|
||||
startRound(players, human);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String str = getDisplayName();
|
||||
if (decks != null) {
|
||||
str += " (" + decks.size() + " opponents)";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
190
forge-gui/src/main/java/forge/tournament/TournamentIO.java
Normal file
190
forge-gui/src/main/java/forge/tournament/TournamentIO.java
Normal file
@@ -0,0 +1,190 @@
|
||||
package forge.tournament;
|
||||
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
import com.thoughtworks.xstream.converters.Converter;
|
||||
import com.thoughtworks.xstream.converters.MarshallingContext;
|
||||
import com.thoughtworks.xstream.converters.UnmarshallingContext;
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
|
||||
import forge.deck.CardPool;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.util.IgnoringXStream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class TournamentIO {
|
||||
/** Prompt in text field for new (unsaved) built gauntlets. */
|
||||
public static final String TXF_PROMPT = "[New Tournament]";
|
||||
/** suffix for all gauntlet data files */
|
||||
public static final String SUFFIX_DATA = ".dat";
|
||||
/** Prefix for quick tournament save files. */
|
||||
public static final String PREFIX_QUICK = "Quick_";
|
||||
/** Prefix for custom tournament save files. */
|
||||
public static final String PREFIX_CUSTOM = "Custom_";
|
||||
/** Regex for locked tournament save files. */
|
||||
public static final String PREFIX_LOCKED = "LOCKED_";
|
||||
|
||||
protected static XStream getSerializer(final boolean isIgnoring) {
|
||||
final XStream xStream = isIgnoring ? new IgnoringXStream() : new XStream();
|
||||
xStream.registerConverter(new DeckSectionToXml());
|
||||
xStream.autodetectAnnotations(true);
|
||||
return xStream;
|
||||
}
|
||||
|
||||
public static File getTournamentFile(final String name) {
|
||||
return new File(ForgeConstants.TOURNAMENT_DIR.userPrefLoc, name + SUFFIX_DATA);
|
||||
}
|
||||
|
||||
public static File getTournamentFile(final TournamentData gd) {
|
||||
return getTournamentFile(gd.getName());
|
||||
}
|
||||
|
||||
public static File[] getTournamentFilesUnlocked(final String prefix) {
|
||||
final FilenameFilter filter = new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(final File dir, final String name) {
|
||||
return ((prefix == null || name.startsWith(prefix)) && name.endsWith(SUFFIX_DATA));
|
||||
}
|
||||
};
|
||||
|
||||
final File folder = new File(ForgeConstants.TOURNAMENT_DIR.userPrefLoc);
|
||||
return folder.listFiles(filter);
|
||||
}
|
||||
|
||||
public static File[] getTournamentFilesLocked() {
|
||||
final FilenameFilter filter = new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(final File dir, final String name) {
|
||||
return (name.startsWith(PREFIX_LOCKED) && name.endsWith(SUFFIX_DATA));
|
||||
}
|
||||
};
|
||||
|
||||
final File folder = new File(ForgeConstants.TOURNAMENT_DIR.defaultLoc);
|
||||
return folder.listFiles(filter);
|
||||
}
|
||||
|
||||
public static TournamentData loadTournament(final File xmlSaveFile) {
|
||||
GZIPInputStream zin = null;
|
||||
boolean isCorrupt = false;
|
||||
try {
|
||||
zin = new GZIPInputStream(new FileInputStream(xmlSaveFile));
|
||||
final InputStreamReader reader = new InputStreamReader(zin);
|
||||
|
||||
final TournamentData data = (TournamentData)TournamentIO.getSerializer(true).fromXML(reader);
|
||||
|
||||
final String filename = xmlSaveFile.getName();
|
||||
data.setName(filename.substring(0, filename.length() - SUFFIX_DATA.length()));
|
||||
return data;
|
||||
}
|
||||
catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (final Exception e) { //if there's a non-IO exception, delete the corrupt file
|
||||
e.printStackTrace();
|
||||
isCorrupt = true;
|
||||
}
|
||||
finally {
|
||||
if (zin != null) {
|
||||
try {
|
||||
zin.close();
|
||||
} catch (final IOException e) {
|
||||
System.out.println("error closing tournament data reader: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isCorrupt) {
|
||||
try {
|
||||
xmlSaveFile.delete();
|
||||
} catch (final Exception e) {
|
||||
System.out.println("error delete corrupt tournament file: " + e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void saveTournament(final TournamentData gd0) {
|
||||
try {
|
||||
final XStream xStream = TournamentIO.getSerializer(false);
|
||||
TournamentIO.savePacked(xStream, gd0);
|
||||
} catch (final Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void savePacked(final XStream xStream0, final TournamentData gd0) throws IOException {
|
||||
final BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(getTournamentFile(gd0)));
|
||||
final GZIPOutputStream zout = new GZIPOutputStream(bout);
|
||||
xStream0.toXML(gd0, zout);
|
||||
zout.flush();
|
||||
zout.close();
|
||||
}
|
||||
|
||||
private static class DeckSectionToXml implements Converter {
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean canConvert(final Class clasz) {
|
||||
return clasz.equals(CardPool.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
|
||||
for (final Map.Entry<PaperCard, Integer> e : (CardPool) source) {
|
||||
writeCardPrinted(e.getKey(), e.getValue(), writer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
|
||||
final CardPool result = new CardPool();
|
||||
while (reader.hasMoreChildren()) {
|
||||
reader.moveDown();
|
||||
final String sCnt = reader.getAttribute("n");
|
||||
final int cnt = StringUtils.isNumeric(sCnt) ? Integer.parseInt(sCnt) : 1;
|
||||
final String nodename = reader.getNodeName();
|
||||
|
||||
if ("string".equals(nodename)) {
|
||||
result.add(FModel.getMagicDb().getCommonCards().getCard(reader.getValue()));
|
||||
} else if ("card".equals(nodename)) { // new format
|
||||
result.add(readCardPrinted(reader), cnt);
|
||||
}
|
||||
reader.moveUp();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void writeCardPrinted(final PaperCard cref, final Integer count, final HierarchicalStreamWriter writer) {
|
||||
writer.startNode("card");
|
||||
writer.addAttribute("c", cref.getName());
|
||||
writer.addAttribute("s", cref.getEdition());
|
||||
if (cref.isFoil()) {
|
||||
writer.addAttribute("foil", "1");
|
||||
}
|
||||
writer.addAttribute("i", Integer.toString(cref.getArtIndex()));
|
||||
writer.addAttribute("n", count.toString());
|
||||
writer.endNode();
|
||||
}
|
||||
|
||||
private static PaperCard readCardPrinted(final HierarchicalStreamReader reader) {
|
||||
final String name = reader.getAttribute("c");
|
||||
final String set = reader.getAttribute("s");
|
||||
final String sIndex = reader.getAttribute("i");
|
||||
final short index = StringUtils.isNumeric(sIndex) ? Short.parseShort(sIndex) : 0;
|
||||
final boolean foil = "1".equals(reader.getAttribute("foil"));
|
||||
PaperCard card = FModel.getMagicDb().getCommonCards().getCard(name, set, index);
|
||||
if (null == card) {
|
||||
card = FModel.getMagicDb().getCommonCards().getCard(name, set, -1);
|
||||
}
|
||||
if (null == card) {
|
||||
throw new RuntimeException("Unsupported card found in quest save: " + name + " from edition " + set);
|
||||
}
|
||||
return foil ? FModel.getMagicDb().getCommonCards().getFoiled(card) : card;
|
||||
}
|
||||
}
|
||||
}
|
||||
76
forge-gui/src/main/java/forge/tournament/TournamentUtil.java
Normal file
76
forge-gui/src/main/java/forge/tournament/TournamentUtil.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package forge.tournament;
|
||||
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckType;
|
||||
import forge.deck.DeckgenUtil;
|
||||
import forge.model.FModel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class TournamentUtil {
|
||||
public static TournamentData createQuickTournament(final Deck userDeck, final int numOpponents, final List<DeckType> allowedDeckTypes) {
|
||||
TournamentData tournament = new TournamentData();
|
||||
setDefaultTournamentName(tournament, TournamentIO.PREFIX_QUICK);
|
||||
FModel.setTournamentData(tournament);
|
||||
|
||||
// Generate tournament decks
|
||||
Deck deck;
|
||||
final List<String> eventNames = new ArrayList<String>();
|
||||
final List<Deck> decks = new ArrayList<Deck>();
|
||||
|
||||
for (int i = 0; i < numOpponents; i++) {
|
||||
int randType = (int)Math.floor(Math.random() * allowedDeckTypes.size());
|
||||
switch (allowedDeckTypes.get(randType)) {
|
||||
case COLOR_DECK:
|
||||
deck = DeckgenUtil.getRandomColorDeck(true);
|
||||
eventNames.add("Random colors deck");
|
||||
break;
|
||||
case CUSTOM_DECK:
|
||||
deck = DeckgenUtil.getRandomCustomDeck();
|
||||
eventNames.add(deck.getName());
|
||||
break;
|
||||
case PRECONSTRUCTED_DECK:
|
||||
deck = DeckgenUtil.getRandomPreconDeck();
|
||||
eventNames.add(deck.getName());
|
||||
break;
|
||||
case QUEST_OPPONENT_DECK:
|
||||
deck = DeckgenUtil.getRandomQuestDeck();
|
||||
eventNames.add(deck.getName());
|
||||
break;
|
||||
case THEME_DECK:
|
||||
deck = DeckgenUtil.getRandomThemeDeck();
|
||||
eventNames.add(deck.getName());
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
decks.add(deck);
|
||||
}
|
||||
|
||||
tournament.setDecks(decks);
|
||||
tournament.setEventNames(eventNames);
|
||||
tournament.setUserDeck(userDeck);
|
||||
|
||||
// Reset all variable fields to 0, stamps and saves automatically.
|
||||
tournament.reset();
|
||||
return tournament;
|
||||
}
|
||||
|
||||
public static void setDefaultTournamentName(TournamentData tournament, String prefix) {
|
||||
final File[] arrFiles = TournamentIO.getTournamentFilesUnlocked(prefix);
|
||||
final Set<String> setNames = new HashSet<String>();
|
||||
for (File f : arrFiles) {
|
||||
setNames.add(f.getName());
|
||||
}
|
||||
|
||||
int num = 1;
|
||||
while (setNames.contains(prefix + num + TournamentIO.SUFFIX_DATA)) {
|
||||
num++;
|
||||
}
|
||||
tournament.setName(prefix + num);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package forge.tournament;
|
||||
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import forge.LobbyPlayer;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.deck.Deck;
|
||||
import forge.game.GameView;
|
||||
import forge.game.player.RegisteredPlayer;
|
||||
import forge.interfaces.IButton;
|
||||
import forge.interfaces.IWinLoseView;
|
||||
import forge.model.FModel;
|
||||
import forge.player.GamePlayerUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
abstract public class TournamentWinLoseController {
|
||||
private final IWinLoseView<? extends IButton> view;
|
||||
private final GameView lastGame;
|
||||
|
||||
public TournamentWinLoseController(IWinLoseView<? extends IButton> view0, final GameView game0) {
|
||||
view = view0;
|
||||
lastGame = game0;
|
||||
}
|
||||
|
||||
public void showOutcome() {
|
||||
final TournamentData gd = FModel.getTournamentData();
|
||||
final List<String> lstEventNames = gd.getEventNames();
|
||||
final List<Deck> lstDecks = gd.getDecks();
|
||||
final List<String> lstEventRecords = gd.getEventRecords();
|
||||
final int len = lstDecks.size();
|
||||
final int num = gd.getCompleted();
|
||||
FSkinProp icon = null;
|
||||
String message1 = null;
|
||||
String message2 = null;
|
||||
|
||||
// No restarts.
|
||||
view.getBtnRestart().setVisible(false);
|
||||
|
||||
// Generic event record.
|
||||
lstEventRecords.set(gd.getCompleted(), "Ongoing");
|
||||
|
||||
// Match won't be saved until it is over. This opens up a cheat
|
||||
// or failsafe mechanism (depending on your perspective) in which
|
||||
// the player can restart Forge to replay a match.
|
||||
// Pretty sure this can't be fixed until in-game states can be
|
||||
// saved. Doublestrike 07-10-12
|
||||
LobbyPlayer questPlayer = GamePlayerUtil.getGuiPlayer();
|
||||
|
||||
// In all cases, update stats.
|
||||
lstEventRecords.set(gd.getCompleted(), lastGame.getGamesWonBy(questPlayer) + " - "
|
||||
+ (lastGame.getNumPlayedGamesInMatch() - lastGame.getGamesWonBy(questPlayer) + 1));
|
||||
|
||||
boolean isMatchOver = lastGame.isMatchOver();
|
||||
if (isMatchOver) {
|
||||
gd.setCompleted(gd.getCompleted() + 1);
|
||||
|
||||
// Win match case
|
||||
if (lastGame.isMatchWonBy(questPlayer)) {
|
||||
// Tournament complete: Remove save file
|
||||
if (gd.getCompleted() == lstDecks.size()) {
|
||||
icon = FSkinProp.ICO_QUEST_COIN;
|
||||
message1 = "CONGRATULATIONS!";
|
||||
message2 = "You made it through the tournament!";
|
||||
|
||||
view.getBtnContinue().setVisible(false);
|
||||
view.getBtnQuit().setText("OK");
|
||||
|
||||
// Remove save file if it's a quickie, or just reset it.
|
||||
if (gd.getName().startsWith(TournamentIO.PREFIX_QUICK)) {
|
||||
TournamentIO.getTournamentFile(gd).delete();
|
||||
}
|
||||
else {
|
||||
gd.reset();
|
||||
}
|
||||
}
|
||||
// Or, save and move to next game
|
||||
else {
|
||||
gd.stamp();
|
||||
TournamentIO.saveTournament(gd);
|
||||
|
||||
view.getBtnContinue().setText("Next Round (" + (gd.getCompleted() + 1)
|
||||
+ "/" + len + ")");
|
||||
view.getBtnContinue().setVisible(true);
|
||||
view.getBtnContinue().setEnabled(true);
|
||||
view.getBtnQuit().setText("Save and Quit");
|
||||
}
|
||||
}
|
||||
// Lose match case; stop tournament.
|
||||
else {
|
||||
icon = FSkinProp.ICO_QUEST_HEART;
|
||||
message1 = "DEFEATED!";
|
||||
message2 = "You have failed to pass the tournament.";
|
||||
|
||||
view.getBtnContinue().setVisible(false);
|
||||
|
||||
// Remove save file if it's a quickie, or just reset it.
|
||||
if (gd.getName().startsWith(TournamentIO.PREFIX_QUICK)) {
|
||||
TournamentIO.getTournamentFile(gd).delete();
|
||||
}
|
||||
else {
|
||||
gd.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gd.setEventRecords(lstEventRecords);
|
||||
|
||||
showOutcome(isMatchOver, message1, message2, icon, lstEventNames, lstEventRecords, len, num);
|
||||
}
|
||||
|
||||
public final boolean actionOnContinue() {
|
||||
if (lastGame.isMatchOver()) {
|
||||
// To change the AI deck, we have to create a new match.
|
||||
final TournamentData gd = FModel.getTournamentData();
|
||||
final RegisteredPlayer human = new RegisteredPlayer(gd.getUserDeck()).setPlayer(GamePlayerUtil.getGuiPlayer());
|
||||
final Deck aiDeck = gd.getDecks().get(gd.getCompleted());
|
||||
final List<RegisteredPlayer> players = Lists.newArrayList();
|
||||
players.add(human);
|
||||
players.add(new RegisteredPlayer(aiDeck).setPlayer(GamePlayerUtil.createAiPlayer()));
|
||||
|
||||
view.hide();
|
||||
saveOptions();
|
||||
gd.nextRound(players, human);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract void showOutcome(boolean isMatchOver, String message1, String message2, FSkinProp icon, List<String> lstEventNames, List<String> lstEventRecords, int len, int num);
|
||||
protected abstract void saveOptions();
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package forge.tournament.system;
|
||||
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
|
||||
import forge.LobbyPlayer;
|
||||
import forge.player.GamePlayerUtil;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractTournament implements Serializable {
|
||||
protected int activeRound;
|
||||
protected int totalRounds;
|
||||
protected int playersInPairing = 2;
|
||||
protected boolean initialized = false;
|
||||
protected boolean continualPairing = true;
|
||||
protected final List<TournamentPlayer> allPlayers = new ArrayList<TournamentPlayer>();
|
||||
protected transient final List<TournamentPlayer> remainingPlayers = new ArrayList<TournamentPlayer>();
|
||||
protected final List<TournamentPairing> completedPairings = new ArrayList<>();
|
||||
protected final List<TournamentPairing> activePairings = new ArrayList<>();
|
||||
|
||||
public List<TournamentPairing> getCompletedPairings() { return completedPairings; }
|
||||
public List<TournamentPairing> getActivePairings() { return activePairings; }
|
||||
|
||||
public AbstractTournament(int ttlRnds) {
|
||||
activeRound = 0;
|
||||
totalRounds = ttlRnds;
|
||||
}
|
||||
|
||||
public AbstractTournament(int ttlRnds, List<TournamentPlayer> plrs) {
|
||||
activeRound = 0;
|
||||
totalRounds = ttlRnds;
|
||||
allPlayers.addAll(plrs);
|
||||
remainingPlayers.addAll(plrs);
|
||||
}
|
||||
|
||||
public void initializeTournament() {
|
||||
// "Randomly" seed players to start tournament
|
||||
Collections.shuffle(remainingPlayers, MyRandom.getRandom());
|
||||
generateActivePairings();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public TournamentPairing getNextPairing() {
|
||||
if (activePairings.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return activePairings.get(0);
|
||||
}
|
||||
|
||||
public boolean isContinualPairing() { return continualPairing; }
|
||||
|
||||
public void setContinualPairing(boolean continualPairing) { this.continualPairing = continualPairing; }
|
||||
|
||||
public boolean isInitialized() { return initialized; }
|
||||
|
||||
public void setInitialized(boolean initialized) { this.initialized = initialized; }
|
||||
|
||||
|
||||
public boolean isPlayerRemaining(TournamentPlayer player) {
|
||||
return remainingPlayers.contains(player);
|
||||
}
|
||||
public boolean isPlayerRemaining(int index) {
|
||||
for(TournamentPlayer player : remainingPlayers) {
|
||||
if (player.getIndex() == index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract public void generateActivePairings();
|
||||
abstract public void reportMatchCompletion(TournamentPairing pairing);
|
||||
abstract public boolean completeRound();
|
||||
|
||||
public void finishMatch(TournamentPairing pairing) {
|
||||
activePairings.remove(pairing);
|
||||
completedPairings.add(pairing);
|
||||
}
|
||||
|
||||
abstract public void endTournament();
|
||||
|
||||
public boolean isTournamentOver() {
|
||||
return (initialized && activeRound == totalRounds && activePairings.isEmpty());
|
||||
}
|
||||
|
||||
public void addTournamentPlayer(LobbyPlayer pl) {
|
||||
TournamentPlayer player = new TournamentPlayer(pl);
|
||||
allPlayers.add(player);
|
||||
remainingPlayers.add(player);
|
||||
}
|
||||
|
||||
public void addTournamentPlayer(LobbyPlayer pl, int idx) {
|
||||
TournamentPlayer player = new TournamentPlayer(pl, idx);
|
||||
allPlayers.add(player);
|
||||
remainingPlayers.add(player);
|
||||
}
|
||||
|
||||
public void createTournamentPlayersForDraft(String[] names, int[] icons) {
|
||||
int size = names.length;
|
||||
for(int i = 0; i < size; i++) {
|
||||
TournamentPlayer player = new TournamentPlayer(GamePlayerUtil.createAiPlayer(names[i], icons[i]), i);
|
||||
allPlayers.add(player);
|
||||
remainingPlayers.add(player);
|
||||
}
|
||||
}
|
||||
|
||||
// Probably should be a interface here, not a standalone function
|
||||
public void exportToXML(HierarchicalStreamWriter writer) {
|
||||
/*
|
||||
protected int activeRound;
|
||||
protected int totalRounds;
|
||||
protected int playersInPairing = 2;
|
||||
protected boolean initialized = false;
|
||||
protected final List<TournamentPlayer> allPlayers = new ArrayList<TournamentPlayer>();
|
||||
protected final List<TournamentPairing> completedPairings = new ArrayList<>();
|
||||
protected final List<TournamentPairing> activePairings = new ArrayList<>();
|
||||
*/
|
||||
writer.startNode("activeRound");
|
||||
writer.setValue(Integer.toString(activeRound));
|
||||
writer.endNode();
|
||||
|
||||
writer.startNode("totalRounds");
|
||||
writer.setValue(Integer.toString(totalRounds));
|
||||
writer.endNode();
|
||||
|
||||
writer.startNode("playersInPairing");
|
||||
writer.setValue(Integer.toString(playersInPairing));
|
||||
writer.endNode();
|
||||
|
||||
writer.startNode("initialized");
|
||||
writer.setValue(Boolean.toString(initialized));
|
||||
writer.endNode();
|
||||
|
||||
writer.startNode("allPlayers");
|
||||
for (TournamentPlayer player : allPlayers) {
|
||||
writer.startNode("tournamentPlayer");
|
||||
writer.setValue(player.toString());
|
||||
writer.endNode();
|
||||
}
|
||||
writer.endNode();
|
||||
|
||||
writer.startNode("completedPairings");
|
||||
for (TournamentPairing pairing : completedPairings) {
|
||||
writer.startNode("tournamentPairing");
|
||||
pairing.exportToXML(writer);
|
||||
writer.endNode();
|
||||
}
|
||||
writer.endNode();
|
||||
|
||||
writer.startNode("completedPairings");
|
||||
for (TournamentPairing pairing : activePairings) {
|
||||
writer.startNode("tournamentPairing");
|
||||
pairing.exportToXML(writer);
|
||||
writer.endNode();
|
||||
}
|
||||
writer.endNode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package forge.tournament.system;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TournamentBracket extends AbstractTournament {
|
||||
// Bracket implies single elimination. For non-single elimination, use Swiss or RoundRobin
|
||||
public TournamentBracket(int ttlRnds, int pairingAmount) {
|
||||
super(ttlRnds);
|
||||
this.playersInPairing = pairingAmount;
|
||||
// Don't initialize the tournament if no players are available
|
||||
}
|
||||
|
||||
public TournamentBracket(int ttlRnds, List<TournamentPlayer> allPlayers) {
|
||||
super(ttlRnds, allPlayers);
|
||||
initializeTournament();
|
||||
}
|
||||
|
||||
public TournamentBracket(int ttlRnds, List<TournamentPlayer> allPlayers, int pairingAmount) {
|
||||
super(ttlRnds, allPlayers);
|
||||
this.playersInPairing = pairingAmount;
|
||||
initializeTournament();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateActivePairings() {
|
||||
activeRound++;
|
||||
List<TournamentPlayer> pair = new ArrayList<>();
|
||||
int count = 0;
|
||||
for (TournamentPlayer tp : this.remainingPlayers) {
|
||||
pair.add(tp);
|
||||
count++;
|
||||
if (count == this.playersInPairing) {
|
||||
count = 0;
|
||||
activePairings.add(new TournamentPairing(activeRound, pair));
|
||||
pair = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
if (count >= 1) {
|
||||
// Leftover players. Really shouldn't happen in a Bracket.
|
||||
TournamentPairing pairing = new TournamentPairing(activeRound, pair);
|
||||
if (count == 1) {
|
||||
pairing.setBye(true);
|
||||
}
|
||||
activePairings.add(pairing);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportMatchCompletion(TournamentPairing pairing) {
|
||||
finishMatch(pairing);
|
||||
|
||||
for (TournamentPlayer tp : pairing.getPairedPlayers()) {
|
||||
if (!tp.equals(pairing.getWinner())) {
|
||||
remainingPlayers.remove(tp);
|
||||
tp.setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (activePairings.isEmpty()) {
|
||||
completeRound();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean completeRound() {
|
||||
if (activeRound < totalRounds) {
|
||||
if (continualPairing) {
|
||||
generateActivePairings();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
endTournament();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTournament() {
|
||||
this.activePairings.clear();
|
||||
}
|
||||
|
||||
public int getFurthestRound(int index) {
|
||||
// If won in round 3, furthest round is 4
|
||||
// If lost in any round, that's the furthest round
|
||||
for (int i = completedPairings.size(); --i >= 0;) {
|
||||
TournamentPairing pairing = completedPairings.get(i);
|
||||
for(TournamentPlayer player : pairing.getPairedPlayers()) {
|
||||
if (player.getIndex() == index) {
|
||||
int roundAdjustment = pairing.getWinner().equals(player) ? 1 : 0;
|
||||
return pairing.getRound() + roundAdjustment;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Really shouldn't get here
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static TournamentBracket importFromXML() {
|
||||
TournamentBracket bracket = new TournamentBracket(3, 2);
|
||||
return bracket;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package forge.tournament.system;
|
||||
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
|
||||
import forge.LobbyPlayer;
|
||||
import forge.game.GameOutcome;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TournamentPairing {
|
||||
private int round;
|
||||
private boolean bye = false;
|
||||
private final List<TournamentPlayer> pairedPlayers = new ArrayList<TournamentPlayer>();
|
||||
private final List<GameOutcome> outcomes = new ArrayList<GameOutcome>();
|
||||
private TournamentPlayer winner;
|
||||
|
||||
public TournamentPairing(int rnd, List<TournamentPlayer> plyrs) {
|
||||
pairedPlayers.addAll(plyrs);
|
||||
round = rnd;
|
||||
winner = null;
|
||||
}
|
||||
|
||||
public int getRound() { return round; }
|
||||
|
||||
public void setRound(int round) { this.round = round; }
|
||||
|
||||
public boolean isBye() { return bye; }
|
||||
|
||||
public void setBye(boolean bye) { this.bye = bye; }
|
||||
|
||||
public List<TournamentPlayer> getPairedPlayers() { return pairedPlayers; }
|
||||
|
||||
public List<GameOutcome> getOutcomes() { return outcomes; }
|
||||
|
||||
public TournamentPlayer getWinner() { return winner; }
|
||||
|
||||
public void setWinner(TournamentPlayer winner) { this.winner = winner; }
|
||||
|
||||
public void setWinnerByIndex(int index) {
|
||||
for(TournamentPlayer pl : pairedPlayers) {
|
||||
if (pl.getIndex() == index) {
|
||||
this.winner = pl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPlayer(LobbyPlayer player) {
|
||||
for(TournamentPlayer pl : this.pairedPlayers) {
|
||||
if (pl.getPlayer().equals(player)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void exportToXML(HierarchicalStreamWriter writer) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package forge.tournament.system;
|
||||
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
|
||||
import forge.LobbyPlayer;
|
||||
|
||||
public class TournamentPlayer {
|
||||
private LobbyPlayer player;
|
||||
private int wins = 0,
|
||||
losses = 0,
|
||||
ties = 0;
|
||||
private boolean active = true;
|
||||
|
||||
private int index = -1;
|
||||
|
||||
public TournamentPlayer(LobbyPlayer plyr) {
|
||||
player = plyr;
|
||||
}
|
||||
|
||||
public TournamentPlayer(LobbyPlayer plyr, int idx) {
|
||||
player = plyr;
|
||||
index = idx;
|
||||
}
|
||||
|
||||
public LobbyPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public void setPlayer(LobbyPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public int getWins() {
|
||||
return wins;
|
||||
}
|
||||
|
||||
public void setWins(int wins) {
|
||||
this.wins = wins;
|
||||
}
|
||||
|
||||
public int getLosses() {
|
||||
return losses;
|
||||
}
|
||||
|
||||
public void setLosses(int losses) {
|
||||
this.losses = losses;
|
||||
}
|
||||
|
||||
public int getTies() {
|
||||
return ties;
|
||||
}
|
||||
|
||||
public void setTies(int ties) {
|
||||
this.ties = ties;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public int getIndex() { return index; }
|
||||
|
||||
public void setIndex(int index) { this.index = index; }
|
||||
|
||||
public void exportToXML(HierarchicalStreamWriter writer) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package forge.tournament.system;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TournamentRoundRobin extends AbstractTournament {
|
||||
// Round Robin tournaments where you play everyone in your group/pod. Declare winner or break to top X
|
||||
public TournamentRoundRobin(int ttlRnds, List<TournamentPlayer> allPlayers) {
|
||||
super(ttlRnds, allPlayers);
|
||||
initializeTournament();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateActivePairings() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportMatchCompletion(TournamentPairing pairing) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean completeRound() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTournament() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package forge.tournament.system;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TournamentSwiss extends AbstractTournament {
|
||||
|
||||
public TournamentSwiss(int ttlRnds, List<TournamentPlayer> allPlayers) {
|
||||
super(ttlRnds, allPlayers);
|
||||
initializeTournament();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateActivePairings() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportMatchCompletion(TournamentPairing pairing) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean completeRound() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTournament() {
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user