From 0e67596ca03acd302905ff9aefab4ba7e6b50415 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Wed, 21 Aug 2013 14:26:10 -0400 Subject: [PATCH 1/3] Progress on Implementing Dungeon Packs 1. Integrated support for dungeon pack config options into the code (i.e. we actually DO what the settings specify) 2. Added random transitions from one dungeon type to another. Dungeons might also begin with a non-default pack. 3. Fixed a config reading bug that caused settings to be ignored and some invalid settings wouldn't trigger exceptions. Also fixed other dungeon pack bugs. --- .../mod_pocketDim/SchematicLoader.java | 39 ++++- .../commands/CommandExportDungeon.java | 5 +- .../dungeon/DungeonSchematic.java | 27 +++- .../dungeon/pack/DungeonChainRule.java | 25 +++- .../pack/DungeonChainRuleDefinition.java | 19 +++ .../dungeon/pack/DungeonPack.java | 9 +- .../dungeon/pack/DungeonPackConfig.java | 8 +- .../dungeon/pack/DungeonPackConfigReader.java | 31 +++- .../mod_pocketDim/helpers/DungeonHelper.java | 141 ++++++++++++++++-- 9 files changed, 263 insertions(+), 41 deletions(-) diff --git a/StevenDimDoors/mod_pocketDim/SchematicLoader.java b/StevenDimDoors/mod_pocketDim/SchematicLoader.java index f5006f4..7f4101c 100644 --- a/StevenDimDoors/mod_pocketDim/SchematicLoader.java +++ b/StevenDimDoors/mod_pocketDim/SchematicLoader.java @@ -6,6 +6,8 @@ import java.util.Random; import net.minecraft.world.World; import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic; +import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPack; +import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPackConfig; import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper; import StevenDimDoors.mod_pocketDim.helpers.dimHelper; import StevenDimDoors.mod_pocketDim.schematic.InvalidSchematicException; @@ -16,8 +18,6 @@ public class SchematicLoader public static boolean generateDungeonPocket(LinkData link, DDProperties properties) { - //TODO: Phase this function out in the next update. ~SenseiKiwi - if (link == null || properties == null) { return false; @@ -46,7 +46,7 @@ public class SchematicLoader final long localSeed = world.getSeed() ^ 0x2F50DB9B4A8057E4L ^ computeDestinationHash(link); final Random random = new Random(localSeed); - dungeonHelper.generateDungeonLink(link, dungeonHelper.RuinsPack, random); + dungeonHelper.generateDungeonLink(link, getDimDungeonPack(originDimID), random); } schematicPath = dimList.get(destDimID).dungeonGenerator.schematicPath; @@ -98,8 +98,11 @@ public class SchematicLoader { dimHelper helperInstance = dimHelper.instance; helperInstance.moveLinkDataDestination(link, link.destXCoord, fixedY, link.destZCoord, link.destDimID, true); - } - dungeon.copyToWorld(world, new Point3D(link.destXCoord, link.destYCoord, link.destZCoord), link.linkOrientation, originDimID, destDimID); + } + DungeonPackConfig packConfig = getDimDungeonPack(destDimID).getConfig(); + + dungeon.copyToWorld(world, new Point3D(link.destXCoord, link.destYCoord, link.destZCoord), + link.linkOrientation, originDimID, destDimID, packConfig.doDistortDoorCoordinates()); return true; } catch (Exception e) @@ -109,6 +112,30 @@ public class SchematicLoader } } + private static DungeonPack getDimDungeonPack(int dimensionID) + { + //FIXME: This function is a workaround to our current dungeon data limitations. Modify later. + //The upcoming save format change and code overhaul will make this obsolete. + + DungeonPack pack; + DungeonGenerator generator = dimHelper.dimList.get(dimensionID).dungeonGenerator; + if (generator != null) + { + pack = generator.getDungeonType().Owner; + + //Make sure the pack isn't null. This can happen for dungeons with the special UNKNOWN type. + if (pack == null) + { + pack = DungeonHelper.instance().RuinsPack; + } + } + else + { + pack = DungeonHelper.instance().RuinsPack; + } + return pack; + } + private static int adjustDestinationY(World world, int y, DungeonSchematic dungeon) { //The goal here is to guarantee that the dungeon fits within the vertical bounds @@ -141,7 +168,7 @@ public class SchematicLoader private static DungeonSchematic checkSourceAndLoad(String schematicPath) throws FileNotFoundException, InvalidSchematicException { - //TODO: Change this code once we introduce an isInternal flag in dungeon data + //FIXME: Change this code once we introduce an isInternal flag in dungeon data DungeonSchematic dungeon; if ((new File(schematicPath)).exists()) { diff --git a/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java b/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java index 194d5ce..f2ee7da 100644 --- a/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java +++ b/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java @@ -105,7 +105,7 @@ public class CommandExportDungeon extends DDCommandBase try { int weight = Integer.parseInt(command[3]); - if (weight >= 0 && weight <= DungeonHelper.MAX_DUNGEON_WEIGHT) + if (weight >= DungeonHelper.MIN_DUNGEON_WEIGHT && weight <= DungeonHelper.MAX_DUNGEON_WEIGHT) { return exportDungeon(sender, join(command, "_", 0, 4)); } @@ -114,7 +114,8 @@ public class CommandExportDungeon extends DDCommandBase } //If we've reached this point, then we must have an invalid weight. - return new DDCommandResult("Invalid dungeon weight. Please specify a weight between 0 and " + DungeonHelper.MAX_DUNGEON_WEIGHT + ", inclusive."); + return new DDCommandResult("Invalid dungeon weight. Please specify a weight between " + + DungeonHelper.MIN_DUNGEON_WEIGHT + " and " + DungeonHelper.MAX_DUNGEON_WEIGHT + ", inclusive."); } return DDCommandResult.SUCCESS; diff --git a/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java b/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java index 8792eae..cae2fb4 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java @@ -167,7 +167,7 @@ public class DungeonSchematic extends Schematic { return new DungeonSchematic(Schematic.copyFromWorld(world, x, y, z, width, height, length, doCompactBounds)); } - public void copyToWorld(World world, Point3D pocketCenter, int dungeonOrientation, int originDimID, int destDimID) + public void copyToWorld(World world, Point3D pocketCenter, int dungeonOrientation, int originDimID, int destDimID, boolean doDistortCoordinates) { //TODO: This function is an improvised solution so we can get the release moving. In the future, //we should generalize block tranformations and implement support for them at the level of Schematic, @@ -222,17 +222,17 @@ public class DungeonSchematic extends Schematic { world.setBlockTileEntity(pocketPoint.getX(), pocketPoint.getY(), pocketPoint.getZ(), TileEntity.createAndLoadEntity(tileTag)); } - setUpDungeon(world, pocketCenter, turnAngle, originDimID, destDimID); + setUpDungeon(world, pocketCenter, turnAngle, originDimID, destDimID, doDistortCoordinates); } - private void setUpDungeon(World world, Point3D pocketCenter, int turnAngle, int originDimID, int destDimID) + private void setUpDungeon(World world, Point3D pocketCenter, int turnAngle, int originDimID, int destDimID, boolean doDistortCoordinates) { //The following Random initialization code is based on code from ChunkProviderGenerate. //It makes our generation depend on the world seed. Random random = new Random(world.getSeed()); long factorA = random.nextLong() / 2L * 2L + 1L; long factorB = random.nextLong() / 2L * 2L + 1L; - random.setSeed((pocketCenter.getX() >> 4) * factorA + (pocketCenter.getZ() >> 4) * factorB ^ world.getSeed()); + random.setSeed(pocketCenter.getX() * factorB + pocketCenter.getZ() * factorA ^ world.getSeed()); //Transform dungeon corners Point3D minCorner = new Point3D(0, 0, 0); @@ -249,7 +249,7 @@ public class DungeonSchematic extends Schematic { //Set up link data for dimensional doors for (Point3D location : dimensionalDoorLocations) { - setUpDimensionalDoorLink(world, location, entranceDoorLocation, turnAngle, pocketCenter, originDimID, destDimID, random); + setUpDimensionalDoorLink(world, location, entranceDoorLocation, turnAngle, pocketCenter, originDimID, destDimID, doDistortCoordinates, random); } //Set up link data for exit door @@ -374,11 +374,22 @@ public class DungeonSchematic extends Schematic { } } - private static void setUpDimensionalDoorLink(World world, Point3D point, Point3D entrance, int rotation, Point3D pocketCenter, int originDimID, int destDimID, Random random) + private static void setUpDimensionalDoorLink(World world, Point3D point, Point3D entrance, int rotation, Point3D pocketCenter, int originDimID, int destDimID, boolean applyNoise, Random random) { int depth = dimHelper.instance.getDimDepth(originDimID) + 1; - int forwardNoise = MathHelper.getRandomIntegerInRange(random, -50 * depth, 150 * depth); - int sidewaysNoise = MathHelper.getRandomIntegerInRange(random, -10 * depth, 10 * depth); + int forwardNoise; + int sidewaysNoise; + + if (applyNoise) + { + forwardNoise = MathHelper.getRandomIntegerInRange(random, -50 * depth, 150 * depth); + sidewaysNoise = MathHelper.getRandomIntegerInRange(random, -10 * depth, 10 * depth); + } + else + { + forwardNoise = 0; + sidewaysNoise = 0; + } //Transform doorLocation to the pocket coordinate system Point3D location = point.clone(); diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java index 17554dc..eacfa9c 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java @@ -11,9 +11,32 @@ public class DungeonChainRule private final ArrayList> products; public DungeonChainRule(DungeonChainRuleDefinition source, HashMap nameToTypeMapping) - { + { ArrayList conditionNames = source.getCondition(); ArrayList> productNames = source.getProducts(); + + //Validate the data, just in case + if (conditionNames == null) + { + throw new NullPointerException("source cannot have null conditions"); + } + if (productNames == null) + { + throw new NullPointerException("source cannot have null products"); + } + if (productNames.isEmpty()) + { + throw new IllegalArgumentException("products cannot be an empty list"); + } + for (WeightedContainer product : productNames) + { + //Check for weights less than 1. Those could cause Minecraft's random selection algorithm to throw an exception. + //At the very least, they're useless values. + if (product.itemWeight < 1) + { + throw new IllegalArgumentException("products cannot contain items with weights less than 1"); + } + } //Obtain the IDs of dungeon types in reverse order. Reverse order makes comparing against chain histories easy. condition = new int[conditionNames.size()]; diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRuleDefinition.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRuleDefinition.java index f06cded..17011e6 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRuleDefinition.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRuleDefinition.java @@ -11,6 +11,25 @@ public class DungeonChainRuleDefinition public DungeonChainRuleDefinition(ArrayList conditions, ArrayList> products) { + //Validate the arguments, just in case + if (conditions == null) + { + throw new NullPointerException("conditions cannot be null"); + } + if (products.isEmpty()) + { + throw new IllegalArgumentException("products cannot be an empty list"); + } + for (WeightedContainer product : products) + { + //Check for weights less than 1. Those could cause Minecraft's random selection algorithm to throw an exception. + //At the very least, they're useless values. + if (product.itemWeight < 1) + { + throw new IllegalArgumentException("products cannot contain items with weights less than 1"); + } + } + this.conditions = conditions; this.products = products; } diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java index 40bc2c7..f9439f2 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java @@ -21,6 +21,8 @@ public class DungeonPack //The ID numbers would be a problem since it couldn't have a valid number, since it wasn't initialized by the pack instance. //FIXME: Do not release this code as an update without dealing with disowned types! + private static final int MAX_HISTORY_LENGTH = 1337; + private final String name; private final HashMap nameToTypeMapping; private final ArrayList> groupedDungeons; @@ -78,6 +80,11 @@ public class DungeonPack return name; } + public DungeonPackConfig getConfig() + { + return config.clone(); + } + public boolean isEmpty() { return allDungeons.isEmpty(); @@ -128,7 +135,7 @@ public class DungeonPack //of the longest rule we have. Getting any more data would be useless. This optimization could be significant //for dungeon packs that can extend arbitrarily deep. We should probably set a reasonable limit anyway. - int maxSearchLength = config.allowDuplicatesInChain() ? maxRuleLength : 1337; + int maxSearchLength = config.allowDuplicatesInChain() ? maxRuleLength : MAX_HISTORY_LENGTH; ArrayList history = DungeonHelper.getDungeonChainHistory( dimHelper.instance.getDimData(inbound.locDimID), this, maxSearchLength); return getNextDungeon(history, random); diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java index 842c94c..0074545 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java @@ -18,14 +18,14 @@ public class DungeonPackConfig @SuppressWarnings("unchecked") private DungeonPackConfig(DungeonPackConfig source) { - this.name = source.name; - this.typeNames = (ArrayList) source.typeNames.clone(); + this.name = (source.name != null) ? source.name : null; + this.typeNames = (source.typeNames != null) ? (ArrayList) source.typeNames.clone() : null; this.allowDuplicatesInChain = source.allowDuplicatesInChain; this.allowPackChangeIn = source.allowPackChangeIn; this.allowPackChangeOut = source.allowPackChangeOut; this.distortDoorCoordinates = source.distortDoorCoordinates; this.packWeight = source.packWeight; - this.rules = (ArrayList) source.rules.clone(); + this.rules = (source.rules != null) ? (ArrayList) source.rules.clone() : null; } public void validate() @@ -114,7 +114,7 @@ public class DungeonPackConfig this.packWeight = packWeight; } - public boolean getDistortDoorCoordinates() + public boolean doDistortDoorCoordinates() { return distortDoorCoordinates; } diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfigReader.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfigReader.java index 8aac967..a4aae61 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfigReader.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfigReader.java @@ -31,9 +31,13 @@ public class DungeonPackConfigReader extends BaseConfigurationProcessor= 0 && weight <= MAX_DUNGEON_PACK_WEIGHT) + if (weight >= MIN_DUNGEON_PACK_WEIGHT && weight <= MAX_DUNGEON_PACK_WEIGHT) { config.setPackWeight(weight); } @@ -267,6 +271,10 @@ public class DungeonPackConfigReader extends BaseConfigurationProcessor MAX_CONDITION_LENGTH) + { + throw new ConfigurationProcessingException("The dungeon pack config has a rule condition that is too long: " + definition); + } } for (String product : WHITESPACE_SPLITTER.split(ruleProduct)) @@ -337,7 +350,7 @@ public class DungeonPackConfigReader extends BaseConfigurationProcessor 1) { weight = Ints.tryParse(productParts[1]); - if (weight == null || (weight > MAX_PRODUCT_WEIGHT) || (weight < 0)) + if (weight == null || (weight > MAX_PRODUCT_WEIGHT) || (weight < MIN_PRODUCT_WEIGHT)) { throw new ConfigurationProcessingException("The dungeon pack config has a rule with an invalid product weight: " + product); } @@ -352,7 +365,17 @@ public class DungeonPackConfigReader extends BaseConfigurationProcessor MAX_PRODUCT_COUNT) + { + throw new ConfigurationProcessingException("The dungeon pack config has a rule with too many products: " + definition); + } } + if (products.isEmpty()) + { + throw new ConfigurationProcessingException("The dungeon pack config has a rule with no products: " + definition); + } + config.getRules().add( new DungeonChainRuleDefinition(condition, products) ); } } diff --git a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java index 4a4b9a4..81e06e4 100644 --- a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java +++ b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java @@ -2,11 +2,13 @@ package StevenDimDoors.mod_pocketDim.helpers; import java.io.BufferedReader; import java.io.File; +import java.io.FileNotFoundException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -14,6 +16,7 @@ import java.util.Queue; import java.util.Random; import java.util.regex.Pattern; +import net.minecraft.util.WeightedRandom; import net.minecraft.world.World; import StevenDimDoors.mod_pocketDim.DDProperties; import StevenDimDoors.mod_pocketDim.DimData; @@ -27,6 +30,7 @@ import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPackConfigReader; import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonType; import StevenDimDoors.mod_pocketDim.items.itemDimDoor; import StevenDimDoors.mod_pocketDim.util.ConfigurationProcessingException; +import StevenDimDoors.mod_pocketDim.util.WeightedContainer; public class DungeonHelper { @@ -42,8 +46,18 @@ public class DungeonHelper private static final String BUNDLED_DUNGEONS_LIST_PATH = "/schematics/schematics.txt"; private static final String DUNGEON_CREATION_GUIDE_SOURCE_PATH = "/mods/DimDoors/text/How_to_add_dungeons.txt"; + private static final String RUINS_PACK_PATH = "/schematics/ruins/rules.txt"; + public static final String SCHEMATIC_FILE_EXTENSION = ".schematic"; + + + private static final int MIN_PACK_SWITCH_CHANCE = 0; + private static final int PACK_SWITCH_CHANCE_PER_LEVEL = 1; + private static final int MAX_PACK_SWITCH_CHANCE = 500; + private static final int START_PACK_SWITCH_CHANCE = MAX_PACK_SWITCH_CHANCE / 9; + private static final int DEFAULT_DUNGEON_WEIGHT = 100; + public static final int MIN_DUNGEON_WEIGHT = 1; //Prevents MC's random selection algorithm from throwing an exception public static final int MAX_DUNGEON_WEIGHT = 10000; //Used to prevent overflows and math breaking down private static final int MAX_EXPORT_RADIUS = 50; public static final short MAX_DUNGEON_WIDTH = 2 * MAX_EXPORT_RADIUS + 1; @@ -54,6 +68,8 @@ public class DungeonHelper private ArrayList registeredDungeons = new ArrayList(); public DungeonPack RuinsPack; + private HashMap dungeonPackMapping = new HashMap(); + private ArrayList dungeonPackList = new ArrayList(); private DungeonGenerator defaultUp; private DungeonGenerator defaultDown; @@ -101,31 +117,45 @@ public class DungeonHelper copyfile.copyFile(DUNGEON_CREATION_GUIDE_SOURCE_PATH, file.getAbsolutePath() + "/How_to_add_dungeons.txt"); } - RuinsPack = new DungeonPack(createRuinsConfig()); + //TODO: Write up a dungeon pack loading function that loads the whole pack and infers its name from the path + DungeonPackConfigReader reader = new DungeonPackConfigReader(); + RuinsPack = new DungeonPack(loadDungeonPackConfig(reader, RUINS_PACK_PATH, "ruins", true)); + dungeonPackMapping.put("ruins", RuinsPack); + dungeonPackList.add(RuinsPack); registerBundledDungeons(); registerCustomDungeons(properties.CustomSchematicDirectory); } - private static DungeonPackConfig createRuinsConfig() + private static DungeonPackConfig loadDungeonPackConfig(DungeonPackConfigReader reader, String configPath, String name, boolean isInternal) { - //This is a temporarily function for testing dungeon packs. - //It'll be removed later when we read dungeon configurations from files. - - DungeonPackConfig config; try { - config = (new DungeonPackConfigReader()).readFromResource("/schematics/ruins/rules.txt"); - config.setName("ruins"); + DungeonPackConfig config; + if (isInternal) + { + config = reader.readFromResource(configPath); + } + else + { + config = reader.readFromFile(configPath); + } + config.setName(name); return config; } catch (ConfigurationProcessingException e) { - //FIXME TEMPORARY DEBUG PRINT, DO SOMETHING BETTER HERE - System.err.println("OH GOD SOMETHING WENT WRONG WITH THE DEFAULT DUNGEON PACK CONFIG"); - e.printStackTrace(); - return null; + System.err.println(e.getMessage()); + if (e.getCause() != null) + { + System.err.println(e.getCause()); + } } + catch (FileNotFoundException e) + { + System.err.println("Could not find a dungeon pack config file: " + configPath); + } + return null; } public List getRegisteredDungeons() @@ -202,7 +232,7 @@ public class DungeonHelper try { int weight = Integer.parseInt(dungeonData[3]); - if (weight < 0 || weight > MAX_DUNGEON_WEIGHT) + if (weight < MIN_DUNGEON_WEIGHT || weight > MAX_DUNGEON_WEIGHT) return false; } catch (NumberFormatException e) @@ -336,10 +366,36 @@ public class DungeonHelper public void generateDungeonLink(LinkData inbound, DungeonPack pack, Random random) { DungeonGenerator selection; + DungeonPackConfig config; + DungeonPack selectedPack; try - { - selection = pack.getNextDungeon(inbound, random); + { + config = pack.getConfig(); + selectedPack = pack; + + //Do we want to switch to another dungeon pack? + if (config.allowPackChangeOut()) + { + //Calculate the chance of switching to a different pack type + int packSwitchChance; + if (dimHelper.dimList.get(inbound.locDimID).depth == 0) + { + packSwitchChance = START_PACK_SWITCH_CHANCE; + } + else + { + packSwitchChance = MIN_PACK_SWITCH_CHANCE + (getPackDepth(inbound, pack) - 1) * PACK_SWITCH_CHANCE_PER_LEVEL; + } + + //Decide randomly whether to switch packs or not + if (random.nextInt(MAX_PACK_SWITCH_CHANCE) < packSwitchChance) + { + //Find another pack + selectedPack = getRandomDungeonPack(pack, random); + } + } + selection = selectedPack.getNextDungeon(inbound, random); } catch (Exception e) { @@ -358,6 +414,29 @@ public class DungeonHelper dimHelper.instance.getDimData(inbound.destDimID).dungeonGenerator = selection; } + @SuppressWarnings("unchecked") + private DungeonPack getRandomDungeonPack(DungeonPack current, Random random) + { + DungeonPack selection = current; + ArrayList> packs = new ArrayList>(dungeonPackList.size()); + + //Load up a list of weighted items with any usable dungeon packs that is not the current pack + for (DungeonPack pack : dungeonPackList) + { + DungeonPackConfig config = pack.getConfig(); + if (pack != current && config.allowPackChangeIn() && !pack.isEmpty()) + { + packs.add(new WeightedContainer(pack, config.getPackWeight())); + } + } + if (!packs.isEmpty()) + { + //Pick a random dungeon pack + selection = ((WeightedContainer) WeightedRandom.getRandomItem(random, packs)).getData(); + } + return selection; + } + public Collection getDungeonNames() { //Use a HashSet to guarantee that all dungeon names will be distinct. @@ -426,6 +505,38 @@ public class DungeonHelper return history; } + private static int getPackDepth(LinkData inbound, DungeonPack pack) + { + //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 + //Dimensions should also just keep track of pack depth internally. + + int packDepth = 1; + DimData tailDim = dimHelper.dimList.get(inbound.destDimID); + boolean found; + + do + { + found = false; + 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) + { + tailDim = neighbor; + found = true; + packDepth++; + break; + } + } + } + while (found); + + return packDepth; + } + 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 From f1bfac3e161e42c18508fec4575fd2c92074d399 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Wed, 21 Aug 2013 14:49:41 -0400 Subject: [PATCH 2/3] Minor Change Moved the getDimDungeonPack() function from SchematicLoader to DungeonHelper so that DungeonHelper.RuinsPack could be a private variable. --- .../mod_pocketDim/SchematicLoader.java | 29 ++----------------- .../mod_pocketDim/helpers/DungeonHelper.java | 27 +++++++++++++++-- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/StevenDimDoors/mod_pocketDim/SchematicLoader.java b/StevenDimDoors/mod_pocketDim/SchematicLoader.java index 7f4101c..3fce739 100644 --- a/StevenDimDoors/mod_pocketDim/SchematicLoader.java +++ b/StevenDimDoors/mod_pocketDim/SchematicLoader.java @@ -6,7 +6,6 @@ import java.util.Random; import net.minecraft.world.World; import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic; -import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPack; import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPackConfig; import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper; import StevenDimDoors.mod_pocketDim.helpers.dimHelper; @@ -46,7 +45,7 @@ public class SchematicLoader final long localSeed = world.getSeed() ^ 0x2F50DB9B4A8057E4L ^ computeDestinationHash(link); final Random random = new Random(localSeed); - dungeonHelper.generateDungeonLink(link, getDimDungeonPack(originDimID), random); + dungeonHelper.generateDungeonLink(link, dungeonHelper.getDimDungeonPack(originDimID), random); } schematicPath = dimList.get(destDimID).dungeonGenerator.schematicPath; @@ -99,7 +98,7 @@ public class SchematicLoader dimHelper helperInstance = dimHelper.instance; helperInstance.moveLinkDataDestination(link, link.destXCoord, fixedY, link.destZCoord, link.destDimID, true); } - DungeonPackConfig packConfig = getDimDungeonPack(destDimID).getConfig(); + DungeonPackConfig packConfig = dungeonHelper.getDimDungeonPack(destDimID).getConfig(); dungeon.copyToWorld(world, new Point3D(link.destXCoord, link.destYCoord, link.destZCoord), link.linkOrientation, originDimID, destDimID, packConfig.doDistortDoorCoordinates()); @@ -111,30 +110,6 @@ public class SchematicLoader return false; } } - - private static DungeonPack getDimDungeonPack(int dimensionID) - { - //FIXME: This function is a workaround to our current dungeon data limitations. Modify later. - //The upcoming save format change and code overhaul will make this obsolete. - - DungeonPack pack; - DungeonGenerator generator = dimHelper.dimList.get(dimensionID).dungeonGenerator; - if (generator != null) - { - pack = generator.getDungeonType().Owner; - - //Make sure the pack isn't null. This can happen for dungeons with the special UNKNOWN type. - if (pack == null) - { - pack = DungeonHelper.instance().RuinsPack; - } - } - else - { - pack = DungeonHelper.instance().RuinsPack; - } - return pack; - } private static int adjustDestinationY(World world, int y, DungeonSchematic dungeon) { diff --git a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java index 81e06e4..0f4f9f6 100644 --- a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java +++ b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java @@ -50,7 +50,6 @@ public class DungeonHelper public static final String SCHEMATIC_FILE_EXTENSION = ".schematic"; - private static final int MIN_PACK_SWITCH_CHANCE = 0; private static final int PACK_SWITCH_CHANCE_PER_LEVEL = 1; private static final int MAX_PACK_SWITCH_CHANCE = 500; @@ -67,7 +66,7 @@ public class DungeonHelper private ArrayList untaggedDungeons = new ArrayList(); private ArrayList registeredDungeons = new ArrayList(); - public DungeonPack RuinsPack; + private DungeonPack RuinsPack; private HashMap dungeonPackMapping = new HashMap(); private ArrayList dungeonPackList = new ArrayList(); @@ -183,6 +182,30 @@ public class DungeonHelper return defaultDown; } + public DungeonPack getDimDungeonPack(int dimensionID) + { + //FIXME: This function is a workaround to our current dungeon data limitations. Modify later. + //The upcoming save format change and code overhaul will make this obsolete. + + DungeonPack pack; + DungeonGenerator generator = dimHelper.dimList.get(dimensionID).dungeonGenerator; + if (generator != null) + { + pack = generator.getDungeonType().Owner; + + //Make sure the pack isn't null. This can happen for dungeons with the special UNKNOWN type. + if (pack == null) + { + pack = RuinsPack; + } + } + else + { + pack = RuinsPack; + } + return pack; + } + public LinkData createCustomDungeonDoor(World world, int x, int y, int z) { //Create a link above the specified position. Link to a new pocket dimension. From 939ed771a8e90087ae9557c0ebc59ad3016b9626 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Thu, 22 Aug 2013 02:25:26 -0400 Subject: [PATCH 3/3] Completed Dungeon Pack Implementation Added code so that the mod loads dungeon packs stored in the custom dungeons folder. Changed the code for loading bundled dungeons so that they're loaded as dungeon packs. This means dungeon packs are now fully integrated into the mod. The changes were tested and seem to be working perfectly. --- .../mod_pocketDim/DungeonGenerator.java | 12 +- .../mod_pocketDim/SchematicLoader.java | 3 +- .../commands/CommandExportDungeon.java | 4 +- .../mod_pocketDim/helpers/DungeonHelper.java | 209 +++++++++++++++--- schematics/{schematics.txt => ruins.txt} | 0 5 files changed, 184 insertions(+), 44 deletions(-) rename schematics/{schematics.txt => ruins.txt} (100%) diff --git a/StevenDimDoors/mod_pocketDim/DungeonGenerator.java b/StevenDimDoors/mod_pocketDim/DungeonGenerator.java index 6c6ea16..0500e76 100644 --- a/StevenDimDoors/mod_pocketDim/DungeonGenerator.java +++ b/StevenDimDoors/mod_pocketDim/DungeonGenerator.java @@ -4,13 +4,11 @@ import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; -import java.util.Random; +import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPack; import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonType; import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper; -import net.minecraft.world.World; - public class DungeonGenerator implements Serializable { //This static field is hax so that I don't have to add an instance field to DungeonGenerator to support DungeonType. @@ -48,7 +46,13 @@ public class DungeonGenerator implements Serializable { File file = new File(schematicPath); String typeName = file.getName().split("_")[0]; - type = DungeonHelper.instance().RuinsPack.getType(typeName); + String packName = file.getParentFile().getName(); + DungeonPack pack = DungeonHelper.instance().getDungeonPack(packName); + if (pack == null) + { + pack = DungeonHelper.instance().getDungeonPack("ruins"); + } + type = pack.getType(typeName); } catch (Exception e) { } if (type == null) diff --git a/StevenDimDoors/mod_pocketDim/SchematicLoader.java b/StevenDimDoors/mod_pocketDim/SchematicLoader.java index 3fce739..630b21f 100644 --- a/StevenDimDoors/mod_pocketDim/SchematicLoader.java +++ b/StevenDimDoors/mod_pocketDim/SchematicLoader.java @@ -47,8 +47,7 @@ public class SchematicLoader dungeonHelper.generateDungeonLink(link, dungeonHelper.getDimDungeonPack(originDimID), random); } - schematicPath = dimList.get(destDimID).dungeonGenerator.schematicPath; - + schematicPath = dimList.get(destDimID).dungeonGenerator.schematicPath; } else { diff --git a/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java b/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java index f2ee7da..8968d11 100644 --- a/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java +++ b/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java @@ -81,7 +81,7 @@ public class CommandExportDungeon extends DDCommandBase //TODO: This validation should be in DungeonHelper or in another class. We should move it //during the save file format rewrite. ~SenseiKiwi - if (!dungeonHelper.validateDungeonType(command[0])) + if (!dungeonHelper.validateDungeonType(command[0], dungeonHelper.getDungeonPack("ruins"))) { return new DDCommandResult("Error: Invalid dungeon type. Please use one of the existing types."); } @@ -133,7 +133,7 @@ public class CommandExportDungeon extends DDCommandBase if (dungeonHelper.exportDungeon(player.worldObj, x, y, z, exportPath)) { player.sendChatToPlayer("Saved dungeon schematic in " + exportPath); - dungeonHelper.registerDungeon(exportPath, false, true); + dungeonHelper.registerDungeon(exportPath, dungeonHelper.getDungeonPack("ruins"), false, true); return DDCommandResult.SUCCESS; } else diff --git a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java index 0f4f9f6..7151624 100644 --- a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java +++ b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java @@ -2,6 +2,7 @@ package StevenDimDoors.mod_pocketDim.helpers; import java.io.BufferedReader; import java.io.File; +import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.InputStreamReader; @@ -34,21 +35,42 @@ import StevenDimDoors.mod_pocketDim.util.WeightedContainer; public class DungeonHelper { + //TODO: File-handling functionality should be spun off to a helper class later + private static class DirectoryFilter implements FileFilter + { + @Override + public boolean accept(File file) + { + return file.isDirectory(); + } + } + + private static class SchematicFileFilter implements FileFilter + { + @Override + public boolean accept(File file) + { + return file.isFile() && file.getName().endsWith(SCHEMATIC_FILE_EXTENSION); + } + } + private static DungeonHelper instance = null; private static DDProperties properties = null; public static final Pattern SCHEMATIC_NAME_PATTERN = Pattern.compile("[A-Za-z0-9_\\-]+"); public static final Pattern DUNGEON_NAME_PATTERN = Pattern.compile("[A-Za-z0-9\\-]+"); + public static final String SCHEMATIC_FILE_EXTENSION = ".schematic"; + private static final String DEFAULT_UP_SCHEMATIC_PATH = "/schematics/core/simpleStairsUp.schematic"; private static final String DEFAULT_DOWN_SCHEMATIC_PATH = "/schematics/core/simpleStairsDown.schematic"; private static final String DEFAULT_ERROR_SCHEMATIC_PATH = "/schematics/core/somethingBroke.schematic"; - private static final String BUNDLED_DUNGEONS_LIST_PATH = "/schematics/schematics.txt"; private static final String DUNGEON_CREATION_GUIDE_SOURCE_PATH = "/mods/DimDoors/text/How_to_add_dungeons.txt"; - - private static final String RUINS_PACK_PATH = "/schematics/ruins/rules.txt"; + private static final String RUINS_PACK_PATH = "/schematics/ruins"; + private static final String BUNDLED_RUINS_LIST_PATH = "/schematics/ruins.txt"; + private static final String STANDARD_CONFIG_FILE_NAME = "rules.txt"; - public static final String SCHEMATIC_FILE_EXTENSION = ".schematic"; + private static final int NETHER_DIMENSION_ID = -1; private static final int MIN_PACK_SWITCH_CHANCE = 0; private static final int PACK_SWITCH_CHANCE_PER_LEVEL = 1; @@ -58,6 +80,7 @@ public class DungeonHelper private static final int DEFAULT_DUNGEON_WEIGHT = 100; public static final int MIN_DUNGEON_WEIGHT = 1; //Prevents MC's random selection algorithm from throwing an exception public static final int MAX_DUNGEON_WEIGHT = 10000; //Used to prevent overflows and math breaking down + private static final int MAX_EXPORT_RADIUS = 50; public static final short MAX_DUNGEON_WIDTH = 2 * MAX_EXPORT_RADIUS + 1; public static final short MAX_DUNGEON_HEIGHT = MAX_DUNGEON_WIDTH; @@ -116,17 +139,12 @@ public class DungeonHelper copyfile.copyFile(DUNGEON_CREATION_GUIDE_SOURCE_PATH, file.getAbsolutePath() + "/How_to_add_dungeons.txt"); } - //TODO: Write up a dungeon pack loading function that loads the whole pack and infers its name from the path - DungeonPackConfigReader reader = new DungeonPackConfigReader(); - RuinsPack = new DungeonPack(loadDungeonPackConfig(reader, RUINS_PACK_PATH, "ruins", true)); - dungeonPackMapping.put("ruins", RuinsPack); - dungeonPackList.add(RuinsPack); - - registerBundledDungeons(); - registerCustomDungeons(properties.CustomSchematicDirectory); + DungeonPackConfigReader reader = new DungeonPackConfigReader(); + registerBundledDungeons(reader); + registerCustomDungeons(properties.CustomSchematicDirectory, reader); } - private static DungeonPackConfig loadDungeonPackConfig(DungeonPackConfigReader reader, String configPath, String name, boolean isInternal) + private static DungeonPackConfig loadDungeonPackConfig(String configPath, String name, boolean isInternal, DungeonPackConfigReader reader) { try { @@ -157,6 +175,49 @@ public class DungeonHelper return null; } + private void registerDungeonPack(String directory, Iterable schematics, boolean isInternal, boolean verbose, DungeonPackConfigReader reader) + { + //First determine the pack's name and validate it + File packDirectory = new File(directory); + String name = packDirectory.getName().toUpperCase(); + //TODO: ADD VALIDATION HERE? + + //Check for naming conflicts + //That could happen if a user has a custom pack with a name that conflicts with a bundled pack, + //or if a user is running Linux and has two directories with names differing only by capitalization. + + DungeonPack pack = dungeonPackMapping.get(name); + if (pack == null) + { + //Load the pack's configuration file + String configPath = directory + File.separator + STANDARD_CONFIG_FILE_NAME; + DungeonPackConfig config = loadDungeonPackConfig(configPath, name, isInternal, reader); + if (config == null) + { + System.err.println("Could not load config file: " + configPath); + return; + } + + //Register the pack + pack = new DungeonPack(config); + dungeonPackMapping.put(name, pack); + dungeonPackList.add(pack); + } + else + { + //Show a warning that there is a naming conflict but keep going. People can use this to extend + //our built-in packs with custom schematics without tampering with our mod's JAR file. + System.err.println("A dungeon pack has the same name as another pack that has already been loaded: " + directory); + System.err.println("We will try to load its schematics but will not check its config file."); + } + + //Register the dungeons! ^_^ + for (String schematicPath : schematics) + { + registerDungeon(schematicPath, pack, isInternal, verbose); + } + } + public List getRegisteredDungeons() { return Collections.unmodifiableList(this.registeredDungeons); @@ -182,6 +243,12 @@ public class DungeonHelper return defaultDown; } + public DungeonPack getDungeonPack(String name) + { + //TODO: This function might be obsolete after the new save format is implemented. + return dungeonPackMapping.get(name.toUpperCase()); + } + public DungeonPack getDimDungeonPack(int dimensionID) { //FIXME: This function is a workaround to our current dungeon data limitations. Modify later. @@ -201,7 +268,15 @@ public class DungeonHelper } else { - pack = RuinsPack; + if (dimensionID == NETHER_DIMENSION_ID) + { + //TODO: Change this to the nether-side pack later ^_^ + pack = RuinsPack; + } + else + { + pack = RuinsPack; + } } return pack; } @@ -218,13 +293,13 @@ public class DungeonHelper return link; } - public boolean validateDungeonType(String type) + public boolean validateDungeonType(String type, DungeonPack pack) { //Check if the dungeon type is valid - return RuinsPack.isKnownType(type); + return pack.isKnownType(type); } - public boolean validateSchematicName(String name) + public boolean validateSchematicName(String name, DungeonPack pack) { String[] dungeonData; @@ -238,7 +313,7 @@ public class DungeonHelper return false; //Check if the dungeon type is valid - if (!validateDungeonType(dungeonData[0])) + if (!validateDungeonType(dungeonData[0], pack)) return false; //Check if the name is valid @@ -267,7 +342,7 @@ public class DungeonHelper return true; } - public void registerDungeon(String schematicPath, boolean isInternal, boolean verbose) + public void registerDungeon(String schematicPath, DungeonPack pack, boolean isInternal, boolean verbose) { //We use schematicPath as the real path for internal files (inside our JAR) because it seems //that File tries to interpret it as a local drive path and mangles it. @@ -276,19 +351,19 @@ public class DungeonHelper String path = isInternal ? schematicPath : schematicFile.getAbsolutePath(); try { - if (validateSchematicName(name)) + if (validateSchematicName(name, pack)) { //Strip off the file extension while splitting the file name String[] dungeonData = name.substring(0, name.length() - SCHEMATIC_FILE_EXTENSION.length()).split("_"); - DungeonType dungeonType = RuinsPack.getType(dungeonData[0]); + DungeonType dungeonType = pack.getType(dungeonData[0]); boolean isOpen = dungeonData[2].equalsIgnoreCase("open"); int weight = (dungeonData.length == 4) ? Integer.parseInt(dungeonData[3]) : DEFAULT_DUNGEON_WEIGHT; //Add this custom dungeon to the list corresponding to its type DungeonGenerator generator = new DungeonGenerator(weight, path, isOpen, dungeonType); - RuinsPack.addDungeon(generator); + pack.addDungeon(generator); registeredDungeons.add(generator); if (verbose) { @@ -299,37 +374,82 @@ public class DungeonHelper { if (verbose) { - System.out.println("Could not parse dungeon filename, not adding dungeon to generation lists"); + System.out.println("The following dungeon name is invalid for its given pack. It will not be generated naturally: " + schematicPath); } untaggedDungeons.add(new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, path, true, DungeonType.UNKNOWN_TYPE)); System.out.println("Registered untagged dungeon: " + name); } } - catch(Exception e) + catch (Exception e) { System.err.println("Failed to register dungeon: " + name); e.printStackTrace(); } } - private void registerCustomDungeons(String path) + private void registerCustomDungeons(String path, DungeonPackConfigReader reader) { + File[] schematics; + File[] packDirectories; + File[] packFiles; + ArrayList packFilePaths; File directory = new File(path); - File[] schematicNames = directory.listFiles(); + SchematicFileFilter schematicFileFilter = new SchematicFileFilter(); - if (schematicNames != null) + //Check that the Ruins pack has been loaded + if (RuinsPack == null) { - for (File schematicFile: schematicNames) + throw new IllegalStateException("Cannot register custom dungeons without first loading the Ruins dungeon pack."); + } + + //Load stray dungeons directly in the custom dungeons folder + schematics = directory.listFiles(schematicFileFilter); + if (schematics != null) + { + for (File schematicFile : schematics) { - if (schematicFile.getName().endsWith(SCHEMATIC_FILE_EXTENSION)) + registerDungeon(schematicFile.getPath(), RuinsPack, false, true); + } + } + else + { + System.err.println("Could not retrieve the list of schematics stored in the custom dungeons directory!"); + } + schematics = null; //Release memory + + //Load the custom dungeon packs + packDirectories = directory.listFiles(new DirectoryFilter()); + if (packDirectories != null) + { + //Loop through each directory, which is assumed to be a dungeon pack + for (File packDirectory : packDirectories) + { + //List the schematics within the dungeon pack directory + packFiles = packDirectory.listFiles(schematicFileFilter); + if (packFiles != null) { - registerDungeon(schematicFile.getPath(), false, true); + //Copy the pack files' paths into an ArrayList for use with registerDungeonPack() + packFilePaths = new ArrayList(packFiles.length); + for (File packFile : packFiles) + { + packFilePaths.add(packFile.getPath()); + } + + registerDungeonPack(packDirectory.getAbsolutePath(), packFilePaths, false, true, reader); + } + else + { + System.err.println("Could not retrieve the list of schematics in a dungeon pack: " + packDirectory.getPath()); } } } + else + { + System.err.println("Could not retrieve the list of dungeon pack directories in the custom dungeons directory!"); + } } - private void registerBundledDungeons() + private void registerBundledDungeons(DungeonPackConfigReader reader) { //Register the core schematics //These are used for debugging and in case of unusual errors while loading dungeons @@ -338,31 +458,46 @@ public class DungeonHelper defaultError = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, DEFAULT_ERROR_SCHEMATIC_PATH, true, DungeonType.UNKNOWN_TYPE); //Open the list of dungeons packaged with our mod and register their schematics - InputStream listStream = this.getClass().getResourceAsStream(BUNDLED_DUNGEONS_LIST_PATH); + registerBundledPack(BUNDLED_RUINS_LIST_PATH, RUINS_PACK_PATH, "Ruins", reader); + RuinsPack = getDungeonPack("Ruins"); + + System.out.println("Finished registering bundled dungeon packs"); + } + + private void registerBundledPack(String listPath, String packPath, String name, DungeonPackConfigReader reader) + { + System.out.println("Registering bundled dungeon pack: " + name); + + InputStream listStream = this.getClass().getResourceAsStream(listPath); if (listStream == null) { - System.err.println("Unable to open list of bundled dungeon schematics."); + System.err.println("Unable to open list of bundled dungeon schematics for " + name); return; } try { + //Read the list of schematics that come with a bundled pack BufferedReader listReader = new BufferedReader(new InputStreamReader(listStream)); + ArrayList schematics = new ArrayList(); String schematicPath = listReader.readLine(); while (schematicPath != null) { schematicPath = schematicPath.trim(); if (!schematicPath.isEmpty()) { - registerDungeon(schematicPath, true, false); + schematics.add(schematicPath); } schematicPath = listReader.readLine(); } listReader.close(); + + //Register the pack + registerDungeonPack(packPath, schematics, true, false, reader); } catch (Exception e) { - System.err.println("An exception occurred while reading the list of bundled dungeon schematics."); + System.err.println("An exception occurred while reading the list of bundled dungeon schematics for " + name); e.printStackTrace(); } } @@ -397,7 +532,7 @@ public class DungeonHelper config = pack.getConfig(); selectedPack = pack; - //Do we want to switch to another dungeon pack? + //Are we allowed to switch to another dungeon pack? if (config.allowPackChangeOut()) { //Calculate the chance of switching to a different pack type @@ -418,6 +553,8 @@ public class DungeonHelper selectedPack = getRandomDungeonPack(pack, random); } } + + //Pick the next dungeon selection = selectedPack.getNextDungeon(inbound, random); } catch (Exception e) diff --git a/schematics/schematics.txt b/schematics/ruins.txt similarity index 100% rename from schematics/schematics.txt rename to schematics/ruins.txt