Decompiled source of internal test ignore v1.0.0

Matsu.MergedEnemyMod.dll

Decompiled 3 months 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 HarmonyLib;
using Microsoft.CodeAnalysis;
using REPOLib.Modules;
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("Matsu.MergedEnemyMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Matsu.MergedEnemyMod")]
[assembly: AssemblyTitle("Matsu.MergedEnemyMod")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 Matsu.MergedEnemyMod
{
	internal static class FusionShared
	{
		private static ParticleScriptExplosion? _sharedExplosion;

		private static GameObject? _explosionPrefab;

		public static void HurtPlayersInRadius(Vector3 center, float radius, int maxDamage)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (maxDamage <= 0)
			{
				return;
			}
			PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
			foreach (PlayerAvatar val in array)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)val.playerHealth == (Object)null)
				{
					continue;
				}
				float num = Vector3.Distance(center, ((Component)val).transform.position);
				if (!(num > radius))
				{
					int num2 = Mathf.RoundToInt(Mathf.Lerp((float)maxDamage, (float)maxDamage * 0.3f, num / radius));
					if (num2 > 0)
					{
						val.playerHealth.HurtOther(num2, center, false, -1, false);
					}
				}
			}
		}

		public static bool NearestPlayerWithin(Vector3 from, float range)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
			foreach (PlayerAvatar val in array)
			{
				if (!((Object)(object)val == (Object)null) && Vector3.Distance(from, ((Component)val).transform.position) <= range)
				{
					return true;
				}
			}
			return false;
		}

		public static void SpawnExplosionVfx(Vector3 pos, float size)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_sharedExplosion == (Object)null)
			{
				_sharedExplosion = Object.FindObjectOfType<ParticleScriptExplosion>(true);
			}
			if ((Object)(object)_sharedExplosion != (Object)null)
			{
				try
				{
					_sharedExplosion.Spawn(pos, size, 0, 0, 3f, true, false, 1f);
					return;
				}
				catch
				{
				}
			}
			if ((Object)(object)_explosionPrefab == (Object)null)
			{
				_explosionPrefab = Resources.Load<GameObject>("Effects/Part Prefab Explosion");
			}
			if ((Object)(object)_explosionPrefab != (Object)null)
			{
				ParticlePrefabExplosion component = Object.Instantiate<GameObject>(_explosionPrefab, pos, Quaternion.identity).GetComponent<ParticlePrefabExplosion>();
				if ((Object)(object)component != (Object)null)
				{
					component.onlyParticleEffect = true;
				}
			}
		}

		public static void AddAuraLight(Transform parent, float range = 6f, float intensity = 2.5f)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("FusionAura");
			val.transform.SetParent(parent, false);
			val.transform.localPosition = Vector3.up * 0.5f;
			Light obj = val.AddComponent<Light>();
			obj.type = (LightType)2;
			obj.color = new Color(1f, 0.4f, 0f);
			obj.range = range;
			obj.intensity = intensity;
		}
	}
	public class BoomdayBoyFusion : MonoBehaviour
	{
		private EnemyBirthdayBoy _birthday;

		private EnemyHealth? _health;

		private bool _fuseLit;

		private bool _deathDetonated;

		private float _fuseTimer;

		private float _explodeCooldown;

		private float _retintTimer;

		private float _proximityTimer;

		private void Awake()
		{
			_birthday = ((Component)this).GetComponent<EnemyBirthdayBoy>();
			_health = ((Component)this).GetComponentInParent<EnemyHealth>() ?? ((Component)this).GetComponentInChildren<EnemyHealth>();
			if ((Object)(object)_health != (Object)null)
			{
				_health.health = Mathf.Max(1, Mathf.RoundToInt((float)_health.health * Plugin.BoomdayHealthMultiplier.Value));
			}
			FusionShared.AddAuraLight(((Component)this).transform);
		}

		private void Update()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			if (!SemiFunc.IsMasterClientOrSingleplayer() || _deathDetonated || (Object)(object)_birthday == (Object)null)
			{
				return;
			}
			_retintTimer -= Time.deltaTime;
			if (_retintTimer <= 0f)
			{
				_retintTimer = 1f;
				TintBalloonsOrange();
			}
			if (_explodeCooldown > 0f)
			{
				_explodeCooldown -= Time.deltaTime;
			}
			_proximityTimer -= Time.deltaTime;
			if (!_fuseLit && _explodeCooldown <= 0f && _proximityTimer <= 0f)
			{
				_proximityTimer = 0.25f;
				if (FusionShared.NearestPlayerWithin(((Component)this).transform.position, Plugin.BoomdayFuseRange.Value))
				{
					_fuseLit = true;
					_fuseTimer = Plugin.BoomdayFuseTime.Value;
					Plugin.Log.LogInfo((object)"[BoomdayBoy] Fuse lit!");
				}
			}
			if (_fuseLit)
			{
				_fuseTimer -= Time.deltaTime;
				if (_fuseTimer <= 0f)
				{
					ProximityExplode();
				}
			}
		}

		private void ProximityExplode()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)this).transform.position;
			FusionShared.SpawnExplosionVfx(position, 1.3f);
			FusionShared.HurtPlayersInRadius(position, 6f, Plugin.BoomdayCoreDamage.Value);
			Plugin.Log.LogInfo((object)"[BoomdayBoy] BOOM (recurring)!");
			_fuseLit = false;
			_explodeCooldown = Plugin.BoomdayExplodeCooldown.Value;
		}

		public void Detonate()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (!_deathDetonated)
			{
				_deathDetonated = true;
				Vector3 position = ((Component)this).transform.position;
				FusionShared.SpawnExplosionVfx(position, 1.6f);
				FusionShared.HurtPlayersInRadius(position, 7f, Plugin.BoomdayCoreDamage.Value);
				ChainExplodeBalloons();
				Plugin.Log.LogInfo((object)"[BoomdayBoy] Death blast + balloon chain reaction!");
			}
		}

		private void ChainExplodeBalloons()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_birthday == (Object)null)
			{
				return;
			}
			foreach (GameObject item in new List<GameObject>(_birthday.balloons.Values))
			{
				if (!((Object)(object)item == (Object)null))
				{
					Vector3 position = item.transform.position;
					FusionShared.SpawnExplosionVfx(position, 0.7f);
					FusionShared.HurtPlayersInRadius(position, 3f, Plugin.BoomdayBalloonDamage.Value);
					if ((Object)(object)_birthday.balloonPopEffect != (Object)null)
					{
						Object.Instantiate<GameObject>(_birthday.balloonPopEffect, position, Quaternion.identity);
					}
					Object.Destroy((Object)(object)item);
				}
			}
			_birthday.balloons.Clear();
		}

		private void TintBalloonsOrange()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_birthday == (Object)null)
			{
				return;
			}
			foreach (GameObject value in _birthday.balloons.Values)
			{
				if ((Object)(object)value == (Object)null)
				{
					continue;
				}
				Renderer[] componentsInChildren = value.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null))
					{
						MaterialPropertyBlock val2 = new MaterialPropertyBlock();
						val.GetPropertyBlock(val2);
						val2.SetColor("_Color", new Color(1f, 0.45f, 0f));
						val2.SetColor("_EmissionColor", new Color(1f, 0.35f, 0f) * 1.5f);
						val.SetPropertyBlock(val2);
					}
				}
			}
		}
	}
	public class JuggernautFusion : MonoBehaviour
	{
		private EnemyFloater _floater;

		private EnemyHealth? _health;

		private Enemy? _enemy;

		private float _stompCooldown;

		private void Awake()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			_floater = ((Component)this).GetComponent<EnemyFloater>();
			_health = ((Component)this).GetComponentInParent<EnemyHealth>() ?? ((Component)this).GetComponentInChildren<EnemyHealth>();
			_enemy = ((Component)this).GetComponentInParent<Enemy>() ?? ((Component)this).GetComponentInChildren<Enemy>();
			if ((Object)(object)_health != (Object)null)
			{
				_health.health = Mathf.Max(1, Mathf.RoundToInt((float)_health.health * Plugin.JuggernautHealthMultiplier.Value));
			}
			if ((Object)(object)_enemy != (Object)null)
			{
				_enemy.Type = (EnemyType)4;
			}
			FusionShared.AddAuraLight(((Component)this).transform, 8f, 3f);
			_stompCooldown = Plugin.JuggernautStompInterval.Value;
		}

		private void Update()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				_stompCooldown -= Time.deltaTime;
				if (!(_stompCooldown > 0f) && FusionShared.NearestPlayerWithin(((Component)this).transform.position, Plugin.JuggernautStompRange.Value))
				{
					_stompCooldown = Plugin.JuggernautStompInterval.Value;
					Stomp();
				}
			}
		}

		private void Stomp()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)this).transform.position;
			FusionShared.SpawnExplosionVfx(position, 1.6f);
			FusionShared.HurtPlayersInRadius(position, Plugin.JuggernautStompRadius.Value, Plugin.JuggernautStompDamage.Value);
			Plugin.Log.LogInfo((object)"[Juggernaut] STOMP shockwave!");
		}
	}
	[BepInPlugin("com.matsu.mergedenemymod", "Matsu Merged Enemy Mod", "0.3.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.matsu.mergedenemymod";

		public const string Name = "Matsu Merged Enemy Mod";

		public const string Version = "0.3.0";

		public static ConfigEntry<float> MutationChancePerLevel;

		public static ConfigEntry<float> MutationMaxChance;

		public static ConfigEntry<float> BoomdayHealthMultiplier;

		public static ConfigEntry<float> BoomdayFuseRange;

		public static ConfigEntry<float> BoomdayFuseTime;

		public static ConfigEntry<float> BoomdayExplodeCooldown;

		public static ConfigEntry<int> BoomdayCoreDamage;

		public static ConfigEntry<int> BoomdayBalloonDamage;

		public static ConfigEntry<float> JuggernautHealthMultiplier;

		public static ConfigEntry<float> JuggernautStompInterval;

		public static ConfigEntry<float> JuggernautStompRange;

		public static ConfigEntry<float> JuggernautStompRadius;

		public static ConfigEntry<int> JuggernautStompDamage;

		public static ConfigEntry<KeyCode> DebugSpawnBoomdayKey;

		public static ConfigEntry<KeyCode> DebugSpawnJuggernautKey;

		internal static float ForceFuseUntil;

		private Harmony _harmony;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			MutationChancePerLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Mutation", "ChancePerLevel", 0.01f, "Fusion (mutation) chance added per level. 0.01 = +1% per level (level 1 = 1%, level 10 = 10%).");
			MutationMaxChance = ((BaseUnityPlugin)this).Config.Bind<float>("Mutation", "MaxChance", 1f, "Upper cap (0..1) for the level-scaled fusion chance.");
			BoomdayHealthMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("BoomdayBoy", "HealthMultiplier", 1.5f, "Health multiplier.");
			BoomdayFuseRange = ((BaseUnityPlugin)this).Config.Bind<float>("BoomdayBoy", "FuseRange", 4f, "Player distance that lights the fuse.");
			BoomdayFuseTime = ((BaseUnityPlugin)this).Config.Bind<float>("BoomdayBoy", "FuseTime", 2.5f, "Seconds from fuse lit to an explosion.");
			BoomdayExplodeCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("BoomdayBoy", "ExplodeCooldown", 4f, "Seconds before a Boomday Boy can explode again. It survives its own blasts (recurring threat).");
			BoomdayCoreDamage = ((BaseUnityPlugin)this).Config.Bind<int>("BoomdayBoy", "CoreDamage", 60, "Explosion max damage.");
			BoomdayBalloonDamage = ((BaseUnityPlugin)this).Config.Bind<int>("BoomdayBoy", "BalloonDamage", 25, "Per-balloon explosion max damage (death chain).");
			JuggernautHealthMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Juggernaut", "HealthMultiplier", 4f, "Health multiplier (tanky).");
			JuggernautStompInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Juggernaut", "StompInterval", 5f, "Seconds between stomps.");
			JuggernautStompRange = ((BaseUnityPlugin)this).Config.Bind<float>("Juggernaut", "StompRange", 7f, "It only stomps when a player is within this range.");
			JuggernautStompRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Juggernaut", "StompRadius", 6f, "Stomp shockwave damage radius.");
			JuggernautStompDamage = ((BaseUnityPlugin)this).Config.Bind<int>("Juggernaut", "StompDamage", 40, "Stomp shockwave max damage.");
			DebugSpawnBoomdayKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Debug", "SpawnBoomdayKey", (KeyCode)290, "Press (host only) to spawn a Boomday Boy in front of your camera.");
			DebugSpawnJuggernautKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Debug", "SpawnJuggernautKey", (KeyCode)291, "Press (host only) to spawn a Juggernaut in front of your camera.");
			_harmony = new Harmony("com.matsu.mergedenemymod");
			_harmony.PatchAll();
			Log.LogInfo((object)string.Format("{0} {1} loaded. Mutation chance = level x {2:P0}. Debug: Boomday Boy (F9), Juggernaut (F10).", "Matsu Merged Enemy Mod", "0.3.0", MutationChancePerLevel.Value));
		}

		private void Update()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(DebugSpawnBoomdayKey.Value))
			{
				DebugSpawn("Birthday", typeof(EnemyBirthdayBoy), "Boomday Boy");
			}
			if (Input.GetKeyDown(DebugSpawnJuggernautKey.Value))
			{
				DebugSpawn("Floater", typeof(EnemyFloater), "Juggernaut");
			}
		}

		private static void DebugSpawn(string nameContains, Type componentType, string label)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!SemiFunc.IsMasterClientOrSingleplayer())
				{
					Log.LogWarning((object)"[Debug] Spawn is host-only.");
					return;
				}
				Camera main = Camera.main;
				if ((Object)(object)main == (Object)null)
				{
					Log.LogWarning((object)"[Debug] No main camera; are you in a level?");
					return;
				}
				Vector3 val = ((Component)main).transform.position + ((Component)main).transform.forward * 3f;
				EnemySetup val2 = FindEnemySetup(nameContains, componentType);
				if ((Object)(object)val2 == (Object)null)
				{
					Log.LogWarning((object)("[Debug] " + label + " base enemy setup not found."));
					return;
				}
				ForceFuseUntil = Time.time + 5f;
				Enemies.SpawnEnemy(val2, val, Quaternion.identity, false);
				Log.LogInfo((object)$"[Debug] Spawned a {label} at {val}.");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[Debug] spawn error: {arg}");
			}
		}

		private static EnemySetup? FindEnemySetup(string nameContains, Type componentType)
		{
			foreach (EnemySetup allEnemy in Enemies.AllEnemies)
			{
				if ((Object)(object)allEnemy != (Object)null && ((Object)allEnemy).name.IndexOf(nameContains, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return allEnemy;
				}
			}
			foreach (EnemySetup allEnemy2 in Enemies.AllEnemies)
			{
				if (allEnemy2?.spawnObjects == null)
				{
					continue;
				}
				foreach (PrefabRef spawnObject in allEnemy2.spawnObjects)
				{
					GameObject val = ((PrefabRef<GameObject>)(object)spawnObject)?.Prefab;
					if ((Object)(object)val != (Object)null && (Object)(object)val.GetComponentInChildren(componentType, true) != (Object)null)
					{
						return allEnemy2;
					}
				}
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(EnemyParent), "Spawn")]
	internal static class EnemyParent_Spawn_Patch
	{
		private static void Postfix(EnemyParent __instance)
		{
			try
			{
				if (!SemiFunc.IsMasterClientOrSingleplayer() || (Object)(object)__instance == (Object)null)
				{
					return;
				}
				float num = ((Time.time <= Plugin.ForceFuseUntil) ? 1f : GetMutationChance());
				EnemyBirthdayBoy componentInChildren = ((Component)__instance).GetComponentInChildren<EnemyBirthdayBoy>();
				if ((Object)(object)componentInChildren != (Object)null && (Object)(object)((Component)componentInChildren).GetComponent<BoomdayBoyFusion>() == (Object)null)
				{
					if (Random.value <= num)
					{
						((Component)componentInChildren).gameObject.AddComponent<BoomdayBoyFusion>();
						Plugin.Log.LogInfo((object)"[BoomdayBoy] A Birthday Boy became a Boomday Boy.");
					}
					return;
				}
				EnemyFloater componentInChildren2 = ((Component)__instance).GetComponentInChildren<EnemyFloater>();
				if ((Object)(object)componentInChildren2 != (Object)null && (Object)(object)((Component)componentInChildren2).GetComponent<JuggernautFusion>() == (Object)null && Random.value <= num)
				{
					((Component)componentInChildren2).gameObject.AddComponent<JuggernautFusion>();
					Plugin.Log.LogInfo((object)"[Juggernaut] A Floater became a Juggernaut.");
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[Fusion] spawn patch error: {arg}");
			}
		}

		private static float GetMutationChance()
		{
			RunManager instance = RunManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0f;
			}
			return Mathf.Clamp((float)(instance.levelsCompleted + 1) * Plugin.MutationChancePerLevel.Value, 0f, Plugin.MutationMaxChance.Value);
		}
	}
	[HarmonyPatch(typeof(EnemyBirthdayBoy), "OnDeath")]
	internal static class EnemyBirthdayBoy_OnDeath_Patch
	{
		private static void Postfix(EnemyBirthdayBoy __instance)
		{
			try
			{
				if (SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)__instance == (Object)null))
				{
					BoomdayBoyFusion component = ((Component)__instance).GetComponent<BoomdayBoyFusion>();
					if ((Object)(object)component != (Object)null)
					{
						component.Detonate();
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[BoomdayBoy] OnDeath patch error: {arg}");
			}
		}
	}
}

Matsu.REPOCompatPatch.dll

Decompiled 3 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
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: AssemblyCompany("Matsu.REPOCompatPatch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.4.0")]
[assembly: AssemblyInformationalVersion("1.3.4")]
[assembly: AssemblyProduct("Matsu.REPOCompatPatch")]
[assembly: AssemblyTitle("Matsu.REPOCompatPatch")]
[assembly: AssemblyVersion("1.3.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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;
		}
	}
}
public static class GhostMeshVisualizerBridge
{
	public static bool Show => false;
}
public static class PathsVisualizerBridge
{
	public static bool Show => false;
}
public static class ValuablesVisualizerBridge
{
	public static bool Show => false;
}
public static class InvisibleColliderVisualizerBridge
{
	public static bool Show => false;
}
namespace Matsu.REPOCompatPatch
{
	internal sealed class DeathSyncBehaviour : MonoBehaviour, IInRoomCallbacks, IOnEventCallback
	{
		internal const byte DeathSyncEventCode = 77;

		private static MethodInfo? isMasterOrSpMethod;

		private static bool isMasterResolved;

		private static FieldInfo? statsInstanceField;

		private static FieldInfo? saveFileCurrentField;

		private static bool statsResolved;

		private string? loadedSaveFile;

		private float pollTimer;

