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.
This commit is contained in:
SenseiKiwi 2013-08-21 14:26:10 -04:00
parent 99d9b5a2a1
commit 0e67596ca0
9 changed files with 263 additions and 41 deletions

View file

@ -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())
{

View file

@ -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;

View file

@ -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();

View file

@ -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()];

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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) );
}
}

View file

@ -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