diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPanel.java index d7079e160c..23760cbe69 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/CollectionViewerPanel.java @@ -177,7 +177,7 @@ public final class CollectionViewerPanel extends JPanel { JCheckBox cardsOrTokens = new JCheckBox("Display Cards"); cardsOrTokens.setSelected(true); cardsOrTokens.setForeground(Color.white); - cardsOrTokens.setToolTipText("Select to show Cards or Tokens(and emblems) for the chosen set"); + cardsOrTokens.setToolTipText("Select to show Cards for the chosen set. When unselected, will show Tokens, Emblems and Planes for the set instead"); cardsOrTokens.addActionListener(e -> mageBook.cardsOrTokens(cardsOrTokens.isSelected())); buttonsPanel.add(cardsOrTokens); diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java index 1ce3320481..180cc2d5d5 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java @@ -57,11 +57,13 @@ import mage.components.ImagePanel; import mage.components.ImagePanelStyle; import mage.constants.Rarity; import mage.game.command.Emblem; +import mage.game.command.Plane; import mage.game.permanent.PermanentToken; import mage.game.permanent.token.Token; import mage.view.CardView; import mage.view.EmblemView; import mage.view.PermanentView; +import mage.view.PlaneView; import org.apache.log4j.Logger; import org.mage.card.arcane.ManaSymbols; import org.mage.plugins.card.images.CardDownloadData; @@ -251,7 +253,8 @@ public class MageBook extends JComponent { } else { updateCardStats(currentSet, false); int numTokens = showTokens(); - showEmblems(numTokens); + int numTokensEmblems = numTokens + showEmblems(numTokens); + showPlanes(numTokensEmblems); } } @@ -317,11 +320,13 @@ public class MageBook extends JComponent { return tokens.size(); } - public void showEmblems(int numTokens) { + public int showEmblems(int numTokens) { List emblems = getEmblems(currentPage, currentSet, numTokens); // System.out.println ("Size of origins in " + currentSet + " = " + emblems.size()); + int numEmblems = 0; if (emblems != null && emblems.size() > 0) { int size = emblems.size(); + numEmblems = size; Rectangle rectangle = new Rectangle(); rectangle.translate(OFFSET_X, OFFSET_Y); // calculate the x offset of the second (right) page @@ -357,6 +362,49 @@ public class MageBook extends JComponent { } } + jLayeredPane.repaint(); + } + return numEmblems; + } + + public void showPlanes(int numTokensEmblems) { + List planes = getPlanes(currentPage, currentSet, numTokensEmblems); + if (planes != null && planes.size() > 0) { + int size = planes.size(); + Rectangle rectangle = new Rectangle(); + rectangle.translate(OFFSET_X, OFFSET_Y); + + int second_page_x = (conf.WIDTH - 2 * LEFT_RIGHT_PAGES_WIDTH) + - (cardDimensions.frameWidth + CardPosition.GAP_X) * conf.CARD_COLUMNS + CardPosition.GAP_X - OFFSET_X; + + numTokensEmblems = numTokensEmblems % conf.CARDS_PER_PAGE; + if (numTokensEmblems < conf.CARDS_PER_PAGE / 2) { + for (int z = 0; z < numTokensEmblems && z < conf.CARDS_PER_PAGE / 2; z++) { + rectangle = CardPosition.translatePosition(z, rectangle, conf); + } + } else { + rectangle.setLocation(second_page_x, OFFSET_Y); + for (int z = 0; z < numTokensEmblems - conf.CARDS_PER_PAGE / 2; z++) { + rectangle = CardPosition.translatePosition(z, rectangle, conf); + } + } + + int lastI = 0; + for (int i = 0; i < size && i + numTokensEmblems < conf.CARDS_PER_PAGE / 2; i++) { + Plane plane = planes.get(i); + addPlane(plane, bigCard, null, rectangle); + rectangle = CardPosition.translatePosition(i + numTokensEmblems, rectangle, conf); + lastI++; + } + + if (size + numTokensEmblems > conf.CARDS_PER_PAGE / 2) { + for (int i = lastI; i < size && i + numTokensEmblems < conf.CARDS_PER_PAGE; i++) { + Plane plane = planes.get(i); + addPlane(plane, bigCard, null, rectangle); + rectangle = CardPosition.translatePosition(i + numTokensEmblems - conf.CARDS_PER_PAGE / 2, rectangle, conf); + } + } + jLayeredPane.repaint(); } } @@ -416,6 +464,11 @@ public class MageBook extends JComponent { addCard(cardView, bigCard, gameId, rectangle); } + private void addPlane(Plane plane, BigCard bigCard, UUID gameId, Rectangle rectangle) { + CardView cardView = new CardView(new PlaneView(plane)); + addCard(cardView, bigCard, gameId, rectangle); + } + private List getCards(int page, String set) { CardCriteria criteria = new CardCriteria(); criteria.setCodes(set); @@ -590,6 +643,62 @@ public class MageBook extends JComponent { return emblems.subList(start, end); } + private List getPlanes(int page, String set, int numTokens) { + ArrayList allPlanes = getTokenCardUrls(); + ArrayList planes = new ArrayList<>(); + + for (CardDownloadData plane : allPlanes) { + if (plane.getSet().equals(set)) { + try { + String className = plane.getName(); + if (plane.getTokenClassName() != null && plane.getTokenClassName().length() > 0) { + if (plane.getTokenClassName().toLowerCase(Locale.ENGLISH).matches(".*plane.*")) { + className = plane.getTokenClassName(); + className = "mage.game.command.planes." + className; + } + } else { + continue; + } + Class c = Class.forName(className); + Constructor cons = c.getConstructor(); + Object newPlane = cons.newInstance(); + if (newPlane != null && newPlane instanceof mage.game.command.Plane) { + ((Plane) newPlane).setExpansionSetCodeForImage(set); + + planes.add((Plane) newPlane); + } + } catch (ClassNotFoundException ex) { + // Swallow exception + } catch (NoSuchMethodException ex) { + // Swallow exception + } catch (SecurityException ex) { + // Swallow exception + } catch (InstantiationException ex) { + // Swallow exception + } catch (IllegalAccessException ex) { + // Swallow exception + } catch (IllegalArgumentException ex) { + // Swallow exception + } catch (InvocationTargetException ex) { + // Swallow exception + } + } + } + int start = 0; + int end = planes.size(); + + if ((page + 1) * conf.CARDS_PER_PAGE < numTokens + planes.size()) { + end = (page + 1) * conf.CARDS_PER_PAGE - numTokens; + pageRight.setVisible(true); + } + + if (planes.size() > conf.CARDS_PER_PAGE) { + pageLeft.setVisible(true); + pageRight.setVisible(true); + } + return planes.subList(start, end); + } + private ImagePanel getImagePanel(String filename, ImagePanelStyle type) { try { InputStream is = this.getClass().getResourceAsStream(filename); @@ -651,7 +760,7 @@ public class MageBook extends JComponent { } } - public void updateSize(String size){ + public void updateSize(String size) { switch (size) { case LAYOUT_3x3: this.conf = new _3x3Configuration(); diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt index 72643ebf22..3abc3a71fe 100644 --- a/Mage.Client/src/main/resources/card-pictures-tok.txt +++ b/Mage.Client/src/main/resources/card-pictures-tok.txt @@ -85,6 +85,21 @@ |Generate|EMBLEM:M14|Liliana of the Dark Realms||Emblem Liliana|LilianaOfTheDarkRealmsEmblem| |Generate|EMBLEM:MMA|Elspeth, Knight Errant||Emblem Elspeth|ElspethKnightErrantEmblem| |Generate|EMBLEM:RIX|Huatli, Radiant Champion||Emblem Huatli|HuatliRadiantChampionEmblem| +|Generate|PLANE:PCA|Plane - Academy At Tolaria West|||AcademyAtTolariaWestPlane| +|Generate|PLANE:PCA|Plane - Agyrem|||AgyremPlane| +|Generate|PLANE:PCA|Plane - Akoum|||AkoumPlane| +|Generate|PLANE:PCA|Plane - Bant|||BantPlane| +|Generate|PLANE:PCA|Plane - Edge Of Malacol|||EdgeOfMalacolPlane| +|Generate|PLANE:PCA|Plane - Feeding Grounds|||FeedingGroundsPlane| +|Generate|PLANE:PCA|Plane - Fields of Summer|||FieldsOfSummerPlane| +|Generate|PLANE:PCA|Plane - Hedron Fields of Agadeem|||HedronFieldsOfAgadeemPlane| +|Generate|PLANE:PCA|Plane - Lethe Lake|||LetheLakePlane| +|Generate|PLANE:PCA|Plane - Naya|||NayaPlane| +|Generate|PLANE:PCA|Plane - The Dark Barony|||TheDarkBaronyPlane| +|Generate|PLANE:PCA|Plane - Tazeem|||TazeemPlane| +|Generate|PLANE:PCA|Plane - The Eon Fog|||TheEonFogPlane| +|Generate|PLANE:PCA|Plane - Turri Island|||TurriIslandPlane| +|Generate|TOK:PCA|Eldrazi|||EldraziAnnihilatorToken| |Generate|TOK:10E|Ape|||PongifyApeToken| |Generate|TOK:10E|Dragon|||DragonToken2| |Generate|TOK:10E|Goblin|||GoblinToken| @@ -1124,4 +1139,4 @@ |Generate|TOK:ZEN|Snake|||SnakeToken| |Generate|TOK:ZEN|Vampire|| |Generate|TOK:ZEN|Wolf|||WolfToken| -|Generate|TOK:ZEN|Zombie Giant|||QuestForTheGravelordZombieToken| \ No newline at end of file +|Generate|TOK:ZEN|Zombie Giant|||QuestForTheGravelordZombieToken| diff --git a/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java index 940df87aae..bd02002b65 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RollPlanarDieEffect.java @@ -116,7 +116,7 @@ public class RollPlanarDieEffect extends OneShotEffect { } try { effect.apply(game, source); - } catch (UnsupportedOperationException exception) { + } catch (UnsupportedOperationException exception) { } if (effect instanceof ContinuousEffect) { game.addEffect((ContinuousEffect) effect, source); @@ -160,7 +160,6 @@ public class RollPlanarDieEffect extends OneShotEffect { foundNextPlane = true; plane.setControllerId(controller.getId()); game.addPlane(plane, null, controller.getId()); - game.informPlayers("You have planeswalked to " + plane.getLogName()); } } catch (Exception ex) { } @@ -176,7 +175,16 @@ public class RollPlanarDieEffect extends OneShotEffect { if (!staticText.isEmpty()) { return staticText; } - StringBuilder sb = new StringBuilder("Roll the planar die"); + StringBuilder sb = new StringBuilder("Roll the planar die. If you roll CHAOS, "); + for (int i = 0; i < chaosEffects.size(); i++) { + Effect effect = chaosEffects.get(i); + if (effect != null) { + String emode = effect.getText(mode); + emode = emode.substring(0, 1).toLowerCase() + emode.substring(1); + sb.append(emode); + } + } + sb.append(". If you roll PW, planeswalk to a new plane"); return sb.toString(); } diff --git a/Mage/src/main/java/mage/constants/Planes.java b/Mage/src/main/java/mage/constants/Planes.java index d7da93f7e2..543d3b2749 100644 --- a/Mage/src/main/java/mage/constants/Planes.java +++ b/Mage/src/main/java/mage/constants/Planes.java @@ -34,6 +34,7 @@ package mage.constants; public enum Planes { PLANE_ACADEMY_AT_TOLARIA_WEST("AcademyAtTolariaWestPlane"), PLANE_AGYREM("AgyremPlane"), + PLANE_AKOUM("AkoumPlane"), PLANE_BANT("BantPlane"), PLANE_EDGE_OF_MALACOL("EdgeOfMalacolPlane"), PLANE_FEEDING_GROUNDS("FeedingGroundsPlane"), @@ -42,6 +43,7 @@ public enum Planes { PLANE_LETHE_LAKE("LetheLakePlane"), PLANE_NAYA("NayaPlane"), PLANE_THE_DARK_BARONY("TheDarkBaronyPlane"), + PLANE_TAZEEM("TazeemPlane"), PLANE_THE_EON_FOG("TheEonFogPlane"), PLANE_TURRI_ISLAND("TurriIslandPlane"); diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index ed19997ff5..2d49f4a51f 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -1034,7 +1034,7 @@ public abstract class GameImpl implements Game, Serializable { watchers.add(new BlockedAttackerWatcher()); watchers.add(new DamageDoneWatcher()); watchers.add(new PlanarRollWatcher()); - + //20100716 - 103.5 for (UUID playerId : state.getPlayerList(startingPlayerId)) { Player player = getPlayer(playerId); @@ -1072,10 +1072,12 @@ public abstract class GameImpl implements Game, Serializable { cardsWithOpeningAction.remove(card); } } - + // 20180408 - 901.5 if (gameOptions.planeChase) { - addPlane(Plane.getRandomPlane(), null, getActivePlayerId()); + Plane plane = Plane.getRandomPlane(); + plane.setControllerId(getActivePlayerId()); + addPlane(plane, null, getActivePlayerId()); } } @@ -1544,7 +1546,7 @@ public abstract class GameImpl implements Game, Serializable { public boolean addPlane(Plane plane, MageObject sourceObject, UUID toPlayerId) { // Implementing planechase as if it were 901.15. Single Planar Deck Option // Here, can enforce the world plane restriction (the Grand Melee format may have some impact on this implementation) - + // Enforce 'world' rule for planes for (CommandObject cobject : state.getCommand()) { if (cobject instanceof Plane) { @@ -1560,6 +1562,7 @@ public abstract class GameImpl implements Game, Serializable { ability.setSourceId(newPlane.getId()); } state.addCommandObject(newPlane); + informPlayers("You have planeswalked to " + newPlane.getLogName()); return true; } diff --git a/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java b/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java new file mode 100644 index 0000000000..75c8dbc9fb --- /dev/null +++ b/Mage/src/main/java/mage/game/command/planes/AkoumPlane.java @@ -0,0 +1,91 @@ +/* + * 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 mage.game.command.planes; + +import java.util.ArrayList; +import java.util.List; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MainPhaseStackEmptyCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.RollPlanarDieEffect; +import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.permanent.EnchantedPredicate; +import mage.game.command.Plane; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.PlanarRollWatcher; + +/** + * + * @author spjspj + */ +public class AkoumPlane extends Plane { + + private static final FilterCard filterCard = new FilterCard("enchantment spells"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that isn't enchanted"); + + static { + filter.add(Predicates.not(new EnchantedPredicate())); + filterCard.add(new CardTypePredicate(CardType.ENCHANTMENT)); + } + + public AkoumPlane() { + this.setName("Plane - Akoum"); + this.setExpansionSetCodeForImage("PCA"); + + // Players may cast enchantment spells as if they had flash + SimpleStaticAbility ability = new SimpleStaticAbility(Zone.COMMAND, new CastAsThoughItHadFlashAllEffect(Duration.Custom, filterCard, false)); + this.getAbilities().add(ability); + + // Active player can roll the planar die: Whenever you roll {CHAOS}, destroy target creature that isn't enchanted + Effect chaosEffect = new DestroyTargetEffect(true); + Target chaosTarget = new TargetCreaturePermanent(filter); + + List chaosEffects = new ArrayList(); + chaosEffects.add(chaosEffect); + List chaosTargets = new ArrayList(); + chaosTargets.add(chaosTarget); + + ActivateIfConditionActivatedAbility chaosAbility = new ActivateIfConditionActivatedAbility(Zone.COMMAND, new RollPlanarDieEffect(chaosEffects, chaosTargets), new GenericManaCost(0), MainPhaseStackEmptyCondition.instance); + chaosAbility.addWatcher(new PlanarRollWatcher()); + this.getAbilities().add(chaosAbility); + chaosAbility.setMayActivate(TargetController.ANY); + this.getAbilities().add(new SimpleStaticAbility(Zone.ALL, new PlanarDieRollCostIncreasingEffect(chaosAbility.getOriginalId()))); + } +} diff --git a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java index f2b68d3e9c..54653f8bf9 100644 --- a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java @@ -30,6 +30,7 @@ package mage.game.command.planes; import java.util.ArrayList; import java.util.List; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MainPhaseStackEmptyCondition; @@ -93,6 +94,7 @@ class EdgeOfMalacolEffect extends ContinuousRuleModifyingEffectImpl { public EdgeOfMalacolEffect() { super(Duration.Custom, Outcome.Detriment); + this.staticText = "If a creature you control would untap during your untap step, put two +1/+1 counters on it instead"; } public EdgeOfMalacolEffect(final EdgeOfMalacolEffect effect) {