From ef0a1a843d4eaa767c8bad4cf6d07a072ec98434 Mon Sep 17 00:00:00 2001 From: friarsol Date: Sun, 9 Feb 2020 21:32:24 -0500 Subject: [PATCH 01/20] Pop up other zones during targeting --- .../java/forge/screens/match/CMatchUI.java | 123 +++++------ .../forge/view/arcane/FloatingCardArea.java | 52 +++-- .../java/forge/view/arcane/FloatingZone.java | 200 ++++++++++-------- .../main/java/forge/interfaces/IGuiGame.java | 15 +- .../input/InputSelectEntitiesFromList.java | 60 +++--- .../forge/match/input/InputSelectTargets.java | 38 ++-- .../main/java/forge/net/ProtocolMethod.java | 20 +- .../java/forge/net/server/NetGuiGame.java | 18 +- .../java/forge/player/TargetSelection.java | 11 +- 9 files changed, 275 insertions(+), 262 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java b/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java index 20900adcb56..b16504526ba 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java @@ -17,39 +17,11 @@ */ package forge.screens.match; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.event.KeyEvent; -import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicReference; - -import javax.swing.JMenu; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; - import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; - -import forge.FThreads; -import forge.GuiBase; -import forge.ImageCache; -import forge.LobbyPlayer; -import forge.Singletons; -import forge.StaticData; +import com.google.common.collect.Lists; +import forge.*; import forge.assets.FSkinProp; import forge.card.CardStateName; import forge.control.KeyboardShortcuts; @@ -71,51 +43,26 @@ import forge.game.phase.PhaseType; import forge.game.player.DelayedReveal; import forge.game.player.IHasIcon; import forge.game.player.PlayerView; -import forge.game.spellability.SpellAbility; -import forge.game.spellability.SpellAbilityStackInstance; -import forge.game.spellability.SpellAbilityView; -import forge.game.spellability.StackItemView; -import forge.game.spellability.TargetChoices; +import forge.game.spellability.*; import forge.game.zone.ZoneType; -import forge.gui.FNetOverlay; -import forge.gui.GuiChoose; -import forge.gui.GuiDialog; -import forge.gui.GuiUtils; -import forge.gui.SOverlayUtils; -import forge.gui.framework.DragCell; -import forge.gui.framework.EDocID; -import forge.gui.framework.FScreen; -import forge.gui.framework.ICDoc; -import forge.gui.framework.IVDoc; -import forge.gui.framework.SDisplayUtil; -import forge.gui.framework.SLayoutIO; -import forge.gui.framework.VEmptyDoc; +import forge.gui.*; +import forge.gui.framework.*; import forge.item.InventoryItem; import forge.item.PaperCard; import forge.match.AbstractGuiGame; import forge.menus.IMenuProvider; import forge.model.FModel; import forge.player.PlayerZoneUpdate; +import forge.player.PlayerZoneUpdates; import forge.properties.ForgeConstants; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; -import forge.screens.match.controllers.CAntes; -import forge.screens.match.controllers.CCombat; -import forge.screens.match.controllers.CDetailPicture; -import forge.screens.match.controllers.CDev; -import forge.screens.match.controllers.CDock; -import forge.screens.match.controllers.CLog; -import forge.screens.match.controllers.CPrompt; -import forge.screens.match.controllers.CStack; +import forge.screens.match.controllers.*; import forge.screens.match.menus.CMatchUIMenus; import forge.screens.match.views.VField; import forge.screens.match.views.VHand; -import forge.toolbox.FButton; -import forge.toolbox.FLabel; -import forge.toolbox.FOptionPane; -import forge.toolbox.FSkin; +import forge.toolbox.*; import forge.toolbox.FSkin.SkinImage; -import forge.toolbox.FTextArea; import forge.toolbox.imaging.FImagePanel; import forge.toolbox.imaging.FImagePanel.AutoSizeImageMode; import forge.toolbox.imaging.FImageUtil; @@ -123,15 +70,26 @@ import forge.toolbox.special.PhaseIndicator; import forge.toolbox.special.PhaseLabel; import forge.trackable.TrackableCollection; import forge.util.ITriggerEvent; +import forge.util.Localizer; import forge.util.collect.FCollection; import forge.util.collect.FCollectionView; import forge.util.gui.SOptionPane; -import forge.util.Localizer; import forge.view.FView; import forge.view.arcane.CardPanel; import forge.view.arcane.FloatingZone; import net.miginfocom.swing.MigLayout; +import javax.swing.*; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import java.util.List; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicReference; + /** * Constructs instance of match UI controller, used as a single point of * top-level control for child UIs. Tasks targeting the view of individual @@ -459,6 +417,8 @@ public final class CMatchUI @Override public Iterable tempShowZones(final PlayerView controller, final Iterable zonesToUpdate) { + List updatedPlayerZones = Lists.newArrayList(); + for (final PlayerZoneUpdate update : zonesToUpdate) { final PlayerView player = update.getPlayer(); for (final ZoneType zone : update.getZones()) { @@ -467,7 +427,9 @@ public final class CMatchUI break; case Hand: // controller hand always shown if (controller != player) { - FloatingZone.show(this,player,zone); + if (FloatingZone.show(this,player,zone)) { + updatedPlayerZones.add(update); + } } break; case Library: @@ -475,14 +437,16 @@ public final class CMatchUI case Exile: case Flashback: case Command: - FloatingZone.show(this,player,zone); + if (FloatingZone.show(this,player,zone)) { + updatedPlayerZones.add(update); + } break; default: break; } } } - return zonesToUpdate; //pfps should return only the newly shown zones + return updatedPlayerZones; } @Override @@ -1086,21 +1050,30 @@ public final class CMatchUI } @Override - public boolean openZones(final Collection zones, final Map players) { - if (zones.size() == 1) { - switch (zones.iterator().next()) { - case Battlefield: - case Hand: - return true; //don't actually need to open anything, but indicate that zone can be opened - default: - return false; + public PlayerZoneUpdates openZones(PlayerView controller, final Collection zones, final Map playersWithTargetables) { + final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates(); + for (final PlayerView view : playersWithTargetables.keySet()) { + for(final ZoneType zone : zones) { + if (zone.equals(ZoneType.Battlefield) || zone.equals(ZoneType.Hand)) { + continue; + } + + if (zone.equals(ZoneType.Stack)) { + // TODO: Remove this if we have ever have a Stack zone that's displayable for Counters + continue; + } + + zonesToUpdate.add(new PlayerZoneUpdate(view, zone)); } } - return false; + + tempShowZones(controller, zonesToUpdate); + return zonesToUpdate; } @Override - public void restoreOldZones(final Map playersToRestoreZonesFor) { + public void restoreOldZones(PlayerView playerView, PlayerZoneUpdates playerZoneUpdates) { + hideZones(playerView, playerZoneUpdates); } @Override diff --git a/forge-gui-desktop/src/main/java/forge/view/arcane/FloatingCardArea.java b/forge-gui-desktop/src/main/java/forge/view/arcane/FloatingCardArea.java index d686c6bb341..c5282b053b5 100644 --- a/forge-gui-desktop/src/main/java/forge/view/arcane/FloatingCardArea.java +++ b/forge-gui-desktop/src/main/java/forge/view/arcane/FloatingCardArea.java @@ -17,18 +17,6 @@ */ package forge.view.arcane; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.ScrollPaneConstants; -import javax.swing.Timer; - import forge.Singletons; import forge.game.card.CardView; import forge.gui.framework.SDisplayUtil; @@ -39,10 +27,19 @@ import forge.screens.match.CMatchUI; import forge.toolbox.FMouseAdapter; import forge.toolbox.FScrollPane; import forge.toolbox.MouseTriggerEvent; -//import forge.util.collect.FCollectionView; import forge.view.FDialog; import forge.view.FFrame; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; + +//import forge.util.collect.FCollectionView; + // show some cards in a new window public abstract class FloatingCardArea extends CardArea { @@ -67,18 +64,20 @@ public abstract class FloatingCardArea extends CardArea { getWindow().setFocusableWindowState(false); // should probably do this earlier getWindow().setVisible(true); } + protected void hideWindow() { onShow(); getWindow().setFocusableWindowState(false); // should probably do this earlier getWindow().setVisible(false); - getWindow().dispose(); //pfps so that old content does not show up + getWindow().dispose(); //pfps so that old content does not show up } + protected void showOrHideWindow() { - if (getWindow().isVisible()) { - hideWindow(); - } else { - showWindow(); - } + if (getWindow().isVisible()) { + hideWindow(); + } else { + showWindow(); + } } protected void onShow() { if (!hasBeenShown) { @@ -98,24 +97,31 @@ public abstract class FloatingCardArea extends CardArea { if (hasBeenShown || locLoaded) { return; } super.setLocationRelativeTo(c); } + @Override public void setVisible(boolean b0) { - if (isVisible() == b0) { return; } + if (isVisible() == b0) { + return; + } if (!b0 && hasBeenShown && locPref != null) { //update preference before hiding window, as otherwise its location will be 0,0 prefs.setPref(locPref, getX() + COORD_DELIM + getY() + COORD_DELIM + - getWidth() + COORD_DELIM + getHeight()); + getWidth() + COORD_DELIM + getHeight()); //don't call prefs.save(), instead allowing them to be saved when match ends } if (b0) { - doRefresh(); // force a refresh before showing to pick up any changes when hidden + doRefresh(); // force a refresh before showing to pick up any changes when hidden hasBeenShown = true; - } + } super.setVisible(b0); } }; + public boolean isVisible() { + return window.isVisible(); + } + protected FDialog getWindow() { return window; } diff --git a/forge-gui-desktop/src/main/java/forge/view/arcane/FloatingZone.java b/forge-gui-desktop/src/main/java/forge/view/arcane/FloatingZone.java index 4dba33b85f2..8efeceef05f 100644 --- a/forge-gui-desktop/src/main/java/forge/view/arcane/FloatingZone.java +++ b/forge-gui-desktop/src/main/java/forge/view/arcane/FloatingZone.java @@ -6,38 +6,37 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package forge.view.arcane; -import java.util.HashMap; -import java.util.Map; -import java.util.Collections; -import java.util.Comparator; - -import java.awt.event.MouseEvent; -import javax.swing.ScrollPaneConstants; -import javax.swing.WindowConstants; - +import forge.FThreads; import forge.assets.FSkinProp; import forge.game.card.CardView; import forge.game.player.PlayerView; import forge.game.zone.ZoneType; import forge.properties.ForgePreferences.FPref; import forge.screens.match.CMatchUI; -import forge.toolbox.FScrollPane; import forge.toolbox.FMouseAdapter; +import forge.toolbox.FScrollPane; import forge.toolbox.FSkin; import forge.util.Lang; import forge.util.collect.FCollection; +import javax.swing.*; +import java.awt.event.MouseEvent; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + public class FloatingZone extends FloatingCardArea { private static final long serialVersionUID = 1927906492186378596L; @@ -46,18 +45,44 @@ public class FloatingZone extends FloatingCardArea { private static int getKey(final PlayerView player, final ZoneType zone) { return 40 * player.getId() + zone.hashCode(); } + public static void showOrHide(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) { final FloatingZone cardArea = _init(matchUI, player, zone); cardArea.showOrHideWindow(); } - public static void show(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) { + + public static boolean show(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) { final FloatingZone cardArea = _init(matchUI, player, zone); - cardArea.showWindow(); + + if (cardArea.isVisible()) { + return false; + } + + FThreads.invokeInEdtNowOrLater(new Runnable() { + @Override public void run() { + cardArea.showWindow(); + } + }); + + return true; } - public static void hide(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) { + + public static boolean hide(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) { final FloatingZone cardArea = _init(matchUI, player, zone); - cardArea.hideWindow(); + + if (!cardArea.isVisible()) { + return false; + } + + FThreads.invokeInEdtNowOrLater(new Runnable() { + @Override public void run() { + cardArea.hideWindow(); + } + }); + + return true; } + private static FloatingZone _init(final CMatchUI matchUI, final PlayerView player, final ZoneType zone) { final int key = getKey(player, zone); FloatingZone cardArea = floatingAreas.get(key); @@ -69,10 +94,12 @@ public class FloatingZone extends FloatingCardArea { } return cardArea; } + public static CardPanel getCardPanel(final CMatchUI matchUI, final CardView card) { final FloatingZone window = _init(matchUI, card.getController(), card.getZone()); return window.getCardPanel(card.getId()); } + public static void refresh(final PlayerView player, final ZoneType zone) { FloatingZone cardArea = floatingAreas.get(getKey(player, zone)); if (cardArea != null) { @@ -82,21 +109,23 @@ public class FloatingZone extends FloatingCardArea { //refresh flashback zone when graveyard, library, or exile zones updated switch (zone) { - case Graveyard: - case Library: - case Exile: - refresh(player, ZoneType.Flashback); - break; - default: - break; + case Graveyard: + case Library: + case Exile: + refresh(player, ZoneType.Flashback); + break; + default: + break; } } + public static void closeAll() { for (final FloatingZone cardArea : floatingAreas.values()) { cardArea.window.setVisible(false); } floatingAreas.clear(); } + public static void refreshAll() { for (final FloatingZone cardArea : floatingAreas.values()) { cardArea.refresh(); @@ -110,23 +139,23 @@ public class FloatingZone extends FloatingCardArea { protected FCollection cardList; private final Comparator comp = new Comparator() { - @Override - public int compare(CardView lhs, CardView rhs) { - if ( !getMatchUI().mayView(lhs) ) { - return ( getMatchUI().mayView(rhs) ) ? 1 : 0 ; - } else if ( !getMatchUI().mayView(rhs) ) { - return -1; - } else { - return lhs.getName().compareTo(rhs.getName()); - } - } - }; + @Override + public int compare(CardView lhs, CardView rhs) { + if (!getMatchUI().mayView(lhs)) { + return (getMatchUI().mayView(rhs)) ? 1 : 0; + } else if (!getMatchUI().mayView(rhs)) { + return -1; + } else { + return lhs.getName().compareTo(rhs.getName()); + } + } + }; protected Iterable getCards() { Iterable zoneCards = player.getCards(zone); - if ( zoneCards != null ) { + if (zoneCards != null) { cardList = new FCollection<>(zoneCards); - if ( sortedByName ) { + if (sortedByName) { Collections.sort(cardList, comp); } return cardList; @@ -138,28 +167,28 @@ public class FloatingZone extends FloatingCardArea { private FloatingZone(final CMatchUI matchUI, final PlayerView player0, final ZoneType zone0) { super(matchUI, new FScrollPane(false, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER)); window.add(getScrollPane(), "grow, push"); - window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); //pfps so that old content does not reappear? + window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); //pfps so that old content does not reappear? getScrollPane().setViewportView(this); setOpaque(false); switch (zone0) { - case Exile: - window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_EXILE)); - break; - case Graveyard: - window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_GRAVEYARD)); - break; - case Hand: - window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_HAND)); - break; - case Library: - window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_LIBRARY)); - break; - case Flashback: - window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_FLASHBACK)); - break; - default: - locPref = null; - break; + case Exile: + window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_EXILE)); + break; + case Graveyard: + window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_GRAVEYARD)); + break; + case Hand: + window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_HAND)); + break; + case Library: + window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_LIBRARY)); + break; + case Flashback: + window.setIconImage(FSkin.getImage(FSkinProp.IMG_ZONE_FLASHBACK)); + break; + default: + locPref = null; + break; } zone = zone0; setPlayer(player0); @@ -167,19 +196,20 @@ public class FloatingZone extends FloatingCardArea { } private void toggleSorted() { - sortedByName = !sortedByName; - setTitle(); - refresh(); - // revalidation does not appear to be necessary here - getWindow().repaint(); + sortedByName = !sortedByName; + setTitle(); + refresh(); + // revalidation does not appear to be necessary here + getWindow().repaint(); } @Override protected void onShow() { - super.onShow(); + super.onShow(); if (!hasBeenShown) { getWindow().getTitleBar().addMouseListener(new FMouseAdapter() { - @Override public final void onRightClick(final MouseEvent e) { + @Override + public final void onRightClick(final MouseEvent e) { toggleSorted(); } }); @@ -188,34 +218,36 @@ public class FloatingZone extends FloatingCardArea { private void setTitle() { title = Lang.getPossessedObject(player.getName(), zone.name()) + " (%d)" + - ( sortedByName ? " - sorted by name (right click in title to not sort)" : " (right click in title to sort)" ) ; - } + (sortedByName ? " - sorted by name (right click in title to not sort)" : " (right click in title to sort)"); + } private void setPlayer(PlayerView player0) { - if (player == player0) { return; } + if (player == player0) { + return; + } player = player0; - setTitle(); + setTitle(); boolean isAi = player0.isAI(); switch (zone) { - case Exile: - locPref = isAi ? FPref.ZONE_LOC_AI_EXILE : FPref.ZONE_LOC_HUMAN_EXILE; - break; - case Graveyard: - locPref = isAi ? FPref.ZONE_LOC_AI_GRAVEYARD : FPref.ZONE_LOC_HUMAN_GRAVEYARD; - break; - case Hand: - locPref = isAi ? FPref.ZONE_LOC_AI_HAND : FPref.ZONE_LOC_HUMAN_HAND; - break; - case Library: - locPref = isAi ? FPref.ZONE_LOC_AI_LIBRARY : FPref.ZONE_LOC_HUMAN_LIBRARY; - break; - case Flashback: - locPref = isAi ? FPref.ZONE_LOC_AI_FLASHBACK : FPref.ZONE_LOC_HUMAN_FLASHBACK; - break; - default: - locPref = null; - break; + case Exile: + locPref = isAi ? FPref.ZONE_LOC_AI_EXILE : FPref.ZONE_LOC_HUMAN_EXILE; + break; + case Graveyard: + locPref = isAi ? FPref.ZONE_LOC_AI_GRAVEYARD : FPref.ZONE_LOC_HUMAN_GRAVEYARD; + break; + case Hand: + locPref = isAi ? FPref.ZONE_LOC_AI_HAND : FPref.ZONE_LOC_HUMAN_HAND; + break; + case Library: + locPref = isAi ? FPref.ZONE_LOC_AI_LIBRARY : FPref.ZONE_LOC_HUMAN_LIBRARY; + break; + case Flashback: + locPref = isAi ? FPref.ZONE_LOC_AI_FLASHBACK : FPref.ZONE_LOC_HUMAN_FLASHBACK; + break; + default: + locPref = null; + break; } } diff --git a/forge-gui/src/main/java/forge/interfaces/IGuiGame.java b/forge-gui/src/main/java/forge/interfaces/IGuiGame.java index aac6d6aaf34..997c5e7b09a 100644 --- a/forge-gui/src/main/java/forge/interfaces/IGuiGame.java +++ b/forge-gui/src/main/java/forge/interfaces/IGuiGame.java @@ -1,11 +1,6 @@ package forge.interfaces; -import java.util.Collection; -import java.util.List; -import java.util.Map; - import com.google.common.base.Function; - import forge.LobbyPlayer; import forge.assets.FSkinProp; import forge.deck.CardPool; @@ -22,9 +17,14 @@ import forge.game.spellability.SpellAbilityView; import forge.game.zone.ZoneType; import forge.item.PaperCard; import forge.player.PlayerZoneUpdate; +import forge.player.PlayerZoneUpdates; import forge.trackable.TrackableCollection; import forge.util.ITriggerEvent; +import java.util.Collection; +import java.util.List; +import java.util.Map; + public interface IGuiGame { void setGameView(GameView gameView); GameView getGameView(); @@ -107,7 +107,6 @@ public interface IGuiGame { * @return null if choices is missing, empty, or if the users' choices are * empty; otherwise, returns the first item in the List returned by * getChoices. - * @see #getChoices(String, int, int, Object...) */ T oneOrNone(String message, List choices); @@ -158,8 +157,8 @@ public interface IGuiGame { void setCard(CardView card); void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi); - boolean openZones(Collection zones, Map players); - void restoreOldZones(Map playersToRestoreZonesFor); + PlayerZoneUpdates openZones(PlayerView controller, Collection zones, Map players); + void restoreOldZones(PlayerView playerView, PlayerZoneUpdates playerZoneUpdates); void setHighlighted(PlayerView pv, boolean b); void setUsedToPay(CardView card, boolean value); void setSelectables(final Iterable cards); diff --git a/forge-gui/src/main/java/forge/match/input/InputSelectEntitiesFromList.java b/forge-gui/src/main/java/forge/match/input/InputSelectEntitiesFromList.java index fc8b962b19c..ab34c9f6abd 100644 --- a/forge-gui/src/main/java/forge/match/input/InputSelectEntitiesFromList.java +++ b/forge-gui/src/main/java/forge/match/input/InputSelectEntitiesFromList.java @@ -1,23 +1,22 @@ package forge.match.input; -import java.util.Collection; -import java.util.List; -import java.util.ArrayList; - +import forge.FThreads; import forge.game.GameEntity; import forge.game.card.Card; import forge.game.card.CardView; - import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.game.zone.Zone; import forge.player.PlayerControllerHuman; -import forge.util.collect.FCollection; -import forge.util.collect.FCollectionView; -import forge.util.ITriggerEvent; import forge.player.PlayerZoneUpdate; import forge.player.PlayerZoneUpdates; -import forge.game.zone.Zone; -import forge.FThreads; +import forge.util.ITriggerEvent; +import forge.util.collect.FCollection; +import forge.util.collect.FCollectionView; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; public class InputSelectEntitiesFromList extends InputSelectManyBase { private static final long serialVersionUID = -6609493252672573139L; @@ -31,31 +30,32 @@ public class InputSelectEntitiesFromList extends InputSele } public InputSelectEntitiesFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView validChoices0, final SpellAbility sa0) { - super(controller, Math.min(min, validChoices0.size()), Math.min(max, validChoices0.size()),sa0); + super(controller, Math.min(min, validChoices0.size()), Math.min(max, validChoices0.size()), sa0); validChoices = validChoices0; if (min > validChoices.size()) { // pfps does this really do anything useful?? System.out.println(String.format("Trying to choose at least %d things from a list with only %d things!", min, validChoices.size())); } - ArrayList vCards = new ArrayList<>(); - for ( T c : validChoices0 ) { - if ( c instanceof Card ) { - vCards.add(((Card)c).getView()) ; - } - } - getController().getGui().setSelectables(vCards); - final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates(); - for (final GameEntity c : validChoices) { - final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null ; - if ( cz != null ) { - zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(),cz.getZoneType())); - } - } - FThreads.invokeInEdtNowOrLater(new Runnable() { - @Override public void run() { - getController().getGui().updateZones(zonesToUpdate); - zonesShown = getController().getGui().tempShowZones(controller.getPlayer().getView(),zonesToUpdate); + ArrayList vCards = new ArrayList<>(); + for (T c : validChoices0) { + if (c instanceof Card) { + vCards.add(((Card) c).getView()); } - }); + } + getController().getGui().setSelectables(vCards); + final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates(); + for (final GameEntity c : validChoices) { + final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null; + if (cz != null) { + zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(), cz.getZoneType())); + } + } + FThreads.invokeInEdtNowOrLater(new Runnable() { + @Override + public void run() { + getController().getGui().updateZones(zonesToUpdate); + zonesShown = getController().getGui().tempShowZones(controller.getPlayer().getView(), zonesToUpdate); + } + }); } @Override diff --git a/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java b/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java index d67b78b4698..547b185b681 100644 --- a/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java +++ b/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java @@ -1,13 +1,7 @@ package forge.match.input; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - import com.google.common.collect.ImmutableList; - +import forge.FThreads; import forge.game.GameEntity; import forge.game.GameObject; import forge.game.ability.ApiType; @@ -18,13 +12,18 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; import forge.model.FModel; import forge.player.PlayerControllerHuman; +import forge.player.PlayerZoneUpdate; +import forge.player.PlayerZoneUpdates; import forge.properties.ForgeConstants; import forge.properties.ForgePreferences; import forge.util.ITriggerEvent; import forge.util.TextUtil; -import forge.player.PlayerZoneUpdate; -import forge.player.PlayerZoneUpdates; -import forge.FThreads; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; public final class InputSelectTargets extends InputSyncronizedBase { private final List choices; @@ -47,16 +46,17 @@ public final class InputSelectTargets extends InputSyncronizedBase { this.tgt = sa.getTargetRestrictions(); this.sa = sa; this.mandatory = mandatory; - controller.getGui().setSelectables(CardView.getCollection(choices)); - final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates(); - for (final Card c : choices) { - zonesToUpdate.add(new PlayerZoneUpdate(c.getZone().getPlayer().getView(),c.getZone().getZoneType())); - } - FThreads.invokeInEdtNowOrLater(new Runnable() { - @Override public void run() { - controller.getGui().updateZones(zonesToUpdate); + controller.getGui().setSelectables(CardView.getCollection(choices)); + final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates(); + for (final Card c : choices) { + zonesToUpdate.add(new PlayerZoneUpdate(c.getZone().getPlayer().getView(), c.getZone().getZoneType())); + } + FThreads.invokeInEdtNowOrLater(new Runnable() { + @Override + public void run() { + controller.getGui().updateZones(zonesToUpdate); } - }); + }); } @Override diff --git a/forge-gui/src/main/java/forge/net/ProtocolMethod.java b/forge-gui/src/main/java/forge/net/ProtocolMethod.java index 254ac09a3c5..4497b5cf0f9 100644 --- a/forge-gui/src/main/java/forge/net/ProtocolMethod.java +++ b/forge-gui/src/main/java/forge/net/ProtocolMethod.java @@ -1,14 +1,6 @@ package forge.net; -import java.io.Serializable; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.List; -import java.util.Map; - import com.google.common.base.Function; - import forge.GuiBase; import forge.assets.FSkinProp; import forge.deck.CardPool; @@ -22,11 +14,19 @@ import forge.game.spellability.SpellAbilityView; import forge.interfaces.IGameController; import forge.interfaces.IGuiGame; import forge.match.NextGameDecision; +import forge.player.PlayerZoneUpdates; import forge.trackable.TrackableCollection; import forge.util.ITriggerEvent; import forge.util.ReflectionUtil; import org.apache.commons.lang3.SerializationUtils; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.List; +import java.util.Map; + /** * The methods that can be sent through this protocol. */ @@ -75,8 +75,8 @@ public enum ProtocolMethod { clearSelectables (Mode.SERVER), refreshField (Mode.SERVER), // TODO case "setPlayerAvatar": - openZones (Mode.SERVER, Boolean.TYPE, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class), - restoreOldZones (Mode.SERVER, Void.TYPE, Map/*PlayerView,Object*/.class), + openZones (Mode.SERVER, PlayerZoneUpdates.class, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class), + restoreOldZones (Mode.SERVER, Void.TYPE, PlayerView.class, PlayerZoneUpdates.class), isUiSetToSkipPhase (Mode.SERVER, Boolean.TYPE, PlayerView.class, PhaseType.class), setRememberedActions(Mode.SERVER, Void.TYPE), nextRememberedAction(Mode.SERVER, Void.TYPE), diff --git a/forge-gui/src/main/java/forge/net/server/NetGuiGame.java b/forge-gui/src/main/java/forge/net/server/NetGuiGame.java index 7601297f2e6..33e6f727b07 100644 --- a/forge-gui/src/main/java/forge/net/server/NetGuiGame.java +++ b/forge-gui/src/main/java/forge/net/server/NetGuiGame.java @@ -1,11 +1,6 @@ package forge.net.server; -import java.util.Collection; -import java.util.List; -import java.util.Map; - import com.google.common.base.Function; - import forge.LobbyPlayer; import forge.assets.FSkinProp; import forge.deck.CardPool; @@ -23,9 +18,14 @@ import forge.match.AbstractGuiGame; import forge.net.GameProtocolSender; import forge.net.ProtocolMethod; import forge.player.PlayerZoneUpdate; +import forge.player.PlayerZoneUpdates; import forge.trackable.TrackableCollection; import forge.util.ITriggerEvent; +import java.util.Collection; +import java.util.List; +import java.util.Map; + public class NetGuiGame extends AbstractGuiGame { private final GameProtocolSender sender; @@ -283,14 +283,14 @@ public class NetGuiGame extends AbstractGuiGame { } @Override - public boolean openZones(final Collection zones, final Map players) { + public PlayerZoneUpdates openZones(PlayerView controller, final Collection zones, final Map players) { updateGameView(); - return sendAndWait(ProtocolMethod.openZones, zones, players); + return sendAndWait(ProtocolMethod.openZones, controller, zones, players); } @Override - public void restoreOldZones(final Map playersToRestoreZonesFor) { - send(ProtocolMethod.restoreOldZones, playersToRestoreZonesFor); + public void restoreOldZones(PlayerView playerView, PlayerZoneUpdates playerZoneUpdates) { + send(ProtocolMethod.restoreOldZones, playerView, playerZoneUpdates); } @Override diff --git a/forge-gui/src/main/java/forge/player/TargetSelection.java b/forge-gui/src/main/java/forge/player/TargetSelection.java index deced7b9fb8..914f22e0916 100644 --- a/forge-gui/src/main/java/forge/player/TargetSelection.java +++ b/forge-gui/src/main/java/forge/player/TargetSelection.java @@ -100,7 +100,7 @@ public class TargetSelection { return true; } - final List zone = tgt.getZone(); + final List zones = tgt.getZone(); final boolean mandatory = tgt.getMandatory() && hasCandidates; final boolean choiceResult; @@ -110,7 +110,7 @@ public class TargetSelection { final GameObject choice = Aggregates.random(candidates); return ability.getTargets().add(choice); } - else if (zone.size() == 1 && zone.get(0) == ZoneType.Stack) { + else if (zones.size() == 1 && zones.get(0) == ZoneType.Stack) { // If Zone is Stack, the choices are handled slightly differently. // Handle everything inside function due to interaction with StackInstance return this.chooseCardFromStack(mandatory); @@ -152,12 +152,15 @@ public class TargetSelection { for (Card card : validTargets) { playersWithValidTargets.put(PlayerView.get(card.getController()), null); } - if (controller.getGui().openZones(zone, playersWithValidTargets)) { + + PlayerView playerView = controller.getLocalPlayerView(); + PlayerZoneUpdates playerZoneUpdates = controller.getGui().openZones(playerView, zones, playersWithValidTargets); + if (!zones.contains(ZoneType.Stack)) { InputSelectTargets inp = new InputSelectTargets(controller, validTargets, ability, mandatory); inp.showAndWait(); choiceResult = !inp.hasCancelled(); bTargetingDone = inp.hasPressedOk(); - controller.getGui().restoreOldZones(playersWithValidTargets); + controller.getGui().restoreOldZones(playerView, playerZoneUpdates); } else { // for every other case an all-purpose GuiChoose From ddc531c5f26f40c29890ff391c17c6083758b52f Mon Sep 17 00:00:00 2001 From: friarsol Date: Mon, 10 Feb 2020 21:30:19 -0500 Subject: [PATCH 02/20] Updates for Android --- .../forge/screens/match/MatchController.java | 41 +++++++++++-------- .../src/forge/screens/match/views/VStack.java | 13 ++++-- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/forge-gui-mobile/src/forge/screens/match/MatchController.java b/forge-gui-mobile/src/forge/screens/match/MatchController.java index 67032a0563b..4dd665a72a3 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchController.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchController.java @@ -5,7 +5,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import forge.FThreads; import forge.assets.FSkinImage; @@ -44,6 +43,7 @@ import forge.match.AbstractGuiGame; import forge.match.HostedMatch; import forge.model.FModel; import forge.player.PlayerZoneUpdate; +import forge.player.PlayerZoneUpdates; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; import forge.screens.match.views.VAssignDamage; @@ -307,40 +307,47 @@ public class MatchController extends AbstractGuiGame { } @Override - public boolean openZones(final Collection zones, final Map players) { + public PlayerZoneUpdates openZones(PlayerView controller, final Collection zones, final Map playersWithTargetables) { + PlayerZoneUpdates updates = new PlayerZoneUpdates(); if (zones.size() == 1) { final ZoneType zoneType = zones.iterator().next(); switch (zoneType) { case Battlefield: case Command: - players.clear(); //clear since no zones need to be restored - return true; //Battlefield is always open + playersWithTargetables.clear(); //clear since no zones need to be restored default: //open zone tab for given zone if needed boolean result = true; - for (final PlayerView player : players.keySet()) { + for (final PlayerView player : playersWithTargetables.keySet()) { final VPlayerPanel playerPanel = view.getPlayerPanel(player); - players.put(player, playerPanel.getSelectedTab()); //backup selected tab before changing it + playersWithTargetables.put(player, playerPanel.getSelectedTab()); //backup selected tab before changing it final InfoTab zoneTab = playerPanel.getZoneTab(zoneType); - if (zoneTab == null) { - result = false; - } else { + if (zoneTab != null) { + updates.add(new PlayerZoneUpdate(player, zoneType)); playerPanel.setSelectedTab(zoneTab); } } - return result; } } - return false; + return updates; } @Override - public void restoreOldZones(final Map playersToRestoreZonesFor) { - for (final Entry player : playersToRestoreZonesFor.entrySet()) { - final VPlayerPanel playerPanel = view.getPlayerPanel(player.getKey()); - if (player.getValue() == null || player.getValue() instanceof InfoTab) { - playerPanel.setSelectedTab((InfoTab) player.getValue()); + public void restoreOldZones(PlayerView playerView, PlayerZoneUpdates playerZoneUpdates) { + for(PlayerZoneUpdate update : playerZoneUpdates) { + PlayerView player = update.getPlayer(); + + ZoneType zone = null; + for(ZoneType type : update.getZones()) { + zone = type; + break; } + + if (zone == null) { return; } + + final VPlayerPanel playerPanel = view.getPlayerPanel(player); + final InfoTab zoneTab = playerPanel.getZoneTab(zone); + playerPanel.setSelectedTab(zoneTab); } } @@ -381,7 +388,7 @@ public class MatchController extends AbstractGuiGame { @Override public void hideZones(final PlayerView controller, final Iterable zonesToUpdate) { - view.hideZones(controller, zonesToUpdate); + view.hideZones(controller, zonesToUpdate); } @Override diff --git a/forge-gui-mobile/src/forge/screens/match/views/VStack.java b/forge-gui-mobile/src/forge/screens/match/views/VStack.java index d37c64f4c8c..e20dddb7e37 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VStack.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VStack.java @@ -31,6 +31,7 @@ import forge.menu.FDropDown; import forge.menu.FMenuItem; import forge.menu.FMenuTab; import forge.menu.FPopupMenu; +import forge.player.PlayerZoneUpdates; import forge.screens.match.MatchController; import forge.screens.match.TargetingOverlay; import forge.toolbox.FCardPanel; @@ -55,6 +56,7 @@ public class VStack extends FDropDown { private StackInstanceDisplay activeItem; private StackItemView activeStackInstance; private Map playersWithValidTargets; + private PlayerZoneUpdates restorablePlayerZones = null; private int stackSize; @@ -70,6 +72,8 @@ public class VStack extends FDropDown { private void revealTargetZones() { if (activeStackInstance == null) { return; } + PlayerView player = MatchController.instance.getCurrentPlayer(); + final Set zones = new HashSet<>(); playersWithValidTargets = new HashMap<>(); for (final CardView c : activeStackInstance.getTargetCards()) { @@ -79,14 +83,15 @@ public class VStack extends FDropDown { } } if (zones.isEmpty() || playersWithValidTargets.isEmpty()) { return; } - MatchController.instance.openZones(zones, playersWithValidTargets); + restorablePlayerZones = MatchController.instance.openZones(player, zones, playersWithValidTargets); } //restore old zones when active stack instance changes private void restoreOldZones() { - if (playersWithValidTargets == null) { return; } - MatchController.instance.restoreOldZones(playersWithValidTargets); - playersWithValidTargets = null; + if (restorablePlayerZones == null) { return; } + PlayerView player = MatchController.instance.getCurrentPlayer(); + MatchController.instance.restoreOldZones(player, restorablePlayerZones); + restorablePlayerZones = null; } @Override From a539d9bf5aec09ee7b089db689e25e399f177611 Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Tue, 11 Feb 2020 03:12:46 +0000 Subject: [PATCH 03/20] 2/10/2020 tweaks --- forge-gui/res/cardsfolder/h/hapatra_vizier_of_poisons.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/h/hapatra_vizier_of_poisons.txt b/forge-gui/res/cardsfolder/h/hapatra_vizier_of_poisons.txt index 163ef02047c..bf96c3ec72a 100644 --- a/forge-gui/res/cardsfolder/h/hapatra_vizier_of_poisons.txt +++ b/forge-gui/res/cardsfolder/h/hapatra_vizier_of_poisons.txt @@ -2,7 +2,7 @@ Name:Hapatra, Vizier of Poisons ManaCost:B G Types:Legendary Creature Human Cleric PT:2/2 -T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigPutCounter | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, you may put a -1/-1 counter on target creature. +T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Opponent | CombatDamage$ True | TriggerZones$ Battlefield | OptionalDecider$ You | Execute$ TrigPutCounter | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, you may put a -1/-1 counter on target creature. SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ M1M1 | CounterNum$ 1 | IsCurse$ True T:Mode$ CounterAddedOnce | ValidCard$ Creature | ValidSource$ You | CounterType$ M1M1 | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever you put one or more -1/-1 counters on a creature, create a 1/1 green Snake creature token with deathtouch. SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_snake_deathtouch | TokenOwner$ You | LegacyImage$ g 1 1 snake deathtouch akh From 9d5d6dd6d208a5b197cf40ec79db15c28e050e25 Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Wed, 12 Feb 2020 04:24:23 +0000 Subject: [PATCH 04/20] Promo arts update (THB, ELD) --- forge-gui/res/blockdata/printsheets.txt | 104 +++++++++++- .../res/editions/Theros Beyond Death.txt | 158 ++++++++++-------- forge-gui/res/editions/Throne of Eldraine.txt | 103 ++++++++++++ 3 files changed, 297 insertions(+), 68 deletions(-) diff --git a/forge-gui/res/blockdata/printsheets.txt b/forge-gui/res/blockdata/printsheets.txt index 07e25c8c98e..90600a81ead 100644 --- a/forge-gui/res/blockdata/printsheets.txt +++ b/forge-gui/res/blockdata/printsheets.txt @@ -1556,6 +1556,39 @@ Evolving Wilds 13 Swamp|M20 [ELD Secret Cards] +Garruk, Cursed Huntsman|ELD|2 +Oko, Thief of Crowns|ELD|2 +The Royal Scions|ELD|2 +Ardenvale Tactician|ELD|2 +Faerie Guidemother|ELD|2 +Giant Killer|ELD|2 +Lonesome Unicorn|ELD|2 +Realm-Cloaked Giant|ELD|2 +Shepherd of the Flock|ELD|2 +Silverflame Squire|ELD|2 +Animating Faerie|ELD|2 +Brazen Borrower|ELD|2 +Fae of Wishes|ELD|2 +Hypnotic Sprite|ELD|2 +Merfolk Secretkeeper|ELD|2 +Queen of Ice|ELD|2 +Foulmire Knight|ELD|2 +Murderous Rider|ELD|2 +Order of Midnight|ELD|2 +Reaper of Night|ELD|2 +Smitten Swordmaster|ELD|2 +Bonecrusher Giant|ELD|2 +Embereth Shieldbreaker|ELD|2 +Merchant of the Vale|ELD|2 +Rimrock Knight|ELD|2 +Beanstalk Giant|ELD|2 +Curious Pair|ELD|2 +Flaxen Intruder|ELD|2 +Garenbrig Carver|ELD|2 +Lovestruck Beast|ELD|2 +Rosethorn Acolyte|ELD|2 +Tuinvale Treefolk|ELD|2 +Oakhame Ranger|ELD|2 Kenrith, the Returned King Rowan, Fearless Sparkmage Garrison Griffin @@ -1587,6 +1620,69 @@ Syr Gwyn, Hero of Ashvale Arcane Signet Tome of Legends Command Tower +Acclaimed Contender|ELD|2 +Charming Prince|ELD|2 +The Circle of Loyalty|ELD|2 +Happily Ever After|ELD|2 +Harmonious Archon|ELD|2 +Hushbringer|ELD|2 +Linden, the Steadfast Queen|ELD|2 +Worthy Knight|ELD|2 +Emry, Lurker of the Loch|ELD|2 +Folio of Fancies|ELD|2 +Gadwick, the Wizened|ELD|2 +The Magic Mirror|ELD|2 +Midnight Clock|ELD|2 +Mirrormade|ELD|2 +Stolen by the Fae|ELD|2 +Vantress Gargoyle|ELD|2 +Ayara, First of Lochthwain|ELD|2 +Blacklance Paragon|ELD|2 +The Cauldron of Eternity|ELD|2 +Clackbridge Troll|ELD|2 +Oathsworn Knight|ELD|2 +Piper of the Swarm|ELD|2 +Rankle, Master of Pranks|ELD|2 +Wishclaw Talisman|ELD|2 +Witch's Vengeance|ELD|2 +Embercleave|ELD|2 +Fervent Champion|ELD|2 +Fires of Invention|ELD|2 +Irencrag Feat|ELD|2 +Irencrag Pyromancer|ELD|2 +Opportunistic Dragon|ELD|2 +Robber of the Rich|ELD|2 +Sundering Stroke|ELD|2 +Torbran, Thane of Red Fell|ELD|2 +Feasting Troll King|ELD|2 +Gilded Goose|ELD|2 +The Great Henge|ELD|2 +Once Upon A Time|ELD|2 +Questing Beast|ELD|2 +Return of the Wildspeaker|ELD|2 +Wicked Wolf|ELD|2 +Wildborn Preserver|ELD|2 +Yorvo, Lord of Garenbrig|ELD|2 +Dance of the Manse|ELD|2 +Doom Foretold|ELD|2 +Escape to the Wilds|ELD|2 +Faeburrow Elder|ELD|2 +Lochmere Serpent|ELD|2 +Outlaws' Merriment|ELD|2 +Stormfist Crusader|ELD|2 +Sorcerous Spyglass|ELD|2 +Stonecoil Serpent|ELD|2 +Castle Ardenvale|ELD|2 +Castle Embereth|ELD|2 +Castle Garenbrig|ELD|2 +Castle Lochthwain|ELD|2 +Castle Vantress|ELD|2 +Piper of the Swarm|ELD|3 +Glass Casket|ELD|2 +Slaying Fire|ELD|2 +Kenrith's Transformation|ELD|2 +Improbable Alliance|ELD|2 +Inspiring Veteran|ELD|2 [THB Secret Cards] Athreos, Shroud-Veiled @@ -1674,4 +1770,10 @@ Temple of Abandon|THB|2 Temple of Deceit|THB|2 Temple of Enlightenment|THB|2 Temple of Malice|THB|2 -Temple of Plenty|THB|2 \ No newline at end of file +Temple of Plenty|THB|2 +Arasta of the Endless Web|THB|3 +Alseid of Life's Bounty|THB|2 +Thirst For Meaning|THB|2 +Gray Merchant of Asphodel|THB|2 +Thrill of Possibility|THB|2 +Wolfwillow Haven|THB|2 \ No newline at end of file diff --git a/forge-gui/res/editions/Theros Beyond Death.txt b/forge-gui/res/editions/Theros Beyond Death.txt index edee4e218d8..95bb4cfcfd3 100644 --- a/forge-gui/res/editions/Theros Beyond Death.txt +++ b/forge-gui/res/editions/Theros Beyond Death.txt @@ -263,21 +263,25 @@ Prerelease=6 Boosters, 1 RareMythic+ 252 L Swamp 253 L Mountain 254 L Forest -#255 M Elspeth, Sun's Nemesis -#256 M Ashiok, Nightmare Muse -#257 M Calix, Destiny's Hand -#258 U Daxos, Blessed by the Sun -#259 M Heliod, Sun-Crowned -#260 U Callaphe, Beloved of the Sea -#261 M Thassa, Deep-Dwelling -#262 M Erebos, Bleak-Hearted -#263 U Tymaret, Chosen from Death -#264 U Anax, Hardened in the Forge -#265 M Purphoros, Bronze-Blooded -#266 M Nylea, Keen-Eyed -#267 U Renata, Called to the Hunt -#268 M Klothys, God of Destiny +#Statue borderless planeswalkers +255 M Elspeth, Sun's Nemesis +256 M Ashiok, Nightmare Muse +#Constellation Gods and Demigods +257 M Calix, Destiny's Hand +258 U Daxos, Blessed by the Sun +259 M Heliod, Sun-Crowned +260 U Callaphe, Beloved of the Sea +261 M Thassa, Deep-Dwelling +262 M Erebos, Bleak-Hearted +263 U Tymaret, Chosen from Death +264 U Anax, Hardened in the Forge +265 M Purphoros, Bronze-Blooded +266 M Nylea, Keen-Eyed +267 U Renata, Called to the Hunt +268 M Klothys, God of Destiny +#Buy-A-Box Promo 269 M Athreos, Shroud-Veiled +#Planeswalker Deck Cards 270 M Elspeth, Undaunted Hero 271 U Eidolon of Inspiration 272 R Elspeth's Devotee @@ -296,6 +300,7 @@ Prerelease=6 Boosters, 1 RareMythic+ 285 L Mountain 286 L Forest 287 L Forest +#Theme Booster Exclusive Rares 288 R Grasping Giant 289 R Victory's Envoy 290 R Sphinx Mindbreaker @@ -306,59 +311,78 @@ Prerelease=6 Boosters, 1 RareMythic+ 295 R Terror of Mount Velus 296 R Ironscale Hydra 297 R Treeshaker Chimera -#298 R Archon of Sun's Grace -#299 R Eidolon of Obstruction -#300 R Heliod's Intervention -#301 R Idyllic Tutor -#302 R Shatter the Sky -#304 R Ashiok's Erasure -#305 R Nadir Kraken -#306 R Protean Thaumaturge -#307 R Thassa's Intervention -#308 R Thassa's Oracle -#309 R Thryx, the Sudden Storm -#310 R Wavebreak Hippocamp -#311 R Aphemia, the Cacophony -#312 R Eat to Extinction -#313 R Erebos's Intervention -#314 R Gravebreaker Lamia -#315 R Nightmare Shepherd -#316 R Treacherous Blessing -#317 R Woe Strider -#318 M Ox of Agonas -#319 R Phoenix of Ash -#320 R Purphoros's Intervention -#321 R Storm Herald -#322 R Storm's Wrath -#323 R Tectonic Giant -#324 R Underworld Breach -#325 R Arasta of the Endless Web -#326 R Dryad of the Ilysian Grove -#327 R Mantle of the Wolf -#328 R Nessian Boar -#329 R Nylea's Intervention -#330 M Nyxbloom Ancient -#331 R Setessan Champion -#332 R Allure of the Unknown -#333 R Atris, Oracle of Half-Truths -#334 R Bronzehide Lion -#335 R Dalakos, Crafter of Wonders -#336 R Dream Trawler -#337 R Enigmatic Incarnation -#338 R Gallia of the Endless Dance -#339 R Haktos the Unscarred -#340 M Kroxa, Titan of Death's Hunger -#341 R Kunoros, Hound of Athreos -#342 M Polukranos, Unchained -#343 M Uro, Titan of Nature's Wrath -#344 R Nyx Lotus -#345 R Shadowspear -#346 R Labyrinth of Skophos -#347 R Temple of Abandon -#348 R Temple of Deceit -#349 R Temple of Enlightenment -#350 R Temple of Malice -#351 R Temple of Plenty +#Borderless art rares and mythics +288 R Grasping Giant +289 R Victory's Envoy +290 R Sphinx Mindbreaker +291 R Serpent of Yawning Depths +292 R Demon of Loathing +293 R Underworld Sentinel +294 R Deathbellow War Cry +295 R Terror of Mount Velus +296 R Ironscale Hydra +297 R Treeshaker Chimera +298 R Archon of Sun's Grace +299 R Eidolon of Obstruction +300 R Heliod's Intervention +301 R Idyllic Tutor +302 R Shatter the Sky +304 R Ashiok's Erasure +305 R Nadir Kraken +306 R Protean Thaumaturge +307 R Thassa's Intervention +308 R Thassa's Oracle +309 R Thryx, the Sudden Storm +310 R Wavebreak Hippocamp +311 R Aphemia, the Cacophony +312 R Eat to Extinction +313 R Erebos's Intervention +314 R Gravebreaker Lamia +315 R Nightmare Shepherd +316 R Treacherous Blessing +317 R Woe Strider +318 M Ox of Agonas +319 R Phoenix of Ash +320 R Purphoros's Intervention +321 R Storm Herald +322 R Storm's Wrath +323 R Tectonic Giant +324 R Underworld Breach +325 R Arasta of the Endless Web +326 R Dryad of the Ilysian Grove +327 R Mantle of the Wolf +328 R Nessian Boar +329 R Nylea's Intervention +330 M Nyxbloom Ancient +331 R Setessan Champion +332 R Allure of the Unknown +333 R Atris, Oracle of Half-Truths +334 R Bronzehide Lion +335 R Dalakos, Crafter of Wonders +336 R Dream Trawler +337 R Enigmatic Incarnation +338 R Gallia of the Endless Dance +339 R Haktos the Unscarred +340 M Kroxa, Titan of Death's Hunger +341 R Kunoros, Hound of Athreos +342 M Polukranos, Unchained +343 M Uro, Titan of Nature's Wrath +344 R Nyx Lotus +345 R Shadowspear +346 R Labyrinth of Skophos +347 R Temple of Abandon +348 R Temple of Deceit +349 R Temple of Enlightenment +350 R Temple of Malice +351 R Temple of Plenty +#Bundle promo +R Arasta of the Endless Web +#Promo Pack +U Alseid of Life's Bounty +C Thirst For Meaning +U Gray Merchant of Asphodel +C Thrill of Possibility +U Wolfwillow Haven [tokens] b_2_2_zombie diff --git a/forge-gui/res/editions/Throne of Eldraine.txt b/forge-gui/res/editions/Throne of Eldraine.txt index d5cb63e5419..91b62358f61 100644 --- a/forge-gui/res/editions/Throne of Eldraine.txt +++ b/forge-gui/res/editions/Throne of Eldraine.txt @@ -279,7 +279,44 @@ Prerelease=6 Boosters, 1 RareMythic+ 267 L Forest 268 L Forest 269 L Forest +#Borderless Planeswalkers +270 M Garruk, Cursed Huntsman +271 M Oko, Thief of Crowns +272 M The Royal Scions +#Storybook Frames +C Ardenvale Tactician +C Faerie Guidemother +R Giant Killer +C Lonesome Unicorn +M Realm-Cloaked Giant +U Shepherd of the Flock +C Silverflame Squire +U Animating Faerie +M Brazen Borrower +R Fae of Wishes +U Hypnotic Sprite +C Merfolk Secretkeeper +C Queen of Ice +U Foulmire Knight +R Murderous Rider +U Order of Midnight +C Reaper of Night +C Smitten Swordmaster +R Bonecrusher Giant +U Embereth Shieldbreaker +C Merchant of the Vale +C Rimrock Knight +U Beanstalk Giant +C Curious Pair +U Flaxen Intruder +C Garenbrig Carver +R Lovestruck Beast +C Rosethorn Acolyte +C Tuinvale Treefolk +U Oakhame Ranger +#Buy-A-Box Promo 303 M Kenrith, the Returned King +#Planeswalker Deck Cards 304 M Rowan, Fearless Sparkmage 305 C Garrison Griffin 306 U Rowan's Battleguard @@ -310,6 +347,72 @@ Prerelease=6 Boosters, 1 RareMythic+ 331 C Arcane Signet 332 R Tome of Legends 333 C Command Tower +#Borderless art rares and mythics +R Acclaimed Contender +R Charming Prince +M The Circle of Loyalty +R Happily Ever After +M Harmonious Archon +R Hushbringer +R Linden, the Steadfast Queen +R Worthy Knight +R Emry, Lurker of the Loch +R Folio of Fancies +R Gadwick, the Wizened +M The Magic Mirror +R Midnight Clock +R Mirrormade +R Stolen by the Fae +R Vantress Gargoyle +R Ayara, First of Lochthwain +R Blacklance Paragon +M The Cauldron of Eternity +R Clackbridge Troll +R Oathsworn Knight +R Piper of the Swarm +M Rankle, Master of Pranks +R Wishclaw Talisman +R Witch's Vengeance +M Embercleave +R Fervent Champion +R Fires of Invention +R Irencrag Feat +R Irencrag Pyromancer +R Opportunistic Dragon +M Robber of the Rich +R Sundering Stroke +R Torbran, Thane of Red Fell +R Feasting Troll King +R Gilded Goose +M The Great Henge +R Once Upon A Time +M Questing Beast +R Return of the Wildspeaker +R Wicked Wolf +R Wildborn Preserver +R Yorvo, Lord of Garenbrig +R Dance of the Manse +R Doom Foretold +R Escape to the Wilds +R Faeburrow Elder +R Lochmere Serpent +M Outlaws' Merriment +R Stormfist Crusader +R Sorcerous Spyglass +R Stonecoil Serpent +R Castle Ardenvale +R Castle Embereth +R Castle Garenbrig +R Castle Lochthwain +R Castle Vantress +#Bundle promo +R Piper of the Swarm +#Promo Pack +U Glass Casket +U Slaying Fire +U Kenrith's Transformation +U Improbable Alliance +U Inspiring Veteran [tokens] w_0_1_goat From 8bda30179ecf6b9430115458c47bd125f30171e8 Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Wed, 12 Feb 2020 04:24:38 +0000 Subject: [PATCH 05/20] Brawl format update --- forge-gui/res/formats/Casual/Brawl.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-gui/res/formats/Casual/Brawl.txt b/forge-gui/res/formats/Casual/Brawl.txt index 78fa086854a..804db4799d5 100644 --- a/forge-gui/res/formats/Casual/Brawl.txt +++ b/forge-gui/res/formats/Casual/Brawl.txt @@ -3,5 +3,5 @@ Name:Brawl Order:101 Type:Casual Subtype:Commander -Sets:GRN, RNA, WAR, M20, ELD -Banned:Oko, Thief of Crowns +Sets:GRN, RNA, WAR, M20, ELD, THB +Banned:Sorcerous Spyglass;Oko, Thief of Crowns From e7a5ee57f1a04d8e3dd38f3b0c2502bd15273d0f Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Wed, 12 Feb 2020 14:43:40 +0000 Subject: [PATCH 06/20] =?UTF-8?q?Promo=20arts=20=E2=80=93=20correcting=20a?= =?UTF-8?q?=20few=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- forge-gui/res/blockdata/printsheets.txt | 4 +++- forge-gui/res/editions/Theros Beyond Death.txt | 11 +---------- forge-gui/res/editions/Throne of Eldraine.txt | 3 ++- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/forge-gui/res/blockdata/printsheets.txt b/forge-gui/res/blockdata/printsheets.txt index 90600a81ead..809d0d7002c 100644 --- a/forge-gui/res/blockdata/printsheets.txt +++ b/forge-gui/res/blockdata/printsheets.txt @@ -1675,8 +1675,9 @@ Stonecoil Serpent|ELD|2 Castle Ardenvale|ELD|2 Castle Embereth|ELD|2 Castle Garenbrig|ELD|2 -Castle Lochthwain|ELD|2 +Castle Locthwain|ELD|2 Castle Vantress|ELD|2 +Fabled Passage|ELD|2 Piper of the Swarm|ELD|3 Glass Casket|ELD|2 Slaying Fire|ELD|2 @@ -1723,6 +1724,7 @@ Eidolon of Obstruction|THB|2 Heliod's Intervention|THB|2 Idyllic Tutor|THB|2 Shatter the Sky|THB|2 +Taranika, Akroan Veteran|THB|2 Ashiok's Erasure|THB|2 Nadir Kraken|THB|2 Protean Thaumaturge|THB|2 diff --git a/forge-gui/res/editions/Theros Beyond Death.txt b/forge-gui/res/editions/Theros Beyond Death.txt index 95bb4cfcfd3..af7be4b1772 100644 --- a/forge-gui/res/editions/Theros Beyond Death.txt +++ b/forge-gui/res/editions/Theros Beyond Death.txt @@ -312,21 +312,12 @@ Prerelease=6 Boosters, 1 RareMythic+ 296 R Ironscale Hydra 297 R Treeshaker Chimera #Borderless art rares and mythics -288 R Grasping Giant -289 R Victory's Envoy -290 R Sphinx Mindbreaker -291 R Serpent of Yawning Depths -292 R Demon of Loathing -293 R Underworld Sentinel -294 R Deathbellow War Cry -295 R Terror of Mount Velus -296 R Ironscale Hydra -297 R Treeshaker Chimera 298 R Archon of Sun's Grace 299 R Eidolon of Obstruction 300 R Heliod's Intervention 301 R Idyllic Tutor 302 R Shatter the Sky +303 R Taranika, Akroan Veteran 304 R Ashiok's Erasure 305 R Nadir Kraken 306 R Protean Thaumaturge diff --git a/forge-gui/res/editions/Throne of Eldraine.txt b/forge-gui/res/editions/Throne of Eldraine.txt index 91b62358f61..7448701073d 100644 --- a/forge-gui/res/editions/Throne of Eldraine.txt +++ b/forge-gui/res/editions/Throne of Eldraine.txt @@ -403,8 +403,9 @@ R Stonecoil Serpent R Castle Ardenvale R Castle Embereth R Castle Garenbrig -R Castle Lochthwain +R Castle Locthwain R Castle Vantress +R Fabled Passage #Bundle promo R Piper of the Swarm #Promo Pack From 6ae29a311a2e6e945724fb4deb1ff59e195ac56b Mon Sep 17 00:00:00 2001 From: Zimtente Date: Wed, 12 Feb 2020 17:40:50 +0100 Subject: [PATCH 07/20] fixed mana symbols vanishing/not being in the right place in card descriptions --- forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java b/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java index a80b635e0fe..e0d059d96ca 100644 --- a/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java +++ b/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java @@ -1023,7 +1023,7 @@ public class FSkin { //format mana symbols to display as icons pattern = "\\{([A-Z0-9]+)\\}|\\{([A-Z0-9]+)/([A-Z0-9]+)\\}"; //fancy pattern needed so "/" can be omitted from replacement try { - replacement = ""; + replacement = ""; str = str.replaceAll(pattern, replacement); } catch (final MalformedURLException e) { e.printStackTrace(); From 562a9e1f8d2df423a7e672ab72848c7e2c69c608 Mon Sep 17 00:00:00 2001 From: Sol Date: Wed, 12 Feb 2020 18:04:34 +0000 Subject: [PATCH 08/20] Update ANNOUNCEMENTS.txt --- forge-gui/release-files/ANNOUNCEMENTS.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/forge-gui/release-files/ANNOUNCEMENTS.txt b/forge-gui/release-files/ANNOUNCEMENTS.txt index 64bb4abaa7f..aa8d1a24122 100644 --- a/forge-gui/release-files/ANNOUNCEMENTS.txt +++ b/forge-gui/release-files/ANNOUNCEMENTS.txt @@ -1,9 +1,6 @@ #Add one announcement per line -Theros Beyond Death Prerelease! -"Prerelease" limited mode for latest sets +We believe the issue with 1.8.0_211 or greater have been resolved. Let us know if you are still on the latest version and things are better now. Bunches of bug fixes, Continued work on Translations [b]Forge now requires Java 8 (or newer). You will not be able to start the game if you are not yet running Java 8.[/b] -For some reason Oracle hates Forge and version 1.8.0_211 does bad things with Forge for unknown reasons. Downgrade to 202 for a beter time. -https://www.oracle.com/technetwork/java/javase/downloads/java-archive-javase8-2177648.html We have a Discord server for hanging out with Forge devs and other Forge fans. Feel free to [url=https://discord.gg/3v9JCVr]jump on in and say hi[/url]! \ No newline at end of file From 01bd5e5d54dc6b1819e78bb803391a4fbc1ff3ff Mon Sep 17 00:00:00 2001 From: Sol Date: Wed, 12 Feb 2020 18:05:09 +0000 Subject: [PATCH 09/20] Update CONTRIBUTORS.txt --- forge-gui/release-files/CONTRIBUTORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/forge-gui/release-files/CONTRIBUTORS.txt b/forge-gui/release-files/CONTRIBUTORS.txt index ffae9fb7a4b..a10b0182abb 100644 --- a/forge-gui/release-files/CONTRIBUTORS.txt +++ b/forge-gui/release-files/CONTRIBUTORS.txt @@ -33,6 +33,7 @@ tjtillman tojammot torridus Xyx +Zimtente Zuchinni (If you think your name should be on this list, add it with your next contribution) \ No newline at end of file From 50c8e129d02034301128fd46c5052f463eb81d4b Mon Sep 17 00:00:00 2001 From: Zimtente Date: Wed, 12 Feb 2020 19:07:45 +0100 Subject: [PATCH 10/20] changed hardcoded symbol WIDTH and HEIGHT to constants --- forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java b/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java index e0d059d96ca..beaa4601708 100644 --- a/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java +++ b/forge-gui-desktop/src/main/java/forge/toolbox/FSkin.java @@ -61,6 +61,9 @@ import forge.util.Localizer; */ public class FSkin { + public static final int SYMBOL_WIDTH = 13; + public static final int SYMBOL_HEIGHT = 13; + /** * Retrieves a color from this skin's color map. * @@ -77,7 +80,7 @@ public class FSkin { * * @param clr0 {@link java.awt.Color} * @param step int - * @return {@link java.awt.Color} + * @return {@link java.awt.CFaceolor} */ public static Color stepColor(final Color clr0, final int step) { int r = clr0.getRed(); @@ -1006,7 +1009,7 @@ public class FSkin { private static void addEncodingSymbol(final String key, final FSkinProp skinProp) { final String path = ForgeConstants.CACHE_SYMBOLS_DIR + "/" + key.replace("/", "") + ".png"; - getImage(skinProp).save(path, 13, 13); + getImage(skinProp).save(path, SYMBOL_WIDTH, SYMBOL_HEIGHT); } public static String encodeSymbols(String str, final boolean formatReminderText) { @@ -1023,7 +1026,7 @@ public class FSkin { //format mana symbols to display as icons pattern = "\\{([A-Z0-9]+)\\}|\\{([A-Z0-9]+)/([A-Z0-9]+)\\}"; //fancy pattern needed so "/" can be omitted from replacement try { - replacement = ""; + replacement = ""; str = str.replaceAll(pattern, replacement); } catch (final MalformedURLException e) { e.printStackTrace(); From d1c2096024d2bc3757604923e56f693ecb41fde0 Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Thu, 13 Feb 2020 05:53:48 +0000 Subject: [PATCH 11/20] Today's tweaks --- forge-gui/res/blockdata/printsheets.txt | 2 +- forge-gui/res/cardsfolder/m/mire_in_misery.txt | 2 +- forge-gui/res/editions/Throne of Eldraine.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/forge-gui/res/blockdata/printsheets.txt b/forge-gui/res/blockdata/printsheets.txt index 809d0d7002c..c98ef6015bd 100644 --- a/forge-gui/res/blockdata/printsheets.txt +++ b/forge-gui/res/blockdata/printsheets.txt @@ -1636,7 +1636,7 @@ Midnight Clock|ELD|2 Mirrormade|ELD|2 Stolen by the Fae|ELD|2 Vantress Gargoyle|ELD|2 -Ayara, First of Lochthwain|ELD|2 +Ayara, First of Locthwain|ELD|2 Blacklance Paragon|ELD|2 The Cauldron of Eternity|ELD|2 Clackbridge Troll|ELD|2 diff --git a/forge-gui/res/cardsfolder/m/mire_in_misery.txt b/forge-gui/res/cardsfolder/m/mire_in_misery.txt index 49023107ee1..0e26e1e763a 100644 --- a/forge-gui/res/cardsfolder/m/mire_in_misery.txt +++ b/forge-gui/res/cardsfolder/m/mire_in_misery.txt @@ -1,5 +1,5 @@ Name:Mire in Misery ManaCost:1 B Types:Sorcery -A:SP$ Sacrifice | Cost$ 1 B | Valid$ Creature,Enchantment | SacMessage$ Creature or Enchantment | Defined$ Player.Opponent | SpellDescription$ Each opponent sacrifices a creature or enchantment. +A:SP$ Sacrifice | Cost$ 1 B | SacValid$ Creature,Enchantment | SacMessage$ Creature or Enchantment | Defined$ Player.Opponent | SpellDescription$ Each opponent sacrifices a creature or enchantment. Oracle:Each opponent sacrifices a creature or enchantment. diff --git a/forge-gui/res/editions/Throne of Eldraine.txt b/forge-gui/res/editions/Throne of Eldraine.txt index 7448701073d..1c567deab3e 100644 --- a/forge-gui/res/editions/Throne of Eldraine.txt +++ b/forge-gui/res/editions/Throne of Eldraine.txt @@ -364,7 +364,7 @@ R Midnight Clock R Mirrormade R Stolen by the Fae R Vantress Gargoyle -R Ayara, First of Lochthwain +R Ayara, First of Locthwain R Blacklance Paragon M The Cauldron of Eternity R Clackbridge Troll From d35cb8de32d65d9bd7806301e334b42f6adc2a68 Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Thu, 13 Feb 2020 05:54:31 +0000 Subject: [PATCH 12/20] Bling cards --- .../res/editions/Ultimate Box Topper.txt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 forge-gui/res/editions/Ultimate Box Topper.txt diff --git a/forge-gui/res/editions/Ultimate Box Topper.txt b/forge-gui/res/editions/Ultimate Box Topper.txt new file mode 100644 index 00000000000..65b99372a70 --- /dev/null +++ b/forge-gui/res/editions/Ultimate Box Topper.txt @@ -0,0 +1,48 @@ +[metadata] +Code=PUMA +Date=2018-12-07 +Name=Ultimate Box Topper - Ultimate Masters +MciCode=uma +Type=Other + +[cards] +1 M Emrakul, the Aeons Torn +2 M Karn Liberated +3 M Kozilek, Butcher of Truth +4 M Ulamog, the Infinite Gyre +5 M Snapcaster Mage +6 M Temporal Manipulation +7 M Bitterblossom +8 M Demonic Tutor +9 M Goryo's Vengeance +10 M Liliana of the Veil +11 M Mikaeus, the Unhallowed +12 M Reanimate +13 M Tasigur, the Golden Fang +14 M Balefire Dragon +15 M Through the Breach +16 M Eternal Witness +17 M Life from the Loam +18 M Noble Hierarch +19 M Tarmogoyf +20 M Vengevine +21 M Gaddock Teeg +22 M Leovold, Emissary of Trest +23 M Lord of Extinction +24 M Maelstrom Pulse +25 M Sigarda, Host of Herons +26 M Fulminator Mage +27 M Kitchen Finks +28 M Engineered Explosives +29 M Mana Vault +30 M Platinum Emperion +31 M Ancient Tomb +32 M Cavern of Souls +33 M Celestial Colonnade +34 M Creeping Tar Pit +35 M Dark Depths +36 M Karakas +37 M Lavaclaw Reaches +38 M Raging Ravine +39 M Stirring Wildwood +40 M Urborg, Tomb of Yawgmoth \ No newline at end of file From bee93cbab3829e0fd96dc101c1bf4c9631dddfe8 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Thu, 13 Feb 2020 10:29:00 +0000 Subject: [PATCH 13/20] Player: update LastState when Land is played --- forge-game/src/main/java/forge/game/player/Player.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 6bd8dbc3be1..af850385eee 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1708,7 +1708,8 @@ public class Player extends GameEntity implements Comparable { if (land.isFaceDown()) { land.turnFaceUp(); } - game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null); + final Card c = game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null); + game.updateLastStateForCard(c); // play a sound game.fireEvent(new GameEventLandPlayed(this, land)); From 57bc3f930a75922d86b62c2f5f420999386b0b85 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Thu, 13 Feb 2020 15:12:52 +0000 Subject: [PATCH 14/20] TriggerDamageAll: add new Trigger for Mindblade Render --- .../main/java/forge/game/CardTraitBase.java | 8 +++ .../java/forge/game/GameObjectPredicates.java | 44 ++++++++++++++++ .../game/ability/effects/DamageAllEffect.java | 2 +- .../ability/effects/DamageDealEffect.java | 4 +- .../ability/effects/DamageEachEffect.java | 2 +- .../ability/effects/DamageResolveEffect.java | 2 +- .../game/ability/effects/FightEffect.java | 2 +- .../ability/effects/RepeatEachEffect.java | 2 +- .../java/forge/game/card/CardDamageMap.java | 52 +++++++++++++++---- .../main/java/forge/game/combat/Combat.java | 2 +- .../main/java/forge/game/cost/CostDamage.java | 2 +- .../game/replacement/ReplacementEffect.java | 7 +-- .../forge/game/spellability/SpellAbility.java | 7 +-- .../game/staticability/StaticAbility.java | 8 +-- .../main/java/forge/game/trigger/Trigger.java | 5 +- .../forge/game/trigger/TriggerDamageAll.java | 52 +++++++++++++++++++ .../java/forge/game/trigger/TriggerType.java | 1 + .../res/cardsfolder/m/mindblade_render.txt | 8 +++ 18 files changed, 171 insertions(+), 39 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/GameObjectPredicates.java create mode 100644 forge-game/src/main/java/forge/game/trigger/TriggerDamageAll.java create mode 100644 forge-gui/res/cardsfolder/m/mindblade_render.txt diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java index e63ae76c366..1df09691ee1 100644 --- a/forge-game/src/main/java/forge/game/CardTraitBase.java +++ b/forge-game/src/main/java/forge/game/CardTraitBase.java @@ -561,4 +561,12 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView { // this does overwrite the original MapParams this.originalMapParams = Maps.newHashMap(this.mapParams); } + + protected void copyHelper(CardTraitBase copy, Card host) { + copy.originalMapParams = Maps.newHashMap(originalMapParams); + copy.mapParams = Maps.newHashMap(originalMapParams); + copy.sVars = Maps.newHashMap(sVars); + // dont use setHostCard to not trigger the not copied parts yet + copy.hostCard = host; + } } diff --git a/forge-game/src/main/java/forge/game/GameObjectPredicates.java b/forge-game/src/main/java/forge/game/GameObjectPredicates.java new file mode 100644 index 00000000000..9331bcb6a89 --- /dev/null +++ b/forge-game/src/main/java/forge/game/GameObjectPredicates.java @@ -0,0 +1,44 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.game; + +import com.google.common.base.Predicate; + +import forge.game.card.Card; +import forge.game.player.Player; +import forge.game.spellability.SpellAbility; + + +/** + *

