Jump to content

Multiblock Structures help


marcotesoalli

Recommended Posts

Hello,

 

Could someone tell me the method to make multiblock structures (e.g. railcraft coke oven or liquid tanks).

Just want to know the basic idea behind these. And how I could make the whole thing render as one block and make it clickable as one block?

 

Would be nice if someone could link me to a source code example of any mod implementing this.

 

greets

Link to comment
Share on other sites

  • 2 months later...

Just throwing a wild guess here but perhaps it's just a block which onUpdate() checks to see if it's a part of a structure(by checking the blocks around it). and if it is, then create a new TileEntityXXX and let all the parts of the struct setTileEntity to that.

 

OnBlockActiviated -> if this.TileEntity != null -> open GUI etc.

 

The railcraft mod warns you that you should NOT use the block as a part of regular construction due to the fact that it will slow down the server with all it's constant checks to see if is a whole coke oven.

 

I have no idea how it is actually done inn RC, but I guess it's something along those lines :)

If you guys dont get it.. then well ya.. try harder...

Link to comment
Share on other sites

a simple way to do it is in the block class

 

public int onBlockPlaced(World par1World, int par2, int par3, int par4, int par5, float par6, float par7, float par8, int par9)
{
    if(par1World.getBlockId(par2, par3, par4) == YourMod.YourBlock.blockID && par1World.getBlockId(par2, par3, par4 + 1) == YourMod.YourBlock.blockID)
    {
    	par1World.setBlock(par2, par3, par4, YourMod.YourMultiBlockPart.blockID);
    	par1World.setBlock(par2, par3, par4 + 1, YourMod.YourMultiBlockPart.blockID);
    	
    }
}

 

then

 

in your other block class put the open gui things in there or you could use the same one by doing this

 

first declare an integer that equals 0 then

public int onBlockPlaced(World par1World, int par2, int par3, int par4, int par5, float par6, float par7, float par8, int par9)
{
    if(par1World.getBlockId(par2, par3, par4) == YourMod.YourBlock.blockID && par1World.getBlockId(par2, par3, par4 + 1) == YourMod.YourBlock.blockID)
    {

      multiblock = 1;
    	
    }

    else
    {
       multiblock = 0;
    }
}

 

then in your on block activated class add this

 

@Override
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int i, float f, float g, float t){
TileEntity tile_entity = world.getBlockTileEntity(x, y, z);
if(tile_entity == null || player.isSneaking() || this.multiblock == 0){
return false;
}

if(this.multiblock == 1)
{
player.openGui(yourMod.instance, 0, world, x, y, z);

return true;
}
}

Link to comment
Share on other sites

I can tell you how I'm doing this in my upcoming stargates mod. The basic idea is that the onBlockAdded method of the component block searches the area around it looking for a particular pattern of blocks. When that pattern is found, the component blocks are flagged as having been merged into a structure. Also, one of the component blocks is picked to be the "master" block having a TileEntity holding the state of the structure as a whole. When any of the component blocks is right-clicked, the click is forwarded to the master block, which opens the GUI.

 

Unmerging is handled by the breakBlock method of the component blocks, which clears the merge flag on all the other merged blocks of the structure.

 

As for rendering the merged structure, I'm doing that using a TileEntitySpecialRenderer on the master block, and the normal block renderers of the component blocks don't render anything when they're merged. But you could also have the component blocks render themselves in two different ways for the merged and unmerged states, or replace the component blocks with different block types when they merge.

 

From what was said about Railcraft above, it sounds like it's using a somewhat inefficient method to detect merging. It shouldn't be necessary to continually check for merging, only when a potential component block is placed or removed, or possibly when a neighbour of such a block changes state.

Link to comment
Share on other sites

I can tell you how I'm doing this in my upcoming stargates mod. The basic idea is that the onBlockAdded method of the component block searches the area around it looking for a particular pattern of blocks. When that pattern is found, the component blocks are flagged as having been merged into a structure. Also, one of the component blocks is picked to be the "master" block having a TileEntity holding the state of the structure as a whole. When any of the component blocks is right-clicked, the click is forwarded to the master block, which opens the GUI.

 

