2.1 What means more advanced ?
2.1.1 Creating a Class.
2.1.2 Function Iteration.
2.1.3 Recursive Looping.
2.2 Exclusive Content
2.2.1 WindBot foreach
01-13-2014, 07:10 PM
Leonardo
Introduction
Well, most of you users already know Lua, it's the language behind Tibia, and helps us developing scripts for Windbot, for those who don't know, this language is responsible for all scripts we have in our forum, it serves as a helper for C programs, however, we won't cover this part.
This tutorial is directed for people who never heard about programming languages so I'll keep stuff clear as it's your first programming language.
Before You start...
Quote:
Lua is a powerful, fast, lightweight, embeddable scripting language.
Lua combines simple procedural syntax with powerful data description constructs based on associative arrays and extensible semantics. Lua is dynamically typed, runs by interpreting bytecode for a register-based virtual machine, and has automatic memory management with incremental garbage collection, making it ideal for configuration, scripting, and rapid prototyping.
This is not very important for scripting, but it's cool if you want to follow the computer science and know what you are dealing with.
The Basics
Have you ever know what's a variable ? Well if you didn't, don not worry. It's easy, just imagine an empty refrigerator, you can put food on it, liquids, or even tools if you lost mind, but the same happens with a variable, you can put things on it and remove whenever you want, use them or let them stored.
This is how you declare a variable:
my_variable = 'hell yeah'
The variable 'my_variable' now holds the text "hell yeah". Its type is called string. But how to check a variable type? There's a function for that.
Input:
my_variable = 19
var_type = type(my_variable)
print(var_type)
Output:
#> 'number'
Yes a number, because that's what 19 is. And that print function is to showing us what we've got. I'll show more stuff:
#> false -- pretended to put nothing on that variable, but I'm a liar haha
I've set the local variable Nothing with a value 'I lied', well a local variable cannot be access outside its own scope, it's hard to understand this but let's see another example:
Input:
function Lying()
local Nothing = "LIAR!"
print(Nothing)
end
Lying() -- use my function
print(Nothing)
Output:
#> 'LIAR!' -- printed when I've used the Lying function. 'Nothing' exists!
#> nil -- Nothing is outside the global scope, it's inside Lying function, therefore can't be accessed
Those examples covers most of how we set variables to work. The next chapter will show an example of how they work together.
It's Easy!
Input:
function get_forms_areas(A, B, C) -- creates a function and nomeate the values variables they will assign the values later
-- areas values:
local triangle = (A * C) / 2
local circle = 3.14159 * (C * C)
local trapezium = ((A * C) + (B * C)) / 2
local square = B * B
local rectangle = A * B
local texts = {
-- creating a ['key'] = value based table, assuming the sentences to keys and areas to values:
["The area of the rectangled triangle that has base A and height C:"] = triangle,
["The area of the circle of radius C:"] = circle,
["The area of the trapezium which has base A and B and C by height:"] = trapezium,
["The area of the square that has side B:"] = square,
["The area of the rectangle that has sides A and B:"] = rectangle
}
table.foreach(texts, print)
return -- we did our first script now let's break
end
get_forms_areas(2, 3, 4)
Output:
The area of the circle of radius C: 50.26544
The area of the rectangled triangle that has base A and height C: 4
The area of the trapezium which has base A and B and C by height: 10
The area of the rectangle that has sides A and B: 6
The area of the square that has side B: 9
Whoa!! What happened here ??
We created a serie of mathematical calculations and sent the result as the output, using a function created just for it. The function can be created once, and whenever we need it, all we need to do is call its name, on the example get_forms_areas, also when using functions they can accept internal values or not, in this example the internal values are A, B and C, which can be set after the function call. After the calculations the results were stored on a table and then printed out. The output for that example is right down:
01-13-2014, 07:14 PM
Leonardo
Values and Types
What the hell are those values ?
Basically there are 6 potential types in Lua, they are listed down, with a quick list of features for each one:
Number
This type can hold various number values: 1, 15.0, 25.5.
You can compare them with other number vars using operators: <, >, <=, >=, == and ~= (respectively, less than, higher than, less equal than, higher equal than, equal to, different from), returning true or false for the operation.
You can do calculations with them using other operators such as: -. +, /, * and % (respectively, minus, plus, divided, multiplied and rest of division).
You can index a part of a table or string using it, for example on a table that hold 5 values, if I wanted to access the 3rd value, I would index it by using: MyTable[3].
String
This type can hold any alpha-numeric characters: "abcdef", "123abc", "dafuqisthis{[()]}".
You can compare them with other string vars using operators <, >, <=, >=, == and ~=, returning true or false for the operation, BUT you can't compare a string that way with other type.
Different from numbers, you can't use mathematical operators on strings, that will raise an error, so forget this operators for strings: +, -, /, * and %.
You can index part of a string using a number, in that case you'll use a function from the string library, for example: string.sub("WindBot", 1, 4) will return the characters 1 to 4 on the string, in that case "Wind".
Table
This is a special type in Lua, in can hold the other types inside of it, creating a list of items, it can hold a table element aswell: {{"a", "b", "c"}, {1, 2, 3}, {{}, {}, {}}, true}.
Different from strings and numbers, there's not any operator that can be used with it, with exception of the # (sharp) character, using it on the front of a table will return its size, for example #{1, 2, 3, "a", "b", "c"} would return 6, because there are 6 elements on the table. Key-Value elements are not counted, like {["key"] = 10}.
You can loop trough the table items more easily than the other types, there are 3 types of function loops that we can use: pairs(), ipairs() and key indexing.
Boolean
This kind contains only 2 possible values: true or false.
They are the most basic conditions in any programming language, every comparation you do, creates a boolean value wheter is true or false. Remember: conditionals consider false and nil as false and anything else as true.
You can compare them using "==" or by statements (var = true; if var then ...).
Functions
This is probably the most used type. Functions may return anything else listed above, acting with arguments given inside brackets and resulting on something or just executing some action.
Functions are always come with brackets, inside them is where is located arguments if there are any. Example: print("hello world", "heavy breathing..", "bye world")
Nil
Nil is equal to nothing or null, non existent. It's used to kill a variable, when you assing a variable to a nil value, or to check if some variable exists.
Both nil and false values are taken as false on a statement, but beware comparing these values using "==", that would just check if it's really equal to nil/false and not both of them as false.
WindBot Types
Among other Lua types there's some exclusive in WindBot, also called pointers:
WindBot Pointers
This type is similar to a table, it holds information about something and should be indexed for the value we want, for example, creature pointer hppc (creature.hppc) would return the health percent of a creature.
Some functions accept this type as a parameter, like attack can receive a creature name, ID or pointer for a creature type.
In WindBot, indexing a pointer with a non existent member of its type will trigger an error, saying it wasn't found. To know if the type has a specific index, use the hasproperty index.
All the types can be found in the sub section Types, in the documentation page.
Conditions and Loops
Now that you have knowledge about types, let's see how conditions and loops work:
Input:
-- A do block provides scoping
do
local foo = 'Hello'
print('Inside the do block, foo is: ', foo)
end
print('Outside the do block, foo is: ', foo)
-- if then else
local kittens = 1
if kittens > 0 then print('You have kitten(s)') end
if kittens == 0 then
print('You have no kittens')
elseif kittens == 1 then
print('You have a kitten')
else
print('You have many kittens')
end
-- while
local kittens = { 'Mr Tibbs', 'Tufty', 'Kipper' }
while #kittens > 0 do
local kitten = table.remove(kittens, 1)
print(kitten)
end
-- repeat until
local kittens = { 'Mr Tibbs', 'Tufty', 'Kipper' }
repeat
local kitten = table.remove(kittens, 1)
print(kitten)
until #kittens == 0
-- break
local kittens = { 'Mr Tibbs', 'Tufty', 'Kipper' }
while #kittens > 0 do
local kitten = table.remove(kittens, 1)
if kitten == 'Tufty' then break end
print(kitten)
end
-- numeric for
for i = 1, 10 do -- count up
print(i..' banana')
end
for i = 10, 1, -1 do -- count down
print(i..' green bottles')
end
print('i is scoped to the for loop: ', i)
-- generic for
local random = { 'boot', foo = 'bar', 22 }
for key, val in ipairs(random) do -- ipairs iterates over numerical indexed elements only.
print(key, val)
end
for key, val in pairs(random) do -- pairs iterates over all elements.
print(key, val)
end
Output:
Inside the do block, foo is: Hello
Outside the do block, foo is: nil
You have kitten(s)
You have a kitten
Mr Tibbs
Tufty
Kipper
Mr Tibbs
Tufty
Kipper
Mr Tibbs
1 banana
2 banana
3 banana
4 banana
5 banana
6 banana
7 banana
8 banana
9 banana
10 banana
10 green bottles
9 green bottles
8 green bottles
7 green bottles
6 green bottles
5 green bottles
4 green bottles
3 green bottles
2 green bottles
1 green bottles
i is scoped to the for loop: nil
1 boot
2 22
1 boot
2 22
foo bar
Ok, there's more than you've had learned on the lesson, but try to figure out what each line is and what they represent as an output. I'll be covering control statements soon.
Your first script
Instead of downloading Lua, you'll use an online editor and compiler, which you can easily use to write any code that don't need Windbot functions. Use the link down and go for it, just wait a little after you open it to make sure it opened correctly.
What means advanced ?
It means now we're going to have all the stuff learnt in the past chapters mixed with WindBot and some new tricks.
Creating a Class
This one could be hard but it's an important tool in some cases, and it also should be easier to use.
Input:
MyClass = {} -- a table
MyClass.__index = MyClass -- this is necessary for classes
-- as it enables the methods for it
-- setting the definition of each function for our new class
-- this function just create our class and return it
function MyClass.New()
return setmetatable({
--scriptversion = '',
}, MyClass)
end
-- this function gives our script a version
function MyClass:setVersion(str)
self.scriptversion = str
end
-- this function returns our script version or an empty string if it wasn't created yet
function MyClass:getVersion()
return self.scriptversion
end
-- now let's use our simple functions
local Script = MyClass.New()
-- instead of MyClass.Function() we can now index as Script.Function()
print(Script:getVersion())
-- why use : you should be guessing, we'll talk about that later
Script:setVersion("1.2.0")
print(Script:getVersion())
Output:
nil
1.2.0
But how does that work ? It's not so simple as anything we saw before, anyway, it's not so complicated.
This is called methamod tables, they have a different behaviour than the common tables.
We're first creating a class function, called New, using the setmetatable function.
That will return our class as a pack and insert into a new variable in the future. Then we're creating the other functions getVersion and setVersion, the last being used to give our class a version, the other used to pick and return the version.
When using metaclasses we can also index using the ":" symbol, because the class was inserted into our variable before, so we can't use MyClass.Function() anymore, that would just return the default value.
There are some good examples for classes in some of the libraries in WindBot, sirmate's library is fully made in Classes, also Raphael's library is partially made of classes (HUD and JSON classes). Other examples are the basic classes in Lua, string, math and io.
Function Iteration
This is usually used to create a loop like ipairs/pairs functions, but in other environments.
Input:
-- copied from Leonardo's library (very edited)
-- first declare our function
function screentiles()
local Positions = {}
for x = -7, 7 do
for y = -5, 5 do
local _x, _y = $posx + x, $posy + y
if tilehasinfo(_x, _y, $posz) then
table.insert(Positions, {_x, _y, $posz})
end
end
end
-- this is what we want
local i = 0
return function()
i = i + 1
if Positions[i] then
return Positions[i][1], Positions[i][2], Positions[i][3]
end
return
end
end
-- using our new loop
for x, y, z in screentiles() do
addwaypoint(x, y, z, "stand")
end
Output:
-- no output
This will add a stand waypoint for all the positions visible on your screen. How does that loop work ?
First we do a loop, inside the function, and get all the positions axis and store them in the var Positions. Then we start a numeric loop using a function, this is possible in Lua, because the interpreter will run this as a loop and not a function that is called and used. We use the i value to index the table, returning our 3 positions axis, x/y/z, then all we need to do is return them.
Recursive Looping
This is called recursive, because we call the function we're creating inside itself in determined situations. This makes the script shorter than using other iteration.
Input:
local timenow = os.clock()
function factorial(num)
return num > 1 and factorial(num - 1) * num or 1
end
print("Recursive way: " .. factorial(11))
print("Took " .. os.clock() - timenow .. " ms to run.")
-- common loop way
local timenow = os.clock()
local result = 1
for i = 2, 11 do
result = result * i
end
print("Loop way: " .. result)
print("Took " .. os.clock() - timenow .. " ms to run.")
-- actually is pretty hard to benchmark lua scripts as they run fast, so the results for this script are 0
Output:
Recursive way: 39916800
Took 0 ms to run.
Loop way: 39916800
Took 0 ms to run.
Recursive loops could be usefull but it doesn't run faster than common iteration in Lua, in some other languages it's slower or faster, depending on the environment. However it's shorter (assuming that the recursive function is already implemented).
Exclusive Content
The next stuff are similar to what we've seen before but it's exclusively to WindBot.
WindBot foreach
This is used to loop between some variables, unlike table.foreach, this can only receive specific variables to loop for, while the other is used to loop between any table, however they have the same engine. And sometimes there's a parameter to filter results.
Input:
foreach creature cre "ps" do
if cre.name ~= $name then
while paround(cre.name) > 0 do
beep()
wait(500)
end
end
end
Output:
-- no output
This is a simple code to alarm if some player is in your screen, using foreach together with creature will loop over creatures with the given filter, this filter is "ps". In this specific case, "ps" is used to find players visible on screen. The creature in each loop round will be stored at the var cre, this cre is a creature type variable (check Chapter 2: WindBot Types for more info).
This example shows how to deal with foreach creature, but you can also use for other types, for a complete list check the documentation Iterators section.
03-23-2014, 04:09 AM
Leonardo
Well, this tutorial was stopped for a long time and now I'll be back on it again. But to let you try some of the stuff that are included on this tutorial, I'll be posting parts of it until I can finish.
If you have some doubt relationed with it, don't be shy and ask for it.
03-23-2014, 01:24 PM
mistgun
I just waited for this bro, greatest tutorial! Great job :D
03-23-2014, 02:21 PM
Imba
So basic stuff, but i hope it will help out people on forum. Guys, with this tutorial you have no excuse that you dont know what programming is and you cant start making scripts ; )