The Video

Where To Go From Here

Take a look around at the .js files in the scriptcraft/plugins directory.

My Notes for the Video

The following sections are my unedited notes that I wrote before making the video.

Part 6: More JS!

In the last part, we broke free from the command line, opening up all kinds of new possibilities. In this part, we’re going to learn more features of JavaScript that we can use to bend Minecraft to our will.

But first, debugging

When you’re writing code, inevitably something is going to go awry. JSHint will help you catch certain kinds of errors, but there are many others that it will miss.

One way to debug is to try things out in Minecraft with the command line. If you can double check a little piece of code there, that may help you.

Probably the easiest and most powerful tool in your toolbelt is echo. Let’s look at mycottage from last time:

/*global require, echo*/

var Drone = require("drone");
var blocks = require("blocks");

function mycottage() {
    this
    .chkpt("cottage")
    .down()
    .box(blocks.birch, 7, 1, 6) // birch wood floor
    .up()
    .box(blocks.air, 7, 5, 6) // clear area first
    .box0(blocks.moss_stone, 7, 2, 6)
    .right(3)
    .door()
    .up(1)
    .left(2)
    .box(blocks.glass_pane)
    .right(4)
    .box(blocks.glass_pane)
    .left(5)
    .up()
    .prism0(blocks.stairs.oak, 7, 6) // add a roof
    .down()
    .right(4)
    .back()
    .wallsign(["My", "Cottage"])
    .move("cottage")
    .right(3)
    .fwd(4)
    .up()
    .hangtorch()
    .move("cottage")
    .right()
    .fwd(3)
    .bed()
    .fwd()
    .right(4)
    .box(blocks.furnace)
    .move("cottage");
}

Drone.extend(mycottage);

When ScriptCraft loads this script, it runs the code outside of the mycottage function at the time it loads. The code inside of mycottage only runs when you call it on the Drone. That means that adding echo calls outside of mycottage will tell you if you’ve got a problem even telling ScriptCraft about mycottage. If the cottage doesn’t build correctly, you’ll want to add echo inside. Here’s an example:

/*global require, echo*/

echo("Going to require other modules");
var Drone = require("drone");
var blocks = require("blocks");
echo("Modules loaded");

function mycottage() {
    this
    .chkpt("cottage")
    .down()
    .box(blocks.birch, 7, 1, 6) // birch wood floor
    .up()
    .box(blocks.air, 7, 5, 6) // clear area first
    .box0(blocks.moss_stone, 7, 2, 6)
    .right(3)
    .door()
    .up(1)
    .left(2)
    .box(blocks.glass_pane)
    .right(4)
    .box(blocks.glass_pane)
    .left(5)
    .up();

    echo("I think something is going on with the roof");

    this
    .prismo(blocks.stairs.oak, 7, 6); // add a roof

	echo("Did the roof draw okay?");

	this
    .down()
    .right(4)
    .back()
    .wallsign(["My", "Cottage"])
    .move("cottage")
    .right(3)
    .fwd(4)
    .up()
    .hangtorch()
    .move("cottage")
    .right()
    .fwd(3)
    .bed()
    .fwd()
    .right(4)
    .box(blocks.furnace)
    .move("cottage");
}

Drone.extend(mycottage);
echo("Drone extended, ready to build the cottage!");

In order to put some echo calls in the middle of the Drone’s drawing, I needed to break up those chained function calls. Those chains().of().calls().strung().together() is a feature of the Drone in particular, and echo isn’t a part of that.

So, I added a ; after that up() call, put the echo call after that (with a ; terminating that, as is normal for JavaScript statements), and then added another this because I needed to restart the chain where the Drone was located.

commands and function arguments

With a quick debugging tutorial out of the way, let’s go back to adding things to Minecraft.

You can create special “jsp” commands using the command function. These commands are called with two arguments. When we’ve called functions before on the Drone, we’ve included arguments. When you want to write a function that takes arguments, you give each one a name based on what is going to be passed in when the function is called. ScriptCraft commands are automatically called with two arguments: parameters and player. parameters is each of the words you type in when you run the command. It’s an array. player is an object with all kinds of stuff on it, as we’ll see shortly.

Let’s write a command. Create a new file in your scriptcraft/plugins directory called “mycommands.js”. This is what goes into it:

/*global require, echo, command*/

command("hi", function (parameters, player) {
    var salutation = parameters[0];
    echo(player, salutation + " " + player.name);
});

Now we try it. In Minecraft, type /jsp hi Amazing.

Conditionals and doing stuff with the player object

In Minecraft, try this:

/js self.location.world.setRaining(true)

Pretty neat to control the weather, eh? Let’s turn the rain back off.

That’s a bit inconvenient to type though. Wouldn’t it be great to have a command to control the weather?

command("rain", function (parameters, player) {
    player.location.world.setRaining(true);
});

