b050c6c61b
Simplified metadata rotation code by only having a single function that rotates metadata by 90 degrees clockwise and applying it repeatedly for 180 and 270 degree rotations. Removed flipDoorMetadata() from dimHelper and replaced all references to it with references to BlockRotator. This makes all our rotations reference a single function. Replaced hardcoded rotation in DungeonSchematic. Added support for wood (tree trunk) and quartz pillar metadata rotations.
419 lines
16 KiB
Java
419 lines
16 KiB
Java
package StevenDimDoors.mod_pocketDim.dungeon;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.InputStream;
|
|
import java.util.ArrayList;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Random;
|
|
import java.util.TreeMap;
|
|
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.entity.Entity;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.tileentity.TileEntity;
|
|
import net.minecraft.util.MathHelper;
|
|
import net.minecraft.world.World;
|
|
import StevenDimDoors.mod_pocketDim.DDProperties;
|
|
import StevenDimDoors.mod_pocketDim.LinkData;
|
|
import StevenDimDoors.mod_pocketDim.Point3D;
|
|
import StevenDimDoors.mod_pocketDim.helpers.dimHelper;
|
|
import StevenDimDoors.mod_pocketDim.helpers.yCoordHelper;
|
|
import StevenDimDoors.mod_pocketDim.schematic.BlockRotator;
|
|
import StevenDimDoors.mod_pocketDim.schematic.CompoundFilter;
|
|
import StevenDimDoors.mod_pocketDim.schematic.InvalidSchematicException;
|
|
import StevenDimDoors.mod_pocketDim.schematic.ReplacementFilter;
|
|
import StevenDimDoors.mod_pocketDim.schematic.Schematic;
|
|
import StevenDimDoors.mod_pocketDim.ticking.MobMonolith;
|
|
|
|
public class DungeonSchematic extends Schematic {
|
|
|
|
private static final short MAX_VANILLA_BLOCK_ID = 158;
|
|
private static final short STANDARD_FABRIC_OF_REALITY_ID = 1973;
|
|
private static final short STANDARD_ETERNAL_FABRIC_ID = 220;
|
|
private static final short STANDARD_WARP_DOOR_ID = 1975;
|
|
private static final short STANDARD_DIMENSIONAL_DOOR_ID = 1970;
|
|
private static final short MONOLITH_SPAWN_MARKER_ID = (short) Block.endPortalFrame.blockID;
|
|
private static final short EXIT_DOOR_MARKER_ID = (short) Block.sandStone.blockID;
|
|
|
|
private int orientation;
|
|
private Point3D entranceDoorLocation;
|
|
private ArrayList<Point3D> exitDoorLocations;
|
|
private ArrayList<Point3D> dimensionalDoorLocations;
|
|
private ArrayList<Point3D> monolithSpawnLocations;
|
|
|
|
private static final short[] MOD_BLOCK_FILTER_EXCEPTIONS = new short[] {
|
|
STANDARD_FABRIC_OF_REALITY_ID,
|
|
STANDARD_ETERNAL_FABRIC_ID,
|
|
STANDARD_WARP_DOOR_ID,
|
|
STANDARD_DIMENSIONAL_DOOR_ID
|
|
};
|
|
|
|
private DungeonSchematic(Schematic source)
|
|
{
|
|
super(source);
|
|
}
|
|
|
|
public int getOrientation()
|
|
{
|
|
return orientation;
|
|
}
|
|
|
|
public Point3D getEntranceDoorLocation()
|
|
{
|
|
return entranceDoorLocation;
|
|
}
|
|
|
|
private DungeonSchematic()
|
|
{
|
|
//Used to create a dummy instance for readFromResource()
|
|
super((short) 0, (short) 0, (short) 0, null, null, null);
|
|
}
|
|
|
|
public static DungeonSchematic readFromFile(String schematicPath) throws FileNotFoundException, InvalidSchematicException
|
|
{
|
|
return readFromFile(new File(schematicPath));
|
|
}
|
|
|
|
public static DungeonSchematic readFromFile(File schematicFile) throws FileNotFoundException, InvalidSchematicException
|
|
{
|
|
return readFromStream(new FileInputStream(schematicFile));
|
|
}
|
|
|
|
public static DungeonSchematic readFromResource(String resourcePath) throws InvalidSchematicException
|
|
{
|
|
//We need an instance of a class in the mod to retrieve a resource
|
|
DungeonSchematic empty = new DungeonSchematic();
|
|
InputStream schematicStream = empty.getClass().getResourceAsStream(resourcePath);
|
|
return readFromStream(schematicStream);
|
|
}
|
|
|
|
public static DungeonSchematic readFromStream(InputStream schematicStream) throws InvalidSchematicException
|
|
{
|
|
return new DungeonSchematic(Schematic.readFromStream(schematicStream));
|
|
}
|
|
|
|
public void applyImportFilters(DDProperties properties)
|
|
{
|
|
//Search for special blocks (warp doors, dim doors, and end portal frames that mark Monolith spawn points)
|
|
SpecialBlockFinder finder = new SpecialBlockFinder(STANDARD_WARP_DOOR_ID, STANDARD_DIMENSIONAL_DOOR_ID,
|
|
MONOLITH_SPAWN_MARKER_ID, EXIT_DOOR_MARKER_ID);
|
|
applyFilter(finder);
|
|
|
|
//Flip the entrance's orientation to get the dungeon's orientation
|
|
orientation = BlockRotator.transformMetadata(finder.getEntranceOrientation(), 2, Block.doorWood.blockID);
|
|
|
|
entranceDoorLocation = finder.getEntranceDoorLocation();
|
|
exitDoorLocations = finder.getExitDoorLocations();
|
|
dimensionalDoorLocations = finder.getDimensionalDoorLocations();
|
|
monolithSpawnLocations = finder.getMonolithSpawnLocations();
|
|
|
|
//Filter out mod blocks except some of our own
|
|
CompoundFilter standardizer = new CompoundFilter();
|
|
standardizer.addFilter(new ModBlockFilter(MAX_VANILLA_BLOCK_ID, MOD_BLOCK_FILTER_EXCEPTIONS,
|
|
(short) properties.FabricBlockID, (byte) 0));
|
|
|
|
//Also convert standard DD block IDs to local versions
|
|
Map<Short, Short> mapping = getAssignedToStandardIDMapping(properties);
|
|
|
|
for (Entry<Short, Short> entry : mapping.entrySet())
|
|
{
|
|
if (entry.getKey() != entry.getValue())
|
|
{
|
|
standardizer.addFilter(new ReplacementFilter(entry.getValue(), entry.getKey()));
|
|
}
|
|
}
|
|
applyFilter(standardizer);
|
|
}
|
|
|
|
public void applyExportFilters(DDProperties properties)
|
|
{
|
|
//Check if some block IDs assigned by Forge differ from our standard IDs
|
|
//If so, change the IDs to standard values
|
|
CompoundFilter standardizer = new CompoundFilter();
|
|
Map<Short, Short> mapping = getAssignedToStandardIDMapping(properties);
|
|
|
|
for (Entry<Short, Short> entry : mapping.entrySet())
|
|
{
|
|
if (entry.getKey() != entry.getValue())
|
|
{
|
|
standardizer.addFilter(new ReplacementFilter(entry.getKey(), entry.getValue()));
|
|
}
|
|
}
|
|
|
|
//Filter out mod blocks except some of our own
|
|
//This comes after ID standardization because the mod block filter relies on standardized IDs
|
|
standardizer.addFilter(new ModBlockFilter(MAX_VANILLA_BLOCK_ID, MOD_BLOCK_FILTER_EXCEPTIONS,
|
|
STANDARD_FABRIC_OF_REALITY_ID, (byte) 0));
|
|
|
|
applyFilter(standardizer);
|
|
}
|
|
|
|
private Map<Short, Short> getAssignedToStandardIDMapping(DDProperties properties)
|
|
{
|
|
//If we ever need this broadly or support other mods, this should be moved to a separate class
|
|
TreeMap<Short, Short> mapping = new TreeMap<Short, Short>();
|
|
mapping.put((short) properties.FabricBlockID, STANDARD_FABRIC_OF_REALITY_ID);
|
|
mapping.put((short) properties.PermaFabricBlockID, STANDARD_ETERNAL_FABRIC_ID);
|
|
mapping.put((short) properties.WarpDoorID, STANDARD_WARP_DOOR_ID);
|
|
mapping.put((short) properties.DimensionalDoorID, STANDARD_DIMENSIONAL_DOOR_ID);
|
|
return mapping;
|
|
}
|
|
|
|
public static DungeonSchematic copyFromWorld(World world, int x, int y, int z, short width, short height, short length, boolean doCompactBounds)
|
|
{
|
|
return new DungeonSchematic(Schematic.copyFromWorld(world, x, y, z, width, height, length, doCompactBounds));
|
|
}
|
|
|
|
public void copyToWorld(World world, Point3D pocketCenter, int dungeonOrientation, int originDimID, int destDimID)
|
|
{
|
|
//TODO: This function is an improvised solution so we can get the release moving. In the future,
|
|
//we should generalize block tranformations and implement support for them at the level of Schematic,
|
|
//then just use that support from DungeonSchematic instead of making this local fix.
|
|
//It might be easiest to support transformations using a WorldOperation
|
|
|
|
final int turnAngle = dungeonOrientation - orientation;
|
|
|
|
|
|
int index;
|
|
int count;
|
|
int blockID;
|
|
int blockMeta;
|
|
int dx, dy, dz;
|
|
Point3D pocketPoint = new Point3D(0, 0, 0);
|
|
|
|
//Copy blocks and metadata into the world
|
|
index = 0;
|
|
for (dy = 0; dy < height; dy++)
|
|
{
|
|
for (dz = 0; dz < length; dz++)
|
|
{
|
|
for (dx = 0; dx < width; dx++)
|
|
{
|
|
pocketPoint.setX(dx);
|
|
pocketPoint.setY(dy);
|
|
pocketPoint.setZ(dz);
|
|
blockID = blocks[index];
|
|
BlockRotator.transformPoint(pocketPoint, entranceDoorLocation, turnAngle, pocketCenter);
|
|
blockMeta = BlockRotator.transformMetadata(metadata[index], turnAngle, blockID);
|
|
|
|
//In the future, we might want to make this more efficient by building whole chunks at a time
|
|
setBlockDirectly(world, pocketPoint.getX(), pocketPoint.getY(), pocketPoint.getZ(), blockID, blockMeta);
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
//Copy tile entities into the world
|
|
count = tileEntities.tagCount();
|
|
for (index = 0; index < count; index++)
|
|
{
|
|
NBTTagCompound tileTag = (NBTTagCompound) tileEntities.tagAt(index);
|
|
//Rewrite its location to be in world coordinates
|
|
pocketPoint.setX(tileTag.getInteger("x"));
|
|
pocketPoint.setY(tileTag.getInteger("y"));
|
|
pocketPoint.setZ(tileTag.getInteger("z"));
|
|
BlockRotator.transformPoint(pocketPoint, entranceDoorLocation, turnAngle, pocketCenter);
|
|
tileTag.setInteger("x", pocketPoint.getX());
|
|
tileTag.setInteger("y", pocketPoint.getY());
|
|
tileTag.setInteger("z", pocketPoint.getZ());
|
|
//Load the tile entity and put it in the world
|
|
world.setBlockTileEntity(pocketPoint.getX(), pocketPoint.getY(), pocketPoint.getZ(), TileEntity.createAndLoadEntity(tileTag));
|
|
}
|
|
|
|
setUpDungeon(world, pocketCenter, turnAngle, originDimID, destDimID);
|
|
}
|
|
|
|
private void setUpDungeon(World world, Point3D pocketCenter, int turnAngle, int originDimID, int destDimID)
|
|
{
|
|
//The following Random initialization code is based on code from ChunkProviderGenerate.
|
|
//It makes our generation depend on the world seed.
|
|
Random random = new Random(world.getSeed());
|
|
long factorA = random.nextLong() / 2L * 2L + 1L;
|
|
long factorB = random.nextLong() / 2L * 2L + 1L;
|
|
random.setSeed((pocketCenter.getX() >> 4) * factorA + (pocketCenter.getZ() >> 4) * factorB ^ world.getSeed());
|
|
|
|
//Transform dungeon corners
|
|
Point3D minCorner = new Point3D(0, 0, 0);
|
|
Point3D maxCorner = new Point3D(width - 1, height - 1, length - 1);
|
|
transformCorners(entranceDoorLocation, pocketCenter, turnAngle, minCorner, maxCorner);
|
|
|
|
//Fill empty chests and dispensers
|
|
FillContainersOperation filler = new FillContainersOperation(random);
|
|
filler.apply(world, minCorner, maxCorner);
|
|
|
|
//Set up entrance door rift
|
|
setUpEntranceDoorLink(world, entranceDoorLocation, turnAngle, pocketCenter);
|
|
|
|
//Set up link data for dimensional doors
|
|
for (Point3D location : dimensionalDoorLocations)
|
|
{
|
|
setUpDimensionalDoorLink(world, location, entranceDoorLocation, turnAngle, pocketCenter, originDimID, destDimID, random);
|
|
}
|
|
|
|
//Set up link data for exit door
|
|
for (Point3D location : exitDoorLocations)
|
|
{
|
|
setUpExitDoorLink(world, location, entranceDoorLocation, turnAngle, pocketCenter, originDimID, destDimID, random);
|
|
}
|
|
|
|
//Remove end portal frames and spawn Monoliths
|
|
for (Point3D location : monolithSpawnLocations)
|
|
{
|
|
spawnMonolith(world, location, entranceDoorLocation, turnAngle, pocketCenter);
|
|
}
|
|
}
|
|
|
|
private static void transformCorners(Point3D schematicEntrance, Point3D pocketCenter, int turnAngle, Point3D minCorner, Point3D maxCorner)
|
|
{
|
|
int temp;
|
|
BlockRotator.transformPoint(minCorner, schematicEntrance, turnAngle, pocketCenter);
|
|
BlockRotator.transformPoint(maxCorner, schematicEntrance, turnAngle, pocketCenter);
|
|
if (minCorner.getX() > maxCorner.getX())
|
|
{
|
|
temp = minCorner.getX();
|
|
minCorner.setX(maxCorner.getX());
|
|
maxCorner.setX(temp);
|
|
}
|
|
if (minCorner.getY() > maxCorner.getY())
|
|
{
|
|
temp = minCorner.getY();
|
|
minCorner.setY(maxCorner.getY());
|
|
maxCorner.setY(temp);
|
|
}
|
|
if (minCorner.getZ() > maxCorner.getZ())
|
|
{
|
|
temp = minCorner.getZ();
|
|
minCorner.setZ(maxCorner.getZ());
|
|
maxCorner.setZ(temp);
|
|
}
|
|
}
|
|
|
|
private static void setUpEntranceDoorLink(World world, Point3D entrance, int rotation, Point3D pocketCenter)
|
|
{
|
|
//Set the orientation of the rift exit
|
|
Point3D entranceRiftLocation = entrance.clone();
|
|
BlockRotator.transformPoint(entranceRiftLocation, entrance, rotation, pocketCenter);
|
|
LinkData sideLink = dimHelper.instance.getLinkDataFromCoords(
|
|
entranceRiftLocation.getX(),
|
|
entranceRiftLocation.getY(),
|
|
entranceRiftLocation.getZ(),
|
|
world);
|
|
sideLink.linkOrientation = world.getBlockMetadata(
|
|
entranceRiftLocation.getX(),
|
|
entranceRiftLocation.getY() - 1,
|
|
entranceRiftLocation.getZ());
|
|
}
|
|
|
|
private static void setUpExitDoorLink(World world, Point3D point, Point3D entrance, int rotation, Point3D pocketCenter, int originDimID, int destDimID, Random random)
|
|
{
|
|
try
|
|
{
|
|
//TODO: Hax, remove this later
|
|
DDProperties properties = DDProperties.instance();
|
|
|
|
//Transform doorLocation to the pocket coordinate system.
|
|
Point3D location = point.clone();
|
|
BlockRotator.transformPoint(location, entrance, rotation, pocketCenter);
|
|
int blockDirection = world.getBlockMetadata(location.getX(), location.getY() - 1, location.getZ());
|
|
Point3D linkDestination = location.clone();
|
|
|
|
LinkData randomLink = dimHelper.instance.getRandomLinkData(false);
|
|
LinkData sideLink = new LinkData(destDimID,
|
|
dimHelper.instance.getDimData(originDimID).exitDimLink.destDimID,
|
|
location.getX(),
|
|
location.getY(),
|
|
location.getZ(),
|
|
linkDestination.getX(),
|
|
linkDestination.getY() + 1,
|
|
linkDestination.getZ(),
|
|
true, blockDirection);
|
|
|
|
if (sideLink.destDimID == properties.LimboDimensionID)
|
|
{
|
|
sideLink.destDimID = 0;
|
|
}
|
|
else if ((random.nextBoolean() && randomLink != null))
|
|
{
|
|
sideLink.destDimID = randomLink.locDimID;
|
|
}
|
|
sideLink.destYCoord = yCoordHelper.getFirstUncovered(sideLink.destDimID, linkDestination.getX(), 10, linkDestination.getZ());
|
|
|
|
if (sideLink.destYCoord < 5)
|
|
{
|
|
sideLink.destYCoord = 70;
|
|
}
|
|
sideLink.linkOrientation = world.getBlockMetadata(linkDestination.getX(), linkDestination.getY() - 1, linkDestination.getZ());
|
|
|
|
dimHelper.instance.createLink(sideLink);
|
|
dimHelper.instance.createLink(sideLink.destDimID ,
|
|
sideLink.locDimID,
|
|
sideLink.destXCoord,
|
|
sideLink.destYCoord,
|
|
sideLink.destZCoord,
|
|
sideLink.locXCoord,
|
|
sideLink.locYCoord,
|
|
sideLink.locZCoord,
|
|
BlockRotator.transformMetadata(sideLink.linkOrientation, 2, Block.doorWood.blockID));
|
|
|
|
if (world.getBlockId(linkDestination.getX(), linkDestination.getY() - 3, linkDestination.getZ()) == properties.FabricBlockID)
|
|
{
|
|
setBlockDirectly(world, linkDestination.getX(), linkDestination.getY() - 2, linkDestination.getZ(), Block.stoneBrick.blockID, 0);
|
|
}
|
|
else
|
|
{
|
|
setBlockDirectly(world,linkDestination.getX(), linkDestination.getY() - 2, linkDestination.getZ(),
|
|
world.getBlockId(linkDestination.getX(), linkDestination.getY() - 3, linkDestination.getZ()),
|
|
world.getBlockMetadata(linkDestination.getX(), linkDestination.getY() - 3, linkDestination.getZ()));
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private static void setUpDimensionalDoorLink(World world, Point3D point, Point3D entrance, int rotation, Point3D pocketCenter, int originDimID, int destDimID, Random random)
|
|
{
|
|
int depth = dimHelper.instance.getDimDepth(originDimID) + 1;
|
|
int forwardNoise = MathHelper.getRandomIntegerInRange(random, -50 * depth, 150 * depth);
|
|
int sidewaysNoise = MathHelper.getRandomIntegerInRange(random, -10 * depth, 10 * depth);
|
|
|
|
//Transform doorLocation to the pocket coordinate system
|
|
Point3D location = point.clone();
|
|
BlockRotator.transformPoint(location, entrance, rotation, pocketCenter);
|
|
int blockDirection = world.getBlockMetadata(location.getX(), location.getY() - 1, location.getZ());
|
|
|
|
//Rotate the link destination noise to point in the same direction as the door exit
|
|
//and add it to the door's location. Use EAST as the reference orientation since linkDestination
|
|
//is constructed as if pointing East.
|
|
Point3D linkDestination = new Point3D(forwardNoise, 0, sidewaysNoise);
|
|
Point3D zeroPoint = new Point3D(0, 0, 0);
|
|
BlockRotator.transformPoint(linkDestination, zeroPoint, blockDirection - BlockRotator.EAST_DOOR_METADATA, location);
|
|
|
|
//Create the link between our current door and its intended exit in destination pocket
|
|
LinkData sideLink = new LinkData(destDimID, 0,
|
|
location.getX(),
|
|
location.getY(),
|
|
location.getZ(),
|
|
linkDestination.getX(),
|
|
linkDestination.getY() + 1,
|
|
linkDestination.getZ(),
|
|
true, blockDirection);
|
|
dimHelper.instance.createPocket(sideLink, true, true);
|
|
}
|
|
|
|
private static void spawnMonolith(World world, Point3D point, Point3D entrance, int rotation, Point3D pocketCenter)
|
|
{
|
|
//Transform the frame block's location to the pocket coordinate system
|
|
Point3D location = point.clone();
|
|
BlockRotator.transformPoint(location, entrance, rotation, pocketCenter);
|
|
//Remove frame block
|
|
setBlockDirectly(world, location.getX(), location.getY(), location.getZ(), 0, 0);
|
|
//Spawn Monolith
|
|
Entity mob = new MobMonolith(world);
|
|
mob.setLocationAndAngles(location.getX(), location.getY(), location.getZ(), 1, 1);
|
|
world.spawnEntityInWorld(mob);
|
|
}
|
|
}
|