Place Block

Place Block

Postby ismellike » 25 Nov 2012, 15:44

I'm trying to make a command where you can spawn something, like /tree where the tree spawns up from where you clicked block.

Couls someone just give me a basic tutorial, like say I place a block and it shows up 2 blocks to the right.
What a beast...
User avatar
ismellike
Coder
 
Posts: 731
Joined: 31 Oct 2012, 04:04
Location: Kansas

Re: Place Block

Postby ismellike » 27 Nov 2012, 05:37

It would also be nice to know how to use cuboid in a command.
What a beast...
User avatar
ismellike
Coder
 
Posts: 731
Joined: 31 Oct 2012, 04:04
Location: Kansas

Re: Place Block

Postby Conor » 11 Jan 2013, 20:10

Hey meep :)

I thought I'd go through the basics with you to help you out. I remember learning this for the first time too and its confusing at first but then you realise its all very beautiful.

But first is first, if you want to use a command that already exists, such as /tree, you should learn this useful way of doing so. This is how you do it on MCLawl/old MCForge, and I'm guessing it is somewhat the same for MCDzienny... but it may have changed:

Code: Select all
Command.all.Find("commandName").Use(Player, "param for the command, being used by Player");


The typical method to place a block in the players level is as follows.

Code: Select all

// Places a block in p.level
// at co-ords x, y and z
// With the byte for the yellow wool block

ushort x = 10;
ushort y = 50;
ushort z = 100;
p.level.Blockchange(p, x, y, z, Block.yellow);


x, y and z are a ushort variable. They are the values of the co-ordinates themselves. But, if you want to place a block where a player is standing for example, then you will need to divide their position by 32. This is a common mistake people make. For example;

Code: Select all
// define the variables for their position, divide by 32 to get the level co-ordinate
ushort x = (ushort)(p.pos[0] / 32);
ushort y = (ushort)(p.pos[1] / 32);
ushort z = (ushort)(p.pos[2] / 32);

//Do the blockchange at their current position
p.level.Blockchange(p, x, y, z, Block.Red);


So, if you want to make a command that places a block in a straight line, forming a pillar from their position, which is 3 blocks long, you could do this:

Code: Select all
// define the variables for their position, divide by 32 to get the level co-ordinate
ushort x = (ushort)(p.pos[0] / 32);
ushort y = (ushort)(p.pos[1] / 32);
ushort z = (ushort)(p.pos[2] / 32);

//Do the blockchange at their current position and 2 blocks above.
p.level.Blockchange(p, x, y, z, Block.White);
p.level.Blockchange(p, x, (ushort)(y + 1), z, Block.White);
p.level.Blockchange(p, x, (ushort)(y + 2), z, Block.White);


Although, it is common that you will use the for loop when modifying blocks within your commands. So, lets improve our pillar, and make it pillar up to the sky. We are also going to check if it hits a block before it reaches the sky, in this case, we would stop the pillar. It would look something like this;

Code: Select all
// define the variables for their position, divide by 32 to get the level co-ordinate
ushort x = (ushort)(p.pos[0] / 32);
ushort y = (ushort)(p.pos[1] / 32);
ushort z = (ushort)(p.pos[2] / 32);

// Start a loop which will pillar upwards
// The loop makes the y value increase by one every cycle, it goes until the maps height is reached.
// But we want to check for any solid blocks that could get in the way, which is the if statement you see in the loop.

for (ushort s = y; s < (ushort)(p.level.depth); ++s) // May be Level.height in MCDzienny.
{
   if (p.level.GetTile(x, s, z) != Block.air))
   {
       // if the block isn't air, stop pillaring up - break from the loop.
       break;
   }
      // else, place the next block within the pillar
   p.level.Blockchange(p, x, s, z);
}


So most of the time, you want the player to place a block before you do something. So you need to trigger the event known as 'Player.Blockchange'. This event occurs when a player places blocks, or deletes them. So, you want to make your own little method in your command, which is what will happen when this is triggered. You can do that like this:

Code: Select all
public override void Use(Player p, string message)
{
   Player.SendMessage(p, "Place a block to start your command in that position.");
   p.Blockchange += new Player.BlockchangeEventHandler(Blockchange1);
}

public void Blockchange1(Player p, ushort x, ushort y, ushort z, byte type)
{
    Player.SendMessage(p, "You placed a block!");
}


Now, it is important to do a few important things within this method when a player places a block, before you get into the real workings of your code. They are explained below.

Code: Select all
public void Blockchange1(Player p, ushort x, ushort y, ushort z, byte type)
{
   // This removes the link between this method and the block event. So it won't keep happening when they place blocks after.
   p.ClearBlockchange();
 
   // Basically, this gets the block that was in the position that they destroyed (or if air, what they placed).
   // Then we send them the block back again, so it looks like nothing has happened.
   byte b = p.level.GetTile(x, y, z);
   p.SendBlockchange(x, y, z, b);
}


Also what is interesting about the above, you may have noticed the 'byte type' variable within the mechanism. And yes, as you probably imagined, this is the byte of the block they placed. So we could use it to place blocks of the block they were handling:

Code: Select all
p.level.Blockchange(p, x, y, z, type);


As for your request, the solid cuboid command from the top of my head would look something like:

Code: Select all
public override void Use(Player p, string message)
{
   Player.SendMessage(p, "Place a block to determine the first edge of your cuboid.");
   p.Blockchange += new Player.BlockchangeEventHandler(Blockchange1);
}

public ushort ax, ushort ay, ushort az, ushort bx, ushort by, ushort bz;
public byte blockType;

public void Blockchange1(Player p, ushort x, ushort y, ushort z, byte type)
{
    ax = x; ay = y; az = z;

    p.ClearBlockchange();
    byte b = p.level.GetTile(x, y, z);
    p.SendBlockchange(x, y, z, b);

    Player.SendMessage(p, "Now place the second edge for your cuboid.");
    p.Blockchange += new Player.BlockchangeEventHandler(Blockchange2);
}

public void Blockchange2(Player p, ushort x, ushort y, ushort z, byte type)
{
    bx = x; by = y; bz = z; blockType = type;

    p.ClearBlockchange();
    byte b = p.level.GetTile(x, y, z);
    p.SendBlockchange(x, y, z, b);

    doCuboid();
}

public void doCuboid()
{
    // So we have the two positions to start and finish from, the co-ordinates are variables:
    // ax, ay, az
    // bx, by, bz
    // And the block they're cuboiding in is saved in vairable 'blockType'.

    // So now we can just use this information in for loops, to cuboid. I'll put it in a thread to prevent major lag.
    // The thread makes it run as a separate process from the command... something like that.
    // I don't think it makes much difference, I'm not very good with threading so I can't really comment on what it really does.
    // It would be better for an expert to talk about it.

    // Lastly, you also need to compare the two co-ordinates (ax to bx, az to bz, ay to by).
    // You need to find the min and the max to make sure your loop is carried out succesfully.
    // Otherwise your second block could be at a lower co-ordinate than the first, so it wouldn't do anything!
    // That is a little confusing, as I am bad at explaining, but you'll understand it if you think about it logically.

  Thread cuboidThread = new Thread(new ThreadStart(delegate
  {
     for (ushort x = Math.Min(ax, bx); x < Math.Max(ax, bx); ++x)
     {
        for (ushort y = Math.Min(ay, by); x < Math.Max(ay, by); ++y)
        {
           for (ushort z = Math.Min(az, bz); x < Math.Max(az, bz); ++z)
           {
              p.level.Blockchange(p, x, y, z, blockType);
           }
        }   
    }
  }));
  cuboidThread.Start();
}


Well... I tried my best to explain some things to you, but I'm not amazing with block modifying myself, so some things could be a little bit wrong, but all in all I hope I taught you something.

All of the above code is based from MCLawl source, I know MCDzienny is forked from MCLawl but I don't know how much he may have touched up on some of the methods within Player.cs and Level.cs so you'd be best to confront him if you get errors trying to use the methods outlined below. It could be because he has changed the arguments needed for them.

For example, Level.Blockchange, Level.GetTile, Player.SendBlockchange, etc. I also think he may have changed the p.level.height/depth/width/length around, as MCLawl originally messed up on these, so make sure you understand them before trying to code with them, and acknowledge which axis is which.

After you learn all of the basics, you can start looking at buffers, i.e. Lists of the CatchPos struct, or Pos, which contain multiple co-ordinates, so you can make things even more magical. If you need any help with anything then just pop up on skype.

Bye!


P.S This is AMAZING:

Code: Select all
        public void Blockchange1(Player p, ushort x, ushort y, ushort z, byte type)
        {
            p.ClearBlockchange();
            byte b = p.level.GetTile(x, y, z);
            p.SendBlockchange(x, y, z, b);

            p.level.Blockchange(p, x, y, z, Block.yellow);
            p.level.Blockchange(p, (ushort)((x + 1)), y, z, Block.yellow);
            p.level.Blockchange(p, (ushort)((x + 1)), (ushort)((y + 1)), z, Block.yellow);
            p.level.Blockchange(p, (ushort)((x + 2)), (ushort)((y + 1)), z, Block.staircasestep);       
        }
Conor (Conanza121)
User avatar
Conor
Coder
 
Posts: 390
Joined: 10 Oct 2012, 21:36
Location: @21Conor

Re: Place Block

Postby dzienny » 11 Jan 2013, 23:59

That's a very good article! I'm sure that many coders will find it helpful, especially because capturing and dealing with a block change is a bit complex.