+ * Predicate interface. + *

+ * + * @author Forge + */ +public final class GameObjectPredicates { + + public static final Predicate restriction(final String[] restrictions, final Player sourceController, final Card source, final SpellAbility spellAbility) { + return new Predicate() { + @Override + public boolean apply(final GameObject c) { + return (c != null) && c.isValid(restrictions, sourceController, source, spellAbility); + } + }; + } +} diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java index c68659e0709..a4e8e6c1217 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java @@ -122,7 +122,7 @@ public class DamageAllEffect extends DamageBaseEffect { if (!usedDamageMap) { preventMap.triggerPreventDamage(false); - damageMap.triggerDamageDoneOnce(false, sa); + damageMap.triggerDamageDoneOnce(false, game, sa); preventMap.clear(); damageMap.clear(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java index 5ba5f4d8203..6092fde9af8 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java @@ -166,7 +166,7 @@ public class DamageDealEffect extends DamageBaseEffect { if (!usedDamageMap) { preventMap.triggerPreventDamage(false); // non combat damage cause lifegain there - damageMap.triggerDamageDoneOnce(false, sa); + damageMap.triggerDamageDoneOnce(false, game, sa); preventMap.clear(); damageMap.clear(); @@ -215,7 +215,7 @@ public class DamageDealEffect extends DamageBaseEffect { if (!usedDamageMap) { preventMap.triggerPreventDamage(false); // non combat damage cause lifegain there - damageMap.triggerDamageDoneOnce(false, sa); + damageMap.triggerDamageDoneOnce(false, game, sa); preventMap.clear(); damageMap.clear(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java index 8887b60bfb6..913b6e979a2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java @@ -136,7 +136,7 @@ public class DamageEachEffect extends DamageBaseEffect { if (!usedDamageMap) { preventMap.triggerPreventDamage(false); - damageMap.triggerDamageDoneOnce(false, sa); + damageMap.triggerDamageDoneOnce(false, game, sa); preventMap.clear(); damageMap.clear(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java index 6ed532889a6..a9d7c35a677 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java @@ -25,7 +25,7 @@ public class DamageResolveEffect extends SpellAbilityEffect { } // non combat damage cause lifegain there if (damageMap != null) { - damageMap.triggerDamageDoneOnce(false, sa); + damageMap.triggerDamageDoneOnce(false, sa.getHostCard().getGame(), sa); damageMap.clear(); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java index 1596cbea793..7ae2fc18dc7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java @@ -154,7 +154,7 @@ public class FightEffect extends DamageBaseEffect { if (!usedDamageMap) { preventMap.triggerPreventDamage(false); - damageMap.triggerDamageDoneOnce(false, sa); + damageMap.triggerDamageDoneOnce(false, fighterA.getGame(), sa); preventMap.clear(); damageMap.clear(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java index e7ed46e2b1e..54e32d8e6d6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java @@ -236,7 +236,7 @@ public class RepeatEachEffect extends SpellAbilityEffect { sa.getPreventMap().triggerPreventDamage(false); sa.setPreventMap(null); // non combat damage cause lifegain there - sa.getDamageMap().triggerDamageDoneOnce(false, sa); + sa.getDamageMap().triggerDamageDoneOnce(false, game, sa); sa.setDamageMap(null); } if (sa.hasParam("ChangeZoneTable")) { diff --git a/forge-game/src/main/java/forge/game/card/CardDamageMap.java b/forge-game/src/main/java/forge/game/card/CardDamageMap.java index 8a742dacf68..b5cf899f352 100644 --- a/forge-game/src/main/java/forge/game/card/CardDamageMap.java +++ b/forge-game/src/main/java/forge/game/card/CardDamageMap.java @@ -1,16 +1,20 @@ /** - * + * */ package forge.game.card; import java.util.Map; +import java.util.Set; import com.google.common.collect.ForwardingTable; import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.common.collect.Table; +import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameObjectPredicates; import forge.game.ability.AbilityKey; import forge.game.keyword.Keyword; import forge.game.spellability.SpellAbility; @@ -18,7 +22,7 @@ import forge.game.trigger.TriggerType; public class CardDamageMap extends ForwardingTable { private Table dataMap = HashBasedTable.create(); - + public CardDamageMap(Table damageMap) { this.putAll(damageMap); } @@ -38,13 +42,13 @@ public class CardDamageMap extends ForwardingTable { runParams.put(AbilityKey.DamageTarget, ge); runParams.put(AbilityKey.DamageAmount, sum); runParams.put(AbilityKey.IsCombatDamage, isCombat); - + ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamagePreventedOnce, runParams, false); } } } - public void triggerDamageDoneOnce(boolean isCombat, final SpellAbility sa) { + public void triggerDamageDoneOnce(boolean isCombat, final Game game, final SpellAbility sa) { // Source -> Targets for (Map.Entry> e : this.rowMap().entrySet()) { final Card sourceLKI = e.getKey(); @@ -58,9 +62,9 @@ public class CardDamageMap extends ForwardingTable { runParams.put(AbilityKey.DamageTargets, Sets.newHashSet(e.getValue().keySet())); runParams.put(AbilityKey.DamageAmount, sum); runParams.put(AbilityKey.IsCombatDamage, isCombat); - - sourceLKI.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false); - + + game.getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false); + if (sourceLKI.hasKeyword(Keyword.LIFELINK)) { sourceLKI.getController().gainLife(sum, sourceLKI, sa); } @@ -79,10 +83,15 @@ public class CardDamageMap extends ForwardingTable { runParams.put(AbilityKey.DamageSources, Sets.newHashSet(e.getValue().keySet())); runParams.put(AbilityKey.DamageAmount, sum); runParams.put(AbilityKey.IsCombatDamage, isCombat); - - ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDoneOnce, runParams, false); + + game.getTriggerHandler().runTrigger(TriggerType.DamageDoneOnce, runParams, false); } } + + final Map runParams = AbilityKey.newMap(); + runParams.put(AbilityKey.DamageMap, new CardDamageMap(this)); + runParams.put(AbilityKey.IsCombatDamage, isCombat); + game.getTriggerHandler().runTrigger(TriggerType.DamageAll, runParams, false); } /** * special put logic, sum the values @@ -98,4 +107,29 @@ public class CardDamageMap extends ForwardingTable { return dataMap; } + public int filteredAmount(String validSource, String validTarget, Card host, SpellAbility sa) { + int result = 0; + + Set filteredSource = null; + Set filteredTarget = null; + if (validSource != null) { + filteredSource = Sets.newHashSet(Iterables.filter(rowKeySet(), GameObjectPredicates.restriction(validSource.split(","), host.getController(), host, sa))); + } + if (validTarget != null) { + filteredTarget = Sets.newHashSet(Iterables.filter(columnKeySet(), GameObjectPredicates.restriction(validTarget.split(","), host.getController(), host, sa))); + } + + for (Table.Cell c : cellSet()) { + if (filteredSource != null && !filteredSource.contains(c.getRowKey())) { + continue; + } + if (filteredTarget != null && !filteredTarget.contains(c.getColumnKey())) { + continue; + } + result += c.getValue(); + } + + return result; + } + } diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java index 72099af6fb4..4fc83734d74 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -836,7 +836,7 @@ public class Combat { // Run the trigger to deal combat damage once // LifeLink for Combat Damage at this place - dealtDamageTo.triggerDamageDoneOnce(true, null); + dealtDamageTo.triggerDamageDoneOnce(true, game, null); dealtDamageTo.clear(); counterTable.triggerCountersPutAll(game); diff --git a/forge-game/src/main/java/forge/game/cost/CostDamage.java b/forge-game/src/main/java/forge/game/cost/CostDamage.java index cc33d7c6501..198ac700ea8 100644 --- a/forge-game/src/main/java/forge/game/cost/CostDamage.java +++ b/forge-game/src/main/java/forge/game/cost/CostDamage.java @@ -74,7 +74,7 @@ public class CostDamage extends CostPart { payer.addDamage(decision.c, source, damageMap, preventMap, table, sa); preventMap.triggerPreventDamage(false); - damageMap.triggerDamageDoneOnce(false, sa); + damageMap.triggerDamageDoneOnce(false, source.getGame(), sa); table.triggerCountersPutAll(payer.getGame()); preventMap.clear(); diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java index 34f48bcc28c..2ff6a07d9ec 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java @@ -164,9 +164,8 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { */ public final ReplacementEffect copy(final Card host, final boolean lki) { final ReplacementEffect res = (ReplacementEffect) clone(); - for (String key : getSVars()) { - res.setSVar(key, getSVar(key)); - } + + copyHelper(res, host); final SpellAbility sa = this.getOverridingAbility(); if (sa != null) { @@ -182,8 +181,6 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { res.setOtherChoices(null); } - res.setHostCard(host); - res.setActiveZone(validHostZones); res.setLayer(getLayer()); return res; diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index d467b6c0ffe..75479eaadfc 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -870,13 +870,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit clone.view = new SpellAbilityView(clone); // dont use setHostCard to not trigger the not copied parts yet - clone.hostCard = host; + + copyHelper(clone, host); if (!lki && host != null && host.getGame() != null) { host.getGame().addSpellAbility(clone); } - // need to clone the maps too so they can be changed - clone.originalMapParams = Maps.newHashMap(this.originalMapParams); - clone.mapParams = Maps.newHashMap(this.mapParams); clone.triggeringObjects = AbilityKey.newMap(this.triggeringObjects); @@ -902,7 +900,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit // clear maps for copy, the values will be added later clone.additionalAbilities = Maps.newHashMap(); clone.additionalAbilityLists = Maps.newHashMap(); - clone.sVars = Maps.newHashMap(); // run special copy Ability to make a deep copy CardFactory.copySpellAbility(this, clone, host, activ, lki); } catch (final CloneNotSupportedException e) { diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java index 3f3b33d26b9..f8442310ec0 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java @@ -819,13 +819,7 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone clone = (StaticAbility) clone(); clone.id = lki ? id : nextId(); - // dont use setHostCard to not trigger the not copied parts yet - clone.hostCard = host; - // need to clone the maps too so they can be changed - clone.originalMapParams = Maps.newHashMap(this.originalMapParams); - clone.mapParams = Maps.newHashMap(this.mapParams); - - clone.sVars = Maps.newHashMap(this.sVars); + copyHelper(clone, host); clone.layers = this.generateLayer(); diff --git a/forge-game/src/main/java/forge/game/trigger/Trigger.java b/forge-game/src/main/java/forge/game/trigger/Trigger.java index b9130a8cbd8..8388fa3ccdf 100644 --- a/forge-game/src/main/java/forge/game/trigger/Trigger.java +++ b/forge-game/src/main/java/forge/game/trigger/Trigger.java @@ -37,7 +37,6 @@ import forge.game.zone.ZoneType; import java.util.*; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.Sets; import forge.util.TextUtil; @@ -523,9 +522,7 @@ public abstract class Trigger extends TriggerReplacementBase { public final Trigger copy(Card newHost, boolean lki) { final Trigger copy = (Trigger) clone(); - copy.originalMapParams = Maps.newHashMap(originalMapParams); - copy.mapParams = Maps.newHashMap(originalMapParams); - copy.setHostCard(newHost); + copyHelper(copy, newHost); if (getOverridingAbility() != null) { copy.setOverridingAbility(getOverridingAbility().copy(newHost, lki)); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerDamageAll.java b/forge-game/src/main/java/forge/game/trigger/TriggerDamageAll.java new file mode 100644 index 00000000000..4df7cf871b8 --- /dev/null +++ b/forge-game/src/main/java/forge/game/trigger/TriggerDamageAll.java @@ -0,0 +1,52 @@ +package forge.game.trigger; + +import java.util.Map; + +import forge.game.ability.AbilityKey; +import forge.game.card.Card; +import forge.game.card.CardDamageMap; +import forge.game.spellability.SpellAbility; +import forge.util.Localizer; + +public class TriggerDamageAll extends Trigger { + + public TriggerDamageAll(Map params, Card host, boolean intrinsic) { + super(params, host, intrinsic); + } + + @Override + public boolean performTest(Map runParams) { + + if (hasParam("CombatDamage")) { + if (getParam("CombatDamage").equals("True")) { + if (!((Boolean) runParams.get(AbilityKey.IsCombatDamage))) { + return false; + } + } else if (getParam("CombatDamage").equals("False")) { + if (((Boolean) runParams.get(AbilityKey.IsCombatDamage))) { + return false; + } + } + } + final CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap); + return filterTable(table) > 0; + } + + @Override + public void setTriggeringObjects(SpellAbility sa, Map runParams) { + final CardDamageMap table = (CardDamageMap) runParams.get(AbilityKey.DamageMap); + + sa.setTriggeringObject(AbilityKey.DamageAmount, filterTable(table)); + } + + @Override + public String getImportantStackObjects(SpellAbility sa) { + StringBuilder sb = new StringBuilder(); + sb.append(Localizer.getInstance().getMessage("lblAmount")).append(": ").append(sa.getTriggeringObject(AbilityKey.DamageAmount)); + return sb.toString(); + } + + private int filterTable(CardDamageMap table) { + return table.filteredAmount(getParam("ValidSource"), getParam("ValidTarget"), getHostCard(), null); + } +} diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerType.java b/forge-game/src/main/java/forge/game/trigger/TriggerType.java index 952a21bb6f9..b0f82c1c153 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java @@ -43,6 +43,7 @@ public enum TriggerType { CounterRemovedOnce(TriggerCounterRemovedOnce.class), Crewed(TriggerCrewed.class), Cycled(TriggerCycled.class), + DamageAll(TriggerDamageAll.class), DamageDealtOnce(TriggerDamageDealtOnce.class), DamageDone(TriggerDamageDone.class), DamageDoneOnce(TriggerDamageDoneOnce.class), diff --git a/forge-gui/res/cardsfolder/m/mindblade_render.txt b/forge-gui/res/cardsfolder/m/mindblade_render.txt new file mode 100644 index 00000000000..0be61532af9 --- /dev/null +++ b/forge-gui/res/cardsfolder/m/mindblade_render.txt @@ -0,0 +1,8 @@ +Name:Mindblade Render +ManaCost:1 B +Types:Creature Azra Warrior +PT:1/3 +T:Mode$ DamageAll | ValidSource$ Creature.Warrior | ValidTarget$ Player.Opponent | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever your opponents are dealt combat damage, if any of that damage was dealt by a Warrior, you draw a card and you lose 1 life. +SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBLoseLife +SVar:DBLoseLife:DB$LoseLife | Defined$ You | LifeAmount$ 1 +Oracle: Whenever your opponents are dealt combat damage, if any of that damage was dealt by a Warrior, you draw a card and you lose 1 life. From 049ed5f9c443101863858a33599a1b23ae01d59d Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Thu, 13 Feb 2020 18:15:02 +0000 Subject: [PATCH 15/20] Secret Lair Drop Series and Promos --- .../res/editions/Secret Lair Drop Series.txt | 46 +++++++++++++++++++ forge-gui/res/editions/Secret Lair Promos.txt | 25 ++++++++++ 2 files changed, 71 insertions(+) create mode 100644 forge-gui/res/editions/Secret Lair Drop Series.txt create mode 100644 forge-gui/res/editions/Secret Lair Promos.txt diff --git a/forge-gui/res/editions/Secret Lair Drop Series.txt b/forge-gui/res/editions/Secret Lair Drop Series.txt new file mode 100644 index 00000000000..6f6fa86f3e4 --- /dev/null +++ b/forge-gui/res/editions/Secret Lair Drop Series.txt @@ -0,0 +1,46 @@ +[metadata] +Code=SLD +Date=2019-12-02 +Name=Secret Lair Drop Series +MciCode=sld +Type=Other + +[cards] +1 R Snow-Covered Plains +2 R Snow-Covered Island +3 R Snow-Covered Swamp +4 R Snow-Covered Mountain +5 R Snow-Covered Forest +6 R Bloodghast +7 R Golgari Thug +8 R Life from the Loam +9 M Reaper King +10 M Sliver Overlord +11 M The Ur-Dragon +12 M Bitterblossom +17 R Goblin Bushwhacker +18 R Goblin Sharpshooter +19 R Goblin King +20 R Goblin Lackey +21 R Goblin Piledriver +22 R Leonin Warleader +23 R Regal Caracal +24 R Qasali Slingers +25 M Arahbo, Roar of the World +26 M Mirri, Weatherlight Duelist +29 R Serum Visions +30 R Serum Visions +31 R Serum Visions +32 R Serum Visions +33 R Ink-Eyes, Servant of Oni +34 R Marrow-Gnawer +35 R Pack Rat +36 R Rat Colony + +[tokens] +b_1_1_faerie_rogue_flying +b_1_1_faerie_rogue_flying +b_1_1_faerie_rogue_flying +b_1_1_faerie_rogue_flying +w_1_1_cat_lifelink +w_1_1_cat_lifelink \ No newline at end of file diff --git a/forge-gui/res/editions/Secret Lair Promos.txt b/forge-gui/res/editions/Secret Lair Promos.txt new file mode 100644 index 00000000000..cd9f2fea783 --- /dev/null +++ b/forge-gui/res/editions/Secret Lair Promos.txt @@ -0,0 +1,25 @@ +[metadata] +Code=PSLD +Date=2019-12-02 +Name=Secret Lair Promos +MciCode=sld +Type=Other + +[cards] +503 M Gideon Blackblade +504 U Teyo, the Shieldmage +505 U The Wanderer +506 R Jace, Wielder of Mysteries +514 R Sarkhan the Masterless +516 U Arlinn, Voice of the Pack +520 R Ajani, the Greathearted +521 R Domri, Anarch of Bolas +522 M Nicol Bolas, Dragon-God +523 R Ral, Storm Conduit +524 R Sorin, Vengeful Bloodlord +525 R Tamiyo, Collector of Tales +526 R Teferi, Time Raveler +527 U Angrath, Captain of Chaos +528 U Ashiok, Dream Render +530 U Huatli, the Sun's Heart +533 U Nahiri, Storm of Stone From ec79434759747348375389794cffdcd58801e40e Mon Sep 17 00:00:00 2001 From: friarsol Date: Thu, 13 Feb 2020 23:17:26 -0500 Subject: [PATCH 16/20] Improve Android zone restoration --- .../forge/screens/match/MatchController.java | 10 +++++++--- .../screens/match/views/VPlayerPanel.java | 20 ++++++++++++++++--- .../java/forge/player/PlayerZoneUpdate.java | 12 +++++++---- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/forge-gui-mobile/src/forge/screens/match/MatchController.java b/forge-gui-mobile/src/forge/screens/match/MatchController.java index 4dd665a72a3..8cc153d9ac4 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchController.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchController.java @@ -322,8 +322,9 @@ public class MatchController extends AbstractGuiGame { final VPlayerPanel playerPanel = view.getPlayerPanel(player); playersWithTargetables.put(player, playerPanel.getSelectedTab()); //backup selected tab before changing it final InfoTab zoneTab = playerPanel.getZoneTab(zoneType); + ZoneType previousZone = playerPanel.getZoneByInfoTab(playerPanel.getSelectedTab()); + updates.add(new PlayerZoneUpdate(player, previousZone)); if (zoneTab != null) { - updates.add(new PlayerZoneUpdate(player, zoneType)); playerPanel.setSelectedTab(zoneTab); } } @@ -343,9 +344,12 @@ public class MatchController extends AbstractGuiGame { break; } - if (zone == null) { return; } - final VPlayerPanel playerPanel = view.getPlayerPanel(player); + if (zone == null) { + playerPanel.hideSelectedTab(); + continue; + } + final InfoTab zoneTab = playerPanel.getZoneTab(zone); playerPanel.setSelectedTab(zoneTab); } diff --git a/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java b/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java index ee7e4c40967..3d24cc4d8e8 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java @@ -102,18 +102,32 @@ public class VPlayerPanel extends FContainer { return zoneTabs.get(zoneType); } + public ZoneType getZoneByInfoTab(InfoTab tab) { + for(ZoneType zone : zoneTabs.keySet()) { + if (zoneTabs.get(zone).equals(tab)) { + return zone; + } + } + + return null; + } + public void setSelectedZone(ZoneType zoneType) { setSelectedTab(zoneTabs.get(zoneType)); } + public void hideSelectedTab() { + if (selectedTab != null) { + selectedTab.displayArea.setVisible(false); + } + } + public void setSelectedTab(InfoTab selectedTab0) { if (selectedTab == selectedTab0) { return; } - if (selectedTab != null) { - selectedTab.displayArea.setVisible(false); - } + hideSelectedTab(); selectedTab = selectedTab0; diff --git a/forge-gui/src/main/java/forge/player/PlayerZoneUpdate.java b/forge-gui/src/main/java/forge/player/PlayerZoneUpdate.java index 5408941ffe5..7ed01f9f469 100644 --- a/forge-gui/src/main/java/forge/player/PlayerZoneUpdate.java +++ b/forge-gui/src/main/java/forge/player/PlayerZoneUpdate.java @@ -14,11 +14,15 @@ public class PlayerZoneUpdate implements Serializable { private final Set zones; public PlayerZoneUpdate(final PlayerView player, final ZoneType zone) { - if (player == null || zone == null) { + if (player == null ) { throw new NullPointerException(); } this.player = player; - this.zones = EnumSet.of(zone); + if (zone != null) { + this.zones = EnumSet.of(zone); + } else { + this.zones = EnumSet.noneOf(ZoneType.class); + } } public PlayerView getPlayer() { @@ -30,13 +34,13 @@ public class PlayerZoneUpdate implements Serializable { void addZone(final ZoneType zone) { if (zone == null) { - throw new NullPointerException(); + return; } zones.add(zone); } void add(final PlayerZoneUpdate other) { if (other == null) { - throw new NullPointerException(); + return; } zones.addAll(other.getZones()); } From 93ae66ad39e6cb00b5c84506d4f1dbd022f176ca Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Fri, 14 Feb 2020 14:01:16 +0000 Subject: [PATCH 17/20] HumanPlaySpellAbility: fix CastSA and CastFrom reset when ability is canceld --- .../src/main/java/forge/player/HumanPlaySpellAbility.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index e3663f41ad3..a337db37099 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -133,8 +133,8 @@ public class HumanPlaySpellAbility { } if (ability.isAbility() && ability instanceof AbilityActivated) { - final Map params = Maps.newHashMap(); - params.put("ManaColorConversion", "Additive"); + final Map params = Maps.newHashMap(); + params.put("ManaColorConversion", "Additive"); for (KeywordInterface inst : c.getKeywords()) { String keyword = inst.getOriginal(); @@ -249,6 +249,8 @@ public class HumanPlaySpellAbility { final Game game = ability.getActivatingPlayer().getGame(); if (fromZone != null) { // and not a copy + ability.getHostCard().setCastSA(null); + ability.getHostCard().setCastFrom(null); // add back to where it came from game.getAction().moveTo(fromZone, ability.getHostCard(), zonePosition >= 0 ? Integer.valueOf(zonePosition) : null, null); } From 6a98c8f371d19287d4589a9b8d7df97a07800837 Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Sat, 15 Feb 2020 03:50:35 +0000 Subject: [PATCH 18/20] SLD - Theros Stargazing --- .../res/editions/Secret Lair Drop Series.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/forge-gui/res/editions/Secret Lair Drop Series.txt b/forge-gui/res/editions/Secret Lair Drop Series.txt index 6f6fa86f3e4..a2eba070cd7 100644 --- a/forge-gui/res/editions/Secret Lair Drop Series.txt +++ b/forge-gui/res/editions/Secret Lair Drop Series.txt @@ -36,6 +36,21 @@ Type=Other 34 R Marrow-Gnawer 35 R Pack Rat 36 R Rat Colony +68 M Heliod, God of the Sun +69 M Karametra, God of Harvests +70 M Iroas, God of Victory +71 M Thassa, God of the Sea +72 M Ephara, God of the Polis +73 M Kruphix, God of Horizons +74 M Erebos, God of the Dead +75 M Phenax, God of Deception +76 M Athreos, God of Passage +77 M Purphoros, God of the Forge +78 M Mogis, God of Slaughter +79 M Keranos, God of Storms +80 M Nylea, God of the Hunt +81 M Xenagos, God of Revels +82 M Pharika, God of Affliction [tokens] b_1_1_faerie_rogue_flying From 452dee7e11d1eb4b9da03c402e5bc261306e07d6 Mon Sep 17 00:00:00 2001 From: Tim Mocny Date: Sat, 15 Feb 2020 03:51:04 +0000 Subject: [PATCH 19/20] Quest Shop Precons - Commander 2018 --- .../quest/precons/Adaptive Enchantment.dck | 92 +++++++++++++++++ .../precons/Ashiok, Sculptor of Fears.dck | 2 +- .../quest/precons/Elspeth, Undaunted Hero.dck | 2 +- .../res/quest/precons/Exquisite Invention.dck | 87 ++++++++++++++++ .../res/quest/precons/Nature's Vengeance.dck | 97 ++++++++++++++++++ .../res/quest/precons/Subjective Reality.dck | 99 +++++++++++++++++++ 6 files changed, 377 insertions(+), 2 deletions(-) create mode 100644 forge-gui/res/quest/precons/Adaptive Enchantment.dck create mode 100644 forge-gui/res/quest/precons/Exquisite Invention.dck create mode 100644 forge-gui/res/quest/precons/Nature's Vengeance.dck create mode 100644 forge-gui/res/quest/precons/Subjective Reality.dck diff --git a/forge-gui/res/quest/precons/Adaptive Enchantment.dck b/forge-gui/res/quest/precons/Adaptive Enchantment.dck new file mode 100644 index 00000000000..ec7c27669fc --- /dev/null +++ b/forge-gui/res/quest/precons/Adaptive Enchantment.dck @@ -0,0 +1,92 @@ +[metadata] +Name=Adaptive Enchantment +[shop] +WinsToUnlock=0 +Credits=3600 +MinDifficulty=0 +MaxDifficulty=5 +[metadata] +Description=Estrid wears many faces, only occasionally wearing her own. Her arsenal of enchanted masks lets her shift seamlessly between the powers and fighting styles of countless creatures to adapt to enemy tactics. +Set=C18 +Image=adaptive_enchantment.jpg +[Main] +1 Ajani's Chosen|C18 +1 Archetype of Imagination|C18 +1 Arixmethes, Slumbering Isle|C18 +1 Aura Gnarlid|C18 +1 Azorius Chancery|C18 +1 Bant Charm|C18 +1 Bear Umbra|C18 +1 Blossoming Sands|C18 +1 Boon Satyr|C18 +1 Bruna, Light of Alabaster|C18 +1 Celestial Archon|C18 +1 Cold-Eyed Selkie|C18 +1 Command Tower|C18 +1 Creeping Renaissance|C18 +1 Dawn's Reflection|C18 +1 Daxos of Meletis|C18 +1 Dictate of Kruphix|C18 +1 Dismantling Blow|C18 +1 Eel Umbra|C18 +1 Eidolon of Blossoms|C18 +1 Elderwood Scion|C18 +1 Empyrial Storm|C18 +1 Enchantress's Presence|C18 +1 Epic Proportions|C18 +1 Estrid's Invocation|C18 +1 Estrid, the Masked+|C18 +1 Ever-Watching Threshold|C18 +1 Evolving Wilds|C18 +1 Fertile Ground|C18 +1 Finest Hour|C18 +8 Forest|C18 +1 Forge of Heroes|C18 +1 Genesis Storm|C18 +1 Ground Seal|C18 +1 Heavenly Blademaster|C18 +1 Herald of the Pantheon|C18 +1 Hydra Omnivore|C18 +6 Island|C18 +1 Kestia, the Cultivator|C18 +1 Krosan Verge|C18 +1 Kruphix's Insight|C18 +1 Loyal Drake|C18 +1 Loyal Guardian|C18 +1 Loyal Unicorn|C18 +1 Martial Coup|C18 +1 Meandering River|C18 +1 Mosswort Bridge|C18 +1 Myth Unbound|C18 +1 Nylea's Colossus|C18 +1 Octopus Umbra|C18 +1 Overgrowth|C18 +1 Phyrexian Rebirth|C18 +9 Plains|C18 +1 Ravenous Slime|C18 +1 Reclamation Sage|C18 +1 Righteous Authority|C18 +1 Sage's Reverie|C18 +1 Seaside Citadel|C18 +1 Selesnya Sanctuary|C18 +1 Sigil of the Empty Throne|C18 +1 Silent Sentinel|C18 +1 Simic Growth Chamber|C18 +1 Snake Umbra|C18 +1 Sol Ring|C18 +1 Soul Snare|C18 +1 Spawning Grounds|C18 +1 Terramorphic Expanse|C18 +1 Thornwood Falls|C18 +1 Tranquil Cove|C18 +1 Tranquil Expanse|C18 +1 Tuvasa the Sunlit|C18 +1 Unflinching Courage|C18 +1 Unquestioned Authority|C18 +1 Vow of Flight|C18 +1 Vow of Wildness|C18 +1 Whitewater Naiads|C18 +1 Wild Growth|C18 +1 Winds of Rath|C18 +1 Woodland Stream|C18 +1 Yavimaya Enchantress|C18 diff --git a/forge-gui/res/quest/precons/Ashiok, Sculptor of Fears.dck b/forge-gui/res/quest/precons/Ashiok, Sculptor of Fears.dck index d0263cad431..d1c983bf8d4 100644 --- a/forge-gui/res/quest/precons/Ashiok, Sculptor of Fears.dck +++ b/forge-gui/res/quest/precons/Ashiok, Sculptor of Fears.dck @@ -6,7 +6,7 @@ Credits=1200 MinDifficulty=0 MaxDifficulty=5 [metadata] -Description=Theros Beyond Death U/B Planeswalker Deck +Description=Ashiok has power to torment foes by conjuring their darkest memories, fears, and regrets. With Ashiok’s deck, you’ll fill your graveyard along with your opponent’s for massive value as you slowly drive them insane. Set=THB Image=ashiok_sculptor_of_fears.jpg [Main] diff --git a/forge-gui/res/quest/precons/Elspeth, Undaunted Hero.dck b/forge-gui/res/quest/precons/Elspeth, Undaunted Hero.dck index 0d3df54935e..68ee479c7cf 100644 --- a/forge-gui/res/quest/precons/Elspeth, Undaunted Hero.dck +++ b/forge-gui/res/quest/precons/Elspeth, Undaunted Hero.dck @@ -6,7 +6,7 @@ Credits=1200 MinDifficulty=0 MaxDifficulty=5 [metadata] -Description=Theros Beyond Death W Planeswalker Deck +Description=Elspeth is a renowned warrior, who has defeated everything from gods to death itself. With Elspeth’s deck, you’ll build up a battalion of devoted soldiers and lead them fearlessly to a glorious victory. Set=THB Image=elspeth_undaunted_hero.jpg [Main] diff --git a/forge-gui/res/quest/precons/Exquisite Invention.dck b/forge-gui/res/quest/precons/Exquisite Invention.dck new file mode 100644 index 00000000000..51b9f2eb393 --- /dev/null +++ b/forge-gui/res/quest/precons/Exquisite Invention.dck @@ -0,0 +1,87 @@ +[metadata] +Name=Exquisite Invention +[shop] +WinsToUnlock=0 +Credits=3600 +MinDifficulty=0 +MaxDifficulty=5 +[metadata] +Description=Saheeli Rai is a brilliant inventor who specializes in creating lifelike animated constructs as beautiful as they are deadly. Her dazzling intellect and mastery over metal make her a formidable adversary. +Set=C18 +Image=exquisite_invention.jpg +[Main] +1 Aether Gale|C18 +1 Ancient Stone Idol|C18 +1 Blasphemous Act|C18 +1 Blinkmoth Urn|C18 +1 Bosh, Iron Golem|C18 +1 Brudiclad, Telchor Engineer|C18 +1 Buried Ruin|C18 +1 Chaos Warp|C18 +1 Chief of the Foundry|C18 +1 Command Tower|C18 +1 Commander's Sphere|C18 +1 Coveted Jewel|C18 +1 Darksteel Citadel|C18 +1 Darksteel Juggernaut|C18 +1 Dreamstone Hedron|C18 +1 Duplicant|C18 +1 Echo Storm|C18 +1 Enchanter's Bane|C18 +1 Endless Atlas|C18 +1 Etherium Sculptor|C18 +1 Forge of Heroes|C18 +1 Foundry of the Consuls|C18 +1 Geode Golem|C18 +1 Great Furnace|C18 +1 Hedron Archive|C18 +1 Hellkite Igniter|C18 +1 Highland Lake|C18 +1 Inkwell Leviathan|C18 +1 Into the Roil|C18 +15 Island|C18 +1 Izzet Boilerworks|C18 +1 Izzet Guildgate|C18 +1 Izzet Signet|C18 +1 Loyal Apprentice|C18 +1 Loyal Drake|C18 +1 Magmaquake|C18 +1 Magnifying Glass|C18 +1 Maverick Thopterist|C18 +1 Mimic Vat|C18 +1 Mind Stone|C18 +1 Mirrorworks|C18 +12 Mountain|C18 +1 Myr Battlesphere|C18 +1 Pilgrim's Eye|C18 +1 Prismatic Lens|C18 +1 Prototype Portal|C18 +1 Psychosis Crawler|C18 +1 Retrofitter Foundry|C18 +1 Reverse Engineer|C18 +1 Saheeli's Artistry|C18 +1 Saheeli's Directive|C18 +1 Saheeli, the Gifted+|C18 +1 Scrabbling Claws|C18 +1 Scuttling Doom Engine|C18 +1 Seat of the Synod|C18 +1 Sharding Sphinx|C18 +1 Sol Ring|C18 +1 Soul of New Phyrexia|C18 +1 Steel Hellkite|C18 +1 Swiftfoot Boots|C18 +1 Swiftwater Cliffs|C18 +1 Tawnos, Urza's Apprentice|C18 +1 Thirst for Knowledge|C18 +1 Thopter Assembly|C18 +1 Thopter Engineer|C18 +1 Thopter Spy Network|C18 +1 Tidings|C18 +1 Treasure Nabber|C18 +1 Unstable Obelisk|C18 +1 Unwinding Clock|C18 +1 Varchild, Betrayer of Kjeldor|C18 +1 Vedalken Humiliator|C18 +1 Vessel of Endless Rest|C18 +1 Whirler Rogue|C18 +1 Worn Powerstone|C18 diff --git a/forge-gui/res/quest/precons/Nature's Vengeance.dck b/forge-gui/res/quest/precons/Nature's Vengeance.dck new file mode 100644 index 00000000000..670b63c11a2 --- /dev/null +++ b/forge-gui/res/quest/precons/Nature's Vengeance.dck @@ -0,0 +1,97 @@ +[metadata] +Name=Nature's Vengeance +[shop] +WinsToUnlock=0 +Credits=3600 +MinDifficulty=0 +MaxDifficulty=5 +[metadata] +Description=Lord Windgrace saw his entire world devastated by the detonation of a magical device, instilling in him a hatred of artifacts. Now he commands nature to rise and join his furious war against synthetic abominations. +Set=C18 +Image=natures_vengeance.jpg +[Main] +1 Acidic Slime|C18 +1 Akoum Refuge|C18 +1 Avenger of Zendikar|C18 +1 Baloth Woodcrasher|C18 +1 Barren Moor|C18 +1 Blighted Woodland|C18 +1 Bloodtracker|C18 +1 Bojuka Bog|C18 +1 Borderland Explorer|C18 +1 Budoka Gardener|C18 +1 Centaur Vinecrasher|C18 +1 Chain Reaction|C18 +1 Charnelhoard Wurm|C18 +1 Command Tower|C18 +1 Consign to Dust|C18 +1 Crash of Rhino Beetles|C18 +1 Cultivate|C18 +1 Deathreap Ritual|C18 +1 Decimate|C18 +1 Emissary of Grudges|C18 +1 Evolving Wilds|C18 +1 Explore|C18 +1 Explosive Vegetation|C18 +1 Far Wanderings|C18 +1 Farhaven Elf|C18 +1 Flameblast Dragon|C18 +7 Forest|C18 +1 Forge of Heroes|C18 +1 Forgotten Cave|C18 +1 Fury Storm|C18 +1 Gaze of Granite|C18 +1 Golgari Rot Farm|C18 +1 Grapple with the Past|C18 +1 Grim Backwoods|C18 +1 Grisly Salvage|C18 +1 Gruul Turf|C18 +1 Gyrus, Waker of Corpses|C18 +1 Harrow|C18 +1 Haunted Fengraf|C18 +1 Hunting Wilds|C18 +1 Jund Panorama|C18 +1 Jungle Hollow|C18 +1 Kazandu Refuge|C18 +1 Khalni Garden|C18 +1 Khalni Heart Expedition|C18 +1 Lavalanche|C18 +1 Lord Windgrace+|C18 +1 Loyal Apprentice|C18 +1 Loyal Guardian|C18 +1 Loyal Subordinate|C18 +1 Moldgraf Monstrosity|C18 +1 Moonlight Bargain|C18 +5 Mountain|C18 +1 Mountain Valley|C18 +1 Myriad Landscape|C18 +1 Nesting Dragon|C18 +1 Putrefy|C18 +1 Rakdos Carnarium|C18 +1 Rampaging Baloths|C18 +1 Reality Scramble|C18 +1 Retreat to Hagra|C18 +1 Rocky Tar Pit|C18 +1 Rubblehulk|C18 +1 Ruinous Path|C18 +1 Sakura-Tribe Elder|C18 +1 Savage Lands|C18 +1 Savage Twister|C18 +1 Scute Mob|C18 +1 Seer's Sundial|C18 +1 Sol Ring|C18 +1 Soul of Innistrad|C18 +1 Stitch Together|C18 +6 Swamp|C18 +1 Temple of the False God|C18 +1 Terramorphic Expanse|C18 +1 Thantis, the Warweaver|C18 +1 Tranquil Thicket|C18 +1 Turntimber Sower|C18 +1 Warped Landscape|C18 +1 Whiptongue Hydra|C18 +1 Windgrace's Judgment|C18 +1 Worm Harvest|C18 +1 Xantcha, Sleeper Agent|C18 +1 Yavimaya Elder|C18 +1 Zendikar Incarnate|C18 diff --git a/forge-gui/res/quest/precons/Subjective Reality.dck b/forge-gui/res/quest/precons/Subjective Reality.dck new file mode 100644 index 00000000000..66f939efa23 --- /dev/null +++ b/forge-gui/res/quest/precons/Subjective Reality.dck @@ -0,0 +1,99 @@ +[metadata] +Name=Subjective Reality +[shop] +WinsToUnlock=0 +Credits=3600 +MinDifficulty=0 +MaxDifficulty=5 +[metadata] +Description=Aminatou may be a child, but she possesses the wisdom of many lifetimes and the power to manipulate reality itself. She can effortlessly alter a person's entire destiny – for better or for worse – and leave no trace of her interference. +Set=C18 +Image=subjective_reality.jpg +[Main] +1 Adarkar Valkyrie|C18 +1 Aethermage's Touch|C18 +1 Akroma's Vengeance|C18 +1 Aminatou's Augury|C18 +1 Aminatou, the Fateshifter+|C18 +1 Arcane Sanctum|C18 +1 Army of the Damned|C18 +1 Azorius Chancery|C18 +1 Azorius Guildgate|C18 +1 Azorius Signet|C18 +1 Banishing Stroke|C18 +1 Barren Moor|C18 +1 Boreas Charger|C18 +1 Brainstorm|C18 +1 Cloudform|C18 +1 Command Tower|C18 +1 Commander's Sphere|C18 +1 Conundrum Sphinx|C18 +1 Crib Swap|C18 +1 Crystal Ball|C18 +1 Devastation Tide|C18 +1 Dimir Aqueduct|C18 +1 Dimir Guildgate|C18 +1 Dimir Signet|C18 +1 Dismal Backwater|C18 +1 Djinn of Wishes|C18 +1 Dream Cache|C18 +1 Duskmantle Seer|C18 +1 Enigma Sphinx|C18 +1 Entreat the Angels|C18 +1 Entreat the Dead|C18 +1 Esper Charm|C18 +1 Forge of Heroes|C18 +1 Forsaken Sanctuary|C18 +1 Geode Golem|C18 +1 Halimar Depths|C18 +1 High Priest of Penance|C18 +5 Island|C18 +1 Isolated Watchtower|C18 +1 Jeskai Infiltrator|C18 +1 Jwar Isle Refuge|C18 +1 Lightform|C18 +1 Lonely Sandbar|C18 +1 Loyal Subordinate|C18 +1 Loyal Unicorn|C18 +1 Magus of the Balance|C18 +1 Meandering River|C18 +1 Mind Stone|C18 +1 Mortify|C18 +1 Mortuary Mire|C18 +1 Mulldrifter|C18 +1 New Benalia|C18 +1 Night Incarnate|C18 +1 Ninja of the Deep Hours|C18 +1 Orzhov Basilica|C18 +1 Orzhov Guildgate|C18 +1 Orzhov Signet|C18 +1 Phyrexian Delver|C18 +1 Pilgrim's Eye|C18 +8 Plains|C18 +1 Ponder|C18 +1 Portent|C18 +1 Predict|C18 +1 Primordial Mist|C18 +1 Return to Dust|C18 +1 Scoured Barrens|C18 +1 Secluded Steppe|C18 +1 Seer's Lantern|C18 +1 Sejiri Refuge|C18 +1 Serra Avatar|C18 +1 Sigiled Starfish|C18 +1 Silent-Blade Oni|C18 +1 Skull Storm|C18 +1 Sol Ring|C18 +1 Sower of Discord|C18 +1 Sphinx of Jwar Isle|C18 +1 Sphinx of Uthuun|C18 +1 Submerged Boneyard|C18 +3 Swamp|C18 +1 Telling Time|C18 +1 Terminus|C18 +1 Tranquil Cove|C18 +1 Treasure Hunt|C18 +1 Utter End|C18 +1 Varina, Lich Queen|C18 +1 Yennett, Cryptic Sovereign|C18 +1 Yuriko, the Tiger's Shadow|C18 From a74f1a14275eae6075e459fb2e5f111bbdadc433 Mon Sep 17 00:00:00 2001 From: Agetian Date: Sat, 15 Feb 2020 16:28:36 +0300 Subject: [PATCH 20/20] - Added puzzle PS_THB3. --- forge-gui/res/puzzle/PS_THB3.pzl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 forge-gui/res/puzzle/PS_THB3.pzl diff --git a/forge-gui/res/puzzle/PS_THB3.pzl b/forge-gui/res/puzzle/PS_THB3.pzl new file mode 100644 index 00000000000..4cf23f2fd02 --- /dev/null +++ b/forge-gui/res/puzzle/PS_THB3.pzl @@ -0,0 +1,20 @@ +[metadata] +Name:Possibility Storm - Theros Beyond Death #03 +URL:https://i0.wp.com/www.possibilitystorm.com/wp-content/uploads/2020/02/146.-THB3-1-scaled.jpg +Goal:Win +Turns:1 +Difficulty:Mythic +Description:Win this turn. Assume any cards drawn are not relevant to solving the puzzle. Assume your opponent has no cards in hand. +[state] +humanlife=20 +ailife=17 +turn=1 +activeplayer=human +activephase=MAIN1 +humanhand=Gingerbrute;Purphoros, Bronze-Blooded;Nyxborn Brute;Mire's Grasp;Omen of the Dead +humanlibrary=Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt;Opt +humangraveyard=Rotting Regisaur;Erebos, Bleak-Hearted;Kroxa, Titan of Death's Hunger +humanbattlefield=Lazav, the Multifarious;Protean Thaumaturge;Bloodmist Infiltrator;The Royal Scions|Counters:LOYALTY=3;Blood Crypt|NoETBTrigs;Blood Crypt|NoETBTrigs;Blood Crypt|NoETBTrigs;Blood Crypt|NoETBTrigs;Watery Grave|NoETBTrigs;Watery Grave|NoETBTrigs;Watery Grave|NoETBTrigs;Watery Grave|NoETBTrigs +aibattlefield=Cerulean Drake;Cerulean Drake;Archon of Sun's Grace +aiprecast=Archon of Sun's Grace:TrigToken;Archon of Sun's Grace:TrigToken +removesummoningsickness=true