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 KnownContents = new List(); private static readonly HashSet Fixed = new HashSet(); 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 list; lock (Sync) { list = new List(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(); 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 list; GameObject foundPrefab; lock (Sync) { list = new List(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 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 list3 = new List(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(); 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(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)(object)val).IsValid()) { continue; } num++; try { GameObject prefab = ((PrefabRef)(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(); if (array == null || array.Length == 0) { array = bundle.LoadAllAssets(); } 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 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 list2 = new List(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)); } } }