Lua Tips & Tricks

https://dl.dropboxusercontent.com/u/50211087/images/shareyourlua.png

While the wiki is useful when searching for functions, it doesn’t really teach good programming habits, or smart ways to do things.
So, let’s have a thread where we share our tips and tricks!

It doesn’t have to be purely lua code tips, it could also be things like:
** • general programming tips
• useful math equations
• alternate ways to do something
• and anything else you’ve picked up, that you think could be useful to others.**

**Try to provide some sort of code example with your tips (if possible), since that’s what this thread is about! **

P.S: Don’t be afraid to post things that are simple and obvious, because it may not be as obvious to new coders.

Looking forward to seeing what you will share with the community! :joy:

I’ll get the thread started with a few tips and tricks I’ve picked up:

[sp](I have experience in other languages, but I’m still fairly new to lua, so correct me if anything’s wrong)[/sp]


1. Toggle a boolean between True and False


myBool = !myBool

**2. Make positive numbers negative, and negative numbers positive **


myNumber = -myNumber

3. Tween any number towards 0, and stop at exactly 0


if myNumber != nil and myNumber != 0 then 
        myNumber = myNumber - math.Clamp( myNumber, -0.5, 0.5 ) 
end

//Change -0.5 and 0.5 with how much you want the number to raise/lower each time the code is being called.
//Since this needs to happen over time, you should put it in a Think function (or any other function that happens over time)


4. Getting the angle between 2 vectors:


myAngle = ( targetVector - startVector ):Angle()

//This will create an angle aiming in a line from startVector towards targetVector.

5. Smarter validity checks without getting lua errors:
Example: we want to check if our table exists and if it contains a value, [sp](table is just an example, could be something else)[/sp]
so we COULD do something like this:


if !myTable then return false end
if !table.HasValue( myTable, "candy" ) then return false end
print("Table exists and has candy! Yay!")

Stop! Why use multiple seperate if-statements when you can just use 1?


if !myTable or !table.HasValue( myTable, "candy" ) then return false end
print("Table exists and has candy! Yay!")

Q: Wait a minute… Won’t that give us an error if the table doesn’t exist and it tries to check the table…?
A: Nope! Because it will bail out before reading that far into the code!
That’s because the program will try to read as little code as possible, starting from left to right. *(so if you started with the table.HasValue check, you could get an error)
*
Basically it does this:
“Oh, I need to check if any of boolean1 OR boolean2 are true…**”
Outcome 1: “I see, boolean1 is true, which means I don’t need to read boolean2 to know that any one them are true.”
**Outcome 2: "I see, boolean1 is false, so I must check if boolean2 is true instead"

(a thing to remember is that “not variable” or "**!**variable" will actually return a boolean with the value true when “variable” is false or doesn’t exist. Just though I’d remind in case anyone was confused by the weird talking lua example above)

This isn’t restricted to just if-statements. Another example:


// set mySpeed to either 300 or 150 depending on whether myEntity is running or not
mySpeed = ( IsValid(myEntity) and myEntity.isRunning ) and 300 or 150

Which brings me to…

6. Setting a variable depending on whether something is true or false:


myVariable = myBoolean and 5 or 0
// myVariable will become 5 if boolean is true, and it will become 0 if boolean is false

another example on the same thing:


myString = (powerLevel > 9000) and "It's over 9000!!!!" or "it's not over 9000.."
print(myString)



More code examples in this thread:
**
7. Convert velocity to KM/H and MPH

8. Convert numbers to a percentage

9. Performance boost

10. Lua garbage tracker (check if your scripts are leaking)

11. Creating a function with optional parameters

Scroll down the thread for even more tips and code examples

**

Protip: Validate everything, and never trust the client.

When you’re coding a script, you must always think that the client wants to exploit your script. Check changes on variables between server/client and try to not make the client send important data to the server :slight_smile:

If you’re doing any kind of vgui stuff at all, make sure to use

Panel:Dock,

Panel:DockMargin and

Panel:DockPadding as much as possible so you don’t want to kill yourself afterward

EDIT: At least if you don’t want to spend about 5 hours manually positioning a bunch of panels and then watching your careful positioning screw up on different screen resolutions or if you make any of the panels bigger


local number = -number

Does NOT make it negative and will cause error.
If you want to make a postive number negative and vice versa you’ll have to multiply it with a negative 1


local number = number * -1 

[editline]21st January 2016[/editline]

Nvm i was wrong, the first works too.
Why rate when I’ve acknowledge I had it wrong. Both works.

When a table has numeric indices and no holes remember to use an ipairs for loop or numeric for loop.

And don’t forget to make variables that don’t need to be accessed globally local. That includes functions too.

:snip:

Remember that code examples will be really helpful for those new to lua coding, so everyone no matter coding experience can understand how and where to apply the tips

I can only try to translate this tip into code, but please correct me if I misunderstand it:


-- a table with numeric indices and no gaps:
local myTable = {}
myTable[1] = "Hello"
myTable[2] = "World!"
myTable[3] = "I like trains"
myTable[4] = "Me too bro"
--myTable[10] = "woot"                <-- if this line was here, there would be a "gap" between index 4 and 10


--using a for loop with ipairs
for i, n in ipairs( myTable ) do
    print( "Index " .. i .. " contains the value: " .. n )
end


--Using a numeric for loop
for i=1, 2 do
    print( "Index " .. i .. " contains the value: " .. myTable* )
end

This would print the following into the console:

ipairs on the gmod wiki: http://wiki.garrysmod.com/page/Global/ipairs
More about for loops here: https://maurits.tv/data/garrysmod/wiki/wiki.garrysmod.com/index76d2.html

This is pretty easy to understand, but I’ve seen too many tutorials and other things where people fail at this, so a code example can’t hurt.

Local variables:


local AliveCount = 0
for i, ply in pairs( player.GetAll() ) do
    if IsValid(ply) and ply:Alive() then
        AliveCount = AliveCount + 1
    end
end
print( "Players alive: " .. AliveCount )

--The above creates a temporary variable called AliveCount.
--We won't need that variable later, so we define it as a local variable.
--We can only use the variable within the part of the code we initialize it in

Local functions:


local function MyCustomSpawnFunction( ply )
    print( ply:Nick() .. " has spawned!" )
end
hook.Add( "PlayerSpawn", "MyCustomSpawnFunction", MyCustomSpawnFunction )

--since we are letting the hook call this function, and we will NEVER call it manually, it makes sense to make it a local function.

EDIT: Look at the post below for a better way to add hooks where you’ll never call the function manually.

If you’re never gonna call the function manually then you might as well make it anonymous, ie



hook.Add("PlayerSpawn", "CustomSpawn", function(ply)

    -- Spawn print code

end)


good tip!
When would local functions be useful in that case? I can’t think of other examples.

If you have a lot of code in a hook like that, functions could be useful in breaking things up so it doesn’t hurt your eyes to look at

Converting velocity to KM/H and MPH


function GetKPH( entity )
	if !IsValid(entity) then return false end
	return entity:GetVelocity():Length()*0.06858
end

function GetMPH( entity )
	if !IsValid(entity) then return false end
	return entity:GetVelocity():Length()*(15/352)
end

**
The calculations should be correct, here’s how they’re done:**
1 source unit = 19.05 millimeters (or 0.75 inches)
19.05 millimeters = 0.00001905 kilometers

source velocity is in source units per second, so, to convert from units per second to units per hour:
units/s * 60 * 60

0.00001905 * 60 * 60 = 0.06858

which means that:
(source units per second) * 0.06858 = KM/H

The MPH calculation is just taken directly from the Valve Developer Community, and it checks out perfectly.
(when multiplying velocity directly with a decimal like with the KM/H calculation, it wasn’t as precice, due to it needing so many decimals… damn imperial system)

Example conversion result:

640 source units per second =
43.8912 KM/H
27.272727272727272727272727272727 MPH

The wiremod e2 converter has a bunch of cool conversion units. That way for other units its not hard to get, heck you could convert the e2functions directly to lua functions and have some nice ones to do changes

I basic function to convert numbers to a percent
[lua]
function toPercent(amount, total)
if tonumber(amount) == 0 or tonumber(total) == 0 then return amount end
return math.Round((tonumber(amount) / tonumber(total)) * 100, 2) – convert it to a number in case you use a string and round it to 2 decimal points
end
–example of it’s use
apple = 10
pears = 27
watermelons = 210
–to get the percent of pears you would put the pear in amount and the sum of the fruits in the total
print(toPercent(pears, pears + apples + watermelons))
[/lua]
returns 10.93

I read somewhere that the wire e2 units converter calculates it wrong, because it assumes 1 source unit = 1 inch. I don’t know if they have fixed that or not.

Seems correct.

Tip: Look up common exploits or have a friend that is a pro minge, and have him test out your script.

Strings use a metatable that points to the string library, which means you can do something like this.
[lua]local str = “Example”

if (str:lower() == “example”) then

end[/lua]
You can also index strings with a number to get whatever character is at that position.
[lua]print(str[1])
> E[/lua]

When adding hooks that should be removed when the entity is removed, pass the entity as hook name. Also works with and very useful with panels.

[lua]
function PANEL:Init( )
self.playerNameLabel = vgui.Create( “DLabel”, self )
hook.Add( “OnPlayerNameChange”, self, function( self, newName )
self.playerNameLabel:SetText( newName )
end )
end
[/lua]

Don’t do this too often, because two addons doing this with the same hook and same entity will have collisions and one addon’s hook will override the other’s.