Sorry if I'm pestering you by asking so many questions. But now the mission just ends right away saying "You Passed!" and no fighting actually happens. I tried looking on Bully Board but could not find anything. I even tried looking a lua tutorial but didn't find anything. Do you know how to make it so that when all the Preppy peds I included are passed out, that results in the end of the mission? Thanks for the help.
You're not pestering me, I actually like to see new modders who want to learn.
How the script runs...Notice your script is divided up into functions, those functions are basically bits of code that won't run until the function is called. You can call a function by putting its name followed by (), or some functions will also get called by Bully. In your case with this ArcRace1.lur script, Bully will first call the MissionSetup function, followed by main, and then MissionCleanup when the mission/script ends (when the player dies, gets busted, or when the main function ends).
Those are the only 3 functions Bully will actually call on its own, and they're all meant for a specific purpose.
MissionSetup: This is meant to set your mission up, doing things such as setting the player position or spawning peds.
main: This is where your script will spend most of its time, and it shouldn't end until you want the mission to be over.
MissionCleanup: This is meant to clean things up, move the player back somewhere after the mission is done, or whatever else.
When I say "it shouldn't end until you want the mission to be over", note that a function will end as soon as the script gets to the bottom of it, after it runs all the code inside it. To stop it from ending, we'll need to make a loop. I notice you already have one but it's not very useful how you have it set up. Try something more like this...
main = function()
while true do
Wait(0)
end
end
This is called a while loop. It will run the code between "do" and "end" repeatedly while the condition between "while" and "do" evaluates to true. I explicitly put the value true there so it will just keep running, however you could also do other things to get whether or not it should be running. However we want it to only run while the peds are alive, so we'll have to do something a bit more.
Making sure the peds are aliveYou could call a function, such as PedIsDead. This function is created by Bully and for you to use whenever you want. The function returns true if the specified ped is dead, and false otherwise. You specify the ped you want to check inside the () when calling it. However before we do that... realize that what we want is for the loop to keep running while the ped is NOT dead. Well, we can use "not" to invert the value of the boolean (a boolean value is just a value that is true or false). So if we do this: "not PedIsDead(ped)" and ped is not dead, PedIsDead will still end up returning false, but the "not" will flip that to "true" instead, so the value we end with is "true".
So now we can add that to the main loop as well...
main = function()
while not PedIsDead(Peanut) do
Wait(0)
end
end
But.... another problem. We have more than one ped we want to check! No fear, we can simply use "or". "or" will just make sure one of the two values are true, and return true if so and false otherwise. Some examples...
(true or true) -- this evaluates to true
(true or false) -- this evaluates to true
(false or false) -- this evaluates to false
Notice we end with "true" as long as one of the two are true, and as before, we can also use function calls to get those true/false values and even use "not". We can also use multiple "or"s!.
So we could do this now:
main = function()
while not PedIsDead(Peanut) or not PedIsDead(Hal) or not PedIsDead(Johnny) or not PedIsDead(Lefty) or not PedIsDead(Lucky) or not PedIsDead(Vance) or not PedIsDead(Ricky) or not PedIsDead(Norton) or not PedIsDead(Tad) or not PedIsDead(Chad) or not PedIsDead(Bif) or not PedIsDead(Justin) or not PedIsDead(Byrce) or not PedIsDead(Darby) or not PedIsDead(Parker) do
Wait(0)
end
end
And this will keep the "main loop" (what we call the while loop when it's the main loop of the script) running as long as any of the peds are still alive (not dead).
WaitingThis isn't actually related to your problem but I still would like to discuss it so you understand how it works. All we actually do in the while loop is wait... but what does waiting do? Well "Wait" is a function we can call to suspend our script for a certain amount of time so that Bully can run the other scripts and also do other things in the game. It's very important to wait because if we don't, the game will be stuck in our script, causing the game to freeze. The time to wait is specified in milliseconds. If we use 0 (like we do here) it will only wait a single frame, meaning if we just keep waiting 0ms, the script will continue to run only once every frame. Here's basically how we have our main loop set up to work:
1. Check if any of the peds are still alive, if so, continue to #2, otherwise #3.
2. Wait for a single frame, then return to #1 (the while loop will keep running until the expression between "while" and "do" ends up being "false".
3. The while loop ended, then the function ends, meaning the whole mission will now end.
So we basically check every single frame that the peds are alive. And that's really all there is to it, nothing else you have to do here, just wanted to explain how Wait works and why we have to use it. All we do in this while loop is wait since we don't need to really do anything else, but if you wanted other code to run each frame, you could put it inside the while loop along with Wait. Put it before Wait if you want it to run before the script suspends and a frame is passed, or after if you want it after. Very important for timing things later on as you'll see in future projects.
Ped validityWhen you spawn a ped using the PedCreateXYZ function, it is said to be a mission ped, meaning it will not be deleted when you go far away from it like most peds. However it WILL be deleted after it is killed for a while, fading away like all the other peds. This is actually a problem for us since if we check if an invalid (deleted) ped is dead, the script will actually crash! (the game won't crash, but the script would stop running). This is because it doesn't even realize the ped is invalid before it checks if they are dead, so it fails to get if they are actually dead since they don't even exist. We can fix this by using the PedIsValid function. This function works much like PedIsDead. It returns true while the ped is valid (even if they are dead, just not deleted), and false otherwise. So we'll have to check that each ped is valid BEFORE checking if they are dead. If a ped is invalid, we can assume they're dead or at least out of the fight now so we can count them dead. So we'll want to modify our script to keep the main loop running while any of the peds are valid AND not dead. We can do this using "and", it works a lot like "or" only it checks that BOTH are true instead of either.
main = function()
while PedIsValid(Peanut) and not PedIsDead(Peanut) do
Wait(0)
end
end
For the sake of simplicity here I only put Peanut here for now, but you'd also put together all the other peds with it. But before that... there are two more things we need to discuss.
Short circuitingSomething you may be thinking is that in the above example, we still check if Peanut is dead. So even if PedIsValid returns false, we'd still check. This is actually FALSE. If PedIsValid is false, PedIsDead is actually never called. This is because of a concept called short circuiting. It deals with both "and" and "or". It basically means that once "and"/"or" got it's answer, it won't waste processing power checking if the other operand is true/false since it already got its answer. I think this would best be explained through some examples...
true or false -- here, it first checks the left operand, "true". Since "or" only needs one value to be true, we already know it will evaluate to regardless of what the right operand is. So, the right one will not even be checked!
false and true -- here, it first checks the left operand, "false". Since "and" needs both values to be true and one of them already isn't, we already have our answer, so it won't even check the right operand.
true and false -- here is an example where it would actually need to check the right operand.
false or true -- and here is another. it doesn't already have it's answer from the left, so it needs to check the right.
Okay so what? A little speed boost? Well that's not the only way it affects us... because if we use functions...
true or PedIsDead(ped) -- here, it already got the answer from the left, so it never checks the right, the function won't even be called!
false or PedIsDead(ped) -- this example however, it will be called.
PedIsValid(ped) or PedIsDead(ped) -- here it will ONLY call PedIsDead if PedIsValid returns false, since if it returned true, we'd already have our answer.
PedIsDead(ped) or true -- even though we know this will always be true, the script always checks the left side first. So PedIsDead will be called here, but since we have "or true", it'll always end up being true anyways.
Because of this, it works fine when we do something like this:
PedIsValid(Peanut) and not PedIsDead(Peanut)
Because Peanut would have to be valid before PedIsDead gets called, and if he weren't valid, then PedIsDead would never be called.
The point of teaching you this is so you understand why order is important, especially with PedIsValid.
Order of operationsAlright so now we can check if peds are dead without the script crashing, so now we can make the full while loop, right? Just something like...
main = function()
while PedIsValid(Peanut) and not PedIsDead(Peanut) or PedIsValid(Hal) and not PedIsDead(Hal) or PedIsValid(Johnny) and not PedIsDead(Johnny) or PedIsValid(Lefty) and not PedIsDead(Lefty) or PedIsValid(Lucky) and not PedIsDead(Lucky) or PedIsValid(Vance) and not PedIsDead(Vance) or PedIsValid(Ricky) and not PedIsDead(Ricky) or PedIsValid(Norton) and not PedIsDead(Norton) or PedIsValid(Tad) and not PedIsDead(Tad) or PedIsValid(Chad) and not PedIsDead(Chad) or PedIsValid(Bif) and not PedIsDead(Bif) or PedIsValid(Justin) and not PedIsDead(Justin) or PedIsValid(Byrce) and not PedIsDead(Byrce) or PedIsValid(Darby) and not PedIsDead(Darby) or PedIsValid(Parker) and not PedIsDead(Parker) do
Wait(0)
end
end
Yeah, that'd work actually. But I would like to still teach you something related to why it works.
Let's say we have something like this:
x and y or z
How do we know if it'll check it like this:
x and (y or z)
Or like this:
(x and y) or z
Well, order of operations. Something you probably learned in math too. It exists in LUA too, "and" is evaluated before "or", so how we have our code it will work. But I still wanted to just explain this little bit in case it confused you while looking at the code.
Another note too, if you want "or" to evaluate first, you can type the () just like I did and it will evaluate first. So...
x and (y or z)
This will force the script to evaluate y or z first, then check "x and (whatever x or z evaluated to)".
Since "and" is by default evaluated before "or", something like this:
x and y or z
Is equal to this:
(x and y) or z
HOWEVER, it can still be a lot easier to read if we put the () anyways. This is up to you if you want to do it or not, it is not at all needed, but it will make things look a bit better. So we could have something like...
(PedIsValid(Peanut) and not PedIsDead(Peanut)) or (PedIsValid(Hal) and not PedIsDead(Hal))
That way each ped neatly gets their own (). But since we only use "and" in there anyways, the () are not needed at all. So the code is fine as it is now, but I thought it'd still be nice to teach you this.
Custom functionsHoly shit, can we address how fucking tedious it was for me to type that huge while loop? Very. The code works as it is now, but there is a better and neater way we could do it. Image it like this...
main = function()
while AnyPedIsAlive(Peanut,Hal,Johnny,Left,Lucky,Vance,Ricky,Norton,Tad,Chad,Bif,Justin,Bryce,Darby) do
Wait(0)
end
end
So much neater right? Well... sadly no function exists. But, we could make one! We start by making the function like any other, but we also add a few more things to it.
AnyPedIsAlive = function(...)
return value
end
The "..." means there can be any number of arguments (arguments are the values we put inside the () when calling the function. However this concept may be a bit hard for us to start on so let's instead look at a simple example of a custom function that takes two arguments, and returns one value.
addition = function(a,b)
local c = a+b
return c
end
test = addition(10,20)
It's a very simple layout, we first create the function like any other and put code in it like any other. The only thing new to you should be the parameters we put in (), and the return value. You may be wondering why I said parameters rather than arguments too... let me explain that real quick.
Parameters are the names of variables used inside a function.
Arguments are values that you give to those parameters when calling the function.
So "a" and "b" are parameters, and "10" and "20" are arguments. When we call the function like this "addition(10,20)", "a" gets filled in with the value 10, and "b" gets filled in with the value 20. Then it adds them together, storing the result in "c", then returning "c". We could of also done "return a+b" and it would of worked the same way. So when the function gets to that "return", it exits the function (so any code after return, will not run), and it returns the value after return (you don't have to specify a value after return, if you don't, then the function just won't return anything). This returned value works just like the true/false returned by PedIsDead. Except rather than using it with a while loop, we just store it in a variable called "test". So now "test" has the value 30. Simple enough right?
Local variablesShort side discussion too, you may be wondering what "local" means in that script, and why we didn't use it for "test". Well, "local" is a keyword that we use when we create a variable if we want it to be local to a certain function. This means it can only be used inside the function. If we don't specify "local", the variable will be global, meaning it can be used anywhere inside the script. For example if we did not make "c" local in that example, we'd be able to later use "c" and it'd still have 30. Or if we called "addition" again, c would be replaced with the new value. But since we made it local, we can't use it from outside the function, which makes sense. Also note if you change a local variable, you do not need to specify local again. For example...
local x = 10
local x = 20 -- wrong
Only specify it once, and it will stay local.
local x = 10
x = 20 -- still local to whatever function this code is in
Nothing you need to do with this either just thought I'd explain it too since we used a local variable. Oh and also note that function parameters are always local, so we cannot use "a" and "b" outside of the function either.
TablesA table is a special kind of value that holds multiple values. There is a lot of depth in this topic, but I'll try to stay on track here since this post is already getting long. Here's an example:
{10,50}
{} creates a table, and values in between it set the initial values for it. We can also make a variable refer to a table like this:
t = {20,60}
Now "t" refers to the created table. We can get certain values from the table using [index].
t = {50,100,150}
t[1] -- this is 50
t[2] -- this is 100
t[3] -- this is 150
t[4] -- invalid
We can also store peds in a table.
t = {Peanut,Hal,Johnny}
When we do this, the script will get the value of "Peanut" (a unique number referring to the created ped), and put it in the table, and do the same for each other value.
Note that if we change any of the variables, the value in the table will NOT change. For example...
x = 10
t = {x} -- takes the value of 'x' at this time and copies it into the table
t[1] -- equal to 10
x = 50 -- x is now 50
t[1] -- is still 10
Custom functions with many argumentsOkay so tables are great, but why teach you them? Because it is how we can get multiple arguments into a function. Check it out:
PrintNames = function(peds)
-- just like before, "peds" is a parameter to the function that we will fill in with a table when calling it.
local i = 1 -- start 'i' at 1
while i <= table.getn(peds) do -- table.getn returns the size of a table, the amount of values in it. while 'i' is less than or equal to that, we have a valid value we can get.
TextPrintString(PedGetName(peds[i],2,1) -- get the name of peds[i].
Wait(2000) -- wait for the name to show for a bit
i = i + 1 -- increment i for use with the next ped in the table
end
end
PrintNames({Peanut,Hal,Johnny})
In this example I use something called comments, it is a very simple concept, I just type "--" and the rest of the line becomes a comment. I use this to make comments about certain parts of the code. LUA ignores comments, they are just for scripters to read.
It's not a very useful example, but it's a simple one for demonstrating the idea. We create a table with Peanut, Hal, and Johnny in it, and pass that table as an argument to the PrintNames function. That table fills in the "peds" parameter, and we use that "peds" table like the other tables, only this time we use a variable to access the values in it instead. It's basically the same idea though, if 'i' is 1, then it's the same as peds[1]. This basically just loops through every ped in the table, doing the code in the while loop on each ped. Saves a lot of script space and effort since we only have to write the code one, and it gets done on each ped. PedGetName by the way is just a simple function that returns the name of a ped. Now... about that "..." parameter.
PrintNames = function(...)
-- ... is a special thing we can make as a parameter. It takes multiple arguments and puts them all into a table called "arg".
local i = 1 -- start 'i' at 1
while i <= table.getn(arg) do -- table.getn returns the size of a table, the amount of values in it. while 'i' is less than or equal to that, we have a valid value we can get.
TextPrintString(PedGetName(arg[i],2,1) -- get the name of peds[i].
Wait(2000) -- wait for the name to show for a bit
i = i + 1 -- increment i for use with the next ped in the table
end
end
-- Notice this time I don't create a table, I just pass multiple values into the function. The ... parameter takes all of these and puts it in a local table called "arg" for the function, as explained above.
PrintNames(Peanut,Hal,Johnny)
Very similar to the last example really, only notice I don't need to create my own table when calling the function, and we change "peds" to be "arg", "arg" is a table that gets filled in with all the values passed to the function when using "..." as the only parameter.
If statementsThis is a very simple concept, so we'll make it brief. An if statement is very similar to a while loop with syntax, except it only runs once (it's not a loop). For example...
if true then
-- code
end
Notice instead of "do" we use "then", and instead of "while" we use "if". These are the only changes to syntax, but also note that the body of the if statement ("--code") will only run once if the expression between "if" and "then" is true. If it's not true, then it won't even run once (just like the while loop) and it'll just go straight to the end.
Finally, the AnyPedIsAlive functionNow, we can finally make the function we talked about before. It will only use concepts we've covered before, so there is nothing new to really learn now. I will still comment the code so you understand what is happening.
AnyPedIsAlive = function(...)
local i = 1
while i <= table.getn(arg) do
if PedIsValid(arg[i]) and not PedIsDead(arg[i]) then
-- The ped is valid and not dead, meaning they are alive. Return true.
return true
end
i = i + 1
end
-- If the function gets here and still hasn't returned (remember if it returned above, then the script wouldn't make it to this part of the function) then none of the peds must be alive, return false.
return false
end
There it is, wonderful isn't it? Now just a few more things before we get your final fixed script going...
Variable namesI notice in your script you use the same variable name multiple times. "Chad" in MissionSetup for example. You create two peds of him which is fine, but you only have one variable for storing the ped in. So the second one ends up overwriting the first one.
function MissionSetup()
Chad = PedCreateXYZ(32, 474.31 , -179.63 , 2.91)
-- Okay, Chad is now a variable that refers to a created ped.
PedSetHealth(Chad, 2000)
Chad = PedCreateXYZ(117, 464.72 , -183.13 , 3.04)
-- Wait, the value of Chad just changed to a new ped. The old value is lost, the old ped still exists, but we no longer have access to him!
PedSetHealth(Chad, 2000)
end
We can fix it easily by calling him Chad2. Same with the other duplicates you have.
Ped limitSadly this game has a pretty small max amount of peds, if more are spawned, the game will crash. The max amount is 26, and you create 17. This is fine but, the problem may occur when you take into account there is usually around 5-15 freeroam peds spawned at once, which can and will end up creating more than 26 peds, and crashing your game. The fix for this is simple though, let's just stop the game from creating more peds! This is done through a function called StopPedProduction. It takes one argument, true or false, and it specifies whether or not it should be stopped or resumed.
StopPedProduction(true) -- stop freeroam peds from spawning
StopPedProduction(false) -- start freeroam ped spawning
We'll stop it in MissionSetup, and start it in MissionCleanup, simple.
Oh but another problem is this will only stop peds, NOT delete the current ones. Luckily there is a function to delete all the freeroam peds (not mission peds though). AreaClearAllPeds. It takes no arguments, and just does what you'd expect. We'll call it once before creating our mission's peds, and after stopping production.
ConclusionI know I've probably went on a lot longer than you expected and I'm sorry if you wanted a more direct answer, but I can tell you're new but want to learn so I wanted to help as much as I could. Not just showing you the answer but teaching you everything that goes along with it, along with some other things along the way. I hope it really helps, and I hope to see you make some really awesome mods in the future too. Good luck and happy modding!
The final scriptMissionSetup = function()
local x = 480.52
local y = -200.43
local z = 2.91
AreaTransitionXYZ(0, x, y, z)
PlayerSetHealth(2000)
PedSetTypeToTypeAttitude(4, 13, 4)
PedSetTypeToTypeAttitude(5, 13, 0)
PedSetTypeToTypeAttitude(4, 5, 0)
StopPedProduction(true)
AreaClearAllPeds()
Peanut = PedCreateXYZ(21, 478.22 , -200.73 , 2.95)
PedSetHealth(Peanut, 2000)
Hal = PedCreateXYZ(22, 475.22 , -202.22 , 3.00)
PedSetHealth(Hal, 2000)
Johnny = PedCreateXYZ(23, 473.29 , -203.48 , 3.05)
PedSetHealth(Johnny, 2000)
Lefty = PedCreateXYZ(24, 470.14 , -205.18 , 3.10)
PedSetHealth(Lefty, 2000)
Lucky = PedCreateXYZ(26, 467.81 , -205.71 , 3.14)
PedSetHealth(Lucky, 2000)
Vance = PedCreateXYZ(27, 468.27 , -203.03 , 3.12)
PedSetHealth(Vance, 2000)
Ricky = PedCreateXYZ(28, 470.92 , -202.25 , 3.08)
PedSetHealth(Ricky, 2000)
Norton = PedCreateXYZ(201, 478.92 , -199.85 , 2.93)
PedSetHealth(Norton, 2000)
Tad = PedCreateXYZ(31, 476.13 , -177.58 , 2.89)
PedSetHealth(Tad, 2000)
Chad = PedCreateXYZ(32, 474.31 , -179.63 , 2.91)
PedSetHealth(Chad, 2000)
Bif = PedCreateXYZ(33, 471.44 , -179.79 , 2.94)
PedSetHealth(Bif, 2000)
Justin = PedCreateXYZ(34, 468.77 , -180.30 , 2.95)
PedSetHealth(Justin, 2000)
Bryce = PedCreateXYZ(35, 466.05 , -181.25 , 2.99)
PedSetHealth(Bryce, 2000)
Darby = PedCreateXYZ(37, 463.08 , -181.66 , 3.00)
PedSetHealth(Darby, 2000)
Parker = PedCreateXYZ(40, 461.70 , -183.52 , 3.06)
PedSetHealth(Parker, 2000)
Chad2 = PedCreateXYZ(117, 464.72 , -183.13 , 3.04)
PedSetHealth(Chad, 2000)
Justin2 = PedCreateXYZ(118, 467.60 , -182.73 , 3.02)
PedSetHealth(Justin, 2000)
end
MissionCleanup = function()
StopPedProduction(false)
end
AnyPedIsAlive = function(...)
local i = 1
while i <= table.getn(arg) do
if PedIsValid(arg[i]) and not PedIsDead(arg[i]) then
-- The ped is valid and not dead, meaning they are alive. Return true.
return true
end
i = i + 1
end
-- If the function gets here and still hasn't returned (remember if it returned above, then the script wouldn't make it to this part of the function) then none of the peds must be alive, return false.
return false
end
main = function()
-- Wait for all the enemies to die:
while AnyPedIsAlive(Chad,Chad2,Bif,Justin,Justin2,Bryce,Darby) do
Wait(0)
end
-- Complete the mission:
MissionSucceed()
end