Skip to content

Instantly share code, notes, and snippets.

@ikt32
Created August 20, 2019 07:59
Show Gist options
  • Save ikt32/58d7bf002aabee23d090988987565cb0 to your computer and use it in GitHub Desktop.
Save ikt32/58d7bf002aabee23d090988987565cb0 to your computer and use it in GitHub Desktop.
Glowing Brake Discs
using System;
using System.Collections.Generic;
using System.Drawing;
using GTA;
using GTA.Math;
using GTA.Native;
static class MathExt
{
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
{
if (val.CompareTo(min) < 0) return min;
else if (val.CompareTo(max) > 0) return max;
else return val;
}
public static float Lerp(float firstFloat, float secondFloat, float by)
{
return firstFloat * (1 - by) + secondFloat * by;
}
public static float Map(float x, float in_min, float in_max, float out_min, float out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
public static float DegToRad(float angle)
{
return (float)(Math.PI * (double)angle / 180.0);
}
public static Vector3 GetOffsetInWorldCoords(Vector3 position, Vector3 rotation, Vector3 forward, Vector3 offset)
{
const float deg2Rad = (float)0.01745329251994329576923690768489;
float num1 = (float)System.Math.Cos(rotation.Y * deg2Rad);
float x = num1 * (float)System.Math.Cos(-rotation.Z * deg2Rad);
float y = num1 * (float)System.Math.Sin(rotation.Z * deg2Rad);
float z = (float)System.Math.Sin(-rotation.Y * deg2Rad);
Vector3 right = new Vector3((float)x, (float)y, (float)z);
Vector3 up = Vector3.Cross(right, forward);
return position + (right * offset.X) + (forward * offset.Y) + (up * offset.Z);
}
}
public class GlowVehicle
{
private readonly Vehicle _vehicle;
public GlowVehicle(Vehicle vehicle)
{
_vehicle = vehicle;
CurrVelocity = Function.Call<Vector3>(Hash.GET_ENTITY_SPEED_VECTOR, Vehicle, true);
LastVelocity = CurrVelocity;
Acceleration = (CurrVelocity - LastVelocity) / Game.LastFrameTime;
}
public Vehicle Vehicle
{
get { return _vehicle; }
}
public float[] BrakeTemps = new float[4];
public int[] PtfxHandles = new int[4] { 0, 0, 0, 0 };
public Vector3 CurrVelocity { get; private set; }
public Vector3 LastVelocity { get; private set; }
public Vector3 Acceleration { get; private set; }
public void Update()
{
LastVelocity = CurrVelocity;
CurrVelocity = Function.Call<Vector3>(Hash.GET_ENTITY_SPEED_VECTOR, Vehicle, true);
Acceleration = (CurrVelocity - LastVelocity) / Game.LastFrameTime;
}
public void ClearPtfx()
{
for (int i = 0; i < 4; ++i)
{
if (this.PtfxHandles[i] != 0)
{
Function.Call(Hash.REMOVE_PARTICLE_FX, this.PtfxHandles[i], 0);
this.PtfxHandles[i] = 0;
}
}
}
}
public class GlowingDisks : Script
{
private List<GlowVehicle> _glowVehicles = null;
public GlowingDisks()
{
_glowVehicles = new List<GlowVehicle>();
Tick += OnTick;
Aborted += OnAbort;
}
private void OnAbort(object sourc, EventArgs e)
{
foreach (var v in _glowVehicles)
{
v.ClearPtfx();
}
}
private void OnTick(object source, EventArgs e)
{
Vehicle[] allVehicles = World.GetAllVehicles();
for (int i = 0; i < _glowVehicles.Count; ++i)
{
bool exist = _glowVehicles[i].Vehicle.Exists();
float distance = 0.0f;
if (exist)
distance = World.GetDistance(Game.Player.Character.Position, _glowVehicles[i].Vehicle.Position);
if (!exist || distance > 50.0f)
{
_glowVehicles.RemoveAt(i);
--i;
}
}
// Add newly discovered vehicles
foreach (var v in allVehicles)
{
if (GetNumWheels(v) != 4)
continue;
if (World.GetDistance(Game.Player.Character.Position, v.Position) > 50.0f)
continue;
if (!_glowVehicles.Exists(x => x.Vehicle == v))
{
_glowVehicles.Add(new GlowVehicle(v));
}
}
// Glow
foreach (var v in _glowVehicles)
{
v.Update();
DrawDisks(v);
}
}
void DrawDisks(GlowVehicle v)
{
if (v.Vehicle.GetBoneIndex("wheel_lf") == -1) return;
if (v.Vehicle.GetBoneIndex("wheel_rf") == -1) return;
if (v.Vehicle.GetBoneIndex("wheel_lr") == -1) return;
if (v.Vehicle.GetBoneIndex("wheel_rr") == -1) return;
Vector3[] wheelCoords = new Vector3[4];
wheelCoords[0] = v.Vehicle.GetBoneCoord("wheel_lf");
wheelCoords[1] = v.Vehicle.GetBoneCoord("wheel_rf");
wheelCoords[2] = v.Vehicle.GetBoneCoord("wheel_lr");
wheelCoords[3] = v.Vehicle.GetBoneCoord("wheel_rr");
int[] boneIdxs = new int[4];
boneIdxs[0] = v.Vehicle.GetBoneIndex("wheel_lf");
boneIdxs[1] = v.Vehicle.GetBoneIndex("wheel_rf");
boneIdxs[2] = v.Vehicle.GetBoneIndex("wheel_lr");
boneIdxs[3] = v.Vehicle.GetBoneIndex("wheel_rr");
float[] weightShiftFactor = new float[4];
weightShiftFactor[0] = -v.Acceleration.Y;
weightShiftFactor[1] = -v.Acceleration.Y;
weightShiftFactor[2] = v.Acceleration.Y;
weightShiftFactor[3] = v.Acceleration.Y;
var wheelRotSpeeds = GetWheelRotationSpeeds(v.Vehicle);
var steeredWheelAngles = GetWheelSteeringAngles(v.Vehicle);
var brakePressures = GetBrakePressures(v.Vehicle);
Vector3 p = v.Vehicle.Position;
for (int i = 0; i < 4; ++i)
{
if (float.IsNaN(v.BrakeTemps[i]))
v.BrakeTemps[i] = 0.0f;
float targetVal = 0.0f;
if (brakePressures[i] > 0.0f)
{
targetVal = (brakePressures[i] + weightShiftFactor[i] / 40.0f) * Math.Abs(wheelRotSpeeds[i]);
}
targetVal = targetVal.Clamp(0.0f, 1.0f);
if (targetVal > 0.0f)
v.BrakeTemps[i] = MathExt.Lerp(v.BrakeTemps[i], targetVal, 1.0f - (float)Math.Pow(0.100f, Game.LastFrameTime));
else
{
float coolRateMod = MathExt.Map(Math.Abs(wheelRotSpeeds[i]), 0.0f, 60.0f, 0.100f, -0.200f);
coolRateMod = coolRateMod.Clamp(-200.0f, 100.0f);
v.BrakeTemps[i] = MathExt.Lerp(v.BrakeTemps[i], 0.0f, 1.0f - (float)Math.Pow(0.800f + coolRateMod, Game.LastFrameTime));
}
v.BrakeTemps[i].Clamp(0.0f, 1.0f);
if (float.IsNaN(v.BrakeTemps[i]))
v.BrakeTemps[i] = 0.0f;
//v.BrakeTemps[i] = 1.0f; //TODO: Temporary
// Drawing ptfx
if (v.BrakeTemps[i] > 0.066f)
{
if (v.PtfxHandles[i] == 0)
{
//RequestEffectLibrary("veh_impexp_rocket");
Function.Call(Hash.REQUEST_NAMED_PTFX_ASSET, "veh_impexp_rocket");
while (!Function.Call<bool>(Hash.HAS_NAMED_PTFX_ASSET_LOADED, "veh_impexp_rocket"))
Wait(0);
Function.Call(Hash._0x6C38AF3693A69A91, "veh_impexp_rocket"); //USE_PARTICLE_FX_ASSET
v.PtfxHandles[i] = Function.Call<int>(Hash._START_PARTICLE_FX_LOOPED_ON_ENTITY_BONE,
"veh_rocket_boost",
v.Vehicle,
-0.09f, 0.0f, 0.0f,
0.0f, 0.0f, -90.0f,
boneIdxs[i], 1.375f,
false, false, false);
Function.Call(Hash.SET_PARTICLE_FX_LOOPED_EVOLUTION, v.PtfxHandles[i], "boost", 0.0f, 0);
Function.Call(Hash.SET_PARTICLE_FX_LOOPED_EVOLUTION, v.PtfxHandles[i], "charge", 0.0f, 0);
Function.Call(Hash.SET_PARTICLE_FX_LOOPED_ALPHA, v.PtfxHandles[i], 100.0f);
}
}
if (v.BrakeTemps[i] < 0.050f)
{
if (v.PtfxHandles[i] != 0)
{
Function.Call(Hash.REMOVE_PARTICLE_FX, v.PtfxHandles[i], 0);
v.PtfxHandles[i] = 0;
}
}
Function.Call(Hash.SET_PARTICLE_FX_LOOPED_ALPHA, v.PtfxHandles[i], v.BrakeTemps[i] * 100.0f);
Function.Call(Hash.SET_PARTICLE_FX_LOOPED_EVOLUTION, v.PtfxHandles[i], "charge", v.BrakeTemps[i], 0);
// want the outer edges to glow before the inner parts
//Function.Call(Hash.SET_PARTICLE_FX_LOOPED_SCALE, v.PtfxHandles[i], 2.375f - v.BrakeTemps[i], 0);
// lmao hot
//float boostFire = v.BrakeTemps[i] > 0.66f ? ( v.BrakeTemps[i] - 0.66f )/0.33f: 0.0f;
//Function.Call(Hash.SET_PARTICLE_FX_LOOPED_EVOLUTION, v.PtfxHandles[i], "boost", boostFire, 0);
//// Drawing/Positioning marker
//float mult = GetDrawnWheelAngleMult(v.Vehicle);
//float actualAngle = steeredWheelAngles[i] * mult;
//float debugAngle = actualAngle - MathExt.DegToRad(90.0f);
//float steeringAngleRelX = (float)-Math.Sin(debugAngle);
//float steeringAngleRelY = (float)Math.Cos(debugAngle);
//Vector3 forward = v.Vehicle.GetOffsetInWorldCoords(new Vector3(steeringAngleRelX, steeringAngleRelY, 0.0f));
//Vector3 dir = forward - p;
//Vector3 rot = new Vector3
//{
// X = 90.0f
//};
//
//float sgn = -1.0f;
//if (i % 2 != 0) sgn = 1.0f;
//Vector3 coord = MathExt.GetOffsetInWorldCoords(wheelCoords[i], rot, dir, new Vector3(0, sgn * 0.011f, 0));
//
//Color color = Color.FromArgb(
// (int)(v.BrakeTemps[i] * 160.0f),
// (int)(v.BrakeTemps[i] * 255.0f),
// v.BrakeTemps[i] > 0.66f ? (int)(((v.BrakeTemps[i] - 0.66f) / (0.33f)) * 128.0f) : 0,
// 0);
//
//DrawMarker(MarkerType.HorizontalCircleFat,
// coord, dir, rot, 0.31f, color);
//DrawMarker(MarkerType.HorizontalCircleFat,
// coord, dir, rot, 0.21f, color);
// rip for this ptfx
//Function.Call(Hash.SET_PARTICLE_FX_LOOPED_COLOUR, v.PtfxHandles[i],
//0.0f, //R
//0.0f, //G
//255.0f, //B
//false);
}
}
private void DrawMarker(MarkerType marker, Vector3 pos, Vector3 dir, Vector3 rot, float scale, Color color)
{
World.DrawMarker(marker, pos, dir, rot, new Vector3(scale, scale, scale), color);
}
unsafe ulong GetWheelsPtr(Vehicle handle)
{
var address = (ulong)handle.MemoryAddress;
ulong offset = 0xBB0; //TODO: Future-proof
return *((ulong*)(address + offset));
}
private unsafe int GetNumWheels(Vehicle v)
{
return *(int*)((ulong)v.MemoryAddress + 0xBB8); //TODO: Future-proof
}
unsafe List<ulong> GetWheelPtrs(Vehicle handle)
{
var wheelPtr = GetWheelsPtr(handle);
var numWheels = GetNumWheels(handle);
List<ulong> wheelPtrs = new List<ulong>();
for (int i = 0; i < numWheels; i++)
{
var wheelAddr = *((ulong*)(wheelPtr + 0x008 * (ulong)i));
wheelPtrs.Add(wheelAddr);
}
return wheelPtrs;
}
unsafe List<float> GetWheelRotationSpeeds(Vehicle handle)
{
List<ulong> wheelPtrs = GetWheelPtrs(handle);
ulong offset = 0x170; // TODO: Future-proof
List<float> speeds = new List<float>();
foreach (var wheel in wheelPtrs)
{
speeds.Add(-*((float*)(wheel + offset)));
}
return speeds;
}
unsafe List<float> GetWheelSteeringAngles(Vehicle handle)
{
List<ulong> wheelPtrs = GetWheelPtrs(handle);
ulong offset = 0x1CC; // TODO: Future-proof
List<float> angles = new List<float>();
foreach (var wheel in wheelPtrs)
{
angles.Add(*((float*)(wheel + offset)));
}
return angles;
}
unsafe List<float> GetBrakePressures(Vehicle handle)
{
List<ulong> wheelPtrs = GetWheelPtrs(handle);
ulong offset = 0x1D0; // TODO: Future-proof
List<float> angles = new List<float>();
foreach (var wheel in wheelPtrs)
{
angles.Add(*((float*)(wheel + offset)));
}
return angles;
}
unsafe ulong GetModelInfoPtr(Vehicle handle)
{
var address = (ulong)handle.MemoryAddress;
ulong offset = 0x020; //TODO: Future-proof
return *((ulong*)(address + offset));
}
unsafe float GetDrawnWheelAngleMult(Vehicle handle)
{
return *(float*)(GetModelInfoPtr(handle) + 0x49c);
}
void ShowText(float x, float y, string text, float size = 0.2f)
{
Function.Call(Hash.SET_TEXT_FONT, 0);
Function.Call(Hash.SET_TEXT_SCALE, size, size);
Function.Call(Hash.SET_TEXT_COLOUR, 255, 0, 0, 255);
Function.Call(Hash.SET_TEXT_WRAP, 0.0, 1.0);
Function.Call(Hash.SET_TEXT_CENTRE, 0);
Function.Call(Hash.SET_TEXT_OUTLINE, true);
Function.Call(Hash._SET_TEXT_ENTRY, "STRING");
Function.Call(Hash._ADD_TEXT_COMPONENT_STRING, text);
Function.Call(Hash._DRAW_TEXT, x, y);
}
void RequestEffectLibrary(string lib)
{
Function.Call(Hash.REQUEST_NAMED_PTFX_ASSET, lib);
while (!Function.Call<bool>(Hash.HAS_NAMED_PTFX_ASSET_LOADED, lib))
Wait(0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment