using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CustomQuotaSystem.Patches; using HarmonyLib; using LethalNetworkAPI; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.0.0.0")] namespace CustomQuotaSystem { public static class ConfigManager { public static ConfigEntry StartingCredits { get; private set; } public static ConfigEntry StartingQuota { get; private set; } public static ConfigEntry DaysToDeadline { get; private set; } public static ConfigEntry BaseIncrease { get; private set; } public static ConfigEntry CurveSharpness { get; private set; } public static ConfigEntry RandomizerMultiplier { get; private set; } public static ConfigEntry OvertimePayPercent { get; private set; } public static ConfigEntry DisableQuota { get; private set; } public static ConfigEntry QuotaCap { get; private set; } internal static void Initialize(ConfigFile config) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown StartingCredits = config.Bind("Basic", "StartingCredits", 60, "Starting credits for a new lobby."); StartingQuota = config.Bind("Basic", "StartingQuota", 130, "Starting quota for a new lobby."); DaysToDeadline = config.Bind("Basic", "DaysToDeadline", 3, "Number of days to meet each quota."); BaseIncrease = config.Bind("Quota Scaling", "BaseIncrease", 100, "Base amount the quota increases per cycle."); CurveSharpness = config.Bind("Quota Scaling", "CurveSharpness", 16f, "Controls how fast the quota ramps up. Higher = slower growth."); RandomizerMultiplier = config.Bind("Quota Scaling", "RandomizerMultiplier", 1f, "Random variance on each quota increase. 0 = no randomness, 1 = ±50%."); OvertimePayPercent = config.Bind("Rollover", "OvertimePayPercent", 0.2f, new ConfigDescription("Percentage of excess scrap beyond (new quota - 1) that is paid as bonus credits. 0.2 = 20% of excess becomes credits.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); DisableQuota = config.Bind("Basic", "DisableQuota", true, "Completely disables the custom quota system."); QuotaCap = config.Bind("Quota Scaling", "QuotaCap", -1, "Maximum quota value. Set -1 for no limit."); } } [BepInPlugin("Syntheal.BetterRollover", "BetterRollover", "0.0.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class QuotaManager : BaseUnityPlugin { private readonly Harmony harmony = new Harmony("Syntheal.BetterRollover"); public static QuotaManager Instance; public static ManualLogSource Log; private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigManager.Initialize(((BaseUnityPlugin)this).Config); NetworkSync.Initialize(); Log.LogInfo((object)"Custom Quota System loaded!"); harmony.PatchAll(); } } } namespace CustomQuotaSystem.Patches { internal static class NetworkSync { private static LNetworkMessage _syncRolloverMessage; private static LNetworkMessage _syncDeadlineMessage; private static LNetworkMessage _syncQuotaMessage; public static void Initialize() { try { _syncRolloverMessage = LNetworkMessage.Connect("CustomQuotaSystem_SyncRollover", (Action)null, (Action)OnRolloverReceived, (Action)null); _syncDeadlineMessage = LNetworkMessage.Connect("CustomQuotaSystem_SyncDeadline", (Action)null, (Action)OnDeadlineReceived, (Action)null); _syncQuotaMessage = LNetworkMessage.Connect("CustomQuotaSystem_SyncQuota", (Action)null, (Action)OnQuotaReceived, (Action)null); QuotaManager.Log.LogInfo((object)"Network sync initialized."); } catch (Exception ex) { QuotaManager.Log.LogError((object)("Could not initialize network sync: " + ex.Message)); } } public static void SyncRolloverToClients(int rollover) { try { if (_syncRolloverMessage == null) { QuotaManager.Log.LogWarning((object)"Rollover message not initialized"); return; } _syncRolloverMessage.SendClients(rollover); QuotaManager.Log.LogInfo((object)$"Synced rollover {rollover} to clients"); } catch (Exception ex) { QuotaManager.Log.LogError((object)("Could not sync rollover: " + ex.Message)); } } public static void SyncDeadlineToClients(int deadline) { try { if (_syncDeadlineMessage == null) { QuotaManager.Log.LogWarning((object)"Deadline message not initialized"); } else { _syncDeadlineMessage.SendClients(deadline); } } catch (Exception ex) { QuotaManager.Log.LogError((object)("Could not sync deadline: " + ex.Message)); } } public static void SyncQuotaToClients(int quota) { try { if (_syncQuotaMessage == null) { QuotaManager.Log.LogWarning((object)"Quota message not initialized"); } else { _syncQuotaMessage.SendClients(quota); } } catch (Exception ex) { QuotaManager.Log.LogError((object)("Could not sync quota: " + ex.Message)); } } private static void OnRolloverReceived(int rollover) { try { TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance != (Object)null) { instance.quotaFulfilled = rollover; QuotaManager.Log.LogInfo((object)$"Client received rollover: {rollover}"); } } catch (Exception ex) { QuotaManager.Log.LogError((object)("Could not apply synced rollover on client: " + ex.Message)); } } private static void OnDeadlineReceived(int deadline) { try { TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance != (Object)null) { instance.daysUntilDeadline = deadline; if (instance.totalTime > 0f) { instance.timeUntilDeadline = instance.totalTime * (float)deadline; } } } catch (Exception ex) { QuotaManager.Log.LogError((object)("Could not apply synced deadline on client: " + ex.Message)); } } private static void OnQuotaReceived(int quota) { try { TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance != (Object)null) { instance.profitQuota = quota; } } catch (Exception ex) { QuotaManager.Log.LogError((object)("Could not apply synced quota on client: " + ex.Message)); } } } [HarmonyPatch(typeof(Terminal))] internal static class TerminalPatch { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void ApplyStartingCredits(Terminal __instance) { try { int value = ConfigManager.StartingCredits.Value; if (value >= 0) { __instance.groupCredits = value; QuotaManager.Log.LogInfo((object)$"Starting credits set to {value}."); } } catch (Exception ex) { QuotaManager.Log.LogWarning((object)("Could not apply starting credits: " + ex.Message)); } } } [HarmonyPatch(typeof(TimeOfDay))] internal static class TimeOfDayPatch { private static bool _initialQuotaApplied; [HarmonyPatch("Awake")] [HarmonyPostfix] private static void TimeOfDay_Awake_Postfix(TimeOfDay __instance) { ApplyQuotaVariables(__instance); } [HarmonyPatch("Start")] [HarmonyPostfix] private static void TimeOfDay_Start_Postfix(TimeOfDay __instance) { if (__instance.timesFulfilledQuota == 0 && !_initialQuotaApplied) { _initialQuotaApplied = true; if (((NetworkBehaviour)__instance).IsServer) { int num = (__instance.profitQuota = ConfigManager.StartingQuota.Value); __instance.quotaFulfilled = 0; QuotaManager.Log.LogInfo((object)$"Initial quota set to {num}"); NetworkSync.SyncQuotaToClients(num); NetworkSync.SyncRolloverToClients(0); } } } private static void ApplyQuotaVariables(TimeOfDay instance) { try { if (instance.quotaVariables != null) { instance.quotaVariables.startingQuota = ConfigManager.StartingQuota.Value; instance.quotaVariables.startingCredits = ConfigManager.StartingCredits.Value; QuotaManager.Log.LogInfo((object)$"Quota variables set: startingQuota={ConfigManager.StartingQuota.Value}, startingCredits={ConfigManager.StartingCredits.Value}"); } } catch (Exception ex) { QuotaManager.Log.LogError((object)("Could not apply quota settings at startup: " + ex.Message)); } } [HarmonyPatch("SetNewProfitQuota")] [HarmonyPrefix] private static bool SetNewProfitQuota_Prefix(TimeOfDay __instance, ref int ___timesFulfilledQuota, ref int ___profitQuota, ref float ___timeUntilDeadline, ref int ___quotaFulfilled, ref int ___daysUntilDeadline, ref float ___totalTime) { try { if (!((NetworkBehaviour)__instance).IsServer) { return false; } if (ConfigManager.DisableQuota.Value) { ___profitQuota = Mathf.Max(0, ConfigManager.StartingQuota.Value); SetDeadlineTimer(___totalTime, ref ___daysUntilDeadline, ref ___timeUntilDeadline); NetworkSync.SyncQuotaToClients(___profitQuota); NetworkSync.SyncRolloverToClients(0); NetworkSync.SyncDeadlineToClients(___daysUntilDeadline); return false; } int num = ___profitQuota; ___timesFulfilledQuota++; int num2 = CalculateNewQuota(num, ___timesFulfilledQuota); int prevDays = ___daysUntilDeadline; int num3 = ___quotaFulfilled - num; int num4 = num2 - 1; int num5 = Mathf.Min(num3, num4); int num6 = Mathf.Max(0, num3 - num4); int num7 = Mathf.RoundToInt((float)num6 * ConfigManager.OvertimePayPercent.Value); int num8 = SetDeadlineTimer(___totalTime, ref ___daysUntilDeadline, ref ___timeUntilDeadline, prevDays); __instance.quotaVariables.deadlineDaysAmount = num8; ___profitQuota = num2; ___quotaFulfilled = num5; ___daysUntilDeadline = num8; ___timeUntilDeadline = ___totalTime * (float)num8; __instance.SyncNewProfitQuotaClientRpc(num2, num7, ___timesFulfilledQuota); NetworkSync.SyncQuotaToClients(num2); NetworkSync.SyncRolloverToClients(num5); NetworkSync.SyncDeadlineToClients(num8); QuotaManager.Log.LogInfo((object)($"Quota {___timesFulfilledQuota}: {num} -> {num2}, " + $"overage: {num3}, rollover: {num5}/{num4}, " + $"overtime scrap: {num6}, overtime bonus: ${num7}")); return false; } catch (Exception ex) { QuotaManager.Log.LogError((object)("Could not calculate the next quota: " + ex.Message)); return true; } } private static int CalculateNewQuota(int previousQuota, int timesFulfilled) { float num = Mathf.Max(0.1f, ConfigManager.CurveSharpness.Value); float num2 = timesFulfilled; float num3 = Mathf.Clamp(1f + num2 * (num2 / num), 0f, 10000f); float num4 = 1f; float value = ConfigManager.RandomizerMultiplier.Value; if (value > 0f) { num4 = 1f + Random.Range(-0.5f, 0.5f) * value; } float num5 = (float)ConfigManager.BaseIncrease.Value * num3 * num4; int num6 = Mathf.RoundToInt(Mathf.Clamp((float)previousQuota + num5, 0f, 1E+09f)); int value2 = ConfigManager.QuotaCap.Value; if (value2 != -1) { num6 = Mathf.Min(num6, value2); } return num6; } private static int SetDeadlineTimer(float dayDuration, ref int days, ref float timeUntilDeadline, int prevDays = -1) { int num = (days = Mathf.Max(1, ConfigManager.DaysToDeadline.Value)); timeUntilDeadline = (float)num * dayDuration; return num; } } }