using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
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("CrossbowReloadTweak")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CrossbowReloadTweak")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("a3a9671a-936a-44f2-a674-710820bfddb2")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace CrossbowReloadTweak;
[BepInPlugin("cr.crossbowreloadtweak", "Crossbow Reload Tweak", "1.1.1")]
public class CrossbowReloadTweak : BaseUnityPlugin
{
[HarmonyPatch(typeof(Player), "UpdateActionQueue")]
public class Player_UpdateActionQueue_Patch
{
private static bool Prefix(Player __instance)
{
if (__instance.m_actionQueue.Count == 0)
{
return true;
}
if (!IsPlayerReloading(__instance))
{
return true;
}
bool flag = IsPlayerMoving(__instance);
if (!flag && Instance.reloadStaminaStationaryEnabled.Value)
{
if (Instance.verboseLogging.Value)
{
logger.LogInfo((object)"UpdateActionQueue: Current action is a reload and player is not moving: Prefix");
}
originalStaminaDrain[__instance] = __instance.m_actionQueue[0].m_staminaDrain;
MinorActionData obj = __instance.m_actionQueue[0];
obj.m_staminaDrain *= Instance.reloadStaminaStationaryMod.Value;
}
else if (flag && Instance.reloadStaminaMovingEnabled.Value)
{
if (Instance.verboseLogging.Value)
{
logger.LogInfo((object)"UpdateActionQueue: Current action is a reload and player is moving: Prefix");
}
originalStaminaDrain[__instance] = __instance.m_actionQueue[0].m_staminaDrain;
MinorActionData obj2 = __instance.m_actionQueue[0];
obj2.m_staminaDrain *= Instance.reloadStaminaMovingMod.Value;
}
return true;
}
private static void Postfix(Player __instance, float dt)
{
if (__instance.m_actionQueue.Count == 0 || !IsPlayerReloading(__instance))
{
return;
}
bool flag = IsPlayerMoving(__instance);
if (flag && Instance.reloadSpeedMovingEnabled.Value)
{
MinorActionData obj = __instance.m_actionQueue[0];
obj.m_time += dt * Instance.reloadSpeedMovingMod.Value;
if (Instance.verboseLogging.Value)
{
logger.LogInfo((object)"UpdateActionQueue: Current action is a reload and player is moving: Postfix");
logger.LogInfo((object)("UpdateActionQueue: Current reload speed modifier is : " + Instance.reloadSpeedMovingMod.Value));
}
}
else if (!flag && Instance.reloadSpeedStationaryEnabled.Value)
{
MinorActionData obj2 = __instance.m_actionQueue[0];
obj2.m_time += dt * Instance.reloadSpeedStationaryMod.Value;
if (Instance.verboseLogging.Value)
{
logger.LogInfo((object)"UpdateActionQueue: Current action is a reload and player is not moving: Postfix");
logger.LogInfo((object)("UpdateActionQueue: Current reload speed modifier is : " + Instance.reloadSpeedStationaryMod.Value));
}
}
if (!((Character)__instance).HaveStamina(0f) && Instance.noStaminaReloadSpeedEnabled.Value)
{
MinorActionData obj3 = __instance.m_actionQueue[0];
obj3.m_time += dt * Instance.noStaminaReloadSpeedMod.Value;
logger.LogInfo((object)"UpdateActionQueue: Current action is a reload and player has no stamina: Postfix");
logger.LogInfo((object)("UpdateActionQueue: Current reload speed modifier is : " + Instance.noStaminaReloadSpeedMod.Value));
}
if (originalStaminaDrain.TryGetValue(__instance, out var value))
{
__instance.m_actionQueue[0].m_staminaDrain = value;
originalStaminaDrain.Remove(__instance);
}
}
}
[HarmonyPatch(typeof(Player), "GetJogSpeedFactor")]
public class Player_GetJogSpeedFactor_Patch
{
private static void Postfix(Player __instance, ref float __result)
{
if (!IsPlayerReloading(__instance))
{
return;
}
if (Instance.movementSpeedEnabled.Value)
{
__result *= Instance.movementSpeedModifier.Value;
if (Instance.verboseLogging.Value)
{
logger.LogInfo((object)("GetJogSpeedFactor: Player is reloading, jog speed factor is: " + __result));
}
}
if (!((Character)__instance).HaveStamina(0f) && Instance.noStaminaMovementSpeedEnabled.Value)
{
__result *= Instance.noStaminaMovementSpeedMod.Value;
if (Instance.verboseLogging.Value)
{
logger.LogInfo((object)("GetJogSpeedFactor: Player is reloading an has no stamina, jog speed factor is: " + __result));
}
}
}
}
private readonly Harmony HarmonyInstance = new Harmony("cr.crossbowreloadtweak");
private static ManualLogSource logger;
private ConfigEntry<float> movementDetectionSpeed;
private ConfigEntry<float> movementSpeedModifier;
private ConfigEntry<bool> movementSpeedEnabled;
private ConfigEntry<float> reloadSpeedStationaryMod;
private ConfigEntry<bool> reloadSpeedStationaryEnabled;
private ConfigEntry<float> reloadSpeedMovingMod;
private ConfigEntry<bool> reloadSpeedMovingEnabled;
private ConfigEntry<float> reloadStaminaMovingMod;
private ConfigEntry<bool> reloadStaminaMovingEnabled;
private ConfigEntry<float> reloadStaminaStationaryMod;
private ConfigEntry<bool> reloadStaminaStationaryEnabled;
private ConfigEntry<float> noStaminaMovementSpeedMod;
private ConfigEntry<bool> noStaminaMovementSpeedEnabled;
private ConfigEntry<float> noStaminaReloadSpeedMod;
private ConfigEntry<bool> noStaminaReloadSpeedEnabled;
private static Dictionary<Player, float> originalStaminaDrain = new Dictionary<Player, float>();
private ConfigEntry<bool> verboseLogging;
public static CrossbowReloadTweak Instance { get; private set; }
private void Awake()
{
Instance = this;
logger = ((BaseUnityPlugin)this).Logger;
movementDetectionSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Other", "Movement Detection Speed", 0.001f, "Only useful to change if you are using a controller. Any movement speed in movement direction biger than this value would be consider moving.");
movementSpeedModifier = ((BaseUnityPlugin)this).Config.Bind<float>("Reload Movement Speed", "Modifier", 0.65f, "Movement speed modifier value. Affects jogging speed while reloading. 0 - 1 decereases movement speed. Anything above '1' is speed increase. Anything negative inverts movement direction");
movementSpeedEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Reload Movement Speed", "Enable", true, "Activates movement speed modifier");
reloadSpeedStationaryMod = ((BaseUnityPlugin)this).Config.Bind<float>("Stationary Reload Speed", "Modifier", 0.4f, "Reload speed modifier value while stationary. Affects reload animation clock while not moving. '0' no change, '1' double speed. Anything between '-1' to '0' is a decrease");
reloadSpeedStationaryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Stationary Reload Speed", "Enable", true, "Activates stationary reload speed modifier");
reloadSpeedMovingMod = ((BaseUnityPlugin)this).Config.Bind<float>("Moving Reload Speed", "Modifier", -0.3f, "Reload speed modifier value while moving. Affects reload animation clock while moving. '0' no change, '1' double speed. Anything between '-1' to '0' is a decrease.");
reloadSpeedMovingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Moving Reload Speed", "Enable", true, "Activates moving reload speed modifier.");
reloadStaminaStationaryMod = ((BaseUnityPlugin)this).Config.Bind<float>("Stationary Reload Stamina Drain", "Modifier", 0f, "Multiplies vanilla stamina drain with this value while stationary. '0' no stamina drain, '1' same as vanilla, '2' double stamina drain.");
reloadStaminaStationaryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Stationary Reload Stamina Drain", "Enable", false, "Activates stationary stamina drain modifier.");
reloadStaminaMovingMod = ((BaseUnityPlugin)this).Config.Bind<float>("Moving Reload Stamina Drain", "Modifier", 0f, "Multiplies vanilla stamina drain with this value while moving. '0' no stamina drain, '1' same as vanilla, '2' double stamina drain.");
reloadStaminaMovingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Moving Reload Stamina Drain", "Enable", false, "Activates moving stamina drain modifier.");
noStaminaMovementSpeedMod = ((BaseUnityPlugin)this).Config.Bind<float>("No Stamina Movement Speed", "Modifier", 0f, "Controls movement speed while out of stamina. Works same as *Reload Movement Speed*");
noStaminaMovementSpeedEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("No Stamina Movement Speed", "Enable", false, "Activates no stamina movement speed modifier");
noStaminaReloadSpeedMod = ((BaseUnityPlugin)this).Config.Bind<float>("No Stamina Reload Speed", "Modifier", 0f, "Controls reload speed while out of stamina. Works same as Stationary Reload Speed");
noStaminaReloadSpeedEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("No Stamina Reload Speed", "Enable", false, "Activates no stamina reload speed modifier");
verboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable Verbose Logging", false, "Enables verbose logging.");
Assembly executingAssembly = Assembly.GetExecutingAssembly();
HarmonyInstance.PatchAll(executingAssembly);
}
public static bool IsPlayerReloading(Player player)
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Invalid comparison between Unknown and I4
if (player.m_actionQueue.Count == 0)
{
return false;
}
if ((int)player.m_actionQueue[0].m_type == 2)
{
return true;
}
return false;
}
public static bool IsPlayerMoving(Player player)
{
if ((Object)(object)player == (Object)null)
{
return false;
}
if (((Vector3)(ref ((Character)player).m_moveDir)).magnitude < Instance.movementDetectionSpeed.Value)
{
return false;
}
return true;
}
}