Spellscript Tutorial

From Hack/Mine Wiki
(Difference between revisions)
Jump to: navigation, search
(Your First Spellscript Script)
 
(44 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{mbox|type=caution|text=This article is totally a work-in-progress, y'all.  Expect frequent updates over the next several days.}}
+
 
 
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!)
 
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...
 
See [[Spellscript]] for a general language reference-- otherwise, read on...
 
==Viewing Spellscript Output==
 
{{mbox|text=The following methods of opening the console won't work if you're launching via the Technic Launcher-- until Technic-y solution is discovered, you'll need to do a manual installation if you want to view it.  Sorry!}}
 
To get started with Spellscript, you'll want to be viewing Minecraft's console output-- this is important so you can see your ''scripts''' output and any lengthy error messages.
 
 
===Command Line (.bat) Method===
 
The easiest way to view Minecraft's output on Windows is to create a .bat file to run it.  This can be done as follows:
 
* Create a file named "run_minecraft.txt" in the same directory as your Minecraft.exe.
 
* Stick the following text in it:
 
java -jar "Minecraft.exe"
 
* Rename the file's extension to .bat, and you're done!
 
 
From now on, you can simply double-click this file to run Minecraft with visible console output.
 
 
===MultiMC Method===
 
If you're using [http://forkk.net/MultiMC4/ MultiMC] to run H/M, it has a handy console viewing option you can use.  They've got info on how to set up mods with it (which includes Hack/Mine!)
 
  
 
==Your First Spellscript Script==
 
==Your First Spellscript Script==
Once you've started Minecraft, make sure the terminal/console/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:
+
Go ahead and start Minecraft ''with the console open'' ([[Viewing Spellscript Output|instructions]]), then 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 those are 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:
 
  <nowiki>print "Hello world!"</nowiki>
 
  <nowiki>print "Hello world!"</nowiki>
  
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.
+
Now press ''ctrl''+''enter'', and verify the script is compiling.  Now press ''escape'', pull 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==
+
==Another Example, Followed by Explanation==
 
Now let's try something a bit more complicated:
 
Now let's try something a bit more complicated:
  <nowiki>string left = "Hello"
+
  <nowiki>str left = "Hello"
string right = "world!"
+
str right = "world!"
  
 
print left + " " + right</nowiki>
 
print left + " " + right</nowiki>
  
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.
+
Now, recompile the script and pull the lever to make sure its still 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'') of type ''str'', and one ''str literal'' (" "), then stuck them together with the '+' operator.
  
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:
+
===Order of Execution===
 +
In many programming languages (Spellscript included), statements are executed from top to bottom-- in programmer jargon, this means that Spellscript is an ''imperative'' programming language.  To elaborate on the above example: first, ''left'' is given the value ''"Hello"'', then ''right'' is given the value ''"world!"'', and then finally, the variables are combined and the result is printed to the console.
 +
 
 +
If the top two lines of code were placed ''below'' the print statement, the script would have failed to compile!  This is because variables must be ''declared'' before they are used (which ''left'' and ''right'' wouldn't be.)  Thus, order of statements is extremely important, and is one of the first hurdles for novice programmers to understand.  While in this example the script simply fails to compile, other cases may result in a valid but logically different result.
 +
 
 +
===Variables and State===
 +
Next on the explanation agenda are ''variables'', 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.
 +
 
 +
A variable is declared like ''<type> <name>'', and its valued is changed with the assignment operator, ''='', like so:
 +
  <nowiki>str left
 +
left = "Hello"
 +
 
 +
int number
 +
number = 3</nowiki>
 +
The two may be combined, as in the original script, to both declare and assign to the variable in one statement:
 +
<nowiki>int anotherNumber = 200</nowiki>
 +
Here's a simple example to demonstrate how a variable can change:
 +
<nowiki>int number = 0
 +
print number
 +
 
 +
number = 7
 +
print number
 +
 
 +
number = 234
 +
print number</nowiki>
 +
If put in a Spellscript-block, the above would have printed "0", "7" and then "234" to the console.
 +
 
 +
====Undefined Behavior====
 +
Currently in Spellscript, using a declared variable before anything is assigned to it results in "undefined behavior."  This means nothing is guaranteed about that variable's value; it could be some random value, or something that will crash your script when accessed.  While some day Spellscript will validate this and throw a compilation error, it's currently your responsibility to ''always'' assign to your variables before using them.  Using compound declaration-assignment statements instead of plain ol' declaration statements is a surefire way to avoid this issue.
 +
 
 +
==The ''If'' Statement==
 +
Combined with control-statements (such as the ''if'' statement, ''while'' loop, and ''for'' loop), variables allow you to do truly powerful things.  Consider the following:
 +
<nowiki>int i = 230
 +
if i > 200:
 +
    print "Yep!"</nowiki>
 +
 
 +
Simply put, the ''if'' statement checks the expression its given, and if true, executes the indented block of code beneath it.  So in our case, we first assign 230 to ''i''.  Then, we check to see if ''i'' is greater than 200: if true, we print "Yep!" to the console!
 +
 
 +
In its current state, this script will always print "Yep!" to the console; if you change 230 to a value less than or equal to 200, however, this script will never print anything at all... you've now experienced the power of the ''if'' statement!
 +
 
 +
But wait, there's more!  You can also chain what are called ''else'' and ''elif'' statements to the ''if'' statement, like so:
 +
 
 +
<nowiki>int i = 13
 +
if i > 0:
 +
    print "Positive!"
 +
elif i < 0:
 +
    print "Negative!"
 +
else:
 +
    print "Zero!"
 +
 
 +
print "Done!"</nowiki>
 +
 
 +
In this script, we first check if ''i'' is greater than 0; if so, we print "Positive!" then exit the if-chain; else, we continue down the chain, checking if ''i'' is less than 0; if so, we print "Negative!" then exit the if-chain; else, we print "Zero!", then exit the if-chain.  In all cases, once we've exited the chain, we print "Done!" and the script is finished.  Whew!
 +
 
 +
The reason we call this an "if-chain" is because you can chain as many ''elif'' statements as you like to the end of an ''if'' statement (or none at all), optionally including an ''else'' statement at the very end, which only executes if all prior expressions in the chain evaluated to false.
 +
 
 +
Go ahead and experiment with different values for ''i'' in the above example, just to convince yourself that it's logically doing what it appears to be: values lower than, greater than, and equal to zero are reported as being negative, positive, and zero, respectively.
 +
 
 +
==The ''While'' Loop==
 +
Next we'll explain how to use the ''while'' loop.  Essentially, once encountered, the ''while'' loop will check its given condition; if that condition is true, it executes its inner block of code, then goes back to the last step; otherwise, execution leaves the ''while'' loop.  Intuitively, if the condition is always false, then the inner block is never executed.  In contrast, if the condition is always true, the inner block will be executed infinitely many times (effectively locking up Minecraft.)  Thankfully, variables allow us to find a happy medium, letting us execute the inner block a controlled number of times to accomplish some task.
 +
 
 +
Observe!
 
  <nowiki>int i = 0
 
  <nowiki>int i = 0
  
 
while i < 10:
 
while i < 10:
 
     print i
 
     print i
     i += 1</nowiki>
+
     i = i + 1</nowiki>
  
 
This prints the numbers 0 though 9 to the console.  How?  Let's step through it.
 
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.
+
First, we create ''i'' and set it to 0.  We then compare it to 10: if it's less, we execute the indented block of code, then go back to the comparison step-- otherwise we cease looping. And in the indented block of code, we simply print ''i'' then assign it to ''itself'' plus 1! 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 MinecraftWe'll talk about a safer construct next (not to dissuade you from the ''while'' loop's usefulness!)
+
Now combine the ''while'' loop with the ''if'' statement, and things start to get ''really'' interesting.  OBSERVE AGAIN:
 +
<nowiki>int i = -10
 +
 
 +
while i <= 20:
 +
    if i > 0:
 +
        print i + " is Positive!"
 +
    elif i < 0:
 +
        print i + " is Negative!"
 +
    else:
 +
        print i + " is Zero!"
 +
    i += 1</nowiki>
 +
 
 +
Before proceeding, try to figure out what this script does. Use that new-found programming knowledge!
 +
 
 +
Answer: we basically print the numbers -20 to 20 to the console, and whether or not each of them is positive, negative, or zero.  And, at the very end of the ''while'' loop, rather than assigning ''i'' to itself redundantly, we use what is called a ''compound assignment operator'', which adds to its left-hand variable whatever the right-hand expression evaluates to.
 +
 
 +
Hmm, the above example is looking slightly illegible.  Let's make it a bit more compact, like this:
 +
 
 +
<nowiki>int i = -10
 +
 
 +
while i <= 20:
 +
    if i > 0: print i + " is Positive!"
 +
    elif i < 0: print i + " is Negative!"
 +
    else: print i + " is Zero!"
 +
    i += 1</nowiki>
 +
 
 +
Ah, much better!  In Spellscript, you can compress control-constructs into one-line statements, so long as the inner code-block of the construct is itself a one-line statement.  This sounds limited, unless you realize that statements can all share one line if they're joined together by semi-colons...
  
 
==The ''For'' Loop==
 
==The ''For'' Loop==
Incidentally, the ''for'' loop can be used as a shorthand version of the entire previous construct, like so:
+
===Iterating Over a Range of Integers===
 +
Incidentally, the ''for'' loop can be used as a shorthand version of the ''while'' loop, like so:
 +
<nowiki>for int i in 0 to 10:
 +
    print i</nowiki>
 +
We call this "iteration" over the integers 0 through 9.  Just like a previous example, it creates a variable ''i'', sets it to 0, then executes the inner block of code and increments ''i'' by 1 repeatedly, until ''i'' equals 10.
 +
 
 +
We can make the above example even shorter-- if we omit the first bound, the ''for'' loop will assume a first bound of 0.  The following will print the exact same thing as above:
 
  <nowiki>for int i in 10:
 
  <nowiki>for int i in 10:
 
     print i</nowiki>
 
     print i</nowiki>
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:
+
The ''for'' loop is your friend-- be good to it and it will be good to you!
<nowiki># This is a comment! Anything to the right of a '#' is removed from the script.
+
  
string[] lines = ["Hello", "World", "This", "Is", "Rad", "Yo"]
+
===Iterating Over a List===
for string line in lines:
+
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 can assign a variable to each element of a list in order, ceasing after 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:
     print i
+
<nowiki># This is a comment!  Anything to the right of a '#' is ignored.
 +
 
 +
str[] lines = ["HERP", "ADERP", "ALERP"]
 +
for str line in lines:
 +
     print line
  
 
# The following should be output to the console:
 
# The following should be output to the console:
# Hello
+
# HERP
# World
+
# ADERP
# This
+
# ALERP</nowiki>
# Is
+
# Rad
+
# Yo</nowiki>
+
  
Did the strings get output the terminal? Awesome!
+
Make sure the output of the script was as expected. (See the comments!)
  
<to be continued>
+
You'll notice a new kind of variable above: the ''list''.  ''lines'' in particular is a list of strings.  We specify the type "list of strings" by simply appending ''[]'' after ''str'', and we create the actual list by placing the comma-separated values we want in between square brackets.
  
==Types and Methods==
+
Here's an equivalent variation of the above:
{{mbox | text = You'll need to be in SMP to test out this script! Sorry, we'll try to replace this with a better example soon. }}
+
<nowiki>str[] lines = ["HERP", "ADERP", "ALERP"]
 +
for int i in lines.size():
 +
    print lines[i]</nowiki>
 +
This will print the exact same thing to the console-- the difference is, we used the ''array indexing operator'' (the square brackets) to select each element by its exact location in the list, rather than letting the ''for'' loop do it for us.
  
  <nowiki>if activator is None:
+
You may be inclined to think the above wouldn't work, since ''i'' will iterate over [''0''..''size - 1''], rather than [''1''..''size'']. However, in most programming languages, ''the first element of an ordered list is at index 0, not 1''.  From this, it follows that if a list contains 10 elements, its indices are [''0''..''9''], and more generally, [''0''..''size - 1''].  This is important information, as indexing outside the bounds of the array will cause your program to ''throw an error'' and immediately cease execution.
    return
+
  
if not (activator instanceof Player):
+
<!-- Here's another script to get the juices flowin':
     return
+
<nowiki>str output = ""
 +
str[] lines = ["HERP", "ADERP", "ALERP"]
 +
for int i in 10:
 +
    int rem = i % 4
 +
    if rem < 3:
 +
        output += lines[rem]
 +
     else:
 +
        print output
 +
        output = ""
  
Player player = activator as Player
+
print output</nowiki>
player.tell("Hello " + player.getName() + "!")</nowiki>
+
  
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!!!
+
This will print "HERPADERPALERP" to the console 3 times, followed by a single "HERP".  Essentially, we iterate over [0..9], and check the remainder of ''i'' / 4 every tick (via the 'modulo' operator, '%'). This has the effect of looping ''rem'' over [0..3] while ''i'' iterates -->
  
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''+''/'')
+
===Review and Take a Break===
 +
Now's a good time to take a break and make sure you understand the examples above-- try fiddling around with them and making changes of your own.  (Come on, you know you want to...)
  
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.
+
==Working with Blocks, Calling ''Methods''==
 +
Alright young padawan, you've mastered the way of the ''print'' statement... it is time to start working with blocks.
  
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''.
+
As you may already know, blocks are simply the combination of a ''blockID'' and a ''metadata'' at a given position, integers x, y, and z.  You can actually view your character's position in terms of blocks by pressing ''F3'' outside of any menu.  (See where it says 'x', 'y', and 'z'?)  Go ahead and pick an empty position you'd like to stick a ''tower'' of blocks by using ''F3'', remembering to floor them to integers (e.g. '17.256' becomes '17' and '-45.3' becomes '-46'). Then, type the following into your script-block, replacing 'x', 'y', and 'z' with your chosen coordinates:
 +
<nowiki>for int i in 10:
 +
    world.setBlockID(x, y + i, z, 20)</nowiki>
 +
Give the lever a tug, and WALLAH!  An instant tower o' glass, baby.  Let's explain...
  
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.
+
You'll notice a variable 'world' of type ''World'' that we never declared-- this variable was handed to us by the Spellscript block and contains the world that the Spellscript-Block exists in (the Surface, the Nether, the End, etc)You can find all such variables and their types by bringing up the ''Info Screen'' via ''ctrl''+''/''.
  
Finally, we call a ''method'' belonging to player called "tell", that accepts a ''string'' parameterThis 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!
+
In this script, we simply iterate over the integers [''0''..''9''], and with each iteration call a ''method'' belonging to 'world' entitled 'setBlockID', that does exactly what its name implies: it sets a block at a given position in the world to a given block IDThe position and block are specified by passing the method a few ''parameters'' when it's called: ''x'', ''y'', ''z'', and ''blockID''.
  
Now the script has reached its end, and will finish executionWhew!
+
A method is essentially a predefined snippet of code belonging to a type.  Using the ''dot'' (".") operator, they can be passed a particular set of ''parameters'' and then executed (or "called", as we say in the business)In our case, ''setBlockID'' is a method belonging to ''World'' that accepts four ''int'' parameters: the x, y, and z coordinates we want to place the block at, and the blockID we want to place.
 +
 
 +
Simple enough, right?  Make sure you understand everything in this section before moving on, since it's fixing to get more complicated (and therefore FUN!!!)
  
 
==Polymorphism==
 
==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''.
+
To truly understand methods and types, 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 "inheritance."  You can think of it as a square-rectangle relationship...
 +
 
 +
Suppose there are two types, ''Player'' and ''Living'', and ''Player'' "inherits" from ''Living''.  Just as a square is a rectangle, a ''Player'' is a ''Living''-- however, the converse isn't necessarily true.  That is, just as a rectangle isn't necessarily a square, a ''Living'' isn't necessarily a ''Player''.  <!-- The ''isa'' 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!
+
The nature of this relationship let's Spellscript do useful things, particularly with methods.  If a 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!
+
This is vital information, as it enables you to understand what methods are available on which objects in Spellscript.  To see all of Hack/Mine's available methods in the [[Spellscript IDE]], simply press ''ctrl''+''m''.  You can also find an [[Hack/Mine Methods and Globals|updated list of methods]] here on the wiki.  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>
+
<to be continued...>

Latest revision as of 04:38, 1 September 2014

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

[edit] Your First Spellscript Script

Go ahead and start Minecraft with the console open (instructions), then 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 those are 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, pull 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.

[edit] Another Example, Followed by Explanation

Now let's try something a bit more complicated:

str left = "Hello"
str right = "world!"

print left + " " + right

Now, recompile the script and pull the lever to make sure its still 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) of type str, and one str literal (" "), then stuck them together with the '+' operator.

[edit] Order of Execution

In many programming languages (Spellscript included), statements are executed from top to bottom-- in programmer jargon, this means that Spellscript is an imperative programming language. To elaborate on the above example: first, left is given the value "Hello", then right is given the value "world!", and then finally, the variables are combined and the result is printed to the console.

If the top two lines of code were placed below the print statement, the script would have failed to compile! This is because variables must be declared before they are used (which left and right wouldn't be.) Thus, order of statements is extremely important, and is one of the first hurdles for novice programmers to understand. While in this example the script simply fails to compile, other cases may result in a valid but logically different result.

[edit] Variables and State

Next on the explanation agenda are variables, 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.

A variable is declared like <type> <name>, and its valued is changed with the assignment operator, =, like so:

str left
left = "Hello"

int number
number = 3

The two may be combined, as in the original script, to both declare and assign to the variable in one statement:

int anotherNumber = 200

Here's a simple example to demonstrate how a variable can change:

int number = 0
print number

number = 7
print number

number = 234
print number

If put in a Spellscript-block, the above would have printed "0", "7" and then "234" to the console.

[edit] Undefined Behavior

Currently in Spellscript, using a declared variable before anything is assigned to it results in "undefined behavior." This means nothing is guaranteed about that variable's value; it could be some random value, or something that will crash your script when accessed. While some day Spellscript will validate this and throw a compilation error, it's currently your responsibility to always assign to your variables before using them. Using compound declaration-assignment statements instead of plain ol' declaration statements is a surefire way to avoid this issue.

[edit] The If Statement

Combined with control-statements (such as the if statement, while loop, and for loop), variables allow you to do truly powerful things. Consider the following:

int i = 230
if i > 200:
    print "Yep!"

Simply put, the if statement checks the expression its given, and if true, executes the indented block of code beneath it. So in our case, we first assign 230 to i. Then, we check to see if i is greater than 200: if true, we print "Yep!" to the console!

In its current state, this script will always print "Yep!" to the console; if you change 230 to a value less than or equal to 200, however, this script will never print anything at all... you've now experienced the power of the if statement!

But wait, there's more! You can also chain what are called else and elif statements to the if statement, like so:

int i = 13
if i > 0:
    print "Positive!"
elif i < 0:
    print "Negative!"
else:
    print "Zero!"

print "Done!"

In this script, we first check if i is greater than 0; if so, we print "Positive!" then exit the if-chain; else, we continue down the chain, checking if i is less than 0; if so, we print "Negative!" then exit the if-chain; else, we print "Zero!", then exit the if-chain. In all cases, once we've exited the chain, we print "Done!" and the script is finished. Whew!

The reason we call this an "if-chain" is because you can chain as many elif statements as you like to the end of an if statement (or none at all), optionally including an else statement at the very end, which only executes if all prior expressions in the chain evaluated to false.

Go ahead and experiment with different values for i in the above example, just to convince yourself that it's logically doing what it appears to be: values lower than, greater than, and equal to zero are reported as being negative, positive, and zero, respectively.

[edit] The While Loop

Next we'll explain how to use the while loop. Essentially, once encountered, the while loop will check its given condition; if that condition is true, it executes its inner block of code, then goes back to the last step; otherwise, execution leaves the while loop. Intuitively, if the condition is always false, then the inner block is never executed. In contrast, if the condition is always true, the inner block will be executed infinitely many times (effectively locking up Minecraft.) Thankfully, variables allow us to find a happy medium, letting us execute the inner block a controlled number of times to accomplish some task.

Observe!

int i = 0

while i < 10:
    print i
    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 execute the indented block of code, then go back to the comparison step-- otherwise we cease looping. And in the indented block of code, we simply print i then assign it to itself plus 1! Intuitively, this process will repeat until i equals 10, at which point we exit the loop and hit the end of the script.

Now combine the while loop with the if statement, and things start to get really interesting. OBSERVE AGAIN:

int i = -10

while i <= 20:
    if i > 0:
        print i + " is Positive!"
    elif i < 0:
        print i + " is Negative!"
    else:
        print i + " is Zero!"
    i += 1

Before proceeding, try to figure out what this script does. Use that new-found programming knowledge!

Answer: we basically print the numbers -20 to 20 to the console, and whether or not each of them is positive, negative, or zero. And, at the very end of the while loop, rather than assigning i to itself redundantly, we use what is called a compound assignment operator, which adds to its left-hand variable whatever the right-hand expression evaluates to.

Hmm, the above example is looking slightly illegible. Let's make it a bit more compact, like this:

int i = -10

while i <= 20:
    if i > 0: print i + " is Positive!"
    elif i < 0: print i + " is Negative!"
    else: print i + " is Zero!"
    i += 1

Ah, much better! In Spellscript, you can compress control-constructs into one-line statements, so long as the inner code-block of the construct is itself a one-line statement. This sounds limited, unless you realize that statements can all share one line if they're joined together by semi-colons...

[edit] The For Loop

[edit] Iterating Over a Range of Integers

Incidentally, the for loop can be used as a shorthand version of the while loop, like so:

for int i in 0 to 10:
    print i

We call this "iteration" over the integers 0 through 9. Just like a previous example, it creates a variable i, sets it to 0, then executes the inner block of code and increments i by 1 repeatedly, until i equals 10.

We can make the above example even shorter-- if we omit the first bound, the for loop will assume a first bound of 0. The following will print the exact same thing as above:

for int i in 10:
    print i

The for loop is your friend-- be good to it and it will be good to you!

[edit] Iterating Over a List

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 can assign a variable to each element of a list in order, ceasing after 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 ignored.

str[] lines = ["HERP", "ADERP", "ALERP"]
for str line in lines:
    print line

# The following should be output to the console:
# HERP
# ADERP
# ALERP

Make sure the output of the script was as expected. (See the comments!)

You'll notice a new kind of variable above: the list. lines in particular is a list of strings. We specify the type "list of strings" by simply appending [] after str, and we create the actual list by placing the comma-separated values we want in between square brackets.

Here's an equivalent variation of the above:

str[] lines = ["HERP", "ADERP", "ALERP"]
for int i in lines.size():
    print lines[i]

This will print the exact same thing to the console-- the difference is, we used the array indexing operator (the square brackets) to select each element by its exact location in the list, rather than letting the for loop do it for us.

You may be inclined to think the above wouldn't work, since i will iterate over [0..size - 1], rather than [1..size]. However, in most programming languages, the first element of an ordered list is at index 0, not 1. From this, it follows that if a list contains 10 elements, its indices are [0..9], and more generally, [0..size - 1]. This is important information, as indexing outside the bounds of the array will cause your program to throw an error and immediately cease execution.


[edit] Review and Take a Break

Now's a good time to take a break and make sure you understand the examples above-- try fiddling around with them and making changes of your own. (Come on, you know you want to...)

[edit] Working with Blocks, Calling Methods

Alright young padawan, you've mastered the way of the print statement... it is time to start working with blocks.

As you may already know, blocks are simply the combination of a blockID and a metadata at a given position, integers x, y, and z. You can actually view your character's position in terms of blocks by pressing F3 outside of any menu. (See where it says 'x', 'y', and 'z'?) Go ahead and pick an empty position you'd like to stick a tower of blocks by using F3, remembering to floor them to integers (e.g. '17.256' becomes '17' and '-45.3' becomes '-46'). Then, type the following into your script-block, replacing 'x', 'y', and 'z' with your chosen coordinates:

for int i in 10:
    world.setBlockID(x, y + i, z, 20)

Give the lever a tug, and WALLAH! An instant tower o' glass, baby. Let's explain...

You'll notice a variable 'world' of type World that we never declared-- this variable was handed to us by the Spellscript block and contains the world that the Spellscript-Block exists in (the Surface, the Nether, the End, etc). You can find all such variables and their types by bringing up the Info Screen via ctrl+/.

In this script, we simply iterate over the integers [0..9], and with each iteration call a method belonging to 'world' entitled 'setBlockID', that does exactly what its name implies: it sets a block at a given position in the world to a given block ID. The position and block are specified by passing the method a few parameters when it's called: x, y, z, and blockID.

A method is essentially a predefined snippet of code belonging to a type. Using the dot (".") operator, they can be passed a particular set of parameters and then executed (or "called", as we say in the business). In our case, setBlockID is a method belonging to World that accepts four int parameters: the x, y, and z coordinates we want to place the block at, and the blockID we want to place.

Simple enough, right? Make sure you understand everything in this section before moving on, since it's fixing to get more complicated (and therefore FUN!!!)

[edit] Polymorphism

To truly understand methods and types, 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 "inheritance." You can think of it as a square-rectangle relationship...

Suppose there are two types, Player and Living, and Player "inherits" from Living. Just as a square is a rectangle, a Player is a Living-- however, the converse isn't necessarily true. That is, just as a rectangle isn't necessarily a square, a Living isn't necessarily a Player.

The nature of this relationship let's Spellscript do useful things, particularly with methods. If a 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 in Spellscript. To see all of Hack/Mine's available methods in the Spellscript IDE, simply press ctrl+m. You can also find an updated list of methods here on the wiki. 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...>

Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox