THE UPDATE

Merging months of dev work into master. The update is playable, but
untested.
This commit is contained in:
StevenRS11
2013-11-05 18:15:23 -05:00
parent be89913263
commit a04a266c17
154 changed files with 8826 additions and 8272 deletions

View File

@@ -0,0 +1,32 @@
package StevenDimDoors.mod_pocketDim.helpers;
import java.util.List;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeChunkManager;
import net.minecraftforge.common.ForgeChunkManager.LoadingCallback;
import net.minecraftforge.common.ForgeChunkManager.Ticket;
import StevenDimDoors.mod_pocketDim.IChunkLoader;
import StevenDimDoors.mod_pocketDim.mod_pocketDim;
import StevenDimDoors.mod_pocketDim.tileentities.TileEntityDimDoorGold;
import com.google.common.collect.Lists;
public class ChunkLoaderHelper implements LoadingCallback
{
@Override
public void ticketsLoaded(List<Ticket> tickets, World world)
{
for (Ticket ticket : tickets)
{
int goldDimDoorX = ticket.getModData().getInteger("goldDimDoorX");
int goldDimDoorY = ticket.getModData().getInteger("goldDimDoorY");
int goldDimDoorZ = ticket.getModData().getInteger("goldDimDoorZ");
IChunkLoader tile = (IChunkLoader) world.getBlockTileEntity(goldDimDoorX, goldDimDoorY, goldDimDoorZ);
tile.forceChunkLoading(ticket,goldDimDoorX,goldDimDoorZ);
}
}
}

View File

@@ -0,0 +1,83 @@
package StevenDimDoors.mod_pocketDim.helpers;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import StevenDimDoors.mod_pocketDim.core.DimLink;
import StevenDimDoors.mod_pocketDim.core.IDimRegistrationCallback;
import StevenDimDoors.mod_pocketDim.core.LinkTypes;
import StevenDimDoors.mod_pocketDim.core.NewDimData;
import StevenDimDoors.mod_pocketDim.util.Point4D;
public class Compactor
{
private static class DimComparator implements Comparator<NewDimData>
{
@Override
public int compare(NewDimData a, NewDimData b)
{
return a.id() - b.id();
}
}
public static void write(Collection<? extends NewDimData> values, DataOutputStream output) throws IOException
{
// SenseiKiwi: Just encode the data straight up for now. I'll implement fancier compression later.
output.writeInt(values.size());
for (NewDimData dimension : values)
{
output.writeInt(dimension.id());
output.writeInt(dimension.root().id());
output.writeInt(dimension.linkCount());
for (DimLink link : dimension.links())
{
Point4D.write(link.source(), output);
}
}
// Note to self: the root ID can be "compressed" by grouping
// dimensions by their root ID and then only sending it once
/*
// To compress the dimension IDs, we'll sort them by ID
// and write the _difference_ between their ID numbers.
NewDimData[] dimensions = new NewDimData[values.size()];
dimensions = values.toArray(dimensions);
Arrays.sort(dimensions, new DimComparator());
*/
}
public static void readDimensions(DataInputStream input, IDimRegistrationCallback callback) throws IOException
{
// Read in the dimensions one by one. Make sure we register root dimensions before
// attempting to register the dimensions under them.
HashSet<Integer> rootIDs = new HashSet<Integer>();
int dimCount = input.readInt();
for (int k = 0; k < dimCount; k++)
{
int id = input.readInt();
int rootID = input.readInt();
if (rootIDs.add(rootID))
{
callback.registerDimension(rootID, rootID);
}
// Don't check if (id != rootID) - we want to retrieve the reference anyway
NewDimData dimension = callback.registerDimension(id, rootID);
int linkCount = input.readInt();
for (int h = 0; h < linkCount; h++)
{
Point4D source = Point4D.read(input);
dimension.createLink(source.getX(), source.getY(), source.getZ(), LinkTypes.CLIENT_SIDE);
}
}
}
}

View File

