Merge pull request #8500 from Jetz72/setEventCommand

Refactor event generation, add `set event` command.
This commit is contained in:
Jetz72
2025-10-16 10:24:20 -04:00
committed by GitHub
6 changed files with 196 additions and 131 deletions

View File

@@ -9,9 +9,7 @@ import forge.util.storage.StorageExtendable;
import forge.util.storage.StorageReaderFileSections; import forge.util.storage.StorageReaderFileSections;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -93,19 +91,6 @@ public class PrintSheet {
return fetchRoulette(sum + 1, roulette, toSkip); // start over from beginning, in case last cards were to skip return fetchRoulette(sum + 1, roulette, toSkip); // start over from beginning, in case last cards were to skip
} }
public boolean containsCardNamed(String name,int atLeast) {
int count=0;
for (Entry<PaperCard, Integer> kv : cardsWithWeights) {
for (int i = 0; i < kv.getValue(); i++) {
if(kv.getKey().getName().equals(name))
{
count++;
if(count>=atLeast)return true;
}
}
}
return false;
}
public String getName() { public String getName() {
return name; return name;
} }
@@ -147,6 +132,10 @@ public class PrintSheet {
return cardsWithWeights.toFlatList(); return cardsWithWeights.toFlatList();
} }
public Map<String, Integer> toNameLookup() {
return cardsWithWeights.toNameLookup();
}
public static class Reader extends StorageReaderFileSections<PrintSheet> { public static class Reader extends StorageReaderFileSections<PrintSheet> {
public Reader(File file) { public Reader(File file) {
super(file, PrintSheet::getName); super(file, PrintSheet::getName);

View File

@@ -23,14 +23,15 @@ import forge.util.Aggregates;
import forge.util.IterableUtil; import forge.util.IterableUtil;
import forge.util.MyRandom; import forge.util.MyRandom;
import forge.util.StreamUtil; import forge.util.StreamUtil;
import org.apache.commons.lang3.tuple.Pair;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class AdventureEventData implements Serializable { public class AdventureEventData implements Serializable {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final int JUMPSTART_TO_PICK_FROM = 6; private static final int JUMPSTART_TO_PICK_FROM = 6;
public transient BoosterDraft draft; public transient BoosterDraft draft;
@@ -69,6 +70,7 @@ public class AdventureEventData implements Serializable {
style = other.style; style = other.style;
random.setSeed(eventSeed); random.setSeed(eventSeed);
eventStatus = other.eventStatus; eventStatus = other.eventStatus;
format = other.format;
registeredDeck = other.registeredDeck; registeredDeck = other.registeredDeck;
isDraftComplete = other.isDraftComplete; isDraftComplete = other.isDraftComplete;
description = other.description; description = other.description;
@@ -94,28 +96,39 @@ public class AdventureEventData implements Serializable {
} }
public AdventureEventData(Long seed, AdventureEventController.EventFormat selectedFormat) { public AdventureEventData(Long seed, AdventureEventController.EventFormat selectedFormat) {
this(seed, selectedFormat, null, pickCardBlockByFormat(selectedFormat));
}
public AdventureEventData(Long seed, AdventureEventController.EventFormat selectedFormat, CardBlock cardBlock) {
this(seed, selectedFormat, null, cardBlock);
}
public AdventureEventData(Long seed, AdventureEventController.EventFormat selectedFormat, AdventureEventController.EventStyle style, CardBlock cardBlock) {
setEventSeed(seed); setEventSeed(seed);
eventStatus = AdventureEventController.EventStatus.Available; eventStatus = AdventureEventController.EventStatus.Available;
registeredDeck = new Deck(); registeredDeck = new Deck();
format = selectedFormat; format = selectedFormat;
this.cardBlock = cardBlock;
if (cardBlock == null)
return;
cardBlockName = cardBlock.getName();
if (format == AdventureEventController.EventFormat.Draft) { if (format == AdventureEventController.EventFormat.Draft) {
cardBlock = pickWeightedCardBlock();
if (cardBlock == null)
return;
cardBlockName = cardBlock.getName();
setupDraftRewards(); setupDraftRewards();
} else if (format == AdventureEventController.EventFormat.Jumpstart) { } else if (format == AdventureEventController.EventFormat.Jumpstart) {
cardBlock = pickJumpstartCardBlock();
if (cardBlock == null)
return;
cardBlockName = cardBlock.getName();
jumpstartBoosters = AdventureEventController.instance().getJumpstartBoosters(cardBlock, JUMPSTART_TO_PICK_FROM); jumpstartBoosters = AdventureEventController.instance().getJumpstartBoosters(cardBlock, JUMPSTART_TO_PICK_FROM);
packConfiguration = new String[]{cardBlock.getLandSet().getCode(), cardBlock.getLandSet().getCode(), cardBlock.getLandSet().getCode()}; packConfiguration = new String[]{cardBlock.getLandSet().getCode(), cardBlock.getLandSet().getCode(), cardBlock.getLandSet().getCode()};
setupJumpstartRewards(); setupJumpstartRewards();
} }
if(style == null) {
// If the chosen event seed recommends a four-person pod, run it as a RoundRobin
if (getRecommendedPodSize(cardBlock) == 4)
style = AdventureEventController.EventStyle.RoundRobin;
else
style = AdventureEventController.EventStyle.Bracket;
}
this.style = style;
} }
public void setEventSeed(long seed) { public void setEventSeed(long seed) {
@@ -147,6 +160,15 @@ public class AdventureEventData implements Serializable {
return draft; return draft;
} }
private static CardBlock pickCardBlockByFormat(AdventureEventController.EventFormat format) {
return switch (format) {
case Draft -> pickWeightedCardBlock();
case Jumpstart -> pickJumpstartCardBlock();
case Constructed -> null;
case Sealed -> null;
};
}
private static final Predicate<CardEdition> filterPioneer = FModel.getFormats().getPioneer().editionLegalPredicate; private static final Predicate<CardEdition> filterPioneer = FModel.getFormats().getPioneer().editionLegalPredicate;
private static final Predicate<CardEdition> filterModern = FModel.getFormats().getModern().editionLegalPredicate; private static final Predicate<CardEdition> filterModern = FModel.getFormats().getModern().editionLegalPredicate;
private static final Predicate<CardEdition> filterVintage = FModel.getFormats().getVintage().editionLegalPredicate; private static final Predicate<CardEdition> filterVintage = FModel.getFormats().getVintage().editionLegalPredicate;
@@ -168,10 +190,11 @@ public class AdventureEventData implements Serializable {
return rolledFilter; return rolledFilter;
} }
private CardBlock pickWeightedCardBlock() { private static final Set<String> POWER_NINE = Set.of("Black Lotus", "Mox Emerald", "Mox Pearl", "Mox Ruby", "Mox Sapphire", "Mox Jet", "Ancestral Recall", "Timetwister", "Time Walk");
private static CardBlock pickWeightedCardBlock() {
CardEdition.Collection editions = FModel.getMagicDb().getEditions(); CardEdition.Collection editions = FModel.getMagicDb().getEditions();
ConfigData configData = Config.instance().getConfigData(); ConfigData configData = Config.instance().getConfigData();
Iterable<CardBlock> src = FModel.getBlocks(); //all blocks
Predicate<CardEdition> filter = CardEdition.Predicates.CAN_MAKE_BOOSTER.and(selectSetPool()); Predicate<CardEdition> filter = CardEdition.Predicates.CAN_MAKE_BOOSTER.and(selectSetPool());
if(configData.restrictedEvents != null) { if(configData.restrictedEvents != null) {
@@ -195,52 +218,41 @@ public class AdventureEventData implements Serializable {
.filter(CardEdition::hasBoosterTemplate) .filter(CardEdition::hasBoosterTemplate)
.forEach(allEditions::add); .forEach(allEditions::add);
List<CardBlock> legalBlocks = new ArrayList<>(); List<CardBlock> legalBlocks = getValidDraftBlocks(allEditions);
for (CardBlock b : src) { // for each block
if (b.getSets().isEmpty() || (b.getCntBoostersDraft() < 1))
continue;
boolean isOkay = true;
for (CardEdition c : b.getSets()) {
if (!allEditions.contains(c)) {
isOkay = false;
break;
}
if (!c.hasBoosterTemplate()) {
isOkay = false;
break;
} else {
final List<Pair<String, Integer>> slots = c.getBoosterTemplate().getSlots();
int boosterSize = 0;
for (Pair<String, Integer> slot : slots) {
boosterSize += slot.getRight();
}
isOkay = boosterSize > 11;
}
for (PrintSheet ps : c.getPrintSheetsBySection()) {
//exclude block with sets containing P9 cards..
if (ps.containsCardNamed("Black Lotus", 1)
|| ps.containsCardNamed("Mox Emerald", 1)
|| ps.containsCardNamed("Mox Pearl", 1)
|| ps.containsCardNamed("Mox Ruby", 1)
|| ps.containsCardNamed("Mox Sapphire", 1)
|| ps.containsCardNamed("Mox Jet", 1)
|| ps.containsCardNamed("Ancestral Recall", 1)
|| ps.containsCardNamed("Timetwister", 1)
|| ps.containsCardNamed("Time Walk", 1)) {
isOkay = false;
break;
}
}
}
if (isOkay) {
legalBlocks.add(b);
}
}
return legalBlocks.isEmpty() ? null : Aggregates.random(legalBlocks); return legalBlocks.isEmpty() ? null : Aggregates.random(legalBlocks);
} }
private CardBlock pickJumpstartCardBlock() { public static List<CardBlock> getValidDraftBlocks(List<CardEdition> validEditions) {
List<CardBlock> legalBlocks = new ArrayList<>();
for (CardBlock b : FModel.getBlocks()) { // for each block
if (b.getSets().isEmpty() || (b.getCntBoostersDraft() < 1))
continue;
if (!isValidDraftBlock(b, validEditions))
continue;
legalBlocks.add(b);
}
return legalBlocks;
}
private static boolean isValidDraftBlock(CardBlock b, List<CardEdition> validEditions) {
for (CardEdition c : b.getSets()) {
if (!validEditions.contains(c))
return false;
if (!c.hasBoosterTemplate())
return false;
if(c.getBoosterTemplate().getNumberOfCardsExpected() <= 11)
return false;
for (PrintSheet ps : c.getPrintSheetsBySection()) {
//exclude block with sets containing P9 cards.
if(!Collections.disjoint(ps.toNameLookup().keySet(), POWER_NINE))
return false;
}
}
return true;
}
private static CardBlock pickJumpstartCardBlock() {
Iterable<CardBlock> src = FModel.getBlocks(); //all blocks Iterable<CardBlock> src = FModel.getBlocks(); //all blocks
List<CardBlock> legalBlocks = new ArrayList<>(); List<CardBlock> legalBlocks = new ArrayList<>();
ConfigData configData = Config.instance().getConfigData(); ConfigData configData = Config.instance().getConfigData();
@@ -359,6 +371,10 @@ public class AdventureEventData implements Serializable {
} }
} }
public void generateParticipants() {
this.generateParticipants(getRecommendedPodSize(this.cardBlock) - 1); //-1 to account for the player
}
public void generateParticipants(int numberOfOpponents) { public void generateParticipants(int numberOfOpponents) {
participants = new AdventureEventParticipant[numberOfOpponents + 1]; participants = new AdventureEventParticipant[numberOfOpponents + 1];
@@ -457,11 +473,10 @@ public class AdventureEventData implements Serializable {
//3. If no matching color found and need more packs, add any available at random. //3. If no matching color found and need more packs, add any available at random.
if (packConfiguration.length > chosenPacks.size() && colorAdded.isEmpty() && !availableOptions.isEmpty()) { if (packConfiguration.length > chosenPacks.size() && colorAdded.isEmpty() && !availableOptions.isEmpty()) {
chosenPacks.add(Aggregates.removeRandom(availableOptions)); chosenPacks.add(Aggregates.removeRandom(availableOptions));
colorAdded = "";
} else { } else {
done = colorAdded.isEmpty() || packConfiguration.length <= chosenPacks.size(); done = colorAdded.isEmpty() || packConfiguration.length <= chosenPacks.size();
colorAdded = "";
} }
colorAdded = "";
} }
participant.registeredDeck = new Deck(); participant.registeredDeck = new Deck();
for (Deck chosen : chosenPacks) { for (Deck chosen : chosenPacks) {
@@ -469,6 +484,22 @@ public class AdventureEventData implements Serializable {
} }
} }
} }
switch (this.style) {
case Swiss:
case Bracket:
this.rounds = (participants.length / 2) - 1;
break;
case RoundRobin:
this.rounds = participants.length - 1;
break;
}
}
public static int getRecommendedPodSize(CardBlock cardBlock) {
// Set can be null when it is only a meta set such as some Jumpstart events.
CardEdition firstSet = cardBlock.getSets().isEmpty() ? null : cardBlock.getSets().get(0);
return firstSet == null ? 8 : firstSet.getDraftOptions().getRecommendedPodSize();
} }
private void assignPlayerNames(BoosterDraft draft) { private void assignPlayerNames(BoosterDraft draft) {
@@ -664,29 +695,13 @@ public class AdventureEventData implements Serializable {
eventStatus = AdventureEventController.EventStatus.Awarded; eventStatus = AdventureEventController.EventStatus.Awarded;
} }
public String getPairingDescription() {
switch (eventRules.pairingStyle) {
case Swiss:
return "swiss";
case SwissWithCut:
return "swiss (with cut)";
case RoundRobin:
return "round robin";
case SingleElimination:
return "single elimination";
case DoubleElimination:
return "double elimination";
}
return "";
}
public String getDescription(PointOfInterestChanges changes) { public String getDescription(PointOfInterestChanges changes) {
float townPriceModifier = changes == null ? 1f : changes.getTownPriceModifier(); float townPriceModifier = changes == null ? 1f : changes.getTownPriceModifier();
if (format.equals(AdventureEventController.EventFormat.Draft)) { if (format == AdventureEventController.EventFormat.Draft) {
description = "Event Type: Booster Draft\n"; description = "Event Type: Booster Draft\n";
description += "Block: " + getCardBlock() + "\n"; description += "Block: " + getCardBlock() + "\n";
description += "Boosters: " + String.join(", ", packConfiguration) + "\n"; description += "Boosters: " + String.join(", ", packConfiguration) + "\n";
description += "Competition Style: " + participants.length + " players, matches played as best of " + eventRules.gamesPerMatch + ", " + (getPairingDescription()) + "\n\n"; description += "Competition Style: " + participants.length + " players, matches played as best of " + eventRules.gamesPerMatch + ", " + (eventRules.getPairingDescription()) + "\n\n";
if (eventStatus == AdventureEventController.EventStatus.Available) { if (eventStatus == AdventureEventController.EventStatus.Available) {
description += String.format("Pay 1 Entry Fee\n- Gold %d[][+Gold][BLACK]\n- Mana Shards %d[][+Shards][BLACK]\n", Math.round(eventRules.goldToEnter * townPriceModifier), Math.round(eventRules.shardsToEnter * townPriceModifier)); description += String.format("Pay 1 Entry Fee\n- Gold %d[][+Gold][BLACK]\n- Mana Shards %d[][+Shards][BLACK]\n", Math.round(eventRules.goldToEnter * townPriceModifier), Math.round(eventRules.shardsToEnter * townPriceModifier));
@@ -701,10 +716,10 @@ public class AdventureEventData implements Serializable {
} }
} }
description += String.format("Prizes\nChampion: Keep drafted deck\n2+ round wins: Challenge Coin \n1+ round wins: %s Booster, %s Booster\n0 round wins: %s Booster", rewardPacks[0].getComment(), rewardPacks[1].getComment(), rewardPacks[2].getComment()); description += String.format("Prizes\nChampion: Keep drafted deck\n2+ round wins: Challenge Coin \n1+ round wins: %s Booster, %s Booster\n0 round wins: %s Booster", rewardPacks[0].getComment(), rewardPacks[1].getComment(), rewardPacks[2].getComment());
} else if (format.equals(AdventureEventController.EventFormat.Jumpstart)) { } else if (format == AdventureEventController.EventFormat.Jumpstart) {
description = "Event Type: Jumpstart\n"; description = "Event Type: Jumpstart\n";
description += "Block: " + getCardBlock() + "\n"; description += "Block: " + getCardBlock() + "\n";
description += "Competition Style: " + participants.length + " players, matches played as best of " + eventRules.gamesPerMatch + ", " + (getPairingDescription()) + "\n\n"; description += "Competition Style: " + participants.length + " players, matches played as best of " + eventRules.gamesPerMatch + ", " + (eventRules.getPairingDescription()) + "\n\n";
description += String.format("Pay 1 Entry Fee\n- Gold %d[][+Gold][BLACK]\n- Mana Shards %d[][+Shards][BLACK]\n", Math.round(eventRules.goldToEnter * townPriceModifier), Math.round(eventRules.shardsToEnter * townPriceModifier)); description += String.format("Pay 1 Entry Fee\n- Gold %d[][+Gold][BLACK]\n- Mana Shards %d[][+Shards][BLACK]\n", Math.round(eventRules.goldToEnter * townPriceModifier), Math.round(eventRules.shardsToEnter * townPriceModifier));
if (eventRules.acceptsBronzeChallengeCoin) { if (eventRules.acceptsBronzeChallengeCoin) {
description += "- Bronze Challenge Coin [][+BronzeChallengeCoin][BLACK]\n\n"; description += "- Bronze Challenge Coin [][+BronzeChallengeCoin][BLACK]\n\n";
@@ -723,6 +738,7 @@ public class AdventureEventData implements Serializable {
public static class AdventureEventParticipant implements Serializable, Comparable<AdventureEventParticipant> { public static class AdventureEventParticipant implements Serializable, Comparable<AdventureEventParticipant> {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private transient EnemySprite sprite; private transient EnemySprite sprite;
String enemyDataName; String enemyDataName;
@@ -810,6 +826,7 @@ public class AdventureEventData implements Serializable {
} }
public static class AdventureEventRules implements Serializable { public static class AdventureEventRules implements Serializable {
@Serial
private static final long serialVersionUID = -2902188278147984885L; private static final long serialVersionUID = -2902188278147984885L;
public int goldToEnter; public int goldToEnter;
public int shardsToEnter; public int shardsToEnter;
@@ -869,9 +886,20 @@ public class AdventureEventData implements Serializable {
goldToEnter = baseGoldEntry; goldToEnter = baseGoldEntry;
shardsToEnter = baseShardEntry; shardsToEnter = baseShardEntry;
} }
public String getPairingDescription() {
return switch (pairingStyle) {
case Swiss -> "swiss";
case SwissWithCut -> "swiss (with cut)";
case RoundRobin -> "round robin";
case SingleElimination -> "single elimination";
case DoubleElimination -> "double elimination";
};
}
} }
public static class AdventureEventMatch implements Serializable { public static class AdventureEventMatch implements Serializable {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public AdventureEventParticipant p1; public AdventureEventParticipant p1;
public AdventureEventParticipant p2; public AdventureEventParticipant p2;
@@ -880,6 +908,7 @@ public class AdventureEventData implements Serializable {
} }
public static class AdventureEventReward implements Serializable { public static class AdventureEventReward implements Serializable {
@Serial
private final static long serialVersionUID = -2605375040895115477L; private final static long serialVersionUID = -2605375040895115477L;
public int minWins = -1; public int minWins = -1;
public int maxWins = -1; public int maxWins = -1;

View File

@@ -11,6 +11,7 @@ import forge.adventure.stage.GameHUD;
import forge.adventure.util.AdventureEventController; import forge.adventure.util.AdventureEventController;
import forge.adventure.util.Controls; import forge.adventure.util.Controls;
import forge.adventure.util.Current; import forge.adventure.util.Current;
import forge.model.CardBlock;
/** /**
* Scene for the Inn in towns * Scene for the Inn in towns
@@ -34,7 +35,7 @@ public class InnScene extends UIScene {
localObjectId = objectId; localObjectId = objectId;
if (lastGameScene != null) if (lastGameScene != null)
object.lastGameScene=lastGameScene; object.lastGameScene=lastGameScene;
getLocalEvent(); initLocalEvent();
return object; return object;
} }
@@ -109,7 +110,7 @@ public class InnScene extends UIScene {
tempHitPointCost.setDisabled(!purchaseable); tempHitPointCost.setDisabled(!purchaseable);
tempHitPointCost.setText("[+GoldCoin] " + tempHealthCost); tempHitPointCost.setText("[+GoldCoin] " + tempHealthCost);
getLocalEvent(); initLocalEvent();
if (localEvent == null){ if (localEvent == null){
eventDescription.setText("[GREY]No events at this time"); eventDescription.setText("[GREY]No events at this time");
event.setDisabled(true); event.setDisabled(true);
@@ -148,9 +149,7 @@ public class InnScene extends UIScene {
Forge.switchScene(ShopScene.instance()); Forge.switchScene(ShopScene.instance());
} }
private static void initLocalEvent() {
private static void getLocalEvent() {
localEvent = null; localEvent = null;
for (AdventureEventData data : AdventurePlayer.current().getEvents()){ for (AdventureEventData data : AdventurePlayer.current().getEvents()){
if (data.sourceID.equals(localPointOfInterestId) && data.eventOrigin == localObjectId){ if (data.sourceID.equals(localPointOfInterestId) && data.eventOrigin == localObjectId){
@@ -158,7 +157,18 @@ public class InnScene extends UIScene {
return; return;
} }
} }
localEvent = AdventureEventController.instance().createEvent(AdventureEventController.EventStyle.Bracket, localPointOfInterestId, localObjectId, changes); AdventureEventController controller = AdventureEventController.instance();
localEvent = controller.createEvent(localPointOfInterestId);
if(localEvent != null)
controller.initializeEvent(localEvent, localPointOfInterestId, localObjectId, changes);
}
public static void replaceLocalEvent(AdventureEventController.EventFormat format, CardBlock cardBlock) {
AdventurePlayer.current().getEvents().removeIf((data) -> data.sourceID.equals(localPointOfInterestId) && data.eventOrigin == localObjectId);
AdventureEventController controller = AdventureEventController.instance();
localEvent = controller.createEvent(format, cardBlock, localPointOfInterestId);
if(localEvent != null)
controller.initializeEvent(localEvent, localPointOfInterestId, localObjectId, changes);
} }
private void startEvent(){ private void startEvent(){

View File

@@ -8,6 +8,7 @@ import forge.StaticData;
import forge.adventure.character.PlayerSprite; import forge.adventure.character.PlayerSprite;
import forge.adventure.data.*; import forge.adventure.data.*;
import forge.adventure.pointofintrest.PointOfInterest; import forge.adventure.pointofintrest.PointOfInterest;
import forge.adventure.scene.InnScene;
import forge.adventure.scene.InventoryScene; import forge.adventure.scene.InventoryScene;
import forge.adventure.util.AdventureEventController; import forge.adventure.util.AdventureEventController;
import forge.adventure.util.Current; import forge.adventure.util.Current;
@@ -21,7 +22,10 @@ import forge.deck.DeckProxy;
import forge.game.GameType; import forge.game.GameType;
import forge.gui.FThreads; import forge.gui.FThreads;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.model.CardBlock;
import forge.model.FModel;
import forge.screens.CoverScreen; import forge.screens.CoverScreen;
import forge.util.Aggregates;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -510,5 +514,26 @@ public class ConsoleCommandInterpreter {
} }
return message; return message;
}); });
registerCommand(new String[]{"set", "event"}, s -> {
if(s.length < 1) return "Command needs 1 parameter: Block or edition name. ";
String blockName = s[0];
if(MapStage.getInstance().findLocalInn() == null)
return "Must be used within a town with an inn.";
CardBlock eventCardBlock = FModel.getBlocks().find(b -> b.getName().equalsIgnoreCase(blockName));
if(eventCardBlock == null) {
CardEdition edition = FModel.getMagicDb().getEditions().find(e -> e.getCode().equalsIgnoreCase(blockName) || e.getName().equalsIgnoreCase(blockName));
if(edition == null)
return "Unable to find edition or block: " + blockName;
eventCardBlock = Aggregates.random(AdventureEventData.getValidDraftBlocks(List.of(edition)));
if(eventCardBlock == null)
return "Unable to find a valid event block that exclusively contains edition " + edition.getName();
}
AdventureEventController.EventFormat eventFormat = s.length > 1 ? AdventureEventController.EventFormat.smartValueOf(s[1])
: eventCardBlock.getName().contains("Jumpstart") ? AdventureEventController.EventFormat.Jumpstart : AdventureEventController.EventFormat.Draft;
if(eventFormat == null)
return "Unknown event format: " + s[1];
InnScene.replaceLocalEvent(eventFormat, eventCardBlock);
return "Replaced local event with " + eventFormat.name() + " - " + eventCardBlock.getName();
});
} }
} }

View File

@@ -260,6 +260,7 @@ public class MapStage extends GameStage {
spawnClassified.clear(); spawnClassified.clear();
sourceMapMatch.clear(); sourceMapMatch.clear();
enemies.clear(); enemies.clear();
localInnID = -1;
for (MapLayer layer : map.getLayers()) { for (MapLayer layer : map.getLayers()) {
if (layer.getProperties().containsKey("spriteLayer") && layer.getProperties().get("spriteLayer", boolean.class)) { if (layer.getProperties().containsKey("spriteLayer") && layer.getProperties().get("spriteLayer", boolean.class)) {
spriteLayer = layer; spriteLayer = layer;
@@ -578,6 +579,7 @@ public class MapStage extends GameStage {
//TODO: Ability to move them (using a sequence such as "UULU" for up, up, left, up). //TODO: Ability to move them (using a sequence such as "UULU" for up, up, left, up).
break; break;
case "inn": case "inn":
localInnID = id;
addMapActor(obj, new OnCollide(() -> Forge.switchScene(InnScene.instance(TileMapScene.instance(), TileMapScene.instance().rootPoint.getID(), changes, id)))); addMapActor(obj, new OnCollide(() -> Forge.switchScene(InnScene.instance(TileMapScene.instance(), TileMapScene.instance().rootPoint.getID(), changes, id))));
break; break;
case "spellsmith": case "spellsmith":
@@ -750,6 +752,14 @@ public class MapStage extends GameStage {
} }
} }
//We could track MapObject IDs more generally but for now this is the only one we might need.
private int localInnID = -1;
public InnScene findLocalInn() {
if(localInnID == -1)
return null;
return InnScene.instance(TileMapScene.instance(), TileMapScene.instance().rootPoint.getID(), changes, localInnID);
}
public boolean exitDungeon(boolean defeated, boolean defeatedByBoss) { public boolean exitDungeon(boolean defeated, boolean defeatedByBoss) {
AdventureQuestController.instance().updateQuestsLeave(); AdventureQuestController.instance().updateQuestsLeave();
clearIsInMap(); clearIsInMap();

View File

@@ -4,7 +4,6 @@ import forge.StaticData;
import forge.adventure.data.AdventureEventData; import forge.adventure.data.AdventureEventData;
import forge.adventure.player.AdventurePlayer; import forge.adventure.player.AdventurePlayer;
import forge.adventure.pointofintrest.PointOfInterestChanges; import forge.adventure.pointofintrest.PointOfInterestChanges;
import forge.card.CardEdition;
import forge.deck.Deck; import forge.deck.Deck;
import forge.item.BoosterPack; import forge.item.BoosterPack;
import forge.item.PaperCard; import forge.item.PaperCard;
@@ -29,7 +28,13 @@ public class AdventureEventController implements Serializable {
Draft, Draft,
Sealed, Sealed,
Jumpstart, Jumpstart,
Constructed Constructed;
public static EventFormat smartValueOf(String name) {
return Arrays.stream(EventFormat.values())
.filter(e -> e.name().equalsIgnoreCase(name))
.findFirst().orElse(null);
}
} }
public enum EventStyle { public enum EventStyle {
@@ -67,27 +72,16 @@ public class AdventureEventController implements Serializable {
object = null; object = null;
} }
public AdventureEventData createEvent(EventStyle style, String pointID, int eventOrigin, PointOfInterestChanges changes) { public AdventureEventData createEvent(String pointID) {
if (nextEventDate.containsKey(pointID) && nextEventDate.get(pointID) >= LocalDate.now().toEpochDay()) { if (nextEventDate.containsKey(pointID) && nextEventDate.get(pointID) >= LocalDate.now().toEpochDay()) {
// No event currently available here // No event currently available here
return null; return null;
} }
long eventSeed; long eventSeed = getEventSeed(pointID);
long timeSeed = LocalDate.now().toEpochDay();
long placeSeed = Long.parseLong(pointID.replaceAll("[^0-9]", ""));
long room = Long.MAX_VALUE - placeSeed;
if (timeSeed > room) {
//ensuring we don't ever hit an overflow
eventSeed = Long.MIN_VALUE + timeSeed - room;
} else {
eventSeed = timeSeed + placeSeed;
}
Random random = new Random(eventSeed); Random random = new Random(eventSeed);
AdventureEventData e; AdventureEventData e;
// After a certain number of wins, stop offering Jumpstart events // After a certain number of wins, stop offering Jumpstart events
if (Current.player().getStatistic().totalWins() < 10 && if (Current.player().getStatistic().totalWins() < 10 &&
random.nextInt(10) <= 2) { random.nextInt(10) <= 2) {
@@ -100,15 +94,34 @@ public class AdventureEventController implements Serializable {
//covers cases where (somehow) editions that do not match the event style have been picked up //covers cases where (somehow) editions that do not match the event style have been picked up
return null; return null;
} }
return e;
}
// If the chosen event seed recommends a four-person pod, run it as a RoundRobin public AdventureEventData createEvent(EventFormat format, CardBlock cardBlock, String pointID) {
// Set can be null when it is only a meta set such as some Jumpstart events. long eventSeed = getEventSeed(pointID);
CardEdition firstSet = e.cardBlock.getSets().isEmpty() ? null : e.cardBlock.getSets().get(0); AdventureEventData e = new AdventureEventData(eventSeed, format, cardBlock);
int podSize = firstSet == null ? 8 : firstSet.getDraftOptions().getRecommendedPodSize(); if(e.cardBlock == null)
return null;
return e;
}
private static long getEventSeed(String pointID) {
long eventSeed;
long timeSeed = LocalDate.now().toEpochDay();
long placeSeed = Long.parseLong(pointID.replaceAll("[^0-9]", ""));
long room = Long.MAX_VALUE - placeSeed;
if (timeSeed > room) {
//ensuring we don't ever hit an overflow
eventSeed = Long.MIN_VALUE + timeSeed - room;
} else {
eventSeed = timeSeed + placeSeed;
}
return eventSeed;
}
public void initializeEvent(AdventureEventData e, String pointID, int eventOrigin, PointOfInterestChanges changes) {
e.sourceID = pointID; e.sourceID = pointID;
e.eventOrigin = eventOrigin; e.eventOrigin = eventOrigin;
e.style = podSize == 4 ? EventStyle.RoundRobin : style;
AdventureEventData.PairingStyle pairingStyle; AdventureEventData.PairingStyle pairingStyle;
if (e.style == EventStyle.RoundRobin) { if (e.style == EventStyle.RoundRobin) {
@@ -118,21 +131,11 @@ public class AdventureEventController implements Serializable {
} }
e.eventRules = new AdventureEventData.AdventureEventRules(e.format, pairingStyle, changes == null ? 1f : changes.getTownPriceModifier()); e.eventRules = new AdventureEventData.AdventureEventRules(e.format, pairingStyle, changes == null ? 1f : changes.getTownPriceModifier());
e.generateParticipants(podSize - 1); //-1 to account for the player
switch (e.style) { e.generateParticipants();
case Swiss:
case Bracket:
e.rounds = (e.participants.length / 2) - 1;
break;
case RoundRobin:
e.rounds = e.participants.length - 1;
break;
}
AdventurePlayer.current().addEvent(e); AdventurePlayer.current().addEvent(e);
nextEventDate.put(pointID, LocalDate.now().toEpochDay() + new Random().nextInt(2)); //next local event availability date nextEventDate.put(pointID, LocalDate.now().toEpochDay() + new Random().nextInt(2)); //next local event availability date
return e;
} }
public Deck generateBooster(String setCode) { public Deck generateBooster(String setCode) {
@@ -144,7 +147,6 @@ public class AdventureEventController implements Serializable {
output.setComment(setCode); output.setComment(setCode);
return output; return output;
} }
public Deck generateBoosterByColor(String color) { public Deck generateBoosterByColor(String color) {
List<PaperCard> cards = BoosterPack.fromColor(color).getCards(); List<PaperCard> cards = BoosterPack.fromColor(color).getCards();
Deck output = new Deck(); Deck output = new Deck();