With that, we have a rain command that makes it rain. It would be nice to be able to turn it off as well. We could make a command called rainoff that does the opposite. Or we could add a parameter to rain that turns it off, if that parameter is there.

So far, all of our code has been “do this, then do that, then do the other”. Now we want a command that does something if a certain parameter is there. And if is the name of the JavaScript keyword we want.

command("rain", function (parameters, player) {
    if (parameters[0] === "off") {
        player.location.world.setRaining(false);
    } else {
        player.location.world.setRaining(true);
    }
});

Give it a try with /jsp rain and /jsp rain off.

for loops

Another new idea along the lines of stretching beyond “do this then do that” is the notion of “do this 5 times then do that”. Of course, if you want to do something five times, you could just write out that set of commands five times. Or, you could make a function that does what you want, and then call that five times.

But, what if you want to do something 100 times. That’s getting to be a bit much to type out. Or, what if you don’t know how many times you want to do something at the time you write the code?

As an example, let’s make a Drone function called torchwall that creates a wall that has torches. You’ll be able to pass in the width and height of the wall.

function torchwall(width, height) {
    this.chkpt("wall");
    this.box(blocks.stone, width, height, 1);
    this.move("wall");
}

Drone.extend(torchwall);

That basic scaffolding will give us a stone wall with the width and height that are passed in. How would we add torches along this wall when we don’t know how big it is? Let’s start by adding one torch:

function torchwall(width, height) {
    this.chkpt("wall");
    this.box(blocks.stone, width, height, 1);

    this
    .up()
    .back()
    .hangtorch()
    .fwd()
    .down()
    .right();

    this.move("wall");
}

Drone.extend(torchwall);

That places a torch one block up on the wall and then moves the Drone back to the bottom. If we could just repeat that, we’d have torches along the wall.

Here’s how we use a for loop to do that:

    for (var counter = 0; counter < width; counter++) {
        this
        .up()
        .back()
        .hangtorch()
        .fwd()
        .down()
        .right();
    }

Note that in Brackets and other editors, you can select a few lines of code and use the Indent or Unindent command in the Edit menu to move the code. Take a look at the edit menu and you’ll likely find a lot of stuff that will come in handy.

So, the for keyword takes a bunch of crazy looking stuff and then has a block of code that it will repeat some number of times. Let’s break apart that for:

for (var counter = 0; counter < width; counter++)

We’ve seen something like var counter = 0 before. This does exactly what it looks like: before it starts looping, it sets a variable called counter to 0. The next part is a check that it does before each run of the loop. This one says to see if counter is less than width, so as long as the current value of counter is less than width, it will run the code to add a torch. The last part, counter++ is what happens at the end of the loop. In JavaScript, you can use ++ as a quick way to add one to a number (this is called incrementing).

Let’s step through what happens if the width is 3.

  1. counter is set to 0
  2. Is counter less than the width (3)? Yes it is, so add a torch (which ultimately causes the Drone to move 1 block to the right)
  3. Increment counter so that it becomes 1.
  4. Is counter (now 1) less than the width (3)? Yes it is, so add another torch.
  5. Increment counter so that it becomes 2.
  6. Is counter (now 2) less than the width (3)? Yes it is, so add another torch.
  7. Increment counter so that it becomes 3.
  8. Is counter (now 3) less than the width (3)? No! So, the loop stops and we start running the code just after the for loop’s block.

The code makes a wall that is 3 blocks wide and places 3 torches. When you list out all of the little things a computer has to do, it seems like a lot. But, modern computers do literally billions of things per second.

Let’s try it:

/js refresh()
/js up().torchwall(10, 3)

It seems a bit much to put torches all along the wall, right? We can make it so that you can space them out with an extra argument to the function.

function torchwall(width, height, torchspacing) {
    this.chkpt("wall");
    this.box(blocks.stone, width, height, 1);
    for (var counter = 0; counter < width; counter += torchspacing) {
        this
        .up()
        .back()
        .hangtorch()
        .fwd()
        .down()
        .right(torchspacing);
    }
    this.move("wall");
}

Let’s erase our old wall and try again.

/js refresh()
/js box(10, 3, 1)
/js up().torchwall(10, 3, 3)

We add a torchspacing argument and change the last part of the for loop to counter += torchspacing. += is a nice shorthand for saying counter = counter + torchspacing, which boils down to “add torchspacing to counter”. The final change is at the end of the for loop’s code block, we move right by torchspacing rather than just 1.

So, if torchspacing is 3, we’ll move right by 3 at the end of that block. As before, we stop placing torches once counter is greater than or equal to width, so if the width is 3 we’ll only place a single torch. If it’s 9, we’ll place 3 torches.

Putting it all together

