News: Welcome back to Bullworth! If you haven't already, you will need to reset your password..


Author Topic: LUA Tutorial  (Read 30930 times)

0 Members and 1 Guest are viewing this topic.

Offline DaBOSS54320

  • Hero Member
  • ****
  • Posts: 3,398
  • Gender: Female
    • View Profile
LUA Tutorial
« on: August 24, 2014, 10:23:45 PM »
I made yet, another LUA tutorial. Hopefully you learn a lot from this tutorial and it helps you get started doing script mods. Bully is a pretty fun and easy game to mod once you get used to it. The tutorial may be boring until you get into doing actual Bully scripts. If you just read and make sure you understand everything in this tutorial, it will pay off a lot when making Bully scripts. If you can understand everything in this tutorial you will be a LUA modding master in no time!



Introduction
LUA is a scripting language that Bully uses for it's scripts. It uses LUA 5.0.2. LUA is currently on version 5.3. For this introduction I just want to explain some simple concepts of programming and scripting that applies to most languages, of course including LUA. A program/script is created in plain text, called the source file. The source file can then be compiled into machine code so the code can be understood by the computer, or interpreted at run time by an interpreter that will only need the source file, no compiled version. Or a hybrid of compiling and being interpreted. For LUA we need to compile it, which is done via LuaC. So you should download it. I suggest doing that right now so you can install visual studio while reading the rest of this tutorial to save some time. LUA needs visual studio because it was made in C, which needs the libraries included in visual studio. So that covers compilation, another big part of scripting is how the code will run. When the script starts, the code will begin at one point (usually the main function, discussed later) and read each code one by one and execute going downward in the file. There are things that can change the flow of execution though, such as functions and control flow. We will cover functions and control flow later in the tutorial.



Example
The best way to start learning will probably be by looking at an example. Here I will put a script to copy to a text editor or some type of LUA editor so we can compile it and see it in-game. I suggest using notepad++ for editing LUA files. This example should include many LUA concepts to be discussed in detail later.

Code: [Select]
-- Weapon Selector
local weaponSelect = 1

MissionSetup = function()
  AreaTransitionXYZ(0,270,-110,6)
  F_SetupWeapons()
end

function MissionCleanup()
 
end

function F_SetupWeapons()
  weapons = {
    {name = "Apple",model = 310}
    {name = "Banana",model = 358}
    {name = "Baseball",model = 302}
    {name = "Basket Ball",model = 381}
    {name = "Bat",model = 300}
    {name = "Big Firework",model = 397}
    {name = "Books 1",model = 405}
    {name = "Books 2",model = 413}
    {name = "Books 3",model = 414}
    {name = "Books 4",model = 415}
    {name = "Books 5",model = 416}
    {name = "Brick",model = 311}
    {name = "Broom",model = 377}
    {name = "Camera",model = 426}
    {name = "Dead Rat",model = 346}
    {name = "Devil Fork",model = 409}
    {name = "Dish",model = 338}
    {name = "Egg",model = 312}
    {name = "Fire Extinguisher",model = 326}
    {name = "Firework",model = 301}
    {name = "Flowers",model = 359}
    {name = "Football",model = 331}
    {name = "Football (Bomb)",model = 400}
    {name = "Frisbee",model = 335}
    {name = "Gold Pipe",model = 418}
    {name = "Itchy Powder",model = 394}
    {name = "Kick Me",model = 372}
    {name = "Lunch Tray",model = 348}
    {name = "Marbles",model = 349}
    {name = "News Roll",model = 320}
    {name = "Pinky Wand",model = 410}
    {name = "Plate",model = 355}
    {name = "Poison Gun",model = 395}
    {name = "Rocket Launcher",model = 307}
    {name = "Rubber Band",model = 325}
    {name = "Shield",model = 387}
    {name = "Metal Plate",model = 360}
    {name = "Skateboard",model = 437}
    {name = "Sledge Hammer",model = 324}
    {name = "Slingshot",model = 303}
    {name = "Snowball",model = 313}
    {name = "Snowball 2",model = 330}
    {name = "Soccer Ball",model = 329}
    {name = "Spray Can",model = 321}
    {name = "Spud Gun",model = 305}
    {name = "Stink Bomb",model = 309}
    {name = "Super Slingshot",model = 306}
    {name = "Super Spud Gun",model = 396}
    {name = "Trash Lid",model = 315}
    {name = "Trophy",model = 385}
    {name = "Umbrella",model = 404}
    {name = "Vase 1",model = 354}
    {name = "Vase 2",model = 353}
    {name = "Vase 3",model = 345}
    {name = "Water Baloon",model = 383}
    {name = "Water Pipe",model = 342}
    {name = "Whip",model = 411}
    {name = "Wood Paddle",model = 357}
    {name = "Wood Plank",model = 323}
    {name = "Yardstick",model = 299}
  }
end

function main()
  repeat
    F_WeaponSelect()
Wait(0)
  until not Alive
end

F_WeaponSelect = function()
  if IsButtonBeingPressed(0,0) and weaponSelect > 1 then
    weaponSelect = weaponSelect - 1
  elseif IsButtonBeingPressed(1,0) and weaponSelect < table.getn(weapons) then
    weaponSelect = weaponSelect + 1
  elseif IsButtonBeingPressed(3,0) then
    PedSetWeapon(gPlayer,weapons[weaponSelect].model,50)
  end
  TextPrintString("Weapon Selector\n\nWeapon: "..weapons[weaponSelect].name,1,1)
end

Copy the above code into notepad++ (Or whatever you decided to use) and save it as "WeaponSelector.lua". Or anything ending with .lua, the file extension for LUA files. There are several ways to go about compiling it which will be discussed later, but for now we will just drag it over LuaC.exe as that is an easy way to do it. LuaC.exe was included in the download link in the introduction. When you do this, it should make a file called LUAC.OUT. This is the compiled output of our script file. We now have a file that can be read by bully. But there is still 2 more things to do. First, rename the file to ArcRace1.lur. .lur is the file extension for compiled LUA scripts in Bully, and ArcRace1 is the name of the script for the arcade machine in the boy's dorm. We will be replacing that script as that script can be activated/launched easily by simply going to the arcade machine and playing since we will replace the old script that contained the arcade racing game. The script files for the game are located in /Scripts/Scripts.img. An IMG file is a file archive used by Bully and other Rockstar games. We will need something to open it up and put our file in, for now we will use MadmaN's IMG tool but we will discuss others later. Open the Scripts.img with the IMG tool. Find ArcRace1.lur inside the Scripts.img and delete it. There isn't a replace feature on this tool and it will let you put 2 files of the same name in without warning which may cause undesired results, so be careful. Once you delete the original one, add our new modified one by pressing Action and then Add. Once you add the new modified ArcRace.lur close the tool and launch Bully. When you get in-game go to the arcade machine and play. You should spawn outside of the dorm and have a weapon selection menu. Use objective left/right to navigate the weapon menu and the zoom out button to get the weapon. If you do see the weapon selector, congratulations! If not, I suggest you try to figure out what the problem was before moving on.

