Skip to content

Instantly share code, notes, and snippets.

@Kianimal
Last active August 20, 2024 17:38
Show Gist options
  • Save Kianimal/8a8984bc76653a27e759458dbcc6c284 to your computer and use it in GitHub Desktop.
Save Kianimal/8a8984bc76653a27e759458dbcc6c284 to your computer and use it in GitHub Desktop.
RedM MoveNetwork research

RedM MoveNetwork Research

Introduction

MoveNetwork natives in RedM are typically used to control specific scenarios that have multiple animation steps and involve interacting with items/objects in the world.

For example, MoveNetwork natives are used to control (portions of) the bathing sequence, campfire interaction, base game character creation char movement, and many of the minigame sequences found in the game.

They are also used in the safe-cracking minigame - this is the example we will be using in this documentation, as it relates to the vault itself.

Note

This documentation/example does not include any animation information included in the bank safe-cracking process - it only covers MoveNetwork information for the safe vaults.

Implementation

Requesting MoveNetwork defs

The first thing you will need to do to use a MoveNetwork is to request the MoveNetwork and its associated clipset. For the bank robbing/vault scenario, we can use the following, called in its native form:

RequestMoveNetworkDef(string name) | 0x2B6529C54D29037A

MoveNetwork string names can be found in the decompiled scripts, or in move_networks.xml via CodeX. For this case we will use Script_MPOUT4_Safecrack_Player and Script_MUD5_Safecrack_Safe:

Citizen.InvokeNative(0x2B6529C54D29037A, "Script_MPOUT4_Safecrack_Player")
Citizen.InvokeNative(0x2B6529C54D29037A, "Script_MUD5_Safecrack_Safe")

Important

Notice that two MoveNetwork requests are called - one for the player and one for the safe. The player has a separate MoveNetwork that controls hand movement for both hands, while the safe has a MoveNetwork that controls dial rotation and door position. Although we will not be using/going into the player MoveNetwork in this example, I wanted to highlight that this can be the case in certain scenarios.

You will then want to ensure that your MoveNetwork definition has loaded. I would recommend a loop to check for the status with a wait period set to your desire, like so:

HasMoveNetworkDefLoaded(string name) | 0x2C04D89A0FB4E244

while not Citizen.InvokeNative(0x2C04D89A0FB4E244, "Script_MPOUT4_Safecrack_Player") 
and not Citizen.InvokeNative(0x2C04D89A0FB4E244, "Script_MUD5_Safecrack_Safe") do
  Wait(1)
end

Important

If either of these MoveNetworks fail to load, this particular example loop will never break and simply be stuck with no way forward. It is therefore recommended to have some type of conditional break to prevent this from occurring.

Requesting Clipsets

You will then need to load your clipset with RequestClipSet(clipSet string) | 0xEF7611B57A820126

Most of the clipsets that are used with MoveNetworks can be found in clip_mini_games.rpf, or in the decompiled scripts. Note that if you are finding it in the .rpf file, you will need to add "CLIPSET@" at the beginning of the string to match the format below. In this case, we will load the clipset for the small safe (CLIPSET@MINI_GAMES@STORY@MUD5@CRACKSAFE@SMALL_R@SAFE) found in the walls of the Valentine & Saint Denis banks:

RequestClipSet("CLIPSET@MINI_GAMES@STORY@MUD5@CRACKSAFE@SMALL_R@SAFE")

And again, we will ensure it loads in the same process with HasClipSetLoaded(clipSet string) | 0x1F23D6B6DA1CC3B2:

while not Citizen.InvokeNative(0x1F23D6B6DA1CC3B2, "CLIPSET@MINI_GAMES@STORY@MUD5@CRACKSAFE@SMALL_R@SAFE") do
  Wait(1)
end

Important

Once again I recommend building in a break condition to prevent failure of the clipset to load from hanging up in an infinite loop.

Attaching MoveNetworks to Objects/Peds

For our case, we will use a small safe object (s_vault_sml_r_val01x_high) that I will assume you know how to create/find via natives already, represented in these examples as safe.

We do this by calling TaskMoveNetworkByNameWithInitParams(Ped ped, char* moveNetworkDefName, Any* taskData, float p3, BOOL p4, char* animDict, int flags) | 0x139805C2A67C4795

This is the tricky part. For this example we will be using a separate Javascript file (I often use JS for dataview because it is built in and easier for me) that we trigger a client event for, but this can easily be converted to Lua with an external or internal dataview setup, in which case you would not trigger an event to a JS file but rather just call a separate function. We will need DataView because we will need to struct the data to be passed into the Any* taskData parameter, as it requires a structed table, with the items of that struct being, respectively, the clipset used, an unknown value (in many cases DEFAULT works but it is unclear as of yet what this does, exaclty; more research is needed), and the MoveNetwork task we will be modifying, which in this case is TurnDial. The safe and the moveNetworkDefName (Script_MUD5_Safecrack_Safe) are passed in as unstructed data.