Unmerging is handled by the breakBlock method of the component blocks, which clears the merge flag on all the other merged blocks of the structure.

 

As for rendering the merged structure, I'm doing that using a TileEntitySpecialRenderer on the master block, and the normal block renderers of the component blocks don't render anything when they're merged. But you could also have the component blocks render themselves in two different ways for the merged and unmerged states, or replace the component blocks with different block types when they merge.

 

From what was said about Railcraft above, it sounds like it's using a somewhat inefficient method to detect merging. It shouldn't be necessary to continually check for merging, only when a potential component block is placed or removed, or possibly when a neighbour of such a block changes state.

 

Could you maybe explain a little more how your mod checks for the pattern? I want to make my mod recognize an entire house and give it a health and it sounds like your approach might be able to help me with that.

Link to comment
Share on other sites

I can tell you how I'm doing this in my upcoming stargates mod. The basic idea is that the onBlockAdded method of the component block searches the area around it looking for a particular pattern of blocks. When that pattern is found, the component blocks are flagged as having been merged into a structure. Also, one of the component blocks is picked to be the "master" block having a TileEntity holding the state of the structure as a whole. When any of the component blocks is right-clicked, the click is forwarded to the master block, which opens the GUI.

 

Unmerging is handled by the breakBlock method of the component blocks, which clears the merge flag on all the other merged blocks of the structure.

 

As for rendering the merged structure, I'm doing that using a TileEntitySpecialRenderer on the master block, and the normal block renderers of the component blocks don't render anything when they're merged. But you could also have the component blocks render themselves in two different ways for the merged and unmerged states, or replace the component blocks with different block types when they merge.

 

From what was said about Railcraft above, it sounds like it's using a somewhat inefficient method to detect merging. It shouldn't be necessary to continually check for merging, only when a potential component block is placed or removed, or possibly when a neighbour of such a block changes state.

 

If you could show us exactly what you did, that would be great.

Link to comment
Share on other sites

Here's some of the code I've been working with

 

	@Override
public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9) {
	if (isValidCombination(par2, par3, par4, par1World)) {
		if (par1World.getBlockTileEntity(par2, par3, par4) != null) {
			System.out.println("This is the master block");
			TileEntityFurnace tile = (TileEntityFurnace) par1World.getBlockTileEntity(par2, par3, par4);
			par5EntityPlayer.displayGUIFurnace(tile);
			return true;
		}
	}

	return false;
}

 

public void onBlockAdded(World world, int x, int y, int z) {
	if (isValidCombination(x, y, z, world)) {
		super.onBlockAdded(world, x, y, z);
		System.out.println("this is a valid combination");
	}
}

 

For some reason, this code creates three seperate TileEntities when the last block is placed. Can anyone see why this is occuring?

Link to comment
Share on other sites

Here's some of the code I've been working with

 

	@Override
public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9) {
	if (isValidCombination(par2, par3, par4, par1World)) {
		if (par1World.getBlockTileEntity(par2, par3, par4) != null) {
			System.out.println("This is the master block");
			TileEntityFurnace tile = (TileEntityFurnace) par1World.getBlockTileEntity(par2, par3, par4);
			par5EntityPlayer.displayGUIFurnace(tile);
			return true;
		}
	}

	return false;
}

 

public void onBlockAdded(World world, int x, int y, int z) {
	if (isValidCombination(x, y, z, world)) {
		super.onBlockAdded(world, x, y, z);
		System.out.println("this is a valid combination");
	}
}

 

For some reason, this code creates three seperate TileEntities when the last block is placed. Can anyone see why this is occuring?

 

how are you detecting if it's valid?

Link to comment
Share on other sites

This is the code that checks for valid combinations

 

private boolean isValidCombination(int x, int y, int z, World world) {
	if (getNextX(x, y, z, world) && getNextZ(x, y, z, world)) {
		return getTopRightDiag(x, y, z, world);
	}

	if (getNextX(x, y, z, world) && getPrevZ(x, y, z, world)) {
		return getTopLeftDiag(x, y, z, world);
	}

	if (getPrevX(x, y, z, world) && getNextZ(x, y, z, world)) {
		return getBottomRightDiag(x, y, z, world);
	}

	if (getPrevX(x, y, z, world) && getPrevZ(x, y, z, world)) {
		return getBottomLeftDiag(x, y, z, world);
	}

	return false;
}

 

