From f372b9ccb59cb171da2bbd63186f72648f92e146 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Mon, 5 Aug 2013 20:16:45 -0400 Subject: [PATCH] Basic Configurable Dungeon Chains Completed a basic version of configurable dungeon chains. Almost all of the final funcionality is present. However, the configuration is hardcoded at the moment, not read from a file. This was done for testing purposes. I'll add reading from config files soon. Dungeon packs are partially implemented. Built-in and custom dungeons are currently thrown into the default pack, Ruins. The next step is to generalize the dungeon registration code in DungeonHelper so that we can detect dungeon packs, read their config files, and register dungeons with their corresponding pack. dd-export will need to support packs as well. dd-rift will have issues dealing with duplicate dungeon names across packs, but this isn't a major concern and can be dealt with in the long term. --- .../mod_pocketDim/SchematicLoader.java | 13 ++-- .../dungeon/pack/DungeonChainRule.java | 7 +- .../dungeon/pack/DungeonPack.java | 4 +- .../mod_pocketDim/helpers/DungeonHelper.java | 78 ++++++++++++++----- .../util/BaseConfigurationProcessor.java | 52 +++++++++++++ .../ConfigurationProcessingException.java | 21 +++++ schematics/ruins/rules.txt | 33 ++++++++ 7 files changed, 180 insertions(+), 28 deletions(-) create mode 100644 StevenDimDoors/mod_pocketDim/util/BaseConfigurationProcessor.java create mode 100644 StevenDimDoors/mod_pocketDim/util/ConfigurationProcessingException.java create mode 100644 schematics/ruins/rules.txt diff --git a/StevenDimDoors/mod_pocketDim/SchematicLoader.java b/StevenDimDoors/mod_pocketDim/SchematicLoader.java index 00d1b6a..b1c796a 100644 --- a/StevenDimDoors/mod_pocketDim/SchematicLoader.java +++ b/StevenDimDoors/mod_pocketDim/SchematicLoader.java @@ -43,12 +43,15 @@ public class SchematicLoader if (dimList.get(destDimID).dungeonGenerator == null) { //The following initialization code is based on code from ChunkProviderGenerate. - //It makes our generation depend on the world seed. + //It makes our generation depend on the world seed. We have an additional seed here + //to prevent correlations between the selected dungeons and the locations of gateways. + //TODO: We should centralize RNG initialization and world-seed modifiers for each specific application. - Random random = new Random(world.getSeed()); - long factorA = random.nextLong() / 2L * 2L + 1L; - long factorB = random.nextLong() / 2L * 2L + 1L; - random.setSeed((link.destXCoord >> 4) * factorA + (link.destZCoord >> 4) * factorB ^ world.getSeed()); + final long localSeed = world.getSeed() ^ 0x2F50DB9B4A8057E4L; + final Random random = new Random(); + final long factorA = random.nextLong() / 2L * 2L + 1L; + final long factorB = random.nextLong() / 2L * 2L + 1L; + random.setSeed((link.destXCoord >> 4) * factorA + (link.destZCoord >> 4) * factorB ^ localSeed); dungeonHelper.generateDungeonLink(link, dungeonHelper.RuinsPack, random); } diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java index 472f20b..17554dc 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java @@ -15,12 +15,13 @@ public class DungeonChainRule ArrayList conditionNames = source.getCondition(); ArrayList> productNames = source.getProducts(); + //Obtain the IDs of dungeon types in reverse order. Reverse order makes comparing against chain histories easy. condition = new int[conditionNames.size()]; - for (int k = 0; k < condition.length; k++) + for (int src = 0, dst = condition.length - 1; src < condition.length; src++, dst--) { - condition[k] = nameToTypeMapping.get(conditionNames.get(k)).ID; + condition[dst] = nameToTypeMapping.get(conditionNames.get(src)).ID; } - products = new ArrayList>(); + products = new ArrayList>(productNames.size()); for (WeightedContainer product : productNames) { products.add(new WeightedContainer(nameToTypeMapping.get(product.getData()), product.itemWeight )); diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java index 9db777d..40bc2c7 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java @@ -162,11 +162,11 @@ public class DungeonPack { excludedDungeons = new HashSet(history); } - + //List which dungeons are allowed ArrayList candidates; ArrayList group = groupedDungeons.get(nextType.ID); - if (excludedDungeons != null) + if (excludedDungeons != null && !excludedDungeons.isEmpty()) { candidates = new ArrayList(group.size()); for (DungeonGenerator dungeon : group) diff --git a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java index c364297..372979d 100644 --- a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java +++ b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java @@ -9,7 +9,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; import java.util.Random; import java.util.regex.Pattern; @@ -423,32 +425,72 @@ public class DungeonHelper //manipulate the output of this function by setting up links to mislead our algorithm or by removing links. //Dimensions MUST have built-in records of their parent dimensions in the future. ~SenseiKiwi - dimHelper helper = dimHelper.instance; ArrayList history = new ArrayList(); - DimData tailDim = helper.getDimData(helper.getLinkDataFromCoords(dimData.exitDimLink.destXCoord, dimData.exitDimLink.destYCoord, dimData.exitDimLink.destZCoord, dimData.exitDimLink.destDimID).destDimID); + DimData tailDim = dimData; + boolean found = true; - for (int count = 0; count < maxSize; count++) + if (dimData.dungeonGenerator == null || dimData.dungeonGenerator.getDungeonType().Owner != pack || maxSize < 1) { - if (tailDim.dungeonGenerator == null || tailDim.dungeonGenerator.getDungeonType().Owner != pack) + //The initial dimension is already outside our pack. Return an empty list. + return history; + } + history.add(dimData.dungeonGenerator); + + for (int count = 1; count < maxSize && found; count++) + { + found = false; + for (LinkData link : tailDim.getLinksInDim()) { - //We've reached a dimension that doesn't belong to our pack. Stop the search here. - break; - } - - history.add(tailDim.dungeonGenerator); - if (count + 1 < maxSize) - { - for (LinkData link : tailDim.getLinksInDim()) + DimData neighbor = dimHelper.instance.getDimData(link.destDimID); + if (neighbor.depth == tailDim.depth - 1 && neighbor.dungeonGenerator != null && + neighbor.dungeonGenerator.getDungeonType().Owner == pack) { - DimData nextDim = dimHelper.instance.getDimData(link.destDimID); - if (helper.getDimDepth(link.destDimID) == tailDim.depth + 1) - { - tailDim = nextDim; - break; - } + tailDim = neighbor; + history.add(tailDim.dungeonGenerator); + found = true; + break; } } } return history; } + + public static ArrayList getFlatDungeonTree(DimData dimData, int maxSize) + { + //TODO: I've improved this code for the time being. However, searching across links is a flawed approach. A player could + //manipulate the output of this function by setting up links to mislead our algorithm or by removing links. + //Dimensions MUST have built-in records of their parent dimensions in the future. ~SenseiKiwi + + dimHelper helper = dimHelper.instance; + ArrayList dungeons = new ArrayList(); + DimData root = helper.getDimData(helper.getLinkDataFromCoords(dimData.exitDimLink.destXCoord, dimData.exitDimLink.destYCoord, dimData.exitDimLink.destZCoord, dimData.exitDimLink.destDimID).destDimID); + HashSet checked = new HashSet(); + Queue pendingDimensions = new LinkedList(); + + if (root.dungeonGenerator == null) + { + return dungeons; + } + pendingDimensions.add(root); + checked.add(root); + + while (dungeons.size() < maxSize && !pendingDimensions.isEmpty()) + { + DimData current = pendingDimensions.remove(); + for (LinkData link : current.getLinksInDim()) + { + DimData child = helper.getDimData(link.destDimID); + if (child.depth == current.depth + 1 && child.dungeonGenerator != null && checked.add(child)) + { + dungeons.add(child.dungeonGenerator); + pendingDimensions.add(child); + } + if (dungeons.size() == maxSize) + { + break; + } + } + } + return dungeons; + } } \ No newline at end of file diff --git a/StevenDimDoors/mod_pocketDim/util/BaseConfigurationProcessor.java b/StevenDimDoors/mod_pocketDim/util/BaseConfigurationProcessor.java new file mode 100644 index 0000000..8fe75a2 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/util/BaseConfigurationProcessor.java @@ -0,0 +1,52 @@ +package StevenDimDoors.mod_pocketDim.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +public abstract class BaseConfigurationProcessor +{ + public BaseConfigurationProcessor() { } + + public boolean canRead() + { + return true; + } + + public boolean canWrite() + { + return true; + } + + public T readFromFile(String path) throws FileNotFoundException, ConfigurationProcessingException + { + return readFromFile(new File(path)); + } + + public T readFromFile(File file) throws FileNotFoundException, ConfigurationProcessingException + { + return readFromStream(new FileInputStream(file)); + } + + public T readFromResource(String resourcePath) throws ConfigurationProcessingException + { + return readFromStream(this.getClass().getResourceAsStream(resourcePath)); + } + + public abstract T readFromStream(InputStream inputStream) throws ConfigurationProcessingException; + + public void writeToFile(File file, T data) throws FileNotFoundException, ConfigurationProcessingException + { + writeToStream(new FileOutputStream(file), data); + } + + public void writeToFile(String path, T data) throws FileNotFoundException, ConfigurationProcessingException + { + writeToFile(new File(path), data); + } + + public abstract void writeToStream(OutputStream outputStream, T data) throws ConfigurationProcessingException; +} diff --git a/StevenDimDoors/mod_pocketDim/util/ConfigurationProcessingException.java b/StevenDimDoors/mod_pocketDim/util/ConfigurationProcessingException.java new file mode 100644 index 0000000..1793f22 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/util/ConfigurationProcessingException.java @@ -0,0 +1,21 @@ +package StevenDimDoors.mod_pocketDim.util; + +public class ConfigurationProcessingException extends Exception +{ + private static final long serialVersionUID = -4525298050874891911L; + + public ConfigurationProcessingException() + { + super(); + } + + public ConfigurationProcessingException(String message) + { + super(message); + } + + public ConfigurationProcessingException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/schematics/ruins/rules.txt b/schematics/ruins/rules.txt new file mode 100644 index 0000000..086036a --- /dev/null +++ b/schematics/ruins/rules.txt @@ -0,0 +1,33 @@ +Version 1 +Types: +Hub +Trap +SimpleHall +ComplexHall +Exit +DeadEnd +Maze + +Settings: +AllowRepetitionsInBranch = false +AllowPackChangeOut = true +AllowPackChangeIn = true +PackWeight = 100 + +Rules: + +Exit -> DeadEnd Exit + +DeadEnd -> DeadEnd Exit + +? ? ? ? ? ? ? ? -> Trap#20 SimpleHall#40 ComplexHall#10 Exit#20 DeadEnd#10 + +? ? ? ? -> Trap#18 SimpleHall#40 ComplexHall#10 Exit#18 DeadEnd#10 Hub#4 + +? ? ? -> ComplexHall Hub Trap SimpleHall Maze + +? ? -> ComplexHall Hub Trap SimpleHall Maze + +? -> ComplexHall#40 Hub#30 Trap#10 SimpleHall#10 Maze#10 + +-> ComplexHall#40 Hub#30 Trap#10 SimpleHall#10 Maze#10 \ No newline at end of file