Default dungeon templates

- Add setBlockState to Schematic
 - Add Schematic constructors that generate an air-filled pocket of the right size
 - Add blank pockets
 - Add void pockets
 - Make /pocket command takes you to center of pocket
 - Quick registry null pointer fix
This commit is contained in:
Runemoro 2018-01-19 23:20:44 -05:00
parent d8d3fa4fc9
commit 86b58c2f9e
40 changed files with 197 additions and 70 deletions

View file

@ -42,11 +42,32 @@ public class Schematic {
public short length;
public int[] offset = {0, 0, 0};
public int paletteMax;
public List<IBlockState> pallette = new ArrayList<>();
public List<IBlockState> palette = new ArrayList<>();
public int[][][] blockData; //[x][y][z]
public List<NBTTagCompound> tileEntities = new ArrayList<>();
public List<NBTTagCompound> entities = new ArrayList<>(); // Not in the specification, but we need this
public Schematic() {
paletteMax = -1;
}
public Schematic(short width, short height, short length) {
this();
this.width = width;
this.height = height;
this.length = length;
blockData = new int[width][length][height];
palette.add(Blocks.AIR.getDefaultState());
paletteMax++;
creationDate = System.currentTimeMillis();
}
public Schematic(String name, String author, short width, short height, short length) {
this(width, height, length);
this.name = name;
this.author = author;
}
public static Schematic loadFromNBT(NBTTagCompound nbt) {
Schematic schematic = new Schematic();
schematic.version = nbt.getInteger("Version"); //Version is required
@ -106,12 +127,12 @@ public class Schematic {
String[] properties = stateString.split(",");
blockstate = getBlockStateWithProperties(block, properties);
}
schematic.pallette.add(blockstate); //@todo, can we assume that a schematic file always has all palette integers used from 0 to pallettemax-1?
schematic.palette.add(blockstate); //@todo, can we assume that a schematic file always has all palette integers used from 0 to pallettemax-1?
}
if (nbt.hasKey("PaletteMax")) { //PaletteMax is not required
schematic.paletteMax = nbt.getInteger("PaletteMax");
} else {
schematic.paletteMax = schematic.pallette.size() - 1;
schematic.paletteMax = schematic.palette.size() - 1;
}
byte[] blockDataIntArray = nbt.getByteArray("BlockData"); //BlockData is required
@ -165,8 +186,8 @@ public class Schematic {
nbt.setInteger("PaletteMax", schematic.paletteMax);
NBTTagCompound paletteNBT = new NBTTagCompound();
for (int i = 0; i < schematic.pallette.size(); i++) {
IBlockState state = schematic.pallette.get(i);
for (int i = 0; i < schematic.palette.size(); i++) {
IBlockState state = schematic.palette.get(i);
String blockStateString = getBlockStateStringFromState(state);
paletteNBT.setInteger(blockStateString, i);
}
@ -251,6 +272,7 @@ public class Schematic {
return totalString;
}
// TODO: use the setBlockState method
public static Schematic createFromWorld(World world, Vector3i from, Vector3i to) {
Schematic schematic = new Schematic();
@ -297,7 +319,7 @@ public class Schematic {
schematic.blockData[pos.getX()][pos.getY()][pos.getZ()] = i;
}
schematic.pallette.add(i, keys[i]);
schematic.palette.add(i, keys[i]);
}
for (Entity entity : world.getEntitiesInAABBexcluding(null, getBoundingBox(from, to), entity -> !(entity instanceof EntityPlayerMP))) {
@ -314,7 +336,7 @@ public class Schematic {
}
schematic.requiredMods = mods.toArray(new String[mods.size()]);
schematic.paletteMax = keys.length;
schematic.paletteMax = keys.length - 1;
schematic.creationDate = System.currentTimeMillis();
return schematic;
@ -326,7 +348,7 @@ public class Schematic {
public static void place(Schematic schematic, World world, int xBase, int yBase, int zBase) { // TODO: check if entities and tileentities are within pocket bounds
// Place the schematic's blocks
List<IBlockState> palette = schematic.pallette;
List<IBlockState> palette = schematic.palette;
int[][][] blockData = schematic.blockData;
Set<Chunk> changedChunks = new HashSet<>();
long start = System.currentTimeMillis();
@ -404,4 +426,13 @@ public class Schematic {
world.spawnEntity(entity);
}
}
public void setBlockState(int x, int y, int z, IBlockState state) {
if (palette.contains(state)) {
blockData[x][y][z] = palette.indexOf(state); // TODO: optimize this (there must be some efficient list implementations)
} else {
palette.add(state);
blockData[x][y][z] = ++paletteMax;
}
}
}

View file

@ -96,7 +96,8 @@ public class CommandPocket extends CommandBase {
TileEntityRift entrance = (TileEntityRift) player.world.getTileEntity(pocket.getEntrance().getPos());
entrance.teleportTo(player);
} else {
TeleportUtils.teleport(player, new Location(player.world, pocket.getOrigin().add(30, 30, 30)));
int size = (pocket.getSize() + 1) * 16;
TeleportUtils.teleport(player, new Location(player.world, pocket.getOrigin().add(size / 2, size / 2, size / 2)));
}
} else {
DimDoors.log.info("Not executing command /" + getName() + " because it wasn't sent by a player.");

View file

@ -59,7 +59,7 @@ public class CommandSaveSchem extends CommandBase {
}
Pocket pocket = PocketRegistry.instance(player.dimension).getPocketAt(player.getPosition());
Schematic schematic = Schematic.createFromWorld(player.world, toVector3i(pocket.getOrigin()), toVector3i(pocket.getOrigin()).add(Vector3i.from((pocket.getSize() + 1) * 16)));
Schematic schematic = Schematic.createFromWorld(player.world, toVector3i(pocket.getOrigin()), toVector3i(pocket.getOrigin()).add(Vector3i.from((pocket.getSize() + 1) * 16 - 1)));
schematic.name = args[0];
schematic.author = player.getName();

View file

@ -42,7 +42,7 @@ public class SchematicHandler {
public void loadSchematics() {
templates = new ArrayList<>();
String[] names = {"default_dungeon_normal", "default_dungeon_nether", "default_private", "default_public"}; // TODO: don't hardcode
String[] names = {"default_dungeon_normal", "default_dungeon_nether", "default_private", "default_public", "default_blank"}; // TODO: don't hardcode
for (String name : names) {
try {
URL resource = DimDoors.class.getResource("/assets/dimdoors/pockets/json/" + name + ".json");

View file

@ -95,7 +95,7 @@ public class RiftRegistry extends WorldSavedData {
// method rather than RiftSubregistry) and save each in the appropriate registry, we can't do this because it is not
// always the case that all worlds will be saved at once.
@Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
if (riftRegistry == null) RiftRegistry.instance();
if (riftRegistry == null) riftRegistry = RiftRegistry.instance();
// Write rifts in this dimension
NBTTagList riftsNBT = new NBTTagList();
NBTTagList pocketsNBT = new NBTTagList();

View file

@ -3,7 +3,6 @@ package org.dimdev.dimdoors.shared.tools;
import net.minecraft.block.Block;
import net.minecraft.block.BlockDoor;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.init.Bootstrap;
import net.minecraft.item.EnumDyeColor;
import net.minecraft.nbt.CompressedStreamTools;
@ -19,6 +18,7 @@ import org.dimdev.ddutils.schem.Schematic;
import org.dimdev.dimdoors.DimDoors;
import org.dimdev.dimdoors.server.ServerProxy;
import org.dimdev.dimdoors.shared.blocks.BlockDimensionalDoor;
import org.dimdev.dimdoors.shared.blocks.BlockFabric;
import org.dimdev.dimdoors.shared.blocks.BlockFabricAncient;
import org.dimdev.dimdoors.shared.blocks.ModBlocks;
import org.dimdev.dimdoors.shared.rifts.RiftDestination;
@ -73,102 +73,102 @@ public final class PocketSchematicGenerator {
}
// Generate the schematics
List<Schematic> schematics = generatePocketSchematics(8);
List<Schematic> schematics = generatePocketSchematics();
// Save the schematics
boolean isPublic = true;
String[] saveFolders = {"public/", "private/", "blank/", "blank/"};
int i = 0;
for (Schematic schematic : schematics) {
NBTTagCompound schematicNBT = Schematic.saveToNBT(schematic);
File saveFile = new File(schematicDir, (isPublic ? "public/" : "private/") + schematic.name + ".schem");
File saveFile = new File(schematicDir, saveFolders[i++ % saveFolders.length] + schematic.name + ".schem");
saveFile.getParentFile().mkdirs();
DataOutputStream schematicDataStream = new DataOutputStream(new FileOutputStream(saveFile));
CompressedStreamTools.writeCompressed(schematicNBT, schematicDataStream);
schematicDataStream.flush();
schematicDataStream.close();
isPublic = !isPublic;
}
// TODO: also generate JSON files
}
public static List<Schematic> generatePocketSchematics(int maxPocketSize) {
public static List<Schematic> generatePocketSchematics() {
List<Schematic> schematics = new ArrayList<>();
for (int pocketSize = 0; pocketSize < maxPocketSize; pocketSize++) {
schematics.add(generatePocketSchematic(
for (int pocketSize = 0; pocketSize < 8; pocketSize++) {
schematics.add(generateBlankWithDoor(
"public_pocket", // base name
pocketSize, // size
ModBlocks.ANCIENT_FABRIC.getDefaultState(), // outer wall
ModBlocks.FABRIC.getDefaultState(), // inner wall
ModBlocks.DIMENSIONAL_DOOR, // door
PocketExitDestination.builder().build(),// exit rift destination
1)); // TODO: pass destination rather than just chaos weight
schematics.add(generatePocketSchematic(
LinkProperties.builder()
.groups(Collections.singleton(1))
.linksRemaining(1)
.entranceWeight(1)
.floatingWeight(1)
.build()));
schematics.add(generateBlankWithDoor(
"private_pocket", // base name
pocketSize, // size
ModBlocks.ANCIENT_FABRIC.getDefaultState().withProperty(BlockFabricAncient.COLOR, EnumDyeColor.WHITE), // outer wall
ModBlocks.FABRIC.getDefaultState().withProperty(BlockFabricAncient.COLOR, EnumDyeColor.WHITE), // inner wall
ModBlocks.PERSONAL_DIMENSIONAL_DOOR, // door
PrivatePocketExitDestination.builder().build(),// exit rift destination
0));
null));
schematics.add(generateBlank("blank_pocket",
pocketSize,
ModBlocks.ANCIENT_FABRIC.getDefaultState(),
ModBlocks.FABRIC.getDefaultState()));
schematics.add(generateFrame("void_pocket",
pocketSize,
ModBlocks.FABRIC.getDefaultState().withProperty(BlockFabric.COLOR, EnumDyeColor.LIGHT_BLUE)));
}
return schematics;
}
private static Schematic generatePocketSchematic(String baseName, int pocketSize, IBlockState outerWallBlockState, IBlockState innerWallBlockState, BlockDimensionalDoor doorBlock, RiftDestination exitDest, float chaosWeight) {
int size = (pocketSize + 1) * 16 - 1; // -1 so that the door can be centered
private static Schematic generateBlank(String baseName, int pocketSize, IBlockState outerWall, IBlockState innerWall) {
short size = (short) ((pocketSize + 1) * 16 - 1); // -1 so that the door can be centered
// Set schematic info
Schematic schematic = new Schematic();
schematic.version = 1;
schematic.author = "Robijnvogel"; //@todo set in build.gradle ${modID}
schematic.name = baseName + "_" + pocketSize;
schematic.creationDate = System.currentTimeMillis();
schematic.requiredMods = new String[1];
schematic.requiredMods[0] = DimDoors.MODID;
schematic.width = (short) size;
schematic.height = (short) size;
schematic.length = (short) size;
schematic.offset = new int[]{0, 0, 0}; // TODO: center pockets
// Generate the pallette
schematic.paletteMax = 4;
schematic.pallette = new ArrayList<>();
schematic.pallette.add(Blocks.AIR.getDefaultState());
schematic.pallette.add(outerWallBlockState);
schematic.pallette.add(innerWallBlockState);
schematic.pallette.add(doorBlock.getDefaultState().withProperty(BlockDoor.HALF, BlockDoor.EnumDoorHalf.LOWER)); //bottom
schematic.pallette.add(doorBlock.getDefaultState().withProperty(BlockDoor.HALF, BlockDoor.EnumDoorHalf.UPPER)); //top
Schematic schematic = new Schematic(baseName + "_" + pocketSize, "DimDoors", size, size, size);
schematic.requiredMods = new String[] { DimDoors.MODID };
// Set block data
schematic.blockData = new int[size][size][size]; //[x][y][z]
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
for (int z = 0; z < size; z++) {
int layer = Collections.min(Arrays.asList(x, y, z, size - 1 - x, size - 1 - y, size - 1 - z));
if (layer == 0) {
schematic.blockData[x][y][z] = 1; // outer wall
schematic.setBlockState(x, y, z, outerWall);
} else if (layer < 5) {
schematic.blockData[x][y][z] = 2; // inner wall
} else {
schematic.blockData[x][y][z] = 0; // air
schematic.setBlockState(x, y, z, innerWall);
}
}
}
}
schematic.blockData[(size - 1) / 2][5][4] = 3; // door bottom
schematic.blockData[(size - 1) / 2][6][4] = 4; // door top
// Generate the rift TileEntities
return schematic;
}
private static Schematic generateBlankWithDoor(String baseName, int pocketSize, IBlockState outerWall, IBlockState innerWall, BlockDimensionalDoor doorBlock, RiftDestination exitDest, LinkProperties link) {
short size = (short) ((pocketSize + 1) * 16 - 1); // -1 so that the door can be centered
// Make the schematic
Schematic schematic = generateBlank(baseName, pocketSize, outerWall, innerWall);
// Add the door
schematic.setBlockState((size - 1) / 2, 5, 4, doorBlock.getDefaultState().withProperty(BlockDoor.HALF, BlockDoor.EnumDoorHalf.LOWER));
schematic.setBlockState((size - 1) / 2, 6, 4, doorBlock.getDefaultState().withProperty(BlockDoor.HALF, BlockDoor.EnumDoorHalf.UPPER));
// Set the rift entities
schematic.tileEntities = new ArrayList<>();
TileEntityEntranceRift rift = (TileEntityEntranceRift) doorBlock.createTileEntity(null, doorBlock.getDefaultState());
rift.setDestination(PocketEntranceDestination.builder()
.ifDestination(exitDest)
.build());
rift.setProperties(LinkProperties.builder()
.groups(Collections.singleton(1))
.linksRemaining(1)
.entranceWeight(chaosWeight)
.floatingWeight(chaosWeight)
.build());
rift.setProperties(link);
rift.setPlaceRiftOnBreak(true);
NBTTagCompound tileNBT = rift.serializeNBT();
@ -179,4 +179,30 @@ public final class PocketSchematicGenerator {
return schematic;
}
private static Schematic generateFrame(String baseName, int chunkSize, IBlockState frame) {
short size = (short) ((chunkSize + 1) * 16 - 1); // -1 so that the door can be centered
// Set schematic info
Schematic schematic = new Schematic(baseName + "_" + chunkSize, "DimDoors", size, size, size);
schematic.requiredMods = new String[] { DimDoors.MODID };
// Set block data
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
for (int z = 0; z < size; z++) {
int sides = 0;
if (x == 0 || x == size - 1) sides++;
if (y == 0 || y == size - 1) sides++;
if (z == 0 || z == size - 1) sides++;
if (sides >= 2) {
schematic.setBlockState(x, y, z, frame);
}
}
}
}
return schematic;
}
}

View file

@ -53,7 +53,7 @@ public final class SchematicConverter {
byte[] blockIdArray = nbt.getByteArray("Blocks");
byte[] addId = nbt.getByteArray("AddBlocks");
Map<Integer, Byte> palletteMap = new HashMap<>(); // block ID -> pallette index
Map<Integer, Byte> palletteMap = new HashMap<>(); // block ID -> palette index
byte currentPalletteIndex = 0;
for (int i = 0; i < blockIdArray.length; i++) {
int id;
@ -94,7 +94,7 @@ public final class SchematicConverter {
}
if (block.equals(Blocks.IRON_DOOR)) block = ModBlocks.DIMENSIONAL_DOOR.getDefaultState();
if (block.equals(Blocks.OAK_DOOR)) block = ModBlocks.WARP_DIMENSIONAL_DOOR.getDefaultState();
schematic.pallette.add(block);
schematic.palette.add(block);
palletteMap.put(id, currentPalletteIndex);
blockIdArray[i] = currentPalletteIndex;
currentPalletteIndex++;
@ -129,7 +129,7 @@ public final class SchematicConverter {
int blockInt = blockIdArray[x + z * schematic.width + y * schematic.width * schematic.length]; //according to the documentation on https://github.com/SpongePowered/Schematic-Specification/blob/master/versions/schematic-1.md
int metadata = dataIntArray[x + z * schematic.width + y * schematic.width * schematic.length]; //according to the documentation on https://github.com/SpongePowered/Schematic-Specification/blob/master/versions/schematic-1.md
IBlockState baseState = schematic.pallette.get(blockInt); //this is the default blockstate except for ancient fabric
IBlockState baseState = schematic.palette.get(blockInt); //this is the default blockstate except for ancient fabric
if (baseState == baseState.getBlock().getDefaultState() || baseState.getBlock().equals(ModBlocks.FABRIC) || baseState.getBlock().equals(ModBlocks.ANCIENT_FABRIC)) { //should only be false if {@code baseState} is ancient fabric
IBlockState blockState;
if (baseState.getBlock().equals(ModBlocks.FABRIC) || baseState.getBlock().equals(ModBlocks.ANCIENT_FABRIC)) {
@ -137,12 +137,12 @@ public final class SchematicConverter {
} else {
blockState = baseState.getBlock().getStateFromMeta(metadata);
}
if (schematic.pallette.contains(blockState)) { //check whether or not this blockstate is already in the list
blockInt = schematic.pallette.indexOf(blockState);
if (schematic.palette.contains(blockState)) { //check whether or not this blockstate is already in the list
blockInt = schematic.palette.indexOf(blockState);
} else {
schematic.pallette.add(blockState);
schematic.palette.add(blockState);
//DimDoors.log.info("New blockstate detected. Original blockInt = " + blockInt + " and blockState is " + blockState);
blockInt = schematic.pallette.size() - 1;
blockInt = schematic.palette.size() - 1;
}
if (blockState.getBlock().equals(Blocks.CHEST)) chests++;
@ -167,7 +167,7 @@ public final class SchematicConverter {
.weightMaximum(100)
.newRiftWeight(1).build());
} else if (blockState.getBlock().equals(ModBlocks.WARP_DIMENSIONAL_DOOR)) {
IBlockState stateBelow = schematic.pallette.get(schematic.blockData[x][y - 1][z]);
IBlockState stateBelow = schematic.palette.get(schematic.blockData[x][y - 1][z]);
if (stateBelow.getBlock().equals(Blocks.SANDSTONE)) {
sandstoneDoors++;
rift.setProperties(null); // TODO: this should be removed once the linking equations are made symmetric
@ -219,14 +219,14 @@ public final class SchematicConverter {
if (blockState.getBlock().equals(Blocks.END_PORTAL_FRAME)) {
monoliths++;
// I think it's safe to assume that air is present
blockInt = schematic.pallette.indexOf(Blocks.AIR.getDefaultState());
blockInt = schematic.palette.indexOf(Blocks.AIR.getDefaultState());
EntityMonolith monolith = new EntityMonolith(null);
EnumFacing facing = blockState.getValue(BlockEndPortalFrame.FACING);
monolith.setLocationAndAngles(x + 0.5d, y, z + 0.5d, facing.getHorizontalAngle(), 0);
schematic.entities.add(monolith.serializeNBT());
}
} else { // if this is ancient fabric
blockInt = schematic.pallette.indexOf(baseState);
blockInt = schematic.palette.indexOf(baseState);
}
assert blockInt >= 0;
schematic.blockData[x][y][z] = blockInt;
@ -235,7 +235,7 @@ public final class SchematicConverter {
}
if (!nbt.getTag("Entities").hasNoTags())
throw new RuntimeException("Schematic contains entities, but those aren't implemented in the conversion code");
schematic.paletteMax = schematic.pallette.size() - 1;
schematic.paletteMax = schematic.palette.size() - 1;
DimDoors.log.info(schematicId + "," + schematic.name + "," + ironDoors + "," + woodDoors + "," + sandstoneDoors + "," + monoliths + "," + chests);

View file

@ -0,0 +1,69 @@
{
"group": "blank",
"pockets": [
{
"id": "blank_pocket_0",
"size": 0
},
{
"id": "blank_pocket_1",
"size": 1
},
{
"id": "blank_pocket_2",
"size": 2
},
{
"id": "blank_pocket_3",
"size": 3
},
{
"id": "blank_pocket_4",
"size": 4
},
{
"id": "blank_pocket_5",
"size": 5
},
{
"id": "blank_pocket_6",
"size": 6
},
{
"id": "blank_pocket_7",
"size": 7
},
{
"id": "void_pocket_0",
"size": 0
},
{
"id": "void_pocket_1",
"size": 1
},
{
"id": "void_pocket_2",
"size": 2
},
{
"id": "void_pocket_3",
"size": 3
},
{
"id": "void_pocket_4",
"size": 4
},
{
"id": "void_pocket_5",
"size": 5
},
{
"id": "void_pocket_6",
"size": 6
},
{
"id": "void_pocket_7",
"size": 7
}
]
}