private boolean getNextX(int x, int y, int z, World world) {
	return world.getBlockId(x + 1, y, z) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getPrevX(int x, int y, int z, World world) {
	return world.getBlockId(x - 1, y, z) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getNextZ(int x, int y, int z, World world) {
	return world.getBlockId(x, y, z + 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getPrevZ(int x, int y, int z, World world) {
	return world.getBlockId(x, y, z - 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getTopLeftDiag(int x, int y, int z, World world) {
	return world.getBlockId(x + 1, y, z - 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getTopRightDiag(int x, int y, int z, World world) {
	return world.getBlockId(x + 1, y, z + 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getBottomLeftDiag(int x, int y, int z, World world) {
	return world.getBlockId(x - 1, y, z - 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getBottomRightDiag(int x, int y, int z, World world) {
	return world.getBlockId(x + 1, y, z + 1) == modHerbcraft.BlockOvenBrick.blockID;
}

 

It's just a boolean check.

Link to comment
Share on other sites

change

private boolean getBottomRightDiag(int x, int y, int z, World world) {
        	  return world.getBlockId(x + 1, y, z + 1) == CodeLyoko.SuperCalc.blockID;
        	 }

to

private boolean getBottomRightDiag(int x, int y, int z, World world) {
        	  return world.getBlockId(x - 1, y, z + 1) == CodeLyoko.SuperCalc.blockID;
        	 }

but I'm not sure about why it makes 4 different tile entities.

 

EDIT: I figured out why. when you right click each of the blocks to open it, it checks if they're valid and then makes them they're own tile entity.

Link to comment
Share on other sites

Interesting. The TileEntity should only be called once, and that's when the last block is placed.

 

I added a println statement at several points, and it's clear from console feedback that the method calls occur for all blocks when a new one is placed in close proximity to existing ones. The method calls should only happen once. What do I need to change to stop this from happening?

Link to comment
Share on other sites

I previously had a boolean field which would be set to "true" if the block being placed was the master block (ie: it would only be true if it was the last block in the combination). The same problem was still present even with this field present. Additionally, the GUI can only open if a TileEntity exists at the location, so it's creating TileEntities multiple times for some reason.

 

Try this version of the boolean check. It has more console statements and should show the method calls occuring more than once.

 

private boolean isValidCombination(int x, int y, int z, World world) {
	System.out.println("checking for valid combinations");
	if (getNextX(x, y, z, world) && getNextZ(x, y, z, world)) {
		return getTopRightDiag(x, y, z, world);
	}

	if (getNextX(x, y, z, world) && getPrevZ(x, y, z, world)) {
		return getTopLeftDiag(x, y, z, world);
	}

	if (getPrevX(x, y, z, world) && getNextZ(x, y, z, world)) {
		return getBottomRightDiag(x, y, z, world);
	}

	if (getPrevX(x, y, z, world) && getPrevZ(x, y, z, world)) {
		return getBottomLeftDiag(x, y, z, world);
	}

	System.out.println("no combinations found");
	return false;
}

private boolean getNextX(int x, int y, int z, World world) {
	return world.getBlockId(x + 1, y, z) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getPrevX(int x, int y, int z, World world) {
	return world.getBlockId(x - 1, y, z) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getNextZ(int x, int y, int z, World world) {
	return world.getBlockId(x, y, z + 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getPrevZ(int x, int y, int z, World world) {
	return world.getBlockId(x, y, z - 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getTopLeftDiag(int x, int y, int z, World world) {
	return world.getBlockId(x + 1, y, z - 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getTopRightDiag(int x, int y, int z, World world) {
	return world.getBlockId(x + 1, y, z + 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getBottomLeftDiag(int x, int y, int z, World world) {
	return world.getBlockId(x - 1, y, z - 1) == modHerbcraft.BlockOvenBrick.blockID;
}

private boolean getBottomRightDiag(int x, int y, int z, World world) {
	return world.getBlockId(x - 1, y, z - 1) == modHerbcraft.BlockOvenBrick.blockID;
}

Link to comment
Share on other sites

  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.