Before you start, please note that this document is:
- *not* a Lua tutorial (Lua is the language use for scripting in U61). Of course at the end of this document I hope you'll be able to do some basic Lua scripting since Lua is quite quick to learn, but if you want to do advanced Lua coding, go and check the official sources. I'm myself not a great Lua programmer so I won't be able to help you a lot anyway.
- *not* a complete reference of what is possible to do with U61. It only shows how to get started with script writing, but if you really want to do complicated things, please read the rest of this site.
However, it should help you write your first script lines, and I have good reasons to think these are the hardest to code.
You do not need any special programming skills before reading this document. If you are curious and wish to learn how to toy with u61 it should be enough, provided that you know at least how to move, copy and edit files on your system. Of course it makes things easier to know about programming, but honestly Lua is not a difficult language to start with.
Step 1: create a script file
Well, let's not reinvent the wheel for instance, we'll just copy an existing file. Writing a complete file from scratch might indeed be too hard for a start.
If you got an "official" U61 package (from http://www.ufoot.org for instance) you already have some scripts that came with the game. By the time I write this document, only the UNIX platform is supported, so the script files should be in "/usr/share/script/u61" or "/usr/local/share/script/u61" or in the directory where you installed U61.
Now copy "/usr/local/share/u61/script/classic.lua" into "~/.u61/script/test.lua".
If now you launch U61, and go to "Game options / Rules" you should see "test" appear in the list, use the arrows to move the selection on it, and the start a new game. You should have the impression to play with the "classic" rules, but in fact you are using the "test.lua" script.
Step 2: create an error
OK, now scripting allows lots of liberty in programming. One of the basic consequences for U61 is that you can code almost anything in a script, you are pretty sure you won't get a protection fault. In fact the worst thing that can happen (and it does happen) is an infinite loop. The result is that the game hangs and you have to kill it.
So what I propose is to deliberately create an error in the script and see what happens. This will help you deal with errors later. So just edit your brand new test.lua script file with your favorite editor (vim and GNU Emacs seem to work good but I would never mean to influence you in the choice of the editor) and go to, say line 93. There you just add the following text:
Now relaunch U61, which should have kept test.lua as your default script, and you should get nothing on the player(s) map(s) but instead a message on the console (provided that you have launched U61 from a terminal) that says:
lua error: unexpected token; last token read: `\' at line 93 in file `/home/ufoot/.u61/test.lua' Lua error reading script, code is 1.
And this is it. You have the line number of the error and a short description of it. This should help debugging.
It is not necessary to quit and re-run completely u61 before trying out a new script. Indeed the script is reloaded from the disk each time you start a new game (with "Quick start" for instance). So all you need to do is:
- Launch U61
- Select your script with "Game options / Rules"
- Start a game with "Quick start", view what happens.
- Edit your file, save it.
- Re-start the game with "Escape / Stop this game / Quick start" and view how your modifications influence the gameplay.
As a start, just remove the faulty text I just told you to add from the script, same it and re-start a new game without quitting U61. The error should disappear.
Step 3: learn Lua basics
As I said before, this document is not a Lua reference, and it won't replace the reading of the official manual, which is available on this site anyway. Still, you do not need to be a Lua wizzard to tweak U61's scripts, as I'm not myself a very bright Lua programmer. So this short passage is just to give you a quick start.
In U61, I essentially use Lua functions, there are other aspects of Lua programming than functions, but I think functions are easy to understand, so I use them. To declare a function, do like this:
function my_function(par1,par2) ... end
As you see, it's easy to declare a function, you might use this function elsewhere by calling:
Within a function, you can declare local values. Local values have a scope which is limited to the function where they are declared. Any function may return a value, here's an example:
function calculate(a,b) local temp temp=a+b return temp end
As you may have guessed, this function performs an addition, you can call:
and the value of "c" will be 3.
Lua has some common controls like "if...then" or "while...do". Here is a short example:
if val==0 then result="zero" elseif val < 0 then result="negative" else result="positive" end
And another one:
while i>0 do result=result*2 i=i-1 end
To compare numeric values, you can use the operators:
- == (equals)
- ~= (different)
- >= (greater or equal)
- <= (lower or equal)
Now if you have already programmed in another language like C, Pascal or Basic you know that almost as much as I do about Lua. I can tell you it's enough to start hacking.
Step 4: Try to understand the existing scripts
OK, now we know how to modify a script randomly and generate errors. Great, but you might want to do more than that! Let's start by a - very - short analysys of our test.lua script file.
"user_rotate_left" is called when the rotate left key is pressed. In U61 0.2.2 the code for user_rotate_left for the classic.lua script is:
function user_rotate_left() rotate_block_left() end
This means the action to perform when "user_rotate_left" is called is to call "rotate_block_left". So what's the difference between "user_rotate_left" and "rotate_block_left"? Well, they do exactly the same but "user_rotate_left" is a reserved name, this function being what I call a "user callback", since it's called by the C++ engine and it's the responsibility of the scripter to code it correctly. "rotate_block_left" is totally unknown from the C++ engine, it's just a function I created because I found it convenient to have a function that rotates a block already coded and usable elsewhere in the script. If we look at the code of "rotate_block_left", we find:
function rotate_block_left() local x local y local i i = u61_get_nb_items()-1 while i>=0 do x = u61_get_item_x(i) y = u61_get_item_y(i) x,y = y,-x u61_set_item_x(i,x) u61_set_item_y(i,y) i = i-1 end end
Well, we won't detail the way it works, but we'll still notice that it uses the functions with a name like "u61_fonction_name". These functions belong to what I call the "system api". It's a set of functions which allow the user to query and modify U61's world. For instance "u61_get_nb_items" returns the size of the current block, which with the classic rules is generally 4. But beware, it could be more if you had a nasty curse, and in a general manner, it's good to write scripts that are usable in various situations.
Step 5: Toy arround
Now, we'll as a first example change our "test.lua" file so that when the user rotates a block, the block goes down of one square. This will forbid abusive rotation of the block and make the game harder.
Just edit test.lua and find the code for "user_rotate_left" and "user_rotate_right". It should look like:
function user_rotate_left() rotate_block_left() end function user_rotate_right() rotate_block_right() end
Just replace this by:
function user_rotate_left() rotate_block_left() u61_set_block_y(u61_get_block_y()+1) end function user_rotate_right() rotate_block_right() translate_block_y(1) end
And it should work! You'll notice that the 2 functions are coded differently. With "user_rotate_left" I used the U61 system API directly, whereas with "user_rotate_right" I used an Lua function which encapsulates the calls to the system API. I personnally find it a good idea to make very small functions but you are free to do whatever you want. But I believe some functions such as the user callbacks should be as small as possible, for in the long run it should make the sharing and merging of script files easier.
To be continued...
Well, that's all for now, I hope to enrich this document soon.