Decompiled source of PSOFOSDeath v3.6.1

BepInEx/plugins/PSOFOSDeath/PSOFOSDeath.dll

Decompiled 3 weeks 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.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("PSOFOSDeath")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PSOFOSDeath")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e1c6268f-b8b6-49c2-b44e-6adbecdd155f")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("com.yourname.psofosdeath", "PSOFOS Death Overlay", "3.6.1")]
public class PSOFOSDeath : BaseUnityPlugin
{
	public class DeathHeartbeat : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <Tick>d__3 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public DeathHeartbeat <>4__this;

			private WaitForSeconds <wait>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <Tick>d__3(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<wait>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Expected O, but got Unknown
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0049: Expected O, but got Unknown
				int num = <>1__state;
				DeathHeartbeat deathHeartbeat = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<wait>5__2 = new WaitForSeconds(0.25f);
					<>2__current = (object)new WaitForSeconds(1f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					break;
				case 2:
					<>1__state = -1;
					break;
				}
				bool flag = false;
				try
				{
					flag = deathHeartbeat.DetectDeadByReflection();
				}
				catch
				{
				}
				if (flag && !deathHeartbeat._lastDead)
				{
					deathHeartbeat._overlay?.PlayOneShotIfReady();
				}
				deathHeartbeat._lastDead = flag;
				<>2__current = <wait>5__2;
				<>1__state = 2;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private DeathOverlay _overlay;

		private bool _lastDead;

		public void Init(DeathOverlay overlay)
		{
			_overlay = overlay;
			((MonoBehaviour)this).StartCoroutine(Tick());
		}

		[IteratorStateMachine(typeof(<Tick>d__3))]
		private IEnumerator Tick()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <Tick>d__3(0)
			{
				<>4__this = this
			};
		}

