Decompiled source of Shadowman v1.1.1

plugins\Shadowman\Shadowman.dll

Decompiled 4 hours 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 BepInEx;
using BepInEx.Configuration;
using FMOD;
using FMOD.Studio;
using FMODUnity;
using Features.AIModule.Scripts;
using Features.AIModule.Scripts.ManInShadows;
using Fusion;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Shadowman")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Shadowman")]
[assembly: AssemblyTitle("Shadowman")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LastPirates.Shadowman
{
	[BepInPlugin("com.bwadd.lastpirates.shadowman", "Shadowman", "1.1.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.bwadd.lastpirates.shadowman";

		public const string PluginName = "Shadowman";

		public const string PluginVersion = "1.1.1";

		private const string AudioFileName = "Shadowman_18s.wav";

		private const string DeathAudioFileName = "Shadowman_Death.mp3";

		private const uint DeathAudioStartMilliseconds = 54000u;

		internal static Plugin Instance;

		internal static ConfigEntry<float> Volume;

		internal static ConfigEntry<float> MinimumDistance;

		internal static ConfigEntry<float> MaximumDistance;

		internal static ConfigEntry<float> DeathVolume;

		internal static ConfigEntry<float> EchoWetLevel;

		private readonly Dictionary<int, Channel> _channels = new Dictionary<int, Channel>();

		private readonly Dictionary<int, DSP> _echoes = new Dictionary<int, DSP>();

		private Sound _replacementSound;

		private Sound _deathSound;

		private bool _soundReady;

		private bool _deathSoundReady;

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Volume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "Volume", 0.8f, new ConfigDescription("Replacement whisper volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			MinimumDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "MinimumDistance", 3f, new ConfigDescription("Distance where the replacement plays at full volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 50f), Array.Empty<object>()));
			MaximumDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "MaximumDistance", 35f, new ConfigDescription("Distance where the replacement becomes inaudible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 200f), Array.Empty<object>()));
			DeathVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "DeathVolume", 1f, new ConfigDescription("Volume of the Shadowman death clip.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			EchoWetLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "EchoWetLevel", -18f, new ConfigDescription("Echo level in decibels. Set to -80 to disable it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-80f, 0f), Array.Empty<object>()));
			new Harmony("com.bwadd.lastpirates.shadowman").PatchAll(typeof(Plugin).Assembly);
			((MonoBehaviour)this).StartCoroutine(LoadSounds());
		}

		private IEnumerator LoadSounds()
		{
			string path = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Shadowman_18s.wav");
			string deathPath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Shadowman_Death.mp3");
			if (!File.Exists(path))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Replacement audio not found: " + path));
				yield break;
			}
			float deadline = Time.realtimeSinceStartup + 30f;
			string lastError = "FMOD is not initialized.";
			while (Time.realtimeSinceStartup < deadline)
			{
				try
				{
					System coreSystem = RuntimeManager.CoreSystem;
					if (((System)(ref coreSystem)).hasHandle())
					{
						RESULT val = ((System)(ref coreSystem)).createSound(path, (MODE)1048722, ref _replacementSound);
						if ((int)val == 0)
						{
							((Sound)(ref _replacementSound)).setLoopCount(-1);
							((Sound)(ref _replacementSound)).set3DMinMaxDistance(MinimumDistance.Value, MaximumDistance.Value);
							_soundReady = true;
							((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded Shadowman_18s.wav; Stalker whispers will be replaced locally.");
							break;
						}
						lastError = $"{val}: {Error.String(val)}";
					}
				}
				catch (Exception ex)
				{
					lastError = ex.ToString();
				}
				yield return (object)new WaitForSecondsRealtime(0.5f);
			}
			if (!_soundReady)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not load replacement through FMOD within 30 seconds: " + lastError));
			}
			if (!File.Exists(deathPath))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Death audio not found: " + deathPath));
				yield break;
			}
			System coreSystem2 = RuntimeManager.CoreSystem;
			RESULT val2 = ((System)(ref coreSystem2)).createSound(deathPath, (MODE)1048721, ref _deathSound);
			if ((int)val2 != 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Could not load Shadowman death audio: {val2}: {Error.String(val2)}");
				yield break;
			}
			((Sound)(ref _deathSound)).set3DMinMaxDistance(MinimumDistance.Value, MaximumDistance.Value);
			_deathSoundReady = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded Shadowman_Death.mp3; playback begins at 54 seconds on Stalker death.");
		}

		internal void PlayDeathSound(Vector3 worldPosition)
		{
			//IL_0019: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			if (!_deathSoundReady)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Shadowman died before the death audio was ready.");
				return;
			}
			System coreSystem = RuntimeManager.CoreSystem;
			Channel channel = default(Channel);
			RESULT val = ((System)(ref coreSystem)).playSound(_deathSound, default(ChannelGroup), true, ref channel);
			if ((int)val != 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Could not start Shadowman death audio: {val}: {Error.String(val)}");
				return;
			}
			VECTOR val2 = new VECTOR
			{
				x = worldPosition.x,
				y = worldPosition.y,
				z = worldPosition.z
			};
			VECTOR val3 = default(VECTOR);
			((Channel)(ref channel)).set3DAttributes(ref val2, ref val3);
			((Channel)(ref channel)).setVolume(DeathVolume.Value);
			AddSubtleEcho(channel);
			((Channel)(ref channel)).setPosition(54000u, (TIMEUNIT)1);
			((Channel)(ref channel)).setPaused(false);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Played Shadowman death audio from 54 seconds.");
		}

		internal void UpdateReplacement(ManInShadowsBehaviour enemy, float visibilityVolume)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			if (!_soundReady || (Object)(object)enemy == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)enemy).GetInstanceID();
			if (!_channels.TryGetValue(instanceID, out var value) || !((Channel)(ref value)).hasHandle())
			{
				System coreSystem = RuntimeManager.CoreSystem;
				RESULT val = ((System)(ref coreSystem)).playSound(_replacementSound, default(ChannelGroup), false, ref value);
				if ((int)val != 0)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)$"Could not start Shadowman whisper: {val}: {Error.String(val)}");
					return;
				}
				_channels[instanceID] = value;
				AddSubtleEcho(value, instanceID);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Started replacement whisper for Stalker instance {instanceID}.");
			}
			Vector3 position = ((Component)enemy).transform.position;
			VECTOR val2 = new VECTOR
			{
				x = position.x,
				y = position.y,
				z = position.z
			};
			VECTOR val3 = default(VECTOR);
			((Channel)(ref value)).set3DAttributes(ref val2, ref val3);
			((Channel)(ref value)).setVolume(Mathf.Clamp01(visibilityVolume) * Volume.Value);
		}

		internal void StopReplacement(ManInShadowsBehaviour enemy)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)enemy == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)enemy).GetInstanceID();
			if (_channels.TryGetValue(instanceID, out var value))
			{
				if (((Channel)(ref value)).hasHandle())
				{
					((Channel)(ref value)).stop();
					((Channel)(ref value)).clearHandle();
				}
				_channels.Remove(instanceID);
			}
			if (_echoes.TryGetValue(instanceID, out var value2))
			{
				if (((DSP)(ref value2)).hasHandle())
				{
					((DSP)(ref value2)).release();
				}
				_echoes.Remove(instanceID);
			}
		}

		private void AddSubtleEcho(Channel channel, int trackedInstanceId = 0)
		{
			//IL_0012: 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_0022: 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_0050: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			if (EchoWetLevel.Value <= -79.9f)
			{
				return;
			}
			System coreSystem = RuntimeManager.CoreSystem;
			DSP val2 = default(DSP);
			RESULT val = ((System)(ref coreSystem)).createDSPByType((DSP_TYPE)6, ref val2);
			if ((int)val != 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not create Shadowman echo: {val}: {Error.String(val)}");
				return;
			}
			((DSP)(ref val2)).setParameterFloat(0, 115f);
			((DSP)(ref val2)).setParameterFloat(1, 8f);
			((DSP)(ref val2)).setParameterFloat(2, 0f);
			((DSP)(ref val2)).setParameterFloat(3, EchoWetLevel.Value);
			val = ((Channel)(ref channel)).addDSP(0, val2);
			if ((int)val != 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not attach Shadowman echo: {val}: {Error.String(val)}");
				((DSP)(ref val2)).release();
			}
			else if (trackedInstanceId != 0)
			{
				_echoes[trackedInstanceId] = val2;
			}
			else
			{
				((DSP)(ref val2)).release();
			}
		}

		private void OnDestroy()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			foreach (Channel value in _channels.Values)
			{
				Channel current = value;
				if (((Channel)(ref current)).hasHandle())
				{
					((Channel)(ref current)).stop();
				}
			}
			_channels.Clear();
			foreach (DSP value2 in _echoes.Values)
			{
				DSP current2 = value2;
				if (((DSP)(ref current2)).hasHandle())
				{
					((DSP)(ref current2)).release();
				}
			}
			_echoes.Clear();
			if (((Sound)(ref _replacementSound)).hasHandle())
			{
				((Sound)(ref _replacementSound)).release();
				((Sound)(ref _replacementSound)).clearHandle();
			}
			if (((Sound)(ref _deathSound)).hasHandle())
			{
				((Sound)(ref _deathSound)).release();
				((Sound)(ref _deathSound)).clearHandle();
			}
			Instance = null;
		}
	}
	[HarmonyPatch(typeof(SimpleEnemyHealthController), "ReceiveDamage")]
	internal static class ShadowmanDeathPatch
	{
		private struct DeathState
		{
			internal bool WasAlive;

			internal bool IsShadowman;

			internal Vector3 Position;
		}

		private static void Prefix(SimpleEnemyHealthController __instance, ref DeathState __state)
		{
			//IL_002c: 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)
			__state.WasAlive = __instance.CurrentHealth > 0f;
			__state.IsShadowman = (Object)(object)((Component)__instance).GetComponent<ManInShadowsBehaviour>() != (Object)null;
			__state.Position = ((Component)__instance).transform.position;
		}

		private static void Postfix(SimpleEnemyHealthController __instance, DeathState __state)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (__state.WasAlive && __state.IsShadowman && __instance.CurrentHealth <= 0f)
			{
				Plugin.Instance?.PlayDeathSound(__state.Position);
			}
		}
	}
	[HarmonyPatch(typeof(ManInShadowsBehaviour), "FixedUpdate")]
	internal static class ReplaceWhisperUpdatePatch
	{
		private static void Postfix(ManInShadowsBehaviour __instance, ref EventInstance ____localVisibilitySoundInstance)
		{
			//IL_0011: 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)
			float visibilityVolume = 1f;
			if (((EventInstance)(ref ____localVisibilitySoundInstance)).isValid())
			{
				((EventInstance)(ref ____localVisibilitySoundInstance)).getVolume(ref visibilityVolume);
				((EventInstance)(ref ____localVisibilitySoundInstance)).setVolume(0f);
			}
			Plugin.Instance?.UpdateReplacement(__instance, visibilityVolume);
		}
	}
	[HarmonyPatch(typeof(ManInShadowsBehaviour), "Despawned")]
	internal static class ReplaceWhisperDespawnPatch
	{
		private static void Prefix(ManInShadowsBehaviour __instance, NetworkRunner runner, bool hasState)
		{
			Plugin.Instance?.StopReplacement(__instance);
		}
	}
}