In our client.lua file:

TriggerEvent("Your_Script_Name:TASK_MOVE_NETWORK_BY_NAME_WITH_INIT_PARAMS",
{ safe, "Script_MUD5_Safecrack_Safe", clipsetSafe, `DEFAULT`, "TurnDial" })

And in our client.js file:

on('Your_Script_Name:TASK_MOVE_NETWORK_BY_NAME_WITH_INIT_PARAMS', (args) => {
    const struct = new DataView(new ArrayBuffer(512));
    struct.setBigInt64(0, BigInt(args[2]), true);
    struct.setBigInt64(8, BigInt(args[3]), true);
    struct.setBigInt64(240, BigInt(CreateVarString(10, "LITERAL_STRING", args[4])), true);

    Citizen.invokeNative("0x139805C2A67C4795", args[0], args[1], struct, 0.0, 0, 0, 0);
});

We then perform a check to ensure that the MoveNetwork has been applied and that the MoveNetwork object is ready to transition states with IsTaskMoveNetworkActive(ped Ped) | 0x921CE12C489C4C41 and IsTaskMoveNetworkReadyForTransition(ped Ped) | 0x30ED88D5E0C56A37:

while not IsTaskMoveNetworkReadyForTransition(safe) and not IsTaskMoveNetworkActive(safe) do
  Wait(10)
end

Note that while the documentation calls for a ped to be passed as the param, it will also accept objects provided they match the criteria for the structed data and clipsets used.

Important

Once again I recommend building in a break condition to prevent failure of the loop resolution from hanging up in an infinite loop.

The Fun Part

Once all of that is out of the way, we can now start to manipulate our safe object! The two main natives you will use for this process will be:
SetTaskMoveNetworkSignalFloat(ped Ped, signalName string, value number) | 0xD5BB4025AE449A4E
RequestTaskMoveNetworkStateTransition(ped Ped, name string) | 0xD01015C7316AE176

There are other natives for MoveNetwork states as well, such as SetTaskMoveNetworkSignalVector and SetTaskMoveNetworkSignalBool, for situations where the object/ped states can be modified with vector and true/false values, but this is not applicable to the safe door scenario.

We will call the native to rotate the safe door dial first, passing it the safe object, the "signal name", Rotation, and the value to alter the signal name by in float form, 45.0. Then we will force the entity update with ForceEntityAiAndAnimationUpdate(entity Entity) | 0x4C9E96473D4F1A88. It is also recommended to check the IsTaskMoveNetworkReadyForTransition before each update.

if IsTaskMoveNetworkReadyForTransition(safe) then
  Citizen.InvokeNative(0xD5BB4025AE449A4E, safe, "Rotation", 45.0)
  ForceEntityAiAndAnimationUpdate(safe)
end

And just like that, our vault door dial turns to the number 45!

You can use this native to set the dial to any value between 0 and 99, as that is the max value on the dial itself.

Once you are done manipulating dial rotation, you can then open the door with RequestTaskMoveNetworkStateTransition, passing it the safe object and the state we want to transition to, which is Open. Once again, it is recommended to check for IsTaskMoveNetworkReadyForTransition before doing so, and we will then once again force the entity anim update afterward:

if IsTaskMoveNetworkReadyForTransition(safe) then
  RequestTaskMoveNetworkStateTransition(safe, "Open")
  ForceEntityAiAndAnimationUpdate(safe)
end

And voila, your safe door is now open!

Tip

You can use small, medium, and large safe objects for this process, but the ones that spawn in certain areas will not work correctly with this process. If a safe has des within its name, it will usually (always?) not work correctly. Saint Denis has an IPL for the vault interior that comes with safes that DO work for this process, I believe.

Removing MoveNetworks and defs

To remove an object or ped from a MoveNetwork, simply use RemoveMoveNetworkDef(name string) | 0x57A197AD83F66BBF and pass it the same MoveNetwork string we used previously:

Citizen.InvokeNative(0x57A197AD83F66BBF, "Script_MUD5_Safecrack_Safe")

This will remove the MoveNetwork and disassociate any objects/peds associated with it, although for peds you will also want to ClearPedTasks.

Further Usage

As mentioned previously, MoveNetworks are used for MANY animation processes in the game, primarily for minigames. There are MoveNetworks for peds and objects, and different situations use different natives. Most or all of the minigames involve MoveNetworks, but the paramaters for each will be different and vary in complexity. All of these situations ALSO involve some form of animation that is separate to the MoveNetwork, so don't forget to incorporate those!

Have a look through the game files via CodeX and the decompiled scripts and see what other fun stuff you can discover using MoveNetworks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment