From 50a98238d17768b81868a38d9fb913c68b20fa8d Mon Sep 17 00:00:00 2001 From: Jetz Date: Thu, 6 Nov 2025 08:29:55 -0500 Subject: [PATCH 1/5] Add `allowedEvents` support for adventure configs --- .../src/forge/adventure/data/AdventureEventData.java | 8 ++++++-- forge-gui-mobile/src/forge/adventure/data/ConfigData.java | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java b/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java index d6246d1381a..266eaddf722 100644 --- a/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java +++ b/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java @@ -197,7 +197,11 @@ public class AdventureEventData implements Serializable { ConfigData configData = Config.instance().getConfigData(); Predicate filter = CardEdition.Predicates.CAN_MAKE_BOOSTER.and(selectSetPool()); - if(configData.restrictedEvents != null) { + if(configData.allowedEvents != null) { + Set allowedEvents = Set.of(configData.allowedEvents); + filter = filter.and(q -> allowedEvents.contains(q.getCode())); + } + else if(configData.restrictedEvents != null) { //Temporary restriction until rewards are more diverse - don't want to award restricted cards so these editions need different rewards added. //Also includes sets that use conspiracy or commander drafts. Set restrictedEvents = Set.of(configData.restrictedEvents); @@ -206,7 +210,7 @@ public class AdventureEventData implements Serializable { if (configData.allowedEditions != null) { Set allowed = Set.of(configData.allowedEditions); filter = filter.and(q -> allowed.contains(q.getCode())); - } else { + } else if(configData.restrictedEditions != null) { List restrictedList = Arrays.asList(configData.restrictedEditions); Set restricted = new HashSet<>(restrictedList); //Would use Set.of but that throws an error if there's any duplicates, and users edit these lists all the time. filter = filter.and(q -> !restricted.contains(q.getCode())); diff --git a/forge-gui-mobile/src/forge/adventure/data/ConfigData.java b/forge-gui-mobile/src/forge/adventure/data/ConfigData.java index c5b2a78ba32..c7df86452eb 100644 --- a/forge-gui-mobile/src/forge/adventure/data/ConfigData.java +++ b/forge-gui-mobile/src/forge/adventure/data/ConfigData.java @@ -24,5 +24,6 @@ public class ConfigData { public String[] restrictedEditions; public String[] allowedEditions; public String[] restrictedEvents; + public String[] allowedEvents; public String[] allowedJumpstart; } From 48be3406c725e4b7cf261a650681a0133f8aacd1 Mon Sep 17 00:00:00 2001 From: Jetz Date: Thu, 6 Nov 2025 08:50:00 -0500 Subject: [PATCH 2/5] Reduce bias towards standard in event selection. Make allowedEvents override other filters. Ensure set pool filter can't filter out every event. --- .../adventure/data/AdventureEventData.java | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java b/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java index 266eaddf722..75a4673017b 100644 --- a/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java +++ b/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java @@ -175,17 +175,17 @@ public class AdventureEventData implements Serializable { private static final Predicate filterStandard = FModel.getFormats().getStandard().editionLegalPredicate; public static Predicate selectSetPool() { - // Should we negate any of these to avoid overlap? final int rollD100 = MyRandom.getRandom().nextInt(100); Predicate rolledFilter; if (rollD100 < 30) { rolledFilter = filterStandard; } else if (rollD100 < 60) { - rolledFilter = filterPioneer; + // Remove standard from older pools because its representation is already inflated. + rolledFilter = filterPioneer.and(filterStandard.negate()); } else if (rollD100 < 80) { - rolledFilter = filterModern; + rolledFilter = filterModern.and(filterStandard.negate()); } else { - rolledFilter = filterVintage; + rolledFilter = filterVintage.and(filterStandard.negate()); } return rolledFilter; } @@ -195,25 +195,33 @@ public class AdventureEventData implements Serializable { private static CardBlock pickWeightedCardBlock() { CardEdition.Collection editions = FModel.getMagicDb().getEditions(); ConfigData configData = Config.instance().getConfigData(); - Predicate filter = CardEdition.Predicates.CAN_MAKE_BOOSTER.and(selectSetPool()); + Predicate filter = CardEdition.Predicates.CAN_MAKE_BOOSTER; if(configData.allowedEvents != null) { Set allowedEvents = Set.of(configData.allowedEvents); filter = filter.and(q -> allowedEvents.contains(q.getCode())); } - else if(configData.restrictedEvents != null) { - //Temporary restriction until rewards are more diverse - don't want to award restricted cards so these editions need different rewards added. - //Also includes sets that use conspiracy or commander drafts. - Set restrictedEvents = Set.of(configData.restrictedEvents); - filter = filter.and((q) -> !restrictedEvents.contains(q.getCode())); - } - if (configData.allowedEditions != null) { - Set allowed = Set.of(configData.allowedEditions); - filter = filter.and(q -> allowed.contains(q.getCode())); - } else if(configData.restrictedEditions != null) { - List restrictedList = Arrays.asList(configData.restrictedEditions); - Set restricted = new HashSet<>(restrictedList); //Would use Set.of but that throws an error if there's any duplicates, and users edit these lists all the time. - filter = filter.and(q -> !restricted.contains(q.getCode())); + else + { + //The whitelist beats all other filters. + if(configData.restrictedEvents != null) { + //Temporary restriction until rewards are more diverse - don't want to award restricted cards so these editions need different rewards added. + //Also includes sets that use conspiracy or commander drafts. + Set restrictedEvents = Set.of(configData.restrictedEvents); + filter = filter.and((q) -> !restrictedEvents.contains(q.getCode())); + } + if (configData.allowedEditions != null) { + Set allowed = Set.of(configData.allowedEditions); + filter = filter.and(q -> allowed.contains(q.getCode())); + } else if(configData.restrictedEditions != null) { + List restrictedList = Arrays.asList(configData.restrictedEditions); + Set restricted = new HashSet<>(restrictedList); //Would use Set.of but that throws an error if there's any duplicates, and users edit these lists all the time. + filter = filter.and(q -> !restricted.contains(q.getCode())); + } + + Predicate setPoolFilter = selectSetPool(); + if(editions.stream().anyMatch(setPoolFilter)) + filter = filter.and(setPoolFilter); } List allEditions = new ArrayList<>(); From f27eb1042fe143813611cfce8958324a9913da4e Mon Sep 17 00:00:00 2001 From: Jetz Date: Thu, 6 Nov 2025 09:30:27 -0500 Subject: [PATCH 3/5] Expand anti-cheese. --- .../src/forge/adventure/character/EnemySprite.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java b/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java index 3f8d99f9f9d..d4841b54ff8 100644 --- a/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java +++ b/forge-gui-mobile/src/forge/adventure/character/EnemySprite.java @@ -450,7 +450,9 @@ public class EnemySprite extends CharacterSprite implements Steerable { .filter(paperCard -> !paperCard.isVeryBasicLand()) .collect(Collectors.toList()); - if (paperCardList.size() < 6) { + int uniqueRules = paperCardList.stream().map(PaperCard::getRules).collect(Collectors.toSet()).size(); + + if (uniqueRules < 4 || paperCardList.size() < 10) { // Player trying to cheese doppleganger and farm cards. Sorry, the fun police have arrived // Static rewards of 199 GP, 9 Shards, and 1 Cheese Stands Alone rewards.add(new Reward(199)); @@ -466,7 +468,7 @@ public class EnemySprite extends CharacterSprite implements Steerable { if (AdventurePlayer.current().isFantasyMode()) { //random uncommons from deck List uncommonCards = paperCardList.stream() - .filter(paperCard -> CardRarity.Uncommon.equals(paperCard.getRarity()) || CardRarity.Special.equals(paperCard.getRarity())) + .filter(paperCard -> paperCard.getRarity() == CardRarity.Uncommon || paperCard.getRarity() == CardRarity.Special) .collect(Collectors.toList()); if (!uncommonCards.isEmpty()) { rewards.add(new Reward(Aggregates.random(uncommonCards))); @@ -474,7 +476,7 @@ public class EnemySprite extends CharacterSprite implements Steerable { } //random commons from deck List commmonCards = paperCardList.stream() - .filter(paperCard -> CardRarity.Common.equals(paperCard.getRarity())) + .filter(paperCard -> paperCard.getRarity() == CardRarity.Common) .collect(Collectors.toList()); if (!commmonCards.isEmpty()) { rewards.add(new Reward(Aggregates.random(commmonCards))); @@ -483,7 +485,7 @@ public class EnemySprite extends CharacterSprite implements Steerable { } //random rare from deck List rareCards = paperCardList.stream() - .filter(paperCard -> CardRarity.Rare.equals(paperCard.getRarity()) || CardRarity.MythicRare.equals(paperCard.getRarity())) + .filter(paperCard -> paperCard.getRarity() == CardRarity.Rare || paperCard.getRarity() == CardRarity.MythicRare) .collect(Collectors.toList()); if (!rareCards.isEmpty()) { rewards.add(new Reward(Aggregates.random(rareCards))); From 843f2de2726e7f2c689598cea3abdbeb7769f8ad Mon Sep 17 00:00:00 2001 From: Jetz Date: Thu, 6 Nov 2025 09:30:44 -0500 Subject: [PATCH 4/5] Remove some redundant imports. --- forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java index 4e50c772f70..6c9a357e69f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java @@ -1,7 +1,5 @@ package forge.ai.ability; -import forge.ai.AiAbilityDecision; -import forge.ai.AiPlayDecision; import forge.ai.*; import forge.game.Game; import forge.game.ability.AbilityUtils; From d9e356e20cdfd98a8d37ecc2818f7d64a69de0fb Mon Sep 17 00:00:00 2001 From: Jetz Date: Thu, 6 Nov 2025 09:34:56 -0500 Subject: [PATCH 5/5] Add some extra checks for safety. --- .../src/forge/adventure/data/AdventureEventData.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java b/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java index 75a4673017b..7d71d4e6208 100644 --- a/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java +++ b/forge-gui-mobile/src/forge/adventure/data/AdventureEventData.java @@ -197,7 +197,7 @@ public class AdventureEventData implements Serializable { ConfigData configData = Config.instance().getConfigData(); Predicate filter = CardEdition.Predicates.CAN_MAKE_BOOSTER; - if(configData.allowedEvents != null) { + if(configData.allowedEvents != null && configData.allowedEvents.length > 0) { Set allowedEvents = Set.of(configData.allowedEvents); filter = filter.and(q -> allowedEvents.contains(q.getCode())); } @@ -210,7 +210,7 @@ public class AdventureEventData implements Serializable { Set restrictedEvents = Set.of(configData.restrictedEvents); filter = filter.and((q) -> !restrictedEvents.contains(q.getCode())); } - if (configData.allowedEditions != null) { + if (configData.allowedEditions != null && configData.allowedEditions.length > 0) { Set allowed = Set.of(configData.allowedEditions); filter = filter.and(q -> allowed.contains(q.getCode())); } else if(configData.restrictedEditions != null) {