Decompiled source of DSP CustomFastStart v0.1.2

DSP_CustomFastStart.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DSP_CustomFastStart.CustomFastStart.Patchers;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
[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 DSP_CustomFastStart.CustomFastStart
{
	[BepInPlugin("stat0s2p.dsp.custom-fast-start", "DSP Custom Fast Start", "0.1.2")]
	public class CustomFastStartPlugin : BaseUnityPlugin
	{
		internal static readonly ManualLogSource Log = Logger.CreateLogSource("DSP Custom Fast Start");

		internal static ConfigEntry<bool> EnableFastStart = null;

		internal static ConfigEntry<bool> ClearPackageBeforeGrantItems = null;

		internal static ConfigEntry<string> TechIdsCombat = null;

		internal static ConfigEntry<string> TechIdsNonCombat = null;

		internal static ConfigEntry<string> ItemsCombat = null;

		internal static ConfigEntry<string> ItemsNonCombat = null;

		private Harmony? _harmony;

		internal void Awake()
		{
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			EnableFastStart = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableFastStart", true, "Whether custom fast start is enabled.");
			ClearPackageBeforeGrantItems = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ClearPackageBeforeGrantItems", true, "Whether to clear player package before adding configured items.");
			TechIdsCombat = ((BaseUnityPlugin)this).Config.Bind<string>("Combat", "TechIds", string.Empty, "Tech list for combat mode. Format: techId1;techId2.");
			TechIdsNonCombat = ((BaseUnityPlugin)this).Config.Bind<string>("NonCombat", "TechIds", string.Empty, "Tech list for non-combat mode. Format: techId1;techId2.");
			ItemsCombat = ((BaseUnityPlugin)this).Config.Bind<string>("Combat", "Items", "2003:300;2318:50;2316:30;2319:50;2210:20;2902:20;2202:100;2201:100;2212:30;1210:100;1131:1000;2014:200;2020:50;1804:50;2307:20;2308:20;2317:30;2310:20;2103:10;2104:10;2105:10;3008:20;3007:20;3009:20;3002:50", "Item list for combat mode. Format: itemId(:count);itemId2(:count2).");
			ItemsNonCombat = ((BaseUnityPlugin)this).Config.Bind<string>("NonCombat", "Items", "2003:300;2318:50;2316:30;2319:50;2210:20;2902:20;2202:100;2201:100;2212:30;1210:100;1131:1000;2014:200;2020:50;1804:50;2307:20;2308:20;2317:30;2310:20;2103:10;2104:10;2105:10", "Item list for non-combat mode. Format: itemId(:count);itemId2(:count2).");
			_harmony = new Harmony("stat0s2p.dsp.custom-fast-start");
			_harmony.PatchAll(typeof(GameStartPatcher));
			Log.LogInfo((object)"Custom fast start initialized.");
		}

		internal void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class ModInfo
	{
		public const string ModGuid = "stat0s2p.dsp.custom-fast-start";

		public const string ModName = "DSP Custom Fast Start";

		public const string Version = "0.1.2";
	}
}
namespace DSP_CustomFastStart.CustomFastStart.Patchers
{
	public static class GameStartPatcher
	{
		private enum FastStartContext
		{
			Unknown,
			NewGameRequested,
			LoadedSave
		}

		private readonly struct ItemGrant
		{
			public int ItemId { get; }

			public int Count { get; }

			public ItemGrant(int itemId, int count)
			{
				ItemId = itemId;
				Count = count;
			}
		}

		private static bool _appliedForCurrentGame;

		private static bool _galaxySelectOpened;

		private static bool _startupStateLogged;

		private static bool _pendingNewGameFlow;

		private static FastStartContext _context;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(GameSave), "LoadCurrentGame")]
		public static void LoadCurrentGamePrefix()
		{
			if (_pendingNewGameFlow)
			{
				CustomFastStartPlugin.Log.LogInfo((object)"FastStart mark: LoadCurrentGame observed during pending new-game flow; keeping NewGameRequested context.");
				return;
			}
			_context = FastStartContext.LoadedSave;
			CustomFastStartPlugin.Log.LogInfo((object)"FastStart mark: session entered via LoadCurrentGame (treated as save load).");
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UIGalaxySelect), "_OnOpen")]
		public static void GalaxySelectOnOpenPostfix()
		{
			_galaxySelectOpened = true;
			_pendingNewGameFlow = false;
			_context = FastStartContext.Unknown;
			CustomFastStartPlugin.Log.LogInfo((object)"FastStart mark: galaxy select opened (new game flow).");
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UIGalaxySelect), "_OnClose")]
		public static void GalaxySelectOnClosePostfix()
		{
			if (_galaxySelectOpened)
			{
				_context = FastStartContext.NewGameRequested;
				_pendingNewGameFlow = true;
				_galaxySelectOpened = false;
				CustomFastStartPlugin.Log.LogInfo((object)"FastStart mark: galaxy select closed, new game requested.");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UIRoot), "OnGameBegin")]
		public static void OnGameBeginPostfix()
		{
			_appliedForCurrentGame = false;
			_startupStateLogged = false;
			CustomFastStartPlugin.Log.LogInfo((object)"FastStart reset: OnGameBegin.");
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(GameLogic), "LogicFrame")]
		public static void OnLogicFramePostfix()
		{
			if (_appliedForCurrentGame || !CustomFastStartPlugin.EnableFastStart.Value || (Object)(object)GameMain.instance == (Object)null || GameMain.data == null || GameMain.instance.timei < 2 || GameMain.data.mainPlayer == null)
			{
				return;
			}
			if (!_startupStateLogged)
			{
				_startupStateLogged = true;
				CustomFastStartPlugin.Log.LogInfo((object)$"FastStart check: timei={GameMain.instance.timei}, context={_context}.");
			}
			if (_context != FastStartContext.NewGameRequested)
			{
				_appliedForCurrentGame = true;
				_pendingNewGameFlow = false;
				CustomFastStartPlugin.Log.LogInfo((object)$"Fast start skipped because context is {_context}, not NewGameRequested.");
				return;
			}
			try
			{
				_appliedForCurrentGame = true;
				_pendingNewGameFlow = false;
				ApplyFastStart();
			}
			catch (Exception arg)
			{
				_appliedForCurrentGame = true;
				_pendingNewGameFlow = false;
				CustomFastStartPlugin.Log.LogError((object)$"Fast start apply failed: {arg}");
			}
		}

		private static void ApplyFastStart()
		{
			bool isCombatMode = GameMain.data.gameDesc.isCombatMode;
			string text = (isCombatMode ? CustomFastStartPlugin.TechIdsCombat.Value : CustomFastStartPlugin.TechIdsNonCombat.Value);
			string text2 = (isCombatMode ? CustomFastStartPlugin.ItemsCombat.Value : CustomFastStartPlugin.ItemsNonCombat.Value);
			bool flag = !string.IsNullOrWhiteSpace(text);
			bool flag2 = !string.IsNullOrWhiteSpace(text2);
			if (!flag && !flag2)
			{
				CustomFastStartPlugin.Log.LogInfo((object)"Fast start skipped: both tech and item configs are empty.");
				return;
			}
			CustomFastStartPlugin.Log.LogInfo((object)$"Fast start apply begin. isCombat={isCombatMode}, hasTechConfig={flag}, hasItemConfig={flag2}.");
			if (flag)
			{
				if (!TryUnlockTechs(text, out int unlockedCount, out string error))
				{
					CustomFastStartPlugin.Log.LogWarning((object)("Fast start tech config parse failed: " + error));
				}
				else
				{
					CustomFastStartPlugin.Log.LogInfo((object)$"Fast start unlocked techs: {unlockedCount}.");
				}
			}
			if (!flag2)
			{
				return;
			}
			if (!TryParseItems(text2, out List<ItemGrant> items, out string error2))
			{
				CustomFastStartPlugin.Log.LogWarning((object)("Fast start item config parse failed: " + error2));
				return;
			}
			if (CustomFastStartPlugin.ClearPackageBeforeGrantItems.Value)
			{
				ClearPackage();
			}
			int num = GrantItems(items);
			CustomFastStartPlugin.Log.LogInfo((object)$"Fast start granted item stacks: {num}.");
		}

		private static bool TryUnlockTechs(string techConfig, out int unlockedCount, out string? error)
		{
			unlockedCount = 0;
			error = null;
			if (!TryParseTechIds(techConfig, out List<int> techIds, out error))
			{
				return false;
			}
			foreach (int item in techIds)
			{
				if (!GameMain.data.history.TechUnlocked(item))
				{
					GameMain.data.history.UnlockTechUnlimited(item, true);
					unlockedCount++;
				}
			}
			return true;
		}

		private static bool TryParseTechIds(string rawConfig, out List<int> techIds, out string? error)
		{
			techIds = new List<int>();
			error = null;
			string[] array = rawConfig.Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0)
				{
					if (!int.TryParse(text, out var result))
					{
						error = "invalid tech id '" + text + "'";
						return false;
					}
					if (((ProtoSet<TechProto>)(object)LDB.techs).Select(result) == null)
					{
						error = $"tech id not found '{result}'";
						return false;
					}
					techIds.Add(result);
				}
			}
			if (techIds.Count == 0)
			{
				error = "no valid tech ids";
				return false;
			}
			return true;
		}

		private static bool TryParseItems(string rawConfig, out List<ItemGrant> items, out string? error)
		{
			items = new List<ItemGrant>();
			error = null;
			string[] array = rawConfig.Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0)
				{
					string[] array2 = text.Split(new char[1] { ':' });
					if (array2.Length == 0 || array2.Length > 2)
					{
						error = "invalid item segment '" + text + "'";
						return false;
					}
					if (!int.TryParse(array2[0].Trim(), out var result))
					{
						error = "invalid item id '" + array2[0] + "'";
						return false;
					}
					if (((ProtoSet<ItemProto>)(object)LDB.items).Select(result) == null)
					{
						error = $"item id not found '{result}'";
						return false;
					}
					int result2 = 1;
					if (array2.Length == 2 && !int.TryParse(array2[1].Trim(), out result2))
					{
						error = "invalid item count '" + array2[1] + "'";
						return false;
					}
					if (result2 <= 0)
					{
						error = $"item count must be > 0 for item '{result}'";
						return false;
					}
					items.Add(new ItemGrant(result, result2));
				}
			}
			if (items.Count == 0)
			{
				error = "no valid items";
				return false;
			}
			return true;
		}

		private static void ClearPackage()
		{
			GameData data = GameMain.data;
			object obj;
			if (data == null)
			{
				obj = null;
			}
			else
			{
				Player mainPlayer = data.mainPlayer;
				obj = ((mainPlayer != null) ? mainPlayer.package : null);
			}
			if (obj == null)
			{
				return;
			}
			StorageComponent package = GameMain.data.mainPlayer.package;
			ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
			int num = default(int);
			foreach (ItemProto val in dataArray)
			{
				if (val != null)
				{
					int iD = ((Proto)val).ID;
					int itemCount = package.GetItemCount(iD);
					if (itemCount > 0)
					{
						package.TakeTailItems(ref iD, ref itemCount, ref num, false);
					}
				}
			}
		}

		private static int GrantItems(List<ItemGrant> items)
		{
			int num = 0;
			foreach (ItemGrant item in items)
			{
				GameMain.data.mainPlayer.TryAddItemToPackage(item.ItemId, item.Count, 0, false, 0, false);
				num++;
			}
			return num;
		}
	}
}