Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of VepMod v1.1.0
VepMod.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using Photon.Voice; using REPOLib.Objects.Sdk; using TMPro; using Unity.VisualScripting; using UnityEngine; using UnityEngine.AI; using UnityEngine.Animations; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; using VepMod.Enemies.Whispral; using VepMod.Enemies.Whispral.Visuals; using VepMod.Patchs; using VepMod.VepFramework; using VepMod.VepFramework.Audio; using VepMod.VepFramework.Config; using VepMod.VepFramework.Extensions; using VepMod.VepFramework.Structures.FSM; using VepMod.VepFramework.Structures.Range; using WebRtcVadSharp; [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("Vep")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+be4c087c041c4823a6c3e289f18800a10bce2486")] [assembly: AssemblyProduct("VepMod")] [assembly: AssemblyTitle("VepMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 VepMod { public static class ConfigRanges { public static readonly RangeValue<float> Volume = RangeValue.Percentage(); public static readonly RangeValue<int> SamplesPerPlayer = RangeValue.Int(5, 20, 20); public static readonly RangeValue<int> SamplingRate = RangeValue.Int(16000, 48000, 48000); public static readonly MinMaxRange<float> ShareDelay = MinMaxRange.Float(10f, 60f, 10f, 10f, 120f, 30f); public static readonly MinMaxRange<float> VoiceDelay = MinMaxRange.Float(6f, 30f, 8f, 6f, 30f, 15f); public static readonly RangeValue<float> AudioMinDuration = RangeValue.Float(0.1f, 2f, 0.3f); } [BepInPlugin("com.vep.vepMod", "VepMod", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class VepMod : BaseUnityPlugin { private static Harmony _harmony; public static ConfigEntry<float> ConfigVoiceVolume; public static ConfigEntry<int> ConfigSamplingRate; public static BoundMinMaxRange<float> ShareDelay; public static ConfigEntry<int> ConfigSamplesPerPlayer; public static BoundMinMaxRange<float> VoiceDelay; public static ConfigEntry<bool> ConfigVoiceFilterEnabled; public static ConfigEntry<float> ConfigAudioMinDuration; public static ConfigEntry<bool> ConfigVadEnabled; public static ConfigEntry<VadSensitivity> ConfigVadSensitivity; public static readonly Dictionary<string, ConfigEntry<bool>> EnemyConfigEntries = new Dictionary<string, ConfigEntry<bool>>(); internal Harmony? Harmony { get; set; } private void Awake() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown _harmony = new Harmony("VepMod.Plugin"); PreventDelete(); InitConfig(); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!"); ((MonoBehaviour)this).StartCoroutine(DroidPrefabLoader.PreloadAsync(delegate(bool success) { if (success) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"LostDroid prefab preloaded successfully."); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"LostDroid prefab preload failed - hallucinations may not work."); } })); } public void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Scene loaded: " + ((Scene)(ref scene)).name)); } private void InitConfig() { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown ((BaseUnityPlugin)this).Logger.LogInfo((object)"Initializing config for VepMod..."); ConfigVoiceVolume = ((BaseUnityPlugin)this).Config.BindRange("General", "Volume", ConfigRanges.Volume, "Volume of the mimic voices."); ShareDelay = ((BaseUnityPlugin)this).Config.BindMinMax("Audio Sharing", "Share Min Delay", "Share Max Delay", ConfigRanges.ShareDelay, "Minimum time between sharing audio clips with other players.", "Maximum time between sharing audio clips with other players."); ConfigSamplesPerPlayer = ((BaseUnityPlugin)this).Config.BindRange("Audio Sharing", "Samples Per Player", ConfigRanges.SamplesPerPlayer, "Maximum number of audio samples stored per player."); VoiceDelay = ((BaseUnityPlugin)this).Config.BindMinMax("Voice Playback", "Voice Min Delay", "Voice Max Delay", ConfigRanges.VoiceDelay, "Minimum time between voice playback commands during Whispral debuff.", "Maximum time between voice playback commands during Whispral debuff."); ConfigVoiceFilterEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Voice Playback", "Voice Filter Enabled?", false, new ConfigDescription("Turning this will enable modifiable filters for which enemies can play back voices during the Whispral debuff.", (AcceptableValueBase)null, Array.Empty<object>())); ConfigSamplingRate = ((BaseUnityPlugin)this).Config.BindRange("Experimental", "Sampling Rate", ConfigRanges.SamplingRate, "Only change this value if the console gives you a warning about your microphone frequency not being supported."); ConfigAudioMinDuration = ((BaseUnityPlugin)this).Config.BindRange("Audio Quality", "Min Duration", ConfigRanges.AudioMinDuration, "Minimum duration in seconds. Shorter recordings are rejected."); ConfigVadEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio Quality", "VAD Enabled", true, "Enable Voice Activity Detection (VAD) for speech filtering. Uses WebRTC ML model (requires x64). Disable if you have issues."); ConfigVadSensitivity = ((BaseUnityPlugin)this).Config.Bind<VadSensitivity>("Audio Quality", "VAD Sensitivity", VadSensitivity.Balanced, "Speech detection strictness. Permissive (ratio 0.20) keeps almost all voice but lets through more noise. Balanced (0.40) is the default best-F1 setting. Strict (0.60) filters more aggressively but may drop quiet voice."); } private void PreventDelete() { ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; } internal void Unpatch() { _harmony.UnpatchSelf(); } } } namespace VepMod.VepFramework { public sealed class VepLogger { private const string Prefix = "VepMod"; private readonly ManualLogSource _log; public bool DebugEnabled { get; set; } private VepLogger(string className, bool debugEnabled = false) { _log = Logger.CreateLogSource("VepMod." + className); DebugEnabled = debugEnabled; } public static VepLogger Create<T>(bool debugEnabled = false) { return new VepLogger(typeof(T).Name, debugEnabled); } public static VepLogger Create(string className, bool debugEnabled = false) { return new VepLogger(className, debugEnabled); } public void Debug(string message) { if (DebugEnabled) { _log.LogDebug((object)message); } } public void Info(string message) { _log.LogInfo((object)message); } public void Warning(string message) { _log.LogWarning((object)message); } public void Error(string message) { _log.LogError((object)message); } } } namespace VepMod.VepFramework.Structures.Range { public sealed class MinMaxRange<T> where T : struct, IComparable<T> { public RangeValue<T> MinRange { get; } public RangeValue<T> MaxRange { get; } public MinMaxRange(RangeValue<T> minRange, RangeValue<T> maxRange) { MinRange = minRange; MaxRange = maxRange; } public T GetMin(T minValue, T maxValue) { if (minValue.CompareTo(maxValue) > 0) { return maxValue; } return minValue; } public T GetMax(T minValue, T maxValue) { if (maxValue.CompareTo(minValue) < 0) { return minValue; } return maxValue; } } public static class MinMaxRange { public static MinMaxRange<float> Float(float minLower, float minUpper, float minDefault, float maxLower, float maxUpper, float maxDefault) { return new MinMaxRange<float>(RangeValue.Float(minLower, minUpper, minDefault), RangeValue.Float(maxLower, maxUpper, maxDefault)); } } public sealed class RangeValue<T> where T : struct, IComparable<T> { public T Min { get; } public T Max { get; } public T Default { get; } public RangeValue(T min, T max, T defaultValue) { if (min.CompareTo(max) > 0) { throw new ArgumentException($"Min ({min}) cannot be greater than Max ({max})"); } Min = min; Max = max; Default = Clamp(defaultValue); } public T Clamp(T value) { if (value.CompareTo(Min) < 0) { return Min; } if (value.CompareTo(Max) > 0) { return Max; } return value; } } public static class RangeValue { public static RangeValue<float> Float(float min, float max, float defaultValue) { return new RangeValue<float>(min, max, defaultValue); } public static RangeValue<int> Int(int min, int max, int defaultValue) { return new RangeValue<int>(min, max, defaultValue); } public static RangeValue<float> Percentage(float defaultValue = 1f) { return new RangeValue<float>(0f, 1f, defaultValue); } } } namespace VepMod.VepFramework.Structures.FSM { public class StateMachineBase<TMachine, TStateId> where TMachine : StateMachineBase<TMachine, TStateId> { public class StateBase { public TMachine Machine { get; set; } public virtual void OnStateEnter(TStateId previous) { } public virtual void OnStateExit(TStateId next) { } public virtual void OnStateUpdate() { } } public class StateBaseTimed : StateBase { public float TimeElapsed { get; protected set; } public override void OnStateEnter(TStateId previous) { base.OnStateEnter(previous); TimeElapsed = 0f; } public override void OnStateUpdate() { base.OnStateUpdate(); TimeElapsed += Time.deltaTime; } } public class StateBaseTransition : StateBaseTimed { public float Duration; public TStateId NextState; public StateBaseTransition(float duration, TStateId nextState) { Duration = duration; NextState = nextState; } public override void OnStateEnter(TStateId previous) { base.OnStateEnter(previous); if (Duration <= 0f) { base.Machine.NextStateStateId = NextState; } } public override void OnStateUpdate() { base.OnStateUpdate(); if (base.TimeElapsed >= Duration) { base.Machine.NextStateStateId = NextState; } } } private readonly StateBase Default = new StateBase(); private readonly Dictionary<TStateId, StateBase> states = new Dictionary<TStateId, StateBase>(); private StateBase _currentStateBaseRunning; public TStateId CurrentStateStateId { get; private set; } public TStateId NextStateStateId { get; set; } public StateMachineBase(TStateId defaultStateId) { CurrentStateStateId = defaultStateId; NextStateStateId = defaultStateId; } public void AddState(TStateId stateId, StateBase stateBase) { stateBase.Machine = this as TMachine; if (states.ContainsKey(stateId)) { Debug.LogWarning((object)$"[FSM] Fsm already defines a state with key {stateId}, it will be overidden"); } states[stateId] = stateBase; } public StateBase GetState(TStateId stateId) { if (!states.TryGetValue(stateId, out StateBase value)) { return Default; } return value; } public T GetStateTyped<T>(TStateId stateId) where T : StateBase { return GetState(stateId) as T; } private void RefreshStatesIO() { for (StateBase state = GetState(NextStateStateId); state != _currentStateBaseRunning; state = GetState(NextStateStateId)) { TStateId nextStateStateId = NextStateStateId; _currentStateBaseRunning?.OnStateExit(nextStateStateId); if (!states.ContainsKey(nextStateStateId)) { Debug.LogWarning((object)$"[FSM] Fsm does not define any state with key {nextStateStateId}, it will play default state"); } _currentStateBaseRunning = GetState(nextStateStateId); _currentStateBaseRunning?.OnStateEnter(CurrentStateStateId); CurrentStateStateId = nextStateStateId; } } public void Update() { RefreshStatesIO(); _currentStateBaseRunning?.OnStateUpdate(); RefreshStatesIO(); } } public abstract class StateMachineComponent<TOwner, TStateId> : MonoBehaviour where TOwner : StateMachineComponent<TOwner, TStateId> { protected sealed class StateMachine : StateMachineBase<StateMachine, TStateId> { public TOwner Owner { get; } public StateMachine(TOwner owner, TStateId defaultKey) : base(defaultKey) { Owner = owner; } } protected StateMachine Fsm; protected abstract TStateId DefaultState { get; } protected virtual void Awake() { Fsm = new StateMachine(this as TOwner, DefaultState); } protected virtual void Update() { Fsm.Update(); } } } namespace VepMod.VepFramework.Extensions { public static class FileSystemSanitizer { private const string DefaultFallback = "unknown"; private static readonly HashSet<string> WindowsReservedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" }; public static string Sanitize(string input, string fallback = "unknown") { if (string.IsNullOrEmpty(input)) { return fallback; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); string text = string.Join("_", input.Split(invalidFileNameChars, StringSplitOptions.RemoveEmptyEntries)); text = text.TrimEnd('.', ' '); if (string.IsNullOrEmpty(text)) { return fallback; } if (WindowsReservedNames.Contains(text)) { text = "_" + text + "_"; } return text; } } public static class TaskExtensions { public static IEnumerator AsCoroutine(this Task task) { while (!task.IsCompleted) { yield return null; } if (task.Exception != null) { throw task.Exception; } } } } namespace VepMod.VepFramework.Config { public sealed class BoundMinMaxRange<T> where T : struct, IComparable<T> { private readonly MinMaxRange<T> _range; private readonly ConfigEntry<T> _minEntry; private readonly ConfigEntry<T> _maxEntry; public T Min => _range.GetMin(_minEntry.Value, _maxEntry.Value); public T Max => _range.GetMax(_minEntry.Value, _maxEntry.Value); public BoundMinMaxRange(MinMaxRange<T> range, ConfigEntry<T> minEntry, ConfigEntry<T> maxEntry) { _range = range; _minEntry = minEntry; _maxEntry = maxEntry; } } public static class ConfigExtensions { private static AcceptableValueRange<T> ToAcceptableValueRange<T>(this RangeValue<T> range) where T : struct, IComparable, IComparable<T> { return new AcceptableValueRange<T>(range.Min, range.Max); } private static ConfigDescription ToConfigDescription<T>(this RangeValue<T> range, string description) where T : struct, IComparable, IComparable<T> { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown return new ConfigDescription(description, (AcceptableValueBase)(object)range.ToAcceptableValueRange(), Array.Empty<object>()); } public static ConfigEntry<T> BindRange<T>(this ConfigFile config, string section, string key, RangeValue<T> range, string description) where T : struct, IComparable, IComparable<T> { return config.Bind<T>(section, key, range.Default, range.ToConfigDescription(description)); } public static BoundMinMaxRange<T> BindMinMax<T>(this ConfigFile config, string section, string minKey, string maxKey, MinMaxRange<T> range, string minDescription, string maxDescription) where T : struct, IComparable, IComparable<T> { ConfigEntry<T> minEntry = config.Bind<T>(section, minKey, range.MinRange.Default, range.MinRange.ToConfigDescription(minDescription)); ConfigEntry<T> maxEntry = config.Bind<T>(section, maxKey, range.MaxRange.Default, range.MaxRange.ToConfigDescription(maxDescription)); return new BoundMinMaxRange<T>(range, minEntry, maxEntry); } } } namespace VepMod.VepFramework.Audio { public sealed class VadAudioValidator : IDisposable { private readonly VadValidationCriteria _criteria; private readonly WebRtcVad _vad; private readonly object _gate = new object(); private bool _disposed; public VadAudioValidator(VadValidationCriteria? criteria = null) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown _criteria = criteria ?? VadValidationCriteria.FromSensitivity(VadSensitivity.Balanced); _vad = new WebRtcVad { OperatingMode = _criteria.OperatingMode, FrameLength = _criteria.FrameLength, SampleRate = _criteria.SampleRate }; } public void Dispose() { lock (_gate) { if (!_disposed) { _vad.Dispose(); _disposed = true; } } } public VadValidationResult Validate(float[] samples, int sampleRate) { //IL_004f: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Invalid comparison between I4 and Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected I4, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) if (samples == null || samples.Length == 0) { return VadValidationResult.Rejected(VadRejectionReason.EmptyBuffer, null); } float num = (float)samples.Length / (float)sampleRate; if (num < _criteria.MinDurationSeconds) { return VadValidationResult.Rejected(VadRejectionReason.TooShort, new VadAnalysisResult(num, 0, 0, 0f)); } byte[] array = ConvertToPcm16(samples); SampleRate targetSampleRate = GetTargetSampleRate(sampleRate); int frameSamplesCount = GetFrameSamplesCount(targetSampleRate, _criteria.FrameLength); int num2 = frameSamplesCount * 2; if (sampleRate != (int)targetSampleRate) { array = Resample(array, sampleRate, (int)targetSampleRate); } int num3 = array.Length / num2; if (num3 == 0) { return VadValidationResult.Rejected(VadRejectionReason.TooShort, new VadAnalysisResult(num, 0, 0, 0f)); } int num4 = 0; byte[] array2 = new byte[num2]; lock (_gate) { if (_disposed) { return VadValidationResult.Accepted(new VadAnalysisResult(num, num3, 0, 0f)); } for (int i = 0; i < num3; i++) { int num5 = i * num2; if (num5 + num2 > array.Length) { break; } Array.Copy(array, num5, array2, 0, num2); if (_vad.HasSpeech(array2, targetSampleRate, _criteria.FrameLength)) { num4++; } } } float num6 = (float)num4 / (float)num3; VadAnalysisResult vadAnalysisResult = new VadAnalysisResult(num, num3, num4, num6); if (num6 < _criteria.MinSpeechRatio) { return VadValidationResult.Rejected(VadRejectionReason.NotEnoughSpeech, vadAnalysisResult); } return VadValidationResult.Accepted(vadAnalysisResult); } private static byte[] ConvertToPcm16(float[] samples) { byte[] array = new byte[samples.Length * 2]; for (int i = 0; i < samples.Length; i++) { short num = (short)(samples[i] * 32767f); array[i * 2] = (byte)(num & 0xFF); array[i * 2 + 1] = (byte)((num >> 8) & 0xFF); } return array; } private static SampleRate GetTargetSampleRate(int sampleRate) { //IL_0041: 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_0052: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) return (SampleRate)(sampleRate switch { 8000 => 8000, 16000 => 16000, 32000 => 32000, 48000 => 48000, _ => 16000, }); } private static int GetFrameSamplesCount(SampleRate sampleRate, FrameLength frameLength) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I8 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I8 //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I8 int num = (int)sampleRate; int num2 = (((long)frameLength == 10) ? 10 : (((long)frameLength == 20) ? 20 : (((long)frameLength != 30) ? 20 : 30))); int num3 = num2; return num * num3 / 1000; } private static byte[] Resample(byte[] pcmBytes, int sourceSampleRate, int targetSampleRate) { if (pcmBytes == null || pcmBytes.Length < 2) { return Array.Empty<byte>(); } if (sourceSampleRate == targetSampleRate) { return pcmBytes; } int num = pcmBytes.Length / 2; short[] array = new short[num]; int num2 = 0; int num3 = 0; while (num2 < num) { array[num2] = (short)(pcmBytes[num3] | (pcmBytes[num3 + 1] << 8)); num2++; num3 += 2; } double num4 = (double)sourceSampleRate / (double)targetSampleRate; int num5 = (int)Math.Floor((double)(num - 1) / num4); if (num5 <= 0) { return Array.Empty<byte>(); } short[] array2 = new short[num5]; double num6 = 0.0; for (int i = 0; i < num5; i++) { int num7 = (int)num6; double num8 = num6 - (double)num7; short num9 = array[num7]; short num10 = array[num7 + 1]; double num11 = (double)num9 + (double)(num10 - num9) * num8; if (num11 > 32767.0) { num11 = 32767.0; } else if (num11 < -32768.0) { num11 = -32768.0; } array2[i] = (short)num11; num6 += num4; if (num6 >= (double)(num - 1)) { num6 = (double)num - 1.000001; } } byte[] array3 = new byte[num5 * 2]; int num12 = 0; int num13 = 0; while (num12 < num5) { short num14 = array2[num12]; array3[num13] = (byte)(num14 & 0xFF); array3[num13 + 1] = (byte)((num14 >> 8) & 0xFF); num12++; num13 += 2; } return array3; } } public sealed class VadValidationCriteria { public float MinDurationSeconds { get; set; } = 0.3f; public float MinSpeechRatio { get; set; } = 0.1f; public OperatingMode OperatingMode { get; set; } public FrameLength FrameLength { get; set; } = (FrameLength)20; public SampleRate SampleRate { get; set; } = (SampleRate)16000; public static VadValidationCriteria FromSensitivity(VadSensitivity sensitivity) { float minSpeechRatio = sensitivity switch { VadSensitivity.Permissive => 0.2f, VadSensitivity.Strict => 0.6f, _ => 0.4f, }; return new VadValidationCriteria { MinDurationSeconds = 0f, MinSpeechRatio = minSpeechRatio, OperatingMode = (OperatingMode)0, FrameLength = (FrameLength)20, SampleRate = (SampleRate)16000 }; } } public enum VadSensitivity { Permissive, Balanced, Strict } public readonly struct VadAnalysisResult { public float DurationSeconds { get; } public int TotalFrames { get; } public int SpeechFrames { get; } public float SpeechRatio { get; } public VadAnalysisResult(float durationSeconds, int totalFrames, int speechFrames, float speechRatio) { DurationSeconds = durationSeconds; TotalFrames = totalFrames; SpeechFrames = speechFrames; SpeechRatio = speechRatio; } public override string ToString() { return $"Duration: {DurationSeconds:F2}s, Speech: {SpeechFrames}/{TotalFrames} frames ({SpeechRatio:P1})"; } } public readonly struct VadValidationResult { public bool IsValid { get; } public VadRejectionReason RejectionReason { get; } public VadAnalysisResult? Analysis { get; } private VadValidationResult(bool isValid, VadRejectionReason reason, VadAnalysisResult? analysis) { IsValid = isValid; RejectionReason = reason; Analysis = analysis; } public static VadValidationResult Accepted(VadAnalysisResult analysis) { return new VadValidationResult(isValid: true, VadRejectionReason.None, analysis); } public static VadValidationResult Rejected(VadRejectionReason reason, VadAnalysisResult? analysis) { return new VadValidationResult(isValid: false, reason, analysis); } public override string ToString() { if (!IsValid) { return $"Rejected ({RejectionReason}) - {Analysis}"; } return $"Valid - {Analysis}"; } } public enum VadRejectionReason { None, EmptyBuffer, TooShort, NotEnoughSpeech } } namespace VepMod.Patchs { [HarmonyPatch(typeof(EnemyDirector), "FirstSpawnPointAdd")] public class EnemyLoggerPatch { private static readonly VepLogger LOG = VepLogger.Create<EnemyLoggerPatch>(); [HarmonyPrefix] private static void Prefix(EnemyParent _enemyParent) { LOG.Info($"EnemyDirector.FirstSpawnPointAdd: {_enemyParent} added as first spawn point."); } } [HarmonyPatch(typeof(LocalVoiceFramed<short>), "PushDataAsync")] internal class LocalVoiceFramedPatch { private static readonly VepLogger LOG = VepLogger.Create<LocalVoiceFramedPatch>(); private static void Prefix(short[] buf, LocalVoiceFramed<short> __instance) { VepFinder.EnsureInitialized(); if (!IsNotReady()) { var (frameSampleRate, channels) = VoiceFormatResolver.Resolve(__instance, buf.Length); VepFinder.LocalMimics.ProcessVoiceData(buf, frameSampleRate, channels); } } private static bool IsNotReady() { bool flag = !PhotonNetwork.IsConnectedAndReady; WhispralMimics localMimics = VepFinder.LocalMimics; bool flag2 = (Object)(object)localMimics == (Object)null; if (flag || flag2) { return true; } bool flag3 = (Object)(object)((Component)localMimics).gameObject == (Object)null; bool flag4 = (Object)(object)localMimics.PhotonView == (Object)null || !localMimics.PhotonView.IsMine; return flag3 || flag4; } } [HarmonyPatch(typeof(Materials))] internal static class MaterialsImpulsePatch { [HarmonyPrefix] [HarmonyPatch("Impulse")] private static bool Impulse_Prefix(Vector3 origin, bool footstep, HostType hostType) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (!footstep) { return true; } if ((int)hostType != 0 && (int)hostType != 1) { return true; } if (InvisiblePlayerDebuff.IsPositionNearHiddenPlayer(origin)) { return false; } return true; } } [HarmonyPatch(typeof(PlayerAvatar), "Awake")] internal class PlayerAvatarPatch { private static readonly VepLogger LOG = VepLogger.Create<PlayerAvatarPatch>(); private static void Postfix(PlayerAvatar __instance) { if (PhotonNetwork.IsConnectedAndReady) { WhispralMimics whispralMimics = ((Component)__instance).GetComponent<WhispralMimics>(); if (!Object.op_Implicit((Object)(object)whispralMimics)) { whispralMimics = ((Component)__instance).gameObject.AddComponent<WhispralMimics>(); LOG.Debug("Added Mimics component to PlayerAvatar: " + ((Object)__instance).name); } else { LOG.Warning("PlayerAvatar: " + ((Object)__instance).name + " already has a WhispralMimics component."); } PhotonView component = ((Component)__instance).GetComponent<PhotonView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsMine) { LOG.Warning("PhotonView is null or not owned by local player."); return; } VepFinder.LocalMimics = whispralMimics; LOG.Info("Set LocalMimics for local PlayerAvatar: " + ((Object)__instance).name); } } } [HarmonyPatch(typeof(PlayerController))] internal static class PlayerControllerPatch { } public class VepFinder : MonoBehaviour { private static VepFinder _instance; private static bool _initialized; private static readonly VepLogger LOG = VepLogger.Create<VepFinder>(); public static WhispralMimics LocalMimics { get; set; } private void OnDestroy() { if (_initialized) { LocalMimics = null; _instance = null; LOG.Info("VepFinder destroyed, clearing cache."); } } public static void EnsureInitialized() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)_instance)) { _instance = new GameObject("VepFinder").AddComponent<VepFinder>(); _initialized = true; Object.DontDestroyOnLoad((Object)(object)((Component)_instance).gameObject); Player localPlayer = PhotonNetwork.LocalPlayer; LOG.Info($"MimicsFinder initialized for Player {((localPlayer != null) ? localPlayer.ActorNumber : (-1))}"); } } } public static class VoiceFormatResolver { private sealed class Format { public int SampleRate { get; } public int Channels { get; } public Format(int sampleRate, int channels) { SampleRate = sampleRate; Channels = channels; } } private const int MinPlausibleRate = 8000; private const int MaxPlausibleRate = 48000; private static readonly VepLogger LOG = VepLogger.Create("VoiceFormatResolver", debugEnabled: true); private static readonly ConditionalWeakTable<object, Format> Cache = new ConditionalWeakTable<object, Format>(); public static (int sampleRate, int channels) Resolve(object voice, int bufferLength) { if (voice == null) { return (sampleRate: 48000, channels: 1); } if (Cache.TryGetValue(voice, out Format value)) { return (sampleRate: value.SampleRate, channels: value.Channels); } Format format = ResolveUncached(voice, bufferLength); Cache.Add(voice, format); return (sampleRate: format.SampleRate, channels: format.Channels); } private static Format ResolveUncached(object voice, int bufferLength) { object obj = null; try { obj = GetMemberValue(voice, "Info"); } catch (Exception ex) { LOG.Warning("Could not read voice Info: " + ex.Message); } int? num = TryGetInt(voice, "InputSamplingRate"); int? num2 = TryGetInt(voice, "InputChannels"); int? num3 = TryGetInt(voice, "ForceChannels"); int? num4 = ((obj != null) ? TryGetInt(obj, "SamplingRate") : ((int?)null)); int? num5 = ((obj != null) ? TryGetInt(obj, "Channels") : ((int?)null)); int? value = ((obj != null) ? TryGetInt(obj, "FrameDurationUs") : ((int?)null)); int num6 = FirstPlausibleChannels(num2, num3, num5) ?? 1; int num7; string source; if (IsPlausibleRate(num)) { num7 = num.Value; source = "InputSamplingRate"; } else if (value.HasValue && value.GetValueOrDefault() > 0 && bufferLength > 0) { double num8 = (double)bufferLength / (double)num6; int num9 = (int)Math.Round(num8 * 1000000.0 / (double)value.Value); if (IsPlausibleRate(num9)) { num7 = num9; source = "derived(FrameDurationUs)"; } else { num7 = FallbackRate(num4, out source); } } else { num7 = FallbackRate(num4, out source); } LOG.Info($"Voice source format resolved: {num7} Hz, {num6} ch (via {source}). " + "Candidates: InputSamplingRate=" + Fmt(num) + ", InputChannels=" + Fmt(num2) + ", ForceChannels=" + Fmt(num3) + ", encoder=" + Fmt(num4) + "Hz/" + Fmt(num5) + "ch, " + $"FrameDurationUs={Fmt(value)}, bufferLength={bufferLength}."); return new Format(num7, num6); } private static int FallbackRate(int? encoderRate, out string source) { if (IsPlausibleRate(encoderRate)) { source = "Info.SamplingRate(fallback)"; return encoderRate.Value; } source = "default(fallback)"; return 48000; } private static int? FirstPlausibleChannels(params int?[] candidates) { for (int i = 0; i < candidates.Length; i++) { int? result = candidates[i]; if (result.HasValue) { int valueOrDefault = result.GetValueOrDefault(); if (valueOrDefault >= 1 && valueOrDefault <= 8) { return result; } } } return null; } private static bool IsPlausibleRate(int? rate) { if (rate.HasValue) { int valueOrDefault = rate.GetValueOrDefault(); if (valueOrDefault >= 8000) { return valueOrDefault <= 48000; } } return false; } private static int? TryGetInt(object obj, string name) { try { object memberValue = GetMemberValue(obj, name); object obj2 = memberValue; return (obj2 == null) ? ((int?)null) : ((obj2 is int value) ? new int?(value) : ((!(obj2 is Enum)) ? ((!(memberValue is IConvertible)) ? ((int?)null) : new int?(Convert.ToInt32(memberValue))) : new int?(Convert.ToInt32(memberValue)))); } catch { return null; } } private static object? GetMemberValue(object obj, string name) { Type type = obj.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanRead) { return property.GetValue(obj); } FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(obj); } type = type.BaseType; } return null; } private static string Fmt(int? value) { return value?.ToString() ?? "n/a"; } } } namespace VepMod.Enemies.Whispral { public static class AudioFilters { private const float TwoPi = MathF.PI * 2f; private const float Int16MaxValue = 32768f; private const float AlienVibratoFrequency = 5f; private const float AlienVibratoDepth = 0.05f; private const float AlienRingModFrequency = 200f; private const float AlienRingModMix = 0.3f; public const float PitchShiftLow = 0.5f; public const float PitchShiftHigh = 1.2f; private const float DefaultLowPassCutoff = 4500f; public static float[] ApplyAlienFilter(float[] samples, int sampleRate) { float[] array = new float[samples.Length]; for (int i = 0; i < samples.Length; i++) { float num = (float)i / (float)sampleRate; float num2 = 1f + Mathf.Sin(MathF.PI * 10f * num) * 0.05f; float num3 = (float)i * num2; int num4 = (int)num3; float num5 = num3 - (float)num4; float num6 = ((num4 + 1 >= samples.Length) ? ((num4 >= samples.Length) ? 0f : samples[num4]) : (samples[num4] * (1f - num5) + samples[num4 + 1] * num5)); float num7 = Mathf.Sin(MathF.PI * 400f * num); float num8 = num6 * num7 * 0.3f; array[i] = Mathf.Clamp(num6 * 0.7f + num8, -1f, 1f); } return array; } public static float[] ApplyLowPassFilter(float[] samples, int sampleRate, float cutoffFrequency = 4500f) { float num = 1f / (MathF.PI * 2f * cutoffFrequency); float num2 = 1f / (float)sampleRate; float num3 = num2 / (num + num2); float[] array = new float[samples.Length]; array[0] = samples[0]; for (int i = 1; i < samples.Length; i++) { array[i] = array[i - 1] + num3 * (samples[i] - array[i - 1]); } float[] array2 = new float[samples.Length]; array2[samples.Length - 1] = array[samples.Length - 1]; for (int num4 = samples.Length - 2; num4 >= 0; num4--) { array2[num4] = array2[num4 + 1] + num3 * (array[num4] - array2[num4 + 1]); } return array2; } public static float[] ApplyPitchShift(float[] samples, float pitchFactor) { int num = (int)((float)samples.Length / pitchFactor); float[] array = new float[num]; for (int i = 0; i < num; i++) { float num2 = (float)i * pitchFactor; int num3 = (int)num2; float num4 = num2 - (float)num3; if (num3 + 1 < samples.Length) { array[i] = samples[num3] * (1f - num4) + samples[num3 + 1] * num4; } else if (num3 < samples.Length) { array[i] = samples[num3]; } } return array; } public static float[] ConvertBytesToFloats(byte[] byteArray) { int num = byteArray.Length / 2; float[] array = new float[num]; for (int i = 0; i < num; i++) { short num2 = (short)(byteArray[i * 2] | (byteArray[i * 2 + 1] << 8)); array[i] = (float)num2 / 32768f; } return array; } public static byte[] ConvertFloatsToBytes(float[] floatArray) { byte[] array = new byte[floatArray.Length * 2]; for (int i = 0; i < floatArray.Length; i++) { short num = (short)(floatArray[i] * 32767f); array[i * 2] = (byte)(num & 0xFF); array[i * 2 + 1] = (byte)((num >> 8) & 0xFF); } return array; } public static float[] ApplyFadeAndPadding(float[] samples, int sampleRate) { int num = (int)((float)sampleRate * 0.5f); int num2 = (int)((float)sampleRate * 0.02f); float[] array = new float[samples.Length + 2 * num]; for (int i = 0; i < num; i++) { array[i] = 0f; } for (int j = 0; j < samples.Length; j++) { float num3 = 1f; if (j < num2) { num3 = (float)j / (float)num2; } else if (j >= samples.Length - num2) { num3 = (float)(samples.Length - j) / (float)num2; } array[j + num] = samples[j] * num3; } for (int k = samples.Length + num; k < array.Length; k++) { array[k] = 0f; } return array; } } public sealed class DilatedPupilsDebuff : MonoBehaviour { private const float PupilSizeMultiplier = 3f; private const int Priority = 5; private const float SpringSpeedIn = 10f; private const float SpringDampIn = 0.5f; private const float SpringSpeedOut = 5f; private const float SpringDampOut = 0.3f; private const float RefreshInterval = 0.1f; private static readonly VepLogger LOG = VepLogger.Create<DilatedPupilsDebuff>(); private PlayerAvatar? playerAvatar; private float refreshTimer; public bool IsActive { get; private set; } private void Awake() { playerAvatar = ((Component)this).GetComponent<PlayerAvatar>(); } private void Update() { if (IsActive && !((Object)(object)playerAvatar == (Object)null)) { refreshTimer -= Time.deltaTime; if (refreshTimer <= 0f) { ApplyPupilEffect(); refreshTimer = 0.1f; } } } public void ApplyDebuff(bool dilated) { IsActive = dilated; LOG.Debug($"Applying dilated pupils debuff: {dilated}"); if (dilated) { refreshTimer = 0f; ApplyPupilEffect(); } } private void ApplyPupilEffect() { if (!((Object)(object)playerAvatar == (Object)null)) { playerAvatar.OverridePupilSize(3f, 5, 10f, 0.5f, 5f, 0.3f, 0.15f); } } } public sealed class DroidAvatarAnimationController : MonoBehaviour { private static readonly VepLogger LOG = VepLogger.Create<DroidAvatarAnimationController>(); private static readonly int MovingKey = Animator.StringToHash("Moving"); private static readonly int SprintingKey = Animator.StringToHash("Sprinting"); private static readonly int TurningKey = Animator.StringToHash("Turning"); private static readonly int GrabbingKey = Animator.StringToHash("Grabbing"); private static readonly int CrouchingKey = Animator.StringToHash("Crouching"); private static readonly int CrawlingKey = Animator.StringToHash("Crawling"); private static readonly int TumblingKey = Animator.StringToHash("Tumbling"); private static readonly int TumblingMoveKey = Animator.StringToHash("TumblingMove"); private static readonly int SlidingKey = Animator.StringToHash("Sliding"); private static readonly int JumpingKey = Animator.StringToHash("Jumping"); private static readonly int FallingKey = Animator.StringToHash("Falling"); private static readonly int StunKey = Animator.StringToHash("stun"); private static readonly int SprintingImpulseKey = Animator.StringToHash("SprintingImpulse"); private DroidController _droid; private Animator _animator; private bool _wasSprinting; public float AnimationSpeedMultiplier { get; set; } = 1f; public void Initialize(DroidController droid, Animator animator) { _droid = droid; _animator = animator; } private void Update() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_animator == (Object)null) && !((Object)(object)_droid == (Object)null)) { bool isInMovementState = _droid.IsInMovementState; bool flag = false; if (isInMovementState && (Object)(object)_droid.ControllerTransform != (Object)null && (Object)(object)_droid.Movement != (Object)null) { float num = Quaternion.Angle(_droid.ControllerTransform.rotation, _droid.Movement.TargetRotation); flag = num > 7f; } bool isSprinting = _droid.IsSprinting; bool flag2 = _droid.IsWalking || isSprinting; _animator.SetBool(MovingKey, flag2); _animator.SetBool(SprintingKey, isSprinting); _animator.SetBool(TurningKey, flag); _animator.SetBool(GrabbingKey, _droid.WantsGrabbing); _animator.SetBool(CrouchingKey, false); _animator.SetBool(CrawlingKey, false); _animator.SetBool(TumblingKey, false); _animator.SetBool(TumblingMoveKey, false); _animator.SetBool(SlidingKey, false); _animator.SetBool(JumpingKey, false); _animator.SetBool(FallingKey, _droid.IsFalling); _animator.SetBool(StunKey, false); if (isSprinting && !_wasSprinting) { _animator.SetTrigger(SprintingImpulseKey); } _wasSprinting = isSprinting; _animator.speed = AnimationSpeedMultiplier; } } } public sealed class DroidController : StateMachineComponent<DroidController, DroidController.StateId> { public enum StateId { Idle, Wander, Sprint, CheckMap, StalkApproach, StalkStare, StalkFlee } private sealed class IdleState : StateMachineBase<StateMachine, StateId>.StateBaseTimed { private const string ClipName = "LostDroidStand"; private const float IdleLoopStart = 0f; private const float IdleLoopEnd = 4.5f; private const float BlendDelay = 0.3f; private const float MinIdleTime = 2f; private const float MaxIdleTime = 4f; private const float PrecomputeRetryInterval = 1f; private const float CheckMapChance = 0.25f; private Animator _animator; private bool _animatorTakenOver; private AnimationClip? _clip; private float _currentTime; private float _duration; private float _precomputeTimer; private GameObject? _targetObject; public override void OnStateEnter(StateId previous) { base.OnStateEnter(previous); _duration = Random.Range(2f, 4f); _precomputeTimer = 0f; _animatorTakenOver = false; base.Machine.Owner.ResetPath(); base.Machine.Owner.HasChangedMovementState = false; _animator = base.Machine.Owner.Animator; if ((Object)(object)_animator != (Object)null) { _clip = DroidHelpers.GetAnimationClip(_animator, "LostDroidStand"); if ((Object)(object)_clip != (Object)null) { _targetObject = ((Component)_animator).gameObject; } } base.Machine.Owner.StartPrecomputeDestination(); } public override void OnStateUpdate() { base.OnStateUpdate(); if (!_animatorTakenOver && (Object)(object)_clip != (Object)null && (Object)(object)_targetObject != (Object)null && base.TimeElapsed >= 0.3f) { ((Behaviour)_animator).enabled = false; _currentTime = 0f; _animatorTakenOver = true; } if (_animatorTakenOver && (Object)(object)_clip != (Object)null && (Object)(object)_targetObject != (Object)null) { _currentTime += Time.deltaTime; if (_currentTime >= 4.5f) { _currentTime = 0f; } _clip.SampleAnimation(_targetObject, _currentTime); } if (!base.Machine.Owner.HasPrecomputedDestination) { _precomputeTimer -= Time.deltaTime; if (_precomputeTimer <= 0f) { base.Machine.Owner.StartPrecomputeDestination(); _precomputeTimer = 1f; } } if (!(base.TimeElapsed >= _duration)) { return; } float distanceToPlayer = base.Machine.Owner.GetDistanceToPlayer(); if (distanceToPlayer > 15f && Random.value < 0.1f) { base.Machine.NextStateStateId = StateId.StalkApproach; LOG.Debug($"IdleState: -> StalkApproach (dist={distanceToPlayer:F1})"); return; } float value = Random.value; if (value < 0.25f) { base.Machine.NextStateStateId = StateId.CheckMap; LOG.Debug($"IdleState: -> CheckMap (roll={value:F2} < {0.25f})"); } else { base.Machine.NextStateStateId = ((!(Random.value < 0.5f)) ? StateId.Wander : StateId.Sprint); LOG.Debug($"IdleState: -> {base.Machine.NextStateStateId}"); } } public override void OnStateExit(StateId next) { base.OnStateExit(next); if ((Object)(object)_animator != (Object)null) { ((Behaviour)_animator).enabled = true; } } } private abstract class MovementState : StateMachineBase<StateMachine, StateId>.StateBaseTimed { private const float SwitchCheckInterval = 1f; private const float SwitchChance = 0.2f; private const float PrecomputeForwardInterval = 0.5f; private float _duration; private float _nextPrecomputeForward; private float _nextSwitchCheck; protected abstract float MinDuration { get; } protected abstract float MaxDuration { get; } protected abstract float Speed { get; } protected abstract StateId SwitchTargetState { get; } protected abstract void SetMovementFlag(bool value); public override void OnStateEnter(StateId previous) { base.OnStateEnter(previous); _duration = Random.Range(MinDuration, MaxDuration); _nextSwitchCheck = 1f; _nextPrecomputeForward = 0.5f; if ((uint)(previous - 1) <= 1u) { base.Machine.Owner.TryExtendCurrentPath(); } else if (!base.Machine.Owner.TrySetRandomDestination()) { base.Machine.NextStateStateId = StateId.Idle; return; } base.Machine.Owner.SetSpeed(Speed); SetMovementFlag(value: true); base.Machine.Owner.StartPrecomputeForwardDestination(); } public override void OnStateExit(StateId next) { base.OnStateExit(next); SetMovementFlag(value: false); if ((uint)(next - 1) > 1u) { base.Machine.Owner.ClearPrecomputedForwardDestination(); } } public override void OnStateUpdate() { base.OnStateUpdate(); if (base.Machine.Owner.HasReachedDestination() || base.TimeElapsed >= _duration) { base.Machine.NextStateStateId = StateId.Idle; return; } _nextPrecomputeForward -= Time.deltaTime; if (_nextPrecomputeForward <= 0f) { _nextPrecomputeForward = 0.5f; base.Machine.Owner.StartPrecomputeForwardDestination(); } if (!base.Machine.Owner.HasChangedMovementState && base.TimeElapsed >= _nextSwitchCheck) { _nextSwitchCheck += 1f; if (Random.value < 0.2f) { base.Machine.Owner.HasChangedMovementState = true; base.Machine.NextStateStateId = SwitchTargetState; } } } } private sealed class WanderState : MovementState { protected override float MinDuration => 5f; protected override float MaxDuration => 15f; protected override float Speed => 2f; protected override StateId SwitchTargetState => StateId.Sprint; protected override void SetMovementFlag(bool value) { base.Machine.Owner.IsWalking = value; } } private sealed class SprintState : MovementState { protected override float MinDuration => 3f; protected override float MaxDuration => 8f; protected override float Speed => 5f; protected override StateId SwitchTargetState => StateId.Wander; protected override void SetMovementFlag(bool value) { base.Machine.Owner.IsSprinting = value; } } private sealed class CheckMapState : StateMachineBase<StateMachine, StateId>.StateBaseTimed { private enum Phase { Raise, Look, Lower } private const float RaiseDuration = 0.85f; private const float LowerDuration = 0.22f; private const float MinLookDuration = 0.5f; private const float MaxLookDuration = 3f; private Phase _currentPhase; private float _lookDuration; private float _phaseTimer; public override void OnStateEnter(StateId previous) { base.OnStateEnter(previous); base.Machine.Owner.IsWalking = false; base.Machine.Owner.IsSprinting = false; base.Machine.Owner.ResetPath(); base.Machine.Owner.ShowMapTool(visible: true); _lookDuration = Random.Range(0.5f, 3f); _currentPhase = Phase.Raise; _phaseTimer = 0f; LOG.Debug(string.Format("CheckMapState: ENTER - LookDuration={0:F1}s, mapToolClone={1}", _lookDuration, ((Object)(object)base.Machine.Owner._mapToolClone != (Object)null) ? "ok" : "NULL")); } public override void OnStateUpdate() { base.OnStateUpdate(); _phaseTimer += Time.deltaTime; switch (_currentPhase) { case Phase.Raise: if (_phaseTimer >= 0.85f) { _currentPhase = Phase.Look; _phaseTimer = 0f; LOG.Debug("CheckMapState: -> Look"); } break; case Phase.Look: if (_phaseTimer >= _lookDuration) { base.Machine.Owner.ShowMapTool(visible: false); _currentPhase = Phase.Lower; _phaseTimer = 0f; LOG.Debug("CheckMapState: -> Lower"); } break; case Phase.Lower: if (_phaseTimer >= 0.22f) { LOG.Debug("CheckMapState: Done, back to Idle"); base.Machine.NextStateStateId = StateId.Idle; } break; } } public override void OnStateExit(StateId next) { base.OnStateExit(next); LOG.Debug($"CheckMapState: EXIT -> {next}"); base.Machine.Owner.ShowMapTool(visible: false); } } private sealed class StalkApproachState : StateMachineBase<StateMachine, StateId>.StateBaseTimed { private const float MaxApproachTime = 30f; private const float DestinationUpdateInterval = 1f; private float _nextDestinationUpdate; public override void OnStateEnter(StateId previous) { base.OnStateEnter(previous); LOG.Debug("=== STALK: Approach started - running towards player ==="); base.Machine.Owner.IsSprinting = true; base.Machine.Owner.SetSpeed(5f); _nextDestinationUpdate = 0f; if (!base.Machine.Owner.TrySetDestinationToPlayer()) { LOG.Debug("STALK: Failed to set destination to player, aborting"); base.Machine.NextStateStateId = StateId.Idle; } } public override void OnStateUpdate() { base.OnStateUpdate(); float distanceToPlayer = base.Machine.Owner.GetDistanceToPlayer(); bool flag = base.Machine.Owner.HasReachedDestination(); if (base.TimeElapsed >= 30f) { LOG.Debug($"STALK: Approach timeout, dist={distanceToPlayer:F1}m, aborting"); base.Machine.NextStateStateId = StateId.Idle; return; } if (distanceToPlayer <= 5f) { LOG.Debug($"STALK: Arrived near player (distance={distanceToPlayer:F1}m)"); base.Machine.NextStateStateId = StateId.StalkStare; return; } if (flag) { LOG.Debug($"STALK: Reached nav destination but player still at {distanceToPlayer:F1}m, starting stare anyway"); base.Machine.NextStateStateId = StateId.StalkStare; return; } _nextDestinationUpdate -= Time.deltaTime; if (_nextDestinationUpdate <= 0f) { base.Machine.Owner.TrySetDestinationToPlayer(); _nextDestinationUpdate = 1f; } } public override void OnStateExit(StateId next) { base.OnStateExit(next); base.Machine.Owner.IsSprinting = false; base.Machine.Owner.ResetPath(); } } private sealed class StalkStareState : StateMachineBase<StateMachine, StateId>.StateBaseTimed { private const float MaxStareTime = 30f; private const float DestinationUpdateInterval = 0.5f; private float _fleeCountdown; private bool _hasBeenSeen; private float _nextDestinationUpdate; public override void OnStateEnter(StateId previous) { base.OnStateEnter(previous); LOG.Debug("=== STALK: Staring at player ==="); _hasBeenSeen = false; _fleeCountdown = 2f; _nextDestinationUpdate = 0f; base.Machine.Owner.IsWalking = true; base.Machine.Owner.IsSprinting = false; base.Machine.Owner.SetSpeed(2f); } public override void OnStateUpdate() { base.OnStateUpdate(); base.Machine.Owner.LookAtPlayer(); float distanceToPlayer = base.Machine.Owner.GetDistanceToPlayer(); if (base.Machine.Owner.IsPlayerLookingAtMe && !_hasBeenSeen) { _hasBeenSeen = true; LOG.Debug("=== STALK: Player sees me! Maintaining eye contact... ==="); base.Machine.Owner.IsWalking = false; base.Machine.Owner.ResetPath(); } if (_hasBeenSeen) { _fleeCountdown -= Time.deltaTime; if (_fleeCountdown <= 0f) { LOG.Debug("=== STALK: Eye contact over, fleeing! ==="); base.Machine.NextStateStateId = StateId.StalkFlee; return; } } else if (distanceToPlayer > 3f) { _nextDestinationUpdate -= Time.deltaTime; if (_nextDestinationUpdate <= 0f) { base.Machine.Owner.TrySetDestinationToPlayer(); _nextDestinationUpdate = 0.5f; } } else { base.Machine.Owner.ResetPath(); } if (base.TimeElapsed >= 30f) { LOG.Debug("STALK: Stare timeout, fleeing"); base.Machine.NextStateStateId = StateId.StalkFlee; } } public override void OnStateExit(StateId next) { base.OnStateExit(next); base.Machine.Owner.IsWalking = false; } } private sealed class StalkFleeState : StateMachineBase<StateMachine, StateId>.StateBaseTimed { private const float MaxFleeTime = 10f; public override void OnStateEnter(StateId previous) { base.OnStateEnter(previous); LOG.Debug("=== STALK: Fleeing from player! ==="); base.Machine.Owner.IsSprinting = true; base.Machine.Owner.SetSpeed(5f); if (!base.Machine.Owner.TrySetFleeDestination()) { LOG.Debug("STALK: Failed to set flee destination, going to idle"); base.Machine.NextStateStateId = StateId.Idle; } } public override void OnStateUpdate() { base.OnStateUpdate(); if (base.Machine.Owner.HasReachedDestination() || base.TimeElapsed >= 10f) { LOG.Debug("=== STALK: Flee complete, returning to idle ==="); base.Machine.NextStateStateId = StateId.Idle; } } public override void OnStateExit(StateId next) { base.OnStateExit(next); base.Machine.Owner.IsSprinting = false; } } internal const float WalkSpeed = 2f; internal const float SprintSpeed = 5f; internal const float SprintChance = 0.5f; internal const float StalkMinDistance = 15f; internal const float StalkChance = 0.1f; internal const float StalkArrivalDistance = 5f; internal const float StalkMinKeepDistance = 3f; internal const float StalkFleeDistance = 20f; internal const float StalkStareBeforeFlee = 2f; private static readonly VepLogger LOG = VepLogger.Create<DroidController>(); private readonly MaterialTrigger _materialTrigger = new MaterialTrigger(); private DroidFaceAnimationController _animController; private DroidRightArmPoseController? _armPoseController; private DroidLeftArmPoseController? _leftArmPoseController; private DroidAvatarAnimationController _avatarAnimController; private CharacterController _charController; private DroidEyeLookAtController? _eyeLookController; private GameObject? _mapToolClone; private DroidNameplate _nameplateController; private NavMeshAgent _navAgent; private Transform _rigidbodyTransform; private int _savedAgentTypeID; private int _savedAreaMask = -1; internal Animator Animator; public bool IsWalking { get; set; } public bool IsSprinting { get; set; } public bool HasChangedMovementState { get; set; } public bool WantsGrabbing { get; set; } public PlayerAvatar SourcePlayer { get; private set; } public Transform ControllerTransform { get; private set; } public DroidMovementController Movement { get; private set; } public bool IsPlayerLookingAtMe => _animController.IsPlayerLookingAtMe; public bool IsStalking { get { StateId currentStateStateId = Fsm.CurrentStateStateId; if ((uint)(currentStateStateId - 4) <= 2u) { return true; } return false; } } public bool IsInMovementState => DroidHelpers.IsMovementState(Fsm.CurrentStateStateId); public bool IsFalling { get { if ((Object)(object)Movement != (Object)null) { return Movement.IsFalling; } return false; } } public string DebugStateName => Fsm.CurrentStateStateId.ToString(); protected override StateId DefaultState => StateId.Idle; public bool HasPrecomputedDestination => Movement.HasPrecomputedDestination; protected override void Awake() { } protected override void Update() { if (!((Object)(object)Movement == (Object)null) && !((Object)(object)_navAgent == (Object)null) && _navAgent.isOnNavMesh && !((Object)(object)ControllerTransform == (Object)null)) { Fsm.Update(); StateId currentStateStateId = Fsm.CurrentStateStateId; bool isMovementState = DroidHelpers.IsMovementState(currentStateStateId); Movement.UpdateMovement(isMovementState); Movement.UpdateRotation(); Movement.SyncVisualsToController(((Object)(object)Animator != (Object)null) ? ((Component)Animator).transform : null); Movement.SyncNavAgentPosition(isMovementState); } } public void ShowMapTool(bool visible) { LOG.Debug(string.Format("ShowMapTool({0}) - clone={1}", visible, ((Object)(object)_mapToolClone != (Object)null) ? "ok" : "NULL")); if ((Object)(object)_mapToolClone != (Object)null) { _mapToolClone.SetActive(visible); } WantsGrabbing = visible; } public void PlayVoice(bool applyFilter = false) { if ((Object)(object)SourcePlayer == (Object)null || (Object)(object)ControllerTransform == (Object)null) { return; } PlayerAvatar instance = PlayerAvatar.instance; if (!((Object)(object)instance == (Object)null)) { WhispralMimics component = ((Component)instance).GetComponent<WhispralMimics>(); if ((Object)(object)component == (Object)null) { LOG.Warning("WhispralMimics not found on local player"); } else { component.PlayAudioAtTransform(ControllerTransform, SourcePlayer.playerName, applyFilter); } } } public void DebugTeleport(Vector3 worldPosition) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ControllerTransform == (Object)null)) { if ((Object)(object)_charController != (Object)null) { ((Collider)_charController).enabled = false; } ControllerTransform.position = worldPosition; if ((Object)(object)_charController != (Object)null) { ((Collider)_charController).enabled = true; } } } public bool TrySetRandomDestination() { if ((Object)(object)Movement != (Object)null) { return Movement.TrySetRandomDestination(); } return false; } public bool TryExtendCurrentPath() { if ((Object)(object)Movement != (Object)null) { return Movement.TryExtendCurrentPath(); } return false; } public void StartPrecomputeForwardDestination() { Movement?.StartPrecomputeForwardDestination(); } public void ClearPrecomputedForwardDestination() { Movement?.ClearPrecomputedForwardDestination(); } public void StartPrecomputeDestination() { Movement.StartPrecomputeDestination(); } public void ResetPath() { Movement.ResetPath(); } public bool HasReachedDestination() { return Movement.HasReachedDestination(); } public float GetDistanceToPlayer() { return Movement.GetDistanceToPlayer(); } public bool TrySetDestinationToPlayer() { if ((Object)(object)Movement != (Object)null) { return Movement.TrySetDestinationToPlayer(); } return false; } public bool TrySetFleeDestination() { if ((Object)(object)Movement != (Object)null) { return Movement.TrySetFleeDestination(20f); } return false; } public void LookAtPlayer() { Movement.LookAtPlayer(); } public void SetSpeed(float speed) { Movement.SetSpeed(speed); } public static DroidController? Create(PlayerAvatar sourcePlayer, Vector3 spawnPosition) { //IL_001d: 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) if (!DroidPrefabLoader.IsAvailable) { LOG.Warning("LostDroid prefab not available"); return null; } GameObject val = Object.Instantiate<GameObject>(DroidPrefabLoader.DroidPrefab, spawnPosition, Quaternion.identity); if ((Object)(object)val == (Object)null) { LOG.Error("Failed to instantiate LostDroid prefab"); return null; } ((Object)val).name = "HallucinationDroid_" + sourcePlayer.playerName; DroidController droidController = val.AddComponent<DroidController>(); droidController.Initialize(sourcePlayer); return droidController; } private void Initialize(PlayerAvatar sourcePlayer) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) SourcePlayer = sourcePlayer; FindCriticalTransforms(); SaveNavMeshSettings(); DisableEnemyComponents(); SetupNavigation(); SetupMovementController(); SetupClonedVisuals(); SetupAnimationController(); SetupNameplate(); InitializeFsm(); LOG.Info($"HallucinationDroid created for {sourcePlayer.playerName} at {ControllerTransform.position}"); } private void SetupMovementController() { if (!((Object)(object)ControllerTransform == (Object)null)) { Movement = ((Component)this).gameObject.AddComponent<DroidMovementController>(); Movement.Initialize(ControllerTransform, _rigidbodyTransform, _navAgent, _charController, _savedAreaMask); Movement.OnNavMeshError += HandleNavMeshError; } } private void HandleNavMeshError() { Fsm.NextStateStateId = StateId.Idle; } private void SetupAnimationController() { _animController = ((Component)this).gameObject.AddComponent<DroidFaceAnimationController>(); _animController.Initialize(this, ControllerTransform); if ((Object)(object)Animator != (Object)null) { _avatarAnimController = ((Component)this).gameObject.AddComponent<DroidAvatarAnimationController>(); _avatarAnimController.Initialize(this, Animator); } Transform val = (((Object)(object)Animator != (Object)null) ? ((Component)Animator).transform : ((Component)this).transform); Transform val2 = DroidHelpers.FindChildByName(val, "code_head_top"); if ((Object)(object)val2 != (Object)null) { _animController.SetupTalking(val2); } SetupEyelidsFromClone(val); } private void SetupEyelidsFromClone(Transform visualsRoot) { try { Transform val = DroidHelpers.FindChildByName(visualsRoot, "EYELIDS LEFT"); Transform val2 = DroidHelpers.FindChildByName(visualsRoot, "EYELIDS RIGHT"); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { LOG.Warning("EYELIDS LEFT/RIGHT not found on cloned visuals"); return; } FindEyelidRotations(val, out Transform upperX, out Transform upperZ, out Transform lowerX); FindEyelidRotations(val2, out Transform upperX2, out Transform upperZ2, out Transform lowerX2); ((Component)val).gameObject.SetActive(false); ((Component)val2).gameObject.SetActive(false); _animController.SetupEyelids(((Component)val).gameObject, ((Component)val2).gameObject, upperX, upperZ, lowerX, upperX2, upperZ2, lowerX2); } catch (Exception ex) { LOG.Error("SetupEyelidsFromClone: Exception - " + ex.Message + "\n" + ex.StackTrace); } } private static void FindEyelidRotations(Transform eyelidsRoot, out Transform? upperX, out Transform? upperZ, out Transform? lowerX) { upperX = null; upperZ = null; lowerX = null; Transform[] componentsInChildren = ((Component)eyelidsRoot).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { switch (((Object)val).name) { case "eyelid_upper": upperX = val; break; case "eyelid_upper_rotation": upperZ = val; break; case "eyelid_lower": lowerX = val; break; } } } private void LateUpdate() { _nameplateController.UpdateNameplate(); _animController.UpdateAnimations(); } private void OnDestroy() { if ((Object)(object)Movement != (Object)null) { Movement.OnNavMeshError -= HandleNavMeshError; } } public void PlayMediumFootstep() { //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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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) if (!((Object)(object)ControllerTransform == (Object)null)) { Vector3 val = ControllerTransform.position + Vector3.up * 0.1f; Materials.Instance.Impulse(val, Vector3.down, (SoundType)1, true, true, _materialTrigger, (HostType)1); } } private void SetupNameplate() { _nameplateController = ((Component)this).gameObject.AddComponent<DroidNameplate>(); _nameplateController.Initialize(ControllerTransform, SourcePlayer); } private void InitializeFsm() { Fsm = new StateMachineComponent<DroidController, StateId>.StateMachine(this, DefaultState); Fsm.AddState(StateId.Idle, new IdleState()); Fsm.AddState(StateId.Wander, new WanderState()); Fsm.AddState(StateId.Sprint, new SprintState()); Fsm.AddState(StateId.CheckMap, new CheckMapState()); Fsm.AddState(StateId.StalkApproach, new StalkApproachState()); Fsm.AddState(StateId.StalkStare, new StalkStareState()); Fsm.AddState(StateId.StalkFlee, new StalkFleeState()); } private void FindCriticalTransforms() { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) ControllerTransform = ((Component)this).transform.Find("Controller"); if ((Object)(object)ControllerTransform == (Object)null) { Transform[] componentsInChildren = ((Component)this).GetComponentsInChildren<Transform>(); foreach (Transform val in componentsInChildren) { if (((Object)val).name == "Controller") { ControllerTransform = val; break; } } } if ((Object)(object)ControllerTransform == (Object)null) { LOG.Warning("Controller transform not found, creating one"); GameObject val2 = new GameObject("Controller"); val2.transform.SetParent(((Component)this).transform); val2.transform.localPosition = Vector3.zero; ControllerTransform = val2.transform; } _rigidbodyTransform = ((Component)this).transform.Find("Rigidbody"); if ((Object)(object)_rigidbodyTransform == (Object)null) { Transform[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<Transform>(); foreach (Transform val3 in componentsInChildren2) { if (((Object)val3).name == "Rigidbody") { _rigidbodyTransform = val3; break; } } } if ((Object)(object)_rigidbodyTransform == (Object)null) { LOG.Warning("Rigidbody transform not found"); } } private void SaveNavMeshSettings() { NavMeshAgent val = null; if ((Object)(object)ControllerTransform != (Object)null) { val = ((Component)ControllerTransform).GetComponent<NavMeshAgent>(); } if ((Object)(object)val == (Object)null) { val = ((Component)this).GetComponentInChildren<NavMeshAgent>(); } if ((Object)(object)val != (Object)null) { _savedAgentTypeID = val.agentTypeID; _savedAreaMask = val.areaMask; } else { _savedAgentTypeID = -334000983; _savedAreaMask = 1; } } private void DisableEnemyComponents() { //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) List<Component> list = new List<Component>(); List<MonoBehaviour> list2 = new List<MonoBehaviour>(); Component[] componentsInChildren = ((Component)this).GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)this) { continue; } string name = ((object)val).GetType().Name; string text = ((object)val).GetType().FullName ?? ""; if (val is Transform || val is Animator || val is Renderer || val is MeshFilter || val is SkinnedMeshRenderer || val is NavMeshAgent) { continue; } if (text.Contains("Photon") || name.Contains("Photon") || text.Contains("Enemy") || text.Contains("LostDroid") || name.Contains("Enemy") || name.Contains("LostDroid")) { list.Add(val); continue; } MonoBehaviour val2 = (MonoBehaviour)(object)((val is MonoBehaviour) ? val : null); if (val2 != null) { list2.Add(val2); } } for (int j = 0; j < 5; j++) { int num = 0; foreach (Component item in list) { if ((Object)(object)item != (Object)null) { try { Object.DestroyImmediate((Object)(object)item); num++; } catch { } } } if (num == 0) { break; } } foreach (MonoBehaviour item2 in list2) { if ((Object)(object)item2 != (Object)null && (Object)(object)item2 != (Object)(object)this) { ((Behaviour)item2).enabled = false; } } Rigidbody[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<Rigidbody>(); foreach (Rigidbody val3 in componentsInChildren2) { val3.isKinematic = true; val3.useGravity = false; val3.velocity = Vector3.zero; val3.angularVelocity = Vector3.zero; } Collider[] componentsInChildren3 = ((Component)this).GetComponentsInChildren<Collider>(); foreach (Collider val4 in componentsInChildren3) { val4.isTrigger = true; } } private void SetupClonedVisuals() { if ((Object)(object)SourcePlayer == (Object)null) { LOG.Error("Cannot clone visuals: source player is null"); return; } Transform val = DroidVisualsClone.FindCube(((Component)this).transform); if ((Object)(object)val == (Object)null) { LOG.Error("Cannot clone visuals: MyDroid/Enable/Cube not found"); return; } DroidVisualsClone.SanitizeCube(val); Animator = ((Component)val).GetComponent<Animator>(); DroidVisualsClone.StripExistingRig(val); if (!DroidVisualsClone.TryCloneRig(SourcePlayer, val, out GameObject cloneRig)) { LOG.Warning("Failed to clone source player [RIG]"); } else if ((Object)(object)Animator != (Object)null && (Object)(object)cloneRig != (Object)null) { ((Behaviour)Animator).enabled = true; Animator.applyRootMotion = false; Animator.Rebind(); Animator.Update(0f); DroidVisualsClone.AttachRelay(Animator, this, cloneRig); DroidVisualsClone.TryCloneFlashlight(SourcePlayer, cloneRig); DroidVisualsClone.TryCloneMapTool(SourcePlayer, cloneRig, out _mapToolClone); SetupArmAndEyeMimics(cloneRig); } else if ((Object)(object)Animator == (Object)null) { LOG.Warning("Cube has no Animator after SanitizeCube"); } } private void SetupArmAndEyeMimics(GameObject cloneRig) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)SourcePlayer == (Object)null || (Object)(object)cloneRig == (Object)null) { return; } PlayerAvatarVisuals playerAvatarVisuals = SourcePlayer.playerAvatarVisuals; if ((Object)(object)playerAvatarVisuals == (Object)null) { LOG.Warning("SetupArmAndEyeMimics: SourcePlayer.playerAvatarVisuals is null"); return; } PlayerAvatarRightArm component = ((Component)playerAvatarVisuals).GetComponent<PlayerAvatarRightArm>(); if ((Object)(object)component != (Object)null && (Object)(object)component.rightArmTransform != (Object)null) { Transform val = DroidHelpers.FindChildByName(cloneRig.transform, ((Object)component.rightArmTransform).name); if ((Object)(object)val != (Object)null) { _armPoseController = ((Component)this).gameObject.AddComponent<DroidRightArmPoseController>(); _armPoseController.Initialize(this, val, component.basePose, component.mapPose, component.poseCurve, component.poseSpeed); LOG.Debug("DroidRightArmPoseController initialized on bone '" + ((Object)component.rightArmTransform).name + "'"); } else { LOG.Warning("SetupArmAndEyeMimics: bone '" + ((Object)component.rightArmTransform).name + "' not found in clone rig"); } } else { LOG.Warning("SetupArmAndEyeMimics: no PlayerAvatarRightArm on source player"); } PlayerAvatarLeftArm component2 = ((Component)playerAvatarVisuals).GetComponent<PlayerAvatarLeftArm>(); if ((Object)(object)component2 != (Object)null && (Object)(object)component2.leftArmTransform != (Object)null) { Transform val2 = DroidHelpers.FindChildByName(cloneRig.transform, ((Object)component2.leftArmTransform).name); if ((Object)(object)val2 != (Object)null) { _leftArmPoseController = ((Component)this).gameObject.AddComponent<DroidLeftArmPoseController>(); _leftArmPoseController.Initialize(val2, component2.basePose, component2.flashlightPose, component2.poseCurve, component2.poseSpeed); LOG.Debug("DroidLeftArmPoseController initialized on bone '" + ((Object)component2.leftArmTransform).name + "'"); } else { LOG.Warning("SetupArmAndEyeMimics: left arm bone '" + ((Object)component2.leftArmTransform).name + "' not found in clone rig"); } } else { LOG.Warning("SetupArmAndEyeMimics: no PlayerAvatarLeftArm on source player"); } PlayerEyes component3 = ((Component)playerAvatarVisuals).GetComponent<PlayerEyes>(); if ((Object)(object)component3 != (Object)null && (Object)(object)component3.eyeLeft != (Object)null && (Object)(object)component3.eyeRight != (Object)null) { Transform val3 = DroidHelpers.FindChildByName(cloneRig.transform, ((Object)component3.eyeLeft).name); Transform val4 = DroidHelpers.FindChildByName(cloneRig.transform, ((Object)component3.eyeRight).name); Transform val5 = (((Object)(object)_mapToolClone != (Object)null) ? DroidHelpers.FindChildByName(_mapToolClone.transform, "Player Look Target") : null); if ((Object)(object)val3 != (Object)null && (Object)(object)val4 != (Object)null && (Object)(object)val5 != (Object)null) { _eyeLookController = ((Component)this).gameObject.AddComponent<DroidEyeLookAtController>(); _eyeLookController.Initialize(this, val3, val4, val5); LOG.Debug("DroidEyeLookAtController initialized"); } else { LOG.Warning("SetupArmAndEyeMimics: eye bones or lookTarget missing " + $"(eyeL={(Object)(object)val3 != (Object)null}, eyeR={(Object)(object)val4 != (Object)null}, lookTarget={(Object)(object)val5 != (Object)null})"); } } else { LOG.Warning("SetupArmAndEyeMimics: no PlayerEyes on source player or eyeLeft/Right null"); } } private void SetupNavigation() { //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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_0085: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ControllerTransform == (Object)null) { LOG.Error("Cannot setup navigation: Controller is null"); return; } _navAgent = ((Component)ControllerTransform).GetComponent<NavMeshAgent>(); if ((Object)(object)_navAgent == (Object)null) { _navAgent = ((Component)ControllerTransform).gameObject.AddComponent<NavMeshAgent>(); } Vector3 position = ControllerTransform.position; Vector3 val = position; bool flag = false; NavMeshQueryFilter val2 = default(NavMeshQueryFilter); ((NavMeshQueryFilter)(ref val2)).agentTypeID = _savedAgentTypeID; ((NavMeshQueryFilter)(ref val2)).areaMask = _savedAreaMask; NavMeshQueryFilter val3 = val2; float[] array = new float[4] { 5f, 10f, 20f, 50f }; float[] array2 = array; NavMeshHit val4 = default(NavMeshHit); foreach (float num in array2) { if (NavMesh.SamplePosition(position, ref val4, num, val3)) { val = ((NavMeshHit)(ref val4)).position; flag = true; break; } } if (!flag) { LOG.Error($"No NavMesh found near {position}"); return; } ((Behaviour)_navAgent).enabled = false; _navAgent.agentTypeID = _savedAgentTypeID; _navAgent.areaMask = _savedAreaMask; _navAgent.baseOffset = 0f; _navAgent.speed = 2f; _navAgent.angularSpeed = 999f; _navAgent.acceleration = 15f; _navAgent.stoppingDistance = 0f; _navAgent.autoBraking = true; _navAgent.updatePosition = false; _navAgent.updateRotation = false; _navAgent.radius = 0.7f; _navAgent.height = 2f; _navAgent.obstacleAvoidanceType = (ObstacleAvoidanceType)4; _navAgent.avoidancePriority = 10; _navAgent.autoTraverseOffMeshLink = true; _navAgent.autoRepath = true; ControllerTransform.position = val; _navAgent.Warp(val); ((Behaviour)_navAgent).enabled = true; SetupCharacterController(); } private void SetupCharacterController() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ControllerTransform == (Object)null)) { CharacterController component = ((Component)ControllerTransform).GetComponent<CharacterController>(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } _charController = ((Component)ControllerTransform).gameObject.AddComponent<CharacterController>(); _charController.height = 2f; _charController.radius = 0.5f; _charController.center = new Vector3(0f, 1f, 0f); _charController.slopeLimit = 45f; _charController.stepOffset = 0.3f; _charController.skinWidth = 0.08f; _charController.minMoveDistance = 0.001f; } } } public sealed class DroidDebuff : MonoBehaviour { private static readonly VepLogger LOG = VepLogger.Create<DroidDebuff>(); private readonly Dictionary<PlayerAvatar, DroidController> _droids = new Dictionary<PlayerAvatar, DroidController>(); private WhispralDebuffManager _debuffManager; private InvisiblePlayerDebuff _invisiblePlayerDebuff; public bool IsActive { get; private set; } private void Update() { if (IsActive && !((Object)(object)_invisiblePlayerDebuff == (Object)null)) { SyncHallucinations(); } } private void OnDestroy() { DestroyAllHallucinations(); } public void ApplyDebuff(bool active, InvisiblePlayerDebuff sourcePlayerDebuff) { if (!SemiFunc.IsMultiplayer()) { LOG.Debug("Singleplayer mode detected, skipping hallucinations."); return; } IsActive = active; _invisiblePlayerDebuff = sourcePlayerDebuff; _debuffManager = ((Component)this).GetComponent<WhispralDebuffManager>(); if (active) { CreateHallucinations(); } else { DestroyAllHallucinations(); } } public DroidController? GetDroidByPlayerName(string playerName) { foreach (KeyValuePair<PlayerAvatar, DroidController> droid in _droids) { if (Object.op_Implicit((Object)(object)droid.Key) && Object.op_Implicit((Object)(object)droid.Value) && droid.Key.playerName == playerName) { return droid.Value; } } return null; } private void CreateHallucination(PlayerAvatar sourcePlayer) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) LOG.Debug("Creating hallucination for player " + sourcePlayer.playerName); if (!DroidPrefabLoader.IsAvailable) { LOG.Warning("LostDroid prefab not available - cannot create hallucination"); return; } Vector3? val = FindSpawnPosition(); if (!val.HasValue) { LOG.Warning("Could not find spawn position for hallucination of " + sourcePlayer.playerName); return; } DroidController droidController = DroidController.Create(sourcePlayer, val.Value); if ((Object)(object)droidController == (Object)null) { LOG.Warning("Hallucination for " + sourcePlayer.playerName + " failed to create"); return; } _droids[sourcePlayer] = droidController; LOG.Debug($"Hallucination created at {((Component)droidController).transform.position}"); } private void CreateHallucinations() { if ((Object)(object)_invisiblePlayerDebuff == (Object)null) { return; } LOG.Debug("Creating hallucinations for hidden players."); foreach (PlayerAvatar hiddenPlayer in _invisiblePlayerDebuff.HiddenPlayers) { if (Object.op_Implicit((Object)(object)hiddenPlayer) && !_droids.ContainsKey(hiddenPlayer)) { CreateHallucination(hiddenPlayer); } } } private void DestroyAllHallucinations() { LOG.Debug("Destroying all hallucinations."); foreach (KeyValuePair<PlayerAvatar, DroidController> droid in _droids) { if (Object.op_Implicit((Object)(object)droid.Value)) { Object.Destroy((Object)(object)((Component)droid.Value).gameObject); } } _droids.Clear(); } private void DestroyHallucination(PlayerAvatar? player) { if (!((Object)(object)player == (Object)null) && _droids.TryGetValue(player, out DroidController value)) { LOG.Debug("Destroying hallucination for player " + player.playerName); if (Object.op_Implicit((Object)(object)value)) { Object.Destroy((Object)(object)((Component)value).gameObject); } _droids.Remove(player); } } private Vector3? FindSpawnPosition() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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) //IL_0045: 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) if ((Object)(object)_debuffManager != (Object)null) { Vector3? precomputedSpawnPosition = _debuffManager.GetPrecomputedSpawnPosition(); if (precomputedSpawnPosition.HasValue) { LOG.Debug($"Using precomputed spawn position: {precomputedSpawnPosition.Value}"); return precomputedSpawnPosition.Value; } } LOG.Warning("No precomputed position available, falling back to sync search"); Vector3 position = ((Component)PlayerAvatar.instance).transform.position; LevelPoint val = SemiFunc.LevelPointGet(position, 5f, 15f) ?? SemiFunc.LevelPointGet(position, 0f, 999f); if (!Object.op_Implicit((Object)(object)val)) { LOG.Warning("No LevelPoint found for hallucination spawn"); return null; } return ((Component)val).transform.position; } private void SyncHallucinations() { IReadOnlyList<PlayerAvatar> hiddenPlayers = _invisiblePlayerDebuff.HiddenPlayers; foreach (PlayerAvatar item in hiddenPlayers) { if (Object.op_Implicit((Object)(object)item) && !_droids.ContainsKey(item)) { CreateHallucination(item); } } List<PlayerAvatar> list = new List<PlayerAvatar>(); foreach (KeyValuePair<PlayerAvatar, DroidController> droid in _droids) { if (!Object.op_Implicit((Object)(object)droid.Key) || !Object.op_Implicit((Object)(object)droid.Value) || !hiddenPlayers.Contains(droid.Key)) { list.Add(droid.Key); } } foreach (PlayerAvatar item2 in list) { DestroyHallucination(item2); } } } public sealed class DroidEyeLookAtController : MonoBehaviour { private const float ClampX = 50f; private const float ClampY = 30f; private const float ReturnSpeed = 8f; private static readonly VepLogger LOG = VepLogger.Create<DroidEyeLookAtController>(); private DroidController _droid; private Transform _eyeLeft; private Quaternion _eyeLeftRest; private Transform _eyeRight; private Quaternion _eyeRightRest; private Transform _lookTarget; private void LateUpdate() { //IL_005d: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00ad: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_droid == (Object)null) { return; } if (_droid.WantsGrabbing && (Object)(object)_lookTarget != (Object)null) { ApplyLookAt(_eyeLeft); ApplyLookAt(_eyeRight); return; } if ((Object)(object)_eyeLeft != (Object)null) { _eyeLeft.localRotation = Quaternion.Slerp(_eyeLeft.localRotation, _eyeLeftRest, 8f * Time.deltaTime); } if ((Object)(object)_eyeRight != (Object)null) { _eyeRight.localRotation = Quaternion.Slerp(_eyeRight.localRotation, _eyeRightRest, 8f * Time.deltaTime); } } public void Initialize(DroidController droid, Transform eyeLeft, Transform eyeRight, Transform lookTarget) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) _droid = droid; _eyeLeft = eyeLeft; _eyeRight = eyeRight; _lookTarget = lookTarget; if ((Object)(object)eyeLeft != (Object)null) { _eyeLeftRest = eyeLeft.localRotation; } if ((Object)(object)eyeRight != (Object)null) { _eyeRightRest = eyeRight.localRotation; } } private void ApplyLookAt(Transform eye) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0020: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)eye == (Object)null)) { Vector3 val = _lookTarget.position - eye.position; if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f)) { eye.rotation = Quaternion.LookRotation(val); Vector3 localEulerAngles = eye.localEulerAngles; localEulerAngles.y = ClampAngle(localEulerAngles.y, 30f); localEulerAngles.x = ClampAngle(localEulerAngles.x, 50f); eye.localEulerAngles = localEulerAngles; } } } private static float ClampAngle(float angle, float limit) { if (angle > limit && angle < 180f) { return limit; } if (angle < 360f - limit && angle > 180f) { return 360f - limit; } return angle; } } public sealed class DroidFaceAnimationController : MonoBehaviour { private static class AngryExpression { public const float UpperLidAngleLeft = -34f; public const float UpperLidAngleRight = 34f; public const float UpperLidClosed = 83f; public const float LowerLidClosed = -83f; } private const float LookAtAngle = 50f; private const float LookAtMaxDistance = 30f; private const float AngryEyesCooldown = 0.3f; private const float TalkRotationMaxAngle = 25f; private const int SampleDataLength = 256; private const float StalkStareBeforeFlee = 3.030303f; private static readonly VepLogger LOG = VepLogger.Create<DroidFaceAnimationController>(); private float _angryTimer; private Transform _controllerTransform; private DroidController _droidController; private GameObject? _eyelidsLeft; private GameObject? _eyelidsRight; private Transform? _headTopTransform; private bool _isAngry; private Transform? _leftLowerEyelidRotationX; private Transform? _leftUpperEyelidRotationX; private Transform? _leftUpperEyelidRotationZ; private Transform? _rightLowerEyelidRotationX; private Transform? _rightUpperEyelidRotationX; private Transform? _rightUpperEyelidRotationZ; private float[]? _sampleData; private AudioSource? _talkingAudioSource; private bool _wasPlayerLooking; public bool IsPlayerLookingAtMe { get; private set; } public void Initialize(DroidController droidController, Transform controllerTransform) { _droidController = droidController; _controllerTransform = droidController.ControllerTransform; } public void SetupEyelids(GameObject? eyelidsLeft, GameObject? eyelidsRight, Transform? leftUpperX, Transform? leftUpperZ, Transform? leftLowerX, Transform? rightUpperX, Transform? rightUpperZ, Transform? rightLowerX) { _eyelidsLeft = eyelidsLeft; _eyelidsRight = eyelidsRight; _leftUpperEyelidRotationX = leftUpperX; _leftUpperEyelidRotationZ = leftUpperZ; _leftLowerEyelidRotationX = leftLowerX; _rightUpperEyelidRotationX = rightUpperX; _rightUpperEyelidRotationZ = rightUpperZ; _rightLowerEyelidRotationX = rightLowerX; } public void SetupTalking(Transform headTopTransform) { _headTopTransform = headTopTransform; } public void UpdateAnimations() { UpdatePlayerLookDetection(); UpdateAngryEyes(); UpdateTalkingAnimation(); } private void UpdateAngryEyes() { //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_eyelidsLeft == (Object)null || (Object)(object)_eyelidsRight == (Object)null || (Object)(object)_leftUpperEyelidRotationX == (Object)null || (Object)(object)_rightUpperEyelidRotationX == (Object)null) { return; } if (IsPlayerLookingAtMe && !_wasPlayerLooking) { _angryTimer = (_droidController.IsStalking ? 3.030303f : 0.3f); } _wasPlayerLooking = IsPlayerLookingAtMe; if (IsPlayerLookingAtMe) { _angryTimer -= Time.deltaTime; if (_angryTimer <= 0f) { _isAngry = false; } } else { _isAngry = true; } if (_isAngry && !_eyelidsLeft.activeSelf) { _eyelidsLeft.SetActive(true); _eyelidsRight.SetActive(true); } float num = (_isAngry ? 83f : 0f); float num2 = (_isAngry ? (-83f) : 0f); float num3 = (_isAngry ? (-34f) : 0f); float num4 = (_isAngry ? 34f : 0f); float num5 = Time.deltaTime * 8f; _leftUpperEyelidRotationX.localRotation = Quaternion.Slerp(_leftUpperEyelidRotationX.localRotation, Quaternion.Euler(num, 0f, 0f), num5); _rightUpperEyelidRotationX.localRotation = Quaternion.Slerp(_rightUpperEyelidRotationX.localRotation, Quaternion.Euler(num, 0f, 0f), num5); if ((Object)(object)_leftUpperEyelidRotationZ != (Object)null) { _leftUpperEyelidRotationZ.localRotation = Quaternion.Slerp(_leftUpperEyelidRotationZ.localRotation, Quaternion.Euler(0f, 0f, num3), num5); } if ((Object)(object)_rightUpperEyelidRotationZ != (Object)null) { _rightUpperEyelidRotationZ.localRotation = Quaternion.Slerp(_rightUpperEyelidRotationZ.localRotation, Quaternion.Euler(0f, 0f, num4), num5); } if ((Object)(object)_leftLowerEyelidRotationX != (Object)null) { _leftLowerEyelidRotationX.localRotation = Quaternion.Slerp(_leftLowerEyelidRotationX.localRotation, Quaternion.Euler(num2, 0f, 0f), num5); } if ((Object)(object)_rightLowerEyelidRotationX != (Object)null) { _rightLowerEyelidRotationX.localRotation = Quaternion.Slerp(_rightLowerEyelidRotationX.localRotation, Quaternion.Euler(num2, 0f, 0f), num5); } if (!_isAngry && _eyelidsLeft.activeSelf) { float num6 = Mathf.Abs(_leftUpperEyelidRotationX.localEulerAngles.x); if ((num6 < 10f || num6 > 350f) ? true : false) { _eyelidsLeft.SetActive(false); _eyelidsRight.SetActive(false); } } } private void UpdatePlayerLookDetection() { //IL_002b: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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) //IL_0066: 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_006f: 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_007c: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if ((Object)(object)main == (Object)null || (Object)(object)_controllerTransform == (Object)null) { IsPlayerLookingAtMe = false; return; } Vector3 val = _controllerTransform.position + Vector3.up; Vector3 position = ((Component)main).transform.position; float num = Vector3.Distance(position, val); if (num > 30f) { IsPlayerLookingAtMe = false; return; } Vector3 val2 = val - position; Vector3 normalized = ((Vector3)(ref val2)).normalized; float num2 = Vector3.Dot(((Component)main).transform.forward, normalized); float num3 = Mathf.Cos(0.87266463f); IsPlayerLookingAtMe = num2 >= num3; } private void UpdateTalkingAnimation() { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_
WebRtcVadSharp.dll
Decompiled a month agousing System; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using WebRtcVadSharp.WebRtc; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("Jay Miller")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2020 Jay Miller")] [assembly: AssemblyDescription(".NET Standard interface for the WebRTC voice activity detection (VAD) component.")] [assembly: AssemblyFileVersion("1.3.2.0")] [assembly: AssemblyInformationalVersion("1.3.2+fdd4f4ef0570520f9031f994d8b587fba0e4f31c")] [assembly: AssemblyProduct("WebRtcVadSharp")] [assembly: AssemblyTitle("WebRtcVadSharp")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ladenedge/WebRtcVadSharp.git")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.2.0")] [module: UnverifiableCode] namespace WebRtcVadSharp { public enum FrameLength : long { Is10ms = 10L, Is20ms = 20L, Is30ms = 30L } public interface IWebRtcVad { SampleRate SampleRate { get; set; } FrameLength FrameLength { get; set; } OperatingMode OperatingMode { get; set; } bool HasSpeech(byte[] audioFrame); bool HasSpeech(byte[] audioFrame, SampleRate sampleRate, FrameLength frameLength); bool HasSpeech(short[] audioFrame); bool HasSpeech(short[] audioFrame, SampleRate sampleRate, FrameLength frameLength); bool HasSpeech(IntPtr audioFrame); bool HasSpeech(IntPtr audioFrame, SampleRate sampleRate, FrameLength frameLength); } public enum OperatingMode { HighQuality, LowBitrate, Aggressive, VeryAggressive } public enum SampleRate { Is8kHz = 8000, Is16kHz = 16000, Is32kHz = 32000, Is48kHz = 48000 } public class WebRtcVad : IWebRtcVad, IDisposable { private readonly IWebRtcDll _webrtc; private IntPtr _handle; private SampleRate _rate; private FrameLength _length; private OperatingMode _mode; private bool hasDisposed; public SampleRate SampleRate { get { return _rate; } set { ValidateRateAndFrameLength(value, FrameLength); _rate = value; } } public FrameLength FrameLength { get { return _length; } set { ValidateRateAndFrameLength(SampleRate, value); _length = value; } } public OperatingMode OperatingMode { get { return _mode; } set { int result = _webrtc.SetMode(_handle, (int)value); ValidateSetMode(result, _handle, value); _mode = value; } } public WebRtcVad() : this(new WebRtcDll()) { } public WebRtcVad(IWebRtcDll library) { _webrtc = library ?? throw new ArgumentNullException("library"); _mode = OperatingMode.HighQuality; _rate = SampleRate.Is8kHz; _length = FrameLength.Is10ms; try { _handle = _webrtc.Create(); int result = _webrtc.Init(_handle); ValidateInitialize(result, _handle); } catch (DllNotFoundException inner) { string currentDirectory = Directory.GetCurrentDirectory(); throw new DllNotFoundException("Unable to load DLL 'WebRtcVad.dll' or a dependency. Be sure it exists in '" + currentDirectory + "' or elsewhere in the DLL search path.", inner); } } public bool HasSpeech(byte[] audioFrame) { return HasSpeech(audioFrame, _rate, _length); } public bool HasSpeech(byte[] audioFrame, SampleRate sampleRate, FrameLength frameLength) { ulong frame_length = CalculateSamples(sampleRate, frameLength); int result = _webrtc.Process(_handle, (int)sampleRate, audioFrame, frame_length); return ValidateProcess(result, _handle, sampleRate, audioFrame, frameLength); } public bool HasSpeech(short[] audioFrame) { return HasSpeech(audioFrame, _rate, _length); } public bool HasSpeech(short[] audioFrame, SampleRate sampleRate, FrameLength frameLength) { ulong frame_length = CalculateSamples(sampleRate, frameLength); int result = _webrtc.Process(_handle, (int)sampleRate, audioFrame, frame_length); return ValidateProcess(result, _handle, sampleRate, audioFrame, frameLength); } public bool HasSpeech(IntPtr audioFrame) { return HasSpeech(audioFrame, _rate, _length); } public bool HasSpeech(IntPtr audioFrame, SampleRate sampleRate, FrameLength frameLength) { ulong frame_length = CalculateSamples(sampleRate, frameLength); int result = _webrtc.Process(_handle, (int)sampleRate, audioFrame, frame_length); return ValidateProcess(result, _handle, sampleRate, audioFrame, frameLength); } private ulong CalculateSamples(SampleRate rate, FrameLength length) { return (ulong)rate / 1000uL * (ulong)length; } private void ValidateInitialize(int result, IntPtr handle) { if (result == 0) { return; } ValidateHandle(handle); throw new InvalidOperationException($"Could not initialize WebRTC [Init({handle}) = {result}]."); } private void ValidateSetMode(int result, IntPtr handle, OperatingMode mode) { if (result == 0) { return; } ValidateHandle(handle); ValidateEnum(mode); throw new InvalidOperationException($"Could not set operating mode [SetMode({handle}, {mode}) = {result}]."); } private void ValidateRateAndFrameLength(SampleRate sampleRate, FrameLength frameLength) { ulong num = CalculateSamples(sampleRate, frameLength); int num2 = _webrtc.ValidRateAndFrameLength((int)sampleRate, num); if (num2 == 0) { return; } ValidateEnum(sampleRate); ValidateEnum(frameLength); throw new InvalidOperationException($"Could not validate rate/length [ValidRateAndFrameLength({(int)sampleRate}, {num}) = {num2}]."); } private bool ValidateProcess(int result, IntPtr handle, SampleRate sampleRate, byte[] audioFrame, FrameLength frameLength) { return ValidateProcess(result, handle, sampleRate, $"byte[{audioFrame.Length}]", frameLength); } private bool ValidateProcess(int result, IntPtr handle, SampleRate sampleRate, short[] audioFrame, FrameLength frameLength) { return ValidateProcess(result, handle, sampleRate, $"short[{audioFrame.Length}]", frameLength); } private bool ValidateProcess(int result, IntPtr handle, SampleRate sampleRate, IntPtr audioFrame, FrameLength frameLength) { return ValidateProcess(result, handle, sampleRate, "0x" + audioFrame.ToInt64().ToString("X8"), frameLength); } private bool ValidateProcess(int result, IntPtr handle, SampleRate sampleRate, string frameDescription, FrameLength frameLength) { switch (result) { case 0: return false; case 1: return true; default: ValidateHandle(handle); ValidateEnum(sampleRate); ValidateEnum(frameLength); throw new InvalidOperationException($"Could not process audio frame [Process({handle}, {sampleRate}, <{frameDescription}>, {frameLength}) = {result}]."); } } private void ValidateHandle(IntPtr handle) { if (handle == IntPtr.Zero) { throw new ObjectDisposedException("Invalid WebRTC handle; object appears to have been disposed."); } } private void ValidateEnum<T>(T val) { if (Enum.IsDefined(typeof(T), val)) { return; } string arg = string.Join(", ", Enum.GetValues(typeof(T)).OfType<T>()); throw new ArgumentException($"{val} was not a valid {typeof(T).Name}. Valid values are [{arg}]."); } protected virtual void Dispose(bool disposing) { if (hasDisposed) { return; } if (disposing) { if (_webrtc != null) { _webrtc.Free(_handle); } _handle = IntPtr.Zero; } hasDisposed = true; } public void Dispose() { Dispose(disposing: true); } } } namespace WebRtcVadSharp.WebRtc { public interface IWebRtcDll { IntPtr Create(); int Init(IntPtr handle); int SetMode(IntPtr self, int mode); int ValidRateAndFrameLength(int rate, ulong frame_length); int Process(IntPtr handle, int fs, byte[] audio_frame, ulong frame_length); int Process(IntPtr handle, int fs, short[] audio_frame, ulong frame_length); int Process(IntPtr handle, int fs, IntPtr audio_frame, ulong frame_length); void Free(IntPtr handle); } public class WebRtcDll : IWebRtcDll { private class NativeMethods { [DllImport("webrtcvad")] public static extern IntPtr Vad_Create(); [DllImport("webrtcvad")] public static extern int Vad_Init(IntPtr handle); [DllImport("webrtcvad")] public static extern int Vad_SetMode(IntPtr self, int mode); [DllImport("webrtcvad")] public static extern int Vad_ValidRateAndFrameLength(int rate, UIntPtr frame_length); [DllImport("webrtcvad")] public static extern int Vad_Process(IntPtr handle, int fs, IntPtr audio_frame, UIntPtr frame_length); [DllImport("webrtcvad")] public static extern void Vad_Free(IntPtr handle); } public IntPtr Create() { return NativeMethods.Vad_Create(); } public int Init(IntPtr handle) { return NativeMethods.Vad_Init(handle); } public unsafe int Process(IntPtr handle, int fs, byte[] audio_frame, ulong frame_length) { UIntPtr frame_length2 = new UIntPtr(frame_length); fixed (byte* ptr = audio_frame) { return NativeMethods.Vad_Process(handle, fs, (IntPtr)ptr, frame_length2); } } public unsafe int Process(IntPtr handle, int fs, short[] audio_frame, ulong frame_length) { UIntPtr frame_length2 = new UIntPtr(frame_length); fixed (short* ptr = audio_frame) { return NativeMethods.Vad_Process(handle, fs, (IntPtr)ptr, frame_length2); } } public int Process(IntPtr handle, int fs, IntPtr audio_frame, ulong frame_length) { UIntPtr frame_length2 = new UIntPtr(frame_length); return NativeMethods.Vad_Process(handle, fs, audio_frame, frame_length2); } public int SetMode(IntPtr self, int mode) { return NativeMethods.Vad_SetMode(self, mode); } public int ValidRateAndFrameLength(int rate, ulong frame_length) { UIntPtr frame_length2 = new UIntPtr(frame_length); return NativeMethods.Vad_ValidRateAndFrameLength(rate, frame_length2); } public void Free(IntPtr handle) { NativeMethods.Vad_Free(handle); } } }