Now it is time to explain all of the concepts shown in the example. Only brief descriptions will be given here as everything will be explored in detail later.

Creating Functions: Examples of functions in the script are "MissionSetup", "MissionCleanup", "main", "F_SetupWeapons", and "F_WeaponSelect". Names of functions can be whatever you like, as long as they are not a reserved keyword (LUA syntax/stuff that notepad++ color codes) and not the name of a Bully function. Functions can be declared (created) by writing "functionName = function()" or "function functionName()" then putting a space, linebreak, tab, or combination of those 3 things then the word "end" which ends the function. The code in a function is executed when the function is called. Bully calls the function MissionSetup when a mission script starts (ArcRace1.lur is a mission script) then main after MissionSetup, then MissionCleanup after main. So those 3 functions are required in all mission scripts. Only main is required in scripts that are not missions.

Variables: The simplest example of a variable in the script is weaponSelect. It is declared a local variable by putting "local" before the variable. If we did not write "local" it would still be a variable, but global instead (There isn't anything to type before global variables, just don't put local). A variable is declared/created by optionally putting "local", the name of the variable, and optionally a '=' sign followed by the initial/starting value of the variable. Variables can keep track of numbers and other information.

Tables: "weapons = {}" declared a table called "weapons". The concept of tables is big/complex so we will get into it a lot more later.

Calling/Using Functions: There are too many examples of this to want to list, but all the words followed by '()' sometimes with numbers/text in between the '(' and ')' are function calls as long as they are not function declarations. You should be able to tell the difference because of being explained declaring/creating functions a moment ago. When a function is called, the code inside it gets executed. After the function reaches "end" or reaches "return" it will go back to the point in the code where the function was called.

Control Flow: The 'if', 'elseif', 'repeat' and 'until' keywords are all part of control flow in the example. Control flow will change the flow of the program so it executes differently then going straight down all the codes.

Comments: The 2 '-'s in the start of the script made the rest of that line a comment. Comments do not effect how the code is run at all, they are not executed when the script runs. They are meant as a way to put text for you (The coder) or people reading your code so the script can be more easily understood.



Variables
Variables are an important thing to know. They can hold information. An example of a variable would be if X = 6, then 7 + X would be 13. X is the variable and it holds the value 6. So when you add 7 to X (X equals 6) it's like 7 + 6. In LUA variables can hold more then just numbers. They can hold numbers, text, boolean, tables, lists and nil. We'll cover the first 3 and nil here and cover tables/lists later.
LUA is a dynamic language, so variables can equal any of the types without having to specify a type for the variable.
Names: A variable name pretty much has to follow the same rules as function names. Make sure variables are not the same name as a function you created. Also the same goes for creating function names, don't make it the same as a variable.

Numbers: A variable can equal a number. Negative/positive numbers and floating point (decimal) numbers. Some examples are X = 47, Variable = -62.4, YoDiggityDawg = 69.

Text: A variable can also hold text. The more technical term is a string. A string can be declared/created by putting text in between 2 "s or 2 's. The double quotes are usually preferred. So for example you could make a variable like var = "This is a string of text" or var = 'Some text'. You can also append (add) to strings by using 2 .s. Example:
name = "DaBOSS"
var = "Hello, I am "..name
After the code is executed, var will equal "Hello, I am DaBOSS". There is also a character called the escape character, it is the backslash. \. It is used to type "Hard to type" characters. Such as a tab, linebreak, quote, or backslash. Here is a list of the important ones, though it is not a full list.
\n = Linebreak
\t = Tab
\" = "
\' = '
\\ = \
The quotes are hard to type because they start and end the strings so if you wanted to make a string where a character was talking:
var = "Andy told me "I seen my dog pooping yesterday and was like woah, that's hot" and I flipped out". It would see the first quote and have that be the start of the string, but when it sees the next one it will stop. So var would equal "Andy told me ". If we used the \" though, it would read the " as normal text so it would work. Another solution is to use the 's instead.

Boolean: Boolean means true/false. Very simple. var = true, var = false. Using the keyword 'not' will invert the meaning of a boolean. For example, if you made var = not true it would be equal to false. Or var = not false it would equal true.

Nil: Nil is nothing. No storage. It isn't even 0, it doesn't hold a number, text, nothing. It is written by simply typing "nil"

Local: Local variables are declared by putting the keyword "local" in front of a variable. When you do not put "local" in front of a variable it is global. When a variable is global it can be used anywhere in the script. If it is local it can only be used in the scope it was declared in. Scope could be a function for example.
function Func1()
  local var = 1
end

function Func2()
  local var2 = var + 1
end
That code would cause an error because the variable var cannot be used as it is local to Func1. It can only be used in Func1 and no other functions.
There can be multiple local variables of the same name and it will work perfectly fine. Since the 2 variables are local to different functions/locations they just do not effect each other They are their own variables.
Also a warning, do not put "local" before a variable when using it. Only when creating it. For example:
local a = 42
a = 72.
If you put local before the 2nd a it would cause an error.

Declaring Multiple Variables: Let's say we wanted to declare 3 variables. 3 variables for coordinates X, Y, and Z. We COULD do...
local X = 270
local Y = -110
local Z = 6
or, we can use commas to declare multiple variables at once.
local X,Y,Z = 270,-110,6



Arithmetic
Math can be used in LUA. It can be used as a way to get numbers to assign to variables, and other uses as well.
+ is addition, - is subtraction, * is multiplication, / is division.
var1 = 42 + 7 * 8/4
var1 equals 56. the * and / operations are evaluation before the +. This is because of the order of operations.
42 + 7 * 8/4 - Beginning.
42 + 56 / 4 - Multiply 7 and 8.
42 + 14 - Divide 56 and 4.
56 - Add 42 and 14.
What if you wanted to do the addition first? You can use parenthesis ()
(42 + 7) * (8/4)
Now it will evaluate the () part first, and it will come down to 49 * 2. (8/4 = 2) Then it would be 98.



TextPrintString
This section will be short. TextPrintString is a function in Bully. When you call it, it can print text to the screen. It takes 3 arguments (things in the (parenthesis)) the text to be printed (As discussed in the variables section, text is made by using the "quotes"), a number that represents how long it stays on screen in seconds, and a number that represents where on the screen to be printed. 1 is the top of the screen, 2 is the bottom.
Examples:
TextPrintString("This is some text",5,1) -- Print "This is some text" on the top of the screen for 5 seconds.
TextPrintString("Yo dawg",9,2) -- Print "Yo dawg" on the bottom of the screen for 9 seconds.
local name,location = "Carl","Grove Street"
local str = "Yo dawg, my name is "..name.." and I live in "..location
TextPrintString(str,5,1) -- Print the value of the variable "str" on the top of the screen for 5 seconds.
This function is important to tell messages to the player of the mod. More bully functions are discussed later, this one just had to be explained first.



Relational Operators
Relational operators compare 2 values and return a boolean. (True/false). It uses the signs <, >, ==, <=,  >= and ~=. They can be used to assign values to boolean variables, be used as arguments to a function, or most importantly in Control Flow (The next section). Let's look at some examples.

local var = 25 < 24 -- var equals "false" because 25 is not less than 24.
local var = 40 == 40 -- var equals "true" because 25 does equal 25.
local var = 29 <= 29 -- var equals "true" because 29 is less than or equal to 29.
local var = 24 < 24 -- var equals "false" because 24 is not less than 24.
local var = 100 ~= 99 -- var equals "true" because 100 does not equal 99.

A common mistake is confusing == with =. The single = is used for assigning a value. The double == is used for comparison. All of the operators above except == can only be used with numbers. == can be used with strings and boolean as well. Examples:

"Yo dawg" == "Yo dawg" -- true
"No" == "Yes" -- false
true ~= false -- true because true is not false.
false == false -- true because false is false.



Control Flow
Control flow is what controls the order your code executes. So far we know that code will run from top to bottom, and calling a function will run all the code in it and return to the point in the code the function was called at. There is a lot more we can do however. What if we wanted to print the game counting to 1000? We could do...
TextPrintString(1,1,1)
TextPrintString(2,1,1)
TextPrintString(3,1,1)
...about 1000 lines of code later...
TextPrintString(1000,1,1)
but that would take a long time to do, and be very tedious. A solution is to use loops, loops are a part of control flow that lets us repeat a certain part of code until a condition is met. There are many types of loops. "while", "repeat", "do-while", and "for". For the counting example the "for" loop would work best but it is also the most complex type of loop. We'll use "repeat" since it was used in the example and you may be a bit familiar with it. If you aren't don't worry. Comments are used to guide you through the process.

local count = 1 -- declare a variable to remember where we are in the counting
repeat -- repeat the following code
  TextPrintString(count,1,1) -- print the value of "count" on the top of the screen for one second
  count = count + 1 -- add 1 to count
  Wait(1000) -- wait 1 second (see below)
until count >= 1000 -- repeat the code until "count" is greater than or equal to 1000

The function "Wait" hasn't been discussed yet, it is a function for Bully. It will pause the script for the time specified in milliseconds.
The repeat loop is pretty simple. Just repeat the code between "repeat" and "until" until the expression after "until" is true. You could also just put "true" or "false" for an infinite loop or a loop that only executes once. repeat does execute at least once, because it executes all the code and then tests if the expression is true/false after execution. If it is false, it will do the code again. Here are all of the loops:

Repeat Loop
repeat -- execute the code below
  -- code to repeat here
until boolean -- check if boolean equals false, if it does repeat the code again.
repeat checks if boolean is false after executing, so it will execute at least one time.

While Loop
while boolean do -- check if boolean equals true (Can be a boolean variable or a relational operator)
  -- code to repeat here
end -- end of the loop. check if the boolean above is still true. if it is repeat, otherwise move on.
while checks if boolean is true before executing. It won't execute even once if it is false, unlike repeat.

Do-While Loop
do
  -- code to repeat here
while boolean
do-while checks if boolean is true after executing. It is very similar to the repeat loop except it repeats if it is true instead of false.

For Loop
for var = num, compare, increment do
  -- code to repeat here
end
var: A variable name. This variable is declared/initialized once when the loop starts.
num: The initial/starting value for var.
compare: Number to compare var with. If var is less than or equal to it, the code will execute.
increment: The amount to increment var after every iteration of the loop. This part is optional, if it is not specified 1 is implied.

Since the for loop is kind of advanced compared to the others, here is an example of counting.
for c = 1, 1000 do
  TextPrintString(c,1,1)
  Wait(1000)
end

Bully Loops: There is something that all loops in Bully need. A loop will crash the game if it does not have the function "Wait" inside of it. Example:
while true do
  Wait(0)
end
The above will work
while true do
 
end
The above will crash the game.

Break: There is also something that you can put inside loops too cause them to break/stop executing early. When the code encounters "break" it will go to the end of the loop it is currently in without executing any more code. Example:
local count = 1
while true do
  break
  TextPrintString("Sup",1,1)
  Wait(0)
end
"Sup" is never printed in the example.

Conditional Statements: Along with loops are the conditional statements. They are actually fairly similar to loops, except they only execute once. The keywords if, else, elseif, then, and end are used for these conditional statements. This is the basic form of it:

if boolean then
  -- code to be executed
end

Along with that however, "else" can also be used instead of "end" if something should happen if it is false instead of true. "elseif" can be used like "else" but for also including another test. Even when "else" and "elseif" are used there still has to be an "end" at the end. "then" only has to appear after "if" or "elseif" not "else".
Here are a few examples to help you understand better:

if 24 > 50 then
  -- This code will not execute
end

if 25 ~= 25 then
  -- This code will not execute
else
  -- This code will execute
end

if true then
  -- This code will execute
elseif true then
  -- This will not
end

local age = 24
if age >= 21 then
  TextPrintString("You can drink beer",1,1)
elseif age >= 16 then
  TextPrintString("You can't drink beer, but at least you can drive",1,1)
elseif age >= 6 then
  TextPrintString("Well... shit you can't do much",1,1)
else
  TextPrintString("You can't do much but at least you don't have to go to school",1,1)
end



Functions
Functions have been discussed already in other sections of this tutorial, so a lot of this may be recap. For that reason it will be a bit more brief.
A function can be declared by writing:
function functionName()
  -- function contents/codes
end

or

functionName = function()
  -- function contents/codes
end

Both are equal, use whatever looks better to you. Usually people use the 2nd one but I prefer the first one. Inside can be all the code to be executed when the function is called. That can include setting variable values, and calling other functions. Naming functions follow the same rules as naming a variable. F_ is often used for function names (EX: F_Function). This does not do anything but organize your code. I usually do it but it is not necessary. The codes used by Bully (Such as TextPrintString,Wait,etc) do not have F_ in front of them so putting F_ in front of yours is a good way to ensure you do not accidentally name your function the same as a Bully function. A function can be called by putting it's name followed by (). EX: F_Function(). When a function is called it will execute all the code in the function then return to the point in code after the function call.

Function Parameters: When declaring a function, you can put variable names in between the parenthesis. They are called parameters. Parameters are important because often times functions need some type of information to be able to do their job. Here is an example for a function to add 2 numbers. Obviously this can be done by simply using the + operator but this is a simple way to show you how parameters work.

function Add(a,b)
  local c = a + b
  TextPrintString("The result is "..c,3,1)
end

When the function is called, it will add the 2 numbers together and print the result. When you call the function you have to give values for the parameters. These are called arguments. Example:
Add(4,2) -- 6 will be printed on screen
Variables can also be passed into the function. Example:

local num1 = 64
local num2 = 42
Add(num1,num2) -- 106 will be printed

Parameters are local to the function they are a part of. So for our Add example the variable 'a' and 'b' cannot be used anywhere else. If we do make a variable called 'a' somewhere else it will still work perfectly fine. The 2 'a' variables just do not relate to each other because they are in different locations. This was discussed earlier in the variables section.

Return: The keyword "return" works just like "break" but for functions. A function can exit before reaching the "end" by using the "return" statement. It does have something more then break though. That is returning a value. It is optional to return a value, but if a function does return a value it is simply put after the return statement. When a function returns a value the function can be called to get a value. Here is an example for our Add function.

function Add(a,b)
  local c = a + b
  return c
  TextPrintString("Yo",1,1)
end

local num = Add(42,7) -- num equals 49.

The string "Yo" is never printed because the function returns before that part of the code is reached.
If you don't put a function somewhere where it's returned value will be used, the value will just be ignored/lost.
You can also use functions that return a value as arguments.
Add(Add(4,6),10) -- 16



Tables
Basics:
If you know what an array is, a table is basically an array in LUA. If you have no idea what an array is, forget I said anything and keep reading.
Tables are types, and thus can be stored in variables. Tables however are special types... they can store more values in them. Check out this example, we'll make a table that has strings in it.
grove = {
  "Big Smoke",
  "Ryder",
  "Sweet",
  "Carl Johnson"
}

Notice the braces, the braces are what means the value is a table. {} for example is an empty table. Commas are required between each value in the table, and line breaks are optional but often look nice and neat. Any values inside the {} are initial values in the table when the table is created. Now... how do we use or change these values? Well, simple!
grove[key] will get a value in the table "grove". a key is basically a part of a table that a value is stored in. That may of sounded confusing... but it's quite simple once you get the idea of it. Let's dive into an example.
grove[1]. 1 is the key here. grove[1] will get the 1st "element" in the "grove" table. "element" is basically the technical term for a value in a table. when we make a table by putting values in {} the values are given keys starting from one. for example...
grove = {
  "Big Smoke", -- key 1
  "Ryder", -- key 2
  "Sweet", -- key 3
  "Carl Johnson" -- key 4
}

Simple enough. Often a "key" can be called an "index" which is fairly accurate too, but in lua it's technically called a key. We can use keys to get parts of a table, we can use the values or change them like this.
TextPrintString(grove[3],1,1) -- prints "Sweet".
grove[4] = "Busta" -- changes the value of the 4th element in "grove".
grove[6] = "New guy" -- Makes a new value in the table, the 6th element in the table. 5 is nothing still (nil)

So basically... a key can get a value in a table, the number of the key is which value in the table we get. 1st value... 6th value... whatever. But oh wait... there's more to it. Keys can be strings too!

ourTable = {
  aValue = "hello",
  secondValue = "shit",
  satan = 666,
  donut = "you can use multiple types of values in the same table, numbers and strings for example"
}

That is how we can initialize a table with string keys. It sort of looks like named variables inside the table.
To access them, it's quite simple.
ourTable["aValue"] -- the key is "aValue". which in our table, "hello" is at that key.

TextPrintString(ourTable["satan"],1,1) -- prints 666
ourTable["donut"] = "donuts are good"

Simple enough right? There is one more thing... we can also access these values with a dot . instead of ["key"]

TextPrintString(ourTable.satan,1,1) -- prints 666
ourTable.aValue = "roar"

Tables of tables:
Now let's dive into a new idea... tables of tables. This is very possible, you can make as many tables as you want in other tables. I'm just going to give a bunch of examples for this one.
cheese = {
  {"yo","hello","shit"},
  {"Mick3Mouse","is","a","faggot","but","is","still","my","friend"}
}

cheese[1] -- The first table in "cheese".
cheese[2] -- The second table in "cheese".
cheese[1][2] -- The second value in the first table of "cheese".

menu = {
   {name = "weapon selector",func = F_WeaponSelector},
   {name = "model selector",func = F_ModelSelector}
}

menu[2].name -- "model selector"
menu[1]["name"]() -- calls F_WeaponSelector.

Tables are copied by reference, not value:
As we know, assinging a variable to the value of another variable will copy the value of the variable by value. That may of just confused you but I'll show you what I mean.
apple = 46
orange = apple
apple = 50.

After those 3 lines, orange is still 46. apple was changed to 50, but that doesn't change orange. In the second line, apple was copied to orange by value. This means the value of apple at the time of that code being executed was copied to orange, and that's it.
Copying by reference will make a variable point to the other variable's value, it won't have it's own value but just always be the same value as the other variable as that variable is just a reference. In LUA, we cannot control when we copy by value or by reference. Tables are copied by reference, other types by value. Variables that refer to functions aren't really copied by value either but that's a little different and you shouldn't have to worry about that. Let's get into an example of what this effect does to us.

table1 = {"donut","cheese","apple"}

Obviously, table1[2] is "cheese".

table2 = table1

Okay, so we made a new table called table2 and made it equal table1. So... basically we copied the value of table1 to table2 right? No. Now table2 is a reference to table1. Watch this. (Or... read this)

table2[2] is "cheese", as you would expect.
table2[2] = "orange"

We changed the value of the 2nd element in "table2". But table2 isn't it's own table, it was copied by reference and thus only refers to table1. So changes to table2's values change table1 as well. Now table1[2] is also "orange"!
This can be used in positive ways if you just acknowledge that is exists. The biggest time this effect is shown is when you pass a table as an argument to a function. If the function interacts with it, it's value is changed as the argument/parameter is just a reference. Normally a function wouldn't be able to do anything to your value.
What if you want to copy a table by value though? Well you have to do it yourself. I have made a function that does this.

function CopyTable(t)
   local newTable = {}
   for key,value in pairs(t) do
      if type(value) ~= "table" then
         newTable[key] = value
      else
         newTable[key] = CopyTable(value)
      end
   end
   return newTable
end

Basically just put that function in your code, then we can do this:

table1 = {"donut","cheese","apple"}
table2 = CopyTable(table1)
table2[2] = "orange"

and table1[2] will still be cheese.

Table functions:
There are a few functions for interacting with tables as well. Here are a few.
table.insert(table,value) -- insert a new value into a table. will insert it in the key after the last key. I'm not sure how this works with tables that have string keys, I've only used it with numbered tables.
table.remove(table,key) -- remove a value in a table.
table.getn(table) -- basically get the size.
table.setn(table,n) -- set what the script sees the table's size as.



Bully Stuff
If you've made it this far, you are now ready to move onto making Bully mods with the language.
Ped IDs, Vehicle IDs: I may of said before a ped is a type. This isn't true though, the value returned by PedCreateXYZ for example is just a number. It is not a special type in LUA. It is a number, that refers to the ped. This number is technically called the ped's ID. Or for vehicles, the vehicle ID. Now you may be thinking... "but I thought a ped ID was the value in default.ide, like 134 for pete". Well... many people call that number an ID which sort of makes sense, but it's not technically accurate. Technically an ID is the number that refers to a ped, the value returned by PedCreateXYZ for example. It is also the value that is used for interacting with peds. So we can make a variable equal the value to simply keep track of the ped and use the ped. The number in default.ide is actually technically called the ped's index. Most people (including myself sometimes) say Ped ID when we mean Ped Index though, so if you hear Ped ID it's often safe to assume they mean index.
Update: The index actually can be called an ID, and people who call it that aren't wrong. But... it isn't a ped ID. I'll clarify the difference: A model id is the numbers in default.ide, also called indexes, or for peds the ped index. However a ped id is different, and not to be confused with model id. A ped id is the number referring to the ped in the world.

