diff --git a/StevenDimDoors/mod_pocketDim/Point3D.java b/StevenDimDoors/mod_pocketDim/Point3D.java index 5d4a03d..2fd653e 100644 --- a/StevenDimDoors/mod_pocketDim/Point3D.java +++ b/StevenDimDoors/mod_pocketDim/Point3D.java @@ -101,4 +101,10 @@ public class Point3D implements Serializable { } return hash; } + + @Override + public String toString() + { + return "(" + x + ", " + "y" + ", " + z + ")"; + } } \ No newline at end of file diff --git a/StevenDimDoors/mod_pocketDim/SchematicLoader.java b/StevenDimDoors/mod_pocketDim/SchematicLoader.java index 332c535..8116cec 100644 --- a/StevenDimDoors/mod_pocketDim/SchematicLoader.java +++ b/StevenDimDoors/mod_pocketDim/SchematicLoader.java @@ -1,1235 +1,21 @@ package StevenDimDoors.mod_pocketDim; import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Random; +import java.io.FileNotFoundException; -import net.minecraft.block.Block; -import net.minecraft.block.BlockComparator; -import net.minecraft.block.BlockContainer; -import net.minecraft.block.BlockDoor; -import net.minecraft.block.BlockRedstoneRepeater; -import net.minecraft.block.BlockStairs; -import net.minecraft.entity.Entity; -import net.minecraft.inventory.IInventory; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompressedStreamTools; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.tileentity.TileEntityChest; -import net.minecraft.tileentity.TileEntityDispenser; -import net.minecraft.util.MathHelper; -import net.minecraft.util.WeightedRandomChestContent; import net.minecraft.world.World; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.storage.ExtendedBlockStorage; -import net.minecraftforge.common.ChestGenHooks; +import StevenDimDoors.mod_pocketDim.dungeon.DungeonSchematic; import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper; import StevenDimDoors.mod_pocketDim.helpers.dimHelper; -import StevenDimDoors.mod_pocketDim.helpers.yCoordHelper; -import StevenDimDoors.mod_pocketDim.ticking.MobMonolith; +import StevenDimDoors.mod_pocketDim.schematic.InvalidSchematicException; public class SchematicLoader -{ - private final static int EAST_DOOR_METADATA = 0; - private final static int SOUTH_DOOR_METADATA = 1; - private final static int WEST_DOOR_METADATA = 2; - private final static int NORTH_DOOR_METADATA = 3; - private final static int MAX_VANILLA_BLOCK_ID = 158; - - private DDProperties properties = DDProperties.instance(); +{ + private SchematicLoader() { } - public SchematicLoader() { } - - private static int transformMetadata(int metadata, int orientation, int blockID) + public static void generateDungeonPocket(LinkData link, DDProperties properties) { - if (DungeonHelper.instance().metadataFlipList.contains(blockID)) - { - switch (orientation) - { - case EAST_DOOR_METADATA: - - if (blockID == Block.hopperBlock.blockID) - { - switch (metadata) - { - case 2: - metadata = 5; - break; - case 3: - metadata = 4; - break; - case 4: - metadata = 2; - break; - case 5: - metadata = 3; - break; - } - } - if(Block.blocksList[blockID] instanceof BlockStairs) - { - - switch (metadata) - { - case 0: - metadata = 2; - break; - case 1: - metadata = 3; - break; - case 2: - metadata = 1; - break; - case 3: - metadata = 0; - break; - case 7: - metadata = 4; - break; - case 6: - metadata = 5; - break; - case 5: - metadata = 7; - break; - case 4: - metadata = 6; - break; - - } - } - - else if(blockID== Block.chest.blockID||blockID== Block.chestTrapped.blockID||blockID== Block.ladder.blockID) - { - switch (metadata) - { - - case 2: - metadata = 5; - break; - case 3: - metadata = 4; - break; - case 4: - metadata = 2; - break; - case 5: - metadata = 3; - break; - } - - } - else if (blockID==Block.vine.blockID) - { - switch (metadata) - { - - case 1: - metadata = 2; - break; - case 2: - metadata = 4; - break; - case 4: - metadata = 8; - break; - case 8: - metadata = 1; - break; - } - } - else if(blockID== Block.lever.blockID||blockID== Block.stoneButton.blockID||blockID== Block.woodenButton.blockID||blockID== Block.torchWood.blockID||blockID== Block.torchRedstoneIdle.blockID||blockID== Block.torchRedstoneActive.blockID) - { - switch (metadata) - { - case 12: - metadata = 9; - break; - case 11: - metadata = 10; - break; - case 10: - metadata = 12; - break; - case 9: - metadata = 11; - break; - case 2: - metadata = 4; - break; - case 3: - metadata = 2; - break; - case 1: - metadata = 3; - break; - case 4: - metadata = 1; - break; - } - } - else if(blockID== Block.pistonBase.blockID||blockID==Block.pistonExtension.blockID||blockID==Block.pistonStickyBase.blockID||blockID==Block.dispenser.blockID||blockID==Block.dropper.blockID) - { - switch (metadata) - { - case 4: - metadata = 2; - break; - case 5: - metadata = 3; - break; - case 13: - metadata = 11; - break; - case 12: - metadata = 10; - break; - case 3: - metadata = 4; - break; - case 2: - metadata = 5; - break; - case 11: - metadata = 12; - break; - case 10: - metadata = 13; - break; - } - } - else if(Block.blocksList[blockID] instanceof BlockRedstoneRepeater ||Block.blocksList[blockID] instanceof BlockDoor ||blockID== Block.tripWireSource.blockID||Block.blocksList[blockID] instanceof BlockComparator) - { - switch (metadata) - { - case 0: - metadata = 1; - break; - case 1: - metadata = 2; - break; - case 2: - metadata = 3; - break; - case 3: - metadata = 0; - break; - case 4: - metadata = 5; - break; - case 5: - metadata = 6; - break; - case 6: - metadata = 7; - break; - case 7: - metadata = 4; - break; - case 8: - metadata = 9; - break; - case 9: - metadata = 10; - break; - case 10: - metadata = 11; - break; - case 11: - metadata = 8; - break; - case 12: - metadata = 13; - break; - case 13: - metadata = 14; - break; - case 14: - metadata = 15; - break; - case 15: - metadata = 12; - break; - } - } - break; - case SOUTH_DOOR_METADATA: - - if (blockID == Block.hopperBlock.blockID) - { - switch (metadata) - { - case 2: - metadata = 3; - break; - case 3: - metadata = 2; - break; - case 4: - metadata = 5; - break; - case 5: - metadata = 4; - break; - } - } - - if(Block.blocksList[blockID] instanceof BlockStairs) - { - switch (metadata) - { - case 0: - metadata = 1; - break; - case 1: - metadata = 0; - break; - case 2: - metadata = 3; - break; - case 3: - metadata = 2; - break; - case 7: - metadata = 6; - break; - case 6: - metadata = 7; - break; - case 5: - metadata = 4; - break; - case 4: - metadata = 5; - break; - } - } - - else if(blockID== Block.chest.blockID||blockID== Block.chestTrapped.blockID||blockID==Block.ladder.blockID) - { - switch (metadata) - { - case 2: - metadata = 3; - break; - case 3: - metadata = 2; - break; - case 4: - metadata = 5; - break; - case 5: - metadata = 4; - break; - } - - } - - else if(blockID==Block.vine.blockID) - { - switch (metadata) - { - - case 1: - metadata = 4; - break; - case 2: - metadata = 8; - break; - case 4: - metadata = 1; - break; - case 8: - metadata = 2; - break; - } - } - - - - - else if(blockID== Block.lever.blockID||blockID== Block.torchWood.blockID||blockID== Block.torchRedstoneIdle.blockID||blockID== Block.torchRedstoneActive.blockID) - { - switch (metadata) - { - case 12: - metadata = 11; - break; - case 11: - metadata = 12; - break; - case 10: - metadata = 9; - break; - case 9: - metadata = 10; - break; - case 2: - metadata = 1; - break; - case 3: - metadata = 4; - break; - case 1: - metadata = 2; - break; - case 4: - metadata = 3; - - break; - - } - - } - - else if(blockID== Block.pistonBase.blockID||blockID==Block.pistonStickyBase.blockID||blockID==Block.dispenser.blockID||blockID==Block.dropper.blockID) - { - switch (metadata) - { - case 4: - metadata = 5; - break; - case 5: - metadata = 4; - break; - case 13: - metadata = 12; - break; - case 12: - metadata = 13; - break; - case 3: - metadata = 2; - break; - case 2: - metadata = 3; - break; - case 11: - metadata = 10; - break; - case 10: - metadata = 11; - break; - - } - - - - } - - else if(Block.blocksList[blockID] instanceof BlockRedstoneRepeater ||Block.blocksList[blockID] instanceof BlockDoor ||blockID== Block.tripWireSource.blockID||Block.blocksList[blockID] instanceof BlockComparator) - { - switch (metadata) - { - case 0: - metadata = 2; - break; - case 1: - metadata = 3; - break; - case 2: - metadata = 0; - break; - case 3: - metadata = 1; - break; - case 4: - metadata = 6; - break; - case 5: - metadata = 7; - break; - case 6: - metadata = 4; - break; - case 7: - metadata = 5; - break; - case 8: - metadata = 10; - break; - case 9: - metadata = 11; - break; - case 10: - metadata = 8; - break; - case 11: - metadata = 9; - break; - case 12: - metadata = 14; - break; - case 13: - metadata = 15; - break; - case 14: - metadata = 12; - break; - case 15: - metadata = 13; - break; - - - } - - - - } - - break; - case WEST_DOOR_METADATA: - - if (blockID == Block.hopperBlock.blockID) - { - switch (metadata) - { - case 2: - metadata = 4; - break; - case 3: - metadata = 5; - break; - case 4: - metadata = 3; - break; - case 5: - metadata = 2; - break; - } - } - - if(Block.blocksList[blockID] instanceof BlockStairs) - { - - switch (metadata) - { - case 2: - metadata = 0; - break; - case 3: - metadata = 1; - break; - case 1: - metadata = 2; - break; - case 0: - metadata = 3; - break; - case 4: - metadata = 7; - break; - case 5: - metadata = 6; - break; - case 7: - metadata = 5; - break; - case 6: - metadata = 4; - break; - - } - } - - else if(blockID== Block.chest.blockID||blockID== Block.chestTrapped.blockID||blockID==Block.ladder.blockID) - { - switch (metadata) - { - - case 2: - metadata = 4; - break; - case 3: - metadata = 5; - break; - case 4: - metadata = 3; - break; - case 5: - metadata = 2; - break; - - - - } - - } - - else if(blockID==Block.vine.blockID) - { - switch (metadata) - { - - case 1: - metadata = 8; - break; - case 2: - metadata = 1; - break; - case 4: - metadata = 2; - break; - case 8: - metadata = 4; - break; - } - } - - - - - else if(blockID== Block.lever.blockID||blockID== Block.torchWood.blockID||blockID== Block.torchRedstoneIdle.blockID||blockID== Block.torchRedstoneActive.blockID) - { - switch (metadata) - { - case 9: - metadata = 12; - break; - case 10: - metadata = 11; - break; - case 12: - metadata = 10; - break; - case 11: - metadata = 9; - break; - case 4: - metadata = 2; - break; - case 2: - metadata = 3; - break; - case 3: - metadata = 1; - break; - case 1: - metadata = 4; - - break; - - } - - } - - else if(blockID== Block.pistonBase.blockID||blockID==Block.pistonStickyBase.blockID||blockID==Block.dispenser.blockID||blockID==Block.dropper.blockID) - - { - switch (metadata) - { - case 2: - metadata = 4; - break; - case 3: - metadata = 5; - break; - case 11: - metadata = 13; - break; - case 10: - metadata = 12; - break; - case 4: - metadata = 3; - break; - case 5: - metadata = 2; - break; - case 12: - metadata = 11; - break; - case 13: - metadata = 10; - break; - } - } - else if(Block.blocksList[blockID] instanceof BlockRedstoneRepeater ||Block.blocksList[blockID] instanceof BlockDoor ||blockID== Block.tripWireSource.blockID||Block.blocksList[blockID] instanceof BlockComparator) - { - switch (metadata) - { - case 1: - metadata = 0; - break; - case 2: - metadata = 1; - break; - case 3: - metadata = 2; - break; - case 0: - metadata = 3; - break; - case 5: - metadata = 4; - break; - case 6: - metadata = 5; - break; - case 7: - metadata = 6; - break; - case 4: - metadata = 7; - break; - case 9: - metadata = 8; - break; - case 10: - metadata = 9; - break; - case 11: - metadata = 10; - break; - case 8: - metadata = 11; - break; - case 13: - metadata = 12; - break; - case 14: - metadata = 13; - break; - case 15: - metadata = 14; - break; - case 12: - metadata = 15; - break; - } - } - break; - case NORTH_DOOR_METADATA: - /** - * this is the default case- never need to change anything here - * - */ - break; - } - } - return metadata; - } - - public void generateSchematic(int riftX, int riftY, int riftZ, int orientation, int destDimID, int originDimID, String schematicPath) - { - short width = 0; - short height = 0; - short length = 0; - - //list of combined blockIds - short[] blocks = new short[0]; - - //block metaData - byte[] blockData = new byte[0]; - - //first 8 bits of the block ID - byte[] blockId = new byte[0]; - - //additional 4 bits of the block ID - byte[] addId = new byte[0]; - - //Variables for loading tile entities - boolean blockChanged = false; - NBTTagList tileEntities = null; - HashMap pointToTileEntityMap = new HashMap(); - - //the wooden door leading into the pocket - Point3D schematicEntrance = new Point3D(0,0,0); - - //the iron dim doors leading to more pockets - ArrayList sideLinks = new ArrayList(); - - //the wooden dim doors leading to the surface - ArrayList exitLinks = new ArrayList(); - - //the locations to spawn monoliths - ArrayList monolithSpawns = new ArrayList(); - - //load the schematic from disk - try - { - InputStream input; - String fname = schematicPath ; - - if(!(new File(fname).exists())) - { - //get file if its in the Jar - input = this.getClass().getResourceAsStream(fname); - } - else - { - //get file if in external library - System.out.println(new File(fname).exists()); - input = new FileInputStream(fname); - } - - NBTTagCompound schematicTag = CompressedStreamTools.readCompressed(input); - input.close(); //readCompressed() probably closes the stream anyway, but close again to be sure. - - //load size of schematic to generate - width = schematicTag.getShort("Width"); - height = schematicTag.getShort("Height"); - length = schematicTag.getShort("Length"); - - //load block info - blockId = schematicTag.getByteArray("Blocks"); - blockData = schematicTag.getByteArray("Data"); - addId = schematicTag.getByteArray("AddBlocks"); - - //create combined block list - blocks = new short[blockId.length]; - - //Combine the split block IDs into a single short[] - for (int index = 0; index < blockId.length; index++) - { - if ((index >> 1) >= addId.length) - { - blocks[index] = (short) (blockId[index] & 0xFF); - } - else - { - if ((index & 1) == 0) - { - blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF)); - } - else - { - blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF)); - } - } - } - - //Get the list of tile entities - tileEntities = schematicTag.getTagList("TileEntities"); - - //Map tile entity positions to the tile entity itself using a hash map - int count = tileEntities.tagCount(); - for (int index = 0; index < count; index++) - { - NBTTagCompound tileEntityData = (NBTTagCompound) tileEntities.tagAt(index); - Point3D location = new Point3D( - tileEntityData.getInteger("x"), - tileEntityData.getInteger("y"), - tileEntityData.getInteger("z")); - pointToTileEntityMap.put(location, tileEntityData); - } - } - catch (Exception e) - { - System.out.println("Error- could not find file "+schematicPath); - e.printStackTrace(); - } - - World world; - dimHelper.dimList.get(destDimID).hasBeenFilled = true; - - if (dimHelper.getWorld(destDimID) == null) - { - dimHelper.initDimension(destDimID); - } - world = dimHelper.getWorld(destDimID); + //TODO: Phase this function out in the next update. ~SenseiKiwi - //The following Random initialization code is based on code from ChunkProviderGenerate. - //It makes our generation depend on the world seed. - Random rand = new Random(world.getSeed()); - long factorA = rand.nextLong() / 2L * 2L + 1L; - long factorB = rand.nextLong() / 2L * 2L + 1L; - rand.setSeed((riftX >> 4) * factorA + (riftZ >> 4) * factorB ^ world.getSeed()); - - //Coordinates relative to the schematic, start at 0 and increase up to max width/height/length - int x, y, z; - Point3D schematicPoint = new Point3D(0, 0, 0); - - //The real point where a block will be placed - int realX, realY, realZ; - Point3D pocketPoint = new Point3D(0, 0, 0); - - //The central point of the pocket where we'll be placing the schematic - Point3D pocketCenter = new Point3D(riftX, riftY, riftZ); - Point3D zeroPoint = new Point3D(0, 0, 0); - - //The direction in which the player will enter. Will be set below. The direction is encoded using - //the same values as door metadata. Those values are not the same as Minecraft's cardinal directions. - int entryDirection = 0; - - //First loop through the schematic to load in all rift locations and Monolith spawn locations. - //Also find the entry door, which determines the final position and orientation of the dungeon. - for (y = 0; y < height; y++) - { - for (z = 0; z < length; z++) - { - for (x = 0; x < width; x++) - { - int index = y * width * length + z * width + x; - int indexBelow = (y - 1) * width * length + z * width + x; - int indexDoubleBelow = (y - 2) * width * length + z * width + x; - - int currentBlock = blocks[index]; - - //NBTTagList tileEntity = tileEntities; - //int size = tileEntity.tagCount(); - - if (currentBlock == Block.doorIron.blockID && indexBelow >= 0 && blocks[indexBelow] == Block.doorIron.blockID) - { - sideLinks.add(new Point3D(x, y, z)); - } - else if (currentBlock == Block.doorWood.blockID) - { - if (indexDoubleBelow >= 0 && blocks[indexDoubleBelow] == Block.sandStone.blockID && - blocks[indexBelow] == Block.doorWood.blockID) - { - exitLinks.add(new Point3D(x, y, z)); - } - else if (indexBelow >= 0 && blocks[indexBelow] == Block.doorWood.blockID) - { - schematicEntrance = new Point3D(x, y, z); - entryDirection = Math.abs(blockData[indexBelow] + 2) % 4; //TODO: Write a function for extracting a door's orientation from its metadata or at least change this to use bitwise operations. - } - } - else if (currentBlock == Block.endPortalFrame.blockID) - { - monolithSpawns.add(new Point3D(x, y, z)); - } - } - } - } - - //Loop to actually place the blocks - for (y = 0; y < height; y++) - { - schematicPoint.setY(y); - for (z = 0; z < length; z++) - { - schematicPoint.setZ(z); - for (x = 0; x < width; x++) - { - schematicPoint.setX(x); - - //Reinitialize pocketPoint with the current schematic coordinate system - pocketPoint.setX(x); - pocketPoint.setY(y); - pocketPoint.setZ(z); - //Transform pocketPoint into the pocket coordinate system - transformPoint(pocketPoint, schematicEntrance, orientation - entryDirection, pocketCenter); - //Store the transformed coordinates - realX = pocketPoint.getX(); - realY = pocketPoint.getY(); - realZ = pocketPoint.getZ(); - - int index = y * width * length + z * width + x; - int currentBlock = blocks[index]; - int blockMetadata = blockData[index]; - blockChanged = false; - - //replace tagging blocks with air, and mod blocks with FoR - if (currentBlock == Block.endPortalFrame.blockID) - { - currentBlock = 0; - blockChanged = true; - } - else if (currentBlock == DungeonHelper.FABRIC_OF_REALITY_EXPORT_ID) - { - currentBlock = mod_pocketDim.blockDimWall.blockID; - } - else if (currentBlock == DungeonHelper.PERMAFABRIC_EXPORT_ID) - { - currentBlock = mod_pocketDim.blockDimWallPerm.blockID; - } - else if ((Block.blocksList[currentBlock] == null && currentBlock != 0) || currentBlock > MAX_VANILLA_BLOCK_ID) - { - currentBlock = mod_pocketDim.blockDimWall.blockID; - blockChanged = true; - } - - //Place blocks and set metadata - if (currentBlock > 0) - { - int fixedMetadata; - - if (!blockChanged) - { - //Calculate new metadata for blocks that have orientations (e.g. doors, pistons) - //We're using a workaround to get the desired rotation relative to the schematic's entrance - fixedMetadata = transformMetadata(blockMetadata, (orientation + NORTH_DOOR_METADATA - entryDirection + 16) % 4, currentBlock); - } - else - { - //Don't include metadata for changed blocks. It's possible that the metadata belonged to a mod block. - //If we include it now, it could cause our Fabric of Reality to change into permafabric. - fixedMetadata = 0; - } - - //Convert vanilla doors to dim doors or place blocks - if (currentBlock == Block.doorIron.blockID) - { - setBlockDirectly(world, realX, realY, realZ, properties.DimensionalDoorID, fixedMetadata); - } - else if (currentBlock == Block.doorWood.blockID) - { - setBlockDirectly(world, realX, realY, realZ, properties.WarpDoorID, fixedMetadata); - } - else - { - setBlockDirectly(world, realX, realY, realZ, currentBlock, fixedMetadata); - } - - //Load the tile entity at this location if any exists, but only if the block wasn't changed - if (!blockChanged) - { - NBTTagCompound tileEntityData = pointToTileEntityMap.get(schematicPoint); - - if (tileEntityData != null) - { - //Change the tile entity's position - tileEntityData.setInteger("x", realX); - tileEntityData.setInteger("y", realY); - tileEntityData.setInteger("z", realZ); - //Load the tile entity into the world - world.setBlockTileEntity(realX, realY, realZ, TileEntity.createAndLoadEntity(tileEntityData)); - } - } - - //Fill empty chests and dispensers - if (Block.blocksList[currentBlock] instanceof BlockContainer) - { - TileEntity tileEntity = world.getBlockTileEntity(realX, realY, realZ); - - //Fill chests - if (tileEntity instanceof TileEntityChest) - { - TileEntityChest chest = (TileEntityChest) tileEntity; - if (isInventoryEmpty(chest)) - { - ChestGenHooks info = DDLoot.DungeonChestInfo; - WeightedRandomChestContent.generateChestContents(rand, info.getItems(rand), chest, info.getCount(rand)); - } - } - - //Fill dispensers - if (tileEntity instanceof TileEntityDispenser) - { - TileEntityDispenser dispenser = (TileEntityDispenser) tileEntity; - if (isInventoryEmpty(dispenser)) - { - dispenser.addItem(new ItemStack(Item.arrow, 64)); - } - } - } - } - } - } - } - - //We'll use an extra block here to restrict the scope of these variables to just inside the block - //This is to avoid declaration conflicts with the loops below - { - //Set the orientation of the rift exit - Point3D entranceRiftLocation = schematicEntrance.clone(); - transformPoint(entranceRiftLocation, schematicEntrance, orientation - entryDirection, pocketCenter); - LinkData sideLink = dimHelper.instance.getLinkDataFromCoords( - entranceRiftLocation.getX(), - entranceRiftLocation.getY(), - entranceRiftLocation.getZ(), - world); - sideLink.linkOrientation = world.getBlockMetadata( - entranceRiftLocation.getX(), - entranceRiftLocation.getY() - 1, - entranceRiftLocation.getZ()); - } - - //Generate the LinkData defined by the door placement, Iron Dim doors first - for(Point3D point : sideLinks) - { - int depth = dimHelper.instance.getDimDepth(originDimID) + 1; - int forwardNoise = MathHelper.getRandomIntegerInRange(rand, -50 * depth, 150 * depth); - int sidewaysNoise = MathHelper.getRandomIntegerInRange(rand, -10 * depth, 10 * depth); - - //Transform doorLocation to the pocket coordinate system - Point3D doorLocation = point.clone(); - transformPoint(doorLocation, schematicEntrance, orientation - entryDirection, pocketCenter); - int blockDirection = world.getBlockMetadata(doorLocation.getX(), doorLocation.getY() - 1, doorLocation.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); - transformPoint(linkDestination, zeroPoint, blockDirection - EAST_DOOR_METADATA, doorLocation); - - //Create the link between our current door and its intended exit in destination pocket - LinkData sideLink = new LinkData(destDimID, 0, - doorLocation.getX(), - doorLocation.getY(), - doorLocation.getZ(), - linkDestination.getX(), - linkDestination.getY() + 1, - linkDestination.getZ(), - true, blockDirection); - dimHelper.instance.createPocket(sideLink, true, true); - } - - //Generate linkData for wooden dim doors leading to the overworld - for(Point3D point : exitLinks) - { - try - { - //Transform doorLocation to the pocket coordinate system. - Point3D doorLocation = point.clone(); - transformPoint(doorLocation, schematicEntrance, orientation - entryDirection, pocketCenter); - int blockDirection = world.getBlockMetadata(doorLocation.getX(), doorLocation.getY() - 1, doorLocation.getZ()); - Point3D linkDestination = doorLocation.clone(); - - LinkData randomLink = dimHelper.instance.getRandomLinkData(false); - LinkData sideLink = new LinkData(destDimID, - dimHelper.dimList.get(originDimID).exitDimLink.destDimID, - doorLocation.getX(), - doorLocation.getY(), - doorLocation.getZ(), - linkDestination.getX(), - linkDestination.getY() + 1, - linkDestination.getZ(), - true, blockDirection); - - if (sideLink.destDimID == properties.LimboDimensionID) - { - sideLink.destDimID = 0; - } - else if ((rand.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, - dimHelper.instance.flipDoorMetadata(sideLink.linkOrientation)); - - 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(); - } - } - - //Spawn monoliths - for(Point3D point : monolithSpawns) - { - //Transform the frame block's location to the pocket coordinate system - Point3D frameLocation = point.clone(); - transformPoint(frameLocation, schematicEntrance, orientation - entryDirection, pocketCenter); - - Entity mob = new MobMonolith(world); - mob.setLocationAndAngles(frameLocation.getX(), frameLocation.getY(), frameLocation.getZ(), 1, 1); //TODO: Why not set the angles to 0? @.@ ~SenseiKiwi - world.spawnEntityInWorld(mob); - } - } - - private static boolean isInventoryEmpty(IInventory inventory) - { - int size = inventory.getSizeInventory(); - for (int index = 0; index < size; index++) - { - if (inventory.getStackInSlot(index) != null) - { - return false; - } - } - return true; - } - - private static void transformPoint(Point3D position, Point3D srcOrigin, int angle, Point3D destOrigin) - { - //This function receives a position (e.g. point in schematic space), translates it relative - //to a source coordinate system (e.g. the point that will be the center of a schematic), - //then rotates and translates it to obtain the corresponding point in a destination - //coordinate system (e.g. the location of the entry rift in the pocket being generated). - //The result is returned by overwriting the original position so that new object references - //aren't needed. That way, repeated use of this function will not incur as much overhead. - - //Position is only overwritten at the end, so it's okay to provide it as srcOrigin or destOrigin as well. - - int tx = position.getX() - srcOrigin.getX(); - int ty = position.getY() - srcOrigin.getY(); - int tz = position.getZ() - srcOrigin.getZ(); - - //"int angle" specifies a rotation consistent with Minecraft's orientation system. - //That means each increment of 1 in angle would be a 90-degree clockwise turn. - //Given a starting direction A and a destination direction B, the rotation would be - //calculated by (B - A). - - //Adjust angle into the expected range - if (angle < 0) - { - int correction = -(angle / 4); - angle = angle + 4 * (correction + 1); - } - angle = angle % 4; - - int rx; - int rz; - switch (angle) - { - case 0: //No rotation - rx = tx; - rz = tz; - break; - case 1: //90 degrees clockwise - rx = -tz; - rz = tx; - break; - case 2: //180 degrees - rx = -tx; - rz = -tz; - break; - case 3: //270 degrees clockwise - rx = tz; - rz = -tx; - - break; - default: //This should never happen - throw new IllegalStateException("Invalid angle value. This should never happen!"); - } - - position.setX( rx + destOrigin.getX() ); - position.setY( ty + destOrigin.getY() ); - position.setZ( rz + destOrigin.getZ() ); - } - - public void generateDungeonPocket(LinkData link) - { String filePath=DungeonHelper.instance().defaultBreak.schematicPath; if(dimHelper.dimList.containsKey(link.destDimID)) { @@ -1239,39 +25,38 @@ public class SchematicLoader } filePath = dimHelper.dimList.get(link.destDimID).dungeonGenerator.schematicPath; } - this.generateSchematic(link.destXCoord, link.destYCoord, link.destZCoord, link.linkOrientation, link.destDimID, link.locDimID, filePath); - } - - - public void setBlockDirectly(World world, int x, int y, int z,int id, int metadata) - { - if (Block.blocksList[id] == null) - { - return; - } - - int cX = x >> 4; - int cZ = z >> 4; - int cY = y >> 4; - Chunk chunk; - - //TODO: This really seems odd to me. Like it's wrong. ~SenseiKiwi - int chunkX=(x % 16)< 0 ? ((x) % 16)+16 : ((x) % 16); - int chunkZ=((z) % 16)< 0 ? ((z) % 16)+16 : ((z) % 16); - + + //this.generateSchematic(link.destXCoord, link.destYCoord, link.destZCoord, link.linkOrientation, link.destDimID, link.locDimID, filePath); + try { - chunk = world.getChunkFromChunkCoords(cX, cZ); - if (chunk.getBlockStorageArray()[cY] == null) + int originDimID = link.locDimID; + int destDimID = link.destDimID; + DungeonSchematic dungeon; + if ((new File(filePath)).exists()) { - chunk.getBlockStorageArray()[cY] = new ExtendedBlockStorage(cY << 4, !world.provider.hasNoSky); + dungeon = DungeonSchematic.readFromFile(filePath); } - - - chunk.getBlockStorageArray()[cY].setExtBlockID(chunkX, (y) & 15, chunkZ, id); - chunk.getBlockStorageArray()[cY].setExtBlockMetadata(chunkX, (y) & 15, chunkZ, metadata); + else + { + dungeon = DungeonSchematic.readFromResource(filePath); + } + dungeon.applyImportFilters(properties); + + dimHelper.dimList.get(destDimID).hasBeenFilled = true; + if (dimHelper.getWorld(destDimID) == null) + { + dimHelper.initDimension(destDimID); + } + World world = dimHelper.getWorld(destDimID); + + dungeon.copyToWorld(world, new Point3D(link.destXCoord, link.destYCoord, link.destZCoord), link.linkOrientation, originDimID, destDimID); } - catch(Exception e) + catch (FileNotFoundException e) + { + e.printStackTrace(); + } + catch (InvalidSchematicException e) { e.printStackTrace(); } diff --git a/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java b/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java new file mode 100644 index 0000000..1eb7d1f --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java @@ -0,0 +1,417 @@ +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 = (short) Block.doorWood.blockID;//1975; + private static final short STANDARD_DIMENSIONAL_DOOR_ID = (short) Block.doorIron.blockID;//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 exitDoorLocations; + private ArrayList dimensionalDoorLocations; + private ArrayList 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); + + orientation = (finder.getEntranceOrientation() + 2) & 3; //Flip the entrance's orientation to get the dungeon's orientation + 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 mapping = getAssignedToStandardIDMapping(properties); + + for (Entry 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 mapping = getAssignedToStandardIDMapping(properties); + + for (Entry 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 getAssignedToStandardIDMapping(DDProperties properties) + { + //If we ever need this broadly or support other mods, this should be moved to a separate class + TreeMap mapping = new TreeMap(); + 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 + BlockRotator.NORTH_DOOR_METADATA, 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.dimList.get(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, + dimHelper.instance.flipDoorMetadata(sideLink.linkOrientation)); + + 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); + } +} diff --git a/StevenDimDoors/mod_pocketDim/dungeon/FillContainersOperation.java b/StevenDimDoors/mod_pocketDim/dungeon/FillContainersOperation.java new file mode 100644 index 0000000..7979eaf --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/dungeon/FillContainersOperation.java @@ -0,0 +1,75 @@ +package StevenDimDoors.mod_pocketDim.dungeon; + +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockContainer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityChest; +import net.minecraft.tileentity.TileEntityDispenser; +import net.minecraft.util.WeightedRandomChestContent; +import net.minecraft.world.World; +import net.minecraftforge.common.ChestGenHooks; +import StevenDimDoors.mod_pocketDim.DDLoot; +import StevenDimDoors.mod_pocketDim.schematic.WorldOperation; + +public class FillContainersOperation extends WorldOperation +{ + private Random random; + + public FillContainersOperation(Random random) + { + super("FillContainersOperation"); + this.random = random; + } + + @Override + protected boolean applyToBlock(World world, int x, int y, int z) + { + int blockID = world.getBlockId(x, y, z); + + //Fill empty chests and dispensers + if (Block.blocksList[blockID] instanceof BlockContainer) + { + TileEntity tileEntity = world.getBlockTileEntity(x, y, z); + + //Fill chests + if (tileEntity instanceof TileEntityChest) + { + TileEntityChest chest = (TileEntityChest) tileEntity; + if (isInventoryEmpty(chest)) + { + ChestGenHooks info = DDLoot.DungeonChestInfo; + WeightedRandomChestContent.generateChestContents(random, info.getItems(random), chest, info.getCount(random)); + } + } + + //Fill dispensers + if (tileEntity instanceof TileEntityDispenser) + { + TileEntityDispenser dispenser = (TileEntityDispenser) tileEntity; + if (isInventoryEmpty(dispenser)) + { + dispenser.addItem(new ItemStack(Item.arrow, 64)); + } + } + } + return true; + } + + private static boolean isInventoryEmpty(IInventory inventory) + { + int size = inventory.getSizeInventory(); + for (int index = 0; index < size; index++) + { + if (inventory.getStackInSlot(index) != null) + { + return false; + } + } + return true; + } +} diff --git a/StevenDimDoors/mod_pocketDim/dungeon/ModBlockFilter.java b/StevenDimDoors/mod_pocketDim/dungeon/ModBlockFilter.java new file mode 100644 index 0000000..c0ab099 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/dungeon/ModBlockFilter.java @@ -0,0 +1,51 @@ +package StevenDimDoors.mod_pocketDim.dungeon; + +import net.minecraft.block.Block; +import StevenDimDoors.mod_pocketDim.schematic.SchematicFilter; + +public class ModBlockFilter extends SchematicFilter { + + private short maxVanillaBlockID; + private short[] exceptions; + private short replacementBlockID; + private byte replacementMetadata; + + public ModBlockFilter(short maxVanillaBlockID, short[] exceptions, short replacementBlockID, byte replacementMetadata) + { + super("ModBlockFilter"); + this.maxVanillaBlockID = maxVanillaBlockID; + this.exceptions = exceptions; + this.replacementBlockID = replacementBlockID; + this.replacementMetadata = replacementMetadata; + } + + @Override + protected boolean applyToBlock(int index, short[] blocks, byte[] metadata) + { + int k; + short currentID = blocks[index]; + if (currentID > maxVanillaBlockID || (currentID != 0 && Block.blocksList[currentID] == null)) + { + //This might be a mod block. Check if an exception exists. + for (k = 0; k < exceptions.length; k++) + { + if (currentID == exceptions[k]) + { + //Exception found, not considered a mod block + return false; + } + } + //No matching exception found. Replace the block. + blocks[index] = replacementBlockID; + metadata[index] = replacementMetadata; + return true; + } + return false; + } + + @Override + protected boolean terminates() + { + return false; + } +} diff --git a/StevenDimDoors/mod_pocketDim/dungeon/SpecialBlockFinder.java b/StevenDimDoors/mod_pocketDim/dungeon/SpecialBlockFinder.java new file mode 100644 index 0000000..ff8fffb --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/dungeon/SpecialBlockFinder.java @@ -0,0 +1,115 @@ +package StevenDimDoors.mod_pocketDim.dungeon; + +import java.util.ArrayList; + +import StevenDimDoors.mod_pocketDim.Point3D; +import StevenDimDoors.mod_pocketDim.schematic.Schematic; +import StevenDimDoors.mod_pocketDim.schematic.SchematicFilter; + +public class SpecialBlockFinder extends SchematicFilter { + + private short warpDoorID; + private short dimensionalDoorID; + private short monolithSpawnMarkerID; + private short exitMarkerID; + private int entranceOrientation; + private Schematic schematic; + private Point3D entranceDoorLocation; + private ArrayList exitDoorLocations; + private ArrayList dimensionalDoorLocations; + private ArrayList monolithSpawnLocations; + + public SpecialBlockFinder(short warpDoorID, short dimensionalDoorID, short monolithSpawnMarkerID, short exitMarkerID) + { + super("SpecialBlockFinder"); + this.warpDoorID = warpDoorID; + this.dimensionalDoorID = dimensionalDoorID; + this.monolithSpawnMarkerID = monolithSpawnMarkerID; + this.exitMarkerID = exitMarkerID; + this.entranceDoorLocation = null; + this.entranceOrientation = 0; + this.exitDoorLocations = new ArrayList(); + this.dimensionalDoorLocations = new ArrayList(); + this.monolithSpawnLocations = new ArrayList(); + this.schematic = null; + } + + public int getEntranceOrientation() { + return entranceOrientation; + } + + public Point3D getEntranceDoorLocation() { + return entranceDoorLocation; + } + + public ArrayList getExitDoorLocations() { + return exitDoorLocations; + } + + public ArrayList getDimensionalDoorLocations() { + return dimensionalDoorLocations; + } + + public ArrayList getMonolithSpawnLocations() { + return monolithSpawnLocations; + } + + @Override + protected boolean initialize(Schematic schematic, short[] blocks, byte[] metadata) + { + this.schematic = schematic; + return true; + } + + @Override + protected boolean applyToBlock(int index, short[] blocks, byte[] metadata) + { + int indexBelow; + int indexDoubleBelow; + + if (blocks[index] == monolithSpawnMarkerID) + { + monolithSpawnLocations.add(schematic.calculatePoint(index)); + return true; + } + if (blocks[index] == dimensionalDoorID) + { + indexBelow = schematic.calculateIndexBelow(index); + if (indexBelow >= 0 && blocks[indexBelow] == dimensionalDoorID) + { + dimensionalDoorLocations.add(schematic.calculatePoint(index)); + return true; + } + else + { + return false; + } + } + if (blocks[index] == warpDoorID) + { + indexBelow = schematic.calculateIndexBelow(index); + if (indexBelow >= 0 && blocks[indexBelow] == warpDoorID) + { + indexDoubleBelow = schematic.calculateIndexBelow(indexBelow); + if (indexDoubleBelow >= 0 && blocks[indexDoubleBelow] == exitMarkerID) + { + exitDoorLocations.add(schematic.calculatePoint(index)); + return true; + } + else if (entranceDoorLocation == null) + { + entranceDoorLocation = schematic.calculatePoint(index); + entranceOrientation = (metadata[indexBelow] & 3); + return true; + } + } + } + return false; + } + + @Override + protected boolean terminates() + { + return false; + } +} diff --git a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java index 268fbd2..3103893 100644 --- a/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java +++ b/StevenDimDoors/mod_pocketDim/helpers/DungeonHelper.java @@ -1,7 +1,6 @@ package StevenDimDoors.mod_pocketDim.helpers; import java.io.File; -import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -10,11 +9,6 @@ import java.util.HashSet; import java.util.Random; import java.util.regex.Pattern; -import net.minecraft.block.Block; -import net.minecraft.nbt.CompressedStreamTools; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.tileentity.TileEntity; import net.minecraft.util.WeightedRandom; import net.minecraft.world.World; import StevenDimDoors.mod_pocketDim.DDProperties; @@ -22,6 +16,7 @@ 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.dungeon.DungeonSchematic; import StevenDimDoors.mod_pocketDim.items.itemDimDoor; import StevenDimDoors.mod_pocketDim.util.WeightedContainer; @@ -73,8 +68,6 @@ public class DungeonHelper private ArrayList pistonTraps = new ArrayList(); private ArrayList exits = new ArrayList(); - public ArrayList metadataFlipList = new ArrayList(); - public ArrayList metadataNextList = new ArrayList(); public DungeonGenerator defaultBreak = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, "/schematics/somethingBroke.schematic", true); public DungeonGenerator defaultUp = new DungeonGenerator(DEFAULT_DUNGEON_WEIGHT, "/schematics/simpleStairsUp.schematic", true); @@ -116,7 +109,6 @@ public class DungeonHelper { copyfile.copyFile("/mods/DimDoors/text/How_to_add_dungeons.txt", file.getAbsolutePath() + "/How_to_add_dungeons.txt"); } - registerFlipBlocks(); importCustomDungeons(properties.CustomSchematicDirectory); registerBaseDungeons(); } @@ -261,44 +253,6 @@ public class DungeonHelper } } - public void registerFlipBlocks() - { - metadataFlipList.add(Block.dispenser.blockID); - metadataFlipList.add(Block.stairsStoneBrick.blockID); - metadataFlipList.add(Block.lever.blockID); - metadataFlipList.add(Block.stoneButton.blockID); - metadataFlipList.add(Block.redstoneRepeaterIdle.blockID); - metadataFlipList.add(Block.redstoneRepeaterActive.blockID); - metadataFlipList.add(Block.tripWireSource.blockID); - metadataFlipList.add(Block.torchWood.blockID); - metadataFlipList.add(Block.torchRedstoneIdle.blockID); - metadataFlipList.add(Block.torchRedstoneActive.blockID); - metadataFlipList.add(Block.doorIron.blockID); - metadataFlipList.add(Block.doorWood.blockID); - metadataFlipList.add(Block.pistonBase.blockID); - metadataFlipList.add(Block.pistonStickyBase.blockID); - metadataFlipList.add(Block.pistonExtension.blockID); - metadataFlipList.add(Block.redstoneComparatorIdle.blockID); - metadataFlipList.add(Block.redstoneComparatorActive.blockID); - metadataFlipList.add(Block.signPost.blockID); - metadataFlipList.add(Block.signWall.blockID); - metadataFlipList.add(Block.skull.blockID); - metadataFlipList.add(Block.ladder.blockID); - metadataFlipList.add(Block.vine.blockID); - metadataFlipList.add(Block.anvil.blockID); - metadataFlipList.add(Block.chest.blockID); - metadataFlipList.add(Block.chestTrapped.blockID); - metadataFlipList.add(Block.hopperBlock.blockID); - metadataFlipList.add(Block.stairsNetherBrick.blockID); - metadataFlipList.add(Block.stairsCobblestone.blockID); - metadataFlipList.add(Block.stairsNetherBrick.blockID); - metadataFlipList.add(Block.stairsNetherQuartz.blockID); - metadataFlipList.add(Block.stairsSandStone.blockID); - - metadataNextList.add(Block.redstoneRepeaterIdle.blockID); - metadataNextList.add(Block.redstoneRepeaterActive.blockID); - } - public void registerBaseDungeons() { hubs.add(new DungeonGenerator(2 * DEFAULT_DUNGEON_WEIGHT, "/schematics/4WayBasicHall.schematic", false)); @@ -383,153 +337,14 @@ public class DungeonHelper public boolean exportDungeon(World world, int centerX, int centerY, int centerZ, String exportPath) { - int xMin, yMin, zMin; - int xMax, yMax, zMax; - int xStart, yStart, zStart; - int xEnd, yEnd, zEnd; - - //Find the smallest bounding box that contains all non-air blocks within a max radius around the player. - xMax = yMax = zMax = Integer.MIN_VALUE; - xMin = yMin = zMin = Integer.MAX_VALUE; - - xStart = centerX - MAX_EXPORT_RADIUS; - zStart = centerZ - MAX_EXPORT_RADIUS; - yStart = Math.max(centerY - MAX_EXPORT_RADIUS, 0); - - xEnd = centerX + MAX_EXPORT_RADIUS; - zEnd = centerZ + MAX_EXPORT_RADIUS; - yEnd = Math.min(centerY + MAX_EXPORT_RADIUS, world.getHeight()); - - //This could be done more efficiently, but honestly, this is the simplest approach and it - //makes it easy for us to verify that the code is correct. - for (int y = yStart; y <= yEnd; y++) - { - for (int z = zStart; z <= zEnd; z++) - { - for (int x = xStart; x <= xEnd; x++) - { - if (!world.isAirBlock(x, y, z)) - { - xMax = x > xMax ? x : xMax; - zMax = z > zMax ? z : zMax; - yMax = y > yMax ? y : yMax; - - xMin = x < xMin ? x : xMin; - zMin = z < zMin ? z : zMin; - yMin = y < yMin ? y : yMin; - } - } - } - } - - //Export all the blocks within our selected bounding box - short width = (short) (xMax - xMin + 1); - short height = (short) (yMax - yMin + 1); - short length = (short) (zMax - zMin + 1); - - byte[] blocks = new byte[width * height * length]; - byte[] addBlocks = null; - byte[] blockData = new byte[width * height * length]; - NBTTagList tileEntities = new NBTTagList(); - - for (int y = 0; y < height; y++) - { - for (int z = 0; z < length; z++) - { - for (int x = 0; x < width; x++) - { - int index = y * width * length + z * width + x; - int blockID = world.getBlockId(x + xMin, y + yMin, z + zMin); - int metadata = world.getBlockMetadata(x + xMin, y + yMin, z + zMin); - boolean changed = false; - - if (blockID == properties.DimensionalDoorID) - { - blockID = Block.doorIron.blockID; - changed = true; - } - if (blockID == properties.WarpDoorID) - { - blockID = Block.doorWood.blockID; - changed = true; - } - //Map fabric of reality and permafabric blocks to standard export IDs - if (blockID == properties.FabricBlockID) - { - blockID = FABRIC_OF_REALITY_EXPORT_ID; - changed = true; - } - if (blockID == properties.PermaFabricBlockID) - { - blockID = PERMAFABRIC_EXPORT_ID; - changed = true; - } - - // Save 4096 IDs in an AddBlocks section - if (blockID > 255) - { - if (addBlocks == null) - { - //Lazily create section - addBlocks = new byte[(blocks.length >> 1) + 1]; - } - - addBlocks[index >> 1] = (byte) (((index & 1) == 0) ? - addBlocks[index >> 1] & 0xF0 | (blockID >> 8) & 0xF - : addBlocks[index >> 1] & 0xF | ((blockID >> 8) & 0xF) << 4); - } - - blocks[index] = (byte) blockID; - blockData[index] = (byte) metadata; - - //Obtain and export the tile entity of the current block, if any. - //Do not obtain a tile entity if the block was changed from its original ID. - //I'm not sure if this approach is the most efficient but it works. ~SenseiKiwi - TileEntity tileEntity = !changed ? world.getBlockTileEntity(x + xMin, y + yMin, z + zMin) : null; - - if (tileEntity != null) - { - //Get the tile entity's description as a compound NBT tag - NBTTagCompound entityData = new NBTTagCompound(); - tileEntity.writeToNBT(entityData); - //Change the tile entity's location to the schematic coordinate system - entityData.setInteger("x", x); - entityData.setInteger("y", y); - entityData.setInteger("z", z); - - tileEntities.appendTag(entityData); - } - } - } - } - - //Write NBT tags for schematic file - NBTTagCompound schematicTag = new NBTTagCompound("Schematic"); - - schematicTag.setShort("Width", width); - schematicTag.setShort("Length", length); - schematicTag.setShort("Height", height); - - schematicTag.setByteArray("Blocks", blocks); - schematicTag.setByteArray("Data", blockData); - - schematicTag.setTag("Entities", new NBTTagList()); - schematicTag.setTag("TileEntities", tileEntities); - schematicTag.setString("Materials", "Alpha"); - - if (addBlocks != null) - { - schematicTag.setByteArray("AddBlocks", addBlocks); - } - //Write schematic data to a file try { - FileOutputStream outputStream = new FileOutputStream(new File(exportPath)); - CompressedStreamTools.writeCompressed(schematicTag, outputStream); - //writeCompressed() probably closes the stream on its own - call close again just in case. - //Closing twice will not throw an exception. - outputStream.close(); + short size = (short) 2 * MAX_EXPORT_RADIUS + 1; + DungeonSchematic dungeon = DungeonSchematic.copyFromWorld(world, + centerX - MAX_EXPORT_RADIUS, centerY - MAX_EXPORT_RADIUS, centerZ - MAX_EXPORT_RADIUS, size, size, size, true); + dungeon.applyExportFilters(properties); + dungeon.writeToFile(exportPath); return true; } catch(Exception e) diff --git a/StevenDimDoors/mod_pocketDim/helpers/dimHelper.java b/StevenDimDoors/mod_pocketDim/helpers/dimHelper.java index c7def9b..24ab4fc 100644 --- a/StevenDimDoors/mod_pocketDim/helpers/dimHelper.java +++ b/StevenDimDoors/mod_pocketDim/helpers/dimHelper.java @@ -39,6 +39,7 @@ import StevenDimDoors.mod_pocketDim.LinkData; import StevenDimDoors.mod_pocketDim.ObjectSaveInputStream; import StevenDimDoors.mod_pocketDim.PacketHandler; import StevenDimDoors.mod_pocketDim.Point3D; +import StevenDimDoors.mod_pocketDim.SchematicLoader; import StevenDimDoors.mod_pocketDim.TileEntityRift; import StevenDimDoors.mod_pocketDim.mod_pocketDim; import StevenDimDoors.mod_pocketDim.world.LimboProvider; @@ -751,12 +752,8 @@ public class dimHelper extends DimensionManager } else if(!data.hasBeenFilled&&data.isPocket&&data.isDimRandomRift) { - //System.out.println("genning dungeon pocket"); - - - mod_pocketDim.loader.generateDungeonPocket(incomingLink); + SchematicLoader.generateDungeonPocket(incomingLink, properties); data.hasBeenFilled=true; - } } @@ -770,7 +767,7 @@ public class dimHelper extends DimensionManager mod_pocketDim.hasInitDims=true; this.load(); - if(!this.dimList.isEmpty()) + if(!dimHelper.dimList.isEmpty()) { diff --git a/StevenDimDoors/mod_pocketDim/mod_pocketDim.java b/StevenDimDoors/mod_pocketDim/mod_pocketDim.java index 6c3cd37..253ae39 100644 --- a/StevenDimDoors/mod_pocketDim/mod_pocketDim.java +++ b/StevenDimDoors/mod_pocketDim/mod_pocketDim.java @@ -99,9 +99,7 @@ public class mod_pocketDim @Instance("PocketDimensions") public static mod_pocketDim instance = new mod_pocketDim(); - public static SchematicLoader loader; public static pocketTeleporter teleporter; - public static BlockRotationHelper rotationHelper; public static Block transientDoor; public static Block ExitDoor; @@ -170,11 +168,9 @@ public class mod_pocketDim //These fields MUST be initialized after properties are loaded to prevent //instances from holding onto null references to the properties. - loader = new SchematicLoader(); teleporter = new pocketTeleporter(); tracker = new PlayerRespawnTracker(); riftGen = new RiftGenerator(); - rotationHelper = new BlockRotationHelper(); } @Init diff --git a/StevenDimDoors/mod_pocketDim/schematic/BlockRotator.java b/StevenDimDoors/mod_pocketDim/schematic/BlockRotator.java new file mode 100644 index 0000000..fded0a4 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/BlockRotator.java @@ -0,0 +1,797 @@ +package StevenDimDoors.mod_pocketDim.schematic; + +import java.util.ArrayList; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockComparator; +import net.minecraft.block.BlockDoor; +import net.minecraft.block.BlockRedstoneRepeater; +import net.minecraft.block.BlockStairs; +import StevenDimDoors.mod_pocketDim.Point3D; +import StevenDimDoors.mod_pocketDim.mod_pocketDim; +import StevenDimDoors.mod_pocketDim.blocks.dimDoor; + +public class BlockRotator +{ + //This class is temporary. It's just a place in which to hold the old block rotation and transformation code + //until we can rewrite it. + + public final static int EAST_DOOR_METADATA = 0; + private final static int SOUTH_DOOR_METADATA = 1; + private final static int WEST_DOOR_METADATA = 2; + public final static int NORTH_DOOR_METADATA = 3; + + private final static ArrayList metadataFlipList = new ArrayList(); + + static + { + metadataFlipList.add(Block.dispenser.blockID); + metadataFlipList.add(Block.stairsStoneBrick.blockID); + metadataFlipList.add(Block.lever.blockID); + metadataFlipList.add(Block.stoneButton.blockID); + metadataFlipList.add(Block.redstoneRepeaterIdle.blockID); + metadataFlipList.add(Block.redstoneRepeaterActive.blockID); + metadataFlipList.add(Block.tripWireSource.blockID); + metadataFlipList.add(Block.torchWood.blockID); + metadataFlipList.add(Block.torchRedstoneIdle.blockID); + metadataFlipList.add(Block.torchRedstoneActive.blockID); + metadataFlipList.add(Block.doorIron.blockID); + metadataFlipList.add(Block.doorWood.blockID); + metadataFlipList.add(Block.pistonBase.blockID); + metadataFlipList.add(Block.pistonStickyBase.blockID); + metadataFlipList.add(Block.pistonExtension.blockID); + metadataFlipList.add(Block.redstoneComparatorIdle.blockID); + metadataFlipList.add(Block.redstoneComparatorActive.blockID); + metadataFlipList.add(Block.signPost.blockID); + metadataFlipList.add(Block.signWall.blockID); + metadataFlipList.add(Block.skull.blockID); + metadataFlipList.add(Block.ladder.blockID); + metadataFlipList.add(Block.vine.blockID); + metadataFlipList.add(Block.anvil.blockID); + metadataFlipList.add(Block.chest.blockID); + metadataFlipList.add(Block.chestTrapped.blockID); + metadataFlipList.add(Block.hopperBlock.blockID); + metadataFlipList.add(Block.stairsNetherBrick.blockID); + metadataFlipList.add(Block.stairsCobblestone.blockID); + metadataFlipList.add(Block.stairsNetherBrick.blockID); + metadataFlipList.add(Block.stairsNetherQuartz.blockID); + metadataFlipList.add(Block.stairsSandStone.blockID); + metadataFlipList.add(mod_pocketDim.dimDoor.blockID); + metadataFlipList.add(mod_pocketDim.ExitDoor.blockID); + } + + public static int transformMetadata(int metadata, int orientation, int blockID) + { + //TODO: Replace this horrible function with something prettier. We promise we will for the next version, + //after switching to MC 1.6. PADRE, PLEASE FORGIVE ME. + + //Hax to fix negative orientations + orientation += 1 << 16; + orientation %= 4; + + if (metadataFlipList.contains(blockID)) + { + switch (orientation) + { + case EAST_DOOR_METADATA: + + if (blockID == Block.hopperBlock.blockID) + { + switch (metadata) + { + case 2: + metadata = 5; + break; + case 3: + metadata = 4; + break; + case 4: + metadata = 2; + break; + case 5: + metadata = 3; + break; + } + } + if(Block.blocksList[blockID] instanceof BlockStairs) + { + + switch (metadata) + { + case 0: + metadata = 2; + break; + case 1: + metadata = 3; + break; + case 2: + metadata = 1; + break; + case 3: + metadata = 0; + break; + case 7: + metadata = 4; + break; + case 6: + metadata = 5; + break; + case 5: + metadata = 7; + break; + case 4: + metadata = 6; + break; + + } + } + + else if(blockID== Block.chest.blockID||blockID== Block.chestTrapped.blockID||blockID== Block.ladder.blockID) + { + switch (metadata) + { + + case 2: + metadata = 5; + break; + case 3: + metadata = 4; + break; + case 4: + metadata = 2; + break; + case 5: + metadata = 3; + break; + } + + } + else if (blockID==Block.vine.blockID) + { + switch (metadata) + { + + case 1: + metadata = 2; + break; + case 2: + metadata = 4; + break; + case 4: + metadata = 8; + break; + case 8: + metadata = 1; + break; + } + } + else if(blockID== Block.lever.blockID||blockID== Block.stoneButton.blockID||blockID== Block.woodenButton.blockID||blockID== Block.torchWood.blockID||blockID== Block.torchRedstoneIdle.blockID||blockID== Block.torchRedstoneActive.blockID) + { + switch (metadata) + { + case 12: + metadata = 9; + break; + case 11: + metadata = 10; + break; + case 10: + metadata = 12; + break; + case 9: + metadata = 11; + break; + case 2: + metadata = 4; + break; + case 3: + metadata = 2; + break; + case 1: + metadata = 3; + break; + case 4: + metadata = 1; + break; + } + } + else if(blockID== Block.pistonBase.blockID||blockID==Block.pistonExtension.blockID||blockID==Block.pistonStickyBase.blockID||blockID==Block.dispenser.blockID||blockID==Block.dropper.blockID) + { + switch (metadata) + { + case 4: + metadata = 2; + break; + case 5: + metadata = 3; + break; + case 13: + metadata = 11; + break; + case 12: + metadata = 10; + break; + case 3: + metadata = 4; + break; + case 2: + metadata = 5; + break; + case 11: + metadata = 12; + break; + case 10: + metadata = 13; + break; + } + } + else if(Block.blocksList[blockID] instanceof BlockRedstoneRepeater || Block.blocksList[blockID] instanceof BlockDoor || Block.blocksList[blockID] instanceof dimDoor || blockID== Block.tripWireSource.blockID || Block.blocksList[blockID] instanceof BlockComparator) + { + switch (metadata) + { + case 0: + metadata = 1; + break; + case 1: + metadata = 2; + break; + case 2: + metadata = 3; + break; + case 3: + metadata = 0; + break; + case 4: + metadata = 5; + break; + case 5: + metadata = 6; + break; + case 6: + metadata = 7; + break; + case 7: + metadata = 4; + break; + case 8: + metadata = 9; + break; + case 9: + metadata = 10; + break; + case 10: + metadata = 11; + break; + case 11: + metadata = 8; + break; + case 12: + metadata = 13; + break; + case 13: + metadata = 14; + break; + case 14: + metadata = 15; + break; + case 15: + metadata = 12; + break; + } + } + break; + case SOUTH_DOOR_METADATA: + + if (blockID == Block.hopperBlock.blockID) + { + switch (metadata) + { + case 2: + metadata = 3; + break; + case 3: + metadata = 2; + break; + case 4: + metadata = 5; + break; + case 5: + metadata = 4; + break; + } + } + + if(Block.blocksList[blockID] instanceof BlockStairs) + { + switch (metadata) + { + case 0: + metadata = 1; + break; + case 1: + metadata = 0; + break; + case 2: + metadata = 3; + break; + case 3: + metadata = 2; + break; + case 7: + metadata = 6; + break; + case 6: + metadata = 7; + break; + case 5: + metadata = 4; + break; + case 4: + metadata = 5; + break; + } + } + + else if(blockID== Block.chest.blockID||blockID== Block.chestTrapped.blockID||blockID==Block.ladder.blockID) + { + switch (metadata) + { + case 2: + metadata = 3; + break; + case 3: + metadata = 2; + break; + case 4: + metadata = 5; + break; + case 5: + metadata = 4; + break; + } + + } + + else if(blockID==Block.vine.blockID) + { + switch (metadata) + { + + case 1: + metadata = 4; + break; + case 2: + metadata = 8; + break; + case 4: + metadata = 1; + break; + case 8: + metadata = 2; + break; + } + } + + + + + else if(blockID== Block.lever.blockID||blockID== Block.torchWood.blockID||blockID== Block.torchRedstoneIdle.blockID||blockID== Block.torchRedstoneActive.blockID) + { + switch (metadata) + { + case 12: + metadata = 11; + break; + case 11: + metadata = 12; + break; + case 10: + metadata = 9; + break; + case 9: + metadata = 10; + break; + case 2: + metadata = 1; + break; + case 3: + metadata = 4; + break; + case 1: + metadata = 2; + break; + case 4: + metadata = 3; + + break; + + } + + } + + else if(blockID== Block.pistonBase.blockID||blockID==Block.pistonStickyBase.blockID||blockID==Block.dispenser.blockID||blockID==Block.dropper.blockID) + { + switch (metadata) + { + case 4: + metadata = 5; + break; + case 5: + metadata = 4; + break; + case 13: + metadata = 12; + break; + case 12: + metadata = 13; + break; + case 3: + metadata = 2; + break; + case 2: + metadata = 3; + break; + case 11: + metadata = 10; + break; + case 10: + metadata = 11; + break; + + } + + + + } + + else if(Block.blocksList[blockID] instanceof BlockRedstoneRepeater ||Block.blocksList[blockID] instanceof BlockDoor || Block.blocksList[blockID] instanceof dimDoor || blockID== Block.tripWireSource.blockID||Block.blocksList[blockID] instanceof BlockComparator) + { + switch (metadata) + { + case 0: + metadata = 2; + break; + case 1: + metadata = 3; + break; + case 2: + metadata = 0; + break; + case 3: + metadata = 1; + break; + case 4: + metadata = 6; + break; + case 5: + metadata = 7; + break; + case 6: + metadata = 4; + break; + case 7: + metadata = 5; + break; + case 8: + metadata = 10; + break; + case 9: + metadata = 11; + break; + case 10: + metadata = 8; + break; + case 11: + metadata = 9; + break; + case 12: + metadata = 14; + break; + case 13: + metadata = 15; + break; + case 14: + metadata = 12; + break; + case 15: + metadata = 13; + break; + + + } + + + + } + + break; + case WEST_DOOR_METADATA: + + if (blockID == Block.hopperBlock.blockID) + { + switch (metadata) + { + case 2: + metadata = 4; + break; + case 3: + metadata = 5; + break; + case 4: + metadata = 3; + break; + case 5: + metadata = 2; + break; + } + } + + if(Block.blocksList[blockID] instanceof BlockStairs) + { + + switch (metadata) + { + case 2: + metadata = 0; + break; + case 3: + metadata = 1; + break; + case 1: + metadata = 2; + break; + case 0: + metadata = 3; + break; + case 4: + metadata = 7; + break; + case 5: + metadata = 6; + break; + case 7: + metadata = 5; + break; + case 6: + metadata = 4; + break; + + } + } + + else if(blockID== Block.chest.blockID||blockID== Block.chestTrapped.blockID||blockID==Block.ladder.blockID) + { + switch (metadata) + { + + case 2: + metadata = 4; + break; + case 3: + metadata = 5; + break; + case 4: + metadata = 3; + break; + case 5: + metadata = 2; + break; + + + + } + + } + + else if(blockID==Block.vine.blockID) + { + switch (metadata) + { + + case 1: + metadata = 8; + break; + case 2: + metadata = 1; + break; + case 4: + metadata = 2; + break; + case 8: + metadata = 4; + break; + } + } + + + + + else if(blockID== Block.lever.blockID||blockID== Block.torchWood.blockID||blockID== Block.torchRedstoneIdle.blockID||blockID== Block.torchRedstoneActive.blockID) + { + switch (metadata) + { + case 9: + metadata = 12; + break; + case 10: + metadata = 11; + break; + case 12: + metadata = 10; + break; + case 11: + metadata = 9; + break; + case 4: + metadata = 2; + break; + case 2: + metadata = 3; + break; + case 3: + metadata = 1; + break; + case 1: + metadata = 4; + + break; + + } + + } + + else if(blockID== Block.pistonBase.blockID||blockID==Block.pistonStickyBase.blockID||blockID==Block.dispenser.blockID||blockID==Block.dropper.blockID) + + { + switch (metadata) + { + case 2: + metadata = 4; + break; + case 3: + metadata = 5; + break; + case 11: + metadata = 13; + break; + case 10: + metadata = 12; + break; + case 4: + metadata = 3; + break; + case 5: + metadata = 2; + break; + case 12: + metadata = 11; + break; + case 13: + metadata = 10; + break; + } + } + else if(Block.blocksList[blockID] instanceof BlockRedstoneRepeater ||Block.blocksList[blockID] instanceof BlockDoor || Block.blocksList[blockID] instanceof dimDoor || blockID== Block.tripWireSource.blockID||Block.blocksList[blockID] instanceof BlockComparator) + { + switch (metadata) + { + case 1: + metadata = 0; + break; + case 2: + metadata = 1; + break; + case 3: + metadata = 2; + break; + case 0: + metadata = 3; + break; + case 5: + metadata = 4; + break; + case 6: + metadata = 5; + break; + case 7: + metadata = 6; + break; + case 4: + metadata = 7; + break; + case 9: + metadata = 8; + break; + case 10: + metadata = 9; + break; + case 11: + metadata = 10; + break; + case 8: + metadata = 11; + break; + case 13: + metadata = 12; + break; + case 14: + metadata = 13; + break; + case 15: + metadata = 14; + break; + case 12: + metadata = 15; + break; + } + } + break; + case NORTH_DOOR_METADATA: + /** + * this is the default case- never need to change anything here + * + */ + break; + } + } + return metadata; + } + + public static void transformPoint(Point3D position, Point3D srcOrigin, int angle, Point3D destOrigin) + { + //This function receives a position (e.g. point in schematic space), translates it relative + //to a source coordinate system (e.g. the point that will be the center of a schematic), + //then rotates and translates it to obtain the corresponding point in a destination + //coordinate system (e.g. the location of the entry rift in the pocket being generated). + //The result is returned by overwriting the original position so that new object references + //aren't needed. That way, repeated use of this function will not incur as much overhead. + + //Position is only overwritten at the end, so it's okay to provide it as srcOrigin or destOrigin as well. + + int tx = position.getX() - srcOrigin.getX(); + int ty = position.getY() - srcOrigin.getY(); + int tz = position.getZ() - srcOrigin.getZ(); + + //"int angle" specifies a rotation consistent with Minecraft's orientation system. + //That means each increment of 1 in angle would be a 90-degree clockwise turn. + //Given a starting direction A and a destination direction B, the rotation would be + //calculated by (B - A). + + //Adjust angle into the expected range + if (angle < 0) + { + int correction = -(angle / 4); + angle = angle + 4 * (correction + 1); + } + angle = angle % 4; + + int rx; + int rz; + switch (angle) + { + case 0: //No rotation + rx = tx; + rz = tz; + break; + case 1: //90 degrees clockwise + rx = -tz; + rz = tx; + break; + case 2: //180 degrees + rx = -tx; + rz = -tz; + break; + case 3: //270 degrees clockwise + rx = tz; + rz = -tx; + + break; + default: //This should never happen + throw new IllegalStateException("Invalid angle value. This should never happen!"); + } + + position.setX( rx + destOrigin.getX() ); + position.setY( ty + destOrigin.getY() ); + position.setZ( rz + destOrigin.getZ() ); + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/CompactBoundsOperation.java b/StevenDimDoors/mod_pocketDim/schematic/CompactBoundsOperation.java new file mode 100644 index 0000000..7bb11bc --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/CompactBoundsOperation.java @@ -0,0 +1,73 @@ +package StevenDimDoors.mod_pocketDim.schematic; + +import net.minecraft.world.World; +import StevenDimDoors.mod_pocketDim.Point3D; + +public class CompactBoundsOperation extends WorldOperation +{ + private int minX; + private int minY; + private int minZ; + private int maxX; + private int maxY; + private int maxZ; + + public CompactBoundsOperation() + { + super("CompactBoundsOperation"); + } + + @Override + protected boolean initialize(World world, int x, int y, int z, int width, int height, int length) + { + minX = Integer.MAX_VALUE; + minY = Integer.MAX_VALUE; + minZ = Integer.MAX_VALUE; + maxX = x; + maxY = y; + maxZ = z; + return true; + } + + @Override + protected boolean applyToBlock(World world, int x, int y, int z) + { + //This could be done more efficiently, but honestly, this is the simplest approach and it + //makes it easy for us to verify that the code is correct. + if (!world.isAirBlock(x, y, z)) + { + maxX = x > maxX ? x : maxX; + maxZ = z > maxZ ? z : maxZ; + maxY = y > maxY ? y : maxY; + + minX = x < minX ? x : minX; + minZ = z < minZ ? z : minZ; + minY = y < minY ? y : minY; + } + return true; + } + + protected boolean finish() + { + if (minX == Integer.MAX_VALUE) + { + //The whole search space was empty! + //Compact the space to a single block. + minX = maxX; + minY = maxY; + minZ = maxZ; + return false; + } + return true; + } + + public Point3D getMaxCorner() + { + return new Point3D(maxX, maxY, maxZ); + } + + public Point3D getMinCorner() + { + return new Point3D(minX, minY, minZ); + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/CompoundFilter.java b/StevenDimDoors/mod_pocketDim/schematic/CompoundFilter.java new file mode 100644 index 0000000..882faa5 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/CompoundFilter.java @@ -0,0 +1,58 @@ +package StevenDimDoors.mod_pocketDim.schematic; + +import java.util.ArrayList; + +public class CompoundFilter extends SchematicFilter { + + private ArrayList filters; + + public CompoundFilter() + { + super("CompoundFilter"); + filters = new ArrayList(); + } + + public void addFilter(SchematicFilter filter) + { + filters.add(filter); + } + + @Override + protected boolean initialize(Schematic schematic, short[] blocks, byte[] metadata) + { + for (SchematicFilter filter : filters) + { + if (!filter.initialize(schematic, blocks, metadata)) + { + return false; + } + } + return !filters.isEmpty(); + } + + @Override + protected boolean finish() + { + for (SchematicFilter filter : filters) + { + if (!filter.finish()) + { + return false; + } + } + return true; + } + + @Override + protected boolean applyToBlock(int index, short[] blocks, byte[] metadata) + { + for (SchematicFilter filter : filters) + { + if (filter.applyToBlock(index, blocks, metadata)) + { + return filter.terminates(); + } + } + return false; + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/InvalidSchematicException.java b/StevenDimDoors/mod_pocketDim/schematic/InvalidSchematicException.java new file mode 100644 index 0000000..d1307a7 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/InvalidSchematicException.java @@ -0,0 +1,21 @@ +package StevenDimDoors.mod_pocketDim.schematic; + +public class InvalidSchematicException extends Exception { + + private static final long serialVersionUID = -1011044077455149932L; + + public InvalidSchematicException() + { + super(); + } + + public InvalidSchematicException(String message) + { + super(message); + } + + public InvalidSchematicException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/ReplacementFilter.java b/StevenDimDoors/mod_pocketDim/schematic/ReplacementFilter.java new file mode 100644 index 0000000..e55a7db --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/ReplacementFilter.java @@ -0,0 +1,76 @@ +package StevenDimDoors.mod_pocketDim.schematic; + + +public class ReplacementFilter extends SchematicFilter { + + private short targetBlock; + private byte targetMetadata; + private boolean matchMetadata; + private short replacementBlock; + private byte replacementMetadata; + private boolean changeMetadata; + + public ReplacementFilter(short targetBlock, byte targetMetadata, short replacementBlock, byte replacementMetadata) + { + super("ReplacementFilter"); + this.targetBlock = targetBlock; + this.targetMetadata = targetMetadata; + this.matchMetadata = true; + this.replacementBlock = replacementBlock; + this.replacementMetadata = replacementMetadata; + this.changeMetadata = true; + } + + public ReplacementFilter(short targetBlock, short replacementBlock, byte replacementMetadata) + { + super("ReplacementFilter"); + this.targetBlock = targetBlock; + this.matchMetadata = false; + this.replacementBlock = replacementBlock; + this.replacementMetadata = replacementMetadata; + this.changeMetadata = true; + } + + public ReplacementFilter(short targetBlock, byte targetMetadata, short replacementBlock) + { + super("ReplacementFilter"); + this.targetBlock = targetBlock; + this.targetMetadata = targetMetadata; + this.matchMetadata = true; + this.replacementBlock = replacementBlock; + this.changeMetadata = false; + } + + public ReplacementFilter(short targetBlock, short replacementBlock) + { + super("ReplacementFilter"); + this.targetBlock = targetBlock; + this.matchMetadata = false; + this.replacementBlock = replacementBlock; + this.changeMetadata = false; + } + + @Override + protected boolean applyToBlock(int index, short[] blocks, byte[] metadata) + { + if (blocks[index] == targetBlock) + { + if ((matchMetadata && metadata[index] == targetMetadata) || !matchMetadata) + { + blocks[index] = replacementBlock; + if (changeMetadata) + { + metadata[index] = replacementMetadata; + } + return true; + } + } + return false; + } + + @Override + protected boolean terminates() + { + return false; + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/Schematic.java b/StevenDimDoors/mod_pocketDim/schematic/Schematic.java new file mode 100644 index 0000000..c81da44 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/Schematic.java @@ -0,0 +1,434 @@ +package StevenDimDoors.mod_pocketDim.schematic; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import net.minecraft.block.Block; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import StevenDimDoors.mod_pocketDim.Point3D; + +/** + * Represents an MC schematic and provides functions for loading, storing, and manipulating schematics. + * This functionality has no dependencies to Dimensional Doors. + */ +public class Schematic { + + protected short width; + protected short height; + protected short length; + + protected short[] blocks; + protected byte[] metadata; + protected NBTTagList tileEntities; + + protected Schematic(short width, short height, short length, short[] blocks, byte[] metadata, NBTTagList tileEntities) + { + this.width = width; + this.height = height; + this.length = length; + this.blocks = blocks; + this.metadata = metadata; + this.tileEntities = tileEntities; + } + + protected Schematic(Schematic source) + { + //Shallow copy constructor - critical for code reuse in derived classes since + //source's fields will be inaccessible if the derived class is in another package. + this.width = source.width; + this.height = source.height; + this.length = source.length; + this.blocks = source.blocks; + this.metadata = source.metadata; + this.tileEntities = source.tileEntities; + } + + public int calculateIndex(int x, int y, int z) + { + if (x < 0 || x >= width) + throw new IndexOutOfBoundsException("x must be non-negative and less than width"); + if (y < 0 || y >= height) + throw new IndexOutOfBoundsException("y must be non-negative and less than height"); + if (z < 0 || z >= length) + throw new IndexOutOfBoundsException("z must be non-negative and less than length"); + + return (y * width * length + z * width + x); + } + + public Point3D calculatePoint(int index) + { + int y = index / (width * length); + index -= y * width * length; + int z = index / width; + index -= z * width; + int x = index; + + return new Point3D(x, y, z); + } + + public int calculateIndexBelow(int index) + { + return index - (width * length); + } + + public short getWidth() + { + return width; + } + + public short getHeight() + { + return height; + } + + public short getLength() + { + return length; + } + + public short getBlockID(int x, int y, int z) + { + return blocks[calculateIndex(x, y, z)]; + } + + public byte getBlockMetadata(int x, int y, int z) + { + return metadata[calculateIndex(x, y, z)]; + } + + public NBTTagList getTileEntities() + { + return (NBTTagList) tileEntities.copy(); + } + + public static Schematic readFromFile(String schematicPath) throws FileNotFoundException, InvalidSchematicException + { + return readFromFile(new File(schematicPath)); + } + + public static Schematic readFromFile(File schematicFile) throws FileNotFoundException, InvalidSchematicException + { + return readFromStream(new FileInputStream(schematicFile)); + } + + public static Schematic readFromResource(String resourcePath) throws InvalidSchematicException + { + //We need an instance of a class in the mod to retrieve a resource + Schematic empty = new Schematic((short) 0, (short) 0, (short) 0, null, null, null); + InputStream schematicStream = empty.getClass().getResourceAsStream(resourcePath); + return readFromStream(schematicStream); + } + + public static Schematic readFromStream(InputStream schematicStream) throws InvalidSchematicException + { + short width; + short height; + short length; + int volume; + int pairs; + + byte[] metadata = null; //block metadata + byte[] lowBits = null; //first 8 bits of the block IDs + byte[] highBits = null; //additional 4 bits of the block IDs + short[] blocks = null; //list of combined block IDs + NBTTagList tileEntities = null; //storage for tile entities in NBT form + NBTTagCompound schematicTag; //the NBT data extracted from the schematic file + boolean hasExtendedBlockIDs; //indicates whether the schematic contains extended block IDs + + try + { + try + { + schematicTag = CompressedStreamTools.readCompressed(schematicStream); + schematicStream.close(); //readCompressed() probably closes the stream anyway, but close again to be sure. + } + catch (Exception ex) + { + throw new InvalidSchematicException("The schematic could not be decoded."); + } + + //load size of schematic to generate + width = schematicTag.getShort("Width"); + height = schematicTag.getShort("Height"); + length = schematicTag.getShort("Length"); + volume = width * length * height; + + if (width < 0) + throw new InvalidSchematicException("The schematic cannot have a negative width."); + if (height < 0) + throw new InvalidSchematicException("The schematic cannot have a negative height."); + if (length < 0) + throw new InvalidSchematicException("The schematic cannot have a negative length."); + + //load block info + lowBits = schematicTag.getByteArray("Blocks"); + highBits = schematicTag.getByteArray("AddBlocks"); + metadata = schematicTag.getByteArray("Data"); + hasExtendedBlockIDs = (highBits.length != 0); + + if (volume != lowBits.length) + throw new InvalidSchematicException("The schematic has data for fewer blocks than its dimensions indicate."); + if (volume != metadata.length) + throw new InvalidSchematicException("The schematic has metadata for fewer blocks than its dimensions indicate."); + if (volume > 2 * highBits.length && hasExtendedBlockIDs) + throw new InvalidSchematicException("The schematic has extended block IDs for fewer blocks than its dimensions indicate."); + + blocks = new short[volume]; + if (hasExtendedBlockIDs) + { + //Combine the split block IDs into a single value + pairs = volume - (volume & 1); + int index; + for (index = 0; index < pairs; index += 2) + { + blocks[index] = (short) (((highBits[index >> 1] & 0x0F) << 8) + (lowBits[index] & 0xFF)); + blocks[index + 1] = (short) (((highBits[index >> 1] & 0xF0) << 4) + (lowBits[index + 1] & 0xFF)); + } + if (index < volume) + { + blocks[index] = lowBits[index >> 1]; + } + } + else + { + //Copy the blockIDs + for (int index = 0; index < volume; index++) + { + blocks[index] = (short) (lowBits[index] & 0xFF); + } + } + + //Get the list of tile entities + tileEntities = schematicTag.getTagList("TileEntities"); + + Schematic result = new Schematic(width, height, length, blocks, metadata, tileEntities); + return result; + } + catch (InvalidSchematicException ex) + { + //Throw the exception again to pass it to the caller. + throw ex; + } + catch (Exception ex) + { + throw new InvalidSchematicException("An unexpected error occurred while trying to decode the schematic.", ex); + } + } + + public static Schematic copyFromWorld(World world, int x, int y, int z, short width, short height, short length, boolean doCompactBounds) + { + if (doCompactBounds) + { + //Adjust the vertical bounds to reasonable values if necessary + int worldHeight = world.getHeight(); + int fixedY = (y < 0) ? 0 : y; + int fixedHeight = height + y - fixedY; + + if (fixedHeight + fixedY >= worldHeight) + { + fixedHeight = worldHeight - fixedY; + } + + //Compact the area to be copied to remove empty borders + CompactBoundsOperation compactor = new CompactBoundsOperation(); + compactor.apply(world, x, fixedY, z, width, fixedHeight, length); + Point3D minCorner = compactor.getMinCorner(); + Point3D maxCorner = compactor.getMaxCorner(); + + short compactWidth = (short) (maxCorner.getX() - minCorner.getX() + 1); + short compactHeight = (short) (maxCorner.getY() - minCorner.getY() + 1); + short compactLength = (short) (maxCorner.getZ() - minCorner.getZ() + 1); + + return copyFromWorld(world, minCorner.getX(), minCorner.getY(), minCorner.getZ(), + compactWidth, compactHeight, compactLength); + } + else + { + return copyFromWorld(world, x, y, z, width, height, length); + } + } + + private static Schematic copyFromWorld(World world, int x, int y, int z, short width, short height, short length) + { + //Short and sweet ^_^ + WorldCopyOperation copier = new WorldCopyOperation(); + copier.apply(world, x, y, z, width, height, length); + return new Schematic(width, height, length, copier.getBlockIDs(), copier.getMetadata(), copier.getTileEntities()); + } + + private static boolean encodeBlockIDs(short[] blocks, byte[] lowBits, byte[] highBits) + { + int index; + int length = blocks.length - (blocks.length & 1); + boolean hasHighBits = false; + for (index = 0; index < length; index += 2) + { + highBits[index >> 1] = (byte) (((blocks[index] >> 8) & 0x0F) + ((blocks[index + 1] >> 4) & 0xF0)); + hasHighBits |= (highBits[index >> 1] != 0); + } + if (index < blocks.length) + { + highBits[index >> 1] = (byte) ((blocks[index] >> 8) & 0x0F); + hasHighBits |= (highBits[index >> 1] != 0); + } + for (index = 0; index < blocks.length; index++) + { + lowBits[index] = (byte) (blocks[index] & 0xFF); + } + return hasHighBits; + } + + public NBTTagCompound writeToNBT() + { + return writeToNBT(true); + } + + protected NBTTagCompound writeToNBT(boolean copyTileEntities) + { + return writeToNBT(width, height, length, blocks, metadata, tileEntities, copyTileEntities); + } + + protected static NBTTagCompound writeToNBT(short width, short height, short length, short[] blocks, byte[] metadata, + NBTTagList tileEntities, boolean copyTileEntities) + { + //This is the main storage function. Schematics are really compressed NBT tags, so if we can generate + //the tags, most of the work is done. All the other storage functions will rely on this one. + + NBTTagCompound schematicTag = new NBTTagCompound("Schematic"); + + schematicTag.setShort("Width", width); + schematicTag.setShort("Length", length); + schematicTag.setShort("Height", height); + + schematicTag.setTag("Entities", new NBTTagList()); + schematicTag.setString("Materials", "Alpha"); + + byte[] lowBits = new byte[blocks.length]; + byte[] highBits = new byte[(blocks.length >> 1) + 1]; + boolean hasExtendedIDs = encodeBlockIDs(blocks, lowBits, highBits); + + schematicTag.setByteArray("Blocks", lowBits); + schematicTag.setByteArray("Data", metadata); + + if (hasExtendedIDs) + { + schematicTag.setByteArray("AddBlocks", highBits); + } + + if (copyTileEntities) + { + //Used when the result of this function will be passed outside this class. + //Avoids exposing the private field to external modifications. + schematicTag.setTag("TileEntities", (NBTTagList) tileEntities.copy()); + } + else + { + //Used when the result of this function is for internal use. + //It's more efficient not to copy the tags unless it's needed. + schematicTag.setTag("TileEntities", tileEntities); + } + return schematicTag; + } + + public void writeToFile(String schematicPath) throws IOException + { + writeToFile(new File(schematicPath)); + } + + public void writeToFile(File schematicFile) throws IOException + { + FileOutputStream outputStream = new FileOutputStream(schematicFile); + CompressedStreamTools.writeCompressed(writeToNBT(false), outputStream); + //writeCompressed() probably closes the stream on its own - call close again just in case. + //Closing twice will not throw an exception. + outputStream.close(); + } + + public boolean applyFilter(SchematicFilter filter) + { + return filter.apply(this, this.blocks, this.metadata); + } + + public void copyToWorld(World world, int x, int y, int z) + { + //This isn't implemented as a WorldOperation because it doesn't quite fit the structure of those operations. + //It's not worth the trouble in this case. + int index; + int count; + int dx, dy, dz; + + //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++) + { + //In the future, we might want to make this more efficient by building whole chunks at a time + setBlockDirectly(world, x + dx, y + dy, z + dz, blocks[index], metadata[index]); + 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 + dx = tileTag.getInteger("x") + x; + dy = tileTag.getInteger("y") + y; + dz = tileTag.getInteger("z") + z; + tileTag.setInteger("x", dx); + tileTag.setInteger("y", dy); + tileTag.setInteger("z", dz); + //Load the tile entity and put it in the world + world.setBlockTileEntity(dx, dy, dz, TileEntity.createAndLoadEntity(tileTag)); + } + } + + protected static void setBlockDirectly(World world, int x, int y, int z, int blockID, int metadata) + { + if (blockID != 0 && Block.blocksList[blockID] == null) + { + return; + } + + int cX = x >> 4; + int cZ = z >> 4; + int cY = y >> 4; + Chunk chunk; + + int localX = (x % 16) < 0 ? (x % 16) + 16 : (x % 16); + int localZ = (z % 16) < 0 ? (z % 16) + 16 : (z % 16); + ExtendedBlockStorage extBlockStorage; + + try + { + chunk = world.getChunkFromChunkCoords(cX, cZ); + extBlockStorage = chunk.getBlockStorageArray()[cY]; + if (extBlockStorage == null) + { + extBlockStorage = new ExtendedBlockStorage(cY << 4, !world.provider.hasNoSky); + chunk.getBlockStorageArray()[cY] = extBlockStorage; + } + extBlockStorage.setExtBlockID(localX, y & 15, localZ, blockID); + extBlockStorage.setExtBlockMetadata(localX, y & 15, localZ, metadata); + } + catch(Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/SchematicFilter.java b/StevenDimDoors/mod_pocketDim/schematic/SchematicFilter.java new file mode 100644 index 0000000..7e80959 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/SchematicFilter.java @@ -0,0 +1,55 @@ +package StevenDimDoors.mod_pocketDim.schematic; + +public class SchematicFilter { + + private String name; + + protected SchematicFilter(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } + + public boolean apply(Schematic schematic, short[] blocks, byte[] metadata) + { + if (!initialize(schematic, blocks, metadata)) + return false; + + for (int index = 0; index < blocks.length; index++) + { + if (applyToBlock(index, blocks, metadata) && terminates()) + return false; + } + + return finish(); + } + + protected boolean initialize(Schematic schematic, short[] blocks, byte[] metadata) + { + return true; + } + + protected boolean applyToBlock(int index, short[] blocks, byte[] metadata) + { + return true; + } + + protected boolean finish() + { + return true; + } + + protected boolean terminates() + { + return true; + } + + public String toString() + { + return name; + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/WorldCopyOperation.java b/StevenDimDoors/mod_pocketDim/schematic/WorldCopyOperation.java new file mode 100644 index 0000000..fe49a78 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/WorldCopyOperation.java @@ -0,0 +1,76 @@ +package StevenDimDoors.mod_pocketDim.schematic; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +public class WorldCopyOperation extends WorldOperation +{ + private int originX; + private int originY; + private int originZ; + private int index; + private short[] blockIDs; + private byte[] metadata; + private NBTTagList tileEntities; + + public WorldCopyOperation() + { + super("WorldCopyOperation"); + blockIDs = null; + metadata = null; + tileEntities = null; + } + + @Override + protected boolean initialize(World world, int x, int y, int z, int width, int height, int length) + { + index = 0; + originX = x; + originY = y; + originZ = z; + blockIDs = new short[width * height * length]; + metadata = new byte[width * height * length]; + tileEntities = new NBTTagList(); + return true; + } + + @Override + protected boolean applyToBlock(World world, int x, int y, int z) + { + blockIDs[index] = (short) world.getBlockId(x, y, z); + metadata[index] = (byte) world.getBlockMetadata(x, y, z); + + TileEntity tileEntity = world.getBlockTileEntity(x, y, z); + if (tileEntity != null) + { + //Extract tile entity data + NBTTagCompound tileTag = new NBTTagCompound(); + tileEntity.writeToNBT(tileTag); + //Translate the tile entity's position from the world's coordinate system + //to the schematic's coordinate system. + tileTag.setInteger("x", x - originX); + tileTag.setInteger("y", y - originY); + tileTag.setInteger("z", z - originZ); + tileEntities.appendTag(tileTag); + } + index++; //This works assuming the loops in WorldOperation are done in YZX order + return true; + } + + public short[] getBlockIDs() + { + return blockIDs; + } + + public byte[] getMetadata() + { + return metadata; + } + + public NBTTagList getTileEntities() + { + return tileEntities; + } +} diff --git a/StevenDimDoors/mod_pocketDim/schematic/WorldOperation.java b/StevenDimDoors/mod_pocketDim/schematic/WorldOperation.java new file mode 100644 index 0000000..85fa540 --- /dev/null +++ b/StevenDimDoors/mod_pocketDim/schematic/WorldOperation.java @@ -0,0 +1,76 @@ +package StevenDimDoors.mod_pocketDim.schematic; + +import StevenDimDoors.mod_pocketDim.Point3D; +import net.minecraft.world.World; + +public abstract class WorldOperation { + + private String name; + + public WorldOperation(String name) + { + this.name = name; + } + + protected boolean initialize(World world, int x, int y, int z, int width, int height, int length) + { + return true; + } + + protected abstract boolean applyToBlock(World world, int x, int y, int z); + + protected boolean finish() + { + return true; + } + + public boolean apply(World world, Point3D minCorner, Point3D maxCorner) + { + int x = minCorner.getX(); + int y = minCorner.getY(); + int z = minCorner.getZ(); + int width = maxCorner.getX() - x + 1; + int height = maxCorner.getY() - y + 1; + int length = maxCorner.getZ() - z + 1; + return apply(world, x, y, z, width, height, length); + } + + public boolean apply(World world, int x, int y, int z, int width, int height, int length) + { + if (!initialize(world, x, y, z, width, height, length)) + return false; + + int cx, cy, cz; + int limitX = x + width; + int limitY = y + height; + int limitZ = z + length; + + //The order of these loops is important. Don't change it! It's used to avoid calculating + //indeces in some schematic operations. The proper order is YZX. + for (cy = y; cy < limitY; cy++) + { + for (cz = z; cz < limitZ; cz++) + { + for (cx = x; cx < limitX; cx++) + { + if (!applyToBlock(world, cx, cy, cz)) + return false; + } + } + } + + return finish(); + } + + + public String getName() + { + return name; + } + + @Override + public String toString() + { + return name; + } +}