diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java index 9cb1835fdff..80468bfe4be 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java @@ -91,6 +91,7 @@ public enum CSubmenuPreferences implements ICDoc { lstControls.add(Pair.of(view.getCbCompactPrompt(), FPref.UI_COMPACT_PROMPT)); lstControls.add(Pair.of(view.getCbHideReminderText(), FPref.UI_HIDE_REMINDER_TEXT)); lstControls.add(Pair.of(view.getCbOpenPacksIndiv(), FPref.UI_OPEN_PACKS_INDIV)); + lstControls.add(Pair.of(view.getCbStackCreatures(), FPref.UI_STACK_CREATURES)); for(final Pair kv : lstControls) { kv.getKey().addItemListener(new ItemListener() { diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java index 416f143cc19..5c926ebcc44 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java @@ -81,6 +81,7 @@ public enum VSubmenuPreferences implements IVSubmenu { private final JCheckBox cbCompactPrompt = new OptionsCheckBox("Compact Prompt"); private final JCheckBox cbHideReminderText = new OptionsCheckBox("Hide Reminder Text"); private final JCheckBox cbOpenPacksIndiv = new OptionsCheckBox("Open Packs Individually"); + private final JCheckBox cbStackCreatures = new OptionsCheckBox("Stack Creatures"); private final Map shortcutFields = new HashMap(); @@ -214,6 +215,9 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbOpenPacksIndiv, regularConstraints); pnlPrefs.add(new NoteLabel("When opening Fat Packs and Booster Boxes, booster packs will be opened and displayed one at a time."), regularConstraints); + pnlPrefs.add(cbStackCreatures, regularConstraints); + pnlPrefs.add(new NoteLabel("Stacks identical creatures on the battlefield like lands, artifacts, and enchantments."), regularConstraints); + // Sound options pnlPrefs.add(new SectionLabel("Sound Options"), sectionConstraints + ", gaptop 2%"); @@ -527,6 +531,10 @@ public enum VSubmenuPreferences implements IVSubmenu { return cbOpenPacksIndiv; } + public final JCheckBox getCbStackCreatures() { + return cbStackCreatures; + } + /** @return {@link forge.toolbox.FLabel} */ public FLabel getBtnReset() { return btnReset; diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java b/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java index a5a31c1b8b8..d6633dc99a7 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java @@ -337,20 +337,19 @@ public enum CMatchUI implements ICDoc, IMenuProvider { Player owner = kv.getKey(); ZoneType zt = kv.getValue(); - if (zt == ZoneType.Command) + if (zt == ZoneType.Command) { getCommandFor(owner).getTabletop().setupPlayZone(); - else if (zt == ZoneType.Hand) { + } else if (zt == ZoneType.Hand) { VHand vHand = getHandFor(owner); - if (null != vHand) + if (null != vHand) { vHand.getLayoutControl().updateHand(); + } getFieldViewFor(owner).getDetailsPanel().updateZones(); - } - else if (zt == ZoneType.Battlefield) { + } else if (zt == ZoneType.Battlefield) { getFieldViewFor(owner).getTabletop().setupPlayZone(); } else if (zt == ZoneType.Ante) { CAntes.SINGLETON_INSTANCE.update(); - } - else { + } else { getFieldViewFor(owner).getDetailsPanel().updateZones(); } } diff --git a/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java b/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java index 58f2ba7a148..f5b58459ae4 100644 --- a/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java +++ b/forge-gui-desktop/src/main/java/forge/view/arcane/PlayArea.java @@ -19,6 +19,8 @@ package forge.view.arcane; import forge.FThreads; import forge.game.card.Card; +import forge.model.FModel; +import forge.properties.ForgePreferences.FPref; import forge.screens.match.CMatchUI; import forge.screens.match.controllers.CPrompt; import forge.toolbox.FScrollPane; @@ -56,9 +58,10 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen /** Constant STACK_SPACING_Y=0.07f. */ private static final float STACK_SPACING_Y = 0.07f; + private final int creatureStackMax = 4; private final int landStackMax = 5; private final int tokenStackMax = 5; - private final int othersStackMax = 5; + private final int othersStackMax = 4; private final boolean mirror; @@ -183,6 +186,57 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen return allTokens; } + private final CardStackRow collectAllCreatures() { + final CardStackRow allCreatures = new CardStackRow(); + outerLoop: + // + for (final CardPanel panel : this.getCardPanels()) { + if (!panel.getCard().isCreature()) { + continue; + } + + int insertIndex = -1; + + // Find tokens with the same name. + for (int i = 0, n = allCreatures.size(); i < n; i++) { + final CardStack stack = allCreatures.get(i); + final CardPanel firstPanel = stack.get(0); + if (firstPanel.getCard().getName().equals(panel.getCard().getName())) { + if (!firstPanel.getAttachedPanels().isEmpty()) { + // Put this token to the left of tokens with the same + // name and attachments. + insertIndex = i; + break; + } + if (!panel.getAttachedPanels().isEmpty() + || panel.getCard().isEnchanted() + || panel.getCard().isCloned() + || !panel.getCard().getCounters().equals(firstPanel.getCard().getCounters()) + || (panel.getCard().isSick() != firstPanel.getCard().isSick()) + || (panel.getCard().getNetAttack() != firstPanel.getCard().getNetAttack()) + || (panel.getCard().getNetDefense() != firstPanel.getCard().getNetDefense()) + || (stack.size() == creatureStackMax)) { + // If this token has attachments or the stack is full, + // put it to the right. + insertIndex = i + 1; + continue; + } + // Add to stack. + stack.add(0, panel); + continue outerLoop; + } + if (insertIndex != -1) { + break; + } + } + + final CardStack stack = new CardStack(); + stack.add(panel); + allCreatures.add(insertIndex == -1 ? allCreatures.size() : insertIndex, stack); + } + return allCreatures; + } + @Override public final CardPanel addCard(final Card card) { final CardPanel placeholder = new CardPanel(card); @@ -201,7 +255,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen final CardStackRow lands = collectAllLands(); final CardStackRow tokens = collectAllTokens(); - final CardStackRow creatures = new CardStackRow(this.getCardPanels(), RowType.CreatureNonToken); + final CardStackRow creatures = FModel.getPreferences().getPrefBoolean(FPref.UI_STACK_CREATURES) ? collectAllCreatures() : new CardStackRow(this.getCardPanels(), RowType.CreatureNonToken); final CardStackRow others = new CardStackRow(this.getCardPanels(), RowType.Other); // should find an appropriate width of card @@ -380,7 +434,7 @@ public class PlayArea extends CardPanelContainer implements CardPanelMouseListen return false; } - if ( insertIndex == -1) + if (insertIndex == -1) template.add(currentRow); else template.add(insertIndex, currentRow); diff --git a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java index a1938708fad..8a922c956a0 100644 --- a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java +++ b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java @@ -11,6 +11,8 @@ import forge.game.player.Player; import forge.game.zone.PlayerZone; import forge.game.zone.Zone; import forge.game.zone.ZoneType; +import forge.model.FModel; +import forge.properties.ForgePreferences.FPref; import forge.util.Lang; import forge.util.gui.SGuiChoose; import forge.util.maps.MapOfLists; @@ -65,6 +67,12 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { private final AtomicBoolean turnUpdPlanned = new AtomicBoolean(false); @Override public Void visit(final GameEventTurnBegan event) { + + if (FModel.getPreferences().getPrefBoolean(FPref.UI_STACK_CREATURES) && event.turnOwner != null) { + // anything except stack will get here + updateZone(Pair.of(event.turnOwner, ZoneType.Battlefield)); + } + if (turnUpdPlanned.getAndSet(true)) { return null; } final Game game = GuiBase.getInterface().getGame(); // to make sure control gets a correct game instance diff --git a/forge-gui/src/main/java/forge/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/properties/ForgePreferences.java index 7f74eb50e81..edb70c4b405 100644 --- a/forge-gui/src/main/java/forge/properties/ForgePreferences.java +++ b/forge-gui/src/main/java/forge/properties/ForgePreferences.java @@ -47,6 +47,7 @@ public class ForgePreferences extends PreferencesStore { UI_OVERLAY_FOIL_EFFECT ("true"), UI_HIDE_REMINDER_TEXT ("false"), UI_OPEN_PACKS_INDIV ("false"), + UI_STACK_CREATURES ("false"), UI_UPLOAD_DRAFT ("false"), UI_SCALE_LARGER ("true"), UI_LARGE_CARD_VIEWERS ("false"),