Functions: You know how functions work now. The biggest part of modding Bully is calling Bully's functions. They are what have the most effect in the game. Here is a useful function that servers well as an example: PedCreateXYZ.
The parameters are (pedModel, X coord, Y coord, Z coord) and it returns the ped created.
local ped = PedCreateXYZ(37,270,-110,6)
pedModel is a number, that represents a ped model. Coordinates are pretty simple. In Bully, X is left/right (West/East), Y is forward/backward (North/South) and Z is up/down.
Bully has so many functions, and to get them you can look in the Bully.exe using a hex editor or text editor. It will look pretty messed up at first, but you can find function names inside it. Sadly it will not tell you the parameters or return values of the functions, that is up to us modders to figure out. Try to guess what the name of a function may be and search for it inside the bully.exe file. Hopefully you will be at least a little close and be able to find the function you are looking for. Let's say we want to set a ped's health. We should figure that "health" would be in the name of the function. So search "health" inside of the Bully.exe. Eventually you should find PedSetHealth. It takes 2 parameters, the ped (Ped types were discussed earlier) and a number for the new amount of health. Here is an example to add 20 health to the player.
PedSetHealth(gPlayer,PedGetHealth(gPlayer) + 20)

Along with bully functions you can call, there are 3 that you can create that Bully will call. As discussed in the example, MissionSetup, main, and MissionCleanup. If it is a mission script like ArcRace1.lur it will call MissionSetup, main, then MissionCleanup. If it is not a mission it will call just main.

You can also find some Bully functions here. It doesn't have that many but I add to it occasionally...



Modding Other Scripts
Modding ArcRace1 is perfect because you can replace all of it's code without effecting any other parts of Bully except for ArcRace. If you want to mod the game in free roam (Without having to mod ArcRace/a mod that runs during missions) then I suggest modding STimeCycle.lur. The game needs some code in that script to work however, so you should add onto the source of that file and recompile instead of replacing all of the code. The source code is available here thanks to MadmaN for releasing the source of STimeCycle.lua. You can mod many other scripts as well, go ahead and experiment!



Other Stuff
There are some things I didn't yet cover but should be discussed.

Function Type: A function is a type. You can store a function as a variable. Example:

function DoStuff()
  AreaTransitionXYZ(0,270,-110,6)
  PedSetHealth(gPlayer,1000)
  PedSetWeapon(gPlayer,305,50)
  PedRecruitAlly(gPlayer,PedCreateXYZ(134,270,-108,6))
end

local var = DoStuff
var()

Other IMG Tools: There are more IMG tools then just MadmaN's that you can use.
IMG Tool 2: This was meant for GTA 3 and it does not work well for Bully and I advise you do not use it. Some people do anyway...
MadmaN's IMG Toolkit: This one is probably the best choice. It works the best, but only runs through a console. It does not have a GUI. [Download]

Compiling: There are more ways to compile LUA scripts then by dragging it over LuaC.exe. They are in the ReadMe file of the LuaC release. It is through using the command line.

Making a Batch File: Making a simple batch file for LUA scripts can help save a LOT of time. LuaC.exe and IMG Toolkit V1 both have options for the command line. You can make a batch file to compile your LUA script and put it into the Scripts.img in a fraction of a second. A batch file works just like the command line except it runs multiple commands without you having to type anything yourself. Learn more here.



Conclusion
Hopefully you have learned something from this tutorial. Good luck making mods! If you have any questions post them here, in the modding questions & help section, or in requests. If you make a mod you want to release to the public you can upload it here or on some other site and share in the releases section. Add me on XFire or Skype (DaBOSS54320) if you want to talk to me personally.
This tutorial just teaches you the basics and core of LUA modding. There is a lot more to learn, look around the forums here to learn more.
Also you can learn a lot related more closely to Bully from reading c00l's tutorial and the official LUA 5.0 documentation, though note that Bully does not support everything the official documentation discusses.
Also check out BullyMods.net It has modding discussion as well.
« Last Edit: July 06, 2015, 06:57:36 PM by DaBOSS54320 »

Offline GreenOmnitrix

  • XFire: jedijosh920
  • Full Member
  • ***
  • Posts: 292
  • Gender: Male
  • 欺負改裝是真棒
    • View Profile
    • YouTube Channel
Re: LUA Tutorial
« Reply #1 on: August 24, 2014, 10:25:24 PM »
Very nice indeed!

Offline DaBOSS54320

  • Hero Member
  • ****
  • Posts: 3,398
  • Gender: Female
    • View Profile
Re: LUA Tutorial
« Reply #2 on: March 16, 2015, 12:18:53 PM »
Hey everyone. I'm considering making video tutorials for LUA modding, but how many would actually appreciate it and find it useful? Reply if you would/wouldn't watch my video tutorials, and if I get enough then I'll be happy to make some. I'd make some first covering the basics and mechanics of LUA, then how to make individual types of mods, such as missions or styles.

Offline AfterLife

  • The Reaper
  • Sr. Member
  • ***
  • Posts: 830
  • Gender: Male
  • I'm from the AfterLife...
    • View Profile
Re: LUA Tutorial
« Reply #3 on: March 18, 2015, 10:15:57 PM »
Hey everyone. I'm considering making video tutorials for LUA modding, but how many would actually appreciate it and find it useful? Reply if you would/wouldn't watch my video tutorials, and if I get enough then I'll be happy to make some. I'd make some first covering the basics and mechanics of LUA, then how to make individual types of mods, such as missions or styles.
Make it! It will be helpful for newbies.

deadpoolXYZ

  • Guest
Re: LUA Tutorial
« Reply #4 on: March 19, 2015, 02:07:20 PM »
Your current tutorials are already very useful but maybe some new modders will find it easier to learn from video tutorials.
Swegta is the only person I know who has uploaded videos about this but they only cover the basic things so you should give it a try, and then teach the advanced stuff (like tables, calling functions, basic menus, etc).
« Last Edit: March 19, 2015, 02:09:48 PM by The Dream Is Dead »

Offline DaBOSS54320

  • Hero Member
  • ****
  • Posts: 3,398
  • Gender: Female
    • View Profile
Re: LUA Tutorial
« Reply #5 on: March 19, 2015, 02:33:41 PM »
I want to but I just hope enough people will actually try it and want to learn from it. If I do, it'll cover all the basics and advanced things that can be used for Bully. Everything from basic variables to fully taking advantage of big tables and more. I think there'd be 3 series: Bully Script Modding: The Basics | Bully Script Modding: Advanced Topics | Bully Script Modding: Examples. The basic and advanced ones are self explanatory, then with examples I may script a few example scripts in full on video with commentary explaining it.

Offline gamerzod

  • Jr. Member
  • **
  • Posts: 17
    • View Profile
Re: LUA Tutorial
« Reply #6 on: May 21, 2015, 07:20:57 AM »
about the control flow, repeat, while, do-while, for
do I need to put an 'end' code behind all that control flow code? and can i use the 'repeat' code at the MissionSetup = function() or it just only work with the function main() ?
and what is createthread() is it the same with 'repeat' code?

deadpoolXYZ

  • Guest
Re: LUA Tutorial
« Reply #7 on: May 21, 2015, 08:29:28 AM »
about the control flow, repeat, while, do-while, for
do I need to put an 'end' code behind all that control flow code? and can i use the 'repeat' code at the MissionSetup = function() or it just only work with the function main() ?
and what is createthread() is it the same with 'repeat' code?

This is how it should look like... I'm not explaining it since DaBoss already did in thi thread.

Code: [Select]
if SOMETHINGHAPPENS then
YOURCODE
end

while SOMETHINGISHAPPENING do
YOURCODE
Wait(0)
end

repeat
YOURCODE
Wait(0)
until SOMETHINGHAPPENS

for count = MINNUMBER, MAXNUMBER do
YOURCUDE
end

You can use the repeat everywhere in your script but I recommend using it in the main function since the MissionSetup should be for code that only executes once.

createthread("NAMEOFTHETHREAD") starts a function (which you should have created previously or the game might crash) that runs at the same time as the main code from your script. This is useful if you need to check constantly if something is working like it should without messing with the main function.

Offline gamerzod

  • Jr. Member
  • **
  • Posts: 17
    • View Profile
Re: LUA Tutorial
« Reply #8 on: May 21, 2015, 08:59:56 AM »
thank you
but where can i put the createthread("NAMEOFTHETHREAD"), is it in the MissionSetup? or can i put it in function main() ? where should be the best that I put the createthread?
and you say about constanly, is it mean createthread() the same with repeat code?

Offline DaBOSS54320

  • Hero Member
  • ****
  • Posts: 3,398
  • Gender: Female
    • View Profile
Re: LUA Tutorial
« Reply #9 on: May 21, 2015, 01:15:42 PM »

Offline DaBOSS54320

  • Hero Member
  • ****
  • Posts: 3,398
  • Gender: Female
    • View Profile
Menu Tutorial
« Reply #10 on: June 06, 2015, 12:16:21 AM »
I may start posting example scripts with comments in them. I think this is a nice way for people to learn... if you guys find this useful, tell me and I will continue. I realize the bully modding community isn't as active but... for the people that are active, this may help: and maybe tutorials will teach people some things that inspire them to make a mod using the things they learned?

Edit: I'll probably just make separate tutorial topics for smaller ideas. Just makes a bit more sense... and makes things easier to find.

I suggest copying the code to notepad++ and reading it there.

Code: [Select]
-- Compile to ArcRace1.lur to try this script out.

function MissionSetup()
AreaTransitionXYZ(0,270,-110,6) -- If there isn't an AreaTransitionXYZ in the MissionSetup, the script won't launch.
TextPrintString("Menu test",1,1)
Wait(1000)
end

function main()
menu_example_never_ending()
end

-- simple menu. read and understand this before moving on to the next function.
function menu_example_single_table()
local s = 1 -- s for keeping track of where in the menu we are
local options = { -- make a table named "options". contents of the table in { and }, seperated with commas, optionally a linebreak.
"Menu option 1",
"Menu option 2",
"Cheese",
"Donutz",
"Mick3Mouse sux",
"A pointless menu option",
"last menu option"
}
repeat -- repeat, so we can detect button presses and keep the script from moving past the menu
if IsButtonBeingPressed(0,0) then -- press left
s = s - 1 -- decrement s by 1
if s < 1 then -- if "s" is lower than 1, go to the end of the table. (table.getn is size of a table)
s = table.getn(options) -- setting "s" to the end of the menu makes the menu loop, and prevents us from selecting an invalid option.
end
elseif IsButtonBeingPressed(1,0) then -- press right
s = s + 1 -- increment s by 1
if s > table.getn(options) then -- if "s" is higher than the number of options (table.getn is size of a table)
s = 1 -- then make "s" 1, so the menu loops back to the start.
end
end
local option = options[s] -- the option we're currently on. [s] means get the "s"th value of "options". for example if s is 4, options[s] would be the 4th value in the "options" table.
TextPrintString(option,0,1) -- print the option we're currently on. we could also just print "options[s]" and it'd have the same effect.
--TextPrintString(options[s],0,1) -- like this. this is probably easier, but i wanted to explain both parts above.
Wait(0)
until IsButtonBeingPressed(7,0) -- end the repeat loop, ending selection of an item. "s" will be where in the menu we were, and what we selected.
-- this part of the script will not be reached until the repeat loop is finished (so when button 7 is pressed).
TextPrintString(options[s].." was selected.",1,1)
end

-- better menu, understand the concepts in the above menu first though or this may confuse you.
function menu_example_big_table()
--[[ This time around, we're going to use a lot of the same concepts...
but instead of putting strings in our "options" table we're going to put more tables.
so "options" will be a table... of tables.
the point of this is so each option can have more information to it, like a name and model, etc.
]]
local s = 1
local options = { -- so instead of strings, we just put more tables. remember, commas separate the values in a table, so a comma after each table, but not the last one as no more tables are past it.
{"Spud Gun",305},
{"Bottle Rocket",307},
{"Baseball Bat",300},
{"Firecracker",301}
}
repeat
if IsButtonBeingPressed(0,0) then
s = s - 1
if s < 1 then
s = table.getn(options)
end
elseif IsButtonBeingPressed(1,0) then
s = s + 1
if s > table.getn(options) then
s = 1
end
end

local option = options[s] -- the currently selected option, just like last time. But this time "option" isn't going to be a string. it's going to be another table, since tables are the values in "options".
local name = option[1] -- [1], just like [s], gets a part of the table. specifically, the first value of the table. not of the "options" table though, but of "option" since we did "option[1]" not "options[1]". "option" of course is the table on the previous line, so the table we currently are selecting in the "options" table.
local weap = option[2] -- [2], just like above, but the 2nd value of our currently selected table.

TextPrintString(weap.." - "..name,0,1)

Wait(0)
until IsButtonBeingPressed(7,0) -- "options" and "s" retain their values, so we can use them now.
PedSetWeapon(gPlayer,options[s][2],50)
TextPrintString("Player given a "..options[s][1],1,1)
end

-- This shows a simple idea, understand the above 2 menus first.
function menu_example_never_ending()
-- We dont need the repeat loop to end if we don't want it too, however we then will have to put something in the loop to use our menu.
local s = 1
local options = {
{"Spud Gun",305},
{"Bottle Rocket",307},
{"Baseball Bat",300},
{"Firecracker",301}
}
repeat
if IsButtonBeingPressed(0,0) then
s = s - 1
if s < 1 then
s = table.getn(options)
end
elseif IsButtonBeingPressed(1,0) then
s = s + 1
if s > table.getn(options) then
s = 1
end
elseif IsButtonBeingPressed(7,0) then
PedSetWeapon(gPlayer,options[s][2],50)
end

