Merge branch 'master' of https://github.com/StevenRS11/DimDoors
This commit is contained in:
@@ -214,9 +214,9 @@ public class DDProperties
|
|||||||
"Sets the chance (out of " + RiftGenerator.MAX_CLUSTER_GENERATION_CHANCE + ") that a cluster of rifts will " +
|
"Sets the chance (out of " + RiftGenerator.MAX_CLUSTER_GENERATION_CHANCE + ") that a cluster of rifts will " +
|
||||||
"generate in a given chunk. The default chance is 3.").getInt();
|
"generate in a given chunk. The default chance is 3.").getInt();
|
||||||
|
|
||||||
GatewayGenerationChance = config.get(Configuration.CATEGORY_GENERAL, "Gateway Generation Chance", 40,
|
GatewayGenerationChance = config.get(Configuration.CATEGORY_GENERAL, "Gateway Generation Chance", 10,
|
||||||
"Sets the chance (out of " + RiftGenerator.MAX_GATEWAY_GENERATION_CHANCE + ") that a Rift Gateway will " +
|
"Sets the chance (out of " + RiftGenerator.MAX_GATEWAY_GENERATION_CHANCE + ") that a Rift Gateway will " +
|
||||||
"generate in a given chunk. The default chance is 40.").getInt();
|
"generate in a given chunk. The default chance is 10.").getInt();
|
||||||
|
|
||||||
RiftSpreadModifier = config.get(Configuration.CATEGORY_GENERAL, "Rift Spread Modifier", 3,
|
RiftSpreadModifier = config.get(Configuration.CATEGORY_GENERAL, "Rift Spread Modifier", 3,
|
||||||
"Sets the number of times a rift can spread. 0 prevents rifts from spreading at all. " +
|
"Sets the number of times a rift can spread. 0 prevents rifts from spreading at all. " +
|
||||||
|
|||||||
@@ -1,31 +1,80 @@
|
|||||||
package StevenDimDoors.mod_pocketDim;
|
package StevenDimDoors.mod_pocketDim;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonType;
|
||||||
|
import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper;
|
||||||
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class DungeonGenerator implements Serializable
|
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<DungeonGenerator, DungeonType> dungeonTypes = new HashMap<DungeonGenerator, DungeonType>();
|
||||||
|
|
||||||
public int weight;
|
public int weight;
|
||||||
public String schematicPath;
|
public String schematicPath;
|
||||||
public ArrayList<HashMap> sideRifts = new ArrayList<HashMap>();
|
public ArrayList<HashMap> sideRifts = new ArrayList<HashMap>();
|
||||||
public LinkData exitLink;
|
public LinkData exitLink;
|
||||||
public static Random rand = new Random();
|
|
||||||
public boolean isOpen;
|
public boolean isOpen;
|
||||||
|
|
||||||
public int sideDoorsSoFar=0;
|
public int sideDoorsSoFar=0;
|
||||||
public int exitDoorsSoFar=0;
|
public int exitDoorsSoFar=0;
|
||||||
public int deadEndsSoFar=0;
|
public int deadEndsSoFar=0;
|
||||||
|
|
||||||
|
public DungeonGenerator(int weight, String schematicPath, boolean isOpen, DungeonType dungeonType)
|
||||||
|
|
||||||
public DungeonGenerator(int weight, String schematicPath, Boolean isOpen)
|
|
||||||
{
|
{
|
||||||
this.weight=weight;
|
this.weight = weight;
|
||||||
this.schematicPath=schematicPath;
|
this.schematicPath = schematicPath;
|
||||||
this.isOpen=isOpen;
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,32 +46,26 @@ public class EventHookContainer
|
|||||||
dimHelper.instance.interDimLinkList.clear();
|
dimHelper.instance.interDimLinkList.clear();
|
||||||
dimHelper.instance.initPockets();
|
dimHelper.instance.initPockets();
|
||||||
}
|
}
|
||||||
for (Integer ids : dimHelper.getIDs())
|
|
||||||
{
|
//TODO: In the future, we should iterate over DimHelper's dimension list. We ignore other dimensions anyway.
|
||||||
World world = dimHelper.getWorld(ids);
|
for (int dimensionID : dimHelper.getIDs())
|
||||||
|
{
|
||||||
|
World world = dimHelper.getWorld(dimensionID);
|
||||||
int linkCount = 0;
|
int linkCount = 0;
|
||||||
|
|
||||||
if (dimHelper.dimList.containsKey(world.provider.dimensionId))
|
if (dimHelper.dimList.containsKey(dimensionID))
|
||||||
{
|
{
|
||||||
//TODO added temporary Try/catch block to prevent a crash here, getLinksInDim needs to be looked at
|
for (LinkData link : dimHelper.instance.getDimData(dimensionID).getLinksInDim())
|
||||||
try
|
|
||||||
{
|
|
||||||
for (LinkData link:dimHelper.instance.getDimData(world.provider.dimensionId).getLinksInDim())
|
|
||||||
{
|
|
||||||
if (!mod_pocketDim.blockRift.isBlockImmune(world, link.locXCoord, link.locYCoord, link.locZCoord))
|
|
||||||
{
|
|
||||||
dimHelper.getWorld(link.locDimID).setBlock(link.locXCoord, link.locYCoord, link.locZCoord, properties.RiftBlockID);
|
|
||||||
}
|
|
||||||
linkCount++;
|
|
||||||
if (linkCount >= 100)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
if (!mod_pocketDim.blockRift.isBlockImmune(world, link.locXCoord, link.locYCoord, link.locZCoord))
|
||||||
|
{
|
||||||
|
world.setBlock(link.locXCoord, link.locYCoord, link.locZCoord, properties.RiftBlockID);
|
||||||
|
}
|
||||||
|
linkCount++;
|
||||||
|
if (linkCount >= 100)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ public class RiftGenerator implements IWorldGenerator
|
|||||||
private static final int CHUNK_LENGTH = 16;
|
private static final int CHUNK_LENGTH = 16;
|
||||||
private static final int GATEWAY_RADIUS = 4;
|
private static final int GATEWAY_RADIUS = 4;
|
||||||
private static final int MAX_GATEWAY_GENERATION_ATTEMPTS = 10;
|
private static final int MAX_GATEWAY_GENERATION_ATTEMPTS = 10;
|
||||||
|
private static final int NETHER_CHANCE_CORRECTION = 4;
|
||||||
|
private static final int OVERWORLD_DIMENSION_ID = 0;
|
||||||
|
private static final int NETHER_DIMENSION_ID = -1;
|
||||||
private static DDProperties properties = null;
|
private static DDProperties properties = null;
|
||||||
|
|
||||||
public RiftGenerator()
|
public RiftGenerator()
|
||||||
@@ -43,16 +46,30 @@ public class RiftGenerator implements IWorldGenerator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//This check prevents a crash related to superflat worlds not loading World 0
|
//This check prevents a crash related to superflat worlds not loading World 0
|
||||||
if (dimHelper.getWorld(0) == null)
|
if (dimHelper.getWorld(OVERWORLD_DIMENSION_ID) == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int x, y, z;
|
int x, y, z;
|
||||||
int attempts;
|
int attempts;
|
||||||
int blockID;
|
int correction;
|
||||||
boolean valid;
|
boolean valid;
|
||||||
LinkData link;
|
LinkData link;
|
||||||
|
|
||||||
|
//Check if we're generating things in the Nether
|
||||||
|
if (world.provider.dimensionId == NETHER_DIMENSION_ID)
|
||||||
|
{
|
||||||
|
//The terrain in the Nether makes it much harder for our gateway spawning algorithm to find a spot to place a gateway.
|
||||||
|
//Tests show that only about 15% of attempts succeed. Compensate for this by multiplying the chance of generation
|
||||||
|
//by a correction factor.
|
||||||
|
correction = NETHER_CHANCE_CORRECTION;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//No correction
|
||||||
|
correction = 1;
|
||||||
|
}
|
||||||
|
|
||||||
//Randomly decide whether to place a cluster of rifts here
|
//Randomly decide whether to place a cluster of rifts here
|
||||||
if (random.nextInt(MAX_CLUSTER_GENERATION_CHANCE) < properties.ClusterGenerationChance)
|
if (random.nextInt(MAX_CLUSTER_GENERATION_CHANCE) < properties.ClusterGenerationChance)
|
||||||
@@ -91,7 +108,8 @@ public class RiftGenerator implements IWorldGenerator
|
|||||||
|
|
||||||
//Check if generating structures is enabled and randomly decide whether to place a Rift Gateway here.
|
//Check if generating structures is enabled and randomly decide whether to place a Rift Gateway here.
|
||||||
//This only happens if a rift cluster was NOT generated.
|
//This only happens if a rift cluster was NOT generated.
|
||||||
else if (random.nextInt(MAX_GATEWAY_GENERATION_CHANCE) < properties.GatewayGenerationChance && isStructureGenerationAllowed())
|
else if (random.nextInt(MAX_GATEWAY_GENERATION_CHANCE) < properties.GatewayGenerationChance * correction &&
|
||||||
|
isStructureGenerationAllowed())
|
||||||
{
|
{
|
||||||
valid = false;
|
valid = false;
|
||||||
x = y = z = 0; //Stop the compiler from freaking out
|
x = y = z = 0; //Stop the compiler from freaking out
|
||||||
@@ -117,59 +135,74 @@ public class RiftGenerator implements IWorldGenerator
|
|||||||
//If the current dimension isn't Limbo, build a Rift Gateway out of Stone Bricks
|
//If the current dimension isn't Limbo, build a Rift Gateway out of Stone Bricks
|
||||||
if (world.provider.dimensionId != properties.LimboDimensionID)
|
if (world.provider.dimensionId != properties.LimboDimensionID)
|
||||||
{
|
{
|
||||||
blockID = Block.stoneBrick.blockID;
|
createStoneGateway(world, x, y, z, random);
|
||||||
|
|
||||||
//Replace some of the ground around the gateway with bricks
|
|
||||||
for (int xc = -GATEWAY_RADIUS; xc <= GATEWAY_RADIUS; xc++)
|
|
||||||
{
|
|
||||||
for (int zc= -GATEWAY_RADIUS; zc <= GATEWAY_RADIUS; zc++)
|
|
||||||
{
|
|
||||||
//Check that the block is supported by an opaque block.
|
|
||||||
//This prevents us from building over a cliff, on the peak of a mountain,
|
|
||||||
//or the surface of the ocean or a frozen lake.
|
|
||||||
if (world.isBlockOpaqueCube(x + xc, y - 2, z + zc))
|
|
||||||
{
|
|
||||||
//Randomly choose whether to place bricks or not. The math is designed so that the
|
|
||||||
//chances of placing a block decrease as we get farther from the gateway's center.
|
|
||||||
if (Math.abs(xc) + Math.abs(zc) < random.nextInt(2) + 3)
|
|
||||||
{
|
|
||||||
//Place Stone Bricks
|
|
||||||
world.setBlock(x + xc, y - 1, z + zc, blockID, 0, 3);
|
|
||||||
}
|
|
||||||
else if (Math.abs(xc) + Math.abs(zc) < random.nextInt(3) + 3)
|
|
||||||
{
|
|
||||||
//Place Cracked Stone Bricks
|
|
||||||
world.setBlock(x + xc, y - 1, z + zc, blockID, 2, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Use Chiseled Stone Bricks to top off the pillars around the door
|
|
||||||
world.setBlock(x, y + 2, z + 1, blockID, 3, 3);
|
|
||||||
world.setBlock(x, y + 2, z - 1, blockID, 3, 3);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Build the gateway out of Unraveled Fabric. Since nearly all the blocks in Limbo are of
|
createLimboGateway(world, x, y, z);
|
||||||
//that type, there is no point replacing the ground. Just build the tops of the columns here.
|
|
||||||
blockID = properties.LimboBlockID;
|
|
||||||
world.setBlock(x, y + 2, z + 1, blockID, 0, 3);
|
|
||||||
world.setBlock(x, y + 2, z - 1, blockID, 0, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Place the shiny transient door into a dungeon
|
//Place the shiny transient door into a dungeon
|
||||||
itemDimDoor.placeDoorBlock(world, x, y + 1, z, 0, mod_pocketDim.transientDoor);
|
itemDimDoor.placeDoorBlock(world, x, y + 1, z, 0, mod_pocketDim.transientDoor);
|
||||||
|
|
||||||
//Build the columns around the door
|
|
||||||
world.setBlock(x, y + 1, z - 1, blockID, 0, 3);
|
|
||||||
world.setBlock(x, y + 1, z + 1, blockID, 0, 3);
|
|
||||||
world.setBlock(x, y, z - 1, blockID, 0, 3);
|
|
||||||
world.setBlock(x, y, z + 1, blockID, 0, 3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void createStoneGateway(World world, int x, int y, int z, Random random)
|
||||||
|
{
|
||||||
|
final int blockID = Block.stoneBrick.blockID;
|
||||||
|
|
||||||
|
//Replace some of the ground around the gateway with bricks
|
||||||
|
for (int xc = -GATEWAY_RADIUS; xc <= GATEWAY_RADIUS; xc++)
|
||||||
|
{
|
||||||
|
for (int zc= -GATEWAY_RADIUS; zc <= GATEWAY_RADIUS; zc++)
|
||||||
|
{
|
||||||
|
//Check that the block is supported by an opaque block.
|
||||||
|
//This prevents us from building over a cliff, on the peak of a mountain,
|
||||||
|
//or the surface of the ocean or a frozen lake.
|
||||||
|
if (world.isBlockOpaqueCube(x + xc, y - 2, z + zc))
|
||||||
|
{
|
||||||
|
//Randomly choose whether to place bricks or not. The math is designed so that the
|
||||||
|
//chances of placing a block decrease as we get farther from the gateway's center.
|
||||||
|
if (Math.abs(xc) + Math.abs(zc) < random.nextInt(2) + 3)
|
||||||
|
{
|
||||||
|
//Place Stone Bricks
|
||||||
|
world.setBlock(x + xc, y - 1, z + zc, blockID, 0, 3);
|
||||||
|
}
|
||||||
|
else if (Math.abs(xc) + Math.abs(zc) < random.nextInt(3) + 3)
|
||||||
|
{
|
||||||
|
//Place Cracked Stone Bricks
|
||||||
|
world.setBlock(x + xc, y - 1, z + zc, blockID, 2, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Use Chiseled Stone Bricks to top off the pillars around the door
|
||||||
|
world.setBlock(x, y + 2, z + 1, blockID, 3, 3);
|
||||||
|
world.setBlock(x, y + 2, z - 1, blockID, 3, 3);
|
||||||
|
//Build the columns around the door
|
||||||
|
world.setBlock(x, y + 1, z - 1, blockID, 0, 3);
|
||||||
|
world.setBlock(x, y + 1, z + 1, blockID, 0, 3);
|
||||||
|
world.setBlock(x, y, z - 1, blockID, 0, 3);
|
||||||
|
world.setBlock(x, y, z + 1, blockID, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createLimboGateway(World world, int x, int y, int z)
|
||||||
|
{
|
||||||
|
//Build the gateway out of Unraveled Fabric. Since nearly all the blocks in Limbo are of
|
||||||
|
//that type, there is no point replacing the ground.
|
||||||
|
final int blockID = properties.LimboBlockID;
|
||||||
|
world.setBlock(x, y + 2, z + 1, blockID, 0, 3);
|
||||||
|
world.setBlock(x, y + 2, z - 1, blockID, 0, 3);
|
||||||
|
|
||||||
|
//Build the columns around the door
|
||||||
|
world.setBlock(x, y + 1, z - 1, blockID, 0, 3);
|
||||||
|
world.setBlock(x, y + 1, z + 1, blockID, 0, 3);
|
||||||
|
world.setBlock(x, y, z - 1, blockID, 0, 3);
|
||||||
|
world.setBlock(x, y, z + 1, blockID, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean checkGatewayLocation(World world, int x, int y, int z)
|
private static boolean checkGatewayLocation(World world, int x, int y, int z)
|
||||||
{
|
{
|
||||||
//Check if the point is within the acceptable altitude range, the block above that point is empty,
|
//Check if the point is within the acceptable altitude range, the block above that point is empty,
|
||||||
@@ -195,6 +228,6 @@ public class RiftGenerator implements IWorldGenerator
|
|||||||
|
|
||||||
private static boolean isStructureGenerationAllowed()
|
private static boolean isStructureGenerationAllowed()
|
||||||
{
|
{
|
||||||
return DimensionManager.getWorld(0).getWorldInfo().isMapFeaturesEnabled();
|
return DimensionManager.getWorld(OVERWORLD_DIMENSION_ID).getWorldInfo().isMapFeaturesEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package StevenDimDoors.mod_pocketDim;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic;
|
import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic;
|
||||||
@@ -27,14 +28,28 @@ public class SchematicLoader
|
|||||||
int originDimID = link.locDimID;
|
int originDimID = link.locDimID;
|
||||||
int destDimID = link.destDimID;
|
int destDimID = link.destDimID;
|
||||||
HashMap<Integer, DimData> dimList = dimHelper.dimList;
|
HashMap<Integer, DimData> dimList = dimHelper.dimList;
|
||||||
|
DungeonHelper dungeonHelper = DungeonHelper.instance();
|
||||||
|
World world;
|
||||||
|
|
||||||
if (dimList.containsKey(destDimID))
|
if (dimList.containsKey(destDimID))
|
||||||
{
|
{
|
||||||
|
dimList.get(destDimID).hasBeenFilled = true;
|
||||||
|
if (dimHelper.getWorld(destDimID) == null)
|
||||||
|
{
|
||||||
|
dimHelper.initDimension(destDimID);
|
||||||
|
}
|
||||||
|
world = dimHelper.getWorld(destDimID);
|
||||||
|
|
||||||
if (dimList.get(destDimID).dungeonGenerator == null)
|
if (dimList.get(destDimID).dungeonGenerator == null)
|
||||||
{
|
{
|
||||||
DungeonHelper.instance().generateDungeonLink(link);
|
//TODO: We should centralize RNG initialization and world-seed modifiers for each specific application.
|
||||||
|
final long localSeed = world.getSeed() ^ 0x2F50DB9B4A8057E4L ^ computeDestinationHash(link);
|
||||||
|
final Random random = new Random(localSeed);
|
||||||
|
|
||||||
|
dungeonHelper.generateDungeonLink(link, dungeonHelper.RuinsPack, random);
|
||||||
}
|
}
|
||||||
schematicPath = dimList.get(destDimID).dungeonGenerator.schematicPath;
|
schematicPath = dimList.get(destDimID).dungeonGenerator.schematicPath;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -71,19 +86,12 @@ public class SchematicLoader
|
|||||||
//TODO: In the future, remove this dungeon from the generation lists altogether.
|
//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.
|
//That will have to wait until our code is updated to support that more easily.
|
||||||
System.err.println("The dungeon will not be loaded.");
|
System.err.println("The dungeon will not be loaded.");
|
||||||
DungeonGenerator defaultError = DungeonHelper.instance().getDefaultErrorDungeon();
|
DungeonGenerator defaultError = dungeonHelper.getDefaultErrorDungeon();
|
||||||
dimList.get(destDimID).dungeonGenerator = defaultError;
|
dimList.get(destDimID).dungeonGenerator = defaultError;
|
||||||
dungeon = checkSourceAndLoad(defaultError.schematicPath);
|
dungeon = checkSourceAndLoad(defaultError.schematicPath);
|
||||||
dungeon.applyImportFilters(properties);
|
dungeon.applyImportFilters(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
dimList.get(destDimID).hasBeenFilled = true;
|
|
||||||
if (dimHelper.getWorld(destDimID) == null)
|
|
||||||
{
|
|
||||||
dimHelper.initDimension(destDimID);
|
|
||||||
}
|
|
||||||
World world = dimHelper.getWorld(destDimID);
|
|
||||||
|
|
||||||
//Adjust the height at which the dungeon is placed to prevent vertical clipping
|
//Adjust the height at which the dungeon is placed to prevent vertical clipping
|
||||||
int fixedY = adjustDestinationY(world, link.destYCoord, dungeon);
|
int fixedY = adjustDestinationY(world, link.destYCoord, dungeon);
|
||||||
if (fixedY != link.destYCoord)
|
if (fixedY != link.destYCoord)
|
||||||
@@ -145,4 +153,44 @@ public class SchematicLoader
|
|||||||
}
|
}
|
||||||
return dungeon;
|
return dungeon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long computeDestinationHash(LinkData link)
|
||||||
|
{
|
||||||
|
//Time for some witchcraft.
|
||||||
|
//The code here is inspired by a discussion on Stack Overflow regarding hash codes for 3D.
|
||||||
|
//Source: http://stackoverflow.com/questions/9858376/hashcode-for-3d-integer-coordinates-with-high-spatial-coherence
|
||||||
|
|
||||||
|
//Use 8 bits from Y and 24 bits from X and Z. Mix in 8 bits from the destination dim ID too - that means
|
||||||
|
//even if you aligned two doors perfectly between two pockets, it's unlikely they would lead to the same dungeon.
|
||||||
|
|
||||||
|
int bit;
|
||||||
|
int index;
|
||||||
|
long hash;
|
||||||
|
int w = link.destDimID;
|
||||||
|
int x = link.destXCoord;
|
||||||
|
int y = link.destYCoord;
|
||||||
|
int z = link.destZCoord;
|
||||||
|
|
||||||
|
hash = 0;
|
||||||
|
index = 0;
|
||||||
|
for (bit = 0; bit < 8; bit++)
|
||||||
|
{
|
||||||
|
hash |= ((w >> bit) & 1) << index;
|
||||||
|
index++;
|
||||||
|
hash |= ((x >> bit) & 1) << index;
|
||||||
|
index++;
|
||||||
|
hash |= ((y >> bit) & 1) << index;
|
||||||
|
index++;
|
||||||
|
hash |= ((z >> bit) & 1) << index;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
for (; bit < 24; bit++)
|
||||||
|
{
|
||||||
|
hash |= ((x >> bit) & 1) << index;
|
||||||
|
index++;
|
||||||
|
hash |= ((z >> bit) & 1) << index;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ public class CommandExportDungeon extends DDCommandBase
|
|||||||
if (command[1].equalsIgnoreCase("override"))
|
if (command[1].equalsIgnoreCase("override"))
|
||||||
{
|
{
|
||||||
//Check that the schematic name is a legal name
|
//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
|
//Export the schematic
|
||||||
return exportDungeon(sender, command[0]);
|
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.");
|
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.");
|
return new DDCommandResult("Error: Invalid dungeon name. Please use only letters, numbers, and dashes.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
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<WeightedContainer<DungeonType>> products;
|
||||||
|
|
||||||
|
public DungeonChainRule(DungeonChainRuleDefinition source, HashMap<String, DungeonType> nameToTypeMapping)
|
||||||
|
{
|
||||||
|
ArrayList<String> conditionNames = source.getCondition();
|
||||||
|
ArrayList<WeightedContainer<String>> productNames = source.getProducts();
|
||||||
|
|
||||||
|
//Obtain the IDs of dungeon types in reverse order. Reverse order makes comparing against chain histories easy.
|
||||||
|
condition = new int[conditionNames.size()];
|
||||||
|
for (int src = 0, dst = condition.length - 1; src < condition.length; src++, dst--)
|
||||||
|
{
|
||||||
|
condition[dst] = nameToTypeMapping.get(conditionNames.get(src)).ID;
|
||||||
|
}
|
||||||
|
products = new ArrayList<WeightedContainer<DungeonType>>(productNames.size());
|
||||||
|
for (WeightedContainer<String> product : productNames)
|
||||||
|
{
|
||||||
|
products.add(new WeightedContainer<DungeonType>(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<WeightedContainer<DungeonType>> 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<WeightedContainer<DungeonType>> copy = new ArrayList<WeightedContainer<DungeonType>>(products.size());
|
||||||
|
for (WeightedContainer<DungeonType> container : products)
|
||||||
|
{
|
||||||
|
copy.add(container.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.dungeon.pack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import StevenDimDoors.mod_pocketDim.util.WeightedContainer;
|
||||||
|
|
||||||
|
public class DungeonChainRuleDefinition
|
||||||
|
{
|
||||||
|
private ArrayList<String> conditions;
|
||||||
|
private ArrayList<WeightedContainer<String>> products;
|
||||||
|
|
||||||
|
public DungeonChainRuleDefinition(ArrayList<String> conditions, ArrayList<WeightedContainer<String>> products)
|
||||||
|
{
|
||||||
|
this.conditions = conditions;
|
||||||
|
this.products = products;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<String> getCondition()
|
||||||
|
{
|
||||||
|
return conditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<WeightedContainer<String>> getProducts()
|
||||||
|
{
|
||||||
|
return products;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
258
StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java
Normal file
258
StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPack.java
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.dungeon.pack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import net.minecraft.util.WeightedRandom;
|
||||||
|
import StevenDimDoors.mod_pocketDim.DungeonGenerator;
|
||||||
|
import StevenDimDoors.mod_pocketDim.LinkData;
|
||||||
|
import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper;
|
||||||
|
import StevenDimDoors.mod_pocketDim.helpers.dimHelper;
|
||||||
|
import StevenDimDoors.mod_pocketDim.util.WeightedContainer;
|
||||||
|
|
||||||
|
public class DungeonPack
|
||||||
|
{
|
||||||
|
//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<String, DungeonType> nameToTypeMapping;
|
||||||
|
private final ArrayList<ArrayList<DungeonGenerator>> groupedDungeons;
|
||||||
|
private final ArrayList<DungeonGenerator> allDungeons;
|
||||||
|
private final DungeonPackConfig config;
|
||||||
|
private final int maxRuleLength;
|
||||||
|
private final ArrayList<DungeonChainRule> rules;
|
||||||
|
|
||||||
|
public DungeonPack(DungeonPackConfig config)
|
||||||
|
{
|
||||||
|
config.validate();
|
||||||
|
this.config = config.clone(); //Store a clone of the config so that the caller can't change it externally later
|
||||||
|
this.name = config.getName();
|
||||||
|
|
||||||
|
int index;
|
||||||
|
int maxLength = 0;
|
||||||
|
int typeCount = config.getTypeNames().size();
|
||||||
|
this.allDungeons = new ArrayList<DungeonGenerator>();
|
||||||
|
this.nameToTypeMapping = new HashMap<String, DungeonType>(typeCount);
|
||||||
|
this.groupedDungeons = new ArrayList<ArrayList<DungeonGenerator>>(typeCount);
|
||||||
|
|
||||||
|
this.groupedDungeons.add(allDungeons); //Make sure the list of all dungeons is placed at index 0
|
||||||
|
this.nameToTypeMapping.put(DungeonType.WILDCARD_TYPE.Name, DungeonType.WILDCARD_TYPE);
|
||||||
|
|
||||||
|
index = 1;
|
||||||
|
for (String typeName : config.getTypeNames())
|
||||||
|
{
|
||||||
|
String standardName = typeName.toUpperCase();
|
||||||
|
this.nameToTypeMapping.put(standardName, new DungeonType(this, standardName, index));
|
||||||
|
this.groupedDungeons.add(new ArrayList<DungeonGenerator>());
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Construct optimized rules from definitions
|
||||||
|
ArrayList<DungeonChainRuleDefinition> definitions = config.getRules();
|
||||||
|
this.rules = new ArrayList<DungeonChainRule>(definitions.size());
|
||||||
|
for (DungeonChainRuleDefinition definition : definitions)
|
||||||
|
{
|
||||||
|
DungeonChainRule rule = new DungeonChainRule(definition, nameToTypeMapping);
|
||||||
|
this.rules.add(rule);
|
||||||
|
if (maxLength < rule.length())
|
||||||
|
{
|
||||||
|
maxLength = rule.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.maxRuleLength = maxLength;
|
||||||
|
|
||||||
|
//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()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
return allDungeons.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DungeonType getType(String typeName)
|
||||||
|
{
|
||||||
|
DungeonType result = nameToTypeMapping.get(typeName.toUpperCase());
|
||||||
|
if (result.Owner == this) //Filter out the wildcard dungeon type
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isKnownType(String typeName)
|
||||||
|
{
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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.
|
||||||
|
|
||||||
|
int maxSearchLength = config.allowDuplicatesInChain() ? maxRuleLength : 1337;
|
||||||
|
ArrayList<DungeonGenerator> history = DungeonHelper.getDungeonChainHistory(
|
||||||
|
dimHelper.instance.getDimData(inbound.locDimID), this, maxSearchLength);
|
||||||
|
return getNextDungeon(history, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DungeonGenerator getNextDungeon(ArrayList<DungeonGenerator> history, Random random)
|
||||||
|
{
|
||||||
|
//Extract the dungeon types that have been used from history and convert them into an array of IDs
|
||||||
|
int index;
|
||||||
|
int[] typeHistory = new int[history.size()];
|
||||||
|
HashSet<DungeonGenerator> excludedDungeons = null;
|
||||||
|
for (index = 0; index < typeHistory.length; index++)
|
||||||
|
{
|
||||||
|
typeHistory[index] = history.get(index).getDungeonType().ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DungeonChainRule rule : rules)
|
||||||
|
{
|
||||||
|
if (rule.evaluate(typeHistory))
|
||||||
|
{
|
||||||
|
//Pick a random dungeon type to be generated next based on the rule's products
|
||||||
|
ArrayList<WeightedContainer<DungeonType>> products = rule.products();
|
||||||
|
DungeonType nextType;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
nextType = getRandomDungeonType(random, products, groupedDungeons);
|
||||||
|
if (nextType != null)
|
||||||
|
{
|
||||||
|
//Initialize the set of excluded dungeons if needed
|
||||||
|
if (excludedDungeons == null && !config.allowDuplicatesInChain())
|
||||||
|
{
|
||||||
|
excludedDungeons = new HashSet<DungeonGenerator>(history);
|
||||||
|
}
|
||||||
|
|
||||||
|
//List which dungeons are allowed
|
||||||
|
ArrayList<DungeonGenerator> candidates;
|
||||||
|
ArrayList<DungeonGenerator> group = groupedDungeons.get(nextType.ID);
|
||||||
|
if (excludedDungeons != null && !excludedDungeons.isEmpty())
|
||||||
|
{
|
||||||
|
candidates = new ArrayList<DungeonGenerator>(group.size());
|
||||||
|
for (DungeonGenerator dungeon : group)
|
||||||
|
{
|
||||||
|
if (!excludedDungeons.contains(dungeon))
|
||||||
|
{
|
||||||
|
candidates.add(dungeon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
candidates = group;
|
||||||
|
}
|
||||||
|
if (!candidates.isEmpty())
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//None of the rules were applicable. Simply return a random dungeon.
|
||||||
|
return getRandomDungeon(random);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DungeonGenerator getRandomDungeon(Random random)
|
||||||
|
{
|
||||||
|
if (!allDungeons.isEmpty())
|
||||||
|
{
|
||||||
|
return getRandomDungeon(random, allDungeons);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DungeonType getRandomDungeonType(Random random, Collection<WeightedContainer<DungeonType>> types,
|
||||||
|
ArrayList<ArrayList<DungeonGenerator>> 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. 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())
|
||||||
|
{
|
||||||
|
//Pick a random dungeon type
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
WeightedContainer<DungeonType> resultContainer = (WeightedContainer<DungeonType>) WeightedRandom.getRandomItem(random, types);
|
||||||
|
|
||||||
|
//Check if there are any dungeons of that type
|
||||||
|
DungeonType selectedType = resultContainer.getData();
|
||||||
|
if (!groupedDungeons.get(selectedType.ID).isEmpty())
|
||||||
|
{
|
||||||
|
//Choose this type
|
||||||
|
return selectedType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//We can't use this type because there are no dungeons of this type
|
||||||
|
//Remove it from the list of types and try again
|
||||||
|
types.remove(resultContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//We have run out of types to try
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DungeonGenerator getRandomDungeon(Random random, Collection<DungeonGenerator> dungeons)
|
||||||
|
{
|
||||||
|
//Use Minecraft's WeightedRandom to select our dungeon. =D
|
||||||
|
ArrayList<WeightedContainer<DungeonGenerator>> weights =
|
||||||
|
new ArrayList<WeightedContainer<DungeonGenerator>>(dungeons.size());
|
||||||
|
for (DungeonGenerator dungeon : dungeons)
|
||||||
|
{
|
||||||
|
weights.add(new WeightedContainer<DungeonGenerator>(dungeon, dungeon.weight));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
WeightedContainer<DungeonGenerator> resultContainer = (WeightedContainer<DungeonGenerator>) WeightedRandom.getRandomItem(random, weights);
|
||||||
|
return (resultContainer != null) ? resultContainer.getData() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
126
StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java
Normal file
126
StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonPackConfig.java
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.dungeon.pack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class DungeonPackConfig
|
||||||
|
{
|
||||||
|
private String name;
|
||||||
|
private ArrayList<String> typeNames;
|
||||||
|
private boolean allowDuplicatesInChain;
|
||||||
|
private boolean allowPackChangeIn;
|
||||||
|
private boolean allowPackChangeOut;
|
||||||
|
private boolean distortDoorCoordinates;
|
||||||
|
private int packWeight;
|
||||||
|
private ArrayList<DungeonChainRuleDefinition> rules;
|
||||||
|
|
||||||
|
public DungeonPackConfig() { }
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private DungeonPackConfig(DungeonPackConfig source)
|
||||||
|
{
|
||||||
|
this.name = source.name;
|
||||||
|
this.typeNames = (ArrayList<String>) source.typeNames.clone();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<String> getTypeNames()
|
||||||
|
{
|
||||||
|
return typeNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTypeNames(ArrayList<String> typeNames)
|
||||||
|
{
|
||||||
|
this.typeNames = typeNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean allowDuplicatesInChain()
|
||||||
|
{
|
||||||
|
return allowDuplicatesInChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowDuplicatesInChain(boolean value)
|
||||||
|
{
|
||||||
|
allowDuplicatesInChain = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRules(ArrayList<DungeonChainRuleDefinition> rules)
|
||||||
|
{
|
||||||
|
this.rules = rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<DungeonChainRuleDefinition> getRules()
|
||||||
|
{
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean allowPackChangeIn()
|
||||||
|
{
|
||||||
|
return allowPackChangeIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowPackChangeIn(boolean value)
|
||||||
|
{
|
||||||
|
this.allowPackChangeIn = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean allowPackChangeOut()
|
||||||
|
{
|
||||||
|
return allowPackChangeOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowPackChangeOut(boolean value)
|
||||||
|
{
|
||||||
|
this.allowPackChangeOut = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPackWeight()
|
||||||
|
{
|
||||||
|
return packWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPackWeight(int packWeight)
|
||||||
|
{
|
||||||
|
this.packWeight = packWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDistortDoorCoordinates()
|
||||||
|
{
|
||||||
|
return distortDoorCoordinates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDistortDoorCoordinates(boolean value)
|
||||||
|
{
|
||||||
|
this.distortDoorCoordinates = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,385 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.dungeon.pack;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import StevenDimDoors.mod_pocketDim.util.BaseConfigurationProcessor;
|
||||||
|
import StevenDimDoors.mod_pocketDim.util.ConfigurationProcessingException;
|
||||||
|
import StevenDimDoors.mod_pocketDim.util.WeightedContainer;
|
||||||
|
|
||||||
|
import com.google.common.base.CharMatcher;
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
|
public class DungeonPackConfigReader extends BaseConfigurationProcessor<DungeonPackConfig>
|
||||||
|
{
|
||||||
|
private interface ILineProcessor
|
||||||
|
{
|
||||||
|
public void process(String line, DungeonPackConfig config) throws ConfigurationProcessingException;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Note: These constants aren't static so that the memory will be released once
|
||||||
|
//we're done using it an instance. These aren't objects that we need to hold
|
||||||
|
//onto throughout the lifetime of MC, only at loading time.
|
||||||
|
|
||||||
|
private final int CONFIG_VERSION = 1;
|
||||||
|
private final int LOOKAHEAD_LIMIT = 1024;
|
||||||
|
private final int MAX_PRODUCT_WEIGHT = 10000;
|
||||||
|
private final int DEFAULT_PRODUCT_WEIGHT = 100;
|
||||||
|
private final int MAX_DUNGEON_PACK_WEIGHT = 10000;
|
||||||
|
private final int DEFAULT_DUNGEON_PACK_WEIGHT = 100;
|
||||||
|
private final String COMMENT_MARKER = "##";
|
||||||
|
|
||||||
|
private final Pattern DUNGEON_TYPE_PATTERN = Pattern.compile("[A-Za-z0-9_\\-]{1,20}");
|
||||||
|
|
||||||
|
private final Splitter WHITESPACE_SPLITTER = Splitter.on(CharMatcher.WHITESPACE).omitEmptyStrings();
|
||||||
|
private final String SETTING_SEPARATOR = "=";
|
||||||
|
private final String RULE_SEPARATOR = "->";
|
||||||
|
private final String WEIGHT_SEPARATOR = "#";
|
||||||
|
|
||||||
|
public DungeonPackConfigReader() { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DungeonPackConfig readFromStream(InputStream inputStream) throws ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
BufferedReader reader = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DungeonPackConfig config = new DungeonPackConfig();
|
||||||
|
reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
|
||||||
|
//Check the config format version
|
||||||
|
int version = readVersion(reader);
|
||||||
|
if (version != CONFIG_VERSION)
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("The dungeon pack config has an incompatible version.");
|
||||||
|
}
|
||||||
|
|
||||||
|
config.setTypeNames(new ArrayList<String>());
|
||||||
|
config.setRules(new ArrayList<DungeonChainRuleDefinition>());
|
||||||
|
|
||||||
|
//Read the dungeon types
|
||||||
|
if (findSection("Types", reader))
|
||||||
|
{
|
||||||
|
processLines(reader, config, new DungeonTypeProcessor());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load default settings
|
||||||
|
config.setAllowDuplicatesInChain(true);
|
||||||
|
config.setAllowPackChangeIn(true);
|
||||||
|
config.setAllowPackChangeOut(true);
|
||||||
|
config.setDistortDoorCoordinates(false);
|
||||||
|
config.setPackWeight(DEFAULT_DUNGEON_PACK_WEIGHT);
|
||||||
|
|
||||||
|
//Read the settings section
|
||||||
|
if (findSection("Settings", reader))
|
||||||
|
{
|
||||||
|
processLines(reader, config, new DungeonSettingsParser());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Read the rules section
|
||||||
|
if (findSection("Rules", reader))
|
||||||
|
{
|
||||||
|
processLines(reader, config, new RuleDefinitionParser());
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
catch (ConfigurationProcessingException ex)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("An unexpected error occurred while trying to read the configuration file.", ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (reader != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
catch (IOException ex) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int readVersion(BufferedReader reader) throws ConfigurationProcessingException, IOException
|
||||||
|
{
|
||||||
|
String firstLine = reader.readLine();
|
||||||
|
String[] parts = firstLine.split("\\s", 0);
|
||||||
|
Integer version = null;
|
||||||
|
|
||||||
|
if (parts.length == 2 && parts[0].equalsIgnoreCase("version"))
|
||||||
|
{
|
||||||
|
version = Ints.tryParse(parts[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version == null)
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("Could not parse the config format version.");
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processLines(BufferedReader reader, DungeonPackConfig config, ILineProcessor processor) throws IOException, ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
String line;
|
||||||
|
|
||||||
|
while (reader.ready())
|
||||||
|
{
|
||||||
|
reader.mark(LOOKAHEAD_LIMIT);
|
||||||
|
line = reader.readLine();
|
||||||
|
if (!line.startsWith(COMMENT_MARKER))
|
||||||
|
{
|
||||||
|
line = line.trim();
|
||||||
|
if (line.length() > 0)
|
||||||
|
{
|
||||||
|
if (line.endsWith(":"))
|
||||||
|
{
|
||||||
|
//Consider this line a section header, reset the reader to undo consuming it
|
||||||
|
reader.reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processor.process(line, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean findSection(String name, BufferedReader reader) throws IOException, ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
boolean found = false;
|
||||||
|
boolean matched = false;
|
||||||
|
String line = null;
|
||||||
|
String label = name + ":";
|
||||||
|
|
||||||
|
//Find the next section header
|
||||||
|
//Ignore blank lines and comment lines, stop for headers, and throw an exception for anything else
|
||||||
|
while (!found && reader.ready())
|
||||||
|
{
|
||||||
|
reader.mark(LOOKAHEAD_LIMIT);
|
||||||
|
line = reader.readLine();
|
||||||
|
if (!line.startsWith(COMMENT_MARKER))
|
||||||
|
{
|
||||||
|
line = line.trim();
|
||||||
|
if (line.length() > 0)
|
||||||
|
{
|
||||||
|
if (line.endsWith(":"))
|
||||||
|
{
|
||||||
|
//Consider this line a section header
|
||||||
|
found = true;
|
||||||
|
matched = line.equalsIgnoreCase(label);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//This line is invalid
|
||||||
|
throw new ConfigurationProcessingException("The dungeon pack config has an incorrect line where a section was expected: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if the header matches the one we're looking for.
|
||||||
|
//If it doesn't match, undo consuming the line so it can be read later.
|
||||||
|
if (found && !matched)
|
||||||
|
{
|
||||||
|
reader.reset();
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DungeonTypeProcessor implements ILineProcessor
|
||||||
|
{
|
||||||
|
public void process(String line, DungeonPackConfig config) throws ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
List<String> typeNames = config.getTypeNames();
|
||||||
|
|
||||||
|
//Check if the dungeon type has a name that meets our restrictions
|
||||||
|
if (DUNGEON_TYPE_PATTERN.matcher(line).matches())
|
||||||
|
{
|
||||||
|
//Ignore duplicate dungeon types
|
||||||
|
line = line.toUpperCase();
|
||||||
|
if (!typeNames.contains(line))
|
||||||
|
{
|
||||||
|
typeNames.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("The dungeon pack config has a dungeon type with illegal characters in its name: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DungeonSettingsParser implements ILineProcessor
|
||||||
|
{
|
||||||
|
public void process(String line, DungeonPackConfig config) throws ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
//The various settings that we support will be hardcoded here.
|
||||||
|
//In the future, if we get more settings, then this should be
|
||||||
|
//refactored to use a more lookup-driven approach.
|
||||||
|
|
||||||
|
boolean valid = true;
|
||||||
|
String[] settingParts = line.split(SETTING_SEPARATOR, 2);
|
||||||
|
if (settingParts.length == 2)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String name = settingParts[0];
|
||||||
|
String value = settingParts[1];
|
||||||
|
if (name.equalsIgnoreCase("AllowDuplicatesInChain"))
|
||||||
|
{
|
||||||
|
config.setAllowDuplicatesInChain(parseBoolean(value));
|
||||||
|
}
|
||||||
|
else if (name.equalsIgnoreCase("AllowPackChangeOut"))
|
||||||
|
{
|
||||||
|
config.setAllowPackChangeOut(parseBoolean(value));
|
||||||
|
}
|
||||||
|
else if (name.equalsIgnoreCase("AllowPackChangeIn"))
|
||||||
|
{
|
||||||
|
config.setAllowPackChangeIn(parseBoolean(value));
|
||||||
|
}
|
||||||
|
else if (name.equalsIgnoreCase("DistortDoorCoordinates"))
|
||||||
|
{
|
||||||
|
config.setDistortDoorCoordinates(parseBoolean(value));
|
||||||
|
}
|
||||||
|
else if (name.equalsIgnoreCase("PackWeight"))
|
||||||
|
{
|
||||||
|
int weight = Integer.parseInt(value);
|
||||||
|
if (weight >= 0 && weight <= MAX_DUNGEON_PACK_WEIGHT)
|
||||||
|
{
|
||||||
|
config.setPackWeight(weight);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("The dungeon pack config has an invalid setting: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RuleDefinitionParser implements ILineProcessor
|
||||||
|
{
|
||||||
|
public void process(String definition, DungeonPackConfig config) throws ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
String[] ruleParts;
|
||||||
|
String[] productParts;
|
||||||
|
String ruleCondition;
|
||||||
|
String ruleProduct;
|
||||||
|
ArrayList<String> condition;
|
||||||
|
ArrayList<WeightedContainer<String>> products;
|
||||||
|
List<String> typeNames = config.getTypeNames();
|
||||||
|
|
||||||
|
ruleParts = definition.toUpperCase().split(RULE_SEPARATOR, -1);
|
||||||
|
if (ruleParts.length != 2)
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("The dungeon pack config has an invalid rule: " + definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleCondition = ruleParts[0];
|
||||||
|
ruleProduct = ruleParts[1];
|
||||||
|
condition = new ArrayList<String>();
|
||||||
|
products = new ArrayList<WeightedContainer<String>>();
|
||||||
|
|
||||||
|
for (String typeName : WHITESPACE_SPLITTER.split(ruleCondition))
|
||||||
|
{
|
||||||
|
if (isKnownDungeonType(typeName, typeNames))
|
||||||
|
{
|
||||||
|
condition.add(typeName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("The dungeon pack config has a rule condition with an unknown dungeon type: " + typeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String product : WHITESPACE_SPLITTER.split(ruleProduct))
|
||||||
|
{
|
||||||
|
Integer weight;
|
||||||
|
String typeName;
|
||||||
|
|
||||||
|
productParts = product.split(WEIGHT_SEPARATOR, -1);
|
||||||
|
if (productParts.length > 2 || productParts.length == 0)
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("The dungeon pack config has a rule with an invalid product: " + product);
|
||||||
|
}
|
||||||
|
|
||||||
|
typeName = productParts[0];
|
||||||
|
if (isKnownDungeonType(typeName, typeNames))
|
||||||
|
{
|
||||||
|
if (productParts.length > 1)
|
||||||
|
{
|
||||||
|
weight = Ints.tryParse(productParts[1]);
|
||||||
|
if (weight == null || (weight > MAX_PRODUCT_WEIGHT) || (weight < 0))
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("The dungeon pack config has a rule with an invalid product weight: " + product);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
weight = DEFAULT_PRODUCT_WEIGHT;
|
||||||
|
}
|
||||||
|
products.add(new WeightedContainer<String>(typeName, weight));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ConfigurationProcessingException("The dungeon pack config has an unknown dungeon type in a rule: " + typeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.getRules().add( new DungeonChainRuleDefinition(condition, products) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isKnownDungeonType(String typeName, List<String> typeNames)
|
||||||
|
{
|
||||||
|
return typeName.equals(DungeonType.WILDCARD_TYPE.Name) || typeNames.contains(typeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean parseBoolean(String value)
|
||||||
|
{
|
||||||
|
if (value.equalsIgnoreCase("true"))
|
||||||
|
return true;
|
||||||
|
if (value.equalsIgnoreCase("false"))
|
||||||
|
return false;
|
||||||
|
throw new IllegalArgumentException("The boolean value must be either \"true\" or \"false\", ignoring case.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canWrite()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToStream(OutputStream outputStream, DungeonPackConfig data) throws ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("DungeonPackConfigReader does not support writing.");
|
||||||
|
}
|
||||||
|
}
|
||||||
48
StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonType.java
Normal file
48
StevenDimDoors/mod_pocketDim/dungeon/pack/DungeonType.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.dungeon.pack;
|
||||||
|
|
||||||
|
public class DungeonType implements Comparable<DungeonType>
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
public DungeonType(DungeonPack owner, String name, int id)
|
||||||
|
{
|
||||||
|
Owner = owner;
|
||||||
|
Name = name;
|
||||||
|
this.ID = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(DungeonType other)
|
||||||
|
{
|
||||||
|
return this.ID - other.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other)
|
||||||
|
{
|
||||||
|
return equals((DungeonType) other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(DungeonType other)
|
||||||
|
{
|
||||||
|
if (this == other)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (this == null || other == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (this.ID == other.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
final int prime = 2039;
|
||||||
|
return prime * ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,13 +7,13 @@ import java.io.InputStreamReader;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import net.minecraft.util.WeightedRandom;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import StevenDimDoors.mod_pocketDim.DDProperties;
|
import StevenDimDoors.mod_pocketDim.DDProperties;
|
||||||
import StevenDimDoors.mod_pocketDim.DimData;
|
import StevenDimDoors.mod_pocketDim.DimData;
|
||||||
@@ -21,15 +21,20 @@ import StevenDimDoors.mod_pocketDim.DungeonGenerator;
|
|||||||
import StevenDimDoors.mod_pocketDim.LinkData;
|
import StevenDimDoors.mod_pocketDim.LinkData;
|
||||||
import StevenDimDoors.mod_pocketDim.mod_pocketDim;
|
import StevenDimDoors.mod_pocketDim.mod_pocketDim;
|
||||||
import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic;
|
import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic;
|
||||||
|
import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPack;
|
||||||
|
import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPackConfig;
|
||||||
|
import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPackConfigReader;
|
||||||
|
import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonType;
|
||||||
import StevenDimDoors.mod_pocketDim.items.itemDimDoor;
|
import StevenDimDoors.mod_pocketDim.items.itemDimDoor;
|
||||||
import StevenDimDoors.mod_pocketDim.util.WeightedContainer;
|
import StevenDimDoors.mod_pocketDim.util.ConfigurationProcessingException;
|
||||||
|
|
||||||
public class DungeonHelper
|
public class DungeonHelper
|
||||||
{
|
{
|
||||||
private static DungeonHelper instance = null;
|
private static DungeonHelper instance = null;
|
||||||
private static DDProperties properties = 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_UP_SCHEMATIC_PATH = "/schematics/core/simpleStairsUp.schematic";
|
||||||
private static final String DEFAULT_DOWN_SCHEMATIC_PATH = "/schematics/core/simpleStairsDown.schematic";
|
private static final String DEFAULT_DOWN_SCHEMATIC_PATH = "/schematics/core/simpleStairsDown.schematic";
|
||||||
@@ -45,72 +50,22 @@ public class DungeonHelper
|
|||||||
public static final short MAX_DUNGEON_HEIGHT = MAX_DUNGEON_WIDTH;
|
public static final short MAX_DUNGEON_HEIGHT = MAX_DUNGEON_WIDTH;
|
||||||
public static final short MAX_DUNGEON_LENGTH = 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 Random rand = new Random();
|
|
||||||
|
|
||||||
private ArrayList<DungeonGenerator> untaggedDungeons = new ArrayList<DungeonGenerator>();
|
private ArrayList<DungeonGenerator> untaggedDungeons = new ArrayList<DungeonGenerator>();
|
||||||
private ArrayList<DungeonGenerator> registeredDungeons = new ArrayList<DungeonGenerator>();
|
private ArrayList<DungeonGenerator> registeredDungeons = new ArrayList<DungeonGenerator>();
|
||||||
|
|
||||||
private ArrayList<DungeonGenerator> simpleHalls = new ArrayList<DungeonGenerator>();
|
|
||||||
private ArrayList<DungeonGenerator> complexHalls = new ArrayList<DungeonGenerator>();
|
|
||||||
private ArrayList<DungeonGenerator> deadEnds = new ArrayList<DungeonGenerator>();
|
|
||||||
private ArrayList<DungeonGenerator> hubs = new ArrayList<DungeonGenerator>();
|
|
||||||
private ArrayList<DungeonGenerator> mazes = new ArrayList<DungeonGenerator>();
|
|
||||||
private ArrayList<DungeonGenerator> pistonTraps = new ArrayList<DungeonGenerator>();
|
|
||||||
private ArrayList<DungeonGenerator> exits = new ArrayList<DungeonGenerator>();
|
|
||||||
|
|
||||||
|
public DungeonPack RuinsPack;
|
||||||
|
|
||||||
private DungeonGenerator defaultUp;
|
private DungeonGenerator defaultUp;
|
||||||
private DungeonGenerator defaultDown;
|
private DungeonGenerator defaultDown;
|
||||||
private DungeonGenerator defaultError;
|
private DungeonGenerator defaultError;
|
||||||
|
|
||||||
private HashSet<String> dungeonTypeChecker;
|
|
||||||
private HashMap<String, ArrayList<DungeonGenerator>> dungeonTypeMapping;
|
|
||||||
|
|
||||||
private DungeonHelper()
|
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<String>();
|
|
||||||
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<String, ArrayList<DungeonGenerator>>();
|
|
||||||
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
|
//Load our reference to the DDProperties singleton
|
||||||
if (properties == null)
|
if (properties == null)
|
||||||
properties = DDProperties.instance();
|
properties = DDProperties.instance();
|
||||||
|
|
||||||
registerCustomDungeons();
|
registerDungeons();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DungeonHelper initialize()
|
public static DungeonHelper initialize()
|
||||||
@@ -138,15 +93,39 @@ public class DungeonHelper
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerCustomDungeons()
|
private void registerDungeons()
|
||||||
{
|
{
|
||||||
File file = new File(properties.CustomSchematicDirectory);
|
File file = new File(properties.CustomSchematicDirectory);
|
||||||
if (file.exists() || file.mkdir())
|
if (file.exists() || file.mkdir())
|
||||||
{
|
{
|
||||||
copyfile.copyFile(DUNGEON_CREATION_GUIDE_SOURCE_PATH, file.getAbsolutePath() + "/How_to_add_dungeons.txt");
|
copyfile.copyFile(DUNGEON_CREATION_GUIDE_SOURCE_PATH, file.getAbsolutePath() + "/How_to_add_dungeons.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RuinsPack = new DungeonPack(createRuinsConfig());
|
||||||
|
|
||||||
registerBundledDungeons();
|
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.
|
||||||
|
|
||||||
|
DungeonPackConfig config;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
config = (new DungeonPackConfigReader()).readFromResource("/schematics/ruins/rules.txt");
|
||||||
|
config.setName("ruins");
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DungeonGenerator> getRegisteredDungeons()
|
public List<DungeonGenerator> getRegisteredDungeons()
|
||||||
@@ -189,7 +168,7 @@ public class DungeonHelper
|
|||||||
public boolean validateDungeonType(String type)
|
public boolean validateDungeonType(String type)
|
||||||
{
|
{
|
||||||
//Check if the dungeon type is valid
|
//Check if the dungeon type is valid
|
||||||
return dungeonTypeChecker.contains(type.toLowerCase());
|
return RuinsPack.isKnownType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validateSchematicName(String name)
|
public boolean validateSchematicName(String name)
|
||||||
@@ -206,11 +185,11 @@ public class DungeonHelper
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
//Check if the dungeon type is valid
|
//Check if the dungeon type is valid
|
||||||
if (!dungeonTypeChecker.contains(dungeonData[0].toLowerCase()))
|
if (!validateDungeonType(dungeonData[0]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
//Check if the name is valid
|
//Check if the name is valid
|
||||||
if (!SchematicNamePattern.matcher(dungeonData[1]).matches())
|
if (!SCHEMATIC_NAME_PATTERN.matcher(dungeonData[1]).matches())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
//Check if the open/closed flag is present
|
//Check if the open/closed flag is present
|
||||||
@@ -249,14 +228,14 @@ public class DungeonHelper
|
|||||||
//Strip off the file extension while splitting the file name
|
//Strip off the file extension while splitting the file name
|
||||||
String[] dungeonData = name.substring(0, name.length() - SCHEMATIC_FILE_EXTENSION.length()).split("_");
|
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");
|
boolean isOpen = dungeonData[2].equalsIgnoreCase("open");
|
||||||
int weight = (dungeonData.length == 4) ? Integer.parseInt(dungeonData[3]) : DEFAULT_DUNGEON_WEIGHT;
|
int weight = (dungeonData.length == 4) ? Integer.parseInt(dungeonData[3]) : DEFAULT_DUNGEON_WEIGHT;
|
||||||
|
|
||||||
//Add this custom dungeon to the list corresponding to its type
|
//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);
|
registeredDungeons.add(generator);
|
||||||
if (verbose)
|
if (verbose)
|
||||||
{
|
{
|
||||||
@@ -269,7 +248,7 @@ public class DungeonHelper
|
|||||||
{
|
{
|
||||||
System.out.println("Could not parse dungeon filename, not adding dungeon to generation lists");
|
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);
|
System.out.println("Registered untagged dungeon: " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,7 +259,7 @@ public class DungeonHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importCustomDungeons(String path)
|
private void registerCustomDungeons(String path)
|
||||||
{
|
{
|
||||||
File directory = new File(path);
|
File directory = new File(path);
|
||||||
File[] schematicNames = directory.listFiles();
|
File[] schematicNames = directory.listFiles();
|
||||||
@@ -301,9 +280,9 @@ public class DungeonHelper
|
|||||||
{
|
{
|
||||||
//Register the core schematics
|
//Register the core schematics
|
||||||
//These are used for debugging and in case of unusual errors while loading dungeons
|
//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);
|
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);
|
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);
|
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
|
//Open the list of dungeons packaged with our mod and register their schematics
|
||||||
InputStream listStream = this.getClass().getResourceAsStream(BUNDLED_DUNGEONS_LIST_PATH);
|
InputStream listStream = this.getClass().getResourceAsStream(BUNDLED_DUNGEONS_LIST_PATH);
|
||||||
@@ -354,157 +333,29 @@ public class DungeonHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateDungeonLink(LinkData incoming)
|
public void generateDungeonLink(LinkData inbound, DungeonPack pack, Random random)
|
||||||
{
|
{
|
||||||
DungeonGenerator dungeon;
|
DungeonGenerator selection;
|
||||||
int depth = dimHelper.instance.getDimDepth(incoming.locDimID);
|
|
||||||
int depthWeight = rand.nextInt(depth + 2) + rand.nextInt(depth + 2) - 2;
|
|
||||||
|
|
||||||
int count = 10;
|
|
||||||
boolean flag = true;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
selection = pack.getNextDungeon(inbound, random);
|
||||||
if (incoming.destYCoord > 15)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
count--;
|
|
||||||
flag = true;
|
|
||||||
//Select a dungeon at random, taking into account its weight
|
|
||||||
dungeon = getRandomDungeon(rand, registeredDungeons);
|
|
||||||
|
|
||||||
if (depth <= 1)
|
|
||||||
{
|
|
||||||
if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = complexHalls.get(rand.nextInt(complexHalls.size()));
|
|
||||||
|
|
||||||
}
|
|
||||||
else if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = hubs.get(rand.nextInt(hubs.size()));
|
|
||||||
|
|
||||||
}
|
|
||||||
else if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = hubs.get(rand.nextInt(hubs.size()));
|
|
||||||
|
|
||||||
}
|
|
||||||
else if(deadEnds.contains(dungeon)||exits.contains(dungeon))
|
|
||||||
{
|
|
||||||
flag=false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (depth <= 3 && (deadEnds.contains(dungeon) || exits.contains(dungeon) || rand.nextBoolean()))
|
|
||||||
{
|
|
||||||
if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = hubs.get(rand.nextInt(hubs.size()));
|
|
||||||
|
|
||||||
}
|
|
||||||
else if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = mazes.get(rand.nextInt(mazes.size()));
|
|
||||||
}
|
|
||||||
else if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = pistonTraps.get(rand.nextInt(pistonTraps.size()));
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
flag = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (rand.nextInt(3) == 0 && !complexHalls.contains(dungeon))
|
|
||||||
{
|
|
||||||
if (rand.nextInt(3) == 0)
|
|
||||||
{
|
|
||||||
dungeon = simpleHalls.get(rand.nextInt(simpleHalls.size()));
|
|
||||||
}
|
|
||||||
else if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = pistonTraps.get(rand.nextInt(pistonTraps.size()));
|
|
||||||
}
|
|
||||||
else if (depth < 4)
|
|
||||||
{
|
|
||||||
dungeon = hubs.get(rand.nextInt(hubs.size()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (depthWeight - depthWeight / 2 > depth -4 && (deadEnds.contains(dungeon) || exits.contains(dungeon)))
|
|
||||||
{
|
|
||||||
if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = simpleHalls.get(rand.nextInt(simpleHalls.size()));
|
|
||||||
}
|
|
||||||
else if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = complexHalls.get(rand.nextInt(complexHalls.size()));
|
|
||||||
}
|
|
||||||
else if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = pistonTraps.get(rand.nextInt(pistonTraps.size()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
flag = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (depthWeight > 7 && hubs.contains(dungeon))
|
|
||||||
{
|
|
||||||
if(rand.nextInt(12)+5<depthWeight)
|
|
||||||
{
|
|
||||||
if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = exits.get(rand.nextInt(exits.size()));
|
|
||||||
}
|
|
||||||
else if(rand.nextBoolean())
|
|
||||||
{
|
|
||||||
dungeon = deadEnds.get(rand.nextInt(deadEnds.size()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dungeon = pistonTraps.get(rand.nextInt(pistonTraps.size()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
flag = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (depth > 10 && hubs.contains(dungeon))
|
|
||||||
{
|
|
||||||
flag = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(getDungeonDataInChain(dimHelper.instance.getDimData(incoming.locDimID)).contains(dungeon))
|
|
||||||
{
|
|
||||||
flag=false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (!flag && count > 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dungeon = defaultUp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
System.err.println("An exception occurred while selecting a dungeon:");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
if (registeredDungeons.size() > 0)
|
|
||||||
|
if (!pack.isEmpty())
|
||||||
{
|
{
|
||||||
//Select a random dungeon
|
selection = pack.getRandomDungeon(random);
|
||||||
dungeon = getRandomDungeon(rand, registeredDungeons);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return;
|
selection = defaultError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dimHelper.instance.getDimData(incoming.destDimID).dungeonGenerator = dungeon;
|
dimHelper.instance.getDimData(inbound.destDimID).dungeonGenerator = selection;
|
||||||
//dimHelper.instance.getDimData(incoming.destDimID).dungeonGenerator = defaultUp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> getDungeonNames() {
|
public Collection<String> getDungeonNames() {
|
||||||
@@ -539,50 +390,78 @@ public class DungeonHelper
|
|||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DungeonGenerator getRandomDungeon(Random random, Collection<DungeonGenerator> dungeons)
|
public static ArrayList<DungeonGenerator> getDungeonChainHistory(DimData dimData, DungeonPack pack, int maxSize)
|
||||||
{
|
{
|
||||||
//Use Minecraft's WeightedRandom to select our dungeon. =D
|
//TODO: I've improved this code for the time being. However, searching across links is a flawed approach. A player could
|
||||||
ArrayList<WeightedContainer<DungeonGenerator>> weights =
|
//manipulate the output of this function by setting up links to mislead our algorithm or by removing links.
|
||||||
new ArrayList<WeightedContainer<DungeonGenerator>>(dungeons.size());
|
//Dimensions MUST have built-in records of their parent dimensions in the future. ~SenseiKiwi
|
||||||
for (DungeonGenerator dungeon : dungeons)
|
|
||||||
{
|
|
||||||
weights.add(new WeightedContainer<DungeonGenerator>(dungeon, dungeon.weight));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
ArrayList<DungeonGenerator> history = new ArrayList<DungeonGenerator>();
|
||||||
WeightedContainer<DungeonGenerator> resultContainer = (WeightedContainer<DungeonGenerator>) WeightedRandom.getRandomItem(random, weights);
|
DimData tailDim = dimData;
|
||||||
return (resultContainer != null) ? resultContainer.getData() : null;
|
boolean found = true;
|
||||||
}
|
|
||||||
public static ArrayList<DungeonGenerator> getDungeonDataInChain(DimData dimData)
|
if (dimData.dungeonGenerator == null || dimData.dungeonGenerator.getDungeonType().Owner != pack || maxSize < 1)
|
||||||
{
|
|
||||||
DimData startingDim = dimHelper.instance.getDimData(dimHelper.instance.getLinkDataFromCoords(dimData.exitDimLink.destXCoord, dimData.exitDimLink.destYCoord, dimData.exitDimLink.destZCoord, dimData.exitDimLink.destDimID).destDimID);
|
|
||||||
|
|
||||||
return getDungeonDataBelow(startingDim);
|
|
||||||
}
|
|
||||||
private static ArrayList<DungeonGenerator> getDungeonDataBelow(DimData dimData)
|
|
||||||
{
|
|
||||||
ArrayList<DungeonGenerator> dungeonData = new ArrayList<DungeonGenerator>();
|
|
||||||
if(dimData.dungeonGenerator!=null)
|
|
||||||
{
|
{
|
||||||
dungeonData.add(dimData.dungeonGenerator);
|
//The initial dimension is already outside our pack. Return an empty list.
|
||||||
|
return history;
|
||||||
for(LinkData link : dimData.getLinksInDim())
|
}
|
||||||
|
history.add(dimData.dungeonGenerator);
|
||||||
|
|
||||||
|
for (int count = 1; count < maxSize && found; count++)
|
||||||
|
{
|
||||||
|
found = false;
|
||||||
|
for (LinkData link : tailDim.getLinksInDim())
|
||||||
{
|
{
|
||||||
if(dimHelper.dimList.containsKey(link.destDimID))
|
DimData neighbor = dimHelper.instance.getDimData(link.destDimID);
|
||||||
|
if (neighbor.depth == tailDim.depth - 1 && neighbor.dungeonGenerator != null &&
|
||||||
|
neighbor.dungeonGenerator.getDungeonType().Owner == pack)
|
||||||
{
|
{
|
||||||
if(dimHelper.instance.getDimData(link.destDimID).dungeonGenerator!=null&&dimHelper.instance.getDimDepth(link.destDimID)==dimData.depth+1)
|
tailDim = neighbor;
|
||||||
{
|
history.add(tailDim.dungeonGenerator);
|
||||||
for(DungeonGenerator dungeonGen :getDungeonDataBelow(dimHelper.instance.getDimData(link.destDimID)) )
|
found = true;
|
||||||
{
|
break;
|
||||||
if(!dungeonData.contains(dungeonGen))
|
|
||||||
{
|
|
||||||
dungeonData.add(dungeonGen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dungeonData;
|
return history;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
//manipulate the output of this function by setting up links to mislead our algorithm or by removing links.
|
||||||
|
//Dimensions MUST have built-in records of their parent dimensions in the future. ~SenseiKiwi
|
||||||
|
|
||||||
|
dimHelper helper = dimHelper.instance;
|
||||||
|
ArrayList<DungeonGenerator> dungeons = new ArrayList<DungeonGenerator>();
|
||||||
|
DimData root = helper.getDimData(helper.getLinkDataFromCoords(dimData.exitDimLink.destXCoord, dimData.exitDimLink.destYCoord, dimData.exitDimLink.destZCoord, dimData.exitDimLink.destDimID).destDimID);
|
||||||
|
HashSet<DimData> checked = new HashSet<DimData>();
|
||||||
|
Queue<DimData> pendingDimensions = new LinkedList<DimData>();
|
||||||
|
|
||||||
|
if (root.dungeonGenerator == null)
|
||||||
|
{
|
||||||
|
return dungeons;
|
||||||
|
}
|
||||||
|
pendingDimensions.add(root);
|
||||||
|
checked.add(root);
|
||||||
|
|
||||||
|
while (dungeons.size() < maxSize && !pendingDimensions.isEmpty())
|
||||||
|
{
|
||||||
|
DimData current = pendingDimensions.remove();
|
||||||
|
for (LinkData link : current.getLinksInDim())
|
||||||
|
{
|
||||||
|
DimData child = helper.getDimData(link.destDimID);
|
||||||
|
if (child.depth == current.depth + 1 && child.dungeonGenerator != null && checked.add(child))
|
||||||
|
{
|
||||||
|
dungeons.add(child.dungeonGenerator);
|
||||||
|
pendingDimensions.add(child);
|
||||||
|
}
|
||||||
|
if (dungeons.size() == maxSize)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dungeons;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1317,17 +1317,9 @@ public class dimHelper extends DimensionManager
|
|||||||
{
|
{
|
||||||
return dimHelper.instance.getDimData(world.provider.dimensionId);
|
return dimHelper.instance.getDimData(world.provider.dimensionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DimData getDimData(int dimID)
|
public DimData getDimData(int dimID)
|
||||||
{
|
{
|
||||||
if(dimHelper.dimList.containsKey(dimID))
|
return dimHelper.dimList.get(dimID);
|
||||||
{
|
|
||||||
return dimHelper.dimList.get(dimID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public abstract class BaseConfigurationProcessor<T>
|
||||||
|
{
|
||||||
|
public BaseConfigurationProcessor() { }
|
||||||
|
|
||||||
|
public boolean canRead()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canWrite()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T readFromFile(String path) throws FileNotFoundException, ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
return readFromFile(new File(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public T readFromFile(File file) throws FileNotFoundException, ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
return readFromStream(new FileInputStream(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
public T readFromResource(String resourcePath) throws ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
return readFromStream(this.getClass().getResourceAsStream(resourcePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract T readFromStream(InputStream inputStream) throws ConfigurationProcessingException;
|
||||||
|
|
||||||
|
public void writeToFile(File file, T data) throws FileNotFoundException, ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
writeToStream(new FileOutputStream(file), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToFile(String path, T data) throws FileNotFoundException, ConfigurationProcessingException
|
||||||
|
{
|
||||||
|
writeToFile(new File(path), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void writeToStream(OutputStream outputStream, T data) throws ConfigurationProcessingException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package StevenDimDoors.mod_pocketDim.util;
|
||||||
|
|
||||||
|
public class ConfigurationProcessingException extends Exception
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = -4525298050874891911L;
|
||||||
|
|
||||||
|
public ConfigurationProcessingException()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationProcessingException(String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationProcessingException(String message, Throwable cause)
|
||||||
|
{
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,11 +17,15 @@ public class WeightedContainer<T> extends WeightedRandomItem {
|
|||||||
{
|
{
|
||||||
super(weight);
|
super(weight);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
super.itemWeight = weight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getData()
|
public T getData()
|
||||||
{
|
{
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WeightedContainer<T> clone()
|
||||||
|
{
|
||||||
|
return new WeightedContainer<T>(data, itemWeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
schematics/ruins/rules.txt
Normal file
35
schematics/ruins/rules.txt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
Version 1
|
||||||
|
Types:
|
||||||
|
Hub
|
||||||
|
Trap
|
||||||
|
SimpleHall
|
||||||
|
ComplexHall
|
||||||
|
Exit
|
||||||
|
DeadEnd
|
||||||
|
Maze
|
||||||
|
|
||||||
|
Settings:
|
||||||
|
AllowDuplicatesInChain = false
|
||||||
|
AllowPackChangeOut = true
|
||||||
|
DistortDoorCoordinates = true
|
||||||
|
|
||||||
|
## Prevent this pack from being selected for transitioning in once we've transitioned out
|
||||||
|
AllowPackChangeIn = false
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
Exit -> DeadEnd Exit
|
||||||
|
|
||||||
|
DeadEnd -> DeadEnd Exit
|
||||||
|
|
||||||
|
? ? ? ? ? ? ? ? -> Trap#20 SimpleHall#40 ComplexHall#10 Exit#20 DeadEnd#10
|
||||||
|
|
||||||
|
? ? ? ? -> Trap#18 SimpleHall#40 ComplexHall#10 Exit#18 DeadEnd#10 Hub#4
|
||||||
|
|
||||||
|
? ? ? -> ComplexHall Hub Trap SimpleHall Maze
|
||||||
|
|
||||||
|
? ? -> ComplexHall Hub Trap SimpleHall Maze
|
||||||
|
|
||||||
|
? -> ComplexHall#40 Hub#30 Trap#10 SimpleHall#10 Maze#10
|
||||||
|
|
||||||
|
-> ComplexHall#40 Hub#30 Trap#10 SimpleHall#10 Maze#10
|
||||||
Reference in New Issue
Block a user