Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/User-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ The match screen is divided into resizable panels. The numbers below identify ea
3. **Player field tabs** — switch between opponents' fields in multiplayer; the (N new) indicator highlights cards that have changed since you last viewed that field. See [Sort Player Fields in Turn Order](#sort-player-fields-in-turn-order) for ordering options.
4. **Card Detail** — selected card's text and metadata.
5. **Opponent's field** — same layout as your own field (items 6-10 describe its components).
6. **Avatar and life total** — left-click to target the player, hover to see more info including Commander damage.
6. **Avatar and life total** — left-click to target the player, hover to see more info including Commander damage and estimated Commander bracket.
7. **Zone buttons** — hand, library, graveyard, exile, command, and sideboard with live counts; click to view, right-click for display options. See [Viewing cards in different zones](#viewing-cards-in-different-zones) for details.
8. **Turn Phases** — click any pip to toggle a priority stop in that phase; right-click to yield to that phase.
9. **Mana pool** — floating mana; click a symbol to spend it during cost payment.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
private final FLabel btnRandom = new FLabel.ButtonBuilder().fontSize(14).build();

private boolean isAi;
private int maximumCommanderBracket = 5;

private final ForgePreferences prefs = FModel.getPreferences();
private FPref stateSetting = null;
Expand Down Expand Up @@ -298,9 +299,21 @@ public Deck getDeck() {
if (proxy == null) {
return null;
}
if (isGeneratedCommanderDeckType() && proxy instanceof CommanderDeckGenerator commanderDeckGenerator) {
return commanderDeckGenerator.getDeck(maximumCommanderBracket);
}
return proxy.getDeck();
}

private boolean isGeneratedCommanderDeckType() {
return selectedDeckType == DeckType.RANDOM_COMMANDER_DECK
|| selectedDeckType == DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
}

public void setMaximumCommanderBracket(final int maximumCommanderBracket0) {
maximumCommanderBracket = maximumCommanderBracket0;
}

/** Generates deck from current list selection(s). */
public RegisteredPlayer getPlayer() {
if (lstDecks.getSelectedIndex() < 0) { return null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ public void update() {
view.getBtnStart().requestFocusInWindow();
});
view.getGamesInMatchBinder().load();
view.getMaximumCommanderBracketBinder().load();
}

/** React to a lobby-data change: detect event-state transitions and refresh the panel. */
Expand Down
96 changes: 87 additions & 9 deletions forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ public class VLobby implements ILobbyView {
static final int MAX_PLAYERS = 8;
private static final int EVENT_BTN_WIDTH = 200;
private static final int EVENT_BTN_HEIGHT = 50;
private static final int START_ROW_LABEL_WIDTH = 150;
private static final int START_ROW_COMBO_WIDTH = 50;
private static final int START_ROW_GAMES_WIDTH = START_ROW_LABEL_WIDTH + START_ROW_COMBO_WIDTH;
private static final int COMMANDER_BRACKET_SIDE_WIDTH = START_ROW_LABEL_WIDTH * 2 + START_ROW_COMBO_WIDTH;
private static final int COMMANDER_GAMES_SIDE_WIDTH = COMMANDER_BRACKET_SIDE_WIDTH + START_ROW_GAMES_WIDTH;
private static final String DEFAULT_MAXIMUM_COMMANDER_BRACKET = "5";
final Localizer localizer = Localizer.getInstance();
private static final ForgePreferences prefs = FModel.getPreferences();

Expand All @@ -62,7 +68,11 @@ public class VLobby implements ILobbyView {
private int playerWithFocus = 0; // index of the player that currently has focus

private final StartButton btnStart = new StartButton();
private final JPanel pnlStart = new JPanel(new MigLayout("insets 0, gap 0, wrap 2"));
private final JPanel pnlStart = new JPanel(new MigLayout("insets 0, gap 0, wrap 3"));
private final JComboBox<String> maximumCommanderBracket = createMaximumCommanderBracketCombo();
private final SwingPrefBinders.ComboBox maximumCommanderBracketBinder =
new SwingPrefBinders.ComboBox(FPref.UI_MAXIMUM_COMMANDER_BRACKET, maximumCommanderBracket);
private final JPanel maximumCommanderBracketFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap 2"));
private final JComboBox<String> gamesInMatch = new JComboBox<String>(new String[] {"1","3","5"});
private final SwingPrefBinders.ComboBox gamesInMatchBinder =
new SwingPrefBinders.ComboBox(FPref.UI_MATCHES_PER_GAME, gamesInMatch);
Expand Down Expand Up @@ -147,6 +157,12 @@ public class VLobby implements ILobbyView {

// (network draft state lives in CLobby)

private static JComboBox<String> createMaximumCommanderBracketCombo() {
final JComboBox<String> comboBox = new JComboBox<String>(new String[] {"1","2","3","4","5"});
comboBox.setSelectedItem(DEFAULT_MAXIMUM_COMMANDER_BRACKET);
return comboBox;
}

// CTR
public VLobby(final GameLobby lobby) {
this.lobby = lobby;
Expand Down Expand Up @@ -260,9 +276,14 @@ public VLobby(final GameLobby lobby) {
// Start Button
if (lobby.hasControl()) {
pnlStart.setOpaque(false);
pnlStart.add(btnStart, "align center");
maximumCommanderBracket.addActionListener(e -> applyMaximumCommanderBracketToDeckChoosers());
maximumCommanderBracketFrame.add(newLabel("Maximum Bracket:"), "w " + START_ROW_LABEL_WIDTH + "px!, h 30px!");
maximumCommanderBracketFrame.add(maximumCommanderBracket, "w " + START_ROW_COMBO_WIDTH + "px!, h 30px!");
maximumCommanderBracketFrame.setOpaque(false);
addConstructedStartControls();
// Start button event handling
btnStart.addActionListener(arg0 -> {
updateCommanderGeneratedDeckSelections();
Comment thread
tool4ever marked this conversation as resolved.
Outdated
Runnable startGame = lobby.startGame();
if (startGame != null) {
startGame.run();
Expand All @@ -287,11 +308,32 @@ public VLobby(final GameLobby lobby) {
defaultGamesInMatch = "3";
}

gamesInMatchFrame.add(newLabel(localizer.getMessage("lblGamesInMatch")), "w 150px!, h 30px!");
gamesInMatchFrame.add(gamesInMatch, "w 50px!, h 30px!");
gamesInMatchFrame.add(newLabel(localizer.getMessage("lblGamesInMatch")), "w " + START_ROW_LABEL_WIDTH + "px!, h 30px!");
gamesInMatchFrame.add(gamesInMatch, "w " + START_ROW_COMBO_WIDTH + "px!, h 30px!");
gamesInMatchFrame.setOpaque(false);
}

pnlStart.add(gamesInMatchFrame);
private int getMaximumCommanderBracket() {
return Integer.parseInt((String) maximumCommanderBracket.getSelectedItem());
}

private void addConstructedStartControls() {
if (isCommanderBracketSelectorVisible()) {
maximumCommanderBracketFrame.setVisible(true);
pnlStart.setLayout(new MigLayout("insets 0, gap 0, wrap 3"));
pnlStart.add(maximumCommanderBracketFrame, "w " + COMMANDER_BRACKET_SIDE_WIDTH + "px!, align left");
pnlStart.add(btnStart, "align center");
pnlStart.add(gamesInMatchFrame, "w " + COMMANDER_GAMES_SIDE_WIDTH + "px!, align left");
} else {
maximumCommanderBracketFrame.setVisible(false);
pnlStart.setLayout(new MigLayout("insets 0, gap 0, wrap 2"));
pnlStart.add(btnStart, "align center");
pnlStart.add(gamesInMatchFrame, "align center");
}
}

private boolean isCommanderBracketSelectorVisible() {
return lobby.getGameType() == GameType.Commander || hasVariant(GameType.Commander);
}

public void updateDeckPanel() {
Expand Down Expand Up @@ -588,6 +630,7 @@ private FDeckChooser getDeckChooser(final int iSlot) {

private void selectMainDeck(final FDeckChooser mainChooser, final int playerIndex, final boolean isCommanderDeck) {
final DeckType type = mainChooser.getSelectedDeckType();
mainChooser.setMaximumCommanderBracket(getMaximumCommanderBracket());
final Deck deck = mainChooser.getDeck();
// something went wrong, clear selection to prevent error loop
if (deck == null) {
Expand All @@ -606,6 +649,37 @@ private void selectMainDeck(final FDeckChooser mainChooser, final int playerInde
mainChooser.saveState();
}

private void updateCommanderGeneratedDeckSelections() {
final int maximumBracket = getMaximumCommanderBracket();
for (int i = 0; i < activePlayersNum && i < playerPanels.size(); i++) {
if (!lobby.mayEdit(i)) {
continue;
}
final FDeckChooser deckChooser = getDeckChooser(i);
if (deckChooser == null) {
continue;
}
deckChooser.setMaximumCommanderBracket(maximumBracket);
if (isGeneratedCommanderDeckType(deckChooser.getSelectedDeckType())) {
selectMainDeck(deckChooser, i, true);
}
}
}

private void applyMaximumCommanderBracketToDeckChoosers() {
final int maximumBracket = getMaximumCommanderBracket();
for (int i = 0; i < activePlayersNum && i < playerPanels.size(); i++) {
final FDeckChooser deckChooser = getDeckChooser(i);
if (deckChooser != null) {
deckChooser.setMaximumCommanderBracket(maximumBracket);
}
}
}

private static boolean isGeneratedCommanderDeckType(final DeckType type) {
return type == DeckType.RANDOM_COMMANDER_DECK || type == DeckType.RANDOM_CARDGEN_COMMANDER_DECK;
}

private void selectSchemeDeck(final int playerIndex) {
if (playerIndex >= activePlayersNum || !(hasVariant(GameType.Archenemy) || hasVariant(GameType.ArchenemyRumble))) {
return;
Expand Down Expand Up @@ -888,10 +962,7 @@ void updateActionButtons() {
pnlStart.add(btnStartMatch, "cell 2 0, " + eventBtn);
pnlStart.add(gamesInMatchFrame, "cell 2 1, align center");
} else {
// Constructed mode: Start button centered with games-in-match below
pnlStart.setLayout(new MigLayout("insets 0, gap 0, wrap 2"));
pnlStart.add(btnStart, "align center, spanx 2, wrap");
pnlStart.add(gamesInMatchFrame, "spanx 2, align center");
addConstructedStartControls();
}
}
// Non-host: nothing to show here — match controls are host-only.
Expand Down Expand Up @@ -1061,6 +1132,7 @@ private VariantCheckBox(final GameType variantType) {
lobby.removeVariant(variantType);
}
VLobby.this.update(false);
VLobby.this.updateActionButtons();
});
}
}
Expand Down Expand Up @@ -1099,6 +1171,7 @@ private FDeckChooser createDeckChooser(final GameType type, final int iSlot, fin
return cachedDeckChoosers.computeIfAbsent(prefKey, (key) -> {
final GameType gameType = forCommander ? type : GameType.Constructed;
final FDeckChooser fdc = new FDeckChooser(null, ai, gameType, forCommander);
fdc.setMaximumCommanderBracket(getMaximumCommanderBracket());
fdc.initialize(prefKey, deckType);
fdc.getLstDecks().setSelectCommand(() -> selectMainDeck(fdc, iSlot, forCommander));
return fdc;
Expand Down Expand Up @@ -1159,6 +1232,11 @@ public SwingPrefBinders.ComboBox getGamesInMatchBinder() {
return gamesInMatchBinder;
}

/** Return the maximumCommanderBracketBinder. */
public SwingPrefBinders.ComboBox getMaximumCommanderBracketBinder() {
return maximumCommanderBracketBinder;
}

/** Populate vanguard lists. */
private void populateVanguardLists() {
humanListData.add("Use deck's default avatar (random if unavailable)");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import javax.swing.border.Border;
import javax.swing.border.LineBorder;

import forge.deck.CommanderBracketCalculator;
import forge.deck.Deck;
import forge.game.card.CounterEnumType;
import forge.game.player.PlayerView;
import forge.game.zone.ZoneType;
Expand Down Expand Up @@ -64,7 +66,10 @@ public class VField implements IVDoc<CField> {
private final DragTab tab = new DragTab(Localizer.getInstance().getMessage("lblField"));

// Other fields
private final CMatchUI matchUI;
private final PlayerView player;
private boolean commanderBracketTooltipCalculated = false;
private String commanderBracketTooltipLine;

// Top-level containers
private final FScrollPane scroller = new FScrollPane(false);
Expand Down Expand Up @@ -100,6 +105,7 @@ public class VField implements IVDoc<CField> {
public VField(final CMatchUI matchUI, final EDocID id0, final PlayerView p, final boolean mirror) {
this.docID = id0;

this.matchUI = matchUI;
this.player = p;
if (p != null) { tab.setText(Localizer.getInstance().getMessage("lblPlayField", p.getName())); }
else { tab.setText(Localizer.getInstance().getMessage("lblNoPlayerForEDocID", docID.toString())); }
Expand Down Expand Up @@ -406,6 +412,45 @@ public void updateDetails() {
final boolean highlighted = isHighlighted();
this.avatarArea.setBorder(highlighted ? borderAvatarHighlighted : borderAvatarSimple );
this.avatarArea.setOpaque(highlighted);
this.avatarArea.setToolTipText(player.getDetailsHtml());
this.avatarArea.setToolTipText(getPlayerDetailsHtml());
Comment thread
tool4ever marked this conversation as resolved.
}

private String getPlayerDetailsHtml() {
final String detailsHtml = player.getDetailsHtml();
final String commanderBracketLine = getCommanderBracketTooltipLine();
if (commanderBracketLine == null) {
return detailsHtml;
}

final String nameSeparator = "<hr/>";
final int insertIndex = detailsHtml.indexOf(nameSeparator);
if (insertIndex < 0) {
return detailsHtml;
}

final int lineIndex = insertIndex + nameSeparator.length();
return detailsHtml.substring(0, lineIndex)
+ commanderBracketLine + "<br/>"
+ detailsHtml.substring(lineIndex);
}

private String getCommanderBracketTooltipLine() {
if (commanderBracketTooltipCalculated) {
return commanderBracketTooltipLine;
}

commanderBracketTooltipCalculated = true;
if (matchUI == null || matchUI.getGameView() == null || !matchUI.getGameView().isCommander()) {
return null;
}

final Deck deck = matchUI.getGameView().getDeck(player);
if (deck == null) {
return null;
}

commanderBracketTooltipLine = Localizer.getInstance().getMessage("lblBracket")
+ ": " + CommanderBracketCalculator.getDisplayBracket(deck);
return commanderBracketTooltipLine;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ public static Result calculate(final Deck deck) {
return Result.empty();
}

final Set<String> deckCards = getDeckCardNames(deck);
return calculate(getDeckCardNames(deck));
}

public static Result calculate(final Set<String> cardNames) {
final Set<String> deckCards = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
deckCards.addAll(cardNames);
final List<String> gamechangers = DATA.findCards(deckCards, DATA.gamechangers);
final List<String> massLandDenial = DATA.findCards(deckCards, DATA.massLandDenial);
final List<String> extraTurns = DATA.findCards(deckCards, DATA.extraTurns);
Expand Down Expand Up @@ -89,6 +94,14 @@ private static Set<String> getDeckCardNames(final Deck deck) {
return result;
}

public static Set<String> getCardNames(final PaperCard card) {
final Set<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
if (card != null) {
result.addAll(card.getAllSearchableNames());
}
return result;
}

private static String normalize(final String text) {
return text.trim().toLowerCase(Locale.ROOT);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ public int compareTo(final CommanderDeckGenerator d) {

@Override
public Deck getDeck() {
return DeckgenUtil.generateRandomCommanderDeck(legend, format, isForAi, isCardgen);
return getDeck(0);
}

public Deck getDeck(final int maxBracket) {
return DeckgenUtil.generateRandomCommanderDeck(legend, format, isForAi, isCardgen, maxBracket);
}

@Override
Expand Down
Loading