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 Mimics.patches;
using Photon.Pun;
using Photon.Realtime;
using Photon.Voice;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Mimics")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.1.6.0")]
[assembly: AssemblyInformationalVersion("1.1.6")]
[assembly: AssemblyProduct("My first plugin")]
[assembly: AssemblyTitle("Mimics")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.6.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 Mimics
{
public class Mimics : MonoBehaviour
{
[CompilerGenerated]
private sealed class <DestroyAfterDelay>d__34 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public AudioSource audioSource;
public float delay;
public Mimics <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <DestroyAfterDelay>d__34(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(delay);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
Object.Destroy((Object)(object)audioSource);
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <RecordAtRandomIntervals>d__16 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public Mimics <>4__this;
private int <clipCount>5__1;
private float <randomDelay>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <RecordAtRandomIntervals>d__16(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
goto IL_00e0;
case 1:
<>1__state = -1;
break;
case 2:
{
<>1__state = -1;
<>4__this.StartRecording();
goto IL_00e0;
}
IL_00e0:
<clipCount>5__1 = Directory.GetFiles(<>4__this.audioFolderPath, "*.wav").Length;
if (<clipCount>5__1 >= <>4__this.maxClipCount)
{
<>2__current = <>4__this.ClearAudioFolderAsync().AsCoroutine();
<>1__state = 1;
return true;
}
break;
}
<randomDelay>5__2 = Random.Range(Plugin.configMinDelay.Value, Plugin.configMaxDelay.Value);
<>2__current = (object)new WaitForSeconds(<randomDelay>5__2);
<>1__state = 2;
return true;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <WaitForVoiceChat>d__15 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public PlayerAvatar playerAvatar;
public Mimics <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <WaitForVoiceChat>d__15(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Expected O, but got Unknown
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>4__this.playerVoiceChat = (PlayerVoiceChat)<>4__this.voiceChatField.GetValue(playerAvatar);
break;
case 1:
<>1__state = -1;
<>4__this.playerVoiceChat = (PlayerVoiceChat)<>4__this.voiceChatField.GetValue(playerAvatar);
break;
}
if ((Object)(object)<>4__this.playerVoiceChat == (Object)null)
{
<>2__current = null;
<>1__state = 1;
return true;
}
log.LogInfo((object)"PlayerVoiceChat successfully initialized.");
if (<>4__this.photonView.IsMine && SemiFunc.RunIsLevel())
{
<>4__this.audioFolderPath = Path.Combine(Application.dataPath, "AudioFiles");
Directory.CreateDirectory(<>4__this.audioFolderPath);
((MonoBehaviour)<>4__this).StartCoroutine(<>4__this.RecordAtRandomIntervals());
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private static readonly ManualLogSource log = Logger.CreateLogSource("Mimics");
public PhotonView photonView;
private PlayerVoiceChat playerVoiceChat;
private FieldInfo voiceChatField;
private FieldInfo recorderField;
private int sampleRate;
private float[] audioBuffer;
private int bufferPosition = 0;
public bool isRecording = false;
private bool capturingSpeech = false;
private string audioFolderPath;
private int maxClipCount = 10;
private bool fileSaved = false;
private Dictionary<string, bool> filter;
private HashSet<int> sentChunks = new HashSet<int>();
private List<byte[]> receivedChunks = new List<byte[]>();
private int expectedChunkCount = 0;
private void Awake()
{
photonView = ((Component)this).GetComponent<PhotonView>();
if ((Object)(object)photonView == (Object)null)
{
log.LogError((object)"PhotonView not found on Mimics.");
return;
}
PlayerAvatar component = ((Component)this).GetComponent<PlayerAvatar>();
if ((Object)(object)component == (Object)null)
{
log.LogError((object)"PlayerAvatar not found on Mimics.");
return;
}
voiceChatField = typeof(PlayerAvatar).GetField("voiceChat", BindingFlags.Instance | BindingFlags.NonPublic);
if (voiceChatField == null)
{
log.LogError((object)"Could not find 'voiceChat' field in PlayerAvatar.");
return;
}
recorderField = typeof(PlayerVoiceChat).GetField("recorder", BindingFlags.Instance | BindingFlags.NonPublic);
if (recorderField == null)
{
log.LogError((object)"Could not find 'recorder' field in PlayerVoiceChat.");
return;
}
sampleRate = Plugin.configSamplingRate.Value;
if (Plugin.configFilterEnabled.Value)
{
filter = new Dictionary<string, bool>();
SetEnemyFilter();
}
else
{
log.LogInfo((object)"Filter not enabled. All enemies (custom included) will mimic voices.");
}
((MonoBehaviour)this).StartCoroutine(WaitForVoiceChat(component));
}
[IteratorStateMachine(typeof(<WaitForVoiceChat>d__15))]
private IEnumerator WaitForVoiceChat(PlayerAvatar playerAvatar)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <WaitForVoiceChat>d__15(0)
{
<>4__this = this,
playerAvatar = playerAvatar
};
}
[IteratorStateMachine(typeof(<RecordAtRandomIntervals>d__16))]
private IEnumerator RecordAtRandomIntervals()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <RecordAtRandomIntervals>d__16(0)
{
<>4__this = this
};
}
private void StartRecording()
{
if (!isRecording)
{
audioBuffer = new float[sampleRate * 3];
bufferPosition = 0;
isRecording = true;
capturingSpeech = false;
fileSaved = false;
log.LogInfo((object)"Recording started.");
}
}
public void ProcessVoiceData(short[] voiceData)
{
if (!isRecording || !photonView.IsMine)
{
return;
}
if ((bool)typeof(PlayerVoiceChat).GetField("isTalking", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(playerVoiceChat) && !capturingSpeech)
{
capturingSpeech = true;
bufferPosition = 0;
log.LogInfo((object)"Speech detected, capturing audio.");
}
if (capturingSpeech)
{
int num = Mathf.Min(voiceData.Length, audioBuffer.Length - bufferPosition);
for (int i = 0; i < num; i++)
{
audioBuffer[bufferPosition + i] = (float)voiceData[i] / 32768f;
}
bufferPosition += num;
if (bufferPosition >= audioBuffer.Length && !fileSaved)
{
isRecording = false;
capturingSpeech = false;
fileSaved = true;
log.LogInfo((object)"Buffer full, saving audio.");
SaveAudioToFileAsync(audioBuffer);
}
}
}
private async Task SaveAudioToFileAsync(float[] audioData)
{
string filePath = Path.Combine(audioFolderPath, $"audio_{Guid.NewGuid()}.wav");
byte[] audioBytes = ConvertFloatArrayToByteArray(audioData);
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
using BinaryWriter writer = new BinaryWriter(fileStream);
WriteWavHeader(writer, audioData.Length, sampleRate);
writer.Write(audioBytes);
}
log.LogInfo((object)("Audio saved to: " + filePath));
await PlayRandomAudioFile();
}
private void WriteWavHeader(BinaryWriter writer, int sampleCount, int sampleRate)
{
writer.Write("RIFF".ToCharArray());
writer.Write(36 + sampleCount * 2);
writer.Write("WAVE".ToCharArray());
writer.Write("fmt ".ToCharArray());
writer.Write(16);
writer.Write((short)1);
writer.Write((short)1);
writer.Write(sampleRate);
writer.Write(sampleRate * 2);
writer.Write((short)2);
writer.Write((short)16);
writer.Write("data".ToCharArray());
writer.Write(sampleCount * 2);
}
private byte[] ConvertFloatArrayToByteArray(float[] audioData)
{
byte[] array = new byte[audioData.Length * 2];
for (int i = 0; i < audioData.Length; i++)
{
short value = (short)(audioData[i] * 32767f);
BitConverter.GetBytes(value).CopyTo(array, i * 2);
}
return array;
}
private async Task PlayRandomAudioFile()
{
string[] files = Directory.GetFiles(audioFolderPath, "*.wav");
if (files.Length != 0)
{
string selectedFile = files[Random.Range(0, files.Length)];
await SendAudioInChunksAsync(await File.ReadAllBytesAsync(selectedFile));
}
}
private async Task SendAudioInChunksAsync(byte[] audioData)
{
List<byte[]> chunks = ChunkAudioData(audioData, 8192);
sentChunks.Clear();
for (int i = 0; i < chunks.Count; i++)
{
if (!PhotonNetwork.IsConnectedAndReady)
{
log.LogWarning((object)"Photon disconnected, aborting send.");
return;
}
bool applyVoiceFilter = Random.value > 0.9f;
if (Plugin.configHearYourself.Value)
{
photonView.RPC("ReceiveAudioChunk", (RpcTarget)0, new object[5]
{
chunks[i],
i,
chunks.Count,
applyVoiceFilter,
sampleRate
});
}
else
{
photonView.RPC("ReceiveAudioChunk", (RpcTarget)1, new object[5]
{
chunks[i],
i,
chunks.Count,
applyVoiceFilter,
sampleRate
});
}
sentChunks.Add(i);
await Task.Delay(125);
}
log.LogInfo((object)"All chunks sent.");
}
[PunRPC]
public void ReceiveAudioChunk(byte[] chunk, int chunkIndex, int totalChunks, bool applyFilter, int senderSampleRate)
{
if (chunkIndex == 0)
{
receivedChunks.Clear();
expectedChunkCount = totalChunks;
log.LogInfo((object)$"New audio transmission started, expecting {totalChunks} chunks at {senderSampleRate} Hz.");
}
if (chunkIndex >= expectedChunkCount)
{
log.LogWarning((object)$"Received chunk index {chunkIndex} exceeds expected {expectedChunkCount}.");
return;
}
if (chunkIndex >= receivedChunks.Count)
{
receivedChunks.AddRange(Enumerable.Repeat<byte[]>(null, chunkIndex - receivedChunks.Count + 1));
}
receivedChunks[chunkIndex] = chunk;
if (receivedChunks.Count >= expectedChunkCount && receivedChunks.All((byte[] c) => c != null))
{
log.LogInfo((object)"All chunks received, playing audio.");
byte[] audioData = CombineChunks(receivedChunks);
PlayReceivedAudio(audioData, applyFilter, senderSampleRate);
receivedChunks.Clear();
expectedChunkCount = 0;
}
}
private byte[] CombineChunks(List<byte[]> chunks)
{
int num = chunks.Sum((byte[] chunk) => chunk.Length);
byte[] array = new byte[num];
int num2 = 0;
foreach (byte[] chunk in chunks)
{
Array.Copy(chunk, 0, array, num2, chunk.Length);
num2 += chunk.Length;
}
return array;
}
private void PlayReceivedAudio(byte[] audioData, bool applyVoiceFilter, int senderSampleRate)
{
if (applyVoiceFilter)
{
log.LogInfo((object)"Expecting filter.");
}
float[] array = ConvertByteArrayToFloatArray(audioData, applyVoiceFilter, senderSampleRate);
AudioClip val = AudioClip.Create("ReceivedClip", array.Length, 1, senderSampleRate, false);
val.SetData(array, 0);
foreach (GameObject item in from e in GetEnemiesList()
where (Object)(object)e != (Object)null && !((Object)e).name.Contains("Gnome")
select e)
{
if (Plugin.configFilterEnabled.Value)
{
string text = ((Object)item).name.Replace("(Clone)", "");
if (!filter.TryGetValue(text, out var value) || !value)
{
log.LogInfo((object)("Skipped " + text + ": disabled in config"));
continue;
}
}
Transform obj = item.transform.Find("Enable/Controller");
GameObject val2 = ((obj != null) ? ((Component)obj).gameObject : null);
if (!((Object)(object)val2 == (Object)null))
{
AudioSource val3 = val2.GetComponent<AudioSource>() ?? val2.AddComponent<AudioSource>();
val3.clip = val;
val3.volume = Plugin.configVoiceVolume.Value;
val3.spatialBlend = 1f;
val3.dopplerLevel = 0.5f;
val3.minDistance = 1f;
val3.maxDistance = 20f;
val3.rolloffMode = (AudioRolloffMode)1;
val3.outputAudioMixerGroup = playerVoiceChat.mixerMicrophoneSound;
val3.Play();
((MonoBehaviour)this).StartCoroutine(DestroyAfterDelay(val3, val.length + 0.1f));
}
}
}
private void SetEnemyFilter()
{
filter.Clear();
if (filter == null)
{
log.LogError((object)"Enemy filter is null");
}
foreach (KeyValuePair<string, ConfigEntry<bool>> enemyConfigEntry in Plugin.enemyConfigEntries)
{
filter.Add(enemyConfigEntry.Key ?? "", enemyConfigEntry.Value.Value);
}
log.LogInfo((object)"Config loaded and filter set.");
}
private List<GameObject> GetEnemiesList()
{
GameObject obj = GameObject.Find("Level Generator");
Transform val = ((obj != null) ? obj.transform.Find("Enemies") : null);
return ((Object)(object)val != (Object)null) ? (from Transform t in (IEnumerable)val
select ((Component)t).gameObject).ToList() : new List<GameObject>();
}
private float[] ConvertByteArrayToFloatArray(byte[] byteArray, bool applyVoiceFilter, int senderSampleRate)
{
int num = (int)((float)senderSampleRate * 0.5f);
int num2 = byteArray.Length / 2;
int num3 = (int)((float)senderSampleRate * 0.02f);
float[] array = new float[num2];
for (int i = 0; i < num2; i++)
{
array[i] = (float)BitConverter.ToInt16(byteArray, i * 2) / 32768f;
}
array = ApplyLowPassFilter(array, 4500f);
if (applyVoiceFilter)
{
int num4 = Random.Range(0, 3);
if (num4 == 0)
{
array = ApplyPitchShift(array, 0.5f);
}
if (num4 == 1)
{
array = ApplyPitchShift(array, 1.2f);
}
if (num4 == 2)
{
array = ApplyAlienFilter(array);
}
}
float[] array2 = new float[array.Length + 2 * num];
for (int j = 0; j < num; j++)
{
array2[j] = 0f;
}
for (int k = 0; k < array.Length; k++)
{
float num5 = array[k];
float num6 = 1f;
if (k < num3)
{
num6 = (float)k / (float)num3;
}
else if (k >= array.Length - num3)
{
num6 = (float)(array.Length - k) / (float)num3;
}
array2[k + num] = num5 * num6;
}
for (int l = array.Length + num; l < array2.Length; l++)
{
array2[l] = 0f;
}
return array2;
}
private async Task ClearAudioFolderAsync()
{
string[] files = Directory.GetFiles(audioFolderPath, "*.wav");
foreach (string file in files)
{
await Task.Run(delegate
{
File.Delete(file);
});
}
log.LogInfo((object)"Audio folder cleared.");
}
[IteratorStateMachine(typeof(<DestroyAfterDelay>d__34))]
private IEnumerator DestroyAfterDelay(AudioSource audioSource, float delay)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <DestroyAfterDelay>d__34(0)
{
<>4__this = this,
audioSource = audioSource,
delay = delay
};
}
private List<byte[]> ChunkAudioData(byte[] audioData, int chunkSize)
{
List<byte[]> list = new List<byte[]>();
for (int i = 0; i < audioData.Length; i += chunkSize)
{
int num = Mathf.Min(chunkSize, audioData.Length - i);
byte[] array = new byte[num];
Array.Copy(audioData, i, array, 0, num);
list.Add(array);
}
return list;
}
private float[] ApplyPitchShift(float[] samples, float pitchFactor)
{
log.LogInfo((object)"Pitch shift applied.");
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;
}
private float[] ApplyAlienFilter(float[] samples)
{
float[] array = new float[samples.Length];
float num = 5f;
float num2 = 0.05f;
float num3 = 200f;
float num4 = 0.3f;
for (int i = 0; i < samples.Length; i++)
{
float num5 = (float)i / (float)sampleRate;
float num6 = Mathf.Sin(MathF.PI * 2f * num * num5) * num2;
float num7 = 1f + num6;
float num8 = (float)i * num7;
int num9 = (int)num8;
float num10 = num8 - (float)num9;
float num11 = 0f;
if (num9 + 1 < samples.Length)
{
num11 = samples[num9] * (1f - num10) + samples[num9 + 1] * num10;
}
else if (num9 < samples.Length)
{
num11 = samples[num9];
}
float num12 = Mathf.Sin(MathF.PI * 2f * num3 * num5);
float num13 = num11 * num12 * num4;
array[i] = num11 * (1f - num4) + num13;
array[i] = Mathf.Clamp(array[i], -1f, 1f);
}
return array;
}
private float[] ApplyLowPassFilter(float[] samples, float cutoffFreq)
{
float[] array = new float[samples.Length];
float num = 1f / (MathF.PI * 2f * cutoffFreq);
float num2 = 1f / (float)sampleRate;
float num3 = num2 / (num + num2);
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 class TaskExtensions
{
[CompilerGenerated]
private sealed class <AsCoroutine>d__0 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public Task task;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <AsCoroutine>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
break;
case 1:
<>1__state = -1;
break;
}
if (!task.IsCompleted)
{
<>2__current = null;
<>1__state = 1;
return true;
}
if (task.Exception != null)
{
throw task.Exception;
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[IteratorStateMachine(typeof(<AsCoroutine>d__0))]
public static IEnumerator AsCoroutine(this Task task)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <AsCoroutine>d__0(0)
{
task = task
};
}
}
[BepInPlugin("Mimics", "Mimics", "1.1.6")]
public class Plugin : BaseUnityPlugin
{
internal static ManualLogSource Logger;
private static Harmony _harmony;
public static ConfigEntry<float> configVoiceVolume;
public static ConfigEntry<float> configMinDelay;
public static ConfigEntry<float> configMaxDelay;
public static ConfigEntry<bool> configHearYourself;
public static ConfigEntry<bool> configFilterEnabled;
public static Dictionary<string, ConfigEntry<bool>> enemyConfigEntries = new Dictionary<string, ConfigEntry<bool>>();
public static ConfigEntry<int> configSamplingRate;
private void Awake()
{
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Expected O, but got Unknown
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Expected O, but got Unknown
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Expected O, but got Unknown
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
//IL_00f9: Expected O, but got Unknown
//IL_012c: Unknown result type (might be due to invalid IL or missing references)
//IL_0136: Expected O, but got Unknown
//IL_0160: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Expected O, but got Unknown
Logger = ((BaseUnityPlugin)this).Logger;
Logger.LogInfo((object)"Plugin Mimics is loaded!");
configVoiceVolume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 0.75f, new ConfigDescription("Volume of the mimic voices.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
configMinDelay = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MinDelay", 30f, new ConfigDescription("Minimum time before an audio clip is recorded and played.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 120f), Array.Empty<object>()));
configMaxDelay = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxDelay", 120f, new ConfigDescription("Maximum time before an audio clip is recorded and played.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 240f), Array.Empty<object>()));
configHearYourself = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Hear Yourself?", true, new ConfigDescription("Turning this off will make it so you won't hear your own voice played by mimics.", (AcceptableValueBase)null, Array.Empty<object>()));
configSamplingRate = ((BaseUnityPlugin)this).Config.Bind<int>("Experimental", "Sampling Rate", 48000, new ConfigDescription("Only change this value if the console gives you a warning about your microphone frequency not being supported.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(16000, 48000), Array.Empty<object>()));
configFilterEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "Filter Enabled?", false, "Turning this on allows you to customize which enemies can mimic voices. (Keep as 'false' if you want to allow custom enemies to mimic voices)");
_harmony = new Harmony("Mimics");
_harmony.PatchAll();
EnemyDirectorStartPatch.Initialize(((BaseUnityPlugin)this).Config);
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "Mimics";
public const string PLUGIN_NAME = "My first plugin";
public const string PLUGIN_VERSION = "1.1.6";
}
}
namespace Mimics.patches
{
[HarmonyPatch(typeof(EnemyDirector))]
internal class EnemyDirectorStartPatch
{
private static readonly ManualLogSource log = Logger.CreateLogSource("Mimics");
public static HashSet<string> filterEnemies;
private static bool setupComplete = false;
private static ConfigFile configFile;
public static void Initialize(ConfigFile config)
{
configFile = config;
log.LogInfo((object)"EnemyDirectorStartPatch initialized with ConfigFile.");
}
[HarmonyPatch("Start")]
[HarmonyPostfix]
public static void SetupEnemies(EnemyDirector __instance)
{
if (setupComplete)
{
return;
}
List<EnemySetup>[] array = new List<EnemySetup>[3] { __instance.enemiesDifficulty1, __instance.enemiesDifficulty2, __instance.enemiesDifficulty3 };
filterEnemies = new HashSet<string>();
List<EnemySetup>[] array2 = array;
foreach (List<EnemySetup> list in array2)
{
foreach (EnemySetup item in list)
{
if (((Object)item.spawnObjects[0]).name.Contains("Director"))
{
filterEnemies.Add(((Object)item.spawnObjects[1]).name);
}
else
{
filterEnemies.Add(((Object)item.spawnObjects[0]).name);
}
}
}
setupComplete = true;
SetupEnemyConfig();
}
private static void SetupEnemyConfig()
{
log.LogInfo((object)"Setting up enemy config...");
foreach (string filterEnemy in filterEnemies)
{
string text = filterEnemy.Replace("Enemy - ", "");
Plugin.enemyConfigEntries[filterEnemy] = configFile.Bind<bool>("Enemies", filterEnemy, true, "Enables/disables ability for " + filterEnemy + " to mimic player voices.");
log.LogInfo((object)("Added config entry for enemy: " + filterEnemy));
}
}
}
public class MimicsFinder : MonoBehaviour
{
private static MimicsFinder instance;
private static readonly ManualLogSource log = Logger.CreateLogSource("Mimics");
public static Mimics LocalMimics { get; set; }
public static void EnsureInitialized()
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)instance == (Object)null)
{
instance = new GameObject("MimicsFinder").AddComponent<MimicsFinder>();
Object.DontDestroyOnLoad((Object)(object)((Component)instance).gameObject);
ManualLogSource obj = log;
Player localPlayer = PhotonNetwork.LocalPlayer;
obj.LogInfo((object)$"MimicsFinder initialized for Player {((localPlayer != null) ? localPlayer.ActorNumber : (-1))}");
}
}
private void OnDestroy()
{
if ((Object)(object)instance == (Object)(object)this)
{
LocalMimics = null;
instance = null;
log.LogInfo((object)"MimicsFinder destroyed, clearing cache.");
}
}
}
[HarmonyPatch(typeof(PlayerAvatar), "Awake")]
internal class PlayerAvatarPatch
{
private static readonly ManualLogSource log = Logger.CreateLogSource("Mimics");
private static void Postfix(PlayerAvatar __instance)
{
if (PhotonNetwork.IsConnectedAndReady)
{
Mimics mimics = ((Component)__instance).GetComponent<Mimics>();
if ((Object)(object)mimics == (Object)null)
{
mimics = ((Component)__instance).gameObject.AddComponent<Mimics>();
log.LogInfo((object)("Added Mimics component to PlayerAvatar: " + ((Object)__instance).name));
}
PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
if ((Object)(object)component != (Object)null && component.IsMine)
{
MimicsFinder.LocalMimics = mimics;
log.LogInfo((object)("Set LocalMimics for local PlayerAvatar: " + ((Object)__instance).name));
}
}
}
}
[HarmonyPatch(typeof(LocalVoiceFramed<short>), "PushDataAsync")]
internal class LocalVoiceFramedPatch
{
private static void Prefix(short[] buf, LocalVoiceFramed<short> __instance)
{
MimicsFinder.EnsureInitialized();
if (PhotonNetwork.IsConnectedAndReady && !((Object)(object)MimicsFinder.LocalMimics == (Object)null) && !((Object)(object)((Component)MimicsFinder.LocalMimics).gameObject == (Object)null) && MimicsFinder.LocalMimics.photonView.IsMine)
{
MimicsFinder.LocalMimics.ProcessVoiceData(buf);
}
}
}
}