@@ -20,40 +20,24 @@ 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;
import StevenDimDoors.mod_pocketDim.DungeonGenerator;
import StevenDimDoors.mod_pocketDim.LinkData;
import StevenDimDoors.mod_pocketDim.mod_pocketDim;
import StevenDimDoors.mod_pocketDim.core.DimLink;
import StevenDimDoors.mod_pocketDim.core.LinkTypes;
import StevenDimDoors.mod_pocketDim.core.NewDimData;
import StevenDimDoors.mod_pocketDim.core.PocketManager;
import StevenDimDoors.mod_pocketDim.dungeon.DungeonData;
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.ItemDimensionalDoor;
import StevenDimDoors.mod_pocketDim.util.ConfigurationProcessingException;
import StevenDimDoors.mod_pocketDim.util.FileFilters;
import StevenDimDoors.mod_pocketDim.util.WeightedContainer;
public class DungeonHelper
{
//TODO: File-handling functionality should be spun off to a helper class later
private static class DirectoryFilter implements FileFilter
{
@Override
public boolean accept(File file)
{
return file.isDirectory();
}
}
private static class SchematicFileFilter implements FileFilter
{
@Override
public boolean accept(File file)
{
return file.isFile() && file.getName().endsWith(SCHEMATIC_FILE_EXTENSION);
}
}
private static DungeonHelper instance = null;
private static DDProperties properties = null;
@@ -62,8 +46,6 @@ public class DungeonHelper
public static final String SCHEMATIC_FILE_EXTENSION = ".schematic";
private static final String DEFAULT_UP_SCHEMATIC_PATH = "/schematics/core/simpleStairsUp.schematic";
private static final String DEFAULT_DOWN_SCHEMATIC_PATH = "/schematics/core/simpleStairsDown.schematic";
private static final String DEFAULT_ERROR_SCHEMATIC_PATH = "/schematics/core/somethingBroke.schematic";
private static final String DUNGEON_CREATION_GUIDE_SOURCE_PATH = "/mods/DimDoors/text/How_to_add_dungeons.txt";
private static final String RUINS_PACK_PATH = "/schematics/ruins";
@@ -86,16 +68,14 @@ public class DungeonHelper
public static final short MAX_DUNGEON_HEIGHT = MAX_DUNGEON_WIDTH;
public static final short MAX_DUNGEON_LENGTH = MAX_DUNGEON_WIDTH;
private ArrayList<DungeonGenerator> untaggedDungeons = new ArrayList<DungeonGenerator>();
private ArrayList<DungeonGenerator> registeredDungeons = new ArrayList<DungeonGenerator>();
private ArrayList<DungeonData> untaggedDungeons = new ArrayList<DungeonData>();
private ArrayList<DungeonData> registeredDungeons = new ArrayList<DungeonData>();
private DungeonPack RuinsPack;
private HashMap<String, DungeonPack> dungeonPackMapping = new HashMap<String, DungeonPack>();
private ArrayList<DungeonPack> dungeonPackList = new ArrayList<DungeonPack>();
private DungeonGenerator defaultUp;
private DungeonGenerator defaultDown;
private DungeonGenerator defaultError;
private DungeonData defaultError;
private DungeonHelper()
{
@@ -227,47 +207,34 @@ public class DungeonHelper
}
}
public List<DungeonGenerator> getRegisteredDungeons()
public List<DungeonData> getRegisteredDungeons()
{
return Collections.unmodifiableList(this.registeredDungeons);
}
public List<DungeonGenerator> getUntaggedDungeons()
public List<DungeonData> getUntaggedDungeons()
{
return Collections.unmodifiableList(this.untaggedDungeons);
}
public DungeonGenerator getDefaultErrorDungeon()
public DungeonData getDefaultErrorDungeon()
{
return defaultError;
}
public DungeonGenerator getDefaultUpDungeon()
{
return defaultUp;
}
public DungeonGenerator getDefaultDownDungeon()
{
return defaultDown;
}
public DungeonPack getDungeonPack(String name)
{
//TODO: This function might be obsolete after the new save format is implemented.
return dungeonPackMapping.get(name.toUpperCase());
}
public DungeonPack getDimDungeonPack(int dimensionID)
private DungeonPack getDimDungeonPack(NewDimData data)
{
//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)
DungeonData dungeon = data.dungeon();
if (dungeon != null)
{
pack = generator.getDungeonType().Owner;
pack = dungeon.dungeonType().Owner;
//Make sure the pack isn't null. This can happen for dungeons with the special UNKNOWN type.
if (pack == null)
@@ -277,7 +244,7 @@ public class DungeonHelper
}
else
{
if (dimensionID == NETHER_DIMENSION_ID)
if (data.id() == NETHER_DIMENSION_ID)
{
//TODO: Change this to the nether-side pack later ^_^
pack = RuinsPack;
@@ -290,21 +257,20 @@ public class DungeonHelper
return pack;
}
public LinkData createCustomDungeonDoor(World world, int x, int y, int z)
public DimLink createCustomDungeonDoor(World world, int x, int y, int z)
{
//Create a link above the specified position. Link to a new pocket dimension.
LinkData link = new LinkData(world.provider.dimensionId, 0, x, y + 1, z, x, y + 1, z, true, 3);
link = dimHelper.instance.createPocket(link, true, false);
NewDimData dimension = PocketManager.getDimensionData(world);
DimLink link = dimension.createLink(x, y + 1, z, LinkTypes.POCKET,3);
//Place a Warp Door linked to that pocket
itemDimDoor.placeDoorBlock(world, x, y, z, 3, mod_pocketDim.ExitDoor);
ItemDimensionalDoor.placeDoorBlock(world, x, y, z, 3, mod_pocketDim.warpDoor);
return link;
}
public boolean validateDungeonType(String type, DungeonPack pack)
{
//Check if the dungeon type is valid
return pack.isKnownType(type);
}
@@ -370,10 +336,10 @@ public class DungeonHelper
int weight = (dungeonData.length == 4) ? Integer.parseInt(dungeonData[3]) : DEFAULT_DUNGEON_WEIGHT;
//Add this custom dungeon to the list corresponding to its type
DungeonGenerator generator = new DungeonGenerator(weight, path, isOpen, dungeonType);
DungeonData dungeon = new DungeonData(path, isInternal, dungeonType, isOpen, weight);
pack.addDungeon(generator);
registeredDungeons.add(generator);
pack.addDungeon(dungeon);
registeredDungeons.add(dungeon);
if (verbose)
{
System.out.println("Registered dungeon: " + name);
@@ -385,7 +351,7 @@ public class DungeonHelper
{
System.out.println("The following dungeon name is invalid for its given pack. It will not be generated naturally: " + schematicPath);
}
untaggedDungeons.add(new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, path, true, DungeonType.UNKNOWN_TYPE));
untaggedDungeons.add(new DungeonData(path, isInternal, DungeonType.UNKNOWN_TYPE, true, DEFAULT_DUNGEON_WEIGHT));
System.out.println("Registered untagged dungeon: " + name);
}
}
@@ -403,7 +369,7 @@ public class DungeonHelper
File[] packFiles;
ArrayList<String> packFilePaths;
File directory = new File(path);
SchematicFileFilter schematicFileFilter = new SchematicFileFilter();
FileFilter schematicFileFilter = new FileFilters.FileExtensionFilter(SCHEMATIC_FILE_EXTENSION);
//Check that the Ruins pack has been loaded
if (RuinsPack == null)
@@ -427,7 +393,7 @@ public class DungeonHelper
schematics = null; //Release memory
//Load the custom dungeon packs
packDirectories = directory.listFiles(new DirectoryFilter());
packDirectories = directory.listFiles(new FileFilters.DirectoryFilter());
if (packDirectories != null)
{
//Loop through each directory, which is assumed to be a dungeon pack
@@ -462,9 +428,7 @@ public class DungeonHelper
{
//Register the core schematics
//These are used for debugging and in case of unusual errors while loading dungeons
defaultUp = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, DEFAULT_UP_SCHEMATIC_PATH, true, DungeonType.UNKNOWN_TYPE);
defaultDown = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, DEFAULT_DOWN_SCHEMATIC_PATH, true, DungeonType.UNKNOWN_TYPE);
defaultError = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, DEFAULT_ERROR_SCHEMATIC_PATH, true, DungeonType.UNKNOWN_TYPE);
defaultError = new DungeonData(DEFAULT_ERROR_SCHEMATIC_PATH, true, DungeonType.UNKNOWN_TYPE, true, DEFAULT_DUNGEON_WEIGHT);
//Open the list of dungeons packaged with our mod and register their schematics
registerBundledPack(BUNDLED_RUINS_LIST_PATH, RUINS_PACK_PATH, "Ruins", reader);
@@ -530,9 +494,10 @@ public class DungeonHelper
}
}
public void generateDungeonLink(LinkData inbound, DungeonPack pack, Random random)
public DungeonData selectDungeon(NewDimData dimension, Random random)
{
DungeonGenerator selection;
DungeonPack pack = getDimDungeonPack(dimension);
DungeonData selection;
DungeonPackConfig config;
DungeonPack selectedPack;
@@ -546,13 +511,13 @@ public class DungeonHelper
{
//Calculate the chance of switching to a different pack type
int packSwitchChance;
if (dimHelper.dimList.get(inbound.locDimID).depth == 0)
if (dimension.depth() == 1)
{
packSwitchChance = START_PACK_SWITCH_CHANCE;
}
else
{
packSwitchChance = MIN_PACK_SWITCH_CHANCE + (getPackDepth(inbound, pack) - 1) * PACK_SWITCH_CHANCE_PER_LEVEL;
packSwitchChance = MIN_PACK_SWITCH_CHANCE + dimension.parent().packDepth() * PACK_SWITCH_CHANCE_PER_LEVEL;
}
//Decide randomly whether to switch packs or not
@@ -564,7 +529,7 @@ public class DungeonHelper
}
//Pick the next dungeon
selection = selectedPack.getNextDungeon(inbound, random);
selection = selectedPack.getNextDungeon(dimension, random);
}
catch (Exception e)
{
@@ -580,7 +545,7 @@ public class DungeonHelper
selection = defaultError;
}
}
dimHelper.instance.getDimData(inbound.destDimID).dungeonGenerator = selection;
return selection;
}
@SuppressWarnings("unchecked")
@@ -621,16 +586,16 @@ public class DungeonHelper
return sortedNames;
}
private static ArrayList<String> parseDungeonNames(ArrayList<DungeonGenerator> dungeons)
private static ArrayList<String> parseDungeonNames(ArrayList<DungeonData> dungeons)
{
String name;
File schematic;
ArrayList<String> names = new ArrayList<String>(dungeons.size());
for (DungeonGenerator dungeon : dungeons)
for (DungeonData dungeon : dungeons)
{
//Retrieve the file name and strip off the file extension
schematic = new File(dungeon.schematicPath);
schematic = new File(dungeon.schematicPath());
name = schematic.getName();
name = name.substring(0, name.length() - SCHEMATIC_FILE_EXTENSION.length());
names.add(name);
@@ -638,102 +603,48 @@ public class DungeonHelper
return names;
}
public static ArrayList<DungeonGenerator> getDungeonChainHistory(DimData dimData, DungeonPack pack, int maxSize)
public static ArrayList<DungeonData> getDungeonChainHistory(NewDimData dimension, DungeonPack pack, int maxSize)
{
//TODO: I've improved this code for the time being. However, searching across links is a flawed approach. A player could
//manipulate the output of this function by setting up links to mislead our algorithm or by removing links.
//Dimensions MUST have built-in records of their parent dimensions in the future. ~SenseiKiwi
ArrayList<DungeonGenerator> history = new ArrayList<DungeonGenerator>();
DimData tailDim = dimData;
boolean found = true;
if (dimData.dungeonGenerator == null || dimData.dungeonGenerator.getDungeonType().Owner != pack || maxSize < 1)
if (dimension == null)
{
//The initial dimension is already outside our pack. Return an empty list.
return history;
throw new IllegalArgumentException("dimension cannot be null.");
}
history.add(dimData.dungeonGenerator);
for (int count = 1; count < maxSize && found; count++)
int count = 0;
NewDimData tail = dimension;
DungeonData dungeon = tail.dungeon();
ArrayList<DungeonData> history = new ArrayList<DungeonData>();
while (count < maxSize && dungeon != null && dungeon.dungeonType().Owner == pack)
{
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;
history.add(tailDim.dungeonGenerator);
found = true;
break;
}
}
history.add(dungeon);
tail = tail.parent();
dungeon = tail.dungeon();
count++;
}
return history;
}
private static int getPackDepth(LinkData inbound, DungeonPack pack)
public static ArrayList<DungeonData> getFlatDungeonTree(NewDimData dimension, 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
//Dimensions should also just keep track of pack depth internally.
NewDimData root = dimension;
ArrayList<DungeonData> dungeons = new ArrayList<DungeonData>();
Queue<NewDimData> pendingDimensions = new LinkedList<NewDimData>();
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
//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)
if (root.dungeon() == null)
{
return dungeons;
}
pendingDimensions.add(root);
checked.add(root);
while (dungeons.size() < maxSize && !pendingDimensions.isEmpty())
{
DimData current = pendingDimensions.remove();
for (LinkData link : current.getLinksInDim())
NewDimData current = pendingDimensions.remove();
for (NewDimData child : current.children())
{
DimData child = helper.getDimData(link.destDimID);
if (child.depth == current.depth + 1 && child.dungeonGenerator != null && checked.add(child))
if (child.dungeon() != null)
{
dungeons.add(child.dungeonGenerator);
dungeons.add(child.dungeon());
pendingDimensions.add(child);
}
if (dungeons.size() == maxSize)

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +1,23 @@
package StevenDimDoors.mod_pocketDim.helpers;
import StevenDimDoors.mod_pocketDim.LinkData;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider;
import StevenDimDoors.mod_pocketDim.Point3D;
public class yCoordHelper
{
private static final int MAXIMUM_UNCOVERED_Y = 245;
public static int getFirstUncovered(LinkData pointerLink)
{
return yCoordHelper.getFirstUncovered(
pointerLink.destDimID,
pointerLink.destXCoord,
pointerLink.destYCoord,
pointerLink.destZCoord);
}
public static int getFirstUncovered(int worldID, int x, int yStart, int z)
{ return getFirstUncovered(worldID, x, yStart, z, false); }
public static int getFirstUncovered(int worldID, int x, int yStart, int z, boolean fromTop)
{
if (dimHelper.getWorld(worldID) == null ||
dimHelper.getWorld(worldID).provider == null)
{
dimHelper.initDimension(worldID);
}
return yCoordHelper.getFirstUncovered(dimHelper.getWorld(worldID), x, yStart, z, fromTop);
}
private yCoordHelper() { }
public static int getFirstUncovered(World world, int x, int yStart, int z)
{ return getFirstUncovered(world, x, yStart, z, false); }
{
return getFirstUncovered(world, x, yStart, z, false);
}
public static int getFirstUncovered(World world, int x, int yStart, int z, boolean fromTop)
{
@@ -43,21 +25,23 @@ public class yCoordHelper
int localX = x < 0 ? (x % 16) + 16 : (x % 16);
int localZ = z < 0 ? (z % 16) + 16 : (z % 16);
int height = MAXIMUM_UNCOVERED_Y; //world.getHeight();
int height = MAXIMUM_UNCOVERED_Y;
int y;
if(!fromTop)
if (!fromTop)
{
boolean covered = true;
for (y = yStart; y < height && covered; y++)
{
covered = IsCoveredBlock(chunk, localX, y - 1, localZ) || IsCoveredBlock(chunk, localX, y, localZ);
covered = isCoveredBlock(chunk, localX, y - 1, localZ) || isCoveredBlock(chunk, localX, y, localZ);
}
} else {
}
else
{
boolean covered = false;
for (y = MAXIMUM_UNCOVERED_Y; y > 1 && !covered; y--)
{
covered = IsCoveredBlock(chunk, localX, y - 1, localZ);
covered = isCoveredBlock(chunk, localX, y - 1, localZ);
}
if (!covered) y = 63;
y++;
@@ -66,7 +50,7 @@ public class yCoordHelper
return y;
}
public static boolean IsCoveredBlock(Chunk chunk, int localX, int y, int localZ)
public static boolean isCoveredBlock(Chunk chunk, int localX, int y, int localZ)
{
int blockID;
Block block;
@@ -86,4 +70,228 @@ public class yCoordHelper
material = block.blockMaterial;
return (material.isLiquid() || !material.isReplaceable());
}
public static Point3D findSafeCubeUp(World world, int x, int startY, int z)
{
// Search for a 3x3x3 cube of air with blocks underneath
// We can also match against a 3x2x3 box with
// We shift the search area into the bounds of a chunk for the sake of simplicity,
// so that we don't need to worry about working across chunks.
int localX = x < 0 ? (x % 16) + 16 : (x % 16);
int localZ = z < 0 ? (z % 16) + 16 : (z % 16);
int cornerX = x - localX;
int cornerZ = z - localZ;
localX = MathHelper.clamp_int(localX, 1, 14);
localZ = MathHelper.clamp_int(localZ, 1, 14);
Chunk chunk = initializeChunkArea(world, x >> 4, z >> 4);
int height = world.getActualHeight();
int y, dx, dz, blockID;
boolean isSafe;
Block block;
// Initialize layers to a huge negative number so that we won't
// consider an area as usable unless it gets reset to 0 first
// when we find a foundation upon which to build.
int layers = -1000000;
// Check if a 3x3 layer of blocks is empty
// If we find a layer that contains replaceable blocks, it can
// serve as the base where we'll place the player and door.
for (y = Math.max(startY - 1, 0); y < height; y++)
{
isSafe = true;
for (dx = -1; dx <= 1 && isSafe; dx++)
{
for (dz = -1; dz <= 1 && isSafe; dz++)
{
blockID = chunk.getBlockID(localX + dx, y, localZ + dz);
if (blockID != 0)
{
block = Block.blocksList[blockID];
if (!block.blockMaterial.isReplaceable())
{
isSafe = false;
}
layers = 0;
}
}
}
if (isSafe)
{
layers++;
if (layers == 3)
{
return new Point3D(localX + cornerX, y - 2, localZ + cornerZ);
}
}
}
return null;
}
public static Point3D findSafeCubeDown(World world, int x, int startY, int z)
{
// Search for a 3x3x3 cube of air with blocks underneath
// We can also match against a 3x2x3 box with
// We shift the search area into the bounds of a chunk for the sake of simplicity,
// so that we don't need to worry about working across chunks.
int localX = x < 0 ? (x % 16) + 16 : (x % 16);
int localZ = z < 0 ? (z % 16) + 16 : (z % 16);
int cornerX = x - localX;
int cornerZ = z - localZ;
localX = MathHelper.clamp_int(localX, 1, 14);
localZ = MathHelper.clamp_int(localZ, 1, 14);
Chunk chunk = initializeChunkArea(world, x >> 4, z >> 4);
int height = world.getActualHeight();
int y, dx, dz, blockID;
boolean isSafe;
boolean hasBlocks;
Block block;
int layers = 0;
// Check if a 3x3 layer of blocks is empty
// If we find a layer that contains replaceable blocks, it can
// serve as the base where we'll place the player and door.
for (y = Math.min(startY + 2, height - 1); y >= 0; y--)
{
isSafe = true;
hasBlocks = false;
for (dx = -1; dx <= 1 && isSafe; dx++)
{
for (dz = -1; dz <= 1 && isSafe; dz++)
{
blockID = chunk.getBlockID(localX + dx, y, localZ + dz);
if (blockID != 0)
{
block = Block.blocksList[blockID];
if (!block.blockMaterial.isReplaceable())
{
if (layers >= 3)
{
return new Point3D(localX + cornerX, y + 1, localZ + cornerZ);
}
isSafe = false;
}
hasBlocks = true;
}
}
}
if (isSafe)
{
layers++;
if (hasBlocks)
{
if (layers >= 3)
{
return new Point3D(localX + cornerX, y, localZ + cornerZ);
}
layers = 0;
}
}
}
return null;
}
private static Chunk initializeChunkArea(World world, int chunkX, int chunkZ)
{
// We initialize a 3x3 area of chunks instead of just initializing
// the target chunk because things generated in adjacent chunks
// (e.g. trees) might intrude into the target chunk.
IChunkProvider provider = world.getChunkProvider();
Chunk target = provider.loadChunk(chunkX, chunkZ);
for (int dx = -1; dx <= 1; dx++)
{
for (int dz = -1; dz <= 1; dz++)
{
if (!provider.chunkExists(chunkX + dx, chunkZ + dz))
{
provider.loadChunk(chunkX, chunkZ);
}
}
}
return target;
}
public static Point3D findDropPoint(World world, int x, int startY, int z)
{
// Find a simple 2-block-high air gap
// Search across a 3x3 column
final int GAP_HEIGHT = 2;
int localX = x < 0 ? (x % 16) + 16 : (x % 16);
int localZ = z < 0 ? (z % 16) + 16 : (z % 16);
int cornerX = x - localX;
int cornerZ = z - localZ;
localX = MathHelper.clamp_int(localX, 1, 14);
localZ = MathHelper.clamp_int(localZ, 1, 14);
Chunk chunk = initializeChunkArea(world, x >> 4, z >> 4);
int y, dx, dz, index;
int height = world.getActualHeight();
int[] gaps = new int[9];
// Check 3x3 layers of blocks for air spaces
for (y = Math.min(startY, height - 1); y > 0; y--)
{
for (dx = -1, index = 0; dx <= 1; dx++)
{
for (dz = -1; dz <= 1; dz++, index++)
{
if (chunk.getBlockID(localX + dx, y, localZ + dz) != 0)
{
gaps[index] = 0;
}
else
{
gaps[index]++;
}
}
}
// Check if an acceptable gap exists in the center of the search column
if (gaps[index / 2] == GAP_HEIGHT)
{
return new Point3D(localX + cornerX, y + GAP_HEIGHT - 1, localZ + cornerZ);
}
// Check the other positions in the column
for (dx = -1, index = 0; dx <= 1; dx++)
{
for (dz = -1; dz <= 1; dz++, index++)
{
if (gaps[index] == GAP_HEIGHT)
{
return new Point3D(localX + cornerX + dx, y + GAP_HEIGHT - 1, localZ + cornerZ + dz);
}
}
}
}
return null;
}
public static int adjustDestinationY(int y, int worldHeight, int entranceY, int dungeonHeight)
{
//The goal here is to guarantee that the dungeon fits within the vertical bounds
//of the world while shifting it as little as possible.
int destY = y;
//Is the top of the dungeon going to be at Y < worldHeight?
int pocketTop = (dungeonHeight - 1) + destY - entranceY;
if (pocketTop >= worldHeight)
{
destY = (worldHeight - 1) - (dungeonHeight - 1) + entranceY;
}
//Is the bottom of the dungeon at Y >= 0?
if (destY < entranceY)
{
destY = entranceY;
}
return destY;
}
}