		private static string SaveDir => Path.Combine(Application.persistentDataPath, "MatsuDeathCounter");

		private void OnEnable()
		{
			try
			{
				PhotonNetwork.AddCallbackTarget((object)this);
			}
			catch (Exception ex)
			{
				Plugin.TrackerErr("[DeathCounter] AddCallbackTarget failed: " + ex.Message);
			}
		}

		private void OnDisable()
		{
			try
			{
				PhotonNetwork.RemoveCallbackTarget((object)this);
			}
			catch
			{
			}
		}

		private void Update()
		{
			if (!IsHost())
			{
				return;
			}
			pollTimer += Time.deltaTime;
			if (pollTimer < 1f)
			{
				return;
			}
			pollTimer = 0f;
			string saveFileCurrent = GetSaveFileCurrent();
			if (!(saveFileCurrent == loadedSaveFile))
			{
				loadedSaveFile = saveFileCurrent;
				Plugin.DeathCountsBySteamId.Clear();
				Plugin.PlayerNamesBySteamId.Clear();
				if (!string.IsNullOrEmpty(saveFileCurrent))
				{
					LoadFromDisk(saveFileCurrent);
					BroadcastDeaths();
				}
			}
		}

		public void OnEvent(EventData photonEvent)
		{
			if (photonEvent.Code != 77 || IsHost())
			{
				return;
			}
			try
			{
				if (!(photonEvent.CustomData is object[] array) || array.Length < 3)
				{
					return;
				}
				string[] array2 = array[0] as string[];
				int[] array3 = array[1] as int[];
				string[] array4 = array[2] as string[];
				if (array2 == null || array3 == null || array4 == null)
				{
					return;
				}
				Plugin.DeathCountsBySteamId.Clear();
				Plugin.PlayerNamesBySteamId.Clear();
				for (int i = 0; i < array2.Length && i < array3.Length; i++)
				{
					if (!string.IsNullOrEmpty(array2[i]))
					{
						Plugin.DeathCountsBySteamId[array2[i]] = array3[i];
						if (i < array4.Length && !string.IsNullOrEmpty(array4[i]))
						{
							Plugin.PlayerNamesBySteamId[array2[i]] = array4[i];
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.TrackerErr("[DeathCounter] apply snapshot failed: " + ex.Message);
			}
		}

		public void OnPlayerEnteredRoom(Player newPlayer)
		{
			if (IsHost())
			{
				BroadcastDeaths();
			}
		}

		public void OnPlayerLeftRoom(Player otherPlayer)
		{
		}

		public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
		{
		}

		public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
		{
		}

		public void OnMasterClientSwitched(Player newMasterClient)
		{
		}

		internal static void OnHostDeath(string steamId, string name)
		{
			string text = ((!string.IsNullOrEmpty(steamId)) ? steamId : name);
			if (!string.IsNullOrEmpty(text))
			{
				Plugin.DeathCountsBySteamId.TryGetValue(text, out var value);
				Plugin.DeathCountsBySteamId[text] = value + 1;
				if (!string.IsNullOrEmpty(name))
				{
					Plugin.PlayerNamesBySteamId[text] = name;
				}
				Plugin.TrackerLog($"[DeathCounter] {(string.IsNullOrEmpty(name) ? text : name)} died. Total: {Plugin.DeathCountsBySteamId[text]}");
				SaveToDisk();
				BroadcastDeaths();
			}
		}

		private static void BroadcastDeaths()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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
			try
			{
				if (PhotonNetwork.InRoom && !PhotonNetwork.OfflineMode)
				{
					PhotonNetwork.RaiseEvent((byte)77, (object)Pack(), new RaiseEventOptions
					{
						Receivers = (ReceiverGroup)0
					}, SendOptions.SendReliable);
				}
			}
			catch (Exception ex)
			{
				Plugin.TrackerErr("[DeathCounter] broadcast failed: " + ex.Message);
			}
		}

		private static object[] Pack()
		{
			int count = Plugin.DeathCountsBySteamId.Count;
			string[] array = new string[count];
			int[] array2 = new int[count];
			string[] array3 = new string[count];
			int num = 0;
			foreach (KeyValuePair<string, int> item in Plugin.DeathCountsBySteamId)
			{
				array[num] = item.Key;
				array2[num] = item.Value;
				array3[num] = (Plugin.PlayerNamesBySteamId.TryGetValue(item.Key, out string value) ? value : "");
				num++;
			}
			return new object[3] { array, array2, array3 };
		}

		private static string SavePathFor(string saveFile)
		{
			return Path.Combine(SaveDir, Sanitize(saveFile) + ".tsv");
		}

		private static string Sanitize(string s)
		{
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				s = s.Replace(oldChar, '_');
			}
			return s;
		}

		internal static void SaveToDisk()
		{
			string saveFileCurrent = GetSaveFileCurrent();
			if (string.IsNullOrEmpty(saveFileCurrent))
			{
				return;
			}
			try
			{
				Directory.CreateDirectory(SaveDir);
				StringBuilder stringBuilder = new StringBuilder();
				foreach (KeyValuePair<string, int> item in Plugin.DeathCountsBySteamId)
				{
					string text = (Plugin.PlayerNamesBySteamId.TryGetValue(item.Key, out string value) ? value : "");
					text = text.Replace('\t', ' ').Replace('\n', ' ').Replace('\r', ' ');
					stringBuilder.Append(item.Key).Append('\t').Append(item.Value)
						.Append('\t')
						.Append(text)
						.Append('\n');
				}
				File.WriteAllText(SavePathFor(saveFileCurrent), stringBuilder.ToString());
			}
			catch (Exception ex)
			{
				Plugin.TrackerErr("[DeathCounter] save failed: " + ex.Message);
			}
		}

		private static void LoadFromDisk(string saveFile)
		{
			try
			{
				string path = SavePathFor(saveFile);
				if (!File.Exists(path))
				{
					return;
				}
				string[] array = File.ReadAllLines(path);
				foreach (string text in array)
				{
					if (string.IsNullOrWhiteSpace(text))
					{
						continue;
					}
					string[] array2 = text.Split(new char[1] { '\t' });
					if (array2.Length < 2)
					{
						continue;
					}
					string text2 = array2[0];
					if (!string.IsNullOrEmpty(text2) && int.TryParse(array2[1], out var result))
					{
						Plugin.DeathCountsBySteamId[text2] = result;
						if (array2.Length >= 3 && !string.IsNullOrEmpty(array2[2]))
						{
							Plugin.PlayerNamesBySteamId[text2] = array2[2];
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.TrackerErr("[DeathCounter] load failed: " + ex.Message);
			}
		}

		internal static bool IsHost()
		{
			if (!isMasterResolved)
			{
				Type type = AccessTools.TypeByName("SemiFunc");
				isMasterOrSpMethod = ((type == null) ? null : AccessTools.Method(type, "IsMasterClientOrSingleplayer", (Type[])null, (Type[])null));
				isMasterResolved = true;
			}
			if (isMasterOrSpMethod != null)
			{
				try
				{
					return (bool)isMasterOrSpMethod.Invoke(null, null);
				}
				catch
				{
				}
			}
			try
			{
				return PhotonNetwork.OfflineMode || !PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient;
			}
			catch
			{
				return true;
			}
		}

		private static string GetSaveFileCurrent()
		{
			if (!statsResolved)
			{
				Type type = AccessTools.TypeByName("StatsManager");
				statsInstanceField = ((type == null) ? null : AccessTools.Field(type, "instance"));
				saveFileCurrentField = ((type == null) ? null : AccessTools.Field(type, "saveFileCurrent"));
				statsResolved = true;
			}
			try
			{
				object obj = statsInstanceField?.GetValue(null);
				if (obj == null)
				{
					return "";
				}
				return (saveFileCurrentField?.GetValue(obj) as string) ?? "";
			}
			catch
			{
				return "";
			}
		}
	}
	[BepInPlugin("matsu.repo.compat_patch", "Matsu REPO Compat Patch", "1.3.4")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "matsu.repo.compat_patch";

		public const string PluginName = "Matsu REPO Compat Patch";

		public const string PluginVersion = "1.3.4";

		private static ManualLogSource? Log;

		private static ConfigEntry<bool>? VerboseLogging;

		private static readonly HashSet<string> LoggedOnce = new HashSet<string>();

		public static Dictionary<string, int> DeathCountsBySteamId = new Dictionary<string, int>();

		public static Dictionary<string, string> PlayerNamesBySteamId = new Dictionary<string, string>();

		private static FieldInfo? targetComponentField;

		private static FieldInfo? insightGeneratorField;

		private static FieldInfo? insightValueTextField;

		private static bool runManagerReflectionResolved;

		private static FieldInfo? runManagerInstanceField;

		private static FieldInfo? runManagerLevelCurrentField;

		private static FieldInfo? runManagerLevelLobbyField;

		private Harmony harmony;

		private ConfigEntry<bool> patchShopItemNull;

		private ConfigEntry<bool> patchMapNullDoorPrefab;

		private ConfigEntry<bool> patchEnemySetupNullPrefab;

		private ConfigEntry<bool> patchSoundApiNull;

		private ConfigEntry<bool> patchImperiumEnemyStatusNull;

		private ConfigEntry<bool> patchImperiumInsightsNull;

		private ConfigEntry<bool> verboseLogging;

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			harmony = new Harmony("matsu.repo.compat_patch");
			patchShopItemNull = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "PatchItemAttributesShopNull", true, "Suppresses ItemAttributes.ShopInTruckLogic null-reference spam caused by modded item metadata that the current game does not fully understand.");
			patchMapNullDoorPrefab = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "PatchMapNullDoorPrefab", true, "Skips map door marker creation when a modded door has no DirtFinder map prefab, preventing repeated null Instantiate errors.");
			patchEnemySetupNullPrefab = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "PatchEnemySetupNullPrefab", true, "Automatically heals EnemySetup.spawnObjects if it is null, empty or contains broken prefab refs, allowing Jester and other older custom enemies to spawn.");
			patchSoundApiNull = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "PatchSoundApiNull", true, "Suppresses loaforcsSoundAPI NullReferenceException loops inside ResetAudioInfoFetched when playing looped sounds.");
			patchImperiumEnemyStatusNull = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "PatchImperiumEnemyStatusNull", true, "Suppresses Imperium.Visualizers.Objects.EnemyStatus NullReferenceException loops inside Update when tracking custom or uninitialized enemies.");
			patchImperiumInsightsNull = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "PatchImperiumInsightsNull", true, "Suppresses Imperium.Visualizers.ObjectInsights NullReferenceException loops inside RegisterDefaultInsights when tracking custom or uninitialized enemies.");
			verboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "VerboseLogging", false, "Log every skipped compatibility edge instead of one sample per issue.");
			VerboseLogging = verboseLogging;
			((MonoBehaviour)this).StartCoroutine(InstallPatchesWhenGameTypesAreReady());
			((Component)this).gameObject.AddComponent<DeathSyncBehaviour>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Matsu REPO Compat Patch 1.3.4 loaded. Host-safe: no networked prefabs or custom content (the death counter syncs via lightweight host events only).");
		}

		private IEnumerator InstallPatchesWhenGameTypesAreReady()
		{
			bool shopPatched = !patchShopItemNull.Value;
			bool mapPatched = !patchMapNullDoorPrefab.Value;
			bool enemySetupPatched = !patchEnemySetupNullPrefab.Value;
			bool soundApiPatched = !patchSoundApiNull.Value;
			bool imperiumPatched = !patchImperiumEnemyStatusNull.Value;
			bool imperiumInsightsPatched = !patchImperiumInsightsNull.Value;
			bool deathCounterPatched = false;
			for (int attempt = 0; attempt < 20; attempt++)
			{
				if (!shopPatched)
				{
					shopPatched = TryPatch("ItemAttributes", "ShopInTruckLogic", null, null, "ItemAttributesShopInTruckLogicFinalizer");
				}
				if (!mapPatched)
				{
					mapPatched = TryPatch("Map", "AddDoor", null, "MapAddDoorPrefix");
				}
				if (!enemySetupPatched)
				{
					bool flag = TryPatch("EnemyDirector", "Awake", null, null, null, "EnemyDirectorAwakePostfix");
					Type type = AccessTools.TypeByName("REPOLib.Objects.Sdk.EnemyContent");
					bool flag2 = false;
					if (type != null)
					{
						flag2 = TryPatch("REPOLib.Modules.Enemies", "RegisterEnemy", new Type[1] { type }, "RegisterEnemyPrefix");
					}
					enemySetupPatched = flag && flag2;
				}
				if (!soundApiPatched)
				{
					soundApiPatched = !TypeExists("loaforcsSoundAPI.REPO.Patches.SoundPatch") || TryPatch("loaforcsSoundAPI.REPO.Patches.SoundPatch", "ResetAudioInfoFetched", null, null, "SoundApiResetAudioInfoFetchedFinalizer");
				}
				if (!imperiumPatched)
				{
					if (!TypeExists("Imperium.Core.EventLogging.EntityEventLogger"))
					{
						imperiumPatched = true;
					}
					else
					{
						bool flag3 = TryPatch("Imperium.Visualizers.Objects.EnemyStatus", "Update", null, null, "ImperiumEnemyStatusUpdateFinalizer");
						bool flag4 = TryPatch("Imperium.Core.EventLogging.EntityEventLogger", "LogEntityEvent", null, "EntityEventLoggerPrefix");
						bool flag5 = TryPatch("Imperium.Core.EventLogging.EntityEventLogger", "Teleport", null, "EntityEventLoggerPrefix");
						bool flag6 = TryPatch("Imperium.Core.EventLogging.EntityEventLogger", "Spawn", null, "EntityEventLoggerPrefix");
						bool flag7 = TryPatch("Imperium.Core.EventLogging.EntityEventLogger", "Despawn", null, "EntityEventLoggerPrefix");
						imperiumPatched = flag3 && flag4 && flag5 && flag6 && flag7;
					}
				}
				if (!imperiumInsightsPatched)
				{
					if (!TypeExists("Imperium.Visualizers.Objects.ObjectInsightEntry"))
					{
						imperiumInsightsPatched = true;
					}
					else
					{
						bool flag8 = TryPatch("Imperium.Visualizers.Objects.ObjectInsightEntry", "Init", null, "ObjectInsightEntryInitPrefix");
						bool flag9 = TryPatch("Imperium.Visualizers.Objects.ObjectInsightEntry", "Update", null, "ObjectInsightEntryUpdatePrefix");
						imperiumInsightsPatched = flag8 && flag9;
					}
				}
				if (!deathCounterPatched)
				{
					deathCounterPatched = TryPatch("PlayerAvatar", "PlayerDeathRPC", null, "PlayerDeathRpcPrefix");
				}
				if (shopPatched && mapPatched && enemySetupPatched && soundApiPatched && imperiumPatched && imperiumInsightsPatched && deathCounterPatched)
				{
					yield break;
				}
				yield return (object)new WaitForSeconds(1f);
			}
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Some compatibility targets were not patched. The game version may have renamed the target methods.");
		}

		private static bool TypeExists(string typeName)
		{
			return AccessTools.TypeByName(typeName) != null;
		}

		private bool TryPatch(string typeName, string methodName, Type[]? parameterTypes = null, string? prefix = null, string? finalizer = null, string? postfix = null, string? transpiler = null)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			Type type = AccessTools.TypeByName(typeName);
			MethodInfo methodInfo = null;
			if (type != null)
			{
				methodInfo = ((parameterTypes != null) ? AccessTools.Method(type, methodName, parameterTypes, (Type[])null) : AccessTools.Method(type, methodName, (Type[])null, (Type[])null));
			}
			if (methodInfo == null)
			{
				return false;
			}
			HarmonyMethod val = ((prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Plugin), prefix, (Type[])null, (Type[])null)));
			HarmonyMethod val2 = ((finalizer == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Plugin), finalizer, (Type[])null, (Type[])null)));
			HarmonyMethod val3 = ((postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Plugin), postfix, (Type[])null, (Type[])null)));
			HarmonyMethod val4 = ((transpiler == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(typeof(Plugin), transpiler, (Type[])null, (Type[])null)));
			try
			{
				harmony.Patch((MethodBase)methodInfo, val, val3, val4, val2, (HarmonyMethod)null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched " + typeName + "." + methodName + " for REPO mod compatibility."));
				return true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not patch " + typeName + "." + methodName + ": " + ex.GetType().Name + ": " + ex.Message));
				return false;
			}
		}

		private void OnGUI()
		{
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Expected O, but got Unknown
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			if (!runManagerReflectionResolved)
			{
				Type type = AccessTools.TypeByName("RunManager");
				if (type != null)
				{
					runManagerInstanceField = AccessTools.Field(type, "instance");
					runManagerLevelCurrentField = AccessTools.Field(type, "levelCurrent");
					runManagerLevelLobbyField = AccessTools.Field(type, "levelLobby");
				}
				runManagerReflectionResolved = true;
			}
			if (runManagerInstanceField == null || runManagerLevelCurrentField == null || runManagerLevelLobbyField == null)
			{
				return;
			}
			object value = runManagerInstanceField.GetValue(null);
			if (value == null)
			{
				return;
			}
			object value2 = runManagerLevelCurrentField.GetValue(value);
			object value3 = runManagerLevelLobbyField.GetValue(value);
			if (value2 == null || value3 == null || value2 != value3)
			{
				return;
			}
			GUILayout.BeginArea(new Rect((float)Screen.width - 400f - 20f, 20f, 400f, 600f));
			GUI.color = Color.white;
			GUIStyle val = new GUIStyle(GUI.skin.label)
			{
				fontSize = 24,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)2
			};
			val.normal.textColor = Color.red;
			GUILayout.Label("--- Player Deaths ---", val, Array.Empty<GUILayoutOption>());
			GUIStyle val2 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 20,
				fontStyle = (FontStyle)0,
				alignment = (TextAnchor)2
			};
			val2.normal.textColor = Color.white;
			if (DeathCountsBySteamId.Count == 0)
			{
				GUILayout.Label("No deaths recorded yet.", val2, Array.Empty<GUILayoutOption>());
			}
			else
			{
				foreach (KeyValuePair<string, int> item in DeathCountsBySteamId)
				{
					string value4;
					string arg = ((PlayerNamesBySteamId.TryGetValue(item.Key, out value4) && !string.IsNullOrEmpty(value4)) ? value4 : item.Key);
					GUILayout.Label($"{arg}: {item.Value}", val2, Array.Empty<GUILayoutOption>());
				}
			}
			GUILayout.EndArea();
		}

		private static void PlayerDeathRpcPrefix(object __instance)
		{
			try
			{
				if (__instance != null && DeathSyncBehaviour.IsHost())
				{
					Type type = __instance.GetType();
					string steamId = (AccessTools.Field(type, "steamID")?.GetValue(__instance) as string) ?? "";
					string name = (AccessTools.Field(type, "playerName")?.GetValue(__instance) as string) ?? "";
					DeathSyncBehaviour.OnHostDeath(steamId, name);
				}
			}
			catch (Exception arg)
			{
				ManualLogSource? log = Log;
				if (log != null)
				{
					log.LogError((object)$"[DeathCounter] PlayerDeathRpcPrefix error: {arg}");
				}
			}
		}

		internal static void TrackerLog(string message)
		{
			ManualLogSource? log = Log;
			if (log != null)
			{
				log.LogInfo((object)message);
			}
		}

		internal static void TrackerErr(string message)
		{
			ManualLogSource? log = Log;
			if (log != null)
			{
				log.LogError((object)message);
			}
		}

		private static Exception? ItemAttributesShopInTruckLogicFinalizer(Exception? __exception)
		{
			if (__exception is NullReferenceException)
			{
				LogOnce("ItemAttributes.ShopInTruckLogic.NullReference", "Suppressed ItemAttributes.ShopInTruckLogic null reference. A modded item is missing optional shop/truck metadata.");
				return null;
			}
			return __exception;
		}

		private static Exception? SoundApiResetAudioInfoFetchedFinalizer(Exception? __exception)
		{
			if (__exception is NullReferenceException)
			{
				LogOnce("loaforcsSoundAPI.ResetAudioInfoFetched.NullReference", "Suppressed loaforcsSoundAPI SoundPatch.ResetAudioInfoFetched null reference.");
				return null;
			}
			return __exception;
		}

		private static Exception? ImperiumEnemyStatusUpdateFinalizer(Exception? __exception)
		{
			if (__exception is NullReferenceException)
			{
				LogOnce("Imperium.EnemyStatus.Update.NullReference", "Suppressed Imperium EnemyStatus.Update null reference.");
				return null;
			}
			return __exception;
		}

		private static bool ObjectInsightEntryInitPrefix(MonoBehaviour __instance, string __0, object __1, Component __2)
		{
			if ((Object)(object)__2 == (Object)null)
			{
				if (targetComponentField == null)
				{
					targetComponentField = AccessTools.Field(((object)__instance).GetType(), "targetComponent");
				}
				if (insightGeneratorField == null)
				{
					insightGeneratorField = AccessTools.Field(((object)__instance).GetType(), "insightGenerator");
				}
				if (insightValueTextField == null)
				{
					insightValueTextField = AccessTools.Field(((object)__instance).GetType(), "insightValueText");
				}
				targetComponentField?.SetValue(__instance, __2);
				insightGeneratorField?.SetValue(__instance, __1);
				object obj = insightValueTextField?.GetValue(__instance);
				if (obj != null)
				{
					AccessTools.Property(obj.GetType(), "text")?.SetValue(obj, "Destroyed");
				}
				return false;
			}
			return true;
		}

		private static bool ObjectInsightEntryUpdatePrefix(MonoBehaviour __instance)
		{
			if (targetComponentField == null)
			{
				targetComponentField = AccessTools.Field(((object)__instance).GetType(), "targetComponent");
			}
			object? obj = targetComponentField?.GetValue(__instance);
			if ((Object)((obj is Component) ? obj : null) == (Object)null)
			{
				if (insightValueTextField == null)
				{
					insightValueTextField = AccessTools.Field(((object)__instance).GetType(), "insightValueText");
				}
				object obj2 = insightValueTextField?.GetValue(__instance);
				if (obj2 != null)
				{
					AccessTools.Property(obj2.GetType(), "text")?.SetValue(obj2, "Destroyed");
				}
				return false;
			}
			return true;
		}

		private static bool MapAddDoorPrefix(object[] __args)
		{
			if (__args.Length < 2 || __args[1] is GameObject)
			{
				return true;
			}
			LogOnce("Map.AddDoor.NullDoorPrefab", "Skipped a DirtFinder map door marker because the modded door prefab reference is null.");
			return false;
		}

		private static void RegisterEnemyPrefix(object __0)
		{
			if (__0 == null)
			{
				return;
			}
			try
			{
				HealEnemyContent(__0);
			}
			catch (Exception arg)
			{
				ManualLogSource? log = Log;
				if (log != null)
				{
					log.LogError((object)$"Error in RegisterEnemyPrefix: {arg}");
				}
			}
		}

		private static void HealEnemyContent(object enemyContent)
		{
			Type type = AccessTools.TypeByName("REPOLib.Objects.Sdk.EnemyContent");
			if (type == null || !type.IsInstanceOfType(enemyContent))
			{
				return;
			}
			FieldInfo field = type.GetField("_spawnObjects", BindingFlags.Instance | BindingFlags.NonPublic);
			FieldInfo field2 = type.GetField("_setup", BindingFlags.Instance | BindingFlags.NonPublic);
			if (field == null || field2 == null)
			{
				return;
			}
			object value = field2.GetValue(enemyContent);
			IList list = field.GetValue(enemyContent) as IList;
			if (value == null || list == null || list.Count == 0)
			{
				return;
			}
			Type type2 = AccessTools.TypeByName("EnemySetup");
			if (type2 == null)
			{
				return;
			}
			FieldInfo field3 = type2.GetField("spawnObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field3 == null)
			{
				return;
			}
			IList list2 = field3.GetValue(value) as IList;
			bool flag = false;
			if (list2 == null || list2.Count == 0)
			{
				flag = true;
			}
			else
			{
				foreach (object item in list2)
				{
					if (item == null)
					{
						flag = true;
						break;
					}
					PropertyInfo property = item.GetType().GetProperty("Prefab", BindingFlags.Instance | BindingFlags.Public);
					if (property == null || property.GetValue(item) == null)
					{
						flag = true;
						break;
					}
				}
			}
			if (!flag)
			{
				return;
			}
			ManualLogSource? log = Log;
			if (log != null)
			{
				log.LogInfo((object)$"Healing spawnObjects for registered enemy content: {value}");
			}
			Type type3 = AccessTools.TypeByName("REPOLib.Modules.NetworkPrefabs");
			MethodInfo methodInfo = ((type3 == null) ? null : AccessTools.Method(type3, "RegisterNetworkPrefab", new Type[2]
			{
				typeof(string),
				typeof(GameObject)
			}, (Type[])null));
			if (methodInfo == null)
			{
				return;
			}
			Type type4 = field3.FieldType.GetGenericArguments()[0];
			IList list3 = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type4));
			foreach (object item2 in list)
			{
				if (item2 == null)
				{
					continue;
				}
				GameObject val = (GameObject)((item2 is GameObject) ? item2 : null);
				if (!((Object)(object)val == (Object)null))
				{
					string text = "Enemies/" + ((Object)val).name;
					object obj = null;
					MethodInfo methodInfo2 = ((type3 == null) ? null : AccessTools.Method(type3, "HasNetworkPrefab", new Type[1] { typeof(string) }, (Type[])null));
					MethodInfo methodInfo3 = ((type3 == null) ? null : AccessTools.Method(type3, "GetNetworkPrefabRef", new Type[1] { typeof(string) }, (Type[])null));
					bool flag2 = false;
					if (methodInfo2 != null)
					{
						flag2 = (bool)methodInfo2.Invoke(null, new object[1] { text });
					}
					obj = ((!flag2 || !(methodInfo3 != null)) ? methodInfo.Invoke(null, new object[2] { text, val }) : methodInfo3.Invoke(null, new object[1] { text }));
					if (obj != null)
					{
						list3.Add(obj);
					}
				}
			}
			if (list3.Count > 0)
			{
				field3.SetValue(value, list3);
				ManualLogSource? log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"Successfully healed {value} with {list3.Count} prefab refs.");
				}
			}
		}

		private static void EnemyDirectorAwakePostfix(object __instance)
		{
			try
			{
				Type type = AccessTools.TypeByName("REPOLib.Modules.Enemies");
				if (type == null)
				{
					return;
				}
				PropertyInfo property = type.GetProperty("RegisteredEnemies", BindingFlags.Static | BindingFlags.Public);
				if (property == null || !(property.GetValue(null) is IEnumerable enumerable))
				{
					return;
				}
				Type type2 = AccessTools.TypeByName("REPOLib.Objects.Sdk.EnemyContent");
				Type type3 = AccessTools.TypeByName("EnemySetup");
				if (type3 == null)
				{
					return;
				}
				foreach (object item in enumerable)
				{
					if (item == null)
					{
						continue;
					}
					if (type2 != null && type2.IsInstanceOfType(item))
					{
						HealEnemyContent(item);
					}
					else
					{
						if (!type3.IsInstanceOfType(item))
						{
							continue;
						}
						FieldInfo field = type3.GetField("spawnObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (field == null)
						{
							continue;
						}
						IList list = field.GetValue(item) as IList;
						bool flag = list == null || list.Count == 0;
						if (!flag && list != null)
						{
							foreach (object item2 in list)
							{
								if (item2 == null)
								{
									flag = true;
									break;
								}
								PropertyInfo property2 = item2.GetType().GetProperty("Prefab", BindingFlags.Instance | BindingFlags.Public);
								if (property2 == null || property2.GetValue(item2) == null)
								{
									flag = true;
									break;
								}
							}
						}
						if (!flag)
						{
							continue;
						}
						object obj = ((item is ScriptableObject) ? item : null);
						if ((((obj != null) ? ((Object)obj).name : null) ?? "").Contains("Kuwako"))
						{
							ManualLogSource? log = Log;
							if (log != null)
							{
								log.LogInfo((object)$"Healing Kuwako setup in EnemyDirectorAwakePostfix: {item}");
							}
							(AccessTools.TypeByName("RepoCustomEnemyMod.RepoCustomEnemyMod")?.GetMethod("EnsureSetupIsValid", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.Invoke(null, null);
						}
					}
				}
			}
			catch (Exception arg)
			{
				ManualLogSource? log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)$"Error in EnemyDirectorAwakePostfix: {arg}");
				}
			}
		}

		private static bool EntityEventLoggerPrefix(object __0)
		{
			if ((Object)((__0 is Object) ? __0 : null) == (Object)null)
			{
				return false;
			}
			return true;
		}

		private static void LogOnce(string key, string message)
		{
			if (Log != null)
			{
				if (LoggedOnce.Add(key))
				{
					Log.LogWarning((object)message);
				}
				else if (VerboseLogging != null && VerboseLogging.Value)
				{
					Log.LogWarning((object)message);
				}
			}
		}
	}
}