Spellscript Tutorial
This is an informal guide on how to get started creating Spellscript scripts in Minecraft. This guide will assume you're an absolute beginner to programming, and will do its best to not only explain the syntax of Spellscript, but give you a solid introduction to the concepts behind programming as well (you'll need them to make awesome scripts!)
See Spellscript for a general language reference-- otherwise, read on...
Contents |
Your First Spellscript Script
To get started, open up Minecraft via the command line-- this is important so you can see your scripts' output and any lengthy error messages. The easiest way to do this on Windows is to create a new .txt file in the same directory as your Minecraft.exe, stick the following text in it, rename the extension to .bat, then double-click it. Here's the text:
java -Xmx2048M -Xms2048M -jar "Minecraft.exe"
Once you've started Minecraft, make sure the command prompt/terminal/crazy-box-full-of-text has opened, then go ahead and create a new single player world in Creative Mode. Now, place a Spellscript-Block wherever you please, and a lever right next to it-- you can find both in the Creative inventory. Once placed, open up the Spellscript Editor by right-clicking on the block. By default, it's name will be "Untitled Spellscript-Block", and it's content will be "pass" (a statement which equates to "Do nothing"). For your first script, we're going to print "Hello world!" to the command line! Simply replace "pass" with the following:
print "Hello world!"
Now press ctrl+enter, and verify the script is compiling. Now press escape, yank on the lever, and voila! Was "Hello world!" printed to your console? Great, you've created your first Spellscript script! Easy peasy. This is called a "print" statement, and simply prints the expression to its right to the terminal.
Variables and State
Now let's try something a bit more complicated:
string left = "Hello" string right = "world!" print left + " " + right
Go ahead and recompile it and yank the lever to make sure its working; it should have printed the exact same thing to the console. The difference is, we separated the string into three parts using two variables (left and right) and one literal (" "), then concatenated them with the '+' operator in the print statement's expression.
Variables are a very important concept in programming-- they allow your scripts to have "state". In other words, the value stored in a variable can change (hence the name), which can in turn affect the execution of your script. Combined with control-statements, scripts allow you to do truly powerful and creative things. Consider the following:
int i = 0
while i < 10:
print i
i += 1
This prints the numbers 0 though 9 to the console. How? Let's step through it.
First, we create i and set it to 0. We then compare it to 10: if it's less, we print i, increment it by 1, then go back to the comparison step-- otherwise we cease looping. Intuitively, this process will repeat until i equals 10, at which point we exit the loop and hit the end of the script.
Be careful using the while loop: if the comparison always evaluates to True, you'll be stuck in the loop indefinitely and have to force-quit Minecraft! We'll talk about a safer construct next (not to dissuade you from the while loop's usefulness!)
The For Loop
Incidentally, the for loop can be used as a shorthand version of the entire previous construct, like so:
for int i in 10:
print i
We call this "iteration" over the integers 0 through 9. The for loop is your friend-- be good to it and it will be good to you!
Another kind of iteration the for loop is capable of is list iteration; that is, rather than incrementing an integer with each execution of the inner block of code, we set it to each element of the list in order, ceasing when we've iterated over every element. Go ahead and stick this in a script block, compile it with ctrl+enter, then give that lever another tug:
# This is a comment! Anything to the right of a '#' is removed from the script.
string[] lines = ["Hello", "World", "This", "Is", "Rad", "Yo"]
for string line in lines:
print i
# The following should be output to the console:
# Hello
# World
# This
# Is
# Rad
# Yo
Did the numbers get output the terminal? Awesome!
<to be continued>
Types and Methods
if activator is None:
return
if not (activator instanceof Player):
return
Player player = activator as Player
player.tell("Hello " + player.getName() + "!")
We'll break this down and explain what's happening, but first, go ahead and compile, enter Survival Mode, then left-click the block to make sure everything's working (if you don't enter Survival Mode, you'll instantly break the block!) Ready? ZOMG, IT KNOWS YOUR NAME!!!
Now to explain-- You'll notice a variable activator that we never declared-- this variable was handed to us by the Spellscript block and contains the living entity that interacted with the block. (You can find all such variables and their types by bringing up the Info Screen via ctrl+/)
Initially, we check activator against None, a special value indicating a value of "nothing". If activator is None, the block wasn't activated directly by an entity (as explained in the Info Screen), so we don't do anything-- thus, we return, which means we cease execution early, basically.
Next if statement, we check to ensure the activator is a player and not just some arbitrary living entity we weren't expecting. To do this, we check the type of activator via the instanceof keyword. (If you open the Info screen via ctrl+/, you'll notice the type of activator is actually Living, which means the activator isn't necessarily a Player. This involves a programming concept known as "Polymorphism", which we'll discuss later.) We then invert the value of this expression with the not operator, and again cease execution with return if the whole expression evaluates to True.
Now that we're certain that the activator is a Player, we go ahead and safe-cast it as such and stick it in a new variable so we can start working with it as Player, not just as a Living. If we safe-casted a Living that happened not to be a Player, the expression would evaluate to None-- however, we've already checked for this and are consequently guaranteed that it's a Player, so we don't have to check for None as we did in the first step.
Finally, we call a method belonging to player called "tell", that accepts a string parameter. This particular method causes the parameter handed to it to be printed to the player's chat console (which is what we want!), so we grab the player's name with another method called "getName" and give him a detailed "hello" message!
Now the script has reached its end, and will finish execution. Whew!
Polymorphism
Two important concepts here are types and methods. To understand these, you must first understand the programming concept of Polymorphism, which basically means an object can be of more than one type at once. In Spellscript (as in many programming languages), this is accomplished via "single-inheritance." Think of it as a square-rectangle relationship-- just as a square is a rectangle but a rectangle isn't necessarily a square, a Player is a Living but a Living isn't necessarily a Player. The instanceof and as operations allow us to check for the corner cases when a rectangle happens to be a square, or a Living happens to be Player.
The nature of this relationship let's Spellscript do useful things with methods. Particularly, it means if type B "inherits" from type A, then objects of type B have all the methods of type A, in addition to their own. Even better, if a type C inherits from type B, then C has all the methods of type B and A! Thus in our case, if Player inherits from Living, and Living inherits from Entity, it follows that Player has all the methods of Living and Entity in addition to its own!
This is vital information, as it enables you to understand what methods are available on which objects. To see all of Hack/Mine's available methods in the Spellscript Editor, simply press ctrl+m. You'll see a list of all the types available, in addition to what types they inherit from and what methods belong to them. So if Entity has a method getX, so do Living and Player. Piece of cake!
<to be continued>