Here’s a couple of quick Brackets tricks. You can open another file by tracking it down in the file tree over here. But, you can also use Quick Open from the Navigate menu. It’s cmd-shift-O on the Mac and ctrl-shift-O on Windows. You just start typing. I’ll type “cottage” and then open the cottage.js file that comes with ScriptCraft.

Also under the Navigate menu is “Quick Find Definition”. This lets you jump to a function quickly. I’ll select cottage_road.

I don’t know how new cottage_road is, but I only noticed it recently and it’s very cool. Let’s try it.

/js cottage_road(6)

This gives us 6 cottages along a nice street.

There’s a lot going on in this code and it takes a good deal of practice to write code like this. But, I want to talk through it so that at least you’ll be able to read it and have an idea of what it’s doing.

function cottage_road( numberCottages ) {
  if (typeof numberCottages == 'undefined'){
    numberCottages = 6;
  }

JavaScript has a keyword, typeof that takes a variable and tells you what kind of variable it is, whether it’s a number, array, object or, in this case, “undefined”. undefined is a special value in JavaScript. If you don’t pass in an argument to this function, numberCottages will be undefined. If it is undefined, this code will set numberCottages to 6.

  var i=0, distanceBetweenTrees = 11;
  //
  // step 1 build the road.
  //
  var cottagesPerSide = Math.floor(numberCottages/2);

We define a couple of variables that we’ll use. Math is an object that comes with JavaScript that includes a number of useful math functions. The Math.floor function rounds a number down to the previous integer. If numberCottages is 6, then you divide 6 by 2 and get 3. 3 is already an integer, so Math.floor doesn’t change it. If numberCottages is 7, then you divide 7 by 2 and get 3.5. That’s not an integer, so Math.floor basically just chops off anything after the decimal point, rounding the number down to 3.

  this
    .chkpt('cottage_road') // make sure the drone's state is saved.
    .box( blocks.double_slab.stone, 3, 1, cottagesPerSide * ( distanceBetweenTrees + 1 ) ) // build the road
    .up()
    .right() // now centered in middle of road
    .chkpt('cr'); // will be returning to this position later

This is just like all of the drawing code we’ve seen before. It creates the road.

  for ( ; i < cottagesPerSide+1;i++ ) {
    this
      .left(5)
      .oak()
      .right(10)
      .oak()
      .left(5) // return to middle of road
      .fwd( distanceBetweenTrees + 1 ); // move forward.
  }

Now, we’ve got a for loop, just like we’ve been discussing. This one didn’t need to initialize anything, so there’s just a ; after the left parenthesis. This loop will put one more tree per side than there are cottages per side, because of the +1 there. The body of the loop just goes to the left, places an oak, goes to the right, places an oak, returns to the road and then moves forward by the distanceBetweenTrees amount that was computed earlier.

  this
    .move('cr')
    .back(6); // move back 1/2 the distance between trees

More standard drawing stuff.

  function pathAndCottage( drone ) {
    drone
      .down()
      .box(blocks.double_slab.stone, 1, 1, 5)
      .fwd(5)
      .left(3)
      .up()
      .cottage();
    return drone;
  };

This is interesting… there’s a function inside of this function. This is perfectly legal in JavaScript and is a nice way to split things up sometimes. The pathAndCottage function is only available to code within the cottage_road function. The cottage function can’t use it, because it’s tucked away inside of cottage_road.

pathAndCottage is given the drone to draw with and then it puts a path to the cottage and places the cottage itself using normal drone drawing functions.

Another for loop:

  for ( i = 0; i < cottagesPerSide; i++ ) {
    this
      .fwd( distanceBetweenTrees + 1 )
      .chkpt('r'+i);
    // build cottage on left
    pathAndCottage( this.turn(3) ).move( 'r' + i );
    // build cottage on right
    pathAndCottage( this.turn() ).move( 'r' + i );
  }

This starts by resetting i, which is a common variable name used for counters in for loops. It moves forward, keeps a checkpoint with a name of 'r'+i. This means that the first checkpoint will be called “r0”, the next is “r1” and so on. Then you can see why pathAndCottage is a function: it’s used to place the cottages on the right and left. If pathAndCottage wasn’t a function, then there would probably be two copies of the code to draw the path and cottage. That would work fine, but you’ll find it’s often a good idea to make a function rather than duplicating some code.

And, we end the function like most of our drawing code:

  // return drone to where it was at start of function
  this.move('cottage_road');

Look around!

Take a look around at the .js files in the scriptcraft folder. Especially look in scriptcraft/plugins/drone/contrib where you can see how much of the cool stuff on the drone is built. You can even copy and paste code from there as a starting point!

debugging, if and for

In this part, we covered debugging, if and for. That’s a pretty powerful collection of tools for your JavaScript toolbelt. When you use a hammer for the first time, the nail probably isn’t going to go in totally straight… using these tools that the JavaScript language gives us takes some practice to get right, but give it a whirl!