• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[Lua] Writing shorter scripts

Colandus

Advanced OT User
Senator
Joined
Jun 6, 2007
Messages
2,434
Solutions
19
Reaction score
218
Location
Sweden
These are my old tutorials, that have gone through several forums and now it's time I put it here at OtLand!

This tutorial is about how to write shorter scripts. I'll show you some tricks... So let's start.


First of all, if we wanted variable x to be minimum 10, instead of doing this:
Lua:
x = 7
if x < 10 then
     x = 10
end
You do it this way:
Lua:
x = 7
x = math.max(x, 10)

And the same thing with this, if we want variable x to be maximum 20:
Lua:
x = 23
if x > 20 then
     x = 20
end
You do it this way:
Lua:
x = 23
x = math.min(x, 20)

But if you wanted to make both of these restrictions, you'd do it this way:
Lua:
x = 23
if x < 10 then
     x = 10
elseif x > 20 then
     x = 20
end
You do it this way:
Lua:
x = 23
x = math.min(math.max(x, 10), 20)
or, this way (same result):
Lua:
x = 23
x = math.max(math.min(x, 20), 10)

What is math.max and math.min? Simple...

math.min returns the lowest value:
Lua:
math.min(10, 20) -- 10 is lowest, it returns 10.

math.max returns the highest value:
Lua:
math.max(10, 20) -- 20 is highest, it returns 20.


Now, we're done with that. Next thing:
Lua:
if x == 10 then
     y = 100
     b = 50
     xybz = x + y + b + 35
elseif x == 20 then
     y = 100
     b = 50
     xybz = x + y + b + 110
else
     y = 30
     b = 50
     xybz = x + y + b + 20
end

This could be shorten down to:
Lua:
y = 100
b = 50
if x == 10 then
     z = 35
elseif x == 20 then
     z = 110
else
     y = 30
     z = 20
end
xybz = x + y + b + z

Once, when I helped a friend with his server, he sent me a script on 500 lines, and told me to shorten it. Guess how many lines remained? You won't belive it... 170!

I tell you what he did, and how I solved it:
Lua:
if x == 10 then
     y = 100
     b = 50
     xybz = x + y + b + 35
elseif x == 20 then
     y = 40
     b = 78
     xybz = x + y + b + 76
elseif x == 30 then
     y = 80
     b = 50
     xybz = x + y + b + 45
elseif x == 40 then
     y = 100
     b = 666
     xybz = x + y + b + 144
elseif x == 50 then
     y = 33
     b = 45
     xybz = x + y + b + 12
else
     y = 100
     b = 100
     xybz = x + y + b + 20
end
But obviously, he had much more code in each if-statement and not only variables, lots of functions which matched eachothers but just different values on them. Only sometimes, in few statements there were extra values, but that's no problem :) Because it was more stuff in each statement, it makes it more effective to shorten it down. We'll be doing it this way:
Lua:
function numbers(x, y, b, z)
     return (x + y + b + z)
end

if x == 10 then
     xybz = numbers(x, 100, 50, 35)
elseif x == 20 then
     xybz = numbers(x, 40, 78, 76)
elseif x == 30 then
     xybz = numbers(x, 80, 50, 45)
elseif x == 40 then
     xybz = numbers(x, 100, 666, 144)
elseif x == 50 then
     xybz = numbers(x, 33, 45, 12)
else
     xybz = numbers(x, 100, 100, 20)
end
It wasn't the best example, but I think you understand what the point is. If you're going to do something which is almost all the same, just use a function and you'll use less code.

And, if you wish to make it even shorter, you can do it this way:
Lua:
results = {
    [10] = {100, 50, 35},
    [20] = {40, 78, 76},
    [30] = {80, 50, 45},
    [40] = {100, 666, 144},
    [50] = {33, 45, 12},
    default = {100, 100, 20}
}
result = results[x] or results.default
xybz = x + result[1] + result[2] + result[3]

Don't you know arrays? Click the link below (Loops Tutorials, also contains array tutorials). About that "or", it will come soon, scroll down and there it is :)

