First, check out
this step-by-step tutorial I made for someone else on a similar question. That topic only asked how to change styles, but it certainly covers a lot of what you will need to know to start with action node modding. First read that post there I linked it will be quite important, then move on to what I'm about to add onto it below.
So as you should now know from the topic linked above, styles are technically called "action trees" and they are the root of all actions in the style. For example: "/Global/G_Striker_A" is an action tree because it is the root of all actions (moves) in the style. The specific actions in it can be activate by activating an action node. A node may be able to extend to other nodes too that are further in an action. Here's an example:
"/Global/6_02/HeadButt/HeadButt_AnticStart" - This is an action node, that is part of the "/Global/6_02" action tree. 6_02 isn't a valid action tree to be set on a ped, but it does have actions in it, and those specific actions can be set on peds. We know we can use PedSetActionTree to set the action tree of a ped, but we can also use PedSetActionNode to make a ped play an action node. So some valid code could be: PedSetActionNode(gPlayer,"/Global/6_02/HeadButt/HeadButt_AnticStart","Act/Conv/6_02.act")
Notice that it includes "Act/Conv/6_02.act" as well, that is simply the file that the node is in, this was already discussed in the style's tutorial though. gPlayer as you should also know is the ped that we're setting the node on (the player is gPlayer). Also remember to load animations before using action nodes. A way to easily be sure you load the right animations is to simply but possibly lazily load all animations, which you can make code that does that yourself or use mine
found in this topic. So now if you call the function with the arguments shown above, the player should perform an action, the Headbutt one, starting at the "HeadButt_AnticStart" node. While the node plays out, it may have nodes added to it. While the player is playing the headbutt action there, the node will eventually become "/Global/6_02/HeadButt/HeadButt_AnticStart/HeadButt_AnticCyc/HeadButt_RunCyc/HeadButt_Success/HeadButt_Success_Wall_Hit" if you hit a ped and hit a wall. At first we set the player's action node to be "/Global/6_02/HeadButt/HeadButt_AnticStart", which is a certain node in an action, and it played to eventually be longer as it got farther in the action. "/Global/6_02/HeadButt/HeadButt_AnticStart/HeadButt_AnticCyc/HeadButt_RunCyc/HeadButt_Success/HeadButt_Success_Wall_Hit" is an action node that is very far in the headbutt action. HeadButt_AnticStart is one of the nodes in the action, it is the very start of the headbutt. HeadButt-AnticCyc is another node in the action. HeadButt_RunCyc is the running node that plays while the ped is running forward during the headbutt. HeadButt_Success is when a ped was hit, and Headbutt_Success_Wall_Hit is when a wall was hit while a ped is successfully headbutted. We could of also set a different action node that is farther in the action if we wanted for example the player to skip the intro to the headbutt and just start running, we could of set "/Global/6_02/HeadButt/HeadButt_AnticStart/HeadButt_AnticCyc/HeadButt_RunCyc".
Once an action finishes and there is nothing else to play (some nodes however will loop infinitely and never finish so you'll have to set a different node manually to have them break the current action, we'll get to that later), the ped will go back to standing/being idle/walking.
So now you know what an action is, and that an action node is a part of the action, and we can set action nodes on peds. But... how do you find these action nodes? Well an easy way is to check out
this topic where members share many of the nodes they find. But what if you need a node that nobody wanted to give up, or you want to find a new one yourself? Well that is... sort of doable. The current way to find nodes isn't very efficient but it's worked so far. Actions are in the ACT files, or they were during development of the game. Now however they are in CAT files, the CAT files are release versions of the act files and there is no tools to open them to properly read the list of nodes and no efficient ways of extracting the nodes as of now. However what people currently do is look in the cat files with
HxD (but really any text editor will pretty much work) and look in the bottom half of the the file, there are semi-readable nodes in there. They are not full nodes but they are parts of nodes, and what people have been doing is just simply guess what the full action node may be based on the parts of it that they see in the cat files. To get cat files, you need to go in /Bully/Act/Act.img and they are all in there.
Alright, so now you know how actions and action nodes work, and how to set them on peds, AND how to find them! But ugh... you still don't know how to bind them to buttons? Jeez you got a lot of questions kid...
To bind the action nodes to buttons (or really bind any code to a button) in Bully, you'll have to use 1 of 3 functions. IsButtonPressed, IsButtonBeingPressed, or IsButtonBeingReleased. Each of these functions will return a boolean value (true or false) based on if the button is pressed, just pressed, or released. But before we go any further with the button functions... we have to discuss how an "if" statement works in LUA. I'll just give you some examples.
if true then
-- This code gets executed
PlayerSetHealth(500)
end
if false then
-- This code does not
PlayerSetHealth(600)
end
In that example, the player's health will be set to 500 (with the function PlayerSetHealth) but never set to 600. The way an if statement works is code inside the if statement (code between the "then" after an "if" and the "end" after the "then") will only be executed/run if the value between "if" and "then" is true. If it's false it won't run. But you can do more then just write "true" or "false" there. You could also put a function there that returns true/false, like this.
if IsButtonPressed(3,0) then
TextPrintString("button pressed",0,1)
end
There are 2 arguments in the function "IsButtonPressed". The first is the button pressed, the second is on what controller. Check out the "Controls" section of
this topic for a list of buttons/controller ids. Usually you will only need to use 0 for the controller. In our example above, we used 3 and 0, which means the zoom out button on player 1's controller (Note: A controller can be a keyboard, its whatever device is controlling the player). The zoom out button is usually the down button, you know the one that when you zoom in with like a slingshot or camera can zoom back out. TextPrintString is a function with 3 arguments, first the string (text) to be printed, secondly the time for it to print in seconds (0 means for 1 frame), and the third is where on the screen (1 = top, 2 = bottom). So with our whole example, the text "button pressed" will only be printed while the down button is pressed. This isn't really a fun code for a mod though but it just helped to explain how an "if" statement works. The function "IsButtonPressed" returns true if the button is pressed, and false otherwise, so the code runs if it's pressed.
There are 2 variations of IsButtonPressed too:
IsButtonBeingPressed: Returns true only for one single frame, the single instance that the button was pressed. So if you hold the button, it won't still return true, it only returned true one time the single time you pressed the button.
IsButtonBeingReleased: The single instance when the button was let go of.
But for our code to be able to detect a button at any time, we need to repeatedly test if the button is pressed. Check out
this topic again. Look in the example code where this was:
while PedIsAlive(gPlayer) do
Wait(0)
end
The "while" loop is quite similar to the "if" statement. The code between "do" and "end" will run repeatedly while the thing between "while" and "do" is true. The function PedIsAlive(gPlayer) returns true if the player is alive, and false otherwise so this loop will only run while the player is alive then end when the player dies (because the script ends when the player dies). The code after the while loop will not run until the while loop is over, because only one bit of code can be executed at once and the code being executed is the while loop. Also note the while loop will never run even once if the thing condition is false to begin with. The "condition" is the technical word for the thing between "while" and "do" that should be true/false. So... you have to leave "Wait(0)" in there because if you don't the game will crash because that function simply lets the game run for one frame. The game can only run when "Wait" is called, and the 0 meant for just 1 frame. When the script wasn't "waiting" it was doing other things, and the game can only run while the script is waiting, so we have to constantly wait in our script (the reason it's in the while loop, so it runs constantly) so that the game can keep running and not freeze.
So now... you know how an if statement works and how a while statement works so we can FINALLY bind our action node. In the while loop, put code like this:
if IsButtonBeingPressed(6,0) then
PedSetActionNode(gPlayer,"/Global/6_02/HeadButt/HeadButt_AnticStart","Act/Conv/6_02.act")
end
It has to be in the while loop so that every single frame we have an opportunity to press the button and play the node. As we know because of the "if" statement, the node will only play when the button 6 is pressed (the attack button). So finally.... the main function of your code should look something like
function main()
while PedIsAlive(gPlayer) do
if IsButtonBeingPressed(6,0) then
PedSetActionNode(gPlayer,"/Global/6_02/HeadButt/HeadButt_AnticStart","Act/Conv/6_02.act")
end
Wait(0)
end
end
Good luck with modding buddy!