diff --git a/StevenDimDoors/mod_pocketDim/DungeonGenerator.java b/StevenDimDoors/mod_pocketDim/DungeonGenerator.java index ee575f2..6c6ea16 100644 --- a/StevenDimDoors/mod_pocketDim/DungeonGenerator.java +++ b/StevenDimDoors/mod_pocketDim/DungeonGenerator.java @@ -1,14 +1,22 @@ package StevenDimDoors.mod_pocketDim; +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.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. + //Otherwise it would have to be serializable and all sorts of problems would arise. + private static final HashMap dungeonTypes = new HashMap(); + public int weight; public String schematicPath; public ArrayList sideRifts = new ArrayList(); @@ -19,10 +27,54 @@ public class DungeonGenerator implements Serializable public int exitDoorsSoFar=0; public int deadEndsSoFar=0; - public DungeonGenerator(int weight, String schematicPath, Boolean isOpen) + public DungeonGenerator(int weight, String schematicPath, boolean isOpen, DungeonType dungeonType) { - this.weight=weight; - this.schematicPath=schematicPath; - this.isOpen=isOpen; + this.weight = weight; + this.schematicPath = schematicPath; + this.isOpen = isOpen; + + dungeonTypes.put(this, dungeonType); //Hax... + } + + public DungeonType getDungeonType() + { + DungeonType type = dungeonTypes.get(this); + if (type == null) + { + //Infer the dungeon's type from its file name + //There is minimal risk of us applying this to untagged dungeons and this'll be phased out + //when we get the new save format. + try + { + File file = new File(schematicPath); + String typeName = file.getName().split("_")[0]; + type = DungeonHelper.instance().RuinsPack.getType(typeName); + } + catch (Exception e) { } + if (type == null) + { + type = DungeonType.UNKNOWN_TYPE; + } + dungeonTypes.put(this, type); + } + return type; + } + + @Override + public int hashCode() + { + return (schematicPath != null) ? schematicPath.hashCode() : 0; + } + + @Override + public boolean equals(Object other) + { + return equals((DungeonGenerator) other); + } + + public boolean equals(DungeonGenerator other) + { + return ((this.schematicPath != null && this.schematicPath.equals(other.schematicPath)) || + (this.schematicPath == other.schematicPath)); } } \ No newline at end of file diff --git a/StevenDimDoors/mod_pocketDim/SchematicLoader.java b/StevenDimDoors/mod_pocketDim/SchematicLoader.java index 22d46e1..00d1b6a 100644 --- a/StevenDimDoors/mod_pocketDim/SchematicLoader.java +++ b/StevenDimDoors/mod_pocketDim/SchematicLoader.java @@ -28,6 +28,7 @@ public class SchematicLoader int originDimID = link.locDimID; int destDimID = link.destDimID; HashMap dimList = dimHelper.dimList; + DungeonHelper dungeonHelper = DungeonHelper.instance(); World world; if (dimList.containsKey(destDimID)) @@ -49,8 +50,7 @@ public class SchematicLoader long factorB = random.nextLong() / 2L * 2L + 1L; random.setSeed((link.destXCoord >> 4) * factorA + (link.destZCoord >> 4) * factorB ^ world.getSeed()); - //TODO: FIX THIS LINE OR SADNESS WILL FOLLOW. Add a reference to the dungeon pack. - //DungeonHelper.instance().generateDungeonLink(link, ???, random); + dungeonHelper.generateDungeonLink(link, dungeonHelper.RuinsPack, random); } schematicPath = dimList.get(destDimID).dungeonGenerator.schematicPath; @@ -90,7 +90,7 @@ public class SchematicLoader //TODO: In the future, remove this dungeon from the generation lists altogether. //That will have to wait until our code is updated to support that more easily. System.err.println("The dungeon will not be loaded."); - DungeonGenerator defaultError = DungeonHelper.instance().getDefaultErrorDungeon(); + DungeonGenerator defaultError = dungeonHelper.getDefaultErrorDungeon(); dimList.get(destDimID).dungeonGenerator = defaultError; dungeon = checkSourceAndLoad(defaultError.schematicPath); dungeon.applyImportFilters(properties); diff --git a/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java b/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java index ff833b3..194d5ce 100644 --- a/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java +++ b/StevenDimDoors/mod_pocketDim/commands/CommandExportDungeon.java @@ -54,7 +54,7 @@ public class CommandExportDungeon extends DDCommandBase if (command[1].equalsIgnoreCase("override")) { //Check that the schematic name is a legal name - if (DungeonHelper.SchematicNamePattern.matcher(command[0]).matches()) + if (DungeonHelper.SCHEMATIC_NAME_PATTERN.matcher(command[0]).matches()) { //Export the schematic return exportDungeon(sender, command[0]); @@ -85,7 +85,7 @@ public class CommandExportDungeon extends DDCommandBase { return new DDCommandResult("Error: Invalid dungeon type. Please use one of the existing types."); } - if (!DungeonHelper.DungeonNamePattern.matcher(command[1]).matches()) + if (!DungeonHelper.DUNGEON_NAME_PATTERN.matcher(command[1]).matches()) { return new DDCommandResult("Error: Invalid dungeon name. Please use only letters, numbers, and dashes."); } diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java index 5258561..472f20b 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonChainRule.java @@ -1,13 +1,66 @@ package StevenDimDoors.mod_pocketDim.dungeon.pack; +import java.util.ArrayList; import java.util.HashMap; +import StevenDimDoors.mod_pocketDim.util.WeightedContainer; + public class DungeonChainRule { + private final int[] condition; + private final ArrayList> products; + + public DungeonChainRule(DungeonChainRuleDefinition source, HashMap nameToTypeMapping) + { + ArrayList conditionNames = source.getCondition(); + ArrayList> productNames = source.getProducts(); - public OptimizedRule optimize(HashMap nameToTypeMapping) { - // TODO Auto-generated method stub - return null; + condition = new int[conditionNames.size()]; + for (int k = 0; k < condition.length; k++) + { + condition[k] = nameToTypeMapping.get(conditionNames.get(k)).ID; + } + products = new ArrayList>(); + for (WeightedContainer product : productNames) + { + products.add(new WeightedContainer(nameToTypeMapping.get(product.getData()), product.itemWeight )); + } + } + + public int length() + { + return condition.length; } + public boolean evaluate(int[] typeHistory) + { + if (typeHistory.length >= condition.length) + { + for (int k = 0; k < condition.length; k++) + { + if (condition[k] != 0 && typeHistory[k] != condition[k]) + { + return false; + } + } + return true; + } + else + { + return false; + } + } + + public ArrayList> products() + { + //Create a deep copy of the internal list of products. That way, if the list is modified externally, + //it won't affect the reference copy inside this rule. + ArrayList> copy = new ArrayList>(products.size()); + for (WeightedContainer container : products) + { + copy.add(container.clone()); + } + + return copy; + } } diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java index 9bb714e..9db777d 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Random; import net.minecraft.util.WeightedRandom; @@ -16,9 +15,11 @@ import StevenDimDoors.mod_pocketDim.util.WeightedContainer; public class DungeonPack { - //Why final? I just felt like it, honestly. ~SenseiKiwi - - private static final DungeonType WILDCARD_TYPE = new DungeonType(null, "?", 0); + //There is no precaution against having a dungeon type removed from a config file after dungeons of that type + //have been generated. That would likely cause one or two problems. It's hard to guard against when I don't know + //what the save format will be like completely. How should this class behave if it finds a "disowned" type? + //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 final String name; private final HashMap nameToTypeMapping; @@ -26,7 +27,7 @@ public class DungeonPack private final ArrayList allDungeons; private final DungeonPackConfig config; private final int maxRuleLength; - private final ArrayList rules; + private final ArrayList rules; public DungeonPack(DungeonPackConfig config) { @@ -42,7 +43,7 @@ public class DungeonPack this.groupedDungeons = new ArrayList>(typeCount); this.groupedDungeons.add(allDungeons); //Make sure the list of all dungeons is placed at index 0 - this.nameToTypeMapping.put(WILDCARD_TYPE.Name, WILDCARD_TYPE); + this.nameToTypeMapping.put(DungeonType.WILDCARD_TYPE.Name, DungeonType.WILDCARD_TYPE); index = 1; for (String typeName : config.getTypeNames()) @@ -53,22 +54,23 @@ public class DungeonPack index++; } - //Construct optimized rules from config rules - ArrayList chainRules = config.getRules(); - this.rules = new ArrayList(chainRules.size()); - for (DungeonChainRule rule : chainRules) + //Construct optimized rules from definitions + ArrayList definitions = config.getRules(); + this.rules = new ArrayList(definitions.size()); + for (DungeonChainRuleDefinition definition : definitions) { - OptimizedRule optimized = rule.optimize(nameToTypeMapping); - this.rules.add(optimized); - if (maxLength < optimized.length()) + DungeonChainRule rule = new DungeonChainRule(definition, nameToTypeMapping); + this.rules.add(rule); + if (maxLength < rule.length()) { - maxLength = optimized.length(); + maxLength = rule.length(); } } this.maxRuleLength = maxLength; - //Remove the reference to the non-optimized rules to free up memory - we won't need them here + //Remove unnecessary references to save a little memory - we won't need them here this.config.setRules(null); + this.config.setTypeNames(null); } public String getName() @@ -84,7 +86,7 @@ public class DungeonPack public DungeonType getType(String typeName) { DungeonType result = nameToTypeMapping.get(typeName.toUpperCase()); - if (result != WILDCARD_TYPE) + if (result.Owner == this) //Filter out the wildcard dungeon type { return result; } @@ -99,6 +101,21 @@ public class DungeonPack return (this.getType(typeName) != null); } + public void addDungeon(DungeonGenerator generator) + { + //Make sure this dungeon really belongs in this pack + DungeonType type = generator.getDungeonType(); + if (type.Owner == this) + { + allDungeons.add(generator); + groupedDungeons.get(type.ID).add(generator); + } + else + { + throw new IllegalArgumentException("The dungeon type of generator must belong to this instance of DungeonPack."); + } + } + public DungeonGenerator getNextDungeon(LinkData inbound, Random random) { if (allDungeons.isEmpty()) @@ -106,12 +123,14 @@ public class DungeonPack return null; } - //Retrieve a list of the previous dungeons in this chain. Restrict the length of the - //search to the length of the longest rule. Getting more data is useless. - dimHelper helper = dimHelper.instance; + //Retrieve a list of the previous dungeons in this chain. + //If we're not going to check for duplicates in chains, restrict the length of the history to the length + //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. - //TODO: Add dungeon pack parameter! We can't use dungeon types from other packs. - ArrayList history = DungeonHelper.getDungeonChainHistory(helper.getDimData(inbound.locDimID), maxRuleLength); + int maxSearchLength = config.allowDuplicatesInChain() ? maxRuleLength : 1337; + ArrayList history = DungeonHelper.getDungeonChainHistory( + dimHelper.instance.getDimData(inbound.locDimID), this, maxSearchLength); return getNextDungeon(history, random); } @@ -123,10 +142,10 @@ public class DungeonPack HashSet excludedDungeons = null; for (index = 0; index < typeHistory.length; index++) { - typeHistory[index] = getDungeonType(history.get(index)).ID; + typeHistory[index] = history.get(index).getDungeonType().ID; } - for (OptimizedRule rule : rules) + for (DungeonChainRule rule : rules) { if (rule.evaluate(typeHistory)) { @@ -139,9 +158,9 @@ public class DungeonPack if (nextType != null) { //Initialize the set of excluded dungeons if needed - if (excludedDungeons == null && config.allowDuplicatesInChain()) + if (excludedDungeons == null && !config.allowDuplicatesInChain()) { - //TODO: Finish implementing this! + excludedDungeons = new HashSet(history); } //List which dungeons are allowed @@ -166,6 +185,8 @@ public class DungeonPack { return getRandomDungeon(random, candidates); } + //If we've reached this point, then a dungeon was not selected. Discard the type and try again. + products.remove(nextType); } } while (nextType != null); @@ -175,16 +196,6 @@ public class DungeonPack //None of the rules were applicable. Simply return a random dungeon. return getRandomDungeon(random); } - - private DungeonType getDungeonType(DungeonGenerator generator) - { - //This function is a workaround for DungeonGenerator not having a dungeon type or pack field. - //I really don't want to go messing around with that serializable type. - //TODO: Remove this function once we transition to using the new save format. ~SenseiKiwi - - //TODO: Finish implementing this! - return null; - } public DungeonGenerator getRandomDungeon(Random random) { @@ -202,7 +213,7 @@ public class DungeonPack ArrayList> groupedDungeons) { //TODO: Make this faster? This algorithm runs in quadratic time in the worst case because of the random-selection - //process and the removal search. Should be okay for normal use, though. ~SenseiKiwi + //process and the removal search. Might be okay for normal use, though. ~SenseiKiwi //Pick a random dungeon type based on weights. Repeat this process until a non-empty group is found or all groups are checked. while (!types.isEmpty()) diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java index 8655d3c..c99dd91 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java @@ -1,22 +1,36 @@ package StevenDimDoors.mod_pocketDim.dungeon.pack; import java.util.ArrayList; -import java.util.List; public class DungeonPackConfig { + private String name; + private ArrayList typeNames; + private boolean allowDuplicatesInChain; + private ArrayList rules; + public DungeonPackConfig() { } + @SuppressWarnings("unchecked") private DungeonPackConfig(DungeonPackConfig source) { - + this.name = source.name; + this.typeNames = (ArrayList) source.typeNames.clone(); + this.allowDuplicatesInChain = source.allowDuplicatesInChain; + this.rules = (ArrayList) source.rules.clone(); } public void validate() { - + if (this.name == null) + throw new NullPointerException("name cannot be null"); + if (this.typeNames == null) + throw new NullPointerException("typeNames cannot be null"); + if (this.rules == null) + throw new NullPointerException("rules cannot be null"); } + @Override public DungeonPackConfig clone() { return new DungeonPackConfig(this); @@ -24,26 +38,41 @@ public class DungeonPackConfig public String getName() { - return null; + return name; + } + + public void setName(String name) + { + this.name = name; } - public List getTypeNames() + public ArrayList getTypeNames() { - return null; + return typeNames; + } + + public void setTypeNames(ArrayList typeNames) + { + this.typeNames = typeNames; } public boolean allowDuplicatesInChain() { - return false; + return allowDuplicatesInChain; + } + + public void setAllowDuplicatesInChain(boolean value) + { + allowDuplicatesInChain = value; } - public void setRules(Object object) { - // TODO Auto-generated method stub - + public void setRules(ArrayList rules) + { + this.rules = rules; } - public ArrayList getRules() { - // TODO Auto-generated method stub - return null; + public ArrayList getRules() + { + return rules; } } diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonType.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonType.java index e15b683..50e5921 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonType.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonType.java @@ -2,6 +2,9 @@ package StevenDimDoors.mod_pocketDim.dungeon.pack; public class DungeonType implements Comparable { + public static final DungeonType WILDCARD_TYPE = new DungeonType(null, "?", 0); + public static final DungeonType UNKNOWN_TYPE = new DungeonType(null, "!", -1); + public final DungeonPack Owner; public final String Name; public final int ID; diff --git a/StevenDimDoors/mod_pocketDim/dungeon/pack/OptimizedRule.java b/StevenDimDoors/mod_pocketDim/dungeon/pack/OptimizedRule.java deleted file mode 100644 index c69ccd7..0000000 --- a/StevenDimDoors/mod_pocketDim/dungeon/pack/OptimizedRule.java +++ /dev/null @@ -1,25 +0,0 @@ -package StevenDimDoors.mod_pocketDim.dungeon.pack; - -import java.util.ArrayList; - -import StevenDimDoors.mod_pocketDim.util.WeightedContainer; - -public class OptimizedRule -{ - - public int length() { - // TODO Auto-generated method stub - return 0; - } - - public boolean evaluate(int[] typeHistory) { - // TODO Auto-generated method stub - return false; - } - - public ArrayList> products() { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java index 406ade1..c364297 100644 --- a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java +++ b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java @@ -5,9 +5,9 @@ import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Random; @@ -20,15 +20,20 @@ import StevenDimDoors.mod_pocketDim.DungeonGenerator; import StevenDimDoors.mod_pocketDim.LinkData; import StevenDimDoors.mod_pocketDim.mod_pocketDim; import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic; +import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonChainRuleDefinition; import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPack; +import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPackConfig; +import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonType; import StevenDimDoors.mod_pocketDim.items.itemDimDoor; +import StevenDimDoors.mod_pocketDim.util.WeightedContainer; public class DungeonHelper { private static DungeonHelper instance = null; private static DDProperties properties = null; - public static final Pattern SchematicNamePattern = Pattern.compile("[A-Za-z0-9_\\-]+"); - public static final Pattern DungeonNamePattern = Pattern.compile("[A-Za-z0-9\\-]+"); + + 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\\-]+"); private static final String DEFAULT_UP_SCHEMATIC_PATH = "/schematics/core/simpleStairsUp.schematic"; private static final String DEFAULT_DOWN_SCHEMATIC_PATH = "/schematics/core/simpleStairsDown.schematic"; @@ -44,70 +49,22 @@ public class DungeonHelper public static final short MAX_DUNGEON_HEIGHT = MAX_DUNGEON_WIDTH; public static final short MAX_DUNGEON_LENGTH = MAX_DUNGEON_WIDTH; - private static final String HUB_DUNGEON_TYPE = "Hub"; - private static final String TRAP_DUNGEON_TYPE = "Trap"; - private static final String SIMPLE_HALL_DUNGEON_TYPE = "SimpleHall"; - private static final String COMPLEX_HALL_DUNGEON_TYPE = "ComplexHall"; - private static final String EXIT_DUNGEON_TYPE = "Exit"; - private static final String DEAD_END_DUNGEON_TYPE = "DeadEnd"; - private static final String MAZE_DUNGEON_TYPE = "Maze"; - - //The list of dungeon types will be kept as an array for now. If we allow new - //dungeon types in the future, then this can be changed to an ArrayList. - private static final String[] DUNGEON_TYPES = new String[] { - HUB_DUNGEON_TYPE, - TRAP_DUNGEON_TYPE, - SIMPLE_HALL_DUNGEON_TYPE, - COMPLEX_HALL_DUNGEON_TYPE, - EXIT_DUNGEON_TYPE, - DEAD_END_DUNGEON_TYPE, - MAZE_DUNGEON_TYPE - }; - private ArrayList untaggedDungeons = new ArrayList(); private ArrayList registeredDungeons = new ArrayList(); - - private ArrayList simpleHalls = new ArrayList(); - private ArrayList complexHalls = new ArrayList(); - private ArrayList deadEnds = new ArrayList(); - private ArrayList hubs = new ArrayList(); - private ArrayList mazes = new ArrayList(); - private ArrayList pistonTraps = new ArrayList(); - private ArrayList exits = new ArrayList(); + public DungeonPack RuinsPack; + private DungeonGenerator defaultUp; private DungeonGenerator defaultDown; private DungeonGenerator defaultError; - private HashSet dungeonTypeChecker; - private HashMap> dungeonTypeMapping; - private DungeonHelper() { - //Load the dungeon type checker with the list of all types in lowercase. - //Capitalization matters for matching in a hash set. - dungeonTypeChecker = new HashSet(); - for (String dungeonType : DUNGEON_TYPES) - { - dungeonTypeChecker.add(dungeonType.toLowerCase()); - } - - //Add all the basic dungeon types to dungeonTypeMapping - //Dungeon type names must be passed in lowercase to make matching easier. - dungeonTypeMapping = new HashMap>(); - dungeonTypeMapping.put(SIMPLE_HALL_DUNGEON_TYPE.toLowerCase(), simpleHalls); - dungeonTypeMapping.put(COMPLEX_HALL_DUNGEON_TYPE.toLowerCase(), complexHalls); - dungeonTypeMapping.put(HUB_DUNGEON_TYPE.toLowerCase(), hubs); - dungeonTypeMapping.put(EXIT_DUNGEON_TYPE.toLowerCase(), exits); - dungeonTypeMapping.put(DEAD_END_DUNGEON_TYPE.toLowerCase(), deadEnds); - dungeonTypeMapping.put(MAZE_DUNGEON_TYPE.toLowerCase(), mazes); - dungeonTypeMapping.put(TRAP_DUNGEON_TYPE.toLowerCase(), pistonTraps); - //Load our reference to the DDProperties singleton if (properties == null) properties = DDProperties.instance(); - registerCustomDungeons(); + registerDungeons(); } public static DungeonHelper initialize() @@ -135,15 +92,67 @@ public class DungeonHelper return instance; } - private void registerCustomDungeons() + private void registerDungeons() { File file = new File(properties.CustomSchematicDirectory); if (file.exists() || file.mkdir()) { copyfile.copyFile(DUNGEON_CREATION_GUIDE_SOURCE_PATH, file.getAbsolutePath() + "/How_to_add_dungeons.txt"); } + + RuinsPack = new DungeonPack(createRuinsConfig()); + registerBundledDungeons(); - importCustomDungeons(properties.CustomSchematicDirectory); + registerCustomDungeons(properties.CustomSchematicDirectory); + } + + private static DungeonPackConfig createRuinsConfig() + { + //This is a temporarily function for testing dungeon packs. + //It'll be removed later when we read dungeon configurations from files. + + ArrayList rules = new ArrayList(); + rules.add(parseDefinitionUnsafe("? ? ? -> DeadEnd Exit")); + rules.add(parseDefinitionUnsafe("Trap -> ?")); + rules.add(parseDefinitionUnsafe("Hub -> Trap")); + rules.add(parseDefinitionUnsafe("? -> Hub")); + rules.add(parseDefinitionUnsafe("-> ComplexHall#40 Hub#30 Trap#10 SimpleHall#10 Maze#10")); + + String[] typeNames = "Hub Trap Maze Exit DeadEnd SimpleHall ComplexHall".toUpperCase().split(" "); + + DungeonPackConfig config = new DungeonPackConfig(); + config.setName("ruins"); + config.setAllowDuplicatesInChain(false); + config.setRules(rules); + config.setTypeNames(new ArrayList(Arrays.asList(typeNames))); + return config; + } + + private static DungeonChainRuleDefinition parseDefinitionUnsafe(String definition) + { + //This is an improvised parsing function for rule definitions. Only for testing!!! + definition = definition.toUpperCase(); + String[] parts = definition.split("->"); + ArrayList condition = new ArrayList(); + ArrayList> products = new ArrayList>(); + + for (String conditionPart : parts[0].split(" ")) + { + if (!conditionPart.isEmpty()) + condition.add(conditionPart); + } + + for (String product : parts[1].split(" ")) + { + if (!product.isEmpty()) + { + String[] productParts = product.split("#"); + String productType = productParts[0]; + int weight = (productParts.length > 1) ? Integer.parseInt(productParts[1]) : 100; + products.add(new WeightedContainer(productType, weight)); + } + } + return new DungeonChainRuleDefinition(condition, products); } public List getRegisteredDungeons() @@ -186,7 +195,7 @@ public class DungeonHelper public boolean validateDungeonType(String type) { //Check if the dungeon type is valid - return dungeonTypeChecker.contains(type.toLowerCase()); + return RuinsPack.isKnownType(type); } public boolean validateSchematicName(String name) @@ -203,11 +212,11 @@ public class DungeonHelper return false; //Check if the dungeon type is valid - if (!dungeonTypeChecker.contains(dungeonData[0].toLowerCase())) + if (!validateDungeonType(dungeonData[0])) return false; //Check if the name is valid - if (!SchematicNamePattern.matcher(dungeonData[1]).matches()) + if (!SCHEMATIC_NAME_PATTERN.matcher(dungeonData[1]).matches()) return false; //Check if the open/closed flag is present @@ -246,14 +255,14 @@ public class DungeonHelper //Strip off the file extension while splitting the file name String[] dungeonData = name.substring(0, name.length() - SCHEMATIC_FILE_EXTENSION.length()).split("_"); - String dungeonType = dungeonData[0].toLowerCase(); + DungeonType dungeonType = RuinsPack.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); + DungeonGenerator generator = new DungeonGenerator(weight, path, isOpen, dungeonType); - dungeonTypeMapping.get(dungeonType).add(generator); + RuinsPack.addDungeon(generator); registeredDungeons.add(generator); if (verbose) { @@ -266,7 +275,7 @@ public class DungeonHelper { System.out.println("Could not parse dungeon filename, not adding dungeon to generation lists"); } - untaggedDungeons.add(new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, path, true)); + untaggedDungeons.add(new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, path, true, DungeonType.UNKNOWN_TYPE)); System.out.println("Registered untagged dungeon: " + name); } } @@ -277,7 +286,7 @@ public class DungeonHelper } } - private void importCustomDungeons(String path) + private void registerCustomDungeons(String path) { File directory = new File(path); File[] schematicNames = directory.listFiles(); @@ -298,9 +307,9 @@ public class DungeonHelper { //Register the core schematics //These are used for debugging and in case of unusual errors while loading dungeons - defaultUp = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, DEFAULT_UP_SCHEMATIC_PATH, true); - defaultDown = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, DEFAULT_DOWN_SCHEMATIC_PATH, true); - defaultError = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, DEFAULT_ERROR_SCHEMATIC_PATH, true); + defaultUp = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, DEFAULT_UP_SCHEMATIC_PATH, true, DungeonType.UNKNOWN_TYPE); + defaultDown = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, DEFAULT_DOWN_SCHEMATIC_PATH, true, DungeonType.UNKNOWN_TYPE); + 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); @@ -408,7 +417,7 @@ public class DungeonHelper return names; } - public static ArrayList getDungeonChainHistory(DimData dimData, int maxSize) + public static ArrayList getDungeonChainHistory(DimData dimData, DungeonPack pack, 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. @@ -418,10 +427,15 @@ public class DungeonHelper ArrayList history = new ArrayList(); DimData tailDim = helper.getDimData(helper.getLinkDataFromCoords(dimData.exitDimLink.destXCoord, dimData.exitDimLink.destYCoord, dimData.exitDimLink.destZCoord, dimData.exitDimLink.destDimID).destDimID); - for (int count = 0; count < maxSize && tailDim.dungeonGenerator != null; count++) + for (int count = 0; count < maxSize; count++) { - history.add(tailDim.dungeonGenerator); + if (tailDim.dungeonGenerator == null || tailDim.dungeonGenerator.getDungeonType().Owner != pack) + { + //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()) diff --git a/StevenDimDoors/mod_pocketDim/util/WeightedContainer.java b/StevenDimDoors/mod_pocketDim/util/WeightedContainer.java index e69e48b..71332f3 100644 --- a/StevenDimDoors/mod_pocketDim/util/WeightedContainer.java +++ b/StevenDimDoors/mod_pocketDim/util/WeightedContainer.java @@ -17,11 +17,15 @@ public class WeightedContainer extends WeightedRandomItem { { super(weight); this.data = data; - super.itemWeight = weight; } public T getData() { return data; } + + public WeightedContainer clone() + { + return new WeightedContainer(data, itemWeight); + } }