using System; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CSync.Extensions; using CSync.Lib; using Entiffo.UnlimitedQuota.Config; using Entiffo.UnlimitedQuota.Util; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("UnlimitedQuota")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.8.0")] [assembly: AssemblyInformationalVersion("1.0.8")] [assembly: AssemblyProduct("UnlimitedQuota")] [assembly: AssemblyTitle("UnlimitedQuota")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.8.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Entiffo.UnlimitedQuota { public static class CompanyAutoRoute { private static bool _pending; private static Coroutine? _routine; public static void TryScheduleFromShipPhase(string reason) { if (!Plugin.IsEnabled || (!Plugin.Config.AutoRouteToCompany.Value && !Plugin.Config.AutoLandAtCompany.Value) || !DeadlineApplicator.IsHost() || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)TimeOfDay.Instance == (Object)null || StartOfRound.Instance.isChallengeFile || !StartOfRound.Instance.inShipPhase) { return; } TimeOfDay.Instance.UpdateProfitQuotaCurrentTime(); if ((TimeOfDay.Instance.daysUntilDeadline <= 0 || !(TimeOfDay.Instance.timeUntilDeadline > 0f)) && !_pending) { _pending = true; if (_routine != null) { ((MonoBehaviour)StartOfRound.Instance).StopCoroutine(_routine); } _routine = ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(RouteAndMaybeLand(reason)); } } private static IEnumerator RouteAndMaybeLand(string reason) { yield return (object)new WaitForSeconds(0.85f); try { StartOfRound sor = StartOfRound.Instance; TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)sor == (Object)null || (Object)(object)instance == (Object)null || !((NetworkBehaviour)sor).IsServer || !sor.inShipPhase || sor.isChallengeFile) { yield break; } instance.UpdateProfitQuotaCurrentTime(); if (instance.daysUntilDeadline > 0 && instance.timeUntilDeadline > 0f) { yield break; } bool routed = false; if (Plugin.Config.AutoRouteToCompany.Value) { routed = TryRouteNow(sor, reason); } if (!Plugin.Config.AutoLandAtCompany.Value) { yield break; } float waited = 0f; while (sor.travellingToNewLevel && waited < 45f) { waited += Time.unscaledDeltaTime; yield return null; if ((Object)(object)StartOfRound.Instance == (Object)null) { yield break; } sor = StartOfRound.Instance; } if (sor.travellingToNewLevel || !sor.inShipPhase) { Plugin.Log.LogWarning((object)"Auto-land skipped: still travelling or left ship phase."); yield break; } yield return (object)new WaitForSeconds(1.1f); if (!IsOnCompany(sor)) { Plugin.Log.LogWarning((object)"Auto-land skipped: ship is not at the Company."); } else { TryLandNow(sor, routed, reason); } } finally { _pending = false; _routine = null; } } private static bool TryRouteNow(StartOfRound sor, string reason) { if (sor.travellingToNewLevel) { return false; } int num = FindCompanyLevelIndex(sor); if (num < 0) { Plugin.Log.LogWarning((object)"Auto-route to Company failed: no company moon found in levels list."); return false; } if (IsOnCompany(sor) || sor.currentLevelID == num) { Plugin.DebugLog("Already at Company; skip route (" + reason + ")."); return false; } Terminal val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null) { return false; } Plugin.Log.LogInfo((object)($"Auto-routing to Company (levels[{num}] '{sor.levels[num].PlanetName}') " + "— 0 days left (" + reason + ").")); sor.ChangeLevelServerRpc(num, val.groupCredits); if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip("UnlimitedQuota", "0 days left — ship routed to the Company.", false, false, "UQ_AutoCompany"); } return true; } private static void TryLandNow(StartOfRound sor, bool justRouted, string reason) { if (!((NetworkBehaviour)sor).IsServer || !sor.inShipPhase || sor.travellingToNewLevel) { return; } StartMatchLever val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null) { try { val.PlayLeverPullEffectsServerRpc(true); } catch { } } Plugin.Log.LogInfo((object)$"Auto-landing at Company ({reason}, routed={justRouted})."); sor.StartGame(); if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip("UnlimitedQuota", "Landing at the Company…", false, false, "UQ_AutoLand"); } } private static bool IsOnCompany(StartOfRound sor) { if ((Object)(object)sor.currentLevel != (Object)null && IsCompanyLevel(sor.currentLevel)) { return true; } int num = FindCompanyLevelIndex(sor); if (num >= 0) { return sor.currentLevelID == num; } return false; } public static int FindCompanyLevelIndex(StartOfRound sor) { SelectableLevel[] levels = sor.levels; if (levels == null || levels.Length == 0) { return -1; } for (int i = 0; i < levels.Length; i++) { if ((Object)(object)levels[i] != (Object)null && levels[i].levelID == 3) { return i; } } for (int j = 0; j < levels.Length; j++) { if ((Object)(object)levels[j] != (Object)null && IsCompanyLevel(levels[j])) { return j; } } return -1; } public static bool IsCompanyLevel(SelectableLevel level) { if ((Object)(object)level == (Object)null) { return false; } if (level.levelID == 3) { return true; } if (!level.planetHasTime && !level.spawnEnemiesAndScrap) { return true; } string text = level.PlanetName ?? ""; if (text.IndexOf("Company", StringComparison.OrdinalIgnoreCase) < 0) { return text.IndexOf("Gordion", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } } public static class DeadlineApplicator { public static bool IsHost() { if ((Object)(object)TimeOfDay.Instance != (Object)null) { return ((NetworkBehaviour)TimeOfDay.Instance).IsServer; } return false; } public static void ApplyNewQuotaDeadline(TimeOfDay timeOfDay, bool growDailyScrap) { if (Plugin.IsEnabled && !((Object)(object)timeOfDay == (Object)null) && ((NetworkBehaviour)timeOfDay).IsServer && (!((Object)(object)StartOfRound.Instance != (Object)null) || !StartOfRound.Instance.isChallengeFile)) { float num = ComputeFor(timeOfDay); ApplyDays(timeOfDay, num, syncClients: true); SaveState.Save(0f, num, timeOfDay.profitQuota); float num2 = DeadlineCalculator.ResolveStartingQuota(timeOfDay); Plugin.Log.LogInfo((object)($"Deadline days={num} quota={timeOfDay.profitQuota} startQuota={num2:F0} " + $"min={Plugin.Config.MinDeadlineDays.Value} perDay={Plugin.Config.QuotaPerExtraDay.Value}")); } } public static void ApplyOnSaveLoad(TimeOfDay timeOfDay) { if (!Plugin.IsEnabled || (Object)(object)timeOfDay == (Object)null || !((NetworkBehaviour)timeOfDay).IsServer || ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.isChallengeFile)) { return; } float value = Plugin.Config.MinDeadlineDays.Value; float num = ComputeFor(timeOfDay); float num2 = SaveState.LoadDeadlineDays(-1f); if (num2 < value) { ApplyDays(timeOfDay, num, syncClients: true); SaveState.Save(0f, num, timeOfDay.profitQuota); Plugin.Log.LogInfo((object)$"Load: fresh deadline days={num} quota={timeOfDay.profitQuota}"); return; } float num3 = Mathf.Clamp(num, value, 20f); float totalTime = DeadlineCalculator.GetTotalTime(timeOfDay); if (num3 > num2 + 0.01f) { timeOfDay.timeUntilDeadline += (num3 - num2) * totalTime; Plugin.Log.LogInfo((object)$"Load: extended deadline {num2} → {num3} days (quota={timeOfDay.profitQuota})."); } else if (num3 + 0.01f < num2) { float num4 = num3 / Mathf.Max(num2, 1f); timeOfDay.timeUntilDeadline = Mathf.Max(0f, timeOfDay.timeUntilDeadline * num4); Plugin.Log.LogInfo((object)$"Load: shortened deadline {num2} → {num3} days (quota={timeOfDay.profitQuota})."); } timeOfDay.quotaVariables.deadlineDaysAmount = Mathf.RoundToInt(num3); timeOfDay.UpdateProfitQuotaCurrentTime(); SaveState.Save(0f, num3, timeOfDay.profitQuota); if (((NetworkBehaviour)timeOfDay).IsServer) { timeOfDay.SyncTimeClientRpc(timeOfDay.globalTime, (int)timeOfDay.timeUntilDeadline); } } public static void ResetForNewShip(TimeOfDay timeOfDay) { if (Plugin.IsEnabled && !((Object)(object)timeOfDay == (Object)null) && ((NetworkBehaviour)timeOfDay).IsServer) { SaveState.Clear(); float num = ComputeFor(timeOfDay); ApplyDays(timeOfDay, num, syncClients: true); SaveState.Save(0f, num, timeOfDay.profitQuota); Plugin.Log.LogInfo((object)$"ResetShip: deadline days={num} startingQuota={timeOfDay.profitQuota}"); } } public static void ApplyDays(TimeOfDay timeOfDay, float days, bool syncClients) { int deadlineDaysAmount = Mathf.RoundToInt(days); timeOfDay.quotaVariables.deadlineDaysAmount = deadlineDaysAmount; timeOfDay.timeUntilDeadline = DeadlineCalculator.GetTimeUntilDeadline(timeOfDay, days); timeOfDay.UpdateProfitQuotaCurrentTime(); if (syncClients && ((NetworkBehaviour)timeOfDay).IsServer) { timeOfDay.SyncTimeClientRpc(timeOfDay.globalTime, (int)timeOfDay.timeUntilDeadline); } } public static void SyncDeadlineDaysAmount(TimeOfDay timeOfDay) { if (!Plugin.IsEnabled || (Object)(object)timeOfDay == (Object)null || timeOfDay.quotaVariables == null || ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.isChallengeFile)) { return; } float totalTime = DeadlineCalculator.GetTotalTime(timeOfDay); int num = (int)Mathf.Ceil(timeOfDay.timeUntilDeadline / totalTime); if (num > 0 && num > timeOfDay.quotaVariables.deadlineDaysAmount) { timeOfDay.quotaVariables.deadlineDaysAmount = num; if ((Object)(object)StartOfRound.Instance != (Object)null) { timeOfDay.SetBuyingRateForDay(); } } } private static float ComputeFor(TimeOfDay timeOfDay) { return DeadlineCalculator.ComputeDays(timeOfDay.profitQuota, Plugin.Config.MinDeadlineDays.Value, Plugin.Config.QuotaPerExtraDay.Value, DeadlineCalculator.ResolveStartingQuota(timeOfDay)); } } [BepInPlugin("Entiffo.UnlimitedQuota", "UnlimitedQuota", "1.0.8")] [BepInDependency("com.sigurd.csync", "5.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string ModGuid = "Entiffo.UnlimitedQuota"; private readonly Harmony _harmony = new Harmony("Entiffo.UnlimitedQuota"); internal static Plugin Instance { get; private set; } internal static ModConfig Config { get; private set; } internal static ManualLogSource Log { get; private set; } internal static bool IsEnabled => Config.Enabled.Value; private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Config = new ModConfig(((BaseUnityPlugin)this).Config); _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"UnlimitedQuota 1.0.8 loaded (v81 Unlimited Quota)."); } internal static void DebugLog(string message) { if (Config.DebugLogging.Value) { Log.LogInfo((object)message); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "Entiffo.UnlimitedQuota"; public const string PLUGIN_NAME = "UnlimitedQuota"; public const string PLUGIN_VERSION = "1.0.8"; } } namespace Entiffo.UnlimitedQuota.Util { public static class DeadlineCalculator { public const float MaxDeadlineDays = 20f; public const float FallbackStartingQuota = 130f; public static float ComputeDays(float profitQuota, float minDays, float quotaPerExtraDay, float startingQuota) { float num = Mathf.Max(quotaPerExtraDay, 1f); float num2 = Mathf.Max(startingQuota, 0f); float num3 = Mathf.Floor(Mathf.Max(0f, profitQuota - num2) / num); return Mathf.Clamp(minDays + num3, minDays, 20f); } public static float ResolveStartingQuota(TimeOfDay timeOfDay) { if (timeOfDay?.quotaVariables != null && timeOfDay.quotaVariables.startingQuota > 0) { return timeOfDay.quotaVariables.startingQuota; } return 130f; } public static float GetTotalTime(TimeOfDay timeOfDay) { float num = timeOfDay.totalTime; if (num <= 0f) { num = timeOfDay.lengthOfHours * (float)timeOfDay.numberOfHours; } return Mathf.Max(num, 1f); } public static float GetTimeUntilDeadline(TimeOfDay timeOfDay, float days) { return days * GetTotalTime(timeOfDay); } } public static class SaveState { public const string PreviousDailyKey = "UQ_previousDaily"; public const string DeadlineDaysKey = "UQ_deadlineDays"; public const string PreviousQuotaKey = "UQ_previousQuota"; public static string? CurrentSaveName() { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return null; } return GameNetworkManager.Instance.currentSaveFileName; } public static float LoadPreviousDaily(float defaultValue) { string text = CurrentSaveName(); if (string.IsNullOrEmpty(text)) { return defaultValue; } return ES3.Load("UQ_previousDaily", text, defaultValue); } public static float LoadDeadlineDays(float defaultValue) { string text = CurrentSaveName(); if (string.IsNullOrEmpty(text)) { return defaultValue; } return ES3.Load("UQ_deadlineDays", text, defaultValue); } public static float LoadPreviousQuota(float defaultValue) { string text = CurrentSaveName(); if (string.IsNullOrEmpty(text)) { return defaultValue; } return ES3.Load("UQ_previousQuota", text, defaultValue); } public static void Save(float previousDaily, float deadlineDays, float profitQuota) { string text = CurrentSaveName(); if (!string.IsNullOrEmpty(text)) { ES3.Save("UQ_previousDaily", previousDaily, text); ES3.Save("UQ_deadlineDays", deadlineDays, text); ES3.Save("UQ_previousQuota", profitQuota, text); } } public static void Clear() { string text = CurrentSaveName(); if (!string.IsNullOrEmpty(text)) { if (ES3.KeyExists("UQ_previousDaily", text)) { ES3.DeleteKey("UQ_previousDaily", text); } if (ES3.KeyExists("UQ_deadlineDays", text)) { ES3.DeleteKey("UQ_deadlineDays", text); } if (ES3.KeyExists("UQ_previousQuota", text)) { ES3.DeleteKey("UQ_previousQuota", text); } } } } } namespace Entiffo.UnlimitedQuota.Patches { [HarmonyPatch(typeof(GameNetworkManager), "ResetSavedGameValues")] internal static class GameNetworkManagerPatches { [HarmonyPostfix] private static void ResetSavedGameValues_Postfix() { if (DeadlineApplicator.IsHost()) { SaveState.Clear(); Plugin.DebugLog("Cleared UnlimitedQuota save keys after ResetSavedGameValues."); } } } [HarmonyPatch(typeof(StartOfRound))] internal static class StartOfRoundPatches { [HarmonyPatch("SetTimeAndPlanetToSavedSettings")] [HarmonyPostfix] private static void SetTimeAndPlanetToSavedSettings_Postfix() { if (!((Object)(object)TimeOfDay.Instance == (Object)null)) { DeadlineApplicator.ApplyOnSaveLoad(TimeOfDay.Instance); CompanyAutoRoute.TryScheduleFromShipPhase("save load"); } } [HarmonyPatch("ResetShip")] [HarmonyPostfix] private static void ResetShip_Postfix() { if (!((Object)(object)TimeOfDay.Instance == (Object)null)) { DeadlineApplicator.ResetForNewShip(TimeOfDay.Instance); } } [HarmonyPatch("SetShipReadyToLand")] [HarmonyPostfix] private static void SetShipReadyToLand_Postfix() { CompanyAutoRoute.TryScheduleFromShipPhase("orbit ready"); } } [HarmonyPatch(typeof(TimeOfDay))] internal static class TimeOfDayPatches { [HarmonyPatch("SetNewProfitQuota")] [HarmonyPrefix] private static void SetNewProfitQuota_Prefix(TimeOfDay __instance, out int __state) { __state = (((Object)(object)__instance != (Object)null) ? __instance.timesFulfilledQuota : (-1)); } [HarmonyPatch("SetNewProfitQuota")] [HarmonyPostfix] [HarmonyAfter(new string[] { "Boxofbiscuits97.QuotraRollover" })] private static void SetNewProfitQuota_Postfix(TimeOfDay __instance, int __state) { if (!((Object)(object)__instance == (Object)null)) { if (__instance.timesFulfilledQuota == __state) { Plugin.DebugLog("Skipped deadline apply: SetNewProfitQuota did not advance " + $"(fulfilled still {__state}). Compatible with Quota Rollover cancel."); } else { DeadlineApplicator.ApplyNewQuotaDeadline(__instance, growDailyScrap: true); } } } [HarmonyPatch("SyncTimeClientRpc")] [HarmonyPostfix] private static void SyncTimeClientRpc_Postfix(TimeOfDay __instance) { DeadlineApplicator.SyncDeadlineDaysAmount(__instance); if ((Object)(object)__instance != (Object)null && ((NetworkBehaviour)__instance).IsServer) { CompanyAutoRoute.TryScheduleFromShipPhase("sync time"); } } } } namespace Entiffo.UnlimitedQuota.Config { public class ModConfig : SyncedConfig2 { public ConfigEntry DebugLogging { get; private set; } [field: SyncedEntryField] public SyncedEntry Enabled { get; private set; } [field: SyncedEntryField] public SyncedEntry MinDeadlineDays { get; private set; } [field: SyncedEntryField] public SyncedEntry QuotaPerExtraDay { get; private set; } [field: SyncedEntryField] public SyncedEntry AutoRouteToCompany { get; private set; } [field: SyncedEntryField] public SyncedEntry AutoLandAtCompany { get; private set; } public ModConfig(ConfigFile cfg) : base("Entiffo.UnlimitedQuota") { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0032: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0062: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_0092: Expected O, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown //IL_00be: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00ea: Expected O, but got Unknown //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown //IL_0116: Expected O, but got Unknown Enabled = SyncedBindingExtensions.BindSyncedEntry(cfg, new ConfigDefinition("General", "Enabled"), true, new ConfigDescription("Master toggle. When false, vanilla deadlines are used.", (AcceptableValueBase)null, Array.Empty())); MinDeadlineDays = SyncedBindingExtensions.BindSyncedEntry(cfg, new ConfigDefinition("Deadline", "MinDeadlineDays"), 3f, new ConfigDescription("Deadline days at the starting quota.", (AcceptableValueBase)null, Array.Empty())); QuotaPerExtraDay = SyncedBindingExtensions.BindSyncedEntry(cfg, new ConfigDefinition("Deadline", "QuotaPerExtraDay"), 200f, new ConfigDescription("How much the profit quota must rise above the starting quota to add +1 deadline day.", (AcceptableValueBase)null, Array.Empty())); AutoRouteToCompany = SyncedBindingExtensions.BindSyncedEntry(cfg, new ConfigDefinition("Deadline", "AutoRouteToCompany"), true, new ConfigDescription("When 0 days are left, automatically route the ship to the Company.", (AcceptableValueBase)null, Array.Empty())); AutoLandAtCompany = SyncedBindingExtensions.BindSyncedEntry(cfg, new ConfigDefinition("Deadline", "AutoLandAtCompany"), false, new ConfigDescription("If on, after routing to the Company on day 0, also pull the lever and land automatically. Default off.", (AcceptableValueBase)null, Array.Empty())); DebugLogging = cfg.Bind(new ConfigDefinition("General", "DebugLogging"), false, new ConfigDescription("Extra logs (local only).", (AcceptableValueBase)null, Array.Empty())); ConfigManager.Register((SyncedConfig2)(object)this); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }