-
Notifications
You must be signed in to change notification settings - Fork 7
Lua Coding Docs: Basics of Coding
Lua is a lightweight, high-level, multi-paradigm scripting language used in Psych Engine modding. Which is a better alternative to Haxe, the main language that Friday Night Funkin' uses. Because you have to manually download the source code, modify the specific Haxe file, and then compile it. As for Lua just insert a Lua into any script folders to modify something that's it. The current version of Lua that Haxe is using is (5.1).
To create your own Lua file I recommend you download VSCode it's available in Windows, Mac, and Linux. Now after you download VSCode open it, press [Command + N] this will create an untitled file. Click the hyperlink that says [Select a language] after that type or search Lua on the Select language mode. Click Lua and boom your done. If you want to save the Lua file press [Command + S] choose the file path to be saved and click [Ok].
Lua in Psych Engine is different compared to the original Lua. Mainly when coding the code should nested inside the Callback templates except for variables, functions, operators, and predefined functions these can work outside the Callback templates. Printing a value is also different instead of using print()
you should use debugPrint()
, it works the same as the print()
function.
Example [Lua (Original)]:
local vary1 = 'hamburgers are cool'
local vary2 = 'potatoes are also cool, i think'
print(vary1) -- will return 'hamburgers are cool'
print(vary2) -- will return 'potatoes are also cool, i think'
Example [Lua (Psych Engine)]:
local vary1 = 'hamburgers are cool'
local vary2 = 'potatoes are also cool, i think'
function onCreate()
debugPrint(vary1) -- will return 'hamburgers are cool'
debugPrint(vary2) -- will return 'potatoes are also cool, i think'
end
Variables are an abstract manipulable storage space used for storing the variable's assigned value. It can be utilized at any location in the Lua file. The variable's value can be updated based on the condition or new value given by the variable.
To declare a variable assign the scope
attribute of the variable this is optional to add, you can set to global
or local
; Default value: global
. With the chosen name
of your variable, name it what-ever you want. Followed by an equal =
character with the specified value
of the chosen variable.
Multi-line variables can also be declared, each name and value attributes of the variable must be separated with comma ,
character. They must be equal to each-other if not it will return a nil
value or an error.
Syntax:
?scope name = value -- single-line variables
?scope names, ... = values, ... -- multi-line variables
To call a variable assign the chosen name
of your variable to get the current value
of the variable. If you want to set the variable value
with a new one, assign the name, equal =
character, and set the new value.
Example:
local foo = 0 -- single-line
local bar1, bar2, bar3 = 1, 2, 3 -- multi-line
local bar4, bar5, bar6 = 4, 5 -- multi-line (missing)
function onCreate()
debugPrint(foo) -- will return '0'
debugPrint(bar1, bar2, bar3) -- will return '1 2 3'
debugPrint(bar4, bar5, bar6) -- will return '4 5 nil'
foo = 12 -- setting single variable
bar4, bar5, bar6 = 543, 872, 923 -- setting multiple variables
debugPrint(foo) -- will return '12'
debugPrint(bar4, bar5, bar6) -- will return '543 872 923'
end
- Variable names can have alphanumeric
Aa12
and underscore_
characters. Note that digital characters can't be placed at the start of the name only at the middle or at the end. - Variable names are case-sensitive so variable
a
andA
are completely different to each-other. - Variable names can't be named after
keywords
,operators
,control structures
, etc.
Reserved Keywords:
and or not local true break
false nil if else elseif goto
then for in repeat until
while do return end function
Example:
-- Valid --
var = 'string1' -- a variable
var1 = 'string2' -- a variable with a number behind it
var_name = false -- a variable with a underscore
_nameVar = true -- a variable with a underscore at the start
var2 = 'corn'..'lover' -- a variable with the operators as a value
-- Invalid --
if = 'string3' -- a variable named with a keyword
>= = false -- a variable named with a operator
23 = 245 -- a variable named with a value
val 3 = nil -- a variable with a space
4val = 245 -- a variable with the number at the start
Strings are a sequence of characters it can represent an alphabetical, digital, punctuation, etc. They are usually surrounded by single-quote ''
and double-quote ""
which are commonly used when creating a string. You can also surround it by double-brackets [[]]
, this is only used for multi-line strings for longer texts.
Example:
local textString1 = 'Hello' -- a single quote
local textString2 = "World" -- a double quote, this is optional to use
local textString3 = [[Culturae organicae te
capiet ad loca quae numquam
expectata non visere!]] -- a double bracket, used this for longer strings
function onCreate()
debugPrint(textString1) -- will return 'Hello'
debugPrint(textString2) -- will return 'World'
debugPrint(textString3) --[[ will return
'Culturae organicae te capiet ad loca quae numquam
expectata non visere!' --]]
end
Numbers are arithmetic values that represent the quantity or amount of something. It can have positive or negative values, and numbers can be expressed as Float or Int; Float numbers support decimal numbers, whilst Int numbers only uses whole numbers.
Example:
local intNumber1 = 81 -- an int number
local floatNumber1 = 23.42 -- a float number
local floatAltNumber1 = 12. -- a float number in whole numbers
local floatAltNumber2 = .56 -- a float number in decimal numbers
function onCreate()
debugPrint(intNumber1) -- will return '81'
debugPrint(floatNumber1) -- will return '23.42'
debugPrint(floatAltNumber1) -- will return '12.0'
debugPrint(floatAltNumber2) -- will return '0.56'
end
Booleans, often shortened to Bools, are data types that can have two possible values: true
or false
. This is commonly used for conditional statements, which allow for different actions by modifying control flow based on whether the condition is true
or false
.
Nil represents nothingness or non-existence of a value. This can be used for destroying a variable or table values if not used anymore. Or use conditional statements to check if the value is a nil
or not.
Tables are a data structuring mechanism in Lua the only one in fact. They are associative arrays, which means they hold a collection of key or value pairs. Tables can be used for to store multiple values of any kind except for nil
values. To construct a table, use curly-braces {}
characters rather than bracket []
characters like most programming languages use. Tables can be constructed as an Array or a Dictionary.
Example:
local tableGroup1 = {'string', true, nil} -- a table with string, boolean, and nil values
local tableGroup2 = {{45, 13}, {34, 76}} -- a table inside with nested tables
function onCreate()
debugPrint(tableGroup1[1]) -- will return 'string'
debugPrint(tableGroup2[1][2]) -- will return '45'
end
Arrays are the default table syntax; each value within an Array must be separated by a comma ,
character. To read an Array, put a pair of brackets []
around the index position of a table. Lua uses 1-based index rather than 0-based index like other programming languages. In other words, the first index position always start at 1
.
Example:
local tableArray = {'Correct', 'Incorrect', 'Maybe', 'Invalid'} -- a table with string values
function onCreate()
debugPrint(tableArray[1]) -- will return 'Correct'
debugPrint(tableArray[3]) -- will return 'Maybe'
end
Dictionaries are ordered by key-value pairs; each value in a Dictionary must be defined by a key, which is the value's name followed by an equal =
character with the given value at the end. With the key-value pair separated by a comma ,
character. To read a dictionary, add a dot .
character followed by the key name; Example: tableName.keyName
. Or add a pair of brackets []
and quote ''
/ ""
characters around the name of the key; Example: tableName['keyName']
/ tableName["keyName"]
.
Example:
local tableDict1 = {isFatherless = false, hasTouchGrass = true}
local tableDict2 = {['air'] = 'Oxygen', ["food"] = 'potato', ["love"] = 'carbon monoxide'}
function onCreate()
debugPrint(tableDict1.isFatherless) -- will return 'false' (recommended syntax)
debugPrint(tableDict1['hasTouchGrass']) -- will return 'true' (not recommended syntax)
debugPrint(tableDict1.air) -- will return 'Oxygen'
debugPrint(tableDict2['food']) -- will return 'potato'
debugPrint(tableDict2["love"]) -- will return 'carbon monoxide'
end
Functions are a collection of code to perform a specific task. This is used the same functions multiple times to make the code reusable. Functions are defined with the function
keyword followed by the name of your custom function. With the pair of parenthesis ()
characters. To call a function get the name of your custom function followed by a pair of parenthesis ()
characters.
Example:
function hello() -- a 'hello' function
debugPrint('Hello Function')
end
function onCreate()
hello() -- calls the 'hello' function and will return 'Hello Function'
end
Parameters are a special type of variable declared inside the parenthesis ()
character. You can add more parameters by typing the name of parameter and each separating them with a comma ,
character. They can add more functionality to the function. If any of the arguments are left blank then it will cause an error. So uhh double check when calling functions.
Example:
-- function created by Mayo78
function setPos(obj, pos) -- Concatenates setProperty x and y
if pos[1] ~= nil then -- makes pos parameter acts like a table
setProperty(obj..'.x', pos[1])
end
if pos[2] ~= nil then
setProperty(obj..'.y', pos[2])
end
end
function onCreatePost()
setPos('boyfriend', {100, 500}) -- Changes the position to x = 100 and y = 500
end
You can also declare a infinite parameter ...
inside the parenthesis ()
character, just to let you know if you want it for some reason. But if there are non-infinite parameters declared it at the end of the parameter arrays.
Example:
function Array(...)
local convert = {...} -- converts it into a table
local result = {}
for i = 1, #convert do -- alternative to read every value of a table
table.insert(result, convert[i]) -- inserts the value
end
return result -- returns the result
end
function onCreate()
for k,v in pairs(Array(231, 234, 456)) do
debugPrint(v) -- will return '231, 234, 456'
end
end
Comments are used to explain the context of code and prevent the execution on a specific code; Lua will ignore them. Comments starts with a double minus --
characters as the syntax for single-line comment. And for multi-line comment add double brackets [[]]
characters.
Example:
-- Here's a single-line comment nothing special
--[[
Here's a multi-line comment for really
long unnecessary comments, also cheese.
]]
Operators are unique symbols that are used to carry out operations on operands. For the conditional statements to use to determine if the value is true
or false
before executing the code block. They can be represented as Arithmetic, Relational, Logical, and Miscellaneous.
Arithmetic operators are mathematical operators used to perform calculations to numeric values.
-
+
- Addition -
-
- Subtraction -
*
- Multiplication -
/
- Division -
%
- Modulus -
^
- Exponentiation -
-
- Unary Negation
Relational operators are used to compare multiple operands inside a condition in order for the code block to execute.
Operators | Description | Example |
---|---|---|
== |
Checks if the condition is equal to the right. |
a == b , returns false . |
~= |
Checks if the condition is not equal to the right. |
a ~= b , returns true . |
> |
Checks if the condition is greater than the right. |
2 > 1 , returns true . |
< |
Checks if the condition is lesser than the right. |
4 < 2 , returns false . |
>= |
Checks if the condition is greater or equal to the right. |
3 >= 3 , returns true . |
<= |
Checks if the condition is lesser or equal to the right. |
2 <= 23 , returns false . |
Logical operators are used to combine multiple conditions and to specify on what conditions needs to be true
.
Operators | Description | Example |
---|---|---|
and |
Combines multiple conditions together; will return true ,if all the statements are true . |
a == true and b == true , returns false
|
or |
Combines multiple conditions together; will return true ,if any of the statements are true . |
a == true or b == true , returns true
|
not |
Reverses the condition; if the value is equivalent to false ,then the operator will set it into true , and vice versa. |
not true , returns false . |
Miscellaneous operators only features two operators the Length and Concatenate operators.
Operators | Description | Example |
---|---|---|
# |
Length operator, Checks the maximum length size of a string or table . |
#('sussy') , returns 5 . |
.. |
Concatenate operator, Merges multiple string together. |
'snow'..'ball' , returns snowball . |
Control Structures are a block code which analyzes values and decide whether to execute the code or not. Also i'm only mentioning the for
loop statement and not while
, repeat
loop statements because no one ever uses these 2 loops.
These are a type of control structures that specifies whether or not to execute the block code. They are the most common control structures to use. There are only 3 if else statements the if
, else
, elseif
statements.
If statement checks if the condition of the statement is true
. It's define with the if
keyword and the specified condition to execute the code block followed by the then
keyword.
Example:
local getDateWeekDay = os.date('*t').wday -- checks the current day of week
function onCreate()
if getDateWeekDay == 6 then -- checks if the day is 'Friday'
debugPrint('Day: Friday')
end
end
Else statement checks if the condition of the statement fails then this function will be called. It's defined with else
that's it nothing else to say about. This statement should be placed under the if
or elseif
statements, example shown below.
Example:
local getDateWeekDay = os.date('*t').wday -- checks the current day of week
function onCreate()
if getDateWeekDay == 6 then -- checks if the day is 'Friday'
debugPrint('Day: Friday')
else -- checks if the day is not 'Friday'
debugPrint('Not Friday')
end
end
ElseIf statement checks is an alternative condition for the if
statement, if the if
or another elseif
condition fails then this statement will be called. It's define with the elseif
keyword with the same syntax for the if
statement.
Example:
local getDateWeekDay = os.date('*t').wday -- checks the current day of week
function onCreate()
if getDateWeekDay == 2 then -- checks if the day is 'Monday'
debugPrint('Day: Monday')
elseif getDateWeekDay == 4 then -- checks if the day is 'Wednesday'
debugPrint('Day: Wednesday')
elseif getDateWeekDay == 6 then -- checks if the day is 'Friday'
debugPrint('Day: Friday')
end
end
For loop statement allows you to loop a specific number of times. This loop is commonly used for setPropertyFromGroup()
and getPropertyFromGroup()
functions for note modification, modcharts, or something. And used for reading a table values or performing on numeric values. There are 2 types of loops Generic loop or Numeric Loop.
Numeric Loop are a type of loop that use numeric values to increment or to decrement the value. This loop is usually the most common loop to use for setPropertyFromGroup()
and getPropertyFromGroup()
functions. There are 3 attributes when declaring a Numeric loop exp1
, exp2
, and an optional exp3
attributes.
-
exp1
- The local variable for the numeric loop with the minimum number value to start. -
exp2
- The maximum number value of numeric loop to stop at. -
exp3
- An optional attribute, How much the value will be incremented1
or decremented-1
; Default value:1
.
Example:
function onCreate()
for index = 0, 5, 1 do -- Increment loop
debugPrint(index) -- will return '0, 1, 2, 3, 4, 5'
end
for index = 5, 0, -1 do -- Decrement loop
debugPrint(index) -- will return '5, 4, 3, 2, 1, 0'
end
for index = 0, 300, 50 do -- Increment Loop with each value incremented by 50
debugPrint(index) -- will return '0, 50, 100, 150, 200, 250, 300'
end
end
Generic Loop are a type of loop that commonly uses pair functions to read all the table values. This is just an alternative loop for reading every table values. There are 3 attributes when declaring a Generic loop exp1
, exp2
, and iterate
attributes.
-
exp1
- The key name of the table, you can name how you want; Example:key
. -
exp2
- The value name of the table, you can name how you want; Example:value
. -
iterate
- The values to iterate from the loop can be either the pair functions or other.
Example:
function onCreate()
local tableThingy = {num1 = 231, num2 = 345, num3 = 234}
for key, value in pairs(tableThingy) do
debugPrint(key, value) -- will return 'num1 = 231, num2 = 345, num3 = 234'
end
end
Return statement as the name suggests returns the results from the function. And stops the execution of the function. It must be relative at the end of the function or conditional statement.
Example:
function getMidpointNum(ope1, ope2)
return (ope1 + ope2) / 2 -- returns the middle value between 'ope1' and 'ope2'
end
function onCreate()
debugPrint(getMidpointNum(9 + 10)) -- will return '9.5'
end
Break statement stops the loop statements from looping. You can use this for specific conditions for the loop to end. It must be relative at the end of the function or conditional statement.
Example:
function onCreate()
for index = 0, 10 do
debugPrint(index) -- will return '0, 1, 2, 3, 4, 5'
if index == 10 / 2 then -- checks if the 'index' value is '5'
break -- ends the loop
end
end
end
Scope in programming determines whether a variable or function can be accessible outside that code block. They can be determine by either setting them into global
or local
scope. Declare them behind the name
of the variable or function
keyword for functions.
Global scope are the most common and the defualt scope for variables and functions. They can be called anywhere inside the Lua script or outside the Lua script with functions. Local scope are more faster to call than Global scope, they can be only call inside the specific scope of the code block. You can declare them with the local
keyword behind the name
of the variable or the function
keyword of the function.
Example:
function onCreate()
do -- do block
myGlobalVar = false -- global
debugPrint(myGlobalVar) -- will return 'false'
end
do -- do block
local myLocalVar = true -- local
debugPrint(myLocalVar) -- will return 'true'
end
debugPrint(myGlobalVar) -- will return 'false'
debugPrint(myLocalVar) -- will return 'nil'
end
Modules are a code library these mostly contain functions or variables. They can help you maintain a code-base and break your code into different Lua files. If you're using them frequently when coding your weird Lua scripts.
To create your own custom module make a separate Lua script and placed the location of the script. Let's just say you placed them inside mods/scripts/modules
folder and you named it myGamingModules.lua
. Now inside of it declare a local
table variable with the exact name of the Lua script module with no value(s) inside of it; Example: local myGamingModules = {}
.
Before you declare your functions or variables each name should have the local
table variable name followed by the dot .
character. And at the end of the Lua script module should have the return
statement on the local
table variable name to export the modules to other Lua script; Example return myGamingModules
. (I Think)
Warning: DO NOT DECLARE
LOCAL
FUNCTIONS OR VARIABLES INSIDE THE MODULES BECUASE IT WILL NOT EXPORT THEM AND MIGHT CAUSE AN ERROR, IT MUST BE AGLOBAL
ONE OKAY!?
Example:
local myGamingModules = {}
myGamingModules.red = 'ff0000'
myGamingModules.green = '00ff00'
myGamingModules.blue = '0000ff'
function myGamingModules.switch(case, statement)
if statement[case] ~= nil or not statement["default"] then
return statement[case]()
else
return statement["default"]()
end
end
function myGamingModules.setPos(obj, pos) -- Concatenates setProperty x and y
if pos[1] ~= nil then -- makes pos parameter acts like a table
setProperty(obj..'.x', pos[1])
end
if pos[2] ~= nil then
setProperty(obj..'.y', pos[2])
end
end
return myGamingModules
Requires the module name and imports the functions or variables. To declare a require
function either use the function itself or use it inside the value of the variable. This is the only few functions that you can add without the parenthesis ()
characters.
-
moduleName
- The location of the Lua script module file to be used; Starts outside themods
folder.
Syntaxes:
require 'mods/scripts/modules/myGamingModules' -- uses the default name
local moduleName = require 'mods/scripts/modules/myGamingModules' -- uses a custom name
To call the require
function, get the specified module name to use; if it's contained inside a variable, get the variable name; if not get the module name. Add a dot .
character followed by the function or variable name inside the Lua module file.
Example:
local moduleName = require('mods/scripts/modules/myGamingModules') -- gets the module
function onCreatePost()
moduleName.setPos('boyfriend', {500, 1200}) -- sets the boyfriend positions
debugPrint(moduleName.red) -- will return 'ff0000'
debugPrint(moduleName.green) -- will return '00ff00'
debugPrint(moduleName.blue) -- will return '0000ff'
end
Gets the global
functions or variables on other Lua files.
-
path
- The location of the Lua script module file to be used;
Gets the specific value type of the value. Can be used to check the value type inside the conditional statements; Return either: string
, boolean
, number
, table
, function
.
-
value
- The value to be used.
Converts any number into a string to prevent errors.
-
num
- The real number to be converted.
Example: 'Cheese count: '..tostring(34)
, it will return Cheese count: 34
.
Converts a number or boolean inside a string into a real number. If the value contains any characters other than digits, it will return nil
value.
-
num
- The string number to be converted.
Example: tonumber('34'..'23')
, it will return 3423
.
Loads a chunk from the string, basically converts the string into real code; Calling should be followed by a pair of parenthesis ()
character.
-
chunk
- The code to be executed.
Example: load('return 4 * 2')()
, it will return 8
.
Returns every key-value pairs inside a table and is typically used in table dictionaries. It can return as an unorganized table sort; Not to be confused with ipairs()
functions.
-
tab
- The table to be used.
Example:
local function read(tab) -- read through a table
local results = ''
for key, values in pairs(tab) do -- pair function
results = results .. key..'\t'..values..'\n'
end
return results
end
function onCreate()
local tableArry = {123, 567, 134}
local tableDict = {a = 123, b = 567, c = 134}
debugPrint(read(tableArry)) -- will return '123, 567, 123'
debugPrint(read(tableDict)) -- will return 'c = 134, a = 123, b = 567'
end
Returns every index-value pairs inside a table and is typically used in table arrays or with numeric keys within a table dictionary. If the table value has nil
it will stop executing the loop there.
-
tab
- The table to be used.
Example:
local function read(tab) -- read through a table
local results = ''
for key, values in ipairs(tab) do -- ipair function
results = results .. key..'\t'..values..'\n'
end
return results
end
function onCreate()
local tableArry = {123, 567, 134}
local tableDict = {a = 123, b = 567, c = 134}
debugPrint(read(tableArry)) -- will return '123, 567, 123'
debugPrint(read(tableDict)) -- will return 'nil' cuz it's a dictionary
end
The Global Variable is a special type of variable specifically a table dictionary. That gets every global
variable and are saved inside the variable, it's defined with the _G
keyword. Be careful when calling it because it can cause a crash. Changing its value does not affect any environment, nor vice versa.
You can use this for getting multiple global variables from a loop and modify the values easily. The original intended purpose of the global variable is get other global variables from other scripts. But it's broken when using it for some reason.
Example:
function onCreate()
myGlobalVar0, myGlobalVar1 = 183, 231
myGlobalVar2, myGlobalVar3 = 963, 263
for nummys = 0, 3 do
debugPrint(_G['myGlobalVar' .. i]) -- will return '183, 231, 963, 263'
debugPrint(_G['defaultPlayerStrumX' .. i]) -- will return '732, 844, 956, 1068'
end
end
Is the page in some way inaccurate? an error, a typo, or outdated data? To report it, use the "Issue Tab". Or do you wish to include a new function or add new information? use the "Pull Request Tab". Help is always appreciated!
- Event Callbacks
- Custom Sprite
- Custom Text
- Object Functions
- General Functions
- Scripting & File Functions
- Game Input Control Functions
- Language Translation
- HScript Functions
- Custom Substates
- Custom Shaders
- Deprecated & Removed Functions
- Sound & Music Functions
- Tweens & Timers Functions
- Reflection Functions
- Variables