From 004a14759e8c99cf6aa122ac58ab90d25abf0218 Mon Sep 17 00:00:00 2001 From: Eirkei Date: Mon, 3 Oct 2016 11:32:32 +0200 Subject: [PATCH 01/13] Add correct targets to spells with modes - Fixes #2391 --- Mage.Common/src/mage/view/CardView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 2c1aa4c5aed..69b27f15dcf 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -324,7 +324,7 @@ public class CardView extends SimpleCardView { for (UUID modeId : spellAbility.getModes().getSelectedModes()) { Mode mode = spellAbility.getModes().get(modeId); if (mode.getTargets().size() > 0) { - setTargets(spellAbility.getTargets()); + setTargets(mode.getTargets()); } } } From 24fc597fe55a0162f3be25a5afc5c053b6e116a8 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 3 Oct 2016 19:41:46 +0200 Subject: [PATCH 02/13] Fixed mode handling for modes that can be selected multiple times. --- .../cards/modal/SameModeMoreThanOnceTest.java | 99 +++++++++++++++++++ .../java/org/mage/test/player/TestPlayer.java | 15 +-- .../base/impl/CardTestPlayerAPIImpl.java | 3 +- .../main/java/mage/abilities/AbilityImpl.java | 5 +- Mage/src/main/java/mage/abilities/Mode.java | 14 ++- Mage/src/main/java/mage/abilities/Modes.java | 87 ++++++++++++---- Mage/src/main/java/mage/game/stack/Spell.java | 5 +- 7 files changed, 188 insertions(+), 40 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/modal/SameModeMoreThanOnceTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/modal/SameModeMoreThanOnceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/modal/SameModeMoreThanOnceTest.java new file mode 100644 index 00000000000..4bdaf851531 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/modal/SameModeMoreThanOnceTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.modal; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class SameModeMoreThanOnceTest extends CardTestPlayerBase { + + @Test + public void testEachModeOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + // Choose three. You may choose the same mode more than once. + // - Target player draws a card and loses 1 life; + // - Target creature gets -2/-2 until end of turn; + // - Return target creature card from your graveyard to your hand. + addCard(Zone.HAND, playerA, "Wretched Confluence"); // Instant {3}{B}{B} + + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Confluence", "mode=1targetPlayer=PlayerA^mode=2Pillarfield Ox^mode=3Silvercoat Lion"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "3"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Wretched Confluence", 1); + assertLife(playerA, 19); + assertLife(playerB, 20); + assertHandCount(playerA, 2); + assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); + assertGraveyardCount(playerA, "Silvercoat Lion", 0); + + } + + @Test + public void testSecondModeTwiceThridModeOnce() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + // Choose three. You may choose the same mode more than once. + // - Target player draws a card and loses 1 life; + // - Target creature gets -2/-2 until end of turn; + // - Return target creature card from your graveyard to your hand. + addCard(Zone.HAND, playerA, "Wretched Confluence"); // Instant {3}{B}{B} + + addCard(Zone.BATTLEFIELD, playerB, "Wall of Air"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Confluence", "mode=1Pillarfield Ox^mode=2Wall of Air^mode=3Silvercoat Lion"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "3"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Wretched Confluence", 1); + assertLife(playerA, 20); + assertLife(playerB, 20); + assertPowerToughness(playerB, "Wall of Air", -1, 3); + assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); + assertGraveyardCount(playerA, "Silvercoat Lion", 0); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 4c7fd6698fd..7c554025fd0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -282,19 +282,14 @@ public class TestPlayer implements Player { Mode selectedMode = null; if (targetName.startsWith("mode=")) { int modeNr = Integer.parseInt(targetName.substring(5, 6)); - if (modeNr == 0 || modeNr > ability.getModes().size()) { + if (modeNr == 0 || modeNr > (ability.getModes().isEachModeMoreThanOnce() ? ability.getModes().getSelectedModes().size() : ability.getModes().size())) { throw new UnsupportedOperationException("Given mode number (" + modeNr + ") not available for " + ability.toString()); } UUID modeId = ability.getModes().getModeId(modeNr); - - for (UUID currentModeId : ability.getModes().getSelectedModes()) { - Mode mode = ability.getModes().get(currentModeId); - if (mode.getId().equals(modeId)) { - selectedMode = mode; - ability.getModes().setActiveMode(mode); - index = 0; // reset target index if mode changes - break; - } + selectedMode = ability.getModes().get(modeId); + if (modeId != ability.getModes().getMode().getId()) { + ability.getModes().setActiveMode(modeId); + index = 0; // reset target index if mode changes } targetName = targetName.substring(6); } else { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index fc92814a8b6..acba31dad0e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -1131,7 +1131,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * additional by setcode e.g. "creatureName-M15" you can add [no copy] to * the end of the target name to prohibite targets that are copied you can * add [only copy] to the end of the target name to allow only targets that - * are copies + * are copies For modal spells use a prefix with the mode number: + * mode=1Lightning Bolt^mode=2Silvercoat Lion */ public void addTarget(TestPlayer player, String target) { player.addTarget(target); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index c79e37f28bc..cf82795eff0 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -307,8 +307,7 @@ public abstract class AbilityImpl implements Ability { return false; } for (UUID modeId : this.getModes().getSelectedModes()) { - Mode mode = this.getModes().get(modeId); - this.getModes().setActiveMode(mode); + this.getModes().setActiveMode(modeId); //20121001 - 601.2c // 601.2c The player announces his or her choice of an appropriate player, object, or zone for // each target the spell requires. A spell may require some targets only if an alternative or @@ -330,7 +329,7 @@ public abstract class AbilityImpl implements Ability { sourceObject.adjustTargets(this, game); } // Flashback abilities haven't made the choices the underlying spell might need for targetting. - if (!(this instanceof FlashbackAbility) && mode.getTargets().size() > 0 && mode.getTargets().chooseTargets( + if (!(this instanceof FlashbackAbility) && getTargets().size() > 0 && getTargets().chooseTargets( getEffects().get(0).getOutcome(), this.controllerId, this, noMana, game) == false) { if ((variableManaCost != null || announceString != null) && !game.isSimulation()) { game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets with this value of X"); diff --git a/Mage/src/main/java/mage/abilities/Mode.java b/Mage/src/main/java/mage/abilities/Mode.java index 26942355841..113907d70d7 100644 --- a/Mage/src/main/java/mage/abilities/Mode.java +++ b/Mage/src/main/java/mage/abilities/Mode.java @@ -1,16 +1,16 @@ /* * Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. @@ -54,6 +54,10 @@ public class Mode implements Serializable { this.effects = mode.effects.copy(); } + public UUID setRandomId() { + return this.id = UUID.randomUUID(); + } + public Mode copy() { return new Mode(this); } diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index dad2af68c02..a10edeba125 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -55,13 +55,14 @@ public class Modes extends LinkedHashMap { private TargetController modeChooser; private boolean eachModeMoreThanOnce; // each mode can be selected multiple times during one choice private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists + private final LinkedHashMap duplicateModes = new LinkedHashMap<>(); public Modes() { this.currentMode = new Mode(); this.put(currentMode.getId(), currentMode); this.minModes = 1; this.maxModes = 1; - this.selectedModes.add(currentMode.getId()); + this.addSelectedMode(currentMode.getId()); this.modeChooser = TargetController.YOU; this.eachModeOnlyOnce = false; this.eachModeMoreThanOnce = false; @@ -71,6 +72,9 @@ public class Modes extends LinkedHashMap { for (Map.Entry entry : modes.entrySet()) { this.put(entry.getKey(), entry.getValue().copy()); } + for (Map.Entry entry : modes.duplicateModes.entrySet()) { + this.put(entry.getKey(), entry.getValue().copy()); + } this.minModes = modes.minModes; this.maxModes = modes.maxModes; this.selectedModes.addAll(modes.getSelectedModes()); @@ -78,7 +82,7 @@ public class Modes extends LinkedHashMap { if (modes.getSelectedModes().isEmpty()) { this.currentMode = values().iterator().next(); } else { - this.currentMode = get(selectedModes.get(0)); + this.currentMode = get(modes.getMode().getId()); } this.modeChooser = modes.modeChooser; this.eachModeOnlyOnce = modes.eachModeOnlyOnce; @@ -89,16 +93,41 @@ public class Modes extends LinkedHashMap { return new Modes(this); } + @Override + public Mode get(Object key) { + Mode modeToGet = super.get(key); + if (modeToGet == null && eachModeMoreThanOnce) { + modeToGet = duplicateModes.get(key); + } + return modeToGet; + } + public Mode getMode() { return currentMode; } + /** + * Returns the mode by index. For modal spells with eachModeMoreThanOnce, + * the index returns the n selected mode + * + * @param index + * @return + */ public UUID getModeId(int index) { int idx = 0; - for (Mode mode : this.values()) { - idx++; - if (idx == index) { - return mode.getId(); + if (eachModeMoreThanOnce) { + for (UUID modeId : this.selectedModes) { + idx++; + if (idx == index) { + return modeId; + } + } + } else { + for (Mode mode : this.values()) { + idx++; + if (idx == index) { + return mode.getId(); + } } } return null; @@ -138,6 +167,12 @@ public class Modes extends LinkedHashMap { } } + public void setActiveMode(UUID modeId) { + if (selectedModes.contains(modeId)) { + this.currentMode = get(modeId); + } + } + public void addMode(Mode mode) { this.put(mode.getId(), mode); } @@ -145,6 +180,7 @@ public class Modes extends LinkedHashMap { public boolean choose(Game game, Ability source) { if (this.size() > 1) { this.selectedModes.clear(); + this.duplicateModes.clear(); // check if mode modifying abilities exist Card card = game.getCard(source.getSourceId()); if (card != null) { @@ -163,7 +199,7 @@ public class Modes extends LinkedHashMap { for (Mode mode : this.values()) { if ((!isEachModeOnlyOnce() || onceSelectedModes == null || !onceSelectedModes.contains(mode.getId())) && mode.getTargets().canChoose(source.getSourceId(), source.getControllerId(), game)) { - this.selectedModes.add(mode.getId()); + this.addSelectedMode(mode.getId()); } } if (isEachModeOnlyOnce()) { @@ -200,7 +236,7 @@ public class Modes extends LinkedHashMap { } return this.selectedModes.size() >= this.getMinModes(); } - this.selectedModes.add(choice.getId()); + this.addSelectedMode(choice.getId()); if (currentMode == null) { currentMode = choice; } @@ -209,17 +245,15 @@ public class Modes extends LinkedHashMap { setAlreadySelectedModes(selectedModes, source, game); } return true; + } else { // only one mode + if (currentMode == null) { + this.selectedModes.clear(); + Mode mode = this.values().iterator().next(); + this.addSelectedMode(mode.getId()); + this.setActiveMode(mode); + } + return true; } - if (currentMode == null) { - this.selectedModes.clear(); - Mode copiedMode = this.values().iterator().next().copy(); - this.selectedModes.add(copiedMode.getId()); - this.setActiveMode(copiedMode); - } - if (isEachModeOnlyOnce()) { - setAlreadySelectedModes(selectedModes, source, game); - } - return true; } /** @@ -236,6 +270,23 @@ public class Modes extends LinkedHashMap { } } + /** + * Adds a mode as selected. If the mode is already selected, it copies the + * mode and adds it to the duplicate modes + * + * @param modeId + */ + private void addSelectedMode(UUID modeId) { + if (selectedModes.contains(modeId) && eachModeMoreThanOnce) { + Mode duplicateMode = get(modeId).copy(); + duplicateMode.setRandomId(); + modeId = duplicateMode.getId(); + duplicateModes.put(modeId, duplicateMode); + + } + this.selectedModes.add(modeId); + } + // The already once selected modes for a modal card are stored as a state value // That's important for modal abilities with modes that can only selected once while the object stays in its zone @SuppressWarnings("unchecked") diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index bffef3e5ba2..69dfb83d4be 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -211,9 +211,8 @@ public class Spell extends StackObjImpl implements Card { for (SpellAbility spellAbility : this.spellAbilities) { if (spellAbilityHasLegalParts(spellAbility, game)) { for (UUID modeId : spellAbility.getModes().getSelectedModes()) { - Mode mode = spellAbility.getModes().get(modeId); - spellAbility.getModes().setActiveMode(mode); - if (mode.getTargets().stillLegal(spellAbility, game)) { + spellAbility.getModes().setActiveMode(modeId); + if (spellAbility.getTargets().stillLegal(spellAbility, game)) { if (!spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE)) { updateOptionalCosts(index); } From 1a8f38759b46504e6852ed5e6124dd918922447d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 3 Oct 2016 19:43:26 +0200 Subject: [PATCH 03/13] Fixed a problem with filetring of CantBeTargetedAllEffect. --- .../test/cards/replacement/canttarget/DenseFoliageTest.java | 2 ++ .../abilities/effects/common/CantBeTargetedAllEffect.java | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/canttarget/DenseFoliageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/canttarget/DenseFoliageTest.java index 9d7bd0ab72c..2cbe32d2403 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/canttarget/DenseFoliageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/canttarget/DenseFoliageTest.java @@ -35,7 +35,9 @@ public class DenseFoliageTest extends CardTestPlayerBase { */ @Test public void testAbilityCanTarget() { + // Creatures can't be the targets of spells addCard(Zone.BATTLEFIELD, playerA, "Dense Foliage"); + //{T}: Prodigal Sorcerer deals 1 damage to target creature or player. addCard(Zone.BATTLEFIELD, playerA, "Prodigal Sorcerer"); addCard(Zone.BATTLEFIELD, playerB, "Eager Cadet"); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java index f2ff47c9c4e..40635ee0c37 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java @@ -34,6 +34,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.FilterObject; import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -93,6 +94,10 @@ public class CantBeTargetedAllEffect extends ContinuousRuleModifyingEffectImpl { StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); MageObject sourceObject; if (stackObject instanceof StackAbility) { + if (filterSource instanceof FilterSpell) { + // only spells have to be selected + return false; + } sourceObject = ((StackAbility) stackObject).getSourceObject(game); } else { sourceObject = stackObject; From 7dfcb15c3c17bb4e0e81bbf1d11e9559327a1add Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 3 Oct 2016 20:19:57 +0200 Subject: [PATCH 04/13] Fixed some null pointer exceptions with Thieves Auction and Avatar of the Might. --- .../mage/sets/mercadianmasques/ThievesAuction.java | 4 ++-- Mage.Sets/src/mage/sets/prophecy/AvatarOfMight.java | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/ThievesAuction.java b/Mage.Sets/src/mage/sets/mercadianmasques/ThievesAuction.java index 84ffe10daf5..a153561d28d 100644 --- a/Mage.Sets/src/mage/sets/mercadianmasques/ThievesAuction.java +++ b/Mage.Sets/src/mage/sets/mercadianmasques/ThievesAuction.java @@ -109,8 +109,8 @@ class ThievesAuctionEffect extends OneShotEffect { // Starting with you, each player PlayerList playerList = game.getState().getPlayersInRange(controller.getId(), game); Player player = playerList.getCurrent(game); - while (!exiledCards.isEmpty()) { - if (player.canRespond()) { + while (!exiledCards.isEmpty() && !game.hasEnded()) { + if (player != null && player.canRespond()) { // chooses one of the exiled cards TargetCard target = new TargetCardInExile(new FilterCard()); if (player.choose(Outcome.PutCardInPlay, exiledCards, target, game)) { diff --git a/Mage.Sets/src/mage/sets/prophecy/AvatarOfMight.java b/Mage.Sets/src/mage/sets/prophecy/AvatarOfMight.java index 2b7b581f5c0..00bdd683ce7 100644 --- a/Mage.Sets/src/mage/sets/prophecy/AvatarOfMight.java +++ b/Mage.Sets/src/mage/sets/prophecy/AvatarOfMight.java @@ -105,11 +105,13 @@ class AvatarOfMightCostReductionEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - int creatures = game.getBattlefield().countAll(new FilterCreaturePermanent(), source.getControllerId(), game); - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(playerId); - if (opponent != null && game.getBattlefield().countAll(new FilterCreaturePermanent(), opponent.getId(), game) >= creatures + 4) { - return true; + if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) { + int creatures = game.getBattlefield().countAll(new FilterCreaturePermanent(), source.getControllerId(), game); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(playerId); + if (opponent != null && game.getBattlefield().countAll(new FilterCreaturePermanent(), opponent.getId(), game) >= creatures + 4) { + return true; + } } } return false; From 9a00dd20cad988d88243d5c26c45673f33ea5c82 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 3 Oct 2016 20:25:08 +0200 Subject: [PATCH 05/13] Fixed some problems with constructed formats definition and custom sets. --- .../client/util/sets/ConstructedFormats.java | 46 +++++++++++-------- .../src/mage/deck/Legacy.java | 14 ++++-- .../src/mage/deck/Vintage.java | 9 +++- .../src/main/java/mage/server/Session.java | 2 +- .../java/mage/cards/decks/Constructed.java | 8 ---- 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java b/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java index cf424ae636f..ab060fd3f2e 100644 --- a/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java +++ b/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java @@ -25,6 +25,10 @@ public class ConstructedFormats { public static final String EXTENDED = "- Extended"; public static final String FRONTIER = "- Frontier"; public static final String MODERN = "- Modern"; + public static final String VINTAGE_LEGACY = "- Vintage / Legacy"; + ; + public static final String CUSTOM = "- Custom"; + ; public static final Standard STANDARD_CARDS = new Standard(); private static final Map> underlyingSetCodesPerFormat = new HashMap<>(); @@ -50,50 +54,49 @@ public class ConstructedFormats { } public static void ensureLists() { - if (getSetsByFormat(ConstructedFormats.STANDARD) == null) { + if (underlyingSetCodesPerFormat.isEmpty()) { buildLists(); } } private static void buildLists() { + underlyingSetCodesPerFormat.put(STANDARD, new ArrayList<>()); + underlyingSetCodesPerFormat.put(EXTENDED, new ArrayList<>()); + underlyingSetCodesPerFormat.put(FRONTIER, new ArrayList<>()); + underlyingSetCodesPerFormat.put(MODERN, new ArrayList<>()); + underlyingSetCodesPerFormat.put(VINTAGE_LEGACY, new ArrayList<>()); + underlyingSetCodesPerFormat.put(CUSTOM, new ArrayList<>()); final Map expansionInfo = new HashMap<>(); formats.clear(); // prevent NPE on sorting if this is not the first try for (ExpansionInfo set : ExpansionRepository.instance.getAll()) { expansionInfo.put(set.getName(), set); formats.add(set.getName()); + + underlyingSetCodesPerFormat.put(set.getName(), new ArrayList<>()); + underlyingSetCodesPerFormat.get(set.getName()).add(set.getCode()); + + // create the play formats + if (set.getType().equals(SetType.CUSTOM_SET)) { + underlyingSetCodesPerFormat.get(CUSTOM).add(set.getCode()); + continue; + } + underlyingSetCodesPerFormat.get(VINTAGE_LEGACY).add(set.getCode()); if (set.getType().equals(SetType.CORE) || set.getType().equals(SetType.EXPANSION) || set.getType().equals(SetType.SUPPLEMENTAL_STANDARD_LEGAL)) { if (STANDARD_CARDS.getSetCodes().contains(set.getCode())) { - if (underlyingSetCodesPerFormat.get(STANDARD) == null) { - underlyingSetCodesPerFormat.put(STANDARD, new ArrayList<>()); - } underlyingSetCodesPerFormat.get(STANDARD).add(set.getCode()); } if (set.getReleaseDate().after(extendedDate)) { - if (underlyingSetCodesPerFormat.get(EXTENDED) == null) { - underlyingSetCodesPerFormat.put(EXTENDED, new ArrayList<>()); - } underlyingSetCodesPerFormat.get(EXTENDED).add(set.getCode()); } if (set.getReleaseDate().after(frontierDate)) { - if (underlyingSetCodesPerFormat.get(FRONTIER) == null) { - underlyingSetCodesPerFormat.put(FRONTIER, new ArrayList<>()); - } underlyingSetCodesPerFormat.get(FRONTIER).add(set.getCode()); } if (set.getReleaseDate().after(modernDate)) { - if (underlyingSetCodesPerFormat.get(MODERN) == null) { - underlyingSetCodesPerFormat.put(MODERN, new ArrayList<>()); - } underlyingSetCodesPerFormat.get(MODERN).add(set.getCode()); } } - if (underlyingSetCodesPerFormat.get(set.getName()) == null) { - underlyingSetCodesPerFormat.put(set.getName(), new ArrayList<>()); - } - - underlyingSetCodesPerFormat.get(set.getName()).add(set.getCode()); - + // Create the Block formats if (set.getType().equals(SetType.EXPANSION) && set.getBlockName() != null) { String blockDisplayName = getBlockDisplayName(set.getBlockName()); if (underlyingSetCodesPerFormat.get(blockDisplayName) == null) { @@ -209,10 +212,13 @@ public class ConstructedFormats { }); if (!formats.isEmpty()) { + formats.add(0, CUSTOM); + formats.add(0, VINTAGE_LEGACY); formats.add(0, MODERN); - formats.add(0, FRONTIER); formats.add(0, EXTENDED); + formats.add(0, FRONTIER); formats.add(0, STANDARD); + } formats.add(0, ALL); } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java index f8c24589b7b..13fc4428630 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java @@ -24,11 +24,13 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.deck; +import mage.cards.ExpansionSet; +import mage.cards.Sets; import mage.cards.decks.Constructed; +import mage.constants.SetType; /** * @@ -38,7 +40,11 @@ public class Legacy extends Constructed { public Legacy() { super("Constructed - Legacy"); - + for (ExpansionSet set : Sets.getInstance().values()) { + if (set.getSetType() != SetType.CUSTOM_SET) { + setCodes.add(set.getCode()); + } + } banned.add("Advantageous Proclamation"); banned.add("Amulet of Quoz"); banned.add("Ancestral Recall"); @@ -112,6 +118,6 @@ public class Legacy extends Constructed { banned.add("Worldknit"); banned.add("Yawgmoth's Bargain"); banned.add("Yawgmoth's Will"); - + } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java index 8646bcf2905..1dcdb14614a 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Vintage.java @@ -27,7 +27,10 @@ */ package mage.deck; +import mage.cards.ExpansionSet; +import mage.cards.Sets; import mage.cards.decks.Constructed; +import mage.constants.SetType; /** * @@ -37,7 +40,11 @@ public class Vintage extends Constructed { public Vintage() { super("Constructed - Vintage"); - + for (ExpansionSet set : Sets.getInstance().values()) { + if (set.getSetType() != SetType.CUSTOM_SET) { + setCodes.add(set.getCode()); + } + } banned.add("Advantageous Proclamation"); banned.add("Amulet of Quoz"); banned.add("Backup Plan"); diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 602281c4e89..d6aabf7a068 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -284,7 +284,7 @@ public class Session { lockSet = true; logger.debug("SESSION LOCK SET sessionId: " + sessionId); } else { - logger.error("CAN'T GET LOCK - userId: " + userId); + logger.error("CAN'T GET LOCK - userId: " + userId + " hold count: " + lock.getHoldCount()); } User user = UserManager.getInstance().getUser(userId); if (user == null || !user.isConnected()) { diff --git a/Mage/src/main/java/mage/cards/decks/Constructed.java b/Mage/src/main/java/mage/cards/decks/Constructed.java index 5851f1ec66c..dc15a1e9cee 100644 --- a/Mage/src/main/java/mage/cards/decks/Constructed.java +++ b/Mage/src/main/java/mage/cards/decks/Constructed.java @@ -48,9 +48,6 @@ public class Constructed extends DeckValidator { protected List setCodes = new ArrayList<>(); protected List rarities = new ArrayList<>(); - protected boolean allowAllCustomSets = false; - protected Set allowedCustomSetCodes = new HashSet<>(); - public Constructed() { super("Constructed"); } @@ -173,12 +170,7 @@ public class Constructed extends DeckValidator { * @return Whether the set is legal in this format. */ protected boolean isSetAllowed(String code) { - // To check here for custom set makes no sens IMHO because the format does define what's aloowed and what not -// if (Sets.isCustomSet(code)) { -// return allowAllCustomSets || allowedCustomSetCodes.contains(code); -// } else { return setCodes.isEmpty() || setCodes.contains(code); -// } } /** From 01ca3689b3831e6b1fa1c9b2b29472de05973375 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 3 Oct 2016 20:25:17 +0200 Subject: [PATCH 06/13] Some minor changes. --- Mage.Common/src/mage/view/AbilityPickerView.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Mage.Common/src/mage/view/AbilityPickerView.java b/Mage.Common/src/mage/view/AbilityPickerView.java index 489883f31af..b0572c0bb42 100644 --- a/Mage.Common/src/mage/view/AbilityPickerView.java +++ b/Mage.Common/src/mage/view/AbilityPickerView.java @@ -24,8 +24,7 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.view; import java.io.Serializable; @@ -40,12 +39,13 @@ import mage.abilities.Ability; * @author BetaSteward_at_googlemail.com */ public class AbilityPickerView implements Serializable { + private static final long serialVersionUID = 1L; - private Map choices = new LinkedHashMap(); + private Map choices = new LinkedHashMap<>(); public AbilityPickerView(String objectName, List abilities) { - for (Ability ability: abilities) { + for (Ability ability : abilities) { if (objectName == null) { choices.put(ability.getId(), ability.getRule(true)); } else { From e8ab8558d1c73311dafb8118f785597ee9aa0276 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 3 Oct 2016 22:07:10 +0200 Subject: [PATCH 07/13] Fixed some possible null pointer exceptions. --- Mage.Common/src/mage/view/PlayerView.java | 2 +- .../java/mage/abilities/effects/common/DynamicManaEffect.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Common/src/mage/view/PlayerView.java b/Mage.Common/src/mage/view/PlayerView.java index d64d2cb41e5..9752a89a8d8 100644 --- a/Mage.Common/src/mage/view/PlayerView.java +++ b/Mage.Common/src/mage/view/PlayerView.java @@ -125,7 +125,7 @@ public class PlayerView implements Serializable { } catch (ConcurrentModificationException e) { // can happen as a player left battlefield while PlayerView is created } - this.topCard = player.isTopCardRevealed() && player.getLibrary().size() > 0 + this.topCard = (player.isTopCardRevealed() && player.getLibrary().size() > 0) ? new CardView(player.getLibrary().getFromTop(game)) : null; if (player.getUserData() != null) { this.userData = player.getUserData(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/DynamicManaEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DynamicManaEffect.java index 3ab765c7cce..7cfb2118115 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DynamicManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DynamicManaEffect.java @@ -146,7 +146,7 @@ public class DynamicManaEffect extends BasicManaEffect { } else { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - ChoiceColor choiceColor = new ChoiceColor(); + ChoiceColor choiceColor = new ChoiceColor(true); for (int i = 0; i < count; i++) { if (!choiceColor.isChosen()) { while (!controller.choose(Outcome.Benefit, choiceColor, game)) { From 7cbb3df889e65d091afb46a68b9c6ef4b551bf4b Mon Sep 17 00:00:00 2001 From: fireshoes Date: Mon, 3 Oct 2016 16:39:11 -0500 Subject: [PATCH 08/13] Fixed that Eternal format matches did not show in the matchmaking lobby. --- Mage.Client/src/main/java/mage/client/table/TablesPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 30e67c53b4f..9e366c2ceaf 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -602,7 +602,7 @@ public class TablesPanel extends javax.swing.JPanel { formatFilterList.add(RowFilter.regexFilter("^Limited", TableTableModel.COLUMN_DECK_TYPE)); } if (btnFormatOther.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform", TableTableModel.COLUMN_DECK_TYPE)); + formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Eternal|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform", TableTableModel.COLUMN_DECK_TYPE)); } List> skillFilterList = new ArrayList<>(); From 4d3f28ed6d97a0c9c93b4e540b38790faf1eeeb0 Mon Sep 17 00:00:00 2001 From: spjspj Date: Tue, 4 Oct 2016 10:01:41 +1100 Subject: [PATCH 09/13] spjspj - one more token --- .../mage/plugins/card/dl/sources/MtgOnlTokensImageSource.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgOnlTokensImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgOnlTokensImageSource.java index acc2f83e027..cf09fcefb59 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgOnlTokensImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgOnlTokensImageSource.java @@ -177,6 +177,7 @@ public class MtgOnlTokensImageSource implements CardImageSource { copyUrlToImage.put("Elemental_BR_5_5.jpg", "ELEMENTAL.BR.ELEMENTAL.CREATURE.1.1.full.jpg"); copyUrlToImage.put("Elemental_GW_y_y.jpg", "ELEMENTAL.WG.ELEMENTAL.CREATURE.S.S.full.jpg"); copyUrlToImage.put("Elemental_G_2_2.jpg", "ELEMENTAL.G.ELEMENTAL.CREATURE.2.2.full.jpg"); + copyUrlToImage.put("Elemental_R_3_1.jpg", "ELEMENTAL.R.ELEMENTAL.CREATURE.3.1.full.jpg"); copyUrlToImage.put("Elemental_G_4_4.jpg", "ELEMENTAL.G.ELEMENTAL.CREATURE.4.4.full.jpg"); copyUrlToImage.put("Elemental_G_5_3.jpg", "ELEMENTAL.G.ELEMENTAL.CREATURE.5.3.full.jpg"); copyUrlToImage.put("Elemental_G_7_7.jpg", "ELEMENTAL.G.ELEMENTAL.CREATURE.7.7.full.jpg"); From ae41a5fbbf90332436664b37866035e2ab48b378 Mon Sep 17 00:00:00 2001 From: fireshoes Date: Tue, 4 Oct 2016 00:24:45 -0500 Subject: [PATCH 10/13] Changed Duel Commander starting life total to 20. --- .../src/mage/game/CommanderDuelMatch.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Game.CommanderDuel/src/mage/game/CommanderDuelMatch.java b/Mage.Server.Plugins/Mage.Game.CommanderDuel/src/mage/game/CommanderDuelMatch.java index c20af5e9bf3..1090c4bd645 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderDuel/src/mage/game/CommanderDuelMatch.java +++ b/Mage.Server.Plugins/Mage.Game.CommanderDuel/src/mage/game/CommanderDuelMatch.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. @@ -47,7 +47,7 @@ public class CommanderDuelMatch extends MatchImpl { boolean alsoHand = true; // Don't like it to compare but seems like it's complicated to do it in another way if (options.getDeckType().equals("Variant Magic - Duel Commander")) { - startLife = 30; + startLife = 20; // Starting with the Commander 2016 update (on November 11th, 2016), Duel Commander will be played with 20 life points instead of 30. alsoHand = true; // commander going to hand allowed to go to command zone effective July 17, 2015 } CommanderDuel game = new CommanderDuel(options.getAttackOption(), options.getRange(), options.getFreeMulligans(), startLife); From f6d50ce04f1471479df1047677d6424a119fa53b Mon Sep 17 00:00:00 2001 From: Mark Langen Date: Tue, 4 Oct 2016 00:04:13 -0600 Subject: [PATCH 11/13] Various new Drag & Drop deck editor improvements * Shift-Click / Shift-Drag now work as expected as far as multi-selection * Deck editor saves split pane split positions * Card layout and sort settings are now saved along side the a deck when saving to the .dck format, so that you have back the exact same deck layout when you re-load the deck. * Fixed the symbol image downloader to work around some of the large-size symbol images being missing on gatherer. Falls back to the medium sized images currently for those symbols. --- .../java/mage/client/cards/DragCardGrid.java | 439 +++++++++--------- .../java/mage/client/deckeditor/DeckArea.java | 70 ++- .../client/deckeditor/DeckEditorPanel.java | 62 ++- .../mage/client/dialog/PreferencesDialog.java | 4 + .../org/mage/card/arcane/ManaSymbols.java | 8 +- .../card/dl/sources/GathererSymbols.java | 24 +- Mage/src/main/java/mage/cards/Sets.java | 32 ++ Mage/src/main/java/mage/cards/decks/Deck.java | 12 + .../java/mage/cards/decks/DeckCardLayout.java | 24 + .../java/mage/cards/decks/DeckCardLists.java | 20 + .../cards/decks/importer/DckDeckImporter.java | 59 +++ 11 files changed, 516 insertions(+), 238 deletions(-) create mode 100644 Mage/src/main/java/mage/cards/decks/DeckCardLayout.java diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index fa206cb0f63..a04d2e963cd 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -2,8 +2,12 @@ package mage.client.cards; import mage.cards.MageCard; import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLayout; import mage.cards.decks.importer.DeckImporterUtil; +import mage.cards.repository.CardInfo; import mage.client.MageFrame; +import mage.client.deckeditor.DeckArea; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.*; @@ -26,7 +30,12 @@ import java.awt.*; import java.awt.event.*; import java.io.File; import java.io.IOException; +import java.io.Serializable; import java.util.*; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Created by StravantUser on 2016-09-20. @@ -379,6 +388,23 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg cardContent.repaint(); } + public DeckCardLayout getCardLayout() { + // 2D Array to put entries into + List>> info = new ArrayList<>(); + for (ArrayList> gridRow : cardGrid) { + List> row = new ArrayList<>(); + info.add(row); + for (ArrayList stack : gridRow) { + row.add(stack.stream() + .map(card -> new DeckCardInfo(card.getName(), card.getCardNumber(), card.getExpansionSetCode())) + .collect(Collectors.toList())); + } + } + + // Store layout and settings then return them + return new DeckCardLayout(info, saveSettings().toString()); + } + public enum Sort { NONE("No Sort", new Comparator() { @Override @@ -476,6 +502,8 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg JPopupMenu sortPopup; JCheckBox separateCreaturesCb; + Map sortButtons = new HashMap<>(); + JLabel deckNameAndCountLabel; JLabel landCountLabel; JLabel creatureCountLabel; @@ -489,6 +517,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Card area selection panel SelectionBox selectionPanel; + Set selectionDragStartCards; int selectionDragStartX; int selectionDragStartY; @@ -526,6 +555,55 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg private String name; } + public static class Settings { + public Sort sort; + public boolean separateCreatures; + + private static Pattern parser = Pattern.compile("\\(([^,]*),([^)]*)\\)"); + + public static Settings parse(String str) { + Matcher m = parser.matcher(str); + if (m.find()) { + Settings s = new Settings(); + s.sort = Sort.valueOf(m.group(1)); + s.separateCreatures = Boolean.valueOf(m.group(2)); + return s; + } else { + return null; + } + } + + @Override + public String toString() { + return "(" + sort.toString() + "," + Boolean.toString(separateCreatures) + ")"; + } + } + + public Settings saveSettings() { + Settings s = new Settings(); + s.sort = cardSort; + s.separateCreatures = separateCreatures; + return s; + } + + public void loadSettings(Settings s) { + if (s != null) { + setSort(s.sort); + setSeparateCreatures(s.separateCreatures); + resort(); + } + } + + public void setSeparateCreatures(boolean state) { + separateCreatures = state; + separateCreaturesCb.setSelected(state); + } + + public void setSort(Sort s) { + cardSort = s; + sortButtons.get(s).setSelected(true); + } + // Constructor public DragCardGrid() { // Make sure that the card grid is populated with at least one (empty) stack to begin with @@ -540,22 +618,6 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg filterButton = new JButton("Filter"); visibilityButton = new JButton("Visibility"); - addFocusListener(new FocusAdapter() { - @Override - public void focusLost(FocusEvent e) { - deselectAll(); - } - }); - - // Tmp load button - JButton loadButton = new JButton("Load"); - loadButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - loadDeck(); - } - }); - // Name and count label deckNameAndCountLabel = new JLabel(); @@ -576,25 +638,21 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg toolbarInner.add(sortButton); toolbarInner.add(filterButton); toolbarInner.add(visibilityButton); - toolbarInner.add(loadButton); toolbar.add(toolbarInner, BorderLayout.WEST); JPanel sliderPanel = new JPanel(new GridBagLayout()); sliderPanel.setOpaque(false); final JSlider sizeSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 50); sizeSlider.setOpaque(false); sizeSlider.setPreferredSize(new Dimension(100, (int)sizeSlider.getPreferredSize().getHeight())); - sizeSlider.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - if (!sizeSlider.getValueIsAdjusting()) { - // Fraction in [-1, 1] - float sliderFrac = ((float) (sizeSlider.getValue() - 50)) / 50; - // Convert to frac in [0.5, 2.0] exponentially - cardSizeMod = (float) Math.pow(2, sliderFrac); - // Update grid - layoutGrid(); - cardContent.repaint(); - } + sizeSlider.addChangeListener(e -> { + if (!sizeSlider.getValueIsAdjusting()) { + // Fraction in [-1, 1] + float sliderFrac = ((float) (sizeSlider.getValue() - 50)) / 50; + // Convert to frac in [0.5, 2.0] exponentially + cardSizeMod = (float) Math.pow(2, sliderFrac); + // Update grid + layoutGrid(); + cardContent.repaint(); } }); sliderPanel.add(new JLabel("Card Size:")); @@ -610,9 +668,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg private boolean isDragging = false; @Override public void mousePressed(MouseEvent e) { - isDragging = true; - beginSelectionDrag(e.getX(), e.getY()); - updateSelectionDrag(e.getX(), e.getY()); + if (SwingUtilities.isLeftMouseButton(e)) { + isDragging = true; + beginSelectionDrag(e.getX(), e.getY(), e.isShiftDown()); + updateSelectionDrag(e.getX(), e.getY()); + } } @Override public void mouseReleased(MouseEvent e) { @@ -673,15 +733,13 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg if (s == cardSort) { button.setSelected(true); } + sortButtons.put(s, button); sortMode.add(button); sortModeGroup.add(button); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - cardSort = s; - PreferencesDialog.saveValue(PreferencesDialog.KEY_DECK_EDITOR_LAST_SORT, s.toString()); - resort(); - } + button.addActionListener(e -> { + cardSort = s; + PreferencesDialog.saveValue(PreferencesDialog.KEY_DECK_EDITOR_LAST_SORT, s.toString()); + resort(); }); } @@ -695,13 +753,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg separateCreaturesCb = new JCheckBox(); separateCreaturesCb.setText("Creatures in separate row"); separateCreaturesCb.setSelected(separateCreatures); - separateCreaturesCb.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - separateCreatures = separateCreaturesCb.isSelected(); - PreferencesDialog.saveValue(PreferencesDialog.KEY_DECK_EDITOR_LAST_SEPARATE_CREATURES, Boolean.toString(separateCreatures)); - resort(); - } + separateCreaturesCb.addItemListener(e -> { + setSeparateCreatures(separateCreaturesCb.isSelected()); + PreferencesDialog.saveValue(PreferencesDialog.KEY_DECK_EDITOR_LAST_SEPARATE_CREATURES, Boolean.toString(separateCreatures)); + resort(); }); sortOptions.add(separateCreaturesCb); @@ -714,20 +769,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg { final JPopupMenu visPopup = new JPopupMenu(); JMenuItem hideSelected = new JMenuItem("Hide selected"); - hideSelected.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - hideSelection(); - } - }); + hideSelected.addActionListener(e -> hideSelection()); visPopup.add(hideSelected); JMenuItem showAll = new JMenuItem("Show all"); - showAll.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showAll(); - } - }); + showAll.addActionListener(e -> showAll()); visPopup.add(showAll); visibilityButton.addMouseListener(new MouseAdapter() { @Override @@ -743,7 +788,6 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg makeButtonPopup(filterButton, filterPopup); filterButton.setVisible(false); - loadButton.setVisible(false); // Right click in card area initCardAreaPopup(); @@ -756,44 +800,29 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg final JPopupMenu menu = new JPopupMenu(); final JMenuItem hideSelected = new JMenuItem("Hide selected"); - hideSelected.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - hideSelection(); - } - }); + hideSelected.addActionListener(e -> hideSelection()); menu.add(hideSelected); JMenuItem showAll = new JMenuItem("Show all"); - showAll.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showAll(); - } - }); + showAll.addActionListener(e -> showAll()); menu.add(showAll); JMenu sortMenu = new JMenu("Sort by..."); + final Map sortMenuItems = new LinkedHashMap<>(); for (final Sort sort : Sort.values()) { - JMenuItem subSort = new JMenuItem(sort.getText()); - subSort.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - cardSort = sort; - resort(); - } + JMenuItem subSort = new JCheckBoxMenuItem(sort.getText()); + sortMenuItems.put(sort, subSort); + subSort.addActionListener(e -> { + cardSort = sort; + resort(); }); sortMenu.add(subSort); } sortMenu.add(new JPopupMenu.Separator()); final JCheckBoxMenuItem separateButton = new JCheckBoxMenuItem("Separate creatures"); - separateButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - separateCreatures = !separateCreatures; - separateCreaturesCb.setSelected(separateCreatures); - resort(); - } + separateButton.addActionListener(e -> { + setSeparateCreatures(!separateCreatures); + resort(); }); sortMenu.add(separateButton); menu.add(sortMenu); @@ -803,6 +832,9 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg @Override public void mouseClicked(MouseEvent e) { if (SwingUtilities.isRightMouseButton(e)) { + for (Sort s : sortMenuItems.keySet()) { + sortMenuItems.get(s).setSelected(cardSort == s); + } hideSelected.setEnabled(dragCardList().size() > 0); separateButton.setSelected(separateCreatures); menu.show(e.getComponent(), e.getX(), e.getY()); @@ -843,7 +875,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg /** * Selection drag handling */ - private void beginSelectionDrag(int x, int y) { + private void beginSelectionDrag(int x, int y, boolean shiftHeld) { // Show the selection panel selectionPanel.setVisible(true); selectionPanel.setLocation(x, y); @@ -853,6 +885,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg selectionDragStartX = x; selectionDragStartY = y; + // Store the starting cards to include in the selection + selectionDragStartCards = new HashSet<>(); + if (shiftHeld) { + selectionDragStartCards.addAll(dragCardList()); + } + // Notify selection notifyCardsSelected(); } @@ -904,7 +942,8 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg boolean inBoundsX = (col >= col1 && col <= col2); boolean inBoundsY = (i >= stackStartIndex && i <= stackEndIndex); boolean lastCard = (i == stack.size()-1); - if (inBoundsX && (inBoundsY || (lastCard && (y2 >= stackBottomBegin && y1 <= stackBottomEnd)))) { + boolean inSeletionDrag = inBoundsX && (inBoundsY || (lastCard && (y2 >= stackBottomBegin && y1 <= stackBottomEnd))); + if (inSeletionDrag || selectionDragStartCards.contains(card)) { if (!card.isSelected()) { card.setSelected(true); view.update(card); @@ -921,42 +960,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } } - private void endSelectionDrag(int x, int y) { + private void endSelectionDrag(@SuppressWarnings("unused") int x, @SuppressWarnings("unused") int y) { // Hide the selection panel selectionPanel.setVisible(false); } - - private void loadDeck() { - JFileChooser fcSelectDeck = new JFileChooser(); - String lastFolder = MageFrame.getPreferences().get("lastDeckFolder", ""); - if (!lastFolder.isEmpty()) { - fcSelectDeck.setCurrentDirectory(new File(lastFolder)); - } - int ret = fcSelectDeck.showOpenDialog(DragCardGrid.this); - if (ret == JFileChooser.APPROVE_OPTION) { - File file = fcSelectDeck.getSelectedFile(); - try { - setCursor(new Cursor(Cursor.WAIT_CURSOR)); - Deck deck = Deck.load(DeckImporterUtil.importDeck(file.getPath()), true, true); - setCards(new CardsView(deck.getCards()), null); - } catch (GameException ex) { - JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE); - } catch (Exception ex) { - ex.printStackTrace(); - } finally { - setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - try { - if (file != null) { - MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath()); - } - } catch (IOException ex) { - } - } - fcSelectDeck.setSelectedFile(null); - } - // Resort the existing cards based on the current sort public void resort() { // First null out the grid and trim it down @@ -984,7 +992,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } // Update the contents of the card grid - public void setCards(CardsView cardsView, BigCard bigCard) { + public void setCards(CardsView cardsView, DeckCardLayout layout, BigCard bigCard) { if (bigCard != null) { lastBigCard = bigCard; } @@ -1014,20 +1022,89 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg trimGrid(); } - // Add any new card views - for (CardView newCard: cardsView.values()) { - if (!cardViews.containsKey(newCard.getId())) { - // Is a new card - addCardView(newCard); + if (layout == null) { + // No layout -> add any new card views one at a time as par the current sort + for (CardView newCard: cardsView.values()) { + if (!cardViews.containsKey(newCard.getId())) { + // Is a new card + addCardView(newCard); - try { // Put it into the appropirate place in the grid given the current sort sortIntoGrid(newCard); - } catch (Exception e) { - e.printStackTrace(); + + // Mark + didModify = true; + } + } + } else { + // Layout given -> Build card grid using layout, and set sort / separate + + // Always modify when given a layout + didModify = true; + + // Load in settings + loadSettings(Settings.parse(layout.getSettings())); + + // Traverse the cards once and track them so we can pick ones to insert into the grid + Map>> trackedCards = new HashMap<>(); + for (CardView newCard: cardsView.values()) { + if (!cardViews.containsKey(newCard.getId())) { + // Add the new card + addCardView(newCard); + + // Add the new card to tracking + Map> forSetCode; + if (trackedCards.containsKey(newCard.getExpansionSetCode())) { + forSetCode = trackedCards.get(newCard.getExpansionSetCode()); + } else { + forSetCode = new HashMap<>(); + trackedCards.put(newCard.getExpansionSetCode(), forSetCode); + } + ArrayList list; + if (forSetCode.containsKey(newCard.getCardNumber())) { + list = forSetCode.get(newCard.getCardNumber()); + } else { + list = new ArrayList<>(); + forSetCode.put(newCard.getCardNumber(), list); + } + list.add(newCard); + } + } + + // Now go through the layout and use it to build the cardGrid + cardGrid = new ArrayList<>(); + maxStackSize = new ArrayList<>(); + for (List> row : layout.getCards()) { + ArrayList> gridRow = new ArrayList<>(); + int thisMaxStackSize = 0; + cardGrid.add(gridRow); + for (List stack : row) { + ArrayList gridStack = new ArrayList<>(); + gridRow.add(gridStack); + for (DeckCardInfo info : stack) { + if (trackedCards.containsKey(info.getSetCode()) && trackedCards.get(info.getSetCode()).containsKey(info.getCardNum())) { + ArrayList candidates = + trackedCards.get(info.getSetCode()).get(info.getCardNum()); + if (candidates.size() > 0) { + gridStack.add(candidates.remove(0)); + thisMaxStackSize = Math.max(thisMaxStackSize, gridStack.size()); + } + } + } + } + maxStackSize.add(thisMaxStackSize); + } + + // Check that there aren't any "orphans" not referenced in the layout. There should + // never be any under normal operation, but as a failsafe in case the user screwed with + // the file in an invalid way, sort them into the grid so that they aren't just left hanging. + for (Map> tracked : trackedCards.values()) { + for (ArrayList orphans : tracked.values()) { + for (CardView orphan : orphans) { + LOGGER.info("Orphan when setting with layout: "); + sortIntoGrid(orphan); + } } - // Mark - didModify = true; } } @@ -1048,18 +1125,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg landCountLabel.setText("" + landCounter.get()); } - private void showCardRightClickMenu(final CardView card, MouseEvent e) { + private void showCardRightClickMenu(@SuppressWarnings("unused") final CardView card, MouseEvent e) { JPopupMenu menu = new JPopupMenu(); JMenuItem hide = new JMenuItem("Hide"); - hide.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - //if (card.isSelected() && dragCardList().size() > 1) { - // Hide all selected - hideSelection(); - //} - } - }); + hide.addActionListener(e2 -> hideSelection()); menu.add(hide); menu.show(e.getComponent(), e.getX(), e.getY()); } @@ -1156,8 +1225,17 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } } + private void toggleSelected(CardView targetCard) { + targetCard.setSelected(!targetCard.isSelected()); + cardViews.get(targetCard.getId()).update(targetCard); + } + private void cardClicked(CardView targetCard, MouseEvent e) { - selectCard(targetCard); + if (e.isShiftDown()) { + toggleSelected(targetCard); + } else { + selectCard(targetCard); + } notifyCardsSelected(); } @@ -1176,12 +1254,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg /** * Add a card to the cardGrid, in the position that the current sort dictates - * @param newCard + * @param newCard Card to add to the cardGrid array. */ private void sortIntoGrid(CardView newCard) { // Ensure row 1 exists if (cardGrid.size() == 0) { - cardGrid.add(0, new ArrayList>()); + cardGrid.add(0, new ArrayList<>()); maxStackSize.add(0, 0); } // What row to add it to? @@ -1189,11 +1267,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg if (separateCreatures && !newCard.getCardTypes().contains(CardType.CREATURE)) { // Ensure row 2 exists if (cardGrid.size() < 2) { - cardGrid.add(1, new ArrayList>()); + cardGrid.add(1, new ArrayList<>()); maxStackSize.add(1, 0); // Populate with stacks matching the first row for (int i = 0; i < cardGrid.get(0).size(); ++i) { - cardGrid.get(1).add(new ArrayList()); + cardGrid.get(1).add(new ArrayList<>()); } } targetRow = cardGrid.get(1); @@ -1223,7 +1301,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Insert into this col, but if less, then we need to create a new col here first if (res < 0) { for (int rowIndex = 0; rowIndex < cardGrid.size(); ++rowIndex) { - cardGrid.get(rowIndex).add(currentColumn, new ArrayList()); + cardGrid.get(rowIndex).add(currentColumn, new ArrayList<>()); } } targetRow.get(currentColumn).add(newCard); @@ -1238,7 +1316,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // If nothing else, insert in a new column after everything else if (!didInsert) { for (int rowIndex = 0; rowIndex < cardGrid.size(); ++rowIndex) { - cardGrid.get(rowIndex).add(new ArrayList()); + cardGrid.get(rowIndex).add(new ArrayList<>()); } targetRow.get(targetRow.size()-1).add(newCard); } @@ -1335,7 +1413,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Stack count label if (stackCountLabels.size() <= rowIndex) { - stackCountLabels.add(new ArrayList()); + stackCountLabels.add(new ArrayList<>()); } if (stackCountLabels.get(rowIndex).size() <= colIndex) { JLabel countLabel = new JLabel("", SwingConstants.CENTER); @@ -1379,70 +1457,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } private static void makeButtonPopup(final AbstractButton button, final JPopupMenu popup) { - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - popup.show(button, 0, button.getHeight()); - } - }); - } - - static class DeckFilter extends FileFilter { - @Override - public boolean accept(File f) { - if (f.isDirectory()) { - return true; - } - - String ext = null; - String s = f.getName(); - int i = s.lastIndexOf('.'); - - if (i > 0 && i < s.length() - 1) { - ext = s.substring(i + 1).toLowerCase(); - } - return (ext == null) ? false : ext.equals("dck"); - } - - @Override - public String getDescription() { - return "Deck Files"; - } - } - - public static void main(String[] args) { - JFrame frame = new JFrame(); - /* - GUISizeHelper.calculateGUISizes(); - Plugins.getInstance().loadPlugins(); - frame.setTitle("Test"); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - frame.setBackground(Color.BLUE); - DragCardGrid grid = new DragCardGrid(); - grid.setPreferredSize(new Dimension(800, 600)); - */ - try { - UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); - } catch (UnsupportedLookAndFeelException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {} - frame.setVisible(true); - JFileChooser choose = new JFileChooser(); - choose.setAcceptAllFileFilterUsed(false); - choose.addChoosableFileFilter(new DeckFilter()); - choose.showOpenDialog(frame); - LOGGER.info("File: " + choose.getSelectedFile()); - String st = ""; - try { - st = choose.getSelectedFile().getCanonicalPath(); - } catch (Exception e) { - e.printStackTrace(); - } - LOGGER.info("Selected file: " + st); - choose.setSelectedFile(new File(st)); - choose.showOpenDialog(frame); - LOGGER.info("File: " + choose.getSelectedFile()); - //frame.add(grid, BorderLayout.CENTER); - //frame.pack(); - frame.setVisible(false); + button.addActionListener(e -> popup.show(button, 0, button.getHeight())); } } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java index 68dd871c894..4040e1658e9 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java @@ -34,6 +34,9 @@ package mage.client.deckeditor; import mage.cards.Card; import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLayout; +import mage.cards.decks.DeckCardLists; import mage.client.cards.BigCard; import mage.client.cards.CardEventSource; import mage.client.cards.DragCardGrid; @@ -43,9 +46,12 @@ import mage.client.util.GUISizeHelper; import mage.client.util.Listener; import mage.view.CardView; import mage.view.CardsView; +import org.apache.log4j.Logger; import javax.swing.*; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @@ -59,6 +65,40 @@ public class DeckArea extends javax.swing.JPanel { private Deck lastDeck = new Deck(); private BigCard lastBigCard = null; + public DeckCardLayout getCardLayout() { + return deckList.getCardLayout(); + } + + public DeckCardLayout getSideboardLayout() { + return sideboardList.getCardLayout(); + } + + public static class Settings { + public DragCardGrid.Settings maindeckSettings; + public DragCardGrid.Settings sideboardSetings; + public int dividerLocation; + + private static Pattern parser = Pattern.compile("([^|]*)\\|([^|]*)\\|([^|]*)"); + + public static Settings parse(String s) { + Matcher m = parser.matcher(s); + if (m.find()) { + Settings settings = new Settings(); + settings.maindeckSettings = DragCardGrid.Settings.parse(m.group(1)); + settings.sideboardSetings = DragCardGrid.Settings.parse(m.group(2)); + settings.dividerLocation = Integer.parseInt(m.group(3)); + return settings; + } else { + return null; + } + } + + @Override + public String toString() { + return maindeckSettings.toString() + "|" + sideboardSetings.toString() + "|" + dividerLocation; + } + } + /** * Creates new form DeckArea */ @@ -117,6 +157,22 @@ public class DeckArea extends javax.swing.JPanel { }); } + public Settings saveSettings() { + Settings settings = new Settings(); + settings.maindeckSettings = deckList.saveSettings(); + settings.sideboardSetings = sideboardList.saveSettings(); + settings.dividerLocation = deckAreaSplitPane.getDividerLocation(); + return settings; + } + + public void loadSettings(Settings s) { + if (s != null) { + deckList.loadSettings(s.maindeckSettings); + sideboardList.loadSettings(s.sideboardSetings); + deckAreaSplitPane.setDividerLocation(s.dividerLocation); + } + } + public void cleanUp() { deckList.cleanUp(); sideboardList.cleanUp(); @@ -161,11 +217,21 @@ public class DeckArea extends javax.swing.JPanel { } public void loadDeck(Deck deck, BigCard bigCard) { + loadDeck(deck, false, bigCard); + } + + public void loadDeck(Deck deck, boolean useLayout, BigCard bigCard) { lastDeck = deck; lastBigCard = bigCard; - deckList.setCards(new CardsView(filterHidden(lastDeck.getCards())), lastBigCard); + deckList.setCards( + new CardsView(filterHidden(lastDeck.getCards())), + useLayout ? deck.getCardsLayout() : null, + lastBigCard); if (sideboardList.isVisible()) { - sideboardList.setCards(new CardsView(filterHidden(lastDeck.getSideboard())), lastBigCard); + sideboardList.setCards( + new CardsView(filterHidden(lastDeck.getSideboard())), + useLayout ? deck.getSideboardLayout() : null, + lastBigCard); } } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index a6ab56a1c66..a040df0464c 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -28,10 +28,7 @@ package mage.client.deckeditor; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; +import java.awt.event.*; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -50,6 +47,7 @@ import javax.swing.filechooser.FileFilter; import mage.cards.Card; import mage.cards.Sets; import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardLists; import mage.cards.decks.importer.DeckImporter; import mage.cards.decks.importer.DeckImporterUtil; import mage.cards.repository.CardInfo; @@ -62,6 +60,7 @@ import mage.client.constants.Constants.DeckEditorMode; import mage.client.deck.generator.DeckGenerator.DeckGeneratorException; import mage.client.deck.generator.DeckGenerator; import mage.client.dialog.AddLandDialog; +import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.Event; import mage.client.util.Listener; @@ -106,12 +105,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { deckArea.setOpaque(false); jPanel1.setOpaque(false); jSplitPane1.setOpaque(false); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - jSplitPane1.setDividerLocation(0.3); - } - }); + restoreDividerLocationsAndDeckAreaSettings(); countdown = new Timer(1000, new ActionListener() { @Override @@ -128,12 +122,22 @@ public class DeckEditorPanel extends javax.swing.JPanel { } } }); + + // Set up tracking to save the deck editor settings when the deck editor is hidden. + addHierarchyListener((HierarchyEvent e) -> { + if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) { + if (!isShowing()) { + saveDividerLocationsAndDeckAreaSettings(); + } + } + }); } /** * Free resources so GC can remove unused objects from memory */ public void cleanUp() { + saveDividerLocationsAndDeckAreaSettings(); if (updateDeckTask != null) { updateDeckTask.cancel(true); } @@ -152,6 +156,24 @@ public class DeckEditorPanel extends javax.swing.JPanel { this.bigCard = null; } + private void saveDividerLocationsAndDeckAreaSettings() { + PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION, Integer.toString(jSplitPane1.getDividerLocation())); + PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, this.deckArea.saveSettings().toString()); + } + + private void restoreDividerLocationsAndDeckAreaSettings() { + // Load horizontal split position setting + String dividerLocation = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION, ""); + if (!dividerLocation.isEmpty()) { + jSplitPane1.setDividerLocation(Integer.parseInt(dividerLocation)); + } + + // Load deck area settings + this.deckArea.loadSettings( + DeckArea.Settings.parse( + PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, ""))); + } + public void changeGUISize() { this.cardSelector.changeGUISize(); this.deckArea.changeGUISize(); @@ -556,10 +578,14 @@ public class DeckEditorPanel extends javax.swing.JPanel { } private void refreshDeck() { + refreshDeck(false); + } + + private void refreshDeck(boolean useLayout) { try { setCursor(new Cursor(Cursor.WAIT_CURSOR)); this.txtDeckName.setText(deck.getName()); - deckArea.loadDeck(deck, bigCard); + deckArea.loadDeck(deck, useLayout, bigCard); } finally { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } @@ -615,13 +641,6 @@ public class DeckEditorPanel extends javax.swing.JPanel { jSplitPane1.setTopComponent(cardSelector); jSplitPane1.setBottomComponent(deckArea); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - jSplitPane1.setDividerLocation(0.6); - } - }); - bigCard.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); cardInfoPane = Plugins.getInstance().getCardInfoPane(); @@ -878,7 +897,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { } finally { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } - refreshDeck(); + refreshDeck(true); try { if (file != null) { MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath()); @@ -919,7 +938,10 @@ public class DeckEditorPanel extends javax.swing.JPanel { fileName += ".dck"; } setCursor(new Cursor(Cursor.WAIT_CURSOR)); - Sets.saveDeck(fileName, deck.getDeckCardLists()); + DeckCardLists cardLists = deck.getDeckCardLists(); + cardLists.setCardLayout(deckArea.getCardLayout()); + cardLists.setSideboardLayout(deckArea.getSideboardLayout()); + Sets.saveDeck(fileName, cardLists); } catch (FileNotFoundException ex) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage() + "\nTry ensuring that the selected directory is writable.", "Error saving deck", JOptionPane.ERROR_MESSAGE); } finally { diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 4f322571801..57ab43e6e32 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -185,6 +185,10 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_TABLES_DIVIDER_LOCATION_2 = "tablePanelDividerLocation2"; public static final String KEY_TABLES_DIVIDER_LOCATION_3 = "tablePanelDividerLocation3"; + // Positions of deck editor divider bars + public static final String KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION = "editorHorizontalDividerLocation"; + public static final String KEY_EDITOR_DECKAREA_SETTINGS = "editorDeckAreaSettings"; + // user list public static final String KEY_USERS_COLUMNS_WIDTH = "userPanelColumnWidth"; public static final String KEY_USERS_COLUMNS_ORDER = "userPanelColumnSort"; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java index d61c388b4fc..abcd2656d31 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -153,11 +153,13 @@ public class ManaSymbols { sizedSymbols.put(symbol, notResized); } else { Rectangle r = new Rectangle(size, size); - Image image = UI.getImageIcon(file.getAbsolutePath()).getImage(); - BufferedImage resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r); + //Image image = UI.getImageIcon(file.getAbsolutePath()).getImage(); + BufferedImage image = ImageIO.read(file); + //BufferedImage resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r); + BufferedImage resized = ImageHelper.getResizedImage(image, r); sizedSymbols.put(symbol, resized); } - } catch (Exception e) { + } catch (IOException e) { LOGGER.error("Error for symbol:" + symbol); fileErrors = true; } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java index 7aaaa9d0ad0..485582d6a4f 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSymbols.java @@ -73,6 +73,28 @@ public class GathererSymbols implements Iterable { String symbol = sym.replaceAll("/", ""); File dst = new File(dir, symbol + ".gif"); + /** + * Handle a bug on Gatherer where a few symbols are missing at the large size. + * Fall back to using the medium symbol for those cases. + */ + int modSizeIndex = sizeIndex; + if (sizeIndex == 2) { + switch (sym) { + case "WP": + case "UP": + case "BP": + case "RP": + case "GP": + case "E": + case "C": + modSizeIndex = 1; + break; + + default: + // Nothing to do, symbol is available in the large size + } + } + switch (symbol) { case "T": symbol = "tap"; @@ -85,7 +107,7 @@ public class GathererSymbols implements Iterable { break; } - String url = format(urlFmt, sizes[sizeIndex], symbol); + String url = format(urlFmt, sizes[modSizeIndex], symbol); return new DownloadJob(sym, fromURL(url), toFile(dst)); } diff --git a/Mage/src/main/java/mage/cards/Sets.java b/Mage/src/main/java/mage/cards/Sets.java index 642bef29bb3..ee5f2ba64a3 100644 --- a/Mage/src/main/java/mage/cards/Sets.java +++ b/Mage/src/main/java/mage/cards/Sets.java @@ -33,6 +33,7 @@ import java.io.PrintWriter; import java.util.*; import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLayout; import mage.cards.decks.DeckCardLists; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; @@ -166,16 +167,47 @@ public class Sets extends HashMap { } } + // Write out all of the cards for (Map.Entry entry: deckCards.entrySet()) { out.printf("%d [%s:%s] %s%n", entry.getValue().getQuantity(), entry.getValue().getSetCode(), entry.getValue().getCardNum(), entry.getValue().getCardName()); } for (Map.Entry entry: sideboard.entrySet()) { out.printf("SB: %d [%s:%s] %s%n", entry.getValue().getQuantity(), entry.getValue().getSetCode(), entry.getValue().getCardNum(), entry.getValue().getCardName()); } + + // Write out the layout + out.print("LAYOUT MAIN:"); + writeCardLayout(out, deck.getCardLayout()); + out.print("\n"); + out.print("LAYOUT SIDEBOARD:"); + writeCardLayout(out, deck.getSideboardLayout()); + out.print("\n"); } finally { out.close(); } } + private static void writeCardLayout(PrintWriter out, DeckCardLayout layout) { + List>> cardGrid = layout.getCards(); + int height = cardGrid.size(); + int width = (height > 0) ? cardGrid.get(0).size() : 0; + out.print("(" + height + "," + width + ")"); + out.print(layout.getSettings()); + out.print("|"); + for (List> row : cardGrid) { + for (List stack : row) { + out.print("("); + for (int i = 0; i < stack.size(); ++i) { + DeckCardInfo info = stack.get(i); + out.printf("[%s:%s]", info.getSetCode(), info.getCardNum()); + if (i != stack.size() - 1) { + out.print(","); + } + } + out.print(")"); + } + } + } + } diff --git a/Mage/src/main/java/mage/cards/decks/Deck.java b/Mage/src/main/java/mage/cards/decks/Deck.java index 4cd189a1ca2..3ee7dfd1943 100644 --- a/Mage/src/main/java/mage/cards/decks/Deck.java +++ b/Mage/src/main/java/mage/cards/decks/Deck.java @@ -44,6 +44,8 @@ public class Deck implements Serializable { private String name; private final Set cards = new LinkedHashSet<>(); private final Set sideboard = new LinkedHashSet<>(); + private DeckCardLayout cardsLayout; + private DeckCardLayout sideboardLayout; private long deckHashCode = 0; public static Deck load(DeckCardLists deckCardLists) throws GameException { @@ -57,6 +59,8 @@ public class Deck implements Serializable { public static Deck load(DeckCardLists deckCardLists, boolean ignoreErrors, boolean mockCards) throws GameException { Deck deck = new Deck(); deck.setName(deckCardLists.getName()); + deck.cardsLayout = deckCardLists.getCardLayout(); + deck.sideboardLayout = deckCardLists.getSideboardLayout(); List deckCardNames = new ArrayList<>(); for (DeckCardInfo deckCardInfo : deckCardLists.getCards()) { Card card = createCard(deckCardInfo, mockCards); @@ -141,10 +145,18 @@ public class Deck implements Serializable { return cards; } + public DeckCardLayout getCardsLayout() { + return cardsLayout; + } + public Set getSideboard() { return sideboard; } + public DeckCardLayout getSideboardLayout() { + return sideboardLayout; + } + public long getDeckHashCode() { return deckHashCode; } diff --git a/Mage/src/main/java/mage/cards/decks/DeckCardLayout.java b/Mage/src/main/java/mage/cards/decks/DeckCardLayout.java new file mode 100644 index 00000000000..2cbab4ac286 --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/DeckCardLayout.java @@ -0,0 +1,24 @@ +package mage.cards.decks; + +import java.util.List; + +/** + * Created by stravant@gmail.com on 2016-10-03. + */ +public class DeckCardLayout { + private List>> cards; + private String settings; + + public DeckCardLayout(List>> cards, String settings) { + this.cards = cards; + this.settings = settings; + } + + public List>> getCards() { + return cards; + } + + public String getSettings() { + return settings; + } +} diff --git a/Mage/src/main/java/mage/cards/decks/DeckCardLists.java b/Mage/src/main/java/mage/cards/decks/DeckCardLists.java index 7079d8996bc..2272aa3e114 100644 --- a/Mage/src/main/java/mage/cards/decks/DeckCardLists.java +++ b/Mage/src/main/java/mage/cards/decks/DeckCardLists.java @@ -42,6 +42,26 @@ public class DeckCardLists implements Serializable { private List cards = new ArrayList<>(); private List sideboard = new ArrayList<>(); + // Layout (if supported) + private DeckCardLayout cardLayout = null; + private DeckCardLayout sideboardLayout = null; + + /** + * @return The layout of the cards + */ + public DeckCardLayout getCardLayout() { + return cardLayout; + } + public void setCardLayout(DeckCardLayout layout) { + this.cardLayout = layout; + } + public DeckCardLayout getSideboardLayout() { + return sideboardLayout; + } + public void setSideboardLayout(DeckCardLayout layout) { + this.sideboardLayout = layout; + } + /** * @return the cards */ diff --git a/Mage/src/main/java/mage/cards/decks/importer/DckDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/DckDeckImporter.java index bb934de2879..38ebb24e46a 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DckDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DckDeckImporter.java @@ -27,9 +27,12 @@ */ package mage.cards.decks.importer; +import java.util.ArrayList; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLayout; import mage.cards.decks.DeckCardLists; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; @@ -42,6 +45,12 @@ public class DckDeckImporter extends DeckImporter { private static final Pattern pattern = Pattern.compile("(SB:)?\\s*(\\d*)\\s*\\[([^]:]+):([^]:]+)\\].*"); + private static final Pattern layoutPattern = Pattern.compile("LAYOUT (\\w+):\\((\\d+),(\\d+)\\)([^|]+)\\|(.*)$"); + + private static final Pattern layoutStackPattern = Pattern.compile("\\(([^)]*)\\)"); + + private static final Pattern layoutStackEntryPattern = Pattern.compile("\\[(\\w+):(\\w+)]"); + @Override protected void readLine(String line, DeckCardLists deckList) { @@ -79,6 +88,56 @@ public class DckDeckImporter extends DeckImporter { deckList.setName(line.substring(5, line.length())); } else if (line.startsWith("AUTHOR:")) { deckList.setAuthor(line.substring(7, line.length())); + } else if (line.startsWith("LAYOUT")) { + Matcher m2 = layoutPattern.matcher(line); + if (m2.find()) { + String target = m2.group(1); + int rows = Integer.parseInt(m2.group(2)); + int cols = Integer.parseInt(m2.group(3)); + String settings = m2.group(4); + String stackData = m2.group(5); + Matcher stackMatcher = layoutStackPattern.matcher(stackData); + // + List>> grid = new ArrayList<>(); + int totalCardCount = 0; + for (int row = 0; row < rows; ++row) { + List> rowData = new ArrayList<>(); + grid.add(rowData); + for (int col = 0; col < cols; ++col) { + List stack = new ArrayList<>(); + rowData.add(stack); + if (stackMatcher.find()) { + String thisStackData = stackMatcher.group(1); + Matcher stackEntries = layoutStackEntryPattern.matcher(thisStackData); + while (stackEntries.find()) { + ++totalCardCount; + stack.add(new DeckCardInfo("", stackEntries.group(2), stackEntries.group(1))); + } + } else { + sbMessage.append("Missing stack\n."); + } + } + } + // + DeckCardLayout layout = new DeckCardLayout(grid, settings); + int expectedCount = 0; + if (target.equals("MAIN")) { + deckList.setCardLayout(layout); + expectedCount = deckList.getCards().size(); + } else if (target.equals("SIDEBOARD")) { + deckList.setSideboardLayout(layout); + expectedCount = deckList.getSideboard().size(); + } else { + sbMessage.append("Bad target `" + target + "` for layout.\n"); + } + // + if (totalCardCount != expectedCount) { + sbMessage.append("Layout mismatch: Expected " + expectedCount + " cards, but got " + totalCardCount + " in layout `" + target + "`\n."); + } + + } else { + sbMessage.append("Malformed layout line"); + } } } } From 5af417ed0bf74452b88da3951efa394bfdee0c4c Mon Sep 17 00:00:00 2001 From: Mark Langen Date: Tue, 4 Oct 2016 00:23:13 -0600 Subject: [PATCH 12/13] More Drag & Drop editor fixes * Fixed cards being blurry. The problem isn't completely fixed, but the fix will suffice in most cases. Card Images are only loaded in once, and the cards were loaded in initially at a small size instead of their eventual size after layout. Really they should reload images when resized, but I will tackle that later, and it's only noticable when significantly resizing cards within one usage of the editor anyways. * Removed some of the unneeded UI on the sideboard half of the DeckView so that componently are less likely to overlap at a small size. Not sure how to ideally handle this, as Swing doesn't really provide an elegant way to hide components at small sizes, and I'm sure the user would rather the components overlap than be unable to resize the window as small as they want due to a minimum size requirement. --- .../java/mage/client/cards/DragCardGrid.java | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index a04d2e963cd..24bb522b2ee 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -1,36 +1,21 @@ package mage.client.cards; import mage.cards.MageCard; -import mage.cards.decks.Deck; import mage.cards.decks.DeckCardInfo; import mage.cards.decks.DeckCardLayout; -import mage.cards.decks.importer.DeckImporterUtil; -import mage.cards.repository.CardInfo; -import mage.client.MageFrame; -import mage.client.deckeditor.DeckArea; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.*; import mage.client.util.Event; import mage.constants.CardType; -import mage.game.GameException; import mage.view.CardView; import mage.view.CardsView; import org.apache.log4j.Logger; import org.mage.card.arcane.CardRenderer; import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; -import javax.swing.filechooser.FileFilter; import java.awt.*; import java.awt.event.*; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; import java.util.*; import java.util.List; import java.util.regex.Matcher; @@ -367,6 +352,15 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg public void setRole(Role role) { this.role = role; + if (role == Role.SIDEBOARD) { + creatureCountLabel.setVisible(false); + landCountLabel.setVisible(false); + cardSizeSliderLabel.setVisible(false); + } else { + creatureCountLabel.setVisible(true); + landCountLabel.setVisible(true); + cardSizeSliderLabel.setVisible(true); + } updateCounts(); } @@ -502,6 +496,9 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg JPopupMenu sortPopup; JCheckBox separateCreaturesCb; + JSlider cardSizeSlider; + JLabel cardSizeSliderLabel; + Map sortButtons = new HashMap<>(); JLabel deckNameAndCountLabel; @@ -641,13 +638,13 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg toolbar.add(toolbarInner, BorderLayout.WEST); JPanel sliderPanel = new JPanel(new GridBagLayout()); sliderPanel.setOpaque(false); - final JSlider sizeSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 50); - sizeSlider.setOpaque(false); - sizeSlider.setPreferredSize(new Dimension(100, (int)sizeSlider.getPreferredSize().getHeight())); - sizeSlider.addChangeListener(e -> { - if (!sizeSlider.getValueIsAdjusting()) { + cardSizeSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 50); + cardSizeSlider.setOpaque(false); + cardSizeSlider.setPreferredSize(new Dimension(100, (int) cardSizeSlider.getPreferredSize().getHeight())); + cardSizeSlider.addChangeListener(e -> { + if (!cardSizeSlider.getValueIsAdjusting()) { // Fraction in [-1, 1] - float sliderFrac = ((float) (sizeSlider.getValue() - 50)) / 50; + float sliderFrac = ((float) (cardSizeSlider.getValue() - 50)) / 50; // Convert to frac in [0.5, 2.0] exponentially cardSizeMod = (float) Math.pow(2, sliderFrac); // Update grid @@ -655,8 +652,9 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg cardContent.repaint(); } }); - sliderPanel.add(new JLabel("Card Size:")); - sliderPanel.add(sizeSlider); + cardSizeSliderLabel = new JLabel("Card Size:"); + sliderPanel.add(cardSizeSliderLabel); + sliderPanel.add(cardSizeSlider); toolbar.add(sliderPanel, BorderLayout.EAST); this.add(toolbar, BorderLayout.NORTH); @@ -1143,7 +1141,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg updateCounts(); // Create the card view - final MageCard cardPanel = Plugins.getInstance().getMageCard(card, lastBigCard, new Dimension(100, 140), null, true, true); + final MageCard cardPanel = Plugins.getInstance().getMageCard(card, lastBigCard, new Dimension(getCardWidth(), getCardHeight()), null, true, true); cardPanel.update(card); cardPanel.setTextOffset(0); From d604b18f6b5d336b1cc4c675ff3586bd9f1eb348 Mon Sep 17 00:00:00 2001 From: Mark Langen Date: Tue, 4 Oct 2016 01:18:50 -0600 Subject: [PATCH 13/13] Separate setting for split position in sideboarding vs deck construction * Depending on the orientation of the sideboard / maindeck the split should be a separate setting, not one single one. --- .../java/mage/client/deckeditor/DeckArea.java | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java index 4040e1658e9..34b0ab24374 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java @@ -64,6 +64,9 @@ public class DeckArea extends javax.swing.JPanel { private Set hiddenCards = new HashSet<>(); private Deck lastDeck = new Deck(); private BigCard lastBigCard = null; + private int dividerLocationNormal = 0; + private int dividerLocationLimited = 0; + private boolean isLimitedBuildingOrientation = false; public DeckCardLayout getCardLayout() { return deckList.getCardLayout(); @@ -76,9 +79,10 @@ public class DeckArea extends javax.swing.JPanel { public static class Settings { public DragCardGrid.Settings maindeckSettings; public DragCardGrid.Settings sideboardSetings; - public int dividerLocation; + public int dividerLocationLimited; + public int dividerLocationNormal; - private static Pattern parser = Pattern.compile("([^|]*)\\|([^|]*)\\|([^|]*)"); + private static Pattern parser = Pattern.compile("([^|]*)\\|([^|]*)\\|([^|]*)\\|([^|]*)"); public static Settings parse(String s) { Matcher m = parser.matcher(s); @@ -86,7 +90,8 @@ public class DeckArea extends javax.swing.JPanel { Settings settings = new Settings(); settings.maindeckSettings = DragCardGrid.Settings.parse(m.group(1)); settings.sideboardSetings = DragCardGrid.Settings.parse(m.group(2)); - settings.dividerLocation = Integer.parseInt(m.group(3)); + settings.dividerLocationNormal = Integer.parseInt(m.group(3)); + settings.dividerLocationLimited = Integer.parseInt(m.group(4)); return settings; } else { return null; @@ -95,7 +100,7 @@ public class DeckArea extends javax.swing.JPanel { @Override public String toString() { - return maindeckSettings.toString() + "|" + sideboardSetings.toString() + "|" + dividerLocation; + return maindeckSettings.toString() + "|" + sideboardSetings.toString() + "|" + dividerLocationNormal + "|" + dividerLocationLimited; } } @@ -161,7 +166,13 @@ public class DeckArea extends javax.swing.JPanel { Settings settings = new Settings(); settings.maindeckSettings = deckList.saveSettings(); settings.sideboardSetings = sideboardList.saveSettings(); - settings.dividerLocation = deckAreaSplitPane.getDividerLocation(); + if (isLimitedBuildingOrientation) { + dividerLocationLimited = deckAreaSplitPane.getDividerLocation(); + } else { + dividerLocationNormal = deckAreaSplitPane.getDividerLocation(); + } + settings.dividerLocationLimited = dividerLocationLimited; + settings.dividerLocationNormal = dividerLocationNormal; return settings; } @@ -169,7 +180,17 @@ public class DeckArea extends javax.swing.JPanel { if (s != null) { deckList.loadSettings(s.maindeckSettings); sideboardList.loadSettings(s.sideboardSetings); - deckAreaSplitPane.setDividerLocation(s.dividerLocation); + dividerLocationLimited = s.dividerLocationLimited; + dividerLocationNormal = s.dividerLocationNormal; + if (isLimitedBuildingOrientation) { + if (dividerLocationLimited != 0) { + deckAreaSplitPane.setDividerLocation(s.dividerLocationLimited); + } + } else { + if (dividerLocationNormal != 0) { + deckAreaSplitPane.setDividerLocation(s.dividerLocationNormal); + } + } } } @@ -191,8 +212,14 @@ public class DeckArea extends javax.swing.JPanel { public void setOrientation(boolean limitedBuildingOrientation) { if (limitedBuildingOrientation) { deckAreaSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT); + if (dividerLocationLimited != 0) { + deckAreaSplitPane.setDividerLocation(dividerLocationLimited); + } } else { deckAreaSplitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT); + if (dividerLocationNormal != 0) { + deckAreaSplitPane.setDividerLocation(dividerLocationNormal); + } } }