MCDzienny was meant to keep the back compatibility, so everything should work fine.
Spoiler:
User avatar
dzienny
Administrator
 
Posts: 1181
Joined: 23 Jan 2011, 14:27

Re: Place Block

Postby joppiesaus » 14 Jan 2013, 14:46

Conor wrote:Hey meep :)

I thought I'd go through the basics with you to help you out. I remember learning this for the first time too and its confusing at first but then you realise its all very beautiful.

But first is first, if you want to use a command that already exists, such as /tree, you should learn this useful way of doing so. This is how you do it on MCLawl/old MCForge, and I'm guessing it is somewhat the same for MCDzienny... but it may have changed:

Code: Select all
Command.all.Find("commandName").Use(Player, "param for the command, being used by Player");


The typical method to place a block in the players level is as follows.

Code: Select all

// Places a block in p.level
// at co-ords x, y and z
// With the byte for the yellow wool block

ushort x = 10;
ushort y = 50;
ushort z = 100;
p.level.Blockchange(p, x, y, z, Block.yellow);


x, y and z are a ushort variable. They are the values of the co-ordinates themselves. But, if you want to place a block where a player is standing for example, then you will need to divide their position by 32. This is a common mistake people make. For example;

Code: Select all
// define the variables for their position, divide by 32 to get the level co-ordinate
ushort x = (ushort)(p.pos[0] / 32);
ushort y = (ushort)(p.pos[1] / 32);
ushort z = (ushort)(p.pos[2] / 32);

//Do the blockchange at their current position
p.level.Blockchange(p, x, y, z, Block.Red);


So, if you want to make a command that places a block in a straight line, forming a pillar from their position, which is 3 blocks long, you could do this:

Code: Select all
// define the variables for their position, divide by 32 to get the level co-ordinate
ushort x = (ushort)(p.pos[0] / 32);
ushort y = (ushort)(p.pos[1] / 32);
ushort z = (ushort)(p.pos[2] / 32);

//Do the blockchange at their current position and 2 blocks above.
p.level.Blockchange(p, x, y, z, Block.White);
p.level.Blockchange(p, x, (ushort)(y + 1), z, Block.White);
p.level.Blockchange(p, x, (ushort)(y + 2), z, Block.White);


Although, it is common that you will use the for loop when modifying blocks within your commands. So, lets improve our pillar, and make it pillar up to the sky. We are also going to check if it hits a block before it reaches the sky, in this case, we would stop the pillar. It would look something like this;

Code: Select all
// define the variables for their position, divide by 32 to get the level co-ordinate
ushort x = (ushort)(p.pos[0] / 32);
ushort y = (ushort)(p.pos[1] / 32);
ushort z = (ushort)(p.pos[2] / 32);

// Start a loop which will pillar upwards
// The loop makes the y value increase by one every cycle, it goes until the maps height is reached.
// But we want to check for any solid blocks that could get in the way, which is the if statement you see in the loop.

for (ushort s = y; s < (ushort)(p.level.depth); ++s) // May be Level.height in MCDzienny.
{
   if (p.level.GetTile(x, s, z) != Block.air))
   {
       // if the block isn't air, stop pillaring up - break from the loop.
       break;
   }
      // else, place the next block within the pillar
   p.level.Blockchange(p, x, s, z);
}


So most of the time, you want the player to place a block before you do something. So you need to trigger the event known as 'Player.Blockchange'. This event occurs when a player places blocks, or deletes them. So, you want to make your own little method in your command, which is what will happen when this is triggered. You can do that like this:

Code: Select all
public override void Use(Player p, string message)
{
   Player.SendMessage(p, "Place a block to start your command in that position.");
   p.Blockchange += new Player.BlockchangeEventHandler(Blockchange1);
}

public void Blockchange1(Player p, ushort x, ushort y, ushort z, byte type)
{
    Player.SendMessage(p, "You placed a block!");
}


Now, it is important to do a few important things within this method when a player places a block, before you get into the real workings of your code. They are explained below.

Code: Select all
public void Blockchange1(Player p, ushort x, ushort y, ushort z, byte type)
{
   // This removes the link between this method and the block event. So it won't keep happening when they place blocks after.
   p.ClearBlockchange();
 
   // Basically, this gets the block that was in the position that they destroyed (or if air, what they placed).
   // Then we send them the block back again, so it looks like nothing has happened.
   byte b = p.level.GetTile(x, y, z);
   p.SendBlockchange(x, y, z, b);
}


Also what is interesting about the above, you may have noticed the 'byte type' variable within the mechanism. And yes, as you probably imagined, this is the byte of the block they placed. So we could use it to place blocks of the block they were handling:

Code: Select all
p.level.Blockchange(p, x, y, z, type);


As for your request, the solid cuboid command from the top of my head would look something like:

Code: Select all
public override void Use(Player p, string message)
{
   Player.SendMessage(p, "Place a block to determine the first edge of your cuboid.");
   p.Blockchange += new Player.BlockchangeEventHandler(Blockchange1);
}

public ushort ax, ushort ay, ushort az, ushort bx, ushort by, ushort bz;
public byte blockType;

public void Blockchange1(Player p, ushort x, ushort y, ushort z, byte type)
{
    ax = x; ay = y; az = z;

    p.ClearBlockchange();
    byte b = p.level.GetTile(x, y, z);
    p.SendBlockchange(x, y, z, b);

    Player.SendMessage(p, "Now place the second edge for your cuboid.");
    p.Blockchange += new Player.BlockchangeEventHandler(Blockchange2);
}

public void Blockchange2(Player p, ushort x, ushort y, ushort z, byte type)
{
    bx = x; by = y; bz = z; blockType = type;

    p.ClearBlockchange();
    byte b = p.level.GetTile(x, y, z);
    p.SendBlockchange(x, y, z, b);

    doCuboid();
}

public void doCuboid()
{
    // So we have the two positions to start and finish from, the co-ordinates are variables:
    // ax, ay, az
    // bx, by, bz
    // And the block they're cuboiding in is saved in vairable 'blockType'.

    // So now we can just use this information in for loops, to cuboid. I'll put it in a thread to prevent major lag.
    // The thread makes it run as a separate process from the command... something like that.
    // I don't think it makes much difference, I'm not very good with threading so I can't really comment on what it really does.
    // It would be better for an expert to talk about it.

    // Lastly, you also need to compare the two co-ordinates (ax to bx, az to bz, ay to by).
    // You need to find the min and the max to make sure your loop is carried out succesfully.
    // Otherwise your second block could be at a lower co-ordinate than the first, so it wouldn't do anything!
    // That is a little confusing, as I am bad at explaining, but you'll understand it if you think about it logically.

  Thread cuboidThread = new Thread(new ThreadStart(delegate
  {
     for (ushort x = Math.Min(ax, bx); x < Math.Max(ax, bx); ++x)
     {
        for (ushort y = Math.Min(ay, by); x < Math.Max(ay, by); ++y)
        {
           for (ushort z = Math.Min(az, bz); x < Math.Max(az, bz); ++z)
           {
              p.level.Blockchange(p, x, y, z, blockType);
           }
        }   
    }
  }));
  cuboidThread.Start();
}


Well... I tried my best to explain some things to you, but I'm not amazing with block modifying myself, so some things could be a little bit wrong, but all in all I hope I taught you something.

All of the above code is based from MCLawl source, I know MCDzienny is forked from MCLawl but I don't know how much he may have touched up on some of the methods within Player.cs and Level.cs so you'd be best to confront him if you get errors trying to use the methods outlined below. It could be because he has changed the arguments needed for them.

For example, Level.Blockchange, Level.GetTile, Player.SendBlockchange, etc. I also think he may have changed the p.level.height/depth/width/length around, as MCLawl originally messed up on these, so make sure you understand them before trying to code with them, and acknowledge which axis is which.

After you learn all of the basics, you can start looking at buffers, i.e. Lists of the CatchPos struct, or Pos, which contain multiple co-ordinates, so you can make things even more magical. If you need any help with anything then just pop up on skype.

Bye!


P.S This is AMAZING:

Code: Select all
        public void Blockchange1(Player p, ushort x, ushort y, ushort z, byte type)
        {
            p.ClearBlockchange();
            byte b = p.level.GetTile(x, y, z);
            p.SendBlockchange(x, y, z, b);

            p.level.Blockchange(p, x, y, z, Block.yellow);
            p.level.Blockchange(p, (ushort)((x + 1)), y, z, Block.yellow);
            p.level.Blockchange(p, (ushort)((x + 1)), (ushort)((y + 1)), z, Block.yellow);
            p.level.Blockchange(p, (ushort)((x + 2)), (ushort)((y + 1)), z, Block.staircasestep);       
        }

wow... this is some good stuff.
are you coding at an university? or did you just learned this yourselve?
anyway, thanks.
joppiesaus
 
Posts: 379
Joined: 20 Aug 2012, 07:28
Location: in a obsedian house, with glass in it so i can see the lava!

Re: Place Block

Postby Conor » 14 Jan 2013, 17:40

I learnt by myself. Well that is a lie, I learnt by watching tutorials, asking people for help, and spending many months coding for minecraft classic.

I will be going to university to study computer science though haha :P
Conor (Conanza121)
User avatar
Conor
Coder
 
Posts: 390
Joined: 10 Oct 2012, 21:36
Location: @21Conor


Return to Help in Coding

Who is online

Users browsing this forum: No registered users and 1 guest

cron