Auto-spawn a cabin structure that includes a door and a chest. Fill the chest with loot.
Note: the entire GitHub can be found here. My structure class is there inside the structures package.
My basic layout will be the same as before: make a class that extends WorldGenerator.
I want it to do the following:
- check if there's room for the structure to appear (ie, it won't be inside a hill)
- place every block for the structure
- add items to a chest inside the structure
When I make my new class, I override generate to do all of this.
First, I built my cabin in my test world so I know what it should look like. I decided on a simple box with spruce logs and wood, combined with a door and some furnishings.
Here's some images:
Outside:
Inside:
Next, I got some graph paper and wrote down every layer to get a better idea. I chose to use the front-left corner log as the origin (0,0,0). That block is the lower-left in my graphs.
My graphs ended up like this:
My graph paper:
The origin is the bottom left in this grid. Make sure you pick an origin and stick with it if you want to use the same methods I did.
Another note: I had to move the torch from my original plan because it kept falling off the wall when it updated.
To make things easier to read, I have generate call a few methods that check and do various things.
First of all is the method canReplace(), which returns true if the block is air, snow, liquid, leaves, plants, or anything else I consider "replaceable"
private boolean canReplace(World world, BlockPos pos)
{
Block at = world.getBlockState(pos).getBlock();
Material material = at.getMaterial();
// we think it's replaceable if it's air / liquid / snow, plants, or leaves
return material.isReplaceable() || material == Material.plants || material == Material.leaves;
}
Next, canSpawnHere actually checks the first requirement:
private boolean canSpawnHere(World world, BlockPos posAboveGround)
{
// check all the corners to see which ones are replaceable
boolean corner1Air = canReplace(world, posAboveGround);
boolean corner2Air = canReplace(world, posAboveGround.add(4, 0, 0));
boolean corner4Air = canReplace(world, posAboveGround.add(0, 0, 4));
boolean corner3Air = canReplace(world, posAboveGround.add(4, 0, 4));
// if Y > 20 and all corners pass the test, it's okay to spawn the structure
return posAboveGround.getY() > 20 && corner1Air && corner2Air && corner3Air && corner4Air;
}
Why do I add 4? Because my structure's corners are 4 blocks away from the origin. This brings up another good point: always be flexible and adapt your code to the structure, because there are always ways to make things easier. (side note: I shouldn't use magic numbers here. Sorry)
That's all part of the basic setup, and it can be done even before I graph my structure.
Now that I know what it's going to look like, I can make my arrays. For every type of block in the structure (logs and planks mainly), I will make an array in this format:
int[][] blockType = new int[][] { {right1, up1, back1}, {right2, up2, back2} ... etc. };
These numbers represent the location of each block relative to my origin, which was the lower left corner. I will need to add the corner's actual position every time I set a block, so I made a couple methods for that.
First, here's a wall of numbers coming at you. I looked at my graph paper and wrote down the relative coordinates of every log, every plank, and so on.
The finished arrays:
/** BLUEPRINTS **/
// Format: int[][] { {distanceRight, distanceUp, distanceBack} }
private final int[][] logsPos = new int[][]
{
// corners (first layer)
{0,0,0},{0,1,0}, // front-left
{4,0,0},{4,1,0}, // front-right
{4,0,4},{4,1,4}, // back-right
{0,0,4},{0,1,4}, // back-left
// upper trim (first part of roof)
{0,2,0},{1,2,0},{2,2,0},{3,2,0},{4,2,0}, // front
{0,2,4},{1,2,4},{2,2,4},{3,2,4},{4,2,4}, // back
{0,2,1},{0,2,2},{0,2,3}, // left
{4,2,1},{4,2,2},{4,2,3}, // right
// roof
{1,3,1},{2,3,1},{3,3,1},
{1,3,2},{2,3,2},{3,3,2},
{1,3,3},{2,3,3},{3,3,3}
};
private final int[][] planksPos = new int[][]
{
// floor
{0,-1,0},{1,-1,0},{2,-1,0},{3,-1,0},{4,-1,0},
{0,-1,1},{1,-1,1},{2,-1,1},{3,-1,1},{4,-1,1},
{0,-1,2},{1,-1,2},{2,-1,2},{3,-1,2},{4,-1,2},
{0,-1,3},{1,-1,3},{2,-1,3},{3,-1,3},{4,-1,3},
{0,-1,4},{1,-1,4},{2,-1,4},{3,-1,4},{4,-1,4},
// walls
//// front
{1,0,0},{2,0,0},{3,0,0}, // I want a door here, but I place
{1,1,0},{2,1,0},{3,1,0}, // planks anyway for consistency
//// right
{4,0,1},{4,0,2},{4,0,3},
{4,1,1},{4,1,2},{4,1,3},
//// back
{1,0,4},{2,0,4},{3,0,4},
{1,1,4},{2,1,4},{3,1,4},
//// left
{0,0,1},{0,0,2},{0,0,3},
{0,1,1},{0,1,2},{0,1,3}
};
private final int[] doorBottomPos = new int[] {2,0,0};
private final int[] doorTopPos = new int[] {2,1,0};
private final int[] chestPos = new int[] {1,0,3};
private final int[] workbenchPos = new int[] {2,0,3};
private final int[] furnacePos = new int[] {3,0,3};
private final int[] torchPos = new int[] {2,0,2}; // had to move from original location
Here's the methods I used to automatically add from the origin to my array's locations.
The first, buildLayer, iterates through the int[][] and calls placeBlock for every int[] inside.
One overload of placeBlock simply takes an array ( int[], not int[][] ) of offsets and splits them up into the x,y,z components for the next one. Why don't I use BlockPos for this or the arrays? Because it's too tedious to make a BlockPos[][] where I have to call new BlockPos(x,y,z) over and over and over.
The actual worker method adds the offsets to where I know the corner is, then sets the block. I could have an 'if block is air' check here if I want.
// use an int[][] to place a lot of one block at once
private void buildLayer(World world, BlockPos frontLeftCorner, int[][] blockPositions, IBlockState toPlace)
{
// iterate through the entire int[][]
for(int[] coord : blockPositions)
{
placeBlock(world, frontLeftCorner, coord[0], coord[1], coord[2], toPlace);
}
}
/** Helper Method **/
private void placeBlock(World world, BlockPos frontLeftCorner, int[] offsets, IBlockState toPlace)
{
placeBlock(world, frontLeftCorner, offsets[0], offsets[1], offsets[2], toPlace);
}
/** Places a block using corner position and offsets **/
private void placeBlock(World world, BlockPos frontLeftCorner, int offsetX, int offsetY, int offsetZ, IBlockState toPlace)
{
// figure out where that block is relative to the corner
BlockPos placePos = frontLeftCorner.add(offsetX, offsetY, offsetZ);
world.setBlockState(placePos, toPlace, 2);
}
Right, so all my methods are set up. Now what?
Now I use them. This was actually the easiest part. I put calls to my methods inside generate -- very clean, very simple, as you can see:
@Override
public boolean generate(World worldIn, Random rand, BlockPos corner)
{
if(canSpawnHere(worldIn, corner))
{
// figure out each IBlockState we will use
IBlockState spruceLogs = Blocks.log.getStateFromMeta(BlockPlanks.EnumType.SPRUCE.getMetadata());
IBlockState sprucePlank = Blocks.planks.getStateFromMeta(BlockPlanks.EnumType.SPRUCE.getMetadata());
IBlockState doorLower = Blocks.oak_door.getDefaultState().withProperty(BlockDoor.HALF, BlockDoor.EnumDoorHalf.LOWER);
IBlockState doorUpper = Blocks.oak_door.getDefaultState().withProperty(BlockDoor.HALF, BlockDoor.EnumDoorHalf.UPPER);
IBlockState craftingTable = Blocks.crafting_table.getDefaultState();
IBlockState furnace = Blocks.furnace.getDefaultState();
IBlockState chest = Blocks.chest.getDefaultState();
IBlockState torch = Blocks.torch.getDefaultState();
// build the layers using the arrays
buildLayer(worldIn, corner, logsPos, spruceLogs);
buildLayer(worldIn, corner, planksPos, sprucePlank);
// place the other features LAST
placeBlock(worldIn, corner, doorBottomPos, doorLower);
placeBlock(worldIn, corner, doorTopPos, doorUpper);
placeBlock(worldIn, corner, workbenchPos, craftingTable);
placeBlock(worldIn, corner, furnacePos, furnace);
placeBlock(worldIn, corner, torchPos, torch);
// I saved the chest for last
// here's where we use the WeightedRandomChestContent[]
placeBlock(worldIn, corner, chestPos, chest);
// here we have to do some back-math...
// we need to know the actual location of the TileEntityChest,
// not just its offsets from the corner
BlockPos actualPos = corner.add(chestPos[0], chestPos[1], chestPos[2]);
// right, so we get the TileEntityChest here
TileEntityChest chestTE = (TileEntityChest)worldIn.getTileEntity(actualPos);
if(chestTE != null)
{
// now we add the contents we declared earlier (if the TE is not null)
WeightedRandomChestContent.generateChestContents(worldIn.rand, Lists.newArrayList(chestContents), chestTE, 6);
}
// debug:
System.out.println("Built a cabin starting at " + corner + "!");
return true;
} else System.out.println("Sorry, can't spawn a cabin at " + corner);
return false;
}
// use an int[][] to place a lot of one block at once
private void buildLayer(World world, BlockPos frontLeftCorner, int[][] blockPositions, IBlockState toPlace)
{
// iterate through the entire int[][]
for(int[] coord : blockPositions)
{
placeBlock(world, frontLeftCorner, coord[0], coord[1], coord[2], toPlace);
}
}
Notice the order in which I do things. First, I get an instance of IBlockState for every block I will use. I am careful with the door blocks because I want the top half on top, and the bottom half on bottom.
Next I place all the logs and planks. AFTER this, I place features like the torch, door, or crafting table.
Finally, I place the chest and give it its items. I do this by placing the chest, then getting the TileEntityChest and making sure it is not null. But wait! What does Lists.newArray(chestContents) mean? I didn't declare chestContents anywhere!
Oh wait. I did. Here's the WeightedRandomChestContent array that will help us place items randomly in the chest.
WeightedRandomChestContent seems confusing at first, but it's simple.
For every possible item you want in your chest, you make an entry that includes the item, item damage, minimum amount, maximum amount, and weight.
Be careful with the weight -- it will add up all the weights and treat them like proportions in its math, so items that have a heavier weight than the rest have more chance to appear. The actual number doesn't matter as much as the difference between the rarest and most common entries' weights.
Here's the array I used. I kept it small -- my chests can only have iron axes, bowls, fish, and gold nuggets.
/** CHEST CONTENTS **/
private final WeightedRandomChestContent[] chestContents =
{
// parameters: (Item, metadata, min, max, weight)
new WeightedRandomChestContent(Items.bowl, 0, 1, 4, 20),
new WeightedRandomChestContent(Items.fish, 0, 2, 8, 25),
new WeightedRandomChestContent(Items.iron_axe, 0, 1, 1, 30),
new WeightedRandomChestContent(Items.gold_nugget, 0, 3, 18, 15)
};
Lastly, since my structure code is complete, I will add it to my main chunk generation. I opened TutorialWorldGeneration from last time and placed a few method calls inside generateOverworld. There's a few things to notice here:
- I took the getGroundFromAbove function from last time and put it inside TutorialWorldGeneration. Now I can re-use it here.
- I don't want a cabin in every single chunk! So, I do a random percentage check. 1 in 4 chunks having a cabin should work perfectly fine (it won't always be there, though, because of the canSpawnHere method in the structure generation)
- To make sure I generate in the air above the ground, I add 1 to groundY. Always pay attention to this when you generate on the surface.
/** CABIN GEN **/
WorldGenerator genCabin = new StructureCabin();
// 25% of chunks can have a cabin
final int CABIN_CHANCE = 25;
if(rand.nextInt(100) < CABIN_CHANCE)
{
// get a random position in the chunk
int randX = blockX + rand.nextInt(16);
int randZ = blockZ + rand.nextInt(16);
// use our custom function to get the ground height
int groundY = getGroundFromAbove(world, randX, randZ);
genCabin.generate(world, rand, new BlockPos(randX, groundY + 1, randZ));
}
/** END CABIN GEN **/
That's about it. There are many ways to spawn structures without an array, but it's often much harder to trace down where your setBlockState calls went wrong.
On that note, don't be discouraged by things turning out badly. I loaded a new world 7 times before my debugging was complete -- one time a wall was too far to the side, another time there was a door instead of a furnace. I had a lot of problems with a null pointer exception if I used the TileEntityChest before doing a null-check.
Outside, found next to a village: (notice my nice cookie bushes nearby )
The inside: I had to move the torch from my original plan because it kept falling off the wall when it updated. Now it spawns on the floor -- good enough.
Alright, everything worked! *
* finally... After 7 tries...
A main advantage of my array system is that I can use it to rotate my structures. That's what I do in my mod Nomadic Tents -- Using the array of offsets, I modify placeBlock a little to include rotation.
All I have to do is tell my code to add positive offset for this rotation, negative offset for that rotation, and my structure can then face north, east, south, or west randomly. Much more natural and useful.
One more note: if I rotated anything, I would also have to rotate the door (and furnace). I would do this the same way I set the HALF value of the IBlockState, but by setting the FACING value instead.
Any questions or ideas? Comment below.
If this post answered any questions you already had, press the arrow down there to help other people find it
Rollback Post to RevisionRollBack
Click this banner for a list of illegal mod distributors -- only download from legal sites!
To be honest, I have never done exactly the same thing from project to project.
For instance, in a few of my structure generators, I have no Y-value in the int[][] .
My first use of this array technique was strictly by layers. I would lay out arrays called logsLayer1, planksLayer1, etc. and the same for layer 2, layer 3, and so on. This was useful when the layers repeated but with a different Y-value, and I still use it in Nomadic Tents. (Partly because it's too hard-coded now and would take too long to change)
Also in this example, I did not make an array for air. There can be grass or leaves inside my cabin unless I add something that specifically sets the inner area to air blocks.
Rollback Post to RevisionRollBack
Click this banner for a list of illegal mod distributors -- only download from legal sites!
The setup I have started is using is based on a hashmap.
Takes a string and a component, the string is formatted as "relativeX,relativeY,relativeZ" then I just store a max value for x,y,z and loop through the space. If there is no component then nothing changes otherwise it calls a place method of the component with the actual x,y,z and facing. By setting it up this way I can instantiate new components with a given block type and they will do the default placement or I can use an extended version of component that does something special (like your chest filled with items). For blocks that are the same just in different positions I create a local variable of that component and then use that component instance for each entry in the hashmap.
Setting it up this way makes it easy to do block persistance (making the blocks of the structure unbreakable) because I can just check a location against a structures hashmap and see if it returns anything.
Rollback Post to RevisionRollBack
"If It Is To Be It Is Up To Me" simple quote and I don't know where it comes from but I enjoy it.
Current Mod: Armerger | Light Drafter
Thanks for the tutorial. It's very helpful, since there is not so many tutorials covering this topic.
I just want to ask... what if you need a bigger structure? setting all its blocks positions on those arrays would be a nightmare
I really like your tutorial and I'm using it. Thanks dude:)
Well, there's always shortcuts you can use -- making methods that build similar parts of the structure, such as a wall, using for-loops. A lot of this depends on exactly what you are doing, so improvise when you can
Rollback Post to RevisionRollBack
Click this banner for a list of illegal mod distributors -- only download from legal sites!
WeightedRandomChestContent was replaced by Loot Tables. I looked at them and honestly have no idea where to start... It looks like you can call LootTable#fillInventory with the TileEntityChest and a LootContext, but you'll have to look somewhere else for information about getting the LootTable.
getStateFromMeta should no longer be used, like you said. Instead, always use getDefaultState().withProperty(IProperty, value) like my code does for the door. Here is a corrected version of my posted code for IBlockStates I used:
// for Acacia or Dark Oak, use Blocks.log2 and BlockNewLog.VARIANT
IBlockState spruceLogs = Blocks.log.getDefaultState().withProperty(BlockOldLog.VARIANT, BlockPlanks.EnumType.SPRUCE);
IBlockState sprucePlank = Blocks.planks.getDefaultState().withProperty(BlockPlanks.VARIANT, BlockPlanks.EnumType.SPRUCE);
Thanks! Figured that out whilst waiting for a reply
Do you know the difference between this old/new log crap? Haha
Uggh... well, it all started when the Mojang team wanted to add 2 new types of logs but didn't want to change the max metadata of the existing one. Then, their goal was to not break existing maps by removing the log they added. As time went on, they hardcoded the 2 logs more and more -- with IProperty and IBlockState, it has now become practically impossible to merge them.
It would seem easier to just get rid of the "new" one already, possibly adding a script that checks for that missing block id and replaces it with the proper metadata of the correct log. Wishful thinking
Rollback Post to RevisionRollBack
Click this banner for a list of illegal mod distributors -- only download from legal sites!
This is a great tutorial! Could you possibly show me how to add an entity (such as custom mob) to the cabin you generate?
Edit : solved by adding method #setCurrentItemOrArmor ...no earthly idea why!!
@Override
public boolean generate(World worldIn, Random rand, BlockPos corner)
{
boolean b = worldIn.getBiomeGenForCoords(corner).biomeName.equals("Glistering Biome");
if(b == true){
if (canSpawnHere(worldIn, corner))
{
int x = corner.getX();
int z = corner.getZ();
int y = corner.getY();
if(!(worldIn.isRemote))
{
EntityBlackTobo badtobie = new EntityBlackTobo(worldIn);
badtobie.setPosition(x + 2, y, z + 2);
badtobie.setCurrentItemOrArmor(ItemRegistry.ray_gun);
// entity.setLocationAndAngles(i, j, k, yaw, pitch)
badtobie.setLocationAndAngles(x + 2, y, z + 1, 0.1F , 0.1F);
worldIn.spawnEntityInWorld(badtobie);
badtobie.onUpdate();
}}}} return b;
}
This adds the badtobie mob in the cabin but it causes a major lag in the PVP and the game that effects all entities and also causes all mobs to float !! what the heck
So, do you recommend using this method for large structures? I heard something about chunkProviders but I'm not sure what they are, when should they be used in generation?
Complex Structure Generation for 1.8.9
Ok, so my previous Structure Generation Tutorial was helpful for setting up the basics, but what if I want to make a bigger structure, say, a cabin?
Objective:
Auto-spawn a cabin structure that includes a door and a chest. Fill the chest with loot.
Note: the entire GitHub can be found here. My structure class is there inside the structures package.
My basic layout will be the same as before: make a class that extends WorldGenerator.
I want it to do the following:
- check if there's room for the structure to appear (ie, it won't be inside a hill)
- place every block for the structure
- add items to a chest inside the structure
When I make my new class, I override generate to do all of this.
First, I built my cabin in my test world so I know what it should look like. I decided on a simple box with spruce logs and wood, combined with a door and some furnishings.
Here's some images:
Outside:
Inside:
Next, I got some graph paper and wrote down every layer to get a better idea. I chose to use the front-left corner log as the origin (0,0,0). That block is the lower-left in my graphs.
My graphs ended up like this:
My graph paper:
The origin is the bottom left in this grid. Make sure you pick an origin and stick with it if you want to use the same methods I did.
Another note: I had to move the torch from my original plan because it kept falling off the wall when it updated.
To make things easier to read, I have generate call a few methods that check and do various things.
First of all is the method canReplace(), which returns true if the block is air, snow, liquid, leaves, plants, or anything else I consider "replaceable"
Next, canSpawnHere actually checks the first requirement:
Why do I add 4? Because my structure's corners are 4 blocks away from the origin. This brings up another good point: always be flexible and adapt your code to the structure, because there are always ways to make things easier. (side note: I shouldn't use magic numbers here. Sorry)
That's all part of the basic setup, and it can be done even before I graph my structure.
Now that I know what it's going to look like, I can make my arrays. For every type of block in the structure (logs and planks mainly), I will make an array in this format:
These numbers represent the location of each block relative to my origin, which was the lower left corner. I will need to add the corner's actual position every time I set a block, so I made a couple methods for that.
First, here's a wall of numbers coming at you. I looked at my graph paper and wrote down the relative coordinates of every log, every plank, and so on.
The finished arrays:
Here's the methods I used to automatically add from the origin to my array's locations.
The first, buildLayer, iterates through the int[][] and calls placeBlock for every int[] inside.
One overload of placeBlock simply takes an array ( int[], not int[][] ) of offsets and splits them up into the x,y,z components for the next one. Why don't I use BlockPos for this or the arrays? Because it's too tedious to make a BlockPos[][] where I have to call new BlockPos(x,y,z) over and over and over.
The actual worker method adds the offsets to where I know the corner is, then sets the block. I could have an 'if block is air' check here if I want.
Right, so all my methods are set up. Now what?
Now I use them. This was actually the easiest part. I put calls to my methods inside generate -- very clean, very simple, as you can see:
Notice the order in which I do things. First, I get an instance of IBlockState for every block I will use. I am careful with the door blocks because I want the top half on top, and the bottom half on bottom.
Next I place all the logs and planks. AFTER this, I place features like the torch, door, or crafting table.
Finally, I place the chest and give it its items. I do this by placing the chest, then getting the TileEntityChest and making sure it is not null. But wait! What does Lists.newArray(chestContents) mean? I didn't declare chestContents anywhere!
Oh wait. I did. Here's the WeightedRandomChestContent array that will help us place items randomly in the chest.
WeightedRandomChestContent seems confusing at first, but it's simple.
For every possible item you want in your chest, you make an entry that includes the item, item damage, minimum amount, maximum amount, and weight.
Be careful with the weight -- it will add up all the weights and treat them like proportions in its math, so items that have a heavier weight than the rest have more chance to appear. The actual number doesn't matter as much as the difference between the rarest and most common entries' weights.
Here's the array I used. I kept it small -- my chests can only have iron axes, bowls, fish, and gold nuggets.
Lastly, since my structure code is complete, I will add it to my main chunk generation. I opened TutorialWorldGeneration from last time and placed a few method calls inside generateOverworld. There's a few things to notice here:
- I took the getGroundFromAbove function from last time and put it inside TutorialWorldGeneration. Now I can re-use it here.
- I don't want a cabin in every single chunk! So, I do a random percentage check. 1 in 4 chunks having a cabin should work perfectly fine (it won't always be there, though, because of the canSpawnHere method in the structure generation)
- To make sure I generate in the air above the ground, I add 1 to groundY. Always pay attention to this when you generate on the surface.
That's about it. There are many ways to spawn structures without an array, but it's often much harder to trace down where your setBlockState calls went wrong.
On that note, don't be discouraged by things turning out badly. I loaded a new world 7 times before my debugging was complete -- one time a wall was too far to the side, another time there was a door instead of a furnace. I had a lot of problems with a null pointer exception if I used the TileEntityChest before doing a null-check.
The entire class is on GitHub here.
Let's see how it turned out:
Outside, found next to a village: (notice my nice cookie bushes nearby )
The inside: I had to move the torch from my original plan because it kept falling off the wall when it updated. Now it spawns on the floor -- good enough.
Alright, everything worked! *
* finally... After 7 tries...
A main advantage of my array system is that I can use it to rotate my structures. That's what I do in my mod Nomadic Tents -- Using the array of offsets, I modify placeBlock a little to include rotation.
All I have to do is tell my code to add positive offset for this rotation, negative offset for that rotation, and my structure can then face north, east, south, or west randomly. Much more natural and useful.
One more note: if I rotated anything, I would also have to rotate the door (and furnace). I would do this the same way I set the HALF value of the IBlockState, but by setting the FACING value instead.
Any questions or ideas? Comment below.
If this post answered any questions you already had, press the arrow down there to help other people find it
To be honest, I have never done exactly the same thing from project to project.
For instance, in a few of my structure generators, I have no Y-value in the int[][] .
My first use of this array technique was strictly by layers. I would lay out arrays called logsLayer1, planksLayer1, etc. and the same for layer 2, layer 3, and so on. This was useful when the layers repeated but with a different Y-value, and I still use it in Nomadic Tents. (Partly because it's too hard-coded now and would take too long to change)
Also in this example, I did not make an array for air. There can be grass or leaves inside my cabin unless I add something that specifically sets the inner area to air blocks.
The setup I have started is using is based on a hashmap.
Takes a string and a component, the string is formatted as "relativeX,relativeY,relativeZ" then I just store a max value for x,y,z and loop through the space. If there is no component then nothing changes otherwise it calls a place method of the component with the actual x,y,z and facing. By setting it up this way I can instantiate new components with a given block type and they will do the default placement or I can use an extended version of component that does something special (like your chest filled with items). For blocks that are the same just in different positions I create a local variable of that component and then use that component instance for each entry in the hashmap.
Setting it up this way makes it easy to do block persistance (making the blocks of the structure unbreakable) because I can just check a location against a structures hashmap and see if it returns anything.
Current Mod: Armerger | Light Drafter
Should I be worried that the door on your hut is facing the wrong way? ;P
Praise be Ro-naza, greatest among the thaumaturges!
Huh, I didn't even notice
Thanks for the tutorial. It's very helpful, since there is not so many tutorials covering this topic.
I just want to ask... what if you need a bigger structure? setting all its blocks positions on those arrays would be a nightmare
I really like your tutorial and I'm using it. Thanks dude:)
Well, there's always shortcuts you can use -- making methods that build similar parts of the structure, such as a wall, using for-loops. A lot of this depends on exactly what you are doing, so improvise when you can
WeightedRandomChestContent was replaced by Loot Tables. I looked at them and honestly have no idea where to start... It looks like you can call LootTable#fillInventory with the TileEntityChest and a LootContext, but you'll have to look somewhere else for information about getting the LootTable.
getStateFromMeta should no longer be used, like you said. Instead, always use getDefaultState().withProperty(IProperty, value) like my code does for the door. Here is a corrected version of my posted code for IBlockStates I used:
Oh, getMaterial() should be called from the IBlockState, eg, Blocks.log.getDefaultState.getMaterial() or World#getBlockState(pos).getMaterial()
Uggh... well, it all started when the Mojang team wanted to add 2 new types of logs but didn't want to change the max metadata of the existing one. Then, their goal was to not break existing maps by removing the log they added. As time went on, they hardcoded the 2 logs more and more -- with IProperty and IBlockState, it has now become practically impossible to merge them.
It would seem easier to just get rid of the "new" one already, possibly adding a script that checks for that missing block id and replaces it with the proper metadata of the correct log. Wishful thinking
This is a great tutorial! Could you possibly show me how to add an entity (such as custom mob) to the cabin you generate?
Edit : solved by adding method #setCurrentItemOrArmor ...no earthly idea why!!
@Override
public boolean generate(World worldIn, Random rand, BlockPos corner)
{
boolean b = worldIn.getBiomeGenForCoords(corner).biomeName.equals("Glistering Biome");
if(b == true){
if (canSpawnHere(worldIn, corner))
{
int x = corner.getX();
int z = corner.getZ();
int y = corner.getY();
if(!(worldIn.isRemote))
{
EntityBlackTobo badtobie = new EntityBlackTobo(worldIn);
badtobie.setPosition(x + 2, y, z + 2);
badtobie.setCurrentItemOrArmor(ItemRegistry.ray_gun);
// entity.setLocationAndAngles(i, j, k, yaw, pitch)
badtobie.setLocationAndAngles(x + 2, y, z + 1, 0.1F , 0.1F);
worldIn.spawnEntityInWorld(badtobie);
badtobie.onUpdate();
}}}} return b;
}
This adds the badtobie mob in the cabin but it causes a major lag in the PVP and the game that effects all entities and also causes all mobs to float !! what the heck
any idea why?
Edit: Solved that issue
// this.boundingBox.setBounds(minX,minY,minZ,maxX,maxY,maxZ);
this.getEntityBoundingBox().fromBounds(0D, 0D, 0D, .5D, .5D, .5D);
@Override
public AxisAlignedBB getCollisionBox(Entity par1Entity) {
return boundingBox;
}
@Override
public AxisAlignedBB getBoundingBox() {
return boundingBox;
}
Hey! Amazing tutorial, it helped a lot.
So, do you recommend using this method for large structures? I heard something about chunkProviders but I'm not sure what they are, when should they be used in generation?