Decompiled source of TungSahurFix v1.1.0

BepInEx/plugins/TungSahurFix/TungSahurFix.dll

Decompiled 5 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using REPOLib.Modules;
using REPOLib.Objects.Sdk;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TungSahurFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TungSahurFix")]
[assembly: AssemblyTitle("TungSahurFix")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace TungSahurFix;

[BepInPlugin("Tungs.TungSahurFix", "TungSahur Fix", "1.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class TungSahurFixPlugin : BaseUnityPlugin
{
	internal static TungSahurFixPlugin Instance;

	private static readonly object Sync = new object();

	private static readonly List<EnemyContent> KnownContents = new List<EnemyContent>();

	private static readonly HashSet<EnemyContent> Fixed = new HashSet<EnemyContent>();

	private static GameObject FoundPrefab;

	private void Awake()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		PluginInfo.Log = ((BaseUnityPlugin)this).Logger;
		new Harmony("Tungs.TungSahurFix").PatchAll();
		FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyContent), "_spawnObjects");
		PluginInfo.Log.LogInfo((object)("TungSahurFix loaded. _spawnObjects field resolved: " + (fieldInfo != null)));
		((MonoBehaviour)this).StartCoroutine(SceneWatch());
	}

	private static IEnumerator SceneWatch()
	{
		int tick = 0;
		while (tick < 1200)
		{
			yield return (object)new WaitForSeconds(2f);
			tick++;
			if ((Object)(object)FoundPrefab == (Object)null)
			{
				if (tick % 5 == 0)
				{
					PluginInfo.Log.LogInfo((object)("TungSahurFix: heartbeat tick " + tick + " (" + tick * 2 + "s), FoundPrefab=null"));
				}
				if (tick % 2 == 0)
				{
					ProbeSceneForSahur("t+" + tick * 2 + "s");
				}
			}
			if ((Object)(object)FoundPrefab == (Object)null && tick % 5 == 0)
			{
				AttemptBundleResolveFallback();
			}
		}
		if ((Object)(object)FoundPrefab == (Object)null)
		{
			PluginInfo.Log.LogWarning((object)"TungSahurFix: still no prefab after extended watch");
		}
	}

	private static void AttemptBundleResolveFallback()
	{
		List<EnemyContent> list;
		lock (Sync)
		{
			list = new List<EnemyContent>(KnownContents);
		}
		foreach (EnemyContent item in list)
		{
			if ((Object)(object)item == (Object)null)
			{
				continue;
			}
			lock (Sync)
			{
				if (Fixed.Contains(item))
				{
					continue;
				}
			}
			string text = SafeSetupName(item);
			if (text.StartsWith("Tung") || text.StartsWith("Sahur"))
			{
				GameObject val = TryResolvePrefabFromBundle(item);
				if ((Object)(object)val != (Object)null)
				{
					PluginInfo.Log.LogInfo((object)("TungSahurFix: bundle fallback resolved prefab '" + ((Object)val).name + "' for '" + text + "'"));
					AdoptPrefab(val);
					break;
				}
			}
		}
	}

	private static void ProbeSceneForSahur(string tag)
	{
		try
		{
			Stopwatch stopwatch = Stopwatch.StartNew();
			GameObject[] array = Resources.FindObjectsOfTypeAll<GameObject>();
			foreach (GameObject val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string name = ((Object)val).name;
					if (name != null && (name.IndexOf("Sahur", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Tung", StringComparison.OrdinalIgnoreCase) >= 0) && name.IndexOf("Roaster", StringComparison.OrdinalIgnoreCase) < 0 && (Object)(object)val.GetComponent("EnemyParent") != (Object)null)
					{
						stopwatch.Stop();
						PluginInfo.Log.LogInfo((object)("TungSahurFix[" + tag + "]: found EnemyParent GO '" + ((Object)val).name + "' (children " + val.transform.childCount + ", active " + val.activeInHierarchy + ", scan took " + stopwatch.Elapsed.TotalMilliseconds + " ms)"));
						AdoptPrefab(val);
						return;
					}
				}
			}
			stopwatch.Stop();
			PluginInfo.Log.LogInfo((object)("TungSahurFix[" + tag + "]: scan done, no Sahur prefab yet (scan took " + stopwatch.Elapsed.TotalMilliseconds + " ms)"));
		}
		catch (Exception ex)
		{
			PluginInfo.Log.LogError((object)("TungSahurFix: scene probe error: " + ex));
		}
	}

	private static void AdoptPrefab(GameObject prefab)
	{
		lock (Sync)
		{
			if ((Object)(object)FoundPrefab == (Object)(object)prefab)
			{
				return;
			}
			FoundPrefab = prefab;
		}
		PluginInfo.Log.LogInfo((object)("TungSahurFix: adopting prefab '" + ((Object)prefab).name + "'"));
		TryInjectAll();
	}

	private static void TryInjectAll()
	{
		List<EnemyContent> list;
		GameObject foundPrefab;
		lock (Sync)
		{
			list = new List<EnemyContent>(KnownContents);
			foundPrefab = FoundPrefab;
		}
		if ((Object)(object)foundPrefab == (Object)null)
		{
			return;
		}
		PluginInfo.Log.LogInfo((object)("TungSahurFix: TryInjectAll start, " + list.Count + " known content(s)"));
		foreach (EnemyContent item in list)
		{
			if ((Object)(object)item == (Object)null)
			{
				continue;
			}
			string text = SafeSetupName(item);
			if (!text.StartsWith("Tung") && !text.StartsWith("Sahur"))
			{
				continue;
			}
			lock (Sync)
			{
				if (Fixed.Contains(item))
				{
					continue;
				}
			}
			List<GameObject> list2;
			try
			{
				list2 = item.SpawnObjects;
			}
			catch
			{
				list2 = null;
			}
			if (list2 != null && list2.Count > 0)
			{
				bool flag = false;
				foreach (GameObject item2 in list2)
				{
					if ((Object)(object)item2 != (Object)null)
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					lock (Sync)
					{
						Fixed.Add(item);
					}
					PluginInfo.Log.LogInfo((object)("TungSahurFix: setup '" + text + "' already has spawn objects, skipping"));
					continue;
				}
			}
			int num = SpawnCountForSetupName(text);
			List<GameObject> list3 = new List<GameObject>(num);
			for (int i = 0; i < num; i++)
			{
				list3.Add(foundPrefab);
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyContent), "_spawnObjects");
			if (fieldInfo == null)
			{
				PluginInfo.Log.LogError((object)"TungSahurFix: _spawnObjects field not found");
				return;
			}
			PluginInfo.Log.LogInfo((object)("TungSahurFix: about to set _spawnObjects(" + num + ") on '" + text + "'"));
			try
			{
				fieldInfo.SetValue(item, list3);
			}
			catch (Exception ex)
			{
				PluginInfo.Log.LogError((object)("TungSahurFix: field set error: " + ex));
				continue;
			}
			PluginInfo.Log.LogInfo((object)("TungSahurFix: injected " + num + " spawn object(s) for '" + text + "', calling Enemies.RegisterEnemy"));
			try
			{
				Stopwatch stopwatch = Stopwatch.StartNew();
				Enemies.RegisterEnemy(item);
				stopwatch.Stop();
				lock (Sync)
				{
					Fixed.Add(item);
				}
				PluginInfo.Log.LogInfo((object)("TungSahurFix: RegisterEnemy call returned for '" + text + "' (took " + stopwatch.Elapsed.TotalMilliseconds + " ms)"));
			}
			catch (Exception ex2)
			{
				PluginInfo.Log.LogError((object)("TungSahurFix: RegisterEnemy threw: " + ex2));
			}
		}
		PluginInfo.Log.LogInfo((object)"TungSahurFix: TryInjectAll done");
	}

	private static string SafeSetupName(EnemyContent content)
	{
		try
		{
			EnemySetup setup = content.Setup;
			return ((Object)(object)setup != (Object)null) ? ((Object)setup).name : "";
		}
		catch
		{
			return "";
		}
	}

	internal static void RegisterKnown(EnemyContent content)
	{
		lock (Sync)
		{
			if (!KnownContents.Contains(content))
			{
				KnownContents.Add(content);
			}
		}
	}

	internal static void MarkFixed(EnemyContent content, GameObject prefab, string setupName, int count)
	{
		lock (Sync)
		{
			if (!KnownContents.Contains(content))
			{
				KnownContents.Add(content);
			}
			if (!Fixed.Contains(content))
			{
				Fixed.Add(content);
			}
			if ((Object)(object)FoundPrefab == (Object)null || (Object)(object)FoundPrefab == (Object)null)
			{
				FoundPrefab = prefab;
			}
		}
		PluginInfo.Log.LogInfo((object)("TungSahurFix: injected " + count + " spawn object(s) from bundle for '" + setupName + "' (prefab '" + ((Object)prefab).name + "')"));
	}

	internal static GameObject FindMaterializedSahur()
	{
		try
		{
			GameObject[] array = Resources.FindObjectsOfTypeAll<GameObject>();
			foreach (GameObject val in array)
			{
				if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(((Object)val).name))
				{
					string name = ((Object)val).name;
					if (name.IndexOf("roaster", StringComparison.OrdinalIgnoreCase) < 0 && (name.IndexOf("sahur", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("tung", StringComparison.OrdinalIgnoreCase) >= 0) && (Object)(object)val.GetComponent("EnemyParent") != (Object)null)
					{
						return val;
					}
				}
			}
		}
		catch (Exception ex)
		{
			PluginInfo.Log.LogWarning((object)("TungSahurFix: materialized scan error: " + ex.Message));
		}
		return null;
	}

	internal static GameObject TryLoadSahurByPath(AssetBundle bundle)
	{
		try
		{
			string[] allAssetNames = bundle.GetAllAssetNames();
			if (allAssetNames == null || allAssetNames.Length == 0)
			{
				return null;
			}
			GameObject val = null;
			int num = int.MaxValue;
			string[] array = allAssetNames;
			foreach (string text in array)
			{
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				GameObject val2 = bundle.LoadAsset<GameObject>(text);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				string name = ((Object)val2).name;
				if (!string.IsNullOrEmpty(name) && name.IndexOf("roaster", StringComparison.OrdinalIgnoreCase) < 0 && (name.IndexOf("sahur", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("tung", StringComparison.OrdinalIgnoreCase) >= 0) && !((Object)(object)val2.GetComponent("EnemyParent") == (Object)null))
				{
					int num2 = ((name.IndexOf("sahur", StringComparison.OrdinalIgnoreCase) < 0) ? 10 : 0);
					if (num2 < num)
					{
						num = num2;
						val = val2;
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				PluginInfo.Log.LogInfo((object)("TungSahurFix: resolved " + ((Object)val).name + " from bundle path '" + ((Object)val).name + "'"));
			}
			return val;
		}
		catch (Exception ex)
		{
			PluginInfo.Log.LogWarning((object)("TungSahurFix: name-based bundle load failed: " + ex.Message));
			return null;
		}
	}

	internal static GameObject TryResolvePrefabFromBundle(EnemyContent content)
	{
		try
		{
			EnemySetup setup = content.Setup;
			if ((Object)(object)setup != (Object)null && setup.spawnObjects != null && setup.spawnObjects.Count > 0)
			{
				int num = 0;
				for (int i = 0; i < setup.spawnObjects.Count; i++)
				{
					PrefabRef val = setup.spawnObjects[i];
					if (val == null || !((PrefabRef<GameObject>)(object)val).IsValid())
					{
						continue;
					}
					num++;
					try
					{
						GameObject prefab = ((PrefabRef<GameObject>)(object)val).Prefab;
						if ((Object)(object)prefab != (Object)null)
						{
							return prefab;
						}
					}
					catch (Exception ex)
					{
						PluginInfo.Log.LogWarning((object)("TungSahurFix: prefab ref load failed: " + ex.Message));
					}
				}
				if (num == 0)
				{
					PluginInfo.Log.LogInfo((object)("TungSahurFix: '" + SafeSetupName(content) + "' spawn refs are empty/invalid - using bundle lookup"));
				}
			}
			AssetBundle bundle = ((Content)content).Bundle;
			if ((Object)(object)bundle == (Object)null)
			{
				return null;
			}
			GameObject val2 = TryLoadSahurByPath(bundle);
			if ((Object)(object)val2 != (Object)null)
			{
				return val2;
			}
			GameObject[] array = bundle.LoadAllAssets<GameObject>();
			if (array == null || array.Length == 0)
			{
				array = bundle.LoadAllAssets<GameObject>();
			}
			if (array == null || array.Length == 0)
			{
				return FindMaterializedSahur();
			}
			string text = SafeSetupName(content).ToLowerInvariant();
			GameObject result = null;
			int num2 = int.MaxValue;
			GameObject[] array2 = array;
			foreach (GameObject val3 in array2)
			{
				if ((Object)(object)val3 == (Object)null || string.IsNullOrEmpty(((Object)val3).name))
				{
					continue;
				}
				string name = ((Object)val3).name;
				if (name.IndexOf("roaster", StringComparison.OrdinalIgnoreCase) >= 0 || (name.IndexOf("sahur", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("tung", StringComparison.OrdinalIgnoreCase) < 0) || (Object)(object)val3.GetComponent("EnemyParent") == (Object)null)
				{
					continue;
				}
				int num3 = ((name.IndexOf("sahur", StringComparison.OrdinalIgnoreCase) < 0) ? 10 : 0);
				string[] array3 = text.Split(' ', '-', '_');
				foreach (string text2 in array3)
				{
					if (text2.Length > 3 && name.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						num3 -= 2;
					}
				}
				if (num3 < num2)
				{
					num2 = num3;
					result = val3;
				}
			}
			return result;
		}
		catch (Exception ex2)
		{
			PluginInfo.Log.LogWarning((object)("TungSahurFix: bundle prefab resolution failed: " + ex2.Message));
			return null;
		}
	}

	internal static int SpawnCountForSetupName(string setupName)
	{
		return setupName switch
		{
			"Sahur Gang 2" => 2, 
			"Sahur Gang 3" => 3, 
			"Sahur Gang 4" => 4, 
			"Sahur Gang Cooked" => 20, 
			_ => 1, 
		};
	}
}
internal static class PluginInfo
{
	public const string GUID = "Tungs.TungSahurFix";

	public const string Name = "TungSahur Fix";

	public const string Version = "1.1.0";

	public static ManualLogSource Log;
}
[HarmonyPatch(typeof(Enemies), "RegisterEnemy")]
internal static class RegisterEnemyPatch
{
	public static void Prefix(EnemyContent enemyContent)
	{
		try
		{
			if ((Object)(object)enemyContent == (Object)null)
			{
				return;
			}
			EnemySetup setup = enemyContent.Setup;
			string text = (((Object)(object)setup != (Object)null) ? ((Object)setup).name : "");
			if (!text.StartsWith("Tung") && !text.StartsWith("Sahur"))
			{
				return;
			}
			List<GameObject> list = null;
			try
			{
				list = enemyContent.SpawnObjects;
			}
			catch
			{
				list = null;
			}
			bool flag = false;
			if (list != null && list.Count > 0)
			{
				flag = true;
				foreach (GameObject item in list)
				{
					if ((Object)(object)item == (Object)null)
					{
						flag = false;
						break;
					}
				}
			}
			if (!flag)
			{
				GameObject val = TungSahurFixPlugin.TryResolvePrefabFromBundle(enemyContent);
				if ((Object)(object)val != (Object)null)
				{
					int num = TungSahurFixPlugin.SpawnCountForSetupName(text);
					List<GameObject> list2 = new List<GameObject>(num);
					for (int i = 0; i < num; i++)
					{
						list2.Add(val);
					}
					FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyContent), "_spawnObjects");
					if (fieldInfo == null)
					{
						PluginInfo.Log.LogError((object)"TungSahurFix: _spawnObjects field not found");
						return;
					}
					fieldInfo.SetValue(enemyContent, list2);
					TungSahurFixPlugin.MarkFixed(enemyContent, val, text, num);
				}
				else
				{
					TungSahurFixPlugin.RegisterKnown(enemyContent);
				}
			}
			else
			{
				TungSahurFixPlugin.MarkFixed(enemyContent, list[0], text, list.Count);
			}
		}
		catch (Exception ex)
		{
			PluginInfo.Log.LogError((object)("TungSahurFix: prefix error: " + ex));
		}
	}
}