But that wasn't also all I did, I looped some stuffs... If I remember correct, he were summoning stuff... I used arrays and loops.
[Loops Tutorial]
Lua:
     doSummonCreature("Rat", pos)
     doSummonCreature("Rat", pos)
     doSummonCreature("Rat", pos)
     doSummonCreature("Troll", pos)
     doSummonCreature("Troll", pos)
     doSummonCreature("Orc", pos)
Not the longest script, but it's smart to make a loop anyways.
Lua:
     local summons = {["Rat"] = 3, ["Troll"] = 2, ["Orc"] = 1}
     for k, v in pairs(summons) do
          for i=1, v do
               doSummonCreature(k, pos)
          end
     end
Also take a look at the arrays tutorial.

If you were using different than 1 position, you wouldn't do it exactly this way, but the thing about this, is that you should be able to figure your own ways, not ONLY taking other persons ways :)

Here is something I did on a script that shall give you all the addons:
Lua:
sexAddons = {
    {[136]=142, [147]=150, [155]=158, [252]=252},
    {[128]=134, [143]=146, [151]=154, [251]=251}
}
local addons = sexAddons[getPlayerSex(cid)+1]
if (addons ~= nil) then
    for k, v in pairs(addons) do
        for i=k, v do
            doPlayerAddOutfit(player, i, addonType[1])
        end
    end
end
It's quite simple, what I did was "[start]=end", so that means, if you're a female (sex 0) you'd get the array key 1, so it would first loop between 136 and 142, then 147 and 150 etc... Get it?

If not, check this:
Lua:
numbers = {
    [10] = 30,
    [55] = 65,
    [80] = 140
}
for start, stop in pairs(numbers) do
    for number=start, stop do
        print(number)
    end
end
Got it? First it will loop trough "numbers", to get the 10 = 30 etc, and when it got that, it will loop from 10 to 30 and print the numbr and continue until 80 = 140! :)

Okay, and now the way to avoid "nil"...
Lua:
function doSomething(x)
     if x == nil then
          x = 1
     end
     return x
end
Well... What's wrong with this script? Nothing actually... But this could be done much easier:
Lua:
function doSomething(x)
     return x or 1
end
Hmm... What the...?
It's quite simple actually... If x is nil or false, then it takes the other one...

Another example:
Lua:
x = 5
y = 10
z = x or y or 5
Okay... Now this is way too much! I'm outta here!
Well... This is simple too :p First it checks if x is nil, if it is, then it checks if y is nil. Now, both is nil, so z becomes 5.

So that means, if x is NOT nil "z = x", but if it is nil then "z = y" but if y also is nil "z = 5"!

An example, this will NEVER return nil! :p
Lua:
function sum(a, b)
     return (a or 0) + (b or 0)
end
Usage:
Lua:
print(sum(5, 10))
print(sum(5, nil))
print(sum(nil, 10))
print(sum(5))
Result:
Lua:
15
5
10
5

If you want to print a message saying it is wrong, like this:
Lua:
if x == nil then
     if y == nil then
          print("Error, all values are nil!")
     else
          z = y
     end
else
     z = x
end
To simplify it:
Lua:
z = x or y or print("Error, all values are nil")
Now, if x is nil, z will become y and if y is nil, it will print "Error, all values are nil!" BUT, z will be nil! So, how to print the message AND give z a value? Look at this:
Lua:
z = x or y or print("Error, all values are nil") or 0
By adding "or 0", it will print the message AND z will be 0!

Here is another, "real" example, of the usage of "or":
Lua:
function onSay(cid, words, param)
     param = tonumber(param)
     vocations = {"Sorcerer", "Druid", "Paladin", "Knight", "Master Sorcerer", "Elder Druid", "Royal Paladin", "Elite Knight"}
     if vocations[param] ~= nil then
          vocation = vocations[param]
     else
           vocation = "Unknown"
     end
end
Let's say the talkaction is !vocation "number, and you write !vocation "5, then "vocation" would become "Master Sorcerer", but if you wrote !vocation "10, then "vocation" would become "Unknown", why? Because it is not in the array. A shorter way to do this:
Lua:
function onSay(cid, words, param)
     param = tonumber(param)
     vocations = {"Sorcerer", "Druid", "Paladin", "Knight", "Master Sorcerer", "Elder Druid", "Royal Paladin", "Elite Knight"}
     vocation = vocations[param] or "Unknown"
end

If you want to check if several variables are nil together, you do it this way:
Lua:
-- We say, this is positions, and we want all positions to exist.
if x and y and z then
     pos = {x=x, y=y, z=z}
end

You could also do stuff like this with this:
Lua:
(a and b) or (c and d) or e
(a and b) returns "b", if not nil. When using the "and" thingy, it's like "if a == true then return b".

So, this:
Lua:
result = (a and b) or (c and d) or e
Is the same as:
Lua:
if a then
    result = b
elseif c then
    result = d
else
    result = e
end

The expression: "if a then" is the same as "if a == true" or "if a ~= nil and a ~= false"!

Here is another smart example to make shorter scripts, if you're using tonumber.
Lua:
function position(x, y, z, stackpos)
    if ((x and y and z) and type(tonumber(x..y..z)) == 'number') then
        pos = {x=x, y=y, z=z}
        if (stackpos and type(stackpos) == 'number') then
            pos.stackpos = stackpos
        end
        return pos
    end
    return false
end
I suppose you already understand what the (x and y and z) does, if you don't, go back and read it again. But in this script, the main point was: type(tonumber(x..y..z)).
You probably wonder what's so special with it...? I'll tell you what:
Lua:
type(tonumber(x) == 'number' and type(tonumber(y) == 'number' and type(tonumber(z) == 'number'
Is the same as:
Lua:
type(tonumber(x..y..z)) == 'number'
How can it be?! I'll tell you how:
Lua:
x = "134"
y = "abc"
z = "7"
print(tonumber(x))
print(tonumber(y))
print(tonumber(z))
print(tonumber(x..y..z))
The result of this code:
Lua:
134
nil
7
nil
Why? Because tonumber does only accept numbers, if you're using tonumber() on a text, even if it contains a number, it will return nil. Therefore, you can be smart and add them all, like tonumber(x..y..z) instead. If you don't know how to concatenate text eachothers, I can't tell you in here, cuz that doesn't really match to the topic :p

Now, instead of this long script:
Lua:
if (x > z) then
     r = x
else
     r = z
end
You could simply write:
Lua:
r = (x > z and x) or z
Hmm... What is this? As I told you, when you write like "1 and 2 and 3", it will return the last defined, in this case "3", therefore I write "x > z", if that is true, it will also write "and x", which makes the last one to x, so r will become x, but if "x > z" is false, then it will go to the "or z", which makes r to z!

Note that, this does NOT mean: "x > z and x > x". But normally, you'd just use "r = math.max(x, z)" in this case.

Another example:
Lua:
if (type(x) == "number" and type(y) == "number") then
     r = math.max(x, y)
else
     r = 0
end
Could be replaced by:
Lua:
r = (type(x) == "number" and type(y) == "number" and math.max(x, y)) or 0

Or even more advanced:
Lua:
if (type(x) == "number" and type(y) == "number") then
     r = math.max(x, y)
elseif (type(x) == "number") then
     r = x
elseif (type(y) == "number") then
     r = y
else
     r = 0
end
This more longer code, could be replaced by the following code:
Lua:
r = (type(x) == "number" and type(y) == "number" and math.max(x, y)) or (type(x) == "number" and x) or (type(y) == "number" and y) or 0

Well, I never told you to always do it this way, since it's harder for few to read this. I myself find it easy to read them, but most doesn't. So if you want a readable code, stick to normal if-statements in THIS case. Few cases really needs this "boost" :)

Just wrote a function I'd like to give as an example, but then after few changes, it didn't match to the topic, anyways, you could check it out here: Another Tutorial


Also, if you think anything I do is wrong, or if you know other/better ways, please post them here!

All the best,
Colandus
 
Last edited:
Tbh.... This is an awesome guide for peoples that have problems with long scripts :p
Something I found handy was
Lua:
     doSummonCreature("Rat", pos)
     doSummonCreature("Rat", pos)
     doSummonCreature("Rat", pos)
     doSummonCreature("Troll", pos)
     doSummonCreature("Troll", pos)
     doSummonCreature("Orc", pos)

which can be done:
Lua:
 local summons = {["Rat"] = 3, ["Troll"] = 2, ["Orc"] = 1}
     for k, v in pairs(summons) do
          for i=1, v do
               doSummonCreature(k, pos)
          end
     end

Gonna give you rep for this "suber" tutorial!
 
Yes that could be very handy... I've shortened lots of scripts using that, it's those kinds of scripts which always can be shortened most! Usually from about 600~ to 70~ lines, which is a huge deal and makes it very easily editable! Thanks for reading, hope you learnt from it :)

Although sometimes it's not only like that:
Lua:
     doSummonCreature("Rat", pos)
     doSummonCreature("Rat", pos)
     doSummonCreature("Rat", pos)
     doSummonCreature("Troll", pos)
     doSummonCreature("Troll", pos)
     doSummonCreature("Orc", pos)

Mostly it's something like this:
Lua:
if(value == 15) then
  doSummonCreature("Rat", pos)
  doSummonCreature("Rat", pos)
  doSummonCreature("Rat", pos)
  doSendMagicEffect(pos, CONST_ME_MAGIC_BLUE)
--  ~~ some code here
elseif(value == 18) then
     doSummonCreature("Troll", pos)
     doSummonCreature("Troll", pos)
     doSendMagicEffect(pos, CONST_ME_MAGIC_RED)
--   ~~ some code here (although same as above had)
end -- AND ALL OTHER creatures things goes on

then you'd still do the same thing, but like this:
Lua:
local summons = {
    [15]  = {"Rat", 3, CONST_ME_MAGIC_BLUE},
    [18] = {"Troll", 2, CONST_ME_MAGIC_RED},
    -- other monsters here
}

local data = summons[value]
if(data) then -- maybe it's not in array for some reason... so we gotta be sure
    for i=1, data[2] do
         doSummonCreature(data[1], pos)
    end
    doSendMagicEffect(pos, data[3])
    -- some code here (that will be same for all of them... e.g. tp the player or whatever hehe)
end
 
Last edited:
This will be really useful for newbie scripters :) and help for more structured community releases. Thanks Colandus! Rep++
 
