Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FAndCDaveCo v3.0.2
BepInEx/plugins/tesinormed.FAndCDaveCo.dll
Decompiled a year agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using InteractiveTerminalAPI.UI; using InteractiveTerminalAPI.UI.Application; using InteractiveTerminalAPI.UI.Cursor; using InteractiveTerminalAPI.UI.Screen; using LethalModDataLib.Enums; using LethalModDataLib.Events; using LethalModDataLib.Features; using LethalNetworkAPI; using LethalNetworkAPI.Utils; using LobbyCompatibility.Attributes; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using tesinormed.FAndCDaveCo.Bank; using tesinormed.FAndCDaveCo.Bank.UI; using tesinormed.FAndCDaveCo.Extensions; using tesinormed.FAndCDaveCo.Insurance; using tesinormed.FAndCDaveCo.Insurance.UI; using tesinormed.FAndCDaveCo.Network; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("tesinormed.FAndCDaveCo")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("3.0.2.0")] [assembly: AssemblyInformationalVersion("3.0.2+711e33d35204d5457e0bd61776d930848b138e84")] [assembly: AssemblyProduct("FAndCDaveCo")] [assembly: AssemblyTitle("tesinormed.FAndCDaveCo")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("3.0.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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 tesinormed.FAndCDaveCo { public class Config { public readonly ConfigEntry<bool> GameDisableDeathCreditPenalty; public readonly ConfigEntry<double> EconomicBasePremium; public readonly ConfigEntry<double> EconomicDeductible; public readonly ConfigEntry<double> EconomicDeductibleMaximum; public readonly ConfigEntry<double> EconomicDeductibleMinimum; public readonly ConfigEntry<double> StandardBasePremium; public readonly ConfigEntry<double> StandardDeductible; public readonly ConfigEntry<double> StandardDeductibleMaximum; public readonly ConfigEntry<double> StandardDeductibleMinimum; public readonly ConfigEntry<double> BespokeBasePremium; public readonly ConfigEntry<int> CoverageOption00; public readonly ConfigEntry<int> CoverageOption01; public readonly ConfigEntry<int> CoverageOption02; public readonly ConfigEntry<int> CoverageOption03; public readonly ConfigEntry<int> CoverageOption04; public readonly ConfigEntry<int> CoverageOption05; public readonly ConfigEntry<int> CoverageOption06; public readonly ConfigEntry<int> CoverageOption07; public readonly ConfigEntry<int> CoverageOption08; public readonly ConfigEntry<int> CoverageOption09; public readonly ConfigEntry<int> CoverageOption10; public readonly ConfigEntry<int> ClaimRetentionDays; public readonly ConfigEntry<double> ClaimPremiumIncrease; public readonly ConfigEntry<int> PenaltyStartDaysFromIssuance; public readonly ConfigEntry<double> PenaltyAmount; public readonly ConfigEntry<double> InterestAmount; public Config(ConfigFile configFile) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_002d: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0061: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_0095: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00c9: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown //IL_00fd: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown //IL_0131: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected O, but got Unknown //IL_0165: Expected O, but got Unknown //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Expected O, but got Unknown //IL_0199: Expected O, but got Unknown //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Expected O, but got Unknown //IL_01cd: Expected O, but got Unknown //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Expected O, but got Unknown //IL_0201: Expected O, but got Unknown //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Expected O, but got Unknown //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Expected O, but got Unknown //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Expected O, but got Unknown //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Expected O, but got Unknown //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Expected O, but got Unknown //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Expected O, but got Unknown //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Expected O, but got Unknown //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Expected O, but got Unknown //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Expected O, but got Unknown //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Expected O, but got Unknown //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Expected O, but got Unknown //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_03a0: Expected O, but got Unknown //IL_03a0: Expected O, but got Unknown //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Expected O, but got Unknown //IL_03cd: Expected O, but got Unknown //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_03f9: Expected O, but got Unknown //IL_03f9: Expected O, but got Unknown //IL_040a: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Expected O, but got Unknown //IL_042d: Expected O, but got Unknown //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_0457: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Expected O, but got Unknown //IL_0461: Expected O, but got Unknown GameDisableDeathCreditPenalty = configFile.Bind<bool>(new ConfigDefinition("Game", "DisableDeathCreditPenalty"), true, new ConfigDescription("Whether to disable the death credit penalty (only if there is a current insurance policy)", (AcceptableValueBase)null, Array.Empty<object>())); EconomicBasePremium = configFile.Bind<double>(new ConfigDefinition("Insurance.Policies.Economic", "BasePremium"), 0.07, new ConfigDescription("Initial premiums before any additional penalties due to submitted claims", (AcceptableValueBase)null, Array.Empty<object>())); EconomicDeductible = configFile.Bind<double>(new ConfigDefinition("Insurance.Policies.Economic", "Deductible"), 0.25, new ConfigDescription("Percent of claim amount that must be paid before a payout is made", (AcceptableValueBase)null, Array.Empty<object>())); EconomicDeductibleMinimum = configFile.Bind<double>(new ConfigDefinition("Insurance.Policies.Economic", "DeductibleMinimum"), 0.15, new ConfigDescription("Percent of coverage that sets the deductible minimum amount", (AcceptableValueBase)null, Array.Empty<object>())); EconomicDeductibleMaximum = configFile.Bind<double>(new ConfigDefinition("Insurance.Policies.Economic", "DeductibleMaximum"), 0.6, new ConfigDescription("Percent of coverage that sets the deductible maximum amount", (AcceptableValueBase)null, Array.Empty<object>())); StandardBasePremium = configFile.Bind<double>(new ConfigDefinition("Insurance.Policies.Standard", "BasePremium"), 0.15, new ConfigDescription("Initial premiums before any additional penalties due to submitted claims", (AcceptableValueBase)null, Array.Empty<object>())); StandardDeductible = configFile.Bind<double>(new ConfigDefinition("Insurance.Policies.Standard", "Deductible"), 0.1, new ConfigDescription("Percent of claim amount that must be paid before a payout is made", (AcceptableValueBase)null, Array.Empty<object>())); StandardDeductibleMinimum = configFile.Bind<double>(new ConfigDefinition("Insurance.Policies.Standard", "DeductibleMinimum"), 0.1, new ConfigDescription("Percent of coverage that sets the deductible minimum amount", (AcceptableValueBase)null, Array.Empty<object>())); StandardDeductibleMaximum = configFile.Bind<double>(new ConfigDefinition("Insurance.Policies.Standard", "DeductibleMaximum"), 0.3, new ConfigDescription("Percent of coverage that sets the deductible maximum amount", (AcceptableValueBase)null, Array.Empty<object>())); BespokeBasePremium = configFile.Bind<double>(new ConfigDefinition("Insurance.Policies.Bespoke", "BasePremium"), 0.35, new ConfigDescription("Initial premiums before any additional penalties due to submitted claims", (AcceptableValueBase)null, Array.Empty<object>())); CoverageOption00 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option00"), 150, (ConfigDescription)null); CoverageOption01 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option01"), 400, (ConfigDescription)null); CoverageOption02 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option02"), 800, (ConfigDescription)null); CoverageOption03 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option03"), 1500, (ConfigDescription)null); CoverageOption04 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option04"), 2250, (ConfigDescription)null); CoverageOption05 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option05"), 3600, (ConfigDescription)null); CoverageOption06 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option06"), 5500, (ConfigDescription)null); CoverageOption07 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option07"), 8000, (ConfigDescription)null); CoverageOption08 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option08"), 11125, (ConfigDescription)null); CoverageOption09 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option09"), 15250, (ConfigDescription)null); CoverageOption10 = configFile.Bind<int>(new ConfigDefinition("Insurance.Coverage", "Option10"), 18000, (ConfigDescription)null); ClaimPremiumIncrease = configFile.Bind<double>(new ConfigDefinition("Insurance.Claims", "PremiumIncrease"), 0.25, new ConfigDescription("Percent of the cumulative premium increase for each submitted claim", (AcceptableValueBase)null, Array.Empty<object>())); ClaimRetentionDays = configFile.Bind<int>(new ConfigDefinition("Insurance.Claims", "RetentionDays"), 10, new ConfigDescription("Days that claims are retained in the save (sets the maximum increase for premiums)", (AcceptableValueBase)null, Array.Empty<object>())); PenaltyStartDaysFromIssuance = configFile.Bind<int>(new ConfigDefinition("Bank.Loan.Penalty", "StartDaysFromIssuance"), 4, new ConfigDescription("Start of the late penalty in days from the issuance date", (AcceptableValueBase)null, Array.Empty<object>())); PenaltyAmount = configFile.Bind<double>(new ConfigDefinition("Bank.Loan.Penalty", "Amount"), 0.1, new ConfigDescription("Percent of the credits garnished each day", (AcceptableValueBase)null, Array.Empty<object>())); InterestAmount = configFile.Bind<double>(new ConfigDefinition("Bank.Loan", "InterestAmount"), 0.05, new ConfigDescription("Percent of the principal added on for interest (only added once)", (AcceptableValueBase)null, Array.Empty<object>())); } } [BepInPlugin("tesinormed.FAndCDaveCo", "FAndCDaveCo", "3.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency("LethalNetworkAPI", "3.0.0")] [LobbyCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public static Plugin Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } internal static Terminal Terminal { get; set; } internal Config Config { get; private set; } public State State { get; private set; } private void Awake() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) Instance = this; Logger = ((BaseUnityPlugin)this).Logger; Config = new Config(((BaseUnityPlugin)this).Config); State = new State(); SaveLoadEvents.PostSaveGameEvent += (PostSaveGameEventHandler)delegate { State.Save(); }; SaveLoadEvents.PostLoadGameEvent += (PostLoadGameEventHandler)delegate { State.Load(); }; SaveLoadEvents.PostDeleteSaveEvent += (PostDeleteSaveEventHandler)delegate { State.Reset(); HUDManagerEvents.QueuedHudTips.Clear(); }; SaveLoadEvents.PostResetSavedGameValuesEvent += (PostResetSavedGameValuesEventHandler)delegate { State.Reset(); HUDManagerEvents.QueuedHudTips.Clear(); State.Save(); }; CreditEvents.Init(); HUDManagerEvents.Init(); InteractiveTerminalManager.RegisterApplication<PolicyInformationTerminal>(new string[3] { "insurance info", "insurance information", "insurance policy" }); InteractiveTerminalManager.RegisterApplication<PolicySelectTerminal>(new string[2] { "insurance select", "insurance get" }); InteractiveTerminalManager.RegisterApplication<PolicyClaimTerminal>(new string[3] { "insurance claim", "insurance claims", "insurance make claim" }); InteractiveTerminalManager.RegisterApplication<BankLoanInformationTerminal>(new string[2] { "bank loan info", "bank loan information" }); InteractiveTerminalManager.RegisterApplication<BankLoanGetTerminal>(new string[2] { "bank loan get", "bank loan" }); InteractiveTerminalManager.RegisterApplication<BankLoanPaymentTerminal>(new string[2] { "bank loan pay", "bank loan payment" }); Harmony val = new Harmony("tesinormed.FAndCDaveCo"); Logger.LogDebug((object)"patching in progress"); val.PatchAll(Assembly.GetExecutingAssembly()); Logger.LogDebug((object)"finished patching"); Logger.LogInfo((object)"tesinormed.FAndCDaveCo (FAndCDaveCo) version 3.0.2 loaded"); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "tesinormed.FAndCDaveCo"; public const string PLUGIN_NAME = "FAndCDaveCo"; public const string PLUGIN_VERSION = "3.0.2"; } } namespace tesinormed.FAndCDaveCo.Patches { [HarmonyPatch(typeof(NetworkManager), "Initialize")] internal static class NetworkManager_Initialize_Patch { public static void Postfix(ref NetworkManager __instance) { if (LNetworkUtils.IsHostOrServer) { __instance.OnClientConnectedCallback += delegate { Plugin.Instance.State.MakeDirty(); }; } } } [HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")] internal static class RoundManager_DespawnPropsAtEndOfRound_Patch { public static void Prefix(ref RoundManager __instance) { if (!((NetworkBehaviour)__instance).IsServer || !StartOfRound.Instance.allPlayersDead || Plugin.Instance.State.Policy.Tier == PolicyTier.None) { return; } int totalScrapValue = (from grabbableObject in GameObject.Find("/Environment/HangarShip").GetComponentsInChildren<GrabbableObject>() where grabbableObject.itemProperties.isScrap && !(grabbableObject is RagdollGrabbableObject) select grabbableObject).Sum((GrabbableObject grabbableObject) => grabbableObject.scrapValue); if (totalScrapValue > 0) { int claimDay = StartOfRound.Instance.gameStats.daysSpent - 1; Plugin.Instance.State.MutateClaims(delegate(Dictionary<int, Claim> claims) { claims[claimDay] = new Claim(totalScrapValue); }); Plugin.Logger.LogInfo((object)$"crew all dead, recorded pending claim with value of {totalScrapValue} for day {claimDay}"); HUDManagerEvents.InsuranceClaimAvailable.InvokeClients(); } } } [HarmonyPatch(typeof(StartOfRound), "EndOfGame")] internal static class StartOfRound_EndOfGame_Patch { [CompilerGenerated] private sealed class <Postfix>d__2 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public IEnumerator values; public StartOfRound __instance; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <Postfix>d__2(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; if (!values.MoveNext()) { if (__instance == null || !((NetworkBehaviour)__instance).IsServer || __instance.isChallengeFile) { return false; } if ((double)TimeOfDay.Instance.timeUntilDeadline <= 0.0) { return false; } HUDManagerEvents.RunShowQueuedHudTips.InvokeClients(); return false; } } else { <>1__state = -1; } <>2__current = values.Current; <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static void Prefix(ref StartOfRound __instance) { StartOfRound val = __instance; if (val == null || !((NetworkBehaviour)val).IsServer || val.isChallengeFile) { return; } foreach (KeyValuePair<int, Claim> pair2 in Plugin.Instance.State.Claims.Where((KeyValuePair<int, Claim> pair) => StartOfRound.Instance.gameStats.daysSpent - pair.Key > Plugin.Instance.Config.ClaimRetentionDays.Value)) { Plugin.Instance.State.MutateClaims(delegate(Dictionary<int, Claim> claims) { claims.Remove(pair2.Key); }); Plugin.Logger.LogDebug((object)$"deleted old claim from day {pair2.Key}"); } if (Plugin.Instance.State.Policy != Policy.None) { if (Plugin.Terminal.groupCredits >= Plugin.Instance.State.TotalPremium) { CreditEvents.DeductGroupCredits.SendServer(Plugin.Instance.State.TotalPremium); HUDManagerEvents.InsuranceRenewalSuccess.SendClients(Plugin.Instance.State.TotalPremium); Plugin.Logger.LogDebug((object)$"insurance successfully renewed with premium of {Plugin.Instance.State.TotalPremium}"); } else { Plugin.Instance.State.Policy = Policy.None; HUDManagerEvents.InsuranceRenewalFail.InvokeClients(); Plugin.Logger.LogDebug((object)"insurance failed to renew"); } } if (Plugin.Instance.State.Loan.AmountUnpaid > 0 && Plugin.Instance.State.Loan.DaysSinceIssuance >= Plugin.Instance.Config.PenaltyStartDaysFromIssuance.Value) { int num = Math.Min((int)((double)Plugin.Terminal.groupCredits * Plugin.Instance.Config.PenaltyAmount.Value), Plugin.Instance.State.Loan.AmountUnpaid); CreditEvents.DeductGroupCredits.SendServer(num); Plugin.Logger.LogDebug((object)$"garnished ${num} from group credits due to loan nonpayment"); if (Plugin.Instance.State.Loan.AmountUnpaid - num == 0) { Plugin.Instance.State.Loan = Loan.None; Plugin.Logger.LogDebug((object)"loan fully paid off due to garnishment"); } else { Plugin.Instance.State.Loan = Plugin.Instance.State.Loan with { AmountPaid = Plugin.Instance.State.Loan.AmountPaid + num }; Plugin.Logger.LogDebug((object)$"loan amount paid increased by {num}"); } HUDManagerEvents.BankLoanCreditsGarnished.SendClients(num); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown CodeMatcher obj = new CodeMatcher(instructions, generator).SearchForward((Func<CodeInstruction, bool>)((CodeInstruction instruction) => CodeInstructionExtensions.Calls(instruction, AccessTools.Method(typeof(HUDManager), "ApplyPenalty", (Type[])null, (Type[])null)))).SearchBack((Func<CodeInstruction, bool>)((CodeInstruction instruction) => CodeInstructionExtensions.LoadsField(instruction, AccessTools.Field(typeof(StartOfRound), "isChallengeFile"), false))).SearchForward((Func<CodeInstruction, bool>)((CodeInstruction instruction) => instruction.opcode == OpCodes.Brtrue)); object operand = obj.Operand; return obj.Advance(1).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Call, (object)AccessTools.PropertyGetter(typeof(State), "DisableDeathCreditPenalty")), new CodeInstruction(OpCodes.Brtrue, operand) }).InstructionEnumeration(); } [IteratorStateMachine(typeof(<Postfix>d__2))] public static IEnumerator Postfix(IEnumerator values, StartOfRound __instance) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <Postfix>d__2(0) { values = values, __instance = __instance }; } } [HarmonyPatch(typeof(Terminal), "Awake")] internal static class Terminal_Awake_Patch { public static void Postfix(ref Terminal __instance) { Plugin.Terminal = __instance; } } } namespace tesinormed.FAndCDaveCo.Network { public static class CreditEvents { public static LNetworkMessage<int> DeductGroupCredits; public static LNetworkMessage<int> SpawnGoldBar; internal static LNetworkMessage<Tuple<NetworkObjectReference, int>> SyncItemScrap; public static LNetworkEvent TakeOutLoan; internal static void Init() { DeductGroupCredits = LNetworkMessage<int>.Connect("DeductGroupCredits", (Action<int, ulong>)delegate(int amount, ulong _) { if (Plugin.Terminal.groupCredits < amount) { Plugin.Terminal.groupCredits = 0; Plugin.Logger.LogError((object)$"could not completely deduct {amount} credits, balance set to zero"); } else { Terminal terminal = Plugin.Terminal; terminal.groupCredits -= amount; Plugin.Terminal.SyncGroupCreditsServerRpc(Plugin.Terminal.groupCredits, Plugin.Terminal.numberOfItemsInDropship); Plugin.Logger.LogDebug((object)$"deducted {amount} credits"); } }, (Action<int>)null, (Action<int, ulong>)null); SpawnGoldBar = LNetworkMessage<int>.Connect("SpawnGoldBar", (Action<int, ulong>)delegate(int value, ulong client) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) Item val = StartOfRound.Instance.allItemsList.itemsList.Single((Item item) => item.itemName.ToLower() == "gold bar"); PlayerControllerB playerController = LethalNetworkExtensions.GetPlayerController(client); if ((Object)(object)playerController == (Object)null) { Plugin.Logger.LogError((object)$"could not get player controller for client {client}"); } else { GameObject val2 = Object.Instantiate<GameObject>(val.spawnPrefab, ((Component)playerController).transform.position, Quaternion.identity, StartOfRound.Instance.elevatorTransform); GrabbableObject component2 = val2.GetComponent<GrabbableObject>(); component2.SetScrapValue(value); playerController.SetItemInElevator(true, true, component2); val2.GetComponent<NetworkObject>().Spawn(true); Plugin.Logger.LogDebug((object)$"spawned gold bar with value of {value} at client {LethalNetworkExtensions.GetClientId(playerController)}"); SyncItemScrap.SendClients(new Tuple<NetworkObjectReference, int>(NetworkObjectReference.op_Implicit(val2.GetComponent<NetworkObject>()), value)); } }, (Action<int>)null, (Action<int, ulong>)null); SyncItemScrap = LNetworkMessage<Tuple<NetworkObjectReference, int>>.Connect("SyncItemScrapValue", (Action<Tuple<NetworkObjectReference, int>, ulong>)null, (Action<Tuple<NetworkObjectReference, int>>)delegate(Tuple<NetworkObjectReference, int> value) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) GrabbableObject component = NetworkObjectReference.op_Implicit(value.Item1).GetComponent<GrabbableObject>(); component.SetScrapValue(value.Item2); GameNetworkManager.Instance.localPlayerController.SetItemInElevator(true, true, component); }, (Action<Tuple<NetworkObjectReference, int>, ulong>)null); TakeOutLoan = LNetworkEvent.Connect("TakeOutLoan", (Action<ulong>)delegate { Loan loan = new Loan(StartOfRound.Instance.gameStats.daysSpent, TimeOfDay.Instance.profitQuota - TimeOfDay.Instance.quotaFulfilled); Plugin.Instance.State.Loan = loan; Plugin.Logger.LogDebug((object)$"took out a loan for {loan.Principal}"); TimeOfDay.Instance.quotaFulfilled = TimeOfDay.Instance.profitQuota; TakeOutLoan.InvokeClients(); }, (Action)delegate { TimeOfDay.Instance.quotaFulfilled = TimeOfDay.Instance.profitQuota; TimeOfDay.Instance.UpdateProfitQuotaCurrentTime(); StartOfRound.Instance.AutoSaveShipData(); }, (Action<ulong>)null); } } public static class HUDManagerEvents { [CompilerGenerated] private sealed class <ShowQueuedHudTips>d__7 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private int <index>5__2; private Action <tip>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ShowQueuedHudTips>d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <tip>5__3 = null; <>1__state = -2; } private bool MoveNext() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <index>5__2 = 0; goto IL_00bd; case 1: <>1__state = -1; goto IL_006c; case 2: { <>1__state = -1; <tip>5__3 = null; <index>5__2++; goto IL_00bd; } IL_00bd: if (<index>5__2 < QueuedHudTips.Count) { <tip>5__3 = QueuedHudTips[<index>5__2]; if (<index>5__2 == 0) { <>2__current = (object)new WaitForSeconds(3f); <>1__state = 1; return true; } goto IL_006c; } QueuedHudTips.Clear(); return false; IL_006c: <tip>5__3(); Plugin.Logger.LogDebug((object)"displayed queued HUD tip"); <>2__current = (object)new WaitForSeconds(7.5f); <>1__state = 2; return true; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static LNetworkMessage<int> InsuranceRenewalSuccess = null; public static LNetworkEvent InsuranceRenewalFail = null; public static LNetworkEvent InsuranceClaimAvailable = null; public static LNetworkMessage<int> BankLoanCreditsGarnished = null; public static LNetworkEvent RunShowQueuedHudTips = null; internal static readonly List<Action> QueuedHudTips = new List<Action>(); internal static void Init() { InsuranceRenewalSuccess = LNetworkMessage<int>.Connect("InsuranceRenewalSuccess", (Action<int, ulong>)null, (Action<int>)delegate(int value) { QueuedHudTips.Add(delegate { HUDManager.Instance.DisplayTip("Insurance renewed", $"{value} credits have been deducted.", false, false, "LC_Tip1"); }); Plugin.Logger.LogDebug((object)"queued insurance renewal success on HUD"); }, (Action<int, ulong>)null); InsuranceRenewalFail = LNetworkEvent.Connect("InsuranceRenewalFail", (Action<ulong>)null, (Action)delegate { QueuedHudTips.Add(delegate { HUDManager.Instance.DisplayTip("Insurance canceled", "You do not have sufficient credits.", true, false, "LC_Tip1"); }); Plugin.Logger.LogDebug((object)"queued insurance renewal fail on HUD"); }, (Action<ulong>)null); InsuranceClaimAvailable = LNetworkEvent.Connect("InsuranceClaimAvailable", (Action<ulong>)null, (Action)delegate { QueuedHudTips.Add(delegate { HUDManager.Instance.DisplayTip("Insurance claim", "Confirm the pending claim at the terminal.", false, false, "LC_Tip1"); }); Plugin.Logger.LogDebug((object)"queued insurance claim available on HUD"); }, (Action<ulong>)null); BankLoanCreditsGarnished = LNetworkMessage<int>.Connect("BankLoanCreditsGarnished", (Action<int, ulong>)null, (Action<int>)delegate(int value) { QueuedHudTips.Add(delegate { HUDManager.Instance.DisplayTip("Credits garnished", $"Due to loan nonpayment, {(int)(Plugin.Instance.Config.PenaltyAmount.Value * 100.0)}% of your credits ({value}) have been garnished.", true, false, "LC_Tip1"); }); Plugin.Logger.LogDebug((object)"queued loan credit garnishing warning on HUD"); }, (Action<int, ulong>)null); RunShowQueuedHudTips = LNetworkEvent.Connect("RunShowQueuedHudTips", (Action<ulong>)null, (Action)delegate { ((MonoBehaviour)HUDManager.Instance).StartCoroutine(ShowQueuedHudTips()); }, (Action<ulong>)null); } [IteratorStateMachine(typeof(<ShowQueuedHudTips>d__7))] private static IEnumerator ShowQueuedHudTips() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ShowQueuedHudTips>d__7(0); } } public record State { public static bool DisableDeathCreditPenalty { get { if (Plugin.Instance.State.Policy.Tier != 0) { return Plugin.Instance.Config.GameDisableDeathCreditPenalty.Value; } return false; } } public Policy Policy { get { return _policy.Value; } set { _policy.Value = value; } } public Dictionary<int, Claim> Claims { get { return _claims.Value ?? new Dictionary<int, Claim>(); } set { _claims.Value = value; } } public Dictionary<int, Claim> ClaimedClaims => Claims.Where((KeyValuePair<int, Claim> pair) => pair.Value.Claimed).ToDictionary(); public Dictionary<int, Claim> UnclaimedClaims => Claims.Where((KeyValuePair<int, Claim> pair) => !pair.Value.Claimed).ToDictionary(); public double FractionalPremiumIncrease => (double)ClaimedClaims.Count * Plugin.Instance.Config.ClaimPremiumIncrease.Value; public int TotalPremium => CalculateTotalPremium(Policy.BasePremium); public Loan Loan { get { return _loan.Value; } set { _loan.Value = value; } } private readonly LNetworkVariable<Policy> _policy = LNetworkVariable<Policy>.Connect("Policy", default(Policy), (LNetworkVariableWritePerms)2, (Action<Policy, Policy>)delegate(Policy previous, Policy current) { Plugin.Logger.LogDebug((object)string.Format("{0} changed from ({1}) to ({2})", "Policy", previous, current)); }); private readonly LNetworkVariable<Dictionary<int, Claim>?> _claims = LNetworkVariable<Dictionary<int, Claim>>.Connect("Claims", (Dictionary<int, Claim>)null, (LNetworkVariableWritePerms)2, (Action<Dictionary<int, Claim>, Dictionary<int, Claim>>)delegate(Dictionary<int, Claim>? previous, Dictionary<int, Claim>? current) { Plugin.Logger.LogDebug((object)("Claims changed from (" + string.Join(", ", previous ?? new Dictionary<int, Claim>()) + ") to (" + string.Join(", ", current ?? new Dictionary<int, Claim>()) + ")")); }); private readonly LNetworkVariable<Loan> _loan = LNetworkVariable<Loan>.Connect("Loan", default(Loan), (LNetworkVariableWritePerms)2, (Action<Loan, Loan>)delegate(Loan previous, Loan current) { Plugin.Logger.LogDebug((object)string.Format("{0} changed from ({1}) to ({2})", "Loan", previous, current)); }); public void MutateClaims(Action<Dictionary<int, Claim>> mutation) { mutation(Claims); _claims.MakeDirty(); } internal int CalculateTotalPremium(Policy policy) { return CalculateTotalPremium(policy.BasePremium); } private int CalculateTotalPremium(int basePremium) { return (int)((double)basePremium * (1.0 + FractionalPremiumIncrease)); } public void MutateLoan(Action<Loan> mutation) { mutation(Loan); _loan.MakeDirty(); } internal void Save() { SaveLoadHandler.SaveData<Policy>(Policy, "Policy", (SaveLocation)0, true); SaveLoadHandler.SaveData<Dictionary<int, Claim>>(Claims, "Claims", (SaveLocation)0, true); SaveLoadHandler.SaveData<Loan>(Loan, "Loan", (SaveLocation)0, true); } internal void Load() { if (LNetworkUtils.IsHostOrServer) { Policy = SaveLoadHandler.LoadData<Policy>("Policy", (SaveLocation)0, Policy.None, true); Claims = SaveLoadHandler.LoadData<Dictionary<int, Claim>>("Claims", (SaveLocation)0, (Dictionary<int, Claim>)null, true) ?? new Dictionary<int, Claim>(); Loan = SaveLoadHandler.LoadData<Loan>("Loan", (SaveLocation)0, Loan.None, true); } } internal void Reset() { Policy = Policy.None; Claims = new Dictionary<int, Claim>(); Loan = Loan.None; } internal void MakeDirty() { _policy.MakeDirty(); _claims.MakeDirty(); _loan.MakeDirty(); } [CompilerGenerated] protected virtual bool PrintMembers(StringBuilder builder) { RuntimeHelpers.EnsureSufficientExecutionStack(); builder.Append("Policy = "); builder.Append(Policy.ToString()); builder.Append(", Claims = "); builder.Append(Claims); builder.Append(", ClaimedClaims = "); builder.Append(ClaimedClaims); builder.Append(", UnclaimedClaims = "); builder.Append(UnclaimedClaims); builder.Append(", FractionalPremiumIncrease = "); builder.Append(FractionalPremiumIncrease.ToString()); builder.Append(", TotalPremium = "); builder.Append(TotalPremium.ToString()); builder.Append(", Loan = "); builder.Append(Loan.ToString()); return true; } } } namespace tesinormed.FAndCDaveCo.Insurance { public record struct Claim { public int Value; public bool Claimed; public Claim(int value, bool claimed = false) { if (value < 0) { throw new ArgumentOutOfRangeException("value", "nonnegative number required"); } Value = value; Claimed = claimed; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("Claim { "); stringBuilder.Append(string.Format("{0} = {1}, ", "Value", Value)); stringBuilder.Append(string.Format("{0} = {1}", "Claimed", Claimed)); stringBuilder.Append(" }"); return stringBuilder.ToString(); } } public record struct Policy { [ES3NonSerializable] public int BasePremium => Tier switch { PolicyTier.HighDeductible => (int)Math.Floor((double)Coverage * Plugin.Instance.Config.EconomicBasePremium.Value), PolicyTier.LowDeductible => (int)Math.Floor((double)Coverage * Plugin.Instance.Config.StandardBasePremium.Value), PolicyTier.NoDeductible => (int)Math.Floor((double)Coverage * Plugin.Instance.Config.BespokeBasePremium.Value), _ => 0, }; [ES3NonSerializable] public double DeductiblePercent => Tier switch { PolicyTier.HighDeductible => Plugin.Instance.Config.EconomicDeductible.Value, PolicyTier.LowDeductible => Plugin.Instance.Config.StandardDeductible.Value, _ => 0.0, }; [ES3NonSerializable] public int DeductibleMinimum => Tier switch { PolicyTier.HighDeductible => (int)Math.Floor((double)Coverage * Plugin.Instance.Config.EconomicDeductibleMinimum.Value), PolicyTier.LowDeductible => (int)Math.Floor((double)Coverage * Plugin.Instance.Config.StandardDeductibleMinimum.Value), _ => 0, }; [ES3NonSerializable] public int DeductibleMaximum => Tier switch { PolicyTier.HighDeductible => (int)Math.Floor((double)Coverage * Plugin.Instance.Config.EconomicDeductibleMaximum.Value), PolicyTier.LowDeductible => (int)Math.Floor((double)Coverage * Plugin.Instance.Config.StandardDeductibleMaximum.Value), _ => 0, }; [ES3NonSerializable] public static readonly Policy None = new Policy(PolicyTier.None, 0); public PolicyTier Tier; public int Coverage; public Policy(PolicyTier tier, int coverage) { if (coverage < 0) { throw new ArgumentOutOfRangeException("coverage", "nonnegative number required"); } Tier = tier; Coverage = coverage; } public int CalculatePayout(int value) { if (value < 0) { throw new ArgumentOutOfRangeException("value", "nonnegative number required"); } return Math.Min(value, Coverage); } public int CalculateDeductible(int value) { if (value < 0) { throw new ArgumentOutOfRangeException("value", "nonnegative number required"); } return Math.Clamp((int)Math.Floor((double)CalculatePayout(value) * DeductiblePercent), DeductibleMinimum, DeductibleMaximum); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("Policy { "); stringBuilder.Append(string.Format("{0} = {1}, ", "Tier", Tier)); stringBuilder.Append(string.Format("{0} = {1}", "Coverage", Coverage)); stringBuilder.Append(" }"); return stringBuilder.ToString(); } [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { builder.Append("Tier = "); builder.Append(Tier.ToString()); builder.Append(", Coverage = "); builder.Append(Coverage.ToString()); builder.Append(", BasePremium = "); builder.Append(BasePremium.ToString()); builder.Append(", DeductiblePercent = "); builder.Append(DeductiblePercent.ToString()); builder.Append(", DeductibleMinimum = "); builder.Append(DeductibleMinimum.ToString()); builder.Append(", DeductibleMaximum = "); builder.Append(DeductibleMaximum.ToString()); return true; } } public static class PolicyExtensions { public static Policy CreatePolicy(this PolicyTier policyTier, int coverage) { return new Policy(policyTier, coverage); } } public enum PolicyTier : byte { None, HighDeductible, LowDeductible, NoDeductible } public static class PolicyTierExtensions { public static string ToFriendlyString(this PolicyTier policyTier) { return policyTier switch { PolicyTier.HighDeductible => "Economic", PolicyTier.LowDeductible => "Standard", PolicyTier.NoDeductible => "Bespoke", PolicyTier.None => "No Policy?", _ => throw new ArgumentOutOfRangeException("policyTier", $"unexpected value: {policyTier}"), }; } } } namespace tesinormed.FAndCDaveCo.Insurance.UI { public class PolicyClaimTerminal : InteractiveTerminalApplication { protected override string Title => "F&C Dave Co. Insurance: Make a claim"; private CursorElement CreateCursorElement(int day, Claim claim) { return CursorElement.Create($"Day {day}: ${claim.Value}", "", (Action)delegate { ConfirmClaim(day, claim); }, (Func<CursorElement, bool>)((CursorElement _) => true), false); } public override void Initialization() { if (Plugin.Instance.State.Policy.Tier == PolicyTier.None) { LockedNotification((ITextElement)TextElement.Create("You do not have an active insurance policy.")); return; } if (Plugin.Instance.State.UnclaimedClaims.Count == 0) { LockedNotification((ITextElement)TextElement.Create("You do not have any claims.")); return; } CursorElement[] seed = Array.Empty<CursorElement>(); seed = Plugin.Instance.State.UnclaimedClaims.Aggregate(seed, delegate(CursorElement[] current, KeyValuePair<int, Claim> pair) { int num = 0; CursorElement[] array = (CursorElement[])(object)new CursorElement[1 + current.Length]; ReadOnlySpan<CursorElement> readOnlySpan = new ReadOnlySpan<CursorElement>(current); readOnlySpan.CopyTo(new Span<CursorElement>(array).Slice(num, readOnlySpan.Length)); num += readOnlySpan.Length; array[num] = CreateCursorElement(pair.Key, pair.Value); return array; }); (base.MainScreen, base.MainCursorMenu) = Selection("Select a pending claim.", seed); } private void ConfirmClaim(int day, Claim claim) { int deductible = Plugin.Instance.State.Policy.CalculateDeductible(claim.Value); int payout = Plugin.Instance.State.Policy.CalculatePayout(claim.Value); Confirm(delegate { ProcessClaim(day, deductible, payout, claim); }, delegate { ConfirmDeleteClaim(day); }, (ITextElement[])(object)new ITextElement[3] { (ITextElement)TextElement.Create($"Are you sure you want to confirm this claim? You will need to pay a deductible of ${deductible}."), (ITextElement)TextElement.Create($"Your premiums will increase by {(int)(Plugin.Instance.State.FractionalPremiumIncrease * 100.0)}%."), (ITextElement)TextElement.Create($"You will be given back ${payout}.") }); } private void ConfirmDeleteClaim(int day) { Confirm(delegate { DeleteClaim(day); }, base.PreviousMainScreenAction, (ITextElement)TextElement.Create($"Do you want to delete this claim for day {day}?")); } private void DeleteClaim(int day) { Plugin.Instance.State.MutateClaims(delegate(Dictionary<int, Claim> claims) { claims.Remove(day); }); LockedNotification((ITextElement)TextElement.Create($"The claim for day {day} has been deleted.")); } private void ProcessClaim(int day, int deductible, int payout, Claim claim) { if (((TerminalApplication)this).terminal.groupCredits < deductible) { Notification(base.PreviousMainScreenAction, (ITextElement)TextElement.Create("You do not have enough credits to continue with this claim.")); return; } Plugin.Instance.State.MutateClaims(delegate(Dictionary<int, Claim> claims) { claims[day] = claim with { Claimed = true }; }); CreditEvents.DeductGroupCredits.SendServer(deductible); CreditEvents.SpawnGoldBar.SendServer(payout); LockedNotification((ITextElement)TextElement.Create($"You have been given a gold bar worth ${payout}."), (ITextElement)TextElement.Create($"You have been charged ${deductible}."), (ITextElement)TextElement.Create($"Your premiums have increased by {(int)(Plugin.Instance.State.FractionalPremiumIncrease * 100.0)}%.")); } } public class PolicyInformationTerminal : TerminalApplication { public override void Initialization() { List<ITextElement> list = new List<ITextElement>(); if (Plugin.Instance.State.Policy.Tier == PolicyTier.None) { list.Add((ITextElement)(object)TextElement.Create("You do not currently have an insurance policy.")); } else { list.Add((ITextElement)(object)TextElement.Create($"You currently have the {Plugin.Instance.State.Policy.Tier.ToFriendlyString()} policy (${Plugin.Instance.State.Policy.Coverage}).")); list.Add((ITextElement)(object)TextElement.Create($"You have made {Plugin.Instance.State.ClaimedClaims.Count} claim(s) recently (within {Plugin.Instance.Config.ClaimRetentionDays.Value} days).")); list.Add((ITextElement)(object)((Plugin.Instance.State.FractionalPremiumIncrease == 0.0) ? TextElement.Create($"You pay ${Plugin.Instance.State.TotalPremium} per day.") : TextElement.Create($"You pay ${Plugin.Instance.State.TotalPremium} (+{(int)(Plugin.Instance.State.FractionalPremiumIncrease * 100.0)}%) per day."))); list.Add((ITextElement)(object)((Plugin.Instance.State.Policy.DeductiblePercent != 0.0) ? TextElement.Create($"Your deductible is currently {(int)(Plugin.Instance.State.Policy.DeductiblePercent * 100.0)}% (minimum: ${Plugin.Instance.State.Policy.DeductibleMinimum}, maximum: ${Plugin.Instance.State.Policy.DeductibleMaximum}).") : TextElement.Create("You do not pay deductibles."))); } BoxedScreen currentScreen = BoxedScreen.Create("F&C Dave Co. Insurance: Policy information", list.ToArray()); ((TerminalApplication)this).currentScreen = (IScreen)(object)currentScreen; } } public class PolicySelectTerminal : InteractiveTerminalApplication { protected override string Title => "F&C Dave Co. Insurance: Selection"; private CursorElement CreatePolicyTierCursorElement(PolicyTier policyTier, string description) { return CursorElement.Create(policyTier.ToFriendlyString() + ": " + description, "", (Action)delegate { SelectCoverage(policyTier); }, (Func<CursorElement, bool>)((CursorElement _) => true), false); } private CursorElement CreateCoverageCursorElement(Policy policy) { return CursorElement.Create($"${policy.Coverage}", "", (Action)delegate { ConfirmEverything(policy); }, (Func<CursorElement, bool>)((CursorElement _) => ((TerminalApplication)this).terminal.groupCredits >= Plugin.Instance.State.CalculateTotalPremium(policy)), true); } public override void Initialization() { if (StartOfRound.Instance.isChallengeFile) { LockedNotification((ITextElement)TextElement.Create("You cannot get an insurance policy while doing a challenge moon.")); return; } (base.MainScreen, base.MainCursorMenu) = Selection("Select a policy tier.", CreatePolicyTierCursorElement(PolicyTier.HighDeductible, "High deductible, low premium"), CreatePolicyTierCursorElement(PolicyTier.LowDeductible, "Low deductible, medium premium"), CreatePolicyTierCursorElement(PolicyTier.NoDeductible, "No deductible, high premium"), CursorElement.Create("Cancel policy", "", (Action)ConfirmCancelPolicy, (Func<CursorElement, bool>)((CursorElement _) => Plugin.Instance.State.Policy.Tier != PolicyTier.None), true)); } private void ConfirmCancelPolicy() { if (Plugin.Instance.State.Policy.Tier == PolicyTier.None) { Notification(base.PreviousScreenAction, (ITextElement)TextElement.Create("You do not currently have a policy.")); } else { Confirm(CancelPolicy, base.PreviousScreenAction, (ITextElement)TextElement.Create("Are you sure you want to cancel your current policy?"), (ITextElement)TextElement.Create($"Your current policy is {Plugin.Instance.State.Policy.Tier.ToFriendlyString()} (${Plugin.Instance.State.Policy.Coverage}).")); } } private void CancelPolicy() { Plugin.Instance.State.Policy = Policy.None; LockedNotification((ITextElement)TextElement.Create("Your policy has been canceled.")); } private void SelectCoverage(PolicyTier policyTier) { SelectionWithBack("Select the coverage amount.", base.PreviousMainScreenAction, CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption00.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption01.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption02.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption03.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption04.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption05.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption06.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption07.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption08.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption09.Value)), CreateCoverageCursorElement(policyTier.CreatePolicy(Plugin.Instance.Config.CoverageOption10.Value))); } private void ConfirmEverything(Policy policy) { if (policy == Plugin.Instance.State.Policy) { Notification(base.PreviousScreenAction, (ITextElement)TextElement.Create("You already have this policy.")); return; } if (((TerminalApplication)this).terminal.groupCredits < Plugin.Instance.State.CalculateTotalPremium(policy)) { Notification(base.PreviousScreenAction, (ITextElement)TextElement.Create("You do not have enough credits to purchase this policy.")); return; } Confirm(delegate { SetEverything(policy, Plugin.Instance.State.CalculateTotalPremium(policy)); }, base.PreviousScreenAction, (ITextElement)TextElement.Create($"Are you sure you want to pick {policy.Tier.ToFriendlyString()} (${policy.Coverage})?"), (ITextElement)TextElement.Create((policy.DeductiblePercent == 0.0) ? "This policy has no deductibles." : $"The deductibles are {(int)(policy.DeductiblePercent * 100.0)}%, with a minimum of ${policy.DeductibleMinimum} and a maximum of ${policy.DeductibleMaximum}."), (ITextElement)TextElement.Create($"You will be charged ${Plugin.Instance.State.CalculateTotalPremium(policy)}.")); } private void SetEverything(Policy policy, int cost) { Plugin.Instance.State.Policy = policy; CreditEvents.DeductGroupCredits.SendServer(cost); LockedNotification((ITextElement)TextElement.Create($"Your policy has been updated to {policy.Tier.ToFriendlyString()} (${policy.Coverage})."), (ITextElement)TextElement.Create($"You have been charged ${cost}.")); } } internal static class PolicyTerminal { internal const string Title = "F&C Dave Co. Insurance"; } } namespace tesinormed.FAndCDaveCo.Extensions { public static class DictionaryExtensions { public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> dictionary) { return dictionary.ToDictionary<KeyValuePair<TKey, TValue>, TKey, TValue>((KeyValuePair<TKey, TValue> keyValuePair) => keyValuePair.Key, (KeyValuePair<TKey, TValue> keyValuePair) => keyValuePair.Value); } } public abstract class InteractiveTerminalApplication : InteractiveTerminalApplication { protected abstract string Title { get; } protected IScreen MainScreen { get; set; } protected CursorMenu MainCursorMenu { get; set; } private IScreen? PreviousScreen { get; set; } private CursorMenu? PreviousCursorMenu { get; set; } protected Action PreviousMainScreenAction => delegate { ((BaseInteractiveApplication<CursorMenu, CursorElement>)(object)this).SwitchScreen(MainScreen, MainCursorMenu, true); }; protected Action PreviousScreenAction => delegate { ((BaseInteractiveApplication<CursorMenu, CursorElement>)(object)this).SwitchScreen(PreviousScreen ?? throw new InvalidOperationException("no previous screen"), PreviousCursorMenu ?? throw new InvalidOperationException("no previous cursor menu"), true); }; protected override void SwitchScreen(IScreen screen, CursorMenu cursorMenu, bool previous) { PreviousScreen = ((TerminalApplication)this).currentScreen; PreviousCursorMenu = ((BaseInteractiveApplication<CursorMenu, CursorElement>)this).currentCursorMenu; ((BaseInteractiveApplication<CursorMenu, CursorElement>)this).SwitchScreen(screen, cursorMenu, previous); } protected (IScreen, CursorMenu) Selection(string prompt, params CursorElement[] options) { CursorMenu val = CursorMenu.Create(0, '>', options, (Func<CursorElement, CursorElement, int>[])null); ITextElement[] array = (ITextElement[])(object)new ITextElement[3] { (ITextElement)TextElement.Create(prompt), (ITextElement)TextElement.Create(" "), (ITextElement)val }; BoxedScreen val2 = BoxedScreen.Create(Title, array); ((BaseInteractiveApplication<CursorMenu, CursorElement>)(object)this).SwitchScreen((IScreen)(object)val2, val, false); return ((IScreen)(object)val2, val); } protected void SelectionWithBack(string prompt, Action backAction, params CursorElement[] options) { CursorElement[] array = options; int num = 0; CursorElement[] array2 = (CursorElement[])(object)new CursorElement[1 + array.Length]; ReadOnlySpan<CursorElement> readOnlySpan = new ReadOnlySpan<CursorElement>(array); readOnlySpan.CopyTo(new Span<CursorElement>(array2).Slice(num, readOnlySpan.Length)); num += readOnlySpan.Length; array2[num] = CursorElement.Create("Back", "", backAction, (Func<CursorElement, bool>)((CursorElement _) => true), false); options = array2; CursorMenu val = CursorMenu.Create(0, '>', options, (Func<CursorElement, CursorElement, int>[])null); ITextElement[] array3 = (ITextElement[])(object)new ITextElement[3] { (ITextElement)TextElement.Create(prompt), (ITextElement)TextElement.Create(" "), (ITextElement)val }; BoxedScreen val2 = BoxedScreen.Create(Title, array3); ((BaseInteractiveApplication<CursorMenu, CursorElement>)(object)this).SwitchScreen((IScreen)(object)val2, val, false); } protected void Confirm(Action confirmAction, params ITextElement[] text) { CursorMenu val = CursorMenu.Create(0, '>', (CursorElement[])(object)new CursorElement[1] { CursorElement.Create("Confirm", "", confirmAction, (Func<CursorElement, bool>)null, true) }, (Func<CursorElement, CursorElement, int>[])null); int num = 0; ITextElement[] array = (ITextElement[])(object)new ITextElement[2 + text.Length]; ReadOnlySpan<ITextElement> readOnlySpan = new ReadOnlySpan<ITextElement>(text); readOnlySpan.CopyTo(new Span<ITextElement>(array).Slice(num, readOnlySpan.Length)); num += readOnlySpan.Length; array[num] = (ITextElement)(object)TextElement.Create(" "); num++; array[num] = (ITextElement)(object)val; ITextElement[] array2 = array; BoxedScreen val2 = BoxedScreen.Create(Title, array2); ((BaseInteractiveApplication<CursorMenu, CursorElement>)(object)this).SwitchScreen((IScreen)(object)val2, val, false); } protected void Confirm(Action confirmAction, Action declineAction, params ITextElement[] text) { CursorMenu val = CursorMenu.Create(0, '>', (CursorElement[])(object)new CursorElement[2] { CursorElement.Create("Confirm", "", confirmAction, (Func<CursorElement, bool>)null, true), CursorElement.Create("Decline", "", declineAction, (Func<CursorElement, bool>)null, true) }, (Func<CursorElement, CursorElement, int>[])null); int num = 0; ITextElement[] array = (ITextElement[])(object)new ITextElement[2 + text.Length]; ReadOnlySpan<ITextElement> readOnlySpan = new ReadOnlySpan<ITextElement>(text); readOnlySpan.CopyTo(new Span<ITextElement>(array).Slice(num, readOnlySpan.Length)); num += readOnlySpan.Length; array[num] = (ITextElement)(object)TextElement.Create(" "); num++; array[num] = (ITextElement)(object)val; ITextElement[] array2 = array; BoxedScreen val2 = BoxedScreen.Create(Title, array2); ((BaseInteractiveApplication<CursorMenu, CursorElement>)(object)this).SwitchScreen((IScreen)(object)val2, val, false); } protected void Notification(Action backAction, params ITextElement[] text) { CursorMenu val = CursorMenu.Create(0, '>', (CursorElement[])(object)new CursorElement[1] { CursorElement.Create("Back", "", backAction, (Func<CursorElement, bool>)null, true) }, (Func<CursorElement, CursorElement, int>[])null); int num = 0; ITextElement[] array = (ITextElement[])(object)new ITextElement[2 + text.Length]; ReadOnlySpan<ITextElement> readOnlySpan = new ReadOnlySpan<ITextElement>(text); readOnlySpan.CopyTo(new Span<ITextElement>(array).Slice(num, readOnlySpan.Length)); num += readOnlySpan.Length; array[num] = (ITextElement)(object)TextElement.Create(" "); num++; array[num] = (ITextElement)(object)val; ITextElement[] array2 = array; BoxedScreen val2 = BoxedScreen.Create(Title, array2); ((BaseInteractiveApplication<CursorMenu, CursorElement>)(object)this).SwitchScreen((IScreen)(object)val2, val, false); } protected void LockedNotification(params ITextElement[] text) { BoxedScreen val = BoxedScreen.Create(Title, text); CursorMenu val2 = CursorMenu.Create(0, '>', (CursorElement[])null, (Func<CursorElement, CursorElement, int>[])null); ((BaseInteractiveApplication<CursorMenu, CursorElement>)(object)this).SwitchScreen((IScreen)(object)val, val2, false); } } public static class InteractiveTerminalManager { public static void RegisterApplication<T>(params string[] commands) where T : TerminalApplication, new() { for (int i = 0; i < commands.Length; i++) { InteractiveTerminalManager.RegisterApplication<T>(commands[i]); } } } public abstract class TerminalApplication : TerminalApplication { protected override string GetApplicationText() { return ((ITextElement)base.currentScreen).GetText(51); } protected override Action PreviousScreen() { return delegate { }; } } } namespace tesinormed.FAndCDaveCo.Bank { public record struct Loan { [ES3NonSerializable] public int DaysSinceIssuance => StartOfRound.Instance.gameStats.daysSpent - IssuanceDate; [ES3NonSerializable] public int Interest => CalculateInterest(Principal); [ES3NonSerializable] public int Total => Principal + Interest; [ES3NonSerializable] public int AmountUnpaid => Total - AmountPaid; [ES3NonSerializable] public static readonly Loan None = new Loan(0, 0); public int IssuanceDate; public int Principal; public int AmountPaid; public static int CalculateInterest(int principal) { return (int)((double)principal * Plugin.Instance.Config.InterestAmount.Value); } public Loan(int issuanceDate, int principal, int amountPaid = 0) { if (issuanceDate < 0) { throw new ArgumentOutOfRangeException("issuanceDate", "nonnegative number required"); } if (principal < 0) { throw new ArgumentOutOfRangeException("principal", "nonnegative number required"); } if (amountPaid < 0) { throw new ArgumentOutOfRangeException("amountPaid", "nonnegative number required"); } IssuanceDate = issuanceDate; Principal = principal; AmountPaid = amountPaid; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("Loan { "); stringBuilder.Append(string.Format("{0} = {1}, ", "IssuanceDate", IssuanceDate)); stringBuilder.Append(string.Format("{0} = {1}, ", "Principal", Principal)); stringBuilder.Append(string.Format("{0} = {1}", "AmountPaid", AmountPaid)); stringBuilder.Append(" }"); return stringBuilder.ToString(); } [CompilerGenerated] private bool PrintMembers(StringBuilder builder) { builder.Append("IssuanceDate = "); builder.Append(IssuanceDate.ToString()); builder.Append(", DaysSinceIssuance = "); builder.Append(DaysSinceIssuance.ToString()); builder.Append(", Principal = "); builder.Append(Principal.ToString()); builder.Append(", Interest = "); builder.Append(Interest.ToString()); builder.Append(", Total = "); builder.Append(Total.ToString()); builder.Append(", AmountPaid = "); builder.Append(AmountPaid.ToString()); builder.Append(", AmountUnpaid = "); builder.Append(AmountUnpaid.ToString()); return true; } } } namespace tesinormed.FAndCDaveCo.Bank.UI { public class BankLoanGetTerminal : InteractiveTerminalApplication { protected override string Title => "F&C Dave Co. Bank: Take out a loan"; public override void Initialization() { if (!StartOfRound.Instance.inShipPhase || !GameNetworkManager.Instance.localPlayerController.isInHangarShipRoom) { LockedNotification((ITextElement)TextElement.Create("You can only take out a loan while you are in orbit.")); return; } if (StartOfRound.Instance.isChallengeFile) { LockedNotification((ITextElement)TextElement.Create("You cannot take out a loan while doing a challenge moon.")); return; } if (Plugin.Instance.State.Loan.Principal != 0) { LockedNotification((ITextElement)TextElement.Create("You can only take out one loan at a time.")); return; } if (TimeOfDay.Instance.profitQuota - TimeOfDay.Instance.quotaFulfilled <= 0) { LockedNotification((ITextElement)TextElement.Create("Your quota is already fulfilled.")); return; } int principal = TimeOfDay.Instance.profitQuota - TimeOfDay.Instance.quotaFulfilled; Confirm(delegate { GetLoan(principal); }, (ITextElement)TextElement.Create($"Are you sure you want to take out a loan for ${principal}? The total cost will be ${principal + Loan.CalculateInterest(principal)}.")); } private void GetLoan(int principal) { CreditEvents.TakeOutLoan.InvokeServer(); LockedNotification((ITextElement)TextElement.Create($"You have successfully taken out a loan for ${principal}.")); } } public class BankLoanInformationTerminal : TerminalApplication { public override void Initialization() { List<ITextElement> list = new List<ITextElement>(); if (Plugin.Instance.State.Loan.Principal == 0) { list.Add((ITextElement)(object)TextElement.Create("You do not currently have an unpaid loan.")); } else { list.Add((ITextElement)(object)TextElement.Create($"You currently have an unpaid loan from {Plugin.Instance.State.Loan.DaysSinceIssuance} day(s) ago (day {Plugin.Instance.State.Loan.IssuanceDate}).")); list.Add((ITextElement)(object)TextElement.Create($"The loan is for ${Plugin.Instance.State.Loan.Principal} with ${Plugin.Instance.State.Loan.Interest} added as interest.")); list.Add((ITextElement)(object)TextElement.Create($"Amount paid: ${Plugin.Instance.State.Loan.AmountPaid}, amount left: ${Plugin.Instance.State.Loan.AmountUnpaid}, total: ${Plugin.Instance.State.Loan.Total}.")); if (Plugin.Instance.State.Loan.DaysSinceIssuance >= Plugin.Instance.Config.PenaltyStartDaysFromIssuance.Value) { list.Add((ITextElement)(object)TextElement.Create($"{(int)(Plugin.Instance.Config.PenaltyAmount.Value * 100.0)}% of your credits are currently being garnished each day.")); } } BoxedScreen currentScreen = BoxedScreen.Create("F&C Dave Co. Bank: Loan information", list.ToArray()); ((TerminalApplication)this).currentScreen = (IScreen)(object)currentScreen; } } public class BankLoanPaymentTerminal : InteractiveTerminalApplication { protected override string Title => "F&C Dave Co. Bank: Submit loan payment"; private CursorElement CreateCursorElement(double amount) { int cost = Math.Min((int)((double)Plugin.Instance.State.Loan.Total * amount), Plugin.Instance.State.Loan.AmountUnpaid); return CursorElement.Create($"{(int)(amount * 100.0)}% (${cost})", "", (Action)delegate { ConfirmPayLoan(cost); }, (Func<CursorElement, bool>)((CursorElement _) => ((TerminalApplication)this).terminal.groupCredits >= cost), true); } public override void Initialization() { if (Plugin.Instance.State.Loan.Principal == 0) { LockedNotification((ITextElement)TextElement.Create("You do not have a loan taken out.")); return; } (base.MainScreen, base.MainCursorMenu) = Selection("Pick an amount to pay towards the loan.", CreateCursorElement(0.1), CreateCursorElement(0.25), CreateCursorElement(0.5), CreateCursorElement(0.75), CreateCursorElement(1.0)); } private void ConfirmPayLoan(int amount) { if (((TerminalApplication)this).terminal.groupCredits < amount) { Notification(base.PreviousScreenAction, (ITextElement)TextElement.Create($"You do not have enough credits to submit a payment of ${amount} towards the loan.")); return; } Confirm(delegate { PayLoan(amount); }, base.PreviousScreenAction, (ITextElement)TextElement.Create($"Are you sure you want to pay ${amount}?")); } private void PayLoan(int amount) { CreditEvents.DeductGroupCredits.SendServer(amount); if (Plugin.Instance.State.Loan.AmountUnpaid - amount == 0) { Plugin.Instance.State.Loan = Loan.None; Plugin.Logger.LogDebug((object)"loan paid off fully; removed"); LockedNotification((ITextElement)TextElement.Create("You have paid off your loan fully.")); return; } Plugin.Instance.State.MutateLoan(delegate(Loan loan) { loan.AmountPaid += amount; }); Plugin.Logger.LogDebug((object)$"paid {amount} towards the current loan"); LockedNotification((ITextElement)TextElement.Create($"You have paid ${amount}; ${Plugin.Instance.State.Loan.AmountUnpaid} must still be paid.")); } } internal static class BankTerminal { internal const string Title = "F&C Dave Co. Bank"; } } namespace System.Runtime.CompilerServices { [ExcludeFromCodeCoverage] [DebuggerNonUserCode] internal static class IsExternalInit { } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }