Skip to content

Commit

Permalink
feat(extra-natives/five): REGISTER_ROPE_DATA
Browse files Browse the repository at this point in the history
  • Loading branch information
packfile committed Jan 11, 2025
1 parent e84dd03 commit 85247d1
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 4 deletions.
12 changes: 9 additions & 3 deletions code/components/extra-natives-five/include/ropeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ class grcTexture;
class ropeData
{
public:
virtual ~ropeData() = 0;
ropeData();

virtual const char* GetName() = 0;
virtual ~ropeData() {}

virtual parStructure* parser_GetStructure() = 0;
void* operator new(size_t size);

void operator delete(void* pointer);

uint32_t numSections; // 0x8
float radius; // 0xC
Expand All @@ -43,6 +45,10 @@ class ropeDataManager
public:
virtual ~ropeDataManager() = 0;

void UnloadRopeTextures();

void Load();

atArray<ropeData*> typeData; // 0x8

static ropeDataManager* GetInstance();
Expand Down
136 changes: 136 additions & 0 deletions code/components/extra-natives-five/src/RopeNatives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,52 @@
#include <Hooking.h>
#include <ScriptEngine.h>
#include <ScriptSerialization.h>
#include <ScriptWarnings.h>
#include <Pool.h>
#include <ICoreGameInit.h>

namespace rage
{
static hook::thiscall_stub<void(ropeData* self)> ropeData_ropeData([]()
{
return hook::get_pattern("48 89 5C 24 ? 48 89 7C 24 ? 41 56 48 83 EC 20 8B 3D ? ? ? ? C7 41");
});

ropeData::ropeData()
{
ropeData_ropeData(this);
}

void* ropeData::operator new(size_t size)
{
return PoolAllocate(GetPool<ropeData>("ropeData"));
}

void ropeData::operator delete(void* pointer)
{
PoolRelease(GetPool<ropeData>("ropeData"), pointer);
}

static hook::thiscall_stub<void(ropeDataManager* self)> ropeDataManager_UnloadRopeTextures([]()
{
return hook::get_call(hook::get_pattern("E8 ? ? ? ? 48 8B CF E8 ? ? ? ? 48 8B 9F ? ? ? ? 48 8B 8B"));
});

void ropeDataManager::UnloadRopeTextures()
{
ropeDataManager_UnloadRopeTextures(this);
}

static hook::thiscall_stub<void(ropeDataManager* self)> ropeDataManager_Load([]()
{
return hook::get_pattern("48 83 EC 48 E8 ? ? ? ? 4C 8B 0D");
});

void ropeDataManager::Load()
{
ropeDataManager_Load(this);
}

static ropeDataManager* g_ropeDataManager;

ropeDataManager* ropeDataManager::GetInstance()
Expand All @@ -33,6 +76,9 @@ ropeManager* ropeManager::GetInstance()
}
}

static int* ropeDataManager__txdStatus;
static bool g_hasCustomRopeTypes = false;

static HookFunction hookFunction([]()
{
{
Expand Down Expand Up @@ -143,4 +189,94 @@ static HookFunction hookFunction([]()

context.SetResult(updateOrder);
});

{
auto location = hook::get_pattern<char>("48 83 EC 28 33 D2 8D 4A 07");
ropeDataManager__txdStatus = (int*)(hook::get_address<char*>(location + 16) + 1);
}

fx::ScriptEngine::RegisterNativeHandler("REGISTER_ROPE_DATA", [](fx::ScriptContext& context)
{
int numSections = context.GetArgument<int>(0);
float radius = context.GetArgument<float>(1);
const char* diffuseTextureName = context.CheckArgument<const char*>(2);
const char* normalMapName = context.CheckArgument<const char*>(3);
float distanceMappingScale = context.GetArgument<float>(4);
float uvScaleX = context.GetArgument<float>(5);
float uvScaleY = context.GetArgument<float>(6);
float specularFresnel = context.GetArgument<float>(7);
float specularFalloff = context.GetArgument<float>(8);
float specularIntensity = context.GetArgument<float>(9);
float bumpiness = context.GetArgument<float>(10);
int color = context.GetArgument<int>(11);

context.SetResult(-1);

if (numSections <= 0)
{
fx::scripting::Warningf("natives", "Invalid numSections was passed to REGISTER_ROPE_DATA (%d), should greater than 0\n", numSections);
return;
}

if (radius <= 0.0)
{
fx::scripting::Warningf("natives", "Invalid radius was passed to REGISTER_ROPE_DATA (%f), should greater than 0.0\n", radius);
return;
}

rage::ropeDataManager* manager = rage::ropeDataManager::GetInstance();
if (!manager)
{
return;
}

atPool<rage::ropeData>* pool = rage::GetPool<rage::ropeData>("ropeData");
if (pool->GetCount() == pool->GetSize())
{
fx::scripting::Warningf("natives", "Unable to allocate rope data in REGISTER_ROPE_DATA, pool is full\n");
return;
}

rage::ropeData* ropeData = new rage::ropeData();

ropeData->numSections = numSections;
ropeData->radius = radius;
ropeData->diffuseTextureNameHash = HashString(diffuseTextureName);
ropeData->normalMapNameHash = HashString(normalMapName);
ropeData->distanceMappingScale = distanceMappingScale;
ropeData->UVScaleX = uvScaleX;
ropeData->UVScaleY = uvScaleY;
ropeData->specularFresnel = specularFresnel;
ropeData->specularFalloff = specularFalloff;
ropeData->specularIntensity = specularIntensity;
ropeData->bumpiness = bumpiness;
ropeData->color = color;

int ropeType = manager->typeData.GetCount();
manager->typeData.Set(ropeType, ropeData);

// If rope textures are loaded then put them back into the loading state
if (*ropeDataManager__txdStatus == 2)
{
manager->UnloadRopeTextures();
*ropeDataManager__txdStatus = 1;
}

g_hasCustomRopeTypes = true;

context.SetResult(ropeType);
});

Instance<ICoreGameInit>::Get()->OnShutdownSession.Connect([]()
{
if (g_hasCustomRopeTypes)
{
rage::ropeDataManager* manager = rage::ropeDataManager::GetInstance();
if (manager)
{
manager->Load();
}
g_hasCustomRopeTypes = false;
}
});
});
2 changes: 1 addition & 1 deletion data/client/citizen/common/data/gameconfig.xml
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@
</Item>
<Item>
<PoolName>ropeData</PoolName>
<PoolSize value="24"/>
<PoolSize value="64"/>
</Item>
</Entries>
</PoolSizes>
Expand Down
42 changes: 42 additions & 0 deletions ext/native-decls/RegisterRopeData.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
ns: CFX
apiset: client
game: gta5
---
## REGISTER_ROPE_DATA

```c
int REGISTER_ROPE_DATA(int numSections, float radius, char* diffuseTextureName, char* normalMapName, float distanceMappingScale, float uvScaleX, float uvScaleY, float specularFresnel, float specularFalloff, float specularIntensity, float bumpiness, int color);
```
Registers a custom rope data with the game. For guidance on what these values should be use common:/data/ropedata.xml as a reference.
Returns a rope type which can be passed into [ADD_ROPE](?_0xE832D760399EB220) to use a custom rope design.
Once a rope data is registered it can be used indefinitely and you should take caution not too register too many as to exceed the games limit.
## Examples
```lua
-- Create a thick steel cable rope above the players head
local ropeType = RegisterRopeData(6, 0.15, "steel_cable", "steel_cable_n", 1.0, 1.0, 8.775, 0.97, 30.0, 0.25, 1.775, 0x00FFFF00)
if ropeType ~= -1 then
local coords = GetEntityCoords(PlayerPedId()) + vector3(0.0, 0.0, 5.0)
AddRope(coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 25.0, ropeType, 10.0, 0.0, 1.0, false, false, false, 1.0, false, 0)
RopeLoadTextures()
end
```

## Parameters
* **numSections**:
* **radius**:
* **diffuseTextureName**:
* **normalMapName**:
* **uvScaleX**:
* **uvScaleY**:
* **specularFresnel**:
* **specularFalloff**:
* **specularIntensity**:
* **bumpiness**:
* **color**:

## Return value
Returns a non-negative value on success, or -1 if the rope data could not be registered or an invalid argument is passed.

0 comments on commit 85247d1

Please sign in to comment.