From 42e91b60eacb19a92571e9986e16d91c452d084b Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 16:39:59 +0100 Subject: [PATCH 01/31] Code to cover cases for Randomly generated Deck (from Archetypes) not having collectorNumbers and Artist Set --- forge-core/src/main/java/forge/item/PaperCard.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index 674db94fb7d..21bcae19280 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -51,7 +51,7 @@ public final class PaperCard implements Comparable, InventoryItemFro (see getCollectorNumber()) */ private String collectorNumber; - private final String artist; + private String artist; private final int artIndex; private final boolean foil; private Boolean hasImage; @@ -73,6 +73,8 @@ public final class PaperCard implements Comparable, InventoryItemFro @Override public String getCollectorNumber() { + if (collectorNumber == null) + collectorNumber = IPaperCard.NO_COLLECTOR_NUMBER; return collectorNumber; } @@ -103,6 +105,8 @@ public final class PaperCard implements Comparable, InventoryItemFro @Override public String getArtist() { + if (this.artist == null) + artist = IPaperCard.NO_ARTIST_NAME; return artist; } From 7613b27e6e06982d1c4698f72e4f806fea2ebe3d Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 16:47:10 +0100 Subject: [PATCH 02/31] Still scratching a little of performance impros by avoiding checking class instance. When StatTypeFilter is used withing Deck Editor (with all PaperCard instance in ItemPool), avoiding checking instance class improves performance a bit (saving a couple of hundreds of milliseconds). However, when this filter with also other inventory items (pack or deck), the same code as before is executed. --- .../src/main/java/forge/util/ItemPool.java | 13 +++++++---- .../itemmanager/filters/StatTypeFilter.java | 23 +++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/forge-core/src/main/java/forge/util/ItemPool.java b/forge-core/src/main/java/forge/util/ItemPool.java index 6cf22ec2162..7a065ca1639 100644 --- a/forge-core/src/main/java/forge/util/ItemPool.java +++ b/forge-core/src/main/java/forge/util/ItemPool.java @@ -137,13 +137,16 @@ public class ItemPool implements Iterable condition) { + public final int countAll(Predicate condition){ int count = 0; - for (Entry e : this) { - if (condition.apply(e.getKey())) { - count += e.getValue(); + Iterable matchingKeys = Iterables.filter(this.items.keySet(), new Predicate() { + @Override + public boolean apply(T item) { + return condition.apply((U)item); } - } + }); + for (T key : matchingKeys) + count += this.items.get(key); return count; } diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/filters/StatTypeFilter.java b/forge-gui-desktop/src/main/java/forge/itemmanager/filters/StatTypeFilter.java index cfc2041798a..80333946b52 100644 --- a/forge-gui-desktop/src/main/java/forge/itemmanager/filters/StatTypeFilter.java +++ b/forge-gui-desktop/src/main/java/forge/itemmanager/filters/StatTypeFilter.java @@ -75,14 +75,23 @@ public abstract class StatTypeFilter extends ToggleButt if (btnPackOrDeck != null) { //support special pack/deck case int count = items.countAll(ItemPredicate.Presets.IS_PACK_OR_DECK, InventoryItem.class); btnPackOrDeck.setText(String.valueOf(count)); - } - Iterator buttonMapStatsIterator = buttonMap.keySet().iterator(); - while (buttonMapStatsIterator.hasNext()){ - StatTypes statTypes = buttonMapStatsIterator.next(); - if (statTypes.predicate != null){ - int count = items.countAll(Predicates.compose(statTypes.predicate, PaperCard.FN_GET_RULES), PaperCard.class); - buttonMap.get(statTypes).setText(String.valueOf(count)); + Iterator buttonMapStatsIterator = buttonMap.keySet().iterator(); + while (buttonMapStatsIterator.hasNext()){ + StatTypes statTypes = buttonMapStatsIterator.next(); + if (statTypes.predicate != null){ + count = items.countAll(Predicates.compose(statTypes.predicate, PaperCard.FN_GET_RULES), PaperCard.class); + buttonMap.get(statTypes).setText(String.valueOf(count)); + } + } + } else { + Iterator buttonMapStatsIterator = buttonMap.keySet().iterator(); + while (buttonMapStatsIterator.hasNext()) { + StatTypes statTypes = buttonMapStatsIterator.next(); + if (statTypes.predicate != null) { + int count = items.countAll(Predicates.compose(statTypes.predicate, PaperCard.FN_GET_RULES)); + buttonMap.get(statTypes).setText(String.valueOf(count)); + } } } getWidget().revalidate(); From b1c50599e9b22a834a864e0d05662cad6f8a5bea Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 16:59:26 +0100 Subject: [PATCH 03/31] Improvements to getFilteredPool method. Previous implementation did not consider the amount per card included in the pool already - so the filtered pool always got 1 amount for each card passing the filter. This should be no problem considering where this method is used. However, to make it portable to other cases, I made the implementation to report the same amount of cards in the new filtered pool. --- forge-core/src/main/java/forge/deck/CardPool.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/forge-core/src/main/java/forge/deck/CardPool.java b/forge-core/src/main/java/forge/deck/CardPool.java index fd3a517c6ec..e152a3ba47c 100644 --- a/forge-core/src/main/java/forge/deck/CardPool.java +++ b/forge-core/src/main/java/forge/deck/CardPool.java @@ -463,8 +463,11 @@ public class CardPool extends ItemPool { */ public CardPool getFilteredPool(Predicate predicate) { CardPool filteredPool = new CardPool(); - for (PaperCard pc : this.items.keySet()) { - if (predicate.apply(pc)) filteredPool.add(pc); + Iterator cardsInPool = (Iterator) this.items.keySet(); + while (cardsInPool.hasNext()){ + PaperCard c = cardsInPool.next(); + if (predicate.apply(c)) + filteredPool.add(c, this.items.get(c)); } return filteredPool; } From a7d1e7398a4296ff2e6594d047bbb15e6f29b181 Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 17:54:53 +0100 Subject: [PATCH 04/31] FIX cardDb bug for lazy card loading when issuing a request with a non-existing set code. Code FIX + Test --- .../src/main/java/forge/card/CardDb.java | 2 ++ .../forge/card/CardDbTestLazyCardLoading.java | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index b0a390a896e..0e0a05e731d 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -249,6 +249,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { } } else { CardEdition e = editions.get(setCode); + if (e == null) + return; // wrong set code - nothing we can do here! List cardsInSet = e.getCardInSet(cardName); // empty collection if not present for (CardInSet cis : cardsInSet) { addSetCard(e, cis, cr); diff --git a/forge-gui-desktop/src/test/java/forge/card/CardDbTestLazyCardLoading.java b/forge-gui-desktop/src/test/java/forge/card/CardDbTestLazyCardLoading.java index fc169986934..5f6710dffba 100644 --- a/forge-gui-desktop/src/test/java/forge/card/CardDbTestLazyCardLoading.java +++ b/forge-gui-desktop/src/test/java/forge/card/CardDbTestLazyCardLoading.java @@ -89,4 +89,29 @@ public class CardDbTestLazyCardLoading extends ForgeCardMockTestCase { assertNotNull(aetherVialCard); assertEquals(aetherVialCard.getName(), expectedCardName); } + + @Test + public void tesLoadAndGetUnsupportedCardHavingWrongSetCode(){ + String cardName = "Dominating Licid"; + String wrongSetCode = "AA"; + String expectedSetCode = CardEdition.UNKNOWN.getCode(); + CardRarity expectedCardRarity = CardRarity.Unknown; + + PaperCard dominatingLycidCard = this.cardDb.getCard(cardName); + assertNull(dominatingLycidCard); + + // Load the Card (just card name + FModel.getMagicDb().attemptToLoadCard(cardName, wrongSetCode); + + dominatingLycidCard = this.cardDb.getCard(cardName); + assertNull(dominatingLycidCard); // card still not found + + // Resorting to Unsupported Card Request + String cardRequest = CardDb.CardRequest.compose(cardName, wrongSetCode); + dominatingLycidCard = this.cardDb.createUnsupportedCard(cardRequest); + assertNotNull(dominatingLycidCard); + assertEquals(dominatingLycidCard.getName(), cardName); + assertEquals(dominatingLycidCard.getEdition(), expectedSetCode); + assertEquals(dominatingLycidCard.getRarity(), expectedCardRarity); + } } From 8768900cf888246f348bd311cfd76dc13bbc12a4 Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 18:29:07 +0100 Subject: [PATCH 05/31] FIX cardDb bug for lazy card loading when issuing a request with a non-existing set code. Code FIX + Test Now the implementation in CardDb will automatically try to get the card with that name from an existing edition --- forge-core/src/main/java/forge/card/CardDb.java | 10 ++++------ .../java/forge/card/CardDbTestLazyCardLoading.java | 9 ++------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index 0e0a05e731d..998593fa468 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -238,7 +238,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { System.out.println("[LOG]: (Lazy) Loading Card: " + cardName); rulesByName.put(cardName, cr); boolean reIndexNecessary = false; - if ((setCode == null) || setCode.length() == 0 || setCode.equals(CardEdition.UNKNOWN.getCode())) { + CardEdition ed = editions.get(setCode); + if (ed == null || ed.equals(CardEdition.UNKNOWN)) { // look for all possible editions for (CardEdition e : editions) { List cardsInSet = e.getCardInSet(cardName); // empty collection if not present @@ -248,12 +249,9 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { } } } else { - CardEdition e = editions.get(setCode); - if (e == null) - return; // wrong set code - nothing we can do here! - List cardsInSet = e.getCardInSet(cardName); // empty collection if not present + List cardsInSet = ed.getCardInSet(cardName); // empty collection if not present for (CardInSet cis : cardsInSet) { - addSetCard(e, cis, cr); + addSetCard(ed, cis, cr); reIndexNecessary = true; } } diff --git a/forge-gui-desktop/src/test/java/forge/card/CardDbTestLazyCardLoading.java b/forge-gui-desktop/src/test/java/forge/card/CardDbTestLazyCardLoading.java index 5f6710dffba..2ca5b7b828a 100644 --- a/forge-gui-desktop/src/test/java/forge/card/CardDbTestLazyCardLoading.java +++ b/forge-gui-desktop/src/test/java/forge/card/CardDbTestLazyCardLoading.java @@ -94,8 +94,8 @@ public class CardDbTestLazyCardLoading extends ForgeCardMockTestCase { public void tesLoadAndGetUnsupportedCardHavingWrongSetCode(){ String cardName = "Dominating Licid"; String wrongSetCode = "AA"; - String expectedSetCode = CardEdition.UNKNOWN.getCode(); - CardRarity expectedCardRarity = CardRarity.Unknown; + String expectedSetCode = "EXO"; // Exodus + CardRarity expectedCardRarity = CardRarity.Rare; PaperCard dominatingLycidCard = this.cardDb.getCard(cardName); assertNull(dominatingLycidCard); @@ -104,11 +104,6 @@ public class CardDbTestLazyCardLoading extends ForgeCardMockTestCase { FModel.getMagicDb().attemptToLoadCard(cardName, wrongSetCode); dominatingLycidCard = this.cardDb.getCard(cardName); - assertNull(dominatingLycidCard); // card still not found - - // Resorting to Unsupported Card Request - String cardRequest = CardDb.CardRequest.compose(cardName, wrongSetCode); - dominatingLycidCard = this.cardDb.createUnsupportedCard(cardRequest); assertNotNull(dominatingLycidCard); assertEquals(dominatingLycidCard.getName(), cardName); assertEquals(dominatingLycidCard.getEdition(), expectedSetCode); From a8acbf049141fb55eb1285ad47b01d71776952d6 Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 20:46:44 +0100 Subject: [PATCH 06/31] Improved CountAll method implementation using Maps --- .../src/main/java/forge/util/ItemPool.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/forge-core/src/main/java/forge/util/ItemPool.java b/forge-core/src/main/java/forge/util/ItemPool.java index 7a065ca1639..8222f61f597 100644 --- a/forge-core/src/main/java/forge/util/ItemPool.java +++ b/forge-core/src/main/java/forge/util/ItemPool.java @@ -31,7 +31,9 @@ import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; import forge.item.InventoryItem; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; /** *

@@ -137,30 +139,26 @@ public class ItemPool implements Iterable int countAll(Predicate condition){ + public int countAll(Predicate condition){ int count = 0; - Iterable matchingKeys = Iterables.filter(this.items.keySet(), new Predicate() { - @Override - public boolean apply(T item) { - return condition.apply((U)item); - } - }); - for (T key : matchingKeys) - count += this.items.get(key); + for (Integer v : Maps.filterKeys(this.items, condition).values()) + count += v; return count; + } @SuppressWarnings("unchecked") public final int countAll(Predicate condition, Class cls) { int count = 0; - Iterable matchingKeys = Iterables.filter(this.items.keySet(), new Predicate() { + Map matchingKeys = Maps.filterKeys(this.items, new Predicate() { @Override public boolean apply(T item) { - return cls.isInstance(item) && condition.apply((U)item); + return cls.isInstance(item) && (condition.apply((U)item)); } }); - for (T key : matchingKeys) - count += this.items.get(key); + for (Integer i : matchingKeys.values()) { + count += i; + } return count; } From 8d35f747c0333aeefe3cd4d7db6244fb09130b04 Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 20:47:03 +0100 Subject: [PATCH 07/31] Restored original implementation (but still using iterator) --- .../itemmanager/filters/StatTypeFilter.java | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/filters/StatTypeFilter.java b/forge-gui-desktop/src/main/java/forge/itemmanager/filters/StatTypeFilter.java index 80333946b52..7535365afed 100644 --- a/forge-gui-desktop/src/main/java/forge/itemmanager/filters/StatTypeFilter.java +++ b/forge-gui-desktop/src/main/java/forge/itemmanager/filters/StatTypeFilter.java @@ -75,25 +75,17 @@ public abstract class StatTypeFilter extends ToggleButt if (btnPackOrDeck != null) { //support special pack/deck case int count = items.countAll(ItemPredicate.Presets.IS_PACK_OR_DECK, InventoryItem.class); btnPackOrDeck.setText(String.valueOf(count)); + } - Iterator buttonMapStatsIterator = buttonMap.keySet().iterator(); - while (buttonMapStatsIterator.hasNext()){ - StatTypes statTypes = buttonMapStatsIterator.next(); - if (statTypes.predicate != null){ - count = items.countAll(Predicates.compose(statTypes.predicate, PaperCard.FN_GET_RULES), PaperCard.class); - buttonMap.get(statTypes).setText(String.valueOf(count)); - } - } - } else { - Iterator buttonMapStatsIterator = buttonMap.keySet().iterator(); - while (buttonMapStatsIterator.hasNext()) { - StatTypes statTypes = buttonMapStatsIterator.next(); - if (statTypes.predicate != null) { - int count = items.countAll(Predicates.compose(statTypes.predicate, PaperCard.FN_GET_RULES)); - buttonMap.get(statTypes).setText(String.valueOf(count)); - } + Iterator buttonMapStatsIterator = buttonMap.keySet().iterator(); + while (buttonMapStatsIterator.hasNext()){ + StatTypes statTypes = buttonMapStatsIterator.next(); + if (statTypes.predicate != null){ + int count = items.countAll(Predicates.compose(statTypes.predicate, PaperCard.FN_GET_RULES), PaperCard.class); + buttonMap.get(statTypes).setText(String.valueOf(count)); } } + getWidget().revalidate(); } } From e2034ebd51e4aacd62a75eb3d3336e02c1ee3790 Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 20:48:27 +0100 Subject: [PATCH 08/31] Added new Lookup table for sortable collector number to avoid repeated computations for the same coll numbers! --- .../src/main/java/forge/card/CardEdition.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardEdition.java b/forge-core/src/main/java/forge/card/CardEdition.java index 9df2546479f..7e3c3bca32d 100644 --- a/forge-core/src/main/java/forge/card/CardEdition.java +++ b/forge-core/src/main/java/forge/card/CardEdition.java @@ -199,15 +199,21 @@ public final class CardEdition implements Comparable { * @param collectorNumber: Input collectorNumber tro transform in a Sorting Key * @return A 5-digits zero-padded collector number + any non-numerical parts attached. */ + private static Map sortableCollNumberLookup = new HashMap<>(); public static String getSortableCollectorNumber(final String collectorNumber){ - String sortableCollNr = collectorNumber; - if (sortableCollNr == null || sortableCollNr.length() == 0) - sortableCollNr = "50000"; // very big number of 5 digits to have them in last positions + String inputCollNumber = collectorNumber; + 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); + if (matchedCollNr != null) + return matchedCollNr; // Now, for proper sorting, let's zero-pad the collector number (if integer) int collNr; + String sortableCollNr = inputCollNumber; try { - collNr = Integer.parseInt(sortableCollNr); + collNr = Integer.parseInt(collectorNumber); sortableCollNr = String.format("%05d", collNr); } catch (NumberFormatException ex) { String nonNumeric = sortableCollNr.replaceAll("[0-9]", ""); @@ -222,6 +228,7 @@ public final class CardEdition implements Comparable { else // e.g. WS6, S1 sortableCollNr = nonNumeric + String.format("%05d", collNr); } + sortableCollNumberLookup.put(inputCollNumber, sortableCollNr); return sortableCollNr; } From eea2b754ecee3a43945a962c1d60994dcd12700b Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 20:55:33 +0100 Subject: [PATCH 09/31] Added new Lookup table for sortable collector number to avoid repeated computations for the same coll numbers! FIX typo and made map final! --- forge-core/src/main/java/forge/card/CardEdition.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardEdition.java b/forge-core/src/main/java/forge/card/CardEdition.java index 7e3c3bca32d..ed9d8ed12b1 100644 --- a/forge-core/src/main/java/forge/card/CardEdition.java +++ b/forge-core/src/main/java/forge/card/CardEdition.java @@ -199,7 +199,7 @@ public final class CardEdition implements Comparable { * @param collectorNumber: Input collectorNumber tro transform in a Sorting Key * @return A 5-digits zero-padded collector number + any non-numerical parts attached. */ - private static Map sortableCollNumberLookup = new HashMap<>(); + private static final Map sortableCollNumberLookup = new HashMap<>(); public static String getSortableCollectorNumber(final String collectorNumber){ String inputCollNumber = collectorNumber; if (collectorNumber == null || collectorNumber.length() == 0) @@ -213,7 +213,7 @@ public final class CardEdition implements Comparable { int collNr; String sortableCollNr = inputCollNumber; try { - collNr = Integer.parseInt(collectorNumber); + collNr = Integer.parseInt(inputCollNumber); sortableCollNr = String.format("%05d", collNr); } catch (NumberFormatException ex) { String nonNumeric = sortableCollNr.replaceAll("[0-9]", ""); From 924c57be703d14869c75d3e906fb2b76bbf7eb5d Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 20:58:06 +0100 Subject: [PATCH 10/31] FIX typo, and renamed vars! --- .../src/main/java/forge/card/CardEdition.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/forge-core/src/main/java/forge/card/CardEdition.java b/forge-core/src/main/java/forge/card/CardEdition.java index ed9d8ed12b1..820c6865c30 100644 --- a/forge-core/src/main/java/forge/card/CardEdition.java +++ b/forge-core/src/main/java/forge/card/CardEdition.java @@ -211,22 +211,22 @@ public final class CardEdition implements Comparable { // Now, for proper sorting, let's zero-pad the collector number (if integer) int collNr; - String sortableCollNr = inputCollNumber; + String sortableCollNr; try { collNr = Integer.parseInt(inputCollNumber); sortableCollNr = String.format("%05d", collNr); } catch (NumberFormatException ex) { - String nonNumeric = sortableCollNr.replaceAll("[0-9]", ""); - String onlyNumeric = sortableCollNr.replaceAll("[^0-9]", ""); + String nonNumSub = inputCollNumber.replaceAll("[0-9]", ""); + String onlyNumSub = inputCollNumber.replaceAll("[^0-9]", ""); try { - collNr = Integer.parseInt(onlyNumeric); + collNr = Integer.parseInt(onlyNumSub); } catch (NumberFormatException exon) { collNr = 0; // this is the case of ONLY-letters collector numbers } - if ((collNr > 0) && (sortableCollNr.startsWith(onlyNumeric))) // e.g. 12a, 37+, 2018f, - sortableCollNr = String.format("%05d", collNr) + nonNumeric; + if ((collNr > 0) && (inputCollNumber.startsWith(onlyNumSub))) // e.g. 12a, 37+, 2018f, + sortableCollNr = String.format("%05d", collNr) + nonNumSub; else // e.g. WS6, S1 - sortableCollNr = nonNumeric + String.format("%05d", collNr); + sortableCollNr = nonNumSub + String.format("%05d", collNr); } sortableCollNumberLookup.put(inputCollNumber, sortableCollNr); return sortableCollNr; From 831b40e753eb85b846959aea266dcb27fd38357c Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sat, 28 Aug 2021 21:13:59 +0100 Subject: [PATCH 11/31] Removed unused imports! --- forge-core/src/main/java/forge/util/ItemPool.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/forge-core/src/main/java/forge/util/ItemPool.java b/forge-core/src/main/java/forge/util/ItemPool.java index 8222f61f597..63380622e57 100644 --- a/forge-core/src/main/java/forge/util/ItemPool.java +++ b/forge-core/src/main/java/forge/util/ItemPool.java @@ -30,10 +30,8 @@ import java.util.concurrent.ConcurrentHashMap; import com.google.common.base.Function; import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import forge.item.InventoryItem; -import org.checkerframework.checker.nullness.compatqual.NullableDecl; /** *

From b1ed8565d39f55e0386f2700ec68876ea1da17b9 Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sun, 29 Aug 2021 08:06:43 +0100 Subject: [PATCH 12/31] FIXED typo in using iterator rather than keyset directly --- forge-core/src/main/java/forge/deck/CardPool.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-core/src/main/java/forge/deck/CardPool.java b/forge-core/src/main/java/forge/deck/CardPool.java index e152a3ba47c..8cb40f4a8cb 100644 --- a/forge-core/src/main/java/forge/deck/CardPool.java +++ b/forge-core/src/main/java/forge/deck/CardPool.java @@ -435,7 +435,7 @@ public class CardPool extends ItemPool { public String toCardList(String separator) { List> main2sort = Lists.newArrayList(this); - Collections.sort(main2sort, ItemPoolSorter.BY_NAME_THEN_SET); + main2sort.sort(ItemPoolSorter.BY_NAME_THEN_SET); final CardDb commonDb = StaticData.instance().getCommonCards(); StringBuilder sb = new StringBuilder(); @@ -463,7 +463,7 @@ public class CardPool extends ItemPool { */ public CardPool getFilteredPool(Predicate predicate) { CardPool filteredPool = new CardPool(); - Iterator cardsInPool = (Iterator) this.items.keySet(); + Iterator cardsInPool = this.items.keySet().iterator(); while (cardsInPool.hasNext()){ PaperCard c = cardsInPool.next(); if (predicate.apply(c)) From 24c31e9de9fc5de212ddc48d38f74f93ec5d03ee Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Sun, 29 Aug 2021 11:02:26 +0100 Subject: [PATCH 13/31] Restored sort in CardPool using Collections to allow for Android comp. --- forge-core/src/main/java/forge/deck/CardPool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-core/src/main/java/forge/deck/CardPool.java b/forge-core/src/main/java/forge/deck/CardPool.java index 8cb40f4a8cb..ddda471c676 100644 --- a/forge-core/src/main/java/forge/deck/CardPool.java +++ b/forge-core/src/main/java/forge/deck/CardPool.java @@ -435,7 +435,7 @@ public class CardPool extends ItemPool { public String toCardList(String separator) { List> main2sort = Lists.newArrayList(this); - main2sort.sort(ItemPoolSorter.BY_NAME_THEN_SET); + Collections.sort(main2sort, ItemPoolSorter.BY_NAME_THEN_SET); final CardDb commonDb = StaticData.instance().getCommonCards(); StringBuilder sb = new StringBuilder(); From d19f30e9ab1863d8e63ae46b138c8f4064e8cc58 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sun, 29 Aug 2021 21:11:42 +0800 Subject: [PATCH 14/31] Update Venture Effect --- forge-ai/src/main/java/forge/ai/PlayerControllerAi.java | 7 +++++++ .../java/forge/game/ability/effects/VentureEffect.java | 9 ++------- .../main/java/forge/game/player/PlayerController.java | 2 ++ .../main/java/forge/player/PlayerControllerHuman.java | 6 ++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 923d862b406..60821052be8 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -1291,6 +1291,13 @@ public class PlayerControllerAi extends PlayerController { return SpellApiToAi.Converter.get(api).chooseCardName(player, sa, faces); } + @Override + public Card chooseDungeon(Player player, List dungeonCards, String message) { + //todo AI + int i = MyRandom.getRandom().nextInt(dungeonCards.size()); + return Card.fromPaperCard(dungeonCards.get(i), player); + } + @Override public List chooseCardsForSplice(SpellAbility sa, List cards) { // sort from best to worst 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 6c17181028d..8c7cc036900 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 @@ -8,7 +8,6 @@ import com.google.common.base.Predicates; import forge.StaticData; import forge.card.CardRulesPredicates; -import forge.card.ICardFace; import forge.game.Game; import forge.game.ability.AbilityKey; import forge.game.ability.SpellAbilityEffect; @@ -45,13 +44,9 @@ public class VentureEffect extends SpellAbilityEffect { // Create a new dungeon card chosen by player in command zone. List dungeonCards = StaticData.instance().getVariantCards().getAllCards( Predicates.compose(CardRulesPredicates.Presets.IS_DUNGEON, PaperCard.FN_GET_RULES)); - List faces = new ArrayList<>(); - for (PaperCard pc : dungeonCards) { - faces.add(pc.getRules().getMainPart()); - } + String message = Localizer.getInstance().getMessage("lblChooseDungeon"); - String chosen = player.getController().chooseCardName(sa, faces, message); - Card dungeon = Card.fromPaperCard(StaticData.instance().getVariantCards().getUniqueByName(chosen), player); + Card dungeon = player.getController().chooseDungeon(player, dungeonCards, message); game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); game.getAction().moveTo(ZoneType.Command, dungeon, sa); diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index a11be5b4fdd..73307e15989 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -261,6 +261,8 @@ public abstract class PlayerController { public abstract String chooseCardName(SpellAbility sa, Predicate cpp, String valid, String message); public abstract String chooseCardName(SpellAbility sa, List faces, String message); + + public abstract Card chooseDungeon(Player player, List dungeonCards, String message); // better to have this odd method than those if playerType comparison in ChangeZone public abstract Card chooseSingleCardForZoneChange(ZoneType destination, List origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, boolean isOptional, Player decider); diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index cfab29621f6..27dd70665c9 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -3235,6 +3235,12 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return face == null ? "" : face.getName(); } + @Override + public Card chooseDungeon(Player player, List dungeonCards, String message) { + PaperCard dungeon = getGui().one(message, dungeonCards); + return Card.fromPaperCard(dungeon, player); + } + @Override public List chooseCardsForSplice(SpellAbility sa, List cards) { GameEntityViewMap gameCacheSplice = GameEntityView.getMap(cards); From eb0a6d47b1355cb52e7c85e966397947cc325dd7 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sun, 29 Aug 2021 21:19:30 +0800 Subject: [PATCH 15/31] updateTest --- .../gamesimulationtests/util/PlayerControllerForTests.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index 3f78ac4ae49..d8bb86a4f0a 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -681,6 +681,12 @@ public class PlayerControllerForTests extends PlayerController { return null; } + @Override + public Card chooseDungeon(Player player, List dungeonCards, String message) { + // TODO Auto-generated method stub + return null; + } + @Override public List chooseCardsForSplice(SpellAbility sa, List cards) { return Lists.newArrayList(); From 93421a753d4934d184b1cea2180d59aee052bf3a Mon Sep 17 00:00:00 2001 From: TRT <> Date: Mon, 30 Aug 2021 07:42:36 +0200 Subject: [PATCH 16/31] Fix VentureAi --- .../java/forge/ai/PlayerControllerAi.java | 22 +++++++++++++++---- .../main/java/forge/ai/ability/VentureAi.java | 22 ------------------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 60821052be8..c3f032c24f6 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -1292,10 +1292,24 @@ public class PlayerControllerAi extends PlayerController { } @Override - public Card chooseDungeon(Player player, List dungeonCards, String message) { - //todo AI - int i = MyRandom.getRandom().nextInt(dungeonCards.size()); - return Card.fromPaperCard(dungeonCards.get(i), player); + public Card chooseDungeon(Player ai, List dungeonCards, String message) { + // TODO: improve the conditions that define which dungeon is a viable option to choose + List dungeonNames = Lists.newArrayList(); + for (PaperCard pc : dungeonCards) { + dungeonNames.add(pc.getName()); + } + + // Don't choose Tomb of Annihilation when life in danger unless we can win right away or can't lose for 0 life + if (ai.getController().isAI()) { // FIXME: is this needed? Can simulation ever run this for a non-AI player? + int lifeInDanger = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD)); + if ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife()) + && !(ai.getLife() > 1 && ai.getWeakestOpponent().getLife() == 1)) { + dungeonNames.remove("Tomb of Annihilation"); + } + } + + int i = MyRandom.getRandom().nextInt(dungeonNames.size()); + return Card.fromPaperCard(dungeonCards.get(i), ai); } @Override diff --git a/forge-ai/src/main/java/forge/ai/ability/VentureAi.java b/forge-ai/src/main/java/forge/ai/ability/VentureAi.java index e836ad3fde2..f3e7c88642f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/VentureAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/VentureAi.java @@ -2,10 +2,8 @@ package forge.ai.ability; import com.google.common.collect.Lists; import forge.ai.AiPlayDecision; -import forge.ai.AiProps; import forge.ai.PlayerControllerAi; import forge.ai.SpellAbilityAi; -import forge.card.ICardFace; import forge.game.player.Player; import forge.game.player.PlayerActionConfirmMode; import forge.game.spellability.SpellAbility; @@ -52,24 +50,4 @@ public class VentureAi extends SpellAbilityAi { return Aggregates.random(spells); // If we're here, we should choose at least something, so choose a random thing then } - // AI that chooses which dungeon to venture into - @Override - public String chooseCardName(Player ai, SpellAbility sa, List faces) { - // TODO: improve the conditions that define which dungeon is a viable option to choose - List dungeonNames = Lists.newArrayList(); - for (ICardFace face : faces) { - dungeonNames.add(face.getName()); - } - - // Don't choose Tomb of Annihilation when life in danger unless we can win right away or can't lose for 0 life - if (ai.getController().isAI()) { // FIXME: is this needed? Can simulation ever run this for a non-AI player? - int lifeInDanger = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD)); - if ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife()) - && !(ai.getLife() > 1 && ai.getWeakestOpponent().getLife() == 1)) { - dungeonNames.remove("Tomb of Annihilation"); - } - } - - return Aggregates.random(dungeonNames); - } } From 8560b8bd8962a96343c92f6658fe1528e21cd657 Mon Sep 17 00:00:00 2001 From: John Williams Date: Mon, 30 Aug 2021 13:08:22 +0000 Subject: [PATCH 17/31] Update Secret Lair Drop Series.txt --- forge-gui/res/editions/Secret Lair Drop Series.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/forge-gui/res/editions/Secret Lair Drop Series.txt b/forge-gui/res/editions/Secret Lair Drop Series.txt index 9cc0667bd9b..b15236f5408 100644 --- a/forge-gui/res/editions/Secret Lair Drop Series.txt +++ b/forge-gui/res/editions/Secret Lair Drop Series.txt @@ -348,11 +348,14 @@ ScryfallCode=SLD 579 L Forest @Johannes Voss 581 M Lucille @Jason Felix 582 R Brainstorm @Mark Poole +585 R Terramorphic Expanse @ 589 R Arcane Signet @Dan Frazier 591 R Crash Through @Tyler Walpole 603 M Eldrazi Monument @Cosmin Podar 604 R Ornithopter @Cosmin Podar +605 R Panharmonicon @Cosmin Podar 606 R Swiftfoot Boots @Cosmin Podar +607 R Rogue's Passage @Cosmin Podar [tokens] b_1_1_faerie_rogue_flying From 4a4983cd6d379cd370059018bf60abca8c2279b8 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Mon, 16 Aug 2021 19:40:25 -0400 Subject: [PATCH 18/31] Sega Dreamcast Cards edition file --- forge-gui/res/editions/Sega Dreamcast Cards.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 forge-gui/res/editions/Sega Dreamcast Cards.txt diff --git a/forge-gui/res/editions/Sega Dreamcast Cards.txt b/forge-gui/res/editions/Sega Dreamcast Cards.txt new file mode 100644 index 00000000000..6cb4ffa12cb --- /dev/null +++ b/forge-gui/res/editions/Sega Dreamcast Cards.txt @@ -0,0 +1,17 @@ +[metadata] +Code=PSDG +Date=2001-06-28 +Name=Sega Dreamcast Cards +Type=Promo + +[cards] +1 R Arden Angel @Greg Staples +2 R Ashuza's Breath @Glen Angus +3 R Camato Scout @Christopher Moeller +4 R Hapato's Might @Jeff Easley +5 R Lydari Druid @Kev Walker +6 R Lydari Elephant @Heather Hudson +7 R Murgish Cemetery @Daren Bader +8 R Saji's Torrent @Matt Cavotta +9 R Tornellan Protector @Eric Peterson +10 R Velican Dragon @Daren Bader From 5c0ee316982da444cc3ca16063bed36aa7f4ce91 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Mon, 16 Aug 2021 21:52:06 -0400 Subject: [PATCH 19/31] arden_angel.txt --- forge-gui/res/cardsfolder/a/arden_angel.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 forge-gui/res/cardsfolder/a/arden_angel.txt diff --git a/forge-gui/res/cardsfolder/a/arden_angel.txt b/forge-gui/res/cardsfolder/a/arden_angel.txt new file mode 100644 index 00000000000..20cbab881c8 --- /dev/null +++ b/forge-gui/res/cardsfolder/a/arden_angel.txt @@ -0,0 +1,10 @@ +Name:Arden Angel +ManaCost:4 W W +Types:Creature Angel +PT:4/4 +K:Flying +T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Graveyard | IsPresent$ Card.StrictlySelf | PresentZone$ Graveyard | Execute$ TrigChooseNumber | TriggerDescription$ At the beginning of your upkeep, if CARDNAME is in your graveyard, choose a number from 1 to 4 at random. If the chosen number is 1, return CARDNAME from your graveyard to the battlefield. +SVar:TrigChooseNumber:DB$ ChooseNumber | Defined$ You | Min$ 1 | Max$ 4 | Random$ True | SubAbility$ DBChangeZone +SVar:DBChangeZone:DB$ ChangeZone | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ1 | Origin$ Graveyard | Destination$ Battlefield +SVar:X:Count$ChosenNumber +Oracle:Flying\nAt the beginning of your upkeep, if Arden Angel is in your graveyard, choose a number from 1 to 4 at random. If the chosen number is 1, return Arden Angel from your graveyard to the battlefield. From 60ed3bf48bc56e3e97ed3b18f50973856b8b3610 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Wed, 25 Aug 2021 20:02:48 -0400 Subject: [PATCH 20/31] ashuzas_breath.txt --- forge-gui/res/cardsfolder/a/ashuzas_breath.txt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 forge-gui/res/cardsfolder/a/ashuzas_breath.txt diff --git a/forge-gui/res/cardsfolder/a/ashuzas_breath.txt b/forge-gui/res/cardsfolder/a/ashuzas_breath.txt new file mode 100644 index 00000000000..d535a2f87a8 --- /dev/null +++ b/forge-gui/res/cardsfolder/a/ashuzas_breath.txt @@ -0,0 +1,8 @@ +Name:Ashuza's Breath +ManaCost:1 R +Types:Sorcery +A:SP$ RepeatEach | RepeatCards$ Creature | Zone$ Battlefield | RepeatSubAbility$ ChooseNumber | SpellDescription$ For each creature, choose a number from 0 to 2 at random. CARDNAME deals that much damage to that creature. +SVar:ChooseNumber:DB$ ChooseNumber | Defined$ You | Min$ 0 | Max$ 2 | Random$ True | SubAbility$ DBDealDamage +SVar:DBDealDamage:DB$ DealDamage | NumDmg$ X | Defined$ Remembered +SVar:X:Count$ChosenNumber +Oracle:For each creature, choose a number from 0 to 2 at random. Ashuza's Breath deals that much damage to that creature. From a67686ee8ac1ff7e152a75e68778098d1adee93e Mon Sep 17 00:00:00 2001 From: Northmoc Date: Wed, 25 Aug 2021 20:03:50 -0400 Subject: [PATCH 21/31] ChooseTypeEffect support AtRandom --- .../java/forge/game/ability/effects/ChooseTypeEffect.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java index 6a601d7257b..4aae63ff01e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java @@ -11,6 +11,7 @@ import forge.game.card.Card; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; +import forge.util.Aggregates; public class ChooseTypeEffect extends SpellAbilityEffect { @@ -63,8 +64,13 @@ public class ChooseTypeEffect extends SpellAbilityEffect { if (!validTypes.isEmpty()) { for (final Player p : tgtPlayers) { + String choice; if ((tgt == null) || p.canBeTargetedBy(sa)) { - String choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes); + if (sa.hasParam("AtRandom")) { + choice = Aggregates.random(validTypes); + } else { + choice = p.getController().chooseSomeType(type, sa, validTypes, invalidTypes); + } if (!sa.hasParam("ChooseType2")) { card.setChosenType(choice); } else { From 8a60d7006e9d642fcf2afdf541cb5a6be7bf233c Mon Sep 17 00:00:00 2001 From: Northmoc Date: Wed, 25 Aug 2021 20:04:11 -0400 Subject: [PATCH 22/31] camato_scout.txt --- forge-gui/res/cardsfolder/c/camato_scout.txt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 forge-gui/res/cardsfolder/c/camato_scout.txt diff --git a/forge-gui/res/cardsfolder/c/camato_scout.txt b/forge-gui/res/cardsfolder/c/camato_scout.txt new file mode 100644 index 00000000000..d05d51376ce --- /dev/null +++ b/forge-gui/res/cardsfolder/c/camato_scout.txt @@ -0,0 +1,8 @@ +Name:Camato Scout +ManaCost:1 U U +Types:Creature Merfolk +PT:2/3 +K:ETBReplacement:Other:ChooseLT +SVar:ChooseLT:DB$ ChooseType | Defined$ You | AtRandom$ True | Type$ Basic Land | SpellDescription$ As CARDNAME enters the battlefield, choose a land type. +S:Mode$ Continuous | Affected$ Card.Self | AddKeyword$ ChosenTypewalk | Description$ CARDNAME has landwalk of the chosen type. +Oracle:As Camato Scout enters the battlefield, choose a basic land type at random. Camato Scout has landwalk of the chosen type. From 16f043f6a19a4623f3748198a15c968b6a14ef0c Mon Sep 17 00:00:00 2001 From: Northmoc Date: Wed, 25 Aug 2021 20:11:55 -0400 Subject: [PATCH 23/31] hapatos_might.txt --- forge-gui/res/cardsfolder/h/hapatos_might.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 forge-gui/res/cardsfolder/h/hapatos_might.txt diff --git a/forge-gui/res/cardsfolder/h/hapatos_might.txt b/forge-gui/res/cardsfolder/h/hapatos_might.txt new file mode 100644 index 00000000000..70cf4494e20 --- /dev/null +++ b/forge-gui/res/cardsfolder/h/hapatos_might.txt @@ -0,0 +1,7 @@ +Name:Hapato's Might +ManaCost:2 B +Types:Instant +A:SP$ ChooseNumber | Defined$ You | Min$ 0 | Max$ 6 | Random$ True | SubAbility$ DBPump | StackDescription$ None | SpellDescription$ Target creature gets +X/+0 until end of turn, where X is a number from 0 to 6 chosen at random. +SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ X | StackDescription$ {c:Targeted} gets +X/+0 until end of turn, where X is a number from 0 to 6 chosen at random. +SVar:X:Count$ChosenNumber +Oracle:Target creature gets +X/+0 until end of turn, where X is a number from 0 to 6 chosen at random. From b583fe3a5e6da31c7deee2ee0ed2fd8ced5a1bcd Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 27 Aug 2021 18:53:33 -0400 Subject: [PATCH 24/31] lydari_druid.txt --- forge-gui/res/cardsfolder/l/lydari_druid.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 forge-gui/res/cardsfolder/l/lydari_druid.txt diff --git a/forge-gui/res/cardsfolder/l/lydari_druid.txt b/forge-gui/res/cardsfolder/l/lydari_druid.txt new file mode 100644 index 00000000000..cb41b420cd8 --- /dev/null +++ b/forge-gui/res/cardsfolder/l/lydari_druid.txt @@ -0,0 +1,10 @@ +Name:Lydari Druid +ManaCost:2 G +Types:Creature Druid +PT:2/2 +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRepeat | TriggerDescription$ When CARDNAME enters the battlefield, for each land on the battlefield, choose a basic land type at random. Those lands become the land types chosen this way. (This effect lasts indefinitely.) +SVar:TrigRepeat:DB$ RepeatEach | RepeatCards$ Land | RepeatSubAbility$ DBChooseLT | SubAbility$ DBCleanup +SVar:DBChooseLT:DB$ ChooseType | Defined$ You | AtRandom$ True | Type$ Basic Land | SubAbility$ DBAnimate +SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Types$ ChosenType | RemoveLandTypes$ True | RemoveIntrinsicAbilities$ True | Duration$ Permanent +SVar:DBCleanup:DB$ Cleanup | ClearChosenType$ True +Oracle:When Lydari Druid enters the battlefield, for each land on the battlefield, choose a basic land type at random. Those lands become the land types chosen this way. (This effect lasts indefinitely.) From 025fa88aa4776173d3cc3b797a46fe8439a88115 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 27 Aug 2021 18:59:17 -0400 Subject: [PATCH 25/31] lydari_elephant.txt --- forge-gui/res/cardsfolder/l/lydari_elephant.txt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 forge-gui/res/cardsfolder/l/lydari_elephant.txt diff --git a/forge-gui/res/cardsfolder/l/lydari_elephant.txt b/forge-gui/res/cardsfolder/l/lydari_elephant.txt new file mode 100644 index 00000000000..d99b43b672a --- /dev/null +++ b/forge-gui/res/cardsfolder/l/lydari_elephant.txt @@ -0,0 +1,8 @@ +Name:Lydari Elephant +ManaCost:4 G +Types:Creature Elephant +PT:*/* +K:ETBReplacement:Other:RandomPower +SVar:RandomPower:DB$ Animate | Defined$ Self | Power$ X | Toughness$ X | Duration$ Permanent | SpellDescription$ As CARDNAME enters the battlefield, choose two numbers from 3 to 7 at random. CARDNAME's power is equal to the first number chosen and its toughness equal to the second number chosen. +SVar:X:Count$Random.3.7 +Oracle:As Lydari Elephant enters the battlefield, choose two numbers from 3 to 7 at random. Lydari Elephant's power is equal to the first number chosen and its toughness equal to the second number chosen. From 4def4271b053e5aef85228347037337460e79364 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 27 Aug 2021 19:03:02 -0400 Subject: [PATCH 26/31] ashuzas_breath.txt with Count$Random --- forge-gui/res/cardsfolder/a/ashuzas_breath.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/forge-gui/res/cardsfolder/a/ashuzas_breath.txt b/forge-gui/res/cardsfolder/a/ashuzas_breath.txt index d535a2f87a8..8b40649b012 100644 --- a/forge-gui/res/cardsfolder/a/ashuzas_breath.txt +++ b/forge-gui/res/cardsfolder/a/ashuzas_breath.txt @@ -1,8 +1,7 @@ Name:Ashuza's Breath ManaCost:1 R Types:Sorcery -A:SP$ RepeatEach | RepeatCards$ Creature | Zone$ Battlefield | RepeatSubAbility$ ChooseNumber | SpellDescription$ For each creature, choose a number from 0 to 2 at random. CARDNAME deals that much damage to that creature. -SVar:ChooseNumber:DB$ ChooseNumber | Defined$ You | Min$ 0 | Max$ 2 | Random$ True | SubAbility$ DBDealDamage +A:SP$ RepeatEach | RepeatCards$ Creature | Zone$ Battlefield | RepeatSubAbility$ DBDealDamage | SpellDescription$ For each creature, choose a number from 0 to 2 at random. CARDNAME deals that much damage to that creature. SVar:DBDealDamage:DB$ DealDamage | NumDmg$ X | Defined$ Remembered -SVar:X:Count$ChosenNumber +SVar:X:Count$Random.0.2 Oracle:For each creature, choose a number from 0 to 2 at random. Ashuza's Breath deals that much damage to that creature. From ff64b8f9266655b128ecbf73170d720529465056 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Mon, 30 Aug 2021 15:37:32 -0400 Subject: [PATCH 27/31] murgish_cemetery.txt --- forge-gui/res/cardsfolder/m/murgish_cemetery.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 forge-gui/res/cardsfolder/m/murgish_cemetery.txt diff --git a/forge-gui/res/cardsfolder/m/murgish_cemetery.txt b/forge-gui/res/cardsfolder/m/murgish_cemetery.txt new file mode 100644 index 00000000000..d2ee643de8a --- /dev/null +++ b/forge-gui/res/cardsfolder/m/murgish_cemetery.txt @@ -0,0 +1,9 @@ +Name:Murgish Cemetery +ManaCost:4 B B +Types:Enchantment +A:AB$ ChooseNumber | Cost$ 3 B Discard<1/Card> | Min$ 2 | Max$ 6 | Defined$ You | Random$ True | SubAbility$ DBToken | SpellDescription$ Create an X/X black Zombie creature token, where X is a number from 2 to 6 chosen at random. +SVar:DBToken:DB$ Token | TokenScript$ b_x_x_zombie | TokenPower$ X | TokenToughness$ X +SVar:X:Count$Random.2.6 +DeckHas:Ability$Token & Ability$Discard +DeckHints:Type$Zombie +Oracle:{3}{B}, Discard a card: Create an X/X black Zombie creature token, where X is a number from 2 to 6 chosen at random. From faf3efdf743300284be787487456e5a8da55685b Mon Sep 17 00:00:00 2001 From: Northmoc Date: Mon, 30 Aug 2021 16:22:43 -0400 Subject: [PATCH 28/31] sajis_torrent.txt --- forge-gui/res/cardsfolder/s/sajis_torrent.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 forge-gui/res/cardsfolder/s/sajis_torrent.txt diff --git a/forge-gui/res/cardsfolder/s/sajis_torrent.txt b/forge-gui/res/cardsfolder/s/sajis_torrent.txt new file mode 100644 index 00000000000..a0588010c58 --- /dev/null +++ b/forge-gui/res/cardsfolder/s/sajis_torrent.txt @@ -0,0 +1,9 @@ +Name:Saji's Torrent +ManaCost:1 U +Types:Instant +A:SP$ ChooseNumber | Min$ 0 | Max$ 5 | Defined$ You | Random$ True | SubAbility$ DBChoose | StackDescription$ SpellDescription | SpellDescription$ Tap X creatures, where X is a number from 0 to 5 chosen at random. +SVar:DBChoose:DB$ ChooseCard | Amount$ X | Choices$ Creature | RememberChosen$ True | SubAbility$ DBTapAll | StackDescription$ None +SVar:DBTapAll:DB$ TapAll | ValidCards$ Creature.IsRemembered | StackDescription$ None | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Count$ChosenNumber +Oracle:Tap X creatures, where X is a number from 0 to 5 chosen at random. From ef488fc0d4907829c4d8648b1248ed396c731002 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Mon, 30 Aug 2021 16:23:15 -0400 Subject: [PATCH 29/31] tornellan_protector.txt --- forge-gui/res/cardsfolder/t/tornellan_protector.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 forge-gui/res/cardsfolder/t/tornellan_protector.txt diff --git a/forge-gui/res/cardsfolder/t/tornellan_protector.txt b/forge-gui/res/cardsfolder/t/tornellan_protector.txt new file mode 100644 index 00000000000..84cf3c07076 --- /dev/null +++ b/forge-gui/res/cardsfolder/t/tornellan_protector.txt @@ -0,0 +1,9 @@ +Name:Tornellan Protector +ManaCost:2 W +Types:Creature Cleric +PT:1/2 +A:AB$ Effect | Cost$ T | ValidTgts$ Creature,Player | TgtPrompt$ Select target creature or player | RememberObjects$ Targeted | ReplacementEffects$ Protect | SpellDescription$ Until end of turn, each time damage is dealt to target creature or player, prevent X of that damage, where X is a number from 1 to 3 chosen at random each time. +SVar:Protect:Event$ DamageDone | ActiveZones$ Battlefield | ValidTarget$ Creature.IsRemembered,Player.IsRemembered | ReplaceWith$ DBReplace | PreventionEffect$ True | Description$ Until end of turn, each time damage is dealt to target creature or player, prevent X of that damage, where X is a number from 1 to 3 chosen at random each time. +SVar:DBReplace:DB$ ReplaceDamage | Amount$ X +SVar:X:Count$Random.1.3 +Oracle:{T}: Until end of turn, each time damage is dealt to target creature or player, prevent X of that damage, where X is a number from 1 to 3 chosen at random each time. From a30ddcf5eeebb2f617a8eb865f7fcd4453e73d70 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Mon, 30 Aug 2021 16:23:24 -0400 Subject: [PATCH 30/31] velican_dragon.txt --- forge-gui/res/cardsfolder/v/velican_dragon.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 forge-gui/res/cardsfolder/v/velican_dragon.txt diff --git a/forge-gui/res/cardsfolder/v/velican_dragon.txt b/forge-gui/res/cardsfolder/v/velican_dragon.txt new file mode 100644 index 00000000000..196dad1c877 --- /dev/null +++ b/forge-gui/res/cardsfolder/v/velican_dragon.txt @@ -0,0 +1,10 @@ +Name:Velican Dragon +ManaCost:5 R R +Types:Creature Dragon +PT:5/5 +K:Flying +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks or blocks, it gets +X/+0 until end of turn, where X is a number from 0 to 5 chosen at random. +T:Mode$ Blocks | ValidCard$ Card.Self | Execute$ TrigPump | Secondary$ True | TriggerDescription$ Whenever CARDNAME attacks or blocks, +X/+0 until end of turn, where X is a number from 0 to 5 chosen at random. +SVar:TrigPump:DB$ Pump | Defined$ Self | NumAtt$ X +SVar:X:Count$Random.0.5 +Oracle:Flying\nWhenever Velican Dragon attacks or blocks, it gets +X/+0 until end of turn, where X is a number from 0 to 5 chosen at random. From 3a39ffa05e40bf161a37f4313803770042344abf Mon Sep 17 00:00:00 2001 From: TRT <> Date: Mon, 30 Aug 2021 22:35:05 +0200 Subject: [PATCH 31/31] checkForManaSacrificeCost: skip from autopay for now --- forge-ai/src/main/java/forge/ai/ComputerUtilCost.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index f7e151ff2f3..7fd2d6b050a 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -233,14 +233,12 @@ public class ComputerUtilCost { } public static boolean checkForManaSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility) { - if (cost == null) { + // TODO cheating via autopay can still happen, need to get the real ai player from controlledBy + if (cost == null || !ai.isAI()) { return true; } for (final CostPart part : cost.getCostParts()) { if (part instanceof CostSacrifice) { - if (!ai.isAI()) { - return false; - } CardCollection list = new CardCollection(); final CardCollection exclude = new CardCollection(); if (AiCardMemory.getMemorySet(ai, MemorySet.PAYS_SAC_COST) != null) {