TextPrintString(options[s][2].." - "..options[s][1],0,1)

Wait(0)
until false -- until false, meaning until never. the repeat loop will only stop once the value after "until" is true. before we had a function, and the function returned true/false. only if the button was pressed would it return true. now we dont dont it to ever end though.
end

--[[ Buttons used:
  0 - Objective left (typically, left arrow key or left d-pad)
  1 - Objective right (typically, left arrow key or left d-pad)
  7 - Sprint (I think usually alt key, or A on xbox, cross on playstation)
 
Functions used:
  PedSetWeapon(ped,weaponID,ammo) - gives a ped a weapon
  TextPrintString(string,time (seconds),location) - print a string. 0 seconds means 1 frame. location is 1 or 2. 1 = top, 2 = bottom
  IsButtonBeingPressed(button,controller) - returns true/false for if a button was just pressed. controller is 0 for player 1. a keyboard is counted as a controller if its used to play, and would also be 0.
  Wait(ms) -- pause the script for the specified amount of milliseconds. 0 means 1 frame.
 
What's a string?
  I talked about strings a bit. A string is basicly text. Some examples:
  "this is a string"
  local donut = "hi" -- this is a variable, with a string value.
  TextPrintString("hello",1,1) -- this prints a string.
  TextPrintString(donut,1,1) -- this prints "hi" because it's the value of our "donut" variable.
  local cookie = "hello ".."there" -- .. concatenates (combines) 2 strings into 1. so the value of this is "hello there"
 
What's gPlayer?
  gPlayer is a variable that refers to the player. g I would think stands for global.
 
Weapon ID:
  Weapons have certain numbers that refer to them. 305 for example as seen earlier is the spudgun.
  Check /Bully Folder/Objects/default.ide (open in a text editor) and scroll down to find weapons.
]]


Unrelated to this menu tutorial (just didn't want to double post), I edited the main post/tutorial a bit. Mainly I just fixed a problem with saying that peds were types (they aren't, they are just numbers) and also entirely redid the tables section. I suggest you re-read the "Tables" section. Before I updated it, I originally had a bunch of bullshit about "Lists" in there that I heard from someone before but then found out is just sorta bs. I updated it to have more accurate information now.
« Last Edit: June 06, 2015, 09:20:33 PM by DaBOSS54320 »

Offline AfterLife

  • The Reaper
  • Sr. Member
  • ***
  • Posts: 830
  • Gender: Male
  • I'm from the AfterLife...
    • View Profile
Re: Menu Tutorial
« Reply #11 on: June 06, 2015, 04:58:35 AM »
I may start posting example scripts with comments in them. I think this is a nice way for people to learn... if you guys find this useful, tell me and I will continue. I realize the bully modding community isn't as active but... for the people that are active, this may help: and maybe tutorials will teach people some things that inspire them to make a mod using the things they learned?

I suggest copying the code to notepad++ and reading it there.

Code: [Select]
-- Compile to ArcRace1.lur to try this script out.

function MissionSetup()
AreaTransitionXYZ(0,270,-110,6) -- If there isn't an AreaTransitionXYZ in the MissionSetup, the script won't launch.
TextPrintString("Menu test",1,1)
Wait(1000)
end

function main()
menu_example_never_ending()
end

-- simple menu. read and understand this before moving on to the next function.
function menu_example_single_table()
local s = 1 -- s for keeping track of where in the menu we are
local options = { -- make a table named "options". contents of the table in { and }, seperated with commas, optionally a linebreak.
"Menu option 1",
"Menu option 2",
"Cheese",
"Donutz",
"Mick3Mouse sux",
"A pointless menu option",
"last menu option"
}
repeat -- repeat, so we can detect button presses and keep the script from moving past the menu
if IsButtonBeingPressed(0,0) then -- press left
s = s - 1 -- decrement s by 1
if s < 1 then -- if "s" is lower than 1, go to the end of the table. (table.getn is size of a table)
s = table.getn(options) -- setting "s" to the end of the menu makes the menu loop, and prevents us from selecting an invalid option.
end
elseif IsButtonBeingPressed(1,0) then -- press right
s = s + 1 -- increment s by 1
if s > table.getn(options) then -- if "s" is higher than the number of options (table.getn is size of a table)
s = 1 -- then make "s" 1, so the menu loops back to the start.
end
end
local option = options[s] -- the option we're currently on. [s] means get the "s"th value of "options". for example if s is 4, options[s] would be the 4th value in the "options" table.
TextPrintString(option,0,1) -- print the option we're currently on. we could also just print "options[s]" and it'd have the same effect.
--TextPrintString(options[s],0,1) -- like this. this is probably easier, but i wanted to explain both parts above.
Wait(0)
until IsButtonBeingPressed(7,0) -- end the repeat loop, ending selection of an item. "s" will be where in the menu we were, and what we selected.
-- this part of the script will not be reached until the repeat loop is finished (so when button 7 is pressed).
TextPrintString(options[s].." was selected.",1,1)
end

-- better menu, understand the concepts in the above menu first though or this may confuse you.
function menu_example_big_table()
--[[ This time around, we're going to use a lot of the same concepts...
but instead of putting strings in our "options" table we're going to put more tables.
so "options" will be a table... of tables.
the point of this is so each option can have more information to it, like a name and model, etc.
]]
local s = 1
local options = { -- so instead of strings, we just put more tables. remember, commas separate the values in a table, so a comma after each table, but not the last one as no more tables are past it.
{"Spud Gun",305},
{"Bottle Rocket",307},
{"Baseball Bat",300},
{"Firecracker",301}
}
repeat
if IsButtonBeingPressed(0,0) then
s = s - 1
if s < 1 then
s = table.getn(options)
end
elseif IsButtonBeingPressed(1,0) then
s = s + 1
if s > table.getn(options) then
s = 1
end
end

local option = options[s] -- the currently selected option, just like last time. But this time "option" isn't going to be a string. it's going to be another table, since tables are the values in "options".
local name = option[1] -- [1], just like [s], gets a part of the table. specifically, the first value of the table. not of the "options" table though, but of "option" since we did "option[1]" not "options[1]". "option" of course is the table on the previous line, so the table we currently are selecting in the "options" table.
local weap = option[2] -- [2], just like above, but the 2nd value of our currently selected table.

TextPrintString(weap.." - "..name,0,1)

Wait(0)
until IsButtonBeingPressed(7,0) -- "options" and "s" retain their values, so we can use them now.
PedSetWeapon(gPlayer,options[s][2],50)
TextPrintString("Player given a "..options[s][1],1,1)
end

-- This shows a simple idea, understand the above 2 menus first.
function menu_example_never_ending()
-- We dont need the repeat loop to end if we don't want it too, however we then will have to put something in the loop to use our menu.
local s = 1
local options = {
{"Spud Gun",305},
{"Bottle Rocket",307},
{"Baseball Bat",300},
{"Firecracker",301}
}
repeat
if IsButtonBeingPressed(0,0) then
s = s - 1
if s < 1 then
s = table.getn(options)
end
elseif IsButtonBeingPressed(1,0) then
s = s + 1
if s > table.getn(options) then
s = 1
end
elseif IsButtonBeingPressed(7,0) then
PedSetWeapon(gPlayer,options[s][2],50)
end

TextPrintString(options[s][2].." - "..options[s][1],0,1)

Wait(0)
until false -- until false, meaning until never. the repeat loop will only stop once the value after "until" is true. before we had a function, and the function returned true/false. only if the button was pressed would it return true. now we dont dont it to ever end though.
end

--[[ Buttons used:
  0 - Objective left (typically, left arrow key or left d-pad)
  1 - Objective right (typically, left arrow key or left d-pad)
  7 - Sprint (I think usually alt key, or A on xbox, cross on playstation)
 
Functions used:
  PedSetWeapon(ped,weaponID,ammo) - gives a ped a weapon
  TextPrintString(string,time (seconds),location) - print a string. 0 seconds means 1 frame. location is 1 or 2. 1 = top, 2 = bottom
  IsButtonBeingPressed(button,controller) - returns true/false for if a button was just pressed. controller is 0 for player 1. a keyboard is counted as a controller if its used to play, and would also be 0.
  Wait(ms) -- pause the script for the specified amount of milliseconds. 0 means 1 frame.
 
What's a string?
  I talked about strings a bit. A string is basicly text. Some examples:
  "this is a string"
  local donut = "hi" -- this is a variable, with a string value.
  TextPrintString("hello",1,1) -- this prints a string.
  TextPrintString(donut,1,1) -- this prints "hi" because it's the value of our "donut" variable.
  local cookie = "hello ".."there" -- .. concatenates (combines) 2 strings into 1. so the value of this is "hello there"
 
What's gPlayer?
  gPlayer is a variable that refers to the player. g I would think stands for global.
 
Weapon ID:
  Weapons have certain numbers that refer to them. 305 for example as seen earlier is the spudgun.
  Check /Bully Folder/Objects/default.ide (open in a text editor) and scroll down to find weapons.
]]


Unrelated to this menu tutorial (just didn't want to double post), I edited the main post/tutorial a bit. Mainly I just fixed a problem with saying that peds were types (they aren't, they are just numbers) and also entirely redid the tables section. I suggest you re-read the "Tables" section. Before I updated it, I originally had a bunch of bullshit about "Lists" in there that I heard from someone before but then found out is just sorta bs. I updated it to have more accurate information now.
Can you give me a style selector template with action nodes? I can't figure out how to add nodes and strafe to my custom style selector.

Offline DaBOSS54320

  • Hero Member
  • ****
  • Posts: 3,398
  • Gender: Female
    • View Profile
Re: LUA Tutorial
« Reply #12 on: June 06, 2015, 06:19:47 AM »
Can you give me a style selector template with action nodes? I can't figure out how to add nodes and strafe to my custom style selector.

Code: [Select]
function AnimationSelector()
local s = 1
local nodes = {
{"/Global/6_02/HeadButt/HeadButt_AnticStart","Act/Conv/6_02.act","Headbutt"},
{"/Global/BOSS_Darby/Offense/Short/Grapples/HeavyAttacks/Catch_Throw","Act/Anim/BOSS_Darby.act","6 Hits"}
}

while true do
if IsButtonBeingPressed(0,0) then
s = s - 1
if s < 1 then
s = table.getn(nodes)
end
elseif IsButtonBeingPressed(1,0) then
s = s + 1
if s > table.getn(nodes) then
s = 1
end
elseif IsButtonBeingPressed(3,0) then
PedSetActionNode(gPlayer,nodes[s][1],nodes[s][2])
end
Wait(0)
end
end

Offline AfterLife

  • The Reaper
  • Sr. Member
  • ***
  • Posts: 830
  • Gender: Male
  • I'm from the AfterLife...
    • View Profile
Re: LUA Tutorial
« Reply #13 on: June 06, 2015, 09:00:23 AM »
Can you give me a style selector template with action nodes? I can't figure out how to add nodes and strafe to my custom style selector.

Code: [Select]
function AnimationSelector()
local s = 1
local nodes = {
{"/Global/6_02/HeadButt/HeadButt_AnticStart","Act/Conv/6_02.act","Headbutt"},
{"/Global/BOSS_Darby/Offense/Short/Grapples/HeavyAttacks/Catch_Throw","Act/Anim/BOSS_Darby.act","6 Hits"}
}

while true do
if IsButtonBeingPressed(0,0) then
s = s - 1
if s < 1 then
s = table.getn(nodes)
end
elseif IsButtonBeingPressed(1,0) then
s = s + 1
if s > table.getn(nodes) then
s = 1
end
elseif IsButtonBeingPressed(3,0) then
PedSetActionNode(gPlayer,nodes[s][1],nodes[s][2])
end
Wait(0)
end
end
No. That is not what I meant. I mean a style selector with menues like DeadPool's mod. I can get the menu to work, but not the NODES.

Offline DaBOSS54320

  • Hero Member
  • ****
  • Posts: 3,398
  • Gender: Female
    • View Profile
Re: LUA Tutorial
« Reply #14 on: June 06, 2015, 09:45:02 AM »
No. That is not what I meant. I mean a style selector with menues like DeadPool's mod. I can get the menu to work, but not the NODES.

Oh... I've done that a few times before for some other people. If only I saved it... oh well I'll do it again, I didn't test this code but it should work.

Code: [Select]
function AnimationSelector()
 local s = 1
 local currentStyleFunc = nil
 local styles = {
  {name = "Darby BOSS",func = STYLE_BOSS_Darby,tree = "BOSS_Darby"},
  {name = "P Grappler A",func = STYLE_P_Grappler_A,tree = "P_Grappler_A"}
 }
 
 while true do
  if IsButtonBeingPressed(0,0) then
   s = s - 1
   if s < 1 then
    s = table.getn(styles)
   end
  elseif IsButtonBeingPressed(1,0) then
   s = s + 1
   if s > table.getn(styles) then
    s = 1
   end
  elseif IsButtonBeingPressed(3,0) then
   if styles[s].style ~= nil then
    PedSetActionTree(gPlayer,"/Global/"..styles[s].tree,"Act/Anim/"..styles[s].tree..".act")
   end
   currentStyleFunc = styles[s].func
  end
  currentStyleFunc()
  TextPrintString(s..") "..styles[s].name,0,1)
  Wait(0)
 end
end

function STYLE_BOSS_Darby()
 if IsButtonBeingPressed(6,0) then
  PedSetActionNode(gPlayer,"/Global/BOSS_Darby/Offense/Short/Grapples/HeavyAttacks/Catch_Throw","Act/Anim/BOSS_Darby.act")
 end
end

function STYLE_P_Grappler_A()
 if IsButtonBeingPressed(6,0) then
  PedSetActionNode(gPlayer,"/Global/P_Grappler_A/Offense/Medium/Strikes/Kick","Act/Anim/P_Grappler_A.act")
 end
end