diff --git a/StevenDimDoors/mod_pocketDim/SchematicLoader.java b/StevenDimDoors/mod_pocketDim/SchematicLoader.java index f5006f4d..7f4101c3 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 194d5ce1..f2ee7dab 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 8792eaea..cae2fb49 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 17554dcd..eacfa9c6 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<WeightedContainer<DungeonType>> products; public DungeonChainRule(DungeonChainRuleDefinition source, HashMap<String, DungeonType> nameToTypeMapping) - { + { ArrayList<String> conditionNames = source.getCondition(); ArrayList<WeightedContainer<String>> 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<String> 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 f06cdedc..17011e6d 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<String> conditions, ArrayList<WeightedContainer<String>> 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<String> 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 40bc2c77..f9439f27 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<String, DungeonType> nameToTypeMapping; private final ArrayList<ArrayList<DungeonGenerator>> 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<DungeonGenerator> 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 842c94ce..00745450 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<String>) source.typeNames.clone(); + this.name = (source.name != null) ? source.name : null; + this.typeNames = (source.typeNames != null) ? (ArrayList<String>) 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<DungeonChainRuleDefinition>) source.rules.clone(); + this.rules = (source.rules != null) ? (ArrayList<DungeonChainRuleDefinition>) 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 8aac9674..a4aae616 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<DungeonP private final int CONFIG_VERSION = 1; private final int LOOKAHEAD_LIMIT = 1024; private final int MAX_PRODUCT_WEIGHT = 10000; + private final int MIN_PRODUCT_WEIGHT = 1; private final int DEFAULT_PRODUCT_WEIGHT = 100; private final int MAX_DUNGEON_PACK_WEIGHT = 10000; + private final int MIN_DUNGEON_PACK_WEIGHT = 1; private final int DEFAULT_DUNGEON_PACK_WEIGHT = 100; + private final int MAX_CONDITION_LENGTH = 20; + private final int MAX_PRODUCT_COUNT = MAX_CONDITION_LENGTH; private final String COMMENT_MARKER = "##"; private final Pattern DUNGEON_TYPE_PATTERN = Pattern.compile("[A-Za-z0-9_\\-]{1,20}"); @@ -237,8 +241,8 @@ public class DungeonPackConfigReader extends BaseConfigurationProcessor<DungeonP { try { - String name = settingParts[0]; - String value = settingParts[1]; + String name = settingParts[0].trim(); + String value = settingParts[1].trim(); if (name.equalsIgnoreCase("AllowDuplicatesInChain")) { config.setAllowDuplicatesInChain(parseBoolean(value)); @@ -258,7 +262,7 @@ public class DungeonPackConfigReader extends BaseConfigurationProcessor<DungeonP else if (name.equalsIgnoreCase("PackWeight")) { int weight = Integer.parseInt(value); - if (weight >= 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<DungeonP valid = false; } } + else + { + valid = false; + } } catch (Exception e) { @@ -318,6 +326,11 @@ public class DungeonPackConfigReader extends BaseConfigurationProcessor<DungeonP { throw new ConfigurationProcessingException("The dungeon pack config has a rule condition with an unknown dungeon type: " + typeName); } + + if (condition.size() > 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<DungeonP if (productParts.length > 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<DungeonP { throw new ConfigurationProcessingException("The dungeon pack config has an unknown dungeon type in a rule: " + typeName); } + + if (products.size() > 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 4a4b9a49..81e06e40 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<DungeonGenerator> registeredDungeons = new ArrayList<DungeonGenerator>(); public DungeonPack RuinsPack; + private HashMap<String, DungeonPack> dungeonPackMapping = new HashMap<String, DungeonPack>(); + private ArrayList<DungeonPack> dungeonPackList = new ArrayList<DungeonPack>(); 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<DungeonGenerator> 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<WeightedContainer<DungeonPack>> packs = new ArrayList<WeightedContainer<DungeonPack>>(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<DungeonPack>(pack, config.getPackWeight())); + } + } + if (!packs.isEmpty()) + { + //Pick a random dungeon pack + selection = ((WeightedContainer<DungeonPack>) WeightedRandom.getRandomItem(random, packs)).getData(); + } + return selection; + } + public Collection<String> 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<DungeonGenerator> 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