using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("LetsRumble")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LetsRumble")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("1a75fc54-dfa5-454f-a258-8eb69e47fca1")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace LetsRumble;
[HarmonyPatch(typeof(Character), "ApplyDamage")]
public static class PlayerRumble_DamagePatch
{
private static void Postfix(Character __instance, HitData hit)
{
Player val = (Player)(object)((__instance is Player) ? __instance : null);
if (val != null)
{
float totalDamage = hit.GetTotalDamage();
if (!(totalDamage <= 0f))
{
float num = Mathf.Clamp(totalDamage / 50f, 0.1f, 1f);
int durationMs = Mathf.Clamp((int)(totalDamage * 20f), 60, 500);
Rumbler.Pulse(num * Plugin.DamageRumbleMultiplier.Value, num * Plugin.DamageRumbleMultiplier.Value, durationMs);
}
}
}
}
[HarmonyPatch(typeof(Player), "UseStamina")]
public static class PlayerRumble_StaminaPatch
{
private static float _nextStepTime = 0f;
private static bool _leftFoot = true;
private static void Postfix(Player __instance, float v)
{
if (__instance != null && Plugin.EnableStaminaRumble.Value && !(v <= 0f) && (!Plugin.DisableSneakRumble.Value || !((Character)__instance).IsSneaking()) && (!Plugin.DisableSwimmingRumble.Value || !((Character)__instance).IsSwimming()))
{
if (((Character)__instance).IsRunning())
{
HandleRunningRumble(__instance, v);
}
float maxStamina = ((Character)__instance).GetMaxStamina();
float stamina = __instance.GetStamina();
float num = Mathf.Clamp(v / maxStamina, 0.05f, 0.25f);
int durationMs = Mathf.Clamp((int)(v * 6f), 30, 120);
if (Plugin.EnableStaminaLightRumble.Value)
{
Rumbler.Pulse(num, num, durationMs);
}
if (stamina < maxStamina * 0.1f && Plugin.EnableStaminaCRITICALRumble.Value)
{
Rumbler.Pulse(0.5f * Plugin.StaminaRumbleMultiplier.Value, 0.5f * Plugin.StaminaRumbleMultiplier.Value, 250);
}
if (stamina <= 0f && Plugin.EnableStaminaCRITICALRumble.Value)
{
Rumbler.Pulse(1f * Plugin.StaminaRumbleMultiplier.Value, 1f * Plugin.StaminaRumbleMultiplier.Value, 500);
}
}
}
private static void HandleRunningRumble(Player __instance, float v)
{
if (((Character)__instance).IsRunning())
{
float equipmentMovementModifier = ((Character)__instance).GetEquipmentMovementModifier();
float time = Time.time;
float num = Mathf.Clamp(0.35f - equipmentMovementModifier * 0.02f, 0.15f, 0.35f);
if (time >= _nextStepTime)
{
TriggerFootstepRumble(_leftFoot, __instance, v);
_leftFoot = !_leftFoot;
_nextStepTime = time + num;
}
}
}
private static void TriggerFootstepRumble(bool leftFoot, Player __instance, float v)
{
float maxStamina = ((Character)__instance).GetMaxStamina();
float stamina = __instance.GetStamina();
float num = Mathf.Clamp(v / maxStamina, 0.05f, 0.25f);
float num2 = Mathf.Clamp(stamina / maxStamina / 3f, 0.15f, 0.45f);
int durationMs = 10;
if (leftFoot)
{
Rumbler.Pulse(num2, 0f, durationMs);
}
else
{
Rumbler.Pulse(0f, num2, durationMs);
}
}
}
[BepInPlugin("com.FunPlus.LetsRumble", "Lets Rumble", "0.1.0")]
public class Plugin : BaseUnityPlugin
{
public static ConfigEntry<bool> EnableDamageRumble;
public static ConfigEntry<bool> EnableStaminaRumble;
public static ConfigEntry<bool> EnableStaminaLightRumble;
public static ConfigEntry<bool> EnableStaminaHeavyRumble;
public static ConfigEntry<bool> EnableStaminaCRITICALRumble;
public static ConfigEntry<bool> DisableSneakRumble;
public static ConfigEntry<bool> DisableSwimmingRumble;
public static ConfigEntry<bool> EnableRunningRumble;
public static ConfigEntry<float> RunningRumbleIntensity;
public static ConfigEntry<float> DamageRumbleMultiplier;
public static ConfigEntry<float> StaminaRumbleMultiplier;
private Harmony _harmony;
private void Awake()
{
//IL_0173: Unknown result type (might be due to invalid IL or missing references)
//IL_017d: Expected O, but got Unknown
EnableDamageRumble = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDamageRumble", true, "Trigger rumble when the player takes damage.");
EnableStaminaRumble = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableStaminaRumble", true, "Trigger rumble when the player uses stamina.");
EnableStaminaLightRumble = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableStaminaLightRumble", true, "Enable the rumble caused by Light stamina usage.");
EnableStaminaHeavyRumble = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableStaminaHeavyRumble", true, "Enable the rumble caused by Heavy stamina usage.");
EnableStaminaCRITICALRumble = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableStaminaCRITICALRumble", true, "Enable the rumble caused by depleting stamina.");
DisableSneakRumble = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DisableSneakRumble", true, "If true, stamina rumble will NOT trigger while sneaking.");
DisableSwimmingRumble = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DisableSwimmingRumble", true, "If true, stamina rumble will NOT trigger while swimming.");
EnableRunningRumble = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableRunningRumble", true, "Trigger rumble when the player is running.");
DamageRumbleMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Intensity", "DamageRumbleMultiplier", 1f, "Scale the strength of rumble caused by damage.");
StaminaRumbleMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Intensity", "StaminaRumbleMultiplier", 1f, "Scale the strength of rumble caused by stamina usage.");
RunningRumbleIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Intensity", "RunningRumbleIntensity", 0.25f, "Base strength of running rumble (0.0–1.0).");
_harmony = new Harmony("com.FunPlus.LetsRumble");
_harmony.PatchAll();
((BaseUnityPlugin)this).Logger.LogInfo((object)"LetsRumble initialized with configurable settings!");
}
}
public static class Rumbler
{
private struct XInputVibration
{
public ushort wLeftMotorSpeed;
public ushort wRightMotorSpeed;
}
[DllImport("XInput1_4.dll")]
private static extern int XInputSetState(int dwUserIndex, ref XInputVibration pVibration);
public static void Pulse(float strengthL, float strengthR, int durationMs)
{
try
{
ushort wLeftMotorSpeed = (ushort)(Mathf.Clamp01(strengthL) * 65535f);
ushort wRightMotorSpeed = (ushort)(Mathf.Clamp01(strengthR) * 65535f);
XInputVibration xInputVibration = default(XInputVibration);
xInputVibration.wLeftMotorSpeed = wLeftMotorSpeed;
xInputVibration.wRightMotorSpeed = wRightMotorSpeed;
XInputVibration pVibration = xInputVibration;
XInputSetState(0, ref pVibration);
Timer(durationMs, Stop);
}
catch (Exception ex)
{
Debug.Log((object)("[LetsRumble] XInput error: " + ex.Message));
}
}
public static void Stop()
{
XInputVibration pVibration = default(XInputVibration);
XInputSetState(0, ref pVibration);
}
private static async void Timer(int ms, Action callback)
{
await Task.Delay(ms);
callback?.Invoke();
}
}