diff --git a/source/ContractConfigurator/ContractConfigurator.csproj b/source/ContractConfigurator/ContractConfigurator.csproj index bfe294de..30a2863d 100644 --- a/source/ContractConfigurator/ContractConfigurator.csproj +++ b/source/ContractConfigurator/ContractConfigurator.csproj @@ -139,6 +139,7 @@ + diff --git a/source/ContractConfigurator/Harmony/Contract.cs b/source/ContractConfigurator/Harmony/Contract.cs new file mode 100644 index 00000000..ff76efac --- /dev/null +++ b/source/ContractConfigurator/Harmony/Contract.cs @@ -0,0 +1,33 @@ +using Contracts; +using HarmonyLib; +using System; +using System.Collections; +using System.Threading.Tasks; + +namespace ContractConfigurator.Harmony +{ + [HarmonyPatch(typeof(Contract))] + internal class PatchContract + { + /// + /// KSP's ContractSystem calls Contract.Generate() with State.Generated when filling the offered contracts. + /// ConfiguredContract.Generate() always returns false for these calls (contractType is unset on fresh instances) so each attempt is a wasted allocation. + /// Short-circuit here before Activator.CreateInstance is reached. The pre-loader uses State.Withdrawn, which is allowed through. + /// + /// + /// + /// + /// + [HarmonyPrefix] + [HarmonyPatch("Generate", new Type[] { typeof(Type), typeof(Contract.ContractPrestige), typeof(int), typeof(Contract.State) })] + internal static bool Prefix_Generate(ref Contract __result, Type contractType, Contract.State state) + { + if (contractType == typeof(ConfiguredContract) && state == Contract.State.Generated) + { + __result = null; + return false; + } + return true; + } + } +} diff --git a/source/ContractConfigurator/Harmony/ContractSystem.cs b/source/ContractConfigurator/Harmony/ContractSystem.cs index fe78fbfd..d62da850 100644 --- a/source/ContractConfigurator/Harmony/ContractSystem.cs +++ b/source/ContractConfigurator/Harmony/ContractSystem.cs @@ -41,6 +41,39 @@ internal static void PostfixAction(ConfigNode gameNode) } } + /// + /// Make KSP count pre-loaded CC contracts toward the offered-contract quota + /// + /// + /// + [HarmonyPostfix] + [HarmonyPatch("CountContracts")] + internal static void Postfix_CountContracts(ref int __result, Contract.ContractPrestige difficulty) + { + if (ContractPreLoader.Instance == null) return; + foreach (ConfiguredContract c in ContractPreLoader.Instance.PendingContracts()) + { + if (c.Prestige == difficulty) + __result++; + } + } + + /// + /// Skip weighted random selection when ConfiguredContract is the only registered contract type. + /// + [HarmonyPrefix] + [HarmonyPatch("WeightedContractChoice")] + internal static bool Prefix_WeightedContractChoice(ref Type __result) + { + if (ContractSystem.ContractTypes?.Count == 1 && + ContractSystem.ContractTypes[0] == typeof(ConfiguredContract)) + { + __result = typeof(ConfiguredContract); + return false; + } + return true; + } + private class PostfixEnumerator : IEnumerable { public IEnumerator enumerator; diff --git a/source/ContractConfigurator/ScenarioModules/ContractPreLoader.cs b/source/ContractConfigurator/ScenarioModules/ContractPreLoader.cs index ace1864a..116aae58 100644 --- a/source/ContractConfigurator/ScenarioModules/ContractPreLoader.cs +++ b/source/ContractConfigurator/ScenarioModules/ContractPreLoader.cs @@ -29,7 +29,7 @@ public class ContractPreLoader : ScenarioModule private static System.Random rand = new System.Random(); private static int nextContractGroup = rand.Next(); - private List contracts = new List(); + private readonly List contracts = new List(); private string lastKey = null; private double lastGenerationFailure; @@ -376,7 +376,7 @@ public override void OnSave(ConfigNode node) { try { - foreach (ConfiguredContract contract in contracts.Where(c => c.ContractState == Contract.State.Offered)) + foreach (ConfiguredContract contract in contracts) { ConfigNode child = new ConfigNode("CONTRACT"); node.AddNode(child); @@ -436,9 +436,15 @@ public override void OnLoad(ConfigNode node) } } - public IEnumerable PendingContracts() + /// + /// Contracts that have been generated but not yet accepted. Not all of may be presented to the player. + /// Do not edit the returned collection directly. + /// + /// + public List PendingContracts() { - return contracts.Where(c => c.ContractState == Contract.State.Offered); + // contracts only ever holds State.Offered contracts + return contracts; } } }