Where's the like button? <oh jeez> I forgot, this ain't youtube.. Anyway, good job ;)
 
Add this too :D

Syntax sugar:
Code:
msg = "omg"
msg = string.gsub(msg,'m','-')
print(msg)
--> o-g
With syntax sugar:
msg = "omg"
msg = msg:gsub(msg,'m','-')
print(msg)
Lower
print(("omg"):gsub(msg,'m','-'))
 
u mean ("omg"):gsub('m','-') ? :d

well it doesnt really shorten much, i would call that something else than shortening xDD but thanks anyway for trying to help ! :D It seems though only people who does know how to script checks this thread out :(
 
What is i want do something like this:

Code:
local npcPos = getCreaturePosition(getNpcCid())
local playerPos = getCreaturePosition(cid)

         doSummonCreature("Orc Leader", {x=npcPos.x-1,y=npcPos.y,z=npcPos.z})
	  doSummonCreature("Slime", {x=npcPos.x+1,y=npcPos.y,z=npcPos.z})
	  doSummonCreature("Orc Warlord", {x=npcPos.x,y=npcPos.y+1,z=npcPos.z})
	  doSummonCreature("Orc Warlord", {x=npcPos.x,y=npcPos.y-1,z=npcPos.z})
          doSummonCreature("Orc Leader", {x=npcPos.x+1,y=npcPos.y+1,z=npcPos.z})
	  doSummonCreature("Slime", {x=npcPos.x-1,y=npcPos.y-1,z=npcPos.z})
          doSummonCreature("Slime", {x=npcPos.x+1,y=npcPos.y-1,z=npcPos.z})
          doSummonCreature("Orc Leader", {x=npcPos.x-1,y=npcPos.y+1,z=npcPos.z})
 
Back
Top