From eaab0bdd7d6897559d54cc7c0d9795e87e852263 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Tue, 12 Nov 2024 21:21:49 +0800 Subject: [PATCH 001/152] fix Cryptic Spire --- .../src/main/java/forge/game/GameAction.java | 8 ++++++-- .../src/main/java/forge/game/card/Card.java | 15 ++++++++++++++- .../main/java/forge/game/card/CardView.java | 7 ++++++- .../game/spellability/AbilityManaPart.java | 19 +++++++++++++++++++ .../forge/trackable/TrackableProperty.java | 1 + .../res/cardsfolder/c/cryptic_spires.txt | 2 +- .../java/forge/gui/card/CardDetailUtil.java | 10 ++++++++++ 7 files changed, 57 insertions(+), 5 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index e1ccedf9df1..d583954a67b 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -572,6 +572,10 @@ public class GameAction { game.getTriggerHandler().registerActiveTrigger(copied, false); } + if (c.hasChosenColorSpire()) { + copied.setChosenColorSpire(ImmutableList.copyOf(c.getChosenColorSpire())); + } + // update state for view copied.updateStateForView(); @@ -2223,14 +2227,14 @@ public class GameAction { c.setChosenNumber(chosen); } for (Card c : spires) { - if (!c.hasChosenColor()) { + if (!c.hasChosenColorSpire()) { List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); String prompt = CardTranslation.getTranslatedName(c.getName()) + ": " + Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(2)); SpellAbility sa = new SpellAbility.EmptySa(ApiType.ChooseColor, c, takesAction); sa.putParam("AILogic", "MostProminentInComputerDeck"); List chosenColors = takesAction.getController().chooseColors(prompt, sa, 2, 2, colorChoices); - c.setChosenColors(chosenColors); + c.setChosenColorSpire(chosenColors); } } takesAction = game.getNextPlayerAfter(takesAction); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index b8d7e07a2d7..7350fe5adcd 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -293,6 +293,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr private String chosenType2 = ""; private List notedTypes = new ArrayList<>(); private List chosenColors; + private List chosenColorSpire; private List chosenName = new ArrayList<>(); private Integer chosenNumber; private Player chosenPlayer; @@ -2118,7 +2119,19 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr public boolean hasChosenColor(String s) { return chosenColors != null && chosenColors.contains(s); } - + public final Iterable getChosenColorSpire() { + if (chosenColorSpire == null) { + return Lists.newArrayList(); + } + return chosenColorSpire; + } + public final void setChosenColorSpire(final List s) { + chosenColorSpire = s; + view.updateChosenColorSpire(this); + } + public boolean hasChosenColorSpire() { + return chosenColorSpire != null && !chosenColorSpire.isEmpty(); + } public final Card getChosenCard() { return getChosenCards().getFirst(); } diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 1f25f0380d2..9325bbd2896 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -433,7 +433,12 @@ public class CardView extends GameEntityView { void updateChosenColors(Card c) { set(TrackableProperty.ChosenColors, c.getChosenColors()); } - + public List getChosenColorSpire() { + return get(TrackableProperty.ChosenColorSpire); + } + void updateChosenColorSpire(Card c) { + set(TrackableProperty.ChosenColorSpire, c.getChosenColorSpire()); + } public FCollectionView getMergedCardsCollection() { return get(TrackableProperty.MergedCardsCollection); } diff --git a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java index 8fd49d86057..5c357ad9721 100644 --- a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java +++ b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java @@ -654,6 +654,10 @@ public class AbilityManaPart implements java.io.Serializable { if (origProduced.contains("Chosen")) { origProduced = origProduced.replace("Chosen", getChosenColor(sa)); } + // replace Chosen for Spire colors + if (origProduced.contains("Spire")) { + origProduced = origProduced.replace("Spire", getChosenSpireColor(sa)); + } if (origProduced.contains("NotedColors")) { // Should only be used for Paliano, the High City if (sa.getActivatingPlayer() == null) { @@ -697,6 +701,21 @@ public class AbilityManaPart implements java.io.Serializable { return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1); } + public String getChosenSpireColor(SpellAbility sa) { + if (sa == null) { + return ""; + } + Card card = sa.getHostCard(); + if (card != null && card.hasChosenColorSpire()) { + StringBuilder values = new StringBuilder(); + for (String s : card.getChosenColorSpire()) { + values.append(MagicColor.toShortString(MagicColor.fromName(s))).append(" "); + } + return values.toString(); + } + return ""; + } + public String getChosenColor(SpellAbility sa) { if (sa == null) { return ""; diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 58481162c46..638c8694c94 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -67,6 +67,7 @@ public enum TrackableProperty { ChosenType2(TrackableTypes.StringType), NotedTypes(TrackableTypes.StringListType), ChosenColors(TrackableTypes.StringListType), + ChosenColorSpire(TrackableTypes.StringListType), ChosenCards(TrackableTypes.CardViewCollectionType), ChosenNumber(TrackableTypes.StringType), StoredRolls(TrackableTypes.StringListType), diff --git a/forge-gui/res/cardsfolder/c/cryptic_spires.txt b/forge-gui/res/cardsfolder/c/cryptic_spires.txt index 9bcbec5ba29..2f7aafde128 100644 --- a/forge-gui/res/cardsfolder/c/cryptic_spires.txt +++ b/forge-gui/res/cardsfolder/c/cryptic_spires.txt @@ -4,5 +4,5 @@ Types:Land Text:As you create your deck, circle two of the colors below. R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | ReplacementResult$ Updated | ReplaceWith$ ETBTapped | Description$ CARDNAME enters tapped. SVar:ETBTapped:DB$ Tap | Defined$ Self | ETB$ True -A:AB$ Mana | Cost$ T | Produced$ Combo Chosen | SpellDescription$ Add one mana of either of the circled colors. +A:AB$ Mana | Cost$ T | Produced$ Combo Spire | SpellDescription$ Add one mana of either of the circled colors. Oracle:As you create your deck, circle two of the colors below.\nCryptic Spires enters tapped.\n{T}: Add one mana of either of the circled colors. diff --git a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java index 59ebe64a76c..87ec9d6fb70 100644 --- a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java +++ b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java @@ -487,6 +487,16 @@ public class CardDetailUtil { area.append(")"); } + // chosen spire + if (card.getChosenColorSpire() != null && !card.getChosenColorSpire().isEmpty()) { + if (area.length() != 0) { + area.append("\n"); + } + area.append("(").append(Localizer.getInstance().getMessage("lblSelected")).append(": "); + area.append(Lang.joinHomogenous(card.getChosenColorSpire().stream().map(DeckRecognizer::getLocalisedMagicColorName).collect(Collectors.toList()))); + area.append(")"); + } + // chosen color if (card.getChosenColors() != null && !card.getChosenColors().isEmpty()) { if (area.length() != 0) { From 65c5985281656440290f34cc9f124c0e991317d9 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Tue, 12 Nov 2024 22:14:11 +0800 Subject: [PATCH 002/152] add TODO --- forge-game/src/main/java/forge/game/GameAction.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index d583954a67b..974f28f63c9 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -2227,6 +2227,8 @@ public class GameAction { c.setChosenNumber(chosen); } for (Card c : spires) { + // TODO: only do this for the AI, for the player part, get the encoded color from the deck file and pass + // it to either player or the papercard object so it feels like rule based for the player side.. if (!c.hasChosenColorSpire()) { List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); String prompt = CardTranslation.getTranslatedName(c.getName()) + ": " + From 5afaff814a3800657cb8613044a15710388a7dd1 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Wed, 13 Nov 2024 10:29:18 +0800 Subject: [PATCH 003/152] update deck editor for spire --- .../src/main/java/forge/item/IPaperCard.java | 1 + .../src/main/java/forge/item/PaperCard.java | 24 ++++++++++++++--- .../src/main/java/forge/item/PaperToken.java | 6 +++++ .../src/main/java/forge/game/GameAction.java | 20 +++++++++----- .../game/ability/effects/ManaEffect.java | 2 +- .../src/forge/deck/FDeckEditor.java | 27 +++++++++++++++++++ .../forge/itemmanager/views/ImageView.java | 22 +++++++++++++++ 7 files changed, 91 insertions(+), 11 deletions(-) diff --git a/forge-core/src/main/java/forge/item/IPaperCard.java b/forge-core/src/main/java/forge/item/IPaperCard.java index 72d2f9b6b8b..fef8216208f 100644 --- a/forge-core/src/main/java/forge/item/IPaperCard.java +++ b/forge-core/src/main/java/forge/item/IPaperCard.java @@ -234,6 +234,7 @@ public interface IPaperCard extends InventoryItem, Serializable { String getEdition(); String getCollectorNumber(); String getFunctionalVariant(); + List getSpireColors(); int getArtIndex(); boolean isFoil(); boolean isToken(); diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index 02116812ec6..c2462b7bf12 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -28,6 +28,7 @@ import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.ObjectInputStream; +import java.util.List; /** * A lightweight version of a card that matches real-world cards, to use outside of games (eg. inventory, decks, trade). @@ -55,6 +56,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, private final boolean foil; private Boolean hasImage; private final boolean noSell; + private List spireColors; private String sortableName; private final String functionalVariant; @@ -85,6 +87,11 @@ public class PaperCard implements Comparable, InventoryItemFromSet, return functionalVariant; } + @Override + public List getSpireColors() { + return spireColors; + } + @Override public int getArtIndex() { return artIndex; @@ -156,7 +163,10 @@ public class PaperCard implements Comparable, InventoryItemFromSet, this.artIndex, this.foil, String.valueOf(collectorNumber), this.artist, this.functionalVariant, false); return sellable; } - + public PaperCard getSpireVersion(List colors) { + return new PaperCard(this.rules, this.edition, this.rarity, + this.artIndex, this.foil, String.valueOf(collectorNumber), this.artist, this.functionalVariant, false, colors); + } @Override public String getItemType() { final Localizer localizer = Localizer.getInstance(); @@ -190,6 +200,12 @@ public class PaperCard implements Comparable, InventoryItemFromSet, public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0, final boolean foil0, final String collectorNumber0, final String artist0, final String functionalVariant, final boolean noSell0) { + this(rules0, edition0, rarity0, artIndex0, foil0, collectorNumber0, artist0, functionalVariant, noSell0, null); + } + + public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, + final int artIndex0, final boolean foil0, final String collectorNumber0, + final String artist0, final String functionalVariant, final boolean noSell0, final List spires) { if (rules0 == null || edition0 == null || rarity0 == null) { throw new IllegalArgumentException("Cannot create card without rules, edition or rarity"); } @@ -206,6 +222,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, sortableName = TextUtil.toSortableName(CardTranslation.getTranslatedName(rules0.getName())); this.functionalVariant = functionalVariant != null ? functionalVariant : IPaperCard.NO_FUNCTIONAL_VARIANT; noSell = noSell0; + spireColors = spires; } public static PaperCard FAKE_CARD = new PaperCard(CardRules.getUnsupportedCardNamed("Fake Card"), "Fake Edition", CardRarity.Common); @@ -244,10 +261,11 @@ public class PaperCard implements Comparable, InventoryItemFromSet, public int hashCode() { final int code = (name.hashCode() * 11) + (edition.hashCode() * 59) + (artIndex * 2) + (getCollectorNumber().hashCode() * 383); + final int id = spireColors == null ? 0 : spireColors.hashCode(); if (foil) { - return code + 1; + return code + id + 1; } - return code; + return code + id; } // FIXME: Check diff --git a/forge-core/src/main/java/forge/item/PaperToken.java b/forge-core/src/main/java/forge/item/PaperToken.java index c13b0435880..1eea87d50e8 100644 --- a/forge-core/src/main/java/forge/item/PaperToken.java +++ b/forge-core/src/main/java/forge/item/PaperToken.java @@ -1,6 +1,7 @@ package forge.item; import java.util.ArrayList; +import java.util.List; import java.util.Locale; import forge.card.*; @@ -152,6 +153,11 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard { return IPaperCard.NO_FUNCTIONAL_VARIANT; } + @Override + public List getSpireColors() { + return null; + } + @Override public int getArtIndex() { return artIndex; diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 974f28f63c9..c8da2085d42 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -2230,13 +2230,19 @@ public class GameAction { // TODO: only do this for the AI, for the player part, get the encoded color from the deck file and pass // it to either player or the papercard object so it feels like rule based for the player side.. if (!c.hasChosenColorSpire()) { - List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); - String prompt = CardTranslation.getTranslatedName(c.getName()) + ": " + - Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(2)); - SpellAbility sa = new SpellAbility.EmptySa(ApiType.ChooseColor, c, takesAction); - sa.putParam("AILogic", "MostProminentInComputerDeck"); - List chosenColors = takesAction.getController().chooseColors(prompt, sa, 2, 2, colorChoices); - c.setChosenColorSpire(chosenColors); + if (takesAction.isAI()) { + List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); + String prompt = CardTranslation.getTranslatedName(c.getName()) + ": " + + Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(2)); + SpellAbility sa = new SpellAbility.EmptySa(ApiType.ChooseColor, c, takesAction); + sa.putParam("AILogic", "MostProminentInComputerDeck"); + List chosenColors = takesAction.getController().chooseColors(prompt, sa, 2, 2, colorChoices); + c.setChosenColorSpire(chosenColors); + } else { + if (c.getPaperCard().getSpireColors() != null) { + c.setChosenColorSpire(c.getPaperCard().getSpireColors()); + } + } } } takesAction = game.getNextPlayerAfter(takesAction); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java index 5d393c39783..50b209c4cfe 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java @@ -119,7 +119,7 @@ public class ManaEffect extends SpellAbilityEffect { } } - if (choiceString.toString().isEmpty() && "Combo ColorIdentity".equals(abMana.getOrigProduced())) { + if (choiceString.toString().isEmpty() && ("Combo ColorIdentity".equals(abMana.getOrigProduced()) || "Combo Spire".equals(abMana.getOrigProduced()))) { // No mana could be produced here (non-EDH match?), so cut short continue; } diff --git a/forge-gui-mobile/src/forge/deck/FDeckEditor.java b/forge-gui-mobile/src/forge/deck/FDeckEditor.java index 3a2abbc3234..388d27833e2 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckEditor.java +++ b/forge-gui-mobile/src/forge/deck/FDeckEditor.java @@ -12,6 +12,7 @@ import forge.Forge.KeyInputAdapter; import forge.Graphics; import forge.assets.*; import forge.card.CardEdition; +import forge.card.MagicColor; import forge.deck.io.DeckPreferences; import forge.gamemodes.limited.BoosterDraft; import forge.gamemodes.planarconquest.ConquestUtil; @@ -1841,6 +1842,19 @@ public class FDeckEditor extends TabPageScreen { } } addCommanderItems(menu, card); + if ("Cryptic Spires".equalsIgnoreCase(card.getCardName())) { + menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> { + //sort options so current option is on top and selected by default + List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); + GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseNColors", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { + @Override + public void run(List result) { + addCard(card.getSpireVersion(result)); + removeCard(card); + } + }); + })); + } break; case Sideboard: cardSourceSection = parentScreen.isLimitedEditor() ? parentScreen.getMainDeckPage() : parentScreen.getCatalogPage(); @@ -1880,6 +1894,19 @@ public class FDeckEditor extends TabPageScreen { } } addCommanderItems(menu, card); + if ("Cryptic Spires".equalsIgnoreCase(card.getCardName())) { + menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> { + //sort options so current option is on top and selected by default + List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); + GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseNColors", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { + @Override + public void run(List result) { + addCard(card.getSpireVersion(result)); + removeCard(card); + } + }); + })); + } break; case Commander: if (parentScreen.editorType != EditorType.PlanarConquest || isPartnerCommander(card)) { diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 60097618cc1..5618dfe2e99 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -1029,6 +1029,8 @@ public class ImageView extends ItemView { private boolean selected, deckSelectMode, showRanking; private final float IMAGE_SIZE = CardRenderer.MANA_SYMBOL_SIZE; private DeckProxy deckProxy = null; + private StringBuffer spireColor = new StringBuffer(); + private TextRenderer textRenderer; private FImageComplex deckCover = null; private Texture dpImg = null; //private TextureRegion tr; @@ -1055,6 +1057,21 @@ public class ImageView extends ItemView { draftRankImage = FSkinImage.DRAFTRANK_C; } } + if (((PaperCard) item).getSpireColors() != null) { + for (String s : ((PaperCard) item).getSpireColors()) { + if ("white".equalsIgnoreCase(s)) + spireColor.append("{W}"); + if ("green".equalsIgnoreCase(s)) + spireColor.append("{G}"); + if ("red".equalsIgnoreCase(s)) + spireColor.append("{R}"); + if ("blue".equalsIgnoreCase(s)) + spireColor.append("{U}"); + if ("black".equalsIgnoreCase(s)) + spireColor.append("{B}"); + } + textRenderer = new TextRenderer(true); + } } } @@ -1136,6 +1153,11 @@ public class ImageView extends ItemView { } } } + // spire colors + if (!spireColor.isEmpty()) { + drawCardLabel(g,"", Color.GRAY, x, y, w, h); + textRenderer.drawText(g, spireColor.toString(), FSkinFont.forHeight(w / 7), Color.WHITE, x, y, w, h, y, h, false, Align.center, true); + } } else if (item instanceof ConquestCommander) { CardRenderer.drawCard(g, ((ConquestCommander) item).getCard(), x, y, w, h, pos); } else if (deckSelectMode) { From 31abd61a3f97595d3aebc5551a5d9b24151f1618 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Wed, 13 Nov 2024 11:10:27 +0800 Subject: [PATCH 004/152] fix render for ImageView --- forge-gui-mobile/src/forge/itemmanager/views/ImageView.java | 6 ++---- forge-gui/src/main/java/forge/itemmanager/ColumnDef.java | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 5618dfe2e99..47f5ca39ed1 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -44,6 +44,7 @@ public class ImageView extends ItemView { private static final float PADDING = Utils.scale(5); private static final float PILE_SPACING_Y = 0.1f; private static final FSkinFont LABEL_FONT = FSkinFont.get(12); + private TextRenderer textRenderer = new TextRenderer(true); private static FSkinColor getGroupHeaderForeColor() { if (Forge.isMobileAdventureMode) @@ -1030,7 +1031,6 @@ public class ImageView extends ItemView { private final float IMAGE_SIZE = CardRenderer.MANA_SYMBOL_SIZE; private DeckProxy deckProxy = null; private StringBuffer spireColor = new StringBuffer(); - private TextRenderer textRenderer; private FImageComplex deckCover = null; private Texture dpImg = null; //private TextureRegion tr; @@ -1070,7 +1070,6 @@ public class ImageView extends ItemView { if ("black".equalsIgnoreCase(s)) spireColor.append("{B}"); } - textRenderer = new TextRenderer(true); } } } @@ -1155,8 +1154,7 @@ public class ImageView extends ItemView { } // spire colors if (!spireColor.isEmpty()) { - drawCardLabel(g,"", Color.GRAY, x, y, w, h); - textRenderer.drawText(g, spireColor.toString(), FSkinFont.forHeight(w / 7), Color.WHITE, x, y, w, h, y, h, false, Align.center, true); + textRenderer.drawText(g, spireColor.toString(), FSkinFont.forHeight(w / 5), Color.WHITE, x, y, w, h, y, h, false, Align.center, true); } } else if (item instanceof ConquestCommander) { CardRenderer.drawCard(g, ((ConquestCommander) item).getCard(), x, y, w, h, pos); diff --git a/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java b/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java index 77e25d479b6..671c243d53e 100644 --- a/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java +++ b/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java @@ -57,8 +57,9 @@ public enum ColumnDef { NAME("lblName", "lblName", 180, false, SortState.ASC, from -> { if (from.getKey() instanceof PaperCard) { + String spire = ((PaperCard) from.getKey()).getSpireColors() == null ? "" : ((PaperCard) from.getKey()).getSpireColors().toString(); String sortableName = ((PaperCard)from.getKey()).getSortableName(); - return sortableName == null ? TextUtil.toSortableName(from.getKey().getName()) : sortableName; + return sortableName == null ? TextUtil.toSortableName(from.getKey().getName() + spire) : sortableName + spire; } return TextUtil.toSortableName(from.getKey().getName()); }, From 8fd521b949f3f67b19e68eb34908f1d7355cdbab Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Wed, 13 Nov 2024 13:43:47 +0800 Subject: [PATCH 005/152] update desktop editor --- .../main/java/forge/game/card/CardView.java | 3 +++ .../deckeditor/controllers/ACEditorBase.java | 20 +++++++++++++++++++ .../controllers/CEditorConstructed.java | 1 + .../match/controllers/CDetailPicture.java | 2 ++ .../forge/itemmanager/views/ImageView.java | 2 +- 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 9325bbd2896..c7b741590db 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -436,6 +436,9 @@ public class CardView extends GameEntityView { public List getChosenColorSpire() { return get(TrackableProperty.ChosenColorSpire); } + public void updateChosenColorSpire(List chosen) { + set(TrackableProperty.ChosenColorSpire, chosen); + } void updateChosenColorSpire(Card c) { set(TrackableProperty.ChosenColorSpire, c.getChosenColorSpire()); } diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java index 6c5dbc3dc0d..49ce9bb32e4 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java @@ -30,6 +30,7 @@ import javax.swing.SwingUtilities; import com.google.common.collect.Iterables; +import forge.card.MagicColor; import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckBase; @@ -65,6 +66,7 @@ import forge.toolbox.FLabel; import forge.toolbox.FSkin; import forge.util.Aggregates; import forge.util.ItemPool; +import forge.util.Lang; import forge.util.Localizer; import forge.view.FView; @@ -576,5 +578,23 @@ public abstract class ACEditorBase { + List colors = GuiChoose.getChoices(localizer.getMessage("lblChooseNColors", Lang.getNumeral(2)), 2, 2, MagicColor.Constant.ONLY_COLORS); + // make a foiled version based on the original + PaperCard updated = existingCard.getSpireVersion(colors); + // remove *quantity* instances of existing card + CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, 1); + // add *quantity* into the deck and set them as selected + cardManager.addItem(updated, 1); + cardManager.setSelectedItem(updated); + }, true, true); + } } } diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java index be000243440..8d5c7080d04 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java @@ -375,6 +375,7 @@ public final class CEditorConstructed extends CDeckEditor { if (foilAvailable) { cmb.addMakeFoils(); } + cmb.addSetColorSpire(); } /* (non-Javadoc) diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CDetailPicture.java b/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CDetailPicture.java index a2619254602..46bebcab791 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CDetailPicture.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CDetailPicture.java @@ -69,6 +69,8 @@ public class CDetailPicture { c.getCurrentState().setFoilIndexOverride(1); } } + if (paperCard.getSpireColors() != null && c.getChosenColorSpire() == null) + c.updateChosenColorSpire(paperCard.getSpireColors()); showCard(c, isDisplayAlt); } else { currentView = null; diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 47f5ca39ed1..3d67741fc40 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -1154,7 +1154,7 @@ public class ImageView extends ItemView { } // spire colors if (!spireColor.isEmpty()) { - textRenderer.drawText(g, spireColor.toString(), FSkinFont.forHeight(w / 5), Color.WHITE, x, y, w, h, y, h, false, Align.center, true); + textRenderer.drawText(g, spireColor.toString(), FSkinFont.forHeight(w / 5), Color.WHITE, x, y + h / 4, w, h, y, h, false, Align.center, true); } } else if (item instanceof ConquestCommander) { CardRenderer.drawCard(g, ((ConquestCommander) item).getCard(), x, y, w, h, pos); From f814292de741b2a2eca1c2e4693024b67ef9bd9c Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Wed, 13 Nov 2024 13:45:09 +0800 Subject: [PATCH 006/152] update comment --- .../java/forge/screens/deckeditor/controllers/ACEditorBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java index 49ce9bb32e4..4069cb2f3a8 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java @@ -587,7 +587,7 @@ public abstract class ACEditorBase { List colors = GuiChoose.getChoices(localizer.getMessage("lblChooseNColors", Lang.getNumeral(2)), 2, 2, MagicColor.Constant.ONLY_COLORS); - // make a foiled version based on the original + // make an updated version PaperCard updated = existingCard.getSpireVersion(colors); // remove *quantity* instances of existing card CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, 1); From f7dfdea36142ea86a6d7cd76619d21c464b8dd6e Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Wed, 13 Nov 2024 14:13:16 +0800 Subject: [PATCH 007/152] update clone and copy effect --- .../src/main/java/forge/game/ability/effects/CloneEffect.java | 2 ++ .../java/forge/game/ability/effects/CopyPermanentEffect.java | 2 ++ forge-game/src/main/java/forge/game/card/Card.java | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java index 7b5750284d5..9a7a17f216e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java @@ -199,6 +199,8 @@ public class CloneEffect extends SpellAbilityEffect { if (sa.hasParam("RememberCloneOrigin")) { tgtCard.addRemembered(cardToCopy); } + // spire + tgtCard.setChosenColorSpire(cardToCopy.getChosenColorSpire()); game.fireEvent(new GameEventCardStatsChanged(tgtCard)); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java index 08ac7418f67..93c138e9a28 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java @@ -318,6 +318,8 @@ public class CopyPermanentEffect extends TokenEffectBase { copy.setState(copy.getCurrentStateName(), true, true); } } + // spire + copy.setChosenColorSpire(original.getChosenColorSpire()); copy.setTokenSpawningAbility(sa); copy.setGamePieceType(GamePieceType.TOKEN); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 7350fe5adcd..4be24e98ae8 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -2119,7 +2119,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr public boolean hasChosenColor(String s) { return chosenColors != null && chosenColors.contains(s); } - public final Iterable getChosenColorSpire() { + public final List getChosenColorSpire() { if (chosenColorSpire == null) { return Lists.newArrayList(); } From 4012469910be0fbbe1a9586f8e76018146f33c2a Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Wed, 13 Nov 2024 19:12:34 +0800 Subject: [PATCH 008/152] refactor var name --- .../src/main/java/forge/game/GameAction.java | 8 ++------ .../game/ability/effects/CloneEffect.java | 2 +- .../ability/effects/CopyPermanentEffect.java | 2 +- .../src/main/java/forge/game/card/Card.java | 18 ++++++++++-------- .../main/java/forge/game/card/CardView.java | 11 ++++------- .../game/spellability/AbilityManaPart.java | 8 ++++---- .../forge/trackable/TrackableProperty.java | 2 +- .../match/controllers/CDetailPicture.java | 2 -- .../src/forge/itemmanager/views/ImageView.java | 16 ++++++++-------- forge-gui/res/cardsfolder/c/cryptic_spires.txt | 2 +- .../java/forge/gui/card/CardDetailUtil.java | 4 ++-- 11 files changed, 34 insertions(+), 41 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index c8da2085d42..acab924969a 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -573,7 +573,7 @@ public class GameAction { } if (c.hasChosenColorSpire()) { - copied.setChosenColorSpire(ImmutableList.copyOf(c.getChosenColorSpire())); + copied.setChosenColorID(ImmutableList.copyOf(c.getChosenColorID())); } // update state for view @@ -2237,11 +2237,7 @@ public class GameAction { SpellAbility sa = new SpellAbility.EmptySa(ApiType.ChooseColor, c, takesAction); sa.putParam("AILogic", "MostProminentInComputerDeck"); List chosenColors = takesAction.getController().chooseColors(prompt, sa, 2, 2, colorChoices); - c.setChosenColorSpire(chosenColors); - } else { - if (c.getPaperCard().getSpireColors() != null) { - c.setChosenColorSpire(c.getPaperCard().getSpireColors()); - } + c.setChosenColorID(chosenColors); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java index 9a7a17f216e..170c07fcfee 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java @@ -200,7 +200,7 @@ public class CloneEffect extends SpellAbilityEffect { tgtCard.addRemembered(cardToCopy); } // spire - tgtCard.setChosenColorSpire(cardToCopy.getChosenColorSpire()); + tgtCard.setChosenColorID(cardToCopy.getChosenColorID()); game.fireEvent(new GameEventCardStatsChanged(tgtCard)); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java index 93c138e9a28..17ee13acdb4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java @@ -319,7 +319,7 @@ public class CopyPermanentEffect extends TokenEffectBase { } } // spire - copy.setChosenColorSpire(original.getChosenColorSpire()); + copy.setChosenColorID(original.getChosenColorID()); copy.setTokenSpawningAbility(sa); copy.setGamePieceType(GamePieceType.TOKEN); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 4be24e98ae8..c7f74613245 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -293,7 +293,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr private String chosenType2 = ""; private List notedTypes = new ArrayList<>(); private List chosenColors; - private List chosenColorSpire; + private List chosenColorID; private List chosenName = new ArrayList<>(); private Integer chosenNumber; private Player chosenPlayer; @@ -400,6 +400,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr view.updateSickness(this); view.updateClassLevel(this); view.updateDraftAction(this); + if (paperCard != null) + setChosenColorID(paperCard.getSpireColors()); } public boolean changeToState(final CardStateName state) { @@ -2119,18 +2121,18 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr public boolean hasChosenColor(String s) { return chosenColors != null && chosenColors.contains(s); } - public final List getChosenColorSpire() { - if (chosenColorSpire == null) { + public final List getChosenColorID() { + if (chosenColorID == null) { return Lists.newArrayList(); } - return chosenColorSpire; + return chosenColorID; } - public final void setChosenColorSpire(final List s) { - chosenColorSpire = s; - view.updateChosenColorSpire(this); + public final void setChosenColorID(final List s) { + chosenColorID = s; + view.updateChosenColorID(this); } public boolean hasChosenColorSpire() { - return chosenColorSpire != null && !chosenColorSpire.isEmpty(); + return chosenColorID != null && !chosenColorID.isEmpty(); } public final Card getChosenCard() { return getChosenCards().getFirst(); diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index c7b741590db..8553614719d 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -433,14 +433,11 @@ public class CardView extends GameEntityView { void updateChosenColors(Card c) { set(TrackableProperty.ChosenColors, c.getChosenColors()); } - public List getChosenColorSpire() { - return get(TrackableProperty.ChosenColorSpire); + public List getChosenColorID() { + return get(TrackableProperty.ChosenColorID); } - public void updateChosenColorSpire(List chosen) { - set(TrackableProperty.ChosenColorSpire, chosen); - } - void updateChosenColorSpire(Card c) { - set(TrackableProperty.ChosenColorSpire, c.getChosenColorSpire()); + void updateChosenColorID(Card c) { + set(TrackableProperty.ChosenColorID, c.getChosenColorID()); } public FCollectionView getMergedCardsCollection() { return get(TrackableProperty.MergedCardsCollection); diff --git a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java index 5c357ad9721..742a5dc318d 100644 --- a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java +++ b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java @@ -655,8 +655,8 @@ public class AbilityManaPart implements java.io.Serializable { origProduced = origProduced.replace("Chosen", getChosenColor(sa)); } // replace Chosen for Spire colors - if (origProduced.contains("Spire")) { - origProduced = origProduced.replace("Spire", getChosenSpireColor(sa)); + if (origProduced.contains("ColorID")) { + origProduced = origProduced.replace("ColorID", getChosenColorID(sa)); } if (origProduced.contains("NotedColors")) { // Should only be used for Paliano, the High City @@ -701,14 +701,14 @@ public class AbilityManaPart implements java.io.Serializable { return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1); } - public String getChosenSpireColor(SpellAbility sa) { + public String getChosenColorID(SpellAbility sa) { if (sa == null) { return ""; } Card card = sa.getHostCard(); if (card != null && card.hasChosenColorSpire()) { StringBuilder values = new StringBuilder(); - for (String s : card.getChosenColorSpire()) { + for (String s : card.getChosenColorID()) { values.append(MagicColor.toShortString(MagicColor.fromName(s))).append(" "); } return values.toString(); diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 638c8694c94..a02db4eee41 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -67,7 +67,7 @@ public enum TrackableProperty { ChosenType2(TrackableTypes.StringType), NotedTypes(TrackableTypes.StringListType), ChosenColors(TrackableTypes.StringListType), - ChosenColorSpire(TrackableTypes.StringListType), + ChosenColorID(TrackableTypes.StringListType), ChosenCards(TrackableTypes.CardViewCollectionType), ChosenNumber(TrackableTypes.StringType), StoredRolls(TrackableTypes.StringListType), diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CDetailPicture.java b/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CDetailPicture.java index 46bebcab791..a2619254602 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CDetailPicture.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CDetailPicture.java @@ -69,8 +69,6 @@ public class CDetailPicture { c.getCurrentState().setFoilIndexOverride(1); } } - if (paperCard.getSpireColors() != null && c.getChosenColorSpire() == null) - c.updateChosenColorSpire(paperCard.getSpireColors()); showCard(c, isDisplayAlt); } else { currentView = null; diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 3d67741fc40..5f7d82c48b4 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -1030,7 +1030,7 @@ public class ImageView extends ItemView { private boolean selected, deckSelectMode, showRanking; private final float IMAGE_SIZE = CardRenderer.MANA_SYMBOL_SIZE; private DeckProxy deckProxy = null; - private StringBuffer spireColor = new StringBuffer(); + private StringBuffer colorID = new StringBuffer(); private FImageComplex deckCover = null; private Texture dpImg = null; //private TextureRegion tr; @@ -1060,15 +1060,15 @@ public class ImageView extends ItemView { if (((PaperCard) item).getSpireColors() != null) { for (String s : ((PaperCard) item).getSpireColors()) { if ("white".equalsIgnoreCase(s)) - spireColor.append("{W}"); + colorID.append("{W}"); if ("green".equalsIgnoreCase(s)) - spireColor.append("{G}"); + colorID.append("{G}"); if ("red".equalsIgnoreCase(s)) - spireColor.append("{R}"); + colorID.append("{R}"); if ("blue".equalsIgnoreCase(s)) - spireColor.append("{U}"); + colorID.append("{U}"); if ("black".equalsIgnoreCase(s)) - spireColor.append("{B}"); + colorID.append("{B}"); } } } @@ -1153,8 +1153,8 @@ public class ImageView extends ItemView { } } // spire colors - if (!spireColor.isEmpty()) { - textRenderer.drawText(g, spireColor.toString(), FSkinFont.forHeight(w / 5), Color.WHITE, x, y + h / 4, w, h, y, h, false, Align.center, true); + if (!colorID.isEmpty()) { + textRenderer.drawText(g, colorID.toString(), FSkinFont.forHeight(w / 5), Color.WHITE, x, y + h / 4, w, h, y, h, false, Align.center, true); } } else if (item instanceof ConquestCommander) { CardRenderer.drawCard(g, ((ConquestCommander) item).getCard(), x, y, w, h, pos); diff --git a/forge-gui/res/cardsfolder/c/cryptic_spires.txt b/forge-gui/res/cardsfolder/c/cryptic_spires.txt index 2f7aafde128..aff3fb69fe0 100644 --- a/forge-gui/res/cardsfolder/c/cryptic_spires.txt +++ b/forge-gui/res/cardsfolder/c/cryptic_spires.txt @@ -4,5 +4,5 @@ Types:Land Text:As you create your deck, circle two of the colors below. R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | ReplacementResult$ Updated | ReplaceWith$ ETBTapped | Description$ CARDNAME enters tapped. SVar:ETBTapped:DB$ Tap | Defined$ Self | ETB$ True -A:AB$ Mana | Cost$ T | Produced$ Combo Spire | SpellDescription$ Add one mana of either of the circled colors. +A:AB$ Mana | Cost$ T | Produced$ Combo ColorID | SpellDescription$ Add one mana of either of the circled colors. Oracle:As you create your deck, circle two of the colors below.\nCryptic Spires enters tapped.\n{T}: Add one mana of either of the circled colors. diff --git a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java index 87ec9d6fb70..76cc1c0dcdc 100644 --- a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java +++ b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java @@ -488,12 +488,12 @@ public class CardDetailUtil { } // chosen spire - if (card.getChosenColorSpire() != null && !card.getChosenColorSpire().isEmpty()) { + if (card.getChosenColorID() != null && !card.getChosenColorID().isEmpty()) { if (area.length() != 0) { area.append("\n"); } area.append("(").append(Localizer.getInstance().getMessage("lblSelected")).append(": "); - area.append(Lang.joinHomogenous(card.getChosenColorSpire().stream().map(DeckRecognizer::getLocalisedMagicColorName).collect(Collectors.toList()))); + area.append(Lang.joinHomogenous(card.getChosenColorID().stream().map(DeckRecognizer::getLocalisedMagicColorName).collect(Collectors.toList()))); area.append(")"); } From 88613c555a278345862a1f8aa1b30befbd0d94c3 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Wed, 13 Nov 2024 22:13:18 +0800 Subject: [PATCH 009/152] refactor some vars, and save to deck file todo validate --- .../src/main/java/forge/card/CardDb.java | 43 ++++++++++++++++++- .../main/java/forge/card/ICardDatabase.java | 1 + .../src/main/java/forge/deck/CardPool.java | 12 +++--- forge-core/src/main/java/forge/deck/Deck.java | 2 +- .../src/main/java/forge/item/IPaperCard.java | 2 +- .../src/main/java/forge/item/PaperCard.java | 10 ++--- .../src/main/java/forge/item/PaperToken.java | 2 +- .../src/main/java/forge/game/card/Card.java | 2 +- .../src/forge/deck/FDeckEditor.java | 4 +- .../forge/itemmanager/views/ImageView.java | 4 +- .../java/forge/itemmanager/ColumnDef.java | 2 +- 11 files changed, 62 insertions(+), 22 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index ae928fee725..8672ef475e2 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -42,6 +42,7 @@ import java.util.stream.Collectors; public final class CardDb implements ICardDatabase, IDeckGenPool { public final static String foilSuffix = "+"; public final static char NameSetSeparator = '|'; + public final static String colorIDPrefix = "#"; private final String exlcudedCardName = "Concentrate"; private final String exlcudedCardSet = "DS0"; @@ -90,13 +91,19 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { public int artIndex; public boolean isFoil; public String collectorNumber; + public List colorID; private CardRequest(String name, String edition, int artIndex, boolean isFoil, String collectorNumber) { + this(name, edition, artIndex, isFoil, collectorNumber, null); + } + + private CardRequest(String name, String edition, int artIndex, boolean isFoil, String collectorNumber, List colorID) { cardName = name; this.edition = edition; this.artIndex = artIndex; this.isFoil = isFoil; this.collectorNumber = collectorNumber; + this.colorID = colorID; } public static boolean isFoilCardName(final String cardName){ @@ -125,6 +132,14 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { return requestInfo + NameSetSeparator + artIndex; } + public static String compose(String cardName, String setCode, int artIndex, List colorID) { + String requestInfo = compose(cardName, setCode); + artIndex = Math.max(artIndex, IPaperCard.DEFAULT_ART_INDEX); + String cid = colorID == null ? "" : NameSetSeparator + + colorID.toString().replace("[", colorIDPrefix).replace(", ", colorIDPrefix).replace("]", ""); + return requestInfo + NameSetSeparator + artIndex + cid; + } + public static String compose(String cardName, String setCode, String collectorNumber) { String requestInfo = compose(cardName, setCode); // CollectorNumber will be wrapped in square brackets @@ -154,6 +169,10 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { return s.startsWith("[") && s.endsWith("]"); } + private static boolean isColorIDString(String s) { + return s.startsWith(colorIDPrefix); + } + private static boolean isArtIndex(String s) { return StringUtils.isNumeric(s) && s.length() <= 2 ; // only artIndex between 1-99 } @@ -171,7 +190,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { String cardName = info[0]; String setCode = info[1]; int artIndex = Integer.parseInt(info[2]); - return new CardRequest(cardName, setCode, artIndex, isFoil, IPaperCard.NO_COLLECTOR_NUMBER); + return new CardRequest(cardName, setCode, artIndex, isFoil, IPaperCard.NO_COLLECTOR_NUMBER, null); } catch (NumberFormatException ex){ return null; } } @@ -183,22 +202,29 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { int setPos; int artPos; int cNrPos; + int clrPos; if (info.length >= 4) { // name|set|artIndex|[collNr] setPos = isSetCode(info[1]) ? 1 : -1; artPos = isArtIndex(info[2]) ? 2 : -1; cNrPos = isCollectorNumber(info[3]) ? 3 : -1; + int pos = cNrPos > 0 ? 4 : 3; + clrPos = isColorIDString(info[pos]) ? pos : -1; } else if (info.length == 3) { // name|set|artIndex (or CollNr) setPos = isSetCode(info[1]) ? 1 : -1; artPos = isArtIndex(info[2]) ? 2 : -1; cNrPos = isCollectorNumber(info[2]) ? 2 : -1; + int pos = cNrPos > 0 ? 3 : 2; + clrPos = isColorIDString(info[pos]) ? pos : -1; } else if (info.length == 2) { // name|set (or artIndex, even if not possible via compose) setPos = isSetCode(info[1]) ? 1 : -1; artPos = isArtIndex(info[1]) ? 1 : -1; cNrPos = -1; + clrPos = -1; } else { setPos = -1; artPos = -1; cNrPos = -1; + clrPos = -1; } String cardName = info[0]; boolean isFoil = false; @@ -209,6 +235,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { int artIndex = artPos > 0 ? Integer.parseInt(info[artPos]) : IPaperCard.NO_ART_INDEX; // default: no art index String collectorNumber = cNrPos > 0 ? info[cNrPos].substring(1, info[cNrPos].length() - 1) : IPaperCard.NO_COLLECTOR_NUMBER; String setCode = setPos > 0 ? info[setPos] : null; + List colorID = clrPos > 0 ? Arrays.stream(info[clrPos].substring(1).split(colorIDPrefix)).collect(Collectors.toList()) : null; if (setCode != null && setCode.equals(CardEdition.UNKNOWN.getCode())) { // ??? setCode = null; } @@ -224,7 +251,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { // finally, check whether any between artIndex and CollectorNumber has been set if (collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER) && artIndex == IPaperCard.NO_ART_INDEX) artIndex = IPaperCard.DEFAULT_ART_INDEX; - return new CardRequest(cardName, setCode, artIndex, isFoil, collectorNumber); + return new CardRequest(cardName, setCode, artIndex, isFoil, collectorNumber, colorID); } } @@ -569,6 +596,13 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { return tryGetCard(request); } + @Override + public PaperCard getCard(final String cardName, String setCode, int artIndex, List colorID) { + String reqInfo = CardRequest.compose(cardName, setCode, artIndex, colorID); + CardRequest request = CardRequest.fromString(reqInfo); + return tryGetCard(request); + } + private PaperCard tryGetCard(CardRequest request) { // Before doing anything, check that a non-null request has been provided if (request == null) @@ -1128,6 +1162,11 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { if (artCount >= IPaperCard.DEFAULT_ART_INDEX) { sb.append(CardDb.NameSetSeparator).append(card.getArtIndex()); // indexes start at 1 to match image file name conventions } + if (card.getColorID() != null) { + sb.append(CardDb.NameSetSeparator); + for (String color : card.getColorID()) + sb.append(CardDb.colorIDPrefix).append(color); + } } return sb; diff --git a/forge-core/src/main/java/forge/card/ICardDatabase.java b/forge-core/src/main/java/forge/card/ICardDatabase.java index 4618e78f83f..b5984abb741 100644 --- a/forge-core/src/main/java/forge/card/ICardDatabase.java +++ b/forge-core/src/main/java/forge/card/ICardDatabase.java @@ -50,6 +50,7 @@ public interface ICardDatabase extends Iterable { // [NEW Methods] Including the card CollectorNumber as criterion for DB lookup PaperCard getCard(String cardName, String edition, String collectorNumber); PaperCard getCard(String cardName, String edition, int artIndex, String collectorNumber); + PaperCard getCard(String cardName, String edition, int artIndex, List colorID); // 2. Card Lookup from a single Expansion Set PaperCard getCardFromSet(String cardName, CardEdition edition, boolean isFoil); // NOT yet used, included for API symmetry diff --git a/forge-core/src/main/java/forge/deck/CardPool.java b/forge-core/src/main/java/forge/deck/CardPool.java index 0846ae7ed49..6696253b2b5 100644 --- a/forge-core/src/main/java/forge/deck/CardPool.java +++ b/forge-core/src/main/java/forge/deck/CardPool.java @@ -53,7 +53,7 @@ public class CardPool extends ItemPool { public void add(final String cardRequest, final int amount) { CardDb.CardRequest request = CardDb.CardRequest.fromString(cardRequest); - this.add(CardDb.CardRequest.compose(request.cardName, request.isFoil), request.edition, request.artIndex, amount); + this.add(CardDb.CardRequest.compose(request.cardName, request.isFoil), request.edition, request.artIndex, amount, false, request.colorID); } public void add(final String cardName, final String setCode) { @@ -65,14 +65,14 @@ public class CardPool extends ItemPool { } public void add(final String cardName, final String setCode, final int amount, boolean addAny) { - this.add(cardName, setCode, IPaperCard.NO_ART_INDEX, amount, addAny); + this.add(cardName, setCode, IPaperCard.NO_ART_INDEX, amount, addAny, null); } // NOTE: ART indices are "1" -based public void add(String cardName, String setCode, int artIndex, final int amount) { - this.add(cardName, setCode, artIndex, amount, false); + this.add(cardName, setCode, artIndex, amount, false, null); } - public void add(String cardName, String setCode, int artIndex, final int amount, boolean addAny) { + public void add(String cardName, String setCode, int artIndex, final int amount, boolean addAny, List colorID) { Map dbs = StaticData.instance().getAvailableDatabases(); PaperCard paperCard = null; String selectedDbName = ""; @@ -82,7 +82,7 @@ public class CardPool extends ItemPool { for (Map.Entry entry: dbs.entrySet()){ String dbName = entry.getKey(); CardDb db = entry.getValue(); - paperCard = db.getCard(cardName, setCode, artIndex); + paperCard = db.getCard(cardName, setCode, artIndex, colorID); if (paperCard != null) { selectedDbName = dbName; break; @@ -124,7 +124,7 @@ public class CardPool extends ItemPool { int cnt = artGroups[i - 1]; if (cnt <= 0) continue; - PaperCard randomCard = cardDb.getCard(cardName, setCode, i); + PaperCard randomCard = cardDb.getCard(cardName, setCode, i, colorID); this.add(randomCard, cnt); } } diff --git a/forge-core/src/main/java/forge/deck/Deck.java b/forge-core/src/main/java/forge/deck/Deck.java index 8cbbcbcc3b9..974bd4592d1 100644 --- a/forge-core/src/main/java/forge/deck/Deck.java +++ b/forge-core/src/main/java/forge/deck/Deck.java @@ -337,7 +337,7 @@ public class Deck extends DeckBase implements Iterable originalRequest : originalCardRequests){ String cardRequest = originalRequest.getLeft(); diff --git a/forge-core/src/main/java/forge/item/IPaperCard.java b/forge-core/src/main/java/forge/item/IPaperCard.java index fef8216208f..b9694e083d2 100644 --- a/forge-core/src/main/java/forge/item/IPaperCard.java +++ b/forge-core/src/main/java/forge/item/IPaperCard.java @@ -234,7 +234,7 @@ public interface IPaperCard extends InventoryItem, Serializable { String getEdition(); String getCollectorNumber(); String getFunctionalVariant(); - List getSpireColors(); + List getColorID(); int getArtIndex(); boolean isFoil(); boolean isToken(); diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index c2462b7bf12..2da629bdc02 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -56,7 +56,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, private final boolean foil; private Boolean hasImage; private final boolean noSell; - private List spireColors; + private List colorID; private String sortableName; private final String functionalVariant; @@ -88,8 +88,8 @@ public class PaperCard implements Comparable, InventoryItemFromSet, } @Override - public List getSpireColors() { - return spireColors; + public List getColorID() { + return colorID; } @Override @@ -222,7 +222,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, sortableName = TextUtil.toSortableName(CardTranslation.getTranslatedName(rules0.getName())); this.functionalVariant = functionalVariant != null ? functionalVariant : IPaperCard.NO_FUNCTIONAL_VARIANT; noSell = noSell0; - spireColors = spires; + colorID = spires; } public static PaperCard FAKE_CARD = new PaperCard(CardRules.getUnsupportedCardNamed("Fake Card"), "Fake Edition", CardRarity.Common); @@ -261,7 +261,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, public int hashCode() { final int code = (name.hashCode() * 11) + (edition.hashCode() * 59) + (artIndex * 2) + (getCollectorNumber().hashCode() * 383); - final int id = spireColors == null ? 0 : spireColors.hashCode(); + final int id = colorID == null ? 0 : colorID.hashCode(); if (foil) { return code + id + 1; } diff --git a/forge-core/src/main/java/forge/item/PaperToken.java b/forge-core/src/main/java/forge/item/PaperToken.java index 1eea87d50e8..b4a2411897e 100644 --- a/forge-core/src/main/java/forge/item/PaperToken.java +++ b/forge-core/src/main/java/forge/item/PaperToken.java @@ -154,7 +154,7 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard { } @Override - public List getSpireColors() { + public List getColorID() { return null; } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index c7f74613245..45616ce4850 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -401,7 +401,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr view.updateClassLevel(this); view.updateDraftAction(this); if (paperCard != null) - setChosenColorID(paperCard.getSpireColors()); + setChosenColorID(paperCard.getColorID()); } public boolean changeToState(final CardStateName state) { diff --git a/forge-gui-mobile/src/forge/deck/FDeckEditor.java b/forge-gui-mobile/src/forge/deck/FDeckEditor.java index 388d27833e2..020c706925a 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckEditor.java +++ b/forge-gui-mobile/src/forge/deck/FDeckEditor.java @@ -1846,7 +1846,7 @@ public class FDeckEditor extends TabPageScreen { menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> { //sort options so current option is on top and selected by default List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); - GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseNColors", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { + GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { @Override public void run(List result) { addCard(card.getSpireVersion(result)); @@ -1898,7 +1898,7 @@ public class FDeckEditor extends TabPageScreen { menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> { //sort options so current option is on top and selected by default List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); - GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseNColors", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { + GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { @Override public void run(List result) { addCard(card.getSpireVersion(result)); diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 5f7d82c48b4..1c6d0d26b2d 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -1057,8 +1057,8 @@ public class ImageView extends ItemView { draftRankImage = FSkinImage.DRAFTRANK_C; } } - if (((PaperCard) item).getSpireColors() != null) { - for (String s : ((PaperCard) item).getSpireColors()) { + if (((PaperCard) item).getColorID() != null) { + for (String s : ((PaperCard) item).getColorID()) { if ("white".equalsIgnoreCase(s)) colorID.append("{W}"); if ("green".equalsIgnoreCase(s)) diff --git a/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java b/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java index 671c243d53e..2f910ef7ecc 100644 --- a/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java +++ b/forge-gui/src/main/java/forge/itemmanager/ColumnDef.java @@ -57,7 +57,7 @@ public enum ColumnDef { NAME("lblName", "lblName", 180, false, SortState.ASC, from -> { if (from.getKey() instanceof PaperCard) { - String spire = ((PaperCard) from.getKey()).getSpireColors() == null ? "" : ((PaperCard) from.getKey()).getSpireColors().toString(); + String spire = ((PaperCard) from.getKey()).getColorID() == null ? "" : ((PaperCard) from.getKey()).getColorID().toString(); String sortableName = ((PaperCard)from.getKey()).getSortableName(); return sortableName == null ? TextUtil.toSortableName(from.getKey().getName() + spire) : sortableName + spire; } From 85913a3d6c89c4ffd76c939d06522663a8162b29 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Wed, 13 Nov 2024 23:42:08 +0800 Subject: [PATCH 010/152] load from deck --- .../src/main/java/forge/card/CardDb.java | 33 ++++++++++++++----- .../main/java/forge/card/ICardDatabase.java | 2 ++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index 8672ef475e2..e9e1e1890c3 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -207,14 +207,14 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { setPos = isSetCode(info[1]) ? 1 : -1; artPos = isArtIndex(info[2]) ? 2 : -1; cNrPos = isCollectorNumber(info[3]) ? 3 : -1; - int pos = cNrPos > 0 ? 4 : 3; - clrPos = isColorIDString(info[pos]) ? pos : -1; + int pos = cNrPos > 0 ? -1 : 3; + clrPos = pos > 0 ? isColorIDString(info[pos]) ? pos : -1 : -1; } else if (info.length == 3) { // name|set|artIndex (or CollNr) setPos = isSetCode(info[1]) ? 1 : -1; artPos = isArtIndex(info[2]) ? 2 : -1; cNrPos = isCollectorNumber(info[2]) ? 2 : -1; - int pos = cNrPos > 0 ? 3 : 2; - clrPos = isColorIDString(info[pos]) ? pos : -1; + int pos = cNrPos > 0 ? -1 : 2; + clrPos = pos > 0 ? isColorIDString(info[pos]) ? pos : -1 : -1; } else if (info.length == 2) { // name|set (or artIndex, even if not possible via compose) setPos = isSetCode(info[1]) ? 1 : -1; artPos = isArtIndex(info[1]) ? 1 : -1; @@ -614,8 +614,9 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { // MOST of the extensions have two short codes, 141 out of 221 (so far) // ALSO: Set Code are always UpperCase CardEdition edition = editions.get(reqEditionCode.toUpperCase()); + return this.getCardFromSet(request.cardName, edition, request.artIndex, - request.collectorNumber, request.isFoil); + request.collectorNumber, request.isFoil, request.colorID); } // 2. Card lookup in edition with specified filter didn't work. @@ -657,8 +658,12 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { } @Override - public PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, - String collectorNumber, boolean isFoil) { + public PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, String collectorNumber, boolean isFoil) { + return getCardFromSet(cardName, edition, artIndex, collectorNumber, isFoil, null); + } + + @Override + public PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, String collectorNumber, boolean isFoil, List colorID) { if (edition == null || cardName == null) // preview cards return null; // No cards will be returned @@ -692,7 +697,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { while (!candidate.hasImage() && candidatesIterator.hasNext()) candidate = candidatesIterator.next(); candidate = candidate.hasImage() ? candidate : firstCandidate; - return isFoil ? candidate.getFoiled() : candidate; + return isFoil ? candidate.getFoiled().getSpireVersion(colorID) : candidate.getSpireVersion(colorID); } /* @@ -735,6 +740,11 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { return this.tryToGetCardFromEditions(cardInfo, artPreference, artIndex, filter); } + @Override + public PaperCard getCardFromEditions(final String cardInfo, final CardArtPreference artPreference, int artIndex, List colorID) { + return this.tryToGetCardFromEditions(cardInfo, artPreference, artIndex, null, false, null, colorID); + } + /* * =============================================== * 4. SPECIALISED CARD LOOKUP BASED ON @@ -809,6 +819,11 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { private PaperCard tryToGetCardFromEditions(String cardInfo, CardArtPreference artPreference, int artIndex, Date releaseDate, boolean releasedBeforeFlag, Predicate filter){ + return this.tryToGetCardFromEditions(cardInfo, artPreference, artIndex, releaseDate, releasedBeforeFlag, filter, null); + } + + private PaperCard tryToGetCardFromEditions(String cardInfo, CardArtPreference artPreference, int artIndex, + Date releaseDate, boolean releasedBeforeFlag, Predicate filter, List colorID){ if (cardInfo == null) return null; final CardRequest cr = CardRequest.fromString(cardInfo); @@ -889,7 +904,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { } candidate = candidate.hasImage() ? candidate : firstCandidate; //If any, we're sure that at least one candidate is always returned despite it having any image - return cr.isFoil ? candidate.getFoiled() : candidate; + return cr.isFoil ? candidate.getFoiled().getSpireVersion(colorID) : candidate.getSpireVersion(colorID); } @Override diff --git a/forge-core/src/main/java/forge/card/ICardDatabase.java b/forge-core/src/main/java/forge/card/ICardDatabase.java index b5984abb741..a8165dff000 100644 --- a/forge-core/src/main/java/forge/card/ICardDatabase.java +++ b/forge-core/src/main/java/forge/card/ICardDatabase.java @@ -57,12 +57,14 @@ public interface ICardDatabase extends Iterable { PaperCard getCardFromSet(String cardName, CardEdition edition, String collectorNumber, boolean isFoil); PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, boolean isFoil); PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, String collectorNumber, boolean isFoil); + PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, String collectorNumber, boolean isFoil, List colorID); // 3. Card lookup based on CardArtPreference Selection Policy PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference); PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference, Predicate filter); PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference, int artIndex); PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference, int artIndex, Predicate filter); + PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference, int artIndex, List colorID); // 4. Specialised Card Lookup on CardArtPreference Selection and Release Date PaperCard getCardFromEditionsReleasedBefore(String cardName, CardArtPreference artPreference, Date releaseDate); From 71ecf91bb5a74d27df41b7716b7f2daa19e00c58 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 14 Nov 2024 06:00:02 +0800 Subject: [PATCH 011/152] from list to set --- .../src/main/java/forge/card/CardDb.java | 19 ++++++++++--------- .../main/java/forge/card/ICardDatabase.java | 7 ++++--- .../src/main/java/forge/deck/CardPool.java | 2 +- .../src/main/java/forge/item/IPaperCard.java | 2 +- .../src/main/java/forge/item/PaperCard.java | 17 +++++++++++------ .../src/main/java/forge/item/PaperToken.java | 3 ++- .../src/main/java/forge/game/GameAction.java | 4 ++-- .../src/main/java/forge/game/card/Card.java | 8 ++++---- .../main/java/forge/game/card/CardView.java | 2 +- .../forge/trackable/TrackableProperty.java | 2 +- .../deckeditor/controllers/ACEditorBase.java | 5 ++++- .../src/forge/deck/FDeckEditor.java | 8 ++++---- 12 files changed, 45 insertions(+), 34 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index e9e1e1890c3..4dcd013be60 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -28,6 +28,7 @@ import forge.deck.generation.IDeckGenPool; import forge.item.IPaperCard; import forge.item.PaperCard; import forge.util.CollectionSuppliers; +import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.TextUtil; import forge.util.lang.LangEnglish; @@ -91,13 +92,13 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { public int artIndex; public boolean isFoil; public String collectorNumber; - public List colorID; + public Set colorID; private CardRequest(String name, String edition, int artIndex, boolean isFoil, String collectorNumber) { this(name, edition, artIndex, isFoil, collectorNumber, null); } - private CardRequest(String name, String edition, int artIndex, boolean isFoil, String collectorNumber, List colorID) { + private CardRequest(String name, String edition, int artIndex, boolean isFoil, String collectorNumber, Set colorID) { cardName = name; this.edition = edition; this.artIndex = artIndex; @@ -132,7 +133,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { return requestInfo + NameSetSeparator + artIndex; } - public static String compose(String cardName, String setCode, int artIndex, List colorID) { + public static String compose(String cardName, String setCode, int artIndex, Set colorID) { String requestInfo = compose(cardName, setCode); artIndex = Math.max(artIndex, IPaperCard.DEFAULT_ART_INDEX); String cid = colorID == null ? "" : NameSetSeparator + @@ -235,7 +236,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { int artIndex = artPos > 0 ? Integer.parseInt(info[artPos]) : IPaperCard.NO_ART_INDEX; // default: no art index String collectorNumber = cNrPos > 0 ? info[cNrPos].substring(1, info[cNrPos].length() - 1) : IPaperCard.NO_COLLECTOR_NUMBER; String setCode = setPos > 0 ? info[setPos] : null; - List colorID = clrPos > 0 ? Arrays.stream(info[clrPos].substring(1).split(colorIDPrefix)).collect(Collectors.toList()) : null; + Set colorID = clrPos > 0 ? Arrays.stream(info[clrPos].substring(1).split(colorIDPrefix)).collect(Collectors.toSet()) : null; if (setCode != null && setCode.equals(CardEdition.UNKNOWN.getCode())) { // ??? setCode = null; } @@ -597,7 +598,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { } @Override - public PaperCard getCard(final String cardName, String setCode, int artIndex, List colorID) { + public PaperCard getCard(final String cardName, String setCode, int artIndex, Set colorID) { String reqInfo = CardRequest.compose(cardName, setCode, artIndex, colorID); CardRequest request = CardRequest.fromString(reqInfo); return tryGetCard(request); @@ -663,7 +664,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { } @Override - public PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, String collectorNumber, boolean isFoil, List colorID) { + public PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, String collectorNumber, boolean isFoil, Set colorID) { if (edition == null || cardName == null) // preview cards return null; // No cards will be returned @@ -741,7 +742,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { } @Override - public PaperCard getCardFromEditions(final String cardInfo, final CardArtPreference artPreference, int artIndex, List colorID) { + public PaperCard getCardFromEditions(final String cardInfo, final CardArtPreference artPreference, int artIndex, Set colorID) { return this.tryToGetCardFromEditions(cardInfo, artPreference, artIndex, null, false, null, colorID); } @@ -823,7 +824,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { } private PaperCard tryToGetCardFromEditions(String cardInfo, CardArtPreference artPreference, int artIndex, - Date releaseDate, boolean releasedBeforeFlag, Predicate filter, List colorID){ + Date releaseDate, boolean releasedBeforeFlag, Predicate filter, Set colorID){ if (cardInfo == null) return null; final CardRequest cr = CardRequest.fromString(cardInfo); @@ -891,7 +892,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { if (acceptedEditions.size() > 1) { Collections.sort(acceptedEditions); // CardEdition correctly sort by (release) date if (artPref.latestFirst) - Collections.reverse(acceptedEditions); // newest editions first + CollectionUtil.reverse(acceptedEditions); // newest editions first } final Iterator editionIterator = acceptedEditions.iterator(); diff --git a/forge-core/src/main/java/forge/card/ICardDatabase.java b/forge-core/src/main/java/forge/card/ICardDatabase.java index a8165dff000..15761b234fa 100644 --- a/forge-core/src/main/java/forge/card/ICardDatabase.java +++ b/forge-core/src/main/java/forge/card/ICardDatabase.java @@ -7,6 +7,7 @@ import forge.item.PaperCard; import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.Set; public interface ICardDatabase extends Iterable { /** @@ -50,21 +51,21 @@ public interface ICardDatabase extends Iterable { // [NEW Methods] Including the card CollectorNumber as criterion for DB lookup PaperCard getCard(String cardName, String edition, String collectorNumber); PaperCard getCard(String cardName, String edition, int artIndex, String collectorNumber); - PaperCard getCard(String cardName, String edition, int artIndex, List colorID); + PaperCard getCard(String cardName, String edition, int artIndex, Set colorID); // 2. Card Lookup from a single Expansion Set PaperCard getCardFromSet(String cardName, CardEdition edition, boolean isFoil); // NOT yet used, included for API symmetry PaperCard getCardFromSet(String cardName, CardEdition edition, String collectorNumber, boolean isFoil); PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, boolean isFoil); PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, String collectorNumber, boolean isFoil); - PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, String collectorNumber, boolean isFoil, List colorID); + PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, String collectorNumber, boolean isFoil, Set colorID); // 3. Card lookup based on CardArtPreference Selection Policy PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference); PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference, Predicate filter); PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference, int artIndex); PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference, int artIndex, Predicate filter); - PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference, int artIndex, List colorID); + PaperCard getCardFromEditions(String cardName, CardArtPreference artPreference, int artIndex, Set colorID); // 4. Specialised Card Lookup on CardArtPreference Selection and Release Date PaperCard getCardFromEditionsReleasedBefore(String cardName, CardArtPreference artPreference, Date releaseDate); diff --git a/forge-core/src/main/java/forge/deck/CardPool.java b/forge-core/src/main/java/forge/deck/CardPool.java index 6696253b2b5..5b9d3d2c98a 100644 --- a/forge-core/src/main/java/forge/deck/CardPool.java +++ b/forge-core/src/main/java/forge/deck/CardPool.java @@ -72,7 +72,7 @@ public class CardPool extends ItemPool { public void add(String cardName, String setCode, int artIndex, final int amount) { this.add(cardName, setCode, artIndex, amount, false, null); } - public void add(String cardName, String setCode, int artIndex, final int amount, boolean addAny, List colorID) { + public void add(String cardName, String setCode, int artIndex, final int amount, boolean addAny, Set colorID) { Map dbs = StaticData.instance().getAvailableDatabases(); PaperCard paperCard = null; String selectedDbName = ""; diff --git a/forge-core/src/main/java/forge/item/IPaperCard.java b/forge-core/src/main/java/forge/item/IPaperCard.java index b9694e083d2..51cf00fe92f 100644 --- a/forge-core/src/main/java/forge/item/IPaperCard.java +++ b/forge-core/src/main/java/forge/item/IPaperCard.java @@ -234,7 +234,7 @@ public interface IPaperCard extends InventoryItem, Serializable { String getEdition(); String getCollectorNumber(); String getFunctionalVariant(); - List getColorID(); + Set getColorID(); int getArtIndex(); boolean isFoil(); boolean isToken(); diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index 2da629bdc02..17cdaab38ac 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -29,6 +29,8 @@ import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.ObjectInputStream; import java.util.List; +import java.util.Optional; +import java.util.Set; /** * A lightweight version of a card that matches real-world cards, to use outside of games (eg. inventory, decks, trade). @@ -56,7 +58,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, private final boolean foil; private Boolean hasImage; private final boolean noSell; - private List colorID; + private Set colorID; private String sortableName; private final String functionalVariant; @@ -88,7 +90,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, } @Override - public List getColorID() { + public Set getColorID() { return colorID; } @@ -163,7 +165,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, this.artIndex, this.foil, String.valueOf(collectorNumber), this.artist, this.functionalVariant, false); return sellable; } - public PaperCard getSpireVersion(List colors) { + public PaperCard getSpireVersion(Set colors) { return new PaperCard(this.rules, this.edition, this.rarity, this.artIndex, this.foil, String.valueOf(collectorNumber), this.artist, this.functionalVariant, false, colors); } @@ -205,7 +207,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0, final boolean foil0, final String collectorNumber0, - final String artist0, final String functionalVariant, final boolean noSell0, final List spires) { + final String artist0, final String functionalVariant, final boolean noSell0, final Set colorID0) { if (rules0 == null || edition0 == null || rarity0 == null) { throw new IllegalArgumentException("Cannot create card without rules, edition or rarity"); } @@ -222,7 +224,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, sortableName = TextUtil.toSortableName(CardTranslation.getTranslatedName(rules0.getName())); this.functionalVariant = functionalVariant != null ? functionalVariant : IPaperCard.NO_FUNCTIONAL_VARIANT; noSell = noSell0; - colorID = spires; + colorID = colorID0; } public static PaperCard FAKE_CARD = new PaperCard(CardRules.getUnsupportedCardNamed("Fake Card"), "Fake Edition", CardRarity.Common); @@ -249,6 +251,9 @@ public class PaperCard implements Comparable, InventoryItemFromSet, } if (!getCollectorNumber().equals(other.getCollectorNumber())) return false; + // colorID can be NULL + if (getColorID() != other.getColorID()) + return false; return (other.foil == foil) && (other.artIndex == artIndex); } @@ -261,7 +266,7 @@ public class PaperCard implements Comparable, InventoryItemFromSet, public int hashCode() { final int code = (name.hashCode() * 11) + (edition.hashCode() * 59) + (artIndex * 2) + (getCollectorNumber().hashCode() * 383); - final int id = colorID == null ? 0 : colorID.hashCode(); + final int id = Optional.ofNullable(colorID).map(Set::hashCode).orElse(0); if (foil) { return code + id + 1; } diff --git a/forge-core/src/main/java/forge/item/PaperToken.java b/forge-core/src/main/java/forge/item/PaperToken.java index b4a2411897e..cbabd12bfa9 100644 --- a/forge-core/src/main/java/forge/item/PaperToken.java +++ b/forge-core/src/main/java/forge/item/PaperToken.java @@ -3,6 +3,7 @@ package forge.item; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Set; import forge.card.*; import org.apache.commons.lang3.StringUtils; @@ -154,7 +155,7 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard { } @Override - public List getColorID() { + public Set getColorID() { return null; } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index acab924969a..d9e52576309 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -573,7 +573,7 @@ public class GameAction { } if (c.hasChosenColorSpire()) { - copied.setChosenColorID(ImmutableList.copyOf(c.getChosenColorID())); + copied.setChosenColorID(ImmutableSet.copyOf(c.getChosenColorID())); } // update state for view @@ -2236,7 +2236,7 @@ public class GameAction { Localizer.getInstance().getMessage("lblChooseNColors", Lang.getNumeral(2)); SpellAbility sa = new SpellAbility.EmptySa(ApiType.ChooseColor, c, takesAction); sa.putParam("AILogic", "MostProminentInComputerDeck"); - List chosenColors = takesAction.getController().chooseColors(prompt, sa, 2, 2, colorChoices); + Set chosenColors = new HashSet<>(takesAction.getController().chooseColors(prompt, sa, 2, 2, colorChoices)); c.setChosenColorID(chosenColors); } } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 45616ce4850..2d00f281ea6 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -293,7 +293,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr private String chosenType2 = ""; private List notedTypes = new ArrayList<>(); private List chosenColors; - private List chosenColorID; + private Set chosenColorID; private List chosenName = new ArrayList<>(); private Integer chosenNumber; private Player chosenPlayer; @@ -2121,13 +2121,13 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr public boolean hasChosenColor(String s) { return chosenColors != null && chosenColors.contains(s); } - public final List getChosenColorID() { + public final Set getChosenColorID() { if (chosenColorID == null) { - return Lists.newArrayList(); + return Sets.newHashSet(); } return chosenColorID; } - public final void setChosenColorID(final List s) { + public final void setChosenColorID(final Set s) { chosenColorID = s; view.updateChosenColorID(this); } diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index 8553614719d..5ce321d5fac 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -433,7 +433,7 @@ public class CardView extends GameEntityView { void updateChosenColors(Card c) { set(TrackableProperty.ChosenColors, c.getChosenColors()); } - public List getChosenColorID() { + public Set getChosenColorID() { return get(TrackableProperty.ChosenColorID); } void updateChosenColorID(Card c) { diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index a02db4eee41..9ce53471afb 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -67,7 +67,7 @@ public enum TrackableProperty { ChosenType2(TrackableTypes.StringType), NotedTypes(TrackableTypes.StringListType), ChosenColors(TrackableTypes.StringListType), - ChosenColorID(TrackableTypes.StringListType), + ChosenColorID(TrackableTypes.StringSetType), ChosenCards(TrackableTypes.CardViewCollectionType), ChosenNumber(TrackableTypes.StringType), StoredRolls(TrackableTypes.StringListType), diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java index 4069cb2f3a8..5fc72473d1c 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java @@ -20,8 +20,11 @@ package forge.screens.deckeditor.controllers; import java.awt.Toolkit; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; +import java.util.HashSet; import java.util.List; import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; import javax.swing.JMenu; import javax.swing.JPopupMenu; @@ -586,7 +589,7 @@ public abstract class ACEditorBase { - List colors = GuiChoose.getChoices(localizer.getMessage("lblChooseNColors", Lang.getNumeral(2)), 2, 2, MagicColor.Constant.ONLY_COLORS); + Set colors = new HashSet<>(GuiChoose.getChoices(localizer.getMessage("lblChooseNColors", Lang.getNumeral(2)), 2, 2, MagicColor.Constant.ONLY_COLORS)); // make an updated version PaperCard updated = existingCard.getSpireVersion(colors); // remove *quantity* instances of existing card diff --git a/forge-gui-mobile/src/forge/deck/FDeckEditor.java b/forge-gui-mobile/src/forge/deck/FDeckEditor.java index 020c706925a..1b4c58df7b9 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckEditor.java +++ b/forge-gui-mobile/src/forge/deck/FDeckEditor.java @@ -1845,11 +1845,11 @@ public class FDeckEditor extends TabPageScreen { if ("Cryptic Spires".equalsIgnoreCase(card.getCardName())) { menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> { //sort options so current option is on top and selected by default - List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); + Set colorChoices = new HashSet<>(MagicColor.Constant.ONLY_COLORS); GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { @Override public void run(List result) { - addCard(card.getSpireVersion(result)); + addCard(card.getSpireVersion(new HashSet<>(result))); removeCard(card); } }); @@ -1897,11 +1897,11 @@ public class FDeckEditor extends TabPageScreen { if ("Cryptic Spires".equalsIgnoreCase(card.getCardName())) { menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> { //sort options so current option is on top and selected by default - List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); + Set colorChoices = new HashSet<>(MagicColor.Constant.ONLY_COLORS); GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { @Override public void run(List result) { - addCard(card.getSpireVersion(result)); + addCard(card.getSpireVersion(new HashSet<>(result))); removeCard(card); } }); From 60cfd971625e68e7c4034185d20e542927177982 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 14 Nov 2024 06:02:38 +0800 Subject: [PATCH 012/152] remove unused imports --- forge-core/src/main/java/forge/item/PaperCard.java | 1 - forge-core/src/main/java/forge/item/PaperToken.java | 1 - .../java/forge/screens/deckeditor/controllers/ACEditorBase.java | 1 - 3 files changed, 3 deletions(-) diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index 17cdaab38ac..24e7939df0b 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -28,7 +28,6 @@ import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.ObjectInputStream; -import java.util.List; import java.util.Optional; import java.util.Set; diff --git a/forge-core/src/main/java/forge/item/PaperToken.java b/forge-core/src/main/java/forge/item/PaperToken.java index cbabd12bfa9..4f96abee9e0 100644 --- a/forge-core/src/main/java/forge/item/PaperToken.java +++ b/forge-core/src/main/java/forge/item/PaperToken.java @@ -1,7 +1,6 @@ package forge.item; import java.util.ArrayList; -import java.util.List; import java.util.Locale; import java.util.Set; diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java index 5fc72473d1c..9771ac7bf1b 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java @@ -24,7 +24,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; -import java.util.stream.Collectors; import javax.swing.JMenu; import javax.swing.JPopupMenu; From 8d9dabf7d70e630dc89baa6a6604334c67dab8eb Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 14 Nov 2024 06:06:26 +0800 Subject: [PATCH 013/152] remove uncommited changes --- forge-core/src/main/java/forge/card/CardDb.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index 4dcd013be60..b781ae6c641 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -28,7 +28,6 @@ import forge.deck.generation.IDeckGenPool; import forge.item.IPaperCard; import forge.item.PaperCard; import forge.util.CollectionSuppliers; -import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.TextUtil; import forge.util.lang.LangEnglish; @@ -892,7 +891,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { if (acceptedEditions.size() > 1) { Collections.sort(acceptedEditions); // CardEdition correctly sort by (release) date if (artPref.latestFirst) - CollectionUtil.reverse(acceptedEditions); // newest editions first + Collections.reverse(acceptedEditions); // newest editions first } final Iterator editionIterator = acceptedEditions.iterator(); From b1615025954a6d954f999cf4b602e99ff1f5a150 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 14 Nov 2024 10:30:33 +0800 Subject: [PATCH 014/152] update setting colorID --- forge-core/src/main/java/forge/card/CardDb.java | 4 ++-- forge-core/src/main/java/forge/card/CardRules.java | 12 ++++++++++++ forge-core/src/main/java/forge/item/PaperCard.java | 4 ++-- .../deckeditor/controllers/ACEditorBase.java | 9 +++++---- .../deckeditor/controllers/CEditorConstructed.java | 2 +- forge-gui-mobile/src/forge/deck/FDeckEditor.java | 14 ++++++++------ forge-gui/res/cardsfolder/c/cryptic_spires.txt | 1 + 7 files changed, 31 insertions(+), 15 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index b781ae6c641..db7718b875f 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -697,7 +697,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { while (!candidate.hasImage() && candidatesIterator.hasNext()) candidate = candidatesIterator.next(); candidate = candidate.hasImage() ? candidate : firstCandidate; - return isFoil ? candidate.getFoiled().getSpireVersion(colorID) : candidate.getSpireVersion(colorID); + return isFoil ? candidate.getFoiled().getColorIDVersion(colorID) : candidate.getColorIDVersion(colorID); } /* @@ -904,7 +904,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { } candidate = candidate.hasImage() ? candidate : firstCandidate; //If any, we're sure that at least one candidate is always returned despite it having any image - return cr.isFoil ? candidate.getFoiled().getSpireVersion(colorID) : candidate.getSpireVersion(colorID); + return cr.isFoil ? candidate.getFoiled().getColorIDVersion(colorID) : candidate.getColorIDVersion(colorID); } @Override diff --git a/forge-core/src/main/java/forge/card/CardRules.java b/forge-core/src/main/java/forge/card/CardRules.java index c6e8cdc29c7..3811eeb3ded 100644 --- a/forge-core/src/main/java/forge/card/CardRules.java +++ b/forge-core/src/main/java/forge/card/CardRules.java @@ -53,6 +53,7 @@ public final class CardRules implements ICardCharacteristics { private String meldWith; private String partnerWith; private boolean addsWildCardColor; + private int setColorID; private boolean custom; public CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) { @@ -72,6 +73,8 @@ public final class CardRules implements ICardCharacteristics { meldWith = ""; partnerWith = ""; addsWildCardColor = false; + setColorID = 0; + //calculate color identity byte colMask = calculateColorIdentity(mainPart); @@ -95,6 +98,7 @@ public final class CardRules implements ICardCharacteristics { meldWith = newRules.meldWith; partnerWith = newRules.partnerWith; addsWildCardColor = newRules.addsWildCardColor; + setColorID = newRules.setColorID; tokens = newRules.tokens; } @@ -397,6 +401,10 @@ public final class CardRules implements ICardCharacteristics { return addsWildCardColor; } + public int getSetColorID() { + return setColorID; + } + // vanguard card fields, they don't use sides. private int deltaHand; private int deltaLife; @@ -448,6 +456,7 @@ public final class CardRules implements ICardCharacteristics { private String meldWith = ""; private String partnerWith = ""; private boolean addsWildCardColor = false; + private int setColorID = 0; private String handLife = null; private String normalizedName = ""; private Set supportedFunctionalVariants = null; @@ -512,6 +521,7 @@ public final class CardRules implements ICardCharacteristics { result.meldWith = this.meldWith; result.partnerWith = this.partnerWith; result.addsWildCardColor = this.addsWildCardColor; + result.setColorID = this.setColorID; if (!tokens.isEmpty()) { result.tokens = tokens; } @@ -687,6 +697,8 @@ public final class CardRules implements ICardCharacteristics { value = colonPos > 0 ? value.substring(1+colonPos) : null; face.addSVar(variable, value); + } else if (key.startsWith("SETCOLORID")) { + this.setColorID = Integer.parseInt(value); } break; diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index 24e7939df0b..b81f3825266 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -164,9 +164,9 @@ public class PaperCard implements Comparable, InventoryItemFromSet, this.artIndex, this.foil, String.valueOf(collectorNumber), this.artist, this.functionalVariant, false); return sellable; } - public PaperCard getSpireVersion(Set colors) { + public PaperCard getColorIDVersion(Set colors) { return new PaperCard(this.rules, this.edition, this.rarity, - this.artIndex, this.foil, String.valueOf(collectorNumber), this.artist, this.functionalVariant, false, colors); + this.artIndex, this.foil, String.valueOf(collectorNumber), this.artist, this.functionalVariant, this.noSell, colors); } @Override public String getItemType() { diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java index 9771ac7bf1b..4f966a099cc 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java @@ -580,17 +580,18 @@ public abstract class ACEditorBase 0) return; GuiUtils.addMenuItem(menu, label, null, () -> { - Set colors = new HashSet<>(GuiChoose.getChoices(localizer.getMessage("lblChooseNColors", Lang.getNumeral(2)), 2, 2, MagicColor.Constant.ONLY_COLORS)); + Set colors = new HashSet<>(GuiChoose.getChoices(localizer.getMessage("lblChooseNColors", Lang.getNumeral(val)), val, val, MagicColor.Constant.ONLY_COLORS)); // make an updated version - PaperCard updated = existingCard.getSpireVersion(colors); + PaperCard updated = existingCard.getColorIDVersion(colors); // remove *quantity* instances of existing card CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, 1); // add *quantity* into the deck and set them as selected diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java index 8d5c7080d04..f3be724aab0 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CEditorConstructed.java @@ -375,7 +375,7 @@ public final class CEditorConstructed extends CDeckEditor { if (foilAvailable) { cmb.addMakeFoils(); } - cmb.addSetColorSpire(); + cmb.addSetColorID(); } /* (non-Javadoc) diff --git a/forge-gui-mobile/src/forge/deck/FDeckEditor.java b/forge-gui-mobile/src/forge/deck/FDeckEditor.java index 1b4c58df7b9..8578e757cd5 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckEditor.java +++ b/forge-gui-mobile/src/forge/deck/FDeckEditor.java @@ -1800,6 +1800,8 @@ public class FDeckEditor extends TabPageScreen { CardManagerPage cardSourceSection; DeckSection destination = DeckSection.matchingSection(card); final DeckSectionPage destinationPage = parentScreen.getPageForSection(destination); + // val for colorID setup + int val; switch (deckSection) { default: case Main: @@ -1842,14 +1844,14 @@ public class FDeckEditor extends TabPageScreen { } } addCommanderItems(menu, card); - if ("Cryptic Spires".equalsIgnoreCase(card.getCardName())) { + if ((val = card.getRules().getSetColorID()) > 0) { menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> { //sort options so current option is on top and selected by default Set colorChoices = new HashSet<>(MagicColor.Constant.ONLY_COLORS); - GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { + GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(val)), val, val, colorChoices, new Callback<>() { @Override public void run(List result) { - addCard(card.getSpireVersion(new HashSet<>(result))); + addCard(card.getColorIDVersion(new HashSet<>(result))); removeCard(card); } }); @@ -1894,14 +1896,14 @@ public class FDeckEditor extends TabPageScreen { } } addCommanderItems(menu, card); - if ("Cryptic Spires".equalsIgnoreCase(card.getCardName())) { + if ((val = card.getRules().getSetColorID()) > 0) { menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> { //sort options so current option is on top and selected by default Set colorChoices = new HashSet<>(MagicColor.Constant.ONLY_COLORS); - GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(2)), 2, 2, colorChoices, new Callback<>() { + GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(val)), val, val, colorChoices, new Callback<>() { @Override public void run(List result) { - addCard(card.getSpireVersion(new HashSet<>(result))); + addCard(card.getColorIDVersion(new HashSet<>(result))); removeCard(card); } }); diff --git a/forge-gui/res/cardsfolder/c/cryptic_spires.txt b/forge-gui/res/cardsfolder/c/cryptic_spires.txt index aff3fb69fe0..d7031b33257 100644 --- a/forge-gui/res/cardsfolder/c/cryptic_spires.txt +++ b/forge-gui/res/cardsfolder/c/cryptic_spires.txt @@ -6,3 +6,4 @@ R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | ReplacementRe SVar:ETBTapped:DB$ Tap | Defined$ Self | ETB$ True A:AB$ Mana | Cost$ T | Produced$ Combo ColorID | SpellDescription$ Add one mana of either of the circled colors. Oracle:As you create your deck, circle two of the colors below.\nCryptic Spires enters tapped.\n{T}: Add one mana of either of the circled colors. +SETCOLORID:2 From 4248d1930ef5a34f2b00e31116e92d5755b2af00 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 11:54:34 +0800 Subject: [PATCH 015/152] Update snapshots-android.yml --- .github/workflows/snapshots-android.yml | 27 ++++--------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/.github/workflows/snapshots-android.yml b/.github/workflows/snapshots-android.yml index 04fdf794e11..e59b5744d21 100644 --- a/.github/workflows/snapshots-android.yml +++ b/.github/workflows/snapshots-android.yml @@ -89,31 +89,12 @@ jobs: - name: Build/Install/Publish to GitHub Packages Apache Maven run: | export _JAVA_OPTIONS="-Xmx2g" - d=$(date +%m.%d) - # Replace date in forge-gui-mobile/src/forge/Forge.java - # sed -i -e "s/-SNAPSHOT/-SNAPSHOT-${d}/g" forge-gui-mobile/src/forge/Forge.java mvn -U -B -P android-release-build install -e -Dcardforge-repo.username=${{ secrets.FTP_USERNAME }} -Dcardforge-repo.password=${{ secrets.FTP_PASSWORD }} -Dandroid.sdk.path=/usr/local/lib/android/sdk -Dandroid.buildToolsVersion=35.0.0 -Dmaven.test.skip=true - mkdir -p forge-gui-android/target/upload - mv forge-gui-android/target/*-signed-aligned.apk forge-gui-android/target/upload/ - mv forge-gui-android/target/assets.zip forge-gui-android/target/upload/ - cd forge-gui-android/target/upload/ - # Get the first APK file in the folder + mkdir upload + mv /home/runner/work/forge/forge/forge-gui-android/target/*-signed-aligned.apk upload/ + mv /home/runner/work/forge/forge/forge-gui-android/target/assets.zip upload/ + cd upload ls - apk_file=$(find . -maxdepth 1 -type f -name '*.apk' -print -quit) - - if [ -n "$apk_file" ]; then - version=$(echo "$apk_file" | grep -oP 'forge-android-\K\d+\.\d+\.\d+-SNAPSHOT' | sed 's/-signed-aligned.apk//') - echo "APK File: $apk_file" - echo "Version: $version" - # mv *.apk "forge-android-$version-$d-signed-aligned.apk" - - echo "$version-$d" > version.txt - else - echo "No .apk files found in the specified folder." - fi - - cd - - env: GITHUB_TOKEN: ${{ github.token }} From bf24813aa79d401ab4068f68712607f8e754d19b Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 11:59:14 +0800 Subject: [PATCH 016/152] update local-dir --- .github/workflows/snapshots-android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snapshots-android.yml b/.github/workflows/snapshots-android.yml index e59b5744d21..d1fdb04adb9 100644 --- a/.github/workflows/snapshots-android.yml +++ b/.github/workflows/snapshots-android.yml @@ -105,6 +105,6 @@ jobs: server: ftp.cardforge.org username: ${{ secrets.FTP_USERNAME }} password: ${{ secrets.FTP_PASSWORD }} - local-dir: forge-gui-android/target/upload/ + local-dir: upload/ server-dir: downloads/dailysnapshots/ state-name: .ftp-deploy-android-sync-state.json From 80215206922366225402307ae0be4fd515d438d6 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Thu, 14 Nov 2024 07:09:21 +0100 Subject: [PATCH 017/152] moved GameEventCardForetold and GameEventCardPlotted to GameLogFormatter --- .../src/main/java/forge/game/GameLogFormatter.java | 11 +++++++++++ .../main/java/forge/game/card/CardFactoryUtil.java | 4 ---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameLogFormatter.java b/forge-game/src/main/java/forge/game/GameLogFormatter.java index 4599f4c17b3..911cd2e7f37 100644 --- a/forge-game/src/main/java/forge/game/GameLogFormatter.java +++ b/forge-game/src/main/java/forge/game/GameLogFormatter.java @@ -306,6 +306,17 @@ public class GameLogFormatter extends IGameEventVisitor.Base { return new GameLogEntry(GameLogEntryType.MULLIGAN, message); } + @Override + public GameLogEntry visit(GameEventCardForetold ev) { + String sb = TextUtil.concatWithSpace(ev.activatingPlayer.toString(), "has foretold."); + return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, sb); + } + + @Override + public GameLogEntry visit(GameEventCardPlotted ev) { + return new GameLogEntry(GameLogEntryType.STACK_RESOLVE, ev.toString()); + } + @Subscribe public void recieve(GameEvent ev) { GameLogEntry le = ev.visit(this); diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index be1af91202f..4f9cc1b0551 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -3101,8 +3101,6 @@ public class CardFactoryUtil { // because it doesn't work other wise c.setForetoldCostByEffect(true); } - String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(), "has foretold."); - game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb); game.fireEvent(new GameEventCardForetold(getActivatingPlayer())); } }; @@ -3425,8 +3423,6 @@ public class CardFactoryUtil { c.setPlotted(true); - String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(), "has plotted", c.toString()); - game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb); game.fireEvent(new GameEventCardPlotted(c, getActivatingPlayer())); } }; From 88ca1d509f9515914167948d2e63742de6f321f5 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 15:28:43 +0800 Subject: [PATCH 018/152] Update VAssignGenericAmount.java support double tapping the mana symbols for max amount --- .../match/views/VAssignGenericAmount.java | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/forge-gui-mobile/src/forge/screens/match/views/VAssignGenericAmount.java b/forge-gui-mobile/src/forge/screens/match/views/VAssignGenericAmount.java index ca738800da4..9fc02058b6a 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VAssignGenericAmount.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VAssignGenericAmount.java @@ -70,7 +70,6 @@ public class VAssignGenericAmount extends FDialog { /** Constructor. * - * @param attacker0 {@link forge.game.card.Card} * @param targets Map, map of GameEntity and its maximum assignable amount * @param amount Total amount to be assigned * @param atLeastOne Must assign at least one amount to each target @@ -168,7 +167,7 @@ public class VAssignGenericAmount extends FDialog { obj = add(new EffectSourcePanel((CardView)entity)); } else if (entity instanceof PlayerView) { PlayerView player = (PlayerView)entity; - obj = add(new MiscTargetPanel(player.getName(), MatchController.getPlayerAvatar(player))); + obj = add(new MiscTargetPanel(player.getName(), MatchController.getPlayerAvatar(player), null)); } else if (entity instanceof Byte) { FSkinImageInterface manaSymbol; byte color = (Byte) entity; @@ -185,9 +184,9 @@ public class VAssignGenericAmount extends FDialog { } else { // Should never come here, but add this to avoid compile error manaSymbol = Forge.getAssets().images().get(FSkinProp.IMG_MANA_COLORLESS); } - obj = add(new MiscTargetPanel("", manaSymbol)); + obj = add(new MiscTargetPanel("", manaSymbol, entity)); } else { - obj = add(new MiscTargetPanel(entity.toString(), FSkinImage.UNKNOWN)); + obj = add(new MiscTargetPanel(entity.toString(), FSkinImage.UNKNOWN, null)); } label = add(new FLabel.Builder().text("0").font(FSkinFont.get(18)).align(Align.center).build()); btnSubtract = add(new FLabel.ButtonBuilder().icon(FSkinImage.MINUS).command(e -> assignAmountTo(entity, false)).build()); @@ -232,19 +231,21 @@ public class VAssignGenericAmount extends FDialog { } } - private static class MiscTargetPanel extends FDisplayObject { - private static final FSkinFont FONT = FSkinFont.get(18); - private static FSkinColor getForeColor() { + private class MiscTargetPanel extends FDisplayObject { + private final FSkinFont FONT = FSkinFont.get(18); + private FSkinColor getForeColor() { if (Forge.isMobileAdventureMode) return FSkinColor.get(Colors.ADV_CLR_TEXT); return FSkinColor.get(Colors.CLR_TEXT); } private final String name; private final FImage image; + private final Object entity; - private MiscTargetPanel(String name0, FImage image0) { + private MiscTargetPanel(String name0, FImage image0, Object entity0) { name = name0; image = image0; + entity = entity0; } @Override @@ -254,6 +255,24 @@ public class VAssignGenericAmount extends FDialog { g.drawImage(image, 0, 0, w, w); g.drawText(name, FONT, getForeColor(), 0, w, w, h - w, false, Align.center, true); } + + @Override + public boolean tap(float x, float y, int count) { + if (count > 1 && entity != null) { + AssignTarget at = targetsMap.get(entity); + int assigned = at.amount; + int leftToAssign = Math.max(0, at.max - assigned); + int amountToAdd = Math.min(getRemainingAmount(), leftToAssign); + + if (0 == amountToAdd || amountToAdd + assigned < 0) { + return false; + } + + addAssignedAmount(at, amountToAdd); + updateLabels(); + } + return super.tap(x, y, count); + } } private void assignAmountTo(Object source, boolean isAdding) { From 4ed7cb30d87653ebf11f3f8c499366b406ad3b49 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 15:51:11 +0800 Subject: [PATCH 019/152] Update SColumnUtil.java add adventure default columns --- forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java b/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java index ff0328f4e28..b2309f45346 100644 --- a/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java +++ b/forge-gui/src/main/java/forge/itemmanager/SColumnUtil.java @@ -143,6 +143,13 @@ public final class SColumnUtil { return columns; } + public static Map getAdventureCollectionDefaultColumns() { + Map columns = getCardColumns(ColumnDef.QUANTITY, false, false, false, true, false); + columns.get(ColumnDef.NEW).setSortPriority(1); + columns.get(ColumnDef.NAME).setSortPriority(2); + return columns; + } + public static Map getAttractionPoolDefaultColumns() { //Similar to special card pool, but show the collector number and hide the type. List colDefs = new ArrayList<>(); From e0d8b24412e764468a137a80754e3f3511581cf6 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 15:52:18 +0800 Subject: [PATCH 020/152] Update ItemManagerConfig.java use adventure default columns --- .../src/main/java/forge/itemmanager/ItemManagerConfig.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/forge-gui/src/main/java/forge/itemmanager/ItemManagerConfig.java b/forge-gui/src/main/java/forge/itemmanager/ItemManagerConfig.java index 685f1d6deda..7f4adf97539 100644 --- a/forge-gui/src/main/java/forge/itemmanager/ItemManagerConfig.java +++ b/forge-gui/src/main/java/forge/itemmanager/ItemManagerConfig.java @@ -119,9 +119,9 @@ public enum ItemManagerConfig { null, null, 3, 0), NET_ARCHIVE_BLOCK_DECKS(SColumnUtil.getDecksDefaultColumns(false, false), false, false, false, null, null, 3, 0), - ADVENTURE_EDITOR_POOL(SColumnUtil.getConquestCollectionDefaultColumns(), false, false, false, + ADVENTURE_EDITOR_POOL(SColumnUtil.getAdventureCollectionDefaultColumns(), false, false, false, null, null, 6, 0), - ADVENTURE_STORE_POOL(SColumnUtil.getConquestCollectionDefaultColumns(), false, false, true, + ADVENTURE_STORE_POOL(SColumnUtil.getAdventureCollectionDefaultColumns(), false, false, true, null, null, 6, 0), ADVENTURE_SIDEBOARD(SColumnUtil.getDeckEditorDefaultColumns(), false, false, true, null, null, 6, 0), @@ -376,4 +376,4 @@ public enum ItemManagerConfig { e.printStackTrace(); } } -} \ No newline at end of file +} From 919832cc06c4d2445f1cb4bf869b1d7f4c29a3ca Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 14 Nov 2024 16:11:14 +0800 Subject: [PATCH 021/152] Update pom.xml remove duplicate plugin --- forge-gui-desktop/pom.xml | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml index 1aeb7a74877..6bd211ca954 100644 --- a/forge-gui-desktop/pom.xml +++ b/forge-gui-desktop/pom.xml @@ -54,6 +54,19 @@ false + + released-version + validate + + released-version + + + + parse-version + + parse-version + + @@ -170,26 +183,6 @@ - - org.codehaus.mojo - build-helper-maven-plugin - 3.6.0 - - - released-version - validate - - released-version - - - - parse-version - - parse-version - - - - se.bjurr.gitchangelog git-changelog-maven-plugin From ac96890f44757b3b27f524ba98821700399eaa0c Mon Sep 17 00:00:00 2001 From: tool4ever Date: Thu, 14 Nov 2024 10:34:05 +0100 Subject: [PATCH 022/152] Some fixes (#6575) --- .../src/main/java/forge/game/GameAction.java | 2 +- .../java/forge/game/ability/AbilityUtils.java | 14 ++++++++++--- .../java/forge/game/cost/ICostVisitor.java | 5 ++++- .../game/replacement/ReplacementHandler.java | 1 + .../screens/match/controllers/CCombat.java | 3 +-- .../res/cardsfolder/i/infernal_vessel.txt | 2 +- .../k/king_of_the_oathbreakers.txt | 2 +- .../res/cardsfolder/n/needletooth_pack.txt | 2 +- .../res/cardsfolder/t/the_mindskinner.txt | 2 +- .../java/forge/gamemodes/match/GameLobby.java | 20 +++++++------------ 10 files changed, 29 insertions(+), 24 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index e1ccedf9df1..a8b6930717c 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -2456,7 +2456,7 @@ public class GameAction { game.getAction().reveal(milledPlayer, destination, p, false, message, addSuffix); } game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, p + " milled " + - Lang.joinHomogenous(milled) + toZoneStr + "."); + Lang.joinHomogenous(milledPlayer) + toZoneStr + "."); } } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 6f3475832fe..d0e727a2bed 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1640,9 +1640,15 @@ public class AbilityUtils { return doXMath(calculateAmount(c, sq[v ? 1 : 2], ctb), expr, c, ctb); } + SpellAbility sa = null; if (ctb instanceof SpellAbility) { - final SpellAbility sa = (SpellAbility) ctb; + sa = (SpellAbility) ctb; + } else if (sq[0].contains("xPaid") && ctb instanceof TriggerReplacementBase) { + // try avoid fallback + sa = ((TriggerReplacementBase) ctb).getOverridingAbility(); + } + if (sa != null) { // special logic for xPaid in SpellAbility if (sq[0].contains("xPaid")) { SpellAbility root = sa.getRootAbility(); @@ -1672,7 +1678,8 @@ public class AbilityUtils { // and the spell that became that object as it resolved had a value of X chosen for any of its costs, // the value of X for that ability is the same as the value of X for that spell, although the value of X for that permanent is 0. if (TriggerType.ChangesZone.equals(t.getMode()) && ZoneType.Battlefield.name().equals(t.getParam("Destination"))) { - return doXMath(c.getXManaCostPaid(), expr, c, ctb); + int x = isUnlinkedFromCastSA(ctb, c) ? 0 : c.getXManaCostPaid(); + return doXMath(x, expr, c, ctb); } else if (TriggerType.SpellCast.equals(t.getMode())) { // Cast Trigger like Hydroid Krasis SpellAbilityStackInstance castSI = (SpellAbilityStackInstance) root.getTriggeringObject(AbilityKey.StackInstance); @@ -1696,7 +1703,8 @@ public class AbilityUtils { } if (root.isReplacementAbility() && sa.hasParam("ETB")) { - return doXMath(c.getXManaCostPaid(), expr, c, ctb); + int x = isUnlinkedFromCastSA(ctb, c) ? 0 : c.getXManaCostPaid(); + return doXMath(x, expr, c, ctb); } return doXMath(0, expr, c, ctb); diff --git a/forge-game/src/main/java/forge/game/cost/ICostVisitor.java b/forge-game/src/main/java/forge/game/cost/ICostVisitor.java index 1ee90baa331..190e1c806ce 100644 --- a/forge-game/src/main/java/forge/game/cost/ICostVisitor.java +++ b/forge-game/src/main/java/forge/game/cost/ICostVisitor.java @@ -105,6 +105,7 @@ public interface ICostVisitor { public T visit(CostFlipCoin cost) { return null; } + @Override public T visit(CostForage cost) { return null; @@ -146,7 +147,9 @@ public interface ICostVisitor { } @Override - public T visit(CostPromiseGift cost) { return null; } + public T visit(CostPromiseGift cost) { + return null; + } @Override public T visit(CostPutCardToLib cost) { diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index cd28fa4316a..75262669cfe 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -665,6 +665,7 @@ public class ReplacementHandler { } List possibleReplacers = new ArrayList<>(replaceCandidateMap.keySet()); + // TODO should be able to choose different order for each entity ReplacementEffect chosenRE = decider.getController().chooseSingleReplacementEffect(possibleReplacers); List> runParamList = replaceCandidateMap.get(chosenRE); diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CCombat.java b/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CCombat.java index 750d7403a42..9ee50bc9fc1 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CCombat.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/controllers/CCombat.java @@ -78,9 +78,8 @@ public class CCombat implements ICDoc { } display.append("\n"); - PlayerView controller = null; if (defender instanceof CardView) { - controller = ((CardView) defender).getController(); + PlayerView controller = ((CardView) defender).getController(); if (controller == null) //shouldn't be null but display card's + controller ie Black Knight's controller display.append(Lang.getInstance().getPossesive(defender.getName())).append(" controller"); diff --git a/forge-gui/res/cardsfolder/i/infernal_vessel.txt b/forge-gui/res/cardsfolder/i/infernal_vessel.txt index 8aec8a859df..064ca619fab 100644 --- a/forge-gui/res/cardsfolder/i/infernal_vessel.txt +++ b/forge-gui/res/cardsfolder/i/infernal_vessel.txt @@ -2,7 +2,7 @@ Name:Infernal Vessel ManaCost:2 B Types:Creature Human Cleric PT:2/1 -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+notDemon | Execute$ TrigReturn | TriggerDescription$ When this creature dies, if it wasn't a Demon, return it to the battlefield under its owner's control with two +1/+1 counters on it. It's a Demon in addition to its other types. +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+nonDemon | Execute$ TrigReturn | TriggerDescription$ When this creature dies, if it wasn't a Demon, return it to the battlefield under its owner's control with two +1/+1 counters on it. It's a Demon in addition to its other types. SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredNewCardLKICopy | WithCountersType$ P1P1 | WithCountersAmount$ 2 | AnimateSubAbility$ DBAnimate SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Demon | Duration$ Permanent Oracle:When this creature dies, if it wasn't a Demon, return it to the battlefield under its owner's control with two +1/+1 counters on it. It's a Demon in addition to its other types. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/k/king_of_the_oathbreakers.txt b/forge-gui/res/cardsfolder/k/king_of_the_oathbreakers.txt index 9e400b406ba..4a93802da32 100644 --- a/forge-gui/res/cardsfolder/k/king_of_the_oathbreakers.txt +++ b/forge-gui/res/cardsfolder/k/king_of_the_oathbreakers.txt @@ -3,7 +3,7 @@ ManaCost:2 W B Types:Legendary Creature Spirit Noble PT:3/3 K:Flying -T:Mode$ BecomesTarget | ValidTarget$ Card.Self,Spirit.YouCtrl+Other | ValidSource$ Spell | TriggerZones$ Battlefield | Execute$ TrigPhaseOut | TriggerDescription$ Whenever CARDNAME or another Spirit you control becomes the target of a spell, it phases out. (Treat it and anything attached to it as though they don't exist until your next turn.) +T:Mode$ BecomesTarget | ValidTarget$ Card.Self,Spirit.YouCtrl+Other+inZoneBattlefield | ValidSource$ Spell | TriggerZones$ Battlefield | Execute$ TrigPhaseOut | TriggerDescription$ Whenever CARDNAME or another Spirit you control becomes the target of a spell, it phases out. (Treat it and anything attached to it as though they don't exist until your next turn.) SVar:TrigPhaseOut:DB$ Phases | Defined$ TriggeredTargetLKICopy T:Mode$ PhaseIn | ValidCard$ Card.Self,Spirit.YouCtrl+Other | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME or another Spirit you control phases in, create a tapped 1/1 white Spirit creature token with flying. SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_spirit_flying | TokenOwner$ You | TokenTapped$ True diff --git a/forge-gui/res/cardsfolder/n/needletooth_pack.txt b/forge-gui/res/cardsfolder/n/needletooth_pack.txt index 4c7b222a5c3..81e7abaf07b 100644 --- a/forge-gui/res/cardsfolder/n/needletooth_pack.txt +++ b/forge-gui/res/cardsfolder/n/needletooth_pack.txt @@ -2,7 +2,7 @@ Name:Needletooth Pack ManaCost:3 G G Types:Creature Dinosaur PT:4/5 -T:Mode$ Phase | Phase$ End of Turn | CheckSVar$ Morbid | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, put two +1/+1 counters on target creature you control. +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | CheckSVar$ Morbid | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, put two +1/+1 counters on target creature you control. SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 2 SVar:Morbid:Count$Morbid.1.0 Oracle:Morbid — At the beginning of your end step, if a creature died this turn, put two +1/+1 counters on target creature you control. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/t/the_mindskinner.txt b/forge-gui/res/cardsfolder/t/the_mindskinner.txt index fce38f7d439..74bcacb712a 100644 --- a/forge-gui/res/cardsfolder/t/the_mindskinner.txt +++ b/forge-gui/res/cardsfolder/t/the_mindskinner.txt @@ -3,7 +3,7 @@ ManaCost:U U U Types:Legendary Enchantment Creature Nightmare PT:10/1 S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked. -R:Event$ DamageDone | ActiveZones$ Battlefield | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent | ReplaceWith$ Mill | PreventionEffect$ True | ExecuteMode$ PerTarget | Description$ If a source you control would deal damage to an opponent, prevent that damage and each opponent mills that many cards. +R:Event$ DamageDone | ActiveZones$ Battlefield | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent | ReplaceWith$ Mill | PreventionEffect$ True | ExecuteMode$ PerSource | Description$ If a source you control would deal damage to an opponent, prevent that damage and each opponent mills that many cards. SVar:Mill:DB$ Mill | Defined$ Opponent | NumCards$ X SVar:X:ReplaceCount$DamageAmount Oracle:The Mindskinner can't be blocked.\nIf a source you control would deal damage to an opponent, prevent that damage and each opponent mills that many cards. diff --git a/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java b/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java index c92bc865263..340a9633b46 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/GameLobby.java @@ -443,17 +443,13 @@ public abstract class GameLobby implements IHasGameType { Deck deck = slot.getDeck(); RegisteredPlayer rp = new RegisteredPlayer(deck); - if (variantTypes.isEmpty()) { - rp.setTeamNumber(team); - players.add(rp.setPlayer(lobbyPlayer)); - } - else { + if (!variantTypes.isEmpty()) { if (isCommanderMatch) { final GameType commanderGameType = isOathbreakerMatch ? GameType.Oathbreaker : - isTinyLeadersMatch ? GameType.TinyLeaders : - isBrawlMatch ? GameType.Brawl : - GameType.Commander; + isTinyLeadersMatch ? GameType.TinyLeaders : + isBrawlMatch ? GameType.Brawl : + GameType.Commander; if (checkLegality) { final String errMsg = commanderGameType.getDeckFormat().getDeckConformanceProblem(deck); if (errMsg != null) { @@ -481,7 +477,6 @@ public abstract class GameLobby implements IHasGameType { Iterable schemes = null; Iterable planes = null; - //Archenemy if (variantTypes.contains(GameType.ArchenemyRumble) || (variantTypes.contains(GameType.Archenemy) && isArchenemy)) { final CardPool schemePool = deck.get(DeckSection.Schemes); @@ -495,7 +490,6 @@ public abstract class GameLobby implements IHasGameType { schemes = schemePool == null ? Collections.emptyList() : schemePool.toFlatList(); } - //Planechase if (variantTypes.contains(GameType.Planechase)) { final CardPool planePool = deck.get(DeckSection.Planes); if (checkLegality) { @@ -508,7 +502,6 @@ public abstract class GameLobby implements IHasGameType { planes = planePool == null ? Collections.emptyList() : planePool.toFlatList(); } - //Vanguard if (variantTypes.contains(GameType.Vanguard)) { if (avatarPool == null || avatarPool.countAll() == 0) { //ERROR! null if avatar deselected on list SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblNoSelectedVanguardAvatarForPlayer", name)); @@ -517,10 +510,11 @@ public abstract class GameLobby implements IHasGameType { } rp = RegisteredPlayer.forVariants(activeSlots.size(), variantTypes, deck, schemes, isArchenemy, planes, avatarPool); - rp.setTeamNumber(team); - players.add(rp.setPlayer(lobbyPlayer)); } + rp.setTeamNumber(team); + players.add(rp.setPlayer(lobbyPlayer)); + if (!isAI) { guis.put(rp, gui); } From e2c37d11e711a97e4d32b9e20b7ac4288595f574 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 14 Nov 2024 19:11:25 +0800 Subject: [PATCH 023/152] AI Attack Timeout --- .../java/forge/ai/AiAttackController.java | 288 +++++++------- .../src/main/java/forge/ai/AiController.java | 182 +++++---- .../main/java/forge/ai/AiCostDecision.java | 5 +- .../src/main/java/forge/ai/ComputerUtil.java | 24 +- .../main/java/forge/ai/ComputerUtilMana.java | 28 +- .../java/forge/ai/PlayerControllerAi.java | 12 +- .../src/main/java/forge/ai/SpecialCardAi.java | 3 +- .../main/java/forge/ai/ability/CharmAi.java | 6 +- .../java/forge/ai/ability/ChooseCardAi.java | 4 +- .../forge/ai/ability/ChooseCompanionAi.java | 4 +- .../main/java/forge/ai/ability/DiscardAi.java | 4 +- .../ai/simulation/SimulationController.java | 4 +- .../src/main/java/forge/StaticData.java | 3 +- .../src/main/java/forge/card/CardDb.java | 3 +- .../src/main/java/forge/card/CardEdition.java | 4 +- .../src/main/java/forge/deck/CardPool.java | 3 +- .../item/generation/BoosterGenerator.java | 3 +- .../main/java/forge/util/CollectionUtil.java | 65 ++++ .../java/forge/util/collect/FCollection.java | 134 ++++--- forge-game/src/main/java/forge/game/Game.java | 5 +- .../src/main/java/forge/game/GameAction.java | 6 +- .../forge/game/ability/effects/DigEffect.java | 5 +- .../ability/effects/DigMultipleEffect.java | 4 +- .../game/ability/effects/DigUntilEffect.java | 4 +- .../game/ability/effects/DraftEffect.java | 3 +- .../ability/effects/ReorderZoneEffect.java | 4 +- .../main/java/forge/game/card/CardLists.java | 3 +- .../java/forge/game/card/CardProperty.java | 7 +- .../main/java/forge/game/combat/Combat.java | 337 ++++++++++------ .../java/forge/game/cost/CostPayment.java | 4 +- .../main/java/forge/game/mana/ManaPool.java | 69 ++-- .../forge/game/mana/ManaRefundService.java | 4 +- .../main/java/forge/game/player/Player.java | 4 +- .../forge/game/spellability/SpellAbility.java | 359 +++++++++++++----- .../src/main/java/forge/game/zone/Zone.java | 4 +- .../java/forge/itemmanager/CardManager.java | 3 +- .../controllers/CProbabilities.java | 4 +- .../screens/home/quest/DialogChooseSets.java | 3 +- .../home/sanctioned/CSubmenuDraft.java | 4 +- .../main/java/forge/view/SimulateMatch.java | 4 +- .../src/main/java/forge/deck/DeckgenUtil.java | 6 +- .../java/forge/deck/NetDeckArchiveBlock.java | 3 +- .../java/forge/deck/NetDeckArchiveLegacy.java | 3 +- .../java/forge/deck/NetDeckArchiveModern.java | 3 +- .../java/forge/deck/NetDeckArchivePauper.java | 3 +- .../forge/deck/NetDeckArchivePioneer.java | 3 +- .../forge/deck/NetDeckArchiveStandard.java | 3 +- .../forge/deck/NetDeckArchiveVintage.java | 3 +- .../forge/gamemodes/limited/BoosterDraft.java | 5 +- .../limited/CardThemedDeckBuilder.java | 6 +- .../gamemodes/limited/LimitedPlayer.java | 3 +- .../gamemodes/limited/LimitedPlayerAI.java | 8 +- .../limited/SealedCardPoolGenerator.java | 3 +- .../forge/gamemodes/limited/WinstonDraft.java | 4 +- .../planarconquest/ConquestData.java | 3 +- .../forge/gamemodes/quest/BoosterUtils.java | 6 +- .../quest/MainWorldEventDuelManager.java | 4 +- .../gamemodes/quest/QuestController.java | 4 +- .../quest/QuestEventCommanderDuelManager.java | 4 +- .../gamemodes/quest/QuestEventDraft.java | 7 +- .../quest/QuestEventDuelManager.java | 4 +- .../quest/QuestEventLDADuelManager.java | 4 +- .../forge/gamemodes/quest/QuestUtilCards.java | 4 +- .../gamemodes/quest/QuestUtilUnlockSets.java | 9 +- .../setrotation/QueueRandomRotation.java | 4 +- .../tournament/system/AbstractTournament.java | 4 +- .../tournament/system/TournamentSwiss.java | 4 +- .../main/java/forge/itemmanager/GroupDef.java | 3 +- .../java/forge/player/HumanCostDecision.java | 2 +- .../forge/player/PlayerControllerHuman.java | 4 +- 70 files changed, 1128 insertions(+), 617 deletions(-) create mode 100644 forge-core/src/main/java/forge/util/CollectionUtil.java diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 88c05099c95..d4975a41bd9 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -50,6 +50,10 @@ import forge.util.collect.FCollectionView; import org.apache.commons.lang3.tuple.Pair; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; /** @@ -169,7 +173,7 @@ public class AiAttackController { } public void removeBlocker(Card blocker) { - this.oppList.remove(blocker); + this.oppList.remove(blocker); this.blockers.remove(blocker); } @@ -286,7 +290,7 @@ public class AiAttackController { } if ("TRUE".equals(attacker.getSVar("HasAttackEffect"))) { - return true; + return true; } // Damage opponent if unblocked @@ -388,7 +392,7 @@ public class AiAttackController { // reduce the search space final List opponentsAttackers = CardLists.filter(ai.getOpponents().getCreaturesInPlay(), c -> !c.hasSVar("EndOfTurnLeavePlay") && (c.toughnessAssignsDamage() || c.getNetCombatDamage() > 0 // performance shortcuts - || c.getNetCombatDamage() + ComputerUtilCombat.predictPowerBonusOfAttacker(c, null, null, true) > 0) + || c.getNetCombatDamage() + ComputerUtilCombat.predictPowerBonusOfAttacker(c, null, null, true) > 0) && ComputerUtilCombat.canAttackNextTurn(c)); // don't hold back creatures that can't block any of the human creatures @@ -884,7 +888,7 @@ public class AiAttackController { // TODO: detect Season of the Witch by presence of a card with a specific trigger final boolean seasonOfTheWitch = ai.getGame().isCardInPlay("Season of the Witch"); - List attackersLeft = new ArrayList<>(this.attackers); + final Queue attackersLeft = new ConcurrentLinkedQueue<>(this.attackers); // TODO probably use AttackConstraints instead of only GlobalAttackRestrictions? GlobalAttackRestrictions restrict = GlobalAttackRestrictions.getGlobalRestrictions(ai, combat.getDefenders()); @@ -900,63 +904,71 @@ public class AiAttackController { } // Attackers that don't really have a choice - int numForcedAttackers = 0; + final AtomicInteger numForcedAttackers = new AtomicInteger(0); // nextTurn is now only used by effect from Oracle en-Vec, which can skip check must attack, // because creatures not chosen can't attack. + List> futures = new ArrayList<>(); if (!nextTurn) { for (final Card attacker : this.attackers) { - GameEntity mustAttackDef = null; - if (attacker.getSVar("MustAttack").equals("True")) { - mustAttackDef = defender; - } else if (attacker.hasSVar("EndOfTurnLeavePlay") - && isEffectiveAttacker(ai, attacker, combat, defender)) { - mustAttackDef = defender; - } else if (seasonOfTheWitch) { - //TODO: if there are other ways to tap this creature (like mana creature), then don't need to attack - mustAttackDef = defender; - } else { - if (combat.getAttackConstraints().getRequirements().get(attacker) == null) continue; - // check defenders in order of maximum requirements - List> reqs = combat.getAttackConstraints().getRequirements().get(attacker).getSortedRequirements(); - final GameEntity def = defender; - reqs.sort((r1, r2) -> { - if (r1.getValue() == r2.getValue()) { - // try to attack the designated defender - if (r1.getKey().equals(def) && !r2.getKey().equals(def)) { - return -1; + final GameEntity finalDefender = defender; + futures.add(CompletableFuture.supplyAsync(()-> { + GameEntity mustAttackDef = null; + if (attacker.getSVar("MustAttack").equals("True")) { + mustAttackDef = finalDefender; + } else if (attacker.hasSVar("EndOfTurnLeavePlay") + && isEffectiveAttacker(ai, attacker, combat, finalDefender)) { + mustAttackDef = finalDefender; + } else if (seasonOfTheWitch) { + //TODO: if there are other ways to tap this creature (like mana creature), then don't need to attack + mustAttackDef = finalDefender; + } else { + if (combat.getAttackConstraints().getRequirements().get(attacker) == null) return 0; + // check defenders in order of maximum requirements + List> reqs = combat.getAttackConstraints().getRequirements().get(attacker).getSortedRequirements(); + final GameEntity def = finalDefender; + reqs.sort((r1, r2) -> { + if (r1.getValue() == r2.getValue()) { + // try to attack the designated defender + if (r1.getKey().equals(def) && !r2.getKey().equals(def)) { + return -1; + } + if (r2.getKey().equals(def) && !r1.getKey().equals(def)) { + return 1; + } + // otherwise PW + if (r1.getKey() instanceof Card && r2.getKey() instanceof Player) { + return -1; + } + if (r2.getKey() instanceof Card && r1.getKey() instanceof Player) { + return 1; + } + // or weakest player + if (r1.getKey() instanceof Player && r2.getKey() instanceof Player) { + return ((Player) r1.getKey()).getLife() - ((Player) r2.getKey()).getLife(); + } } - if (r2.getKey().equals(def) && !r1.getKey().equals(def)) { - return 1; + return r2.getValue() - r1.getValue(); + }); + for (Pair e : reqs) { + if (e.getRight() == 0) continue; + GameEntity mustAttackDefMaybe = e.getLeft(); + if (canAttackWrapper(attacker, mustAttackDefMaybe) && CombatUtil.getAttackCost(ai.getGame(), attacker, mustAttackDefMaybe) == null) { + mustAttackDef = mustAttackDefMaybe; + break; } - // otherwise PW - if (r1.getKey() instanceof Card && r2.getKey() instanceof Player) { - return -1; - } - if (r2.getKey() instanceof Card && r1.getKey() instanceof Player) { - return 1; - } - // or weakest player - if (r1.getKey() instanceof Player && r2.getKey() instanceof Player) { - return ((Player) r1.getKey()).getLife() - ((Player) r2.getKey()).getLife(); - } - } - return r2.getValue() - r1.getValue(); - }); - for (Pair e : reqs) { - if (e.getRight() == 0) continue; - GameEntity mustAttackDefMaybe = e.getLeft(); - if (canAttackWrapper(attacker, mustAttackDefMaybe) && CombatUtil.getAttackCost(ai.getGame(), attacker, mustAttackDefMaybe) == null) { - mustAttackDef = mustAttackDefMaybe; - break; } } - } - if (mustAttackDef != null) { - combat.addAttacker(attacker, mustAttackDef); - attackersLeft.remove(attacker); - numForcedAttackers++; - } + if (mustAttackDef != null) { + combat.addAttacker(attacker, mustAttackDef); + attackersLeft.remove(attacker); + numForcedAttackers.incrementAndGet(); + } + return 0; + })); } + CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); + CompletableFuture.allOf(futuresArray).completeOnTimeout(null, 5, TimeUnit.SECONDS).join(); + futures.clear(); if (attackersLeft.isEmpty()) { return aiAggression; } @@ -964,18 +976,19 @@ public class AiAttackController { // Lightmine Field: make sure the AI doesn't wipe out its own creatures if (lightmineField) { - doLightmineFieldAttackLogic(attackersLeft, numForcedAttackers, playAggro); + doLightmineFieldAttackLogic(attackersLeft, numForcedAttackers.get(), playAggro); } // Revenge of Ravens: make sure the AI doesn't kill itself and doesn't damage itself unnecessarily - if (!doRevengeOfRavensAttackLogic(defender, attackersLeft, numForcedAttackers, attackMax)) { + if (!doRevengeOfRavensAttackLogic(defender, attackersLeft, numForcedAttackers.get(), attackMax)) { return aiAggression; } if (bAssault && defender == defendingOpponent) { // in case we are forced to attack someone else if (LOG_AI_ATTACKS) System.out.println("Assault"); - CardLists.sortByPowerDesc(attackersLeft); - for (Card attacker : attackersLeft) { + List left = new ArrayList<>(attackersLeft); + CardLists.sortByPowerDesc(left); + for (Card attacker : left) { // reached max, breakup if (attackMax != -1 && combat.getAttackers().size() >= attackMax) return aiAggression; @@ -1227,7 +1240,7 @@ public class AiAttackController { if (ratioDiff > 0 && doAttritionalAttack) { aiAggression = 5; // attack at all costs } else if ((ratioDiff >= 1 && this.attackers.size() > 1 && (humanLifeToDamageRatio < 2 || outNumber > 0)) - || (playAggro && MyRandom.percentTrue(chanceToAttackToTrade) && humanLifeToDamageRatio > 1)) { + || (playAggro && MyRandom.percentTrue(chanceToAttackToTrade) && humanLifeToDamageRatio > 1)) { aiAggression = 4; // attack expecting to trade or damage player. } else if (MyRandom.percentTrue(chanceToAttackToTrade) && humanLifeToDamageRatio > 1 && defendingOpponent != null @@ -1237,7 +1250,7 @@ public class AiAttackController { && (ComputerUtilMana.getAvailableManaEstimate(ai) > 0) || tradeIfTappedOut && (ComputerUtilMana.getAvailableManaEstimate(defendingOpponent) == 0) || MyRandom.percentTrue(extraChanceIfOppHasMana) && (!tradeIfLowerLifePressure || (ai.getLifeLostLastTurn() + ai.getLifeLostThisTurn() < - defendingOpponent.getLifeLostThisTurn() + defendingOpponent.getLifeLostThisTurn()))) { + defendingOpponent.getLifeLostThisTurn() + defendingOpponent.getLifeLostThisTurn()))) { aiAggression = 4; // random (chance-based) attack expecting to trade or damage player. } else if (ratioDiff >= 0 && this.attackers.size() > 1) { aiAggression = 3; // attack expecting to make good trades or damage player. @@ -1265,19 +1278,20 @@ public class AiAttackController { if ( LOG_AI_ATTACKS ) System.out.println("Normal attack"); - attackersLeft = notNeededAsBlockers(combat.getAttackers(), attackersLeft); - attackersLeft = sortAttackers(attackersLeft); + List left = new ArrayList<>(attackersLeft); + left = notNeededAsBlockers(combat.getAttackers(), left); + left = sortAttackers(left); if ( LOG_AI_ATTACKS ) - System.out.println("attackersLeft = " + attackersLeft); + System.out.println("attackersLeft = " + left); FCollection possibleDefenders = new FCollection<>(defendingOpponent); possibleDefenders.addAll(defendingOpponent.getPlaneswalkersInPlay()); - while (!attackersLeft.isEmpty()) { + while (!left.isEmpty()) { CardCollection attackersAssigned = new CardCollection(); - for (int i = 0; i < attackersLeft.size(); i++) { - final Card attacker = attackersLeft.get(i); + for (int i = 0; i < left.size(); i++) { + final Card attacker = left.get(i); if (aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike() && ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, defendingOpponent) >= ComputerUtilCombat.getDamageToKill(attacker, false)) { @@ -1291,7 +1305,7 @@ public class AiAttackController { attackersAssigned.add(attacker); // check if attackers are enough to finish the attacked planeswalker - if (i < attackersLeft.size() - 1 && defender instanceof Card) { + if (i < left.size() - 1 && defender instanceof Card) { final int blockNum = this.blockers.size(); int attackNum = 0; int damage = 0; @@ -1312,9 +1326,9 @@ public class AiAttackController { } } - attackersLeft.removeAll(attackersAssigned); + left.removeAll(attackersAssigned); possibleDefenders.remove(defender); - if (attackersLeft.isEmpty() || possibleDefenders.isEmpty()) { + if (left.isEmpty() || possibleDefenders.isEmpty()) { break; } CardCollection pwDefending = new CardCollection(Iterables.filter(possibleDefenders, Card.class)); @@ -1496,51 +1510,51 @@ public class AiAttackController { // decide if the creature should attack based on the prevailing strategy choice in aiAggression switch (aiAggression) { - case 6: // Exalted: expecting to at least kill a creature of equal value or not be blocked - if ((saf.canKillAll && saf.isWorthLessThanAllKillers) || !saf.canBeBlocked()) { + case 6: // Exalted: expecting to at least kill a creature of equal value or not be blocked + if ((saf.canKillAll && saf.isWorthLessThanAllKillers) || !saf.canBeBlocked()) { + if (LOG_AI_ATTACKS) + System.out.println(attacker.getName() + " = attacking expecting to kill creature, or is unblockable"); + return true; + } + break; + case 5: // all out attacking if (LOG_AI_ATTACKS) - System.out.println(attacker.getName() + " = attacking expecting to kill creature, or is unblockable"); + System.out.println(attacker.getName() + " = all out attacking"); return true; - } - break; - case 5: // all out attacking - if (LOG_AI_ATTACKS) - System.out.println(attacker.getName() + " = all out attacking"); - return true; - case 4: // expecting to at least trade with something, or can attack "for free", expecting no counterattack - if (saf.canKillAll || (saf.dangerousBlockersPresent && saf.canKillAllDangerous && !saf.canBeKilledByOne) || !saf.canBeBlocked() - || saf.defPower == 0) { - if (LOG_AI_ATTACKS) - System.out.println(attacker.getName() + " = attacking expecting to at least trade with something"); - return true; - } - break; - case 3: // expecting to at least kill a creature of equal value or not be blocked - if ((saf.canKillAll && saf.isWorthLessThanAllKillers) - || (((saf.dangerousBlockersPresent && saf.canKillAllDangerous) || saf.hasAttackEffect || saf.hasCombatEffect) && !saf.canBeKilledByOne) - || !saf.canBeBlocked()) { - if (LOG_AI_ATTACKS) - System.out.println(attacker.getName() + " = attacking expecting to kill creature or cause damage, or is unblockable"); - return true; - } - break; - case 2: // attack expecting to attract a group block or destroying a single blocker and surviving - if (!saf.canBeBlocked() || ((saf.canKillAll || saf.hasAttackEffect || saf.hasCombatEffect) && !saf.canBeKilledByOne && - ((saf.dangerousBlockersPresent && saf.canKillAllDangerous) || !saf.canBeKilled))) { - if (LOG_AI_ATTACKS) - System.out.println(attacker.getName() + " = attacking expecting to survive or attract group block"); - return true; - } - break; - case 1: // unblockable creatures only - if (!saf.canBeBlocked() || (saf.numberOfPossibleBlockers == 1 && saf.canKillAll && !saf.canBeKilledByOne)) { - if (LOG_AI_ATTACKS) - System.out.println(attacker.getName() + " = attacking expecting not to be blocked"); - return true; - } - break; - default: - break; + case 4: // expecting to at least trade with something, or can attack "for free", expecting no counterattack + if (saf.canKillAll || (saf.dangerousBlockersPresent && saf.canKillAllDangerous && !saf.canBeKilledByOne) || !saf.canBeBlocked() + || saf.defPower == 0) { + if (LOG_AI_ATTACKS) + System.out.println(attacker.getName() + " = attacking expecting to at least trade with something"); + return true; + } + break; + case 3: // expecting to at least kill a creature of equal value or not be blocked + if ((saf.canKillAll && saf.isWorthLessThanAllKillers) + || (((saf.dangerousBlockersPresent && saf.canKillAllDangerous) || saf.hasAttackEffect || saf.hasCombatEffect) && !saf.canBeKilledByOne) + || !saf.canBeBlocked()) { + if (LOG_AI_ATTACKS) + System.out.println(attacker.getName() + " = attacking expecting to kill creature or cause damage, or is unblockable"); + return true; + } + break; + case 2: // attack expecting to attract a group block or destroying a single blocker and surviving + if (!saf.canBeBlocked() || ((saf.canKillAll || saf.hasAttackEffect || saf.hasCombatEffect) && !saf.canBeKilledByOne && + ((saf.dangerousBlockersPresent && saf.canKillAllDangerous) || !saf.canBeKilled))) { + if (LOG_AI_ATTACKS) + System.out.println(attacker.getName() + " = attacking expecting to survive or attract group block"); + return true; + } + break; + case 1: // unblockable creatures only + if (!saf.canBeBlocked() || (saf.numberOfPossibleBlockers == 1 && saf.canKillAll && !saf.canBeKilledByOne)) { + if (LOG_AI_ATTACKS) + System.out.println(attacker.getName() + " = attacking expecting not to be blocked"); + return true; + } + break; + default: + break; } return false; // don't attack } @@ -1657,31 +1671,31 @@ public class AiAttackController { } if (color != null) { switch (color) { - case "black": - if (!c.isBlack()) { - color = null; - } - break; - case "blue": - if (!c.isBlue()) { - color = null; - } - break; - case "green": - if (!c.isGreen()) { - color = null; - } - break; - case "red": - if (!c.isRed()) { - color = null; - } - break; - case "white": - if (!c.isWhite()) { - color = null; - } - break; + case "black": + if (!c.isBlack()) { + color = null; + } + break; + case "blue": + if (!c.isBlue()) { + color = null; + } + break; + case "green": + if (!c.isGreen()) { + color = null; + } + break; + case "red": + if (!c.isRed()) { + color = null; + } + break; + case "white": + if (!c.isWhite()) { + color = null; + } + break; } } if (color == null && artifact == null) { //nothing can make the attacker unblockable @@ -1697,7 +1711,7 @@ public class AiAttackController { return null; //should never get here } - private void doLightmineFieldAttackLogic(final List attackersLeft, int numForcedAttackers, boolean playAggro) { + private void doLightmineFieldAttackLogic(final Queue attackersLeft, int numForcedAttackers, boolean playAggro) { CardCollection attSorted = new CardCollection(attackersLeft); CardCollection attUnsafe = new CardCollection(); CardLists.sortByToughnessDesc(attSorted); @@ -1727,7 +1741,7 @@ public class AiAttackController { attackersLeft.removeAll(attUnsafe); } - private boolean doRevengeOfRavensAttackLogic(final GameEntity defender, final List attackersLeft, int numForcedAttackers, int maxAttack) { + private boolean doRevengeOfRavensAttackLogic(final GameEntity defender, final Queue attackersLeft, int numForcedAttackers, int maxAttack) { // TODO: detect Revenge of Ravens by the trigger instead of by name boolean revengeOfRavens = false; if (defender instanceof Player) { diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 14915df4a79..cb8e12abeaa 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -61,6 +61,7 @@ import forge.game.trigger.WrappedAbility; import forge.game.zone.ZoneType; import forge.item.PaperCard; import forge.util.Aggregates; +import forge.util.CollectionUtil; import forge.util.ComparatorUtil; import forge.util.Expressions; import forge.util.MyRandom; @@ -68,6 +69,9 @@ import io.sentry.Breadcrumb; import io.sentry.Sentry; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; /** *

@@ -88,6 +92,7 @@ public class AiController { private SpellAbilityPicker simPicker; private int lastAttackAggression; private boolean useLivingEnd; + private List skipped; public AiController(final Player computerPlayer, final Game game0) { player = computerPlayer; @@ -397,10 +402,27 @@ public class AiController { private static List getPlayableCounters(final CardCollection l) { final List spellAbility = Lists.newArrayList(); for (final Card c : l) { - for (final SpellAbility sa : c.getNonManaAbilities()) { - // Check if this AF is a Counterspell - if (sa.getApi() == ApiType.Counter) { - spellAbility.add(sa); + if (c.isForetold() && c.getAlternateState() != null) { + try { + for (final SpellAbility sa : c.getAlternateState().getNonManaAbilities()) { + // Check if this AF is a Counterspell + if (sa.getApi() == ApiType.Counter) { + spellAbility.add(sa); + } else { + if (sa.getApi() != null && sa.getApi().toString().contains("Foretell") && c.getAlternateState().getName().equalsIgnoreCase("Saw It Coming")) + spellAbility.add(sa); + } + } + } catch (Exception e) { + // facedown and alternatestate counters should be accessible + e.printStackTrace(); + } + } else { + for (final SpellAbility sa : c.getNonManaAbilities()) { + // Check if this AF is a Counterspell + if (sa.getApi() == ApiType.Counter) { + spellAbility.add(sa); + } } } } @@ -1323,9 +1345,7 @@ public class AiController { for (final Card element : combat.getAttackers()) { // tapping of attackers happens after Propaganda is paid for - final StringBuilder sb = new StringBuilder(); - sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker."); - Log.debug(sb.toString()); + Log.debug("Computer just assigned " + element.getName() + " as an attacker."); } } @@ -1369,7 +1389,7 @@ public class AiController { if (landsWannaPlay != null && !landsWannaPlay.isEmpty()) { // TODO search for other land it might want to play? Card land = chooseBestLandToPlay(landsWannaPlay); - if ((!player.canLoseLife() || player.cantLoseForZeroOrLessLife() || ComputerUtil.getDamageFromETB(player, land) < player.getLife()) + if (land != null && (!player.canLoseLife() || player.cantLoseForZeroOrLessLife() || ComputerUtil.getDamageFromETB(player, land) < player.getLife()) && (!game.getPhaseHandler().is(PhaseType.MAIN1) || !isSafeToHoldLandDropForMain2(land))) { final List abilities = land.getAllPossibleAbilities(player, true); // skip non Land Abilities @@ -1502,6 +1522,13 @@ public class AiController { } private SpellAbility getSpellAbilityToPlay() { + if (skipped != null) { + //FIXME: this is for failed SA to skip temporarily, don't know why AI computation for mana fails, maybe due to auto mana compute? + for (SpellAbility sa : skipped) { + //System.out.println("Unskip: " + sa.toString() + " (" + sa.getHostCard().getName() + ")."); + sa.setSkip(false); + } + } CardCollection cards = ComputerUtilAbility.getAvailableCards(game, player); cards = ComputerUtilCard.dedupeCards(cards); List saList = Lists.newArrayList(); @@ -1547,8 +1574,12 @@ public class AiController { Iterables.removeIf(saList, spellAbility -> { //don't include removedAI cards if somehow the AI can play the ability or gain control of unsupported card // TODO allow when experimental profile? - return spellAbility.isLandAbility() || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard())); + return spellAbility.isLandAbility() || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard())) || !spellAbility.canCastTiming(player); }); + //removed skipped SA + skipped = Lists.newArrayList(Iterables.filter(saList, SpellAbility::isSkip)); + if (!skipped.isEmpty()) + saList.removeAll(skipped); //update LivingEndPlayer useLivingEnd = Iterables.any(player.getZone(ZoneType.Library), CardPredicates.nameEquals("Living End")); @@ -1565,79 +1596,94 @@ public class AiController { if (all == null || all.isEmpty()) return null; - try { - all.sort(ComputerUtilAbility.saEvaluator); // put best spells first - ComputerUtilAbility.sortCreatureSpells(all); - } catch (IllegalArgumentException ex) { - System.err.println(ex.getMessage()); - String assertex = ComparatorUtil.verifyTransitivity(ComputerUtilAbility.saEvaluator, all); - Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex); - } //avoid ComputerUtil.aiLifeInDanger in loops as it slows down a lot.. call this outside loops will generally be fast... boolean isLifeInDanger = useLivingEnd && ComputerUtil.aiLifeInDanger(player, true, 0); + List> futures = new ArrayList<>(); + Queue spells = new ConcurrentLinkedQueue<>(); for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) { - // Don't add Counterspells to the "normal" playcard lookups - if (skipCounter && sa.getApi() == ApiType.Counter) { - continue; - } - - if (sa.getHostCard().hasKeyword(Keyword.STORM) - && sa.getApi() != ApiType.Counter // AI would suck at trying to deliberately proc a Storm counterspell - && player.getZone(ZoneType.Hand).contains(Predicates.not(Predicates.or(CardPredicates.Presets.LANDS, CardPredicates.hasKeyword("Storm"))))) { - if (game.getView().getStormCount() < this.getIntProperty(AiProps.MIN_COUNT_FOR_STORM_SPELLS)) { - // skip evaluating Storm unless we reached the minimum Storm count - continue; + futures.add(CompletableFuture.supplyAsync(()-> { + // Don't add Counterspells to the "normal" playcard lookups + if (skipCounter && sa.getApi() == ApiType.Counter) { + return 0; } - } - // living end AI decks - // TODO: generalize the implementation so that superfluous logic-specific checks for life, library size, etc. aren't needed - AiPlayDecision aiPlayDecision = AiPlayDecision.CantPlaySa; - if (useLivingEnd) { - if (sa.isCycling() && sa.canCastTiming(player) && player.getCardsIn(ZoneType.Library).size() >= 10) { - if (ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) { - if (sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostPayLife.class) - && !player.cantLoseForZeroOrLessLife() - && player.getLife() <= sa.getPayCosts().getCostPartByType(CostPayLife.class).getAbilityAmount(sa) * 2) { - aiPlayDecision = AiPlayDecision.CantAfford; + + if (sa.getHostCard().hasKeyword(Keyword.STORM) + && sa.getApi() != ApiType.Counter // AI would suck at trying to deliberately proc a Storm counterspell + && player.getZone(ZoneType.Hand).contains(Predicates.not(Predicates.or(CardPredicates.Presets.LANDS, CardPredicates.hasKeyword("Storm"))))) { + if (game.getView().getStormCount() < this.getIntProperty(AiProps.MIN_COUNT_FOR_STORM_SPELLS)) { + // skip evaluating Storm unless we reached the minimum Storm count + return 0; + } + } + // living end AI decks + // TODO: generalize the implementation so that superfluous logic-specific checks for life, library size, etc. aren't needed + AiPlayDecision aiPlayDecision = AiPlayDecision.CantPlaySa; + if (useLivingEnd) { + if (sa.isCycling() && sa.canCastTiming(player) && player.getCardsIn(ZoneType.Library).size() >= 10) { + if (ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) { + if (sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostPayLife.class) + && !player.cantLoseForZeroOrLessLife() + && player.getLife() <= sa.getPayCosts().getCostPartByType(CostPayLife.class).getAbilityAmount(sa) * 2) { + aiPlayDecision = AiPlayDecision.CantAfford; + } else { + aiPlayDecision = AiPlayDecision.WillPlay; + } + } + } else if (sa.getHostCard().hasKeyword(Keyword.CASCADE)) { + if (isLifeInDanger) { //needs more tune up for certain conditions + aiPlayDecision = player.getCreaturesInPlay().size() >= 4 ? AiPlayDecision.CantPlaySa : AiPlayDecision.WillPlay; + } else if (CardLists.filter(player.getZone(ZoneType.Graveyard).getCards(), CardPredicates.Presets.CREATURES).size() > 4) { + if (player.getCreaturesInPlay().size() >= 4) // it's good minimum + return 0; + else if (!sa.getHostCard().isPermanent() && sa.canCastTiming(player) && ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) + aiPlayDecision = AiPlayDecision.WillPlay;// needs tuneup for bad matchups like reanimator and other things to check on opponent graveyard } else { - aiPlayDecision = AiPlayDecision.WillPlay; + return 0; } } - } else if (sa.getHostCard().hasKeyword(Keyword.CASCADE)) { - if (isLifeInDanger) { //needs more tune up for certain conditions - aiPlayDecision = player.getCreaturesInPlay().size() >= 4 ? AiPlayDecision.CantPlaySa : AiPlayDecision.WillPlay; - } else if (CardLists.filter(player.getZone(ZoneType.Graveyard).getCards(), CardPredicates.Presets.CREATURES).size() > 4) { - if (player.getCreaturesInPlay().size() >= 4) // it's good minimum - continue; - else if (!sa.getHostCard().isPermanent() && sa.canCastTiming(player) && ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) - aiPlayDecision = AiPlayDecision.WillPlay;// needs tuneup for bad matchups like reanimator and other things to check on opponent graveyard - } else { - continue; - } } - } - sa.setActivatingPlayer(player, true); - SpellAbility root = sa.getRootAbility(); + sa.setActivatingPlayer(player, true); + SpellAbility root = sa.getRootAbility(); - if (root.isSpell() || root.isTrigger() || root.isReplacementAbility()) { - sa.setLastStateBattlefield(game.getLastStateBattlefield()); - sa.setLastStateGraveyard(game.getLastStateGraveyard()); - } - //override decision for living end player - AiPlayDecision opinion = useLivingEnd && AiPlayDecision.WillPlay.equals(aiPlayDecision) ? aiPlayDecision : canPlayAndPayFor(sa); + if (root.isSpell() || root.isTrigger() || root.isReplacementAbility()) { + sa.setLastStateBattlefield(game.getLastStateBattlefield()); + sa.setLastStateGraveyard(game.getLastStateGraveyard()); + } + //override decision for living end player + AiPlayDecision opinion = useLivingEnd && AiPlayDecision.WillPlay.equals(aiPlayDecision) ? aiPlayDecision : canPlayAndPayFor(sa); - // reset LastStateBattlefield - sa.clearLastState(); - // PhaseHandler ph = game.getPhaseHandler(); - // System.out.printf("Ai thinks '%s' of %s -> %s @ %s %s >>> \n", opinion, sa.getHostCard(), sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase()); + // reset LastStateBattlefield + sa.clearLastState(); + // PhaseHandler ph = game.getPhaseHandler(); + // System.out.printf("Ai thinks '%s' of %s -> %s @ %s %s >>> \n", opinion, sa.getHostCard(), sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase()); - if (opinion != AiPlayDecision.WillPlay) - continue; + if (opinion != AiPlayDecision.WillPlay) + return 0; - return sa; + spells.add(sa); + return 0; + })); } + //timeout 5 seconds? even the AI don't acquire all, there should be SA to cast if valid + CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); + CompletableFuture.allOf(futuresArray).completeOnTimeout(null, 5, TimeUnit.SECONDS).join(); + futures.clear(); + if (!spells.isEmpty()) { + List spellAbilities = new ArrayList<>(spells); + if (spellAbilities.size() == 1) + return spellAbilities.get(0); + try { + spellAbilities.sort(ComputerUtilAbility.saEvaluator); // put best spells first + ComputerUtilAbility.sortCreatureSpells(spellAbilities); + } catch (IllegalArgumentException ex) { + System.err.println(ex.getMessage()); + String assertex = ComparatorUtil.verifyTransitivity(ComputerUtilAbility.saEvaluator, spellAbilities); + Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex); + } + return spellAbilities.get(0); + } return null; } @@ -2205,7 +2251,7 @@ public class AiController { result.addAll(activePlayerSAs); //need to reverse because of magic stack - Collections.reverse(result); + CollectionUtil.reverse(result); return result; } diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 662a42b7318..eb6f4a92f0d 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -15,6 +15,7 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.zone.ZoneType; import forge.util.Aggregates; +import forge.util.CollectionUtil; import forge.util.TextUtil; import forge.util.collect.FCollectionView; import org.apache.commons.lang3.ObjectUtils; @@ -155,7 +156,7 @@ public class AiCostDecision extends CostDecisionMakerBase { List res = cost.getPotentialPlayers(player, ability); // I should only choose one of these right? // TODO Choose the "worst" player. - Collections.shuffle(res); + CollectionUtil.shuffle(res); return PaymentDecision.players(res.subList(0, 1)); } @@ -179,7 +180,7 @@ public class AiCostDecision extends CostDecisionMakerBase { CardCollection chosen = new CardCollection(); CardLists.sortByCmcDesc(valid); - Collections.reverse(valid); + CollectionUtil.reverse(valid); int totalCMC = 0; for (Card card : valid) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 0d83d91aefe..d6aacc9be91 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -68,6 +68,7 @@ import forge.game.trigger.WrappedAbility; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.util.Aggregates; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; import forge.util.collect.FCollection; @@ -87,6 +88,8 @@ public class ComputerUtil { } public static boolean handlePlayingSpellAbility(final Player ai, SpellAbility sa, final Game game, Runnable chooseTargets) { final Card source = sa.getHostCard(); + final Card host = sa.getHostCard(); + final Zone hz = host.isCopiedSpell() ? null : host.getZone(); source.setSplitStateToPlayAbility(sa); if (sa.isSpell() && !source.isCopiedSpell()) { @@ -144,8 +147,15 @@ public class ComputerUtil { return true; } } - //Should not arrive here - System.out.println("AI failed to play " + sa.getHostCard()); + // FIXME: Should not arrive here, though the card seems to be stucked on stack zone and invalidated and nowhere to be found, try to put back to original zone and maybe try to cast again if possible at later time? + System.out.println("[" + sa.getActivatingPlayer() + "] AI failed to play " + sa.getHostCard() + " [" + sa.getHostCard().getZone() + "]"); + sa.setSkip(true); + if (host != null && hz != null) { + Card c = game.getAction().moveTo(hz.getZoneType(), host, null, null); + for (SpellAbility csa : c.getSpellAbilities()) { + csa.setSkip(true); + } + } return false; } @@ -673,7 +683,7 @@ public class ComputerUtil { // FIXME: This is suboptimal, maybe implement a single comparator that'll take care of all of this? CardLists.sortByCmcDesc(typeList); - Collections.reverse(typeList); + CollectionUtil.reverse(typeList); // TODO AI needs some improvements here @@ -727,7 +737,7 @@ public class ComputerUtil { // FIXME: This is suboptimal, maybe implement a single comparator that'll take care of all of this? CardLists.sortByCmcDesc(typeList); - Collections.reverse(typeList); + CollectionUtil.reverse(typeList); typeList.sort((a, b) -> { if (!a.isInPlay() && b.isInPlay()) return -1; else if (!b.isInPlay() && a.isInPlay()) return 1; @@ -757,7 +767,7 @@ public class ComputerUtil { final CardCollection list = new CardCollection(); if (zone != ZoneType.Hand) { - Collections.reverse(typeList); + CollectionUtil.reverse(typeList); } for (int i = 0; i < amount; i++) { @@ -807,7 +817,7 @@ public class ComputerUtil { typeList.remove(activate); } ComputerUtilCard.sortByEvaluateCreature(typeList); - Collections.reverse(typeList); + CollectionUtil.reverse(typeList); final CardCollection tapList = new CardCollection(); @@ -1759,7 +1769,7 @@ public class ComputerUtil { // align threatened with resolve order // matters if stack contains multiple activations (e.g. Temur Sabertooth) - Collections.reverse(objects); + CollectionUtil.reverse(objects); return objects; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index b23d4d6b250..aba20b8ed8d 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -38,6 +38,7 @@ import forge.game.trigger.Trigger; import forge.game.trigger.TriggerType; import forge.game.zone.Zone; import forge.game.zone.ZoneType; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; @@ -466,18 +467,18 @@ public class ComputerUtilMana { public static String predictManafromSpellAbility(SpellAbility saPayment, Player ai, ManaCostShard toPay) { Card hostCard = saPayment.getHostCard(); - String manaProduced = predictManaReplacement(saPayment, ai, toPay); - String originalProduced = manaProduced; + StringBuilder manaProduced = new StringBuilder(predictManaReplacement(saPayment, ai, toPay)); + String originalProduced = manaProduced.toString(); if (originalProduced.isEmpty()) { - return manaProduced; + return manaProduced.toString(); } // Run triggers like Nissa final Map runParams = AbilityKey.mapFromCard(hostCard); runParams.put(AbilityKey.Activator, ai); // assuming AI would only ever gives itself mana runParams.put(AbilityKey.AbilityMana, saPayment); - runParams.put(AbilityKey.Produced, manaProduced); + runParams.put(AbilityKey.Produced, manaProduced.toString()); for (Trigger tr : ai.getGame().getTriggerHandler().getActiveTrigger(TriggerType.TapsForMana, runParams)) { SpellAbility trSA = tr.ensureAbility(); if (trSA == null) { @@ -489,7 +490,7 @@ public class ComputerUtilMana { if (produced.equals("Chosen")) { produced = MagicColor.toShortString(trSA.getHostCard().getChosenColor()); } - manaProduced += " " + StringUtils.repeat(produced, " ", pAmount); + manaProduced.append(" ").append(StringUtils.repeat(produced, " ", pAmount)); } else if (ApiType.ManaReflected.equals(trSA.getApi())) { final String colorOrType = trSA.getParamOrDefault("ColorOrType", "Color"); // currently Color or Type, Type is colors + colorless @@ -498,11 +499,11 @@ public class ComputerUtilMana { if (reflectProperty.equals("Produced") && !originalProduced.isEmpty()) { // check if a colorless shard can be paid from the trigger if (toPay.equals(ManaCostShard.COLORLESS) && colorOrType.equals("Type") && originalProduced.contains("C")) { - manaProduced += " " + "C"; + manaProduced.append(" " + "C"); } else if (originalProduced.length() == 1) { // if length is only one, and it either is equal C == Type if (colorOrType.equals("Type") || !originalProduced.equals("C")) { - manaProduced += " " + originalProduced; + manaProduced.append(" ").append(originalProduced); } } else { // should it look for other shards too? @@ -510,7 +511,7 @@ public class ComputerUtilMana { for (String s : originalProduced.split(" ")) { if (colorOrType.equals("Type") || !s.equals("C") && toPay.canBePaidWithManaOfColor(MagicColor.fromName(s))) { found = true; - manaProduced += " " + s; + manaProduced.append(" ").append(s); break; } } @@ -518,7 +519,7 @@ public class ComputerUtilMana { if (!found) { for (String s : originalProduced.split(" ")) { if (colorOrType.equals("Type") || !s.equals("C")) { - manaProduced += " " + s; + manaProduced.append(" ").append(s); break; } } @@ -527,7 +528,7 @@ public class ComputerUtilMana { } } } - return manaProduced; + return manaProduced.toString(); } public static CardCollection getManaSourcesToPayCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai) { @@ -826,7 +827,8 @@ public class ComputerUtilMana { if (test) { resetPayment(paymentList); } else { - System.out.println("ComputerUtilMana: payManaCost() cost was not paid for " + sa.toString() + " (" + sa.getHostCard().getName() + "). Didn't find what to pay for " + toPay); + System.out.println("ComputerUtilMana: payManaCost() cost was not paid for " + sa + " (" + sa.getHostCard().getName() + "). Didn't find what to pay for " + toPay); + sa.setSkip(true); } return false; } @@ -1518,11 +1520,11 @@ public class ComputerUtilMana { sortedManaSources.addAll(sortedManaSources.size(), anyColorManaSources); //use better creatures later ComputerUtilCard.sortByEvaluateCreature(otherManaSources); - Collections.reverse(otherManaSources); + CollectionUtil.reverse(otherManaSources); sortedManaSources.addAll(sortedManaSources.size(), otherManaSources); // This should be things like sacrifice other stuff. ComputerUtilCard.sortByEvaluateCreature(useLastManaSources); - Collections.reverse(useLastManaSources); + CollectionUtil.reverse(useLastManaSources); sortedManaSources.addAll(sortedManaSources.size(), useLastManaSources); if (DEBUG_MANA_PAYMENT) { diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index ec8db182168..e7f0b8c15d6 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -40,6 +40,7 @@ import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; import forge.item.PaperCard; import forge.util.Aggregates; +import forge.util.CollectionUtil; import forge.util.ITriggerEvent; import forge.util.MyRandom; import forge.util.collect.FCollection; @@ -650,7 +651,7 @@ public class PlayerControllerAi extends PlayerController { if(source == null || !source.hasParam("LibraryPosition") || AbilityUtils.calculateAmount(source.getHostCard(), source.getParam("LibraryPosition"), source) >= 0) { //Cards going to the top of a deck are returned in reverse order. - Collections.reverse(reordered); + CollectionUtil.reverse(reordered); } assert(reordered.size() == cards.size()); @@ -1565,8 +1566,13 @@ public class PlayerControllerAi extends PlayerController { } } - int i = MyRandom.getRandom().nextInt(dungeonNames.size()); - return Card.fromPaperCard(dungeonCards.get(i), ai); + try { + // if this fail somehow add fallback to get any from dungeonCards + int i = MyRandom.getRandom().nextInt(dungeonNames.size()); + return Card.fromPaperCard(dungeonCards.get(i), ai); + } catch (Exception e) { + return Card.fromPaperCard(Aggregates.random(dungeonCards), ai); + } } @Override diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 7731c20bf9e..c737cd27657 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -47,6 +47,7 @@ import forge.game.staticability.StaticAbility; import forge.game.trigger.Trigger; import forge.game.zone.ZoneType; import forge.util.Aggregates; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; import forge.util.maps.LinkedHashMapToAmount; @@ -319,7 +320,7 @@ public class SpecialCardAi { best = ComputerUtilCard.getBestCreatureAI(cardlist); if (best == null) { // If nothing on the battlefield has a nonmana ability choose something - Collections.shuffle(cardlist); + CollectionUtil.shuffle(cardlist); best = cardlist.getFirst(); } diff --git a/forge-ai/src/main/java/forge/ai/ability/CharmAi.java b/forge-ai/src/main/java/forge/ai/ability/CharmAi.java index 7d02582f652..5ea0778a711 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CharmAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CharmAi.java @@ -1,6 +1,5 @@ package forge.ai.ability; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -18,6 +17,7 @@ import forge.game.player.Player; import forge.game.spellability.AbilitySub; import forge.game.spellability.SpellAbility; import forge.util.Aggregates; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.collect.FCollection; @@ -52,7 +52,7 @@ public class CharmAi extends SpellAbilityAi { } else { // only randomize if not all possible together if (num < choices.size()) { - Collections.shuffle(choices); + CollectionUtil.shuffle(choices); } /* @@ -101,7 +101,7 @@ public class CharmAi extends SpellAbilityAi { // Pawprint final int pawprintLimit = sa.hasParam("Pawprint") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Pawprint"), sa) : 0; if (pawprintLimit > 0) { - Collections.reverse(choices); // try to pay for the more expensive subs first + CollectionUtil.reverse(choices); // try to pay for the more expensive subs first } int pawprintAmount = 0; diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java index 721c183f635..21b38f0b844 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java @@ -16,8 +16,8 @@ import forge.game.player.PlayerPredicates; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.util.Aggregates; +import forge.util.CollectionUtil; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -226,7 +226,7 @@ public class ChooseCardAi extends SpellAbilityAi { choice = ComputerUtilCard.getWorstAI(options); } else { CardLists.sortByCmcDesc(creats); - Collections.reverse(creats); + CollectionUtil.reverse(creats); choice = creats.get(0); } } else if ("NegativePowerFirst".equals(logic)) { diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java index e3e50950b18..ee7d0f57992 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java @@ -1,6 +1,5 @@ package forge.ai.ability; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -10,6 +9,7 @@ import forge.ai.SpellAbilityAi; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.util.CollectionUtil; public class ChooseCompanionAi extends SpellAbilityAi { @@ -23,7 +23,7 @@ public class ChooseCompanionAi extends SpellAbilityAi { return null; } - Collections.shuffle(cards); + CollectionUtil.shuffle(cards); return cards.get(0); } } diff --git a/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java b/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java index b8ac075c536..4b2100aa5c7 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java @@ -1,6 +1,5 @@ package forge.ai.ability; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -23,6 +22,7 @@ import forge.game.player.PlayerCollection; import forge.game.player.PlayerPredicates; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; +import forge.util.CollectionUtil; import forge.util.MyRandom; public class DiscardAi extends SpellAbilityAi { @@ -148,7 +148,7 @@ public class DiscardAi extends SpellAbilityAi { private boolean discardTargetAI(final Player ai, final SpellAbility sa) { final PlayerCollection opps = ai.getOpponents(); - Collections.shuffle(opps); + CollectionUtil.shuffle(opps); for (Player opp : opps) { if (opp.getCardsIn(ZoneType.Hand).isEmpty() && !ComputerUtil.activateForCost(sa, ai)) { continue; diff --git a/forge-ai/src/main/java/forge/ai/simulation/SimulationController.java b/forge-ai/src/main/java/forge/ai/simulation/SimulationController.java index 2f214ecd09a..7669dcf7275 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/SimulationController.java +++ b/forge-ai/src/main/java/forge/ai/simulation/SimulationController.java @@ -1,7 +1,6 @@ package forge.ai.simulation; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import forge.ai.simulation.GameStateEvaluator.Score; @@ -9,6 +8,7 @@ import forge.game.GameObject; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; +import forge.util.CollectionUtil; public class SimulationController { private static boolean DEBUG = false; @@ -106,7 +106,7 @@ public class SimulationController { sequence.add(current); current = current.prevDecision; } - Collections.reverse(sequence); + CollectionUtil.reverse(sequence); // Merge targets & choices into their parents. int writeIndex = 0; for (int i = 0; i < sequence.size(); i++) { diff --git a/forge-core/src/main/java/forge/StaticData.java b/forge-core/src/main/java/forge/StaticData.java index 068e1956d6f..9d34b9922b9 100644 --- a/forge-core/src/main/java/forge/StaticData.java +++ b/forge-core/src/main/java/forge/StaticData.java @@ -7,6 +7,7 @@ import forge.card.CardRules; import forge.card.PrintSheet; import forge.item.*; import forge.token.TokenDb; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.ImageUtil; import forge.util.TextUtil; @@ -195,7 +196,7 @@ public class StaticData { sortedEditions.add(set); } Collections.sort(sortedEditions); - Collections.reverse(sortedEditions); //put newer sets at the top + CollectionUtil.reverse(sortedEditions); //put newer sets at the top } return sortedEditions; } diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index ae928fee725..d21715165df 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -28,6 +28,7 @@ import forge.deck.generation.IDeckGenPool; import forge.item.IPaperCard; import forge.item.PaperCard; import forge.util.CollectionSuppliers; +import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.TextUtil; import forge.util.lang.LangEnglish; @@ -842,7 +843,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { if (acceptedEditions.size() > 1) { Collections.sort(acceptedEditions); // CardEdition correctly sort by (release) date if (artPref.latestFirst) - Collections.reverse(acceptedEditions); // newest editions first + CollectionUtil.reverse(acceptedEditions); // newest editions first } final Iterator editionIterator = acceptedEditions.iterator(); diff --git a/forge-core/src/main/java/forge/card/CardEdition.java b/forge-core/src/main/java/forge/card/CardEdition.java index 96c6caf33dc..6e02ef50f45 100644 --- a/forge-core/src/main/java/forge/card/CardEdition.java +++ b/forge-core/src/main/java/forge/card/CardEdition.java @@ -490,7 +490,7 @@ public final class CardEdition implements Comparable { return null; } - Collections.shuffle(boosterTypes); + CollectionUtil.shuffle(boosterTypes); return boosterTypes.get(0); } @@ -802,7 +802,7 @@ public final class CardEdition implements Comparable { public Iterable getOrderedEditions() { List res = Lists.newArrayList(this); Collections.sort(res); - Collections.reverse(res); + CollectionUtil.reverse(res); return res; } diff --git a/forge-core/src/main/java/forge/deck/CardPool.java b/forge-core/src/main/java/forge/deck/CardPool.java index 0846ae7ed49..34265f136d5 100644 --- a/forge-core/src/main/java/forge/deck/CardPool.java +++ b/forge-core/src/main/java/forge/deck/CardPool.java @@ -27,6 +27,7 @@ import forge.card.CardEdition; import forge.item.IPaperCard; import forge.item.PaperCard; import forge.util.CollectionSuppliers; +import forge.util.CollectionUtil; import forge.util.ItemPool; import forge.util.ItemPoolSorter; import forge.util.MyRandom; @@ -349,7 +350,7 @@ public class CardPool extends ItemPool { pivotCandidates.sort(CardEdition::compareTo); boolean searchPolicyAndPoolAreCompliant = isLatestCardArtPreference == this.isModern(); if (!searchPolicyAndPoolAreCompliant) - Collections.reverse(pivotCandidates); // reverse to have latest-first. + CollectionUtil.reverse(pivotCandidates); // reverse to have latest-first. return pivotCandidates.get(0); } diff --git a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java index 812dd7ff779..2c3f88c2094 100644 --- a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java +++ b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java @@ -28,6 +28,7 @@ import forge.card.CardEdition.FoilType; import forge.item.*; import forge.item.IPaperCard.Predicates.Presets; import forge.util.Aggregates; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; @@ -58,7 +59,7 @@ public class BoosterGenerator { } private static PaperCard generateFoilCard(List cardList) { - Collections.shuffle(cardList, MyRandom.getRandom()); + CollectionUtil.shuffle(cardList, MyRandom.getRandom()); PaperCard randomCard = cardList.get(0); return randomCard.getFoiled(); } diff --git a/forge-core/src/main/java/forge/util/CollectionUtil.java b/forge-core/src/main/java/forge/util/CollectionUtil.java new file mode 100644 index 00000000000..6ce27407888 --- /dev/null +++ b/forge-core/src/main/java/forge/util/CollectionUtil.java @@ -0,0 +1,65 @@ +package forge.util; + +import forge.util.collect.FCollection; + +import java.util.*; + +public class CollectionUtil { + public static void shuffle(List list) { + shuffle(list, MyRandom.getRandom()); + } + + public static void shuffle(List list, Random random) { + if (list instanceof FCollection) { + //FCollection -> copyonwritearraylist is not compatible, use different method + shuffleList(list, random); + } else { + //use Collections -> shuffle(LIST, RANDOM) since it's not FCollection + Collections.shuffle(list, random); + } + } + + public static void shuffleList(List a, Random r) { + int n = a.size(); + for (int i = 0; i < n; i++) { + int change = i + r.nextInt(n - i); + swap(a, i, change); + } + } + + private static void swap(List a, int i, int change) { + T helper = a.get(i); + a.set(i, a.get(change)); + a.set(change, helper); + } + + public static void reverse(List list) { + if (list == null || list.isEmpty()) + return; + if (list instanceof FCollection) { + //FCollection -> copyonwritearraylist is not compatible, use different method + reverseWithRecursion(list, 0, list.size() - 1); + } else { + Collections.reverse(list); + } + } + + public static void reverseWithRecursion(List list) { + if (list.size() > 1) { + T value = list.remove(0); + reverseWithRecursion(list); + list.add(value); + } + } + + public static void reverseWithRecursion(List list, int startIndex, int lastIndex) { + if (startIndex < lastIndex) { + T t = list.get(lastIndex); + list.set(lastIndex, list.get(startIndex)); + list.set(startIndex, t); + startIndex++; + lastIndex--; + reverseWithRecursion(list, startIndex, lastIndex); + } + } +} \ No newline at end of file diff --git a/forge-core/src/main/java/forge/util/collect/FCollection.java b/forge-core/src/main/java/forge/util/collect/FCollection.java index d54fe2adb24..16ba3a21f3a 100644 --- a/forge-core/src/main/java/forge/util/collect/FCollection.java +++ b/forge-core/src/main/java/forge/util/collect/FCollection.java @@ -6,7 +6,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; @@ -43,12 +42,38 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** * The {@link Set} representation of this collection. */ - private final Set set = Sets.newHashSet(); + private Set SET; + private Set set() { + Set result = SET; + if (result == null) { + synchronized (this) { + result = SET; + if (result == null) { + result = Sets.newConcurrentHashSet(); + SET = result; + } + } + } + return SET; + } /** * The {@link List} representation of this collection. */ - private final LinkedList list = Lists.newLinkedList(); + private List LIST; + private List list() { + List result = LIST; + if (result == null) { + synchronized (this) { + result = LIST; + if (result == null) { + result = Lists.newCopyOnWriteArrayList(); + LIST = result; + } + } + } + return LIST; + } /** * Create an empty {@link FCollection}. @@ -139,7 +164,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public int hashCode() { - return list.hashCode(); + return list().hashCode(); } /** @@ -149,7 +174,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public String toString() { - return list.toString(); + return list().toString(); } /** @@ -158,7 +183,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public final FCollection clone() { - return new FCollection<>(list); + return new FCollection<>(list()); } /** @@ -169,7 +194,10 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public T getFirst() { - return list.getFirst(); + if (list().isEmpty()) + return null; + return list().get(0); + //return list.getFirst(); } /** @@ -180,7 +208,10 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public T getLast() { - return list.getLast(); + if (list().isEmpty()) + return null; + return list().get(list().size() - 1); + //return list.getLast(); } /** @@ -188,7 +219,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public int size() { - return set.size(); + return set().size(); } /** @@ -196,11 +227,11 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public boolean isEmpty() { - return set.isEmpty(); + return set().isEmpty(); } - + public Set asSet() { - return set; + return set(); } /** @@ -211,7 +242,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public boolean contains(final Object o) { - return set.contains(o); + if (o == null) + return false; + return set().contains(o); } /** @@ -219,7 +252,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public Iterator iterator() { - return list.iterator(); + return list().iterator(); } /** @@ -227,7 +260,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public Object[] toArray() { - return list.toArray(); + return list().toArray(); } /** @@ -236,7 +269,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override @SuppressWarnings("hiding") public T[] toArray(final T[] a) { - return list.toArray(a); + return list().toArray(a); } /** @@ -248,8 +281,10 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public boolean add(final T e) { - if (set.add(e)) { - list.add(e); + if (e == null) + return false; + if (set().add(e)) { + list().add(e); return true; } return false; @@ -264,8 +299,10 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public boolean remove(final Object o) { - if (set.remove(o)) { - list.remove(o); + if (o == null) + return false; + if (set().remove(o)) { + list().remove(o); return true; } return false; @@ -273,8 +310,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public boolean removeIf(Predicate filter) { - if (list.removeIf(filter)) { - set.removeIf(filter); + if (list().removeIf(filter)) { + set().removeIf(filter); return true; } return false; @@ -285,7 +322,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public boolean containsAll(final Collection c) { - return set.containsAll(c); + return set().containsAll(c); } /** @@ -307,6 +344,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ public boolean addAll(final Iterable i) { boolean changed = false; + if (i == null) + return false; for (final T e : i) { changed |= add(e); } @@ -370,6 +409,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ public boolean removeAll(final Iterable c) { boolean changed = false; + if (c == null) + return false; for (final Object o : c) { changed |= remove(o); } @@ -381,8 +422,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public boolean retainAll(final Collection c) { - if (set.retainAll(c)) { - list.retainAll(c); + if (set().retainAll(c)) { + list().retainAll(c); return true; } return false; @@ -393,9 +434,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public void clear() { - if (set.isEmpty()) { return; } - set.clear(); - list.clear(); + if (set().isEmpty()) { return; } + set().clear(); + list().clear(); } /** @@ -403,7 +444,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public T get(final int index) { - return list.get(index); + return list().get(index); } /** @@ -413,7 +454,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public T set(final int index, final T element) { //assume this isn't called except when changing list order, so don't worry about updating set - return list.set(index, element); + return list().set(index, element); } /** @@ -434,12 +475,12 @@ public class FCollection implements List, /*Set,*/ FCollectionView, * @return whether this collection changed as a result of this method call. */ private boolean insert(int index, final T element) { - if (set.add(element)) { - list.add(index, element); + if (set().add(element)) { + list().add(index, element); return true; } //re-position in list if needed - final int oldIndex = list.indexOf(element); + final int oldIndex = list().indexOf(element); if (index == oldIndex) { return false; } @@ -447,8 +488,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, if (index > oldIndex) { index--; //account for being removed } - list.remove(oldIndex); - list.add(index, element); + list().remove(oldIndex); + list().add(index, element); return true; } @@ -457,9 +498,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public T remove(final int index) { - final T removedItem = list.remove(index); + final T removedItem = list().remove(index); if (removedItem != null) { - set.remove(removedItem); + set().remove(removedItem); } return removedItem; } @@ -469,7 +510,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public int indexOf(final Object o) { - return list.indexOf(o); + return list().indexOf(o); } /** @@ -477,7 +518,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public int lastIndexOf(final Object o) { - return list.lastIndexOf(o); + return list().lastIndexOf(o); } /** @@ -485,7 +526,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public ListIterator listIterator() { - return list.listIterator(); + return list().listIterator(); } /** @@ -493,7 +534,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public ListIterator listIterator(final int index) { - return list.listIterator(index); + return list().listIterator(index); } /** @@ -506,7 +547,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public List subList(final int fromIndex, final int toIndex) { - return ImmutableList.copyOf(list.subList(fromIndex, toIndex)); + return ImmutableList.copyOf(list().subList(fromIndex, toIndex)); } /** @@ -525,7 +566,11 @@ public class FCollection implements List, /*Set,*/ FCollectionView, * {@inheritDoc} */ public void sort(final Comparator comparator) { - list.sort(comparator); + try { + list().sort(comparator); + } catch (Exception e) { + System.err.println("FCollection failed to sort: \n" + comparator + "\n" + e.getMessage()); + } } /** @@ -533,8 +578,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public Iterable threadSafeIterable() { + return list(); //create a new linked list for iterating to make it thread safe and avoid concurrent modification exceptions - return Iterables.unmodifiableIterable(new LinkedList<>(list)); + //return Iterables.unmodifiableIterable(new LinkedList<>(list)); } @Override diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 41be0638d13..ad3115a6802 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -47,6 +47,7 @@ import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.trackable.Tracker; import forge.util.Aggregates; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.Visitor; import forge.util.collect.FCollection; @@ -398,7 +399,7 @@ public class Game { return ingamePlayers; } final PlayerCollection players = new PlayerCollection(ingamePlayers); - Collections.reverse(players); + CollectionUtil.reverse(players); return players; } @@ -418,7 +419,7 @@ public class Game { final PlayerCollection players = new PlayerCollection(ingamePlayers); players.remove(phaseHandler.getPlayerTurn()); if (!getTurnOrder().isDefaultDirection()) { - Collections.reverse(players); + CollectionUtil.reverse(players); } return players; } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index e1ccedf9df1..c0cb8950264 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -2041,7 +2041,7 @@ public class GameAction { //shuffle List shuffledCards = Lists.newArrayList(p1.getZone(ZoneType.Library).getCards().threadSafeIterable()); - Collections.shuffle(shuffledCards); + CollectionUtil.shuffle(shuffledCards); //check a second hand List hand2 = shuffledCards.subList(0,p1.getMaxHandSize()); @@ -2171,7 +2171,7 @@ public class GameAction { if (!powerPlayers.isEmpty()) { List players = Lists.newArrayList(powerPlayers); - Collections.shuffle(players, MyRandom.getRandom()); + CollectionUtil.shuffle(players, MyRandom.getRandom()); return players.get(0); } @@ -2409,7 +2409,7 @@ public class GameAction { int numLookedAt = 0; if (toTop != null) { numLookedAt += toTop.size(); - Collections.reverse(toTop); // reverse to get the correct order + CollectionUtil.reverse(toTop); // reverse to get the correct order for (Card c : toTop) { moveToLibrary(c, cause, null); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index 996a3fc998a..fbbd31041b2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -17,6 +17,7 @@ import forge.game.player.PlayerView; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.util.CardTranslation; +import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.Localizer; import forge.util.TextUtil; @@ -174,7 +175,7 @@ public class DigEffect extends SpellAbilityEffect { CardCollection all = new CardCollection(p.getCardsIn(srcZone)); if (sa.hasParam("FromBottom")) { - Collections.reverse(all); + CollectionUtil.reverse(all); } int numToDig = Math.min(digNum, all.size()); @@ -356,7 +357,7 @@ public class DigEffect extends SpellAbilityEffect { if (sa.hasParam("ForgetOtherRemembered")) { host.clearRemembered(); } - Collections.reverse(movedCards); + CollectionUtil.reverse(movedCards); if (destZone1.equals(ZoneType.Battlefield) || destZone1.equals(ZoneType.Library)) { if (sa.hasParam("GainControl")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java index 2f41746a575..98d3f95e6e7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java @@ -1,6 +1,5 @@ package forge.game.ability.effects; -import java.util.Collections; import java.util.Map; import com.google.common.collect.Maps; @@ -18,6 +17,7 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; +import forge.util.CollectionUtil; import forge.util.Localizer; public class DigMultipleEffect extends SpellAbilityEffect { @@ -160,7 +160,7 @@ public class DigMultipleEffect extends SpellAbilityEffect { } if (libraryPosition2 != -1) { // Closest to top - Collections.reverse(afterOrder); + CollectionUtil.reverse(afterOrder); } for (final Card c : afterOrder) { final ZoneType origin = c.getZone().getZoneType(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java index 93460c57a8e..b4f3850b55b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java @@ -1,6 +1,5 @@ package forge.game.ability.effects; -import java.util.Collections; import java.util.Iterator; import java.util.Map; @@ -18,6 +17,7 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; +import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.Localizer; import forge.util.MyRandom; @@ -261,7 +261,7 @@ public class DigUntilEffect extends SpellAbilityEffect { } if (sa.hasParam("RevealRandomOrder")) { - Collections.shuffle(revealed, MyRandom.getRandom()); + CollectionUtil.shuffle(revealed, MyRandom.getRandom()); } if (sa.hasParam("NoMoveRevealed") || sequential) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/DraftEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DraftEffect.java index 77175b86dee..b323b101088 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DraftEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DraftEffect.java @@ -11,6 +11,7 @@ import forge.game.card.CardZoneTable; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; +import forge.util.CollectionUtil; import forge.util.Localizer; import java.util.*; @@ -51,7 +52,7 @@ import java.util.*; CardCollection drafted = new CardCollection(); for (int i = 0; i < numToDraft; i++) { - Collections.shuffle(spellbook); + CollectionUtil.shuffle(spellbook); List draftOptions = new ArrayList<>(); for (String name : spellbook.subList(0, 3)) { // Cardnames that include "," must use ";" instead in Spellbook$ (i.e. Tovolar; Dire Overlord) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java index 4ab1ab560b5..ce24ca6bf65 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java @@ -1,6 +1,5 @@ package forge.game.ability.effects; -import java.util.Collections; import java.util.List; import forge.game.ability.SpellAbilityEffect; @@ -9,6 +8,7 @@ import forge.game.card.CardCollectionView; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; +import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.MyRandom; @@ -34,7 +34,7 @@ public class ReorderZoneEffect extends SpellAbilityEffect { CardCollection list = new CardCollection(p.getCardsIn(zone)); if (shuffle) { - Collections.shuffle(list, MyRandom.getRandom()); + CollectionUtil.shuffle(list, MyRandom.getRandom()); p.getZone(zone).setCards(list); } else { CardCollectionView orderedCards = p.getController().orderMoveToZoneList(list, zone, sa); diff --git a/forge-game/src/main/java/forge/game/card/CardLists.java b/forge-game/src/main/java/forge/game/card/CardLists.java index 3688b8668a5..0414714b1c2 100644 --- a/forge-game/src/main/java/forge/game/card/CardLists.java +++ b/forge-game/src/main/java/forge/game/card/CardLists.java @@ -32,6 +32,7 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; import forge.game.staticability.StaticAbilityCrewValue; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.collect.FCollectionView; @@ -153,7 +154,7 @@ public class CardLists { } public static void shuffle(List list) { - Collections.shuffle(list, MyRandom.getRandom()); + CollectionUtil.shuffle(list, MyRandom.getRandom()); } public static CardCollection filterControlledBy(Iterable cardList, Player player) { diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 2570a8a36ee..31701856d3d 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -28,6 +28,7 @@ import forge.game.spellability.SpellAbilityStackInstance; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.item.PaperCard; +import forge.util.CollectionUtil; import forge.util.Expressions; import forge.util.TextUtil; import forge.util.collect.FCollection; @@ -623,13 +624,13 @@ public class CardProperty { } } else if (property.startsWith("TopGraveyardCreature")) { CardCollection cards = CardLists.filter(card.getOwner().getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES); - Collections.reverse(cards); + CollectionUtil.reverse(cards); if (cards.isEmpty() || !card.equals(cards.get(0))) { return false; } } else if (property.startsWith("TopGraveyard")) { final CardCollection cards = new CardCollection(card.getOwner().getCardsIn(ZoneType.Graveyard)); - Collections.reverse(cards); + CollectionUtil.reverse(cards); if (property.substring(12).matches("[0-9][0-9]?")) { int n = Integer.parseInt(property.substring(12)); int num = Math.min(n, cards.size()); @@ -665,7 +666,7 @@ public class CardProperty { if (property.startsWith("BottomLibrary_")) { cards = CardLists.getValidCards(cards, property.substring(14), sourceController, source, spellAbility); } - Collections.reverse(cards); + CollectionUtil.reverse(cards); if (cards.isEmpty() || !card.equals(cards.get(0))) { return false; } 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 37785ca8595..b06b20e0b17 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -53,19 +53,122 @@ public class Combat { private boolean legacyOrderCombatants; private AttackConstraints attackConstraints; // Defenders, as they are attacked by hostile forces - private final FCollection attackableEntries = new FCollection<>(); - + private FCollection _attackableEntries; + private FCollection attackableEntries() { + FCollection result = _attackableEntries; + if (result == null) { + synchronized (this) { + result = _attackableEntries; + if (result == null) { + result = new FCollection<>(); + _attackableEntries = result; + } + } + } + return _attackableEntries; + } // Keyed by attackable defender (player or planeswalker or battle) - private final Multimap attackedByBands = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); - private final Multimap blockedBands = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); + private Multimap _attackedByBands; + private Multimap attackedByBands() { + Multimap result = _attackedByBands; + if (result == null) { + synchronized (this) { + result = _attackedByBands; + if (result == null) { + result = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); + _attackedByBands = result; + } + } + } + return _attackedByBands; + } + private Multimap _blockedBands; + private Multimap blockedBands() { + Multimap result = _blockedBands; + if (result == null) { + synchronized (this) { + result = _blockedBands; + if (result == null) { + result = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); + _blockedBands = result; + } + } + } + return _blockedBands; + } - private final Map attackersOrderedForDamageAssignment = Maps.newHashMap(); - private final Map blockersOrderedForDamageAssignment = Maps.newHashMap(); - private CardCollection lkiCache = new CardCollection(); - private CardDamageMap damageMap = new CardDamageMap(); + private Map _attackersOrderedForDamageAssignment; + private Map attackersOrderedForDamageAssignment() { + Map result = _attackersOrderedForDamageAssignment; + if (result == null) { + synchronized (this) { + result = _attackersOrderedForDamageAssignment; + if (result == null) { + result = Maps.newHashMap(); + _attackersOrderedForDamageAssignment = result; + } + } + } + return _attackersOrderedForDamageAssignment; + } + private Map _blockersOrderedForDamageAssignment; + private Map blockersOrderedForDamageAssignment() { + Map result = _blockersOrderedForDamageAssignment; + if (result == null) { + synchronized (this) { + result = _blockersOrderedForDamageAssignment; + if (result == null) { + result = Maps.newHashMap(); + _blockersOrderedForDamageAssignment = result; + } + } + } + return _blockersOrderedForDamageAssignment; + } + private CardCollection _lkiCache; + private CardCollection lkiCache() { + CardCollection result = _lkiCache; + if (result == null) { + synchronized (this) { + result = _lkiCache; + if (result == null) { + result = new CardCollection(); + _lkiCache = result; + } + } + } + return _lkiCache; + } + private CardDamageMap _damageMap; + private CardDamageMap damageMap() { + CardDamageMap result = _damageMap; + if (result == null) { + synchronized (this) { + result = _damageMap; + if (result == null) { + result = new CardDamageMap(); + _damageMap = result; + } + } + } + return _damageMap; + } // List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW) - private CardCollection combatantsThatDealtFirstStrikeDamage = new CardCollection(); + private CardCollection _combatantsThatDealtFirstStrikeDamage; + private CardCollection combatantsThatDealtFirstStrikeDamage() { + CardCollection result = _combatantsThatDealtFirstStrikeDamage; + if (result == null) { + synchronized (this) { + result = _combatantsThatDealtFirstStrikeDamage; + if (result == null) { + result = new CardCollection(); + _combatantsThatDealtFirstStrikeDamage = result; + } + } + } + return _combatantsThatDealtFirstStrikeDamage; + } public Combat(final Player attacker) { playerWhoAttacks = attacker; @@ -75,12 +178,12 @@ public class Combat { public Combat(Combat combat, IEntityMap map) { playerWhoAttacks = map.map(combat.playerWhoAttacks); - for (GameEntity entry : combat.attackableEntries) { - attackableEntries.add(map.map(entry)); + for (GameEntity entry : combat.attackableEntries()) { + attackableEntries().add(map.map(entry)); } HashMap bandsMap = new HashMap<>(); - for (Entry entry : combat.attackedByBands.entries()) { + for (Entry entry : combat.attackedByBands().entries()) { AttackingBand origBand = entry.getValue(); ArrayList attackers = new ArrayList<>(); for (Card c : origBand.getAttackers()) { @@ -92,37 +195,37 @@ public class Combat { newBand.setBlocked(blocked); } bandsMap.put(origBand, newBand); - attackedByBands.put(map.map(entry.getKey()), newBand); + attackedByBands().put(map.map(entry.getKey()), newBand); } - for (Entry entry : combat.blockedBands.entries()) { - blockedBands.put(bandsMap.get(entry.getKey()), map.map(entry.getValue())); + for (Entry entry : combat.blockedBands().entries()) { + blockedBands().put(bandsMap.get(entry.getKey()), map.map(entry.getValue())); } - for (Entry entry : combat.attackersOrderedForDamageAssignment.entrySet()) { - attackersOrderedForDamageAssignment.put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); + for (Entry entry : combat.attackersOrderedForDamageAssignment().entrySet()) { + attackersOrderedForDamageAssignment().put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); } - for (Entry entry : combat.blockersOrderedForDamageAssignment.entrySet()) { - blockersOrderedForDamageAssignment.put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); + for (Entry entry : combat.blockersOrderedForDamageAssignment().entrySet()) { + blockersOrderedForDamageAssignment().put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); } // Note: Doesn't currently set up lkiCache, since it's just a cache and not strictly needed... - for (Table.Cell entry : combat.damageMap.cellSet()) { - damageMap.put(map.map(entry.getRowKey()), map.map(entry.getColumnKey()), entry.getValue()); + for (Table.Cell entry : combat.damageMap().cellSet()) { + damageMap().put(map.map(entry.getRowKey()), map.map(entry.getColumnKey()), entry.getValue()); } attackConstraints = new AttackConstraints(this); } public void initConstraints() { - attackableEntries.clear(); + attackableEntries().clear(); // Create keys for all possible attack targets - attackableEntries.addAll(CombatUtil.getAllPossibleDefenders(playerWhoAttacks)); + attackableEntries().addAll(CombatUtil.getAllPossibleDefenders(playerWhoAttacks)); attackConstraints = new AttackConstraints(this); } @Override public String toString() { StringBuilder sb = new StringBuilder(); - for (GameEntity defender : attackableEntries) { + for (GameEntity defender : attackableEntries()) { CardCollection attackers = getAttackersOf(defender); if (attackers.isEmpty()) { continue; @@ -148,13 +251,13 @@ public class Combat { CardCollection blockers = getAllBlockers(); //clear all combat-related collections - attackableEntries.clear(); - attackedByBands.clear(); - blockedBands.clear(); - attackersOrderedForDamageAssignment.clear(); - blockersOrderedForDamageAssignment.clear(); - lkiCache.clear(); - combatantsThatDealtFirstStrikeDamage.clear(); + attackableEntries().clear(); + attackedByBands().clear(); + blockedBands().clear(); + attackersOrderedForDamageAssignment().clear(); + blockersOrderedForDamageAssignment().clear(); + lkiCache().clear(); + combatantsThatDealtFirstStrikeDamage().clear(); //clear tracking for cards that care about "this combat" Game game = playerWhoAttacks.getGame(); @@ -186,7 +289,7 @@ public class Combat { return attackConstraints; } public final FCollectionView getDefenders() { - return attackableEntries; + return attackableEntries(); } //gets attacked player opponents (ignores planeswalkers) @@ -204,7 +307,7 @@ public class Combat { public final FCollection getDefendersControlledBy(Player who) { FCollection res = new FCollection<>(); - for (GameEntity ge : attackableEntries) { + for (GameEntity ge : attackableEntries()) { // if defender is the player himself or his cards if (ge == who || ge instanceof Card && ((Card) ge).getController() == who) { res.add(ge); @@ -214,15 +317,15 @@ public class Combat { } public final FCollectionView getDefendingPlayers() { - return new FCollection<>(Iterables.filter(attackableEntries, Player.class)); + return new FCollection<>(Iterables.filter(attackableEntries(), Player.class)); } public final CardCollection getDefendingPlaneswalkers() { - return CardLists.filter(Iterables.filter(attackableEntries, Card.class), CardPredicates.isType("Planeswalker")); + return CardLists.filter(Iterables.filter(attackableEntries(), Card.class), CardPredicates.isType("Planeswalker")); } public final CardCollection getDefendingBattles() { - return CardLists.filter(Iterables.filter(attackableEntries, Card.class), CardPredicates.isType("Battle")); + return CardLists.filter(Iterables.filter(attackableEntries(), Card.class), CardPredicates.isType("Battle")); } public final Map getAttackersAndDefenders() { @@ -230,14 +333,14 @@ public class Combat { } public final List getAttackingBandsOf(GameEntity defender) { - return Lists.newArrayList(attackedByBands.get(defender)); + return Lists.newArrayList(attackedByBands().get(defender)); } public final CardCollection getAttackersOf(GameEntity defender) { CardCollection result = new CardCollection(); - if (!attackedByBands.containsKey(defender)) + if (!attackedByBands().containsKey(defender)) return result; - for (AttackingBand v : attackedByBands.get(defender)) { + for (AttackingBand v : attackedByBands().get(defender)) { result.addAll(v.getAttackers()); } return result; @@ -247,7 +350,7 @@ public class Combat { addAttacker(c, defender, null); } public final void addAttacker(final Card c, GameEntity defender, AttackingBand band) { - Collection attackersOfDefender = attackedByBands.get(defender); + Collection attackersOfDefender = attackedByBands().get(defender); if (attackersOfDefender == null) { System.out.println("Trying to add Attacker " + c + " to missing defender " + defender); return; @@ -272,7 +375,7 @@ public class Combat { return getDefenderByAttacker(getBandOfAttacker(c)); } public final GameEntity getDefenderByAttacker(final AttackingBand c) { - for (Entry e : attackedByBands.entries()) { + for (Entry e : attackedByBands().entries()) { if (e.getValue() == c) { return e.getKey(); } @@ -305,12 +408,12 @@ public class Combat { if (c == null) { return null; } - for (AttackingBand ab : attackedByBands.values()) { + for (AttackingBand ab : attackedByBands().values()) { if (ab.contains(c)) { return ab; } } - CombatLki lki = lkiCache.get(c).getCombatLKI(); + CombatLki lki = lkiCache().get(c).getCombatLKI(); return lki == null || !lki.isAttacker ? null : lki.getFirstBand(); } @@ -323,12 +426,12 @@ public class Combat { } public final List getAttackingBands() { - return Lists.newArrayList(attackedByBands.values()); + return Lists.newArrayList(attackedByBands().values()); } public boolean isAttacking(Card card, GameEntity defender) { AttackingBand ab = getBandOfAttacker(card); - for (Entry ee : attackedByBands.entries()) { + for (Entry ee : attackedByBands().entries()) { if (ee.getValue() == ab) { return ee.getKey() == defender; } @@ -340,7 +443,7 @@ public class Combat { * Checks if a card is currently attacking, returns false if the card is not currently attacking, even if its LKI was. */ public final boolean isAttacking(Card card) { - for (AttackingBand ab : attackedByBands.values()) { + for (AttackingBand ab : attackedByBands().values()) { if (ab.contains(card)) { return true; } @@ -350,7 +453,7 @@ public class Combat { public final CardCollection getAttackers() { CardCollection result = new CardCollection(); - for (AttackingBand ab : attackedByBands.values()) { + for (AttackingBand ab : attackedByBands().values()) { result.addAll(ab.getAttackers()); } return result; @@ -368,9 +471,9 @@ public class Combat { public final void addBlocker(final Card attacker, final Card blocker) { final AttackingBand band = getBandOfAttackerNotNull(attacker); - blockedBands.put(band, blocker); + blockedBands().put(band, blocker); // If damage is already assigned, add this blocker as a "late entry" - if (blockersOrderedForDamageAssignment.containsKey(attacker)) { + if (blockersOrderedForDamageAssignment().containsKey(attacker)) { addBlockerToDamageAssignmentOrder(attacker, blocker); } blocker.updateBlockingForView(); @@ -379,7 +482,7 @@ public class Combat { // remove blocker from specific attacker public final void removeBlockAssignment(final Card attacker, final Card blocker) { AttackingBand band = getBandOfAttackerNotNull(attacker); - Collection cc = blockedBands.get(band); + Collection cc = blockedBands().get(band); if (cc != null) { cc.remove(blocker); } @@ -389,13 +492,13 @@ public class Combat { // remove blocker from everywhere public final void undoBlockingAssignment(final Card blocker) { CardCollection toRemove = new CardCollection(blocker); - blockedBands.values().removeAll(toRemove); + blockedBands().values().removeAll(toRemove); blocker.updateBlockingForView(); } public final CardCollection getAllBlockers() { CardCollection result = new CardCollection(); - for (Card blocker : blockedBands.values()) { + for (Card blocker : blockedBands().values()) { if (!result.contains(blocker)) { result.add(blocker); } @@ -406,8 +509,11 @@ public class Combat { public final CardCollection getDefendersCreatures() { CardCollection result = new CardCollection(); for (Card attacker : getAttackers()) { - CardCollection cc = getDefenderPlayerByAttacker(attacker).getCreaturesInPlay(); - result.addAll(cc); + Player defender = getDefenderPlayerByAttacker(attacker); + if (defender != null) { + CardCollection cc = defender.getCreaturesInPlay(); + result.addAll(cc); + } } return result; } @@ -417,13 +523,13 @@ public class Combat { return getBlockers(getBandOfAttacker(card)); } public final CardCollection getBlockers(final AttackingBand band) { - Collection blockers = blockedBands.get(band); + Collection blockers = blockedBands().get(band); return blockers == null ? new CardCollection() : new CardCollection(blockers); } public final CardCollection getAttackersBlockedBy(final Card blocker) { CardCollection blocked = new CardCollection(); - for (Entry s : blockedBands.entries()) { + for (Entry s : blockedBands().entries()) { if (s.getValue().equals(blocker)) { blocked.addAll(s.getKey().getAttackers()); } @@ -433,7 +539,7 @@ public class Combat { public final FCollectionView getAttackingBandsBlockedBy(Card blocker) { FCollection bands = new FCollection<>(); - for (Entry kv : blockedBands.entries()) { + for (Entry kv : blockedBands().entries()) { if (kv.getValue().equals(blocker)) { bands.add(kv.getKey()); } @@ -457,10 +563,10 @@ public class Combat { /** If there are multiple blockers, the Attacker declares the Assignment Order */ public void orderBlockersForDamageAssignment() { // this method performs controller's role List> blockersNeedManualOrdering = new ArrayList<>(); - for (AttackingBand band : attackedByBands.values()) { + for (AttackingBand band : attackedByBands().values()) { if (band.isEmpty()) continue; - Collection blockers = blockedBands.get(band); + Collection blockers = blockedBands().get(band); if (blockers == null || blockers.isEmpty()) { continue; } @@ -484,13 +590,13 @@ public class Combat { /** If there are multiple blockers, the Attacker declares the Assignment Order */ public void orderBlockersForDamageAssignment(Card attacker, CardCollection blockers) { // this method performs controller's role if (blockers.size() <= 1 || !this.legacyOrderCombatants) { - blockersOrderedForDamageAssignment.put(attacker, new CardCollection(blockers)); + blockersOrderedForDamageAssignment().put(attacker, new CardCollection(blockers)); return; } // Damage Ordering needs to take cards like Melee into account, is that happening? CardCollection orderedBlockers = playerWhoAttacks.getController().orderBlockers(attacker, blockers); // we know there's a list - blockersOrderedForDamageAssignment.put(attacker, orderedBlockers); + blockersOrderedForDamageAssignment().put(attacker, orderedBlockers); // Display the chosen order of blockers in the log // TODO: this is best done via a combat panel update @@ -517,15 +623,15 @@ public class Combat { * @param blocker the blocking creature. */ public void addBlockerToDamageAssignmentOrder(Card attacker, Card blocker) { - final CardCollection oldBlockers = blockersOrderedForDamageAssignment.get(attacker); - if (oldBlockers == null || oldBlockers.isEmpty()) { - blockersOrderedForDamageAssignment.put(attacker, new CardCollection(blocker)); - } else if (this.legacyOrderCombatants) { + final CardCollection oldBlockers = blockersOrderedForDamageAssignment().get(attacker); + if (oldBlockers == null || oldBlockers.isEmpty()) { + blockersOrderedForDamageAssignment().put(attacker, new CardCollection(blocker)); + } else if (this.legacyOrderCombatants) { CardCollection orderedBlockers = playerWhoAttacks.getController().orderBlocker(attacker, blocker, oldBlockers); - blockersOrderedForDamageAssignment.put(attacker, orderedBlockers); - } else { + blockersOrderedForDamageAssignment().put(attacker, orderedBlockers); + } else { oldBlockers.add(blocker); - blockersOrderedForDamageAssignment.put(attacker, oldBlockers); + blockersOrderedForDamageAssignment().put(attacker, oldBlockers); } } @@ -544,19 +650,19 @@ public class Combat { CardCollection orderedAttacker = attackers.size() <= 1 || !this.legacyOrderCombatants ? attackers : blockerCtrl.getController().orderAttackers(blocker, attackers); // Damage Ordering needs to take cards like Melee into account, is that happening? - attackersOrderedForDamageAssignment.put(blocker, orderedAttacker); + attackersOrderedForDamageAssignment().put(blocker, orderedAttacker); } // removes references to this attacker from all indices and orders public void unregisterAttacker(final Card c, AttackingBand ab) { - blockersOrderedForDamageAssignment.remove(c); + blockersOrderedForDamageAssignment().remove(c); - Collection blockers = blockedBands.get(ab); + Collection blockers = blockedBands().get(ab); if (blockers != null) { for (Card b : blockers) { // Clear removed attacker from assignment order - if (attackersOrderedForDamageAssignment.containsKey(b)) { - attackersOrderedForDamageAssignment.get(b).remove(c); + if (attackersOrderedForDamageAssignment().containsKey(b)) { + attackersOrderedForDamageAssignment().get(b).remove(c); } } } @@ -582,10 +688,10 @@ public class Combat { // removes references to this defender from all indices and orders public void unregisterDefender(final Card c, AttackingBand bandBeingBlocked) { - attackersOrderedForDamageAssignment.remove(c); + attackersOrderedForDamageAssignment().remove(c); for (Card atk : bandBeingBlocked.getAttackers()) { - if (blockersOrderedForDamageAssignment.containsKey(atk)) { - blockersOrderedForDamageAssignment.get(atk).remove(c); + if (blockersOrderedForDamageAssignment().containsKey(atk)) { + blockersOrderedForDamageAssignment().get(atk).remove(c); } } } @@ -601,16 +707,16 @@ public class Combat { } // if not found in attackers, look for this card in blockers - for (Entry be : blockedBands.entries()) { + for (Entry be : blockedBands().entries()) { if (be.getValue().equals(c)) { unregisterDefender(c, be.getKey()); } } - for (Card battleOrPW : Iterables.filter(attackableEntries, Card.class)) { + for (Card battleOrPW : Iterables.filter(attackableEntries(), Card.class)) { if (battleOrPW.equals(c)) { Multimap attackerBuffer = ArrayListMultimap.create(); - Collection bands = attackedByBands.get(c); + Collection bands = attackedByBands().get(c); for (AttackingBand abDef : bands) { unregisterDefender(c, abDef); // Rule 506.4c workaround to keep creatures in combat @@ -620,20 +726,20 @@ public class Combat { attackerBuffer.put(fake, abDef); } bands.clear(); - attackedByBands.putAll(attackerBuffer); + attackedByBands().putAll(attackerBuffer); break; } } // remove card from map - while (blockedBands.values().remove(c)); + while (blockedBands().values().remove(c)); c.updateBlockingForView(); } public final boolean removeAbsentCombatants() { // CR 506.4 iterate all attackers and remove illegal declarations CardCollection missingCombatants = new CardCollection(); - for (Entry ee : attackedByBands.entries()) { + for (Entry ee : attackedByBands().entries()) { for (Card c : ee.getValue().getAttackers()) { if (!c.isInPlay() || !c.isCreature()) { missingCombatants.add(c); @@ -647,7 +753,7 @@ public class Combat { } } - for (Entry be : blockedBands.entries()) { + for (Entry be : blockedBands().entries()) { Card blocker = be.getValue(); if (!blocker.isInPlay() || !blocker.isCreature()) { missingCombatants.add(blocker); @@ -666,8 +772,8 @@ public class Combat { public final void fireTriggersForUnblockedAttackers(final Game game) { boolean bFlag = false; List defenders = Lists.newArrayList(); - for (AttackingBand ab : attackedByBands.values()) { - Collection blockers = blockedBands.get(ab); + for (AttackingBand ab : attackedByBands().values()) { + Collection blockers = blockedBands().get(ab); boolean isBlocked = blockers != null && !blockers.isEmpty(); ab.setBlocked(isBlocked); @@ -706,23 +812,23 @@ public class Combat { } if (firstStrikeDamage) { - combatantsThatDealtFirstStrikeDamage.add(blocker); + combatantsThatDealtFirstStrikeDamage().add(blocker); } // Run replacement effects blocker.getGame().getReplacementHandler().run(ReplacementType.AssignDealDamage, AbilityKey.mapFromAffected(blocker)); - CardCollection attackers = attackersOrderedForDamageAssignment.get(blocker); + CardCollection attackers = attackersOrderedForDamageAssignment().get(blocker); final int damage = blocker.getNetCombatDamage(); - if (!attackers.isEmpty()) { + if (attackers != null && !attackers.isEmpty()) { Player attackingPlayer = getAttackingPlayer(); Player assigningPlayer = blocker.getController(); Player defender = null; boolean divideCombatDamageAsChoose = blocker.hasKeyword("You may assign CARDNAME's combat damage divided as you choose among " + - "defending player and/or any number of creatures they control.") + "defending player and/or any number of creatures they control.") && blocker.getController().getController().confirmStaticApplication(blocker, PlayerActionConfirmMode.AlternativeDamageAssignment, Localizer.getInstance().getMessage("lblAssignCombatDamageAsChoose", CardTranslation.getTranslatedName(blocker.getName())), null); @@ -740,10 +846,10 @@ public class Combat { for (Entry dt : map.entrySet()) { // Butcher Orgg if (dt.getKey() == null && dt.getValue() > 0) { - damageMap.put(blocker, defender, dt.getValue()); + damageMap().put(blocker, defender, dt.getValue()); } else { dt.getKey().addAssignedDamage(dt.getValue(), blocker); - damageMap.put(blocker, dt.getKey(), dt.getValue()); + damageMap().put(blocker, dt.getKey(), dt.getValue()); } } } @@ -764,7 +870,7 @@ public class Combat { } if (firstStrikeDamage) { - combatantsThatDealtFirstStrikeDamage.add(attacker); + combatantsThatDealtFirstStrikeDamage().add(attacker); } // Run replacement effects @@ -785,7 +891,7 @@ public class Combat { GameEntity defender = getDefenderByAttacker(band); Player assigningPlayer = getAttackingPlayer(); - orderedBlockers = blockersOrderedForDamageAssignment.get(attacker); + orderedBlockers = blockersOrderedForDamageAssignment().get(attacker); // Defensive Formation is very similar to Banding with Blockers // It allows the defending player to assign damage instead of the attacking player if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) { @@ -814,8 +920,8 @@ public class Combat { attacker.hasKeyword("You may assign CARDNAME's combat damage divided as you choose among " + "defending player and/or any number of creatures they control.") && assigningPlayer.getController().confirmStaticApplication(attacker, PlayerActionConfirmMode.AlternativeDamageAssignment, - Localizer.getInstance().getMessage("lblAssignCombatDamageAsChoose", - CardTranslation.getTranslatedName(attacker.getName())), null); + Localizer.getInstance().getMessage("lblAssignCombatDamageAsChoose", + CardTranslation.getTranslatedName(attacker.getName())), null); if (defender instanceof Card && divideCombatDamageAsChoose) { defender = getDefenderPlayerByAttacker(attacker); } @@ -849,7 +955,7 @@ public class Combat { } if (assignToPlayer) { attackers.remove(attacker); - damageMap.put(attacker, defender, damageDealt); + damageMap().put(attacker, defender, damageDealt); } else if (orderedBlockers == null || orderedBlockers.isEmpty()) { attackers.remove(attacker); @@ -857,9 +963,14 @@ public class Combat { final SpellAbility emptySA = new SpellAbility.EmptySa(ApiType.Cleanup, attacker); Card chosen = attacker.getController().getController().chooseCardsForEffect(getDefendersCreatures(), emptySA, Localizer.getInstance().getMessage("lblChooseCreature"), 1, 1, false, null).get(0); - damageMap.put(attacker, chosen, damageDealt); + damageMap().put(attacker, chosen, damageDealt); } else if (trampler || !band.isBlocked()) { // this is called after declare blockers, no worries 'bout nulls in isBlocked - damageMap.put(attacker, defender, damageDealt); + if (defender == null) { + defender = getDefenderPlayerByAttacker(attacker); + System.err.println("[COMBAT] defender is null, getDefenderPlayerByAttacker(attacker) result: " + defender); + } + // this will fail if defender is null, and it doesn't allow null values.. + damageMap().put(attacker, defender, damageDealt); } // No damage happens if blocked but no blockers left } else { Map map = assigningPlayer.getController().assignCombatDamage(attacker, orderedBlockers, attackers, @@ -879,11 +990,11 @@ public class Combat { if (defender instanceof Card) { ((Card) defender).addAssignedDamage(dt.getValue(), attacker); } - damageMap.put(attacker, defender, dt.getValue()); + damageMap().put(attacker, defender, dt.getValue()); } } else { dt.getKey().addAssignedDamage(dt.getValue(), attacker); - damageMap.put(attacker, dt.getKey(), dt.getValue()); + damageMap().put(attacker, dt.getKey(), dt.getValue()); } } } // if !hasFirstStrike ... @@ -900,7 +1011,7 @@ public class Combat { if (firstStrikeDamage && combatant.hasFirstStrike()) { return true; } - return !firstStrikeDamage && !combatantsThatDealtFirstStrikeDamage.contains(combatant); + return !firstStrikeDamage && !combatantsThatDealtFirstStrikeDamage().contains(combatant); } public final boolean assignCombatDamage(boolean firstStrikeDamage) { @@ -908,7 +1019,7 @@ public class Combat { assignedDamage |= assignBlockersDamage(firstStrikeDamage); if (!firstStrikeDamage) { // Clear first strike damage list since it doesn't matter anymore - combatantsThatDealtFirstStrikeDamage.clear(); + combatantsThatDealtFirstStrikeDamage().clear(); } return assignedDamage; } @@ -920,7 +1031,7 @@ public class Combat { CardDamageMap preventMap = new CardDamageMap(); GameEntityCounterTable counterTable = new GameEntityCounterTable(); - game.getAction().dealDamage(true, damageMap, preventMap, counterTable, null); + game.getAction().dealDamage(true, damageMap(), preventMap, counterTable, null); // copy last state again for dying replacement effects game.copyLastState(); @@ -933,7 +1044,7 @@ public class Combat { public final CardCollection getUnblockedAttackers() { CardCollection unblocked = new CardCollection(); - for (AttackingBand ab : attackedByBands.values()) { + for (AttackingBand ab : attackedByBands().values()) { if (Boolean.FALSE.equals(ab.isBlocked())) { unblocked.addAll(ab.getAttackers()); } @@ -942,13 +1053,13 @@ public class Combat { } public boolean isPlayerAttacked(Player who) { - for (GameEntity defender : attackedByBands.keySet()) { + for (GameEntity defender : attackedByBands().keySet()) { Card defenderAsCard = defender instanceof Card ? (Card)defender : null; if ((null != defenderAsCard && (defenderAsCard.getController() != who && defenderAsCard.getProtectingPlayer() != who)) || - (null == defenderAsCard && defender != who)) { + (null == defenderAsCard && defender != who)) { continue; // defender is not related to player 'who' } - for (AttackingBand ab : attackedByBands.get(defender)) { + for (AttackingBand ab : attackedByBands().get(defender)) { if (!ab.isEmpty()) { return true; } @@ -958,7 +1069,7 @@ public class Combat { } public boolean isBlocking(Card blocker) { - if (blockedBands.containsValue(blocker)) { + if (blockedBands().containsValue(blocker)) { return true; // is blocking something at the moment } @@ -966,13 +1077,13 @@ public class Combat { return false; } - CombatLki lki = lkiCache.get(blocker).getCombatLKI(); + CombatLki lki = lkiCache().get(blocker).getCombatLKI(); return null != lki && !lki.isAttacker; // was blocking something anyway } public boolean isBlocking(Card blocker, Card attacker) { AttackingBand ab = getBandOfAttacker(attacker); - Collection blockers = blockedBands.get(ab); + Collection blockers = blockedBands().get(ab); if (blockers != null && blockers.contains(blocker)) { return true; // is blocking the attacker's band at the moment } @@ -981,7 +1092,7 @@ public class Combat { return false; } - CombatLki lki = lkiCache.get(blocker).getCombatLKI(); + CombatLki lki = lkiCache().get(blocker).getCombatLKI(); return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band } @@ -994,7 +1105,7 @@ public class Combat { final boolean isAttacker = attackingBand != null; if (isAttacker) { boolean found = false; - for (AttackingBand ab : attackedByBands.values()) { + for (AttackingBand ab : attackedByBands().values()) { if (ab.contains(lki)) { found = true; break; @@ -1009,7 +1120,7 @@ public class Combat { return null; // card was not even in combat } } - lkiCache.add(lki); + lkiCache().add(lki); final FCollectionView relatedBands = isAttacker ? new FCollection<>(attackingBand) : attackersBlocked; return new CombatLki(isAttacker, relatedBands); } diff --git a/forge-game/src/main/java/forge/game/cost/CostPayment.java b/forge-game/src/main/java/forge/game/cost/CostPayment.java index 7c4ebf3fd2f..d7f48b5aa98 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPayment.java +++ b/forge-game/src/main/java/forge/game/cost/CostPayment.java @@ -229,8 +229,8 @@ public class CostPayment extends ManaConversionMatrix { * @return a {@link forge.game.mana.Mana} object. */ public static Mana getMana(final Player player, final ManaCostShard shard, final SpellAbility saBeingPaidFor, - final byte colorsPaid, Map xManaCostPaidByColor) { - final List> weightedOptions = selectManaToPayFor(player.getManaPool(), shard, + final byte colorsPaid, Map xManaCostPaidByColor) { // player.getManaPool() is not threadsafe if this is called somwewhere concurrently + final List> weightedOptions = selectManaToPayFor(new ManaPool(player), shard, saBeingPaidFor, colorsPaid, xManaCostPaidByColor); // Exclude border case diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index a9e7483e414..bed87be2071 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -23,6 +23,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimaps; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; @@ -51,7 +53,20 @@ import forge.game.staticability.StaticAbilityUnspentMana; */ public class ManaPool extends ManaConversionMatrix implements Iterable { private final Player owner; - private final ArrayListMultimap floatingMana = ArrayListMultimap.create(); + private ListMultimap _floatingMana; + private ListMultimap floatingMana() { + ListMultimap result = _floatingMana; + if (result == null) { + synchronized (this) { + result = _floatingMana; + if (result == null) { + result = Multimaps.synchronizedListMultimap(ArrayListMultimap.create()); + _floatingMana = result; + } + } + } + return _floatingMana; + } public ManaPool(final Player player) { owner = player; @@ -59,7 +74,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { } public final int getAmountOfColor(final byte color) { - Collection ofColor = floatingMana.get(color); + Collection ofColor = floatingMana().get(color); return ofColor == null ? 0 : ofColor.size(); } @@ -67,7 +82,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { addMana(mana, true); } public void addMana(final Mana mana, boolean updateView) { - floatingMana.put(mana.getColor(), mana); + floatingMana().put(mana.getColor(), mana); if (updateView) { owner.updateManaForView(); owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Added, mana)); @@ -88,7 +103,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { *

*/ public final boolean willManaBeLostAtEndOfPhase() { - if (floatingMana.isEmpty()) { + if (floatingMana().isEmpty()) { return false; } @@ -114,13 +129,13 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { public final void resetPool() { // This should only be used to reset the pool to empty by things like restores. - floatingMana.clear(); + floatingMana().clear(); } public final List clearPool(boolean isEndOfPhase) { // isEndOfPhase parameter: true = end of phase, false = mana drain effect List cleared = Lists.newArrayList(); - if (floatingMana.isEmpty()) { return cleared; } + if (floatingMana().isEmpty()) { return cleared; } Byte convertTo = null; @@ -128,17 +143,17 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { final Map runParams = AbilityKey.mapFromAffected(owner); runParams.put(AbilityKey.Mana, "C"); switch (owner.getGame().getReplacementHandler().run(ReplacementType.LoseMana, runParams)) { - case NotReplaced: - break; - case Skipped: - return cleared; - default: - convertTo = ManaAtom.fromName((String) runParams.get(AbilityKey.Mana)); - break; + case NotReplaced: + break; + case Skipped: + return cleared; + default: + convertTo = ManaAtom.fromName((String) runParams.get(AbilityKey.Mana)); + break; } - final List keys = Lists.newArrayList(floatingMana.keySet()); + final List keys = Lists.newArrayList(floatingMana().keySet()); if (isEndOfPhase) { keys.removeAll(StaticAbilityUnspentMana.getManaToKeep(owner)); } @@ -147,7 +162,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { } for (Byte b : keys) { - Collection cm = floatingMana.get(b); + Collection cm = floatingMana().get(b); final List pMana = Lists.newArrayList(); if (isEndOfPhase && !owner.getGame().getPhaseHandler().is(PhaseType.CLEANUP)) { for (final Mana mana : cm) { @@ -163,7 +178,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { } else { cleared.addAll(cm); cm.clear(); - floatingMana.putAll(b, pMana); + floatingMana().putAll(b, pMana); } } @@ -174,12 +189,12 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { private void convertManaColor(final byte originalColor, final byte toColor) { List convert = Lists.newArrayList(); - Collection cm = floatingMana.get(originalColor); + Collection cm = floatingMana().get(originalColor); for (Mana m : cm) { convert.add(new Mana(toColor, m.getSourceCard(), m.getManaAbility())); } cm.clear(); - floatingMana.putAll(toColor, convert); + floatingMana().putAll(toColor, convert); owner.updateManaForView(); } @@ -187,7 +202,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { return removeMana(mana, true); } public boolean removeMana(final Mana mana, boolean updateView) { - boolean result = floatingMana.remove(mana.getColor(), mana); + boolean result = floatingMana().remove(mana.getColor(), mana); if (result && updateView) { owner.updateManaForView(); owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Removed, mana)); @@ -216,7 +231,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { public boolean tryPayCostWithColor(byte colorCode, SpellAbility saPaidFor, ManaCostBeingPaid manaCost, List manaSpentToPay) { Mana manaFound = null; - Collection cm = floatingMana.get(colorCode); + Collection cm = floatingMana().get(colorCode); for (final Mana mana : cm) { if (mana.getManaAbility() != null && !mana.getManaAbility().meetsManaRestrictions(saPaidFor)) { @@ -253,11 +268,11 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { } public final boolean isEmpty() { - return floatingMana.isEmpty(); + return floatingMana().isEmpty(); } public final int totalMana() { - return floatingMana.values().size(); + return floatingMana().values().size(); } //Account for mana part of ability when undoing it @@ -265,7 +280,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { if (ma == null) { return false; } - if (floatingMana.isEmpty()) { + if (floatingMana().isEmpty()) { return false; } @@ -274,7 +289,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { boolean manaNotAccountedFor = false; // loop over mana produced by mana ability for (Mana mana : ma.getLastManaProduced()) { - Collection poolLane = floatingMana.get(mana.getColor()); + Collection poolLane = floatingMana().get(mana.getColor()); if (poolLane != null && poolLane.contains(mana)) { removeFloating.add(mana); @@ -321,7 +336,6 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { * Checks if the given mana cost can be paid from floating mana. * @param cost mana cost to pay for * @param sa ability to pay for - * @param player activating player * @param test actual payment is made if this is false * @param manaSpentToPay list of mana spent * @return whether the floating mana is sufficient to pay the cost fully @@ -358,7 +372,10 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { @Override public Iterator iterator() { - return floatingMana.values().iterator(); + // use synchronizedListMultimap + synchronized (floatingMana()) { + return floatingMana().values().iterator(); + } } } diff --git a/forge-game/src/main/java/forge/game/mana/ManaRefundService.java b/forge-game/src/main/java/forge/game/mana/ManaRefundService.java index 5f73179aa6f..324cc8364bf 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaRefundService.java +++ b/forge-game/src/main/java/forge/game/mana/ManaRefundService.java @@ -6,8 +6,8 @@ import forge.game.event.GameEventZone; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; +import forge.util.CollectionUtil; -import java.util.Collections; import java.util.List; public class ManaRefundService { @@ -39,7 +39,7 @@ public class ManaRefundService { List payingAbilities = sa.getPayingManaAbilities(); // start with the most recent - Collections.reverse(payingAbilities); + CollectionUtil.reverse(payingAbilities); for (final SpellAbility am : payingAbilities) { // What if am is owned by a different player? 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 75bd3d72c02..9d690cf4a62 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1142,7 +1142,7 @@ public class Player extends GameEntity implements Comparable { } if (toTop != null) { - Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. + CollectionUtil.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. for (Card c : toTop) { getGame().getAction().moveToLibrary(c, cause, params); numToTop++; @@ -1669,7 +1669,7 @@ public class Player extends GameEntity implements Comparable { final CardCollection list = new CardCollection(getCardsIn(ZoneType.Library)); // Note: Shuffling once is sufficient. - Collections.shuffle(list, MyRandom.getRandom()); + CollectionUtil.shuffle(list, MyRandom.getRandom()); getZone(ZoneType.Library).setCards(getController().cheatShuffle(list)); 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 43781bb594d..5d61b284d94 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -118,7 +118,23 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private boolean optionalTrigger = false; private ReplacementEffect replacementEffect = null; private int sourceTrigger = -1; - private List triggerRemembered = Lists.newArrayList(); + private List _triggerRemembered; + private List triggerRemembered() { + List result = _triggerRemembered; + if (result == null) { + synchronized (this) { + result = _triggerRemembered; + if (result == null) { + result = Lists.newArrayList(); + _triggerRemembered = result; + } + } + } + return _triggerRemembered; + } + private void _setTriggerRemembered(List tr) { + _triggerRemembered = tr; + } private AlternativeCost altCost = null; private EnumSet optionalCosts = EnumSet.noneOf(OptionalCost.class); @@ -126,30 +142,153 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private boolean aftermath = false; + private boolean skip = false; /** The pay costs. */ private Cost payCosts; private SpellAbilityRestriction restrictions; - private SpellAbilityCondition conditions = new SpellAbilityCondition(); + private SpellAbilityCondition _conditions; + private SpellAbilityCondition conditions() { + SpellAbilityCondition result = _conditions; + if (result == null) { + synchronized (this) { + result = _conditions; + if (result == null) { + result = new SpellAbilityCondition(); + _conditions = result; + } + } + } + return _conditions; + } + private void _setConditions(SpellAbilityCondition c) { + _conditions = c; + } private AbilitySub subAbility; - private Map additionalAbilities = Maps.newHashMap(); - private Map> additionalAbilityLists = Maps.newHashMap(); + private Map _additionalAbilities; + private Map additionalAbilities() { + Map result = _additionalAbilities; + if (result == null) { + synchronized (this) { + result = _additionalAbilities; + if (result == null) { + result = Maps.newHashMap(); + _additionalAbilities = result; + } + } + } + return _additionalAbilities; + } + private void _setAdditionalAbilities(Map aa) { + _additionalAbilities = aa; + } + private Map> _additionalAbilityLists; + private Map> additionalAbilityLists() { + Map> result = _additionalAbilityLists; + if (result == null) { + synchronized (this) { + result = _additionalAbilityLists; + if (result == null) { + result = Maps.newHashMap(); + _additionalAbilityLists = result; + } + } + } + return _additionalAbilityLists; + } + private void _setAdditionalAbilityLists(Map> al) { + _additionalAbilityLists = al; + } protected ApiType api = null; - private List payingMana = Lists.newArrayList(); - private List paidAbilities = Lists.newArrayList(); + private List _payingMana; + private List payingMana() { + List result = _payingMana; + if (result == null) { + synchronized (this) { + result = _payingMana; + if (result == null) { + result = Lists.newArrayList(); + _payingMana = result; + } + } + } + return _payingMana; + } + private void _setPayingMana(List m) { + _payingMana = m; + } + private List _paidAbilities; + private List paidAbilities() { + List result = _paidAbilities; + if (result == null) { + synchronized (this) { + result = _paidAbilities; + if (result == null) { + result = Lists.newArrayList(); + _paidAbilities = result; + } + } + } + return _paidAbilities; + } + private void _setPaidAbilities(List p) { + _paidAbilities = p; + } private Integer xManaCostPaid = null; - private TreeBasedTable paidLists = TreeBasedTable.create(); + private TreeBasedTable _paidLists; + private TreeBasedTable paidLists() { + TreeBasedTable result = _paidLists; + if (result == null) { + synchronized (this) { + result = _paidLists; + if (result == null) { + result = TreeBasedTable.create(); + _paidLists = result; + } + } + } + return _paidLists; + } + private void _setPaidLists(TreeBasedTable p) { + _paidLists = p; + } private EnumMap triggeringObjects = AbilityKey.newMap(); private EnumMap replacingObjects = AbilityKey.newMap(); - private final List pipsToReduce = new ArrayList<>(); + private List _pipsToReduce; + private List pipsToReduce() { + List result = _pipsToReduce; + if (result == null) { + synchronized (this) { + result = _pipsToReduce; + if (result == null) { + result = new ArrayList<>(); + _pipsToReduce = result; + } + } + } + return _pipsToReduce; + } private List chosenList = null; - private CardCollection tappedForConvoke = new CardCollection(); + private CardCollection _tappedForConvoke; + private CardCollection tappedForConvoke() { + CardCollection result = _tappedForConvoke; + if (result == null) { + synchronized (this) { + result = _tappedForConvoke; + if (result == null) { + result = new CardCollection(); + _tappedForConvoke = result; + } + } + } + return _tappedForConvoke; + } private Card sacrificedAsOffering; private Card sacrificedAsEmerge; @@ -163,7 +302,26 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private boolean isCastFromPlayEffect = false; private TargetRestrictions targetRestrictions; - private TargetChoices targetChosen = new TargetChoices(); + private TargetChoices _targetChosen; + private TargetChoices targetChosen() { + TargetChoices result = _targetChosen; + if (result == null) { + synchronized (this) { + result = _targetChosen; + if (result == null) { + result = new TargetChoices(); + _targetChosen = result; + } + } + } + return _targetChosen; + } + private void _setTargetChosen(TargetChoices c) { + _targetChosen = c; + } + private void resetTargetChosen() { + _targetChosen = new TargetChoices(); + } private Integer dividedValue = null; @@ -174,7 +332,20 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private CardCollection lastStateBattlefield; private CardCollection lastStateGraveyard; - private CardCollection rollbackEffects = new CardCollection(); + private CardCollection _rollbackEffects; + private CardCollection rollbackEffects() { + CardCollection result = _rollbackEffects; + if (result == null) { + synchronized (this) { + result = _rollbackEffects; + if (result == null) { + result = new CardCollection(); + _rollbackEffects = result; + } + } + } + return _rollbackEffects; + } private CardDamageMap damageMap; private CardDamageMap preventMap; @@ -242,12 +413,12 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (subAbility != null) { subAbility.setHostCard(c); } - for (SpellAbility sa : additionalAbilities.values()) { + for (SpellAbility sa : additionalAbilities().values()) { if (sa.getHostCard() != c) { sa.setHostCard(c); } } - for (List list : additionalAbilityLists.values()) { + for (List list : additionalAbilityLists().values()) { for (AbilitySub sa : list) { if (sa.getHostCard() != c) { sa.setHostCard(c); @@ -266,10 +437,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (subAbility != null) { subAbility.setKeyword(kw); } - for (SpellAbility sa : additionalAbilities.values()) { + for (SpellAbility sa : additionalAbilities().values()) { sa.setKeyword(kw); } - for (List list : additionalAbilityLists.values()) { + for (List list : additionalAbilityLists().values()) { for (AbilitySub sa : list) { sa.setKeyword(kw); } @@ -467,10 +638,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (subAbility != null) { updated |= subAbility.setActivatingPlayer(player, lki); } - for (SpellAbility sa : additionalAbilities.values()) { + for (SpellAbility sa : additionalAbilities().values()) { updated |= sa.setActivatingPlayer(player, lki); } - for (List list : additionalAbilityLists.values()) { + for (List list : additionalAbilityLists().values()) { for (AbilitySub sa : list) { updated |= sa.setActivatingPlayer(player, lki); } @@ -648,10 +819,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public SpellAbilityCondition getConditions() { - return conditions; + return conditions(); } public final void setConditions(final SpellAbilityCondition condition) { - conditions = condition; + _setConditions(condition); } public boolean metConditions() { @@ -659,13 +830,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public List getPayingMana() { - return payingMana; + return payingMana(); } public void setPayingMana(List paying) { - payingMana = Lists.newArrayList(paying); + _setPayingMana(Lists.newArrayList(paying)); } public final void clearManaPaid() { - payingMana.clear(); + payingMana( ).clear(); } public final int getSpendPhyrexianMana() { @@ -727,42 +898,42 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public ColorSet getPayingColors() { byte colors = 0; - for (Mana m : payingMana) { + for (Mana m : payingMana()) { colors |= m.getColor(); } return ColorSet.fromMask(colors); } public List getPayingManaAbilities() { - return paidAbilities; + return paidAbilities(); } // Combined PaidLists public TreeBasedTable getPaidHash() { - return paidLists; + return paidLists(); } public void setPaidHash(final TreeBasedTable hash) { - paidLists = TreeBasedTable.create(hash); + _setPaidLists(TreeBasedTable.create(hash)); } // use if it doesn't matter if payment was caused by extrinsic cost modifier public Iterable getPaidList(final String str) { - return Iterables.concat(paidLists.row(str).values()); + return Iterables.concat(paidLists().row(str).values()); } public CardCollection getPaidList(final String str, final boolean intrinsic) { - return paidLists.get(str, intrinsic); + return paidLists().get(str, intrinsic); } public void addCostToHashList(final Card c, final String str, final boolean intrinsic) { - if (!paidLists.contains(str, intrinsic)) { - paidLists.put(str, intrinsic, new CardCollection()); + if (!paidLists().contains(str, intrinsic)) { + paidLists().put(str, intrinsic, new CardCollection()); } - paidLists.get(str, intrinsic).add(c); + paidLists().get(str, intrinsic).add(c); } public void resetPaidHash() { - paidLists.clear(); + paidLists().clear(); } public Iterable getOptionalCosts() { @@ -774,7 +945,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit optionalCosts = EnumSet.copyOf(optionalCosts); optionalCosts.add(cost); if (!cost.getPip().isEmpty()) { - pipsToReduce.add(cost.getPip()); + pipsToReduce().add(cost.getPip()); } } @@ -788,7 +959,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public boolean isKicked() { return isOptionalCostPaid(OptionalCost.Kicker1) || isOptionalCostPaid(OptionalCost.Kicker2) || - getRootAbility().getOptionalKeywordAmount(Keyword.MULTIKICKER) > 0; + getRootAbility().getOptionalKeywordAmount(Keyword.MULTIKICKER) > 0; } public boolean isEntwine() { @@ -834,13 +1005,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit @Override public List getTriggerRemembered() { - return triggerRemembered; + return triggerRemembered(); } public void setTriggerRemembered(List list) { - triggerRemembered = list; + _setTriggerRemembered(list); } public void resetTriggerRemembered() { - triggerRemembered = Lists.newArrayList(); + _setTriggerRemembered(Lists.newArrayList()); } public Map getReplacingObjects() { @@ -866,7 +1037,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public void resetOnceResolved() { //resetPaidHash(); // FIXME: if uncommented, breaks Dragon Presence, e.g. Orator of Ojutai + revealing a Dragon from hand. - // Is it truly necessary at this point? The paid hash seems to be reset on all SA instance operations. + // Is it truly necessary at this point? The paid hash seems to be reset on all SA instance operations. // Epic spell keeps original targets if (!isEpic()) { resetTargets(); @@ -898,7 +1069,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public void setStackDescription(final String s) { originalStackDescription = s; stackDescription = originalStackDescription; - if (StringUtils.isEmpty(description) && StringUtils.isEmpty(hostCard.getView().getText())) { + // FIXME: why would the view is null? freezed tracker and the view is not composed yet? + String compareHostText = hostCard.getView() == null ? "" : hostCard.getView().getText(); + if (StringUtils.isEmpty(description) && StringUtils.isEmpty(compareHostText)) { setDescription(s); } } @@ -944,7 +1117,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } sb.append(payCosts.toString()); sb.append(" or ").append(altOnlyMana ? alternateCost.toString() : - StringUtils.uncapitalize(alternateCost.toString())); + StringUtils.uncapitalize(alternateCost.toString())); sb.append(equip && !altOnlyMana ? "." : ""); } else { sb.append(payCosts.toString()); @@ -959,13 +1132,12 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public void rebuiltDescription() { - final StringBuilder sb = new StringBuilder(); // SubAbilities don't have Costs or Cost descriptors - sb.append(getCostDescription()); - sb.append(getParam("SpellDescription")); - setDescription(sb.toString()); + String sb = getCostDescription() + + getParam("SpellDescription"); + setDescription(sb); } /** {@inheritDoc} */ @@ -1015,37 +1187,37 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public Map getAdditionalAbilities() { - return additionalAbilities; + return additionalAbilities(); } public SpellAbility getAdditionalAbility(final String name) { if (hasAdditionalAbility(name)) { - return additionalAbilities.get(name); + return additionalAbilities().get(name); } return null; } public boolean hasAdditionalAbility(final String name) { - return additionalAbilities.containsKey(name); + return additionalAbilities().containsKey(name); } public void setAdditionalAbility(final String name, final SpellAbility sa) { if (sa == null) { - additionalAbilities.remove(name); + additionalAbilities().remove(name); } else { if (sa instanceof AbilitySub) { ((AbilitySub)sa).setParent(this); } - additionalAbilities.put(name, sa); + additionalAbilities().put(name, sa); } view.updateDescription(this); //description changes when sub-abilities change } public Map> getAdditionalAbilityLists() { - return additionalAbilityLists; + return additionalAbilityLists(); } public List getAdditionalAbilityList(final String name) { - if (additionalAbilityLists.containsKey(name)) { - return additionalAbilityLists.get(name); + if (additionalAbilityLists().containsKey(name)) { + return additionalAbilityLists().get(name); } else { return ImmutableList.of(); } @@ -1053,13 +1225,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public void setAdditionalAbilityList(final String name, final List list) { if (list == null || list.isEmpty()) { - additionalAbilityLists.remove(name); + additionalAbilityLists().remove(name); } else { List result = Lists.newArrayList(list); for (AbilitySub sa : result) { sa.setParent(this); } - additionalAbilityLists.put(name, result); + additionalAbilityLists().put(name, result); } view.updateDescription(this); } @@ -1092,7 +1264,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public boolean isPlotting() { return false; } - + /** * @return the aftermath */ @@ -1199,18 +1371,18 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit clone.changeZoneTable = new CardZoneTable(changeZoneTable); } - clone.payingMana = Lists.newArrayList(payingMana); - clone.paidAbilities = Lists.newArrayList(); + clone._setPayingMana(Lists.newArrayList(payingMana())); + clone._setPaidAbilities(Lists.newArrayList()); clone.setPaidHash(getPaidHash()); if (usesTargeting()) { // the targets need to be cloned, otherwise they might be cleared - clone.targetChosen = getTargets().clone(); + clone._setTargetChosen(getTargets().clone()); } // clear maps for copy, the values will be added later - clone.additionalAbilities = Maps.newHashMap(); - clone.additionalAbilityLists = Maps.newHashMap(); + clone._setAdditionalAbilities(Maps.newHashMap()); + clone._setAdditionalAbilityLists(Maps.newHashMap()); // run special copy Ability to make a deep copy CardFactory.copySpellAbility(this, clone, host, activ, lki, keepTextChanges); } catch (final CloneNotSupportedException e) { @@ -1381,15 +1553,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return false; } switch (related) { - case "LEPower" : - if (c.getNetPower() > parentTarget.getNetPower()) { - return false; - } - break; - case "LECMC" : - if (c.getCMC() > parentTarget.getCMC()) { - return false; - } + case "LEPower" : + if (c.getNetPower() > parentTarget.getNetPower()) { + return false; + } + break; + case "LECMC" : + if (c.getCMC() > parentTarget.getCMC()) { + return false; + } } } @@ -1562,25 +1734,20 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public List getPipsToReduce() { - return pipsToReduce; + return pipsToReduce(); } public final void clearPipsToReduce() { - pipsToReduce.clear(); + pipsToReduce().clear(); } public CardCollection getTappedForConvoke() { - return tappedForConvoke; + return tappedForConvoke(); } public void addTappedForConvoke(final Card c) { - if (tappedForConvoke == null) { - tappedForConvoke = new CardCollection(); - } - tappedForConvoke.add(c); + tappedForConvoke().add(c); } public void clearTappedForConvoke() { - if (tappedForConvoke != null) { - tappedForConvoke.clear(); - } + tappedForConvoke().clear(); } public boolean isEmerge() { @@ -1754,16 +1921,16 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit * @return the chosenTarget */ public TargetChoices getTargets() { - return targetChosen; + return targetChosen(); } public void setTargets(TargetChoices targets) { // TODO should copy the target choices? - targetChosen = targets; + _setTargetChosen(targets); } public void resetTargets() { - targetChosen = new TargetChoices(); + resetTargetChosen(); } /** @@ -1879,7 +2046,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public Card getTargetCard() { - return targetChosen.getFirstTargetedCard(); + return targetChosen().getFirstTargetedCard(); } /** @@ -1898,7 +2065,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } resetTargets(); - targetChosen.add(card); + targetChosen().add(card); setStackDescription(getHostCard().getName() + " - targeting " + card); } @@ -2258,11 +2425,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit subAbility.changeText(); } } - for (SpellAbility sa : additionalAbilities.values()) { + for (SpellAbility sa : additionalAbilities().values()) { sa.changeText(); } - for (List list : additionalAbilityLists.values()) { + for (List list : additionalAbilityLists().values()) { for (AbilitySub sa : list) { sa.changeText(); } @@ -2283,11 +2450,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit subAbility.changeTextIntrinsic(colorMap, typeMap); } } - for (SpellAbility sa : additionalAbilities.values()) { + for (SpellAbility sa : additionalAbilities().values()) { sa.changeTextIntrinsic(colorMap, typeMap); } - for (List list : additionalAbilityLists.values()) { + for (List list : additionalAbilityLists().values()) { for (AbilitySub sa : list) { sa.changeTextIntrinsic(colorMap, typeMap); } @@ -2300,12 +2467,12 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (subAbility != null) { subAbility.setIntrinsic(i); } - for (SpellAbility sa : additionalAbilities.values()) { + for (SpellAbility sa : additionalAbilities().values()) { if (sa.isIntrinsic() != i) { sa.setIntrinsic(i); } } - for (List list : additionalAbilityLists.values()) { + for (List list : additionalAbilityLists().values()) { for (AbilitySub sa : list) { if (sa.isIntrinsic() != i) { sa.setIntrinsic(i); @@ -2505,6 +2672,12 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return sb.toString(); } + public boolean isSkip() { + return skip; + } + public void setSkip(boolean val) { + skip = val; + } public boolean canCastTiming(Player activator) { return canCastTiming(getHostCard(), activator); } @@ -2555,14 +2728,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public void addRollbackEffect(Card eff) { - rollbackEffects.add(eff); + rollbackEffects().add(eff); } public void rollback() { - for (Card c : rollbackEffects) { + for (Card c : rollbackEffects()) { c.getGame().getAction().ceaseToExist(c, true); } - rollbackEffects.clear(); + rollbackEffects().clear(); } public boolean isHidden() { diff --git a/forge-game/src/main/java/forge/game/zone/Zone.java b/forge-game/src/main/java/forge/game/zone/Zone.java index 641e33103dd..73ddff61596 100644 --- a/forge-game/src/main/java/forge/game/zone/Zone.java +++ b/forge-game/src/main/java/forge/game/zone/Zone.java @@ -18,7 +18,6 @@ package forge.game.zone; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -36,6 +35,7 @@ import forge.game.event.EventValueChangeType; import forge.game.event.GameEventZone; import forge.game.player.Player; import forge.util.CollectionSuppliers; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.maps.EnumMapOfLists; import forge.util.maps.MapOfLists; @@ -278,7 +278,7 @@ public class Zone implements java.io.Serializable, Iterable { } public void shuffle() { - Collections.shuffle(cardList, MyRandom.getRandom()); + CollectionUtil.shuffle(cardList, MyRandom.getRandom()); onChanged(); } diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java b/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java index 756dfe195fb..f2b57e1f7a6 100644 --- a/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java +++ b/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java @@ -18,6 +18,7 @@ import forge.screens.home.quest.DialogChooseFormats; import forge.screens.home.quest.DialogChooseSets; import forge.screens.match.controllers.CDetailPicture; import forge.util.CollectionSuppliers; +import forge.util.CollectionUtil; import forge.util.Localizer; import javax.swing.*; @@ -98,7 +99,7 @@ public class CardManager extends ItemManager { // Use standard sort + index, for better performance! Collections.sort(acceptedEditions); if (StaticData.instance().cardArtPreferenceIsLatest()) - Collections.reverse(acceptedEditions); + CollectionUtil.reverse(acceptedEditions); Iterator editionIterator = acceptedEditions.iterator(); Entry candidateEntry = null; Entry firstCandidateEntryFound = null; diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CProbabilities.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CProbabilities.java index 2920544e72e..bc334f38bb9 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CProbabilities.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CProbabilities.java @@ -1,7 +1,6 @@ package forge.screens.deckeditor.controllers; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -14,6 +13,7 @@ import forge.item.InventoryItem; import forge.item.PaperCard; import forge.screens.deckeditor.CDeckEditorUI; import forge.screens.deckeditor.views.VProbabilities; +import forge.util.CollectionUtil; import forge.util.ItemPool; import forge.util.MyRandom; @@ -63,7 +63,7 @@ public enum CProbabilities implements ICDoc { final List cardProbabilities = new ArrayList<>(); final List shuffled = deck.toFlatList(); - Collections.shuffle(shuffled, MyRandom.getRandom()); + CollectionUtil.shuffle(shuffled, MyRandom.getRandom()); // Log totals of each card for decrementing final Map cardTotals = new HashMap<>(); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java b/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java index 1bd34fdf521..6ffd7bfedac 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java @@ -13,6 +13,7 @@ import forge.game.GameFormat; import forge.gui.SOverlayUtils; import forge.localinstance.skin.FSkinProp; import forge.model.FModel; +import forge.util.CollectionUtil; import forge.util.Localizer; import net.miginfocom.swing.MigLayout; import forge.toolbox.FCheckBoxTree.FTreeNode; @@ -467,7 +468,7 @@ public class DialogChooseSets { FTreeNode setTypeNode = checkBoxTree.getNodeByKey(editionType); if (setTypeNode != null){ List activeChildNodes = checkBoxTree.getActiveChildNodes(setTypeNode); - Collections.shuffle(activeChildNodes); + CollectionUtil.shuffle(activeChildNodes); for (int i = 0; i < totalToSelect; i++) checkBoxTree.setNodeCheckStatus(activeChildNodes.get(i), true); } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java index d7e6e587cec..a9fd9d834e1 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java @@ -26,12 +26,12 @@ import forge.screens.deckeditor.controllers.CEditorDraftingProcess; import forge.screens.deckeditor.views.VProbabilities; import forge.screens.deckeditor.views.VStatistics; import forge.toolbox.FOptionPane; +import forge.util.CollectionUtil; import forge.util.Localizer; import javax.swing.*; import java.awt.event.ActionListener; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -163,7 +163,7 @@ public enum CSubmenuDraft implements ICDoc { for(int i = 0; i < maxDecks; i++) { aiIndices.add(i); } - Collections.shuffle(aiIndices); + CollectionUtil.shuffle(aiIndices); aiIndices = aiIndices.subList(0, numOpponents); for(int i : aiIndices) { diff --git a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java index 4fd38e12023..8c36a5f347e 100644 --- a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java +++ b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java @@ -2,7 +2,6 @@ package forge.view; import java.io.File; import java.util.ArrayList; -import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -10,6 +9,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import forge.util.CollectionUtil; import org.apache.commons.lang3.time.StopWatch; import forge.LobbyPlayer; @@ -201,7 +201,7 @@ public class SimulateMatch { } else { log = g1.getGameLog().getLogEntries(GameLogEntryType.MATCH_RESULTS); } - Collections.reverse(log); + CollectionUtil.reverse(log); for (GameLogEntry l : log) { System.out.println(l); } diff --git a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java index d5afbe7aba2..c52b899dad4 100644 --- a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java +++ b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java @@ -2,13 +2,13 @@ package forge.deck; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import forge.util.CollectionUtil; import org.apache.commons.lang3.tuple.Pair; import com.google.common.base.Predicate; @@ -701,7 +701,7 @@ public class DeckgenUtil { }else { String matrixKey = (format.equals(DeckFormat.TinyLeaders) ? DeckFormat.Commander : format).toString(); //use Commander for Tiny Leaders List> potentialCards = new ArrayList<>(CardRelationMatrixGenerator.cardPools.get(matrixKey).get(commander.getName())); - Collections.shuffle(potentialCards, MyRandom.getRandom()); + CollectionUtil.shuffle(potentialCards, MyRandom.getRandom()); for(Map.Entry pair:potentialCards){ if(format.isLegalCard(pair.getKey())) { preSelectedCards.add(pair.getKey()); @@ -800,7 +800,7 @@ public class DeckgenUtil { break; } List cardList = Lists.newArrayList(colorList); - Collections.shuffle(cardList, MyRandom.getRandom()); + CollectionUtil.shuffle(cardList, MyRandom.getRandom()); int shortlistlength=400; if(cardList.size() { } List category = new ArrayList<>(categories.values()); - Collections.reverse(category); + CollectionUtil.reverse(category); final NetDeckArchiveBlock c = SGuiChoose.oneOrNone("Select a Net Deck Archive Block category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java b/forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java index 667fc26aed0..7892ff690ec 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java @@ -7,6 +7,7 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -71,7 +72,7 @@ public class NetDeckArchiveLegacy extends StorageBase { } List category = new ArrayList<>(categories.values()); - Collections.reverse(category); + CollectionUtil.reverse(category); final NetDeckArchiveLegacy c = SGuiChoose.oneOrNone("Select a Net Deck Archive Legacy category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java b/forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java index 2060490abc5..4092efb01c2 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java @@ -7,6 +7,7 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -71,7 +72,7 @@ public class NetDeckArchiveModern extends StorageBase { } List category = new ArrayList<>(categories.values()); - Collections.reverse(category); + CollectionUtil.reverse(category); final NetDeckArchiveModern c = SGuiChoose.oneOrNone("Select a Net Deck Archive Modern category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchivePauper.java b/forge-gui/src/main/java/forge/deck/NetDeckArchivePauper.java index ec60dcb8658..751bdb5b400 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchivePauper.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchivePauper.java @@ -7,6 +7,7 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -71,7 +72,7 @@ public class NetDeckArchivePauper extends StorageBase { } List category = new ArrayList<>(categories.values()); - Collections.reverse(category); + CollectionUtil.reverse(category); final NetDeckArchivePauper c = SGuiChoose.oneOrNone("Select a Net Deck Archive Pauper category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java b/forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java index 903d120da1e..02efbee551d 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java @@ -7,6 +7,7 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -71,7 +72,7 @@ public class NetDeckArchivePioneer extends StorageBase { } List category = new ArrayList<>(categories.values()); - Collections.reverse(category); + CollectionUtil.reverse(category); final NetDeckArchivePioneer c = SGuiChoose.oneOrNone("Select a Net Deck Archive Pioneer category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java b/forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java index 62ae7f782a0..b7176c43cf7 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java @@ -7,6 +7,7 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -71,7 +72,7 @@ public class NetDeckArchiveStandard extends StorageBase { } List category = new ArrayList<>(categories.values()); - Collections.reverse(category); + CollectionUtil.reverse(category); final NetDeckArchiveStandard c = SGuiChoose.oneOrNone("Select a Net Deck Archive Standard category",category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java b/forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java index 97a917362f0..b8bc583f0e5 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java @@ -7,6 +7,7 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -71,7 +72,7 @@ public class NetDeckArchiveVintage extends StorageBase { } List category = new ArrayList<>(categories.values()); - Collections.reverse(category); + CollectionUtil.reverse(category); final NetDeckArchiveVintage c = SGuiChoose.oneOrNone("Select a Net Deck Archive Vintage category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java b/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java index 9fb12cbec15..b8ccac3bc89 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java @@ -36,6 +36,7 @@ import forge.localinstance.properties.ForgeConstants; import forge.localinstance.properties.ForgePreferences; import forge.model.CardBlock; import forge.model.FModel; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.ItemPool; import forge.util.Localizer; @@ -474,7 +475,7 @@ public class BoosterDraft implements IBoosterDraft { // Maybe the AI could have more knowledge about the other players. // Like don't pass to players that have revealed certain cards or colors // But random is probably fine for now - Collections.shuffle(dredgers); + CollectionUtil.shuffle(dredgers); passToPlayer = dredgers.get(0); } else { // Human player, so we need to ask them @@ -556,7 +557,7 @@ public class BoosterDraft implements IBoosterDraft { } } - Collections.shuffle(brokers); + CollectionUtil.shuffle(brokers); for(LimitedPlayer pl : brokers) { pl.activateBrokers(this.players); } diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/CardThemedDeckBuilder.java b/forge-gui/src/main/java/forge/gamemodes/limited/CardThemedDeckBuilder.java index a7bb88aee3e..ec25878a6bf 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/CardThemedDeckBuilder.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/CardThemedDeckBuilder.java @@ -1,7 +1,6 @@ package forge.gamemodes.limited; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -34,6 +33,7 @@ import forge.item.IPaperCard; import forge.item.PaperCard; import forge.localinstance.properties.ForgePreferences; import forge.model.FModel; +import forge.util.CollectionUtil; import forge.util.MyRandom; /** @@ -628,7 +628,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { possibleList.removeAll(StaticData.instance().getCommonCards().getAllCards(secondKeyCard.getName())); } //Iterator iRandomPool = CardRanker.rankCardsInDeck(possibleList.subList(0, targetSize <= possibleList.size() ? targetSize : possibleList.size())).iterator(); - Collections.shuffle(possibleList); + CollectionUtil.shuffle(possibleList); Iterator iRandomPool = possibleList.iterator(); while (deckList.size() < targetSize) { if (logToConsole) { @@ -864,7 +864,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { if (secondKeyCard != null) { possibleList.removeAll(StaticData.instance().getCommonCards().getAllCards(secondKeyCard.getName())); } - Collections.shuffle(possibleList); + CollectionUtil.shuffle(possibleList); //addManaCurveCards(CardRanker.rankCardsInDeck(possibleList.subList(0, targetSize*3 <= possibleList.size() ? targetSize*3 : possibleList.size())), //num, "Random Card"); addManaCurveCards(possibleList, num, "Random Card"); diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java index 1a9bae2da9c..89e23687840 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java @@ -11,6 +11,7 @@ import forge.deck.DeckSection; import forge.gui.util.SGuiChoose; import forge.item.PaperCard; import forge.model.FModel; +import forge.util.CollectionUtil; import forge.util.TextUtil; import java.util.*; @@ -723,7 +724,7 @@ public class LimitedPlayer { } public PaperCard pickFromArchdemonCurse(DraftPack chooseFrom) { - Collections.shuffle(chooseFrom); + CollectionUtil.shuffle(chooseFrom); reduceArchdemonOfPalianoCurse(); return chooseFrom.get(0); } diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java index 21da70a76e5..f81da820c98 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java @@ -10,10 +10,10 @@ import forge.deck.DeckSection; import forge.deck.generation.DeckGeneratorBase; import forge.item.PaperCard; import forge.localinstance.properties.ForgePreferences; +import forge.util.CollectionUtil; import forge.util.MyRandom; import org.apache.commons.lang3.tuple.Pair; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -81,7 +81,7 @@ public class LimitedPlayerAI extends LimitedPlayer { // For Paliano, if player has revealed anything, try to avoid that color // For Regicide, don't choose one of my colors } - Collections.shuffle(colors); + CollectionUtil.shuffle(colors); return colors.get(0); } @@ -89,7 +89,7 @@ public class LimitedPlayerAI extends LimitedPlayer { protected String removeWithAny(PaperCard bestPick, List options) { // If we have multiple remove from draft options, do none of them for now - Collections.shuffle(options); + CollectionUtil.shuffle(options); if (options.get(0).equals("Animus of Predation")) { if (removeWithAnimus(bestPick)) { return "Animus of Predation"; @@ -254,7 +254,7 @@ public class LimitedPlayerAI extends LimitedPlayer { @Override protected CardEdition chooseEdition(List possibleEditions) { - Collections.shuffle(possibleEditions); + CollectionUtil.shuffle(possibleEditions); return possibleEditions.get(0); } diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/SealedCardPoolGenerator.java b/forge-gui/src/main/java/forge/gamemodes/limited/SealedCardPoolGenerator.java index d110d7759e5..9d5fe9f7d0a 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/SealedCardPoolGenerator.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/SealedCardPoolGenerator.java @@ -37,6 +37,7 @@ import forge.localinstance.skin.FSkinProp; import forge.model.CardBlock; import forge.model.FModel; import forge.model.UnOpenedMeta; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.Localizer; import forge.util.MyRandom; @@ -173,7 +174,7 @@ public class SealedCardPoolGenerator { case Prerelease: ArrayList editions = Lists.newArrayList(StaticData.instance().getEditions().getPrereleaseEditions()); Collections.sort(editions); - Collections.reverse(editions); + CollectionUtil.reverse(editions); CardEdition chosenEdition = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblChooseAnEdition"), editions); if (chosenEdition == null) { diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/WinstonDraft.java b/forge-gui/src/main/java/forge/gamemodes/limited/WinstonDraft.java index 613aa3a1828..a3a3fd9f935 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/WinstonDraft.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/WinstonDraft.java @@ -1,7 +1,6 @@ package forge.gamemodes.limited; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Stack; @@ -12,6 +11,7 @@ import com.google.common.collect.Iterables; import forge.deck.CardPool; import forge.deck.Deck; import forge.item.PaperCard; +import forge.util.CollectionUtil; import forge.util.MyRandom; public class WinstonDraft extends BoosterDraft { @@ -47,7 +47,7 @@ public class WinstonDraft extends BoosterDraft { } } } - Collections.shuffle(this.deck, MyRandom.getRandom()); + CollectionUtil.shuffle(this.deck, MyRandom.getRandom()); // Create three Winston piles, adding the top card from the Winston deck to start each pile this.piles = new ArrayList<>(); diff --git a/forge-gui/src/main/java/forge/gamemodes/planarconquest/ConquestData.java b/forge-gui/src/main/java/forge/gamemodes/planarconquest/ConquestData.java index ae7bee33b20..51d7142d96a 100644 --- a/forge-gui/src/main/java/forge/gamemodes/planarconquest/ConquestData.java +++ b/forge-gui/src/main/java/forge/gamemodes/planarconquest/ConquestData.java @@ -42,6 +42,7 @@ import forge.localinstance.properties.ForgeConstants; import forge.localinstance.skin.ISkinImage; import forge.model.FModel; import forge.util.CardTranslation; +import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.Localizer; import forge.util.XmlReader; @@ -589,7 +590,7 @@ public final class ConquestData { path.add(current.loc); current = current.came_from; } - Collections.reverse(path); //reverse path so it begins with start location + CollectionUtil.reverse(path); //reverse path so it begins with start location return path; } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/BoosterUtils.java b/forge-gui/src/main/java/forge/gamemodes/quest/BoosterUtils.java index e3d851abecc..76760120093 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/BoosterUtils.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/BoosterUtils.java @@ -20,10 +20,10 @@ package forge.gamemodes.quest; import static forge.gamemodes.quest.QuestUtilCards.isLegalInQuestFormat; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; +import forge.util.CollectionUtil; import org.apache.commons.lang3.StringUtils; import com.google.common.base.Predicate; @@ -236,7 +236,7 @@ public final class BoosterUtils { preferredColors.clear(); int numberOfColors = COLOR_COUNT_PROBABILITIES[(int) (MyRandom.getRandom().nextDouble() * COLOR_COUNT_PROBABILITIES.length)]; if (numberOfColors < 6) { - Collections.shuffle(possibleColors); + CollectionUtil.shuffle(possibleColors); for (int i = 0; i < numberOfColors; i++) { preferredColors.add(possibleColors.get(i)); } @@ -391,7 +391,7 @@ public final class BoosterUtils { final int size = allowedColors == null ? 0 : allowedColors.size(); if (allowedColors != null) { - Collections.shuffle(allowedColors); + CollectionUtil.shuffle(allowedColors); } int cntMade = 0, iAttempt = 0; diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/MainWorldEventDuelManager.java b/forge-gui/src/main/java/forge/gamemodes/quest/MainWorldEventDuelManager.java index d7ac90f1454..d3779651930 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/MainWorldEventDuelManager.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/MainWorldEventDuelManager.java @@ -3,7 +3,6 @@ package forge.gamemodes.quest; import java.io.File; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import forge.gamemodes.quest.data.QuestPreferences; @@ -12,6 +11,7 @@ import forge.gamemodes.quest.data.QuestPreferences.QPref; import forge.gamemodes.quest.io.MainWorldDuelReader; import forge.model.FModel; import forge.util.CollectionSuppliers; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.maps.EnumMapOfLists; import forge.util.maps.MapOfLists; @@ -259,7 +259,7 @@ public class MainWorldEventDuelManager implements QuestEventDuelManagerInterface public void randomizeOpponents() { for (QuestEventDifficulty qd : sortedDuels.keySet()) { List list = (List) sortedDuels.get(qd); - Collections.shuffle(list, MyRandom.getRandom()); + CollectionUtil.shuffle(list, MyRandom.getRandom()); } } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestController.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestController.java index 70412d60b60..9b6db1f4666 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestController.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestController.java @@ -19,7 +19,6 @@ package forge.gamemodes.quest; import java.io.File; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -51,6 +50,7 @@ import forge.item.PreconDeck; import forge.localinstance.properties.ForgeConstants; import forge.model.FModel; import forge.player.GamePlayerUtil; +import forge.util.CollectionUtil; import forge.util.storage.IStorage; import forge.util.storage.StorageBase; @@ -612,7 +612,7 @@ public class QuestController { } } - Collections.shuffle(unlockedChallengeIds); + CollectionUtil.shuffle(unlockedChallengeIds); maxChallenges = Math.min(maxChallenges, unlockedChallengeIds.size()); diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventCommanderDuelManager.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventCommanderDuelManager.java index 686b37d7839..3b1b95445c0 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventCommanderDuelManager.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventCommanderDuelManager.java @@ -1,7 +1,6 @@ package forge.gamemodes.quest; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import forge.deck.CardPool; @@ -12,6 +11,7 @@ import forge.deck.DeckProxy; import forge.gamemodes.quest.data.QuestPreferences; import forge.item.PaperCard; import forge.model.FModel; +import forge.util.CollectionUtil; import forge.util.MyRandom; /** @@ -198,6 +198,6 @@ public class QuestEventCommanderDuelManager implements QuestEventDuelManagerInte * Randomizes the list of Commander Duels. */ public void randomizeOpponents(){ - Collections.shuffle(commanderDuels); + CollectionUtil.shuffle(commanderDuels); } } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDraft.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDraft.java index 1a3a2137df2..1697f194a0f 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDraft.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDraft.java @@ -46,6 +46,7 @@ import forge.item.PaperCard; import forge.model.CardBlock; import forge.model.FModel; import forge.player.GamePlayerUtil; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.NameGenerator; import forge.util.TextUtil; @@ -856,7 +857,7 @@ public class QuestEventDraft implements IQuestEvent { return null; } - Collections.shuffle(possibleFormats); + CollectionUtil.shuffle(possibleFormats); return getDraftOrNull(quest, possibleFormats.get(0)); } @@ -885,7 +886,7 @@ public class QuestEventDraft implements IQuestEvent { System.err.println("Warning: no valid set combinations were detected when trying to generate a draft tournament for the format: " + format); return null; } - Collections.shuffle(possibleSetCombinations); + CollectionUtil.shuffle(possibleSetCombinations); event.boosterConfiguration = possibleSetCombinations.get(0); } @@ -902,7 +903,7 @@ public class QuestEventDraft implements IQuestEvent { players.add("6"); players.add("7"); - Collections.shuffle(players); + CollectionUtil.shuffle(players); // Initialize tournament for (int i = 0; i < players.size(); i++) { diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDuelManager.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDuelManager.java index 9f147e80cbc..23fdfcc2774 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDuelManager.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDuelManager.java @@ -20,7 +20,6 @@ package forge.gamemodes.quest; import java.io.File; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import forge.gamemodes.quest.data.QuestPreferences; @@ -29,6 +28,7 @@ import forge.gamemodes.quest.data.QuestPreferences.QPref; import forge.gamemodes.quest.io.QuestDuelReader; import forge.model.FModel; import forge.util.CollectionSuppliers; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.maps.EnumMapOfLists; import forge.util.maps.MapOfLists; @@ -236,7 +236,7 @@ public class QuestEventDuelManager implements QuestEventDuelManagerInterface { public void randomizeOpponents() { for (QuestEventDifficulty qd : sortedDuels.keySet()) { List list = (List) sortedDuels.get(qd); - Collections.shuffle(list, MyRandom.getRandom()); + CollectionUtil.shuffle(list, MyRandom.getRandom()); } } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventLDADuelManager.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventLDADuelManager.java index e85d3a2f391..c110e1eeff4 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventLDADuelManager.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventLDADuelManager.java @@ -19,7 +19,6 @@ package forge.gamemodes.quest; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import forge.deck.CardArchetypeLDAGenerator; @@ -30,6 +29,7 @@ import forge.gamemodes.quest.data.QuestPreferences.DifficultyPrefs; import forge.gamemodes.quest.data.QuestPreferences.QPref; import forge.model.FModel; import forge.util.CollectionSuppliers; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.maps.EnumMapOfLists; import forge.util.maps.MapOfLists; @@ -236,7 +236,7 @@ public class QuestEventLDADuelManager implements QuestEventDuelManagerInterface public void randomizeOpponents() { for (QuestEventDifficulty qd : sortedDuels.keySet()) { List list = (List) sortedDuels.get(qd); - Collections.shuffle(list, MyRandom.getRandom()); + CollectionUtil.shuffle(list, MyRandom.getRandom()); } } } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilCards.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilCards.java index 063cb5d0e36..bc03895ca91 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilCards.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilCards.java @@ -42,13 +42,13 @@ import forge.item.generation.UnOpenedProduct; import forge.localinstance.properties.ForgePreferences.FPref; import forge.model.FModel; import forge.util.Aggregates; +import forge.util.CollectionUtil; import forge.util.ItemPool; import forge.util.MyRandom; import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map.Entry; @@ -678,7 +678,7 @@ public final class QuestUtilCards { editions.add(e); } - Collections.shuffle(editions); + CollectionUtil.shuffle(editions); int numberOfBoxes = Math.min(Math.max(count / 2, 1), editions.size()); diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java index 7e24021c3d4..12c7720699e 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java @@ -17,12 +17,6 @@ */ package forge.gamemodes.quest; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; - import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -36,6 +30,7 @@ import forge.item.PaperCard; import forge.item.SealedTemplate; import forge.item.generation.UnOpenedProduct; import forge.model.FModel; +import forge.util.CollectionUtil; import forge.util.TextUtil; import forge.util.storage.IStorage; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -176,7 +171,7 @@ public class QuestUtilUnlockSets { options.add(set.left); // System.out.println("Padded with: " + fillers.get(i).getName()); } - Collections.reverse(options); + CollectionUtil.reverse(options); if (FModel.getQuestPreferences().getPrefInt(QPref.UNLIMITED_UNLOCKING) == 0) { return options.subList(0, Math.min(options.size(), Math.min(8, 2 + ((qData.getAchievements().getWin()) / 50)))); diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java b/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java index bc52df6736f..4a455fbe60d 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java @@ -1,10 +1,10 @@ package forge.gamemodes.quest.setrotation; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Random; import forge.model.FModel; +import forge.util.CollectionUtil; import static java.lang.Integer.min; @@ -27,7 +27,7 @@ public class QueueRandomRotation implements ISetRotation { int seed = FModel.getQuest().getName().hashCode(); Random rnd = new Random(seed); List shuffledSets = new ArrayList<>(allSets); - Collections.shuffle(shuffledSets, rnd); + CollectionUtil.shuffle(shuffledSets, rnd); List currentCodes = new ArrayList<>(); int outRotations = FModel.getQuest().getAchievements().getWin() / rotateAfterWins; diff --git a/forge-gui/src/main/java/forge/gamemodes/tournament/system/AbstractTournament.java b/forge-gui/src/main/java/forge/gamemodes/tournament/system/AbstractTournament.java index 9a12e54cdc2..79b9b7d71a5 100644 --- a/forge-gui/src/main/java/forge/gamemodes/tournament/system/AbstractTournament.java +++ b/forge-gui/src/main/java/forge/gamemodes/tournament/system/AbstractTournament.java @@ -2,7 +2,6 @@ package forge.gamemodes.tournament.system; import java.io.Serializable; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import com.google.common.collect.Lists; @@ -12,6 +11,7 @@ import forge.LobbyPlayer; import forge.deck.DeckGroup; import forge.game.player.RegisteredPlayer; import forge.player.GamePlayerUtil; +import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; @@ -44,7 +44,7 @@ public abstract class AbstractTournament implements Serializable { public void initializeTournament() { // "Randomly" seed players to start tournament - Collections.shuffle(remainingPlayers, MyRandom.getRandom()); + CollectionUtil.shuffle(remainingPlayers, MyRandom.getRandom()); generateActivePairings(); initialized = true; } diff --git a/forge-gui/src/main/java/forge/gamemodes/tournament/system/TournamentSwiss.java b/forge-gui/src/main/java/forge/gamemodes/tournament/system/TournamentSwiss.java index 3f617912ed2..1ecff24af74 100644 --- a/forge-gui/src/main/java/forge/gamemodes/tournament/system/TournamentSwiss.java +++ b/forge-gui/src/main/java/forge/gamemodes/tournament/system/TournamentSwiss.java @@ -1,13 +1,13 @@ package forge.gamemodes.tournament.system; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import com.google.common.collect.Lists; +import forge.util.CollectionUtil; @SuppressWarnings("serial") public class TournamentSwiss extends AbstractTournament { @@ -40,7 +40,7 @@ public class TournamentSwiss extends AbstractTournament { activeRound++; // Randomize players, then sort by scores - Collections.shuffle(allPlayers); + CollectionUtil.shuffle(allPlayers); sortAllPlayers("swiss"); if (allPlayers.size() % 2 == 1) { diff --git a/forge-gui/src/main/java/forge/itemmanager/GroupDef.java b/forge-gui/src/main/java/forge/itemmanager/GroupDef.java index fb921e447c6..6238d0f051f 100644 --- a/forge-gui/src/main/java/forge/itemmanager/GroupDef.java +++ b/forge-gui/src/main/java/forge/itemmanager/GroupDef.java @@ -15,6 +15,7 @@ import forge.deck.DeckProxy; import forge.item.InventoryItem; import forge.item.PaperCard; import forge.model.FModel; +import forge.util.CollectionUtil; import forge.util.Localizer; public enum GroupDef { @@ -239,7 +240,7 @@ public enum GroupDef { //build sorted list of sets List sortedSets = Lists.newArrayList(FModel.getMagicDb().getEditions()); Collections.sort(sortedSets); - Collections.reverse(sortedSets); + CollectionUtil.reverse(sortedSets); int groupNum = 0; String[] setGroups = new String[sortedSets.size()]; diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 73402c00b5a..84e833a70bf 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -465,7 +465,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { } private PaymentDecision exileFromTopGraveType(final int nNeeded, final CardCollection typeList) { - Collections.reverse(typeList); + CollectionUtil.reverse(typeList); return PaymentDecision.card(Iterables.limit(typeList, nNeeded)); } diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index b5f64a0fcf9..024bbf7b8d0 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -1141,7 +1141,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } endTempShowCards(); if(topOfDeck) - Collections.reverse(choices); + CollectionUtil.reverse(choices); CardCollection result = new CardCollection(); gameCacheMove.addToList(choices, result); return result; @@ -1663,7 +1663,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont Collections.sort(sortedResults); if (!call) { - Collections.reverse(sortedResults); + CollectionUtil.reverse(sortedResults); } return getGui().one(sa.getHostCard().getName() + " - " + localizer.getMessage("lblChooseAResult"), sortedResults).equals(labelsSrc[0]); } From 8dbb6486381ec344dca1dfa87bd16b03afee36fa Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 14 Nov 2024 22:06:23 +0800 Subject: [PATCH 024/152] add user setting for AI Timeout --- forge-ai/src/main/java/forge/ai/AiAttackController.java | 2 +- forge-ai/src/main/java/forge/ai/AiController.java | 2 +- .../src/main/java/forge/ai/simulation/GameCopier.java | 1 + forge-game/src/main/java/forge/game/Game.java | 1 + forge-game/src/main/java/forge/game/player/Player.java | 7 +++++++ .../forge/screens/home/settings/CSubmenuPreferences.java | 9 +++++++++ .../forge/screens/home/settings/VSubmenuPreferences.java | 8 ++++++++ .../test/java/forge/ai/simulation/SimulationTest.java | 1 + .../src/forge/screens/settings/SettingsPage.java | 4 ++++ forge-gui/res/languages/de-DE.properties | 4 +++- forge-gui/res/languages/en-US.properties | 4 +++- forge-gui/res/languages/es-ES.properties | 4 +++- forge-gui/res/languages/fr-FR.properties | 4 +++- forge-gui/res/languages/it-IT.properties | 4 +++- forge-gui/res/languages/ja-JP.properties | 4 +++- forge-gui/res/languages/pt-BR.properties | 4 +++- forge-gui/res/languages/zh-CN.properties | 4 +++- .../src/main/java/forge/gamemodes/match/HostedMatch.java | 1 + .../forge/localinstance/properties/ForgePreferences.java | 1 + 19 files changed, 59 insertions(+), 10 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index d4975a41bd9..215ee85373b 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -967,7 +967,7 @@ public class AiAttackController { })); } CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); - CompletableFuture.allOf(futuresArray).completeOnTimeout(null, 5, TimeUnit.SECONDS).join(); + CompletableFuture.allOf(futuresArray).completeOnTimeout(null, ai.getTimeout(), TimeUnit.SECONDS).join(); futures.clear(); if (attackersLeft.isEmpty()) { return aiAggression; diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index cb8e12abeaa..682f95fc9cb 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -1667,7 +1667,7 @@ public class AiController { } //timeout 5 seconds? even the AI don't acquire all, there should be SA to cast if valid CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); - CompletableFuture.allOf(futuresArray).completeOnTimeout(null, 5, TimeUnit.SECONDS).join(); + CompletableFuture.allOf(futuresArray).completeOnTimeout(null, player.getTimeout(), TimeUnit.SECONDS).join(); futures.clear(); if (!spells.isEmpty()) { diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java index 2629cf44d0b..592e73974a3 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -215,6 +215,7 @@ public class GameCopier { private void copyGameState(Game newGame, Player aiPlayer) { newGame.EXPERIMENTAL_RESTORE_SNAPSHOT = origGame.EXPERIMENTAL_RESTORE_SNAPSHOT; + newGame.AI_TIMEOUT = origGame.AI_TIMEOUT; newGame.setAge(origGame.getAge()); // TODO countersAddedThisTurn diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index ad3115a6802..578bc472d69 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -90,6 +90,7 @@ public class Game { private final GameLog gameLog = new GameLog(); private final Zone stackZone = new Zone(ZoneType.Stack, this); + public int AI_TIMEOUT = 5; public boolean EXPERIMENTAL_RESTORE_SNAPSHOT = false; // While this is false here, its really set by the Match/Preferences 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 9d690cf4a62..052701ce32e 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -131,6 +131,8 @@ public class Player extends GameEntity implements Comparable { private boolean revolt = false; private int descended = 0; + // AI Timeout + private int aiTimeout = 5; private List sacrificedThisTurn = new ArrayList<>(); private List discardedThisTurn = new ArrayList<>(); @@ -209,6 +211,7 @@ public class Player extends GameEntity implements Comparable { super(id0); game = game0; + aiTimeout = game.AI_TIMEOUT; for (final ZoneType z : Player.ALL_ZONES) { final PlayerZone toPut = z == ZoneType.Battlefield ? new PlayerZoneBattlefield(z, this) : new PlayerZone(z, this); zones.put(z, toPut); @@ -267,6 +270,10 @@ public class Player extends GameEntity implements Comparable { teamNumber = iTeam; } + public final int getTimeout() { + return aiTimeout; + } + public boolean isArchenemy() { return getZone(ZoneType.SchemeDeck).size() > 0; //Only the archenemy has schemes. } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java index 9f68326c97e..eb574fbe1aa 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java @@ -209,6 +209,7 @@ public enum CSubmenuPreferences implements ICDoc { initializeMulliganRuleComboBox(); initializeAiProfilesComboBox(); initializeAiSideboardingModeComboBox(); + initializeAiTimeoutComboBox(); initializeSoundSetsComboBox(); initializeMusicSetsComboBox(); initializeStackAdditionsComboBox(); @@ -415,6 +416,14 @@ public enum CSubmenuPreferences implements ICDoc { comboBox.addActionListener(actionEvent -> AiProfileUtil.setAiSideboardingMode(AiProfileUtil.AISideboardingMode.normalizedValueOf(comboBox.getSelectedItem()))); } + private void initializeAiTimeoutComboBox() { + final FPref userSetting = FPref.MATCH_AI_TIMEOUT; + final FComboBoxPanel panel = this.view.getAiTimeoutComboBox(); + final FComboBox comboBox = createComboBox(new String[] {"5", "10", "60", "120", "240", "300", "600"}, userSetting); + final String selectedItem = this.prefs.getPref(userSetting); + panel.setComboBox(comboBox, selectedItem); + } + private void initializeSoundSetsComboBox() { final FPref userSetting = FPref.UI_CURRENT_SOUND_SET; final FComboBoxPanel panel = this.view.getSoundSetsComboBoxPanel(); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java index 0eabbbe9f97..c00f04fba40 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java @@ -135,6 +135,7 @@ public enum VSubmenuPreferences implements IVSubmenu { private final FComboBoxPanel cbpMusicSets = new FComboBoxPanel<>(localizer.getMessage("cbpMusicSets")+":"); private final FComboBoxPanel cbpAiProfiles = new FComboBoxPanel<>(localizer.getMessage("cbpAiProfiles")+":"); private final FComboBoxPanel cbpAiSideboardingMode = new FComboBoxPanel<>(localizer.getMessage("cbpAiSideboardingMode")+":"); + private final FComboBoxPanel cbpAiTimeout = new FComboBoxPanel<>(localizer.getMessage("cbAITimeout")+":"); private final FComboBoxPanel cbpStackAdditions = new FComboBoxPanel<>(localizer.getMessage("cbpStackAdditions")+":"); private final FComboBoxPanel cbpLandPlayed = new FComboBoxPanel<>(localizer.getMessage("cbpLandPlayed")+":"); private final FComboBoxPanel cbpDisplayCurrentCardColors = new FComboBoxPanel<>(localizer.getMessage("cbpDisplayCurrentCardColors")+":"); @@ -245,6 +246,9 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbExperimentalRestore, titleConstraints); pnlPrefs.add(new NoteLabel(localizer.getMessage("nlExperimentalRestore")), descriptionConstraints); + pnlPrefs.add(cbpAiTimeout, titleConstraints); + pnlPrefs.add(new NoteLabel(localizer.getMessage("nlAITimeout")), descriptionConstraints); + pnlPrefs.add(cbFilteredHands, titleConstraints); pnlPrefs.add(new NoteLabel(localizer.getMessage("nlFilteredHands")), descriptionConstraints); @@ -790,6 +794,10 @@ public enum VSubmenuPreferences implements IVSubmenu { return cbpAiSideboardingMode; } + public FComboBoxPanel getAiTimeoutComboBox() { + return cbpAiTimeout; + } + public FComboBoxPanel getCbpStackAdditionsComboBoxPanel() { return cbpStackAdditions; } diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java index 8f8e41cc47a..a4d9ecf6b9f 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java @@ -47,6 +47,7 @@ public class SimulationTest { Game game = new Game(players, rules, match); game.setAge(GameStage.Play); game.EXPERIMENTAL_RESTORE_SNAPSHOT = false; + game.AI_TIMEOUT = FModel.getPreferences().getPrefInt(FPref.MATCH_AI_TIMEOUT); return game; } diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java index 9e22c5a42da..ca1252e12c4 100644 --- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java +++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java @@ -244,6 +244,10 @@ public class SettingsPage extends TabPage { Forge.getLocalizer().getMessage("cbExperimentalRestore"), Forge.getLocalizer().getMessage("nlExperimentalRestore")), 1); + lstSettings.addItem(new CustomSelectSetting(FPref.MATCH_AI_TIMEOUT, Forge.getLocalizer().getMessage("cbAITimeout"), + Forge.getLocalizer().getMessage("nlAITimeout"), + Lists.newArrayList("5", "10", "60", "120", "240", "300", "600")), + 1); lstSettings.addItem(new BooleanSetting(FPref.FILTERED_HANDS, Forge.getLocalizer().getMessage("cbFilteredHands"), Forge.getLocalizer().getMessage("nlFilteredHands")), diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index 8c25397534c..b77ef340e59 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -3475,4 +3475,6 @@ lblDefaultCollection=Standardsammlungen lblSellable=Verkaufbar lblAutoSellable=Autoverkauf lblNonSellable=Kein Verkauf -lblPromptAutoSell=Aufforderung zum Autoverkauf \ No newline at end of file +lblPromptAutoSell=Aufforderung zum Autoverkauf +cbAITimeout=AI Time-out +nlAITimeout=Zeitüberschreitung in Sekunden für AI, wenn die zu spielenden Zauber berechnet und Angreifer deklariert werden \ No newline at end of file diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index b611eb84be0..9d611ff73ac 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -3194,4 +3194,6 @@ lblDefaultCollection=Default Collections lblSellable=Sellable lblAutoSellable=Auto-Sell lblNonSellable=No-Sell -lblPromptAutoSell=Prompt for auto sell \ No newline at end of file +lblPromptAutoSell=Prompt for auto sell +cbAITimeout=AI Timeout +nlAITimeout=Time-out in seconds for AI when computing for spells to play and declaring attackers \ No newline at end of file diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index 9573eaa3344..81a99c21cbc 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -3482,4 +3482,6 @@ lblDefaultCollection=Colecciones predeterminadas lblSellable=Vendible lblAutoSellable=Venta automática lblNonSellable=Sin venta -lblPromptAutoSell=Solicitar venta automática \ No newline at end of file +lblPromptAutoSell=Solicitar venta automática +cbAITimeout=AI Se acabó el tiempo +nlAITimeout=Tiempo de espera en segundos para AI al calcular los hechizos a jugar y declarar atacantes \ No newline at end of file diff --git a/forge-gui/res/languages/fr-FR.properties b/forge-gui/res/languages/fr-FR.properties index 3a9b25b542c..f375ab44ef8 100644 --- a/forge-gui/res/languages/fr-FR.properties +++ b/forge-gui/res/languages/fr-FR.properties @@ -3483,4 +3483,6 @@ lblDefaultCollection=Collections par défaut lblSellable=Vendable lblAutoSellable=Vente automatique lblNonSellable=Pas de vente -lblPromptAutoSell=Invite pour la vente automatique \ No newline at end of file +lblPromptAutoSell=Invite pour la vente automatique +cbAITimeout=AI Temps mort +nlAITimeout=Délai d'attente en secondes pour AI lors du calcul des sorts à jouer et de la déclaration des attaquants \ No newline at end of file diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties index ed47fe40c9e..5358f2c8e2c 100644 --- a/forge-gui/res/languages/it-IT.properties +++ b/forge-gui/res/languages/it-IT.properties @@ -3481,4 +3481,6 @@ lblDefaultCollection=Raccolte predefinite lblSellable=Vendibile lblAutoSellable=Vendita automatica lblNonSellable=Nessuna vendita -lblPromptAutoSell=Richiesta di vendita auto \ No newline at end of file +lblPromptAutoSell=Richiesta di vendita auto +cbAITimeout=AI Tempo scaduto +nlAITimeout=Timeout in secondi per AI durante il calcolo degli incantesimi da giocare e la dichiarazione degli attaccanti \ No newline at end of file diff --git a/forge-gui/res/languages/ja-JP.properties b/forge-gui/res/languages/ja-JP.properties index cd058a26fbd..4c16cee0701 100644 --- a/forge-gui/res/languages/ja-JP.properties +++ b/forge-gui/res/languages/ja-JP.properties @@ -3477,4 +3477,6 @@ lblDefaultCollection=デフォルトのコレクション lblSellable=販売可能 lblAutoSellable=自動販売 lblNonSellable=売りません -lblPromptAutoSell=自動車販売のプロンプト \ No newline at end of file +lblPromptAutoSell=自動車販売のプロンプト +cbAITimeout=AI タイムアウト +nlAITimeout=プレイする呪文を計算し、攻撃者を宣言するときの AI のタイムアウト (秒単位) \ No newline at end of file diff --git a/forge-gui/res/languages/pt-BR.properties b/forge-gui/res/languages/pt-BR.properties index 5793b6361cb..2e6b7175277 100644 --- a/forge-gui/res/languages/pt-BR.properties +++ b/forge-gui/res/languages/pt-BR.properties @@ -3567,4 +3567,6 @@ lblDefaultCollection=Coleções padrão lblSellable=Vendável lblAutoSellable=Venda automática lblNonSellable=Não vender -lblPromptAutoSell=Solicitação de venda automática \ No newline at end of file +lblPromptAutoSell=Solicitação de venda automática +cbAITimeout=AI Tempo esgotado +nlAITimeout=Tempo limite em segundos para AI ao calcular feitiços para jogar e declarar atacantes \ No newline at end of file diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties index 5ec33090c38..e2ccfe336f1 100644 --- a/forge-gui/res/languages/zh-CN.properties +++ b/forge-gui/res/languages/zh-CN.properties @@ -3468,4 +3468,6 @@ lblDefaultCollection=默认集合 lblSellable=可出售 lblAutoSellable=自动销售 lblNonSellable=不卖 -lblPromptAutoSell=提示汽车出售 \ No newline at end of file +lblPromptAutoSell=提示汽车出售 +cbAITimeout=AI 暂停 +nlAITimeout=计算要播放的咒语并宣布攻击者时,AI 超时(以秒为单位) \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java b/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java index 0b709967128..fc04e5afecb 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java @@ -155,6 +155,7 @@ public class HostedMatch { game = match.createGame(); game.EXPERIMENTAL_RESTORE_SNAPSHOT = FModel.getPreferences().getPrefBoolean(FPref.MATCH_EXPERIMENTAL_RESTORE); + game.AI_TIMEOUT = FModel.getPreferences().getPrefInt(FPref.MATCH_AI_TIMEOUT); StaticData.instance().setSourceImageForClone(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE)); diff --git a/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java index 7898f206ff4..cd7c92340d5 100644 --- a/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java +++ b/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java @@ -205,6 +205,7 @@ public class ForgePreferences extends PreferencesStore { MATCH_AI_SIDEBOARDING_MODE("Human For AI"), MATCH_EXPERIMENTAL_RESTORE("false"), + MATCH_AI_TIMEOUT("5"), ENFORCE_DECK_LEGALITY ("true"), PERFORMANCE_MODE ("false"), FILTERED_HANDS ("false"), From 66c08ab58b30fd3955799c6ab5bc1fcdc92fd6ae Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 15 Nov 2024 06:54:22 +0800 Subject: [PATCH 025/152] try to fix unsupportedoperation --- forge-ai/src/main/java/forge/ai/ability/PlayAi.java | 13 ++++++------- .../java/forge/game/ability/effects/PlayEffect.java | 13 +++++++------ .../src/main/java/forge/game/cost/CostPayment.java | 4 ++-- .../src/main/java/forge/game/mana/ManaPool.java | 13 +++++++------ 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ability/PlayAi.java b/forge-ai/src/main/java/forge/ai/ability/PlayAi.java index 5e7e9c482ff..b37acae6fdc 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PlayAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PlayAi.java @@ -16,7 +16,7 @@ import forge.game.spellability.*; import forge.game.zone.ZoneType; import forge.util.MyRandom; -import java.util.Iterator; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -220,13 +220,12 @@ public class PlayAi extends SpellAbilityAi { if (cards != null & sa.hasParam("ValidSA")) { final String valid[] = sa.getParam("ValidSA").split(","); - final Iterator itr = cards.iterator(); - while (itr.hasNext()) { - final Card c = itr.next(); - if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, ai), SpellAbilityPredicates.isValid(valid, ai , source, sa))) { - itr.remove(); - } + final List invalid = new ArrayList<>(); + for (Card c : cards) { + if(!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, ai), SpellAbilityPredicates.isValid(valid, ai , source, sa))) + invalid.add(c); } + cards.removeAll(invalid); } // Ensure that if a ValidZone is specified, there's at least something to choose from in that zone. diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java index 40f3dd2013c..84e40500b6b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java @@ -191,15 +191,16 @@ public class PlayEffect extends SpellAbilityEffect { if (sa.hasParam("ValidSA")) { final String valid[] = sa.getParam("ValidSA").split(","); - Iterator it = tgtCards.iterator(); - while (it.hasNext()) { - Card c = it.next(); + final List invalid = new ArrayList<>(); + for (Card c : tgtCards) { if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, controller), SpellAbilityPredicates.isValid(valid, controller , source, sa))) { - // it.remove will only remove item from the list part of CardCollection - tgtCards.asSet().remove(c); - it.remove(); + invalid.add(c); } } + // it.remove will only remove item from the list part of CardCollection + if (!invalid.isEmpty()) // why don't we just remove all? + invalid.forEach(tgtCards.asSet()::remove); + if (tgtCards.isEmpty()) { return; } diff --git a/forge-game/src/main/java/forge/game/cost/CostPayment.java b/forge-game/src/main/java/forge/game/cost/CostPayment.java index d7f48b5aa98..7c4ebf3fd2f 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPayment.java +++ b/forge-game/src/main/java/forge/game/cost/CostPayment.java @@ -229,8 +229,8 @@ public class CostPayment extends ManaConversionMatrix { * @return a {@link forge.game.mana.Mana} object. */ public static Mana getMana(final Player player, final ManaCostShard shard, final SpellAbility saBeingPaidFor, - final byte colorsPaid, Map xManaCostPaidByColor) { // player.getManaPool() is not threadsafe if this is called somwewhere concurrently - final List> weightedOptions = selectManaToPayFor(new ManaPool(player), shard, + final byte colorsPaid, Map xManaCostPaidByColor) { + final List> weightedOptions = selectManaToPayFor(player.getManaPool(), shard, saBeingPaidFor, colorsPaid, xManaCostPaidByColor); // Exclude border case diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index bed87be2071..bcecc1f15d4 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -23,11 +23,11 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Multimaps; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import forge.card.mana.ManaAtom; import forge.card.mana.ManaCostShard; import forge.game.Game; @@ -53,14 +53,15 @@ import forge.game.staticability.StaticAbilityUnspentMana; */ public class ManaPool extends ManaConversionMatrix implements Iterable { private final Player owner; - private ListMultimap _floatingMana; - private ListMultimap floatingMana() { - ListMultimap result = _floatingMana; + private Multimap _floatingMana; + private Multimap floatingMana() { + Multimap result = _floatingMana; if (result == null) { synchronized (this) { result = _floatingMana; if (result == null) { - result = Multimaps.synchronizedListMultimap(ArrayListMultimap.create()); + // TODO: can this be replaced with something concurrent + result = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); _floatingMana = result; } } From dcd4fac927404de3384cbbbb78504e22397c9e77 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 09:51:04 +0800 Subject: [PATCH 026/152] update desktop logic --- .../deckeditor/controllers/ACEditorBase.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java index 4f966a099cc..853c5af9df9 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java @@ -585,19 +585,18 @@ public abstract class ACEditorBase 0) - return; - - GuiUtils.addMenuItem(menu, label, null, () -> { - Set colors = new HashSet<>(GuiChoose.getChoices(localizer.getMessage("lblChooseNColors", Lang.getNumeral(val)), val, val, MagicColor.Constant.ONLY_COLORS)); - // make an updated version - PaperCard updated = existingCard.getColorIDVersion(colors); - // remove *quantity* instances of existing card - CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, 1); - // add *quantity* into the deck and set them as selected - cardManager.addItem(updated, 1); - cardManager.setSelectedItem(updated); - }, true, true); + if ((val = existingCard.getRules().getSetColorID()) > 0) { + GuiUtils.addMenuItem(menu, label, null, () -> { + Set colors = new HashSet<>(GuiChoose.getChoices(localizer.getMessage("lblChooseNColors", Lang.getNumeral(val)), val, val, MagicColor.Constant.ONLY_COLORS)); + // make an updated version + PaperCard updated = existingCard.getColorIDVersion(colors); + // remove *quantity* instances of existing card + CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, 1); + // add *quantity* into the deck and set them as selected + cardManager.addItem(updated, 1); + cardManager.setSelectedItem(updated); + }, true, true); + } } } } From 37eed29ad2ef3d920cc186310a6dccb5c314ff74 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 11:08:17 +0800 Subject: [PATCH 027/152] try this multimap for manapool --- forge-core/pom.xml | 5 + .../collect/ConcurrentMultiValuedMap.java | 199 ++++++++++++++++++ .../main/java/forge/game/mana/ManaPool.java | 21 +- 3 files changed, 213 insertions(+), 12 deletions(-) create mode 100644 forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java diff --git a/forge-core/pom.xml b/forge-core/pom.xml index 24740c37a5c..238b8281163 100644 --- a/forge-core/pom.xml +++ b/forge-core/pom.xml @@ -18,6 +18,11 @@ guava 33.3.1-android + + org.apache.commons + commons-collections4 + 4.5.0-M2 + org.apache.commons commons-lang3 diff --git a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java new file mode 100644 index 00000000000..1759db74ced --- /dev/null +++ b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java @@ -0,0 +1,199 @@ +package forge.util.collect; + +import org.apache.commons.collections4.MapIterator; +import org.apache.commons.collections4.MultiSet; +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.map.HashedMap; +import org.apache.commons.collections4.multiset.HashMultiSet; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.function.BiConsumer; + +public class ConcurrentMultiValuedMap implements MultiValuedMap { + + private Map> storage = new ConcurrentHashMap<>(); + + @Override + public int size() { + return storage.size(); + } + + @Override + public boolean isEmpty() { + return storage.isEmpty(); + } + + @Override + public boolean containsKey(Object o) { + //noinspection SuspiciousMethodCalls + return storage.containsKey(o); + } + + @Override + public boolean containsValue(Object o) { + //noinspection SuspiciousMethodCalls + return storage.containsValue(o); + } + + @Override + public boolean containsMapping(Object key, Object value) { + @SuppressWarnings("SuspiciousMethodCalls") + Collection values = storage.get(key); + if(values==null) { + return false; + } + for (V v : values) { + if(v.equals(value)) { + return true; + } + } + return false; + } + + @Override + public Collection get(K k) { + return storage.get(k); + } + + private Collection safeGet(K key) { + return storage.computeIfAbsent(key, value -> new CopyOnWriteArraySet<>()); + } + + @Override + public boolean put(K k, V v) { + return safeGet(k).add(v); + } + + @Override + public boolean putAll(K k, Iterable iterable) { + Collection values = safeGet(k); + for (V v : iterable) { + if(!values.add(v)) { + return false; + } + } + return true; + } + + @Override + public boolean putAll(Map map) { + for (Map.Entry entry : map.entrySet()) { + if(!safeGet(entry.getKey()).add(entry.getValue())) { + return false; + } + } + return true; + } + + @Override + public boolean putAll(MultiValuedMap multiValuedMap) { + MapIterator iterator = multiValuedMap.mapIterator(); + while(iterator.hasNext()) { + if(!safeGet(iterator.getKey()).add(iterator.getValue())) { + return false; + } + } + return true; + } + + @Override + public Collection remove(Object o) { + //noinspection SuspiciousMethodCalls + return storage.remove(o); + } + + @SuppressWarnings("SuspiciousMethodCalls") + @Override + public boolean removeMapping(Object o, Object v) { + Collection values = storage.get(o); + return values != null && values.remove(v); + } + + @Override + public void clear() { + storage.clear(); + } + + @Override + public Collection> entries() { + Collection> collection = new LinkedList<>(); + collectValues((k,v)->{ + Map.Entry mapEntry = new TempMapEntry(k, v); + collection.add(mapEntry); + }); + return collection; + } + + @Override + public MultiSet keys() { + MultiSet multiSet = new HashMultiSet<>(); + multiSet.addAll(storage.keySet()); + return multiSet; + } + + @Override + public Set keySet() { + return storage.keySet(); + } + + @Override + public Collection values() { + List values = new LinkedList<>(); + for (Map.Entry> entry : storage.entrySet()) { + for (V v : entry.getValue()) { + values.add(v); + } + } + return values; + } + + @Override + public Map> asMap() { + return Collections.unmodifiableMap(storage); + } + + @Override + public MapIterator mapIterator() { + HashedMap hashedMap = new HashedMap<>(); + collectValues(hashedMap::put); + return hashedMap.mapIterator(); + } + + private void collectValues(BiConsumer consumer) { + for (Map.Entry> entry : storage.entrySet()) { + for (V v : entry.getValue()) { + consumer.accept(entry.getKey(), v); + } + } + } + + private final class TempMapEntry implements Map.Entry { + + private V val; + private final K key; + + TempMapEntry(K key, V value) { + this.val = value; + this.key = key; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return val; + } + + @Override + public V setValue(V value) { + V old = val; + val = value; + return old; + } + } +} diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index bcecc1f15d4..9d9e0748982 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -23,11 +23,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; import forge.card.mana.ManaAtom; import forge.card.mana.ManaCostShard; import forge.game.Game; @@ -42,6 +39,7 @@ import forge.game.replacement.ReplacementType; import forge.game.spellability.AbilityManaPart; import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbilityUnspentMana; +import forge.util.collect.ConcurrentMultiValuedMap; /** *

@@ -53,15 +51,15 @@ import forge.game.staticability.StaticAbilityUnspentMana; */ public class ManaPool extends ManaConversionMatrix implements Iterable { private final Player owner; - private Multimap _floatingMana; - private Multimap floatingMana() { - Multimap result = _floatingMana; + private ConcurrentMultiValuedMap _floatingMana; + private ConcurrentMultiValuedMap floatingMana() { + ConcurrentMultiValuedMap result = _floatingMana; if (result == null) { synchronized (this) { result = _floatingMana; if (result == null) { // TODO: can this be replaced with something concurrent - result = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); + result = new ConcurrentMultiValuedMap<>(); _floatingMana = result; } } @@ -203,7 +201,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { return removeMana(mana, true); } public boolean removeMana(final Mana mana, boolean updateView) { - boolean result = floatingMana().remove(mana.getColor(), mana); + boolean result = floatingMana().removeMapping(mana.getColor(), mana); if (result && updateView) { owner.updateManaForView(); owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Removed, mana)); @@ -234,6 +232,8 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { Mana manaFound = null; Collection cm = floatingMana().get(colorCode); + if (cm == null) + return false; for (final Mana mana : cm) { if (mana.getManaAbility() != null && !mana.getManaAbility().meetsManaRestrictions(saPaidFor)) { continue; @@ -373,10 +373,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { @Override public Iterator iterator() { - // use synchronizedListMultimap - synchronized (floatingMana()) { - return floatingMana().values().iterator(); - } + return floatingMana().values().iterator(); } } From a988261377e234b18642ddb012886ac562e83f4c Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 11:19:52 +0800 Subject: [PATCH 028/152] check for stack only --- forge-ai/src/main/java/forge/ai/ComputerUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index d6aacc9be91..d63f7d30674 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -150,7 +150,7 @@ public class ComputerUtil { // FIXME: Should not arrive here, though the card seems to be stucked on stack zone and invalidated and nowhere to be found, try to put back to original zone and maybe try to cast again if possible at later time? System.out.println("[" + sa.getActivatingPlayer() + "] AI failed to play " + sa.getHostCard() + " [" + sa.getHostCard().getZone() + "]"); sa.setSkip(true); - if (host != null && hz != null) { + if (host != null && hz != null && hz.is(ZoneType.Stack)) { Card c = game.getAction().moveTo(hz.getZoneType(), host, null, null); for (SpellAbility csa : c.getSpellAbilities()) { csa.setSkip(true); From 8a1d25e4e95ecb462d68bb72fe53baf4b6491eb4 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 11:46:20 +0800 Subject: [PATCH 029/152] fix failing test --- .../java/forge/util/collect/ConcurrentMultiValuedMap.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java index 1759db74ced..77eed9bee81 100644 --- a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java +++ b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java @@ -8,7 +8,7 @@ import org.apache.commons.collections4.multiset.HashMultiSet; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.BiConsumer; public class ConcurrentMultiValuedMap implements MultiValuedMap { @@ -58,7 +58,7 @@ public class ConcurrentMultiValuedMap implements MultiValuedMap { } private Collection safeGet(K key) { - return storage.computeIfAbsent(key, value -> new CopyOnWriteArraySet<>()); + return storage.computeIfAbsent(key, value -> new CopyOnWriteArrayList<>()); } @Override From fc85623343e9792123a0e30650b5ede6cf3995a8 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 11:51:26 +0800 Subject: [PATCH 030/152] better to use a linkedqueue --- .../forge/util/collect/ConcurrentMultiValuedMap.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java index 77eed9bee81..cff6992098a 100644 --- a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java +++ b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java @@ -6,9 +6,14 @@ import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.collections4.map.HashedMap; import org.apache.commons.collections4.multiset.HashMultiSet; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.BiConsumer; public class ConcurrentMultiValuedMap implements MultiValuedMap { @@ -58,7 +63,7 @@ public class ConcurrentMultiValuedMap implements MultiValuedMap { } private Collection safeGet(K key) { - return storage.computeIfAbsent(key, value -> new CopyOnWriteArrayList<>()); + return storage.computeIfAbsent(key, value -> new ConcurrentLinkedQueue<>()); } @Override From 4936fd3950269683c3d526ba2536f8d033a61d9d Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 12:38:00 +0800 Subject: [PATCH 031/152] remove comment, seems it works fine. need to test on android. --- forge-game/src/main/java/forge/game/mana/ManaPool.java | 1 - 1 file changed, 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index 9d9e0748982..166891ea146 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -58,7 +58,6 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { synchronized (this) { result = _floatingMana; if (result == null) { - // TODO: can this be replaced with something concurrent result = new ConcurrentMultiValuedMap<>(); _floatingMana = result; } From a094149ce01dd3efd6618af9fa647d2e1463805a Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 12:40:14 +0800 Subject: [PATCH 032/152] remove redundant check for can cast timing --- forge-ai/src/main/java/forge/ai/AiController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 682f95fc9cb..2332f4afe34 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -1574,7 +1574,7 @@ public class AiController { Iterables.removeIf(saList, spellAbility -> { //don't include removedAI cards if somehow the AI can play the ability or gain control of unsupported card // TODO allow when experimental profile? - return spellAbility.isLandAbility() || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard())) || !spellAbility.canCastTiming(player); + return spellAbility.isLandAbility() || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard())); }); //removed skipped SA skipped = Lists.newArrayList(Iterables.filter(saList, SpellAbility::isSkip)); From b9a56f04e3eb78efebf18eeda3e4441f0515cc3f Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 13:40:27 +0800 Subject: [PATCH 033/152] revert multimap, needs better implementation for this --- .../src/main/java/forge/game/mana/ManaPool.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index 166891ea146..53e239a6e7a 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -23,8 +23,11 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import forge.card.mana.ManaAtom; import forge.card.mana.ManaCostShard; import forge.game.Game; @@ -39,7 +42,6 @@ import forge.game.replacement.ReplacementType; import forge.game.spellability.AbilityManaPart; import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbilityUnspentMana; -import forge.util.collect.ConcurrentMultiValuedMap; /** *

@@ -51,14 +53,14 @@ import forge.util.collect.ConcurrentMultiValuedMap; */ public class ManaPool extends ManaConversionMatrix implements Iterable { private final Player owner; - private ConcurrentMultiValuedMap _floatingMana; - private ConcurrentMultiValuedMap floatingMana() { - ConcurrentMultiValuedMap result = _floatingMana; + private Multimap _floatingMana; + private Multimap floatingMana() { + Multimap result = _floatingMana; if (result == null) { synchronized (this) { result = _floatingMana; if (result == null) { - result = new ConcurrentMultiValuedMap<>(); + result = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); _floatingMana = result; } } @@ -200,7 +202,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { return removeMana(mana, true); } public boolean removeMana(final Mana mana, boolean updateView) { - boolean result = floatingMana().removeMapping(mana.getColor(), mana); + boolean result = floatingMana().remove(mana.getColor(), mana); if (result && updateView) { owner.updateManaForView(); owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Removed, mana)); From bd186003855223c1c5bb412de2f7891667ccfa2a Mon Sep 17 00:00:00 2001 From: tool4ever Date: Fri, 15 Nov 2024 07:01:08 +0100 Subject: [PATCH 034/152] Fix DontPayTapCostWithManaSources (#6578) * Fix DontPayTapCostWithManaSources * Foundations Update Bulletin (engine) --------- Co-authored-by: tool4EvEr --- .../src/main/java/forge/ai/AiController.java | 9 ++++ .../main/java/forge/ai/AiCostDecision.java | 21 +++------ .../src/main/java/forge/ai/ComputerUtil.java | 1 + .../main/java/forge/ai/ComputerUtilCost.java | 46 ++++--------------- .../main/java/forge/ai/ComputerUtilMana.java | 3 +- .../src/main/java/forge/game/card/Card.java | 3 -- .../forge/game/replacement/ReplaceDamage.java | 3 -- .../res/cardsfolder/p/pariahs_shield.txt | 2 +- .../res/cardsfolder/s/sephara_skys_blade.txt | 1 - .../res/cardsfolder/w/warlords_elite.txt | 1 - .../cardsfolder/z/zahid_djinn_of_the_lamp.txt | 2 - 11 files changed, 29 insertions(+), 63 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 14915df4a79..e20e9718b34 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -22,6 +22,8 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; + +import forge.ai.AiCardMemory.MemorySet; import forge.ai.ability.ChangeZoneAi; import forge.ai.ability.LearnAi; import forge.ai.simulation.SpellAbilityPicker; @@ -845,6 +847,13 @@ public class AiController { return AiPlayDecision.CantAfford; } + // check if enough left (pass memory indirectly because we don't want to include those) + Set tappedForMana = AiCardMemory.getMemorySet(player, MemorySet.PAYS_TAP_COST); + if (tappedForMana != null && tappedForMana.isEmpty() && + !ComputerUtilCost.checkTapTypeCost(player, sa.getPayCosts(), host, sa, new CardCollection(tappedForMana))) { + return AiPlayDecision.CantAfford; + } + // if we got here, looks like we can play the final cost and we could properly set up and target the API and // are willing to play the SA return AiPlayDecision.WillPlay; diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 662a42b7318..9ebcff668a4 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -2,6 +2,8 @@ package forge.ai; import com.google.common.base.Predicates; import com.google.common.collect.Lists; + +import forge.ai.AiCardMemory.MemorySet; import forge.card.CardType; import forge.card.MagicColor; import forge.game.Game; @@ -32,6 +34,10 @@ public class AiCostDecision extends CostDecisionMakerBase { discarded = new CardCollection(); tapped = new CardCollection(); + Set tappedForMana = AiCardMemory.getMemorySet(ai0, MemorySet.PAYS_TAP_COST); + if (tappedForMana != null) { + tapped.addAll(tappedForMana); + } } @Override @@ -440,21 +446,6 @@ public class AiCostDecision extends CostDecisionMakerBase { return null; } - if ("DontPayTapCostWithManaSources".equals(source.getSVar("AIPaymentPreference"))) { - CardCollectionView toExclude = - CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), type.split(";"), - ability.getActivatingPlayer(), ability.getHostCard(), ability); - toExclude = CardLists.filter(toExclude, card -> { - for (final SpellAbility sa : card.getSpellAbilities()) { - if (sa.isManaAbility() && sa.getPayCosts().hasTapCost()) { - return true; - } - } - return false; - }); - exclude.addAll(toExclude); - } - String totalP = ""; CardCollectionView totap; if (isVehicle) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 0d83d91aefe..2d4b7308c8c 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -782,6 +782,7 @@ public class ComputerUtil { } CardLists.sortByPowerAsc(typeList); + // TODO prefer noncreatures without tap abilities final CardCollection tapList = new CardCollection(); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index bfee0f90eb7..dfd27b69b04 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -29,6 +29,7 @@ import forge.util.collect.FCollectionView; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -450,7 +451,7 @@ public class ComputerUtilCost { * the source * @return true, if successful */ - public static boolean checkTapTypeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sa, final CardCollection alreadyTapped) { + public static boolean checkTapTypeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sa, final Collection alreadyTapped) { if (cost == null) { return true; } @@ -487,8 +488,8 @@ public class ComputerUtilCost { c = AbilityUtils.calculateAmount(source, part.getAmount(), sa); } CardCollection exclude = new CardCollection(); - if (AiCardMemory.getMemorySet(ai, MemorySet.PAYS_TAP_COST) != null) { - exclude.addAll(AiCardMemory.getMemorySet(ai, MemorySet.PAYS_TAP_COST)); + if (alreadyTapped != null) { + exclude.addAll(alreadyTapped); } // trying to produce mana that includes tapping source that will already be tapped if (exclude.contains(source) && cost.hasTapCost()) { @@ -500,12 +501,12 @@ public class ComputerUtilCost { } CardCollection tapChoices = ComputerUtil.chooseTapType(ai, type, source, cost.hasTapCost(), c, exclude, sa); if (tapChoices != null) { - for (Card choice : tapChoices) { - AiCardMemory.rememberCard(ai, choice, MemorySet.PAYS_TAP_COST); - } - // if manasource gets tapped to produce it also can't help paying another - if (cost.hasTapCost()) { - AiCardMemory.rememberCard(ai, source, MemorySet.PAYS_TAP_COST); + if (alreadyTapped != null) { + alreadyTapped.addAll(tapChoices); + // if manasource gets tapped to produce it also can't help paying another + if (cost.hasTapCost()) { + alreadyTapped.add(source); + } } return true; } @@ -600,33 +601,6 @@ public class ComputerUtilCost { } } - // TODO: Alternate costs which involve both paying mana and tapping a card, e.g. Zahid, Djinn of the Lamp - // Current AI decides on each part separately, thus making it possible for the AI to cheat by - // tapping a mana source for mana and for the tap cost at the same time. Until this is improved, AI - // will not consider mana sources valid for paying the tap cost to avoid this exact situation. - if ("DontPayTapCostWithManaSources".equals(sa.getHostCard().getSVar("AIPaymentPreference"))) { - for (final CostPart part : sa.getPayCosts().getCostParts()) { - if (part instanceof CostTapType) { - CardCollectionView nonManaSources = - CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), part.getType().split(";"), - sa.getActivatingPlayer(), sa.getHostCard(), sa); - nonManaSources = CardLists.filter(nonManaSources, card -> { - boolean hasManaSa = false; - for (final SpellAbility sa1 : card.getSpellAbilities()) { - if (sa1.isManaAbility() && sa1.getPayCosts().hasTapCost()) { - hasManaSa = true; - break; - } - } - return !hasManaSa; - }); - if (nonManaSources.size() < part.convertAmount()) { - return false; - } - } - } - } - // Bail early on Casualty in case there are no cards that would make sense to pay with if (sa.getHostCard().hasKeyword(Keyword.CASUALTY)) { for (final CostPart part : sa.getPayCosts().getCostParts()) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index b23d4d6b250..1dd1c1448e8 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -268,6 +268,7 @@ public class ComputerUtilMana { } for (final SpellAbility ma : saList) { + // this rarely seems like a good idea if (ma.getHostCard() == saHost) { continue; } @@ -276,7 +277,7 @@ public class ComputerUtilMana { continue; } - if (!ComputerUtilCost.checkTapTypeCost(ai, ma.getPayCosts(), ma.getHostCard(), sa, new CardCollection())) { + if (!ComputerUtilCost.checkTapTypeCost(ai, ma.getPayCosts(), ma.getHostCard(), sa, AiCardMemory.getMemorySet(ai, MemorySet.PAYS_TAP_COST))) { continue; } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index b8d7e07a2d7..383828f37bc 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -4096,9 +4096,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr } public final boolean isModified() { - if (!isCreature()) { - return false; - } if (this.isEquipped() || this.hasCounters()) { return true; } diff --git a/forge-game/src/main/java/forge/game/replacement/ReplaceDamage.java b/forge-game/src/main/java/forge/game/replacement/ReplaceDamage.java index cbd590a9d9f..32d3b393a80 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplaceDamage.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplaceDamage.java @@ -92,9 +92,6 @@ public class ReplaceDamage extends ReplacementEffect { return false; } } - if (hasParam("IsEquipping") && !getHostCard().isEquipping()) { - return false; - } if (hasParam("DamageTarget")) { //Lava Burst and Whippoorwill check diff --git a/forge-gui/res/cardsfolder/p/pariahs_shield.txt b/forge-gui/res/cardsfolder/p/pariahs_shield.txt index 1166af2a770..ce73061b4d3 100644 --- a/forge-gui/res/cardsfolder/p/pariahs_shield.txt +++ b/forge-gui/res/cardsfolder/p/pariahs_shield.txt @@ -2,6 +2,6 @@ Name:Pariah's Shield ManaCost:5 Types:Artifact Equipment K:Equip:3 -R:Event$ DamageDone | ActiveZones$ Battlefield | ValidTarget$ You | ReplaceWith$ DmgEquipped | IsEquipping$ True | DamageTarget$ Equipped | Description$ All damage that would be dealt to you is dealt to equipped creature instead. +R:Event$ DamageDone | ActiveZones$ Battlefield | ValidTarget$ You | ReplaceWith$ DmgEquipped | IsPresent$ Card.equipping | PresentDefined$ Self | DamageTarget$ Equipped | Description$ All damage that would be dealt to you is dealt to equipped creature instead. SVar:DmgEquipped:DB$ ReplaceEffect | VarName$ Affected | VarValue$ Equipped | VarType$ Card Oracle:All damage that would be dealt to you is dealt to equipped creature instead.\nEquip {3} diff --git a/forge-gui/res/cardsfolder/s/sephara_skys_blade.txt b/forge-gui/res/cardsfolder/s/sephara_skys_blade.txt index a1873fea7ce..4c2bc985e56 100644 --- a/forge-gui/res/cardsfolder/s/sephara_skys_blade.txt +++ b/forge-gui/res/cardsfolder/s/sephara_skys_blade.txt @@ -3,7 +3,6 @@ ManaCost:4 W W W Types:Legendary Creature Angel PT:7/7 S:Mode$ AlternativeCost | ValidSA$ Spell.Self | EffectZone$ All | Cost$ W tapXType<4/Creature.withFlying> | Description$ You may pay {W} and tap four untapped creatures you control with flying rather than pay this spell's mana cost. -SVar:AIPaymentPreference:DontPayTapCostWithManaSources K:Flying K:Lifelink S:Mode$ Continuous | Affected$ Creature.withFlying+Other+YouCtrl | AddKeyword$ Indestructible | Description$ Other creatures you control with flying have indestructible. diff --git a/forge-gui/res/cardsfolder/w/warlords_elite.txt b/forge-gui/res/cardsfolder/w/warlords_elite.txt index 615cf18e4a1..81de84233b6 100644 --- a/forge-gui/res/cardsfolder/w/warlords_elite.txt +++ b/forge-gui/res/cardsfolder/w/warlords_elite.txt @@ -3,6 +3,5 @@ ManaCost:2 W Types:Creature Human Soldier PT:4/4 A:SP$ PermanentCreature | Cost$ 2 W tapXType<2/Artifact;Creature;Land/artifacts, creatures, and/or lands> | SpellDescription$ As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control. -SVar:AIPaymentPreference:DontPayTapCostWithManaSources DeckHints:Type$Artifact Oracle:As an additional cost to cast this spell, tap two untapped artifacts, creatures, and/or lands you control. diff --git a/forge-gui/res/cardsfolder/z/zahid_djinn_of_the_lamp.txt b/forge-gui/res/cardsfolder/z/zahid_djinn_of_the_lamp.txt index 75c52775ad7..997b8c36bfd 100644 --- a/forge-gui/res/cardsfolder/z/zahid_djinn_of_the_lamp.txt +++ b/forge-gui/res/cardsfolder/z/zahid_djinn_of_the_lamp.txt @@ -4,6 +4,4 @@ Types:Legendary Creature Djinn PT:5/6 K:Flying S:Mode$ AlternativeCost | ValidSA$ Spell.Self | EffectZone$ All | Cost$ 3 U tapXType<1/Artifact> | Description$ You may pay {3}{U} and tap an untapped artifact you control rather than pay this spell's mana cost. -# TODO: Currently the AI may cheat without the following flag by tapping the same artifact for mana and for the tap cost, e.g. 2 Islands + Sol Ring. Remove this flag once the AI is smart enough not to do that. -SVar:AIPaymentPreference:DontPayTapCostWithManaSources Oracle:You may pay {3}{U} and tap an untapped artifact you control rather than pay this spell's mana cost.\nFlying From 4b13e55d83a561bbc86d1a254a3faa5a50637f36 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 15 Nov 2024 16:26:05 +0800 Subject: [PATCH 035/152] fix infinite notice for missing tokens --- forge-core/src/main/java/forge/ImageKeys.java | 4 ++-- forge-gui/src/main/java/forge/util/ImageFetcher.java | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/forge-core/src/main/java/forge/ImageKeys.java b/forge-core/src/main/java/forge/ImageKeys.java index aa0d4912565..803d437aa83 100644 --- a/forge-core/src/main/java/forge/ImageKeys.java +++ b/forge-core/src/main/java/forge/ImageKeys.java @@ -85,7 +85,7 @@ public final class ImageKeys { } private static final Map cachedCards = new HashMap<>(50000); - private static HashSet missingCards = new HashSet<>(); + public static HashSet missingCards = new HashSet<>(); public static void clearMissingCards() { missingCards.clear(); } @@ -310,7 +310,7 @@ public final class ImageKeys { } // System.out.println("File not found, no image created: " + key); - //add missing cards - disable for desktop version for compatibility reasons with autodownloader + // add missing cards - disable for desktop version for compatibility reasons with autodownloader if (isLibGDXPort && !hasSetLookup(filename)) //missing cards with setlookup is handled differently missingCards.add(filename); return null; diff --git a/forge-gui/src/main/java/forge/util/ImageFetcher.java b/forge-gui/src/main/java/forge/util/ImageFetcher.java index 516988793c2..45f87d9dfc8 100644 --- a/forge-gui/src/main/java/forge/util/ImageFetcher.java +++ b/forge-gui/src/main/java/forge/util/ImageFetcher.java @@ -228,6 +228,11 @@ public abstract class ImageFetcher { } if (filename.equalsIgnoreCase("null.jpg")) return; + + if (ImageKeys.missingCards.contains(filename)) + return; + + ImageKeys.missingCards.add(filename); System.err.println("No specified file for '" + filename + "'.. Attempting to download from default Url"); tokenUrl = String.format("%s%s", ForgeConstants.URL_TOKEN_DOWNLOAD, filename); } From f52b7abe61034f1f2a8d90a38cc2828a365b02df Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 15 Nov 2024 20:24:20 +0800 Subject: [PATCH 036/152] create ConcurrentMultiMap, remove unused map --- forge-core/pom.xml | 5 - .../util/collect/ConcurrentMultiMap.java | 135 ++++++++++++ .../collect/ConcurrentMultiValuedMap.java | 204 ------------------ .../main/java/forge/game/mana/ManaPool.java | 9 +- 4 files changed, 140 insertions(+), 213 deletions(-) create mode 100644 forge-core/src/main/java/forge/util/collect/ConcurrentMultiMap.java delete mode 100644 forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java diff --git a/forge-core/pom.xml b/forge-core/pom.xml index 238b8281163..24740c37a5c 100644 --- a/forge-core/pom.xml +++ b/forge-core/pom.xml @@ -18,11 +18,6 @@ guava 33.3.1-android - - org.apache.commons - commons-collections4 - 4.5.0-M2 - org.apache.commons commons-lang3 diff --git a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiMap.java b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiMap.java new file mode 100644 index 00000000000..bae112acebb --- /dev/null +++ b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiMap.java @@ -0,0 +1,135 @@ +package forge.util.collect; + +import com.google.common.collect.ConcurrentHashMultiset; +import com.google.common.collect.Multiset; + +import java.util.Collection; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class ConcurrentMultiMap { + private Map> _MAP; + private Map> MAP() { + Map> result = _MAP; + if (result == null) { + synchronized (this) { + result = _MAP; + if (result == null) { + result = new ConcurrentHashMap<>(); + _MAP = result; + } + } + } + return _MAP; + } + + public int size() { + return MAP().size(); + } + + + public boolean isEmpty() { + return MAP().isEmpty(); + } + + @SuppressWarnings("SuspiciousMethodCalls") + public boolean containsKey(Object key) { + if (key == null) + return false; + return MAP().containsKey(key); + } + + public boolean put(K key, V value) { + return safeGet(key).add(value); + } + + @SuppressWarnings("SuspiciousMethodCalls") + public boolean remove(Object key, Object value) { + if (key == null || value == null) + return false; + return MAP().get(key).remove(value); + } + + public boolean putAll(K key, Iterable iterable) { + Collection values = safeGet(key); + for (V v : iterable) { + if(!values.add(v)) { + return false; + } + } + return true; + } + + public boolean putAll(Map map) { + for (Map.Entry entry : map.entrySet()) { + if(!safeGet(entry.getKey()).add(entry.getValue())) { + return false; + } + } + return true; + } + + public void clear() { + MAP().clear(); + } + + public Collection safeGet(K key) { + return MAP().computeIfAbsent(key, value -> new ConcurrentLinkedQueue<>()); + } + + public Collection get(K key) { + return MAP().get(key); + } + + public Set keySet() { + return MAP().keySet(); + } + + public Multiset keys() { + Multiset multiset = ConcurrentHashMultiset.create(); + multiset.addAll(MAP().keySet()); + return multiset; + } + + public Collection values() { + Queue values = new ConcurrentLinkedQueue<>(); + for (Map.Entry> entry : MAP().entrySet()) { + values.addAll(entry.getValue()); + } + return values; + } + + /*@SuppressWarnings("SuspiciousMethodCalls") + public boolean containsValue(Object value) { + if (value == null) + return false; + return storage.containsValue(value); + } + + public boolean containsEntry(Object key, Object value) { + if (key == null || value == null) + return false; + return storage.entrySet().contains(Maps.immutableEntry(key, value)); + } + + public Collection replaceValues(K key, Iterable values) { + return null; + } + + public Collection removeAll(Object key) { + if (key == null) + return null; + return storage.containsKey(key) ? storage.remove(key) : null; + } + + public Collection> entries() { + return null; + } + + public Map> asMap() { + return null; + }*/ +} diff --git a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java deleted file mode 100644 index cff6992098a..00000000000 --- a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiValuedMap.java +++ /dev/null @@ -1,204 +0,0 @@ -package forge.util.collect; - -import org.apache.commons.collections4.MapIterator; -import org.apache.commons.collections4.MultiSet; -import org.apache.commons.collections4.MultiValuedMap; -import org.apache.commons.collections4.map.HashedMap; -import org.apache.commons.collections4.multiset.HashMultiSet; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.function.BiConsumer; - -public class ConcurrentMultiValuedMap implements MultiValuedMap { - - private Map> storage = new ConcurrentHashMap<>(); - - @Override - public int size() { - return storage.size(); - } - - @Override - public boolean isEmpty() { - return storage.isEmpty(); - } - - @Override - public boolean containsKey(Object o) { - //noinspection SuspiciousMethodCalls - return storage.containsKey(o); - } - - @Override - public boolean containsValue(Object o) { - //noinspection SuspiciousMethodCalls - return storage.containsValue(o); - } - - @Override - public boolean containsMapping(Object key, Object value) { - @SuppressWarnings("SuspiciousMethodCalls") - Collection values = storage.get(key); - if(values==null) { - return false; - } - for (V v : values) { - if(v.equals(value)) { - return true; - } - } - return false; - } - - @Override - public Collection get(K k) { - return storage.get(k); - } - - private Collection safeGet(K key) { - return storage.computeIfAbsent(key, value -> new ConcurrentLinkedQueue<>()); - } - - @Override - public boolean put(K k, V v) { - return safeGet(k).add(v); - } - - @Override - public boolean putAll(K k, Iterable iterable) { - Collection values = safeGet(k); - for (V v : iterable) { - if(!values.add(v)) { - return false; - } - } - return true; - } - - @Override - public boolean putAll(Map map) { - for (Map.Entry entry : map.entrySet()) { - if(!safeGet(entry.getKey()).add(entry.getValue())) { - return false; - } - } - return true; - } - - @Override - public boolean putAll(MultiValuedMap multiValuedMap) { - MapIterator iterator = multiValuedMap.mapIterator(); - while(iterator.hasNext()) { - if(!safeGet(iterator.getKey()).add(iterator.getValue())) { - return false; - } - } - return true; - } - - @Override - public Collection remove(Object o) { - //noinspection SuspiciousMethodCalls - return storage.remove(o); - } - - @SuppressWarnings("SuspiciousMethodCalls") - @Override - public boolean removeMapping(Object o, Object v) { - Collection values = storage.get(o); - return values != null && values.remove(v); - } - - @Override - public void clear() { - storage.clear(); - } - - @Override - public Collection> entries() { - Collection> collection = new LinkedList<>(); - collectValues((k,v)->{ - Map.Entry mapEntry = new TempMapEntry(k, v); - collection.add(mapEntry); - }); - return collection; - } - - @Override - public MultiSet keys() { - MultiSet multiSet = new HashMultiSet<>(); - multiSet.addAll(storage.keySet()); - return multiSet; - } - - @Override - public Set keySet() { - return storage.keySet(); - } - - @Override - public Collection values() { - List values = new LinkedList<>(); - for (Map.Entry> entry : storage.entrySet()) { - for (V v : entry.getValue()) { - values.add(v); - } - } - return values; - } - - @Override - public Map> asMap() { - return Collections.unmodifiableMap(storage); - } - - @Override - public MapIterator mapIterator() { - HashedMap hashedMap = new HashedMap<>(); - collectValues(hashedMap::put); - return hashedMap.mapIterator(); - } - - private void collectValues(BiConsumer consumer) { - for (Map.Entry> entry : storage.entrySet()) { - for (V v : entry.getValue()) { - consumer.accept(entry.getKey(), v); - } - } - } - - private final class TempMapEntry implements Map.Entry { - - private V val; - private final K key; - - TempMapEntry(K key, V value) { - this.val = value; - this.key = key; - } - - @Override - public K getKey() { - return key; - } - - @Override - public V getValue() { - return val; - } - - @Override - public V setValue(V value) { - V old = val; - val = value; - return old; - } - } -} diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index 53e239a6e7a..aa88e107923 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -42,6 +42,7 @@ import forge.game.replacement.ReplacementType; import forge.game.spellability.AbilityManaPart; import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbilityUnspentMana; +import forge.util.collect.ConcurrentMultiMap; /** *

@@ -53,14 +54,14 @@ import forge.game.staticability.StaticAbilityUnspentMana; */ public class ManaPool extends ManaConversionMatrix implements Iterable { private final Player owner; - private Multimap _floatingMana; - private Multimap floatingMana() { - Multimap result = _floatingMana; + private ConcurrentMultiMap _floatingMana; + private ConcurrentMultiMap floatingMana() { + ConcurrentMultiMap result = _floatingMana; if (result == null) { synchronized (this) { result = _floatingMana; if (result == null) { - result = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); + result = new ConcurrentMultiMap<>(); _floatingMana = result; } } From 5c4604839442e6d09bf757fc006b9b70962dd808 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 15 Nov 2024 20:44:02 +0800 Subject: [PATCH 037/152] removed unused import --- forge-game/src/main/java/forge/game/mana/ManaPool.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index aa88e107923..61678c47e8a 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -23,11 +23,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; import forge.card.mana.ManaAtom; import forge.card.mana.ManaCostShard; import forge.game.Game; From 7c35968dfdcbc04348bcf594cfdc49e67eda68a3 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 15 Nov 2024 21:19:15 +0800 Subject: [PATCH 038/152] try to fix ConcurrentModificationException on FCollection -> addAll cause: tapped.addAll(tappedForMana) on AiCardMemory --- .../src/main/java/forge/ai/AiCardMemory.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiCardMemory.java b/forge-ai/src/main/java/forge/ai/AiCardMemory.java index 5bcfacb2ab3..c9963033c90 100644 --- a/forge-ai/src/main/java/forge/ai/AiCardMemory.java +++ b/forge-ai/src/main/java/forge/ai/AiCardMemory.java @@ -18,8 +18,8 @@ package forge.ai; -import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import forge.game.card.Card; import forge.game.player.Player; @@ -79,21 +79,21 @@ public class AiCardMemory { private final Set memRevealedCards; public AiCardMemory() { - this.memMandatoryAttackers = new HashSet<>(); - this.memHeldManaSources = new HashSet<>(); - this.memHeldManaSourcesForCombat = new HashSet<>(); - this.memHeldManaSourcesForEnemyCombat = new HashSet<>(); - this.memAttachedThisTurn = new HashSet<>(); - this.memAnimatedThisTurn = new HashSet<>(); - this.memBouncedThisTurn = new HashSet<>(); - this.memActivatedThisTurn = new HashSet<>(); - this.memTrickAttackers = new HashSet<>(); - this.memChosenFogEffect = new HashSet<>(); - this.memMarkedToAvoidReentry = new HashSet<>(); - this.memHeldManaSourcesForNextSpell = new HashSet<>(); - this.memPaysTapCost = new HashSet<>(); - this.memPaysSacCost = new HashSet<>(); - this.memRevealedCards = new HashSet<>(); + this.memMandatoryAttackers = ConcurrentHashMap.newKeySet(); + this.memHeldManaSources = ConcurrentHashMap.newKeySet(); + this.memHeldManaSourcesForCombat = ConcurrentHashMap.newKeySet(); + this.memHeldManaSourcesForEnemyCombat = ConcurrentHashMap.newKeySet(); + this.memAttachedThisTurn = ConcurrentHashMap.newKeySet(); + this.memAnimatedThisTurn = ConcurrentHashMap.newKeySet(); + this.memBouncedThisTurn = ConcurrentHashMap.newKeySet(); + this.memActivatedThisTurn = ConcurrentHashMap.newKeySet(); + this.memTrickAttackers = ConcurrentHashMap.newKeySet(); + this.memChosenFogEffect = ConcurrentHashMap.newKeySet(); + this.memMarkedToAvoidReentry = ConcurrentHashMap.newKeySet(); + this.memHeldManaSourcesForNextSpell = ConcurrentHashMap.newKeySet(); + this.memPaysTapCost = ConcurrentHashMap.newKeySet(); + this.memPaysSacCost = ConcurrentHashMap.newKeySet(); + this.memRevealedCards = ConcurrentHashMap.newKeySet(); } private Set getMemorySet(MemorySet set) { From 548f97ae363332ca12b5158c1fb8e732fd78a69e Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 15 Nov 2024 21:56:15 +0800 Subject: [PATCH 039/152] dumb check for AI --- forge-ai/src/main/java/forge/ai/AiController.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 763dff96b34..4a73c6434b8 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -1652,6 +1652,11 @@ public class AiController { } } + // dumb check needed? don't really know why the AI tapped the land for nothing at EOT of his opponent. + // without this, you can see the weird tapping of single land -> mana ability. with this, it doesn't confuse human player + if (sa.isManaAbility() && game.getPhaseHandler() != null && game.getPhaseHandler().is(PhaseType.END_OF_TURN) && !game.getPhaseHandler().isPlayerTurn(player)) + return 0; + sa.setActivatingPlayer(player, true); SpellAbility root = sa.getRootAbility(); From a53dc4ef852e7fff3045db4da2893a9b30ce392a Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sat, 16 Nov 2024 06:49:36 +0800 Subject: [PATCH 040/152] Update snapshots-android.yml add missing version.txt --- .github/workflows/snapshots-android.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/snapshots-android.yml b/.github/workflows/snapshots-android.yml index d1fdb04adb9..bfc32f45f87 100644 --- a/.github/workflows/snapshots-android.yml +++ b/.github/workflows/snapshots-android.yml @@ -93,6 +93,7 @@ jobs: mkdir upload mv /home/runner/work/forge/forge/forge-gui-android/target/*-signed-aligned.apk upload/ mv /home/runner/work/forge/forge/forge-gui-android/target/assets.zip upload/ + mv /home/runner/work/forge/forge/forge-gui-android/target/classes/assets/version.txt upload/ cd upload ls env: From cf84ae5719e6ee1755002d0a9fdd3e5222d915e2 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sat, 16 Nov 2024 08:33:45 +0800 Subject: [PATCH 041/152] fix NoSuchElementException --- .../main/java/forge/gamemodes/match/input/InputQueue.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputQueue.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputQueue.java index b669697d598..7319d31e2d2 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputQueue.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputQueue.java @@ -56,8 +56,9 @@ public class InputQueue extends Observable { if (topMostInput != inp) { System.out.println("Cannot remove input " + inp.getClass().getSimpleName() + " because it's not on top of stack. Stack = " + inputStack ); - } else { - inputStack.pop(); + } else if (topMostInput != null) { + // if topMostInput is null then it means the inputstack is already empty, why this is called twice? + inputStack.pop(); } updateObservers(); } From c11ca5d3fbdca457c25d66a98f9736f9d6475053 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sat, 16 Nov 2024 09:39:28 +0800 Subject: [PATCH 042/152] Remove input check upload on android snapshot workflow Don't know why this input is needed but it seems it's not taken into account even with default value to true when the cron job triggers, we have already test build workflow for android apk along with debug signing for checking if build and signing succeeded. Check discord reports on missing/cannot update reports. --- .github/workflows/snapshots-android.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/snapshots-android.yml b/.github/workflows/snapshots-android.yml index bfc32f45f87..793f4cddd7f 100644 --- a/.github/workflows/snapshots-android.yml +++ b/.github/workflows/snapshots-android.yml @@ -8,11 +8,11 @@ on: description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' required: false default: false - upload_package: - type: boolean - description: 'Upload the completed Android package' - required: false - default: true + #upload_package: + # type: boolean + # description: 'Upload the completed Android package' + # required: false + # default: true schedule: # * is a special character in YAML so you have to quote this string - cron: '00 19 * * *' @@ -101,7 +101,7 @@ jobs: - name: 📂 Sync files uses: SamKirkland/FTP-Deploy-Action@v4.3.4 - if: ${{ inputs.upload_package }} + #if: ${{ inputs.upload_package }} with: server: ftp.cardforge.org username: ${{ secrets.FTP_USERNAME }} From 95e33049483fe1173f14e18fa462b827a3e71539 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sat, 16 Nov 2024 18:24:22 +0800 Subject: [PATCH 043/152] add coloridversion check --- forge-core/src/main/java/forge/item/PaperCard.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index b81f3825266..7ecbce6c09b 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -165,6 +165,12 @@ public class PaperCard implements Comparable, InventoryItemFromSet, return sellable; } public PaperCard getColorIDVersion(Set colors) { + if (colors == null && this.colorID == null) + return this; + if (this.colorID != null && this.colorID.equals(colors)) + return this; + if (colors != null && colors.equals(this.colorID)) + return this; return new PaperCard(this.rules, this.edition, this.rarity, this.artIndex, this.foil, String.valueOf(collectorNumber), this.artist, this.functionalVariant, this.noSell, colors); } From 5597d0bf31d361ed3b9563ede745902e005c9e8c Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sat, 16 Nov 2024 19:50:09 +0800 Subject: [PATCH 044/152] fix arrows drawing out of bounds --- .../src/forge/screens/match/MatchScreen.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java index 41528cf4aaa..1d245df8c6a 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java @@ -466,22 +466,35 @@ public class MatchScreen extends FScreen { final GameView game = MatchController.instance.getGameView(); try { for (PlayerView p : game.getPlayers()) { - if (p != null && playerPanelsList.contains(getPlayerPanel(p))) { + if (p == null) + continue; + VPlayerPanel playerPanel = getPlayerPanel(p); + if (playerPanel != null && playerPanelsList.contains(playerPanel)) { playerViewSet.add(p); if (p.getBattlefield() != null) { for (CardView c : p.getBattlefield()) { - endpoints.put(c.getId(), CardAreaPanel.get(c).getTargetingArrowOrigin()); + CardAreaPanel panel = CardAreaPanel.get(c); + Vector2 origin = panel.getTargetingArrowOrigin(); + //outside left bounds + if (origin.x < playerPanel.getField().getLeft()) + continue; + //outside right bounds + if (origin.x > playerPanel.getField().getRight()) + continue; + endpoints.put(c.getId(), origin); cardsonBattlefield.add(c); } } } } + if (endpoints.isEmpty()) + return; //draw arrows for combat final CombatView combat = game.getCombat(); for (CardView c : cardsonBattlefield) { TargetingOverlay.assembleArrows(g, c, endpoints, combat, playerViewSet); } - } catch (Exception e) { + } catch (Exception ignored) { } } From f46e22cc54606778142c66991dd537870955e9ee Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sat, 16 Nov 2024 20:13:17 +0800 Subject: [PATCH 045/152] minor cleanup --- .../src/forge/screens/match/MatchScreen.java | 64 ++++++++----------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java index 1d245df8c6a..0b84c8a1700 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java @@ -392,8 +392,7 @@ public class MatchScreen extends FScreen { if (Forge.isLandscapeMode() && (!GuiBase.isAndroid() || Forge.hasGamepad()) && !CardZoom.isOpen() && potentialListener != null) { for (FDisplayObject object : potentialListener) { if (object != null) { - if (object instanceof FCardPanel) { - FCardPanel cardPanel = (FCardPanel) object; + if (object instanceof FCardPanel cardPanel) { try { if (cardPanel.isHovered()) { VPlayerPanel vPlayerPanel = getPlayerPanel(cardPanel.getCard().getController()); @@ -431,9 +430,9 @@ public class MatchScreen extends FScreen { } catch (Exception e) { e.printStackTrace(); } - } else if (object instanceof VStack.StackInstanceDisplay) { + } else if (object instanceof VStack.StackInstanceDisplay vstackDisplay) { try { - CardView cardView = ((VStack.StackInstanceDisplay) object).stackInstance.getSourceCard(); + CardView cardView = vstackDisplay.stackInstance.getSourceCard(); if (object.isHovered() && cardView != null && getStack().isVisible()) { float cardW = getHeight() * 0.45f; float cardH = FCardPanel.ASPECT_RATIO * cardW; @@ -524,7 +523,7 @@ public class MatchScreen extends FScreen { } } revalidate(true); - } catch (Exception e) { + } catch (Exception ignored) { } } break; @@ -538,7 +537,7 @@ public class MatchScreen extends FScreen { selectedPlayerPanel().getSelectedRow().setNextSelected(1); } revalidate(true); - } catch (Exception e) { + } catch (Exception ignored) { } } break; @@ -562,7 +561,7 @@ public class MatchScreen extends FScreen { } } revalidate(true); - } catch (Exception e) { + } catch (Exception ignored) { } } break; @@ -576,7 +575,7 @@ public class MatchScreen extends FScreen { selectedPlayerPanel().getSelectedRow().setPreviousSelected(1); } revalidate(true); - } catch (Exception e) { + } catch (Exception ignored) { } } break; @@ -589,7 +588,7 @@ public class MatchScreen extends FScreen { } else { selectedPlayerPanel().getSelectedRow().showZoom(); } - } catch (Exception e) { + } catch (Exception ignored) { } } break; @@ -604,7 +603,7 @@ public class MatchScreen extends FScreen { //nullPotentialListener(); selectedPlayerPanel().getSelectedRow().tapChild(); } - } catch (Exception e) { + } catch (Exception ignored) { } } break; @@ -628,7 +627,7 @@ public class MatchScreen extends FScreen { return getActivePrompt().getBtnCancel().trigger(); //trigger Cancel if can't trigger OK case Keys.ESCAPE: if (!FModel.getPreferences().getPrefBoolean(FPref.UI_ALLOW_ESC_TO_END_TURN) && !Forge.hasGamepad()) {//bypass check - if (getActivePrompt().getBtnCancel().getText().equals(Forge.getLocalizer().getInstance().getMessage("lblEndTurn"))) { + if (getActivePrompt().getBtnCancel().getText().equals(Forge.getLocalizer().getMessage("lblEndTurn"))) { return false; } } @@ -809,12 +808,11 @@ public class MatchScreen extends FScreen { } public void updateSingleCard(final CardView card) { - final CardAreaPanel pnl = CardAreaPanel.get(card); - if (pnl == null) { + if (card == null) return; - } + final CardAreaPanel pnl = CardAreaPanel.get(card); final ZoneType zone = card.getZone(); - if (zone != null && zone == ZoneType.Battlefield) { + if (zone == ZoneType.Battlefield) { pnl.updateCard(card); } else { //ensure card not on battlefield is reset such that it no longer thinks it's on the battlefield pnl.setTapped(false); @@ -831,28 +829,18 @@ public class MatchScreen extends FScreen { FSkinTexture getBG() { if (Forge.isMobileAdventureMode) { - switch (GameScene.instance().getAdventurePlayerLocation(false, true)) { - case "green": - return FSkinTexture.ADV_BG_FOREST; - case "black": - return FSkinTexture.ADV_BG_SWAMP; - case "red": - return FSkinTexture.ADV_BG_MOUNTAIN; - case "blue": - return FSkinTexture.ADV_BG_ISLAND; - case "white": - return FSkinTexture.ADV_BG_PLAINS; - case "waste": - return FSkinTexture.ADV_BG_WASTE; - case "cave": - return FSkinTexture.ADV_BG_CAVE; - case "dungeon": - return FSkinTexture.ADV_BG_DUNGEON; - case "castle": - return FSkinTexture.ADV_BG_CASTLE; - default: - return FSkinTexture.ADV_BG_COMMON; - } + return switch (GameScene.instance().getAdventurePlayerLocation(false, true)) { + case "green" -> FSkinTexture.ADV_BG_FOREST; + case "black" -> FSkinTexture.ADV_BG_SWAMP; + case "red" -> FSkinTexture.ADV_BG_MOUNTAIN; + case "blue" -> FSkinTexture.ADV_BG_ISLAND; + case "white" -> FSkinTexture.ADV_BG_PLAINS; + case "waste" -> FSkinTexture.ADV_BG_WASTE; + case "cave" -> FSkinTexture.ADV_BG_CAVE; + case "dungeon" -> FSkinTexture.ADV_BG_DUNGEON; + case "castle" -> FSkinTexture.ADV_BG_CASTLE; + default -> FSkinTexture.ADV_BG_COMMON; + }; } return FSkinTexture.BG_MATCH; } @@ -965,7 +953,7 @@ public class MatchScreen extends FScreen { if (!hasActivePlane()) return; } - boolean isGameFast = MatchController.instance.isGameFast(); + //boolean isGameFast = MatchController.instance.isGameFast(); //this used to control animation speed float midField = topPlayerPanel.getBottom(); float promptHeight = !Forge.isLandscapeMode() || bottomPlayerPrompt == null ? 0f : bottomPlayerPrompt.getHeight() / 1.3f; float x = topPlayerPanel.getField().getLeft(); From 12613328fe7e282329dda8651552e4f988cbb00d Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sun, 17 Nov 2024 00:37:22 +0800 Subject: [PATCH 046/152] Update PlayEffect.java --- .../java/forge/game/ability/effects/PlayEffect.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java index 84e40500b6b..74f2ff66b31 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java @@ -233,16 +233,16 @@ public class PlayEffect extends SpellAbilityEffect { while (!tgtCards.isEmpty() && amount > 0 && totalCMCLimit >= 0) { if (hasTotalCMCLimit) { // filter out cards with mana value greater than limit - Iterator it = tgtCards.iterator(); final String [] valid = {"Spell.cmcLE" + totalCMCLimit}; - while (it.hasNext()) { - Card c = it.next(); + List invalid = new ArrayList<>(); + for (Card c : tgtCards) { if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, controller), SpellAbilityPredicates.isValid(valid, controller , c, sa))) { - // it.remove will only remove item from the list part of CardCollection - tgtCards.asSet().remove(c); - it.remove(); + invalid.add(c); } } + // it.remove will only remove item from the list part of CardCollection + if (!invalid.isEmpty()) + invalid.forEach(tgtCards.asSet()::remove); if (tgtCards.isEmpty()) break; params.put("CMCLimit", totalCMCLimit); From f72ab6f22a6109128f5a13beb33af158b5937878 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sun, 17 Nov 2024 00:45:27 +0800 Subject: [PATCH 047/152] use removeall the fcollection will check if removing from set is successful, it will also remove the element from the list --- .../main/java/forge/game/ability/effects/PlayEffect.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java index 74f2ff66b31..c09371d965d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java @@ -197,9 +197,8 @@ public class PlayEffect extends SpellAbilityEffect { invalid.add(c); } } - // it.remove will only remove item from the list part of CardCollection - if (!invalid.isEmpty()) // why don't we just remove all? - invalid.forEach(tgtCards.asSet()::remove); + if (!invalid.isEmpty()) + tgtCards.removeAll(invalid); if (tgtCards.isEmpty()) { return; @@ -242,7 +241,7 @@ public class PlayEffect extends SpellAbilityEffect { } // it.remove will only remove item from the list part of CardCollection if (!invalid.isEmpty()) - invalid.forEach(tgtCards.asSet()::remove); + tgtCards.removeAll(invalid); if (tgtCards.isEmpty()) break; params.put("CMCLimit", totalCMCLimit); From 6c847e00a29b796afe1f099a0fbcd12bbd97fb89 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sun, 17 Nov 2024 06:49:50 +0800 Subject: [PATCH 048/152] refactor AiCardMemory --- .../src/main/java/forge/ai/AiCardMemory.java | 152 ++++-------------- 1 file changed, 34 insertions(+), 118 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiCardMemory.java b/forge-ai/src/main/java/forge/ai/AiCardMemory.java index c9963033c90..dba3c6e57dd 100644 --- a/forge-ai/src/main/java/forge/ai/AiCardMemory.java +++ b/forge-ai/src/main/java/forge/ai/AiCardMemory.java @@ -18,9 +18,11 @@ package forge.ai; +import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import forge.game.card.Card; import forge.game.player.Player; @@ -62,75 +64,26 @@ public class AiCardMemory { REVEALED_CARDS // These cards were recently revealed to the AI by a call to PlayerControllerAi.reveal } - private final Set memMandatoryAttackers; - private final Set memTrickAttackers; - private final Set memHeldManaSources; - private final Set memHeldManaSourcesForCombat; - private final Set memHeldManaSourcesForEnemyCombat; - private final Set memHeldManaSourcesForNextSpell; - private final Set memAttachedThisTurn; - private final Set memAnimatedThisTurn; - private final Set memBouncedThisTurn; - private final Set memActivatedThisTurn; - private final Set memChosenFogEffect; - private final Set memMarkedToAvoidReentry; - private final Set memPaysTapCost; - private final Set memPaysSacCost; - private final Set memRevealedCards; + private Map> _memoryMap; + private Map> memoryMap() { + Map> result = _memoryMap; + if (result == null) { + synchronized (this) { + result = _memoryMap; + if (result == null) { + result = Maps.newConcurrentMap(); + _memoryMap = result; + } + } + } + return _memoryMap; + } public AiCardMemory() { - this.memMandatoryAttackers = ConcurrentHashMap.newKeySet(); - this.memHeldManaSources = ConcurrentHashMap.newKeySet(); - this.memHeldManaSourcesForCombat = ConcurrentHashMap.newKeySet(); - this.memHeldManaSourcesForEnemyCombat = ConcurrentHashMap.newKeySet(); - this.memAttachedThisTurn = ConcurrentHashMap.newKeySet(); - this.memAnimatedThisTurn = ConcurrentHashMap.newKeySet(); - this.memBouncedThisTurn = ConcurrentHashMap.newKeySet(); - this.memActivatedThisTurn = ConcurrentHashMap.newKeySet(); - this.memTrickAttackers = ConcurrentHashMap.newKeySet(); - this.memChosenFogEffect = ConcurrentHashMap.newKeySet(); - this.memMarkedToAvoidReentry = ConcurrentHashMap.newKeySet(); - this.memHeldManaSourcesForNextSpell = ConcurrentHashMap.newKeySet(); - this.memPaysTapCost = ConcurrentHashMap.newKeySet(); - this.memPaysSacCost = ConcurrentHashMap.newKeySet(); - this.memRevealedCards = ConcurrentHashMap.newKeySet(); } private Set getMemorySet(MemorySet set) { - switch (set) { - case MANDATORY_ATTACKERS: - return memMandatoryAttackers; - case TRICK_ATTACKERS: - return memTrickAttackers; - case HELD_MANA_SOURCES_FOR_MAIN2: - return memHeldManaSources; - case HELD_MANA_SOURCES_FOR_DECLBLK: - return memHeldManaSourcesForCombat; - case HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK: - return memHeldManaSourcesForEnemyCombat; - case HELD_MANA_SOURCES_FOR_NEXT_SPELL: - return memHeldManaSourcesForNextSpell; - case ATTACHED_THIS_TURN: - return memAttachedThisTurn; - case ANIMATED_THIS_TURN: - return memAnimatedThisTurn; - case BOUNCED_THIS_TURN: - return memBouncedThisTurn; - case ACTIVATED_THIS_TURN: - return memActivatedThisTurn; - case CHOSEN_FOG_EFFECT: - return memChosenFogEffect; - case MARKED_TO_AVOID_REENTRY: - return memMarkedToAvoidReentry; - case PAYS_TAP_COST: - return memPaysTapCost; - case PAYS_SAC_COST: - return memPaysSacCost; - case REVEALED_CARDS: - return memRevealedCards; - default: - return null; - } + return memoryMap().computeIfAbsent(set, value -> Sets.newConcurrentHashSet()); } /** @@ -145,10 +98,7 @@ public class AiCardMemory { if (c == null) { return false; } - - Set memorySet = getMemorySet(set); - - return memorySet != null && memorySet.contains(c); + return getMemorySet(set).contains(c); } /** @@ -160,16 +110,11 @@ public class AiCardMemory { * @return true, if at least one card with the given name is remembered in the given memory set */ public boolean isRememberedCardByName(String cardName, MemorySet set) { - Set memorySet = getMemorySet(set); - - if (memorySet != null) { - for (Card c : memorySet) { - if (c.getName().equals(cardName)) { - return true; - } + for (Card c : getMemorySet(set)) { + if (c.getName().equals(cardName)) { + return true; } } - return false; } @@ -184,16 +129,11 @@ public class AiCardMemory { * @return true, if at least one card with the given name is remembered in the given memory set */ public boolean isRememberedCardByName(String cardName, MemorySet set, Player owner) { - Set memorySet = getMemorySet(set); - - if (memorySet != null) { - for (Card c : memorySet) { - if (c.getName().equals(cardName) && c.getOwner().equals(owner)) { - return true; - } + for (Card c : getMemorySet(set)) { + if (c.getName().equals(cardName) && c.getOwner().equals(owner)) { + return true; } } - return false; } @@ -207,14 +147,7 @@ public class AiCardMemory { public boolean rememberCard(Card c, MemorySet set) { if (c == null) return false; - - Set memorySet = getMemorySet(set); - - if (memorySet != null) { - memorySet.add(c); - } - - return true; + return getMemorySet(set).add(c); } /** @@ -231,14 +164,7 @@ public class AiCardMemory { if (!isRememberedCard(c, set)) { return false; } - - Set memorySet = getMemorySet(set); - - if (memorySet != null) { - memorySet.remove(c); - } - - return true; + return getMemorySet(set).remove(c); } /** @@ -249,16 +175,11 @@ public class AiCardMemory { * @return true, if at least one card with the given name was previously remembered in the given memory set and was successfully forgotten */ public boolean forgetAnyCardWithName(String cardName, MemorySet set) { - Set memorySet = getMemorySet(set); - - if (memorySet != null) { - for (Card c : memorySet) { - if (c.getName().equals(cardName)) { - return forgetCard(c, set); - } + for (Card c : getMemorySet(set)) { + if (c.getName().equals(cardName)) { + return forgetCard(c, set); } } - return false; } @@ -271,16 +192,11 @@ public class AiCardMemory { * @return true, if at least one card with the given name was previously remembered in the given memory set and was successfully forgotten */ public boolean forgetAnyCardWithName(String cardName, MemorySet set, Player owner) { - Set memorySet = getMemorySet(set); - - if (memorySet != null) { - for (Card c : memorySet) { - if (c.getName().equals(cardName) && c.getOwner().equals(owner)) { - return forgetCard(c, set); - } + for (Card c : getMemorySet(set)) { + if (c.getName().equals(cardName) && c.getOwner().equals(owner)) { + return forgetCard(c, set); } } - return false; } @@ -374,4 +290,4 @@ public class AiCardMemory { public static boolean isMemorySetEmpty(AiController aic, MemorySet set) { return aic.getCardMemory().isMemorySetEmpty(set); } -} \ No newline at end of file +} From f6d2d420bb310b6abe5131102ac356e48ffecc1d Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sun, 17 Nov 2024 07:14:33 +0800 Subject: [PATCH 049/152] use anymatch --- forge-ai/src/main/java/forge/ai/AiCardMemory.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiCardMemory.java b/forge-ai/src/main/java/forge/ai/AiCardMemory.java index dba3c6e57dd..8d000584474 100644 --- a/forge-ai/src/main/java/forge/ai/AiCardMemory.java +++ b/forge-ai/src/main/java/forge/ai/AiCardMemory.java @@ -110,12 +110,7 @@ public class AiCardMemory { * @return true, if at least one card with the given name is remembered in the given memory set */ public boolean isRememberedCardByName(String cardName, MemorySet set) { - for (Card c : getMemorySet(set)) { - if (c.getName().equals(cardName)) { - return true; - } - } - return false; + return getMemorySet(set).stream().anyMatch(c -> c.getName().equals(cardName)); } /** @@ -129,12 +124,7 @@ public class AiCardMemory { * @return true, if at least one card with the given name is remembered in the given memory set */ public boolean isRememberedCardByName(String cardName, MemorySet set, Player owner) { - for (Card c : getMemorySet(set)) { - if (c.getName().equals(cardName) && c.getOwner().equals(owner)) { - return true; - } - } - return false; + return getMemorySet(set).stream().anyMatch(c -> c.getName().equals(cardName) && c.getOwner().equals(owner)); } /** From 5dc84a68c473e4d323688b8413ccfefbb065d3fe Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sun, 17 Nov 2024 08:00:29 +0800 Subject: [PATCH 050/152] remove threadsafeIterable The original purpose which is to create a copy of linkedlist to iterate to avoid concurrent modification, but we use copyonwritearray design and it is already thread safe and iteration while modification is handled internally. --- .../src/main/java/forge/ai/AiAttackController.java | 2 +- forge-ai/src/main/java/forge/ai/SpecialCardAi.java | 4 ++-- .../main/java/forge/util/collect/FCollection.java | 13 ------------- .../java/forge/util/collect/FCollectionView.java | 8 -------- .../src/main/java/forge/game/GameAction.java | 14 +++++++------- .../forge/game/ability/effects/EffectEffect.java | 2 +- .../forge/game/ability/effects/LifeSetEffect.java | 2 +- forge-gui-mobile/src/forge/Graphics.java | 5 +++-- forge-gui-mobile/src/forge/assets/FSkinFont.java | 7 ++----- .../match/input/InputConfirmMulligan.java | 2 +- 10 files changed, 18 insertions(+), 41 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 215ee85373b..5021d827064 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -774,7 +774,7 @@ public class AiAttackController { currentAttackTax += taxCMC; int damage = ComputerUtilCombat.getAttack(attacker); - for (Card blocker : remainingBlockers.threadSafeIterable()) { + for (Card blocker : remainingBlockers) { if (CombatUtil.canBlock(attacker, blocker) && damage > 0) { damage -= ComputerUtilCombat.shieldDamage(attacker, blocker); remainingBlockers.remove(blocker); diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index c737cd27657..f5e769747b5 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -386,7 +386,7 @@ public class SpecialCardAi { public static class Donate { public static boolean considerTargetingOpponent(final Player ai, final SpellAbility sa) { final Card donateTarget = ComputerUtil.getCardPreference(ai, sa.getHostCard(), "DonateMe", CardLists.filter( - ai.getCardsIn(ZoneType.Battlefield).threadSafeIterable(), CardPredicates.hasSVar("DonateMe"))); + ai.getCardsIn(ZoneType.Battlefield), CardPredicates.hasSVar("DonateMe"))); if (donateTarget != null) { // first filter for opponents which can be targeted by SA final Iterable oppList = Iterables.filter(ai.getOpponents(), @@ -421,7 +421,7 @@ public class SpecialCardAi { } public static boolean considerDonatingPermanent(final Player ai, final SpellAbility sa) { - Card donateTarget = ComputerUtil.getCardPreference(ai, sa.getHostCard(), "DonateMe", CardLists.filter(ai.getCardsIn(ZoneType.Battlefield).threadSafeIterable(), CardPredicates.hasSVar("DonateMe"))); + Card donateTarget = ComputerUtil.getCardPreference(ai, sa.getHostCard(), "DonateMe", CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.hasSVar("DonateMe"))); if (donateTarget != null) { sa.resetTargets(); sa.getTargets().add(donateTarget); diff --git a/forge-core/src/main/java/forge/util/collect/FCollection.java b/forge-core/src/main/java/forge/util/collect/FCollection.java index 16ba3a21f3a..43c428b590d 100644 --- a/forge-core/src/main/java/forge/util/collect/FCollection.java +++ b/forge-core/src/main/java/forge/util/collect/FCollection.java @@ -573,16 +573,6 @@ public class FCollection implements List, /*Set,*/ FCollectionView, } } - /** - * {@inheritDoc} - */ - @Override - public Iterable threadSafeIterable() { - return list(); - //create a new linked list for iterating to make it thread safe and avoid concurrent modification exceptions - //return Iterables.unmodifiableIterable(new LinkedList<>(list)); - } - @Override public T get(final T obj) { if (obj == null) { @@ -688,9 +678,6 @@ public class FCollection implements List, /*Set,*/ FCollectionView, } throw new IndexOutOfBoundsException("Any index is out of bounds for an empty collection"); } - @Override public final Iterable threadSafeIterable() { - return this; - } @Override public final Object[] toArray() { return ArrayUtils.EMPTY_OBJECT_ARRAY; } @Override @SuppressWarnings("hiding") diff --git a/forge-core/src/main/java/forge/util/collect/FCollectionView.java b/forge-core/src/main/java/forge/util/collect/FCollectionView.java index 56da5c6d0a4..f4d99e01a54 100644 --- a/forge-core/src/main/java/forge/util/collect/FCollectionView.java +++ b/forge-core/src/main/java/forge/util/collect/FCollectionView.java @@ -67,13 +67,5 @@ public interface FCollectionView extends Iterable { */ List subList(int fromIndex, int toIndex); - /** - * Get a thread-safe {@link Iterable}, ie. one that is not backed by this - * collection, but rather represents the state at the time this method is - * called. The iterator is read-only (does not support - * {@link Iterator#remove()}), as such an operation would have no meaning. - */ - Iterable threadSafeIterable(); - T get(final T obj); } \ No newline at end of file diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 46dd526406d..8b895351918 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -1187,7 +1187,7 @@ public class GameAction { // in that case Always trigger should not Run if (preList.isEmpty()) { for (Player p : game.getPlayers()) { - for (Card c : p.getCardsIn(ZoneType.Battlefield).threadSafeIterable()) { + for (Card c : p.getCardsIn(ZoneType.Battlefield)) { if (!c.getController().equals(p)) { controllerChangeZoneCorrection(c); affectedCards.add(c); @@ -1269,7 +1269,7 @@ public class GameAction { if (zt == ZoneType.Battlefield) { continue; } - for (final Card c : p.getCardsIn(zt).threadSafeIterable()) { + for (final Card c : p.getCardsIn(zt)) { checkAgain |= stateBasedAction704_5d(c); // Dungeon Card won't affect other cards, so don't need to set checkAgain stateBasedAction_Dungeon(c); @@ -1381,10 +1381,10 @@ public class GameAction { if ((game.getRules().hasAppliedVariant(GameType.Commander) || game.getRules().hasAppliedVariant(GameType.Brawl) || game.getRules().hasAppliedVariant(GameType.Planeswalker)) && !checkAgain) { - for (final Card c : p.getCardsIn(ZoneType.Graveyard).threadSafeIterable()) { + for (final Card c : p.getCardsIn(ZoneType.Graveyard)) { checkAgain |= stateBasedAction_Commander(c, mapParams); } - for (final Card c : p.getCardsIn(ZoneType.Exile).threadSafeIterable()) { + for (final Card c : p.getCardsIn(ZoneType.Exile)) { checkAgain |= stateBasedAction_Commander(c, mapParams); } } @@ -1590,7 +1590,7 @@ public class GameAction { CardCollection toAssign = new CardCollection(); - for (final Card c : p.getCreaturesInPlay().threadSafeIterable()) { + for (final Card c : p.getCreaturesInPlay()) { if (!c.hasSector()) { toAssign.add(c); if (!checkAgain) { @@ -2040,11 +2040,11 @@ public class GameAction { private void drawStartingHand(Player p1) { //check initial hand - List lib1 = Lists.newArrayList(p1.getZone(ZoneType.Library).getCards().threadSafeIterable()); + List lib1 = Lists.newArrayList(p1.getZone(ZoneType.Library).getCards()); List hand1 = lib1.subList(0,p1.getMaxHandSize()); //shuffle - List shuffledCards = Lists.newArrayList(p1.getZone(ZoneType.Library).getCards().threadSafeIterable()); + List shuffledCards = Lists.newArrayList(p1.getZone(ZoneType.Library).getCards()); CollectionUtil.shuffle(shuffledCards); //check a second hand diff --git a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java index 09382b1e310..da64d792160 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java @@ -131,7 +131,7 @@ public class EffectEffect extends SpellAbilityEffect { // Unique$ is for effects that should be one per player (e.g. Gollum, Obsessed Stalker) if (sa.hasParam("Unique")) { - for (Player eo : effectOwner.threadSafeIterable()) { + for (Player eo : effectOwner) { if (eo.isCardInCommand(name)) { effectOwner.remove(eo); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/LifeSetEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LifeSetEffect.java index f1b709fb688..68ce8fc96c9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/LifeSetEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/LifeSetEffect.java @@ -68,7 +68,7 @@ public class LifeSetEffect extends SpellAbilityEffect { } final Map lossMap = Maps.newHashMap(); - for (final Player p : players.threadSafeIterable()) { + for (final Player p : players) { if (!p.isInGame()) { continue; } diff --git a/forge-gui-mobile/src/forge/Graphics.java b/forge-gui-mobile/src/forge/Graphics.java index bb5f7303693..9581fb43550 100644 --- a/forge-gui-mobile/src/forge/Graphics.java +++ b/forge-gui-mobile/src/forge/Graphics.java @@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.SpriteBatch; @@ -33,7 +34,7 @@ public class Graphics { private static final int GL_BLEND = GL20.GL_BLEND; private static final int GL_LINE_SMOOTH = 2848; //create constant here since not in GL20 - private final SpriteBatch batch = new SpriteBatch(); + private final Batch batch = new SpriteBatch(); private final ShapeRenderer shapeRenderer = new ShapeRenderer(); private final Deque Dtransforms = new ArrayDeque<>(); private final Vector3 tmp = new Vector3(); @@ -114,7 +115,7 @@ public class Graphics { if (dummyTexture != null) dummyTexture.dispose(); } - public SpriteBatch getBatch() { + public Batch getBatch() { return batch; } diff --git a/forge-gui-mobile/src/forge/assets/FSkinFont.java b/forge-gui-mobile/src/forge/assets/FSkinFont.java index 552aebb5d8b..c353069582a 100644 --- a/forge-gui-mobile/src/forge/assets/FSkinFont.java +++ b/forge-gui-mobile/src/forge/assets/FSkinFont.java @@ -11,12 +11,9 @@ import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.*; import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData; import com.badlogic.gdx.graphics.g2d.BitmapFont.Glyph; -import com.badlogic.gdx.graphics.g2d.PixmapPacker; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; import com.badlogic.gdx.graphics.glutils.PixmapTextureData; @@ -323,7 +320,7 @@ public class FSkinFont { return font.getLineHeight(); } - public void draw(SpriteBatch batch, String text, Color color, float x, float y, float w, boolean wrap, int horzAlignment) { + public void draw(Batch batch, String text, Color color, float x, float y, float w, boolean wrap, int horzAlignment) { updateScale(); font.setColor(color); font.draw(batch, text, x, y, w, horzAlignment, wrap); diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputConfirmMulligan.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputConfirmMulligan.java index 7643ba5bdb1..ac7fbf80496 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputConfirmMulligan.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputConfirmMulligan.java @@ -121,7 +121,7 @@ public class InputConfirmMulligan extends InputSyncronizedBase { ThreadUtil.invokeInGameThread(() -> { final CardCollectionView hand = c0.getController().getCardsIn(ZoneType.Hand); final int handSize = hand.size(); - for (final Card c : hand.threadSafeIterable()) { + for (final Card c : hand) { player.getGame().getAction().exile(c, null, null); } c0.getController().drawCards(handSize); From 619130b51961b01dc5c9ad796f85c0ef140278e6 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sun, 17 Nov 2024 08:07:07 +0800 Subject: [PATCH 051/152] remove unused import --- forge-core/src/main/java/forge/util/collect/FCollectionView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/forge-core/src/main/java/forge/util/collect/FCollectionView.java b/forge-core/src/main/java/forge/util/collect/FCollectionView.java index f4d99e01a54..3957350d434 100644 --- a/forge-core/src/main/java/forge/util/collect/FCollectionView.java +++ b/forge-core/src/main/java/forge/util/collect/FCollectionView.java @@ -1,7 +1,6 @@ package forge.util.collect; import java.util.Collection; -import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; From c603bfb8182bd9db246cb13cdce7c6008adac443 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sun, 17 Nov 2024 11:07:44 +0800 Subject: [PATCH 052/152] try to use immersive mode on android 13 and above --- forge-gui-android/src/forge/app/Main.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/forge-gui-android/src/forge/app/Main.java b/forge-gui-android/src/forge/app/Main.java index ad8027f2de3..c344dd86863 100644 --- a/forge-gui-android/src/forge/app/Main.java +++ b/forge-gui-android/src/forge/app/Main.java @@ -432,7 +432,8 @@ public class Main extends AndroidApplication { config.useCompass = false; config.useGyroscope = false; config.useRotationVectorSensor = false; - config.useImmersiveMode = false; + // try to use immersive mode on android 13 and above + config.useImmersiveMode = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; config.nativeLoader = () -> ReLinker.loadLibrary(getContext(), "gdx"); if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { From cca1468a85f620e15a6ba1ca5ddc643fcdb032a4 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sun, 17 Nov 2024 12:12:58 +0800 Subject: [PATCH 053/152] try to optout on Edge-to-Edge for android 15 --- forge-gui-android/res/values-v35/styles.xml | 7 +++++++ forge-gui-android/res/values/styles.xml | 5 +++++ forge-gui-android/src/forge/app/Main.java | 3 +-- forge-gui-android/src/main/AndroidManifest.xml | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 forge-gui-android/res/values-v35/styles.xml diff --git a/forge-gui-android/res/values-v35/styles.xml b/forge-gui-android/res/values-v35/styles.xml new file mode 100644 index 00000000000..58d5c88393e --- /dev/null +++ b/forge-gui-android/res/values-v35/styles.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/forge-gui-android/res/values/styles.xml b/forge-gui-android/res/values/styles.xml index 90ec268463f..b24940bfd36 100644 --- a/forge-gui-android/res/values/styles.xml +++ b/forge-gui-android/res/values/styles.xml @@ -7,5 +7,10 @@ true true false + true + + \ No newline at end of file diff --git a/forge-gui-android/src/forge/app/Main.java b/forge-gui-android/src/forge/app/Main.java index c344dd86863..ad8027f2de3 100644 --- a/forge-gui-android/src/forge/app/Main.java +++ b/forge-gui-android/src/forge/app/Main.java @@ -432,8 +432,7 @@ public class Main extends AndroidApplication { config.useCompass = false; config.useGyroscope = false; config.useRotationVectorSensor = false; - // try to use immersive mode on android 13 and above - config.useImmersiveMode = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; + config.useImmersiveMode = false; config.nativeLoader = () -> ReLinker.loadLibrary(getContext(), "gdx"); if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { diff --git a/forge-gui-android/src/main/AndroidManifest.xml b/forge-gui-android/src/main/AndroidManifest.xml index d7c65c78e87..d7cd2a7e9cb 100644 --- a/forge-gui-android/src/main/AndroidManifest.xml +++ b/forge-gui-android/src/main/AndroidManifest.xml @@ -58,7 +58,7 @@ android:label="@string/app_name" android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode" android:launchMode="singleInstance" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:theme="@style/NormalTheme" android:exported="false"> From c9e733ecbdda0dfea6d94ce10f4860a8d5129987 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Sun, 17 Nov 2024 14:26:08 +0800 Subject: [PATCH 054/152] Update ImageView.java isempty missing in streambuffer on android --- forge-gui-mobile/src/forge/itemmanager/views/ImageView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 1c6d0d26b2d..e733f3339b2 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -1153,7 +1153,7 @@ public class ImageView extends ItemView { } } // spire colors - if (!colorID.isEmpty()) { + if (colorID.length() > 0) { textRenderer.drawText(g, colorID.toString(), FSkinFont.forHeight(w / 5), Color.WHITE, x, y + h / 4, w, h, y, h, false, Align.center, true); } } else if (item instanceof ConquestCommander) { From 73940c584e9dfc486c04fd026daf9e8dafff1270 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 17 Nov 2024 10:10:49 +0100 Subject: [PATCH 055/152] ColorSet: use Stream to map ManaSymbol --- .../forge/itemmanager/views/ImageView.java | 20 +++++-------------- .../java/forge/gui/card/CardDetailUtil.java | 15 +------------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index e733f3339b2..ccdea3aa8b0 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; +import java.util.stream.Collectors; import java.util.stream.IntStream; import static forge.assets.FSkin.getDefaultSkinFile; @@ -1030,7 +1031,7 @@ public class ImageView extends ItemView { private boolean selected, deckSelectMode, showRanking; private final float IMAGE_SIZE = CardRenderer.MANA_SYMBOL_SIZE; private DeckProxy deckProxy = null; - private StringBuffer colorID = new StringBuffer(); + private String colorID = null; private FImageComplex deckCover = null; private Texture dpImg = null; //private TextureRegion tr; @@ -1058,18 +1059,7 @@ public class ImageView extends ItemView { } } if (((PaperCard) item).getColorID() != null) { - for (String s : ((PaperCard) item).getColorID()) { - if ("white".equalsIgnoreCase(s)) - colorID.append("{W}"); - if ("green".equalsIgnoreCase(s)) - colorID.append("{G}"); - if ("red".equalsIgnoreCase(s)) - colorID.append("{R}"); - if ("blue".equalsIgnoreCase(s)) - colorID.append("{U}"); - if ("black".equalsIgnoreCase(s)) - colorID.append("{B}"); - } + colorID = ((PaperCard) item).getColorID().stream().map(MagicColor::toSymbol).collect(Collectors.joining()); } } } @@ -1153,8 +1143,8 @@ public class ImageView extends ItemView { } } // spire colors - if (colorID.length() > 0) { - textRenderer.drawText(g, colorID.toString(), FSkinFont.forHeight(w / 5), Color.WHITE, x, y + h / 4, w, h, y, h, false, Align.center, true); + if (!colorID.isEmpty()) { + textRenderer.drawText(g, colorID, FSkinFont.forHeight(w / 5), Color.WHITE, x, y + h / 4, w, h, y, h, false, Align.center, true); } } else if (item instanceof ConquestCommander) { CardRenderer.drawCard(g, ((ConquestCommander) item).getCard(), x, y, w, h, pos); diff --git a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java index 76cc1c0dcdc..d37fd9a31a4 100644 --- a/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java +++ b/forge-gui/src/main/java/forge/gui/card/CardDetailUtil.java @@ -129,20 +129,7 @@ public class CardDetailUtil { } public static String getCurrentColors(final CardStateView c) { - ColorSet curColors = c.getColors(); - String strCurColors = ""; - - if (curColors.hasWhite()) { strCurColors += "{W}"; } - if (curColors.hasBlue()) { strCurColors += "{U}"; } - if (curColors.hasBlack()) { strCurColors += "{B}"; } - if (curColors.hasRed()) { strCurColors += "{R}"; } - if (curColors.hasGreen()) { strCurColors += "{G}"; } - - if (strCurColors.isEmpty()) { - strCurColors = "{C}"; - } - - return strCurColors; + return c.getColors().toEnumSet().stream().map(MagicColor.Color::getSymbol).collect(Collectors.joining()); } public static DetailColors getRarityColor(final CardRarity rarity) { From 24182e337a0ed2fe332dc5e3a6b2305a6d2c2ef7 Mon Sep 17 00:00:00 2001 From: Chris H Date: Sun, 17 Nov 2024 16:02:06 -0500 Subject: [PATCH 056/152] Update rune_sealed_wall.txt (#6587) --- forge-gui/res/cardsfolder/r/rune_sealed_wall.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-gui/res/cardsfolder/r/rune_sealed_wall.txt b/forge-gui/res/cardsfolder/r/rune_sealed_wall.txt index 42f78ee11f4..a8c51611bbc 100644 --- a/forge-gui/res/cardsfolder/r/rune_sealed_wall.txt +++ b/forge-gui/res/cardsfolder/r/rune_sealed_wall.txt @@ -1,7 +1,7 @@ Name:Rune-Sealed Wall -ManaCost:1 U +ManaCost:2 U Types:Artifact Creature Wall PT:0/6 K:Defender A:AB$ Surveil | Cost$ T | Amount$ 1 | SpellDescription$ Surveil 1. (Look at the top card of your library. You may put that card into your graveyard.) -Oracle:Defender\n{T}: Surveil 1. (Look at the top card of your library. You may put that card into your graveyard.) \ No newline at end of file +Oracle:Defender\n{T}: Surveil 1. (Look at the top card of your library. You may put that card into your graveyard.) From b8a2903617500c00902877ac7f79fe265a4d05c3 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Mon, 18 Nov 2024 05:11:48 +0800 Subject: [PATCH 057/152] NPE prevention - closes #6588 --- forge-gui-mobile/src/forge/itemmanager/views/ImageView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index ccdea3aa8b0..1dc7f7bb5d2 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -1143,7 +1143,7 @@ public class ImageView extends ItemView { } } // spire colors - if (!colorID.isEmpty()) { + if (colorID != null && !colorID.isEmpty()) { textRenderer.drawText(g, colorID, FSkinFont.forHeight(w / 5), Color.WHITE, x, y + h / 4, w, h, y, h, false, Align.center, true); } } else if (item instanceof ConquestCommander) { From f5e024bf6333a9cf24c83cbba2338aebbd96370f Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Mon, 18 Nov 2024 06:11:42 +0800 Subject: [PATCH 058/152] Update ItemManager.java it seems this is controlled via preference and would fail the logic for the view index --- .../src/forge/itemmanager/ItemManager.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java index 566b33d7713..221411de0ae 100644 --- a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java +++ b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java @@ -149,16 +149,9 @@ public abstract class ItemManager extends FContainer im listView = new ItemListView<>(this, model); imageView = createImageView(model); - if (Forge.isMobileAdventureMode) { - // reversed default - views.add(imageView); - views.add(listView); - currentView = imageView; - } else { - views.add(listView); - views.add(imageView); - currentView = listView; - } + views.add(listView); + views.add(imageView); + currentView = listView; btnView.setIcon(currentView.getIcon()); //build display From 9093c77e72c4af0a75684ab4453a3c8804801a0e Mon Sep 17 00:00:00 2001 From: 0nderzeeboot <67536282+0nderzeeboot@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:44:00 +0100 Subject: [PATCH 059/152] Update arahbo_the_first_fang.txt (#6589) --- forge-gui/res/cardsfolder/a/arahbo_the_first_fang.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/a/arahbo_the_first_fang.txt b/forge-gui/res/cardsfolder/a/arahbo_the_first_fang.txt index 43169a5907e..4432e8c3267 100644 --- a/forge-gui/res/cardsfolder/a/arahbo_the_first_fang.txt +++ b/forge-gui/res/cardsfolder/a/arahbo_the_first_fang.txt @@ -3,7 +3,7 @@ ManaCost:2 W Types:Legendary Creature Cat Avatar PT:2/2 S:Mode$ Continuous | Affected$ Cat.Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Cats you control get +1/+1. -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self,Cat.nonToken+Other | Execute$ TrigToken | TriggerDescription$ Whenever NICKNAME or another nontoken Cat you control enters, create a 1/1 white Cat creature token. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self,Cat.nonToken+Other+YouCtrl | Execute$ TrigToken | TriggerDescription$ Whenever NICKNAME or another nontoken Cat you control enters, create a 1/1 white Cat creature token. SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_cat DeckHas:Ability$Token DeckHints:Type$Cat From f48cf951db15f589e709986a0b7ca2cfb8eba139 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Mon, 18 Nov 2024 11:23:00 +0800 Subject: [PATCH 060/152] .. --- forge-gui-mobile-dev/src/forge/app/Main.java | 3 +++ forge-gui-mobile/src/forge/Forge.java | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/forge-gui-mobile-dev/src/forge/app/Main.java b/forge-gui-mobile-dev/src/forge/app/Main.java index a8e9c424299..8714712af23 100644 --- a/forge-gui-mobile-dev/src/forge/app/Main.java +++ b/forge-gui-mobile-dev/src/forge/app/Main.java @@ -96,6 +96,9 @@ public class Main { @Override public void closeSplashScreen() { + // FIXME: on Linux system it can't close splashscreen image or crash with SIGSEGV? How come it works on other OS? + if (OperatingSystem.isUnix() || OperatingSystem.isSolaris()) + return; //could throw exception.. try { Optional.ofNullable(SplashScreen.getSplashScreen()).ifPresent(SplashScreen::close); diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index 669008d9155..7dafac0f1e7 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -159,6 +159,7 @@ public class Forge implements ApplicationListener { public void create() { //install our error handler ExceptionHandler.registerErrorHandling(); + getDeviceAdapter().closeSplashScreen(); GuiBase.setIsAndroid(Gdx.app.getType() == Application.ApplicationType.Android); @@ -257,8 +258,6 @@ public class Forge implements ApplicationListener { /* call preloadExtendedArt here, if we put it above we will * * get error: No OpenGL context found in the current thread. */ preloadExtendedArt(); - // should be after create method but try to close this at a later time. - getDeviceAdapter().closeSplashScreen(); }); }; //see if app or assets need updating From a1d1e2b335de3c2d40e10ecb55af238d8c110c16 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Mon, 18 Nov 2024 12:41:58 +0800 Subject: [PATCH 061/152] set navigation bar color add note to edge-to-edge enforcement on android 15 --- forge-gui-android/res/values-v35/styles.xml | 1 + forge-gui-android/res/values/styles.xml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/forge-gui-android/res/values-v35/styles.xml b/forge-gui-android/res/values-v35/styles.xml index 58d5c88393e..dde42e65871 100644 --- a/forge-gui-android/res/values-v35/styles.xml +++ b/forge-gui-android/res/values-v35/styles.xml @@ -3,5 +3,6 @@ \ No newline at end of file diff --git a/forge-gui-android/res/values/styles.xml b/forge-gui-android/res/values/styles.xml index b24940bfd36..65ccef592b7 100644 --- a/forge-gui-android/res/values/styles.xml +++ b/forge-gui-android/res/values/styles.xml @@ -11,6 +11,11 @@ \ No newline at end of file From 8d230c774c8e21dce530988e6820941e68c7b62d Mon Sep 17 00:00:00 2001 From: 0nderzeeboot <67536282+0nderzeeboot@users.noreply.github.com> Date: Mon, 18 Nov 2024 07:36:57 +0100 Subject: [PATCH 062/152] Update cephalid_inkmage.txt (#6590) Fixed wrong condition for threshold check. --- forge-gui/res/cardsfolder/c/cephalid_inkmage.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-gui/res/cardsfolder/c/cephalid_inkmage.txt b/forge-gui/res/cardsfolder/c/cephalid_inkmage.txt index 834f43c11cb..f5784419be2 100644 --- a/forge-gui/res/cardsfolder/c/cephalid_inkmage.txt +++ b/forge-gui/res/cardsfolder/c/cephalid_inkmage.txt @@ -4,6 +4,6 @@ Types:Creature Octopus Wizard PT:2/2 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSurveil | TriggerDescription$ When this creature enters, surveil 3. (Look at the top three cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.) SVar:TrigSurveil:DB$ Surveil | Amount$ 3 -S:Mode$ CantBlockBy | ValidAttacker$ Card.Self | Threshold$ True | Description$ Threshold — This creature can't be blocked as long as there are seven or more cards in your graveyard. +S:Mode$ CantBlockBy | ValidAttacker$ Card.Self | Condition$ Threshold | Description$ Threshold — This creature can't be blocked as long as there are seven or more cards in your graveyard. DeckHas:Ability$Surveil|Graveyard -Oracle:When this creature enters, surveil 3. (Look at the top three cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.)\nThreshold — This creature can't be blocked as long as there are seven or more cards in your graveyard. \ No newline at end of file +Oracle:When this creature enters, surveil 3. (Look at the top three cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.)\nThreshold — This creature can't be blocked as long as there are seven or more cards in your graveyard. From cbe48d0892621aaa81196dc1d1993a9b7a7fa5f1 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Mon, 18 Nov 2024 15:09:10 +0800 Subject: [PATCH 063/152] Update VPlayerPanel.java should display error for null icons on VPlayerPanel Infotab. If pointer is still null then the icon seems to be disposed (probably on android) or just deliberately missing. --- .../src/forge/screens/match/views/VPlayerPanel.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 108e251af07..c8e8ea99c49 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java @@ -615,7 +615,13 @@ public class VPlayerPanel extends FContainer { private final VDisplayArea displayArea; private InfoTab(FSkinImageInterface icon0, VDisplayArea displayArea0) { - icon = icon0; + // missing or invalid player infotab icon probably old theme or custom theme. + if (icon0 == null) { + System.err.println("Missing/Invalid VPlayerPanel icon for: " + displayArea0 + " , defaulting to blank icon. Check your theme/skin layout."); + icon = FSkinImage.BLANK; + } else { + icon = icon0; + } displayArea = displayArea0; } From 974e8b076066b86c546cdf2b71a325478d59342c Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Mon, 18 Nov 2024 15:17:38 +0800 Subject: [PATCH 064/152] update xstream, netty fix dependabot vulnerability --- forge-gui/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-gui/pom.xml b/forge-gui/pom.xml index 2feebeda020..ba97aab0114 100644 --- a/forge-gui/pom.xml +++ b/forge-gui/pom.xml @@ -44,7 +44,7 @@ com.thoughtworks.xstream xstream - 1.4.20 + 1.4.21 xmlpull @@ -55,7 +55,7 @@ io.netty netty-all - 4.1.114.Final + 4.1.115.Final compile From fdfabfd5cfdadf6d5d061b34d1257f22f1d4a8c3 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Mon, 18 Nov 2024 15:55:52 +0800 Subject: [PATCH 065/152] fix layout --- forge-gui-android/src/forge/app/Main.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/forge-gui-android/src/forge/app/Main.java b/forge-gui-android/src/forge/app/Main.java index ad8027f2de3..242625adc0e 100644 --- a/forge-gui-android/src/forge/app/Main.java +++ b/forge-gui-android/src/forge/app/Main.java @@ -279,8 +279,8 @@ public class Main extends AndroidApplication { row2.addView(button); row2.setGravity(Gravity.CENTER); - TL.addView(row, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT)); - TL.addView(row2, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT)); + TL.addView(row, new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)); + TL.addView(row2, new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)); TL.setGravity(Gravity.CENTER); TL.setOnClickListener(v -> adapter.restart()); crossfade(TL, previousView); @@ -377,9 +377,9 @@ public class Main extends AndroidApplication { buttonRow.addView(button); buttonRow.setGravity(Gravity.CENTER); - TL.addView(messageRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT)); - TL.addView(checkboxRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT)); - TL.addView(buttonRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT)); + TL.addView(messageRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)); + TL.addView(checkboxRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)); + TL.addView(buttonRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)); TL.setGravity(Gravity.CENTER); crossfade(TL, forgeLogo); } From 0cfc7c25452fe0704a96cb0b0f773c7a675f1fc5 Mon Sep 17 00:00:00 2001 From: Chris H Date: Mon, 18 Nov 2024 15:00:31 -0500 Subject: [PATCH 066/152] Update snapshot-both-pc-android.yml --- .github/workflows/snapshot-both-pc-android.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/snapshot-both-pc-android.yml b/.github/workflows/snapshot-both-pc-android.yml index 46b2cf6ceb3..6191c8045cc 100644 --- a/.github/workflows/snapshot-both-pc-android.yml +++ b/.github/workflows/snapshot-both-pc-android.yml @@ -117,6 +117,7 @@ jobs: password: ${{ secrets.FTP_PASSWORD }} local-dir: izpack/ server-dir: downloads/dailysnapshots/ + state-name: .ftp-deploy-both-sync-state.json exclude: | *.pom *.repositories From d165aba6240c33445ca0ccbbc63cde1593757b35 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Tue, 19 Nov 2024 11:37:22 +0100 Subject: [PATCH 067/152] Update tyvar_the_pummeler.txt Closes #6594 --- forge-gui/res/cardsfolder/t/tyvar_the_pummeler.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/t/tyvar_the_pummeler.txt b/forge-gui/res/cardsfolder/t/tyvar_the_pummeler.txt index 627a93f1322..446b357af7c 100644 --- a/forge-gui/res/cardsfolder/t/tyvar_the_pummeler.txt +++ b/forge-gui/res/cardsfolder/t/tyvar_the_pummeler.txt @@ -4,6 +4,6 @@ Types:Legendary Creature Elf Warrior PT:3/3 A:AB$ Pump | Cost$ tapXType<1/Creature.Other> | Defined$ Self | KW$ Indestructible | SubAbility$ DBTap | SpellDescription$ CARDNAME gains indestructible until end of turn. Tap it. SVar:DBTap:DB$ Tap | Defined$ Self -A:AB$ PumpAll | Cost$ 2 G G G | ValidCards$ Creature.YouCtrl | NumAtt$ +X | NumDef$ +X | SpellDescription$ Creatures you control get +X/+X until end of turn, where X is the greatest power among creatures you control. +A:AB$ PumpAll | Cost$ 3 G G | ValidCards$ Creature.YouCtrl | NumAtt$ +X | NumDef$ +X | SpellDescription$ Creatures you control get +X/+X until end of turn, where X is the greatest power among creatures you control. SVar:X:Count$Valid Creature.YouCtrl$GreatestPower Oracle:Tap another untapped creature you control: Tyvar, the Pummeler gains indestructible until end of turn. Tap it.\n{3}{G}{G}: Creatures you control get +X/+X until end of turn, where X is the greatest power among creatures you control. From 0854fbb947eb07d3692a3125af84a72cb75001ac Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Tue, 19 Nov 2024 22:32:37 +0800 Subject: [PATCH 068/152] update FCollection Replace copyonwritearray to a custom implementation for concurrent modification exception. A little bit slower but supports iterator operations. Added FCollectionTest --- .../src/main/java/forge/ai/AiController.java | 3 +- .../main/java/forge/ai/AiCostDecision.java | 5 +- .../src/main/java/forge/ai/ComputerUtil.java | 11 +- .../main/java/forge/ai/ComputerUtilMana.java | 5 +- .../java/forge/ai/PlayerControllerAi.java | 3 +- .../src/main/java/forge/ai/SpecialCardAi.java | 3 +- .../main/java/forge/ai/ability/CharmAi.java | 6 +- .../java/forge/ai/ability/ChooseCardAi.java | 4 +- .../forge/ai/ability/ChooseCompanionAi.java | 4 +- .../main/java/forge/ai/ability/DiscardAi.java | 4 +- .../main/java/forge/ai/ability/PlayAi.java | 13 +- .../ai/simulation/SimulationController.java | 4 +- .../src/main/java/forge/StaticData.java | 3 +- .../src/main/java/forge/card/CardDb.java | 3 +- .../src/main/java/forge/card/CardEdition.java | 4 +- .../src/main/java/forge/deck/CardPool.java | 3 +- .../item/generation/BoosterGenerator.java | 3 +- .../main/java/forge/util/CollectionUtil.java | 65 -- .../java/forge/util/collect/FCollection.java | 558 ++++++++++++------ forge-game/src/main/java/forge/game/Game.java | 5 +- .../src/main/java/forge/game/GameAction.java | 6 +- .../forge/game/ability/effects/DigEffect.java | 5 +- .../ability/effects/DigMultipleEffect.java | 4 +- .../game/ability/effects/DigUntilEffect.java | 4 +- .../game/ability/effects/DraftEffect.java | 3 +- .../game/ability/effects/PlayEffect.java | 24 +- .../ability/effects/ReorderZoneEffect.java | 4 +- .../main/java/forge/game/card/CardLists.java | 3 +- .../java/forge/game/card/CardProperty.java | 7 +- .../forge/game/mana/ManaRefundService.java | 4 +- .../main/java/forge/game/player/Player.java | 4 +- .../src/main/java/forge/game/zone/Zone.java | 8 +- .../java/forge/itemmanager/CardManager.java | 3 +- .../controllers/CProbabilities.java | 9 +- .../screens/home/quest/DialogChooseSets.java | 3 +- .../home/sanctioned/CSubmenuDraft.java | 4 +- .../main/java/forge/view/SimulateMatch.java | 9 +- .../src/test/java/forge/FCollectionTest.java | 64 ++ .../src/main/java/forge/deck/DeckgenUtil.java | 13 +- .../java/forge/deck/NetDeckArchiveBlock.java | 3 +- .../java/forge/deck/NetDeckArchiveLegacy.java | 3 +- .../java/forge/deck/NetDeckArchiveModern.java | 3 +- .../java/forge/deck/NetDeckArchivePauper.java | 3 +- .../forge/deck/NetDeckArchivePioneer.java | 3 +- .../forge/deck/NetDeckArchiveStandard.java | 3 +- .../forge/deck/NetDeckArchiveVintage.java | 3 +- .../forge/gamemodes/limited/BoosterDraft.java | 5 +- .../limited/CardThemedDeckBuilder.java | 13 +- .../gamemodes/limited/LimitedPlayer.java | 3 +- .../gamemodes/limited/LimitedPlayerAI.java | 8 +- .../limited/SealedCardPoolGenerator.java | 3 +- .../forge/gamemodes/limited/WinstonDraft.java | 4 +- .../planarconquest/ConquestData.java | 3 +- .../forge/gamemodes/quest/BoosterUtils.java | 6 +- .../quest/MainWorldEventDuelManager.java | 4 +- .../gamemodes/quest/QuestController.java | 10 +- .../quest/QuestEventCommanderDuelManager.java | 4 +- .../gamemodes/quest/QuestEventDraft.java | 7 +- .../quest/QuestEventDuelManager.java | 4 +- .../quest/QuestEventLDADuelManager.java | 4 +- .../forge/gamemodes/quest/QuestUtilCards.java | 4 +- .../gamemodes/quest/QuestUtilUnlockSets.java | 3 +- .../setrotation/QueueRandomRotation.java | 4 +- .../tournament/system/AbstractTournament.java | 4 +- .../tournament/system/TournamentSwiss.java | 9 +- .../main/java/forge/itemmanager/GroupDef.java | 3 +- .../java/forge/player/HumanCostDecision.java | 2 +- .../forge/player/PlayerControllerHuman.java | 4 +- 68 files changed, 576 insertions(+), 446 deletions(-) delete mode 100644 forge-core/src/main/java/forge/util/CollectionUtil.java create mode 100644 forge-gui-desktop/src/test/java/forge/FCollectionTest.java diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 4a73c6434b8..31fe79a7f8c 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -63,7 +63,6 @@ import forge.game.trigger.WrappedAbility; import forge.game.zone.ZoneType; import forge.item.PaperCard; import forge.util.Aggregates; -import forge.util.CollectionUtil; import forge.util.ComparatorUtil; import forge.util.Expressions; import forge.util.MyRandom; @@ -2265,7 +2264,7 @@ public class AiController { result.addAll(activePlayerSAs); //need to reverse because of magic stack - CollectionUtil.reverse(result); + Collections.reverse(result); return result; } diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 373ad91eaf5..9ebcff668a4 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -17,7 +17,6 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.zone.ZoneType; import forge.util.Aggregates; -import forge.util.CollectionUtil; import forge.util.TextUtil; import forge.util.collect.FCollectionView; import org.apache.commons.lang3.ObjectUtils; @@ -162,7 +161,7 @@ public class AiCostDecision extends CostDecisionMakerBase { List res = cost.getPotentialPlayers(player, ability); // I should only choose one of these right? // TODO Choose the "worst" player. - CollectionUtil.shuffle(res); + Collections.shuffle(res); return PaymentDecision.players(res.subList(0, 1)); } @@ -186,7 +185,7 @@ public class AiCostDecision extends CostDecisionMakerBase { CardCollection chosen = new CardCollection(); CardLists.sortByCmcDesc(valid); - CollectionUtil.reverse(valid); + Collections.reverse(valid); int totalCMC = 0; for (Card card : valid) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index bfdcde24fe0..57abdca4936 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -68,7 +68,6 @@ import forge.game.trigger.WrappedAbility; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.util.Aggregates; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; import forge.util.collect.FCollection; @@ -683,7 +682,7 @@ public class ComputerUtil { // FIXME: This is suboptimal, maybe implement a single comparator that'll take care of all of this? CardLists.sortByCmcDesc(typeList); - CollectionUtil.reverse(typeList); + Collections.reverse(typeList); // TODO AI needs some improvements here @@ -737,7 +736,7 @@ public class ComputerUtil { // FIXME: This is suboptimal, maybe implement a single comparator that'll take care of all of this? CardLists.sortByCmcDesc(typeList); - CollectionUtil.reverse(typeList); + Collections.reverse(typeList); typeList.sort((a, b) -> { if (!a.isInPlay() && b.isInPlay()) return -1; else if (!b.isInPlay() && a.isInPlay()) return 1; @@ -767,7 +766,7 @@ public class ComputerUtil { final CardCollection list = new CardCollection(); if (zone != ZoneType.Hand) { - CollectionUtil.reverse(typeList); + Collections.reverse(typeList); } for (int i = 0; i < amount; i++) { @@ -818,7 +817,7 @@ public class ComputerUtil { typeList.remove(activate); } ComputerUtilCard.sortByEvaluateCreature(typeList); - CollectionUtil.reverse(typeList); + Collections.reverse(typeList); final CardCollection tapList = new CardCollection(); @@ -1770,7 +1769,7 @@ public class ComputerUtil { // align threatened with resolve order // matters if stack contains multiple activations (e.g. Temur Sabertooth) - CollectionUtil.reverse(objects); + Collections.reverse(objects); return objects; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 4295b0d3f6a..0d73234bc02 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -38,7 +38,6 @@ import forge.game.trigger.Trigger; import forge.game.trigger.TriggerType; import forge.game.zone.Zone; import forge.game.zone.ZoneType; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; @@ -1521,11 +1520,11 @@ public class ComputerUtilMana { sortedManaSources.addAll(sortedManaSources.size(), anyColorManaSources); //use better creatures later ComputerUtilCard.sortByEvaluateCreature(otherManaSources); - CollectionUtil.reverse(otherManaSources); + Collections.reverse(otherManaSources); sortedManaSources.addAll(sortedManaSources.size(), otherManaSources); // This should be things like sacrifice other stuff. ComputerUtilCard.sortByEvaluateCreature(useLastManaSources); - CollectionUtil.reverse(useLastManaSources); + Collections.reverse(useLastManaSources); sortedManaSources.addAll(sortedManaSources.size(), useLastManaSources); if (DEBUG_MANA_PAYMENT) { diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index e7f0b8c15d6..b71df96b718 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -40,7 +40,6 @@ import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; import forge.item.PaperCard; import forge.util.Aggregates; -import forge.util.CollectionUtil; import forge.util.ITriggerEvent; import forge.util.MyRandom; import forge.util.collect.FCollection; @@ -651,7 +650,7 @@ public class PlayerControllerAi extends PlayerController { if(source == null || !source.hasParam("LibraryPosition") || AbilityUtils.calculateAmount(source.getHostCard(), source.getParam("LibraryPosition"), source) >= 0) { //Cards going to the top of a deck are returned in reverse order. - CollectionUtil.reverse(reordered); + Collections.reverse(reordered); } assert(reordered.size() == cards.size()); diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index f5e769747b5..aa8c1f03c7d 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -47,7 +47,6 @@ import forge.game.staticability.StaticAbility; import forge.game.trigger.Trigger; import forge.game.zone.ZoneType; import forge.util.Aggregates; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; import forge.util.maps.LinkedHashMapToAmount; @@ -320,7 +319,7 @@ public class SpecialCardAi { best = ComputerUtilCard.getBestCreatureAI(cardlist); if (best == null) { // If nothing on the battlefield has a nonmana ability choose something - CollectionUtil.shuffle(cardlist); + Collections.shuffle(cardlist); best = cardlist.getFirst(); } diff --git a/forge-ai/src/main/java/forge/ai/ability/CharmAi.java b/forge-ai/src/main/java/forge/ai/ability/CharmAi.java index 5ea0778a711..7d02582f652 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CharmAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CharmAi.java @@ -1,5 +1,6 @@ package forge.ai.ability; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -17,7 +18,6 @@ import forge.game.player.Player; import forge.game.spellability.AbilitySub; import forge.game.spellability.SpellAbility; import forge.util.Aggregates; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.collect.FCollection; @@ -52,7 +52,7 @@ public class CharmAi extends SpellAbilityAi { } else { // only randomize if not all possible together if (num < choices.size()) { - CollectionUtil.shuffle(choices); + Collections.shuffle(choices); } /* @@ -101,7 +101,7 @@ public class CharmAi extends SpellAbilityAi { // Pawprint final int pawprintLimit = sa.hasParam("Pawprint") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Pawprint"), sa) : 0; if (pawprintLimit > 0) { - CollectionUtil.reverse(choices); // try to pay for the more expensive subs first + Collections.reverse(choices); // try to pay for the more expensive subs first } int pawprintAmount = 0; diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java index 21b38f0b844..721c183f635 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java @@ -16,8 +16,8 @@ import forge.game.player.PlayerPredicates; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.util.Aggregates; -import forge.util.CollectionUtil; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -226,7 +226,7 @@ public class ChooseCardAi extends SpellAbilityAi { choice = ComputerUtilCard.getWorstAI(options); } else { CardLists.sortByCmcDesc(creats); - CollectionUtil.reverse(creats); + Collections.reverse(creats); choice = creats.get(0); } } else if ("NegativePowerFirst".equals(logic)) { diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java index ee7d0f57992..e3e50950b18 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java @@ -1,5 +1,6 @@ package forge.ai.ability; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -9,7 +10,6 @@ import forge.ai.SpellAbilityAi; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.util.CollectionUtil; public class ChooseCompanionAi extends SpellAbilityAi { @@ -23,7 +23,7 @@ public class ChooseCompanionAi extends SpellAbilityAi { return null; } - CollectionUtil.shuffle(cards); + Collections.shuffle(cards); return cards.get(0); } } diff --git a/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java b/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java index 4b2100aa5c7..b8ac075c536 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java @@ -1,5 +1,6 @@ package forge.ai.ability; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -22,7 +23,6 @@ import forge.game.player.PlayerCollection; import forge.game.player.PlayerPredicates; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.util.CollectionUtil; import forge.util.MyRandom; public class DiscardAi extends SpellAbilityAi { @@ -148,7 +148,7 @@ public class DiscardAi extends SpellAbilityAi { private boolean discardTargetAI(final Player ai, final SpellAbility sa) { final PlayerCollection opps = ai.getOpponents(); - CollectionUtil.shuffle(opps); + Collections.shuffle(opps); for (Player opp : opps) { if (opp.getCardsIn(ZoneType.Hand).isEmpty() && !ComputerUtil.activateForCost(sa, ai)) { continue; diff --git a/forge-ai/src/main/java/forge/ai/ability/PlayAi.java b/forge-ai/src/main/java/forge/ai/ability/PlayAi.java index b37acae6fdc..5e7e9c482ff 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PlayAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PlayAi.java @@ -16,7 +16,7 @@ import forge.game.spellability.*; import forge.game.zone.ZoneType; import forge.util.MyRandom; -import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -220,12 +220,13 @@ public class PlayAi extends SpellAbilityAi { if (cards != null & sa.hasParam("ValidSA")) { final String valid[] = sa.getParam("ValidSA").split(","); - final List invalid = new ArrayList<>(); - for (Card c : cards) { - if(!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, ai), SpellAbilityPredicates.isValid(valid, ai , source, sa))) - invalid.add(c); + final Iterator itr = cards.iterator(); + while (itr.hasNext()) { + final Card c = itr.next(); + if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, ai), SpellAbilityPredicates.isValid(valid, ai , source, sa))) { + itr.remove(); + } } - cards.removeAll(invalid); } // Ensure that if a ValidZone is specified, there's at least something to choose from in that zone. diff --git a/forge-ai/src/main/java/forge/ai/simulation/SimulationController.java b/forge-ai/src/main/java/forge/ai/simulation/SimulationController.java index 7669dcf7275..2f214ecd09a 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/SimulationController.java +++ b/forge-ai/src/main/java/forge/ai/simulation/SimulationController.java @@ -1,6 +1,7 @@ package forge.ai.simulation; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import forge.ai.simulation.GameStateEvaluator.Score; @@ -8,7 +9,6 @@ import forge.game.GameObject; import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.util.CollectionUtil; public class SimulationController { private static boolean DEBUG = false; @@ -106,7 +106,7 @@ public class SimulationController { sequence.add(current); current = current.prevDecision; } - CollectionUtil.reverse(sequence); + Collections.reverse(sequence); // Merge targets & choices into their parents. int writeIndex = 0; for (int i = 0; i < sequence.size(); i++) { diff --git a/forge-core/src/main/java/forge/StaticData.java b/forge-core/src/main/java/forge/StaticData.java index 9d34b9922b9..068e1956d6f 100644 --- a/forge-core/src/main/java/forge/StaticData.java +++ b/forge-core/src/main/java/forge/StaticData.java @@ -7,7 +7,6 @@ import forge.card.CardRules; import forge.card.PrintSheet; import forge.item.*; import forge.token.TokenDb; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.ImageUtil; import forge.util.TextUtil; @@ -196,7 +195,7 @@ public class StaticData { sortedEditions.add(set); } Collections.sort(sortedEditions); - CollectionUtil.reverse(sortedEditions); //put newer sets at the top + Collections.reverse(sortedEditions); //put newer sets at the top } return sortedEditions; } diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index 83e82191061..db7718b875f 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -28,7 +28,6 @@ import forge.deck.generation.IDeckGenPool; import forge.item.IPaperCard; import forge.item.PaperCard; import forge.util.CollectionSuppliers; -import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.TextUtil; import forge.util.lang.LangEnglish; @@ -892,7 +891,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { if (acceptedEditions.size() > 1) { Collections.sort(acceptedEditions); // CardEdition correctly sort by (release) date if (artPref.latestFirst) - CollectionUtil.reverse(acceptedEditions); // newest editions first + Collections.reverse(acceptedEditions); // newest editions first } final Iterator editionIterator = acceptedEditions.iterator(); diff --git a/forge-core/src/main/java/forge/card/CardEdition.java b/forge-core/src/main/java/forge/card/CardEdition.java index 6e02ef50f45..96c6caf33dc 100644 --- a/forge-core/src/main/java/forge/card/CardEdition.java +++ b/forge-core/src/main/java/forge/card/CardEdition.java @@ -490,7 +490,7 @@ public final class CardEdition implements Comparable { return null; } - CollectionUtil.shuffle(boosterTypes); + Collections.shuffle(boosterTypes); return boosterTypes.get(0); } @@ -802,7 +802,7 @@ public final class CardEdition implements Comparable { public Iterable getOrderedEditions() { List res = Lists.newArrayList(this); Collections.sort(res); - CollectionUtil.reverse(res); + Collections.reverse(res); return res; } diff --git a/forge-core/src/main/java/forge/deck/CardPool.java b/forge-core/src/main/java/forge/deck/CardPool.java index fcf68572fc4..5b9d3d2c98a 100644 --- a/forge-core/src/main/java/forge/deck/CardPool.java +++ b/forge-core/src/main/java/forge/deck/CardPool.java @@ -27,7 +27,6 @@ import forge.card.CardEdition; import forge.item.IPaperCard; import forge.item.PaperCard; import forge.util.CollectionSuppliers; -import forge.util.CollectionUtil; import forge.util.ItemPool; import forge.util.ItemPoolSorter; import forge.util.MyRandom; @@ -350,7 +349,7 @@ public class CardPool extends ItemPool { pivotCandidates.sort(CardEdition::compareTo); boolean searchPolicyAndPoolAreCompliant = isLatestCardArtPreference == this.isModern(); if (!searchPolicyAndPoolAreCompliant) - CollectionUtil.reverse(pivotCandidates); // reverse to have latest-first. + Collections.reverse(pivotCandidates); // reverse to have latest-first. return pivotCandidates.get(0); } diff --git a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java index 2c3f88c2094..812dd7ff779 100644 --- a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java +++ b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java @@ -28,7 +28,6 @@ import forge.card.CardEdition.FoilType; import forge.item.*; import forge.item.IPaperCard.Predicates.Presets; import forge.util.Aggregates; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; @@ -59,7 +58,7 @@ public class BoosterGenerator { } private static PaperCard generateFoilCard(List cardList) { - CollectionUtil.shuffle(cardList, MyRandom.getRandom()); + Collections.shuffle(cardList, MyRandom.getRandom()); PaperCard randomCard = cardList.get(0); return randomCard.getFoiled(); } diff --git a/forge-core/src/main/java/forge/util/CollectionUtil.java b/forge-core/src/main/java/forge/util/CollectionUtil.java deleted file mode 100644 index 6ce27407888..00000000000 --- a/forge-core/src/main/java/forge/util/CollectionUtil.java +++ /dev/null @@ -1,65 +0,0 @@ -package forge.util; - -import forge.util.collect.FCollection; - -import java.util.*; - -public class CollectionUtil { - public static void shuffle(List list) { - shuffle(list, MyRandom.getRandom()); - } - - public static void shuffle(List list, Random random) { - if (list instanceof FCollection) { - //FCollection -> copyonwritearraylist is not compatible, use different method - shuffleList(list, random); - } else { - //use Collections -> shuffle(LIST, RANDOM) since it's not FCollection - Collections.shuffle(list, random); - } - } - - public static void shuffleList(List a, Random r) { - int n = a.size(); - for (int i = 0; i < n; i++) { - int change = i + r.nextInt(n - i); - swap(a, i, change); - } - } - - private static void swap(List a, int i, int change) { - T helper = a.get(i); - a.set(i, a.get(change)); - a.set(change, helper); - } - - public static void reverse(List list) { - if (list == null || list.isEmpty()) - return; - if (list instanceof FCollection) { - //FCollection -> copyonwritearraylist is not compatible, use different method - reverseWithRecursion(list, 0, list.size() - 1); - } else { - Collections.reverse(list); - } - } - - public static void reverseWithRecursion(List list) { - if (list.size() > 1) { - T value = list.remove(0); - reverseWithRecursion(list); - list.add(value); - } - } - - public static void reverseWithRecursion(List list, int startIndex, int lastIndex) { - if (startIndex < lastIndex) { - T t = list.get(lastIndex); - list.set(lastIndex, list.get(startIndex)); - list.set(startIndex, t); - startIndex++; - lastIndex--; - reverseWithRecursion(list, startIndex, lastIndex); - } - } -} \ No newline at end of file diff --git a/forge-core/src/main/java/forge/util/collect/FCollection.java b/forge-core/src/main/java/forge/util/collect/FCollection.java index 43c428b590d..d3d5ce469e0 100644 --- a/forge-core/src/main/java/forge/util/collect/FCollection.java +++ b/forge-core/src/main/java/forge/util/collect/FCollection.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; @@ -23,7 +24,7 @@ import com.google.common.collect.Sets; /** * Collection with unique elements ({@link Set}) that maintains the order in * which the elements are added to it ({@link List}). - * + *

* This object is serializable if all elements it contains are. * * @param the type of the elements this collection contains. @@ -31,6 +32,7 @@ import com.google.common.collect.Sets; */ public class FCollection implements List, /*Set,*/ FCollectionView, Cloneable, Serializable { private static final long serialVersionUID = -1664555336364294106L; + private final Object lock = new Object(); private static final FCollection EMPTY = new EmptyFCollection<>(); @@ -43,13 +45,14 @@ public class FCollection implements List, /*Set,*/ FCollectionView, * The {@link Set} representation of this collection. */ private Set SET; + private Set set() { Set result = SET; if (result == null) { - synchronized (this) { + synchronized (lock) { result = SET; if (result == null) { - result = Sets.newConcurrentHashSet(); + result = Sets.newHashSet(); SET = result; } } @@ -60,14 +63,15 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** * The {@link List} representation of this collection. */ - private List LIST; - private List list() { - List result = LIST; + private LinkedList LIST; + + private LinkedList list() { + LinkedList result = LIST; if (result == null) { - synchronized (this) { + synchronized (lock) { result = LIST; if (result == null) { - result = Lists.newCopyOnWriteArrayList(); + result = Lists.newLinkedList(); LIST = result; } } @@ -84,8 +88,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** * Create an {@link FCollection} containing a single element. * - * @param e - * the single element the new collection contains. + * @param e the single element the new collection contains. */ public FCollection(final T e) { add(e); @@ -95,9 +98,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, * Create an {@link FCollection} from an array. The order of the elements in * the array is preserved in the new collection. * - * @param c - * an array, whose elements will be in the collection upon its - * creation. + * @param c an array, whose elements will be in the collection upon its + * creation. */ public FCollection(final T[] c) { this.addAll(Arrays.asList(c)); @@ -107,9 +109,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, * Create an {@link FCollection} from an {@link Iterable}. The order of the * elements in the iterable is preserved in the new collection. * - * @param i - * an iterable, whose elements will be in the collection upon its - * creation. + * @param i an iterable, whose elements will be in the collection upon its + * creation. */ public FCollection(final Iterable i) { this.addAll(i); @@ -118,19 +119,19 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** * Create an {@link FCollection} from an {@link FCollectionReader}. * - * @param reader - * a reader used to populate collection + * @param reader a reader used to populate collection */ public FCollection(final FCollectionReader reader) { - reader.readAll(this); + synchronized (lock) { + reader.readAll(this); + } } /** * Check whether an {@link Iterable} contains any iterable, silently * returning {@code false} when {@code null} is passed as an argument. * - * @param iterable - * a card collection. + * @param iterable a card collection. */ public static boolean hasElements(final Iterable iterable) { return iterable != null && !Iterables.isEmpty(iterable); @@ -140,10 +141,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, * Check whether a {@link Collection} contains a particular element, silently * returning {@code false} when {@code null} is passed as the first argument. * - * @param collection - * a collection. - * @param element - * a possible element of the collection. + * @param collection a collection. + * @param element a possible element of the collection. */ public static boolean hasElement(final Collection collection, final T element) { return collection != null && collection.contains(element); @@ -159,7 +158,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** *

This implementation uses the hash code of the backing list.

- * + *

* {@inheritDoc} */ @Override @@ -183,35 +182,37 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public final FCollection clone() { - return new FCollection<>(list()); + synchronized (lock) { + return new FCollection<>(list()); + } } /** * Get the first object in this {@link FCollection}. * - * @throws NoSuchElementException - * if the collection is empty. + * @throws NoSuchElementException if the collection is empty. */ @Override public T getFirst() { - if (list().isEmpty()) - return null; - return list().get(0); - //return list.getFirst(); + synchronized (lock) { + if (list().isEmpty()) + return null; + return list().getFirst(); + } } /** * Get the last object in this {@link FCollection}. * - * @throws NoSuchElementException - * if the collection is empty. + * @throws NoSuchElementException if the collection is empty. */ @Override public T getLast() { - if (list().isEmpty()) - return null; - return list().get(list().size() - 1); - //return list.getLast(); + synchronized (lock) { + if (list().isEmpty()) + return null; + return list().getLast(); + } } /** @@ -219,7 +220,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public int size() { - return set().size(); + synchronized (lock) { + return set().size(); + } } /** @@ -237,14 +240,15 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** * Check whether this collection contains a particular object. * - * @param o - * an object. + * @param o an object. */ @Override public boolean contains(final Object o) { - if (o == null) - return false; - return set().contains(o); + synchronized (lock) { + if (o == null) + return false; + return set().contains(o); + } } /** @@ -252,7 +256,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public Iterator iterator() { - return list().iterator(); + return new Itr(); + //return list().iterator(); } /** @@ -260,7 +265,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public Object[] toArray() { - return list().toArray(); + synchronized (lock) { + return list().toArray(); + } } /** @@ -269,52 +276,58 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override @SuppressWarnings("hiding") public T[] toArray(final T[] a) { - return list().toArray(a); + synchronized (lock) { + return list().toArray(a); + } } /** * Add an element to this collection, if it isn't already present. * - * @param e - * the object to add. + * @param e the object to add. * @return whether the collection changed as a result of this method call. */ @Override public boolean add(final T e) { - if (e == null) + synchronized (lock) { + if (e == null) + return false; + if (set().add(e)) { + list().add(e); + return true; + } return false; - if (set().add(e)) { - list().add(e); - return true; } - return false; } /** * Remove an element from this collection. * - * @param o - * the object to remove. + * @param o the object to remove. * @return whether the collection changed as a result of this method call. */ @Override public boolean remove(final Object o) { - if (o == null) + synchronized (lock) { + if (o == null) + return false; + if (set().remove(o)) { + list().remove(o); + return true; + } return false; - if (set().remove(o)) { - list().remove(o); - return true; } - return false; } @Override public boolean removeIf(Predicate filter) { - if (list().removeIf(filter)) { - set().removeIf(filter); - return true; + synchronized (lock) { + if (list().removeIf(filter)) { + set().removeIf(filter); + return true; + } + return false; } - return false; } /** @@ -322,7 +335,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public boolean containsAll(final Collection c) { - return set().containsAll(c); + synchronized (lock) { + return set().containsAll(c); + } } /** @@ -337,35 +352,37 @@ public class FCollection implements List, /*Set,*/ FCollectionView, * Add all the elements in the specified {@link Iterator} to this * collection, in the order in which they appear. * - * @param i - * an iterator. + * @param i an iterator. * @return whether this collection changed as a result of this method call. * @see #addAll(Collection) */ public boolean addAll(final Iterable i) { - boolean changed = false; - if (i == null) - return false; - for (final T e : i) { - changed |= add(e); + synchronized (lock) { + boolean changed = false; + if (i == null) + return false; + for (final T e : i) { + changed |= add(e); + } + return changed; } - return changed; } /** * Add all the elements in the specified array to this collection, * respecting the ordering. * - * @param c - * an array. + * @param c an array. * @return whether this collection changed as a result of this method call. */ public boolean addAll(final T[] c) { - boolean changed = false; - for (final T e : c) { - changed |= add(e); + synchronized (lock) { + boolean changed = false; + for (final T e : c) { + changed |= add(e); + } + return changed; } - return changed; } /** @@ -374,22 +391,24 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @SuppressWarnings("unchecked") @Override public boolean addAll(final int index, final Collection c) { - if (c == null) { - return false; - } + synchronized (lock) { + if (c == null) { + return false; + } - final List list; - if (c instanceof List) { - list = (List) c; - } else { - list = Lists.newArrayList(c); - } + final List list; + if (c instanceof List) { + list = (List) c; + } else { + list = Lists.newArrayList(c); + } - boolean changed = false; - for (int i = list.size() - 1; i >= 0; i--) { //must add in reverse order so they show up in the right place - changed |= insert(index, list.get(i)); + boolean changed = false; + for (int i = list.size() - 1; i >= 0; i--) { //must add in reverse order so they show up in the right place + changed |= insert(index, list.get(i)); + } + return changed; } - return changed; } /** @@ -403,18 +422,19 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** * Remove all objects appearing in an {@link Iterable}. * - * @param c - * an iterable. + * @param c an iterable. * @return whether this collection changed as a result of this method call. */ public boolean removeAll(final Iterable c) { - boolean changed = false; - if (c == null) - return false; - for (final Object o : c) { - changed |= remove(o); + synchronized (lock) { + boolean changed = false; + if (c == null) + return false; + for (final Object o : c) { + changed |= remove(o); + } + return changed; } - return changed; } /** @@ -422,11 +442,13 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public boolean retainAll(final Collection c) { - if (set().retainAll(c)) { - list().retainAll(c); - return true; + synchronized (lock) { + if (set().retainAll(c)) { + list().retainAll(c); + return true; + } + return false; } - return false; } /** @@ -434,9 +456,13 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public void clear() { - if (set().isEmpty()) { return; } - set().clear(); - list().clear(); + synchronized (lock) { + if (set().isEmpty()) { + return; + } + set().clear(); + list().clear(); + } } /** @@ -444,7 +470,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public T get(final int index) { - return list().get(index); + synchronized (lock) { + return list().get(index); + } } /** @@ -454,7 +482,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public T set(final int index, final T element) { //assume this isn't called except when changing list order, so don't worry about updating set - return list().set(index, element); + synchronized (lock) { + return list().set(index, element); + } } /** @@ -468,29 +498,29 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** * Helper method to insert an element at a particular index. * - * @param index - * the index to insert the element at. - * @param element - * the element to insert. + * @param index the index to insert the element at. + * @param element the element to insert. * @return whether this collection changed as a result of this method call. */ private boolean insert(int index, final T element) { - if (set().add(element)) { + synchronized (lock) { + if (set().add(element)) { + list().add(index, element); + return true; + } + //re-position in list if needed + final int oldIndex = list().indexOf(element); + if (index == oldIndex) { + return false; + } + + if (index > oldIndex) { + index--; //account for being removed + } + list().remove(oldIndex); list().add(index, element); return true; } - //re-position in list if needed - final int oldIndex = list().indexOf(element); - if (index == oldIndex) { - return false; - } - - if (index > oldIndex) { - index--; //account for being removed - } - list().remove(oldIndex); - list().add(index, element); - return true; } /** @@ -498,11 +528,13 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public T remove(final int index) { - final T removedItem = list().remove(index); - if (removedItem != null) { - set().remove(removedItem); + synchronized (lock) { + final T removedItem = list().remove(index); + if (removedItem != null) { + set().remove(removedItem); + } + return removedItem; } - return removedItem; } /** @@ -510,7 +542,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public int indexOf(final Object o) { - return list().indexOf(o); + synchronized (lock) { + return list().indexOf(o); + } } /** @@ -518,7 +552,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public int lastIndexOf(final Object o) { - return list().lastIndexOf(o); + synchronized (lock) { + return list().lastIndexOf(o); + } } /** @@ -526,7 +562,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public ListIterator listIterator() { - return list().listIterator(); + return new ListItr(0); + //return list().listIterator(); } /** @@ -534,7 +571,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public ListIterator listIterator(final int index) { - return list().listIterator(index); + return new ListItr(index); + //return list().listIterator(index); } /** @@ -542,12 +580,14 @@ public class FCollection implements List, /*Set,*/ FCollectionView, * Note This method breaks the contract of {@link List#subList(int, int)} * by returning a static collection, rather than a view, of the sublist. *

- * + *

* {@inheritDoc} */ @Override public List subList(final int fromIndex, final int toIndex) { - return ImmutableList.copyOf(list().subList(fromIndex, toIndex)); + synchronized (lock) { + return ImmutableList.copyOf(list().subList(fromIndex, toIndex)); + } } /** @@ -566,25 +606,30 @@ public class FCollection implements List, /*Set,*/ FCollectionView, * {@inheritDoc} */ public void sort(final Comparator comparator) { - try { - list().sort(comparator); - } catch (Exception e) { - System.err.println("FCollection failed to sort: \n" + comparator + "\n" + e.getMessage()); + synchronized (lock) { + try { + list().sort(comparator); + } catch (Exception e) { + System.err.println("FCollection failed to sort: \n" + comparator + "\n" + e.getMessage()); + } } } @Override public T get(final T obj) { - if (obj == null) { - return null; - } - for(T x : this) { - if (x.equals(obj)) { - return x; + synchronized (lock) { + if (obj == null) { + return null; } + for (T x : this) { + if (x.equals(obj)) { + return x; + } + } + return obj; } - return obj; } + /** * An unmodifiable, empty {@link FCollection}. Overrides all methods with * default implementations suitable for an empty collection, to improve @@ -592,93 +637,155 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ public static class EmptyFCollection extends FCollection { private static final long serialVersionUID = 8667965158891635997L; + public EmptyFCollection() { super(); } - @Override public final void add(final int index, final T element) { + + @Override + public final void add(final int index, final T element) { } - @Override public final boolean add(final T e) { + + @Override + public final boolean add(final T e) { return false; } - @Override public final boolean addAll(final Collection c) { + + @Override + public final boolean addAll(final Collection c) { return false; } - @Override public final boolean addAll(final int index, final Collection c) { + + @Override + public final boolean addAll(final int index, final Collection c) { return false; } - @Override public final boolean addAll(final Iterable i) { + + @Override + public final boolean addAll(final Iterable i) { return false; } - @Override public final boolean addAll(final T[] c) { + + @Override + public final boolean addAll(final T[] c) { return false; } - @Override public final void clear() { + + @Override + public final void clear() { } - @Override public final boolean contains(final Object o) { + + @Override + public final boolean contains(final Object o) { return false; } - @Override public final boolean containsAll(final Collection c) { + + @Override + public final boolean containsAll(final Collection c) { return c.isEmpty(); } - @Override public final T get(final int index) { + + @Override + public final T get(final int index) { throw new IndexOutOfBoundsException("Any index is out of bounds for an empty collection"); } - @Override public final T getFirst() { + + @Override + public final T getFirst() { throw new NoSuchElementException("Collection is empty"); } - @Override public final T getLast() { + + @Override + public final T getLast() { throw new NoSuchElementException("Collection is empty"); } - @Override public final int indexOf(final Object o) { + + @Override + public final int indexOf(final Object o) { return -1; } - @Override public final boolean isEmpty() { + + @Override + public final boolean isEmpty() { return true; } - @Override public final Iterator iterator() { + + @Override + public final Iterator iterator() { return Collections.emptyIterator(); } - @Override public final int lastIndexOf(final Object o) { + + @Override + public final int lastIndexOf(final Object o) { return -1; } - @Override public final ListIterator listIterator() { + + @Override + public final ListIterator listIterator() { return Collections.emptyListIterator(); } - @Override public final ListIterator listIterator(final int index) { + + @Override + public final ListIterator listIterator(final int index) { return Collections.emptyListIterator(); } - @Override public final T remove(final int index) { + + @Override + public final T remove(final int index) { throw new IndexOutOfBoundsException("Any index is out of bounds for an empty collection"); } - @Override public final boolean remove(final Object o) { + + @Override + public final boolean remove(final Object o) { return false; } - @Override public boolean removeAll(final Collection c) { + + @Override + public boolean removeAll(final Collection c) { return false; } - @Override public final boolean removeAll(final Iterable c) { + + @Override + public final boolean removeAll(final Iterable c) { return false; } - @Override public final boolean retainAll(final Collection c) { + + @Override + public final boolean retainAll(final Collection c) { return false; } - @Override public final T set(final int index, final T element) { + + @Override + public final T set(final int index, final T element) { throw new IndexOutOfBoundsException("Any index is out of bounds for an empty collection"); } - @Override public final int size() { + + @Override + public final int size() { return 0; } - @Override public final void sort() { + + @Override + public final void sort() { } - @Override public final void sort(final Comparator comparator) { + + @Override + public final void sort(final Comparator comparator) { } - @Override public final List subList(final int fromIndex, final int toIndex) { + + @Override + public final List subList(final int fromIndex, final int toIndex) { if (fromIndex == 0 && toIndex == 0) { return this; } throw new IndexOutOfBoundsException("Any index is out of bounds for an empty collection"); } - @Override public final Object[] toArray() { return ArrayUtils.EMPTY_OBJECT_ARRAY; } + + @Override + public final Object[] toArray() { + return ArrayUtils.EMPTY_OBJECT_ARRAY; + } + @Override @SuppressWarnings("hiding") public final T[] toArray(final T[] a) { @@ -687,8 +794,101 @@ public class FCollection implements List, /*Set,*/ FCollectionView, } return a; } - @Override public final String toString() { + + @Override + public final String toString() { return "[]"; } } + + private class Itr implements Iterator { + protected int cursor; + protected int lastRet; + final FCollection l; + + public Itr() { + cursor = 0; + lastRet = -1; + l = FCollection.this.clone(); + } + + + @Override + public boolean hasNext() { + return cursor < l.size(); + } + + @Override + public T next() { + int i = cursor; + if (i >= l.size()) { + throw new NoSuchElementException(); + } + cursor = i + 1; + return (T) l.get(lastRet = i); + } + + @Override + public void remove() { + if (lastRet < 0) { + throw new IllegalStateException(); + } + + l.remove(lastRet); + FCollection.this.remove(lastRet); + cursor = lastRet; + lastRet = -1; + } + } + + public class ListItr extends Itr implements ListIterator { + ListItr(int index) { + super(); + cursor = index; + } + + @Override + public boolean hasPrevious() { + return cursor > 0; + } + + @Override + public int nextIndex() { + return cursor; + } + + @Override + public int previousIndex() { + return cursor - 1; + } + + @Override + public T previous() { + int i = cursor - 1; + if (i < 0) { + throw new NoSuchElementException(); + } + cursor = i; + return (T) l.get(lastRet = i); + } + + @Override + public void set(T e) { + if (lastRet < 0) { + throw new IllegalStateException(); + } + + l.set(lastRet, e); + FCollection.this.set(lastRet, e); + } + + @Override + public void add(T e) { + int i = cursor; + l.add(i, e); + FCollection.this.add(i, e); + cursor = i + 1; + lastRet = -1; + } + } } diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 578bc472d69..325d3a6df5d 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -47,7 +47,6 @@ import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.trackable.Tracker; import forge.util.Aggregates; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.Visitor; import forge.util.collect.FCollection; @@ -400,7 +399,7 @@ public class Game { return ingamePlayers; } final PlayerCollection players = new PlayerCollection(ingamePlayers); - CollectionUtil.reverse(players); + Collections.reverse(players); return players; } @@ -420,7 +419,7 @@ public class Game { final PlayerCollection players = new PlayerCollection(ingamePlayers); players.remove(phaseHandler.getPlayerTurn()); if (!getTurnOrder().isDefaultDirection()) { - CollectionUtil.reverse(players); + Collections.reverse(players); } return players; } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 8b895351918..cd8a35ed702 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -2045,7 +2045,7 @@ public class GameAction { //shuffle List shuffledCards = Lists.newArrayList(p1.getZone(ZoneType.Library).getCards()); - CollectionUtil.shuffle(shuffledCards); + Collections.shuffle(shuffledCards); //check a second hand List hand2 = shuffledCards.subList(0,p1.getMaxHandSize()); @@ -2175,7 +2175,7 @@ public class GameAction { if (!powerPlayers.isEmpty()) { List players = Lists.newArrayList(powerPlayers); - CollectionUtil.shuffle(players, MyRandom.getRandom()); + Collections.shuffle(players, MyRandom.getRandom()); return players.get(0); } @@ -2417,7 +2417,7 @@ public class GameAction { int numLookedAt = 0; if (toTop != null) { numLookedAt += toTop.size(); - CollectionUtil.reverse(toTop); // reverse to get the correct order + Collections.reverse(toTop); // reverse to get the correct order for (Card c : toTop) { moveToLibrary(c, cause, null); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index fbbd31041b2..996a3fc998a 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -17,7 +17,6 @@ import forge.game.player.PlayerView; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.util.CardTranslation; -import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.Localizer; import forge.util.TextUtil; @@ -175,7 +174,7 @@ public class DigEffect extends SpellAbilityEffect { CardCollection all = new CardCollection(p.getCardsIn(srcZone)); if (sa.hasParam("FromBottom")) { - CollectionUtil.reverse(all); + Collections.reverse(all); } int numToDig = Math.min(digNum, all.size()); @@ -357,7 +356,7 @@ public class DigEffect extends SpellAbilityEffect { if (sa.hasParam("ForgetOtherRemembered")) { host.clearRemembered(); } - CollectionUtil.reverse(movedCards); + Collections.reverse(movedCards); if (destZone1.equals(ZoneType.Battlefield) || destZone1.equals(ZoneType.Library)) { if (sa.hasParam("GainControl")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java index 98d3f95e6e7..2f41746a575 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java @@ -1,5 +1,6 @@ package forge.game.ability.effects; +import java.util.Collections; import java.util.Map; import com.google.common.collect.Maps; @@ -17,7 +18,6 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; -import forge.util.CollectionUtil; import forge.util.Localizer; public class DigMultipleEffect extends SpellAbilityEffect { @@ -160,7 +160,7 @@ public class DigMultipleEffect extends SpellAbilityEffect { } if (libraryPosition2 != -1) { // Closest to top - CollectionUtil.reverse(afterOrder); + Collections.reverse(afterOrder); } for (final Card c : afterOrder) { final ZoneType origin = c.getZone().getZoneType(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java index b4f3850b55b..93460c57a8e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java @@ -1,5 +1,6 @@ package forge.game.ability.effects; +import java.util.Collections; import java.util.Iterator; import java.util.Map; @@ -17,7 +18,6 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; -import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.Localizer; import forge.util.MyRandom; @@ -261,7 +261,7 @@ public class DigUntilEffect extends SpellAbilityEffect { } if (sa.hasParam("RevealRandomOrder")) { - CollectionUtil.shuffle(revealed, MyRandom.getRandom()); + Collections.shuffle(revealed, MyRandom.getRandom()); } if (sa.hasParam("NoMoveRevealed") || sequential) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/DraftEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DraftEffect.java index b323b101088..77175b86dee 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DraftEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DraftEffect.java @@ -11,7 +11,6 @@ import forge.game.card.CardZoneTable; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.util.CollectionUtil; import forge.util.Localizer; import java.util.*; @@ -52,7 +51,7 @@ import java.util.*; CardCollection drafted = new CardCollection(); for (int i = 0; i < numToDraft; i++) { - CollectionUtil.shuffle(spellbook); + Collections.shuffle(spellbook); List draftOptions = new ArrayList<>(); for (String name : spellbook.subList(0, 3)) { // Cardnames that include "," must use ";" instead in Spellbook$ (i.e. Tovolar; Dire Overlord) diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java index c09371d965d..40f3dd2013c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java @@ -191,15 +191,15 @@ public class PlayEffect extends SpellAbilityEffect { if (sa.hasParam("ValidSA")) { final String valid[] = sa.getParam("ValidSA").split(","); - final List invalid = new ArrayList<>(); - for (Card c : tgtCards) { + Iterator it = tgtCards.iterator(); + while (it.hasNext()) { + Card c = it.next(); if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, controller), SpellAbilityPredicates.isValid(valid, controller , source, sa))) { - invalid.add(c); + // it.remove will only remove item from the list part of CardCollection + tgtCards.asSet().remove(c); + it.remove(); } } - if (!invalid.isEmpty()) - tgtCards.removeAll(invalid); - if (tgtCards.isEmpty()) { return; } @@ -232,16 +232,16 @@ public class PlayEffect extends SpellAbilityEffect { while (!tgtCards.isEmpty() && amount > 0 && totalCMCLimit >= 0) { if (hasTotalCMCLimit) { // filter out cards with mana value greater than limit + Iterator it = tgtCards.iterator(); final String [] valid = {"Spell.cmcLE" + totalCMCLimit}; - List invalid = new ArrayList<>(); - for (Card c : tgtCards) { + while (it.hasNext()) { + Card c = it.next(); if (!Iterables.any(AbilityUtils.getBasicSpellsFromPlayEffect(c, controller), SpellAbilityPredicates.isValid(valid, controller , c, sa))) { - invalid.add(c); + // it.remove will only remove item from the list part of CardCollection + tgtCards.asSet().remove(c); + it.remove(); } } - // it.remove will only remove item from the list part of CardCollection - if (!invalid.isEmpty()) - tgtCards.removeAll(invalid); if (tgtCards.isEmpty()) break; params.put("CMCLimit", totalCMCLimit); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java index ce24ca6bf65..4ab1ab560b5 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java @@ -1,5 +1,6 @@ package forge.game.ability.effects; +import java.util.Collections; import java.util.List; import forge.game.ability.SpellAbilityEffect; @@ -8,7 +9,6 @@ import forge.game.card.CardCollectionView; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.util.CollectionUtil; import forge.util.Lang; import forge.util.MyRandom; @@ -34,7 +34,7 @@ public class ReorderZoneEffect extends SpellAbilityEffect { CardCollection list = new CardCollection(p.getCardsIn(zone)); if (shuffle) { - CollectionUtil.shuffle(list, MyRandom.getRandom()); + Collections.shuffle(list, MyRandom.getRandom()); p.getZone(zone).setCards(list); } else { CardCollectionView orderedCards = p.getController().orderMoveToZoneList(list, zone, sa); diff --git a/forge-game/src/main/java/forge/game/card/CardLists.java b/forge-game/src/main/java/forge/game/card/CardLists.java index 0414714b1c2..3688b8668a5 100644 --- a/forge-game/src/main/java/forge/game/card/CardLists.java +++ b/forge-game/src/main/java/forge/game/card/CardLists.java @@ -32,7 +32,6 @@ import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; import forge.game.staticability.StaticAbilityCrewValue; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.collect.FCollectionView; @@ -154,7 +153,7 @@ public class CardLists { } public static void shuffle(List list) { - CollectionUtil.shuffle(list, MyRandom.getRandom()); + Collections.shuffle(list, MyRandom.getRandom()); } public static CardCollection filterControlledBy(Iterable cardList, Player player) { diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 31701856d3d..2570a8a36ee 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -28,7 +28,6 @@ import forge.game.spellability.SpellAbilityStackInstance; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.item.PaperCard; -import forge.util.CollectionUtil; import forge.util.Expressions; import forge.util.TextUtil; import forge.util.collect.FCollection; @@ -624,13 +623,13 @@ public class CardProperty { } } else if (property.startsWith("TopGraveyardCreature")) { CardCollection cards = CardLists.filter(card.getOwner().getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES); - CollectionUtil.reverse(cards); + Collections.reverse(cards); if (cards.isEmpty() || !card.equals(cards.get(0))) { return false; } } else if (property.startsWith("TopGraveyard")) { final CardCollection cards = new CardCollection(card.getOwner().getCardsIn(ZoneType.Graveyard)); - CollectionUtil.reverse(cards); + Collections.reverse(cards); if (property.substring(12).matches("[0-9][0-9]?")) { int n = Integer.parseInt(property.substring(12)); int num = Math.min(n, cards.size()); @@ -666,7 +665,7 @@ public class CardProperty { if (property.startsWith("BottomLibrary_")) { cards = CardLists.getValidCards(cards, property.substring(14), sourceController, source, spellAbility); } - CollectionUtil.reverse(cards); + Collections.reverse(cards); if (cards.isEmpty() || !card.equals(cards.get(0))) { return false; } diff --git a/forge-game/src/main/java/forge/game/mana/ManaRefundService.java b/forge-game/src/main/java/forge/game/mana/ManaRefundService.java index 324cc8364bf..5f73179aa6f 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaRefundService.java +++ b/forge-game/src/main/java/forge/game/mana/ManaRefundService.java @@ -6,8 +6,8 @@ import forge.game.event.GameEventZone; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.util.CollectionUtil; +import java.util.Collections; import java.util.List; public class ManaRefundService { @@ -39,7 +39,7 @@ public class ManaRefundService { List payingAbilities = sa.getPayingManaAbilities(); // start with the most recent - CollectionUtil.reverse(payingAbilities); + Collections.reverse(payingAbilities); for (final SpellAbility am : payingAbilities) { // What if am is owned by a different player? 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 052701ce32e..0eb1c7bf820 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1149,7 +1149,7 @@ public class Player extends GameEntity implements Comparable { } if (toTop != null) { - CollectionUtil.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. + Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. for (Card c : toTop) { getGame().getAction().moveToLibrary(c, cause, params); numToTop++; @@ -1676,7 +1676,7 @@ public class Player extends GameEntity implements Comparable { final CardCollection list = new CardCollection(getCardsIn(ZoneType.Library)); // Note: Shuffling once is sufficient. - CollectionUtil.shuffle(list, MyRandom.getRandom()); + Collections.shuffle(list, MyRandom.getRandom()); getZone(ZoneType.Library).setCards(getController().cheatShuffle(list)); diff --git a/forge-game/src/main/java/forge/game/zone/Zone.java b/forge-game/src/main/java/forge/game/zone/Zone.java index 73ddff61596..8035c09abaa 100644 --- a/forge-game/src/main/java/forge/game/zone/Zone.java +++ b/forge-game/src/main/java/forge/game/zone/Zone.java @@ -17,10 +17,7 @@ */ package forge.game.zone; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import com.google.common.base.Predicate; @@ -35,7 +32,6 @@ import forge.game.event.EventValueChangeType; import forge.game.event.GameEventZone; import forge.game.player.Player; import forge.util.CollectionSuppliers; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.maps.EnumMapOfLists; import forge.util.maps.MapOfLists; @@ -278,7 +274,7 @@ public class Zone implements java.io.Serializable, Iterable { } public void shuffle() { - CollectionUtil.shuffle(cardList, MyRandom.getRandom()); + Collections.shuffle(cardList, MyRandom.getRandom()); onChanged(); } diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java b/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java index f2b57e1f7a6..756dfe195fb 100644 --- a/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java +++ b/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java @@ -18,7 +18,6 @@ import forge.screens.home.quest.DialogChooseFormats; import forge.screens.home.quest.DialogChooseSets; import forge.screens.match.controllers.CDetailPicture; import forge.util.CollectionSuppliers; -import forge.util.CollectionUtil; import forge.util.Localizer; import javax.swing.*; @@ -99,7 +98,7 @@ public class CardManager extends ItemManager { // Use standard sort + index, for better performance! Collections.sort(acceptedEditions); if (StaticData.instance().cardArtPreferenceIsLatest()) - CollectionUtil.reverse(acceptedEditions); + Collections.reverse(acceptedEditions); Iterator editionIterator = acceptedEditions.iterator(); Entry candidateEntry = null; Entry firstCandidateEntryFound = null; diff --git a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CProbabilities.java b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CProbabilities.java index bc334f38bb9..f70ba920632 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CProbabilities.java +++ b/forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/CProbabilities.java @@ -1,10 +1,6 @@ package forge.screens.deckeditor.controllers; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import forge.deck.DeckBase; import forge.gui.UiCommand; @@ -13,7 +9,6 @@ import forge.item.InventoryItem; import forge.item.PaperCard; import forge.screens.deckeditor.CDeckEditorUI; import forge.screens.deckeditor.views.VProbabilities; -import forge.util.CollectionUtil; import forge.util.ItemPool; import forge.util.MyRandom; @@ -63,7 +58,7 @@ public enum CProbabilities implements ICDoc { final List cardProbabilities = new ArrayList<>(); final List shuffled = deck.toFlatList(); - CollectionUtil.shuffle(shuffled, MyRandom.getRandom()); + Collections.shuffle(shuffled, MyRandom.getRandom()); // Log totals of each card for decrementing final Map cardTotals = new HashMap<>(); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java b/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java index 6ffd7bfedac..1bd34fdf521 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java @@ -13,7 +13,6 @@ import forge.game.GameFormat; import forge.gui.SOverlayUtils; import forge.localinstance.skin.FSkinProp; import forge.model.FModel; -import forge.util.CollectionUtil; import forge.util.Localizer; import net.miginfocom.swing.MigLayout; import forge.toolbox.FCheckBoxTree.FTreeNode; @@ -468,7 +467,7 @@ public class DialogChooseSets { FTreeNode setTypeNode = checkBoxTree.getNodeByKey(editionType); if (setTypeNode != null){ List activeChildNodes = checkBoxTree.getActiveChildNodes(setTypeNode); - CollectionUtil.shuffle(activeChildNodes); + Collections.shuffle(activeChildNodes); for (int i = 0; i < totalToSelect; i++) checkBoxTree.setNodeCheckStatus(activeChildNodes.get(i), true); } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java index a9fd9d834e1..d7e6e587cec 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java @@ -26,12 +26,12 @@ import forge.screens.deckeditor.controllers.CEditorDraftingProcess; import forge.screens.deckeditor.views.VProbabilities; import forge.screens.deckeditor.views.VStatistics; import forge.toolbox.FOptionPane; -import forge.util.CollectionUtil; import forge.util.Localizer; import javax.swing.*; import java.awt.event.ActionListener; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -163,7 +163,7 @@ public enum CSubmenuDraft implements ICDoc { for(int i = 0; i < maxDecks; i++) { aiIndices.add(i); } - CollectionUtil.shuffle(aiIndices); + Collections.shuffle(aiIndices); aiIndices = aiIndices.subList(0, numOpponents); for(int i : aiIndices) { diff --git a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java index 8c36a5f347e..6fe9bd2e68b 100644 --- a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java +++ b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java @@ -1,15 +1,10 @@ package forge.view; import java.io.File; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import forge.util.CollectionUtil; import org.apache.commons.lang3.time.StopWatch; import forge.LobbyPlayer; @@ -201,7 +196,7 @@ public class SimulateMatch { } else { log = g1.getGameLog().getLogEntries(GameLogEntryType.MATCH_RESULTS); } - CollectionUtil.reverse(log); + Collections.reverse(log); for (GameLogEntry l : log) { System.out.println(l); } diff --git a/forge-gui-desktop/src/test/java/forge/FCollectionTest.java b/forge-gui-desktop/src/test/java/forge/FCollectionTest.java new file mode 100644 index 00000000000..fd3a5863385 --- /dev/null +++ b/forge-gui-desktop/src/test/java/forge/FCollectionTest.java @@ -0,0 +1,64 @@ +package forge; + +import forge.game.card.Card; +import forge.game.card.CardCollection; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import static org.testng.Assert.assertEquals; + +public class FCollectionTest { + /** + * Just a quick test for FCollection. + */ + @Test + void testBadIteratorLogic() { + List cards = new ArrayList<>(); + for (int i = 1; i < 5; i++) + cards.add(new Card(i, null)); + CardCollection cc = new CardCollection(cards); + Iterator it = cc.iterator(); + it.next(); + it.remove(); + assertEquals(cc.size(), 3); + } + + @Test + void testBadIteratorLogicTwo() { + List cards = new ArrayList<>(); + for (int i = 1; i <= 10; i++) + cards.add(new Card(i, null)); + CardCollection cc = new CardCollection(cards); + int i = 0; + for (Card c : cc) { + if (i != 3) + cc.remove(c); + i++; + } + assertEquals(cc.size(), 1); + } + + @Test + void testCompletableFuture() { + List cards = new ArrayList<>(); + for (int i = 1; i < 5; i++) + cards.add(new Card(i, null)); + CardCollection cc = new CardCollection(cards); + List> futures = new ArrayList<>(); + for (Card c : cc) { + futures.add(CompletableFuture.supplyAsync(() -> { + if (c.getId() % 2 > 0) + cc.remove(c); + return 0; + })); + } + CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); + CompletableFuture.allOf(futuresArray).join(); + futures.clear(); + assertEquals(cc.size(), 2); + } +} diff --git a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java index c52b899dad4..57fad325c95 100644 --- a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java +++ b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java @@ -1,14 +1,7 @@ package forge.deck; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; -import forge.util.CollectionUtil; import org.apache.commons.lang3.tuple.Pair; import com.google.common.base.Predicate; @@ -701,7 +694,7 @@ public class DeckgenUtil { }else { String matrixKey = (format.equals(DeckFormat.TinyLeaders) ? DeckFormat.Commander : format).toString(); //use Commander for Tiny Leaders List> potentialCards = new ArrayList<>(CardRelationMatrixGenerator.cardPools.get(matrixKey).get(commander.getName())); - CollectionUtil.shuffle(potentialCards, MyRandom.getRandom()); + Collections.shuffle(potentialCards, MyRandom.getRandom()); for(Map.Entry pair:potentialCards){ if(format.isLegalCard(pair.getKey())) { preSelectedCards.add(pair.getKey()); @@ -800,7 +793,7 @@ public class DeckgenUtil { break; } List cardList = Lists.newArrayList(colorList); - CollectionUtil.shuffle(cardList, MyRandom.getRandom()); + Collections.shuffle(cardList, MyRandom.getRandom()); int shortlistlength=400; if(cardList.size() { } List category = new ArrayList<>(categories.values()); - CollectionUtil.reverse(category); + Collections.reverse(category); final NetDeckArchiveBlock c = SGuiChoose.oneOrNone("Select a Net Deck Archive Block category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java b/forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java index 7892ff690ec..667fc26aed0 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchiveLegacy.java @@ -7,7 +7,6 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -72,7 +71,7 @@ public class NetDeckArchiveLegacy extends StorageBase { } List category = new ArrayList<>(categories.values()); - CollectionUtil.reverse(category); + Collections.reverse(category); final NetDeckArchiveLegacy c = SGuiChoose.oneOrNone("Select a Net Deck Archive Legacy category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java b/forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java index 4092efb01c2..2060490abc5 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchiveModern.java @@ -7,7 +7,6 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -72,7 +71,7 @@ public class NetDeckArchiveModern extends StorageBase { } List category = new ArrayList<>(categories.values()); - CollectionUtil.reverse(category); + Collections.reverse(category); final NetDeckArchiveModern c = SGuiChoose.oneOrNone("Select a Net Deck Archive Modern category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchivePauper.java b/forge-gui/src/main/java/forge/deck/NetDeckArchivePauper.java index 751bdb5b400..ec60dcb8658 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchivePauper.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchivePauper.java @@ -7,7 +7,6 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -72,7 +71,7 @@ public class NetDeckArchivePauper extends StorageBase { } List category = new ArrayList<>(categories.values()); - CollectionUtil.reverse(category); + Collections.reverse(category); final NetDeckArchivePauper c = SGuiChoose.oneOrNone("Select a Net Deck Archive Pauper category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java b/forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java index 02efbee551d..903d120da1e 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchivePioneer.java @@ -7,7 +7,6 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -72,7 +71,7 @@ public class NetDeckArchivePioneer extends StorageBase { } List category = new ArrayList<>(categories.values()); - CollectionUtil.reverse(category); + Collections.reverse(category); final NetDeckArchivePioneer c = SGuiChoose.oneOrNone("Select a Net Deck Archive Pioneer category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java b/forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java index b7176c43cf7..62ae7f782a0 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchiveStandard.java @@ -7,7 +7,6 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -72,7 +71,7 @@ public class NetDeckArchiveStandard extends StorageBase { } List category = new ArrayList<>(categories.values()); - CollectionUtil.reverse(category); + Collections.reverse(category); final NetDeckArchiveStandard c = SGuiChoose.oneOrNone("Select a Net Deck Archive Standard category",category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java b/forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java index b8bc583f0e5..97a917362f0 100644 --- a/forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java +++ b/forge-gui/src/main/java/forge/deck/NetDeckArchiveVintage.java @@ -7,7 +7,6 @@ import forge.gui.GuiBase; import forge.gui.download.GuiDownloadZipService; import forge.gui.util.SGuiChoose; import forge.localinstance.properties.ForgeConstants; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.WaitCallback; import forge.util.storage.StorageBase; @@ -72,7 +71,7 @@ public class NetDeckArchiveVintage extends StorageBase { } List category = new ArrayList<>(categories.values()); - CollectionUtil.reverse(category); + Collections.reverse(category); final NetDeckArchiveVintage c = SGuiChoose.oneOrNone("Select a Net Deck Archive Vintage category", category); if (c == null) { return null; } diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java b/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java index b8ccac3bc89..9fb12cbec15 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/BoosterDraft.java @@ -36,7 +36,6 @@ import forge.localinstance.properties.ForgeConstants; import forge.localinstance.properties.ForgePreferences; import forge.model.CardBlock; import forge.model.FModel; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.ItemPool; import forge.util.Localizer; @@ -475,7 +474,7 @@ public class BoosterDraft implements IBoosterDraft { // Maybe the AI could have more knowledge about the other players. // Like don't pass to players that have revealed certain cards or colors // But random is probably fine for now - CollectionUtil.shuffle(dredgers); + Collections.shuffle(dredgers); passToPlayer = dredgers.get(0); } else { // Human player, so we need to ask them @@ -557,7 +556,7 @@ public class BoosterDraft implements IBoosterDraft { } } - CollectionUtil.shuffle(brokers); + Collections.shuffle(brokers); for(LimitedPlayer pl : brokers) { pl.activateBrokers(this.players); } diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/CardThemedDeckBuilder.java b/forge-gui/src/main/java/forge/gamemodes/limited/CardThemedDeckBuilder.java index ec25878a6bf..cadbcedb48f 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/CardThemedDeckBuilder.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/CardThemedDeckBuilder.java @@ -1,12 +1,6 @@ package forge.gamemodes.limited; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import com.google.common.base.Predicate; import com.google.common.base.Predicates; @@ -33,7 +27,6 @@ import forge.item.IPaperCard; import forge.item.PaperCard; import forge.localinstance.properties.ForgePreferences; import forge.model.FModel; -import forge.util.CollectionUtil; import forge.util.MyRandom; /** @@ -628,7 +621,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { possibleList.removeAll(StaticData.instance().getCommonCards().getAllCards(secondKeyCard.getName())); } //Iterator iRandomPool = CardRanker.rankCardsInDeck(possibleList.subList(0, targetSize <= possibleList.size() ? targetSize : possibleList.size())).iterator(); - CollectionUtil.shuffle(possibleList); + Collections.shuffle(possibleList); Iterator iRandomPool = possibleList.iterator(); while (deckList.size() < targetSize) { if (logToConsole) { @@ -864,7 +857,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { if (secondKeyCard != null) { possibleList.removeAll(StaticData.instance().getCommonCards().getAllCards(secondKeyCard.getName())); } - CollectionUtil.shuffle(possibleList); + Collections.shuffle(possibleList); //addManaCurveCards(CardRanker.rankCardsInDeck(possibleList.subList(0, targetSize*3 <= possibleList.size() ? targetSize*3 : possibleList.size())), //num, "Random Card"); addManaCurveCards(possibleList, num, "Random Card"); diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java index 89e23687840..1a9bae2da9c 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayer.java @@ -11,7 +11,6 @@ import forge.deck.DeckSection; import forge.gui.util.SGuiChoose; import forge.item.PaperCard; import forge.model.FModel; -import forge.util.CollectionUtil; import forge.util.TextUtil; import java.util.*; @@ -724,7 +723,7 @@ public class LimitedPlayer { } public PaperCard pickFromArchdemonCurse(DraftPack chooseFrom) { - CollectionUtil.shuffle(chooseFrom); + Collections.shuffle(chooseFrom); reduceArchdemonOfPalianoCurse(); return chooseFrom.get(0); } diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java index f81da820c98..21da70a76e5 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/LimitedPlayerAI.java @@ -10,10 +10,10 @@ import forge.deck.DeckSection; import forge.deck.generation.DeckGeneratorBase; import forge.item.PaperCard; import forge.localinstance.properties.ForgePreferences; -import forge.util.CollectionUtil; import forge.util.MyRandom; import org.apache.commons.lang3.tuple.Pair; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -81,7 +81,7 @@ public class LimitedPlayerAI extends LimitedPlayer { // For Paliano, if player has revealed anything, try to avoid that color // For Regicide, don't choose one of my colors } - CollectionUtil.shuffle(colors); + Collections.shuffle(colors); return colors.get(0); } @@ -89,7 +89,7 @@ public class LimitedPlayerAI extends LimitedPlayer { protected String removeWithAny(PaperCard bestPick, List options) { // If we have multiple remove from draft options, do none of them for now - CollectionUtil.shuffle(options); + Collections.shuffle(options); if (options.get(0).equals("Animus of Predation")) { if (removeWithAnimus(bestPick)) { return "Animus of Predation"; @@ -254,7 +254,7 @@ public class LimitedPlayerAI extends LimitedPlayer { @Override protected CardEdition chooseEdition(List possibleEditions) { - CollectionUtil.shuffle(possibleEditions); + Collections.shuffle(possibleEditions); return possibleEditions.get(0); } diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/SealedCardPoolGenerator.java b/forge-gui/src/main/java/forge/gamemodes/limited/SealedCardPoolGenerator.java index 9d5fe9f7d0a..d110d7759e5 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/SealedCardPoolGenerator.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/SealedCardPoolGenerator.java @@ -37,7 +37,6 @@ import forge.localinstance.skin.FSkinProp; import forge.model.CardBlock; import forge.model.FModel; import forge.model.UnOpenedMeta; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.Localizer; import forge.util.MyRandom; @@ -174,7 +173,7 @@ public class SealedCardPoolGenerator { case Prerelease: ArrayList editions = Lists.newArrayList(StaticData.instance().getEditions().getPrereleaseEditions()); Collections.sort(editions); - CollectionUtil.reverse(editions); + Collections.reverse(editions); CardEdition chosenEdition = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblChooseAnEdition"), editions); if (chosenEdition == null) { diff --git a/forge-gui/src/main/java/forge/gamemodes/limited/WinstonDraft.java b/forge-gui/src/main/java/forge/gamemodes/limited/WinstonDraft.java index a3a3fd9f935..613aa3a1828 100644 --- a/forge-gui/src/main/java/forge/gamemodes/limited/WinstonDraft.java +++ b/forge-gui/src/main/java/forge/gamemodes/limited/WinstonDraft.java @@ -1,6 +1,7 @@ package forge.gamemodes.limited; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Stack; @@ -11,7 +12,6 @@ import com.google.common.collect.Iterables; import forge.deck.CardPool; import forge.deck.Deck; import forge.item.PaperCard; -import forge.util.CollectionUtil; import forge.util.MyRandom; public class WinstonDraft extends BoosterDraft { @@ -47,7 +47,7 @@ public class WinstonDraft extends BoosterDraft { } } } - CollectionUtil.shuffle(this.deck, MyRandom.getRandom()); + Collections.shuffle(this.deck, MyRandom.getRandom()); // Create three Winston piles, adding the top card from the Winston deck to start each pile this.piles = new ArrayList<>(); diff --git a/forge-gui/src/main/java/forge/gamemodes/planarconquest/ConquestData.java b/forge-gui/src/main/java/forge/gamemodes/planarconquest/ConquestData.java index 51d7142d96a..ae7bee33b20 100644 --- a/forge-gui/src/main/java/forge/gamemodes/planarconquest/ConquestData.java +++ b/forge-gui/src/main/java/forge/gamemodes/planarconquest/ConquestData.java @@ -42,7 +42,6 @@ import forge.localinstance.properties.ForgeConstants; import forge.localinstance.skin.ISkinImage; import forge.model.FModel; import forge.util.CardTranslation; -import forge.util.CollectionUtil; import forge.util.FileUtil; import forge.util.Localizer; import forge.util.XmlReader; @@ -590,7 +589,7 @@ public final class ConquestData { path.add(current.loc); current = current.came_from; } - CollectionUtil.reverse(path); //reverse path so it begins with start location + Collections.reverse(path); //reverse path so it begins with start location return path; } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/BoosterUtils.java b/forge-gui/src/main/java/forge/gamemodes/quest/BoosterUtils.java index 76760120093..e3d851abecc 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/BoosterUtils.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/BoosterUtils.java @@ -20,10 +20,10 @@ package forge.gamemodes.quest; import static forge.gamemodes.quest.QuestUtilCards.isLegalInQuestFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; -import forge.util.CollectionUtil; import org.apache.commons.lang3.StringUtils; import com.google.common.base.Predicate; @@ -236,7 +236,7 @@ public final class BoosterUtils { preferredColors.clear(); int numberOfColors = COLOR_COUNT_PROBABILITIES[(int) (MyRandom.getRandom().nextDouble() * COLOR_COUNT_PROBABILITIES.length)]; if (numberOfColors < 6) { - CollectionUtil.shuffle(possibleColors); + Collections.shuffle(possibleColors); for (int i = 0; i < numberOfColors; i++) { preferredColors.add(possibleColors.get(i)); } @@ -391,7 +391,7 @@ public final class BoosterUtils { final int size = allowedColors == null ? 0 : allowedColors.size(); if (allowedColors != null) { - CollectionUtil.shuffle(allowedColors); + Collections.shuffle(allowedColors); } int cntMade = 0, iAttempt = 0; diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/MainWorldEventDuelManager.java b/forge-gui/src/main/java/forge/gamemodes/quest/MainWorldEventDuelManager.java index d3779651930..d7ac90f1454 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/MainWorldEventDuelManager.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/MainWorldEventDuelManager.java @@ -3,6 +3,7 @@ package forge.gamemodes.quest; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import forge.gamemodes.quest.data.QuestPreferences; @@ -11,7 +12,6 @@ import forge.gamemodes.quest.data.QuestPreferences.QPref; import forge.gamemodes.quest.io.MainWorldDuelReader; import forge.model.FModel; import forge.util.CollectionSuppliers; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.maps.EnumMapOfLists; import forge.util.maps.MapOfLists; @@ -259,7 +259,7 @@ public class MainWorldEventDuelManager implements QuestEventDuelManagerInterface public void randomizeOpponents() { for (QuestEventDifficulty qd : sortedDuels.keySet()) { List list = (List) sortedDuels.get(qd); - CollectionUtil.shuffle(list, MyRandom.getRandom()); + Collections.shuffle(list, MyRandom.getRandom()); } } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestController.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestController.java index 9b6db1f4666..5bd6af0bb2d 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestController.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestController.java @@ -18,12 +18,7 @@ package forge.gamemodes.quest; import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; import com.google.common.collect.Lists; import com.google.common.eventbus.Subscribe; @@ -50,7 +45,6 @@ import forge.item.PreconDeck; import forge.localinstance.properties.ForgeConstants; import forge.model.FModel; import forge.player.GamePlayerUtil; -import forge.util.CollectionUtil; import forge.util.storage.IStorage; import forge.util.storage.StorageBase; @@ -612,7 +606,7 @@ public class QuestController { } } - CollectionUtil.shuffle(unlockedChallengeIds); + Collections.shuffle(unlockedChallengeIds); maxChallenges = Math.min(maxChallenges, unlockedChallengeIds.size()); diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventCommanderDuelManager.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventCommanderDuelManager.java index 3b1b95445c0..686b37d7839 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventCommanderDuelManager.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventCommanderDuelManager.java @@ -1,6 +1,7 @@ package forge.gamemodes.quest; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import forge.deck.CardPool; @@ -11,7 +12,6 @@ import forge.deck.DeckProxy; import forge.gamemodes.quest.data.QuestPreferences; import forge.item.PaperCard; import forge.model.FModel; -import forge.util.CollectionUtil; import forge.util.MyRandom; /** @@ -198,6 +198,6 @@ public class QuestEventCommanderDuelManager implements QuestEventDuelManagerInte * Randomizes the list of Commander Duels. */ public void randomizeOpponents(){ - CollectionUtil.shuffle(commanderDuels); + Collections.shuffle(commanderDuels); } } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDraft.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDraft.java index 1697f194a0f..1a3a2137df2 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDraft.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDraft.java @@ -46,7 +46,6 @@ import forge.item.PaperCard; import forge.model.CardBlock; import forge.model.FModel; import forge.player.GamePlayerUtil; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.NameGenerator; import forge.util.TextUtil; @@ -857,7 +856,7 @@ public class QuestEventDraft implements IQuestEvent { return null; } - CollectionUtil.shuffle(possibleFormats); + Collections.shuffle(possibleFormats); return getDraftOrNull(quest, possibleFormats.get(0)); } @@ -886,7 +885,7 @@ public class QuestEventDraft implements IQuestEvent { System.err.println("Warning: no valid set combinations were detected when trying to generate a draft tournament for the format: " + format); return null; } - CollectionUtil.shuffle(possibleSetCombinations); + Collections.shuffle(possibleSetCombinations); event.boosterConfiguration = possibleSetCombinations.get(0); } @@ -903,7 +902,7 @@ public class QuestEventDraft implements IQuestEvent { players.add("6"); players.add("7"); - CollectionUtil.shuffle(players); + Collections.shuffle(players); // Initialize tournament for (int i = 0; i < players.size(); i++) { diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDuelManager.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDuelManager.java index 23fdfcc2774..9f147e80cbc 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDuelManager.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventDuelManager.java @@ -20,6 +20,7 @@ package forge.gamemodes.quest; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import forge.gamemodes.quest.data.QuestPreferences; @@ -28,7 +29,6 @@ import forge.gamemodes.quest.data.QuestPreferences.QPref; import forge.gamemodes.quest.io.QuestDuelReader; import forge.model.FModel; import forge.util.CollectionSuppliers; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.maps.EnumMapOfLists; import forge.util.maps.MapOfLists; @@ -236,7 +236,7 @@ public class QuestEventDuelManager implements QuestEventDuelManagerInterface { public void randomizeOpponents() { for (QuestEventDifficulty qd : sortedDuels.keySet()) { List list = (List) sortedDuels.get(qd); - CollectionUtil.shuffle(list, MyRandom.getRandom()); + Collections.shuffle(list, MyRandom.getRandom()); } } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventLDADuelManager.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventLDADuelManager.java index c110e1eeff4..e85d3a2f391 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventLDADuelManager.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestEventLDADuelManager.java @@ -19,6 +19,7 @@ package forge.gamemodes.quest; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import forge.deck.CardArchetypeLDAGenerator; @@ -29,7 +30,6 @@ import forge.gamemodes.quest.data.QuestPreferences.DifficultyPrefs; import forge.gamemodes.quest.data.QuestPreferences.QPref; import forge.model.FModel; import forge.util.CollectionSuppliers; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.maps.EnumMapOfLists; import forge.util.maps.MapOfLists; @@ -236,7 +236,7 @@ public class QuestEventLDADuelManager implements QuestEventDuelManagerInterface public void randomizeOpponents() { for (QuestEventDifficulty qd : sortedDuels.keySet()) { List list = (List) sortedDuels.get(qd); - CollectionUtil.shuffle(list, MyRandom.getRandom()); + Collections.shuffle(list, MyRandom.getRandom()); } } } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilCards.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilCards.java index bc03895ca91..063cb5d0e36 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilCards.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilCards.java @@ -42,13 +42,13 @@ import forge.item.generation.UnOpenedProduct; import forge.localinstance.properties.ForgePreferences.FPref; import forge.model.FModel; import forge.util.Aggregates; -import forge.util.CollectionUtil; import forge.util.ItemPool; import forge.util.MyRandom; import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map.Entry; @@ -678,7 +678,7 @@ public final class QuestUtilCards { editions.add(e); } - CollectionUtil.shuffle(editions); + Collections.shuffle(editions); int numberOfBoxes = Math.min(Math.max(count / 2, 1), editions.size()); diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java index 12c7720699e..05d391aa92d 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtilUnlockSets.java @@ -30,7 +30,6 @@ import forge.item.PaperCard; import forge.item.SealedTemplate; import forge.item.generation.UnOpenedProduct; import forge.model.FModel; -import forge.util.CollectionUtil; import forge.util.TextUtil; import forge.util.storage.IStorage; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -171,7 +170,7 @@ public class QuestUtilUnlockSets { options.add(set.left); // System.out.println("Padded with: " + fillers.get(i).getName()); } - CollectionUtil.reverse(options); + Collections.reverse(options); if (FModel.getQuestPreferences().getPrefInt(QPref.UNLIMITED_UNLOCKING) == 0) { return options.subList(0, Math.min(options.size(), Math.min(8, 2 + ((qData.getAchievements().getWin()) / 50)))); diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java b/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java index 4a455fbe60d..bc52df6736f 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java @@ -1,10 +1,10 @@ package forge.gamemodes.quest.setrotation; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Random; import forge.model.FModel; -import forge.util.CollectionUtil; import static java.lang.Integer.min; @@ -27,7 +27,7 @@ public class QueueRandomRotation implements ISetRotation { int seed = FModel.getQuest().getName().hashCode(); Random rnd = new Random(seed); List shuffledSets = new ArrayList<>(allSets); - CollectionUtil.shuffle(shuffledSets, rnd); + Collections.shuffle(shuffledSets, rnd); List currentCodes = new ArrayList<>(); int outRotations = FModel.getQuest().getAchievements().getWin() / rotateAfterWins; diff --git a/forge-gui/src/main/java/forge/gamemodes/tournament/system/AbstractTournament.java b/forge-gui/src/main/java/forge/gamemodes/tournament/system/AbstractTournament.java index 79b9b7d71a5..9a12e54cdc2 100644 --- a/forge-gui/src/main/java/forge/gamemodes/tournament/system/AbstractTournament.java +++ b/forge-gui/src/main/java/forge/gamemodes/tournament/system/AbstractTournament.java @@ -2,6 +2,7 @@ package forge.gamemodes.tournament.system; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import com.google.common.collect.Lists; @@ -11,7 +12,6 @@ import forge.LobbyPlayer; import forge.deck.DeckGroup; import forge.game.player.RegisteredPlayer; import forge.player.GamePlayerUtil; -import forge.util.CollectionUtil; import forge.util.MyRandom; import forge.util.TextUtil; @@ -44,7 +44,7 @@ public abstract class AbstractTournament implements Serializable { public void initializeTournament() { // "Randomly" seed players to start tournament - CollectionUtil.shuffle(remainingPlayers, MyRandom.getRandom()); + Collections.shuffle(remainingPlayers, MyRandom.getRandom()); generateActivePairings(); initialized = true; } diff --git a/forge-gui/src/main/java/forge/gamemodes/tournament/system/TournamentSwiss.java b/forge-gui/src/main/java/forge/gamemodes/tournament/system/TournamentSwiss.java index 1ecff24af74..b619631c696 100644 --- a/forge-gui/src/main/java/forge/gamemodes/tournament/system/TournamentSwiss.java +++ b/forge-gui/src/main/java/forge/gamemodes/tournament/system/TournamentSwiss.java @@ -1,13 +1,8 @@ package forge.gamemodes.tournament.system; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; +import java.util.*; import com.google.common.collect.Lists; -import forge.util.CollectionUtil; @SuppressWarnings("serial") public class TournamentSwiss extends AbstractTournament { @@ -40,7 +35,7 @@ public class TournamentSwiss extends AbstractTournament { activeRound++; // Randomize players, then sort by scores - CollectionUtil.shuffle(allPlayers); + Collections.shuffle(allPlayers); sortAllPlayers("swiss"); if (allPlayers.size() % 2 == 1) { diff --git a/forge-gui/src/main/java/forge/itemmanager/GroupDef.java b/forge-gui/src/main/java/forge/itemmanager/GroupDef.java index 6238d0f051f..fb921e447c6 100644 --- a/forge-gui/src/main/java/forge/itemmanager/GroupDef.java +++ b/forge-gui/src/main/java/forge/itemmanager/GroupDef.java @@ -15,7 +15,6 @@ import forge.deck.DeckProxy; import forge.item.InventoryItem; import forge.item.PaperCard; import forge.model.FModel; -import forge.util.CollectionUtil; import forge.util.Localizer; public enum GroupDef { @@ -240,7 +239,7 @@ public enum GroupDef { //build sorted list of sets List sortedSets = Lists.newArrayList(FModel.getMagicDb().getEditions()); Collections.sort(sortedSets); - CollectionUtil.reverse(sortedSets); + Collections.reverse(sortedSets); int groupNum = 0; String[] setGroups = new String[sortedSets.size()]; diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 84e833a70bf..73402c00b5a 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -465,7 +465,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { } private PaymentDecision exileFromTopGraveType(final int nNeeded, final CardCollection typeList) { - CollectionUtil.reverse(typeList); + Collections.reverse(typeList); return PaymentDecision.card(Iterables.limit(typeList, nNeeded)); } diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 024bbf7b8d0..b5f64a0fcf9 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -1141,7 +1141,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } endTempShowCards(); if(topOfDeck) - CollectionUtil.reverse(choices); + Collections.reverse(choices); CardCollection result = new CardCollection(); gameCacheMove.addToList(choices, result); return result; @@ -1663,7 +1663,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont Collections.sort(sortedResults); if (!call) { - CollectionUtil.reverse(sortedResults); + Collections.reverse(sortedResults); } return getGui().one(sa.getHostCard().getName() + " - " + localizer.getMessage("lblChooseAResult"), sortedResults).equals(labelsSrc[0]); } From 81777761f50e25c9986d8bba85ffa716553f91ba Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Wed, 20 Nov 2024 22:02:25 +0800 Subject: [PATCH 069/152] add ManaPoolTest --- .../src/test/java/forge/ManaPoolTest.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 forge-gui-desktop/src/test/java/forge/ManaPoolTest.java diff --git a/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java b/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java new file mode 100644 index 00000000000..8c629e71a3a --- /dev/null +++ b/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java @@ -0,0 +1,64 @@ +package forge; + +import forge.ai.simulation.SimulationTest; +import forge.card.MagicColor; +import forge.game.Game; +import forge.game.card.Card; +import forge.game.mana.Mana; +import forge.game.player.Player; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import static org.testng.Assert.assertEquals; + +public class ManaPoolTest extends SimulationTest { + /** + * Just a quick test for ManaPool. + */ + @Test + void testCompletableFuture() { + Game game = initAndCreateGame(); + Player p0 = game.getPlayers().get(0); + Mana m = new Mana(MagicColor.COLORLESS, new Card(1, game), null); + p0.getManaPool().addMana(m, false); + p0.getManaPool().addMana(m, false); + p0.getManaPool().addMana(m, false); + + List> futures = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + futures.add(CompletableFuture.supplyAsync(() -> { + p0.getManaPool().removeMana(m, true); + return 0; + })); + } + CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); + CompletableFuture.allOf(futuresArray).join(); + futures.clear(); + assertEquals(p0.getManaPool().getAmountOfColor(MagicColor.COLORLESS), 1); + } + + @Test + void testCompletableFutureTwo() { + Game game = initAndCreateGame(); + Player p0 = game.getPlayers().get(0); + Player p1 = game.getPlayers().get(1); + Mana m = new Mana(MagicColor.COLORLESS, new Card(1, game), null); + + List> futures = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + futures.add(CompletableFuture.supplyAsync(() -> { + p0.getManaPool().addMana(m, true); + p1.getManaPool().addMana(m, true); + return 0; + })); + } + CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); + CompletableFuture.allOf(futuresArray).join(); + futures.clear(); + assertEquals(p0.getManaPool().getAmountOfColor(MagicColor.COLORLESS), 4); + assertEquals(p1.getManaPool().getAmountOfColor(MagicColor.COLORLESS), 4); + } +} From 0553eb58ef336cefcaef8053b4aba1386c984fe4 Mon Sep 17 00:00:00 2001 From: Chris H Date: Wed, 20 Nov 2024 19:59:37 -0500 Subject: [PATCH 070/152] Temporarily remove flatten to get a release out --- adventure-editor/pom.xml | 2 +- forge-ai/pom.xml | 2 +- forge-core/pom.xml | 2 +- forge-game/pom.xml | 2 +- forge-gui-android/pom.xml | 2 +- forge-gui-desktop/pom.xml | 2 +- forge-gui-ios/pom.xml | 2 +- forge-gui-mobile-dev/pom.xml | 2 +- forge-gui-mobile/pom.xml | 2 +- forge-gui/pom.xml | 2 +- forge-installer/pom.xml | 6 +++++- forge-lda/pom.xml | 2 +- pom.xml | 36 ++++++------------------------------ 13 files changed, 22 insertions(+), 42 deletions(-) diff --git a/adventure-editor/pom.xml b/adventure-editor/pom.xml index 542d52eb86c..147b348fb00 100644 --- a/adventure-editor/pom.xml +++ b/adventure-editor/pom.xml @@ -3,7 +3,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT 4.0.0 diff --git a/forge-ai/pom.xml b/forge-ai/pom.xml index b51460f8ce7..8f9b45944cc 100644 --- a/forge-ai/pom.xml +++ b/forge-ai/pom.xml @@ -6,7 +6,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-ai diff --git a/forge-core/pom.xml b/forge-core/pom.xml index 24740c37a5c..1a3d86310a0 100644 --- a/forge-core/pom.xml +++ b/forge-core/pom.xml @@ -6,7 +6,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-core diff --git a/forge-game/pom.xml b/forge-game/pom.xml index 1810d23cc36..617182db5fa 100644 --- a/forge-game/pom.xml +++ b/forge-game/pom.xml @@ -6,7 +6,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-game diff --git a/forge-gui-android/pom.xml b/forge-gui-android/pom.xml index a12b2bdf31a..9f2260d5e23 100644 --- a/forge-gui-android/pom.xml +++ b/forge-gui-android/pom.xml @@ -21,7 +21,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-gui-android diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml index 6bd211ca954..d06e7d82b78 100644 --- a/forge-gui-desktop/pom.xml +++ b/forge-gui-desktop/pom.xml @@ -4,7 +4,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-gui-desktop diff --git a/forge-gui-ios/pom.xml b/forge-gui-ios/pom.xml index e72343d5356..d95e2a017ac 100644 --- a/forge-gui-ios/pom.xml +++ b/forge-gui-ios/pom.xml @@ -11,7 +11,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-gui-ios diff --git a/forge-gui-mobile-dev/pom.xml b/forge-gui-mobile-dev/pom.xml index bf7c57c2260..6959bff8a68 100644 --- a/forge-gui-mobile-dev/pom.xml +++ b/forge-gui-mobile-dev/pom.xml @@ -9,7 +9,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-gui-mobile-dev diff --git a/forge-gui-mobile/pom.xml b/forge-gui-mobile/pom.xml index 3dd42d225a6..044d08d9015 100644 --- a/forge-gui-mobile/pom.xml +++ b/forge-gui-mobile/pom.xml @@ -4,7 +4,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-gui-mobile diff --git a/forge-gui/pom.xml b/forge-gui/pom.xml index ba97aab0114..9c5fdb822b0 100644 --- a/forge-gui/pom.xml +++ b/forge-gui/pom.xml @@ -4,7 +4,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-gui diff --git a/forge-installer/pom.xml b/forge-installer/pom.xml index f81742660c8..7b31ad028fc 100644 --- a/forge-installer/pom.xml +++ b/forge-installer/pom.xml @@ -4,7 +4,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-installer @@ -285,6 +285,10 @@ ${basedir}/target/${project.build.finalName}.jar jar + + ${basedir}/target/${project.build.finalName}.tar.bz2 + tar.bz2 + diff --git a/forge-lda/pom.xml b/forge-lda/pom.xml index 7cc9377a9d1..a04ffae5d94 100644 --- a/forge-lda/pom.xml +++ b/forge-lda/pom.xml @@ -4,7 +4,7 @@ forge forge - ${revision} + 2.0.00-SNAPSHOT forge-lda diff --git a/pom.xml b/pom.xml index 14ebe1ebca8..b892e84e64f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ forge pom Forge Parent - ${revision} + 2.0.00-SNAPSHOT Forge lets you play the card game Magic: The Gathering against a computer opponent using all of the rules. @@ -30,7 +30,7 @@ -SNAPSHOT - ${versionCode}${snapshotName} + 2.0.00-SNAPSHOT @@ -200,9 +200,10 @@ 3.0.1 true - - .mvn/local-settings.xml - + + .mvn/local-settings.xml + forge-gui/release-files/CHANGES.txt + @@ -331,31 +332,6 @@ - - org.codehaus.mojo - flatten-maven-plugin - 1.6.0 - - true - resolveCiFriendliesOnly - - - - flatten - process-resources - - flatten - - - - flatten.clean - clean - - clean - - - - From ac37ee1947b3755fc105188a4c7203a84e5d8c34 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 21 Nov 2024 01:09:58 +0000 Subject: [PATCH 071/152] [maven-release-plugin] prepare release forge-2.0.00 --- adventure-editor/pom.xml | 2 +- forge-ai/pom.xml | 2 +- forge-core/pom.xml | 2 +- forge-game/pom.xml | 2 +- forge-gui-android/pom.xml | 2 +- forge-gui-desktop/pom.xml | 2 +- forge-gui-ios/pom.xml | 2 +- forge-gui-mobile-dev/pom.xml | 2 +- forge-gui-mobile/pom.xml | 2 +- forge-gui/pom.xml | 2 +- forge-installer/pom.xml | 2 +- forge-lda/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/adventure-editor/pom.xml b/adventure-editor/pom.xml index 147b348fb00..670c0273451 100644 --- a/adventure-editor/pom.xml +++ b/adventure-editor/pom.xml @@ -3,7 +3,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 4.0.0 diff --git a/forge-ai/pom.xml b/forge-ai/pom.xml index 8f9b45944cc..cdb2f070db9 100644 --- a/forge-ai/pom.xml +++ b/forge-ai/pom.xml @@ -6,7 +6,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-ai diff --git a/forge-core/pom.xml b/forge-core/pom.xml index 1a3d86310a0..f990eb3ed5c 100644 --- a/forge-core/pom.xml +++ b/forge-core/pom.xml @@ -6,7 +6,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-core diff --git a/forge-game/pom.xml b/forge-game/pom.xml index 617182db5fa..0cebeb16a5c 100644 --- a/forge-game/pom.xml +++ b/forge-game/pom.xml @@ -6,7 +6,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-game diff --git a/forge-gui-android/pom.xml b/forge-gui-android/pom.xml index 9f2260d5e23..08a54bf488e 100644 --- a/forge-gui-android/pom.xml +++ b/forge-gui-android/pom.xml @@ -21,7 +21,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-gui-android diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml index d06e7d82b78..455dce9d477 100644 --- a/forge-gui-desktop/pom.xml +++ b/forge-gui-desktop/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-gui-desktop diff --git a/forge-gui-ios/pom.xml b/forge-gui-ios/pom.xml index d95e2a017ac..374dc743ac3 100644 --- a/forge-gui-ios/pom.xml +++ b/forge-gui-ios/pom.xml @@ -11,7 +11,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-gui-ios diff --git a/forge-gui-mobile-dev/pom.xml b/forge-gui-mobile-dev/pom.xml index 6959bff8a68..de97bd4e2fe 100644 --- a/forge-gui-mobile-dev/pom.xml +++ b/forge-gui-mobile-dev/pom.xml @@ -9,7 +9,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-gui-mobile-dev diff --git a/forge-gui-mobile/pom.xml b/forge-gui-mobile/pom.xml index 044d08d9015..719bc89403d 100644 --- a/forge-gui-mobile/pom.xml +++ b/forge-gui-mobile/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-gui-mobile diff --git a/forge-gui/pom.xml b/forge-gui/pom.xml index 9c5fdb822b0..984d0759d62 100644 --- a/forge-gui/pom.xml +++ b/forge-gui/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-gui diff --git a/forge-installer/pom.xml b/forge-installer/pom.xml index 7b31ad028fc..a4bf3882036 100644 --- a/forge-installer/pom.xml +++ b/forge-installer/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-installer diff --git a/forge-lda/pom.xml b/forge-lda/pom.xml index a04ffae5d94..5df05f0b380 100644 --- a/forge-lda/pom.xml +++ b/forge-lda/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00-SNAPSHOT + 2.0.00 forge-lda diff --git a/pom.xml b/pom.xml index b892e84e64f..299d31c7b36 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ forge pom Forge Parent - 2.0.00-SNAPSHOT + 2.0.00 Forge lets you play the card game Magic: The Gathering against a computer opponent using all of the rules. @@ -43,7 +43,7 @@ scm:git:https://github.com/Card-Forge/forge.git scm:git:https://github.com/Card-Forge/forge.git - HEAD + forge-2.0.00 From c95afd86d812ca375fa6b560de2d440058f8591f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 21 Nov 2024 01:10:00 +0000 Subject: [PATCH 072/152] [maven-release-plugin] prepare for next development iteration --- adventure-editor/pom.xml | 2 +- forge-ai/pom.xml | 2 +- forge-core/pom.xml | 2 +- forge-game/pom.xml | 2 +- forge-gui-android/pom.xml | 2 +- forge-gui-desktop/pom.xml | 2 +- forge-gui-ios/pom.xml | 2 +- forge-gui-mobile-dev/pom.xml | 2 +- forge-gui-mobile/pom.xml | 2 +- forge-gui/pom.xml | 2 +- forge-installer/pom.xml | 2 +- forge-lda/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/adventure-editor/pom.xml b/adventure-editor/pom.xml index 670c0273451..d16ba19389f 100644 --- a/adventure-editor/pom.xml +++ b/adventure-editor/pom.xml @@ -3,7 +3,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT 4.0.0 diff --git a/forge-ai/pom.xml b/forge-ai/pom.xml index cdb2f070db9..8a66ed3baff 100644 --- a/forge-ai/pom.xml +++ b/forge-ai/pom.xml @@ -6,7 +6,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-ai diff --git a/forge-core/pom.xml b/forge-core/pom.xml index f990eb3ed5c..5963d1f2009 100644 --- a/forge-core/pom.xml +++ b/forge-core/pom.xml @@ -6,7 +6,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-core diff --git a/forge-game/pom.xml b/forge-game/pom.xml index 0cebeb16a5c..7cafceb0044 100644 --- a/forge-game/pom.xml +++ b/forge-game/pom.xml @@ -6,7 +6,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-game diff --git a/forge-gui-android/pom.xml b/forge-gui-android/pom.xml index 08a54bf488e..290d1b970b6 100644 --- a/forge-gui-android/pom.xml +++ b/forge-gui-android/pom.xml @@ -21,7 +21,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-gui-android diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml index 455dce9d477..e91c895018a 100644 --- a/forge-gui-desktop/pom.xml +++ b/forge-gui-desktop/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-gui-desktop diff --git a/forge-gui-ios/pom.xml b/forge-gui-ios/pom.xml index 374dc743ac3..90cbc67320a 100644 --- a/forge-gui-ios/pom.xml +++ b/forge-gui-ios/pom.xml @@ -11,7 +11,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-gui-ios diff --git a/forge-gui-mobile-dev/pom.xml b/forge-gui-mobile-dev/pom.xml index de97bd4e2fe..f4e0f0343df 100644 --- a/forge-gui-mobile-dev/pom.xml +++ b/forge-gui-mobile-dev/pom.xml @@ -9,7 +9,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-gui-mobile-dev diff --git a/forge-gui-mobile/pom.xml b/forge-gui-mobile/pom.xml index 719bc89403d..a3ac845ea87 100644 --- a/forge-gui-mobile/pom.xml +++ b/forge-gui-mobile/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-gui-mobile diff --git a/forge-gui/pom.xml b/forge-gui/pom.xml index 984d0759d62..fc3f6032913 100644 --- a/forge-gui/pom.xml +++ b/forge-gui/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-gui diff --git a/forge-installer/pom.xml b/forge-installer/pom.xml index a4bf3882036..20848c55021 100644 --- a/forge-installer/pom.xml +++ b/forge-installer/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-installer diff --git a/forge-lda/pom.xml b/forge-lda/pom.xml index 5df05f0b380..bea8bfdf730 100644 --- a/forge-lda/pom.xml +++ b/forge-lda/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.00 + 2.0.01-SNAPSHOT forge-lda diff --git a/pom.xml b/pom.xml index 299d31c7b36..a62ab4045d5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ forge pom Forge Parent - 2.0.00 + 2.0.01-SNAPSHOT Forge lets you play the card game Magic: The Gathering against a computer opponent using all of the rules. @@ -43,7 +43,7 @@ scm:git:https://github.com/Card-Forge/forge.git scm:git:https://github.com/Card-Forge/forge.git - forge-2.0.00 + HEAD From 0bc4e5d7b994f0d69040463d04648c321f7b0ac3 Mon Sep 17 00:00:00 2001 From: Chris H Date: Wed, 20 Nov 2024 20:41:23 -0500 Subject: [PATCH 073/152] Restore flatten and version for now --- adventure-editor/pom.xml | 2 +- forge-ai/pom.xml | 2 +- forge-core/pom.xml | 2 +- forge-game/pom.xml | 2 +- forge-gui-android/pom.xml | 2 +- forge-gui-desktop/pom.xml | 2 +- forge-gui-ios/pom.xml | 2 +- forge-gui-mobile-dev/pom.xml | 2 +- forge-gui-mobile/pom.xml | 2 +- forge-gui/pom.xml | 2 +- forge-installer/pom.xml | 2 +- forge-lda/pom.xml | 2 +- pom.xml | 31 ++++++++++++++++++++++++++++--- 13 files changed, 40 insertions(+), 15 deletions(-) diff --git a/adventure-editor/pom.xml b/adventure-editor/pom.xml index d16ba19389f..542d52eb86c 100644 --- a/adventure-editor/pom.xml +++ b/adventure-editor/pom.xml @@ -3,7 +3,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} 4.0.0 diff --git a/forge-ai/pom.xml b/forge-ai/pom.xml index 8a66ed3baff..b51460f8ce7 100644 --- a/forge-ai/pom.xml +++ b/forge-ai/pom.xml @@ -6,7 +6,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-ai diff --git a/forge-core/pom.xml b/forge-core/pom.xml index 5963d1f2009..24740c37a5c 100644 --- a/forge-core/pom.xml +++ b/forge-core/pom.xml @@ -6,7 +6,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-core diff --git a/forge-game/pom.xml b/forge-game/pom.xml index 7cafceb0044..1810d23cc36 100644 --- a/forge-game/pom.xml +++ b/forge-game/pom.xml @@ -6,7 +6,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-game diff --git a/forge-gui-android/pom.xml b/forge-gui-android/pom.xml index 290d1b970b6..a12b2bdf31a 100644 --- a/forge-gui-android/pom.xml +++ b/forge-gui-android/pom.xml @@ -21,7 +21,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-gui-android diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml index e91c895018a..6bd211ca954 100644 --- a/forge-gui-desktop/pom.xml +++ b/forge-gui-desktop/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-gui-desktop diff --git a/forge-gui-ios/pom.xml b/forge-gui-ios/pom.xml index 90cbc67320a..e72343d5356 100644 --- a/forge-gui-ios/pom.xml +++ b/forge-gui-ios/pom.xml @@ -11,7 +11,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-gui-ios diff --git a/forge-gui-mobile-dev/pom.xml b/forge-gui-mobile-dev/pom.xml index f4e0f0343df..bf7c57c2260 100644 --- a/forge-gui-mobile-dev/pom.xml +++ b/forge-gui-mobile-dev/pom.xml @@ -9,7 +9,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-gui-mobile-dev diff --git a/forge-gui-mobile/pom.xml b/forge-gui-mobile/pom.xml index a3ac845ea87..3dd42d225a6 100644 --- a/forge-gui-mobile/pom.xml +++ b/forge-gui-mobile/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-gui-mobile diff --git a/forge-gui/pom.xml b/forge-gui/pom.xml index fc3f6032913..ba97aab0114 100644 --- a/forge-gui/pom.xml +++ b/forge-gui/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-gui diff --git a/forge-installer/pom.xml b/forge-installer/pom.xml index 20848c55021..90ed2806e94 100644 --- a/forge-installer/pom.xml +++ b/forge-installer/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-installer diff --git a/forge-lda/pom.xml b/forge-lda/pom.xml index bea8bfdf730..7cc9377a9d1 100644 --- a/forge-lda/pom.xml +++ b/forge-lda/pom.xml @@ -4,7 +4,7 @@ forge forge - 2.0.01-SNAPSHOT + ${revision} forge-lda diff --git a/pom.xml b/pom.xml index a62ab4045d5..50829826efe 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ forge pom Forge Parent - 2.0.01-SNAPSHOT + ${revision} Forge lets you play the card game Magic: The Gathering against a computer opponent using all of the rules. @@ -26,11 +26,11 @@ src/main/config 17 - 2.0.00 + 2.0.01 -SNAPSHOT - 2.0.00-SNAPSHOT + ${versionCode}${snapshotName} @@ -332,6 +332,31 @@ + + org.codehaus.mojo + flatten-maven-plugin + 1.6.0 + + true + resolveCiFriendliesOnly + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + From dda5afe70480dcd2f6adef5a994b7678c35d8ab0 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 21 Nov 2024 13:33:52 +0800 Subject: [PATCH 074/152] refactor ManaPool move custom multimap implementation for floating mana since this only covers small section --- .../util/collect/ConcurrentMultiMap.java | 135 ------------------ .../main/java/forge/game/mana/ManaPool.java | 108 +++++++++++++- 2 files changed, 102 insertions(+), 141 deletions(-) delete mode 100644 forge-core/src/main/java/forge/util/collect/ConcurrentMultiMap.java diff --git a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiMap.java b/forge-core/src/main/java/forge/util/collect/ConcurrentMultiMap.java deleted file mode 100644 index bae112acebb..00000000000 --- a/forge-core/src/main/java/forge/util/collect/ConcurrentMultiMap.java +++ /dev/null @@ -1,135 +0,0 @@ -package forge.util.collect; - -import com.google.common.collect.ConcurrentHashMultiset; -import com.google.common.collect.Multiset; - -import java.util.Collection; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; - -public class ConcurrentMultiMap { - private Map> _MAP; - private Map> MAP() { - Map> result = _MAP; - if (result == null) { - synchronized (this) { - result = _MAP; - if (result == null) { - result = new ConcurrentHashMap<>(); - _MAP = result; - } - } - } - return _MAP; - } - - public int size() { - return MAP().size(); - } - - - public boolean isEmpty() { - return MAP().isEmpty(); - } - - @SuppressWarnings("SuspiciousMethodCalls") - public boolean containsKey(Object key) { - if (key == null) - return false; - return MAP().containsKey(key); - } - - public boolean put(K key, V value) { - return safeGet(key).add(value); - } - - @SuppressWarnings("SuspiciousMethodCalls") - public boolean remove(Object key, Object value) { - if (key == null || value == null) - return false; - return MAP().get(key).remove(value); - } - - public boolean putAll(K key, Iterable iterable) { - Collection values = safeGet(key); - for (V v : iterable) { - if(!values.add(v)) { - return false; - } - } - return true; - } - - public boolean putAll(Map map) { - for (Map.Entry entry : map.entrySet()) { - if(!safeGet(entry.getKey()).add(entry.getValue())) { - return false; - } - } - return true; - } - - public void clear() { - MAP().clear(); - } - - public Collection safeGet(K key) { - return MAP().computeIfAbsent(key, value -> new ConcurrentLinkedQueue<>()); - } - - public Collection get(K key) { - return MAP().get(key); - } - - public Set keySet() { - return MAP().keySet(); - } - - public Multiset keys() { - Multiset multiset = ConcurrentHashMultiset.create(); - multiset.addAll(MAP().keySet()); - return multiset; - } - - public Collection values() { - Queue values = new ConcurrentLinkedQueue<>(); - for (Map.Entry> entry : MAP().entrySet()) { - values.addAll(entry.getValue()); - } - return values; - } - - /*@SuppressWarnings("SuspiciousMethodCalls") - public boolean containsValue(Object value) { - if (value == null) - return false; - return storage.containsValue(value); - } - - public boolean containsEntry(Object key, Object value) { - if (key == null || value == null) - return false; - return storage.entrySet().contains(Maps.immutableEntry(key, value)); - } - - public Collection replaceValues(K key, Iterable values) { - return null; - } - - public Collection removeAll(Object key) { - if (key == null) - return null; - return storage.containsKey(key) ? storage.remove(key) : null; - } - - public Collection> entries() { - return null; - } - - public Map> asMap() { - return null; - }*/ -} diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index 61678c47e8a..9add3814575 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -22,9 +22,14 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.Set; +import com.google.common.collect.ConcurrentHashMultiset; import com.google.common.collect.Lists; - +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; +import com.google.common.collect.Queues; import forge.card.mana.ManaAtom; import forge.card.mana.ManaCostShard; import forge.game.Game; @@ -39,7 +44,6 @@ import forge.game.replacement.ReplacementType; import forge.game.spellability.AbilityManaPart; import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbilityUnspentMana; -import forge.util.collect.ConcurrentMultiMap; /** *

@@ -51,14 +55,14 @@ import forge.util.collect.ConcurrentMultiMap; */ public class ManaPool extends ManaConversionMatrix implements Iterable { private final Player owner; - private ConcurrentMultiMap _floatingMana; - private ConcurrentMultiMap floatingMana() { - ConcurrentMultiMap result = _floatingMana; + private ManaMultiMap _floatingMana; + private ManaMultiMap floatingMana() { + ManaMultiMap result = _floatingMana; if (result == null) { synchronized (this) { result = _floatingMana; if (result == null) { - result = new ConcurrentMultiMap<>(); + result = new ManaMultiMap<>(); _floatingMana = result; } } @@ -375,4 +379,96 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { return floatingMana().values().iterator(); } + private static class ManaMultiMap { + private Map> _storage; + private Map> storage() { + Map> result = _storage; + if (result == null) { + synchronized (this) { + result = _storage; + if (result == null) { + result = Maps.newConcurrentMap(); + _storage = result; + } + } + } + return _storage; + } + + public int size() { + return storage().size(); + } + + + public boolean isEmpty() { + return storage().isEmpty(); + } + + @SuppressWarnings("SuspiciousMethodCalls") + public boolean containsKey(Object key) { + if (key == null) + return false; + return storage().containsKey(key); + } + + public boolean put(K key, V value) { + return safeGet(key).add(value); + } + + @SuppressWarnings("SuspiciousMethodCalls") + public boolean remove(Object key, Object value) { + if (key == null || value == null) + return false; + return storage().get(key).remove(value); + } + + public boolean putAll(K key, Iterable iterable) { + Collection values = safeGet(key); + for (V v : iterable) { + if(!values.add(v)) { + return false; + } + } + return true; + } + + public boolean putAll(Map map) { + for (Map.Entry entry : map.entrySet()) { + if(!safeGet(entry.getKey()).add(entry.getValue())) { + return false; + } + } + return true; + } + + public void clear() { + storage().clear(); + } + + public Collection safeGet(K key) { + return storage().computeIfAbsent(key, value -> Queues.newConcurrentLinkedQueue()); + } + + public Collection get(K key) { + return storage().get(key); + } + + public Set keySet() { + return storage().keySet(); + } + + public Multiset keys() { + Multiset multiset = ConcurrentHashMultiset.create(); + multiset.addAll(storage().keySet()); + return multiset; + } + + public Collection values() { + Queue values = Queues.newConcurrentLinkedQueue(); + for (Map.Entry> entry : storage().entrySet()) { + values.addAll(entry.getValue()); + } + return values; + } + } } From 259247c8427f86be02b04f3a6e06461298742b9f Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 21 Nov 2024 14:37:32 +0800 Subject: [PATCH 075/152] update ManaMultiMap clear update ManaPoolTest --- .../main/java/forge/game/mana/ManaPool.java | 3 +++ .../src/test/java/forge/FCollectionTest.java | 2 +- .../src/test/java/forge/ManaPoolTest.java | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index 9add3814575..0b923ad4f12 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -442,6 +442,9 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { } public void clear() { + for (Map.Entry> entry : storage().entrySet()) { + entry.getValue().clear(); + } storage().clear(); } diff --git a/forge-gui-desktop/src/test/java/forge/FCollectionTest.java b/forge-gui-desktop/src/test/java/forge/FCollectionTest.java index fd3a5863385..5d88377a5f2 100644 --- a/forge-gui-desktop/src/test/java/forge/FCollectionTest.java +++ b/forge-gui-desktop/src/test/java/forge/FCollectionTest.java @@ -36,7 +36,7 @@ public class FCollectionTest { int i = 0; for (Card c : cc) { if (i != 3) - cc.remove(c); + cc.remove(c); // throws error if the CardCollection not threadsafe i++; } assertEquals(cc.size(), 1); diff --git a/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java b/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java index 8c629e71a3a..3804c0fc958 100644 --- a/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java +++ b/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java @@ -19,6 +19,26 @@ public class ManaPoolTest extends SimulationTest { * Just a quick test for ManaPool. */ @Test + void testManaPoolBadLogic() { + Game game = initAndCreateGame(); + Player p0 = game.getPlayers().get(0); + Player p1 = game.getPlayers().get(1); + Mana w = new Mana(MagicColor.WHITE, new Card(1, game), null); + Mana b = new Mana(MagicColor.BLACK, new Card(1, game), null); + p0.getManaPool().addMana(w, false); + p0.getManaPool().addMana(w, false); + p0.getManaPool().addMana(w, false); + p1.getManaPool().addMana(b, false); + p1.getManaPool().addMana(b, false); + p1.getManaPool().resetPool(); // empty manapool, should clear all values + for (Mana m : p0.getManaPool()) { + p1.getManaPool().addMana(m, false); + p0.getManaPool().removeMana(m, false); // throws error if ManaPool is not threadsafe + } + assertEquals(p0.getManaPool().getAmountOfColor(MagicColor.WHITE), 0); + assertEquals(p1.getManaPool().getAmountOfColor(MagicColor.WHITE), 3); + } + @Test void testCompletableFuture() { Game game = initAndCreateGame(); Player p0 = game.getPlayers().get(0); From 3d42da96d4848b8608e14c5b4b68ad2cd10b81c7 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Thu, 21 Nov 2024 16:10:20 +0800 Subject: [PATCH 076/152] add persistentmana check --- .../src/main/java/forge/game/mana/ManaPool.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index 0b923ad4f12..a69079cf21d 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -119,9 +119,16 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { safeMana += getAmountOfColor(c); } - // TODO isPersistentMana + boolean hasPersistentMana = false; + for (Mana m : floatingMana().values()) { + AbilityManaPart mp = m.getManaAbility(); + if (mp != null && mp.isPersistentMana()) { + hasPersistentMana = true; + break; + } + } - return totalMana() != safeMana; //won't lose floating mana if all mana is of colors that aren't going to be emptied + return hasPersistentMana || totalMana() != safeMana; //won't lose floating mana if all mana is of colors that aren't going to be emptied } public final boolean hasBurn() { From 5fa9f5b50e325346c8e44b041df8c05584a36af9 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Thu, 21 Nov 2024 17:58:13 +0100 Subject: [PATCH 077/152] Fix Kentaro (#6600) --- .../main/java/forge/card/mana/ManaCost.java | 2 +- .../game/ability/effects/CounterEffect.java | 5 +++ .../forge/game/mana/ManaCostBeingPaid.java | 2 +- .../main/java/forge/game/player/Player.java | 43 ++++++++++--------- .../game/replacement/ReplacementHandler.java | 9 ---- .../cardsfolder/a/archon_of_coronation.txt | 2 +- forge-gui/res/cardsfolder/c/complicate.txt | 2 +- forge-gui/res/cardsfolder/m/mystic_remora.txt | 4 +- .../res/cardsfolder/p/paradise_druid.txt | 4 +- .../res/cardsfolder/p/plumb_the_forbidden.txt | 2 +- .../res/cardsfolder/s/simic_basilisk.txt | 2 +- .../res/cardsfolder/s/sloppity_bilepiper.txt | 2 +- 12 files changed, 39 insertions(+), 40 deletions(-) diff --git a/forge-core/src/main/java/forge/card/mana/ManaCost.java b/forge-core/src/main/java/forge/card/mana/ManaCost.java index fdfa28734c2..6773fb8ad75 100644 --- a/forge-core/src/main/java/forge/card/mana/ManaCost.java +++ b/forge-core/src/main/java/forge/card/mana/ManaCost.java @@ -100,7 +100,7 @@ public final class ManaCost implements Comparable, Iterable { return drawn; } - // Replacement effects - final Map repRunParams = AbilityKey.mapFromAffected(this); - repRunParams.put(AbilityKey.Number, n); - if (params != null) { - repRunParams.putAll(params); - } - - if (game.getReplacementHandler().run(ReplacementType.DrawCards, repRunParams) != ReplacementResult.NotReplaced) { - return drawn; - } - // always allow drawing cards before the game actually starts (e.g. Maralen of the Mornsong Avatar) final boolean gameStarted = game.getAge().ordinal() > GameStage.Mulligan.ordinal(); + + if (gameStarted) { + final Map repRunParams = AbilityKey.mapFromAffected(this); + repRunParams.put(AbilityKey.Number, n); + if (params != null) { + repRunParams.putAll(params); + } + if (game.getReplacementHandler().run(ReplacementType.DrawCards, repRunParams) != ReplacementResult.NotReplaced) { + return drawn; + } + } + final Map toReveal = Maps.newHashMap(); for (int i = 0; i < n; i++) { @@ -1249,14 +1250,17 @@ public class Player extends GameEntity implements Comparable { cause = (SpellAbility) cause.getReplacingObject(AbilityKey.Cause); } - // Replacement effects - Map repParams = AbilityKey.mapFromAffected(this); - repParams.put(AbilityKey.Cause, cause); - if (params != null) { - repParams.putAll(params); - } - if (game.getReplacementHandler().run(ReplacementType.Draw, repParams) != ReplacementResult.NotReplaced) { - return drawn; + final boolean gameStarted = game.getAge().ordinal() > GameStage.Mulligan.ordinal(); + + if (gameStarted) { + Map repParams = AbilityKey.mapFromAffected(this); + repParams.put(AbilityKey.Cause, cause); + if (params != null) { + repParams.putAll(params); + } + if (game.getReplacementHandler().run(ReplacementType.Draw, repParams) != ReplacementResult.NotReplaced) { + return drawn; + } } if (!library.isEmpty()) { @@ -1291,7 +1295,6 @@ public class Player extends GameEntity implements Comparable { revealed.get(p).add(c); } - final boolean gameStarted = game.getAge().ordinal() > GameStage.Mulligan.ordinal(); if (gameStarted) { setLastDrawnCard(c); c.setDrawnThisTurn(true); diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index 75262669cfe..0146bf1390d 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -71,8 +71,6 @@ public class ReplacementHandler { game = gameState; } - //private final List tmpEffects = new ArrayList(); - public List getReplacementList(final ReplacementType event, final Map runParams, final ReplacementLayer layer) { final CardCollection preList = new CardCollection(); boolean checkAgain = false; @@ -108,13 +106,6 @@ public class ReplacementHandler { } final List possibleReplacers = Lists.newArrayList(); - // Round up Non-static replacement effects ("Until EOT," or - // "The next time you would..." etc) - /*for (final ReplacementEffect replacementEffect : this.tmpEffects) { - if (!replacementEffect.hasRun() && replacementEffect.canReplace(runParams) && replacementEffect.getLayer() == layer) { - possibleReplacers.add(replacementEffect); - } - }*/ // Round up Static replacement effects game.forEachCardInGame(new Visitor() { diff --git a/forge-gui/res/cardsfolder/a/archon_of_coronation.txt b/forge-gui/res/cardsfolder/a/archon_of_coronation.txt index a21b851b094..9e0f62891a7 100644 --- a/forge-gui/res/cardsfolder/a/archon_of_coronation.txt +++ b/forge-gui/res/cardsfolder/a/archon_of_coronation.txt @@ -5,5 +5,5 @@ PT:5/5 K:Flying T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMonarch | TriggerDescription$ When CARDNAME enters, you become the monarch. SVar:TrigMonarch:DB$ BecomeMonarch | Defined$ You -R:Event$ LifeReduced | ValidPlayer$ You | IsDamage$ True | Monarch$ True | Layer$ CantHappen | Description$ As long as you're the monarch, damage doesn't cause you to lose life. (When a creature deals combat damage to you, its controller still becomes the monarch.) +R:Event$ LifeReduced | ValidPlayer$ You | IsDamage$ True | Monarch$ True | Layer$ CantHappen | ActiveZones$ Battlefield | Description$ As long as you're the monarch, damage doesn't cause you to lose life. (When a creature deals combat damage to you, its controller still becomes the monarch.) Oracle:Flying\nWhen Archon of Coronation enters, you become the monarch.\nAs long as you're the monarch, damage doesn't cause you to lose life. (When a creature deals combat damage to you, its controller still becomes the monarch.) diff --git a/forge-gui/res/cardsfolder/c/complicate.txt b/forge-gui/res/cardsfolder/c/complicate.txt index 6c363acff0e..8e5d6d3bb41 100644 --- a/forge-gui/res/cardsfolder/c/complicate.txt +++ b/forge-gui/res/cardsfolder/c/complicate.txt @@ -4,5 +4,5 @@ Types:Instant K:Cycling:2 U A:SP$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | UnlessCost$ 3 | SpellDescription$ Counter target spell unless its controller pays {3}. T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When you cycle CARDNAME, you may counter target spell unless its controller pays {1}. -SVar:TrigExile:DB$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | UnlessCost$ 1 +SVar:TrigExile:DB$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | Optional$ True | UnlessCost$ 1 Oracle:Counter target spell unless its controller pays {3}.\nCycling {2}{U} ({2}{U}, Discard this card: Draw a card.)\nWhen you cycle Complicate, you may counter target spell unless its controller pays {1}. diff --git a/forge-gui/res/cardsfolder/m/mystic_remora.txt b/forge-gui/res/cardsfolder/m/mystic_remora.txt index aa994d34814..210d969f7d5 100644 --- a/forge-gui/res/cardsfolder/m/mystic_remora.txt +++ b/forge-gui/res/cardsfolder/m/mystic_remora.txt @@ -2,7 +2,7 @@ Name:Mystic Remora ManaCost:U Types:Enchantment K:Cumulative upkeep:1 -T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ Opponent | TriggerZones$ Battlefield | Execute$ TrigDraw | OptionalDecider$ You | TriggerDescription$ Whenever an opponent casts a noncreature spell, you may draw a card unless that player pays {4}. -SVar:TrigDraw:DB$ Draw | Defined$ You | UnlessCost$ 4 | UnlessPayer$ TriggeredPlayer | NumCards$ 1 +T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ Opponent | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever an opponent casts a noncreature spell, you may draw a card unless that player pays {4}. +SVar:TrigDraw:DB$ Draw | Defined$ You | UnlessCost$ 4 | UnlessPayer$ TriggeredPlayer | NumCards$ 1 | OptionalDecider$ You AI:RemoveDeck:Random Oracle:Cumulative upkeep {1} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nWhenever an opponent casts a noncreature spell, you may draw a card unless that player pays {4}. diff --git a/forge-gui/res/cardsfolder/p/paradise_druid.txt b/forge-gui/res/cardsfolder/p/paradise_druid.txt index 08e4976b504..bfffe761022 100644 --- a/forge-gui/res/cardsfolder/p/paradise_druid.txt +++ b/forge-gui/res/cardsfolder/p/paradise_druid.txt @@ -2,6 +2,6 @@ Name:Paradise Druid ManaCost:1 G Types:Creature Elf Druid PT:2/1 -S:Mode$ Continuous | Affected$ Card.Self+untapped | AddKeyword$ Hexproof | Description$ CARDNAME has hexproof as long as it's untapped.(It can't be the target of spells or abilities your opponents control.) +S:Mode$ Continuous | Affected$ Card.Self+untapped | AddKeyword$ Hexproof | Description$ CARDNAME has hexproof as long as it's untapped. (It can't be the target of spells or abilities your opponents control.) A:AB$ Mana | Cost$ T | Produced$ Any | SpellDescription$ Add one mana of any color. -Oracle:Paradise Druid has hexproof as long as it's untapped.(It can't be the target of spells or abilities your opponents control.)\n{T}: Add one mana of any color. +Oracle:Paradise Druid has hexproof as long as it's untapped. (It can't be the target of spells or abilities your opponents control.)\n{T}: Add one mana of any color. diff --git a/forge-gui/res/cardsfolder/p/plumb_the_forbidden.txt b/forge-gui/res/cardsfolder/p/plumb_the_forbidden.txt index bd9acfef136..7efc206584d 100644 --- a/forge-gui/res/cardsfolder/p/plumb_the_forbidden.txt +++ b/forge-gui/res/cardsfolder/p/plumb_the_forbidden.txt @@ -1,7 +1,7 @@ Name:Plumb the Forbidden ManaCost:1 B Types:Instant -T:Mode$ SpellCast | ValidCard$ Card.Self | Execute$ TrigCopy | CheckSVar$ X | TriggerDescription$ As an additional cost to cast this spell, you may sacrifice one or more creatures. When you do, copy this spell for each creature sacrificed this way. +T:Mode$ SpellCast | ValidCard$ Card.Self | Execute$ TrigCopy | CheckSVar$ CastSA>Count$xPaid | TriggerDescription$ As an additional cost to cast this spell, you may sacrifice one or more creatures. When you do, copy this spell for each creature sacrificed this way. SVar:TrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Amount$ X A:SP$ Draw | Cost$ 1 B Sac | SubAbility$ DBLoseLife | CostDesc$ | SpellDescription$ You draw a card and you lose 1 life. SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 1 diff --git a/forge-gui/res/cardsfolder/s/simic_basilisk.txt b/forge-gui/res/cardsfolder/s/simic_basilisk.txt index 035c0546e58..5e4ea338f5c 100644 --- a/forge-gui/res/cardsfolder/s/simic_basilisk.txt +++ b/forge-gui/res/cardsfolder/s/simic_basilisk.txt @@ -4,7 +4,7 @@ Types:Creature Basilisk Mutant PT:0/0 K:Graft:3 A:AB$ Animate | Cost$ 1 G | ValidTgts$ Creature.counters_GE1_P1P1 | TgtPrompt$ Select target creature with a +1/+1 counter on it | Triggers$ DestroyTrigger | SpellDescription$ Until end of turn, target creature with a +1/+1 counter on it gains "Whenever this creature deals combat damage to a creature, destroy that creature at end of combat." -SVar:DestroyTrigger:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Creature | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ DelTrig | TriggerDescription$ Whenever CARDNAME deals combat damage to a creature, destroy that creature at end of combat. +SVar:DestroyTrigger:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Creature | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ DelTrigSimic | TriggerDescription$ Whenever CARDNAME deals combat damage to a creature, destroy that creature at end of combat. SVar:DelTrigSimic:DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | ValidPlayer$ Player | Execute$ TrigDestroySimic | RememberObjects$ TriggeredTargetLKICopy | TriggerDescription$ Destroy damaged creature at end of combat. SVar:TrigDestroySimic:DB$ Destroy | Defined$ DelayTriggerRememberedLKI SVar:AIGraftPreference:DontMoveCounterIfLethal diff --git a/forge-gui/res/cardsfolder/s/sloppity_bilepiper.txt b/forge-gui/res/cardsfolder/s/sloppity_bilepiper.txt index bb3c708347b..65a3ddb3772 100644 --- a/forge-gui/res/cardsfolder/s/sloppity_bilepiper.txt +++ b/forge-gui/res/cardsfolder/s/sloppity_bilepiper.txt @@ -3,7 +3,7 @@ ManaCost:3 B Types:Creature Demon PT:3/3 A:AB$ Effect | PrecostDesc$ Jolly Gutpipes — | Cost$ 2 T Sac<1/Creature> | StaticAbilities$ GrantCascade | Triggers$ ExileEffect | SpellDescription$ The next creature spell you cast this turn has cascade. (When you cast your next creature spell, exile cards from the top of your library until you exile a nonland card that costs less. You may cast it without paying its mana cost. Put the exiled cards on the bottom of your library in a random order.) -SVar:GrantCascade:Mode$ Continuous | EffectZone$ Command | Affected$ Card.Creature+YouCtrl | AffectedZone$ Stack | Execute$ ExileEff | AddKeyword$ Cascade | Description$ The next noncreature spell you cast this turn has cascade. +SVar:GrantCascade:Mode$ Continuous | EffectZone$ Command | Affected$ Card.Creature+YouCtrl | AffectedZone$ Stack | AddKeyword$ Cascade | Description$ The next noncreature spell you cast this turn has cascade. SVar:ExileEffect:Mode$ SpellCast | EffectZone$ Command | ValidCard$ Card.Creature+YouCtrl | Execute$ RemoveEffect | Static$ True SVar:RemoveEffect:DB$ ChangeZone | Origin$ Command | Destination$ Exile | Defined$ Self DeckHas:Keyword$Cascade & Ability$Sacrifice From 6f59c0d2ce4b2162939d334af7c2309828ce4ac1 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 22 Nov 2024 05:58:49 +0800 Subject: [PATCH 078/152] prevent NPE --- forge-gui-mobile/src/forge/card/CardZoom.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/forge-gui-mobile/src/forge/card/CardZoom.java b/forge-gui-mobile/src/forge/card/CardZoom.java index 7060e870273..2e9155c8651 100644 --- a/forge-gui-mobile/src/forge/card/CardZoom.java +++ b/forge-gui-mobile/src/forge/card/CardZoom.java @@ -377,9 +377,11 @@ public class CardZoom extends FOverlay { float x = (w - cardWidth) / 2; y = (h - cardHeight) / 2; if (zoomMode) { - CardImageRenderer.drawZoom(g, currentCard, gameView, showBackSide? showBackSide : showAltState, x, y, cardWidth, cardHeight, getWidth(), getHeight(), true); + if (currentCard != null) + CardImageRenderer.drawZoom(g, currentCard, gameView, showBackSide? showBackSide : showAltState, x, y, cardWidth, cardHeight, getWidth(), getHeight(), true); } else { - CardImageRenderer.drawDetails(g, currentCard, gameView, showBackSide ? showBackSide : showAltState, x, y, cardWidth, cardHeight); + if (currentCard != null) + CardImageRenderer.drawDetails(g, currentCard, gameView, showBackSide ? showBackSide : showAltState, x, y, cardWidth, cardHeight); } if (!showMerged) { From 29ce2e01fd9680372078353a20976f86d3519bdc Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 22 Nov 2024 06:04:25 +0800 Subject: [PATCH 079/152] Update ImageView.java --- .../src/main/java/forge/itemmanager/views/ImageView.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/views/ImageView.java b/forge-gui-desktop/src/main/java/forge/itemmanager/views/ImageView.java index e01969bbeef..0136367ac69 100644 --- a/forge-gui-desktop/src/main/java/forge/itemmanager/views/ImageView.java +++ b/forge-gui-desktop/src/main/java/forge/itemmanager/views/ImageView.java @@ -591,10 +591,12 @@ public class ImageView extends ItemView { Map, Pile> piles = new TreeMap<>(); for (ItemInfo itemInfo : group.items) { Comparable key = groupPileBy.fnSort.apply(itemInfo); - if (!piles.containsKey(key)) { + if (key != null && !piles.containsKey(key)) { piles.put(key, new Pile()); } - piles.get(key).items.add(itemInfo); + Pile p = key == null ? null : piles.getOrDefault(key, null); + if (p != null) + p.items.add(itemInfo); } group.piles.clear(); group.piles.addAll(piles.values()); From 2bf67e931e897bd780b028b3227f58244ddaf960 Mon Sep 17 00:00:00 2001 From: Chris H Date: Thu, 21 Nov 2024 22:15:51 -0500 Subject: [PATCH 080/152] Update remove-stale-branches.yml --- .github/workflows/remove-stale-branches.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/remove-stale-branches.yml b/.github/workflows/remove-stale-branches.yml index 8f06accdb9d..564c709f28a 100644 --- a/.github/workflows/remove-stale-branches.yml +++ b/.github/workflows/remove-stale-branches.yml @@ -1,4 +1,7 @@ +name: Remove stale branches + on: + workflow_dispatch: schedule: - cron: "0 0 * * *" # Everday at midnight @@ -10,3 +13,4 @@ jobs: - uses: fpicalausa/remove-stale-branches@v1.6.0 with: dry-run: true # Check out the console output before setting this to false + ignore-unknown-authors: true From 4436a83cc558bb83c14d4b39b6ce57aca2aba098 Mon Sep 17 00:00:00 2001 From: Chris H Date: Thu, 21 Nov 2024 22:20:21 -0500 Subject: [PATCH 081/152] Update remove-stale-branches.yml --- .github/workflows/remove-stale-branches.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/remove-stale-branches.yml b/.github/workflows/remove-stale-branches.yml index 564c709f28a..78933088d37 100644 --- a/.github/workflows/remove-stale-branches.yml +++ b/.github/workflows/remove-stale-branches.yml @@ -14,3 +14,4 @@ jobs: with: dry-run: true # Check out the console output before setting this to false ignore-unknown-authors: true + default-recipient: tehdiplomat From 03b87003ef6bf8733f33db368ba1ee1e693af89f Mon Sep 17 00:00:00 2001 From: Chris H Date: Thu, 21 Nov 2024 22:22:32 -0500 Subject: [PATCH 082/152] Update remove-stale-branches.yml --- .github/workflows/remove-stale-branches.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/remove-stale-branches.yml b/.github/workflows/remove-stale-branches.yml index 78933088d37..40a68cf2307 100644 --- a/.github/workflows/remove-stale-branches.yml +++ b/.github/workflows/remove-stale-branches.yml @@ -12,6 +12,6 @@ jobs: steps: - uses: fpicalausa/remove-stale-branches@v1.6.0 with: - dry-run: true # Check out the console output before setting this to false + dry-run: false # Check out the console output before setting this to false ignore-unknown-authors: true default-recipient: tehdiplomat From afb062e770723756b2717b5ce48a80bb2ad031cf Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 22 Nov 2024 12:44:51 +0800 Subject: [PATCH 083/152] revert ManaPool remove ManaPoolTest for concurrency refactor timeout for chooseSpellAbilityToPlayFromList --- .../src/main/java/forge/ai/AiController.java | 68 +++---- .../main/java/forge/game/mana/ManaPool.java | 179 +++--------------- .../src/test/java/forge/ManaPoolTest.java | 84 -------- 3 files changed, 58 insertions(+), 273 deletions(-) delete mode 100644 forge-gui-desktop/src/test/java/forge/ManaPoolTest.java diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 31fe79a7f8c..6b4b5022a9d 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -71,7 +71,7 @@ import io.sentry.Sentry; import java.util.*; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** @@ -1604,15 +1604,22 @@ public class AiController { if (all == null || all.isEmpty()) return null; - //avoid ComputerUtil.aiLifeInDanger in loops as it slows down a lot.. call this outside loops will generally be fast... - boolean isLifeInDanger = useLivingEnd && ComputerUtil.aiLifeInDanger(player, true, 0); - List> futures = new ArrayList<>(); - Queue spells = new ConcurrentLinkedQueue<>(); - for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) { - futures.add(CompletableFuture.supplyAsync(()-> { + try { + all.sort(ComputerUtilAbility.saEvaluator); // put best spells first + ComputerUtilAbility.sortCreatureSpells(all); + } catch (IllegalArgumentException ex) { + System.err.println(ex.getMessage()); + String assertex = ComparatorUtil.verifyTransitivity(ComputerUtilAbility.saEvaluator, all); + Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex); + } + + CompletableFuture future = CompletableFuture.supplyAsync(() -> { + //avoid ComputerUtil.aiLifeInDanger in loops as it slows down a lot.. call this outside loops will generally be fast... + boolean isLifeInDanger = useLivingEnd && ComputerUtil.aiLifeInDanger(player, true, 0); + for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) { // Don't add Counterspells to the "normal" playcard lookups if (skipCounter && sa.getApi() == ApiType.Counter) { - return 0; + continue; } if (sa.getHostCard().hasKeyword(Keyword.STORM) @@ -1620,7 +1627,7 @@ public class AiController { && player.getZone(ZoneType.Hand).contains(Predicates.not(Predicates.or(CardPredicates.Presets.LANDS, CardPredicates.hasKeyword("Storm"))))) { if (game.getView().getStormCount() < this.getIntProperty(AiProps.MIN_COUNT_FOR_STORM_SPELLS)) { // skip evaluating Storm unless we reached the minimum Storm count - return 0; + continue; } } // living end AI decks @@ -1642,20 +1649,15 @@ public class AiController { aiPlayDecision = player.getCreaturesInPlay().size() >= 4 ? AiPlayDecision.CantPlaySa : AiPlayDecision.WillPlay; } else if (CardLists.filter(player.getZone(ZoneType.Graveyard).getCards(), CardPredicates.Presets.CREATURES).size() > 4) { if (player.getCreaturesInPlay().size() >= 4) // it's good minimum - return 0; + continue; else if (!sa.getHostCard().isPermanent() && sa.canCastTiming(player) && ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) aiPlayDecision = AiPlayDecision.WillPlay;// needs tuneup for bad matchups like reanimator and other things to check on opponent graveyard } else { - return 0; + continue; } } } - // dumb check needed? don't really know why the AI tapped the land for nothing at EOT of his opponent. - // without this, you can see the weird tapping of single land -> mana ability. with this, it doesn't confuse human player - if (sa.isManaAbility() && game.getPhaseHandler() != null && game.getPhaseHandler().is(PhaseType.END_OF_TURN) && !game.getPhaseHandler().isPlayerTurn(player)) - return 0; - sa.setActivatingPlayer(player, true); SpellAbility root = sa.getRootAbility(); @@ -1672,32 +1674,20 @@ public class AiController { // System.out.printf("Ai thinks '%s' of %s -> %s @ %s %s >>> \n", opinion, sa.getHostCard(), sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase()); if (opinion != AiPlayDecision.WillPlay) - return 0; + continue; - spells.add(sa); - return 0; - })); - } - //timeout 5 seconds? even the AI don't acquire all, there should be SA to cast if valid - CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); - CompletableFuture.allOf(futuresArray).completeOnTimeout(null, player.getTimeout(), TimeUnit.SECONDS).join(); - futures.clear(); - - if (!spells.isEmpty()) { - List spellAbilities = new ArrayList<>(spells); - if (spellAbilities.size() == 1) - return spellAbilities.get(0); - try { - spellAbilities.sort(ComputerUtilAbility.saEvaluator); // put best spells first - ComputerUtilAbility.sortCreatureSpells(spellAbilities); - } catch (IllegalArgumentException ex) { - System.err.println(ex.getMessage()); - String assertex = ComparatorUtil.verifyTransitivity(ComputerUtilAbility.saEvaluator, spellAbilities); - Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex); + return sa; } - return spellAbilities.get(0); + + return null; + }); + + // instead of computing all available concurrently just add a simple timeout depending on the user prefs + try { + return future.completeOnTimeout(null, player.getTimeout(), TimeUnit.SECONDS).get(); + } catch (InterruptedException | ExecutionException e) { + return null; } - return null; } public CardCollection chooseCardsToDelve(int genericCost, CardCollection grave) { diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index a69079cf21d..949202bafd8 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -22,14 +22,10 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Queue; -import java.util.Set; -import com.google.common.collect.ConcurrentHashMultiset; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multiset; -import com.google.common.collect.Queues; + import forge.card.mana.ManaAtom; import forge.card.mana.ManaCostShard; import forge.game.Game; @@ -55,20 +51,7 @@ import forge.game.staticability.StaticAbilityUnspentMana; */ public class ManaPool extends ManaConversionMatrix implements Iterable { private final Player owner; - private ManaMultiMap _floatingMana; - private ManaMultiMap floatingMana() { - ManaMultiMap result = _floatingMana; - if (result == null) { - synchronized (this) { - result = _floatingMana; - if (result == null) { - result = new ManaMultiMap<>(); - _floatingMana = result; - } - } - } - return _floatingMana; - } + private final ArrayListMultimap floatingMana = ArrayListMultimap.create(); public ManaPool(final Player player) { owner = player; @@ -76,7 +59,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { } public final int getAmountOfColor(final byte color) { - Collection ofColor = floatingMana().get(color); + Collection ofColor = floatingMana.get(color); return ofColor == null ? 0 : ofColor.size(); } @@ -84,7 +67,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { addMana(mana, true); } public void addMana(final Mana mana, boolean updateView) { - floatingMana().put(mana.getColor(), mana); + floatingMana.put(mana.getColor(), mana); if (updateView) { owner.updateManaForView(); owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Added, mana)); @@ -105,7 +88,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { *

*/ public final boolean willManaBeLostAtEndOfPhase() { - if (floatingMana().isEmpty()) { + if (floatingMana.isEmpty()) { return false; } @@ -119,16 +102,9 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { safeMana += getAmountOfColor(c); } - boolean hasPersistentMana = false; - for (Mana m : floatingMana().values()) { - AbilityManaPart mp = m.getManaAbility(); - if (mp != null && mp.isPersistentMana()) { - hasPersistentMana = true; - break; - } - } + // TODO isPersistentMana - return hasPersistentMana || totalMana() != safeMana; //won't lose floating mana if all mana is of colors that aren't going to be emptied + return totalMana() != safeMana; //won't lose floating mana if all mana is of colors that aren't going to be emptied } public final boolean hasBurn() { @@ -138,13 +114,13 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { public final void resetPool() { // This should only be used to reset the pool to empty by things like restores. - floatingMana().clear(); + floatingMana.clear(); } public final List clearPool(boolean isEndOfPhase) { // isEndOfPhase parameter: true = end of phase, false = mana drain effect List cleared = Lists.newArrayList(); - if (floatingMana().isEmpty()) { return cleared; } + if (floatingMana.isEmpty()) { return cleared; } Byte convertTo = null; @@ -152,17 +128,17 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { final Map runParams = AbilityKey.mapFromAffected(owner); runParams.put(AbilityKey.Mana, "C"); switch (owner.getGame().getReplacementHandler().run(ReplacementType.LoseMana, runParams)) { - case NotReplaced: - break; - case Skipped: - return cleared; - default: - convertTo = ManaAtom.fromName((String) runParams.get(AbilityKey.Mana)); - break; + case NotReplaced: + break; + case Skipped: + return cleared; + default: + convertTo = ManaAtom.fromName((String) runParams.get(AbilityKey.Mana)); + break; } - final List keys = Lists.newArrayList(floatingMana().keySet()); + final List keys = Lists.newArrayList(floatingMana.keySet()); if (isEndOfPhase) { keys.removeAll(StaticAbilityUnspentMana.getManaToKeep(owner)); } @@ -171,7 +147,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { } for (Byte b : keys) { - Collection cm = floatingMana().get(b); + Collection cm = floatingMana.get(b); final List pMana = Lists.newArrayList(); if (isEndOfPhase && !owner.getGame().getPhaseHandler().is(PhaseType.CLEANUP)) { for (final Mana mana : cm) { @@ -187,7 +163,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { } else { cleared.addAll(cm); cm.clear(); - floatingMana().putAll(b, pMana); + floatingMana.putAll(b, pMana); } } @@ -198,12 +174,12 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { private void convertManaColor(final byte originalColor, final byte toColor) { List convert = Lists.newArrayList(); - Collection cm = floatingMana().get(originalColor); + Collection cm = floatingMana.get(originalColor); for (Mana m : cm) { convert.add(new Mana(toColor, m.getSourceCard(), m.getManaAbility())); } cm.clear(); - floatingMana().putAll(toColor, convert); + floatingMana.putAll(toColor, convert); owner.updateManaForView(); } @@ -211,7 +187,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { return removeMana(mana, true); } public boolean removeMana(final Mana mana, boolean updateView) { - boolean result = floatingMana().remove(mana.getColor(), mana); + boolean result = floatingMana.remove(mana.getColor(), mana); if (result && updateView) { owner.updateManaForView(); owner.getGame().fireEvent(new GameEventManaPool(owner, EventValueChangeType.Removed, mana)); @@ -240,10 +216,8 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { public boolean tryPayCostWithColor(byte colorCode, SpellAbility saPaidFor, ManaCostBeingPaid manaCost, List manaSpentToPay) { Mana manaFound = null; - Collection cm = floatingMana().get(colorCode); + Collection cm = floatingMana.get(colorCode); - if (cm == null) - return false; for (final Mana mana : cm) { if (mana.getManaAbility() != null && !mana.getManaAbility().meetsManaRestrictions(saPaidFor)) { continue; @@ -279,11 +253,11 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { } public final boolean isEmpty() { - return floatingMana().isEmpty(); + return floatingMana.isEmpty(); } public final int totalMana() { - return floatingMana().values().size(); + return floatingMana.values().size(); } //Account for mana part of ability when undoing it @@ -291,7 +265,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { if (ma == null) { return false; } - if (floatingMana().isEmpty()) { + if (floatingMana.isEmpty()) { return false; } @@ -300,7 +274,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { boolean manaNotAccountedFor = false; // loop over mana produced by mana ability for (Mana mana : ma.getLastManaProduced()) { - Collection poolLane = floatingMana().get(mana.getColor()); + Collection poolLane = floatingMana.get(mana.getColor()); if (poolLane != null && poolLane.contains(mana)) { removeFloating.add(mana); @@ -383,102 +357,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable { @Override public Iterator iterator() { - return floatingMana().values().iterator(); + return floatingMana.values().iterator(); } - private static class ManaMultiMap { - private Map> _storage; - private Map> storage() { - Map> result = _storage; - if (result == null) { - synchronized (this) { - result = _storage; - if (result == null) { - result = Maps.newConcurrentMap(); - _storage = result; - } - } - } - return _storage; - } - - public int size() { - return storage().size(); - } - - - public boolean isEmpty() { - return storage().isEmpty(); - } - - @SuppressWarnings("SuspiciousMethodCalls") - public boolean containsKey(Object key) { - if (key == null) - return false; - return storage().containsKey(key); - } - - public boolean put(K key, V value) { - return safeGet(key).add(value); - } - - @SuppressWarnings("SuspiciousMethodCalls") - public boolean remove(Object key, Object value) { - if (key == null || value == null) - return false; - return storage().get(key).remove(value); - } - - public boolean putAll(K key, Iterable iterable) { - Collection values = safeGet(key); - for (V v : iterable) { - if(!values.add(v)) { - return false; - } - } - return true; - } - - public boolean putAll(Map map) { - for (Map.Entry entry : map.entrySet()) { - if(!safeGet(entry.getKey()).add(entry.getValue())) { - return false; - } - } - return true; - } - - public void clear() { - for (Map.Entry> entry : storage().entrySet()) { - entry.getValue().clear(); - } - storage().clear(); - } - - public Collection safeGet(K key) { - return storage().computeIfAbsent(key, value -> Queues.newConcurrentLinkedQueue()); - } - - public Collection get(K key) { - return storage().get(key); - } - - public Set keySet() { - return storage().keySet(); - } - - public Multiset keys() { - Multiset multiset = ConcurrentHashMultiset.create(); - multiset.addAll(storage().keySet()); - return multiset; - } - - public Collection values() { - Queue values = Queues.newConcurrentLinkedQueue(); - for (Map.Entry> entry : storage().entrySet()) { - values.addAll(entry.getValue()); - } - return values; - } - } } diff --git a/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java b/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java deleted file mode 100644 index 3804c0fc958..00000000000 --- a/forge-gui-desktop/src/test/java/forge/ManaPoolTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package forge; - -import forge.ai.simulation.SimulationTest; -import forge.card.MagicColor; -import forge.game.Game; -import forge.game.card.Card; -import forge.game.mana.Mana; -import forge.game.player.Player; -import org.testng.annotations.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import static org.testng.Assert.assertEquals; - -public class ManaPoolTest extends SimulationTest { - /** - * Just a quick test for ManaPool. - */ - @Test - void testManaPoolBadLogic() { - Game game = initAndCreateGame(); - Player p0 = game.getPlayers().get(0); - Player p1 = game.getPlayers().get(1); - Mana w = new Mana(MagicColor.WHITE, new Card(1, game), null); - Mana b = new Mana(MagicColor.BLACK, new Card(1, game), null); - p0.getManaPool().addMana(w, false); - p0.getManaPool().addMana(w, false); - p0.getManaPool().addMana(w, false); - p1.getManaPool().addMana(b, false); - p1.getManaPool().addMana(b, false); - p1.getManaPool().resetPool(); // empty manapool, should clear all values - for (Mana m : p0.getManaPool()) { - p1.getManaPool().addMana(m, false); - p0.getManaPool().removeMana(m, false); // throws error if ManaPool is not threadsafe - } - assertEquals(p0.getManaPool().getAmountOfColor(MagicColor.WHITE), 0); - assertEquals(p1.getManaPool().getAmountOfColor(MagicColor.WHITE), 3); - } - @Test - void testCompletableFuture() { - Game game = initAndCreateGame(); - Player p0 = game.getPlayers().get(0); - Mana m = new Mana(MagicColor.COLORLESS, new Card(1, game), null); - p0.getManaPool().addMana(m, false); - p0.getManaPool().addMana(m, false); - p0.getManaPool().addMana(m, false); - - List> futures = new ArrayList<>(); - for (int i = 0; i < 2; i++) { - futures.add(CompletableFuture.supplyAsync(() -> { - p0.getManaPool().removeMana(m, true); - return 0; - })); - } - CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); - CompletableFuture.allOf(futuresArray).join(); - futures.clear(); - assertEquals(p0.getManaPool().getAmountOfColor(MagicColor.COLORLESS), 1); - } - - @Test - void testCompletableFutureTwo() { - Game game = initAndCreateGame(); - Player p0 = game.getPlayers().get(0); - Player p1 = game.getPlayers().get(1); - Mana m = new Mana(MagicColor.COLORLESS, new Card(1, game), null); - - List> futures = new ArrayList<>(); - for (int i = 0; i < 4; i++) { - futures.add(CompletableFuture.supplyAsync(() -> { - p0.getManaPool().addMana(m, true); - p1.getManaPool().addMana(m, true); - return 0; - })); - } - CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); - CompletableFuture.allOf(futuresArray).join(); - futures.clear(); - assertEquals(p0.getManaPool().getAmountOfColor(MagicColor.COLORLESS), 4); - assertEquals(p1.getManaPool().getAmountOfColor(MagicColor.COLORLESS), 4); - } -} From 66045c0c0a6ccb2be4bee2ab2a1a9edb386d9c73 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 22 Nov 2024 15:45:44 +0800 Subject: [PATCH 084/152] .. --- forge-gui-mobile-dev/src/forge/app/Main.java | 24 ++++++++++++-------- forge-gui-mobile/src/forge/Forge.java | 4 +++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/forge-gui-mobile-dev/src/forge/app/Main.java b/forge-gui-mobile-dev/src/forge/app/Main.java index 8714712af23..2ebf437ce40 100644 --- a/forge-gui-mobile-dev/src/forge/app/Main.java +++ b/forge-gui-mobile-dev/src/forge/app/Main.java @@ -20,9 +20,21 @@ import java.util.Optional; public class Main { private static final String versionString = BuildInfo.getVersionString(); public static void main(String[] args) { + if (!OperatingSystem.isWindows()) { + /* Prevents crash on non Windows OS before creating the LWJGL3 window. + It seems it defeats the purpose of having a splash image since + this is an indicator if the LWJGL3 has booted up succesfully. */ + closeSplash(); + } new GameLauncher(versionString); } - + public static void closeSplash() { + try { + Optional.ofNullable(SplashScreen.getSplashScreen()).ifPresent(SplashScreen::close); + } catch (Exception e) { + e.printStackTrace(); + } + } public static class DesktopAdapter implements IDeviceAdapter { private final String switchOrientationFile; @@ -96,15 +108,7 @@ public class Main { @Override public void closeSplashScreen() { - // FIXME: on Linux system it can't close splashscreen image or crash with SIGSEGV? How come it works on other OS? - if (OperatingSystem.isUnix() || OperatingSystem.isSolaris()) - return; - //could throw exception.. - try { - Optional.ofNullable(SplashScreen.getSplashScreen()).ifPresent(SplashScreen::close); - } catch (Exception e) { - e.printStackTrace(); - } + closeSplash(); } @Override diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index 7dafac0f1e7..8d03bdc24f2 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -159,7 +159,9 @@ public class Forge implements ApplicationListener { public void create() { //install our error handler ExceptionHandler.registerErrorHandling(); - getDeviceAdapter().closeSplashScreen(); + // closeSplashScreen() is called early on non-Windows OS so it will not crash, LWJGL3 bug on AWT Splash. + if (OperatingSystem.isWindows()) + getDeviceAdapter().closeSplashScreen(); GuiBase.setIsAndroid(Gdx.app.getType() == Application.ApplicationType.Android); From 69fef79105adef9ddcc8ac53f2c259459b4380b2 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 22 Nov 2024 16:06:37 +0800 Subject: [PATCH 085/152] refactor timeout --- forge-ai/src/main/java/forge/ai/AiAttackController.java | 5 ++++- forge-ai/src/main/java/forge/ai/AiController.java | 2 +- forge-game/src/main/java/forge/game/Game.java | 3 +++ forge-game/src/main/java/forge/game/player/Player.java | 7 ------- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 5021d827064..ea6faef9d05 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -78,6 +78,7 @@ public class AiAttackController { private int aiAggression = 0; // how aggressive the ai is attack will be depending on circumstances private final boolean nextTurn; // include creature that can only attack/block next turn + private final int timeOut; /** *

@@ -95,6 +96,7 @@ public class AiAttackController { myList = ai.getCreaturesInPlay(); this.nextTurn = nextTurn; refreshCombatants(defendingOpponent); + this.timeOut = ai.getGame().getAITimeout(); } // overloaded constructor to evaluate attackers that should attack next turn public AiAttackController(final Player ai, Card attacker) { @@ -108,6 +110,7 @@ public class AiAttackController { attackers.add(attacker); } this.blockers = getPossibleBlockers(oppList, this.attackers, this.nextTurn); + this.timeOut = ai.getGame().getAITimeout(); } // overloaded constructor to evaluate single specified attacker private void refreshCombatants(GameEntity defender) { @@ -967,7 +970,7 @@ public class AiAttackController { })); } CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); - CompletableFuture.allOf(futuresArray).completeOnTimeout(null, ai.getTimeout(), TimeUnit.SECONDS).join(); + CompletableFuture.allOf(futuresArray).completeOnTimeout(null, timeOut, TimeUnit.SECONDS).join(); futures.clear(); if (attackersLeft.isEmpty()) { return aiAggression; diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 6b4b5022a9d..1688adbf056 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -1684,7 +1684,7 @@ public class AiController { // instead of computing all available concurrently just add a simple timeout depending on the user prefs try { - return future.completeOnTimeout(null, player.getTimeout(), TimeUnit.SECONDS).get(); + return future.completeOnTimeout(null, game.getAITimeout(), TimeUnit.SECONDS).get(); } catch (InterruptedException | ExecutionException e) { return null; } diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 325d3a6df5d..640638135e2 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -1356,4 +1356,7 @@ public class Game { if (!isNeitherDayNorNight()) fireEvent(new GameEventDayTimeChanged(isDay())); } + public int getAITimeout() { + return AI_TIMEOUT; + } } 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 72887ca2f94..761b2b1244d 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -131,8 +131,6 @@ public class Player extends GameEntity implements Comparable { private boolean revolt = false; private int descended = 0; - // AI Timeout - private int aiTimeout = 5; private List sacrificedThisTurn = new ArrayList<>(); private List discardedThisTurn = new ArrayList<>(); @@ -211,7 +209,6 @@ public class Player extends GameEntity implements Comparable { super(id0); game = game0; - aiTimeout = game.AI_TIMEOUT; for (final ZoneType z : Player.ALL_ZONES) { final PlayerZone toPut = z == ZoneType.Battlefield ? new PlayerZoneBattlefield(z, this) : new PlayerZone(z, this); zones.put(z, toPut); @@ -270,10 +267,6 @@ public class Player extends GameEntity implements Comparable { teamNumber = iTeam; } - public final int getTimeout() { - return aiTimeout; - } - public boolean isArchenemy() { return getZone(ZoneType.SchemeDeck).size() > 0; //Only the archenemy has schemes. } From 78e587b32bacdeead5000c0b1234c31120ff49bd Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Fri, 22 Nov 2024 19:13:15 +0800 Subject: [PATCH 086/152] handle exception --- .../src/main/java/forge/StaticData.java | 3 +++ .../src/forge/adventure/world/World.java | 20 ++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/forge-core/src/main/java/forge/StaticData.java b/forge-core/src/main/java/forge/StaticData.java index 068e1956d6f..56a8cab7a04 100644 --- a/forge-core/src/main/java/forge/StaticData.java +++ b/forge-core/src/main/java/forge/StaticData.java @@ -834,6 +834,9 @@ public class StaticData { } } return 0; + }).exceptionally(ex -> { + ex.printStackTrace(); + return 0; })); } CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); diff --git a/forge-gui-mobile/src/forge/adventure/world/World.java b/forge-gui-mobile/src/forge/adventure/world/World.java index 48dea36c094..48d0a6d40b6 100644 --- a/forge-gui-mobile/src/forge/adventure/world/World.java +++ b/forge-gui-mobile/src/forge/adventure/world/World.java @@ -277,11 +277,8 @@ public class World implements Disposable, SaveFileContent { for (int xclear = -size; xclear < size; xclear++) for (int yclear = -size; yclear < size; yclear++) { try { - terrainMap[x + xclear][height - 1 - (y + yclear)] = 0; - } catch (ArrayIndexOutOfBoundsException e) { - - } + } catch (ArrayIndexOutOfBoundsException ignored) {} } } @@ -344,6 +341,9 @@ public class World implements Disposable, SaveFileContent { structure.initialize(); structureDataMap.put(data, structure); return measureGenerationTime("wavefunctioncollapse " + data.sourcePath, threadStartTime); + }).exceptionally(ex -> { + ex.printStackTrace(); + return 0L; })); } } @@ -648,7 +648,10 @@ public class World implements Disposable, SaveFileContent { startY = startY + sy; } } - return 0l; + return 0L; + }).exceptionally(ex -> { + ex.printStackTrace(); + return 0L; })); } futuresArray = futures.toArray(new CompletableFuture[0]); @@ -689,7 +692,10 @@ public class World implements Disposable, SaveFileContent { startY = startY + sy; } } - return 0l; + return 0L; + }).exceptionally(ex -> { + ex.printStackTrace(); + return 0L; })); } futuresArray = futures.toArray(new CompletableFuture[0]); @@ -931,7 +937,7 @@ public class World implements Disposable, SaveFileContent { } public int getChunkSize() { - return (Scene.getIntendedWidth() > Scene.getIntendedHeight() ? Scene.getIntendedWidth() : Scene.getIntendedHeight()) / data.tileSize; + return (Math.max(Scene.getIntendedWidth(), Scene.getIntendedHeight())) / data.tileSize; } public void dispose() { From 8efa2234d5ea18a6581e514bd6ebf4b25f9f2610 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 22 Nov 2024 16:41:43 -0500 Subject: [PATCH 087/152] YDSK: gilded_ambusher.txt --- forge-gui/res/cardsfolder/g/gilded_ambusher.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 forge-gui/res/cardsfolder/g/gilded_ambusher.txt diff --git a/forge-gui/res/cardsfolder/g/gilded_ambusher.txt b/forge-gui/res/cardsfolder/g/gilded_ambusher.txt new file mode 100644 index 00000000000..57d35ae084f --- /dev/null +++ b/forge-gui/res/cardsfolder/g/gilded_ambusher.txt @@ -0,0 +1,14 @@ +Name:Gilded Ambusher +ManaCost:1 B R +Types:Enchantment Creature Lizard Glimmer +PT:4/2 +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever CARDNAME attacks, you may sacrifice another nonland permanent. When you do, each opponent exiles a nonland, nontoken permanent they control and a random nonland card from their library, then CARDNAME deals damage to them equal to the total mana value of the cards exiled this way. +SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ Sac<1/Permanent.Other+nonLand/another nonland permanent> | Execute$ TrigEachOpp | TriggerDescription$ When you do, each opponent exiles a nonland, nontoken permanent they control and a random nonland card from their library, then CARDNAME deals damage to them equal to the total mana value of the cards exiled this way. +SVar:TrigEachOpp:DB$ RepeatEach | RepeatPlayers$ Opponent | RepeatSubAbility$ DBChoose | SubAbility$ DBExile +SVar:DBChoose:DB$ ChooseCard | Defined$ Remembered | Choices$ Permanent.nonLand+nonToken | ControlledByPlayer$ Chooser | ChoiceZone$ Battlefield | ImprintChosen$ True | Mandatory$ True | SubAbility$ DBRandom +SVar:DBRandom:DB$ ChooseCard | Defined$ Remembered | Choices$ Card.nonLand | ControlledByPlayer$ Chooser | ChoiceZone$ Library | AtRandom$ True | ImprintChosen$ True +SVar:DBExile:DB$ ChangeZone | Defined$ Imprinted | Origin$ Battlefield,Library | Destination$ Exile | SubAbility$ DBDamage | RememberChanged$ True +SVar:DBDamage:DB$ DealDamage | Defined$ Opponent | NumDmg$ Remembered$SumCMC | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True | ClearChosenCard$ True +DeckHas:Ability$Sacrifice +Oracle:Whenever Gilded Ambusher attacks, you may sacrifice another nonland permanent. When you do, each opponent exiles a nonland, nontoken permanent they control and a random nonland card from their library, then Gilded Ambusher deals damage to them equal to the total mana value of the cards exiled this way. From 14291e6a4f7971af94218c79aa8aa6932f6ea94d Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 22 Nov 2024 19:36:00 -0500 Subject: [PATCH 088/152] YDSK: valiant_emberkin.txt + support --- .../game/staticability/StaticAbilityPanharmonicon.java | 10 +++++++++- forge-gui/res/cardsfolder/v/valiant_emberkin.txt | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 forge-gui/res/cardsfolder/v/valiant_emberkin.txt diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityPanharmonicon.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityPanharmonicon.java index 1b4bc16c7f4..677c61b3e98 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityPanharmonicon.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityPanharmonicon.java @@ -161,7 +161,15 @@ public class StaticAbilityPanharmonicon { if (!stAb.matchesValidParam("ValidActivator", sa.getActivatingPlayer())) { return false; } - } else if (trigMode.equals(TriggerType.DamageDone) || trigMode.equals(TriggerType.DamageDoneOnce) + } else if (trigMode.equals(TriggerType.BecomesTarget)) { + if (!stAb.matchesValidParam("ValidTarget", runParams.get(AbilityKey.Target))) { + return false; + } + } else if (trigMode.equals(TriggerType.BecomesTargetOnce)) { + if (!stAb.matchesValidParam("ValidTarget", runParams.get(AbilityKey.Targets))) { + return false; + } + } else if (trigMode.equals(TriggerType.DamageDone) || trigMode.equals(TriggerType.DamageDoneOnce) || trigMode.equals(TriggerType.DamageAll) || trigMode.equals(TriggerType.DamageDealtOnce)) { if (stAb.hasParam("CombatDamage") && stAb.getParam("CombatDamage").equalsIgnoreCase("True") != (Boolean) runParams.get(AbilityKey.IsCombatDamage)) { diff --git a/forge-gui/res/cardsfolder/v/valiant_emberkin.txt b/forge-gui/res/cardsfolder/v/valiant_emberkin.txt new file mode 100644 index 00000000000..61ef65bf8c4 --- /dev/null +++ b/forge-gui/res/cardsfolder/v/valiant_emberkin.txt @@ -0,0 +1,10 @@ +Name:Valiant Emberkin +ManaCost:1 R W +Types:Enchantment Creature Mouse Glimmer +PT:1/1 +K:Haste +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME enters or attacks, target creature you control perpetually gets +1/+0. +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | Secondary$ True | TriggerDescription$ Whenever CARDNAME enters or attacks, target creature you control perpetually gets +1/+0. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumAtt$ 1 | Duration$ Perpetual +S:Mode$ Panharmonicon | ValidMode$ BecomesTarget,BecomesTargetOnce | ValidCard$ Permanent.YouCtrl | ValidTarget$ Creature.YouCtrl | Description$ If a creature you control becoming the target of a spell or ability causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. +Oracle:Haste\Whenever Valiant Emberkin enters or attacks, target creature you control perpetually gets +1/+0.\nIf a creature you control becoming the target of a spell or ability causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. From b0411423f6b8a6fdbb6c2e3d8f4652abc92ab40f Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sat, 23 Nov 2024 13:22:41 +0800 Subject: [PATCH 089/152] use Executor, update MatchScreen --- .../main/java/forge/ai/AiAttackController.java | 2 +- forge-core/src/main/java/forge/StaticData.java | 16 ++++++++-------- .../src/forge/screens/match/MatchScreen.java | 14 +++++++++++--- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index ea6faef9d05..fa9d32a5645 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -79,6 +79,7 @@ public class AiAttackController { private int aiAggression = 0; // how aggressive the ai is attack will be depending on circumstances private final boolean nextTurn; // include creature that can only attack/block next turn private final int timeOut; + private List> futures = new ArrayList<>(); /** *

@@ -910,7 +911,6 @@ public class AiAttackController { final AtomicInteger numForcedAttackers = new AtomicInteger(0); // nextTurn is now only used by effect from Oracle en-Vec, which can skip check must attack, // because creatures not chosen can't attack. - List> futures = new ArrayList<>(); if (!nextTurn) { for (final Card attacker : this.attackers) { final GameEntity finalDefender = defender; diff --git a/forge-core/src/main/java/forge/StaticData.java b/forge-core/src/main/java/forge/StaticData.java index 56a8cab7a04..b536a089316 100644 --- a/forge-core/src/main/java/forge/StaticData.java +++ b/forge-core/src/main/java/forge/StaticData.java @@ -777,7 +777,7 @@ public class StaticData { continue; Map> cardCount = new HashMap<>(); - List> futures = new ArrayList<>(); + List> futures = new ArrayList<>(); for (CardEdition.CardInSet c : e.getAllCardsInSet()) { if (cardCount.containsKey(c.name)) { cardCount.put(c.name, Pair.of(c.collectorNumber != null && c.collectorNumber.startsWith("F"), cardCount.get(c.name).getRight() + 1)); @@ -798,12 +798,12 @@ public class StaticData { } if (cp == null) { if (isFunny) //skip funny cards - return 0; + return null; if (!loadNonLegalCards && CardEdition.Type.FUNNY.equals(e.getType())) - return 0; + return null; EDITION_Q.add(e.getCode() + "_" + e.getName()); CNI_Q.add(e.getCode() + "_" + c + "\n"); - return 0; + return null; } // check the front image String imagePath = ImageUtil.getImageRelativePath(cp, "", true, false); @@ -813,7 +813,7 @@ public class StaticData { file = ImageKeys.setLookUpFile(imagePath, imagePath +"border"); if (file == null) { if (imagePath.isEmpty()) - return 0; + return null; EDITION_Q.add(e.getCode() + "_" + e.getName()); NIF_Q.add(e.getCode() + "_" + imagePath + "\n"); } @@ -827,16 +827,16 @@ public class StaticData { file = ImageKeys.setLookUpFile(imagePath, imagePath +"border"); if (file == null) { if (imagePath.isEmpty()) - return 0; + return null; EDITION_Q.add(e.getCode() + "_" + e.getName()); NIF_Q.add(e.getCode() + "_" + imagePath + "\n"); } } } - return 0; + return null; }).exceptionally(ex -> { ex.printStackTrace(); - return 0; + return null; })); } CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); diff --git a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java index 0b84c8a1700..98b5a8ddbe8 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java @@ -91,6 +91,11 @@ public class MatchScreen extends FScreen { private static List potentialListener; private int selectedPlayer; + + private final Map endpoints; + private final Set cardsonBattlefield; + private final Set playerViewSet; + public MatchScreen(List playerPanels0) { super(new FMenuBar()); @@ -164,6 +169,9 @@ public class MatchScreen extends FScreen { log.setMenuTab(new HiddenMenuTab(log)); devMenu.setMenuTab(new HiddenMenuTab(devMenu)); } + endpoints = new HashMap<>(); + cardsonBattlefield = new HashSet<>(); + playerViewSet = new HashSet<>(); } private boolean is4Player() { @@ -459,9 +467,9 @@ public class MatchScreen extends FScreen { void drawArcs(Graphics g) { //get all card targeting arrow origins on the battlefield - final Map endpoints = new HashMap<>(); - final Set cardsonBattlefield = new HashSet<>(); - final Set playerViewSet = new HashSet<>(); + endpoints.clear(); + cardsonBattlefield.clear(); + playerViewSet.clear(); final GameView game = MatchController.instance.getGameView(); try { for (PlayerView p : game.getPlayers()) { From 8fa95a01ae0e865bccb7a089aafbb1e9a5dd6ca3 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sat, 23 Nov 2024 19:32:03 +0800 Subject: [PATCH 090/152] update arrows --- forge-gui-mobile/src/forge/Graphics.java | 31 ++++--- .../src/forge/screens/match/MatchScreen.java | 14 ++-- .../forge/screens/match/TargetingOverlay.java | 80 ++++++++++--------- .../src/forge/screens/match/views/VStack.java | 6 +- 4 files changed, 70 insertions(+), 61 deletions(-) diff --git a/forge-gui-mobile/src/forge/Graphics.java b/forge-gui-mobile/src/forge/Graphics.java index 9581fb43550..0ad99284545 100644 --- a/forge-gui-mobile/src/forge/Graphics.java +++ b/forge-gui-mobile/src/forge/Graphics.java @@ -318,24 +318,14 @@ public class Graphics { } public void drawLineArrow(float arrowThickness, FSkinColor skinColor, float x1, float y1, float x2, float y2) { - fillCircle(skinColor.getColor(), x2, y2, arrowThickness); drawLineArrow(arrowThickness, skinColor.getColor(), x1, y1, x2, y2); - fillCircle(Color.WHITE, x2, y2, arrowThickness / 2); - drawLineArrow(arrowThickness / 3, Color.WHITE, x1, y1, x2, y2); - //drawLine(arrowThickness / 3, Color.WHITE, x1, y1, x2, y2); } public void drawLineArrow(float thickness, Color color, float x1, float y1, float x2, float y2) { batch.end(); //must pause batch while rendering shapes + float ct = thickness / 2; + float lt = thickness / 3; - /*float angle = new Vector2(x1 - x2, y1 - y2).angleRad(); - float arrowHeadRotation = (float) (Math.PI * 0.8f); - Vector2 arrowCorner3 = new Vector2(x2 + (thickness / 3) * (float) Math.cos(angle + arrowHeadRotation), y2 + (thickness / 3) * (float) Math.sin(angle + arrowHeadRotation)); - Vector2 arrowCorner4 = new Vector2(x2 + (thickness / 3) * (float) Math.cos(angle - arrowHeadRotation), y2 + (thickness / 3) * (float) Math.sin(angle - arrowHeadRotation));*/ - - if (thickness > 1) { - Gdx.gl.glLineWidth(thickness); - } if (alphaComposite < 1) { color = FSkinColor.alphaColor(color, color.a * alphaComposite); } @@ -346,12 +336,29 @@ public class Graphics { if (needSmoothing) { Gdx.gl.glEnable(GL_LINE_SMOOTH); } + startShape(ShapeType.Filled); + shapeRenderer.setColor(color); + shapeRenderer.circle(adjustX(x2), adjustY(y2, 0), thickness); + shapeRenderer.setColor(Color.WHITE); + shapeRenderer.circle(adjustX(x2), adjustY(y2, 0), ct); + endShape(); + if (thickness > 1) { + Gdx.gl.glLineWidth(thickness); + } startShape(ShapeType.Line); shapeRenderer.setColor(color); shapeRenderer.line(adjustX(x1), adjustY(y1, 0), adjustX(x2), adjustY(y2, 0)); endShape(); + if (lt > 1) { + Gdx.gl.glLineWidth(lt); + } + startShape(ShapeType.Line); + shapeRenderer.setColor(Color.WHITE); + shapeRenderer.line(adjustX(x1), adjustY(y1, 0), adjustX(x2), adjustY(y2, 0)); + endShape(); + if (needSmoothing) { Gdx.gl.glDisable(GL_LINE_SMOOTH); } diff --git a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java index 98b5a8ddbe8..a84d7ea77d8 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchScreen.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchScreen.java @@ -32,7 +32,6 @@ import forge.assets.FSkinColor.Colors; import forge.assets.FSkinTexture; import forge.game.GameView; import forge.game.card.CardView; -import forge.game.combat.CombatView; import forge.game.phase.PhaseType; import forge.game.player.PlayerView; import forge.game.zone.ZoneType; @@ -466,11 +465,13 @@ public class MatchScreen extends FScreen { } void drawArcs(Graphics g) { + final GameView game = MatchController.instance.getGameView(); + if (game == null) + return; //get all card targeting arrow origins on the battlefield endpoints.clear(); cardsonBattlefield.clear(); playerViewSet.clear(); - final GameView game = MatchController.instance.getGameView(); try { for (PlayerView p : game.getPlayers()) { if (p == null) @@ -496,13 +497,8 @@ public class MatchScreen extends FScreen { } if (endpoints.isEmpty()) return; - //draw arrows for combat - final CombatView combat = game.getCombat(); - for (CardView c : cardsonBattlefield) { - TargetingOverlay.assembleArrows(g, c, endpoints, combat, playerViewSet); - } - } catch (Exception ignored) { - } + TargetingOverlay.assembleArrows(g, cardsonBattlefield, endpoints, game.getCombat(), playerViewSet); + } catch (Exception ignored) {} } @Override diff --git a/forge-gui-mobile/src/forge/screens/match/TargetingOverlay.java b/forge-gui-mobile/src/forge/screens/match/TargetingOverlay.java index 73796dbe256..b150ab3f7aa 100644 --- a/forge-gui-mobile/src/forge/screens/match/TargetingOverlay.java +++ b/forge-gui-mobile/src/forge/screens/match/TargetingOverlay.java @@ -68,50 +68,54 @@ public class TargetingOverlay { private TargetingOverlay() { } - public static void assembleArrows(final Graphics g, final CardView c, final Map endpoints, final CombatView combat, final Set playerViewSet) { - final CardView attachedTo = c.getAttachedTo(); - final Iterable attachedCards = c.getAttachedCards(); - final CardView paired = c.getPairedWith(); - if (null != attachedTo) { - if (attachedTo.getController() != null && !attachedTo.getController().equals(c.getController())) { + public static void assembleArrows(final Graphics g, final Set cardsonBattlefield, final Map endpoints, final CombatView combat, final Set playerViewSet) { + if (cardsonBattlefield.isEmpty()) + return; + for (CardView c : cardsonBattlefield) { + final CardView attachedTo = c.getAttachedTo(); + final Iterable attachedCards = c.getAttachedCards(); + final CardView paired = c.getPairedWith(); + if (null != attachedTo) { + if (attachedTo.getController() != null && !attachedTo.getController().equals(c.getController())) { + drawArrow(g, endpoints.get(attachedTo.getId()), endpoints.get(c.getId()), ArcConnection.Friends); + } + } + if (null != attachedTo && c == attachedTo.getAttachedTo()) { drawArrow(g, endpoints.get(attachedTo.getId()), endpoints.get(c.getId()), ArcConnection.Friends); } - } - if (null != attachedTo && c == attachedTo.getAttachedTo()) { - drawArrow(g, endpoints.get(attachedTo.getId()), endpoints.get(c.getId()), ArcConnection.Friends); - } - if (null != attachedCards) { - for (final CardView enc : attachedCards) { - if (enc.getController() != null && !enc.getController().equals(c.getController())) { - drawArrow(g, endpoints.get(c.getId()), endpoints.get(enc.getId()), ArcConnection.Friends); + if (null != attachedCards) { + for (final CardView enc : attachedCards) { + if (enc.getController() != null && !enc.getController().equals(c.getController())) { + drawArrow(g, endpoints.get(c.getId()), endpoints.get(enc.getId()), ArcConnection.Friends); + } } } - } - if (null != paired) { - drawArrow(g, endpoints.get(paired.getId()), endpoints.get(c.getId()), ArcConnection.Friends); - } - if (null != combat) { - final GameEntityView defender = combat.getDefender(c); - // if c is attacking a planeswalker or battle - if (defender instanceof CardView) { - drawArrow(g, endpoints.get(defender.getId()), endpoints.get(c.getId()), ArcConnection.FoesAttacking); + if (null != paired) { + drawArrow(g, endpoints.get(paired.getId()), endpoints.get(c.getId()), ArcConnection.Friends); } - // if c is a planeswalker that's being attacked - for (final CardView pwAttacker : combat.getAttackersOf(c)) { - drawArrow(g, endpoints.get(c.getId()), endpoints.get(pwAttacker.getId()), ArcConnection.FoesAttacking); - } - for (final CardView attackingCard : combat.getAttackers()) { - final Iterable cards = combat.getPlannedBlockers(attackingCard); - if (cards == null) continue; - for (final CardView blockingCard : cards) { - if (!attackingCard.equals(c) && !blockingCard.equals(c)) { continue; } - drawArrow(g, endpoints.get(attackingCard.getId()), endpoints.get(blockingCard.getId()), ArcConnection.FoesBlocking); + if (null != combat) { + final GameEntityView defender = combat.getDefender(c); + // if c is attacking a planeswalker or battle + if (defender instanceof CardView) { + drawArrow(g, endpoints.get(defender.getId()), endpoints.get(c.getId()), ArcConnection.FoesAttacking); } - if (playerViewSet != null) { - for (final PlayerView p : playerViewSet) { - if (combat.getAttackersOf(p).contains(attackingCard)) { - final Vector2 vPlayer = MatchController.getView().getPlayerPanel(p).getAvatar().getTargetingArrowOrigin(); - drawArrow(g, endpoints.get(attackingCard.getId()), vPlayer, TargetingOverlay.ArcConnection.FoesAttacking); + // if c is a planeswalker that's being attacked + for (final CardView pwAttacker : combat.getAttackersOf(c)) { + drawArrow(g, endpoints.get(c.getId()), endpoints.get(pwAttacker.getId()), ArcConnection.FoesAttacking); + } + for (final CardView attackingCard : combat.getAttackers()) { + final Iterable cards = combat.getPlannedBlockers(attackingCard); + if (cards == null) continue; + for (final CardView blockingCard : cards) { + if (!attackingCard.equals(c) && !blockingCard.equals(c)) { continue; } + drawArrow(g, endpoints.get(attackingCard.getId()), endpoints.get(blockingCard.getId()), ArcConnection.FoesBlocking); + } + if (playerViewSet != null) { + for (final PlayerView p : playerViewSet) { + if (combat.getAttackersOf(p).contains(attackingCard)) { + final Vector2 vPlayer = MatchScreen.getPlayerPanel(p).getAvatar().getTargetingArrowOrigin(); + drawArrow(g, endpoints.get(attackingCard.getId()), vPlayer, TargetingOverlay.ArcConnection.FoesAttacking); + } } } } 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 47384176c22..69d278e1710 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VStack.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VStack.java @@ -33,6 +33,7 @@ import forge.menu.FMenuTab; import forge.menu.FPopupMenu; import forge.player.PlayerZoneUpdates; import forge.screens.match.MatchController; +import forge.screens.match.MatchScreen; import forge.screens.match.TargetingOverlay; import forge.toolbox.FCardPanel; import forge.toolbox.FDisplayObject; @@ -179,7 +180,8 @@ public class VStack extends FDropDown { activeItem = display; } else { - activeItem.setHeight(display.preferredHeight); //increase active item height to preferred height if needed + if (display != null) + activeItem.setHeight(display.preferredHeight); //increase active item height to preferred height if needed if (activeItem.getBottom() > y) { y = activeItem.getBottom(); //ensure stack height increases if needed } @@ -233,7 +235,7 @@ public class VStack extends FDropDown { } for (PlayerView p : instance.getTargetPlayers()) { TargetingOverlay.ArcConnection conn = activator.isOpponentOf(p) ? TargetingOverlay.ArcConnection.FoesStackTargeting : TargetingOverlay.ArcConnection.FriendsStackTargeting; - TargetingOverlay.drawArrow(g, arrowOrigin, MatchController.getView().getPlayerPanel(p).getAvatar().getTargetingArrowOrigin(), conn); + TargetingOverlay.drawArrow(g, arrowOrigin, MatchScreen.getPlayerPanel(p).getAvatar().getTargetingArrowOrigin(), conn); } instance = instance.getSubInstance(); } From 2905c742a8156cbcf3c124d9ae67810facaf10b1 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sat, 23 Nov 2024 13:29:29 -0500 Subject: [PATCH 091/152] YDSK: lurker_in_the_deep.txt + support --- .../java/forge/game/ability/effects/SeekEffect.java | 8 +++++--- .../java/forge/game/trigger/TriggerSeekAll.java | 1 + forge-gui/res/cardsfolder/l/lurker_in_the_deep.txt | 13 +++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 forge-gui/res/cardsfolder/l/lurker_in_the_deep.txt diff --git a/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java index b1507988173..618b1f57991 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java @@ -67,7 +67,7 @@ public class SeekEffect extends SpellAbilityEffect { pool = CardLists.getValidCards(pool, seekType, source.getController(), source, sa); } if (pool.isEmpty()) { - if (notify.length() != 0) notify.append("\r\n"); + if (!notify.isEmpty()) notify.append("\r\n"); notify.append(Localizer.getInstance().getMessage("lblSeekFailed", seekType)); continue; // can't find if nothing to seek } @@ -88,7 +88,7 @@ public class SeekEffect extends SpellAbilityEffect { } } - if (notify.length() != 0) { + if (!notify.isEmpty()) { game.getAction().notifyOfValue(sa, source, notify.toString(), null); } if (!soughtCards.isEmpty()) { @@ -98,7 +98,9 @@ public class SeekEffect extends SpellAbilityEffect { if (sa.hasParam("ImprintFound")) { source.addImprintedCards(soughtCards); } - game.getTriggerHandler().runTrigger(TriggerType.SeekAll, AbilityKey.mapFromPlayer(seeker), false); + final Map runParams = AbilityKey.mapFromPlayer(seeker); + runParams.put(AbilityKey.Cards, soughtCards); + game.getTriggerHandler().runTrigger(TriggerType.SeekAll, runParams, false); } } triggerList.triggerChangesZoneAll(game, sa); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerSeekAll.java b/forge-game/src/main/java/forge/game/trigger/TriggerSeekAll.java index c1471c5f549..25bf89fbb1b 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerSeekAll.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerSeekAll.java @@ -24,6 +24,7 @@ public class TriggerSeekAll extends Trigger { @Override public void setTriggeringObjects(SpellAbility sa, Map runParams) { sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player); + sa.setTriggeringObjectsFrom(runParams, AbilityKey.Cards); } @Override diff --git a/forge-gui/res/cardsfolder/l/lurker_in_the_deep.txt b/forge-gui/res/cardsfolder/l/lurker_in_the_deep.txt new file mode 100644 index 00000000000..58e80fd94d8 --- /dev/null +++ b/forge-gui/res/cardsfolder/l/lurker_in_the_deep.txt @@ -0,0 +1,13 @@ +Name:Lurker in the Deep +ManaCost:3 U U U +Types:Enchantment Creature Fish Illusion +PT:7/7 +K:Impending:4:2 U U +T:Mode$ ChangesZone | Destination$ Battlefield | Origin$ Any | ValidCard$ Card.Self | Execute$ TrigSeek | TriggerDescription$ Whenever CARDNAME enters or attacks, seek a nonland card. +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigSeek | Secondary$ True | TriggerDescription$ Whenever CARDNAME enters or attacks, seek a nonland card. +SVar:TrigSeek:DB$ Seek | Type$ Card.nonLand +T:Mode$ SeekAll | PlayerTurn$ True | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigConjure | TriggerDescription$ Whenever you seek one or more cards during your turn, conjure a duplicate of each of those cards into your hand, then manifest those duplicates. +SVar:TrigConjure:DB$ MakeCard | DefinedName$ TriggeredCards | RememberMade$ True | Zone$ Hand | Conjure$ True | SubAbility$ DBManifest +SVar:DBManifest:DB$ Manifest | Defined$ Remembered | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +Oracle:Impending 4-{2}{U}{U}\nWhenever Lurker in the Deep enters or attacks, seek a nonland card.\nWhenever you seek one or more cards during your turn, conjure a duplicate of each of those cards into your hand, then manifest those duplicates. From 4d21db94f9add660d9f7f1df3b9f319208603dd0 Mon Sep 17 00:00:00 2001 From: Chris H Date: Fri, 22 Nov 2024 20:48:26 -0500 Subject: [PATCH 092/152] Update remove-stale-branches.yml --- .github/workflows/remove-stale-branches.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/remove-stale-branches.yml b/.github/workflows/remove-stale-branches.yml index 40a68cf2307..9e5be51edda 100644 --- a/.github/workflows/remove-stale-branches.yml +++ b/.github/workflows/remove-stale-branches.yml @@ -7,6 +7,7 @@ on: jobs: remove-stale-branches: + if: github.repository_owner == 'Card-Forge' name: Remove Stale Branches runs-on: ubuntu-latest steps: From ac721780f7e47cb7fb322c6f9788096bf9110ddc Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 22 Nov 2024 19:05:27 -0500 Subject: [PATCH 093/152] YDSK: improvising_aerialist.txt + support --- forge-game/src/main/java/forge/game/card/CardProperty.java | 4 ++++ forge-gui/res/cardsfolder/i/improvising_aerialist.txt | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 forge-gui/res/cardsfolder/i/improvising_aerialist.txt diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 2570a8a36ee..70068787441 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -650,6 +650,10 @@ public class CardProperty { if (cards.isEmpty() || !card.equals(cards.get(0))) { return false; } + } else if (property.startsWith("TopLibrary_")) { + CardCollection cards = new CardCollection(card.getOwner().getCardsIn(ZoneType.Library)); + cards = CardLists.getValidCards(cards, property.substring(11), sourceController, source, spellAbility); + if (cards.isEmpty() || !card.equals(cards.get(0))) return false; } else if (property.startsWith("TopLibraryLand")) { CardCollection cards = CardLists.filter(card.getOwner().getCardsIn(ZoneType.Library), CardPredicates.Presets.LANDS); if (cards.isEmpty() || !card.equals(cards.get(0))) { diff --git a/forge-gui/res/cardsfolder/i/improvising_aerialist.txt b/forge-gui/res/cardsfolder/i/improvising_aerialist.txt new file mode 100644 index 00000000000..56117666c89 --- /dev/null +++ b/forge-gui/res/cardsfolder/i/improvising_aerialist.txt @@ -0,0 +1,7 @@ +Name:Improvising Aerialist +ManaCost:1 W +Types:Creature Human Survivor +PT:3/2 +T:Mode$ Phase | Phase$ Main | PhaseCount$ 2 | ValidPlayer$ You | PresentDefined$ Self | IsPresent$ Card.tapped | Execute$ TrigPerpetual | TriggerDescription$ Survival — At the beginning of your second main phase, if CARDNAME is tapped, CARDNAME and the top creature card in your library without flying perpetually gain flying. +SVar:TrigPerpetual:DB$ Pump | PumpZone$ Battlefield,Library | Defined$ Self & ValidLibrary Creature.YouOwn+TopLibrary_Creature.withoutFlying | KW$ Flying | Duration$ Perpetual +Oracle:Survival — At the beginning of your second main phase, if Improvising Aerialist is tapped, Improvising Aerialist and the top creature card in your library without flying perpetually gain flying. From bd55cc330e21d234ff9a1cb94117b318851e1797 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sat, 23 Nov 2024 16:14:12 -0500 Subject: [PATCH 094/152] tidy up CardProperty.cardHasProperty for Top/BottomLibrary --- .../java/forge/game/card/CardProperty.java | 30 +++++-------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 70068787441..05b982e2b08 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -650,29 +650,15 @@ public class CardProperty { if (cards.isEmpty() || !card.equals(cards.get(0))) { return false; } - } else if (property.startsWith("TopLibrary_")) { - CardCollection cards = new CardCollection(card.getOwner().getCardsIn(ZoneType.Library)); - cards = CardLists.getValidCards(cards, property.substring(11), sourceController, source, spellAbility); + } else if (property.startsWith("TopLibrary") || property.startsWith("BottomLibrary")) { + CardCollection cards = (CardCollection) card.getOwner().getCardsIn(ZoneType.Library); + if (!property.equals("TopLibrary")) { + if (property.equals("TopLibraryLand")) cards = CardLists.filter(cards, Presets.LANDS); + else if (property.contains("_")) cards = CardLists.getValidCards(cards, property.split("_")[1], + sourceController, source, spellAbility); + if (property.startsWith("Bottom")) Collections.reverse(cards); + } if (cards.isEmpty() || !card.equals(cards.get(0))) return false; - } else if (property.startsWith("TopLibraryLand")) { - CardCollection cards = CardLists.filter(card.getOwner().getCardsIn(ZoneType.Library), CardPredicates.Presets.LANDS); - if (cards.isEmpty() || !card.equals(cards.get(0))) { - return false; - } - } else if (property.startsWith("TopLibrary")) { - final CardCollectionView cards = card.getOwner().getCardsIn(ZoneType.Library); - if (cards.isEmpty() || !card.equals(cards.get(0))) { - return false; - } - } else if (property.startsWith("BottomLibrary")) { - CardCollection cards = new CardCollection(card.getOwner().getCardsIn(ZoneType.Library)); - if (property.startsWith("BottomLibrary_")) { - cards = CardLists.getValidCards(cards, property.substring(14), sourceController, source, spellAbility); - } - Collections.reverse(cards); - if (cards.isEmpty() || !card.equals(cards.get(0))) { - return false; - } } else if (property.startsWith("Cloned")) { if (card.getCloneOrigin() == null || !card.getCloneOrigin().equals(source)) { return false; From 14ad887a0a247418074358b7deda625ec807a3f7 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sat, 23 Nov 2024 13:22:11 -0500 Subject: [PATCH 095/152] YDSK: harrowing_swarm.txt + support --- .../java/forge/game/cost/CostAdjustment.java | 93 ++++++++++--------- .../res/cardsfolder/h/harrowing_swarm.txt | 10 ++ 2 files changed, 60 insertions(+), 43 deletions(-) create mode 100644 forge-gui/res/cardsfolder/h/harrowing_swarm.txt diff --git a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java index aa8133afe2f..447264024a8 100644 --- a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java +++ b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java @@ -526,64 +526,71 @@ public class CostAdjustment { } if (st.hasParam("Type")) { - final String type = st.getParam("Type"); - if (type.equals("Spell")) { - if (!sa.isSpell()) { - return false; - } - if (st.hasParam("OnlyFirstSpell")) { - if (activator == null ) { + switch (st.getParam("Type")) { + case "Spell" -> { + if (!sa.isSpell()) { return false; } - List list; - if (st.hasParam("ValidCard")) { - list = CardUtil.getThisTurnCast(st.getParam("ValidCard"), hostCard, st, controller); - } else { - list = game.getStack().getSpellsCastThisTurn(); - } + if (st.hasParam("OnlyFirstSpell")) { + if (activator == null) { + return false; + } + List list; + if (st.hasParam("ValidCard")) { + list = CardUtil.getThisTurnCast(st.getParam("ValidCard"), hostCard, st, controller); + } else { + list = game.getStack().getSpellsCastThisTurn(); + } - if (st.hasParam("ValidSpell")) { - list = CardLists.filterAsList(list, CardPredicates.castSA( - SpellAbilityPredicates.isValid(st.getParam("ValidSpell").split(","), controller, hostCard, st)) - ); - } + if (st.hasParam("ValidSpell")) { + list = CardLists.filterAsList(list, CardPredicates.castSA( + SpellAbilityPredicates.isValid(st.getParam("ValidSpell").split(","), controller, hostCard, st)) + ); + } - if (CardLists.filterControlledBy(list, activator).size() > 0) { + if (!CardLists.filterControlledBy(list, activator).isEmpty()) return false; + } + } + case "Ability" -> { + if (!sa.isActivatedAbility() || sa.isReplacementAbility()) { return false; } - } - } else if (type.equals("Ability")) { - if (!sa.isActivatedAbility() || sa.isReplacementAbility()) { - return false; - } - if (st.hasParam("OnlyFirstActivation")) { - int times = 0; - for (IndividualCostPaymentInstance i : game.costPaymentStack) { - SpellAbility paymentSa = i.getPayment().getAbility(); - if (paymentSa.isActivatedAbility() && st.matchesValidParam("ValidCard", paymentSa.getHostCard())) { - times++; - if (times > 1) { - return false; + if (st.hasParam("OnlyFirstActivation")) { + int times = 0; + for (IndividualCostPaymentInstance i : game.costPaymentStack) { + SpellAbility paymentSa = i.getPayment().getAbility(); + if (paymentSa.isActivatedAbility() && st.matchesValidParam("ValidCard", paymentSa.getHostCard())) { + times++; + if (times > 1) { + return false; + } } } } } - } else if (type.equals("NonManaAbility")) { - if (!sa.isActivatedAbility() || sa.isManaAbility() || sa.isReplacementAbility()) { - return false; + case "NonManaAbility" -> { + if (!sa.isActivatedAbility() || sa.isManaAbility() || sa.isReplacementAbility()) { + return false; + } } - } else if (type.equals("MorphDown")) { - if (!sa.isSpell() || !sa.isCastFaceDown()) { - return false; + case "MorphDown" -> { + if (!sa.isSpell() || !sa.isCastFaceDown()) { + return false; + } } - } else if (type.equals("Foretell")) { - if (!sa.isForetelling()) { - return false; + case "Foretell" -> { + if (!sa.isForetelling()) { + return false; + } + if (st.hasParam("FirstForetell") && activator.getNumForetoldThisTurn() > 0) { + return false; + } } - if (st.hasParam("FirstForetell") && activator.getNumForetoldThisTurn() > 0) { - return false; + case "TurnFaceUp" -> { + if (!sa.isTurnFaceUp()) return false; } } + } if (st.hasParam("AffectedZone")) { List zones = ZoneType.listValueOf(st.getParam("AffectedZone")); diff --git a/forge-gui/res/cardsfolder/h/harrowing_swarm.txt b/forge-gui/res/cardsfolder/h/harrowing_swarm.txt new file mode 100644 index 00000000000..b373bd91dc0 --- /dev/null +++ b/forge-gui/res/cardsfolder/h/harrowing_swarm.txt @@ -0,0 +1,10 @@ +Name:Harrowing Swarm +ManaCost:1 G +Types:Sorcery +A:SP$ ManifestDread | SubAbility$ DBAnimate | SpellDescription$ Manifest dread. +SVar:DBAnimate:DB$ AnimateAll | ValidCards$ Creature.faceDown+YouCtrl | Zone$ Battlefield | staticAbilities$ ReduceCost | Triggers$ TurnedFaceUp | Duration$ Permanent | StackDescription$ SpellDescription | SpellDescription$ Then each face-down creature you control gains "This permanent costs {2} less to turn face up" and "When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it." +SVar:ReduceCost:Mode$ ReduceCost | Amount$ 2 | Type$ TurnFaceUp | ValidCard$ Card.Self | Activator$ You | EffectZone$ All | Description$ This permanent costs {2} less to turn face up. +SVar:TurnedFaceUp:Mode$ TurnFaceUp | ValidCard$ Creature.Self | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it. | Description$ When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it. +SVar:TrigPump:DB$ PutCounter | Defined$ TriggeredCard | CounterType$ P1P1 +DeckHas:Ability$Counters +Oracle:Manifest dread. Then each face-down creature you control gains "This permanent costs {2} less to turn face up" and "When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it." From 6b3159273eec893f45523414c316b4a7a61f39be Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sat, 23 Nov 2024 17:21:47 -0500 Subject: [PATCH 096/152] harrowing_swarm.txt fixes --- .../src/main/java/forge/game/cost/CostAdjustment.java | 3 --- forge-gui/res/cardsfolder/h/harrowing_swarm.txt | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java index 447264024a8..c0a7e65bb1c 100644 --- a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java +++ b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java @@ -586,9 +586,6 @@ public class CostAdjustment { return false; } } - case "TurnFaceUp" -> { - if (!sa.isTurnFaceUp()) return false; - } } } diff --git a/forge-gui/res/cardsfolder/h/harrowing_swarm.txt b/forge-gui/res/cardsfolder/h/harrowing_swarm.txt index b373bd91dc0..d599b61836e 100644 --- a/forge-gui/res/cardsfolder/h/harrowing_swarm.txt +++ b/forge-gui/res/cardsfolder/h/harrowing_swarm.txt @@ -3,8 +3,8 @@ ManaCost:1 G Types:Sorcery A:SP$ ManifestDread | SubAbility$ DBAnimate | SpellDescription$ Manifest dread. SVar:DBAnimate:DB$ AnimateAll | ValidCards$ Creature.faceDown+YouCtrl | Zone$ Battlefield | staticAbilities$ ReduceCost | Triggers$ TurnedFaceUp | Duration$ Permanent | StackDescription$ SpellDescription | SpellDescription$ Then each face-down creature you control gains "This permanent costs {2} less to turn face up" and "When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it." -SVar:ReduceCost:Mode$ ReduceCost | Amount$ 2 | Type$ TurnFaceUp | ValidCard$ Card.Self | Activator$ You | EffectZone$ All | Description$ This permanent costs {2} less to turn face up. -SVar:TurnedFaceUp:Mode$ TurnFaceUp | ValidCard$ Creature.Self | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it. | Description$ When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it. -SVar:TrigPump:DB$ PutCounter | Defined$ TriggeredCard | CounterType$ P1P1 +SVar:ReduceCost:Mode$ ReduceCost | Amount$ 2 | Type$ Static.isTurnFaceUp | ValidCard$ Card.Self | Activator$ You | EffectZone$ All | Description$ This permanent costs {2} less to turn face up. +SVar:TurnedFaceUp:Mode$ TurnFaceUp | ValidCard$ Card.Self | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it. +SVar:TrigPump:DB$ PutCounter | Defined$ TriggeredCard | CounterType$ P1P1 | ConditionDefined$ TriggeredCard | ConditionPresent$ Creature DeckHas:Ability$Counters Oracle:Manifest dread. Then each face-down creature you control gains "This permanent costs {2} less to turn face up" and "When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it." From 05d812d5c0860d5183a8273056451ab48442b97b Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sat, 23 Nov 2024 18:16:48 -0500 Subject: [PATCH 097/152] harrowing_swarm.txt param fix --- forge-gui/res/cardsfolder/h/harrowing_swarm.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/h/harrowing_swarm.txt b/forge-gui/res/cardsfolder/h/harrowing_swarm.txt index d599b61836e..fa2560344e4 100644 --- a/forge-gui/res/cardsfolder/h/harrowing_swarm.txt +++ b/forge-gui/res/cardsfolder/h/harrowing_swarm.txt @@ -3,7 +3,7 @@ ManaCost:1 G Types:Sorcery A:SP$ ManifestDread | SubAbility$ DBAnimate | SpellDescription$ Manifest dread. SVar:DBAnimate:DB$ AnimateAll | ValidCards$ Creature.faceDown+YouCtrl | Zone$ Battlefield | staticAbilities$ ReduceCost | Triggers$ TurnedFaceUp | Duration$ Permanent | StackDescription$ SpellDescription | SpellDescription$ Then each face-down creature you control gains "This permanent costs {2} less to turn face up" and "When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it." -SVar:ReduceCost:Mode$ ReduceCost | Amount$ 2 | Type$ Static.isTurnFaceUp | ValidCard$ Card.Self | Activator$ You | EffectZone$ All | Description$ This permanent costs {2} less to turn face up. +SVar:ReduceCost:Mode$ ReduceCost | Amount$ 2 | ValidSpell$ Static.isTurnFaceUp | ValidCard$ Card.Self | Activator$ You | EffectZone$ All | Description$ This permanent costs {2} less to turn face up. SVar:TurnedFaceUp:Mode$ TurnFaceUp | ValidCard$ Card.Self | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ When this permanent is turned face up, if it's a creature, put a +1/+1 counter on it. SVar:TrigPump:DB$ PutCounter | Defined$ TriggeredCard | CounterType$ P1P1 | ConditionDefined$ TriggeredCard | ConditionPresent$ Creature DeckHas:Ability$Counters From 30e82c6b258f97e0396aabbb07774084b6ed17c8 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Sat, 23 Nov 2024 17:12:02 -0500 Subject: [PATCH 098/152] ChooseCardNameEffect.getStackDescription small fixes --- .../forge/game/ability/effects/ChooseCardNameEffect.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java index 6129b170bdc..9d16fdab72a 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java @@ -27,12 +27,8 @@ public class ChooseCardNameEffect extends SpellAbilityEffect { @Override protected String getStackDescription(SpellAbility sa) { - final StringBuilder sb = new StringBuilder(); - sb.append(Lang.joinHomogenous(getTargetPlayers(sa))); - sb.append("names a card."); - - return sb.toString(); + return Lang.joinHomogenous(getTargetPlayers(sa)) + " names a card."; } @Override @@ -66,7 +62,7 @@ public class ChooseCardNameEffect extends SpellAbilityEffect { if (!p.isInGame()) { continue; } - String chosen = ""; + String chosen; //This section was used for Momir Avatar, which no longer uses it - commented out 7/28/2021 //if (randomChoice) { //String numericAmount = "X"; From a611930041d51fba87c59ceb3a1d4733e255c20a Mon Sep 17 00:00:00 2001 From: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com> Date: Sat, 23 Nov 2024 17:37:09 +0000 Subject: [PATCH 099/152] Update draft rankings: BLB, DSK, FDN, LCI, MH3, OTJ --- forge-gui/res/draft/rankings/blb.rnk | 542 ++++++++++----------- forge-gui/res/draft/rankings/dsk.rnk | 20 +- forge-gui/res/draft/rankings/fdn.rnk | 569 +++++++++++----------- forge-gui/res/draft/rankings/lci.rnk | 451 ++++++++--------- forge-gui/res/draft/rankings/mh3.rnk | 568 +++++++++++----------- forge-gui/res/draft/rankings/otj.rnk | 700 +++++++++++++-------------- 6 files changed, 1426 insertions(+), 1424 deletions(-) diff --git a/forge-gui/res/draft/rankings/blb.rnk b/forge-gui/res/draft/rankings/blb.rnk index 453f7a8864d..3d4210b1e49 100644 --- a/forge-gui/res/draft/rankings/blb.rnk +++ b/forge-gui/res/draft/rankings/blb.rnk @@ -1,272 +1,272 @@ //Rank|Name|Rarity|Set -#1|Maha, Its Feathers Night|M|BLB -#2|Fecund Greenshell|R|BLB -#3|Dragonhawk, Fate's Tempest|M|BLB -#4|Warren Warleader|M|BLB -#5|Beza, the Bounding Spring|M|BLB -#6|Mabel, Heir to Cragflame|R|BLB -#7|Season of Loss|M|BLB -#8|Ral, Crackling Wit|M|BLB -#9|Innkeeper's Talent|R|BLB -#10|Darkstar Augur|R|BLB -#11|Sword of Fire and Ice|M|BLB -#12|Valley Questcaller|R|BLB -#13|Manifold Mouse|R|BLB -#14|Dreamdew Entrancer|R|BLB -#15|Ygra, Eater of All|M|BLB -#16|Vren, the Relentless|R|BLB -#17|Gev, Scaled Scorch|R|BLB -#18|Salvation Swan|R|BLB -#19|Valley Rotcaller|R|BLB -#20|Hunter's Talent|U|BLB -#21|Tender Wildguide|R|BLB -#22|Kastral, the Windcrested|R|BLB -#23|Zoraline, Cosmos Caller|R|BLB -#24|Season of the Burrow|M|BLB -#25|Starfall Invocation|R|BLB -#26|The Infamous Cruelclaw|M|BLB -#27|Rottenmouth Viper|M|BLB -#28|Valley Mightcaller|R|BLB -#29|Ledger Shredder|M|BLB -#30|Toski, Bearer of Secrets|M|BLB -#31|Pawpatch Recruit|R|BLB -#32|Camellia, the Seedmiser|R|BLB -#33|Finneas, Ace Archer|R|BLB -#34|Hugs, Grisly Guardian|M|BLB -#35|Jackdaw Savior|R|BLB -#36|Kitsa, Otterball Elite|M|BLB -#37|Thought-Stalker Warlock|U|BLB -#38|Season of Gathering|M|BLB -#39|Swords to Plowshares|M|BLB -#40|Hop to It|U|BLB -#41|Season of Weaving|M|BLB -#42|Emberheart Challenger|R|BLB -#43|Burrowguard Mentor|U|BLB -#44|Bushy Bodyguard|U|BLB -#45|Rabid Gnaw|U|BLB -#46|Valley Flamecaller|R|BLB -#47|Muerra, Trash Tactician|R|BLB -#48|Byway Barterer|R|BLB -#49|Hired Claw|R|BLB -#50|Vinereap Mentor|U|BLB -#51|Eddymurk Crab|U|BLB -#52|Jolly Gerbils|U|BLB -#53|Brightblade Stoat|U|BLB -#54|Flowerfoot Swordmaster|U|BLB -#55|Iridescent Vinelasher|R|BLB -#56|Osteomancer Adept|R|BLB -#57|Lumra, Bellow of the Woods|M|BLB -#58|Thornvault Forager|R|BLB -#59|Clement, the Worrywort|R|BLB -#60|Glarb, Calamity's Augur|M|BLB -#61|Wandertale Mentor|U|BLB -#62|Star Charter|U|BLB -#63|Harvestrite Host|U|BLB -#64|Whiskervale Forerunner|R|BLB -#65|Plumecreed Escort|U|BLB -#66|Baylen, the Haymaker|R|BLB -#67|Lunar Convocation|R|BLB -#68|Essence Channeler|R|BLB -#69|Azure Beastbinder|R|BLB -#70|Stormchaser's Talent|R|BLB -#71|Keen-Eyed Curator|R|BLB -#72|Scrapshooter|R|BLB -#73|Shrike Force|U|BLB -#74|Kitnap|R|BLB -#75|Brazen Collector|U|BLB -#76|Seedglaive Mentor|U|BLB -#77|Driftgloom Coyote|U|BLB -#78|Splash Lasher|U|BLB -#79|Downwind Ambusher|U|BLB -#80|Seasoned Warrenguard|U|BLB -#81|Daring Waverider|U|BLB -#82|Persistent Marshstalker|U|BLB -#83|Wick, the Whorled Mind|R|BLB -#84|Wick's Patrol|U|BLB -#85|Hearthborn Battler|R|BLB -#86|Curious Forager|U|BLB -#87|Overprotect|U|BLB -#88|Dour Port-Mage|R|BLB -#89|Feed the Cycle|U|BLB -#90|Honored Dreyleader|U|BLB -#91|Intrepid Rabbit|C|BLB -#92|Thundertrap Trainer|R|BLB -#93|Fell|U|BLB -#94|Heartfire Hero|U|BLB -#95|Brambleguard Veteran|U|BLB -#96|Fireglass Mentor|U|BLB -#97|Pond Prophet|C|BLB -#98|Long River Lurker|U|BLB -#99|Coruscation Mage|U|BLB -#100|Bark-Knuckle Boxer|U|BLB -#101|Knightfisher|U|BLB -#102|Mockingbird|R|BLB -#103|Take Out the Trash|C|BLB -#104|Clifftop Lookout|U|BLB -#105|Hivespine Wolverine|U|BLB -#106|Polliwallop|C|BLB -#107|Banishing Light|C|BLB -#108|Starscape Cleric|U|BLB -#109|Flamecache Gecko|U|BLB -#110|Carrot Cake|C|BLB -#111|Mabel's Mettle|U|BLB -#112|Consumed by Greed|U|BLB -#113|Brambleguard Captain|U|BLB -#114|Quaketusk Boar|U|BLB -#115|Sunspine Lynx|R|BLB -#116|Head of the Homestead|C|BLB -#117|Lilysplash Mentor|U|BLB -#118|Plumecreed Mentor|U|BLB -#119|Starseer Mentor|U|BLB -#120|Warren Elder|C|BLB -#121|Patchwork Banner|U|BLB -#122|Caretaker's Talent|R|BLB -#123|Nocturnal Hunger|C|BLB -#124|Alania's Pathmaker|C|BLB -#125|Savor|C|BLB -#126|Shoreline Looter|U|BLB -#127|Valley Floodcaller|R|BLB -#128|Stormcatch Mentor|U|BLB -#129|Feather of Flight|U|BLB -#130|Huskburster Swarm|U|BLB -#131|Calamitous Tide|U|BLB -#132|Thornplate Intimidator|C|BLB -#133|Pawpatch Formation|U|BLB -#134|Three Tree Scribe|U|BLB -#135|Builder's Talent|U|BLB -#136|Blacksmith's Talent|U|BLB -#137|Playful Shove|U|BLB -#138|Bakersbane Duo|C|BLB -#139|Rat Colony|M|BLB -#140|Dire Downdraft|C|BLB -#141|Repel Calamity|U|BLB -#142|Might of the Meek|C|BLB -#143|Cache Grab|C|BLB -#144|Galewind Moose|U|BLB -#145|Tidecaller Mentor|U|BLB -#146|Lifecreed Duo|C|BLB -#147|Bonecache Overseer|U|BLB -#148|Moonstone Harbinger|U|BLB -#149|Scales of Shale|C|BLB -#150|Longstalk Brawl|C|BLB -#151|Treetop Sentries|C|BLB -#152|Mouse Trapper|U|BLB -#153|Mindwhisker|U|BLB -#154|Hazel's Nocturne|U|BLB -#155|Reptilian Recruiter|U|BLB -#156|For the Common Good|R|BLB -#157|Hazardroot Herbalist|U|BLB -#158|Fountainport Bell|C|BLB -#159|Fabled Passage|R|BLB -#160|Uncharted Haven|C|BLB -#161|Spellgyre|U|BLB -#162|Coiling Rebirth|R|BLB -#163|Agate Assault|C|BLB -#164|Blooming Blast|U|BLB -#165|Shore Up|C|BLB -#166|Tangle Tumbler|U|BLB -#167|Sugar Coat|U|BLB -#168|Season of the Bold|M|BLB -#169|Sunshower Druid|C|BLB -#170|Stargaze|U|BLB -#171|Splash Portal|U|BLB -#172|Glidedive Duo|C|BLB -#173|Helga, Skittish Seer|M|BLB -#174|Sonar Strike|C|BLB -#175|Cruelclaw's Heist|R|BLB -#176|Stickytongue Sentinel|C|BLB -#177|Dazzling Denial|C|BLB -#178|Ravine Raider|C|BLB -#179|Brave-Kin Duo|C|BLB -#180|Run Away Together|C|BLB -#181|Cindering Cutthroat|C|BLB -#182|Moonrise Cleric|C|BLB -#183|Tempest Angler|C|BLB -#184|Rockface Village|U|BLB -#185|Mistbreath Elder|R|BLB -#186|Nettle Guard|C|BLB -#187|Pileated Provisioner|C|BLB -#188|Lightshell Duo|C|BLB -#189|Mind Spiral|C|BLB -#190|Sinister Monolith|U|BLB -#191|Rabbit Response|C|BLB -#192|Bellowing Crier|C|BLB -#193|Teapot Slinger|U|BLB -#194|Druid of the Spade|C|BLB -#195|Treeguard Duo|C|BLB -#196|Crumb and Get It|C|BLB -#197|Daggerfang Duo|C|BLB -#198|Roughshod Duo|C|BLB -#199|Sazacap's Brew|C|BLB -#200|Heaped Harvest|C|BLB -#201|High Stride|C|BLB -#202|Three Tree Rootweaver|C|BLB -#203|Short Bow|U|BLB -#204|Parting Gust|U|BLB -#205|Valley Rally|U|BLB -#206|Alania, Divergent Storm|R|BLB -#207|Seedpod Squire|C|BLB -#208|Oakhollow Village|U|BLB -#209|Dawn's Truce|R|BLB -#210|Wax-Wane Witness|C|BLB -#211|Into the Flood Maw|U|BLB -#212|Otterball Antics|U|BLB -#213|Rust-Shield Rampager|C|BLB -#214|Agate-Blade Assassin|C|BLB -#215|Raccoon Rallier|C|BLB -#216|Whiskerquill Scribe|C|BLB -#217|Skyskipper Duo|C|BLB -#218|Corpseberry Cultivator|C|BLB -#219|Junkblade Bruiser|C|BLB -#220|Veteran Guardmouse|C|BLB -#221|Lilypad Village|U|BLB -#222|Mudflat Village|U|BLB -#223|Dewdrop Cure|U|BLB -#224|Thistledown Players|C|BLB -#225|Finch Formation|C|BLB -#226|Harnesser of Storms|U|BLB -#227|Steampath Charger|C|BLB -#228|Long River's Pull|U|BLB -#229|Waterspout Warden|C|BLB -#230|Frilled Sparkshooter|C|BLB -#231|Peerless Recycling|U|BLB -#232|Hidden Grotto|C|BLB -#233|Scavenger's Talent|R|BLB -#234|Stormsplitter|M|BLB -#235|Wildfire Howl|U|BLB -#236|Secluded Courtyard|M|BLB -#237|Bonebind Orator|C|BLB -#238|Three Tree Mascot|C|BLB -#239|Fountainport|R|BLB -#240|Pearl of Wisdom|C|BLB -#241|Early Winter|C|BLB -#242|Ruthless Negotiation|U|BLB -#243|Mind Drill Assailant|C|BLB -#244|Lupinflower Village|U|BLB -#245|Thought Shucker|C|BLB -#246|Diresight|C|BLB -#247|Conduct Electricity|C|BLB -#248|Barkform Harvester|C|BLB -#249|Starlit Soothsayer|C|BLB -#250|Kindlespark Duo|C|BLB -#251|War Squeak|C|BLB -#252|Bumbleflower's Sharepot|C|BLB -#253|Eluge, the Shoreless Sea|M|BLB -#254|Psychic Whorl|C|BLB -#255|Hoarder's Overflow|U|BLB -#256|Bandit's Talent|U|BLB -#257|Nightwhorl Hermit|C|BLB -#258|Stocking the Pantry|U|BLB -#259|Starforged Sword|U|BLB -#260|Gossip's Talent|U|BLB -#261|Kindred Charge|M|BLB -#262|Wishing Well|R|BLB -#263|Heirloom Epic|U|BLB -#264|Portent of Calamity|R|BLB -#265|Festival of Embers|R|BLB -#266|Relentless Rats|M|BLB -#267|Artist's Talent|R|BLB -#268|Three Tree City|R|BLB -#269|Sylvan Tutor|M|BLB -#270|Frogmite|M|BLB -#271|Wear Down|U|BLB +#1|Valley Questcaller|R|BLB +#2|Maha, Its Feathers Night|M|BLB +#3|Season of Loss|M|BLB +#4|Dreamdew Entrancer|R|BLB +#5|Fecund Greenshell|R|BLB +#6|Sword of Fire and Ice|M|SPG +#7|Warren Warleader|M|BLB +#8|Mabel, Heir to Cragflame|R|BLB +#9|Ygra, Eater of All|M|BLB +#10|Innkeeper's Talent|R|BLB +#11|Beza, the Bounding Spring|M|BLB +#12|Manifold Mouse|R|BLB +#13|Glarb, Calamity's Augur|M|BLB +#14|Vinereap Mentor|U|BLB +#15|Dragonhawk, Fate's Tempest|M|BLB +#16|Hunter's Talent|U|BLB +#17|Camellia, the Seedmiser|R|BLB +#18|Zoraline, Cosmos Caller|R|BLB +#19|Vren, the Relentless|R|BLB +#20|Season of Gathering|M|BLB +#21|Burrowguard Mentor|U|BLB +#22|Thought-Stalker Warlock|U|BLB +#23|Wick's Patrol|U|BLB +#24|Season of Weaving|M|BLB +#25|Tender Wildguide|R|BLB +#26|Season of the Burrow|M|BLB +#27|Valley Mightcaller|R|BLB +#28|Gev, Scaled Scorch|R|BLB +#29|Shoreline Looter|U|BLB +#30|Fell|U|BLB +#31|Lumra, Bellow of the Woods|M|BLB +#32|Downwind Ambusher|U|BLB +#33|Pawpatch Recruit|R|BLB +#34|Swords to Plowshares|M|SPG +#35|Osteomancer Adept|R|BLB +#36|Finneas, Ace Archer|R|BLB +#37|Harvestrite Host|U|BLB +#38|Darkstar Augur|R|BLB +#39|Patchwork Banner|U|BLB +#40|Carrot Cake|C|BLB +#41|Hop to It|U|BLB +#42|Consumed by Greed|U|BLB +#43|Thornvault Forager|R|BLB +#44|Wandertale Mentor|U|BLB +#45|Rottenmouth Viper|M|BLB +#46|Hired Claw|R|BLB +#47|Feed the Cycle|U|BLB +#48|Keen-Eyed Curator|R|BLB +#49|Ledger Shredder|M|SPG +#50|Toski, Bearer of Secrets|M|SPG +#51|Savor|C|BLB +#52|Iridescent Vinelasher|R|BLB +#53|Valley Rotcaller|R|BLB +#54|Cache Grab|C|BLB +#55|Overprotect|U|BLB +#56|Caretaker's Talent|R|BLB +#57|Azure Beastbinder|R|BLB +#58|Rabid Gnaw|U|BLB +#59|Intrepid Rabbit|C|BLB +#60|Brightblade Stoat|U|BLB +#61|Galewind Moose|U|BLB +#62|Clement, the Worrywort|R|BLB +#63|Fireglass Mentor|U|BLB +#64|Lilysplash Mentor|U|BLB +#65|Muerra, Trash Tactician|R|BLB +#66|Scales of Shale|C|BLB +#67|Emberheart Challenger|R|BLB +#68|Mistbreath Elder|R|BLB +#69|Stormchaser's Talent|R|BLB +#70|Bakersbane Duo|C|BLB +#71|Repel Calamity|U|BLB +#72|Thundertrap Trainer|R|BLB +#73|Ruthless Negotiation|U|BLB +#74|Hugs, Grisly Guardian|M|BLB +#75|Ral, Crackling Wit|M|BLB +#76|Mudflat Village|U|BLB +#77|Hivespine Wolverine|U|BLB +#78|Dour Port-Mage|R|BLB +#79|Scrapshooter|R|BLB +#80|Treeguard Duo|C|BLB +#81|Head of the Homestead|C|BLB +#82|Pond Prophet|C|BLB +#83|Driftgloom Coyote|U|BLB +#84|Feather of Flight|U|BLB +#85|Splash Lasher|U|BLB +#86|Bonecache Overseer|U|BLB +#87|Seasoned Warrenguard|U|BLB +#88|Mindwhisker|U|BLB +#89|Glidedive Duo|C|BLB +#90|Three Tree Scribe|U|BLB +#91|Essence Channeler|R|BLB +#92|Nocturnal Hunger|C|BLB +#93|Valley Flamecaller|R|BLB +#94|Salvation Swan|R|BLB +#95|Plumecreed Mentor|U|BLB +#96|Tidecaller Mentor|U|BLB +#97|Lupinflower Village|U|BLB +#98|Starfall Invocation|R|BLB +#99|Moonstone Harbinger|U|BLB +#100|Byway Barterer|R|BLB +#101|Bark-Knuckle Boxer|U|BLB +#102|Jackdaw Savior|R|BLB +#103|Warren Elder|C|BLB +#104|Kitsa, Otterball Elite|M|BLB +#105|Take Out the Trash|C|BLB +#106|Bushy Bodyguard|U|BLB +#107|Polliwallop|C|BLB +#108|Builder's Talent|U|BLB +#109|Crumb and Get It|C|BLB +#110|Kitnap|R|BLB +#111|Heartfire Hero|U|BLB +#112|Brambleguard Veteran|U|BLB +#113|Kastral, the Windcrested|R|BLB +#114|Long River Lurker|U|BLB +#115|Longstalk Brawl|C|BLB +#116|Daring Waverider|U|BLB +#117|Hazel's Nocturne|U|BLB +#118|Persistent Marshstalker|U|BLB +#119|Pawpatch Formation|U|BLB +#120|Scavenger's Talent|R|BLB +#121|Starscape Cleric|U|BLB +#122|Flamecache Gecko|U|BLB +#123|Stickytongue Sentinel|C|BLB +#124|Daggerfang Duo|C|BLB +#125|Quaketusk Boar|U|BLB +#126|Sunshower Druid|C|BLB +#127|Starseer Mentor|U|BLB +#128|Whiskervale Forerunner|R|BLB +#129|Calamitous Tide|U|BLB +#130|Plumecreed Escort|U|BLB +#131|Curious Forager|U|BLB +#132|Fabled Passage|R|BLB +#133|Banishing Light|C|BLB +#134|Sonar Strike|C|BLB +#135|Dire Downdraft|C|BLB +#136|Bonebind Orator|C|BLB +#137|Ravine Raider|C|BLB +#138|Moonrise Cleric|C|BLB +#139|Oakhollow Village|U|BLB +#140|Lifecreed Duo|C|BLB +#141|Treetop Sentries|C|BLB +#142|Into the Flood Maw|U|BLB +#143|Mockingbird|R|BLB +#144|Rust-Shield Rampager|C|BLB +#145|Rabbit Response|C|BLB +#146|Agate-Blade Assassin|C|BLB +#147|Honored Dreyleader|U|BLB +#148|Rat Colony|M|SPG +#149|Three Tree Rootweaver|C|BLB +#150|Fountainport|R|BLB +#151|Flowerfoot Swordmaster|U|BLB +#152|Shrike Force|U|BLB +#153|Sugar Coat|U|BLB +#154|Brazen Collector|U|BLB +#155|Stargaze|U|BLB +#156|Nettle Guard|C|BLB +#157|Lightshell Duo|C|BLB +#158|Thornplate Intimidator|C|BLB +#159|Hidden Grotto|C|BLB +#160|Blacksmith's Talent|U|BLB +#161|Dazzling Denial|C|BLB +#162|High Stride|C|BLB +#163|Jolly Gerbils|U|BLB +#164|Pearl of Wisdom|C|BLB +#165|Valley Floodcaller|R|BLB +#166|Brambleguard Captain|U|BLB +#167|Corpseberry Cultivator|C|BLB +#168|Star Charter|U|BLB +#169|Diresight|C|BLB +#170|Huskburster Swarm|U|BLB +#171|Knightfisher|U|BLB +#172|Splash Portal|U|BLB +#173|Wick, the Whorled Mind|R|BLB +#174|Eddymurk Crab|U|BLB +#175|Coiling Rebirth|R|BLB +#176|Teapot Slinger|U|BLB +#177|Druid of the Spade|C|BLB +#178|Parting Gust|U|BLB +#179|Early Winter|C|BLB +#180|Cindering Cutthroat|C|BLB +#181|Seedglaive Mentor|U|BLB +#182|Rockface Village|U|BLB +#183|The Infamous Cruelclaw|M|BLB +#184|Pileated Provisioner|C|BLB +#185|Wax-Wane Witness|C|BLB +#186|Bandit's Talent|U|BLB +#187|Hearthborn Battler|R|BLB +#188|Clifftop Lookout|U|BLB +#189|Fountainport Bell|C|BLB +#190|Uncharted Haven|C|BLB +#191|Spellgyre|U|BLB +#192|Agate Assault|C|BLB +#193|Alania's Pathmaker|C|BLB +#194|Blooming Blast|U|BLB +#195|Shore Up|C|BLB +#196|Mabel's Mettle|U|BLB +#197|Season of the Bold|M|BLB +#198|Lilypad Village|U|BLB +#199|Coruscation Mage|U|BLB +#200|Long River's Pull|U|BLB +#201|Mind Spiral|C|BLB +#202|Reptilian Recruiter|U|BLB +#203|Helga, Skittish Seer|M|BLB +#204|Bellowing Crier|C|BLB +#205|Eluge, the Shoreless Sea|M|BLB +#206|Playful Shove|U|BLB +#207|Wildfire Howl|U|BLB +#208|Skyskipper Duo|C|BLB +#209|Roughshod Duo|C|BLB +#210|Heaped Harvest|C|BLB +#211|Short Bow|U|BLB +#212|Junkblade Bruiser|C|BLB +#213|Seedpod Squire|C|BLB +#214|Veteran Guardmouse|C|BLB +#215|Thistledown Players|C|BLB +#216|Cruelclaw's Heist|R|BLB +#217|Psychic Whorl|C|BLB +#218|Three Tree Mascot|C|BLB +#219|Tangle Tumbler|U|BLB +#220|Brave-Kin Duo|C|BLB +#221|Run Away Together|C|BLB +#222|Might of the Meek|C|BLB +#223|Steampath Charger|C|BLB +#224|Barkform Harvester|C|BLB +#225|Baylen, the Haymaker|R|BLB +#226|Lunar Convocation|R|BLB +#227|Heirloom Epic|U|BLB +#228|Whiskerquill Scribe|C|BLB +#229|Sunspine Lynx|R|BLB +#230|Valley Rally|U|BLB +#231|Mind Drill Assailant|C|BLB +#232|Stormcatch Mentor|U|BLB +#233|Conduct Electricity|C|BLB +#234|Otterball Antics|U|BLB +#235|Waterspout Warden|C|BLB +#236|Sinister Monolith|U|BLB +#237|Starlit Soothsayer|C|BLB +#238|Frilled Sparkshooter|C|BLB +#239|Hazardroot Herbalist|U|BLB +#240|Peerless Recycling|U|BLB +#241|Bumbleflower's Sharepot|C|BLB +#242|Portent of Calamity|R|BLB +#243|Raccoon Rallier|C|BLB +#244|Three Tree City|R|BLB +#245|Finch Formation|C|BLB +#246|Thought Shucker|C|BLB +#247|Mouse Trapper|U|BLB +#248|Wishing Well|R|BLB +#249|Nightwhorl Hermit|C|BLB +#250|Kindred Charge|M|SPG +#251|Dewdrop Cure|U|BLB +#252|Sylvan Tutor|M|SPG +#253|Artist's Talent|R|BLB +#254|Wear Down|U|BLB +#255|Alania, Divergent Storm|R|BLB +#256|Tempest Angler|C|BLB +#257|Dawn's Truce|R|BLB +#258|Harnesser of Storms|U|BLB +#259|Gossip's Talent|U|BLB +#260|Kindlespark Duo|C|BLB +#261|War Squeak|C|BLB +#262|For the Common Good|R|BLB +#263|Festival of Embers|R|BLB +#264|Hoarder's Overflow|U|BLB +#265|Stormsplitter|M|BLB +#266|Stocking the Pantry|U|BLB +#267|Sazacap's Brew|C|BLB +#268|Starforged Sword|U|BLB +#269|Relentless Rats|M|SPG +#270|Frogmite|M|SPG +#271|Secluded Courtyard|M|SPG diff --git a/forge-gui/res/draft/rankings/dsk.rnk b/forge-gui/res/draft/rankings/dsk.rnk index 41fc36ba66c..7ee28814dda 100644 --- a/forge-gui/res/draft/rankings/dsk.rnk +++ b/forge-gui/res/draft/rankings/dsk.rnk @@ -47,7 +47,7 @@ #46|Floodpits Drowner|U|DSK #47|Patchwork Beastie|U|DSK #48|Threats Around Every Corner|U|DSK -#49|Phantasmal Image|M|DSK +#49|Phantasmal Image|M|SPG #50|Betrayer's Bargain|U|DSK #51|Ethereal Armor|U|DSK #52|Growing Dread|U|DSK @@ -66,7 +66,7 @@ #65|Inquisitive Glimmer|U|DSK #66|Painter's Studio Defeated Gallery|U|DSK #67|Piggy Bank|U|DSK -#68|Damnation|M|DSK +#68|Damnation|M|SPG #69|Glassworks Shattered Yard|C|DSK #70|Glimmerlight|C|DSK #71|Spineseeker Centipede|C|DSK @@ -91,7 +91,7 @@ #90|Cursed Windbreaker|U|DSK #91|Insidious Fungus|U|DSK #92|Paranormal Analyst|U|DSK -#93|Unholy Heat|M|DSK +#93|Unholy Heat|M|SPG #94|Monstrous Emergence|C|DSK #95|Say Its Name|C|DSK #96|Seized from Slumber|C|DSK @@ -173,7 +173,7 @@ #172|Rite of the Moth|U|DSK #173|Valgavoth's Faithful|U|DSK #174|Raucous Carnival|C|DSK -#175|Hallowed Haunting|M|DSK +#175|Hallowed Haunting|M|SPG #176|Emerge from the Cocoon|C|DSK #177|Fear of Lost Teeth|C|DSK #178|Grasping Longneck|C|DSK @@ -216,7 +216,7 @@ #215|Nashi, Searcher in the Dark|R|DSK #216|Fear of Exposure|U|DSK #217|Neglected Manor|C|DSK -#218|Collected Company|M|DSK +#218|Collected Company|M|SPG #219|Derelict Attic Widow's Walk|C|DSK #220|Possessed Goat|C|DSK #221|Central Elevator Promising Stairs|R|DSK @@ -275,11 +275,11 @@ #274|Leyline of Transformation|R|DSK #275|Marina Vendrell's Grimoire|R|DSK #276|The Tale of Tamiyo|R|DSK -#277|Noxious Revival|M|DSK -#278|Maddening Hex|M|DSK -#279|Sacrifice|M|DSK -#280|Expropriate|M|DSK -#281|Soul Warden|M|DSK +#277|Noxious Revival|M|SPG +#278|Maddening Hex|M|SPG +#279|Sacrifice|M|SPG +#280|Expropriate|M|SPG +#281|Soul Warden|M|SPG #282|Forest|C|DSK #283|Mountain|C|DSK #284|Swamp|C|DSK diff --git a/forge-gui/res/draft/rankings/fdn.rnk b/forge-gui/res/draft/rankings/fdn.rnk index 4bd1dbe28b3..62edf1c0872 100644 --- a/forge-gui/res/draft/rankings/fdn.rnk +++ b/forge-gui/res/draft/rankings/fdn.rnk @@ -1,286 +1,287 @@ //Rank|Name|Rarity|Set -#1|Kaito, Cunning Infiltrator|M|FDN -#2|Twinflame Tyrant|M|FDN -#3|Liliana, Dreadhorde General|M|FDN -#4|Blasphemous Edict|R|FDN -#5|Embercleave|M|FDN -#6|Sire of Seven Deaths|M|FDN -#7|Arahbo, the First Fang|R|FDN -#8|Herald of Eternal Dawn|M|FDN -#9|Chandra, Flameshaper|M|FDN -#10|Koma, World-Eater|R|FDN -#11|Day of Judgment|R|FDN -#12|Consuming Aberration|R|FDN -#13|Curator of Destinies|R|FDN -#14|Krenko, Mob Boss|R|FDN -#15|Akroma's Memorial|M|FDN -#16|Kellan, Planar Trailblazer|R|FDN -#17|Muldrotha, the Gravetide|M|FDN -#18|Celestial Armor|R|FDN -#19|Valkyrie's Call|M|FDN -#20|Archmage of Runes|R|FDN -#21|Sphinx of Forgotten Lore|M|FDN -#22|Bloodthirsty Conqueror|M|FDN -#23|Ashroot Animist|R|FDN -#24|Kykar, Zephyr Awakener|R|FDN -#25|Niv-Mizzet, Visionary|M|FDN -#26|Etali, Primal Storm|R|FDN -#27|Scavenging Ooze|R|FDN -#28|Zimone, Paradox Sculptor|M|FDN -#29|Quilled Greatwurm|M|FDN -#30|Rite of the Dragoncaller|M|FDN -#31|Temporal Manipulation|M|FDN -#32|Searslicer Goblin|R|FDN -#33|Mossborn Hydra|R|FDN -#34|Alesha, Who Laughs at Fate|R|FDN -#35|Leyline Axe|R|FDN -#36|Ajani's Pridemate|U|FDN -#37|Slagstorm|R|FDN -#38|Vivien Reid|M|FDN -#39|Sylvan Scavenging|R|FDN -#40|Exsanguinate|U|FDN -#41|Spinner of Souls|R|FDN -#42|Exemplar of Light|R|FDN -#43|High-Society Hunter|R|FDN -#44|Ajani, Caller of the Pride|M|FDN -#45|Balmor, Battlemage Captain|U|FDN -#46|Tatyova, Benthic Druid|U|FDN -#47|Kiora, the Rising Tide|R|FDN -#48|Skyknight Squire|R|FDN -#49|Elenda, Saint of Dusk|R|FDN -#50|Lathril, Blade of the Elves|R|FDN -#51|Faebloom Trick|U|FDN -#52|Vengeful Bloodwitch|U|FDN -#53|Elvish Regrower|U|FDN -#54|Needletooth Pack|U|FDN -#55|Aetherize|U|FDN -#56|Mischievous Mystic|U|FDN -#57|Strongbox Raider|U|FDN -#58|Felling Blow|U|FDN -#59|Cat Collector|U|FDN -#60|Sun-Blessed Healer|U|FDN -#61|High Fae Trickster|R|FDN -#62|Homunculus Horde|R|FDN -#63|Tinybones, Bauble Burglar|R|FDN -#64|Zul Ashur, Lich Lord|R|FDN -#65|Anthem of Champions|R|FDN -#66|Extravagant Replication|R|FDN -#67|Firespitter Whelp|U|FDN -#68|Heartfire Immolator|U|FDN -#69|Elvish Archdruid|R|FDN -#70|Nessian Hornbeetle|U|FDN -#71|Empyrean Eagle|U|FDN -#72|Solemn Simulacrum|R|FDN -#73|Preposterous Proportions|R|FDN -#74|Fiendish Panda|U|FDN -#75|Banner of Kinship|R|FDN -#76|Soulstone Sanctuary|R|FDN -#77|Dwynen, Gilt-Leaf Daen|U|FDN -#78|Heroic Reinforcements|U|FDN -#79|Swiftblade Vindicator|R|FDN -#80|Flamewake Phoenix|R|FDN -#81|Rune-Scarred Demon|R|FDN -#82|Abyssal Harvester|R|FDN -#83|Loot, Exuberant Explorer|R|FDN -#84|Perforating Artist|U|FDN -#85|Wardens of the Cycle|U|FDN -#86|Rise of the Dark Realms|M|FDN -#87|Drakuseth, Maw of Flames|R|FDN -#88|Good-Fortune Unicorn|U|FDN -#89|Vampire Nighthawk|U|FDN -#90|Goblin Bushwhacker|M|FDN -#91|Guarded Heir|U|FDN -#92|Giada, Font of Hope|R|FDN -#93|Micromancer|U|FDN -#94|Llanowar Elves|C|FDN -#95|Abrade|U|FDN -#96|Bigfin Bouncer|C|FDN -#97|Elfsworn Giant|C|FDN -#98|Affectionate Indrik|U|FDN -#99|Dwynen's Elite|C|FDN -#100|Heraldic Banner|U|FDN -#101|Helpful Hunter|C|FDN -#102|Skyship Buccaneer|U|FDN -#103|Tragic Banshee|U|FDN -#104|Fiery Annihilation|U|FDN -#105|Think Twice|C|FDN -#106|Hero's Downfall|U|FDN -#107|Burst Lightning|C|FDN -#108|Overrun|U|FDN -#109|Drake Hatcher|R|FDN -#110|Spectral Sailor|U|FDN -#111|Tolarian Terror|C|FDN -#112|Meteor Golem|U|FDN -#113|Dragon Trainer|U|FDN -#114|Dauntless Veteran|U|FDN -#115|Quakestrider Ceratops|U|FDN -#116|Snakeskin Veil|C|FDN -#117|Infernal Vessel|U|FDN -#118|Banishing Light|C|FDN -#119|Bite Down|C|FDN -#120|Bloom Tender|M|FDN -#121|Fiend Artisan|M|FDN -#122|Crystal Barricade|R|FDN -#123|Prideful Parent|C|FDN -#124|Arbiter of Woe|U|FDN -#125|Midnight Snack|U|FDN -#126|Vampire Gourmand|U|FDN -#127|Slumbering Cerberus|U|FDN -#128|Angel of Finality|U|FDN -#129|Imprisoned in the Moon|U|FDN -#130|Blanchwood Armor|U|FDN -#131|Divine Resilience|U|FDN -#132|Youthful Valkyrie|U|FDN -#133|Genesis Wave|R|FDN -#134|Mild-Mannered Librarian|U|FDN -#135|Wildwood Scourge|U|FDN -#136|Revenge of the Rats|U|FDN -#137|Dreadwing Scavenger|U|FDN -#138|Cathar Commando|C|FDN -#139|Resolute Reinforcements|U|FDN -#140|Garruk's Uprising|U|FDN -#141|Ruby, Daring Tracker|U|FDN -#142|Stab|C|FDN -#143|Soul-Shackled Zombie|C|FDN -#144|Healer's Hawk|C|FDN -#145|Witness Protection|C|FDN -#146|Condemn|M|FDN -#147|Paradise Druid|M|FDN -#148|Claws Out|U|FDN -#149|Arcane Epiphany|U|FDN -#150|Nine-Lives Familiar|R|FDN -#151|Battlesong Berserker|U|FDN -#152|Electroduplicate|R|FDN -#153|Scrawling Crawler|R|FDN -#154|Savannah Lions|U|FDN -#155|Serra Angel|U|FDN -#156|Phyrexian Arena|R|FDN -#157|Swiftfoot Boots|U|FDN -#158|Joust Through|U|FDN -#159|Brineborn Cutthroat|U|FDN -#160|Hare Apparent|C|FDN -#161|Eager Trufflesnout|U|FDN -#162|Mischievous Pup|U|FDN -#163|Diregraf Ghoul|U|FDN -#164|Inspiring Call|U|FDN -#165|Courageous Goblin|C|FDN -#166|Brazen Scourge|U|FDN -#167|Bake into a Pie|C|FDN -#168|Elementalist Adept|C|FDN -#169|Dazzling Angel|C|FDN -#170|Inspiring Paladin|C|FDN -#171|Bushwack|C|FDN -#172|Grappling Kraken|U|FDN -#173|Refute|C|FDN -#174|Bulk Up|U|FDN -#175|Zombify|U|FDN -#176|Shivan Dragon|U|FDN -#177|Adventuring Gear|U|FDN -#178|Twinblade Blessing|U|FDN -#179|Cephalid Inkmage|U|FDN -#180|Goblin Negotiation|U|FDN -#181|Boltwave|U|FDN -#182|Beast-Kin Ranger|C|FDN -#183|Reclamation Sage|U|FDN -#184|Goldvein Pick|C|FDN -#185|Apothecary Stomper|C|FDN -#186|Goblin Surprise|C|FDN -#187|Uncharted Voyage|C|FDN -#188|Squad Rallier|C|FDN -#189|Felidar Savior|C|FDN -#190|Inspiration from Beyond|U|FDN -#191|Lunar Insight|R|FDN -#192|Crypt Feaster|C|FDN -#193|Lightshell Duo|C|FDN -#194|Stromkirk Bloodthief|U|FDN -#195|Giant Growth|C|FDN -#196|Hungry Ghoul|C|FDN -#197|Burnished Hart|U|FDN -#198|Bloodfell Caves|C|FDN -#199|Blossoming Sands|C|FDN -#200|Dismal Backwater|C|FDN -#201|Evolving Wilds|C|FDN -#202|Jungle Hollow|C|FDN -#203|Rugged Highlands|C|FDN -#204|Scoured Barrens|C|FDN -#205|Secluded Courtyard|U|FDN -#206|Swiftwater Cliffs|C|FDN -#207|Thornwood Falls|C|FDN -#208|Tranquil Cove|C|FDN -#209|Wind-Scarred Crag|C|FDN -#210|Wary Thespian|C|FDN -#211|Treetop Snarespinner|C|FDN -#212|Gnarlid Colony|C|FDN -#213|Cackling Prowler|C|FDN -#214|Ambush Wolf|C|FDN -#215|Firebrand Archer|C|FDN -#216|Axgard Cavalry|C|FDN -#217|Macabre Waltz|C|FDN -#218|Burglar Rat|C|FDN -#219|Fleeting Flight|C|FDN -#220|Luminous Rebuke|C|FDN -#221|Make Your Move|C|FDN -#222|Vanguard Seraph|C|FDN -#223|Infestation Sage|C|FDN -#224|Marauding Blight-Priest|C|FDN -#225|Clinquant Skymage|U|FDN -#226|Erudite Wizard|C|FDN -#227|Billowing Shriekmass|U|FDN -#228|Seeker's Folly|U|FDN -#229|Self-Reflection|U|FDN -#230|Reassembling Skeleton|U|FDN -#231|Seismic Rupture|U|FDN -#232|Juggernaut|U|FDN -#233|Rogue's Passage|U|FDN -#234|Essence Scatter|U|FDN -#235|Ravenous Amulet|U|FDN -#236|Eaten Alive|C|FDN -#237|Quick-Draw Katana|C|FDN -#238|Campus Guide|C|FDN -#239|Grow from the Ashes|C|FDN -#240|Sure Strike|C|FDN -#241|Sower of Chaos|C|FDN -#242|Involuntary Employment|C|FDN -#243|Gorehorn Raider|C|FDN -#244|Goblin Boarders|C|FDN -#245|Frenzied Goblin|U|FDN -#246|Vampire Soulcaller|C|FDN -#247|Sanguine Syphoner|C|FDN -#248|Fake Your Own Death|C|FDN -#249|Run Away Together|C|FDN -#250|Strix Lookout|C|FDN -#251|Armasaur Guide|C|FDN -#252|Fanatical Firebrand|C|FDN -#253|Thrill of Possibility|C|FDN -#254|Mocking Sprite|C|FDN -#255|Fleeting Distraction|C|FDN -#256|Rune-Sealed Wall|U|FDN -#257|Gleaming Barrier|C|FDN -#258|Incinerating Blast|C|FDN -#259|Gutless Plunderer|C|FDN -#260|Crackling Cyclops|C|FDN +#1|Liliana, Dreadhorde General|M|FDN +#2|Bloodthirsty Conqueror|M|FDN +#3|Alesha, Who Laughs at Fate|R|FDN +#4|Celestial Armor|R|FDN +#5|Zimone, Paradox Sculptor|M|FDN +#6|Spinner of Souls|R|FDN +#7|Scavenging Ooze|R|FDN +#8|Curator of Destinies|R|FDN +#9|Dreadwing Scavenger|U|FDN +#10|Elenda, Saint of Dusk|R|FDN +#11|Embercleave|M|SPG +#12|Leyline Axe|R|FDN +#13|Vivien Reid|M|FDN +#14|Sylvan Scavenging|R|FDN +#15|Sire of Seven Deaths|M|FDN +#16|Arahbo, the First Fang|R|FDN +#17|Chandra, Flameshaper|M|FDN +#18|Searslicer Goblin|R|FDN +#19|Giada, Font of Hope|R|FDN +#20|Kiora, the Rising Tide|R|FDN +#21|Twinflame Tyrant|M|FDN +#22|Empyrean Eagle|U|FDN +#23|Mischievous Mystic|U|FDN +#24|Cat Collector|U|FDN +#25|Exemplar of Light|R|FDN +#26|High-Society Hunter|R|FDN +#27|Vampire Gourmand|U|FDN +#28|Day of Judgment|R|FDN +#29|Blasphemous Edict|R|FDN +#30|Skyknight Squire|R|FDN +#31|Sun-Blessed Healer|U|FDN +#32|Faebloom Trick|U|FDN +#33|Arbiter of Woe|U|FDN +#34|Ashroot Animist|R|FDN +#35|Micromancer|U|FDN +#36|Infernal Vessel|U|FDN +#37|Bake into a Pie|C|FDN +#38|Tragic Banshee|U|FDN +#39|Fiery Annihilation|U|FDN +#40|Kellan, Planar Trailblazer|R|FDN +#41|Spectral Sailor|U|FDN +#42|Perforating Artist|U|FDN +#43|Stab|C|FDN +#44|Vampire Nighthawk|U|FDN +#45|Helpful Hunter|C|FDN +#46|Herald of Eternal Dawn|M|FDN +#47|Valkyrie's Call|M|FDN +#48|Refute|C|FDN +#49|Sphinx of Forgotten Lore|M|FDN +#50|Battlesong Berserker|U|FDN +#51|Kykar, Zephyr Awakener|R|FDN +#52|Hero's Downfall|U|FDN +#53|Burst Lightning|C|FDN +#54|Felling Blow|U|FDN +#55|Dazzling Angel|C|FDN +#56|Felidar Savior|C|FDN +#57|Ajani, Caller of the Pride|M|FDN +#58|Essence Scatter|U|FDN +#59|Rune-Scarred Demon|R|FDN +#60|Bigfin Bouncer|C|FDN +#61|Banishing Light|C|FDN +#62|Luminous Rebuke|C|FDN +#63|Akroma's Memorial|M|SPG +#64|Kaito, Cunning Infiltrator|M|FDN +#65|Tinybones, Bauble Burglar|R|FDN +#66|Koma, World-Eater|R|FDN +#67|Scrawling Crawler|R|FDN +#68|Drake Hatcher|R|FDN +#69|Abrade|U|FDN +#70|Healer's Hawk|C|FDN +#71|Infestation Sage|C|FDN +#72|Guarded Heir|U|FDN +#73|Skyship Buccaneer|U|FDN +#74|Seeker's Folly|U|FDN +#75|Think Twice|C|FDN +#76|Heartfire Immolator|U|FDN +#77|Youthful Valkyrie|U|FDN +#78|Dragon Trainer|U|FDN +#79|Eaten Alive|C|FDN +#80|Dismal Backwater|C|FDN +#81|Gorehorn Raider|C|FDN +#82|Burglar Rat|C|FDN +#83|Fleeting Flight|C|FDN +#84|Elvish Regrower|U|FDN +#85|Angel of Finality|U|FDN +#86|Llanowar Elves|C|FDN +#87|Nessian Hornbeetle|U|FDN +#88|Fiendish Panda|U|FDN +#89|Abyssal Harvester|R|FDN +#90|Hungry Ghoul|C|FDN +#91|Tranquil Cove|C|FDN +#92|Involuntary Employment|C|FDN +#93|Fake Your Own Death|C|FDN +#94|Uncharted Voyage|C|FDN +#95|Goblin Bushwhacker|M|SPG +#96|Arcane Epiphany|U|FDN +#97|Ajani's Pridemate|U|FDN +#98|Lightshell Duo|C|FDN +#99|Slagstorm|R|FDN +#100|Solemn Simulacrum|R|FDN +#101|Joust Through|U|FDN +#102|Soulstone Sanctuary|R|FDN +#103|Krenko, Mob Boss|R|FDN +#104|Mild-Mannered Librarian|U|FDN +#105|Heroic Reinforcements|U|FDN +#106|Rise of the Dark Realms|M|FDN +#107|Bloodfell Caves|C|FDN +#108|Soul-Shackled Zombie|C|FDN +#109|Pilfer|C|FDN +#110|Bloom Tender|M|SPG +#111|Condemn|M|SPG +#112|Fiend Artisan|M|SPG +#113|Slumbering Cerberus|U|FDN +#114|Serra Angel|U|FDN +#115|Phyrexian Arena|R|FDN +#116|Rogue's Passage|U|FDN +#117|Eager Trufflesnout|U|FDN +#118|Loot, Exuberant Explorer|R|FDN +#119|Cathar Commando|C|FDN +#120|Ruby, Daring Tracker|U|FDN +#121|Scoured Barrens|C|FDN +#122|Make Your Move|C|FDN +#123|Squad Rallier|C|FDN +#124|Fleeting Distraction|C|FDN +#125|Prideful Parent|C|FDN +#126|Archmage of Runes|R|FDN +#127|Vengeful Bloodwitch|U|FDN +#128|Anthem of Champions|R|FDN +#129|Hidetsugu's Second Rite|U|FDN +#130|Balmor, Battlemage Captain|U|FDN +#131|Cephalid Inkmage|U|FDN +#132|Strongbox Raider|U|FDN +#133|Beast-Kin Ranger|C|FDN +#134|Affectionate Indrik|U|FDN +#135|Heraldic Banner|U|FDN +#136|Wary Thespian|C|FDN +#137|Ambush Wolf|C|FDN +#138|Strix Lookout|C|FDN +#139|Vanguard Seraph|C|FDN +#140|Paradise Druid|M|SPG +#141|High Fae Trickster|R|FDN +#142|Billowing Shriekmass|U|FDN +#143|Zul Ashur, Lich Lord|R|FDN +#144|Overrun|U|FDN +#145|Meteor Golem|U|FDN +#146|Quilled Greatwurm|M|FDN +#147|Resolute Reinforcements|U|FDN +#148|Diregraf Ghoul|U|FDN +#149|Evolving Wilds|C|FDN +#150|Rugged Highlands|C|FDN +#151|Wind-Scarred Crag|C|FDN +#152|Vampire Soulcaller|C|FDN +#153|Bite Down|C|FDN +#154|Grappling Kraken|U|FDN +#155|Nine-Lives Familiar|R|FDN +#156|Mossborn Hydra|R|FDN +#157|Brineborn Cutthroat|U|FDN +#158|Ghalta, Primal Hunger|R|FDN +#159|Ravenous Amulet|U|FDN +#160|Thornwood Falls|C|FDN +#161|Treetop Snarespinner|C|FDN +#162|Sower of Chaos|C|FDN +#163|Gutless Plunderer|C|FDN +#164|Inspiring Paladin|C|FDN +#165|Icewind Elemental|C|FDN +#166|Claws Out|U|FDN +#167|Inspiration from Beyond|U|FDN +#168|Stromkirk Bloodthief|U|FDN +#169|Etali, Primal Storm|R|FDN +#170|Elvish Archdruid|R|FDN +#171|Muldrotha, the Gravetide|M|FDN +#172|Preposterous Proportions|R|FDN +#173|Dwynen, Gilt-Leaf Daen|U|FDN +#174|Authority of the Consuls|R|FDN +#175|Drakuseth, Maw of Flames|R|FDN +#176|Snakeskin Veil|U|FDN +#177|Swiftwater Cliffs|C|FDN +#178|Goldvein Pick|C|FDN +#179|Cackling Prowler|C|FDN +#180|Courageous Goblin|C|FDN +#181|Sanguine Syphoner|C|FDN +#182|Fanatical Firebrand|C|FDN +#183|Marauding Blight-Priest|C|FDN +#184|Bushwhack|C|FDN +#185|Temporal Manipulation|M|SPG +#186|Lunar Insight|R|FDN +#187|Needletooth Pack|U|FDN +#188|Savannah Lions|U|FDN +#189|Self-Reflection|U|FDN +#190|Giant Growth|C|FDN +#191|Consuming Aberration|R|FDN +#192|Tatyova, Benthic Druid|U|FDN +#193|Blossoming Sands|C|FDN +#194|Goblin Boarders|C|FDN +#195|Frenzied Goblin|U|FDN +#196|Brazen Scourge|U|FDN +#197|Macabre Waltz|C|FDN +#198|Witness Protection|C|FDN +#199|Extravagant Replication|R|FDN +#200|Reassembling Skeleton|U|FDN +#201|Zombify|U|FDN +#202|Swiftblade Vindicator|R|FDN +#203|Goblin Negotiation|U|FDN +#204|Dauntless Veteran|U|FDN +#205|Elfsworn Giant|C|FDN +#206|Quakestrider Ceratops|U|FDN +#207|Mischievous Pup|U|FDN +#208|Dwynen's Elite|C|FDN +#209|Jungle Hollow|C|FDN +#210|Gnarlid Colony|C|FDN +#211|Elementalist Adept|C|FDN +#212|Blanchwood Armor|U|FDN +#213|Rune-Sealed Wall|U|FDN +#214|Tolarian Terror|C|FDN +#215|Flamewake Phoenix|R|FDN +#216|Garruk's Uprising|U|FDN +#217|Good-Fortune Unicorn|U|FDN +#218|Sure Strike|C|FDN +#219|Incinerating Blast|C|FDN +#220|Goblin Surprise|C|FDN +#221|Axgard Cavalry|C|FDN +#222|Thrill of Possibility|C|FDN +#223|Homunculus Horde|R|FDN +#224|Stroke of Midnight|U|FDN +#225|Revenge of the Rats|U|FDN +#226|Wardens of the Cycle|U|FDN +#227|Run Away Together|C|FDN +#228|Erudite Wizard|C|FDN +#229|Electroduplicate|R|FDN +#230|Firespitter Whelp|U|FDN +#231|Divine Resilience|U|FDN +#232|Raise the Past|R|FDN +#233|Hare Apparent|C|FDN +#234|Aetherize|U|FDN +#235|Imprisoned in the Moon|U|FDN +#236|Shivan Dragon|U|FDN +#237|Wildwood Scourge|U|FDN +#238|Quick-Draw Katana|C|FDN +#239|Campus Guide|C|FDN +#240|Apothecary Stomper|C|FDN +#241|Rite of the Dragoncaller|M|FDN +#242|Crystal Barricade|R|FDN +#243|Clinquant Skymage|U|FDN +#244|Crypt Feaster|C|FDN +#245|Midnight Snack|U|FDN +#246|Bulk Up|U|FDN +#247|Niv-Mizzet, Visionary|M|FDN +#248|Fishing Pole|U|FDN +#249|An Offer You Can't Refuse|U|FDN +#250|Time Stop|R|FDN +#251|Painful Quandary|R|FDN +#252|Seismic Rupture|U|FDN +#253|Adventuring Gear|U|FDN +#254|Juggernaut|U|FDN +#255|Swiftfoot Boots|U|FDN +#256|Twinblade Blessing|U|FDN +#257|Banner of Kinship|R|FDN +#258|Genesis Wave|R|FDN +#259|Exsanguinate|U|FDN +#260|Boltwave|U|FDN #261|Broken Wings|C|FDN -#262|Spitfire Lagac|C|FDN -#263|Stroke of Midnight|U|FDN -#264|Omniscience|M|FDN -#265|Time Stop|R|FDN -#266|Painful Quandary|R|FDN -#267|Doubling Season|M|FDN -#268|Progenitus|M|FDN -#269|Ghalta, Primal Hunger|R|FDN -#270|Raise the Past|R|FDN -#271|Authority of the Consuls|R|FDN -#272|Pilfer|C|FDN -#273|Aegis Turtle|C|FDN -#274|Grim Tutor|M|FDN -#275|Sphinx's Tutelage|M|FDN -#276|An Offer You Can't Refuse|U|FDN -#277|Hidetsugu's Second Rite|U|FDN -#278|Fishing Pole|U|FDN -#279|Brass's Bounty|R|FDN -#280|Thousand-Year Storm|R|FDN -#281|Plains|C|FDN -#282|Island|C|FDN -#283|Swamp|C|FDN -#284|Mountain|C|FDN -#285|Forest|C|FDN +#262|Inspiring Call|U|FDN +#263|Reclamation Sage|U|FDN +#264|Lathril, Blade of the Elves|R|FDN +#265|Burnished Hart|U|FDN +#266|Secluded Courtyard|U|FDN +#267|Gleaming Barrier|C|FDN +#268|Grow from the Ashes|C|FDN +#269|Spitfire Lagac|C|FDN +#270|Firebrand Archer|C|FDN +#271|Aegis Turtle|C|FDN +#272|Armasaur Guide|C|FDN +#273|Crackling Cyclops|C|FDN +#274|Mocking Sprite|C|FDN +#275|Omniscience|M|FDN +#276|Doubling Season|M|FDN +#277|Progenitus|M|FDN +#278|Brass's Bounty|R|FDN +#279|Thousand-Year Storm|R|FDN +#280|Grim Tutor|M|SPG +#281|Sphinx's Tutelage|M|SPG +#282|Plains|C|FDN +#283|Island|C|FDN +#284|Swamp|C|FDN +#285|Mountain|C|FDN +#286|Forest|C|FDN diff --git a/forge-gui/res/draft/rankings/lci.rnk b/forge-gui/res/draft/rankings/lci.rnk index 1a233920887..e2ceb7bf752 100644 --- a/forge-gui/res/draft/rankings/lci.rnk +++ b/forge-gui/res/draft/rankings/lci.rnk @@ -1,3 +1,4 @@ +//Rank|Name|Rarity|Set #1|Aclazotz, Deepest Betrayal|M|LCI #2|Bonehoard Dracosaur|M|LCI #3|Palani's Hatcher|R|LCI @@ -8,28 +9,28 @@ #8|Kitesail Larcenist|R|LCI #9|Magmatic Galleon|R|LCI #10|Pugnacious Hammerskull|R|LCI -#11|Sentinel of the Nameless City|R|LCI -#12|Zoetic Glyph|U|LCI -#13|Ojer Kaslem, Deepest Growth|M|LCI +#11|Ojer Kaslem, Deepest Growth|M|LCI +#12|Sentinel of the Nameless City|R|LCI +#13|Zoetic Glyph|U|LCI #14|Trumpeting Carnosaur|R|LCI #15|Warden of the Inner Sky|R|LCI #16|Spyglass Siren|U|LCI -#17|Quintorius Kand|M|LCI -#18|Preacher of the Schism|R|LCI +#17|Preacher of the Schism|R|LCI +#18|Quintorius Kand|M|LCI #19|Thousand Moons Smithy|R|LCI #20|Akal Pakal, First Among Equals|R|LCI #21|Deep-Cavern Bat|U|LCI #22|The Skullspore Nexus|M|LCI #23|Resplendent Angel|M|LCI #24|Inti, Seneschal of the Sun|R|LCI -#25|Subterranean Schooner|R|LCI -#26|Chimil, the Inner Sun|M|LCI -#27|Anim Pakal, Thousandth Moon|R|LCI -#28|Malcolm, Alluring Scoundrel|R|LCI -#29|Intrepid Paleontologist|R|LCI -#30|Staunch Crewmate|U|LCI +#25|Chimil, the Inner Sun|M|LCI +#26|Malcolm, Alluring Scoundrel|R|LCI +#27|Subterranean Schooner|R|LCI +#28|Anim Pakal, Thousandth Moon|R|LCI +#29|Itzquinth, Firstborn of Gishath|U|LCI +#30|Intrepid Paleontologist|R|LCI #31|Ruin-Lurker Bat|U|LCI -#32|Itzquinth, Firstborn of Gishath|U|LCI +#32|Staunch Crewmate|U|LCI #33|Clay-Fired Bricks|U|LCI #34|Bedrock Tortoise|R|LCI #35|Spring-Loaded Sawblades|U|LCI @@ -37,228 +38,228 @@ #37|Ojer Pakpatiq, Deepest Epoch|M|LCI #38|Captain Storm, Cosmium Raider|U|LCI #39|Chupacabra Echo|U|LCI -#40|Miner's Guidewing|C|LCI -#41|Oltec Cloud Guard|C|LCI -#42|Waterwind Scout|C|LCI -#43|Kellan, Daring Traveler|R|LCI -#44|Restless Anchorage|R|LCI -#45|Restless Ridgeline|R|LCI -#46|Deepfathom Echo|R|LCI -#47|Jadelight Spelunker|R|LCI -#48|Starving Revenant|R|LCI -#49|Hulking Raptor|R|LCI -#50|Oaken Siren|C|LCI -#51|The Everflowing Well|R|LCI -#52|Bringer of the Last Gift|R|LCI -#53|Throne of the Grim Captain|R|LCI -#54|Dusk Rose Reliquary|U|LCI -#55|Dire Flail|R|LCI -#56|Cogwork Wrestler|C|LCI -#57|Lodestone Needle|U|LCI -#58|Cenote Scout|U|LCI -#59|Abuelo, Ancestral Echo|R|LCI -#60|Poetic Ingenuity|R|LCI -#61|Pathfinding Axejaw|C|LCI -#62|Petrify|C|LCI +#40|Deepfathom Echo|R|LCI +#41|Miner's Guidewing|C|LCI +#42|Oltec Cloud Guard|C|LCI +#43|Waterwind Scout|C|LCI +#44|Kellan, Daring Traveler|R|LCI +#45|Restless Anchorage|R|LCI +#46|Restless Ridgeline|R|LCI +#47|Starving Revenant|R|LCI +#48|The Everflowing Well|R|LCI +#49|Bringer of the Last Gift|R|LCI +#50|Jadelight Spelunker|R|LCI +#51|Hulking Raptor|R|LCI +#52|Cenote Scout|U|LCI +#53|Abuelo, Ancestral Echo|R|LCI +#54|Throne of the Grim Captain|R|LCI +#55|Lodestone Needle|U|LCI +#56|Oaken Siren|C|LCI +#57|Poetic Ingenuity|R|LCI +#58|Dusk Rose Reliquary|U|LCI +#59|Dire Flail|R|LCI +#60|Pathfinding Axejaw|C|LCI +#61|Petrify|C|LCI +#62|Cogwork Wrestler|C|LCI #63|Inverted Iceberg|C|LCI #64|Dreadmaw's Ire|U|LCI -#65|The Ancient One|M|LCI -#66|Etali's Favor|C|LCI -#67|Bloodletter of Aclazotz|M|LCI -#68|Ojer Axonil, Deepest Might|M|LCI -#69|Poison Dart Frog|C|LCI -#70|Thrashing Brontodon|U|LCI -#71|Vito, Fanatic of Aclazotz|M|LCI -#72|Defossilize|U|LCI -#73|Belligerent Yearling|U|LCI +#65|Etali's Favor|C|LCI +#66|Bloodletter of Aclazotz|M|LCI +#67|Ojer Axonil, Deepest Might|M|LCI +#68|Poison Dart Frog|C|LCI +#69|Thrashing Brontodon|U|LCI +#70|Vito, Fanatic of Aclazotz|M|LCI +#71|Defossilize|U|LCI +#72|Belligerent Yearling|U|LCI +#73|Colossadactyl|U|LCI #74|Roaming Throne|R|LCI -#75|Restless Reef|R|LCI -#76|Staggering Size|C|LCI -#77|Malamet Battle Glyph|U|LCI -#78|Goblin Tomb Raider|C|LCI -#79|Kinjalli's Dawnrunner|U|LCI -#80|Unlucky Drop|C|LCI -#81|Brackish Blunder|C|LCI -#82|Tinker's Tote|C|LCI -#83|Souls of the Lost|R|LCI -#84|Restless Prairie|R|LCI -#85|Threefold Thunderhulk|R|LCI -#86|Market Gnome|U|LCI -#87|Diamond Pick-Axe|U|LCI -#88|Colossadactyl|U|LCI +#75|The Ancient One|M|LCI +#76|Restless Reef|R|LCI +#77|Huatli's Final Strike|C|LCI +#78|Staggering Size|C|LCI +#79|Malamet Battle Glyph|U|LCI +#80|Goblin Tomb Raider|C|LCI +#81|Kinjalli's Dawnrunner|U|LCI +#82|Unlucky Drop|C|LCI +#83|Brackish Blunder|C|LCI +#84|Tinker's Tote|C|LCI +#85|Stalactite Stalker|R|LCI +#86|Threefold Thunderhulk|R|LCI +#87|Market Gnome|U|LCI +#88|Diamond Pick-Axe|U|LCI #89|Earthshaker Dreadmaw|U|LCI #90|Master's Guide-Mural|U|LCI -#91|Triumphant Chomp|U|LCI -#92|Abrade|C|LCI -#93|Huatli's Final Strike|C|LCI -#94|Scampering Surveyor|U|LCI -#95|Volatile Wanderglyph|C|LCI -#96|Tithing Blade|C|LCI -#97|Waylaying Pirates|C|LCI -#98|Stalactite Stalker|R|LCI -#99|The Belligerent|R|LCI -#100|Braided Net|R|LCI -#101|Guardian of the Great Door|U|LCI -#102|Corpses of the Lost|R|LCI -#103|Caparocti Sunborn|U|LCI -#104|Kutzil, Malamet Exemplar|U|LCI -#105|Bitter Triumph|U|LCI -#106|Cavern Stomper|C|LCI -#107|Armored Kincaller|C|LCI -#108|Plundering Pirate|C|LCI -#109|Ancestors' Aid|C|LCI -#110|Quicksand Whirlpool|C|LCI -#111|Ironpaw Aspirant|C|LCI -#112|Ojer Taq, Deepest Foundation|M|LCI -#113|Tishana's Tidebinder|R|LCI -#114|Terror Tide|R|LCI -#115|Scytheclaw Raptor|U|LCI -#116|Explorer's Cache|U|LCI -#117|Vanguard of the Rose|U|LCI -#118|Saheeli's Lattice|U|LCI -#119|Ixalli's Lorekeeper|U|LCI -#120|Jade Seedstones|U|LCI +#91|Guardian of the Great Door|U|LCI +#92|Triumphant Chomp|U|LCI +#93|Bitter Triumph|U|LCI +#94|Abrade|C|LCI +#95|Scampering Surveyor|U|LCI +#96|Cavern Stomper|C|LCI +#97|Armored Kincaller|C|LCI +#98|Volatile Wanderglyph|C|LCI +#99|Tithing Blade|C|LCI +#100|Quicksand Whirlpool|C|LCI +#101|Ojer Taq, Deepest Foundation|M|LCI +#102|Souls of the Lost|R|LCI +#103|Restless Prairie|R|LCI +#104|The Belligerent|R|LCI +#105|Braided Net|R|LCI +#106|Vanguard of the Rose|U|LCI +#107|Corpses of the Lost|R|LCI +#108|Jade Seedstones|U|LCI +#109|Caparocti Sunborn|U|LCI +#110|Kutzil, Malamet Exemplar|U|LCI +#111|Molten Collapse|R|LCI +#112|Wail of the Forgotten|R|LCI +#113|Plundering Pirate|C|LCI +#114|Waylaying Pirates|C|LCI +#115|Tishana's Tidebinder|R|LCI +#116|Terror Tide|R|LCI +#117|Scytheclaw Raptor|U|LCI +#118|Explorer's Cache|U|LCI +#119|Saheeli's Lattice|U|LCI +#120|Ixalli's Lorekeeper|U|LCI #121|Tendril of the Mycotyrant|U|LCI -#122|Wail of the Forgotten|R|LCI -#123|Orazca Puzzle-Door|C|LCI -#124|Sunshot Militia|C|LCI -#125|Panicked Altisaur|C|LCI -#126|Another Chance|C|LCI -#127|Deconstruction Hammer|C|LCI -#128|Acrobatic Leap|C|LCI -#129|Hidden Courtyard|C|LCI -#130|Hidden Nursery|C|LCI -#131|Chart a Course|U|LCI -#132|Dinotomaton|C|LCI -#133|Hermitic Nautilus|U|LCI -#134|Council of Echoes|U|LCI -#135|Molten Collapse|R|LCI -#136|Fanatical Offering|C|LCI -#137|Sunfire Torch|C|LCI -#138|Join the Dead|C|LCI -#139|Echo of Dusk|C|LCI -#140|Deathcap Marionette|C|LCI -#141|Dead Weight|C|LCI -#142|River Herald Scout|C|LCI -#143|Attentive Sunscribe|C|LCI -#144|Captivating Cave|C|LCI -#145|Hidden Cataract|C|LCI -#146|Hidden Necropolis|C|LCI -#147|Hidden Volcano|C|LCI -#148|Sinuous Benthisaur|U|LCI -#149|Idol of the Deep King|C|LCI -#150|Cosmium Confluence|R|LCI -#151|Saheeli, the Sun's Brilliance|M|LCI -#152|Sovereign Okinec Ahau|M|LCI -#153|Skullcap Snail|C|LCI -#154|Malamet War Scribe|U|LCI -#155|Twists and Turns|U|LCI -#156|Visage of Dread|U|LCI -#157|River Herald Guide|C|LCI -#158|Shipwreck Sentry|C|LCI -#159|Eaten by Piranhas|U|LCI -#160|Soaring Sandwing|C|LCI -#161|Oltec Archaeologists|C|LCI -#162|Ghalta, Stampede Tyrant|M|LCI -#163|Bat Colony|U|LCI -#164|Dauntless Dismantler|U|LCI -#165|Confounding Riddle|U|LCI -#166|Rampaging Ceratops|U|LCI -#167|Nurturing Bristleback|C|LCI -#168|Akawalli, the Seething Tower|U|LCI -#169|Restless Vents|R|LCI -#170|Synapse Necromage|U|LCI -#171|Malamet Scythe|C|LCI -#172|In the Presence of Ages|C|LCI -#173|Burning Sun Cavalry|C|LCI -#174|Ray of Ruin|C|LCI +#122|Sunshot Militia|C|LCI +#123|Panicked Altisaur|C|LCI +#124|Ancestors' Aid|C|LCI +#125|Join the Dead|C|LCI +#126|Deathcap Marionette|C|LCI +#127|Dead Weight|C|LCI +#128|Another Chance|C|LCI +#129|Ironpaw Aspirant|C|LCI +#130|Deconstruction Hammer|C|LCI +#131|Acrobatic Leap|C|LCI +#132|Hidden Cataract|C|LCI +#133|Hidden Courtyard|C|LCI +#134|Hidden Nursery|C|LCI +#135|Chart a Course|U|LCI +#136|Dinotomaton|C|LCI +#137|Cosmium Confluence|R|LCI +#138|Hermitic Nautilus|U|LCI +#139|Twists and Turns|U|LCI +#140|Restless Vents|R|LCI +#141|Council of Echoes|U|LCI +#142|Orazca Puzzle-Door|C|LCI +#143|Fanatical Offering|C|LCI +#144|River Herald Guide|C|LCI +#145|Sunfire Torch|C|LCI +#146|Echo of Dusk|C|LCI +#147|River Herald Scout|C|LCI +#148|Oltec Archaeologists|C|LCI +#149|Captivating Cave|C|LCI +#150|Hidden Necropolis|C|LCI +#151|Hidden Volcano|C|LCI +#152|Sinuous Benthisaur|U|LCI +#153|Idol of the Deep King|C|LCI +#154|Sovereign Okinec Ahau|M|LCI +#155|Skullcap Snail|C|LCI +#156|Malamet War Scribe|U|LCI +#157|Nurturing Bristleback|C|LCI +#158|Akawalli, the Seething Tower|U|LCI +#159|Synapse Necromage|U|LCI +#160|Visage of Dread|U|LCI +#161|Burning Sun Cavalry|C|LCI +#162|Ray of Ruin|C|LCI +#163|Shipwreck Sentry|C|LCI +#164|Eaten by Piranhas|U|LCI +#165|Soaring Sandwing|C|LCI +#166|Attentive Sunscribe|C|LCI +#167|Rampaging Spiketail|C|LCI +#168|Saheeli, the Sun's Brilliance|M|LCI +#169|Bat Colony|U|LCI +#170|Confounding Riddle|U|LCI +#171|Rampaging Ceratops|U|LCI +#172|Forgotten Monument|U|LCI +#173|Malamet Scythe|C|LCI +#174|In the Presence of Ages|C|LCI #175|Sage of Days|C|LCI #176|Pirate Hat|C|LCI #177|Might of the Ancestors|U|LCI #178|Cosmium Blast|C|LCI #179|Adaptive Gemguard|C|LCI -#180|Rampaging Spiketail|C|LCI +#180|Coati Scavenger|U|LCI #181|Uchbenbak, the Great Mistake|U|LCI #182|Careening Mine Cart|U|LCI -#183|Brass's Tunnel-Grinder|R|LCI -#184|Kutzil's Flanker|R|LCI -#185|Stinging Cave Crawler|U|LCI -#186|Zoyowa Lava-Tongue|U|LCI -#187|Forgotten Monument|U|LCI -#188|Rumbling Rockslide|C|LCI -#189|Primordial Gnawer|C|LCI -#190|Tarrian's Journal|R|LCI -#191|Goldfury Strider|U|LCI -#192|Coati Scavenger|U|LCI -#193|Sunbird Standard|U|LCI -#194|Enterprising Scallywag|U|LCI -#195|Digsite Conservator|U|LCI -#196|Oteclan Landmark|C|LCI -#197|Merfolk Cave-Diver|U|LCI -#198|Hoverstone Pilgrim|U|LCI -#199|Promising Vein|C|LCI -#200|Walk with the Ancestors|C|LCI -#201|Mineshaft Spider|C|LCI -#202|Seismic Monstrosaur|C|LCI -#203|Daring Discovery|C|LCI -#204|Mischievous Pup|U|LCI -#205|Thousand Moons Crackshot|C|LCI -#206|Envoy of Okinec Ahau|C|LCI -#207|The Mycotyrant|M|LCI -#208|Cavern of Souls|M|LCI +#183|Sunbird Standard|U|LCI +#184|Dauntless Dismantler|U|LCI +#185|Brass's Tunnel-Grinder|R|LCI +#186|Kutzil's Flanker|R|LCI +#187|Stinging Cave Crawler|U|LCI +#188|Zoyowa Lava-Tongue|U|LCI +#189|Promising Vein|C|LCI +#190|Mineshaft Spider|C|LCI +#191|Rumbling Rockslide|C|LCI +#192|Primordial Gnawer|C|LCI +#193|Tarrian's Journal|R|LCI +#194|Goldfury Strider|U|LCI +#195|Ghalta, Stampede Tyrant|M|LCI +#196|Soulcoil Viper|U|LCI +#197|Sunken Citadel|R|LCI +#198|Enterprising Scallywag|U|LCI +#199|Digsite Conservator|U|LCI +#200|Oteclan Landmark|C|LCI +#201|Merfolk Cave-Diver|U|LCI +#202|Hoverstone Pilgrim|U|LCI +#203|Seismic Monstrosaur|C|LCI +#204|Daring Discovery|C|LCI +#205|Mischievous Pup|U|LCI +#206|Fungal Fortitude|C|LCI +#207|Envoy of Okinec Ahau|C|LCI +#208|The Mycotyrant|M|LCI #209|Cavernous Maw|U|LCI -#210|Soulcoil Viper|U|LCI -#211|Sunken Citadel|R|LCI -#212|Helping Hand|U|LCI -#213|Abyssal Gorestalker|U|LCI -#214|Gargantuan Leech|U|LCI +#210|Helping Hand|U|LCI +#211|Abyssal Gorestalker|U|LCI +#212|Gargantuan Leech|U|LCI +#213|Dowsing Device|U|LCI +#214|Nicanzil, Current Conductor|U|LCI #215|Calamitous Cave-In|U|LCI #216|Tarrian's Soulcleaver|R|LCI -#217|Malamet Veteran|C|LCI -#218|Fungal Fortitude|C|LCI +#217|Walk with the Ancestors|C|LCI +#218|Malamet Veteran|C|LCI #219|Out of Air|C|LCI #220|Ancestral Reminiscence|C|LCI -#221|Glorifier of Suffering|C|LCI +#221|Thousand Moons Crackshot|C|LCI #222|Didact Echo|C|LCI -#223|Amalia Benavides Aguirre|R|LCI -#224|Hurl into History|U|LCI -#225|Dowsing Device|U|LCI -#226|Nicanzil, Current Conductor|U|LCI -#227|Runaway Boulder|C|LCI -#228|Cartographer's Companion|C|LCI -#229|Family Reunion|C|LCI -#230|Bartolome del Presidio|U|LCI +#223|Bartolome del Presidio|U|LCI +#224|Amalia Benavides Aguirre|R|LCI +#225|Hurl into History|U|LCI +#226|Runaway Boulder|C|LCI +#227|Cartographer's Companion|C|LCI +#228|Mephitic Draught|C|LCI +#229|Glorifier of Suffering|C|LCI +#230|Family Reunion|C|LCI #231|Gishath, Sun's Avatar|M|LCI -#232|Glimpse the Core|U|LCI -#233|Compass Gnome|C|LCI -#234|Over the Edge|C|LCI -#235|Malamet Brawler|C|LCI -#236|Hotfoot Gnome|C|LCI -#237|Mephitic Draught|C|LCI +#232|Cavern of Souls|M|LCI +#233|Queen's Bay Paladin|R|LCI +#234|Compass Gnome|C|LCI +#235|Over the Edge|C|LCI +#236|Malamet Brawler|C|LCI +#237|Hotfoot Gnome|C|LCI #238|Greedy Freebooter|C|LCI -#239|Relic's Roar|C|LCI -#240|Marauding Brinefang|C|LCI -#241|Queen's Bay Paladin|R|LCI -#242|Seeker of Sunlight|C|LCI -#243|Brazen Blademaster|C|LCI -#244|Screaming Phantom|C|LCI -#245|Treasure Map|R|LCI -#246|Get Lost|R|LCI -#247|Curator of Sun's Creation|U|LCI -#248|Self-Reflection|U|LCI +#239|Marauding Brinefang|C|LCI +#240|Treasure Map|R|LCI +#241|Curator of Sun's Creation|U|LCI +#242|Glimpse the Core|U|LCI +#243|Self-Reflection|U|LCI +#244|Seeker of Sunlight|C|LCI +#245|Screaming Phantom|C|LCI +#246|Relic's Roar|C|LCI +#247|Broodrage Mycoid|C|LCI +#248|Get Lost|R|LCI #249|Waterlogged Hulk|U|LCI #250|Hunter's Blowgun|C|LCI -#251|Thousand Moons Infantry|C|LCI -#252|Song of Stupefaction|C|LCI -#253|Broodrage Mycoid|C|LCI -#254|Spelunking|U|LCI -#255|Squirming Emergence|R|LCI +#251|Brazen Blademaster|C|LCI +#252|Basking Capybara|C|LCI +#253|Song of Stupefaction|C|LCI +#254|Growing Rites of Itlimoc|R|LCI +#255|Spelunking|U|LCI #256|Frilled Cave-Wurm|C|LCI -#257|Basking Capybara|C|LCI -#258|Deep Goblin Skulltaker|C|LCI -#259|Acolyte of Aclazotz|C|LCI +#257|Deep Goblin Skulltaker|C|LCI +#258|Acolyte of Aclazotz|C|LCI +#259|Thousand Moons Infantry|C|LCI #260|Deeproot Pilgrimage|R|LCI -#261|Growing Rites of Itlimoc|R|LCI +#261|Hit the Mother Lode|R|LCI #262|Matzalantli, the Great Door|R|LCI #263|Bloodthorn Flail|U|LCI #264|The Millennium Calendar|M|LCI @@ -266,22 +267,22 @@ #266|Fabrication Foundry|R|LCI #267|Zoyowa's Justice|U|LCI #268|Glowcap Lantern|U|LCI -#269|Echoing Deeps|R|LCI -#270|Abuelo's Awakening|R|LCI -#271|Canonized in Blood|U|LCI -#272|Malicious Eclipse|U|LCI -#273|Kaslem's Stonetree|C|LCI -#274|Swashbuckler's Whip|U|LCI -#275|Volatile Fault|U|LCI -#276|Disruptor Wanderglyph|C|LCI -#277|Buried Treasure|C|LCI -#278|Disturbed Slumber|C|LCI -#279|Tectonic Hazard|C|LCI -#280|Child of the Volcano|C|LCI -#281|Vito's Inquisitor|C|LCI -#282|Grasping Shadows|U|LCI -#283|The Enigma Jewel|M|LCI -#284|Hit the Mother Lode|R|LCI +#269|Squirming Emergence|R|LCI +#270|Echoing Deeps|R|LCI +#271|Abuelo's Awakening|R|LCI +#272|Canonized in Blood|U|LCI +#273|Malicious Eclipse|U|LCI +#274|Kaslem's Stonetree|C|LCI +#275|Swashbuckler's Whip|U|LCI +#276|Volatile Fault|U|LCI +#277|Disruptor Wanderglyph|C|LCI +#278|Buried Treasure|C|LCI +#279|Disturbed Slumber|C|LCI +#280|Tectonic Hazard|C|LCI +#281|Child of the Volcano|C|LCI +#282|Vito's Inquisitor|C|LCI +#283|Grasping Shadows|U|LCI +#284|The Enigma Jewel|M|LCI #285|Sorcerous Spyglass|U|LCI #286|Contested Game Ball|U|LCI #287|Plains|C|LCI diff --git a/forge-gui/res/draft/rankings/mh3.rnk b/forge-gui/res/draft/rankings/mh3.rnk index 0f1d8c242fb..4b319d8db7c 100644 --- a/forge-gui/res/draft/rankings/mh3.rnk +++ b/forge-gui/res/draft/rankings/mh3.rnk @@ -1,325 +1,325 @@ //Rank|Name|Rarity|Set #1|Guide of Souls|R|MH3 #2|Ocelot Pride|M|MH3 -#3|Writhing Chrysalis|C|MH3 -#4|Crabomination|R|MH3 -#5|Amped Raptor|U|MH3 -#6|Phlage, Titan of Fire's Fury|M|MH3 +#3|Amped Raptor|U|MH3 +#4|Phlage, Titan of Fire's Fury|M|MH3 +#5|Crabomination|R|MH3 +#6|Writhing Chrysalis|C|MH3 #7|Ajani, Nacatl Pariah|M|MH3 -#8|Fury|M|MH3 +#8|Fury|M|SPG #9|Tamiyo, Inquisitive Student|M|MH3 -#10|Satya, Aetherflux Genius|M|MH3 +#10|Satya, Aetherflux Genius|M|M3C #11|Psychic Frog|R|MH3 -#12|Solitude|M|MH3 -#13|Path of Annihilation|U|MH3 -#14|Scurry of Gremlins|U|MH3 -#15|Laelia, the Blade Reforged|R|MH3 -#16|Galvanic Discharge|C|MH3 -#17|Fanatic of Rhonas|R|MH3 -#18|Nadu, Winged Wisdom|R|MH3 -#19|Malevolent Rumble|C|MH3 -#20|Grief|M|MH3 +#12|Path of Annihilation|U|MH3 +#13|Scurry of Gremlins|U|MH3 +#14|Solitude|M|SPG +#15|Galvanic Discharge|C|MH3 +#16|Fanatic of Rhonas|R|MH3 +#17|Nadu, Winged Wisdom|R|MH3 +#18|Malevolent Rumble|C|MH3 +#19|Laelia, the Blade Reforged|R|MH3 +#20|Kozilek's Command|R|MH3 #21|Phelia, Exuberant Shepherd|R|MH3 #22|Marionette Apprentice|U|MH3 #23|Eldrazi Repurposer|C|MH3 #24|Ugin's Binding|M|MH3 -#25|Dismember|M|MH3 -#26|Kozilek's Command|R|MH3 +#25|Dismember|M|SPG +#26|Spawn-Gang Commander|U|MH3 #27|Signature Slam|U|MH3 #28|Depth Defiler|U|MH3 -#29|Ophiomancer|R|MH3 -#30|Static Prison|U|MH3 -#31|Detective's Phoenix|R|MH3 -#32|Spawn-Gang Commander|U|MH3 +#29|Titans' Vanguard|U|MH3 +#30|Ophiomancer|R|MH3 +#31|Grief|M|SPG +#32|Static Prison|U|MH3 #33|Horrific Assault|C|MH3 #34|Sowing Mycospawn|R|MH3 #35|Conduit Goblin|C|MH3 #36|Aerie Auxiliary|C|MH3 -#37|Nightshade Dryad|C|MH3 -#38|Titans' Vanguard|U|MH3 -#39|Riddle Gate Gargoyle|C|MH3 -#40|Toxic Deluge|R|MH3 -#41|Mandibular Kite|C|MH3 -#42|Roil Cartographer|U|MH3 -#43|Serum Visionary|C|MH3 -#44|Six|R|MH3 -#45|Genku, Future Shaper|R|MH3 -#46|Glyph Elemental|U|MH3 -#47|Springheart Nantuko|R|MH3 -#48|Party Thrasher|R|MH3 -#49|Eldrazi Linebreaker|R|MH3 -#50|Ral and the Implicit Maze|U|MH3 -#51|Sneaky Snacker|C|MH3 -#52|Emrakul, the World Anew|M|MH3 -#53|Shadow of the Second Sun|M|MH3 -#54|Unfathomable Truths|C|MH3 -#55|Volatile Stormdrake|R|MH3 +#37|Springheart Nantuko|R|MH3 +#38|Party Thrasher|R|MH3 +#39|Nightshade Dryad|C|MH3 +#40|Sneaky Snacker|C|MH3 +#41|Riddle Gate Gargoyle|C|MH3 +#42|Toxic Deluge|R|MH3 +#43|Roil Cartographer|U|MH3 +#44|Serum Visionary|C|MH3 +#45|Detective's Phoenix|R|MH3 +#46|Six|R|MH3 +#47|Mindless Conscription|U|MH3 +#48|Eldrazi Linebreaker|R|MH3 +#49|Ral and the Implicit Maze|U|MH3 +#50|Mandibular Kite|C|MH3 +#51|Brainsurge|U|MH3 +#52|Shadow of the Second Sun|M|MH3 +#53|Unfathomable Truths|C|MH3 +#54|Volatile Stormdrake|R|MH3 +#55|Emperor of Bones|R|MH3 #56|Collective Resistance|U|MH3 -#57|Wastescape Battlemage|U|MH3 -#58|Mindless Conscription|U|MH3 -#59|Deem Inferior|C|MH3 -#60|Brainsurge|U|MH3 -#61|Hope-Ender Coatl|U|MH3 -#62|Emperor of Bones|R|MH3 +#57|Genku, Future Shaper|R|MH3 +#58|Wastescape Battlemage|U|MH3 +#59|Glyph Elemental|U|MH3 +#60|Deem Inferior|C|MH3 +#61|Emrakul, the World Anew|M|MH3 +#62|Hope-Ender Coatl|U|MH3 #63|Wither and Bloom|C|MH3 #64|Glimpse the Impossible|C|MH3 #65|Ajani Fells the Godsire|U|MH3 #66|Horrid Shadowspinner|U|MH3 -#67|Nulldrifter|R|MH3 -#68|Metastatic Evangel|U|MH3 -#69|Proud Pack-Rhino|U|MH3 -#70|Thraben Charm|C|MH3 -#71|Voltstorm Angel|U|MH3 -#72|Flare of Malice|R|MH3 -#73|Refurbished Familiar|C|MH3 -#74|Basking Broodscale|C|MH3 -#75|Witch Enchanter|U|MH3 -#76|Unstable Amulet|U|MH3 -#77|Abstruse Appropriation|R|MH3 -#78|Territory Culler|U|MH3 -#79|Solar Transformer|U|MH3 -#80|Kozilek, the Broken Reality|M|MH3 -#81|Twisted Riddlekeeper|U|MH3 +#67|Thraben Charm|C|MH3 +#68|Voltstorm Angel|U|MH3 +#69|Flare of Malice|R|MH3 +#70|Refurbished Familiar|C|MH3 +#71|Basking Broodscale|C|MH3 +#72|Witch Enchanter|U|MH3 +#73|Unstable Amulet|U|MH3 +#74|Abstruse Appropriation|R|MH3 +#75|Recruiter of the Guard|M|MH3 +#76|Devourer of Destiny|R|MH3 +#77|Kozilek, the Broken Reality|M|MH3 +#78|Nulldrifter|R|MH3 +#79|Twisted Riddlekeeper|U|MH3 +#80|Metastatic Evangel|U|MH3 +#81|Proud Pack-Rhino|U|MH3 #82|Solstice Zealot|C|MH3 #83|Accursed Marauder|C|MH3 -#84|Reckless Pyrosurfer|U|MH3 -#85|Smelted Chargebug|C|MH3 -#86|Wumpus Aberration|U|MH3 -#87|Bridgeworks Battle|U|MH3 -#88|Legion Leadership|U|MH3 -#89|Revitalizing Repast|U|MH3 -#90|Ghostfire Slice|U|MH3 -#91|Propagator Drone|U|MH3 -#92|Bountiful Landscape|C|MH3 -#93|Sheltering Landscape|C|MH3 -#94|Annoyed Altisaur|U|MH3 -#95|Devourer of Destiny|R|MH3 -#96|Glaring Fleshraker|U|MH3 -#97|Aether Spike|C|MH3 -#98|Petrifying Meddler|C|MH3 -#99|Breathe Your Last|C|MH3 -#100|Chthonian Nightmare|R|MH3 -#101|Scurrilous Sentry|C|MH3 -#102|Wurmcoil Larva|U|MH3 -#103|Fanged Flames|C|MH3 -#104|Cranial Ram|C|MH3 -#105|Sundering Eruption|U|MH3 -#106|Disciple of Freyalise|U|MH3 -#107|Drowner of Truth|U|MH3 -#108|Stump Stomp|U|MH3 -#109|Emrakul's Messenger|U|MH3 -#110|Nyxborn Hydra|C|MH3 -#111|Invert Polarity|R|MH3 -#112|Etherium Pteramander|U|MH3 -#113|Perilous Landscape|C|MH3 -#114|Pinnacle Monk|U|MH3 -#115|Faithful Watchdog|C|MH3 -#116|Recruiter of the Guard|M|MH3 +#84|Chthonian Nightmare|R|MH3 +#85|Wurmcoil Larva|U|MH3 +#86|Reckless Pyrosurfer|U|MH3 +#87|Smelted Chargebug|C|MH3 +#88|Wumpus Aberration|U|MH3 +#89|Legion Leadership|U|MH3 +#90|Revitalizing Repast|U|MH3 +#91|Ghostfire Slice|U|MH3 +#92|Propagator Drone|U|MH3 +#93|Territory Culler|U|MH3 +#94|Solar Transformer|U|MH3 +#95|Bountiful Landscape|C|MH3 +#96|Sheltering Landscape|C|MH3 +#97|Annoyed Altisaur|U|MH3 +#98|Glaring Fleshraker|U|MH3 +#99|Dog Umbra|C|MH3 +#100|Pearl-Ear, Imperial Advisor|R|MH3 +#101|Aether Spike|C|MH3 +#102|Breathe Your Last|C|MH3 +#103|Scurrilous Sentry|C|MH3 +#104|Fanged Flames|C|MH3 +#105|Cranial Ram|C|MH3 +#106|Spymaster's Vault|R|MH3 +#107|Sorin of House Markov|M|MH3 +#108|Bridgeworks Battle|U|MH3 +#109|Disciple of Freyalise|U|MH3 +#110|Stump Stomp|U|MH3 +#111|Emrakul's Messenger|U|MH3 +#112|Nyxborn Hydra|C|MH3 +#113|Invert Polarity|R|MH3 +#114|Etherium Pteramander|U|MH3 +#115|Perilous Landscape|C|MH3 +#116|Faithful Watchdog|C|MH3 #117|Deep Analysis|U|MH3 -#118|Sylvan Safekeeper|R|MH3 -#119|Thought-Knot Seer|M|MH3 -#120|Breaker of Creation|U|MH3 -#121|Warped Tusker|C|MH3 -#122|Dog Umbra|C|MH3 -#123|Hexgold Slith|C|MH3 -#124|Pearl-Ear, Imperial Advisor|R|MH3 -#125|Wrath of the Skies|R|MH3 -#126|Tune the Narrative|C|MH3 -#127|Mogg Mob|U|MH3 +#118|Expressive Iteration|M|SPG +#119|Breaker of Creation|U|MH3 +#120|Warped Tusker|C|MH3 +#121|Hexgold Slith|C|MH3 +#122|Petrifying Meddler|C|MH3 +#123|Tempest Harvester|C|MH3 +#124|Tune the Narrative|C|MH3 +#125|Kami of Jealous Thirst|C|MH3 +#126|Nethergoyf|M|MH3 +#127|Inventor's Axe|C|MH3 #128|Siege Smash|C|MH3 -#129|Shifting Woodland|R|MH3 -#130|Spymaster's Vault|R|MH3 -#131|Wooded Foothills|R|MH3 -#132|Fell the Profane|U|MH3 -#133|Sorin of House Markov|M|MH3 -#134|Glasswing Grace|U|MH3 -#135|Rush of Inspiration|U|MH3 -#136|Strength of the Harvest|U|MH3 -#137|Suppression Ray|U|MH3 -#138|Reiterating Bolt|U|MH3 -#139|Skittering Precursor|U|MH3 -#140|Foreboding Landscape|C|MH3 -#141|Seething Landscape|C|MH3 -#142|Tranquil Landscape|C|MH3 -#143|Twisted Landscape|C|MH3 -#144|Arena of Glory|R|MH3 +#129|Arna Kennerüd, Skycaptain|M|MH3 +#130|Fell the Profane|U|MH3 +#131|Sundering Eruption|U|MH3 +#132|Drowner of Truth|U|MH3 +#133|Glasswing Grace|U|MH3 +#134|Rush of Inspiration|U|MH3 +#135|Strength of the Harvest|U|MH3 +#136|Eviscerator's Insight|C|MH3 +#137|Skittering Precursor|U|MH3 +#138|Foreboding Landscape|C|MH3 +#139|Seething Landscape|C|MH3 +#140|Tranquil Landscape|C|MH3 +#141|Twisted Landscape|C|MH3 +#142|Pinnacle Monk|U|MH3 +#143|Arena of Glory|R|MH3 +#144|Sylvan Safekeeper|R|MH3 #145|Sevinne's Reclamation|R|MH3 -#146|Cayth, Famed Mechanist|M|MH3 +#146|Thought-Knot Seer|M|SPG #147|Jolted Awake|C|MH3 -#148|Amphibian Downpour|R|MH3 -#149|Flare of Denial|R|MH3 -#150|Tempest Harvester|C|MH3 -#151|Kami of Jealous Thirst|C|MH3 -#152|Inventor's Axe|C|MH3 -#153|Skoa, Embermage|C|MH3 -#154|Gift of the Viper|C|MH3 -#155|Arna Kennerüd, Skycaptain|M|MH3 -#156|Wight of the Reliquary|R|MH3 -#157|Eviscerator's Insight|C|MH3 -#158|Dreamtide Whale|R|MH3 -#159|Cursed Wombat|U|MH3 -#160|Cyclops Superconductor|C|MH3 -#161|Contaminated Landscape|C|MH3 -#162|Shattered Landscape|C|MH3 -#163|Breya, Etherium Shaper|M|MH3 -#164|Endurance|M|MH3 -#165|Drownyard Lurker|C|MH3 +#148|Wrath of the Skies|R|MH3 +#149|Amphibian Downpour|R|MH3 +#150|Flare of Denial|R|MH3 +#151|Mogg Mob|U|MH3 +#152|Skoa, Embermage|C|MH3 +#153|Gift of the Viper|C|MH3 +#154|Wight of the Reliquary|R|MH3 +#155|Wooded Foothills|R|MH3 +#156|Suppression Ray|U|MH3 +#157|Reiterating Bolt|U|MH3 +#158|Lethal Throwdown|U|MH3 +#159|Consuming Corruption|U|MH3 +#160|Dreamtide Whale|R|MH3 +#161|Shilgengar, Sire of Famine|R|MH3 +#162|Contaminated Landscape|C|MH3 +#163|Shattered Landscape|C|MH3 +#164|Cayth, Famed Mechanist|M|M3C +#165|Endurance|M|SPG #166|It That Heralds the End|U|MH3 -#167|Envoy of the Ancestors|U|MH3 -#168|Indebted Spirit|U|MH3 -#169|Inspired Inventor|C|MH3 -#170|Wing It|C|MH3 -#171|Bespoke Battlewagon|U|MH3 -#172|Molten Gatekeeper|C|MH3 -#173|Evolution Witness|C|MH3 -#174|Temperamental Oozewagg|C|MH3 -#175|Bloodstained Mire|R|MH3 -#176|Flooded Strand|R|MH3 -#177|Ugin's Labyrinth|M|MH3 -#178|Sink into Stupor|U|MH3 +#167|Indebted Spirit|U|MH3 +#168|Inspired Inventor|C|MH3 +#169|Wing It|C|MH3 +#170|Bespoke Battlewagon|U|MH3 +#171|Molten Gatekeeper|C|MH3 +#172|Temperamental Oozewagg|C|MH3 +#173|Bloodstained Mire|R|MH3 +#174|Flooded Strand|R|MH3 +#175|Shifting Woodland|R|MH3 +#176|Ugin's Labyrinth|M|MH3 +#177|Sink into Stupor|U|MH3 +#178|Waterlogged Teachings|U|MH3 #179|Nyxborn Unicorn|C|MH3 #180|Aether Revolt|R|MH3 -#181|Lethal Throwdown|U|MH3 -#182|Colossal Dreadmask|C|MH3 -#183|Utter Insignificance|C|MH3 -#184|Consuming Corruption|U|MH3 -#185|Shilgengar, Sire of Famine|R|MH3 -#186|Deceptive Landscape|C|MH3 -#187|Emissary of Soulfire|U|MH3 -#188|Nethergoyf|M|MH3 -#189|Retrofitted Transmogrant|C|MH3 -#190|Infernal Captor|C|MH3 +#181|Colossal Dreadmask|C|MH3 +#182|Utter Insignificance|C|MH3 +#183|Cursed Wombat|U|MH3 +#184|Cyclops Superconductor|C|MH3 +#185|Emissary of Soulfire|U|MH3 +#186|Kappa Cannoneer|R|MH3 +#187|Breya, Etherium Shaper|M|MH3 +#188|Drownyard Lurker|C|MH3 +#189|Envoy of the Ancestors|U|MH3 +#190|Retrofitted Transmogrant|C|MH3 #191|Sarpadian Simulacrum|C|MH3 #192|Voidpouncer|C|MH3 -#193|Bloodsoaked Insight|U|MH3 -#194|Waterlogged Teachings|U|MH3 +#193|Evolution Witness|C|MH3 +#194|Bloodsoaked Insight|U|MH3 #195|Guardian of the Forgotten|U|MH3 -#196|Kappa Cannoneer|R|MH3 +#196|Deceptive Landscape|C|MH3 #197|Priest of Titania|U|MH3 -#198|Herigast, Erupting Nullkite|M|MH3 -#199|White Orchid Phantom|R|MH3 -#200|Dreamdrinker Vampire|C|MH3 -#201|Razorgrass Ambush|U|MH3 -#202|Monumental Henge|R|MH3 -#203|Electrozoa|C|MH3 -#204|Snapping Voidcraw|C|MH3 -#205|Subtlety|M|MH3 -#206|Copycrook|U|MH3 -#207|Kozilek's Unsealing|U|MH3 -#208|Grim Servant|U|MH3 -#209|Phyrexian Ironworks|U|MH3 -#210|Thriving Skyclaw|C|MH3 -#211|Kudo, King Among Bears|R|MH3 -#212|Polluted Delta|R|MH3 -#213|Windswept Heath|R|MH3 -#214|Grist, Voracious Larva|M|MH3 -#215|Pyretic Rebirth|U|MH3 -#216|Hydroelectric Specimen|U|MH3 -#217|Barbarian Ring|U|MH3 -#218|Muster the Departed|U|MH3 -#219|Rosecot Knight|C|MH3 -#220|Rosheen, Roaring Prophet|R|MH3 -#221|Birthing Ritual|M|MH3 -#222|Planar Genesis|U|MH3 -#223|Boggart Trawler|U|MH3 -#224|Etched Slith|U|MH3 -#225|Fowl Strike|C|MH3 -#226|Lion Umbra|U|MH3 +#198|Subtlety|M|SPG +#199|Herigast, Erupting Nullkite|M|MH3 +#200|White Orchid Phantom|R|MH3 +#201|Copycrook|U|MH3 +#202|Dreamdrinker Vampire|C|MH3 +#203|Infernal Captor|C|MH3 +#204|Thriving Skyclaw|C|MH3 +#205|Rosheen, Roaring Prophet|R|MH3 +#206|Windswept Heath|R|MH3 +#207|Razorgrass Ambush|U|MH3 +#208|Grist, Voracious Larva|M|MH3 +#209|Monumental Henge|R|MH3 +#210|Electrozoa|C|MH3 +#211|Snapping Voidcraw|C|MH3 +#212|Barbarian Ring|U|MH3 +#213|Grim Servant|U|MH3 +#214|Kudo, King Among Bears|R|MH3 +#215|Polluted Delta|R|MH3 +#216|Pyretic Rebirth|U|MH3 +#217|Planar Genesis|U|MH3 +#218|Boggart Trawler|U|MH3 +#219|Hydroelectric Specimen|U|MH3 +#220|Muster the Departed|U|MH3 +#221|Rosecot Knight|C|MH3 +#222|Kozilek's Unsealing|U|MH3 +#223|Phyrexian Ironworks|U|MH3 +#224|Eladamri, Korvecdal|M|MH3 +#225|Etched Slith|U|MH3 +#226|Fowl Strike|C|MH3 #227|Angel of the Ruins|U|MH3 #228|Reef Worm|U|MH3 #229|Ulamog, the Defiler|M|MH3 #230|Expel the Unworthy|C|MH3 -#231|Drossclaw|C|MH3 -#232|Eladamri, Korvecdal|M|MH3 -#233|Fangs of Kalonia|U|MH3 -#234|Worn Powerstone|U|MH3 -#235|Cephalid Coliseum|U|MH3 -#236|Decree of Justice|U|MH3 -#237|Echoes of Eternity|R|MH3 -#238|Eldrazi Ravager|U|MH3 -#239|Flare of Fortitude|R|MH3 -#240|Sage of the Unknowable|C|MH3 -#241|Arcbound Condor|U|MH3 -#242|Warren Soultrader|R|MH3 -#243|Thief of Existence|R|MH3 -#244|Trickster's Elk|U|MH3 -#245|Fetid Gargantua|C|MH3 -#246|Ashling, Flame Dancer|M|MH3 -#247|Nesting Grounds|U|MH3 -#248|Disa the Restless|M|MH3 -#249|Expanding Ooze|C|MH3 -#250|Ral, Monsoon Mage|M|MH3 -#251|Furnace Hellkite|U|MH3 -#252|Buried Alive|U|MH3 -#253|Victimize|U|MH3 -#254|Omo, Queen of Vesuva|M|MH3 -#255|Essence Reliquary|U|MH3 -#256|Ondu Knotmaster|U|MH3 -#257|Obstinate Gargoyle|C|MH3 -#258|Null Elemental Blast|U|MH3 -#259|Argent Dais|R|MH3 -#260|Corrupted Shapeshifter|C|MH3 -#261|Strix Serenade|R|MH3 -#262|Tamiyo Meets the Story Circle|U|MH3 -#263|Triton Wavebreaker|U|MH3 -#264|Dreadmobile|U|MH3 -#265|Flare of Duplication|R|MH3 -#266|Frogmyr Enforcer|U|MH3 -#267|Wheel of Potential|R|MH3 -#268|Flare of Cultivation|R|MH3 -#269|Izzet Generatorium|U|MH3 -#270|The Necrobloom|R|MH3 -#271|Snow-Covered Wastes|U|MH3 -#272|Urza's Cave|U|MH3 -#273|The Hunger Tide Rises|U|MH3 -#274|Hydra Trainer|U|MH3 -#275|Golden-Tail Trainer|U|MH3 -#276|Imskir Iron-Eater|R|MH3 -#277|Archway of Innovation|R|MH3 -#278|Quest for the Necropolis|U|MH3 -#279|Vexing Bauble|U|MH3 -#280|Gravedig|C|MH3 -#281|Idol of False Gods|U|MH3 -#282|The Creation of Avacyn|U|MH3 -#283|Consign to Memory|U|MH3 -#284|Monstrous Vortex|U|MH3 -#285|Urza's Incubator|R|MH3 -#286|Branching Evolution|R|MH3 -#287|Meteoric Mace|U|MH3 -#288|Fledgling Dragon|U|MH3 -#289|Nadier's Nightblade|U|MH3 -#290|Shrieking Drake|U|MH3 -#291|Distinguished Conjurer|U|MH3 -#292|Phyrexian Tower|M|MH3 -#293|K'rrik, Son of Yawgmoth|R|MH3 -#294|Cursed Mirror|R|MH3 -#295|Deserted Temple|R|MH3 -#296|Junk Diver|U|MH3 -#297|Ulalek, Fused Atrocity|M|MH3 -#298|Coram, the Undertaker|M|MH3 -#299|Harbinger of the Seas|R|MH3 -#300|Powerbalance|R|MH3 -#301|Disruptor Flute|R|MH3 -#302|Winter Moon|R|MH3 -#303|Charitable Levy|U|MH3 -#304|Necrodominance|M|MH3 -#305|Ripples of Undeath|R|MH3 -#306|Primal Prayers|R|MH3 -#307|Orim's Chant|R|MH3 -#308|Estrid's Invocation|R|MH3 -#309|Meltdown|U|MH3 -#310|Wirewood Symbiote|U|MH3 -#311|Kaalia of the Vast|M|MH3 -#312|Emerald Medallion|R|MH3 -#313|Jet Medallion|R|MH3 -#314|Pearl Medallion|R|MH3 -#315|Ruby Medallion|R|MH3 -#316|Sapphire Medallion|R|MH3 -#317|Jyoti, Moag Ancient|M|MH3 -#318|Azlask, the Swelling Scourge|M|MH3 -#319|Prismatic Ending|M|MH3 -#320|Persist|M|MH3 -#321|Expressive Iteration|M|MH3 +#231|Flare of Fortitude|R|MH3 +#232|Drossclaw|C|MH3 +#233|Fetid Gargantua|C|MH3 +#234|Birthing Ritual|M|MH3 +#235|Fangs of Kalonia|U|MH3 +#236|Ashling, Flame Dancer|M|MH3 +#237|Lion Umbra|U|MH3 +#238|Nesting Grounds|U|MH3 +#239|Worn Powerstone|U|MH3 +#240|Cephalid Coliseum|U|MH3 +#241|Decree of Justice|U|MH3 +#242|Disa the Restless|M|M3C +#243|Echoes of Eternity|R|MH3 +#244|Eldrazi Ravager|U|MH3 +#245|Sage of the Unknowable|C|MH3 +#246|Arcbound Condor|U|MH3 +#247|Warren Soultrader|R|MH3 +#248|Thief of Existence|R|MH3 +#249|Trickster's Elk|U|MH3 +#250|Expanding Ooze|C|MH3 +#251|Ral, Monsoon Mage|M|MH3 +#252|Phyrexian Tower|M|MH3 +#253|Essence Reliquary|U|MH3 +#254|The Hunger Tide Rises|U|MH3 +#255|Ondu Knotmaster|U|MH3 +#256|Gravedig|C|MH3 +#257|Furnace Hellkite|U|MH3 +#258|Consign to Memory|U|MH3 +#259|Obstinate Gargoyle|C|MH3 +#260|Victimize|U|MH3 +#261|Null Elemental Blast|U|MH3 +#262|Argent Dais|R|MH3 +#263|Corrupted Shapeshifter|C|MH3 +#264|Strix Serenade|R|MH3 +#265|Tamiyo Meets the Story Circle|U|MH3 +#266|Triton Wavebreaker|U|MH3 +#267|Dreadmobile|U|MH3 +#268|Flare of Duplication|R|MH3 +#269|Frogmyr Enforcer|U|MH3 +#270|Wheel of Potential|R|MH3 +#271|Flare of Cultivation|R|MH3 +#272|Izzet Generatorium|U|MH3 +#273|The Necrobloom|R|MH3 +#274|Snow-Covered Wastes|U|MH3 +#275|Urza's Cave|U|MH3 +#276|Necrodominance|M|MH3 +#277|Ripples of Undeath|R|MH3 +#278|Hydra Trainer|U|MH3 +#279|Golden-Tail Trainer|U|MH3 +#280|Imskir Iron-Eater|R|MH3 +#281|Archway of Innovation|R|MH3 +#282|Quest for the Necropolis|U|MH3 +#283|Vexing Bauble|U|MH3 +#284|Idol of False Gods|U|MH3 +#285|The Creation of Avacyn|U|MH3 +#286|Monstrous Vortex|U|MH3 +#287|Urza's Incubator|R|MH3 +#288|Branching Evolution|R|MH3 +#289|Meteoric Mace|U|MH3 +#290|Fledgling Dragon|U|MH3 +#291|Nadier's Nightblade|U|MH3 +#292|Shrieking Drake|U|MH3 +#293|Distinguished Conjurer|U|MH3 +#294|Buried Alive|U|MH3 +#295|K'rrik, Son of Yawgmoth|R|MH3 +#296|Cursed Mirror|R|MH3 +#297|Deserted Temple|R|MH3 +#298|Junk Diver|U|MH3 +#299|Ulalek, Fused Atrocity|M|M3C +#300|Omo, Queen of Vesuva|M|M3C +#301|Coram, the Undertaker|M|M3C +#302|Azlask, the Swelling Scourge|M|M3C +#303|Harbinger of the Seas|R|MH3 +#304|Powerbalance|R|MH3 +#305|Disruptor Flute|R|MH3 +#306|Winter Moon|R|MH3 +#307|Charitable Levy|U|MH3 +#308|Primal Prayers|R|MH3 +#309|Orim's Chant|R|MH3 +#310|Estrid's Invocation|R|MH3 +#311|Meltdown|U|MH3 +#312|Wirewood Symbiote|U|MH3 +#313|Kaalia of the Vast|M|MH3 +#314|Emerald Medallion|R|MH3 +#315|Jet Medallion|R|MH3 +#316|Pearl Medallion|R|MH3 +#317|Ruby Medallion|R|MH3 +#318|Sapphire Medallion|R|MH3 +#319|Jyoti, Moag Ancient|M|M3C +#320|Prismatic Ending|M|SPG +#321|Persist|M|SPG #322|Plains|C|MH3 #323|Island|C|MH3 #324|Swamp|C|MH3 diff --git a/forge-gui/res/draft/rankings/otj.rnk b/forge-gui/res/draft/rankings/otj.rnk index 1135f91229f..22eb6cd169d 100644 --- a/forge-gui/res/draft/rankings/otj.rnk +++ b/forge-gui/res/draft/rankings/otj.rnk @@ -1,334 +1,334 @@ //Rank|Name|Rarity|Set #1|Bonny Pall, Clearcutter|R|OTJ -#2|Overwhelming Forces|M|OTJ -#3|Bristly Bill, Spine Sower|M|OTJ -#4|Railway Brawler|M|OTJ -#5|Roxanne, Starfall Savant|R|OTJ -#6|Seraphic Steed|R|OTJ -#7|Vaultborn Tyrant|M|BIG -#8|Bristlebud Farmer|M|BIG -#9|Ornery Tumblewagg|R|OTJ -#10|Rakdos, the Muscle|M|OTJ -#11|Fractured Identity|M|OTP -#12|Oko, Thief of Crowns|M|OTP -#13|Hostile Investigator|M|BIG -#14|Collector's Cage|M|BIG -#15|Final Showdown|M|OTJ -#16|Gisa, the Hellraiser|M|OTJ -#17|Outcaster Greenblade|U|OTJ -#18|Lassoed by the Law|U|OTJ -#19|Colossal Rattlewurm|R|OTJ -#20|Selvala, Eager Trailblazer|M|OTJ -#21|Sandstorm Salvager|M|BIG -#22|Hellspur Posse Boss|R|OTJ +#2|Railway Brawler|M|OTJ +#3|Overwhelming Forces|M|OTP +#4|Vaultborn Tyrant|M|BIG +#5|Bristly Bill, Spine Sower|M|OTJ +#6|Rakdos, the Muscle|M|OTJ +#7|Roxanne, Starfall Savant|R|OTJ +#8|Seraphic Steed|R|OTJ +#9|Oko, Thief of Crowns|M|OTP +#10|Bristlebud Farmer|M|BIG +#11|Ornery Tumblewagg|R|OTJ +#12|Fractured Identity|M|OTP +#13|Collector's Cage|M|BIG +#14|Hostile Investigator|M|BIG +#15|Gisa, the Hellraiser|M|OTJ +#16|Outcaster Greenblade|U|OTJ +#17|Selvala, Eager Trailblazer|M|OTJ +#18|Hellspur Posse Boss|R|OTJ +#19|Sandstorm Salvager|M|BIG +#20|Harvester of Misery|M|BIG +#21|Lassoed by the Law|U|OTJ +#22|Colossal Rattlewurm|R|OTJ #23|Spinewoods Armadillo|U|OTJ -#24|Wylie Duke, Atiin Hero|R|OTJ +#24|Bruse Tarl, Roving Rancher|R|OTJ #25|Dust Animus|R|OTJ -#26|Pest Infestation|R|OTJ -#27|Harvester of Misery|M|BIG -#28|Hollow Marauder|U|OTJ -#29|Bruse Tarl, Roving Rancher|R|OTJ -#30|Buried in the Garden|U|OTP -#31|Rush of Dread|R|OTJ -#32|The Gitrog, Ravenous Ride|M|OTJ -#33|Miriam, Herd Whisperer|U|OTJ -#34|Throw from the Saddle|C|OTJ +#26|Pest Infestation|R|OTP +#27|Cruel Ultimatum|R|OTP +#28|Final Showdown|M|OTJ +#29|The Gitrog, Ravenous Ride|M|OTJ +#30|Stoic Sphinx|R|OTJ +#31|Hollow Marauder|U|OTJ +#32|Rush of Dread|R|OTJ +#33|Wylie Duke, Atiin Hero|R|OTJ +#34|Buried in the Garden|U|OTP #35|Shepherd of the Clouds|U|OTJ -#36|Goldvein Hydra|M|OTJ +#36|Terror of the Peaks|M|OTJ #37|Outcaster Trailblazer|R|OTJ #38|Annie Flash, the Veteran|M|OTJ -#39|Honest Rutstein|U|OTJ -#40|Morbid Opportunist|M|OTJ -#41|Primal Might|R|OTP +#39|Miriam, Herd Whisperer|U|OTJ +#40|Throw from the Saddle|C|OTJ +#41|Mana Drain|M|OTP #42|Vanishing Verse|R|OTP -#43|Vadmir, New Blood|R|OTJ -#44|Terror of the Peaks|M|OTJ -#45|Beastbond Outcaster|U|OTJ -#46|Trash the Town|U|OTJ -#47|Congregation Gryff|U|OTJ -#48|Laughing Jasper Flint|R|OTJ -#49|Ruthless Lawbringer|U|OTJ -#50|Cruel Ultimatum|R|OTP -#51|Getaway Glamer|U|OTJ -#52|Stoic Sphinx|R|OTJ -#53|Caustic Bronco|R|OTJ -#54|Stingerback Terror|R|OTJ -#55|Stubborn Burrowfiend|U|OTJ -#56|Oko, the Ringleader|M|OTJ -#57|Journey to Nowhere|U|OTJ -#58|Mana Drain|M|OTP -#59|Clear Shot|U|OTP -#60|Fortune, Loyal Steed|R|OTJ -#61|Frontier Seeker|U|OTJ -#62|Prosperity Tycoon|U|OTJ -#63|Shoot the Sheriff|U|OTJ -#64|Aloe Alchemist|U|OTJ -#65|Take Up the Shield|C|OTJ -#66|Consuming Ashes|C|OTJ -#67|Snakeskin Veil|C|OTJ -#68|Brazen Borrower|M|SPG -#69|Leyline Binding|M|OTJ -#70|Primal Command|R|OTJ -#71|Back for More|U|OTJ -#72|Oltec Matterweaver|M|BIG -#73|Generous Plunderer|M|BIG -#74|Aven Interrupter|R|OTJ +#43|Getaway Glamer|U|OTJ +#44|Caustic Bronco|R|OTJ +#45|Stingerback Terror|R|OTJ +#46|Beastbond Outcaster|U|OTJ +#47|Goldvein Hydra|M|OTJ +#48|Honest Rutstein|U|OTJ +#49|Laughing Jasper Flint|R|OTJ +#50|Oko, the Ringleader|M|OTJ +#51|Brazen Borrower|M|SPG +#52|Primal Might|R|OTP +#53|Vadmir, New Blood|R|OTJ +#54|Trash the Town|U|OTJ +#55|Congregation Gryff|U|OTJ +#56|Ruthless Lawbringer|U|OTJ +#57|Journey to Nowhere|U|OTP +#58|Generous Plunderer|M|BIG +#59|Fortune, Loyal Steed|R|OTJ +#60|Prosperity Tycoon|U|OTJ +#61|Duelist of the Mind|R|OTJ +#62|Shoot the Sheriff|U|OTJ +#63|Aloe Alchemist|U|OTJ +#64|Stubborn Burrowfiend|U|OTJ +#65|Assimilation Aegis|M|OTJ +#66|Cactusfolk Sureshot|U|OTJ +#67|Take Up the Shield|C|OTJ +#68|Consuming Ashes|C|OTJ +#69|Clear Shot|U|OTP +#70|Humiliate|U|OTP +#71|Primal Command|R|OTP +#72|Outlaws' Merriment|R|OTP +#73|Aven Interrupter|R|OTJ +#74|Frontier Seeker|U|OTJ #75|Trained Arynx|C|OTJ -#76|Cactarantula|C|OTJ -#77|Intrepid Stablemaster|U|OTJ -#78|Assimilation Aegis|M|OTJ -#79|Cactusfolk Sureshot|U|OTJ -#80|Bucolic Ranch|U|OTJ -#81|Desert's Due|C|OTJ -#82|Spinewoods Paladin|C|OTJ -#83|Port Razer|M|OTJ -#84|Humiliate|U|OTJ -#85|Nexus of Becoming|M|OTJ -#86|Holy Cow|C|OTJ -#87|Mystical Tether|C|OTJ -#88|Prairie Dog|U|OTJ -#89|Marauding Sphinx|U|OTJ -#90|Desperate Bloodseeker|C|OTJ -#91|Unfortunate Accident|U|OTJ -#92|Betrayal at the Vault|U|OTJ -#93|Dance of the Tumbleweeds|C|OTJ -#94|Patient Naturalist|C|OTJ -#95|Smuggler's Surprise|R|OTJ -#96|Badlands Revival|U|OTJ -#97|Vault Plunderer|C|OTJ -#98|Creosote Heath|C|OTJ -#99|Festering Gulch|C|OTJ -#100|Abrupt Decay|R|OTJ -#101|Outlaws' Merriment|R|OTP -#102|Tyrant's Scorn|U|OTJ -#103|Lotus Ring|M|OTJ -#104|Duelist of the Mind|R|OTJ -#105|Three Steps Ahead|R|OTJ -#106|Lively Dirge|U|OTJ -#107|Servant of the Stinger|U|OTJ -#108|Jolene, Plundering Pugilist|U|OTJ -#109|Mobile Homestead|U|OTJ -#110|Sterling Supplier|C|OTJ -#111|Mourner's Surprise|C|OTJ -#112|Giant Beaver|C|OTJ -#113|Path to Exile|R|OTJ -#114|Archive Trap|R|OTJ -#115|Repulse|U|OTJ -#116|Murder|U|OTJ -#117|Crackle with Power|M|OTP -#118|Crime Punishment|M|OTP -#119|Void Rend|R|OTJ -#120|Sterling Keykeeper|C|OTJ -#121|Blood Hustler|U|OTJ -#122|Longhorn Sharpshooter|U|OTJ -#123|Bristlepack Sentry|C|OTJ -#124|Freestrider Lookout|R|OTJ -#125|Tumbleweed Rising|C|OTJ -#126|Ertha Jo, Frontier Mentor|U|OTJ -#127|Pillage the Bog|R|OTJ -#128|Ambush Gigapede|C|OTJ -#129|Drover Grizzly|C|OTJ -#130|Conduit Pylons|C|OTJ -#131|Forlorn Flats|C|OTJ -#132|Mystic Snake|M|OTJ -#133|Archangel of Tithes|M|OTJ -#134|Bounding Felidar|U|OTJ -#135|Geralf, the Fleshwright|M|OTJ -#136|Fake Your Own Death|C|OTJ -#137|Rooftop Assassin|C|OTJ -#138|Hardbristle Bandit|C|OTJ -#139|Rambling Possum|U|OTJ -#140|Kambal, Profiteering Mayor|R|OTJ -#141|Kellan, the Kid|M|OTJ -#142|Concealed Courtyard|R|OTJ -#143|Stagecoach Security|C|OTJ -#144|Bristling Backwoods|C|OTJ -#145|Lush Oasis|C|OTJ -#146|Soured Springs|C|OTJ -#147|Fell the Mighty|R|OTP -#148|One Last Job|R|OTJ -#149|Metamorphic Blast|U|OTJ -#150|Rictus Robber|U|OTJ -#151|Tinybones, the Pickpocket|M|OTJ -#152|Calamity, Galloping Inferno|R|OTJ -#153|Scalestorm Summoner|U|OTJ -#154|Reach for the Sky|C|OTJ -#155|Voracious Varmint|C|OTJ -#156|Make Your Own Luck|U|OTJ -#157|Malcolm, the Eyes|R|OTJ -#158|Vraska, the Silencer|M|OTJ -#159|Sandstorm Verge|U|OTJ -#160|Outlaw Medic|C|OTJ -#161|Phantom Interference|C|OTJ -#162|Desertion|M|OTJ -#163|Essence Capture|U|OTJ +#76|Marauding Sphinx|U|OTJ +#77|Three Steps Ahead|R|OTJ +#78|Dance of the Tumbleweeds|C|OTJ +#79|Smuggler's Surprise|R|OTJ +#80|Desert's Due|C|OTJ +#81|Vault Plunderer|C|OTJ +#82|Snakeskin Veil|C|OTJ +#83|Spinewoods Paladin|C|OTJ +#84|Morbid Opportunist|M|SPG +#85|Archive Trap|R|OTP +#86|Crackle with Power|M|OTP +#87|Back for More|U|OTP +#88|Tyrant's Scorn|U|OTP +#89|Nexus of Becoming|M|BIG +#90|Oltec Matterweaver|M|BIG +#91|Holy Cow|C|OTJ +#92|Mystical Tether|C|OTJ +#93|Prairie Dog|U|OTJ +#94|Desperate Bloodseeker|C|OTJ +#95|Servant of the Stinger|U|OTJ +#96|Unfortunate Accident|U|OTJ +#97|Longhorn Sharpshooter|U|OTJ +#98|Betrayal at the Vault|U|OTJ +#99|Cactarantula|C|OTJ +#100|Intrepid Stablemaster|U|OTJ +#101|Patient Naturalist|C|OTJ +#102|Badlands Revival|U|OTJ +#103|Bucolic Ranch|U|OTJ +#104|Sterling Supplier|C|OTJ +#105|Festering Gulch|C|OTJ +#106|Desertion|M|SPG +#107|Leyline Binding|M|OTP +#108|Repulse|U|OTP +#109|Abrupt Decay|R|OTP +#110|Lotus Ring|M|BIG +#111|Geralf, the Fleshwright|M|OTJ +#112|Plan the Heist|U|OTJ +#113|Blood Hustler|U|OTJ +#114|Freestrider Lookout|R|OTJ +#115|Ertha Jo, Frontier Mentor|U|OTJ +#116|Jolene, Plundering Pugilist|U|OTJ +#117|Malcolm, the Eyes|R|OTJ +#118|Ambush Gigapede|C|OTJ +#119|Mourner's Surprise|C|OTJ +#120|Creosote Heath|C|OTJ +#121|Desert|M|SPG +#122|Path to Exile|R|OTP +#123|Murder|U|OTP +#124|Sterling Keykeeper|C|OTJ +#125|Lively Dirge|U|OTJ +#126|Hardbristle Bandit|C|OTJ +#127|Tumbleweed Rising|C|OTJ +#128|Mobile Homestead|U|OTJ +#129|Stagecoach Security|C|OTJ +#130|Phantom Interference|C|OTJ +#131|Giant Beaver|C|OTJ +#132|Conduit Pylons|C|OTJ +#133|Forlorn Flats|C|OTJ +#134|Lush Oasis|C|OTJ +#135|Soured Springs|C|OTJ +#136|Void Rend|R|OTP +#137|Archangel of Tithes|M|OTJ +#138|Bounding Felidar|U|OTJ +#139|Metamorphic Blast|U|OTJ +#140|Slickshot Lockpicker|U|OTJ +#141|Fake Your Own Death|C|OTJ +#142|Rooftop Assassin|C|OTJ +#143|Calamity, Galloping Inferno|R|OTJ +#144|Magda, the Hoardmaster|R|OTJ +#145|Scalestorm Summoner|U|OTJ +#146|Bristlepack Sentry|C|OTJ +#147|Reach for the Sky|C|OTJ +#148|Kambal, Profiteering Mayor|R|OTJ +#149|Kellan, the Kid|M|OTJ +#150|Make Your Own Luck|U|OTJ +#151|Pillage the Bog|R|OTJ +#152|Slick Sequence|U|OTJ +#153|Jailbreak Scheme|C|OTJ +#154|Take the Fall|C|OTJ +#155|Drover Grizzly|C|OTJ +#156|Bristling Backwoods|C|OTJ +#157|Mystic Snake|M|SPG +#158|Endless Detour|R|OTP +#159|Siphon Insight|R|OTP +#160|Fell the Mighty|R|OTP +#161|Archmage's Charm|R|OTP +#162|Essence Capture|U|OTP +#163|Collective Defiance|R|OTP #164|Lost Jitte|M|BIG -#165|Bridled Bighorn|C|OTJ -#166|Vengeful Townsfolk|C|OTJ +#165|Nurturing Pixie|U|OTJ +#166|One Last Job|R|OTJ #167|Wanted Griffin|C|OTJ -#168|Double Down|M|OTJ -#169|Plan the Heist|U|OTJ -#170|Slickshot Lockpicker|U|OTJ -#171|Binding Negotiation|U|OTJ -#172|Magda, the Hoardmaster|R|OTJ -#173|Scorching Shot|U|OTJ -#174|Rise of the Varmints|U|OTJ -#175|Slick Sequence|U|OTJ -#176|Redrock Sentinel|U|OTJ -#177|Jailbreak Scheme|C|OTJ -#178|Take the Fall|C|OTJ -#179|Nezumi Linkbreaker|C|OTJ -#180|Mirage Mesa|C|OTJ -#181|Abraded Bluffs|C|OTJ -#182|Jagged Barrens|C|OTJ -#183|Endless Detour|R|OTP -#184|Villainous Wealth|R|OTP -#185|Fierce Retribution|U|OTJ -#186|Archmage's Charm|R|OTJ -#187|Reanimate|R|OTJ -#188|Collective Defiance|R|OTP -#189|Detention Sphere|R|OTJ -#190|Claim Jumper|R|OTJ -#191|Nurturing Pixie|U|OTJ -#192|Forsaken Miner|U|OTJ -#193|Unscrupulous Contractor|U|OTJ -#194|Explosive Derailment|C|OTJ -#195|Great Train Heist|R|OTJ -#196|Full Steam Ahead|U|OTJ -#197|Baron Bertram Graywater|U|OTJ -#198|Form a Posse|U|OTJ -#199|Intimidation Campaign|U|OTJ -#200|Blooming Marsh|R|OTJ -#201|Ankle Biter|C|OTJ -#202|Akul the Unrepentant|R|OTJ -#203|Lonely Arroyo|C|OTJ -#204|Heartless Pillage|U|OTP -#205|Siphon Insight|R|OTP -#206|Contagion Engine|M|OTP -#207|Omenport Vigilante|U|OTJ -#208|Requisition Raid|U|OTJ -#209|Steer Clear|C|OTJ -#210|Thunder Lasso|U|OTJ -#211|Outlaw Stitcher|U|OTJ -#212|This Town Ain't Big Enough|U|OTJ -#213|Rattleback Apothecary|U|OTJ -#214|Raven of Fell Omens|C|OTJ -#215|Treasure Dredger|U|OTJ -#216|Cunning Coyote|U|OTJ -#217|Discerning Peddler|C|OTJ -#218|Hellspur Brute|U|OTJ -#219|Raucous Entertainer|U|OTJ -#220|Annie Joins Up|R|OTJ -#221|Eriette, the Beguiler|R|OTJ -#222|Ghired, Mirror of the Wilds|M|OTJ -#223|Kraum, Violent Cacophony|U|OTJ -#224|Marchesa, Dealer of Death|R|OTJ -#225|Satoru, the Infiltrator|R|OTJ -#226|Vial Smasher, Gleeful Grenadier|U|OTJ -#227|Oasis Gardener|C|OTJ -#228|Skulduggery|C|OTJ -#229|Prickly Pair|C|OTJ -#230|Freestrider Commando|C|OTJ -#231|Eroded Canyon|C|OTJ -#232|Decisive Denial|U|OTP -#233|Savage Smash|U|OTP -#234|Loot, the Key to Everything|M|BIG -#235|Legion Extruder|M|OTJ -#236|Eriette's Lullaby|C|OTJ -#237|Canyon Crab|U|OTJ -#238|Loan Shark|C|OTJ -#239|Peerless Ropemaster|C|OTJ -#240|Insatiable Avarice|R|OTJ -#241|Neutralize the Guards|U|OTJ -#242|Gold Rush|U|OTJ -#243|Doc Aurlock, Grizzled Genius|U|OTJ -#244|Lazav, Familiar Stranger|U|OTJ -#245|Rakdos Joins Up|R|OTJ -#246|Bandit's Haul|U|OTJ -#247|Botanical Sanctum|R|OTJ -#248|Sterling Hound|C|OTJ -#249|Notion Thief|M|OTJ -#250|Electrodominance|R|OTP -#251|Skewer the Critics|U|OTJ -#252|Anguished Unmaking|M|OTP -#253|Bedevil|R|OTJ -#254|Terminal Agony|U|OTJ -#255|Sword of Wealth and Power|M|OTJ -#256|Tarnation Vista|M|OTJ -#257|Sheriff of Safe Passage|U|OTJ -#258|Archmage's Newt|R|OTJ -#259|Nimble Brigand|U|OTJ -#260|Visage Bandit|U|OTJ -#261|Demonic Ruckus|U|OTJ -#262|Gila Courser|U|OTJ -#263|Resilient Roadrunner|U|OTJ -#264|Gold Pan|C|OTJ -#265|Inspiring Vantage|R|OTJ -#266|Stop Cold|C|OTJ -#267|Corrupted Conviction|C|OTJ -#268|Thunder Salvo|C|OTJ -#269|Thoughtseize|M|OTJ -#270|Ride Down|U|OTJ -#271|Ionize|R|OTJ -#272|Rustler Rampage|U|OTJ -#273|Emergent Haunting|U|OTJ -#274|Failed Fording|C|OTJ -#275|Irascible Wolverine|C|OTJ -#276|Outlaws' Fury|C|OTJ -#277|Reckless Lackey|C|OTJ -#278|Seize the Secrets|C|OTJ -#279|Trick Shot|C|OTJ -#280|Stoneforge Mystic|M|OTJ -#281|Hypothesizzle|U|OTP -#282|Unlicensed Hearse|R|OTP -#283|Deepmuck Desperado|U|OTJ -#284|Quilled Charger|C|OTJ +#168|Binding Negotiation|U|OTJ +#169|Rictus Robber|U|OTJ +#170|Tinybones, the Pickpocket|M|OTJ +#171|Rambling Possum|U|OTJ +#172|Voracious Varmint|C|OTJ +#173|Vraska, the Silencer|M|OTJ +#174|Redrock Sentinel|U|OTJ +#175|Outlaw Medic|C|OTJ +#176|Nezumi Linkbreaker|C|OTJ +#177|Jagged Barrens|C|OTJ +#178|Crime Punishment|M|OTP +#179|Fierce Retribution|U|OTP +#180|Detention Sphere|R|OTP +#181|Claim Jumper|R|OTJ +#182|Omenport Vigilante|U|OTJ +#183|Vengeful Townsfolk|C|OTJ +#184|Double Down|M|OTJ +#185|Forsaken Miner|U|OTJ +#186|Cunning Coyote|U|OTJ +#187|Explosive Derailment|C|OTJ +#188|Scorching Shot|U|OTJ +#189|Rise of the Varmints|U|OTJ +#190|Form a Posse|U|OTJ +#191|Intimidation Campaign|U|OTJ +#192|Vial Smasher, Gleeful Grenadier|U|OTJ +#193|Sandstorm Verge|U|OTJ +#194|Concealed Courtyard|R|OTJ +#195|Mirage Mesa|C|OTJ +#196|Abraded Bluffs|C|OTJ +#197|Eroded Canyon|C|OTJ +#198|Lonely Arroyo|C|OTJ +#199|Villainous Wealth|R|OTP +#200|Ionize|R|OTP +#201|Bridled Bighorn|C|OTJ +#202|Requisition Raid|U|OTJ +#203|Canyon Crab|U|OTJ +#204|Loan Shark|C|OTJ +#205|Nimble Brigand|U|OTJ +#206|Peerless Ropemaster|C|OTJ +#207|This Town Ain't Big Enough|U|OTJ +#208|Rattleback Apothecary|U|OTJ +#209|Raven of Fell Omens|C|OTJ +#210|Unscrupulous Contractor|U|OTJ +#211|Great Train Heist|R|OTJ +#212|Hellspur Brute|U|OTJ +#213|Full Steam Ahead|U|OTJ +#214|Annie Joins Up|R|OTJ +#215|Baron Bertram Graywater|U|OTJ +#216|Ghired, Mirror of the Wilds|M|OTJ +#217|Kraum, Violent Cacophony|U|OTJ +#218|Lazav, Familiar Stranger|U|OTJ +#219|Rakdos Joins Up|R|OTJ +#220|Satoru, the Infiltrator|R|OTJ +#221|Oasis Gardener|C|OTJ +#222|Blooming Marsh|R|OTJ +#223|Botanical Sanctum|R|OTJ +#224|Prickly Pair|C|OTJ +#225|Ankle Biter|C|OTJ +#226|Akul the Unrepentant|R|OTJ +#227|Heartless Pillage|U|OTP +#228|Anguished Unmaking|M|OTP +#229|Decisive Denial|U|OTP +#230|Reanimate|R|OTP +#231|Contagion Engine|M|OTP +#232|Loot, the Key to Everything|M|BIG +#233|Legion Extruder|M|BIG +#234|Steer Clear|C|OTJ +#235|Thunder Lasso|U|OTJ +#236|Outlaw Stitcher|U|OTJ +#237|Treasure Dredger|U|OTJ +#238|Discerning Peddler|C|OTJ +#239|Gila Courser|U|OTJ +#240|Resilient Roadrunner|U|OTJ +#241|Marchesa, Dealer of Death|R|OTJ +#242|Bandit's Haul|U|OTJ +#243|Skulduggery|C|OTJ +#244|Freestrider Commando|C|OTJ +#245|Sterling Hound|C|OTJ +#246|Thoughtseize|M|OTP +#247|Skewer the Critics|U|OTP +#248|Bedevil|R|OTP +#249|Hypothesizzle|U|OTP +#250|Terminal Agony|U|OTP +#251|Tarnation Vista|M|BIG +#252|Eriette's Lullaby|C|OTJ +#253|Failed Fording|C|OTJ +#254|Visage Bandit|U|OTJ +#255|Insatiable Avarice|R|OTJ +#256|Demonic Ruckus|U|OTJ +#257|Gold Rush|U|OTJ +#258|Raucous Entertainer|U|OTJ +#259|Doc Aurlock, Grizzled Genius|U|OTJ +#260|Eriette, the Beguiler|R|OTJ +#261|Lilah, Undefeated Slickshot|R|OTJ +#262|Taii Wakeen, Perfect Shot|R|OTJ +#263|Inspiring Vantage|R|OTJ +#264|Seize the Secrets|C|OTJ +#265|Stop Cold|C|OTJ +#266|Thunder Salvo|C|OTJ +#267|Electrodominance|R|OTP +#268|Ride Down|U|OTP +#269|Savage Smash|U|OTP +#270|Sheriff of Safe Passage|U|OTJ +#271|Neutralize the Guards|U|OTJ +#272|Reckless Lackey|C|OTJ +#273|Gold Pan|C|OTJ +#274|Harrier Strix|C|OTJ +#275|Corrupted Conviction|C|OTJ +#276|Trick Shot|C|OTJ +#277|Prismatic Vista|M|SPG +#278|Unlicensed Hearse|R|OTP +#279|Sword of Wealth and Power|M|BIG +#280|Archmage's Newt|R|OTJ +#281|Deepmuck Desperado|U|OTJ +#282|Emergent Haunting|U|OTJ +#283|Irascible Wolverine|C|OTJ +#284|Outlaws' Fury|C|OTJ #285|Slickshot Show-Off|R|OTJ -#286|Take for a Ride|U|OTJ -#287|Arid Archway|U|OTJ -#288|Harrier Strix|C|OTJ -#289|Highway Robbery|C|OTJ -#290|Prismatic Vista|M|SPG -#291|Desert|M|OTJ -#292|Ancient Cornucopia|M|OTJ -#293|Armored Armadillo|C|OTJ -#294|Blacksnag Buzzard|C|OTJ -#295|Boneyard Desecrator|C|OTJ -#296|Kaervek, the Punisher|R|OTJ -#297|Rakish Crew|U|OTJ -#298|Hell to Pay|R|OTJ -#299|Mine Raider|C|OTJ -#300|Jem Lightfoote, Sky Explorer|U|OTJ -#301|Lilah, Undefeated Slickshot|R|OTJ -#302|Taii Wakeen, Perfect Shot|R|OTJ -#303|Wrangler of the Damned|U|OTJ -#304|Geyser Drake|C|OTJ -#305|Deadeye Duelist|C|OTJ -#306|Iron-Fist Pulverizer|C|OTJ -#307|Thornado|U|OTP -#308|Inventive Wingsmith|C|OTJ -#309|At Knifepoint|U|OTJ -#310|Jace Reawakened|M|OTJ -#311|Voidslime|R|OTJ -#312|Transmutation Font|M|OTJ -#313|Map the Frontier|U|OTJ -#314|Boom Box|U|OTJ -#315|Daring Thunder-Thief|C|OTJ -#316|Silver Deputy|C|OTJ -#317|Fling|U|OTP -#318|Mindslaver|M|OTP -#319|Greed's Gambit|M|OTJ -#320|Bovine Intervention|U|OTJ -#321|High Noon|R|OTJ -#322|Djinn of Fool's Fall|C|OTJ -#323|Fblthp, Lost on the Range|R|OTJ -#324|Fleeting Reflection|U|OTJ -#325|The Key to the Vault|R|OTJ -#326|Razzle-Dazzler|C|OTJ -#327|Shackle Slinger|U|OTJ -#328|Shifting Grift|U|OTJ -#329|Slickshot Vault-Buster|C|OTJ -#330|Spring Splasher|C|OTJ +#286|Spirebluff Canal|R|OTJ +#287|Highway Robbery|C|OTJ +#288|Iron-Fist Pulverizer|C|OTJ +#289|Rustler Rampage|U|OTJ +#290|Boneyard Desecrator|C|OTJ +#291|Kaervek, the Punisher|R|OTJ +#292|Rakish Crew|U|OTJ +#293|Hell to Pay|R|OTJ +#294|Mine Raider|C|OTJ +#295|Quilled Charger|C|OTJ +#296|Jem Lightfoote, Sky Explorer|U|OTJ +#297|Boom Box|U|OTJ +#298|Lavaspur Boots|U|OTJ +#299|Arid Archway|U|OTJ +#300|Geyser Drake|C|OTJ +#301|Armored Armadillo|C|OTJ +#302|Inventive Wingsmith|C|OTJ +#303|Blacksnag Buzzard|C|OTJ +#304|Take for a Ride|U|OTJ +#305|Wrangler of the Damned|U|OTJ +#306|Jace Reawakened|M|OTJ +#307|Deadeye Duelist|C|OTJ +#308|Fling|U|OTP +#309|Mindslaver|M|OTP +#310|Grand Abolisher|M|BIG +#311|Razzle-Dazzler|C|OTJ +#312|Map the Frontier|U|OTJ +#313|At Knifepoint|U|OTJ +#314|Daring Thunder-Thief|C|OTJ +#315|Thornado|U|OTP +#316|Transmutation Font|M|BIG +#317|Ancient Cornucopia|M|BIG +#318|Another Round|R|OTJ +#319|Bovine Intervention|U|OTJ +#320|High Noon|R|OTJ +#321|Djinn of Fool's Fall|C|OTJ +#322|Fblthp, Lost on the Range|R|OTJ +#323|Fleeting Reflection|U|OTJ +#324|The Key to the Vault|R|OTJ +#325|Shackle Slinger|U|OTJ +#326|Shifting Grift|U|OTJ +#327|Slickshot Vault-Buster|C|OTJ +#328|Spring Splasher|C|OTJ +#329|Step Between Worlds|R|OTJ +#330|Pitiless Carnage|R|OTJ #331|Tinybones Joins Up|R|OTJ #332|Brimstone Roundup|U|OTJ #333|Caught in the Crossfire|U|OTJ @@ -337,43 +337,43 @@ #336|Return the Favor|U|OTJ #337|Rodeo Pyromancers|C|OTJ #338|Breeches, the Blastmaker|R|OTJ -#339|Riku of Many Paths|R|OTJ -#340|Vraska Joins Up|R|OTJ -#341|Lavaspur Boots|U|OTJ +#339|Kellan Joins Up|R|OTJ +#340|Riku of Many Paths|R|OTJ +#341|Vraska Joins Up|R|OTJ #342|Luxurious Locomotive|U|OTJ #343|Tomb Trawler|U|OTJ -#344|Spirebluff Canal|R|OTJ -#345|Overzealous Muscle|C|OTJ -#346|Quick Draw|C|OTJ -#347|Pariah|R|OTJ -#348|Imp's Mischief|R|OTJ -#349|Skullcrack|R|OTJ -#350|Hindering Light|U|OTJ -#351|Grand Abolisher|M|OTJ -#352|Omenpath Journey|M|BIG -#353|Another Round|R|OTJ -#354|Step Between Worlds|R|OTJ -#355|Pitiless Carnage|R|OTJ -#356|Kellan Joins Up|R|OTJ -#357|Obeka, Splitter of Seconds|R|OTJ -#358|Scapeshift|M|OTJ -#359|Mindbreak Trap|M|OTP -#360|Surgical Extraction|R|OTJ -#361|Decimate|R|OTJ -#362|Grindstone|M|OTP -#363|Dust Bowl|R|OTJ -#364|Commandeer|R|OTJ -#365|Indomitable Creativity|M|OTJ +#344|Overzealous Muscle|C|OTJ +#345|Quick Draw|C|OTJ +#346|Silver Deputy|C|OTJ +#347|Notion Thief|M|SPG +#348|Voidslime|R|OTP +#349|Grindstone|M|OTP +#350|Pariah|R|OTP +#351|Imp's Mischief|R|OTP +#352|Skullcrack|R|OTP +#353|Hindering Light|U|OTP +#354|Greed's Gambit|M|BIG +#355|Omenpath Journey|M|BIG +#356|Obeka, Splitter of Seconds|R|OTJ +#357|Stoneforge Mystic|M|SPG +#358|Scapeshift|M|SPG +#359|Port Razer|M|SPG +#360|Mindbreak Trap|M|OTP +#361|Surgical Extraction|R|OTP +#362|Decimate|R|OTP +#363|Dust Bowl|R|OTP +#364|Commandeer|R|OTP +#365|Indomitable Creativity|M|OTP #366|Force of Vigor|M|OTP #367|Torpor Orb|M|BIG -#368|Simulacrum Synthesizer|M|OTJ -#369|Rest in Peace|M|OTJ -#370|Esoteric Duplicator|M|OTJ +#368|Simulacrum Synthesizer|M|BIG +#369|Rest in Peace|M|BIG +#370|Esoteric Duplicator|M|BIG #371|Worldwalker Helm|M|BIG -#372|Memory Vessel|M|OTJ +#372|Memory Vessel|M|BIG #373|Molten Duplication|M|BIG -#374|Pest Control|M|OTJ -#375|Fomori Vault|M|OTJ +#374|Pest Control|M|BIG +#375|Fomori Vault|M|BIG #376|Territory Forge|M|BIG #377|Plains|C|OTJ #378|Island|C|OTJ From 212f14063e0f93e386bbec5d09eb145e72ca8c74 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 24 Nov 2024 10:46:07 +0100 Subject: [PATCH 100/152] Miracle&Madness: use ManaCost instead of CardManaCost (#6606) * Miracle&Madness: use ManaCost instead of CardManaCost --- .../game/ability/effects/PlayEffect.java | 14 ++++---- .../src/main/java/forge/game/card/Card.java | 34 ++++++++++++++----- .../java/forge/game/card/CardFactoryUtil.java | 27 ++++++++++++--- .../StaticAbilityContinuous.java | 16 +-------- .../forge/game/trigger/TriggerExiled.java | 6 +++- .../cardsfolder/a/aminatou_veil_piercer.txt | 2 +- .../res/cardsfolder/f/falkenrath_gorger.txt | 2 +- 7 files changed, 63 insertions(+), 38 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java index 40f3dd2013c..ae2a778eaaf 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java @@ -91,6 +91,7 @@ public class PlayEffect extends SpellAbilityEffect { final boolean forget = sa.hasParam("ForgetPlayed"); final boolean hasTotalCMCLimit = sa.hasParam("WithTotalCMC"); final boolean altCost = sa.hasParam("WithoutManaCost") || sa.hasParam("PlayCost"); + final boolean altCostManaCost = "ManaCost".equals(sa.getParam("PlayCost")); int totalCMCLimit = Integer.MAX_VALUE; final Player controller; if (sa.hasParam("Controller")) { @@ -308,6 +309,10 @@ public class PlayEffect extends SpellAbilityEffect { sas.removeIf(sp -> !sp.isValid(valid, controller , source, sa)); } + if (altCostManaCost) { + sas.removeIf(sp -> sp.getPayCosts().getCostMana().getMana().isNoCost()); + } + if (hasTotalCMCLimit) { Iterator it = sas.iterator(); while (it.hasNext()) { @@ -380,11 +385,8 @@ public class PlayEffect extends SpellAbilityEffect { } else if (sa.hasParam("PlayCost")) { Cost abCost; String cost = sa.getParam("PlayCost"); - if (cost.equals("ManaCost")) { - if (unpayableCost) { - continue; - } - abCost = new Cost(source.getManaCost(), false); + if (altCostManaCost) { + abCost = new Cost(tgtSA.getCardState().getManaCost(), false); } else if (cost.equals("SuspendCost")) { abCost = Iterables.find(tgtCard.getNonManaAbilities(), s -> s.isKeyword(Keyword.SUSPEND)).getPayCosts(); } else { @@ -428,7 +430,7 @@ public class PlayEffect extends SpellAbilityEffect { tgtSA.putParam("RaiseCost", raise); } - if (sa.hasParam("Madness")) { + if (sa.isKeyword(Keyword.MADNESS)) { tgtSA.setAlternativeCost(AlternativeCost.Madness); } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 1f5819e494b..e2c6744ed08 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -38,6 +38,7 @@ import forge.game.cost.Cost; import forge.game.event.*; import forge.game.event.GameEventCardDamaged.DamageType; import forge.game.keyword.*; +import forge.game.mana.ManaCostBeingPaid; import forge.game.player.Player; import forge.game.player.PlayerCollection; import forge.game.replacement.*; @@ -2349,7 +2350,21 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr String[] k = keyword.split(":"); sbLong.append(k[0]); if (k.length > 1) { - final Cost mCost = new Cost(k[1], true); + final Cost mCost; + if ("ManaCost".equals(k[1])) { + ManaCost cost; + if (keyword.startsWith("Miracle") && k.length > 2) { + // TODO better handle 2 hybrid, these should not be reduced? + ManaCostBeingPaid mcbp = new ManaCostBeingPaid(getManaCost()); + mcbp.decreaseGenericMana(Integer.valueOf(k[2])); + cost = mcbp.toManaCost(); + } else { + cost = getManaCost(); + } + mCost = new Cost(cost, true); + } else { + mCost = new Cost(k[1], true); + } if (mCost.isOnlyManaCost()) { sbLong.append(" "); } else { @@ -2363,17 +2378,13 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr if (!mCost.isOnlyManaCost()) { sbLong.append("."); } - if (k.length > 2) { + if (k.length > 3) { sbLong.append(". " + k[3]); } } sbLong.append(" (").append(inst.getReminderText()).append(")"); sbLong.append("\r\n"); } - } else if (keyword.startsWith("Madness")) { - // If no colon exists in Madness keyword, it must have been granted and assumed the cost from host - sbLong.append("Madness ").append(this.getManaCost()).append(" (").append(inst.getReminderText()); - sbLong.append(")").append("\r\n"); } else if (keyword.startsWith("Reflect")) { final String[] k = keyword.split(":"); sbLong.append(k[0]).append(" ").append(ManaCostParser.parse(k[1])); @@ -3181,15 +3192,20 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr || keyword.startsWith("Disturb") || keyword.startsWith("Overload") || keyword.startsWith("Plot")) { final String[] k = keyword.split(":"); - final Cost cost = new Cost(k[1], false); + final Cost mCost; + if ("ManaCost".equals(k[1])) { + mCost = new Cost(getManaCost(), false); + } else { + mCost = new Cost(k[1], false); + } StringBuilder sbCost = new StringBuilder(k[0]); - if (!cost.isOnlyManaCost()) { + if (!mCost.isOnlyManaCost()) { sbCost.append("—"); } else { sbCost.append(" "); } - sbCost.append(cost.toSimpleString()); + sbCost.append(mCost.toSimpleString()); sbAfter.append(sbCost).append(" (").append(inst.getReminderText()).append(")"); sbAfter.append("\r\n"); } else if (keyword.equals("Gift")) { diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 4f9cc1b0551..b816fa39bc5 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -1460,12 +1460,17 @@ public class CardFactoryUtil { final String[] k = keyword.split(":"); final String manacost = k[1]; - final String trigStr = "Mode$ Exiled | ValidCard$ Card.Self | Madness$ True | Secondary$ True" - + " | TriggerDescription$ Play Madness " + ManaCostParser.parse(manacost) + " - " + card.getName(); + StringBuilder desc = new StringBuilder("Play Madness "); + if (!"ManaCost".equals(manacost)) { + desc.append(ManaCostParser.parse(manacost)).append(" "); + } + desc.append(" - " + card.getName()); + + final String trigStr = "Mode$ Exiled | ValidCard$ Card.Self | Secondary$ True | TriggerDescription$ " + desc.toString(); final String playMadnessStr = "DB$ Play | Defined$ Self | ValidSA$ Spell | PlayCost$ " + manacost + " | ConditionDefined$ Self | ConditionPresent$ Card.StrictlySelf+inZoneExile" + - " | Optional$ True | RememberPlayed$ True | Madness$ True"; + " | Optional$ True | RememberPlayed$ True"; final String moveToYardStr = "DB$ ChangeZone | Defined$ Self.StrictlySelf | Origin$ Exile" + " | Destination$ Graveyard | TrackDiscarded$ True | ConditionDefined$ Remembered | ConditionPresent$ Card" + @@ -1518,7 +1523,10 @@ public class CardFactoryUtil { final String manacost = k[1]; final String abStrReveal = "DB$ Reveal | Defined$ You | RevealDefined$ Self" + " | MiracleCost$ " + manacost; - final String abStrPlay = "DB$ Play | Defined$ Self | Optional$ True | PlayCost$ " + manacost; + String abStrPlay = "DB$ Play | Defined$ Self | Optional$ True | PlayCost$ " + manacost; + if (k.length >= 2) { + abStrPlay += " | PlayReduceCost$ " + k[2]; + } String revealed = "DB$ ImmediateTrigger | TriggerDescription$ CARDNAME - Miracle"; @@ -2387,8 +2395,17 @@ public class CardFactoryUtil { inst.addReplacement(re); } else if (keyword.startsWith("Madness")) { + final String[] k = keyword.split(":"); + final String manacost = k[1]; + + StringBuilder desc = new StringBuilder("Madness"); + if (!"ManaCost".equals(manacost)) { + desc.append(" ").append(ManaCostParser.parse(manacost)); + } + desc.append(": If you discard this card, discard it into exile."); + String repeffstr = "Event$ Moved | ActiveZones$ Hand | ValidCard$ Card.Self | Discard$ True | Secondary$ True " - + " | Description$ Madness: If you discard this card, discard it into exile."; + + " | Description$ " + desc; ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, host, intrinsic, card); String sVarMadness = "DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard"; diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index 6d379797868..56f523e25b6 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -23,7 +23,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import forge.GameCommand; import forge.card.*; -import forge.card.mana.ManaCost; import forge.game.Game; import forge.game.StaticEffect; import forge.game.StaticEffects; @@ -33,7 +32,6 @@ import forge.game.card.*; import forge.game.cost.Cost; import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; -import forge.game.mana.ManaCostBeingPaid; import forge.game.player.Player; import forge.game.player.PlayerCollection; import forge.game.replacement.ReplacementEffect; @@ -744,20 +742,8 @@ public final class StaticAbilityContinuous { newKeywords.addAll(extraKeywords); newKeywords = Lists.transform(newKeywords, input -> { - int reduced = 0; - if (stAb.hasParam("ReduceCost")) { - reduced = AbilityUtils.calculateAmount(hostCard, stAb.getParam("ReduceCost"), stAb); - } if (input.contains("CardManaCost")) { - ManaCost cost; - if (reduced > 0) { - ManaCostBeingPaid mcbp = new ManaCostBeingPaid(affectedCard.getManaCost()); - mcbp.decreaseGenericMana(reduced); - cost = mcbp.toManaCost(); - } else { - cost = affectedCard.getManaCost(); - } - input = input.replace("CardManaCost", cost.getShortString()); + input = input.replace("CardManaCost", affectedCard.getManaCost().getShortString()); } else if (input.contains("ConvertedManaCost")) { final String costcmc = Integer.toString(affectedCard.getCMC()); input = input.replace("ConvertedManaCost", costcmc); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerExiled.java b/forge-game/src/main/java/forge/game/trigger/TriggerExiled.java index 16ca81e2a73..3442157a1c6 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerExiled.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerExiled.java @@ -18,6 +18,7 @@ package forge.game.trigger; import java.util.Map; +import java.util.Objects; import org.apache.commons.lang3.ArrayUtils; @@ -84,10 +85,13 @@ public class TriggerExiled extends Trigger { return false; } - if (hasParam("Madness")) { + if (isKeyword(Keyword.MADNESS)) { if (cause == null || !cause.isKeyword(Keyword.MADNESS)) { return false; } + if (!Objects.equals(getKeyword().getStatic(), cause.getKeyword().getStatic())) { + return false; + } } return true; diff --git a/forge-gui/res/cardsfolder/a/aminatou_veil_piercer.txt b/forge-gui/res/cardsfolder/a/aminatou_veil_piercer.txt index 51c1de9eb4a..168340edbba 100644 --- a/forge-gui/res/cardsfolder/a/aminatou_veil_piercer.txt +++ b/forge-gui/res/cardsfolder/a/aminatou_veil_piercer.txt @@ -4,6 +4,6 @@ Types:Legendary Creature Human Wizard PT:2/4 T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigSurveil | TriggerDescription$ At the beginning of your upkeep, surveil 2. (Look at the top two cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.) SVar:TrigSurveil:DB$ Surveil | Amount$ 2 -S:Mode$ Continuous | Affected$ Card.Enchantment+YouOwn | AffectedZone$ Hand | AddKeyword$ Miracle:CardManaCost | ReduceCost$ 4 | Description$ Each enchantment card in your hand has miracle. Its miracle cost is equal to its mana cost reduced by {4}. (You may cast a card for its miracle cost when you draw it if it's the first card you drew this turn.) +S:Mode$ Continuous | Affected$ Card.Enchantment+YouOwn | AffectedZone$ Hand | AddKeyword$ Miracle:ManaCost:4 | Description$ Each enchantment card in your hand has miracle. Its miracle cost is equal to its mana cost reduced by {4}. (You may cast a card for its miracle cost when you draw it if it's the first card you drew this turn.) DeckHints:Ability$Graveyard & Type$Enchantment Oracle:At the beginning of your upkeep, surveil 2. (Look at the top two cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.)\nEach enchantment card in your hand has miracle. Its miracle cost is equal to its mana cost reduced by {4}. (You may cast a card for its miracle cost when you draw it if it's the first card you drew this turn.) diff --git a/forge-gui/res/cardsfolder/f/falkenrath_gorger.txt b/forge-gui/res/cardsfolder/f/falkenrath_gorger.txt index ce0fc265110..10250c706d8 100644 --- a/forge-gui/res/cardsfolder/f/falkenrath_gorger.txt +++ b/forge-gui/res/cardsfolder/f/falkenrath_gorger.txt @@ -2,5 +2,5 @@ Name:Falkenrath Gorger ManaCost:R Types:Creature Vampire Berserker PT:2/1 -S:Mode$ Continuous | Affected$ Creature.YouOwn+Vampire | AffectedZone$ Hand,Library,Graveyard,Exile,Command,Stack | AddKeyword$ Madness:CardManaCost | Description$ Each Vampire creature card you own that isn't on the battlefield has madness. The madness cost is equal to its mana cost. (If you discard a card with madness, discard it into exile. When you do, cast it for its madness cost or put it into your graveyard.) +S:Mode$ Continuous | Affected$ Creature.YouOwn+Vampire | AffectedZone$ Hand,Library,Graveyard,Exile,Command,Stack | AddKeyword$ Madness:ManaCost | Description$ Each Vampire creature card you own that isn't on the battlefield has madness. The madness cost is equal to its mana cost. (If you discard a card with madness, discard it into exile. When you do, cast it for its madness cost or put it into your graveyard.) Oracle:Each Vampire creature card you own that isn't on the battlefield has madness. The madness cost is equal to its mana cost. (If you discard a card with madness, discard it into exile. When you do, cast it for its madness cost or put it into your graveyard.) From e035af3389ff2046a8cf284dbb3e47da93e96b4d Mon Sep 17 00:00:00 2001 From: Alexander Kim <37757170+joongiealexkim@users.noreply.github.com> Date: Sun, 24 Nov 2024 04:49:45 -0500 Subject: [PATCH 101/152] Update ashroot_animist.txt (#6621) --- forge-gui/res/cardsfolder/a/ashroot_animist.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/forge-gui/res/cardsfolder/a/ashroot_animist.txt b/forge-gui/res/cardsfolder/a/ashroot_animist.txt index 95ed3aa6608..fdbaff99614 100644 --- a/forge-gui/res/cardsfolder/a/ashroot_animist.txt +++ b/forge-gui/res/cardsfolder/a/ashroot_animist.txt @@ -3,7 +3,7 @@ ManaCost:2 R G Types:Creature Lizard Druid PT:4/4 K:Trample -T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ When this creature attacks, another target attacking creature you control gets +X/+X until end of turn, where X is this creature's power. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.attacking+Other+YouCtrl | TgtPrompt$ Select another target attacking creature you control | NumAtt$ X | NumDef$ X +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ When this creature attacks, another target creature you control gains trample and gets +X/+X until end of turn, where X is this creature's power. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.Other+YouCtrl | TgtPrompt$ Select another target creature you control | NumAtt$ X | NumDef$ X | KW$ Trample SVar:X:Count$CardPower -Oracle:Trample\nWhen this creature attacks, another target attacking creature you control gets +X/+X until end of turn, where X is this creature's power. \ No newline at end of file +Oracle:Trample\nWhen this creature attacks, another target creature you control gains trample and gets +X/+X until end of turn, where X is this creature's power. From 870add0da414ebe6e76033f6110712a8d0115ef5 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 24 Nov 2024 11:21:12 +0100 Subject: [PATCH 102/152] ~fix line style in cardsfolder --- .../res/cardsfolder/a/abyssal_harvester.txt | 18 ++++---- .../a/alesha_who_laughs_at_fate.txt | 24 +++++----- forge-gui/res/cardsfolder/a/ambush_wolf.txt | 14 +++--- .../cardsfolder/a/anep_vizier_of_hazoret.txt | 22 +++++----- forge-gui/res/cardsfolder/a/anthropede.txt | 20 ++++----- .../cardsfolder/a/aphelia_viper_whisperer.txt | 24 +++++----- .../res/cardsfolder/a/arbiter_of_woe.txt | 26 +++++------ .../res/cardsfolder/a/armasaur_guide.txt | 16 +++---- .../res/cardsfolder/a/ashroot_animist.txt | 18 ++++---- .../res/cardsfolder/b/banner_of_kinship.txt | 24 +++++----- .../cardsfolder/b/battlesong_berserker.txt | 16 +++---- .../res/cardsfolder/b/beast_kin_ranger.txt | 16 +++---- .../res/cardsfolder/b/beastie_beatdown.txt | 14 +++--- .../res/cardsfolder/b/bigfin_bouncer.txt | 12 ++--- .../cardsfolder/b/billowing_shriekmass.txt | 22 +++++----- .../res/cardsfolder/b/blasphemous_edict.txt | 10 ++--- forge-gui/res/cardsfolder/b/boltwave.txt | 8 ++-- .../b/bottomless_pool_locker_room.txt | 30 ++++++------- .../cardsfolder/b/braulios_of_pheres_band.txt | 20 ++++----- .../b/brigone_soldier_of_meletis.txt | 16 +++---- forge-gui/res/cardsfolder/b/bulk_up.txt | 10 ++--- .../res/cardsfolder/c/cackling_prowler.txt | 16 +++---- .../res/cardsfolder/c/cackling_slasher.txt | 16 +++---- forge-gui/res/cardsfolder/c/cat_collector.txt | 22 +++++----- .../res/cardsfolder/c/celestial_armor.txt | 20 ++++----- .../res/cardsfolder/c/cephalid_inkmage.txt | 18 ++++---- .../c/charred_foyer_warped_space.txt | 34 +++++++------- forge-gui/res/cardsfolder/c/claws_out.txt | 16 +++---- .../cardsfolder/c/cleon_merry_champion.txt | 20 ++++----- .../res/cardsfolder/c/clinquant_skymage.txt | 14 +++--- .../res/cardsfolder/c/courageous_goblin.txt | 14 +++--- .../res/cardsfolder/c/crackling_cyclops.txt | 14 +++--- .../c/cramped_vents_access_room.txt | 30 ++++++------- .../res/cardsfolder/c/creeping_peeper.txt | 10 ++--- .../cardsfolder/c/curator_of_destinies.txt | 22 +++++----- .../cardsfolder/c/cynette_jelly_drover.txt | 20 ++++----- .../res/cardsfolder/d/dauntless_veteran.txt | 12 ++--- .../res/cardsfolder/d/dawnwing_marshal.txt | 12 ++--- .../res/cardsfolder/d/dazzling_angel.txt | 18 ++++---- .../d/dazzling_theater_prop_room.txt | 26 +++++------ .../d/defiled_crypt_cadaver_lab.txt | 34 +++++++------- .../cardsfolder/d/delightful_discovery.txt | 14 +++--- .../d/derelict_attic_widows_walk.txt | 32 +++++++------- .../cardsfolder/d/dionus_elvish_archdruid.txt | 20 ++++----- .../res/cardsfolder/d/dissection_tools.txt | 20 ++++----- .../res/cardsfolder/d/divine_resilience.txt | 16 +++---- .../res/cardsfolder/d/dragon_trainer.txt | 12 ++--- forge-gui/res/cardsfolder/d/drake_hatcher.txt | 22 +++++----- .../res/cardsfolder/d/dreadwing_scavenger.txt | 24 +++++----- .../res/cardsfolder/d/dropkick_bomber.txt | 22 +++++----- .../res/cardsfolder/e/eager_trufflesnout.txt | 16 +++---- .../cardsfolder/e/eidolon_of_astral_winds.txt | 16 +++---- .../res/cardsfolder/e/electroduplicate.txt | 12 ++--- .../res/cardsfolder/e/elementalist_adept.txt | 12 ++--- .../cardsfolder/e/elenda_saint_of_dusk.txt | 24 +++++----- .../res/cardsfolder/e/elfsworn_giant.txt | 14 +++--- .../res/cardsfolder/e/elvish_regrower.txt | 12 ++--- .../res/cardsfolder/e/entity_tracker.txt | 20 ++++----- .../res/cardsfolder/e/erudite_wizard.txt | 12 ++--- .../e/evereth_viceroy_of_plunder.txt | 26 +++++------ .../res/cardsfolder/e/exemplar_of_light.txt | 22 +++++----- .../e/experimental_lab_staff_room.txt | 40 ++++++++--------- .../res/cardsfolder/f/faebloom_trick.txt | 16 +++---- .../res/cardsfolder/f/faithful_pikemaster.txt | 14 +++--- .../res/cardsfolder/f/fear_of_infinity.txt | 20 ++++----- forge-gui/res/cardsfolder/f/felling_blow.txt | 14 +++--- .../res/cardsfolder/f/fiendish_panda.txt | 22 +++++----- .../res/cardsfolder/f/fiery_annihilation.txt | 12 ++--- .../res/cardsfolder/f/firespitter_whelp.txt | 16 +++---- forge-gui/res/cardsfolder/f/fishing_pole.txt | 24 +++++----- .../res/cardsfolder/f/fleeting_flight.txt | 14 +++--- .../res/cardsfolder/f/frontline_heroism.txt | 20 ++++----- .../cardsfolder/f/fumulus_the_infestation.txt | 26 +++++------ .../f/funeral_room_awakening_hall.txt | 32 +++++++------- .../g/general_kreat_the_boltbringer.txt | 18 ++++---- forge-gui/res/cardsfolder/g/generous_pup.txt | 18 ++++---- forge-gui/res/cardsfolder/g/get_out.txt | 14 +++--- .../res/cardsfolder/g/gilded_scuttler.txt | 16 +++---- forge-gui/res/cardsfolder/g/go_forth.txt | 12 ++--- .../res/cardsfolder/g/goblin_boarders.txt | 12 ++--- .../res/cardsfolder/g/goblin_negotiation.txt | 16 +++---- .../res/cardsfolder/g/goblin_surprise.txt | 12 ++--- .../res/cardsfolder/g/gorehorn_raider.txt | 14 +++--- .../cardsfolder/g/gornog_the_red_reaper.txt | 24 +++++----- .../g/grand_entryway_elegant_rotunda.txt | 30 ++++++------- .../res/cardsfolder/g/grappling_kraken.txt | 18 ++++---- .../g/greenhouse_rickety_gazebo.txt | 34 +++++++------- forge-gui/res/cardsfolder/g/guarded_heir.txt | 14 +++--- .../res/cardsfolder/g/gutless_plunderer.txt | 20 ++++----- forge-gui/res/cardsfolder/h/hare_apparent.txt | 20 ++++----- .../res/cardsfolder/h/hearts_on_fire.txt | 8 ++-- .../res/cardsfolder/h/high_fae_trickster.txt | 14 +++--- .../res/cardsfolder/h/high_society_hunter.txt | 22 +++++----- .../cardsfolder/h/hinterland_sanctifier.txt | 16 +++---- .../res/cardsfolder/h/homunculus_horde.txt | 16 +++---- forge-gui/res/cardsfolder/h/hungry_ghoul.txt | 12 ++--- .../res/cardsfolder/h/hungry_megasloth.txt | 12 ++--- .../res/cardsfolder/h/hurska_sweet_tooth.txt | 22 +++++----- .../res/cardsfolder/i/icewind_elemental.txt | 16 +++---- .../res/cardsfolder/i/incinerating_blast.txt | 12 ++--- .../res/cardsfolder/i/infernal_vessel.txt | 14 +++--- .../res/cardsfolder/i/infestation_sage.txt | 14 +++--- .../cardsfolder/i/inspiration_from_beyond.txt | 14 +++--- .../res/cardsfolder/i/inspiring_paladin.txt | 14 +++--- .../res/cardsfolder/i/intruding_soulrager.txt | 18 ++++---- .../cardsfolder/i/ivora_insatiable_heir.txt | 22 +++++----- forge-gui/res/cardsfolder/j/joust_through.txt | 10 ++--- .../k/kellan_planar_trailblazer.txt | 22 +++++----- .../res/cardsfolder/k/keys_to_the_house.txt | 10 ++--- .../cardsfolder/k/kiora_the_rising_tide.txt | 22 +++++----- .../res/cardsfolder/k/koma_world_eater.txt | 20 ++++----- .../cardsfolder/k/kykar_zephyr_awakener.txt | 26 +++++------ forge-gui/res/cardsfolder/l/leyline_axe.txt | 14 +++--- forge-gui/res/cardsfolder/l/living_phone.txt | 12 ++--- .../cardsfolder/l/loot_exuberant_explorer.txt | 14 +++--- .../res/cardsfolder/l/luminous_rebuke.txt | 10 ++--- forge-gui/res/cardsfolder/l/lunar_insight.txt | 12 ++--- .../res/cardsfolder/m/marina_vendrell.txt | 16 +++---- .../res/cardsfolder/m/midnight_snack.txt | 18 ++++---- .../res/cardsfolder/m/mischievous_mystic.txt | 16 +++---- .../cardsfolder/n/nazar_the_velvet_fang.txt | 28 ++++++------ .../res/cardsfolder/n/needletooth_pack.txt | 14 +++--- .../cardsfolder/n/neerdiv_devious_diver.txt | 24 +++++----- .../cardsfolder/n/niv_mizzet_visionary.txt | 20 ++++----- .../o/ozox_the_clattering_king.txt | 16 +++---- .../res/cardsfolder/p/perforating_artist.txt | 22 +++++----- .../cardsfolder/p/phantasmal_shieldback.txt | 20 ++++----- .../p/plagon_lord_of_the_beach.txt | 18 ++++---- .../cardsfolder/p/pol_jamaar_illusionist.txt | 18 ++++---- .../p/polluted_cistern_dim_oubliette.txt | 34 +++++++------- .../p/preposterous_proportions.txt | 8 ++-- .../cardsfolder/p/psemilla_meletian_poet.txt | 18 ++++---- .../cardsfolder/q/qala_ajanis_pridemate.txt | 22 +++++----- .../cardsfolder/q/quakestrider_ceratops.txt | 8 ++-- .../res/cardsfolder/q/quick_draw_katana.txt | 10 ++--- .../res/cardsfolder/q/quilled_greatwurm.txt | 18 ++++---- .../res/cardsfolder/r/raise_the_past.txt | 10 ++--- .../res/cardsfolder/r/ravenous_amulet.txt | 14 +++--- .../res/cardsfolder/r/razorgrass_invoker.txt | 12 ++--- forge-gui/res/cardsfolder/r/refute.txt | 12 ++--- .../res/cardsfolder/r/rev_tithe_extractor.txt | 30 ++++++------- .../res/cardsfolder/r/revenge_of_the_rats.txt | 12 ++--- forge-gui/res/cardsfolder/r/revoke_demise.txt | 14 +++--- .../res/cardsfolder/r/ripchain_razorkin.txt | 12 ++--- .../r/rite_of_the_dragoncaller.txt | 14 +++--- .../res/cardsfolder/r/rune_sealed_wall.txt | 14 +++--- .../res/cardsfolder/r/running_is_useless.txt | 14 +++--- .../res/cardsfolder/s/sandstorm_crasher.txt | 16 +++---- .../res/cardsfolder/s/sanguine_syphoner.txt | 18 ++++---- .../res/cardsfolder/s/saurian_symbiote.txt | 20 ++++----- .../cardsfolder/s/scholar_of_combustion.txt | 20 ++++----- .../s/scourge_of_the_undercity.txt | 18 ++++---- .../res/cardsfolder/s/scrawling_crawler.txt | 16 +++---- forge-gui/res/cardsfolder/s/scythecat_cub.txt | 22 +++++----- .../res/cardsfolder/s/searslicer_goblin.txt | 14 +++--- .../s/secret_arcade_dusty_parlor.txt | 30 ++++++------- forge-gui/res/cardsfolder/s/seekers_folly.txt | 14 +++--- .../res/cardsfolder/s/shardless_outlander.txt | 12 ++--- .../res/cardsfolder/s/shroofus_sproutsire.txt | 22 +++++----- .../cardsfolder/s/sire_of_seven_deaths.txt | 22 +++++----- .../res/cardsfolder/s/skyknight_squire.txt | 16 +++---- .../res/cardsfolder/s/skyship_buccaneer.txt | 16 +++---- forge-gui/res/cardsfolder/s/slimy_piper.txt | 18 ++++---- .../s/slinza_the_spiked_stampede.txt | 22 +++++----- .../res/cardsfolder/s/slumbering_cerberus.txt | 16 +++---- .../s/smoky_lounge_misty_salon.txt | 32 +++++++------- .../s/solitary_study_endless_corridor.txt | 34 +++++++------- .../cardsfolder/s/soul_shackled_zombie.txt | 20 ++++----- .../res/cardsfolder/s/soulstone_sanctuary.txt | 12 ++--- .../res/cardsfolder/s/sower_of_chaos.txt | 10 ++--- .../s/sphinx_of_forgotten_lore.txt | 20 ++++----- .../s/spiked_corridor_torture_pit.txt | 30 ++++++------- .../res/cardsfolder/s/spined_tyrranax.txt | 20 ++++----- .../res/cardsfolder/s/spinner_of_souls.txt | 14 +++--- forge-gui/res/cardsfolder/s/star_athlete.txt | 22 +++++----- .../res/cardsfolder/s/starlight_snare.txt | 18 ++++---- .../res/cardsfolder/s/starnheim_memento.txt | 10 ++--- forge-gui/res/cardsfolder/s/strix_lookout.txt | 18 ++++---- .../res/cardsfolder/s/strongbox_raider.txt | 22 +++++----- .../res/cardsfolder/s/sun_blessed_healer.txt | 20 ++++----- .../s/sutina_speaker_of_the_tajuru.txt | 20 ++++----- .../res/cardsfolder/s/sylvan_scavenging.txt | 16 +++---- .../t/taeko_the_patient_avalanche.txt | 30 ++++++------- .../cardsfolder/t/thurid_mare_of_destiny.txt | 18 ++++---- .../t/tinybones_bauble_burglar.txt | 18 ++++---- .../res/cardsfolder/t/tragic_banshee.txt | 14 +++--- .../cardsfolder/t/treetop_snarespinner.txt | 16 +++---- .../res/cardsfolder/t/twinblade_blessing.txt | 14 +++--- .../res/cardsfolder/t/twinflame_tyrant.txt | 20 ++++----- .../res/cardsfolder/u/uncharted_voyage.txt | 10 ++--- .../u/unholy_annex_ritual_chamber.txt | 44 +++++++++---------- .../cardsfolder/u/unidentified_hovership.txt | 24 +++++----- .../res/cardsfolder/u/unnerving_grasp.txt | 12 ++--- .../cardsfolder/u/urdnan_dromoka_warrior.txt | 28 ++++++------ .../res/cardsfolder/v/valkyries_call.txt | 14 +++--- .../res/cardsfolder/v/vampire_gourmand.txt | 16 +++---- .../res/cardsfolder/v/vampire_soulcaller.txt | 16 +++---- .../res/cardsfolder/v/vanguard_seraph.txt | 16 +++---- .../cardsfolder/w/wardens_of_the_cycle.txt | 20 ++++----- .../cardsfolder/w/winters_intervention.txt | 10 ++--- .../res/cardsfolder/w/woodland_liege.txt | 16 +++---- .../res/cardsfolder/w/wriggling_grub.txt | 16 +++---- .../cardsfolder/y/your_plans_mean_nothing.txt | 26 +++++------ .../cardsfolder/z/zimone_paradox_sculptor.txt | 16 +++---- .../res/cardsfolder/z/zul_ashur_lich_lord.txt | 14 +++--- 205 files changed, 1860 insertions(+), 1860 deletions(-) diff --git a/forge-gui/res/cardsfolder/a/abyssal_harvester.txt b/forge-gui/res/cardsfolder/a/abyssal_harvester.txt index e026a1070ff..7ff4abb6893 100644 --- a/forge-gui/res/cardsfolder/a/abyssal_harvester.txt +++ b/forge-gui/res/cardsfolder/a/abyssal_harvester.txt @@ -1,9 +1,9 @@ -Name:Abyssal Harvester -ManaCost:1 B B -Types:Creature Demon Warlock -PT:3/2 -A:AB$ ChangeZone | Cost$ T | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Creature.ThisTurnEntered | RememberChanged$ True | TgtPrompt$ Select target creature card from a graveyard that was put there this turn | SubAbility$ DBCopy | SpellDescription$ Exile target creature card from a graveyard that was put there this turn. Create a token that's a copy of it, except it's a Nightmare in addition to its other types. Then exile all other Nightmare tokens you control. -SVar:DBCopy:DB$ CopyPermanent | Defined$ Remembered | Controller$ You | AddTypes$ Nightmare | RememberTokens$ True | SubAbility$ DBChangeZoneAll -SVar:DBChangeZoneAll:DB$ ChangeZoneAll | ChangeType$ Nightmare.token+IsNotRemembered+YouCtrl | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -Oracle:{T}: Exile target creature card from a graveyard that was put there this turn. Create a token that's a copy of it, except it's a Nightmare in addition to its other types. Then exile all other Nightmare tokens you control. +Name:Abyssal Harvester +ManaCost:1 B B +Types:Creature Demon Warlock +PT:3/2 +A:AB$ ChangeZone | Cost$ T | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Creature.ThisTurnEntered | RememberChanged$ True | TgtPrompt$ Select target creature card from a graveyard that was put there this turn | SubAbility$ DBCopy | SpellDescription$ Exile target creature card from a graveyard that was put there this turn. Create a token that's a copy of it, except it's a Nightmare in addition to its other types. Then exile all other Nightmare tokens you control. +SVar:DBCopy:DB$ CopyPermanent | Defined$ Remembered | Controller$ You | AddTypes$ Nightmare | RememberTokens$ True | SubAbility$ DBChangeZoneAll +SVar:DBChangeZoneAll:DB$ ChangeZoneAll | ChangeType$ Nightmare.token+IsNotRemembered+YouCtrl | Origin$ Battlefield | Destination$ Exile | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +Oracle:{T}: Exile target creature card from a graveyard that was put there this turn. Create a token that's a copy of it, except it's a Nightmare in addition to its other types. Then exile all other Nightmare tokens you control. diff --git a/forge-gui/res/cardsfolder/a/alesha_who_laughs_at_fate.txt b/forge-gui/res/cardsfolder/a/alesha_who_laughs_at_fate.txt index 2e817fec6dc..6c8d41fcdbd 100644 --- a/forge-gui/res/cardsfolder/a/alesha_who_laughs_at_fate.txt +++ b/forge-gui/res/cardsfolder/a/alesha_who_laughs_at_fate.txt @@ -1,12 +1,12 @@ -Name:Alesha, Who Laughs at Fate -ManaCost:1 B R -Types:Legendary Creature Human Warrior -PT:2/2 -K:First Strike -T:Mode$ Attacks | ValidCard$ Creature.Self | Execute$ TrigPutCounter | TriggerDescription$ Whenever NICKNAME attacks, put a +1/+1 counter on it. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ RaidTest | Execute$ TrigChangeZone | TriggerDescription$ Raid — At the beginning of your end step, if you attacked this turn, return target creature card with mana value less than or equal to NICKNAME's power from your graveyard to the battlefield. -SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn+cmcLEX | TgtPrompt$ Select target creature card with mana value less than or equal to Alesha's power -SVar:RaidTest:Count$AttackersDeclared -SVar:X:Count$CardPower -Oracle:First strike\nWhenever Alesha attacks, put a +1/+1 counter on it.\nRaid — At the beginning of your end step, if you attacked this turn, return target creature card with mana value less than or equal to Alesha's power from your graveyard to the battlefield. +Name:Alesha, Who Laughs at Fate +ManaCost:1 B R +Types:Legendary Creature Human Warrior +PT:2/2 +K:First Strike +T:Mode$ Attacks | ValidCard$ Creature.Self | Execute$ TrigPutCounter | TriggerDescription$ Whenever NICKNAME attacks, put a +1/+1 counter on it. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ RaidTest | Execute$ TrigChangeZone | TriggerDescription$ Raid — At the beginning of your end step, if you attacked this turn, return target creature card with mana value less than or equal to NICKNAME's power from your graveyard to the battlefield. +SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn+cmcLEX | TgtPrompt$ Select target creature card with mana value less than or equal to Alesha's power +SVar:RaidTest:Count$AttackersDeclared +SVar:X:Count$CardPower +Oracle:First strike\nWhenever Alesha attacks, put a +1/+1 counter on it.\nRaid — At the beginning of your end step, if you attacked this turn, return target creature card with mana value less than or equal to Alesha's power from your graveyard to the battlefield. diff --git a/forge-gui/res/cardsfolder/a/ambush_wolf.txt b/forge-gui/res/cardsfolder/a/ambush_wolf.txt index dfe62eeb032..81ddfaea9da 100644 --- a/forge-gui/res/cardsfolder/a/ambush_wolf.txt +++ b/forge-gui/res/cardsfolder/a/ambush_wolf.txt @@ -1,8 +1,8 @@ -Name:Ambush Wolf -ManaCost:2 G -Types:Creature Wolf -PT:4/2 -K:Flash -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, exile up to one target card from a graveyard. -SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target card in a graveyard to exile +Name:Ambush Wolf +ManaCost:2 G +Types:Creature Wolf +PT:4/2 +K:Flash +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, exile up to one target card from a graveyard. +SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target card in a graveyard to exile Oracle:Flash (You may cast this spell any time you could cast an instant.)\nWhen this creature enters, exile up to one target card from a graveyard. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/a/anep_vizier_of_hazoret.txt b/forge-gui/res/cardsfolder/a/anep_vizier_of_hazoret.txt index 877ee494ab8..173a06d97c7 100644 --- a/forge-gui/res/cardsfolder/a/anep_vizier_of_hazoret.txt +++ b/forge-gui/res/cardsfolder/a/anep_vizier_of_hazoret.txt @@ -1,11 +1,11 @@ -Name:Anep, Vizier of Hazoret -ManaCost:2 R -Types:Legendary Creature Jackal Warrior -PT:4/2 -K:Trample -S:Mode$ OptionalAttackCost | ValidCard$ Card.Self | Trigger$ TrigExile | Cost$ Exert<1/CARDNAME> | Description$ You may exert CARDNAME as it attacks. When you do, exile the top two cards of your library. Until the end of your next turn, you may play those cards. (An exerted creature won't untap during your next untap step.) -SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 2 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ When you do, exile the top two cards of your library. Until the end of your next turn, you may play those cards. -SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ STPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn -SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ Until the end of your next turn, you may play the exiled cards. -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -Oracle:Trample\nYou may exert Anep, Vizier of Hazoret as it attacks. When you do, exile the top two cards of your library. Until the end of your next turn, you may play those cards. (An exerted creature won't untap during your next untap step.) +Name:Anep, Vizier of Hazoret +ManaCost:2 R +Types:Legendary Creature Jackal Warrior +PT:4/2 +K:Trample +S:Mode$ OptionalAttackCost | ValidCard$ Card.Self | Trigger$ TrigExile | Cost$ Exert<1/CARDNAME> | Description$ You may exert CARDNAME as it attacks. When you do, exile the top two cards of your library. Until the end of your next turn, you may play those cards. (An exerted creature won't untap during your next untap step.) +SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 2 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ When you do, exile the top two cards of your library. Until the end of your next turn, you may play those cards. +SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ STPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn +SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ Until the end of your next turn, you may play the exiled cards. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +Oracle:Trample\nYou may exert Anep, Vizier of Hazoret as it attacks. When you do, exile the top two cards of your library. Until the end of your next turn, you may play those cards. (An exerted creature won't untap during your next untap step.) diff --git a/forge-gui/res/cardsfolder/a/anthropede.txt b/forge-gui/res/cardsfolder/a/anthropede.txt index d2bc81c3f04..4edd0f28571 100644 --- a/forge-gui/res/cardsfolder/a/anthropede.txt +++ b/forge-gui/res/cardsfolder/a/anthropede.txt @@ -1,11 +1,11 @@ -Name:Anthropede -ManaCost:3 G -Types:Creature Insect -PT:3/4 -K:Reach -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChoice | TriggerDescription$ When CARDNAME enters, you may discard a card or pay {2}. When you do, destroy target Room. -SVar:TrigChoice:DB$ GenericChoice | Choices$ PayDiscard,Pay2 -SVar:Pay2:DB$ ImmediateTrigger | UnlessCost$ 2 | UnlessPayer$ You | UnlessSwitched$ True | Execute$ TrigDestroy | SpellDescription$ pay {2}: When you do, destroy target Room. -SVar:PayDiscard:DB$ ImmediateTrigger | UnlessCost$ Discard<1/Card> | UnlessPayer$ You | UnlessSwitched$ True | Execute$ TrigDestroy | SpellDescription$ discard a card: When you do, destroy target Room. -SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Room | TgtPrompt$ Select target Room +Name:Anthropede +ManaCost:3 G +Types:Creature Insect +PT:3/4 +K:Reach +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChoice | TriggerDescription$ When CARDNAME enters, you may discard a card or pay {2}. When you do, destroy target Room. +SVar:TrigChoice:DB$ GenericChoice | Choices$ PayDiscard,Pay2 +SVar:Pay2:DB$ ImmediateTrigger | UnlessCost$ 2 | UnlessPayer$ You | UnlessSwitched$ True | Execute$ TrigDestroy | SpellDescription$ pay {2}: When you do, destroy target Room. +SVar:PayDiscard:DB$ ImmediateTrigger | UnlessCost$ Discard<1/Card> | UnlessPayer$ You | UnlessSwitched$ True | Execute$ TrigDestroy | SpellDescription$ discard a card: When you do, destroy target Room. +SVar:TrigDestroy:DB$ Destroy | ValidTgts$ Room | TgtPrompt$ Select target Room Oracle:Reach\nWhen Anthropede enters, you may discard a card or pay {2}. When you do, destroy target Room. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/a/aphelia_viper_whisperer.txt b/forge-gui/res/cardsfolder/a/aphelia_viper_whisperer.txt index 18aa3731b6e..d889871cdec 100644 --- a/forge-gui/res/cardsfolder/a/aphelia_viper_whisperer.txt +++ b/forge-gui/res/cardsfolder/a/aphelia_viper_whisperer.txt @@ -1,13 +1,13 @@ -Name:Aphelia, Viper Whisperer -ManaCost:1 B -Types:Legendary Creature Gorgon Assassin -PT:1/3 -K:Deathtouch -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ Whenever NICKNAME attacks, you may pay {1}{B/G}. If you do, create a 1/1 black Snake creature token with deathtouch. -SVar:TrigToken:AB$ Token | Cost$ 1 BG | TokenAmount$ 1 | TokenScript$ b_1_1_snake_deathtouch | TokenOwner$ You -A:AB$ Effect | Cost$ 4 B | Triggers$ TrigDamage | SpellDescription$ Until end of turn, whenever one or more Gorgons and/or Snakes you control deal combat damage to a player, that player loses half their life, rounded up. -SVar:TrigDamage:Mode$ DamageDoneOnce | CombatDamage$ True | ValidSource$ Creature.Gorgon+YouCtrl,Creature.Snake+YouCtrl | ValidTarget$ Player | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever one or more Gorgons and/or Snakes you control deal combat damage to a player, that player loses half their life, rounded up. -SVar:TrigLoseLife:DB$ LoseLife | Defined$ TriggeredTarget | LifeAmount$ X -SVar:X:TriggeredTarget$LifeTotal/HalfUp -SVar:HasAttackEffect:TRUE +Name:Aphelia, Viper Whisperer +ManaCost:1 B +Types:Legendary Creature Gorgon Assassin +PT:1/3 +K:Deathtouch +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ Whenever NICKNAME attacks, you may pay {1}{B/G}. If you do, create a 1/1 black Snake creature token with deathtouch. +SVar:TrigToken:AB$ Token | Cost$ 1 BG | TokenAmount$ 1 | TokenScript$ b_1_1_snake_deathtouch | TokenOwner$ You +A:AB$ Effect | Cost$ 4 B | Triggers$ TrigDamage | SpellDescription$ Until end of turn, whenever one or more Gorgons and/or Snakes you control deal combat damage to a player, that player loses half their life, rounded up. +SVar:TrigDamage:Mode$ DamageDoneOnce | CombatDamage$ True | ValidSource$ Creature.Gorgon+YouCtrl,Creature.Snake+YouCtrl | ValidTarget$ Player | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever one or more Gorgons and/or Snakes you control deal combat damage to a player, that player loses half their life, rounded up. +SVar:TrigLoseLife:DB$ LoseLife | Defined$ TriggeredTarget | LifeAmount$ X +SVar:X:TriggeredTarget$LifeTotal/HalfUp +SVar:HasAttackEffect:TRUE Oracle:Deathtouch\nWhenever Aphelia attacks, you may pay {1}{B/G}. If you do, create a 1/1 black Snake creature token with deathtouch.\n{4}{B}: Until end of turn, whenever one or more Gorgons and/or Snakes you control deal combat damage to a player, that player loses half their life, rounded up. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/a/arbiter_of_woe.txt b/forge-gui/res/cardsfolder/a/arbiter_of_woe.txt index 0e6bab4a5a2..50f4d196feb 100644 --- a/forge-gui/res/cardsfolder/a/arbiter_of_woe.txt +++ b/forge-gui/res/cardsfolder/a/arbiter_of_woe.txt @@ -1,14 +1,14 @@ -Name:Arbiter of Woe -ManaCost:4 B B -Types:Creature Demon -PT:5/4 -A:SP$ PermanentCreature | Cost$ 4 B B Sac<1/Creature> -K:Flying -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDiscard | TriggerDescription$ When this creature enters, each opponent discards a card and loses 2 life. You draw a card and gain 2 life. -SVar:TrigDiscard:DB$ Discard | Defined$ Opponent | Mode$ TgtChoose | SubAbility$ DBLoseLife -SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 2 | Defined$ Opponent | SubAbility$ DBDraw -SVar:DBDraw:DB$ Draw | SubAbility$ DBGainLife -SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 -DeckHas:Ability$LifeGain -SVar:AIPreference:SacCost$Creature.token,Creature.cmcLE4 +Name:Arbiter of Woe +ManaCost:4 B B +Types:Creature Demon +PT:5/4 +A:SP$ PermanentCreature | Cost$ 4 B B Sac<1/Creature> +K:Flying +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDiscard | TriggerDescription$ When this creature enters, each opponent discards a card and loses 2 life. You draw a card and gain 2 life. +SVar:TrigDiscard:DB$ Discard | Defined$ Opponent | Mode$ TgtChoose | SubAbility$ DBLoseLife +SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 2 | Defined$ Opponent | SubAbility$ DBDraw +SVar:DBDraw:DB$ Draw | SubAbility$ DBGainLife +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 +DeckHas:Ability$LifeGain +SVar:AIPreference:SacCost$Creature.token,Creature.cmcLE4 Oracle:As an additional cost to cast this spell, sacrifice a creature.\nFlying\nWhen this creature enters, each opponent discards a card and loses 2 life. You draw a card and gain 2 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/a/armasaur_guide.txt b/forge-gui/res/cardsfolder/a/armasaur_guide.txt index 514e49b0f82..446595f9440 100644 --- a/forge-gui/res/cardsfolder/a/armasaur_guide.txt +++ b/forge-gui/res/cardsfolder/a/armasaur_guide.txt @@ -1,9 +1,9 @@ -Name:Armasaur Guide -ManaCost:4 W -Types:Creature Dinosaur -PT:4/4 -K:Vigilance -T:Mode$ AttackersDeclared | Execute$ TrigPutCounter | ValidAttackers$ Creature | ValidAttackersAmount$ GE3 | TriggerZones$ Battlefield | AttackingPlayer$ You | TriggerDescription$ Whenever you attack with three or more creatures, put a +1/+1 counter on target creature you control. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1 -DeckHas:Ability$Counters +Name:Armasaur Guide +ManaCost:4 W +Types:Creature Dinosaur +PT:4/4 +K:Vigilance +T:Mode$ AttackersDeclared | Execute$ TrigPutCounter | ValidAttackers$ Creature | ValidAttackersAmount$ GE3 | TriggerZones$ Battlefield | AttackingPlayer$ You | TriggerDescription$ Whenever you attack with three or more creatures, put a +1/+1 counter on target creature you control. +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1 +DeckHas:Ability$Counters Oracle:Vigilance (Attacking doesn't cause this creature to tap.)\nWhenever you attack with three or more creatures, put a +1/+1 counter on target creature you control. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/a/ashroot_animist.txt b/forge-gui/res/cardsfolder/a/ashroot_animist.txt index fdbaff99614..6ce550272f3 100644 --- a/forge-gui/res/cardsfolder/a/ashroot_animist.txt +++ b/forge-gui/res/cardsfolder/a/ashroot_animist.txt @@ -1,9 +1,9 @@ -Name:Ashroot Animist -ManaCost:2 R G -Types:Creature Lizard Druid -PT:4/4 -K:Trample -T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ When this creature attacks, another target creature you control gains trample and gets +X/+X until end of turn, where X is this creature's power. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.Other+YouCtrl | TgtPrompt$ Select another target creature you control | NumAtt$ X | NumDef$ X | KW$ Trample -SVar:X:Count$CardPower -Oracle:Trample\nWhen this creature attacks, another target creature you control gains trample and gets +X/+X until end of turn, where X is this creature's power. +Name:Ashroot Animist +ManaCost:2 R G +Types:Creature Lizard Druid +PT:4/4 +K:Trample +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ When this creature attacks, another target creature you control gains trample and gets +X/+X until end of turn, where X is this creature's power. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.Other+YouCtrl | TgtPrompt$ Select another target creature you control | NumAtt$ X | NumDef$ X | KW$ Trample +SVar:X:Count$CardPower +Oracle:Trample\nWhen this creature attacks, another target creature you control gains trample and gets +X/+X until end of turn, where X is this creature's power. diff --git a/forge-gui/res/cardsfolder/b/banner_of_kinship.txt b/forge-gui/res/cardsfolder/b/banner_of_kinship.txt index d2bb8eddb73..0ac4c37ce07 100644 --- a/forge-gui/res/cardsfolder/b/banner_of_kinship.txt +++ b/forge-gui/res/cardsfolder/b/banner_of_kinship.txt @@ -1,12 +1,12 @@ -Name:Banner of Kinship -ManaCost:5 -Types:Artifact -R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | ReplaceWith$ ChooseCT | ReplacementResult$ Updated | Description$ As this artifact enters, choose a creature type. This artifact enters with a fellowship counter on it for each creature you control of the chosen type. -SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SubAbility$ DBCounters -SVar:DBCounters:DB$ PutCounter | ETB$ True | Defined$ Self | CounterType$ FELLOWSHIP | CounterNum$ X -S:Mode$ Continuous | Affected$ Creature.ChosenType+YouCtrl | AddPower$ Y | AddToughness$ Y | Description$ Creatures you control of the chosen type get +1/+1 for each fellowship counter on this artifact. -SVar:X:Count$Valid Creature.ChosenType+YouCtrl -SVar:Y:Count$CardCounters.FELLOWSHIP -SVar:BuffedBy:Creature -AI:RemoveDeck:Random -Oracle:As this artifact enters, choose a creature type. This artifact enters with a fellowship counter on it for each creature you control of the chosen type.\nCreatures you control of the chosen type get +1/+1 for each fellowship counter on this artifact. +Name:Banner of Kinship +ManaCost:5 +Types:Artifact +R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | ReplaceWith$ ChooseCT | ReplacementResult$ Updated | Description$ As this artifact enters, choose a creature type. This artifact enters with a fellowship counter on it for each creature you control of the chosen type. +SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SubAbility$ DBCounters +SVar:DBCounters:DB$ PutCounter | ETB$ True | Defined$ Self | CounterType$ FELLOWSHIP | CounterNum$ X +S:Mode$ Continuous | Affected$ Creature.ChosenType+YouCtrl | AddPower$ Y | AddToughness$ Y | Description$ Creatures you control of the chosen type get +1/+1 for each fellowship counter on this artifact. +SVar:X:Count$Valid Creature.ChosenType+YouCtrl +SVar:Y:Count$CardCounters.FELLOWSHIP +SVar:BuffedBy:Creature +AI:RemoveDeck:Random +Oracle:As this artifact enters, choose a creature type. This artifact enters with a fellowship counter on it for each creature you control of the chosen type.\nCreatures you control of the chosen type get +1/+1 for each fellowship counter on this artifact. diff --git a/forge-gui/res/cardsfolder/b/battlesong_berserker.txt b/forge-gui/res/cardsfolder/b/battlesong_berserker.txt index e2d819aeebc..2e97fc606d6 100644 --- a/forge-gui/res/cardsfolder/b/battlesong_berserker.txt +++ b/forge-gui/res/cardsfolder/b/battlesong_berserker.txt @@ -1,8 +1,8 @@ -Name:Battlesong Berserker -ManaCost:3 R -Types:Creature Human Berserker -PT:3/4 -T:Mode$ AttackersDeclared | AttackingPlayer$ You | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever you attack, target creature you control gets +1/+0 and gains menace until end of turn. (It can't be blocked except by two or more creatures.) -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumAtt$ +1 | KW$ Menace -SVar:PlayMain1:TRUE -Oracle:Whenever you attack, target creature you control gets +1/+0 and gains menace until end of turn. (It can't be blocked except by two or more creatures.) +Name:Battlesong Berserker +ManaCost:3 R +Types:Creature Human Berserker +PT:3/4 +T:Mode$ AttackersDeclared | AttackingPlayer$ You | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever you attack, target creature you control gets +1/+0 and gains menace until end of turn. (It can't be blocked except by two or more creatures.) +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumAtt$ +1 | KW$ Menace +SVar:PlayMain1:TRUE +Oracle:Whenever you attack, target creature you control gets +1/+0 and gains menace until end of turn. (It can't be blocked except by two or more creatures.) diff --git a/forge-gui/res/cardsfolder/b/beast_kin_ranger.txt b/forge-gui/res/cardsfolder/b/beast_kin_ranger.txt index fafd82b7e4c..03a6d5ece07 100644 --- a/forge-gui/res/cardsfolder/b/beast_kin_ranger.txt +++ b/forge-gui/res/cardsfolder/b/beast_kin_ranger.txt @@ -1,9 +1,9 @@ -Name:Beast-Kin Ranger -ManaCost:2 G -Types:Creature Elf Ranger -PT:3/3 -K:Trample -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever another creature you control enters, this creature gets +1/+0 until end of turn. -SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ +1 -SVar:BuffedBy:Creature +Name:Beast-Kin Ranger +ManaCost:2 G +Types:Creature Elf Ranger +PT:3/3 +K:Trample +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever another creature you control enters, this creature gets +1/+0 until end of turn. +SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ +1 +SVar:BuffedBy:Creature Oracle:Trample (This creature can deal excess combat damage to the player or planeswalker it's attacking.)\nWhenever another creature you control enters, this creature gets +1/+0 until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/b/beastie_beatdown.txt b/forge-gui/res/cardsfolder/b/beastie_beatdown.txt index 3f2ece9b25f..7895b26e6af 100644 --- a/forge-gui/res/cardsfolder/b/beastie_beatdown.txt +++ b/forge-gui/res/cardsfolder/b/beastie_beatdown.txt @@ -1,7 +1,7 @@ -Name:Beastie Beatdown -ManaCost:R G -Types:Sorcery -A:SP$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Choose target creature you control | Condition$ Delirium | AILogic$ Fight | CounterType$ P1P1 | CounterNum$ 2 | SubAbility$ DBDealDamage | SpellDescription$ Choose target creature you control and target creature an opponent controls. Delirium — If there are four or more card types among cards in your graveyard, put two +1/+1 counters on the creature you control. The creature you control deals damage equal to its power to the creature an opponent controls. -SVar:DBDealDamage:DB$ DealDamage | ValidTgts$ Creature.OppCtrl | AILogic$ PowerDmg | TgtPrompt$ Select target creature an opponent controls | NumDmg$ X | DamageSource$ ParentTarget -SVar:X:ParentTargeted$CardPower -Oracle:Choose target creature you control and target creature an opponent controls.\nDelirium — If there are four or more card types among cards in your graveyard, put two +1/+1 counters on the creature you control.\nThe creature you control deals damage equal to its power to the creature an opponent controls. +Name:Beastie Beatdown +ManaCost:R G +Types:Sorcery +A:SP$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Choose target creature you control | Condition$ Delirium | AILogic$ Fight | CounterType$ P1P1 | CounterNum$ 2 | SubAbility$ DBDealDamage | SpellDescription$ Choose target creature you control and target creature an opponent controls. Delirium — If there are four or more card types among cards in your graveyard, put two +1/+1 counters on the creature you control. The creature you control deals damage equal to its power to the creature an opponent controls. +SVar:DBDealDamage:DB$ DealDamage | ValidTgts$ Creature.OppCtrl | AILogic$ PowerDmg | TgtPrompt$ Select target creature an opponent controls | NumDmg$ X | DamageSource$ ParentTarget +SVar:X:ParentTargeted$CardPower +Oracle:Choose target creature you control and target creature an opponent controls.\nDelirium — If there are four or more card types among cards in your graveyard, put two +1/+1 counters on the creature you control.\nThe creature you control deals damage equal to its power to the creature an opponent controls. diff --git a/forge-gui/res/cardsfolder/b/bigfin_bouncer.txt b/forge-gui/res/cardsfolder/b/bigfin_bouncer.txt index a68f9d8d16f..e1166c28b1e 100644 --- a/forge-gui/res/cardsfolder/b/bigfin_bouncer.txt +++ b/forge-gui/res/cardsfolder/b/bigfin_bouncer.txt @@ -1,7 +1,7 @@ -Name:Bigfin Bouncer -ManaCost:3 U -Types:Creature Shark Pirate -PT:3/2 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, return target creature an opponent controls to its owner's hand. -SVar:TrigChangeZone:DB$ ChangeZone | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | Origin$ Battlefield | Destination$ Hand +Name:Bigfin Bouncer +ManaCost:3 U +Types:Creature Shark Pirate +PT:3/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, return target creature an opponent controls to its owner's hand. +SVar:TrigChangeZone:DB$ ChangeZone | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | Origin$ Battlefield | Destination$ Hand Oracle:When this creature enters, return target creature an opponent controls to its owner's hand. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/b/billowing_shriekmass.txt b/forge-gui/res/cardsfolder/b/billowing_shriekmass.txt index b0e2002ec44..d4e19f7bebc 100644 --- a/forge-gui/res/cardsfolder/b/billowing_shriekmass.txt +++ b/forge-gui/res/cardsfolder/b/billowing_shriekmass.txt @@ -1,11 +1,11 @@ -Name:Billowing Shriekmass -ManaCost:3 B -Types:Creature Spirit -PT:2/3 -K:Flying -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMill | TriggerDescription$ When this creature enters, mill three cards. (Put the top three cards of your library into your graveyard.) -SVar:TrigMill:DB$ Mill | Defined$ You | NumCards$ 3 -S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 2 | AddToughness$ 1 | Condition$ Threshold | Description$ Threshold — This creature gets +2/+1 as long as there are seven or more cards in your graveyard. -DeckHas:Ability$Mill|Graveyard -DeckHints:Ability$Mill|Graveyard -Oracle:Flying\nWhen this creature enters, mill three cards. (Put the top three cards of your library into your graveyard.)\nThreshold — This creature gets +2/+1 as long as there are seven or more cards in your graveyard. +Name:Billowing Shriekmass +ManaCost:3 B +Types:Creature Spirit +PT:2/3 +K:Flying +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMill | TriggerDescription$ When this creature enters, mill three cards. (Put the top three cards of your library into your graveyard.) +SVar:TrigMill:DB$ Mill | Defined$ You | NumCards$ 3 +S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 2 | AddToughness$ 1 | Condition$ Threshold | Description$ Threshold — This creature gets +2/+1 as long as there are seven or more cards in your graveyard. +DeckHas:Ability$Mill|Graveyard +DeckHints:Ability$Mill|Graveyard +Oracle:Flying\nWhen this creature enters, mill three cards. (Put the top three cards of your library into your graveyard.)\nThreshold — This creature gets +2/+1 as long as there are seven or more cards in your graveyard. diff --git a/forge-gui/res/cardsfolder/b/blasphemous_edict.txt b/forge-gui/res/cardsfolder/b/blasphemous_edict.txt index 4b5231d8fcd..642dca164be 100644 --- a/forge-gui/res/cardsfolder/b/blasphemous_edict.txt +++ b/forge-gui/res/cardsfolder/b/blasphemous_edict.txt @@ -1,6 +1,6 @@ -Name:Blasphemous Edict -ManaCost:3 B B -Types:Sorcery -S:Mode$ AlternativeCost | ValidSA$ Spell.Self | EffectZone$ All | Cost$ B | IsPresent$ Creature | PresentCompare$ GE13 | Description$ You may pay {B} rather than pay this spell's mana cost if there are thirteen or more creatures on the battlefield. -A:SP$ Sacrifice | Amount$ 13 | SacValid$ Creature | Defined$ Player | SpellDescription$ Each player sacrifices thirteen creatures of their choice. +Name:Blasphemous Edict +ManaCost:3 B B +Types:Sorcery +S:Mode$ AlternativeCost | ValidSA$ Spell.Self | EffectZone$ All | Cost$ B | IsPresent$ Creature | PresentCompare$ GE13 | Description$ You may pay {B} rather than pay this spell's mana cost if there are thirteen or more creatures on the battlefield. +A:SP$ Sacrifice | Amount$ 13 | SacValid$ Creature | Defined$ Player | SpellDescription$ Each player sacrifices thirteen creatures of their choice. Oracle:You may pay {B} rather than pay this spell's mana cost if there are thirteen or more creatures on the battlefield.\nEach player sacrifices thirteen creatures of their choice. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/b/boltwave.txt b/forge-gui/res/cardsfolder/b/boltwave.txt index bfb93bbfc05..f450fb02b39 100644 --- a/forge-gui/res/cardsfolder/b/boltwave.txt +++ b/forge-gui/res/cardsfolder/b/boltwave.txt @@ -1,5 +1,5 @@ -Name:Boltwave -ManaCost:R -Types:Sorcery -A:SP$ DealDamage | Defined$ Player.Opponent | NumDmg$ 3 | SpellDescription$ CARDNAME deals 3 damage to each opponent. +Name:Boltwave +ManaCost:R +Types:Sorcery +A:SP$ DealDamage | Defined$ Player.Opponent | NumDmg$ 3 | SpellDescription$ CARDNAME deals 3 damage to each opponent. Oracle:Boltwave deals 3 damage to each opponent. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/b/bottomless_pool_locker_room.txt b/forge-gui/res/cardsfolder/b/bottomless_pool_locker_room.txt index 1309547a77f..ea10cc0fed9 100644 --- a/forge-gui/res/cardsfolder/b/bottomless_pool_locker_room.txt +++ b/forge-gui/res/cardsfolder/b/bottomless_pool_locker_room.txt @@ -1,16 +1,16 @@ -Name:Bottomless Pool -ManaCost:U -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigReturn | TriggerDescription$ When you unlock this door, return up to one target creature to its owner's hand. -SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select up to one target creature | Origin$ Battlefield | Destination$ Hand -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, return up to one target creature to its owner's hand. - -ALTERNATE - -Name:Locker Room -ManaCost:4 U -Types:Enchantment Room -T:Mode$ DamageDoneOnce | CombatDamage$ True | ValidSource$ Creature.YouCtrl | TriggerZones$ Battlefield | ValidTarget$ Player | Execute$ TrigDraw | TriggerDescription$ Whenever one or more creatures you control deal combat damage to a player, draw a card. -SVar:TrigDraw:DB$ Draw +Name:Bottomless Pool +ManaCost:U +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigReturn | TriggerDescription$ When you unlock this door, return up to one target creature to its owner's hand. +SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select up to one target creature | Origin$ Battlefield | Destination$ Hand +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, return up to one target creature to its owner's hand. + +ALTERNATE + +Name:Locker Room +ManaCost:4 U +Types:Enchantment Room +T:Mode$ DamageDoneOnce | CombatDamage$ True | ValidSource$ Creature.YouCtrl | TriggerZones$ Battlefield | ValidTarget$ Player | Execute$ TrigDraw | TriggerDescription$ Whenever one or more creatures you control deal combat damage to a player, draw a card. +SVar:TrigDraw:DB$ Draw Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever one or more creatures you control deal combat damage to a player, draw a card. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/b/braulios_of_pheres_band.txt b/forge-gui/res/cardsfolder/b/braulios_of_pheres_band.txt index afaa44b060e..46b2b70ee3a 100644 --- a/forge-gui/res/cardsfolder/b/braulios_of_pheres_band.txt +++ b/forge-gui/res/cardsfolder/b/braulios_of_pheres_band.txt @@ -1,11 +1,11 @@ -Name:Braulios of Pheres Band -ManaCost:3 G G -Types:Legendary Creature Centaur Scout -PT:*/* -S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAME's power and toughness are each equal to the number of lands you control. -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever CARDNAME attacks, draw a card, then you may put a land card from your hand onto the battlefield. -SVar:TrigDraw:DB$ Draw | SubAbility$ DBChangeZone -SVar:DBChangeZone:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Land | ChangeNum$ 1 | Optional$ You -SVar:X:Count$Valid Land.YouCtrl -SVar:BuffedBy:Land +Name:Braulios of Pheres Band +ManaCost:3 G G +Types:Legendary Creature Centaur Scout +PT:*/* +S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ X | SetToughness$ X | Description$ CARDNAME's power and toughness are each equal to the number of lands you control. +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever CARDNAME attacks, draw a card, then you may put a land card from your hand onto the battlefield. +SVar:TrigDraw:DB$ Draw | SubAbility$ DBChangeZone +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Land | ChangeNum$ 1 | Optional$ You +SVar:X:Count$Valid Land.YouCtrl +SVar:BuffedBy:Land Oracle:Braulios of Pheres Band's power and toughness are each equal to the number of lands you control.\nWhenever Braulios of Pheres Band attacks, draw a card, then you may put a land card from your hand onto the battlefield. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/b/brigone_soldier_of_meletis.txt b/forge-gui/res/cardsfolder/b/brigone_soldier_of_meletis.txt index 8e9863eb39e..2ce09160f78 100644 --- a/forge-gui/res/cardsfolder/b/brigone_soldier_of_meletis.txt +++ b/forge-gui/res/cardsfolder/b/brigone_soldier_of_meletis.txt @@ -1,9 +1,9 @@ -Name:Brigone, Soldier of Meletis -ManaCost:1 W -Types:Legendary Creature Human Soldier -PT:2/2 -K:Vigilance -T:Mode$ SpellCast | ValidActivatingPlayer$ You | TargetsValid$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigCounters | TriggerDescription$ Heroic — Whenever you cast a spell that targets CARDNAME, put a +1/+1 counter on NICKNAME. -SVar:TrigCounters:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 -A:AB$ Draw | Cost$ T SubCounter<1/P1P1> | SpellDescription$ Draw a card. +Name:Brigone, Soldier of Meletis +ManaCost:1 W +Types:Legendary Creature Human Soldier +PT:2/2 +K:Vigilance +T:Mode$ SpellCast | ValidActivatingPlayer$ You | TargetsValid$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigCounters | TriggerDescription$ Heroic — Whenever you cast a spell that targets CARDNAME, put a +1/+1 counter on NICKNAME. +SVar:TrigCounters:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +A:AB$ Draw | Cost$ T SubCounter<1/P1P1> | SpellDescription$ Draw a card. Oracle:Vigilance\nHeroic — Whenever you cast a spell that targets Brigone, Soldier of Meletis, put a +1/+1 counter on Brigone.\n{T}, Remove a +1/+1 counter from Brigone: Draw a card. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/b/bulk_up.txt b/forge-gui/res/cardsfolder/b/bulk_up.txt index fef34370f83..193b53ede7a 100644 --- a/forge-gui/res/cardsfolder/b/bulk_up.txt +++ b/forge-gui/res/cardsfolder/b/bulk_up.txt @@ -1,6 +1,6 @@ -Name:Bulk Up -ManaCost:1 R -Types:Instant -A:SP$ Pump | ValidTgts$ Creature | NumAtt$ Double | StackDescription$ REP target creature_{c:Targeted} | SpellDescription$ Double target creature's power until end of turn. -K:Flashback:4 R R +Name:Bulk Up +ManaCost:1 R +Types:Instant +A:SP$ Pump | ValidTgts$ Creature | NumAtt$ Double | StackDescription$ REP target creature_{c:Targeted} | SpellDescription$ Double target creature's power until end of turn. +K:Flashback:4 R R Oracle:Double target creature's power until end of turn.\nFlashback {4}{R}{R} (You may cast this card from your graveyard for its flashback cost. Then exile it.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/cackling_prowler.txt b/forge-gui/res/cardsfolder/c/cackling_prowler.txt index 40e3cd939c6..74c7e59ead2 100644 --- a/forge-gui/res/cardsfolder/c/cackling_prowler.txt +++ b/forge-gui/res/cardsfolder/c/cackling_prowler.txt @@ -1,9 +1,9 @@ -Name:Cackling Prowler -ManaCost:3 G -Types:Creature Hyena Rogue -PT:4/3 -K:Ward:2 -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ Morbid | SVarCompare$ GE1 | Execute$ TrigPutCounter | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on this creature. -SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 -SVar:Morbid:Count$Morbid.1.0 +Name:Cackling Prowler +ManaCost:3 G +Types:Creature Hyena Rogue +PT:4/3 +K:Ward:2 +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ Morbid | SVarCompare$ GE1 | Execute$ TrigPutCounter | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on this creature. +SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 +SVar:Morbid:Count$Morbid.1.0 Oracle:Ward {2} (Whenever this creature becomes the target of a spell or ability an opponent controls, counter it unless that player pays {2}.)\nMorbid — At the beginning of your end step, if a creature died this turn, put a +1/+1 counter on this creature. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/cackling_slasher.txt b/forge-gui/res/cardsfolder/c/cackling_slasher.txt index 5549bcf1ad1..5fb47d3484d 100644 --- a/forge-gui/res/cardsfolder/c/cackling_slasher.txt +++ b/forge-gui/res/cardsfolder/c/cackling_slasher.txt @@ -1,8 +1,8 @@ -Name:Cackling Slasher -ManaCost:3 B -Types:Creature Human Assassin -PT:3/3 -K:Deathtouch -K:etbCounter:P1P1:1:CheckSVar$ Count$Morbid.1.0:CARDNAME enters with a +1/+1 counter on it if a creature died this turn. -DeckHas:Ability$Counters -Oracle:Deathtouch\nCackling Slasher enters with a +1/+1 counter on it if a creature died this turn. +Name:Cackling Slasher +ManaCost:3 B +Types:Creature Human Assassin +PT:3/3 +K:Deathtouch +K:etbCounter:P1P1:1:CheckSVar$ Count$Morbid.1.0:CARDNAME enters with a +1/+1 counter on it if a creature died this turn. +DeckHas:Ability$Counters +Oracle:Deathtouch\nCackling Slasher enters with a +1/+1 counter on it if a creature died this turn. diff --git a/forge-gui/res/cardsfolder/c/cat_collector.txt b/forge-gui/res/cardsfolder/c/cat_collector.txt index c02f58d5165..4788ed9cbd9 100644 --- a/forge-gui/res/cardsfolder/c/cat_collector.txt +++ b/forge-gui/res/cardsfolder/c/cat_collector.txt @@ -1,11 +1,11 @@ -Name:Cat Collector -ManaCost:2 W -Types:Creature Human Citizen -PT:3/2 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigFood | TriggerDescription$ When this creature enters, create a Food token. (It's an artifact with "2, {T}, Sacrifice this token: You gain 3 life.") -SVar:TrigFood:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_food_sac | TokenOwner$ You -T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | FirstTime$ True | PlayerTurn$ True | Execute$ DBToken | TriggerDescription$ Whenever you gain life for the first time during each of your turns, create a 1/1 white Cat creature token. -SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_cat | TokenOwner$ You -DeckHas:Ability$LifeGain|Token|Food -DeckHints:Ability$LifeGain -Oracle:When this creature enters, create a Food token. (It's an artifact with "2, {T}, Sacrifice this token: You gain 3 life.")\nWhenever you gain life for the first time during each of your turns, create a 1/1 white Cat creature token. +Name:Cat Collector +ManaCost:2 W +Types:Creature Human Citizen +PT:3/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigFood | TriggerDescription$ When this creature enters, create a Food token. (It's an artifact with "2, {T}, Sacrifice this token: You gain 3 life.") +SVar:TrigFood:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_food_sac | TokenOwner$ You +T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | FirstTime$ True | PlayerTurn$ True | Execute$ DBToken | TriggerDescription$ Whenever you gain life for the first time during each of your turns, create a 1/1 white Cat creature token. +SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_cat | TokenOwner$ You +DeckHas:Ability$LifeGain|Token|Food +DeckHints:Ability$LifeGain +Oracle:When this creature enters, create a Food token. (It's an artifact with "2, {T}, Sacrifice this token: You gain 3 life.")\nWhenever you gain life for the first time during each of your turns, create a 1/1 white Cat creature token. diff --git a/forge-gui/res/cardsfolder/c/celestial_armor.txt b/forge-gui/res/cardsfolder/c/celestial_armor.txt index 2b2259d1112..85c68f9c092 100644 --- a/forge-gui/res/cardsfolder/c/celestial_armor.txt +++ b/forge-gui/res/cardsfolder/c/celestial_armor.txt @@ -1,10 +1,10 @@ -Name:Celestial Armor -ManaCost:2 W -Types:Artifact Equipment -K:Flash -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigAttach | TriggerDescription$ When this Equipment enters, attach it to target creature you control. That creature gains hexproof and indestructible until end of turn. -SVar:TrigAttach:DB$ Attach | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | SubAbility$ DBPump -SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Hexproof & Indestructible -S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddKeyword$ Flying | Description$ Equipped creature gets +2/+0 and has flying. -K:Equip:3 W -Oracle:Flash (You may cast this spell any time you could cast an instant.)\nWhen this Equipment enters, attach it to target creature you control. That creature gains hexproof and indestructible until end of turn.\nEquipped creature gets +2/+0 and has flying.\nEquip {3}{W} ({3}{W}: Attach to target creature you control. Equip only as a sorcery.) +Name:Celestial Armor +ManaCost:2 W +Types:Artifact Equipment +K:Flash +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigAttach | TriggerDescription$ When this Equipment enters, attach it to target creature you control. That creature gains hexproof and indestructible until end of turn. +SVar:TrigAttach:DB$ Attach | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | SubAbility$ DBPump +SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Hexproof & Indestructible +S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddKeyword$ Flying | Description$ Equipped creature gets +2/+0 and has flying. +K:Equip:3 W +Oracle:Flash (You may cast this spell any time you could cast an instant.)\nWhen this Equipment enters, attach it to target creature you control. That creature gains hexproof and indestructible until end of turn.\nEquipped creature gets +2/+0 and has flying.\nEquip {3}{W} ({3}{W}: Attach to target creature you control. Equip only as a sorcery.) diff --git a/forge-gui/res/cardsfolder/c/cephalid_inkmage.txt b/forge-gui/res/cardsfolder/c/cephalid_inkmage.txt index f5784419be2..81f14369d22 100644 --- a/forge-gui/res/cardsfolder/c/cephalid_inkmage.txt +++ b/forge-gui/res/cardsfolder/c/cephalid_inkmage.txt @@ -1,9 +1,9 @@ -Name:Cephalid Inkmage -ManaCost:2 U -Types:Creature Octopus Wizard -PT:2/2 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSurveil | TriggerDescription$ When this creature enters, surveil 3. (Look at the top three cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.) -SVar:TrigSurveil:DB$ Surveil | Amount$ 3 -S:Mode$ CantBlockBy | ValidAttacker$ Card.Self | Condition$ Threshold | Description$ Threshold — This creature can't be blocked as long as there are seven or more cards in your graveyard. -DeckHas:Ability$Surveil|Graveyard -Oracle:When this creature enters, surveil 3. (Look at the top three cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.)\nThreshold — This creature can't be blocked as long as there are seven or more cards in your graveyard. +Name:Cephalid Inkmage +ManaCost:2 U +Types:Creature Octopus Wizard +PT:2/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSurveil | TriggerDescription$ When this creature enters, surveil 3. (Look at the top three cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.) +SVar:TrigSurveil:DB$ Surveil | Amount$ 3 +S:Mode$ CantBlockBy | ValidAttacker$ Card.Self | Condition$ Threshold | Description$ Threshold — This creature can't be blocked as long as there are seven or more cards in your graveyard. +DeckHas:Ability$Surveil|Graveyard +Oracle:When this creature enters, surveil 3. (Look at the top three cards of your library, then put any number of them into your graveyard and the rest on top of your library in any order.)\nThreshold — This creature can't be blocked as long as there are seven or more cards in your graveyard. diff --git a/forge-gui/res/cardsfolder/c/charred_foyer_warped_space.txt b/forge-gui/res/cardsfolder/c/charred_foyer_warped_space.txt index fa5c20b80e1..ce7183b8a53 100644 --- a/forge-gui/res/cardsfolder/c/charred_foyer_warped_space.txt +++ b/forge-gui/res/cardsfolder/c/charred_foyer_warped_space.txt @@ -1,18 +1,18 @@ -Name:Charred Foyer -ManaCost:3 R -Types:Enchantment Room -T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | ValidPlayer$ You | Execute$ TrigDig | TriggerDescription$ At the beginning of your upkeep, exile the top card of your library. You may play that card this turn. -SVar:TrigDig:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect -SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ MayPlay | SubAbility$ DBCleanup | ExileOnMoved$ Exile -SVar:MayPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play the exiled card this turn. -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nAt the beginning of your upkeep, exile the top card of your library. You may play that card this turn. - -ALTERNATE - -Name:Warped Space -ManaCost:4 R R -Types:Enchantment Room -S:Mode$ Continuous | MayPlay$ True | MayPlayAltManaCost$ 0 | MayPlayLimit$ 1 | MayPlayDontGrantZonePermissions$ True | Affected$ Card.YouCtrl+nonLand | AffectedZone$ Exile| Description$ Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast from exile. +Name:Charred Foyer +ManaCost:3 R +Types:Enchantment Room +T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | ValidPlayer$ You | Execute$ TrigDig | TriggerDescription$ At the beginning of your upkeep, exile the top card of your library. You may play that card this turn. +SVar:TrigDig:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect +SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ MayPlay | SubAbility$ DBCleanup | ExileOnMoved$ Exile +SVar:MayPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play the exiled card this turn. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nAt the beginning of your upkeep, exile the top card of your library. You may play that card this turn. + +ALTERNATE + +Name:Warped Space +ManaCost:4 R R +Types:Enchantment Room +S:Mode$ Continuous | MayPlay$ True | MayPlayAltManaCost$ 0 | MayPlayLimit$ 1 | MayPlayDontGrantZonePermissions$ True | Affected$ Card.YouCtrl+nonLand | AffectedZone$ Exile| Description$ Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast from exile. Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nOnce each turn, you may pay {0} rather than pay the mana cost for a spell you cast from exile. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/claws_out.txt b/forge-gui/res/cardsfolder/c/claws_out.txt index c9ac6390eab..c768a3f3a3b 100644 --- a/forge-gui/res/cardsfolder/c/claws_out.txt +++ b/forge-gui/res/cardsfolder/c/claws_out.txt @@ -1,8 +1,8 @@ -Name:Claws Out -ManaCost:3 W W -Types:Instant -S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each Cat you control. -A:SP$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +2 | NumDef$ +2 | SpellDescription$ Creatures you control get +2/+2 until end of turn. -SVar:X:Count$Valid Cat.YouCtrl -DeckHints:Type$Cat -Oracle:This spell costs {1} less to cast for each Cat you control.\nCreatures you control get +2/+2 until end of turn. +Name:Claws Out +ManaCost:3 W W +Types:Instant +S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each Cat you control. +A:SP$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +2 | NumDef$ +2 | SpellDescription$ Creatures you control get +2/+2 until end of turn. +SVar:X:Count$Valid Cat.YouCtrl +DeckHints:Type$Cat +Oracle:This spell costs {1} less to cast for each Cat you control.\nCreatures you control get +2/+2 until end of turn. diff --git a/forge-gui/res/cardsfolder/c/cleon_merry_champion.txt b/forge-gui/res/cardsfolder/c/cleon_merry_champion.txt index 81b68c92d1b..4756e5960cd 100644 --- a/forge-gui/res/cardsfolder/c/cleon_merry_champion.txt +++ b/forge-gui/res/cardsfolder/c/cleon_merry_champion.txt @@ -1,11 +1,11 @@ -Name:Cleon, Merry Champion -ManaCost:2 R -Types:Legendary Creature Human Soldier -PT:2/2 -K:Double Strike -T:Mode$ SpellCast | ValidActivatingPlayer$ You | TargetsValid$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigDig | TriggerDescription$ Heroic — Whenever you cast a spell that targets NICKNAME, exile the top card of your library. You may play that card until the end of your next turn. -SVar:TrigDig:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect -SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ STPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn -SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play that card until the end of your next turn. -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +Name:Cleon, Merry Champion +ManaCost:2 R +Types:Legendary Creature Human Soldier +PT:2/2 +K:Double Strike +T:Mode$ SpellCast | ValidActivatingPlayer$ You | TargetsValid$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigDig | TriggerDescription$ Heroic — Whenever you cast a spell that targets NICKNAME, exile the top card of your library. You may play that card until the end of your next turn. +SVar:TrigDig:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect +SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ STPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn +SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play that card until the end of your next turn. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True Oracle:Double strike\nHeroic — Whenever you cast a spell that targets Cleon, exile the top card of your library. You may play that card until the end of your next turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/clinquant_skymage.txt b/forge-gui/res/cardsfolder/c/clinquant_skymage.txt index 41447e48086..85795f2f095 100644 --- a/forge-gui/res/cardsfolder/c/clinquant_skymage.txt +++ b/forge-gui/res/cardsfolder/c/clinquant_skymage.txt @@ -1,8 +1,8 @@ -Name:Clinquant Skymage -ManaCost:3 U -Types:Creature Bird Wizard -PT:1/1 -K:Flying -T:Mode$ Drawn | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you draw a card, put a +1/+1 counter on this creature. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +Name:Clinquant Skymage +ManaCost:3 U +Types:Creature Bird Wizard +PT:1/1 +K:Flying +T:Mode$ Drawn | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you draw a card, put a +1/+1 counter on this creature. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 Oracle:Flying\nWhenever you draw a card, put a +1/+1 counter on this creature. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/courageous_goblin.txt b/forge-gui/res/cardsfolder/c/courageous_goblin.txt index c242d2a0f75..2467e3b398a 100644 --- a/forge-gui/res/cardsfolder/c/courageous_goblin.txt +++ b/forge-gui/res/cardsfolder/c/courageous_goblin.txt @@ -1,8 +1,8 @@ -Name:Courageous Goblin -ManaCost:1 R -Types:Creature Goblin -PT:2/2 -T:Mode$ Attacks | ValidCard$ Card.Self | IsPresent$ Creature.YouCtrl+powerGE4 | PresentCompare$ GE1 | NoResolvingCheck$ True | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever this creature attacks while you control a creature with power 4 or greater, this creature gets +1/+0 and gains menace until end of turn. -SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ 1 | KW$ Menace -SVar:HasAttackEffect:TRUE +Name:Courageous Goblin +ManaCost:1 R +Types:Creature Goblin +PT:2/2 +T:Mode$ Attacks | ValidCard$ Card.Self | IsPresent$ Creature.YouCtrl+powerGE4 | PresentCompare$ GE1 | NoResolvingCheck$ True | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever this creature attacks while you control a creature with power 4 or greater, this creature gets +1/+0 and gains menace until end of turn. +SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ 1 | KW$ Menace +SVar:HasAttackEffect:TRUE Oracle:Whenever this creature attacks while you control a creature with power 4 or greater, this creature gets +1/+0 and gains menace until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/crackling_cyclops.txt b/forge-gui/res/cardsfolder/c/crackling_cyclops.txt index 90055500f35..96a5be33947 100644 --- a/forge-gui/res/cardsfolder/c/crackling_cyclops.txt +++ b/forge-gui/res/cardsfolder/c/crackling_cyclops.txt @@ -1,8 +1,8 @@ -Name:Crackling Cyclops -ManaCost:2 R -Types:Creature Cyclops Wizard -PT:0/4 -T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, this creature gets +3/+0 until end of turn. -SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ 3 -SVar:BuffedBy:Card.nonLand+nonCreature +Name:Crackling Cyclops +ManaCost:2 R +Types:Creature Cyclops Wizard +PT:0/4 +T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, this creature gets +3/+0 until end of turn. +SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ 3 +SVar:BuffedBy:Card.nonLand+nonCreature Oracle:Whenever you cast a noncreature spell, this creature gets +3/+0 until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/cramped_vents_access_room.txt b/forge-gui/res/cardsfolder/c/cramped_vents_access_room.txt index bae123ca17b..fb69e4ed9ae 100644 --- a/forge-gui/res/cardsfolder/c/cramped_vents_access_room.txt +++ b/forge-gui/res/cardsfolder/c/cramped_vents_access_room.txt @@ -1,16 +1,16 @@ -Name:Cramped Vents -ManaCost:3 B -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigDealDamage | TriggerDescription$ When you unlock this door, this Room deals 6 damage to target creature an opponent controls. You gain life equal to the excess damage dealt this way. -SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | NumDmg$ 6 | ExcessSVar$ Excess | SubAbility$ DBGainLife -SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ Excess -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, this Room deals 6 damage to target creature an opponent controls. You gain life equal to the excess damage dealt this way. - -ALTERNATE - -Name:Access Maze -ManaCost:5 B B -Types:Enchantment Room -S:Mode$ Continuous | Condition$ PlayerTurn | Affected$ Card.YouCtrl+nonLand | MayPlayLimit$ 1 | MayPlay$ True | MayPlayAltManaCost$ PayLife | MayPlayDontGrantZonePermissions$ True | AffectedZone$ Hand | Description$ Once during each of your turns, you may cast a spell from your hand by paying life equal to its mana value rather than paying its mana cost. +Name:Cramped Vents +ManaCost:3 B +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigDealDamage | TriggerDescription$ When you unlock this door, this Room deals 6 damage to target creature an opponent controls. You gain life equal to the excess damage dealt this way. +SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | NumDmg$ 6 | ExcessSVar$ Excess | SubAbility$ DBGainLife +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ Excess +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, this Room deals 6 damage to target creature an opponent controls. You gain life equal to the excess damage dealt this way. + +ALTERNATE + +Name:Access Maze +ManaCost:5 B B +Types:Enchantment Room +S:Mode$ Continuous | Condition$ PlayerTurn | Affected$ Card.YouCtrl+nonLand | MayPlayLimit$ 1 | MayPlay$ True | MayPlayAltManaCost$ PayLife | MayPlayDontGrantZonePermissions$ True | AffectedZone$ Hand | Description$ Once during each of your turns, you may cast a spell from your hand by paying life equal to its mana value rather than paying its mana cost. Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nOnce during each of your turns, you may cast a spell from your hand by paying life equal to its mana value rather than paying its mana cost. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/creeping_peeper.txt b/forge-gui/res/cardsfolder/c/creeping_peeper.txt index 08c24d5284d..65d47fb8913 100644 --- a/forge-gui/res/cardsfolder/c/creeping_peeper.txt +++ b/forge-gui/res/cardsfolder/c/creeping_peeper.txt @@ -1,6 +1,6 @@ -Name:Creeping Peeper -ManaCost:1 U -Types:Creature Eye -PT:2/1 -A:AB$ Mana | Cost$ T | Produced$ U | Amount$ 1 | RestrictValid$ Spell.Enchantment,Static.Unlock,Static.isTurnFaceUp | SpellDescription$ Add {U}. Spend this mana only to cast an enchantment spell, unlock a door, or turn a permanent face up. +Name:Creeping Peeper +ManaCost:1 U +Types:Creature Eye +PT:2/1 +A:AB$ Mana | Cost$ T | Produced$ U | Amount$ 1 | RestrictValid$ Spell.Enchantment,Static.Unlock,Static.isTurnFaceUp | SpellDescription$ Add {U}. Spend this mana only to cast an enchantment spell, unlock a door, or turn a permanent face up. Oracle:{T}: Add {U}. Spend this mana only to cast an enchantment spell, unlock a door, or turn a permanent face up. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/curator_of_destinies.txt b/forge-gui/res/cardsfolder/c/curator_of_destinies.txt index 5391591bdd2..f4350bebb8c 100644 --- a/forge-gui/res/cardsfolder/c/curator_of_destinies.txt +++ b/forge-gui/res/cardsfolder/c/curator_of_destinies.txt @@ -1,12 +1,12 @@ -Name:Curator of Destinies -ManaCost:4 U U -Types:Creature Sphinx -PT:5/5 -R:Event$ Counter | ValidCard$ Card.Self | ValidSA$ Spell | Layer$ CantHappen | Description$ This spell can't be countered. -K:Flying -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPeekAndReveal | TriggerDescription$ When this creature enters, look at the top five cards of your library and separate them into a face-down pile and a face-up pile. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard. -SVar:TrigPeekAndReveal:DB$ PeekAndReveal | Defined$ You | PeekAmount$ 5 | NoReveal$ True | RememberPeeked$ True | SubAbility$ Separate -SVar:Separate:DB$ TwoPiles | Defined$ You | Separator$ You | Chooser$ Opponent | DefinedCards$ Remembered | ChosenPile$ DBHand | UnchosenPile$ DBGraveyard | Zone$ Library | FaceDown$ One -SVar:DBHand:DB$ ChangeZone | Defined$ Remembered | Origin$ Library | Destination$ Hand -SVar:DBGraveyard:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Library | Destination$ Graveyard +Name:Curator of Destinies +ManaCost:4 U U +Types:Creature Sphinx +PT:5/5 +R:Event$ Counter | ValidCard$ Card.Self | ValidSA$ Spell | Layer$ CantHappen | Description$ This spell can't be countered. +K:Flying +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPeekAndReveal | TriggerDescription$ When this creature enters, look at the top five cards of your library and separate them into a face-down pile and a face-up pile. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard. +SVar:TrigPeekAndReveal:DB$ PeekAndReveal | Defined$ You | PeekAmount$ 5 | NoReveal$ True | RememberPeeked$ True | SubAbility$ Separate +SVar:Separate:DB$ TwoPiles | Defined$ You | Separator$ You | Chooser$ Opponent | DefinedCards$ Remembered | ChosenPile$ DBHand | UnchosenPile$ DBGraveyard | Zone$ Library | FaceDown$ One +SVar:DBHand:DB$ ChangeZone | Defined$ Remembered | Origin$ Library | Destination$ Hand +SVar:DBGraveyard:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ Library | Destination$ Graveyard Oracle:This spell can't be countered.\nFlying\nWhen this creature enters, look at the top five cards of your library and separate them into a face-down pile and a face-up pile. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/c/cynette_jelly_drover.txt b/forge-gui/res/cardsfolder/c/cynette_jelly_drover.txt index 98b94522adf..ab576b4e373 100644 --- a/forge-gui/res/cardsfolder/c/cynette_jelly_drover.txt +++ b/forge-gui/res/cardsfolder/c/cynette_jelly_drover.txt @@ -1,10 +1,10 @@ -Name:Cynette, Jelly Drover -ManaCost:3 U -Types:Legendary Creature Human Wizard -PT:2/2 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters or dies, create a 1/1 blue Jellyfish creature token with flying. -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | Secondary$ True | TriggerDescription$ When CARDNAME enters or dies, create a 1/1 blue Jellyfish creature token with flying. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ u_1_1_jellyfish_flying | TokenOwner$ You -S:Mode$ Continuous | Affected$ Creature.withFlying+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control with flying get +1/+1. -SVar:PlayMain1:TRUE -Oracle:When Cynette, Jelly Drover enters or dies, create a 1/1 blue Jellyfish creature token with flying.\nCreatures you control with flying get +1/+1. +Name:Cynette, Jelly Drover +ManaCost:3 U +Types:Legendary Creature Human Wizard +PT:2/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters or dies, create a 1/1 blue Jellyfish creature token with flying. +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | Secondary$ True | TriggerDescription$ When CARDNAME enters or dies, create a 1/1 blue Jellyfish creature token with flying. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ u_1_1_jellyfish_flying | TokenOwner$ You +S:Mode$ Continuous | Affected$ Creature.withFlying+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures you control with flying get +1/+1. +SVar:PlayMain1:TRUE +Oracle:When Cynette, Jelly Drover enters or dies, create a 1/1 blue Jellyfish creature token with flying.\nCreatures you control with flying get +1/+1. diff --git a/forge-gui/res/cardsfolder/d/dauntless_veteran.txt b/forge-gui/res/cardsfolder/d/dauntless_veteran.txt index 8cdffc6a8c4..e82a1f50f21 100644 --- a/forge-gui/res/cardsfolder/d/dauntless_veteran.txt +++ b/forge-gui/res/cardsfolder/d/dauntless_veteran.txt @@ -1,7 +1,7 @@ -Name:Dauntless Veteran -ManaCost:1 W W -Types:Creature Human Soldier -PT:2/2 -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPumpAll | TriggerDescription$ Whenever CARDNAME attacks, creatures you control get +1/+1 until end of turn. -SVar:TrigPumpAll:DB$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +1 | NumDef$ +1 +Name:Dauntless Veteran +ManaCost:1 W W +Types:Creature Human Soldier +PT:2/2 +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPumpAll | TriggerDescription$ Whenever CARDNAME attacks, creatures you control get +1/+1 until end of turn. +SVar:TrigPumpAll:DB$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +1 | NumDef$ +1 Oracle:Whenever this creature attacks, creatures you control get +1/+1 until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/dawnwing_marshal.txt b/forge-gui/res/cardsfolder/d/dawnwing_marshal.txt index 2280cc4a15b..f4507a03e18 100644 --- a/forge-gui/res/cardsfolder/d/dawnwing_marshal.txt +++ b/forge-gui/res/cardsfolder/d/dawnwing_marshal.txt @@ -1,7 +1,7 @@ -Name:Dawnwing Marshal -ManaCost:1 W -Types:Creature Cat Soldier -PT:2/2 -K:Flying -A:AB$ PumpAll | Cost$ 4 W | ValidCards$ Creature.YouCtrl | NumAtt$ 1 | NumDef$ 1 | SpellDescription$ Creatures you control get +1/+1 until end of turn. +Name:Dawnwing Marshal +ManaCost:1 W +Types:Creature Cat Soldier +PT:2/2 +K:Flying +A:AB$ PumpAll | Cost$ 4 W | ValidCards$ Creature.YouCtrl | NumAtt$ 1 | NumDef$ 1 | SpellDescription$ Creatures you control get +1/+1 until end of turn. Oracle:Flying\n{4}{W}: Creatures you control get +1/+1 until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/dazzling_angel.txt b/forge-gui/res/cardsfolder/d/dazzling_angel.txt index 311e7d8964a..4ea2351db82 100644 --- a/forge-gui/res/cardsfolder/d/dazzling_angel.txt +++ b/forge-gui/res/cardsfolder/d/dazzling_angel.txt @@ -1,10 +1,10 @@ -Name:Dazzling Angel -ManaCost:2 W -Types:Creature Angel -PT:2/3 -K:Flying -T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever another creature you control enters, you gain 1 life. -SVar:TrigGainLife:DB$ GainLife | LifeAmount$ 1 -SVar:BuffedBy:Creature -DeckHas:Ability$LifeGain +Name:Dazzling Angel +ManaCost:2 W +Types:Creature Angel +PT:2/3 +K:Flying +T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever another creature you control enters, you gain 1 life. +SVar:TrigGainLife:DB$ GainLife | LifeAmount$ 1 +SVar:BuffedBy:Creature +DeckHas:Ability$LifeGain Oracle:Flying\nWhenever another creature you control enters, you gain 1 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/dazzling_theater_prop_room.txt b/forge-gui/res/cardsfolder/d/dazzling_theater_prop_room.txt index 623721a6ea9..9d8b8ac5b33 100644 --- a/forge-gui/res/cardsfolder/d/dazzling_theater_prop_room.txt +++ b/forge-gui/res/cardsfolder/d/dazzling_theater_prop_room.txt @@ -1,14 +1,14 @@ -Name:Dazzling Theater -ManaCost:3 W -Types:Enchantment Room -S:Mode$ Continuous | Affected$ Card.Creature+YouCtrl+wasCast | AffectedZone$ Stack | AddKeyword$ Convoke | Description$ Creature spells you cast have convoke. (Your creatures can help cast those spells. Each creature you tap while casting a creature spell pays for {1} or one mana of that creature's color.) -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nCreature spells you cast have convoke. (Your creatures can help cast those spells. Each creature you tap while casting a creature spell pays for {1} or one mana of that creature's color.) - -ALTERNATE - -Name:Prop Room -ManaCost:2 W -Types:Enchantment Room -S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddHiddenKeyword$ CARDNAME untaps during each other player's untap step. | Description$ Untap each creature you control during each other player's untap step. +Name:Dazzling Theater +ManaCost:3 W +Types:Enchantment Room +S:Mode$ Continuous | Affected$ Card.Creature+YouCtrl+wasCast | AffectedZone$ Stack | AddKeyword$ Convoke | Description$ Creature spells you cast have convoke. (Your creatures can help cast those spells. Each creature you tap while casting a creature spell pays for {1} or one mana of that creature's color.) +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nCreature spells you cast have convoke. (Your creatures can help cast those spells. Each creature you tap while casting a creature spell pays for {1} or one mana of that creature's color.) + +ALTERNATE + +Name:Prop Room +ManaCost:2 W +Types:Enchantment Room +S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddHiddenKeyword$ CARDNAME untaps during each other player's untap step. | Description$ Untap each creature you control during each other player's untap step. Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nUntap each creature you control during each other player's untap step. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/defiled_crypt_cadaver_lab.txt b/forge-gui/res/cardsfolder/d/defiled_crypt_cadaver_lab.txt index 9e3421b09cd..cccc3ef24ab 100644 --- a/forge-gui/res/cardsfolder/d/defiled_crypt_cadaver_lab.txt +++ b/forge-gui/res/cardsfolder/d/defiled_crypt_cadaver_lab.txt @@ -1,17 +1,17 @@ -Name:Defiled Crypt -ManaCost:3 B -Types:Enchantment Room -T:Mode$ ChangesZoneAll | ValidCards$ Card.YouOwn | Origin$ Graveyard | Destination$ Any | TriggerZones$ Battlefield | Execute$ TrigToken | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more cards leave your graveyard, create a 2/2 black Horror enchantment creature token. This ability triggers only once each turn. -SVar:TrigToken:DB$ Token | TokenScript$ b_2_2_e_horror -AlternateMode:Split -DeckHas:Ability$Token -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever one or more cards leave your graveyard, create a 2/2 black Horror enchantment creature token. This ability triggers only once each turn. - -ALTERNATE - -Name:Cadaver Lab -ManaCost:B -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigChangeZone | TriggerDescription$ When you unlock this door, return target creature card from your graveyard to your hand. -SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select up to one target creature -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, return target creature card from your graveyard to your hand. +Name:Defiled Crypt +ManaCost:3 B +Types:Enchantment Room +T:Mode$ ChangesZoneAll | ValidCards$ Card.YouOwn | Origin$ Graveyard | Destination$ Any | TriggerZones$ Battlefield | Execute$ TrigToken | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more cards leave your graveyard, create a 2/2 black Horror enchantment creature token. This ability triggers only once each turn. +SVar:TrigToken:DB$ Token | TokenScript$ b_2_2_e_horror +AlternateMode:Split +DeckHas:Ability$Token +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever one or more cards leave your graveyard, create a 2/2 black Horror enchantment creature token. This ability triggers only once each turn. + +ALTERNATE + +Name:Cadaver Lab +ManaCost:B +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigChangeZone | TriggerDescription$ When you unlock this door, return target creature card from your graveyard to your hand. +SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select up to one target creature +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, return target creature card from your graveyard to your hand. diff --git a/forge-gui/res/cardsfolder/d/delightful_discovery.txt b/forge-gui/res/cardsfolder/d/delightful_discovery.txt index b6061944e8a..c267708415e 100644 --- a/forge-gui/res/cardsfolder/d/delightful_discovery.txt +++ b/forge-gui/res/cardsfolder/d/delightful_discovery.txt @@ -1,8 +1,8 @@ -Name:Delightful Discovery -ManaCost:4 U -Types:Instant -S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each spell your opponents have cast this turn. -A:SP$ Scry | ScryNum$ 2 | SubAbility$ DBDraw | SpellDescription$ Scry 2, then draw two cards. (To scry 2, look at the top two cards of your library, then put any number of them on the bottom and the rest on top in any order.) -SVar:DBDraw:DB$ Draw | NumCards$ 2 -SVar:X:Count$ThisTurnCast_Card.OppCtrl +Name:Delightful Discovery +ManaCost:4 U +Types:Instant +S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ X | EffectZone$ All | Description$ This spell costs {1} less to cast for each spell your opponents have cast this turn. +A:SP$ Scry | ScryNum$ 2 | SubAbility$ DBDraw | SpellDescription$ Scry 2, then draw two cards. (To scry 2, look at the top two cards of your library, then put any number of them on the bottom and the rest on top in any order.) +SVar:DBDraw:DB$ Draw | NumCards$ 2 +SVar:X:Count$ThisTurnCast_Card.OppCtrl Oracle:This spell costs {1} less to cast for each spell your opponents have cast this turn.\nScry 2, then draw two cards. (To scry 2, look at the top two cards of your library, then put any number of them on the bottom and the rest on top in any order.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/derelict_attic_widows_walk.txt b/forge-gui/res/cardsfolder/d/derelict_attic_widows_walk.txt index 551b51499c2..9b833fc1ce8 100644 --- a/forge-gui/res/cardsfolder/d/derelict_attic_widows_walk.txt +++ b/forge-gui/res/cardsfolder/d/derelict_attic_widows_walk.txt @@ -1,17 +1,17 @@ -Name:Derelict Attic -ManaCost:2 B -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigDraw | TriggerDescription$ When you unlock this door, you draw two cards and you lose 2 life. -SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 2 | SubAbility$ DBLoseLife -SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 2 -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, you draw two cards and you lose 2 life. - -ALTERNATE - -Name:Widow's Walk -ManaCost:3 B -Types:Enchantment Room -T:Mode$ Attacks | ValidCard$ Creature.YouCtrl | Alone$ True | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever a creature you control attacks alone, it gets +1/+0 and gains deathtouch until end of turn. -SVar:TrigPump:DB$ Pump | Defined$ TriggeredAttackerLKICopy | NumAtt$ +1 | KW$ Deathtouch +Name:Derelict Attic +ManaCost:2 B +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigDraw | TriggerDescription$ When you unlock this door, you draw two cards and you lose 2 life. +SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 2 | SubAbility$ DBLoseLife +SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 2 +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, you draw two cards and you lose 2 life. + +ALTERNATE + +Name:Widow's Walk +ManaCost:3 B +Types:Enchantment Room +T:Mode$ Attacks | ValidCard$ Creature.YouCtrl | Alone$ True | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever a creature you control attacks alone, it gets +1/+0 and gains deathtouch until end of turn. +SVar:TrigPump:DB$ Pump | Defined$ TriggeredAttackerLKICopy | NumAtt$ +1 | KW$ Deathtouch Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever a creature you control attacks alone, it gets +1/+0 and gains deathtouch until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/dionus_elvish_archdruid.txt b/forge-gui/res/cardsfolder/d/dionus_elvish_archdruid.txt index 099a3e3c562..1efe751bd50 100644 --- a/forge-gui/res/cardsfolder/d/dionus_elvish_archdruid.txt +++ b/forge-gui/res/cardsfolder/d/dionus_elvish_archdruid.txt @@ -1,11 +1,11 @@ -Name:Dionus, Elvish Archdruid -ManaCost:3 G -Types:Legendary Creature Elf Druid -PT:2/3 -S:Mode$ Continuous | Affected$ Elf.YouCtrl | AddTrigger$ TrigTapped | Description$ Elves you control have "Whenever this creature becomes tapped during your turn, untap it and put a +1/+1 counter on it. This ability triggers only once each turn." -SVar:TrigTapped:Mode$ Taps | ValidCard$ Card.Self | TriggerZones$ Battlefield | ActivationLimit$ 1 | PlayerTurn$ True | Execute$ TrigUntap | TriggerDescription$ Whenever this creature becomes tapped during your turn, untap it and put a +1/+1 counter on it. This ability triggers only once each turn. -SVar:TrigUntap:DB$ Untap | Defined$ Self | SubAbility$ DBPutCounter -SVar:DBPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 -DeckHas:Ability$Counters -DeckHints:Type$Elf +Name:Dionus, Elvish Archdruid +ManaCost:3 G +Types:Legendary Creature Elf Druid +PT:2/3 +S:Mode$ Continuous | Affected$ Elf.YouCtrl | AddTrigger$ TrigTapped | Description$ Elves you control have "Whenever this creature becomes tapped during your turn, untap it and put a +1/+1 counter on it. This ability triggers only once each turn." +SVar:TrigTapped:Mode$ Taps | ValidCard$ Card.Self | TriggerZones$ Battlefield | ActivationLimit$ 1 | PlayerTurn$ True | Execute$ TrigUntap | TriggerDescription$ Whenever this creature becomes tapped during your turn, untap it and put a +1/+1 counter on it. This ability triggers only once each turn. +SVar:TrigUntap:DB$ Untap | Defined$ Self | SubAbility$ DBPutCounter +SVar:DBPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +DeckHas:Ability$Counters +DeckHints:Type$Elf Oracle:Elves you control have "Whenever this creature becomes tapped during your turn, untap it and put a +1/+1 counter on it. This ability triggers only once each turn." \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/dissection_tools.txt b/forge-gui/res/cardsfolder/d/dissection_tools.txt index 4ef8e047fc5..3678f4ac2bf 100644 --- a/forge-gui/res/cardsfolder/d/dissection_tools.txt +++ b/forge-gui/res/cardsfolder/d/dissection_tools.txt @@ -1,10 +1,10 @@ -Name:Dissection Tools -ManaCost:5 -Types:Artifact Equipment -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDread | TriggerDescription$ When CARDNAME enters, manifest dread, then attach CARDNAME to that creature. -SVar:TrigDread:DB$ ManifestDread | RememberManifested$ True | SubAbility$ DBAttach -SVar:DBAttach:DB$ Attach | Defined$ Remembered | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddToughness$ 2 | AddKeyword$ Deathtouch & Lifelink | Description$ Equipped creature has +2/+2 and has deathtouch and lifelink. -K:Equip:Sac<1/Creature> -Oracle:When Dissection Tools enters, manifest dread, then attach Dissection Tools to that creature.\nEquipped creature has +2/+2 and has deathtouch and lifelink.\nEquip—Sacrifice a creature. +Name:Dissection Tools +ManaCost:5 +Types:Artifact Equipment +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDread | TriggerDescription$ When CARDNAME enters, manifest dread, then attach CARDNAME to that creature. +SVar:TrigDread:DB$ ManifestDread | RememberManifested$ True | SubAbility$ DBAttach +SVar:DBAttach:DB$ Attach | Defined$ Remembered | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddToughness$ 2 | AddKeyword$ Deathtouch & Lifelink | Description$ Equipped creature has +2/+2 and has deathtouch and lifelink. +K:Equip:Sac<1/Creature> +Oracle:When Dissection Tools enters, manifest dread, then attach Dissection Tools to that creature.\nEquipped creature has +2/+2 and has deathtouch and lifelink.\nEquip—Sacrifice a creature. diff --git a/forge-gui/res/cardsfolder/d/divine_resilience.txt b/forge-gui/res/cardsfolder/d/divine_resilience.txt index b855d602fba..99491a183bd 100644 --- a/forge-gui/res/cardsfolder/d/divine_resilience.txt +++ b/forge-gui/res/cardsfolder/d/divine_resilience.txt @@ -1,9 +1,9 @@ -Name:Divine Resilience -ManaCost:W -Types:Instant -K:Kicker:2 W -A:SP$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | TargetMin$ X | TargetMax$ Y | KW$ Indestructible | StackDescription$ SpellDescription | SpellDescription$ Target creature you control gains indestructible until end of turn. If this spell was kicked, any number of target creatures you control gain indestructible until end of turn instead. (Damage and effects that say "destroy" don't destroy them.) -SVar:X:Count$Kicked.0.1 -SVar:Y:Count$Kicked.Z.1 -SVar:Z:Count$Valid Creature.YouCtrl +Name:Divine Resilience +ManaCost:W +Types:Instant +K:Kicker:2 W +A:SP$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | TargetMin$ X | TargetMax$ Y | KW$ Indestructible | StackDescription$ SpellDescription | SpellDescription$ Target creature you control gains indestructible until end of turn. If this spell was kicked, any number of target creatures you control gain indestructible until end of turn instead. (Damage and effects that say "destroy" don't destroy them.) +SVar:X:Count$Kicked.0.1 +SVar:Y:Count$Kicked.Z.1 +SVar:Z:Count$Valid Creature.YouCtrl Oracle:Kicker {2}{W} (You may pay an additional {2}{W} as you cast this spell.)\nTarget creature you control gains indestructible until end of turn. If this spell was kicked, any number of target creatures you control gain indestructible until end of turn instead. (Damage and effects that say "destroy" don't destroy them.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/dragon_trainer.txt b/forge-gui/res/cardsfolder/d/dragon_trainer.txt index e1c8a36c9d9..52890a8b4d8 100644 --- a/forge-gui/res/cardsfolder/d/dragon_trainer.txt +++ b/forge-gui/res/cardsfolder/d/dragon_trainer.txt @@ -1,7 +1,7 @@ -Name:Dragon Trainer -ManaCost:3 R R -Types:Creature Human -PT:1/1 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When this creature enters, create a 4/4 red Dragon creature token with flying. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_4_4_dragon_flying | TokenOwner$ You +Name:Dragon Trainer +ManaCost:3 R R +Types:Creature Human +PT:1/1 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When this creature enters, create a 4/4 red Dragon creature token with flying. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_4_4_dragon_flying | TokenOwner$ You Oracle:When this creature enters, create a 4/4 red Dragon creature token with flying. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/drake_hatcher.txt b/forge-gui/res/cardsfolder/d/drake_hatcher.txt index f6b19fdf00c..208c0fd6105 100644 --- a/forge-gui/res/cardsfolder/d/drake_hatcher.txt +++ b/forge-gui/res/cardsfolder/d/drake_hatcher.txt @@ -1,12 +1,12 @@ -Name:Drake Hatcher -ManaCost:1 U -Types:Creature Human Wizard -PT:1/3 -K:Vigilance -K:Prowess -T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | TriggerZones$ Battlefield | CombatDamage$ True | Execute$ TrigPutCounter | TriggerDescription$ Whenever this creature deals combat damage to a player, put that many incubation counters on it. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredSourceLKICopy | CounterType$ INCUBATION | CounterNum$ X -A:AB$ Token | Cost$ SubCounter<3/INCUBATION> | TokenScript$ u_2_2_drake_flying | SpellDescription$ Create a 2/2 blue Drake creature token with flying. -SVar:X:TriggerCount$DamageAmount -DeckHas:Ability$Counters|Token +Name:Drake Hatcher +ManaCost:1 U +Types:Creature Human Wizard +PT:1/3 +K:Vigilance +K:Prowess +T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | TriggerZones$ Battlefield | CombatDamage$ True | Execute$ TrigPutCounter | TriggerDescription$ Whenever this creature deals combat damage to a player, put that many incubation counters on it. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredSourceLKICopy | CounterType$ INCUBATION | CounterNum$ X +A:AB$ Token | Cost$ SubCounter<3/INCUBATION> | TokenScript$ u_2_2_drake_flying | SpellDescription$ Create a 2/2 blue Drake creature token with flying. +SVar:X:TriggerCount$DamageAmount +DeckHas:Ability$Counters|Token Oracle:Vigilance, prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.)\nWhenever this creature deals combat damage to a player, put that many incubation counters on it.\nRemove three incubation counters from this creature: Create a 2/2 blue Drake creature token with flying. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/dreadwing_scavenger.txt b/forge-gui/res/cardsfolder/d/dreadwing_scavenger.txt index 1d8449cb5b6..169fa17c592 100644 --- a/forge-gui/res/cardsfolder/d/dreadwing_scavenger.txt +++ b/forge-gui/res/cardsfolder/d/dreadwing_scavenger.txt @@ -1,13 +1,13 @@ -Name:Dreadwing Scavenger -ManaCost:1 U B -Types:Creature Nightmare Bird -PT:2/2 -K:Flying -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever this creature enters or attacks, draw a card, then discard a card. -T:Mode$ Attacks | ValidCard$ Card.Self | Secondary$ True | Execute$ TrigDraw | TriggerDescription$ Whenever this creature enters or attacks, draw a card, then discard a card. -SVar:TrigDraw:DB$ Draw | SubAbility$ TrigDiscard -SVar:TrigDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose -S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Deathtouch | Condition$ Threshold | Description$ Threshold — This creature gets +1/+1 and has deathtouch as long as there are seven or more cards in your graveyard. -SVar:HasAttackEffect:TRUE -DeckHas:Ability$Discard +Name:Dreadwing Scavenger +ManaCost:1 U B +Types:Creature Nightmare Bird +PT:2/2 +K:Flying +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever this creature enters or attacks, draw a card, then discard a card. +T:Mode$ Attacks | ValidCard$ Card.Self | Secondary$ True | Execute$ TrigDraw | TriggerDescription$ Whenever this creature enters or attacks, draw a card, then discard a card. +SVar:TrigDraw:DB$ Draw | SubAbility$ TrigDiscard +SVar:TrigDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose +S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Deathtouch | Condition$ Threshold | Description$ Threshold — This creature gets +1/+1 and has deathtouch as long as there are seven or more cards in your graveyard. +SVar:HasAttackEffect:TRUE +DeckHas:Ability$Discard Oracle:Flying\nWhenever this creature enters or attacks, draw a card, then discard a card.\nThreshold — This creature gets +1/+1 and has deathtouch as long as there are seven or more cards in your graveyard. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/d/dropkick_bomber.txt b/forge-gui/res/cardsfolder/d/dropkick_bomber.txt index 44e61e6a307..a88517da61b 100644 --- a/forge-gui/res/cardsfolder/d/dropkick_bomber.txt +++ b/forge-gui/res/cardsfolder/d/dropkick_bomber.txt @@ -1,11 +1,11 @@ -Name:Dropkick Bomber -ManaCost:2 R -Types:Creature Goblin Warrior -PT:2/3 -S:Mode$ Continuous | Affected$ Goblin.Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Goblins you control get +1/+1. -A:AB$ Animate | Cost$ R | ValidTgts$ Goblin.Other+YouCtrl | TgtPrompt$ Select another target Goblin you control | Keywords$ Flying | Triggers$ TrigDamageDealtOnce | SpellDescription$ Until end of turn, another target Goblin you control gains flying and "When this creature deals combat damage, sacrifice it." -SVar:TrigDamageDealtOnce:Mode$ DamageDealtOnce | CombatDamage$ True | ValidSource$ Card.Self | Execute$ TrigSac | TriggerDescription$ When this creature deals combat damage, sacrifice it. -SVar:TrigSac:DB$ Sacrifice -SVar:PlayMain1:TRUE -SVar:BuffedBy:Goblin -Oracle:Other Goblins you control get +1/+1.\n{R}: Until end of turn, another target Goblin you control gains flying and "When this creature deals combat damage, sacrifice it." +Name:Dropkick Bomber +ManaCost:2 R +Types:Creature Goblin Warrior +PT:2/3 +S:Mode$ Continuous | Affected$ Goblin.Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Goblins you control get +1/+1. +A:AB$ Animate | Cost$ R | ValidTgts$ Goblin.Other+YouCtrl | TgtPrompt$ Select another target Goblin you control | Keywords$ Flying | Triggers$ TrigDamageDealtOnce | SpellDescription$ Until end of turn, another target Goblin you control gains flying and "When this creature deals combat damage, sacrifice it." +SVar:TrigDamageDealtOnce:Mode$ DamageDealtOnce | CombatDamage$ True | ValidSource$ Card.Self | Execute$ TrigSac | TriggerDescription$ When this creature deals combat damage, sacrifice it. +SVar:TrigSac:DB$ Sacrifice +SVar:PlayMain1:TRUE +SVar:BuffedBy:Goblin +Oracle:Other Goblins you control get +1/+1.\n{R}: Until end of turn, another target Goblin you control gains flying and "When this creature deals combat damage, sacrifice it." diff --git a/forge-gui/res/cardsfolder/e/eager_trufflesnout.txt b/forge-gui/res/cardsfolder/e/eager_trufflesnout.txt index 24b6a0b0e9a..962e020e4e3 100644 --- a/forge-gui/res/cardsfolder/e/eager_trufflesnout.txt +++ b/forge-gui/res/cardsfolder/e/eager_trufflesnout.txt @@ -1,9 +1,9 @@ -Name:Eager Trufflesnout -ManaCost:2 G -Types:Creature Boar -PT:4/2 -K:Trample -T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever this creature deals combat damage to a player, create a Food token. (It's an artifact with "{2}, {T} Sacrifice this token: You gain 3 life.") -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_food_sac | TokenOwner$ You -DeckHas:Ability$Token +Name:Eager Trufflesnout +ManaCost:2 G +Types:Creature Boar +PT:4/2 +K:Trample +T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever this creature deals combat damage to a player, create a Food token. (It's an artifact with "{2}, {T} Sacrifice this token: You gain 3 life.") +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ c_a_food_sac | TokenOwner$ You +DeckHas:Ability$Token Oracle:Trample (This creature can deal excess combat damage to the player or planeswalker it's attacking.)\nWhenever this creature deals combat damage to a player, create a Food token. (It's an artifact with "{2}, {T} Sacrifice this token: You gain 3 life.") \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/e/eidolon_of_astral_winds.txt b/forge-gui/res/cardsfolder/e/eidolon_of_astral_winds.txt index fc5864fdd7f..d64840db576 100644 --- a/forge-gui/res/cardsfolder/e/eidolon_of_astral_winds.txt +++ b/forge-gui/res/cardsfolder/e/eidolon_of_astral_winds.txt @@ -1,9 +1,9 @@ -Name:Eidolon of Astral Winds -ManaCost:2 W -Types:Enchantment Creature Spirit -PT:2/4 -K:Vigilance -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self,Enchantment.YouCtrl | Execute$ TrigAnimate | TriggerDescription$ Constellation — Whenever CARDNAME or another enchantment you control enters, choose target creature you control. Until end of turn, that creature has base power and toughness 4/4 and gains flying. -SVar:TrigAnimate:DB$ Animate | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | Power$ 4 | Toughness$ 4 | Keywords$ Flying -DeckHints:Type$Enchantment +Name:Eidolon of Astral Winds +ManaCost:2 W +Types:Enchantment Creature Spirit +PT:2/4 +K:Vigilance +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self,Enchantment.YouCtrl | Execute$ TrigAnimate | TriggerDescription$ Constellation — Whenever CARDNAME or another enchantment you control enters, choose target creature you control. Until end of turn, that creature has base power and toughness 4/4 and gains flying. +SVar:TrigAnimate:DB$ Animate | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | Power$ 4 | Toughness$ 4 | Keywords$ Flying +DeckHints:Type$Enchantment Oracle:Vigilance\nConstellation — Whenever Eidolon of Astral Winds or another enchantment you control enters, choose target creature you control. Until end of turn, that creature has base power and toughness 4/4 and gains flying. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/e/electroduplicate.txt b/forge-gui/res/cardsfolder/e/electroduplicate.txt index 41e6ec9c963..e58a0b88c3e 100644 --- a/forge-gui/res/cardsfolder/e/electroduplicate.txt +++ b/forge-gui/res/cardsfolder/e/electroduplicate.txt @@ -1,6 +1,6 @@ -Name:Electroduplicate -ManaCost:2 R -Types:Sorcery -K:Flashback:2 R R -A:SP$ CopyPermanent | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | AddKeywords$ Haste | AtEOTTrig$ Sacrifice | SpellDescription$ Create a token that's a copy of target creature you control, except it has haste and "At the beginning of the end step, sacrifice this token." -Oracle:Create a token that's a copy of target creature you control, except it has haste and "At the beginning of the end step, sacrifice this token."\nFlashback {2}{R}{R} (You may cast this card from your graveyard for its flashback cost. Then exile it.) +Name:Electroduplicate +ManaCost:2 R +Types:Sorcery +K:Flashback:2 R R +A:SP$ CopyPermanent | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | AddKeywords$ Haste | AtEOTTrig$ Sacrifice | SpellDescription$ Create a token that's a copy of target creature you control, except it has haste and "At the beginning of the end step, sacrifice this token." +Oracle:Create a token that's a copy of target creature you control, except it has haste and "At the beginning of the end step, sacrifice this token."\nFlashback {2}{R}{R} (You may cast this card from your graveyard for its flashback cost. Then exile it.) diff --git a/forge-gui/res/cardsfolder/e/elementalist_adept.txt b/forge-gui/res/cardsfolder/e/elementalist_adept.txt index 7235b1dc1f6..4b0077d2823 100644 --- a/forge-gui/res/cardsfolder/e/elementalist_adept.txt +++ b/forge-gui/res/cardsfolder/e/elementalist_adept.txt @@ -1,7 +1,7 @@ -Name:Elementalist Adept -ManaCost:1 U -Types:Creature Human Wizard -PT:2/1 -K:Flash -K:Prowess +Name:Elementalist Adept +ManaCost:1 U +Types:Creature Human Wizard +PT:2/1 +K:Flash +K:Prowess Oracle:Flash (You may cast this spell any time you could cast an instant.)\nProwess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/e/elenda_saint_of_dusk.txt b/forge-gui/res/cardsfolder/e/elenda_saint_of_dusk.txt index 296f8417506..afb78901476 100644 --- a/forge-gui/res/cardsfolder/e/elenda_saint_of_dusk.txt +++ b/forge-gui/res/cardsfolder/e/elenda_saint_of_dusk.txt @@ -1,13 +1,13 @@ -Name:Elenda, Saint of Dusk -ManaCost:2 W B -Types:Legendary Creature Vampire Knight -PT:4/4 -K:Lifelink -K:Hexproof:Card.Instant:instants -S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Menace | CheckSVar$ X | SVarCompare$ GTY | Description$ As long as your life total is greater than your starting life total, NICKNAME gets +1/+1 and has menace. NICKNAME gets an additional +5/+5 as long as your life total is at least 10 greater than your starting life total. -S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 5 | AddToughness$ 5 | Secondary$ True | CheckSVar$ X | SVarCompare$ GEZ | Description$ As long as your life total is greater than your starting life total, NICKNAME gets +1/+1 and has menace. NICKNAME gets an additional +5/+5 as long as your life total is at least 10 greater than your starting life total. -SVar:X:Count$YourLifeTotal -SVar:Y:Count$YourStartingLife -SVar:Z:Count$YourStartingLife/Plus.10 -DeckHints:Ability$LifeGain +Name:Elenda, Saint of Dusk +ManaCost:2 W B +Types:Legendary Creature Vampire Knight +PT:4/4 +K:Lifelink +K:Hexproof:Card.Instant:instants +S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Menace | CheckSVar$ X | SVarCompare$ GTY | Description$ As long as your life total is greater than your starting life total, NICKNAME gets +1/+1 and has menace. NICKNAME gets an additional +5/+5 as long as your life total is at least 10 greater than your starting life total. +S:Mode$ Continuous | Affected$ Card.Self | AddPower$ 5 | AddToughness$ 5 | Secondary$ True | CheckSVar$ X | SVarCompare$ GEZ | Description$ As long as your life total is greater than your starting life total, NICKNAME gets +1/+1 and has menace. NICKNAME gets an additional +5/+5 as long as your life total is at least 10 greater than your starting life total. +SVar:X:Count$YourLifeTotal +SVar:Y:Count$YourStartingLife +SVar:Z:Count$YourStartingLife/Plus.10 +DeckHints:Ability$LifeGain Oracle:Lifelink, hexproof from instants\nAs long as your life total is greater than your starting life total, Elenda gets +1/+1 and has menace. Elenda gets an additional +5/+5 as long as your life total is at least 10 greater than your starting life total. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/e/elfsworn_giant.txt b/forge-gui/res/cardsfolder/e/elfsworn_giant.txt index 2a1d8a59ee3..b43a705c989 100644 --- a/forge-gui/res/cardsfolder/e/elfsworn_giant.txt +++ b/forge-gui/res/cardsfolder/e/elfsworn_giant.txt @@ -1,8 +1,8 @@ -Name:Elfsworn Giant -ManaCost:3 G G -Types:Creature Giant -PT:5/3 -K:Reach -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Landfall — Whenever a land you control enters, create a 1/1 green Elf Warrior creature token. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_elf_warrior | TokenOwner$ You +Name:Elfsworn Giant +ManaCost:3 G G +Types:Creature Giant +PT:5/3 +K:Reach +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Landfall — Whenever a land you control enters, create a 1/1 green Elf Warrior creature token. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_elf_warrior | TokenOwner$ You Oracle:Reach (This creature can block creatures with flying.)\nLandfall — Whenever a land you control enters, create a 1/1 green Elf Warrior creature token. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/e/elvish_regrower.txt b/forge-gui/res/cardsfolder/e/elvish_regrower.txt index 00aa0abcf65..2a0b5ac89de 100644 --- a/forge-gui/res/cardsfolder/e/elvish_regrower.txt +++ b/forge-gui/res/cardsfolder/e/elvish_regrower.txt @@ -1,7 +1,7 @@ -Name:Elvish Regrower -ManaCost:2 G G -Types:Creature Elf Druid -PT:4/3 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, return target permanent card from your graveyard to your hand. -SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Permanent.YouOwn | TgtPrompt$ Select target permanent card in your graveyard +Name:Elvish Regrower +ManaCost:2 G G +Types:Creature Elf Druid +PT:4/3 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, return target permanent card from your graveyard to your hand. +SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Permanent.YouOwn | TgtPrompt$ Select target permanent card in your graveyard Oracle:When this creature enters, return target permanent card from your graveyard to your hand. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/e/entity_tracker.txt b/forge-gui/res/cardsfolder/e/entity_tracker.txt index ef6dc96a943..ceb648b38c0 100644 --- a/forge-gui/res/cardsfolder/e/entity_tracker.txt +++ b/forge-gui/res/cardsfolder/e/entity_tracker.txt @@ -1,10 +1,10 @@ -Name:Entity Tracker -ManaCost:2 U -Types:Creature Human Scout -PT:2/3 -K:Flash -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Enchantment.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, draw a card. -T:Mode$ FullyUnlock | ValidCard$ Card.Room | ValidPlayer$ You | Secondary$ True | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, draw a card. -SVar:TrigDraw:DB$ Draw -DeckNeeds:Type$Enchantment -Oracle:Flash\nEerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, draw a card. +Name:Entity Tracker +ManaCost:2 U +Types:Creature Human Scout +PT:2/3 +K:Flash +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Enchantment.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, draw a card. +T:Mode$ FullyUnlock | ValidCard$ Card.Room | ValidPlayer$ You | Secondary$ True | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, draw a card. +SVar:TrigDraw:DB$ Draw +DeckNeeds:Type$Enchantment +Oracle:Flash\nEerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, draw a card. diff --git a/forge-gui/res/cardsfolder/e/erudite_wizard.txt b/forge-gui/res/cardsfolder/e/erudite_wizard.txt index 906a4b98c13..d7dc9287bf7 100644 --- a/forge-gui/res/cardsfolder/e/erudite_wizard.txt +++ b/forge-gui/res/cardsfolder/e/erudite_wizard.txt @@ -1,7 +1,7 @@ -Name:Erudite Wizard -ManaCost:2 U -Types:Creature Human Wizard -PT:2/3 -T:Mode$ Drawn | ValidCard$ Card.YouCtrl | Number$ 2 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you draw your second card each turn, put a +1/+1 counter on this creature. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +Name:Erudite Wizard +ManaCost:2 U +Types:Creature Human Wizard +PT:2/3 +T:Mode$ Drawn | ValidCard$ Card.YouCtrl | Number$ 2 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you draw your second card each turn, put a +1/+1 counter on this creature. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 Oracle:Whenever you draw your second card each turn, put a +1/+1 counter on this creature. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/e/evereth_viceroy_of_plunder.txt b/forge-gui/res/cardsfolder/e/evereth_viceroy_of_plunder.txt index 03875f84c08..45c98095824 100644 --- a/forge-gui/res/cardsfolder/e/evereth_viceroy_of_plunder.txt +++ b/forge-gui/res/cardsfolder/e/evereth_viceroy_of_plunder.txt @@ -1,13 +1,13 @@ -Name:Evereth, Viceroy of Plunder -ManaCost:2 B -Types:Legendary Creature Vampire Soldier -PT:2/2 -K:Flying -A:AB$ PutCounter | Cost$ Sac<1/Creature.Other;Artifact.Other/another creature or artifact> | CounterType$ P1P1 | CounterNum$ 1 | SorcerySpeed$ True | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on CARDNAME. If the sacrificed permanent was a Treasure, NICKNAME gains lifelink until end of turn. Activate only as a sorcery. -SVar:DBPump:DB$ Pump | Defined$ Self | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | KW$ Lifelink -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | OptionalDecider$ TriggeredCardController | Execute$ TrigImmediateTrig | TriggerDescription$ When NICKNAME dies, you may pay {1}{B/R}. When you do, NICKNAME deals damage equal to its power to each opponent. -SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ 1 BR | Execute$ TrigDealDamage | SpellDescription$ When you do, NICKNAME deals damage equal to its power to each opponent. -SVar:TrigDealDamage:DB$ DealDamage | Defined$ Opponent | NumDmg$ Y -SVar:X:Sacrificed$Valid Treasure -SVar:Y:Count$CardPower -Oracle:Flying\nSacrifice another creature or artifact: Put a +1/+1 counter on Evereth, Viceroy of Plunder. If the sacrificed permanent was a Treasure, Evereth gains lifelink until end of turn. Activate only as a sorcery.\nWhen Evereth dies, you may pay {1}{B/R}. When you do, Evereth deals damage equal to its power to each opponent. +Name:Evereth, Viceroy of Plunder +ManaCost:2 B +Types:Legendary Creature Vampire Soldier +PT:2/2 +K:Flying +A:AB$ PutCounter | Cost$ Sac<1/Creature.Other;Artifact.Other/another creature or artifact> | CounterType$ P1P1 | CounterNum$ 1 | SorcerySpeed$ True | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on CARDNAME. If the sacrificed permanent was a Treasure, NICKNAME gains lifelink until end of turn. Activate only as a sorcery. +SVar:DBPump:DB$ Pump | Defined$ Self | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | KW$ Lifelink +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | OptionalDecider$ TriggeredCardController | Execute$ TrigImmediateTrig | TriggerDescription$ When NICKNAME dies, you may pay {1}{B/R}. When you do, NICKNAME deals damage equal to its power to each opponent. +SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ 1 BR | Execute$ TrigDealDamage | SpellDescription$ When you do, NICKNAME deals damage equal to its power to each opponent. +SVar:TrigDealDamage:DB$ DealDamage | Defined$ Opponent | NumDmg$ Y +SVar:X:Sacrificed$Valid Treasure +SVar:Y:Count$CardPower +Oracle:Flying\nSacrifice another creature or artifact: Put a +1/+1 counter on Evereth, Viceroy of Plunder. If the sacrificed permanent was a Treasure, Evereth gains lifelink until end of turn. Activate only as a sorcery.\nWhen Evereth dies, you may pay {1}{B/R}. When you do, Evereth deals damage equal to its power to each opponent. diff --git a/forge-gui/res/cardsfolder/e/exemplar_of_light.txt b/forge-gui/res/cardsfolder/e/exemplar_of_light.txt index 38c40eab9fb..56e061069f3 100644 --- a/forge-gui/res/cardsfolder/e/exemplar_of_light.txt +++ b/forge-gui/res/cardsfolder/e/exemplar_of_light.txt @@ -1,12 +1,12 @@ -Name:Exemplar of Light -ManaCost:2 W W -Types:Creature Angel -PT:3/3 -K:Flying -T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you gain life, put a +1/+1 counter on this creature. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 -T:Mode$ CounterAddedOnce | CounterType$ P1P1 | ValidSource$ You | ValidCard$ Card.Self | TriggerZones$ Battlefield | ActivationLimit$ 1 | Execute$ TrigDraw | TriggerDescription$ Whenever you put one or more +1/+1 counters on this creature, draw a card. This ability triggers only once each turn. -SVar:TrigDraw:DB$ Draw -DeckHas:Ability$Counters -DeckHints:Ability$LifeGain +Name:Exemplar of Light +ManaCost:2 W W +Types:Creature Angel +PT:3/3 +K:Flying +T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you gain life, put a +1/+1 counter on this creature. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +T:Mode$ CounterAddedOnce | CounterType$ P1P1 | ValidSource$ You | ValidCard$ Card.Self | TriggerZones$ Battlefield | ActivationLimit$ 1 | Execute$ TrigDraw | TriggerDescription$ Whenever you put one or more +1/+1 counters on this creature, draw a card. This ability triggers only once each turn. +SVar:TrigDraw:DB$ Draw +DeckHas:Ability$Counters +DeckHints:Ability$LifeGain Oracle:Flying\nWhenever you gain life, put a +1/+1 counter on this creature.\nWhenever you put one or more +1/+1 counters on this creature, draw a card. This ability triggers only once each turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/e/experimental_lab_staff_room.txt b/forge-gui/res/cardsfolder/e/experimental_lab_staff_room.txt index 915f88ddfaa..b6b58a275d8 100644 --- a/forge-gui/res/cardsfolder/e/experimental_lab_staff_room.txt +++ b/forge-gui/res/cardsfolder/e/experimental_lab_staff_room.txt @@ -1,20 +1,20 @@ -Name:Experimental Lab -ManaCost:3 G -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigManifest | TriggerDescription$ When you unlock this door, manifest dread, then put two +1/+1 counters and a trample counter on that creature. -SVar:TrigManifest:DB$ ManifestDread | Amount$ 1 | RememberManifested$ True | SubAbility$ DBPutCounters -SVar:DBPutCounters:DB$ PutCounterAll | ValidCards$ Card.IsRemembered | CounterType$ P1P1 | CounterNum$ 2 | CounterType2$ Trample | CounterNum2$ 1 | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, manifest dread, then put two +1/+1 counters and a trample counter on that creature. - -ALTERNATE - -Name:Staff Room -ManaCost:2 G -Types:Enchantment Room -T:Mode$ DamageDone | ValidSource$ Creature.YouCtrl | ValidTarget$ Player | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigChoose | TriggerDescription$ Whenever a creature you control deals combat damage to a player, turn that creature face up or put a +1/+1 counter on it. -SVar:TrigChoose:DB$ GenericChoice | Choices$ DBPutCounter,DBTurnFaceUp -SVar:DBTurnFaceUp:DB$ SetState | Defined$ TriggeredSourceLKICopy | Mode$ TurnFaceUp | IsPresent$ Card.canBeTurnedFaceUp+faceDown | PresentDefined$ TriggeredSourceLKICopy | SpellDescription$ Turn it face up -SVar:DBPutCounter:DB$ PutCounter | Defined$ TriggeredSourceLKICopy | CounterType$ P1P1 | CounterNum$ 1 | IsPresent$ Card.canReceiveCounters P1P1 | PresentDefined$ TriggeredSourceLKICopy | SpellDescription$ Put a +1/+1 counter on it -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever a creature you control deals combat damage to a player, turn that creature face up or put a +1/+1 counter on it. +Name:Experimental Lab +ManaCost:3 G +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigManifest | TriggerDescription$ When you unlock this door, manifest dread, then put two +1/+1 counters and a trample counter on that creature. +SVar:TrigManifest:DB$ ManifestDread | Amount$ 1 | RememberManifested$ True | SubAbility$ DBPutCounters +SVar:DBPutCounters:DB$ PutCounterAll | ValidCards$ Card.IsRemembered | CounterType$ P1P1 | CounterNum$ 2 | CounterType2$ Trample | CounterNum2$ 1 | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, manifest dread, then put two +1/+1 counters and a trample counter on that creature. + +ALTERNATE + +Name:Staff Room +ManaCost:2 G +Types:Enchantment Room +T:Mode$ DamageDone | ValidSource$ Creature.YouCtrl | ValidTarget$ Player | CombatDamage$ True | TriggerZones$ Battlefield | Execute$ TrigChoose | TriggerDescription$ Whenever a creature you control deals combat damage to a player, turn that creature face up or put a +1/+1 counter on it. +SVar:TrigChoose:DB$ GenericChoice | Choices$ DBPutCounter,DBTurnFaceUp +SVar:DBTurnFaceUp:DB$ SetState | Defined$ TriggeredSourceLKICopy | Mode$ TurnFaceUp | IsPresent$ Card.canBeTurnedFaceUp+faceDown | PresentDefined$ TriggeredSourceLKICopy | SpellDescription$ Turn it face up +SVar:DBPutCounter:DB$ PutCounter | Defined$ TriggeredSourceLKICopy | CounterType$ P1P1 | CounterNum$ 1 | IsPresent$ Card.canReceiveCounters P1P1 | PresentDefined$ TriggeredSourceLKICopy | SpellDescription$ Put a +1/+1 counter on it +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever a creature you control deals combat damage to a player, turn that creature face up or put a +1/+1 counter on it. diff --git a/forge-gui/res/cardsfolder/f/faebloom_trick.txt b/forge-gui/res/cardsfolder/f/faebloom_trick.txt index 2e9cb934d6c..c946ca5fe36 100644 --- a/forge-gui/res/cardsfolder/f/faebloom_trick.txt +++ b/forge-gui/res/cardsfolder/f/faebloom_trick.txt @@ -1,8 +1,8 @@ -Name:Faebloom Trick -ManaCost:2 U -Types:Instant -A:SP$ Token | TokenAmount$ 2 | TokenScript$ u_1_1_faerie_flying | TokenOwner$ You | RememberOriginalTokens$ True | SubAbility$ DBImmediateTrig | SpellDescription$ Create two 1/1 blue Faerie creature tokens with flying. When you do, tap target creature an opponent controls. -SVar:DBImmediateTrig:DB$ ImmediateTrigger | TriggerAmount$ Remembered$Amount/DivideEvenlyDown.2 | Execute$ TrigTap | SubAbility$ DBCleanup | TriggerDescription$ When you do, tap target creature an opponent controls. -SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -Oracle:Create two 1/1 blue Faerie creature tokens with flying. When you do, tap target creature an opponent controls. +Name:Faebloom Trick +ManaCost:2 U +Types:Instant +A:SP$ Token | TokenAmount$ 2 | TokenScript$ u_1_1_faerie_flying | TokenOwner$ You | RememberOriginalTokens$ True | SubAbility$ DBImmediateTrig | SpellDescription$ Create two 1/1 blue Faerie creature tokens with flying. When you do, tap target creature an opponent controls. +SVar:DBImmediateTrig:DB$ ImmediateTrigger | TriggerAmount$ Remembered$Amount/DivideEvenlyDown.2 | Execute$ TrigTap | SubAbility$ DBCleanup | TriggerDescription$ When you do, tap target creature an opponent controls. +SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +Oracle:Create two 1/1 blue Faerie creature tokens with flying. When you do, tap target creature an opponent controls. diff --git a/forge-gui/res/cardsfolder/f/faithful_pikemaster.txt b/forge-gui/res/cardsfolder/f/faithful_pikemaster.txt index 6f921ed5150..e416438053b 100644 --- a/forge-gui/res/cardsfolder/f/faithful_pikemaster.txt +++ b/forge-gui/res/cardsfolder/f/faithful_pikemaster.txt @@ -1,8 +1,8 @@ -Name:Faithful Pikemaster -ManaCost:3 W -Types:Creature Rhino Monk Soldier -PT:3/4 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigScry | TriggerDescription$ When CARDNAME enters, scry 2. (Look at the top two cards of your library, then put any number of them on the bottom and the rest on top in any order.) -SVar:TrigScry:DB$ Scry | ScryNum$ 2 -S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ As long as it's your turn, CARDNAME has first strike. +Name:Faithful Pikemaster +ManaCost:3 W +Types:Creature Rhino Monk Soldier +PT:3/4 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigScry | TriggerDescription$ When CARDNAME enters, scry 2. (Look at the top two cards of your library, then put any number of them on the bottom and the rest on top in any order.) +SVar:TrigScry:DB$ Scry | ScryNum$ 2 +S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ As long as it's your turn, CARDNAME has first strike. Oracle:When Faithful Pikemaster enters, scry 2. (Look at the top two cards of your library, then put any number of them on the bottom and the rest on top in any order.)\nAs long as it's your turn, Faithful Pikemaster has first strike. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/f/fear_of_infinity.txt b/forge-gui/res/cardsfolder/f/fear_of_infinity.txt index 98a22cbe836..3a5a9701215 100644 --- a/forge-gui/res/cardsfolder/f/fear_of_infinity.txt +++ b/forge-gui/res/cardsfolder/f/fear_of_infinity.txt @@ -1,11 +1,11 @@ -Name:Fear of Infinity -ManaCost:1 U B -Types:Enchantment Creature Nightmare -PT:2/2 -K:Flying -K:Lifelink -K:CARDNAME can't block. -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Enchantment.YouCtrl | OptionalDecider$ You | TriggerZones$ Graveyard | Execute$ TrigChange | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, you may return CARDNAME from your graveyard to your hand. -T:Mode$ FullyUnlock | ValidCard$ Card.Room | ValidPlayer$ You | Secondary$ True | OptionalDecider$ You | TriggerZones$ Graveyard | Execute$ TrigChange | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, you may return CARDNAME from your graveyard to your hand. -SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand +Name:Fear of Infinity +ManaCost:1 U B +Types:Enchantment Creature Nightmare +PT:2/2 +K:Flying +K:Lifelink +K:CARDNAME can't block. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Enchantment.YouCtrl | OptionalDecider$ You | TriggerZones$ Graveyard | Execute$ TrigChange | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, you may return CARDNAME from your graveyard to your hand. +T:Mode$ FullyUnlock | ValidCard$ Card.Room | ValidPlayer$ You | Secondary$ True | OptionalDecider$ You | TriggerZones$ Graveyard | Execute$ TrigChange | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, you may return CARDNAME from your graveyard to your hand. +SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand Oracle:Flying, lifelink\nFear of Infinity can't block.\nEerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, you may return Fear of Infinity from your graveyard to your hand. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/f/felling_blow.txt b/forge-gui/res/cardsfolder/f/felling_blow.txt index de30876417c..142aa4991fb 100644 --- a/forge-gui/res/cardsfolder/f/felling_blow.txt +++ b/forge-gui/res/cardsfolder/f/felling_blow.txt @@ -1,8 +1,8 @@ -Name:Felling Blow -ManaCost:2 G -Types:Sorcery -A:SP$ PutCounter | ValidTgts$ Creature.YouCtrl | CounterType$ P1P1 | TgtPrompt$ Select target creature you control to put a +1/+1 counter | SubAbility$ DBDamage | AILogic$ PowerDmg | SpellDescription$ Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature an opponent controls. -SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target target creature an opponent controls | NumDmg$ X | DamageSource$ ParentTarget | AILogic$ PowerDmg -SVar:X:ParentTargeted$CardPower -DeckHas:Ability$Counters +Name:Felling Blow +ManaCost:2 G +Types:Sorcery +A:SP$ PutCounter | ValidTgts$ Creature.YouCtrl | CounterType$ P1P1 | TgtPrompt$ Select target creature you control to put a +1/+1 counter | SubAbility$ DBDamage | AILogic$ PowerDmg | SpellDescription$ Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature an opponent controls. +SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target target creature an opponent controls | NumDmg$ X | DamageSource$ ParentTarget | AILogic$ PowerDmg +SVar:X:ParentTargeted$CardPower +DeckHas:Ability$Counters Oracle:Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature an opponent controls. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/f/fiendish_panda.txt b/forge-gui/res/cardsfolder/f/fiendish_panda.txt index 7d0f5a00e1d..c81dbe2c812 100644 --- a/forge-gui/res/cardsfolder/f/fiendish_panda.txt +++ b/forge-gui/res/cardsfolder/f/fiendish_panda.txt @@ -1,12 +1,12 @@ -Name:Fiendish Panda -ManaCost:2 W B -Types:Creature Bear Demon -PT:3/2 -T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you gain life, put a +1/+1 counter on this creature. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When this creature dies, return another target non-Bear creature card with mana value less than or equal to this creature's power from your graveyard to the battlefield. -SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature.cmcLEX+YouOwn+nonBear+Other | TgtPrompt$ Select target non-Bear creature card with mana value less than or equal to this creature's power | Origin$ Graveyard | Destination$ Battlefield -SVar:X:TriggeredCard$CardPower -DeckHas:Ability$Counters|Graveyard -DeckHints:Ability$LifeGain +Name:Fiendish Panda +ManaCost:2 W B +Types:Creature Bear Demon +PT:3/2 +T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you gain life, put a +1/+1 counter on this creature. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When this creature dies, return another target non-Bear creature card with mana value less than or equal to this creature's power from your graveyard to the battlefield. +SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature.cmcLEX+YouOwn+nonBear+Other | TgtPrompt$ Select target non-Bear creature card with mana value less than or equal to this creature's power | Origin$ Graveyard | Destination$ Battlefield +SVar:X:TriggeredCard$CardPower +DeckHas:Ability$Counters|Graveyard +DeckHints:Ability$LifeGain Oracle:Whenever you gain life, put a +1/+1 counter on this creature.\nWhen this creature dies, return another target non-Bear creature card with mana value less than or equal to this creature's power from your graveyard to the battlefield. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/f/fiery_annihilation.txt b/forge-gui/res/cardsfolder/f/fiery_annihilation.txt index ddec1b15b7f..5b69d12eaf7 100644 --- a/forge-gui/res/cardsfolder/f/fiery_annihilation.txt +++ b/forge-gui/res/cardsfolder/f/fiery_annihilation.txt @@ -1,6 +1,6 @@ -Name:Fiery Annihilation -ManaCost:2 R -Types:Instant -A:SP$ DealDamage | NumDmg$ 5 | ValidTgts$ Creature | TgtPrompt$ Select target creature | ReplaceDyingDefined$ Targeted | SubAbility$ DBChangeZone | SpellDescription$ CARDNAME deals 5 damage to target creature. Exile up to one target Equipment attached to that creature. If that creature would die this turn, exile it instead. -SVar:DBChangeZone:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Equipment.AttachedTo ParentTarget | TargetMin$ 0 | TargetMax$ 1 -Oracle:Fiery Annihilation deals 5 damage to target creature. Exile up to one target Equipment attached to that creature. If that creature would die this turn, exile it instead. +Name:Fiery Annihilation +ManaCost:2 R +Types:Instant +A:SP$ DealDamage | NumDmg$ 5 | ValidTgts$ Creature | TgtPrompt$ Select target creature | ReplaceDyingDefined$ Targeted | SubAbility$ DBChangeZone | SpellDescription$ CARDNAME deals 5 damage to target creature. Exile up to one target Equipment attached to that creature. If that creature would die this turn, exile it instead. +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Equipment.AttachedTo ParentTarget | TargetMin$ 0 | TargetMax$ 1 +Oracle:Fiery Annihilation deals 5 damage to target creature. Exile up to one target Equipment attached to that creature. If that creature would die this turn, exile it instead. diff --git a/forge-gui/res/cardsfolder/f/firespitter_whelp.txt b/forge-gui/res/cardsfolder/f/firespitter_whelp.txt index d27858bcee0..f440883addc 100644 --- a/forge-gui/res/cardsfolder/f/firespitter_whelp.txt +++ b/forge-gui/res/cardsfolder/f/firespitter_whelp.txt @@ -1,9 +1,9 @@ -Name:Firespitter Whelp -ManaCost:2 R -Types:Creature Dragon -PT:2/2 -K:Flying -T:Mode$ SpellCast | ValidCard$ Card.nonCreature,Dragon | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDealDamage | TriggerDescription$ Whenever you cast a noncreature or Dragon spell, this creature deals 1 damage to each opponent. -SVar:TrigDealDamage:DB$ DealDamage | Defined$ Player.Opponent | NumDmg$ 1 -DeckHints:Type$Dragon +Name:Firespitter Whelp +ManaCost:2 R +Types:Creature Dragon +PT:2/2 +K:Flying +T:Mode$ SpellCast | ValidCard$ Card.nonCreature,Dragon | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDealDamage | TriggerDescription$ Whenever you cast a noncreature or Dragon spell, this creature deals 1 damage to each opponent. +SVar:TrigDealDamage:DB$ DealDamage | Defined$ Player.Opponent | NumDmg$ 1 +DeckHints:Type$Dragon Oracle:Flying\nWhenever you cast a noncreature or Dragon spell, this creature deals 1 damage to each opponent. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/f/fishing_pole.txt b/forge-gui/res/cardsfolder/f/fishing_pole.txt index 4187aed2881..edadeab5f5d 100644 --- a/forge-gui/res/cardsfolder/f/fishing_pole.txt +++ b/forge-gui/res/cardsfolder/f/fishing_pole.txt @@ -1,12 +1,12 @@ -Name:Fishing Pole -ManaCost:1 -Types:Artifact Equipment -S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddAbility$ FishingPoleBaiting | Description$ Equipped creature has "{1}, {T}, Tap CARDNAME: Put a bait counter on CARDNAME." -SVar:FishingPoleBaiting:AB$ PutCounter | Cost$ 1 T tapXType<1/OriginalHost/Fishing Pole> | CounterType$ BAIT | CounterNum$ 1 | Defined$ OriginalHost | SpellDescription$ Put a bait counter on ORIGINALHOST. -T:Mode$ Untaps | ValidCard$ Creature.EquippedBy | TriggerZones$ Battlefield | Execute$ TrigRemoveCounter | TriggerDescription$ Whenever equipped creature becomes untapped, remove a bait counter from this Equipment. If you do, create a 1/1 blue Fish creature token. -SVar:TrigRemoveCounter:DB$ RemoveCounter | Defined$ Self | CounterType$ BAIT | CounterNum$ 1 | RememberRemoved$ True | SubAbility$ DBToken -SVar:DBToken:DB$ Token | TokenAmount$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | TokenScript$ u_1_1_fish | TokenOwner$ You | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -SVar:X:Count$RememberedSize -K:Equip:2 -Oracle:Equipped creature has "{1}, {T}, Tap Fishing Pole: Put a bait counter on Fishing Pole."\nWhenever equipped creature becomes untapped, remove a bait counter from this Equipment. If you do, create a 1/1 blue Fish creature token.\nEquip {2} ({2}: Attach to target creature you control. Equip only as a sorcery.) +Name:Fishing Pole +ManaCost:1 +Types:Artifact Equipment +S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddAbility$ FishingPoleBaiting | Description$ Equipped creature has "{1}, {T}, Tap CARDNAME: Put a bait counter on CARDNAME." +SVar:FishingPoleBaiting:AB$ PutCounter | Cost$ 1 T tapXType<1/OriginalHost/Fishing Pole> | CounterType$ BAIT | CounterNum$ 1 | Defined$ OriginalHost | SpellDescription$ Put a bait counter on ORIGINALHOST. +T:Mode$ Untaps | ValidCard$ Creature.EquippedBy | TriggerZones$ Battlefield | Execute$ TrigRemoveCounter | TriggerDescription$ Whenever equipped creature becomes untapped, remove a bait counter from this Equipment. If you do, create a 1/1 blue Fish creature token. +SVar:TrigRemoveCounter:DB$ RemoveCounter | Defined$ Self | CounterType$ BAIT | CounterNum$ 1 | RememberRemoved$ True | SubAbility$ DBToken +SVar:DBToken:DB$ Token | TokenAmount$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | TokenScript$ u_1_1_fish | TokenOwner$ You | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Count$RememberedSize +K:Equip:2 +Oracle:Equipped creature has "{1}, {T}, Tap Fishing Pole: Put a bait counter on Fishing Pole."\nWhenever equipped creature becomes untapped, remove a bait counter from this Equipment. If you do, create a 1/1 blue Fish creature token.\nEquip {2} ({2}: Attach to target creature you control. Equip only as a sorcery.) diff --git a/forge-gui/res/cardsfolder/f/fleeting_flight.txt b/forge-gui/res/cardsfolder/f/fleeting_flight.txt index 28ed2d15f67..d60518ef8c9 100644 --- a/forge-gui/res/cardsfolder/f/fleeting_flight.txt +++ b/forge-gui/res/cardsfolder/f/fleeting_flight.txt @@ -1,7 +1,7 @@ -Name:Fleeting Flight -ManaCost:W -Types:Instant -A:SP$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on target creature. It gains flying until end of turn. Prevent all combat damage that would be dealt to it this turn. -SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Flying & Prevent all combat damage that would be dealt to CARDNAME. -DeckHas:Ability$Counters -Oracle:Put a +1/+1 counter on target creature. It gains flying until end of turn. Prevent all combat damage that would be dealt to it this turn. +Name:Fleeting Flight +ManaCost:W +Types:Instant +A:SP$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on target creature. It gains flying until end of turn. Prevent all combat damage that would be dealt to it this turn. +SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Flying & Prevent all combat damage that would be dealt to CARDNAME. +DeckHas:Ability$Counters +Oracle:Put a +1/+1 counter on target creature. It gains flying until end of turn. Prevent all combat damage that would be dealt to it this turn. diff --git a/forge-gui/res/cardsfolder/f/frontline_heroism.txt b/forge-gui/res/cardsfolder/f/frontline_heroism.txt index f455c0f9274..9dd89c1eec9 100644 --- a/forge-gui/res/cardsfolder/f/frontline_heroism.txt +++ b/forge-gui/res/cardsfolder/f/frontline_heroism.txt @@ -1,10 +1,10 @@ -Name:Frontline Heroism -ManaCost:2 R -Types:Enchantment -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken1 | TriggerDescription$ When CARDNAME enters, create a 1/1 red Soldier creature token with haste. -SVar:TrigToken1:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_soldier_haste | TokenOwner$ You -T:Mode$ SpellCast | ValidCard$ Card | Execute$ TrigToken2 | ValidActivatingPlayer$ You | IsSingleTarget$ True | TriggerZones$ Battlefield | TargetsValid$ Creature.YouCtrl+inZoneBattlefield | TriggerDescription$ Whenever you cast a spell that targets only a single creature you control, create a 1/1 red Soldier creature token with haste, then copy that spell. The copy targets that token. -SVar:TrigToken2:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_soldier_haste | TokenOwner$ You | RememberTokens$ True | SubAbility$ DBCopy -SVar:DBCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Controller$ You | CopyForEachCanTarget$ Permanent.IsRemembered | ChooseOnlyOne$ True | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -Oracle:When Frontline Heroism enters, create a 1/1 red Soldier creature token with haste.\nWhenever you cast a spell that targets only a single creature you control, create a 1/1 red Soldier creature token with haste, then copy that spell. The copy targets that token. +Name:Frontline Heroism +ManaCost:2 R +Types:Enchantment +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken1 | TriggerDescription$ When CARDNAME enters, create a 1/1 red Soldier creature token with haste. +SVar:TrigToken1:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_soldier_haste | TokenOwner$ You +T:Mode$ SpellCast | ValidCard$ Card | Execute$ TrigToken2 | ValidActivatingPlayer$ You | IsSingleTarget$ True | TriggerZones$ Battlefield | TargetsValid$ Creature.YouCtrl+inZoneBattlefield | TriggerDescription$ Whenever you cast a spell that targets only a single creature you control, create a 1/1 red Soldier creature token with haste, then copy that spell. The copy targets that token. +SVar:TrigToken2:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_soldier_haste | TokenOwner$ You | RememberTokens$ True | SubAbility$ DBCopy +SVar:DBCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | Controller$ You | CopyForEachCanTarget$ Permanent.IsRemembered | ChooseOnlyOne$ True | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +Oracle:When Frontline Heroism enters, create a 1/1 red Soldier creature token with haste.\nWhenever you cast a spell that targets only a single creature you control, create a 1/1 red Soldier creature token with haste, then copy that spell. The copy targets that token. diff --git a/forge-gui/res/cardsfolder/f/fumulus_the_infestation.txt b/forge-gui/res/cardsfolder/f/fumulus_the_infestation.txt index 85094f7a937..11a191e0e86 100644 --- a/forge-gui/res/cardsfolder/f/fumulus_the_infestation.txt +++ b/forge-gui/res/cardsfolder/f/fumulus_the_infestation.txt @@ -1,14 +1,14 @@ -Name:Fumulus, the Infestation -ManaCost:3 B -Types:Legendary Creature Vampire Insect -PT:2/2 -K:Flying -K:Deathtouch -T:Mode$ Sacrificed | ValidCard$ Creature.nonToken | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever a player sacrifices a nontoken creature, create a 1/1 black Insect creature token with flying. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ b_1_1_insect_flying | TokenOwner$ You -T:Mode$ Attacks | ValidCard$ Insect.YouCtrl,Leech.YouCtrl,Slug.YouCtrl,Worm.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever an Insect, Leech, Slug, or Worm you control attacks, defending player loses 1 life and you gain 1 life. -SVar:TrigLoseLife:DB$ LoseLife | Defined$ TriggeredDefendingPlayer | LifeAmount$ 1 | SubAbility$ DBGainLife -SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1 -SVar:PlayMain1:TRUE -DeckHints:Type$Insect|Leech|Slug|Worm +Name:Fumulus, the Infestation +ManaCost:3 B +Types:Legendary Creature Vampire Insect +PT:2/2 +K:Flying +K:Deathtouch +T:Mode$ Sacrificed | ValidCard$ Creature.nonToken | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever a player sacrifices a nontoken creature, create a 1/1 black Insect creature token with flying. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ b_1_1_insect_flying | TokenOwner$ You +T:Mode$ Attacks | ValidCard$ Insect.YouCtrl,Leech.YouCtrl,Slug.YouCtrl,Worm.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever an Insect, Leech, Slug, or Worm you control attacks, defending player loses 1 life and you gain 1 life. +SVar:TrigLoseLife:DB$ LoseLife | Defined$ TriggeredDefendingPlayer | LifeAmount$ 1 | SubAbility$ DBGainLife +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1 +SVar:PlayMain1:TRUE +DeckHints:Type$Insect|Leech|Slug|Worm Oracle:Flying, deathtouch\nWhenever a player sacrifices a nontoken creature, create a 1/1 black Insect creature token with flying.\nWhenever an Insect, Leech, Slug, or Worm you control attacks, defending player loses 1 life and you gain 1 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/f/funeral_room_awakening_hall.txt b/forge-gui/res/cardsfolder/f/funeral_room_awakening_hall.txt index 8bb0e95bde3..b2034caf590 100644 --- a/forge-gui/res/cardsfolder/f/funeral_room_awakening_hall.txt +++ b/forge-gui/res/cardsfolder/f/funeral_room_awakening_hall.txt @@ -1,17 +1,17 @@ -Name:Funeral Room -ManaCost:2 B -Types:Enchantment Room -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever a creature you control dies, each opponent loses 1 life and you gain 1 life. -SVar:TrigLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 1 | SubAbility$ DBGainLife -SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1 -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever a creature you control dies, each opponent loses 1 life and you gain 1 life. - -ALTERNATE - -Name:Awakening Hall -ManaCost:6 B B -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigChangeZone | TriggerDescription$ When you unlock this door, return all creature cards from your graveyard to the battlefield. -SVar:TrigChangeZone:DB$ ChangeZoneAll | ChangeType$ Creature.YouOwn | Origin$ Graveyard | Destination$ Battlefield +Name:Funeral Room +ManaCost:2 B +Types:Enchantment Room +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever a creature you control dies, each opponent loses 1 life and you gain 1 life. +SVar:TrigLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 1 | SubAbility$ DBGainLife +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1 +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever a creature you control dies, each opponent loses 1 life and you gain 1 life. + +ALTERNATE + +Name:Awakening Hall +ManaCost:6 B B +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigChangeZone | TriggerDescription$ When you unlock this door, return all creature cards from your graveyard to the battlefield. +SVar:TrigChangeZone:DB$ ChangeZoneAll | ChangeType$ Creature.YouOwn | Origin$ Graveyard | Destination$ Battlefield Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, return all creature cards from your graveyard to the battlefield. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/general_kreat_the_boltbringer.txt b/forge-gui/res/cardsfolder/g/general_kreat_the_boltbringer.txt index 1783d699a18..114f0eedb42 100644 --- a/forge-gui/res/cardsfolder/g/general_kreat_the_boltbringer.txt +++ b/forge-gui/res/cardsfolder/g/general_kreat_the_boltbringer.txt @@ -1,10 +1,10 @@ -Name:General Kreat, the Boltbringer -ManaCost:2 R -Types:Legendary Creature Goblin Soldier -PT:2/2 -T:Mode$ AttackersDeclared | ValidAttackers$ Goblin.YouCtrl | Execute$ TrigToken | TriggerZones$ Battlefield | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more Goblins you control attack, create a 1/1 red Goblin creature token that's tapped and attacking. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_goblin | TokenOwner$ You | TokenTapped$ True | TokenAttacking$ True -T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigDealDamage | TriggerDescription$ Whenever another creature you control enters, CARDNAME deals 1 damage to each opponent. -SVar:TrigDealDamage:DB$ DealDamage | Defined$ Player.Opponent | NumDmg$ 1 -DeckHints:Type$Goblin +Name:General Kreat, the Boltbringer +ManaCost:2 R +Types:Legendary Creature Goblin Soldier +PT:2/2 +T:Mode$ AttackersDeclared | ValidAttackers$ Goblin.YouCtrl | Execute$ TrigToken | TriggerZones$ Battlefield | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more Goblins you control attack, create a 1/1 red Goblin creature token that's tapped and attacking. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_goblin | TokenOwner$ You | TokenTapped$ True | TokenAttacking$ True +T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigDealDamage | TriggerDescription$ Whenever another creature you control enters, CARDNAME deals 1 damage to each opponent. +SVar:TrigDealDamage:DB$ DealDamage | Defined$ Player.Opponent | NumDmg$ 1 +DeckHints:Type$Goblin Oracle:Whenever one or more Goblins you control attack, create a 1/1 red Goblin creature token that's tapped and attacking.\nWhenever another creature you control enters, General Kreat, the Boltbringer deals 1 damage to each opponent. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/generous_pup.txt b/forge-gui/res/cardsfolder/g/generous_pup.txt index 70f88880d38..a7ad24fdd10 100644 --- a/forge-gui/res/cardsfolder/g/generous_pup.txt +++ b/forge-gui/res/cardsfolder/g/generous_pup.txt @@ -1,9 +1,9 @@ -Name:Generous Pup -ManaCost:1 W -Types:Creature Dog -PT:2/2 -K:Vigilance -T:Mode$ CounterAddedOnce | ValidCard$ Card.Self | TriggerZones$ Battlefield | CounterType$ P1P1 | Execute$ TrigPutCounterAll | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more +1/+1 counters are put on CARDNAME, put a +1/+1 counter on each other creature you control. This ability triggers only once each turn. -SVar:TrigPutCounterAll:DB$ PutCounterAll | ValidCards$ Creature.YouCtrl+StrictlyOther | CounterType$ P1P1 | CounterNum$ 1 -DeckHas:Ability$Counters -Oracle:Vigilance\nWhenever one or more +1/+1 counters are put on Generous Pup, put a +1/+1 counter on each other creature you control. This ability triggers only once each turn. +Name:Generous Pup +ManaCost:1 W +Types:Creature Dog +PT:2/2 +K:Vigilance +T:Mode$ CounterAddedOnce | ValidCard$ Card.Self | TriggerZones$ Battlefield | CounterType$ P1P1 | Execute$ TrigPutCounterAll | ActivationLimit$ 1 | TriggerDescription$ Whenever one or more +1/+1 counters are put on CARDNAME, put a +1/+1 counter on each other creature you control. This ability triggers only once each turn. +SVar:TrigPutCounterAll:DB$ PutCounterAll | ValidCards$ Creature.YouCtrl+StrictlyOther | CounterType$ P1P1 | CounterNum$ 1 +DeckHas:Ability$Counters +Oracle:Vigilance\nWhenever one or more +1/+1 counters are put on Generous Pup, put a +1/+1 counter on each other creature you control. This ability triggers only once each turn. diff --git a/forge-gui/res/cardsfolder/g/get_out.txt b/forge-gui/res/cardsfolder/g/get_out.txt index 658b3bcc90e..696f99bc074 100644 --- a/forge-gui/res/cardsfolder/g/get_out.txt +++ b/forge-gui/res/cardsfolder/g/get_out.txt @@ -1,7 +1,7 @@ -Name:Get Out -ManaCost:U U -Types:Instant -A:SP$ Charm | Choices$ DBCounter,DBChangeZone -SVar:DBCounter:DB$ Counter | TargetType$ Spell | ValidTgts$ Creature,Enchantment | TgtPrompt$ Counter target creature or enchantment spell | SpellDescription$ Counter target creature or enchantment spell. -SVar:DBChangeZone:DB$ ChangeZone | ValidTgts$ Creature.YouOwn,Enchantment.YouOwn | TgtPrompt$ Select target creature or enchantment you own | TargetMin$ 1 | TargetMax$ 2 | TgtZone$ Battlefield | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return one or two target creatures and/or enchantments you own to your hand. -Oracle:Choose one —\n• Counter target creature or enchantment spell.\n• Return one or two target creatures and/or enchantments you own to your hand. +Name:Get Out +ManaCost:U U +Types:Instant +A:SP$ Charm | Choices$ DBCounter,DBChangeZone +SVar:DBCounter:DB$ Counter | TargetType$ Spell | ValidTgts$ Creature,Enchantment | TgtPrompt$ Counter target creature or enchantment spell | SpellDescription$ Counter target creature or enchantment spell. +SVar:DBChangeZone:DB$ ChangeZone | ValidTgts$ Creature.YouOwn,Enchantment.YouOwn | TgtPrompt$ Select target creature or enchantment you own | TargetMin$ 1 | TargetMax$ 2 | TgtZone$ Battlefield | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return one or two target creatures and/or enchantments you own to your hand. +Oracle:Choose one —\n• Counter target creature or enchantment spell.\n• Return one or two target creatures and/or enchantments you own to your hand. diff --git a/forge-gui/res/cardsfolder/g/gilded_scuttler.txt b/forge-gui/res/cardsfolder/g/gilded_scuttler.txt index d7ad30139ae..a1c0759a354 100644 --- a/forge-gui/res/cardsfolder/g/gilded_scuttler.txt +++ b/forge-gui/res/cardsfolder/g/gilded_scuttler.txt @@ -1,9 +1,9 @@ -Name:Gilded Scuttler -ManaCost:2 U -Types:Artifact Creature Crab -PT:1/3 -S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked. -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigTap | TriggerDescription$ When CARDNAME enters, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.) -SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | SubAbility$ DBCounter -SVar:DBCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ Stun | CounterNum$ 1 +Name:Gilded Scuttler +ManaCost:2 U +Types:Artifact Creature Crab +PT:1/3 +S:Mode$ CantBlockBy | ValidAttacker$ Creature.Self | Description$ CARDNAME can't be blocked. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigTap | TriggerDescription$ When CARDNAME enters, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.) +SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | SubAbility$ DBCounter +SVar:DBCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ Stun | CounterNum$ 1 Oracle:Gilded Scuttler can't be blocked.\nWhen Gilded Scuttler enters, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/go_forth.txt b/forge-gui/res/cardsfolder/g/go_forth.txt index 53841b3e2c9..031e959f0fd 100644 --- a/forge-gui/res/cardsfolder/g/go_forth.txt +++ b/forge-gui/res/cardsfolder/g/go_forth.txt @@ -1,7 +1,7 @@ -Name:Go Forth -ManaCost:G -Types:Instant -A:SP$ Charm | Choices$ DBSearch,DBPump | CharmNum$ 1 -SVar:DBSearch:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic land card, reveal it, put it into your hand, then shuffle. -SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | NumDef$ +2 | SpellDescription$ Target creature gets +2/+2 until end of turn. +Name:Go Forth +ManaCost:G +Types:Instant +A:SP$ Charm | Choices$ DBSearch,DBPump | CharmNum$ 1 +SVar:DBSearch:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic land card, reveal it, put it into your hand, then shuffle. +SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | NumDef$ +2 | SpellDescription$ Target creature gets +2/+2 until end of turn. Oracle:Choose one —\n• Search your library for a basic land card, reveal it, put it into your hand, then shuffle.\n• Target creature gets +2/+2 until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/goblin_boarders.txt b/forge-gui/res/cardsfolder/g/goblin_boarders.txt index fbe2b708573..ac3db32a7f4 100644 --- a/forge-gui/res/cardsfolder/g/goblin_boarders.txt +++ b/forge-gui/res/cardsfolder/g/goblin_boarders.txt @@ -1,7 +1,7 @@ -Name:Goblin Boarders -ManaCost:2 R -Types:Creature Goblin Pirate -PT:3/2 -K:etbCounter:P1P1:1:CheckSVar$ RaidTest:Raid — CARDNAME enters with a +1/+1 counter on it if you attacked this turn. -SVar:RaidTest:Count$AttackersDeclared +Name:Goblin Boarders +ManaCost:2 R +Types:Creature Goblin Pirate +PT:3/2 +K:etbCounter:P1P1:1:CheckSVar$ RaidTest:Raid — CARDNAME enters with a +1/+1 counter on it if you attacked this turn. +SVar:RaidTest:Count$AttackersDeclared Oracle:Raid — This creature enters with a +1/+1 counter on it if you attacked this turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/goblin_negotiation.txt b/forge-gui/res/cardsfolder/g/goblin_negotiation.txt index 5e8fe854280..15842a85acf 100644 --- a/forge-gui/res/cardsfolder/g/goblin_negotiation.txt +++ b/forge-gui/res/cardsfolder/g/goblin_negotiation.txt @@ -1,8 +1,8 @@ -Name:Goblin Negotiation -ManaCost:X R R -Types:Sorcery -A:SP$ DealDamage | ValidTgts$ Creature | NumDmg$ X | ExcessSVar$ Excess | ExcessSVarCondition$ Card.targetedBy | SubAbility$ DBToken | SpellDescription$ CARDNAME deals X damage to target creature. Create a number of 1/1 red Goblin creature tokens equal to the amount of excess damage dealt to that creature this way. -SVar:DBToken:DB$ Token | TokenScript$ r_1_1_goblin | TokenAmount$ Excess -SVar:X:Count$xPaid -DeckHas:Ability$Token -Oracle:Goblin Negotiation deals X damage to target creature. Create a number of 1/1 red Goblin creature tokens equal to the amount of excess damage dealt to that creature this way. +Name:Goblin Negotiation +ManaCost:X R R +Types:Sorcery +A:SP$ DealDamage | ValidTgts$ Creature | NumDmg$ X | ExcessSVar$ Excess | ExcessSVarCondition$ Card.targetedBy | SubAbility$ DBToken | SpellDescription$ CARDNAME deals X damage to target creature. Create a number of 1/1 red Goblin creature tokens equal to the amount of excess damage dealt to that creature this way. +SVar:DBToken:DB$ Token | TokenScript$ r_1_1_goblin | TokenAmount$ Excess +SVar:X:Count$xPaid +DeckHas:Ability$Token +Oracle:Goblin Negotiation deals X damage to target creature. Create a number of 1/1 red Goblin creature tokens equal to the amount of excess damage dealt to that creature this way. diff --git a/forge-gui/res/cardsfolder/g/goblin_surprise.txt b/forge-gui/res/cardsfolder/g/goblin_surprise.txt index 967e96f6beb..d0d6524c6cf 100644 --- a/forge-gui/res/cardsfolder/g/goblin_surprise.txt +++ b/forge-gui/res/cardsfolder/g/goblin_surprise.txt @@ -1,7 +1,7 @@ -Name:Goblin Surprise -ManaCost:2 R -Types:Instant -A:SP$ Charm | Choices$ DBPumpAll,DBToken | CharmNum$ 1 -SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +2 | SpellDescription$ Creatures you control get +2/+0 until end of turn. -SVar:DBToken:DB$ Token | TokenAmount$ 2 | TokenScript$ r_1_1_goblin | SpellDescription$ Create two 1/1 red Goblin creature tokens. +Name:Goblin Surprise +ManaCost:2 R +Types:Instant +A:SP$ Charm | Choices$ DBPumpAll,DBToken | CharmNum$ 1 +SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +2 | SpellDescription$ Creatures you control get +2/+0 until end of turn. +SVar:DBToken:DB$ Token | TokenAmount$ 2 | TokenScript$ r_1_1_goblin | SpellDescription$ Create two 1/1 red Goblin creature tokens. Oracle:Choose one —\n• Creatures you control get +2/+0 until end of turn.\n• Create two 1/1 red Goblin creature tokens. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/gorehorn_raider.txt b/forge-gui/res/cardsfolder/g/gorehorn_raider.txt index fd681b74807..ad324eb1175 100644 --- a/forge-gui/res/cardsfolder/g/gorehorn_raider.txt +++ b/forge-gui/res/cardsfolder/g/gorehorn_raider.txt @@ -1,8 +1,8 @@ -Name:Gorehorn Raider -ManaCost:4 R -Types:Creature Minotaur Pirate -PT:4/4 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | CheckSVar$ RaidTest | Execute$ TrigDealDamage | TriggerDescription$ Raid — When this creature enters, if you attacked this turn, this creature deals 2 damage to any target. -SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Any | NumDmg$ 2 -SVar:RaidTest:Count$AttackersDeclared +Name:Gorehorn Raider +ManaCost:4 R +Types:Creature Minotaur Pirate +PT:4/4 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | CheckSVar$ RaidTest | Execute$ TrigDealDamage | TriggerDescription$ Raid — When this creature enters, if you attacked this turn, this creature deals 2 damage to any target. +SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Any | NumDmg$ 2 +SVar:RaidTest:Count$AttackersDeclared Oracle:Raid — When this creature enters, if you attacked this turn, this creature deals 2 damage to any target. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/gornog_the_red_reaper.txt b/forge-gui/res/cardsfolder/g/gornog_the_red_reaper.txt index d1c58504ff6..6c57dde3f7b 100644 --- a/forge-gui/res/cardsfolder/g/gornog_the_red_reaper.txt +++ b/forge-gui/res/cardsfolder/g/gornog_the_red_reaper.txt @@ -1,12 +1,12 @@ -Name:Gornog, the Red Reaper -ManaCost:2 R -Types:Legendary Creature Minotaur Warrior -PT:2/3 -K:Haste -S:Mode$ CantBlockBy | ValidAttacker$ Creature.Warrior | ValidBlocker$ Creature.Coward | Description$ Cowards can't block Warriors. -T:Mode$ AttackersDeclaredOneTarget | AttackedTarget$ Player | ValidAttackers$ Creature.Warrior+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigAnimate | TriggerDescription$ Whenever one or more Warriors you control attack a player, target creature that player controls becomes a Coward. -SVar:TrigAnimate:DB$ Animate | ValidTgts$ Creature | TargetsWithDefinedController$ TriggeredAttackedTarget | TgtPrompt$ Select target creature that player controls | Types$ Coward | RemoveCreatureTypes$ True | Duration$ Permanent -S:Mode$ Continuous | Affected$ Creature.Warrior+attacking+YouCtrl | AddPower$ X | Description$ Attacking Warriors you control get +X/+0, where X is the number of Cowards your opponents control. -SVar:X:Count$Valid Coward.OppCtrl -SVar:PlayMain1:TRUE -Oracle:Haste\nCowards can't block Warriors.\nWhenever one or more Warriors you control attack a player, target creature that player controls becomes a Coward.\nAttacking Warriors you control get +X/+0, where X is the number of Cowards your opponents control. +Name:Gornog, the Red Reaper +ManaCost:2 R +Types:Legendary Creature Minotaur Warrior +PT:2/3 +K:Haste +S:Mode$ CantBlockBy | ValidAttacker$ Creature.Warrior | ValidBlocker$ Creature.Coward | Description$ Cowards can't block Warriors. +T:Mode$ AttackersDeclaredOneTarget | AttackedTarget$ Player | ValidAttackers$ Creature.Warrior+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigAnimate | TriggerDescription$ Whenever one or more Warriors you control attack a player, target creature that player controls becomes a Coward. +SVar:TrigAnimate:DB$ Animate | ValidTgts$ Creature | TargetsWithDefinedController$ TriggeredAttackedTarget | TgtPrompt$ Select target creature that player controls | Types$ Coward | RemoveCreatureTypes$ True | Duration$ Permanent +S:Mode$ Continuous | Affected$ Creature.Warrior+attacking+YouCtrl | AddPower$ X | Description$ Attacking Warriors you control get +X/+0, where X is the number of Cowards your opponents control. +SVar:X:Count$Valid Coward.OppCtrl +SVar:PlayMain1:TRUE +Oracle:Haste\nCowards can't block Warriors.\nWhenever one or more Warriors you control attack a player, target creature that player controls becomes a Coward.\nAttacking Warriors you control get +X/+0, where X is the number of Cowards your opponents control. diff --git a/forge-gui/res/cardsfolder/g/grand_entryway_elegant_rotunda.txt b/forge-gui/res/cardsfolder/g/grand_entryway_elegant_rotunda.txt index 9b071dcf5c6..b7a99593d8e 100644 --- a/forge-gui/res/cardsfolder/g/grand_entryway_elegant_rotunda.txt +++ b/forge-gui/res/cardsfolder/g/grand_entryway_elegant_rotunda.txt @@ -1,16 +1,16 @@ -Name:Grand Entryway -ManaCost:1 W -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigToken | TriggerDescription$ When you unlock this door, create a 1/1 white Glimmer enchantment creature token. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_e_glimmer | TokenOwner$ You -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, create a 1/1 white Glimmer enchantment creature token. - -ALTERNATE - -Name:Elegant Rotunda -ManaCost:2 W -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigPutCounter | TriggerDescription$ When you unlock this door, put a +1/+1 counter on each of up to two target creatures. -SVar:TrigPutCounter:DB$ PutCounter | CounterNum$ 1 | CounterType$ P1P1 | TargetMin$ 0 | TargetMax$ 2 | ValidTgts$ Creature | TgtPrompt$ Select up to 2 target creatures +Name:Grand Entryway +ManaCost:1 W +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigToken | TriggerDescription$ When you unlock this door, create a 1/1 white Glimmer enchantment creature token. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_e_glimmer | TokenOwner$ You +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, create a 1/1 white Glimmer enchantment creature token. + +ALTERNATE + +Name:Elegant Rotunda +ManaCost:2 W +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigPutCounter | TriggerDescription$ When you unlock this door, put a +1/+1 counter on each of up to two target creatures. +SVar:TrigPutCounter:DB$ PutCounter | CounterNum$ 1 | CounterType$ P1P1 | TargetMin$ 0 | TargetMax$ 2 | ValidTgts$ Creature | TgtPrompt$ Select up to 2 target creatures Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, put a +1/+1 counter on each of up to two target creatures. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/grappling_kraken.txt b/forge-gui/res/cardsfolder/g/grappling_kraken.txt index 0eeb8426e01..cbeb40e5d2d 100644 --- a/forge-gui/res/cardsfolder/g/grappling_kraken.txt +++ b/forge-gui/res/cardsfolder/g/grappling_kraken.txt @@ -1,10 +1,10 @@ -Name:Grappling Kraken -ManaCost:4 U U -Types:Creature Kraken -PT:5/6 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigTap | TriggerDescription$ Landfall — Whenever a land you control enters, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.) -SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | SubAbility$ DBCounter -SVar:DBCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ Stun | CounterNum$ 1 -DeckHas:Ability$Counters -SVar:BuffedBy:Land +Name:Grappling Kraken +ManaCost:4 U U +Types:Creature Kraken +PT:5/6 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigTap | TriggerDescription$ Landfall — Whenever a land you control enters, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.) +SVar:TrigTap:DB$ Tap | ValidTgts$ Creature.OppCtrl | SubAbility$ DBCounter +SVar:DBCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ Stun | CounterNum$ 1 +DeckHas:Ability$Counters +SVar:BuffedBy:Land Oracle:Landfall — Whenever a land you control enters, tap target creature an opponent controls and put a stun counter on it. (If a permanent with a stun counter would become untapped, remove one from it instead.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/greenhouse_rickety_gazebo.txt b/forge-gui/res/cardsfolder/g/greenhouse_rickety_gazebo.txt index 0a7537cdff5..e3e76bb6452 100644 --- a/forge-gui/res/cardsfolder/g/greenhouse_rickety_gazebo.txt +++ b/forge-gui/res/cardsfolder/g/greenhouse_rickety_gazebo.txt @@ -1,18 +1,18 @@ -Name:Greenhouse -ManaCost:2 G -Types:Enchantment Room -S:Mode$ Continuous | Affected$ Land.YouCtrl | AddAbility$ AnyMana | Description$ Lands you control have "{T}: Add one mana of any color." -SVar:AnyMana:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color. -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nLands you control have "{T}: Add one mana of any color." - -ALTERNATE - -Name:Rickety Gazebo -ManaCost:3 G -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigMill | TriggerDescription$ When you unlock this door, mill four cards, then return up to two permanent cards from among them to your hand. -SVar:TrigMill:DB$ Mill | NumCards$ 4 | Defined$ You | RememberMilled$ True | SubAbility$ DBChangeZone -SVar:DBChangeZone:DB$ ChangeZone | Hidden$ True | Origin$ Graveyard,Exile | Destination$ Hand | ChangeType$ Permanent.IsRemembered | ChangeNum$ 2 | SelectPrompt$ You may select a permanent card milled this way | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +Name:Greenhouse +ManaCost:2 G +Types:Enchantment Room +S:Mode$ Continuous | Affected$ Land.YouCtrl | AddAbility$ AnyMana | Description$ Lands you control have "{T}: Add one mana of any color." +SVar:AnyMana:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color. +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nLands you control have "{T}: Add one mana of any color." + +ALTERNATE + +Name:Rickety Gazebo +ManaCost:3 G +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigMill | TriggerDescription$ When you unlock this door, mill four cards, then return up to two permanent cards from among them to your hand. +SVar:TrigMill:DB$ Mill | NumCards$ 4 | Defined$ You | RememberMilled$ True | SubAbility$ DBChangeZone +SVar:DBChangeZone:DB$ ChangeZone | Hidden$ True | Origin$ Graveyard,Exile | Destination$ Hand | ChangeType$ Permanent.IsRemembered | ChangeNum$ 2 | SelectPrompt$ You may select a permanent card milled this way | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, mill four cards, then return up to two permanent cards from among them to your hand. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/guarded_heir.txt b/forge-gui/res/cardsfolder/g/guarded_heir.txt index cb4e489059c..b834bdc37a0 100644 --- a/forge-gui/res/cardsfolder/g/guarded_heir.txt +++ b/forge-gui/res/cardsfolder/g/guarded_heir.txt @@ -1,8 +1,8 @@ -Name:Guarded Heir -ManaCost:5 W -Types:Creature Human Noble -PT:1/1 -K:Lifelink -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When this creature enters, create two 3/3 white Knight creature tokens. -SVar:TrigToken:DB$ Token | TokenAmount$ 2 | TokenScript$ w_3_3_knight | TokenOwner$ You +Name:Guarded Heir +ManaCost:5 W +Types:Creature Human Noble +PT:1/1 +K:Lifelink +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When this creature enters, create two 3/3 white Knight creature tokens. +SVar:TrigToken:DB$ Token | TokenAmount$ 2 | TokenScript$ w_3_3_knight | TokenOwner$ You Oracle:Lifelink (Damage dealt by this creature also causes you to gain that much life.)\nWhen this creature enters, create two 3/3 white Knight creature tokens. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/g/gutless_plunderer.txt b/forge-gui/res/cardsfolder/g/gutless_plunderer.txt index 4e3afaf3856..0fdc6a20044 100644 --- a/forge-gui/res/cardsfolder/g/gutless_plunderer.txt +++ b/forge-gui/res/cardsfolder/g/gutless_plunderer.txt @@ -1,10 +1,10 @@ -Name:Gutless Plunderer -ManaCost:2 B -Types:Creature Skeleton Pirate -PT:2/2 -K:Deathtouch -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | CheckSVar$ RaidTest | Execute$ TrigDig | TriggerDescription$ Raid — When this creature enters, if you attacked this turn, look at the top three cards of your library. You may put one of those cards back on top of your library. Put the rest into your graveyard. -SVar:TrigDig:DB$ Dig | DigNum$ 3 | ChangeNum$ 1 | ChangeValid$ Card | DestinationZone2$ Graveyard | DestinationZone$ Library | LibraryPosition$ 0 | Optional$ True -DeckHas:Ability$Graveyard -SVar:RaidTest:Count$AttackersDeclared -Oracle:Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.)\nRaid — When this creature enters, if you attacked this turn, look at the top three cards of your library. You may put one of those cards back on top of your library. Put the rest into your graveyard. +Name:Gutless Plunderer +ManaCost:2 B +Types:Creature Skeleton Pirate +PT:2/2 +K:Deathtouch +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | CheckSVar$ RaidTest | Execute$ TrigDig | TriggerDescription$ Raid — When this creature enters, if you attacked this turn, look at the top three cards of your library. You may put one of those cards back on top of your library. Put the rest into your graveyard. +SVar:TrigDig:DB$ Dig | DigNum$ 3 | ChangeNum$ 1 | ChangeValid$ Card | DestinationZone2$ Graveyard | DestinationZone$ Library | LibraryPosition$ 0 | Optional$ True +DeckHas:Ability$Graveyard +SVar:RaidTest:Count$AttackersDeclared +Oracle:Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.)\nRaid — When this creature enters, if you attacked this turn, look at the top three cards of your library. You may put one of those cards back on top of your library. Put the rest into your graveyard. diff --git a/forge-gui/res/cardsfolder/h/hare_apparent.txt b/forge-gui/res/cardsfolder/h/hare_apparent.txt index db787d6c2a6..6894137853b 100644 --- a/forge-gui/res/cardsfolder/h/hare_apparent.txt +++ b/forge-gui/res/cardsfolder/h/hare_apparent.txt @@ -1,11 +1,11 @@ -Name:Hare Apparent -ManaCost:1 W -Types:Creature Rabbit Noble -PT:2/2 -K:A deck can have any number of cards named CARDNAME. -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When this creature enters, create a number of 1/1 white Rabbit creature tokens equal to the number of other creatures you control named Hare Apparent. -SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ w_1_1_rabbit | TokenOwner$ You -SVar:X:Count$Valid Creature.namedHare Apparent+YouCtrl+Other -SVar:PlayMain1:TRUE -DeckNeeds:Name$Hare Apparent +Name:Hare Apparent +ManaCost:1 W +Types:Creature Rabbit Noble +PT:2/2 +K:A deck can have any number of cards named CARDNAME. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When this creature enters, create a number of 1/1 white Rabbit creature tokens equal to the number of other creatures you control named Hare Apparent. +SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ w_1_1_rabbit | TokenOwner$ You +SVar:X:Count$Valid Creature.namedHare Apparent+YouCtrl+Other +SVar:PlayMain1:TRUE +DeckNeeds:Name$Hare Apparent Oracle:When this creature enters, create a number of 1/1 white Rabbit creature tokens equal to the number of other creatures you control named Hare Apparent.\nA deck can have any number of cards named Hare Apparent. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/h/hearts_on_fire.txt b/forge-gui/res/cardsfolder/h/hearts_on_fire.txt index 7db7b2c963b..e9c6c008177 100644 --- a/forge-gui/res/cardsfolder/h/hearts_on_fire.txt +++ b/forge-gui/res/cardsfolder/h/hearts_on_fire.txt @@ -1,5 +1,5 @@ -Name:Hearts on Fire -ManaCost:1 R -Types:Instant -A:SP$ Pump | TargetMin$ 1 | TargetMax$ 2 | NumAtt$ +2 | NumDef$ +1 | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ One or two target creatures each get +2/+1 until end of turn. +Name:Hearts on Fire +ManaCost:1 R +Types:Instant +A:SP$ Pump | TargetMin$ 1 | TargetMax$ 2 | NumAtt$ +2 | NumDef$ +1 | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ One or two target creatures each get +2/+1 until end of turn. Oracle:One or two target creatures each get +2/+1 until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/h/high_fae_trickster.txt b/forge-gui/res/cardsfolder/h/high_fae_trickster.txt index 2a25d913acc..7e2775c41e1 100644 --- a/forge-gui/res/cardsfolder/h/high_fae_trickster.txt +++ b/forge-gui/res/cardsfolder/h/high_fae_trickster.txt @@ -1,8 +1,8 @@ -Name:High Fae Trickster -ManaCost:3 U -Types:Creature Faerie Wizard -PT:4/2 -K:Flash -K:Flying -S:Mode$ CastWithFlash | ValidCard$ Card | ValidSA$ Spell | Caster$ You | Description$ You may cast spells as though they had flash. +Name:High Fae Trickster +ManaCost:3 U +Types:Creature Faerie Wizard +PT:4/2 +K:Flash +K:Flying +S:Mode$ CastWithFlash | ValidCard$ Card | ValidSA$ Spell | Caster$ You | Description$ You may cast spells as though they had flash. Oracle:Flash (You may cast this spell any time you could cast an instant.)\nFlying\nYou may cast spells as though they had flash. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/h/high_society_hunter.txt b/forge-gui/res/cardsfolder/h/high_society_hunter.txt index c5d150dd8e1..853b109d406 100644 --- a/forge-gui/res/cardsfolder/h/high_society_hunter.txt +++ b/forge-gui/res/cardsfolder/h/high_society_hunter.txt @@ -1,12 +1,12 @@ -Name:High-Society Hunter -ManaCost:3 B B -Types:Creature Vampire Noble -PT:5/3 -K:Flying -T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever this creature attacks, you may sacrifice another creature. If you do, put a +1/+1 counter on this creature. -SVar:TrigPutCounter:AB$ PutCounter | Cost$ Sac<1/Creature.Other/another creature> | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 -T:Mode$ ChangesZone | ValidCard$ Creature.nonToken+Other | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever another nontoken creature dies, draw a card. -SVar:TrigDraw:DB$ Draw -SVar:HasAttackEffect:TRUE -DeckHas:Ability$Counters|Sacrifice +Name:High-Society Hunter +ManaCost:3 B B +Types:Creature Vampire Noble +PT:5/3 +K:Flying +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever this creature attacks, you may sacrifice another creature. If you do, put a +1/+1 counter on this creature. +SVar:TrigPutCounter:AB$ PutCounter | Cost$ Sac<1/Creature.Other/another creature> | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +T:Mode$ ChangesZone | ValidCard$ Creature.nonToken+Other | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever another nontoken creature dies, draw a card. +SVar:TrigDraw:DB$ Draw +SVar:HasAttackEffect:TRUE +DeckHas:Ability$Counters|Sacrifice Oracle:Flying\nWhenever this creature attacks, you may sacrifice another creature. If you do, put a +1/+1 counter on this creature.\nWhenever another nontoken creature dies, draw a card. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/h/hinterland_sanctifier.txt b/forge-gui/res/cardsfolder/h/hinterland_sanctifier.txt index d87d656ae61..19b9b24d510 100644 --- a/forge-gui/res/cardsfolder/h/hinterland_sanctifier.txt +++ b/forge-gui/res/cardsfolder/h/hinterland_sanctifier.txt @@ -1,9 +1,9 @@ -Name:Hinterland Sanctifier -ManaCost:W -Types:Creature Rabbit Cleric -PT:1/2 -T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever another creature you control enters, you gain 1 life. -SVar:TrigGainLife:DB$ GainLife | LifeAmount$ 1 -SVar:BuffedBy:Creature -DeckHas:Ability$LifeGain +Name:Hinterland Sanctifier +ManaCost:W +Types:Creature Rabbit Cleric +PT:1/2 +T:Mode$ ChangesZone | ValidCard$ Creature.Other+YouCtrl | Destination$ Battlefield | TriggerZones$ Battlefield | Execute$ TrigGainLife | TriggerDescription$ Whenever another creature you control enters, you gain 1 life. +SVar:TrigGainLife:DB$ GainLife | LifeAmount$ 1 +SVar:BuffedBy:Creature +DeckHas:Ability$LifeGain Oracle:Whenever another creature you control enters, you gain 1 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/h/homunculus_horde.txt b/forge-gui/res/cardsfolder/h/homunculus_horde.txt index 050563cc4f0..5568e8594b9 100644 --- a/forge-gui/res/cardsfolder/h/homunculus_horde.txt +++ b/forge-gui/res/cardsfolder/h/homunculus_horde.txt @@ -1,8 +1,8 @@ -Name:Homunculus Horde -ManaCost:3 U -Types:Creature Homunculus -PT:2/2 -T:Mode$ Drawn | ValidCard$ Card.YouCtrl | Number$ 2 | TriggerZones$ Battlefield | Execute$ TrigCopy | TriggerDescription$ Whenever you draw your second card each turn, create a token that's a copy of this creature. -SVar:TrigCopy:DB$ CopyPermanent | Defined$ Self | NumCopies$ 1 -DeckHas:Ability$Token -Oracle:Whenever you draw your second card each turn, create a token that's a copy of this creature. +Name:Homunculus Horde +ManaCost:3 U +Types:Creature Homunculus +PT:2/2 +T:Mode$ Drawn | ValidCard$ Card.YouCtrl | Number$ 2 | TriggerZones$ Battlefield | Execute$ TrigCopy | TriggerDescription$ Whenever you draw your second card each turn, create a token that's a copy of this creature. +SVar:TrigCopy:DB$ CopyPermanent | Defined$ Self | NumCopies$ 1 +DeckHas:Ability$Token +Oracle:Whenever you draw your second card each turn, create a token that's a copy of this creature. diff --git a/forge-gui/res/cardsfolder/h/hungry_ghoul.txt b/forge-gui/res/cardsfolder/h/hungry_ghoul.txt index 9478da6d059..7ba4b932ce0 100644 --- a/forge-gui/res/cardsfolder/h/hungry_ghoul.txt +++ b/forge-gui/res/cardsfolder/h/hungry_ghoul.txt @@ -1,6 +1,6 @@ -Name:Hungry Ghoul -ManaCost:1 B -Types:Creature Zombie -PT:2/2 -A:AB$ PutCounter | Cost$ 1 Sac<1/Creature.Other/another creature> | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on this creature. -Oracle:{1}, Sacrifice another creature: Put a +1/+1 counter on this creature. +Name:Hungry Ghoul +ManaCost:1 B +Types:Creature Zombie +PT:2/2 +A:AB$ PutCounter | Cost$ 1 Sac<1/Creature.Other/another creature> | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on this creature. +Oracle:{1}, Sacrifice another creature: Put a +1/+1 counter on this creature. diff --git a/forge-gui/res/cardsfolder/h/hungry_megasloth.txt b/forge-gui/res/cardsfolder/h/hungry_megasloth.txt index 7243d4df99b..9c7731d9801 100644 --- a/forge-gui/res/cardsfolder/h/hungry_megasloth.txt +++ b/forge-gui/res/cardsfolder/h/hungry_megasloth.txt @@ -1,7 +1,7 @@ -Name:Hungry Megasloth -ManaCost:2 G -Types:Creature Sloth Beast -PT:3/3 -K:Reach -A:AB$ PutCounter | Cost$ 2 T | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on CARDNAME. +Name:Hungry Megasloth +ManaCost:2 G +Types:Creature Sloth Beast +PT:3/3 +K:Reach +A:AB$ PutCounter | Cost$ 2 T | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on CARDNAME. Oracle:Reach (This creature can block creatures with flying.)\n{2}, {T}: Put a +1/+1 counter on Hungry Megasloth. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/h/hurska_sweet_tooth.txt b/forge-gui/res/cardsfolder/h/hurska_sweet_tooth.txt index a9dfe015692..ff2db828e15 100644 --- a/forge-gui/res/cardsfolder/h/hurska_sweet_tooth.txt +++ b/forge-gui/res/cardsfolder/h/hurska_sweet_tooth.txt @@ -1,11 +1,11 @@ -Name:Hurska Sweet-Tooth -ManaCost:2 G -Types:Legendary Creature Bear -PT:3/3 -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME attacks, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this artifact: You gain 3 life.") -SVar:TrigToken:DB$ Token | TokenScript$ c_a_food_sac -T:Mode$ LifeGained | ValidPlayer$ You | Execute$ TrigImmediateTrig | TriggerZones$ Battlefield | TriggerDescription$ Whenever you gain life, you may pay {G/W}. When you do, target creature gets +X/+X until end of turn, where X is the amount of life you gained. -SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ GW | Execute$ TrigPump | TriggerDescription$ When you do, target creature gets +X/+X until end of turn, where X is the amount of life you gained. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature | NumAtt$ Spawner>TriggerCount$LifeAmount | NumDef$ Spawner>TriggerCount$LifeAmount -DeckHas:Ability$Sacrifice|Token & Type$Food|Artifact -Oracle:Whenever Hurska Sweet-Tooth attacks, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this artifact: You gain 3 life.")\nWhenever you gain life, you may pay {G/W}. When you do, target creature gets +X/+X until end of turn, where X is the amount of life you gained. +Name:Hurska Sweet-Tooth +ManaCost:2 G +Types:Legendary Creature Bear +PT:3/3 +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME attacks, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this artifact: You gain 3 life.") +SVar:TrigToken:DB$ Token | TokenScript$ c_a_food_sac +T:Mode$ LifeGained | ValidPlayer$ You | Execute$ TrigImmediateTrig | TriggerZones$ Battlefield | TriggerDescription$ Whenever you gain life, you may pay {G/W}. When you do, target creature gets +X/+X until end of turn, where X is the amount of life you gained. +SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ GW | Execute$ TrigPump | TriggerDescription$ When you do, target creature gets +X/+X until end of turn, where X is the amount of life you gained. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature | NumAtt$ Spawner>TriggerCount$LifeAmount | NumDef$ Spawner>TriggerCount$LifeAmount +DeckHas:Ability$Sacrifice|Token & Type$Food|Artifact +Oracle:Whenever Hurska Sweet-Tooth attacks, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this artifact: You gain 3 life.")\nWhenever you gain life, you may pay {G/W}. When you do, target creature gets +X/+X until end of turn, where X is the amount of life you gained. diff --git a/forge-gui/res/cardsfolder/i/icewind_elemental.txt b/forge-gui/res/cardsfolder/i/icewind_elemental.txt index 72a10647fe2..f6fee77d252 100644 --- a/forge-gui/res/cardsfolder/i/icewind_elemental.txt +++ b/forge-gui/res/cardsfolder/i/icewind_elemental.txt @@ -1,9 +1,9 @@ -Name:Icewind Elemental -ManaCost:4 U -Types:Creature Elemental -PT:3/4 -K:Flying -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When this creature enters, draw a card, then discard a card. -SVar:TrigDraw:DB$ Draw | SubAbility$ DBDiscard -SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose +Name:Icewind Elemental +ManaCost:4 U +Types:Creature Elemental +PT:3/4 +K:Flying +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When this creature enters, draw a card, then discard a card. +SVar:TrigDraw:DB$ Draw | SubAbility$ DBDiscard +SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose Oracle:Flying\nWhen this creature enters, draw a card, then discard a card. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/i/incinerating_blast.txt b/forge-gui/res/cardsfolder/i/incinerating_blast.txt index e58afa22def..71b386b8726 100644 --- a/forge-gui/res/cardsfolder/i/incinerating_blast.txt +++ b/forge-gui/res/cardsfolder/i/incinerating_blast.txt @@ -1,6 +1,6 @@ -Name:Incinerating Blast -ManaCost:4 R -Types:Sorcery -A:SP$ DealDamage | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 6 | SubAbility$ DBDraw | SpellDescription$ CARDNAME deals 6 damage to target creature. You may discard a card. If you do, draw a card. -SVar:DBDraw:DB$ Draw | UnlessCost$ Discard<1/Card> | UnlessSwitched$ True | UnlessPayer$ You -Oracle:Incinerating Blast deals 6 damage to target creature.\nYou may discard a card. If you do, draw a card. +Name:Incinerating Blast +ManaCost:4 R +Types:Sorcery +A:SP$ DealDamage | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 6 | SubAbility$ DBDraw | SpellDescription$ CARDNAME deals 6 damage to target creature. You may discard a card. If you do, draw a card. +SVar:DBDraw:DB$ Draw | UnlessCost$ Discard<1/Card> | UnlessSwitched$ True | UnlessPayer$ You +Oracle:Incinerating Blast deals 6 damage to target creature.\nYou may discard a card. If you do, draw a card. diff --git a/forge-gui/res/cardsfolder/i/infernal_vessel.txt b/forge-gui/res/cardsfolder/i/infernal_vessel.txt index 064ca619fab..5884baf24f4 100644 --- a/forge-gui/res/cardsfolder/i/infernal_vessel.txt +++ b/forge-gui/res/cardsfolder/i/infernal_vessel.txt @@ -1,8 +1,8 @@ -Name:Infernal Vessel -ManaCost:2 B -Types:Creature Human Cleric -PT:2/1 -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+nonDemon | Execute$ TrigReturn | TriggerDescription$ When this creature dies, if it wasn't a Demon, return it to the battlefield under its owner's control with two +1/+1 counters on it. It's a Demon in addition to its other types. -SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredNewCardLKICopy | WithCountersType$ P1P1 | WithCountersAmount$ 2 | AnimateSubAbility$ DBAnimate -SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Demon | Duration$ Permanent +Name:Infernal Vessel +ManaCost:2 B +Types:Creature Human Cleric +PT:2/1 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self+nonDemon | Execute$ TrigReturn | TriggerDescription$ When this creature dies, if it wasn't a Demon, return it to the battlefield under its owner's control with two +1/+1 counters on it. It's a Demon in addition to its other types. +SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredNewCardLKICopy | WithCountersType$ P1P1 | WithCountersAmount$ 2 | AnimateSubAbility$ DBAnimate +SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ Demon | Duration$ Permanent Oracle:When this creature dies, if it wasn't a Demon, return it to the battlefield under its owner's control with two +1/+1 counters on it. It's a Demon in addition to its other types. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/i/infestation_sage.txt b/forge-gui/res/cardsfolder/i/infestation_sage.txt index b253f1212d6..2287a6c5459 100644 --- a/forge-gui/res/cardsfolder/i/infestation_sage.txt +++ b/forge-gui/res/cardsfolder/i/infestation_sage.txt @@ -1,8 +1,8 @@ -Name:Infestation Sage -ManaCost:B -Types:Creature Elf Warlock -PT:1/1 -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When this creature dies, create a 1/1 black and green Insect token with flying. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ bg_1_1_insect_flying | TokenOwner$ You -DeckHas:Ability$Token +Name:Infestation Sage +ManaCost:B +Types:Creature Elf Warlock +PT:1/1 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When this creature dies, create a 1/1 black and green Insect token with flying. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ bg_1_1_insect_flying | TokenOwner$ You +DeckHas:Ability$Token Oracle:When this creature dies, create a 1/1 black and green Insect token with flying. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/i/inspiration_from_beyond.txt b/forge-gui/res/cardsfolder/i/inspiration_from_beyond.txt index 9c78a83e484..e2f4b88ea8b 100644 --- a/forge-gui/res/cardsfolder/i/inspiration_from_beyond.txt +++ b/forge-gui/res/cardsfolder/i/inspiration_from_beyond.txt @@ -1,8 +1,8 @@ -Name:Inspiration from Beyond -ManaCost:2 U -Types:Sorcery -A:SP$ Mill | NumCards$ 3 | Defined$ You | SubAbility$ DBChangeZone | SpellDescription$ Mill three cards, then return an instant or sorcery card from your graveyard to your hand. -SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | Mandatory$ True | ChangeType$ Instant.YouOwn,Sorcery.YouOwn | ChangeNum$ 1 | Hidden$ True -K:Flashback:5 U U -DeckHas:Ability$Graveyard +Name:Inspiration from Beyond +ManaCost:2 U +Types:Sorcery +A:SP$ Mill | NumCards$ 3 | Defined$ You | SubAbility$ DBChangeZone | SpellDescription$ Mill three cards, then return an instant or sorcery card from your graveyard to your hand. +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | Mandatory$ True | ChangeType$ Instant.YouOwn,Sorcery.YouOwn | ChangeNum$ 1 | Hidden$ True +K:Flashback:5 U U +DeckHas:Ability$Graveyard Oracle:Mill three cards, then return an instant or sorcery card from your graveyard to your hand.\nFlashback {5}{U}{U} (You may cast this card from your graveyard for its flashback cost. Then exile it.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/i/inspiring_paladin.txt b/forge-gui/res/cardsfolder/i/inspiring_paladin.txt index cfd15d8286d..ff41bb78108 100644 --- a/forge-gui/res/cardsfolder/i/inspiring_paladin.txt +++ b/forge-gui/res/cardsfolder/i/inspiring_paladin.txt @@ -1,8 +1,8 @@ -Name:Inspiring Paladin -ManaCost:2 W -Types:Creature Human Knight -PT:3/3 -S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ During your turn, this creature has first strike. (It deals combat damage before creatures without first strike.) -S:Mode$ Continuous | Affected$ Creature.YouCtrl+counters_GE1_P1P1 | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ During your turn, creatures you control with +1/+1 counters on them have first strike. -DeckHints:Ability$Counters +Name:Inspiring Paladin +ManaCost:2 W +Types:Creature Human Knight +PT:3/3 +S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ During your turn, this creature has first strike. (It deals combat damage before creatures without first strike.) +S:Mode$ Continuous | Affected$ Creature.YouCtrl+counters_GE1_P1P1 | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ During your turn, creatures you control with +1/+1 counters on them have first strike. +DeckHints:Ability$Counters Oracle:During your turn, this creature has first strike. (It deals combat damage before creatures without first strike.)\nDuring your turn, creatures you control with +1/+1 counters on them have first strike. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/i/intruding_soulrager.txt b/forge-gui/res/cardsfolder/i/intruding_soulrager.txt index d93545a1275..20be5a7c12a 100644 --- a/forge-gui/res/cardsfolder/i/intruding_soulrager.txt +++ b/forge-gui/res/cardsfolder/i/intruding_soulrager.txt @@ -1,9 +1,9 @@ -Name:Intruding Soulrager -ManaCost:U R -Types:Creature Spirit -PT:2/2 -K:Vigilance -A:AB$ DealDamage | Cost$ T Sac<1/Room> | NumDmg$ 2 | Defined$ Opponent | SubAbility$ DBDraw | SpellDescription$ CARDNAME deals 2 damage to each opponent. Draw a card. -SVar:DBDraw:DB$ Draw -DeckNeeds:Type$Room -Oracle:Vigilance\n{T}, Sacrifice a Room: Intruding Soulrager deals 2 damage to each opponent. Draw a card. +Name:Intruding Soulrager +ManaCost:U R +Types:Creature Spirit +PT:2/2 +K:Vigilance +A:AB$ DealDamage | Cost$ T Sac<1/Room> | NumDmg$ 2 | Defined$ Opponent | SubAbility$ DBDraw | SpellDescription$ CARDNAME deals 2 damage to each opponent. Draw a card. +SVar:DBDraw:DB$ Draw +DeckNeeds:Type$Room +Oracle:Vigilance\n{T}, Sacrifice a Room: Intruding Soulrager deals 2 damage to each opponent. Draw a card. diff --git a/forge-gui/res/cardsfolder/i/ivora_insatiable_heir.txt b/forge-gui/res/cardsfolder/i/ivora_insatiable_heir.txt index b19eb9b5a82..86e201f16c3 100644 --- a/forge-gui/res/cardsfolder/i/ivora_insatiable_heir.txt +++ b/forge-gui/res/cardsfolder/i/ivora_insatiable_heir.txt @@ -1,12 +1,12 @@ -Name:Ivora, Insatiable Heir -ManaCost:1 R -Types:Legendary Creature Vampire Warrior -PT:1/1 -K:Trample -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters and whenever it deals combat damage to a player, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, sacrifice this artifact: Draw a card.") -T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | Execute$ TrigToken | CombatDamage$ True | Secondary$ True | TriggerDescription$ When CARDNAME enters and whenever it deals combat damage to a player, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, sacrifice this artifact: Draw a card.") -SVar:TrigToken:DB$ Token | TokenScript$ c_a_blood_draw -T:Mode$ Discarded | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you discard a card, put a +1/+1 counter on NICKNAME. -DeckHas:Ability$Counters -SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +Name:Ivora, Insatiable Heir +ManaCost:1 R +Types:Legendary Creature Vampire Warrior +PT:1/1 +K:Trample +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters and whenever it deals combat damage to a player, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, sacrifice this artifact: Draw a card.") +T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | Execute$ TrigToken | CombatDamage$ True | Secondary$ True | TriggerDescription$ When CARDNAME enters and whenever it deals combat damage to a player, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, sacrifice this artifact: Draw a card.") +SVar:TrigToken:DB$ Token | TokenScript$ c_a_blood_draw +T:Mode$ Discarded | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you discard a card, put a +1/+1 counter on NICKNAME. +DeckHas:Ability$Counters +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 Oracle:Trample\nWhen Ivora, Insatiable Heir enters and whenever it deals combat damage to a player, create a Blood token. (It's an artifact with "{1}, {T}, Discard a card, sacrifice this artifact: Draw a card.")\nWhenever you discard a card, put a +1/+1 counter on Ivora. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/j/joust_through.txt b/forge-gui/res/cardsfolder/j/joust_through.txt index e1e198dc8ae..7da1b754f93 100644 --- a/forge-gui/res/cardsfolder/j/joust_through.txt +++ b/forge-gui/res/cardsfolder/j/joust_through.txt @@ -1,6 +1,6 @@ -Name:Joust Through -ManaCost:W -Types:Instant -A:SP$ DealDamage | ValidTgts$ Creature.attacking,Creature.blocking | TgtPrompt$ Select target attacking or blocking creature | NumDmg$ 3 | SubAbility$ DBGainLife | SpellDescription$ CARDNAME deals 3 damage to target attacking or blocking creature. You gain 1 life. -SVar:DBGainLife:DB$ GainLife | LifeAmount$ 1 +Name:Joust Through +ManaCost:W +Types:Instant +A:SP$ DealDamage | ValidTgts$ Creature.attacking,Creature.blocking | TgtPrompt$ Select target attacking or blocking creature | NumDmg$ 3 | SubAbility$ DBGainLife | SpellDescription$ CARDNAME deals 3 damage to target attacking or blocking creature. You gain 1 life. +SVar:DBGainLife:DB$ GainLife | LifeAmount$ 1 Oracle:Joust Through deals 3 damage to target attacking or blocking creature. You gain 1 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/k/kellan_planar_trailblazer.txt b/forge-gui/res/cardsfolder/k/kellan_planar_trailblazer.txt index f267a8f6648..22905755312 100644 --- a/forge-gui/res/cardsfolder/k/kellan_planar_trailblazer.txt +++ b/forge-gui/res/cardsfolder/k/kellan_planar_trailblazer.txt @@ -1,12 +1,12 @@ -Name:Kellan, Planar Trailblazer -ManaCost:R -Types:Legendary Creature Human Faerie Scout -PT:2/1 -A:AB$ Animate | Cost$ 1 R | ConditionPresent$ Card.Self+Scout | Types$ Human,Faerie,Detective | RemoveCreatureTypes$ True | Triggers$ KellanCombat | Duration$ Permanent | SpellDescription$ If NICKNAME is a Scout, it becomes a Human Faerie Detective and gains "Whenever NICKNAME deals combat damage to a player, exile the top card of your library. You may play that card this turn." -SVar:KellanCombat:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigExile | TriggerZones$ Battlefield | TriggerDescription$ Whenever NICKNAME deals combat damage to a player, exile the top card of your library. You may play that card this turn. -SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect -SVar:DBEffect:DB$ Effect | StaticAbilities$ STPlay | RememberObjects$ Remembered | ForgetOnMoved$ Exile | SubAbility$ DBCleanup -SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play this card this turn. -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -A:AB$ Animate | Cost$ 2 R | ConditionPresent$ Card.Self+Detective | Types$ Human,Faerie,Rogue | RemoveCreatureTypes$ True | Duration$ Permanent | Power$ 3 | Toughness$ 2 | Keywords$ Double Strike | SpellDescription$ If NICKNAME is a Detective, it becomes a 3/2 Human Faerie Rogue and gains double strike. +Name:Kellan, Planar Trailblazer +ManaCost:R +Types:Legendary Creature Human Faerie Scout +PT:2/1 +A:AB$ Animate | Cost$ 1 R | ConditionPresent$ Card.Self+Scout | Types$ Human,Faerie,Detective | RemoveCreatureTypes$ True | Triggers$ KellanCombat | Duration$ Permanent | SpellDescription$ If NICKNAME is a Scout, it becomes a Human Faerie Detective and gains "Whenever NICKNAME deals combat damage to a player, exile the top card of your library. You may play that card this turn." +SVar:KellanCombat:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigExile | TriggerZones$ Battlefield | TriggerDescription$ Whenever NICKNAME deals combat damage to a player, exile the top card of your library. You may play that card this turn. +SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect +SVar:DBEffect:DB$ Effect | StaticAbilities$ STPlay | RememberObjects$ Remembered | ForgetOnMoved$ Exile | SubAbility$ DBCleanup +SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play this card this turn. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +A:AB$ Animate | Cost$ 2 R | ConditionPresent$ Card.Self+Detective | Types$ Human,Faerie,Rogue | RemoveCreatureTypes$ True | Duration$ Permanent | Power$ 3 | Toughness$ 2 | Keywords$ Double Strike | SpellDescription$ If NICKNAME is a Detective, it becomes a 3/2 Human Faerie Rogue and gains double strike. Oracle:{1}{R}: If Kellan is a Scout, it becomes a Human Faerie Detective and gains "Whenever Kellan deals combat damage to a player, exile the top card of your library. You may play that card this turn."\n{2}{R}: If Kellan is a Detective, it becomes a 3/2 Human Faerie Rogue and gains double strike. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/k/keys_to_the_house.txt b/forge-gui/res/cardsfolder/k/keys_to_the_house.txt index 76ce843920f..1677dfa7166 100644 --- a/forge-gui/res/cardsfolder/k/keys_to_the_house.txt +++ b/forge-gui/res/cardsfolder/k/keys_to_the_house.txt @@ -1,6 +1,6 @@ -Name:Keys to the House -ManaCost:1 -Types:Artifact -A:AB$ ChangeZone | Cost$ 1 T Sac<1/CARDNAME> | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic land card, reveal it, put it into your hand, then shuffle. -A:AB$ UnlockDoor | Cost$ 3 T Sac<1/CARDNAME> | Mode$ LockOrUnlock | ValidTgts$ Room.YouCtrl | TgtPrompt$ Choose target Room you control | SorcerySpeed$ True | SpellDescription$ Lock or unlock a door of target Room you control. Activate only as a sorcery. +Name:Keys to the House +ManaCost:1 +Types:Artifact +A:AB$ ChangeZone | Cost$ 1 T Sac<1/CARDNAME> | Origin$ Library | Destination$ Hand | ChangeType$ Land.Basic | ChangeNum$ 1 | SpellDescription$ Search your library for a basic land card, reveal it, put it into your hand, then shuffle. +A:AB$ UnlockDoor | Cost$ 3 T Sac<1/CARDNAME> | Mode$ LockOrUnlock | ValidTgts$ Room.YouCtrl | TgtPrompt$ Choose target Room you control | SorcerySpeed$ True | SpellDescription$ Lock or unlock a door of target Room you control. Activate only as a sorcery. Oracle:{1}, {T}, Sacrifice Keys to the House: Search your library for a basic land card, reveal it, put it into your hand, then shuffle.\n{3}, {T}, Sacrifice Keys to the House: Lock or unlock a door of target Room you control. Activate only as a sorcery. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/k/kiora_the_rising_tide.txt b/forge-gui/res/cardsfolder/k/kiora_the_rising_tide.txt index a902e6554c6..d40b921cb0f 100644 --- a/forge-gui/res/cardsfolder/k/kiora_the_rising_tide.txt +++ b/forge-gui/res/cardsfolder/k/kiora_the_rising_tide.txt @@ -1,11 +1,11 @@ -Name:Kiora, the Rising Tide -ManaCost:2 U -Types:Legendary Creature Merfolk Noble -PT:3/2 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When NICKNAME enters, draw two cards, then discard two cards. -SVar:TrigDraw:DB$ Draw | NumCards$ 2 | SubAbility$ DBDiscard -SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 2 | Mode$ TgtChoose -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigToken | Threshold$ True | OptionalDecider$ You | TriggerDescription$ Threshold — Whenever NICKNAME attacks, if there are seven or more cards in your graveyard, you may create Scion of the Deep, a legendary 8/8 blue Octopus creature token. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ scion_of_the_deep | TokenOwner$ You -DeckHas:Ability$Token -Oracle:When Kiora enters, draw two cards, then discard two cards.\nThreshold — Whenever Kiora attacks, if there are seven or more cards in your graveyard, you may create Scion of the Deep, a legendary 8/8 blue Octopus creature token. +Name:Kiora, the Rising Tide +ManaCost:2 U +Types:Legendary Creature Merfolk Noble +PT:3/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When NICKNAME enters, draw two cards, then discard two cards. +SVar:TrigDraw:DB$ Draw | NumCards$ 2 | SubAbility$ DBDiscard +SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 2 | Mode$ TgtChoose +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigToken | Threshold$ True | OptionalDecider$ You | TriggerDescription$ Threshold — Whenever NICKNAME attacks, if there are seven or more cards in your graveyard, you may create Scion of the Deep, a legendary 8/8 blue Octopus creature token. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ scion_of_the_deep | TokenOwner$ You +DeckHas:Ability$Token +Oracle:When Kiora enters, draw two cards, then discard two cards.\nThreshold — Whenever Kiora attacks, if there are seven or more cards in your graveyard, you may create Scion of the Deep, a legendary 8/8 blue Octopus creature token. diff --git a/forge-gui/res/cardsfolder/k/koma_world_eater.txt b/forge-gui/res/cardsfolder/k/koma_world_eater.txt index 12d414e25e3..a293e36800a 100644 --- a/forge-gui/res/cardsfolder/k/koma_world_eater.txt +++ b/forge-gui/res/cardsfolder/k/koma_world_eater.txt @@ -1,11 +1,11 @@ -Name:Koma, World-Eater -ManaCost:3 G G U U -Types:Legendary Creature Serpent -PT:8/12 -R:Event$ Counter | ValidCard$ Card.Self | ValidSA$ Spell | Layer$ CantHappen | Description$ This spell can't be countered. -K:Trample -K:Ward:4 -T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever NICKNAME deals combat damage to a player, create four 3/3 blue Serpent creature tokens named Koma's Coil. -SVar:TrigToken:DB$ Token | TokenAmount$ 4 | TokenScript$ komas_coil | TokenOwner$ You -DeckHas:Ability$Token +Name:Koma, World-Eater +ManaCost:3 G G U U +Types:Legendary Creature Serpent +PT:8/12 +R:Event$ Counter | ValidCard$ Card.Self | ValidSA$ Spell | Layer$ CantHappen | Description$ This spell can't be countered. +K:Trample +K:Ward:4 +T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever NICKNAME deals combat damage to a player, create four 3/3 blue Serpent creature tokens named Koma's Coil. +SVar:TrigToken:DB$ Token | TokenAmount$ 4 | TokenScript$ komas_coil | TokenOwner$ You +DeckHas:Ability$Token Oracle:This spell can't be countered.\nTrample, ward {4}\nWhenever Koma deals combat damage to a player, create four 3/3 blue Serpent creature tokens named Koma's Coil. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/k/kykar_zephyr_awakener.txt b/forge-gui/res/cardsfolder/k/kykar_zephyr_awakener.txt index d10ff0b2ca0..fe0454f34f9 100644 --- a/forge-gui/res/cardsfolder/k/kykar_zephyr_awakener.txt +++ b/forge-gui/res/cardsfolder/k/kykar_zephyr_awakener.txt @@ -1,14 +1,14 @@ -Name:Kykar, Zephyr Awakener -ManaCost:2 W U -Types:Legendary Creature Bird Wizard -PT:3/4 -K:Flying -T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigCharm | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, ABILITY -SVar:TrigCharm:DB$ Charm | Choices$ DBBlink,DBToken -SVar:DBBlink:DB$ ChangeZone | ValidTgts$ Creature.YouCtrl+Other | TgtPrompt$ Select another target creature you control | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DelTrig | SpellDescription$ Exile another target creature you control. Return that card to the battlefield under its owner's control at the beginning of the next end step. -SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturn | RememberObjects$ RememberedLKI | TriggerDescription$ Return exiled card to the battlefield under its owner's control. | SubAbility$ DBCleanup -SVar:TrigReturn:DB$ ChangeZone | Origin$ Exile | Destination$ Battlefield | Defined$ DelayTriggerRememberedLKI -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -SVar:DBToken:DB$ Token | TokenScript$ w_1_1_spirit_flying | SpellDescription$ Create a 1/1 white Spirit creature token with flying. -DeckHas:Ability$Token +Name:Kykar, Zephyr Awakener +ManaCost:2 W U +Types:Legendary Creature Bird Wizard +PT:3/4 +K:Flying +T:Mode$ SpellCast | ValidCard$ Card.nonCreature | ValidActivatingPlayer$ You | Execute$ TrigCharm | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a noncreature spell, ABILITY +SVar:TrigCharm:DB$ Charm | Choices$ DBBlink,DBToken +SVar:DBBlink:DB$ ChangeZone | ValidTgts$ Creature.YouCtrl+Other | TgtPrompt$ Select another target creature you control | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DelTrig | SpellDescription$ Exile another target creature you control. Return that card to the battlefield under its owner's control at the beginning of the next end step. +SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigReturn | RememberObjects$ RememberedLKI | TriggerDescription$ Return exiled card to the battlefield under its owner's control. | SubAbility$ DBCleanup +SVar:TrigReturn:DB$ ChangeZone | Origin$ Exile | Destination$ Battlefield | Defined$ DelayTriggerRememberedLKI +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:DBToken:DB$ Token | TokenScript$ w_1_1_spirit_flying | SpellDescription$ Create a 1/1 white Spirit creature token with flying. +DeckHas:Ability$Token Oracle:Flying\nWhenever you cast a noncreature spell, choose one —\n• Exile another target creature you control. Return that card to the battlefield under its owner's control at the beginning of the next end step.\n• Create a 1/1 white Spirit creature token with flying. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/l/leyline_axe.txt b/forge-gui/res/cardsfolder/l/leyline_axe.txt index bf31243d58b..607a89f6539 100644 --- a/forge-gui/res/cardsfolder/l/leyline_axe.txt +++ b/forge-gui/res/cardsfolder/l/leyline_axe.txt @@ -1,8 +1,8 @@ -Name:Leyline Axe -ManaCost:4 -Types:Artifact Equipment -K:MayEffectFromOpeningHand:FromHand -SVar:FromHand:DB$ ChangeZone | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If this card is in your opening hand, you may begin the game with it on the battlefield. -S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Double Strike & Trample | Description$ Equipped creature gets +1/+1 and has double strike and trample. -K:Equip:3 +Name:Leyline Axe +ManaCost:4 +Types:Artifact Equipment +K:MayEffectFromOpeningHand:FromHand +SVar:FromHand:DB$ ChangeZone | Defined$ Self | Origin$ Hand | Destination$ Battlefield | SpellDescription$ If this card is in your opening hand, you may begin the game with it on the battlefield. +S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 1 | AddToughness$ 1 | AddKeyword$ Double Strike & Trample | Description$ Equipped creature gets +1/+1 and has double strike and trample. +K:Equip:3 Oracle:If this card is in your opening hand, you may begin the game with it on the battlefield.\nEquipped creature gets +1/+1 and has double strike and trample.\nEquip {3} ({3}: Attach to target creature you control. Equip only as a sorcery.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/l/living_phone.txt b/forge-gui/res/cardsfolder/l/living_phone.txt index 91289a7e185..8a3aa866e93 100644 --- a/forge-gui/res/cardsfolder/l/living_phone.txt +++ b/forge-gui/res/cardsfolder/l/living_phone.txt @@ -1,7 +1,7 @@ -Name:Living Phone -ManaCost:2 W -Types:Artifact Creature Toy -PT:2/1 -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigDig | TriggerDescription$ When CARDNAME dies, look at the top five cards of your library. You may reveal a creature card with power 2 or less from among them and put it into your hand. Put the rest on the bottom of your library in a random order. -SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Creature.powerLE2 | RestRandomOrder$ True | ForceRevealToController$ True +Name:Living Phone +ManaCost:2 W +Types:Artifact Creature Toy +PT:2/1 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigDig | TriggerDescription$ When CARDNAME dies, look at the top five cards of your library. You may reveal a creature card with power 2 or less from among them and put it into your hand. Put the rest on the bottom of your library in a random order. +SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Creature.powerLE2 | RestRandomOrder$ True | ForceRevealToController$ True Oracle:When Living Phone dies, look at the top five cards of your library. You may reveal a creature card with power 2 or less from among them and put it into your hand. Put the rest on the bottom of your library in a random order. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/l/loot_exuberant_explorer.txt b/forge-gui/res/cardsfolder/l/loot_exuberant_explorer.txt index 8d8bd0bcf0c..2127621f1b1 100644 --- a/forge-gui/res/cardsfolder/l/loot_exuberant_explorer.txt +++ b/forge-gui/res/cardsfolder/l/loot_exuberant_explorer.txt @@ -1,8 +1,8 @@ -Name:Loot, Exuberant Explorer -ManaCost:2 G -Types:Legendary Creature Beast Noble -PT:1/4 -S:Mode$ Continuous | Affected$ You | AdjustLandPlays$ 1 | Description$ You may play an additional land on each of your turns. -A:AB$ Dig | Cost$ 4 G G T | DigNum$ 6 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Creature.cmcLEX | DestinationZone$ Battlefield | RestRandomOrder$ True | SpellDescription$ Look at the top six cards of your library. You may reveal a creature card with mana value less than or equal to the number of lands you control from among them and put it onto the battlefield. Put the rest on the bottom in a random order. -SVar:X:Count$Valid Land.YouCtrl +Name:Loot, Exuberant Explorer +ManaCost:2 G +Types:Legendary Creature Beast Noble +PT:1/4 +S:Mode$ Continuous | Affected$ You | AdjustLandPlays$ 1 | Description$ You may play an additional land on each of your turns. +A:AB$ Dig | Cost$ 4 G G T | DigNum$ 6 | ChangeNum$ 1 | Optional$ True | ChangeValid$ Creature.cmcLEX | DestinationZone$ Battlefield | RestRandomOrder$ True | SpellDescription$ Look at the top six cards of your library. You may reveal a creature card with mana value less than or equal to the number of lands you control from among them and put it onto the battlefield. Put the rest on the bottom in a random order. +SVar:X:Count$Valid Land.YouCtrl Oracle:You may play an additional land on each of your turns.\n{4}{G}{G}, {T}: Look at the top six cards of your library. You may reveal a creature card with mana value less than or equal to the number of lands you control from among them and put it onto the battlefield. Put the rest on the bottom in a random order. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/l/luminous_rebuke.txt b/forge-gui/res/cardsfolder/l/luminous_rebuke.txt index 6cbd13623a1..31bfecc880d 100644 --- a/forge-gui/res/cardsfolder/l/luminous_rebuke.txt +++ b/forge-gui/res/cardsfolder/l/luminous_rebuke.txt @@ -1,6 +1,6 @@ -Name:Luminous Rebuke -ManaCost:4 W -Types:Instant -S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 3 | EffectZone$ All | ValidTarget$ Creature.tapped | Description$ This spell costs {3} less to cast if it targets a tapped creature. -A:SP$ Destroy | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Destroy target creature. +Name:Luminous Rebuke +ManaCost:4 W +Types:Instant +S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 3 | EffectZone$ All | ValidTarget$ Creature.tapped | Description$ This spell costs {3} less to cast if it targets a tapped creature. +A:SP$ Destroy | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$ Destroy target creature. Oracle:This spell costs {3} less to cast if it targets a tapped creature.\nDestroy target creature. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/l/lunar_insight.txt b/forge-gui/res/cardsfolder/l/lunar_insight.txt index 0422e819888..69a84871b16 100644 --- a/forge-gui/res/cardsfolder/l/lunar_insight.txt +++ b/forge-gui/res/cardsfolder/l/lunar_insight.txt @@ -1,6 +1,6 @@ -Name:Lunar Insight -ManaCost:2 U -Types:Instant -A:SP$ Draw | NumCards$ X | SpellDescription$ Draw a card for each different mana value among nonland permanents you control. -SVar:X:Count$Valid Card.YouCtrl+nonLand$DifferentCMC -Oracle:Draw a card for each different mana value among nonland permanents you control. +Name:Lunar Insight +ManaCost:2 U +Types:Instant +A:SP$ Draw | NumCards$ X | SpellDescription$ Draw a card for each different mana value among nonland permanents you control. +SVar:X:Count$Valid Card.YouCtrl+nonLand$DifferentCMC +Oracle:Draw a card for each different mana value among nonland permanents you control. diff --git a/forge-gui/res/cardsfolder/m/marina_vendrell.txt b/forge-gui/res/cardsfolder/m/marina_vendrell.txt index b72f821fd90..adcf3cde45d 100644 --- a/forge-gui/res/cardsfolder/m/marina_vendrell.txt +++ b/forge-gui/res/cardsfolder/m/marina_vendrell.txt @@ -1,9 +1,9 @@ -Name:Marina Vendrell -ManaCost:W U B R G -Types:Legendary Creature Human Warlock -PT:3/5 -K:Flying -T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters, reveal the top seven cards of your library. Put all enchantment cards from among them into your hand and the rest on the bottom of your library in a random order. -SVar:TrigDig:DB$ Dig | DigNum$ 7 | Reveal$ True | ChangeNum$ All | ChangeValid$ Enchantment | RestRandomOrder$ True -A:AB$ UnlockDoor | Cost$ T | Mode$ LockOrUnlock | ValidTgts$ Room.YouCtrl | TgtPrompt$ Choose target Room you control | SorcerySpeed$ True | SpellDescription$ Lock or unlock a door of target Room you control. Activate only as a sorcery. +Name:Marina Vendrell +ManaCost:W U B R G +Types:Legendary Creature Human Warlock +PT:3/5 +K:Flying +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters, reveal the top seven cards of your library. Put all enchantment cards from among them into your hand and the rest on the bottom of your library in a random order. +SVar:TrigDig:DB$ Dig | DigNum$ 7 | Reveal$ True | ChangeNum$ All | ChangeValid$ Enchantment | RestRandomOrder$ True +A:AB$ UnlockDoor | Cost$ T | Mode$ LockOrUnlock | ValidTgts$ Room.YouCtrl | TgtPrompt$ Choose target Room you control | SorcerySpeed$ True | SpellDescription$ Lock or unlock a door of target Room you control. Activate only as a sorcery. Oracle:When Marina Vendrell enters, reveal the top seven cards of your library. Put all enchantment cards from among them into your hand and the rest on the bottom of your library in a random order.\n{T}: Lock or unlock a door of target Room you control. Activate only as a sorcery. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/m/midnight_snack.txt b/forge-gui/res/cardsfolder/m/midnight_snack.txt index e3f93cde524..2ac2975817e 100644 --- a/forge-gui/res/cardsfolder/m/midnight_snack.txt +++ b/forge-gui/res/cardsfolder/m/midnight_snack.txt @@ -1,10 +1,10 @@ -Name:Midnight Snack -ManaCost:2 B -Types:Enchantment -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | CheckSVar$ RaidTest | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Raid — At the beginning of your end step, if you attacked this turn, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this token: You gain 3 life.") -SVar:TrigToken:DB$ Token | TokenScript$ c_a_food_sac -A:AB$ LoseLife | Cost$ 2 B Sac<1/CARDNAME> | ValidTgts$ Opponent | LifeAmount$ X | SpellDescription$ Target opponent loses X life, where X is the amount of life you gained this turn. -SVar:RaidTest:Count$AttackersDeclared -SVar:X:Count$LifeYouGainedThisTurn -DeckHas:Ability$LifeGain|Token +Name:Midnight Snack +ManaCost:2 B +Types:Enchantment +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | CheckSVar$ RaidTest | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Raid — At the beginning of your end step, if you attacked this turn, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this token: You gain 3 life.") +SVar:TrigToken:DB$ Token | TokenScript$ c_a_food_sac +A:AB$ LoseLife | Cost$ 2 B Sac<1/CARDNAME> | ValidTgts$ Opponent | LifeAmount$ X | SpellDescription$ Target opponent loses X life, where X is the amount of life you gained this turn. +SVar:RaidTest:Count$AttackersDeclared +SVar:X:Count$LifeYouGainedThisTurn +DeckHas:Ability$LifeGain|Token Oracle:Raid — At the beginning of your end step, if you attacked this turn, create a Food token. (It's an artifact with "{2}, {T}, Sacrifice this token: You gain 3 life.")\n{2}{B}, Sacrifice this enchantment: Target opponent loses X life, where X is the amount of life you gained this turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/m/mischievous_mystic.txt b/forge-gui/res/cardsfolder/m/mischievous_mystic.txt index c28e495fa3a..4d21a4e5ec5 100644 --- a/forge-gui/res/cardsfolder/m/mischievous_mystic.txt +++ b/forge-gui/res/cardsfolder/m/mischievous_mystic.txt @@ -1,9 +1,9 @@ -Name:Mischievous Mystic -ManaCost:1 U -Types:Creature Human Wizard -PT:2/1 -K:Flying -T:Mode$ Drawn | ValidCard$ Card.YouCtrl | Number$ 2 | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever you draw your second card each turn, create a 1/1 blue Faerie token with flying. -SVar:TrigToken:DB$ Token | TokenScript$ u_1_1_faerie_flying -DeckHas:Ability$Token +Name:Mischievous Mystic +ManaCost:1 U +Types:Creature Human Wizard +PT:2/1 +K:Flying +T:Mode$ Drawn | ValidCard$ Card.YouCtrl | Number$ 2 | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever you draw your second card each turn, create a 1/1 blue Faerie token with flying. +SVar:TrigToken:DB$ Token | TokenScript$ u_1_1_faerie_flying +DeckHas:Ability$Token Oracle:Flying\nWhenever you draw your second card each turn, create a 1/1 blue Faerie token with flying. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/n/nazar_the_velvet_fang.txt b/forge-gui/res/cardsfolder/n/nazar_the_velvet_fang.txt index e5c631ad22f..6c65bccb8cb 100644 --- a/forge-gui/res/cardsfolder/n/nazar_the_velvet_fang.txt +++ b/forge-gui/res/cardsfolder/n/nazar_the_velvet_fang.txt @@ -1,14 +1,14 @@ -Name:Nazar, the Velvet Fang -ManaCost:3 B -Types:Legendary Creature Vampire Warlock -PT:3/3 -K:Menace -T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you gain life, put a feeding counter on CARDNAME. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ FEEDING | CounterNum$ 1 -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever NICKNAME attacks, you may remove three feeding counters from it. If you do, you draw three cards and you lose 3 life. -SVar:TrigDraw:AB$ Draw | Cost$ SubCounter<3/FEEDING> | Defined$ You | NumCards$ 3 | SubAbility$ DBLoseLife -SVar:DBLoseLife:DB$ LoseLife | Defined$ You | LifeAmount$ 3 -SVar:HasAttackEffect:TRUE -DeckHas:Ability$Counters -DeckHints:Ability$LifeGain -Oracle:Menace (This creature can't be blocked except by two or more creatures.)\nWhenever you gain life, put a feeding counter on Nazar, the Velvet Fang.\nWhenever Nazar attacks, you may remove three feeding counters from it. If you do, you draw three cards and you lose 3 life. +Name:Nazar, the Velvet Fang +ManaCost:3 B +Types:Legendary Creature Vampire Warlock +PT:3/3 +K:Menace +T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you gain life, put a feeding counter on CARDNAME. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ FEEDING | CounterNum$ 1 +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever NICKNAME attacks, you may remove three feeding counters from it. If you do, you draw three cards and you lose 3 life. +SVar:TrigDraw:AB$ Draw | Cost$ SubCounter<3/FEEDING> | Defined$ You | NumCards$ 3 | SubAbility$ DBLoseLife +SVar:DBLoseLife:DB$ LoseLife | Defined$ You | LifeAmount$ 3 +SVar:HasAttackEffect:TRUE +DeckHas:Ability$Counters +DeckHints:Ability$LifeGain +Oracle:Menace (This creature can't be blocked except by two or more creatures.)\nWhenever you gain life, put a feeding counter on Nazar, the Velvet Fang.\nWhenever Nazar attacks, you may remove three feeding counters from it. If you do, you draw three cards and you lose 3 life. diff --git a/forge-gui/res/cardsfolder/n/needletooth_pack.txt b/forge-gui/res/cardsfolder/n/needletooth_pack.txt index 81e7abaf07b..644de028a72 100644 --- a/forge-gui/res/cardsfolder/n/needletooth_pack.txt +++ b/forge-gui/res/cardsfolder/n/needletooth_pack.txt @@ -1,8 +1,8 @@ -Name:Needletooth Pack -ManaCost:3 G G -Types:Creature Dinosaur -PT:4/5 -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | CheckSVar$ Morbid | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, put two +1/+1 counters on target creature you control. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 2 -SVar:Morbid:Count$Morbid.1.0 +Name:Needletooth Pack +ManaCost:3 G G +Types:Creature Dinosaur +PT:4/5 +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | CheckSVar$ Morbid | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, put two +1/+1 counters on target creature you control. +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 2 +SVar:Morbid:Count$Morbid.1.0 Oracle:Morbid — At the beginning of your end step, if a creature died this turn, put two +1/+1 counters on target creature you control. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/n/neerdiv_devious_diver.txt b/forge-gui/res/cardsfolder/n/neerdiv_devious_diver.txt index c1bc83a84d8..cbbd30e40f4 100644 --- a/forge-gui/res/cardsfolder/n/neerdiv_devious_diver.txt +++ b/forge-gui/res/cardsfolder/n/neerdiv_devious_diver.txt @@ -1,12 +1,12 @@ -Name:Neerdiv, Devious Diver -ManaCost:2 U -Types:Legendary Creature Merfolk Rogue -PT:2/2 -T:Mode$ Taps | ValidCard$ Card.Self | Execute$ TrigMill | TriggerDescription$ Whenever CARDNAME becomes tapped, target player mills cards equal to its power. -SVar:TrigMill:DB$ Mill | NumCards$ X | ValidTgts$ Player -T:Mode$ SpellCast | ValidCard$ Card.wasCastFromYourGraveyard | ValidActivatingPlayer$ You | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a spell from your graveyard or activate an ability of a card in your graveyard, draw a card and put a +1/+1 counter on NICKNAME. -T:Mode$ AbilityCast | ValidCard$ Card.inZoneGraveyard | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | Secondary$ True | TriggerDescription$ Whenever you cast a spell from your graveyard or activate an ability of a card in your graveyard, draw a card and put a +1/+1 counter on NICKNAME. -SVar:TrigDraw:DB$ Draw | SubAbility$ DBPutCounter -SVar:DBPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | Defined$ Self -SVar:X:Count$CardPower -Oracle:Whenever Neerdiv, Devious Diver becomes tapped, target player mills cards equal to its power.\nWhenever you cast a spell from your graveyard or activate an ability of a card in your graveyard, draw a card and put a +1/+1 counter on Neerdiv. +Name:Neerdiv, Devious Diver +ManaCost:2 U +Types:Legendary Creature Merfolk Rogue +PT:2/2 +T:Mode$ Taps | ValidCard$ Card.Self | Execute$ TrigMill | TriggerDescription$ Whenever CARDNAME becomes tapped, target player mills cards equal to its power. +SVar:TrigMill:DB$ Mill | NumCards$ X | ValidTgts$ Player +T:Mode$ SpellCast | ValidCard$ Card.wasCastFromYourGraveyard | ValidActivatingPlayer$ You | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a spell from your graveyard or activate an ability of a card in your graveyard, draw a card and put a +1/+1 counter on NICKNAME. +T:Mode$ AbilityCast | ValidCard$ Card.inZoneGraveyard | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | Secondary$ True | TriggerDescription$ Whenever you cast a spell from your graveyard or activate an ability of a card in your graveyard, draw a card and put a +1/+1 counter on NICKNAME. +SVar:TrigDraw:DB$ Draw | SubAbility$ DBPutCounter +SVar:DBPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | Defined$ Self +SVar:X:Count$CardPower +Oracle:Whenever Neerdiv, Devious Diver becomes tapped, target player mills cards equal to its power.\nWhenever you cast a spell from your graveyard or activate an ability of a card in your graveyard, draw a card and put a +1/+1 counter on Neerdiv. diff --git a/forge-gui/res/cardsfolder/n/niv_mizzet_visionary.txt b/forge-gui/res/cardsfolder/n/niv_mizzet_visionary.txt index 7b9c4d0fdbe..a0a2ce99a15 100644 --- a/forge-gui/res/cardsfolder/n/niv_mizzet_visionary.txt +++ b/forge-gui/res/cardsfolder/n/niv_mizzet_visionary.txt @@ -1,10 +1,10 @@ -Name:Niv-Mizzet, Visionary -ManaCost:4 U R -Types:Legendary Creature Dragon Wizard -PT:5/5 -K:Flying -S:Mode$ Continuous | Affected$ You | SetMaxHandSize$ Unlimited | Description$ You have no maximum hand size. -T:Mode$ DamageDone | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent | CombatDamage$ False | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever a source you control deals noncombat damage to an opponent, you draw that many cards. -SVar:TrigDraw:DB$ Draw | NumCards$ X -SVar:X:TriggerCount$DamageAmount -Oracle:Flying\nYou have no maximum hand size.\nWhenever a source you control deals noncombat damage to an opponent, you draw that many cards. +Name:Niv-Mizzet, Visionary +ManaCost:4 U R +Types:Legendary Creature Dragon Wizard +PT:5/5 +K:Flying +S:Mode$ Continuous | Affected$ You | SetMaxHandSize$ Unlimited | Description$ You have no maximum hand size. +T:Mode$ DamageDone | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent | CombatDamage$ False | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ Whenever a source you control deals noncombat damage to an opponent, you draw that many cards. +SVar:TrigDraw:DB$ Draw | NumCards$ X +SVar:X:TriggerCount$DamageAmount +Oracle:Flying\nYou have no maximum hand size.\nWhenever a source you control deals noncombat damage to an opponent, you draw that many cards. diff --git a/forge-gui/res/cardsfolder/o/ozox_the_clattering_king.txt b/forge-gui/res/cardsfolder/o/ozox_the_clattering_king.txt index a42ef84944a..54840bd34b5 100644 --- a/forge-gui/res/cardsfolder/o/ozox_the_clattering_king.txt +++ b/forge-gui/res/cardsfolder/o/ozox_the_clattering_king.txt @@ -1,9 +1,9 @@ -Name:Ozox, the Clattering King -ManaCost:2 B -Types:Legendary Creature Skeleton Noble -PT:3/2 -K:CARDNAME can't block. -T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigToken | TriggerDescription$ When NICKNAME dies, create Jumblebones, a legendary 2/1 black Skeleton creature token with "This creature can't block" and "When this creature leaves the battlefield, return target card named Ozox, the Clattering King from your graveyard to your hand." -SVar:TrigToken:DB$ Token | TokenScript$ jumblebones | TokenAmount$ 1 -DeckHas:Ability$Token +Name:Ozox, the Clattering King +ManaCost:2 B +Types:Legendary Creature Skeleton Noble +PT:3/2 +K:CARDNAME can't block. +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigToken | TriggerDescription$ When NICKNAME dies, create Jumblebones, a legendary 2/1 black Skeleton creature token with "This creature can't block" and "When this creature leaves the battlefield, return target card named Ozox, the Clattering King from your graveyard to your hand." +SVar:TrigToken:DB$ Token | TokenScript$ jumblebones | TokenAmount$ 1 +DeckHas:Ability$Token Oracle:Ozox, the Clattering King can't block.\nWhen Ozox dies, create Jumblebones, a legendary 2/1 black Skeleton creature token with "This creature can't block" and "When this creature leaves the battlefield, return target card named Ozox, the Clattering King from your graveyard to your hand." \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/p/perforating_artist.txt b/forge-gui/res/cardsfolder/p/perforating_artist.txt index f2a49f772aa..9af5ed7aab5 100644 --- a/forge-gui/res/cardsfolder/p/perforating_artist.txt +++ b/forge-gui/res/cardsfolder/p/perforating_artist.txt @@ -1,12 +1,12 @@ -Name:Perforating Artist -ManaCost:1 B R -Types:Creature Devil -PT:3/2 -K:Deathtouch -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ RaidTest | Execute$ TrigTorment | TriggerDescription$ Raid — At the beginning of your end step, if you attacked this turn, each opponent loses 3 life unless that player sacrifices a nonland permanent of their choice or discards a card. -SVar:TrigTorment:DB$ GenericChoice | Defined$ Opponent | TempRemember$ Chooser | Choices$ SacNonland,Discard | FallbackAbility$ LoseLifeFallback | AILogic$ PayUnlessCost -SVar:Discard:DB$ LoseLife | Defined$ Remembered | LifeAmount$ 3 | UnlessCost$ Discard<1/Card> | UnlessPayer$ Remembered | SpellDescription$ You lose 3 life unless you discard a card. -SVar:SacNonland:DB$ LoseLife | Defined$ Remembered | LifeAmount$ 3 | UnlessCost$ Sac<1/Permanent.nonLand/nonland permanent> | UnlessPayer$ Remembered | SpellDescription$ You lose 3 life unless you sacrifice a nonland permanent. -SVar:LoseLifeFallback:DB$ LoseLife | Defined$ Remembered | LifeAmount$ 3 -SVar:RaidTest:Count$AttackersDeclared +Name:Perforating Artist +ManaCost:1 B R +Types:Creature Devil +PT:3/2 +K:Deathtouch +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ RaidTest | Execute$ TrigTorment | TriggerDescription$ Raid — At the beginning of your end step, if you attacked this turn, each opponent loses 3 life unless that player sacrifices a nonland permanent of their choice or discards a card. +SVar:TrigTorment:DB$ GenericChoice | Defined$ Opponent | TempRemember$ Chooser | Choices$ SacNonland,Discard | FallbackAbility$ LoseLifeFallback | AILogic$ PayUnlessCost +SVar:Discard:DB$ LoseLife | Defined$ Remembered | LifeAmount$ 3 | UnlessCost$ Discard<1/Card> | UnlessPayer$ Remembered | SpellDescription$ You lose 3 life unless you discard a card. +SVar:SacNonland:DB$ LoseLife | Defined$ Remembered | LifeAmount$ 3 | UnlessCost$ Sac<1/Permanent.nonLand/nonland permanent> | UnlessPayer$ Remembered | SpellDescription$ You lose 3 life unless you sacrifice a nonland permanent. +SVar:LoseLifeFallback:DB$ LoseLife | Defined$ Remembered | LifeAmount$ 3 +SVar:RaidTest:Count$AttackersDeclared Oracle:Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.)\nRaid — At the beginning of your end step, if you attacked this turn, each opponent loses 3 life unless that player sacrifices a nonland permanent of their choice or discards a card. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/p/phantasmal_shieldback.txt b/forge-gui/res/cardsfolder/p/phantasmal_shieldback.txt index 52930184486..2d8bc546159 100644 --- a/forge-gui/res/cardsfolder/p/phantasmal_shieldback.txt +++ b/forge-gui/res/cardsfolder/p/phantasmal_shieldback.txt @@ -1,10 +1,10 @@ -Name:Phantasmal Shieldback -ManaCost:U -Types:Creature Turtle Illusion -PT:1/3 -T:Mode$ BecomesTarget | ValidTarget$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ When CARDNAME becomes the target of a spell or ability, sacrifice it. -SVar:TrigSac:DB$ Sacrifice -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME dies, draw a card. -SVar:TrigDraw:DB$ Draw | Defined$ TriggeredCardController -SVar:Targeting:Dies -Oracle:When Phantasmal Shieldback becomes the target of a spell or ability, sacrifice it.\nWhen Phantasmal Shieldback dies, draw a card. +Name:Phantasmal Shieldback +ManaCost:U +Types:Creature Turtle Illusion +PT:1/3 +T:Mode$ BecomesTarget | ValidTarget$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ When CARDNAME becomes the target of a spell or ability, sacrifice it. +SVar:TrigSac:DB$ Sacrifice +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME dies, draw a card. +SVar:TrigDraw:DB$ Draw | Defined$ TriggeredCardController +SVar:Targeting:Dies +Oracle:When Phantasmal Shieldback becomes the target of a spell or ability, sacrifice it.\nWhen Phantasmal Shieldback dies, draw a card. diff --git a/forge-gui/res/cardsfolder/p/plagon_lord_of_the_beach.txt b/forge-gui/res/cardsfolder/p/plagon_lord_of_the_beach.txt index dc9ab885434..0b83a850996 100644 --- a/forge-gui/res/cardsfolder/p/plagon_lord_of_the_beach.txt +++ b/forge-gui/res/cardsfolder/p/plagon_lord_of_the_beach.txt @@ -1,10 +1,10 @@ -Name:Plagon, Lord of the Beach -ManaCost:2 U -Types:Legendary Creature Starfish Wizard -PT:0/3 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters, draw a card for each creature you control with toughness greater than its power. -SVar:TrigDraw:DB$ Draw | NumCards$ X -A:AB$ Effect | Cost$ WU | ValidTgts$ Creature.YouCtrl | RememberObjects$ Targeted | StaticAbilities$ CombatDamageToughness | ForgetOnMoved$ Battlefield | TgtPrompt$ Select target creature you control | SpellDescription$ Target creature you control assigns combat damage equal to its toughness rather than its power this turn. -SVar:CombatDamageToughness:Mode$ CombatDamageToughness | ValidCard$ Card.IsRemembered | Description$ This creature assigns combat damage equal to its toughness rather than its power. -SVar:X:Count$Valid Creature.YouCtrl+powerLTtoughness +Name:Plagon, Lord of the Beach +ManaCost:2 U +Types:Legendary Creature Starfish Wizard +PT:0/3 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ When CARDNAME enters, draw a card for each creature you control with toughness greater than its power. +SVar:TrigDraw:DB$ Draw | NumCards$ X +A:AB$ Effect | Cost$ WU | ValidTgts$ Creature.YouCtrl | RememberObjects$ Targeted | StaticAbilities$ CombatDamageToughness | ForgetOnMoved$ Battlefield | TgtPrompt$ Select target creature you control | SpellDescription$ Target creature you control assigns combat damage equal to its toughness rather than its power this turn. +SVar:CombatDamageToughness:Mode$ CombatDamageToughness | ValidCard$ Card.IsRemembered | Description$ This creature assigns combat damage equal to its toughness rather than its power. +SVar:X:Count$Valid Creature.YouCtrl+powerLTtoughness Oracle:When Plagon, Lord of the Beach enters, draw a card for each creature you control with toughness greater than its power.\n{W/U}: Target creature you control assigns combat damage equal to its toughness rather than its power this turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/p/pol_jamaar_illusionist.txt b/forge-gui/res/cardsfolder/p/pol_jamaar_illusionist.txt index 7e039b381f1..07e4cee623f 100644 --- a/forge-gui/res/cardsfolder/p/pol_jamaar_illusionist.txt +++ b/forge-gui/res/cardsfolder/p/pol_jamaar_illusionist.txt @@ -1,10 +1,10 @@ -Name:Pol Jamaar, Illusionist -ManaCost:4 U U -Types:Legendary Creature Human Illusion Wizard -PT:4/5 -K:Flying -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChooseType | TriggerDescription$ When CARDNAME enters, choose a creature type. Draw a card for each creature you control of that type. -SVar:TrigChooseType:DB$ ChooseType | Defined$ You | Type$ Creature | SubAbility$ DBDraw | AILogic$ MostProminentComputerControls -SVar:DBDraw:DB$ Draw | NumCards$ X -SVar:X:Count$Valid Creature.ChosenType+YouCtrl +Name:Pol Jamaar, Illusionist +ManaCost:4 U U +Types:Legendary Creature Human Illusion Wizard +PT:4/5 +K:Flying +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChooseType | TriggerDescription$ When CARDNAME enters, choose a creature type. Draw a card for each creature you control of that type. +SVar:TrigChooseType:DB$ ChooseType | Defined$ You | Type$ Creature | SubAbility$ DBDraw | AILogic$ MostProminentComputerControls +SVar:DBDraw:DB$ Draw | NumCards$ X +SVar:X:Count$Valid Creature.ChosenType+YouCtrl Oracle:Flying\nWhen Pol Jamaar, Illusionist enters, choose a creature type. Draw a card for each creature you control of that type. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/p/polluted_cistern_dim_oubliette.txt b/forge-gui/res/cardsfolder/p/polluted_cistern_dim_oubliette.txt index b1be64f8b70..204af3677a4 100644 --- a/forge-gui/res/cardsfolder/p/polluted_cistern_dim_oubliette.txt +++ b/forge-gui/res/cardsfolder/p/polluted_cistern_dim_oubliette.txt @@ -1,18 +1,18 @@ -Name:Polluted Cistern -ManaCost:1 B -Types:Enchantment Room -T:Mode$ ChangesZoneAll | ValidCards$ Card.YouOwn | Origin$ Library | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever one or more cards are put into your graveyard from your library, each opponent loses 1 life for each card type among those cards. -SVar:TrigLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ X -SVar:X:TriggerObjectsCards$CardTypes -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever one or more cards are put into your graveyard from your library, each opponent loses 1 life for each card type among those cards. - -ALTERNATE - -Name:Dim Oubliette -ManaCost:4 B -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigMill | TriggerDescription$ When you unlock this door, mill three cards, then return a creature card from your graveyard to the battlefield. -SVar:TrigMill:DB$ Mill | Defined$ You | NumCards$ 3 | SubAbility$ DBReturn -SVar:DBReturn:DB$ ChangeZone | ChangeType$ Creature.YouOwn | Mandatory$ True | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | ChangeNum$ 1 +Name:Polluted Cistern +ManaCost:1 B +Types:Enchantment Room +T:Mode$ ChangesZoneAll | ValidCards$ Card.YouOwn | Origin$ Library | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever one or more cards are put into your graveyard from your library, each opponent loses 1 life for each card type among those cards. +SVar:TrigLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ X +SVar:X:TriggerObjectsCards$CardTypes +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever one or more cards are put into your graveyard from your library, each opponent loses 1 life for each card type among those cards. + +ALTERNATE + +Name:Dim Oubliette +ManaCost:4 B +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigMill | TriggerDescription$ When you unlock this door, mill three cards, then return a creature card from your graveyard to the battlefield. +SVar:TrigMill:DB$ Mill | Defined$ You | NumCards$ 3 | SubAbility$ DBReturn +SVar:DBReturn:DB$ ChangeZone | ChangeType$ Creature.YouOwn | Mandatory$ True | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | ChangeNum$ 1 Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, mill three cards, then return a creature card from your graveyard to the battlefield. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/p/preposterous_proportions.txt b/forge-gui/res/cardsfolder/p/preposterous_proportions.txt index 99133610eb9..96963ea2229 100644 --- a/forge-gui/res/cardsfolder/p/preposterous_proportions.txt +++ b/forge-gui/res/cardsfolder/p/preposterous_proportions.txt @@ -1,5 +1,5 @@ -Name:Preposterous Proportions -ManaCost:5 G G -Types:Sorcery -A:SP$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +10 | NumDef$ +10 | KW$ Vigilance | SpellDescription$ Creatures you control get +10/+10 and gain vigilance until end of turn. +Name:Preposterous Proportions +ManaCost:5 G G +Types:Sorcery +A:SP$ PumpAll | ValidCards$ Creature.YouCtrl | NumAtt$ +10 | NumDef$ +10 | KW$ Vigilance | SpellDescription$ Creatures you control get +10/+10 and gain vigilance until end of turn. Oracle:Creatures you control get +10/+10 and gain vigilance until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/p/psemilla_meletian_poet.txt b/forge-gui/res/cardsfolder/p/psemilla_meletian_poet.txt index 91910aa7e04..4d843ae523a 100644 --- a/forge-gui/res/cardsfolder/p/psemilla_meletian_poet.txt +++ b/forge-gui/res/cardsfolder/p/psemilla_meletian_poet.txt @@ -1,10 +1,10 @@ -Name:Psemilla, Meletian Poet -ManaCost:2 W -Types:Legendary Creature Human Bard -PT:1/1 -T:Mode$ SpellCast | ValidCard$ Enchantment | ValidActivatingPlayer$ You | ActivatorThisTurnCast$ EQ1 | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast your first enchantment spell each turn, create a 2/2 white Nymph enchantment creature token. -SVar:TrigToken:DB$ Token | TokenScript$ w_2_2_e_nymph -T:Mode$ Phase | Phase$ BeginCombat | TriggerZones$ Battlefield | Execute$ TrigPump | IsPresent$ Enchantment.YouCtrl | PresentCompare$ GE5 | TriggerDescription$ At the beginning of each combat, if you control five or more enchantments, CARDNAME gets +4/+4 and gains lifelink until end of turn. (Damage dealt by this creature also causes you to gain that much life.) -SVar:TrigPump:DB$ Pump | NumAtt$ +4 | NumDef$ +4 | KW$ Lifelink | Defined$ Self -DeckHints:Type$Enchantment +Name:Psemilla, Meletian Poet +ManaCost:2 W +Types:Legendary Creature Human Bard +PT:1/1 +T:Mode$ SpellCast | ValidCard$ Enchantment | ValidActivatingPlayer$ You | ActivatorThisTurnCast$ EQ1 | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast your first enchantment spell each turn, create a 2/2 white Nymph enchantment creature token. +SVar:TrigToken:DB$ Token | TokenScript$ w_2_2_e_nymph +T:Mode$ Phase | Phase$ BeginCombat | TriggerZones$ Battlefield | Execute$ TrigPump | IsPresent$ Enchantment.YouCtrl | PresentCompare$ GE5 | TriggerDescription$ At the beginning of each combat, if you control five or more enchantments, CARDNAME gets +4/+4 and gains lifelink until end of turn. (Damage dealt by this creature also causes you to gain that much life.) +SVar:TrigPump:DB$ Pump | NumAtt$ +4 | NumDef$ +4 | KW$ Lifelink | Defined$ Self +DeckHints:Type$Enchantment Oracle:Whenever you cast your first enchantment spell each turn, create a 2/2 white Nymph enchantment creature token.\nAt the beginning of each combat, if you control five or more enchantments, Psemilla, Meletian Poet gets +4/+4 and gains lifelink until end of turn. (Damage dealt by this creature also causes you to gain that much life.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/q/qala_ajanis_pridemate.txt b/forge-gui/res/cardsfolder/q/qala_ajanis_pridemate.txt index 74c3fc57d98..7542acda865 100644 --- a/forge-gui/res/cardsfolder/q/qala_ajanis_pridemate.txt +++ b/forge-gui/res/cardsfolder/q/qala_ajanis_pridemate.txt @@ -1,12 +1,12 @@ -Name:Qala, Ajani's Pridemate -ManaCost:3 W -Types:Legendary Creature Cat Warrior -PT:3/3 -T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPumpAll | TriggerDescription$ Whenever CARDNAME attacks, other attacking creatures you control get +X/+0 until end of turn, where X is the number of counters on NICKNAME. -SVar:TrigPumpAll:DB$ PumpAll | ValidCards$ Creature.attacking+Other | NumAtt$ X -T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you gain life, put a +1/+1 counter on NICKNAME. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 -A:AB$ GainLife | Cost$ 3 W | LifeAmount$ 1 | SpellDescription$ You gain 1 life. -SVar:X:Count$CardCounters.ALL -DeckHas:Ability$Counters & LifeGain +Name:Qala, Ajani's Pridemate +ManaCost:3 W +Types:Legendary Creature Cat Warrior +PT:3/3 +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPumpAll | TriggerDescription$ Whenever CARDNAME attacks, other attacking creatures you control get +X/+0 until end of turn, where X is the number of counters on NICKNAME. +SVar:TrigPumpAll:DB$ PumpAll | ValidCards$ Creature.attacking+Other | NumAtt$ X +T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever you gain life, put a +1/+1 counter on NICKNAME. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +A:AB$ GainLife | Cost$ 3 W | LifeAmount$ 1 | SpellDescription$ You gain 1 life. +SVar:X:Count$CardCounters.ALL +DeckHas:Ability$Counters & LifeGain Oracle:Whenever Qala, Ajani's Pridemate attacks, other attacking creatures you control get +X/+0 until end of turn, where X is the number of counters on Qala.\nWhenever you gain life, put a +1/+1 counter on Qala.\n{3}{W}: You gain 1 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/q/quakestrider_ceratops.txt b/forge-gui/res/cardsfolder/q/quakestrider_ceratops.txt index d55dea7debd..bcdf0584277 100644 --- a/forge-gui/res/cardsfolder/q/quakestrider_ceratops.txt +++ b/forge-gui/res/cardsfolder/q/quakestrider_ceratops.txt @@ -1,5 +1,5 @@ -Name:Quakestrider Ceratops -ManaCost:3 G G G -Types:Creature Dinosaur -PT:12/8 +Name:Quakestrider Ceratops +ManaCost:3 G G G +Types:Creature Dinosaur +PT:12/8 Oracle: \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/q/quick_draw_katana.txt b/forge-gui/res/cardsfolder/q/quick_draw_katana.txt index 34a6d3532ac..875196972a2 100644 --- a/forge-gui/res/cardsfolder/q/quick_draw_katana.txt +++ b/forge-gui/res/cardsfolder/q/quick_draw_katana.txt @@ -1,6 +1,6 @@ -Name:Quick-Draw Katana -ManaCost:2 -Types:Artifact Equipment -S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ During your turn, equipped creature gets +2/+0 and has first strike. (It deals combat damage before creatures without first strike.) -K:Equip:2 +Name:Quick-Draw Katana +ManaCost:2 +Types:Artifact Equipment +S:Mode$ Continuous | Affected$ Creature.EquippedBy | AddPower$ 2 | AddKeyword$ First Strike | Condition$ PlayerTurn | Description$ During your turn, equipped creature gets +2/+0 and has first strike. (It deals combat damage before creatures without first strike.) +K:Equip:2 Oracle:During your turn, equipped creature gets +2/+0 and has first strike. (It deals combat damage before creatures without first strike.)\nEquip {2} ({2}: Attach to target creature you control. Equip only as a sorcery.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/q/quilled_greatwurm.txt b/forge-gui/res/cardsfolder/q/quilled_greatwurm.txt index a60cc1a3de8..e594b1496ef 100644 --- a/forge-gui/res/cardsfolder/q/quilled_greatwurm.txt +++ b/forge-gui/res/cardsfolder/q/quilled_greatwurm.txt @@ -1,10 +1,10 @@ -Name:Quilled Greatwurm -ManaCost:4 G G -Types:Creature Wurm -PT:7/7 -K:Trample -T:Mode$ DamageDealtOnce | CombatDamage$ True | PlayerTurn$ True | TriggerZones$ Battlefield | ValidSource$ Creature.YouCtrl | Execute$ TrigPutCounter | TriggerDescription$ Whenever a creature you control deals combat damage during your turn, put that many +1/+1 counters on it. (It must survive to get the counters.) -SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredSourceLKICopy | CounterType$ P1P1 | CounterNum$ X -S:Mode$ Continuous | Affected$ Card.Self | MayPlay$ True | AffectedZone$ Graveyard | EffectZone$ Graveyard | RaiseCost$ RemoveAnyCounter<6/Any/Creature.YouCtrl/among creatures you control> | Description$ You may cast this card from your graveyard by removing six counters from among creatures you control in addition to paying its other costs. -SVar:X:TriggerCount$DamageAmount +Name:Quilled Greatwurm +ManaCost:4 G G +Types:Creature Wurm +PT:7/7 +K:Trample +T:Mode$ DamageDealtOnce | CombatDamage$ True | PlayerTurn$ True | TriggerZones$ Battlefield | ValidSource$ Creature.YouCtrl | Execute$ TrigPutCounter | TriggerDescription$ Whenever a creature you control deals combat damage during your turn, put that many +1/+1 counters on it. (It must survive to get the counters.) +SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredSourceLKICopy | CounterType$ P1P1 | CounterNum$ X +S:Mode$ Continuous | Affected$ Card.Self | MayPlay$ True | AffectedZone$ Graveyard | EffectZone$ Graveyard | RaiseCost$ RemoveAnyCounter<6/Any/Creature.YouCtrl/among creatures you control> | Description$ You may cast this card from your graveyard by removing six counters from among creatures you control in addition to paying its other costs. +SVar:X:TriggerCount$DamageAmount Oracle:Trample\nWhenever a creature you control deals combat damage during your turn, put that many +1/+1 counters on it. (It must survive to get the counters.)\nYou may cast this card from your graveyard by removing six counters from among creatures you control in addition to paying its other costs. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/raise_the_past.txt b/forge-gui/res/cardsfolder/r/raise_the_past.txt index ad1058578c6..b24b63230bb 100644 --- a/forge-gui/res/cardsfolder/r/raise_the_past.txt +++ b/forge-gui/res/cardsfolder/r/raise_the_past.txt @@ -1,6 +1,6 @@ -Name:Raise the Past -ManaCost:2 W W -Types:Sorcery -A:SP$ ChangeZoneAll | ChangeType$ Creature.YouOwn+cmcLE2 | Origin$ Graveyard | Destination$ Battlefield | SpellDescription$ Return all creature cards with mana value 2 or less from your graveyard to the battlefield. -DeckHas:Ability$Graveyard +Name:Raise the Past +ManaCost:2 W W +Types:Sorcery +A:SP$ ChangeZoneAll | ChangeType$ Creature.YouOwn+cmcLE2 | Origin$ Graveyard | Destination$ Battlefield | SpellDescription$ Return all creature cards with mana value 2 or less from your graveyard to the battlefield. +DeckHas:Ability$Graveyard Oracle:Return all creature cards with mana value 2 or less from your graveyard to the battlefield. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/ravenous_amulet.txt b/forge-gui/res/cardsfolder/r/ravenous_amulet.txt index 82a79fd8a97..1332d2ee5dc 100644 --- a/forge-gui/res/cardsfolder/r/ravenous_amulet.txt +++ b/forge-gui/res/cardsfolder/r/ravenous_amulet.txt @@ -1,8 +1,8 @@ -Name:Ravenous Amulet -ManaCost:2 -Types:Artifact -A:AB$ Draw | Cost$ 2 T Sac<1/Creature> | SubAbility$ DBPutCounter | SorcerySpeed$ True | SpellDescription$ Draw a card and put a soul counter on this artifact. Activate only as a sorcery. -SVar:DBPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ SOUL | CounterNum$ 1 -A:AB$ LoseLife | Cost$ 4 T Sac<1/CARDNAME> | Defined$ Player.Opponent | LifeAmount$ X | SpellDescription$ Each opponent loses life equal to the number of soul counters on this artifact. -SVar:X:Count$CardCounters.SOUL +Name:Ravenous Amulet +ManaCost:2 +Types:Artifact +A:AB$ Draw | Cost$ 2 T Sac<1/Creature> | SubAbility$ DBPutCounter | SorcerySpeed$ True | SpellDescription$ Draw a card and put a soul counter on this artifact. Activate only as a sorcery. +SVar:DBPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ SOUL | CounterNum$ 1 +A:AB$ LoseLife | Cost$ 4 T Sac<1/CARDNAME> | Defined$ Player.Opponent | LifeAmount$ X | SpellDescription$ Each opponent loses life equal to the number of soul counters on this artifact. +SVar:X:Count$CardCounters.SOUL Oracle:{1}, {T}, Sacrifice a creature: Draw a card and put a soul counter on this artifact. Activate only as a sorcery.\n{4}, {T}, Sacrifice this artifact: Each opponent loses life equal to the number of soul counters on this artifact. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/razorgrass_invoker.txt b/forge-gui/res/cardsfolder/r/razorgrass_invoker.txt index eacd3d24260..d5e192969a8 100644 --- a/forge-gui/res/cardsfolder/r/razorgrass_invoker.txt +++ b/forge-gui/res/cardsfolder/r/razorgrass_invoker.txt @@ -1,7 +1,7 @@ -Name:Razorgrass Invoker -ManaCost:3 G -Types:Creature Elf Scout -PT:4/3 -K:Vigilance -A:AB$ Pump | Cost$ 8 | Defined$ Self | ValidTgts$ Creature.Other | TgtPrompt$ Select another target creature | TargetMin$ 0 | TargetMax$ 1 | ThisDefinedAndTgts$ Self | NumAtt$ +3 | NumDef$ +3 | SpellDescription$ CARDNAME and up to one other target creature each get +3/+3 until end of turn. +Name:Razorgrass Invoker +ManaCost:3 G +Types:Creature Elf Scout +PT:4/3 +K:Vigilance +A:AB$ Pump | Cost$ 8 | Defined$ Self | ValidTgts$ Creature.Other | TgtPrompt$ Select another target creature | TargetMin$ 0 | TargetMax$ 1 | ThisDefinedAndTgts$ Self | NumAtt$ +3 | NumDef$ +3 | SpellDescription$ CARDNAME and up to one other target creature each get +3/+3 until end of turn. Oracle:Vigilance (Attacking doesn't cause this creature to tap.)\n{8}: Razorgrass Invoker and up to one other target creature each get +3/+3 until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/refute.txt b/forge-gui/res/cardsfolder/r/refute.txt index 9621bbd0b3b..1f2f6bc9184 100644 --- a/forge-gui/res/cardsfolder/r/refute.txt +++ b/forge-gui/res/cardsfolder/r/refute.txt @@ -1,7 +1,7 @@ -Name:Refute -ManaCost:1 U U -Types:Instant -A:SP$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | SubAbility$ DBDraw | SpellDescription$ Counter target spell. Draw a card, then discard a card. -SVar:DBDraw:DB$ Draw | SubAbility$ DBDiscard -SVar:DBDiscard:DB$ Discard | Defined$ You | Mode$ TgtChoose | NumCards$ 1 +Name:Refute +ManaCost:1 U U +Types:Instant +A:SP$ Counter | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | SubAbility$ DBDraw | SpellDescription$ Counter target spell. Draw a card, then discard a card. +SVar:DBDraw:DB$ Draw | SubAbility$ DBDiscard +SVar:DBDiscard:DB$ Discard | Defined$ You | Mode$ TgtChoose | NumCards$ 1 Oracle:Counter target spell. Draw a card, then discard a card. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/rev_tithe_extractor.txt b/forge-gui/res/cardsfolder/r/rev_tithe_extractor.txt index 84c6ef911e7..3dd2fe8eda8 100644 --- a/forge-gui/res/cardsfolder/r/rev_tithe_extractor.txt +++ b/forge-gui/res/cardsfolder/r/rev_tithe_extractor.txt @@ -1,15 +1,15 @@ -Name:Rev, Tithe Extractor -ManaCost:3 B -Types:Legendary Creature Human Rogue -PT:3/3 -T:Mode$ AttackersDeclared | AttackingPlayer$ You | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever you attack, target creature gains deathtouch until end of turn. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ Deathtouch -T:Mode$ DamageDoneOnce | ValidSource$ Creature.YouCtrl | TriggerZones$ Battlefield | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever one or more creatures you control deal combat damage to a player, create a Treasure token, then look at the top card of that player's library and exile it face down. You may cast that card for as long as it remains exiled. (A Treasure token is an artifact with "{T}, Sacrifice this artifact: Add one mana of any color.") -SVar:TrigToken:DB$ Token | TokenScript$ c_a_treasure_sac | SubAbility$ DBDig -SVar:DBDig:DB$ Dig | DigNum$ 1 | Defined$ TriggeredTarget | ForceRevealToController$ True | ChangeNum$ All | DestinationZone$ Exile | ExileFaceDown$ True | RememberChanged$ True | SubAbility$ DBEffect -SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ STPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile | Duration$ Permanent -SVar:STPlay:Mode$ Continuous | MayLookAt$ You | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Description$ You may cast that card for as long as it remains exiled. -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -SVar:PlayMain1:TRUE -DeckHints:Ability$Token -Oracle:Whenever you attack, target creature gains deathtouch until end of turn.\nWhenever one or more creatures you control deal combat damage to a player, create a Treasure token, then look at the top card of that player's library and exile it face down. You may cast that card for as long as it remains exiled. (A Treasure token is an artifact with "{T}, Sacrifice this artifact: Add one mana of any color.") +Name:Rev, Tithe Extractor +ManaCost:3 B +Types:Legendary Creature Human Rogue +PT:3/3 +T:Mode$ AttackersDeclared | AttackingPlayer$ You | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever you attack, target creature gains deathtouch until end of turn. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ Deathtouch +T:Mode$ DamageDoneOnce | ValidSource$ Creature.YouCtrl | TriggerZones$ Battlefield | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever one or more creatures you control deal combat damage to a player, create a Treasure token, then look at the top card of that player's library and exile it face down. You may cast that card for as long as it remains exiled. (A Treasure token is an artifact with "{T}, Sacrifice this artifact: Add one mana of any color.") +SVar:TrigToken:DB$ Token | TokenScript$ c_a_treasure_sac | SubAbility$ DBDig +SVar:DBDig:DB$ Dig | DigNum$ 1 | Defined$ TriggeredTarget | ForceRevealToController$ True | ChangeNum$ All | DestinationZone$ Exile | ExileFaceDown$ True | RememberChanged$ True | SubAbility$ DBEffect +SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ STPlay | SubAbility$ DBCleanup | ForgetOnMoved$ Exile | Duration$ Permanent +SVar:STPlay:Mode$ Continuous | MayLookAt$ You | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Description$ You may cast that card for as long as it remains exiled. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:PlayMain1:TRUE +DeckHints:Ability$Token +Oracle:Whenever you attack, target creature gains deathtouch until end of turn.\nWhenever one or more creatures you control deal combat damage to a player, create a Treasure token, then look at the top card of that player's library and exile it face down. You may cast that card for as long as it remains exiled. (A Treasure token is an artifact with "{T}, Sacrifice this artifact: Add one mana of any color.") diff --git a/forge-gui/res/cardsfolder/r/revenge_of_the_rats.txt b/forge-gui/res/cardsfolder/r/revenge_of_the_rats.txt index 42da67f3dbd..31b6d6b1a86 100644 --- a/forge-gui/res/cardsfolder/r/revenge_of_the_rats.txt +++ b/forge-gui/res/cardsfolder/r/revenge_of_the_rats.txt @@ -1,7 +1,7 @@ -Name:Revenge of the Rats -ManaCost:2 B B -Types:Sorcery -K:Flashback:2 B B -A:SP$ Token | TokenAmount$ X | TokenScript$ b_1_1_rat | TokenOwner$ You | TokenTapped$ True | SpellDescription$ Create a tapped 1/1 black Rat creature token for each creature card in your graveyard. -SVar:X:Count$TypeInYourYard.Creature +Name:Revenge of the Rats +ManaCost:2 B B +Types:Sorcery +K:Flashback:2 B B +A:SP$ Token | TokenAmount$ X | TokenScript$ b_1_1_rat | TokenOwner$ You | TokenTapped$ True | SpellDescription$ Create a tapped 1/1 black Rat creature token for each creature card in your graveyard. +SVar:X:Count$TypeInYourYard.Creature Oracle:Create a tapped 1/1 black Rat creature token for each creature card in your graveyard.\nFlashback {2}{B}{B} (You may cast this card from your graveyard for its flashback cost. Then exile it.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/revoke_demise.txt b/forge-gui/res/cardsfolder/r/revoke_demise.txt index 13c720ca062..0fd5620d37c 100644 --- a/forge-gui/res/cardsfolder/r/revoke_demise.txt +++ b/forge-gui/res/cardsfolder/r/revoke_demise.txt @@ -1,8 +1,8 @@ -Name:Revoke Demise -ManaCost:4 B -Types:Sorcery -S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 2 | ValidTarget$ Creature.cmcLE3 | EffectZone$ All | Description$ This spell costs {2} less to cast if it targets a creature card with mana value 3 or less. -A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouOwn | SubAbility$ DBGainLife | SpellDescription$ Return target creature card from your graveyard to the battlefield. You gain 2 life. -SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 -DeckHas:Ability$Graveyard +Name:Revoke Demise +ManaCost:4 B +Types:Sorcery +S:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 2 | ValidTarget$ Creature.cmcLE3 | EffectZone$ All | Description$ This spell costs {2} less to cast if it targets a creature card with mana value 3 or less. +A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouOwn | SubAbility$ DBGainLife | SpellDescription$ Return target creature card from your graveyard to the battlefield. You gain 2 life. +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 +DeckHas:Ability$Graveyard Oracle:This spell costs {2} less to cast if it targets a creature card with mana value 3 or less.\nReturn target creature card from your graveyard to the battlefield. You gain 2 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/ripchain_razorkin.txt b/forge-gui/res/cardsfolder/r/ripchain_razorkin.txt index 5e3b213c392..1b34a1765d7 100644 --- a/forge-gui/res/cardsfolder/r/ripchain_razorkin.txt +++ b/forge-gui/res/cardsfolder/r/ripchain_razorkin.txt @@ -1,7 +1,7 @@ -Name:Ripchain Razorkin -ManaCost:3 R -Types:Creature Human Berserker -PT:5/3 -K:Reach -A:AB$ Draw | Cost$ 2 R Sac<1/Land> | SpellDescription$ Draw a card. +Name:Ripchain Razorkin +ManaCost:3 R +Types:Creature Human Berserker +PT:5/3 +K:Reach +A:AB$ Draw | Cost$ 2 R Sac<1/Land> | SpellDescription$ Draw a card. Oracle:Reach\n{2}{R}, Sacrifice a land: Draw a card. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/rite_of_the_dragoncaller.txt b/forge-gui/res/cardsfolder/r/rite_of_the_dragoncaller.txt index 0e6fd672626..f8cb45fc52f 100644 --- a/forge-gui/res/cardsfolder/r/rite_of_the_dragoncaller.txt +++ b/forge-gui/res/cardsfolder/r/rite_of_the_dragoncaller.txt @@ -1,8 +1,8 @@ -Name:Rite of the Dragoncaller -ManaCost:4 R R -Types:Enchantment -T:Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever you cast an instant or sorcery spell, create a 5/5 red Dragon creature token with flying. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_5_5_dragon_flying | TokenOwner$ You -DeckHas:Ability$Token -DeckNeeds:Type$Instant|Sorcery +Name:Rite of the Dragoncaller +ManaCost:4 R R +Types:Enchantment +T:Mode$ SpellCast | ValidCard$ Instant,Sorcery | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever you cast an instant or sorcery spell, create a 5/5 red Dragon creature token with flying. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_5_5_dragon_flying | TokenOwner$ You +DeckHas:Ability$Token +DeckNeeds:Type$Instant|Sorcery Oracle:Whenever you cast an instant or sorcery spell, create a 5/5 red Dragon creature token with flying. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/r/rune_sealed_wall.txt b/forge-gui/res/cardsfolder/r/rune_sealed_wall.txt index a8c51611bbc..32b981cc2f7 100644 --- a/forge-gui/res/cardsfolder/r/rune_sealed_wall.txt +++ b/forge-gui/res/cardsfolder/r/rune_sealed_wall.txt @@ -1,7 +1,7 @@ -Name:Rune-Sealed Wall -ManaCost:2 U -Types:Artifact Creature Wall -PT:0/6 -K:Defender -A:AB$ Surveil | Cost$ T | Amount$ 1 | SpellDescription$ Surveil 1. (Look at the top card of your library. You may put that card into your graveyard.) -Oracle:Defender\n{T}: Surveil 1. (Look at the top card of your library. You may put that card into your graveyard.) +Name:Rune-Sealed Wall +ManaCost:2 U +Types:Artifact Creature Wall +PT:0/6 +K:Defender +A:AB$ Surveil | Cost$ T | Amount$ 1 | SpellDescription$ Surveil 1. (Look at the top card of your library. You may put that card into your graveyard.) +Oracle:Defender\n{T}: Surveil 1. (Look at the top card of your library. You may put that card into your graveyard.) diff --git a/forge-gui/res/cardsfolder/r/running_is_useless.txt b/forge-gui/res/cardsfolder/r/running_is_useless.txt index 6294deb7009..2dde8ba8e74 100644 --- a/forge-gui/res/cardsfolder/r/running_is_useless.txt +++ b/forge-gui/res/cardsfolder/r/running_is_useless.txt @@ -1,7 +1,7 @@ -Name:Running Is Useless -ManaCost:no cost -Types:Scheme -T:Mode$ SetInMotion | ValidCard$ Card.Self | Execute$ DBDestroy | TriggerZones$ Command | TriggerDescription$ When you set this scheme in motion, choose any number of creatures with different mana values. Destroy those creatures. -SVar:DBDestroy:DB$ Destroy | ValidTgts$ Creature | TargetsWithDifferentCMC$ True | TargetMax$ AmountToChoose | TargetMin$ 0 | TgtPrompt$ Choose any number of creatures with different mana values. -SVar:AmountToChoose:Count$Valid Creature -Oracle:When you set this scheme in motion, choose any number of creatures with different mana values. Destroy those creatures. +Name:Running Is Useless +ManaCost:no cost +Types:Scheme +T:Mode$ SetInMotion | ValidCard$ Card.Self | Execute$ DBDestroy | TriggerZones$ Command | TriggerDescription$ When you set this scheme in motion, choose any number of creatures with different mana values. Destroy those creatures. +SVar:DBDestroy:DB$ Destroy | ValidTgts$ Creature | TargetsWithDifferentCMC$ True | TargetMax$ AmountToChoose | TargetMin$ 0 | TgtPrompt$ Choose any number of creatures with different mana values. +SVar:AmountToChoose:Count$Valid Creature +Oracle:When you set this scheme in motion, choose any number of creatures with different mana values. Destroy those creatures. diff --git a/forge-gui/res/cardsfolder/s/sandstorm_crasher.txt b/forge-gui/res/cardsfolder/s/sandstorm_crasher.txt index db6e51c1d9e..a24a252972e 100644 --- a/forge-gui/res/cardsfolder/s/sandstorm_crasher.txt +++ b/forge-gui/res/cardsfolder/s/sandstorm_crasher.txt @@ -1,8 +1,8 @@ -Name:Sandstorm Crasher -ManaCost:3 R -Types:Creature Minotaur Berserker Wizard -PT:3/4 -K:Trample -S:Mode$ OptionalAttackCost | ValidCard$ Card.Self | Trigger$ TrigCopy | Cost$ Exert<1/CARDNAME> | Description$ You may exert CARDNAME as it attacks. When you do, create a tapped and attacking token that's a copy of target creature you control. Sacrifice the token at the beginning of the next end step. (An exerted creature won't untap during your next untap step.) -SVar:TrigCopy:DB$ CopyPermanent | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumCopies$ 1 | AtEOT$ Sacrifice | TokenTapped$ True | TokenAttacking$ True | SpellDescription$ When you do, create a tapped and attacking token that's a copy of target creature you control. Sacrifice the token at the beginning of the next end step. -Oracle:Trample\nYou may exert Sandstorm Crasher as it attacks. When you do, create a tapped and attacking token that's a copy of target creature you control. Sacrifice the token at the beginning of the next end step. (An exerted creature won't untap during your next untap step.) +Name:Sandstorm Crasher +ManaCost:3 R +Types:Creature Minotaur Berserker Wizard +PT:3/4 +K:Trample +S:Mode$ OptionalAttackCost | ValidCard$ Card.Self | Trigger$ TrigCopy | Cost$ Exert<1/CARDNAME> | Description$ You may exert CARDNAME as it attacks. When you do, create a tapped and attacking token that's a copy of target creature you control. Sacrifice the token at the beginning of the next end step. (An exerted creature won't untap during your next untap step.) +SVar:TrigCopy:DB$ CopyPermanent | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumCopies$ 1 | AtEOT$ Sacrifice | TokenTapped$ True | TokenAttacking$ True | SpellDescription$ When you do, create a tapped and attacking token that's a copy of target creature you control. Sacrifice the token at the beginning of the next end step. +Oracle:Trample\nYou may exert Sandstorm Crasher as it attacks. When you do, create a tapped and attacking token that's a copy of target creature you control. Sacrifice the token at the beginning of the next end step. (An exerted creature won't untap during your next untap step.) diff --git a/forge-gui/res/cardsfolder/s/sanguine_syphoner.txt b/forge-gui/res/cardsfolder/s/sanguine_syphoner.txt index 502f3fe47ba..752318b40e4 100644 --- a/forge-gui/res/cardsfolder/s/sanguine_syphoner.txt +++ b/forge-gui/res/cardsfolder/s/sanguine_syphoner.txt @@ -1,10 +1,10 @@ -Name:Sanguine Syphoner -ManaCost:1 B -Types:Creature Vampire Warlock -PT:1/3 -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigLoseLife | TriggerDescription$ Whenever this creature attacks, each opponent loses 1 life and you gain 1 life. -SVar:TrigLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 1 | SubAbility$ DBGainLife -SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1 -SVar:HasAttackEffect:TRUE -DeckHas:Ability$LifeGain +Name:Sanguine Syphoner +ManaCost:1 B +Types:Creature Vampire Warlock +PT:1/3 +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigLoseLife | TriggerDescription$ Whenever this creature attacks, each opponent loses 1 life and you gain 1 life. +SVar:TrigLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 1 | SubAbility$ DBGainLife +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 1 +SVar:HasAttackEffect:TRUE +DeckHas:Ability$LifeGain Oracle:Whenever this creature attacks, each opponent loses 1 life and you gain 1 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/saurian_symbiote.txt b/forge-gui/res/cardsfolder/s/saurian_symbiote.txt index f5e4f512f21..fcedf13de70 100644 --- a/forge-gui/res/cardsfolder/s/saurian_symbiote.txt +++ b/forge-gui/res/cardsfolder/s/saurian_symbiote.txt @@ -1,11 +1,11 @@ -Name:Saurian Symbiote -ManaCost:3 G -Types:Creature Fungus Dinosaur -PT:2/3 -K:Reach -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigCharm | TriggerDescription$ When CARDNAME enters, ABILITY -SVar:TrigCharm:DB$ Charm | Choices$ DBCounter,DBToken -SVar:DBCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on CARDNAME. -SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_saproling | TokenOwner$ You | SpellDescription$ Create a 1/1 green Saproling creature token. -DeckHas:Ability$Counters|Token +Name:Saurian Symbiote +ManaCost:3 G +Types:Creature Fungus Dinosaur +PT:2/3 +K:Reach +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigCharm | TriggerDescription$ When CARDNAME enters, ABILITY +SVar:TrigCharm:DB$ Charm | Choices$ DBCounter,DBToken +SVar:DBCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on CARDNAME. +SVar:DBToken:DB$ Token | TokenAmount$ 1 | TokenScript$ g_1_1_saproling | TokenOwner$ You | SpellDescription$ Create a 1/1 green Saproling creature token. +DeckHas:Ability$Counters|Token Oracle:Reach (This creature can block creatures with flying.)\nWhen Saurian Symbiote enters, choose one —\n• Put a +1/+1 counter on Saurian Symbiote.\n• Create a 1/1 green Saproling creature token. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/scholar_of_combustion.txt b/forge-gui/res/cardsfolder/s/scholar_of_combustion.txt index f56e7e1dc10..b753c2de68b 100644 --- a/forge-gui/res/cardsfolder/s/scholar_of_combustion.txt +++ b/forge-gui/res/cardsfolder/s/scholar_of_combustion.txt @@ -1,11 +1,11 @@ -Name:Scholar of Combustion -ManaCost:3 R -Types:Creature Human Wizard -PT:3/2 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When CARDNAME enters, exile up to one target instant or sorcery card from your graveyard. You may cast that card until the end of your next turn. (You still pay its costs. Timing rules still apply.) -SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Instant.YouOwn,Sorcery.YouOwn | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target instant or sorcery card in your graveyard. | RememberChanged$ True | SubAbility$ DBEffect -SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn | SubAbility$ DBCleanup -SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Description$ You may cast that card until the end of your next turn. -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -DeckNeeds:Type$Instant|Sorcery +Name:Scholar of Combustion +ManaCost:3 R +Types:Creature Human Wizard +PT:3/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When CARDNAME enters, exile up to one target instant or sorcery card from your graveyard. You may cast that card until the end of your next turn. (You still pay its costs. Timing rules still apply.) +SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Instant.YouOwn,Sorcery.YouOwn | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select target instant or sorcery card in your graveyard. | RememberChanged$ True | SubAbility$ DBEffect +SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | StaticAbilities$ Play | ForgetOnMoved$ Exile | Duration$ UntilTheEndOfYourNextTurn | SubAbility$ DBCleanup +SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Description$ You may cast that card until the end of your next turn. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +DeckNeeds:Type$Instant|Sorcery Oracle:When Scholar of Combustion enters, exile up to one target instant or sorcery card from your graveyard. You may cast that card until the end of your next turn. (You still pay its costs. Timing rules still apply.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/scourge_of_the_undercity.txt b/forge-gui/res/cardsfolder/s/scourge_of_the_undercity.txt index fe4651b47b5..0f0476629df 100644 --- a/forge-gui/res/cardsfolder/s/scourge_of_the_undercity.txt +++ b/forge-gui/res/cardsfolder/s/scourge_of_the_undercity.txt @@ -1,9 +1,9 @@ -Name:Scourge of the Undercity -ManaCost:1 B -Types:Creature Gorgon Vampire -PT:2/1 -K:Lifelink -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ When CARDNAME enters, another target creature you control gains lifelink until end of turn. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.Other+YouCtrl | TgtPrompt$ Select another target creature you control | KW$ Lifelink -SVar:PlayMain1:TRUE -Oracle:Lifelink (Damage dealt by this creature also causes you to gain that much life.)\nWhen Scourge of the Undercity enters, another target creature you control gains lifelink until end of turn. +Name:Scourge of the Undercity +ManaCost:1 B +Types:Creature Gorgon Vampire +PT:2/1 +K:Lifelink +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ When CARDNAME enters, another target creature you control gains lifelink until end of turn. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.Other+YouCtrl | TgtPrompt$ Select another target creature you control | KW$ Lifelink +SVar:PlayMain1:TRUE +Oracle:Lifelink (Damage dealt by this creature also causes you to gain that much life.)\nWhen Scourge of the Undercity enters, another target creature you control gains lifelink until end of turn. diff --git a/forge-gui/res/cardsfolder/s/scrawling_crawler.txt b/forge-gui/res/cardsfolder/s/scrawling_crawler.txt index a6e06296dea..d0ca029c69f 100644 --- a/forge-gui/res/cardsfolder/s/scrawling_crawler.txt +++ b/forge-gui/res/cardsfolder/s/scrawling_crawler.txt @@ -1,9 +1,9 @@ -Name:Scrawling Crawler -ManaCost:3 -Types:Artifact Creature Phyrexian Construct -PT:3/2 -T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ At the beginning of your upkeep, each player draws a card. -SVar:TrigDraw:DB$ Draw | Defined$ Player | NumCards$ 1 -T:Mode$ Drawn | ValidCard$ Card.OppOwn | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever an opponent draws a card, that player loses 1 life. -SVar:TrigLoseLife:DB$ LoseLife | Defined$ TriggeredCardController | LifeAmount$ 1 +Name:Scrawling Crawler +ManaCost:3 +Types:Artifact Creature Phyrexian Construct +PT:3/2 +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ At the beginning of your upkeep, each player draws a card. +SVar:TrigDraw:DB$ Draw | Defined$ Player | NumCards$ 1 +T:Mode$ Drawn | ValidCard$ Card.OppOwn | TriggerZones$ Battlefield | Execute$ TrigLoseLife | TriggerDescription$ Whenever an opponent draws a card, that player loses 1 life. +SVar:TrigLoseLife:DB$ LoseLife | Defined$ TriggeredCardController | LifeAmount$ 1 Oracle:At the beginning of your upkeep, each player draws a card.\nWhenever an opponent draws a card, that player loses 1 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/scythecat_cub.txt b/forge-gui/res/cardsfolder/s/scythecat_cub.txt index 89f8f195b48..fcd0cdc3e9b 100644 --- a/forge-gui/res/cardsfolder/s/scythecat_cub.txt +++ b/forge-gui/res/cardsfolder/s/scythecat_cub.txt @@ -1,12 +1,12 @@ -Name:Scythecat Cub -ManaCost:1 G -Types:Creature Cat -PT:2/2 -K:Trample -T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Landfall — Whenever a land you control enters, put a +1/+1 counter on target creature you control. If this is the second time this ability has resolved this turn, double the number of +1/+1 counters on that creature instead. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | SubAbility$ DBPutCounter -SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ P1P1 | CounterNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ NE2 | SubAbility$ DBMultiplyCounter -SVar:DBMultiplyCounter:DB$ MultiplyCounter | Defined$ Targeted | CounterType$ P1P1 | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ2 -SVar:X:Count$ResolvedThisTurn -DeckHints:Ability$Counters +Name:Scythecat Cub +ManaCost:1 G +Types:Creature Cat +PT:2/2 +K:Trample +T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Landfall — Whenever a land you control enters, put a +1/+1 counter on target creature you control. If this is the second time this ability has resolved this turn, double the number of +1/+1 counters on that creature instead. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | SubAbility$ DBPutCounter +SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ P1P1 | CounterNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ NE2 | SubAbility$ DBMultiplyCounter +SVar:DBMultiplyCounter:DB$ MultiplyCounter | Defined$ Targeted | CounterType$ P1P1 | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ2 +SVar:X:Count$ResolvedThisTurn +DeckHints:Ability$Counters Oracle:Trample\nLandfall — Whenever a land you control enters, put a +1/+1 counter on target creature you control. If this is the second time this ability has resolved this turn, double the number of +1/+1 counters on that creature instead. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/searslicer_goblin.txt b/forge-gui/res/cardsfolder/s/searslicer_goblin.txt index 22d8195f758..17fabfe1176 100644 --- a/forge-gui/res/cardsfolder/s/searslicer_goblin.txt +++ b/forge-gui/res/cardsfolder/s/searslicer_goblin.txt @@ -1,8 +1,8 @@ -Name:Searslicer Goblin -ManaCost:1 R -Types:Creature Goblin Warrior -PT:2/1 -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ RaidTest | Execute$ TrigToken| TriggerDescription$ Raid — At the beginning of your end step, if you attacked this turn, create a 1/1 red Goblin creature token. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_goblin | TokenOwner$ You -SVar:RaidTest:Count$AttackersDeclared +Name:Searslicer Goblin +ManaCost:1 R +Types:Creature Goblin Warrior +PT:2/1 +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | CheckSVar$ RaidTest | Execute$ TrigToken| TriggerDescription$ Raid — At the beginning of your end step, if you attacked this turn, create a 1/1 red Goblin creature token. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ r_1_1_goblin | TokenOwner$ You +SVar:RaidTest:Count$AttackersDeclared Oracle:Raid — At the beginning of your end step, if you attacked this turn, create a 1/1 red Goblin creature token. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/secret_arcade_dusty_parlor.txt b/forge-gui/res/cardsfolder/s/secret_arcade_dusty_parlor.txt index 63aac3a38e4..a8a05c3b342 100644 --- a/forge-gui/res/cardsfolder/s/secret_arcade_dusty_parlor.txt +++ b/forge-gui/res/cardsfolder/s/secret_arcade_dusty_parlor.txt @@ -1,16 +1,16 @@ -Name:Secret Arcade -ManaCost:4 W -Types:Enchantment Room -S:Mode$ Continuous | Affected$ Permanent.nonLand+YouCtrl | AffectedZone$ Battlefield,Stack | AddType$ Enchantment | Description$ Nonland permanents you control and permanent spells you control are enchantments in addition to their other types. -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nNonland permanents you control and permanent spells you control are enchantments in addition to their other types. - -ALTERNATE - -Name:Dusty Parlor -ManaCost:2 W -Types:Enchantment Room -T:Mode$ SpellCast | ValidCard$ Enchantment | ValidActivatingPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast an enchantment spell, put a number of +1/+1 counters equal to that spell's mana value on up to one target creature. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select up to one target creature | TargetMin$ 0 | TargetMax$ 1 | CounterType$ P1P1 | CounterNum$ X -SVar:X:TriggeredStackInstance$CardManaCostLKI +Name:Secret Arcade +ManaCost:4 W +Types:Enchantment Room +S:Mode$ Continuous | Affected$ Permanent.nonLand+YouCtrl | AffectedZone$ Battlefield,Stack | AddType$ Enchantment | Description$ Nonland permanents you control and permanent spells you control are enchantments in addition to their other types. +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nNonland permanents you control and permanent spells you control are enchantments in addition to their other types. + +ALTERNATE + +Name:Dusty Parlor +ManaCost:2 W +Types:Enchantment Room +T:Mode$ SpellCast | ValidCard$ Enchantment | ValidActivatingPlayer$ You | Execute$ TrigPutCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast an enchantment spell, put a number of +1/+1 counters equal to that spell's mana value on up to one target creature. +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select up to one target creature | TargetMin$ 0 | TargetMax$ 1 | CounterType$ P1P1 | CounterNum$ X +SVar:X:TriggeredStackInstance$CardManaCostLKI Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhenever you cast an enchantment spell, put a number of +1/+1 counters equal to that spell's mana value on up to one target creature. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/seekers_folly.txt b/forge-gui/res/cardsfolder/s/seekers_folly.txt index 4da18cf9d7b..fc2167231fe 100644 --- a/forge-gui/res/cardsfolder/s/seekers_folly.txt +++ b/forge-gui/res/cardsfolder/s/seekers_folly.txt @@ -1,8 +1,8 @@ -Name:Seeker's Folly -ManaCost:2 B -Types:Sorcery -A:SP$ Charm | Choices$ DBDiscard,DBDebuff -SVar:DBDiscard:DB$ Discard | ValidTgts$ Opponent | NumCards$ 2 | Mode$ TgtChoose | SpellDescription$ Target opponent discards two cards. -SVar:DBDebuff:DB$ PumpAll | ValidCards$ Creature.OppCtrl | NumAtt$ -1 | NumDef$ -1 | IsCurse$ True | SpellDescription$ Creatures your opponents control get -1/-1 until end of turn. -DeckHas:Ability$Discard +Name:Seeker's Folly +ManaCost:2 B +Types:Sorcery +A:SP$ Charm | Choices$ DBDiscard,DBDebuff +SVar:DBDiscard:DB$ Discard | ValidTgts$ Opponent | NumCards$ 2 | Mode$ TgtChoose | SpellDescription$ Target opponent discards two cards. +SVar:DBDebuff:DB$ PumpAll | ValidCards$ Creature.OppCtrl | NumAtt$ -1 | NumDef$ -1 | IsCurse$ True | SpellDescription$ Creatures your opponents control get -1/-1 until end of turn. +DeckHas:Ability$Discard Oracle:Choose one —\n• Target opponent discards two cards.\n• Creatures your opponents control get -1/-1 until end of turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/shardless_outlander.txt b/forge-gui/res/cardsfolder/s/shardless_outlander.txt index 3002e77949d..676006cb8ad 100644 --- a/forge-gui/res/cardsfolder/s/shardless_outlander.txt +++ b/forge-gui/res/cardsfolder/s/shardless_outlander.txt @@ -1,7 +1,7 @@ -Name:Shardless Outlander -ManaCost:7 -Types:Artifact Creature Construct Scout -PT:6/5 -K:Trample -K:TypeCycling:Basic:2 +Name:Shardless Outlander +ManaCost:7 +Types:Artifact Creature Construct Scout +PT:6/5 +K:Trample +K:TypeCycling:Basic:2 Oracle:Trample (This creature can deal excess combat damage to the player or planeswalker it's attacking.)\nBasic landcycling {2} ({2}, Discard this card: Search your library for a basic land card, reveal it, put it into your hand, then shuffle.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/shroofus_sproutsire.txt b/forge-gui/res/cardsfolder/s/shroofus_sproutsire.txt index 2c857b0f27d..c9a87529680 100644 --- a/forge-gui/res/cardsfolder/s/shroofus_sproutsire.txt +++ b/forge-gui/res/cardsfolder/s/shroofus_sproutsire.txt @@ -1,12 +1,12 @@ -Name:Shroofus Sproutsire -ManaCost:2 G -Types:Legendary Creature Saproling -PT:1/1 -K:Trample -T:Mode$ DamageDone | ValidSource$ Saproling.YouCtrl | ValidTarget$ Player | TriggerZones$ Battlefield | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever a Saproling you control deals combat damage to a player, create that many 1/1 green Saproling creature tokens. -SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ g_1_1_saproling | TokenOwner$ You -SVar:PlayMain1:TRUE -SVar:X:TriggerCount$DamageAmount -DeckHints:Type$Saproling -DeckHas:Ability$Token +Name:Shroofus Sproutsire +ManaCost:2 G +Types:Legendary Creature Saproling +PT:1/1 +K:Trample +T:Mode$ DamageDone | ValidSource$ Saproling.YouCtrl | ValidTarget$ Player | TriggerZones$ Battlefield | CombatDamage$ True | Execute$ TrigToken | TriggerDescription$ Whenever a Saproling you control deals combat damage to a player, create that many 1/1 green Saproling creature tokens. +SVar:TrigToken:DB$ Token | TokenAmount$ X | TokenScript$ g_1_1_saproling | TokenOwner$ You +SVar:PlayMain1:TRUE +SVar:X:TriggerCount$DamageAmount +DeckHints:Type$Saproling +DeckHas:Ability$Token Oracle:Trample\nWhenever a Saproling you control deals combat damage to a player, create that many 1/1 green Saproling creature tokens. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/sire_of_seven_deaths.txt b/forge-gui/res/cardsfolder/s/sire_of_seven_deaths.txt index 6108abbb4e4..b04c542d76e 100644 --- a/forge-gui/res/cardsfolder/s/sire_of_seven_deaths.txt +++ b/forge-gui/res/cardsfolder/s/sire_of_seven_deaths.txt @@ -1,12 +1,12 @@ -Name:Sire of Seven Deaths -ManaCost:7 -Types:Creature Eldrazi -PT:7/7 -K:First Strike -K:Vigilance -K:Menace -K:Trample -K:Reach -K:Lifelink -K:Ward:PayLife<7> +Name:Sire of Seven Deaths +ManaCost:7 +Types:Creature Eldrazi +PT:7/7 +K:First Strike +K:Vigilance +K:Menace +K:Trample +K:Reach +K:Lifelink +K:Ward:PayLife<7> Oracle:First strike, vigilance\nMenace, trample\nReach, lifelink\nWard—Pay 7 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/skyknight_squire.txt b/forge-gui/res/cardsfolder/s/skyknight_squire.txt index a1dafae19c1..a5afa5f4c00 100644 --- a/forge-gui/res/cardsfolder/s/skyknight_squire.txt +++ b/forge-gui/res/cardsfolder/s/skyknight_squire.txt @@ -1,9 +1,9 @@ -Name:Skyknight Squire -ManaCost:1 W -Types:Creature Cat Scout -PT:1/1 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever another creature you control enters, put a +1/+1 counter on this creature. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 -S:Mode$ Continuous | Affected$ Card.Self+counters_GE3_P1P1 | AddKeyword$ Flying | AddType$ Knight | Description$ As long as this creature has three or more +1/+1 counters on it, it has flying and is a Knight in addition to its other types. -DeckHas:Ability$Counters & Keyword$Flying +Name:Skyknight Squire +ManaCost:1 W +Types:Creature Cat Scout +PT:1/1 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever another creature you control enters, put a +1/+1 counter on this creature. +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +S:Mode$ Continuous | Affected$ Card.Self+counters_GE3_P1P1 | AddKeyword$ Flying | AddType$ Knight | Description$ As long as this creature has three or more +1/+1 counters on it, it has flying and is a Knight in addition to its other types. +DeckHas:Ability$Counters & Keyword$Flying Oracle:Whenever another creature you control enters, put a +1/+1 counter on this creature.\nAs long as this creature has three or more +1/+1 counters on it, it has flying and is a Knight in addition to its other types. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/skyship_buccaneer.txt b/forge-gui/res/cardsfolder/s/skyship_buccaneer.txt index 94d938b1873..8c05db9cd82 100644 --- a/forge-gui/res/cardsfolder/s/skyship_buccaneer.txt +++ b/forge-gui/res/cardsfolder/s/skyship_buccaneer.txt @@ -1,9 +1,9 @@ -Name:Skyship Buccaneer -ManaCost:3 U U -Types:Creature Human Pirate -PT:4/3 -K:Flying -T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | CheckSVar$ RaidTest | Execute$ TrigDraw | TriggerDescription$ Raid — When this creature enters, if you attacked this turn, draw a card. -SVar:TrigDraw:DB$ Draw -SVar:RaidTest:Count$AttackersDeclared +Name:Skyship Buccaneer +ManaCost:3 U U +Types:Creature Human Pirate +PT:4/3 +K:Flying +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | CheckSVar$ RaidTest | Execute$ TrigDraw | TriggerDescription$ Raid — When this creature enters, if you attacked this turn, draw a card. +SVar:TrigDraw:DB$ Draw +SVar:RaidTest:Count$AttackersDeclared Oracle:Flying\nRaid — When this creature enters, if you attacked this turn, draw a card. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/slimy_piper.txt b/forge-gui/res/cardsfolder/s/slimy_piper.txt index 7a612373087..1c67d3fb8d1 100644 --- a/forge-gui/res/cardsfolder/s/slimy_piper.txt +++ b/forge-gui/res/cardsfolder/s/slimy_piper.txt @@ -1,10 +1,10 @@ -Name:Slimy Piper -ManaCost:1 G -Types:Creature Fungus Bard -PT:2/1 -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigBranch | TriggerDescription$ Whenever CARDNAME attacks, it gets +1/+1 until end of turn. If you control four or more creatures, it gets +2/+2 and gains indestructible until end of turn instead. (Damage and effects that say "destroy" don't destroy it.) -SVar:TrigBranch:DB$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$GE4 | TrueSubAbility$ DBPump1 | FalseSubAbility$ DBPump2 -SVar:DBPump1:DB$ Pump | Defined$ Self | NumAtt$ 2 | NumDef$ 2 | KW$ Indestructible -SVar:DBPump2:DB$ Pump | Defined$ Self | NumAtt$ 1 | NumDef$ 1 -SVar:X:Count$Valid Creature.YouCtrl +Name:Slimy Piper +ManaCost:1 G +Types:Creature Fungus Bard +PT:2/1 +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigBranch | TriggerDescription$ Whenever CARDNAME attacks, it gets +1/+1 until end of turn. If you control four or more creatures, it gets +2/+2 and gains indestructible until end of turn instead. (Damage and effects that say "destroy" don't destroy it.) +SVar:TrigBranch:DB$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$GE4 | TrueSubAbility$ DBPump1 | FalseSubAbility$ DBPump2 +SVar:DBPump1:DB$ Pump | Defined$ Self | NumAtt$ 2 | NumDef$ 2 | KW$ Indestructible +SVar:DBPump2:DB$ Pump | Defined$ Self | NumAtt$ 1 | NumDef$ 1 +SVar:X:Count$Valid Creature.YouCtrl Oracle:Whenever Slimy Piper attacks, it gets +1/+1 until end of turn. If you control four or more creatures, it gets +2/+2 and gains indestructible until end of turn instead. (Damage and effects that say "destroy" don't destroy it.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/slinza_the_spiked_stampede.txt b/forge-gui/res/cardsfolder/s/slinza_the_spiked_stampede.txt index f1e5a2c88d5..f8a22371208 100644 --- a/forge-gui/res/cardsfolder/s/slinza_the_spiked_stampede.txt +++ b/forge-gui/res/cardsfolder/s/slinza_the_spiked_stampede.txt @@ -1,11 +1,11 @@ -Name:Slinza, the Spiked Stampede -ManaCost:4 G -Types:Legendary Creature Beast -PT:5/5 -S:Mode$ ReduceCost | ValidCard$ Beast | Type$ Spell | Activator$ You | Amount$ 2 | Description$ Beast spells you cast cost {2} less to cast. -K:ETBReplacement:Other:AddExtraCounter:Mandatory:Battlefield:Creature.Beast+YouCtrl+Other -SVar:AddExtraCounter:DB$ PutCounter | ETB$ True | Defined$ ReplacedCard | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Each other Beast creature you control enters with an additional +1/+1 counter on it. -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self,Creature.powerGE4+Other | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever NICKNAME or another creature with power 4 or greater enters, you may pay {1}{R/G}. When you do, NICKNAME fights target creature you don't control. -SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ 1 RG | Execute$ TrigFight | TriggerDescription$ When you do, NICKNAME fights target creature you don't control. -SVar:TrigFight:DB$ Fight | Defined$ Self | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control -Oracle:Beast spells you cast cost {2} less to cast.\nEach other Beast creature you control enters with an additional +1/+1 counter on it.\nWhenever Slinza or another creature with power 4 or greater enters, you may pay {1}{R/G}. When you do, Slinza fights target creature you don't control. +Name:Slinza, the Spiked Stampede +ManaCost:4 G +Types:Legendary Creature Beast +PT:5/5 +S:Mode$ ReduceCost | ValidCard$ Beast | Type$ Spell | Activator$ You | Amount$ 2 | Description$ Beast spells you cast cost {2} less to cast. +K:ETBReplacement:Other:AddExtraCounter:Mandatory:Battlefield:Creature.Beast+YouCtrl+Other +SVar:AddExtraCounter:DB$ PutCounter | ETB$ True | Defined$ ReplacedCard | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Each other Beast creature you control enters with an additional +1/+1 counter on it. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self,Creature.powerGE4+Other | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever NICKNAME or another creature with power 4 or greater enters, you may pay {1}{R/G}. When you do, NICKNAME fights target creature you don't control. +SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ 1 RG | Execute$ TrigFight | TriggerDescription$ When you do, NICKNAME fights target creature you don't control. +SVar:TrigFight:DB$ Fight | Defined$ Self | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Choose target creature you don't control +Oracle:Beast spells you cast cost {2} less to cast.\nEach other Beast creature you control enters with an additional +1/+1 counter on it.\nWhenever Slinza or another creature with power 4 or greater enters, you may pay {1}{R/G}. When you do, Slinza fights target creature you don't control. diff --git a/forge-gui/res/cardsfolder/s/slumbering_cerberus.txt b/forge-gui/res/cardsfolder/s/slumbering_cerberus.txt index d1b9a7989c8..f501add4bae 100644 --- a/forge-gui/res/cardsfolder/s/slumbering_cerberus.txt +++ b/forge-gui/res/cardsfolder/s/slumbering_cerberus.txt @@ -1,9 +1,9 @@ -Name:Slumbering Cerberus -ManaCost:1 R -Types:Creature Dog -PT:4/2 -K:CARDNAME doesn't untap during your untap step. -T:Mode$ Phase | Phase$ End of Turn | CheckSVar$ X | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigUntap | TriggerDescription$ Morbid — At the beginning of each end step, if a creature died this turn, untap this creature. -SVar:TrigUntap:DB$ Untap | Defined$ Self -SVar:X:Count$ThisTurnEntered_Graveyard_from_Battlefield_Creature +Name:Slumbering Cerberus +ManaCost:1 R +Types:Creature Dog +PT:4/2 +K:CARDNAME doesn't untap during your untap step. +T:Mode$ Phase | Phase$ End of Turn | CheckSVar$ X | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigUntap | TriggerDescription$ Morbid — At the beginning of each end step, if a creature died this turn, untap this creature. +SVar:TrigUntap:DB$ Untap | Defined$ Self +SVar:X:Count$ThisTurnEntered_Graveyard_from_Battlefield_Creature Oracle:This creature doesn't untap during your untap step.\nMorbid — At the beginning of each end step, if a creature died this turn, untap this creature. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/smoky_lounge_misty_salon.txt b/forge-gui/res/cardsfolder/s/smoky_lounge_misty_salon.txt index 598ffa64665..a3addfd6823 100644 --- a/forge-gui/res/cardsfolder/s/smoky_lounge_misty_salon.txt +++ b/forge-gui/res/cardsfolder/s/smoky_lounge_misty_salon.txt @@ -1,17 +1,17 @@ -Name:Smoky Lounge -ManaCost:2 R -Types:Enchantment Room -T:Mode$ Phase | Phase$ Main1 | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigMana | TriggerDescription$ At the beginning of your first main phase, add {R}{R}. Spend this mana only to cast Room spells and unlock doors. -SVar:TrigMana:DB$ Mana | Produced$ R | Amount$ 2 | RestrictValid$ Static.Unlock,Spell.Room | Defined$ TriggeredPlayer -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nAt the beginning of your first main phase, add {R}{R}. Spend this mana only to cast Room spells and unlock doors. - -ALTERNATE - -Name:Misty Salon -ManaCost:3 U -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigToken | TriggerDescription$ When you unlock this door, create an X/X blue Spirit creature token with flying, where X is the number of unlocked doors among Rooms you control. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ u_x_x_spirit_flying | TokenPower$ X | TokenToughness$ X | TokenOwner$ You -SVar:X:Count$UnlockedDoors Card.Room+YouCtrl +Name:Smoky Lounge +ManaCost:2 R +Types:Enchantment Room +T:Mode$ Phase | Phase$ Main1 | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigMana | TriggerDescription$ At the beginning of your first main phase, add {R}{R}. Spend this mana only to cast Room spells and unlock doors. +SVar:TrigMana:DB$ Mana | Produced$ R | Amount$ 2 | RestrictValid$ Static.Unlock,Spell.Room | Defined$ TriggeredPlayer +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nAt the beginning of your first main phase, add {R}{R}. Spend this mana only to cast Room spells and unlock doors. + +ALTERNATE + +Name:Misty Salon +ManaCost:3 U +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigToken | TriggerDescription$ When you unlock this door, create an X/X blue Spirit creature token with flying, where X is the number of unlocked doors among Rooms you control. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ u_x_x_spirit_flying | TokenPower$ X | TokenToughness$ X | TokenOwner$ You +SVar:X:Count$UnlockedDoors Card.Room+YouCtrl Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, create an X/X blue Spirit creature token with flying, where X is the number of unlocked doors among Rooms you control. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/solitary_study_endless_corridor.txt b/forge-gui/res/cardsfolder/s/solitary_study_endless_corridor.txt index e84fb963a92..e0d7b8382aa 100644 --- a/forge-gui/res/cardsfolder/s/solitary_study_endless_corridor.txt +++ b/forge-gui/res/cardsfolder/s/solitary_study_endless_corridor.txt @@ -1,17 +1,17 @@ -Name:Solitary Study -ManaCost:1 W -Types:Enchantment Room -AlternateMode:Split -S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ 1 | Description$ Creatures you control get +1/+0. -Oracle:Creatures you control get +1/+0. - -ALTERNATE - -Name:Endless Corridor -ManaCost:1 W -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | Execute$ TrigConjure | TriggerDescription$ When you unlock this door, conjure a duplicate of this card into your hand. When you do, target creature you control gains first strike until end of turn. -SVar:TrigConjure:DB$ MakeCard | Conjure$ True | DefinedName$ TriggeredCard | Zone$ Hand | SubAbility$ DBImmediateTrigger -SVar:DBImmediateTrigger:DB$ ImmediateTrigger | Execute$ TrigFirstStrike | TriggerDescription$ When you do, target creature you control gains first strike until end of turn. -SVar:TrigFirstStrike:DB$ Pump | ValidTgts$ Creature.YouCtrl | KW$ First Strike | TgtPrompt$ Select target creature you control | SpellDescription$ Target creature you control gains first strike until end of turn. -Oracle:When you unlock this door, conjure a duplicate of this card into your hand. When you do, target creature you control gains first strike until end of turn. +Name:Solitary Study +ManaCost:1 W +Types:Enchantment Room +AlternateMode:Split +S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ 1 | Description$ Creatures you control get +1/+0. +Oracle:Creatures you control get +1/+0. + +ALTERNATE + +Name:Endless Corridor +ManaCost:1 W +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | Execute$ TrigConjure | TriggerDescription$ When you unlock this door, conjure a duplicate of this card into your hand. When you do, target creature you control gains first strike until end of turn. +SVar:TrigConjure:DB$ MakeCard | Conjure$ True | DefinedName$ TriggeredCard | Zone$ Hand | SubAbility$ DBImmediateTrigger +SVar:DBImmediateTrigger:DB$ ImmediateTrigger | Execute$ TrigFirstStrike | TriggerDescription$ When you do, target creature you control gains first strike until end of turn. +SVar:TrigFirstStrike:DB$ Pump | ValidTgts$ Creature.YouCtrl | KW$ First Strike | TgtPrompt$ Select target creature you control | SpellDescription$ Target creature you control gains first strike until end of turn. +Oracle:When you unlock this door, conjure a duplicate of this card into your hand. When you do, target creature you control gains first strike until end of turn. diff --git a/forge-gui/res/cardsfolder/s/soul_shackled_zombie.txt b/forge-gui/res/cardsfolder/s/soul_shackled_zombie.txt index 6f0d320e4b7..cd373bb1abd 100644 --- a/forge-gui/res/cardsfolder/s/soul_shackled_zombie.txt +++ b/forge-gui/res/cardsfolder/s/soul_shackled_zombie.txt @@ -1,11 +1,11 @@ -Name:Soul-Shackled Zombie -ManaCost:3 B -Types:Creature Zombie -PT:4/2 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, exile up to two target cards from a single graveyard. If at least one creature card was exiled this way, each opponent loses 2 life and you gain 2 life. -SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card | TargetMin$ 0 | TargetMax$ 2 | TargetsFromSingleZone$ True | TgtPrompt$ Select target card in a graveyard to exile | RememberChanged$ True | SubAbility$ DBLoseLife -SVar:DBLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 2 | ConditionDefined$ Remembered | ConditionPresent$ Card.Creature | SubAbility$ DBGainLife -SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 | ConditionDefined$ Remembered | ConditionPresent$ Card.Creature | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -DeckHas:Ability$LifeGain +Name:Soul-Shackled Zombie +ManaCost:3 B +Types:Creature Zombie +PT:4/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, exile up to two target cards from a single graveyard. If at least one creature card was exiled this way, each opponent loses 2 life and you gain 2 life. +SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | ValidTgts$ Card | TargetMin$ 0 | TargetMax$ 2 | TargetsFromSingleZone$ True | TgtPrompt$ Select target card in a graveyard to exile | RememberChanged$ True | SubAbility$ DBLoseLife +SVar:DBLoseLife:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 2 | ConditionDefined$ Remembered | ConditionPresent$ Card.Creature | SubAbility$ DBGainLife +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 | ConditionDefined$ Remembered | ConditionPresent$ Card.Creature | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +DeckHas:Ability$LifeGain Oracle:When this creature enters, exile up to two target cards from a single graveyard. If at least one creature card was exiled this way, each opponent loses 2 life and you gain 2 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/soulstone_sanctuary.txt b/forge-gui/res/cardsfolder/s/soulstone_sanctuary.txt index 442f1db37a8..fc5f1eca20c 100644 --- a/forge-gui/res/cardsfolder/s/soulstone_sanctuary.txt +++ b/forge-gui/res/cardsfolder/s/soulstone_sanctuary.txt @@ -1,6 +1,6 @@ -Name:Soulstone Sanctuary -ManaCost:no cost -Types:Land -A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. -A:AB$ Animate | Cost$ 4 | Defined$ Self | Power$ 3 | Toughness$ 3 | Keywords$ Vigilance | Types$ Creature | AddAllCreatureTypes$ True | Duration$ Permanent | SpellDescription$ This land becomes a 3/3 creature with vigilance and all creature types. It's still a land. -Oracle:{T}: Add {C}.\n{4}: This land becomes a 3/3 creature with vigilance and all creature types. It's still a land. +Name:Soulstone Sanctuary +ManaCost:no cost +Types:Land +A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. +A:AB$ Animate | Cost$ 4 | Defined$ Self | Power$ 3 | Toughness$ 3 | Keywords$ Vigilance | Types$ Creature | AddAllCreatureTypes$ True | Duration$ Permanent | SpellDescription$ This land becomes a 3/3 creature with vigilance and all creature types. It's still a land. +Oracle:{T}: Add {C}.\n{4}: This land becomes a 3/3 creature with vigilance and all creature types. It's still a land. diff --git a/forge-gui/res/cardsfolder/s/sower_of_chaos.txt b/forge-gui/res/cardsfolder/s/sower_of_chaos.txt index 95a45cdd5f4..263f9f86980 100644 --- a/forge-gui/res/cardsfolder/s/sower_of_chaos.txt +++ b/forge-gui/res/cardsfolder/s/sower_of_chaos.txt @@ -1,6 +1,6 @@ -Name:Sower of Chaos -ManaCost:3 R -Types:Creature Devil -PT:4/3 -A:AB$ Pump | Cost$ 2 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN CARDNAME can't block. | IsCurse$ True | SpellDescription$ Target creature can't block this turn. +Name:Sower of Chaos +ManaCost:3 R +Types:Creature Devil +PT:4/3 +A:AB$ Pump | Cost$ 2 R | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN CARDNAME can't block. | IsCurse$ True | SpellDescription$ Target creature can't block this turn. Oracle:{2}{R}: Target creature can't block this turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/sphinx_of_forgotten_lore.txt b/forge-gui/res/cardsfolder/s/sphinx_of_forgotten_lore.txt index 449b175c712..0a69c2fb42e 100644 --- a/forge-gui/res/cardsfolder/s/sphinx_of_forgotten_lore.txt +++ b/forge-gui/res/cardsfolder/s/sphinx_of_forgotten_lore.txt @@ -1,10 +1,10 @@ -Name:Sphinx of Forgotten Lore -ManaCost:2 U U -Types:Creature Sphinx -PT:3/3 -K:Flash -K:Flying -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever this creature attacks, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to that card's mana cost. (You may cast that card from your graveyard for its flashback cost. Then exile it.) -SVar:TrigPump:DB$ Pump | ValidTgts$ Instant.YouOwn,Sorcery.YouOwn | TgtZone$ Graveyard | TgtPrompt$ Select target instant or sorcery card in your graveyard | KW$ Flashback | PumpZone$ Graveyard | AILogic$ ReplaySpell -SVar:HasAttackEffect:TRUE -Oracle:Flash (You may cast this spell any time you could cast an instant.)\nFlying\nWhenever this creature attacks, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to that card's mana cost. (You may cast that card from your graveyard for its flashback cost. Then exile it.) +Name:Sphinx of Forgotten Lore +ManaCost:2 U U +Types:Creature Sphinx +PT:3/3 +K:Flash +K:Flying +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever this creature attacks, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to that card's mana cost. (You may cast that card from your graveyard for its flashback cost. Then exile it.) +SVar:TrigPump:DB$ Pump | ValidTgts$ Instant.YouOwn,Sorcery.YouOwn | TgtZone$ Graveyard | TgtPrompt$ Select target instant or sorcery card in your graveyard | KW$ Flashback | PumpZone$ Graveyard | AILogic$ ReplaySpell +SVar:HasAttackEffect:TRUE +Oracle:Flash (You may cast this spell any time you could cast an instant.)\nFlying\nWhenever this creature attacks, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to that card's mana cost. (You may cast that card from your graveyard for its flashback cost. Then exile it.) diff --git a/forge-gui/res/cardsfolder/s/spiked_corridor_torture_pit.txt b/forge-gui/res/cardsfolder/s/spiked_corridor_torture_pit.txt index 84cbcb02732..52b7328f71d 100644 --- a/forge-gui/res/cardsfolder/s/spiked_corridor_torture_pit.txt +++ b/forge-gui/res/cardsfolder/s/spiked_corridor_torture_pit.txt @@ -1,16 +1,16 @@ -Name:Spiked Corridor -ManaCost:3 R -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigToken | TriggerDescription$ When you unlock this door, create three 1/1 red Devil creature tokens with "When this creature dies, it deals 1 damage to any target." -SVar:TrigToken:DB$ Token | TokenAmount$ 3 | TokenScript$ r_1_1_devil_burn | TokenOwner$ You -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, create three 1/1 red Devil creature tokens with "When this creature dies, it deals 1 damage to any target." - -ALTERNATE - -Name:Torture Pit -ManaCost:3 R -Types:Enchantment Room -R:Event$ DamageDone | ActiveZones$ Battlefield | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent | IsCombat$ False | ReplaceWith$ DamageReplace | Description$ If a source you control would deal noncombat damage to an opponent, it deals that much damage plus 2 instead. -SVar:DamageReplace:DB$ ReplaceEffect | VarName$ DamageAmount | VarValue$ ReplaceCount$DamageAmount/Plus.2 +Name:Spiked Corridor +ManaCost:3 R +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigToken | TriggerDescription$ When you unlock this door, create three 1/1 red Devil creature tokens with "When this creature dies, it deals 1 damage to any target." +SVar:TrigToken:DB$ Token | TokenAmount$ 3 | TokenScript$ r_1_1_devil_burn | TokenOwner$ You +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, create three 1/1 red Devil creature tokens with "When this creature dies, it deals 1 damage to any target." + +ALTERNATE + +Name:Torture Pit +ManaCost:3 R +Types:Enchantment Room +R:Event$ DamageDone | ActiveZones$ Battlefield | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent | IsCombat$ False | ReplaceWith$ DamageReplace | Description$ If a source you control would deal noncombat damage to an opponent, it deals that much damage plus 2 instead. +SVar:DamageReplace:DB$ ReplaceEffect | VarName$ DamageAmount | VarValue$ ReplaceCount$DamageAmount/Plus.2 Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nIf a source you control would deal noncombat damage to an opponent, it deals that much damage plus 2 instead. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/spined_tyrranax.txt b/forge-gui/res/cardsfolder/s/spined_tyrranax.txt index 1758c405b47..809a1b619df 100644 --- a/forge-gui/res/cardsfolder/s/spined_tyrranax.txt +++ b/forge-gui/res/cardsfolder/s/spined_tyrranax.txt @@ -1,10 +1,10 @@ -Name:Spined Tyrranax -ManaCost:4 G -Types:Creature Dinosaur Beast -PT:5/5 -T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ At the beginning of combat on your turn, you may pay {2}{G}. When you do, put a +1/+1 counter on target creature. That creature gains trample until end of turn. (It can deal excess combat damage to the player or planeswalker it's attacking.) -SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ 2 G | Execute$ TrigPutCounter | TriggerDescription$ When you do, put a +1/+1 counter on target creature. That creature gains trample until end of turn. (It can deal excess combat damage to the player or planeswalker it's attacking.) -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump -SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Trample -DeckHas:Ability$Counters -Oracle:At the beginning of combat on your turn, you may pay {2}{G}. When you do, put a +1/+1 counter on target creature. That creature gains trample until end of turn. (It can deal excess combat damage to the player or planeswalker it's attacking.) +Name:Spined Tyrranax +ManaCost:4 G +Types:Creature Dinosaur Beast +PT:5/5 +T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ At the beginning of combat on your turn, you may pay {2}{G}. When you do, put a +1/+1 counter on target creature. That creature gains trample until end of turn. (It can deal excess combat damage to the player or planeswalker it's attacking.) +SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ 2 G | Execute$ TrigPutCounter | TriggerDescription$ When you do, put a +1/+1 counter on target creature. That creature gains trample until end of turn. (It can deal excess combat damage to the player or planeswalker it's attacking.) +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump +SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Trample +DeckHas:Ability$Counters +Oracle:At the beginning of combat on your turn, you may pay {2}{G}. When you do, put a +1/+1 counter on target creature. That creature gains trample until end of turn. (It can deal excess combat damage to the player or planeswalker it's attacking.) diff --git a/forge-gui/res/cardsfolder/s/spinner_of_souls.txt b/forge-gui/res/cardsfolder/s/spinner_of_souls.txt index 8ffdf2db031..bad10be0751 100644 --- a/forge-gui/res/cardsfolder/s/spinner_of_souls.txt +++ b/forge-gui/res/cardsfolder/s/spinner_of_souls.txt @@ -1,8 +1,8 @@ -Name:Spinner of Souls -ManaCost:2 G -Types:Creature Spider Spirit -PT:4/3 -K:Reach -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.nonToken+YouCtrl+Other | Execute$ TrigDigUntil | OptionalDecider$ You | TriggerDescription$ Whenever another nontoken creature you control dies, you may reveal cards from the top of your library until you reveal a creature card. Put that card into your hand and the rest on the bottom of your library in a random order. -SVar:TrigDigUntil:DB$ DigUntil | Valid$ Creature | ValidDescription$ creature that shares a creature type | FoundDestination$ Hand | RevealedDestination$ Library | RevealedLibraryPosition$ -1 | RevealRandomOrder$ True +Name:Spinner of Souls +ManaCost:2 G +Types:Creature Spider Spirit +PT:4/3 +K:Reach +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.nonToken+YouCtrl+Other | Execute$ TrigDigUntil | OptionalDecider$ You | TriggerDescription$ Whenever another nontoken creature you control dies, you may reveal cards from the top of your library until you reveal a creature card. Put that card into your hand and the rest on the bottom of your library in a random order. +SVar:TrigDigUntil:DB$ DigUntil | Valid$ Creature | ValidDescription$ creature that shares a creature type | FoundDestination$ Hand | RevealedDestination$ Library | RevealedLibraryPosition$ -1 | RevealRandomOrder$ True Oracle:Reach\nWhenever another nontoken creature you control dies, you may reveal cards from the top of your library until you reveal a creature card. Put that card into your hand and the rest on the bottom of your library in a random order. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/star_athlete.txt b/forge-gui/res/cardsfolder/s/star_athlete.txt index 8154825863e..e47bf821d98 100644 --- a/forge-gui/res/cardsfolder/s/star_athlete.txt +++ b/forge-gui/res/cardsfolder/s/star_athlete.txt @@ -1,11 +1,11 @@ -Name:Star Athlete -ManaCost:1 R R -Types:Creature Human Warrior -PT:3/2 -K:Menace -K:Blitz:3 R -T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ DBPump | TriggerDescription$ Whenever CARDNAME attacks, choose up to one target nonland permanent. Its controller may sacrifice it. If they don't, CARDNAME deals 5 damage to that player. -SVar:DBPump:DB$ Pump | ValidTgts$ Permanent.nonLand | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select up to one target nonland permanent | SubAbility$ DBDamage -SVar:DBDamage:DB$ DealDamage | Defined$ TargetedController | UnlessCost$ Sac<1/Permanent.nonLand+targetedBy/targeted nonland permanent> | NumDmg$ 5 -DeckHas:Ability$Sacrifice -Oracle:Menace\nWhenever Star Athlete attacks, choose up to one target nonland permanent. Its controller may sacrifice it. If they don't, Star Athlete deals 5 damage to that player.\nBlitz {3}{R} (If you cast this spell for its blitz cost, it gains haste and "When this creature dies, draw a card." Sacrifice it at the beginning of the next end step.) +Name:Star Athlete +ManaCost:1 R R +Types:Creature Human Warrior +PT:3/2 +K:Menace +K:Blitz:3 R +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ DBPump | TriggerDescription$ Whenever CARDNAME attacks, choose up to one target nonland permanent. Its controller may sacrifice it. If they don't, CARDNAME deals 5 damage to that player. +SVar:DBPump:DB$ Pump | ValidTgts$ Permanent.nonLand | TargetMin$ 0 | TargetMax$ 1 | TgtPrompt$ Select up to one target nonland permanent | SubAbility$ DBDamage +SVar:DBDamage:DB$ DealDamage | Defined$ TargetedController | UnlessCost$ Sac<1/Permanent.nonLand+targetedBy/targeted nonland permanent> | NumDmg$ 5 +DeckHas:Ability$Sacrifice +Oracle:Menace\nWhenever Star Athlete attacks, choose up to one target nonland permanent. Its controller may sacrifice it. If they don't, Star Athlete deals 5 damage to that player.\nBlitz {3}{R} (If you cast this spell for its blitz cost, it gains haste and "When this creature dies, draw a card." Sacrifice it at the beginning of the next end step.) diff --git a/forge-gui/res/cardsfolder/s/starlight_snare.txt b/forge-gui/res/cardsfolder/s/starlight_snare.txt index f317977ca77..d5f2554f115 100644 --- a/forge-gui/res/cardsfolder/s/starlight_snare.txt +++ b/forge-gui/res/cardsfolder/s/starlight_snare.txt @@ -1,9 +1,9 @@ -Name:Starlight Snare -ManaCost:2 U -Types:Enchantment Aura -K:Enchant creature -A:SP$ Attach | Cost$ 2 U | ValidTgts$ Creature | AILogic$ KeepTapped -T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigTap | TriggerDescription$ When CARDNAME enters, tap enchanted creature. -SVar:TrigTap:DB$ Tap | Defined$ Enchanted -S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Enchanted creature doesn't untap during its controller's untap step. -Oracle:Enchant creature\nWhen Starlight Snare enters, tap enchanted creature.\nEnchanted creature doesn't untap during its controller's untap step. +Name:Starlight Snare +ManaCost:2 U +Types:Enchantment Aura +K:Enchant creature +A:SP$ Attach | Cost$ 2 U | ValidTgts$ Creature | AILogic$ KeepTapped +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigTap | TriggerDescription$ When CARDNAME enters, tap enchanted creature. +SVar:TrigTap:DB$ Tap | Defined$ Enchanted +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Enchanted creature doesn't untap during its controller's untap step. +Oracle:Enchant creature\nWhen Starlight Snare enters, tap enchanted creature.\nEnchanted creature doesn't untap during its controller's untap step. diff --git a/forge-gui/res/cardsfolder/s/starnheim_memento.txt b/forge-gui/res/cardsfolder/s/starnheim_memento.txt index e3eb3adf691..8c0ee686370 100644 --- a/forge-gui/res/cardsfolder/s/starnheim_memento.txt +++ b/forge-gui/res/cardsfolder/s/starnheim_memento.txt @@ -1,6 +1,6 @@ -Name:Starnheim Memento -ManaCost:3 -Types:Artifact -A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add {W}. -A:AB$ Pump | Cost$ 1 W T | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ 1 | NumDef$ 1 | KW$ Flying | SorcerySpeed$ True | SpellDescription$ Target creature gets +1/+1 and gains flying until end of turn. Activate only as a sorcery. +Name:Starnheim Memento +ManaCost:3 +Types:Artifact +A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add {W}. +A:AB$ Pump | Cost$ 1 W T | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ 1 | NumDef$ 1 | KW$ Flying | SorcerySpeed$ True | SpellDescription$ Target creature gets +1/+1 and gains flying until end of turn. Activate only as a sorcery. Oracle:{T}: Add {W}.\n{1}{W}, {T}: Target creature gets +1/+1 and gains flying until end of turn. Activate only as a sorcery. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/strix_lookout.txt b/forge-gui/res/cardsfolder/s/strix_lookout.txt index 5efc453c333..76256c61981 100644 --- a/forge-gui/res/cardsfolder/s/strix_lookout.txt +++ b/forge-gui/res/cardsfolder/s/strix_lookout.txt @@ -1,9 +1,9 @@ -Name:Strix Lookout -ManaCost:1 U -Types:Creature Bird -PT:1/2 -K:Flying -K:Vigilance -A:AB$ Draw | Cost$ 1 U T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard -SVar:DBDiscard:DB$ Discard | Defined$ You | Mode$ TgtChoose | NumCards$ 1 -Oracle:Flying, vigilance (Attacking doesn't cause this creature to tap.)\n{1}{U}, {T}: Draw a card, then discard a card. +Name:Strix Lookout +ManaCost:1 U +Types:Creature Bird +PT:1/2 +K:Flying +K:Vigilance +A:AB$ Draw | Cost$ 1 U T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard +SVar:DBDiscard:DB$ Discard | Defined$ You | Mode$ TgtChoose | NumCards$ 1 +Oracle:Flying, vigilance (Attacking doesn't cause this creature to tap.)\n{1}{U}, {T}: Draw a card, then discard a card. diff --git a/forge-gui/res/cardsfolder/s/strongbox_raider.txt b/forge-gui/res/cardsfolder/s/strongbox_raider.txt index 5e0928853ca..0bae9f4419e 100644 --- a/forge-gui/res/cardsfolder/s/strongbox_raider.txt +++ b/forge-gui/res/cardsfolder/s/strongbox_raider.txt @@ -1,12 +1,12 @@ -Name:Strongbox Raider -ManaCost:2 R R -Types:Creature Orc Pirate -PT:5/2 -T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | CheckSVar$ RaidTest | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ Raid — When this creature enters, if you attacked this turn, exile the top two cards of your library. Choose one of them. Until the end of your next turn, you may play that card. -SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 2 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBChoose -SVar:DBChoose:DB$ ChooseCard | Choices$ Card.IsRemembered | ChoiceZone$ Exile | Mandatory$ True | ForgetOtherRemembered$ True | SubAbility$ DBEffect -SVar:DBEffect:DB$ Effect | RememberObjects$ ChosenCard | StaticAbilities$ Play | Duration$ UntilTheEndOfYourNextTurn | ForgetOnMoved$ Exile | SubAbility$ DBCleanup -SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.ChosenCard | AffectedZone$ Exile | Description$ Until the end of your next turn, you may play the chosen card. -SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True -SVar:RaidTest:Count$AttackersDeclared +Name:Strongbox Raider +ManaCost:2 R R +Types:Creature Orc Pirate +PT:5/2 +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | CheckSVar$ RaidTest | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ Raid — When this creature enters, if you attacked this turn, exile the top two cards of your library. Choose one of them. Until the end of your next turn, you may play that card. +SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 2 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBChoose +SVar:DBChoose:DB$ ChooseCard | Choices$ Card.IsRemembered | ChoiceZone$ Exile | Mandatory$ True | ForgetOtherRemembered$ True | SubAbility$ DBEffect +SVar:DBEffect:DB$ Effect | RememberObjects$ ChosenCard | StaticAbilities$ Play | Duration$ UntilTheEndOfYourNextTurn | ForgetOnMoved$ Exile | SubAbility$ DBCleanup +SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.ChosenCard | AffectedZone$ Exile | Description$ Until the end of your next turn, you may play the chosen card. +SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True +SVar:RaidTest:Count$AttackersDeclared Oracle:Raid — When this creature enters, if you attacked this turn, exile the top two cards of your library. Choose one of them. Until the end of your next turn, you may play that card. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/sun_blessed_healer.txt b/forge-gui/res/cardsfolder/s/sun_blessed_healer.txt index efa984884fe..99bd6638b68 100644 --- a/forge-gui/res/cardsfolder/s/sun_blessed_healer.txt +++ b/forge-gui/res/cardsfolder/s/sun_blessed_healer.txt @@ -1,10 +1,10 @@ -Name:Sun-Blessed Healer -ManaCost:1 W -Types:Creature Human Cleric -PT:3/1 -K:Kicker:1 W -K:Lifelink -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+kicked | Execute$ TrigChange | TriggerDescription$ When this creature enters, if it was kicked, return target nonland permanent card with mana value 2 or less from your graveyard to the battlefield. -SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Permanent.YouOwn+nonLand+cmcLE2 | TgtPrompt$ Select target nonland permanent card with mana value 2 or less in your graveyard -DeckHints:Ability$Graveyard -Oracle:Kicker {1}{W} (You may pay an additional {1}{W} as you cast this spell.)\nLifelink (Damage dealt by this creature also causes you to gain that much life.)\nWhen this creature enters, if it was kicked, return target nonland permanent card with mana value 2 or less from your graveyard to the battlefield. +Name:Sun-Blessed Healer +ManaCost:1 W +Types:Creature Human Cleric +PT:3/1 +K:Kicker:1 W +K:Lifelink +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+kicked | Execute$ TrigChange | TriggerDescription$ When this creature enters, if it was kicked, return target nonland permanent card with mana value 2 or less from your graveyard to the battlefield. +SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Permanent.YouOwn+nonLand+cmcLE2 | TgtPrompt$ Select target nonland permanent card with mana value 2 or less in your graveyard +DeckHints:Ability$Graveyard +Oracle:Kicker {1}{W} (You may pay an additional {1}{W} as you cast this spell.)\nLifelink (Damage dealt by this creature also causes you to gain that much life.)\nWhen this creature enters, if it was kicked, return target nonland permanent card with mana value 2 or less from your graveyard to the battlefield. diff --git a/forge-gui/res/cardsfolder/s/sutina_speaker_of_the_tajuru.txt b/forge-gui/res/cardsfolder/s/sutina_speaker_of_the_tajuru.txt index 74728097503..ae4092d2e27 100644 --- a/forge-gui/res/cardsfolder/s/sutina_speaker_of_the_tajuru.txt +++ b/forge-gui/res/cardsfolder/s/sutina_speaker_of_the_tajuru.txt @@ -1,11 +1,11 @@ -Name:Sutina, Speaker of the Tajuru -ManaCost:2 G -Types:Legendary Creature Elf Scout -PT:2/2 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChange | TriggerDescription$ When CARDNAME enters, search your library for a basic land card, put it onto the battlefield tapped, then shuffle. -SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ 1 -T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever NICKNAME attacks, you may return a land you control to its owner's hand. When you do, put a +1/+1 counter on target creature. -SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ Return<1/Land> | Execute$ TrigPutCounter | TriggerDescription$ When you do, put a +1/+1 counter on target creature. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 -SVar:HasAttackEffect:TRUE +Name:Sutina, Speaker of the Tajuru +ManaCost:2 G +Types:Legendary Creature Elf Scout +PT:2/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChange | TriggerDescription$ When CARDNAME enters, search your library for a basic land card, put it onto the battlefield tapped, then shuffle. +SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | Tapped$ True | ChangeType$ Land.Basic | ChangeNum$ 1 +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever NICKNAME attacks, you may return a land you control to its owner's hand. When you do, put a +1/+1 counter on target creature. +SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ Return<1/Land> | Execute$ TrigPutCounter | TriggerDescription$ When you do, put a +1/+1 counter on target creature. +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 +SVar:HasAttackEffect:TRUE Oracle:When Sutina, Speaker of the Tajuru enters, search your library for a basic land card, put it onto the battlefield tapped, then shuffle.\nWhenever Sutina attacks, you may return a land you control to its owner's hand. When you do, put a +1/+1 counter on target creature. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/s/sylvan_scavenging.txt b/forge-gui/res/cardsfolder/s/sylvan_scavenging.txt index 59918b4a5b5..61053977a2f 100644 --- a/forge-gui/res/cardsfolder/s/sylvan_scavenging.txt +++ b/forge-gui/res/cardsfolder/s/sylvan_scavenging.txt @@ -1,9 +1,9 @@ -Name:Sylvan Scavenging -ManaCost:1 G G -Types:Enchantment -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigCharm | TriggerDescription$ At the beginning of your end step, ABILITY -SVar:TrigCharm:DB$ Charm | Choices$ DBPutCounter,DBToken | CharmNum$ 1 -SVar:DBPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | SpellDescription$ Put a +1/+1 counter on target creature you control. -SVar:DBToken:DB$ Token | TokenScript$ g_3_3_raccoon | ConditionPresent$ Creature.powerGE4+YouCtrl | SpellDescription$ Create a 3/3 green Raccoon creature token if you control a creature with power 4 or greater. -DeckHas:Ability$Counters|Token +Name:Sylvan Scavenging +ManaCost:1 G G +Types:Enchantment +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigCharm | TriggerDescription$ At the beginning of your end step, ABILITY +SVar:TrigCharm:DB$ Charm | Choices$ DBPutCounter,DBToken | CharmNum$ 1 +SVar:DBPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | SpellDescription$ Put a +1/+1 counter on target creature you control. +SVar:DBToken:DB$ Token | TokenScript$ g_3_3_raccoon | ConditionPresent$ Creature.powerGE4+YouCtrl | SpellDescription$ Create a 3/3 green Raccoon creature token if you control a creature with power 4 or greater. +DeckHas:Ability$Counters|Token Oracle:At the beginning of your end step, choose one —\n• Put a +1/+1 counter on target creature you control.\n• Create a 3/3 green Raccoon creature token if you control a creature with power 4 or greater. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/t/taeko_the_patient_avalanche.txt b/forge-gui/res/cardsfolder/t/taeko_the_patient_avalanche.txt index 92ac5f35065..0ba041cca17 100644 --- a/forge-gui/res/cardsfolder/t/taeko_the_patient_avalanche.txt +++ b/forge-gui/res/cardsfolder/t/taeko_the_patient_avalanche.txt @@ -1,15 +1,15 @@ -Name:Taeko, the Patient Avalanche -ManaCost:3 U -Types:Legendary Creature Turtle Ninja -PT:4/5 -R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | ReplacementResult$ Updated | ReplaceWith$ ETBTapped | Description$ CARDNAME enters tapped. -SVar:ETBTapped:DB$ Tap | Defined$ Self | ETB$ True -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Ante,Command,Exile,Hand,Library | ValidCard$ Creature.Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigScry | TriggerDescription$ Whenever another creature you control leaves the battlefield, if it didn't die, scry 1 and put a +1/+1 counter on NICKNAME. -SVar:TrigScry:DB$ Scry | ScryNum$ 1 | SubAbility$ DBPutCounter -SVar:DBPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 -T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever NICKNAME attacks, you may pay {U/B}. When you do, target attacking creature can't be blocked this turn. -SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ UB | Execute$ TrigUnblockable | SpellDescription$ When you do, target attacking creature can't be blocked this turn. -SVar:TrigUnblockable:DB$ Effect | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | StaticAbilities$ Unblockable -SVar:Unblockable:Mode$ CantBlockBy | ValidAttacker$ Card.IsRemembered | Description$ This creature can't be blocked this turn. -DeckHas:Ability$Counters -Oracle:Taeko, the Patient Avalanche enters tapped.\nWhenever another creature you control leaves the battlefield, if it didn't die, scry 1 and put a +1/+1 counter on Taeko.\nWhenever Taeko attacks, you may pay {U/B}. When you do, target attacking creature can't be blocked this turn. +Name:Taeko, the Patient Avalanche +ManaCost:3 U +Types:Legendary Creature Turtle Ninja +PT:4/5 +R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | ReplacementResult$ Updated | ReplaceWith$ ETBTapped | Description$ CARDNAME enters tapped. +SVar:ETBTapped:DB$ Tap | Defined$ Self | ETB$ True +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Ante,Command,Exile,Hand,Library | ValidCard$ Creature.Other+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigScry | TriggerDescription$ Whenever another creature you control leaves the battlefield, if it didn't die, scry 1 and put a +1/+1 counter on NICKNAME. +SVar:TrigScry:DB$ Scry | ScryNum$ 1 | SubAbility$ DBPutCounter +SVar:DBPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 +T:Mode$ Attacks | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigImmediateTrig | TriggerDescription$ Whenever NICKNAME attacks, you may pay {U/B}. When you do, target attacking creature can't be blocked this turn. +SVar:TrigImmediateTrig:AB$ ImmediateTrigger | Cost$ UB | Execute$ TrigUnblockable | SpellDescription$ When you do, target attacking creature can't be blocked this turn. +SVar:TrigUnblockable:DB$ Effect | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | StaticAbilities$ Unblockable +SVar:Unblockable:Mode$ CantBlockBy | ValidAttacker$ Card.IsRemembered | Description$ This creature can't be blocked this turn. +DeckHas:Ability$Counters +Oracle:Taeko, the Patient Avalanche enters tapped.\nWhenever another creature you control leaves the battlefield, if it didn't die, scry 1 and put a +1/+1 counter on Taeko.\nWhenever Taeko attacks, you may pay {U/B}. When you do, target attacking creature can't be blocked this turn. diff --git a/forge-gui/res/cardsfolder/t/thurid_mare_of_destiny.txt b/forge-gui/res/cardsfolder/t/thurid_mare_of_destiny.txt index 1b342441651..5319b3926a5 100644 --- a/forge-gui/res/cardsfolder/t/thurid_mare_of_destiny.txt +++ b/forge-gui/res/cardsfolder/t/thurid_mare_of_destiny.txt @@ -1,10 +1,10 @@ -Name:Thurid, Mare of Destiny -ManaCost:2 W W -Types:Legendary Creature Pegasus -PT:2/4 -K:Flying -K:Lifelink -T:Mode$ SpellCast | ValidCard$ Creature.Pegasus,Creature.Unicorn,Creature.Horse | ValidActivatingPlayer$ You | Execute$ TrigCopySpell | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a Pegasus, Unicorn, or Horse creature spell, copy it. (The copy becomes a token.) -SVar:TrigCopySpell:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility -S:Mode$ Continuous | Affected$ Pegasus.Other+YouCtrl,Unicorn.Other+YouCtrl,Horse.Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Pegasi, Unicorns, and Horses you control get +1/+1. +Name:Thurid, Mare of Destiny +ManaCost:2 W W +Types:Legendary Creature Pegasus +PT:2/4 +K:Flying +K:Lifelink +T:Mode$ SpellCast | ValidCard$ Creature.Pegasus,Creature.Unicorn,Creature.Horse | ValidActivatingPlayer$ You | Execute$ TrigCopySpell | TriggerZones$ Battlefield | TriggerDescription$ Whenever you cast a Pegasus, Unicorn, or Horse creature spell, copy it. (The copy becomes a token.) +SVar:TrigCopySpell:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility +S:Mode$ Continuous | Affected$ Pegasus.Other+YouCtrl,Unicorn.Other+YouCtrl,Horse.Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Pegasi, Unicorns, and Horses you control get +1/+1. Oracle:Flying, lifelink\nWhenever you cast a Pegasus, Unicorn, or Horse creature spell, copy it. (The copy becomes a token.)\nOther Pegasi, Unicorns, and Horses you control get +1/+1. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/t/tinybones_bauble_burglar.txt b/forge-gui/res/cardsfolder/t/tinybones_bauble_burglar.txt index b1b99a62fd6..8ae33ccb9e9 100644 --- a/forge-gui/res/cardsfolder/t/tinybones_bauble_burglar.txt +++ b/forge-gui/res/cardsfolder/t/tinybones_bauble_burglar.txt @@ -1,9 +1,9 @@ -Name:Tinybones, Bauble Burglar -ManaCost:1 B -Types:Legendary Creature Skeleton Rogue -PT:1/3 -T:Mode$ Discarded | ValidCard$ Card.OppOwn | TriggerZones$ Battlefield | Execute$ TrigExile | TriggerDescription$ Whenever an opponent discards a card, exile it from their graveyard with a stash counter on it. -SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Exile | WithCountersType$ STASH -S:Mode$ Continuous | Condition$ PlayerTurn | MayPlay$ True | Affected$ Card.YouDontOwn+counters_GE1_STASH | AffectedZone$ Exile | MayPlayIgnoreType$ True | Description$ During your turn, you may play cards you don't own with stash counters on them from exile, and mana of any type can be spent to cast those spells. -A:AB$ Discard | Cost$ 3 B T | Defined$ Opponent | Mode$ TgtChoose | SorcerySpeed$ True | SpellDescription$ Each opponent discards a card. Activate only as a sorcery. -Oracle:Whenever an opponent discards a card, exile it from their graveyard with a stash counter on it.\nDuring your turn, you may play cards you don't own with stash counters on them from exile, and mana of any type can be spent to cast those spells.\n{3}{B}, {T}: Each opponent discards a card. Activate only as a sorcery. +Name:Tinybones, Bauble Burglar +ManaCost:1 B +Types:Legendary Creature Skeleton Rogue +PT:1/3 +T:Mode$ Discarded | ValidCard$ Card.OppOwn | TriggerZones$ Battlefield | Execute$ TrigExile | TriggerDescription$ Whenever an opponent discards a card, exile it from their graveyard with a stash counter on it. +SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Exile | WithCountersType$ STASH +S:Mode$ Continuous | Condition$ PlayerTurn | MayPlay$ True | Affected$ Card.YouDontOwn+counters_GE1_STASH | AffectedZone$ Exile | MayPlayIgnoreType$ True | Description$ During your turn, you may play cards you don't own with stash counters on them from exile, and mana of any type can be spent to cast those spells. +A:AB$ Discard | Cost$ 3 B T | Defined$ Opponent | Mode$ TgtChoose | SorcerySpeed$ True | SpellDescription$ Each opponent discards a card. Activate only as a sorcery. +Oracle:Whenever an opponent discards a card, exile it from their graveyard with a stash counter on it.\nDuring your turn, you may play cards you don't own with stash counters on them from exile, and mana of any type can be spent to cast those spells.\n{3}{B}, {T}: Each opponent discards a card. Activate only as a sorcery. diff --git a/forge-gui/res/cardsfolder/t/tragic_banshee.txt b/forge-gui/res/cardsfolder/t/tragic_banshee.txt index 511af56cd6a..1ef9e9bf53c 100644 --- a/forge-gui/res/cardsfolder/t/tragic_banshee.txt +++ b/forge-gui/res/cardsfolder/t/tragic_banshee.txt @@ -1,8 +1,8 @@ -Name:Tragic Banshee -ManaCost:4 B -Types:Creature Spirit -PT:5/3 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ Morbid — When this creature enters, target creature an opponent controls gets -1/-1 until end of turn. If a creature died this turn, that creature gets -13/-13 instead. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | NumAtt$ -X | NumDef$ -X | IsCurse$ True -SVar:X:Count$Morbid.13.1 +Name:Tragic Banshee +ManaCost:4 B +Types:Creature Spirit +PT:5/3 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ Morbid — When this creature enters, target creature an opponent controls gets -1/-1 until end of turn. If a creature died this turn, that creature gets -13/-13 instead. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | NumAtt$ -X | NumDef$ -X | IsCurse$ True +SVar:X:Count$Morbid.13.1 Oracle:Morbid — When this creature enters, target creature an opponent controls gets -1/-1 until end of turn. If a creature died this turn, that creature gets -13/-13 instead. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/t/treetop_snarespinner.txt b/forge-gui/res/cardsfolder/t/treetop_snarespinner.txt index 934cd0d0a0f..0cf35d78a89 100644 --- a/forge-gui/res/cardsfolder/t/treetop_snarespinner.txt +++ b/forge-gui/res/cardsfolder/t/treetop_snarespinner.txt @@ -1,9 +1,9 @@ -Name:Treetop Snarespinner -ManaCost:3 G -Types:Creature Spider -PT:1/4 -K:Reach -K:Deathtouch -A:AB$ PutCounter | Cost$ 2 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SorcerySpeed$ True | SpellDescription$ Put a +1/+1 counter on target creature you control. Activate only as a sorcery. -DeckHas:Ability$Counters +Name:Treetop Snarespinner +ManaCost:3 G +Types:Creature Spider +PT:1/4 +K:Reach +K:Deathtouch +A:AB$ PutCounter | Cost$ 2 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SorcerySpeed$ True | SpellDescription$ Put a +1/+1 counter on target creature you control. Activate only as a sorcery. +DeckHas:Ability$Counters Oracle:Reach (This creature can block creatures with flying.)\nDeathtouch (Any amount of damage this deals to a creature is enough to destroy it.)\n{2}{G}: Put a +1/+1 counter on target creature you control. Activate only as a sorcery. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/t/twinblade_blessing.txt b/forge-gui/res/cardsfolder/t/twinblade_blessing.txt index d86dc397963..9ea6157b213 100644 --- a/forge-gui/res/cardsfolder/t/twinblade_blessing.txt +++ b/forge-gui/res/cardsfolder/t/twinblade_blessing.txt @@ -1,8 +1,8 @@ -Name:Twinblade Blessing -ManaCost:1 W W -Types:Enchantment Aura -K:Flash -K:Enchant creature -A:SP$ Attach | Cost$ 1 W W | ValidTgts$ Creature | AILogic$ Pump -S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Double Strike | Description$ Enchanted creature has double strike. (It deals both first-strike and regular combat damage.) +Name:Twinblade Blessing +ManaCost:1 W W +Types:Enchantment Aura +K:Flash +K:Enchant creature +A:SP$ Attach | Cost$ 1 W W | ValidTgts$ Creature | AILogic$ Pump +S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddKeyword$ Double Strike | Description$ Enchanted creature has double strike. (It deals both first-strike and regular combat damage.) Oracle:Flash (You may cast this spell any time you could cast an instant.)\nEnchant creature\nEnchanted creature has double strike. (It deals both first-strike and regular combat damage.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/t/twinflame_tyrant.txt b/forge-gui/res/cardsfolder/t/twinflame_tyrant.txt index 2c5147cf9e9..62f6114d0e2 100644 --- a/forge-gui/res/cardsfolder/t/twinflame_tyrant.txt +++ b/forge-gui/res/cardsfolder/t/twinflame_tyrant.txt @@ -1,11 +1,11 @@ -Name:Twinflame Tyrant -ManaCost:3 R R -Types:Creature Dragon -PT:3/5 -K:Flying -R:Event$ DamageDone | ActiveZones$ Battlefield | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent,Permanent.OppCtrl | ReplaceWith$ DmgTwice | Description$ If a source you control would deal damage to an opponent or a permanent an opponent controls, it deals double that damage instead. -SVar:DmgTwice:DB$ ReplaceEffect | VarName$ DamageAmount | VarValue$ X -SVar:X:ReplaceCount$DamageAmount/Twice -SVar:PlayMain1:TRUE -AI:RemoveDeck:Random +Name:Twinflame Tyrant +ManaCost:3 R R +Types:Creature Dragon +PT:3/5 +K:Flying +R:Event$ DamageDone | ActiveZones$ Battlefield | ValidSource$ Card.YouCtrl,Emblem.YouCtrl | ValidTarget$ Opponent,Permanent.OppCtrl | ReplaceWith$ DmgTwice | Description$ If a source you control would deal damage to an opponent or a permanent an opponent controls, it deals double that damage instead. +SVar:DmgTwice:DB$ ReplaceEffect | VarName$ DamageAmount | VarValue$ X +SVar:X:ReplaceCount$DamageAmount/Twice +SVar:PlayMain1:TRUE +AI:RemoveDeck:Random Oracle:Flying\nIf a source you control would deal damage to an opponent or a permanent an opponent controls, it deals double that damage instead. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/u/uncharted_voyage.txt b/forge-gui/res/cardsfolder/u/uncharted_voyage.txt index 9e31a30f1bc..53df16b0c09 100644 --- a/forge-gui/res/cardsfolder/u/uncharted_voyage.txt +++ b/forge-gui/res/cardsfolder/u/uncharted_voyage.txt @@ -1,6 +1,6 @@ -Name:Uncharted Voyage -ManaCost:3 U -Types:Instant -A:SP$ ChangeZone | ValidTgts$ Creature | AlternativeDecider$ TargetedOwner | Origin$ Battlefield | Destination$ Library | DestinationAlternative$ Library | LibraryPositionAlternative$ -1 | SubAbility$ DBSurveil | StackDescription$ {p:TargetedOwner} puts {c:Targeted} on the top or bottom of their library. | SpellDescription$ Target creature's owner puts it on their choice of the top or bottom of their library. Surveil 1. (Look at the top card of your library. You may put it into your graveyard.) -SVar:DBSurveil:DB$ Surveil | Amount$ 1 +Name:Uncharted Voyage +ManaCost:3 U +Types:Instant +A:SP$ ChangeZone | ValidTgts$ Creature | AlternativeDecider$ TargetedOwner | Origin$ Battlefield | Destination$ Library | DestinationAlternative$ Library | LibraryPositionAlternative$ -1 | SubAbility$ DBSurveil | StackDescription$ {p:TargetedOwner} puts {c:Targeted} on the top or bottom of their library. | SpellDescription$ Target creature's owner puts it on their choice of the top or bottom of their library. Surveil 1. (Look at the top card of your library. You may put it into your graveyard.) +SVar:DBSurveil:DB$ Surveil | Amount$ 1 Oracle:Target creature's owner puts it on their choice of the top or bottom of their library.\nSurveil 1. (Look at the top card of your library. You may put it into your graveyard.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/u/unholy_annex_ritual_chamber.txt b/forge-gui/res/cardsfolder/u/unholy_annex_ritual_chamber.txt index adfa71edf78..f7a0eebe848 100644 --- a/forge-gui/res/cardsfolder/u/unholy_annex_ritual_chamber.txt +++ b/forge-gui/res/cardsfolder/u/unholy_annex_ritual_chamber.txt @@ -1,22 +1,22 @@ -Name:Unholy Annex -ManaCost:2 B -Types:Enchantment Room -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ At the beginning of your end step, draw a card. If you control a Demon, each opponent loses 2 life and you gain 2 life. Otherwise, you lose 2 life. -SVar:TrigDraw:DB$ Draw | SubAbility$ DBBranch -SVar:DBBranch:DB$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$ GT0 | TrueSubAbility$ DBLoseLife1 | FalseSubAbility$ DBLoseLife2 -SVar:DBLoseLife1:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 2 | SubAbility$ DBGainLife -SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 -SVar:DBLoseLife2:DB$ LoseLife | Defined$ You | LifeAmount$ 2 -SVar:X:Count$Valid Demon.YouCtrl -DeckHas:Ability$LifeGain -AlternateMode:Split -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nAt the beginning of your end step, draw a card. If you control a Demon, each opponent loses 2 life and you gain 2 life. Otherwise, you lose 2 life. - -ALTERNATE - -Name:Ritual Chamber -ManaCost:3 B B -Types:Enchantment Room -T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigToken | TriggerDescription$ When you unlock this door, create a 6/6 black Demon creature token with flying. -SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ b_6_6_demon_flying | TokenOwner$ You -Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, create a 6/6 black Demon creature token with flying. +Name:Unholy Annex +ManaCost:2 B +Types:Enchantment Room +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDraw | TriggerDescription$ At the beginning of your end step, draw a card. If you control a Demon, each opponent loses 2 life and you gain 2 life. Otherwise, you lose 2 life. +SVar:TrigDraw:DB$ Draw | SubAbility$ DBBranch +SVar:DBBranch:DB$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$ GT0 | TrueSubAbility$ DBLoseLife1 | FalseSubAbility$ DBLoseLife2 +SVar:DBLoseLife1:DB$ LoseLife | Defined$ Player.Opponent | LifeAmount$ 2 | SubAbility$ DBGainLife +SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 2 +SVar:DBLoseLife2:DB$ LoseLife | Defined$ You | LifeAmount$ 2 +SVar:X:Count$Valid Demon.YouCtrl +DeckHas:Ability$LifeGain +AlternateMode:Split +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nAt the beginning of your end step, draw a card. If you control a Demon, each opponent loses 2 life and you gain 2 life. Otherwise, you lose 2 life. + +ALTERNATE + +Name:Ritual Chamber +ManaCost:3 B B +Types:Enchantment Room +T:Mode$ UnlockDoor | ValidPlayer$ You | ValidCard$ Card.Self | ThisDoor$ True | Execute$ TrigToken | TriggerDescription$ When you unlock this door, create a 6/6 black Demon creature token with flying. +SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ b_6_6_demon_flying | TokenOwner$ You +Oracle:(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)\nWhen you unlock this door, create a 6/6 black Demon creature token with flying. diff --git a/forge-gui/res/cardsfolder/u/unidentified_hovership.txt b/forge-gui/res/cardsfolder/u/unidentified_hovership.txt index 5f1f7995005..38a492b6d1f 100644 --- a/forge-gui/res/cardsfolder/u/unidentified_hovership.txt +++ b/forge-gui/res/cardsfolder/u/unidentified_hovership.txt @@ -1,12 +1,12 @@ -Name:Unidentified Hovership -ManaCost:1 W W -Types:Artifact Vehicle -PT:2/2 -K:Flying -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When CARDNAME enters, exile up to one target creature with toughness 5 or less. -SVar:TrigChangeZone:DB$ ChangeZone | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature.toughnessLE5 | TgtPrompt$ Select target creature with toughness 5 or less. | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigDread | TriggerDescription$ When CARDNAME leaves the battlefield, the exiled card's owner manifests dread. -SVar:TrigDread:DB$ ManifestDread | DefinedPlayer$ RememberedOwner | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -K:Crew:1 -Oracle:Flying\nWhen Unidentified Hovership enters, exile up to one target creature with toughness 5 or less.\nWhen Unidentified Hovership leaves the battlefield, the exiled card's owner manifests dread.\nCrew 1 +Name:Unidentified Hovership +ManaCost:1 W W +Types:Artifact Vehicle +PT:2/2 +K:Flying +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When CARDNAME enters, exile up to one target creature with toughness 5 or less. +SVar:TrigChangeZone:DB$ ChangeZone | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature.toughnessLE5 | TgtPrompt$ Select target creature with toughness 5 or less. | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigDread | TriggerDescription$ When CARDNAME leaves the battlefield, the exiled card's owner manifests dread. +SVar:TrigDread:DB$ ManifestDread | DefinedPlayer$ RememberedOwner | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +K:Crew:1 +Oracle:Flying\nWhen Unidentified Hovership enters, exile up to one target creature with toughness 5 or less.\nWhen Unidentified Hovership leaves the battlefield, the exiled card's owner manifests dread.\nCrew 1 diff --git a/forge-gui/res/cardsfolder/u/unnerving_grasp.txt b/forge-gui/res/cardsfolder/u/unnerving_grasp.txt index d2acd38f625..ece49537fdc 100644 --- a/forge-gui/res/cardsfolder/u/unnerving_grasp.txt +++ b/forge-gui/res/cardsfolder/u/unnerving_grasp.txt @@ -1,6 +1,6 @@ -Name:Unnerving Grasp -ManaCost:2 U -Types:Sorcery -A:SP$ ChangeZone | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | Destination$ Hand | TargetMin$ 0 | TargetMax$ 1 | SubAbility$ DBDread | SpellDescription$ Return up to one target nonland permanent to its owner's hand. Manifest dread. (Look at the top two cards of your library. Put one onto the battlefield face down as a 2/2 creature and the other into your graveyard. Turn it face up any time for its mana cost if it's a creature card.) -SVar:DBDread:DB$ ManifestDread -Oracle:Return up to one target nonland permanent to its owner's hand. Manifest dread. (Look at the top two cards of your library. Put one onto the battlefield face down as a 2/2 creature and the other into your graveyard. Turn it face up any time for its mana cost if it's a creature card.) +Name:Unnerving Grasp +ManaCost:2 U +Types:Sorcery +A:SP$ ChangeZone | ValidTgts$ Permanent.nonLand | TgtPrompt$ Select target nonland permanent | Origin$ Battlefield | Destination$ Hand | TargetMin$ 0 | TargetMax$ 1 | SubAbility$ DBDread | SpellDescription$ Return up to one target nonland permanent to its owner's hand. Manifest dread. (Look at the top two cards of your library. Put one onto the battlefield face down as a 2/2 creature and the other into your graveyard. Turn it face up any time for its mana cost if it's a creature card.) +SVar:DBDread:DB$ ManifestDread +Oracle:Return up to one target nonland permanent to its owner's hand. Manifest dread. (Look at the top two cards of your library. Put one onto the battlefield face down as a 2/2 creature and the other into your graveyard. Turn it face up any time for its mana cost if it's a creature card.) diff --git a/forge-gui/res/cardsfolder/u/urdnan_dromoka_warrior.txt b/forge-gui/res/cardsfolder/u/urdnan_dromoka_warrior.txt index d03eea0dea9..084ab505a63 100644 --- a/forge-gui/res/cardsfolder/u/urdnan_dromoka_warrior.txt +++ b/forge-gui/res/cardsfolder/u/urdnan_dromoka_warrior.txt @@ -1,14 +1,14 @@ -Name:Urdnan, Dromoka Warrior -ManaCost:1 W -Types:Legendary Creature Human Warrior -PT:1/1 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters, put a +1/+1 counter on target creature. -SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 -T:Mode$ AttackersDeclared | AttackingPlayer$ You | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever you attack, target attacking creature with a +1/+1 counter on it gains first strike until end of turn. If that creature has two or more +1/+1 counters on it, it gains double strike until end of turn instead. -SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.attacking+counters_GE1_P1P1 | TgtPrompt$ Select target attacking creature with a +1/+1 counter on it | SubAbility$ DBBranch -SVar:DBBranch:DB$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$ GE2 | TrueSubAbility$ DBPump1 | FalseSubAbility$ DBPump2 -SVar:DBPump1:DB$ Pump | Defined$ Targeted | KW$ Double Strike -SVar:DBPump2:DB$ Pump | Defined$ Targeted | KW$ First Strike -SVar:X:Targeted$CardCounters.P1P1 -DeckHas:Ability$Counters -Oracle:When Urdnan, Dromoka Warrior enters, put a +1/+1 counter on target creature.\nWhenever you attack, target attacking creature with a +1/+1 counter on it gains first strike until end of turn. If that creature has two or more +1/+1 counters on it, it gains double strike until end of turn instead. +Name:Urdnan, Dromoka Warrior +ManaCost:1 W +Types:Legendary Creature Human Warrior +PT:1/1 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters, put a +1/+1 counter on target creature. +SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 +T:Mode$ AttackersDeclared | AttackingPlayer$ You | Execute$ TrigPump | TriggerZones$ Battlefield | TriggerDescription$ Whenever you attack, target attacking creature with a +1/+1 counter on it gains first strike until end of turn. If that creature has two or more +1/+1 counters on it, it gains double strike until end of turn instead. +SVar:TrigPump:DB$ Pump | ValidTgts$ Creature.attacking+counters_GE1_P1P1 | TgtPrompt$ Select target attacking creature with a +1/+1 counter on it | SubAbility$ DBBranch +SVar:DBBranch:DB$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$ GE2 | TrueSubAbility$ DBPump1 | FalseSubAbility$ DBPump2 +SVar:DBPump1:DB$ Pump | Defined$ Targeted | KW$ Double Strike +SVar:DBPump2:DB$ Pump | Defined$ Targeted | KW$ First Strike +SVar:X:Targeted$CardCounters.P1P1 +DeckHas:Ability$Counters +Oracle:When Urdnan, Dromoka Warrior enters, put a +1/+1 counter on target creature.\nWhenever you attack, target attacking creature with a +1/+1 counter on it gains first strike until end of turn. If that creature has two or more +1/+1 counters on it, it gains double strike until end of turn instead. diff --git a/forge-gui/res/cardsfolder/v/valkyries_call.txt b/forge-gui/res/cardsfolder/v/valkyries_call.txt index 0dc5a842a6e..e2ac0eb9dd8 100644 --- a/forge-gui/res/cardsfolder/v/valkyries_call.txt +++ b/forge-gui/res/cardsfolder/v/valkyries_call.txt @@ -1,7 +1,7 @@ -Name:Valkyrie's Call -ManaCost:3 W W -Types:Enchantment -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.nonToken+nonAngel+YouCtrl | TriggerZones$ Battlefield | Execute$ DBChangeZone | TriggerDescription$ Whenever a nontoken, non-Angel creature you control dies, return that card to the battlefield under its owner's control with a +1/+1 counter on it. It has flying and is an Angel in addition to its other types. -SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard | WithCountersType$ P1P1 | AnimateSubAbility$ DBAnimate -SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Keywords$ Flying | Types$ Angel | Duration$ Permanent -Oracle:Whenever a nontoken, non-Angel creature you control dies, return that card to the battlefield under its owner's control with a +1/+1 counter on it. It has flying and is an Angel in addition to its other types. +Name:Valkyrie's Call +ManaCost:3 W W +Types:Enchantment +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.nonToken+nonAngel+YouCtrl | TriggerZones$ Battlefield | Execute$ DBChangeZone | TriggerDescription$ Whenever a nontoken, non-Angel creature you control dies, return that card to the battlefield under its owner's control with a +1/+1 counter on it. It has flying and is an Angel in addition to its other types. +SVar:DBChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard | WithCountersType$ P1P1 | AnimateSubAbility$ DBAnimate +SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Keywords$ Flying | Types$ Angel | Duration$ Permanent +Oracle:Whenever a nontoken, non-Angel creature you control dies, return that card to the battlefield under its owner's control with a +1/+1 counter on it. It has flying and is an Angel in addition to its other types. diff --git a/forge-gui/res/cardsfolder/v/vampire_gourmand.txt b/forge-gui/res/cardsfolder/v/vampire_gourmand.txt index 6dae077e458..64781580cd6 100644 --- a/forge-gui/res/cardsfolder/v/vampire_gourmand.txt +++ b/forge-gui/res/cardsfolder/v/vampire_gourmand.txt @@ -1,9 +1,9 @@ -Name:Vampire Gourmand -ManaCost:1 B -Types:Creature Vampire -PT:2/2 -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever this creature attacks, you may sacrifice another creature. If you do, draw a card and this creature can't be blocked this turn. -SVar:TrigDraw:AB$ Draw | Cost$ Sac<1/Creature.Other/another creature> | Defined$ You | SubAbility$ DBUnblockable -SVar:DBUnblockable:DB$ Effect | RememberObjects$ Self | ExileOnMoved$ Battlefield | StaticAbilities$ Unblockable -SVar:Unblockable:Mode$ CantBlockBy | ValidAttacker$ Card.IsRemembered | Description$ EFFECTSOURCE can't be blocked this turn. +Name:Vampire Gourmand +ManaCost:1 B +Types:Creature Vampire +PT:2/2 +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever this creature attacks, you may sacrifice another creature. If you do, draw a card and this creature can't be blocked this turn. +SVar:TrigDraw:AB$ Draw | Cost$ Sac<1/Creature.Other/another creature> | Defined$ You | SubAbility$ DBUnblockable +SVar:DBUnblockable:DB$ Effect | RememberObjects$ Self | ExileOnMoved$ Battlefield | StaticAbilities$ Unblockable +SVar:Unblockable:Mode$ CantBlockBy | ValidAttacker$ Card.IsRemembered | Description$ EFFECTSOURCE can't be blocked this turn. Oracle:Whenever this creature attacks, you may sacrifice another creature. If you do, draw a card and this creature can't be blocked this turn. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/v/vampire_soulcaller.txt b/forge-gui/res/cardsfolder/v/vampire_soulcaller.txt index 7c075e3f4d1..414663fa576 100644 --- a/forge-gui/res/cardsfolder/v/vampire_soulcaller.txt +++ b/forge-gui/res/cardsfolder/v/vampire_soulcaller.txt @@ -1,9 +1,9 @@ -Name:Vampire Soulcaller -ManaCost:4 B -Types:Creature Vampire Warlock -PT:3/2 -K:Flying -K:CARDNAME can't block. -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, return target creature card from your graveyard to your hand. -SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Creature.YouOwn +Name:Vampire Soulcaller +ManaCost:4 B +Types:Creature Vampire Warlock +PT:3/2 +K:Flying +K:CARDNAME can't block. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When this creature enters, return target creature card from your graveyard to your hand. +SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Creature.YouOwn Oracle:Flying\nThis creature can't block.\nWhen this creature enters, return target creature card from your graveyard to your hand. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/v/vanguard_seraph.txt b/forge-gui/res/cardsfolder/v/vanguard_seraph.txt index f85f1c0c74e..2b92dbf4ad2 100644 --- a/forge-gui/res/cardsfolder/v/vanguard_seraph.txt +++ b/forge-gui/res/cardsfolder/v/vanguard_seraph.txt @@ -1,9 +1,9 @@ -Name:Vanguard Seraph -ManaCost:3 W -Types:Creature Angel Warrior -PT:3/3 -K:Flying -T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | FirstTime$ True | Execute$ TrigSurveil | TriggerDescription$ Whenever you gain life for the first time each turn, surveil 1. (Look at the top card of your library. You may put it into your graveyard.) -SVar:TrigSurveil:DB$ Surveil | Amount$ 1 -DeckHints:Ability$LifeGain +Name:Vanguard Seraph +ManaCost:3 W +Types:Creature Angel Warrior +PT:3/3 +K:Flying +T:Mode$ LifeGained | ValidPlayer$ You | TriggerZones$ Battlefield | FirstTime$ True | Execute$ TrigSurveil | TriggerDescription$ Whenever you gain life for the first time each turn, surveil 1. (Look at the top card of your library. You may put it into your graveyard.) +SVar:TrigSurveil:DB$ Surveil | Amount$ 1 +DeckHints:Ability$LifeGain Oracle:Flying\nWhenever you gain life for the first time each turn, surveil 1. (Look at the top card of your library. You may put it into your graveyard.) \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/w/wardens_of_the_cycle.txt b/forge-gui/res/cardsfolder/w/wardens_of_the_cycle.txt index d31b202b884..3cef896826d 100644 --- a/forge-gui/res/cardsfolder/w/wardens_of_the_cycle.txt +++ b/forge-gui/res/cardsfolder/w/wardens_of_the_cycle.txt @@ -1,11 +1,11 @@ -Name:Wardens of the Cycle -ManaCost:1 B G G -Types:Creature Elf Warlock -PT:3/4 -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | CheckSVar$ Morbid | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigCharm | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, ABILITY -SVar:TrigCharm:DB$ Charm | Choices$ DBGainLife,DBDraw | CharmNum$ 1 -SVar:DBGainLife:DB$ GainLife | LifeAmount$ 2 | Defined$ You | SpellDescription$ You gain 2 life. -SVar:DBDraw:DB$ Draw | SubAbility$ DBLoseLife | SpellDescription$ You draw a card and you lose 1 life. -SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 1 -SVar:Morbid:Count$Morbid.1.0 +Name:Wardens of the Cycle +ManaCost:1 B G G +Types:Creature Elf Warlock +PT:3/4 +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | CheckSVar$ Morbid | SVarCompare$ GE1 | TriggerZones$ Battlefield | Execute$ TrigCharm | TriggerDescription$ Morbid — At the beginning of your end step, if a creature died this turn, ABILITY +SVar:TrigCharm:DB$ Charm | Choices$ DBGainLife,DBDraw | CharmNum$ 1 +SVar:DBGainLife:DB$ GainLife | LifeAmount$ 2 | Defined$ You | SpellDescription$ You gain 2 life. +SVar:DBDraw:DB$ Draw | SubAbility$ DBLoseLife | SpellDescription$ You draw a card and you lose 1 life. +SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ 1 +SVar:Morbid:Count$Morbid.1.0 Oracle:Morbid — At the beginning of your end step, if a creature died this turn, choose one —\n• You gain 2 life.\n• You draw a card and you lose 1 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/w/winters_intervention.txt b/forge-gui/res/cardsfolder/w/winters_intervention.txt index 47757627732..ed233c8a098 100644 --- a/forge-gui/res/cardsfolder/w/winters_intervention.txt +++ b/forge-gui/res/cardsfolder/w/winters_intervention.txt @@ -1,6 +1,6 @@ -Name:Winter's Intervention -ManaCost:1 B -Types:Instant -A:SP$ DealDamage | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 2 | SubAbility$ DBGainLife | SpellDescription$ CARDNAME deals 2 damage to target creature. You gain 2 life. -SVar:DBGainLife:DB$ GainLife | LifeAmount$ 2 +Name:Winter's Intervention +ManaCost:1 B +Types:Instant +A:SP$ DealDamage | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumDmg$ 2 | SubAbility$ DBGainLife | SpellDescription$ CARDNAME deals 2 damage to target creature. You gain 2 life. +SVar:DBGainLife:DB$ GainLife | LifeAmount$ 2 Oracle:Winter's Intervention deals 2 damage to target creature. You gain 2 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/w/woodland_liege.txt b/forge-gui/res/cardsfolder/w/woodland_liege.txt index 67ff9049721..23843a257b3 100644 --- a/forge-gui/res/cardsfolder/w/woodland_liege.txt +++ b/forge-gui/res/cardsfolder/w/woodland_liege.txt @@ -1,8 +1,8 @@ -Name:Woodland Liege -ManaCost:2 G -Types:Creature Elf Druid Noble -PT:2/2 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Beast.YouCtrl | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever a Beast you control enters, draw a card. -SVar:TrigDraw:DB$ Draw -SVar:BuffedBy:Beast -Oracle:Whenever a Beast you control enters, draw a card. +Name:Woodland Liege +ManaCost:2 G +Types:Creature Elf Druid Noble +PT:2/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Beast.YouCtrl | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever a Beast you control enters, draw a card. +SVar:TrigDraw:DB$ Draw +SVar:BuffedBy:Beast +Oracle:Whenever a Beast you control enters, draw a card. diff --git a/forge-gui/res/cardsfolder/w/wriggling_grub.txt b/forge-gui/res/cardsfolder/w/wriggling_grub.txt index 4d905c73e5e..f8f4e320482 100644 --- a/forge-gui/res/cardsfolder/w/wriggling_grub.txt +++ b/forge-gui/res/cardsfolder/w/wriggling_grub.txt @@ -1,9 +1,9 @@ -Name:Wriggling Grub -ManaCost:1 B -Types:Creature Worm -PT:1/1 -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME dies, create two 1/1 black and green Worm creature tokens. -SVar:TrigToken:DB$ Token | TokenAmount$ 2 | TokenScript$ bg_1_1_worm -SVar:SacMe:4 -DeckHas:Ability$Token +Name:Wriggling Grub +ManaCost:1 B +Types:Creature Worm +PT:1/1 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME dies, create two 1/1 black and green Worm creature tokens. +SVar:TrigToken:DB$ Token | TokenAmount$ 2 | TokenScript$ bg_1_1_worm +SVar:SacMe:4 +DeckHas:Ability$Token Oracle:When Wriggling Grub dies, create two 1/1 black and green Worm creature tokens. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/y/your_plans_mean_nothing.txt b/forge-gui/res/cardsfolder/y/your_plans_mean_nothing.txt index ccce13c7ba8..56be9794cef 100644 --- a/forge-gui/res/cardsfolder/y/your_plans_mean_nothing.txt +++ b/forge-gui/res/cardsfolder/y/your_plans_mean_nothing.txt @@ -1,13 +1,13 @@ -Name:Your Plans Mean Nothing -ManaCost:no cost -Types:Scheme -T:Mode$ SetInMotion | ValidCard$ Card.Self | Execute$ DBChoosePlayer | TriggerZones$ Command | TriggerDescription$ When you set this scheme in motion, any number of target players each discard their hands. Each opponent draws cards equal to the number of cards that player discarded minus one. Then if you discarded a card this way, draw seven cards. -SVar:DBChoosePlayer:DB$ Pump | ValidTgts$ Player | TargetMin$ 0 | TargetMax$ PlayerCount | TgtPrompt$ Choose any number of players | SubAbility$ DBRepeat -SVar:DBRepeat:DB$ RepeatEach | RepeatPlayers$ Targeted | RepeatSubAbility$ DBDiscard | SubAbility$ DBYouDraw | SpellDescription$ Each opponent draws cards equal to the number of cards that player discarded minus one. Then if you discarded a card this way, draw seven cards. -SVar:DBDiscard:DB$ Discard | Defined$ Remembered | Mode$ Hand | RememberDiscarded$ True | SubAbility$ DBDraw -SVar:DBDraw:DB$ Draw | NumCards$ X | Defined$ Player.Opponent+IsRemembered -SVar:DBYouDraw:DB$ Draw | Defined$ You | NumCards$ 7 | ConditionDefined$ Remembered | ConditionPresent$ Card.YouOwn | SubAbility$ DBClearRemembered -SVar:DBClearRemembered:DB$ Cleanup | ClearRemembered$ True -SVar:PlayerCount:PlayerCountPlayers$Amount -SVar:X:Remembered$Valid Card.RememberedPlayerOwn/Minus.1 -Oracle:When you set this scheme in motion, any number of target players each discard their hands. Each opponent draws cards equal to the number of cards that player discarded minus one. Then if you discarded a card this way, draw seven cards. +Name:Your Plans Mean Nothing +ManaCost:no cost +Types:Scheme +T:Mode$ SetInMotion | ValidCard$ Card.Self | Execute$ DBChoosePlayer | TriggerZones$ Command | TriggerDescription$ When you set this scheme in motion, any number of target players each discard their hands. Each opponent draws cards equal to the number of cards that player discarded minus one. Then if you discarded a card this way, draw seven cards. +SVar:DBChoosePlayer:DB$ Pump | ValidTgts$ Player | TargetMin$ 0 | TargetMax$ PlayerCount | TgtPrompt$ Choose any number of players | SubAbility$ DBRepeat +SVar:DBRepeat:DB$ RepeatEach | RepeatPlayers$ Targeted | RepeatSubAbility$ DBDiscard | SubAbility$ DBYouDraw | SpellDescription$ Each opponent draws cards equal to the number of cards that player discarded minus one. Then if you discarded a card this way, draw seven cards. +SVar:DBDiscard:DB$ Discard | Defined$ Remembered | Mode$ Hand | RememberDiscarded$ True | SubAbility$ DBDraw +SVar:DBDraw:DB$ Draw | NumCards$ X | Defined$ Player.Opponent+IsRemembered +SVar:DBYouDraw:DB$ Draw | Defined$ You | NumCards$ 7 | ConditionDefined$ Remembered | ConditionPresent$ Card.YouOwn | SubAbility$ DBClearRemembered +SVar:DBClearRemembered:DB$ Cleanup | ClearRemembered$ True +SVar:PlayerCount:PlayerCountPlayers$Amount +SVar:X:Remembered$Valid Card.RememberedPlayerOwn/Minus.1 +Oracle:When you set this scheme in motion, any number of target players each discard their hands. Each opponent draws cards equal to the number of cards that player discarded minus one. Then if you discarded a card this way, draw seven cards. diff --git a/forge-gui/res/cardsfolder/z/zimone_paradox_sculptor.txt b/forge-gui/res/cardsfolder/z/zimone_paradox_sculptor.txt index b607acf8b78..ada0588cb5f 100644 --- a/forge-gui/res/cardsfolder/z/zimone_paradox_sculptor.txt +++ b/forge-gui/res/cardsfolder/z/zimone_paradox_sculptor.txt @@ -1,9 +1,9 @@ -Name:Zimone, Paradox Sculptor -ManaCost:2 G U -Types:Legendary Creature Human Wizard -PT:1/4 -T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of combat on your turn, put a +1/+1 counter on each of up to two target creatures you control. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TargetMin$ 0 | TargetMax$ 2 | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1 -A:AB$ MultiplyCounter | Cost$ G U T | ValidTgts$ Creature.YouCtrl,Artifact.YouCtrl | TgtPrompt$ Select target creature or artifact you control | TargetMin$ 0 | TargetMax$ 2 | StackDescription$ SpellDescription | SpellDescription$ Double the number of each kind of counter on up to two target creatures and/or artifacts you control. -DeckHas:Ability$Counters +Name:Zimone, Paradox Sculptor +ManaCost:2 G U +Types:Legendary Creature Human Wizard +PT:1/4 +T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ At the beginning of combat on your turn, put a +1/+1 counter on each of up to two target creatures you control. +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TargetMin$ 0 | TargetMax$ 2 | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1 +A:AB$ MultiplyCounter | Cost$ G U T | ValidTgts$ Creature.YouCtrl,Artifact.YouCtrl | TgtPrompt$ Select target creature or artifact you control | TargetMin$ 0 | TargetMax$ 2 | StackDescription$ SpellDescription | SpellDescription$ Double the number of each kind of counter on up to two target creatures and/or artifacts you control. +DeckHas:Ability$Counters Oracle:At the beginning of combat on your turn, put a +1/+1 counter on each of up to two target creatures you control.\n{G}{U}, {T}: Double the number of each kind of counter on up to two target creatures and/or artifacts you control. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/z/zul_ashur_lich_lord.txt b/forge-gui/res/cardsfolder/z/zul_ashur_lich_lord.txt index fc6db3d3631..3ba40ecde9a 100644 --- a/forge-gui/res/cardsfolder/z/zul_ashur_lich_lord.txt +++ b/forge-gui/res/cardsfolder/z/zul_ashur_lich_lord.txt @@ -1,8 +1,8 @@ -Name:Zul Ashur, Lich Lord -ManaCost:1 B -Types:Legendary Creature Zombie Warlock -PT:2/2 -K:Ward:PayLife<2> -A:AB$ Effect | Cost$ T | TgtZone$ Graveyard | ValidTgts$ Creature.Zombie+YouOwn | PumpZone$ Graveyard | TgtPrompt$ Select target Zombie creature card in your graveyard | RememberObjects$ Targeted | StaticAbilities$ Play | ExileOnMoved$ Graveyard | SpellDescription$ You may cast target Zombie creature card from your graveyard this turn. -SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ You may play remembered card. +Name:Zul Ashur, Lich Lord +ManaCost:1 B +Types:Legendary Creature Zombie Warlock +PT:2/2 +K:Ward:PayLife<2> +A:AB$ Effect | Cost$ T | TgtZone$ Graveyard | ValidTgts$ Creature.Zombie+YouOwn | PumpZone$ Graveyard | TgtPrompt$ Select target Zombie creature card in your graveyard | RememberObjects$ Targeted | StaticAbilities$ Play | ExileOnMoved$ Graveyard | SpellDescription$ You may cast target Zombie creature card from your graveyard this turn. +SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ You may play remembered card. Oracle:Ward—Pay 2 life. (Whenever this creature becomes the target of a spell or ability an opponent controls, counter it unless that player pays 2 life.)\n{T}: You may cast target Zombie creature card from your graveyard this turn. \ No newline at end of file From c20898ececf528d2ac6b276432f8261563d9c45e Mon Sep 17 00:00:00 2001 From: tool4ever Date: Sun, 24 Nov 2024 12:43:57 +0100 Subject: [PATCH 103/152] Fix Offspring not triggering for AI (#6622) --- .../src/main/java/forge/game/GameActionUtil.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 9290dcf2dbd..9f29d642257 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -711,7 +711,15 @@ public final class GameActionUtil { host.getGame().getTriggerHandler().resetActiveTriggers(false); } - return result != null ? result : sa; + if (result != null) { + // sanity check if need to update castSA + if (sa.getHostCard().getCastSA() == sa) { + sa.getHostCard().setCastSA(result); + } + return result; + } + + return sa; } public static Card createETBCountersEffect(Card sourceCard, Card c, Player controller, String counter, String amount) { From 46f3f01450d6a188d279e25b1993c96bbcb4cb91 Mon Sep 17 00:00:00 2001 From: Ayora29 Date: Sun, 24 Nov 2024 14:39:21 +0100 Subject: [PATCH 104/152] Update draftable edition sections (#6274) * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix art index when parsing edition files sections Add "scheme" section Add fallback sheet in BoosterGenerator Rewrite FileSection and Use LinkedHashMap for sections map * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Convert MKM to BoosterSlots * Rename "RareMythic" section * Remove conjured cards that do not have unique artwork. * Restore basic land order --- .../src/main/java/forge/card/CardEdition.java | 27 +- .../item/generation/BoosterGenerator.java | 19 +- .../src/main/java/forge/util/FileSection.java | 223 ++----- .../java/forge/util/FileSectionManual.java | 2 +- forge-gui/res/blockdata/printsheets.txt | 587 ------------------ .../res/editions/30th Anniversary Edition.txt | 309 +-------- .../Alchemy Horizons Baldur's Gate.txt | 31 +- forge-gui/res/editions/Amonkhet.txt | 6 +- .../res/editions/Battle for Zendikar.txt | 4 +- forge-gui/res/editions/Battlebond.txt | 74 ++- forge-gui/res/editions/Bloomburrow.txt | 30 +- ...ander Legends Battle for Baldur's Gate.txt | 42 +- forge-gui/res/editions/Commander Legends.txt | 2 +- .../editions/Conspiracy Take the Crown.txt | 4 +- .../res/editions/Dominaria Remastered.txt | 146 +---- forge-gui/res/editions/Dominaria United.txt | 10 +- forge-gui/res/editions/Dominaria.txt | 6 +- .../res/editions/Double Masters 2022.txt | 2 +- forge-gui/res/editions/Double Masters.txt | 6 +- forge-gui/res/editions/Guilds of Ravnica.txt | 18 +- .../res/editions/Hour of Devastation.txt | 6 +- .../res/editions/Innistrad Crimson Vow.txt | 2 + .../res/editions/Innistrad Midnight Hunt.txt | 16 +- .../res/editions/Kamigawa Neon Dynasty.txt | 2 + forge-gui/res/editions/Khans of Tarkir.txt | 2 +- forge-gui/res/editions/Magic 2015.txt | 4 +- forge-gui/res/editions/Magic 2021.txt | 12 +- forge-gui/res/editions/Magic Origins.txt | 4 +- .../March of the Machine The Aftermath.txt | 12 + .../Masterpiece Series - Amonkhet.txt | 4 + forge-gui/res/editions/Masters Edition IV.txt | 16 +- forge-gui/res/editions/Modern Horizons 2.txt | 8 +- forge-gui/res/editions/Modern Horizons 3.txt | 41 +- forge-gui/res/editions/Modern Horizons.txt | 4 +- .../res/editions/Murders at Karlov Manor.txt | 35 +- .../res/editions/Oath of the Gatewatch.txt | 6 +- .../editions/Outlaws of Thunder Junction.txt | 30 +- .../res/editions/Phyrexia All Will Be One.txt | 14 +- forge-gui/res/editions/Portal.txt | 52 +- forge-gui/res/editions/Ravnica Allegiance.txt | 13 +- forge-gui/res/editions/Ravnica Remastered.txt | 2 +- .../res/editions/Streets of New Capenna.txt | 2 + forge-gui/res/editions/The Big Score.txt | 4 + forge-gui/res/editions/The Brothers War.txt | 10 +- ...ord of the Rings Tales of Middle-earth.txt | 20 +- .../res/editions/Time Spiral Remastered.txt | 4 +- forge-gui/res/editions/Unglued.txt | 2 +- forge-gui/res/editions/Unhinged.txt | 4 +- forge-gui/res/editions/Unstable.txt | 528 ++++++++-------- forge-gui/res/editions/War of the Spark.txt | 82 +-- .../res/editions/Zendikar Expeditions.txt | 4 + forge-gui/res/editions/Zendikar Rising.txt | 2 + 52 files changed, 780 insertions(+), 1715 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardEdition.java b/forge-core/src/main/java/forge/card/CardEdition.java index 96c6caf33dc..ea945205f75 100644 --- a/forge-core/src/main/java/forge/card/CardEdition.java +++ b/forge-core/src/main/java/forge/card/CardEdition.java @@ -54,24 +54,19 @@ public final class CardEdition implements Comparable { // immutable public enum Type { UNKNOWN, - CORE, EXPANSION, STARTER, REPRINT, BOXED_SET, - COLLECTOR_EDITION, DUEL_DECK, PROMO, ONLINE, - DRAFT, - COMMANDER, MULTIPLAYER, FUNNY, - OTHER, // FALLBACK CATEGORY CUSTOM_SET; // custom sets @@ -131,22 +126,23 @@ public final class CardEdition implements Comparable { SPECIAL_SLOT("special slot"), //to help with convoluted boosters PRECON_PRODUCT("precon product"), BORDERLESS("borderless"), - BORDERLESS_PROFILE("borderless profile"), - BORDERLESS_FRAME("borderless frame"), ETCHED("etched"), SHOWCASE("showcase"), FULL_ART("full art"), EXTENDED_ART("extended art"), ALTERNATE_ART("alternate art"), - ALTERNATE_FRAME("alternate frame"), + RETRO_FRAME("retro frame"), BUY_A_BOX("buy a box"), PROMO("promo"), + PRERELEASE_PROMO("prerelease promo"), BUNDLE("bundle"), BOX_TOPPER("box topper"), DUNGEONS("dungeons"), JUMPSTART("jumpstart"), REBALANCED("rebalanced"), - ETERNAL("eternal"); + ETERNAL("eternal"), + CONJURED("conjured"), + SCHEME("scheme"); private final String name; @@ -215,7 +211,7 @@ public final class CardEdition implements Comparable { */ public static String getSortableCollectorNumber(final String collectorNumber){ String inputCollNumber = collectorNumber; - if (collectorNumber == null || collectorNumber.length() == 0) + if (collectorNumber == null || collectorNumber.isEmpty()) inputCollNumber = "50000"; // very big number of 5 digits to have them in last positions String matchedCollNr = sortableCollNumberLookup.getOrDefault(inputCollNumber, null); @@ -379,7 +375,6 @@ public final class CardEdition implements Comparable { public String getSlotReplaceCommonWith() { return slotReplaceCommonWith; } public String getAdditionalSheetForFoils() { return additionalSheetForFoils; } public String getAdditionalUnlockSet() { return additionalUnlockSet; } - public boolean getSmallSetOverride() { return smallSetOverride; } public String getDoublePickDuringDraft() { return doublePickDuringDraft; } public String getBoosterMustContain() { return boosterMustContain; } public String getBoosterReplaceSlotFromPrintSheet() { return boosterReplaceSlotFromPrintSheet; } @@ -514,7 +509,7 @@ public final class CardEdition implements Comparable { for (CardInSet card : cards) { int index = 1; if (cardToIndex.containsKey(card.name)) { - index = cardToIndex.get(card.name); + index = cardToIndex.get(card.name) + 1; } cardToIndex.put(card.name, index); @@ -709,9 +704,6 @@ public final class CardEdition implements Comparable { res.fatPackExtraSlots = metadata.get("FatPackExtraSlots", ""); switch (metadata.get("foil", "newstyle").toLowerCase()) { - case "notsupported": - res.foilType = FoilType.NOT_SUPPORTED; - break; case "oldstyle": case "classic": res.foilType = FoilType.OLD_STYLE; @@ -720,6 +712,7 @@ public final class CardEdition implements Comparable { case "modern": res.foilType = FoilType.MODERN; break; + case "notsupported": default: res.foilType = FoilType.NOT_SUPPORTED; break; @@ -774,7 +767,7 @@ public final class CardEdition implements Comparable { public void add(CardEdition item) { //Even though we want it to be read only, make an exception for custom content. if(lock) throw new UnsupportedOperationException("This is a read-only storage"); else map.put(item.getName(), item); - }; + } public void append(CardEdition.Collection C){ //Append custom editions if (lock) throw new UnsupportedOperationException("This is a read-only storage"); for(CardEdition E : C){ //Update the alias list as above or else it'll fail to look up. @@ -908,7 +901,7 @@ public final class CardEdition implements Comparable { StaticData.instance().getEditions().getOrderedEditions(), com.google.common.base.Predicates.and(hasBasicLands, artPreference::accept)); Iterator editionsIterator = editionsWithBasicLands.iterator(); - List selectedEditions = new ArrayList(); + List selectedEditions = new ArrayList<>(); while (editionsIterator.hasNext()) selectedEditions.add(editionsIterator.next()); if (selectedEditions.isEmpty()) diff --git a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java index 812dd7ff779..ec1ee50aba4 100644 --- a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java +++ b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java @@ -69,7 +69,6 @@ public class BoosterGenerator { } List result = new ArrayList<>(); - List sheetsUsed = new ArrayList<>(); CardEdition edition = StaticData.instance().getEditions().get(template.getEdition()); @@ -233,7 +232,6 @@ public class BoosterGenerator { if (sheetKey.startsWith("wholeSheet")) { PrintSheet ps = getPrintSheet(sheetKey); result.addAll(ps.all()); - sheetsUsed.add(ps); continue; } @@ -252,7 +250,7 @@ public class BoosterGenerator { if ((edition.getName().equals("Planeshift")) && (slotType.startsWith(BoosterSlots.RARE)) && (foilSlot.startsWith(BoosterSlots.SPECIAL)) - ) { + ) { numCards--; } } @@ -264,7 +262,6 @@ public class BoosterGenerator { : edition.getSlotReplaceCommonWith().trim(); PrintSheet replaceSheet = getPrintSheet(replaceKey); result.addAll(replaceSheet.random(1, true)); - sheetsUsed.add(replaceSheet); System.out.println("Common was replaced with something from the replace sheet..."); replaceCommon = false; } @@ -283,7 +280,6 @@ public class BoosterGenerator { } result.addAll(paperCards); - sheetsUsed.add(ps); if (foilInThisSlot) { if (!foilAtEndOfPack) { @@ -395,8 +391,6 @@ public class BoosterGenerator { public static List getBoosterPack(SealedTemplateWithSlots template) { // SealedTemplateWithSlots ignores all Edition level params // Instead each slot defines their percentages on their own - - CardEdition edition = StaticData.instance().getEditions().get(template.getEdition()); List result = new ArrayList<>(); Map boosterSlots = template.getNamedSlots(); @@ -501,7 +495,7 @@ public class BoosterGenerator { * Replaces an already present card in the booster with a card from the supplied print sheet. * Nothing is replaced if there is no matching rarity found. * @param booster in which a card gets replaced - * @param printSheetKey + * @param printSheetKey print sheet key from which take the replacement card */ public static void replaceCardFromExtraSheet(List booster, String printSheetKey) { PrintSheet replacementSheet = StaticData.instance().getPrintSheets().get(printSheetKey); @@ -516,7 +510,7 @@ public class BoosterGenerator { * @param toAdd new card which replaces a card in the booster */ public static void replaceCard(List booster, PaperCard toAdd) { - Predicate rarityPredicate = null; + Predicate rarityPredicate; switch (toAdd.getRarity()) { case BasicLand: rarityPredicate = Presets.IS_BASIC_LAND; @@ -573,11 +567,10 @@ public class BoosterGenerator { } } - @SuppressWarnings("unchecked") public static PrintSheet makeSheet(String sheetKey, Iterable src) { PrintSheet ps = new PrintSheet(sheetKey); String[] sKey = TextUtil.splitWithParenthesis(sheetKey, ' ', 2); - Predicate setPred = (Predicate) (sKey.length > 1 ? IPaperCard.Predicates.printedInSets(sKey[1].split(" ")) : Predicates.alwaysTrue()); + Predicate setPred = (sKey.length > 1 ? IPaperCard.Predicates.printedInSets(sKey[1].split(" ")) : Predicates.alwaysTrue()); List operators = new LinkedList<>(Arrays.asList(TextUtil.splitWithParenthesis(sKey[0], ':'))); Predicate extraPred = buildExtraPredicate(operators); @@ -679,11 +672,11 @@ public class BoosterGenerator { Predicate toAdd = null; if (operator.equalsIgnoreCase(BoosterSlots.DUAL_FACED_CARD)) { toAdd = Predicates.compose( - Predicates.or( + Predicates.or( CardRulesPredicates.splitType(CardSplitType.Transform), CardRulesPredicates.splitType(CardSplitType.Meld), CardRulesPredicates.splitType(CardSplitType.Modal) - ), + ), PaperCard::getRules); } else if (operator.equalsIgnoreCase(BoosterSlots.LAND)) { toAdd = Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard::getRules); } else if (operator.equalsIgnoreCase(BoosterSlots.BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND; diff --git a/forge-core/src/main/java/forge/util/FileSection.java b/forge-core/src/main/java/forge/util/FileSection.java index e4db0e9dd5d..dfa8eb58bca 100644 --- a/forge-core/src/main/java/forge/util/FileSection.java +++ b/forge-core/src/main/java/forge/util/FileSection.java @@ -19,12 +19,7 @@ package forge.util; import java.text.NumberFormat; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; @@ -33,73 +28,60 @@ import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; /** - * TODO: Write javadoc for this type. - * + * Parse text file to extract [sections] and key/value data. + * Store the result in a HashMap */ public class FileSection { /** The lines. */ - private final Map lines; - - /** - * Gets the lines. - * - * @return the lines - */ - protected final Map getLines() { - return this.lines; - } + protected final Map lines; /** * Instantiates a new file section. */ protected FileSection() { - this(new TreeMap<>(String.CASE_INSENSITIVE_ORDER)); - } - - protected FileSection(Map lines0) { - lines = lines0; + lines = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } public static final Pattern DOLLAR_SIGN_KV_SEPARATOR = Pattern.compile(Pattern.quote("$")); public static final Pattern ARROW_KV_SEPARATOR = Pattern.compile(Pattern.quote("->")); public static final Pattern EQUALS_KV_SEPARATOR = Pattern.compile(Pattern.quote("=")); public static final Pattern COLON_KV_SEPARATOR = Pattern.compile(Pattern.quote(":")); - private static final String BAR_PAIR_SPLITTER = Pattern.quote("|"); - private static Table> parseToMapCache = HashBasedTable.create(); + private static final Table> parseToMapCache = HashBasedTable.create(); + /** + * Parses the key=value text line and return a HashMap + * + * @param line the text line to parse + * @param kvSeparator the key/value separator + * @return a HashMap + */ public static Map parseToMap(final String line, final Pattern kvSeparator) { - Map result = parseToMapCache.get(line, kvSeparator); - if (result != null) { - return result; - } - result = parseToMapImpl(line, kvSeparator); - parseToMapCache.put(line, kvSeparator, result); - return result; - } - - private static Map parseToMapImpl(final String line, final Pattern kvSeparator) { - if (StringUtils.isEmpty(line)) { - return Collections.emptyMap(); + Map cached = parseToMapCache.get(line, kvSeparator); + if (cached != null) { + return cached; } - final Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - final String[] pairs = line.split(BAR_PAIR_SPLITTER); - for (final String dd : pairs) { - final String[] v = kvSeparator.split(dd, 2); - result.put(v[0].trim(), v.length > 1 ? v[1].trim() : ""); + Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + if (!StringUtils.isEmpty(line)) { + for (final String dd : line.split(BAR_PAIR_SPLITTER)) { + final String[] v = kvSeparator.split(dd, 2); + result.put(v[0].trim(), v.length > 1 ? v[1].trim() : ""); + } } - return Collections.unmodifiableMap(result); + cached = Collections.unmodifiableMap(result); + parseToMapCache.put(line, kvSeparator, cached); + return cached; } /** - * Parses the. + * Parses the key=value text lines and return a HashMap * - * @param lines the lines - * @param kvSeparator the kv separator - * @return the file section + * @param lines the text lines to parse + * @param kvSeparator the key/value separator + * @return a FileSection Object containing the HashMap */ public static FileSection parse(final Iterable lines, final Pattern kvSeparator) { final FileSection result = new FileSection(); @@ -112,11 +94,36 @@ public class FileSection { } /** - * Gets the. + * Parses the sections ([sectionName]) from a list of text line * - * @param fieldName the field name - * @return the string + * @param lines + * the text lines to parse + * @return a LinkedHashMap containing the sections and text line associated. The order of the sections is preserved */ + public static Map> parseSections(final List lines) { + final Map> result = new LinkedHashMap<>(); + int lineNumber = 0; + String section = null; + + do{ + String line = lines.get(lineNumber++); + if (line.startsWith("[") && line.endsWith("]")) { + section = line.substring(1, line.length() - 1); + if(!result.containsKey(section)) { + result.put(section, new ArrayList<>()); + } + } + else if(null != section && !line.isEmpty() && !line.startsWith("#")){ + result.get(section).add(line); + } + } + while(lineNumber> parseSections(final List source) { - final Map> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - String currentSection = ""; - List currentList = null; - - for (final String s : source) { - final String st = s.trim(); - if (st.length() == 0) { - continue; - } - if (st.startsWith("[") && st.endsWith("]")) { - if ((currentList != null) && (currentList.size() > 0)) { - final Object oldVal = result.get(currentSection); - if ((oldVal != null) && (oldVal instanceof List)) { - currentList.addAll((List) oldVal); - } - result.put(currentSection, currentList); - } - - final String newSection = st.substring(1, st.length() - 1); - currentSection = newSection; - currentList = null; - } else { - if (currentList == null) { - currentList = new ArrayList<>(); - } - currentList.add(st); - } - } - - // save final block - if ((currentList != null) && (currentList.size() > 0)) { - final Object oldVal = result.get(currentSection); - if ((oldVal != null) && (oldVal instanceof List)) { - currentList.addAll((List) oldVal); - } - result.put(currentSection, currentList); - } - - return result; - } - } diff --git a/forge-core/src/main/java/forge/util/FileSectionManual.java b/forge-core/src/main/java/forge/util/FileSectionManual.java index 5a7a6c58dfe..999e82f42c1 100644 --- a/forge-core/src/main/java/forge/util/FileSectionManual.java +++ b/forge-core/src/main/java/forge/util/FileSectionManual.java @@ -30,7 +30,7 @@ public class FileSectionManual extends FileSection { * @param value the value */ public void put(final String key, final String value) { - this.getLines().put(key, value); + this.lines.put(key, value); } } diff --git a/forge-gui/res/blockdata/printsheets.txt b/forge-gui/res/blockdata/printsheets.txt index 8d892d27cbb..be76601b822 100644 --- a/forge-gui/res/blockdata/printsheets.txt +++ b/forge-gui/res/blockdata/printsheets.txt @@ -635,41 +635,6 @@ 1 Wayfaring Temple|RTR 1 Wild Beastmaster|RTR -[M15 Sample Cards] -1 Aegis Angel|M15 -1 Cancel|M15 -1 Centaur Courser|M15 -1 Divine Verdict|M15 -1 Furnace Whelp|M15 -1 Garruk's Packleader|M15 -1 Inspired Charge|M15 -1 Mahamoti Djinn|M15 -1 Nightmare|M15 -1 Seismic Strike|M15 -1 Sengir Vampire|M15 -1 Serra Angel|M15 -1 Shivan Dragon|M15 -1 Terra Stomper|M15 -1 Walking Corpse|M15 - -[ORI Sample Cards] -1 Aegis Angel|ORI -1 Divine Verdict|ORI -1 Eagle of the Watch|ORI -1 Serra Angel|ORI -1 Into the Void|ORI -1 Mahamoti Djinn|ORI -1 Weave Fate|ORI -1 Flesh to Dust|ORI -1 Mind Rot|ORI -1 Nightmare|ORI -1 Sengir Vampire|ORI -1 Fiery Hellhound|ORI -1 Shivan Dragon|ORI -1 Plummet|ORI -1 Prized Unicorn|ORI -1 Terra Stomper|ORI - [ORI Gideon] Akroan Jailer|ORI Ampryn Tactician|ORI @@ -889,558 +854,6 @@ Woodland Bellower|ORI Yeva's Forcemage|ORI Zendikar's Roil|ORI -[EXP Lands] -Prairie Stream|EXP -Sunken Hollow|EXP -Smoldering Marsh|EXP -Cinder Glade|EXP -Canopy Vista|EXP -Hallowed Fountain|EXP -Watery Grave|EXP -Blood Crypt|EXP -Stomping Ground|EXP -Temple Garden|EXP -Godless Shrine|EXP -Steam Vents|EXP -Overgrown Tomb|EXP -Sacred Foundry|EXP -Breeding Pool|EXP -Flooded Strand|EXP -Polluted Delta|EXP -Bloodstained Mire|EXP -Wooded Foothills|EXP -Windswept Heath|EXP -Marsh Flats|EXP -Scalding Tarn|EXP -Verdant Catacombs|EXP -Arid Mesa|EXP -Misty Rainforest|EXP - -[EXP Lands 2] -Mystic Gate|EXP -Sunken Ruins|EXP -Graven Cairns|EXP -Fire-Lit Thicket|EXP -Wooded Bastion|EXP -Fetid Heath|EXP -Cascade Bluffs|EXP -Twilight Mire|EXP -Rugged Prairie|EXP -Flooded Grove|EXP -Ancient Tomb|EXP -Dust Bowl|EXP -Eye of Ugin|EXP -Forbidden Orchard|EXP -Horizon Canopy|EXP -Kor Haven|EXP -Mana Confluence|EXP -Strip Mine|EXP -Tectonic Edge|EXP -Wasteland|EXP - -[MPS Amonkhet Invocations] -Austere Command|MPS_AKH -Aven Mindcensor|MPS_AKH -Containment Priest|MPS_AKH -Loyal Retainers|MPS_AKH -Oketra the True|MPS_AKH -Worship|MPS_AKH -Wrath of God|MPS_AKH -Consecrated Sphinx|MPS_AKH -Counterbalance|MPS_AKH -Counterspell|MPS_AKH -Cryptic Command|MPS_AKH -Daze|MPS_AKH -Divert|MPS_AKH -Force of Will|MPS_AKH -Kefnet the Mindful|MPS_AKH -Pact of Negation|MPS_AKH -Spell Pierce|MPS_AKH -Stifle|MPS_AKH -Attrition|MPS_AKH -Bontu the Glorified|MPS_AKH -Dark Ritual|MPS_AKH -Diabolic Intent|MPS_AKH -Entomb|MPS_AKH -Mind Twist|MPS_AKH -Aggravated Assault|MPS_AKH -Chain Lightning|MPS_AKH -Hazoret the Fervent|MPS_AKH -Rhonas the Indomitable|MPS_AKH -Maelstrom Pulse|MPS_AKH -Vindicate|MPS_AKH - -[MPS Hour of Devastation Invocations] -Armageddon|MPS_AKH -Capsize|MPS_AKH -Forbid|MPS_AKH -Omniscience|MPS_AKH -Opposition|MPS_AKH -Sunder|MPS_AKH -Threads of Disloyalty|MPS_AKH -Avatar of Woe|MPS_AKH -Damnation|MPS_AKH -Desolation Angel|MPS_AKH -Diabolic Edict|MPS_AKH -Doomsday|MPS_AKH -No Mercy|MPS_AKH -Slaughter Pact|MPS_AKH -Thoughtseize|MPS_AKH -Blood Moon|MPS_AKH -Boil|MPS_AKH -Shatterstorm|MPS_AKH -Through the Breach|MPS_AKH -Choke|MPS_AKH -The Locust God|MPS_AKH -Lord of Extinction|MPS_AKH -The Scarab God|MPS_AKH -The Scorpion God|MPS_AKH - -[AKH Planeswalker Decks and Toolkit] -Gideon, Martial Paragon -Companion of the Trials -Gideon's Resolve -Graceful Cat -Stone Quarry -Liliana, Death Wielder -Desiccated Naga -Liliana's Influence -Tattered Mummy -Foul Orchard -Cinder Barrens -Forsaken Sanctuary -Highland Lake -Meandering River -Submerged Boneyard -Timber Gorge -Tranquil Expanse -Woodland Stream - -[HOU Planeswalker Decks and Toolkit] -Nissa, Genesis Mage -Avid Reclaimer -Brambleweft Behemoth -Nissa's Encouragement -Woodland Stream -Nicol Bolas, the Deceiver -Wasp of the Bitter End -Zealot of the God-Pharaoh -Visage of Bolas -Cinder Barrens - -[DOM Planeswalker Decks and Additional Promo] -Teferi, Timebender -Temporal Machinations -Niambi, Faithful Healer -Teferi's Sentinel -Meandering River -Chandra, Bold Pyromancer -Chandra's Outburst -Karplusan Hound -Pyromantic Pilgrim -Timber Gorge -Firesong and Sunspeaker - -[BBD RareMythic] -1 Will Kenrith|BBD|1 -1 Rowan Kenrith|BBD|1 -8 Regna, the Redeemer -8 Krav, the Unredeemed -8 Zndrsplt, Eye of Wisdom -8 Okaun, Eye of Chaos -8 Virtus the Veiled -8 Gorm the Great -8 Khorvath Brightflame -8 Sylvia Brightspear -8 Pir, Imaginative Rascal -8 Toothy, Imaginary Friend -1 Arena Rector -1 Brightling -8 Play of the Game -8 Regna's Sanction -8 Together Forever -1 Arcane Artisan -8 Game Plan -8 Spellseeker -8 Zndrsplt's Judgment -1 Archfiend of Despair -8 Mindblade Render -1 Stunning Reversal -8 Thrilling Encore -8 Virtus's Maneuver -8 Bonus Round -8 Khorvath's Fury -1 Najeela, the Blade-Blossom -8 Stolen Strategy -1 Bramble Sovereign -8 Generous Patron -1 Grothama, All-Devouring -8 Pir's Whim -8 Archon of Valor's Reach -8 Last One Standing -8 Sentinel Tower -8 Victory Chimes -8 Bountiful Promenade -8 Luxury Suite -8 Morphic Pool -8 Sea of Clouds -8 Spire Garden -8 Angelic Chorus -8 Kor Spiritdancer -1 Land Tax -8 Mangara of Corondor -8 Mystic Confluence -8 Sower of Temptation -8 Tidespout Tyrant -1 True-Name Nemesis -8 Diabolic Intent -1 Nirkana Revenant -8 Noosegraf Mob -8 Nyxathid -8 Goblin Razerunners -8 Magmatic Force -8 War's Toll -1 Doubling Season -8 Greater Good -8 Magus of the Candelabra -8 Seedborn Muse -8 Vigor -8 Apocalypse Hydra -8 Evil Twin -8 Gwafa Hazid, Profiteer -8 Mind's Eye -1 Mycosynth Lattice - -[M19 Secret Cards] -Ajani, Wise Counselor -Ajani's Influence -Court Cleric -Serra's Guardian -Silverbeak Griffin -Tezzeret, Cruel Machinist -Riddlemaster Sphinx -Pendulum of Patterns -Tezzeret's Gatebreaker -Tezzeret's Strider -Liliana, the Necromancer -Arisen Gorgon -Gravewaker -Liliana's Spoils -Tattered Mummy -Sarkhan, Dragonsoul -Kargan Dragonrider -Sarkhan's Dragonfire -Sarkhan's Whelp -Shivan Dragon -Vivien of the Arkbow -Aggressive Mammoth -Skalla Wolf -Ursine Champion -Vivien's Jaguar -Nexus of Fate -Sun Sentinel -Air Elemental -Befuddle -Mist-Cloaked Herald -Waterknot -Grasping Scoundrel -Radiating Lightning -Llanowar Elves -Cinder Barrens -Forsaken Sanctuary -Foul Orchard -Highland Lake -Meandering River -Stone Quarry -Submerged Boneyard -Timber Gorge -Tranquil Expanse -Woodland Stream - -[M19 Lands] -5 Cinder Barrens|M19 -5 Forsaken Sanctuary|M19 -5 Foul Orchard|M19 -5 Highland Lake|M19 -5 Meandering River|M19 -5 Stone Quarry|M19 -5 Submerged Boneyard|M19 -5 Timber Gorge|M19 -5 Tranquil Expanse|M19 -5 Woodland Stream|M19 -14 Forest|M19 -14 Island|M19 -14 Mountain|M19 -14 Plains|M19 -14 Swamp|M19 - -[GRN Lands] -10 Golgari Guildgate|GRN -10 Izzet Guildgate|GRN -10 Selesnya Guildgate|GRN -10 Dimir Guildgate|GRN -10 Boros Guildgate|GRN - -[GRN Secret Cards] -Ral, Caller of Storms -Ral's Dispersal -Ral's Staticaster -Vraska, Regal Gorgon -Attendant of Vraska -Vraska's Stoneglare -Impervious Greatwurm -Precision Bolt -Kraul Raider -Golgari Guildgate|GRN -Izzet Guildgate|GRN -Selesnya Guildgate|GRN -Dimir Guildgate|GRN -Boros Guildgate|GRN - -[RNA Lands] -10 Azorius Guildgate|RNA -10 Gruul Guildgate|RNA -10 Orzhov Guildgate|RNA -10 Rakdos Guildgate|RNA -10 Simic Guildgate|RNA - -[RNA Secret Cards] -Dovin, Architect of Law -Elite Arrester -Dovin's Dismissal -Dovin's Automaton -Domri, City Smasher -Ragefire -Charging War Boar -Domri's Nodorog -The Haunt of Hightower -Azorius Guildgate|RNA -Gruul Guildgate|RNA -Orzhov Guildgate|RNA -Rakdos Guildgate|RNA -Simic Guildgate|RNA - -[WAR Secret Cards] -Gideon, the Oathsworn -Desperate Lunge -Gideon's Battle Cry -Gideon's Company -Orzhov Guildgate|WAR -Jace, Arcane Strategist -Guildpact Informant -Jace's Projection -Jace's Ruse -Simic Guildgate|WAR -Tezzeret, Master of the Bridge - -[MH1 Lands] -10 Snow-Covered Plains|MH1 -10 Snow-Covered Island|MH1 -10 Snow-Covered Swamp|MH1 -10 Snow-Covered Mountain|MH1 -10 Snow-Covered Forest|MH1 - -[MH1 Secret Cards] -Flusterstorm -Snow-Covered Plains -Snow-Covered Island -Snow-Covered Swamp -Snow-Covered Mountain -Snow-Covered Forest - -[M20 Secret Cards] -Rienne, Angel of Rebirth -Ajani, Inspiring Leader -Goldmane Griffin -Savannah Sage -Twinblade Paladin -Mu Yanling, Celestial Wind -Celestial Messenger -Waterkin Shaman -Yanling's Harbinger -Sorin, Vampire Lord -Savage Gorger -Sorin's Guide -Thirsting Bloodlord -Chandra, Flame's Fury -Chandra's Flame Wave -Pyroclastic Elemental -Wildfire Elemental -Vivien, Nature's Avenger -Ethereal Elk -Gnarlback Rhino -Vivien's Crocodile -Angelic Guardian -Bastion Enforcer -Concordia Pegasus -Haazda Officer -Impassioned Orator -Imperial Outrider -Ironclad Krovod -Prowling Caracal -Serra's Guardian -Show of Valor -Siege Mastodon -Take Vengeance -Trusted Pegasus -Coral Merfolk -Phantom Warrior -Riddlemaster Sphinx -Snapping Drake -Bartizan Bats -Bogstomper -Dark Remedy -Disentomb -Gravewaker -Skeleton Archer -Sorin's Thirst -Vampire Opportunist -Walking Corpse -Engulfing Eruption -Fearless Halberdier -Goblin Assailant -Hostile Minotaur -Immortal Phoenix -Nimble Birdsticker -Rubblebelt Recluse -Shivan Dragon -Volcanic Dragon -Aggressive Mammoth -Bristling Boar -Canopy Spider -Frilled Sandwalla -Oakenform -Prized Unicorn -Titanic Growth -Woodland Mystic - -Bloodfell Caves -Blossoming Sands -Dismal Backwater -Jungle Hollow -Rugged Highlands -Scoured Barrens -Swiftwater Cliffs -Thornwood Falls -Tranquil Cove -Wind-Scarred Crag -Evolving Wilds - -[M20 Lands] -5 Bloodfell Caves|M20 -5 Blossoming Sands|M20 -5 Dismal Backwater|M20 -5 Jungle Hollow|M20 -5 Rugged Highlands|M20 -5 Scoured Barrens|M20 -5 Swiftwater Cliffs|M20 -5 Thornwood Falls|M20 -5 Tranquil Cove|M20 -5 Wind-Scarred Crag|M20 -5 Evolving Wilds -13 Forest|M20 -13 Island|M20 -13 Mountain|M20 -13 Plains|M20 -13 Swamp|M20 - -[ELD Secret Cards] -Kenrith, the Returned King -Rowan, Fearless Sparkmage -Garrison Griffin -Rowan's Battleguard -Rowan's Stalwarts -Wind-Scarred Crag -Oko, the Trickster -Oko's Accomplices -Bramblefort Fink -Oko's Hospitality -Thornwood Falls -Mace of the Valiant -Silverwing Squadron -Faerie Formation -Shimmer Dragon -Workshop Elders -Chittering Witch -Taste of Death -Embereth Skyblazer -Steelbane Hydra -Thorn Mammoth -Alela, Artful Provocateur -Banish into Fable -Chulane, Teller of Tales -Gluttonous Troll -Knights' Charge -Korvold, Fae-Cursed King -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 Locthwain|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 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 -Kenrith's Transformation|ELD|2 -Improbable Alliance|ELD|2 -Inspiring Veteran|ELD|2 - [JMP Above the Clouds 1] 1 Warden of Evos Isle|JMP 1 Inniaz, the Gale Force|JMP diff --git a/forge-gui/res/editions/30th Anniversary Edition.txt b/forge-gui/res/editions/30th Anniversary Edition.txt index de6b0282f84..abc30da2ce6 100644 --- a/forge-gui/res/editions/30th Anniversary Edition.txt +++ b/forge-gui/res/editions/30th Anniversary Edition.txt @@ -4,7 +4,7 @@ Date=2022-11-28 Name=30th Anniversary Edition Type=Collector_Edition BoosterCovers=3 -Booster=7 Common:fromsheet("30A cards"), 3 Uncommon:fromSheet("30A cards"), 1 Rare:fromSheet("30A cards"), 2 BasicLand:fromSheet("30A cards"), 1 fromSheet("30A retrolands"), 1 fromSheet("30A retrocards") +Booster=7 Common:fromsheet("30A cards"), 3 Uncommon:fromSheet("30A cards"), 1 Rare:fromSheet("30A cards"), 2 BasicLand:fromSheet("30A cards"), 1 BasicLand:fromSheet("30A retro frame"), 1 !BasicLand:fromSheet("30A retro frame") Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 ScryfallCode=30A @@ -275,7 +275,6 @@ ScryfallCode=30A 263 R Nevinyrral's Disk @Mark Tedin 264 U Obsianus Golem @Jesper Myrfors 265 U Rod of Ruin @Christopher Rush -266 U Sol Ring @Mark Tedin 267 U Soul Net @Dameon Willich 268 R Sunglasses of Urza @Dan Frazier 269 U Throne of Bone @Anson Maddocks @@ -307,6 +306,11 @@ ScryfallCode=30A 295 L Forest @Christopher Rush 296 L Forest @Christopher Rush 297 L Forest @Christopher Rush + +[alternate art] +266 U Sol Ring @Mark Tedin + +[retro frame] 298 R Animate Wall @Dan Frazier 299 R Armageddon @Jesper Myrfors 300 R Balance @Mark Poole @@ -605,307 +609,6 @@ ScryfallCode=30A 593 L Forest @Christopher Rush 594 L Forest @Christopher Rush -[retrocards] -1 Animate Wall|30A|2 -1 Armageddon|30A|2 -1 Balance|30A|2 -1 Benalish Hero|30A|2 -1 Black Ward|30A|2 -1 Blaze of Glory|30A|2 -1 Blessing|30A|2 -1 Blue Ward|30A|2 -1 Castle|30A|2 -1 Circle of Protection: Black|30A|2 -1 Circle of Protection: Blue|30A|2 -1 Circle of Protection: Green|30A|2 -1 Circle of Protection: Red|30A|2 -1 Circle of Protection: White|30A|2 -1 Consecrate Land|30A|2 -1 Conversion|30A|2 -1 Death Ward|30A|2 -1 Disenchant|30A|2 -1 Farmstead|30A|2 -1 Green Ward|30A|2 -1 Guardian Angel|30A|2 -1 Healing Salve|30A|2 -1 Holy Armor|30A|2 -1 Holy Strength|30A|2 -1 Island Sanctuary|30A|2 -1 Karma|30A|2 -1 Lance|30A|2 -1 Mesa Pegasus|30A|2 -1 Northern Paladin|30A|2 -1 Pearled Unicorn|30A|2 -1 Personal Incarnation|30A|2 -1 Purelace|30A|2 -1 Red Ward|30A|2 -1 Resurrection|30A|2 -1 Reverse Damage|30A|2 -1 Righteousness|30A|2 -1 Samite Healer|30A|2 -1 Savannah Lions|30A|2 -1 Serra Angel|30A|2 -1 Swords to Plowshares|30A|2 -1 Veteran Bodyguard|30A|2 -1 Wall of Swords|30A|2 -1 White Knight|30A|2 -1 White Ward|30A|2 -1 Wrath of God|30A|2 -1 Air Elemental|30A|2 -1 Ancestral Recall|30A|2 -1 Animate Artifact|30A|2 -1 Blue Elemental Blast|30A|2 -1 Braingeyser|30A|2 -1 Clone|30A|2 -1 Control Magic|30A|2 -1 Copy Artifact|30A|2 -1 Counterspell|30A|2 -1 Creature Bond|30A|2 -1 Drain Power|30A|2 -1 Feedback|30A|2 -1 Flight|30A|2 -1 Invisibility|30A|2 -1 Jump|30A|2 -1 Lifetap|30A|2 -1 Lord of Atlantis|30A|2 -1 Magical Hack|30A|2 -1 Mahamoti Djinn|30A|2 -1 Mana Short|30A|2 -1 Merfolk of the Pearl Trident|30A|2 -1 Phantasmal Forces|30A|2 -1 Phantasmal Terrain|30A|2 -1 Phantom Monster|30A|2 -1 Pirate Ship|30A|2 -1 Power Leak|30A|2 -1 Power Sink|30A|2 -1 Prodigal Sorcerer|30A|2 -1 Psionic Blast|30A|2 -1 Psychic Venom|30A|2 -1 Sea Serpent|30A|2 -1 Siren's Call|30A|2 -1 Sleight of Mind|30A|2 -1 Spell Blast|30A|2 -1 Stasis|30A|2 -1 Steal Artifact|30A|2 -1 Thoughtlace|30A|2 -1 Time Walk|30A|2 -1 Timetwister|30A|2 -1 Twiddle|30A|2 -1 Unsummon|30A|2 -1 Vesuvan Doppelganger|30A|2 -1 Volcanic Eruption|30A|2 -1 Wall of Air|30A|2 -1 Wall of Water|30A|2 -1 Water Elemental|30A|2 -1 Animate Dead|30A|2 -1 Bad Moon|30A|2 -1 Black Knight|30A|2 -1 Bog Wraith|30A|2 -1 Cursed Land|30A|2 -1 Dark Ritual|30A|2 -1 Deathgrip|30A|2 -1 Deathlace|30A|2 -1 Demonic Hordes|30A|2 -1 Demonic Tutor|30A|2 -1 Drain Life|30A|2 -1 Drudge Skeletons|30A|2 -1 Evil Presence|30A|2 -1 Fear|30A|2 -1 Frozen Shade|30A|2 -1 Gloom|30A|2 -1 Howl from Beyond|30A|2 -1 Hypnotic Specter|30A|2 -1 Lich|30A|2 -1 Lord of the Pit|30A|2 -1 Mind Twist|30A|2 -1 Nether Shadow|30A|2 -1 Nettling Imp|30A|2 -1 Nightmare|30A|2 -1 Paralyze|30A|2 -1 Pestilence|30A|2 -1 Plague Rats|30A|2 -1 Raise Dead|30A|2 -1 Royal Assassin|30A|2 -1 Sacrifice|30A|2 -1 Scathe Zombies|30A|2 -1 Scavenging Ghoul|30A|2 -1 Sengir Vampire|30A|2 -1 Simulacrum|30A|2 -1 Sinkhole|30A|2 -1 Terror|30A|2 -1 Unholy Strength|30A|2 -1 Wall of Bone|30A|2 -1 Warp Artifact|30A|2 -1 Sol Ring|30A|2 -1 Will-o'-the-Wisp|30A|2 -1 Word of Command|30A|2 -1 Zombie Master|30A|2 -1 Burrowing|30A|2 -1 Chaoslace|30A|2 -1 Disintegrate|30A|2 -1 Dragon Whelp|30A|2 -1 Dwarven Demolition Team|30A|2 -1 Dwarven Warriors|30A|2 -1 Earth Elemental|30A|2 -1 Earthquake|30A|2 -1 False Orders|30A|2 -1 Fire Elemental|30A|2 -1 Fireball|30A|2 -1 Firebreathing|30A|2 -1 Flashfires|30A|2 -1 Fork|30A|2 -1 Goblin Balloon Brigade|30A|2 -1 Goblin King|30A|2 -1 Granite Gargoyle|30A|2 -1 Gray Ogre|30A|2 -1 Hill Giant|30A|2 -1 Hurloon Minotaur|30A|2 -1 Ironclaw Orcs|30A|2 -1 Keldon Warlord|30A|2 -1 Lightning Bolt|30A|2 -1 Mana Flare|30A|2 -1 Manabarbs|30A|2 -1 Mons's Goblin Raiders|30A|2 -1 Orcish Artillery|30A|2 -1 Orcish Oriflamme|30A|2 -1 Power Surge|30A|2 -1 Raging River|30A|2 -1 Red Elemental Blast|30A|2 -1 Roc of Kher Ridges|30A|2 -1 Rock Hydra|30A|2 -1 Sedge Troll|30A|2 -1 Shatter|30A|2 -1 Shivan Dragon|30A|2 -1 Smoke|30A|2 -1 Stone Giant|30A|2 -1 Stone Rain|30A|2 -1 Tunnel|30A|2 -1 Two-Headed Giant of Foriys|30A|2 -1 Uthden Troll|30A|2 -1 Wall of Fire|30A|2 -1 Wall of Stone|30A|2 -1 Wheel of Fortune|30A|2 -1 Aspect of Wolf|30A|2 -1 Berserk|30A|2 -1 Birds of Paradise|30A|2 -1 Camouflage|30A|2 -1 Channel|30A|2 -1 Cockatrice|30A|2 -1 Craw Wurm|30A|2 -1 Elvish Archers|30A|2 -1 Fastbond|30A|2 -1 Fog|30A|2 -1 Force of Nature|30A|2 -1 Fungusaur|30A|2 -1 Gaea's Liege|30A|2 -1 Giant Growth|30A|2 -1 Giant Spider|30A|2 -1 Grizzly Bears|30A|2 -1 Hurricane|30A|2 -1 Ice Storm|30A|2 -1 Instill Energy|30A|2 -1 Ironroot Treefolk|30A|2 -1 Kudzu|30A|2 -1 Ley Druid|30A|2 -1 Lifeforce|30A|2 -1 Lifelace|30A|2 -1 Living Artifact|30A|2 -1 Living Lands|30A|2 -1 Llanowar Elves|30A|2 -1 Lure|30A|2 -1 Natural Selection|30A|2 -1 Regeneration|30A|2 -1 Regrowth|30A|2 -1 Scryb Sprites|30A|2 -1 Shanodin Dryads|30A|2 -1 Stream of Life|30A|2 -1 Thicket Basilisk|30A|2 -1 Timber Wolves|30A|2 -1 Tranquility|30A|2 -1 Tsunami|30A|2 -1 Verduran Enchantress|30A|2 -1 Wall of Brambles|30A|2 -1 Wall of Ice|30A|2 -1 Wall of Wood|30A|2 -1 Wanderlust|30A|2 -1 War Mammoth|30A|2 -1 Web|30A|2 -1 Wild Growth|30A|2 -1 Ankh of Mishra|30A|2 -1 Basalt Monolith|30A|2 -1 Black Lotus|30A|2 -1 Black Vise|30A|2 -1 Celestial Prism|30A|2 -1 Chaos Orb|30A|2 -1 Clockwork Beast|30A|2 -1 Conservator|30A|2 -1 Copper Tablet|30A|2 -1 Crystal Rod|30A|2 -1 Cyclopean Tomb|30A|2 -1 Dingus Egg|30A|2 -1 Disrupting Scepter|30A|2 -1 Forcefield|30A|2 -1 Gauntlet of Might|30A|2 -1 Glasses of Urza|30A|2 -1 Helm of Chatzuk|30A|2 -1 The Hive|30A|2 -1 Howling Mine|30A|2 -1 Icy Manipulator|30A|2 -1 Illusionary Mask|30A|2 -1 Iron Star|30A|2 -1 Ivory Cup|30A|2 -1 Jade Monolith|30A|2 -1 Jade Statue|30A|2 -1 Jayemdae Tome|30A|2 -1 Juggernaut|30A|2 -1 Kormus Bell|30A|2 -1 Library of Leng|30A|2 -1 Living Wall|30A|2 -1 Mana Vault|30A|2 -1 Meekstone|30A|2 -1 Mox Emerald|30A|2 -1 Mox Jet|30A|2 -1 Mox Pearl|30A|2 -1 Mox Ruby|30A|2 -1 Mox Sapphire|30A|2 -1 Nevinyrral's Disk|30A|2 -1 Obsianus Golem|30A|2 -1 Rod of Ruin|30A|2 -1 Sol Ring|30A|2 -1 Soul Net|30A|2 -1 Sunglasses of Urza|30A|2 -1 Throne of Bone|30A|2 -1 Time Vault|30A|2 -1 Winter Orb|30A|2 -1 Wooden Sphere|30A|2 -1 Badlands|30A|2 -1 Bayou|30A|2 -1 Plateau|30A|2 -1 Savannah|30A|2 -1 Scrubland|30A|2 -1 Taiga|30A|2 -1 Tropical Island|30A|2 -1 Tundra|30A|2 -1 Underground Sea|30A|2 -1 Volcanic Island|30A|2 - -[retrolands] -1 Plains|30A|4 -1 Plains|30A|5 -1 Plains|30A|6 -1 Island|30A|4 -1 Island|30A|5 -1 Island|30A|6 -1 Swamp|30A|4 -1 Swamp|30A|5 -1 Swamp|30A|6 -1 Mountain|30A|4 -1 Mountain|30A|5 -1 Mountain|30A|6 -1 Forest|30A|4 -1 Forest|30A|5 -1 Forest|30A|6 - [tokens] b_1_1_skeleton b_5_5_demon_flying diff --git a/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt b/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt index 3996b420206..d92a9df83bd 100644 --- a/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt +++ b/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt @@ -55,15 +55,6 @@ ScryfallCode=HBG 43 U Grave Choice @Will Gist 44 C Hook Horror @Olivier Bernard 45 R The Hourglass Coven @Konstantin Porubov -45a R Hag of Ceaseless Torment @Konstantin Porubov -45b R Hag of Dark Duress @Konstantin Porubov -45c R Hag of Death's Legion @Konstantin Porubov -45d R Hag of Inner Weakness @Konstantin Porubov -45e R Hag of Mage's Doom @Konstantin Porubov -45f R Hag of Noxious Nightmares @Konstantin Porubov -45g R Hag of Scoured Thoughts @Konstantin Porubov -45h R Hag of Syphoned Breath @Konstantin Porubov -45i R Hag of Twisted Visions @Konstantin Porubov 46 U Mind Spike @Matt Forsyth 47 C Sewer Plague @Piotr Foksowicz 48 R Stroke of Luck @Forrest Imel @@ -93,7 +84,7 @@ ScryfallCode=HBG 72 R Jon Irenicus, the Exile @Igor Grechanyi 73 U Liara of the Flaming Fist @David Rapoza 74 U Minthara of the Absolute @Evyn Fong -75 M Tasha, Unholy Archmage @Martina Fackova +75 M Tasha, Unholy Archmage @Martina Fačková 76 R Ulder Ravengard, Marshal @Eric Deschamps 77 U Gate of the Black Dragon @Sergey Glushakov 78 U Gate to Manorborn @Andreas Rocha @@ -211,8 +202,8 @@ ScryfallCode=HBG 190 U Swashbuckler Extraordinaire @Durion 191 U Two-Handed Axe @Milivoj Ćeran 192 C Unexpected Windfall @Alayna Danner -193 C Valor Singer @Justyna Gil -194 R Wrathful Red Dragon @Dan Scott +193 C Valor Singer @Justyna Dura +194 R Wrathful Red Dragon @Dan Murayama Scott 195 C You Come to the Gnoll Camp @Billy Christian 196 C You Find Some Prisoners @Lie Setiawan 197 C Young Red Dragon @Adam Vehige @@ -259,7 +250,7 @@ ScryfallCode=HBG 238 U Korlessa, Scale Singer @Jesper Ejsing 239 U Krydle of Baldur's Gate @Bryan Sola 240 U Lozhan, Dragons' Legacy @Rudy Siswanto -241 R Mazzy, Truesword Paladin @Justyna Gil +241 R Mazzy, Truesword Paladin @Justyna Dura 242 R Miirym, Sentinel Wyrm @Kekai Kotaki 243 M Minsc & Boo, Timeless Heroes @Andreas Zafiratos 244 M Nalia de'Arnise @John Stanko @@ -268,7 +259,7 @@ ScryfallCode=HBG 247 M Prosper, Tome-Bound @Yongjae Choi 248 R Raggadragga, Goreguts Boss @Xavier Ribeiro 249 R Raphael, Fiendish Savior @Livia Prima -250 U Thrakkus the Butcher @Nestor Ossandon Leal +250 U Thrakkus the Butcher @Néstor Ossandón Leal 251 U Trelasarra, Moon Dancer @Kieran Yanner 252 U Bag of Holding @Evyn Fong 253 R Basilisk Collar @Craig J Spearing @@ -337,3 +328,15 @@ A239 U A-Krydle of Baldur's Gate @Bryan Sola A243 M A-Minsc & Boo, Timeless Heroes @Andreas Zafiratos A259 C A-Lantern of Revealing @Eytan Zana A262 U A-Navigation Orb @Robin Olausson + +[conjured] +45a R Hag of Ceaseless Torment @Konstantin Porubov +45b R Hag of Dark Duress @Konstantin Porubov +45c R Hag of Death's Legion @Konstantin Porubov +45d R Hag of Inner Weakness @Konstantin Porubov +45e R Hag of Mage's Doom @Konstantin Porubov +45f R Hag of Noxious Nightmares @Konstantin Porubov +45g R Hag of Scoured Thoughts @Konstantin Porubov +45h R Hag of Syphoned Breath @Konstantin Porubov +45i R Hag of Twisted Visions @Konstantin Porubov + diff --git a/forge-gui/res/editions/Amonkhet.txt b/forge-gui/res/editions/Amonkhet.txt index ac6c0248b3b..31111edf6a2 100644 --- a/forge-gui/res/editions/Amonkhet.txt +++ b/forge-gui/res/editions/Amonkhet.txt @@ -5,8 +5,8 @@ Name=Amonkhet Code2=AKH Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("AKH Planeswalker Decks and Toolkit"), 3 Uncommon:!fromSheet("AKH Planeswalker Decks and Toolkit"), 1 RareMythic:!fromSheet("AKH Planeswalker Decks and Toolkit"), 1 BasicLand AKH -AdditionalSheetForFoils=fromSheet("MPS Amonkhet Invocations") +Booster=10 Common:fromSheet("AKH cards"), 3 Uncommon:fromSheet("AKH cards"), 1 RareMythic:fromSheet("AKH cards"), 1 BasicLand AKH +AdditionalSheetForFoils=fromSheet("MPS_AKH cards") FatPack=10 FatPackExtraSlots=80 BasicLands AdditionalSetUnlockedInQuest=MPS_AKH @@ -283,6 +283,8 @@ ScryfallCode=AKH 267 L Forest @Titus Lunter 268 L Forest @Titus Lunter 269 L Forest @Matt Stewart + +[precon product] 270 M Gideon, Martial Paragon @Daarken 271 U Companion of the Trials @Aaron Miller 272 R Gideon's Resolve @Jakub Kasper diff --git a/forge-gui/res/editions/Battle for Zendikar.txt b/forge-gui/res/editions/Battle for Zendikar.txt index 7eb55e0f3e2..1c29f9c0636 100644 --- a/forge-gui/res/editions/Battle for Zendikar.txt +++ b/forge-gui/res/editions/Battle for Zendikar.txt @@ -5,10 +5,10 @@ Code=BFZ Code2=BFZ Type=Expansion BoosterCovers=5 -Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand BFZ +Booster=10 Common:fromSheet("BFZ cards"), 3 Uncommon:fromSheet("BFZ cards"), 1 RareMythic:fromSheet("BFZ cards"), 1 BasicLand BFZ FatPack=9 FatPackExtraSlots=80 BasicLands -AdditionalSheetForFoils=fromSheet("EXP Lands") +AdditionalSheetForFoils=fromSheet("EXP cards") AdditionalSetUnlockedInQuest=EXP ScryfallCode=BFZ diff --git a/forge-gui/res/editions/Battlebond.txt b/forge-gui/res/editions/Battlebond.txt index 29b967e2163..e84bc8a2444 100644 --- a/forge-gui/res/editions/Battlebond.txt +++ b/forge-gui/res/editions/Battlebond.txt @@ -4,7 +4,7 @@ Date=2018-06-08 Name=Battlebond Type=Draft BoosterCovers=3 -Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythic"), 1 BasicLand +Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythicDistribution"), 1 BasicLand ScryfallCode=BBD [cards] @@ -262,9 +262,81 @@ ScryfallCode=BBD 252 L Swamp @Titus Lunter 253 L Mountain @Titus Lunter 254 L Forest @Titus Lunter + +[etched] 255 M Will Kenrith @Anna Steinbauer 256 M Rowan Kenrith @Anna Steinbauer +[RareMythicDistribution] +1 Will Kenrith|BBD|1 +1 Rowan Kenrith|BBD|1 +8 Regna, the Redeemer +8 Krav, the Unredeemed +8 Zndrsplt, Eye of Wisdom +8 Okaun, Eye of Chaos +8 Virtus the Veiled +8 Gorm the Great +8 Khorvath Brightflame +8 Sylvia Brightspear +8 Pir, Imaginative Rascal +8 Toothy, Imaginary Friend +1 Arena Rector +1 Brightling +8 Play of the Game +8 Regna's Sanction +8 Together Forever +1 Arcane Artisan +8 Game Plan +8 Spellseeker +8 Zndrsplt's Judgment +1 Archfiend of Despair +8 Mindblade Render +1 Stunning Reversal +8 Thrilling Encore +8 Virtus's Maneuver +8 Bonus Round +8 Khorvath's Fury +1 Najeela, the Blade-Blossom +8 Stolen Strategy +1 Bramble Sovereign +8 Generous Patron +1 Grothama, All-Devouring +8 Pir's Whim +8 Archon of Valor's Reach +8 Last One Standing +8 Sentinel Tower +8 Victory Chimes +8 Bountiful Promenade +8 Luxury Suite +8 Morphic Pool +8 Sea of Clouds +8 Spire Garden +8 Angelic Chorus +8 Kor Spiritdancer +1 Land Tax +8 Mangara of Corondor +8 Mystic Confluence +8 Sower of Temptation +8 Tidespout Tyrant +1 True-Name Nemesis +8 Diabolic Intent +1 Nirkana Revenant +8 Noosegraf Mob +8 Nyxathid +8 Goblin Razerunners +8 Magmatic Force +8 War's Toll +1 Doubling Season +8 Greater Good +8 Magus of the Candelabra +8 Seedborn Muse +8 Vigor +8 Apocalypse Hydra +8 Evil Twin +8 Gwafa Hazid, Profiteer +8 Mind's Eye +1 Mycosynth Lattice + [tokens] w_1_1_spirit_flying w_1_1_warrior diff --git a/forge-gui/res/editions/Bloomburrow.txt b/forge-gui/res/editions/Bloomburrow.txt index 909acbe75d2..832a98d1bf0 100644 --- a/forge-gui/res/editions/Bloomburrow.txt +++ b/forge-gui/res/editions/Bloomburrow.txt @@ -9,6 +9,7 @@ Booster=6 Common, 1 Common-Guest, 3 Uncommon, 1 RareMythic, 1 FullArtLand, 1 Wil Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 +#Numbers from https://mtgscribe.com/2024/07/14/play-booster-fact-sheet-bloomburrow/ [Common] Base=Common:fromSheet("BLB cards") @@ -21,9 +22,6 @@ Base=Uncommon:fromSheet("BLB cards") [RareMythic] Base=RareMythic:fromSheet("BLB cards") -# I don't know about these numbers, so i'm just copying them from MH3 -Replace=.021F fromSheet("BLB borderless") -Replace=.051F fromSheet("BLB showcase") [FullArtLand] Base=Land:fromSheet("BLB full art") @@ -33,9 +31,7 @@ Replace=.20F Land:fromSheet("BLB full art")+ Base=Common:fromSheet("BLB cards") # I don't know about these numbers, so i'm just copying them from MH3 Replace=.417F Uncommon:fromSheet("BLB cards") -Replace=.078F RareMythic:fromSheet("BLB cards") -Replace=.004F fromSheet("BLB borderless") -Replace=.042F fromSheet("BLB showcase") +Replace=.012F RareMythic:fromSheet("BLB cards") [cards] 1 C Banishing Light @Zoltan Boros @@ -319,16 +315,6 @@ Replace=.042F fromSheet("BLB showcase") 279 L Forest @David Robert Hovey 280 L Forest @David Robert Hovey 281 L Forest @David Robert Hovey -369 L Plains @Piotr Dura -370 L Plains @Julian Kok Joon Wen -371 L Island @Alexander Forssberg -372 L Island @Lorenzo Lanfranconi -373 L Swamp @Piotr Dura -374 L Swamp @Thomas Stoop -375 L Mountain @Samuele Bandini -376 L Mountain @Adam Paquette -377 L Forest @Alayna Danner -378 L Forest @Donato Giancola [borderless] 282 M Season of the Burrow @Edgar Sánchez Hidalgo @@ -424,6 +410,16 @@ Replace=.042F fromSheet("BLB showcase") 368 R Fountainport @Leon Tukker [precon product] +369 L Plains @Piotr Dura +370 L Plains @Julian Kok Joon Wen +371 L Island @Alexander Forssberg +372 L Island @Lorenzo Lanfranconi +373 L Swamp @Piotr Dura +374 L Swamp @Thomas Stoop +375 L Mountain @Samuele Bandini +376 L Mountain @Adam Paquette +377 L Forest @Alayna Danner +378 L Forest @Donato Giancola 379 M Bria, Riptide Rogue @Borja Pindado 380 M Byrke, Long Ear of the Law @Manuel Castañón 387 R Serra Redeemer @Joshua Raphael @@ -444,8 +440,6 @@ Replace=.042F fromSheet("BLB showcase") 383 U Fell @A. M. Sartor 384 U Wear Down @Iris Compiet 385 U Stormcatch Mentor @Manuel Castañón - -[bundle] 386 R Thundertrap Trainer @Jesper Ejsing [rebalanced] diff --git a/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt b/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt index e434eb1635e..d958b138739 100644 --- a/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt +++ b/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt @@ -370,26 +370,6 @@ ScryfallCode=CLB 359 C Sea Gate @Kamila Szutenberg 360 R Sea of Clouds @Matt Gaser 361 R Spire Garden @Alexander Forssberg -451 L Plains @Bruce Brenneise -452 L Plains @Leanna Crossan -453 L Plains @Titus Lunter -454 L Plains @Emmanuel Shiu -455 L Island @Bruce Brenneise -456 L Island @Piotr Dura -457 L Island @James Paick -458 L Island @Sam White -459 L Swamp @Piotr Dura -460 L Swamp @Logan Feliciano -461 L Swamp @Grady Frederick -462 L Swamp @Sam White -463 L Mountain @Matt Gaser -464 L Mountain @Lucas Graciano -465 L Mountain @Muhammad Firdaus -466 L Mountain @Sam White -467 L Forest @Bruce Brenneise -468 L Forest @Muhammad Firdaus -469 L Forest @Lucas Graciano -470 L Forest @Julian Kok Joon Wen [borderless] 362 M Elminster @Tyler Jacobson @@ -483,6 +463,8 @@ ScryfallCode=CLB 448 C Moss Diamond @Phil Stone 449 C Sky Diamond @Phil Stone 450 U Stonespeaker Crystal @Mark A. Nelson + +[etched] 471 U Abdel Adrian, Gorion's Ward @Karl Kopinski 472 U Ellyn Harbreeze, Busybody @Dan Murayama Scott 473 U Far Traveler @Alix Branwyn @@ -666,6 +648,26 @@ ScryfallCode=CLB 645 R Sarevok's Tome @Titus Lunter [precon product] +451 L Plains @Bruce Brenneise +452 L Plains @Leanna Crossan +453 L Plains @Titus Lunter +454 L Plains @Emmanuel Shiu +455 L Island @Bruce Brenneise +456 L Island @Piotr Dura +457 L Island @James Paick +458 L Island @Sam White +459 L Swamp @Piotr Dura +460 L Swamp @Logan Feliciano +461 L Swamp @Grady Frederick +462 L Swamp @Sam White +463 L Mountain @Matt Gaser +464 L Mountain @Lucas Graciano +465 L Mountain @Muhammad Firdaus +466 L Mountain @Sam White +467 L Forest @Bruce Brenneise +468 L Forest @Muhammad Firdaus +469 L Forest @Lucas Graciano +470 L Forest @Julian Kok Joon Wen 646 M Captain N'ghathrod @Andrey Kuzinskiy 647 M Faldorn, Dread Wolf Herald @Jason A. Engle 648 M Firkraag, Cunning Instigator @Andrew Mar diff --git a/forge-gui/res/editions/Commander Legends.txt b/forge-gui/res/editions/Commander Legends.txt index 6afbdf1d5d3..e3a3b1e6dda 100644 --- a/forge-gui/res/editions/Commander Legends.txt +++ b/forge-gui/res/editions/Commander Legends.txt @@ -527,7 +527,7 @@ ScryfallCode=CMR 512 M Tevesh Szat, Doom of Fools @Chris Rallis 513 M Jeska, Thrice Reborn @Chris Rallis -[showcase] +[etched] 514 M Najeela, the Blade-Blossom @Matt Stewart 515 M Akiri, Line-Slinger @David Gaillet 516 M Brago, King Eternal @Karla Ortiz diff --git a/forge-gui/res/editions/Conspiracy Take the Crown.txt b/forge-gui/res/editions/Conspiracy Take the Crown.txt index 1f89547d7a3..d856802edc9 100644 --- a/forge-gui/res/editions/Conspiracy Take the Crown.txt +++ b/forge-gui/res/editions/Conspiracy Take the Crown.txt @@ -5,7 +5,7 @@ Name=Conspiracy: Take the Crown Code2=CN2 Type=Draft BoosterCovers=3 -Booster=10 Common:!fromSheet("CN2 Draft Matters"), 3 Uncommon:!fromSheet("CN2 Draft Matters"), 1 RareMythic:!fromSheet("CN2 Draft Matters"), 1 fromSheet("CN2 Draft Matters") +Booster=10 Common:!fromSheet("CN2 Draft Matters"), 3 Uncommon:!fromSheet("CN2 Draft Matters"), 1 RareMythic:!fromSheet("CN2 Draft Matters"):fromSheet("CN2 cards"), 1 fromSheet("CN2 Draft Matters") AdditionalSheetForFoils=fromSheet("CN2 Foil Kaya") ScryfallCode=CN2 @@ -162,7 +162,7 @@ ScryfallCode=CN2 150 C Unnerve @Terese Nielsen 151 U Burn Away @Vincent Proce 152 R Burning Wish @Scott M. Fischer -153 R Charmbreaker Devils @Dan Scott +153 R Charmbreaker Devils @Dan Murayama Scott 154 U Coordinated Assault @John Severin Brassell 155 C Ember Beast @David Rapoza 156 C Fiery Fall @Daarken diff --git a/forge-gui/res/editions/Dominaria Remastered.txt b/forge-gui/res/editions/Dominaria Remastered.txt index df2d5c51c45..eafea138da6 100644 --- a/forge-gui/res/editions/Dominaria Remastered.txt +++ b/forge-gui/res/editions/Dominaria Remastered.txt @@ -5,7 +5,7 @@ Name=Dominaria Remastered Code2=DMR Type=Reprint BoosterCovers=3 -Booster=9 Common:fromsheet("DMR cards"), 3 Uncommon:fromSheet("DMR cards"), 1 RareMythic:fromSheet("DMR cards"), 1 BasicLand:fromSheet("DMR showcase"), 1 Any:fromSheet("DMR oldframes") +Booster=9 Common:fromsheet("DMR cards"), 3 Uncommon:fromSheet("DMR cards"), 1 RareMythic:fromSheet("DMR cards"), 1 BasicLand:fromSheet("DMR retro frame"), 1 !BasicLand:fromSheet("DMR retro frame") BoosterMustContain=Legendary Creature Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 @@ -274,7 +274,7 @@ ScryfallCode=DMR 260 U Treva's Ruins @Jerry Tiritilli 261 R Woodland Cemetery @Christine Choi -[showcase] +[retro frame] 262 R Divine Sacrament @Ekaterina Burmak 263 R Enlightened Tutor @Howard Lyon 264 R Glory @Donato Giancola @@ -476,148 +476,6 @@ ScryfallCode=DMR [promo] 457 R Counterspell @Mark Poole -[oldframes] -1 Divine Sacrament|DMR|2 -1 Enlightened Tutor|DMR|2 -1 Glory|DMR|2 -1 Lieutenant Kirtar|DMR|2 -1 Lyra Dawnbringer|DMR|2 -1 Mesa Enchantress|DMR|2 -1 Momentary Blink|DMR|2 -1 Renewed Faith|DMR|2 -1 Savannah Lions|DMR|2 -1 Serra Angel|DMR|2 -1 Serra Avatar|DMR|2 -1 Sevinne's Reclamation|DMR|2 -1 Spirit Link|DMR|2 -1 Swords to Plowshares|DMR|2 -1 Test of Endurance|DMR|2 -1 Whitemane Lion|DMR|2 -1 Windborn Muse|DMR|2 -1 Wrath of God|DMR|2 -1 Arcanis the Omnipotent|DMR|2 -1 Counterspell|DMR|2 -1 Denizen of the Deep|DMR|2 -1 Fact or Fiction|DMR|2 -1 Force of Will|DMR|2 -1 Frantic Search|DMR|2 -1 High Tide|DMR|2 -1 Impulse|DMR|2 -1 Mystic Remora|DMR|2 -1 Mystical Tutor|DMR|2 -1 Opposition|DMR|2 -1 Ovinize|DMR|2 -1 Peregrine Drake|DMR|2 -1 Stroke of Genius|DMR|2 -1 Time Stretch|DMR|2 -1 Turnabout|DMR|2 -1 Urza, Lord High Artificer|DMR|2 -1 Vexing Sphinx|DMR|2 -1 Body Snatcher|DMR|2 -1 Chainer, Dementia Master|DMR|2 -1 Chainer's Edict|DMR|2 -1 Dark Withering|DMR|2 -1 Dread Return|DMR|2 -1 Duress|DMR|2 -1 Entomb|DMR|2 -1 Mindslicer|DMR|2 -1 Nantuko Shade|DMR|2 -1 Necrosavant|DMR|2 -1 No Mercy|DMR|2 -1 Oversold Cemetery|DMR|2 -1 Royal Assassin|DMR|2 -1 Street Wraith|DMR|2 -1 Terror|DMR|2 -1 Undead Gladiator|DMR|2 -1 Vampiric Tutor|DMR|2 -1 Yawgmoth, Thran Physician|DMR|2 -1 Chain Lightning|DMR|2 -1 Dragon Whelp|DMR|2 -1 Empty the Warrens|DMR|2 -1 Fireblast|DMR|2 -1 Flametongue Kavu|DMR|2 -1 Gamble|DMR|2 -1 Gempalm Incinerator|DMR|2 -1 Goblin Matron|DMR|2 -1 Grim Lavamancer|DMR|2 -1 Last Chance|DMR|2 -1 Mogg War Marshal|DMR|2 -1 Overmaster|DMR|2 -1 Pashalik Mons|DMR|2 -1 Shivan Dragon|DMR|2 -1 Siege-Gang Commander|DMR|2 -1 Sneak Attack|DMR|2 -1 Sulfuric Vortex|DMR|2 -1 Valduk, Keeper of the Flame|DMR|2 -1 Worldgorger Dragon|DMR|2 -1 Arboria|DMR|2 -1 Birds of Paradise|DMR|2 -1 Deadwood Treefolk|DMR|2 -1 Elvish Spirit Guide|DMR|2 -1 Exploration|DMR|2 -1 Fa'adiyah Seer|DMR|2 -1 Forgotten Ancient|DMR|2 -1 Invigorating Boon|DMR|2 -1 Jolrael, Mwonvuli Recluse|DMR|2 -1 Kamahl, Fist of Krosa|DMR|2 -1 Lull|DMR|2 -1 Nature's Lore|DMR|2 -1 Nut Collector|DMR|2 -1 Saproling Symbiosis|DMR|2 -1 Squirrel Nest|DMR|2 -1 Sylvan Library|DMR|2 -1 Wild Dogs|DMR|2 -1 Wild Growth|DMR|2 -1 Worldly Tutor|DMR|2 -1 Absorb|DMR|2 -1 Arcades Sabboth|DMR|2 -1 Decimate|DMR|2 -1 Dralnu's Crusade|DMR|2 -1 Gerrard's Verdict|DMR|2 -1 Hunting Grounds|DMR|2 -1 Mystic Enforcer|DMR|2 -1 Phantom Nishoba|DMR|2 -1 Pyre Zombie|DMR|2 -1 Quicksilver Dagger|DMR|2 -1 Radha, Heir to Keld|DMR|2 -1 Recoil|DMR|2 -1 Rith, the Awakener|DMR|2 -1 Sawtooth Loon|DMR|2 -1 Sol'kanar the Swamp King|DMR|2 -1 Spinal Embrace|DMR|2 -1 Spiritmonger|DMR|2 -1 Tatyova, Benthic Druid|DMR|2 -1 Tiana, Ship's Caretaker|DMR|2 -1 Xira Arien|DMR|2 -1 Zur the Enchanter|DMR|2 -1 Crawlspace|DMR|2 -1 Cryptic Gateway|DMR|2 -1 Damping Sphere|DMR|2 -1 Gauntlet of Power|DMR|2 -1 Helm of Awakening|DMR|2 -1 Icy Manipulator|DMR|2 -1 Jester's Cap|DMR|2 -1 Juggernaut|DMR|2 -1 Legacy Weapon|DMR|2 -1 Lotus Blossom|DMR|2 -1 Mind Stone|DMR|2 -1 Ornithopter|DMR|2 -1 Thran Golem|DMR|2 -1 Tormod's Crypt|DMR|2 -1 Triskelion|DMR|2 -1 Umbilicus|DMR|2 -1 Urza's Blueprints|DMR|2 -1 Urza's Incubator|DMR|2 -1 Clifftop Retreat|DMR|2 -1 Dark Depths|DMR|2 -1 Gemstone Mine|DMR|2 -1 Hinterland Harbor|DMR|2 -1 Isolated Chapel|DMR|2 -1 Maze of Ith|DMR|2 -1 Mishra's Factory|DMR|2 -1 Sulfur Falls|DMR|2 -1 Woodland Cemetery|DMR|2 - [tokens] b_2_1_cat b_2_2_zombie diff --git a/forge-gui/res/editions/Dominaria United.txt b/forge-gui/res/editions/Dominaria United.txt index 76abb6c4c9e..3821424cb9f 100644 --- a/forge-gui/res/editions/Dominaria United.txt +++ b/forge-gui/res/editions/Dominaria United.txt @@ -300,11 +300,6 @@ ScryfallCode=DMU 284 R Tyrannical Pitlord @Donato Giancola 285 R Ragefire Hellkite @Xavier Ribeiro 286 R Briar Hydra @Caio Monteiro -423 R Serra Redeemer @Joshua Raphael -424 R Cosmic Epiphany @Eli Minaya -425 R Tyrannical Pitlord @Donato Giancola -426 R Ragefire Hellkite @Xavier Ribeiro -427 R Briar Hydra @Caio Monteiro [showcase] 287 R Danitha, Benalia's Hope @Barbara Rosiak @@ -447,6 +442,11 @@ ScryfallCode=DMU 420 R Urborg Lhurgoyf @Andrey Kuzinskiy 421 R Plaza of Heroes @Gabor Szikszai 422 R Thran Portal @Sarah Finnigan +423 R Serra Redeemer @Joshua Raphael +424 R Cosmic Epiphany @Eli Minaya +425 R Tyrannical Pitlord @Donato Giancola +426 R Ragefire Hellkite @Xavier Ribeiro +427 R Briar Hydra @Caio Monteiro [buy a box] 428 R Llanowar Loamspeaker @Volkan Baǵa diff --git a/forge-gui/res/editions/Dominaria.txt b/forge-gui/res/editions/Dominaria.txt index 4bb4d649760..89efc848197 100644 --- a/forge-gui/res/editions/Dominaria.txt +++ b/forge-gui/res/editions/Dominaria.txt @@ -4,7 +4,7 @@ Date=2018-04-27 Name=Dominaria Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("DOM Planeswalker Decks and Additional Promo"), 3 Uncommon:!fromSheet("DOM Planeswalker Decks and Additional Promo"), 1 RareMythic:!fromSheet("DOM Planeswalker Decks and Additional Promo"), 1 BasicLand DOM +Booster=10 Common:fromSheet("DOM cards"), 3 Uncommon:fromSheet("DOM cards"), 1 RareMythic:fromSheet("DOM cards"), 1 BasicLand DOM BoosterMustContain=Legendary Creature FatPack=10 FatPackExtraSlots=80 BasicLands @@ -280,6 +280,8 @@ ScryfallCode=DOM 267 L Forest @Titus Lunter 268 L Forest @Dimitar Marinski 269 L Forest @Mark Poole + +[precon product] 270 M Teferi, Timebender @Zack Stella 271 C Temporal Machinations @Zack Stella 272 R Niambi, Faithful Healer @Greg Opalinski @@ -290,6 +292,8 @@ ScryfallCode=DOM 277 U Karplusan Hound @Viktor Titov 278 C Pyromantic Pilgrim @Magali Villeneuve 279 C Timber Gorge @YW Tang + +[buy a box] 280 R Firesong and Sunspeaker @Zoltan Boros [tokens] diff --git a/forge-gui/res/editions/Double Masters 2022.txt b/forge-gui/res/editions/Double Masters 2022.txt index 0652342c305..f9e6baf725c 100644 --- a/forge-gui/res/editions/Double Masters 2022.txt +++ b/forge-gui/res/editions/Double Masters 2022.txt @@ -426,7 +426,7 @@ ScryfallCode=2X2 411 U Selesnya Sanctuary @Ron Spears 412 U Simic Growth Chamber @Pete Venters -[showcase] +[etched] 413 M Emrakul, the Aeons Torn @Mark Tedin 414 M Kozilek, Butcher of Truth @Michael Komarck 415 M Ulamog, the Infinite Gyre @Aleksi Briclot diff --git a/forge-gui/res/editions/Double Masters.txt b/forge-gui/res/editions/Double Masters.txt index f2eacc39340..5345abc2b90 100644 --- a/forge-gui/res/editions/Double Masters.txt +++ b/forge-gui/res/editions/Double Masters.txt @@ -4,7 +4,7 @@ Date=2020-08-07 Name=Double Masters Type=Reprint BoosterCovers=3 -Booster=8 Common, 3 Uncommon, 2 RareMythic, 2 fromSheet("2XM Foils") +Booster=8 Common:fromsheet("2XM cards"), 3 Uncommon:fromsheet("2XM cards"), 2 RareMythic:fromsheet("2XM cards"), 2 fromSheet("2XM Foils") Foil=NotSupported DoublePick=FirstPick ChaosDraftThemes=MASTERS_SET @@ -343,6 +343,8 @@ ScryfallCode=2XM 330 C Urza's Power Plant @Brian Snõddy 331 C Urza's Tower @Brian Snõddy 332 R Wooded Bastion @Christopher Moeller + +[borderless] 333 M Karn Liberated @Mark Tedin 334 M Jace, the Mind Sculptor @Jason Chan 335 M Avacyn, Angel of Hope @Randy Vargas @@ -383,6 +385,8 @@ ScryfallCode=2XM 370 R Urza's Mine @Mark Tedin 371 R Urza's Power Plant @Mark Tedin 372 R Urza's Tower @Mark Tedin + +[buy a box] 373 L Plains @John Avon 374 L Plains @Noah Bradley 375 L Island @John Avon diff --git a/forge-gui/res/editions/Guilds of Ravnica.txt b/forge-gui/res/editions/Guilds of Ravnica.txt index a3475d24e0a..22b2a69218f 100644 --- a/forge-gui/res/editions/Guilds of Ravnica.txt +++ b/forge-gui/res/editions/Guilds of Ravnica.txt @@ -5,7 +5,7 @@ Name=Guilds of Ravnica Code2=GRN Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("GRN Secret Cards"), 3 Uncommon:!fromSheet("GRN Secret Cards"), 1 RareMythic:!fromSheet("GRN Secret Cards"), 1 fromSheet("GRN Lands") +Booster=10 Common:!fromSheet("GRN lands"):fromSheet("GRN cards"), 3 Uncommon:fromSheet("GRN cards"), 1 RareMythic:fromSheet("GRN cards"), 1 fromSheet("GRN lands") AdditionalSetUnlockedInQuest=GK1 FatPack=10 FatPackExtraSlots=80 BasicLands @@ -272,6 +272,8 @@ ScryfallCode=GRN 257 R Steam Vents @Jonas De Ro 258 R Temple Garden @Titus Lunter 259 R Watery Grave @Cliff Childs + +[precon product] 260 L Plains @Richard Wright 261 L Island @Richard Wright 262 L Swamp @Richard Wright @@ -285,8 +287,22 @@ ScryfallCode=GRN 270 C Kraul Raider @Ben Wootten 271 U Attendant of Vraska @Magali Villeneuve 272 R Vraska's Stoneglare @Yongjae Choi + +[buy a box] 273 M Impervious Greatwurm @Simon Dominic +[lands] +Golgari Guildgate|GRN|1 +Golgari Guildgate|GRN|2 +Izzet Guildgate|GRN|1 +Izzet Guildgate|GRN|2 +Selesnya Guildgate|GRN|1 +Selesnya Guildgate|GRN|2 +Dimir Guildgate|GRN|1 +Dimir Guildgate|GRN|2 +Boros Guildgate|GRN|1 +Boros Guildgate|GRN|2 + [tokens] w_4_4_angel_flying_vigilance w_1_1_soldier_lifelink diff --git a/forge-gui/res/editions/Hour of Devastation.txt b/forge-gui/res/editions/Hour of Devastation.txt index c48e663c645..ddc95416c2c 100644 --- a/forge-gui/res/editions/Hour of Devastation.txt +++ b/forge-gui/res/editions/Hour of Devastation.txt @@ -5,8 +5,8 @@ Name=Hour of Devastation Code2=HOU Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("HOU Planeswalker Decks and Toolkit"), 3 Uncommon:!fromSheet("HOU Planeswalker Decks and Toolkit"), 1 RareMythic:!fromSheet("HOU Planeswalker Decks and Toolkit"), 1 BasicLand -AdditionalSheetForFoils=fromSheet("MPS Hour of Devastation Invocations") +Booster=10 Common:fromSheet("HOU cards"), 3 Uncommon:fromSheet("HOU cards"), 1 RareMythic:fromSheet("HOU cards"), 1 BasicLand +AdditionalSheetForFoils=fromSheet("MPS_AKH special slot") FatPack=10 FatPackExtraSlots=80 BasicLands AdditionalSetUnlockedInQuest=MPS_AKH @@ -214,6 +214,8 @@ ScryfallCode=HOU 197 L Mountain @Kev Walker 198 L Forest @Titus Lunter 199 L Forest @Mark Poole + +[precon product] 200 M Nissa, Genesis Mage @Chris Rallis 201 U Avid Reclaimer @Josu Hernaiz 202 C Brambleweft Behemoth @Florian de Gesincourt diff --git a/forge-gui/res/editions/Innistrad Crimson Vow.txt b/forge-gui/res/editions/Innistrad Crimson Vow.txt index 37ef374a9d7..98ed149c709 100644 --- a/forge-gui/res/editions/Innistrad Crimson Vow.txt +++ b/forge-gui/res/editions/Innistrad Crimson Vow.txt @@ -412,6 +412,8 @@ Prerelease=6 Boosters, 1 RareMythic+ 395 R Dollhouse of Horrors @Muhammad Firdaus 396 R Investigator's Journal @Yeong-Hao Han 397 R Voldaren Estate @Richard Wright + +[bundle] 398 L Plains @Sam White 399 L Island @Sam White 400 L Swamp @Sam White diff --git a/forge-gui/res/editions/Innistrad Midnight Hunt.txt b/forge-gui/res/editions/Innistrad Midnight Hunt.txt index 691a18fa7e9..1b191b4a281 100644 --- a/forge-gui/res/editions/Innistrad Midnight Hunt.txt +++ b/forge-gui/res/editions/Innistrad Midnight Hunt.txt @@ -66,7 +66,6 @@ Prerelease=6 Boosters, 1 RareMythic+ 55 C Galedrifter @Daniel Ljunggren 56 C Geistwave @Olena Richards 57 R Grafted Identity @Manuel Castañón -57† R Grafted Identity @Manuel Castañón 58 C Larder Zombie @E. M. Gist 59 M Lier, Disciple of the Drowned @Ekaterina Burmak 60 C Locked in the Cemetery @Tran Nguyen @@ -288,6 +287,16 @@ Prerelease=6 Boosters, 1 RareMythic+ 276 L Forest @Alayna Danner 277 L Forest @Dan Mumford +[alternate art] +57† R Grafted Identity @Manuel Castañón + +[precon product] +380 L Plains @Andreas Rocha +381 L Island @Andreas Rocha +382 L Swamp @Kasia 'Kafis' Zielińska +383 L Mountain @Muhammad Firdaus +384 L Forest @Andreas Rocha + [borderless] 278 M Wrenn and Seven @Bram Sels 279 M Arlinn, the Pack's Hope @Eric Deschamps @@ -395,11 +404,6 @@ Prerelease=6 Boosters, 1 RareMythic+ 377 R The Celestus @Jonas De Ro 378 R Pithing Needle @Ovidio Cartagena 379 M Hostile Hostel @Daniel Ljunggren -380 L Plains @Andreas Rocha -381 L Island @Andreas Rocha -382 L Swamp @Kasia 'Kafis' Zielińska -383 L Mountain @Muhammad Firdaus -384 L Forest @Andreas Rocha [buy a box] 385 R Champion of the Perished @Daarken diff --git a/forge-gui/res/editions/Kamigawa Neon Dynasty.txt b/forge-gui/res/editions/Kamigawa Neon Dynasty.txt index df6c9b3aa5e..f6a58630f71 100644 --- a/forge-gui/res/editions/Kamigawa Neon Dynasty.txt +++ b/forge-gui/res/editions/Kamigawa Neon Dynasty.txt @@ -431,6 +431,8 @@ ScryfallCode=NEO 403 R Mirror Box @Mid 404 R Reckoner Bankbuster @Yoshiya 405 R Surgehacker Mech @Inuchiyo Meimaru + +[etched] 417 R Farewell @Fuzichoco 418 M The Wandering Emperor @Hisashi Momose 419 M Tezzeret, Betrayer of Flesh @Maekawa Yuichi diff --git a/forge-gui/res/editions/Khans of Tarkir.txt b/forge-gui/res/editions/Khans of Tarkir.txt index b3c441c332f..c9ef690ab6f 100644 --- a/forge-gui/res/editions/Khans of Tarkir.txt +++ b/forge-gui/res/editions/Khans of Tarkir.txt @@ -5,7 +5,7 @@ Name=Khans of Tarkir Code2=KTK Type=Expansion BoosterCovers=5 -Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand KTK +Booster=10 Common:fromsheet("KTK cards"), 3 Uncommon:fromsheet("KTK cards"), 1 RareMythic:fromsheet("KTK cards"), 1 BasicLand KTK FatPack=9 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=GRAVEYARD_MATTERS diff --git a/forge-gui/res/editions/Magic 2015.txt b/forge-gui/res/editions/Magic 2015.txt index ab0dd41160a..d763442489e 100644 --- a/forge-gui/res/editions/Magic 2015.txt +++ b/forge-gui/res/editions/Magic 2015.txt @@ -5,7 +5,7 @@ Name=Magic 2015 Code2=M15 Type=Core BoosterCovers=5 -Booster=10 Common:!fromSheet("M15 Sample Cards"), 3 Uncommon:!fromSheet("M15 Sample Cards"), 1 RareMythic:!fromSheet("M15 Sample Cards"), 1 BasicLand +Booster=10 Common:fromSheet("M15 cards"), 3 Uncommon:fromSheet("M15 cards"), 1 RareMythic:fromSheet("M15 cards"), 1 BasicLand FatPack=9 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=CORE_SET @@ -281,6 +281,8 @@ ScryfallCode=M15 267 L Forest @Steven Belledin 268 L Forest @Noah Bradley 269 L Forest @Jonas De Ro + +[precon product] 270 R Aegis Angel @Aleksi Briclot 271 C Divine Verdict @Kev Walker 272 C Inspired Charge @Wayne Reynolds diff --git a/forge-gui/res/editions/Magic 2021.txt b/forge-gui/res/editions/Magic 2021.txt index 1c17ce37397..38e70df02ea 100644 --- a/forge-gui/res/editions/Magic 2021.txt +++ b/forge-gui/res/editions/Magic 2021.txt @@ -302,6 +302,12 @@ ScryfallCode=M21 282 M Liliana, Waker of the Dead @Magali Villeneuve 283 M Chandra, Heart of Fire @Jason Rainville 284 M Garruk, Unleashed @Cristi Balanescu +314 R Containment Priest @Jesper Ejsing +315 M Grim Tutor @Antonio José Manzanedo +316 M Massacre Wurm @Kekai Kotaki +317 R Cultivate @Billy Christian +318 R Scavenging Ooze @Sam Rowan +319 R Solemn Simulacrum @Joseph Meehan [showcase] 285 M Ugin, the Spirit Dragon @Raymond Swanland @@ -333,12 +339,6 @@ ScryfallCode=M21 311 L Swamp @Jonas De Ro 312 L Mountain @Jonas De Ro 313 L Forest @Jonas De Ro -314 R Containment Priest @Jesper Ejsing -315 M Grim Tutor @Antonio José Manzanedo -316 M Massacre Wurm @Kekai Kotaki -317 R Cultivate @Billy Christian -318 R Scavenging Ooze @Sam Rowan -319 R Solemn Simulacrum @Joseph Meehan [precon product] 320 M Basri, Devoted Paladin @Jason Rainville diff --git a/forge-gui/res/editions/Magic Origins.txt b/forge-gui/res/editions/Magic Origins.txt index 305820c21e6..58a9879c2b3 100644 --- a/forge-gui/res/editions/Magic Origins.txt +++ b/forge-gui/res/editions/Magic Origins.txt @@ -5,7 +5,7 @@ Name=Magic Origins Code2=ORI Type=Core BoosterCovers=5 -Booster=10 Common:!fromSheet("ORI Sample Cards"), 3 Uncommon:!fromSheet("ORI Sample Cards"), 1 RareMythic:!fromSheet("ORI Sample Cards"), 1 BasicLand +Booster=10 Common:fromSheet("ORI cards"), 3 Uncommon:fromSheet("ORI cards"), 1 RareMythic:fromSheet("ORI cards"), 1 BasicLand FatPack=9 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=CORE_SET @@ -284,6 +284,8 @@ ScryfallCode=ORI 270 L Forest @Jonas De Ro 271 L Forest @Jonas De Ro 272 L Forest @Vincent Proce + +[precon product] 273 R Aegis Angel @Aleksi Briclot 274 C Divine Verdict @Kev Walker 275 C Eagle of the Watch @Scott Murphy diff --git a/forge-gui/res/editions/March of the Machine The Aftermath.txt b/forge-gui/res/editions/March of the Machine The Aftermath.txt index d3e5ca37c72..7a71997bf4d 100644 --- a/forge-gui/res/editions/March of the Machine The Aftermath.txt +++ b/forge-gui/res/editions/March of the Machine The Aftermath.txt @@ -57,6 +57,8 @@ BoosterBox=0 48 M Tyvar the Bellicose @Jarel Threat 49 M Karn, Legacy Reforged @Grzegorz Rutkowski 50 R Drannith Ruins @Martin de Diego Sádaba + +[showcase] 51 U Coppercoat Vanguard @Steve Ellis 52 R Deification @Jason A. Engle 53 U Harnessed Snubhorn @Jody Clark @@ -107,6 +109,8 @@ BoosterBox=0 98 M Tyvar the Bellicose @Richard Luong 99 M Karn, Legacy Reforged @Daren Bader 100 R Drannith Ruins @Steve Ellis + +[etched] 101 U Coppercoat Vanguard @Bruno Biazotto 102 R Deification @Maxime Minard 103 U Harnessed Snubhorn @Quintin Gleim @@ -157,6 +161,8 @@ BoosterBox=0 148 M Tyvar the Bellicose @Jarel Threat 149 M Karn, Legacy Reforged @Grzegorz Rutkowski 150 R Drannith Ruins @Martin de Diego Sádaba + +[extended art] 151 R Deification @Maxime Minard 152 R Metropolis Reformer @Ryan Pancoast 153 R Spark Rupture @Viko Menezes @@ -192,6 +198,8 @@ BoosterBox=0 183 M Tyvar the Bellicose @Jarel Threat 184 M Karn, Legacy Reforged @Grzegorz Rutkowski 185 R Drannith Ruins @Martin de Diego Sádaba + +[etched] 186 U Coppercoat Vanguard @Steve Ellis 187 R Deification @Jason A. Engle 188 U Harnessed Snubhorn @Jody Clark @@ -235,5 +243,9 @@ BoosterBox=0 226 R Sigarda, Font of Blessings @Sami Makkonen 227 M Tyvar the Bellicose @Richard Luong 228 R Drannith Ruins @Steve Ellis + +[promo] 229 R Spark Rupture @Scott M. Fischer + +[buy a box] 230 R Jolrael, Voice of Zhalfir @Ernanda Souza diff --git a/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt b/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt index d5f5f7ba2b0..9d87ce5010d 100644 --- a/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt +++ b/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt @@ -5,6 +5,7 @@ Name=Masterpiece Series - Amonkhet Type=Collector_Edition ScryfallCode=mp2 +#Bonus cards for AKH Amonkhet [cards] 1 S Austere Command @Richard Wright 2 S Aven Mindcensor @Jose Cabrera @@ -36,6 +37,9 @@ ScryfallCode=mp2 28 S Rhonas the Indomitable @Jack Wang 29 S Maelstrom Pulse @Igor Kieryluk 30 S Vindicate @Igor Kieryluk + +#Bonus cards for HOU Hour of Devastation +[special slot] 31 S Armageddon @Florian de Gesincourt 32 S Capsize @Cliff Childs 33 S Forbid @Richard Wright diff --git a/forge-gui/res/editions/Masters Edition IV.txt b/forge-gui/res/editions/Masters Edition IV.txt index 354f35f6f65..344ae3b5436 100644 --- a/forge-gui/res/editions/Masters Edition IV.txt +++ b/forge-gui/res/editions/Masters Edition IV.txt @@ -5,7 +5,7 @@ Name=Masters Edition IV Code2=ME4 Type=Online BoosterCovers=1 -Booster=10 Common, 3 Uncommon, 1 Rare, 1 fromSheet("ME4 UrzaLands") +Booster=10 Common, 3 Uncommon, 1 Rare, 1 BasicLand ScryfallCode=ME4 [cards] @@ -279,20 +279,6 @@ ScryfallCode=ME4 259d L Urza's Tower @Mark Poole 260 R Volcanic Island @Brian Snõddy -[UrzaLands] -1 Urza's Tower|ME4|1 -1 Urza's Tower|ME4|2 -1 Urza's Tower|ME4|3 -1 Urza's Tower|ME4|4 -1 Urza's Mine|ME4|1 -1 Urza's Mine|ME4|2 -1 Urza's Mine|ME4|3 -1 Urza's Mine|ME4|4 -1 Urza's Power Plant|ME4|1 -1 Urza's Power Plant|ME4|2 -1 Urza's Power Plant|ME4|3 -1 Urza's Power Plant|ME4|4 - [tokens] r_1_1_goblin c_1_1_a_tetravite_flying_noenchant diff --git a/forge-gui/res/editions/Modern Horizons 2.txt b/forge-gui/res/editions/Modern Horizons 2.txt index a67b2715c2d..ba8171f53fa 100644 --- a/forge-gui/res/editions/Modern Horizons 2.txt +++ b/forge-gui/res/editions/Modern Horizons 2.txt @@ -318,7 +318,7 @@ ScryfallCode=MH2 302 U Mishra's Factory @Scott Chou 303 R Riptide Laboratory @John Avon -[alternate art] +[borderless] 304 M Dakkon, Shadow Slayer @Jake Murray 305 M Geyadrone Dihada @Aleksi Briclot 306 M Grist, the Hunger Tide @Victor Adame Minguez @@ -399,7 +399,7 @@ ScryfallCode=MH2 379 M Kaldra Compleat @Vincent Proce 380 R Urza's Saga @Titus Lunter -[alternate frame] +[retro frame] 381 C Blacksmith's Skill @Jason A. Engle 382 C Marble Gargoyle @Drew Tucker 383 R Out of Time @Tobias Kwan @@ -503,7 +503,7 @@ ScryfallCode=MH2 479 R Verdant Catacombs @Vance Kovacs 480 R Yavimaya, Cradle of Growth @Sarah Finnigan -[promo] +[bundle] 481 L Plains @Eric Peterson 482 L Plains @Alan Pollack 483 L Island @Donato Giancola @@ -518,7 +518,7 @@ ScryfallCode=MH2 [buy a box] 491 M Sanctum Prelate @Michael C. Hayes -[bundle] +[promo] 492 R Yusri, Fortune's Flame @Evyn Fong [Lands] diff --git a/forge-gui/res/editions/Modern Horizons 3.txt b/forge-gui/res/editions/Modern Horizons 3.txt index 7bfa0cd196c..2aaaa60079e 100644 --- a/forge-gui/res/editions/Modern Horizons 3.txt +++ b/forge-gui/res/editions/Modern Horizons 3.txt @@ -10,6 +10,7 @@ BoosterCovers=3 ChaosDraftThemes=MASTERS_SET ScryfallCode=MH3 +#Numbers from https://mtgscribe.com/2024/05/22/play-booster-fact-sheet-modern-horizons-3/ [Common] Base=Common:fromSheet("MH3 cards") @@ -22,8 +23,8 @@ Base=Uncommon:fromSheet("MH3 cards") [RareMythic] Base=RareMythic:fromSheet("MH3 cards") -Replace=.021F fromSheet("MH3 alternate frame") -Replace=.051F fromSheet("MH3 borderless") +Replace=.021F RareMythic:fromSheet("MH3 retro frame") +Replace=.051F RareMythic:fromSheet("MH3 borderless") [Common-Land] Base=Common:fromSheet("MH3 cards") @@ -36,16 +37,16 @@ Replace=.067F fromSheet("MH3 full art")+ Base=Uncommon:fromSheet("MH3 new to modern") Replace=.213F Rare:fromSheet("MH3 new to modern") Replace=.023F Mythic:fromSheet("MH3 new to modern") -Replace=.008F fromSheet("MH3 borderless frame") -Replace=.003F fromSheet("MH3 borderless profile") -Replace=.001F fromSheet("MH3 alternate frame") +Replace=.011F RareMythic:fromSheet("MH3 borderless") +Replace=.001F Mythic:fromSheet("MH3 borderless") +Replace=.002F RareMythic:fromSheet("MH3 retro frame") [Wildcard] Base=Common:fromSheet("MH3 cards") Replace=.417F Uncommon:fromSheet("MH3 cards") Replace=.078F RareMythic:fromSheet("MH3 cards") -Replace=.004F fromSheet("MH3 borderless frame") -Replace=.042F fromSheet("MH3 alternate frame") +Replace=.004F RareMythic:fromSheet("MH3 borderless") +Replace=.042F fromSheet("MH3 retro frame") [cards] 1 U Breaker of Creation @Yohann Schepacz @@ -351,6 +352,8 @@ Replace=.042F fromSheet("MH3 alternate frame") 301 R Deserted Temple @Rob Alexander 302 U Nesting Grounds @Yeong-Hao Han 303 M Phyrexian Tower @Martin de Diego Sádaba + +[bundle] 310 L Plains @Volkan Baǵa 311 L Plains @Lius Lasahido 312 L Island @Alayna Danner @@ -401,8 +404,6 @@ Replace=.042F fromSheet("MH3 alternate frame") 347 R Pearl Medallion @Olena Richards 348 R Ruby Medallion @Martina Pilcerova 349 R Sapphire Medallion @Ron Spears - -[borderless frame] 350 R Archway of Innovation @Sam Burley 351 R Arena of Glory @Piotr Dura 352 R Bloodstained Mire @Sean Vo @@ -424,8 +425,6 @@ Replace=.042F fromSheet("MH3 alternate frame") 368 R Laelia, the Blade Reforged @Tyler Walpole 369 M Eladamri, Korvecdal @Tyler Walpole 370 R Six @Ivan Shavrin - -[borderless profile] 371 M Arna Kennerüd, Skycaptain @Grant Griffin 372 M Breya, Etherium Shaper @Jack Hughes 373 R Genku, Future Shaper @Ivan Shavrin @@ -450,7 +449,7 @@ Replace=.042F fromSheet("MH3 alternate frame") 471 M Ral, Monsoon Mage @Borja Pindado 472 M Grist, Voracious Larva @Ron Spencer -[showcase] +[retro frame] 384 M Emrakul, the World Anew @Brent Hollowell 385 U It That Heralds the End @Alex Konstad 386 M Kozilek, the Broken Reality @Brent Hollowell @@ -502,6 +501,15 @@ Replace=.042F fromSheet("MH3 alternate frame") 432 R Kudo, King Among Bears @Ekaterina Burmak 433 R Psychic Frog @Pete Venters 434 R Rosheen, Roaring Prophet @Fang Xinyu +435 R Bloodstained Mire @Bruce Brenneise +436 R Flooded Strand @Alexander Forssberg +437 U Nesting Grounds @Yeong-Hao Han +438 R Polluted Delta @Chris Ostrowski +439 U Snow-Covered Wastes @Mark Poole +440 R Windswept Heath @Alexander Forssberg +441 R Wooded Foothills @Chris Ostrowski + +[etched] 497 L Plains @Volkan Baǵa 498 L Plains @Lius Lasahido 499 L Island @Alayna Danner @@ -527,15 +535,6 @@ Replace=.042F fromSheet("MH3 alternate frame") 519 U Solar Transformer @Mike Bierek 520 C Tranquil Landscape @Randy Gallegos 521 C Twisted Landscape @Piotr Dura - -[alternate frame] -435 R Bloodstained Mire @Bruce Brenneise -436 R Flooded Strand @Alexander Forssberg -437 U Nesting Grounds @Yeong-Hao Han -438 R Polluted Delta @Chris Ostrowski -439 U Snow-Covered Wastes @Mark Poole -440 R Windswept Heath @Alexander Forssberg -441 R Wooded Foothills @Chris Ostrowski 473 M Emrakul, the World Anew @Brent Hollowell 474 M Herigast, Erupting Nullkite @Lucas Graciano 475 M Kozilek, the Broken Reality @Brent Hollowell diff --git a/forge-gui/res/editions/Modern Horizons.txt b/forge-gui/res/editions/Modern Horizons.txt index b23b38581f6..d7d020ea26f 100644 --- a/forge-gui/res/editions/Modern Horizons.txt +++ b/forge-gui/res/editions/Modern Horizons.txt @@ -5,7 +5,7 @@ Name=Modern Horizons Code2=MH1 Type=Draft BoosterCovers=5 -Booster=10 Common:!fromSheet("MH1 Secret Cards"), 3 Uncommon:!fromSheet("MH1 Secret Cards"), 1 RareMythic:!fromSheet("MH1 Secret Cards"), 1 fromSheet("MH1 Lands") +Booster=10 Common:fromSheet("MH1 cards"), 3 Uncommon:fromSheet("MH1 cards"), 1 RareMythic:fromSheet("MH1 cards"), 1 BasicLand:fromSheet("MH1 cards") BoosterBox=24 ChaosDraftThemes=MASTERS_SET;GRAVEYARD_MATTERS ScryfallCode=MH1 @@ -265,6 +265,8 @@ ScryfallCode=MH1 252 L Snow-Covered Swamp @Titus Lunter 253 L Snow-Covered Mountain @Titus Lunter 254 L Snow-Covered Forest @Titus Lunter + +[buy a box] 255 R Flusterstorm @Chris Rallis [tokens] diff --git a/forge-gui/res/editions/Murders at Karlov Manor.txt b/forge-gui/res/editions/Murders at Karlov Manor.txt index e87bba11f92..2c98d646b3a 100644 --- a/forge-gui/res/editions/Murders at Karlov Manor.txt +++ b/forge-gui/res/editions/Murders at Karlov Manor.txt @@ -4,11 +4,33 @@ Date=2024-02-09 Name=Murders at Karlov Manor Type=Expansion ScryfallCode=MKM -Booster=7 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand, 1 Any, 1 Any+ -ChanceReplaceCommonWith=.125F fromsheet("MKM karlov surprise") -Prerelease=6 Boosters, 1 RareMythic+, 1 Any:fromsheet("MKM prerelease promo")+ +BoosterSlots=Common,Common-Guest,Uncommon,RareMythic,BasicLand,Wildcard,PrereleasePromo +Booster=6 Common, 1 Common-Guest, 3 Uncommon, 1 RareMythic, 1 BasicLand, 1 Wildcard, 1 Wildcard+ +Prerelease=6 Boosters, 1 RareMythic+, 1 PrereleasePromo+ BoosterBox=36 +[Common] +Base=Common:fromSheet("MKM cards") + +[Common-Guest] +Base=Common:fromSheet("MKM cards") +Replace=.125F fromSheet("MKM karlov surprise") + +[Uncommon] +Base=Uncommon:fromSheet("MKM cards") + +[RareMythic] +Base=RareMythic:fromSheet("MKM cards") + +[BasicLand] +Base=BasicLand:fromSheet("MKM cards") + +[Wildcard] +Base=Any:fromSheet("MKM cards") + +[PrereleasePromo] +Base=Any:fromsheet("MKM prerelease promo") + [cards] 1 U Case of the Shattered Pact @Peter Polach 2 U Absolving Lammasu @Izzy @@ -447,6 +469,8 @@ BoosterBox=36 426 U Lightning Helix @Eli Minaya 427 U No More Lies @Liiga Smilshkalne 428 R Axebane Ferox @Adam Volker + +[prerelease promo] 430 M Melek, Reforged Researcher @Andreas Zafiratos 431 M Tomik, Wielder of Law @Valera Lutfullina 432 M Voja, Jaws of the Conclave @Valera Lutfullina @@ -454,11 +478,6 @@ BoosterBox=36 [buy a box] 429 R Wojek Investigator @Greg Staples -[prerelease promo] -1 Melek, Reforged Researcher|MKM -1 Tomik, Wielder of Law|MKM -1 Voja, Jaws of the Conclave|MKM - [karlov surprise] 1 Baleful Mastery|PLIST 1 Bishop of the Bloodstained|PLIST diff --git a/forge-gui/res/editions/Oath of the Gatewatch.txt b/forge-gui/res/editions/Oath of the Gatewatch.txt index 005ed48174a..d77daed9188 100644 --- a/forge-gui/res/editions/Oath of the Gatewatch.txt +++ b/forge-gui/res/editions/Oath of the Gatewatch.txt @@ -8,7 +8,7 @@ BoosterCovers=4 Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand BFZ FatPack=9 FatPackExtraSlots=66 BasicLands BFZ, 14 name("Wastes") -AdditionalSheetForFoils=fromSheet("EXP Lands 2") +AdditionalSheetForFoils=fromSheet("EXP special slot") AdditionalSetUnlockedInQuest=EXP ScryfallCode=OGW @@ -196,9 +196,9 @@ ScryfallCode=OGW 181 C Unknown Shores @Jung Park 182 R Wandering Fumarole @Florian de Gesincourt 183 C Wastes @Jason Felix -183 C Wastes @Jason Felix -184 C Wastes @Raymond Swanland +183a C Wastes @Jason Felix 184 C Wastes @Raymond Swanland +184a C Wastes @Raymond Swanland [tokens] c_1_1_eldrazi_scion_sac diff --git a/forge-gui/res/editions/Outlaws of Thunder Junction.txt b/forge-gui/res/editions/Outlaws of Thunder Junction.txt index 0f4d9d99a09..2795a29e0f4 100644 --- a/forge-gui/res/editions/Outlaws of Thunder Junction.txt +++ b/forge-gui/res/editions/Outlaws of Thunder Junction.txt @@ -4,11 +4,37 @@ Date=2024-04-19 Name=Outlaws of Thunder Junction Type=Expansion ScryfallCode=OTJ -Booster=6 Common, 3 Uncommon, 1 RareMythic, 1 Land, 1 Any, 1 Any OTP, 1 Any+ -ChanceReplaceCommonWith=.20F fromsheet("OTJ outlaw list") +BoosterSlots=Common,Common-Guest,Uncommon,RareMythic,Land,Wildcard,WildcardOTP +Booster=5 Common, 1 Common-Guest, 3 Uncommon, 1 RareMythic, 1 Land, 1 Wildcard, 1 Wildcard+, 1 WildcardOTP Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 +#Numbers from https://mtgscribe.com/2024/04/05/outlaws-of-thunder-junction-play-booster-fact-sheet/ +[Common] +Base=Common:fromSheet("OTJ cards") + +[Common-Guest] +Base=Common:fromSheet("OTJ cards") +Replace=.20F fromSheet("OTJ outlaw list") + +[Uncommon] +Base=Uncommon:fromSheet("OTJ cards") + +[RareMythic] +Base=RareMythic:fromSheet("OTJ cards") +Replace=.025F RareMythic:!fromSheet("OTJ outlaw list") + +[Wildcard] +Base=Any:!fromSheet("OTJ outlaw list") +Replace=.0833F RareMythic:fromSheet("OTJ cards") + +[WildcardOTP] +Base=Uncommon:fromSheet("OTP cards") +Replace=.3333F RareMythic:fromSheet("OTP cards") + +[Land] +Base=Land:fromSheet("OTJ cards") + [cards] 1 R Another Round @Darrell Riche 2 M Archangel of Tithes @Denys Tsiperko diff --git a/forge-gui/res/editions/Phyrexia All Will Be One.txt b/forge-gui/res/editions/Phyrexia All Will Be One.txt index de9b2b065e3..d7b9eda8240 100644 --- a/forge-gui/res/editions/Phyrexia All Will Be One.txt +++ b/forge-gui/res/editions/Phyrexia All Will Be One.txt @@ -281,6 +281,8 @@ ScryfallCode=ONE 269 L Swamp @Mark Riddick 270 L Mountain @Mark Riddick 271 L Forest @Mark Riddick + +[precon product] 272 L Plains @Sergey Glushakov 273 L Island @David Álvarez 274 L Swamp @Julian Kok Joon Wen @@ -293,11 +295,6 @@ ScryfallCode=ONE 406 R Kinzu of the Bleak Coven @Andreas Zafiratos 407 R Rhuk, Hexgold Nabber @Andrea De Dominicis 408 R Goliath Hatchery @Simon Dominic -409 R Mite Overseer @Néstor Ossandón Leal -410 R Serum Sovereign @Chris Rallis -411 R Kinzu of the Bleak Coven @Andreas Zafiratos -412 R Rhuk, Hexgold Nabber @Andrea De Dominicis -413 R Goliath Hatchery @Simon Dominic [showcase] 285 U Bladed Ambassador @Ravenna Tran @@ -377,6 +374,8 @@ ScryfallCode=ONE 369 L Forest @Alayna Danner 414 M Elesh Norn, Mother of Machines @Martina Fačková 415 M Elesh Norn, Mother of Machines @Junji Ito + +[etched] 417 U Bladed Ambassador @Ravenna Tran 418 M Elesh Norn, Mother of Machines @Martina Fačková 419 M Elesh Norn, Mother of Machines @Junji Ito @@ -489,6 +488,11 @@ ScryfallCode=ONE 401 R The Monumental Facade @Bruce Brenneise 402 R The Mycosynth Gardens @Andrew Mar 403 R The Seedcore @Kasia 'Kafis' Zielińska +409 R Mite Overseer @Néstor Ossandón Leal +410 R Serum Sovereign @Chris Rallis +411 R Kinzu of the Bleak Coven @Andreas Zafiratos +412 R Rhuk, Hexgold Nabber @Andrea De Dominicis +413 R Goliath Hatchery @Simon Dominic [buy a box] 284 R Green Sun's Twilight @Piotr Dura diff --git a/forge-gui/res/editions/Portal.txt b/forge-gui/res/editions/Portal.txt index 8b7a83d5710..1fbbe394094 100644 --- a/forge-gui/res/editions/Portal.txt +++ b/forge-gui/res/editions/Portal.txt @@ -16,7 +16,6 @@ ScryfallCode=POR 4 U Ardent Militia @Mike Raabe 5 R Armageddon @John Avon 6 C Armored Pegasus @Andrew Robinson -6d C Armored Pegasus @Andrew Robinson 7 R Blessed Reversal @Zina Saunders 8 R Blinding Light @John Coulthart 9 C Border Guard @Kev Walker @@ -41,7 +40,6 @@ ScryfallCode=POR 28 C Spotted Griffin @William Simpson 29 U Starlight @John Avon 30 U Starlit Angel @Rebecca Guay -30s U Starlit Angel @徐晓鸣 31 C Steadfastness @Kev Walker 32 R Stern Marshal @D. Alexander Gregory 33 R Temporary Truce @Mike Raabe @@ -50,7 +48,6 @@ ScryfallCode=POR 36 U Vengeance @Andrew Robinson 37 U Wall of Swords @Douglas Shuler 38 C Warrior's Charge @Ted Naifeh -38† C Warrior's Charge @Ted Naifeh 39 R Wrath of God @Mike Raabe 40 R Ancestral Memories @Dan Frazier 41 R Balance of Power @Adam Rex @@ -59,7 +56,6 @@ ScryfallCode=POR 44 C Cloak of Feathers @Rebecca Guay 45 R Cloud Dragon @John Avon 46 C Cloud Pirates @Phil Foglio -46d C Cloud Pirates @Phil Foglio 47 U Cloud Spirit @DiTerlizzi 48 U Command of Unsummoning @Phil Foglio 49 C Coral Eel @Una Fricker @@ -71,7 +67,6 @@ ScryfallCode=POR 55 U Flux @Ted Naifeh 56 C Giant Octopus @John Matson 57 C Horned Turtle @Adrian Smith -57s C Horned Turtle @Wang Yuqun 58 U Ingenious Thief @Dan Frazier 59 U Man-o'-War @Una Fricker 60 C Merfolk of the Pearl Trident @DiTerlizzi @@ -81,15 +76,11 @@ ScryfallCode=POR 64 U Personal Tutor @D. Alexander Gregory 65 R Phantom Warrior @Dan Frazier 66 R Prosperity @Phil Foglio -66s R Prosperity @李有良 67 C Snapping Drake @Christopher Rush -67d C Snapping Drake @Christopher Rush 68 C Sorcerous Sight @Kaja Foglio 69 C Storm Crow @Una Fricker -69d C Storm Crow @Una Fricker 70 C Symbol of Unsummoning @Adam Rex 71 R Taunt @Phil Foglio -71s R Taunt @杨钊 72 U Theft of Dreams @Adam Rex 73 R Thing from the Deep @Paolo Parente 74 C Tidal Surge @Douglas Shuler @@ -99,7 +90,6 @@ ScryfallCode=POR 78 U Withering Gaze @Scott M. Fischer 79 U Arrogant Vampire @Zina Saunders 80 U Assassin's Blade @John Matson -80s U Assassin's Blade @徐晓鸣 81 C Bog Imp @Christopher Rush 82 C Bog Raiders @Steve Luke 83 U Bog Wraith @Ted Naifeh @@ -113,11 +103,9 @@ ScryfallCode=POR 91 R Ebon Dragon @Donato Giancola 92 R Endless Cockroaches @Ron Spencer 93 C Feral Shadow @Colin MacNeil -93d C Feral Shadow @Colin MacNeil 94 R Final Strike @John Coulthart 95 U Gravedigger @Scott M. Fischer 96 C Hand of Death @John Coulthart -96† C Hand of Death @John Coulthart 97 C Howling Fury @Mike Dringenberg 98 R King's Assassin @Zina Saunders 99 R Mercenary Knight @Adrian Smith @@ -129,7 +117,6 @@ ScryfallCode=POR 105 C Python @Alan Rabinowitz 106 U Rain of Tears @Eric Peterson 107 C Raise Dead @Charles Gillespie -107s C Raise Dead @李尤松 108 R Serpent Assassin @Roger Raupp 109 C Serpent Warrior @Roger Raupp 110 C Skeletal Crocodile @Mike Dringenberg @@ -137,14 +124,10 @@ ScryfallCode=POR 112 C Soul Shred @Alan Rabinowitz 113 C Undying Beast @Steve Luke 114 U Vampiric Feast @D. Alexander Gregory -114s U Vampiric Feast @王峰 115 C Vampiric Touch @Zina Saunders 116 U Virtue's Ruin @Mike Dringenberg 117 R Wicked Pact @Adam Rex -117s R Wicked Pact @杨光恒 118 U Blaze @Gerry Grace -118† U Blaze @Gerry Grace -118s U Blaze @David A. Cherry 119 U Boiling Seas @Tom Wänerstrand 120 C Burning Cloak @Scott M. Fischer 121 C Craven Giant @Ron Spencer @@ -161,7 +144,6 @@ ScryfallCode=POR 132 C Highland Giant @Ron Spencer 133 C Hill Giant @Randy Gallegos 134 U Hulking Cyclops @Paolo Parente -134s U Hulking Cyclops @Lin Yan 135 C Hulking Goblin @Pete Venters 136 R Last Chance @Hannibal King 137 C Lava Axe @Adrian Smith @@ -173,7 +155,6 @@ ScryfallCode=POR 143 R Pyroclasm @John Matson 144 C Raging Cougar @Terese Nielsen 145 C Raging Goblin @Pete Venters -145† C Raging Goblin @Pete Venters 146 C Raging Minotaur @Scott M. Fischer 147 U Rain of Salt @Charles Gillespie 148 C Scorching Spear @Mike Raabe @@ -187,19 +168,14 @@ ScryfallCode=POR 156 R Winds of Change @Adam Rex 157 R Alluring Scent @Ted Naifeh 158 U Anaconda @Andrew Robinson -158† U Anaconda @Andrew Robinson 159 U Bee Sting @Phil Foglio 160 U Bull Hippo @Roger Raupp -160d U Bull Hippo @Roger Raupp 161 R Charging Rhino @Una Fricker 162 U Deep Wood @Paolo Parente 163 C Elite Cat Warrior @Eric Peterson -163† C Elite Cat Warrior @Eric Peterson 164 C Elven Cache @Rebecca Guay -164s C Elven Cache @张艺娜 165 C Elvish Ranger @DiTerlizzi 166 C Fruition @Steve Luke -166s C Fruition @Wang Yuqun 167 C Giant Spider @Randy Gallegos 168 C Gorilla Warrior @John Matson 169 C Grizzly Bears @Zina Saunders @@ -207,7 +183,6 @@ ScryfallCode=POR 171 C Jungle Lion @Janine Johnston 172 C Mobilize @Rebecca Guay 173 C Monstrous Growth @Dan Frazier -173† C Monstrous Growth @Dan Frazier 174 U Moon Sprite @Terese Nielsen 175 R Natural Order @Alan Rabinowitz 176 U Natural Spring @Janine Johnston @@ -266,3 +241,30 @@ ScryfallCode=POR 214s L Forest @李铁 215 L Forest @John Avon 215s L Forest @李铁 + +[alternate art] +6d C Armored Pegasus @Andrew Robinson +30s U Starlit Angel @徐晓鸣 +38† C Warrior's Charge @Ted Naifeh +46d C Cloud Pirates @Phil Foglio +57s C Horned Turtle @Wang Yuqun +66s R Prosperity @李有良 +67d C Snapping Drake @Christopher Rush +69d C Storm Crow @Una Fricker +71s R Taunt @杨钊 +80s U Assassin's Blade @徐晓鸣 +93d C Feral Shadow @Colin MacNeil +96† C Hand of Death @John Coulthart +107s C Raise Dead @李尤松 +114s U Vampiric Feast @王峰 +117s R Wicked Pact @杨光恒 +118† U Blaze @Gerry Grace +118s U Blaze @David A. Cherry +134s U Hulking Cyclops @Lin Yan +145† C Raging Goblin @Pete Venters +158† U Anaconda @Andrew Robinson +160d U Bull Hippo @Roger Raupp +163† C Elite Cat Warrior @Eric Peterson +164s C Elven Cache @张艺娜 +166s C Fruition @Wang Yuqun +173† C Monstrous Growth @Dan Frazier diff --git a/forge-gui/res/editions/Ravnica Allegiance.txt b/forge-gui/res/editions/Ravnica Allegiance.txt index d0c565e90a3..ba8f71d8089 100644 --- a/forge-gui/res/editions/Ravnica Allegiance.txt +++ b/forge-gui/res/editions/Ravnica Allegiance.txt @@ -5,7 +5,7 @@ Name=Ravnica Allegiance Code2=RNA Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("RNA Secret Cards"), 3 Uncommon:!fromSheet("RNA Secret Cards"), 1 RareMythic:!fromSheet("RNA Secret Cards"), 1 fromSheet("RNA Lands") +Booster=10 Common:fromSheet("RNA cards"):!fromSheet("RNA Lands"), 3 Uncommon:fromSheet("RNA cards"), 1 RareMythic:fromSheet("RNA cards"), 1 fromSheet("RNA Lands") FatPack=10 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=RAVNICA @@ -271,6 +271,8 @@ ScryfallCode=RNA 257 C Simic Guildgate @Adam Paquette 258 C Simic Guildgate @Adam Paquette 259 R Stomping Ground @James Paick + +[precon product] 260 L Plains @Titus Lunter 261 L Island @Eytan Zana 262 L Swamp @Adam Paquette @@ -284,8 +286,17 @@ ScryfallCode=RNA 270 C Ragefire @Randy Vargas 271 U Charging War Boar @Izzy 272 R Domri's Nodorog @Svetlin Velinov + +[buy a box] 273 M The Haunt of Hightower @Lius Lasahido +[Lands] +Azorius Guildgate|RNA +Gruul Guildgate|RNA +Orzhov Guildgate|RNA +Rakdos Guildgate|RNA +Simic Guildgate|RNA + [tokens] rg_4_4_beast_trample g_3_3_centaur diff --git a/forge-gui/res/editions/Ravnica Remastered.txt b/forge-gui/res/editions/Ravnica Remastered.txt index def70749f12..d35e155c6d5 100644 --- a/forge-gui/res/editions/Ravnica Remastered.txt +++ b/forge-gui/res/editions/Ravnica Remastered.txt @@ -341,7 +341,7 @@ ScryfallCode=RVR 444 M Domri Rade @Susumu Kuroi 445 M Ral Zarek @Fukuzo Katsura -[showcase] +[retro frame] 302 R Blazing Archon @Zoltan Boros & Gabor Szikszai 303 R Blind Obedience @Seb McKinnon 304 U Condemn @Daren Bader diff --git a/forge-gui/res/editions/Streets of New Capenna.txt b/forge-gui/res/editions/Streets of New Capenna.txt index 9bc7ae5706e..ffcb6a1de32 100644 --- a/forge-gui/res/editions/Streets of New Capenna.txt +++ b/forge-gui/res/editions/Streets of New Capenna.txt @@ -420,6 +420,8 @@ ScryfallCode=SNC 403 R Void Rend @Krharts 404 M Ziatora, the Incinerator @Scott M. Fischer 405 R Ziatora's Envoy @Olga Tereshenko + +[etched] 441 M Elspeth Resplendent @Krharts 442 R Giada, Font of Hope @Scott M. Fischer 443 M Sanctuary Warden @Julie Dillon diff --git a/forge-gui/res/editions/The Big Score.txt b/forge-gui/res/editions/The Big Score.txt index 3e7234983f0..ebe60453f6a 100644 --- a/forge-gui/res/editions/The Big Score.txt +++ b/forge-gui/res/editions/The Big Score.txt @@ -36,6 +36,8 @@ ScryfallCode=BIG 28 M Transmutation Font @Mark Poole 29 M Fomori Vault @Jonas De Ro 30 M Tarnation Vista @Alayna Danner + +[showcase] 31 M Collector's Cage @Ben Hill 32 M Grand Abolisher @David Astruga 33 M Oltec Matterweaver @Inkognit @@ -71,6 +73,8 @@ ScryfallCode=BIG 63 M Lotus Ring @Ben Hill 64 M Sword of Wealth and Power @Artur Nakhodkin 65 M Tarnation Vista @Artur Nakhodkin + +[extended art] 66 M Collector's Cage @Bartek Fedyczak 67 M Grand Abolisher @Aurore Folny 68 M Oltec Matterweaver @Villarrte diff --git a/forge-gui/res/editions/The Brothers War.txt b/forge-gui/res/editions/The Brothers War.txt index 7de183f006d..4a565b0f3af 100644 --- a/forge-gui/res/editions/The Brothers War.txt +++ b/forge-gui/res/editions/The Brothers War.txt @@ -305,11 +305,6 @@ ScryfallCode=BRO 290 R Terror Ballista @Leon Tukker 291 R Artificer's Dragon @Leon Tukker 292 R Woodcaller Automaton @Ryan Pancoast -373 R Rescue Retriever @Jesper Ejsing -374 R Geology Enthusiast @Fajareka Setiawan -375 R Terror Ballista @Leon Tukker -376 R Artificer's Dragon @Leon Tukker -377 R Woodcaller Automaton @Ryan Pancoast [borderless] 293 M Teferi, Temporal Pilgrim @Cosmin Podar @@ -394,6 +389,11 @@ ScryfallCode=BRO 370 R Fortified Beachhead @Christian Dimitrov 371 R Hall of Tagsin @Christian Dimitrov 372 R Mishra's Foundry @Leon Tukker +373 R Rescue Retriever @Jesper Ejsing +374 R Geology Enthusiast @Fajareka Setiawan +375 R Terror Ballista @Leon Tukker +376 R Artificer's Dragon @Leon Tukker +377 R Woodcaller Automaton @Ryan Pancoast [buy a box] 378 R Mishra's Foundry @Leon Tukker diff --git a/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt b/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt index 4691ed5e74a..e7906598cb8 100644 --- a/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt +++ b/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt @@ -298,21 +298,11 @@ ScryfallCode=LTR 284 R Ringwraiths @Warren Mahy 285 R Assault on Osgiliath @Warren Mahy 286 R Elanor Gardner @Torgeir Fjereide -383 R Saradoc, Master of Buckland @Sean Vo -384 R Elvish Mariner @Axel Sauerwald -385 R Ringwraiths @Warren Mahy -386 R Assault on Osgiliath @Warren Mahy -387 R Elanor Gardner @Torgeir Fjereide 824 R Eagle of Deliverance @Sidharth Chaturvedi 825 R Minas Tirith Garrison @Irina Nordsol 826 R Warg Rider @Pascal Quidault 827 R Riders of the Mark @Antonio José Manzanedo 828 R Mirkwood Channeler @Irina Nordsol -829 R Eagle of Deliverance @Sidharth Chaturvedi -830 R Minas Tirith Garrison @Irina Nordsol -831 R Warg Rider @Pascal Quidault -832 R Riders of the Mark @Antonio José Manzanedo -833 R Mirkwood Channeler @Irina Nordsol [bundle] 287 M Aragorn and Arwen, Wed @Magali Villeneuve @@ -805,6 +795,11 @@ ScryfallCode=LTR 380 M The One Ring @Veli Nyström 381 M Palantír of Orthanc @Tatiana Veryayskaya 382 R Phial of Galadriel @Andrea Piparo +383 R Saradoc, Master of Buckland @Sean Vo +384 R Elvish Mariner @Axel Sauerwald +385 R Ringwraiths @Warren Mahy +386 R Assault on Osgiliath @Warren Mahy +387 R Elanor Gardner @Torgeir Fjereide 388 R Frodo, Determined Hero @Magali Villeneuve 389 R Gandalf, White Rider @Ekaterina Burmak 390 R Gollum, Scheming Guide @Dmitry Burmak @@ -852,6 +847,11 @@ ScryfallCode=LTR 791 M The One Ring @Veli Nyström 792 M Palantír of Orthanc @Tatiana Veryayskaya 793 R Phial of Galadriel @Andrea Piparo +829 R Eagle of Deliverance @Sidharth Chaturvedi +830 R Minas Tirith Garrison @Irina Nordsol +831 R Warg Rider @Pascal Quidault +832 R Riders of the Mark @Antonio José Manzanedo +833 R Mirkwood Channeler @Irina Nordsol [buy a box] 398 R Trailblazer's Boots @Alexander Gering diff --git a/forge-gui/res/editions/Time Spiral Remastered.txt b/forge-gui/res/editions/Time Spiral Remastered.txt index c840d738798..4c13f0b3785 100644 --- a/forge-gui/res/editions/Time Spiral Remastered.txt +++ b/forge-gui/res/editions/Time Spiral Remastered.txt @@ -298,7 +298,7 @@ ScryfallCode=TSR 288 U Urza's Factory @Mark Tedin 289 M Vesuva @Zoltan Boros & Gabor Szikszai -[showcase] +[retro frame] 290 S Ajani's Pridemate @Svetlin Velinov 291 S Banishing Light @Willian Murai 292 S Containment Priest @John Stanko @@ -421,7 +421,7 @@ ScryfallCode=TSR 409 S Ramunap Ruins @Florian de Gesincourt 410 S Wastes @Raymond Swanland -[promo] +[buy a box] 411 R Lotus Bloom @Christopher Rush [tokens] diff --git a/forge-gui/res/editions/Unglued.txt b/forge-gui/res/editions/Unglued.txt index fb881546a66..440f8535341 100644 --- a/forge-gui/res/editions/Unglued.txt +++ b/forge-gui/res/editions/Unglued.txt @@ -30,7 +30,7 @@ ScryfallCode=UGL 18 C Clambassadors @Randy Elliott 19 C Clam-I-Am @Randy Elliott 20 C Clam Session @Randy Elliott -21 C Common Courtesy @Mike Raabe +21 U Common Courtesy @Mike Raabe 22 C Denied! @Quinton Hoover 23 C Double Take @Claymore J. Flapdoodle 24 C Fowl Play @Mark Poole diff --git a/forge-gui/res/editions/Unhinged.txt b/forge-gui/res/editions/Unhinged.txt index 1377b6c17cc..ecd990e8791 100644 --- a/forge-gui/res/editions/Unhinged.txt +++ b/forge-gui/res/editions/Unhinged.txt @@ -12,7 +12,7 @@ ScryfallCode=UNH [cards] 1 U Atinlay Igpay @Evkay Alkerway 2 C AWOL @Stephen Tappin -3 U Bosom Buddy @Dan Scott +3 U Bosom Buddy @Dan Murayama Scott 4 C Cardpecker @Richard Sardinha 5 C Cheap Ass @Randy Gallegos 6 C Circle of Protection: Art @Jim "Stop the Da Vinci Beatdown" Pavelec @@ -150,7 +150,7 @@ ScryfallCode=UNH 138 L Swamp @John Avon 139 L Mountain @John Avon 140 L Forest @John Avon -141 S Super Secret Tech @Dan Frazier +141 R Super Secret Tech @Dan Frazier [tokens] r_1_1_goblin diff --git a/forge-gui/res/editions/Unstable.txt b/forge-gui/res/editions/Unstable.txt index 23637ce69e2..1f0b8004b26 100644 --- a/forge-gui/res/editions/Unstable.txt +++ b/forge-gui/res/editions/Unstable.txt @@ -10,270 +10,270 @@ FoilAlwaysInCommonSlot=False ScryfallCode=UST [cards] -3a C Amateur Auteur -3b C Amateur Auteur -3c C Amateur Auteur -3d C Amateur Auteur -6 M Do-It-Yourself Seraph -7 U Gimme Five -8 C GO TO JAIL -9 U Half-Kitten, Half- -10 C Humming- -11 R Jackknight -12a U Knight of the Kitchen Sink $A -12b U Knight of the Kitchen Sink $B -12c U Knight of the Kitchen Sink $C -12d U Knight of the Kitchen Sink $D -12e U Knight of the Kitchen Sink $E -12f U Knight of the Kitchen Sink $F -13 U Knight of the Widget -14 U Midlife Upgrade -15 R Oddly Uneven -16 C Old Guard -17 C Ordinary Pony -18 U Rhino- -19 C Riveting Rigger -20 R Rules Lawyer -21 C Sacrifice Play -22 C Shaggy Camel -23 U Side Quest -24 C Success! -25 U Teacher's Pet -26 R Animate Library -27 C Blurry Beeble -28 C Chipper Chopper -29 R Clocknapper -30 C Crafty Octopus -31 U Crow Storm -32 C Defective Detective -33 U Five-Finger Discount -34 R Graveyard Busybody -35 U Half-Shark, Half- -36 R Incite Insight -37 U Kindly Cognician -38 C Magic Word -39 C Mer Man -40 U More or Less -41a C Novellamental -41b C Novellamental -41c C Novellamental -41d C Novellamental -42 C Numbing Jellyfish -43 U S.N.E.A.K. Dispatcher -44 U Socketed Sprocketer -45 C Spell Suck -46 U Spy Eye -47 U Suspicious Nanny -48 C Time Out -49a R Very Cryptic Command $A -49b R Very Cryptic Command $B -49c R Very Cryptic Command $C -49d R Very Cryptic Command $D -49e R Very Cryptic Command $E -49f R Very Cryptic Command $F -50 C Wall of Fortune -51 C Big Boa Constrictor -52 C capital offense -53 C Dirty Rat -54a C Extremely Slow Zombie -54b C Extremely Slow Zombie -54c C Extremely Slow Zombie -54d C Extremely Slow Zombie -55 C Finders, Keepers -56 R Hangman -57 C Hazmat Suit (Used) -58 C Hoisted Hireling -59 U Inhumaniac -60 R Masterful Ninja -61 U Ninja -62 U Old-Fashioned Vampire -63 R Over My Dead Bodies -64 U Overt Operative -65 U "Rumors of My Death..." -66 U Skull Saucer -67a U Sly Spy $A -67b U Sly Spy $B -67c U Sly Spy $C -67d U Sly Spy $D -67e U Sly Spy $E -67f U Sly Spy $F -68 C Snickering Squirrel -69 R Spike, Tournament Grinder -70 U Squirrel-Powered Scheme -71 C Steady-Handed Mook -72 C Stinging Scorpion -73 C Subcontract -74 M Summon the Pack -75 U Zombified -76 R The Big Idea -77 C Box of Free-Range Goblins -78 C Bumbling Pangolin -79 C Common Iguana -80 R The Countdown Is at One -81 C Feisty Stegosaurus -82a U Garbage Elemental $A -82b U Garbage Elemental $B -82c U Garbage Elemental $C -82d U Garbage Elemental $D -82e U Garbage Elemental $E -82f U Garbage Elemental $F -83 U Goblin Haberdasher -84 U Half-Orc, Half- -85 C Hammer Helper -86 U Hammer Jammer -87 U Hammerfest Boomtacular -88 M Infinity Elemental -89 C It That Gets Left Hanging -90 C Just Desserts -91 C Painiac -92 U Party Crasher -93 R Steamflogger Boss -94 R Steamflogger of the Month -95 U Steamflogger Temp -96 U Steamfloggery -97 U Super-Duper Death Ray -98a C Target Minotaur -98b C Target Minotaur -98c C Target Minotaur -98d C Target Minotaur -99 R Three-Headed Goblin -100 C Work a Double -101 C Wrench-Rigger -102 R As Luck Would Have It -103a C Beast in Show -103b C Beast in Show -103c C Beast in Show -103d C Beast in Show -104 U Chittering Doom -105 U Clever Combo -106 U Druid of the Sacred Beaker -107 C Eager Beaver -108 R Earl of Squirrel -109 U First Pick -110 U Ground Pounder -111 U Half-Squirrel, Half- -112 R Hydradoodle -113a R Ineffable Blessing $A -113b R Ineffable Blessing $B -113c R Ineffable Blessing $C -113d R Ineffable Blessing $D -113e R Ineffable Blessing $E -113f R Ineffable Blessing $F -114 C Joyride Rigger -115 U Monkey- -116 C Mother Kangaroo -117 C Multi-Headed -118 C Really Epic Punch -119 C Selfie Preservation -120 R Serpentine -121 U Shellephant -122 U Slaying Mantis -123 C Squirrel Dealer -124 U Steamflogger Service Rep -125 C Wild Crocodile -126 C Willing Test Subject -127 M Baron Von Count -128 R Better Than One -129 R Cramped Bunker -130 M Dr. Julius Jumblemorph -131 M The Grand Calcutron -132 R Grusilda, Monster Masher -133 R Hot Fix -134 M Ol' Buzzbark -135 M Phoebe, Head of S.N.E.A.K. -136 M Urza, Academy Headmaster -137 R X -138 R Mary O'Kill -139 R Angelic Rocket -140 U Border Guardian -141 U Buzzing Whack-a-Doodle -142 U Clock of DOOOOOOOOOOOOM! -143 U Cogmentor -144 U Contraption Cannon -145a C Curious Killbot -145b C Delighted Killbot -145c C Despondent Killbot -145d C Enraged Killbot -146 U Entirely Normal Armchair -147a R Everythingamajig $A -147b R Everythingamajig $B -147c R Everythingamajig $C -147d R Everythingamajig $D -147e R Everythingamajig $E -147f R Everythingamajig $F -148 C Gnome-Made Engine -149 R Handy Dandy Clone Machine -150 R Kindslaver -151 U Krark's Other Thumb -152 U Labro Bot -153 U Lobe Lobber -154 C Mad Science Fair Project -155 R Modular Monstrosity -156 U Proper Laboratory Attire -157 U Robo- -158 R Split Screen -159 U Staff of the Letter Magus -160 U Stamp of Approval -161 U Steam-Powered -162 U Steel Squirrel -163 M Sword of Dungeons & Dragons -164 C Voracious Vacuum -165a C Secret Base -165b C Secret Base -165c C Secret Base -165d C Secret Base -165e C Secret Base -166 R Watermarket -167 U Accessories to Murder -168 C Applied Aeronautics -169 U Arms Depot -170 C Auto-Key -171 M Bee-Bee Gun -172 C Boomflinger -173 C Buzz Buggy -174 R Deadly Poison Sampler -175 C Dictation Quillograph -176 U Dispatch Dispensary -177 C Division Table -178 U Dogsnail Engine -179 R Dual Doomsuits -180 R Duplication Device -181 M Faerie Aerie -182 U Genetic Recombinator -183 R Gift Horse -184 U Gnomeball Machine -185 R Goblin Slingshot -186 R Guest List -187 M Hard Hat Area -188 C Head Banger -189 R Hypnotic Swirly Disc -190 C Inflation Station -191 U Insufferable Syphon -192 U Jamming Device -193 C Lackey Recycler -194 C Mandatory Friendship Shackles -195 U Neural Network -196 R Oaken Power Suit -197 U Optical Optimizer -198 M Pet Project -199 C Quick-Stick Lick Trick -200 M Rapid Prototyper -201 R Record Store -202 R Refibrillator -203 C Sap Sucker -204 U Sundering Fork -205 U Targeting Rocket -206 U Thud-for-Duds -207 C Top-Secret Tunnel -208 C Tread Mill -209 U Turbo-Thwacking Auto-Hammer -210 C Twiddlestick Charger -211 U Widget Contraption -212 L Plains -213 L Island -214 L Swamp -215 L Mountain -216 L Forest +3a C Amateur Auteur @McLean Kendree +3b C Amateur Auteur @McLean Kendree +3c C Amateur Auteur @McLean Kendree +3d C Amateur Auteur @McLean Kendree +6 M Do-It-Yourself Seraph @David Sladek +7 U Gimme Five @Jesper Ejsing +8 C GO TO JAIL @Marco Teixeira +9 U Half-Kitten, Half- @Andrea Radeck +10 C Humming- @Mark Behm +11 R Jackknight @Ben Wootten +12a U Knight of the Kitchen Sink @Mark A. Nelson $A +12b U Knight of the Kitchen Sink @Mark A. Nelson $B +12c U Knight of the Kitchen Sink @Mark A. Nelson $C +12d U Knight of the Kitchen Sink @Mark A. Nelson $D +12e U Knight of the Kitchen Sink @Mark A. Nelson $E +12f U Knight of the Kitchen Sink @Mark A. Nelson $F +13 U Knight of the Widget @Emrah Elmasli +14 U Midlife Upgrade @Hector Ortiz +15 R Oddly Uneven @Ben Wootten +16 C Old Guard @David Sladek +17 C Ordinary Pony @Andrea Radeck +18 U Rhino- @YW Tang +19 C Riveting Rigger @Mark A. Nelson +20 R Rules Lawyer @Sean Murray +21 C Sacrifice Play @Matt Gaser +22 C Shaggy Camel @Kari Christensen +23 U Side Quest @Alex Konstad +24 C Success! @Andrea Radeck +25 U Teacher's Pet @Mark Behm +26 R Animate Library @Raymond Swanland +27 C Blurry Beeble @Jeff Miracola +28 C Chipper Chopper @Dmitry Burmak +29 R Clocknapper @Marco Teixeira +30 C Crafty Octopus @Mark Behm +31 U Crow Storm @YW Tang +32 C Defective Detective @Matt Dixon +33 U Five-Finger Discount @Milivoj Ćeran +34 R Graveyard Busybody @Bram Sels +35 U Half-Shark, Half- @Brynn Metheney +36 R Incite Insight @David Sladek +37 U Kindly Cognician @Mark Behm +38 C Magic Word @Carl Frank +39 C Mer Man @Mark Behm +40 U More or Less @Chris Seaman +41a C Novellamental @Tom Babbey +41b C Novellamental @Tom Babbey +41c C Novellamental @Tom Babbey +41d C Novellamental @Tom Babbey +42 C Numbing Jellyfish @Matt Dixon +43 U S.N.E.A.K. Dispatcher @John Thacker +44 U Socketed Sprocketer @David Sladek +45 C Spell Suck @Michael Phillippi +46 U Spy Eye @Ben Wootten +47 U Suspicious Nanny @Chris Seaman +48 C Time Out @Dave Allsop +49a R Very Cryptic Command @Wayne England $A +49b R Very Cryptic Command @Zoltan Boros $B +49c R Very Cryptic Command @Zoltan Boros $C +49d R Very Cryptic Command @Zoltan Boros $D +49e R Very Cryptic Command @Zoltan Boros $E +49f R Very Cryptic Command @Zoltan Boros $F +50 C Wall of Fortune @Tom Babbey +51 C Big Boa Constrictor @Kari Christensen +52 C capital offense @Matt Dixon +53 C Dirty Rat @Kari Christensen +54a C Extremely Slow Zombie @Emrah Elmasli +54b C Extremely Slow Zombie @Emrah Elmasli +54c C Extremely Slow Zombie @Emrah Elmasli +54d C Extremely Slow Zombie @Emrah Elmasli +55 C Finders, Keepers @Mark Behm +56 R Hangman @Alex Konstad +57 C Hazmat Suit (Used) @Michael Phillippi +58 C Hoisted Hireling @Alex Konstad +59 U Inhumaniac @Matt Dixon +60 R Masterful Ninja @Matt Gaser +61 U Ninja @David Sladek +62 U Old-Fashioned Vampire @Simon Dominic +63 R Over My Dead Bodies @Even Amundsen +64 U Overt Operative @Bram Sels +65 U "Rumors of My Death..." @Alex Konstad +66 U Skull Saucer @Mike Burns +67a U Sly Spy @Michael Phillippi $A +67b U Sly Spy @Michael Phillippi $B +67c U Sly Spy @Michael Phillippi $C +67d U Sly Spy @Michael Phillippi $D +67e U Sly Spy @Michael Phillippi $E +67f U Sly Spy @Michael Phillippi $F +68 C Snickering Squirrel @Michael Phillippi +69 R Spike, Tournament Grinder @Zoltan Boros +70 U Squirrel-Powered Scheme @Even Amundsen +71 C Steady-Handed Mook @Carl Frank +72 C Stinging Scorpion @YW Tang +73 C Subcontract @Hector Ortiz +74 M Summon the Pack @Matt Cavotta +75 U Zombified @Kev Walker +76 R The Big Idea @Bram Sels +77 C Box of Free-Range Goblins @Chris Seaman +78 C Bumbling Pangolin @YW Tang +79 C Common Iguana @Brynn Metheney +80 R The Countdown Is at One @Jesper Ejsing +81 C Feisty Stegosaurus @Kari Christensen +82a U Garbage Elemental @Hector Ortiz $A +82b U Garbage Elemental @Hector Ortiz $B +82c U Garbage Elemental @Hector Ortiz $C +82d U Garbage Elemental @Hector Ortiz $D +82e U Garbage Elemental @Hector Ortiz $E +82f U Garbage Elemental @Hector Ortiz $F +83 U Goblin Haberdasher @Jesper Ejsing +84 U Half-Orc, Half- @Kev Walker +85 C Hammer Helper @Dave Allsop +86 U Hammer Jammer @Wayne Reynolds +87 U Hammerfest Boomtacular @Dave Allsop +88 M Infinity Elemental @Seb McKinnon +89 C It That Gets Left Hanging @Jesper Ejsing +90 C Just Desserts @Zoltan Boros +91 C Painiac @McLean Kendree +92 U Party Crasher @Mike Burns +93 R Steamflogger Boss @Warren Mahy +94 R Steamflogger of the Month @Warren Mahy +95 U Steamflogger Temp @Jeff Miracola +96 U Steamfloggery @Emrah Elmasli +97 U Super-Duper Death Ray @Even Amundsen +98a C Target Minotaur @Warren Mahy +98b C Target Minotaur @Warren Mahy +98c C Target Minotaur @Warren Mahy +98d C Target Minotaur @Warren Mahy +99 R Three-Headed Goblin @Mike Burns +100 C Work a Double @Carl Frank +101 C Wrench-Rigger @Jesper Ejsing +102 R As Luck Would Have It @Milivoj Ćeran +103a C Beast in Show @Mike Burns +103b C Beast in Show @Mike Burns +103c C Beast in Show @Mike Burns +103d C Beast in Show @Mike Burns +104 U Chittering Doom @Kari Christensen +105 U Clever Combo @Kev Walker +106 U Druid of the Sacred Beaker @Simon Dominic +107 C Eager Beaver @Andrea Radeck +108 R Earl of Squirrel @Milivoj Ćeran +109 U First Pick @John Thacker +110 C Ground Pounder @Warren Mahy +111 U Half-Squirrel, Half- @Andrea Radeck +112 R Hydradoodle @Mathias Kollros +113a R Ineffable Blessing @Milivoj Ćeran $A +113b R Ineffable Blessing @Milivoj Ćeran $B +113c R Ineffable Blessing @Milivoj Ćeran $C +113d R Ineffable Blessing @Milivoj Ćeran $D +113e R Ineffable Blessing @Milivoj Ćeran $E +113f R Ineffable Blessing @Milivoj Ćeran $F +114 C Joyride Rigger @Wayne Reynolds +115 U Monkey- @Andrea Radeck +116 C Mother Kangaroo @Andrea Radeck +117 C Multi-Headed @YW Tang +118 C Really Epic Punch @Ben Wootten +119 C Selfie Preservation @Chris Seaman +120 R Serpentine @Kari Christensen +121 U Shellephant @Hector Ortiz +122 U Slaying Mantis @Ben Wootten +123 C Squirrel Dealer @Bram Sels +124 U Steamflogger Service Rep @Warren Mahy +125 C Wild Crocodile @Brynn Metheney +126 C Willing Test Subject @Dmitry Burmak +127 M Baron Von Count @Jesper Ejsing +128 R Better Than One @Alex Konstad +129 R Cramped Bunker @Ben Wootten +130 M Dr. Julius Jumblemorph @Simon Dominic +131 M The Grand Calcutron @Sean Murray +132 R Grusilda, Monster Masher @Mathias Kollros +133 R Hot Fix @David Sladek +134 M Ol' Buzzbark @Wayne Reynolds +135 M Phoebe, Head of S.N.E.A.K. @Ralph Horsley +136 M Urza, Academy Headmaster @Terese Nielsen +137 R X @Dmitry Burmak +138 R Mary O'Kill @Simon Dominic +139 R Angelic Rocket @Carl Critchlow +140 U Border Guardian @Chris Seaman +141 U Buzzing Whack-a-Doodle @Mark A. Nelson +142 U Clock of DOOOOOOOOOOOOM! @Tom Babbey +143 U Cogmentor @Matt Gaser +144 U Contraption Cannon @Mike Burns +145a C Curious Killbot @Alex Konstad +145b C Delighted Killbot @Alex Konstad +145c C Despondent Killbot @Alex Konstad +145d C Enraged Killbot @Alex Konstad +146 U Entirely Normal Armchair @Tom Babbey +147a R Everythingamajig @Chris Seaman $A +147b R Everythingamajig @Chris Seaman $B +147c R Everythingamajig @Chris Seaman $C +147d R Everythingamajig @Chris Seaman $D +147e R Everythingamajig @Chris Seaman $E +147f R Everythingamajig @Chris Seaman $F +148 C Gnome-Made Engine @Sean Murray +149 R Handy Dandy Clone Machine @Mike Burns +150 R Kindslaver @Zoltan Boros +151 U Krark's Other Thumb @Jeff Miracola +152 U Labro Bot @Carl Critchlow +153 U Lobe Lobber @Dmitry Burmak +154 C Mad Science Fair Project @Carl Frank +155 R Modular Monstrosity @Alex Konstad +156 U Proper Laboratory Attire @Tom Babbey +157 U Robo- @Matt Dixon +158 R Split Screen @Simon Dominic +159 U Staff of the Letter Magus @Daniel Ljunggren +160 U Stamp of Approval @Zoltan Boros +161 U Steam-Powered @Carl Critchlow +162 U Steel Squirrel @Carl Critchlow +163 M Sword of Dungeons & Dragons @Chris Rahn +164 C Voracious Vacuum @Matt Dixon +165a C Secret Base @John Thacker +165b C Secret Base @Matt Gaser +165c C Secret Base @Seb McKinnon +165d C Secret Base @Dave Allsop +165e C Secret Base @Simon Dominic +166 R Watermarket @Simon Dominic +167 U Accessories to Murder @Ralph Horsley +168 C Applied Aeronautics @Jason Felix +169 U Arms Depot @Chuck Lukacs +170 C Auto-Key @Jason Felix +171 M Bee-Bee Gun @Chuck Lukacs +172 C Boomflinger @Steve Prescott +173 C Buzz Buggy @Steve Prescott +174 R Deadly Poison Sampler @Ralph Horsley +175 C Dictation Quillograph @Ralph Horsley +176 U Dispatch Dispensary @Ralph Horsley +177 C Division Table @Franz Vohwinkel +178 U Dogsnail Engine @Chuck Lukacs +179 R Dual Doomsuits @Franz Vohwinkel +180 R Duplication Device @Jason Felix +181 M Faerie Aerie @Ralph Horsley +182 U Genetic Recombinator @Chuck Lukacs +183 R Gift Horse @Steve Prescott +184 U Gnomeball Machine @Jason Felix +185 R Goblin Slingshot @Steve Prescott +186 R Guest List @Franz Vohwinkel +187 M Hard Hat Area @Steve Prescott +188 C Head Banger @Steve Prescott +189 R Hypnotic Swirly Disc @Ralph Horsley +190 C Inflation Station @Chuck Lukacs +191 U Insufferable Syphon @Ralph Horsley +192 U Jamming Device @Franz Vohwinkel +193 C Lackey Recycler @Franz Vohwinkel +194 C Mandatory Friendship Shackles @Franz Vohwinkel +195 U Neural Network @Franz Vohwinkel +196 R Oaken Power Suit @Chuck Lukacs +197 U Optical Optimizer @Jason Felix +198 M Pet Project @Franz Vohwinkel +199 C Quick-Stick Lick Trick @Chuck Lukacs +200 M Rapid Prototyper @Jason Felix +201 R Record Store @Jason Felix +202 R Refibrillator @Chuck Lukacs +203 C Sap Sucker @Chuck Lukacs +204 U Sundering Fork @Franz Vohwinkel +205 U Targeting Rocket @Steve Prescott +206 U Thud-for-Duds @Steve Prescott +207 C Top-Secret Tunnel @Ralph Horsley +208 C Tread Mill @Jason Felix +209 U Turbo-Thwacking Auto-Hammer @Steve Prescott +210 C Twiddlestick Charger @Ralph Horsley +211 U Widget Contraption @Jason Felix +212 L Plains @John Avon +213 L Island @John Avon +214 L Swamp @John Avon +215 L Mountain @John Avon +216 L Forest @John Avon [tokens] storm_crow diff --git a/forge-gui/res/editions/War of the Spark.txt b/forge-gui/res/editions/War of the Spark.txt index 98d64a05dad..bf244121200 100644 --- a/forge-gui/res/editions/War of the Spark.txt +++ b/forge-gui/res/editions/War of the Spark.txt @@ -5,7 +5,7 @@ Name=War of the Spark Code2=WAR Type=Expansion BoosterCovers=3 -Booster=10 Common:fromSheet("WAR cards"):!fromSheet("WAR Secret Cards"), 3 Uncommon:fromSheet("WAR cards"):!fromSheet("WAR Secret Cards"), 1 RareMythic:fromSheet("WAR cards"):!fromSheet("WAR Secret Cards"), 1 BasicLand +Booster=10 Common:fromSheet("WAR cards"), 3 Uncommon:fromSheet("WAR cards"), 1 RareMythic:fromSheet("WAR cards"), 1 BasicLand BoosterMustContain=Planeswalker FatPack=10 FatPackExtraSlots=80 BasicLands @@ -14,9 +14,7 @@ ScryfallCode=WAR [cards] 1 R Karn, the Great Creator @Wisnu Tan -1★ R Karn, the Great Creator @Naochika Morishita 2 R Ugin, the Ineffable @Daarken -2★ R Ugin, the Ineffable @Maekawa Yuichi 3 U Ugin's Conjurant @Ryan Yee 4 U Ajani's Pridemate @Sidharth Chaturvedi 5 C Battlefield Promotion @Scott Murphy @@ -28,7 +26,6 @@ ScryfallCode=WAR 11 C Enforcer Griffin @Johan Grenier 12 M Finale of Glory @Stanton Feng 13 M Gideon Blackblade @Viktor Titov -13★ M Gideon Blackblade @Tada 14 C Gideon's Sacrifice @Chris Rallis 15 U Gideon's Triumph @Kieran Yanner 16 M God-Eternal Oketra @Grzegorz Rutkowski @@ -48,13 +45,11 @@ ScryfallCode=WAR 30 R Single Combat @Livia Prima 31 U Sunblade Angel @Johannes Voss 32 U Teyo, the Shieldmage @Magali Villeneuve -32★ U Teyo, the Shieldmage @Foo Midori 33 C Teyo's Lightshield @Igor Kieryluk 34 R Tomik, Distinguished Advokist @Johannes Voss 35 C Topple the Statue @Sidharth Chaturvedi 36 C Trusted Pegasus @Chris Rahn 37 U The Wanderer @Wesley Burt -37★ U The Wanderer @Norikatsu Miyoshi 38 C Wanderer's Strike @Sara Winters 39 C War Screecher @Dan Murayama Scott 40 C Ashiok's Skulker @Livia Prima @@ -72,16 +67,13 @@ ScryfallCode=WAR 52 U Flux Channeler @Heonhwa Choe 53 M God-Eternal Kefnet @Lius Lasahido 54 R Jace, Wielder of Mysteries @Anna Steinbauer -54★ R Jace, Wielder of Mysteries @Toshiaki Takayama 55 U Jace's Triumph @Kieran Yanner 56 U Kasmina, Enigmatic Mentor @Magali Villeneuve -56★ U Kasmina, Enigmatic Mentor @Mid 57 C Kasmina's Transmutation @Uriah Voth 58 C Kiora's Dambreaker @Mathias Kollros 59 U Lazotep Plating @Yeong-Hao Han 60 C Naga Eternal @Johann Bodin 61 U Narset, Parter of Veils @Magali Villeneuve -61★ U Narset, Parter of Veils @Foo Midori 62 R Narset's Reversal @Viktor Titov 63 C No Escape @G-host Lee 64 C Relentless Advance @Stanton Feng @@ -104,7 +96,6 @@ ScryfallCode=WAR 81 C Charity Extractor @Matt Stewart 82 R Command the Dreadhorde @Daarken 83 U Davriel, Rogue Shadowmage @Daarken -83★ U Davriel, Rogue Shadowmage @Hagiya Kaoru 84 C Davriel's Shadowfugue @Daarken 85 R Deliver Unto Evil @Seb McKinnon 86 R Dreadhorde Invasion @Stanton Feng @@ -119,11 +110,9 @@ ScryfallCode=WAR 95 C Lazotep Behemoth @Zezhou Chen 96 C Lazotep Reaver @Craig J Spearing 97 M Liliana, Dreadhorde General @Chris Rallis -97★ M Liliana, Dreadhorde General @Yoshitaka Amano 98 U Liliana's Triumph @Kieran Yanner 99 R Massacre Girl @Chris Rallis 100 U Ob Nixilis, the Hate-Twisted @Yongjae Choi -100★ U Ob Nixilis, the Hate-Twisted @Sansyu 101 C Ob Nixilis's Cruelty @Igor Kieryluk 102 U Price of Betrayal @Ryan Yee 103 C Shriekdiver @Piotr Dura @@ -143,7 +132,6 @@ ScryfallCode=WAR 117 C Burning Prophet @Mathias Kollros 118 C Chainwhip Cyclops @Johann Bodin 119 R Chandra, Fire Artisan @Yongjae Choi -119★ R Chandra, Fire Artisan @Ryota-H 120 C Chandra's Pyrohelix @Aleksi Briclot 121 U Chandra's Triumph @Kieran Yanner 122 U Cyclops Electromancer @Jason Felix @@ -160,7 +148,6 @@ ScryfallCode=WAR 133 M Ilharg, the Raze-Boar @Filip Burburan 134 C Invading Manticore @Jehan Choo 135 U Jaya, Venerated Firemage @Yongjae Choi -135★ U Jaya, Venerated Firemage @Maekawa Yuichi 136 C Jaya's Greeting @Victor Adame Minguez 137 R Krenko, Tin Street Kingpin @Mark Behm 138 R Mizzium Tank @Wayne Reynolds @@ -169,16 +156,13 @@ ScryfallCode=WAR 141 C Raging Kronch @Steve Prescott 142 C Samut's Sprint @Aleksi Briclot 143 R Sarkhan the Masterless @Kieran Yanner -143★ R Sarkhan the Masterless @Lack 144 C Sarkhan's Catharsis @Zack Stella 145 C Spellgorger Weird @James Paick 146 U Tibalt, Rakish Instigator @Chase Stone -146★ U Tibalt, Rakish Instigator @Clover.K 147 U Tibalt's Rager @Yongjae Choi 148 C Turret Ogre @Johann Bodin 149 C Arboreal Grazer @Jason Rainville 150 U Arlinn, Voice of the Pack @Ryan Pancoast -150★ U Arlinn, Voice of the Pack @D-suzuki 151 C Arlinn's Wolf @Kimonas Theodossiou 152 R Awakening of Vitu-Ghazi @Jaime Jones 153 C Band Together @Josh Hass @@ -193,13 +177,11 @@ ScryfallCode=WAR 162 C Giant Growth @Dmitry Burmak 163 M God-Eternal Rhonas @Lius Lasahido 164 U Jiang Yanggu, Wildcrafter @Anna Steinbauer -164★ U Jiang Yanggu, Wildcrafter @Daisuke Izuka 165 C Kraul Stinger @Randy Vargas 166 C Kronch Wrangler @Steve Prescott 167 U Mowu, Loyal Companion @Kimonas Theodossiou 168 C New Horizons @Eytan Zana 169 R Nissa, Who Shakes the World @Chris Rallis -169★ R Nissa, Who Shakes the World @Hitowa 170 U Nissa's Triumph @Kieran Yanner 171 U Paradise Druid @Nils Hamm 172 R Planewide Celebration @Wisnu Tan @@ -211,12 +193,10 @@ ScryfallCode=WAR 178 U Storm the Citadel @Grzegorz Rutkowski 179 C Thundering Ceratok @Izzy 180 R Vivien, Champion of the Wilds @Magali Villeneuve -180★ R Vivien, Champion of the Wilds @Hisashi Momose 181 R Vivien's Arkbow @Zack Stella 182 C Vivien's Grizzly @Lius Lasahido 183 C Wardscale Crocodile @Zezhou Chen 184 R Ajani, the Greathearted @Victor Adame Minguez -184★ R Ajani, the Greathearted @Miho Midorikawa 185 U Angrath's Rampage @Victor Adame Minguez 186 R Bioessence Hydra @Mathias Kollros 187 R Casualties of War @Tomasz Jedruszek @@ -224,7 +204,6 @@ ScryfallCode=WAR 189 U Deathsprout @Seb McKinnon 190 U Despark @Slawomir Maniak 191 R Domri, Anarch of Bolas @Raymond Swanland -191★ R Domri, Anarch of Bolas @Raita Kazama 192 U Domri's Ambush @Victor Adame Minguez 193 U Dovin's Veto @Izzy 194 R Dreadhorde Butcher @Piotr Dura @@ -241,50 +220,35 @@ ScryfallCode=WAR 205 U Merfolk Skydiver @Sara Winters 206 U Neoform @Bram Sels 207 M Nicol Bolas, Dragon-God @Raymond Swanland -207★ M Nicol Bolas, Dragon-God @Kaida Yuji 208 M Niv-Mizzet Reborn @Raymond Swanland 209 R Oath of Kaya @Wesley Burt 210 U Pledge of Unity @Chris Rallis 211 R Ral, Storm Conduit @Wesley Burt -211★ R Ral, Storm Conduit @Naochika Morishita 212 U Ral's Outburst @Joseph Meehan 213 M Roalesk, Apex Hybrid @Svetlin Velinov 214 R Role Reversal @Mathias Kollros 215 U Rubblebelt Rioters @Tomasz Jedruszek 216 R Solar Blaze @Adam Paquette 217 R Sorin, Vengeful Bloodlord @Tommy Arnold -217★ R Sorin, Vengeful Bloodlord @Yukie Tajima 218 R Soul Diviner @Randy Vargas 219 R Storrev, Devkarin Lich @Igor Kieryluk 220 R Tamiyo, Collector of Tales @Chase Stone -220★ R Tamiyo, Collector of Tales @Fuzichoco 221 R Teferi, Time Raveler @Chris Rallis -221★ R Teferi, Time Raveler @Shishizaru 222 U Tenth District Legionnaire @Victor Adame Minguez 223 R Time Wipe @Svetlin Velinov 224 R Tolsimir, Friend to Wolves @Ryan Pancoast 225 U Tyrant's Scorn @Svetlin Velinov 226 R Widespread Brutality @Victor Adame Minguez 227 U Angrath, Captain of Chaos @Slawomir Maniak -227★ U Angrath, Captain of Chaos @Sansyu 228 U Ashiok, Dream Render @Cynthia Sheppard -228★ U Ashiok, Dream Render @Hozan Shinomaru 229 U Dovin, Hand of Control @Kieran Yanner -229★ U Dovin, Hand of Control @Nablange 230 U Huatli, the Sun's Heart @Lius Lasahido -230★ U Huatli, the Sun's Heart @Mikio Masuda 231 U Kaya, Bane of the Dead @Magali Villeneuve -231★ U Kaya, Bane of the Dead @Mid 232 U Kiora, Behemoth Beckoner @Jaime Jones -232★ U Kiora, Behemoth Beckoner @Yamamoto Akifumi 233 U Nahiri, Storm of Stone @Aleksi Briclot -233★ U Nahiri, Storm of Stone @Yukie Tajima 234 U Saheeli, Sublime Artificer @Wesley Burt -234★ U Saheeli, Sublime Artificer @Hisashi Momose 235 U Samut, Tyrant Smasher @Aleksi Briclot -235★ U Samut, Tyrant Smasher @Norikatsu Miyoshi 236 U Vraska, Swarm's Eminence @Anna Steinbauer -236★ U Vraska, Swarm's Eminence @Ryota Murayama 237 U Firemind Vessel @Ravenna Tran 238 U God-Pharaoh's Statue @Igor Kieryluk 239 C Guild Globe @Daniel Ljunggren @@ -313,6 +277,46 @@ ScryfallCode=WAR 262 L Forest @Jonas De Ro 263 L Forest @Titus Lunter 264 L Forest @Richard Wright + +[promo] +1★ R Karn, the Great Creator @Naochika Morishita +2★ R Ugin, the Ineffable @Maekawa Yuichi +13★ M Gideon Blackblade @Tada +32★ U Teyo, the Shieldmage @Foo Midori +37★ U The Wanderer @Norikatsu Miyoshi +54★ R Jace, Wielder of Mysteries @Toshiaki Takayama +56★ U Kasmina, Enigmatic Mentor @Mid +61★ U Narset, Parter of Veils @Foo Midori +83★ U Davriel, Rogue Shadowmage @Hagiya Kaoru +97★ M Liliana, Dreadhorde General @Yoshitaka Amano +100★ U Ob Nixilis, the Hate-Twisted @Sansyu +119★ R Chandra, Fire Artisan @Ryota-H +135★ U Jaya, Venerated Firemage @Maekawa Yuichi +143★ R Sarkhan the Masterless @Lack +146★ U Tibalt, Rakish Instigator @Clover.K +150★ U Arlinn, Voice of the Pack @D-suzuki +164★ U Jiang Yanggu, Wildcrafter @Daisuke Izuka +169★ R Nissa, Who Shakes the World @Hitowa +180★ R Vivien, Champion of the Wilds @Hisashi Momose +184★ R Ajani, the Greathearted @Miho Midorikawa +191★ R Domri, Anarch of Bolas @Raita Kazama +207★ M Nicol Bolas, Dragon-God @Kaida Yuji +211★ R Ral, Storm Conduit @Naochika Morishita +217★ R Sorin, Vengeful Bloodlord @Yukie Tajima +220★ R Tamiyo, Collector of Tales @Fuzichoco +221★ R Teferi, Time Raveler @Shishizaru +227★ U Angrath, Captain of Chaos @Sansyu +228★ U Ashiok, Dream Render @Hozan Shinomaru +229★ U Dovin, Hand of Control @Nablange +230★ U Huatli, the Sun's Heart @Mikio Masuda +231★ U Kaya, Bane of the Dead @Mid +232★ U Kiora, Behemoth Beckoner @Yamamoto Akifumi +233★ U Nahiri, Storm of Stone @Yukie Tajima +234★ U Saheeli, Sublime Artificer @Hisashi Momose +235★ U Samut, Tyrant Smasher @Norikatsu Miyoshi +236★ U Vraska, Swarm's Eminence @Ryota Murayama + +[precon product] 265 M Gideon, the Oathsworn @Kieran Yanner 266 C Desperate Lunge @Deruchenko Alexander 267 R Gideon's Battle Cry @Zoltan Boros @@ -323,6 +327,8 @@ ScryfallCode=WAR 272 U Jace's Projection @Darek Zabrocki 273 R Jace's Ruse @Clint Cearley 274 C Simic Guildgate @Adam Paquette + +[buy a box] 275 M Tezzeret, Master of the Bridge @Chase Stone [rebalanced] @@ -347,3 +353,5 @@ voja_friend_to_elves w_0_3_wall_defender w_2_2_soldier_vigilance w_4_4_angel_flying_vigilance + + \ No newline at end of file diff --git a/forge-gui/res/editions/Zendikar Expeditions.txt b/forge-gui/res/editions/Zendikar Expeditions.txt index b7da0cd08f6..4b5b26925e2 100644 --- a/forge-gui/res/editions/Zendikar Expeditions.txt +++ b/forge-gui/res/editions/Zendikar Expeditions.txt @@ -5,6 +5,7 @@ Name=Zendikar Expeditions Type=Collector_Edition ScryfallCode=EXP +#Bonus cards for BFZ Battle for Zendikar [cards] 1 M Prairie Stream @Titus Lunter 2 M Sunken Hollow @Titus Lunter @@ -31,6 +32,9 @@ ScryfallCode=EXP 23 M Verdant Catacombs @Ryan Yee 24 M Arid Mesa @Ryan Yee 25 M Misty Rainforest @Ryan Yee + +#Bonus cards for OGW Oath of the Gatewatch +[special slot] 26 M Mystic Gate @Adam Paquette 27 M Sunken Ruins @Adam Paquette 28 M Graven Cairns @Adam Paquette diff --git a/forge-gui/res/editions/Zendikar Rising.txt b/forge-gui/res/editions/Zendikar Rising.txt index 3f2c5e882b9..a65ba6ef048 100644 --- a/forge-gui/res/editions/Zendikar Rising.txt +++ b/forge-gui/res/editions/Zendikar Rising.txt @@ -399,6 +399,8 @@ ScryfallCode=ZNR 377 R Skyclave Relic @Daniel Ljunggren 378 R Crawling Barrens @Jonas De Ro 379 R Throne of Makindi @Igor Kieryluk + +[precon product] 380 L Plains @Adam Paquette 381 L Island @Tianhua X 382 L Swamp @Adam Paquette From 85e3cef612c9a7b6afd0fdd6b0d18e7ca3d31fda Mon Sep 17 00:00:00 2001 From: Agetian Date: Sun, 24 Nov 2024 16:48:01 +0300 Subject: [PATCH 105/152] Revert "Update draftable edition sections (#6274)" (#6623) This reverts commit 46f3f01450d6a188d279e25b1993c96bbcb4cb91. --- .../src/main/java/forge/card/CardEdition.java | 27 +- .../item/generation/BoosterGenerator.java | 19 +- .../src/main/java/forge/util/FileSection.java | 223 +++++-- .../java/forge/util/FileSectionManual.java | 2 +- forge-gui/res/blockdata/printsheets.txt | 587 ++++++++++++++++++ .../res/editions/30th Anniversary Edition.txt | 309 ++++++++- .../Alchemy Horizons Baldur's Gate.txt | 31 +- forge-gui/res/editions/Amonkhet.txt | 6 +- .../res/editions/Battle for Zendikar.txt | 4 +- forge-gui/res/editions/Battlebond.txt | 74 +-- forge-gui/res/editions/Bloomburrow.txt | 30 +- ...ander Legends Battle for Baldur's Gate.txt | 42 +- forge-gui/res/editions/Commander Legends.txt | 2 +- .../editions/Conspiracy Take the Crown.txt | 4 +- .../res/editions/Dominaria Remastered.txt | 146 ++++- forge-gui/res/editions/Dominaria United.txt | 10 +- forge-gui/res/editions/Dominaria.txt | 6 +- .../res/editions/Double Masters 2022.txt | 2 +- forge-gui/res/editions/Double Masters.txt | 6 +- forge-gui/res/editions/Guilds of Ravnica.txt | 18 +- .../res/editions/Hour of Devastation.txt | 6 +- .../res/editions/Innistrad Crimson Vow.txt | 2 - .../res/editions/Innistrad Midnight Hunt.txt | 16 +- .../res/editions/Kamigawa Neon Dynasty.txt | 2 - forge-gui/res/editions/Khans of Tarkir.txt | 2 +- forge-gui/res/editions/Magic 2015.txt | 4 +- forge-gui/res/editions/Magic 2021.txt | 12 +- forge-gui/res/editions/Magic Origins.txt | 4 +- .../March of the Machine The Aftermath.txt | 12 - .../Masterpiece Series - Amonkhet.txt | 4 - forge-gui/res/editions/Masters Edition IV.txt | 16 +- forge-gui/res/editions/Modern Horizons 2.txt | 8 +- forge-gui/res/editions/Modern Horizons 3.txt | 41 +- forge-gui/res/editions/Modern Horizons.txt | 4 +- .../res/editions/Murders at Karlov Manor.txt | 35 +- .../res/editions/Oath of the Gatewatch.txt | 6 +- .../editions/Outlaws of Thunder Junction.txt | 30 +- .../res/editions/Phyrexia All Will Be One.txt | 14 +- forge-gui/res/editions/Portal.txt | 52 +- forge-gui/res/editions/Ravnica Allegiance.txt | 13 +- forge-gui/res/editions/Ravnica Remastered.txt | 2 +- .../res/editions/Streets of New Capenna.txt | 2 - forge-gui/res/editions/The Big Score.txt | 4 - forge-gui/res/editions/The Brothers War.txt | 10 +- ...ord of the Rings Tales of Middle-earth.txt | 20 +- .../res/editions/Time Spiral Remastered.txt | 4 +- forge-gui/res/editions/Unglued.txt | 2 +- forge-gui/res/editions/Unhinged.txt | 4 +- forge-gui/res/editions/Unstable.txt | 528 ++++++++-------- forge-gui/res/editions/War of the Spark.txt | 82 ++- .../res/editions/Zendikar Expeditions.txt | 4 - forge-gui/res/editions/Zendikar Rising.txt | 2 - 52 files changed, 1715 insertions(+), 780 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardEdition.java b/forge-core/src/main/java/forge/card/CardEdition.java index ea945205f75..96c6caf33dc 100644 --- a/forge-core/src/main/java/forge/card/CardEdition.java +++ b/forge-core/src/main/java/forge/card/CardEdition.java @@ -54,19 +54,24 @@ public final class CardEdition implements Comparable { // immutable public enum Type { UNKNOWN, + CORE, EXPANSION, STARTER, REPRINT, BOXED_SET, + COLLECTOR_EDITION, DUEL_DECK, PROMO, ONLINE, + DRAFT, + COMMANDER, MULTIPLAYER, FUNNY, + OTHER, // FALLBACK CATEGORY CUSTOM_SET; // custom sets @@ -126,23 +131,22 @@ public final class CardEdition implements Comparable { SPECIAL_SLOT("special slot"), //to help with convoluted boosters PRECON_PRODUCT("precon product"), BORDERLESS("borderless"), + BORDERLESS_PROFILE("borderless profile"), + BORDERLESS_FRAME("borderless frame"), ETCHED("etched"), SHOWCASE("showcase"), FULL_ART("full art"), EXTENDED_ART("extended art"), ALTERNATE_ART("alternate art"), - RETRO_FRAME("retro frame"), + ALTERNATE_FRAME("alternate frame"), BUY_A_BOX("buy a box"), PROMO("promo"), - PRERELEASE_PROMO("prerelease promo"), BUNDLE("bundle"), BOX_TOPPER("box topper"), DUNGEONS("dungeons"), JUMPSTART("jumpstart"), REBALANCED("rebalanced"), - ETERNAL("eternal"), - CONJURED("conjured"), - SCHEME("scheme"); + ETERNAL("eternal"); private final String name; @@ -211,7 +215,7 @@ public final class CardEdition implements Comparable { */ public static String getSortableCollectorNumber(final String collectorNumber){ String inputCollNumber = collectorNumber; - if (collectorNumber == null || collectorNumber.isEmpty()) + if (collectorNumber == null || collectorNumber.length() == 0) inputCollNumber = "50000"; // very big number of 5 digits to have them in last positions String matchedCollNr = sortableCollNumberLookup.getOrDefault(inputCollNumber, null); @@ -375,6 +379,7 @@ public final class CardEdition implements Comparable { public String getSlotReplaceCommonWith() { return slotReplaceCommonWith; } public String getAdditionalSheetForFoils() { return additionalSheetForFoils; } public String getAdditionalUnlockSet() { return additionalUnlockSet; } + public boolean getSmallSetOverride() { return smallSetOverride; } public String getDoublePickDuringDraft() { return doublePickDuringDraft; } public String getBoosterMustContain() { return boosterMustContain; } public String getBoosterReplaceSlotFromPrintSheet() { return boosterReplaceSlotFromPrintSheet; } @@ -509,7 +514,7 @@ public final class CardEdition implements Comparable { for (CardInSet card : cards) { int index = 1; if (cardToIndex.containsKey(card.name)) { - index = cardToIndex.get(card.name) + 1; + index = cardToIndex.get(card.name); } cardToIndex.put(card.name, index); @@ -704,6 +709,9 @@ public final class CardEdition implements Comparable { res.fatPackExtraSlots = metadata.get("FatPackExtraSlots", ""); switch (metadata.get("foil", "newstyle").toLowerCase()) { + case "notsupported": + res.foilType = FoilType.NOT_SUPPORTED; + break; case "oldstyle": case "classic": res.foilType = FoilType.OLD_STYLE; @@ -712,7 +720,6 @@ public final class CardEdition implements Comparable { case "modern": res.foilType = FoilType.MODERN; break; - case "notsupported": default: res.foilType = FoilType.NOT_SUPPORTED; break; @@ -767,7 +774,7 @@ public final class CardEdition implements Comparable { public void add(CardEdition item) { //Even though we want it to be read only, make an exception for custom content. if(lock) throw new UnsupportedOperationException("This is a read-only storage"); else map.put(item.getName(), item); - } + }; public void append(CardEdition.Collection C){ //Append custom editions if (lock) throw new UnsupportedOperationException("This is a read-only storage"); for(CardEdition E : C){ //Update the alias list as above or else it'll fail to look up. @@ -901,7 +908,7 @@ public final class CardEdition implements Comparable { StaticData.instance().getEditions().getOrderedEditions(), com.google.common.base.Predicates.and(hasBasicLands, artPreference::accept)); Iterator editionsIterator = editionsWithBasicLands.iterator(); - List selectedEditions = new ArrayList<>(); + List selectedEditions = new ArrayList(); while (editionsIterator.hasNext()) selectedEditions.add(editionsIterator.next()); if (selectedEditions.isEmpty()) diff --git a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java index ec1ee50aba4..812dd7ff779 100644 --- a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java +++ b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java @@ -69,6 +69,7 @@ public class BoosterGenerator { } List result = new ArrayList<>(); + List sheetsUsed = new ArrayList<>(); CardEdition edition = StaticData.instance().getEditions().get(template.getEdition()); @@ -232,6 +233,7 @@ public class BoosterGenerator { if (sheetKey.startsWith("wholeSheet")) { PrintSheet ps = getPrintSheet(sheetKey); result.addAll(ps.all()); + sheetsUsed.add(ps); continue; } @@ -250,7 +252,7 @@ public class BoosterGenerator { if ((edition.getName().equals("Planeshift")) && (slotType.startsWith(BoosterSlots.RARE)) && (foilSlot.startsWith(BoosterSlots.SPECIAL)) - ) { + ) { numCards--; } } @@ -262,6 +264,7 @@ public class BoosterGenerator { : edition.getSlotReplaceCommonWith().trim(); PrintSheet replaceSheet = getPrintSheet(replaceKey); result.addAll(replaceSheet.random(1, true)); + sheetsUsed.add(replaceSheet); System.out.println("Common was replaced with something from the replace sheet..."); replaceCommon = false; } @@ -280,6 +283,7 @@ public class BoosterGenerator { } result.addAll(paperCards); + sheetsUsed.add(ps); if (foilInThisSlot) { if (!foilAtEndOfPack) { @@ -391,6 +395,8 @@ public class BoosterGenerator { public static List getBoosterPack(SealedTemplateWithSlots template) { // SealedTemplateWithSlots ignores all Edition level params // Instead each slot defines their percentages on their own + + CardEdition edition = StaticData.instance().getEditions().get(template.getEdition()); List result = new ArrayList<>(); Map boosterSlots = template.getNamedSlots(); @@ -495,7 +501,7 @@ public class BoosterGenerator { * Replaces an already present card in the booster with a card from the supplied print sheet. * Nothing is replaced if there is no matching rarity found. * @param booster in which a card gets replaced - * @param printSheetKey print sheet key from which take the replacement card + * @param printSheetKey */ public static void replaceCardFromExtraSheet(List booster, String printSheetKey) { PrintSheet replacementSheet = StaticData.instance().getPrintSheets().get(printSheetKey); @@ -510,7 +516,7 @@ public class BoosterGenerator { * @param toAdd new card which replaces a card in the booster */ public static void replaceCard(List booster, PaperCard toAdd) { - Predicate rarityPredicate; + Predicate rarityPredicate = null; switch (toAdd.getRarity()) { case BasicLand: rarityPredicate = Presets.IS_BASIC_LAND; @@ -567,10 +573,11 @@ public class BoosterGenerator { } } + @SuppressWarnings("unchecked") public static PrintSheet makeSheet(String sheetKey, Iterable src) { PrintSheet ps = new PrintSheet(sheetKey); String[] sKey = TextUtil.splitWithParenthesis(sheetKey, ' ', 2); - Predicate setPred = (sKey.length > 1 ? IPaperCard.Predicates.printedInSets(sKey[1].split(" ")) : Predicates.alwaysTrue()); + Predicate setPred = (Predicate) (sKey.length > 1 ? IPaperCard.Predicates.printedInSets(sKey[1].split(" ")) : Predicates.alwaysTrue()); List operators = new LinkedList<>(Arrays.asList(TextUtil.splitWithParenthesis(sKey[0], ':'))); Predicate extraPred = buildExtraPredicate(operators); @@ -672,11 +679,11 @@ public class BoosterGenerator { Predicate toAdd = null; if (operator.equalsIgnoreCase(BoosterSlots.DUAL_FACED_CARD)) { toAdd = Predicates.compose( - Predicates.or( + Predicates.or( CardRulesPredicates.splitType(CardSplitType.Transform), CardRulesPredicates.splitType(CardSplitType.Meld), CardRulesPredicates.splitType(CardSplitType.Modal) - ), + ), PaperCard::getRules); } else if (operator.equalsIgnoreCase(BoosterSlots.LAND)) { toAdd = Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard::getRules); } else if (operator.equalsIgnoreCase(BoosterSlots.BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND; diff --git a/forge-core/src/main/java/forge/util/FileSection.java b/forge-core/src/main/java/forge/util/FileSection.java index dfa8eb58bca..e4db0e9dd5d 100644 --- a/forge-core/src/main/java/forge/util/FileSection.java +++ b/forge-core/src/main/java/forge/util/FileSection.java @@ -19,7 +19,12 @@ package forge.util; import java.text.NumberFormat; import java.text.ParseException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; @@ -28,60 +33,73 @@ import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; /** - * Parse text file to extract [sections] and key/value data. - * Store the result in a HashMap + * TODO: Write javadoc for this type. + * */ public class FileSection { /** The lines. */ - protected final Map lines; + private final Map lines; + + /** + * Gets the lines. + * + * @return the lines + */ + protected final Map getLines() { + return this.lines; + } /** * Instantiates a new file section. */ protected FileSection() { - lines = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + this(new TreeMap<>(String.CASE_INSENSITIVE_ORDER)); + } + + protected FileSection(Map lines0) { + lines = lines0; } public static final Pattern DOLLAR_SIGN_KV_SEPARATOR = Pattern.compile(Pattern.quote("$")); public static final Pattern ARROW_KV_SEPARATOR = Pattern.compile(Pattern.quote("->")); public static final Pattern EQUALS_KV_SEPARATOR = Pattern.compile(Pattern.quote("=")); public static final Pattern COLON_KV_SEPARATOR = Pattern.compile(Pattern.quote(":")); + private static final String BAR_PAIR_SPLITTER = Pattern.quote("|"); - private static final Table> parseToMapCache = HashBasedTable.create(); + private static Table> parseToMapCache = HashBasedTable.create(); - /** - * Parses the key=value text line and return a HashMap - * - * @param line the text line to parse - * @param kvSeparator the key/value separator - * @return a HashMap - */ public static Map parseToMap(final String line, final Pattern kvSeparator) { - Map cached = parseToMapCache.get(line, kvSeparator); - if (cached != null) { - return cached; + Map result = parseToMapCache.get(line, kvSeparator); + if (result != null) { + return result; + } + result = parseToMapImpl(line, kvSeparator); + parseToMapCache.put(line, kvSeparator, result); + return result; + } + + private static Map parseToMapImpl(final String line, final Pattern kvSeparator) { + if (StringUtils.isEmpty(line)) { + return Collections.emptyMap(); } - Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - if (!StringUtils.isEmpty(line)) { - for (final String dd : line.split(BAR_PAIR_SPLITTER)) { - final String[] v = kvSeparator.split(dd, 2); - result.put(v[0].trim(), v.length > 1 ? v[1].trim() : ""); - } + final Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + final String[] pairs = line.split(BAR_PAIR_SPLITTER); + for (final String dd : pairs) { + final String[] v = kvSeparator.split(dd, 2); + result.put(v[0].trim(), v.length > 1 ? v[1].trim() : ""); } - cached = Collections.unmodifiableMap(result); - parseToMapCache.put(line, kvSeparator, cached); - return cached; + return Collections.unmodifiableMap(result); } /** - * Parses the key=value text lines and return a HashMap + * Parses the. * - * @param lines the text lines to parse - * @param kvSeparator the key/value separator - * @return a FileSection Object containing the HashMap + * @param lines the lines + * @param kvSeparator the kv separator + * @return the file section */ public static FileSection parse(final Iterable lines, final Pattern kvSeparator) { final FileSection result = new FileSection(); @@ -94,36 +112,11 @@ public class FileSection { } /** - * Parses the sections ([sectionName]) from a list of text line + * Gets the. * - * @param lines - * the text lines to parse - * @return a LinkedHashMap containing the sections and text line associated. The order of the sections is preserved + * @param fieldName the field name + * @return the string */ - public static Map> parseSections(final List lines) { - final Map> result = new LinkedHashMap<>(); - int lineNumber = 0; - String section = null; - - do{ - String line = lines.get(lineNumber++); - if (line.startsWith("[") && line.endsWith("]")) { - section = line.substring(1, line.length() - 1); - if(!result.containsKey(section)) { - result.put(section, new ArrayList<>()); - } - } - else if(null != section && !line.isEmpty() && !line.startsWith("#")){ - result.get(section).add(line); - } - } - while(lineNumber> parseSections(final List source) { + final Map> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + String currentSection = ""; + List currentList = null; + + for (final String s : source) { + final String st = s.trim(); + if (st.length() == 0) { + continue; + } + if (st.startsWith("[") && st.endsWith("]")) { + if ((currentList != null) && (currentList.size() > 0)) { + final Object oldVal = result.get(currentSection); + if ((oldVal != null) && (oldVal instanceof List)) { + currentList.addAll((List) oldVal); + } + result.put(currentSection, currentList); + } + + final String newSection = st.substring(1, st.length() - 1); + currentSection = newSection; + currentList = null; + } else { + if (currentList == null) { + currentList = new ArrayList<>(); + } + currentList.add(st); + } + } + + // save final block + if ((currentList != null) && (currentList.size() > 0)) { + final Object oldVal = result.get(currentSection); + if ((oldVal != null) && (oldVal instanceof List)) { + currentList.addAll((List) oldVal); + } + result.put(currentSection, currentList); + } + + return result; + } + } diff --git a/forge-core/src/main/java/forge/util/FileSectionManual.java b/forge-core/src/main/java/forge/util/FileSectionManual.java index 999e82f42c1..5a7a6c58dfe 100644 --- a/forge-core/src/main/java/forge/util/FileSectionManual.java +++ b/forge-core/src/main/java/forge/util/FileSectionManual.java @@ -30,7 +30,7 @@ public class FileSectionManual extends FileSection { * @param value the value */ public void put(final String key, final String value) { - this.lines.put(key, value); + this.getLines().put(key, value); } } diff --git a/forge-gui/res/blockdata/printsheets.txt b/forge-gui/res/blockdata/printsheets.txt index be76601b822..8d892d27cbb 100644 --- a/forge-gui/res/blockdata/printsheets.txt +++ b/forge-gui/res/blockdata/printsheets.txt @@ -635,6 +635,41 @@ 1 Wayfaring Temple|RTR 1 Wild Beastmaster|RTR +[M15 Sample Cards] +1 Aegis Angel|M15 +1 Cancel|M15 +1 Centaur Courser|M15 +1 Divine Verdict|M15 +1 Furnace Whelp|M15 +1 Garruk's Packleader|M15 +1 Inspired Charge|M15 +1 Mahamoti Djinn|M15 +1 Nightmare|M15 +1 Seismic Strike|M15 +1 Sengir Vampire|M15 +1 Serra Angel|M15 +1 Shivan Dragon|M15 +1 Terra Stomper|M15 +1 Walking Corpse|M15 + +[ORI Sample Cards] +1 Aegis Angel|ORI +1 Divine Verdict|ORI +1 Eagle of the Watch|ORI +1 Serra Angel|ORI +1 Into the Void|ORI +1 Mahamoti Djinn|ORI +1 Weave Fate|ORI +1 Flesh to Dust|ORI +1 Mind Rot|ORI +1 Nightmare|ORI +1 Sengir Vampire|ORI +1 Fiery Hellhound|ORI +1 Shivan Dragon|ORI +1 Plummet|ORI +1 Prized Unicorn|ORI +1 Terra Stomper|ORI + [ORI Gideon] Akroan Jailer|ORI Ampryn Tactician|ORI @@ -854,6 +889,558 @@ Woodland Bellower|ORI Yeva's Forcemage|ORI Zendikar's Roil|ORI +[EXP Lands] +Prairie Stream|EXP +Sunken Hollow|EXP +Smoldering Marsh|EXP +Cinder Glade|EXP +Canopy Vista|EXP +Hallowed Fountain|EXP +Watery Grave|EXP +Blood Crypt|EXP +Stomping Ground|EXP +Temple Garden|EXP +Godless Shrine|EXP +Steam Vents|EXP +Overgrown Tomb|EXP +Sacred Foundry|EXP +Breeding Pool|EXP +Flooded Strand|EXP +Polluted Delta|EXP +Bloodstained Mire|EXP +Wooded Foothills|EXP +Windswept Heath|EXP +Marsh Flats|EXP +Scalding Tarn|EXP +Verdant Catacombs|EXP +Arid Mesa|EXP +Misty Rainforest|EXP + +[EXP Lands 2] +Mystic Gate|EXP +Sunken Ruins|EXP +Graven Cairns|EXP +Fire-Lit Thicket|EXP +Wooded Bastion|EXP +Fetid Heath|EXP +Cascade Bluffs|EXP +Twilight Mire|EXP +Rugged Prairie|EXP +Flooded Grove|EXP +Ancient Tomb|EXP +Dust Bowl|EXP +Eye of Ugin|EXP +Forbidden Orchard|EXP +Horizon Canopy|EXP +Kor Haven|EXP +Mana Confluence|EXP +Strip Mine|EXP +Tectonic Edge|EXP +Wasteland|EXP + +[MPS Amonkhet Invocations] +Austere Command|MPS_AKH +Aven Mindcensor|MPS_AKH +Containment Priest|MPS_AKH +Loyal Retainers|MPS_AKH +Oketra the True|MPS_AKH +Worship|MPS_AKH +Wrath of God|MPS_AKH +Consecrated Sphinx|MPS_AKH +Counterbalance|MPS_AKH +Counterspell|MPS_AKH +Cryptic Command|MPS_AKH +Daze|MPS_AKH +Divert|MPS_AKH +Force of Will|MPS_AKH +Kefnet the Mindful|MPS_AKH +Pact of Negation|MPS_AKH +Spell Pierce|MPS_AKH +Stifle|MPS_AKH +Attrition|MPS_AKH +Bontu the Glorified|MPS_AKH +Dark Ritual|MPS_AKH +Diabolic Intent|MPS_AKH +Entomb|MPS_AKH +Mind Twist|MPS_AKH +Aggravated Assault|MPS_AKH +Chain Lightning|MPS_AKH +Hazoret the Fervent|MPS_AKH +Rhonas the Indomitable|MPS_AKH +Maelstrom Pulse|MPS_AKH +Vindicate|MPS_AKH + +[MPS Hour of Devastation Invocations] +Armageddon|MPS_AKH +Capsize|MPS_AKH +Forbid|MPS_AKH +Omniscience|MPS_AKH +Opposition|MPS_AKH +Sunder|MPS_AKH +Threads of Disloyalty|MPS_AKH +Avatar of Woe|MPS_AKH +Damnation|MPS_AKH +Desolation Angel|MPS_AKH +Diabolic Edict|MPS_AKH +Doomsday|MPS_AKH +No Mercy|MPS_AKH +Slaughter Pact|MPS_AKH +Thoughtseize|MPS_AKH +Blood Moon|MPS_AKH +Boil|MPS_AKH +Shatterstorm|MPS_AKH +Through the Breach|MPS_AKH +Choke|MPS_AKH +The Locust God|MPS_AKH +Lord of Extinction|MPS_AKH +The Scarab God|MPS_AKH +The Scorpion God|MPS_AKH + +[AKH Planeswalker Decks and Toolkit] +Gideon, Martial Paragon +Companion of the Trials +Gideon's Resolve +Graceful Cat +Stone Quarry +Liliana, Death Wielder +Desiccated Naga +Liliana's Influence +Tattered Mummy +Foul Orchard +Cinder Barrens +Forsaken Sanctuary +Highland Lake +Meandering River +Submerged Boneyard +Timber Gorge +Tranquil Expanse +Woodland Stream + +[HOU Planeswalker Decks and Toolkit] +Nissa, Genesis Mage +Avid Reclaimer +Brambleweft Behemoth +Nissa's Encouragement +Woodland Stream +Nicol Bolas, the Deceiver +Wasp of the Bitter End +Zealot of the God-Pharaoh +Visage of Bolas +Cinder Barrens + +[DOM Planeswalker Decks and Additional Promo] +Teferi, Timebender +Temporal Machinations +Niambi, Faithful Healer +Teferi's Sentinel +Meandering River +Chandra, Bold Pyromancer +Chandra's Outburst +Karplusan Hound +Pyromantic Pilgrim +Timber Gorge +Firesong and Sunspeaker + +[BBD RareMythic] +1 Will Kenrith|BBD|1 +1 Rowan Kenrith|BBD|1 +8 Regna, the Redeemer +8 Krav, the Unredeemed +8 Zndrsplt, Eye of Wisdom +8 Okaun, Eye of Chaos +8 Virtus the Veiled +8 Gorm the Great +8 Khorvath Brightflame +8 Sylvia Brightspear +8 Pir, Imaginative Rascal +8 Toothy, Imaginary Friend +1 Arena Rector +1 Brightling +8 Play of the Game +8 Regna's Sanction +8 Together Forever +1 Arcane Artisan +8 Game Plan +8 Spellseeker +8 Zndrsplt's Judgment +1 Archfiend of Despair +8 Mindblade Render +1 Stunning Reversal +8 Thrilling Encore +8 Virtus's Maneuver +8 Bonus Round +8 Khorvath's Fury +1 Najeela, the Blade-Blossom +8 Stolen Strategy +1 Bramble Sovereign +8 Generous Patron +1 Grothama, All-Devouring +8 Pir's Whim +8 Archon of Valor's Reach +8 Last One Standing +8 Sentinel Tower +8 Victory Chimes +8 Bountiful Promenade +8 Luxury Suite +8 Morphic Pool +8 Sea of Clouds +8 Spire Garden +8 Angelic Chorus +8 Kor Spiritdancer +1 Land Tax +8 Mangara of Corondor +8 Mystic Confluence +8 Sower of Temptation +8 Tidespout Tyrant +1 True-Name Nemesis +8 Diabolic Intent +1 Nirkana Revenant +8 Noosegraf Mob +8 Nyxathid +8 Goblin Razerunners +8 Magmatic Force +8 War's Toll +1 Doubling Season +8 Greater Good +8 Magus of the Candelabra +8 Seedborn Muse +8 Vigor +8 Apocalypse Hydra +8 Evil Twin +8 Gwafa Hazid, Profiteer +8 Mind's Eye +1 Mycosynth Lattice + +[M19 Secret Cards] +Ajani, Wise Counselor +Ajani's Influence +Court Cleric +Serra's Guardian +Silverbeak Griffin +Tezzeret, Cruel Machinist +Riddlemaster Sphinx +Pendulum of Patterns +Tezzeret's Gatebreaker +Tezzeret's Strider +Liliana, the Necromancer +Arisen Gorgon +Gravewaker +Liliana's Spoils +Tattered Mummy +Sarkhan, Dragonsoul +Kargan Dragonrider +Sarkhan's Dragonfire +Sarkhan's Whelp +Shivan Dragon +Vivien of the Arkbow +Aggressive Mammoth +Skalla Wolf +Ursine Champion +Vivien's Jaguar +Nexus of Fate +Sun Sentinel +Air Elemental +Befuddle +Mist-Cloaked Herald +Waterknot +Grasping Scoundrel +Radiating Lightning +Llanowar Elves +Cinder Barrens +Forsaken Sanctuary +Foul Orchard +Highland Lake +Meandering River +Stone Quarry +Submerged Boneyard +Timber Gorge +Tranquil Expanse +Woodland Stream + +[M19 Lands] +5 Cinder Barrens|M19 +5 Forsaken Sanctuary|M19 +5 Foul Orchard|M19 +5 Highland Lake|M19 +5 Meandering River|M19 +5 Stone Quarry|M19 +5 Submerged Boneyard|M19 +5 Timber Gorge|M19 +5 Tranquil Expanse|M19 +5 Woodland Stream|M19 +14 Forest|M19 +14 Island|M19 +14 Mountain|M19 +14 Plains|M19 +14 Swamp|M19 + +[GRN Lands] +10 Golgari Guildgate|GRN +10 Izzet Guildgate|GRN +10 Selesnya Guildgate|GRN +10 Dimir Guildgate|GRN +10 Boros Guildgate|GRN + +[GRN Secret Cards] +Ral, Caller of Storms +Ral's Dispersal +Ral's Staticaster +Vraska, Regal Gorgon +Attendant of Vraska +Vraska's Stoneglare +Impervious Greatwurm +Precision Bolt +Kraul Raider +Golgari Guildgate|GRN +Izzet Guildgate|GRN +Selesnya Guildgate|GRN +Dimir Guildgate|GRN +Boros Guildgate|GRN + +[RNA Lands] +10 Azorius Guildgate|RNA +10 Gruul Guildgate|RNA +10 Orzhov Guildgate|RNA +10 Rakdos Guildgate|RNA +10 Simic Guildgate|RNA + +[RNA Secret Cards] +Dovin, Architect of Law +Elite Arrester +Dovin's Dismissal +Dovin's Automaton +Domri, City Smasher +Ragefire +Charging War Boar +Domri's Nodorog +The Haunt of Hightower +Azorius Guildgate|RNA +Gruul Guildgate|RNA +Orzhov Guildgate|RNA +Rakdos Guildgate|RNA +Simic Guildgate|RNA + +[WAR Secret Cards] +Gideon, the Oathsworn +Desperate Lunge +Gideon's Battle Cry +Gideon's Company +Orzhov Guildgate|WAR +Jace, Arcane Strategist +Guildpact Informant +Jace's Projection +Jace's Ruse +Simic Guildgate|WAR +Tezzeret, Master of the Bridge + +[MH1 Lands] +10 Snow-Covered Plains|MH1 +10 Snow-Covered Island|MH1 +10 Snow-Covered Swamp|MH1 +10 Snow-Covered Mountain|MH1 +10 Snow-Covered Forest|MH1 + +[MH1 Secret Cards] +Flusterstorm +Snow-Covered Plains +Snow-Covered Island +Snow-Covered Swamp +Snow-Covered Mountain +Snow-Covered Forest + +[M20 Secret Cards] +Rienne, Angel of Rebirth +Ajani, Inspiring Leader +Goldmane Griffin +Savannah Sage +Twinblade Paladin +Mu Yanling, Celestial Wind +Celestial Messenger +Waterkin Shaman +Yanling's Harbinger +Sorin, Vampire Lord +Savage Gorger +Sorin's Guide +Thirsting Bloodlord +Chandra, Flame's Fury +Chandra's Flame Wave +Pyroclastic Elemental +Wildfire Elemental +Vivien, Nature's Avenger +Ethereal Elk +Gnarlback Rhino +Vivien's Crocodile +Angelic Guardian +Bastion Enforcer +Concordia Pegasus +Haazda Officer +Impassioned Orator +Imperial Outrider +Ironclad Krovod +Prowling Caracal +Serra's Guardian +Show of Valor +Siege Mastodon +Take Vengeance +Trusted Pegasus +Coral Merfolk +Phantom Warrior +Riddlemaster Sphinx +Snapping Drake +Bartizan Bats +Bogstomper +Dark Remedy +Disentomb +Gravewaker +Skeleton Archer +Sorin's Thirst +Vampire Opportunist +Walking Corpse +Engulfing Eruption +Fearless Halberdier +Goblin Assailant +Hostile Minotaur +Immortal Phoenix +Nimble Birdsticker +Rubblebelt Recluse +Shivan Dragon +Volcanic Dragon +Aggressive Mammoth +Bristling Boar +Canopy Spider +Frilled Sandwalla +Oakenform +Prized Unicorn +Titanic Growth +Woodland Mystic + +Bloodfell Caves +Blossoming Sands +Dismal Backwater +Jungle Hollow +Rugged Highlands +Scoured Barrens +Swiftwater Cliffs +Thornwood Falls +Tranquil Cove +Wind-Scarred Crag +Evolving Wilds + +[M20 Lands] +5 Bloodfell Caves|M20 +5 Blossoming Sands|M20 +5 Dismal Backwater|M20 +5 Jungle Hollow|M20 +5 Rugged Highlands|M20 +5 Scoured Barrens|M20 +5 Swiftwater Cliffs|M20 +5 Thornwood Falls|M20 +5 Tranquil Cove|M20 +5 Wind-Scarred Crag|M20 +5 Evolving Wilds +13 Forest|M20 +13 Island|M20 +13 Mountain|M20 +13 Plains|M20 +13 Swamp|M20 + +[ELD Secret Cards] +Kenrith, the Returned King +Rowan, Fearless Sparkmage +Garrison Griffin +Rowan's Battleguard +Rowan's Stalwarts +Wind-Scarred Crag +Oko, the Trickster +Oko's Accomplices +Bramblefort Fink +Oko's Hospitality +Thornwood Falls +Mace of the Valiant +Silverwing Squadron +Faerie Formation +Shimmer Dragon +Workshop Elders +Chittering Witch +Taste of Death +Embereth Skyblazer +Steelbane Hydra +Thorn Mammoth +Alela, Artful Provocateur +Banish into Fable +Chulane, Teller of Tales +Gluttonous Troll +Knights' Charge +Korvold, Fae-Cursed King +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 Locthwain|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 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 +Kenrith's Transformation|ELD|2 +Improbable Alliance|ELD|2 +Inspiring Veteran|ELD|2 + [JMP Above the Clouds 1] 1 Warden of Evos Isle|JMP 1 Inniaz, the Gale Force|JMP diff --git a/forge-gui/res/editions/30th Anniversary Edition.txt b/forge-gui/res/editions/30th Anniversary Edition.txt index abc30da2ce6..de6b0282f84 100644 --- a/forge-gui/res/editions/30th Anniversary Edition.txt +++ b/forge-gui/res/editions/30th Anniversary Edition.txt @@ -4,7 +4,7 @@ Date=2022-11-28 Name=30th Anniversary Edition Type=Collector_Edition BoosterCovers=3 -Booster=7 Common:fromsheet("30A cards"), 3 Uncommon:fromSheet("30A cards"), 1 Rare:fromSheet("30A cards"), 2 BasicLand:fromSheet("30A cards"), 1 BasicLand:fromSheet("30A retro frame"), 1 !BasicLand:fromSheet("30A retro frame") +Booster=7 Common:fromsheet("30A cards"), 3 Uncommon:fromSheet("30A cards"), 1 Rare:fromSheet("30A cards"), 2 BasicLand:fromSheet("30A cards"), 1 fromSheet("30A retrolands"), 1 fromSheet("30A retrocards") Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 ScryfallCode=30A @@ -275,6 +275,7 @@ ScryfallCode=30A 263 R Nevinyrral's Disk @Mark Tedin 264 U Obsianus Golem @Jesper Myrfors 265 U Rod of Ruin @Christopher Rush +266 U Sol Ring @Mark Tedin 267 U Soul Net @Dameon Willich 268 R Sunglasses of Urza @Dan Frazier 269 U Throne of Bone @Anson Maddocks @@ -306,11 +307,6 @@ ScryfallCode=30A 295 L Forest @Christopher Rush 296 L Forest @Christopher Rush 297 L Forest @Christopher Rush - -[alternate art] -266 U Sol Ring @Mark Tedin - -[retro frame] 298 R Animate Wall @Dan Frazier 299 R Armageddon @Jesper Myrfors 300 R Balance @Mark Poole @@ -609,6 +605,307 @@ ScryfallCode=30A 593 L Forest @Christopher Rush 594 L Forest @Christopher Rush +[retrocards] +1 Animate Wall|30A|2 +1 Armageddon|30A|2 +1 Balance|30A|2 +1 Benalish Hero|30A|2 +1 Black Ward|30A|2 +1 Blaze of Glory|30A|2 +1 Blessing|30A|2 +1 Blue Ward|30A|2 +1 Castle|30A|2 +1 Circle of Protection: Black|30A|2 +1 Circle of Protection: Blue|30A|2 +1 Circle of Protection: Green|30A|2 +1 Circle of Protection: Red|30A|2 +1 Circle of Protection: White|30A|2 +1 Consecrate Land|30A|2 +1 Conversion|30A|2 +1 Death Ward|30A|2 +1 Disenchant|30A|2 +1 Farmstead|30A|2 +1 Green Ward|30A|2 +1 Guardian Angel|30A|2 +1 Healing Salve|30A|2 +1 Holy Armor|30A|2 +1 Holy Strength|30A|2 +1 Island Sanctuary|30A|2 +1 Karma|30A|2 +1 Lance|30A|2 +1 Mesa Pegasus|30A|2 +1 Northern Paladin|30A|2 +1 Pearled Unicorn|30A|2 +1 Personal Incarnation|30A|2 +1 Purelace|30A|2 +1 Red Ward|30A|2 +1 Resurrection|30A|2 +1 Reverse Damage|30A|2 +1 Righteousness|30A|2 +1 Samite Healer|30A|2 +1 Savannah Lions|30A|2 +1 Serra Angel|30A|2 +1 Swords to Plowshares|30A|2 +1 Veteran Bodyguard|30A|2 +1 Wall of Swords|30A|2 +1 White Knight|30A|2 +1 White Ward|30A|2 +1 Wrath of God|30A|2 +1 Air Elemental|30A|2 +1 Ancestral Recall|30A|2 +1 Animate Artifact|30A|2 +1 Blue Elemental Blast|30A|2 +1 Braingeyser|30A|2 +1 Clone|30A|2 +1 Control Magic|30A|2 +1 Copy Artifact|30A|2 +1 Counterspell|30A|2 +1 Creature Bond|30A|2 +1 Drain Power|30A|2 +1 Feedback|30A|2 +1 Flight|30A|2 +1 Invisibility|30A|2 +1 Jump|30A|2 +1 Lifetap|30A|2 +1 Lord of Atlantis|30A|2 +1 Magical Hack|30A|2 +1 Mahamoti Djinn|30A|2 +1 Mana Short|30A|2 +1 Merfolk of the Pearl Trident|30A|2 +1 Phantasmal Forces|30A|2 +1 Phantasmal Terrain|30A|2 +1 Phantom Monster|30A|2 +1 Pirate Ship|30A|2 +1 Power Leak|30A|2 +1 Power Sink|30A|2 +1 Prodigal Sorcerer|30A|2 +1 Psionic Blast|30A|2 +1 Psychic Venom|30A|2 +1 Sea Serpent|30A|2 +1 Siren's Call|30A|2 +1 Sleight of Mind|30A|2 +1 Spell Blast|30A|2 +1 Stasis|30A|2 +1 Steal Artifact|30A|2 +1 Thoughtlace|30A|2 +1 Time Walk|30A|2 +1 Timetwister|30A|2 +1 Twiddle|30A|2 +1 Unsummon|30A|2 +1 Vesuvan Doppelganger|30A|2 +1 Volcanic Eruption|30A|2 +1 Wall of Air|30A|2 +1 Wall of Water|30A|2 +1 Water Elemental|30A|2 +1 Animate Dead|30A|2 +1 Bad Moon|30A|2 +1 Black Knight|30A|2 +1 Bog Wraith|30A|2 +1 Cursed Land|30A|2 +1 Dark Ritual|30A|2 +1 Deathgrip|30A|2 +1 Deathlace|30A|2 +1 Demonic Hordes|30A|2 +1 Demonic Tutor|30A|2 +1 Drain Life|30A|2 +1 Drudge Skeletons|30A|2 +1 Evil Presence|30A|2 +1 Fear|30A|2 +1 Frozen Shade|30A|2 +1 Gloom|30A|2 +1 Howl from Beyond|30A|2 +1 Hypnotic Specter|30A|2 +1 Lich|30A|2 +1 Lord of the Pit|30A|2 +1 Mind Twist|30A|2 +1 Nether Shadow|30A|2 +1 Nettling Imp|30A|2 +1 Nightmare|30A|2 +1 Paralyze|30A|2 +1 Pestilence|30A|2 +1 Plague Rats|30A|2 +1 Raise Dead|30A|2 +1 Royal Assassin|30A|2 +1 Sacrifice|30A|2 +1 Scathe Zombies|30A|2 +1 Scavenging Ghoul|30A|2 +1 Sengir Vampire|30A|2 +1 Simulacrum|30A|2 +1 Sinkhole|30A|2 +1 Terror|30A|2 +1 Unholy Strength|30A|2 +1 Wall of Bone|30A|2 +1 Warp Artifact|30A|2 +1 Sol Ring|30A|2 +1 Will-o'-the-Wisp|30A|2 +1 Word of Command|30A|2 +1 Zombie Master|30A|2 +1 Burrowing|30A|2 +1 Chaoslace|30A|2 +1 Disintegrate|30A|2 +1 Dragon Whelp|30A|2 +1 Dwarven Demolition Team|30A|2 +1 Dwarven Warriors|30A|2 +1 Earth Elemental|30A|2 +1 Earthquake|30A|2 +1 False Orders|30A|2 +1 Fire Elemental|30A|2 +1 Fireball|30A|2 +1 Firebreathing|30A|2 +1 Flashfires|30A|2 +1 Fork|30A|2 +1 Goblin Balloon Brigade|30A|2 +1 Goblin King|30A|2 +1 Granite Gargoyle|30A|2 +1 Gray Ogre|30A|2 +1 Hill Giant|30A|2 +1 Hurloon Minotaur|30A|2 +1 Ironclaw Orcs|30A|2 +1 Keldon Warlord|30A|2 +1 Lightning Bolt|30A|2 +1 Mana Flare|30A|2 +1 Manabarbs|30A|2 +1 Mons's Goblin Raiders|30A|2 +1 Orcish Artillery|30A|2 +1 Orcish Oriflamme|30A|2 +1 Power Surge|30A|2 +1 Raging River|30A|2 +1 Red Elemental Blast|30A|2 +1 Roc of Kher Ridges|30A|2 +1 Rock Hydra|30A|2 +1 Sedge Troll|30A|2 +1 Shatter|30A|2 +1 Shivan Dragon|30A|2 +1 Smoke|30A|2 +1 Stone Giant|30A|2 +1 Stone Rain|30A|2 +1 Tunnel|30A|2 +1 Two-Headed Giant of Foriys|30A|2 +1 Uthden Troll|30A|2 +1 Wall of Fire|30A|2 +1 Wall of Stone|30A|2 +1 Wheel of Fortune|30A|2 +1 Aspect of Wolf|30A|2 +1 Berserk|30A|2 +1 Birds of Paradise|30A|2 +1 Camouflage|30A|2 +1 Channel|30A|2 +1 Cockatrice|30A|2 +1 Craw Wurm|30A|2 +1 Elvish Archers|30A|2 +1 Fastbond|30A|2 +1 Fog|30A|2 +1 Force of Nature|30A|2 +1 Fungusaur|30A|2 +1 Gaea's Liege|30A|2 +1 Giant Growth|30A|2 +1 Giant Spider|30A|2 +1 Grizzly Bears|30A|2 +1 Hurricane|30A|2 +1 Ice Storm|30A|2 +1 Instill Energy|30A|2 +1 Ironroot Treefolk|30A|2 +1 Kudzu|30A|2 +1 Ley Druid|30A|2 +1 Lifeforce|30A|2 +1 Lifelace|30A|2 +1 Living Artifact|30A|2 +1 Living Lands|30A|2 +1 Llanowar Elves|30A|2 +1 Lure|30A|2 +1 Natural Selection|30A|2 +1 Regeneration|30A|2 +1 Regrowth|30A|2 +1 Scryb Sprites|30A|2 +1 Shanodin Dryads|30A|2 +1 Stream of Life|30A|2 +1 Thicket Basilisk|30A|2 +1 Timber Wolves|30A|2 +1 Tranquility|30A|2 +1 Tsunami|30A|2 +1 Verduran Enchantress|30A|2 +1 Wall of Brambles|30A|2 +1 Wall of Ice|30A|2 +1 Wall of Wood|30A|2 +1 Wanderlust|30A|2 +1 War Mammoth|30A|2 +1 Web|30A|2 +1 Wild Growth|30A|2 +1 Ankh of Mishra|30A|2 +1 Basalt Monolith|30A|2 +1 Black Lotus|30A|2 +1 Black Vise|30A|2 +1 Celestial Prism|30A|2 +1 Chaos Orb|30A|2 +1 Clockwork Beast|30A|2 +1 Conservator|30A|2 +1 Copper Tablet|30A|2 +1 Crystal Rod|30A|2 +1 Cyclopean Tomb|30A|2 +1 Dingus Egg|30A|2 +1 Disrupting Scepter|30A|2 +1 Forcefield|30A|2 +1 Gauntlet of Might|30A|2 +1 Glasses of Urza|30A|2 +1 Helm of Chatzuk|30A|2 +1 The Hive|30A|2 +1 Howling Mine|30A|2 +1 Icy Manipulator|30A|2 +1 Illusionary Mask|30A|2 +1 Iron Star|30A|2 +1 Ivory Cup|30A|2 +1 Jade Monolith|30A|2 +1 Jade Statue|30A|2 +1 Jayemdae Tome|30A|2 +1 Juggernaut|30A|2 +1 Kormus Bell|30A|2 +1 Library of Leng|30A|2 +1 Living Wall|30A|2 +1 Mana Vault|30A|2 +1 Meekstone|30A|2 +1 Mox Emerald|30A|2 +1 Mox Jet|30A|2 +1 Mox Pearl|30A|2 +1 Mox Ruby|30A|2 +1 Mox Sapphire|30A|2 +1 Nevinyrral's Disk|30A|2 +1 Obsianus Golem|30A|2 +1 Rod of Ruin|30A|2 +1 Sol Ring|30A|2 +1 Soul Net|30A|2 +1 Sunglasses of Urza|30A|2 +1 Throne of Bone|30A|2 +1 Time Vault|30A|2 +1 Winter Orb|30A|2 +1 Wooden Sphere|30A|2 +1 Badlands|30A|2 +1 Bayou|30A|2 +1 Plateau|30A|2 +1 Savannah|30A|2 +1 Scrubland|30A|2 +1 Taiga|30A|2 +1 Tropical Island|30A|2 +1 Tundra|30A|2 +1 Underground Sea|30A|2 +1 Volcanic Island|30A|2 + +[retrolands] +1 Plains|30A|4 +1 Plains|30A|5 +1 Plains|30A|6 +1 Island|30A|4 +1 Island|30A|5 +1 Island|30A|6 +1 Swamp|30A|4 +1 Swamp|30A|5 +1 Swamp|30A|6 +1 Mountain|30A|4 +1 Mountain|30A|5 +1 Mountain|30A|6 +1 Forest|30A|4 +1 Forest|30A|5 +1 Forest|30A|6 + [tokens] b_1_1_skeleton b_5_5_demon_flying diff --git a/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt b/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt index d92a9df83bd..3996b420206 100644 --- a/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt +++ b/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt @@ -55,6 +55,15 @@ ScryfallCode=HBG 43 U Grave Choice @Will Gist 44 C Hook Horror @Olivier Bernard 45 R The Hourglass Coven @Konstantin Porubov +45a R Hag of Ceaseless Torment @Konstantin Porubov +45b R Hag of Dark Duress @Konstantin Porubov +45c R Hag of Death's Legion @Konstantin Porubov +45d R Hag of Inner Weakness @Konstantin Porubov +45e R Hag of Mage's Doom @Konstantin Porubov +45f R Hag of Noxious Nightmares @Konstantin Porubov +45g R Hag of Scoured Thoughts @Konstantin Porubov +45h R Hag of Syphoned Breath @Konstantin Porubov +45i R Hag of Twisted Visions @Konstantin Porubov 46 U Mind Spike @Matt Forsyth 47 C Sewer Plague @Piotr Foksowicz 48 R Stroke of Luck @Forrest Imel @@ -84,7 +93,7 @@ ScryfallCode=HBG 72 R Jon Irenicus, the Exile @Igor Grechanyi 73 U Liara of the Flaming Fist @David Rapoza 74 U Minthara of the Absolute @Evyn Fong -75 M Tasha, Unholy Archmage @Martina Fačková +75 M Tasha, Unholy Archmage @Martina Fackova 76 R Ulder Ravengard, Marshal @Eric Deschamps 77 U Gate of the Black Dragon @Sergey Glushakov 78 U Gate to Manorborn @Andreas Rocha @@ -202,8 +211,8 @@ ScryfallCode=HBG 190 U Swashbuckler Extraordinaire @Durion 191 U Two-Handed Axe @Milivoj Ćeran 192 C Unexpected Windfall @Alayna Danner -193 C Valor Singer @Justyna Dura -194 R Wrathful Red Dragon @Dan Murayama Scott +193 C Valor Singer @Justyna Gil +194 R Wrathful Red Dragon @Dan Scott 195 C You Come to the Gnoll Camp @Billy Christian 196 C You Find Some Prisoners @Lie Setiawan 197 C Young Red Dragon @Adam Vehige @@ -250,7 +259,7 @@ ScryfallCode=HBG 238 U Korlessa, Scale Singer @Jesper Ejsing 239 U Krydle of Baldur's Gate @Bryan Sola 240 U Lozhan, Dragons' Legacy @Rudy Siswanto -241 R Mazzy, Truesword Paladin @Justyna Dura +241 R Mazzy, Truesword Paladin @Justyna Gil 242 R Miirym, Sentinel Wyrm @Kekai Kotaki 243 M Minsc & Boo, Timeless Heroes @Andreas Zafiratos 244 M Nalia de'Arnise @John Stanko @@ -259,7 +268,7 @@ ScryfallCode=HBG 247 M Prosper, Tome-Bound @Yongjae Choi 248 R Raggadragga, Goreguts Boss @Xavier Ribeiro 249 R Raphael, Fiendish Savior @Livia Prima -250 U Thrakkus the Butcher @Néstor Ossandón Leal +250 U Thrakkus the Butcher @Nestor Ossandon Leal 251 U Trelasarra, Moon Dancer @Kieran Yanner 252 U Bag of Holding @Evyn Fong 253 R Basilisk Collar @Craig J Spearing @@ -328,15 +337,3 @@ A239 U A-Krydle of Baldur's Gate @Bryan Sola A243 M A-Minsc & Boo, Timeless Heroes @Andreas Zafiratos A259 C A-Lantern of Revealing @Eytan Zana A262 U A-Navigation Orb @Robin Olausson - -[conjured] -45a R Hag of Ceaseless Torment @Konstantin Porubov -45b R Hag of Dark Duress @Konstantin Porubov -45c R Hag of Death's Legion @Konstantin Porubov -45d R Hag of Inner Weakness @Konstantin Porubov -45e R Hag of Mage's Doom @Konstantin Porubov -45f R Hag of Noxious Nightmares @Konstantin Porubov -45g R Hag of Scoured Thoughts @Konstantin Porubov -45h R Hag of Syphoned Breath @Konstantin Porubov -45i R Hag of Twisted Visions @Konstantin Porubov - diff --git a/forge-gui/res/editions/Amonkhet.txt b/forge-gui/res/editions/Amonkhet.txt index 31111edf6a2..ac6c0248b3b 100644 --- a/forge-gui/res/editions/Amonkhet.txt +++ b/forge-gui/res/editions/Amonkhet.txt @@ -5,8 +5,8 @@ Name=Amonkhet Code2=AKH Type=Expansion BoosterCovers=5 -Booster=10 Common:fromSheet("AKH cards"), 3 Uncommon:fromSheet("AKH cards"), 1 RareMythic:fromSheet("AKH cards"), 1 BasicLand AKH -AdditionalSheetForFoils=fromSheet("MPS_AKH cards") +Booster=10 Common:!fromSheet("AKH Planeswalker Decks and Toolkit"), 3 Uncommon:!fromSheet("AKH Planeswalker Decks and Toolkit"), 1 RareMythic:!fromSheet("AKH Planeswalker Decks and Toolkit"), 1 BasicLand AKH +AdditionalSheetForFoils=fromSheet("MPS Amonkhet Invocations") FatPack=10 FatPackExtraSlots=80 BasicLands AdditionalSetUnlockedInQuest=MPS_AKH @@ -283,8 +283,6 @@ ScryfallCode=AKH 267 L Forest @Titus Lunter 268 L Forest @Titus Lunter 269 L Forest @Matt Stewart - -[precon product] 270 M Gideon, Martial Paragon @Daarken 271 U Companion of the Trials @Aaron Miller 272 R Gideon's Resolve @Jakub Kasper diff --git a/forge-gui/res/editions/Battle for Zendikar.txt b/forge-gui/res/editions/Battle for Zendikar.txt index 1c29f9c0636..7eb55e0f3e2 100644 --- a/forge-gui/res/editions/Battle for Zendikar.txt +++ b/forge-gui/res/editions/Battle for Zendikar.txt @@ -5,10 +5,10 @@ Code=BFZ Code2=BFZ Type=Expansion BoosterCovers=5 -Booster=10 Common:fromSheet("BFZ cards"), 3 Uncommon:fromSheet("BFZ cards"), 1 RareMythic:fromSheet("BFZ cards"), 1 BasicLand BFZ +Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand BFZ FatPack=9 FatPackExtraSlots=80 BasicLands -AdditionalSheetForFoils=fromSheet("EXP cards") +AdditionalSheetForFoils=fromSheet("EXP Lands") AdditionalSetUnlockedInQuest=EXP ScryfallCode=BFZ diff --git a/forge-gui/res/editions/Battlebond.txt b/forge-gui/res/editions/Battlebond.txt index e84bc8a2444..29b967e2163 100644 --- a/forge-gui/res/editions/Battlebond.txt +++ b/forge-gui/res/editions/Battlebond.txt @@ -4,7 +4,7 @@ Date=2018-06-08 Name=Battlebond Type=Draft BoosterCovers=3 -Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythicDistribution"), 1 BasicLand +Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythic"), 1 BasicLand ScryfallCode=BBD [cards] @@ -262,81 +262,9 @@ ScryfallCode=BBD 252 L Swamp @Titus Lunter 253 L Mountain @Titus Lunter 254 L Forest @Titus Lunter - -[etched] 255 M Will Kenrith @Anna Steinbauer 256 M Rowan Kenrith @Anna Steinbauer -[RareMythicDistribution] -1 Will Kenrith|BBD|1 -1 Rowan Kenrith|BBD|1 -8 Regna, the Redeemer -8 Krav, the Unredeemed -8 Zndrsplt, Eye of Wisdom -8 Okaun, Eye of Chaos -8 Virtus the Veiled -8 Gorm the Great -8 Khorvath Brightflame -8 Sylvia Brightspear -8 Pir, Imaginative Rascal -8 Toothy, Imaginary Friend -1 Arena Rector -1 Brightling -8 Play of the Game -8 Regna's Sanction -8 Together Forever -1 Arcane Artisan -8 Game Plan -8 Spellseeker -8 Zndrsplt's Judgment -1 Archfiend of Despair -8 Mindblade Render -1 Stunning Reversal -8 Thrilling Encore -8 Virtus's Maneuver -8 Bonus Round -8 Khorvath's Fury -1 Najeela, the Blade-Blossom -8 Stolen Strategy -1 Bramble Sovereign -8 Generous Patron -1 Grothama, All-Devouring -8 Pir's Whim -8 Archon of Valor's Reach -8 Last One Standing -8 Sentinel Tower -8 Victory Chimes -8 Bountiful Promenade -8 Luxury Suite -8 Morphic Pool -8 Sea of Clouds -8 Spire Garden -8 Angelic Chorus -8 Kor Spiritdancer -1 Land Tax -8 Mangara of Corondor -8 Mystic Confluence -8 Sower of Temptation -8 Tidespout Tyrant -1 True-Name Nemesis -8 Diabolic Intent -1 Nirkana Revenant -8 Noosegraf Mob -8 Nyxathid -8 Goblin Razerunners -8 Magmatic Force -8 War's Toll -1 Doubling Season -8 Greater Good -8 Magus of the Candelabra -8 Seedborn Muse -8 Vigor -8 Apocalypse Hydra -8 Evil Twin -8 Gwafa Hazid, Profiteer -8 Mind's Eye -1 Mycosynth Lattice - [tokens] w_1_1_spirit_flying w_1_1_warrior diff --git a/forge-gui/res/editions/Bloomburrow.txt b/forge-gui/res/editions/Bloomburrow.txt index 832a98d1bf0..909acbe75d2 100644 --- a/forge-gui/res/editions/Bloomburrow.txt +++ b/forge-gui/res/editions/Bloomburrow.txt @@ -9,7 +9,6 @@ Booster=6 Common, 1 Common-Guest, 3 Uncommon, 1 RareMythic, 1 FullArtLand, 1 Wil Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 -#Numbers from https://mtgscribe.com/2024/07/14/play-booster-fact-sheet-bloomburrow/ [Common] Base=Common:fromSheet("BLB cards") @@ -22,6 +21,9 @@ Base=Uncommon:fromSheet("BLB cards") [RareMythic] Base=RareMythic:fromSheet("BLB cards") +# I don't know about these numbers, so i'm just copying them from MH3 +Replace=.021F fromSheet("BLB borderless") +Replace=.051F fromSheet("BLB showcase") [FullArtLand] Base=Land:fromSheet("BLB full art") @@ -31,7 +33,9 @@ Replace=.20F Land:fromSheet("BLB full art")+ Base=Common:fromSheet("BLB cards") # I don't know about these numbers, so i'm just copying them from MH3 Replace=.417F Uncommon:fromSheet("BLB cards") -Replace=.012F RareMythic:fromSheet("BLB cards") +Replace=.078F RareMythic:fromSheet("BLB cards") +Replace=.004F fromSheet("BLB borderless") +Replace=.042F fromSheet("BLB showcase") [cards] 1 C Banishing Light @Zoltan Boros @@ -315,6 +319,16 @@ Replace=.012F RareMythic:fromSheet("BLB cards") 279 L Forest @David Robert Hovey 280 L Forest @David Robert Hovey 281 L Forest @David Robert Hovey +369 L Plains @Piotr Dura +370 L Plains @Julian Kok Joon Wen +371 L Island @Alexander Forssberg +372 L Island @Lorenzo Lanfranconi +373 L Swamp @Piotr Dura +374 L Swamp @Thomas Stoop +375 L Mountain @Samuele Bandini +376 L Mountain @Adam Paquette +377 L Forest @Alayna Danner +378 L Forest @Donato Giancola [borderless] 282 M Season of the Burrow @Edgar Sánchez Hidalgo @@ -410,16 +424,6 @@ Replace=.012F RareMythic:fromSheet("BLB cards") 368 R Fountainport @Leon Tukker [precon product] -369 L Plains @Piotr Dura -370 L Plains @Julian Kok Joon Wen -371 L Island @Alexander Forssberg -372 L Island @Lorenzo Lanfranconi -373 L Swamp @Piotr Dura -374 L Swamp @Thomas Stoop -375 L Mountain @Samuele Bandini -376 L Mountain @Adam Paquette -377 L Forest @Alayna Danner -378 L Forest @Donato Giancola 379 M Bria, Riptide Rogue @Borja Pindado 380 M Byrke, Long Ear of the Law @Manuel Castañón 387 R Serra Redeemer @Joshua Raphael @@ -440,6 +444,8 @@ Replace=.012F RareMythic:fromSheet("BLB cards") 383 U Fell @A. M. Sartor 384 U Wear Down @Iris Compiet 385 U Stormcatch Mentor @Manuel Castañón + +[bundle] 386 R Thundertrap Trainer @Jesper Ejsing [rebalanced] diff --git a/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt b/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt index d958b138739..e434eb1635e 100644 --- a/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt +++ b/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt @@ -370,6 +370,26 @@ ScryfallCode=CLB 359 C Sea Gate @Kamila Szutenberg 360 R Sea of Clouds @Matt Gaser 361 R Spire Garden @Alexander Forssberg +451 L Plains @Bruce Brenneise +452 L Plains @Leanna Crossan +453 L Plains @Titus Lunter +454 L Plains @Emmanuel Shiu +455 L Island @Bruce Brenneise +456 L Island @Piotr Dura +457 L Island @James Paick +458 L Island @Sam White +459 L Swamp @Piotr Dura +460 L Swamp @Logan Feliciano +461 L Swamp @Grady Frederick +462 L Swamp @Sam White +463 L Mountain @Matt Gaser +464 L Mountain @Lucas Graciano +465 L Mountain @Muhammad Firdaus +466 L Mountain @Sam White +467 L Forest @Bruce Brenneise +468 L Forest @Muhammad Firdaus +469 L Forest @Lucas Graciano +470 L Forest @Julian Kok Joon Wen [borderless] 362 M Elminster @Tyler Jacobson @@ -463,8 +483,6 @@ ScryfallCode=CLB 448 C Moss Diamond @Phil Stone 449 C Sky Diamond @Phil Stone 450 U Stonespeaker Crystal @Mark A. Nelson - -[etched] 471 U Abdel Adrian, Gorion's Ward @Karl Kopinski 472 U Ellyn Harbreeze, Busybody @Dan Murayama Scott 473 U Far Traveler @Alix Branwyn @@ -648,26 +666,6 @@ ScryfallCode=CLB 645 R Sarevok's Tome @Titus Lunter [precon product] -451 L Plains @Bruce Brenneise -452 L Plains @Leanna Crossan -453 L Plains @Titus Lunter -454 L Plains @Emmanuel Shiu -455 L Island @Bruce Brenneise -456 L Island @Piotr Dura -457 L Island @James Paick -458 L Island @Sam White -459 L Swamp @Piotr Dura -460 L Swamp @Logan Feliciano -461 L Swamp @Grady Frederick -462 L Swamp @Sam White -463 L Mountain @Matt Gaser -464 L Mountain @Lucas Graciano -465 L Mountain @Muhammad Firdaus -466 L Mountain @Sam White -467 L Forest @Bruce Brenneise -468 L Forest @Muhammad Firdaus -469 L Forest @Lucas Graciano -470 L Forest @Julian Kok Joon Wen 646 M Captain N'ghathrod @Andrey Kuzinskiy 647 M Faldorn, Dread Wolf Herald @Jason A. Engle 648 M Firkraag, Cunning Instigator @Andrew Mar diff --git a/forge-gui/res/editions/Commander Legends.txt b/forge-gui/res/editions/Commander Legends.txt index e3a3b1e6dda..6afbdf1d5d3 100644 --- a/forge-gui/res/editions/Commander Legends.txt +++ b/forge-gui/res/editions/Commander Legends.txt @@ -527,7 +527,7 @@ ScryfallCode=CMR 512 M Tevesh Szat, Doom of Fools @Chris Rallis 513 M Jeska, Thrice Reborn @Chris Rallis -[etched] +[showcase] 514 M Najeela, the Blade-Blossom @Matt Stewart 515 M Akiri, Line-Slinger @David Gaillet 516 M Brago, King Eternal @Karla Ortiz diff --git a/forge-gui/res/editions/Conspiracy Take the Crown.txt b/forge-gui/res/editions/Conspiracy Take the Crown.txt index d856802edc9..1f89547d7a3 100644 --- a/forge-gui/res/editions/Conspiracy Take the Crown.txt +++ b/forge-gui/res/editions/Conspiracy Take the Crown.txt @@ -5,7 +5,7 @@ Name=Conspiracy: Take the Crown Code2=CN2 Type=Draft BoosterCovers=3 -Booster=10 Common:!fromSheet("CN2 Draft Matters"), 3 Uncommon:!fromSheet("CN2 Draft Matters"), 1 RareMythic:!fromSheet("CN2 Draft Matters"):fromSheet("CN2 cards"), 1 fromSheet("CN2 Draft Matters") +Booster=10 Common:!fromSheet("CN2 Draft Matters"), 3 Uncommon:!fromSheet("CN2 Draft Matters"), 1 RareMythic:!fromSheet("CN2 Draft Matters"), 1 fromSheet("CN2 Draft Matters") AdditionalSheetForFoils=fromSheet("CN2 Foil Kaya") ScryfallCode=CN2 @@ -162,7 +162,7 @@ ScryfallCode=CN2 150 C Unnerve @Terese Nielsen 151 U Burn Away @Vincent Proce 152 R Burning Wish @Scott M. Fischer -153 R Charmbreaker Devils @Dan Murayama Scott +153 R Charmbreaker Devils @Dan Scott 154 U Coordinated Assault @John Severin Brassell 155 C Ember Beast @David Rapoza 156 C Fiery Fall @Daarken diff --git a/forge-gui/res/editions/Dominaria Remastered.txt b/forge-gui/res/editions/Dominaria Remastered.txt index eafea138da6..df2d5c51c45 100644 --- a/forge-gui/res/editions/Dominaria Remastered.txt +++ b/forge-gui/res/editions/Dominaria Remastered.txt @@ -5,7 +5,7 @@ Name=Dominaria Remastered Code2=DMR Type=Reprint BoosterCovers=3 -Booster=9 Common:fromsheet("DMR cards"), 3 Uncommon:fromSheet("DMR cards"), 1 RareMythic:fromSheet("DMR cards"), 1 BasicLand:fromSheet("DMR retro frame"), 1 !BasicLand:fromSheet("DMR retro frame") +Booster=9 Common:fromsheet("DMR cards"), 3 Uncommon:fromSheet("DMR cards"), 1 RareMythic:fromSheet("DMR cards"), 1 BasicLand:fromSheet("DMR showcase"), 1 Any:fromSheet("DMR oldframes") BoosterMustContain=Legendary Creature Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 @@ -274,7 +274,7 @@ ScryfallCode=DMR 260 U Treva's Ruins @Jerry Tiritilli 261 R Woodland Cemetery @Christine Choi -[retro frame] +[showcase] 262 R Divine Sacrament @Ekaterina Burmak 263 R Enlightened Tutor @Howard Lyon 264 R Glory @Donato Giancola @@ -476,6 +476,148 @@ ScryfallCode=DMR [promo] 457 R Counterspell @Mark Poole +[oldframes] +1 Divine Sacrament|DMR|2 +1 Enlightened Tutor|DMR|2 +1 Glory|DMR|2 +1 Lieutenant Kirtar|DMR|2 +1 Lyra Dawnbringer|DMR|2 +1 Mesa Enchantress|DMR|2 +1 Momentary Blink|DMR|2 +1 Renewed Faith|DMR|2 +1 Savannah Lions|DMR|2 +1 Serra Angel|DMR|2 +1 Serra Avatar|DMR|2 +1 Sevinne's Reclamation|DMR|2 +1 Spirit Link|DMR|2 +1 Swords to Plowshares|DMR|2 +1 Test of Endurance|DMR|2 +1 Whitemane Lion|DMR|2 +1 Windborn Muse|DMR|2 +1 Wrath of God|DMR|2 +1 Arcanis the Omnipotent|DMR|2 +1 Counterspell|DMR|2 +1 Denizen of the Deep|DMR|2 +1 Fact or Fiction|DMR|2 +1 Force of Will|DMR|2 +1 Frantic Search|DMR|2 +1 High Tide|DMR|2 +1 Impulse|DMR|2 +1 Mystic Remora|DMR|2 +1 Mystical Tutor|DMR|2 +1 Opposition|DMR|2 +1 Ovinize|DMR|2 +1 Peregrine Drake|DMR|2 +1 Stroke of Genius|DMR|2 +1 Time Stretch|DMR|2 +1 Turnabout|DMR|2 +1 Urza, Lord High Artificer|DMR|2 +1 Vexing Sphinx|DMR|2 +1 Body Snatcher|DMR|2 +1 Chainer, Dementia Master|DMR|2 +1 Chainer's Edict|DMR|2 +1 Dark Withering|DMR|2 +1 Dread Return|DMR|2 +1 Duress|DMR|2 +1 Entomb|DMR|2 +1 Mindslicer|DMR|2 +1 Nantuko Shade|DMR|2 +1 Necrosavant|DMR|2 +1 No Mercy|DMR|2 +1 Oversold Cemetery|DMR|2 +1 Royal Assassin|DMR|2 +1 Street Wraith|DMR|2 +1 Terror|DMR|2 +1 Undead Gladiator|DMR|2 +1 Vampiric Tutor|DMR|2 +1 Yawgmoth, Thran Physician|DMR|2 +1 Chain Lightning|DMR|2 +1 Dragon Whelp|DMR|2 +1 Empty the Warrens|DMR|2 +1 Fireblast|DMR|2 +1 Flametongue Kavu|DMR|2 +1 Gamble|DMR|2 +1 Gempalm Incinerator|DMR|2 +1 Goblin Matron|DMR|2 +1 Grim Lavamancer|DMR|2 +1 Last Chance|DMR|2 +1 Mogg War Marshal|DMR|2 +1 Overmaster|DMR|2 +1 Pashalik Mons|DMR|2 +1 Shivan Dragon|DMR|2 +1 Siege-Gang Commander|DMR|2 +1 Sneak Attack|DMR|2 +1 Sulfuric Vortex|DMR|2 +1 Valduk, Keeper of the Flame|DMR|2 +1 Worldgorger Dragon|DMR|2 +1 Arboria|DMR|2 +1 Birds of Paradise|DMR|2 +1 Deadwood Treefolk|DMR|2 +1 Elvish Spirit Guide|DMR|2 +1 Exploration|DMR|2 +1 Fa'adiyah Seer|DMR|2 +1 Forgotten Ancient|DMR|2 +1 Invigorating Boon|DMR|2 +1 Jolrael, Mwonvuli Recluse|DMR|2 +1 Kamahl, Fist of Krosa|DMR|2 +1 Lull|DMR|2 +1 Nature's Lore|DMR|2 +1 Nut Collector|DMR|2 +1 Saproling Symbiosis|DMR|2 +1 Squirrel Nest|DMR|2 +1 Sylvan Library|DMR|2 +1 Wild Dogs|DMR|2 +1 Wild Growth|DMR|2 +1 Worldly Tutor|DMR|2 +1 Absorb|DMR|2 +1 Arcades Sabboth|DMR|2 +1 Decimate|DMR|2 +1 Dralnu's Crusade|DMR|2 +1 Gerrard's Verdict|DMR|2 +1 Hunting Grounds|DMR|2 +1 Mystic Enforcer|DMR|2 +1 Phantom Nishoba|DMR|2 +1 Pyre Zombie|DMR|2 +1 Quicksilver Dagger|DMR|2 +1 Radha, Heir to Keld|DMR|2 +1 Recoil|DMR|2 +1 Rith, the Awakener|DMR|2 +1 Sawtooth Loon|DMR|2 +1 Sol'kanar the Swamp King|DMR|2 +1 Spinal Embrace|DMR|2 +1 Spiritmonger|DMR|2 +1 Tatyova, Benthic Druid|DMR|2 +1 Tiana, Ship's Caretaker|DMR|2 +1 Xira Arien|DMR|2 +1 Zur the Enchanter|DMR|2 +1 Crawlspace|DMR|2 +1 Cryptic Gateway|DMR|2 +1 Damping Sphere|DMR|2 +1 Gauntlet of Power|DMR|2 +1 Helm of Awakening|DMR|2 +1 Icy Manipulator|DMR|2 +1 Jester's Cap|DMR|2 +1 Juggernaut|DMR|2 +1 Legacy Weapon|DMR|2 +1 Lotus Blossom|DMR|2 +1 Mind Stone|DMR|2 +1 Ornithopter|DMR|2 +1 Thran Golem|DMR|2 +1 Tormod's Crypt|DMR|2 +1 Triskelion|DMR|2 +1 Umbilicus|DMR|2 +1 Urza's Blueprints|DMR|2 +1 Urza's Incubator|DMR|2 +1 Clifftop Retreat|DMR|2 +1 Dark Depths|DMR|2 +1 Gemstone Mine|DMR|2 +1 Hinterland Harbor|DMR|2 +1 Isolated Chapel|DMR|2 +1 Maze of Ith|DMR|2 +1 Mishra's Factory|DMR|2 +1 Sulfur Falls|DMR|2 +1 Woodland Cemetery|DMR|2 + [tokens] b_2_1_cat b_2_2_zombie diff --git a/forge-gui/res/editions/Dominaria United.txt b/forge-gui/res/editions/Dominaria United.txt index 3821424cb9f..76abb6c4c9e 100644 --- a/forge-gui/res/editions/Dominaria United.txt +++ b/forge-gui/res/editions/Dominaria United.txt @@ -300,6 +300,11 @@ ScryfallCode=DMU 284 R Tyrannical Pitlord @Donato Giancola 285 R Ragefire Hellkite @Xavier Ribeiro 286 R Briar Hydra @Caio Monteiro +423 R Serra Redeemer @Joshua Raphael +424 R Cosmic Epiphany @Eli Minaya +425 R Tyrannical Pitlord @Donato Giancola +426 R Ragefire Hellkite @Xavier Ribeiro +427 R Briar Hydra @Caio Monteiro [showcase] 287 R Danitha, Benalia's Hope @Barbara Rosiak @@ -442,11 +447,6 @@ ScryfallCode=DMU 420 R Urborg Lhurgoyf @Andrey Kuzinskiy 421 R Plaza of Heroes @Gabor Szikszai 422 R Thran Portal @Sarah Finnigan -423 R Serra Redeemer @Joshua Raphael -424 R Cosmic Epiphany @Eli Minaya -425 R Tyrannical Pitlord @Donato Giancola -426 R Ragefire Hellkite @Xavier Ribeiro -427 R Briar Hydra @Caio Monteiro [buy a box] 428 R Llanowar Loamspeaker @Volkan Baǵa diff --git a/forge-gui/res/editions/Dominaria.txt b/forge-gui/res/editions/Dominaria.txt index 89efc848197..4bb4d649760 100644 --- a/forge-gui/res/editions/Dominaria.txt +++ b/forge-gui/res/editions/Dominaria.txt @@ -4,7 +4,7 @@ Date=2018-04-27 Name=Dominaria Type=Expansion BoosterCovers=5 -Booster=10 Common:fromSheet("DOM cards"), 3 Uncommon:fromSheet("DOM cards"), 1 RareMythic:fromSheet("DOM cards"), 1 BasicLand DOM +Booster=10 Common:!fromSheet("DOM Planeswalker Decks and Additional Promo"), 3 Uncommon:!fromSheet("DOM Planeswalker Decks and Additional Promo"), 1 RareMythic:!fromSheet("DOM Planeswalker Decks and Additional Promo"), 1 BasicLand DOM BoosterMustContain=Legendary Creature FatPack=10 FatPackExtraSlots=80 BasicLands @@ -280,8 +280,6 @@ ScryfallCode=DOM 267 L Forest @Titus Lunter 268 L Forest @Dimitar Marinski 269 L Forest @Mark Poole - -[precon product] 270 M Teferi, Timebender @Zack Stella 271 C Temporal Machinations @Zack Stella 272 R Niambi, Faithful Healer @Greg Opalinski @@ -292,8 +290,6 @@ ScryfallCode=DOM 277 U Karplusan Hound @Viktor Titov 278 C Pyromantic Pilgrim @Magali Villeneuve 279 C Timber Gorge @YW Tang - -[buy a box] 280 R Firesong and Sunspeaker @Zoltan Boros [tokens] diff --git a/forge-gui/res/editions/Double Masters 2022.txt b/forge-gui/res/editions/Double Masters 2022.txt index f9e6baf725c..0652342c305 100644 --- a/forge-gui/res/editions/Double Masters 2022.txt +++ b/forge-gui/res/editions/Double Masters 2022.txt @@ -426,7 +426,7 @@ ScryfallCode=2X2 411 U Selesnya Sanctuary @Ron Spears 412 U Simic Growth Chamber @Pete Venters -[etched] +[showcase] 413 M Emrakul, the Aeons Torn @Mark Tedin 414 M Kozilek, Butcher of Truth @Michael Komarck 415 M Ulamog, the Infinite Gyre @Aleksi Briclot diff --git a/forge-gui/res/editions/Double Masters.txt b/forge-gui/res/editions/Double Masters.txt index 5345abc2b90..f2eacc39340 100644 --- a/forge-gui/res/editions/Double Masters.txt +++ b/forge-gui/res/editions/Double Masters.txt @@ -4,7 +4,7 @@ Date=2020-08-07 Name=Double Masters Type=Reprint BoosterCovers=3 -Booster=8 Common:fromsheet("2XM cards"), 3 Uncommon:fromsheet("2XM cards"), 2 RareMythic:fromsheet("2XM cards"), 2 fromSheet("2XM Foils") +Booster=8 Common, 3 Uncommon, 2 RareMythic, 2 fromSheet("2XM Foils") Foil=NotSupported DoublePick=FirstPick ChaosDraftThemes=MASTERS_SET @@ -343,8 +343,6 @@ ScryfallCode=2XM 330 C Urza's Power Plant @Brian Snõddy 331 C Urza's Tower @Brian Snõddy 332 R Wooded Bastion @Christopher Moeller - -[borderless] 333 M Karn Liberated @Mark Tedin 334 M Jace, the Mind Sculptor @Jason Chan 335 M Avacyn, Angel of Hope @Randy Vargas @@ -385,8 +383,6 @@ ScryfallCode=2XM 370 R Urza's Mine @Mark Tedin 371 R Urza's Power Plant @Mark Tedin 372 R Urza's Tower @Mark Tedin - -[buy a box] 373 L Plains @John Avon 374 L Plains @Noah Bradley 375 L Island @John Avon diff --git a/forge-gui/res/editions/Guilds of Ravnica.txt b/forge-gui/res/editions/Guilds of Ravnica.txt index 22b2a69218f..a3475d24e0a 100644 --- a/forge-gui/res/editions/Guilds of Ravnica.txt +++ b/forge-gui/res/editions/Guilds of Ravnica.txt @@ -5,7 +5,7 @@ Name=Guilds of Ravnica Code2=GRN Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("GRN lands"):fromSheet("GRN cards"), 3 Uncommon:fromSheet("GRN cards"), 1 RareMythic:fromSheet("GRN cards"), 1 fromSheet("GRN lands") +Booster=10 Common:!fromSheet("GRN Secret Cards"), 3 Uncommon:!fromSheet("GRN Secret Cards"), 1 RareMythic:!fromSheet("GRN Secret Cards"), 1 fromSheet("GRN Lands") AdditionalSetUnlockedInQuest=GK1 FatPack=10 FatPackExtraSlots=80 BasicLands @@ -272,8 +272,6 @@ ScryfallCode=GRN 257 R Steam Vents @Jonas De Ro 258 R Temple Garden @Titus Lunter 259 R Watery Grave @Cliff Childs - -[precon product] 260 L Plains @Richard Wright 261 L Island @Richard Wright 262 L Swamp @Richard Wright @@ -287,22 +285,8 @@ ScryfallCode=GRN 270 C Kraul Raider @Ben Wootten 271 U Attendant of Vraska @Magali Villeneuve 272 R Vraska's Stoneglare @Yongjae Choi - -[buy a box] 273 M Impervious Greatwurm @Simon Dominic -[lands] -Golgari Guildgate|GRN|1 -Golgari Guildgate|GRN|2 -Izzet Guildgate|GRN|1 -Izzet Guildgate|GRN|2 -Selesnya Guildgate|GRN|1 -Selesnya Guildgate|GRN|2 -Dimir Guildgate|GRN|1 -Dimir Guildgate|GRN|2 -Boros Guildgate|GRN|1 -Boros Guildgate|GRN|2 - [tokens] w_4_4_angel_flying_vigilance w_1_1_soldier_lifelink diff --git a/forge-gui/res/editions/Hour of Devastation.txt b/forge-gui/res/editions/Hour of Devastation.txt index ddc95416c2c..c48e663c645 100644 --- a/forge-gui/res/editions/Hour of Devastation.txt +++ b/forge-gui/res/editions/Hour of Devastation.txt @@ -5,8 +5,8 @@ Name=Hour of Devastation Code2=HOU Type=Expansion BoosterCovers=5 -Booster=10 Common:fromSheet("HOU cards"), 3 Uncommon:fromSheet("HOU cards"), 1 RareMythic:fromSheet("HOU cards"), 1 BasicLand -AdditionalSheetForFoils=fromSheet("MPS_AKH special slot") +Booster=10 Common:!fromSheet("HOU Planeswalker Decks and Toolkit"), 3 Uncommon:!fromSheet("HOU Planeswalker Decks and Toolkit"), 1 RareMythic:!fromSheet("HOU Planeswalker Decks and Toolkit"), 1 BasicLand +AdditionalSheetForFoils=fromSheet("MPS Hour of Devastation Invocations") FatPack=10 FatPackExtraSlots=80 BasicLands AdditionalSetUnlockedInQuest=MPS_AKH @@ -214,8 +214,6 @@ ScryfallCode=HOU 197 L Mountain @Kev Walker 198 L Forest @Titus Lunter 199 L Forest @Mark Poole - -[precon product] 200 M Nissa, Genesis Mage @Chris Rallis 201 U Avid Reclaimer @Josu Hernaiz 202 C Brambleweft Behemoth @Florian de Gesincourt diff --git a/forge-gui/res/editions/Innistrad Crimson Vow.txt b/forge-gui/res/editions/Innistrad Crimson Vow.txt index 98ed149c709..37ef374a9d7 100644 --- a/forge-gui/res/editions/Innistrad Crimson Vow.txt +++ b/forge-gui/res/editions/Innistrad Crimson Vow.txt @@ -412,8 +412,6 @@ Prerelease=6 Boosters, 1 RareMythic+ 395 R Dollhouse of Horrors @Muhammad Firdaus 396 R Investigator's Journal @Yeong-Hao Han 397 R Voldaren Estate @Richard Wright - -[bundle] 398 L Plains @Sam White 399 L Island @Sam White 400 L Swamp @Sam White diff --git a/forge-gui/res/editions/Innistrad Midnight Hunt.txt b/forge-gui/res/editions/Innistrad Midnight Hunt.txt index 1b191b4a281..691a18fa7e9 100644 --- a/forge-gui/res/editions/Innistrad Midnight Hunt.txt +++ b/forge-gui/res/editions/Innistrad Midnight Hunt.txt @@ -66,6 +66,7 @@ Prerelease=6 Boosters, 1 RareMythic+ 55 C Galedrifter @Daniel Ljunggren 56 C Geistwave @Olena Richards 57 R Grafted Identity @Manuel Castañón +57† R Grafted Identity @Manuel Castañón 58 C Larder Zombie @E. M. Gist 59 M Lier, Disciple of the Drowned @Ekaterina Burmak 60 C Locked in the Cemetery @Tran Nguyen @@ -287,16 +288,6 @@ Prerelease=6 Boosters, 1 RareMythic+ 276 L Forest @Alayna Danner 277 L Forest @Dan Mumford -[alternate art] -57† R Grafted Identity @Manuel Castañón - -[precon product] -380 L Plains @Andreas Rocha -381 L Island @Andreas Rocha -382 L Swamp @Kasia 'Kafis' Zielińska -383 L Mountain @Muhammad Firdaus -384 L Forest @Andreas Rocha - [borderless] 278 M Wrenn and Seven @Bram Sels 279 M Arlinn, the Pack's Hope @Eric Deschamps @@ -404,6 +395,11 @@ Prerelease=6 Boosters, 1 RareMythic+ 377 R The Celestus @Jonas De Ro 378 R Pithing Needle @Ovidio Cartagena 379 M Hostile Hostel @Daniel Ljunggren +380 L Plains @Andreas Rocha +381 L Island @Andreas Rocha +382 L Swamp @Kasia 'Kafis' Zielińska +383 L Mountain @Muhammad Firdaus +384 L Forest @Andreas Rocha [buy a box] 385 R Champion of the Perished @Daarken diff --git a/forge-gui/res/editions/Kamigawa Neon Dynasty.txt b/forge-gui/res/editions/Kamigawa Neon Dynasty.txt index f6a58630f71..df6c9b3aa5e 100644 --- a/forge-gui/res/editions/Kamigawa Neon Dynasty.txt +++ b/forge-gui/res/editions/Kamigawa Neon Dynasty.txt @@ -431,8 +431,6 @@ ScryfallCode=NEO 403 R Mirror Box @Mid 404 R Reckoner Bankbuster @Yoshiya 405 R Surgehacker Mech @Inuchiyo Meimaru - -[etched] 417 R Farewell @Fuzichoco 418 M The Wandering Emperor @Hisashi Momose 419 M Tezzeret, Betrayer of Flesh @Maekawa Yuichi diff --git a/forge-gui/res/editions/Khans of Tarkir.txt b/forge-gui/res/editions/Khans of Tarkir.txt index c9ef690ab6f..b3c441c332f 100644 --- a/forge-gui/res/editions/Khans of Tarkir.txt +++ b/forge-gui/res/editions/Khans of Tarkir.txt @@ -5,7 +5,7 @@ Name=Khans of Tarkir Code2=KTK Type=Expansion BoosterCovers=5 -Booster=10 Common:fromsheet("KTK cards"), 3 Uncommon:fromsheet("KTK cards"), 1 RareMythic:fromsheet("KTK cards"), 1 BasicLand KTK +Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand KTK FatPack=9 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=GRAVEYARD_MATTERS diff --git a/forge-gui/res/editions/Magic 2015.txt b/forge-gui/res/editions/Magic 2015.txt index d763442489e..ab0dd41160a 100644 --- a/forge-gui/res/editions/Magic 2015.txt +++ b/forge-gui/res/editions/Magic 2015.txt @@ -5,7 +5,7 @@ Name=Magic 2015 Code2=M15 Type=Core BoosterCovers=5 -Booster=10 Common:fromSheet("M15 cards"), 3 Uncommon:fromSheet("M15 cards"), 1 RareMythic:fromSheet("M15 cards"), 1 BasicLand +Booster=10 Common:!fromSheet("M15 Sample Cards"), 3 Uncommon:!fromSheet("M15 Sample Cards"), 1 RareMythic:!fromSheet("M15 Sample Cards"), 1 BasicLand FatPack=9 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=CORE_SET @@ -281,8 +281,6 @@ ScryfallCode=M15 267 L Forest @Steven Belledin 268 L Forest @Noah Bradley 269 L Forest @Jonas De Ro - -[precon product] 270 R Aegis Angel @Aleksi Briclot 271 C Divine Verdict @Kev Walker 272 C Inspired Charge @Wayne Reynolds diff --git a/forge-gui/res/editions/Magic 2021.txt b/forge-gui/res/editions/Magic 2021.txt index 38e70df02ea..1c17ce37397 100644 --- a/forge-gui/res/editions/Magic 2021.txt +++ b/forge-gui/res/editions/Magic 2021.txt @@ -302,12 +302,6 @@ ScryfallCode=M21 282 M Liliana, Waker of the Dead @Magali Villeneuve 283 M Chandra, Heart of Fire @Jason Rainville 284 M Garruk, Unleashed @Cristi Balanescu -314 R Containment Priest @Jesper Ejsing -315 M Grim Tutor @Antonio José Manzanedo -316 M Massacre Wurm @Kekai Kotaki -317 R Cultivate @Billy Christian -318 R Scavenging Ooze @Sam Rowan -319 R Solemn Simulacrum @Joseph Meehan [showcase] 285 M Ugin, the Spirit Dragon @Raymond Swanland @@ -339,6 +333,12 @@ ScryfallCode=M21 311 L Swamp @Jonas De Ro 312 L Mountain @Jonas De Ro 313 L Forest @Jonas De Ro +314 R Containment Priest @Jesper Ejsing +315 M Grim Tutor @Antonio José Manzanedo +316 M Massacre Wurm @Kekai Kotaki +317 R Cultivate @Billy Christian +318 R Scavenging Ooze @Sam Rowan +319 R Solemn Simulacrum @Joseph Meehan [precon product] 320 M Basri, Devoted Paladin @Jason Rainville diff --git a/forge-gui/res/editions/Magic Origins.txt b/forge-gui/res/editions/Magic Origins.txt index 58a9879c2b3..305820c21e6 100644 --- a/forge-gui/res/editions/Magic Origins.txt +++ b/forge-gui/res/editions/Magic Origins.txt @@ -5,7 +5,7 @@ Name=Magic Origins Code2=ORI Type=Core BoosterCovers=5 -Booster=10 Common:fromSheet("ORI cards"), 3 Uncommon:fromSheet("ORI cards"), 1 RareMythic:fromSheet("ORI cards"), 1 BasicLand +Booster=10 Common:!fromSheet("ORI Sample Cards"), 3 Uncommon:!fromSheet("ORI Sample Cards"), 1 RareMythic:!fromSheet("ORI Sample Cards"), 1 BasicLand FatPack=9 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=CORE_SET @@ -284,8 +284,6 @@ ScryfallCode=ORI 270 L Forest @Jonas De Ro 271 L Forest @Jonas De Ro 272 L Forest @Vincent Proce - -[precon product] 273 R Aegis Angel @Aleksi Briclot 274 C Divine Verdict @Kev Walker 275 C Eagle of the Watch @Scott Murphy diff --git a/forge-gui/res/editions/March of the Machine The Aftermath.txt b/forge-gui/res/editions/March of the Machine The Aftermath.txt index 7a71997bf4d..d3e5ca37c72 100644 --- a/forge-gui/res/editions/March of the Machine The Aftermath.txt +++ b/forge-gui/res/editions/March of the Machine The Aftermath.txt @@ -57,8 +57,6 @@ BoosterBox=0 48 M Tyvar the Bellicose @Jarel Threat 49 M Karn, Legacy Reforged @Grzegorz Rutkowski 50 R Drannith Ruins @Martin de Diego Sádaba - -[showcase] 51 U Coppercoat Vanguard @Steve Ellis 52 R Deification @Jason A. Engle 53 U Harnessed Snubhorn @Jody Clark @@ -109,8 +107,6 @@ BoosterBox=0 98 M Tyvar the Bellicose @Richard Luong 99 M Karn, Legacy Reforged @Daren Bader 100 R Drannith Ruins @Steve Ellis - -[etched] 101 U Coppercoat Vanguard @Bruno Biazotto 102 R Deification @Maxime Minard 103 U Harnessed Snubhorn @Quintin Gleim @@ -161,8 +157,6 @@ BoosterBox=0 148 M Tyvar the Bellicose @Jarel Threat 149 M Karn, Legacy Reforged @Grzegorz Rutkowski 150 R Drannith Ruins @Martin de Diego Sádaba - -[extended art] 151 R Deification @Maxime Minard 152 R Metropolis Reformer @Ryan Pancoast 153 R Spark Rupture @Viko Menezes @@ -198,8 +192,6 @@ BoosterBox=0 183 M Tyvar the Bellicose @Jarel Threat 184 M Karn, Legacy Reforged @Grzegorz Rutkowski 185 R Drannith Ruins @Martin de Diego Sádaba - -[etched] 186 U Coppercoat Vanguard @Steve Ellis 187 R Deification @Jason A. Engle 188 U Harnessed Snubhorn @Jody Clark @@ -243,9 +235,5 @@ BoosterBox=0 226 R Sigarda, Font of Blessings @Sami Makkonen 227 M Tyvar the Bellicose @Richard Luong 228 R Drannith Ruins @Steve Ellis - -[promo] 229 R Spark Rupture @Scott M. Fischer - -[buy a box] 230 R Jolrael, Voice of Zhalfir @Ernanda Souza diff --git a/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt b/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt index 9d87ce5010d..d5f5f7ba2b0 100644 --- a/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt +++ b/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt @@ -5,7 +5,6 @@ Name=Masterpiece Series - Amonkhet Type=Collector_Edition ScryfallCode=mp2 -#Bonus cards for AKH Amonkhet [cards] 1 S Austere Command @Richard Wright 2 S Aven Mindcensor @Jose Cabrera @@ -37,9 +36,6 @@ ScryfallCode=mp2 28 S Rhonas the Indomitable @Jack Wang 29 S Maelstrom Pulse @Igor Kieryluk 30 S Vindicate @Igor Kieryluk - -#Bonus cards for HOU Hour of Devastation -[special slot] 31 S Armageddon @Florian de Gesincourt 32 S Capsize @Cliff Childs 33 S Forbid @Richard Wright diff --git a/forge-gui/res/editions/Masters Edition IV.txt b/forge-gui/res/editions/Masters Edition IV.txt index 344ae3b5436..354f35f6f65 100644 --- a/forge-gui/res/editions/Masters Edition IV.txt +++ b/forge-gui/res/editions/Masters Edition IV.txt @@ -5,7 +5,7 @@ Name=Masters Edition IV Code2=ME4 Type=Online BoosterCovers=1 -Booster=10 Common, 3 Uncommon, 1 Rare, 1 BasicLand +Booster=10 Common, 3 Uncommon, 1 Rare, 1 fromSheet("ME4 UrzaLands") ScryfallCode=ME4 [cards] @@ -279,6 +279,20 @@ ScryfallCode=ME4 259d L Urza's Tower @Mark Poole 260 R Volcanic Island @Brian Snõddy +[UrzaLands] +1 Urza's Tower|ME4|1 +1 Urza's Tower|ME4|2 +1 Urza's Tower|ME4|3 +1 Urza's Tower|ME4|4 +1 Urza's Mine|ME4|1 +1 Urza's Mine|ME4|2 +1 Urza's Mine|ME4|3 +1 Urza's Mine|ME4|4 +1 Urza's Power Plant|ME4|1 +1 Urza's Power Plant|ME4|2 +1 Urza's Power Plant|ME4|3 +1 Urza's Power Plant|ME4|4 + [tokens] r_1_1_goblin c_1_1_a_tetravite_flying_noenchant diff --git a/forge-gui/res/editions/Modern Horizons 2.txt b/forge-gui/res/editions/Modern Horizons 2.txt index ba8171f53fa..a67b2715c2d 100644 --- a/forge-gui/res/editions/Modern Horizons 2.txt +++ b/forge-gui/res/editions/Modern Horizons 2.txt @@ -318,7 +318,7 @@ ScryfallCode=MH2 302 U Mishra's Factory @Scott Chou 303 R Riptide Laboratory @John Avon -[borderless] +[alternate art] 304 M Dakkon, Shadow Slayer @Jake Murray 305 M Geyadrone Dihada @Aleksi Briclot 306 M Grist, the Hunger Tide @Victor Adame Minguez @@ -399,7 +399,7 @@ ScryfallCode=MH2 379 M Kaldra Compleat @Vincent Proce 380 R Urza's Saga @Titus Lunter -[retro frame] +[alternate frame] 381 C Blacksmith's Skill @Jason A. Engle 382 C Marble Gargoyle @Drew Tucker 383 R Out of Time @Tobias Kwan @@ -503,7 +503,7 @@ ScryfallCode=MH2 479 R Verdant Catacombs @Vance Kovacs 480 R Yavimaya, Cradle of Growth @Sarah Finnigan -[bundle] +[promo] 481 L Plains @Eric Peterson 482 L Plains @Alan Pollack 483 L Island @Donato Giancola @@ -518,7 +518,7 @@ ScryfallCode=MH2 [buy a box] 491 M Sanctum Prelate @Michael C. Hayes -[promo] +[bundle] 492 R Yusri, Fortune's Flame @Evyn Fong [Lands] diff --git a/forge-gui/res/editions/Modern Horizons 3.txt b/forge-gui/res/editions/Modern Horizons 3.txt index 2aaaa60079e..7bfa0cd196c 100644 --- a/forge-gui/res/editions/Modern Horizons 3.txt +++ b/forge-gui/res/editions/Modern Horizons 3.txt @@ -10,7 +10,6 @@ BoosterCovers=3 ChaosDraftThemes=MASTERS_SET ScryfallCode=MH3 -#Numbers from https://mtgscribe.com/2024/05/22/play-booster-fact-sheet-modern-horizons-3/ [Common] Base=Common:fromSheet("MH3 cards") @@ -23,8 +22,8 @@ Base=Uncommon:fromSheet("MH3 cards") [RareMythic] Base=RareMythic:fromSheet("MH3 cards") -Replace=.021F RareMythic:fromSheet("MH3 retro frame") -Replace=.051F RareMythic:fromSheet("MH3 borderless") +Replace=.021F fromSheet("MH3 alternate frame") +Replace=.051F fromSheet("MH3 borderless") [Common-Land] Base=Common:fromSheet("MH3 cards") @@ -37,16 +36,16 @@ Replace=.067F fromSheet("MH3 full art")+ Base=Uncommon:fromSheet("MH3 new to modern") Replace=.213F Rare:fromSheet("MH3 new to modern") Replace=.023F Mythic:fromSheet("MH3 new to modern") -Replace=.011F RareMythic:fromSheet("MH3 borderless") -Replace=.001F Mythic:fromSheet("MH3 borderless") -Replace=.002F RareMythic:fromSheet("MH3 retro frame") +Replace=.008F fromSheet("MH3 borderless frame") +Replace=.003F fromSheet("MH3 borderless profile") +Replace=.001F fromSheet("MH3 alternate frame") [Wildcard] Base=Common:fromSheet("MH3 cards") Replace=.417F Uncommon:fromSheet("MH3 cards") Replace=.078F RareMythic:fromSheet("MH3 cards") -Replace=.004F RareMythic:fromSheet("MH3 borderless") -Replace=.042F fromSheet("MH3 retro frame") +Replace=.004F fromSheet("MH3 borderless frame") +Replace=.042F fromSheet("MH3 alternate frame") [cards] 1 U Breaker of Creation @Yohann Schepacz @@ -352,8 +351,6 @@ Replace=.042F fromSheet("MH3 retro frame") 301 R Deserted Temple @Rob Alexander 302 U Nesting Grounds @Yeong-Hao Han 303 M Phyrexian Tower @Martin de Diego Sádaba - -[bundle] 310 L Plains @Volkan Baǵa 311 L Plains @Lius Lasahido 312 L Island @Alayna Danner @@ -404,6 +401,8 @@ Replace=.042F fromSheet("MH3 retro frame") 347 R Pearl Medallion @Olena Richards 348 R Ruby Medallion @Martina Pilcerova 349 R Sapphire Medallion @Ron Spears + +[borderless frame] 350 R Archway of Innovation @Sam Burley 351 R Arena of Glory @Piotr Dura 352 R Bloodstained Mire @Sean Vo @@ -425,6 +424,8 @@ Replace=.042F fromSheet("MH3 retro frame") 368 R Laelia, the Blade Reforged @Tyler Walpole 369 M Eladamri, Korvecdal @Tyler Walpole 370 R Six @Ivan Shavrin + +[borderless profile] 371 M Arna Kennerüd, Skycaptain @Grant Griffin 372 M Breya, Etherium Shaper @Jack Hughes 373 R Genku, Future Shaper @Ivan Shavrin @@ -449,7 +450,7 @@ Replace=.042F fromSheet("MH3 retro frame") 471 M Ral, Monsoon Mage @Borja Pindado 472 M Grist, Voracious Larva @Ron Spencer -[retro frame] +[showcase] 384 M Emrakul, the World Anew @Brent Hollowell 385 U It That Heralds the End @Alex Konstad 386 M Kozilek, the Broken Reality @Brent Hollowell @@ -501,15 +502,6 @@ Replace=.042F fromSheet("MH3 retro frame") 432 R Kudo, King Among Bears @Ekaterina Burmak 433 R Psychic Frog @Pete Venters 434 R Rosheen, Roaring Prophet @Fang Xinyu -435 R Bloodstained Mire @Bruce Brenneise -436 R Flooded Strand @Alexander Forssberg -437 U Nesting Grounds @Yeong-Hao Han -438 R Polluted Delta @Chris Ostrowski -439 U Snow-Covered Wastes @Mark Poole -440 R Windswept Heath @Alexander Forssberg -441 R Wooded Foothills @Chris Ostrowski - -[etched] 497 L Plains @Volkan Baǵa 498 L Plains @Lius Lasahido 499 L Island @Alayna Danner @@ -535,6 +527,15 @@ Replace=.042F fromSheet("MH3 retro frame") 519 U Solar Transformer @Mike Bierek 520 C Tranquil Landscape @Randy Gallegos 521 C Twisted Landscape @Piotr Dura + +[alternate frame] +435 R Bloodstained Mire @Bruce Brenneise +436 R Flooded Strand @Alexander Forssberg +437 U Nesting Grounds @Yeong-Hao Han +438 R Polluted Delta @Chris Ostrowski +439 U Snow-Covered Wastes @Mark Poole +440 R Windswept Heath @Alexander Forssberg +441 R Wooded Foothills @Chris Ostrowski 473 M Emrakul, the World Anew @Brent Hollowell 474 M Herigast, Erupting Nullkite @Lucas Graciano 475 M Kozilek, the Broken Reality @Brent Hollowell diff --git a/forge-gui/res/editions/Modern Horizons.txt b/forge-gui/res/editions/Modern Horizons.txt index d7d020ea26f..b23b38581f6 100644 --- a/forge-gui/res/editions/Modern Horizons.txt +++ b/forge-gui/res/editions/Modern Horizons.txt @@ -5,7 +5,7 @@ Name=Modern Horizons Code2=MH1 Type=Draft BoosterCovers=5 -Booster=10 Common:fromSheet("MH1 cards"), 3 Uncommon:fromSheet("MH1 cards"), 1 RareMythic:fromSheet("MH1 cards"), 1 BasicLand:fromSheet("MH1 cards") +Booster=10 Common:!fromSheet("MH1 Secret Cards"), 3 Uncommon:!fromSheet("MH1 Secret Cards"), 1 RareMythic:!fromSheet("MH1 Secret Cards"), 1 fromSheet("MH1 Lands") BoosterBox=24 ChaosDraftThemes=MASTERS_SET;GRAVEYARD_MATTERS ScryfallCode=MH1 @@ -265,8 +265,6 @@ ScryfallCode=MH1 252 L Snow-Covered Swamp @Titus Lunter 253 L Snow-Covered Mountain @Titus Lunter 254 L Snow-Covered Forest @Titus Lunter - -[buy a box] 255 R Flusterstorm @Chris Rallis [tokens] diff --git a/forge-gui/res/editions/Murders at Karlov Manor.txt b/forge-gui/res/editions/Murders at Karlov Manor.txt index 2c98d646b3a..e87bba11f92 100644 --- a/forge-gui/res/editions/Murders at Karlov Manor.txt +++ b/forge-gui/res/editions/Murders at Karlov Manor.txt @@ -4,33 +4,11 @@ Date=2024-02-09 Name=Murders at Karlov Manor Type=Expansion ScryfallCode=MKM -BoosterSlots=Common,Common-Guest,Uncommon,RareMythic,BasicLand,Wildcard,PrereleasePromo -Booster=6 Common, 1 Common-Guest, 3 Uncommon, 1 RareMythic, 1 BasicLand, 1 Wildcard, 1 Wildcard+ -Prerelease=6 Boosters, 1 RareMythic+, 1 PrereleasePromo+ +Booster=7 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand, 1 Any, 1 Any+ +ChanceReplaceCommonWith=.125F fromsheet("MKM karlov surprise") +Prerelease=6 Boosters, 1 RareMythic+, 1 Any:fromsheet("MKM prerelease promo")+ BoosterBox=36 -[Common] -Base=Common:fromSheet("MKM cards") - -[Common-Guest] -Base=Common:fromSheet("MKM cards") -Replace=.125F fromSheet("MKM karlov surprise") - -[Uncommon] -Base=Uncommon:fromSheet("MKM cards") - -[RareMythic] -Base=RareMythic:fromSheet("MKM cards") - -[BasicLand] -Base=BasicLand:fromSheet("MKM cards") - -[Wildcard] -Base=Any:fromSheet("MKM cards") - -[PrereleasePromo] -Base=Any:fromsheet("MKM prerelease promo") - [cards] 1 U Case of the Shattered Pact @Peter Polach 2 U Absolving Lammasu @Izzy @@ -469,8 +447,6 @@ Base=Any:fromsheet("MKM prerelease promo") 426 U Lightning Helix @Eli Minaya 427 U No More Lies @Liiga Smilshkalne 428 R Axebane Ferox @Adam Volker - -[prerelease promo] 430 M Melek, Reforged Researcher @Andreas Zafiratos 431 M Tomik, Wielder of Law @Valera Lutfullina 432 M Voja, Jaws of the Conclave @Valera Lutfullina @@ -478,6 +454,11 @@ Base=Any:fromsheet("MKM prerelease promo") [buy a box] 429 R Wojek Investigator @Greg Staples +[prerelease promo] +1 Melek, Reforged Researcher|MKM +1 Tomik, Wielder of Law|MKM +1 Voja, Jaws of the Conclave|MKM + [karlov surprise] 1 Baleful Mastery|PLIST 1 Bishop of the Bloodstained|PLIST diff --git a/forge-gui/res/editions/Oath of the Gatewatch.txt b/forge-gui/res/editions/Oath of the Gatewatch.txt index d77daed9188..005ed48174a 100644 --- a/forge-gui/res/editions/Oath of the Gatewatch.txt +++ b/forge-gui/res/editions/Oath of the Gatewatch.txt @@ -8,7 +8,7 @@ BoosterCovers=4 Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand BFZ FatPack=9 FatPackExtraSlots=66 BasicLands BFZ, 14 name("Wastes") -AdditionalSheetForFoils=fromSheet("EXP special slot") +AdditionalSheetForFoils=fromSheet("EXP Lands 2") AdditionalSetUnlockedInQuest=EXP ScryfallCode=OGW @@ -196,9 +196,9 @@ ScryfallCode=OGW 181 C Unknown Shores @Jung Park 182 R Wandering Fumarole @Florian de Gesincourt 183 C Wastes @Jason Felix -183a C Wastes @Jason Felix +183 C Wastes @Jason Felix +184 C Wastes @Raymond Swanland 184 C Wastes @Raymond Swanland -184a C Wastes @Raymond Swanland [tokens] c_1_1_eldrazi_scion_sac diff --git a/forge-gui/res/editions/Outlaws of Thunder Junction.txt b/forge-gui/res/editions/Outlaws of Thunder Junction.txt index 2795a29e0f4..0f4d9d99a09 100644 --- a/forge-gui/res/editions/Outlaws of Thunder Junction.txt +++ b/forge-gui/res/editions/Outlaws of Thunder Junction.txt @@ -4,37 +4,11 @@ Date=2024-04-19 Name=Outlaws of Thunder Junction Type=Expansion ScryfallCode=OTJ -BoosterSlots=Common,Common-Guest,Uncommon,RareMythic,Land,Wildcard,WildcardOTP -Booster=5 Common, 1 Common-Guest, 3 Uncommon, 1 RareMythic, 1 Land, 1 Wildcard, 1 Wildcard+, 1 WildcardOTP +Booster=6 Common, 3 Uncommon, 1 RareMythic, 1 Land, 1 Any, 1 Any OTP, 1 Any+ +ChanceReplaceCommonWith=.20F fromsheet("OTJ outlaw list") Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 -#Numbers from https://mtgscribe.com/2024/04/05/outlaws-of-thunder-junction-play-booster-fact-sheet/ -[Common] -Base=Common:fromSheet("OTJ cards") - -[Common-Guest] -Base=Common:fromSheet("OTJ cards") -Replace=.20F fromSheet("OTJ outlaw list") - -[Uncommon] -Base=Uncommon:fromSheet("OTJ cards") - -[RareMythic] -Base=RareMythic:fromSheet("OTJ cards") -Replace=.025F RareMythic:!fromSheet("OTJ outlaw list") - -[Wildcard] -Base=Any:!fromSheet("OTJ outlaw list") -Replace=.0833F RareMythic:fromSheet("OTJ cards") - -[WildcardOTP] -Base=Uncommon:fromSheet("OTP cards") -Replace=.3333F RareMythic:fromSheet("OTP cards") - -[Land] -Base=Land:fromSheet("OTJ cards") - [cards] 1 R Another Round @Darrell Riche 2 M Archangel of Tithes @Denys Tsiperko diff --git a/forge-gui/res/editions/Phyrexia All Will Be One.txt b/forge-gui/res/editions/Phyrexia All Will Be One.txt index d7b9eda8240..de9b2b065e3 100644 --- a/forge-gui/res/editions/Phyrexia All Will Be One.txt +++ b/forge-gui/res/editions/Phyrexia All Will Be One.txt @@ -281,8 +281,6 @@ ScryfallCode=ONE 269 L Swamp @Mark Riddick 270 L Mountain @Mark Riddick 271 L Forest @Mark Riddick - -[precon product] 272 L Plains @Sergey Glushakov 273 L Island @David Álvarez 274 L Swamp @Julian Kok Joon Wen @@ -295,6 +293,11 @@ ScryfallCode=ONE 406 R Kinzu of the Bleak Coven @Andreas Zafiratos 407 R Rhuk, Hexgold Nabber @Andrea De Dominicis 408 R Goliath Hatchery @Simon Dominic +409 R Mite Overseer @Néstor Ossandón Leal +410 R Serum Sovereign @Chris Rallis +411 R Kinzu of the Bleak Coven @Andreas Zafiratos +412 R Rhuk, Hexgold Nabber @Andrea De Dominicis +413 R Goliath Hatchery @Simon Dominic [showcase] 285 U Bladed Ambassador @Ravenna Tran @@ -374,8 +377,6 @@ ScryfallCode=ONE 369 L Forest @Alayna Danner 414 M Elesh Norn, Mother of Machines @Martina Fačková 415 M Elesh Norn, Mother of Machines @Junji Ito - -[etched] 417 U Bladed Ambassador @Ravenna Tran 418 M Elesh Norn, Mother of Machines @Martina Fačková 419 M Elesh Norn, Mother of Machines @Junji Ito @@ -488,11 +489,6 @@ ScryfallCode=ONE 401 R The Monumental Facade @Bruce Brenneise 402 R The Mycosynth Gardens @Andrew Mar 403 R The Seedcore @Kasia 'Kafis' Zielińska -409 R Mite Overseer @Néstor Ossandón Leal -410 R Serum Sovereign @Chris Rallis -411 R Kinzu of the Bleak Coven @Andreas Zafiratos -412 R Rhuk, Hexgold Nabber @Andrea De Dominicis -413 R Goliath Hatchery @Simon Dominic [buy a box] 284 R Green Sun's Twilight @Piotr Dura diff --git a/forge-gui/res/editions/Portal.txt b/forge-gui/res/editions/Portal.txt index 1fbbe394094..8b7a83d5710 100644 --- a/forge-gui/res/editions/Portal.txt +++ b/forge-gui/res/editions/Portal.txt @@ -16,6 +16,7 @@ ScryfallCode=POR 4 U Ardent Militia @Mike Raabe 5 R Armageddon @John Avon 6 C Armored Pegasus @Andrew Robinson +6d C Armored Pegasus @Andrew Robinson 7 R Blessed Reversal @Zina Saunders 8 R Blinding Light @John Coulthart 9 C Border Guard @Kev Walker @@ -40,6 +41,7 @@ ScryfallCode=POR 28 C Spotted Griffin @William Simpson 29 U Starlight @John Avon 30 U Starlit Angel @Rebecca Guay +30s U Starlit Angel @徐晓鸣 31 C Steadfastness @Kev Walker 32 R Stern Marshal @D. Alexander Gregory 33 R Temporary Truce @Mike Raabe @@ -48,6 +50,7 @@ ScryfallCode=POR 36 U Vengeance @Andrew Robinson 37 U Wall of Swords @Douglas Shuler 38 C Warrior's Charge @Ted Naifeh +38† C Warrior's Charge @Ted Naifeh 39 R Wrath of God @Mike Raabe 40 R Ancestral Memories @Dan Frazier 41 R Balance of Power @Adam Rex @@ -56,6 +59,7 @@ ScryfallCode=POR 44 C Cloak of Feathers @Rebecca Guay 45 R Cloud Dragon @John Avon 46 C Cloud Pirates @Phil Foglio +46d C Cloud Pirates @Phil Foglio 47 U Cloud Spirit @DiTerlizzi 48 U Command of Unsummoning @Phil Foglio 49 C Coral Eel @Una Fricker @@ -67,6 +71,7 @@ ScryfallCode=POR 55 U Flux @Ted Naifeh 56 C Giant Octopus @John Matson 57 C Horned Turtle @Adrian Smith +57s C Horned Turtle @Wang Yuqun 58 U Ingenious Thief @Dan Frazier 59 U Man-o'-War @Una Fricker 60 C Merfolk of the Pearl Trident @DiTerlizzi @@ -76,11 +81,15 @@ ScryfallCode=POR 64 U Personal Tutor @D. Alexander Gregory 65 R Phantom Warrior @Dan Frazier 66 R Prosperity @Phil Foglio +66s R Prosperity @李有良 67 C Snapping Drake @Christopher Rush +67d C Snapping Drake @Christopher Rush 68 C Sorcerous Sight @Kaja Foglio 69 C Storm Crow @Una Fricker +69d C Storm Crow @Una Fricker 70 C Symbol of Unsummoning @Adam Rex 71 R Taunt @Phil Foglio +71s R Taunt @杨钊 72 U Theft of Dreams @Adam Rex 73 R Thing from the Deep @Paolo Parente 74 C Tidal Surge @Douglas Shuler @@ -90,6 +99,7 @@ ScryfallCode=POR 78 U Withering Gaze @Scott M. Fischer 79 U Arrogant Vampire @Zina Saunders 80 U Assassin's Blade @John Matson +80s U Assassin's Blade @徐晓鸣 81 C Bog Imp @Christopher Rush 82 C Bog Raiders @Steve Luke 83 U Bog Wraith @Ted Naifeh @@ -103,9 +113,11 @@ ScryfallCode=POR 91 R Ebon Dragon @Donato Giancola 92 R Endless Cockroaches @Ron Spencer 93 C Feral Shadow @Colin MacNeil +93d C Feral Shadow @Colin MacNeil 94 R Final Strike @John Coulthart 95 U Gravedigger @Scott M. Fischer 96 C Hand of Death @John Coulthart +96† C Hand of Death @John Coulthart 97 C Howling Fury @Mike Dringenberg 98 R King's Assassin @Zina Saunders 99 R Mercenary Knight @Adrian Smith @@ -117,6 +129,7 @@ ScryfallCode=POR 105 C Python @Alan Rabinowitz 106 U Rain of Tears @Eric Peterson 107 C Raise Dead @Charles Gillespie +107s C Raise Dead @李尤松 108 R Serpent Assassin @Roger Raupp 109 C Serpent Warrior @Roger Raupp 110 C Skeletal Crocodile @Mike Dringenberg @@ -124,10 +137,14 @@ ScryfallCode=POR 112 C Soul Shred @Alan Rabinowitz 113 C Undying Beast @Steve Luke 114 U Vampiric Feast @D. Alexander Gregory +114s U Vampiric Feast @王峰 115 C Vampiric Touch @Zina Saunders 116 U Virtue's Ruin @Mike Dringenberg 117 R Wicked Pact @Adam Rex +117s R Wicked Pact @杨光恒 118 U Blaze @Gerry Grace +118† U Blaze @Gerry Grace +118s U Blaze @David A. Cherry 119 U Boiling Seas @Tom Wänerstrand 120 C Burning Cloak @Scott M. Fischer 121 C Craven Giant @Ron Spencer @@ -144,6 +161,7 @@ ScryfallCode=POR 132 C Highland Giant @Ron Spencer 133 C Hill Giant @Randy Gallegos 134 U Hulking Cyclops @Paolo Parente +134s U Hulking Cyclops @Lin Yan 135 C Hulking Goblin @Pete Venters 136 R Last Chance @Hannibal King 137 C Lava Axe @Adrian Smith @@ -155,6 +173,7 @@ ScryfallCode=POR 143 R Pyroclasm @John Matson 144 C Raging Cougar @Terese Nielsen 145 C Raging Goblin @Pete Venters +145† C Raging Goblin @Pete Venters 146 C Raging Minotaur @Scott M. Fischer 147 U Rain of Salt @Charles Gillespie 148 C Scorching Spear @Mike Raabe @@ -168,14 +187,19 @@ ScryfallCode=POR 156 R Winds of Change @Adam Rex 157 R Alluring Scent @Ted Naifeh 158 U Anaconda @Andrew Robinson +158† U Anaconda @Andrew Robinson 159 U Bee Sting @Phil Foglio 160 U Bull Hippo @Roger Raupp +160d U Bull Hippo @Roger Raupp 161 R Charging Rhino @Una Fricker 162 U Deep Wood @Paolo Parente 163 C Elite Cat Warrior @Eric Peterson +163† C Elite Cat Warrior @Eric Peterson 164 C Elven Cache @Rebecca Guay +164s C Elven Cache @张艺娜 165 C Elvish Ranger @DiTerlizzi 166 C Fruition @Steve Luke +166s C Fruition @Wang Yuqun 167 C Giant Spider @Randy Gallegos 168 C Gorilla Warrior @John Matson 169 C Grizzly Bears @Zina Saunders @@ -183,6 +207,7 @@ ScryfallCode=POR 171 C Jungle Lion @Janine Johnston 172 C Mobilize @Rebecca Guay 173 C Monstrous Growth @Dan Frazier +173† C Monstrous Growth @Dan Frazier 174 U Moon Sprite @Terese Nielsen 175 R Natural Order @Alan Rabinowitz 176 U Natural Spring @Janine Johnston @@ -241,30 +266,3 @@ ScryfallCode=POR 214s L Forest @李铁 215 L Forest @John Avon 215s L Forest @李铁 - -[alternate art] -6d C Armored Pegasus @Andrew Robinson -30s U Starlit Angel @徐晓鸣 -38† C Warrior's Charge @Ted Naifeh -46d C Cloud Pirates @Phil Foglio -57s C Horned Turtle @Wang Yuqun -66s R Prosperity @李有良 -67d C Snapping Drake @Christopher Rush -69d C Storm Crow @Una Fricker -71s R Taunt @杨钊 -80s U Assassin's Blade @徐晓鸣 -93d C Feral Shadow @Colin MacNeil -96† C Hand of Death @John Coulthart -107s C Raise Dead @李尤松 -114s U Vampiric Feast @王峰 -117s R Wicked Pact @杨光恒 -118† U Blaze @Gerry Grace -118s U Blaze @David A. Cherry -134s U Hulking Cyclops @Lin Yan -145† C Raging Goblin @Pete Venters -158† U Anaconda @Andrew Robinson -160d U Bull Hippo @Roger Raupp -163† C Elite Cat Warrior @Eric Peterson -164s C Elven Cache @张艺娜 -166s C Fruition @Wang Yuqun -173† C Monstrous Growth @Dan Frazier diff --git a/forge-gui/res/editions/Ravnica Allegiance.txt b/forge-gui/res/editions/Ravnica Allegiance.txt index ba8f71d8089..d0c565e90a3 100644 --- a/forge-gui/res/editions/Ravnica Allegiance.txt +++ b/forge-gui/res/editions/Ravnica Allegiance.txt @@ -5,7 +5,7 @@ Name=Ravnica Allegiance Code2=RNA Type=Expansion BoosterCovers=5 -Booster=10 Common:fromSheet("RNA cards"):!fromSheet("RNA Lands"), 3 Uncommon:fromSheet("RNA cards"), 1 RareMythic:fromSheet("RNA cards"), 1 fromSheet("RNA Lands") +Booster=10 Common:!fromSheet("RNA Secret Cards"), 3 Uncommon:!fromSheet("RNA Secret Cards"), 1 RareMythic:!fromSheet("RNA Secret Cards"), 1 fromSheet("RNA Lands") FatPack=10 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=RAVNICA @@ -271,8 +271,6 @@ ScryfallCode=RNA 257 C Simic Guildgate @Adam Paquette 258 C Simic Guildgate @Adam Paquette 259 R Stomping Ground @James Paick - -[precon product] 260 L Plains @Titus Lunter 261 L Island @Eytan Zana 262 L Swamp @Adam Paquette @@ -286,17 +284,8 @@ ScryfallCode=RNA 270 C Ragefire @Randy Vargas 271 U Charging War Boar @Izzy 272 R Domri's Nodorog @Svetlin Velinov - -[buy a box] 273 M The Haunt of Hightower @Lius Lasahido -[Lands] -Azorius Guildgate|RNA -Gruul Guildgate|RNA -Orzhov Guildgate|RNA -Rakdos Guildgate|RNA -Simic Guildgate|RNA - [tokens] rg_4_4_beast_trample g_3_3_centaur diff --git a/forge-gui/res/editions/Ravnica Remastered.txt b/forge-gui/res/editions/Ravnica Remastered.txt index d35e155c6d5..def70749f12 100644 --- a/forge-gui/res/editions/Ravnica Remastered.txt +++ b/forge-gui/res/editions/Ravnica Remastered.txt @@ -341,7 +341,7 @@ ScryfallCode=RVR 444 M Domri Rade @Susumu Kuroi 445 M Ral Zarek @Fukuzo Katsura -[retro frame] +[showcase] 302 R Blazing Archon @Zoltan Boros & Gabor Szikszai 303 R Blind Obedience @Seb McKinnon 304 U Condemn @Daren Bader diff --git a/forge-gui/res/editions/Streets of New Capenna.txt b/forge-gui/res/editions/Streets of New Capenna.txt index ffcb6a1de32..9bc7ae5706e 100644 --- a/forge-gui/res/editions/Streets of New Capenna.txt +++ b/forge-gui/res/editions/Streets of New Capenna.txt @@ -420,8 +420,6 @@ ScryfallCode=SNC 403 R Void Rend @Krharts 404 M Ziatora, the Incinerator @Scott M. Fischer 405 R Ziatora's Envoy @Olga Tereshenko - -[etched] 441 M Elspeth Resplendent @Krharts 442 R Giada, Font of Hope @Scott M. Fischer 443 M Sanctuary Warden @Julie Dillon diff --git a/forge-gui/res/editions/The Big Score.txt b/forge-gui/res/editions/The Big Score.txt index ebe60453f6a..3e7234983f0 100644 --- a/forge-gui/res/editions/The Big Score.txt +++ b/forge-gui/res/editions/The Big Score.txt @@ -36,8 +36,6 @@ ScryfallCode=BIG 28 M Transmutation Font @Mark Poole 29 M Fomori Vault @Jonas De Ro 30 M Tarnation Vista @Alayna Danner - -[showcase] 31 M Collector's Cage @Ben Hill 32 M Grand Abolisher @David Astruga 33 M Oltec Matterweaver @Inkognit @@ -73,8 +71,6 @@ ScryfallCode=BIG 63 M Lotus Ring @Ben Hill 64 M Sword of Wealth and Power @Artur Nakhodkin 65 M Tarnation Vista @Artur Nakhodkin - -[extended art] 66 M Collector's Cage @Bartek Fedyczak 67 M Grand Abolisher @Aurore Folny 68 M Oltec Matterweaver @Villarrte diff --git a/forge-gui/res/editions/The Brothers War.txt b/forge-gui/res/editions/The Brothers War.txt index 4a565b0f3af..7de183f006d 100644 --- a/forge-gui/res/editions/The Brothers War.txt +++ b/forge-gui/res/editions/The Brothers War.txt @@ -305,6 +305,11 @@ ScryfallCode=BRO 290 R Terror Ballista @Leon Tukker 291 R Artificer's Dragon @Leon Tukker 292 R Woodcaller Automaton @Ryan Pancoast +373 R Rescue Retriever @Jesper Ejsing +374 R Geology Enthusiast @Fajareka Setiawan +375 R Terror Ballista @Leon Tukker +376 R Artificer's Dragon @Leon Tukker +377 R Woodcaller Automaton @Ryan Pancoast [borderless] 293 M Teferi, Temporal Pilgrim @Cosmin Podar @@ -389,11 +394,6 @@ ScryfallCode=BRO 370 R Fortified Beachhead @Christian Dimitrov 371 R Hall of Tagsin @Christian Dimitrov 372 R Mishra's Foundry @Leon Tukker -373 R Rescue Retriever @Jesper Ejsing -374 R Geology Enthusiast @Fajareka Setiawan -375 R Terror Ballista @Leon Tukker -376 R Artificer's Dragon @Leon Tukker -377 R Woodcaller Automaton @Ryan Pancoast [buy a box] 378 R Mishra's Foundry @Leon Tukker diff --git a/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt b/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt index e7906598cb8..4691ed5e74a 100644 --- a/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt +++ b/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt @@ -298,11 +298,21 @@ ScryfallCode=LTR 284 R Ringwraiths @Warren Mahy 285 R Assault on Osgiliath @Warren Mahy 286 R Elanor Gardner @Torgeir Fjereide +383 R Saradoc, Master of Buckland @Sean Vo +384 R Elvish Mariner @Axel Sauerwald +385 R Ringwraiths @Warren Mahy +386 R Assault on Osgiliath @Warren Mahy +387 R Elanor Gardner @Torgeir Fjereide 824 R Eagle of Deliverance @Sidharth Chaturvedi 825 R Minas Tirith Garrison @Irina Nordsol 826 R Warg Rider @Pascal Quidault 827 R Riders of the Mark @Antonio José Manzanedo 828 R Mirkwood Channeler @Irina Nordsol +829 R Eagle of Deliverance @Sidharth Chaturvedi +830 R Minas Tirith Garrison @Irina Nordsol +831 R Warg Rider @Pascal Quidault +832 R Riders of the Mark @Antonio José Manzanedo +833 R Mirkwood Channeler @Irina Nordsol [bundle] 287 M Aragorn and Arwen, Wed @Magali Villeneuve @@ -795,11 +805,6 @@ ScryfallCode=LTR 380 M The One Ring @Veli Nyström 381 M Palantír of Orthanc @Tatiana Veryayskaya 382 R Phial of Galadriel @Andrea Piparo -383 R Saradoc, Master of Buckland @Sean Vo -384 R Elvish Mariner @Axel Sauerwald -385 R Ringwraiths @Warren Mahy -386 R Assault on Osgiliath @Warren Mahy -387 R Elanor Gardner @Torgeir Fjereide 388 R Frodo, Determined Hero @Magali Villeneuve 389 R Gandalf, White Rider @Ekaterina Burmak 390 R Gollum, Scheming Guide @Dmitry Burmak @@ -847,11 +852,6 @@ ScryfallCode=LTR 791 M The One Ring @Veli Nyström 792 M Palantír of Orthanc @Tatiana Veryayskaya 793 R Phial of Galadriel @Andrea Piparo -829 R Eagle of Deliverance @Sidharth Chaturvedi -830 R Minas Tirith Garrison @Irina Nordsol -831 R Warg Rider @Pascal Quidault -832 R Riders of the Mark @Antonio José Manzanedo -833 R Mirkwood Channeler @Irina Nordsol [buy a box] 398 R Trailblazer's Boots @Alexander Gering diff --git a/forge-gui/res/editions/Time Spiral Remastered.txt b/forge-gui/res/editions/Time Spiral Remastered.txt index 4c13f0b3785..c840d738798 100644 --- a/forge-gui/res/editions/Time Spiral Remastered.txt +++ b/forge-gui/res/editions/Time Spiral Remastered.txt @@ -298,7 +298,7 @@ ScryfallCode=TSR 288 U Urza's Factory @Mark Tedin 289 M Vesuva @Zoltan Boros & Gabor Szikszai -[retro frame] +[showcase] 290 S Ajani's Pridemate @Svetlin Velinov 291 S Banishing Light @Willian Murai 292 S Containment Priest @John Stanko @@ -421,7 +421,7 @@ ScryfallCode=TSR 409 S Ramunap Ruins @Florian de Gesincourt 410 S Wastes @Raymond Swanland -[buy a box] +[promo] 411 R Lotus Bloom @Christopher Rush [tokens] diff --git a/forge-gui/res/editions/Unglued.txt b/forge-gui/res/editions/Unglued.txt index 440f8535341..fb881546a66 100644 --- a/forge-gui/res/editions/Unglued.txt +++ b/forge-gui/res/editions/Unglued.txt @@ -30,7 +30,7 @@ ScryfallCode=UGL 18 C Clambassadors @Randy Elliott 19 C Clam-I-Am @Randy Elliott 20 C Clam Session @Randy Elliott -21 U Common Courtesy @Mike Raabe +21 C Common Courtesy @Mike Raabe 22 C Denied! @Quinton Hoover 23 C Double Take @Claymore J. Flapdoodle 24 C Fowl Play @Mark Poole diff --git a/forge-gui/res/editions/Unhinged.txt b/forge-gui/res/editions/Unhinged.txt index ecd990e8791..1377b6c17cc 100644 --- a/forge-gui/res/editions/Unhinged.txt +++ b/forge-gui/res/editions/Unhinged.txt @@ -12,7 +12,7 @@ ScryfallCode=UNH [cards] 1 U Atinlay Igpay @Evkay Alkerway 2 C AWOL @Stephen Tappin -3 U Bosom Buddy @Dan Murayama Scott +3 U Bosom Buddy @Dan Scott 4 C Cardpecker @Richard Sardinha 5 C Cheap Ass @Randy Gallegos 6 C Circle of Protection: Art @Jim "Stop the Da Vinci Beatdown" Pavelec @@ -150,7 +150,7 @@ ScryfallCode=UNH 138 L Swamp @John Avon 139 L Mountain @John Avon 140 L Forest @John Avon -141 R Super Secret Tech @Dan Frazier +141 S Super Secret Tech @Dan Frazier [tokens] r_1_1_goblin diff --git a/forge-gui/res/editions/Unstable.txt b/forge-gui/res/editions/Unstable.txt index 1f0b8004b26..23637ce69e2 100644 --- a/forge-gui/res/editions/Unstable.txt +++ b/forge-gui/res/editions/Unstable.txt @@ -10,270 +10,270 @@ FoilAlwaysInCommonSlot=False ScryfallCode=UST [cards] -3a C Amateur Auteur @McLean Kendree -3b C Amateur Auteur @McLean Kendree -3c C Amateur Auteur @McLean Kendree -3d C Amateur Auteur @McLean Kendree -6 M Do-It-Yourself Seraph @David Sladek -7 U Gimme Five @Jesper Ejsing -8 C GO TO JAIL @Marco Teixeira -9 U Half-Kitten, Half- @Andrea Radeck -10 C Humming- @Mark Behm -11 R Jackknight @Ben Wootten -12a U Knight of the Kitchen Sink @Mark A. Nelson $A -12b U Knight of the Kitchen Sink @Mark A. Nelson $B -12c U Knight of the Kitchen Sink @Mark A. Nelson $C -12d U Knight of the Kitchen Sink @Mark A. Nelson $D -12e U Knight of the Kitchen Sink @Mark A. Nelson $E -12f U Knight of the Kitchen Sink @Mark A. Nelson $F -13 U Knight of the Widget @Emrah Elmasli -14 U Midlife Upgrade @Hector Ortiz -15 R Oddly Uneven @Ben Wootten -16 C Old Guard @David Sladek -17 C Ordinary Pony @Andrea Radeck -18 U Rhino- @YW Tang -19 C Riveting Rigger @Mark A. Nelson -20 R Rules Lawyer @Sean Murray -21 C Sacrifice Play @Matt Gaser -22 C Shaggy Camel @Kari Christensen -23 U Side Quest @Alex Konstad -24 C Success! @Andrea Radeck -25 U Teacher's Pet @Mark Behm -26 R Animate Library @Raymond Swanland -27 C Blurry Beeble @Jeff Miracola -28 C Chipper Chopper @Dmitry Burmak -29 R Clocknapper @Marco Teixeira -30 C Crafty Octopus @Mark Behm -31 U Crow Storm @YW Tang -32 C Defective Detective @Matt Dixon -33 U Five-Finger Discount @Milivoj Ćeran -34 R Graveyard Busybody @Bram Sels -35 U Half-Shark, Half- @Brynn Metheney -36 R Incite Insight @David Sladek -37 U Kindly Cognician @Mark Behm -38 C Magic Word @Carl Frank -39 C Mer Man @Mark Behm -40 U More or Less @Chris Seaman -41a C Novellamental @Tom Babbey -41b C Novellamental @Tom Babbey -41c C Novellamental @Tom Babbey -41d C Novellamental @Tom Babbey -42 C Numbing Jellyfish @Matt Dixon -43 U S.N.E.A.K. Dispatcher @John Thacker -44 U Socketed Sprocketer @David Sladek -45 C Spell Suck @Michael Phillippi -46 U Spy Eye @Ben Wootten -47 U Suspicious Nanny @Chris Seaman -48 C Time Out @Dave Allsop -49a R Very Cryptic Command @Wayne England $A -49b R Very Cryptic Command @Zoltan Boros $B -49c R Very Cryptic Command @Zoltan Boros $C -49d R Very Cryptic Command @Zoltan Boros $D -49e R Very Cryptic Command @Zoltan Boros $E -49f R Very Cryptic Command @Zoltan Boros $F -50 C Wall of Fortune @Tom Babbey -51 C Big Boa Constrictor @Kari Christensen -52 C capital offense @Matt Dixon -53 C Dirty Rat @Kari Christensen -54a C Extremely Slow Zombie @Emrah Elmasli -54b C Extremely Slow Zombie @Emrah Elmasli -54c C Extremely Slow Zombie @Emrah Elmasli -54d C Extremely Slow Zombie @Emrah Elmasli -55 C Finders, Keepers @Mark Behm -56 R Hangman @Alex Konstad -57 C Hazmat Suit (Used) @Michael Phillippi -58 C Hoisted Hireling @Alex Konstad -59 U Inhumaniac @Matt Dixon -60 R Masterful Ninja @Matt Gaser -61 U Ninja @David Sladek -62 U Old-Fashioned Vampire @Simon Dominic -63 R Over My Dead Bodies @Even Amundsen -64 U Overt Operative @Bram Sels -65 U "Rumors of My Death..." @Alex Konstad -66 U Skull Saucer @Mike Burns -67a U Sly Spy @Michael Phillippi $A -67b U Sly Spy @Michael Phillippi $B -67c U Sly Spy @Michael Phillippi $C -67d U Sly Spy @Michael Phillippi $D -67e U Sly Spy @Michael Phillippi $E -67f U Sly Spy @Michael Phillippi $F -68 C Snickering Squirrel @Michael Phillippi -69 R Spike, Tournament Grinder @Zoltan Boros -70 U Squirrel-Powered Scheme @Even Amundsen -71 C Steady-Handed Mook @Carl Frank -72 C Stinging Scorpion @YW Tang -73 C Subcontract @Hector Ortiz -74 M Summon the Pack @Matt Cavotta -75 U Zombified @Kev Walker -76 R The Big Idea @Bram Sels -77 C Box of Free-Range Goblins @Chris Seaman -78 C Bumbling Pangolin @YW Tang -79 C Common Iguana @Brynn Metheney -80 R The Countdown Is at One @Jesper Ejsing -81 C Feisty Stegosaurus @Kari Christensen -82a U Garbage Elemental @Hector Ortiz $A -82b U Garbage Elemental @Hector Ortiz $B -82c U Garbage Elemental @Hector Ortiz $C -82d U Garbage Elemental @Hector Ortiz $D -82e U Garbage Elemental @Hector Ortiz $E -82f U Garbage Elemental @Hector Ortiz $F -83 U Goblin Haberdasher @Jesper Ejsing -84 U Half-Orc, Half- @Kev Walker -85 C Hammer Helper @Dave Allsop -86 U Hammer Jammer @Wayne Reynolds -87 U Hammerfest Boomtacular @Dave Allsop -88 M Infinity Elemental @Seb McKinnon -89 C It That Gets Left Hanging @Jesper Ejsing -90 C Just Desserts @Zoltan Boros -91 C Painiac @McLean Kendree -92 U Party Crasher @Mike Burns -93 R Steamflogger Boss @Warren Mahy -94 R Steamflogger of the Month @Warren Mahy -95 U Steamflogger Temp @Jeff Miracola -96 U Steamfloggery @Emrah Elmasli -97 U Super-Duper Death Ray @Even Amundsen -98a C Target Minotaur @Warren Mahy -98b C Target Minotaur @Warren Mahy -98c C Target Minotaur @Warren Mahy -98d C Target Minotaur @Warren Mahy -99 R Three-Headed Goblin @Mike Burns -100 C Work a Double @Carl Frank -101 C Wrench-Rigger @Jesper Ejsing -102 R As Luck Would Have It @Milivoj Ćeran -103a C Beast in Show @Mike Burns -103b C Beast in Show @Mike Burns -103c C Beast in Show @Mike Burns -103d C Beast in Show @Mike Burns -104 U Chittering Doom @Kari Christensen -105 U Clever Combo @Kev Walker -106 U Druid of the Sacred Beaker @Simon Dominic -107 C Eager Beaver @Andrea Radeck -108 R Earl of Squirrel @Milivoj Ćeran -109 U First Pick @John Thacker -110 C Ground Pounder @Warren Mahy -111 U Half-Squirrel, Half- @Andrea Radeck -112 R Hydradoodle @Mathias Kollros -113a R Ineffable Blessing @Milivoj Ćeran $A -113b R Ineffable Blessing @Milivoj Ćeran $B -113c R Ineffable Blessing @Milivoj Ćeran $C -113d R Ineffable Blessing @Milivoj Ćeran $D -113e R Ineffable Blessing @Milivoj Ćeran $E -113f R Ineffable Blessing @Milivoj Ćeran $F -114 C Joyride Rigger @Wayne Reynolds -115 U Monkey- @Andrea Radeck -116 C Mother Kangaroo @Andrea Radeck -117 C Multi-Headed @YW Tang -118 C Really Epic Punch @Ben Wootten -119 C Selfie Preservation @Chris Seaman -120 R Serpentine @Kari Christensen -121 U Shellephant @Hector Ortiz -122 U Slaying Mantis @Ben Wootten -123 C Squirrel Dealer @Bram Sels -124 U Steamflogger Service Rep @Warren Mahy -125 C Wild Crocodile @Brynn Metheney -126 C Willing Test Subject @Dmitry Burmak -127 M Baron Von Count @Jesper Ejsing -128 R Better Than One @Alex Konstad -129 R Cramped Bunker @Ben Wootten -130 M Dr. Julius Jumblemorph @Simon Dominic -131 M The Grand Calcutron @Sean Murray -132 R Grusilda, Monster Masher @Mathias Kollros -133 R Hot Fix @David Sladek -134 M Ol' Buzzbark @Wayne Reynolds -135 M Phoebe, Head of S.N.E.A.K. @Ralph Horsley -136 M Urza, Academy Headmaster @Terese Nielsen -137 R X @Dmitry Burmak -138 R Mary O'Kill @Simon Dominic -139 R Angelic Rocket @Carl Critchlow -140 U Border Guardian @Chris Seaman -141 U Buzzing Whack-a-Doodle @Mark A. Nelson -142 U Clock of DOOOOOOOOOOOOM! @Tom Babbey -143 U Cogmentor @Matt Gaser -144 U Contraption Cannon @Mike Burns -145a C Curious Killbot @Alex Konstad -145b C Delighted Killbot @Alex Konstad -145c C Despondent Killbot @Alex Konstad -145d C Enraged Killbot @Alex Konstad -146 U Entirely Normal Armchair @Tom Babbey -147a R Everythingamajig @Chris Seaman $A -147b R Everythingamajig @Chris Seaman $B -147c R Everythingamajig @Chris Seaman $C -147d R Everythingamajig @Chris Seaman $D -147e R Everythingamajig @Chris Seaman $E -147f R Everythingamajig @Chris Seaman $F -148 C Gnome-Made Engine @Sean Murray -149 R Handy Dandy Clone Machine @Mike Burns -150 R Kindslaver @Zoltan Boros -151 U Krark's Other Thumb @Jeff Miracola -152 U Labro Bot @Carl Critchlow -153 U Lobe Lobber @Dmitry Burmak -154 C Mad Science Fair Project @Carl Frank -155 R Modular Monstrosity @Alex Konstad -156 U Proper Laboratory Attire @Tom Babbey -157 U Robo- @Matt Dixon -158 R Split Screen @Simon Dominic -159 U Staff of the Letter Magus @Daniel Ljunggren -160 U Stamp of Approval @Zoltan Boros -161 U Steam-Powered @Carl Critchlow -162 U Steel Squirrel @Carl Critchlow -163 M Sword of Dungeons & Dragons @Chris Rahn -164 C Voracious Vacuum @Matt Dixon -165a C Secret Base @John Thacker -165b C Secret Base @Matt Gaser -165c C Secret Base @Seb McKinnon -165d C Secret Base @Dave Allsop -165e C Secret Base @Simon Dominic -166 R Watermarket @Simon Dominic -167 U Accessories to Murder @Ralph Horsley -168 C Applied Aeronautics @Jason Felix -169 U Arms Depot @Chuck Lukacs -170 C Auto-Key @Jason Felix -171 M Bee-Bee Gun @Chuck Lukacs -172 C Boomflinger @Steve Prescott -173 C Buzz Buggy @Steve Prescott -174 R Deadly Poison Sampler @Ralph Horsley -175 C Dictation Quillograph @Ralph Horsley -176 U Dispatch Dispensary @Ralph Horsley -177 C Division Table @Franz Vohwinkel -178 U Dogsnail Engine @Chuck Lukacs -179 R Dual Doomsuits @Franz Vohwinkel -180 R Duplication Device @Jason Felix -181 M Faerie Aerie @Ralph Horsley -182 U Genetic Recombinator @Chuck Lukacs -183 R Gift Horse @Steve Prescott -184 U Gnomeball Machine @Jason Felix -185 R Goblin Slingshot @Steve Prescott -186 R Guest List @Franz Vohwinkel -187 M Hard Hat Area @Steve Prescott -188 C Head Banger @Steve Prescott -189 R Hypnotic Swirly Disc @Ralph Horsley -190 C Inflation Station @Chuck Lukacs -191 U Insufferable Syphon @Ralph Horsley -192 U Jamming Device @Franz Vohwinkel -193 C Lackey Recycler @Franz Vohwinkel -194 C Mandatory Friendship Shackles @Franz Vohwinkel -195 U Neural Network @Franz Vohwinkel -196 R Oaken Power Suit @Chuck Lukacs -197 U Optical Optimizer @Jason Felix -198 M Pet Project @Franz Vohwinkel -199 C Quick-Stick Lick Trick @Chuck Lukacs -200 M Rapid Prototyper @Jason Felix -201 R Record Store @Jason Felix -202 R Refibrillator @Chuck Lukacs -203 C Sap Sucker @Chuck Lukacs -204 U Sundering Fork @Franz Vohwinkel -205 U Targeting Rocket @Steve Prescott -206 U Thud-for-Duds @Steve Prescott -207 C Top-Secret Tunnel @Ralph Horsley -208 C Tread Mill @Jason Felix -209 U Turbo-Thwacking Auto-Hammer @Steve Prescott -210 C Twiddlestick Charger @Ralph Horsley -211 U Widget Contraption @Jason Felix -212 L Plains @John Avon -213 L Island @John Avon -214 L Swamp @John Avon -215 L Mountain @John Avon -216 L Forest @John Avon +3a C Amateur Auteur +3b C Amateur Auteur +3c C Amateur Auteur +3d C Amateur Auteur +6 M Do-It-Yourself Seraph +7 U Gimme Five +8 C GO TO JAIL +9 U Half-Kitten, Half- +10 C Humming- +11 R Jackknight +12a U Knight of the Kitchen Sink $A +12b U Knight of the Kitchen Sink $B +12c U Knight of the Kitchen Sink $C +12d U Knight of the Kitchen Sink $D +12e U Knight of the Kitchen Sink $E +12f U Knight of the Kitchen Sink $F +13 U Knight of the Widget +14 U Midlife Upgrade +15 R Oddly Uneven +16 C Old Guard +17 C Ordinary Pony +18 U Rhino- +19 C Riveting Rigger +20 R Rules Lawyer +21 C Sacrifice Play +22 C Shaggy Camel +23 U Side Quest +24 C Success! +25 U Teacher's Pet +26 R Animate Library +27 C Blurry Beeble +28 C Chipper Chopper +29 R Clocknapper +30 C Crafty Octopus +31 U Crow Storm +32 C Defective Detective +33 U Five-Finger Discount +34 R Graveyard Busybody +35 U Half-Shark, Half- +36 R Incite Insight +37 U Kindly Cognician +38 C Magic Word +39 C Mer Man +40 U More or Less +41a C Novellamental +41b C Novellamental +41c C Novellamental +41d C Novellamental +42 C Numbing Jellyfish +43 U S.N.E.A.K. Dispatcher +44 U Socketed Sprocketer +45 C Spell Suck +46 U Spy Eye +47 U Suspicious Nanny +48 C Time Out +49a R Very Cryptic Command $A +49b R Very Cryptic Command $B +49c R Very Cryptic Command $C +49d R Very Cryptic Command $D +49e R Very Cryptic Command $E +49f R Very Cryptic Command $F +50 C Wall of Fortune +51 C Big Boa Constrictor +52 C capital offense +53 C Dirty Rat +54a C Extremely Slow Zombie +54b C Extremely Slow Zombie +54c C Extremely Slow Zombie +54d C Extremely Slow Zombie +55 C Finders, Keepers +56 R Hangman +57 C Hazmat Suit (Used) +58 C Hoisted Hireling +59 U Inhumaniac +60 R Masterful Ninja +61 U Ninja +62 U Old-Fashioned Vampire +63 R Over My Dead Bodies +64 U Overt Operative +65 U "Rumors of My Death..." +66 U Skull Saucer +67a U Sly Spy $A +67b U Sly Spy $B +67c U Sly Spy $C +67d U Sly Spy $D +67e U Sly Spy $E +67f U Sly Spy $F +68 C Snickering Squirrel +69 R Spike, Tournament Grinder +70 U Squirrel-Powered Scheme +71 C Steady-Handed Mook +72 C Stinging Scorpion +73 C Subcontract +74 M Summon the Pack +75 U Zombified +76 R The Big Idea +77 C Box of Free-Range Goblins +78 C Bumbling Pangolin +79 C Common Iguana +80 R The Countdown Is at One +81 C Feisty Stegosaurus +82a U Garbage Elemental $A +82b U Garbage Elemental $B +82c U Garbage Elemental $C +82d U Garbage Elemental $D +82e U Garbage Elemental $E +82f U Garbage Elemental $F +83 U Goblin Haberdasher +84 U Half-Orc, Half- +85 C Hammer Helper +86 U Hammer Jammer +87 U Hammerfest Boomtacular +88 M Infinity Elemental +89 C It That Gets Left Hanging +90 C Just Desserts +91 C Painiac +92 U Party Crasher +93 R Steamflogger Boss +94 R Steamflogger of the Month +95 U Steamflogger Temp +96 U Steamfloggery +97 U Super-Duper Death Ray +98a C Target Minotaur +98b C Target Minotaur +98c C Target Minotaur +98d C Target Minotaur +99 R Three-Headed Goblin +100 C Work a Double +101 C Wrench-Rigger +102 R As Luck Would Have It +103a C Beast in Show +103b C Beast in Show +103c C Beast in Show +103d C Beast in Show +104 U Chittering Doom +105 U Clever Combo +106 U Druid of the Sacred Beaker +107 C Eager Beaver +108 R Earl of Squirrel +109 U First Pick +110 U Ground Pounder +111 U Half-Squirrel, Half- +112 R Hydradoodle +113a R Ineffable Blessing $A +113b R Ineffable Blessing $B +113c R Ineffable Blessing $C +113d R Ineffable Blessing $D +113e R Ineffable Blessing $E +113f R Ineffable Blessing $F +114 C Joyride Rigger +115 U Monkey- +116 C Mother Kangaroo +117 C Multi-Headed +118 C Really Epic Punch +119 C Selfie Preservation +120 R Serpentine +121 U Shellephant +122 U Slaying Mantis +123 C Squirrel Dealer +124 U Steamflogger Service Rep +125 C Wild Crocodile +126 C Willing Test Subject +127 M Baron Von Count +128 R Better Than One +129 R Cramped Bunker +130 M Dr. Julius Jumblemorph +131 M The Grand Calcutron +132 R Grusilda, Monster Masher +133 R Hot Fix +134 M Ol' Buzzbark +135 M Phoebe, Head of S.N.E.A.K. +136 M Urza, Academy Headmaster +137 R X +138 R Mary O'Kill +139 R Angelic Rocket +140 U Border Guardian +141 U Buzzing Whack-a-Doodle +142 U Clock of DOOOOOOOOOOOOM! +143 U Cogmentor +144 U Contraption Cannon +145a C Curious Killbot +145b C Delighted Killbot +145c C Despondent Killbot +145d C Enraged Killbot +146 U Entirely Normal Armchair +147a R Everythingamajig $A +147b R Everythingamajig $B +147c R Everythingamajig $C +147d R Everythingamajig $D +147e R Everythingamajig $E +147f R Everythingamajig $F +148 C Gnome-Made Engine +149 R Handy Dandy Clone Machine +150 R Kindslaver +151 U Krark's Other Thumb +152 U Labro Bot +153 U Lobe Lobber +154 C Mad Science Fair Project +155 R Modular Monstrosity +156 U Proper Laboratory Attire +157 U Robo- +158 R Split Screen +159 U Staff of the Letter Magus +160 U Stamp of Approval +161 U Steam-Powered +162 U Steel Squirrel +163 M Sword of Dungeons & Dragons +164 C Voracious Vacuum +165a C Secret Base +165b C Secret Base +165c C Secret Base +165d C Secret Base +165e C Secret Base +166 R Watermarket +167 U Accessories to Murder +168 C Applied Aeronautics +169 U Arms Depot +170 C Auto-Key +171 M Bee-Bee Gun +172 C Boomflinger +173 C Buzz Buggy +174 R Deadly Poison Sampler +175 C Dictation Quillograph +176 U Dispatch Dispensary +177 C Division Table +178 U Dogsnail Engine +179 R Dual Doomsuits +180 R Duplication Device +181 M Faerie Aerie +182 U Genetic Recombinator +183 R Gift Horse +184 U Gnomeball Machine +185 R Goblin Slingshot +186 R Guest List +187 M Hard Hat Area +188 C Head Banger +189 R Hypnotic Swirly Disc +190 C Inflation Station +191 U Insufferable Syphon +192 U Jamming Device +193 C Lackey Recycler +194 C Mandatory Friendship Shackles +195 U Neural Network +196 R Oaken Power Suit +197 U Optical Optimizer +198 M Pet Project +199 C Quick-Stick Lick Trick +200 M Rapid Prototyper +201 R Record Store +202 R Refibrillator +203 C Sap Sucker +204 U Sundering Fork +205 U Targeting Rocket +206 U Thud-for-Duds +207 C Top-Secret Tunnel +208 C Tread Mill +209 U Turbo-Thwacking Auto-Hammer +210 C Twiddlestick Charger +211 U Widget Contraption +212 L Plains +213 L Island +214 L Swamp +215 L Mountain +216 L Forest [tokens] storm_crow diff --git a/forge-gui/res/editions/War of the Spark.txt b/forge-gui/res/editions/War of the Spark.txt index bf244121200..98d64a05dad 100644 --- a/forge-gui/res/editions/War of the Spark.txt +++ b/forge-gui/res/editions/War of the Spark.txt @@ -5,7 +5,7 @@ Name=War of the Spark Code2=WAR Type=Expansion BoosterCovers=3 -Booster=10 Common:fromSheet("WAR cards"), 3 Uncommon:fromSheet("WAR cards"), 1 RareMythic:fromSheet("WAR cards"), 1 BasicLand +Booster=10 Common:fromSheet("WAR cards"):!fromSheet("WAR Secret Cards"), 3 Uncommon:fromSheet("WAR cards"):!fromSheet("WAR Secret Cards"), 1 RareMythic:fromSheet("WAR cards"):!fromSheet("WAR Secret Cards"), 1 BasicLand BoosterMustContain=Planeswalker FatPack=10 FatPackExtraSlots=80 BasicLands @@ -14,7 +14,9 @@ ScryfallCode=WAR [cards] 1 R Karn, the Great Creator @Wisnu Tan +1★ R Karn, the Great Creator @Naochika Morishita 2 R Ugin, the Ineffable @Daarken +2★ R Ugin, the Ineffable @Maekawa Yuichi 3 U Ugin's Conjurant @Ryan Yee 4 U Ajani's Pridemate @Sidharth Chaturvedi 5 C Battlefield Promotion @Scott Murphy @@ -26,6 +28,7 @@ ScryfallCode=WAR 11 C Enforcer Griffin @Johan Grenier 12 M Finale of Glory @Stanton Feng 13 M Gideon Blackblade @Viktor Titov +13★ M Gideon Blackblade @Tada 14 C Gideon's Sacrifice @Chris Rallis 15 U Gideon's Triumph @Kieran Yanner 16 M God-Eternal Oketra @Grzegorz Rutkowski @@ -45,11 +48,13 @@ ScryfallCode=WAR 30 R Single Combat @Livia Prima 31 U Sunblade Angel @Johannes Voss 32 U Teyo, the Shieldmage @Magali Villeneuve +32★ U Teyo, the Shieldmage @Foo Midori 33 C Teyo's Lightshield @Igor Kieryluk 34 R Tomik, Distinguished Advokist @Johannes Voss 35 C Topple the Statue @Sidharth Chaturvedi 36 C Trusted Pegasus @Chris Rahn 37 U The Wanderer @Wesley Burt +37★ U The Wanderer @Norikatsu Miyoshi 38 C Wanderer's Strike @Sara Winters 39 C War Screecher @Dan Murayama Scott 40 C Ashiok's Skulker @Livia Prima @@ -67,13 +72,16 @@ ScryfallCode=WAR 52 U Flux Channeler @Heonhwa Choe 53 M God-Eternal Kefnet @Lius Lasahido 54 R Jace, Wielder of Mysteries @Anna Steinbauer +54★ R Jace, Wielder of Mysteries @Toshiaki Takayama 55 U Jace's Triumph @Kieran Yanner 56 U Kasmina, Enigmatic Mentor @Magali Villeneuve +56★ U Kasmina, Enigmatic Mentor @Mid 57 C Kasmina's Transmutation @Uriah Voth 58 C Kiora's Dambreaker @Mathias Kollros 59 U Lazotep Plating @Yeong-Hao Han 60 C Naga Eternal @Johann Bodin 61 U Narset, Parter of Veils @Magali Villeneuve +61★ U Narset, Parter of Veils @Foo Midori 62 R Narset's Reversal @Viktor Titov 63 C No Escape @G-host Lee 64 C Relentless Advance @Stanton Feng @@ -96,6 +104,7 @@ ScryfallCode=WAR 81 C Charity Extractor @Matt Stewart 82 R Command the Dreadhorde @Daarken 83 U Davriel, Rogue Shadowmage @Daarken +83★ U Davriel, Rogue Shadowmage @Hagiya Kaoru 84 C Davriel's Shadowfugue @Daarken 85 R Deliver Unto Evil @Seb McKinnon 86 R Dreadhorde Invasion @Stanton Feng @@ -110,9 +119,11 @@ ScryfallCode=WAR 95 C Lazotep Behemoth @Zezhou Chen 96 C Lazotep Reaver @Craig J Spearing 97 M Liliana, Dreadhorde General @Chris Rallis +97★ M Liliana, Dreadhorde General @Yoshitaka Amano 98 U Liliana's Triumph @Kieran Yanner 99 R Massacre Girl @Chris Rallis 100 U Ob Nixilis, the Hate-Twisted @Yongjae Choi +100★ U Ob Nixilis, the Hate-Twisted @Sansyu 101 C Ob Nixilis's Cruelty @Igor Kieryluk 102 U Price of Betrayal @Ryan Yee 103 C Shriekdiver @Piotr Dura @@ -132,6 +143,7 @@ ScryfallCode=WAR 117 C Burning Prophet @Mathias Kollros 118 C Chainwhip Cyclops @Johann Bodin 119 R Chandra, Fire Artisan @Yongjae Choi +119★ R Chandra, Fire Artisan @Ryota-H 120 C Chandra's Pyrohelix @Aleksi Briclot 121 U Chandra's Triumph @Kieran Yanner 122 U Cyclops Electromancer @Jason Felix @@ -148,6 +160,7 @@ ScryfallCode=WAR 133 M Ilharg, the Raze-Boar @Filip Burburan 134 C Invading Manticore @Jehan Choo 135 U Jaya, Venerated Firemage @Yongjae Choi +135★ U Jaya, Venerated Firemage @Maekawa Yuichi 136 C Jaya's Greeting @Victor Adame Minguez 137 R Krenko, Tin Street Kingpin @Mark Behm 138 R Mizzium Tank @Wayne Reynolds @@ -156,13 +169,16 @@ ScryfallCode=WAR 141 C Raging Kronch @Steve Prescott 142 C Samut's Sprint @Aleksi Briclot 143 R Sarkhan the Masterless @Kieran Yanner +143★ R Sarkhan the Masterless @Lack 144 C Sarkhan's Catharsis @Zack Stella 145 C Spellgorger Weird @James Paick 146 U Tibalt, Rakish Instigator @Chase Stone +146★ U Tibalt, Rakish Instigator @Clover.K 147 U Tibalt's Rager @Yongjae Choi 148 C Turret Ogre @Johann Bodin 149 C Arboreal Grazer @Jason Rainville 150 U Arlinn, Voice of the Pack @Ryan Pancoast +150★ U Arlinn, Voice of the Pack @D-suzuki 151 C Arlinn's Wolf @Kimonas Theodossiou 152 R Awakening of Vitu-Ghazi @Jaime Jones 153 C Band Together @Josh Hass @@ -177,11 +193,13 @@ ScryfallCode=WAR 162 C Giant Growth @Dmitry Burmak 163 M God-Eternal Rhonas @Lius Lasahido 164 U Jiang Yanggu, Wildcrafter @Anna Steinbauer +164★ U Jiang Yanggu, Wildcrafter @Daisuke Izuka 165 C Kraul Stinger @Randy Vargas 166 C Kronch Wrangler @Steve Prescott 167 U Mowu, Loyal Companion @Kimonas Theodossiou 168 C New Horizons @Eytan Zana 169 R Nissa, Who Shakes the World @Chris Rallis +169★ R Nissa, Who Shakes the World @Hitowa 170 U Nissa's Triumph @Kieran Yanner 171 U Paradise Druid @Nils Hamm 172 R Planewide Celebration @Wisnu Tan @@ -193,10 +211,12 @@ ScryfallCode=WAR 178 U Storm the Citadel @Grzegorz Rutkowski 179 C Thundering Ceratok @Izzy 180 R Vivien, Champion of the Wilds @Magali Villeneuve +180★ R Vivien, Champion of the Wilds @Hisashi Momose 181 R Vivien's Arkbow @Zack Stella 182 C Vivien's Grizzly @Lius Lasahido 183 C Wardscale Crocodile @Zezhou Chen 184 R Ajani, the Greathearted @Victor Adame Minguez +184★ R Ajani, the Greathearted @Miho Midorikawa 185 U Angrath's Rampage @Victor Adame Minguez 186 R Bioessence Hydra @Mathias Kollros 187 R Casualties of War @Tomasz Jedruszek @@ -204,6 +224,7 @@ ScryfallCode=WAR 189 U Deathsprout @Seb McKinnon 190 U Despark @Slawomir Maniak 191 R Domri, Anarch of Bolas @Raymond Swanland +191★ R Domri, Anarch of Bolas @Raita Kazama 192 U Domri's Ambush @Victor Adame Minguez 193 U Dovin's Veto @Izzy 194 R Dreadhorde Butcher @Piotr Dura @@ -220,35 +241,50 @@ ScryfallCode=WAR 205 U Merfolk Skydiver @Sara Winters 206 U Neoform @Bram Sels 207 M Nicol Bolas, Dragon-God @Raymond Swanland +207★ M Nicol Bolas, Dragon-God @Kaida Yuji 208 M Niv-Mizzet Reborn @Raymond Swanland 209 R Oath of Kaya @Wesley Burt 210 U Pledge of Unity @Chris Rallis 211 R Ral, Storm Conduit @Wesley Burt +211★ R Ral, Storm Conduit @Naochika Morishita 212 U Ral's Outburst @Joseph Meehan 213 M Roalesk, Apex Hybrid @Svetlin Velinov 214 R Role Reversal @Mathias Kollros 215 U Rubblebelt Rioters @Tomasz Jedruszek 216 R Solar Blaze @Adam Paquette 217 R Sorin, Vengeful Bloodlord @Tommy Arnold +217★ R Sorin, Vengeful Bloodlord @Yukie Tajima 218 R Soul Diviner @Randy Vargas 219 R Storrev, Devkarin Lich @Igor Kieryluk 220 R Tamiyo, Collector of Tales @Chase Stone +220★ R Tamiyo, Collector of Tales @Fuzichoco 221 R Teferi, Time Raveler @Chris Rallis +221★ R Teferi, Time Raveler @Shishizaru 222 U Tenth District Legionnaire @Victor Adame Minguez 223 R Time Wipe @Svetlin Velinov 224 R Tolsimir, Friend to Wolves @Ryan Pancoast 225 U Tyrant's Scorn @Svetlin Velinov 226 R Widespread Brutality @Victor Adame Minguez 227 U Angrath, Captain of Chaos @Slawomir Maniak +227★ U Angrath, Captain of Chaos @Sansyu 228 U Ashiok, Dream Render @Cynthia Sheppard +228★ U Ashiok, Dream Render @Hozan Shinomaru 229 U Dovin, Hand of Control @Kieran Yanner +229★ U Dovin, Hand of Control @Nablange 230 U Huatli, the Sun's Heart @Lius Lasahido +230★ U Huatli, the Sun's Heart @Mikio Masuda 231 U Kaya, Bane of the Dead @Magali Villeneuve +231★ U Kaya, Bane of the Dead @Mid 232 U Kiora, Behemoth Beckoner @Jaime Jones +232★ U Kiora, Behemoth Beckoner @Yamamoto Akifumi 233 U Nahiri, Storm of Stone @Aleksi Briclot +233★ U Nahiri, Storm of Stone @Yukie Tajima 234 U Saheeli, Sublime Artificer @Wesley Burt +234★ U Saheeli, Sublime Artificer @Hisashi Momose 235 U Samut, Tyrant Smasher @Aleksi Briclot +235★ U Samut, Tyrant Smasher @Norikatsu Miyoshi 236 U Vraska, Swarm's Eminence @Anna Steinbauer +236★ U Vraska, Swarm's Eminence @Ryota Murayama 237 U Firemind Vessel @Ravenna Tran 238 U God-Pharaoh's Statue @Igor Kieryluk 239 C Guild Globe @Daniel Ljunggren @@ -277,46 +313,6 @@ ScryfallCode=WAR 262 L Forest @Jonas De Ro 263 L Forest @Titus Lunter 264 L Forest @Richard Wright - -[promo] -1★ R Karn, the Great Creator @Naochika Morishita -2★ R Ugin, the Ineffable @Maekawa Yuichi -13★ M Gideon Blackblade @Tada -32★ U Teyo, the Shieldmage @Foo Midori -37★ U The Wanderer @Norikatsu Miyoshi -54★ R Jace, Wielder of Mysteries @Toshiaki Takayama -56★ U Kasmina, Enigmatic Mentor @Mid -61★ U Narset, Parter of Veils @Foo Midori -83★ U Davriel, Rogue Shadowmage @Hagiya Kaoru -97★ M Liliana, Dreadhorde General @Yoshitaka Amano -100★ U Ob Nixilis, the Hate-Twisted @Sansyu -119★ R Chandra, Fire Artisan @Ryota-H -135★ U Jaya, Venerated Firemage @Maekawa Yuichi -143★ R Sarkhan the Masterless @Lack -146★ U Tibalt, Rakish Instigator @Clover.K -150★ U Arlinn, Voice of the Pack @D-suzuki -164★ U Jiang Yanggu, Wildcrafter @Daisuke Izuka -169★ R Nissa, Who Shakes the World @Hitowa -180★ R Vivien, Champion of the Wilds @Hisashi Momose -184★ R Ajani, the Greathearted @Miho Midorikawa -191★ R Domri, Anarch of Bolas @Raita Kazama -207★ M Nicol Bolas, Dragon-God @Kaida Yuji -211★ R Ral, Storm Conduit @Naochika Morishita -217★ R Sorin, Vengeful Bloodlord @Yukie Tajima -220★ R Tamiyo, Collector of Tales @Fuzichoco -221★ R Teferi, Time Raveler @Shishizaru -227★ U Angrath, Captain of Chaos @Sansyu -228★ U Ashiok, Dream Render @Hozan Shinomaru -229★ U Dovin, Hand of Control @Nablange -230★ U Huatli, the Sun's Heart @Mikio Masuda -231★ U Kaya, Bane of the Dead @Mid -232★ U Kiora, Behemoth Beckoner @Yamamoto Akifumi -233★ U Nahiri, Storm of Stone @Yukie Tajima -234★ U Saheeli, Sublime Artificer @Hisashi Momose -235★ U Samut, Tyrant Smasher @Norikatsu Miyoshi -236★ U Vraska, Swarm's Eminence @Ryota Murayama - -[precon product] 265 M Gideon, the Oathsworn @Kieran Yanner 266 C Desperate Lunge @Deruchenko Alexander 267 R Gideon's Battle Cry @Zoltan Boros @@ -327,8 +323,6 @@ ScryfallCode=WAR 272 U Jace's Projection @Darek Zabrocki 273 R Jace's Ruse @Clint Cearley 274 C Simic Guildgate @Adam Paquette - -[buy a box] 275 M Tezzeret, Master of the Bridge @Chase Stone [rebalanced] @@ -353,5 +347,3 @@ voja_friend_to_elves w_0_3_wall_defender w_2_2_soldier_vigilance w_4_4_angel_flying_vigilance - - \ No newline at end of file diff --git a/forge-gui/res/editions/Zendikar Expeditions.txt b/forge-gui/res/editions/Zendikar Expeditions.txt index 4b5b26925e2..b7da0cd08f6 100644 --- a/forge-gui/res/editions/Zendikar Expeditions.txt +++ b/forge-gui/res/editions/Zendikar Expeditions.txt @@ -5,7 +5,6 @@ Name=Zendikar Expeditions Type=Collector_Edition ScryfallCode=EXP -#Bonus cards for BFZ Battle for Zendikar [cards] 1 M Prairie Stream @Titus Lunter 2 M Sunken Hollow @Titus Lunter @@ -32,9 +31,6 @@ ScryfallCode=EXP 23 M Verdant Catacombs @Ryan Yee 24 M Arid Mesa @Ryan Yee 25 M Misty Rainforest @Ryan Yee - -#Bonus cards for OGW Oath of the Gatewatch -[special slot] 26 M Mystic Gate @Adam Paquette 27 M Sunken Ruins @Adam Paquette 28 M Graven Cairns @Adam Paquette diff --git a/forge-gui/res/editions/Zendikar Rising.txt b/forge-gui/res/editions/Zendikar Rising.txt index a65ba6ef048..3f2c5e882b9 100644 --- a/forge-gui/res/editions/Zendikar Rising.txt +++ b/forge-gui/res/editions/Zendikar Rising.txt @@ -399,8 +399,6 @@ ScryfallCode=ZNR 377 R Skyclave Relic @Daniel Ljunggren 378 R Crawling Barrens @Jonas De Ro 379 R Throne of Makindi @Igor Kieryluk - -[precon product] 380 L Plains @Adam Paquette 381 L Island @Tianhua X 382 L Swamp @Adam Paquette From 78ad99ae6e45186abac4b4b19301cab393497c1f Mon Sep 17 00:00:00 2001 From: Chris H Date: Sun, 24 Nov 2024 11:01:51 -0500 Subject: [PATCH 106/152] Update snapshot automation (#6624) * Update snapshots-pc.yml * Update snapshots-android.yml * Update snapshots-pc.yml --- .github/workflows/snapshots-android.yml | 7 +++++++ .github/workflows/snapshots-pc.yml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/.github/workflows/snapshots-android.yml b/.github/workflows/snapshots-android.yml index 793f4cddd7f..6e19c623045 100644 --- a/.github/workflows/snapshots-android.yml +++ b/.github/workflows/snapshots-android.yml @@ -109,3 +109,10 @@ jobs: local-dir: upload/ server-dir: downloads/dailysnapshots/ state-name: .ftp-deploy-android-sync-state.json + + - name: Send failure notification to Discord + if: failure() # This step runs only if the job fails + run: | + curl -X POST -H "Content-Type: application/json" \ + -d "{\"content\": \"🔴 Android Snapshot Build Failed in branch: \`${{ github.ref_name }}\` by \`${{ github.actor }}\`.\nCheck logs: ${{ github.run_url }}\"}" \ + ${{ secrets.DISCORD_AUTOMATION_WEBHOOK }} diff --git a/.github/workflows/snapshots-pc.yml b/.github/workflows/snapshots-pc.yml index 0dc50ecd525..c65f1f6fa93 100644 --- a/.github/workflows/snapshots-pc.yml +++ b/.github/workflows/snapshots-pc.yml @@ -89,3 +89,10 @@ jobs: *.pom *.repositories *.xml + + - name: Send failure notification to Discord + if: failure() # This step runs only if the job fails + run: | + curl -X POST -H "Content-Type: application/json" \ + -d "{\"content\": \"🔴 Desktop Snapshot Build Failed in branch: \`${{ github.ref_name }}\` by \`${{ github.actor }}\`.\nCheck logs: ${{ github.run_url }}\"}" \ + ${{ secrets.DISCORD_AUTOMATION_WEBHOOK }} From 3e5a728646b7bd55aa7c37e12fc8ebdd2423d97b Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 24 Nov 2024 19:43:37 +0100 Subject: [PATCH 107/152] Miracle: fix index --- forge-game/src/main/java/forge/game/card/CardFactoryUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index b816fa39bc5..ff1a8c7ea8a 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -1524,7 +1524,7 @@ public class CardFactoryUtil { final String abStrReveal = "DB$ Reveal | Defined$ You | RevealDefined$ Self" + " | MiracleCost$ " + manacost; String abStrPlay = "DB$ Play | Defined$ Self | Optional$ True | PlayCost$ " + manacost; - if (k.length >= 2) { + if (k.length > 2) { abStrPlay += " | PlayReduceCost$ " + k[2]; } From 8c277983e5a4951425657f3e9c70bdc6db7ccd59 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Mon, 25 Nov 2024 08:34:23 +0000 Subject: [PATCH 108/152] Update reality_acid.txt --- forge-gui/res/cardsfolder/r/reality_acid.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/r/reality_acid.txt b/forge-gui/res/cardsfolder/r/reality_acid.txt index a8a4c48e0d4..ad04b2296e3 100644 --- a/forge-gui/res/cardsfolder/r/reality_acid.txt +++ b/forge-gui/res/cardsfolder/r/reality_acid.txt @@ -5,5 +5,5 @@ K:Enchant permanent K:Vanishing:3 A:SP$ Attach | Cost$ 2 U | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | AILogic$ Curse T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigSac | TriggerDescription$ When CARDNAME leaves the battlefield, enchanted permanent's controller sacrifices it. -SVar:TrigSac:DB$ Destroy | Sacrifice$ True | Defined$ AttachedBy TriggeredCardLKICopy +SVar:TrigSac:DB$ SacrificeAll | Defined$ AttachedBy TriggeredCardLKICopy Oracle:Enchant permanent\nVanishing 3 (This Aura enters with three time counters on it. At the beginning of your upkeep, remove a time counter from it. When the last is removed, sacrifice it.)\nWhen Reality Acid leaves the battlefield, enchanted permanent's controller sacrifices it. From b56e5e20297bcbcb95e0224c71e7b69de28bd364 Mon Sep 17 00:00:00 2001 From: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:53:50 +0000 Subject: [PATCH 109/152] Fix card name in DA1 edition --- forge-gui/res/editions/Unknown Event.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/editions/Unknown Event.txt b/forge-gui/res/editions/Unknown Event.txt index a9201958fe5..6ffad764162 100644 --- a/forge-gui/res/editions/Unknown Event.txt +++ b/forge-gui/res/editions/Unknown Event.txt @@ -318,7 +318,7 @@ CW12 C Taught by Serra @ RA12a R Guildmark @ RA12b R Trampled Lotus @ RG12a R HONK! @ -RG12b R Clean, Fair Magic @ +RG12b R Clear, Fair Magic @ RU12 R Untap, Upkeep, Draw @ RZ12 R Eldest Dragon Highlander @ RZ12b R Zimone's Homework @ From 87a16ea58ca2fe5eff9e63cb9ca9b00c5531fb88 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sat, 16 Nov 2024 16:30:43 +0100 Subject: [PATCH 110/152] Card: copy getChangedCardTypes --- forge-game/src/main/java/forge/game/card/Card.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index e2c6744ed08..9c0468daf8c 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -4178,12 +4178,12 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr return ImmutableList.of(); } Iterable byText = changedTypeByText == null ? ImmutableList.of() : ImmutableList.of(this.changedTypeByText); - return Iterables.unmodifiableIterable(Iterables.concat( + return ImmutableList.copyOf(Iterables.concat( changedCardTypesByText.values(), // Layer 3 byText, // Layer 3 by Word Changes, changedCardTypesCharacterDefining.values(), // Layer 4 changedCardTypes.values() // Layer 6 - )); + )); } public Table getChangedCardTypesTable() { From 9170c64847bd12c66cf89990d4b7b8e3b5823eb1 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sat, 16 Nov 2024 16:34:48 +0100 Subject: [PATCH 111/152] use CardState.getTypeWithChanges directly --- forge-game/src/main/java/forge/game/card/Card.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 9c0468daf8c..25a6795c803 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -4153,14 +4153,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr } public final CardTypeView getType() { - return getType(currentState); - } - public final CardTypeView getType(CardState state) { - final Iterable changedCardTypes = getChangedCardTypes(); - if (Iterables.isEmpty(changedCardTypes)) { - return state.getType(); - } - return state.getType().getTypeWithChanges(changedCardTypes); + return currentState.getTypeWithChanges(); } public final CardTypeView getOriginalType() { From 3c21054f8e92033cebb545317155b289ba32c8ca Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 17 Nov 2024 21:14:30 +0100 Subject: [PATCH 112/152] Card: moved some getter/setter into a copyFrom --- .../java/forge/ai/simulation/GameCopier.java | 8 +-- .../src/main/java/forge/game/card/Card.java | 55 +++++-------------- .../java/forge/game/card/CardCopyService.java | 8 +-- 3 files changed, 17 insertions(+), 54 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java index 2629cf44d0b..7d288c8013e 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -358,13 +358,7 @@ public class GameCopier { newCard.setDamage(c.getDamage()); newCard.setDamageReceivedThisTurn(c.getDamageReceivedThisTurn()); - newCard.setChangedCardColors(c.getChangedCardColorsTable()); - newCard.setChangedCardColorsCharacterDefining(c.getChangedCardColorsCharacterDefiningTable()); - - newCard.setChangedCardTypes(c.getChangedCardTypesTable()); - newCard.setChangedCardTypesCharacterDefining(c.getChangedCardTypesCharacterDefiningTable()); - newCard.setChangedCardKeywords(c.getChangedCardKeywords()); - newCard.setChangedCardNames(c.getChangedCardNames()); + newCard.copyFrom(c); for (Table.Cell> kw : c.getHiddenExtrinsicKeywordsTable().cellSet()) { newCard.addHiddenExtrinsicKeywords(kw.getRowKey(), kw.getColumnKey(), kw.getValue()); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 25a6795c803..dc96a3526d0 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -989,15 +989,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr currentState.getView().updateName(currentState); } - public Table getChangedCardNames() { - return changedCardNames; - } - - public void setChangedCardNames(Table changedCardNames) { - this.changedCardNames.clear(); - this.changedCardNames.putAll(changedCardNames); - } - public void setGamePieceType(GamePieceType gamePieceType) { this.gamePieceType = gamePieceType; this.view.updateGamePieceType(this); @@ -4179,13 +4170,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr )); } - public Table getChangedCardTypesTable() { - return Tables.unmodifiableTable(changedCardTypes); - } - public Table getChangedCardTypesCharacterDefiningTable() { - return Tables.unmodifiableTable(changedCardTypesCharacterDefining); - } - public boolean clearChangedCardTypes() { boolean changed = false; @@ -4243,12 +4227,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr return changedCardKeywords; } - public Table getChangedCardColorsTable() { - return changedCardColors; - } - public Table getChangedCardColorsCharacterDefiningTable() { - return changedCardColorsCharacterDefining; - } public Iterable getChangedCardColors() { return Iterables.concat(changedCardColorsByText.values(), changedCardColorsCharacterDefining.values(), changedCardColors.values()); } @@ -7770,15 +7748,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr } } - public void setChangedCardTypes(Table changedCardTypes) { - this.changedCardTypes.clear(); - this.changedCardTypes.putAll(changedCardTypes); - } - public void setChangedCardTypesCharacterDefining(Table changedCardTypes) { - this.changedCardTypesCharacterDefining.clear(); - this.changedCardTypesCharacterDefining.putAll(changedCardTypes); - } - public void setChangedCardKeywords(Table changedCardKeywords) { this.changedCardKeywords.clear(); for (Table.Cell entry : changedCardKeywords.cellSet()) { @@ -7786,15 +7755,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr } } - public void setChangedCardColors(Table changedCardColors) { - this.changedCardColors.clear(); - this.changedCardColors.putAll(changedCardColors); - } - public void setChangedCardColorsCharacterDefining(Table changedCardColors) { - this.changedCardColorsCharacterDefining.clear(); - this.changedCardColorsCharacterDefining.putAll(changedCardColors); - } - public void cleanupCopiedChangesFrom(Card c) { for (StaticAbility stAb : c.getStaticAbilities()) { this.removeChangedCardTypes(c.getLayerTimestamp(), stAb.getId(), false); @@ -8269,4 +8229,19 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr } return unlockAbilities.get(state); } + + public void copyFrom(Card in) { + // clean is not needed? + this.changedCardColors.putAll(in.changedCardColors); + this.changedCardColorsCharacterDefining.putAll(in.changedCardColorsCharacterDefining); + + setChangedCardKeywords(in.getChangedCardKeywords()); + + this.changedCardTypes.putAll(in.changedCardTypes); + this.changedCardTypesCharacterDefining.putAll(in.changedCardTypesCharacterDefining); + + this.changedCardNames.putAll(in.changedCardNames); + setChangedCardTraits(in.getChangedCardTraits()); + + } } diff --git a/forge-game/src/main/java/forge/game/card/CardCopyService.java b/forge-game/src/main/java/forge/game/card/CardCopyService.java index 579da3ff7b6..15e085cc2ba 100644 --- a/forge-game/src/main/java/forge/game/card/CardCopyService.java +++ b/forge-game/src/main/java/forge/game/card/CardCopyService.java @@ -356,13 +356,7 @@ public class CardCopyService { newCopy.setUnearthed(copyFrom.isUnearthed()); - newCopy.setChangedCardColors(copyFrom.getChangedCardColorsTable()); - newCopy.setChangedCardColorsCharacterDefining(copyFrom.getChangedCardColorsCharacterDefiningTable()); - newCopy.setChangedCardKeywords(copyFrom.getChangedCardKeywords()); - newCopy.setChangedCardTypes(copyFrom.getChangedCardTypesTable()); - newCopy.setChangedCardTypesCharacterDefining(copyFrom.getChangedCardTypesCharacterDefiningTable()); - newCopy.setChangedCardNames(copyFrom.getChangedCardNames()); - newCopy.setChangedCardTraits(copyFrom.getChangedCardTraits()); + newCopy.copyFrom(copyFrom); // for getReplacementList (run after setChangedCardKeywords for caching) newCopy.setStoredKeywords(copyFrom.getStoredKeywords(), true); From 08bab22a23a452a447193ae6bbc6602d11380f2a Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Mon, 25 Nov 2024 20:45:05 +0800 Subject: [PATCH 113/152] refactor lazy initialization --- .../src/main/java/forge/ai/AiCardMemory.java | 18 +- forge-core/src/main/java/forge/util/Lazy.java | 46 +++ .../java/forge/util/collect/FCollection.java | 115 +++---- .../main/java/forge/game/combat/Combat.java | 310 ++++++----------- .../forge/game/spellability/SpellAbility.java | 315 ++++-------------- .../src/forge/assets/ImageCache.java | 49 +-- .../src/forge/itemmanager/ItemManager.java | 67 ++-- .../forge/itemmanager/views/ImageView.java | 178 ++++------ .../screens/match/views/VCardDisplayArea.java | 73 ++-- .../screens/match/views/VZoneDisplay.java | 42 +-- 10 files changed, 412 insertions(+), 801 deletions(-) create mode 100644 forge-core/src/main/java/forge/util/Lazy.java diff --git a/forge-ai/src/main/java/forge/ai/AiCardMemory.java b/forge-ai/src/main/java/forge/ai/AiCardMemory.java index 8d000584474..87bcc7f56a4 100644 --- a/forge-ai/src/main/java/forge/ai/AiCardMemory.java +++ b/forge-ai/src/main/java/forge/ai/AiCardMemory.java @@ -25,6 +25,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import forge.game.card.Card; import forge.game.player.Player; +import forge.util.Lazy; /** *

@@ -64,26 +65,13 @@ public class AiCardMemory { REVEALED_CARDS // These cards were recently revealed to the AI by a call to PlayerControllerAi.reveal } - private Map> _memoryMap; - private Map> memoryMap() { - Map> result = _memoryMap; - if (result == null) { - synchronized (this) { - result = _memoryMap; - if (result == null) { - result = Maps.newConcurrentMap(); - _memoryMap = result; - } - } - } - return _memoryMap; - } + private final Lazy>> memoryMap = Lazy.of(Maps::newConcurrentMap); public AiCardMemory() { } private Set getMemorySet(MemorySet set) { - return memoryMap().computeIfAbsent(set, value -> Sets.newConcurrentHashSet()); + return memoryMap.get().computeIfAbsent(set, value -> Sets.newConcurrentHashSet()); } /** diff --git a/forge-core/src/main/java/forge/util/Lazy.java b/forge-core/src/main/java/forge/util/Lazy.java new file mode 100644 index 00000000000..2bc57bb8d65 --- /dev/null +++ b/forge-core/src/main/java/forge/util/Lazy.java @@ -0,0 +1,46 @@ +package forge.util; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/*https://github.com/pivovarit/articles/tree/master/java-lazy-initialization*/ +public final class Lazy implements Supplier { + private transient Supplier supplier; + private volatile T value; + + public Lazy(Supplier supplier) { + this.supplier = Objects.requireNonNull(supplier); + } + + @Override + public T get() { + if (value == null) { + synchronized (this) { + if (value == null) { + value = Objects.requireNonNull(supplier.get()); + supplier = null; + } + } + } + return value; + } + + public Lazy map(Function mapper) { + return new Lazy<>(() -> mapper.apply(this.get())); + } + + public Lazy flatMap(Function> mapper) { + return new Lazy<>(() -> mapper.apply(this.get()).get()); + } + + public Lazy> filter(Predicate predicate) { + return new Lazy<>(() -> Optional.of(get()).filter(predicate)); + } + + public static Lazy of(Supplier supplier) { + return new Lazy<>(supplier); + } +} \ No newline at end of file diff --git a/forge-core/src/main/java/forge/util/collect/FCollection.java b/forge-core/src/main/java/forge/util/collect/FCollection.java index d3d5ce469e0..885beb74b1f 100644 --- a/forge-core/src/main/java/forge/util/collect/FCollection.java +++ b/forge-core/src/main/java/forge/util/collect/FCollection.java @@ -13,6 +13,7 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.function.Predicate; +import forge.util.Lazy; import org.apache.commons.lang3.ArrayUtils; import com.google.common.collect.ImmutableList; @@ -44,40 +45,12 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** * The {@link Set} representation of this collection. */ - private Set SET; - - private Set set() { - Set result = SET; - if (result == null) { - synchronized (lock) { - result = SET; - if (result == null) { - result = Sets.newHashSet(); - SET = result; - } - } - } - return SET; - } + private final Lazy> set = Lazy.of(Sets::newHashSet); /** * The {@link List} representation of this collection. */ - private LinkedList LIST; - - private LinkedList list() { - LinkedList result = LIST; - if (result == null) { - synchronized (lock) { - result = LIST; - if (result == null) { - result = Lists.newLinkedList(); - LIST = result; - } - } - } - return LIST; - } + private final Lazy> list = Lazy.of(Lists::newLinkedList); /** * Create an empty {@link FCollection}. @@ -163,7 +136,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public int hashCode() { - return list().hashCode(); + return list.get().hashCode(); } /** @@ -173,7 +146,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public String toString() { - return list().toString(); + return list.get().toString(); } /** @@ -183,7 +156,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public final FCollection clone() { synchronized (lock) { - return new FCollection<>(list()); + return new FCollection<>(list.get()); } } @@ -195,9 +168,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public T getFirst() { synchronized (lock) { - if (list().isEmpty()) + if (list.get().isEmpty()) return null; - return list().getFirst(); + return list.get().getFirst(); } } @@ -209,9 +182,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public T getLast() { synchronized (lock) { - if (list().isEmpty()) + if (list.get().isEmpty()) return null; - return list().getLast(); + return list.get().getLast(); } } @@ -221,7 +194,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public int size() { synchronized (lock) { - return set().size(); + return set.get().size(); } } @@ -230,11 +203,11 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ @Override public boolean isEmpty() { - return set().isEmpty(); + return set.get().isEmpty(); } public Set asSet() { - return set(); + return set.get(); } /** @@ -247,7 +220,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, synchronized (lock) { if (o == null) return false; - return set().contains(o); + return set.get().contains(o); } } @@ -257,7 +230,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public Iterator iterator() { return new Itr(); - //return list().iterator(); + //return list.get().iterator(); } /** @@ -266,7 +239,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public Object[] toArray() { synchronized (lock) { - return list().toArray(); + return list.get().toArray(); } } @@ -277,7 +250,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @SuppressWarnings("hiding") public T[] toArray(final T[] a) { synchronized (lock) { - return list().toArray(a); + return list.get().toArray(a); } } @@ -292,8 +265,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, synchronized (lock) { if (e == null) return false; - if (set().add(e)) { - list().add(e); + if (set.get().add(e)) { + list.get().add(e); return true; } return false; @@ -311,8 +284,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, synchronized (lock) { if (o == null) return false; - if (set().remove(o)) { - list().remove(o); + if (set.get().remove(o)) { + list.get().remove(o); return true; } return false; @@ -322,8 +295,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public boolean removeIf(Predicate filter) { synchronized (lock) { - if (list().removeIf(filter)) { - set().removeIf(filter); + if (list.get().removeIf(filter)) { + set.get().removeIf(filter); return true; } return false; @@ -336,7 +309,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public boolean containsAll(final Collection c) { synchronized (lock) { - return set().containsAll(c); + return set.get().containsAll(c); } } @@ -443,8 +416,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public boolean retainAll(final Collection c) { synchronized (lock) { - if (set().retainAll(c)) { - list().retainAll(c); + if (set.get().retainAll(c)) { + list.get().retainAll(c); return true; } return false; @@ -457,11 +430,11 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public void clear() { synchronized (lock) { - if (set().isEmpty()) { + if (set.get().isEmpty()) { return; } - set().clear(); - list().clear(); + set.get().clear(); + list.get().clear(); } } @@ -471,7 +444,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public T get(final int index) { synchronized (lock) { - return list().get(index); + return list.get().get(index); } } @@ -483,7 +456,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public T set(final int index, final T element) { //assume this isn't called except when changing list order, so don't worry about updating set synchronized (lock) { - return list().set(index, element); + return list.get().set(index, element); } } @@ -504,12 +477,12 @@ public class FCollection implements List, /*Set,*/ FCollectionView, */ private boolean insert(int index, final T element) { synchronized (lock) { - if (set().add(element)) { - list().add(index, element); + if (set.get().add(element)) { + list.get().add(index, element); return true; } //re-position in list if needed - final int oldIndex = list().indexOf(element); + final int oldIndex = list.get().indexOf(element); if (index == oldIndex) { return false; } @@ -517,8 +490,8 @@ public class FCollection implements List, /*Set,*/ FCollectionView, if (index > oldIndex) { index--; //account for being removed } - list().remove(oldIndex); - list().add(index, element); + list.get().remove(oldIndex); + list.get().add(index, element); return true; } } @@ -529,9 +502,9 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public T remove(final int index) { synchronized (lock) { - final T removedItem = list().remove(index); + final T removedItem = list.get().remove(index); if (removedItem != null) { - set().remove(removedItem); + set.get().remove(removedItem); } return removedItem; } @@ -543,7 +516,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public int indexOf(final Object o) { synchronized (lock) { - return list().indexOf(o); + return list.get().indexOf(o); } } @@ -553,7 +526,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public int lastIndexOf(final Object o) { synchronized (lock) { - return list().lastIndexOf(o); + return list.get().lastIndexOf(o); } } @@ -563,7 +536,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public ListIterator listIterator() { return new ListItr(0); - //return list().listIterator(); + //return list.get().listIterator(); } /** @@ -572,7 +545,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public ListIterator listIterator(final int index) { return new ListItr(index); - //return list().listIterator(index); + //return list.get().listIterator(index); } /** @@ -586,7 +559,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, @Override public List subList(final int fromIndex, final int toIndex) { synchronized (lock) { - return ImmutableList.copyOf(list().subList(fromIndex, toIndex)); + return ImmutableList.copyOf(list.get().subList(fromIndex, toIndex)); } } @@ -608,7 +581,7 @@ public class FCollection implements List, /*Set,*/ FCollectionView, public void sort(final Comparator comparator) { synchronized (lock) { try { - list().sort(comparator); + list.get().sort(comparator); } catch (Exception e) { System.err.println("FCollection failed to sort: \n" + comparator + "\n" + e.getMessage()); } 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 b06b20e0b17..5dc49252613 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -32,6 +32,7 @@ import forge.game.staticability.StaticAbilityAssignCombatDamageAsUnblocked; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; import forge.util.CardTranslation; +import forge.util.Lazy; import forge.util.Localizer; import forge.util.collect.FCollection; import forge.util.collect.FCollectionView; @@ -53,122 +54,17 @@ public class Combat { private boolean legacyOrderCombatants; private AttackConstraints attackConstraints; // Defenders, as they are attacked by hostile forces - private FCollection _attackableEntries; - private FCollection attackableEntries() { - FCollection result = _attackableEntries; - if (result == null) { - synchronized (this) { - result = _attackableEntries; - if (result == null) { - result = new FCollection<>(); - _attackableEntries = result; - } - } - } - return _attackableEntries; - } + private final Lazy> attackableEntries = Lazy.of(FCollection::new); // Keyed by attackable defender (player or planeswalker or battle) - private Multimap _attackedByBands; - private Multimap attackedByBands() { - Multimap result = _attackedByBands; - if (result == null) { - synchronized (this) { - result = _attackedByBands; - if (result == null) { - result = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); - _attackedByBands = result; - } - } - } - return _attackedByBands; - } - private Multimap _blockedBands; - private Multimap blockedBands() { - Multimap result = _blockedBands; - if (result == null) { - synchronized (this) { - result = _blockedBands; - if (result == null) { - result = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); - _blockedBands = result; - } - } - } - return _blockedBands; - } - - private Map _attackersOrderedForDamageAssignment; - private Map attackersOrderedForDamageAssignment() { - Map result = _attackersOrderedForDamageAssignment; - if (result == null) { - synchronized (this) { - result = _attackersOrderedForDamageAssignment; - if (result == null) { - result = Maps.newHashMap(); - _attackersOrderedForDamageAssignment = result; - } - } - } - return _attackersOrderedForDamageAssignment; - } - private Map _blockersOrderedForDamageAssignment; - private Map blockersOrderedForDamageAssignment() { - Map result = _blockersOrderedForDamageAssignment; - if (result == null) { - synchronized (this) { - result = _blockersOrderedForDamageAssignment; - if (result == null) { - result = Maps.newHashMap(); - _blockersOrderedForDamageAssignment = result; - } - } - } - return _blockersOrderedForDamageAssignment; - } - private CardCollection _lkiCache; - private CardCollection lkiCache() { - CardCollection result = _lkiCache; - if (result == null) { - synchronized (this) { - result = _lkiCache; - if (result == null) { - result = new CardCollection(); - _lkiCache = result; - } - } - } - return _lkiCache; - } - private CardDamageMap _damageMap; - private CardDamageMap damageMap() { - CardDamageMap result = _damageMap; - if (result == null) { - synchronized (this) { - result = _damageMap; - if (result == null) { - result = new CardDamageMap(); - _damageMap = result; - } - } - } - return _damageMap; - } + private final Lazy> attackedByBands = Lazy.of(() -> Multimaps.synchronizedMultimap(ArrayListMultimap.create())); + private final Lazy> blockedBands = Lazy.of(() -> Multimaps.synchronizedMultimap(ArrayListMultimap.create())); + private final Lazy> attackersOrderedForDamageAssignment = Lazy.of(Maps::newHashMap); + private final Lazy> blockersOrderedForDamageAssignment = Lazy.of(Maps::newHashMap); + private final Lazy lkiCache = Lazy.of(CardCollection::new); + private final Lazy damageMap = Lazy.of(CardDamageMap::new); // List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW) - private CardCollection _combatantsThatDealtFirstStrikeDamage; - private CardCollection combatantsThatDealtFirstStrikeDamage() { - CardCollection result = _combatantsThatDealtFirstStrikeDamage; - if (result == null) { - synchronized (this) { - result = _combatantsThatDealtFirstStrikeDamage; - if (result == null) { - result = new CardCollection(); - _combatantsThatDealtFirstStrikeDamage = result; - } - } - } - return _combatantsThatDealtFirstStrikeDamage; - } + private final Lazy combatantsThatDealtFirstStrikeDamage = Lazy.of(CardCollection::new); public Combat(final Player attacker) { playerWhoAttacks = attacker; @@ -178,12 +74,12 @@ public class Combat { public Combat(Combat combat, IEntityMap map) { playerWhoAttacks = map.map(combat.playerWhoAttacks); - for (GameEntity entry : combat.attackableEntries()) { - attackableEntries().add(map.map(entry)); + for (GameEntity entry : combat.attackableEntries.get()) { + attackableEntries.get().add(map.map(entry)); } HashMap bandsMap = new HashMap<>(); - for (Entry entry : combat.attackedByBands().entries()) { + for (Entry entry : combat.attackedByBands.get().entries()) { AttackingBand origBand = entry.getValue(); ArrayList attackers = new ArrayList<>(); for (Card c : origBand.getAttackers()) { @@ -195,37 +91,37 @@ public class Combat { newBand.setBlocked(blocked); } bandsMap.put(origBand, newBand); - attackedByBands().put(map.map(entry.getKey()), newBand); + attackedByBands.get().put(map.map(entry.getKey()), newBand); } - for (Entry entry : combat.blockedBands().entries()) { - blockedBands().put(bandsMap.get(entry.getKey()), map.map(entry.getValue())); + for (Entry entry : combat.blockedBands.get().entries()) { + blockedBands.get().put(bandsMap.get(entry.getKey()), map.map(entry.getValue())); } - for (Entry entry : combat.attackersOrderedForDamageAssignment().entrySet()) { - attackersOrderedForDamageAssignment().put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); + for (Entry entry : combat.attackersOrderedForDamageAssignment.get().entrySet()) { + attackersOrderedForDamageAssignment.get().put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); } - for (Entry entry : combat.blockersOrderedForDamageAssignment().entrySet()) { - blockersOrderedForDamageAssignment().put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); + for (Entry entry : combat.blockersOrderedForDamageAssignment.get().entrySet()) { + blockersOrderedForDamageAssignment.get().put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); } // Note: Doesn't currently set up lkiCache, since it's just a cache and not strictly needed... - for (Table.Cell entry : combat.damageMap().cellSet()) { - damageMap().put(map.map(entry.getRowKey()), map.map(entry.getColumnKey()), entry.getValue()); + for (Table.Cell entry : combat.damageMap.get().cellSet()) { + damageMap.get().put(map.map(entry.getRowKey()), map.map(entry.getColumnKey()), entry.getValue()); } attackConstraints = new AttackConstraints(this); } public void initConstraints() { - attackableEntries().clear(); + attackableEntries.get().clear(); // Create keys for all possible attack targets - attackableEntries().addAll(CombatUtil.getAllPossibleDefenders(playerWhoAttacks)); + attackableEntries.get().addAll(CombatUtil.getAllPossibleDefenders(playerWhoAttacks)); attackConstraints = new AttackConstraints(this); } @Override public String toString() { StringBuilder sb = new StringBuilder(); - for (GameEntity defender : attackableEntries()) { + for (GameEntity defender : attackableEntries.get()) { CardCollection attackers = getAttackersOf(defender); if (attackers.isEmpty()) { continue; @@ -251,13 +147,13 @@ public class Combat { CardCollection blockers = getAllBlockers(); //clear all combat-related collections - attackableEntries().clear(); - attackedByBands().clear(); - blockedBands().clear(); - attackersOrderedForDamageAssignment().clear(); - blockersOrderedForDamageAssignment().clear(); - lkiCache().clear(); - combatantsThatDealtFirstStrikeDamage().clear(); + attackableEntries.get().clear(); + attackedByBands.get().clear(); + blockedBands.get().clear(); + attackersOrderedForDamageAssignment.get().clear(); + blockersOrderedForDamageAssignment.get().clear(); + lkiCache.get().clear(); + combatantsThatDealtFirstStrikeDamage.get().clear(); //clear tracking for cards that care about "this combat" Game game = playerWhoAttacks.getGame(); @@ -289,7 +185,7 @@ public class Combat { return attackConstraints; } public final FCollectionView getDefenders() { - return attackableEntries(); + return attackableEntries.get(); } //gets attacked player opponents (ignores planeswalkers) @@ -307,7 +203,7 @@ public class Combat { public final FCollection getDefendersControlledBy(Player who) { FCollection res = new FCollection<>(); - for (GameEntity ge : attackableEntries()) { + for (GameEntity ge : attackableEntries.get()) { // if defender is the player himself or his cards if (ge == who || ge instanceof Card && ((Card) ge).getController() == who) { res.add(ge); @@ -317,15 +213,15 @@ public class Combat { } public final FCollectionView getDefendingPlayers() { - return new FCollection<>(Iterables.filter(attackableEntries(), Player.class)); + return new FCollection<>(Iterables.filter(attackableEntries.get(), Player.class)); } public final CardCollection getDefendingPlaneswalkers() { - return CardLists.filter(Iterables.filter(attackableEntries(), Card.class), CardPredicates.isType("Planeswalker")); + return CardLists.filter(Iterables.filter(attackableEntries.get(), Card.class), CardPredicates.isType("Planeswalker")); } public final CardCollection getDefendingBattles() { - return CardLists.filter(Iterables.filter(attackableEntries(), Card.class), CardPredicates.isType("Battle")); + return CardLists.filter(Iterables.filter(attackableEntries.get(), Card.class), CardPredicates.isType("Battle")); } public final Map getAttackersAndDefenders() { @@ -333,14 +229,14 @@ public class Combat { } public final List getAttackingBandsOf(GameEntity defender) { - return Lists.newArrayList(attackedByBands().get(defender)); + return Lists.newArrayList(attackedByBands.get().get(defender)); } public final CardCollection getAttackersOf(GameEntity defender) { CardCollection result = new CardCollection(); - if (!attackedByBands().containsKey(defender)) + if (!attackedByBands.get().containsKey(defender)) return result; - for (AttackingBand v : attackedByBands().get(defender)) { + for (AttackingBand v : attackedByBands.get().get(defender)) { result.addAll(v.getAttackers()); } return result; @@ -350,7 +246,7 @@ public class Combat { addAttacker(c, defender, null); } public final void addAttacker(final Card c, GameEntity defender, AttackingBand band) { - Collection attackersOfDefender = attackedByBands().get(defender); + Collection attackersOfDefender = attackedByBands.get().get(defender); if (attackersOfDefender == null) { System.out.println("Trying to add Attacker " + c + " to missing defender " + defender); return; @@ -375,7 +271,7 @@ public class Combat { return getDefenderByAttacker(getBandOfAttacker(c)); } public final GameEntity getDefenderByAttacker(final AttackingBand c) { - for (Entry e : attackedByBands().entries()) { + for (Entry e : attackedByBands.get().entries()) { if (e.getValue() == c) { return e.getKey(); } @@ -408,12 +304,12 @@ public class Combat { if (c == null) { return null; } - for (AttackingBand ab : attackedByBands().values()) { + for (AttackingBand ab : attackedByBands.get().values()) { if (ab.contains(c)) { return ab; } } - CombatLki lki = lkiCache().get(c).getCombatLKI(); + CombatLki lki = lkiCache.get().get(c).getCombatLKI(); return lki == null || !lki.isAttacker ? null : lki.getFirstBand(); } @@ -426,12 +322,12 @@ public class Combat { } public final List getAttackingBands() { - return Lists.newArrayList(attackedByBands().values()); + return Lists.newArrayList(attackedByBands.get().values()); } public boolean isAttacking(Card card, GameEntity defender) { AttackingBand ab = getBandOfAttacker(card); - for (Entry ee : attackedByBands().entries()) { + for (Entry ee : attackedByBands.get().entries()) { if (ee.getValue() == ab) { return ee.getKey() == defender; } @@ -443,7 +339,7 @@ public class Combat { * Checks if a card is currently attacking, returns false if the card is not currently attacking, even if its LKI was. */ public final boolean isAttacking(Card card) { - for (AttackingBand ab : attackedByBands().values()) { + for (AttackingBand ab : attackedByBands.get().values()) { if (ab.contains(card)) { return true; } @@ -453,7 +349,7 @@ public class Combat { public final CardCollection getAttackers() { CardCollection result = new CardCollection(); - for (AttackingBand ab : attackedByBands().values()) { + for (AttackingBand ab : attackedByBands.get().values()) { result.addAll(ab.getAttackers()); } return result; @@ -471,9 +367,9 @@ public class Combat { public final void addBlocker(final Card attacker, final Card blocker) { final AttackingBand band = getBandOfAttackerNotNull(attacker); - blockedBands().put(band, blocker); + blockedBands.get().put(band, blocker); // If damage is already assigned, add this blocker as a "late entry" - if (blockersOrderedForDamageAssignment().containsKey(attacker)) { + if (blockersOrderedForDamageAssignment.get().containsKey(attacker)) { addBlockerToDamageAssignmentOrder(attacker, blocker); } blocker.updateBlockingForView(); @@ -482,7 +378,7 @@ public class Combat { // remove blocker from specific attacker public final void removeBlockAssignment(final Card attacker, final Card blocker) { AttackingBand band = getBandOfAttackerNotNull(attacker); - Collection cc = blockedBands().get(band); + Collection cc = blockedBands.get().get(band); if (cc != null) { cc.remove(blocker); } @@ -492,13 +388,13 @@ public class Combat { // remove blocker from everywhere public final void undoBlockingAssignment(final Card blocker) { CardCollection toRemove = new CardCollection(blocker); - blockedBands().values().removeAll(toRemove); + blockedBands.get().values().removeAll(toRemove); blocker.updateBlockingForView(); } public final CardCollection getAllBlockers() { CardCollection result = new CardCollection(); - for (Card blocker : blockedBands().values()) { + for (Card blocker : blockedBands.get().values()) { if (!result.contains(blocker)) { result.add(blocker); } @@ -523,13 +419,13 @@ public class Combat { return getBlockers(getBandOfAttacker(card)); } public final CardCollection getBlockers(final AttackingBand band) { - Collection blockers = blockedBands().get(band); + Collection blockers = blockedBands.get().get(band); return blockers == null ? new CardCollection() : new CardCollection(blockers); } public final CardCollection getAttackersBlockedBy(final Card blocker) { CardCollection blocked = new CardCollection(); - for (Entry s : blockedBands().entries()) { + for (Entry s : blockedBands.get().entries()) { if (s.getValue().equals(blocker)) { blocked.addAll(s.getKey().getAttackers()); } @@ -539,7 +435,7 @@ public class Combat { public final FCollectionView getAttackingBandsBlockedBy(Card blocker) { FCollection bands = new FCollection<>(); - for (Entry kv : blockedBands().entries()) { + for (Entry kv : blockedBands.get().entries()) { if (kv.getValue().equals(blocker)) { bands.add(kv.getKey()); } @@ -563,10 +459,10 @@ public class Combat { /** If there are multiple blockers, the Attacker declares the Assignment Order */ public void orderBlockersForDamageAssignment() { // this method performs controller's role List> blockersNeedManualOrdering = new ArrayList<>(); - for (AttackingBand band : attackedByBands().values()) { + for (AttackingBand band : attackedByBands.get().values()) { if (band.isEmpty()) continue; - Collection blockers = blockedBands().get(band); + Collection blockers = blockedBands.get().get(band); if (blockers == null || blockers.isEmpty()) { continue; } @@ -590,13 +486,13 @@ public class Combat { /** If there are multiple blockers, the Attacker declares the Assignment Order */ public void orderBlockersForDamageAssignment(Card attacker, CardCollection blockers) { // this method performs controller's role if (blockers.size() <= 1 || !this.legacyOrderCombatants) { - blockersOrderedForDamageAssignment().put(attacker, new CardCollection(blockers)); + blockersOrderedForDamageAssignment.get().put(attacker, new CardCollection(blockers)); return; } // Damage Ordering needs to take cards like Melee into account, is that happening? CardCollection orderedBlockers = playerWhoAttacks.getController().orderBlockers(attacker, blockers); // we know there's a list - blockersOrderedForDamageAssignment().put(attacker, orderedBlockers); + blockersOrderedForDamageAssignment.get().put(attacker, orderedBlockers); // Display the chosen order of blockers in the log // TODO: this is best done via a combat panel update @@ -623,15 +519,15 @@ public class Combat { * @param blocker the blocking creature. */ public void addBlockerToDamageAssignmentOrder(Card attacker, Card blocker) { - final CardCollection oldBlockers = blockersOrderedForDamageAssignment().get(attacker); + final CardCollection oldBlockers = blockersOrderedForDamageAssignment.get().get(attacker); if (oldBlockers == null || oldBlockers.isEmpty()) { - blockersOrderedForDamageAssignment().put(attacker, new CardCollection(blocker)); + blockersOrderedForDamageAssignment.get().put(attacker, new CardCollection(blocker)); } else if (this.legacyOrderCombatants) { CardCollection orderedBlockers = playerWhoAttacks.getController().orderBlocker(attacker, blocker, oldBlockers); - blockersOrderedForDamageAssignment().put(attacker, orderedBlockers); + blockersOrderedForDamageAssignment.get().put(attacker, orderedBlockers); } else { oldBlockers.add(blocker); - blockersOrderedForDamageAssignment().put(attacker, oldBlockers); + blockersOrderedForDamageAssignment.get().put(attacker, oldBlockers); } } @@ -650,19 +546,19 @@ public class Combat { CardCollection orderedAttacker = attackers.size() <= 1 || !this.legacyOrderCombatants ? attackers : blockerCtrl.getController().orderAttackers(blocker, attackers); // Damage Ordering needs to take cards like Melee into account, is that happening? - attackersOrderedForDamageAssignment().put(blocker, orderedAttacker); + attackersOrderedForDamageAssignment.get().put(blocker, orderedAttacker); } // removes references to this attacker from all indices and orders public void unregisterAttacker(final Card c, AttackingBand ab) { - blockersOrderedForDamageAssignment().remove(c); + blockersOrderedForDamageAssignment.get().remove(c); - Collection blockers = blockedBands().get(ab); + Collection blockers = blockedBands.get().get(ab); if (blockers != null) { for (Card b : blockers) { // Clear removed attacker from assignment order - if (attackersOrderedForDamageAssignment().containsKey(b)) { - attackersOrderedForDamageAssignment().get(b).remove(c); + if (attackersOrderedForDamageAssignment.get().containsKey(b)) { + attackersOrderedForDamageAssignment.get().get(b).remove(c); } } } @@ -688,10 +584,10 @@ public class Combat { // removes references to this defender from all indices and orders public void unregisterDefender(final Card c, AttackingBand bandBeingBlocked) { - attackersOrderedForDamageAssignment().remove(c); + attackersOrderedForDamageAssignment.get().remove(c); for (Card atk : bandBeingBlocked.getAttackers()) { - if (blockersOrderedForDamageAssignment().containsKey(atk)) { - blockersOrderedForDamageAssignment().get(atk).remove(c); + if (blockersOrderedForDamageAssignment.get().containsKey(atk)) { + blockersOrderedForDamageAssignment.get().get(atk).remove(c); } } } @@ -707,16 +603,16 @@ public class Combat { } // if not found in attackers, look for this card in blockers - for (Entry be : blockedBands().entries()) { + for (Entry be : blockedBands.get().entries()) { if (be.getValue().equals(c)) { unregisterDefender(c, be.getKey()); } } - for (Card battleOrPW : Iterables.filter(attackableEntries(), Card.class)) { + for (Card battleOrPW : Iterables.filter(attackableEntries.get(), Card.class)) { if (battleOrPW.equals(c)) { Multimap attackerBuffer = ArrayListMultimap.create(); - Collection bands = attackedByBands().get(c); + Collection bands = attackedByBands.get().get(c); for (AttackingBand abDef : bands) { unregisterDefender(c, abDef); // Rule 506.4c workaround to keep creatures in combat @@ -726,20 +622,20 @@ public class Combat { attackerBuffer.put(fake, abDef); } bands.clear(); - attackedByBands().putAll(attackerBuffer); + attackedByBands.get().putAll(attackerBuffer); break; } } // remove card from map - while (blockedBands().values().remove(c)); + while (blockedBands.get().values().remove(c)); c.updateBlockingForView(); } public final boolean removeAbsentCombatants() { // CR 506.4 iterate all attackers and remove illegal declarations CardCollection missingCombatants = new CardCollection(); - for (Entry ee : attackedByBands().entries()) { + for (Entry ee : attackedByBands.get().entries()) { for (Card c : ee.getValue().getAttackers()) { if (!c.isInPlay() || !c.isCreature()) { missingCombatants.add(c); @@ -753,7 +649,7 @@ public class Combat { } } - for (Entry be : blockedBands().entries()) { + for (Entry be : blockedBands.get().entries()) { Card blocker = be.getValue(); if (!blocker.isInPlay() || !blocker.isCreature()) { missingCombatants.add(blocker); @@ -772,8 +668,8 @@ public class Combat { public final void fireTriggersForUnblockedAttackers(final Game game) { boolean bFlag = false; List defenders = Lists.newArrayList(); - for (AttackingBand ab : attackedByBands().values()) { - Collection blockers = blockedBands().get(ab); + for (AttackingBand ab : attackedByBands.get().values()) { + Collection blockers = blockedBands.get().get(ab); boolean isBlocked = blockers != null && !blockers.isEmpty(); ab.setBlocked(isBlocked); @@ -812,13 +708,13 @@ public class Combat { } if (firstStrikeDamage) { - combatantsThatDealtFirstStrikeDamage().add(blocker); + combatantsThatDealtFirstStrikeDamage.get().add(blocker); } // Run replacement effects blocker.getGame().getReplacementHandler().run(ReplacementType.AssignDealDamage, AbilityKey.mapFromAffected(blocker)); - CardCollection attackers = attackersOrderedForDamageAssignment().get(blocker); + CardCollection attackers = attackersOrderedForDamageAssignment.get().get(blocker); final int damage = blocker.getNetCombatDamage(); @@ -846,10 +742,10 @@ public class Combat { for (Entry dt : map.entrySet()) { // Butcher Orgg if (dt.getKey() == null && dt.getValue() > 0) { - damageMap().put(blocker, defender, dt.getValue()); + damageMap.get().put(blocker, defender, dt.getValue()); } else { dt.getKey().addAssignedDamage(dt.getValue(), blocker); - damageMap().put(blocker, dt.getKey(), dt.getValue()); + damageMap.get().put(blocker, dt.getKey(), dt.getValue()); } } } @@ -870,7 +766,7 @@ public class Combat { } if (firstStrikeDamage) { - combatantsThatDealtFirstStrikeDamage().add(attacker); + combatantsThatDealtFirstStrikeDamage.get().add(attacker); } // Run replacement effects @@ -891,7 +787,7 @@ public class Combat { GameEntity defender = getDefenderByAttacker(band); Player assigningPlayer = getAttackingPlayer(); - orderedBlockers = blockersOrderedForDamageAssignment().get(attacker); + orderedBlockers = blockersOrderedForDamageAssignment.get().get(attacker); // Defensive Formation is very similar to Banding with Blockers // It allows the defending player to assign damage instead of the attacking player if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) { @@ -955,7 +851,7 @@ public class Combat { } if (assignToPlayer) { attackers.remove(attacker); - damageMap().put(attacker, defender, damageDealt); + damageMap.get().put(attacker, defender, damageDealt); } else if (orderedBlockers == null || orderedBlockers.isEmpty()) { attackers.remove(attacker); @@ -963,14 +859,14 @@ public class Combat { final SpellAbility emptySA = new SpellAbility.EmptySa(ApiType.Cleanup, attacker); Card chosen = attacker.getController().getController().chooseCardsForEffect(getDefendersCreatures(), emptySA, Localizer.getInstance().getMessage("lblChooseCreature"), 1, 1, false, null).get(0); - damageMap().put(attacker, chosen, damageDealt); + damageMap.get().put(attacker, chosen, damageDealt); } else if (trampler || !band.isBlocked()) { // this is called after declare blockers, no worries 'bout nulls in isBlocked if (defender == null) { defender = getDefenderPlayerByAttacker(attacker); System.err.println("[COMBAT] defender is null, getDefenderPlayerByAttacker(attacker) result: " + defender); } // this will fail if defender is null, and it doesn't allow null values.. - damageMap().put(attacker, defender, damageDealt); + damageMap.get().put(attacker, defender, damageDealt); } // No damage happens if blocked but no blockers left } else { Map map = assigningPlayer.getController().assignCombatDamage(attacker, orderedBlockers, attackers, @@ -990,11 +886,11 @@ public class Combat { if (defender instanceof Card) { ((Card) defender).addAssignedDamage(dt.getValue(), attacker); } - damageMap().put(attacker, defender, dt.getValue()); + damageMap.get().put(attacker, defender, dt.getValue()); } } else { dt.getKey().addAssignedDamage(dt.getValue(), attacker); - damageMap().put(attacker, dt.getKey(), dt.getValue()); + damageMap.get().put(attacker, dt.getKey(), dt.getValue()); } } } // if !hasFirstStrike ... @@ -1011,7 +907,7 @@ public class Combat { if (firstStrikeDamage && combatant.hasFirstStrike()) { return true; } - return !firstStrikeDamage && !combatantsThatDealtFirstStrikeDamage().contains(combatant); + return !firstStrikeDamage && !combatantsThatDealtFirstStrikeDamage.get().contains(combatant); } public final boolean assignCombatDamage(boolean firstStrikeDamage) { @@ -1019,7 +915,7 @@ public class Combat { assignedDamage |= assignBlockersDamage(firstStrikeDamage); if (!firstStrikeDamage) { // Clear first strike damage list since it doesn't matter anymore - combatantsThatDealtFirstStrikeDamage().clear(); + combatantsThatDealtFirstStrikeDamage.get().clear(); } return assignedDamage; } @@ -1031,7 +927,7 @@ public class Combat { CardDamageMap preventMap = new CardDamageMap(); GameEntityCounterTable counterTable = new GameEntityCounterTable(); - game.getAction().dealDamage(true, damageMap(), preventMap, counterTable, null); + game.getAction().dealDamage(true, damageMap.get(), preventMap, counterTable, null); // copy last state again for dying replacement effects game.copyLastState(); @@ -1044,7 +940,7 @@ public class Combat { public final CardCollection getUnblockedAttackers() { CardCollection unblocked = new CardCollection(); - for (AttackingBand ab : attackedByBands().values()) { + for (AttackingBand ab : attackedByBands.get().values()) { if (Boolean.FALSE.equals(ab.isBlocked())) { unblocked.addAll(ab.getAttackers()); } @@ -1053,13 +949,13 @@ public class Combat { } public boolean isPlayerAttacked(Player who) { - for (GameEntity defender : attackedByBands().keySet()) { + for (GameEntity defender : attackedByBands.get().keySet()) { Card defenderAsCard = defender instanceof Card ? (Card)defender : null; if ((null != defenderAsCard && (defenderAsCard.getController() != who && defenderAsCard.getProtectingPlayer() != who)) || (null == defenderAsCard && defender != who)) { continue; // defender is not related to player 'who' } - for (AttackingBand ab : attackedByBands().get(defender)) { + for (AttackingBand ab : attackedByBands.get().get(defender)) { if (!ab.isEmpty()) { return true; } @@ -1069,7 +965,7 @@ public class Combat { } public boolean isBlocking(Card blocker) { - if (blockedBands().containsValue(blocker)) { + if (blockedBands.get().containsValue(blocker)) { return true; // is blocking something at the moment } @@ -1077,13 +973,13 @@ public class Combat { return false; } - CombatLki lki = lkiCache().get(blocker).getCombatLKI(); + CombatLki lki = lkiCache.get().get(blocker).getCombatLKI(); return null != lki && !lki.isAttacker; // was blocking something anyway } public boolean isBlocking(Card blocker, Card attacker) { AttackingBand ab = getBandOfAttacker(attacker); - Collection blockers = blockedBands().get(ab); + Collection blockers = blockedBands.get().get(ab); if (blockers != null && blockers.contains(blocker)) { return true; // is blocking the attacker's band at the moment } @@ -1092,7 +988,7 @@ public class Combat { return false; } - CombatLki lki = lkiCache().get(blocker).getCombatLKI(); + CombatLki lki = lkiCache.get().get(blocker).getCombatLKI(); return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band } @@ -1105,7 +1001,7 @@ public class Combat { final boolean isAttacker = attackingBand != null; if (isAttacker) { boolean found = false; - for (AttackingBand ab : attackedByBands().values()) { + for (AttackingBand ab : attackedByBands.get().values()) { if (ab.contains(lki)) { found = true; break; @@ -1120,7 +1016,7 @@ public class Combat { return null; // card was not even in combat } } - lkiCache().add(lki); + lkiCache.get().add(lki); final FCollectionView relatedBands = isAttacker ? new FCollection<>(attackingBand) : attackersBlocked; return new CombatLki(isAttacker, relatedBands); } 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 5d61b284d94..1a033fd2456 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -118,23 +118,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private boolean optionalTrigger = false; private ReplacementEffect replacementEffect = null; private int sourceTrigger = -1; - private List _triggerRemembered; - private List triggerRemembered() { - List result = _triggerRemembered; - if (result == null) { - synchronized (this) { - result = _triggerRemembered; - if (result == null) { - result = Lists.newArrayList(); - _triggerRemembered = result; - } - } - } - return _triggerRemembered; - } - private void _setTriggerRemembered(List tr) { - _triggerRemembered = tr; - } + private List triggerRemembered = Lists.newArrayList(); private AlternativeCost altCost = null; private EnumSet optionalCosts = EnumSet.noneOf(OptionalCost.class); @@ -146,149 +130,22 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit /** The pay costs. */ private Cost payCosts; private SpellAbilityRestriction restrictions; - private SpellAbilityCondition _conditions; - private SpellAbilityCondition conditions() { - SpellAbilityCondition result = _conditions; - if (result == null) { - synchronized (this) { - result = _conditions; - if (result == null) { - result = new SpellAbilityCondition(); - _conditions = result; - } - } - } - return _conditions; - } - private void _setConditions(SpellAbilityCondition c) { - _conditions = c; - } + private SpellAbilityCondition conditions = new SpellAbilityCondition(); private AbilitySub subAbility; - private Map _additionalAbilities; - private Map additionalAbilities() { - Map result = _additionalAbilities; - if (result == null) { - synchronized (this) { - result = _additionalAbilities; - if (result == null) { - result = Maps.newHashMap(); - _additionalAbilities = result; - } - } - } - return _additionalAbilities; - } - private void _setAdditionalAbilities(Map aa) { - _additionalAbilities = aa; - } - private Map> _additionalAbilityLists; - private Map> additionalAbilityLists() { - Map> result = _additionalAbilityLists; - if (result == null) { - synchronized (this) { - result = _additionalAbilityLists; - if (result == null) { - result = Maps.newHashMap(); - _additionalAbilityLists = result; - } - } - } - return _additionalAbilityLists; - } - private void _setAdditionalAbilityLists(Map> al) { - _additionalAbilityLists = al; - } + private Map additionalAbilities = Maps.newHashMap(); + private Map> additionalAbilityLists = Maps.newHashMap(); protected ApiType api = null; - - private List _payingMana; - private List payingMana() { - List result = _payingMana; - if (result == null) { - synchronized (this) { - result = _payingMana; - if (result == null) { - result = Lists.newArrayList(); - _payingMana = result; - } - } - } - return _payingMana; - } - private void _setPayingMana(List m) { - _payingMana = m; - } - private List _paidAbilities; - private List paidAbilities() { - List result = _paidAbilities; - if (result == null) { - synchronized (this) { - result = _paidAbilities; - if (result == null) { - result = Lists.newArrayList(); - _paidAbilities = result; - } - } - } - return _paidAbilities; - } - private void _setPaidAbilities(List p) { - _paidAbilities = p; - } + private List payingMana = Lists.newArrayList(); + private List paidAbilities = Lists.newArrayList(); private Integer xManaCostPaid = null; - - private TreeBasedTable _paidLists; - private TreeBasedTable paidLists() { - TreeBasedTable result = _paidLists; - if (result == null) { - synchronized (this) { - result = _paidLists; - if (result == null) { - result = TreeBasedTable.create(); - _paidLists = result; - } - } - } - return _paidLists; - } - private void _setPaidLists(TreeBasedTable p) { - _paidLists = p; - } - + private TreeBasedTable paidLists = TreeBasedTable.create(); private EnumMap triggeringObjects = AbilityKey.newMap(); private EnumMap replacingObjects = AbilityKey.newMap(); - - private List _pipsToReduce; - private List pipsToReduce() { - List result = _pipsToReduce; - if (result == null) { - synchronized (this) { - result = _pipsToReduce; - if (result == null) { - result = new ArrayList<>(); - _pipsToReduce = result; - } - } - } - return _pipsToReduce; - } - + private final Lazy> pipsToReduce = Lazy.of(ArrayList::new); private List chosenList = null; - private CardCollection _tappedForConvoke; - private CardCollection tappedForConvoke() { - CardCollection result = _tappedForConvoke; - if (result == null) { - synchronized (this) { - result = _tappedForConvoke; - if (result == null) { - result = new CardCollection(); - _tappedForConvoke = result; - } - } - } - return _tappedForConvoke; - } + private final Lazy tappedForConvoke = Lazy.of(CardCollection::new); private Card sacrificedAsOffering; private Card sacrificedAsEmerge; @@ -302,26 +159,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private boolean isCastFromPlayEffect = false; private TargetRestrictions targetRestrictions; - private TargetChoices _targetChosen; - private TargetChoices targetChosen() { - TargetChoices result = _targetChosen; - if (result == null) { - synchronized (this) { - result = _targetChosen; - if (result == null) { - result = new TargetChoices(); - _targetChosen = result; - } - } - } - return _targetChosen; - } - private void _setTargetChosen(TargetChoices c) { - _targetChosen = c; - } - private void resetTargetChosen() { - _targetChosen = new TargetChoices(); - } + private TargetChoices targetChosen = new TargetChoices(); private Integer dividedValue = null; @@ -332,20 +170,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private CardCollection lastStateBattlefield; private CardCollection lastStateGraveyard; - private CardCollection _rollbackEffects; - private CardCollection rollbackEffects() { - CardCollection result = _rollbackEffects; - if (result == null) { - synchronized (this) { - result = _rollbackEffects; - if (result == null) { - result = new CardCollection(); - _rollbackEffects = result; - } - } - } - return _rollbackEffects; - } + private final Lazy rollbackEffects = Lazy.of(CardCollection::new); private CardDamageMap damageMap; private CardDamageMap preventMap; @@ -413,12 +238,12 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (subAbility != null) { subAbility.setHostCard(c); } - for (SpellAbility sa : additionalAbilities().values()) { + for (SpellAbility sa : additionalAbilities.values()) { if (sa.getHostCard() != c) { sa.setHostCard(c); } } - for (List list : additionalAbilityLists().values()) { + for (List list : additionalAbilityLists.values()) { for (AbilitySub sa : list) { if (sa.getHostCard() != c) { sa.setHostCard(c); @@ -437,10 +262,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (subAbility != null) { subAbility.setKeyword(kw); } - for (SpellAbility sa : additionalAbilities().values()) { + for (SpellAbility sa : additionalAbilities.values()) { sa.setKeyword(kw); } - for (List list : additionalAbilityLists().values()) { + for (List list : additionalAbilityLists.values()) { for (AbilitySub sa : list) { sa.setKeyword(kw); } @@ -638,10 +463,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (subAbility != null) { updated |= subAbility.setActivatingPlayer(player, lki); } - for (SpellAbility sa : additionalAbilities().values()) { + for (SpellAbility sa : additionalAbilities.values()) { updated |= sa.setActivatingPlayer(player, lki); } - for (List list : additionalAbilityLists().values()) { + for (List list : additionalAbilityLists.values()) { for (AbilitySub sa : list) { updated |= sa.setActivatingPlayer(player, lki); } @@ -819,10 +644,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public SpellAbilityCondition getConditions() { - return conditions(); + return conditions; } public final void setConditions(final SpellAbilityCondition condition) { - _setConditions(condition); + conditions = condition; } public boolean metConditions() { @@ -830,13 +655,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public List getPayingMana() { - return payingMana(); + return payingMana; } public void setPayingMana(List paying) { - _setPayingMana(Lists.newArrayList(paying)); + payingMana = Lists.newArrayList(paying); } public final void clearManaPaid() { - payingMana( ).clear(); + payingMana.clear(); } public final int getSpendPhyrexianMana() { @@ -898,42 +723,42 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public ColorSet getPayingColors() { byte colors = 0; - for (Mana m : payingMana()) { + for (Mana m : payingMana) { colors |= m.getColor(); } return ColorSet.fromMask(colors); } public List getPayingManaAbilities() { - return paidAbilities(); + return paidAbilities; } // Combined PaidLists public TreeBasedTable getPaidHash() { - return paidLists(); + return paidLists; } public void setPaidHash(final TreeBasedTable hash) { - _setPaidLists(TreeBasedTable.create(hash)); + paidLists = TreeBasedTable.create(hash); } // use if it doesn't matter if payment was caused by extrinsic cost modifier public Iterable getPaidList(final String str) { - return Iterables.concat(paidLists().row(str).values()); + return Iterables.concat(paidLists.row(str).values()); } public CardCollection getPaidList(final String str, final boolean intrinsic) { - return paidLists().get(str, intrinsic); + return paidLists.get(str, intrinsic); } public void addCostToHashList(final Card c, final String str, final boolean intrinsic) { - if (!paidLists().contains(str, intrinsic)) { - paidLists().put(str, intrinsic, new CardCollection()); + if (!paidLists.contains(str, intrinsic)) { + paidLists.put(str, intrinsic, new CardCollection()); } - paidLists().get(str, intrinsic).add(c); + paidLists.get(str, intrinsic).add(c); } public void resetPaidHash() { - paidLists().clear(); + paidLists.clear(); } public Iterable getOptionalCosts() { @@ -945,7 +770,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit optionalCosts = EnumSet.copyOf(optionalCosts); optionalCosts.add(cost); if (!cost.getPip().isEmpty()) { - pipsToReduce().add(cost.getPip()); + pipsToReduce.get().add(cost.getPip()); } } @@ -1005,13 +830,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit @Override public List getTriggerRemembered() { - return triggerRemembered(); + return triggerRemembered; } public void setTriggerRemembered(List list) { - _setTriggerRemembered(list); + triggerRemembered = list; } public void resetTriggerRemembered() { - _setTriggerRemembered(Lists.newArrayList()); + triggerRemembered = Lists.newArrayList(); } public Map getReplacingObjects() { @@ -1187,37 +1012,37 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public Map getAdditionalAbilities() { - return additionalAbilities(); + return additionalAbilities; } public SpellAbility getAdditionalAbility(final String name) { if (hasAdditionalAbility(name)) { - return additionalAbilities().get(name); + return additionalAbilities.get(name); } return null; } public boolean hasAdditionalAbility(final String name) { - return additionalAbilities().containsKey(name); + return additionalAbilities.containsKey(name); } public void setAdditionalAbility(final String name, final SpellAbility sa) { if (sa == null) { - additionalAbilities().remove(name); + additionalAbilities.remove(name); } else { if (sa instanceof AbilitySub) { ((AbilitySub)sa).setParent(this); } - additionalAbilities().put(name, sa); + additionalAbilities.put(name, sa); } view.updateDescription(this); //description changes when sub-abilities change } public Map> getAdditionalAbilityLists() { - return additionalAbilityLists(); + return additionalAbilityLists; } public List getAdditionalAbilityList(final String name) { - if (additionalAbilityLists().containsKey(name)) { - return additionalAbilityLists().get(name); + if (additionalAbilityLists.containsKey(name)) { + return additionalAbilityLists.get(name); } else { return ImmutableList.of(); } @@ -1225,13 +1050,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public void setAdditionalAbilityList(final String name, final List list) { if (list == null || list.isEmpty()) { - additionalAbilityLists().remove(name); + additionalAbilityLists.remove(name); } else { List result = Lists.newArrayList(list); for (AbilitySub sa : result) { sa.setParent(this); } - additionalAbilityLists().put(name, result); + additionalAbilityLists.put(name, result); } view.updateDescription(this); } @@ -1371,18 +1196,18 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit clone.changeZoneTable = new CardZoneTable(changeZoneTable); } - clone._setPayingMana(Lists.newArrayList(payingMana())); - clone._setPaidAbilities(Lists.newArrayList()); + clone.payingMana = Lists.newArrayList(payingMana); + clone.paidAbilities = Lists.newArrayList(); clone.setPaidHash(getPaidHash()); if (usesTargeting()) { // the targets need to be cloned, otherwise they might be cleared - clone._setTargetChosen(getTargets().clone()); + clone.targetChosen = getTargets().clone(); } // clear maps for copy, the values will be added later - clone._setAdditionalAbilities(Maps.newHashMap()); - clone._setAdditionalAbilityLists(Maps.newHashMap()); + clone.additionalAbilities = Maps.newHashMap(); + clone.additionalAbilityLists = Maps.newHashMap(); // run special copy Ability to make a deep copy CardFactory.copySpellAbility(this, clone, host, activ, lki, keepTextChanges); } catch (final CloneNotSupportedException e) { @@ -1734,20 +1559,20 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public List getPipsToReduce() { - return pipsToReduce(); + return pipsToReduce.get(); } public final void clearPipsToReduce() { - pipsToReduce().clear(); + pipsToReduce.get().clear(); } public CardCollection getTappedForConvoke() { - return tappedForConvoke(); + return tappedForConvoke.get(); } public void addTappedForConvoke(final Card c) { - tappedForConvoke().add(c); + tappedForConvoke.get().add(c); } public void clearTappedForConvoke() { - tappedForConvoke().clear(); + tappedForConvoke.get().clear(); } public boolean isEmerge() { @@ -1921,16 +1746,16 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit * @return the chosenTarget */ public TargetChoices getTargets() { - return targetChosen(); + return targetChosen; } public void setTargets(TargetChoices targets) { // TODO should copy the target choices? - _setTargetChosen(targets); + targetChosen = targets; } public void resetTargets() { - resetTargetChosen(); + targetChosen = new TargetChoices(); } /** @@ -2046,7 +1871,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public Card getTargetCard() { - return targetChosen().getFirstTargetedCard(); + return targetChosen.getFirstTargetedCard(); } /** @@ -2065,7 +1890,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } resetTargets(); - targetChosen().add(card); + targetChosen.add(card); setStackDescription(getHostCard().getName() + " - targeting " + card); } @@ -2425,11 +2250,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit subAbility.changeText(); } } - for (SpellAbility sa : additionalAbilities().values()) { + for (SpellAbility sa : additionalAbilities.values()) { sa.changeText(); } - for (List list : additionalAbilityLists().values()) { + for (List list : additionalAbilityLists.values()) { for (AbilitySub sa : list) { sa.changeText(); } @@ -2450,11 +2275,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit subAbility.changeTextIntrinsic(colorMap, typeMap); } } - for (SpellAbility sa : additionalAbilities().values()) { + for (SpellAbility sa : additionalAbilities.values()) { sa.changeTextIntrinsic(colorMap, typeMap); } - for (List list : additionalAbilityLists().values()) { + for (List list : additionalAbilityLists.values()) { for (AbilitySub sa : list) { sa.changeTextIntrinsic(colorMap, typeMap); } @@ -2467,12 +2292,12 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (subAbility != null) { subAbility.setIntrinsic(i); } - for (SpellAbility sa : additionalAbilities().values()) { + for (SpellAbility sa : additionalAbilities.values()) { if (sa.isIntrinsic() != i) { sa.setIntrinsic(i); } } - for (List list : additionalAbilityLists().values()) { + for (List list : additionalAbilityLists.values()) { for (AbilitySub sa : list) { if (sa.isIntrinsic() != i) { sa.setIntrinsic(i); @@ -2728,14 +2553,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public void addRollbackEffect(Card eff) { - rollbackEffects().add(eff); + rollbackEffects.get().add(eff); } public void rollback() { - for (Card c : rollbackEffects()) { + for (Card c : rollbackEffects.get()) { c.getGame().getAction().ceaseToExist(c, true); } - rollbackEffects().clear(); + rollbackEffects.get().clear(); } public boolean isHidden() { diff --git a/forge-gui-mobile/src/forge/assets/ImageCache.java b/forge-gui-mobile/src/forge/assets/ImageCache.java index 3ecc0e9ccb8..b474c2591c0 100644 --- a/forge-gui-mobile/src/forge/assets/ImageCache.java +++ b/forge-gui-mobile/src/forge/assets/ImageCache.java @@ -31,6 +31,7 @@ import com.google.common.collect.Sets; import forge.deck.DeckProxy; import forge.gui.GuiBase; import forge.util.FileUtil; +import forge.util.Lazy; import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -70,20 +71,7 @@ import forge.util.ImageUtil; */ public class ImageCache { private static ImageCache imageCache; - private HashSet _missingIconKeys; - private HashSet missingIconKeys() { - HashSet result = _missingIconKeys; - if (result == null) { - synchronized (this) { - result = _missingIconKeys; - if (result == null) { - result = new HashSet<>(); - _missingIconKeys = result; - } - } - } - return _missingIconKeys; - } + private Lazy> missingIconKeys = Lazy.of(HashSet::new); private final List borderlessCardlistKey = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE); public int counter = 0; private int maxCardCapacity = 300; //default card capacity @@ -133,20 +121,7 @@ public class ImageCache { return Forge.getAssets().getDefaultImage(); } - private HashMap _imageRecord; - private HashMap imageRecord() { - HashMap result = _imageRecord; - if (result == null) { - synchronized (this) { - result = _imageRecord; - if (result == null) { - result = new HashMap<>(maxCardCapacity + (maxCardCapacity / 3)); - _imageRecord = result; - } - } - } - return _imageRecord; - } + private Lazy> imageRecord = Lazy.of(() -> new HashMap<>(maxCardCapacity + (maxCardCapacity / 3))); private boolean imageLoaded, delayLoadRequested; public void allowSingleLoad() { @@ -155,7 +130,7 @@ public class ImageCache { } public void clear() { - missingIconKeys().clear(); + missingIconKeys.get().clear(); ImageKeys.clearMissingCards(); } @@ -201,8 +176,8 @@ public class ImageCache { public FImage getIcon(IHasIcon ihi) { String imageKey = ihi.getIconImageKey(); final Texture icon; - if (missingIconKeys().contains(imageKey) || (icon = getImage(ihi.getIconImageKey(), false, true)) == null) { - missingIconKeys().add(imageKey); + if (missingIconKeys.get().contains(imageKey) || (icon = getImage(ihi.getIconImageKey(), false, true)) == null) { + missingIconKeys.get().add(imageKey); return FSkinImage.UNKNOWN; } return new FTextureImage(icon); @@ -317,8 +292,8 @@ public class ImageCache { /*fix not loading image file since we intentionally not to update the cache in order for the image fetcher to update automatically after the card image/s are downloaded*/ imageLoaded = false; - if (image != null && imageRecord().get(image.toString()) == null) - imageRecord().put(image.toString(), new ImageRecord(Color.valueOf("#171717").toString(), false, getRadius(image))); //black border + if (image != null && imageRecord.get().get(image.toString()) == null) + imageRecord.get().put(image.toString(), new ImageRecord(Color.valueOf("#171717").toString(), false, getRadius(image))); //black border } } return image; @@ -450,7 +425,7 @@ public class ImageCache { if (t == null) return Color.valueOf("#171717"); try { - return Color.valueOf(imageRecord().get(t.toString()).colorValue); + return Color.valueOf(imageRecord.get().get(t.toString()).colorValue); } catch (Exception e) { return Color.valueOf("#171717"); } @@ -468,13 +443,13 @@ public class ImageCache { return 0; } public void updateImageRecord(String textureString, String colorValue, Boolean isClosertoWhite, int radius) { - imageRecord().put(textureString, new ImageRecord(colorValue, isClosertoWhite, radius)); + imageRecord.get().put(textureString, new ImageRecord(colorValue, isClosertoWhite, radius)); } public int getRadius(Texture t) { if (t == null) return 20; - ImageRecord record = imageRecord().get(t.toString()); + ImageRecord record = imageRecord.get().get(t.toString()); if (record == null) return 20; Integer i = record.cardRadius; @@ -484,7 +459,7 @@ public class ImageCache { } public FImage getBorder(String textureString) { - ImageRecord record = imageRecord().get(textureString); + ImageRecord record = imageRecord.get().get(textureString); if (record == null) return FSkinImage.IMG_BORDER_BLACK; Boolean border = record.isCloserToWhite; diff --git a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java index 221411de0ae..10955310d62 100644 --- a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java +++ b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java @@ -58,6 +58,7 @@ import forge.toolbox.FList; import forge.toolbox.FList.CompactModeHandler; import forge.util.ItemPool; import forge.util.LayoutHelper; +import forge.util.Lazy; public abstract class ItemManager extends FContainer implements IItemManager, ActivateHandler { @@ -67,22 +68,7 @@ public abstract class ItemManager extends FContainer im protected final ItemManagerModel model; private Predicate filterPredicate = null; private AdvancedSearchFilter advancedSearchFilter; - private List> _filters; - - private List> filters() { - List> result = _filters; - if (result == null) { - synchronized (this) { - result = _filters; - if (result == null) { - result = new ArrayList<>(); - _filters = result; - } - } - } - return _filters; - } - + private Lazy>> filters = Lazy.of(ArrayList::new); private boolean hideFilters = false; private boolean wantUnique = false; private boolean showRanking = false; @@ -95,22 +81,7 @@ public abstract class ItemManager extends FContainer im private ItemManagerConfig config; private Function, Object> fnNewGet; private boolean viewUpdating, needSecondUpdate; - private List _sortCols; - - private List sortCols() { - List result = _sortCols; - if (result == null) { - synchronized (this) { - result = _sortCols; - if (result == null) { - result = new ArrayList<>(); - _sortCols = result; - } - } - } - return _sortCols; - } - + private Lazy> sortCols = Lazy.of(ArrayList::new); private final TextSearchFilter searchFilter; private CardFormatFilter cardFormatFilter; @@ -244,7 +215,7 @@ public abstract class ItemManager extends FContainer im } cols.sort(Comparator.comparingInt(arg0 -> arg0.getConfig().getIndex())); - sortCols().clear(); + sortCols.get().clear(); if (cbxSortOptions != null) { cbxSortOptions.setDropDownItemTap(null); cbxSortOptions.removeAllItems(); @@ -254,14 +225,14 @@ public abstract class ItemManager extends FContainer im for (final ItemColumn col : cols) { col.setIndex(modelIndex++); if (col.isVisible()) { - sortCols().add(col); + sortCols.get().add(col); } } - final ItemColumn[] sortcols = new ItemColumn[sortCols().size()]; + final ItemColumn[] sortcols = new ItemColumn[sortCols.get().size()]; // Assemble priority sort. - for (ItemColumn col : sortCols()) { + for (ItemColumn col : sortCols.get()) { if (cbxSortOptions != null) { cbxSortOptions.addItem(col); } @@ -378,7 +349,7 @@ public abstract class ItemManager extends FContainer im helper.offset(0, ItemFilter.PADDING); // for Adventure Mode Store, Sideboard and Deck Event previews layout if (ItemManagerConfig.ADVENTURE_STORE_POOL.equals(config) || ItemManagerConfig.ADVENTURE_SIDEBOARD.equals(config)) { - for (ItemFilter filter : filters()) { + for (ItemFilter filter : filters.get()) { if (filter instanceof CardColorFilter) { filter.getWidget().setVisible(true); helper.include(filter.getWidget(), (viewButtonWidth + helper.getGapX()) * 7, fieldHeight); @@ -401,7 +372,7 @@ public abstract class ItemManager extends FContainer im helper.fillLine(advancedSearchFilter.getWidget(), fieldHeight); } if (!hideFilters) { - for (ItemFilter filter : filters()) { + for (ItemFilter filter : filters.get()) { helper.include(filter.getWidget(), filter.getPreferredWidth(helper.getRemainingLineWidth(), fieldHeight), fieldHeight); } if (allowSortChange()) { @@ -691,7 +662,7 @@ public abstract class ItemManager extends FContainer im protected abstract AdvancedSearchFilter createAdvancedSearchFilter(); public void addFilter(final ItemFilter filter) { - filters().add(filter); + filters.get().add(filter); add(filter.getWidget()); if (filter instanceof CardFormatFilter) cardFormatFilter = (CardFormatFilter) filter; @@ -725,10 +696,10 @@ public abstract class ItemManager extends FContainer im public void restoreDefaultFilters() { lockFiltering = true; - for (ItemFilter filter : filters()) { + for (ItemFilter filter : filters.get()) { remove(filter.getWidget()); } - filters().clear(); + filters.get().clear(); addDefaultFilters(); lockFiltering = false; revalidate(); @@ -737,7 +708,7 @@ public abstract class ItemManager extends FContainer im public void resetFilters() { lockFiltering = true; //prevent updating filtering from this change until all filters reset - for (final ItemFilter filter : filters()) { + for (final ItemFilter filter : filters.get()) { filter.reset(); } searchFilter.reset(); @@ -755,7 +726,7 @@ public abstract class ItemManager extends FContainer im } public void removeFilter(ItemFilter filter) { - filters().remove(filter); + filters.get().remove(filter); remove(filter.getWidget()); revalidate(); applyFilters(); @@ -767,7 +738,7 @@ public abstract class ItemManager extends FContainer im } List> predicates = new ArrayList<>(); - for (ItemFilter filter : filters()) { + for (ItemFilter filter : filters.get()) { if (!filter.isEmpty()) { predicates.add(filter.buildPredicate(genericType)); } @@ -818,7 +789,7 @@ public abstract class ItemManager extends FContainer im hideFilters = hideFilters0; boolean visible = !hideFilters0; - for (ItemFilter filter : filters()) { + for (ItemFilter filter : filters.get()) { filter.getWidget().setVisible(visible); } if (allowSortChange()) { @@ -853,7 +824,7 @@ public abstract class ItemManager extends FContainer im ItemManager.this.add(advancedSearchFilter.getWidget()); } lockFiltering = true; - for (final ItemFilter filter : filters()) { + for (final ItemFilter filter : filters.get()) { filter.reset(); } searchFilter.reset(); @@ -1128,9 +1099,9 @@ public abstract class ItemManager extends FContainer im if (cbxSortOptions != null) { return cbxSortOptions.getWidth(); } - if (filters().isEmpty()) { + if (filters.get().isEmpty()) { return 0f; } - return filters().get(filters().size() - 1).getWidget().getWidth(); + return filters.get().get(filters.get().size() - 1).getWidget().getWidth(); } } diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 1dc7f7bb5d2..503c999d8e7 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -28,6 +28,7 @@ import forge.model.FModel; import forge.toolbox.*; import forge.util.ImageFetcher; import forge.util.ImageUtil; +import forge.util.Lazy; import forge.util.TextUtil; import forge.util.Utils; @@ -65,20 +66,7 @@ public class ImageView extends ItemView { private static final int MIN_COLUMN_COUNT = Forge.isLandscapeMode() ? 2 : 1; private static final int MAX_COLUMN_COUNT = 10; - private List _selectedIndices; - private List selectedIndices() { - List result = _selectedIndices; - if (result == null) { - synchronized (this) { - result = _selectedIndices; - if (result == null) { - result = new ArrayList<>(); - _selectedIndices = result; - } - } - } - return _selectedIndices; - } + private Lazy> selectedIndices = Lazy.of(ArrayList::new); private int columnCount = 4; private float scrollHeight = 0; private ColumnDef pileBy = null; @@ -86,34 +74,8 @@ public class ImageView extends ItemView { private ItemInfo focalItem; private boolean updatingLayout; private float totalZoomAmount; - private List _orderedItems; - private List orderedItems() { - List result = _orderedItems; - if (result == null) { - synchronized (this) { - result = _orderedItems; - if (result == null) { - result = new ArrayList<>(); - _orderedItems = result; - } - } - } - return _orderedItems; - } - private List _groups; - private List groups() { - List result = _groups; - if (result == null) { - synchronized (this) { - result = _groups; - if (result == null) { - result = new ArrayList<>(); - _groups = result; - } - } - } - return _groups; - } + private Lazy> orderedItems = Lazy.of(ArrayList::new); + private Lazy> groups = Lazy.of(ArrayList::new); private class ExpandCollapseButton extends FLabel { private boolean isAllCollapsed; @@ -126,7 +88,7 @@ public class ImageView extends ItemView { } boolean collapsed = !isAllCollapsed; - for (Group group : groups()) { + for (Group group : groups.get()) { group.isCollapsed = collapsed; } @@ -138,7 +100,7 @@ public class ImageView extends ItemView { private void updateIsAllCollapsed() { boolean isAllCollapsed0 = true; - for (Group group : groups()) { + for (Group group : groups.get()) { if (!group.isCollapsed) { isAllCollapsed0 = false; break; @@ -213,7 +175,7 @@ public class ImageView extends ItemView { getPnlOptions().add(cbPileByOptions); Group group = new Group(""); //add default group - groups().add(group); + groups.get().add(group); getScroller().add(group); } @@ -244,26 +206,26 @@ public class ImageView extends ItemView { cbGroupByOptions.setSelectedItem(groupBy); } - groups().clear(); + groups.get().clear(); if (groupBy == null) { - groups().add(new Group("")); + groups.get().add(new Group("")); btnExpandCollapseAll.updateIsAllCollapsed(); } else { for (String groupName : groupBy.getGroups()) { - groups().add(new Group(groupName)); + groups.get().add(new Group(groupName)); } //collapse all groups by default if all previous groups were collapsed if (btnExpandCollapseAll.isAllCollapsed) { - for (Group group : groups()) { + for (Group group : groups.get()) { group.isCollapsed = true; } } } getScroller().clear(); - for (Group group : groups()) { + for (Group group : groups.get()) { getScroller().add(group); } @@ -354,7 +316,7 @@ public class ImageView extends ItemView { //if not item hovered, use first fully visible item as focal point final float visibleTop = getScrollValue(); - for (Group group : groups()) { + for (Group group : groups.get()) { if (group.getBottom() < visibleTop) { continue; } @@ -369,10 +331,10 @@ public class ImageView extends ItemView { } } } - if (orderedItems().isEmpty()) { + if (orderedItems.get().isEmpty()) { return null; } - return orderedItems().get(0); + return orderedItems.get().get(0); } @Override @@ -382,9 +344,9 @@ public class ImageView extends ItemView { @Override protected void onRefresh() { - Group otherItems = groupBy == null ? groups().get(0) : null; + Group otherItems = groupBy == null ? groups.get().get(0) : null; - for (Group group : groups()) { + for (Group group : groups.get()) { group.items.clear(); } clearSelection(); @@ -397,19 +359,19 @@ public class ImageView extends ItemView { Group group; if (groupIndex >= 0) { - if (groupIndex >= groups().size()) - group = groups().get(groups().size() - 1); + if (groupIndex >= groups.get().size()) + group = groups.get().get(groups.get().size() - 1); else - group = groups().get(groupIndex); + group = groups.get().get(groupIndex); } else { if (otherItems == null) { //reuse existing Other group if possible - if (groups().size() > groupBy.getGroups().length) { - otherItems = groups().get(groups().size() - 1); + if (groups.get().size() > groupBy.getGroups().length) { + otherItems = groups.get().get(groups.get().size() - 1); } else { otherItems = new Group(Forge.getLocalizer().getMessage("lblOther")); otherItems.isCollapsed = btnExpandCollapseAll.isAllCollapsed; - groups().add(otherItems); + groups.get().add(otherItems); } } group = otherItems; @@ -421,10 +383,10 @@ public class ImageView extends ItemView { } } - if (otherItems == null && groups().size() > groupBy.getGroups().length) { - int index = groups().size() - 1; - if (index < groups().size() && index >= 0) - groups().remove(index); //remove Other group if empty + if (otherItems == null && groups.get().size() > groupBy.getGroups().length) { + int index = groups.get().size() - 1; + if (index < groups.get().size() && index >= 0) + groups.get().remove(index); //remove Other group if empty btnExpandCollapseAll.updateIsAllCollapsed(); } @@ -472,8 +434,8 @@ public class ImageView extends ItemView { float dx = itemWidth + gap; float dy = pileBy == null ? itemHeight + gap : itemHeight * PILE_SPACING_Y; - for (int i = 0; i < groups().size(); i++) { - Group group = groups().get(i); + for (int i = 0; i < groups.get().size(); i++) { + Group group = groups.get().get(i); if (forRefresh && pileBy != null) { //refresh piles if needed //use TreeMap to build pile set so iterating below sorts on key @@ -566,8 +528,8 @@ public class ImageView extends ItemView { if (forRefresh) { //refresh ordered items if needed int index = 0; - orderedItems().clear(); - for (Group group : groups()) { + orderedItems.get().clear(); + for (Group group : groups.get()) { if (group.isCollapsed || group.items.isEmpty()) { continue; } @@ -575,7 +537,7 @@ public class ImageView extends ItemView { for (Pile pile : group.piles) { for (ItemInfo itemInfo : pile.items) { itemInfo.index = index++; - orderedItems().add(itemInfo); + orderedItems.get().add(itemInfo); } } } @@ -625,11 +587,11 @@ public class ImageView extends ItemView { private ItemInfo getItemAtPoint(float x, float y) { //check selected items first since they appear on top - for (int i = selectedIndices().size() - 1; i >= 0; i--) { - int currentIndex = selectedIndices().get(i); - if (currentIndex < 0 || orderedItems().size() <= currentIndex) + for (int i = selectedIndices.get().size() - 1; i >= 0; i--) { + int currentIndex = selectedIndices.get().get(i); + if (currentIndex < 0 || orderedItems.get().size() <= currentIndex) continue; - ItemInfo item = orderedItems().get(currentIndex); + ItemInfo item = orderedItems.get().get(currentIndex); float relX = x + item.group.getScrollLeft() - item.group.getLeft(); float relY = y + getScrollValue(); if (item.contains(relX, relY)) { @@ -637,8 +599,8 @@ public class ImageView extends ItemView { } } - for (int i = groups().size() - 1; i >= 0; i--) { - Group group = groups().get(i); + for (int i = groups.get().size() - 1; i >= 0; i--) { + Group group = groups.get().get(i); if (!group.isCollapsed) { for (int j = group.piles.size() - 1; j >= 0; j--) { float relX = x + group.getScrollLeft() - group.getLeft(); @@ -661,14 +623,14 @@ public class ImageView extends ItemView { @Override public T getItemAtIndex(int index) { if (index >= 0 && index < getCount()) { - return orderedItems().get(index).item; + return orderedItems.get().get(index).item; } return null; } @Override public int getIndexOfItem(T item) { - for (Group group : groups()) { + for (Group group : groups.get()) { for (ItemInfo itemInfo : group.items) { if (itemInfo.item == item) { //if group containing item is collapsed, expand it so the item can be selected and has a valid index @@ -687,22 +649,22 @@ public class ImageView extends ItemView { @Override public int getSelectedIndex() { - return selectedIndices().isEmpty() ? -1 : selectedIndices().get(0); + return selectedIndices.get().isEmpty() ? -1 : selectedIndices.get().get(0); } @Override public Iterable getSelectedIndices() { - return selectedIndices(); + return selectedIndices.get(); } @Override public int getCount() { - return orderedItems().size(); + return orderedItems.get().size(); } @Override public int getSelectionCount() { - return selectedIndices().size(); + return selectedIndices.get().size(); } @Override @@ -730,7 +692,7 @@ public class ImageView extends ItemView { @Override public void selectAll() { clearSelection(); - IntStream.range(0, getCount()).forEach(selectedIndices()::add); + IntStream.range(0, getCount()).forEach(selectedIndices.get()::add); updateSelection(); onSelectionChange(); } @@ -738,7 +700,7 @@ public class ImageView extends ItemView { @Override protected void onSetSelectedIndex(int index) { clearSelection(); - selectedIndices().add(index); + selectedIndices.get().add(index); updateSelection(); } @@ -746,24 +708,24 @@ public class ImageView extends ItemView { protected void onSetSelectedIndices(Iterable indices) { clearSelection(); for (Integer index : indices) { - selectedIndices().add(index); + selectedIndices.get().add(index); } updateSelection(); } private void clearSelection() { int count = getCount(); - for (Integer i : selectedIndices()) { + for (Integer i : selectedIndices.get()) { if (i < count) { - orderedItems().get(i).selected = false; + orderedItems.get().get(i).selected = false; } } - selectedIndices().clear(); + selectedIndices.get().clear(); } private void updateSelection() { - for (Integer i : selectedIndices()) { - orderedItems().get(i).selected = true; + for (Integer i : selectedIndices.get()) { + orderedItems.get().get(i).selected = true; } } @@ -779,9 +741,9 @@ public class ImageView extends ItemView { } if (item.selected) { //unselect item if already selected - if (selectedIndices().size() > minSelections) { + if (selectedIndices.get().size() > minSelections) { item.selected = false; - selectedIndices().remove((Object) item.index); + selectedIndices.get().remove((Object) item.index); onSelectionChange(); item.group.scrollIntoView(item); } @@ -790,8 +752,8 @@ public class ImageView extends ItemView { if (maxSelections <= 1 || (!KeyInputAdapter.isCtrlKeyDown() && !KeyInputAdapter.isShiftKeyDown())) { clearSelection(); } - if (selectedIndices().size() < maxSelections) { - selectedIndices().add(0, item.index); + if (selectedIndices.get().size() < maxSelections) { + selectedIndices.get().add(0, item.index); item.selected = true; onSelectionChange(); item.group.scrollIntoView(item); @@ -802,29 +764,29 @@ public class ImageView extends ItemView { @Override public void scrollSelectionIntoView() { - if (selectedIndices().isEmpty()) { + if (selectedIndices.get().isEmpty()) { return; } - int index = selectedIndices().get(0); - if (index < 0 || orderedItems().size() <= index) { + int index = selectedIndices.get().get(0); + if (index < 0 || orderedItems.get().size() <= index) { return; } - ItemInfo itemInfo = orderedItems().get(index); + ItemInfo itemInfo = orderedItems.get().get(index); getScroller().scrollIntoView(itemInfo); } @Override public Rectangle getSelectionBounds() { - if (selectedIndices().isEmpty()) { + if (selectedIndices.get().isEmpty()) { return new Rectangle(); } - int index = selectedIndices().get(0); - if (index < 0 || orderedItems().size() <= index) { + int index = selectedIndices.get().get(0); + if (index < 0 || orderedItems.get().size() <= index) { return new Rectangle(); } - ItemInfo itemInfo = orderedItems().get(index); + ItemInfo itemInfo = orderedItems.get().get(index); Vector2 relPos = itemInfo.group.getChildRelativePosition(itemInfo); return new Rectangle(itemInfo.group.screenPos.x + relPos.x - SEL_BORDER_SIZE + itemInfo.group.getLeft(), itemInfo.group.screenPos.y + relPos.y - SEL_BORDER_SIZE, @@ -833,21 +795,21 @@ public class ImageView extends ItemView { @Override public void zoomSelected() { - if (selectedIndices().isEmpty()) { + if (selectedIndices.get().isEmpty()) { return; } - int index = selectedIndices().get(0); - if (index < 0 || orderedItems().size() <= index) { + int index = selectedIndices.get().get(0); + if (index < 0 || orderedItems.get().size() <= index) { return; } - ItemInfo itemInfo = orderedItems().get(index); + ItemInfo itemInfo = orderedItems.get().get(index); if (itemInfo != null) { if (itemInfo.getKey() instanceof CardThemedDeckGenerator || itemInfo.getKey() instanceof CommanderDeckGenerator || itemInfo.getKey() instanceof ArchetypeDeckGenerator || itemInfo.getKey() instanceof DeckProxy) { FDeckViewer.show(((DeckProxy) itemInfo.getKey()).getDeck()); } - CardZoom.show(orderedItems(), orderedItems().indexOf(itemInfo), itemManager); + CardZoom.show(orderedItems.get(), orderedItems.get().indexOf(itemInfo), itemManager); } } @@ -977,7 +939,7 @@ public class ImageView extends ItemView { FDeckViewer.show(((DeckProxy) item.getKey()).getDeck()); return true; } - CardZoom.show(orderedItems(), orderedItems().indexOf(item), itemManager); + CardZoom.show(orderedItems.get(), orderedItems.get().indexOf(item), itemManager); return true; } return false; diff --git a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java index 0df3913311b..81a77a05234 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java @@ -22,53 +22,28 @@ import forge.gui.GuiBase; import forge.screens.match.MatchController; import forge.toolbox.FCardPanel; import forge.toolbox.FDisplayObject; +import forge.util.Lazy; import forge.util.ThreadUtil; import io.sentry.Sentry; public abstract class VCardDisplayArea extends VDisplayArea implements ActivateHandler { private static final float CARD_STACK_OFFSET = 0.2f; - protected List _orderedCards; - protected List orderedCards() { - List result = _orderedCards; - if (result == null) { - synchronized (this) { - result = _orderedCards; - if (result == null) { - result = new ArrayList<>(); - _orderedCards = result; - } - } - } - return _orderedCards; - } - protected List _cardPanels; - protected List cardPanels() { - List result = _cardPanels; - if (result == null) { - synchronized (this) { - result = _cardPanels; - if (result == null) { - result = new ArrayList<>(); - _cardPanels = result; - } - } - } - return _cardPanels; - } + protected Lazy> orderedCards = Lazy.of(ArrayList::new); + protected Lazy> cardPanels = Lazy.of(ArrayList::new); private boolean rotateCards180; public Iterable getOrderedCards() { - return orderedCards(); + return orderedCards.get(); } public Iterable getCardPanels() { - return cardPanels(); + return cardPanels.get(); } @Override public int getCount() { - return cardPanels().size(); + return cardPanels.get().size(); } @Override @@ -85,15 +60,15 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH for (CardView card : model) { CardAreaPanel cardPanel = CardAreaPanel.get(card); addCardPanelToDisplayArea(cardPanel); - cardPanels().add(cardPanel); - if (newCardPanel == null && !orderedCards().contains(card)) { + cardPanels.get().add(cardPanel); + if (newCardPanel == null && !orderedCards.get().contains(card)) { newCardPanel = cardPanel; } } } if (isVisible()) { //only revalidate if currently visible revalidate(); - + if (newCardPanel != null) { //if new cards added, ensure first new card is scrolled into view scrollIntoView(newCardPanel); } @@ -105,7 +80,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH if (isVisible() == b0) { return; } super.setVisible(b0); if (b0) { //when zone becomes visible, ensure display area of panels is updated and panels layed out - for (CardAreaPanel pnl : cardPanels()) { + for (CardAreaPanel pnl : cardPanels.get()) { pnl.displayArea = this; } revalidate(); @@ -140,7 +115,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH CardPanelContainer.this.remove(CardPanel.getDragAnimationPanel()); CardPanelContainer.this.setMouseDragPanel(null); }*/ - cardPanels().remove(fromPanel); + cardPanels.get().remove(fromPanel); remove(fromPanel); } @@ -151,14 +126,14 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH @Override public void clear() { super.clear(); - if (!cardPanels().isEmpty()) { - for (CardAreaPanel panel : cardPanels()) { + if (!cardPanels.get().isEmpty()) { + for (CardAreaPanel panel : cardPanels.get()) { if (panel.displayArea == null || panel.displayArea == this || - !panel.displayArea.cardPanels().contains(panel)) { //don't reset if panel's displayed in another area already + !panel.displayArea.cardPanels.get().contains(panel)) { //don't reset if panel's displayed in another area already panel.reset(); } } - cardPanels().clear(); + cardPanels.get().clear(); } } @@ -176,7 +151,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH } } - orderedCards().add(cardPanel.getCard()); + orderedCards.get().add(cardPanel.getCard()); cardPanel.setBounds(x, y, cardWidth, cardHeight); if (cardPanel.getNextPanelInStack() != null) { //add next panel in stack if needed @@ -195,14 +170,14 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH @Override protected ScrollBounds layoutAndGetScrollBounds(float visibleWidth, float visibleHeight) { - orderedCards().clear(); + orderedCards.get().clear(); float x = 0; float y = 0; float cardHeight = visibleHeight; float cardWidth = getCardWidth(cardHeight); - for (CardAreaPanel cardPanel : new ArrayList<>(cardPanels())) { + for (CardAreaPanel cardPanel : new ArrayList<>(cardPanels.get())) { if (cardPanel != null) { int count = addCards(cardPanel, x, y, cardWidth, cardHeight); x += cardWidth + (count - 1) * cardWidth * CARD_STACK_OFFSET; @@ -223,8 +198,8 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH public String getActivateAction(int index) { if(!GuiBase.isNetworkplay()) { //causes lag on netplay client side, also index shouldn't be out of bounds - if (index >= 0 && index < orderedCards().size()) - return MatchController.instance.getGameController().getActivateDescription(orderedCards().get(index)); + if (index >= 0 && index < orderedCards.get().size()) + return MatchController.instance.getGameController().getActivateDescription(orderedCards.get().get(index)); } return Forge.getLocalizer().getMessage("lblActivateAction"); //simple text on card zoom swipe up @@ -233,15 +208,15 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH @Override public void setSelectedIndex(int index) { //just scroll card into view - if (index < orderedCards().size()) { - final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards().get(index)); + if (index < orderedCards.get().size()) { + final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards.get().get(index)); scrollIntoView(cardPanel); } } @Override public void activate(int index) { - final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards().get(index)); + final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards.get().get(index)); //must invoke in game thread in case a dialog needs to be shown ThreadUtil.invokeInGameThread(() -> cardPanel.selectCard(false)); } @@ -449,7 +424,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH public void showZoom() { if (displayArea == null) { return; } - final List cards = displayArea.orderedCards(); + final List cards = displayArea.orderedCards.get(); CardZoom.show(cards, cards.indexOf(getCard()), displayArea); } diff --git a/forge-gui-mobile/src/forge/screens/match/views/VZoneDisplay.java b/forge-gui-mobile/src/forge/screens/match/views/VZoneDisplay.java index 0f4b2b074f4..02f5dde97c3 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VZoneDisplay.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VZoneDisplay.java @@ -37,11 +37,11 @@ public class VZoneDisplay extends VCardDisplayArea { float y = screenToLocalY(screenY); if (revealedPanel.contains(x, y)) { return; } - int idx = cardPanels().size() - 1; + int idx = cardPanels.get().size() - 1; for (int i = getChildCount() - 2; i >= 0; i--) { final FDisplayObject cardPanel = getChildAt(i); if (cardPanel.contains(x, y)) { - idx = cardPanels().indexOf(cardPanel); + idx = cardPanels.get().indexOf(cardPanel); break; } } @@ -54,9 +54,9 @@ public class VZoneDisplay extends VCardDisplayArea { if (revealedPanel == null) { //if no overlapping panels, just pan scroll as normal return super.pan(x, y, deltaX, deltaY, moreVertical); } - int idx = cardPanels().size() - 1; + int idx = cardPanels.get().size() - 1; for (int i = idx - 1; i >= 0; i--) { - if (cardPanels().get(i).contains(x, y)) { + if (cardPanels.get().get(i).contains(x, y)) { idx = i; break; } @@ -66,15 +66,15 @@ public class VZoneDisplay extends VCardDisplayArea { } private void setRevealedPanel(int idx) { - if (idx >= 0 && idx < cardPanels().size()) - revealedPanel = cardPanels().get(idx); + if (idx >= 0 && idx < cardPanels.get().size()) + revealedPanel = cardPanels.get().get(idx); else return; clearChildren(); if (Forge.isLandscapeMode()) { //for landscape mode, just show revealed card on top - for (CardAreaPanel cardPanel : cardPanels()) { + for (CardAreaPanel cardPanel : cardPanels.get()) { if (cardPanel != revealedPanel) { add(cardPanel); } @@ -82,16 +82,16 @@ public class VZoneDisplay extends VCardDisplayArea { } else { //for portrait mode, cascade cards back from revealed panel - int maxIdx = cardPanels().size() - 1; + int maxIdx = cardPanels.get().size() - 1; int offset = Math.max(idx, maxIdx - idx); for (int i = offset; i > 0; i--) { int idx1 = idx - i; int idx2 = idx + i; if (idx1 >= 0) { - add(cardPanels().get(idx1)); + add(cardPanels.get().get(idx1)); } if (idx2 <= maxIdx) { - add(cardPanels().get(idx2)); + add(cardPanels.get().get(idx2)); } } } @@ -122,7 +122,7 @@ public class VZoneDisplay extends VCardDisplayArea { return new ScrollBounds(visibleWidth, visibleHeight); } - orderedCards().clear(); + orderedCards.get().clear(); if (Forge.isLandscapeMode() && layoutVerticallyForLandscapeMode()) { return layoutAndGetScrollBoundsLandscape(visibleWidth, visibleHeight); @@ -134,22 +134,22 @@ public class VZoneDisplay extends VCardDisplayArea { float cardWidth = getCardWidth(cardHeight); float dx = cardWidth; - float totalWidth = cardWidth * cardPanels().size(); + float totalWidth = cardWidth * cardPanels.get().size(); if (totalWidth > visibleWidth && totalWidth <= visibleWidth * 2) { //allow overlapping cards up to one half of the card, //otherwise don't overlap and allow scrolling horizontally dx *= (visibleWidth - cardWidth) / (totalWidth - cardWidth); - dx += FCardPanel.PADDING / cardPanels().size(); //make final card go right up to right edge of screen + dx += FCardPanel.PADDING / cardPanels.get().size(); //make final card go right up to right edge of screen if (revealedPanel == null) { - revealedPanel = cardPanels().get(cardPanels().size() - 1); + revealedPanel = cardPanels.get().get(cardPanels.get().size() - 1); } } else { revealedPanel = null; } - for (CardAreaPanel cardPanel : cardPanels()) { - orderedCards().add(cardPanel.getCard()); + for (CardAreaPanel cardPanel : cardPanels.get()) { + orderedCards.get().add(cardPanel.getCard()); cardPanel.setBounds(x, y, cardWidth, cardHeight); x += dx; } @@ -165,7 +165,7 @@ public class VZoneDisplay extends VCardDisplayArea { float dy = cardHeight; float scrollHeight; - int rowCount = (int)Math.ceil((float)cardPanels().size() / 2f); + int rowCount = (int)Math.ceil((float)cardPanels.get().size() / 2f); float totalHeight = cardHeight * rowCount; if (totalHeight > visibleHeight && totalHeight <= visibleHeight * 3) { //allow overlapping cards up to one third of the card, @@ -173,7 +173,7 @@ public class VZoneDisplay extends VCardDisplayArea { dy *= (visibleHeight - cardHeight) / (totalHeight - cardHeight); dy += FCardPanel.PADDING / rowCount; //make final card go right up to right edge of screen if (revealedPanel == null) { - revealedPanel = cardPanels().get(cardPanels().size() - 1); + revealedPanel = cardPanels.get().get(cardPanels.get().size() - 1); } scrollHeight = visibleHeight; } @@ -182,10 +182,10 @@ public class VZoneDisplay extends VCardDisplayArea { scrollHeight = rowCount * dy; } - for (CardAreaPanel cardPanel : cardPanels()) { - orderedCards().add(cardPanel.getCard()); + for (CardAreaPanel cardPanel : cardPanels.get()) { + orderedCards.get().add(cardPanel.getCard()); cardPanel.setBounds(x, y, cardWidth, cardHeight); - if (orderedCards().size() % 2 == 0) { + if (orderedCards.get().size() % 2 == 0) { x = 0; y += dy; } From 4b63fd8dc1545fd030f8c342e39268b3e05f9854 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Mon, 25 Nov 2024 15:27:43 +0100 Subject: [PATCH 114/152] Update SeekEffect.java Fix StringBuilder.isEmpty to length() != 0 --- .../src/main/java/forge/game/ability/effects/SeekEffect.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java index 618b1f57991..fc5b97f97a8 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java @@ -67,7 +67,7 @@ public class SeekEffect extends SpellAbilityEffect { pool = CardLists.getValidCards(pool, seekType, source.getController(), source, sa); } if (pool.isEmpty()) { - if (!notify.isEmpty()) notify.append("\r\n"); + if (!notify.length() != 0) notify.append("\r\n"); notify.append(Localizer.getInstance().getMessage("lblSeekFailed", seekType)); continue; // can't find if nothing to seek } @@ -88,7 +88,7 @@ public class SeekEffect extends SpellAbilityEffect { } } - if (!notify.isEmpty()) { + if (!notify.length() != 0) { game.getAction().notifyOfValue(sa, source, notify.toString(), null); } if (!soughtCards.isEmpty()) { From 451f12826f21e76dc9757b1b9ba35daa47cdf3f8 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Mon, 25 Nov 2024 15:31:27 +0100 Subject: [PATCH 115/152] Update SeekEffect.java Fix --- .../src/main/java/forge/game/ability/effects/SeekEffect.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java index fc5b97f97a8..7edc4a5806a 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java @@ -67,7 +67,7 @@ public class SeekEffect extends SpellAbilityEffect { pool = CardLists.getValidCards(pool, seekType, source.getController(), source, sa); } if (pool.isEmpty()) { - if (!notify.length() != 0) notify.append("\r\n"); + if (notify.length() != 0) notify.append("\r\n"); notify.append(Localizer.getInstance().getMessage("lblSeekFailed", seekType)); continue; // can't find if nothing to seek } @@ -88,7 +88,7 @@ public class SeekEffect extends SpellAbilityEffect { } } - if (!notify.length() != 0) { + if (notify.length() != 0) { game.getAction().notifyOfValue(sa, source, notify.toString(), null); } if (!soughtCards.isEmpty()) { From c3fd663b4945a646be9a8d3a7656a66747733147 Mon Sep 17 00:00:00 2001 From: Agetian Date: Mon, 25 Nov 2024 18:32:19 +0300 Subject: [PATCH 116/152] - Only mark Resizable as false on non-fullscreen setups, helps avoid corner cases when fullscreen fails on Linux (e.g. on multiple monitor setups) (#6627) --- forge-gui-mobile-dev/src/forge/app/GameLauncher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui-mobile-dev/src/forge/app/GameLauncher.java b/forge-gui-mobile-dev/src/forge/app/GameLauncher.java index a924eaee7da..8c6e1dd19da 100644 --- a/forge-gui-mobile-dev/src/forge/app/GameLauncher.java +++ b/forge-gui-mobile-dev/src/forge/app/GameLauncher.java @@ -25,7 +25,6 @@ public class GameLauncher { Configuration.GLFW_LIBRARY_NAME.set("glfw_async"); } Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); - config.setResizable(false); ApplicationListener start = Forge.getApp(new Lwjgl3Clipboard(), new Main.DesktopAdapter(switchOrientationFile),//todo get totalRAM && isTabletDevice assetsDir, false, false, 0, false, 0, "", ""); if (Config.instance().getSettingData().fullScreen) { @@ -34,6 +33,7 @@ public class GameLauncher { config.setHdpiMode(HdpiMode.Logical); } else { config.setWindowedMode(Config.instance().getSettingData().width, Config.instance().getSettingData().height); + config.setResizable(false); } config.setTitle("Forge - " + versionString); config.setWindowListener(new Lwjgl3WindowAdapter() { From e3e2429acb1c0049487cd75e8702b935511e598d Mon Sep 17 00:00:00 2001 From: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:32:03 +0000 Subject: [PATCH 117/152] Play booster updates: FDN, MH3 --- forge-gui/res/editions/Foundations.txt | 18 +++++++++++++++--- forge-gui/res/editions/Modern Horizons 3.txt | 11 +++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/forge-gui/res/editions/Foundations.txt b/forge-gui/res/editions/Foundations.txt index bc81c36e56a..45a2dce99d7 100644 --- a/forge-gui/res/editions/Foundations.txt +++ b/forge-gui/res/editions/Foundations.txt @@ -11,10 +11,10 @@ Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 [Common] -Base=Common:fromSheet("FDN cards") +Base=Common:!fromSheet("FDN gain lands"):fromSheet("FDN cards") [Common-Guest] -Base=Common:fromSheet("FDN cards") +Base=Common:!fromSheet("FDN gain lands"):fromSheet("FDN cards") Replace=.015625F fromSheet("FDN special guests") [Uncommon] @@ -26,7 +26,7 @@ Replace=.077F Rare:fromSheet("FDN borderless") Replace=.015F Mythic:fromSheet("FDN borderless") [AnyLand] -Base=Land:Common:fromSheet("FDN cards") +Base=fromSheet("FDN gain lands") Replace=.25F Land:fromSheet("FDN full art") Replace=.25F BasicLand:fromSheet("FDN cards") @@ -784,6 +784,18 @@ Replace=.024F Uncommon:fromSheet("FDN borderless") 728 R Phyrexian Arena @Aaron J. Riley 729 R Solemn Simulacrum @Forrest Imel +[gain lands] +1 Bloodfell Caves|FDN +1 Blossoming Sands|FDN +1 Dismal Backwater|FDN +1 Jungle Hollow|FDN +1 Rugged Highlands|FDN +1 Scoured Barrens|FDN +1 Swiftwater Cliffs|FDN +1 Thornwood Falls|FDN +1 Tranquil Cove|FDN +1 Wind-Scarred Crag|FDN + [special guests] 1 Akroma's Memorial|SPG 1 Bloom Tender|SPG diff --git a/forge-gui/res/editions/Modern Horizons 3.txt b/forge-gui/res/editions/Modern Horizons 3.txt index 7bfa0cd196c..229b9748a13 100644 --- a/forge-gui/res/editions/Modern Horizons 3.txt +++ b/forge-gui/res/editions/Modern Horizons 3.txt @@ -46,6 +46,7 @@ Replace=.417F Uncommon:fromSheet("MH3 cards") Replace=.078F RareMythic:fromSheet("MH3 cards") Replace=.004F fromSheet("MH3 borderless frame") Replace=.042F fromSheet("MH3 alternate frame") +Replace=.042F fromSheet("MH3 commanders") [cards] 1 U Breaker of Creation @Yohann Schepacz @@ -648,6 +649,16 @@ A193 R A-Nadu, Winged Wisdom @Daren Bader 1 Fury|SPG 1 Endurance|SPG +[commanders] +1 Disa the Restless|M3C +1 Omo, Queen of Vesuva|M3C +1 Satya, Aetherflux Genius|M3C +1 Ulalek, Fused Atrocity|M3C +1 Azlask, the Swelling Scourge|M3C +1 Cayth, Famed Mechanist|M3C +1 Coram, the Undertaker|M3C +1 Jyoti, Moag Ancient|M3C + [tokens] b_0_0_phyrexian_germ b_0_0_zombie_army From cf8c206a737d5dd003901dea3b35afa9297efd46 Mon Sep 17 00:00:00 2001 From: Agetian Date: Mon, 25 Nov 2024 19:12:12 +0300 Subject: [PATCH 118/152] - Add puzzle PS_FDN1. (#6629) --- forge-gui/res/puzzle/PS_FDN1.pzl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 forge-gui/res/puzzle/PS_FDN1.pzl diff --git a/forge-gui/res/puzzle/PS_FDN1.pzl b/forge-gui/res/puzzle/PS_FDN1.pzl new file mode 100644 index 00000000000..d5d2e241d1b --- /dev/null +++ b/forge-gui/res/puzzle/PS_FDN1.pzl @@ -0,0 +1,19 @@ +[metadata] +Name:Possibility Storm - Foundations #01 +URL:https://i1.wp.com/www.possibilitystorm.com/wp-content/uploads/2024/11/latest-scaled.jpg?ssl=1 +Goal:Win +Turns:1 +Difficulty:Mythic +Description:Win this turn. Ensure your solution satisfies all possible blocking decisions. You have exactly 26 cards remaining in your library. Assume that any of the cards you or your opponent could draw are irrelevant to the puzzle. Good luck! +[state] +turn=1 +activeplayer=p0 +activephase=MAIN1 +p0life=5 +p0hand=Molten Duplication;Lightning Strike;Boltwave;Twinflame Tyrant;Warleader's Call +p0library=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 +p0graveyard=Electroduplicate +p0battlefield=Stormsplitter;Fear of Missing Out;Ghired, Mirror of the Wilds;Mountain;Mountain;Mountain;Forgotten Monument;Forgotten Monument;Forgotten Monument +p1life=27 +p0library=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 +p1battlefield=Tomik, Wielder of Law;Soul Warden;Soul Warden From 3e26f859dc56d7db6bfed551f377a8d1571091fe Mon Sep 17 00:00:00 2001 From: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:27:39 +0000 Subject: [PATCH 119/152] Edition updates: HHO --- forge-gui/res/editions/Happy Holidays.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/forge-gui/res/editions/Happy Holidays.txt b/forge-gui/res/editions/Happy Holidays.txt index c9dd6421a93..b347174ed40 100644 --- a/forge-gui/res/editions/Happy Holidays.txt +++ b/forge-gui/res/editions/Happy Holidays.txt @@ -25,6 +25,7 @@ ScryfallCode=HHO 21 M Last-Minute Chopping @Marta Nael 22 M Chaos Wrap @Zoltan Boros 23 M Seasonal Sequels @Adrián Rodríguez Pérez +24 M Eggnogger's 'Stache @Arif Wijaya [tokens] c_a_treasure_sac From 35173eb55ecedbc3a2c2fe97c2281fe7a0f4d59a Mon Sep 17 00:00:00 2001 From: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com> Date: Mon, 25 Nov 2024 20:01:28 +0000 Subject: [PATCH 120/152] Edition updates: PIO --- forge-gui/res/editions/Pioneer Masters.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 forge-gui/res/editions/Pioneer Masters.txt diff --git a/forge-gui/res/editions/Pioneer Masters.txt b/forge-gui/res/editions/Pioneer Masters.txt new file mode 100644 index 00000000000..43c9c6dd8bd --- /dev/null +++ b/forge-gui/res/editions/Pioneer Masters.txt @@ -0,0 +1,22 @@ +[metadata] +Code=PIO +Date=2024-12-10 +Name=Pioneer Masters +Type=Reprint +ScryfallCode=PIO + +[cards] +0 R Silence @Wayne Reynolds +0 U Hidden Strings @Daarken +0 M Jace, Vryn's Prodigy @Jaime Jones +0 M Liliana, Heretical Healer @Karla Ortiz +0 R Tasigur, the Golden Fang @Chris Rahn +0 R Legion Loyalist @Eric Deschamps +0 M Purphoros, God of the Forge @Eric Deschamps +0 C Gladecover Scout @Brian Valeza +0 R Hornet Nest @Adam Paquette +0 R Sylvan Caryatid @Chase Stone +0 M Athreos, God of Passage @Ryan Barger +0 R Bring to Light @Jonas De Ro +0 M Mogis, God of Slaughter @Chase Stone +0 R Urborg, Tomb of Yawgmoth @John Avon From e1d7c4a4295d47103bd35ae6ca45f92255b2f4a6 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Tue, 26 Nov 2024 05:48:21 +0800 Subject: [PATCH 121/152] use Guava memoize --- .../src/main/java/forge/ai/AiCardMemory.java | 5 +- forge-core/src/main/java/forge/util/Lazy.java | 46 ------------------- .../java/forge/util/collect/FCollection.java | 7 +-- .../main/java/forge/game/combat/Combat.java | 19 ++++---- .../forge/game/spellability/SpellAbility.java | 8 ++-- .../src/forge/assets/ImageCache.java | 7 +-- .../src/forge/itemmanager/ItemManager.java | 9 ++-- .../forge/itemmanager/views/ImageView.java | 9 ++-- .../screens/match/views/VCardDisplayArea.java | 7 +-- 9 files changed, 38 insertions(+), 79 deletions(-) delete mode 100644 forge-core/src/main/java/forge/util/Lazy.java diff --git a/forge-ai/src/main/java/forge/ai/AiCardMemory.java b/forge-ai/src/main/java/forge/ai/AiCardMemory.java index 87bcc7f56a4..07994a4cf18 100644 --- a/forge-ai/src/main/java/forge/ai/AiCardMemory.java +++ b/forge-ai/src/main/java/forge/ai/AiCardMemory.java @@ -21,11 +21,12 @@ package forge.ai; import java.util.Map; import java.util.Set; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import forge.game.card.Card; import forge.game.player.Player; -import forge.util.Lazy; /** *

@@ -65,7 +66,7 @@ public class AiCardMemory { REVEALED_CARDS // These cards were recently revealed to the AI by a call to PlayerControllerAi.reveal } - private final Lazy>> memoryMap = Lazy.of(Maps::newConcurrentMap); + private final Supplier>> memoryMap = Suppliers.memoize(Maps::newConcurrentMap); public AiCardMemory() { } diff --git a/forge-core/src/main/java/forge/util/Lazy.java b/forge-core/src/main/java/forge/util/Lazy.java deleted file mode 100644 index 2bc57bb8d65..00000000000 --- a/forge-core/src/main/java/forge/util/Lazy.java +++ /dev/null @@ -1,46 +0,0 @@ -package forge.util; - -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -/*https://github.com/pivovarit/articles/tree/master/java-lazy-initialization*/ -public final class Lazy implements Supplier { - private transient Supplier supplier; - private volatile T value; - - public Lazy(Supplier supplier) { - this.supplier = Objects.requireNonNull(supplier); - } - - @Override - public T get() { - if (value == null) { - synchronized (this) { - if (value == null) { - value = Objects.requireNonNull(supplier.get()); - supplier = null; - } - } - } - return value; - } - - public Lazy map(Function mapper) { - return new Lazy<>(() -> mapper.apply(this.get())); - } - - public Lazy flatMap(Function> mapper) { - return new Lazy<>(() -> mapper.apply(this.get()).get()); - } - - public Lazy> filter(Predicate predicate) { - return new Lazy<>(() -> Optional.of(get()).filter(predicate)); - } - - public static Lazy of(Supplier supplier) { - return new Lazy<>(supplier); - } -} \ No newline at end of file diff --git a/forge-core/src/main/java/forge/util/collect/FCollection.java b/forge-core/src/main/java/forge/util/collect/FCollection.java index 885beb74b1f..d86c2a2b907 100644 --- a/forge-core/src/main/java/forge/util/collect/FCollection.java +++ b/forge-core/src/main/java/forge/util/collect/FCollection.java @@ -13,7 +13,8 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.function.Predicate; -import forge.util.Lazy; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import org.apache.commons.lang3.ArrayUtils; import com.google.common.collect.ImmutableList; @@ -45,12 +46,12 @@ public class FCollection implements List, /*Set,*/ FCollectionView, /** * The {@link Set} representation of this collection. */ - private final Lazy> set = Lazy.of(Sets::newHashSet); + private final Supplier> set = Suppliers.memoize(Sets::newHashSet); /** * The {@link List} representation of this collection. */ - private final Lazy> list = Lazy.of(Lists::newLinkedList); + private final Supplier> list = Suppliers.memoize(Lists::newLinkedList); /** * Create an empty {@link FCollection}. 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 5dc49252613..16a146c96f1 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -17,6 +17,8 @@ */ package forge.game.combat; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.collect.*; import forge.game.*; import forge.game.ability.AbilityKey; @@ -32,7 +34,6 @@ import forge.game.staticability.StaticAbilityAssignCombatDamageAsUnblocked; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; import forge.util.CardTranslation; -import forge.util.Lazy; import forge.util.Localizer; import forge.util.collect.FCollection; import forge.util.collect.FCollectionView; @@ -54,17 +55,17 @@ public class Combat { private boolean legacyOrderCombatants; private AttackConstraints attackConstraints; // Defenders, as they are attacked by hostile forces - private final Lazy> attackableEntries = Lazy.of(FCollection::new); + private final Supplier> attackableEntries = Suppliers.memoize(FCollection::new); // Keyed by attackable defender (player or planeswalker or battle) - private final Lazy> attackedByBands = Lazy.of(() -> Multimaps.synchronizedMultimap(ArrayListMultimap.create())); - private final Lazy> blockedBands = Lazy.of(() -> Multimaps.synchronizedMultimap(ArrayListMultimap.create())); - private final Lazy> attackersOrderedForDamageAssignment = Lazy.of(Maps::newHashMap); - private final Lazy> blockersOrderedForDamageAssignment = Lazy.of(Maps::newHashMap); - private final Lazy lkiCache = Lazy.of(CardCollection::new); - private final Lazy damageMap = Lazy.of(CardDamageMap::new); + private final Supplier> attackedByBands = Suppliers.memoize(() -> Multimaps.synchronizedMultimap(ArrayListMultimap.create())); + private final Supplier> blockedBands = Suppliers.memoize(() -> Multimaps.synchronizedMultimap(ArrayListMultimap.create())); + private final Supplier> attackersOrderedForDamageAssignment = Suppliers.memoize(Maps::newHashMap); + private final Supplier> blockersOrderedForDamageAssignment = Suppliers.memoize(Maps::newHashMap); + private final Supplier lkiCache = Suppliers.memoize(CardCollection::new); + private final Supplier damageMap = Suppliers.memoize(CardDamageMap::new); // List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW) - private final Lazy combatantsThatDealtFirstStrikeDamage = Lazy.of(CardCollection::new); + private final Supplier combatantsThatDealtFirstStrikeDamage = Suppliers.memoize(CardCollection::new); public Combat(final Player attacker) { playerWhoAttacks = attacker; 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 1a033fd2456..149d35d771e 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -19,6 +19,8 @@ package forge.game.spellability; import java.util.*; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import forge.game.cost.CostSacrifice; import forge.util.*; import org.apache.commons.lang3.ObjectUtils; @@ -143,9 +145,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private TreeBasedTable paidLists = TreeBasedTable.create(); private EnumMap triggeringObjects = AbilityKey.newMap(); private EnumMap replacingObjects = AbilityKey.newMap(); - private final Lazy> pipsToReduce = Lazy.of(ArrayList::new); + private final Supplier> pipsToReduce = Suppliers.memoize(ArrayList::new); private List chosenList = null; - private final Lazy tappedForConvoke = Lazy.of(CardCollection::new); + private final Supplier tappedForConvoke = Suppliers.memoize(CardCollection::new); private Card sacrificedAsOffering; private Card sacrificedAsEmerge; @@ -170,7 +172,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private CardCollection lastStateBattlefield; private CardCollection lastStateGraveyard; - private final Lazy rollbackEffects = Lazy.of(CardCollection::new); + private final Supplier rollbackEffects = Suppliers.memoize(CardCollection::new); private CardDamageMap damageMap; private CardDamageMap preventMap; diff --git a/forge-gui-mobile/src/forge/assets/ImageCache.java b/forge-gui-mobile/src/forge/assets/ImageCache.java index b474c2591c0..294de8d5a37 100644 --- a/forge-gui-mobile/src/forge/assets/ImageCache.java +++ b/forge-gui-mobile/src/forge/assets/ImageCache.java @@ -25,13 +25,14 @@ import java.util.Queue; import java.util.Set; import com.badlogic.gdx.graphics.Pixmap; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.collect.EvictingQueue; import com.google.common.collect.Queues; import com.google.common.collect.Sets; import forge.deck.DeckProxy; import forge.gui.GuiBase; import forge.util.FileUtil; -import forge.util.Lazy; import forge.util.TextUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -71,7 +72,7 @@ import forge.util.ImageUtil; */ public class ImageCache { private static ImageCache imageCache; - private Lazy> missingIconKeys = Lazy.of(HashSet::new); + private Supplier> missingIconKeys = Suppliers.memoize(HashSet::new); private final List borderlessCardlistKey = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE); public int counter = 0; private int maxCardCapacity = 300; //default card capacity @@ -121,7 +122,7 @@ public class ImageCache { return Forge.getAssets().getDefaultImage(); } - private Lazy> imageRecord = Lazy.of(() -> new HashMap<>(maxCardCapacity + (maxCardCapacity / 3))); + private Supplier> imageRecord = Suppliers.memoize(() -> new HashMap<>(maxCardCapacity + (maxCardCapacity / 3))); private boolean imageLoaded, delayLoadRequested; public void allowSingleLoad() { diff --git a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java index 10955310d62..62cce669ccb 100644 --- a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java +++ b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java @@ -23,9 +23,7 @@ import java.util.Map.Entry; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.utils.Align; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; +import com.google.common.base.*; import com.google.common.collect.Iterables; import forge.Forge; @@ -58,7 +56,6 @@ import forge.toolbox.FList; import forge.toolbox.FList.CompactModeHandler; import forge.util.ItemPool; import forge.util.LayoutHelper; -import forge.util.Lazy; public abstract class ItemManager extends FContainer implements IItemManager, ActivateHandler { @@ -68,7 +65,7 @@ public abstract class ItemManager extends FContainer im protected final ItemManagerModel model; private Predicate filterPredicate = null; private AdvancedSearchFilter advancedSearchFilter; - private Lazy>> filters = Lazy.of(ArrayList::new); + private Supplier>> filters = Suppliers.memoize(ArrayList::new); private boolean hideFilters = false; private boolean wantUnique = false; private boolean showRanking = false; @@ -81,7 +78,7 @@ public abstract class ItemManager extends FContainer im private ItemManagerConfig config; private Function, Object> fnNewGet; private boolean viewUpdating, needSecondUpdate; - private Lazy> sortCols = Lazy.of(ArrayList::new); + private Supplier> sortCols = Suppliers.memoize(ArrayList::new); private final TextSearchFilter searchFilter; private CardFormatFilter cardFormatFilter; diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 503c999d8e7..287f68f2377 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -5,6 +5,8 @@ import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Align; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import forge.Forge; import forge.Forge.KeyInputAdapter; import forge.Graphics; @@ -28,7 +30,6 @@ import forge.model.FModel; import forge.toolbox.*; import forge.util.ImageFetcher; import forge.util.ImageUtil; -import forge.util.Lazy; import forge.util.TextUtil; import forge.util.Utils; @@ -66,7 +67,7 @@ public class ImageView extends ItemView { private static final int MIN_COLUMN_COUNT = Forge.isLandscapeMode() ? 2 : 1; private static final int MAX_COLUMN_COUNT = 10; - private Lazy> selectedIndices = Lazy.of(ArrayList::new); + private Supplier> selectedIndices = Suppliers.memoize(ArrayList::new); private int columnCount = 4; private float scrollHeight = 0; private ColumnDef pileBy = null; @@ -74,8 +75,8 @@ public class ImageView extends ItemView { private ItemInfo focalItem; private boolean updatingLayout; private float totalZoomAmount; - private Lazy> orderedItems = Lazy.of(ArrayList::new); - private Lazy> groups = Lazy.of(ArrayList::new); + private Supplier> orderedItems = Suppliers.memoize(ArrayList::new); + private Supplier> groups = Suppliers.memoize(ArrayList::new); private class ExpandCollapseButton extends FLabel { private boolean isAllCollapsed; diff --git a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java index 81a77a05234..9f58e34ed43 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VCardDisplayArea.java @@ -9,6 +9,8 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.math.Vector2; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import forge.Forge; import forge.Graphics; import forge.card.CardRenderer.CardStackPosition; @@ -22,15 +24,14 @@ import forge.gui.GuiBase; import forge.screens.match.MatchController; import forge.toolbox.FCardPanel; import forge.toolbox.FDisplayObject; -import forge.util.Lazy; import forge.util.ThreadUtil; import io.sentry.Sentry; public abstract class VCardDisplayArea extends VDisplayArea implements ActivateHandler { private static final float CARD_STACK_OFFSET = 0.2f; - protected Lazy> orderedCards = Lazy.of(ArrayList::new); - protected Lazy> cardPanels = Lazy.of(ArrayList::new); + protected Supplier> orderedCards = Suppliers.memoize(ArrayList::new); + protected Supplier> cardPanels = Suppliers.memoize(ArrayList::new); private boolean rotateCards180; public Iterable getOrderedCards() { From f76c665e4573958bba367f308e9595fd188ffc6f Mon Sep 17 00:00:00 2001 From: Northmoc <103371817+Northmoc@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:14:52 -0500 Subject: [PATCH 122/152] UNF: eelectrocute.txt + support (#6616) * UNF: eelectrocute.txt + support * tweak * tweak2 --- .../src/main/java/forge/game/ability/AbilityUtils.java | 5 +++++ .../java/forge/game/ability/effects/RollDiceEffect.java | 1 + forge-game/src/main/java/forge/game/player/Player.java | 9 +++++++++ forge-gui/res/cardsfolder/a/adorable_kitten.txt | 1 + forge-gui/res/cardsfolder/e/eelectrocute.txt | 9 +++++++++ 5 files changed, 25 insertions(+) create mode 100644 forge-gui/res/cardsfolder/e/eelectrocute.txt diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index d0e727a2bed..f44797dc8e8 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -2320,6 +2320,11 @@ public class AbilityUtils { return doXMath(player.getNumRollsThisTurn(), expr, c, ctb); } + if (sq[0].startsWith("YouRolledThisTurn")) { + int n = calculateAmount(c, sq[0].substring(17), ctb); + return doXMath(Collections.frequency(player.getDiceRollsThisTurn(), n), expr, c, ctb); + } + if (sq[0].equals("YouSurveilThisTurn")) { return doXMath(player.getSurveilThisTurn(), expr, c, ctb); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java index 44833067e66..35e1b69035a 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java @@ -143,6 +143,7 @@ public class RollDiceEffect extends SpellAbilityEffect { StringUtils.join(ignored, ", "))); } player.getGame().getAction().notifyOfValue(sa, player, sb.toString(), null); + player.addDieRollThisTurn(naturalRolls); } List rolls = Lists.newArrayList(); 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 761b2b1244d..47a593bd760 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -100,6 +100,7 @@ public class Player extends GameEntity implements Comparable { private int lifeGainedTimesThisTurn; private int lifeGainedByTeamThisTurn; private int committedCrimeThisTurn; + private List diceRollsThisTurn = Lists.newArrayList(); private int expentThisTurn; private int numManaShards; private int numPowerSurgeLands; @@ -2520,6 +2521,7 @@ public class Player extends GameEntity implements Comparable { setNumManaConversion(0); setCommitedCrimeThisTurn(0); + diceRollsThisTurn = Lists.newArrayList(); setExpentThisTurn(0); damageReceivedThisTurn.clear(); @@ -3930,6 +3932,13 @@ public class Player extends GameEntity implements Comparable { committedCrimeThisTurn = v; } + public List getDiceRollsThisTurn() { + return diceRollsThisTurn; + } + public void addDieRollThisTurn(List rolls) { + diceRollsThisTurn.addAll(rolls); + } + public int getExpentThisTurn() { return expentThisTurn; } diff --git a/forge-gui/res/cardsfolder/a/adorable_kitten.txt b/forge-gui/res/cardsfolder/a/adorable_kitten.txt index f3784f5ca27..8570a54f29f 100644 --- a/forge-gui/res/cardsfolder/a/adorable_kitten.txt +++ b/forge-gui/res/cardsfolder/a/adorable_kitten.txt @@ -5,4 +5,5 @@ PT:1/1 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRoll | Host$ True | TriggerDescription$ When this creature enters, roll a six-sided die. You gain life equal to the result. SVar:TrigRoll:DB$ RollDice | ResultSVar$ Result | SubAbility$ DBLife SVar:DBLife:DB$ GainLife | LifeAmount$ Result +DeckHas:Ability$LifeGain Oracle:When this creature enters, roll a six-sided die. You gain life equal to the result. diff --git a/forge-gui/res/cardsfolder/e/eelectrocute.txt b/forge-gui/res/cardsfolder/e/eelectrocute.txt new file mode 100644 index 00000000000..6e08c96cba3 --- /dev/null +++ b/forge-gui/res/cardsfolder/e/eelectrocute.txt @@ -0,0 +1,9 @@ +Name:Eelectrocute +ManaCost:1 R +Types:Instant +A:SP$ DealDamage | ValidTgts$ Any | NumDmg$ 2 | SpellDescription$ CARDNAME deals 2 damage to any target. +S:Mode$ Continuous | Affected$ Card.Self | AffectedZone$ Graveyard | EffectZone$ Graveyard | MayPlay$ True | CheckSVar$ Count$YouRolledThisTurn6 | Description$ You may cast CARDNAME from your graveyard as long as you've rolled a 6 this turn. If you cast CARDNAME this way and it would be put into your graveyard, exile it instead. +R:Event$ Moved | ValidLKI$ Card.CastSa Spell.MayPlaySource | Origin$ Stack | Destination$ Graveyard | ReplaceWith$ MoveExile +SVar:MoveExile:DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Stack | Destination$ Exile +DeckHas:Ability$Graveyard +Oracle:Eelectrocute deals 2 damage to any target.\nYou may cast Eelectrocute from your graveyard as long as you've rolled a 6 this turn. If you cast Eelectrocute this way and it would be put into your graveyard, exile it instead. From 5aaf65e3943d903447f5987e080d24004ade5307 Mon Sep 17 00:00:00 2001 From: Northmoc <103371817+Northmoc@users.noreply.github.com> Date: Wed, 27 Nov 2024 04:18:42 -0500 Subject: [PATCH 123/152] magar_of_the_magic_strings.txt bug fix (#6618) * take3 * tweak --- forge-game/src/main/java/forge/game/ability/AbilityUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index f44797dc8e8..a7a386db38d 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -3009,7 +3009,7 @@ public class AbilityUtils { sp.setCardState(original); list.add(sp); } - if (tgtCard.isModal()) { + if (tgtCard.isModal() && tgtCard.hasState(CardStateName.Modal)) { collectSpellsForPlayEffect(list, tgtCard.getState(CardStateName.Modal), controller, withAltCost); } } From d7a93ac59c245ba66a52fc48b51a9d70991782ca Mon Sep 17 00:00:00 2001 From: Churrufli Date: Wed, 27 Nov 2024 11:45:46 +0100 Subject: [PATCH 124/152] Net Decks Archive Updates (Standard, Modern, Legacy, Vintage, Pioneer, Pauper) --- .../res/lists/net-decks-archive-legacy.txt | 84 ++++++++++++++ .../res/lists/net-decks-archive-modern.txt | 108 ++++++++++++++++++ .../res/lists/net-decks-archive-pauper.txt | 60 ++++++++++ .../res/lists/net-decks-archive-pioneer.txt | 91 +++++++++++++++ .../res/lists/net-decks-archive-standard.txt | 87 ++++++++++++++ .../res/lists/net-decks-archive-vintage.txt | 67 +++++++++++ 6 files changed, 497 insertions(+) diff --git a/forge-gui/res/lists/net-decks-archive-legacy.txt b/forge-gui/res/lists/net-decks-archive-legacy.txt index 0f9bc916179..81e9e34398a 100644 --- a/forge-gui/res/lists/net-decks-archive-legacy.txt +++ b/forge-gui/res/lists/net-decks-archive-legacy.txt @@ -3483,3 +3483,87 @@ 2024-10-13 8524 Legacy League (29 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-13-8524-legacy-league.zip 2024-10-14 8524 Legacy League (27 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-14-8524-legacy-league.zip 2024-10-15 8524 Legacy League (27 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-15-8524-legacy-league.zip + +2024-10-13 12699756 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-13-12699756-legacy-challenge-32.zip +2024-10-16 12699756 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-16-12699756-legacy-challenge-32.zip +2024-10-16 8524 Legacy League (12 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-16-8524-legacy-league.zip +2024-10-17 8524 Legacy League (19 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-17-8524-legacy-league.zip +2024-10-18 8524 Legacy League (29 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-18-8524-legacy-league.zip +2024-10-19 12699811 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-19-12699811-legacy-challenge-32.zip +2024-10-19 12699819 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-19-12699819-legacy-challenge-32.zip +2024-10-19 8524 Legacy League (20 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-19-8524-legacy-league.zip +2024-10-20 12699827 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-20-12699827-legacy-challenge-32.zip +2024-10-20 12699832 Legacy Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-20-12699832-legacy-challenge-64.zip +2024-10-20 8524 Legacy League (30 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-20-8524-legacy-league.zip +2024-10-21 8524 Legacy League (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-21-8524-legacy-league.zip +2024-10-22 8524 Legacy League (28 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-22-8524-legacy-league.zip +2024-10-23 12701189 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-23-12701189-legacy-challenge-32.zip +2024-10-23 8524 Legacy League (23 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-23-8524-legacy-league.zip +2024-10-24 8524 Legacy League (20 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-24-8524-legacy-league.zip +2024-10-25 8524 Legacy League (23 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-25-8524-legacy-league.zip +2024-10-26 12701226 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-26-12701226-legacy-challenge-32.zip +2024-10-26 12701234 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-26-12701234-legacy-challenge-32.zip +2024-10-26 8524 Legacy League (23 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-26-8524-legacy-league.zip +2024-10-27 12701243 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-27-12701243-legacy-challenge-32.zip +2024-10-27 12701249 Legacy Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-27-12701249-legacy-challenge-64.zip +2024-10-27 8524 Legacy League (25 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-27-8524-legacy-league.zip +2024-10-28 8524 Legacy League (17 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-28-8524-legacy-league.zip +2024-10-29 8524 Legacy League (24 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-29-8524-legacy-league.zip +2024-10-30 12703164 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-30-12703164-legacy-challenge-32.zip +2024-10-30 8524 Legacy League (20 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-30-8524-legacy-league.zip +2024-10-31 8524 Legacy League (21 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-10-31-8524-legacy-league.zip +2024-11-01 8524 Legacy League (28 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-01-8524-legacy-league.zip +2024-11-02 12703201 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-02-12703201-legacy-challenge-32.zip +2024-11-02 12703209 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-02-12703209-legacy-challenge-32.zip +2024-11-02 12703235 Legacy Prelim Anzid Showdown (26 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-02-12703235-legacy-prelim-anzid-showdown.zip +2024-11-02 8524 Legacy League (17 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-02-8524-legacy-league.zip +2024-11-03 12703218 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-03-12703218-legacy-challenge-32.zip +2024-11-03 12703224 Legacy Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-03-12703224-legacy-challenge-64.zip +2024-11-03 8524 Legacy League (25 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-03-8524-legacy-league.zip +2024-11-04 12703235 Legacy Prelim Anzid Showdown (26 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-04-12703235-legacy-prelim-anzid-showdown.zip +2024-11-04 12703240 Legacy Prelim Anzid Showdown (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-04-12703240-legacy-prelim-anzid-showdown.zip +2024-11-04 8524 Legacy League (37 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-04-8524-legacy-league.zip +2024-11-05 8524 Legacy League (29 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-05-8524-legacy-league.zip +2024-11-06 12703260 Legacy Prelim Anzid Showdown (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-06-12703260-legacy-prelim-anzid-showdown.zip +2024-11-06 12703263 Legacy Prelim Anzid Showdown (29 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-06-12703263-legacy-prelim-anzid-showdown.zip +2024-11-06 12704516 Legacy Challenge 32 Anzid Showdown (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-06-12704516-legacy-challenge-32-anzid-showdown.zip +2024-11-06 8524 Legacy League (24 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-06-8524-legacy-league.zip +2024-11-07 8524 Legacy League (22 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-07-8524-legacy-league.zip +2024-11-08 12704815 Legacy Prelim Anzid Showdown (19 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-08-12704815-legacy-prelim-anzid-showdown.zip +2024-11-08 8524 Legacy League (26 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-08-8524-legacy-league.zip +2024-11-09 12704551 Legacy Challenge 32 Anzid Showdown (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-09-12704551-legacy-challenge-32-anzid-showdown.zip +2024-11-09 12704559 Legacy Challenge 32 Anzid Showdown (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-09-12704559-legacy-challenge-32-anzid-showdown.zip +2024-11-09 8524 Legacy League (16 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-09-8524-legacy-league.zip +2024-11-10 12704512 Anzids Eternal Legacy Showdown (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-10-12704512-anzids-eternal-legacy-showdown.zip +2024-11-10 12704568 Legacy Challenge 32 Anzid Showdown (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-10-12704568-legacy-challenge-32-anzid-showdown.zip +2024-11-10 12704575 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-10-12704575-legacy-challenge-32.zip +2024-11-10 8524 Legacy League (31 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-10-8524-legacy-league.zip +2024-11-11 8524 Legacy League (28 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-11-8524-legacy-league.zip +2024-11-12 8524 Legacy League (22 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-12-8524-legacy-league.zip +2024-11-12 8672 Legacy League (3 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-12-8672-legacy-league.zip +2024-11-13 12705620 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-13-12705620-legacy-challenge-32.zip +2024-11-13 8672 Legacy League (24 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-13-8672-legacy-league.zip +2024-11-14 8672 Legacy League (23 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-14-8672-legacy-league.zip +2024-11-15 8672 Legacy League (26 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-15-8672-legacy-league.zip +2024-11-16 12705657 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-16-12705657-legacy-challenge-32.zip +2024-11-16 12705665 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-16-12705665-legacy-challenge-32.zip +2024-11-16 8672 Legacy League (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-16-8672-legacy-league.zip +2024-11-17 12705674 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-17-12705674-legacy-challenge-32.zip +2024-11-17 12705681 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-17-12705681-legacy-challenge-32.zip +2024-11-17 12705888 Legacy Showcase Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-17-12705888-legacy-showcase-challenge.zip +2024-11-17 8672 Legacy League (29 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-17-8672-legacy-league.zip +2024-11-18 8672 Legacy League (22 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-18-8672-legacy-league.zip +2024-11-19 8672 Legacy League (31 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-19-8672-legacy-league.zip +2024-11-20 12706722 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-20-12706722-legacy-challenge-32.zip +2024-11-20 8672 Legacy League (26 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-20-8672-legacy-league.zip +2024-11-21 8672 Legacy League (22 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-21-8672-legacy-league.zip +2024-11-22 8672 Legacy League (22 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-22-8672-legacy-league.zip +2024-11-23 12706766 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-23-12706766-legacy-challenge-32.zip +2024-11-23 8672 Legacy League (19 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-23-8672-legacy-league.zip +2024-11-24 12706775 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-24-12706775-legacy-challenge-32.zip +2024-11-24 12708093 Legacy Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-24-12708093-legacy-challenge-32.zip +2024-11-24 8672 Legacy League (24 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-24-8672-legacy-league.zip +2024-11-25 12706792 Legacy Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-25-12706792-legacy-last-chance.zip +2024-11-25 12706797 Legacy Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-25-12706797-legacy-last-chance.zip +2024-11-25 8672 Legacy League (22 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-25-8672-legacy-league.zip +2024-11-26 8672 Legacy League (14 decks) | https://downloads.cardforge.org/decks/archive/legacy/2024-11-26-8672-legacy-league.zip diff --git a/forge-gui/res/lists/net-decks-archive-modern.txt b/forge-gui/res/lists/net-decks-archive-modern.txt index fb7c085b706..9f59494265b 100644 --- a/forge-gui/res/lists/net-decks-archive-modern.txt +++ b/forge-gui/res/lists/net-decks-archive-modern.txt @@ -5251,3 +5251,111 @@ 2024-10-14 12698363 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-14-12698363-modern-challenge-64.zip 2024-10-14 8540 Modern League (61 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-14-8540-modern-league.zip 2024-10-15 8540 Modern League (33 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-15-8540-modern-league.zip + +2024-10-05 12698386 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-05-12698386-modern-challenge-64.zip +2024-10-07 12699783 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-07-12699783-modern-challenge-32.zip +2024-10-07 12699793 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-07-12699793-modern-challenge-32.zip +2024-10-15 12698386 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-15-12698386-modern-challenge-64.zip +2024-10-15 12698387 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-15-12698387-modern-challenge-32.zip +2024-10-16 8540 Modern League (38 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-16-8540-modern-league.zip +2024-10-17 12699783 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-17-12699783-modern-challenge-32.zip +2024-10-17 8540 Modern League (56 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-17-8540-modern-league.zip +2024-10-18 12699793 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-18-12699793-modern-challenge-32.zip +2024-10-18 12699802 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-18-12699802-modern-challenge-32.zip +2024-10-18 8540 Modern League (47 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-18-8540-modern-league.zip +2024-10-19 12699812 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-19-12699812-modern-challenge-64.zip +2024-10-19 12699814 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-19-12699814-modern-challenge-32.zip +2024-10-19 12699820 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-19-12699820-modern-challenge-64.zip +2024-10-19 8540 Modern League (40 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-19-8540-modern-league.zip +2024-10-20 12699826 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-20-12699826-modern-challenge-64.zip +2024-10-20 12699831 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-20-12699831-modern-challenge-32.zip +2024-10-20 8540 Modern League (51 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-20-8540-modern-league.zip +2024-10-21 12699840 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-21-12699840-modern-challenge-64.zip +2024-10-21 8540 Modern League (47 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-21-8540-modern-league.zip +2024-10-22 8540 Modern League (51 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-22-8540-modern-league.zip +2024-10-23 8540 Modern League (48 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-23-8540-modern-league.zip +2024-10-24 12701198 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-24-12701198-modern-challenge-32.zip +2024-10-24 8540 Modern League (49 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-24-8540-modern-league.zip +2024-10-25 12701208 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-25-12701208-modern-challenge-32.zip +2024-10-25 12701217 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-25-12701217-modern-challenge-32.zip +2024-10-25 8540 Modern League (58 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-25-8540-modern-league.zip +2024-10-26 12701229 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-26-12701229-modern-challenge-32.zip +2024-10-26 12701236 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-26-12701236-modern-challenge-64.zip +2024-10-26 8540 Modern League (46 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-26-8540-modern-league.zip +2024-10-27 12701242 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-27-12701242-modern-challenge-64.zip +2024-10-27 12701247 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-27-12701247-modern-challenge-32.zip +2024-10-27 8540 Modern League (47 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-27-8540-modern-league.zip +2024-10-28 12701257 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-28-12701257-modern-challenge-64.zip +2024-10-28 8540 Modern League (52 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-28-8540-modern-league.zip +2024-10-29 12701280 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-29-12701280-modern-challenge-64.zip +2024-10-29 8540 Modern League (46 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-29-8540-modern-league.zip +2024-10-30 8540 Modern League (40 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-30-8540-modern-league.zip +2024-10-31 12703173 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-31-12703173-modern-challenge-32.zip +2024-10-31 8540 Modern League (48 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-10-31-8540-modern-league.zip +2024-11-01 12703183 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-01-12703183-modern-challenge-32.zip +2024-11-01 12703192 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-01-12703192-modern-challenge-32.zip +2024-11-01 8540 Modern League (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-01-8540-modern-league.zip +2024-11-02 12703160 Modern Showcase Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-02-12703160-modern-showcase-challenge.zip +2024-11-02 12703204 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-02-12703204-modern-challenge-32.zip +2024-11-02 12703212 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-02-12703212-modern-challenge-32.zip +2024-11-02 8540 Modern League (49 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-02-8540-modern-league.zip +2024-11-03 12703217 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-03-12703217-modern-challenge-64.zip +2024-11-03 12703222 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-03-12703222-modern-challenge-32.zip +2024-11-03 8540 Modern League (38 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-03-8540-modern-league.zip +2024-11-04 12703232 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-04-12703232-modern-challenge-64.zip +2024-11-04 8540 Modern League (44 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-04-8540-modern-league.zip +2024-11-05 12703255 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-05-12703255-modern-challenge-64.zip +2024-11-05 8540 Modern League (31 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-05-8540-modern-league.zip +2024-11-06 8540 Modern League (27 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-06-8540-modern-league.zip +2024-11-07 12704525 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-07-12704525-modern-challenge-32.zip +2024-11-07 8540 Modern League (30 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-07-8540-modern-league.zip +2024-11-08 12704534 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-08-12704534-modern-challenge-32.zip +2024-11-08 12704542 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-08-12704542-modern-challenge-32.zip +2024-11-08 8540 Modern League (31 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-08-8540-modern-league.zip +2024-11-09 12704552 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-09-12704552-modern-challenge-64.zip +2024-11-09 12704554 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-09-12704554-modern-challenge-32.zip +2024-11-09 12704561 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-09-12704561-modern-challenge-32.zip +2024-11-09 8540 Modern League (22 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-09-8540-modern-league.zip +2024-11-10 12704567 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-10-12704567-modern-challenge-64.zip +2024-11-10 12704572 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-10-12704572-modern-challenge-32.zip +2024-11-10 8540 Modern League (42 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-10-8540-modern-league.zip +2024-11-11 12704582 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-11-12704582-modern-challenge-64.zip +2024-11-11 8540 Modern League (42 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-11-8540-modern-league.zip +2024-11-12 12705730 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-12-12705730-modern-challenge-32.zip +2024-11-12 8540 Modern League (26 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-12-8540-modern-league.zip +2024-11-12 8688 Modern League (2 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-12-8688-modern-league.zip +2024-11-13 8688 Modern League (29 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-13-8688-modern-league.zip +2024-11-14 12705629 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-14-12705629-modern-challenge-32.zip +2024-11-14 8688 Modern League (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-14-8688-modern-league.zip +2024-11-15 12705639 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-15-12705639-modern-challenge-32.zip +2024-11-15 12705648 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-15-12705648-modern-challenge-32.zip +2024-11-15 8688 Modern League (35 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-15-8688-modern-league.zip +2024-11-16 12705660 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-16-12705660-modern-challenge-32.zip +2024-11-16 12705668 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-16-12705668-modern-challenge-32.zip +2024-11-16 12705887 Modern Showcase Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-16-12705887-modern-showcase-challenge.zip +2024-11-16 8688 Modern League (29 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-16-8688-modern-league.zip +2024-11-17 12705673 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-17-12705673-modern-challenge-64.zip +2024-11-17 12705678 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-17-12705678-modern-challenge-32.zip +2024-11-17 8688 Modern League (33 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-17-8688-modern-league.zip +2024-11-18 8688 Modern League (35 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-18-8688-modern-league.zip +2024-11-19 12705711 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-19-12705711-modern-challenge-64.zip +2024-11-19 8688 Modern League (30 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-19-8688-modern-league.zip +2024-11-20 8688 Modern League (33 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-20-8688-modern-league.zip +2024-11-21 12706731 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-21-12706731-modern-challenge-32.zip +2024-11-21 8688 Modern League (44 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-21-8688-modern-league.zip +2024-11-22 12706741 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-22-12706741-modern-challenge-32.zip +2024-11-22 12706750 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-22-12706750-modern-challenge-32.zip +2024-11-22 8688 Modern League (35 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-22-8688-modern-league.zip +2024-11-23 12706760 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-23-12706760-modern-challenge-32.zip +2024-11-23 12706762 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-23-12706762-modern-challenge-32.zip +2024-11-23 12706768 Modern Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-23-12706768-modern-challenge-64.zip +2024-11-23 8688 Modern League (31 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-23-8688-modern-league.zip +2024-11-24 12706778 Modern Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-24-12706778-modern-challenge-32.zip +2024-11-24 12708094 Modern Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-24-12708094-modern-last-chance.zip +2024-11-24 8688 Modern League (30 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-24-8688-modern-league.zip +2024-11-25 12706794 Modern Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-25-12706794-modern-last-chance.zip +2024-11-25 12706799 Modern Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-25-12706799-modern-last-chance.zip +2024-11-25 8688 Modern League (33 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-25-8688-modern-league.zip +2024-11-26 12706804 Modern Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-26-12706804-modern-last-chance.zip +2024-11-26 12706806 Modern Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-26-12706806-modern-last-chance.zip +2024-11-26 8688 Modern League (19 decks) | https://downloads.cardforge.org/decks/archive/modern/2024-11-26-8688-modern-league.zip diff --git a/forge-gui/res/lists/net-decks-archive-pauper.txt b/forge-gui/res/lists/net-decks-archive-pauper.txt index 8705ee277c1..39717348c92 100644 --- a/forge-gui/res/lists/net-decks-archive-pauper.txt +++ b/forge-gui/res/lists/net-decks-archive-pauper.txt @@ -2698,3 +2698,63 @@ 2024-10-13 8548 Pauper League (16 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-13-8548-pauper-league.zip 2024-10-14 8548 Pauper League (27 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-14-8548-pauper-league.zip 2024-10-15 8548 Pauper League (16 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-15-8548-pauper-league.zip + +2024-10-16 8548 Pauper League (15 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-16-8548-pauper-league.zip +2024-10-17 8548 Pauper League (17 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-17-8548-pauper-league.zip +2024-10-18 12699800 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-18-12699800-pauper-challenge-32.zip +2024-10-18 8548 Pauper League (23 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-18-8548-pauper-league.zip +2024-10-19 12699817 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-19-12699817-pauper-challenge-32.zip +2024-10-19 8548 Pauper League (18 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-19-8548-pauper-league.zip +2024-10-20 12699834 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-20-12699834-pauper-challenge-32.zip +2024-10-20 8548 Pauper League (24 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-20-8548-pauper-league.zip +2024-10-21 8548 Pauper League (16 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-21-8548-pauper-league.zip +2024-10-22 8548 Pauper League (25 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-22-8548-pauper-league.zip +2024-10-23 8548 Pauper League (29 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-23-8548-pauper-league.zip +2024-10-24 8548 Pauper League (20 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-24-8548-pauper-league.zip +2024-10-25 12701215 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-25-12701215-pauper-challenge-32.zip +2024-10-25 8548 Pauper League (19 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-25-8548-pauper-league.zip +2024-10-26 12701232 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-26-12701232-pauper-challenge-32.zip +2024-10-26 8548 Pauper League (17 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-26-8548-pauper-league.zip +2024-10-27 12701251 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-27-12701251-pauper-challenge-32.zip +2024-10-27 8548 Pauper League (15 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-27-8548-pauper-league.zip +2024-10-28 8548 Pauper League (24 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-28-8548-pauper-league.zip +2024-10-29 8548 Pauper League (26 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-29-8548-pauper-league.zip +2024-10-30 8548 Pauper League (15 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-30-8548-pauper-league.zip +2024-10-31 8548 Pauper League (23 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-10-31-8548-pauper-league.zip +2024-11-01 12703190 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-01-12703190-pauper-challenge-32.zip +2024-11-01 8548 Pauper League (16 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-01-8548-pauper-league.zip +2024-11-02 12703207 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-02-12703207-pauper-challenge-32.zip +2024-11-02 8548 Pauper League (23 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-02-8548-pauper-league.zip +2024-11-03 12703226 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-03-12703226-pauper-challenge-32.zip +2024-11-03 8548 Pauper League (25 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-03-8548-pauper-league.zip +2024-11-04 8548 Pauper League (21 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-04-8548-pauper-league.zip +2024-11-05 8548 Pauper League (24 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-05-8548-pauper-league.zip +2024-11-06 8548 Pauper League (11 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-06-8548-pauper-league.zip +2024-11-07 8548 Pauper League (13 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-07-8548-pauper-league.zip +2024-11-08 12704541 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-08-12704541-pauper-challenge-32.zip +2024-11-08 8548 Pauper League (23 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-08-8548-pauper-league.zip +2024-11-09 12704557 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-09-12704557-pauper-challenge-32.zip +2024-11-09 8548 Pauper League (6 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-09-8548-pauper-league.zip +2024-11-10 12704576 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-10-12704576-pauper-challenge-32.zip +2024-11-10 8548 Pauper League (17 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-10-8548-pauper-league.zip +2024-11-11 8548 Pauper League (25 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-11-8548-pauper-league.zip +2024-11-12 8548 Pauper League (11 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-12-8548-pauper-league.zip +2024-11-14 8760 Pauper League (2 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-14-8760-pauper-league.zip +2024-11-15 12705646 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-15-12705646-pauper-challenge-32.zip +2024-11-15 8760 Pauper League (13 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-15-8760-pauper-league.zip +2024-11-16 12705663 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-16-12705663-pauper-challenge-32.zip +2024-11-16 8760 Pauper League (18 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-16-8760-pauper-league.zip +2024-11-17 12705682 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-17-12705682-pauper-challenge-32.zip +2024-11-17 8760 Pauper League (15 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-17-8760-pauper-league.zip +2024-11-18 8760 Pauper League (20 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-18-8760-pauper-league.zip +2024-11-19 8760 Pauper League (22 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-19-8760-pauper-league.zip +2024-11-20 8760 Pauper League (24 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-20-8760-pauper-league.zip +2024-11-21 8760 Pauper League (21 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-21-8760-pauper-league.zip +2024-11-22 12706748 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-22-12706748-pauper-challenge-32.zip +2024-11-22 8760 Pauper League (16 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-22-8760-pauper-league.zip +2024-11-23 12706764 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-23-12706764-pauper-challenge-32.zip +2024-11-23 8760 Pauper League (16 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-23-8760-pauper-league.zip +2024-11-24 12706783 Pauper Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-24-12706783-pauper-challenge-32.zip +2024-11-24 8760 Pauper League (17 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-24-8760-pauper-league.zip +2024-11-25 8760 Pauper League (17 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-25-8760-pauper-league.zip +2024-11-26 8760 Pauper League (14 decks) | https://downloads.cardforge.org/decks/archive/pauper/2024-11-26-8760-pauper-league.zip diff --git a/forge-gui/res/lists/net-decks-archive-pioneer.txt b/forge-gui/res/lists/net-decks-archive-pioneer.txt index c76f63f0f8c..6b17b8f8a9a 100644 --- a/forge-gui/res/lists/net-decks-archive-pioneer.txt +++ b/forge-gui/res/lists/net-decks-archive-pioneer.txt @@ -2710,3 +2710,94 @@ 2024-10-14 12698375 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-14-12698375-pioneer-challenge-32.zip 2024-10-14 8556 Pioneer League (27 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-14-8556-pioneer-league.zip 2024-10-15 8556 Pioneer League (21 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-15-8556-pioneer-league.zip + +2024-10-04 12698375 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-04-12698375-pioneer-challenge-32.zip +2024-10-16 8556 Pioneer League (18 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-16-8556-pioneer-league.zip +2024-10-17 12699790 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-17-12699790-pioneer-challenge-64.zip +2024-10-17 8556 Pioneer League (26 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-17-8556-pioneer-league.zip +2024-10-18 12699799 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-18-12699799-pioneer-challenge-32.zip +2024-10-18 12699808 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-18-12699808-pioneer-challenge-32.zip +2024-10-18 8556 Pioneer League (24 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-18-8556-pioneer-league.zip +2024-10-19 12699824 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-19-12699824-pioneer-challenge-64.zip +2024-10-19 8556 Pioneer League (28 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-19-8556-pioneer-league.zip +2024-10-20 12699780 Pioneer Showcase Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-20-12699780-pioneer-showcase-challenge.zip +2024-10-20 12699836 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-20-12699836-pioneer-challenge-64.zip +2024-10-20 8556 Pioneer League (24 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-20-8556-pioneer-league.zip +2024-10-21 12699851 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-21-12699851-pioneer-challenge-64.zip +2024-10-21 12699852 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-21-12699852-pioneer-challenge-32.zip +2024-10-21 8556 Pioneer League (27 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-21-8556-pioneer-league.zip +2024-10-22 8556 Pioneer League (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-22-8556-pioneer-league.zip +2024-10-23 8556 Pioneer League (29 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-23-8556-pioneer-league.zip +2024-10-24 12701205 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-24-12701205-pioneer-challenge-64.zip +2024-10-24 8556 Pioneer League (23 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-24-8556-pioneer-league.zip +2024-10-25 12701214 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-25-12701214-pioneer-challenge-32.zip +2024-10-25 12701223 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-25-12701223-pioneer-challenge-32.zip +2024-10-25 8556 Pioneer League (30 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-25-8556-pioneer-league.zip +2024-10-26 12701240 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-26-12701240-pioneer-challenge-64.zip +2024-10-26 8556 Pioneer League (22 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-26-8556-pioneer-league.zip +2024-10-27 12701248 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-27-12701248-pioneer-challenge-32.zip +2024-10-27 12701253 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-27-12701253-pioneer-challenge-64.zip +2024-10-27 8556 Pioneer League (21 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-27-8556-pioneer-league.zip +2024-10-28 12701268 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-28-12701268-pioneer-challenge-64.zip +2024-10-28 12701269 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-28-12701269-pioneer-challenge-32.zip +2024-10-28 8556 Pioneer League (24 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-28-8556-pioneer-league.zip +2024-10-29 8556 Pioneer League (24 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-29-8556-pioneer-league.zip +2024-10-30 8556 Pioneer League (26 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-30-8556-pioneer-league.zip +2024-10-31 12703180 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-31-12703180-pioneer-challenge-64.zip +2024-10-31 8556 Pioneer League (26 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-10-31-8556-pioneer-league.zip +2024-11-01 12703189 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-01-12703189-pioneer-challenge-32.zip +2024-11-01 12703198 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-01-12703198-pioneer-challenge-32.zip +2024-11-01 8556 Pioneer League (18 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-01-8556-pioneer-league.zip +2024-11-02 12703215 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-02-12703215-pioneer-challenge-64.zip +2024-11-02 8556 Pioneer League (21 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-02-8556-pioneer-league.zip +2024-11-03 12703223 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-03-12703223-pioneer-challenge-32.zip +2024-11-03 12703228 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-03-12703228-pioneer-challenge-64.zip +2024-11-03 8556 Pioneer League (15 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-03-8556-pioneer-league.zip +2024-11-04 8556 Pioneer League (18 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-04-8556-pioneer-league.zip +2024-11-05 12703243 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-05-12703243-pioneer-challenge-64.zip +2024-11-05 12703244 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-05-12703244-pioneer-challenge-32.zip +2024-11-05 8556 Pioneer League (20 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-05-8556-pioneer-league.zip +2024-11-06 8556 Pioneer League (19 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-06-8556-pioneer-league.zip +2024-11-07 8556 Pioneer League (16 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-07-8556-pioneer-league.zip +2024-11-08 12704540 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-08-12704540-pioneer-challenge-32.zip +2024-11-08 12704548 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-08-12704548-pioneer-challenge-32.zip +2024-11-08 8556 Pioneer League (12 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-08-8556-pioneer-league.zip +2024-11-09 12704565 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-09-12704565-pioneer-challenge-64.zip +2024-11-09 8556 Pioneer League (5 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-09-8556-pioneer-league.zip +2024-11-10 12704573 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-10-12704573-pioneer-challenge-32.zip +2024-11-10 12704578 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-10-12704578-pioneer-challenge-64.zip +2024-11-10 8556 Pioneer League (10 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-10-8556-pioneer-league.zip +2024-11-11 8556 Pioneer League (20 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-11-8556-pioneer-league.zip +2024-11-12 12704593 Pioneer Challenge 64 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-12-12704593-pioneer-challenge-64.zip +2024-11-12 12704594 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-12-12704594-pioneer-challenge-32.zip +2024-11-12 8556 Pioneer League (10 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-12-8556-pioneer-league.zip +2024-11-13 8752 Pioneer League (12 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-13-8752-pioneer-league.zip +2024-11-14 12705985 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-14-12705985-pioneer-challenge-32.zip +2024-11-14 8752 Pioneer League (12 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-14-8752-pioneer-league.zip +2024-11-15 12705645 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-15-12705645-pioneer-challenge-32.zip +2024-11-15 12705654 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-15-12705654-pioneer-challenge-32.zip +2024-11-15 8752 Pioneer League (10 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-15-8752-pioneer-league.zip +2024-11-16 8752 Pioneer League (6 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-16-8752-pioneer-league.zip +2024-11-17 12705679 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-17-12705679-pioneer-challenge-32.zip +2024-11-17 8752 Pioneer League (5 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-17-8752-pioneer-league.zip +2024-11-18 8752 Pioneer League (5 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-18-8752-pioneer-league.zip +2024-11-19 12705700 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-19-12705700-pioneer-challenge-32.zip +2024-11-19 8752 Pioneer League (14 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-19-8752-pioneer-league.zip +2024-11-20 8752 Pioneer League (7 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-20-8752-pioneer-league.zip +2024-11-21 12706738 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-21-12706738-pioneer-challenge-32.zip +2024-11-21 8752 Pioneer League (11 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-21-8752-pioneer-league.zip +2024-11-22 12706747 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-22-12706747-pioneer-challenge-32.zip +2024-11-22 12706756 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-22-12706756-pioneer-challenge-32.zip +2024-11-22 8752 Pioneer League (14 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-22-8752-pioneer-league.zip +2024-11-23 12706772 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-23-12706772-pioneer-challenge-32.zip +2024-11-23 8752 Pioneer League (8 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-23-8752-pioneer-league.zip +2024-11-24 12706718 Pioneer Showcase Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-24-12706718-pioneer-showcase-challenge.zip +2024-11-24 12706780 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-24-12706780-pioneer-challenge-32.zip +2024-11-24 12706785 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-24-12706785-pioneer-challenge-32.zip +2024-11-24 8752 Pioneer League (6 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-24-8752-pioneer-league.zip +2024-11-25 12706790 Pioneer Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-25-12706790-pioneer-last-chance.zip +2024-11-25 12706795 Pioneer Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-25-12706795-pioneer-last-chance.zip +2024-11-25 8752 Pioneer League (5 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-25-8752-pioneer-league.zip +2024-11-26 12706800 Pioneer Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-26-12706800-pioneer-challenge-32.zip +2024-11-26 12706803 Pioneer Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-26-12706803-pioneer-last-chance.zip +2024-11-26 8752 Pioneer League (4 decks) | https://downloads.cardforge.org/decks/archive/pioneer/2024-11-26-8752-pioneer-league.zip diff --git a/forge-gui/res/lists/net-decks-archive-standard.txt b/forge-gui/res/lists/net-decks-archive-standard.txt index 7630c735293..3d1f7bd03e9 100644 --- a/forge-gui/res/lists/net-decks-archive-standard.txt +++ b/forge-gui/res/lists/net-decks-archive-standard.txt @@ -3561,3 +3561,90 @@ 2024-10-13 8564 Standard League (8 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-13-8564-standard-league.zip 2024-10-14 8564 Standard League (15 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-14-8564-standard-league.zip 2024-10-15 8564 Standard League (11 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-15-8564-standard-league.zip + +2024-10-16 8564 Standard League (5 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-16-8564-standard-league.zip +2024-10-17 12699759 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-17-12699759-standard-challenge-32.zip +2024-10-17 8564 Standard League (12 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-17-8564-standard-league.zip +2024-10-18 12699804 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-18-12699804-standard-challenge-32.zip +2024-10-18 12699837 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-18-12699837-standard-challenge-32.zip +2024-10-18 8564 Standard League (14 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-18-8564-standard-league.zip +2024-10-19 12699779 Standard Showcase Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-19-12699779-standard-showcase-challenge.zip +2024-10-19 12699813 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-19-12699813-standard-challenge-32.zip +2024-10-19 8564 Standard League (10 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-19-8564-standard-league.zip +2024-10-20 12699837 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-20-12699837-standard-challenge-32.zip +2024-10-20 8564 Standard League (8 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-20-8564-standard-league.zip +2024-10-21 12699849 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-21-12699849-standard-challenge-32.zip +2024-10-21 8564 Standard League (9 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-21-8564-standard-league.zip +2024-10-22 8564 Standard League (10 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-22-8564-standard-league.zip +2024-10-23 8564 Standard League (20 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-23-8564-standard-league.zip +2024-10-24 12701192 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-24-12701192-standard-challenge-32.zip +2024-10-24 8564 Standard League (4 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-24-8564-standard-league.zip +2024-10-25 12701219 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-25-12701219-standard-challenge-32.zip +2024-10-25 8564 Standard League (13 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-25-8564-standard-league.zip +2024-10-26 12701228 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-26-12701228-standard-challenge-32.zip +2024-10-26 12701235 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-26-12701235-standard-challenge-32.zip +2024-10-26 8564 Standard League (9 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-26-8564-standard-league.zip +2024-10-27 12701176 Standard Qualifier (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-27-12701176-standard-qualifier.zip +2024-10-27 12701254 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-27-12701254-standard-challenge-32.zip +2024-10-27 8564 Standard League (17 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-27-8564-standard-league.zip +2024-10-28 12701266 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-28-12701266-standard-challenge-32.zip +2024-10-28 8564 Standard League (13 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-28-8564-standard-league.zip +2024-10-29 12701278 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-29-12701278-standard-challenge-32.zip +2024-10-29 8564 Standard League (14 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-29-8564-standard-league.zip +2024-10-30 8564 Standard League (18 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-30-8564-standard-league.zip +2024-10-31 12703167 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-31-12703167-standard-challenge-32.zip +2024-10-31 8564 Standard League (9 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-10-31-8564-standard-league.zip +2024-11-01 12703194 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-01-12703194-standard-challenge-32.zip +2024-11-01 8564 Standard League (11 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-01-8564-standard-league.zip +2024-11-02 12703203 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-02-12703203-standard-challenge-32.zip +2024-11-02 12703210 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-02-12703210-standard-challenge-32.zip +2024-11-02 8564 Standard League (14 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-02-8564-standard-league.zip +2024-11-03 12703229 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-03-12703229-standard-challenge-32.zip +2024-11-03 8564 Standard League (9 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-03-8564-standard-league.zip +2024-11-04 12703241 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-04-12703241-standard-challenge-32.zip +2024-11-04 8564 Standard League (10 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-04-8564-standard-league.zip +2024-11-05 12703253 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-05-12703253-standard-challenge-32.zip +2024-11-05 8564 Standard League (13 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-05-8564-standard-league.zip +2024-11-06 8564 Standard League (15 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-06-8564-standard-league.zip +2024-11-07 12704519 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-07-12704519-standard-challenge-32.zip +2024-11-07 8564 Standard League (10 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-07-8564-standard-league.zip +2024-11-08 12704544 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-08-12704544-standard-challenge-32.zip +2024-11-08 8564 Standard League (9 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-08-8564-standard-league.zip +2024-11-09 12704553 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-09-12704553-standard-challenge-32.zip +2024-11-09 12704560 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-09-12704560-standard-challenge-32.zip +2024-11-09 8564 Standard League (4 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-09-8564-standard-league.zip +2024-11-10 12704579 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-10-12704579-standard-challenge-32.zip +2024-11-10 8564 Standard League (7 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-10-8564-standard-league.zip +2024-11-11 12704591 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-11-12704591-standard-challenge-32.zip +2024-11-11 8564 Standard League (13 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-11-8564-standard-league.zip +2024-11-12 12705725 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-12-12705725-standard-challenge-32.zip +2024-11-12 8564 Standard League (8 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-12-8564-standard-league.zip +2024-11-13 8696 Standard League (18 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-13-8696-standard-league.zip +2024-11-14 12705623 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-14-12705623-standard-challenge-32.zip +2024-11-14 8696 Standard League (14 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-14-8696-standard-league.zip +2024-11-15 12705650 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-15-12705650-standard-challenge-32.zip +2024-11-15 8696 Standard League (14 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-15-8696-standard-league.zip +2024-11-16 12705659 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-16-12705659-standard-challenge-32.zip +2024-11-16 12705666 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-16-12705666-standard-challenge-32.zip +2024-11-16 8696 Standard League (14 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-16-8696-standard-league.zip +2024-11-17 12705685 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-17-12705685-standard-challenge-32.zip +2024-11-17 8696 Standard League (13 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-17-8696-standard-league.zip +2024-11-18 12705697 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-18-12705697-standard-challenge-32.zip +2024-11-18 8696 Standard League (28 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-18-8696-standard-league.zip +2024-11-19 12705709 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-19-12705709-standard-challenge-32.zip +2024-11-19 8696 Standard League (26 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-19-8696-standard-league.zip +2024-11-20 8696 Standard League (24 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-20-8696-standard-league.zip +2024-11-21 12706725 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-21-12706725-standard-challenge-32.zip +2024-11-21 8696 Standard League (27 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-21-8696-standard-league.zip +2024-11-22 12706752 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-22-12706752-standard-challenge-32.zip +2024-11-22 8696 Standard League (25 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-22-8696-standard-league.zip +2024-11-23 12706717 Standard Showcase Challenge (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-23-12706717-standard-showcase-challenge.zip +2024-11-23 12706761 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-23-12706761-standard-challenge-32.zip +2024-11-23 12706767 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-23-12706767-standard-challenge-32.zip +2024-11-23 8696 Standard League (21 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-23-8696-standard-league.zip +2024-11-24 12706786 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-24-12706786-standard-challenge-32.zip +2024-11-24 8696 Standard League (17 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-24-8696-standard-league.zip +2024-11-25 12706793 Standard Last Chance (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-25-12706793-standard-last-chance.zip +2024-11-25 12706798 Standard Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-25-12706798-standard-challenge-32.zip +2024-11-25 8696 Standard League (23 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-25-8696-standard-league.zip +2024-11-26 8696 Standard League (17 decks) | https://downloads.cardforge.org/decks/archive/standard/2024-11-26-8696-standard-league.zip diff --git a/forge-gui/res/lists/net-decks-archive-vintage.txt b/forge-gui/res/lists/net-decks-archive-vintage.txt index c1307494454..cfe0a15c420 100644 --- a/forge-gui/res/lists/net-decks-archive-vintage.txt +++ b/forge-gui/res/lists/net-decks-archive-vintage.txt @@ -2571,3 +2571,70 @@ 2024-10-13 8572 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-13-8572-vintage-league.zip 2024-10-14 8572 Vintage League (4 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-14-8572-vintage-league.zip 2024-10-15 8572 Vintage League (1 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-15-8572-vintage-league.zip + +2024-10-16 8572 Vintage League (4 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-16-8572-vintage-league.zip +2024-10-17 12699785 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-17-12699785-vintage-challenge-32.zip +2024-10-17 8572 Vintage League (3 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-17-8572-vintage-league.zip +2024-10-18 12699809 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-18-12699809-vintage-challenge-32.zip +2024-10-18 8572 Vintage League (5 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-18-8572-vintage-league.zip +2024-10-19 12699822 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-19-12699822-vintage-challenge-32.zip +2024-10-19 8572 Vintage League (4 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-19-8572-vintage-league.zip +2024-10-20 12699828 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-20-12699828-vintage-challenge-32.zip +2024-10-20 8572 Vintage League (4 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-20-8572-vintage-league.zip +2024-10-21 8572 Vintage League (2 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-21-8572-vintage-league.zip +2024-10-22 8572 Vintage League (2 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-22-8572-vintage-league.zip +2024-10-23 8572 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-23-8572-vintage-league.zip +2024-10-24 12701200 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-24-12701200-vintage-challenge-32.zip +2024-10-24 8572 Vintage League (4 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-24-8572-vintage-league.zip +2024-10-25 12701224 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-25-12701224-vintage-challenge-32.zip +2024-10-25 8572 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-25-8572-vintage-league.zip +2024-10-26 12701238 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-26-12701238-vintage-challenge-32.zip +2024-10-26 8572 Vintage League (3 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-26-8572-vintage-league.zip +2024-10-27 12701244 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-27-12701244-vintage-challenge-32.zip +2024-10-27 8572 Vintage League (3 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-27-8572-vintage-league.zip +2024-10-28 8572 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-28-8572-vintage-league.zip +2024-10-29 8572 Vintage League (5 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-29-8572-vintage-league.zip +2024-10-30 8572 Vintage League (9 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-30-8572-vintage-league.zip +2024-10-31 12703175 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-31-12703175-vintage-challenge-32.zip +2024-10-31 8572 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-10-31-8572-vintage-league.zip +2024-11-01 12703199 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-01-12703199-vintage-challenge-32.zip +2024-11-01 8572 Vintage League (5 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-01-8572-vintage-league.zip +2024-11-02 12703213 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-02-12703213-vintage-challenge-32.zip +2024-11-02 8572 Vintage League (7 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-02-8572-vintage-league.zip +2024-11-03 12703219 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-03-12703219-vintage-challenge-32.zip +2024-11-03 8572 Vintage League (13 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-03-8572-vintage-league.zip +2024-11-04 8572 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-04-8572-vintage-league.zip +2024-11-05 8572 Vintage League (3 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-05-8572-vintage-league.zip +2024-11-06 8572 Vintage League (7 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-06-8572-vintage-league.zip +2024-11-07 12704527 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-07-12704527-vintage-challenge-32.zip +2024-11-07 8572 Vintage League (4 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-07-8572-vintage-league.zip +2024-11-08 12704549 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-08-12704549-vintage-challenge-32.zip +2024-11-08 8572 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-08-8572-vintage-league.zip +2024-11-09 12704563 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-09-12704563-vintage-challenge-32.zip +2024-11-09 8572 Vintage League (1 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-09-8572-vintage-league.zip +2024-11-10 12704569 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-10-12704569-vintage-challenge-32.zip +2024-11-10 8572 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-10-8572-vintage-league.zip +2024-11-11 8572 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-11-8572-vintage-league.zip +2024-11-12 8572 Vintage League (5 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-12-8572-vintage-league.zip +2024-11-13 8704 Vintage League (4 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-13-8704-vintage-league.zip +2024-11-14 12705631 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-14-12705631-vintage-challenge-32.zip +2024-11-14 8704 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-14-8704-vintage-league.zip +2024-11-15 12705655 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-15-12705655-vintage-challenge-32.zip +2024-11-15 8704 Vintage League (5 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-15-8704-vintage-league.zip +2024-11-16 12705669 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-16-12705669-vintage-challenge-32.zip +2024-11-16 8704 Vintage League (7 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-16-8704-vintage-league.zip +2024-11-17 12705675 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-17-12705675-vintage-challenge-32.zip +2024-11-17 8704 Vintage League (8 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-17-8704-vintage-league.zip +2024-11-18 8704 Vintage League (5 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-18-8704-vintage-league.zip +2024-11-19 8704 Vintage League (8 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-19-8704-vintage-league.zip +2024-11-20 8704 Vintage League (9 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-20-8704-vintage-league.zip +2024-11-21 12706733 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-21-12706733-vintage-challenge-32.zip +2024-11-21 8704 Vintage League (2 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-21-8704-vintage-league.zip +2024-11-22 12706757 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-22-12706757-vintage-challenge-32.zip +2024-11-22 8704 Vintage League (4 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-22-8704-vintage-league.zip +2024-11-23 12706770 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-23-12706770-vintage-challenge-32.zip +2024-11-23 8704 Vintage League (9 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-23-8704-vintage-league.zip +2024-11-24 12706776 Vintage Challenge 32 (32 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-24-12706776-vintage-challenge-32.zip +2024-11-24 8704 Vintage League (5 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-24-8704-vintage-league.zip +2024-11-25 8704 Vintage League (5 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-25-8704-vintage-league.zip +2024-11-26 8704 Vintage League (6 decks) | https://downloads.cardforge.org/decks/archive/vintage/2024-11-26-8704-vintage-league.zip From 3f7f777be1796766d0cda7acb66b2588f83ed132 Mon Sep 17 00:00:00 2001 From: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:26:05 +0000 Subject: [PATCH 125/152] Fix DA1 edition and related cards --- .../a/anax_and_cymede_and_kynaios_and_tiro.txt | 4 ++-- ...xt => myojin_of_nights_reach_and_grim_betrayal.txt} | 4 ++-- forge-gui/res/editions/Unknown Event.txt | 10 ++-------- 3 files changed, 6 insertions(+), 12 deletions(-) rename forge-gui/res/cardsfolder/m/{myojin_of_nights_reach_grim_betrayal.txt => myojin_of_nights_reach_and_grim_betrayal.txt} (62%) diff --git a/forge-gui/res/cardsfolder/a/anax_and_cymede_and_kynaios_and_tiro.txt b/forge-gui/res/cardsfolder/a/anax_and_cymede_and_kynaios_and_tiro.txt index 34d261e7d11..8ecfa8a001c 100644 --- a/forge-gui/res/cardsfolder/a/anax_and_cymede_and_kynaios_and_tiro.txt +++ b/forge-gui/res/cardsfolder/a/anax_and_cymede_and_kynaios_and_tiro.txt @@ -1,4 +1,4 @@ -Name:Anax and Cymede and Kynaios and Tiro +Name:Anax and Cymede & Kynaios and Tiro ManaCost:1 W U R G Types:Legendary Creature Human Soldier PT:3/8 @@ -9,4 +9,4 @@ SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ EachPlayLand SVar:EachPlayLand:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Land | DefinedPlayer$ Player | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DrawAbstainers SVar:DrawAbstainers:DB$ Draw | Defined$ OppNonRememberedOwner | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -Oracle:First strike, vigilance\nHeroic — Whenever you cast a spell that targets Anax and Cymede and Kynaios and Tiro, draw a card. Each player may put a land card from their hand onto the battlefield, then each opponent who didn't draws a card. +Oracle:First strike, vigilance\nHeroic — Whenever you cast a spell that targets Anax and Cymede & Kynaios and Tiro, draw a card. Each player may put a land card from their hand onto the battlefield, then each opponent who didn't draws a card. diff --git a/forge-gui/res/cardsfolder/m/myojin_of_nights_reach_grim_betrayal.txt b/forge-gui/res/cardsfolder/m/myojin_of_nights_reach_and_grim_betrayal.txt similarity index 62% rename from forge-gui/res/cardsfolder/m/myojin_of_nights_reach_grim_betrayal.txt rename to forge-gui/res/cardsfolder/m/myojin_of_nights_reach_and_grim_betrayal.txt index 156c2373efc..3ecd8564cca 100644 --- a/forge-gui/res/cardsfolder/m/myojin_of_nights_reach_grim_betrayal.txt +++ b/forge-gui/res/cardsfolder/m/myojin_of_nights_reach_and_grim_betrayal.txt @@ -1,4 +1,4 @@ -Name:Myojin of Night's Reach Grim Betrayal +Name:Myojin of Night's Reach and Grim Betrayal ManaCost:5 B B B Types:Legendary Creature Spirit PT:10/4 @@ -7,4 +7,4 @@ SVar:FromHand:Count$wasCastFromYourHandByYou.1.0 A:AB$ Discard | Cost$ SubCounter<1/Indestructible> | Defined$ Player.Opponent | Mode$ Hand | SubAbility$ DBReturn | SpellDescription$ Each opponent discards their hand. Put onto the battlefield under your control all creature cards in all graveyards that were put there from anywhere this turn. SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ ValidGraveyard Creature.ThisTurnEntered | GainControl$ True DeckHas:Ability$Graveyard -Oracle:Myojin of Night's Reach Grim Betrayal enters the battlefield with an indestructible counter on it if you cast it from your hand.\nRemove an indestructible counter from Myojin of Night's Reach Grim Betrayal: Each opponent discards their hand. Put onto the battlefield under your control all creature cards in all graveyards that were put there from anywhere this turn. +Oracle:Myojin of Night's Reach and Grim Betrayal enters the battlefield with an indestructible counter on it if you cast it from your hand.\nRemove an indestructible counter from Myojin of Night's Reach and Grim Betrayal: Each opponent discards their hand. Put onto the battlefield under your control all creature cards in all graveyards that were put there from anywhere this turn. diff --git a/forge-gui/res/editions/Unknown Event.txt b/forge-gui/res/editions/Unknown Event.txt index 6ffad764162..a8b6580cad6 100644 --- a/forge-gui/res/editions/Unknown Event.txt +++ b/forge-gui/res/editions/Unknown Event.txt @@ -90,7 +90,7 @@ UW02c U Identify the Culprit @ WW02 S The Pleasant Taxer @ CA03 C Unicycling Automaton @ CB03 C May of the Machine @ -CG03a C Garruk's Lost Wolf // Hey, Has Anyone Seen Garruk? @ +CG03a C Garruk's Lost Wolf @ CG03b C Growing Detective @ CG03c C Investi-Gate @ CG03d C Merfolk Surveyor @ @@ -269,7 +269,7 @@ PLA007 C Pin Collector's Booth @ RA07a R Battle Myrsphere @ RA07b R Chancellor of the Mulligan @ RC07a R Call Up Emrakul to Help @ -RC07b R The Battle of Dragon Brothers // Fate Reforged @ +RC07b R The Battle of Dragon Brothers @ RG07 R The Massive Zatcatl @ RU07a R The Value Knight @ RU07b R Fourth Stage of Magic Design @ @@ -374,9 +374,3 @@ RR66 R The Crafter @ RU72 R The Rhystic Storyteller @ RW90 R The Keeper of Favorite Cards @ MZ99 M The Collector @ -ZZ1 R Anax and Cymede and Kynaios and Tiro @ -ZZ2 R Myojin of Night's Reach Grim Betrayal @ -ZZ3 C Garruk's Lost Wolf @ -ZZ4 R Sort of ____ and ____ @ -ZZ5 R Quest Compleated Best @ -ZZ6 R The Magic City, New @ From 877fb992c56c8c229c9a0217a3a0bf732773dc4d Mon Sep 17 00:00:00 2001 From: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com> Date: Thu, 28 Nov 2024 09:54:10 +0000 Subject: [PATCH 126/152] Edition updates: PIO, PMEI, SLD --- forge-gui/res/editions/Media Inserts.txt | 3 +- forge-gui/res/editions/Pioneer Masters.txt | 9 ++++++ .../res/editions/Secret Lair Drop Series.txt | 28 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/forge-gui/res/editions/Media Inserts.txt b/forge-gui/res/editions/Media Inserts.txt index 7e1ccefdd7a..56313c220c1 100644 --- a/forge-gui/res/editions/Media Inserts.txt +++ b/forge-gui/res/editions/Media Inserts.txt @@ -12,7 +12,7 @@ ScryfallCode=PMEI 9 C Scent of Cinder @Carl Critchlow 10 C Lightning Hounds @Andrew Robinson 10★ C Jamuraan Lion @Stuart Griffin -11 C Spined Wurm @Keith Parkinson +11 R Voltaic Key @Henry G. Higginbotham 12 U Warmonger @Heather Hudson 13 C Silver Drake @Alan Pollack 14 C Phyrexian Rager @Mark Tedin @@ -67,5 +67,6 @@ ScryfallCode=PMEI 63 R Snuff Out @Mike Ploog 66 R Saryth, the Viper's Fang @Igor Kieryluk 67 R Gush @Kev Walker +121 C Spined Wurm @Keith Parkinson 211 R Park Heights Pegasus @Randy Gallegos 232 R Liesa, Forgotten Archangel @Dmitry Burmak diff --git a/forge-gui/res/editions/Pioneer Masters.txt b/forge-gui/res/editions/Pioneer Masters.txt index 43c9c6dd8bd..9443c04226b 100644 --- a/forge-gui/res/editions/Pioneer Masters.txt +++ b/forge-gui/res/editions/Pioneer Masters.txt @@ -6,17 +6,26 @@ Type=Reprint ScryfallCode=PIO [cards] +0 U Bane of Bala Ged @Chase Stone 0 R Silence @Wayne Reynolds 0 U Hidden Strings @Daarken 0 M Jace, Vryn's Prodigy @Jaime Jones +0 M Temporal Trespass @Clint Cearley +0 C Void Shatter @Yohann Schepacz +0 M Behold the Beyond @Madeline Boni +0 R Dark Petition @Igor Kieryluk +0 C Gurmag Angler @YW Tang 0 M Liliana, Heretical Healer @Karla Ortiz 0 R Tasigur, the Golden Fang @Chris Rahn 0 R Legion Loyalist @Eric Deschamps 0 M Purphoros, God of the Forge @Eric Deschamps 0 C Gladecover Scout @Brian Valeza 0 R Hornet Nest @Adam Paquette +0 R Oath of Nissa @Wesley Burt 0 R Sylvan Caryatid @Chase Stone +0 C Voyaging Satyr @Tyler Jacobson 0 M Athreos, God of Passage @Ryan Barger 0 R Bring to Light @Jonas De Ro 0 M Mogis, God of Slaughter @Chase Stone +0 M The Chain Veil @Volkan Baǵa 0 R Urborg, Tomb of Yawgmoth @John Avon diff --git a/forge-gui/res/editions/Secret Lair Drop Series.txt b/forge-gui/res/editions/Secret Lair Drop Series.txt index 5ab1760bcfd..ed035b7e2b8 100644 --- a/forge-gui/res/editions/Secret Lair Drop Series.txt +++ b/forge-gui/res/editions/Secret Lair Drop Series.txt @@ -1704,6 +1704,32 @@ F1540 M Rainbow Dash @John Thacker 1822 R Lightning Bolt @Mike Burns 1823 R Fierce Guardianship @Mike Burns 1824 R Delayed Blast Fireball @Mike Burns +1825 M Go-Shintai of Life's Origin @Yuko Shimizu +1826 R Approach of the Second Sun @Brandi Milne +1827 R Felidar Sovereign @Heikala +1828 R Happily Ever After @Jordan Crane +1829 R Triskaidekaphile @Wizard of Barge +1830 R Revel in Riches @Kelogsloops +1831 R Helix Pinnacle @AlbaBG +1832 R Simic Ascendancy @Luke Pearson +1833 R Sol Ring @Aya Kakeda +1834 M Maze's End @Natalie Andrewson +1836 U Darksteel Mutation @Daniel Ljunggren +1837 U Ghostly Prison @Lars Grant-West +1838 R Monologue Tax @Justine Cruz +1839 R Pariah @Scott M. Fischer +1840 M Test of Endurance @Denman Rooke +1841 M Mechanized Production @Adam Paquette +1842 R Mystic Remora @Jesper Ejsing +1843 R Ghoulish Impetus @Steve Ellis +1844 R Liliana's Contract @Bastien L. Deharme +1845 U Shiny Impetus @Svetlin Velinov +1846 C Fertile Ground @Heather Hudson +1847 C Lignify @Jesper Ejsing +1848 R Mayael's Aria @Steve Argyle +1849 C Trace of Abundance @Dave Kendall +1850 U Crib Swap @Brandon Dorman +1851 R Homeward Path @Franz Vohwinkel 8001 M Jace, the Mind Sculptor @Wizard of Barge 9990 R Doom Blade @Cynthia Sheppard 9991 R Massacre @Andrey Kuzinskiy @@ -1724,6 +1750,8 @@ b_1_1_faerie_rogue_flying b_1_1_faerie_rogue_flying b_1_1_faerie_rogue_flying b_2_2_zombie +c_1_1_e_shrine +c_1_1_spirit c_a_clue_draw c_a_treasure_sac c_a_treasure_sac From 3971d5374668fffdd3016a51032a0d00bdc07029 Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Thu, 28 Nov 2024 20:00:17 +0800 Subject: [PATCH 127/152] Update TextRenderer.java fix symbol out of bounds --- forge-gui-mobile/src/forge/assets/TextRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui-mobile/src/forge/assets/TextRenderer.java b/forge-gui-mobile/src/forge/assets/TextRenderer.java index 74c50d0ff4d..ca5cec94f63 100644 --- a/forge-gui-mobile/src/forge/assets/TextRenderer.java +++ b/forge-gui-mobile/src/forge/assets/TextRenderer.java @@ -177,7 +177,7 @@ public class TextRenderer { needClip = true; } } - addPiece(new SymbolPiece(symbol, inReminderTextCount > 0), lineNum, x, y - font.getAscent() + (lineHeight - pieceWidth) / 2, pieceWidth, pieceWidth); + addPiece(new SymbolPiece(symbol, inReminderTextCount > 0), lineNum, x, y - lineHeight / 4, pieceWidth, pieceWidth); x += pieceWidth; pieceWidth = 0; text.setLength(0); From ad3044133cff4f47038346f81cacce7b7b04452f Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Thu, 28 Nov 2024 15:59:18 +0100 Subject: [PATCH 128/152] Class level ai (#6641) * ClassLevelAi: add first logic * Update ClassLevelUpAi.java Check for ClassLevelGained Trigger * Update ClassLevelUpAi.java ~ fix import --- .../src/main/java/forge/ai/SpellApiToAi.java | 2 +- .../java/forge/ai/ability/ClassLevelUpAi.java | 36 +++++++++++++++++++ .../StaticAbilityContinuous.java | 7 +--- 3 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 forge-ai/src/main/java/forge/ai/ability/ClassLevelUpAi.java diff --git a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java index db5e141ea87..dac83007906 100644 --- a/forge-ai/src/main/java/forge/ai/SpellApiToAi.java +++ b/forge-ai/src/main/java/forge/ai/SpellApiToAi.java @@ -54,7 +54,7 @@ public enum SpellApiToAi { .put(ApiType.ChooseSource, ChooseSourceAi.class) .put(ApiType.ChooseType, ChooseTypeAi.class) .put(ApiType.Clash, ClashAi.class) - .put(ApiType.ClassLevelUp, AlwaysPlayAi.class) + .put(ApiType.ClassLevelUp, ClassLevelUpAi.class) .put(ApiType.Cleanup, AlwaysPlayAi.class) .put(ApiType.Cloak, CloakAi.class) .put(ApiType.Clone, CloneAi.class) diff --git a/forge-ai/src/main/java/forge/ai/ability/ClassLevelUpAi.java b/forge-ai/src/main/java/forge/ai/ability/ClassLevelUpAi.java new file mode 100644 index 00000000000..ef7594bd094 --- /dev/null +++ b/forge-ai/src/main/java/forge/ai/ability/ClassLevelUpAi.java @@ -0,0 +1,36 @@ +package forge.ai.ability; + +import forge.ai.SpellAbilityAi; +import forge.ai.SpellApiToAi; +import forge.game.ability.AbilityUtils; +import forge.game.card.Card; +import forge.game.player.Player; +import forge.game.spellability.SpellAbility; +import forge.game.staticability.StaticAbility; +import forge.game.trigger.Trigger; +import forge.game.trigger.TriggerType; + +public class ClassLevelUpAi extends SpellAbilityAi { + @Override + protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) { + Card host = sa.getHostCard(); + final int level = host.getClassLevel() + 1; + for (StaticAbility stAb : host.getStaticAbilities()) { + if (!stAb.hasParam("AddTrigger") || !stAb.isClassLevelNAbility(level)) { + continue; + } + for (String sTrig : stAb.getParam("AddTrigger").split(" & ")) { + Trigger t = host.getTriggerForStaticAbility(AbilityUtils.getSVar(stAb, sTrig), stAb); + if (t.getMode() != TriggerType.ClassLevelGained) { + continue; + } + SpellAbility effect = t.ensureAbility(); + if (!SpellApiToAi.Converter.get(effect.getApi()).doTriggerAI(aiPlayer, effect, false)) { + return false; + } + } + } + return true; + } + +} diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index 56f523e25b6..720eafe063c 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -831,12 +831,7 @@ public final class StaticAbilityContinuous { // add triggers if (addTriggers != null) { for (final String trigger : addTriggers) { - final Trigger actualTrigger = affectedCard.getTriggerForStaticAbility(trigger, stAb); - // if the trigger has Execute param, which most trigger gained by Static Abilties should have - // turn them into SpellAbility object before adding to card - // with that the TargetedCard does not need the Svars added to them anymore - // but only do it if the trigger doesn't already have a overriding ability - addedTrigger.add(actualTrigger); + addedTrigger.add(affectedCard.getTriggerForStaticAbility(trigger, stAb)); } } From 535f175864df8ce42f9175b7b45352cfbf9d4973 Mon Sep 17 00:00:00 2001 From: Myyk Seok <2080820+myyk@users.noreply.github.com> Date: Thu, 28 Nov 2024 23:04:37 +0800 Subject: [PATCH 129/152] Alphabetically sort MTGO Cubes so they are easier to find (#6201) * chore: move files to be alphabetically sorted * feat: make the alphabetically stored cube contents match the new file names * fix: cube name typo in MTGO Legacy Cube 2017-01 --------- Co-authored-by: Agetian --- .../{MTGO Cube March 2014.dck => MTGO Cube 2014-03.dck} | 2 +- ... Cards).dck => MTGO Grixis Cube 2019-09 (540 Cards).dck} | 2 +- ...acy Cube March 2015.dck => MTGO Legacy Cube 2015-03.dck} | 2 +- ...Cube September 2015.dck => MTGO Legacy Cube 2015-09.dck} | 2 +- ...y Cube January 2016.dck => MTGO Legacy Cube 2016-01.dck} | 2 +- ...Cube September 2016.dck => MTGO Legacy Cube 2016-09.dck} | 2 +- ...y Cube January 2017.dck => MTGO Legacy Cube 2017-01.dck} | 2 +- ...acy Cube April 2017.dck => MTGO Legacy Cube 2017-04.dck} | 2 +- ... Cube February 2018.dck => MTGO Legacy Cube 2018-02.dck} | 2 +- ...gacy Cube July 2019.dck => MTGO Legacy Cube 2019-07.dck} | 2 +- ...egacy Cube May 2021.dck => MTGO Legacy Cube 2021-05.dck} | 2 +- ...cy Cube August 2021.dck => MTGO Legacy Cube 2021-08.dck} | 2 +- ...age Cube June 2016.dck => MTGO Vintage Cube 2016-06.dck} | 2 +- ...Cube November 2016.dck => MTGO Vintage Cube 2016-11.dck} | 2 +- ...age Cube June 2017.dck => MTGO Vintage Cube 2017-06.dck} | 2 +- ...Cube December 2017.dck => MTGO Vintage Cube 2017-12.dck} | 2 +- ...age Cube June 2018.dck => MTGO Vintage Cube 2018-06.dck} | 2 +- ...Cube December 2018.dck => MTGO Vintage Cube 2018-12.dck} | 2 +- ...be Summer 2019.dck => MTGO Vintage Cube 2019 Summer.dck} | 2 +- ...age Cube June 2019.dck => MTGO Vintage Cube 2019-06.dck} | 2 +- ...Cube December 2019.dck => MTGO Vintage Cube 2019-12.dck} | 2 +- ...ge Cube April 2020.dck => MTGO Vintage Cube 2020-04.dck} | 2 +- ...age Cube July 2020.dck => MTGO Vintage Cube 2020-07.dck} | 2 +- ...Cube December 2020.dck => MTGO Vintage Cube 2020-12.dck} | 2 +- ...age Cube July 2021.dck => MTGO Vintage Cube 2021-07.dck} | 2 +- ...Cube February 2022.dck => MTGO Vintage Cube 2022-02.dck} | 2 +- ...tage Cube May 2023.dck => MTGO Vintage Cube 2023-05.dck} | 2 +- ...e Cube August 2023.dck => MTGO Vintage Cube 2023-08.dck} | 2 +- ... Cube October 2023.dck => MTGO Vintage Cube 2023-10.dck} | 2 +- ...Cube February 2024.dck => MTGO Vintage Cube 2024-02.dck} | 2 +- ...age Cube June 2024.dck => MTGO Vintage Cube 2024-06.dck} | 2 +- forge-gui/res/draft/MTGO Cube 2014-03.draft | 6 ++++++ forge-gui/res/draft/MTGO Cube March 2014.draft | 6 ------ .../res/draft/MTGO Grixis Cube 2019-09 (540 Cards).draft | 6 ++++++ .../draft/MTGO Grixis Cube September 2019 (540 Cards).draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube 2015-03.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube 2015-09.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube 2016-01.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube 2016-09.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube 2017-01.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube 2017-04.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube 2018-02.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube 2019-07.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube 2021-05.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube 2021-08.draft | 6 ++++++ forge-gui/res/draft/MTGO Legacy Cube April 2017.draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube August 2021.draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube February 2018.draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube January 2016.draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube January 2017.draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube July 2019.draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube March 2015.draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube May 2021.draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube September 2015.draft | 6 ------ forge-gui/res/draft/MTGO Legacy Cube September 2016.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube 2016-06.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2016-11.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2017-06.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2017-12.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2018-06.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2018-12.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2019 Summer.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2019-06.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2019-12.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2020-04.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2020-07.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2020-12.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2021-07.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2022-02.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2023-05.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2023-08.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2023-10.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2024-02.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube 2024-06.draft | 6 ++++++ forge-gui/res/draft/MTGO Vintage Cube April 2020.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube August 2023.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube December 2017.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube December 2018.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube December 2019.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube December 2020.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube February 2022.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube February 2024.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube July 2020.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube July 2021.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube June 2016.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube June 2017.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube June 2018.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube June 2019.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube June 2024.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube May 2023.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube November 2016.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube October 2023.draft | 6 ------ forge-gui/res/draft/MTGO Vintage Cube Summer 2019.draft | 6 ------ 93 files changed, 217 insertions(+), 217 deletions(-) rename forge-gui/res/cube/{MTGO Cube March 2014.dck => MTGO Cube 2014-03.dck} (99%) rename forge-gui/res/cube/{MTGO Grixis Cube September 2019 (540 Cards).dck => MTGO Grixis Cube 2019-09 (540 Cards).dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube March 2015.dck => MTGO Legacy Cube 2015-03.dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube September 2015.dck => MTGO Legacy Cube 2015-09.dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube January 2016.dck => MTGO Legacy Cube 2016-01.dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube September 2016.dck => MTGO Legacy Cube 2016-09.dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube January 2017.dck => MTGO Legacy Cube 2017-01.dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube April 2017.dck => MTGO Legacy Cube 2017-04.dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube February 2018.dck => MTGO Legacy Cube 2018-02.dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube July 2019.dck => MTGO Legacy Cube 2019-07.dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube May 2021.dck => MTGO Legacy Cube 2021-05.dck} (99%) rename forge-gui/res/cube/{MTGO Legacy Cube August 2021.dck => MTGO Legacy Cube 2021-08.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube June 2016.dck => MTGO Vintage Cube 2016-06.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube November 2016.dck => MTGO Vintage Cube 2016-11.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube June 2017.dck => MTGO Vintage Cube 2017-06.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube December 2017.dck => MTGO Vintage Cube 2017-12.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube June 2018.dck => MTGO Vintage Cube 2018-06.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube December 2018.dck => MTGO Vintage Cube 2018-12.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube Summer 2019.dck => MTGO Vintage Cube 2019 Summer.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube June 2019.dck => MTGO Vintage Cube 2019-06.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube December 2019.dck => MTGO Vintage Cube 2019-12.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube April 2020.dck => MTGO Vintage Cube 2020-04.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube July 2020.dck => MTGO Vintage Cube 2020-07.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube December 2020.dck => MTGO Vintage Cube 2020-12.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube July 2021.dck => MTGO Vintage Cube 2021-07.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube February 2022.dck => MTGO Vintage Cube 2022-02.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube May 2023.dck => MTGO Vintage Cube 2023-05.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube August 2023.dck => MTGO Vintage Cube 2023-08.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube October 2023.dck => MTGO Vintage Cube 2023-10.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube February 2024.dck => MTGO Vintage Cube 2024-02.dck} (99%) rename forge-gui/res/cube/{MTGO Vintage Cube June 2024.dck => MTGO Vintage Cube 2024-06.dck} (99%) create mode 100644 forge-gui/res/draft/MTGO Cube 2014-03.draft delete mode 100644 forge-gui/res/draft/MTGO Cube March 2014.draft create mode 100644 forge-gui/res/draft/MTGO Grixis Cube 2019-09 (540 Cards).draft delete mode 100644 forge-gui/res/draft/MTGO Grixis Cube September 2019 (540 Cards).draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2015-03.draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2015-09.draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2016-01.draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2016-09.draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2017-01.draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2017-04.draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2018-02.draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2019-07.draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2021-05.draft create mode 100644 forge-gui/res/draft/MTGO Legacy Cube 2021-08.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube April 2017.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube August 2021.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube February 2018.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube January 2016.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube January 2017.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube July 2019.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube March 2015.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube May 2021.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube September 2015.draft delete mode 100644 forge-gui/res/draft/MTGO Legacy Cube September 2016.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2016-06.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2016-11.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2017-06.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2017-12.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2018-06.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2018-12.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2019 Summer.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2019-06.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2019-12.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2020-04.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2020-07.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2020-12.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2021-07.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2022-02.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2023-05.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2023-08.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2023-10.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2024-02.draft create mode 100644 forge-gui/res/draft/MTGO Vintage Cube 2024-06.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube April 2020.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube August 2023.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube December 2017.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube December 2018.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube December 2019.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube December 2020.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube February 2022.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube February 2024.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube July 2020.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube July 2021.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube June 2016.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube June 2017.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube June 2018.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube June 2019.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube June 2024.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube May 2023.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube November 2016.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube October 2023.draft delete mode 100644 forge-gui/res/draft/MTGO Vintage Cube Summer 2019.draft diff --git a/forge-gui/res/cube/MTGO Cube March 2014.dck b/forge-gui/res/cube/MTGO Cube 2014-03.dck similarity index 99% rename from forge-gui/res/cube/MTGO Cube March 2014.dck rename to forge-gui/res/cube/MTGO Cube 2014-03.dck index 3893697c0e2..23950073f49 100644 --- a/forge-gui/res/cube/MTGO Cube March 2014.dck +++ b/forge-gui/res/cube/MTGO Cube 2014-03.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Cube March 2014 +Name=MTGO Cube 2014-03 [main] 1 Academy Rector|UDS 1 Academy Ruins|2XM diff --git a/forge-gui/res/cube/MTGO Grixis Cube September 2019 (540 Cards).dck b/forge-gui/res/cube/MTGO Grixis Cube 2019-09 (540 Cards).dck similarity index 99% rename from forge-gui/res/cube/MTGO Grixis Cube September 2019 (540 Cards).dck rename to forge-gui/res/cube/MTGO Grixis Cube 2019-09 (540 Cards).dck index d6b86770ca6..988512271f5 100644 --- a/forge-gui/res/cube/MTGO Grixis Cube September 2019 (540 Cards).dck +++ b/forge-gui/res/cube/MTGO Grixis Cube 2019-09 (540 Cards).dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Grixis Cube September 2019 (540 cards) +Name=MTGO Grixis Cube 2019-09 (540 cards) [main] Abbot of Keral Keep|ORI Abrade|HOU diff --git a/forge-gui/res/cube/MTGO Legacy Cube March 2015.dck b/forge-gui/res/cube/MTGO Legacy Cube 2015-03.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube March 2015.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2015-03.dck index 4e4df7fa216..0372743ab50 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube March 2015.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2015-03.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Legacy Cube March 2015 +Name=MTGO Legacy Cube 2015-03 [main] 1 Accorder Paladin|MBS 1 Abrupt Decay|MM3 diff --git a/forge-gui/res/cube/MTGO Legacy Cube September 2015.dck b/forge-gui/res/cube/MTGO Legacy Cube 2015-09.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube September 2015.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2015-09.dck index d17bd8a3ff2..724414a7dc6 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube September 2015.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2015-09.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Legacy Cube September 2015 +Name=MTGO Legacy Cube 2015-09 [main] 1 Abbot of Keral Keep|ORI 1 Abhorrent Overlord|THS diff --git a/forge-gui/res/cube/MTGO Legacy Cube January 2016.dck b/forge-gui/res/cube/MTGO Legacy Cube 2016-01.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube January 2016.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2016-01.dck index 5909d03cc84..547a1c04d27 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube January 2016.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2016-01.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Legacy Cube January 2016 +Name=MTGO Legacy Cube 2016-01 [main] 1 Abbot of Keral Keep|ORI 1 Abhorrent Overlord|THS diff --git a/forge-gui/res/cube/MTGO Legacy Cube September 2016.dck b/forge-gui/res/cube/MTGO Legacy Cube 2016-09.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube September 2016.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2016-09.dck index 0a92c3bbff1..de9c046a3f8 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube September 2016.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2016-09.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Legacy Cube September 2016 +Name=MTGO Legacy Cube 2016-09 [main] 1 Abbot of Keral Keep|ORI 1 Abhorrent Overlord|THS diff --git a/forge-gui/res/cube/MTGO Legacy Cube January 2017.dck b/forge-gui/res/cube/MTGO Legacy Cube 2017-01.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube January 2017.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2017-01.dck index 5084d3d415a..fceabea4c74 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube January 2017.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2017-01.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Legacy Cube January 2017 +Name=MTGO Legacy Cube 2017-01 [main] 1 Abbot of Keral Keep|ORI 1 Abrupt Decay|MM3 diff --git a/forge-gui/res/cube/MTGO Legacy Cube April 2017.dck b/forge-gui/res/cube/MTGO Legacy Cube 2017-04.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube April 2017.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2017-04.dck index 97d5736f323..d2ec585eab1 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube April 2017.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2017-04.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Legacy Cube April 2017 +Name=MTGO Legacy Cube 2017-04 [main] 1 Abbot of Keral Keep|ORI 1 Abrupt Decay|MM3 diff --git a/forge-gui/res/cube/MTGO Legacy Cube February 2018.dck b/forge-gui/res/cube/MTGO Legacy Cube 2018-02.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube February 2018.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2018-02.dck index 91e259e8ca5..acd46d5522b 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube February 2018.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2018-02.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Legacy Cube February 2018 +Name=MTGO Legacy Cube 2018-02 [main] 1 Abbot of Keral Keep|ORI 1 Abrade|AKR diff --git a/forge-gui/res/cube/MTGO Legacy Cube July 2019.dck b/forge-gui/res/cube/MTGO Legacy Cube 2019-07.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube July 2019.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2019-07.dck index 4208d554cb4..9862b6d6694 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube July 2019.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2019-07.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Legacy Cube July 2019 +Name=MTGO Legacy Cube 2019-07 [main] 1 Abbot of Keral Keep|ORI 1 Abrade|AKR diff --git a/forge-gui/res/cube/MTGO Legacy Cube May 2021.dck b/forge-gui/res/cube/MTGO Legacy Cube 2021-05.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube May 2021.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2021-05.dck index a63ca261d28..756ee18a806 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube May 2021.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2021-05.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Legacy Cube May 2021 +Name=MTGO Legacy Cube 2021-05 [main] 1 Abbot of Keral Keep|ORI 1 Abrade|AKR diff --git a/forge-gui/res/cube/MTGO Legacy Cube August 2021.dck b/forge-gui/res/cube/MTGO Legacy Cube 2021-08.dck similarity index 99% rename from forge-gui/res/cube/MTGO Legacy Cube August 2021.dck rename to forge-gui/res/cube/MTGO Legacy Cube 2021-08.dck index 242eca293c0..b115e766e80 100644 --- a/forge-gui/res/cube/MTGO Legacy Cube August 2021.dck +++ b/forge-gui/res/cube/MTGO Legacy Cube 2021-08.dck @@ -1,5 +1,5 @@ [metadata] -name=MTGO Legacy Cube August 2021 +name=MTGO Legacy Cube 2021-08 [Main] 1 Abbot of Keral Keep|ORI 1 Abrade|HOU diff --git a/forge-gui/res/cube/MTGO Vintage Cube June 2016.dck b/forge-gui/res/cube/MTGO Vintage Cube 2016-06.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube June 2016.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2016-06.dck index b7347359918..74d0a16d494 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube June 2016.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2016-06.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube June 2016 +Name=MTGO Vintage Cube 2016-06 [main] 1 Abbot of Keral Keep|ORI 1 Abrupt Decay|MM3 diff --git a/forge-gui/res/cube/MTGO Vintage Cube November 2016.dck b/forge-gui/res/cube/MTGO Vintage Cube 2016-11.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube November 2016.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2016-11.dck index bbd38c368dc..136730617ef 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube November 2016.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2016-11.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube November 2016 +Name=MTGO Vintage Cube 2016-11 [main] 1 Abbot of Keral Keep|ORI 1 Abrupt Decay|MM3 diff --git a/forge-gui/res/cube/MTGO Vintage Cube June 2017.dck b/forge-gui/res/cube/MTGO Vintage Cube 2017-06.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube June 2017.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2017-06.dck index 0b26b70fe9c..23f1d83481f 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube June 2017.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2017-06.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube June 2017 +Name=MTGO Vintage Cube 2017-06 [main] 1 Abbot of Keral Keep|ORI 1 Abrupt Decay|MM3 diff --git a/forge-gui/res/cube/MTGO Vintage Cube December 2017.dck b/forge-gui/res/cube/MTGO Vintage Cube 2017-12.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube December 2017.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2017-12.dck index 4b716d3c509..8a68894e9e2 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube December 2017.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2017-12.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube December 2017 +Name=MTGO Vintage Cube 2017-12 [main] 1 Mana Flare|5ED 1 Zurgo Bellstriker|DTK diff --git a/forge-gui/res/cube/MTGO Vintage Cube June 2018.dck b/forge-gui/res/cube/MTGO Vintage Cube 2018-06.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube June 2018.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2018-06.dck index 10c3cc22842..5e765ed89bc 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube June 2018.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2018-06.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube June 2018 +Name=MTGO Vintage Cube 2018-06 [main] 1 Abbot of Keral Keep|ORI 1 Abrade|AKR diff --git a/forge-gui/res/cube/MTGO Vintage Cube December 2018.dck b/forge-gui/res/cube/MTGO Vintage Cube 2018-12.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube December 2018.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2018-12.dck index 02a6557ff8b..37c7edded9c 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube December 2018.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2018-12.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube December 2018 +Name=MTGO Vintage Cube 2018-12 [main] 1 Dauntless Bodyguard|DOM 1 Kytheon, Hero of Akros|ORI diff --git a/forge-gui/res/cube/MTGO Vintage Cube Summer 2019.dck b/forge-gui/res/cube/MTGO Vintage Cube 2019 Summer.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube Summer 2019.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2019 Summer.dck index c483d22c208..d10d6a7042d 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube Summer 2019.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2019 Summer.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube Summer 2019 +Name=MTGO Vintage Cube 2019 Summer [main] 1 Abbot of Keral Keep|ORI 1 Abrade|AKR diff --git a/forge-gui/res/cube/MTGO Vintage Cube June 2019.dck b/forge-gui/res/cube/MTGO Vintage Cube 2019-06.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube June 2019.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2019-06.dck index c2be3b17c6b..887f6f209ad 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube June 2019.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2019-06.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube June 2019 +Name=MTGO Vintage Cube 2019-06 [main] 1 Dauntless Bodyguard|DOM 1 Kytheon, Hero of Akros|ORI diff --git a/forge-gui/res/cube/MTGO Vintage Cube December 2019.dck b/forge-gui/res/cube/MTGO Vintage Cube 2019-12.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube December 2019.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2019-12.dck index ecd025bed18..5be563114e6 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube December 2019.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2019-12.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube December 2019 +Name=MTGO Vintage Cube 2019-12 [main] 1 Kytheon, Hero of Akros|ORI 1 Mother of Runes|EMA diff --git a/forge-gui/res/cube/MTGO Vintage Cube April 2020.dck b/forge-gui/res/cube/MTGO Vintage Cube 2020-04.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube April 2020.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2020-04.dck index 814390a4683..3a09392e175 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube April 2020.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2020-04.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube April 2020 +Name=MTGO Vintage Cube 2020-04 [main] 1 Kytheon, Hero of Akros|ORI 1 Mother of Runes|EMA diff --git a/forge-gui/res/cube/MTGO Vintage Cube July 2020.dck b/forge-gui/res/cube/MTGO Vintage Cube 2020-07.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube July 2020.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2020-07.dck index cecbf39b3a0..c0ea509b21e 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube July 2020.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2020-07.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube July 2020 +Name=MTGO Vintage Cube 2020-07 [main] 1 Kytheon, Hero of Akros|ORI 1 Mother of Runes|EMA diff --git a/forge-gui/res/cube/MTGO Vintage Cube December 2020.dck b/forge-gui/res/cube/MTGO Vintage Cube 2020-12.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube December 2020.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2020-12.dck index 9f87088b84c..1a901d616c7 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube December 2020.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2020-12.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube December 2020 +Name=MTGO Vintage Cube 2020-12 [main] 1 Kytheon, Hero of Akros|ORI 1 Mother of Runes|EMA diff --git a/forge-gui/res/cube/MTGO Vintage Cube July 2021.dck b/forge-gui/res/cube/MTGO Vintage Cube 2021-07.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube July 2021.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2021-07.dck index 0d8712c8bad..7b55209c5e4 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube July 2021.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2021-07.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube July 2021 +Name=MTGO Vintage Cube 2021-07 [main] 1 Abbot of Keral Keep|ORI 1 Abrade|AKR diff --git a/forge-gui/res/cube/MTGO Vintage Cube February 2022.dck b/forge-gui/res/cube/MTGO Vintage Cube 2022-02.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube February 2022.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2022-02.dck index 375ed2ea232..af5b7c0966a 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube February 2022.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2022-02.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube February 2022 +Name=MTGO Vintage Cube 2022-02 [main] 1 Abbot of Keral Keep|ORI 1 Abrade|DBL diff --git a/forge-gui/res/cube/MTGO Vintage Cube May 2023.dck b/forge-gui/res/cube/MTGO Vintage Cube 2023-05.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube May 2023.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2023-05.dck index 23d4f491802..19ad67c9b84 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube May 2023.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2023-05.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube May 2023 +Name=MTGO Vintage Cube 2023-05 [main] 1 Abrade|VOW 1 Acidic Slime|M13 diff --git a/forge-gui/res/cube/MTGO Vintage Cube August 2023.dck b/forge-gui/res/cube/MTGO Vintage Cube 2023-08.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube August 2023.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2023-08.dck index 20105ab915b..ae9a784681e 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube August 2023.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2023-08.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube August 2023 +Name=MTGO Vintage Cube 2023-08 [main] 1 Abrade|HOU 1 Acidic Slime|M10 diff --git a/forge-gui/res/cube/MTGO Vintage Cube October 2023.dck b/forge-gui/res/cube/MTGO Vintage Cube 2023-10.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube October 2023.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2023-10.dck index 1b4412a463a..71f68541abe 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube October 2023.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2023-10.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube October 2023 +Name=MTGO Vintage Cube 2023-10 [Main] 1 Abrade|HOU 1 Adanto Vanguard|XLN diff --git a/forge-gui/res/cube/MTGO Vintage Cube February 2024.dck b/forge-gui/res/cube/MTGO Vintage Cube 2024-02.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube February 2024.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2024-02.dck index 190b77933ec..43c4e3b00e0 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube February 2024.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2024-02.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube February 2024 +Name=MTGO Vintage Cube 2024-02 [main] 1 Abrade|HOU 1 Adeline, Resplendent Cathar|MID diff --git a/forge-gui/res/cube/MTGO Vintage Cube June 2024.dck b/forge-gui/res/cube/MTGO Vintage Cube 2024-06.dck similarity index 99% rename from forge-gui/res/cube/MTGO Vintage Cube June 2024.dck rename to forge-gui/res/cube/MTGO Vintage Cube 2024-06.dck index 5df4bc66017..48c3fc49d97 100644 --- a/forge-gui/res/cube/MTGO Vintage Cube June 2024.dck +++ b/forge-gui/res/cube/MTGO Vintage Cube 2024-06.dck @@ -1,5 +1,5 @@ [metadata] -Name=MTGO Vintage Cube June 2024 +Name=MTGO Vintage Cube 2024-06 [Main] 1 Abrade|HOU|1 1 Adeline, Resplendent Cathar|MID|1 diff --git a/forge-gui/res/draft/MTGO Cube 2014-03.draft b/forge-gui/res/draft/MTGO Cube 2014-03.draft new file mode 100644 index 00000000000..8d67c9d8c4d --- /dev/null +++ b/forge-gui/res/draft/MTGO Cube 2014-03.draft @@ -0,0 +1,6 @@ +Name:MTGO Cube 2014-03 +DeckFile:MTGO Cube 2014-03 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Cube March 2014.draft b/forge-gui/res/draft/MTGO Cube March 2014.draft deleted file mode 100644 index aeb3c2a9856..00000000000 --- a/forge-gui/res/draft/MTGO Cube March 2014.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Cube March 2014 -DeckFile:MTGO Cube March 2014 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Grixis Cube 2019-09 (540 Cards).draft b/forge-gui/res/draft/MTGO Grixis Cube 2019-09 (540 Cards).draft new file mode 100644 index 00000000000..4ee19d7fc55 --- /dev/null +++ b/forge-gui/res/draft/MTGO Grixis Cube 2019-09 (540 Cards).draft @@ -0,0 +1,6 @@ +Name:MTGO Grixis Cube 2019-09 (540 cards) +DeckFile:MTGO Grixis Cube 2019-09 (540 cards) +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Grixis Cube September 2019 (540 Cards).draft b/forge-gui/res/draft/MTGO Grixis Cube September 2019 (540 Cards).draft deleted file mode 100644 index 636d4dea560..00000000000 --- a/forge-gui/res/draft/MTGO Grixis Cube September 2019 (540 Cards).draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Grixis Cube September 2019 (540 cards) -DeckFile:MTGO Grixis Cube September 2019 (540 cards) -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2015-03.draft b/forge-gui/res/draft/MTGO Legacy Cube 2015-03.draft new file mode 100644 index 00000000000..9cee57a83b4 --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2015-03.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2015-03 +DeckFile:MTGO Legacy Cube 2015-03 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2015-09.draft b/forge-gui/res/draft/MTGO Legacy Cube 2015-09.draft new file mode 100644 index 00000000000..fc8a3d0d00c --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2015-09.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2015-09 +DeckFile:MTGO Legacy Cube 2015-09 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2016-01.draft b/forge-gui/res/draft/MTGO Legacy Cube 2016-01.draft new file mode 100644 index 00000000000..47ddc844ba9 --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2016-01.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2016-01 +DeckFile:MTGO Legacy Cube 2016-01 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2016-09.draft b/forge-gui/res/draft/MTGO Legacy Cube 2016-09.draft new file mode 100644 index 00000000000..160271357dc --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2016-09.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2016-09 +DeckFile:MTGO Legacy Cube 2016-09 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2017-01.draft b/forge-gui/res/draft/MTGO Legacy Cube 2017-01.draft new file mode 100644 index 00000000000..ae917cb9d49 --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2017-01.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2017-01 +DeckFile:MTGO Legacy Cube 2017-01 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2017-04.draft b/forge-gui/res/draft/MTGO Legacy Cube 2017-04.draft new file mode 100644 index 00000000000..1953dc7c245 --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2017-04.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2017-04 +DeckFile:MTGO Legacy Cube 2017-04 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2018-02.draft b/forge-gui/res/draft/MTGO Legacy Cube 2018-02.draft new file mode 100644 index 00000000000..07b99b81400 --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2018-02.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2018-02 +DeckFile:MTGO Legacy Cube 2018-02 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2019-07.draft b/forge-gui/res/draft/MTGO Legacy Cube 2019-07.draft new file mode 100644 index 00000000000..5a3748f4035 --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2019-07.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2019-07 +DeckFile:MTGO Legacy Cube 2019-07 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2021-05.draft b/forge-gui/res/draft/MTGO Legacy Cube 2021-05.draft new file mode 100644 index 00000000000..144ec57f600 --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2021-05.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2021-05 +DeckFile:MTGO Legacy Cube 2021-05 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube 2021-08.draft b/forge-gui/res/draft/MTGO Legacy Cube 2021-08.draft new file mode 100644 index 00000000000..b6e4e03e7cd --- /dev/null +++ b/forge-gui/res/draft/MTGO Legacy Cube 2021-08.draft @@ -0,0 +1,6 @@ +Name:MTGO Legacy Cube 2021-08 +DeckFile:MTGO Legacy Cube 2021-08 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube April 2017.draft b/forge-gui/res/draft/MTGO Legacy Cube April 2017.draft deleted file mode 100644 index d9e780a918b..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube April 2017.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube April 2017 -DeckFile:MTGO Legacy Cube April 2017 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube August 2021.draft b/forge-gui/res/draft/MTGO Legacy Cube August 2021.draft deleted file mode 100644 index fa79526b553..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube August 2021.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube August 2021 -DeckFile:MTGO Legacy Cube August 2021 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube February 2018.draft b/forge-gui/res/draft/MTGO Legacy Cube February 2018.draft deleted file mode 100644 index c966a44dd34..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube February 2018.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube February 2018 -DeckFile:MTGO Legacy Cube February 2018 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube January 2016.draft b/forge-gui/res/draft/MTGO Legacy Cube January 2016.draft deleted file mode 100644 index 47a12c57e55..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube January 2016.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube January 2016 -DeckFile:MTGO Legacy Cube January 2016 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube January 2017.draft b/forge-gui/res/draft/MTGO Legacy Cube January 2017.draft deleted file mode 100644 index 564084593fe..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube January 2017.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube January 2017 -DeckFile:MTGO Legacy Cube January 2017 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube July 2019.draft b/forge-gui/res/draft/MTGO Legacy Cube July 2019.draft deleted file mode 100644 index 2a69d64d21b..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube July 2019.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube July 2019 -DeckFile:MTGO Legacy Cube July 2019 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube March 2015.draft b/forge-gui/res/draft/MTGO Legacy Cube March 2015.draft deleted file mode 100644 index f8db27c20d2..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube March 2015.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube March 2015 -DeckFile:MTGO Legacy Cube March 2015 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube May 2021.draft b/forge-gui/res/draft/MTGO Legacy Cube May 2021.draft deleted file mode 100644 index c2c572e93a9..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube May 2021.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube May 2021 -DeckFile:MTGO Legacy Cube May 2021 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube September 2015.draft b/forge-gui/res/draft/MTGO Legacy Cube September 2015.draft deleted file mode 100644 index f7ae9605f61..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube September 2015.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube September 2015 -DeckFile:MTGO Legacy Cube September 2015 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Legacy Cube September 2016.draft b/forge-gui/res/draft/MTGO Legacy Cube September 2016.draft deleted file mode 100644 index c6fd9b3a9a7..00000000000 --- a/forge-gui/res/draft/MTGO Legacy Cube September 2016.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Legacy Cube September 2016 -DeckFile:MTGO Legacy Cube September 2016 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2016-06.draft b/forge-gui/res/draft/MTGO Vintage Cube 2016-06.draft new file mode 100644 index 00000000000..ceac948b616 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2016-06.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2016-06 +DeckFile:MTGO Vintage Cube 2016-06 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2016-11.draft b/forge-gui/res/draft/MTGO Vintage Cube 2016-11.draft new file mode 100644 index 00000000000..d48a62dcae2 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2016-11.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2016-11 +DeckFile:MTGO Vintage Cube 2016-11 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2017-06.draft b/forge-gui/res/draft/MTGO Vintage Cube 2017-06.draft new file mode 100644 index 00000000000..83bc52dc71d --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2017-06.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2017-06 +DeckFile:MTGO Vintage Cube 2017-06 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2017-12.draft b/forge-gui/res/draft/MTGO Vintage Cube 2017-12.draft new file mode 100644 index 00000000000..d78880bc8f3 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2017-12.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2017-12 +DeckFile:MTGO Vintage Cube 2017-12 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2018-06.draft b/forge-gui/res/draft/MTGO Vintage Cube 2018-06.draft new file mode 100644 index 00000000000..536bb89ca16 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2018-06.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2018-06 +DeckFile:MTGO Vintage Cube 2018-06 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2018-12.draft b/forge-gui/res/draft/MTGO Vintage Cube 2018-12.draft new file mode 100644 index 00000000000..813f7c7b326 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2018-12.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2018-12 +DeckFile:MTGO Vintage Cube 2018-12 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2019 Summer.draft b/forge-gui/res/draft/MTGO Vintage Cube 2019 Summer.draft new file mode 100644 index 00000000000..7238d53082a --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2019 Summer.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2019 Summer +DeckFile:MTGO Vintage Cube 2019 Summer +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2019-06.draft b/forge-gui/res/draft/MTGO Vintage Cube 2019-06.draft new file mode 100644 index 00000000000..ac831a6e981 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2019-06.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2019-06 +DeckFile:MTGO Vintage Cube 2019-06 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2019-12.draft b/forge-gui/res/draft/MTGO Vintage Cube 2019-12.draft new file mode 100644 index 00000000000..8e12dbfce01 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2019-12.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2019-12 +DeckFile:MTGO Vintage Cube 2019-12 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2020-04.draft b/forge-gui/res/draft/MTGO Vintage Cube 2020-04.draft new file mode 100644 index 00000000000..0c1ea6f2f14 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2020-04.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2020-04 +DeckFile:MTGO Vintage Cube 2020-04 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2020-07.draft b/forge-gui/res/draft/MTGO Vintage Cube 2020-07.draft new file mode 100644 index 00000000000..69698093ef0 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2020-07.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2020-07 +DeckFile:MTGO Vintage Cube 2020-07 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2020-12.draft b/forge-gui/res/draft/MTGO Vintage Cube 2020-12.draft new file mode 100644 index 00000000000..c9612cd0fbd --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2020-12.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2020-12 +DeckFile:MTGO Vintage Cube 2020-12 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2021-07.draft b/forge-gui/res/draft/MTGO Vintage Cube 2021-07.draft new file mode 100644 index 00000000000..08a499e587f --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2021-07.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2021-07 +DeckFile:MTGO Vintage Cube 2021-07 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2022-02.draft b/forge-gui/res/draft/MTGO Vintage Cube 2022-02.draft new file mode 100644 index 00000000000..4acfbd3c9a6 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2022-02.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2022-02 +DeckFile:MTGO Vintage Cube 2022-02 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2023-05.draft b/forge-gui/res/draft/MTGO Vintage Cube 2023-05.draft new file mode 100644 index 00000000000..736c384e75c --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2023-05.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2023-05 +DeckFile:MTGO Vintage Cube 2023-05 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2023-08.draft b/forge-gui/res/draft/MTGO Vintage Cube 2023-08.draft new file mode 100644 index 00000000000..69c990cdff0 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2023-08.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2023-08 +DeckFile:MTGO Vintage Cube 2023-08 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2023-10.draft b/forge-gui/res/draft/MTGO Vintage Cube 2023-10.draft new file mode 100644 index 00000000000..a739beeebe3 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2023-10.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2023-10 +DeckFile:MTGO Vintage Cube 2023-10 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2024-02.draft b/forge-gui/res/draft/MTGO Vintage Cube 2024-02.draft new file mode 100644 index 00000000000..6f69c8c0835 --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2024-02.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2024-02 +DeckFile:MTGO Vintage Cube 2024-02 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube 2024-06.draft b/forge-gui/res/draft/MTGO Vintage Cube 2024-06.draft new file mode 100644 index 00000000000..b3227e990ae --- /dev/null +++ b/forge-gui/res/draft/MTGO Vintage Cube 2024-06.draft @@ -0,0 +1,6 @@ +Name:MTGO Vintage Cube 2024-06 +DeckFile:MTGO Vintage Cube 2024-06 +Singleton:True + +Booster: 15 Any +NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube April 2020.draft b/forge-gui/res/draft/MTGO Vintage Cube April 2020.draft deleted file mode 100644 index bcfed364059..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube April 2020.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube April 2020 -DeckFile:MTGO Vintage Cube April 2020 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube August 2023.draft b/forge-gui/res/draft/MTGO Vintage Cube August 2023.draft deleted file mode 100644 index 8927dec14fa..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube August 2023.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube August 2023 -DeckFile:MTGO Vintage Cube August 2023 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube December 2017.draft b/forge-gui/res/draft/MTGO Vintage Cube December 2017.draft deleted file mode 100644 index 1496f54fb13..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube December 2017.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube December 2017 -DeckFile:MTGO Vintage Cube December 2017 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube December 2018.draft b/forge-gui/res/draft/MTGO Vintage Cube December 2018.draft deleted file mode 100644 index fb31024b39c..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube December 2018.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube December 2018 -DeckFile:MTGO Vintage Cube December 2018 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube December 2019.draft b/forge-gui/res/draft/MTGO Vintage Cube December 2019.draft deleted file mode 100644 index cfcaf43247a..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube December 2019.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube December 2019 -DeckFile:MTGO Vintage Cube December 2019 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube December 2020.draft b/forge-gui/res/draft/MTGO Vintage Cube December 2020.draft deleted file mode 100644 index b9c7b3ff5a7..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube December 2020.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube December 2020 -DeckFile:MTGO Vintage Cube December 2020 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube February 2022.draft b/forge-gui/res/draft/MTGO Vintage Cube February 2022.draft deleted file mode 100644 index 6f79ac5b40b..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube February 2022.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube February 2022 -DeckFile:MTGO Vintage Cube February 2022 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube February 2024.draft b/forge-gui/res/draft/MTGO Vintage Cube February 2024.draft deleted file mode 100644 index 36762d0d883..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube February 2024.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube February 2024 -DeckFile:MTGO Vintage Cube February 2024 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube July 2020.draft b/forge-gui/res/draft/MTGO Vintage Cube July 2020.draft deleted file mode 100644 index 51e0ba15b29..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube July 2020.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube July 2020 -DeckFile:MTGO Vintage Cube July 2020 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube July 2021.draft b/forge-gui/res/draft/MTGO Vintage Cube July 2021.draft deleted file mode 100644 index 74986f7d472..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube July 2021.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube July 2021 -DeckFile:MTGO Vintage Cube July 2021 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube June 2016.draft b/forge-gui/res/draft/MTGO Vintage Cube June 2016.draft deleted file mode 100644 index 08f63ef1159..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube June 2016.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube June 2016 -DeckFile:MTGO Vintage Cube June 2016 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube June 2017.draft b/forge-gui/res/draft/MTGO Vintage Cube June 2017.draft deleted file mode 100644 index 48f394ae6bf..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube June 2017.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube June 2017 -DeckFile:MTGO Vintage Cube June 2017 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube June 2018.draft b/forge-gui/res/draft/MTGO Vintage Cube June 2018.draft deleted file mode 100644 index 3604766b322..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube June 2018.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube June 2018 -DeckFile:MTGO Vintage Cube June 2018 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube June 2019.draft b/forge-gui/res/draft/MTGO Vintage Cube June 2019.draft deleted file mode 100644 index 63351922a12..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube June 2019.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube June 2019 -DeckFile:MTGO Vintage Cube June 2019 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube June 2024.draft b/forge-gui/res/draft/MTGO Vintage Cube June 2024.draft deleted file mode 100644 index f0cebffaf08..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube June 2024.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube June 2024 -DeckFile:MTGO Vintage Cube June 2024 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube May 2023.draft b/forge-gui/res/draft/MTGO Vintage Cube May 2023.draft deleted file mode 100644 index 0fa0fbee353..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube May 2023.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube May 2023 -DeckFile:MTGO Vintage Cube May 2023 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube November 2016.draft b/forge-gui/res/draft/MTGO Vintage Cube November 2016.draft deleted file mode 100644 index 85c9c797dc9..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube November 2016.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube November 2016 -DeckFile:MTGO Vintage Cube November 2016 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube October 2023.draft b/forge-gui/res/draft/MTGO Vintage Cube October 2023.draft deleted file mode 100644 index 6d7e4e8f51a..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube October 2023.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube October 2023 -DeckFile:MTGO Vintage Cube October 2023 -Singleton:True - -Booster: 15 Any -NumPacks: 3 diff --git a/forge-gui/res/draft/MTGO Vintage Cube Summer 2019.draft b/forge-gui/res/draft/MTGO Vintage Cube Summer 2019.draft deleted file mode 100644 index 4a29b23dfd8..00000000000 --- a/forge-gui/res/draft/MTGO Vintage Cube Summer 2019.draft +++ /dev/null @@ -1,6 +0,0 @@ -Name:MTGO Vintage Cube Summer 2019 -DeckFile:MTGO Vintage Cube Summer 2019 -Singleton:True - -Booster: 15 Any -NumPacks: 3 From da46cf785e9d12fb89b2a765c1dec95a3a891f91 Mon Sep 17 00:00:00 2001 From: Agetian Date: Thu, 28 Nov 2024 20:56:16 +0300 Subject: [PATCH 130/152] - Kayla's Reconstruction AI hint. --- forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt b/forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt index b129fb078c4..7e993e8b6d3 100644 --- a/forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt +++ b/forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt @@ -1,7 +1,7 @@ Name:Kayla's Reconstruction ManaCost:X W W W Types:Sorcery -A:SP$ Dig | DigNum$ 7 | ChangeNum$ X | Optional$ True | ChangeValid$ Creature.cmcLE3,Artifact.cmcLE3 | DestinationZone$ Battlefield | RestRandomOrder$ True | SpellDescription$ Look at the top seven cards of your library. Put up to X artifact and/or creature cards with mana value 3 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order. +A:SP$ Dig | DigNum$ 7 | ChangeNum$ X | Optional$ True | ChangeValid$ Creature.cmcLE3,Artifact.cmcLE3 | DestinationZone$ Battlefield | RestRandomOrder$ True | AILogic$ PayX | SpellDescription$ Look at the top seven cards of your library. Put up to X artifact and/or creature cards with mana value 3 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order. SVar:X:Count$xPaid DeckHints:Type$Artifact Oracle:Look at the top seven cards of your library. Put up to X artifact and/or creature cards with mana value 3 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order. From 9878544b5a9b1e4f9aa432bc743b091f061e54f5 Mon Sep 17 00:00:00 2001 From: Agetian Date: Thu, 28 Nov 2024 20:56:55 +0300 Subject: [PATCH 131/152] - Kayla's Reconstruction AI hint. (#6642) --- forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt b/forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt index b129fb078c4..7e993e8b6d3 100644 --- a/forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt +++ b/forge-gui/res/cardsfolder/k/kaylas_reconstruction.txt @@ -1,7 +1,7 @@ Name:Kayla's Reconstruction ManaCost:X W W W Types:Sorcery -A:SP$ Dig | DigNum$ 7 | ChangeNum$ X | Optional$ True | ChangeValid$ Creature.cmcLE3,Artifact.cmcLE3 | DestinationZone$ Battlefield | RestRandomOrder$ True | SpellDescription$ Look at the top seven cards of your library. Put up to X artifact and/or creature cards with mana value 3 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order. +A:SP$ Dig | DigNum$ 7 | ChangeNum$ X | Optional$ True | ChangeValid$ Creature.cmcLE3,Artifact.cmcLE3 | DestinationZone$ Battlefield | RestRandomOrder$ True | AILogic$ PayX | SpellDescription$ Look at the top seven cards of your library. Put up to X artifact and/or creature cards with mana value 3 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order. SVar:X:Count$xPaid DeckHints:Type$Artifact Oracle:Look at the top seven cards of your library. Put up to X artifact and/or creature cards with mana value 3 or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order. From 4c87dce4e90289029ea44cdb923a726c14e3df7f Mon Sep 17 00:00:00 2001 From: Agetian Date: Thu, 28 Nov 2024 21:28:44 +0300 Subject: [PATCH 132/152] - Update LDA deckgen info for the current Standard and Modern formats. --- forge-gui/res/deckgendecks/Modern.lda.dat | Bin 173740 -> 187129 bytes forge-gui/res/deckgendecks/Modern.raw.dat | Bin 160501 -> 180973 bytes forge-gui/res/deckgendecks/Standard.lda.dat | Bin 136263 -> 201056 bytes forge-gui/res/deckgendecks/Standard.raw.dat | Bin 114282 -> 187061 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/forge-gui/res/deckgendecks/Modern.lda.dat b/forge-gui/res/deckgendecks/Modern.lda.dat index 27013a148ad45468f7eb1c4cd795cb266384fd39..51eddd292c5a762476b09b20dc43bea8b5192fe5 100644 GIT binary patch literal 187129 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 E2gDD?7ytkO literal 173740 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 z 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 RqFJym2jT}8^^Rw!{2u{p$Zh}t diff --git a/forge-gui/res/deckgendecks/Modern.raw.dat b/forge-gui/res/deckgendecks/Modern.raw.dat index e78189a3e16896d5fe9a3b2f1a396a3e85e29a35..f3350bfe8f2ae5980523c8f384c07bd573a88383 100644 GIT binary patch literal 180973 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

9s02B$P^})BTCwQN=OV(v)87QY* zFV^XgbUCuS?5w4H>==zvbwROvx1Iia_Gx(#BjBMnY`{)03>|}mQF7m_ReVB#_OM$SkrIaCBA+n+(daUMNR6c2JH|g@ z$}43U7m8bRIE%nc8yLwpNk(GO1O2UaDrZ`hu&SmMBz zASprl^=ox4>!zp%KQrp|V z8=1d7b6S8d)G>M}qG2MG8XIvoFJHK~Odf8P5cb%sJQOtA95-}lBFf8Godv8bq}XPB1L{pv%TE9VXpt?_+9+i>5kfOMC!F2mz)Y#hBK;JQG@XPBsCw z($;l;l3&K}Cjk`ok-o{;)T*=(0W`(Z1~pE7u>KPVn6DX&@pM%=>#VIbxXr#stBKac zO!^gpk>e-L&ie9(05cB(EMsrR?KlBed^dwKg_ z=;HMdV^@Ga(yxe3(*U%Tr<&W~fk7l0pe z5D;QmUI={@{avuq0+f+K3y7FG4J!y__-Z1{lN4|lgi1UG@r54b$NZI~qQ1BD@Dwr#-w@)&0SXEhqaMJ!(i<# zbEzMKUEb^_?T++Rj^yNzoV($ly!l6jQ4$lxCzM6)CrFxf+cpp8hd#Y^~+$9}~jzLZI?QXgn^C419D`2PCa_AH_F0 z_Kqn0V~wx=#f2-Is0(rvShj9Lo>7PW1dxyN;;P{#??&ZDpO5(Rkl0YLTJo)84UF>@xkts3 zl1y__mPPTC2YWa&yQ%<8c6f-y;$}4=2{VcNt;*Ye0-F!NjipYFmW#0uV$YP-hyV)v z(gL&@r~s(0Zb6Bmkui8ww`3rpJ2s?mL`A6~`-(RGrC)XWeCdDN37`}rxxZI`TJG3h0_>&>ifz|_RvY`?f&eGJ ziz3rOpTPb9Ys^m{(lU^b#GBtY`PnmgGEtCGZ?oC48ZResRgFaN(!Ul(IV=~||a zu$yw?vjm7hurkh8PJM0?1u4SA z$H!MnyVi{Z(68E|-g#V1tZQ2({eh|pp2$pI0a}18z6nP+UHs~Ef_k$zk$#23VF=(m z;iek?G01+Bz|tU*r`_OrE&BCb7%mUWp=~(Qq9^l2NwvLkg(UA!VVB}{Iij3{H0j!i zj)I*w?XjBSPa~itD*ZQ~yzqx^nasMRS7O452vlIe)h<#KCLjg_0sBf#f`A4sYA2dZ zB|fI|7Onyy|KJD3ch4AjU`D%oqKLW}O4q0WC$G29hy`Vb(*iDhZs_#C4{LsQ^Zlbl zVRl}y(gKukMhlSLKnu`ilop^og<62>!wEo=L9y(o5Ee{GX^a18H4@5_oOkp?wV*|% z=+Vf`m^VFRWZC9D2(Xp~PTolSxu+i}Qi4*9=5_6H_S&7fL{VhDcuRRj;u^cvxK2P( zdjaGO*f!g``#1t9ukzMEdw9?J%uk(c8c`DXjmJktEGzMqD4ezkRvW&A&aB!)nc?NQ zB?+~|uU@NmdWNE+kVH-2aW%TZ7jc$FAC%8Bb;^kMgFfhaADBtV%)%6PZi|8Qf->9J zY<0d-_vjrOjg7qHtQz#yX+)hW1dxlc%Vae6=h+OO`FzQl^8z}PR3|FU|?w;J|V>D&_EJn8wJ zTgq^0MMU|JI{tXT5MpI2A95pd#=o7L&U=(tZ4jLY4Vu;QQ4Qr>R{Vzm1}?I_x4UzF zVr6_x0R1Lc{IASyO9E&`^txZI#EJ6!14ET^Ks5uYQZK(AYoACLM(vj-&$!=rK5-}_ zkFw1wi$5YxD0*E{o#;~wP<^<1R%PNHt_E;c@Wn!3(0y?N>zo`tTo7y8=-`|Pw?r0h z6{T*$)})IGev}Ku53>*KZ^x!m8@5IjgPWr*5S}!>I9E^AcyX>4vwzh3ZIHjih8LH| zhkvRmkKDc&GDU~8Tu{jLIwV=C@4dF`lb3%~y(PDE^kfB|mAc~wb;Z_<&I}4(H=eh| z0E<78eueqz)R5}gnL?SvrqMY$E>OCOM>vQ<{SQ?$&|AI}^vjwDM3(Y#Q5f$Qz+&gR zuRmn*Zt%tJW!cYMeWWTDqTPZImWdaBZY2iRcLgvvxY==3TM035yxidhqvk7#6igtG=;_cD?5i;NmMNQ~;hvDnQI0 z#UE#XgNOi4JKJ`FBSxUi8R4i6CLiaXL3+e>`Byw#Q?Q#BJ$)7F-IC!+3%50s5~ z(NiDxX)p4Vv?Nzj3jxfU7XsLFol2+IvZAx83c&eRx~WP)HbUq;Zo$Fu4Wq2Rs-}3k zVs-w?-13ZiINS$op7f7w*%pyP8rTzA`rt>&N$g801_SM?ru&DT!;^;jJmEHLMs3d&JPz|=+_?jPQUKAe?ERVTu&jcEQr%bjWRQvovQ!|0!XdY$D_+E{`sUw;yMQ6AD5XN zxZX(^jitrQ>#D**Nir~#ykGp}uv?_(WX>8NFd!UGukb6x(vmJ}nc+xurfQ1!N2zG$ zH5&VeQ9CYIA!>@DFca>dvB?JyQCyI9dQCGs|CopnWa1y>+98?6zKLE`tn}3qkOz72{I1!MNx^uL6+e$seBa(hf#B=rk>g`-euyhr;Z_QpB`6<6-BJ%Rd_r*;N_nLn646ij_K-j!TZk-DWgS6o^6-fbD~bdV^>GjHSO36B>Db(am7 z(hFqO|GSdkIYs%TVa0_SBuHLIKi@ZsDoLpjRzx<8To?JCI;T7} zOAbt_`o7N+0?5;Ed`?nBt3Q5IfXmXa$nv?+>Ftg2y$LYM7C^w&Vh_AN@J&YgQ1W@A zLxr%&?+>OCg*pG2I(@TU_7ej4DXi#DUkf3Q=ve@4udWB7FJ8663X9@PVv-Gz!Jw1udoukq(Z%^Yp z2YVKZfZ0(#+p~-1UkZghjJ8;a5Xu5kU6mydS4y=ld!O6=69Jod62EOU?9}{NtLUzS3^L zy49Np8-C|IIU6qB*sQp|>*j!t;lzqc+s<~Gawo^eTLFqozv}d3EgL>hIpjuwv#$Vx zKXeaGrK{jVS96o}aJiy0n)fGxP+CdX57N2%A2 zmr6B@Ahr~QBmjPRVsq=4TYtXeBf0(0!TbJKzaz15h!P{)(b6~MQ1!M1(4@;hUh~}E z0ZR#>36X!`$9~7ha}_|IjrpBxr>*oTN&wBq+!|*>9*O84_UdUi=KS3M<$rYs69s+u zoF*5FW?Y#>08P_vGZu%u4N-(9O}g~`13r#^(3mJ_nx>nasX21A@=a(quJ!o)(SNVF zypTp!RU+2uSGMyD@OEBEtY`|YaQlAi;7hK-six3EuOA-oXDc&>u0Q9E2_87@fwI;q z5!)=AQ>$Lwol1Ssl~VkPA-9d+aea>UL2G1aiOH=S)l*iLNmDW0sBQO#?kl~5SW!Gm z*u=A^+#MBTl0&SZ=;gJ^;{7~+#$tVt>#y&#JEwYC8xt$DuY#gZrERks?&G^Zqp*E0 zfbt7VjTjryPnm&Q=|qyl~GoXdY!Pf99Ch^1`E{btG)AvS)Bz00XT-0k&UE}W%TQCU1#_Mr;>qHA_j zI+quJ_KYH-i_;VVLn3KY#IX4;0KlL zy!2D~Ks09;b!t*?Ws6M;D^83nd+%NTTL$eRfJDT+6Cdwp)^0%nX~}boY%E{@?PCHI zhw6%ZyB_VMOf*Us7q>q5&xz}l)k=xH;+zNHYxiIuv7$_6VOh>6*RQHV0NEfbUES6= z>Mw(3%=IIkUI5B`QpKy|%h7ImaMiS_As*L=H_eVcJ^y!XQ#VBt&*pR!PB2JWb_d2;vg9O{Fq~ZMvvR{T0_YuOl$dF3 zbaxE_CPSrPp`!gh>bD*p|0AH(4FRP8dwW%}EGq)&HZuKC(Q7Y}J`-<4Dw{yEW9_r@ z0}^g4Z1Co%-t40t2efvV#lEQ)IRy|l7q)Y6DzufY&!Gan(Rr6s3VnG3l!lmTBlb5{9uXu zY36I->K7A3c_>6PriU4%ST7~%U8QIoq%v%G8F4Y*;v)ex#qw@F++1VtF#>2q%iClz zuA%p00%*eIK3#mKN6JkCXo}?w+#a&Lk}}0;QfdL3#+xlxO={e)J+ZRldo)s`b-nj$ z!e~XUCh3=D|NA?qS`&zZW^4N9;s4xd??nLZDXD9AxC8|&2MYR_sWufBj4FA7C}?9| zb^eXj0E1D$dD-R*G}J*YogXcNN9RQT-dK>+P~LrxVL@WP&paVF}`xm?oe2UPjk zu9BRJmI0l`sCrq)W;AZ6h!{Gjbj&<;E-y~eCFyX{;@;Q_ThA&Rs5Q6zAS^@+3=@3_ zfX!8_bNFFkj@Fh{EX`LP(kA>}tz`CYgHxTXf96$qF($E53_Uwdzue5(pxI0SC2aZy zkd?Vyzb+NuZ&M^gscmAw+nnaUnsq~2Ei_@bPD%~Rebk2fFdi=`w%m7p(%EUA0_+w* z_S>|f&KZglWF9Ag4Cw)tU72R`x_`s(I*gx5tVoKcuZ?)z?m;~QXkMlcexBrh`7{Bp zeCI~itC^RM1_bXVfad;G_sgfI{~`82Tv4@y+XY;yWeb=$9GxU0oqpVv>n|s-Uqmb@ zBV?TZK*K>-?-M|ibHF{9y#HeD37`w7_N&?Z&Lx110a}1O=2ZYJ;2mMmhHVQ5y>5t0 zh!~VC@zUv4?J&W}HChW$B?T=&3q=c%hNA_joRSuxutWhU`@GiNL-J$CmHp-uYo4Pw zLvwojINyZJyUP$jI$i$Cod>NVGYKF8nLq60Foyw8381N)JALipDSKKe0GASQWNv-5 z?$Xpl1dwx1j%|mh_gkD*02}F7SarIs99i_MB4tR>Z+h7ETC@KBh=S(Zy5n)x9)HsvFpgBt;~)uuG@YX8?MgC!`dhx69oj@h&{lqg6&oHhUa zp~R(%{e;xR#D!iiUG*c0g0#Tkp_WUVO#Ms%DSN*EM&>`+c9;OlCEM;!qnDm0ie^A& zD=i?x3UqocKs&M)po)_!0IR`2P;#5*d7T4LOj;V&ubj#0OvAv=3l$3k$^Tzo@v2r> zOh#|je3=-xJ|_JeJC19WS4ymPA|i6a;3LZPp_iCZ@y`3L*^bIfl=ZH4rRPmBj#YMj znk8DGSWPjcwbQ#i^uDd^MaFz1!)AMLZ2EQQJJbiggGG5;-W|zP4k|R;Q?dicJ0)?k z%?$cH`5?QjU_mT&cd#(S$u_K&qOy>DSg`8&id{yRsYhC>^XAsv(P#P#0?3_kwsD2E zKdRaiKx=r$+bR3jZdEQ+NVJaIFwX6A-%Uh88Jdzyro3!)N70JOjW%NZ(=txglpTPS zjzG_mQ~n=i-vM29(ZrixxT%nk(2@{B2T_WMEp$*oN+_a&gd{*9BoGoXNbkL)(tDGZ zPz4bYK}E2Dh=3Kb;b%j|uDmIGXY$|N`_6mc`OZDW-_C6L@6OK5&dzS^!j>Ru3N47M zE44@E#Lov@uOxMdB4q=9O+8UitR_6)1MfIN5Oh8S;&jT*8`!RPC1RB3*3v4??Qp}l8JA7KmrcKPs|8`Ymc2 zpQ|zCfKbHqJ^@JqhCkkEz@-%e$P;VJH_a_~d;UBD;#m!LrOx|fS@~ZtsC1P*dUob7 zOB%OSPpHJ3O+TL&owsL{u##!v0N`k>)nGW)YzWtiA)!nw_^goRm8~2&o3eNWurA|D zo$|oGMZ2p$%FH1Gts}f`T>#fd;Ib!6XIJX@o+*j%#}v7Xz>)P}xR5$19S+7crG`}~ z7>M!Cia;|$I~k_AuC^_fKW7#g9K7|(&VW3TN${F?KA3k<65{x+GEu!D>^FlH zpWa#da7gE=7*Lwr`=IU@O&eZuo%sX+RQ=LW*np^74ggG;*7ykRsDYT3MZbun5U`aD zjy4d;OQdW=!dP-z07UC!F2EwVFqm01xK!#Lo3Hr&Y`}-o ziZ8`9Km1Iv)FENI#kC&V`_D@Di6Dg;R`3kmQT_L~>P88Lg7QSAXJY4VX(2%1YQw5; z-qCkob3Zf*K{Sx%BoD)4va<0Cbw<{3j7KqLJKq1r&5K?66Vp_4f1+j|v}p2U@_)iT zgdIGt)VLA7`!!zvivSX8?f@X%g41X5C8IH=uhE!-xKmfifuG?1@RRr~{8U>2KmL{A zrxKnm#x8-Ma+&zw7Q&AY%P(9yb|8H8G1H%Y{++hWh>nrgMQ<^{_Ht)}M*Lh}0GU8r z=XQPI<9WPSfeH`?aSw0_By9wQ?eH9dt0ZTa^j&&|7cLHxylbQwZfF?HB;N> z&hd+VM!6UO)w~VnYt2X>0dap>rSH3}Th%*v-jpDBITt;>2wNjOMQJ##t@6>9Kdbt4 z38*_V&I=H{!4rVtNcm$S9uF__qLmCO&GcI3>eT~3&N@^|UFP`*>Wc+P_W4y+Z^T^a zs?$R5M66ho*ZIL4e{(xw7~4apg#2tr3iRcPzCTYp{Ik0Vzg9Q~q6s0?rejvlaMS(; zt(&dSO?`)pL67BLs}01yVkqXeX&u>N)SGU`kggt*`M^kz8YLI9X0?4}Lee(2Qb>Nd zSL&4ZgCBn{@~E3Ow&xIVCtylQ%C?r2HXPh`EWse--GZ-|KRT1`OPDG#kj_7SG$%5G z^kzAEqcqIsDfs;5el!jh=!9rm`AaKxd#O(^?cF)k2^)w$;sprn=LrC(O{esHFy1;- ziUjEWV7OpqN@mx~eB#pytPoM}Cwl?H-21dm5zTVOm1+Y4tvs>TA_0Oca74fyW$LJ{be2{wf!8?^yZ z(rv)+pqDzt(_vFocWZYBU}YXfm+kZsuh+%vU5aKuwTMRsdR*=qQ|X(>Mvkx6XpjKD ztN^%DcU2q_H0j6Q0?1}9c!sW|OYr0o{^~vf#2y{`{CzuLkGmj%D8HF? zJNIso(MSL}39^B(@2HF`)d65E+!%C*!W$o@qHG;ypiBPM4e{gMj{4TK=R> z5HsEeBt-!>>|YB^z$ z22YjX9-lqD5b|R|m?I(Z0AcBJ$e_>w%PK#zYuqzztm3jsbW6hp9q!G~9(%7;DH3FM zw^_HVuUBO`oP9B_)M8RhLjDk;X?jb$X^Lpb4|&D%QqfgR_`wQ3oG?o z?*&NaPYjpHEDA}gK2Hq;j zAKa~QkbC4DC4khomX}WWbn^npO+A~HyfKsYLD(B1Kep6?a%*EYAfm zAn$}23bMmvzNFHn1P?TmugJJ~nIhy*Cb~aB(LdwQEckIse?<0ClSlD z9CD?eh|2x$xZh%;#_2ElTei-s_}ujAZ3U1R($!B+10uCUL*07r@?Y<4_d%}ufh$H4 zG3?$0KkQgLn9UWeLsGWcK)KC^Vqx^*XYapyN-CGTH1j46Trsb}m_xuyR396VTyE3O zWvqWX^+#bPo`i{MqaJShWvBqLhfL)*Aa`PHKvr&HL?SX1Rw95M06K89K=()4Zjwfu z8Xd*(Bk(icz$(YA77aTTg02Qnyn!259tpAvMuR}dOAz?*NzbAT&5~`E zi)odcmrd^6Zt$X)GjEuqD73a>vgw}<1n}~JoE=vG0(`tsghqP;U?0L4Sj~n2+d>*9 zcJ@>fS|biVN2RcVnaDf0eSoiFMLH0a!`Qe?1d>`=tfD}Fmc(M zLTVq>ArYjDT(s4<|BX15_wKh;D}MXnW~Kof9$M5WW`(~15>vFQ`8S38C(IPU*FC=+ z0JN+NNc2HqrqQD=nTG8%-G*O1`b1-)meV&IkT*gX5BVkal@^~c1%3;3^pC-d(n)FX zx-m=+7E!KLt7JSJT|x^m4M)4tc{!uOf@lXhxre8lrp*6#*cY?A^Hv2$V-@hxK(-Ia z3IXrOfvMgOjEy3^NdgZd8j5LEM(=qxOb@_ZZY$9SL=sFG{73EkE?$=I%4;e%AffO! zAV<;;0Giu3J-={N14^aij;QFG13BekU`%rhR`}h7uPBwv7Ulq8`9cB?c+?;ZUzdZo z4hHKZV2*+?JH%A50oU(d>J-b)L4l6EfjQt)z$gp2jyDYa=Wc!3Q4s1(n(u|RWaiCU zhTkPz`YCraY(SJm8z{$y6^Pr?tiT!5zHo3hzGMvZATw(q%nzMyj-t>`-(9AiHXtt} zIsjk}hDI(tn9O%A3mbMAl0KH*KxNty(G4z(fjgR7GSfK9K~2eSIcftHcxqVT^0z~@2TlP z;m79^Whun_U@GV14>qY$x6-unKJNjam^k=}XbL|;HK{5rQ-OqE1^OFhoR8bPbgII# zxOT8DdVG7@K= zi?6;<0ErQ>0nyldhHTvu9#>T;#7$}gK5ox~0|3()o}bFkf#*S!+KtF|e|<+8=&Lw_ zY{2(9()HRzWTBcuPh+@pH1Qajxs9jP*!4(=nT1MBv#Xme?= z0HUAo+S~nH`M=*4K+?I`fQY2+6T9?$=EMe}kn=96V9&{q<_RE*(7eVio2|I;ngHg) z2;^J{p1onyr)va=V-?kvI>xB ziA(`Pt`m5TV3*1ol~I@lnVvw@WM>&3j-t5y-23d{QJwETT%rnTuzR}LfS3yo03E>D zBO1VQOFG1abi}v-Z=B^-ibogMZ#Q*(UuiZp7~aFfw=*DDU1mNc+!P*;qR?!AxA(#Z zLfq=44TQ5c15Y)Tt`%5Gf^r)W?cN3|#+$*kff)CWYy*`(a8l5sNC%HvL>ins*6}N*h)*jNdu|hoMGa?AtylvTMAF7Xfc#tNe6G7t6^C+h^y4 zUf3QYF-%*EXT@ZXzK1n=(8a=9)Q1>xELCv%smD3kOcQ zr2_`6Qqir3f~t$r7044Fp>R*|6-Qh5K5=)hyT#$=&Di#N@U)M6MlLT}=ZYxo&t+f= z1ifS$ytHqZtY*tDxrsyJecCajlDZbUo4WX(zh8dhz#~E^866w|tiT?i&BF0u0r=11 ztu#=7bval?qdLJ*41;e3$1I?h$^G;NclB%%yv;}&SY2SmV2CUn`9b2NQa)ivIp|Bc zdv~+}$)Gj+r{b@Bef6|-BJ>W^`{~8_C58zgspTf;4ek1K=Hm>&EPzL$F$Ez!{R~Hg zJZVADISa+6;mJW(_Tb#4u3#j=`%A5cq`Td)j{US~#}f zf{@AWpLI8+e5=g#5u_?&N_Ut9a!P0wjOvgFsxP!8KYI`)kCLQ`j$+UbIouohQHPP$ zPK}YFsQQp;fdjx52FbS|*&r{m&go;4S`CDLdnyd9 zWq;SPXXVF~;+6rH&#Ak7QHoLQVNLgfS9dY2*nXIS{$nj#I25*1y{KkC(1e`If zx@O&;l@b1@01}jC19A(YWqx{x5tkM+1qjB@V5{;IeWf11HHFxU>PKa9)+W#_O~5I_ z`z!uO`vR)G8+9mcT==zdyoB)!%A!56;iJV9EAyU?fV|DHzs>sHA+6M&lKHm(!RSe+ zAKN6|4CzDvbfxZ(elB58@=gKd@vVJb1EXinKPP|>Pj6TrBfe`A@vF8@-|D?8fVc;C)@uApVo4JLBr)2KTF0-%?4GTFdl%mkHKk6dc^Mmf zE^d!)Z~#>`EPb=G)liH$D_V zGGb=l^X7_It~@D#%$3d+0{>U=!w3Q7_~owqF8MuoeXIcTj#urzX%jQvxGX?$68+Pa z8ee$pm-@?D_rMNDxV>mLAg+A_pn93q7=9|`5G$Nn_65Qew~J@r$sbdHUszeH_suZX z?>n-fW#(hXo&pU75?vQk#w;sK)xOjX?Y=ExF@Vh!QCRZpiif&~UlY#qkVeVr1D!?& zzafAKu##hGoj!h#=L)it@bQv;KX-n3?Me161X7iMw%KZ(nr*XxLKh#rk zBqII!-G}}?Iz{{IGsjSDytLubUB$euAS>T#Zs4g8p7?I_o%9z1vXVA*K0I_rMK$_F z)^G6LJN&y*s!7Q%w=#2K(+9h%VJVOLEWaH$_<@&tNF8x3dLb2mc5rUF(Q5<{&(@-) z{#$G0wG%*ca?S6(?x~_BKM4@El>X^To%`vS*+m2E3n0RN`0(l~kZ8?^zQ5rle_;BgM+~rU`aMM@ zvsKi|flWzhbk{mF-x5}G&$jsAJ7X`V9v48CZ*i5UE-v{>#i^uO+ga}&eactkCRTV(0k=5a`oX<9n{Q4KK%~w3 znM;5FWXB=_#Q(OoegBF7DS+JZu9sYr z7Vy1pq2foh0lAl90G-T9w9E4hrb)nEfke*(rgt9Nwv$%sp5-rfhWVgvD?CDj`+SK z6f)5^ys+$x!4o3{kU6^{txf!0wLcd?7R>tZZ+80Y-9rM1nz8QMLo2`ho+Tmbof`U7^g9E- z6hM~P*d~uPYdJ@4wOE#uuGCQ#R$R%=R_QMbCVj~>&EHGVoig@M;_6Cu0I+Mrc^y1^ z42KwyZyruTB?H`(f2|GwWomzJ3qF^SH#~b-1JJqQ?I_Ne2iC0N*{$ZKnuTBJ*ofn? z{zqTj>hp&xhVrVJ4aiesWq@>3hYd({ya7kBJud3_EpD;txLeCkzqCOtxAJ?-ua5uA!h#U$oX&n{O)!Jz#j%Lr4$T-Ge=0? zZLSbL^pN2(x}?hK*P8K!#}4?h&IT@z1L^C2bX6DT^5RZ*-Xp&@I-$me+zoAabH%96 zy}l8O2=^>-0AOQ+i4D$BI=6%O#zvWzI7&fFM2)Zk397RJe_jf%R0H6oO^Ah`>OSz} z^CXxgwygxfa1-`Mf}!BSeVd-^*7IQs9Moe!_j;K?Rl_sq%4 zlthp_eMj#9>F;B*c$)u46uDgyK{fmQ770RgEaPs{sZy*p;&UZMHP%1G!ge zx0{!5Joyg~5V88(P?Q1g_X32u9sM?iq(pE4um?N>?pl0a4pu50dgE<9h_3a*-A^_L zZYWN5in2?o2>(ia1b&h+!*yZ+{P^V%7nIZ~q!4j0v0ss_ghO7CT`z{-(ep1Vh=8ab z2M5nzaOq9{S~L=wrb$~0n;-f8^WTLcprHXa^;rMUxoTesAcoG0_m9k3yrz^2_@1Fy zzGz<5y(R-UResq3ONZY|i+H4_u#z`57kBEv{PWpnX5v0e52_8MBjC3A5SC>a%n2LV zJoaB+8r0vXy#SJ3!luv%XJ2o6?D!~^=;9BUZUKQj+~AX+65M*wjb+?=2mt8@d9LY> z23*!C$|!Y7!%+|hKlqes<=CSAixQ4sV-5g__G1I0LXQ0L(N%fRKPMD&nb`*9J!J;~ zUfwovfusn+QqxC_fX3ofaHS4B^LLlY4b}_S$X~nwqMO(h5(Z=g!5?{H73ZE$Hic{q z4gg)ZaL*S)(joe(OLium&%p^*>HOpl0_d4auyu$s zpa#i+8dL_?gjfLYect%;i8OXjBOr?Yz7Lzt`uR{Fp%CG)ui~<+Bfe=XKn$-)@bw>P zJM~WEoJLk5jZKrbZ+rRa_Ad<)3JJ+vzWuA5&XcMs5M?NqC2ooAcwOa;SbK}ZlY6z= z`Mglb%LTJOKQ?ao9u}Qgd3kOi+#0&_ashi(Sib{H>+%$(v0{@E zOoP$@2%v{@A*KkEiNRLg3Z6wtH=G^ipe;T#Jpr)Ocg`vTi9lO@mqL7lPF(ahi(nok zxI^}K@#N6_>Czn$v^F3SbvEF4+_c69s<708gzBN4?z)n^S?cg(7lj2B_*8lr^ZCQV zXp6y$B;`6}K?H%EWtU;J)l_W*HGcI11Rd}Ki14#n`PTJ9AqG(yihf=w{APIpB+szT zO15YlD0jmPE3s4!1+LrVweS;;Q_uG{{8S%`|Is&(IN)N|AbLv75uF5Z481xNy-Esc zVvp!qNblm=iS+u|Mdj^?R72csGl&}x3jRk_q7&c2pWDLO)+d5io4y_Ld80>IygEN5 zt<%BI^@bd}aDs|qx8#=l{z=*2+4K>)=7~<9&sv}yZX#Ow+r`UH+`NQps1rSqeZ(f(A1KIrrkR5pUl!Gs> z>C#sKIp)~D@tt$aZ#^u4Opt9q+;zR>-i`u@S7gn!$0}TI{ip!qt65Y1{Xs$QS5Khl z5-Yu`*@Hem9_=m^;tyKds_~|Nek}xuY(oEZrOprNaM!ify#){hfA*eHewYgo1YG4sd$Cmxi31oP*I*P|}Z zdZKGftvtjnvs;Ynv-tfmC=7iIV;6-hH97LlSu=g^ zmU`tyEE@>mGG!@(7nm+MC_qJP4N6FM0Vp>R8SYj#9c8c`>|7D9xUOr)<4M=M9;XJPESr+kSNyIe&k+il(j_yiUVEfqsQ|kC>-z2aIZu^RS>?Ou6u)!N+AowM zh5qSEeg3}Fq5tHo9wQ5F^S!UWmw&E~P{=}C|G&!-cduhfianRrb8>aXaFv2?A8uVw(H-r9EF>UlAmLeA2Yr z_4d!dT2TOT)wUe-#=Dir7-$K&|0<>- zHV~BH1fHTlaSGXi$MW3|4VV?b{nI<8Qkf|w6K@9ee0j0}@)b++I@MkE$VdTXLKJ^n zHSL|#Hw6%pT>QqnYoEBz^PWD|3seT4TwHtC%Kv8SW+a!&_Ky4V%eh-N3M)Cp-qZd5 zX_NO&5kMy0o`@}#R{hXgfM9MQxLq38n0##FQ~_iPZ7%oejPP&yn_{bExR*sV`QrB5 z5kes|W!9u`kJVgLT>!b|J^ub{$6|hOFMzDWyquQhE;L;tK-@I?rz`cLw?4hF(I}%> zxhOuZ#{OJ*;ZqFkk?6ff3RfF)ENr}kPDl;K6!S+9}{>_Kvupin_Q{eLJp)K z>N-Ox68<%HZ2h!Zqr@8X7%1?G@03?C1h)ss%tmVslJCRSPPsN>Ab6k7YcIn%=#nh~bLdueQ4O)K#bd= z)b32flZ%R3g3!3OH54`=5de2zIN4%uh6-WvHrjx1zF}npVLXRisSW_Fv(D-8RwdqH zOX@Zx58^mkU|HqhWFrVU4zhCcj*&msB?`zgVMN3IW;aO5?eKjPSY zA#u;h4R?Mi6k^~Xj6Hdyb0bxg#5O+g=Get=RBR^{zWq$)#k;1zvEWE80VJemSNN^$ z_exkdz*behVkn-EuaKPKV%G|NmTUv;=-Q)&-y6Gym5lzjpM2J@?Ab*C36R;=H}B?> z*S$O-{al@>^jgO5N5UFMZSU-1`E^h3d7VU#r$V?-2n+ce4S}_Et`9J@Jix zJX@*q6+{eAE+$ktcCoc;t+Izd`&ihJ+EZ&v9ir_$^Xr#enhbtR0NKN5-1bk5Uvo?V z(e|=euAX=2^eh2HEl*ofwL$zu9o&+a^v zu!G`gw*k?R41jYdbO!!l2J)KA;HMn72&a;*3y9DBp7az21mV zII$rT?RDU;7ylVBxtdUj_jCWhp*7Agyh{L`V_knuS=heRtYpO>j^*-y{-%FpRVyTD zbLZW!ydN<0NnsVi`YIR>Z#_}tt4*^6kkf)qZ><{e?Uc3(+@gQFQa2o5`@de1c^d;|Z>z=2!kVKtL{l3NL zS3bK(suTG#;#{-fx;I7$AaUrO|Ngzou!6k`xFxF%h?~L&e4~jN64h9&WT&$MA2!y? zQY0+$!b((Mo0W)M8xXzQ2E@EK0M6W+XQ*DBs`dsiVLV6I$2D)1pCJP!8{>fsogRDU zEf$XSC0m(J?5gx)@R^a*nF9O=t>IWIvw#l0@p?66mxfG&?)k9g-Gn<*V%W8bTq-p& zTsGW%rrq)G$E99*v}Z%%>5e5Ube77_FmuSE2Yp+8FBD>FlmW(@I;PIKqt~hlzX^pT zVtx9lQ$GhD3>83LLKw61fzWlO^8bl8kazU4k=rk8M3tn6Ed#iBY8$A@N|Ysxl$cPq08d2bO3m7D-W)g!TYttIGdA~wuv}(kON;4rv@OM z;Cle7ora&Vj_^|p50q>0aNvruBSod(M?X5Tf99k+WC%sL@0ow!rnBethJ=Xft^V70 z?JuYLpQr=RRA^Vb)-c}J@QcfoUiP8undEC~xr;hr1LA>Ly759_;-lIsG3l2KeXgL& z+v<2039`6x|LgJX)ZCV2ZVP{Xcyrw`Tcm@cC(PP-^TC)0e-J>t1LIFVH0;N(Sj1vK zL>+kQT1M?_CoTwuI8$vvE>L8SA8A!jF6HwM3naei0Wk5A)?*}WR@F1?K@jjaZNOpUTE{s zst3Ae(_dgWS4@Kh1CX z`QL}NY5p4b6{U>?#ucv)*mKYDyNLq=mD%tFm3U{R)!#q=hX8VwXmj%|QwE<BOeR_g4G5bEHs&?(zb}v3mj%+eiNUzrqx4l{hO3dn|1K;gL6mmFyQbAW0N#Ak@9z zk4S8qd^z^Au#yFs^+CzM+pnJyK&FTd1m17@(fb;XqHb1V+S!0CCmWEHCL0Lic0isc zs}BD?UMd%}%chXIV*{1jd0{2nn@u6X05%{lQ5%reZveVvSD_zOj1yE3mnjc*g$TGs zTAsoJ=&hj~xTX!4lv$s{@^&iAniQ#0llb9u73Jr|vNaXcs^~nHL*ye1ie7*JN}f`% zP66?477*!Hvv&`C>*GfUgbIaZE8q5O+YztMO%p)m)A}zKzv&mHGO*5HRK}HR19F1B zzV9^GFL(4}RuHJvbtuGmw|WXL+QT~@ZL_jQV9uy6rE+Yo8F6sa{HO<|?jZNPSoZRj zlWTvhDu84+Ub^FW`^ojhdaTwhJ3Z-<^qh3e1qCL@D7f}d&gs#R0f*g!knXND4Ggb1 z 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 Ee;AA@asU7T literal 136263 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

W5QUv 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 z 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 literal 114282 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 From ea8fb23e9b08f527b5650d55e9f0e651eca6dc84 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Thu, 28 Nov 2024 21:01:19 +0000 Subject: [PATCH 133/152] Update lunar_insight.txt --- forge-gui/res/cardsfolder/l/lunar_insight.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/l/lunar_insight.txt b/forge-gui/res/cardsfolder/l/lunar_insight.txt index 69a84871b16..08a465cb8a0 100644 --- a/forge-gui/res/cardsfolder/l/lunar_insight.txt +++ b/forge-gui/res/cardsfolder/l/lunar_insight.txt @@ -1,6 +1,6 @@ Name:Lunar Insight ManaCost:2 U -Types:Instant +Types:Sorcery A:SP$ Draw | NumCards$ X | SpellDescription$ Draw a card for each different mana value among nonland permanents you control. SVar:X:Count$Valid Card.YouCtrl+nonLand$DifferentCMC Oracle:Draw a card for each different mana value among nonland permanents you control. From 5b183f5a06e355ab9d0a2caf6481665fab88b17b Mon Sep 17 00:00:00 2001 From: kevlahnota Date: Fri, 29 Nov 2024 05:55:28 +0800 Subject: [PATCH 134/152] update check for internet - closes #6640 --- forge-gui-android/src/forge/app/Main.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-gui-android/src/forge/app/Main.java b/forge-gui-android/src/forge/app/Main.java index 242625adc0e..eb0df5c4e87 100644 --- a/forge-gui-android/src/forge/app/Main.java +++ b/forge-gui-android/src/forge/app/Main.java @@ -595,7 +595,7 @@ public class Main extends AndroidApplication { if (connManager != null) { NetworkCapabilities capabilities = connManager.getNetworkCapabilities(connManager.getActiveNetwork()); if (capabilities != null) { - if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { result = connected; } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { result = connected && !wifiOnly; @@ -607,7 +607,7 @@ public class Main extends AndroidApplication { NetworkInfo activeNetwork = connManager.getActiveNetworkInfo(); if (activeNetwork != null) { // connected to the internet - if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) { + if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI || activeNetwork.getType() == ConnectivityManager.TYPE_ETHERNET) { result = true; } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) { result = !wifiOnly; From 115276fa5600dccced56252026f02e180ed6968b Mon Sep 17 00:00:00 2001 From: Ayora29 Date: Fri, 29 Nov 2024 10:15:11 +0100 Subject: [PATCH 135/152] Update draftable edition sections (#6631) * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix art index when parsing edition files sections Add "scheme" section Add fallback sheet in BoosterGenerator Rewrite FileSection and Use LinkedHashMap for sections map * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Fix edition txt files * Convert MKM to BoosterSlots * Rename "RareMythic" section * Remove conjured cards that do not have unique artwork. * Restore basic land order * Fixes * parseSections : Fix Index out of bounds error for empty file * FileSection.java : use for loop instead of do/while --------- Co-authored-by: Agetian --- .../src/main/java/forge/card/CardEdition.java | 27 +- .../item/generation/BoosterGenerator.java | 19 +- .../src/main/java/forge/util/FileSection.java | 221 ++----- .../java/forge/util/FileSectionManual.java | 2 +- forge-gui/res/blockdata/printsheets.txt | 587 ------------------ .../res/editions/30th Anniversary Edition.txt | 309 +-------- .../Alchemy Horizons Baldur's Gate.txt | 31 +- forge-gui/res/editions/Amonkhet.txt | 6 +- .../res/editions/Battle for Zendikar.txt | 4 +- forge-gui/res/editions/Battlebond.txt | 74 ++- forge-gui/res/editions/Bloomburrow.txt | 30 +- ...ander Legends Battle for Baldur's Gate.txt | 42 +- forge-gui/res/editions/Commander Legends.txt | 2 +- .../editions/Conspiracy Take the Crown.txt | 4 +- .../res/editions/Dominaria Remastered.txt | 146 +---- forge-gui/res/editions/Dominaria United.txt | 10 +- forge-gui/res/editions/Dominaria.txt | 6 +- .../res/editions/Double Masters 2022.txt | 2 +- forge-gui/res/editions/Double Masters.txt | 6 +- forge-gui/res/editions/Guilds of Ravnica.txt | 18 +- .../res/editions/Hour of Devastation.txt | 6 +- .../res/editions/Innistrad Crimson Vow.txt | 2 + .../res/editions/Innistrad Midnight Hunt.txt | 16 +- .../res/editions/Kamigawa Neon Dynasty.txt | 2 + forge-gui/res/editions/Khans of Tarkir.txt | 2 +- forge-gui/res/editions/Magic 2015.txt | 4 +- forge-gui/res/editions/Magic 2021.txt | 12 +- forge-gui/res/editions/Magic Origins.txt | 4 +- .../March of the Machine The Aftermath.txt | 12 + .../Masterpiece Series - Amonkhet.txt | 4 + forge-gui/res/editions/Masters Edition IV.txt | 16 +- forge-gui/res/editions/Modern Horizons 2.txt | 8 +- forge-gui/res/editions/Modern Horizons 3.txt | 39 +- forge-gui/res/editions/Modern Horizons.txt | 4 +- .../res/editions/Murders at Karlov Manor.txt | 35 +- .../res/editions/Oath of the Gatewatch.txt | 6 +- .../editions/Outlaws of Thunder Junction.txt | 30 +- .../res/editions/Phyrexia All Will Be One.txt | 14 +- forge-gui/res/editions/Portal.txt | 52 +- forge-gui/res/editions/Ravnica Allegiance.txt | 13 +- forge-gui/res/editions/Ravnica Remastered.txt | 2 +- .../res/editions/Streets of New Capenna.txt | 2 + forge-gui/res/editions/The Big Score.txt | 4 + forge-gui/res/editions/The Brothers War.txt | 10 +- ...ord of the Rings Tales of Middle-earth.txt | 20 +- .../res/editions/Time Spiral Remastered.txt | 4 +- forge-gui/res/editions/Unglued.txt | 2 +- forge-gui/res/editions/Unhinged.txt | 4 +- forge-gui/res/editions/Unstable.txt | 528 ++++++++-------- forge-gui/res/editions/War of the Spark.txt | 80 +-- .../res/editions/Zendikar Expeditions.txt | 4 + forge-gui/res/editions/Zendikar Rising.txt | 2 + 52 files changed, 774 insertions(+), 1715 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardEdition.java b/forge-core/src/main/java/forge/card/CardEdition.java index 96c6caf33dc..ea945205f75 100644 --- a/forge-core/src/main/java/forge/card/CardEdition.java +++ b/forge-core/src/main/java/forge/card/CardEdition.java @@ -54,24 +54,19 @@ public final class CardEdition implements Comparable { // immutable public enum Type { UNKNOWN, - CORE, EXPANSION, STARTER, REPRINT, BOXED_SET, - COLLECTOR_EDITION, DUEL_DECK, PROMO, ONLINE, - DRAFT, - COMMANDER, MULTIPLAYER, FUNNY, - OTHER, // FALLBACK CATEGORY CUSTOM_SET; // custom sets @@ -131,22 +126,23 @@ public final class CardEdition implements Comparable { SPECIAL_SLOT("special slot"), //to help with convoluted boosters PRECON_PRODUCT("precon product"), BORDERLESS("borderless"), - BORDERLESS_PROFILE("borderless profile"), - BORDERLESS_FRAME("borderless frame"), ETCHED("etched"), SHOWCASE("showcase"), FULL_ART("full art"), EXTENDED_ART("extended art"), ALTERNATE_ART("alternate art"), - ALTERNATE_FRAME("alternate frame"), + RETRO_FRAME("retro frame"), BUY_A_BOX("buy a box"), PROMO("promo"), + PRERELEASE_PROMO("prerelease promo"), BUNDLE("bundle"), BOX_TOPPER("box topper"), DUNGEONS("dungeons"), JUMPSTART("jumpstart"), REBALANCED("rebalanced"), - ETERNAL("eternal"); + ETERNAL("eternal"), + CONJURED("conjured"), + SCHEME("scheme"); private final String name; @@ -215,7 +211,7 @@ public final class CardEdition implements Comparable { */ public static String getSortableCollectorNumber(final String collectorNumber){ String inputCollNumber = collectorNumber; - if (collectorNumber == null || collectorNumber.length() == 0) + if (collectorNumber == null || collectorNumber.isEmpty()) inputCollNumber = "50000"; // very big number of 5 digits to have them in last positions String matchedCollNr = sortableCollNumberLookup.getOrDefault(inputCollNumber, null); @@ -379,7 +375,6 @@ public final class CardEdition implements Comparable { public String getSlotReplaceCommonWith() { return slotReplaceCommonWith; } public String getAdditionalSheetForFoils() { return additionalSheetForFoils; } public String getAdditionalUnlockSet() { return additionalUnlockSet; } - public boolean getSmallSetOverride() { return smallSetOverride; } public String getDoublePickDuringDraft() { return doublePickDuringDraft; } public String getBoosterMustContain() { return boosterMustContain; } public String getBoosterReplaceSlotFromPrintSheet() { return boosterReplaceSlotFromPrintSheet; } @@ -514,7 +509,7 @@ public final class CardEdition implements Comparable { for (CardInSet card : cards) { int index = 1; if (cardToIndex.containsKey(card.name)) { - index = cardToIndex.get(card.name); + index = cardToIndex.get(card.name) + 1; } cardToIndex.put(card.name, index); @@ -709,9 +704,6 @@ public final class CardEdition implements Comparable { res.fatPackExtraSlots = metadata.get("FatPackExtraSlots", ""); switch (metadata.get("foil", "newstyle").toLowerCase()) { - case "notsupported": - res.foilType = FoilType.NOT_SUPPORTED; - break; case "oldstyle": case "classic": res.foilType = FoilType.OLD_STYLE; @@ -720,6 +712,7 @@ public final class CardEdition implements Comparable { case "modern": res.foilType = FoilType.MODERN; break; + case "notsupported": default: res.foilType = FoilType.NOT_SUPPORTED; break; @@ -774,7 +767,7 @@ public final class CardEdition implements Comparable { public void add(CardEdition item) { //Even though we want it to be read only, make an exception for custom content. if(lock) throw new UnsupportedOperationException("This is a read-only storage"); else map.put(item.getName(), item); - }; + } public void append(CardEdition.Collection C){ //Append custom editions if (lock) throw new UnsupportedOperationException("This is a read-only storage"); for(CardEdition E : C){ //Update the alias list as above or else it'll fail to look up. @@ -908,7 +901,7 @@ public final class CardEdition implements Comparable { StaticData.instance().getEditions().getOrderedEditions(), com.google.common.base.Predicates.and(hasBasicLands, artPreference::accept)); Iterator editionsIterator = editionsWithBasicLands.iterator(); - List selectedEditions = new ArrayList(); + List selectedEditions = new ArrayList<>(); while (editionsIterator.hasNext()) selectedEditions.add(editionsIterator.next()); if (selectedEditions.isEmpty()) diff --git a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java index 812dd7ff779..ec1ee50aba4 100644 --- a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java +++ b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java @@ -69,7 +69,6 @@ public class BoosterGenerator { } List result = new ArrayList<>(); - List sheetsUsed = new ArrayList<>(); CardEdition edition = StaticData.instance().getEditions().get(template.getEdition()); @@ -233,7 +232,6 @@ public class BoosterGenerator { if (sheetKey.startsWith("wholeSheet")) { PrintSheet ps = getPrintSheet(sheetKey); result.addAll(ps.all()); - sheetsUsed.add(ps); continue; } @@ -252,7 +250,7 @@ public class BoosterGenerator { if ((edition.getName().equals("Planeshift")) && (slotType.startsWith(BoosterSlots.RARE)) && (foilSlot.startsWith(BoosterSlots.SPECIAL)) - ) { + ) { numCards--; } } @@ -264,7 +262,6 @@ public class BoosterGenerator { : edition.getSlotReplaceCommonWith().trim(); PrintSheet replaceSheet = getPrintSheet(replaceKey); result.addAll(replaceSheet.random(1, true)); - sheetsUsed.add(replaceSheet); System.out.println("Common was replaced with something from the replace sheet..."); replaceCommon = false; } @@ -283,7 +280,6 @@ public class BoosterGenerator { } result.addAll(paperCards); - sheetsUsed.add(ps); if (foilInThisSlot) { if (!foilAtEndOfPack) { @@ -395,8 +391,6 @@ public class BoosterGenerator { public static List getBoosterPack(SealedTemplateWithSlots template) { // SealedTemplateWithSlots ignores all Edition level params // Instead each slot defines their percentages on their own - - CardEdition edition = StaticData.instance().getEditions().get(template.getEdition()); List result = new ArrayList<>(); Map boosterSlots = template.getNamedSlots(); @@ -501,7 +495,7 @@ public class BoosterGenerator { * Replaces an already present card in the booster with a card from the supplied print sheet. * Nothing is replaced if there is no matching rarity found. * @param booster in which a card gets replaced - * @param printSheetKey + * @param printSheetKey print sheet key from which take the replacement card */ public static void replaceCardFromExtraSheet(List booster, String printSheetKey) { PrintSheet replacementSheet = StaticData.instance().getPrintSheets().get(printSheetKey); @@ -516,7 +510,7 @@ public class BoosterGenerator { * @param toAdd new card which replaces a card in the booster */ public static void replaceCard(List booster, PaperCard toAdd) { - Predicate rarityPredicate = null; + Predicate rarityPredicate; switch (toAdd.getRarity()) { case BasicLand: rarityPredicate = Presets.IS_BASIC_LAND; @@ -573,11 +567,10 @@ public class BoosterGenerator { } } - @SuppressWarnings("unchecked") public static PrintSheet makeSheet(String sheetKey, Iterable src) { PrintSheet ps = new PrintSheet(sheetKey); String[] sKey = TextUtil.splitWithParenthesis(sheetKey, ' ', 2); - Predicate setPred = (Predicate) (sKey.length > 1 ? IPaperCard.Predicates.printedInSets(sKey[1].split(" ")) : Predicates.alwaysTrue()); + Predicate setPred = (sKey.length > 1 ? IPaperCard.Predicates.printedInSets(sKey[1].split(" ")) : Predicates.alwaysTrue()); List operators = new LinkedList<>(Arrays.asList(TextUtil.splitWithParenthesis(sKey[0], ':'))); Predicate extraPred = buildExtraPredicate(operators); @@ -679,11 +672,11 @@ public class BoosterGenerator { Predicate toAdd = null; if (operator.equalsIgnoreCase(BoosterSlots.DUAL_FACED_CARD)) { toAdd = Predicates.compose( - Predicates.or( + Predicates.or( CardRulesPredicates.splitType(CardSplitType.Transform), CardRulesPredicates.splitType(CardSplitType.Meld), CardRulesPredicates.splitType(CardSplitType.Modal) - ), + ), PaperCard::getRules); } else if (operator.equalsIgnoreCase(BoosterSlots.LAND)) { toAdd = Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard::getRules); } else if (operator.equalsIgnoreCase(BoosterSlots.BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND; diff --git a/forge-core/src/main/java/forge/util/FileSection.java b/forge-core/src/main/java/forge/util/FileSection.java index e4db0e9dd5d..d7ff8299693 100644 --- a/forge-core/src/main/java/forge/util/FileSection.java +++ b/forge-core/src/main/java/forge/util/FileSection.java @@ -19,12 +19,7 @@ package forge.util; import java.text.NumberFormat; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; @@ -33,73 +28,60 @@ import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; /** - * TODO: Write javadoc for this type. - * + * Parse text file to extract [sections] and key/value data. + * Store the result in a HashMap */ public class FileSection { /** The lines. */ - private final Map lines; - - /** - * Gets the lines. - * - * @return the lines - */ - protected final Map getLines() { - return this.lines; - } + protected final Map lines; /** * Instantiates a new file section. */ protected FileSection() { - this(new TreeMap<>(String.CASE_INSENSITIVE_ORDER)); - } - - protected FileSection(Map lines0) { - lines = lines0; + lines = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } public static final Pattern DOLLAR_SIGN_KV_SEPARATOR = Pattern.compile(Pattern.quote("$")); public static final Pattern ARROW_KV_SEPARATOR = Pattern.compile(Pattern.quote("->")); public static final Pattern EQUALS_KV_SEPARATOR = Pattern.compile(Pattern.quote("=")); public static final Pattern COLON_KV_SEPARATOR = Pattern.compile(Pattern.quote(":")); - private static final String BAR_PAIR_SPLITTER = Pattern.quote("|"); - private static Table> parseToMapCache = HashBasedTable.create(); + private static final Table> parseToMapCache = HashBasedTable.create(); + /** + * Parses the key=value text line and return a HashMap + * + * @param line the text line to parse + * @param kvSeparator the key/value separator + * @return a HashMap + */ public static Map parseToMap(final String line, final Pattern kvSeparator) { - Map result = parseToMapCache.get(line, kvSeparator); - if (result != null) { - return result; - } - result = parseToMapImpl(line, kvSeparator); - parseToMapCache.put(line, kvSeparator, result); - return result; - } - - private static Map parseToMapImpl(final String line, final Pattern kvSeparator) { - if (StringUtils.isEmpty(line)) { - return Collections.emptyMap(); + Map cached = parseToMapCache.get(line, kvSeparator); + if (cached != null) { + return cached; } - final Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - final String[] pairs = line.split(BAR_PAIR_SPLITTER); - for (final String dd : pairs) { - final String[] v = kvSeparator.split(dd, 2); - result.put(v[0].trim(), v.length > 1 ? v[1].trim() : ""); + Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + if (!StringUtils.isEmpty(line)) { + for (final String dd : line.split(BAR_PAIR_SPLITTER)) { + final String[] v = kvSeparator.split(dd, 2); + result.put(v[0].trim(), v.length > 1 ? v[1].trim() : ""); + } } - return Collections.unmodifiableMap(result); + cached = Collections.unmodifiableMap(result); + parseToMapCache.put(line, kvSeparator, cached); + return cached; } /** - * Parses the. + * Parses the key=value text lines and return a HashMap * - * @param lines the lines - * @param kvSeparator the kv separator - * @return the file section + * @param lines the text lines to parse + * @param kvSeparator the key/value separator + * @return a FileSection Object containing the HashMap */ public static FileSection parse(final Iterable lines, final Pattern kvSeparator) { final FileSection result = new FileSection(); @@ -112,11 +94,34 @@ public class FileSection { } /** - * Gets the. + * Parses the sections ([sectionName]) from a list of text line * - * @param fieldName the field name - * @return the string + * @param lines + * the text lines to parse + * @return a LinkedHashMap containing the sections and text line associated. The order of the sections is preserved */ + public static Map> parseSections(final List lines) { + final Map> result = new LinkedHashMap<>(); + String section = null; + + if(null != lines && !lines.isEmpty()){ + for(String l : lines) { + String line = l.trim(); + if (line.startsWith("#")) continue; + if (line.startsWith("[") && line.endsWith("]")) { + section = line.substring(1, line.length() - 1); + if (!result.containsKey(section)) { + result.put(section, new ArrayList<>()); + } + } else if (null != section && !line.isEmpty()) { + result.get(section).add(line); + } + } + } + + return result; + } + public String get(final String fieldName) { return this.lines.get(fieldName); } @@ -125,59 +130,27 @@ public class FileSection { return lines.containsKey(fieldName) ? this.lines.get(fieldName) : defaultValue; } - public boolean contains(String keyName) { - return lines.containsKey(keyName); + public boolean contains(String fieldName) { + return lines.containsKey(fieldName); } - /** - * Gets the double. - * - * @param fieldName the field name - * @return the int - */ - public double getDouble(final String fieldName) { - return this.getDouble(fieldName, 0.0F); - } - - /** - * Gets the double. - * - * @param fieldName the field name - * @param defaultValue the default value - * @return the int - */ public double getDouble(final String fieldName, final double defaultValue) { + final String field = this.get(fieldName); + if (null == field) return defaultValue; try { - if (this.get(fieldName) == null) { - return defaultValue; - } - NumberFormat format = NumberFormat.getInstance(Locale.US); - Number number = format.parse(this.get(fieldName)); - + Number number = format.parse(field); return number.doubleValue(); } catch (final NumberFormatException | ParseException ex) { return defaultValue; } } - /** - * Gets the int. - * - * @param fieldName the field name - * @return the int - */ public int getInt(final String fieldName) { return this.getInt(fieldName, 0); } - /** - * Gets the int. - * - * @param fieldName the field name - * @param defaultValue the default value - * @return the int - */ + public int getInt(final String fieldName, final int defaultValue) { try { return Integer.parseInt(this.get(fieldName)); @@ -186,79 +159,13 @@ public class FileSection { } } - /** - * Gets the boolean. - * - * @param fieldName the field name - * @return the boolean - */ public boolean getBoolean(final String fieldName) { return this.getBoolean(fieldName, false); } - /** - * Gets the boolean. - * - * @param fieldName the field name - * @param defaultValue the default value - * @return the boolean - */ public boolean getBoolean(final String fieldName, final boolean defaultValue) { - final String s = this.get(fieldName); - if (s == null) { - return defaultValue; - } - return "true".equalsIgnoreCase(s); + final String field = this.get(fieldName); + if (field == null) return defaultValue; + return "true".equalsIgnoreCase(field); } - - /** - * Parses the sections. - * - * @param source - * the source - * @return the map - */ - @SuppressWarnings("unchecked") - public static Map> parseSections(final List source) { - final Map> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - String currentSection = ""; - List currentList = null; - - for (final String s : source) { - final String st = s.trim(); - if (st.length() == 0) { - continue; - } - if (st.startsWith("[") && st.endsWith("]")) { - if ((currentList != null) && (currentList.size() > 0)) { - final Object oldVal = result.get(currentSection); - if ((oldVal != null) && (oldVal instanceof List)) { - currentList.addAll((List) oldVal); - } - result.put(currentSection, currentList); - } - - final String newSection = st.substring(1, st.length() - 1); - currentSection = newSection; - currentList = null; - } else { - if (currentList == null) { - currentList = new ArrayList<>(); - } - currentList.add(st); - } - } - - // save final block - if ((currentList != null) && (currentList.size() > 0)) { - final Object oldVal = result.get(currentSection); - if ((oldVal != null) && (oldVal instanceof List)) { - currentList.addAll((List) oldVal); - } - result.put(currentSection, currentList); - } - - return result; - } - } diff --git a/forge-core/src/main/java/forge/util/FileSectionManual.java b/forge-core/src/main/java/forge/util/FileSectionManual.java index 5a7a6c58dfe..999e82f42c1 100644 --- a/forge-core/src/main/java/forge/util/FileSectionManual.java +++ b/forge-core/src/main/java/forge/util/FileSectionManual.java @@ -30,7 +30,7 @@ public class FileSectionManual extends FileSection { * @param value the value */ public void put(final String key, final String value) { - this.getLines().put(key, value); + this.lines.put(key, value); } } diff --git a/forge-gui/res/blockdata/printsheets.txt b/forge-gui/res/blockdata/printsheets.txt index 8d892d27cbb..be76601b822 100644 --- a/forge-gui/res/blockdata/printsheets.txt +++ b/forge-gui/res/blockdata/printsheets.txt @@ -635,41 +635,6 @@ 1 Wayfaring Temple|RTR 1 Wild Beastmaster|RTR -[M15 Sample Cards] -1 Aegis Angel|M15 -1 Cancel|M15 -1 Centaur Courser|M15 -1 Divine Verdict|M15 -1 Furnace Whelp|M15 -1 Garruk's Packleader|M15 -1 Inspired Charge|M15 -1 Mahamoti Djinn|M15 -1 Nightmare|M15 -1 Seismic Strike|M15 -1 Sengir Vampire|M15 -1 Serra Angel|M15 -1 Shivan Dragon|M15 -1 Terra Stomper|M15 -1 Walking Corpse|M15 - -[ORI Sample Cards] -1 Aegis Angel|ORI -1 Divine Verdict|ORI -1 Eagle of the Watch|ORI -1 Serra Angel|ORI -1 Into the Void|ORI -1 Mahamoti Djinn|ORI -1 Weave Fate|ORI -1 Flesh to Dust|ORI -1 Mind Rot|ORI -1 Nightmare|ORI -1 Sengir Vampire|ORI -1 Fiery Hellhound|ORI -1 Shivan Dragon|ORI -1 Plummet|ORI -1 Prized Unicorn|ORI -1 Terra Stomper|ORI - [ORI Gideon] Akroan Jailer|ORI Ampryn Tactician|ORI @@ -889,558 +854,6 @@ Woodland Bellower|ORI Yeva's Forcemage|ORI Zendikar's Roil|ORI -[EXP Lands] -Prairie Stream|EXP -Sunken Hollow|EXP -Smoldering Marsh|EXP -Cinder Glade|EXP -Canopy Vista|EXP -Hallowed Fountain|EXP -Watery Grave|EXP -Blood Crypt|EXP -Stomping Ground|EXP -Temple Garden|EXP -Godless Shrine|EXP -Steam Vents|EXP -Overgrown Tomb|EXP -Sacred Foundry|EXP -Breeding Pool|EXP -Flooded Strand|EXP -Polluted Delta|EXP -Bloodstained Mire|EXP -Wooded Foothills|EXP -Windswept Heath|EXP -Marsh Flats|EXP -Scalding Tarn|EXP -Verdant Catacombs|EXP -Arid Mesa|EXP -Misty Rainforest|EXP - -[EXP Lands 2] -Mystic Gate|EXP -Sunken Ruins|EXP -Graven Cairns|EXP -Fire-Lit Thicket|EXP -Wooded Bastion|EXP -Fetid Heath|EXP -Cascade Bluffs|EXP -Twilight Mire|EXP -Rugged Prairie|EXP -Flooded Grove|EXP -Ancient Tomb|EXP -Dust Bowl|EXP -Eye of Ugin|EXP -Forbidden Orchard|EXP -Horizon Canopy|EXP -Kor Haven|EXP -Mana Confluence|EXP -Strip Mine|EXP -Tectonic Edge|EXP -Wasteland|EXP - -[MPS Amonkhet Invocations] -Austere Command|MPS_AKH -Aven Mindcensor|MPS_AKH -Containment Priest|MPS_AKH -Loyal Retainers|MPS_AKH -Oketra the True|MPS_AKH -Worship|MPS_AKH -Wrath of God|MPS_AKH -Consecrated Sphinx|MPS_AKH -Counterbalance|MPS_AKH -Counterspell|MPS_AKH -Cryptic Command|MPS_AKH -Daze|MPS_AKH -Divert|MPS_AKH -Force of Will|MPS_AKH -Kefnet the Mindful|MPS_AKH -Pact of Negation|MPS_AKH -Spell Pierce|MPS_AKH -Stifle|MPS_AKH -Attrition|MPS_AKH -Bontu the Glorified|MPS_AKH -Dark Ritual|MPS_AKH -Diabolic Intent|MPS_AKH -Entomb|MPS_AKH -Mind Twist|MPS_AKH -Aggravated Assault|MPS_AKH -Chain Lightning|MPS_AKH -Hazoret the Fervent|MPS_AKH -Rhonas the Indomitable|MPS_AKH -Maelstrom Pulse|MPS_AKH -Vindicate|MPS_AKH - -[MPS Hour of Devastation Invocations] -Armageddon|MPS_AKH -Capsize|MPS_AKH -Forbid|MPS_AKH -Omniscience|MPS_AKH -Opposition|MPS_AKH -Sunder|MPS_AKH -Threads of Disloyalty|MPS_AKH -Avatar of Woe|MPS_AKH -Damnation|MPS_AKH -Desolation Angel|MPS_AKH -Diabolic Edict|MPS_AKH -Doomsday|MPS_AKH -No Mercy|MPS_AKH -Slaughter Pact|MPS_AKH -Thoughtseize|MPS_AKH -Blood Moon|MPS_AKH -Boil|MPS_AKH -Shatterstorm|MPS_AKH -Through the Breach|MPS_AKH -Choke|MPS_AKH -The Locust God|MPS_AKH -Lord of Extinction|MPS_AKH -The Scarab God|MPS_AKH -The Scorpion God|MPS_AKH - -[AKH Planeswalker Decks and Toolkit] -Gideon, Martial Paragon -Companion of the Trials -Gideon's Resolve -Graceful Cat -Stone Quarry -Liliana, Death Wielder -Desiccated Naga -Liliana's Influence -Tattered Mummy -Foul Orchard -Cinder Barrens -Forsaken Sanctuary -Highland Lake -Meandering River -Submerged Boneyard -Timber Gorge -Tranquil Expanse -Woodland Stream - -[HOU Planeswalker Decks and Toolkit] -Nissa, Genesis Mage -Avid Reclaimer -Brambleweft Behemoth -Nissa's Encouragement -Woodland Stream -Nicol Bolas, the Deceiver -Wasp of the Bitter End -Zealot of the God-Pharaoh -Visage of Bolas -Cinder Barrens - -[DOM Planeswalker Decks and Additional Promo] -Teferi, Timebender -Temporal Machinations -Niambi, Faithful Healer -Teferi's Sentinel -Meandering River -Chandra, Bold Pyromancer -Chandra's Outburst -Karplusan Hound -Pyromantic Pilgrim -Timber Gorge -Firesong and Sunspeaker - -[BBD RareMythic] -1 Will Kenrith|BBD|1 -1 Rowan Kenrith|BBD|1 -8 Regna, the Redeemer -8 Krav, the Unredeemed -8 Zndrsplt, Eye of Wisdom -8 Okaun, Eye of Chaos -8 Virtus the Veiled -8 Gorm the Great -8 Khorvath Brightflame -8 Sylvia Brightspear -8 Pir, Imaginative Rascal -8 Toothy, Imaginary Friend -1 Arena Rector -1 Brightling -8 Play of the Game -8 Regna's Sanction -8 Together Forever -1 Arcane Artisan -8 Game Plan -8 Spellseeker -8 Zndrsplt's Judgment -1 Archfiend of Despair -8 Mindblade Render -1 Stunning Reversal -8 Thrilling Encore -8 Virtus's Maneuver -8 Bonus Round -8 Khorvath's Fury -1 Najeela, the Blade-Blossom -8 Stolen Strategy -1 Bramble Sovereign -8 Generous Patron -1 Grothama, All-Devouring -8 Pir's Whim -8 Archon of Valor's Reach -8 Last One Standing -8 Sentinel Tower -8 Victory Chimes -8 Bountiful Promenade -8 Luxury Suite -8 Morphic Pool -8 Sea of Clouds -8 Spire Garden -8 Angelic Chorus -8 Kor Spiritdancer -1 Land Tax -8 Mangara of Corondor -8 Mystic Confluence -8 Sower of Temptation -8 Tidespout Tyrant -1 True-Name Nemesis -8 Diabolic Intent -1 Nirkana Revenant -8 Noosegraf Mob -8 Nyxathid -8 Goblin Razerunners -8 Magmatic Force -8 War's Toll -1 Doubling Season -8 Greater Good -8 Magus of the Candelabra -8 Seedborn Muse -8 Vigor -8 Apocalypse Hydra -8 Evil Twin -8 Gwafa Hazid, Profiteer -8 Mind's Eye -1 Mycosynth Lattice - -[M19 Secret Cards] -Ajani, Wise Counselor -Ajani's Influence -Court Cleric -Serra's Guardian -Silverbeak Griffin -Tezzeret, Cruel Machinist -Riddlemaster Sphinx -Pendulum of Patterns -Tezzeret's Gatebreaker -Tezzeret's Strider -Liliana, the Necromancer -Arisen Gorgon -Gravewaker -Liliana's Spoils -Tattered Mummy -Sarkhan, Dragonsoul -Kargan Dragonrider -Sarkhan's Dragonfire -Sarkhan's Whelp -Shivan Dragon -Vivien of the Arkbow -Aggressive Mammoth -Skalla Wolf -Ursine Champion -Vivien's Jaguar -Nexus of Fate -Sun Sentinel -Air Elemental -Befuddle -Mist-Cloaked Herald -Waterknot -Grasping Scoundrel -Radiating Lightning -Llanowar Elves -Cinder Barrens -Forsaken Sanctuary -Foul Orchard -Highland Lake -Meandering River -Stone Quarry -Submerged Boneyard -Timber Gorge -Tranquil Expanse -Woodland Stream - -[M19 Lands] -5 Cinder Barrens|M19 -5 Forsaken Sanctuary|M19 -5 Foul Orchard|M19 -5 Highland Lake|M19 -5 Meandering River|M19 -5 Stone Quarry|M19 -5 Submerged Boneyard|M19 -5 Timber Gorge|M19 -5 Tranquil Expanse|M19 -5 Woodland Stream|M19 -14 Forest|M19 -14 Island|M19 -14 Mountain|M19 -14 Plains|M19 -14 Swamp|M19 - -[GRN Lands] -10 Golgari Guildgate|GRN -10 Izzet Guildgate|GRN -10 Selesnya Guildgate|GRN -10 Dimir Guildgate|GRN -10 Boros Guildgate|GRN - -[GRN Secret Cards] -Ral, Caller of Storms -Ral's Dispersal -Ral's Staticaster -Vraska, Regal Gorgon -Attendant of Vraska -Vraska's Stoneglare -Impervious Greatwurm -Precision Bolt -Kraul Raider -Golgari Guildgate|GRN -Izzet Guildgate|GRN -Selesnya Guildgate|GRN -Dimir Guildgate|GRN -Boros Guildgate|GRN - -[RNA Lands] -10 Azorius Guildgate|RNA -10 Gruul Guildgate|RNA -10 Orzhov Guildgate|RNA -10 Rakdos Guildgate|RNA -10 Simic Guildgate|RNA - -[RNA Secret Cards] -Dovin, Architect of Law -Elite Arrester -Dovin's Dismissal -Dovin's Automaton -Domri, City Smasher -Ragefire -Charging War Boar -Domri's Nodorog -The Haunt of Hightower -Azorius Guildgate|RNA -Gruul Guildgate|RNA -Orzhov Guildgate|RNA -Rakdos Guildgate|RNA -Simic Guildgate|RNA - -[WAR Secret Cards] -Gideon, the Oathsworn -Desperate Lunge -Gideon's Battle Cry -Gideon's Company -Orzhov Guildgate|WAR -Jace, Arcane Strategist -Guildpact Informant -Jace's Projection -Jace's Ruse -Simic Guildgate|WAR -Tezzeret, Master of the Bridge - -[MH1 Lands] -10 Snow-Covered Plains|MH1 -10 Snow-Covered Island|MH1 -10 Snow-Covered Swamp|MH1 -10 Snow-Covered Mountain|MH1 -10 Snow-Covered Forest|MH1 - -[MH1 Secret Cards] -Flusterstorm -Snow-Covered Plains -Snow-Covered Island -Snow-Covered Swamp -Snow-Covered Mountain -Snow-Covered Forest - -[M20 Secret Cards] -Rienne, Angel of Rebirth -Ajani, Inspiring Leader -Goldmane Griffin -Savannah Sage -Twinblade Paladin -Mu Yanling, Celestial Wind -Celestial Messenger -Waterkin Shaman -Yanling's Harbinger -Sorin, Vampire Lord -Savage Gorger -Sorin's Guide -Thirsting Bloodlord -Chandra, Flame's Fury -Chandra's Flame Wave -Pyroclastic Elemental -Wildfire Elemental -Vivien, Nature's Avenger -Ethereal Elk -Gnarlback Rhino -Vivien's Crocodile -Angelic Guardian -Bastion Enforcer -Concordia Pegasus -Haazda Officer -Impassioned Orator -Imperial Outrider -Ironclad Krovod -Prowling Caracal -Serra's Guardian -Show of Valor -Siege Mastodon -Take Vengeance -Trusted Pegasus -Coral Merfolk -Phantom Warrior -Riddlemaster Sphinx -Snapping Drake -Bartizan Bats -Bogstomper -Dark Remedy -Disentomb -Gravewaker -Skeleton Archer -Sorin's Thirst -Vampire Opportunist -Walking Corpse -Engulfing Eruption -Fearless Halberdier -Goblin Assailant -Hostile Minotaur -Immortal Phoenix -Nimble Birdsticker -Rubblebelt Recluse -Shivan Dragon -Volcanic Dragon -Aggressive Mammoth -Bristling Boar -Canopy Spider -Frilled Sandwalla -Oakenform -Prized Unicorn -Titanic Growth -Woodland Mystic - -Bloodfell Caves -Blossoming Sands -Dismal Backwater -Jungle Hollow -Rugged Highlands -Scoured Barrens -Swiftwater Cliffs -Thornwood Falls -Tranquil Cove -Wind-Scarred Crag -Evolving Wilds - -[M20 Lands] -5 Bloodfell Caves|M20 -5 Blossoming Sands|M20 -5 Dismal Backwater|M20 -5 Jungle Hollow|M20 -5 Rugged Highlands|M20 -5 Scoured Barrens|M20 -5 Swiftwater Cliffs|M20 -5 Thornwood Falls|M20 -5 Tranquil Cove|M20 -5 Wind-Scarred Crag|M20 -5 Evolving Wilds -13 Forest|M20 -13 Island|M20 -13 Mountain|M20 -13 Plains|M20 -13 Swamp|M20 - -[ELD Secret Cards] -Kenrith, the Returned King -Rowan, Fearless Sparkmage -Garrison Griffin -Rowan's Battleguard -Rowan's Stalwarts -Wind-Scarred Crag -Oko, the Trickster -Oko's Accomplices -Bramblefort Fink -Oko's Hospitality -Thornwood Falls -Mace of the Valiant -Silverwing Squadron -Faerie Formation -Shimmer Dragon -Workshop Elders -Chittering Witch -Taste of Death -Embereth Skyblazer -Steelbane Hydra -Thorn Mammoth -Alela, Artful Provocateur -Banish into Fable -Chulane, Teller of Tales -Gluttonous Troll -Knights' Charge -Korvold, Fae-Cursed King -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 Locthwain|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 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 -Kenrith's Transformation|ELD|2 -Improbable Alliance|ELD|2 -Inspiring Veteran|ELD|2 - [JMP Above the Clouds 1] 1 Warden of Evos Isle|JMP 1 Inniaz, the Gale Force|JMP diff --git a/forge-gui/res/editions/30th Anniversary Edition.txt b/forge-gui/res/editions/30th Anniversary Edition.txt index de6b0282f84..abc30da2ce6 100644 --- a/forge-gui/res/editions/30th Anniversary Edition.txt +++ b/forge-gui/res/editions/30th Anniversary Edition.txt @@ -4,7 +4,7 @@ Date=2022-11-28 Name=30th Anniversary Edition Type=Collector_Edition BoosterCovers=3 -Booster=7 Common:fromsheet("30A cards"), 3 Uncommon:fromSheet("30A cards"), 1 Rare:fromSheet("30A cards"), 2 BasicLand:fromSheet("30A cards"), 1 fromSheet("30A retrolands"), 1 fromSheet("30A retrocards") +Booster=7 Common:fromsheet("30A cards"), 3 Uncommon:fromSheet("30A cards"), 1 Rare:fromSheet("30A cards"), 2 BasicLand:fromSheet("30A cards"), 1 BasicLand:fromSheet("30A retro frame"), 1 !BasicLand:fromSheet("30A retro frame") Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 ScryfallCode=30A @@ -275,7 +275,6 @@ ScryfallCode=30A 263 R Nevinyrral's Disk @Mark Tedin 264 U Obsianus Golem @Jesper Myrfors 265 U Rod of Ruin @Christopher Rush -266 U Sol Ring @Mark Tedin 267 U Soul Net @Dameon Willich 268 R Sunglasses of Urza @Dan Frazier 269 U Throne of Bone @Anson Maddocks @@ -307,6 +306,11 @@ ScryfallCode=30A 295 L Forest @Christopher Rush 296 L Forest @Christopher Rush 297 L Forest @Christopher Rush + +[alternate art] +266 U Sol Ring @Mark Tedin + +[retro frame] 298 R Animate Wall @Dan Frazier 299 R Armageddon @Jesper Myrfors 300 R Balance @Mark Poole @@ -605,307 +609,6 @@ ScryfallCode=30A 593 L Forest @Christopher Rush 594 L Forest @Christopher Rush -[retrocards] -1 Animate Wall|30A|2 -1 Armageddon|30A|2 -1 Balance|30A|2 -1 Benalish Hero|30A|2 -1 Black Ward|30A|2 -1 Blaze of Glory|30A|2 -1 Blessing|30A|2 -1 Blue Ward|30A|2 -1 Castle|30A|2 -1 Circle of Protection: Black|30A|2 -1 Circle of Protection: Blue|30A|2 -1 Circle of Protection: Green|30A|2 -1 Circle of Protection: Red|30A|2 -1 Circle of Protection: White|30A|2 -1 Consecrate Land|30A|2 -1 Conversion|30A|2 -1 Death Ward|30A|2 -1 Disenchant|30A|2 -1 Farmstead|30A|2 -1 Green Ward|30A|2 -1 Guardian Angel|30A|2 -1 Healing Salve|30A|2 -1 Holy Armor|30A|2 -1 Holy Strength|30A|2 -1 Island Sanctuary|30A|2 -1 Karma|30A|2 -1 Lance|30A|2 -1 Mesa Pegasus|30A|2 -1 Northern Paladin|30A|2 -1 Pearled Unicorn|30A|2 -1 Personal Incarnation|30A|2 -1 Purelace|30A|2 -1 Red Ward|30A|2 -1 Resurrection|30A|2 -1 Reverse Damage|30A|2 -1 Righteousness|30A|2 -1 Samite Healer|30A|2 -1 Savannah Lions|30A|2 -1 Serra Angel|30A|2 -1 Swords to Plowshares|30A|2 -1 Veteran Bodyguard|30A|2 -1 Wall of Swords|30A|2 -1 White Knight|30A|2 -1 White Ward|30A|2 -1 Wrath of God|30A|2 -1 Air Elemental|30A|2 -1 Ancestral Recall|30A|2 -1 Animate Artifact|30A|2 -1 Blue Elemental Blast|30A|2 -1 Braingeyser|30A|2 -1 Clone|30A|2 -1 Control Magic|30A|2 -1 Copy Artifact|30A|2 -1 Counterspell|30A|2 -1 Creature Bond|30A|2 -1 Drain Power|30A|2 -1 Feedback|30A|2 -1 Flight|30A|2 -1 Invisibility|30A|2 -1 Jump|30A|2 -1 Lifetap|30A|2 -1 Lord of Atlantis|30A|2 -1 Magical Hack|30A|2 -1 Mahamoti Djinn|30A|2 -1 Mana Short|30A|2 -1 Merfolk of the Pearl Trident|30A|2 -1 Phantasmal Forces|30A|2 -1 Phantasmal Terrain|30A|2 -1 Phantom Monster|30A|2 -1 Pirate Ship|30A|2 -1 Power Leak|30A|2 -1 Power Sink|30A|2 -1 Prodigal Sorcerer|30A|2 -1 Psionic Blast|30A|2 -1 Psychic Venom|30A|2 -1 Sea Serpent|30A|2 -1 Siren's Call|30A|2 -1 Sleight of Mind|30A|2 -1 Spell Blast|30A|2 -1 Stasis|30A|2 -1 Steal Artifact|30A|2 -1 Thoughtlace|30A|2 -1 Time Walk|30A|2 -1 Timetwister|30A|2 -1 Twiddle|30A|2 -1 Unsummon|30A|2 -1 Vesuvan Doppelganger|30A|2 -1 Volcanic Eruption|30A|2 -1 Wall of Air|30A|2 -1 Wall of Water|30A|2 -1 Water Elemental|30A|2 -1 Animate Dead|30A|2 -1 Bad Moon|30A|2 -1 Black Knight|30A|2 -1 Bog Wraith|30A|2 -1 Cursed Land|30A|2 -1 Dark Ritual|30A|2 -1 Deathgrip|30A|2 -1 Deathlace|30A|2 -1 Demonic Hordes|30A|2 -1 Demonic Tutor|30A|2 -1 Drain Life|30A|2 -1 Drudge Skeletons|30A|2 -1 Evil Presence|30A|2 -1 Fear|30A|2 -1 Frozen Shade|30A|2 -1 Gloom|30A|2 -1 Howl from Beyond|30A|2 -1 Hypnotic Specter|30A|2 -1 Lich|30A|2 -1 Lord of the Pit|30A|2 -1 Mind Twist|30A|2 -1 Nether Shadow|30A|2 -1 Nettling Imp|30A|2 -1 Nightmare|30A|2 -1 Paralyze|30A|2 -1 Pestilence|30A|2 -1 Plague Rats|30A|2 -1 Raise Dead|30A|2 -1 Royal Assassin|30A|2 -1 Sacrifice|30A|2 -1 Scathe Zombies|30A|2 -1 Scavenging Ghoul|30A|2 -1 Sengir Vampire|30A|2 -1 Simulacrum|30A|2 -1 Sinkhole|30A|2 -1 Terror|30A|2 -1 Unholy Strength|30A|2 -1 Wall of Bone|30A|2 -1 Warp Artifact|30A|2 -1 Sol Ring|30A|2 -1 Will-o'-the-Wisp|30A|2 -1 Word of Command|30A|2 -1 Zombie Master|30A|2 -1 Burrowing|30A|2 -1 Chaoslace|30A|2 -1 Disintegrate|30A|2 -1 Dragon Whelp|30A|2 -1 Dwarven Demolition Team|30A|2 -1 Dwarven Warriors|30A|2 -1 Earth Elemental|30A|2 -1 Earthquake|30A|2 -1 False Orders|30A|2 -1 Fire Elemental|30A|2 -1 Fireball|30A|2 -1 Firebreathing|30A|2 -1 Flashfires|30A|2 -1 Fork|30A|2 -1 Goblin Balloon Brigade|30A|2 -1 Goblin King|30A|2 -1 Granite Gargoyle|30A|2 -1 Gray Ogre|30A|2 -1 Hill Giant|30A|2 -1 Hurloon Minotaur|30A|2 -1 Ironclaw Orcs|30A|2 -1 Keldon Warlord|30A|2 -1 Lightning Bolt|30A|2 -1 Mana Flare|30A|2 -1 Manabarbs|30A|2 -1 Mons's Goblin Raiders|30A|2 -1 Orcish Artillery|30A|2 -1 Orcish Oriflamme|30A|2 -1 Power Surge|30A|2 -1 Raging River|30A|2 -1 Red Elemental Blast|30A|2 -1 Roc of Kher Ridges|30A|2 -1 Rock Hydra|30A|2 -1 Sedge Troll|30A|2 -1 Shatter|30A|2 -1 Shivan Dragon|30A|2 -1 Smoke|30A|2 -1 Stone Giant|30A|2 -1 Stone Rain|30A|2 -1 Tunnel|30A|2 -1 Two-Headed Giant of Foriys|30A|2 -1 Uthden Troll|30A|2 -1 Wall of Fire|30A|2 -1 Wall of Stone|30A|2 -1 Wheel of Fortune|30A|2 -1 Aspect of Wolf|30A|2 -1 Berserk|30A|2 -1 Birds of Paradise|30A|2 -1 Camouflage|30A|2 -1 Channel|30A|2 -1 Cockatrice|30A|2 -1 Craw Wurm|30A|2 -1 Elvish Archers|30A|2 -1 Fastbond|30A|2 -1 Fog|30A|2 -1 Force of Nature|30A|2 -1 Fungusaur|30A|2 -1 Gaea's Liege|30A|2 -1 Giant Growth|30A|2 -1 Giant Spider|30A|2 -1 Grizzly Bears|30A|2 -1 Hurricane|30A|2 -1 Ice Storm|30A|2 -1 Instill Energy|30A|2 -1 Ironroot Treefolk|30A|2 -1 Kudzu|30A|2 -1 Ley Druid|30A|2 -1 Lifeforce|30A|2 -1 Lifelace|30A|2 -1 Living Artifact|30A|2 -1 Living Lands|30A|2 -1 Llanowar Elves|30A|2 -1 Lure|30A|2 -1 Natural Selection|30A|2 -1 Regeneration|30A|2 -1 Regrowth|30A|2 -1 Scryb Sprites|30A|2 -1 Shanodin Dryads|30A|2 -1 Stream of Life|30A|2 -1 Thicket Basilisk|30A|2 -1 Timber Wolves|30A|2 -1 Tranquility|30A|2 -1 Tsunami|30A|2 -1 Verduran Enchantress|30A|2 -1 Wall of Brambles|30A|2 -1 Wall of Ice|30A|2 -1 Wall of Wood|30A|2 -1 Wanderlust|30A|2 -1 War Mammoth|30A|2 -1 Web|30A|2 -1 Wild Growth|30A|2 -1 Ankh of Mishra|30A|2 -1 Basalt Monolith|30A|2 -1 Black Lotus|30A|2 -1 Black Vise|30A|2 -1 Celestial Prism|30A|2 -1 Chaos Orb|30A|2 -1 Clockwork Beast|30A|2 -1 Conservator|30A|2 -1 Copper Tablet|30A|2 -1 Crystal Rod|30A|2 -1 Cyclopean Tomb|30A|2 -1 Dingus Egg|30A|2 -1 Disrupting Scepter|30A|2 -1 Forcefield|30A|2 -1 Gauntlet of Might|30A|2 -1 Glasses of Urza|30A|2 -1 Helm of Chatzuk|30A|2 -1 The Hive|30A|2 -1 Howling Mine|30A|2 -1 Icy Manipulator|30A|2 -1 Illusionary Mask|30A|2 -1 Iron Star|30A|2 -1 Ivory Cup|30A|2 -1 Jade Monolith|30A|2 -1 Jade Statue|30A|2 -1 Jayemdae Tome|30A|2 -1 Juggernaut|30A|2 -1 Kormus Bell|30A|2 -1 Library of Leng|30A|2 -1 Living Wall|30A|2 -1 Mana Vault|30A|2 -1 Meekstone|30A|2 -1 Mox Emerald|30A|2 -1 Mox Jet|30A|2 -1 Mox Pearl|30A|2 -1 Mox Ruby|30A|2 -1 Mox Sapphire|30A|2 -1 Nevinyrral's Disk|30A|2 -1 Obsianus Golem|30A|2 -1 Rod of Ruin|30A|2 -1 Sol Ring|30A|2 -1 Soul Net|30A|2 -1 Sunglasses of Urza|30A|2 -1 Throne of Bone|30A|2 -1 Time Vault|30A|2 -1 Winter Orb|30A|2 -1 Wooden Sphere|30A|2 -1 Badlands|30A|2 -1 Bayou|30A|2 -1 Plateau|30A|2 -1 Savannah|30A|2 -1 Scrubland|30A|2 -1 Taiga|30A|2 -1 Tropical Island|30A|2 -1 Tundra|30A|2 -1 Underground Sea|30A|2 -1 Volcanic Island|30A|2 - -[retrolands] -1 Plains|30A|4 -1 Plains|30A|5 -1 Plains|30A|6 -1 Island|30A|4 -1 Island|30A|5 -1 Island|30A|6 -1 Swamp|30A|4 -1 Swamp|30A|5 -1 Swamp|30A|6 -1 Mountain|30A|4 -1 Mountain|30A|5 -1 Mountain|30A|6 -1 Forest|30A|4 -1 Forest|30A|5 -1 Forest|30A|6 - [tokens] b_1_1_skeleton b_5_5_demon_flying diff --git a/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt b/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt index 3996b420206..d92a9df83bd 100644 --- a/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt +++ b/forge-gui/res/editions/Alchemy Horizons Baldur's Gate.txt @@ -55,15 +55,6 @@ ScryfallCode=HBG 43 U Grave Choice @Will Gist 44 C Hook Horror @Olivier Bernard 45 R The Hourglass Coven @Konstantin Porubov -45a R Hag of Ceaseless Torment @Konstantin Porubov -45b R Hag of Dark Duress @Konstantin Porubov -45c R Hag of Death's Legion @Konstantin Porubov -45d R Hag of Inner Weakness @Konstantin Porubov -45e R Hag of Mage's Doom @Konstantin Porubov -45f R Hag of Noxious Nightmares @Konstantin Porubov -45g R Hag of Scoured Thoughts @Konstantin Porubov -45h R Hag of Syphoned Breath @Konstantin Porubov -45i R Hag of Twisted Visions @Konstantin Porubov 46 U Mind Spike @Matt Forsyth 47 C Sewer Plague @Piotr Foksowicz 48 R Stroke of Luck @Forrest Imel @@ -93,7 +84,7 @@ ScryfallCode=HBG 72 R Jon Irenicus, the Exile @Igor Grechanyi 73 U Liara of the Flaming Fist @David Rapoza 74 U Minthara of the Absolute @Evyn Fong -75 M Tasha, Unholy Archmage @Martina Fackova +75 M Tasha, Unholy Archmage @Martina Fačková 76 R Ulder Ravengard, Marshal @Eric Deschamps 77 U Gate of the Black Dragon @Sergey Glushakov 78 U Gate to Manorborn @Andreas Rocha @@ -211,8 +202,8 @@ ScryfallCode=HBG 190 U Swashbuckler Extraordinaire @Durion 191 U Two-Handed Axe @Milivoj Ćeran 192 C Unexpected Windfall @Alayna Danner -193 C Valor Singer @Justyna Gil -194 R Wrathful Red Dragon @Dan Scott +193 C Valor Singer @Justyna Dura +194 R Wrathful Red Dragon @Dan Murayama Scott 195 C You Come to the Gnoll Camp @Billy Christian 196 C You Find Some Prisoners @Lie Setiawan 197 C Young Red Dragon @Adam Vehige @@ -259,7 +250,7 @@ ScryfallCode=HBG 238 U Korlessa, Scale Singer @Jesper Ejsing 239 U Krydle of Baldur's Gate @Bryan Sola 240 U Lozhan, Dragons' Legacy @Rudy Siswanto -241 R Mazzy, Truesword Paladin @Justyna Gil +241 R Mazzy, Truesword Paladin @Justyna Dura 242 R Miirym, Sentinel Wyrm @Kekai Kotaki 243 M Minsc & Boo, Timeless Heroes @Andreas Zafiratos 244 M Nalia de'Arnise @John Stanko @@ -268,7 +259,7 @@ ScryfallCode=HBG 247 M Prosper, Tome-Bound @Yongjae Choi 248 R Raggadragga, Goreguts Boss @Xavier Ribeiro 249 R Raphael, Fiendish Savior @Livia Prima -250 U Thrakkus the Butcher @Nestor Ossandon Leal +250 U Thrakkus the Butcher @Néstor Ossandón Leal 251 U Trelasarra, Moon Dancer @Kieran Yanner 252 U Bag of Holding @Evyn Fong 253 R Basilisk Collar @Craig J Spearing @@ -337,3 +328,15 @@ A239 U A-Krydle of Baldur's Gate @Bryan Sola A243 M A-Minsc & Boo, Timeless Heroes @Andreas Zafiratos A259 C A-Lantern of Revealing @Eytan Zana A262 U A-Navigation Orb @Robin Olausson + +[conjured] +45a R Hag of Ceaseless Torment @Konstantin Porubov +45b R Hag of Dark Duress @Konstantin Porubov +45c R Hag of Death's Legion @Konstantin Porubov +45d R Hag of Inner Weakness @Konstantin Porubov +45e R Hag of Mage's Doom @Konstantin Porubov +45f R Hag of Noxious Nightmares @Konstantin Porubov +45g R Hag of Scoured Thoughts @Konstantin Porubov +45h R Hag of Syphoned Breath @Konstantin Porubov +45i R Hag of Twisted Visions @Konstantin Porubov + diff --git a/forge-gui/res/editions/Amonkhet.txt b/forge-gui/res/editions/Amonkhet.txt index ac6c0248b3b..31111edf6a2 100644 --- a/forge-gui/res/editions/Amonkhet.txt +++ b/forge-gui/res/editions/Amonkhet.txt @@ -5,8 +5,8 @@ Name=Amonkhet Code2=AKH Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("AKH Planeswalker Decks and Toolkit"), 3 Uncommon:!fromSheet("AKH Planeswalker Decks and Toolkit"), 1 RareMythic:!fromSheet("AKH Planeswalker Decks and Toolkit"), 1 BasicLand AKH -AdditionalSheetForFoils=fromSheet("MPS Amonkhet Invocations") +Booster=10 Common:fromSheet("AKH cards"), 3 Uncommon:fromSheet("AKH cards"), 1 RareMythic:fromSheet("AKH cards"), 1 BasicLand AKH +AdditionalSheetForFoils=fromSheet("MPS_AKH cards") FatPack=10 FatPackExtraSlots=80 BasicLands AdditionalSetUnlockedInQuest=MPS_AKH @@ -283,6 +283,8 @@ ScryfallCode=AKH 267 L Forest @Titus Lunter 268 L Forest @Titus Lunter 269 L Forest @Matt Stewart + +[precon product] 270 M Gideon, Martial Paragon @Daarken 271 U Companion of the Trials @Aaron Miller 272 R Gideon's Resolve @Jakub Kasper diff --git a/forge-gui/res/editions/Battle for Zendikar.txt b/forge-gui/res/editions/Battle for Zendikar.txt index 7eb55e0f3e2..1c29f9c0636 100644 --- a/forge-gui/res/editions/Battle for Zendikar.txt +++ b/forge-gui/res/editions/Battle for Zendikar.txt @@ -5,10 +5,10 @@ Code=BFZ Code2=BFZ Type=Expansion BoosterCovers=5 -Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand BFZ +Booster=10 Common:fromSheet("BFZ cards"), 3 Uncommon:fromSheet("BFZ cards"), 1 RareMythic:fromSheet("BFZ cards"), 1 BasicLand BFZ FatPack=9 FatPackExtraSlots=80 BasicLands -AdditionalSheetForFoils=fromSheet("EXP Lands") +AdditionalSheetForFoils=fromSheet("EXP cards") AdditionalSetUnlockedInQuest=EXP ScryfallCode=BFZ diff --git a/forge-gui/res/editions/Battlebond.txt b/forge-gui/res/editions/Battlebond.txt index 29b967e2163..e84bc8a2444 100644 --- a/forge-gui/res/editions/Battlebond.txt +++ b/forge-gui/res/editions/Battlebond.txt @@ -4,7 +4,7 @@ Date=2018-06-08 Name=Battlebond Type=Draft BoosterCovers=3 -Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythic"), 1 BasicLand +Booster=10 Common, 3 Uncommon, 1 fromSheet("BBD RareMythicDistribution"), 1 BasicLand ScryfallCode=BBD [cards] @@ -262,9 +262,81 @@ ScryfallCode=BBD 252 L Swamp @Titus Lunter 253 L Mountain @Titus Lunter 254 L Forest @Titus Lunter + +[etched] 255 M Will Kenrith @Anna Steinbauer 256 M Rowan Kenrith @Anna Steinbauer +[RareMythicDistribution] +1 Will Kenrith|BBD|1 +1 Rowan Kenrith|BBD|1 +8 Regna, the Redeemer +8 Krav, the Unredeemed +8 Zndrsplt, Eye of Wisdom +8 Okaun, Eye of Chaos +8 Virtus the Veiled +8 Gorm the Great +8 Khorvath Brightflame +8 Sylvia Brightspear +8 Pir, Imaginative Rascal +8 Toothy, Imaginary Friend +1 Arena Rector +1 Brightling +8 Play of the Game +8 Regna's Sanction +8 Together Forever +1 Arcane Artisan +8 Game Plan +8 Spellseeker +8 Zndrsplt's Judgment +1 Archfiend of Despair +8 Mindblade Render +1 Stunning Reversal +8 Thrilling Encore +8 Virtus's Maneuver +8 Bonus Round +8 Khorvath's Fury +1 Najeela, the Blade-Blossom +8 Stolen Strategy +1 Bramble Sovereign +8 Generous Patron +1 Grothama, All-Devouring +8 Pir's Whim +8 Archon of Valor's Reach +8 Last One Standing +8 Sentinel Tower +8 Victory Chimes +8 Bountiful Promenade +8 Luxury Suite +8 Morphic Pool +8 Sea of Clouds +8 Spire Garden +8 Angelic Chorus +8 Kor Spiritdancer +1 Land Tax +8 Mangara of Corondor +8 Mystic Confluence +8 Sower of Temptation +8 Tidespout Tyrant +1 True-Name Nemesis +8 Diabolic Intent +1 Nirkana Revenant +8 Noosegraf Mob +8 Nyxathid +8 Goblin Razerunners +8 Magmatic Force +8 War's Toll +1 Doubling Season +8 Greater Good +8 Magus of the Candelabra +8 Seedborn Muse +8 Vigor +8 Apocalypse Hydra +8 Evil Twin +8 Gwafa Hazid, Profiteer +8 Mind's Eye +1 Mycosynth Lattice + [tokens] w_1_1_spirit_flying w_1_1_warrior diff --git a/forge-gui/res/editions/Bloomburrow.txt b/forge-gui/res/editions/Bloomburrow.txt index 909acbe75d2..832a98d1bf0 100644 --- a/forge-gui/res/editions/Bloomburrow.txt +++ b/forge-gui/res/editions/Bloomburrow.txt @@ -9,6 +9,7 @@ Booster=6 Common, 1 Common-Guest, 3 Uncommon, 1 RareMythic, 1 FullArtLand, 1 Wil Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 +#Numbers from https://mtgscribe.com/2024/07/14/play-booster-fact-sheet-bloomburrow/ [Common] Base=Common:fromSheet("BLB cards") @@ -21,9 +22,6 @@ Base=Uncommon:fromSheet("BLB cards") [RareMythic] Base=RareMythic:fromSheet("BLB cards") -# I don't know about these numbers, so i'm just copying them from MH3 -Replace=.021F fromSheet("BLB borderless") -Replace=.051F fromSheet("BLB showcase") [FullArtLand] Base=Land:fromSheet("BLB full art") @@ -33,9 +31,7 @@ Replace=.20F Land:fromSheet("BLB full art")+ Base=Common:fromSheet("BLB cards") # I don't know about these numbers, so i'm just copying them from MH3 Replace=.417F Uncommon:fromSheet("BLB cards") -Replace=.078F RareMythic:fromSheet("BLB cards") -Replace=.004F fromSheet("BLB borderless") -Replace=.042F fromSheet("BLB showcase") +Replace=.012F RareMythic:fromSheet("BLB cards") [cards] 1 C Banishing Light @Zoltan Boros @@ -319,16 +315,6 @@ Replace=.042F fromSheet("BLB showcase") 279 L Forest @David Robert Hovey 280 L Forest @David Robert Hovey 281 L Forest @David Robert Hovey -369 L Plains @Piotr Dura -370 L Plains @Julian Kok Joon Wen -371 L Island @Alexander Forssberg -372 L Island @Lorenzo Lanfranconi -373 L Swamp @Piotr Dura -374 L Swamp @Thomas Stoop -375 L Mountain @Samuele Bandini -376 L Mountain @Adam Paquette -377 L Forest @Alayna Danner -378 L Forest @Donato Giancola [borderless] 282 M Season of the Burrow @Edgar Sánchez Hidalgo @@ -424,6 +410,16 @@ Replace=.042F fromSheet("BLB showcase") 368 R Fountainport @Leon Tukker [precon product] +369 L Plains @Piotr Dura +370 L Plains @Julian Kok Joon Wen +371 L Island @Alexander Forssberg +372 L Island @Lorenzo Lanfranconi +373 L Swamp @Piotr Dura +374 L Swamp @Thomas Stoop +375 L Mountain @Samuele Bandini +376 L Mountain @Adam Paquette +377 L Forest @Alayna Danner +378 L Forest @Donato Giancola 379 M Bria, Riptide Rogue @Borja Pindado 380 M Byrke, Long Ear of the Law @Manuel Castañón 387 R Serra Redeemer @Joshua Raphael @@ -444,8 +440,6 @@ Replace=.042F fromSheet("BLB showcase") 383 U Fell @A. M. Sartor 384 U Wear Down @Iris Compiet 385 U Stormcatch Mentor @Manuel Castañón - -[bundle] 386 R Thundertrap Trainer @Jesper Ejsing [rebalanced] diff --git a/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt b/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt index e434eb1635e..d958b138739 100644 --- a/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt +++ b/forge-gui/res/editions/Commander Legends Battle for Baldur's Gate.txt @@ -370,26 +370,6 @@ ScryfallCode=CLB 359 C Sea Gate @Kamila Szutenberg 360 R Sea of Clouds @Matt Gaser 361 R Spire Garden @Alexander Forssberg -451 L Plains @Bruce Brenneise -452 L Plains @Leanna Crossan -453 L Plains @Titus Lunter -454 L Plains @Emmanuel Shiu -455 L Island @Bruce Brenneise -456 L Island @Piotr Dura -457 L Island @James Paick -458 L Island @Sam White -459 L Swamp @Piotr Dura -460 L Swamp @Logan Feliciano -461 L Swamp @Grady Frederick -462 L Swamp @Sam White -463 L Mountain @Matt Gaser -464 L Mountain @Lucas Graciano -465 L Mountain @Muhammad Firdaus -466 L Mountain @Sam White -467 L Forest @Bruce Brenneise -468 L Forest @Muhammad Firdaus -469 L Forest @Lucas Graciano -470 L Forest @Julian Kok Joon Wen [borderless] 362 M Elminster @Tyler Jacobson @@ -483,6 +463,8 @@ ScryfallCode=CLB 448 C Moss Diamond @Phil Stone 449 C Sky Diamond @Phil Stone 450 U Stonespeaker Crystal @Mark A. Nelson + +[etched] 471 U Abdel Adrian, Gorion's Ward @Karl Kopinski 472 U Ellyn Harbreeze, Busybody @Dan Murayama Scott 473 U Far Traveler @Alix Branwyn @@ -666,6 +648,26 @@ ScryfallCode=CLB 645 R Sarevok's Tome @Titus Lunter [precon product] +451 L Plains @Bruce Brenneise +452 L Plains @Leanna Crossan +453 L Plains @Titus Lunter +454 L Plains @Emmanuel Shiu +455 L Island @Bruce Brenneise +456 L Island @Piotr Dura +457 L Island @James Paick +458 L Island @Sam White +459 L Swamp @Piotr Dura +460 L Swamp @Logan Feliciano +461 L Swamp @Grady Frederick +462 L Swamp @Sam White +463 L Mountain @Matt Gaser +464 L Mountain @Lucas Graciano +465 L Mountain @Muhammad Firdaus +466 L Mountain @Sam White +467 L Forest @Bruce Brenneise +468 L Forest @Muhammad Firdaus +469 L Forest @Lucas Graciano +470 L Forest @Julian Kok Joon Wen 646 M Captain N'ghathrod @Andrey Kuzinskiy 647 M Faldorn, Dread Wolf Herald @Jason A. Engle 648 M Firkraag, Cunning Instigator @Andrew Mar diff --git a/forge-gui/res/editions/Commander Legends.txt b/forge-gui/res/editions/Commander Legends.txt index 6afbdf1d5d3..e3a3b1e6dda 100644 --- a/forge-gui/res/editions/Commander Legends.txt +++ b/forge-gui/res/editions/Commander Legends.txt @@ -527,7 +527,7 @@ ScryfallCode=CMR 512 M Tevesh Szat, Doom of Fools @Chris Rallis 513 M Jeska, Thrice Reborn @Chris Rallis -[showcase] +[etched] 514 M Najeela, the Blade-Blossom @Matt Stewart 515 M Akiri, Line-Slinger @David Gaillet 516 M Brago, King Eternal @Karla Ortiz diff --git a/forge-gui/res/editions/Conspiracy Take the Crown.txt b/forge-gui/res/editions/Conspiracy Take the Crown.txt index 1f89547d7a3..d856802edc9 100644 --- a/forge-gui/res/editions/Conspiracy Take the Crown.txt +++ b/forge-gui/res/editions/Conspiracy Take the Crown.txt @@ -5,7 +5,7 @@ Name=Conspiracy: Take the Crown Code2=CN2 Type=Draft BoosterCovers=3 -Booster=10 Common:!fromSheet("CN2 Draft Matters"), 3 Uncommon:!fromSheet("CN2 Draft Matters"), 1 RareMythic:!fromSheet("CN2 Draft Matters"), 1 fromSheet("CN2 Draft Matters") +Booster=10 Common:!fromSheet("CN2 Draft Matters"), 3 Uncommon:!fromSheet("CN2 Draft Matters"), 1 RareMythic:!fromSheet("CN2 Draft Matters"):fromSheet("CN2 cards"), 1 fromSheet("CN2 Draft Matters") AdditionalSheetForFoils=fromSheet("CN2 Foil Kaya") ScryfallCode=CN2 @@ -162,7 +162,7 @@ ScryfallCode=CN2 150 C Unnerve @Terese Nielsen 151 U Burn Away @Vincent Proce 152 R Burning Wish @Scott M. Fischer -153 R Charmbreaker Devils @Dan Scott +153 R Charmbreaker Devils @Dan Murayama Scott 154 U Coordinated Assault @John Severin Brassell 155 C Ember Beast @David Rapoza 156 C Fiery Fall @Daarken diff --git a/forge-gui/res/editions/Dominaria Remastered.txt b/forge-gui/res/editions/Dominaria Remastered.txt index df2d5c51c45..eafea138da6 100644 --- a/forge-gui/res/editions/Dominaria Remastered.txt +++ b/forge-gui/res/editions/Dominaria Remastered.txt @@ -5,7 +5,7 @@ Name=Dominaria Remastered Code2=DMR Type=Reprint BoosterCovers=3 -Booster=9 Common:fromsheet("DMR cards"), 3 Uncommon:fromSheet("DMR cards"), 1 RareMythic:fromSheet("DMR cards"), 1 BasicLand:fromSheet("DMR showcase"), 1 Any:fromSheet("DMR oldframes") +Booster=9 Common:fromsheet("DMR cards"), 3 Uncommon:fromSheet("DMR cards"), 1 RareMythic:fromSheet("DMR cards"), 1 BasicLand:fromSheet("DMR retro frame"), 1 !BasicLand:fromSheet("DMR retro frame") BoosterMustContain=Legendary Creature Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 @@ -274,7 +274,7 @@ ScryfallCode=DMR 260 U Treva's Ruins @Jerry Tiritilli 261 R Woodland Cemetery @Christine Choi -[showcase] +[retro frame] 262 R Divine Sacrament @Ekaterina Burmak 263 R Enlightened Tutor @Howard Lyon 264 R Glory @Donato Giancola @@ -476,148 +476,6 @@ ScryfallCode=DMR [promo] 457 R Counterspell @Mark Poole -[oldframes] -1 Divine Sacrament|DMR|2 -1 Enlightened Tutor|DMR|2 -1 Glory|DMR|2 -1 Lieutenant Kirtar|DMR|2 -1 Lyra Dawnbringer|DMR|2 -1 Mesa Enchantress|DMR|2 -1 Momentary Blink|DMR|2 -1 Renewed Faith|DMR|2 -1 Savannah Lions|DMR|2 -1 Serra Angel|DMR|2 -1 Serra Avatar|DMR|2 -1 Sevinne's Reclamation|DMR|2 -1 Spirit Link|DMR|2 -1 Swords to Plowshares|DMR|2 -1 Test of Endurance|DMR|2 -1 Whitemane Lion|DMR|2 -1 Windborn Muse|DMR|2 -1 Wrath of God|DMR|2 -1 Arcanis the Omnipotent|DMR|2 -1 Counterspell|DMR|2 -1 Denizen of the Deep|DMR|2 -1 Fact or Fiction|DMR|2 -1 Force of Will|DMR|2 -1 Frantic Search|DMR|2 -1 High Tide|DMR|2 -1 Impulse|DMR|2 -1 Mystic Remora|DMR|2 -1 Mystical Tutor|DMR|2 -1 Opposition|DMR|2 -1 Ovinize|DMR|2 -1 Peregrine Drake|DMR|2 -1 Stroke of Genius|DMR|2 -1 Time Stretch|DMR|2 -1 Turnabout|DMR|2 -1 Urza, Lord High Artificer|DMR|2 -1 Vexing Sphinx|DMR|2 -1 Body Snatcher|DMR|2 -1 Chainer, Dementia Master|DMR|2 -1 Chainer's Edict|DMR|2 -1 Dark Withering|DMR|2 -1 Dread Return|DMR|2 -1 Duress|DMR|2 -1 Entomb|DMR|2 -1 Mindslicer|DMR|2 -1 Nantuko Shade|DMR|2 -1 Necrosavant|DMR|2 -1 No Mercy|DMR|2 -1 Oversold Cemetery|DMR|2 -1 Royal Assassin|DMR|2 -1 Street Wraith|DMR|2 -1 Terror|DMR|2 -1 Undead Gladiator|DMR|2 -1 Vampiric Tutor|DMR|2 -1 Yawgmoth, Thran Physician|DMR|2 -1 Chain Lightning|DMR|2 -1 Dragon Whelp|DMR|2 -1 Empty the Warrens|DMR|2 -1 Fireblast|DMR|2 -1 Flametongue Kavu|DMR|2 -1 Gamble|DMR|2 -1 Gempalm Incinerator|DMR|2 -1 Goblin Matron|DMR|2 -1 Grim Lavamancer|DMR|2 -1 Last Chance|DMR|2 -1 Mogg War Marshal|DMR|2 -1 Overmaster|DMR|2 -1 Pashalik Mons|DMR|2 -1 Shivan Dragon|DMR|2 -1 Siege-Gang Commander|DMR|2 -1 Sneak Attack|DMR|2 -1 Sulfuric Vortex|DMR|2 -1 Valduk, Keeper of the Flame|DMR|2 -1 Worldgorger Dragon|DMR|2 -1 Arboria|DMR|2 -1 Birds of Paradise|DMR|2 -1 Deadwood Treefolk|DMR|2 -1 Elvish Spirit Guide|DMR|2 -1 Exploration|DMR|2 -1 Fa'adiyah Seer|DMR|2 -1 Forgotten Ancient|DMR|2 -1 Invigorating Boon|DMR|2 -1 Jolrael, Mwonvuli Recluse|DMR|2 -1 Kamahl, Fist of Krosa|DMR|2 -1 Lull|DMR|2 -1 Nature's Lore|DMR|2 -1 Nut Collector|DMR|2 -1 Saproling Symbiosis|DMR|2 -1 Squirrel Nest|DMR|2 -1 Sylvan Library|DMR|2 -1 Wild Dogs|DMR|2 -1 Wild Growth|DMR|2 -1 Worldly Tutor|DMR|2 -1 Absorb|DMR|2 -1 Arcades Sabboth|DMR|2 -1 Decimate|DMR|2 -1 Dralnu's Crusade|DMR|2 -1 Gerrard's Verdict|DMR|2 -1 Hunting Grounds|DMR|2 -1 Mystic Enforcer|DMR|2 -1 Phantom Nishoba|DMR|2 -1 Pyre Zombie|DMR|2 -1 Quicksilver Dagger|DMR|2 -1 Radha, Heir to Keld|DMR|2 -1 Recoil|DMR|2 -1 Rith, the Awakener|DMR|2 -1 Sawtooth Loon|DMR|2 -1 Sol'kanar the Swamp King|DMR|2 -1 Spinal Embrace|DMR|2 -1 Spiritmonger|DMR|2 -1 Tatyova, Benthic Druid|DMR|2 -1 Tiana, Ship's Caretaker|DMR|2 -1 Xira Arien|DMR|2 -1 Zur the Enchanter|DMR|2 -1 Crawlspace|DMR|2 -1 Cryptic Gateway|DMR|2 -1 Damping Sphere|DMR|2 -1 Gauntlet of Power|DMR|2 -1 Helm of Awakening|DMR|2 -1 Icy Manipulator|DMR|2 -1 Jester's Cap|DMR|2 -1 Juggernaut|DMR|2 -1 Legacy Weapon|DMR|2 -1 Lotus Blossom|DMR|2 -1 Mind Stone|DMR|2 -1 Ornithopter|DMR|2 -1 Thran Golem|DMR|2 -1 Tormod's Crypt|DMR|2 -1 Triskelion|DMR|2 -1 Umbilicus|DMR|2 -1 Urza's Blueprints|DMR|2 -1 Urza's Incubator|DMR|2 -1 Clifftop Retreat|DMR|2 -1 Dark Depths|DMR|2 -1 Gemstone Mine|DMR|2 -1 Hinterland Harbor|DMR|2 -1 Isolated Chapel|DMR|2 -1 Maze of Ith|DMR|2 -1 Mishra's Factory|DMR|2 -1 Sulfur Falls|DMR|2 -1 Woodland Cemetery|DMR|2 - [tokens] b_2_1_cat b_2_2_zombie diff --git a/forge-gui/res/editions/Dominaria United.txt b/forge-gui/res/editions/Dominaria United.txt index 76abb6c4c9e..3821424cb9f 100644 --- a/forge-gui/res/editions/Dominaria United.txt +++ b/forge-gui/res/editions/Dominaria United.txt @@ -300,11 +300,6 @@ ScryfallCode=DMU 284 R Tyrannical Pitlord @Donato Giancola 285 R Ragefire Hellkite @Xavier Ribeiro 286 R Briar Hydra @Caio Monteiro -423 R Serra Redeemer @Joshua Raphael -424 R Cosmic Epiphany @Eli Minaya -425 R Tyrannical Pitlord @Donato Giancola -426 R Ragefire Hellkite @Xavier Ribeiro -427 R Briar Hydra @Caio Monteiro [showcase] 287 R Danitha, Benalia's Hope @Barbara Rosiak @@ -447,6 +442,11 @@ ScryfallCode=DMU 420 R Urborg Lhurgoyf @Andrey Kuzinskiy 421 R Plaza of Heroes @Gabor Szikszai 422 R Thran Portal @Sarah Finnigan +423 R Serra Redeemer @Joshua Raphael +424 R Cosmic Epiphany @Eli Minaya +425 R Tyrannical Pitlord @Donato Giancola +426 R Ragefire Hellkite @Xavier Ribeiro +427 R Briar Hydra @Caio Monteiro [buy a box] 428 R Llanowar Loamspeaker @Volkan Baǵa diff --git a/forge-gui/res/editions/Dominaria.txt b/forge-gui/res/editions/Dominaria.txt index 4bb4d649760..89efc848197 100644 --- a/forge-gui/res/editions/Dominaria.txt +++ b/forge-gui/res/editions/Dominaria.txt @@ -4,7 +4,7 @@ Date=2018-04-27 Name=Dominaria Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("DOM Planeswalker Decks and Additional Promo"), 3 Uncommon:!fromSheet("DOM Planeswalker Decks and Additional Promo"), 1 RareMythic:!fromSheet("DOM Planeswalker Decks and Additional Promo"), 1 BasicLand DOM +Booster=10 Common:fromSheet("DOM cards"), 3 Uncommon:fromSheet("DOM cards"), 1 RareMythic:fromSheet("DOM cards"), 1 BasicLand DOM BoosterMustContain=Legendary Creature FatPack=10 FatPackExtraSlots=80 BasicLands @@ -280,6 +280,8 @@ ScryfallCode=DOM 267 L Forest @Titus Lunter 268 L Forest @Dimitar Marinski 269 L Forest @Mark Poole + +[precon product] 270 M Teferi, Timebender @Zack Stella 271 C Temporal Machinations @Zack Stella 272 R Niambi, Faithful Healer @Greg Opalinski @@ -290,6 +292,8 @@ ScryfallCode=DOM 277 U Karplusan Hound @Viktor Titov 278 C Pyromantic Pilgrim @Magali Villeneuve 279 C Timber Gorge @YW Tang + +[buy a box] 280 R Firesong and Sunspeaker @Zoltan Boros [tokens] diff --git a/forge-gui/res/editions/Double Masters 2022.txt b/forge-gui/res/editions/Double Masters 2022.txt index 0652342c305..f9e6baf725c 100644 --- a/forge-gui/res/editions/Double Masters 2022.txt +++ b/forge-gui/res/editions/Double Masters 2022.txt @@ -426,7 +426,7 @@ ScryfallCode=2X2 411 U Selesnya Sanctuary @Ron Spears 412 U Simic Growth Chamber @Pete Venters -[showcase] +[etched] 413 M Emrakul, the Aeons Torn @Mark Tedin 414 M Kozilek, Butcher of Truth @Michael Komarck 415 M Ulamog, the Infinite Gyre @Aleksi Briclot diff --git a/forge-gui/res/editions/Double Masters.txt b/forge-gui/res/editions/Double Masters.txt index f2eacc39340..5345abc2b90 100644 --- a/forge-gui/res/editions/Double Masters.txt +++ b/forge-gui/res/editions/Double Masters.txt @@ -4,7 +4,7 @@ Date=2020-08-07 Name=Double Masters Type=Reprint BoosterCovers=3 -Booster=8 Common, 3 Uncommon, 2 RareMythic, 2 fromSheet("2XM Foils") +Booster=8 Common:fromsheet("2XM cards"), 3 Uncommon:fromsheet("2XM cards"), 2 RareMythic:fromsheet("2XM cards"), 2 fromSheet("2XM Foils") Foil=NotSupported DoublePick=FirstPick ChaosDraftThemes=MASTERS_SET @@ -343,6 +343,8 @@ ScryfallCode=2XM 330 C Urza's Power Plant @Brian Snõddy 331 C Urza's Tower @Brian Snõddy 332 R Wooded Bastion @Christopher Moeller + +[borderless] 333 M Karn Liberated @Mark Tedin 334 M Jace, the Mind Sculptor @Jason Chan 335 M Avacyn, Angel of Hope @Randy Vargas @@ -383,6 +385,8 @@ ScryfallCode=2XM 370 R Urza's Mine @Mark Tedin 371 R Urza's Power Plant @Mark Tedin 372 R Urza's Tower @Mark Tedin + +[buy a box] 373 L Plains @John Avon 374 L Plains @Noah Bradley 375 L Island @John Avon diff --git a/forge-gui/res/editions/Guilds of Ravnica.txt b/forge-gui/res/editions/Guilds of Ravnica.txt index a3475d24e0a..22b2a69218f 100644 --- a/forge-gui/res/editions/Guilds of Ravnica.txt +++ b/forge-gui/res/editions/Guilds of Ravnica.txt @@ -5,7 +5,7 @@ Name=Guilds of Ravnica Code2=GRN Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("GRN Secret Cards"), 3 Uncommon:!fromSheet("GRN Secret Cards"), 1 RareMythic:!fromSheet("GRN Secret Cards"), 1 fromSheet("GRN Lands") +Booster=10 Common:!fromSheet("GRN lands"):fromSheet("GRN cards"), 3 Uncommon:fromSheet("GRN cards"), 1 RareMythic:fromSheet("GRN cards"), 1 fromSheet("GRN lands") AdditionalSetUnlockedInQuest=GK1 FatPack=10 FatPackExtraSlots=80 BasicLands @@ -272,6 +272,8 @@ ScryfallCode=GRN 257 R Steam Vents @Jonas De Ro 258 R Temple Garden @Titus Lunter 259 R Watery Grave @Cliff Childs + +[precon product] 260 L Plains @Richard Wright 261 L Island @Richard Wright 262 L Swamp @Richard Wright @@ -285,8 +287,22 @@ ScryfallCode=GRN 270 C Kraul Raider @Ben Wootten 271 U Attendant of Vraska @Magali Villeneuve 272 R Vraska's Stoneglare @Yongjae Choi + +[buy a box] 273 M Impervious Greatwurm @Simon Dominic +[lands] +Golgari Guildgate|GRN|1 +Golgari Guildgate|GRN|2 +Izzet Guildgate|GRN|1 +Izzet Guildgate|GRN|2 +Selesnya Guildgate|GRN|1 +Selesnya Guildgate|GRN|2 +Dimir Guildgate|GRN|1 +Dimir Guildgate|GRN|2 +Boros Guildgate|GRN|1 +Boros Guildgate|GRN|2 + [tokens] w_4_4_angel_flying_vigilance w_1_1_soldier_lifelink diff --git a/forge-gui/res/editions/Hour of Devastation.txt b/forge-gui/res/editions/Hour of Devastation.txt index c48e663c645..ddc95416c2c 100644 --- a/forge-gui/res/editions/Hour of Devastation.txt +++ b/forge-gui/res/editions/Hour of Devastation.txt @@ -5,8 +5,8 @@ Name=Hour of Devastation Code2=HOU Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("HOU Planeswalker Decks and Toolkit"), 3 Uncommon:!fromSheet("HOU Planeswalker Decks and Toolkit"), 1 RareMythic:!fromSheet("HOU Planeswalker Decks and Toolkit"), 1 BasicLand -AdditionalSheetForFoils=fromSheet("MPS Hour of Devastation Invocations") +Booster=10 Common:fromSheet("HOU cards"), 3 Uncommon:fromSheet("HOU cards"), 1 RareMythic:fromSheet("HOU cards"), 1 BasicLand +AdditionalSheetForFoils=fromSheet("MPS_AKH special slot") FatPack=10 FatPackExtraSlots=80 BasicLands AdditionalSetUnlockedInQuest=MPS_AKH @@ -214,6 +214,8 @@ ScryfallCode=HOU 197 L Mountain @Kev Walker 198 L Forest @Titus Lunter 199 L Forest @Mark Poole + +[precon product] 200 M Nissa, Genesis Mage @Chris Rallis 201 U Avid Reclaimer @Josu Hernaiz 202 C Brambleweft Behemoth @Florian de Gesincourt diff --git a/forge-gui/res/editions/Innistrad Crimson Vow.txt b/forge-gui/res/editions/Innistrad Crimson Vow.txt index 37ef374a9d7..98ed149c709 100644 --- a/forge-gui/res/editions/Innistrad Crimson Vow.txt +++ b/forge-gui/res/editions/Innistrad Crimson Vow.txt @@ -412,6 +412,8 @@ Prerelease=6 Boosters, 1 RareMythic+ 395 R Dollhouse of Horrors @Muhammad Firdaus 396 R Investigator's Journal @Yeong-Hao Han 397 R Voldaren Estate @Richard Wright + +[bundle] 398 L Plains @Sam White 399 L Island @Sam White 400 L Swamp @Sam White diff --git a/forge-gui/res/editions/Innistrad Midnight Hunt.txt b/forge-gui/res/editions/Innistrad Midnight Hunt.txt index 691a18fa7e9..1b191b4a281 100644 --- a/forge-gui/res/editions/Innistrad Midnight Hunt.txt +++ b/forge-gui/res/editions/Innistrad Midnight Hunt.txt @@ -66,7 +66,6 @@ Prerelease=6 Boosters, 1 RareMythic+ 55 C Galedrifter @Daniel Ljunggren 56 C Geistwave @Olena Richards 57 R Grafted Identity @Manuel Castañón -57† R Grafted Identity @Manuel Castañón 58 C Larder Zombie @E. M. Gist 59 M Lier, Disciple of the Drowned @Ekaterina Burmak 60 C Locked in the Cemetery @Tran Nguyen @@ -288,6 +287,16 @@ Prerelease=6 Boosters, 1 RareMythic+ 276 L Forest @Alayna Danner 277 L Forest @Dan Mumford +[alternate art] +57† R Grafted Identity @Manuel Castañón + +[precon product] +380 L Plains @Andreas Rocha +381 L Island @Andreas Rocha +382 L Swamp @Kasia 'Kafis' Zielińska +383 L Mountain @Muhammad Firdaus +384 L Forest @Andreas Rocha + [borderless] 278 M Wrenn and Seven @Bram Sels 279 M Arlinn, the Pack's Hope @Eric Deschamps @@ -395,11 +404,6 @@ Prerelease=6 Boosters, 1 RareMythic+ 377 R The Celestus @Jonas De Ro 378 R Pithing Needle @Ovidio Cartagena 379 M Hostile Hostel @Daniel Ljunggren -380 L Plains @Andreas Rocha -381 L Island @Andreas Rocha -382 L Swamp @Kasia 'Kafis' Zielińska -383 L Mountain @Muhammad Firdaus -384 L Forest @Andreas Rocha [buy a box] 385 R Champion of the Perished @Daarken diff --git a/forge-gui/res/editions/Kamigawa Neon Dynasty.txt b/forge-gui/res/editions/Kamigawa Neon Dynasty.txt index df6c9b3aa5e..f6a58630f71 100644 --- a/forge-gui/res/editions/Kamigawa Neon Dynasty.txt +++ b/forge-gui/res/editions/Kamigawa Neon Dynasty.txt @@ -431,6 +431,8 @@ ScryfallCode=NEO 403 R Mirror Box @Mid 404 R Reckoner Bankbuster @Yoshiya 405 R Surgehacker Mech @Inuchiyo Meimaru + +[etched] 417 R Farewell @Fuzichoco 418 M The Wandering Emperor @Hisashi Momose 419 M Tezzeret, Betrayer of Flesh @Maekawa Yuichi diff --git a/forge-gui/res/editions/Khans of Tarkir.txt b/forge-gui/res/editions/Khans of Tarkir.txt index b3c441c332f..c9ef690ab6f 100644 --- a/forge-gui/res/editions/Khans of Tarkir.txt +++ b/forge-gui/res/editions/Khans of Tarkir.txt @@ -5,7 +5,7 @@ Name=Khans of Tarkir Code2=KTK Type=Expansion BoosterCovers=5 -Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand KTK +Booster=10 Common:fromsheet("KTK cards"), 3 Uncommon:fromsheet("KTK cards"), 1 RareMythic:fromsheet("KTK cards"), 1 BasicLand KTK FatPack=9 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=GRAVEYARD_MATTERS diff --git a/forge-gui/res/editions/Magic 2015.txt b/forge-gui/res/editions/Magic 2015.txt index ab0dd41160a..d763442489e 100644 --- a/forge-gui/res/editions/Magic 2015.txt +++ b/forge-gui/res/editions/Magic 2015.txt @@ -5,7 +5,7 @@ Name=Magic 2015 Code2=M15 Type=Core BoosterCovers=5 -Booster=10 Common:!fromSheet("M15 Sample Cards"), 3 Uncommon:!fromSheet("M15 Sample Cards"), 1 RareMythic:!fromSheet("M15 Sample Cards"), 1 BasicLand +Booster=10 Common:fromSheet("M15 cards"), 3 Uncommon:fromSheet("M15 cards"), 1 RareMythic:fromSheet("M15 cards"), 1 BasicLand FatPack=9 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=CORE_SET @@ -281,6 +281,8 @@ ScryfallCode=M15 267 L Forest @Steven Belledin 268 L Forest @Noah Bradley 269 L Forest @Jonas De Ro + +[precon product] 270 R Aegis Angel @Aleksi Briclot 271 C Divine Verdict @Kev Walker 272 C Inspired Charge @Wayne Reynolds diff --git a/forge-gui/res/editions/Magic 2021.txt b/forge-gui/res/editions/Magic 2021.txt index 1c17ce37397..38e70df02ea 100644 --- a/forge-gui/res/editions/Magic 2021.txt +++ b/forge-gui/res/editions/Magic 2021.txt @@ -302,6 +302,12 @@ ScryfallCode=M21 282 M Liliana, Waker of the Dead @Magali Villeneuve 283 M Chandra, Heart of Fire @Jason Rainville 284 M Garruk, Unleashed @Cristi Balanescu +314 R Containment Priest @Jesper Ejsing +315 M Grim Tutor @Antonio José Manzanedo +316 M Massacre Wurm @Kekai Kotaki +317 R Cultivate @Billy Christian +318 R Scavenging Ooze @Sam Rowan +319 R Solemn Simulacrum @Joseph Meehan [showcase] 285 M Ugin, the Spirit Dragon @Raymond Swanland @@ -333,12 +339,6 @@ ScryfallCode=M21 311 L Swamp @Jonas De Ro 312 L Mountain @Jonas De Ro 313 L Forest @Jonas De Ro -314 R Containment Priest @Jesper Ejsing -315 M Grim Tutor @Antonio José Manzanedo -316 M Massacre Wurm @Kekai Kotaki -317 R Cultivate @Billy Christian -318 R Scavenging Ooze @Sam Rowan -319 R Solemn Simulacrum @Joseph Meehan [precon product] 320 M Basri, Devoted Paladin @Jason Rainville diff --git a/forge-gui/res/editions/Magic Origins.txt b/forge-gui/res/editions/Magic Origins.txt index 305820c21e6..58a9879c2b3 100644 --- a/forge-gui/res/editions/Magic Origins.txt +++ b/forge-gui/res/editions/Magic Origins.txt @@ -5,7 +5,7 @@ Name=Magic Origins Code2=ORI Type=Core BoosterCovers=5 -Booster=10 Common:!fromSheet("ORI Sample Cards"), 3 Uncommon:!fromSheet("ORI Sample Cards"), 1 RareMythic:!fromSheet("ORI Sample Cards"), 1 BasicLand +Booster=10 Common:fromSheet("ORI cards"), 3 Uncommon:fromSheet("ORI cards"), 1 RareMythic:fromSheet("ORI cards"), 1 BasicLand FatPack=9 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=CORE_SET @@ -284,6 +284,8 @@ ScryfallCode=ORI 270 L Forest @Jonas De Ro 271 L Forest @Jonas De Ro 272 L Forest @Vincent Proce + +[precon product] 273 R Aegis Angel @Aleksi Briclot 274 C Divine Verdict @Kev Walker 275 C Eagle of the Watch @Scott Murphy diff --git a/forge-gui/res/editions/March of the Machine The Aftermath.txt b/forge-gui/res/editions/March of the Machine The Aftermath.txt index d3e5ca37c72..7a71997bf4d 100644 --- a/forge-gui/res/editions/March of the Machine The Aftermath.txt +++ b/forge-gui/res/editions/March of the Machine The Aftermath.txt @@ -57,6 +57,8 @@ BoosterBox=0 48 M Tyvar the Bellicose @Jarel Threat 49 M Karn, Legacy Reforged @Grzegorz Rutkowski 50 R Drannith Ruins @Martin de Diego Sádaba + +[showcase] 51 U Coppercoat Vanguard @Steve Ellis 52 R Deification @Jason A. Engle 53 U Harnessed Snubhorn @Jody Clark @@ -107,6 +109,8 @@ BoosterBox=0 98 M Tyvar the Bellicose @Richard Luong 99 M Karn, Legacy Reforged @Daren Bader 100 R Drannith Ruins @Steve Ellis + +[etched] 101 U Coppercoat Vanguard @Bruno Biazotto 102 R Deification @Maxime Minard 103 U Harnessed Snubhorn @Quintin Gleim @@ -157,6 +161,8 @@ BoosterBox=0 148 M Tyvar the Bellicose @Jarel Threat 149 M Karn, Legacy Reforged @Grzegorz Rutkowski 150 R Drannith Ruins @Martin de Diego Sádaba + +[extended art] 151 R Deification @Maxime Minard 152 R Metropolis Reformer @Ryan Pancoast 153 R Spark Rupture @Viko Menezes @@ -192,6 +198,8 @@ BoosterBox=0 183 M Tyvar the Bellicose @Jarel Threat 184 M Karn, Legacy Reforged @Grzegorz Rutkowski 185 R Drannith Ruins @Martin de Diego Sádaba + +[etched] 186 U Coppercoat Vanguard @Steve Ellis 187 R Deification @Jason A. Engle 188 U Harnessed Snubhorn @Jody Clark @@ -235,5 +243,9 @@ BoosterBox=0 226 R Sigarda, Font of Blessings @Sami Makkonen 227 M Tyvar the Bellicose @Richard Luong 228 R Drannith Ruins @Steve Ellis + +[promo] 229 R Spark Rupture @Scott M. Fischer + +[buy a box] 230 R Jolrael, Voice of Zhalfir @Ernanda Souza diff --git a/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt b/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt index d5f5f7ba2b0..9d87ce5010d 100644 --- a/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt +++ b/forge-gui/res/editions/Masterpiece Series - Amonkhet.txt @@ -5,6 +5,7 @@ Name=Masterpiece Series - Amonkhet Type=Collector_Edition ScryfallCode=mp2 +#Bonus cards for AKH Amonkhet [cards] 1 S Austere Command @Richard Wright 2 S Aven Mindcensor @Jose Cabrera @@ -36,6 +37,9 @@ ScryfallCode=mp2 28 S Rhonas the Indomitable @Jack Wang 29 S Maelstrom Pulse @Igor Kieryluk 30 S Vindicate @Igor Kieryluk + +#Bonus cards for HOU Hour of Devastation +[special slot] 31 S Armageddon @Florian de Gesincourt 32 S Capsize @Cliff Childs 33 S Forbid @Richard Wright diff --git a/forge-gui/res/editions/Masters Edition IV.txt b/forge-gui/res/editions/Masters Edition IV.txt index 354f35f6f65..344ae3b5436 100644 --- a/forge-gui/res/editions/Masters Edition IV.txt +++ b/forge-gui/res/editions/Masters Edition IV.txt @@ -5,7 +5,7 @@ Name=Masters Edition IV Code2=ME4 Type=Online BoosterCovers=1 -Booster=10 Common, 3 Uncommon, 1 Rare, 1 fromSheet("ME4 UrzaLands") +Booster=10 Common, 3 Uncommon, 1 Rare, 1 BasicLand ScryfallCode=ME4 [cards] @@ -279,20 +279,6 @@ ScryfallCode=ME4 259d L Urza's Tower @Mark Poole 260 R Volcanic Island @Brian Snõddy -[UrzaLands] -1 Urza's Tower|ME4|1 -1 Urza's Tower|ME4|2 -1 Urza's Tower|ME4|3 -1 Urza's Tower|ME4|4 -1 Urza's Mine|ME4|1 -1 Urza's Mine|ME4|2 -1 Urza's Mine|ME4|3 -1 Urza's Mine|ME4|4 -1 Urza's Power Plant|ME4|1 -1 Urza's Power Plant|ME4|2 -1 Urza's Power Plant|ME4|3 -1 Urza's Power Plant|ME4|4 - [tokens] r_1_1_goblin c_1_1_a_tetravite_flying_noenchant diff --git a/forge-gui/res/editions/Modern Horizons 2.txt b/forge-gui/res/editions/Modern Horizons 2.txt index a67b2715c2d..ba8171f53fa 100644 --- a/forge-gui/res/editions/Modern Horizons 2.txt +++ b/forge-gui/res/editions/Modern Horizons 2.txt @@ -318,7 +318,7 @@ ScryfallCode=MH2 302 U Mishra's Factory @Scott Chou 303 R Riptide Laboratory @John Avon -[alternate art] +[borderless] 304 M Dakkon, Shadow Slayer @Jake Murray 305 M Geyadrone Dihada @Aleksi Briclot 306 M Grist, the Hunger Tide @Victor Adame Minguez @@ -399,7 +399,7 @@ ScryfallCode=MH2 379 M Kaldra Compleat @Vincent Proce 380 R Urza's Saga @Titus Lunter -[alternate frame] +[retro frame] 381 C Blacksmith's Skill @Jason A. Engle 382 C Marble Gargoyle @Drew Tucker 383 R Out of Time @Tobias Kwan @@ -503,7 +503,7 @@ ScryfallCode=MH2 479 R Verdant Catacombs @Vance Kovacs 480 R Yavimaya, Cradle of Growth @Sarah Finnigan -[promo] +[bundle] 481 L Plains @Eric Peterson 482 L Plains @Alan Pollack 483 L Island @Donato Giancola @@ -518,7 +518,7 @@ ScryfallCode=MH2 [buy a box] 491 M Sanctum Prelate @Michael C. Hayes -[bundle] +[promo] 492 R Yusri, Fortune's Flame @Evyn Fong [Lands] diff --git a/forge-gui/res/editions/Modern Horizons 3.txt b/forge-gui/res/editions/Modern Horizons 3.txt index 229b9748a13..c3d07e28847 100644 --- a/forge-gui/res/editions/Modern Horizons 3.txt +++ b/forge-gui/res/editions/Modern Horizons 3.txt @@ -10,6 +10,7 @@ BoosterCovers=3 ChaosDraftThemes=MASTERS_SET ScryfallCode=MH3 +#Numbers from https://mtgscribe.com/2024/05/22/play-booster-fact-sheet-modern-horizons-3/ [Common] Base=Common:fromSheet("MH3 cards") @@ -22,8 +23,8 @@ Base=Uncommon:fromSheet("MH3 cards") [RareMythic] Base=RareMythic:fromSheet("MH3 cards") -Replace=.021F fromSheet("MH3 alternate frame") -Replace=.051F fromSheet("MH3 borderless") +Replace=.021F RareMythic:fromSheet("MH3 retro frame") +Replace=.051F RareMythic:fromSheet("MH3 borderless") [Common-Land] Base=Common:fromSheet("MH3 cards") @@ -36,16 +37,16 @@ Replace=.067F fromSheet("MH3 full art")+ Base=Uncommon:fromSheet("MH3 new to modern") Replace=.213F Rare:fromSheet("MH3 new to modern") Replace=.023F Mythic:fromSheet("MH3 new to modern") -Replace=.008F fromSheet("MH3 borderless frame") -Replace=.003F fromSheet("MH3 borderless profile") -Replace=.001F fromSheet("MH3 alternate frame") +Replace=.011F RareMythic:fromSheet("MH3 borderless") +Replace=.001F Mythic:fromSheet("MH3 borderless") +Replace=.002F RareMythic:fromSheet("MH3 retro frame") [Wildcard] Base=Common:fromSheet("MH3 cards") Replace=.417F Uncommon:fromSheet("MH3 cards") Replace=.078F RareMythic:fromSheet("MH3 cards") -Replace=.004F fromSheet("MH3 borderless frame") -Replace=.042F fromSheet("MH3 alternate frame") +Replace=.004F RareMythic:fromSheet("MH3 borderless") +Replace=.042F fromSheet("MH3 retro frame") Replace=.042F fromSheet("MH3 commanders") [cards] @@ -402,8 +403,6 @@ Replace=.042F fromSheet("MH3 commanders") 347 R Pearl Medallion @Olena Richards 348 R Ruby Medallion @Martina Pilcerova 349 R Sapphire Medallion @Ron Spears - -[borderless frame] 350 R Archway of Innovation @Sam Burley 351 R Arena of Glory @Piotr Dura 352 R Bloodstained Mire @Sean Vo @@ -425,8 +424,6 @@ Replace=.042F fromSheet("MH3 commanders") 368 R Laelia, the Blade Reforged @Tyler Walpole 369 M Eladamri, Korvecdal @Tyler Walpole 370 R Six @Ivan Shavrin - -[borderless profile] 371 M Arna Kennerüd, Skycaptain @Grant Griffin 372 M Breya, Etherium Shaper @Jack Hughes 373 R Genku, Future Shaper @Ivan Shavrin @@ -451,7 +448,7 @@ Replace=.042F fromSheet("MH3 commanders") 471 M Ral, Monsoon Mage @Borja Pindado 472 M Grist, Voracious Larva @Ron Spencer -[showcase] +[retro frame] 384 M Emrakul, the World Anew @Brent Hollowell 385 U It That Heralds the End @Alex Konstad 386 M Kozilek, the Broken Reality @Brent Hollowell @@ -503,6 +500,15 @@ Replace=.042F fromSheet("MH3 commanders") 432 R Kudo, King Among Bears @Ekaterina Burmak 433 R Psychic Frog @Pete Venters 434 R Rosheen, Roaring Prophet @Fang Xinyu +435 R Bloodstained Mire @Bruce Brenneise +436 R Flooded Strand @Alexander Forssberg +437 U Nesting Grounds @Yeong-Hao Han +438 R Polluted Delta @Chris Ostrowski +439 U Snow-Covered Wastes @Mark Poole +440 R Windswept Heath @Alexander Forssberg +441 R Wooded Foothills @Chris Ostrowski + +[etched] 497 L Plains @Volkan Baǵa 498 L Plains @Lius Lasahido 499 L Island @Alayna Danner @@ -528,15 +534,6 @@ Replace=.042F fromSheet("MH3 commanders") 519 U Solar Transformer @Mike Bierek 520 C Tranquil Landscape @Randy Gallegos 521 C Twisted Landscape @Piotr Dura - -[alternate frame] -435 R Bloodstained Mire @Bruce Brenneise -436 R Flooded Strand @Alexander Forssberg -437 U Nesting Grounds @Yeong-Hao Han -438 R Polluted Delta @Chris Ostrowski -439 U Snow-Covered Wastes @Mark Poole -440 R Windswept Heath @Alexander Forssberg -441 R Wooded Foothills @Chris Ostrowski 473 M Emrakul, the World Anew @Brent Hollowell 474 M Herigast, Erupting Nullkite @Lucas Graciano 475 M Kozilek, the Broken Reality @Brent Hollowell diff --git a/forge-gui/res/editions/Modern Horizons.txt b/forge-gui/res/editions/Modern Horizons.txt index b23b38581f6..d7d020ea26f 100644 --- a/forge-gui/res/editions/Modern Horizons.txt +++ b/forge-gui/res/editions/Modern Horizons.txt @@ -5,7 +5,7 @@ Name=Modern Horizons Code2=MH1 Type=Draft BoosterCovers=5 -Booster=10 Common:!fromSheet("MH1 Secret Cards"), 3 Uncommon:!fromSheet("MH1 Secret Cards"), 1 RareMythic:!fromSheet("MH1 Secret Cards"), 1 fromSheet("MH1 Lands") +Booster=10 Common:fromSheet("MH1 cards"), 3 Uncommon:fromSheet("MH1 cards"), 1 RareMythic:fromSheet("MH1 cards"), 1 BasicLand:fromSheet("MH1 cards") BoosterBox=24 ChaosDraftThemes=MASTERS_SET;GRAVEYARD_MATTERS ScryfallCode=MH1 @@ -265,6 +265,8 @@ ScryfallCode=MH1 252 L Snow-Covered Swamp @Titus Lunter 253 L Snow-Covered Mountain @Titus Lunter 254 L Snow-Covered Forest @Titus Lunter + +[buy a box] 255 R Flusterstorm @Chris Rallis [tokens] diff --git a/forge-gui/res/editions/Murders at Karlov Manor.txt b/forge-gui/res/editions/Murders at Karlov Manor.txt index e87bba11f92..2c98d646b3a 100644 --- a/forge-gui/res/editions/Murders at Karlov Manor.txt +++ b/forge-gui/res/editions/Murders at Karlov Manor.txt @@ -4,11 +4,33 @@ Date=2024-02-09 Name=Murders at Karlov Manor Type=Expansion ScryfallCode=MKM -Booster=7 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand, 1 Any, 1 Any+ -ChanceReplaceCommonWith=.125F fromsheet("MKM karlov surprise") -Prerelease=6 Boosters, 1 RareMythic+, 1 Any:fromsheet("MKM prerelease promo")+ +BoosterSlots=Common,Common-Guest,Uncommon,RareMythic,BasicLand,Wildcard,PrereleasePromo +Booster=6 Common, 1 Common-Guest, 3 Uncommon, 1 RareMythic, 1 BasicLand, 1 Wildcard, 1 Wildcard+ +Prerelease=6 Boosters, 1 RareMythic+, 1 PrereleasePromo+ BoosterBox=36 +[Common] +Base=Common:fromSheet("MKM cards") + +[Common-Guest] +Base=Common:fromSheet("MKM cards") +Replace=.125F fromSheet("MKM karlov surprise") + +[Uncommon] +Base=Uncommon:fromSheet("MKM cards") + +[RareMythic] +Base=RareMythic:fromSheet("MKM cards") + +[BasicLand] +Base=BasicLand:fromSheet("MKM cards") + +[Wildcard] +Base=Any:fromSheet("MKM cards") + +[PrereleasePromo] +Base=Any:fromsheet("MKM prerelease promo") + [cards] 1 U Case of the Shattered Pact @Peter Polach 2 U Absolving Lammasu @Izzy @@ -447,6 +469,8 @@ BoosterBox=36 426 U Lightning Helix @Eli Minaya 427 U No More Lies @Liiga Smilshkalne 428 R Axebane Ferox @Adam Volker + +[prerelease promo] 430 M Melek, Reforged Researcher @Andreas Zafiratos 431 M Tomik, Wielder of Law @Valera Lutfullina 432 M Voja, Jaws of the Conclave @Valera Lutfullina @@ -454,11 +478,6 @@ BoosterBox=36 [buy a box] 429 R Wojek Investigator @Greg Staples -[prerelease promo] -1 Melek, Reforged Researcher|MKM -1 Tomik, Wielder of Law|MKM -1 Voja, Jaws of the Conclave|MKM - [karlov surprise] 1 Baleful Mastery|PLIST 1 Bishop of the Bloodstained|PLIST diff --git a/forge-gui/res/editions/Oath of the Gatewatch.txt b/forge-gui/res/editions/Oath of the Gatewatch.txt index 005ed48174a..d77daed9188 100644 --- a/forge-gui/res/editions/Oath of the Gatewatch.txt +++ b/forge-gui/res/editions/Oath of the Gatewatch.txt @@ -8,7 +8,7 @@ BoosterCovers=4 Booster=10 Common, 3 Uncommon, 1 RareMythic, 1 BasicLand BFZ FatPack=9 FatPackExtraSlots=66 BasicLands BFZ, 14 name("Wastes") -AdditionalSheetForFoils=fromSheet("EXP Lands 2") +AdditionalSheetForFoils=fromSheet("EXP special slot") AdditionalSetUnlockedInQuest=EXP ScryfallCode=OGW @@ -196,9 +196,9 @@ ScryfallCode=OGW 181 C Unknown Shores @Jung Park 182 R Wandering Fumarole @Florian de Gesincourt 183 C Wastes @Jason Felix -183 C Wastes @Jason Felix -184 C Wastes @Raymond Swanland +183a C Wastes @Jason Felix 184 C Wastes @Raymond Swanland +184a C Wastes @Raymond Swanland [tokens] c_1_1_eldrazi_scion_sac diff --git a/forge-gui/res/editions/Outlaws of Thunder Junction.txt b/forge-gui/res/editions/Outlaws of Thunder Junction.txt index 0f4d9d99a09..2795a29e0f4 100644 --- a/forge-gui/res/editions/Outlaws of Thunder Junction.txt +++ b/forge-gui/res/editions/Outlaws of Thunder Junction.txt @@ -4,11 +4,37 @@ Date=2024-04-19 Name=Outlaws of Thunder Junction Type=Expansion ScryfallCode=OTJ -Booster=6 Common, 3 Uncommon, 1 RareMythic, 1 Land, 1 Any, 1 Any OTP, 1 Any+ -ChanceReplaceCommonWith=.20F fromsheet("OTJ outlaw list") +BoosterSlots=Common,Common-Guest,Uncommon,RareMythic,Land,Wildcard,WildcardOTP +Booster=5 Common, 1 Common-Guest, 3 Uncommon, 1 RareMythic, 1 Land, 1 Wildcard, 1 Wildcard+, 1 WildcardOTP Prerelease=6 Boosters, 1 RareMythic+ BoosterBox=36 +#Numbers from https://mtgscribe.com/2024/04/05/outlaws-of-thunder-junction-play-booster-fact-sheet/ +[Common] +Base=Common:fromSheet("OTJ cards") + +[Common-Guest] +Base=Common:fromSheet("OTJ cards") +Replace=.20F fromSheet("OTJ outlaw list") + +[Uncommon] +Base=Uncommon:fromSheet("OTJ cards") + +[RareMythic] +Base=RareMythic:fromSheet("OTJ cards") +Replace=.025F RareMythic:!fromSheet("OTJ outlaw list") + +[Wildcard] +Base=Any:!fromSheet("OTJ outlaw list") +Replace=.0833F RareMythic:fromSheet("OTJ cards") + +[WildcardOTP] +Base=Uncommon:fromSheet("OTP cards") +Replace=.3333F RareMythic:fromSheet("OTP cards") + +[Land] +Base=Land:fromSheet("OTJ cards") + [cards] 1 R Another Round @Darrell Riche 2 M Archangel of Tithes @Denys Tsiperko diff --git a/forge-gui/res/editions/Phyrexia All Will Be One.txt b/forge-gui/res/editions/Phyrexia All Will Be One.txt index de9b2b065e3..d7b9eda8240 100644 --- a/forge-gui/res/editions/Phyrexia All Will Be One.txt +++ b/forge-gui/res/editions/Phyrexia All Will Be One.txt @@ -281,6 +281,8 @@ ScryfallCode=ONE 269 L Swamp @Mark Riddick 270 L Mountain @Mark Riddick 271 L Forest @Mark Riddick + +[precon product] 272 L Plains @Sergey Glushakov 273 L Island @David Álvarez 274 L Swamp @Julian Kok Joon Wen @@ -293,11 +295,6 @@ ScryfallCode=ONE 406 R Kinzu of the Bleak Coven @Andreas Zafiratos 407 R Rhuk, Hexgold Nabber @Andrea De Dominicis 408 R Goliath Hatchery @Simon Dominic -409 R Mite Overseer @Néstor Ossandón Leal -410 R Serum Sovereign @Chris Rallis -411 R Kinzu of the Bleak Coven @Andreas Zafiratos -412 R Rhuk, Hexgold Nabber @Andrea De Dominicis -413 R Goliath Hatchery @Simon Dominic [showcase] 285 U Bladed Ambassador @Ravenna Tran @@ -377,6 +374,8 @@ ScryfallCode=ONE 369 L Forest @Alayna Danner 414 M Elesh Norn, Mother of Machines @Martina Fačková 415 M Elesh Norn, Mother of Machines @Junji Ito + +[etched] 417 U Bladed Ambassador @Ravenna Tran 418 M Elesh Norn, Mother of Machines @Martina Fačková 419 M Elesh Norn, Mother of Machines @Junji Ito @@ -489,6 +488,11 @@ ScryfallCode=ONE 401 R The Monumental Facade @Bruce Brenneise 402 R The Mycosynth Gardens @Andrew Mar 403 R The Seedcore @Kasia 'Kafis' Zielińska +409 R Mite Overseer @Néstor Ossandón Leal +410 R Serum Sovereign @Chris Rallis +411 R Kinzu of the Bleak Coven @Andreas Zafiratos +412 R Rhuk, Hexgold Nabber @Andrea De Dominicis +413 R Goliath Hatchery @Simon Dominic [buy a box] 284 R Green Sun's Twilight @Piotr Dura diff --git a/forge-gui/res/editions/Portal.txt b/forge-gui/res/editions/Portal.txt index 8b7a83d5710..1fbbe394094 100644 --- a/forge-gui/res/editions/Portal.txt +++ b/forge-gui/res/editions/Portal.txt @@ -16,7 +16,6 @@ ScryfallCode=POR 4 U Ardent Militia @Mike Raabe 5 R Armageddon @John Avon 6 C Armored Pegasus @Andrew Robinson -6d C Armored Pegasus @Andrew Robinson 7 R Blessed Reversal @Zina Saunders 8 R Blinding Light @John Coulthart 9 C Border Guard @Kev Walker @@ -41,7 +40,6 @@ ScryfallCode=POR 28 C Spotted Griffin @William Simpson 29 U Starlight @John Avon 30 U Starlit Angel @Rebecca Guay -30s U Starlit Angel @徐晓鸣 31 C Steadfastness @Kev Walker 32 R Stern Marshal @D. Alexander Gregory 33 R Temporary Truce @Mike Raabe @@ -50,7 +48,6 @@ ScryfallCode=POR 36 U Vengeance @Andrew Robinson 37 U Wall of Swords @Douglas Shuler 38 C Warrior's Charge @Ted Naifeh -38† C Warrior's Charge @Ted Naifeh 39 R Wrath of God @Mike Raabe 40 R Ancestral Memories @Dan Frazier 41 R Balance of Power @Adam Rex @@ -59,7 +56,6 @@ ScryfallCode=POR 44 C Cloak of Feathers @Rebecca Guay 45 R Cloud Dragon @John Avon 46 C Cloud Pirates @Phil Foglio -46d C Cloud Pirates @Phil Foglio 47 U Cloud Spirit @DiTerlizzi 48 U Command of Unsummoning @Phil Foglio 49 C Coral Eel @Una Fricker @@ -71,7 +67,6 @@ ScryfallCode=POR 55 U Flux @Ted Naifeh 56 C Giant Octopus @John Matson 57 C Horned Turtle @Adrian Smith -57s C Horned Turtle @Wang Yuqun 58 U Ingenious Thief @Dan Frazier 59 U Man-o'-War @Una Fricker 60 C Merfolk of the Pearl Trident @DiTerlizzi @@ -81,15 +76,11 @@ ScryfallCode=POR 64 U Personal Tutor @D. Alexander Gregory 65 R Phantom Warrior @Dan Frazier 66 R Prosperity @Phil Foglio -66s R Prosperity @李有良 67 C Snapping Drake @Christopher Rush -67d C Snapping Drake @Christopher Rush 68 C Sorcerous Sight @Kaja Foglio 69 C Storm Crow @Una Fricker -69d C Storm Crow @Una Fricker 70 C Symbol of Unsummoning @Adam Rex 71 R Taunt @Phil Foglio -71s R Taunt @杨钊 72 U Theft of Dreams @Adam Rex 73 R Thing from the Deep @Paolo Parente 74 C Tidal Surge @Douglas Shuler @@ -99,7 +90,6 @@ ScryfallCode=POR 78 U Withering Gaze @Scott M. Fischer 79 U Arrogant Vampire @Zina Saunders 80 U Assassin's Blade @John Matson -80s U Assassin's Blade @徐晓鸣 81 C Bog Imp @Christopher Rush 82 C Bog Raiders @Steve Luke 83 U Bog Wraith @Ted Naifeh @@ -113,11 +103,9 @@ ScryfallCode=POR 91 R Ebon Dragon @Donato Giancola 92 R Endless Cockroaches @Ron Spencer 93 C Feral Shadow @Colin MacNeil -93d C Feral Shadow @Colin MacNeil 94 R Final Strike @John Coulthart 95 U Gravedigger @Scott M. Fischer 96 C Hand of Death @John Coulthart -96† C Hand of Death @John Coulthart 97 C Howling Fury @Mike Dringenberg 98 R King's Assassin @Zina Saunders 99 R Mercenary Knight @Adrian Smith @@ -129,7 +117,6 @@ ScryfallCode=POR 105 C Python @Alan Rabinowitz 106 U Rain of Tears @Eric Peterson 107 C Raise Dead @Charles Gillespie -107s C Raise Dead @李尤松 108 R Serpent Assassin @Roger Raupp 109 C Serpent Warrior @Roger Raupp 110 C Skeletal Crocodile @Mike Dringenberg @@ -137,14 +124,10 @@ ScryfallCode=POR 112 C Soul Shred @Alan Rabinowitz 113 C Undying Beast @Steve Luke 114 U Vampiric Feast @D. Alexander Gregory -114s U Vampiric Feast @王峰 115 C Vampiric Touch @Zina Saunders 116 U Virtue's Ruin @Mike Dringenberg 117 R Wicked Pact @Adam Rex -117s R Wicked Pact @杨光恒 118 U Blaze @Gerry Grace -118† U Blaze @Gerry Grace -118s U Blaze @David A. Cherry 119 U Boiling Seas @Tom Wänerstrand 120 C Burning Cloak @Scott M. Fischer 121 C Craven Giant @Ron Spencer @@ -161,7 +144,6 @@ ScryfallCode=POR 132 C Highland Giant @Ron Spencer 133 C Hill Giant @Randy Gallegos 134 U Hulking Cyclops @Paolo Parente -134s U Hulking Cyclops @Lin Yan 135 C Hulking Goblin @Pete Venters 136 R Last Chance @Hannibal King 137 C Lava Axe @Adrian Smith @@ -173,7 +155,6 @@ ScryfallCode=POR 143 R Pyroclasm @John Matson 144 C Raging Cougar @Terese Nielsen 145 C Raging Goblin @Pete Venters -145† C Raging Goblin @Pete Venters 146 C Raging Minotaur @Scott M. Fischer 147 U Rain of Salt @Charles Gillespie 148 C Scorching Spear @Mike Raabe @@ -187,19 +168,14 @@ ScryfallCode=POR 156 R Winds of Change @Adam Rex 157 R Alluring Scent @Ted Naifeh 158 U Anaconda @Andrew Robinson -158† U Anaconda @Andrew Robinson 159 U Bee Sting @Phil Foglio 160 U Bull Hippo @Roger Raupp -160d U Bull Hippo @Roger Raupp 161 R Charging Rhino @Una Fricker 162 U Deep Wood @Paolo Parente 163 C Elite Cat Warrior @Eric Peterson -163† C Elite Cat Warrior @Eric Peterson 164 C Elven Cache @Rebecca Guay -164s C Elven Cache @张艺娜 165 C Elvish Ranger @DiTerlizzi 166 C Fruition @Steve Luke -166s C Fruition @Wang Yuqun 167 C Giant Spider @Randy Gallegos 168 C Gorilla Warrior @John Matson 169 C Grizzly Bears @Zina Saunders @@ -207,7 +183,6 @@ ScryfallCode=POR 171 C Jungle Lion @Janine Johnston 172 C Mobilize @Rebecca Guay 173 C Monstrous Growth @Dan Frazier -173† C Monstrous Growth @Dan Frazier 174 U Moon Sprite @Terese Nielsen 175 R Natural Order @Alan Rabinowitz 176 U Natural Spring @Janine Johnston @@ -266,3 +241,30 @@ ScryfallCode=POR 214s L Forest @李铁 215 L Forest @John Avon 215s L Forest @李铁 + +[alternate art] +6d C Armored Pegasus @Andrew Robinson +30s U Starlit Angel @徐晓鸣 +38† C Warrior's Charge @Ted Naifeh +46d C Cloud Pirates @Phil Foglio +57s C Horned Turtle @Wang Yuqun +66s R Prosperity @李有良 +67d C Snapping Drake @Christopher Rush +69d C Storm Crow @Una Fricker +71s R Taunt @杨钊 +80s U Assassin's Blade @徐晓鸣 +93d C Feral Shadow @Colin MacNeil +96† C Hand of Death @John Coulthart +107s C Raise Dead @李尤松 +114s U Vampiric Feast @王峰 +117s R Wicked Pact @杨光恒 +118† U Blaze @Gerry Grace +118s U Blaze @David A. Cherry +134s U Hulking Cyclops @Lin Yan +145† C Raging Goblin @Pete Venters +158† U Anaconda @Andrew Robinson +160d U Bull Hippo @Roger Raupp +163† C Elite Cat Warrior @Eric Peterson +164s C Elven Cache @张艺娜 +166s C Fruition @Wang Yuqun +173† C Monstrous Growth @Dan Frazier diff --git a/forge-gui/res/editions/Ravnica Allegiance.txt b/forge-gui/res/editions/Ravnica Allegiance.txt index d0c565e90a3..ba8f71d8089 100644 --- a/forge-gui/res/editions/Ravnica Allegiance.txt +++ b/forge-gui/res/editions/Ravnica Allegiance.txt @@ -5,7 +5,7 @@ Name=Ravnica Allegiance Code2=RNA Type=Expansion BoosterCovers=5 -Booster=10 Common:!fromSheet("RNA Secret Cards"), 3 Uncommon:!fromSheet("RNA Secret Cards"), 1 RareMythic:!fromSheet("RNA Secret Cards"), 1 fromSheet("RNA Lands") +Booster=10 Common:fromSheet("RNA cards"):!fromSheet("RNA Lands"), 3 Uncommon:fromSheet("RNA cards"), 1 RareMythic:fromSheet("RNA cards"), 1 fromSheet("RNA Lands") FatPack=10 FatPackExtraSlots=80 BasicLands ChaosDraftThemes=RAVNICA @@ -271,6 +271,8 @@ ScryfallCode=RNA 257 C Simic Guildgate @Adam Paquette 258 C Simic Guildgate @Adam Paquette 259 R Stomping Ground @James Paick + +[precon product] 260 L Plains @Titus Lunter 261 L Island @Eytan Zana 262 L Swamp @Adam Paquette @@ -284,8 +286,17 @@ ScryfallCode=RNA 270 C Ragefire @Randy Vargas 271 U Charging War Boar @Izzy 272 R Domri's Nodorog @Svetlin Velinov + +[buy a box] 273 M The Haunt of Hightower @Lius Lasahido +[Lands] +Azorius Guildgate|RNA +Gruul Guildgate|RNA +Orzhov Guildgate|RNA +Rakdos Guildgate|RNA +Simic Guildgate|RNA + [tokens] rg_4_4_beast_trample g_3_3_centaur diff --git a/forge-gui/res/editions/Ravnica Remastered.txt b/forge-gui/res/editions/Ravnica Remastered.txt index def70749f12..d35e155c6d5 100644 --- a/forge-gui/res/editions/Ravnica Remastered.txt +++ b/forge-gui/res/editions/Ravnica Remastered.txt @@ -341,7 +341,7 @@ ScryfallCode=RVR 444 M Domri Rade @Susumu Kuroi 445 M Ral Zarek @Fukuzo Katsura -[showcase] +[retro frame] 302 R Blazing Archon @Zoltan Boros & Gabor Szikszai 303 R Blind Obedience @Seb McKinnon 304 U Condemn @Daren Bader diff --git a/forge-gui/res/editions/Streets of New Capenna.txt b/forge-gui/res/editions/Streets of New Capenna.txt index 9bc7ae5706e..ffcb6a1de32 100644 --- a/forge-gui/res/editions/Streets of New Capenna.txt +++ b/forge-gui/res/editions/Streets of New Capenna.txt @@ -420,6 +420,8 @@ ScryfallCode=SNC 403 R Void Rend @Krharts 404 M Ziatora, the Incinerator @Scott M. Fischer 405 R Ziatora's Envoy @Olga Tereshenko + +[etched] 441 M Elspeth Resplendent @Krharts 442 R Giada, Font of Hope @Scott M. Fischer 443 M Sanctuary Warden @Julie Dillon diff --git a/forge-gui/res/editions/The Big Score.txt b/forge-gui/res/editions/The Big Score.txt index 3e7234983f0..ebe60453f6a 100644 --- a/forge-gui/res/editions/The Big Score.txt +++ b/forge-gui/res/editions/The Big Score.txt @@ -36,6 +36,8 @@ ScryfallCode=BIG 28 M Transmutation Font @Mark Poole 29 M Fomori Vault @Jonas De Ro 30 M Tarnation Vista @Alayna Danner + +[showcase] 31 M Collector's Cage @Ben Hill 32 M Grand Abolisher @David Astruga 33 M Oltec Matterweaver @Inkognit @@ -71,6 +73,8 @@ ScryfallCode=BIG 63 M Lotus Ring @Ben Hill 64 M Sword of Wealth and Power @Artur Nakhodkin 65 M Tarnation Vista @Artur Nakhodkin + +[extended art] 66 M Collector's Cage @Bartek Fedyczak 67 M Grand Abolisher @Aurore Folny 68 M Oltec Matterweaver @Villarrte diff --git a/forge-gui/res/editions/The Brothers War.txt b/forge-gui/res/editions/The Brothers War.txt index 7de183f006d..4a565b0f3af 100644 --- a/forge-gui/res/editions/The Brothers War.txt +++ b/forge-gui/res/editions/The Brothers War.txt @@ -305,11 +305,6 @@ ScryfallCode=BRO 290 R Terror Ballista @Leon Tukker 291 R Artificer's Dragon @Leon Tukker 292 R Woodcaller Automaton @Ryan Pancoast -373 R Rescue Retriever @Jesper Ejsing -374 R Geology Enthusiast @Fajareka Setiawan -375 R Terror Ballista @Leon Tukker -376 R Artificer's Dragon @Leon Tukker -377 R Woodcaller Automaton @Ryan Pancoast [borderless] 293 M Teferi, Temporal Pilgrim @Cosmin Podar @@ -394,6 +389,11 @@ ScryfallCode=BRO 370 R Fortified Beachhead @Christian Dimitrov 371 R Hall of Tagsin @Christian Dimitrov 372 R Mishra's Foundry @Leon Tukker +373 R Rescue Retriever @Jesper Ejsing +374 R Geology Enthusiast @Fajareka Setiawan +375 R Terror Ballista @Leon Tukker +376 R Artificer's Dragon @Leon Tukker +377 R Woodcaller Automaton @Ryan Pancoast [buy a box] 378 R Mishra's Foundry @Leon Tukker diff --git a/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt b/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt index 4691ed5e74a..e7906598cb8 100644 --- a/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt +++ b/forge-gui/res/editions/The Lord of the Rings Tales of Middle-earth.txt @@ -298,21 +298,11 @@ ScryfallCode=LTR 284 R Ringwraiths @Warren Mahy 285 R Assault on Osgiliath @Warren Mahy 286 R Elanor Gardner @Torgeir Fjereide -383 R Saradoc, Master of Buckland @Sean Vo -384 R Elvish Mariner @Axel Sauerwald -385 R Ringwraiths @Warren Mahy -386 R Assault on Osgiliath @Warren Mahy -387 R Elanor Gardner @Torgeir Fjereide 824 R Eagle of Deliverance @Sidharth Chaturvedi 825 R Minas Tirith Garrison @Irina Nordsol 826 R Warg Rider @Pascal Quidault 827 R Riders of the Mark @Antonio José Manzanedo 828 R Mirkwood Channeler @Irina Nordsol -829 R Eagle of Deliverance @Sidharth Chaturvedi -830 R Minas Tirith Garrison @Irina Nordsol -831 R Warg Rider @Pascal Quidault -832 R Riders of the Mark @Antonio José Manzanedo -833 R Mirkwood Channeler @Irina Nordsol [bundle] 287 M Aragorn and Arwen, Wed @Magali Villeneuve @@ -805,6 +795,11 @@ ScryfallCode=LTR 380 M The One Ring @Veli Nyström 381 M Palantír of Orthanc @Tatiana Veryayskaya 382 R Phial of Galadriel @Andrea Piparo +383 R Saradoc, Master of Buckland @Sean Vo +384 R Elvish Mariner @Axel Sauerwald +385 R Ringwraiths @Warren Mahy +386 R Assault on Osgiliath @Warren Mahy +387 R Elanor Gardner @Torgeir Fjereide 388 R Frodo, Determined Hero @Magali Villeneuve 389 R Gandalf, White Rider @Ekaterina Burmak 390 R Gollum, Scheming Guide @Dmitry Burmak @@ -852,6 +847,11 @@ ScryfallCode=LTR 791 M The One Ring @Veli Nyström 792 M Palantír of Orthanc @Tatiana Veryayskaya 793 R Phial of Galadriel @Andrea Piparo +829 R Eagle of Deliverance @Sidharth Chaturvedi +830 R Minas Tirith Garrison @Irina Nordsol +831 R Warg Rider @Pascal Quidault +832 R Riders of the Mark @Antonio José Manzanedo +833 R Mirkwood Channeler @Irina Nordsol [buy a box] 398 R Trailblazer's Boots @Alexander Gering diff --git a/forge-gui/res/editions/Time Spiral Remastered.txt b/forge-gui/res/editions/Time Spiral Remastered.txt index c840d738798..4c13f0b3785 100644 --- a/forge-gui/res/editions/Time Spiral Remastered.txt +++ b/forge-gui/res/editions/Time Spiral Remastered.txt @@ -298,7 +298,7 @@ ScryfallCode=TSR 288 U Urza's Factory @Mark Tedin 289 M Vesuva @Zoltan Boros & Gabor Szikszai -[showcase] +[retro frame] 290 S Ajani's Pridemate @Svetlin Velinov 291 S Banishing Light @Willian Murai 292 S Containment Priest @John Stanko @@ -421,7 +421,7 @@ ScryfallCode=TSR 409 S Ramunap Ruins @Florian de Gesincourt 410 S Wastes @Raymond Swanland -[promo] +[buy a box] 411 R Lotus Bloom @Christopher Rush [tokens] diff --git a/forge-gui/res/editions/Unglued.txt b/forge-gui/res/editions/Unglued.txt index fb881546a66..440f8535341 100644 --- a/forge-gui/res/editions/Unglued.txt +++ b/forge-gui/res/editions/Unglued.txt @@ -30,7 +30,7 @@ ScryfallCode=UGL 18 C Clambassadors @Randy Elliott 19 C Clam-I-Am @Randy Elliott 20 C Clam Session @Randy Elliott -21 C Common Courtesy @Mike Raabe +21 U Common Courtesy @Mike Raabe 22 C Denied! @Quinton Hoover 23 C Double Take @Claymore J. Flapdoodle 24 C Fowl Play @Mark Poole diff --git a/forge-gui/res/editions/Unhinged.txt b/forge-gui/res/editions/Unhinged.txt index 1377b6c17cc..ecd990e8791 100644 --- a/forge-gui/res/editions/Unhinged.txt +++ b/forge-gui/res/editions/Unhinged.txt @@ -12,7 +12,7 @@ ScryfallCode=UNH [cards] 1 U Atinlay Igpay @Evkay Alkerway 2 C AWOL @Stephen Tappin -3 U Bosom Buddy @Dan Scott +3 U Bosom Buddy @Dan Murayama Scott 4 C Cardpecker @Richard Sardinha 5 C Cheap Ass @Randy Gallegos 6 C Circle of Protection: Art @Jim "Stop the Da Vinci Beatdown" Pavelec @@ -150,7 +150,7 @@ ScryfallCode=UNH 138 L Swamp @John Avon 139 L Mountain @John Avon 140 L Forest @John Avon -141 S Super Secret Tech @Dan Frazier +141 R Super Secret Tech @Dan Frazier [tokens] r_1_1_goblin diff --git a/forge-gui/res/editions/Unstable.txt b/forge-gui/res/editions/Unstable.txt index 23637ce69e2..1f0b8004b26 100644 --- a/forge-gui/res/editions/Unstable.txt +++ b/forge-gui/res/editions/Unstable.txt @@ -10,270 +10,270 @@ FoilAlwaysInCommonSlot=False ScryfallCode=UST [cards] -3a C Amateur Auteur -3b C Amateur Auteur -3c C Amateur Auteur -3d C Amateur Auteur -6 M Do-It-Yourself Seraph -7 U Gimme Five -8 C GO TO JAIL -9 U Half-Kitten, Half- -10 C Humming- -11 R Jackknight -12a U Knight of the Kitchen Sink $A -12b U Knight of the Kitchen Sink $B -12c U Knight of the Kitchen Sink $C -12d U Knight of the Kitchen Sink $D -12e U Knight of the Kitchen Sink $E -12f U Knight of the Kitchen Sink $F -13 U Knight of the Widget -14 U Midlife Upgrade -15 R Oddly Uneven -16 C Old Guard -17 C Ordinary Pony -18 U Rhino- -19 C Riveting Rigger -20 R Rules Lawyer -21 C Sacrifice Play -22 C Shaggy Camel -23 U Side Quest -24 C Success! -25 U Teacher's Pet -26 R Animate Library -27 C Blurry Beeble -28 C Chipper Chopper -29 R Clocknapper -30 C Crafty Octopus -31 U Crow Storm -32 C Defective Detective -33 U Five-Finger Discount -34 R Graveyard Busybody -35 U Half-Shark, Half- -36 R Incite Insight -37 U Kindly Cognician -38 C Magic Word -39 C Mer Man -40 U More or Less -41a C Novellamental -41b C Novellamental -41c C Novellamental -41d C Novellamental -42 C Numbing Jellyfish -43 U S.N.E.A.K. Dispatcher -44 U Socketed Sprocketer -45 C Spell Suck -46 U Spy Eye -47 U Suspicious Nanny -48 C Time Out -49a R Very Cryptic Command $A -49b R Very Cryptic Command $B -49c R Very Cryptic Command $C -49d R Very Cryptic Command $D -49e R Very Cryptic Command $E -49f R Very Cryptic Command $F -50 C Wall of Fortune -51 C Big Boa Constrictor -52 C capital offense -53 C Dirty Rat -54a C Extremely Slow Zombie -54b C Extremely Slow Zombie -54c C Extremely Slow Zombie -54d C Extremely Slow Zombie -55 C Finders, Keepers -56 R Hangman -57 C Hazmat Suit (Used) -58 C Hoisted Hireling -59 U Inhumaniac -60 R Masterful Ninja -61 U Ninja -62 U Old-Fashioned Vampire -63 R Over My Dead Bodies -64 U Overt Operative -65 U "Rumors of My Death..." -66 U Skull Saucer -67a U Sly Spy $A -67b U Sly Spy $B -67c U Sly Spy $C -67d U Sly Spy $D -67e U Sly Spy $E -67f U Sly Spy $F -68 C Snickering Squirrel -69 R Spike, Tournament Grinder -70 U Squirrel-Powered Scheme -71 C Steady-Handed Mook -72 C Stinging Scorpion -73 C Subcontract -74 M Summon the Pack -75 U Zombified -76 R The Big Idea -77 C Box of Free-Range Goblins -78 C Bumbling Pangolin -79 C Common Iguana -80 R The Countdown Is at One -81 C Feisty Stegosaurus -82a U Garbage Elemental $A -82b U Garbage Elemental $B -82c U Garbage Elemental $C -82d U Garbage Elemental $D -82e U Garbage Elemental $E -82f U Garbage Elemental $F -83 U Goblin Haberdasher -84 U Half-Orc, Half- -85 C Hammer Helper -86 U Hammer Jammer -87 U Hammerfest Boomtacular -88 M Infinity Elemental -89 C It That Gets Left Hanging -90 C Just Desserts -91 C Painiac -92 U Party Crasher -93 R Steamflogger Boss -94 R Steamflogger of the Month -95 U Steamflogger Temp -96 U Steamfloggery -97 U Super-Duper Death Ray -98a C Target Minotaur -98b C Target Minotaur -98c C Target Minotaur -98d C Target Minotaur -99 R Three-Headed Goblin -100 C Work a Double -101 C Wrench-Rigger -102 R As Luck Would Have It -103a C Beast in Show -103b C Beast in Show -103c C Beast in Show -103d C Beast in Show -104 U Chittering Doom -105 U Clever Combo -106 U Druid of the Sacred Beaker -107 C Eager Beaver -108 R Earl of Squirrel -109 U First Pick -110 U Ground Pounder -111 U Half-Squirrel, Half- -112 R Hydradoodle -113a R Ineffable Blessing $A -113b R Ineffable Blessing $B -113c R Ineffable Blessing $C -113d R Ineffable Blessing $D -113e R Ineffable Blessing $E -113f R Ineffable Blessing $F -114 C Joyride Rigger -115 U Monkey- -116 C Mother Kangaroo -117 C Multi-Headed -118 C Really Epic Punch -119 C Selfie Preservation -120 R Serpentine -121 U Shellephant -122 U Slaying Mantis -123 C Squirrel Dealer -124 U Steamflogger Service Rep -125 C Wild Crocodile -126 C Willing Test Subject -127 M Baron Von Count -128 R Better Than One -129 R Cramped Bunker -130 M Dr. Julius Jumblemorph -131 M The Grand Calcutron -132 R Grusilda, Monster Masher -133 R Hot Fix -134 M Ol' Buzzbark -135 M Phoebe, Head of S.N.E.A.K. -136 M Urza, Academy Headmaster -137 R X -138 R Mary O'Kill -139 R Angelic Rocket -140 U Border Guardian -141 U Buzzing Whack-a-Doodle -142 U Clock of DOOOOOOOOOOOOM! -143 U Cogmentor -144 U Contraption Cannon -145a C Curious Killbot -145b C Delighted Killbot -145c C Despondent Killbot -145d C Enraged Killbot -146 U Entirely Normal Armchair -147a R Everythingamajig $A -147b R Everythingamajig $B -147c R Everythingamajig $C -147d R Everythingamajig $D -147e R Everythingamajig $E -147f R Everythingamajig $F -148 C Gnome-Made Engine -149 R Handy Dandy Clone Machine -150 R Kindslaver -151 U Krark's Other Thumb -152 U Labro Bot -153 U Lobe Lobber -154 C Mad Science Fair Project -155 R Modular Monstrosity -156 U Proper Laboratory Attire -157 U Robo- -158 R Split Screen -159 U Staff of the Letter Magus -160 U Stamp of Approval -161 U Steam-Powered -162 U Steel Squirrel -163 M Sword of Dungeons & Dragons -164 C Voracious Vacuum -165a C Secret Base -165b C Secret Base -165c C Secret Base -165d C Secret Base -165e C Secret Base -166 R Watermarket -167 U Accessories to Murder -168 C Applied Aeronautics -169 U Arms Depot -170 C Auto-Key -171 M Bee-Bee Gun -172 C Boomflinger -173 C Buzz Buggy -174 R Deadly Poison Sampler -175 C Dictation Quillograph -176 U Dispatch Dispensary -177 C Division Table -178 U Dogsnail Engine -179 R Dual Doomsuits -180 R Duplication Device -181 M Faerie Aerie -182 U Genetic Recombinator -183 R Gift Horse -184 U Gnomeball Machine -185 R Goblin Slingshot -186 R Guest List -187 M Hard Hat Area -188 C Head Banger -189 R Hypnotic Swirly Disc -190 C Inflation Station -191 U Insufferable Syphon -192 U Jamming Device -193 C Lackey Recycler -194 C Mandatory Friendship Shackles -195 U Neural Network -196 R Oaken Power Suit -197 U Optical Optimizer -198 M Pet Project -199 C Quick-Stick Lick Trick -200 M Rapid Prototyper -201 R Record Store -202 R Refibrillator -203 C Sap Sucker -204 U Sundering Fork -205 U Targeting Rocket -206 U Thud-for-Duds -207 C Top-Secret Tunnel -208 C Tread Mill -209 U Turbo-Thwacking Auto-Hammer -210 C Twiddlestick Charger -211 U Widget Contraption -212 L Plains -213 L Island -214 L Swamp -215 L Mountain -216 L Forest +3a C Amateur Auteur @McLean Kendree +3b C Amateur Auteur @McLean Kendree +3c C Amateur Auteur @McLean Kendree +3d C Amateur Auteur @McLean Kendree +6 M Do-It-Yourself Seraph @David Sladek +7 U Gimme Five @Jesper Ejsing +8 C GO TO JAIL @Marco Teixeira +9 U Half-Kitten, Half- @Andrea Radeck +10 C Humming- @Mark Behm +11 R Jackknight @Ben Wootten +12a U Knight of the Kitchen Sink @Mark A. Nelson $A +12b U Knight of the Kitchen Sink @Mark A. Nelson $B +12c U Knight of the Kitchen Sink @Mark A. Nelson $C +12d U Knight of the Kitchen Sink @Mark A. Nelson $D +12e U Knight of the Kitchen Sink @Mark A. Nelson $E +12f U Knight of the Kitchen Sink @Mark A. Nelson $F +13 U Knight of the Widget @Emrah Elmasli +14 U Midlife Upgrade @Hector Ortiz +15 R Oddly Uneven @Ben Wootten +16 C Old Guard @David Sladek +17 C Ordinary Pony @Andrea Radeck +18 U Rhino- @YW Tang +19 C Riveting Rigger @Mark A. Nelson +20 R Rules Lawyer @Sean Murray +21 C Sacrifice Play @Matt Gaser +22 C Shaggy Camel @Kari Christensen +23 U Side Quest @Alex Konstad +24 C Success! @Andrea Radeck +25 U Teacher's Pet @Mark Behm +26 R Animate Library @Raymond Swanland +27 C Blurry Beeble @Jeff Miracola +28 C Chipper Chopper @Dmitry Burmak +29 R Clocknapper @Marco Teixeira +30 C Crafty Octopus @Mark Behm +31 U Crow Storm @YW Tang +32 C Defective Detective @Matt Dixon +33 U Five-Finger Discount @Milivoj Ćeran +34 R Graveyard Busybody @Bram Sels +35 U Half-Shark, Half- @Brynn Metheney +36 R Incite Insight @David Sladek +37 U Kindly Cognician @Mark Behm +38 C Magic Word @Carl Frank +39 C Mer Man @Mark Behm +40 U More or Less @Chris Seaman +41a C Novellamental @Tom Babbey +41b C Novellamental @Tom Babbey +41c C Novellamental @Tom Babbey +41d C Novellamental @Tom Babbey +42 C Numbing Jellyfish @Matt Dixon +43 U S.N.E.A.K. Dispatcher @John Thacker +44 U Socketed Sprocketer @David Sladek +45 C Spell Suck @Michael Phillippi +46 U Spy Eye @Ben Wootten +47 U Suspicious Nanny @Chris Seaman +48 C Time Out @Dave Allsop +49a R Very Cryptic Command @Wayne England $A +49b R Very Cryptic Command @Zoltan Boros $B +49c R Very Cryptic Command @Zoltan Boros $C +49d R Very Cryptic Command @Zoltan Boros $D +49e R Very Cryptic Command @Zoltan Boros $E +49f R Very Cryptic Command @Zoltan Boros $F +50 C Wall of Fortune @Tom Babbey +51 C Big Boa Constrictor @Kari Christensen +52 C capital offense @Matt Dixon +53 C Dirty Rat @Kari Christensen +54a C Extremely Slow Zombie @Emrah Elmasli +54b C Extremely Slow Zombie @Emrah Elmasli +54c C Extremely Slow Zombie @Emrah Elmasli +54d C Extremely Slow Zombie @Emrah Elmasli +55 C Finders, Keepers @Mark Behm +56 R Hangman @Alex Konstad +57 C Hazmat Suit (Used) @Michael Phillippi +58 C Hoisted Hireling @Alex Konstad +59 U Inhumaniac @Matt Dixon +60 R Masterful Ninja @Matt Gaser +61 U Ninja @David Sladek +62 U Old-Fashioned Vampire @Simon Dominic +63 R Over My Dead Bodies @Even Amundsen +64 U Overt Operative @Bram Sels +65 U "Rumors of My Death..." @Alex Konstad +66 U Skull Saucer @Mike Burns +67a U Sly Spy @Michael Phillippi $A +67b U Sly Spy @Michael Phillippi $B +67c U Sly Spy @Michael Phillippi $C +67d U Sly Spy @Michael Phillippi $D +67e U Sly Spy @Michael Phillippi $E +67f U Sly Spy @Michael Phillippi $F +68 C Snickering Squirrel @Michael Phillippi +69 R Spike, Tournament Grinder @Zoltan Boros +70 U Squirrel-Powered Scheme @Even Amundsen +71 C Steady-Handed Mook @Carl Frank +72 C Stinging Scorpion @YW Tang +73 C Subcontract @Hector Ortiz +74 M Summon the Pack @Matt Cavotta +75 U Zombified @Kev Walker +76 R The Big Idea @Bram Sels +77 C Box of Free-Range Goblins @Chris Seaman +78 C Bumbling Pangolin @YW Tang +79 C Common Iguana @Brynn Metheney +80 R The Countdown Is at One @Jesper Ejsing +81 C Feisty Stegosaurus @Kari Christensen +82a U Garbage Elemental @Hector Ortiz $A +82b U Garbage Elemental @Hector Ortiz $B +82c U Garbage Elemental @Hector Ortiz $C +82d U Garbage Elemental @Hector Ortiz $D +82e U Garbage Elemental @Hector Ortiz $E +82f U Garbage Elemental @Hector Ortiz $F +83 U Goblin Haberdasher @Jesper Ejsing +84 U Half-Orc, Half- @Kev Walker +85 C Hammer Helper @Dave Allsop +86 U Hammer Jammer @Wayne Reynolds +87 U Hammerfest Boomtacular @Dave Allsop +88 M Infinity Elemental @Seb McKinnon +89 C It That Gets Left Hanging @Jesper Ejsing +90 C Just Desserts @Zoltan Boros +91 C Painiac @McLean Kendree +92 U Party Crasher @Mike Burns +93 R Steamflogger Boss @Warren Mahy +94 R Steamflogger of the Month @Warren Mahy +95 U Steamflogger Temp @Jeff Miracola +96 U Steamfloggery @Emrah Elmasli +97 U Super-Duper Death Ray @Even Amundsen +98a C Target Minotaur @Warren Mahy +98b C Target Minotaur @Warren Mahy +98c C Target Minotaur @Warren Mahy +98d C Target Minotaur @Warren Mahy +99 R Three-Headed Goblin @Mike Burns +100 C Work a Double @Carl Frank +101 C Wrench-Rigger @Jesper Ejsing +102 R As Luck Would Have It @Milivoj Ćeran +103a C Beast in Show @Mike Burns +103b C Beast in Show @Mike Burns +103c C Beast in Show @Mike Burns +103d C Beast in Show @Mike Burns +104 U Chittering Doom @Kari Christensen +105 U Clever Combo @Kev Walker +106 U Druid of the Sacred Beaker @Simon Dominic +107 C Eager Beaver @Andrea Radeck +108 R Earl of Squirrel @Milivoj Ćeran +109 U First Pick @John Thacker +110 C Ground Pounder @Warren Mahy +111 U Half-Squirrel, Half- @Andrea Radeck +112 R Hydradoodle @Mathias Kollros +113a R Ineffable Blessing @Milivoj Ćeran $A +113b R Ineffable Blessing @Milivoj Ćeran $B +113c R Ineffable Blessing @Milivoj Ćeran $C +113d R Ineffable Blessing @Milivoj Ćeran $D +113e R Ineffable Blessing @Milivoj Ćeran $E +113f R Ineffable Blessing @Milivoj Ćeran $F +114 C Joyride Rigger @Wayne Reynolds +115 U Monkey- @Andrea Radeck +116 C Mother Kangaroo @Andrea Radeck +117 C Multi-Headed @YW Tang +118 C Really Epic Punch @Ben Wootten +119 C Selfie Preservation @Chris Seaman +120 R Serpentine @Kari Christensen +121 U Shellephant @Hector Ortiz +122 U Slaying Mantis @Ben Wootten +123 C Squirrel Dealer @Bram Sels +124 U Steamflogger Service Rep @Warren Mahy +125 C Wild Crocodile @Brynn Metheney +126 C Willing Test Subject @Dmitry Burmak +127 M Baron Von Count @Jesper Ejsing +128 R Better Than One @Alex Konstad +129 R Cramped Bunker @Ben Wootten +130 M Dr. Julius Jumblemorph @Simon Dominic +131 M The Grand Calcutron @Sean Murray +132 R Grusilda, Monster Masher @Mathias Kollros +133 R Hot Fix @David Sladek +134 M Ol' Buzzbark @Wayne Reynolds +135 M Phoebe, Head of S.N.E.A.K. @Ralph Horsley +136 M Urza, Academy Headmaster @Terese Nielsen +137 R X @Dmitry Burmak +138 R Mary O'Kill @Simon Dominic +139 R Angelic Rocket @Carl Critchlow +140 U Border Guardian @Chris Seaman +141 U Buzzing Whack-a-Doodle @Mark A. Nelson +142 U Clock of DOOOOOOOOOOOOM! @Tom Babbey +143 U Cogmentor @Matt Gaser +144 U Contraption Cannon @Mike Burns +145a C Curious Killbot @Alex Konstad +145b C Delighted Killbot @Alex Konstad +145c C Despondent Killbot @Alex Konstad +145d C Enraged Killbot @Alex Konstad +146 U Entirely Normal Armchair @Tom Babbey +147a R Everythingamajig @Chris Seaman $A +147b R Everythingamajig @Chris Seaman $B +147c R Everythingamajig @Chris Seaman $C +147d R Everythingamajig @Chris Seaman $D +147e R Everythingamajig @Chris Seaman $E +147f R Everythingamajig @Chris Seaman $F +148 C Gnome-Made Engine @Sean Murray +149 R Handy Dandy Clone Machine @Mike Burns +150 R Kindslaver @Zoltan Boros +151 U Krark's Other Thumb @Jeff Miracola +152 U Labro Bot @Carl Critchlow +153 U Lobe Lobber @Dmitry Burmak +154 C Mad Science Fair Project @Carl Frank +155 R Modular Monstrosity @Alex Konstad +156 U Proper Laboratory Attire @Tom Babbey +157 U Robo- @Matt Dixon +158 R Split Screen @Simon Dominic +159 U Staff of the Letter Magus @Daniel Ljunggren +160 U Stamp of Approval @Zoltan Boros +161 U Steam-Powered @Carl Critchlow +162 U Steel Squirrel @Carl Critchlow +163 M Sword of Dungeons & Dragons @Chris Rahn +164 C Voracious Vacuum @Matt Dixon +165a C Secret Base @John Thacker +165b C Secret Base @Matt Gaser +165c C Secret Base @Seb McKinnon +165d C Secret Base @Dave Allsop +165e C Secret Base @Simon Dominic +166 R Watermarket @Simon Dominic +167 U Accessories to Murder @Ralph Horsley +168 C Applied Aeronautics @Jason Felix +169 U Arms Depot @Chuck Lukacs +170 C Auto-Key @Jason Felix +171 M Bee-Bee Gun @Chuck Lukacs +172 C Boomflinger @Steve Prescott +173 C Buzz Buggy @Steve Prescott +174 R Deadly Poison Sampler @Ralph Horsley +175 C Dictation Quillograph @Ralph Horsley +176 U Dispatch Dispensary @Ralph Horsley +177 C Division Table @Franz Vohwinkel +178 U Dogsnail Engine @Chuck Lukacs +179 R Dual Doomsuits @Franz Vohwinkel +180 R Duplication Device @Jason Felix +181 M Faerie Aerie @Ralph Horsley +182 U Genetic Recombinator @Chuck Lukacs +183 R Gift Horse @Steve Prescott +184 U Gnomeball Machine @Jason Felix +185 R Goblin Slingshot @Steve Prescott +186 R Guest List @Franz Vohwinkel +187 M Hard Hat Area @Steve Prescott +188 C Head Banger @Steve Prescott +189 R Hypnotic Swirly Disc @Ralph Horsley +190 C Inflation Station @Chuck Lukacs +191 U Insufferable Syphon @Ralph Horsley +192 U Jamming Device @Franz Vohwinkel +193 C Lackey Recycler @Franz Vohwinkel +194 C Mandatory Friendship Shackles @Franz Vohwinkel +195 U Neural Network @Franz Vohwinkel +196 R Oaken Power Suit @Chuck Lukacs +197 U Optical Optimizer @Jason Felix +198 M Pet Project @Franz Vohwinkel +199 C Quick-Stick Lick Trick @Chuck Lukacs +200 M Rapid Prototyper @Jason Felix +201 R Record Store @Jason Felix +202 R Refibrillator @Chuck Lukacs +203 C Sap Sucker @Chuck Lukacs +204 U Sundering Fork @Franz Vohwinkel +205 U Targeting Rocket @Steve Prescott +206 U Thud-for-Duds @Steve Prescott +207 C Top-Secret Tunnel @Ralph Horsley +208 C Tread Mill @Jason Felix +209 U Turbo-Thwacking Auto-Hammer @Steve Prescott +210 C Twiddlestick Charger @Ralph Horsley +211 U Widget Contraption @Jason Felix +212 L Plains @John Avon +213 L Island @John Avon +214 L Swamp @John Avon +215 L Mountain @John Avon +216 L Forest @John Avon [tokens] storm_crow diff --git a/forge-gui/res/editions/War of the Spark.txt b/forge-gui/res/editions/War of the Spark.txt index 98d64a05dad..20a638a938f 100644 --- a/forge-gui/res/editions/War of the Spark.txt +++ b/forge-gui/res/editions/War of the Spark.txt @@ -5,7 +5,7 @@ Name=War of the Spark Code2=WAR Type=Expansion BoosterCovers=3 -Booster=10 Common:fromSheet("WAR cards"):!fromSheet("WAR Secret Cards"), 3 Uncommon:fromSheet("WAR cards"):!fromSheet("WAR Secret Cards"), 1 RareMythic:fromSheet("WAR cards"):!fromSheet("WAR Secret Cards"), 1 BasicLand +Booster=10 Common:fromSheet("WAR cards"), 3 Uncommon:fromSheet("WAR cards"), 1 RareMythic:fromSheet("WAR cards"), 1 BasicLand BoosterMustContain=Planeswalker FatPack=10 FatPackExtraSlots=80 BasicLands @@ -14,9 +14,7 @@ ScryfallCode=WAR [cards] 1 R Karn, the Great Creator @Wisnu Tan -1★ R Karn, the Great Creator @Naochika Morishita 2 R Ugin, the Ineffable @Daarken -2★ R Ugin, the Ineffable @Maekawa Yuichi 3 U Ugin's Conjurant @Ryan Yee 4 U Ajani's Pridemate @Sidharth Chaturvedi 5 C Battlefield Promotion @Scott Murphy @@ -28,7 +26,6 @@ ScryfallCode=WAR 11 C Enforcer Griffin @Johan Grenier 12 M Finale of Glory @Stanton Feng 13 M Gideon Blackblade @Viktor Titov -13★ M Gideon Blackblade @Tada 14 C Gideon's Sacrifice @Chris Rallis 15 U Gideon's Triumph @Kieran Yanner 16 M God-Eternal Oketra @Grzegorz Rutkowski @@ -48,13 +45,11 @@ ScryfallCode=WAR 30 R Single Combat @Livia Prima 31 U Sunblade Angel @Johannes Voss 32 U Teyo, the Shieldmage @Magali Villeneuve -32★ U Teyo, the Shieldmage @Foo Midori 33 C Teyo's Lightshield @Igor Kieryluk 34 R Tomik, Distinguished Advokist @Johannes Voss 35 C Topple the Statue @Sidharth Chaturvedi 36 C Trusted Pegasus @Chris Rahn 37 U The Wanderer @Wesley Burt -37★ U The Wanderer @Norikatsu Miyoshi 38 C Wanderer's Strike @Sara Winters 39 C War Screecher @Dan Murayama Scott 40 C Ashiok's Skulker @Livia Prima @@ -72,16 +67,13 @@ ScryfallCode=WAR 52 U Flux Channeler @Heonhwa Choe 53 M God-Eternal Kefnet @Lius Lasahido 54 R Jace, Wielder of Mysteries @Anna Steinbauer -54★ R Jace, Wielder of Mysteries @Toshiaki Takayama 55 U Jace's Triumph @Kieran Yanner 56 U Kasmina, Enigmatic Mentor @Magali Villeneuve -56★ U Kasmina, Enigmatic Mentor @Mid 57 C Kasmina's Transmutation @Uriah Voth 58 C Kiora's Dambreaker @Mathias Kollros 59 U Lazotep Plating @Yeong-Hao Han 60 C Naga Eternal @Johann Bodin 61 U Narset, Parter of Veils @Magali Villeneuve -61★ U Narset, Parter of Veils @Foo Midori 62 R Narset's Reversal @Viktor Titov 63 C No Escape @G-host Lee 64 C Relentless Advance @Stanton Feng @@ -104,7 +96,6 @@ ScryfallCode=WAR 81 C Charity Extractor @Matt Stewart 82 R Command the Dreadhorde @Daarken 83 U Davriel, Rogue Shadowmage @Daarken -83★ U Davriel, Rogue Shadowmage @Hagiya Kaoru 84 C Davriel's Shadowfugue @Daarken 85 R Deliver Unto Evil @Seb McKinnon 86 R Dreadhorde Invasion @Stanton Feng @@ -119,11 +110,9 @@ ScryfallCode=WAR 95 C Lazotep Behemoth @Zezhou Chen 96 C Lazotep Reaver @Craig J Spearing 97 M Liliana, Dreadhorde General @Chris Rallis -97★ M Liliana, Dreadhorde General @Yoshitaka Amano 98 U Liliana's Triumph @Kieran Yanner 99 R Massacre Girl @Chris Rallis 100 U Ob Nixilis, the Hate-Twisted @Yongjae Choi -100★ U Ob Nixilis, the Hate-Twisted @Sansyu 101 C Ob Nixilis's Cruelty @Igor Kieryluk 102 U Price of Betrayal @Ryan Yee 103 C Shriekdiver @Piotr Dura @@ -143,7 +132,6 @@ ScryfallCode=WAR 117 C Burning Prophet @Mathias Kollros 118 C Chainwhip Cyclops @Johann Bodin 119 R Chandra, Fire Artisan @Yongjae Choi -119★ R Chandra, Fire Artisan @Ryota-H 120 C Chandra's Pyrohelix @Aleksi Briclot 121 U Chandra's Triumph @Kieran Yanner 122 U Cyclops Electromancer @Jason Felix @@ -160,7 +148,6 @@ ScryfallCode=WAR 133 M Ilharg, the Raze-Boar @Filip Burburan 134 C Invading Manticore @Jehan Choo 135 U Jaya, Venerated Firemage @Yongjae Choi -135★ U Jaya, Venerated Firemage @Maekawa Yuichi 136 C Jaya's Greeting @Victor Adame Minguez 137 R Krenko, Tin Street Kingpin @Mark Behm 138 R Mizzium Tank @Wayne Reynolds @@ -169,16 +156,13 @@ ScryfallCode=WAR 141 C Raging Kronch @Steve Prescott 142 C Samut's Sprint @Aleksi Briclot 143 R Sarkhan the Masterless @Kieran Yanner -143★ R Sarkhan the Masterless @Lack 144 C Sarkhan's Catharsis @Zack Stella 145 C Spellgorger Weird @James Paick 146 U Tibalt, Rakish Instigator @Chase Stone -146★ U Tibalt, Rakish Instigator @Clover.K 147 U Tibalt's Rager @Yongjae Choi 148 C Turret Ogre @Johann Bodin 149 C Arboreal Grazer @Jason Rainville 150 U Arlinn, Voice of the Pack @Ryan Pancoast -150★ U Arlinn, Voice of the Pack @D-suzuki 151 C Arlinn's Wolf @Kimonas Theodossiou 152 R Awakening of Vitu-Ghazi @Jaime Jones 153 C Band Together @Josh Hass @@ -193,13 +177,11 @@ ScryfallCode=WAR 162 C Giant Growth @Dmitry Burmak 163 M God-Eternal Rhonas @Lius Lasahido 164 U Jiang Yanggu, Wildcrafter @Anna Steinbauer -164★ U Jiang Yanggu, Wildcrafter @Daisuke Izuka 165 C Kraul Stinger @Randy Vargas 166 C Kronch Wrangler @Steve Prescott 167 U Mowu, Loyal Companion @Kimonas Theodossiou 168 C New Horizons @Eytan Zana 169 R Nissa, Who Shakes the World @Chris Rallis -169★ R Nissa, Who Shakes the World @Hitowa 170 U Nissa's Triumph @Kieran Yanner 171 U Paradise Druid @Nils Hamm 172 R Planewide Celebration @Wisnu Tan @@ -211,12 +193,10 @@ ScryfallCode=WAR 178 U Storm the Citadel @Grzegorz Rutkowski 179 C Thundering Ceratok @Izzy 180 R Vivien, Champion of the Wilds @Magali Villeneuve -180★ R Vivien, Champion of the Wilds @Hisashi Momose 181 R Vivien's Arkbow @Zack Stella 182 C Vivien's Grizzly @Lius Lasahido 183 C Wardscale Crocodile @Zezhou Chen 184 R Ajani, the Greathearted @Victor Adame Minguez -184★ R Ajani, the Greathearted @Miho Midorikawa 185 U Angrath's Rampage @Victor Adame Minguez 186 R Bioessence Hydra @Mathias Kollros 187 R Casualties of War @Tomasz Jedruszek @@ -224,7 +204,6 @@ ScryfallCode=WAR 189 U Deathsprout @Seb McKinnon 190 U Despark @Slawomir Maniak 191 R Domri, Anarch of Bolas @Raymond Swanland -191★ R Domri, Anarch of Bolas @Raita Kazama 192 U Domri's Ambush @Victor Adame Minguez 193 U Dovin's Veto @Izzy 194 R Dreadhorde Butcher @Piotr Dura @@ -241,50 +220,35 @@ ScryfallCode=WAR 205 U Merfolk Skydiver @Sara Winters 206 U Neoform @Bram Sels 207 M Nicol Bolas, Dragon-God @Raymond Swanland -207★ M Nicol Bolas, Dragon-God @Kaida Yuji 208 M Niv-Mizzet Reborn @Raymond Swanland 209 R Oath of Kaya @Wesley Burt 210 U Pledge of Unity @Chris Rallis 211 R Ral, Storm Conduit @Wesley Burt -211★ R Ral, Storm Conduit @Naochika Morishita 212 U Ral's Outburst @Joseph Meehan 213 M Roalesk, Apex Hybrid @Svetlin Velinov 214 R Role Reversal @Mathias Kollros 215 U Rubblebelt Rioters @Tomasz Jedruszek 216 R Solar Blaze @Adam Paquette 217 R Sorin, Vengeful Bloodlord @Tommy Arnold -217★ R Sorin, Vengeful Bloodlord @Yukie Tajima 218 R Soul Diviner @Randy Vargas 219 R Storrev, Devkarin Lich @Igor Kieryluk 220 R Tamiyo, Collector of Tales @Chase Stone -220★ R Tamiyo, Collector of Tales @Fuzichoco 221 R Teferi, Time Raveler @Chris Rallis -221★ R Teferi, Time Raveler @Shishizaru 222 U Tenth District Legionnaire @Victor Adame Minguez 223 R Time Wipe @Svetlin Velinov 224 R Tolsimir, Friend to Wolves @Ryan Pancoast 225 U Tyrant's Scorn @Svetlin Velinov 226 R Widespread Brutality @Victor Adame Minguez 227 U Angrath, Captain of Chaos @Slawomir Maniak -227★ U Angrath, Captain of Chaos @Sansyu 228 U Ashiok, Dream Render @Cynthia Sheppard -228★ U Ashiok, Dream Render @Hozan Shinomaru 229 U Dovin, Hand of Control @Kieran Yanner -229★ U Dovin, Hand of Control @Nablange 230 U Huatli, the Sun's Heart @Lius Lasahido -230★ U Huatli, the Sun's Heart @Mikio Masuda 231 U Kaya, Bane of the Dead @Magali Villeneuve -231★ U Kaya, Bane of the Dead @Mid 232 U Kiora, Behemoth Beckoner @Jaime Jones -232★ U Kiora, Behemoth Beckoner @Yamamoto Akifumi 233 U Nahiri, Storm of Stone @Aleksi Briclot -233★ U Nahiri, Storm of Stone @Yukie Tajima 234 U Saheeli, Sublime Artificer @Wesley Burt -234★ U Saheeli, Sublime Artificer @Hisashi Momose 235 U Samut, Tyrant Smasher @Aleksi Briclot -235★ U Samut, Tyrant Smasher @Norikatsu Miyoshi 236 U Vraska, Swarm's Eminence @Anna Steinbauer -236★ U Vraska, Swarm's Eminence @Ryota Murayama 237 U Firemind Vessel @Ravenna Tran 238 U God-Pharaoh's Statue @Igor Kieryluk 239 C Guild Globe @Daniel Ljunggren @@ -313,6 +277,46 @@ ScryfallCode=WAR 262 L Forest @Jonas De Ro 263 L Forest @Titus Lunter 264 L Forest @Richard Wright + +[promo] +1★ R Karn, the Great Creator @Naochika Morishita +2★ R Ugin, the Ineffable @Maekawa Yuichi +13★ M Gideon Blackblade @Tada +32★ U Teyo, the Shieldmage @Foo Midori +37★ U The Wanderer @Norikatsu Miyoshi +54★ R Jace, Wielder of Mysteries @Toshiaki Takayama +56★ U Kasmina, Enigmatic Mentor @Mid +61★ U Narset, Parter of Veils @Foo Midori +83★ U Davriel, Rogue Shadowmage @Hagiya Kaoru +97★ M Liliana, Dreadhorde General @Yoshitaka Amano +100★ U Ob Nixilis, the Hate-Twisted @Sansyu +119★ R Chandra, Fire Artisan @Ryota-H +135★ U Jaya, Venerated Firemage @Maekawa Yuichi +143★ R Sarkhan the Masterless @Lack +146★ U Tibalt, Rakish Instigator @Clover.K +150★ U Arlinn, Voice of the Pack @D-suzuki +164★ U Jiang Yanggu, Wildcrafter @Daisuke Izuka +169★ R Nissa, Who Shakes the World @Hitowa +180★ R Vivien, Champion of the Wilds @Hisashi Momose +184★ R Ajani, the Greathearted @Miho Midorikawa +191★ R Domri, Anarch of Bolas @Raita Kazama +207★ M Nicol Bolas, Dragon-God @Kaida Yuji +211★ R Ral, Storm Conduit @Naochika Morishita +217★ R Sorin, Vengeful Bloodlord @Yukie Tajima +220★ R Tamiyo, Collector of Tales @Fuzichoco +221★ R Teferi, Time Raveler @Shishizaru +227★ U Angrath, Captain of Chaos @Sansyu +228★ U Ashiok, Dream Render @Hozan Shinomaru +229★ U Dovin, Hand of Control @Nablange +230★ U Huatli, the Sun's Heart @Mikio Masuda +231★ U Kaya, Bane of the Dead @Mid +232★ U Kiora, Behemoth Beckoner @Yamamoto Akifumi +233★ U Nahiri, Storm of Stone @Yukie Tajima +234★ U Saheeli, Sublime Artificer @Hisashi Momose +235★ U Samut, Tyrant Smasher @Norikatsu Miyoshi +236★ U Vraska, Swarm's Eminence @Ryota Murayama + +[precon product] 265 M Gideon, the Oathsworn @Kieran Yanner 266 C Desperate Lunge @Deruchenko Alexander 267 R Gideon's Battle Cry @Zoltan Boros @@ -323,6 +327,8 @@ ScryfallCode=WAR 272 U Jace's Projection @Darek Zabrocki 273 R Jace's Ruse @Clint Cearley 274 C Simic Guildgate @Adam Paquette + +[buy a box] 275 M Tezzeret, Master of the Bridge @Chase Stone [rebalanced] diff --git a/forge-gui/res/editions/Zendikar Expeditions.txt b/forge-gui/res/editions/Zendikar Expeditions.txt index b7da0cd08f6..4b5b26925e2 100644 --- a/forge-gui/res/editions/Zendikar Expeditions.txt +++ b/forge-gui/res/editions/Zendikar Expeditions.txt @@ -5,6 +5,7 @@ Name=Zendikar Expeditions Type=Collector_Edition ScryfallCode=EXP +#Bonus cards for BFZ Battle for Zendikar [cards] 1 M Prairie Stream @Titus Lunter 2 M Sunken Hollow @Titus Lunter @@ -31,6 +32,9 @@ ScryfallCode=EXP 23 M Verdant Catacombs @Ryan Yee 24 M Arid Mesa @Ryan Yee 25 M Misty Rainforest @Ryan Yee + +#Bonus cards for OGW Oath of the Gatewatch +[special slot] 26 M Mystic Gate @Adam Paquette 27 M Sunken Ruins @Adam Paquette 28 M Graven Cairns @Adam Paquette diff --git a/forge-gui/res/editions/Zendikar Rising.txt b/forge-gui/res/editions/Zendikar Rising.txt index 3f2c5e882b9..a65ba6ef048 100644 --- a/forge-gui/res/editions/Zendikar Rising.txt +++ b/forge-gui/res/editions/Zendikar Rising.txt @@ -399,6 +399,8 @@ ScryfallCode=ZNR 377 R Skyclave Relic @Daniel Ljunggren 378 R Crawling Barrens @Jonas De Ro 379 R Throne of Makindi @Igor Kieryluk + +[precon product] 380 L Plains @Adam Paquette 381 L Island @Tianhua X 382 L Swamp @Adam Paquette From 143e3c8c7543c8d4f1de3475f2025e88b007ca5c Mon Sep 17 00:00:00 2001 From: Agetian Date: Fri, 29 Nov 2024 13:13:11 +0300 Subject: [PATCH 136/152] - Add new genetic decks for Standard and Modern as of November 2024. (#6647) --- .../GAM_10_Shacklegeist based deck_14_1.dck | 32 +++++++++++++++++ ...11_Leyline of Sanctity based deck_64_1.dck | 35 ++++++++++++++++++ ...e, Resplendent Cathar based deck_157_0.dck | 32 +++++++++++++++++ ...M_13_Unsettled Mariner based deck_15_0.dck | 30 ++++++++++++++++ ...14_Sarinth Steelseeker based deck_95_0.dck | 33 +++++++++++++++++ ...Third Path Iconoclast based deck_140_0.dck | 36 +++++++++++++++++++ ...Nixilis, the Adversary based deck_54_0.dck | 35 ++++++++++++++++++ ...m, Alluring Scoundrel based deck_105_0.dck | 32 +++++++++++++++++ ...8_Sword of Fire and Ice based deck_8_1.dck | 34 ++++++++++++++++++ ... of the Mirror-Breaker based deck_21_1.dck | 32 +++++++++++++++++ ...GAM_1_Gladecover Scout based deck_25_1.dck | 34 ++++++++++++++++++ ...AM_20_Phantasmal Image based deck_22_1.dck | 32 +++++++++++++++++ ...e, Resplendent Cathar based deck_156_0.dck | 35 ++++++++++++++++++ ...mwise the Stouthearted based deck_53_1.dck | 34 ++++++++++++++++++ ...ine, Resplendent Cathar based deck_2_1.dck | 33 +++++++++++++++++ .../GAM_24_Cache Grab based deck_26_0.dck | 32 +++++++++++++++++ ..._Bloodtithe Harvester based deck_110_0.dck | 35 ++++++++++++++++++ ...26_Smuggler's Surprise based deck_49_0.dck | 33 +++++++++++++++++ ...M_27_Unsettled Mariner based deck_14_0.dck | 29 +++++++++++++++ ..._Scorn-Blade Berserker based deck_26_1.dck | 26 ++++++++++++++ ...mwise the Stouthearted based deck_57_0.dck | 32 +++++++++++++++++ .../GAM_2_Spider Umbra based deck_9_1.dck | 31 ++++++++++++++++ .../GAM_30_Eiganjo Castle based deck_11_1.dck | 35 ++++++++++++++++++ ...Nixilis, the Adversary based deck_55_0.dck | 36 +++++++++++++++++++ ...AM_32_Fractured Sanity based deck_72_1.dck | 33 +++++++++++++++++ ...M_33_Collected Company based deck_43_1.dck | 34 ++++++++++++++++++ ...4_Cemetery Illuminator based deck_75_0.dck | 35 ++++++++++++++++++ ...5_Domain Aggro Zoo Generated Deck_25_0.dck | 29 +++++++++++++++ ...Mishra's Research Desk based deck_32_0.dck | 32 +++++++++++++++++ ...GAM_37_Narnam Renegade based deck_30_1.dck | 32 +++++++++++++++++ .../GAM_38_Vein Ripper based deck_36_1.dck | 34 ++++++++++++++++++ .../GAM_39_Memory Deluge based deck_129_0.dck | 35 ++++++++++++++++++ ...ri Zev, Skyship Raider based deck_78_1.dck | 33 +++++++++++++++++ .../GAM_40_Lion Sash based deck_120_0.dck | 36 +++++++++++++++++++ ... Titan of Fire's Fury based deck_115_0.dck | 33 +++++++++++++++++ ..._Extraction Specialist based deck_30_0.dck | 34 ++++++++++++++++++ ..._6_Deeproot Pilgrimage based deck_67_1.dck | 29 +++++++++++++++ ...avan, Nimble Pilferer based deck_136_0.dck | 26 ++++++++++++++ ...ril, Flame of the West based deck_22_0.dck | 34 ++++++++++++++++++ ...Kellan, Daring Traveler based deck_5_0.dck | 32 +++++++++++++++++ ..._10_Sharp-Eyed Rookie based deck_129_0.dck | 27 ++++++++++++++ .../GAS_11_Monstrous Rage based deck_79_1.dck | 27 ++++++++++++++ ...12_Aurelia's Vindicator based deck_2_1.dck | 29 +++++++++++++++ .../GAS_13_Blooming Marsh based deck_0_1.dck | 28 +++++++++++++++ ...enn, Paranoid Partisan based deck_22_1.dck | 32 +++++++++++++++++ ...GAS_15_Decadent Dragon based deck_26_0.dck | 31 ++++++++++++++++ ...d Skitter, Sewer King based deck_141_0.dck | 29 +++++++++++++++ .../GAS_17_Dawn's Truce based deck_77_1.dck | 31 ++++++++++++++++ .../GAS_18_Braided Net based deck_157_0.dck | 29 +++++++++++++++ ...var, Jubilant Brawler based deck_143_0.dck | 33 +++++++++++++++++ ...quee, Dubious Monarch based deck_154_0.dck | 25 +++++++++++++ .../GAS_20_Spell Stutter based deck_35_1.dck | 31 ++++++++++++++++ ...Virtue of Persistence based deck_101_0.dck | 28 +++++++++++++++ .../GAS_22_Pile On based deck_36_1.dck | 31 ++++++++++++++++ ...S_23_Palani's Hatcher based deck_148_0.dck | 31 ++++++++++++++++ ..._Kaito, Dancing Shadow based deck_77_0.dck | 27 ++++++++++++++ ...S_25_Soul of Windgrace based deck_53_0.dck | 33 +++++++++++++++++ ...S_26_Palani's Hatcher based deck_149_0.dck | 32 +++++++++++++++++ ...27_Rocco, Street Chef based deck_158_0.dck | 29 +++++++++++++++ ...nie Flash, the Veteran based deck_29_1.dck | 32 +++++++++++++++++ ...ring-Loaded Sawblades based deck_122_0.dck | 32 +++++++++++++++++ ...S_2_Rocco, Street Chef based deck_75_1.dck | 33 +++++++++++++++++ ...S_30_Bramble Familiar based deck_124_0.dck | 31 ++++++++++++++++ ...AS_31_Gumdrop Poisoner based deck_94_0.dck | 31 ++++++++++++++++ ...S_32_Plundering Pirate based deck_84_0.dck | 33 +++++++++++++++++ ..._Excavation Explosion based deck_105_0.dck | 29 +++++++++++++++ ..._34_Invasion of Tarkir based deck_47_1.dck | 31 ++++++++++++++++ .../GAS_35_Spyglass Siren based deck_18_1.dck | 28 +++++++++++++++ .../GAS_36_Tough Cookie based deck_112_0.dck | 32 +++++++++++++++++ ..._37_Portal to Phyrexia based deck_72_0.dck | 32 +++++++++++++++++ ...Pugnacious Hammerskull based deck_32_1.dck | 32 +++++++++++++++++ ...S_39_Searslicer Goblin based deck_71_1.dck | 31 ++++++++++++++++ ...bel, Heir to Cragflame based deck_81_0.dck | 31 ++++++++++++++++ ...40_Rocco, Street Chef based deck_159_0.dck | 31 ++++++++++++++++ ..._4_Concealed Courtyard based deck_23_1.dck | 27 ++++++++++++++ ...gari Midrange Mono Generated Deck_78_1.dck | 30 ++++++++++++++++ ...S_6_Sharp-Eyed Rookie based deck_128_0.dck | 30 ++++++++++++++++ ...GAS_7_Starscape Cleric based deck_15_1.dck | 32 +++++++++++++++++ ...bel, Heir to Cragflame based deck_80_0.dck | 31 ++++++++++++++++ ..._9_Bonehoard Dracosaur based deck_33_0.dck | 30 ++++++++++++++++ 80 files changed, 2521 insertions(+) create mode 100644 forge-gui/res/geneticaidecks/GAM_10_Shacklegeist based deck_14_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_11_Leyline of Sanctity based deck_64_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_12_Adeline, Resplendent Cathar based deck_157_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_13_Unsettled Mariner based deck_15_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_14_Sarinth Steelseeker based deck_95_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_15_Third Path Iconoclast based deck_140_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_16_Ob Nixilis, the Adversary based deck_54_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_17_Malcolm, Alluring Scoundrel based deck_105_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_18_Sword of Fire and Ice based deck_8_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_19_Fable of the Mirror-Breaker based deck_21_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_1_Gladecover Scout based deck_25_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_20_Phantasmal Image based deck_22_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_21_Adeline, Resplendent Cathar based deck_156_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_22_Samwise the Stouthearted based deck_53_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_23_Adeline, Resplendent Cathar based deck_2_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_24_Cache Grab based deck_26_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_25_Bloodtithe Harvester based deck_110_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_26_Smuggler's Surprise based deck_49_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_27_Unsettled Mariner based deck_14_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_28_Scorn-Blade Berserker based deck_26_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_29_Samwise the Stouthearted based deck_57_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_2_Spider Umbra based deck_9_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_30_Eiganjo Castle based deck_11_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_31_Ob Nixilis, the Adversary based deck_55_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_32_Fractured Sanity based deck_72_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_33_Collected Company based deck_43_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_34_Cemetery Illuminator based deck_75_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_35_Domain Aggro Zoo Generated Deck_25_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_36_Mishra's Research Desk based deck_32_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_37_Narnam Renegade based deck_30_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_38_Vein Ripper based deck_36_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_39_Memory Deluge based deck_129_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_3_Kari Zev, Skyship Raider based deck_78_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_40_Lion Sash based deck_120_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_4_Phlage, Titan of Fire's Fury based deck_115_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_5_Extraction Specialist based deck_30_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_6_Deeproot Pilgrimage based deck_67_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_7_Ragavan, Nimble Pilferer based deck_136_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_8_Anduril, Flame of the West based deck_22_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAM_9_Kellan, Daring Traveler based deck_5_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_10_Sharp-Eyed Rookie based deck_129_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_11_Monstrous Rage based deck_79_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_12_Aurelia's Vindicator based deck_2_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_13_Blooming Marsh based deck_0_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_14_Stenn, Paranoid Partisan based deck_22_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_15_Decadent Dragon based deck_26_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_16_Lord Skitter, Sewer King based deck_141_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_17_Dawn's Truce based deck_77_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_18_Braided Net based deck_157_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_19_Tyvar, Jubilant Brawler based deck_143_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_1_Squee, Dubious Monarch based deck_154_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_20_Spell Stutter based deck_35_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_21_Virtue of Persistence based deck_101_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_22_Pile On based deck_36_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_23_Palani's Hatcher based deck_148_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_24_Kaito, Dancing Shadow based deck_77_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_25_Soul of Windgrace based deck_53_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_26_Palani's Hatcher based deck_149_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_27_Rocco, Street Chef based deck_158_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_28_Annie Flash, the Veteran based deck_29_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_29_Spring-Loaded Sawblades based deck_122_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_2_Rocco, Street Chef based deck_75_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_30_Bramble Familiar based deck_124_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_31_Gumdrop Poisoner based deck_94_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_32_Plundering Pirate based deck_84_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_33_Excavation Explosion based deck_105_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_34_Invasion of Tarkir based deck_47_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_35_Spyglass Siren based deck_18_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_36_Tough Cookie based deck_112_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_37_Portal to Phyrexia based deck_72_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_38_Pugnacious Hammerskull based deck_32_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_39_Searslicer Goblin based deck_71_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_3_Mabel, Heir to Cragflame based deck_81_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_40_Rocco, Street Chef based deck_159_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_4_Concealed Courtyard based deck_23_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_5_Golgari Midrange Mono Generated Deck_78_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_6_Sharp-Eyed Rookie based deck_128_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_7_Starscape Cleric based deck_15_1.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_8_Mabel, Heir to Cragflame based deck_80_0.dck create mode 100644 forge-gui/res/geneticaidecks/GAS_9_Bonehoard Dracosaur based deck_33_0.dck diff --git a/forge-gui/res/geneticaidecks/GAM_10_Shacklegeist based deck_14_1.dck b/forge-gui/res/geneticaidecks/GAM_10_Shacklegeist based deck_14_1.dck new file mode 100644 index 00000000000..d93f34e2b81 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_10_Shacklegeist based deck_14_1.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_10_Shacklegeist based deck_14_1 +[Main] +4 Aether Vial|DST|1 +3 Cavern of Souls|MM3|1 +4 Drogskol Captain|DKA|1 +3 Flooded Strand|MH3|4 +3 Hallowed Fountain|RNA|1 +1 Island|LRW|1 +1 Island|M21|1 +1 Island|MH2|1 +1 Island|RTR|1 +4 Mausoleum Wanderer|EMN|1 +4 Phantasmal Image|MM3|1 +2 Plains|DTK|1 +1 Plains|MH3|1 +4 Seachrome Coast|SOM|1 +3 Selfless Spirit|EMN|1 +4 Shacklegeist|M21|2 +3 Skyclave Apparition|ZNR|1 +4 Spectral Sailor|M20|1 +4 Spell Queller|EMN|1 +2 Supreme Phantom|M19|1 +2 Unsettled Mariner|MH1|1 +2 Wanderwine Hub|LRW|1 +[Sideboard] +3 Get Lost|LCI|1 +3 Kira, Great Glass-Spinner|BOK|1 +2 Nebelgast Herald|EMN|1 +1 Selfless Spirit|EMN|1 +3 Spell Pierce|MM3|1 +3 Surge of Salvation|MOM|1 diff --git a/forge-gui/res/geneticaidecks/GAM_11_Leyline of Sanctity based deck_64_1.dck b/forge-gui/res/geneticaidecks/GAM_11_Leyline of Sanctity based deck_64_1.dck new file mode 100644 index 00000000000..3b46c7188d6 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_11_Leyline of Sanctity based deck_64_1.dck @@ -0,0 +1,35 @@ +[metadata] +Name=GAM_11_Leyline of Sanctity based deck_64_1 +[Main] +3 Bishop of Wings|M20|1 +2 Castle Ardenvale|ELD|2 +4 Cavern of Souls|AVR|1 +4 Chalice of the Void|MRD|1 +4 Giada, Font of Hope|SNC|3 +3 Kayla's Reconstruction|BRO|2 +4 Leyline of Sanctity|M11|1 +4 March of Otherworldly Light|NEO|1 +4 Path to Exile|ACR|2 +1 Plains|ALA|1 +1 Plains|AVR|1 +2 Plains|CHK|1 +2 Plains|ELD|1 +1 Plains|KHM|1 +1 Plains|LCI|1 +1 Plains|M10|1 +2 Plains|M19|1 +1 Plains|M20|1 +1 Plains|MID|1 +1 Plains|MOM|1 +1 Plains|MRD|1 +1 Plains|NEO|1 +4 Resplendent Angel|M19|1 +4 Righteous Valkyrie|KHM|2 +1 Roaming Throne|LCI|2 +3 Yotian Frontliner|BRO|1 +[Sideboard] +4 Gisela, the Broken Blade|EMN|1 +1 Kayla's Reconstruction|BRO|2 +4 Ranger of Eos|ALA|1 +2 Valiant Veteran|DMU|1 +4 Winds of Abandon|MH1|1 diff --git a/forge-gui/res/geneticaidecks/GAM_12_Adeline, Resplendent Cathar based deck_157_0.dck b/forge-gui/res/geneticaidecks/GAM_12_Adeline, Resplendent Cathar based deck_157_0.dck new file mode 100644 index 00000000000..fbce576dfb2 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_12_Adeline, Resplendent Cathar based deck_157_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_12_Adeline, Resplendent Cathar based deck_157_0 +[Main] +4 Adeline, Resplendent Cathar|MID|2 +4 Anointed Peacekeeper|DMU|1 +4 Cavern of Souls|LCI|5 +4 Champion of the Parish|ISD|1 +4 Coppercoat Vanguard|MAT|4 +1 Esper Sentinel|MH2|2 +3 Horizon Canopy|FUT|1 +1 Island|MH2|1 +1 Island|XLN|1 +4 Jirina, Dauntless General|MAT|5 +3 Kellan, Daring Traveler|LCI|1 +4 Lavinia, Azorius Renegade|RNA|1 +2 Noble Hierarch|MM2|1 +1 Plains|ISD|1 +1 Plains|MH2|1 +1 Plains|ONE|1 +1 Plains|RNA|1 +4 Reflector Mage|OGW|1 +3 Secluded Courtyard|NEO|2 +2 Silent Clearing|MH1|1 +4 Thalia's Lieutenant|SOI|1 +4 Unclaimed Territory|XLN|1 +[Sideboard] +2 Esper Sentinel|MH2|2 +4 Hopeful Initiate|VOW|1 +1 Kitesail Freebooter|M21|1 +4 Kitesail Larcenist|LCI|2 +1 Pyre of Heroes|KHM|2 +3 Unsettled Mariner|MH1|1 diff --git a/forge-gui/res/geneticaidecks/GAM_13_Unsettled Mariner based deck_15_0.dck b/forge-gui/res/geneticaidecks/GAM_13_Unsettled Mariner based deck_15_0.dck new file mode 100644 index 00000000000..d22436cc07c --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_13_Unsettled Mariner based deck_15_0.dck @@ -0,0 +1,30 @@ +[metadata] +Name=GAM_13_Unsettled Mariner based deck_15_0 +[Main] +3 Adeline, Resplendent Cathar|DBL|1 +4 Ancient Ziggurat|CFX|1 +4 Cavern of Souls|MM3|1 +1 Champion of the Parish|ISD|1 +4 Coppercoat Vanguard|MAT|2 +4 Esper Sentinel|MH2|1 +4 Imperial Recruiter|MH2|1 +1 Island|GRN|1 +1 Mountain|MKM|1 +1 Mountain|THB|1 +2 Plains|ACR|1 +1 Plains|GRN|1 +2 Plains|NEO|1 +4 Ranger-Captain of Eos|MH1|1 +4 Riders of the Mark|LTR|2 +2 Sacred Foundry|GRN|1 +4 Secluded Courtyard|NEO|1 +4 Solitude|MH2|2 +3 Thalia's Lieutenant|SOI|1 +3 Unclaimed Territory|XLN|1 +4 Unsettled Mariner|MH1|1 +[Sideboard] +4 Aether Vial|MMA|1 +1 Charming Prince|FDN|1 +3 Delney, Streetwise Lookout|MKM|3 +4 Hopeful Initiate|VOW|2 +3 Norin the Wary|TSP|1 diff --git a/forge-gui/res/geneticaidecks/GAM_14_Sarinth Steelseeker based deck_95_0.dck b/forge-gui/res/geneticaidecks/GAM_14_Sarinth Steelseeker based deck_95_0.dck new file mode 100644 index 00000000000..edf799e2348 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_14_Sarinth Steelseeker based deck_95_0.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAM_14_Sarinth Steelseeker based deck_95_0 +[Main] +2 Aclazotz, Deepest Betrayal|LCI|2 +4 Archon of Cruelty|MH2|2 +4 Caustic Bronco|OTJ|1 +1 Combat Research|DMU|1 +4 Curious Obsession|RIX|1 +4 Drown in the Loch|ELD|1 +1 Forest|AFR|1 +1 Forest|MH3|1 +4 Hall of Storm Giants|AFR|2 +2 Island|DMU|1 +1 Island|KTK|1 +3 Island|MH3|1 +1 Island|OTJ|1 +1 Island|XLN|1 +4 Kira, Great Glass-Spinner|MMA|1 +4 Polluted Delta|MH3|1 +4 Sarinth Steelseeker|BRO|1 +2 Siren Stormtamer|XLN|1 +3 Spectral Sailor|M20|1 +1 Swamp|ELD|1 +1 Swamp|LCI|1 +1 Swamp|LTR|1 +1 Swamp|MKM|1 +2 Swamp|RIX|1 +4 Vendilion Clique|MOR|1 +[Sideboard] +3 Combat Research|DMU|1 +3 Hope of Ghirapur|AER|1 +4 Stubborn Denial|KTK|1 +4 The One Ring|LTR|4 diff --git a/forge-gui/res/geneticaidecks/GAM_15_Third Path Iconoclast based deck_140_0.dck b/forge-gui/res/geneticaidecks/GAM_15_Third Path Iconoclast based deck_140_0.dck new file mode 100644 index 00000000000..6f33dd7116f --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_15_Third Path Iconoclast based deck_140_0.dck @@ -0,0 +1,36 @@ +[metadata] +Name=GAM_15_Third Path Iconoclast based deck_140_0 +[Main] +1 Goblin Grenade|M12|1 +1 Island|M10|1 +1 Island|MH2|1 +3 Koth, Fire of Resistance|ONE|1 +4 Kumano Faces Kakkazan|NEO|1 +4 Laelia, the Blade Reforged|MH3|3 +4 Lightning Bolt|M10|1 +3 Lightning Strike|THS|1 +4 Manifold Mouse|BLB|1 +2 Might of the Meek|BLB|1 +1 Mountain|BRO|1 +1 Mountain|ELD|1 +1 Mountain|GK1|1 +1 Mountain|KTK|1 +1 Mountain|M10|1 +1 Mountain|MH2|1 +2 Mountain|MH3|1 +1 Mountain|ONE|1 +1 Mountain|OTJ|1 +1 Mountain|RAV|1 +2 Play with Fire|DBL|1 +4 Radiant Fountain|M21|1 +4 Ramunap Ruins|HOU|1 +4 Roughshod Duo|BLB|1 +3 Sardian Cliffstomper|BRO|1 +1 Sunscorched Desert|AKH|1 +4 Third Path Iconoclast|BRO|1 +[Sideboard] +2 Embereth Shieldbreaker|ELD|2 +3 Goblin Grenade|M12|1 +4 Koth of the Hammer|SOM|1 +3 Reckless Lackey|OTJ|1 +3 Seal of Fire|DIS|1 diff --git a/forge-gui/res/geneticaidecks/GAM_16_Ob Nixilis, the Adversary based deck_54_0.dck b/forge-gui/res/geneticaidecks/GAM_16_Ob Nixilis, the Adversary based deck_54_0.dck new file mode 100644 index 00000000000..6995d5bb6d7 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_16_Ob Nixilis, the Adversary based deck_54_0.dck @@ -0,0 +1,35 @@ +[metadata] +Name=GAM_16_Ob Nixilis, the Adversary based deck_54_0 +[Main] +3 Blood Spatter Analysis|MKM|2 +3 Bloodghast|ZEN|1 +1 Claim // Fame|HOU|1 +4 Collective Brutality|EMN|1 +4 Den of the Bugbear|AFR|1 +3 Lightning Skelemental|MH1|1 +1 Mountain|AFR|1 +2 Mountain|AVR|1 +1 Mountain|SNC|1 +4 Ob Nixilis, the Adversary|SNC|4 +4 Persist|MH2|2 +3 Raucous Theater|MKM|1 +4 Rotting Regisaur|M20|1 +3 Stitcher's Supplier|M19|1 +1 Swamp|AVR|1 +1 Swamp|LCI|1 +1 Swamp|M19|1 +2 Swamp|M20|1 +1 Swamp|MKM|1 +1 Swamp|NEO|1 +1 Swamp|ORI|1 +1 Swamp|THS|1 +4 Takenuma, Abandoned Mire|NEO|3 +3 Troll of Khazad-dûm|LTR|2 +1 Unearth|MH1|1 +3 Unmarked Grave|MH2|1 +[Sideboard] +3 Archfiend's Vessel|M21|1 +4 Carrion Feeder|MH1|1 +2 Corrupted Conviction|MOM|1 +2 Unearth|MH1|1 +4 Village Rites|M21|1 diff --git a/forge-gui/res/geneticaidecks/GAM_17_Malcolm, Alluring Scoundrel based deck_105_0.dck b/forge-gui/res/geneticaidecks/GAM_17_Malcolm, Alluring Scoundrel based deck_105_0.dck new file mode 100644 index 00000000000..e53574f5342 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_17_Malcolm, Alluring Scoundrel based deck_105_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_17_Malcolm, Alluring Scoundrel based deck_105_0 +[Main] +4 Bitter Triumph|LCI|1 +3 Brazen Borrower|ELD|2 +2 Death's Shadow|MM3|1 +4 Dismember|MM2|1 +3 Dress Down|MH2|1 +4 Drown in the Loch|ELD|1 +1 Island|ACR|1 +1 Island|KTK|1 +1 Island|LCI|1 +2 Island|M11|1 +1 Island|MH2|1 +3 Island|SNC|1 +4 Malcolm, Alluring Scoundrel|LCI|2 +3 Murktide Regent|MH2|1 +4 Orcish Bowmasters|LTR|2 +4 Otawara, Soaring City|NEO|1 +2 Swamp|ACR|1 +1 Swamp|KTK|1 +3 Swamp|M11|1 +1 Swamp|NEO|1 +1 Thoughtseize|LRW|1 +4 Tishana's Tidebinder|LCI|2 +4 Watery Grave|GTC|1 +[Sideboard] +1 Ledger Shredder|SNC|1 +3 Preordain|M11|1 +4 Sauron's Ransom|LTR|3 +4 Stubborn Denial|KTK|1 +3 Thoughtseize|LRW|1 diff --git a/forge-gui/res/geneticaidecks/GAM_18_Sword of Fire and Ice based deck_8_1.dck b/forge-gui/res/geneticaidecks/GAM_18_Sword of Fire and Ice based deck_8_1.dck new file mode 100644 index 00000000000..a58367d4583 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_18_Sword of Fire and Ice based deck_8_1.dck @@ -0,0 +1,34 @@ +[metadata] +Name=GAM_18_Sword of Fire and Ice based deck_8_1 +[Main] +3 Aether Vial|DST|1 +3 Anointed Peacekeeper|DMU|2 +3 Archon of Emeria|ZNR|1 +4 Batterskull|NPH|1 +3 Ghost Quarter|MD1|1 +4 Giver of Runes|MH1|1 +4 Leonin Arbiter|SOM|1 +2 Plains|9ED|1 +2 Plains|ACR|1 +2 Plains|CHK|1 +1 Plains|KLD|1 +1 Plains|LCI|1 +1 Plains|LTR|1 +1 Plains|MH2|1 +1 Plains|NEO|1 +1 Plains|NPH|1 +2 Plains|ONE|1 +1 Plains|OTJ|1 +2 Plains|STX|1 +2 Plains|TSP|1 +4 Serra Paragon|DMU|1 +4 Solitude|MH2|1 +4 Sword of Fire and Ice|DST|1 +2 Thalia, Guardian of Thraben|DBL|1 +3 Volatile Fault|LCI|1 +[Sideboard] +4 Aven Interrupter|OTJ|1 +3 Ephemerate|MH1|1 +3 Flickerwisp|EVE|1 +4 Lion Sash|NEO|2 +1 Path to Exile|MMA|1 diff --git a/forge-gui/res/geneticaidecks/GAM_19_Fable of the Mirror-Breaker based deck_21_1.dck b/forge-gui/res/geneticaidecks/GAM_19_Fable of the Mirror-Breaker based deck_21_1.dck new file mode 100644 index 00000000000..0ef13d7fe3e --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_19_Fable of the Mirror-Breaker based deck_21_1.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_19_Fable of the Mirror-Breaker based deck_21_1 +[Main] +2 Blackcleave Cliffs|ONE|1 +4 Blood Crypt|RNA|1 +3 Bloodstained Mire|KTK|1 +4 Dauthi Voidwalker|MH2|1 +4 Fable of the Mirror-Breaker|NEO|3 +4 Fatal Push|ACR|2 +4 Inquisition of Kozilek|MD1|1 +3 Kroxa, Titan of Death's Hunger|THB|1 +4 Liliana of the Veil|MM3|1 +1 Molten Collapse|LCI|1 +1 Mountain|DMU|1 +1 Mountain|LCI|1 +1 Mountain|M11|1 +4 Orcish Bowmasters|LTR|1 +4 Ragavan, Nimble Pilferer|MH2|1 +3 Sheoldred, the Apocalypse|DMU|3 +1 Swamp|LCI|1 +1 Swamp|M10|1 +3 Swamp|MKM|1 +2 Swamp|RNA|1 +1 Swamp|THB|1 +2 Takenuma, Abandoned Mire|NEO|3 +3 Thoughtseize|LRW|1 +[Sideboard] +4 Burning Inquiry|M10|1 +1 Kroxa, Titan of Death's Hunger|THB|1 +2 Seasoned Pyromancer|MH1|1 +4 Terminate|ARB|1 +4 Waste Not|M15|1 diff --git a/forge-gui/res/geneticaidecks/GAM_1_Gladecover Scout based deck_25_1.dck b/forge-gui/res/geneticaidecks/GAM_1_Gladecover Scout based deck_25_1.dck new file mode 100644 index 00000000000..ba68855ba68 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_1_Gladecover Scout based deck_25_1.dck @@ -0,0 +1,34 @@ +[metadata] +Name=GAM_1_Gladecover Scout based deck_25_1 +[Main] +3 Daybreak Coronet|MM2|1 +4 Ethereal Armor|RTR|1 +1 Forest|GRN|1 +1 Forest|M11|1 +1 Forest|MH2|1 +4 Gladecover Scout|M12|1 +1 Horizon Canopy|FUT|1 +1 Island|8ED|1 +4 Kor Spiritdancer|ROE|1 +4 Leyline of Sanctity|M11|1 +4 Light-Paws, Emperor's Voice|NEO|3 +1 Plains|AFR|1 +1 Plains|ELD|1 +1 Plains|GRN|1 +3 Plains|MKM|1 +1 Plains|SOI|1 +3 Rancor|M13|1 +3 Razorverge Thicket|ONE|1 +4 Sanctifier en-Vec|MH2|3 +4 Sheltered by Ghosts|DSK|1 +3 Slippery Bogle|EVE|1 +1 Spirit Mantle|M12|1 +3 Temple Garden|GRN|1 +4 Windswept Heath|MH3|2 +[Sideboard] +3 Audacity|BRO|1 +2 Gryff's Boon|SOI|1 +4 Pick Your Poison|MKM|1 +1 Silhana Ledgewalker|GPT|1 +1 Slippery Bogle|EVE|1 +4 Spider Umbra|ROE|1 diff --git a/forge-gui/res/geneticaidecks/GAM_20_Phantasmal Image based deck_22_1.dck b/forge-gui/res/geneticaidecks/GAM_20_Phantasmal Image based deck_22_1.dck new file mode 100644 index 00000000000..3b50bd3f65c --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_20_Phantasmal Image based deck_22_1.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_20_Phantasmal Image based deck_22_1 +[Main] +4 Aether Vial|DST|1 +4 Cavern of Souls|LCI|2 +3 Drogskol Captain|DKA|1 +4 Flooded Strand|MH3|1 +2 Island|DMU|1 +1 Island|FDN|1 +1 Island|ISD|1 +1 Island|MH2|1 +4 Mausoleum Wanderer|EMN|1 +4 Phantasmal Image|M12|1 +1 Plains|AKH|1 +1 Plains|FDN|1 +1 Plains|MOM|1 +1 Plains|ONE|1 +3 Rattlechains|SOI|1 +4 Seachrome Coast|ONE|2 +3 Selfless Spirit|EMN|1 +4 Shacklegeist|M21|1 +3 Skyclave Apparition|ZNR|2 +1 Spectral Sailor|FDN|1 +2 Spell Queller|EMN|1 +4 Supreme Phantom|M19|1 +3 Unsettled Mariner|MH1|1 +1 Wanderwine Hub|LRW|1 +[Sideboard] +3 Cemetery Illuminator|DBL|1 +4 Kira, Great Glass-Spinner|BOK|1 +4 Spell Pierce|MM3|1 +4 Surge of Salvation|MOM|1 diff --git a/forge-gui/res/geneticaidecks/GAM_21_Adeline, Resplendent Cathar based deck_156_0.dck b/forge-gui/res/geneticaidecks/GAM_21_Adeline, Resplendent Cathar based deck_156_0.dck new file mode 100644 index 00000000000..8dfe74b06f2 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_21_Adeline, Resplendent Cathar based deck_156_0.dck @@ -0,0 +1,35 @@ +[metadata] +Name=GAM_21_Adeline, Resplendent Cathar based deck_156_0 +[Main] +4 Adeline, Resplendent Cathar|MID|2 +4 Anafenza, the Foremost|KTK|1 +4 Andúril, Flame of the West|LTR|5 +1 Eiganjo Castle|CHK|1 +4 Eiganjo, Seat of the Empire|NEO|2 +4 Flowering of the White Tree|LTR|2 +1 Forest|ACR|1 +1 Forest|MID|1 +3 Frodo, Sauron's Bane|LTR|3 +2 Isamaru, Hound of Konda|CHK|1 +4 Kellan, Daring Traveler|LCI|2 +4 Mox Amber|DOM|1 +1 Plains|ACR|1 +1 Plains|DOM|1 +1 Plains|DTK|1 +2 Plains|KTK|1 +1 Plains|MID|1 +1 Plains|MOM|1 +1 Plains|ONE|1 +1 Plains|WAR|1 +4 Plaza of Heroes|DMU|1 +4 Samwise the Stouthearted|LTR|4 +1 Swamp|LTR|1 +1 Swamp|RTR|1 +2 Thalia, Guardian of Thraben|DBL|1 +3 Tomik, Distinguished Advokist|WAR|1 +[Sideboard] +1 Horn of the Mark|LTR|1 +3 Inti, Seneschal of the Sun|LCI|2 +4 Kari Zev, Skyship Raider|AER|1 +4 Legion's Landing|XLN|1 +3 Zurgo Bellstriker|DTK|1 diff --git a/forge-gui/res/geneticaidecks/GAM_22_Samwise the Stouthearted based deck_53_1.dck b/forge-gui/res/geneticaidecks/GAM_22_Samwise the Stouthearted based deck_53_1.dck new file mode 100644 index 00000000000..1f899191e74 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_22_Samwise the Stouthearted based deck_53_1.dck @@ -0,0 +1,34 @@ +[metadata] +Name=GAM_22_Samwise the Stouthearted based deck_53_1 +[Main] +2 Aether Vial|DST|1 +4 Anointed Peacekeeper|DMU|2 +3 Archon of Emeria|ZNR|1 +2 Ghost Quarter|MD1|1 +4 Giver of Runes|MH1|1 +2 Kaldra Compleat|MH2|2 +4 Leonin Arbiter|SOM|1 +1 Plains|9ED|1 +1 Plains|AFR|1 +1 Plains|KLD|1 +1 Plains|LTR|1 +1 Plains|NEO|1 +2 Plains|NPH|1 +1 Plains|OTJ|1 +1 Plains|ROE|1 +2 Plains|SNC|1 +1 Plains|SOM|1 +5 Plains|STX|1 +3 Plains|TSP|1 +1 Plains|ZNR|1 +4 Samwise the Stouthearted|LTR|5 +3 Skyclave Apparition|ZNR|2 +4 Solitude|MH2|1 +4 Stoneforge Mystic|WWK|1 +3 Thalia, Guardian of Thraben|VOW|1 +[Sideboard] +2 Batterskull|NPH|1 +1 Kaldra Compleat|MH2|2 +4 Lion Sash|NEO|3 +4 Serra Paragon|DMU|2 +4 Sword of Fire and Ice|MMA|1 diff --git a/forge-gui/res/geneticaidecks/GAM_23_Adeline, Resplendent Cathar based deck_2_1.dck b/forge-gui/res/geneticaidecks/GAM_23_Adeline, Resplendent Cathar based deck_2_1.dck new file mode 100644 index 00000000000..1f08987c9b9 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_23_Adeline, Resplendent Cathar based deck_2_1.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAM_23_Adeline, Resplendent Cathar based deck_2_1 +[Main] +4 Adeline, Resplendent Cathar|MID|1 +4 Andúril, Flame of the West|LTR|1 +2 Eiganjo Castle|CHK|1 +3 Eiganjo, Seat of the Empire|NEO|2 +3 Flowering of the White Tree|LTR|1 +2 Frodo, Sauron's Bane|LTR|2 +1 Giada, Font of Hope|FDN|2 +4 Isamaru, Hound of Konda|CHK|1 +4 Kellan, Daring Traveler|LCI|2 +2 Kytheon, Hero of Akros|ORI|1 +1 Legion's Landing|XLN|1 +3 Merry, Esquire of Rohan|LTR|5 +1 Mountain|LTR|1 +1 Mountain|RAV|1 +4 Mox Amber|DOM|1 +4 Plains|ACR|1 +1 Plains|DMU|1 +1 Plains|MID|1 +1 Plains|RAV|1 +1 Plains|WAR|1 +1 Plains|XLN|1 +4 Plaza of Heroes|DMU|2 +4 Tomik, Distinguished Advokist|WAR|1 +4 Zurgo Bellstriker|DTK|1 +[Sideboard] +1 Horn of the Mark|LTR|1 +3 Inti, Seneschal of the Sun|LCI|1 +3 Kari Zev, Skyship Raider|AER|1 +4 Shadowspear|THB|2 +4 Thalia, Guardian of Thraben|VOW|3 diff --git a/forge-gui/res/geneticaidecks/GAM_24_Cache Grab based deck_26_0.dck b/forge-gui/res/geneticaidecks/GAM_24_Cache Grab based deck_26_0.dck new file mode 100644 index 00000000000..b20ea53ca01 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_24_Cache Grab based deck_26_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_24_Cache Grab based deck_26_0 +[Main] +4 Amped Raptor|MH3|1 +4 Boseiju, Who Endures|NEO|2 +4 Cache Grab|BLB|1 +3 Detective's Phoenix|MH3|1 +3 Dour Port-Mage|BLB|2 +1 Forest|KLD|1 +1 Forest|RNA|1 +4 Galvanic Discharge|MH3|1 +3 Greenbelt Rampager|AER|1 +4 Imperial Recruiter|MH2|2 +1 Island|LTR|1 +1 Island|MH2|1 +3 Malevolent Rumble|MH3|1 +1 Misty Rainforest|MH2|3 +1 Mountain|BLB|1 +1 Mountain|NEO|1 +3 Shifting Woodland|MH3|2 +2 Shrieking Drake|MH3|1 +3 Six|MH3|1 +2 Solar Transformer|MH3|2 +3 Stomping Ground|GTC|1 +1 Thassa's Oracle|THB|1 +3 Tune the Narrative|MH3|1 +4 Wooded Foothills|MH3|4 +[Sideboard] +4 Bloodbraid Marauder|MH2|2 +4 Dead // Gone|PLC|1 +4 Eternal Witness|5DN|1 +3 Thassa's Oracle|THB|1 diff --git a/forge-gui/res/geneticaidecks/GAM_25_Bloodtithe Harvester based deck_110_0.dck b/forge-gui/res/geneticaidecks/GAM_25_Bloodtithe Harvester based deck_110_0.dck new file mode 100644 index 00000000000..39b195f8c93 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_25_Bloodtithe Harvester based deck_110_0.dck @@ -0,0 +1,35 @@ +[metadata] +Name=GAM_25_Bloodtithe Harvester based deck_110_0 +[Main] +4 Bloodtithe Harvester|VOW|2 +3 Cavern of Souls|MM3|1 +2 Darkstar Augur|BLB|1 +2 Deep-Cavern Bat|LCI|1 +4 Duress|MD1|1 +1 Dusk Legion Zealot|RIX|1 +4 Goblin Chainwhirler|DOM|1 +1 Haunted Ridge|MID|1 +4 Hive of the Eye Tyrant|AFR|1 +2 Mountain|BLB|1 +1 Mountain|GRN|1 +1 Mountain|ORI|1 +2 Pharika's Chosen|JOU|1 +4 Smuggler's Copter|KLD|1 +4 Sorin, Imperious Bloodlord|M20|1 +4 Sulfurous Springs|DMU|1 +2 Swamp|AFR|1 +1 Swamp|LCI|1 +1 Swamp|M13|1 +1 Swamp|ORI|1 +1 Swamp|OTJ|1 +1 Swamp|WAR|1 +1 Swamp|WOE|1 +1 Unstoppable Slasher|DSK|3 +4 Vampire Nighthawk|M13|1 +4 Vein Ripper|MKM|3 +[Sideboard] +1 Banehound|WAR|1 +4 Call of the Death-Dweller|IKO|1 +3 Dusk Legion Zealot|RIX|1 +4 Starscape Cleric|BLB|1 +3 Valley Rotcaller|BLB|1 diff --git a/forge-gui/res/geneticaidecks/GAM_26_Smuggler's Surprise based deck_49_0.dck b/forge-gui/res/geneticaidecks/GAM_26_Smuggler's Surprise based deck_49_0.dck new file mode 100644 index 00000000000..a6065c402e6 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_26_Smuggler's Surprise based deck_49_0.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAM_26_Smuggler's Surprise based deck_49_0 +[Main] +3 Bloodghast|ZEN|1 +4 Creeping Chill|GRN|1 +1 Forest|M19|1 +1 Forest|MH3|1 +1 Forest|OTJ|1 +3 Gravecrawler|DKA|1 +4 Hedron Crab|ZEN|1 +2 Island|LCI|1 +1 Island|RAV|1 +1 Merfolk Secretkeeper|ELD|2 +1 Misty Rainforest|ZEN|1 +3 Narcomoeba|MMA|1 +4 Polluted Delta|MH3|1 +4 Prized Amalgam|SOI|1 +2 Satoru, the Infiltrator|OTJ|2 +4 Smuggler's Surprise|OTJ|2 +4 Souls of the Lost|LCI|2 +3 Stitcher's Supplier|M19|1 +1 Swamp|MH2|1 +1 Swamp|MH3|1 +1 Swamp|RAV|1 +2 Swamp|SOI|1 +3 Vengevine|ROE|1 +3 Verdant Catacombs|MH2|3 +3 Watery Grave|GTC|1 +[Sideboard] +3 Cruel Somnophage|WOE|1 +4 Glimpse the Unthinkable|RAV|1 +4 Otherworldly Gaze|MID|1 +4 Wonder|MH2|1 diff --git a/forge-gui/res/geneticaidecks/GAM_27_Unsettled Mariner based deck_14_0.dck b/forge-gui/res/geneticaidecks/GAM_27_Unsettled Mariner based deck_14_0.dck new file mode 100644 index 00000000000..45a91264197 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_27_Unsettled Mariner based deck_14_0.dck @@ -0,0 +1,29 @@ +[metadata] +Name=GAM_27_Unsettled Mariner based deck_14_0 +[Main] +4 Cavern of Souls|LCI|7 +4 Collected Company|DTK|1 +4 Drogskol Captain|DKA|1 +2 Flooded Strand|MH3|1 +1 Forest|M21|1 +4 Hallowed Fountain|RTR|1 +1 Island|ACR|1 +1 Island|DSK|1 +3 Noble Hierarch|MM2|1 +2 Phantasmal Image|MM3|1 +1 Plains|DSK|1 +1 Plains|LCI|1 +4 Rattlechains|SOI|1 +4 Seachrome Coast|SOM|1 +3 Selfless Spirit|EMN|1 +4 Shacklegeist|M21|2 +3 Skyclave Apparition|ZNR|2 +3 Spell Queller|EMN|1 +4 Supreme Phantom|M19|1 +4 Unsettled Mariner|MH1|1 +3 Wanderwine Hub|LRW|1 +[Sideboard] +4 Kira, Great Glass-Spinner|BOK|1 +4 Spectral Sailor|M20|1 +4 Spell Pierce|XLN|1 +3 Surge of Salvation|MOM|1 diff --git a/forge-gui/res/geneticaidecks/GAM_28_Scorn-Blade Berserker based deck_26_1.dck b/forge-gui/res/geneticaidecks/GAM_28_Scorn-Blade Berserker based deck_26_1.dck new file mode 100644 index 00000000000..aaece609609 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_28_Scorn-Blade Berserker based deck_26_1.dck @@ -0,0 +1,26 @@ +[metadata] +Name=GAM_28_Scorn-Blade Berserker based deck_26_1 +[Main] +4 Agatha's Soul Cauldron|WOE|2 +4 Carrion Feeder|MH1|1 +4 Collector's Vault|WOE|1 +4 Dismember|MM2|1 +4 Gravecrawler|DKA|1 +4 Orcish Bowmasters|LTR|2 +4 Priest of Forgotten Gods|RNA|1 +4 Scorn-Blade Berserker|MOM|1 +4 Stalactite Stalker|LCI|2 +1 Swamp|AFR|1 +2 Swamp|ELD|1 +4 Swamp|MH3|1 +2 Swamp|MID|1 +1 Swamp|NEO|1 +1 Swamp|OTJ|1 +2 Swamp|ROE|1 +1 Swamp|WOE|1 +3 Takenuma, Abandoned Mire|NEO|1 +3 Walking Ballista|AER|1 +4 Witch's Cottage|ELD|1 +[Sideboard] +3 Gisa, the Hellraiser|OTJ|1 +4 Inquisition of Kozilek|ROE|1 diff --git a/forge-gui/res/geneticaidecks/GAM_29_Samwise the Stouthearted based deck_57_0.dck b/forge-gui/res/geneticaidecks/GAM_29_Samwise the Stouthearted based deck_57_0.dck new file mode 100644 index 00000000000..314be336f7b --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_29_Samwise the Stouthearted based deck_57_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_29_Samwise the Stouthearted based deck_57_0 +[Main] +4 Adeline, Resplendent Cathar|MID|1 +1 Andúril, Flame of the West|LTR|4 +4 Eiganjo Castle|CHK|1 +4 Eiganjo, Seat of the Empire|NEO|3 +4 Flowering of the White Tree|LTR|3 +4 Frodo, Sauron's Bane|LTR|4 +4 Isamaru, Hound of Konda|CHK|1 +3 Kytheon, Hero of Akros|ORI|1 +3 Legion's Landing|XLN|1 +3 Merry, Esquire of Rohan|LTR|3 +4 Mox Amber|DOM|1 +1 Plains|BRO|1 +1 Plains|DTK|1 +1 Plains|MID|1 +1 Plains|MOM|1 +1 Plains|ONE|1 +1 Plains|RAV|1 +4 Plaza of Heroes|DMU|2 +4 Samwise the Stouthearted|LTR|5 +1 Sunbaked Canyon|MH1|1 +3 Thalia, Guardian of Thraben|VOW|3 +3 Tomik, Distinguished Advokist|WAR|1 +1 Zurgo Bellstriker|DTK|1 +[Sideboard] +3 Horn of the Mark|LTR|1 +3 Inti, Seneschal of the Sun|LCI|2 +3 Kari Zev, Skyship Raider|AER|1 +2 Skrelv, Defector Mite|ONE|1 +4 Surge of Salvation|MOM|1 diff --git a/forge-gui/res/geneticaidecks/GAM_2_Spider Umbra based deck_9_1.dck b/forge-gui/res/geneticaidecks/GAM_2_Spider Umbra based deck_9_1.dck new file mode 100644 index 00000000000..7204fbe4e7a --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_2_Spider Umbra based deck_9_1.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAM_2_Spider Umbra based deck_9_1 +[Main] +4 All That Glitters|ELD|1 +4 Audacity|BRO|1 +4 Daybreak Coronet|MM2|1 +4 Ethereal Armor|DSK|1 +1 Forest|BRO|1 +1 Forest|WOE|1 +3 Gladecover Scout|M12|1 +3 Horizon Canopy|FUT|1 +1 Hyena Umbra|ROE|1 +4 Leyline of Sanctity|MM2|1 +4 Light-Paws, Emperor's Voice|NEO|2 +1 Plains|MH3|1 +1 Plains|NEO|1 +1 Plains|RIX|1 +1 Plains|XLN|1 +4 Razorverge Thicket|ONE|1 +3 Sanctifier en-Vec|MH2|1 +2 Sheltered by Ghosts|DSK|1 +3 Slippery Bogle|EVE|1 +4 Spider Umbra|ROE|1 +3 Temple Garden|GRN|1 +4 Windswept Heath|MH3|3 +[Sideboard] +3 Gryff's Boon|SOI|1 +3 Kor Spiritdancer|ROE|1 +4 Pick Your Poison|MKM|1 +2 Silhana Ledgewalker|GPT|1 +3 Spirit Mantle|M12|1 diff --git a/forge-gui/res/geneticaidecks/GAM_30_Eiganjo Castle based deck_11_1.dck b/forge-gui/res/geneticaidecks/GAM_30_Eiganjo Castle based deck_11_1.dck new file mode 100644 index 00000000000..315e9d07d53 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_30_Eiganjo Castle based deck_11_1.dck @@ -0,0 +1,35 @@ +[metadata] +Name=GAM_30_Eiganjo Castle based deck_11_1 +[Main] +4 Adeline, Resplendent Cathar|MID|2 +4 Andúril, Flame of the West|LTR|2 +4 Eiganjo Castle|CHK|1 +3 Eiganjo, Seat of the Empire|NEO|1 +2 Flowering of the White Tree|LTR|2 +4 Frodo, Sauron's Bane|LTR|3 +4 Giada, Font of Hope|SNC|3 +3 Kellan, Daring Traveler|LCI|2 +4 Kytheon, Hero of Akros|ORI|1 +3 Legion's Landing|XLN|1 +1 Mountain|MH2|1 +1 Plains|8ED|1 +1 Plains|DMU|1 +1 Plains|KTK|1 +1 Plains|NEO|1 +1 Plains|ONE|1 +1 Plains|RNA|1 +1 Plains|RTR|1 +1 Plains|SNC|1 +1 Plains|THB|1 +3 Plaza of Heroes|DMU|2 +1 Prismatic Vista|MH1|1 +3 Samwise the Stouthearted|LTR|3 +1 Thalia, Guardian of Thraben|DBL|1 +4 Urza's Saga|MH2|1 +3 Zurgo Bellstriker|DTK|1 +[Sideboard] +2 Horn of the Mark|LTR|2 +4 Inti, Seneschal of the Sun|LCI|2 +2 Kari Zev, Skyship Raider|AER|1 +4 Shadowspear|THB|1 +3 Thalia, Guardian of Thraben|DBL|1 diff --git a/forge-gui/res/geneticaidecks/GAM_31_Ob Nixilis, the Adversary based deck_55_0.dck b/forge-gui/res/geneticaidecks/GAM_31_Ob Nixilis, the Adversary based deck_55_0.dck new file mode 100644 index 00000000000..d84ea9c68e3 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_31_Ob Nixilis, the Adversary based deck_55_0.dck @@ -0,0 +1,36 @@ +[metadata] +Name=GAM_31_Ob Nixilis, the Adversary based deck_55_0 +[Main] +2 Archfiend's Vessel|M21|1 +1 Blood Crypt|DIS|1 +4 Blood Spatter Analysis|MKM|1 +4 Bloodghast|ZEN|1 +1 Callous Sell-Sword|WOE|1 +4 Claim // Fame|HOU|1 +4 Demonic Embrace|M21|2 +3 Den of the Bugbear|AFR|2 +1 Mountain|MH2|1 +1 Mountain|NEO|1 +1 Mountain|WOE|1 +4 Ob Nixilis, the Adversary|SNC|4 +4 Persist|MH2|2 +4 Raucous Theater|MKM|1 +3 Rotting Regisaur|M20|1 +3 Souls of the Lost|LCI|1 +3 Stitcher's Supplier|M19|1 +1 Swamp|AFR|1 +1 Swamp|KHM|1 +1 Swamp|MH3|1 +2 Swamp|MKM|1 +1 Swamp|NEO|1 +1 Swamp|ORI|1 +1 Swamp|SNC|1 +1 Swamp|THS|1 +3 Takenuma, Abandoned Mire|NEO|2 +1 Unearth|MH1|1 +[Sideboard] +2 Archfiend's Vessel|M21|1 +4 Carrion Feeder|MH1|1 +3 Corrupted Conviction|MOM|1 +4 Village Rites|M21|1 +2 Voldaren Epicure|VOW|1 diff --git a/forge-gui/res/geneticaidecks/GAM_32_Fractured Sanity based deck_72_1.dck b/forge-gui/res/geneticaidecks/GAM_32_Fractured Sanity based deck_72_1.dck new file mode 100644 index 00000000000..0d6ab3279eb --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_32_Fractured Sanity based deck_72_1.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAM_32_Fractured Sanity based deck_72_1 +[Main] +4 Archive Trap|ZEN|1 +4 Baleful Mastery|STX|2 +2 Crypt Incursion|DGM|1 +4 Echoing Truth|MMA|1 +3 Ensnaring Bridge|8ED|1 +4 Field of Ruin|MID|1 +4 Fractured Sanity|MH2|2 +4 Glimpse the Unthinkable|GK1|1 +1 Island|AFR|1 +2 Island|GK1|1 +1 Island|M12|1 +1 Island|MRD|1 +2 Island|ONE|1 +1 Island|RAV|1 +1 Island|ZEN|1 +4 Jace, the Perfected Mind|ONE|5 +4 Polluted Delta|MH3|2 +3 Ruin Crab|ZNR|2 +4 Shelldock Isle|LRW|1 +1 Swamp|ELD|1 +1 Swamp|ONE|1 +1 Swamp|RAV|1 +3 Tasha's Hideous Laughter|AFR|1 +1 Watery Grave|RAV|1 +[Sideboard] +3 Fatal Push|ACR|1 +3 Hedron Crab|ZEN|1 +3 Soul-Guide Lantern|WOE|1 +3 Surgical Extraction|MM2|1 +3 Visions of Beyond|M12|1 diff --git a/forge-gui/res/geneticaidecks/GAM_33_Collected Company based deck_43_1.dck b/forge-gui/res/geneticaidecks/GAM_33_Collected Company based deck_43_1.dck new file mode 100644 index 00000000000..8c9b9af9b60 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_33_Collected Company based deck_43_1.dck @@ -0,0 +1,34 @@ +[metadata] +Name=GAM_33_Collected Company based deck_43_1 +[Main] +2 Atarka's Command|DTK|1 +4 Break Out|MKM|1 +4 Burning-Tree Emissary|GTC|1 +4 Collected Company|DTK|1 +2 Commercial District|MKM|2 +2 Dryad Arbor|FUT|1 +1 Forest|DMU|1 +2 Forest|MH2|1 +1 Forest|MKM|1 +2 Gallia of the Endless Dance|THB|2 +1 Goblin Anarchomancer|MH2|2 +4 Goblin Guide|MM3|1 +1 Hexdrinker|MH1|1 +4 Ignoble Hierarch|MH2|3 +3 Karplusan Forest|DMU|1 +1 Mountain|GRN|1 +2 Mountain|MH3|1 +1 Mountain|NEO|1 +1 Mountain|THB|1 +2 Narnam Renegade|AER|1 +2 Ragavan, Nimble Pilferer|MH2|1 +4 Rage Forger|MOR|1 +4 Reckless Bushwhacker|OGW|1 +2 Shinka, the Bloodsoaked Keep|CHK|1 +4 Wooded Foothills|MH3|3 +[Sideboard] +4 Bomat Courier|KLD|1 +4 Elvish Visionary|ALA|1 +3 Eternal Witness|MMA|1 +2 Gallia of the Endless Dance|THB|2 +2 Pelt Collector|GRN|1 diff --git a/forge-gui/res/geneticaidecks/GAM_34_Cemetery Illuminator based deck_75_0.dck b/forge-gui/res/geneticaidecks/GAM_34_Cemetery Illuminator based deck_75_0.dck new file mode 100644 index 00000000000..906f4ee0bf1 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_34_Cemetery Illuminator based deck_75_0.dck @@ -0,0 +1,35 @@ +[metadata] +Name=GAM_34_Cemetery Illuminator based deck_75_0 +[Main] +4 Aether Vial|MMA|1 +3 Cavern of Souls|LCI|3 +4 Cemetery Illuminator|VOW|2 +4 Drogskol Captain|DKA|1 +4 Flooded Strand|MH3|3 +2 Hallowed Fountain|RNA|1 +1 Island|DMU|1 +1 Island|DSK|1 +1 Island|DTK|1 +1 Island|ISD|1 +1 Island|KHM|1 +1 Island|MKM|1 +1 Island|RNA|1 +1 Island|VOW|1 +3 Kira, Great Glass-Spinner|MMA|1 +4 Mausoleum Wanderer|EMN|1 +1 Plains|MOM|1 +2 Plains|OTJ|1 +1 Plains|RNA|1 +1 Plains|SOM|1 +4 Selfless Spirit|EMN|1 +2 Skyclave Apparition|ZNR|1 +2 Spectral Sailor|FDN|1 +4 Spell Queller|EMN|1 +4 Supreme Phantom|M19|1 +3 Unsettled Mariner|MH1|1 +[Sideboard] +4 Get Lost|LCI|1 +1 Kira, Great Glass-Spinner|MMA|1 +3 Nebelgast Herald|EMN|1 +4 Spell Pierce|XLN|1 +3 Surge of Salvation|MOM|1 diff --git a/forge-gui/res/geneticaidecks/GAM_35_Domain Aggro Zoo Generated Deck_25_0.dck b/forge-gui/res/geneticaidecks/GAM_35_Domain Aggro Zoo Generated Deck_25_0.dck new file mode 100644 index 00000000000..fd0bbcdb262 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_35_Domain Aggro Zoo Generated Deck_25_0.dck @@ -0,0 +1,29 @@ +[metadata] +Name=GAM_35_Domain Aggro Zoo Generated Deck_25_0 +[Main] +1 Forest|FDN|1 +1 Forest|IKO|1 +1 Forest|MKM|1 +4 Gaea's Might|DMU|1 +1 Island|GRN|1 +1 Kumano Faces Kakkazan|NEO|1 +3 Leyline Binding|DMU|1 +4 Leyline of the Guildpact|MKM|1 +1 Mountain|DOM|1 +1 Mountain|NEO|1 +3 Nishoba Brawler|DMU|2 +4 Overgrown Tomb|GRN|1 +1 Plains|IKO|1 +1 Plains|MH2|1 +4 Ragavan, Nimble Pilferer|MH2|2 +4 Sacred Foundry|RAV|1 +3 Savai Triome|IKO|2 +3 Scion of Draco|MH2|1 +1 Stubborn Denial|KTK|1 +2 Temple Garden|RAV|1 +4 Territorial Kavu|MH2|2 +4 Tribal Flames|MM2|1 +4 Wild Nacatl|ALA|1 +4 Windswept Heath|MH3|4 +[Sideboard] + diff --git a/forge-gui/res/geneticaidecks/GAM_36_Mishra's Research Desk based deck_32_0.dck b/forge-gui/res/geneticaidecks/GAM_36_Mishra's Research Desk based deck_32_0.dck new file mode 100644 index 00000000000..de7754b6c38 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_36_Mishra's Research Desk based deck_32_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_36_Mishra's Research Desk based deck_32_0 +[Main] +2 Angrath's Rampage|WAR|1 +4 Blood Crypt|RNA|1 +4 Bloodstained Mire|MH3|3 +4 Boseiju, Who Endures|NEO|3 +4 Fable of the Mirror-Breaker|NEO|3 +4 Fatal Push|ACR|1 +1 Forest|ACR|1 +1 Forest|SNC|1 +4 Grist, the Hunger Tide|MH2|1 +2 Maelstrom Pulse|MMA|1 +4 Mishra's Research Desk|BRO|1 +3 Molten Collapse|LCI|1 +1 Mountain|10E|1 +1 Mountain|LTR|1 +3 Orcish Bowmasters|LTR|3 +2 Overgrown Tomb|RAV|1 +3 Scavenging Ooze|FDN|1 +1 Swamp|DTK|1 +1 Swamp|RNA|1 +1 Swamp|THB|1 +3 Tarmogoyf|MM2|1 +3 Terminate|ACR|1 +1 Unholy Heat|MH2|1 +3 Urza's Saga|MH2|2 +[Sideboard] +4 Elvish Reclaimer|M20|1 +4 Haywire Mite|BRO|1 +4 Inquisition of Kozilek|MD1|1 +3 Shadowspear|THB|2 diff --git a/forge-gui/res/geneticaidecks/GAM_37_Narnam Renegade based deck_30_1.dck b/forge-gui/res/geneticaidecks/GAM_37_Narnam Renegade based deck_30_1.dck new file mode 100644 index 00000000000..db0f9367752 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_37_Narnam Renegade based deck_30_1.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_37_Narnam Renegade based deck_30_1 +[Main] +4 Break Out|MKM|1 +3 Dryad Arbor|FUT|1 +1 Forest|GRN|1 +1 Forest|KHM|1 +2 Forest|LRW|1 +1 Forest|MH3|1 +3 Gallia of the Endless Dance|THB|2 +4 Ghor-Clan Rampager|MM3|1 +3 Goblin Anarchomancer|MH2|1 +3 Goblin Guide|MM3|1 +4 Harmonic Prodigy|MH2|1 +1 Mountain|DMU|1 +1 Mountain|DTK|1 +1 Mountain|FDN|1 +2 Mountain|KHM|1 +1 Mountain|MH2|1 +1 Mountain|NEO|1 +4 Narnam Renegade|AER|1 +4 Ragavan, Nimble Pilferer|MH2|2 +3 Rage Forger|MOR|1 +3 Reckless Bushwhacker|OGW|1 +4 Stomping Ground|RNA|1 +3 Wild Nacatl|ALA|1 +3 Wooded Foothills|MH3|2 +[Sideboard] +4 Elvish Visionary|ALA|1 +4 Hexdrinker|MH1|1 +4 Hidden Herbalists|AER|1 +3 Pelt Collector|GRN|1 diff --git a/forge-gui/res/geneticaidecks/GAM_38_Vein Ripper based deck_36_1.dck b/forge-gui/res/geneticaidecks/GAM_38_Vein Ripper based deck_36_1.dck new file mode 100644 index 00000000000..98f90296a6b --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_38_Vein Ripper based deck_36_1.dck @@ -0,0 +1,34 @@ +[metadata] +Name=GAM_38_Vein Ripper based deck_36_1 +[Main] +1 Bloodthirsty Aerialist|M20|1 +4 Bloodtithe Harvester|VOW|1 +2 Case of the Uneaten Feast|MKM|1 +2 Cavern of Souls|LCI|5 +3 Deep-Cavern Bat|LCI|2 +3 Duress|M21|1 +4 Dusk Legion Zealot|RIX|1 +2 Goblin Chainwhirler|DOM|1 +4 Hive of the Eye Tyrant|AFR|1 +4 Lifecreed Duo|BLB|1 +1 Mountain|MKM|1 +1 Mountain|WAR|1 +4 Mutavault|M14|1 +1 Plains|GRN|1 +1 Plains|KLD|1 +4 Preacher of the Schism|LCI|1 +4 Smuggler's Copter|KLD|1 +4 Sulfurous Springs|DMU|1 +1 Swamp|DSK|1 +1 Swamp|GRN|1 +1 Swamp|KHM|1 +1 Swamp|KLD|1 +1 Swamp|M20|1 +3 Vampire Nighthawk|FDN|1 +3 Vein Ripper|MKM|3 +[Sideboard] +1 Case of the Uneaten Feast|MKM|1 +4 Darkstar Augur|BLB|2 +4 Ruin-Lurker Bat|LCI|1 +3 Starscape Cleric|BLB|1 +3 Unstoppable Slasher|DSK|2 diff --git a/forge-gui/res/geneticaidecks/GAM_39_Memory Deluge based deck_129_0.dck b/forge-gui/res/geneticaidecks/GAM_39_Memory Deluge based deck_129_0.dck new file mode 100644 index 00000000000..b484b90b397 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_39_Memory Deluge based deck_129_0.dck @@ -0,0 +1,35 @@ +[metadata] +Name=GAM_39_Memory Deluge based deck_129_0 +[Main] +2 Beza, the Bounding Spring|BLB|1 +3 Cryptic Coat|MKM|2 +4 Geist of Saint Traft|ISD|1 +4 Hallowed Fountain|DIS|1 +4 Hengegate Pathway|KHM|1 +1 Island|BFZ|1 +1 Island|BLB|1 +2 Island|DMU|1 +1 Island|FDN|1 +1 Island|ISD|1 +1 Island|MKM|1 +4 Memory Deluge|MID|2 +1 Meticulous Archive|MKM|2 +4 No More Lies|MKM|2 +4 Path to Exile|CFX|1 +1 Plains|IKO|1 +1 Plains|KHM|1 +1 Plains|NEO|1 +1 Plains|ONE|1 +1 Plains|THB|1 +1 Plains|WAR|1 +4 Portable Hole|AFR|2 +3 Seachrome Coast|ONE|2 +2 Shark Typhoon|IKO|1 +4 Teferi, Hero of Dominaria|DOM|1 +4 Teferi, Time Raveler|WAR|1 +[Sideboard] +4 Deduce|MKM|1 +3 Saving Grasp|DKA|1 +4 Sunfall|MOM|1 +1 The Wandering Emperor|NEO|3 +3 Volatile Stormdrake|MH3|3 diff --git a/forge-gui/res/geneticaidecks/GAM_3_Kari Zev, Skyship Raider based deck_78_1.dck b/forge-gui/res/geneticaidecks/GAM_3_Kari Zev, Skyship Raider based deck_78_1.dck new file mode 100644 index 00000000000..c71993d8637 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_3_Kari Zev, Skyship Raider based deck_78_1.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAM_3_Kari Zev, Skyship Raider based deck_78_1 +[Main] +3 Adeline, Resplendent Cathar|MID|1 +2 Anafenza, the Foremost|KTK|1 +3 Andúril, Flame of the West|LTR|4 +3 Eiganjo Castle|CHK|1 +4 Eiganjo, Seat of the Empire|NEO|3 +4 Flowering of the White Tree|LTR|3 +2 Frodo, Sauron's Bane|LTR|1 +3 Giada, Font of Hope|FDN|2 +3 Isamaru, Hound of Konda|CHK|1 +4 Kari Zev, Skyship Raider|AER|1 +1 Kellan, Daring Traveler|LCI|2 +2 Kytheon, Hero of Akros|ORI|1 +1 Mountain|KTK|1 +1 Mountain|LTR|1 +1 Plains|CHK|1 +1 Plains|DTK|1 +1 Plains|FDN|1 +1 Plains|MID|1 +1 Plains|XLN|1 +4 Plaza of Heroes|DMU|2 +4 Prismatic Vista|MH1|1 +4 Samwise the Stouthearted|LTR|2 +4 Tomik, Distinguished Advokist|WAR|1 +3 Zurgo Bellstriker|DTK|1 +[Sideboard] +1 Giada, Font of Hope|FDN|2 +3 Horn of the Mark|LTR|1 +4 Inti, Seneschal of the Sun|LCI|1 +4 Legion's Landing|XLN|1 +3 Shadowspear|THB|2 diff --git a/forge-gui/res/geneticaidecks/GAM_40_Lion Sash based deck_120_0.dck b/forge-gui/res/geneticaidecks/GAM_40_Lion Sash based deck_120_0.dck new file mode 100644 index 00000000000..76f89617ea9 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_40_Lion Sash based deck_120_0.dck @@ -0,0 +1,36 @@ +[metadata] +Name=GAM_40_Lion Sash based deck_120_0 +[Main] +3 Aether Vial|MMA|1 +4 Anointed Peacekeeper|DMU|1 +3 Archon of Emeria|ZNR|1 +4 Eiganjo, Seat of the Empire|NEO|2 +2 Emeria's Call|ZNR|1 +1 Flagstones of Trokair|TSP|1 +4 Ghost Quarter|DIS|1 +4 Giver of Runes|MH1|1 +4 Kaldra Compleat|MH2|2 +2 Leonin Arbiter|SOM|1 +3 Linvala, Keeper of Silence|MM3|1 +4 Lion Sash|NEO|3 +1 Plains|AFR|1 +1 Plains|CHK|1 +2 Plains|LCI|1 +1 Plains|LTR|1 +2 Plains|M15|1 +1 Plains|NEO|1 +1 Plains|SOM|1 +1 Plains|STX|1 +1 Plains|TSP|1 +1 Plains|WOE|1 +1 Plains|ZNR|1 +3 Serra Paragon|DMU|2 +1 Skyclave Apparition|ZNR|1 +2 Stoneforge Mystic|WWK|1 +3 Volatile Fault|LCI|1 +[Sideboard] +4 Aven Interrupter|OTJ|2 +2 Flickerwisp|MMA|1 +2 Path to Exile|MM3|1 +3 Sword of Fire and Ice|MMA|1 +4 Thalia, Guardian of Thraben|VOW|3 diff --git a/forge-gui/res/geneticaidecks/GAM_4_Phlage, Titan of Fire's Fury based deck_115_0.dck b/forge-gui/res/geneticaidecks/GAM_4_Phlage, Titan of Fire's Fury based deck_115_0.dck new file mode 100644 index 00000000000..aef323af593 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_4_Phlage, Titan of Fire's Fury based deck_115_0.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAM_4_Phlage, Titan of Fire's Fury based deck_115_0 +[Main] +4 Aether Hub|KLD|1 +3 Ajani, Nacatl Pariah|MH3|3 +4 Amped Raptor|MH3|2 +2 Arena of Glory|MH3|2 +4 Arid Mesa|MH2|2 +1 Blood Moon|8ED|1 +3 Bonecrusher Giant|ELD|2 +4 Elegant Parlor|MKM|2 +4 Galvanic Discharge|MH3|1 +4 Guide of Souls|MH3|1 +1 Mountain|KLD|1 +1 Mountain|MKM|1 +1 Mountain|MOM|1 +4 Ocelot Pride|MH3|3 +1 Phelia, Exuberant Shepherd|MH3|1 +4 Phlage, Titan of Fire's Fury|MH3|3 +1 Plains|BLB|1 +2 Plains|KLD|1 +1 Plains|RNA|1 +1 Plains|SNC|1 +3 Ranger-Captain of Eos|MH1|1 +4 Static Prison|MH3|1 +3 The One Ring|LTR|2 +[Sideboard] +1 Bonecrusher Giant|ELD|2 +1 Chained to the Rocks|THS|1 +3 Dewdrop Cure|BLB|1 +3 Invasion of Gobakhan|MOM|1 +4 Jolted Awake|MH3|1 +3 Unstable Amulet|MH3|1 diff --git a/forge-gui/res/geneticaidecks/GAM_5_Extraction Specialist based deck_30_0.dck b/forge-gui/res/geneticaidecks/GAM_5_Extraction Specialist based deck_30_0.dck new file mode 100644 index 00000000000..cf9e4d4b84c --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_5_Extraction Specialist based deck_30_0.dck @@ -0,0 +1,34 @@ +[metadata] +Name=GAM_5_Extraction Specialist based deck_30_0 +[Main] +4 Adeline, Resplendent Cathar|MID|1 +1 Aether Vial|DST|1 +4 Ancient Ziggurat|CFX|1 +3 Cavern of Souls|LCI|2 +4 Champion of the Parish|ISD|1 +2 Charming Prince|ELD|2 +4 Coppercoat Vanguard|MAT|4 +1 Delney, Streetwise Lookout|MKM|2 +4 Esper Sentinel|MH2|1 +4 Extraction Specialist|SNC|2 +1 Island|LTR|1 +1 Island|OTJ|1 +1 Plains|FDN|1 +1 Plains|M14|1 +3 Plains|MID|1 +1 Plains|SOI|1 +2 Plains|XLN|1 +3 Ranger-Captain of Eos|MH1|1 +4 Satoru, the Infiltrator|OTJ|1 +3 Secluded Courtyard|NEO|2 +2 Solitude|MH2|1 +1 Swamp|FDN|1 +3 Thalia's Lieutenant|SOI|1 +2 Unclaimed Territory|XLN|1 +1 Unsettled Mariner|MH1|1 +[Sideboard] +1 Charming Prince|ELD|2 +4 Jirina, Dauntless General|MAT|1 +3 Kitesail Larcenist|LCI|1 +4 March of Otherworldly Light|NEO|3 +3 Zephyr Sentinel|BRO|1 diff --git a/forge-gui/res/geneticaidecks/GAM_6_Deeproot Pilgrimage based deck_67_1.dck b/forge-gui/res/geneticaidecks/GAM_6_Deeproot Pilgrimage based deck_67_1.dck new file mode 100644 index 00000000000..cbcb3fbbfc9 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_6_Deeproot Pilgrimage based deck_67_1.dck @@ -0,0 +1,29 @@ +[metadata] +Name=GAM_6_Deeproot Pilgrimage based deck_67_1 +[Main] +2 Aether Vial|MMA|1 +4 Deeproot Pilgrimage|LCI|2 +4 Force of Negation|MH1|1 +1 Glasspool Mimic|ZNR|1 +2 Island|ACR|1 +2 Island|CHK|1 +2 Island|DMU|1 +1 Island|DOM|1 +4 Island|ELD|1 +3 Island|FDN|1 +1 Island|LCI|1 +4 Island|MH2|1 +2 Island|NEO|1 +1 Island|ZEN|1 +3 Lord of Atlantis|TSB|1 +3 Master of the Pearl Trident|M13|1 +3 Merfolk Trickster|DOM|1 +4 Rishadan Dockhand|MH2|1 +3 Spreading Seas|ZEN|1 +2 Svyelun of Sea and Sky|MH2|3 +3 Tide Shaper|MH2|1 +3 Tishana's Tidebinder|LCI|1 +3 Vodalian Hexcatcher|DMU|2 +[Sideboard] +4 Harbinger of the Tides|FDN|1 +3 Squelch|CHK|1 diff --git a/forge-gui/res/geneticaidecks/GAM_7_Ragavan, Nimble Pilferer based deck_136_0.dck b/forge-gui/res/geneticaidecks/GAM_7_Ragavan, Nimble Pilferer based deck_136_0.dck new file mode 100644 index 00000000000..15fecfe8f25 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_7_Ragavan, Nimble Pilferer based deck_136_0.dck @@ -0,0 +1,26 @@ +[metadata] +Name=GAM_7_Ragavan, Nimble Pilferer based deck_136_0 +[Main] +4 Breeding Pool|RNA|1 +3 Defense Grid|8ED|1 +1 Forest|ALA|1 +1 Forest|RAV|1 +4 Leyline Binding|DMU|2 +4 Leyline of the Guildpact|MKM|1 +1 Mountain|8ED|1 +1 Mountain|MH2|1 +4 Nishoba Brawler|DMU|1 +4 Overgrown Tomb|GRN|1 +1 Plains|DMU|1 +4 Ragavan, Nimble Pilferer|MH2|2 +3 Sacred Foundry|RAV|1 +3 Scion of Draco|MH2|1 +4 Sword of Wealth and Power|BIG|4 +3 Temple Garden|GRN|1 +3 Territorial Kavu|MH2|1 +4 Tribal Flames|MMA|1 +2 Wild Nacatl|ALA|1 +4 Windswept Heath|MH3|4 +2 Wooded Foothills|MH3|3 +[Sideboard] +4 Stubborn Denial|KTK|1 diff --git a/forge-gui/res/geneticaidecks/GAM_8_Anduril, Flame of the West based deck_22_0.dck b/forge-gui/res/geneticaidecks/GAM_8_Anduril, Flame of the West based deck_22_0.dck new file mode 100644 index 00000000000..d21a461278e --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_8_Anduril, Flame of the West based deck_22_0.dck @@ -0,0 +1,34 @@ +[metadata] +Name=GAM_8_Andúril, Flame of the West based deck_22_0 +[Main] +4 Adeline, Resplendent Cathar|DBL|1 +4 Andúril, Flame of the West|LTR|5 +4 Eiganjo Castle|CHK|1 +3 Eiganjo, Seat of the Empire|NEO|1 +4 Flowering of the White Tree|LTR|1 +4 Frodo, Sauron's Bane|LTR|2 +4 Giada, Font of Hope|FDN|2 +2 Isamaru, Hound of Konda|CHK|1 +2 Kellan, Daring Traveler|LCI|2 +3 Kytheon, Hero of Akros|ORI|1 +4 Legion's Landing|XLN|1 +3 Mox Amber|DOM|1 +4 Palantír of Orthanc|LTR|2 +1 Plains|ACR|1 +1 Plains|CHK|1 +1 Plains|GRN|1 +1 Plains|LTR|1 +3 Plains|MH2|1 +1 Plains|MOM|1 +2 Plains|NEO|1 +1 Plains|ONE|1 +1 Plains|THB|1 +1 Prismatic Vista|MH1|1 +1 Samwise the Stouthearted|LTR|5 +1 Thalia, Guardian of Thraben|VOW|2 +[Sideboard] +3 Horn of the Mark|LTR|1 +1 Sanctifier en-Vec|MH2|3 +4 Shadowspear|THB|1 +4 Skrelv, Defector Mite|ONE|2 +3 Surge of Salvation|MOM|1 diff --git a/forge-gui/res/geneticaidecks/GAM_9_Kellan, Daring Traveler based deck_5_0.dck b/forge-gui/res/geneticaidecks/GAM_9_Kellan, Daring Traveler based deck_5_0.dck new file mode 100644 index 00000000000..bca30110283 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAM_9_Kellan, Daring Traveler based deck_5_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAM_9_Kellan, Daring Traveler based deck_5_0 +[Main] +4 Adeline, Resplendent Cathar|MID|2 +1 Anafenza, the Foremost|KTK|1 +4 Andúril, Flame of the West|LTR|2 +3 Eiganjo Castle|CHK|1 +4 Eiganjo, Seat of the Empire|NEO|2 +4 Flowering of the White Tree|LTR|4 +3 Giada, Font of Hope|FDN|3 +2 Horn of the Mark|LTR|3 +4 Inti, Seneschal of the Sun|LCI|1 +3 Isamaru, Hound of Konda|CHK|1 +4 Kellan, Daring Traveler|LCI|2 +1 Mountain|BRO|1 +1 Mountain|NEO|1 +2 Mox Amber|DOM|1 +1 Plains|BRO|1 +2 Plains|CHK|1 +1 Plains|DOM|1 +3 Plains|KTK|1 +1 Plains|LCI|1 +1 Plains|MID|1 +4 Plaza of Heroes|DMU|2 +3 Samwise the Stouthearted|LTR|5 +4 Thalia, Guardian of Thraben|VOW|3 +[Sideboard] +3 Frodo, Sauron's Bane|LTR|2 +1 Isamaru, Hound of Konda|CHK|1 +4 Kytheon, Hero of Akros|ORI|1 +4 Legion's Landing|XLN|1 +3 Zurgo Bellstriker|DTK|1 diff --git a/forge-gui/res/geneticaidecks/GAS_10_Sharp-Eyed Rookie based deck_129_0.dck b/forge-gui/res/geneticaidecks/GAS_10_Sharp-Eyed Rookie based deck_129_0.dck new file mode 100644 index 00000000000..46722f08763 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_10_Sharp-Eyed Rookie based deck_129_0.dck @@ -0,0 +1,27 @@ +[metadata] +Name=GAS_10_Sharp-Eyed Rookie based deck_129_0 +[Main] +4 Axebane Ferox|MKM|1 +3 Crawling Barrens|FDN|1 +3 Flourishing Bloom-Kin|MKM|1 +1 Forest|BLB|1 +4 Forest|DMU|1 +1 Forest|FDN|1 +2 Forest|LCI|1 +5 Forest|MKM|1 +4 Forest|MOM|1 +1 Forest|ONE|1 +4 Forest|WOE|1 +4 Keen-Eyed Curator|BLB|1 +3 Nissa, Ascended Animist|ONE|5 +4 Outcaster Trailblazer|OTJ|2 +2 Overrun|FDN|1 +4 Pawpatch Recruit|BLB|1 +4 Quirion Beastcaller|DMU|2 +3 Scrapshooter|BLB|1 +4 Sharp-Eyed Rookie|MKM|2 +[Sideboard] +4 Cenote Scout|LCI|2 +3 Evolving Adaptive|ONE|1 +4 Goldvein Hydra|OTJ|2 +4 Hard-Hitting Question|MKM|1 diff --git a/forge-gui/res/geneticaidecks/GAS_11_Monstrous Rage based deck_79_1.dck b/forge-gui/res/geneticaidecks/GAS_11_Monstrous Rage based deck_79_1.dck new file mode 100644 index 00000000000..17a035a4615 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_11_Monstrous Rage based deck_79_1.dck @@ -0,0 +1,27 @@ +[metadata] +Name=GAS_11_Monstrous Rage based deck_79_1 +[Main] +4 Charming Scoundrel|WOE|2 +4 Emberheart Challenger|BLB|2 +4 Goddric, Cloaked Reveler|WOE|1 +3 Heartfire Hero|BLB|1 +3 Hired Claw|BLB|2 +4 Lightning Strike|DMU|2 +4 Monastery Swiftspear|BRO|1 +4 Monstrous Rage|WOE|1 +4 Mountain|BLB|1 +3 Mountain|DMU|1 +6 Mountain|FDN|1 +2 Mountain|ONE|1 +2 Mountain|OTJ|1 +4 Mountain|WOE|1 +1 Obliterating Bolt|FDN|1 +1 Rockface Village|BLB|1 +4 Squee, Dubious Monarch|DMU|3 +3 Witchstalker Frenzy|WOE|1 +[Sideboard] +3 Draconic Destiny|BRO|1 +4 Feldon, Ronom Excavator|BRO|2 +3 Great Train Heist|OTJ|1 +1 Hired Claw|BLB|2 +4 Might of the Meek|BLB|1 diff --git a/forge-gui/res/geneticaidecks/GAS_12_Aurelia's Vindicator based deck_2_1.dck b/forge-gui/res/geneticaidecks/GAS_12_Aurelia's Vindicator based deck_2_1.dck new file mode 100644 index 00000000000..ba9580e25d1 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_12_Aurelia's Vindicator based deck_2_1.dck @@ -0,0 +1,29 @@ +[metadata] +Name=GAS_12_Aurelia's Vindicator based deck_2_1 +[Main] +4 Aurelia's Vindicator|MKM|3 +2 Crumb and Get It|BLB|1 +4 Doorkeeper Thrull|MKM|1 +3 Dust Animus|OTJ|1 +1 Elesh Norn, Mother of Machines|ONE|5 +4 Jolly Gerbils|BLB|1 +3 Parting Gust|BLB|1 +3 Phyrexian Vindicator|ONE|2 +2 Plains|BLB|1 +3 Plains|DMU|1 +2 Plains|LCI|1 +1 Plains|MKM|1 +3 Plains|MOM|1 +5 Plains|ONE|1 +4 Plains|OTJ|1 +1 Plains|WOE|1 +4 Skrelv, Defector Mite|ONE|2 +3 Steel Seraph|BRO|2 +4 Sunken Citadel|LCI|1 +4 Virtue of Loyalty|WOE|2 +[Sideboard] +4 Combat Thresher|BRO|1 +4 Ghost Vacuum|DSK|1 +1 Kutzil's Flanker|LCI|1 +3 Seal from Existence|MOM|1 +3 Serra Paragon|DMU|2 diff --git a/forge-gui/res/geneticaidecks/GAS_13_Blooming Marsh based deck_0_1.dck b/forge-gui/res/geneticaidecks/GAS_13_Blooming Marsh based deck_0_1.dck new file mode 100644 index 00000000000..414c45954be --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_13_Blooming Marsh based deck_0_1.dck @@ -0,0 +1,28 @@ +[metadata] +Name=GAS_13_Blooming Marsh based deck_0_1 +[Main] +3 Aclazotz, Deepest Betrayal|LCI|2 +3 Ashiok, Wicked Manipulator|WOE|2 +3 Binding Negotiation|OTJ|1 +4 Blooming Marsh|OTJ|1 +1 Forest|WOE|1 +2 Innkeeper's Talent|BLB|1 +3 Lord Skitter, Sewer King|WOE|2 +1 Nissa, Ascended Animist|ONE|6 +3 Outrageous Robbery|MKM|1 +3 Sheoldred's Edict|ONE|1 +4 Sheoldred, the Apocalypse|DMU|5 +4 Swamp|BLB|1 +2 Swamp|DMU|1 +1 Swamp|LCI|1 +2 Swamp|MOM|1 +5 Swamp|OTJ|1 +3 Swamp|WOE|1 +4 The End|WOE|1 +3 Thrun, Breaker of Silence|ONE|2 +3 Underground Mortuary|MKM|2 +3 Vraska, Betrayal's Sting|ONE|5 +[Sideboard] +4 Hostile Investigator|BIG|3 +4 Nemata, Primeval Warden|DMU|1 +4 Pile On|MOM|1 diff --git a/forge-gui/res/geneticaidecks/GAS_14_Stenn, Paranoid Partisan based deck_22_1.dck b/forge-gui/res/geneticaidecks/GAS_14_Stenn, Paranoid Partisan based deck_22_1.dck new file mode 100644 index 00000000000..0bb7b17e16c --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_14_Stenn, Paranoid Partisan based deck_22_1.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAS_14_Stenn, Paranoid Partisan based deck_22_1 +[Main] +3 Adarkar Wastes|DMU|1 +3 Enduring Curiosity|DSK|4 +3 Enduring Innocence|DSK|2 +3 Entity Tracker|DSK|3 +3 Ethereal Armor|DSK|1 +4 Floodfarm Verge|DSK|2 +1 Grand Entryway // Elegant Rotunda|DSK|2 +4 Gremlin Tamer|DSK|1 +3 Island|DMU|1 +1 Island|ONE|1 +2 Optimistic Scavenger|DSK|2 +3 Ossification|ONE|2 +1 Plains|DMU|1 +1 Plains|MOM|1 +3 Plains|ONE|1 +1 Plains|WOE|1 +3 Proft's Eidetic Memory|MKM|2 +2 Restless Anchorage|LCI|1 +3 Seachrome Coast|ONE|1 +2 Shardmage's Rescue|DSK|2 +4 Sheltered by Ghosts|DSK|1 +3 Silent Hallcreeper|DSK|2 +4 Stenn, Paranoid Partisan|DMU|3 +[Sideboard] +4 Bottomless Pool // Locker Room|DSK|1 +3 Fear of Impostors|DSK|1 +1 Invasion of Theros|MOM|1 +3 Spellbook Vendor|WOE|1 +4 Trapped in the Screen|DSK|1 diff --git a/forge-gui/res/geneticaidecks/GAS_15_Decadent Dragon based deck_26_0.dck b/forge-gui/res/geneticaidecks/GAS_15_Decadent Dragon based deck_26_0.dck new file mode 100644 index 00000000000..0b3fe491be1 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_15_Decadent Dragon based deck_26_0.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_15_Decadent Dragon based deck_26_0 +[Main] +2 Bitter Reunion|BRO|1 +4 Blackcleave Cliffs|ONE|2 +3 Cavern of Souls|LCI|6 +4 Coiling Rebirth|BLB|2 +4 Decadent Dragon|WOE|2 +3 Geological Appraiser|LCI|2 +2 Harvester of Misery|BIG|3 +4 Jagged Barrens|OTJ|1 +1 Mountain|LCI|1 +2 Phyrexian Fleshgorger|BRO|1 +3 Plaza of Heroes|DMU|1 +3 Raucous Theater|MKM|2 +3 Sheoldred, the Apocalypse|DMU|4 +2 Swamp|BLB|1 +2 Swamp|DMU|1 +1 Swamp|LCI|1 +1 Swamp|MOM|1 +1 Swamp|OTJ|1 +4 The Cruelty of Gix|DMU|1 +4 The Infamous Cruelclaw|BLB|2 +3 Vein Ripper|MKM|3 +4 Virtue of Persistence|WOE|1 +[Sideboard] +4 Brotherhood's End|BRO|2 +3 Gloomfang Mauler|MOM|1 +3 Insatiable Avarice|OTJ|2 +4 Molten Collapse|LCI|1 +1 Sheoldred's Edict|ONE|1 diff --git a/forge-gui/res/geneticaidecks/GAS_16_Lord Skitter, Sewer King based deck_141_0.dck b/forge-gui/res/geneticaidecks/GAS_16_Lord Skitter, Sewer King based deck_141_0.dck new file mode 100644 index 00000000000..99751978290 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_16_Lord Skitter, Sewer King based deck_141_0.dck @@ -0,0 +1,29 @@ +[metadata] +Name=GAS_16_Lord Skitter, Sewer King based deck_141_0 +[Main] +2 Aclazotz, Deepest Betrayal|LCI|2 +3 Beza, the Bounding Spring|BLB|2 +3 Caves of Koilos|DMU|2 +4 Concealed Courtyard|OTJ|2 +4 Cut Down|DMU|2 +4 Deep-Cavern Bat|LCI|2 +4 Fabled Passage|BLB|2 +4 Lord Skitter, Sewer King|WOE|2 +1 Plains|LCI|1 +2 Plains|ONE|1 +1 Plains|OTJ|1 +1 Plains|WOE|1 +4 Preacher of the Schism|LCI|2 +4 Restless Fortress|WOE|2 +4 Season of the Burrow|BLB|1 +2 Swamp|BLB|1 +1 Swamp|DSK|1 +2 Swamp|ONE|1 +3 Virtue of Loyalty|WOE|1 +4 Virtue of Persistence|WOE|1 +3 Zoraline, Cosmos Caller|BLB|1 +[Sideboard] +3 Go for the Throat|BRO|1 +4 Legions to Ashes|BRO|2 +4 Liliana of the Veil|DMU|2 +4 Loran of the Third Path|BRO|1 diff --git a/forge-gui/res/geneticaidecks/GAS_17_Dawn's Truce based deck_77_1.dck b/forge-gui/res/geneticaidecks/GAS_17_Dawn's Truce based deck_77_1.dck new file mode 100644 index 00000000000..d3b8fdae11d --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_17_Dawn's Truce based deck_77_1.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_17_Dawn's Truce based deck_77_1 +[Main] +3 Banner of Kinship|FDN|2 +3 Cavern of Souls|LCI|1 +4 Citanul Stalwart|BRO|1 +4 Dawn's Truce|BLB|2 +3 Dwynen's Elite|FDN|1 +4 Elvish Archdruid|FDN|1 +1 Forest|BLB|1 +2 Forest|BRO|1 +2 Forest|DMU|1 +1 Forest|DSK|1 +1 Forest|FDN|1 +2 Forest|LCI|1 +2 Forest|MOM|1 +2 Forest|OTJ|1 +2 Genesis Wave|FDN|1 +4 Imperious Perfect|FDN|1 +4 Leaf-Crowned Visionary|DMU|2 +3 Llanowar Stalker|DMU|1 +2 Overgrown Zealot|DSK|1 +3 Plains|OTJ|1 +4 Secluded Courtyard|FDN|1 +4 Tyvar, the Pummeler|DSK|3 +[Sideboard] +4 Llanowar Loamspeaker|DMU|1 +2 Overgrown Zealot|DSK|1 +4 Tribute to the World Tree|MOM|2 +4 Werefox Bodyguard|WOE|1 +1 Wildborn Preserver|FDN|1 diff --git a/forge-gui/res/geneticaidecks/GAS_18_Braided Net based deck_157_0.dck b/forge-gui/res/geneticaidecks/GAS_18_Braided Net based deck_157_0.dck new file mode 100644 index 00000000000..5832c9f06da --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_18_Braided Net based deck_157_0.dck @@ -0,0 +1,29 @@ +[metadata] +Name=GAS_18_Braided Net based deck_157_0 +[Main] +3 Adarkar Wastes|DMU|1 +4 Braided Net|LCI|2 +4 Cavern of Souls|LCI|7 +4 Cityscape Leveler|BRO|2 +2 Island|BLB|1 +2 Island|DMU|1 +1 Island|MOM|1 +4 Meticulous Archive|MKM|2 +1 Plains|BRO|1 +2 Plains|LCI|1 +1 Plains|ONE|1 +2 Plains|OTJ|1 +3 Plaza of Heroes|DMU|1 +3 Relic of Legends|DMU|1 +4 Rona, Herald of Invasion|MOM|2 +4 Simulacrum Synthesizer|BIG|2 +4 Stenn, Paranoid Partisan|DMU|3 +4 The Mightstone and Weakstone|BRO|1 +4 Thousand Moons Smithy|LCI|2 +3 Thran Spider|BRO|2 +1 Urza, Lord Protector|BRO|1 +[Sideboard] +3 Assimilation Aegis|OTJ|1 +4 Patchwork Banner|BLB|1 +4 Stern Lesson|BRO|1 +4 Worldwalker Helm|BIG|2 diff --git a/forge-gui/res/geneticaidecks/GAS_19_Tyvar, Jubilant Brawler based deck_143_0.dck b/forge-gui/res/geneticaidecks/GAS_19_Tyvar, Jubilant Brawler based deck_143_0.dck new file mode 100644 index 00000000000..fe5d975c3c1 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_19_Tyvar, Jubilant Brawler based deck_143_0.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAS_19_Tyvar, Jubilant Brawler based deck_143_0 +[Main] +4 Blooming Marsh|OTJ|2 +4 Cavern of Souls|LCI|5 +4 Citanul Stalwart|BRO|1 +4 Dwynen's Elite|FDN|1 +4 Elvish Archdruid|FDN|1 +1 Forest|BLB|1 +1 Forest|DMU|1 +1 Forest|FDN|1 +1 Forest|LCI|1 +2 Forest|MOM|1 +1 Forest|ONE|1 +1 Forest|OTJ|1 +4 Leaf-Crowned Visionary|DMU|2 +4 Llanowar Stalker|DMU|1 +3 Overgrown Zealot|DSK|1 +1 Plains|FDN|1 +1 Plains|OTJ|1 +1 Swamp|MOM|1 +1 Swamp|ONE|1 +2 Three Tree City|BLB|5 +4 Tyvar, Jubilant Brawler|ONE|3 +4 Tyvar, the Pummeler|DSK|1 +3 Werefox Bodyguard|WOE|2 +4 Yavimaya Iconoclast|DMU|1 +[Sideboard] +3 Banner of Kinship|FDN|3 +1 Dwynen, Gilt-Leaf Daen|FDN|1 +4 Glissa Sunslayer|ONE|3 +4 Rustvine Cultivator|ONE|3 +3 Wildborn Preserver|FDN|1 diff --git a/forge-gui/res/geneticaidecks/GAS_1_Squee, Dubious Monarch based deck_154_0.dck b/forge-gui/res/geneticaidecks/GAS_1_Squee, Dubious Monarch based deck_154_0.dck new file mode 100644 index 00000000000..2ab6d769d14 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_1_Squee, Dubious Monarch based deck_154_0.dck @@ -0,0 +1,25 @@ +[metadata] +Name=GAS_1_Squee, Dubious Monarch based deck_154_0 +[Main] +4 Charming Scoundrel|WOE|1 +4 Emberheart Challenger|BLB|1 +4 Feldon, Ronom Excavator|BRO|1 +4 Goddric, Cloaked Reveler|WOE|2 +4 Lightning Strike|DMU|1 +2 Monastery Swiftspear|BRO|1 +3 Mountain|BLB|1 +1 Mountain|DMU|1 +6 Mountain|MKM|1 +3 Mountain|ONE|1 +6 Mountain|OTJ|1 +4 Mountain|WOE|1 +4 Obliterating Bolt|BRO|1 +4 Shock|MKM|1 +4 Squee, Dubious Monarch|DMU|3 +3 Witchstalker Frenzy|WOE|1 +[Sideboard] +1 Draconic Destiny|BRO|1 +4 Heartfire Hero|BLB|1 +4 Hired Claw|BLB|2 +2 Monastery Swiftspear|BRO|1 +4 Monstrous Rage|WOE|1 diff --git a/forge-gui/res/geneticaidecks/GAS_20_Spell Stutter based deck_35_1.dck b/forge-gui/res/geneticaidecks/GAS_20_Spell Stutter based deck_35_1.dck new file mode 100644 index 00000000000..d772bbd8023 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_20_Spell Stutter based deck_35_1.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_20_Spell Stutter based deck_35_1 +[Main] +4 Aclazotz, Deepest Betrayal|LCI|1 +4 Darkslick Shores|ONE|1 +3 Deep-Cavern Bat|LCI|1 +4 Faerie Dreamthief|WOE|1 +3 Faerie Mastermind|MOM|1 +3 Go for the Throat|BRO|1 +1 Ingenious Prodigy|WOE|1 +2 Invasion of Amonkhet|MOM|1 +1 Island|BLB|1 +3 Island|LCI|1 +1 Island|ONE|1 +1 Island|OTJ|1 +4 Kaito, Dancing Shadow|ONE|3 +4 Phantom Interference|OTJ|1 +2 Restless Reef|LCI|2 +1 Sheoldred, the Apocalypse|DMU|5 +4 Spell Stutter|WOE|1 +4 Steamcore Scholar|MKM|2 +2 Swamp|BLB|1 +1 Swamp|DMU|1 +1 Swamp|DSK|1 +2 Swamp|MOM|1 +1 Swamp|WOE|1 +4 Underground River|BRO|1 +[Sideboard] +4 Duelist of the Mind|OTJ|2 +4 Feed the Cycle|BLB|1 +2 Invasion of Amonkhet|MOM|1 diff --git a/forge-gui/res/geneticaidecks/GAS_21_Virtue of Persistence based deck_101_0.dck b/forge-gui/res/geneticaidecks/GAS_21_Virtue of Persistence based deck_101_0.dck new file mode 100644 index 00000000000..bd724589f0b --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_21_Virtue of Persistence based deck_101_0.dck @@ -0,0 +1,28 @@ +[metadata] +Name=GAS_21_Virtue of Persistence based deck_101_0 +[Main] +2 Aclazotz, Deepest Betrayal|LCI|2 +4 Blooming Marsh|OTJ|2 +4 Cut Down|DMU|2 +1 Forest|FDN|1 +1 Forest|ONE|1 +1 Forest|OTJ|1 +1 Forest|WOE|1 +4 Glissa Sunslayer|ONE|1 +3 Go for the Throat|BRO|1 +4 Llanowar Wastes|BRO|2 +4 Mosswood Dreadknight|WOE|2 +3 Restless Cottage|WOE|2 +4 Sentinel of the Nameless City|LCI|1 +4 Sheoldred, the Apocalypse|DMU|1 +3 Shoot the Sheriff|OTJ|1 +2 Swamp|DMU|1 +1 Swamp|LCI|1 +1 Swamp|ONE|1 +1 Swamp|OTJ|1 +1 Swamp|WOE|1 +4 Tear Asunder|DMU|1 +4 Temple of Malady|FDN|1 +3 Virtue of Persistence|WOE|2 +[Sideboard] +4 Caustic Bronco|OTJ|1 diff --git a/forge-gui/res/geneticaidecks/GAS_22_Pile On based deck_36_1.dck b/forge-gui/res/geneticaidecks/GAS_22_Pile On based deck_36_1.dck new file mode 100644 index 00000000000..0c5c2ec75be --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_22_Pile On based deck_36_1.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_22_Pile On based deck_36_1 +[Main] +4 Azure Beastbinder|BLB|2 +4 Cut Down|DMU|2 +4 Darkslick Shores|ONE|1 +4 Go for the Throat|BRO|1 +1 Island|DSK|1 +1 Island|FDN|1 +1 Island|OTJ|1 +2 Karumonix, the Rat King|ONE|3 +4 Lilypad Village|BLB|1 +4 Lord Skitter, Sewer King|WOE|1 +2 Nashi, Searcher in the Dark|DSK|2 +4 Nowhere to Run|DSK|1 +2 Patchwork Banner|BLB|1 +4 Persistent Marshstalker|BLB|1 +4 Pile On|MOM|2 +4 Restless Reef|LCI|1 +2 Swamp|BLB|1 +2 Swamp|DMU|1 +1 Swamp|ONE|1 +2 Swamp|OTJ|1 +1 Swamp|WOE|1 +3 Vren, the Relentless|BLB|2 +[Sideboard] +3 Anoint with Affliction|ONE|1 +3 Lord Skitter's Butcher|WOE|1 +2 Nashi, Searcher in the Dark|DSK|2 +3 Nezumi Informant|MOM|1 +4 Shoreline Looter|BLB|1 diff --git a/forge-gui/res/geneticaidecks/GAS_23_Palani's Hatcher based deck_148_0.dck b/forge-gui/res/geneticaidecks/GAS_23_Palani's Hatcher based deck_148_0.dck new file mode 100644 index 00000000000..8dab83b4673 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_23_Palani's Hatcher based deck_148_0.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_23_Palani's Hatcher based deck_148_0 +[Main] +3 Belligerent Yearling|LCI|2 +4 Bristling Backwoods|OTJ|1 +1 Forest|FDN|1 +2 Forest|LCI|1 +1 Forest|MOM|1 +2 Forest|ONE|1 +3 Forest|OTJ|1 +1 Forest|WOE|1 +3 Hulking Raptor|LCI|1 +4 Ixalli's Lorekeeper|LCI|1 +3 Llanowar Elves|FDN|2 +2 Mountain|FDN|1 +1 Mountain|LCI|1 +2 Mountain|MOM|1 +2 Mountain|ONE|1 +4 Palani's Hatcher|LCI|2 +4 Pugnacious Hammerskull|LCI|1 +4 Rampaging Raptor|MOM|1 +3 Scytheclaw Raptor|LCI|2 +4 Surrak, the Hunt Caller|FDN|1 +4 Terramorphic Expanse|ONE|1 +3 Triumphant Chomp|LCI|1 +[Sideboard] +3 Aloe Alchemist|OTJ|1 +3 Armored Kincaller|LCI|1 +3 Intrepid Paleontologist|LCI|2 +3 Itzquinth, Firstborn of Gishath|LCI|1 +3 Spinner of Souls|FDN|4 diff --git a/forge-gui/res/geneticaidecks/GAS_24_Kaito, Dancing Shadow based deck_77_0.dck b/forge-gui/res/geneticaidecks/GAS_24_Kaito, Dancing Shadow based deck_77_0.dck new file mode 100644 index 00000000000..258a66a5835 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_24_Kaito, Dancing Shadow based deck_77_0.dck @@ -0,0 +1,27 @@ +[metadata] +Name=GAS_24_Kaito, Dancing Shadow based deck_77_0 +[Main] +4 Darkslick Shores|ONE|2 +4 Deep-Cavern Bat|LCI|1 +4 Drannith Ruins|MAT|3 +3 Duelist of the Mind|OTJ|1 +3 Faerie Mastermind|MOM|2 +4 Feed the Cycle|BLB|1 +3 Invasion of Amonkhet|MOM|1 +1 Island|BLB|1 +2 Island|BRO|1 +1 Island|WOE|1 +4 Kaito, Dancing Shadow|ONE|3 +1 Mudflat Village|BLB|1 +4 Phantom Interference|OTJ|1 +4 Proft's Eidetic Memory|MKM|2 +3 Restless Reef|LCI|2 +1 Shoot the Sheriff|OTJ|1 +4 Spell Stutter|WOE|1 +4 Steamcore Scholar|MKM|2 +1 Swamp|BRO|1 +1 Swamp|WOE|1 +4 Underground River|BRO|1 +[Sideboard] +3 Ingenious Prodigy|WOE|1 +4 Sheoldred, the Apocalypse|DMU|5 diff --git a/forge-gui/res/geneticaidecks/GAS_25_Soul of Windgrace based deck_53_0.dck b/forge-gui/res/geneticaidecks/GAS_25_Soul of Windgrace based deck_53_0.dck new file mode 100644 index 00000000000..d305e2cd7dc --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_25_Soul of Windgrace based deck_53_0.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAS_25_Soul of Windgrace based deck_53_0 +[Main] +4 Abrade|FDN|2 +4 Aclazotz, Deepest Betrayal|LCI|2 +4 Fabled Passage|BLB|2 +3 Feed the Cycle|BLB|1 +1 Forest|DMU|1 +4 Glarb, Calamity's Augur|BLB|1 +3 Hedge Maze|MKM|2 +1 Island|DMU|1 +1 Island|ONE|1 +4 Long Goodbye|MKM|2 +3 Maha, Its Feathers Night|BLB|2 +4 Malicious Eclipse|LCI|1 +1 Mountain|LCI|1 +1 Mountain|MOM|1 +4 Soul of Windgrace|DMU|3 +1 Swamp|DMU|1 +2 Swamp|FDN|1 +2 Swamp|LCI|1 +1 Swamp|MKM|1 +2 Swamp|ONE|1 +1 Swamp|OTJ|1 +4 Undercity Sewers|MKM|1 +4 Virtue of Persistence|WOE|1 +1 Vren, the Relentless|BLB|1 +[Sideboard] +3 Chandra, Hope's Beacon|MOM|2 +4 Ghalta, Stampede Tyrant|LCI|1 +2 Gix's Command|BRO|2 +4 Lazav, Wearer of Faces|MKM|3 +2 Lost Jitte|BIG|3 diff --git a/forge-gui/res/geneticaidecks/GAS_26_Palani's Hatcher based deck_149_0.dck b/forge-gui/res/geneticaidecks/GAS_26_Palani's Hatcher based deck_149_0.dck new file mode 100644 index 00000000000..d3d5938c590 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_26_Palani's Hatcher based deck_149_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAS_26_Palani's Hatcher based deck_149_0 +[Main] +3 Bristling Backwoods|OTJ|1 +3 Cavern of Souls|LCI|1 +1 Forest|DSK|1 +1 Forest|FDN|1 +4 Forest|LCI|1 +1 Forest|ONE|1 +1 Forest|OTJ|1 +1 Forest|WOE|1 +3 Ghalta, Primal Hunger|FDN|1 +2 Hulking Raptor|LCI|2 +3 Intrepid Paleontologist|LCI|2 +4 Ixalli's Lorekeeper|LCI|1 +1 Mountain|DSK|1 +1 Mountain|FDN|1 +1 Mountain|ONE|1 +1 Mountain|OTJ|1 +2 Mountain|WOE|1 +4 Palani's Hatcher|LCI|2 +4 Pugnacious Hammerskull|LCI|1 +3 Rampaging Raptor|MOM|2 +4 Scytheclaw Raptor|LCI|1 +4 Surrak, the Hunt Caller|FDN|1 +4 Terramorphic Expanse|DSK|1 +4 Triumphant Chomp|LCI|1 +[Sideboard] +3 Armored Kincaller|LCI|1 +4 Itzquinth, Firstborn of Gishath|LCI|2 +4 Llanowar Elves|FDN|1 +4 Spinner of Souls|FDN|1 diff --git a/forge-gui/res/geneticaidecks/GAS_27_Rocco, Street Chef based deck_158_0.dck b/forge-gui/res/geneticaidecks/GAS_27_Rocco, Street Chef based deck_158_0.dck new file mode 100644 index 00000000000..09e46256a46 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_27_Rocco, Street Chef based deck_158_0.dck @@ -0,0 +1,29 @@ +[metadata] +Name=GAS_27_Rocco, Street Chef based deck_158_0 +[Main] +4 Annie Joins Up|OTJ|2 +3 Battlefield Forge|BRO|1 +3 Beza, the Bounding Spring|BLB|2 +3 Brushland|BRO|2 +4 Dragonhawk, Fate's Tempest|BLB|2 +4 Etali, Primal Conqueror|MOM|2 +1 Forest|DMU|1 +4 Hajar, Loyal Bodyguard|BRO|1 +4 Huatli, Poet of Unity|LCI|1 +4 Karplusan Forest|DMU|2 +3 Lush Portico|MKM|2 +1 Mountain|BLB|1 +2 Mountain|BRO|1 +1 Mountain|DMU|1 +2 Plains|BRO|1 +1 Plains|FDN|1 +4 Plaza of Heroes|DMU|1 +4 Rocco, Street Chef|MAT|5 +4 Roxanne, Starfall Savant|OTJ|1 +4 Soul Partition|BRO|2 +[Sideboard] +4 Djeru and Hazoret|MOM|1 +3 Fortune, Loyal Steed|OTJ|2 +3 Hugs, Grisly Guardian|BLB|1 +1 Melira, the Living Cure|ONE|3 +4 Ruby, Daring Tracker|FDN|1 diff --git a/forge-gui/res/geneticaidecks/GAS_28_Annie Flash, the Veteran based deck_29_1.dck b/forge-gui/res/geneticaidecks/GAS_28_Annie Flash, the Veteran based deck_29_1.dck new file mode 100644 index 00000000000..00f41c0bb03 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_28_Annie Flash, the Veteran based deck_29_1.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAS_28_Annie Flash, the Veteran based deck_29_1 +[Main] +4 Annie Flash, the Veteran|OTJ|2 +4 Annie Joins Up|OTJ|2 +4 Battlefield Forge|BRO|1 +4 Beza, the Bounding Spring|BLB|1 +3 Brushland|BRO|2 +3 Copperline Gorge|ONE|2 +2 Djeru and Hazoret|MOM|2 +3 Dragonhawk, Fate's Tempest|BLB|1 +1 Forest|LCI|1 +1 Forest|OTJ|1 +4 Hajar, Loyal Bodyguard|BRO|1 +3 Huatli, Poet of Unity|LCI|3 +1 Hugs, Grisly Guardian|BLB|2 +1 Mountain|BRO|1 +2 Mountain|DMU|1 +1 Mountain|OTJ|1 +1 Mountain|WOE|1 +2 Plains|DMU|1 +2 Plains|MOM|1 +3 Plaza of Heroes|DMU|2 +1 Razorverge Thicket|ONE|1 +3 Roxanne, Starfall Savant|OTJ|1 +4 Ruby, Daring Tracker|WOE|1 +3 Soul Partition|BRO|1 +[Sideboard] +4 Fortune, Loyal Steed|OTJ|1 +3 Kellan, Daring Traveler|LCI|1 +4 Melira, the Living Cure|ONE|2 +4 Relic of Legends|DMU|1 diff --git a/forge-gui/res/geneticaidecks/GAS_29_Spring-Loaded Sawblades based deck_122_0.dck b/forge-gui/res/geneticaidecks/GAS_29_Spring-Loaded Sawblades based deck_122_0.dck new file mode 100644 index 00000000000..c2eecdd2da6 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_29_Spring-Loaded Sawblades based deck_122_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAS_29_Spring-Loaded Sawblades based deck_122_0 +[Main] +4 Adarkar Wastes|DMU|1 +3 Ajani, Caller of the Pride|FDN|2 +3 Case of the Gateway Express|MKM|1 +4 Cryptic Coat|MKM|2 +3 Enduring Curiosity|DSK|1 +1 Floodfarm Verge|DSK|2 +1 Island|DMU|1 +1 Island|MOM|1 +1 Island|OTJ|1 +1 Island|WOE|1 +4 Mirrex|ONE|2 +1 Mockingbird|BLB|2 +2 Plains|DSK|1 +1 Plains|WOE|1 +3 Regal Bunnicorn|WOE|1 +4 Restless Anchorage|LCI|2 +4 Seachrome Coast|ONE|2 +4 Spring-Loaded Sawblades|LCI|1 +4 Spyglass Siren|LCI|2 +4 Steel Seraph|BRO|1 +3 Subterranean Schooner|LCI|1 +1 Warden of the Inner Sky|LCI|1 +3 Zoetic Glyph|LCI|1 +[Sideboard] +4 Case of the Filched Falcon|MKM|1 +1 Destroy Evil|DMU|1 +4 Dusk Rose Reliquary|LCI|1 +2 Mockingbird|BLB|2 +4 Norn's Inquisitor|MOM|1 diff --git a/forge-gui/res/geneticaidecks/GAS_2_Rocco, Street Chef based deck_75_1.dck b/forge-gui/res/geneticaidecks/GAS_2_Rocco, Street Chef based deck_75_1.dck new file mode 100644 index 00000000000..c6c9748975a --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_2_Rocco, Street Chef based deck_75_1.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAS_2_Rocco, Street Chef based deck_75_1 +[Main] +4 Annie Joins Up|OTJ|2 +3 Battlefield Forge|BRO|2 +3 Beza, the Bounding Spring|BLB|1 +4 Brushland|BRO|2 +4 Copperline Gorge|ONE|2 +1 Djeru and Hazoret|MOM|2 +3 Dragonhawk, Fate's Tempest|BLB|2 +3 Etali, Primal Conqueror|MOM|2 +1 Forest|BRO|1 +1 Forest|MOM|1 +4 Hajar, Loyal Bodyguard|BRO|2 +4 Huatli, Poet of Unity|LCI|3 +1 Mountain|BRO|1 +1 Mountain|MOM|1 +2 Mountain|ONE|1 +1 Mountain|WOE|1 +1 Plains|BLB|1 +1 Plains|DMU|1 +1 Plains|WOE|1 +4 Plaza of Heroes|DMU|2 +4 Rocco, Street Chef|MAT|5 +4 Roxanne, Starfall Savant|OTJ|1 +2 Ruby, Daring Tracker|WOE|1 +3 Soul Partition|BRO|2 +[Sideboard] +1 Baylen, the Haymaker|BLB|2 +4 Fortune, Loyal Steed|OTJ|2 +2 Hugs, Grisly Guardian|BLB|1 +4 Melira, the Living Cure|ONE|3 +4 Relic of Legends|DMU|1 diff --git a/forge-gui/res/geneticaidecks/GAS_30_Bramble Familiar based deck_124_0.dck b/forge-gui/res/geneticaidecks/GAS_30_Bramble Familiar based deck_124_0.dck new file mode 100644 index 00000000000..e8782f2ac4c --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_30_Bramble Familiar based deck_124_0.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_30_Bramble Familiar based deck_124_0 +[Main] +4 Blossoming Sands|FDN|1 +4 Bramble Familiar|WOE|2 +3 Brambleguard Veteran|BLB|1 +2 Forest|BLB|1 +1 Forest|ONE|1 +1 Forest|OTJ|1 +2 Forest|WOE|1 +3 Giant Growth|BLB|1 +3 Keen-Eyed Curator|BLB|2 +1 Mountain|WOE|1 +1 Muerra, Trash Tactician|BLB|1 +2 Oakhollow Village|BLB|1 +4 Outcaster Trailblazer|OTJ|2 +1 Pileated Provisioner|BLB|1 +2 Plains|WOE|1 +4 Restless Ridgeline|LCI|2 +4 Rockface Village|BLB|1 +4 Scrapshooter|BLB|2 +4 Tail Swipe|DMU|1 +3 Treeguard Duo|BLB|1 +3 Wandertale Mentor|BLB|1 +4 Warren Elder|BLB|1 +[Sideboard] +4 Carrot Cake|BLB|1 +4 Druid of the Spade|BLB|1 +3 Pileated Provisioner|BLB|1 +3 Take Out the Trash|BLB|1 +1 Tender Wildguide|BLB|1 diff --git a/forge-gui/res/geneticaidecks/GAS_31_Gumdrop Poisoner based deck_94_0.dck b/forge-gui/res/geneticaidecks/GAS_31_Gumdrop Poisoner based deck_94_0.dck new file mode 100644 index 00000000000..ef74f39e747 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_31_Gumdrop Poisoner based deck_94_0.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_31_Gumdrop Poisoner based deck_94_0 +[Main] +3 Aclazotz, Deepest Betrayal|LCI|2 +4 Amalia Benavides Aguirre|LCI|1 +4 Case of the Uneaten Feast|MKM|1 +3 Caves of Koilos|DMU|2 +4 Concealed Courtyard|OTJ|2 +4 Deep-Cavern Bat|LCI|2 +2 Elas il-Kor, Sadistic Pilgrim|DMU|2 +2 Enduring Tenacity|DSK|4 +4 Gumdrop Poisoner|WOE|2 +3 Lunar Convocation|BLB|1 +1 Plains|DMU|1 +1 Plains|DSK|1 +2 Plains|FDN|1 +1 Plains|WOE|1 +3 Restless Fortress|WOE|1 +3 Ruin-Lurker Bat|LCI|1 +3 Starscape Cleric|BLB|1 +3 Swamp|BLB|1 +2 Swamp|DSK|1 +3 Swamp|MKM|1 +2 Valley Questcaller|BLB|2 +3 Zoraline, Cosmos Caller|BLB|2 +[Sideboard] +4 Bitter Triumph|LCI|1 +4 Legions to Ashes|BRO|1 +2 Lifecreed Duo|BLB|1 +3 Miner's Guidewing|LCI|1 +2 Valley Questcaller|BLB|2 diff --git a/forge-gui/res/geneticaidecks/GAS_32_Plundering Pirate based deck_84_0.dck b/forge-gui/res/geneticaidecks/GAS_32_Plundering Pirate based deck_84_0.dck new file mode 100644 index 00000000000..ef2264a364c --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_32_Plundering Pirate based deck_84_0.dck @@ -0,0 +1,33 @@ +[metadata] +Name=GAS_32_Plundering Pirate based deck_84_0 +[Main] +4 Blood Hustler|OTJ|1 +3 Bloodfell Caves|FDN|1 +1 Geothermal Bog|DMU|1 +4 Hellspur Brute|OTJ|1 +2 Hellspur Posse Boss|OTJ|2 +4 Jagged Barrens|OTJ|1 +2 Laughing Jasper Flint|OTJ|1 +2 Mountain|DSK|1 +1 Mountain|LCI|1 +1 Mountain|MOM|1 +1 Mountain|OTJ|1 +2 Mountain|WOE|1 +2 Outlaws' Fury|OTJ|1 +4 Plundering Pirate|LCI|1 +4 Rakish Crew|OTJ|1 +4 Raucous Theater|MKM|2 +2 Reckless Lackey|OTJ|1 +4 Roaming Throne|LCI|1 +1 Swamp|DSK|1 +1 Swamp|FDN|1 +1 Swamp|LCI|1 +3 Swamp|OTJ|1 +4 Vadmir, New Blood|OTJ|1 +3 Vial Smasher, Gleeful Grenadier|OTJ|1 +[Sideboard] +3 Cerebral Confiscation|MKM|1 +4 Greedy Freebooter|LCI|1 +3 Long Goodbye|MKM|2 +4 Sheoldred, the Apocalypse|DMU|6 +1 Trick Shot|OTJ|1 diff --git a/forge-gui/res/geneticaidecks/GAS_33_Excavation Explosion based deck_105_0.dck b/forge-gui/res/geneticaidecks/GAS_33_Excavation Explosion based deck_105_0.dck new file mode 100644 index 00000000000..ddf32ea6911 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_33_Excavation Explosion based deck_105_0.dck @@ -0,0 +1,29 @@ +[metadata] +Name=GAS_33_Excavation Explosion based deck_105_0 +[Main] +4 Adarkar Wastes|DMU|2 +4 Braided Net|LCI|1 +4 Cavern of Souls|LCI|1 +4 Chimil, the Inner Sun|LCI|1 +4 Excavation Explosion|BRO|1 +3 Fomori Vault|BIG|2 +1 Island|BRO|1 +1 Island|WOE|1 +2 Karn, Living Legacy|DMU|2 +4 Meticulous Archive|MKM|1 +1 Mountain|OTJ|1 +1 Plains|BLB|1 +2 Plains|DMU|1 +4 Plaza of Heroes|DMU|1 +2 Portal to Phyrexia|BRO|2 +4 Rona, Herald of Invasion|MOM|1 +4 Simulacrum Synthesizer|BIG|3 +3 Stenn, Paranoid Partisan|DMU|1 +4 Thousand Moons Smithy|LCI|2 +4 Thran Spider|BRO|2 +[Sideboard] +4 Assimilation Aegis|OTJ|1 +3 Relic of Legends|DMU|1 +2 Stern Lesson|BRO|1 +3 Urza, Lord Protector|BRO|1 +3 Worldwalker Helm|BIG|2 diff --git a/forge-gui/res/geneticaidecks/GAS_34_Invasion of Tarkir based deck_47_1.dck b/forge-gui/res/geneticaidecks/GAS_34_Invasion of Tarkir based deck_47_1.dck new file mode 100644 index 00000000000..1b5f8a70ebf --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_34_Invasion of Tarkir based deck_47_1.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_34_Invasion of Tarkir based deck_47_1 +[Main] +3 Bonehoard Dracosaur|LCI|2 +4 Branch of Vitu-Ghazi|MKM|1 +3 Cavern of Souls|LCI|8 +2 Cunning Coyote|OTJ|1 +4 Decadent Dragon|WOE|2 +3 Dragonhawk, Fate's Tempest|BLB|2 +4 Invasion of Tarkir|MOM|1 +3 Kolaghan Warmonger|MAT|2 +2 Mountain|BLB|1 +3 Mountain|BRO|1 +2 Mountain|DSK|1 +1 Mountain|FDN|1 +2 Mountain|LCI|1 +4 Mountain|MOM|1 +1 Mountain|OTJ|1 +1 Mountain|WOE|1 +4 Nahiri's Warcrafting|MOM|1 +4 Rivaz of the Claw|DMU|3 +4 Scorching Shot|OTJ|2 +1 Shivan Devastator|DMU|1 +3 Stingerback Terror|OTJ|2 +1 Swamp|FDN|1 +1 Swamp|WOE|1 +[Sideboard] +3 Carnelian Orb of Dragonkind|FDN|1 +4 Dragonlord's Servant|FDN|1 +4 Go for the Throat|BRO|1 +4 Inti, Seneschal of the Sun|LCI|1 diff --git a/forge-gui/res/geneticaidecks/GAS_35_Spyglass Siren based deck_18_1.dck b/forge-gui/res/geneticaidecks/GAS_35_Spyglass Siren based deck_18_1.dck new file mode 100644 index 00000000000..3ba1e93f840 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_35_Spyglass Siren based deck_18_1.dck @@ -0,0 +1,28 @@ +[metadata] +Name=GAS_35_Spyglass Siren based deck_18_1 +[Main] +4 Cut Down|DMU|2 +3 Darkslick Shores|ONE|1 +4 Deep-Cavern Bat|LCI|1 +4 Enduring Curiosity|DSK|1 +1 Fountainport|BLB|2 +4 Gloomlake Verge|DSK|2 +4 Go for the Throat|BRO|1 +1 Island|BLB|1 +1 Island|DMU|1 +1 Island|DSK|1 +1 Island|ONE|1 +4 Kaito, Bane of Nightmares|DSK|1 +3 Phantom Interference|OTJ|1 +4 Restless Reef|LCI|2 +1 Sheoldred, the Apocalypse|DMU|6 +4 Spyglass Siren|LCI|2 +3 Swamp|DMU|1 +1 Swamp|OTJ|1 +4 Three Steps Ahead|OTJ|2 +1 Treasure Map|LCI|1 +4 Underground River|BRO|2 +3 Unholy Annex // Ritual Chamber|DSK|1 +[Sideboard] +3 Nowhere to Run|DSK|1 +2 Treasure Map|LCI|1 diff --git a/forge-gui/res/geneticaidecks/GAS_36_Tough Cookie based deck_112_0.dck b/forge-gui/res/geneticaidecks/GAS_36_Tough Cookie based deck_112_0.dck new file mode 100644 index 00000000000..4d973413153 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_36_Tough Cookie based deck_112_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAS_36_Tough Cookie based deck_112_0 +[Main] +4 Agatha's Soul Cauldron|WOE|2 +2 Botanical Sanctum|OTJ|2 +1 Forest|BLB|1 +1 Forest|BRO|1 +1 Forest|ONE|1 +1 Forest|OTJ|1 +2 Forest|WOE|1 +4 Hard-Hitting Question|MKM|1 +4 Hunter's Talent|BLB|1 +1 Island|ONE|1 +1 Island|OTJ|1 +1 Island|WOE|1 +4 Mirrex|ONE|2 +1 Mockingbird|BLB|2 +3 Restless Vinestalk|WOE|1 +4 Sentinel of the Nameless City|LCI|1 +4 Subterranean Schooner|LCI|2 +4 Surge Engine|BRO|2 +3 Syr Ginger, the Meal Ender|WOE|2 +3 Teething Wurmlet|BRO|2 +4 Tough Cookie|WOE|1 +4 Yavimaya Coast|DMU|1 +3 Zoetic Glyph|LCI|1 +[Sideboard] +4 Case of the Filched Falcon|MKM|1 +4 Levitating Statue|BRO|1 +4 Lost Jitte|BIG|2 +2 Machine Over Matter|BRO|1 +1 Syr Ginger, the Meal Ender|WOE|2 diff --git a/forge-gui/res/geneticaidecks/GAS_37_Portal to Phyrexia based deck_72_0.dck b/forge-gui/res/geneticaidecks/GAS_37_Portal to Phyrexia based deck_72_0.dck new file mode 100644 index 00000000000..3fdd8a66da9 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_37_Portal to Phyrexia based deck_72_0.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAS_37_Portal to Phyrexia based deck_72_0 +[Main] +4 Adarkar Wastes|DMU|2 +3 Braided Net|LCI|2 +4 Cavern of Souls|LCI|6 +4 Cityscape Leveler|BRO|2 +2 Island|BLB|1 +1 Island|BRO|1 +1 Island|LCI|1 +1 Island|OTJ|1 +1 Island|WOE|1 +4 Meticulous Archive|MKM|1 +1 Plains|BLB|1 +1 Plains|BRO|1 +1 Plains|DMU|1 +4 Plaza of Heroes|DMU|1 +3 Portal to Phyrexia|BRO|2 +4 Relic of Legends|DMU|1 +4 Rona, Herald of Invasion|MOM|2 +4 Simulacrum Synthesizer|BIG|1 +3 Stenn, Paranoid Partisan|DMU|3 +4 The Mightstone and Weakstone|BRO|1 +3 Thran Spider|BRO|1 +2 Urza, Lord Protector|BRO|1 +1 Worldwalker Helm|BIG|3 +[Sideboard] +4 Assimilation Aegis|OTJ|1 +1 Patchwork Banner|BLB|1 +4 Season of Weaving|BLB|1 +3 Stern Lesson|BRO|1 +3 Thousand Moons Smithy|LCI|2 diff --git a/forge-gui/res/geneticaidecks/GAS_38_Pugnacious Hammerskull based deck_32_1.dck b/forge-gui/res/geneticaidecks/GAS_38_Pugnacious Hammerskull based deck_32_1.dck new file mode 100644 index 00000000000..ab509b9b2d6 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_38_Pugnacious Hammerskull based deck_32_1.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAS_38_Pugnacious Hammerskull based deck_32_1 +[Main] +2 Aloe Alchemist|OTJ|1 +4 Belligerent Yearling|LCI|1 +3 Bristling Backwoods|OTJ|1 +4 Cavern of Souls|LCI|3 +1 Forest|LCI|1 +1 Forest|MOM|1 +3 Forest|ONE|1 +1 Forest|OTJ|1 +2 Forest|WOE|1 +4 Ghalta, Primal Hunger|FDN|2 +4 Hulking Raptor|LCI|1 +4 Intrepid Paleontologist|LCI|2 +4 Ixalli's Lorekeeper|LCI|1 +1 Mountain|FDN|1 +1 Mountain|LCI|1 +2 Mountain|MOM|1 +1 Mountain|ONE|1 +1 Mountain|OTJ|1 +4 Pugnacious Hammerskull|LCI|2 +3 Rampaging Raptor|MOM|1 +4 Scytheclaw Raptor|LCI|1 +4 Terramorphic Expanse|ONE|1 +2 Triumphant Chomp|LCI|1 +[Sideboard] +1 Armored Kincaller|LCI|1 +3 Itzquinth, Firstborn of Gishath|LCI|1 +4 Llanowar Elves|FDN|3 +4 Palani's Hatcher|LCI|1 +3 Spinner of Souls|FDN|3 diff --git a/forge-gui/res/geneticaidecks/GAS_39_Searslicer Goblin based deck_71_1.dck b/forge-gui/res/geneticaidecks/GAS_39_Searslicer Goblin based deck_71_1.dck new file mode 100644 index 00000000000..9a6f7e8375d --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_39_Searslicer Goblin based deck_71_1.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_39_Searslicer Goblin based deck_71_1 +[Main] +4 Adaptive Automaton|FDN|1 +2 Ajani's Pridemate|FDN|1 +4 Arahbo, the First Fang|FDN|4 +4 Claws Out|FDN|1 +4 Fabled Passage|BLB|2 +3 Knight-Errant of Eos|MOM|2 +2 Krenko, Mob Boss|FDN|1 +2 Mountain|BRO|1 +1 Mountain|MOM|1 +2 Patchwork Banner|BLB|1 +1 Plains|BLB|1 +2 Plains|BRO|1 +2 Plains|DMU|1 +1 Plains|FDN|1 +2 Plains|LCI|1 +2 Plains|MOM|1 +4 Plains|ONE|1 +4 Prideful Parent|FDN|1 +4 Regal Caracal|FDN|1 +2 Savannah Lions|FDN|1 +4 Searslicer Goblin|FDN|4 +4 Sunken Citadel|LCI|2 +[Sideboard] +4 Dragon Fodder|FDN|1 +2 Hinterland Sanctifier|FDN|1 +4 Leonin Vanguard|FDN|1 +1 Progenitor Exarch|MOM|2 +4 Rundvelt Hordemaster|DMU|2 diff --git a/forge-gui/res/geneticaidecks/GAS_3_Mabel, Heir to Cragflame based deck_81_0.dck b/forge-gui/res/geneticaidecks/GAS_3_Mabel, Heir to Cragflame based deck_81_0.dck new file mode 100644 index 00000000000..75055ac634d --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_3_Mabel, Heir to Cragflame based deck_81_0.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_3_Mabel, Heir to Cragflame based deck_81_0 +[Main] +4 Battlefield Forge|BRO|1 +3 Emberheart Challenger|BLB|2 +3 Flowerfoot Swordmaster|BLB|1 +4 Heartfire Hero|BLB|1 +4 Imodane's Recruiter|WOE|1 +4 Inspiring Vantage|OTJ|2 +4 Mabel, Heir to Cragflame|BLB|3 +4 Manifold Mouse|BLB|1 +1 Might of the Meek|BLB|1 +3 Monstrous Rage|WOE|1 +1 Mountain|LCI|1 +2 Mountain|MOM|1 +3 Mountain|ONE|1 +1 Mountain|WOE|1 +1 Plains|BLB|1 +1 Plains|FDN|1 +1 Plains|ONE|1 +1 Plains|WOE|1 +1 Raging Battle Mouse|WOE|1 +4 Rockface Village|BLB|1 +3 Sunspine Lynx|BLB|2 +3 Valley Questcaller|BLB|2 +4 Whiskervale Forerunner|BLB|2 +[Sideboard] +4 Lavaspur Boots|OTJ|1 +4 Loran's Escape|BRO|1 +4 Nettle Guard|BLB|1 +3 Raging Battle Mouse|WOE|1 diff --git a/forge-gui/res/geneticaidecks/GAS_40_Rocco, Street Chef based deck_159_0.dck b/forge-gui/res/geneticaidecks/GAS_40_Rocco, Street Chef based deck_159_0.dck new file mode 100644 index 00000000000..da0f1598809 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_40_Rocco, Street Chef based deck_159_0.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_40_Rocco, Street Chef based deck_159_0 +[Main] +4 Annie Joins Up|OTJ|2 +4 Battlefield Forge|BRO|2 +3 Beza, the Bounding Spring|BLB|1 +4 Brushland|BRO|1 +4 Copperline Gorge|ONE|1 +4 Dragonhawk, Fate's Tempest|BLB|2 +3 Etali, Primal Conqueror|MOM|1 +1 Forest|MOM|1 +3 Fortune, Loyal Steed|OTJ|1 +4 Huatli, Poet of Unity|LCI|3 +3 Hugs, Grisly Guardian|BLB|2 +3 Lush Portico|MKM|1 +1 Mountain|BRO|1 +1 Mountain|LCI|1 +2 Mountain|OTJ|1 +1 Mountain|WOE|1 +1 Plains|DMU|1 +1 Plains|LCI|1 +2 Plains|ONE|1 +4 Rocco, Street Chef|MAT|5 +3 Ruby, Daring Tracker|FDN|1 +4 Soul Partition|BRO|2 +[Sideboard] +3 Baylen, the Haymaker|BLB|2 +4 Kellan, Daring Traveler|LCI|2 +4 Melira, the Living Cure|ONE|3 +1 Migloz, Maze Crusher|ONE|2 +3 Relic of Legends|DMU|1 diff --git a/forge-gui/res/geneticaidecks/GAS_4_Concealed Courtyard based deck_23_1.dck b/forge-gui/res/geneticaidecks/GAS_4_Concealed Courtyard based deck_23_1.dck new file mode 100644 index 00000000000..2d795d4f53e --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_4_Concealed Courtyard based deck_23_1.dck @@ -0,0 +1,27 @@ +[metadata] +Name=GAS_4_Concealed Courtyard based deck_23_1 +[Main] +4 Aclazotz, Deepest Betrayal|LCI|2 +3 Blooming Marsh|OTJ|1 +4 Concealed Courtyard|OTJ|2 +1 Forest|WOE|1 +3 Glissa Sunslayer|ONE|2 +4 Go for the Throat|BRO|1 +2 Llanowar Wastes|BRO|1 +3 Mosswood Dreadknight|WOE|1 +3 Phyrexian Fleshgorger|BRO|1 +3 Restless Cottage|WOE|2 +4 Sentinel of the Nameless City|LCI|2 +3 Sheoldred, the Apocalypse|DMU|6 +4 Shoot the Sheriff|OTJ|1 +1 Swamp|BRO|1 +1 Swamp|DMU|1 +2 Swamp|ONE|1 +1 Swamp|OTJ|1 +1 Swamp|WOE|1 +3 Tear Asunder|DMU|1 +4 Temple of Malady|FDN|1 +2 Underground Mortuary|MKM|2 +4 Virtue of Persistence|WOE|1 +[Sideboard] +3 Teysa, Opulent Oligarch|MKM|1 diff --git a/forge-gui/res/geneticaidecks/GAS_5_Golgari Midrange Mono Generated Deck_78_1.dck b/forge-gui/res/geneticaidecks/GAS_5_Golgari Midrange Mono Generated Deck_78_1.dck new file mode 100644 index 00000000000..31532d3f342 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_5_Golgari Midrange Mono Generated Deck_78_1.dck @@ -0,0 +1,30 @@ +[metadata] +Name=GAS_5_Golgari Midrange Mono Generated Deck_78_1 +[Main] +4 Aclazotz, Deepest Betrayal|LCI|1 +4 Caustic Bronco|OTJ|2 +1 Cut Down|DMU|1 +3 Demolition Field|BRO|1 +2 Go for the Throat|BRO|1 +3 Hostile Investigator|BIG|1 +3 Kaervek, the Punisher|OTJ|2 +4 Liliana of the Veil|DMU|1 +3 Mirrex|ONE|2 +4 Preacher of the Schism|LCI|1 +4 Sheoldred, the Apocalypse|DMU|1 +4 Shoot the Sheriff|OTJ|1 +3 Swamp|BLB|1 +3 Swamp|DMU|1 +3 Swamp|DSK|1 +1 Swamp|FDN|1 +1 Swamp|LCI|1 +3 Swamp|ONE|1 +2 Swamp|OTJ|1 +3 Swamp|WOE|1 +2 Tinybones, the Pickpocket|OTJ|1 +[Sideboard] +4 Bitter Triumph|LCI|1 +2 Feed the Cycle|BLB|1 +3 Lord Skitter, Sewer King|WOE|2 +4 Overlord of the Balemurk|DSK|4 +2 Season of Loss|BLB|1 diff --git a/forge-gui/res/geneticaidecks/GAS_6_Sharp-Eyed Rookie based deck_128_0.dck b/forge-gui/res/geneticaidecks/GAS_6_Sharp-Eyed Rookie based deck_128_0.dck new file mode 100644 index 00000000000..a36b27250ab --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_6_Sharp-Eyed Rookie based deck_128_0.dck @@ -0,0 +1,30 @@ +[metadata] +Name=GAS_6_Sharp-Eyed Rookie based deck_128_0 +[Main] +4 Axebane Ferox|MKM|3 +4 Cenote Scout|LCI|2 +4 Crawling Barrens|FDN|1 +3 Evolving Adaptive|ONE|1 +3 Flourishing Bloom-Kin|MKM|1 +5 Forest|BLB|1 +3 Forest|FDN|1 +1 Forest|MKM|1 +2 Forest|MOM|1 +2 Forest|ONE|1 +2 Forest|OTJ|1 +3 Forest|WOE|1 +2 Goldvein Hydra|OTJ|1 +4 Hard-Hitting Question|MKM|1 +3 Hunter's Talent|BLB|1 +2 Outcaster Trailblazer|OTJ|2 +4 Pawpatch Recruit|BLB|2 +2 Railway Brawler|OTJ|1 +3 Scrapshooter|BLB|1 +4 Sharp-Eyed Rookie|MKM|2 +[Sideboard] +3 Aloe Alchemist|OTJ|1 +1 Bristly Bill, Spine Sower|OTJ|2 +1 Hunter's Talent|BLB|1 +4 Keen-Eyed Curator|BLB|2 +3 Quirion Beastcaller|DMU|1 +3 Snakeskin Veil|FDN|1 diff --git a/forge-gui/res/geneticaidecks/GAS_7_Starscape Cleric based deck_15_1.dck b/forge-gui/res/geneticaidecks/GAS_7_Starscape Cleric based deck_15_1.dck new file mode 100644 index 00000000000..31ae0568efe --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_7_Starscape Cleric based deck_15_1.dck @@ -0,0 +1,32 @@ +[metadata] +Name=GAS_7_Starscape Cleric based deck_15_1 +[Main] +4 Aclazotz, Deepest Betrayal|LCI|1 +1 Case of the Uneaten Feast|MKM|1 +3 Caves of Koilos|DMU|2 +3 Concealed Courtyard|OTJ|1 +4 Darkstar Augur|BLB|2 +1 Deep-Cavern Bat|LCI|1 +2 Enduring Tenacity|DSK|1 +3 Essence Channeler|BLB|1 +4 Gumdrop Poisoner|WOE|2 +1 Legions to Ashes|BRO|1 +4 Liliana of the Veil|DMU|2 +4 Lupinflower Village|BLB|1 +4 Mudflat Village|BLB|1 +1 Plains|DMU|1 +1 Plains|LCI|1 +4 Restless Fortress|WOE|2 +3 Ruin-Lurker Bat|LCI|1 +1 Scoured Barrens|MOM|1 +4 Starscape Cleric|BLB|1 +1 Swamp|BLB|1 +1 Swamp|DMU|1 +1 Swamp|DSK|1 +1 Swamp|FDN|1 +4 Zoraline, Cosmos Caller|BLB|1 +[Sideboard] +4 Amalia Benavides Aguirre|LCI|1 +3 Case of the Uneaten Feast|MKM|1 +4 Elas il-Kor, Sadistic Pilgrim|DMU|2 +4 Lunar Convocation|BLB|2 diff --git a/forge-gui/res/geneticaidecks/GAS_8_Mabel, Heir to Cragflame based deck_80_0.dck b/forge-gui/res/geneticaidecks/GAS_8_Mabel, Heir to Cragflame based deck_80_0.dck new file mode 100644 index 00000000000..213c10ed561 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_8_Mabel, Heir to Cragflame based deck_80_0.dck @@ -0,0 +1,31 @@ +[metadata] +Name=GAS_8_Mabel, Heir to Cragflame based deck_80_0 +[Main] +4 Battlefield Forge|BRO|1 +4 Cavern of Souls|LCI|3 +4 Emberheart Challenger|BLB|2 +3 Flowerfoot Swordmaster|BLB|1 +3 Imodane's Recruiter|WOE|1 +4 Inspiring Vantage|OTJ|1 +4 Mabel, Heir to Cragflame|BLB|3 +4 Manifold Mouse|BLB|1 +3 Might of the Meek|BLB|1 +3 Monstrous Rage|WOE|1 +1 Mountain|FDN|1 +1 Mountain|LCI|1 +1 Mountain|OTJ|1 +1 Mountain|WOE|1 +2 Nettle Guard|BLB|1 +1 Plains|MOM|1 +1 Plains|OTJ|1 +4 Rabid Gnaw|BLB|1 +4 Raging Battle Mouse|WOE|1 +1 Restless Bivouac|WOE|2 +3 Rockface Village|BLB|1 +4 Valley Questcaller|BLB|2 +[Sideboard] +4 Cheeky House-Mouse|WOE|1 +4 Loran's Escape|BRO|1 +4 Lost Jitte|BIG|3 +1 Might of the Meek|BLB|1 +2 Skrelv, Defector Mite|ONE|2 diff --git a/forge-gui/res/geneticaidecks/GAS_9_Bonehoard Dracosaur based deck_33_0.dck b/forge-gui/res/geneticaidecks/GAS_9_Bonehoard Dracosaur based deck_33_0.dck new file mode 100644 index 00000000000..2a88f7b85f4 --- /dev/null +++ b/forge-gui/res/geneticaidecks/GAS_9_Bonehoard Dracosaur based deck_33_0.dck @@ -0,0 +1,30 @@ +[metadata] +Name=GAS_9_Bonehoard Dracosaur based deck_33_0 +[Main] +4 Aloe Alchemist|OTJ|1 +4 Belligerent Yearling|LCI|2 +4 Bonehoard Dracosaur|LCI|2 +4 Bristling Backwoods|OTJ|1 +4 Cavern of Souls|LCI|7 +2 Forest|FDN|1 +1 Forest|MOM|1 +3 Forest|ONE|1 +1 Forest|OTJ|1 +1 Forest|WOE|1 +4 Ghalta, Primal Hunger|FDN|1 +3 Hulking Raptor|LCI|1 +4 Ixalli's Lorekeeper|LCI|1 +4 Llanowar Elves|FDN|2 +1 Mountain|FDN|1 +2 Mountain|LCI|1 +1 Mountain|MOM|1 +2 Mountain|WOE|1 +4 Pugnacious Hammerskull|LCI|1 +3 Terramorphic Expanse|ONE|1 +4 Triumphant Chomp|LCI|1 +[Sideboard] +2 Armored Kincaller|LCI|1 +3 Intrepid Paleontologist|LCI|2 +4 Itzquinth, Firstborn of Gishath|LCI|2 +3 Palani's Hatcher|LCI|1 +3 Spinner of Souls|FDN|4 From 4b1ca2449c3f68c87fa871c7ca4bb81115a8ac52 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Fri, 29 Nov 2024 12:21:08 +0100 Subject: [PATCH 137/152] Update mana_drain.txt --- forge-gui/res/cardsfolder/m/mana_drain.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-gui/res/cardsfolder/m/mana_drain.txt b/forge-gui/res/cardsfolder/m/mana_drain.txt index 2bcd8fad8eb..cffe2be349a 100644 --- a/forge-gui/res/cardsfolder/m/mana_drain.txt +++ b/forge-gui/res/cardsfolder/m/mana_drain.txt @@ -1,8 +1,8 @@ Name:Mana Drain ManaCost:U U Types:Instant -A:SP$ Counter | TargetType$ Spell | RememberCounteredCMC$ True | ValidTgts$ Card | SubAbility$ DBDelTrig | SpellDescription$ Counter target spell. At the beginning of your next main phase, add {X}, where X is that spell's mana value. -SVar:DBDelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ Main1,Main2 | ValidPlayer$ You | Execute$ AddMana | TriggerDescription$ At the beginning of your next main phase, add an amount of {C} equal to that spell's mana value. | RememberNumber$ True | SubAbility$ DBCleanup +A:SP$ Counter | TargetType$ Spell | RememberCounteredCMC$ True | ValidTgts$ Card | SubAbility$ DBDelTrig | SpellDescription$ Counter target spell. +SVar:DBDelTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ Main1,Main2 | ValidPlayer$ You | Execute$ AddMana | SpellDescription$ At the beginning of your next main phase, add an amount of {C} equal to that spell's mana value. | RememberNumber$ True | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:AddMana:DB$ Mana | Produced$ C | Amount$ X SVar:X:Count$TriggerRememberAmount From 947d19078b38838f422cd8186344a6556e775db1 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Fri, 29 Nov 2024 12:22:23 +0100 Subject: [PATCH 138/152] Fix cheating class level 3 by copying level 2 activation (#6643) --- .../ability/effects/ClassLevelUpEffect.java | 11 +++++++--- .../game/ability/effects/RollDiceEffect.java | 20 +++++++++---------- .../game/ability/effects/RunChaosEffect.java | 1 - .../game/ability/effects/SeekEffect.java | 3 +-- .../ability/effects/SetInMotionEffect.java | 1 - .../ability/effects/UnlockDoorEffect.java | 6 +++--- .../game/ability/effects/VentureEffect.java | 1 - .../java/forge/game/cost/CostAdjustment.java | 10 ---------- .../main/java/forge/game/player/Player.java | 1 - .../cardsfolder/a/anointed_peacekeeper.txt | 2 +- forge-gui/res/cardsfolder/c/carrionette.txt | 2 +- forge-gui/res/cardsfolder/d/dream_chisel.txt | 2 +- forge-gui/res/cardsfolder/i/iron_mastiff.txt | 2 +- .../res/cardsfolder/o/obscuring_aether.txt | 2 +- .../res/cardsfolder/s/suppression_field.txt | 2 +- .../res/cardsfolder/z/zirda_the_dawnwaker.txt | 2 +- 16 files changed, 28 insertions(+), 40 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ClassLevelUpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ClassLevelUpEffect.java index 5c7aafc7644..16dbe0f52e8 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ClassLevelUpEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ClassLevelUpEffect.java @@ -4,6 +4,7 @@ import java.util.Map; import forge.game.Game; import forge.game.ability.AbilityKey; +import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.spellability.SpellAbility; @@ -18,8 +19,13 @@ public class ClassLevelUpEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { final Card host = sa.getHostCard(); final Game game = host.getGame(); - final int level = host.getClassLevel() + 1; - host.setClassLevel(level); + int level = host.getClassLevel(); + + if (AbilityUtils.calculateAmount(host, sa.getRestrictions().getClassLevel(), sa) != level) { + return; + } + + host.setClassLevel(++level); // need to run static ability to get Trigger online game.getAction().checkStaticAbilities(); @@ -28,7 +34,6 @@ public class ClassLevelUpEffect extends SpellAbilityEffect { game.getTriggerHandler().clearActiveTriggers(host, null); game.getTriggerHandler().registerActiveTrigger(host, false); - // Run ClassLevelGained trigger final Map runParams = AbilityKey.mapFromCard(host); runParams.put(AbilityKey.ClassLevel, level); game.getTriggerHandler().runTrigger(TriggerType.ClassLevelGained, runParams, false); diff --git a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java index 35e1b69035a..99cc2718796 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java @@ -1,5 +1,6 @@ package forge.game.ability.effects; +import com.google.common.base.Functions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import forge.game.ability.AbilityKey; @@ -12,6 +13,7 @@ import forge.game.player.PlayerCollection; import forge.game.replacement.ReplacementType; import forge.game.spellability.SpellAbility; import forge.game.trigger.TriggerType; +import forge.util.Aggregates; import forge.util.Lang; import forge.util.Localizer; import forge.util.MyRandom; @@ -98,8 +100,6 @@ public class RollDiceEffect extends SpellAbilityEffect { } } - int total = 0; - int countMaxRolls = 0; List naturalRolls = (rollsResult == null ? new ArrayList<>() : rollsResult); for (int i = 0; i < amount; i++) { @@ -108,7 +108,6 @@ public class RollDiceEffect extends SpellAbilityEffect { player.getGame().fireEvent(new GameEventRollDie()); player.roll(); naturalRolls.add(roll); - total += roll; } naturalRolls.sort(null); @@ -117,7 +116,6 @@ public class RollDiceEffect extends SpellAbilityEffect { // Ignore lowest rolls if (ignore > 0) { for (int i = ignore - 1; i >= 0; --i) { - total -= naturalRolls.get(i); ignored.add(naturalRolls.get(i)); naturalRolls.remove(i); } @@ -126,12 +124,15 @@ public class RollDiceEffect extends SpellAbilityEffect { for (Player chooser : ignoreChosenMap.keySet()) { for (int ig = 0; ig < ignoreChosenMap.get(chooser); ig++) { Integer ign = chooser.getController().chooseRollToIgnore(naturalRolls); - total -= ign; ignored.add(ign); naturalRolls.remove(ign); } } + if (sa.hasParam("UseHighestRoll")) { + naturalRolls.subList(0, naturalRolls.size() - 1).clear(); + } + //Notify of results if (amount > 0) { StringBuilder sb = new StringBuilder(); @@ -150,6 +151,7 @@ public class RollDiceEffect extends SpellAbilityEffect { int oddResults = 0; int evenResults = 0; int differentResults = 0; + int countMaxRolls = 0; for (Integer i : naturalRolls) { final int modifiedRoll = i + modifier; if (!rolls.contains(modifiedRoll)) { @@ -177,9 +179,7 @@ public class RollDiceEffect extends SpellAbilityEffect { sa.setSVar("MaxRolls", Integer.toString(countMaxRolls)); } } - total += modifier; - // Run triggers int rollNum = 1; for (Integer roll : rolls) { final Map runParams = AbilityKey.mapFromPlayer(player); @@ -197,7 +197,7 @@ public class RollDiceEffect extends SpellAbilityEffect { runParams.put(AbilityKey.RolledToVisitAttractions, toVisitAttractions); player.getGame().getTriggerHandler().runTrigger(TriggerType.RolledDieOnce, runParams, false); - return total; + return Aggregates.sum(rolls, Functions.identity()); } private static void resolveSub(SpellAbility sa, int num) { @@ -232,9 +232,7 @@ public class RollDiceEffect extends SpellAbilityEffect { List rolls = new ArrayList<>(); int total = rollDiceForPlayer(sa, player, amount, sides, ignore, modifier, rolls, sa.hasParam("ToVisitYourAttractions")); - if (sa.hasParam("UseHighestRoll")) { - total = Collections.max(rolls); - } else if (sa.hasParam("UseDifferenceBetweenRolls")) { + if (sa.hasParam("UseDifferenceBetweenRolls")) { total = Collections.max(rolls) - Collections.min(rolls); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RunChaosEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RunChaosEffect.java index 7bd737e9623..f0fd52cc1cf 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RunChaosEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RunChaosEffect.java @@ -16,7 +16,6 @@ public class RunChaosEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { - List validSA = Lists.newArrayList(); for (final Card c : getTargetCards(sa)) { for (Trigger t : c.getTriggers()) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java index 7edc4a5806a..24623a91fa6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SeekEffect.java @@ -73,7 +73,6 @@ public class SeekEffect extends SpellAbilityEffect { } for (final Card c : Aggregates.random(pool, seekNum)) { - Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); @@ -85,9 +84,9 @@ public class SeekEffect extends SpellAbilityEffect { if (resultZone.equals(ZoneType.Hand)) { // if it went to hand as planned, consider it "sought" soughtCards.add(movedCard); } - } } + if (notify.length() != 0) { game.getAction().notifyOfValue(sa, source, notify.toString(), null); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetInMotionEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetInMotionEffect.java index bc94921a9cb..ff402b97069 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SetInMotionEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SetInMotionEffect.java @@ -41,7 +41,6 @@ public class SetInMotionEffect extends SpellAbilityEffect { game.getAction().moveToCommand(controller.getActiveScheme(), sa); - // Run triggers final Map runParams = AbilityKey.newMap(); runParams.put(AbilityKey.Scheme, controller.getActiveScheme()); game.getTriggerHandler().runTrigger(TriggerType.SetInMotion, runParams, false); diff --git a/forge-game/src/main/java/forge/game/ability/effects/UnlockDoorEffect.java b/forge-game/src/main/java/forge/game/ability/effects/UnlockDoorEffect.java index 7ab3831e2bb..52be9017666 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/UnlockDoorEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/UnlockDoorEffect.java @@ -26,9 +26,9 @@ public class UnlockDoorEffect extends SpellAbilityEffect { final Card source = sa.getHostCard(); final Game game = source.getGame(); final Player activator = sa.getActivatingPlayer(); - + CardCollection list; - + if (sa.hasParam("Choices")) { Player chooser = activator; String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoose") + " "; @@ -43,7 +43,7 @@ public class UnlockDoorEffect extends SpellAbilityEffect { } else { list = getTargetCards(sa); } - + for (Card c : list) { Map params = Maps.newHashMap(); params.put("Object", c); diff --git a/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java index 4c0214d0410..cd19e209ad5 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java @@ -115,7 +115,6 @@ public class VentureEffect extends SpellAbilityEffect { // TODO: Currently play the Add Counter sound, but maybe add soundeffect for marker? game.fireEvent(new GameEventCardCounters(dungeon, CounterType.getType("LEVEL"), 0, 1)); - // Run RoomEntered trigger final Map runParams = AbilityKey.mapFromCard(dungeon); runParams.put(AbilityKey.RoomName, nextRoom); game.getTriggerHandler().runTrigger(TriggerType.RoomEntered, runParams, false); diff --git a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java index c0a7e65bb1c..52c73431b78 100644 --- a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java +++ b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java @@ -568,16 +568,6 @@ public class CostAdjustment { } } } - case "NonManaAbility" -> { - if (!sa.isActivatedAbility() || sa.isManaAbility() || sa.isReplacementAbility()) { - return false; - } - } - case "MorphDown" -> { - if (!sa.isSpell() || !sa.isCastFaceDown()) { - return false; - } - } case "Foretell" -> { if (!sa.isForetelling()) { return false; 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 47a593bd760..8cae7f17367 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -2293,7 +2293,6 @@ public class Player extends GameEntity implements Comparable { sacrificedThisTurn.add(cpy); - // Run triggers final Map runParams = AbilityKey.mapFromPlayer(this); // use a copy that preserves last known information about the card (e.g. for Savra, Queen of the Golgari + Painter's Servant) runParams.put(AbilityKey.Card, cpy); diff --git a/forge-gui/res/cardsfolder/a/anointed_peacekeeper.txt b/forge-gui/res/cardsfolder/a/anointed_peacekeeper.txt index 426471b4486..e63e2720ae6 100644 --- a/forge-gui/res/cardsfolder/a/anointed_peacekeeper.txt +++ b/forge-gui/res/cardsfolder/a/anointed_peacekeeper.txt @@ -9,6 +9,6 @@ SVar:DBLook:DB$ RevealHand | Defined$ ChosenPlayer | Look$ True | SubAbility$ DB SVar:DBNameCard:DB$ NameCard | Defined$ You | SubAbility$ DBClear SVar:DBClear:DB$ Cleanup | ClearChosenPlayer$ True S:Mode$ RaiseCost | EffectZone$ Battlefield | ValidCard$ Card.NamedCard | Type$ Spell | Amount$ 2 | Activator$ Opponent | Description$ Spells your opponents cast with the chosen name cost {2} more to cast. -S:Mode$ RaiseCost | EffectZone$ Battlefield | ValidCard$ Card.NamedCard | Type$ NonManaAbility | Amount$ 2 | Description$ Activated abilities of sources with the chosen name cost {2} more to activate unless they're mana abilities. +S:Mode$ RaiseCost | EffectZone$ Battlefield | ValidCard$ Card.NamedCard | ValidSpell$ Activated.nonManaAbility | Amount$ 2 | Description$ Activated abilities of sources with the chosen name cost {2} more to activate unless they're mana abilities. AI:RemoveDeck:Random Oracle:Vigilance\nAs Anointed Peacekeeper enters, look at an opponent's hand, then choose any card name.\nSpells your opponents cast with the chosen name cost {2} more to cast.\nActivated abilities of sources with the chosen name cost {2} more to activate unless they're mana abilities. diff --git a/forge-gui/res/cardsfolder/c/carrionette.txt b/forge-gui/res/cardsfolder/c/carrionette.txt index 448531b2442..0d0208609b5 100644 --- a/forge-gui/res/cardsfolder/c/carrionette.txt +++ b/forge-gui/res/cardsfolder/c/carrionette.txt @@ -2,5 +2,5 @@ Name:Carrionette ManaCost:1 B Types:Creature Skeleton PT:1/1 -A:AB$ ChangeZone | Cost$ 2 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | ThisDefinedAndTgts$ Self | Origin$ Battlefield | Destination$ Exile | UnlessCost$ 2 | UnlessPayer$ TargetedController | ActivationZone$ Graveyard | SpellDescription$ Exile CARDNAME and target creature unless that creature's controller pays {2}. Activate only if CARDNAME is in your graveyard. +A:AB$ ChangeZone | Cost$ 2 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | ThisDefinedAndTgts$ Self | Origin$ Battlefield,Graveyard | Destination$ Exile | UnlessCost$ 2 | UnlessPayer$ TargetedController | ActivationZone$ Graveyard | SpellDescription$ Exile CARDNAME and target creature unless that creature's controller pays {2}. Activate only if CARDNAME is in your graveyard. Oracle:{2}{B}{B}: Exile Carrionette and target creature unless that creature's controller pays {2}. Activate only if Carrionette is in your graveyard. diff --git a/forge-gui/res/cardsfolder/d/dream_chisel.txt b/forge-gui/res/cardsfolder/d/dream_chisel.txt index 2a081736acf..29fcc21bd50 100644 --- a/forge-gui/res/cardsfolder/d/dream_chisel.txt +++ b/forge-gui/res/cardsfolder/d/dream_chisel.txt @@ -1,6 +1,6 @@ Name:Dream Chisel ManaCost:2 Types:Artifact -S:Mode$ ReduceCost | ValidCard$ Creature | Type$ MorphDown | Activator$ You | Amount$ 1 | Description$ Face-down creature spells you cast cost {1} less to cast. +S:Mode$ ReduceCost | ValidCard$ Creature | ValidSpell$ Spell.isCastFaceDown | Activator$ You | Amount$ 1 | Description$ Face-down creature spells you cast cost {1} less to cast. AI:RemoveDeck:Random Oracle:Face-down creature spells you cast cost {1} less to cast. diff --git a/forge-gui/res/cardsfolder/i/iron_mastiff.txt b/forge-gui/res/cardsfolder/i/iron_mastiff.txt index 83f805f815c..081c5c28b1e 100644 --- a/forge-gui/res/cardsfolder/i/iron_mastiff.txt +++ b/forge-gui/res/cardsfolder/i/iron_mastiff.txt @@ -2,7 +2,7 @@ Name:Iron Mastiff ManaCost:4 Types:Artifact Creature Dog PT:4/4 -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ DBTrigRollDice | TriggerDescription$ Whenever CARDNAME attacks, roll a d20 for each player being attacked and ignore all but the highest roll. +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ DBTrigRollDice | TriggerDescription$ Whenever CARDNAME attacks, roll a d20 for each player being attacked and ignore all but the highest roll. ABILITY SVar:DBTrigRollDice:DB$ RollDice | Sides$ 20 | Amount$ NbAttackedPlayers | UseHighestRoll$ True | ResultSubAbilities$ 1-9:DBDamageToController,10-19:DBDamageToDefending,20:DBDamageToOpponents SVar:NbAttackedPlayers:PlayerCountOpponents$HasPropertyDefending SVar:DBDamageToController:DB$ DealDamage | Defined$ You | NumDmg$ X | SpellDescription$ 1—9 VERT CARDNAME deals damage equal to its power to you. diff --git a/forge-gui/res/cardsfolder/o/obscuring_aether.txt b/forge-gui/res/cardsfolder/o/obscuring_aether.txt index e6d5c1766c9..44b00cc50fe 100644 --- a/forge-gui/res/cardsfolder/o/obscuring_aether.txt +++ b/forge-gui/res/cardsfolder/o/obscuring_aether.txt @@ -1,7 +1,7 @@ Name:Obscuring Aether ManaCost:G Types:Enchantment -S:Mode$ ReduceCost | ValidCard$ Creature | Type$ MorphDown | Activator$ You | Amount$ 1 | Description$ Face-down creature spells you cast cost {1} less to cast. +S:Mode$ ReduceCost | ValidCard$ Creature | ValidSpell$ Spell.isCastFaceDown | Activator$ You | Amount$ 1 | Description$ Face-down creature spells you cast cost {1} less to cast. A:AB$ SetState | Cost$ 1 G | Defined$ Self | Mode$ TurnFaceDown | SpellDescription$ Turn CARDNAME face down. (It becomes a 2/2 creature.) AI:RemoveDeck:All DeckHints:Keyword$Morph|Megamorph diff --git a/forge-gui/res/cardsfolder/s/suppression_field.txt b/forge-gui/res/cardsfolder/s/suppression_field.txt index 54e21a9075f..7ae4ff55f04 100644 --- a/forge-gui/res/cardsfolder/s/suppression_field.txt +++ b/forge-gui/res/cardsfolder/s/suppression_field.txt @@ -1,6 +1,6 @@ Name:Suppression Field ManaCost:1 W Types:Enchantment -S:Mode$ RaiseCost | ValidCard$ Card | Type$ NonManaAbility | Amount$ 2 | Description$ Activated abilities cost {2} more to activate unless they're mana abilities. +S:Mode$ RaiseCost | ValidCard$ Card | ValidSpell$ Activated.nonManaAbility | Amount$ 2 | Description$ Activated abilities cost {2} more to activate unless they're mana abilities. AI:RemoveDeck:Random Oracle:Activated abilities cost {2} more to activate unless they're mana abilities. diff --git a/forge-gui/res/cardsfolder/z/zirda_the_dawnwaker.txt b/forge-gui/res/cardsfolder/z/zirda_the_dawnwaker.txt index 499966a3a46..8d44fc40955 100644 --- a/forge-gui/res/cardsfolder/z/zirda_the_dawnwaker.txt +++ b/forge-gui/res/cardsfolder/z/zirda_the_dawnwaker.txt @@ -3,6 +3,6 @@ ManaCost:1 RW RW Types:Legendary Creature Elemental Fox PT:3/3 K:Companion:Permanent.hasActivatedAbility,Instant,Sorcery:Each permanent card in your starting deck has an activated ability. -S:Mode$ ReduceCost | ValidCard$ Card | Activator$ You | Type$ NonManaAbility | Amount$ 2 | MinMana$ 1 | Description$ Abilities you activate that aren't mana abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana. +S:Mode$ ReduceCost | ValidCard$ Card | Activator$ You | ValidSpell$ Activated.nonManaAbility | Amount$ 2 | MinMana$ 1 | Description$ Abilities you activate that aren't mana abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana. A:AB$ Pump | Cost$ 1 T | ValidTgts$ Creature | TgtPrompt$ Select target creature | KW$ HIDDEN CARDNAME can't block. | IsCurse$ True | SpellDescription$ Target creature can't block this turn. Oracle:Companion — Each permanent card in your starting deck has an activated ability. (If this card is your chosen companion, you may put it into your hand from outside the game for {3} any time you could cast a sorcery.)\nAbilities you activate that aren't mana abilities cost {2} less to activate. This effect can't reduce the mana in that cost to less than one mana.\n{1}, {T}: Target creature can't block this turn. From 4fb110939a176bc1c1b032b311a0cb933e8cc0d1 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Sat, 30 Nov 2024 19:31:26 +0000 Subject: [PATCH 139/152] Update commander_liara_portyr.txt --- forge-gui/res/cardsfolder/c/commander_liara_portyr.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/c/commander_liara_portyr.txt b/forge-gui/res/cardsfolder/c/commander_liara_portyr.txt index 987be1e46ad..0be926f4a4f 100644 --- a/forge-gui/res/cardsfolder/c/commander_liara_portyr.txt +++ b/forge-gui/res/cardsfolder/c/commander_liara_portyr.txt @@ -5,7 +5,7 @@ PT:5/3 T:Mode$ AttackersDeclared | AttackingPlayer$ You | Execute$ TrigDig | TriggerZones$ Battlefield | TriggerDescription$ Whenever you attack, spells you cast from exile this turn cost {X} less to cast, where X is the number of players being attacked. Exile the top X cards of your library. Until end of turn, you may cast spells from among those exiled cards. SVar:TrigDig:DB$ Dig | DigNum$ X | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect SVar:DBEffect:DB$ Effect | StaticAbilities$ ReduceCost,STPlay | RememberObjects$ Remembered | SetChosenNumber$ X | SubAbility$ DBCleanup -SVar:ReduceCost:Mode$ ReduceCost | EffectZone$ Command | ValidCard$ Card.wasCastFromExile | Type$ Spell | Activator$ You | Amount$ ChosenNumber | Description$ Spells you cast from exile this turn cost {X} less to cast, where X is the number of players you attacked. +SVar:ReduceCost:Mode$ ReduceCost | EffectZone$ Command | ValidCard$ Card.wasCastFromExile | Type$ Spell | Activator$ You | Amount$ Count$ChosenNumber | Description$ Spells you cast from exile this turn cost {X} less to cast, where X is the number of players you attacked. SVar:STPlay:Mode$ Continuous | Affected$ Card.IsRemembered+nonLand | EffectZone$ Command | AffectedZone$ Exile | MayPlay$ True | Description$ Until end of turn, you may cast spells from among those exiled cards. SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:TriggeredPlayersAttackedTarget$Amount From 53541e8f5fdc6768416da418bede5e6c16ed9fcd Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 1 Dec 2024 08:54:58 +0100 Subject: [PATCH 140/152] Update remove-stale-branches.yml (#6645) --- .github/workflows/remove-stale-branches.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/remove-stale-branches.yml b/.github/workflows/remove-stale-branches.yml index 9e5be51edda..00c31a7698e 100644 --- a/.github/workflows/remove-stale-branches.yml +++ b/.github/workflows/remove-stale-branches.yml @@ -15,4 +15,5 @@ jobs: with: dry-run: false # Check out the console output before setting this to false ignore-unknown-authors: true + ignore-branches-with-open-prs: true default-recipient: tehdiplomat From e8a6d4ce9290396be95d6ebf958c09e9e12b3950 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Mon, 2 Dec 2024 09:44:29 +0800 Subject: [PATCH 141/152] prevent crash on Android 11 and below Completablefuture -> completeOnTimeout --- .../src/main/java/forge/ai/AiAttackController.java | 11 ++++++++++- forge-ai/src/main/java/forge/ai/AiController.java | 5 ++++- .../src/main/java/forge/ai/simulation/GameCopier.java | 1 + forge-game/src/main/java/forge/game/Game.java | 4 ++++ .../test/java/forge/ai/simulation/SimulationTest.java | 1 + .../src/forge/screens/settings/SettingsPage.java | 10 ++++++---- .../main/java/forge/gamemodes/match/HostedMatch.java | 3 +++ 7 files changed, 29 insertions(+), 6 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index fa9d32a5645..43ac68318ca 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -79,6 +79,7 @@ public class AiAttackController { private int aiAggression = 0; // how aggressive the ai is attack will be depending on circumstances private final boolean nextTurn; // include creature that can only attack/block next turn private final int timeOut; + private final boolean canUseTimeout; private List> futures = new ArrayList<>(); /** @@ -98,6 +99,7 @@ public class AiAttackController { this.nextTurn = nextTurn; refreshCombatants(defendingOpponent); this.timeOut = ai.getGame().getAITimeout(); + this.canUseTimeout = ai.getGame().canUseTimeout(); } // overloaded constructor to evaluate attackers that should attack next turn public AiAttackController(final Player ai, Card attacker) { @@ -112,6 +114,7 @@ public class AiAttackController { } this.blockers = getPossibleBlockers(oppList, this.attackers, this.nextTurn); this.timeOut = ai.getGame().getAITimeout(); + this.canUseTimeout = ai.getGame().canUseTimeout(); } // overloaded constructor to evaluate single specified attacker private void refreshCombatants(GameEntity defender) { @@ -967,10 +970,16 @@ public class AiAttackController { numForcedAttackers.incrementAndGet(); } return 0; + }).exceptionally(ex -> { + ex.printStackTrace(); + return 0; })); } CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); - CompletableFuture.allOf(futuresArray).completeOnTimeout(null, timeOut, TimeUnit.SECONDS).join(); + if (canUseTimeout) + CompletableFuture.allOf(futuresArray).completeOnTimeout(null, timeOut, TimeUnit.SECONDS).join(); + else + CompletableFuture.allOf(futuresArray).join(); futures.clear(); if (attackersLeft.isEmpty()) { return aiAggression; diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 1688adbf056..64c08e7574e 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -1684,7 +1684,10 @@ public class AiController { // instead of computing all available concurrently just add a simple timeout depending on the user prefs try { - return future.completeOnTimeout(null, game.getAITimeout(), TimeUnit.SECONDS).get(); + if (game.AI_CAN_USE_TIMEOUT) + return future.completeOnTimeout(null, game.getAITimeout(), TimeUnit.SECONDS).get(); + else + return future.get(); } catch (InterruptedException | ExecutionException e) { return null; } diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java index 2108118976b..38a90a59112 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -216,6 +216,7 @@ public class GameCopier { private void copyGameState(Game newGame, Player aiPlayer) { newGame.EXPERIMENTAL_RESTORE_SNAPSHOT = origGame.EXPERIMENTAL_RESTORE_SNAPSHOT; newGame.AI_TIMEOUT = origGame.AI_TIMEOUT; + newGame.AI_CAN_USE_TIMEOUT = origGame.AI_CAN_USE_TIMEOUT; newGame.setAge(origGame.getAge()); // TODO countersAddedThisTurn diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 640638135e2..489672d2a04 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -90,6 +90,7 @@ public class Game { private final Zone stackZone = new Zone(ZoneType.Stack, this); public int AI_TIMEOUT = 5; + public boolean AI_CAN_USE_TIMEOUT = true; public boolean EXPERIMENTAL_RESTORE_SNAPSHOT = false; // While this is false here, its really set by the Match/Preferences @@ -1359,4 +1360,7 @@ public class Game { public int getAITimeout() { return AI_TIMEOUT; } + public boolean canUseTimeout() { + return AI_CAN_USE_TIMEOUT; + } } diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java index a4d9ecf6b9f..d3481d2b46b 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java @@ -48,6 +48,7 @@ public class SimulationTest { game.setAge(GameStage.Play); game.EXPERIMENTAL_RESTORE_SNAPSHOT = false; game.AI_TIMEOUT = FModel.getPreferences().getPrefInt(FPref.MATCH_AI_TIMEOUT); + game.AI_CAN_USE_TIMEOUT = true; //Only Android is restricted according to API Level return game; } diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java index ca1252e12c4..5738c1497ef 100644 --- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java +++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java @@ -244,10 +244,12 @@ public class SettingsPage extends TabPage { Forge.getLocalizer().getMessage("cbExperimentalRestore"), Forge.getLocalizer().getMessage("nlExperimentalRestore")), 1); - lstSettings.addItem(new CustomSelectSetting(FPref.MATCH_AI_TIMEOUT, Forge.getLocalizer().getMessage("cbAITimeout"), - Forge.getLocalizer().getMessage("nlAITimeout"), - Lists.newArrayList("5", "10", "60", "120", "240", "300", "600")), - 1); + if (!GuiBase.isAndroid() || GuiBase.getAndroidAPILevel() > 30) { + lstSettings.addItem(new CustomSelectSetting(FPref.MATCH_AI_TIMEOUT, Forge.getLocalizer().getMessage("cbAITimeout"), + Forge.getLocalizer().getMessage("nlAITimeout"), + Lists.newArrayList("5", "10", "60", "120", "240", "300", "600")), + 1); + } lstSettings.addItem(new BooleanSetting(FPref.FILTERED_HANDS, Forge.getLocalizer().getMessage("cbFilteredHands"), Forge.getLocalizer().getMessage("nlFilteredHands")), diff --git a/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java b/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java index fc04e5afecb..901e446ec96 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java @@ -156,6 +156,9 @@ public class HostedMatch { game = match.createGame(); game.EXPERIMENTAL_RESTORE_SNAPSHOT = FModel.getPreferences().getPrefBoolean(FPref.MATCH_EXPERIMENTAL_RESTORE); game.AI_TIMEOUT = FModel.getPreferences().getPrefInt(FPref.MATCH_AI_TIMEOUT); + // Android API 31 and above can use completeOnTimeout -> CompletableFuture: + //https://developer.android.com/reference/java/util/concurrent/CompletableFuture#completeOnTimeout(T,%20long,%20java.util.concurrent.TimeUnit) + game.AI_CAN_USE_TIMEOUT = !GuiBase.isAndroid() || GuiBase.getAndroidAPILevel() > 30; StaticData.instance().setSourceImageForClone(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE)); From d733b8933e5ae25e492f69adc7bc04aef38fdd20 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Mon, 2 Dec 2024 10:10:00 +0800 Subject: [PATCH 142/152] enable timeout on chooseSpellAbilityToPlayFromList --- forge-ai/src/main/java/forge/ai/AiController.java | 5 +++-- .../src/forge/screens/settings/SettingsPage.java | 10 ++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 64c08e7574e..b6d99434e71 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -73,6 +73,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** *

@@ -1687,8 +1688,8 @@ public class AiController { if (game.AI_CAN_USE_TIMEOUT) return future.completeOnTimeout(null, game.getAITimeout(), TimeUnit.SECONDS).get(); else - return future.get(); - } catch (InterruptedException | ExecutionException e) { + return future.get(game.getAITimeout(), TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { return null; } } diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java index 5738c1497ef..ca1252e12c4 100644 --- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java +++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java @@ -244,12 +244,10 @@ public class SettingsPage extends TabPage { Forge.getLocalizer().getMessage("cbExperimentalRestore"), Forge.getLocalizer().getMessage("nlExperimentalRestore")), 1); - if (!GuiBase.isAndroid() || GuiBase.getAndroidAPILevel() > 30) { - lstSettings.addItem(new CustomSelectSetting(FPref.MATCH_AI_TIMEOUT, Forge.getLocalizer().getMessage("cbAITimeout"), - Forge.getLocalizer().getMessage("nlAITimeout"), - Lists.newArrayList("5", "10", "60", "120", "240", "300", "600")), - 1); - } + lstSettings.addItem(new CustomSelectSetting(FPref.MATCH_AI_TIMEOUT, Forge.getLocalizer().getMessage("cbAITimeout"), + Forge.getLocalizer().getMessage("nlAITimeout"), + Lists.newArrayList("5", "10", "60", "120", "240", "300", "600")), + 1); lstSettings.addItem(new BooleanSetting(FPref.FILTERED_HANDS, Forge.getLocalizer().getMessage("cbFilteredHands"), Forge.getLocalizer().getMessage("nlFilteredHands")), From 5008c519a13f5d81e83bdf5401e01012fb5cd9a0 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Mon, 2 Dec 2024 12:28:19 +0000 Subject: [PATCH 143/152] Update pollen_shield_hare_hare_raising.txt --- forge-gui/res/cardsfolder/p/pollen_shield_hare_hare_raising.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/p/pollen_shield_hare_hare_raising.txt b/forge-gui/res/cardsfolder/p/pollen_shield_hare_hare_raising.txt index f360c4e3003..77094e01fa0 100644 --- a/forge-gui/res/cardsfolder/p/pollen_shield_hare_hare_raising.txt +++ b/forge-gui/res/cardsfolder/p/pollen_shield_hare_hare_raising.txt @@ -12,6 +12,6 @@ ALTERNATE Name:Hare Raising ManaCost:G Types:Sorcery Adventure -A:SP$ Pump | ValidTgts$ Creature | NumAtt$ X | NumDef$ X | KW$ Vigilance | SpellDescription$ Target creature you control gains vigilance and gets +X/+X until end of turn, where X is the number of creatures you control. +A:SP$ Pump | ValidTgts$ Creature.YouCtrl | NumAtt$ X | NumDef$ X | KW$ Vigilance | SpellDescription$ Target creature you control gains vigilance and gets +X/+X until end of turn, where X is the number of creatures you control. SVar:X:Count$Valid Creature.YouCtrl Oracle:Target creature you control gains vigilance and gets +X/+X until end of turn, where X is the number of creatures you control. From c88bdc2e1d86c029672b5c4ef3ab47e95fb6f6a4 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Mon, 2 Dec 2024 12:42:14 +0000 Subject: [PATCH 144/152] Update protect_serve.txt --- forge-gui/res/cardsfolder/p/protect_serve.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/p/protect_serve.txt b/forge-gui/res/cardsfolder/p/protect_serve.txt index ce3b0fbb958..c8d28e2bcf9 100644 --- a/forge-gui/res/cardsfolder/p/protect_serve.txt +++ b/forge-gui/res/cardsfolder/p/protect_serve.txt @@ -2,7 +2,7 @@ Name:Protect ManaCost:2 W Types:Instant K:Fuse -A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature to get +2/+4 | NumAtt$ +2 | NumDef$ +4 | SpellDescription$ Target creature you control gets +2/+4 until end of turn. +A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature to get +2/+4 | NumAtt$ +2 | NumDef$ +4 | SpellDescription$ Target creature gets +2/+4 until end of turn. AlternateMode:Split Oracle:Target creature gets +2/+4 until end of turn.\nFuse (You may cast one or both halves of this card from your hand.) From b0fdd39371e281a4cf3eb46739974e9cfcaa54ea Mon Sep 17 00:00:00 2001 From: tool4ever Date: Mon, 2 Dec 2024 12:43:51 +0000 Subject: [PATCH 145/152] Update touch_of_moonglove.txt --- forge-gui/res/cardsfolder/t/touch_of_moonglove.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/t/touch_of_moonglove.txt b/forge-gui/res/cardsfolder/t/touch_of_moonglove.txt index 9f2ea44b19d..3f2c45f3c2f 100644 --- a/forge-gui/res/cardsfolder/t/touch_of_moonglove.txt +++ b/forge-gui/res/cardsfolder/t/touch_of_moonglove.txt @@ -1,7 +1,7 @@ Name:Touch of Moonglove ManaCost:B Types:Instant -A:SP$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +1 | KW$ Deathtouch | SubAbility$ DBMoonglove | SpellDescription$ Target creature you control gets +1/+0 and gains deathtouch until end of turn. Whenever a creature dealt damage by that creature dies this turn, its controller loses 2 life. (Any amount of damage a creature with deathtouch deals to a creature is enough to destroy it.) +A:SP$ Pump | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | NumAtt$ +1 | KW$ Deathtouch | SubAbility$ DBMoonglove | SpellDescription$ Target creature you control gets +1/+0 and gains deathtouch until end of turn. Whenever a creature dealt damage by that creature dies this turn, its controller loses 2 life. (Any amount of damage a creature with deathtouch deals to a creature is enough to destroy it.) SVar:DBMoonglove:DB$ Effect | Triggers$ MoongloveTrigger | RememberObjects$ Targeted | StackDescription$ Whenever a creature dealt damage by that creature dies this turn, its controller loses 2 life. SVar:MoongloveTrigger:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.DamagedBy Remembered | TriggerZones$ Command | Execute$ TrigLoseLife | TriggerDescription$ Whenever a creature dealt damage by that creature dies this turn, its controller loses 2 life. SVar:TrigLoseLife:DB$ LoseLife | LifeAmount$ 2 | Defined$ TriggeredCardController From eb2a584f689f9c33b0b8a353c3680b0742bb5274 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Mon, 2 Dec 2024 12:44:34 +0000 Subject: [PATCH 146/152] Update treetop_snarespinner.txt --- forge-gui/res/cardsfolder/t/treetop_snarespinner.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-gui/res/cardsfolder/t/treetop_snarespinner.txt b/forge-gui/res/cardsfolder/t/treetop_snarespinner.txt index 0cf35d78a89..751b0283002 100644 --- a/forge-gui/res/cardsfolder/t/treetop_snarespinner.txt +++ b/forge-gui/res/cardsfolder/t/treetop_snarespinner.txt @@ -4,6 +4,6 @@ Types:Creature Spider PT:1/4 K:Reach K:Deathtouch -A:AB$ PutCounter | Cost$ 2 G | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SorcerySpeed$ True | SpellDescription$ Put a +1/+1 counter on target creature you control. Activate only as a sorcery. +A:AB$ PutCounter | Cost$ 2 G | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1 | SorcerySpeed$ True | SpellDescription$ Put a +1/+1 counter on target creature you control. Activate only as a sorcery. DeckHas:Ability$Counters -Oracle:Reach (This creature can block creatures with flying.)\nDeathtouch (Any amount of damage this deals to a creature is enough to destroy it.)\n{2}{G}: Put a +1/+1 counter on target creature you control. Activate only as a sorcery. \ No newline at end of file +Oracle:Reach (This creature can block creatures with flying.)\nDeathtouch (Any amount of damage this deals to a creature is enough to destroy it.)\n{2}{G}: Put a +1/+1 counter on target creature you control. Activate only as a sorcery. From ecd257777023f71f3a3d5e2a3bc2600acb4e5ad7 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Mon, 2 Dec 2024 12:50:50 +0000 Subject: [PATCH 147/152] Update embereth_veteran.txt --- forge-gui/res/cardsfolder/e/embereth_veteran.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/e/embereth_veteran.txt b/forge-gui/res/cardsfolder/e/embereth_veteran.txt index fd12470714f..c0d3d4c65c2 100644 --- a/forge-gui/res/cardsfolder/e/embereth_veteran.txt +++ b/forge-gui/res/cardsfolder/e/embereth_veteran.txt @@ -2,6 +2,6 @@ Name:Embereth Veteran ManaCost:R Types:Creature Human Knight PT:2/1 -A:AB$ Token | Cost$ 1 Sac<1/CARDNAME> | TokenAmount$ 1 | TokenScript$ role_young_hero | AttachedTo$ Targeted | ValidTgts$ Creature.YouCtrl+Other | TgtPrompt$ Select another target creature you control | SpellDescription$ Create a Young Hero Role token attached to another target creature. (If you control another Role on it, put that one into the graveyard. Enchanted creature has "Whenever this creature attacks, if its toughness is 3 or less, put a +1/+1 counter on it.") +A:AB$ Token | Cost$ 1 Sac<1/CARDNAME> | TokenAmount$ 1 | TokenScript$ role_young_hero | AttachedTo$ Targeted | ValidTgts$ Creature.Other | TgtPrompt$ Select another target creature | SpellDescription$ Create a Young Hero Role token attached to another target creature. (If you control another Role on it, put that one into the graveyard. Enchanted creature has "Whenever this creature attacks, if its toughness is 3 or less, put a +1/+1 counter on it.") DeckHas:Ability$Token|Sacrifice & Type$Aura|Enchantment|Role Oracle:{1}, Sacrifice Embereth Veteran: Create a Young Hero Role token attached to another target creature. (If you control another Role on it, put that one into the graveyard. Enchanted creature has "Whenever this creature attacks, if its toughness is 3 or less, put a +1/+1 counter on it.") From 560f660925e1b33b72b2e62a78ef87027d785a19 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Mon, 2 Dec 2024 12:51:51 +0000 Subject: [PATCH 148/152] Update revitalizing_repast_old_growth_grove.txt --- .../res/cardsfolder/r/revitalizing_repast_old_growth_grove.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-gui/res/cardsfolder/r/revitalizing_repast_old_growth_grove.txt b/forge-gui/res/cardsfolder/r/revitalizing_repast_old_growth_grove.txt index c9f63353a4d..b7e69124c9c 100644 --- a/forge-gui/res/cardsfolder/r/revitalizing_repast_old_growth_grove.txt +++ b/forge-gui/res/cardsfolder/r/revitalizing_repast_old_growth_grove.txt @@ -1,7 +1,7 @@ Name:Revitalizing Repast ManaCost:BG Types:Instant -A:SP$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on target creature. +A:SP$ PutCounter | ValidTgts$ Creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPump | SpellDescription$ Put a +1/+1 counter on target creature. SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ Indestructible | SpellDescription$ It gains indestructible until end of turn. DeckHas:Ability$Counters AlternateMode:Modal From 739a4ef0bd883de28279f50ab6610be69c4c553c Mon Sep 17 00:00:00 2001 From: tool4ever Date: Mon, 2 Dec 2024 21:44:11 +0100 Subject: [PATCH 149/152] Fix RollDice (#6653) --- forge-game/src/main/java/forge/game/GameLog.java | 6 +----- .../src/main/java/forge/game/GameOutcome.java | 13 +------------ .../forge/game/ability/effects/RollDiceEffect.java | 2 +- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameLog.java b/forge-game/src/main/java/forge/game/GameLog.java index 6b7f7587ace..e29a1b00cb9 100644 --- a/forge-game/src/main/java/forge/game/GameLog.java +++ b/forge-game/src/main/java/forge/game/GameLog.java @@ -37,8 +37,6 @@ public class GameLog extends Observable implements Serializable { private final List log = new ArrayList<>(); private final transient GameLogFormatter formatter = new GameLogFormatter(this); - - private final boolean quiet = false; /** Logging level: * 0 - Turn @@ -56,8 +54,6 @@ public class GameLog extends Observable implements Serializable { } void add(GameLogEntry entry) { - if (quiet) return; - log.add(entry); this.setChanged(); this.notifyObservers(); @@ -92,7 +88,7 @@ public class GameLog extends Observable implements Serializable { } return result; } - + public IGameEventVisitor getEventVisitor() { return formatter; } diff --git a/forge-game/src/main/java/forge/game/GameOutcome.java b/forge-game/src/main/java/forge/game/GameOutcome.java index 03ab133b9fd..67bc61a76ac 100644 --- a/forge-game/src/main/java/forge/game/GameOutcome.java +++ b/forge-game/src/main/java/forge/game/GameOutcome.java @@ -87,22 +87,11 @@ public final class GameOutcome implements Iterable players) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java index 99cc2718796..3d3dc84063b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RollDiceEffect.java @@ -129,7 +129,7 @@ public class RollDiceEffect extends SpellAbilityEffect { } } - if (sa.hasParam("UseHighestRoll")) { + if (sa != null && sa.hasParam("UseHighestRoll")) { naturalRolls.subList(0, naturalRolls.size() - 1).clear(); } From e6deda205c2f16ac8f253a27667e0f56ba0f2826 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Mon, 2 Dec 2024 22:29:40 +0100 Subject: [PATCH 150/152] Fully unlocked room traits should still be the same (for once per turn) --- .../java/forge/game/ability/effects/ClassLevelUpEffect.java | 3 +-- forge-game/src/main/java/forge/game/card/CardFactory.java | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ClassLevelUpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ClassLevelUpEffect.java index 16dbe0f52e8..f9dd81a5e3c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ClassLevelUpEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ClassLevelUpEffect.java @@ -4,7 +4,6 @@ import java.util.Map; import forge.game.Game; import forge.game.ability.AbilityKey; -import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.spellability.SpellAbility; @@ -21,7 +20,7 @@ public class ClassLevelUpEffect extends SpellAbilityEffect { final Game game = host.getGame(); int level = host.getClassLevel(); - if (AbilityUtils.calculateAmount(host, sa.getRestrictions().getClassLevel(), sa) != level) { + if (!sa.isClassLevelNAbility(level)) { return; } diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 6e218f09e25..c8bada15d85 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -261,17 +261,17 @@ public class CardFactory { original.addIntrinsicKeywords(card.getCurrentState().getIntrinsicKeywords()); // Copy 'Fuse' to original side for (Trigger t : card.getCurrentState().getTriggers()) { if (t.isIntrinsic()) { - original.addTrigger(t.copy(card, false)); + original.addTrigger(t); } } for (StaticAbility st : card.getCurrentState().getStaticAbilities()) { if (st.isIntrinsic()) { - original.addStaticAbility(st.copy(card, false)); + original.addStaticAbility(st); } } for (ReplacementEffect re : card.getCurrentState().getReplacementEffects()) { if (re.isIntrinsic()) { - original.addReplacementEffect(re.copy(card, false)); + original.addReplacementEffect(re); } } original.getSVars().putAll(card.getCurrentState().getSVars()); // Unfortunately need to copy these to (Effect looks for sVars on execute) From a94dc8d94f38dba15da2c46ca67146ea0b22f91e Mon Sep 17 00:00:00 2001 From: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:27:41 +0000 Subject: [PATCH 151/152] Edition updates: PF25, PIO, PMEI --- forge-gui/res/editions/MagicFest 2025.txt | 9 + forge-gui/res/editions/Media Inserts.txt | 10 +- forge-gui/res/editions/Pioneer Masters.txt | 421 +++++++++++++++++++-- 3 files changed, 411 insertions(+), 29 deletions(-) create mode 100644 forge-gui/res/editions/MagicFest 2025.txt diff --git a/forge-gui/res/editions/MagicFest 2025.txt b/forge-gui/res/editions/MagicFest 2025.txt new file mode 100644 index 00000000000..80c60b04ae6 --- /dev/null +++ b/forge-gui/res/editions/MagicFest 2025.txt @@ -0,0 +1,9 @@ +[metadata] +Code=PF25 +Date=2025-01-01 +Name=MagicFest 2025 +Type=Promo +ScryfallCode=PF25 + +[cards] +1 R Avacyn's Pilgrim @Mark Zug diff --git a/forge-gui/res/editions/Media Inserts.txt b/forge-gui/res/editions/Media Inserts.txt index 56313c220c1..5d2c20f3c74 100644 --- a/forge-gui/res/editions/Media Inserts.txt +++ b/forge-gui/res/editions/Media Inserts.txt @@ -17,7 +17,8 @@ ScryfallCode=PMEI 13 C Silver Drake @Alan Pollack 14 C Phyrexian Rager @Mark Tedin 15 R Shivan Dragon @Donato Giancola -16 R Darksteel Juggernaut @Randis Albion +16 M Jace Beleren @Aleksi Briclot +16j R Darksteel Juggernaut @Randis Albion 17 U Cunning Sparkmage @Wayne Reynolds 18 C Chandra's Outrage @Christopher Moeller 19 U Chandra's Spitfire @Justin Sweet @@ -60,13 +61,10 @@ ScryfallCode=PMEI 56 R Pyromancer's Gauntlet @Magali Villeneuve 57 R Culling the Weak @Scott M. Fischer 58 R Winged Boots @Filipe Pagliuso -59 R Goro-Goro, Disciple of Ryusei @Mike Jordana 60 C Talruum Champion @Pete Venters 61 C Shield Wall @Scott Kirschner -62 R Raiyuu, Storm's Edge @Heonhwa Choe 63 R Snuff Out @Mike Ploog -66 R Saryth, the Viper's Fang @Igor Kieryluk 67 R Gush @Kev Walker +80 M Jace, Memory Adept @D. Alexander Gregory +81 M Ajani, Mentor of Heroes @Aaron Miller 121 C Spined Wurm @Keith Parkinson -211 R Park Heights Pegasus @Randy Gallegos -232 R Liesa, Forgotten Archangel @Dmitry Burmak diff --git a/forge-gui/res/editions/Pioneer Masters.txt b/forge-gui/res/editions/Pioneer Masters.txt index 9443c04226b..6caa2c9a2b9 100644 --- a/forge-gui/res/editions/Pioneer Masters.txt +++ b/forge-gui/res/editions/Pioneer Masters.txt @@ -6,26 +6,401 @@ Type=Reprint ScryfallCode=PIO [cards] -0 U Bane of Bala Ged @Chase Stone -0 R Silence @Wayne Reynolds -0 U Hidden Strings @Daarken -0 M Jace, Vryn's Prodigy @Jaime Jones -0 M Temporal Trespass @Clint Cearley -0 C Void Shatter @Yohann Schepacz -0 M Behold the Beyond @Madeline Boni -0 R Dark Petition @Igor Kieryluk -0 C Gurmag Angler @YW Tang -0 M Liliana, Heretical Healer @Karla Ortiz -0 R Tasigur, the Golden Fang @Chris Rahn -0 R Legion Loyalist @Eric Deschamps -0 M Purphoros, God of the Forge @Eric Deschamps -0 C Gladecover Scout @Brian Valeza -0 R Hornet Nest @Adam Paquette -0 R Oath of Nissa @Wesley Burt -0 R Sylvan Caryatid @Chase Stone -0 C Voyaging Satyr @Tyler Jacobson -0 M Athreos, God of Passage @Ryan Barger -0 R Bring to Light @Jonas De Ro -0 M Mogis, God of Slaughter @Chase Stone -0 M The Chain Veil @Volkan Baǵa -0 R Urborg, Tomb of Yawgmoth @John Avon +94223 U Bane of Bala Ged @Chase Stone +94224 U Scion of Ugin @ +94225 M Void Winnower @ +94226 M Archangel of Thune @ +94227 U Archway Angel @ +94228 C Ardenvale Tactician @ +94230 C Artful Maneuver @ +94231 R Celestial Archon @ +94232 R Chained to the Rocks @ +94233 C Compulsory Rest @ +94234 R Dictate of Heliod @ +94235 U Extricator of Sin @ +94237 U Ghostblade Eidolon @ +94238 M Gideon, Ally of Zendikar @ +94239 C Heliod's Pilgrim @ +94240 R Hero of Iroas @ +94241 C Hopeful Eidolon @ +94242 R Imposing Sovereign @ +94243 U Kabira Takedown @ +94245 C Keening Apparition @ +94246 R Knight of the White Orchid @ +94247 C Knightly Valor @ +94248 M Kytheon, Hero of Akros @ +94250 C Lagonna-Band Trailblazer @ +94251 M Linvala, the Preserver @ +94252 U Lotus-Eye Mystics @ +94253 R Mentor of the Meek @ +94254 U Phalanx Leader @ +94255 U Phalanx Tactics @ +94256 R Secure the Wastes @ +94257 U Sejiri Shelter @ +94259 R Silence @Wayne Reynolds +94260 U Silkwrap @ +94261 U Sphere of Safety @ +94262 R Spirit of the Labyrinth @ +94263 M Starfield of Nyx @ +94264 U Stasis Snare @ +94265 U Steward of Solidarity @ +94266 U Swift Reckoning @ +94267 C Syndicate Messenger @ +94268 C Triplicate Spirits @ +94269 U War Oracle @ +94270 R Aetherling @ +94271 U Anchor to the Aether @ +94272 C Aqueous Form @ +94273 R Artisan of Forms @ +94274 R Bident of Thassa @ +94275 U Brineborn Cutthroat @ +94276 R Chasm Skulker @ +94277 C Cloudfin Raptor @ +94278 C Consider @ +94279 M Crush of Tentacles @ +94280 M Day's Undoing @ +94281 C Essence Scatter @ +94282 C Fallaji Archaeologist @ +94283 U Guild Summit @ +94284 C Gust of Wind @ +94285 U Illusory Angel @ +94286 C Ingenious Skaab @ +94287 M Jace, Vryn's Prodigy @Jaime Jones +94289 U Jhessian Thief @ +94290 U Jwari Disruption @ +94292 M Master of Waves @ +94293 C Mizzium Skin @ +94294 U Murmuring Mystic @ +94295 R Niblis of Frost @ +94296 C Nimbus Naiad @ +94297 C Opal Lake Gatekeepers @ +94298 R Quicken @ +94299 U Rapid Hybridization @ +94300 R Scatter to the Winds @ +94301 R Shipbreaker Kraken @ +94302 U Sight Beyond Sight @ +94303 U Silundi Vision @ +94305 R Stormtide Leviathan @ +94306 C Tah-Crop Skirmisher @ +94307 M Temporal Trespass @Clint Cearley +94308 R Tidebinder Mage @ +94309 C Treasure Cruise @ +94310 U Windrider Patrol @ +94311 C Zephyr Winder @ +94312 C Baleful Eidolon @ +94313 C Basilica Screecher @ +94314 M Behold the Beyond @Madeline Boni +94315 U Blackbloom Rogue @ +94317 R Blood Scrivener @ +94318 U Cruel Revival @ +94319 C Crypt Incursion @ +94320 U Dark Deal @ +94321 C Devour Flesh @ +94322 R Dictate of Erebos @ +94323 U Dreadhound @ +94324 U Fell Stinger @ +94325 C Gurmag Angler @YW Tang +94326 R Lifebane Zombie @ +94327 M Liliana, Heretical Healer @Karla Ortiz +94329 C Nantuko Husk @ +94330 R Nighthowler @ +94331 M Ob Nixilis Reignited @ +94332 C Ob Nixilis's Cruelty @ +94333 R Painful Truths @ +94334 U Pelakka Predation @ +94336 R Priest of the Blood Rite @ +94337 C Read the Bones @ +94338 U Rescue from the Underworld @ +94339 C Returned Centaur @ +94340 C Sanitarium Skeleton @ +94341 R Sidisi, Undead Vizier @ +94342 C Silumgar Butcher @ +94343 R Soulflayer @ +94344 U Stab Wound @ +94345 C Supernatural Stamina @ +94346 R Tasigur, the Golden Fang @Chris Rahn +94347 U Tormented Hero @ +94348 C Ubul Sar Gatekeepers @ +94349 U Ultimate Price @ +94350 U Undead Butler @ +94351 R Urborg, Tomb of Yawgmoth @John Avon +94352 R Whip of Erebos @ +94353 R Xathrid Necromancer @ +94354 U Zulaport Cutthroat @ +94355 U Akoum Warrior @ +94357 C Akroan Crusader @ +94358 U Bloodfire Enforcers @ +94359 C Boulder Salvo @ +94360 U Boundary Lands Ranger @ +94361 U Burning Anger @ +94362 M Chandra, Flamecaller @ +94363 U Coordinated Assault @ +94364 U Draconic Roar @ +94365 C Dragon Mantle @ +94366 R Dragon-Style Twins @ +94367 R Exquisite Firecraft @ +94368 C Fall of the Hammer @ +94369 U Fight with Fire @ +94370 U Furious Rise @ +94371 U Gates Ablaze @ +94372 C Ghirapur Gearcrafter @ +94373 R Goblin Rabblemaster @ +94374 R Heart-Piercer Manticore @ +94375 U Humble Defector @ +94376 M Kozilek's Return @ +94377 R Labyrinth Champion @ +94378 R Legion Loyalist @Eric Deschamps +94379 C Makindi Sliderunner @ +94380 U Mogis's Warhound @ +94381 U Monastery Swiftspear @ +94382 R Oath of Chandra @ +94383 U Ordeal of Purphoros @ +94384 R Outpost Siege @ +94385 R Pia and Kiran Nalaar @ +94386 U Purphoros's Emissary @ +94387 C Rimrock Knight @ +94389 R Scab-Clan Berserker @ +94390 M Scourge of Valkas @ +94391 U Scytheclaw Raptor @ +94392 M Stormbreath Dragon @ +94393 R Valakut Awakening @ +94395 U Wild Slash @ +94396 C Witch's Mark @ +94397 R Zurgo Bellstriker @ +94398 U Alpha Authority @ +94399 C Aspect of Hydra @ +94400 U Audacity @ +94401 U Bala Ged Recovery @ +94403 U Bassara Tower Archer @ +94404 R Boon Satyr @ +94405 U Clear Shot @ +94406 C Commune with the Gods @ +94407 U Conclave Naturalists @ +94408 U Courier's Briefcase @ +94409 U District Guide @ +94410 U Experiment One @ +94411 U Gatebreaker Ram @ +94412 C Gladecover Scout @Brian Valeza +94413 U Gnarlback Rhino @ +94414 R Goreclaw, Terror of Qal Sisma +94415 R Hero of Leina Tower @ +94416 R Honored Hydra @ +94417 R Hornet Nest @Adam Paquette +94418 U Khalni Ambush @ +94420 U Kraul Harpooner @ +94421 C Leafcrown Dryad @ +94422 R Mistcutter Hydra @ +94423 U Nemesis of Mortals @ +94424 C Nessian Asp @ +94425 M Nissa, Vastwood Seer @ +94427 M Nissa, Voice of Zendikar @ +94428 R Oath of Nissa @ +94429 M Polukranos, World Eater @ +94430 U Pulse of Murasa @ +94431 C Savage Punch @ +94432 U Seed Guardian @ +94433 R Shamanic Revelation @ +94434 R Skylasher @ +94435 R Sylvan Caryatid @Chase Stone +94436 R Sylvan Primordial @ +94437 U Unravel the Aether @ +94438 C Voyaging Satyr @Tyler Jacobson +94439 M Whisperwood Elemental @ +94440 R Woodland Wanderer @ +94441 R Anax and Cymede @ +94442 M Ashen Rider @ +94443 R Assemble the Legion @ +94444 U Azorius Charm @ +94445 M Blood Baron of Vizkopa @ +94446 U Bloodtithe Harvester @ +94447 R Boros Reckoner @ +94448 R Bring to Light @Jonas De Ro +94449 U Cartel Aristocrat @ +94450 U Catacomb Sifter @ +94451 M Chromanticore @ +94452 R Counterflux @ +94453 U Destructive Revelry @ +94454 U Dinrova Horror @ +94455 M Dragonlord Atarka @ +94456 M Dragonlord Dromoka @ +94457 M Dragonlord Kolaghan @ +94458 M Dragonlord Ojutai @ +94459 M Dragonlord Silumgar @ +94460 R Dreadbore @ +94461 U Dreg Mangler @ +94462 M Epic Experiment @ +94463 R Fleecemane Lion @ +94464 M Garruk, Apex Predator @ +94465 U Ghor-Clan Rampager @ +94466 C Imperious Oligarch @ +94467 U Kiora's Follower @ +94468 R Lotleth Troll @ +94469 R Loxodon Smiter @ +94470 U Lyev Skyknight @ +94471 C Martial Glory @ +94472 M Medomai the Ageless @ +94473 C Nivix Cyclops @ +94474 R Notion Thief @ +94475 U Nyx Weaver @ +94476 U Possessed Skaab @ +94477 M Progenitor Mimic @ +94478 R Ruric Thar, the Unbowed @ +94479 U Selesnya Charm @ +94480 U Sin Collector @ +94481 R Sire of Insanity @ +94482 U Skyrider Elf @ +94483 R Steam Augury @ +94484 U Stormchaser Mage @ +94485 U Swift Warkite @ +94486 U Tenth District Legionnaire @ +94487 U Thunderclap Wyvern @ +94488 U Tomebound Lich @ +94489 U Unflinching Courage @ +94490 U Urban Evolution @ +94491 U Zendikar Incarnate @ +94492 U Crackdown Construct @ +94493 U Darksteel Ingot @ +94494 U Gate Colossus @ +94495 C Halo Scarab @ +94496 C Pilgrim's Eye @ +94497 C Azorius Guildgate @ +94498 C Boros Guildgate @ +94499 C Dimir Guildgate @ +94500 C Evolving Wilds @ +94501 C Gateway Plaza @ +94502 C Golgari Guildgate @ +94503 C Gruul Guildgate @ +94504 R Haven of the Spirit Dragon @ +94505 R Hissing Quagmire @ +94506 C Izzet Guildgate @ +94507 R Lumbering Falls @ +94508 M Maze's End @ +94509 R Needle Spires @ +94510 C Orzhov Guildgate @ +94511 C Rakdos Guildgate @ +94512 U Rogue's Passage @ +94513 C Selesnya Guildgate @ +94514 R Shambling Vent @ +94515 C Simic Guildgate @ +94516 U Spawning Bed @ +94517 R Wandering Fumarole @ +94518 U Eidolon of Rhetoric @ +94519 U Evangel of Heliod @ +94520 R Fiendslayer Paladin @ +94521 M Heliod, God of the Sun @ +94522 C Wingsteed Rider @ +94523 C Archaeomancer @ +94524 U Silumgar Sorcerer @ +94525 R Sphinx of Magosi @ +94526 U Sphinx's Tutelage @ +94527 M Thassa, God of the Sea @ +94528 R Abhorrent Overlord @ +94529 C Disciple of Phenax @ +94530 M Erebos, God of the Dead @ +94531 U Illness in the Ranks @ +94532 U Keepsake Gorgon @ +94533 R Ash Zealot @ +94534 U Fanatic of Mogis @ +94535 M Purphoros, God of the Forge @Eric Deschamps +94536 U Pyromancer's Assault @ +94537 U Stoneshock Giant @ +94538 R Avatar of the Resolute @ +94539 U Bounding Krasis @ +94540 M Nylea, God of the Hunt @ +94541 C Nylea's Disciple @ +94542 R Reverent Hunter @ +94543 M Athreos, God of Passage @Ryan Barger +94544 M Ephara, God of the Polis @ +94545 C Frostburn Weird @ +94546 U Gift of Orzhova @ +94547 R Growing Ranks @ +94548 M Iroas, God of Victory @ +94549 M Karametra, God of Harvests @ +94550 M Keranos, God of Storms @ +94551 M Kruphix, God of Horizons @ +94552 M Mogis, God of Slaughter @Chase Stone +94553 R Nightveil Specter @ +94554 M Pharika, God of Affliction @ +94555 M Phenax, God of Deception @ +94556 R Rubblebelt Raiders @ +94557 M Xenagos, God of Revels @ +94558 M Ajani Steadfast @ +94559 R Call the Gatewatch @ +94560 M Elspeth, Sun's Champion @ +94561 R Hushwing Gryff @ +94562 R Oath of Gideon @ +94563 U Sage's Reverie @ +94564 C Sungrace Pegasus @ +94565 C Clutch of Currents @ +94566 U Flitterstep Eidolon @ +94567 U Hidden Strings @Daarken @ +94568 M Jace, Architect of Thought @ +94569 M Jace, Memory Adept @ +94570 R Oath of Jace @ +94571 C Retraction Helix @ +94572 C Cavern Lampad @ +94573 M Liliana of the Dark Realms @ +94574 R Liliana Vess @ +94575 R Oath of Liliana @ +94576 U Ovalchase Daredevil @ +94577 R Ruinous Path @ +94578 R Stain the Mind @ +94579 U Crumble to Dust @ +94580 C Goblin Heelcutter @ +94581 U Reckless Bushwhacker @ +94582 C Smash to Smithereens @ +94583 U Thopter Engineer @ +94584 U Brood Monitor @ +94585 C Fog @ +94586 M Garruk, Caller of Beasts @ +94587 C Nissa's Pilgrimage @ +94588 U Skyreaping @ +94589 C Stampeding Elk Herd @ +94590 M Ajani, Mentor of Heroes @ +94591 M Ashiok, Nightmare Weaver @ +94592 M Domri Rade @ +94593 M Kiora, the Crashing Wave @ +94594 M Narset Transcendent @ +94595 M Sarkhan Unbroken @ +94596 M Xenagos, the Reveler @ +94597 M The Chain Veil @Volkan Baǵa +94598 R Hallowed Moonlight @ +94599 U Open the Armory @ +94600 U Reprisal @ +94601 C Shoulder to Shoulder @ +94602 R Tragic Arrogance @ +94603 C Dramatic Reversal @ +94604 M Enter the Infinite @Lindsey Look +94605 M Part the Waterveil @ +94606 C Tome Scour @ +94607 C Void Shatter @Yohann Schepacz +94608 U Bile Blight @ +94609 U Dark Betrayal @ +94610 R Dark Petition @Igor Kieryluk +94611 C Touch of Moonglove @ +94612 M Worst Fears @ +94613 C Expedite @ +94614 U Limits of Solidarity @ +94615 R Mizzium Mortars @ +94616 R Radiant Flames @ +94617 C Seismic Stomp @ +94618 U Gather the Pack @ +94619 M The Great Aurora @ +94620 R Hunter's Prowess @ +94621 U Miming Slime @ +94622 C Natural State @ +94623 M Aurelia's Fury @ +94624 R Jarad's Orders @ +94625 M Rakdos's Return @ +94626 R Render Silent @ +94627 R Unexpected Results @ +94671 U Alive // Well @ +94674 U Armed // Dangerous @ +94677 U Down // Dirty @ +94680 U Far // Away @ +94683 U Give // Take @ +94686 U Profit // Loss @ +94689 U Protect // Serve @ +94692 U Toil // Trouble @ +94695 U Turn // Burn @ +94698 U Wear // Tear @ From 14ef18ae966e206609a882d826c77090ded01491 Mon Sep 17 00:00:00 2001 From: Paul Hammerton <18243520+paulsnoops@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:34:25 +0000 Subject: [PATCH 152/152] Add PIO to Explorer, Historic & Timeless formats --- forge-gui/res/formats/Archived/Explorer/2024-12-10.txt | 8 ++++++++ forge-gui/res/formats/Archived/Historic/2024-12-10.txt | 8 ++++++++ forge-gui/res/formats/Archived/Timeless/2024-12-10.txt | 8 ++++++++ forge-gui/res/formats/Sanctioned/Historic.txt | 2 +- 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 forge-gui/res/formats/Archived/Explorer/2024-12-10.txt create mode 100644 forge-gui/res/formats/Archived/Historic/2024-12-10.txt create mode 100644 forge-gui/res/formats/Archived/Timeless/2024-12-10.txt diff --git a/forge-gui/res/formats/Archived/Explorer/2024-12-10.txt b/forge-gui/res/formats/Archived/Explorer/2024-12-10.txt new file mode 100644 index 00000000000..0e3a42bc91a --- /dev/null +++ b/forge-gui/res/formats/Archived/Explorer/2024-12-10.txt @@ -0,0 +1,8 @@ +[format] +Name:Explorer (PIO) +Type:Archived +Subtype:Pioneer +Effective:2024-12-10 +Sets:KTK, XLN, RIX, DOM, M19, GRN, G18, RNA, WAR, M20, ELD, THB, IKO, M21, ZNR, KHM, STX, AFR, MID, VOW, NEO, SNC, EA1, DMU, BRO, EA2, ONE, MOM, MAT, EA3, WOE, LCI, MKM, OTJ, BIG, BLB, DSK, FDN, PIO +Banned:Amalia Benavides Aguirre; Bloodstained Mire; Expressive Iteration; Field of the Dead; Flooded Strand; Geological Appraiser; Karn, the Great Creator; Kethis, the Hidden Hand; Leyline of Abundance; Lurrus of the Dream-Den; Nexus of Fate; Oko, Thief of Crowns; Once Upon a Time; Polluted Delta; Sorin, Imperious Bloodlord; Teferi, Time Raveler; Tibalt's Trickery; Underworld Breach; Uro, Titan of Nature's Wrath; Veil of Summer; Wilderness Reclamation; Windswept Heath; Winota, Joiner of Forces; Wooded Foothills +Additional:Abandoned Sarcophagus; Abundant Maw; Accursed Horde; Accursed Witch; Adaptive Snapjaw; Adorned Pouncer; Advanced Stitchwing; Aerial Guide; Aerial Responder; Aeronaut Admiral; Aether Chaser; Aether Hub; Aether Inspector; Aether Meltdown; Aether Poisoner; Aether Swooper; Aether Theorist; Aether Tradewinds; Aetherborn Marauder; Aetherflux Reservoir; Aethersphere Harvester; Aetherstorm Roc; Aetherstream Leopard; Aethertorch Renegade; Aetherworks Marvel; Ahn-Crop Champion; Ahn-Crop Crasher; Aim High; Airdrop Aeronauts; Ajani Unyielding; Alchemist's Greeting; Alley Evasion; Alley Strangler; Alms of the Vein; Altar's Reap; Altered Ego; Always Watching; Ammit Eternal; Anafenza, Kin-Tree Spirit; Ancestral Statue; Ancient Crab; Angel of Invention; Angel of Sanctions; Angel of the God-Pharaoh; Angelic Edict; Angelic Purge; Anger of the Gods; Anguished Unmaking; Animation Module; Anointed Procession; Anointer Priest; Apothecary Geist; Appeal // Authority; Appetite for the Unnatural; Approach of the Second Sun; Arborback Stomper; Archangel Avacyn; Archfiend of Ifnir; Arlinn Kord; Armorcraft Judge; As Foretold; Assassin's Strike; Assault Formation; Assembled Alphas; Astral Cornucopia; Asylum Visitor; Atarka's Command; Attune with Aether; Audacious Infiltrator; Auger Spree; Aurelia, the Warleader; Authority of the Consuls; Avacyn's Judgment; Aven Initiate; Aven Mindcensor; Aven of Enduring Hope; Aven Wind Guide; Aviary Mechanic; Baleful Ammit; Ballista Charger; Baral's Expertise; Baral, Chief of Compliance; Barrage of Expendables; Barricade Breaker; Bastion Mastodon; Bathe in Dragonfire; Battering Krasis; Battlefield Scavenger; Bedlam Reveler; Belligerent Sliver; Beneath the Sands; Binding Mummy; Biting Rain; Bitterbow Sharpshooters; Black Cat; Blessed Alliance; Blessed Spirits; Blighted Bat; Blind Obedience; Blood Host; Blood Mist; Bloodbond Vampire; Bloodbriar; Bloodhall Priest; Bloodhunter Bat; Bloodlust Inciter; Bloodmad Vampire; Bloodrage Brawler; Blossoming Defense; Blur of Blades; Blur Sliver; Bogbrew Witch; Bomat Bazaar Barge; Bomat Courier; Bonded Construct; Bone Picker; Bone Saw; Bonescythe Sliver; Bontu the Glorified; Bontu's Last Reckoning; Bontu's Monument; Borderland Marauder; Borderland Minotaur; Boros Elite; Borrowed Grace; Borrowed Hostility; Borrowed Malevolence; Bound by Moonsilver; Brain in a Jar; Brain Maggot; Breaching Hippocamp; Bred for the Hunt; Briarbridge Patrol; Brisela, Voice of Nightmares; Bristling Hydra; Bruna, the Fading Light; Brushstrider; Brute Strength; Bubbling Cauldron; Built to Last; Built to Smash; Burn from Within; Burning-Fist Minotaur; Burning-Tree Emissary; Burnished Hart; By Force; Bygone Bishop; Byway Courier; Call the Bloodline; Canyon Slough; Carrier Thrall; Cartouche of Ambition; Cartouche of Knowledge; Cartouche of Solidarity; Cartouche of Strength; Cartouche of Zeal; Cascading Cataracts; Cast Out; Cataclysmic Gearhulk; Cathar's Companion; Cemetery Recruitment; Censor; Ceremonious Rejection; Certain Death; Champion of Rhonas; Champion of Wits; Chandra's Defeat; Chandra's Revolution; Chandra, Pyromaster; Chandra, Torch of Defiance; Chaos Maw; Charging Badger; Chief of the Foundry; Chittering Host; Choked Estuary; Cinder Elemental; Claim // Fame; Cloudblazer; Cogworker's Puzzleknot; Collateral Damage; Collected Company; Collective Brutality; Collective Defiance; Collective Effort; Combat Celebrant; Combustible Gearhulk; Commencement of Festivities; Commit // Memory; Compelling Argument; Compelling Deterrence; Compulsory Rest; Conduit of Storms; Confirm Suspicions; Confiscation Coup; Confront the Unknown; Consign // Oblivion; Consulate Skygate; Consulate Turret; Contraband Kingpin; Conviction; Coralhelm Guide; Corpse Hauler; Countervailing Winds; Countless Gears Renegade; Courageous Outrider; Crawling Sensation; Creeping Mold; Crested Sunmare; Crocanura; Crocodile of the Crossing; Crow of Dark Tidings; Cruel Reality; Crux of Fate; Crypt of the Eternals; Cryptbreaker; Cryptic Serpent; Cryptolith Fragment; Cryptolith Rite; Cultivator's Caravan; Curator of Mysteries; Curious Homunculus; Cut // Ribbons; Dance with Devils; Daredevil Dragster; Daring Demolition; Daring Sleuth; Dark Intimations; Dark Salvation; Dauntless Aven; Dauntless Cathar; Dauntless Onslaught; Dawn Gryff; Dawnfeather Eagle; Death Wind; Death's Approach; Deathcap Cultivator; Decimator of the Provinces; Declaration in Stone; Decoction Module; Deem Worthy; Defiant Greatmaw; Defiant Salvager; Demolition Stomper; Demon of Dark Schemes; Demonic Pact; Deny Existence; Depala, Pilot Exemplar; Deranged Whelp; Descend upon the Sinful; Desert Cerodon; Desert of the Fervent; Desert of the Glorified; Desert of the Indomitable; Desert of the Mindful; Desert of the True; Desert's Hold; Destined // Lead; Devils' Playground; Devilthorn Fox; Devouring Light; Die Young; Diffusion Sliver; Dinrova Horror; Diregraf Colossus; Disallow; Disposal Mummy; Dispossess; Dissenter's Deliverance; Distended Mindbender; Djeru's Renunciation; Djeru's Resolve; Docent of Perfection; Doom Blade; Doomfall; Douse in Gloom; Dovin Baan; Drag Under; Dragon Fodder; Dragon Hatchling; Dragon Mantle; Dragonloft Idol; Dragonlord's Servant; Dragonmaster Outcast; Drainpipe Vermin; Drake Haven; Drana, Liberator of Malakir; Dread Wanderer; Driven // Despair; Drogskol Shieldmate; Dromoka's Command; Drownyard Behemoth; Drownyard Explorers; Drunau Corpse Trawler; Dukhara Peafowl; Dune Beetle; Dusk // Dawn; Dusk Feaster; Duskwatch Recruiter; Dutiful Attendant; Dwynen's Elite; Dynavolt Tower; Eager Construct; Earthshaker Khenra; Eddytrail Hawk; Edifice of Authority; Elder Deep-Fiend; Eldritch Evolution; Electrostatic Pummeler; Elemental Uprising; Elusive Krasis; Elvish Visionary; Ember-Eye Wolf; Embraal Bruiser; Empyreal Voyager; Emrakul, the Promised End; Engineered Might; Enlarge; Enraged Giant; Epiphany at the Drownyard; Era of Innovation; Erdwal Illuminator; Essence Extraction; Essence Flux; Eternal of Harsh Truths; Eternal Scourge; Eternal Thirst; Ever After; Evolutionary Leap; Exemplar of Strength; Exultant Cultist; Eyeblight Assassin; Fabrication Module; Failure // Comply; Fairgrounds Warden; Faith of the Devoted; Faith Unbroken; Faithbearer Paladin; Falkenrath Gorger; Fan Bearer; Fanatic of Mogis; Farm // Market; Fatal Push; Fateful Showdown; Fen Hauler; Feral Prowler; Fervent Paincaster; Festering Mummy; Festering Newt; Fetid Pools; Fevered Visions; Fiend Binder; Fiery Temper; Filigree Familiar; Final Reward; Firebrand Archer; Fireforger's Puzzleknot; Flame Lash; Flameblade Adept; Flameblade Angel; Flames of the Firebrand; Fleeting Memories; Fleshbag Marauder; Floodwaters; Flurry of Horns; Fog; Fogwalker; Foreboding Ruins; Forge Devil; Forgotten Creation; Forsake the Worldly; Fortified Village; Fortify; Fortuitous Find; Foundry Hornet; Foundry Inspector; Foundry Screecher; Foundry Street Denizen; Fourth Bridge Prowler; Fragmentize; Fraying Sanity; Freejam Regent; Fretwork Colony; Frontline Rebel; Fumigate; Furious Reprisal; Furnace Whelp; Furyblade Vampire; Galvanic Bombardment; Game Trail; Gate to the Afterlife; Gatstaf Arsonists; Gavony Unhallowed; Gearseeker Serpent; Gearshift Ace; Geier Reach Bandit; Geier Reach Sanitarium; Geist of the Archives; Geralf's Masterpiece; Ghoulcaller's Accomplice; Gideon of the Trials; Gideon's Intervention; Gifted Aetherborn; Gilded Cerodon; Gisa and Geralf; Gisa's Bidding; Gisela, the Broken Blade; Glimmer of Genius; Glint; Glint-Nest Crane; Glint-Sleeve Artisan; Glint-Sleeve Siphoner; Glorious End; Glory-Bound Initiate; Glorybringer; Gnarlwood Dryad; Goblin Dark-Dwellers; Goblin Rally; Goblin Shortcutter; God-Pharaoh's Gift; Goldnight Castigator; Gonti, Lord of Luxury; Graf Harvest; Graf Mole; Graf Rats; Grapple with the Past; Greenbelt Rampager; Grim Flayer; Grind // Dust; Grisly Salvage; Grotesque Mutation; Groundskeeper; Gryff's Boon; Guardian of Pilgrims; Gust Walker; Hamlet Captain; Hanweir Battlements; Hanweir Garrison; Hanweir Militia Captain; Hanweir, the Writhing Township; Hapatra, Vizier of Poisons; Harmless Offering; Harnessed Lightning; Harsh Mentor; Harvest Hand; Hashep Oasis; Haunted Dead; Hazardous Conditions; Haze of Pollen; Hazoret the Fervent; Hazoret's Monument; Heart of Kiran; Heaven // Earth; Hedron Archive; Heir of Falkenrath; Hekma Sentinels; Herald of Anguish; Herald of the Fair; Heron's Grace Champion; Hidden Stockpile; Hieroglyphic Illumination; Highspire Artisan; Highspire Infusion; Hinterland Drake; Hinterland Logger; Hive Stirrings; Hollow One; Homing Lightning; Honored Crop-Captain; Hooded Brawler; Hope Against Hope; Hope of Ghirapur; Hope Tender; Hornet Queen; Horror of the Broken Lands; Hour of Devastation; Hour of Promise; Hour of Revelation; Howlpack Resurgence; Howlpack Wolf; Humble the Brute; Hungry Flames; Ice Over; Ifnir Deadlands; Illusionist's Stratagem; Imminent Doom; Impact Tremors; Impeccable Timing; Implement of Combustion; Implement of Examination; Implement of Malice; Imprisoned in the Moon; In Oketra's Name; Incendiary Flow; Incorrigible Youths; Indomitable Creativity; Indulgent Aristocrat; Ingenious Skaab; Initiate's Companion; Insatiable Gorgers; Insolent Neonate; Inspiring Call; Inspiring Statuary; Insult // Injury; Intrepid Provisioner; Invasive Surgery; Inventor's Apprentice; Inventor's Goggles; Inventors' Fair; Invigorated Rampage; Ipnu Rivulet; Ironclad Slayer; Irontread Crusher; Irrigated Farmland; Ishkanah, Grafwidow; Jace's Scrutiny; Jace, Unraveler of Secrets; Just the Wind; Kalastria Nightwatch; Kambal, Consul of Allocation; Kari Zev's Expertise; Kari Zev, Skyship Raider; Kefnet the Mindful; Kefnet's Monument; Key to the City; Khenra Charioteer; Khenra Eternal; Khenra Scrapper; Kindly Stranger; Kolaghan's Command; Kujar Seedsculptor; Laboratory Brute; Labyrinth Guardian; Languish; Lathnu Sailback; Launch Party; Lawless Broker; Lay Claim; Leaf Gilder; Leave // Chance; Leave in the Dust; Leeching Sliver; Lethal Sting; Lifecraft Cavalry; Lifecrafter's Bestiary; Lifecrafter's Gift; Lightning Axe; Lightning Diadem; Lightning Shrieker; Lightwalker; Liliana's Defeat; Liliana's Elite; Liliana's Mastery; Liliana's Reaver; Liliana, Death's Majesty; Liliana, the Last Hope; Live Fast; Lone Rider; Long Road Home; Longtusk Cub; Lord of the Accursed; Lost Legacy; Lunarch Mantle; Lupine Prototype; Mad Prophet; Magma Jet; Magma Spray; Magmaroth; Magmatic Chasm; Majestic Myriarch; Make Mischief; Make Obsolete; Malakir Cullblade; Malakir Familiar; Malfunction; Manaweft Sliver; Manglehorn; Manic Scribe; Marauding Boneslasher; Marionette Master; Markov Crusader; Master Trinketeer; Maulfist Revolutionary; Maulfist Squad; Maverick Thopterist; Maze's End; Merchant's Dockhand; Merciless Javelineer; Merciless Resolve; Mercurial Geists; Metallic Mimic; Metallic Rebuke; Metallurgic Summonings; Metalspinner's Puzzleknot; Metalwork Colossus; Miasmic Mummy; Midnight Oil; Midnight Scavengers; Mind's Dilation; Mindwrack Demon; Minister of Inquiries; Minotaur Skullcleaver; Minotaur Sureshot; Mirage Mirror; Mirrorwing Dragon; Mobile Garrison; Mockery of Nature; Monstrous Onslaught; Moonlight Hunt; Morkrut Necropod; Mournwillow; Mouth // Feed; Mugging; Murderer's Axe; Murmuring Phantasm; Naga Oracle; Naga Vitalist; Nahiri's Wrath; Nahiri, the Harbinger; Narnam Cobra; Narnam Renegade; Nature's Way; Nearheath Chaplain; Nebelgast Herald; Nef-Crop Entangler; Neglected Heirloom; Neheb, the Eternal; Neheb, the Worthy; Nest of Scarabs; Never // Return; New Perspectives; Nicol Bolas, God-Pharaoh; Night Market Aeronaut; Night Market Lookout; Nightmare; Nimble Innovator; Nimble Obstructionist; Nimble-Blade Khenra; Nimbus Swimmer; Nissa, Steward of Elements; Nissa, Vital Force; Noose Constrictor; Noosegraf Mob; Notion Thief; Noxious Gearhulk; Nyx-Fleece Ram; Nyx Weaver; Oashra Cultivator; Oasis Ritualist; Oath of Ajani; Obelisk Spider; Obsessive Skinner; Odric, Lunarch Marshal; Ogre Battledriver; Ogre Slumlord; Ojutai's Command; Ojutai's Summons; Oketra the True; Oketra's Attendant; Oketra's Avenger; Oketra's Monument; Olivia's Bloodsworn; Olivia's Dragoon; Olivia, Mobilized for War; Ominous Sphinx; Ongoing Investigation; Onward // Victory; Open Fire; Ornamental Courage; Ornery Kudu; Ornithopter; Outland Boar; Outnumber; Ovalchase Dragster; Overwhelming Splendor; Oviya Pashiri, Sage Lifecrafter; Pacification Array; Pack Guardian; Pack Rat; Padeem, Consul of Innovation; Panharmonicon; Paradox Engine; Paradoxical Outcome; Path of Bravery; Pathmaker Initiate; Patron of the Valiant; Peacewalker Colossus; Peel from Reality; Peema Aether-Seer; Peema Outrider; Perilous Vault; Permeating Mass; Phyrexian Revoker; Pia Nalaar; Pick the Brain; Pieces of the Puzzle; Pilgrim's Eye; Pitiless Vizier; Planar Bridge; Pore Over the Pages; Port Town; Possibility Storm; Pouncing Cheetah; Prakhata Pillar-Bug; Precise Strike; Predatory Sliver; Prepare // Fight; Prescient Chimera; Pride Sovereign; Primeval Bounty; Prized Amalgam; Propeller Pioneer; Protection of the Hekma; Prowling Serpopard; Pull from Tomorrow; Puncturing Blow; Puncturing Light; Pursue Glory; Putrefy; Pyre Hound; Quarry Hauler; Quicksmith Genius; Quicksmith Rebel; Rageblood Shaman; Rags // Riches; Raise Dead; Ramunap Excavator; Ramunap Ruins; Rashmi, Eternities Crafter; Ratchet Bomb; Rattlechains; Ravenous Bloodseeker; Ravenous Intruder; Razaketh, the Foulblooded; Reaper of Flight Moonsilver; Reason // Believe; Reckless Fireweaver; Reckless Racer; Reckless Scholar; Reduce // Rubble; Refurbish; Refuse // Cooperate; Regal Caracal; Relentless Dead; Renegade Map; Renegade Rallier; Renegade Tactics; Renegade Wheelsmith; Renewed Faith; Reservoir Walker; Resilient Khenra; Restoration Gearsmith; Restoration Specialist; Return to the Ranks; Reverse Engineer; Revoke Privileges; Revolutionary Rebuff; Rhonas the Indomitable; Rhonas's Monument; Rhonas's Stalwart; Riddle of Lightning; Ridgescale Tusker; Riparian Tiger; Rise from the Tides; Rise of the Dark Realms; Rishkar's Expertise; Rishkar, Peema Renegade; River Hoopoe; Rogue Refiner; Ruin Rat; Ruinous Gremlin; Rumbling Baloth; Runeclaw Bear; Runed Servitor; Rush of Adrenaline; Rush of Vitality; Ruthless Disposal; Ruthless Sniper; Sacred Cat; Sage of Ancient Lore; Sage of Shaila's Claim; Saheeli Rai; Salivating Gremlins; Samut, the Tested; Samut, Voice of Dissent; Sand Strangler; Sandsteppe Outcast; Sandwurm Convergence; Sanguine Bond; Sarkhan's Rage; Scarab Feast; Scattered Groves; Scavenger Grounds; Scour the Laboratory; Scourge Wolf; Scrap Trawler; Scrapheap Scrounger; Scrapper Champion; Seasons Past; Second Harvest; Seeker of Insight; Seer of the Last Tomorrow; Seismic Elemental; Seismic Rupture; Select for Inspection; Self-Assembler; Selfless Cathar; Selfless Spirit; Sengir Vampire; Sentinel Sliver; Servant of the Conduit; Servant of the Scale; Servo Exhibition; Servo Schematic; Shadow of the Grave; Shadowstorm Vizier; Shambleshark; Shambling Goblin; Shard of Broken Glass; Shed Weakness; Shefet Dunes; Shefet Monitor; Sheltered Thicket; Shielded Aether Thief; Shimmerscale Drake; Shipwreck Moray; Shiv's Embrace; Shreds of Sanity; Shrewd Negotiation; Shrill Howler; Sidewinder Naga; Siege Dragon; Siege Modification; Sifter Wurm; Sigarda's Aid; Sigarda, Heron's Grace; Sigardian Priest; Sigil of the Empty Throne; Sigil of Valor; Sigiled Starfish; Sign in Blood; Silumgar's Command; Sin Prodder; Sixth Sense; Sky Skiff; Skyship Plunderer; Skyship Stalker; Skysovereign, Consul Flagship; Skywhaler's Shot; Slate Street Ruffian; Slayer's Plate; Slither Blade; Sliver Hive; Sly Requisitioner; Smuggler's Copter; Solemnity; Solitary Camel; Somberwald Stag; Sorin, Grim Nemesis; Soul of the Harvest; Soul Separator; Soul-Scar Mage; Soulblade Djinn; Soulstinger; Spectral Shepherd; Speedway Fanatic; Spell Queller; Spellweaver Eternal; Sphinx's Revelation; Spire of Industry; Spire Patrol; Spireside Infiltrator; Spirit of the Hunt; Splendid Agony; Spontaneous Mutation; Sporemound; Spring // Mind; Springleaf Drum; Sram's Expertise; Sram, Senior Edificer; Stab Wound; Start // Finish; Startled Awake; Steadfast Cathar; Steelform Sliver; Stensia Masquerade; Steward of Solidarity; Stinging Shot; Stitcher's Graft; Stitchwing Skaab; Stormfront Pegasus; Strength of Arms; Striking Sliver; Striped Riverwinder; Stromkirk Condemned; Stromkirk Occultist; Struggle // Survive; Subjugator Angel; Summary Dismissal; Sunscorched Desert; Sunscourge Champion; Sunset Pyramid; Supernatural Stamina; Supply Caravan; Supreme Will; Swan Song; Sweatworks Brawler; Sweep Away; Sweltering Suns; Swift Spinner; Synchronized Strike; Tah-Crop Elite; Tajuru Pathwarden; Take Inventory; Tamiyo's Journal; Tamiyo, Field Researcher; Tandem Tactics; Tattered Haunter; Temmet, Vizier of Naktamun; Terrarion; Tezzeret the Schemer; Tezzeret's Ambition; Tezzeret's Touch; Thalia's Lancers; Thalia's Lieutenant; Thalia, Heretic Cathar; The Gitrog Monster; The Locust God; The Scarab God; The Scorpion God; Thing in the Ice; Thopter Arrest; Thorned Moloch; Those Who Serve; Thoughtseize; Thraben Foulbloods; Thraben Inspector; Thraben Standard Bearer; Thresher Lizard; Thriving Rhino; Thriving Turtle; Throne of the God-Pharaoh; Thunderbreak Regent; Tightening Coils; Toolcraft Exemplar; Topplegeist; Torch Fiend; Torment of Hailfire; Torment of Scarabs; Torrential Gearhulk; Town Gossipmonger; Traverse the Ulvenwald; Treasure Keeper; Tree of Perdition; Trespasser's Curse; Trial of Ambition; Trial of Knowledge; Trial of Solidarity; Trial of Strength; Trial of Zeal; Triskaidekaphobia; Trophy Mage; True-Faith Censer; Typhoid Rats; Ulamog, the Ceaseless Hunger; Ulrich of the Krallenhorde; Ulrich's Kindred; Ulvenwald Captive; Ulvenwald Hydra; Ulvenwald Mysteries; Unbridled Growth; Unburden; Unconventional Tactics; Underhanded Designs; Unesh, Criosphinx Sovereign; Universal Solvent; Unlicensed Disintegration; Unquenchable Thirst; Untethered Express; Vampiric Rites; Vengeful Rebel; Verdurous Gearhulk; Vessel of Nascency; Veteran Cathar; Veteran Motorist; Vile Manifestation; Village Messenger; Virulent Plague; Visionary Augmenter; Vizier of Deferment; Vizier of Many Faces; Vizier of Remedies; Vizier of the Anointed; Vizier of the Menagerie; Vizier of Tumbling Sands; Voldaren Pariah; Voltaic Brawler; Voyage's End; Wailing Ghoul; Wall of Forgotten Pharaohs; Wander in Death; Warfire Javelineer; Wasp of the Bitter End; Waste Not; Wasteland Scorpion; Watchers of the Dead; Watchful Naga; Wayward Servant; Weaponcraft Enthusiast; Weaver of Lightning; Weirded Vampire; Weirding Wood; Weldfast Engineer; Weldfast Monitor; Weldfast Wingsmith; Welding Sparks; Westvale Abbey; Wharf Infiltrator; Whelming Wave; Whir of Invention; Whirler Rogue; Whirler Virtuoso; Whirlermaker; Wight of Precinct Six; Wild Wanderer; Wild-Field Scarecrow; Wildest Dreams; Wind-Kin Raiders; Winding Constrictor; Winds of Rebuke; Winged Shepherd; Wispweaver Angel; Witch's Familiar; Woodborn Behemoth; Woodweaver's Puzzleknot; Workshop Assistant; Wretched Gryff; Yahenni's Expertise; Yahenni, Undying Partisan; Young Pyromancer; Zada, Hedron Grinder; Zealot of the God-Pharaoh; Zendikar's Roil; Abrupt Decay; Back for More; Bedevil; Buried in the Garden; Clear Shot; Crackle with Power; Decisive Denial; Detention Sphere; Electrodominance; Endless Detour; Essence Capture; Fierce Retribution; Fling; Heartless Pillage; Humiliate; Hypothesizzle; Ionize; Leyline Binding; Murder; Outlaws' Merriment; Primal Might; Ride Down; Savage Smash; Siphon Insight; Skewer the Critics; Skullcrack; Tyrant's Scorn; Unlicensed Hearse; Vanishing Verse; Villainous Wealth; Void Rend diff --git a/forge-gui/res/formats/Archived/Historic/2024-12-10.txt b/forge-gui/res/formats/Archived/Historic/2024-12-10.txt new file mode 100644 index 00000000000..3a3e0c962d7 --- /dev/null +++ b/forge-gui/res/formats/Archived/Historic/2024-12-10.txt @@ -0,0 +1,8 @@ +[format] +Name:Historic (PIO) +Type:Archived +Subtype:Arena +Effective:2024-12-10 +Sets:KTK, XLN, RIX, DOM, M19, ANA, PANA, GRN, G18, RNA, WAR, M20, ELD, HA1, THB, HA2, IKO, HA3, M21, JMP, AJMP, AKR, ANB, ZNR, KLR, KHM, HA4, STX, STA, HA5, AFR, J21, MID, VOW, YMID, NEO, YNEO, SNC, YSNC, HBG, HA6, EA1, DMU, YDMU, BRO, BRR, YBRO, EA2, ONE, YONE, SIR, SIS, MOM, MUL, MAT, LTR, HA7, EA3, WOE, WOT, YWOE, LCI, YLCI, MKM, YMKM, OTJ, OTP, BIG, YOTJ, MH3, BLB, YBLB, DSK, YDSK, FDN, J25, PIO +Banned:Agent of Treachery; Arid Mesa; Blood Moon; Bloodstained Mire; Brainstorm; Channel; Commandeer; Counterspell; Dark Ritual; Demonic Tutor; Endurance; Field of the Dead; Flare of Cultivation; Flare of Denial; Flare of Duplication; Flare of Fortitude; Flare of Malice; Flooded Strand; Force of Vigor; Fury; Grief; Harbinger of the Seas; Intruder Alarm; Land Tax; Lightning Bolt; Mana Drain; Marsh Flats; Memory Lapse; Mishra's Bauble; Misty Rainforest; Natural Order; Necropotence; Nexus of Fate; Oko, Thief of Crowns; Once Upon a Time; Polluted Delta; Ragavan, Nimble Pilferer; Reanimate; Scalding Tarn; Show and Tell; Sneak Attack; Solitude; Spreading Seas; Subtlety; Swords to Plowshares; Temporal Manipulation; Thassa's Oracle; Tibalt's Trickery; Time Warp; Uro, Titan of Nature's Wrath; Veil of Summer; Verdant Catacombs; Wilderness Reclamation; Windswept Heath; Winter Moon; Wooded Foothills +Additional:Admiral Brass, Unsinkable; Burden of Guilt; Clavileño, First of the Blessed; Crashing Footfalls; Desert; Desertion; Dismember; Duskmantle, House of Shadow; Enlisted Wurm; Evolutionary Leap; Fabricate; Gamble; Ghostly Prison; Gonti, Canny Acquisitor; Goro-Goro and Satoru; Ixidor, Reality Sculptor; Katilda and Lier; Kuldotha Rebirth; Leonin Relic-Warder; Magmaw; Mass Hysteria; Metalspinner's Puzzleknot; Mistveil Plains; Molten Psyche; Monologue Tax; Mystery Key; Mystic Snake; Notion Thief; Nyx Weaver; Olivia, Opulent Outlaw; Pantlaza, Sun-Favored; Persist; Port Razer; Possibility Storm; Prismatic Ending; Prismatic Vista; Putrid Warrior; Shard of Broken Glass; Slimefoot and Squee; Smuggler's Copter; Spell Snare; Stella Lee, Wild Card; Stoneforge Mystic; Timeless Dragon; Treacherous Terrain; Victimize; Xolatoyac, the Smiling Flood; Yuma, Proud Protector diff --git a/forge-gui/res/formats/Archived/Timeless/2024-12-10.txt b/forge-gui/res/formats/Archived/Timeless/2024-12-10.txt new file mode 100644 index 00000000000..55a4069ab8e --- /dev/null +++ b/forge-gui/res/formats/Archived/Timeless/2024-12-10.txt @@ -0,0 +1,8 @@ +[format] +Name:Timeless (PIO) +Type:Archived +Subtype:Vintage +Effective:2024-12-10 +Sets:KTK, XLN, RIX, DOM, M19, ANA, PANA, GRN, G18, RNA, WAR, M20, ELD, HA1, THB, HA2, IKO, HA3, M21, JMP, AJMP, AKR, ANB, ZNR, KLR, KHM, HA4, STX, STA, HA5, AFR, J21, MID, VOW, YMID, NEO, YNEO, SNC, YSNC, HBG, HA6, EA1, DMU, YDMU, BRO, BRR, YBRO, EA2, ONE, YONE, SIR, SIS, MOM, MUL, MAT, LTR, HA7, EA3, WOE, WOT, YWOE, LCI, YLCI, MKM, YMKM, OTJ, OTP, BIG, YOTJ, MH3, BLB, YBLB, DSK, YDSK, FDN, J25, PIO +Restricted:Channel; Demonic Tutor; Tibalt's Trickery +Additional:Admiral Brass, Unsinkable; Burden of Guilt; Clavileño, First of the Blessed; Crashing Footfalls; Desert; Desertion; Dismember; Duskmantle, House of Shadow; Endurance; Enlisted Wurm; Evolutionary Leap; Fabricate; Fury; Gamble; Ghostly Prison; Gonti, Canny Acquisitor; Goro-Goro and Satoru; Grief; Ixidor, Reality Sculptor; Katilda and Lier; Kuldotha Rebirth; Leonin Relic-Warder; Magmaw; Mass Hysteria; Metalspinner's Puzzleknot; Mistveil Plains; Molten Psyche; Monologue Tax; Mystery Key; Mystic Snake; Notion Thief; Nyx Weaver; Olivia, Opulent Outlaw; Pantlaza, Sun-Favored; Persist; Port Razer; Possibility Storm; Prismatic Ending; Prismatic Vista; Putrid Warrior; Shard of Broken Glass; Show and Tell; Slimefoot and Squee; Smuggler's Copter; Solitude; Spell Snare; Stella Lee, Wild Card; Stoneforge Mystic; Subtlety; Timeless Dragon; Treacherous Terrain; Victimize; Xolatoyac, the Smiling Flood; Yuma, Proud Protector diff --git a/forge-gui/res/formats/Sanctioned/Historic.txt b/forge-gui/res/formats/Sanctioned/Historic.txt index bdfd4c7a2a7..8b9544437af 100644 --- a/forge-gui/res/formats/Sanctioned/Historic.txt +++ b/forge-gui/res/formats/Sanctioned/Historic.txt @@ -4,6 +4,6 @@ Type:Digital Subtype:Arena Effective:2019-11-21 Order:142 -Sets:KTK, XLN, RIX, DOM, M19, ANA, PANA, GRN, G18, RNA, WAR, M20, ELD, HA1, THB, HA2, IKO, HA3, M21, JMP, AJMP, AKR, ANB, ZNR, KLR, KHM, HA4, STX, STA, HA5, AFR, J21, MID, VOW, YMID, NEO, YNEO, SNC, YSNC, HBG, HA6, EA1, DMU, YDMU, BRO, BRR, YBRO, EA2, ONE, YONE, SIR, SIS, MOM, MUL, MAT, LTR, HA7, EA3, WOE, WOT, YWOE, LCI, YLCI, MKM, YMKM, OTJ, OTP, BIG, YOTJ, MH3, BLB, YBLB, DSK, YDSK, FDN, J25 +Sets:KTK, XLN, RIX, DOM, M19, ANA, PANA, GRN, G18, RNA, WAR, M20, ELD, HA1, THB, HA2, IKO, HA3, M21, JMP, AJMP, AKR, ANB, ZNR, KLR, KHM, HA4, STX, STA, HA5, AFR, J21, MID, VOW, YMID, NEO, YNEO, SNC, YSNC, HBG, HA6, EA1, DMU, YDMU, BRO, BRR, YBRO, EA2, ONE, YONE, SIR, SIS, MOM, MUL, MAT, LTR, HA7, EA3, WOE, WOT, YWOE, LCI, YLCI, MKM, YMKM, OTJ, OTP, BIG, YOTJ, MH3, BLB, YBLB, DSK, YDSK, FDN, J25, PIO Banned:Agent of Treachery; Arid Mesa; Blood Moon; Bloodstained Mire; Brainstorm; Channel; Commandeer; Counterspell; Dark Ritual; Demonic Tutor; Endurance; Field of the Dead; Flare of Cultivation; Flare of Denial; Flare of Duplication; Flare of Fortitude; Flare of Malice; Flooded Strand; Force of Vigor; Fury; Grief; Harbinger of the Seas; Intruder Alarm; Land Tax; Lightning Bolt; Mana Drain; Marsh Flats; Memory Lapse; Mishra's Bauble; Misty Rainforest; Natural Order; Necropotence; Nexus of Fate; Oko, Thief of Crowns; Once Upon a Time; Polluted Delta; Ragavan, Nimble Pilferer; Reanimate; Scalding Tarn; Show and Tell; Sneak Attack; Solitude; Spreading Seas; Subtlety; Swords to Plowshares; Temporal Manipulation; Thassa's Oracle; Tibalt's Trickery; Time Warp; Uro, Titan of Nature's Wrath; Veil of Summer; Verdant Catacombs; Wilderness Reclamation; Windswept Heath; Winter Moon; Wooded Foothills Additional:Admiral Brass, Unsinkable; Burden of Guilt; Clavileño, First of the Blessed; Crashing Footfalls; Desert; Desertion; Dismember; Duskmantle, House of Shadow; Enlisted Wurm; Evolutionary Leap; Fabricate; Gamble; Ghostly Prison; Gonti, Canny Acquisitor; Goro-Goro and Satoru; Ixidor, Reality Sculptor; Katilda and Lier; Kuldotha Rebirth; Leonin Relic-Warder; Magmaw; Mass Hysteria; Metalspinner's Puzzleknot; Mistveil Plains; Molten Psyche; Monologue Tax; Mystery Key; Mystic Snake; Notion Thief; Nyx Weaver; Olivia, Opulent Outlaw; Pantlaza, Sun-Favored; Persist; Port Razer; Possibility Storm; Prismatic Ending; Prismatic Vista; Putrid Warrior; Shard of Broken Glass; Slimefoot and Squee; Smuggler's Copter; Spell Snare; Stella Lee, Wild Card; Stoneforge Mystic; Timeless Dragon; Treacherous Terrain; Victimize; Xolatoyac, the Smiling Flood; Yuma, Proud Protector