		private bool DetectDeadByReflection()
		{
			MonoBehaviour[] array = Object.FindObjectsOfType<MonoBehaviour>();
			foreach (MonoBehaviour val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Type type = ((object)val).GetType();
				string name = type.Name;
				if (name.IndexOf("PlayerAvatar", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("StatsManager", StringComparison.OrdinalIgnoreCase) < 0)
				{
					continue;
				}
				FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					string text = fieldInfo.Name.ToLowerInvariant();
					if ((text == "isdead" || text == "dead") && fieldInfo.FieldType == typeof(bool))
					{
						try
						{
							if ((bool)fieldInfo.GetValue(val))
							{
								return true;
							}
						}
						catch
						{
						}
					}
					if (text.Contains("health") && (fieldInfo.FieldType == typeof(int) || fieldInfo.FieldType == typeof(float)))
					{
						try
						{
							if (Convert.ToSingle(fieldInfo.GetValue(val)) <= 0f)
							{
								return true;
							}
						}
						catch
						{
						}
					}
					if (!(text == "isdisabled") || !(fieldInfo.FieldType == typeof(bool)))
					{
						continue;
					}
					try
					{
						if ((bool)fieldInfo.GetValue(val))
						{
							return true;
						}
					}
					catch
					{
					}
				}
				PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (PropertyInfo propertyInfo in properties)
				{
					if (!propertyInfo.CanRead)
					{
						continue;
					}
					string text2 = propertyInfo.Name.ToLowerInvariant();
					if ((!(text2 == "isdead") && !(text2 == "dead")) || !(propertyInfo.PropertyType == typeof(bool)))
					{
						continue;
					}
					try
					{
						if ((bool)propertyInfo.GetValue(val, null))
						{
							return true;
						}
					}
					catch
					{
					}
				}
			}
			return false;
		}
	}

	public class DeathOverlay : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <EnsureListenerAfterDelay>d__7 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public DeathOverlay <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <EnsureListenerAfterDelay>d__7(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Expected O, but got Unknown
				int num = <>1__state;
				DeathOverlay deathOverlay = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(1f);
					<>1__state = 1;
					return true;
				case 1:
				{
					<>1__state = -1;
					bool flag = false;
					AudioListener[] array = Object.FindObjectsOfType<AudioListener>();
					foreach (AudioListener val in array)
					{
						if ((Object)(object)val != (Object)null && ((Behaviour)val).enabled)
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						deathOverlay._busGO.AddComponent<AudioListener>();
						ManualLogSource log = Log;
						if (log != null)
						{
							log.LogWarning((object)"[PSOFOS] No enabled AudioListener detected after delay; attached a temporary fallback listener.");
						}
					}
					return false;
				}
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <LoadClips>d__8 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public DeathOverlay <>4__this;

			private string[] <names>5__2;

			private int <i>5__3;

			private string <name>5__4;

			private UnityWebRequest <req>5__5;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <LoadClips>d__8(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<names>5__2 = null;
				<name>5__4 = null;
				<req>5__5 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					int num = <>1__state;
					DeathOverlay deathOverlay = <>4__this;
					if (num != 0)
					{
						if (num != 1)
						{
							return false;
						}
						<>1__state = -3;
						if (<req>5__5.isNetworkError || <req>5__5.isHttpError)
						{
							Debug.LogWarning((object)("[PSOFOS] Failed to load " + <name>5__4 + ": " + <req>5__5.error));
						}
						else
						{
							AudioClip content = DownloadHandlerAudioClip.GetContent(<req>5__5);
							if (!((Object)(object)content == (Object)null))
							{
								((Object)content).name = <name>5__4;
								deathOverlay._clips.Add(content);
								Debug.Log((object)("[PSOFOS] Loaded " + <name>5__4));
								<>m__Finally1();
								<req>5__5 = null;
								<name>5__4 = null;
								goto IL_018d;
							}
							Debug.LogWarning((object)("[PSOFOS] Loaded null clip for " + <name>5__4));
						}
						<>m__Finally1();
						goto IL_018d;
					}
					<>1__state = -1;
					<names>5__2 = new string[3] { "PSOFOS01.ogg", "PSOFOS02.ogg", "PSOFOS03.ogg" };
					<i>5__3 = 0;
					goto IL_019f;
					IL_019f:
					if (<i>5__3 < <names>5__2.Length)
					{
						<name>5__4 = <names>5__2[<i>5__3];
						string text = Path.Combine(deathOverlay._pluginDir, <name>5__4);
						if (File.Exists(text))
						{
							string absoluteUri = new Uri(text).AbsoluteUri;
							<req>5__5 = UnityWebRequestMultimedia.GetAudioClip(absoluteUri, (AudioType)14);
							<>1__state = -3;
							<>2__current = <req>5__5.SendWebRequest();
							<>1__state = 1;
							return true;
						}
						goto IL_018d;
					}
					if (deathOverlay._clips.Count == 0)
					{
						Debug.LogWarning((object)("[PSOFOS] No OGGs found. Expected next to DLL at: " + deathOverlay._pluginDir));
					}
					return false;
					IL_018d:
					<i>5__3++;
					goto IL_019f;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<req>5__5 != null)
				{
					((IDisposable)<req>5__5).Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private readonly List<AudioClip> _clips = new List<AudioClip>();

		private AudioSource _bus;

		private GameObject _busGO;

		private string _pluginDir;

		private float _cooldownUntil;

		public void Init()
		{
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			_pluginDir = (string.IsNullOrEmpty(directoryName) ? Path.Combine(Paths.PluginPath, "PSOFOSDeath") : directoryName);
			try
			{
				Directory.CreateDirectory(_pluginDir);
			}
			catch
			{
			}
			EnsureBus();
			((MonoBehaviour)this).StartCoroutine(LoadClips());
		}

		private void EnsureBus()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			if (!((Object)(object)_bus != (Object)null))
			{
				_busGO = new GameObject("PSOFOS_AudioBus");
				((Object)_busGO).hideFlags = (HideFlags)52;
				Object.DontDestroyOnLoad((Object)(object)_busGO);
				_bus = _busGO.AddComponent<AudioSource>();
				_bus.playOnAwake = false;
				_bus.spatialBlend = 0f;
				_bus.priority = 0;
				_bus.volume = 1.35f;
				_bus.bypassReverbZones = true;
				_bus.bypassEffects = true;
				_bus.bypassListenerEffects = true;
				_bus.dopplerLevel = 0f;
				_bus.reverbZoneMix = 0f;
				((MonoBehaviour)this).StartCoroutine(EnsureListenerAfterDelay());
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)"[PSOFOS] Created PSOFOS audio bus.");
				}
			}
		}

		[IteratorStateMachine(typeof(<EnsureListenerAfterDelay>d__7))]
		private IEnumerator EnsureListenerAfterDelay()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <EnsureListenerAfterDelay>d__7(0)
			{
				<>4__this = this
			};
		}

		[IteratorStateMachine(typeof(<LoadClips>d__8))]
		private IEnumerator LoadClips()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadClips>d__8(0)
			{
				<>4__this = this
			};
		}

		public void PlayOneShotIfReady()
		{
			if (Time.unscaledTime < _cooldownUntil || _clips.Count == 0)
			{
				return;
			}
			EnsureBus();
			if ((Object)(object)_bus == (Object)null)
			{
				return;
			}
			AudioClip val = _clips[Random.Range(0, _clips.Count)];
			try
			{
				if (((Behaviour)_bus).isActiveAndEnabled && ((Component)_bus).gameObject.activeInHierarchy)
				{
					_bus.PlayOneShot(val, 1f);
					_cooldownUntil = Time.unscaledTime + 1f;
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)("[PSOFOS] Death event -> played '" + ((Object)val).name + "'."));
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[PSOFOS] OneShot play failed: " + ex));
				}
			}
		}
	}

	internal static ManualLogSource Log;

	private Harmony _harmony;

	private static DeathOverlay _overlay;

	private void Awake()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		GameObject val = new GameObject("PSOFOS_DeathOverlay");
		Object.DontDestroyOnLoad((Object)val);
		_overlay = val.AddComponent<DeathOverlay>();
		_overlay.Init();
		_harmony = new Harmony("com.yourname.psofosdeath.harmony");
		Type type = AccessTools.TypeByName("PlayerAvatar");
		TryPatchPostfix(type, "PlayerDeath", "OnDeathPostfix");
		TryPatchPostfix(type, "PlayerDeathRPC", "OnDeathPostfix");
		TryPatchPostfix(type, "PlayerDeathDone", "OnDeathPostfix");
		val.AddComponent<DeathHeartbeat>().Init(_overlay);
		Log.LogInfo((object)"[PSOFOS] v3.6.1 initialized (3.2 hooks + edge-detected watcher)");
	}

	private void TryPatchPostfix(Type type, string method, string handlerName)
	{
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Expected O, but got Unknown
		if (type == null)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)("[PSOFOS] Type not found for method: " + method));
			}
			return;
		}
		MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null);
		if (methodInfo == null)
		{
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogWarning((object)("[PSOFOS] Method not found: " + ((type != null) ? type.Name : "null") + "." + method + "()"));
			}
			return;
		}
		HarmonyMethod val = new HarmonyMethod(typeof(PSOFOSDeath).GetMethod(handlerName, BindingFlags.Static | BindingFlags.NonPublic));
		_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		ManualLogSource log3 = Log;
		if (log3 != null)
		{
			log3.LogInfo((object)("[PSOFOS] Patched " + type.Name + "." + method + "()"));
		}
	}

	private static void OnDeathPostfix()
	{
		try
		{
			_overlay?.PlayOneShotIfReady();
		}
		catch (Exception ex)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)("[PSOFOS] DeathPostfix error: " + ex));
			}
		}
	}

	private void OnDestroy()
	{
		try
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
	}
}