using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Peak; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [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("CarlUnleashedMod")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("CarlUnleashedMod")] [assembly: AssemblyTitle("CarlUnleashedMod")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CarlUnleashedMod { internal static class BingBongFace { public const string ObjectName = "CarlBingBongFace"; public const string ImageName = "carl_face.png"; private static Texture2D texture; private static Material material; private static readonly Dictionary meshes = new Dictionary(); private static readonly List spawned = new List(); private static readonly List hiddenLips = new List(); private static readonly HashSet described = new HashSet(); private const int GridSteps = 12; private const float ScanSeconds = 2f; private static float nextScan; public static void Load(string modFolder) { texture = LoadTexture(modFolder); if ((Object)(object)texture == (Object)null) { Plugin.Log.LogWarning((object)"[BingBongFace] No face image; Bing Bong keeps his own."); } else { Plugin.Log.LogInfo((object)$"[BingBongFace] Loaded {((Texture)texture).width}x{((Texture)texture).height} face."); } } private static Texture2D LoadTexture(string modFolder) { //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown byte[] array = null; string text = Path.Combine(modFolder, "carl_face.png"); if (File.Exists(text)) { try { array = File.ReadAllBytes(text); } catch (Exception ex) { Plugin.Log.LogError((object)("[BingBongFace] " + text + ": " + ex.Message)); } } if (array == null) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text2 = executingAssembly.GetManifestResourceNames().FirstOrDefault((string n) => n.EndsWith("carl_face.png", StringComparison.OrdinalIgnoreCase)); if (text2 == null) { return null; } using Stream stream = executingAssembly.GetManifestResourceStream(text2); if (stream == null) { return null; } array = new byte[stream.Length]; int num2; for (int num = 0; num < array.Length; num += num2) { num2 = stream.Read(array, num, array.Length - num); if (num2 <= 0) { break; } } text = text2; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, array)) { Plugin.Log.LogError((object)("[BingBongFace] Could not decode " + text + ".")); Object.Destroy((Object)(object)val); return null; } ((Object)val).name = "CarlBingBongFace"; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; ((Texture)val).anisoLevel = 4; ((Object)val).hideFlags = (HideFlags)61; return val; } public static void Tick() { if ((Object)(object)texture == (Object)null || Plugin.BoundConfig == null) { return; } if (!Plugin.BoundConfig.Enabled.Value) { Clear(); return; } for (int num = spawned.Count - 1; num >= 0; num--) { if ((Object)(object)spawned[num] == (Object)null) { spawned.RemoveAt(num); } } if (!(Time.unscaledTime < nextScan)) { nextScan = Time.unscaledTime + 2f; Action_AskBingBong[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (Action_AskBingBong bb in array) { Ensure(bb); } } } private static void Clear() { if (spawned.Count == 0 && hiddenLips.Count == 0) { return; } foreach (GameObject item in spawned) { if ((Object)(object)item != (Object)null) { Object.Destroy((Object)(object)item); } } spawned.Clear(); foreach (Renderer hiddenLip in hiddenLips) { if ((Object)(object)hiddenLip != (Object)null) { hiddenLip.enabled = true; } } hiddenLips.Clear(); } public static void Ensure(Action_AskBingBong bb) { //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown if ((Object)(object)bb == (Object)null || (Object)(object)texture == (Object)null || Plugin.BoundConfig == null || !Plugin.BoundConfig.Enabled.Value) { return; } Transform transform = ((Component)bb).transform; Item componentInParent = ((Component)bb).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { if ((Object)(object)componentInParent.mainRenderer == (Object)null) { return; } transform = ((Component)componentInParent).transform; } if (((Component)transform).GetComponentsInChildren(true).Any((Transform t) => ((Object)t).name == "CarlBingBongFace")) { return; } Renderer val = PickBody(transform); if (!((Object)(object)val == (Object)null)) { if (Plugin.BoundConfig.DebugLog.Value) { Describe(transform); } GameObject val2 = new GameObject("CarlBingBongFace"); val2.transform.SetParent(((Component)val).transform, false); val2.layer = ((Component)val).gameObject.layer; val2.AddComponent(); MeshRenderer val3 = val2.AddComponent(); ((Renderer)val3).sharedMaterial = GetMaterial(val); ((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val3).receiveShadows = true; ((Renderer)val3).reflectionProbeUsage = (ReflectionProbeUsage)0; Place(val2.transform, val, transform); HideLips(transform); spawned.Add(val2); } } private static Renderer PickBody(Transform root) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) Renderer val = null; Renderer val2 = null; float num = -1f; float num2 = -1f; Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Renderer val3 in componentsInChildren) { if ((Object)(object)val3 == (Object)null || ((Object)((Component)val3).transform).name == "CarlBingBongFace" || (!(val3 is MeshRenderer) && !(val3 is SkinnedMeshRenderer))) { continue; } Bounds val4 = LocalBounds(val3); Vector3 size = ((Bounds)(ref val4)).size; float num3 = size.x * size.y * size.z; if (num3 <= 0f) { continue; } string text = (((Object)(object)val3.sharedMaterial != (Object)null) ? ((Object)val3.sharedMaterial).name : ""); if (text.IndexOf("BingBong", StringComparison.OrdinalIgnoreCase) >= 0) { if (num3 > num) { num = num3; val = val3; } } else if (text.IndexOf("M_Player", StringComparison.OrdinalIgnoreCase) < 0 && num3 > num2) { num2 = num3; val2 = val3; } } return val ?? val2; } private static Bounds LocalBounds(Renderer r) { //IL_002a: 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((r is SkinnedMeshRenderer) ? r : null); if ((Object)(object)val != (Object)null && (Object)(object)val.sharedMesh != (Object)null) { return val.sharedMesh.bounds; } MeshFilter component = ((Component)r).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null) { return component.sharedMesh.bounds; } Bounds bounds = r.bounds; return new Bounds(((Component)r).transform.InverseTransformPoint(((Bounds)(ref bounds)).center), ((Component)r).transform.InverseTransformVector(((Bounds)(ref bounds)).size)); } private static bool TryFindMouth(Transform root, Renderer body, out Vector3 mouth) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) mouth = Vector3.zero; Vector3 val = Vector3.zero; int num = 0; Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && !(((Object)((Component)val2).transform).name == "CarlBingBongFace")) { string text = ((Object)val2).name.ToLowerInvariant(); if (text.Contains("lip") || text.Contains("mouth")) { Transform transform = ((Component)val2).transform; Bounds val3 = LocalBounds(val2); Vector3 val4 = transform.TransformPoint(((Bounds)(ref val3)).center); val += ((Component)body).transform.InverseTransformPoint(val4); num++; } } } if (num == 0) { return false; } mouth = val / (float)num; return true; } private static void Place(Transform decal, Renderer body, Transform root) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //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_002c: 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_001e: 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_0042: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: 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) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_022d: 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_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) Bounds val = LocalBounds(body); Vector3 extents = ((Bounds)(ref val)).extents; int num = ((!(extents.x <= extents.y) || !(extents.x <= extents.z)) ? ((extents.y <= extents.z) ? 1 : 2) : 0); Vector3 val2 = Vector3.zero; Vector3 mouth; bool flag = TryFindMouth(root, body, out mouth); int num2; float num3; if (flag) { val2 = mouth - ((Bounds)(ref val)).center; Vector3 val3 = default(Vector3); ((Vector3)(ref val3))..ctor(val2.x / Mathf.Max(extents.x, 0.0001f), val2.y / Mathf.Max(extents.y, 0.0001f), val2.z / Mathf.Max(extents.z, 0.0001f)); num2 = ((!(Mathf.Abs(val3.x) >= Mathf.Abs(val3.y)) || !(Mathf.Abs(val3.x) >= Mathf.Abs(val3.z))) ? ((Mathf.Abs(val3.y) >= Mathf.Abs(val3.z)) ? 1 : 2) : 0); num3 = ((((Vector3)(ref val3))[num2] >= 0f) ? 1f : (-1f)); } else { num2 = num; num3 = Mathf.Sign(1f); Plugin.Log.LogInfo((object)"[BingBongFace] No lips found; using his thinnest axis."); } Vector3 val4 = (Vector3)(num2 switch { 1 => Vector3.up, 0 => Vector3.right, _ => Vector3.forward, }) * num3; float num4 = num2 switch { 1 => Mathf.Min(extents.x, extents.z), 0 => Mathf.Min(extents.y, extents.z), _ => Mathf.Min(extents.x, extents.y), }; float num5 = Mathf.Max(2f * num4 * 0.525f, 0.0001f); float num6 = Mathf.Max(((Vector3)(ref extents))[num2], 0.0001f); Vector3 val5 = ((num2 == 1) ? Vector3.forward : Vector3.up); Quaternion val6 = Quaternion.LookRotation(val4, val5) * Quaternion.Euler(0f, 0f, -90f); Vector3 val7 = (flag ? (val2 - val4 * Vector3.Dot(val2, val4) + val6 * Vector3.up * (num5 * 0.2f)) : Vector3.zero); decal.localPosition = ((Bounds)(ref val)).center + val4 * num6 * 1.02f + val7; decal.localRotation = val6; decal.localScale = Vector3.one * num5; SetMesh(decal, (num6 * num6 + num4 * num4) / (2f * num6) / num5); Plugin.Log.LogInfo((object)("[BingBongFace] Face on '" + ((Object)root).name + "' at '" + ((Object)body).name + "', " + (flag ? "over his lips" : "on his broad side") + ", " + string.Format("axis {0}{1}, size {2:0.00}, ", (num3 < 0f) ? "-" : "+", "XYZ"[num2], num5) + $"body {((Bounds)(ref val)).size}.")); } private static void HideLips(Transform root) { if (!Tuning.FaceHideLips) { return; } Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !(((Object)((Component)val).transform).name == "CarlBingBongFace") && val.enabled) { string text = ((Object)val).name.ToLowerInvariant(); if (text.Contains("lip") || text.Contains("mouth")) { val.enabled = false; hiddenLips.Add(val); } } } } private static void SetMesh(Transform decal, float bulgeRadius) { //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: 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_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Expected O, but got Unknown MeshFilter component = ((Component)decal).GetComponent(); if ((Object)(object)component == (Object)null) { return; } float num = ((((Texture)texture).height > 0) ? ((float)((Texture)texture).width / (float)((Texture)texture).height) : 1f); float num2 = Mathf.Sqrt(0.25f * num * num + 0.25f); float num3 = Mathf.Max(bulgeRadius, num2 * 1.05f); float num4 = Mathf.Clamp01(0.9f); int key = Mathf.RoundToInt(num3 * 100f); if (meshes.TryGetValue(key, out var value) && (Object)(object)value != (Object)null) { component.sharedMesh = value; return; } int num5 = 13; Vector3[] array = (Vector3[])(object)new Vector3[num5 * num5]; Vector2[] array2 = (Vector2[])(object)new Vector2[num5 * num5]; Vector3[] array3 = (Vector3[])(object)new Vector3[num5 * num5]; for (int i = 0; i < num5; i++) { for (int j = 0; j < num5; j++) { float num6 = (float)j / 12f; float num7 = (float)i / 12f; float num8 = (num6 - 0.5f) * num; float num9 = num7 - 0.5f; float num10 = Mathf.Sqrt(num8 * num8 + num9 * num9); float num11 = Mathf.Sqrt(Mathf.Max(0f, num3 * num3 - num10 * num10)); int num12 = i * num5 + j; array[num12] = new Vector3(num8, num9, (0f - (num3 - num11)) * num4); array2[num12] = new Vector2(1f - num6, num7); Vector3 forward = Vector3.forward; Vector3 val = new Vector3(num8, num9, num11); array3[num12] = Vector3.Slerp(forward, ((Vector3)(ref val)).normalized, num4); } } int[] array4 = new int[864]; int num13 = 0; for (int k = 0; k < 12; k++) { for (int l = 0; l < 12; l++) { int num14 = k * num5 + l; array4[num13++] = num14; array4[num13++] = num14 + num5; array4[num13++] = num14 + 1; array4[num13++] = num14 + 1; array4[num13++] = num14 + num5; array4[num13++] = num14 + num5 + 1; } } Mesh val2 = new Mesh { name = "CarlBingBongFace", hideFlags = (HideFlags)61, vertices = array, uv = array2, normals = array3, triangles = array4 }; val2.RecalculateBounds(); meshes[key] = val2; component.sharedMesh = val2; } private static Material GetMaterial(Renderer donor) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_014d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)material != (Object)null) { return material; } Shader val = Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Sprites/Default"); if ((Object)(object)val == (Object)null && (Object)(object)donor != (Object)null && (Object)(object)donor.sharedMaterial != (Object)null) { val = donor.sharedMaterial.shader; } if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"[BingBongFace] No usable shader found."); return null; } material = new Material(val) { name = "CarlBingBongFace", hideFlags = (HideFlags)61 }; string[] array = new string[2] { "_BaseMap", "_MainTex" }; foreach (string text in array) { if (material.HasProperty(text)) { material.SetTexture(text, (Texture)(object)texture); } } string[] array2 = new string[2] { "_BaseColor", "_Color" }; foreach (string text2 in array2) { if (material.HasProperty(text2)) { material.SetColor(text2, Color.white); } } if (material.HasProperty("_AlphaClip")) { material.SetFloat("_AlphaClip", 1f); } if (material.HasProperty("_Cutoff")) { material.SetFloat("_Cutoff", 0.35f); } if (material.HasProperty("_Smoothness")) { material.SetFloat("_Smoothness", 0f); } if (material.HasProperty("_Metallic")) { material.SetFloat("_Metallic", 0f); } if (material.HasProperty("_Cull")) { material.SetFloat("_Cull", 0f); } material.EnableKeyword("_ALPHATEST_ON"); material.SetOverrideTag("RenderType", "TransparentCutout"); material.renderQueue = 2450; return material; } private static void Describe(Transform root) { if (described.Add(((Object)root).name)) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("[BingBongFace] hierarchy of '" + ((Object)root).name + "':"); Walk(root, 0, stringBuilder); Plugin.Log.LogInfo((object)stringBuilder.ToString()); } } private static void Walk(Transform t, int depth, StringBuilder sb) { //IL_0034: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) sb.Append(new string(' ', depth * 2)).Append(((Object)t).name); Renderer component = ((Component)t).GetComponent(); if ((Object)(object)component != (Object)null) { Bounds val = LocalBounds(component); sb.Append($" [{((object)component).GetType().Name} size={((Bounds)(ref val)).size} center={((Bounds)(ref val)).center}"); Material sharedMaterial = component.sharedMaterial; if ((Object)(object)sharedMaterial != (Object)null) { sb.Append(" mat=" + ((Object)sharedMaterial).name + " shader=" + (((Object)(object)sharedMaterial.shader != (Object)null) ? ((Object)sharedMaterial.shader).name : "?")); } SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((component is SkinnedMeshRenderer) ? component : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.sharedMesh != (Object)null) { for (int i = 0; i < val2.sharedMesh.blendShapeCount; i++) { sb.Append(" blendshape=\"" + val2.sharedMesh.GetBlendShapeName(i) + "\""); } } sb.Append(']'); } sb.AppendLine(); for (int j = 0; j < t.childCount; j++) { Walk(t.GetChild(j), depth + 1, sb); } } } internal class BingBongFaceTicker : MonoBehaviour { private void Update() { try { BingBongFace.Tick(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[BingBongFace] {arg}"); } } } [HarmonyPatch(typeof(ItemActionBase), "Start")] public static class PatchBingBongFaceSpawn { private static void Postfix(ItemActionBase __instance) { Action_AskBingBong val = (Action_AskBingBong)(object)((__instance is Action_AskBingBong) ? __instance : null); if ((Object)(object)val == (Object)null) { return; } try { BingBongFace.Ensure(val); } catch (Exception arg) { Plugin.Log.LogError((object)$"[BingBongFace] {arg}"); } } } internal static class BingBongSounds { public const string FolderName = "bingbong"; public const string IdPrefix = "CARLBB"; private static readonly List clips = new List(); private static readonly List texts = new List(); private static readonly Dictionary byId = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet injected = new HashSet(); private static readonly Dictionary sidecar = new Dictionary(StringComparer.OrdinalIgnoreCase); public static int Count => clips.Count; private static void LoadSidecar(string dir) { sidecar.Clear(); string text = ClipLayout.Sidecar(dir, "bingbong", "subtitles.tsv"); if (text == null || !File.Exists(text)) { return; } try { string[] array = File.ReadAllLines(text); foreach (string text2 in array) { if (text2.Length != 0 && text2[0] != '#') { int num = text2.IndexOf('\t'); if (num > 0) { sidecar[text2.Substring(0, num)] = text2.Substring(num + 1).Trim(); } } } Plugin.Log.LogInfo((object)$"[BingBong] {sidecar.Count} subtitle(s) from sidecar."); } catch (Exception arg) { Plugin.Log.LogError((object)$"[BingBong] subtitles.tsv: {arg}"); } } public static void Load(string modFolder) { clips.Clear(); texts.Clear(); byId.Clear(); injected.Clear(); string text = Path.Combine(modFolder, "bingbong"); string[] array = ClipLayout.Files(modFolder, "bingbong"); if (array.Length == 0) { Plugin.Log.LogInfo((object)"[BingBong] no 'bingbong' clips; answers untouched."); return; } LoadSidecar(modFolder); foreach (string item in array.OrderBy((string x) => x)) { AudioClip val = WavLoader.Load(item); if (!((Object)(object)val == (Object)null)) { string text2 = ClipLayout.Stem(item); if (!sidecar.TryGetValue(text2, out var value) || string.IsNullOrEmpty(value)) { value = text2.Replace('_', ' ').Trim(); } string text3 = "CARLBB" + clips.Count; clips.Add(val); texts.Add(value); byId[text3] = value; Plugin.Log.LogInfo((object)("[BingBong] " + text3 + " <- " + Path.GetFileName(item))); } } Plugin.Log.LogInfo((object)$"[BingBong] {clips.Count} answer(s) loaded."); } public static bool TryText(string id, out string text) { return byId.TryGetValue(id, out text); } public static void Ensure(Action_AskBingBong bb) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown if (!((Object)(object)bb == (Object)null) && clips.Count != 0 && !injected.Contains(bb)) { injected.Add(bb); List list = ((IEnumerable)(((object)bb.responses) ?? ((object)new BingBongResponse[0]))).ToList(); int count = list.Count; for (int i = 0; i < count; i++) { SFX_Instance val = ScriptableObject.CreateInstance(); AudioClip val2 = clips[i % clips.Count]; val.clips = (AudioClip[])(object)new AudioClip[1] { val2 }; val.settings = new SFX_Settings { volume = ClipVolumes.Scaled(val2), volume_Variation = 0f, pitch = 1f, pitch_Variation = 0f, spatialBlend = 1f, range = 120f, dopplerLevel = 0f }; list[i].subtitleID = "CARLBB" + i % clips.Count; list[i].sfx = val; } bb.responses = list.ToArray(); Plugin.Log.LogInfo((object)($"[BingBong] replaced all {count} answer slot(s) with {clips.Count} clip(s)" + ((clips.Count != count) ? " (cycled)" : ""))); } } } [HarmonyPatch(typeof(Action_AskBingBong), "RunAction")] public static class PatchBingBongRun { private static void Prefix(Action_AskBingBong __instance) { try { BingBongSounds.Ensure(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"[BingBong] {arg}"); } } } [HarmonyPatch(typeof(Action_AskBingBong), "Ask")] public static class PatchBingBongAsk { private static void Prefix(Action_AskBingBong __instance) { try { BingBongSounds.Ensure(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"[BingBong] {arg}"); } } } [HarmonyPatch(typeof(LocalizedText), "GetText", new Type[] { typeof(string), typeof(bool) })] public static class PatchLocalizedText { private static bool Prefix(string id, ref string __result) { if (id == null || id.IndexOf("CARLBB", StringComparison.OrdinalIgnoreCase) != 0) { return true; } if (!BingBongSounds.TryText(id, out var text)) { return true; } __result = text; return false; } } internal static class BingBongSubtitles { private const float VanillaSeconds = 1.8f; private const float HardCap = 30f; private const float StartGrace = 0.5f; public static IEnumerator Routine(Action_AskBingBong bb, string subtitleID) { yield return (object)new WaitForSeconds(0.19f); TextMeshPro label = (((Object)(object)bb != (Object)null) ? bb.subtitles : null); if (!((Object)(object)label == (Object)null)) { ((TMP_Text)label).text = LocalizedText.GetText(subtitleID, true); Fit(label); ((Component)label).gameObject.SetActive(true); AudioSource src = bb.source; float pitch = (((Object)(object)src != (Object)null && src.pitch > 0.01f) ? src.pitch : 1f); float floor = 1.8f / pitch; float t = 0f; float waited = 0f; while ((Object)(object)src != (Object)null && !src.isPlaying && waited < 0.5f) { waited += Time.deltaTime; t += Time.deltaTime; Billboard(bb, label, t); yield return null; } while (t < 30f && (t < floor || ((Object)(object)src != (Object)null && src.isPlaying))) { t += Time.deltaTime; Billboard(bb, label, t); yield return null; } ((Component)label).gameObject.SetActive(false); } } private static void Fit(TextMeshPro label) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) try { ((TMP_Text)label).overflowMode = (TextOverflowModes)0; ((TMP_Text)label).maxVisibleCharacters = int.MaxValue; ((TMP_Text)label).maxVisibleLines = int.MaxValue; RectTransform rectTransform = ((TMP_Text)label).rectTransform; if ((Object)(object)rectTransform != (Object)null) { float y = ((TMP_Text)label).GetPreferredValues(((TMP_Text)label).text, rectTransform.sizeDelta.x, 0f).y; if (y > rectTransform.sizeDelta.y) { rectTransform.sizeDelta = new Vector2(rectTransform.sizeDelta.x, y + ((TMP_Text)label).fontSize); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"[BingBong] subtitle fit: {arg}"); } } private static void Billboard(Action_AskBingBong bb, TextMeshPro label, float t) { //IL_00a2: 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_0071: 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) //IL_008b: Unknown result type (might be due to invalid IL or missing references) ((TMP_Text)label).alpha = Mathf.Clamp01(t * 12f); Camera val = Camera.main; if ((Object)(object)val == (Object)null && (Object)(object)MainCamera.instance != (Object)null) { val = ((Component)MainCamera.instance).GetComponentInChildren(); } if (!((Object)(object)val == (Object)null)) { if (bb.scaleCurve != null) { label.transform.localScale = Vector3.one * bb.scaleCurve.Evaluate(Vector3.Distance(label.transform.position, ((Component)val).transform.position)); } label.transform.forward = ((Component)val).transform.forward; } } } [HarmonyPatch(typeof(Action_AskBingBong), "RunAction")] public static class PatchBingBongAskGate { private static bool Prefix(Action_AskBingBong __instance) { try { if (Plugin.BoundConfig == null || !Plugin.BoundConfig.Enabled.Value) { return true; } if (BingBongSounds.Count == 0) { return true; } AudioSource val = (((Object)(object)__instance != (Object)null) ? __instance.source : null); if ((Object)(object)val == (Object)null || !val.isPlaying) { return true; } Plugin.Log.LogInfo((object)"[BingBong] still answering; question ignored."); return false; } catch (Exception arg) { Plugin.Log.LogError((object)$"[BingBong] {arg}"); return true; } } } [HarmonyPatch(typeof(Action_AskBingBong), "SubtitleRoutine")] public static class PatchBingBongSubtitle { private static bool Prefix(Action_AskBingBong __instance, string subtitleID, ref IEnumerator __result) { try { if (Plugin.BoundConfig == null || !Plugin.BoundConfig.Enabled.Value) { return true; } if (BingBongSounds.Count == 0) { return true; } __result = BingBongSubtitles.Routine(__instance, subtitleID); return false; } catch (Exception arg) { Plugin.Log.LogError((object)$"[BingBong] {arg}"); return true; } } } internal static class ChatterSounds { public const string FolderName = "chatter"; internal const byte ChatterEvent = 80; private static readonly List clips = new List(); private static SFX_Instance sfx; private static float nextAt = -1f; private static float busyUntil; private static readonly Queue waiting = new Queue(); public static int Count => clips.Count; public static void Load(string modFolder) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown clips.Clear(); waiting.Clear(); busyUntil = 0f; nextAt = -1f; string[] array = ClipLayout.Files(modFolder, "chatter"); if (array.Length == 0) { Plugin.Log.LogInfo((object)"[Chatter] no 'chatter' clips; idle chatter off."); return; } foreach (string item in array.OrderBy((string x) => x, StringComparer.Ordinal)) { AudioClip val = WavLoader.Load(item); if ((Object)(object)val != (Object)null) { clips.Add(val); } } if (clips.Count > 0) { sfx = ScriptableObject.CreateInstance(); sfx.clips = (AudioClip[])(object)new AudioClip[0]; sfx.settings = new SFX_Settings(); } Plugin.Log.LogInfo((object)$"[Chatter] {clips.Count} clip(s) in the pool."); } private static Character PickSinger() { List list = new List(); List allCharacters = Character.AllCharacters; for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.data == (Object)null) && !val.data.dead && val.IsPlayerControlled) { list.Add(val); } } if (list.Count == 0) { return null; } return list[Random.Range(0, list.Count)]; } private static void Reschedule() { float value = Plugin.BoundConfig.ChatterMinSeconds.Value; float num = Mathf.Max(value, Plugin.BoundConfig.ChatterMaxSeconds.Value); int num2 = 1; try { if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null) { num2 = Mathf.Max(1, PhotonNetwork.CurrentRoom.PlayerCount); } } catch { } nextAt = Time.time + Random.Range(value, num) / Mathf.Pow((float)num2, 0.25f); } internal static void Tick() { //IL_0158: 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_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Expected O, but got Unknown if ((Object)(object)sfx == (Object)null || Plugin.BoundConfig == null) { return; } if (!Plugin.BoundConfig.Enabled.Value || !Plugin.BoundConfig.ChatterEnabled.Value) { nextAt = -1f; } else if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { nextAt = -1f; } else if (nextAt < 0f) { Reschedule(); } else { if (Time.time < nextAt) { return; } Reschedule(); if (!SoundBus.Allows(SoundLane.Chatter)) { return; } Character val = PickSinger(); if (!((Object)(object)val == (Object)null)) { int num = Random.Range(0, clips.Count); int num2 = (((Object)(object)((MonoBehaviourPun)val).photonView != (Object)null) ? ((MonoBehaviourPun)val).photonView.ViewID : 0); if (PhotonNetwork.OfflineMode || !PhotonNetwork.InRoom) { Enqueue(num, num2); return; } PhotonNetwork.RaiseEvent((byte)80, (object)new object[2] { num, num2 }, new RaiseEventOptions { Receivers = (ReceiverGroup)1 }, SendOptions.SendReliable); } } } internal static void Enqueue(int index, int viewId) { //IL_0119: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)sfx == (Object)null) && index >= 0 && index < clips.Count) { AudioClip val = clips[index]; Character val2 = default(Character); Character.GetCharacterWithPhotonID(viewId, ref val2); sfx.settings.volume = ClipVolumes.Scaled(val); sfx.settings.range = 120f; sfx.settings.volume_Variation = 0f; sfx.settings.pitch = 1f; sfx.settings.pitch_Variation = 0f; sfx.settings.dopplerLevel = 0f; sfx.settings.spatialBlend = 0.8f; sfx.clips = (AudioClip[])(object)new AudioClip[1] { val }; if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogInfo((object)("[Chatter] '" + ((Object)val).name + "' skipped; the singer has gone.")); } else if (!SoundBus.Play(sfx, val, val2.Center, SoundSource.Of(val2), SoundLane.Chatter)) { Plugin.Log.LogInfo((object)$"[Chatter] '{((Object)val).name}' dropped; {SoundBus.Holder} has the floor"); } else { Plugin.Log.LogInfo((object)$"[Chatter] {val2.characterName} sings '{((Object)val).name}' ({val.length:0.0}s)"); } } } } internal class ChatterWatcher : MonoBehaviour, IOnEventCallback { private void OnEnable() { PhotonNetwork.AddCallbackTarget((object)this); } private void OnDisable() { PhotonNetwork.RemoveCallbackTarget((object)this); } private void Update() { try { ChatterSounds.Tick(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Chatter] {arg}"); } } public void OnEvent(EventData photonEvent) { if (photonEvent.Code != 80) { return; } try { if (photonEvent.CustomData is object[] array && array.Length >= 2) { ChatterSounds.Enqueue(Convert.ToInt32(array[0]), Convert.ToInt32(array[1])); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Chatter] {arg}"); } } } internal static class ClipLayout { public const char Separator = '.'; private static readonly HashSet pools = new HashSet(StringComparer.OrdinalIgnoreCase) { "state", "items", "ui", "progress", "cues", "chatter", "bingbong", "bugle" }; public static string[] Files(string modFolder, string pool) { string value = pool + "."; List list = new List(); string[] array = WavLoader.Files(modFolder); foreach (string text in array) { if (Path.GetFileName(text).StartsWith(value, StringComparison.OrdinalIgnoreCase)) { list.Add(text); } } string text2 = Path.Combine(modFolder, pool); if (Directory.Exists(text2)) { string[] array2 = WavLoader.Files(text2); foreach (string item in array2) { list.Add(item); } } return list.ToArray(); } public static string Stem(string path) { string text = Path.GetFileNameWithoutExtension(path) ?? ""; int num = text.IndexOf('.'); if (num <= 0) { return text; } string s = text.Substring(0, num); return IsPool(s) ? text.Substring(num + 1) : text; } public static string Sidecar(string modFolder, string pool, string name) { string text = Path.Combine(modFolder, name); if (File.Exists(text)) { return text; } string text2 = Path.Combine(modFolder, pool, name); return File.Exists(text2) ? text2 : null; } private static bool IsPool(string s) { return pools.Contains(s); } } internal static class ClipVolumes { public const string Section = "Volume Tuning"; private static readonly Dictionary> entries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> plays = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary byKey = new Dictionary(StringComparer.OrdinalIgnoreCase); private static AudioSource speaker; private static float nextPoll; private static string playing; public static int Count => entries.Count; public static void Register(AudioClip clip) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown if (!((Object)(object)clip == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null)) { bool isFlat; string text = FlatClips.Strip(((Object)clip).name ?? "", out isFlat).Trim(); if (text.Length != 0 && !entries.ContainsKey(text)) { entries[text] = ((BaseUnityPlugin)Plugin.Instance).Config.Bind("Volume Tuning", text, 1f, new ConfigDescription("Trim for this one clip, on top of Master Volume. 1 = as cut, 0.5 = half, 2 = double. Unity clamps a source at full scale, so Master x trim can never exceed 1.0 - with Master at its default 0.5, a trim of 2 is already that ceiling. A clip that still wants to be louder has to be cut louder, not trimmed louder.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 2f), Array.Empty())); byKey[text] = clip; plays[text] = ((BaseUnityPlugin)Plugin.Instance).Config.Bind("Volume Tuning", text + " (play)", false, "Tick to hear this clip at its current trim. Unticks itself."); } } } public static float For(AudioClip clip) { if ((Object)(object)clip == (Object)null) { return 1f; } bool isFlat; string key = FlatClips.Strip(((Object)clip).name ?? "", out isFlat).Trim(); ConfigEntry value; return entries.TryGetValue(key, out value) ? value.Value : 1f; } internal static void Preview() { if (plays.Count == 0 || Time.unscaledTime < nextPoll) { return; } nextPoll = Time.unscaledTime + 0.1f; if (playing != null) { if (!plays.TryGetValue(playing, out var value) || !value.Value) { Stop(); playing = null; } else { if (!((Object)(object)speaker == (Object)null) && speaker.isPlaying) { if (byKey.TryGetValue(playing, out var value2) && (Object)(object)value2 != (Object)null) { speaker.volume = Mathf.Clamp01(Scaled(value2)); } return; } value.Value = false; playing = null; } } foreach (KeyValuePair> play in plays) { if (play.Value.Value) { if (byKey.TryGetValue(play.Key, out var value3) && !((Object)(object)value3 == (Object)null)) { Play(value3); playing = play.Key; break; } play.Value.Value = false; } } } private static void Stop() { if ((Object)(object)speaker != (Object)null && speaker.isPlaying) { speaker.Stop(); } } private static void Play(AudioClip clip) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if ((Object)(object)speaker == (Object)null) { if ((Object)(object)Plugin.Instance == (Object)null) { return; } GameObject val = new GameObject("CarlVolumePreview"); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; speaker = val.AddComponent(); speaker.playOnAwake = false; speaker.spatialBlend = 0f; speaker.dopplerLevel = 0f; speaker.bypassEffects = true; speaker.bypassListenerEffects = true; speaker.bypassReverbZones = true; } speaker.Stop(); if (playing != null && plays.ContainsKey(playing)) { plays[playing].Value = false; } speaker.clip = clip; speaker.volume = Mathf.Clamp01(Scaled(clip)); speaker.Play(); Plugin.Log.LogInfo((object)($"[Volume] preview '{((Object)clip).name}' at {speaker.volume:0.00} " + $"(master {Plugin.BoundConfig.MasterVolume.Value:0.00} x trim {For(clip):0.00})")); } public static float Scaled(AudioClip clip) { return Plugin.BoundConfig.MasterVolume.Value * For(clip); } } internal static class DownedCues { public const string FolderName = "cues"; private static readonly Dictionary> clips = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static SFX_Instance sfx; internal static readonly Dictionary DownedSince = new Dictionary(); internal static readonly Dictionary Fired = new Dictionary(); public static int Count => clips.Values.Sum((List l) => l.Count); public static void Load(string modFolder) { //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown clips.Clear(); string text = Path.Combine(modFolder, "cues"); string[] array = ClipLayout.Files(modFolder, "cues"); if (array.Length == 0) { Plugin.Log.LogInfo((object)"[Cues] no 'cues' clips; timed cues off."); return; } string[] array2 = array; foreach (string text2 in array2) { bool isFlat; string text3 = FlatClips.Strip(ClipLayout.Stem(text2), out isFlat).ToLowerInvariant(); string text4 = (text3.StartsWith("downed_10s") ? "downed_10s" : (text3.StartsWith("downed_2s") ? "downed_2s" : text3)); AudioClip val = WavLoader.Load(text2); if (!((Object)(object)val == (Object)null)) { FlatClips.Register(val, isFlat); if (!clips.TryGetValue(text4, out var value)) { value = (clips[text4] = new List()); } value.Add(val); Plugin.Log.LogInfo((object)("[Cues] " + Path.GetFileName(text2) + " -> " + text4)); } } if (clips.Count > 0) { sfx = ScriptableObject.CreateInstance(); sfx.clips = (AudioClip[])(object)new AudioClip[0]; sfx.settings = new SFX_Settings(); } Plugin.Log.LogInfo((object)$"[Cues] {Count} clip(s) across {clips.Count} cue(s)."); } public static void Play(string key, Vector3 pos, Transform follow = null) { //IL_0116: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)sfx == (Object)null) && clips.TryGetValue(key, out var value) && value.Count != 0) { sfx.settings.volume = Plugin.BoundConfig.MasterVolume.Value; sfx.settings.range = 120f; sfx.settings.volume_Variation = 0f; sfx.settings.pitch = 1f; sfx.settings.pitch_Variation = 0f; sfx.settings.dopplerLevel = 0f; AudioClip val = value[Random.Range(0, value.Count)]; sfx.settings.spatialBlend = FlatClips.Blend(val, 0.8f); sfx.clips = (AudioClip[])(object)new AudioClip[1] { val }; sfx.settings.volume = ClipVolumes.Scaled(val); sfx.Play(pos, follow); Plugin.Log.LogInfo((object)("[Cues] " + key + " played")); } } internal static void Begin(Character c) { if (!((Object)(object)c == (Object)null)) { DownedSince[c] = Time.time; Fired[c] = 0; } } internal static void End(Character c) { if (!((Object)(object)c == (Object)null)) { DownedSince.Remove(c); Fired.Remove(c); } } internal static void Tick() { //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) if (DownedSince.Count == 0) { return; } float num = 2f; float num2 = 10f; Character[] array = DownedSince.Keys.ToArray(); Character[] array2 = array; foreach (Character val in array2) { if ((Object)(object)val == (Object)null) { DownedSince.Remove(val); Fired.Remove(val); } else if (!EventSounds.SleptOut(val)) { float num3 = Time.time - DownedSince[val]; int value; int num4 = (Fired.TryGetValue(val, out value) ? value : 0); if (num4 < 1 && num3 >= num) { Fired[val] = 1; Play("downed_2s", val.Center, SoundSource.Of(val)); } else if (num4 < 2 && num3 >= num2) { Fired[val] = 2; Play("downed_10s", val.Center, SoundSource.Of(val)); } } } } } internal class CueTicker : MonoBehaviour { private void Update() { try { DownedCues.Tick(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Cues] {arg}"); } } } [HarmonyPatch(typeof(Character), "RPCA_PassOut")] public static class PatchPassOutCue { private static void Postfix(Character __instance) { DownedCues.Begin(__instance); } } [HarmonyPatch(typeof(Character), "RPCA_UnPassOut")] public static class PatchUnPassOutCue { private static void Postfix(Character __instance) { DownedCues.End(__instance); } } internal static class EventSounds { public const string AfflictionInjury = "affliction_injury"; public const string AfflictionHunger = "affliction_hunger"; public const string AfflictionCold = "affliction_cold"; public const string AfflictionHot = "affliction_hot"; public const string AfflictionPoison = "affliction_poison"; public const string AfflictionThorns = "affliction_thorns"; public const string AfflictionSpores = "affliction_spores"; public const string LongFall = "long_fall"; public const string Downed = "downed"; public const string SleepyKo = "sleepy_ko"; public const string FullyPassedOut = "fully_passed_out"; public const string Revived = "revived"; public const string Zombified = "zombified"; public const string ZombieTalk = "zombie_talk"; public const string Cannibalism = "cannibalism"; public const string MiddleMousePing = "middle_mouse_ping"; public const string RocketpackLaunch = "rocketpack_launch"; public const string EnterAntigravity = "enter_antigravity"; public const string PlayerJoined = "player_joined"; public const string PlayerLeft = "player_left"; private static readonly HashSet Replaces = new HashSet(StringComparer.OrdinalIgnoreCase) { "affliction_injury", "affliction_hunger", "affliction_cold", "affliction_hot", "affliction_poison", "affliction_thorns", "affliction_spores", "zombie_talk", "middle_mouse_ping" }; internal static readonly HashSet Muted = new HashSet(); private static readonly HashSet sleptOut = new HashSet(); private static readonly HashSet fullyOut = new HashSet(); public static bool ShouldReplace(string key) { return Replaces.Contains(key); } public static bool Fire(string key, Character who) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (Plugin.BoundConfig == null || !Plugin.BoundConfig.Enabled.Value) { return true; } if (!StateSounds.Has(key)) { return true; } Vector3 pos = (((Object)(object)who != (Object)null) ? who.Center : Vector3.zero); StateSounds.PlayAt(key, pos, -1f, SoundSource.Of(who)); StateSounds.Schedule(key, pos, SoundSource.Of(who)); return !ShouldReplace(key); } public static bool FireNet(string key, Character who) { if (Plugin.BoundConfig == null || !Plugin.BoundConfig.Enabled.Value) { return true; } if (!StateSounds.Has(key)) { return true; } SoundNet.Fire(SoundPool.State, key, who); return !ShouldReplace(key); } public static bool FireAt(string key, Vector3 pos, bool flat = false) { //IL_0037: 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) if (Plugin.BoundConfig == null || !Plugin.BoundConfig.Enabled.Value) { return true; } if (!StateSounds.Has(key)) { return true; } StateSounds.PlayAt(key, pos, -1f, null, flat ? 0f : (-1f)); StateSounds.Schedule(key, pos); return !ShouldReplace(key); } public static bool SleptOut(Character c) { return (Object)(object)c != (Object)null && sleptOut.Contains(c); } internal static void MarkSleptOut(Character c, bool yes) { if (!((Object)(object)c == (Object)null)) { if (yes) { sleptOut.Add(c); } else { sleptOut.Remove(c); } } } internal static void Tick() { if (Plugin.BoundConfig == null || !Plugin.BoundConfig.Enabled.Value || !StateSounds.Has("fully_passed_out")) { return; } List allCharacters = Character.AllCharacters; for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if ((Object)(object)val == (Object)null || (Object)(object)val.data == (Object)null) { continue; } if (val.data.fullyPassedOut) { if (fullyOut.Add(val)) { Fire("fully_passed_out", val); } } else { fullyOut.Remove(val); } } } } [HarmonyPatch(typeof(SFX_Player), "PlaySFX")] public static class PatchMuteReplaced { private static bool Prefix(SFX_Instance SFX) { if ((Object)(object)SFX == (Object)null || EventSounds.Muted.Count == 0) { return true; } return !EventSounds.Muted.Contains(SFX); } } [HarmonyPatch(typeof(CharacterAfflictions), "StatusSFX")] public static class PatchStatusSFX { private static bool Prefix(CharacterAfflictions __instance, STATUSTYPE sT, float ammount) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) try { string text = KeyFor(sT); if (text == null) { return true; } if ((int)sT == 0 && PatchLongFall.inside) { return true; } return EventSounds.Fire(text, ((Object)(object)__instance != (Object)null) ? __instance.character : null); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Event] {arg}"); return true; } } private static string KeyFor(STATUSTYPE t) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected I4, but got Unknown return (int)t switch { 0 => "affliction_injury", 1 => "affliction_hunger", 2 => "affliction_cold", 8 => "affliction_hot", 3 => "affliction_poison", 9 => "affliction_thorns", 10 => "affliction_spores", _ => null, }; } } [HarmonyPatch(typeof(MushroomZombie), "RPC_PlaySFX")] public static class PatchZombieSFX { private static bool Prefix(MushroomZombie __instance) { try { Character who = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponent() : null); return EventSounds.Fire("zombie_talk", who); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Event] {arg}"); return true; } } } [HarmonyPatch(typeof(PointPing), "Awake")] public static class PatchPing { private static void Prefix(PointPing __instance) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)__instance == (Object)null)) { if (EventSounds.ShouldReplace("middle_mouse_ping") && (Object)(object)__instance.pingSound != (Object)null) { EventSounds.Muted.Add(__instance.pingSound); } EventSounds.FireAt("middle_mouse_ping", ((Component)__instance).transform.position); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Event] {arg}"); } } } [HarmonyPatch(typeof(CharacterBackpackHandler), "RPCA_Fire")] public static class PatchRocketpackLaunch { private static void Postfix(CharacterBackpackHandler __instance) { try { if (!((Object)(object)__instance == (Object)null)) { Character who = ((Component)__instance).GetComponentInParent() ?? ((Component)__instance).GetComponent(); EventSounds.Fire("rocketpack_launch", who); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Event] {arg}"); } } } [HarmonyPatch(typeof(Character), "RPCA_PassOut")] public static class PatchPassOutEvent { private static void Postfix(Character __instance) { bool flag = false; try { flag = WentDownSleepy(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Event] {arg}"); } EventSounds.MarkSleptOut(__instance, flag); EventSounds.Fire(flag ? "sleepy_ko" : "downed", __instance); } private static bool WentDownSleepy(Character c) { //IL_0098: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Invalid comparison between Unknown and I4 //IL_00ae: Unknown result type (might be due to invalid IL or missing references) CharacterAfflictions val = (((Object)(object)c != (Object)null && c.refs != null) ? c.refs.afflictions : null); if ((Object)(object)val == (Object)null) { return false; } float currentStatus = val.GetCurrentStatus((STATUSTYPE)6); if (currentStatus <= 0f) { return false; } float num = Time.time - val.LastAddedStatus((STATUSTYPE)6); bool flag = num <= 6f; float num2 = 0f; foreach (STATUSTYPE value in Enum.GetValues(typeof(STATUSTYPE))) { if ((int)value != 6) { float currentStatus2 = val.GetCurrentStatus(value); if (currentStatus2 > num2) { num2 = currentStatus2; } } } if (!flag && (currentStatus < 0.25f || currentStatus < num2)) { return false; } Plugin.Log.LogInfo((object)($"[Event] {c.characterName} went down asleep - drowsy {currentStatus:0.00}, " + $"topped up {num:0.0}s ago, next highest {num2:0.00}.")); return true; } } [HarmonyPatch(typeof(Character), "RPCA_UnPassOut")] public static class PatchUnPassOutEvent { private static void Postfix(Character __instance) { EventSounds.MarkSleptOut(__instance, yes: false); EventSounds.Fire("revived", __instance); } } [HarmonyPatch(typeof(Character), "RPCA_Zombify")] public static class PatchZombify { private static void Postfix(Character __instance) { EventSounds.Fire("zombified", __instance); } } [HarmonyPatch(typeof(CharacterInteractible), "GetEaten")] public static class PatchGetEaten { private static void Postfix(Character eater) { EventSounds.FireNet("cannibalism", eater); } } [HarmonyPatch(typeof(CharacterMovement), "CheckFallDamage")] public static class PatchLongFall { internal static bool inside; internal static bool armed; private static void Prefix(CharacterMovement __instance) { inside = true; armed = false; try { if (!((Object)(object)__instance == (Object)null)) { armed = __instance.sinceGrounded >= 4f && StateSounds.Has("long_fall"); } } catch { armed = false; } } private static void Postfix() { inside = false; armed = false; } } [HarmonyPatch(typeof(CharacterAfflictions), "AddStatus")] public static class PatchFallInjury { private static void Postfix(CharacterAfflictions __instance, STATUSTYPE statusType, bool __result) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 try { if (PatchLongFall.inside && __result && (int)statusType <= 0 && PatchLongFall.armed) { EventSounds.FireNet("long_fall", ((Object)(object)__instance != (Object)null) ? __instance.character : null); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Event] {arg}"); } } } internal static class ItHurtsBridge { public static bool SkipFallSound() { return !PatchLongFall.armed; } public static void Apply(Harmony harmony) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown try { Type type = AccessTools.TypeByName("ItHurts.Plugin"); if (type == null) { Plugin.Log.LogInfo((object)"[Event] ItHurts not installed; nothing to quiet."); return; } MethodInfo methodInfo = AccessTools.Method(type, "RaiseFallSoundEvent", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogWarning((object)"[Event] ItHurts found but RaiseFallSoundEvent is not where it was; leaving it alone."); return; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ItHurtsBridge), "SkipFallSound", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[Event] ItHurts will stand aside on a long fall."); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Event] ItHurts bridge: {arg}"); } } } [HarmonyPatch(typeof(AntiSphere), "OnTriggerEnter")] public static class PatchAntigravEnter { internal static readonly HashSet inside = new HashSet(); private static void Postfix(Collider other) { try { Character val = default(Character); if (CharacterRagdoll.TryGetCharacterFromCollider(other, ref val) && !((Object)(object)val == (Object)null) && inside.Add(val)) { EventSounds.Fire("enter_antigravity", val); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Event] {arg}"); } } } [HarmonyPatch(typeof(AntiSphere), "OnTriggerExit")] public static class PatchAntigravExit { private static void Postfix(Collider other) { try { Character val = default(Character); if (CharacterRagdoll.TryGetCharacterFromCollider(other, ref val) && (Object)(object)val != (Object)null) { PatchAntigravEnter.inside.Remove(val); } } catch { } } } internal class RoomWatcher : MonoBehaviour, IInRoomCallbacks, IMatchmakingCallbacks { private void OnEnable() { PhotonNetwork.AddCallbackTarget((object)this); } private void OnDisable() { PhotonNetwork.RemoveCallbackTarget((object)this); } private void Update() { try { EventSounds.Tick(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Event] {arg}"); } } public void OnPlayerEnteredRoom(Player newPlayer) { At("player_joined"); } public void OnPlayerLeftRoom(Player otherPlayer) { At("player_left"); } private static void At(string key) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Character localCharacter = Character.localCharacter; EventSounds.FireAt(key, ((Object)(object)localCharacter != (Object)null) ? localCharacter.Center : Vector3.zero, flat: true); } public void OnJoinedRoom() { At("player_joined"); } public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged) { } public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps) { } public void OnMasterClientSwitched(Player newMasterClient) { } public void OnFriendListUpdate(List friendList) { } public void OnCreatedRoom() { } public void OnCreateRoomFailed(short returnCode, string message) { } public void OnJoinRoomFailed(short returnCode, string message) { } public void OnJoinRandomFailed(short returnCode, string message) { } public void OnLeftRoom() { } } internal static class FlatClips { public const string Marker = ".2d"; private static readonly HashSet flat = new HashSet(); public static int Count => flat.Count; public static string Strip(string stem, out bool isFlat) { isFlat = false; if (string.IsNullOrEmpty(stem)) { return stem; } int num = stem.IndexOf(".2d", StringComparison.OrdinalIgnoreCase); if (num < 0) { return stem; } isFlat = true; return (stem.Substring(0, num) + stem.Substring(num + ".2d".Length)).Trim(); } public static void Register(AudioClip clip, bool isFlat) { if ((Object)(object)clip != (Object)null && isFlat) { flat.Add(clip); } } public static bool Is(AudioClip clip) { return (Object)(object)clip != (Object)null && flat.Contains(clip); } public static void Clear() { flat.Clear(); } public static float Blend(AudioClip clip, float configured) { return Is(clip) ? 0f : configured; } } internal static class ItemAudit { private static readonly Dictionary known = new Dictionary(StringComparer.OrdinalIgnoreCase); public static int KnownCount => known.Count; private static void Collect() { known.Clear(); Item[] array = Resources.FindObjectsOfTypeAll(); foreach (Item val in array) { if ((Object)(object)val == (Object)null || val.UIData == null) { continue; } string itemName = val.UIData.itemName; if (!string.IsNullOrEmpty(itemName)) { string text = ItemSounds.Norm(itemName); if (text.Length > 0 && !known.ContainsKey(text)) { known[text] = itemName; } } } } public static IEnumerator RunLater() { yield return (object)new WaitForSeconds(5f); try { Run(); } catch (Exception ex) { Exception ex2 = ex; Plugin.Log.LogError((object)$"[Audit] {ex2}"); } } public static void Run() { Collect(); if (known.Count == 0) { Plugin.Log.LogInfo((object)"[Audit] no item prefabs visible yet; skipping."); return; } List list = new List(); int num = 0; foreach (string key in ItemSounds.Keys) { int num2 = key.IndexOf('|'); if (num2 > 0) { string text = key.Substring(0, num2); string text2 = key.Substring(num2 + 1); if (text == "exit") { num++; continue; } if (known.ContainsKey(text2)) { num++; continue; } list.Add(text + "_" + text2 + " -> did you mean \"" + Nearest(text2) + "\"?"); } } Plugin.Log.LogInfo((object)($"[Audit] {num} item context(s) match a real item; {list.Count} do not " + $"(out of {known.Count} items in the game).")); foreach (string item in list) { Plugin.Log.LogWarning((object)("[Audit] " + item)); } if (!Plugin.BoundConfig.DebugLog.Value) { return; } Plugin.Log.LogInfo((object)"[Audit] every item name the game knows:"); foreach (string item2 in known.Values.OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase)) { Plugin.Log.LogInfo((object)("[Audit] " + item2)); } } private static string Nearest(string item) { foreach (string key in known.Keys) { if (key.Contains(item) || item.Contains(key)) { return known[key]; } } string text = null; int num = int.MaxValue; foreach (string key2 in known.Keys) { int num2 = Distance(item, key2); if (num2 < num) { num = num2; text = known[key2]; } } return text ?? "(nothing close)"; } private static int Distance(string a, string b) { int[] array = new int[b.Length + 1]; int[] array2 = new int[b.Length + 1]; for (int i = 0; i <= b.Length; i++) { array[i] = i; } for (int j = 1; j <= a.Length; j++) { array2[0] = j; for (int k = 1; k <= b.Length; k++) { int num = ((a[j - 1] != b[k - 1]) ? 1 : 0); array2[k] = Mathf.Min(Mathf.Min(array2[k - 1] + 1, array[k] + 1), array[k - 1] + num); } int[] array3 = array; array = array2; array2 = array3; } return array[b.Length]; } } internal static class ItemSounds { public const string ItemFolder = "items"; public const string UIFolder = "ui"; private const string PoolSep = "__"; private static readonly Dictionary> clips = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static SFX_Instance sfx; public static int Count => clips.Values.Sum((List l) => l.Count); public static IEnumerable Keys => clips.Keys; public static string Norm(string s) { if (string.IsNullOrEmpty(s)) { return ""; } StringBuilder stringBuilder = new StringBuilder(s.Length); foreach (char c in s) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } public static void Load(string modFolder) { //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Expected O, but got Unknown clips.Clear(); string[] array = new string[2] { "items", "ui" }; foreach (string pool in array) { string[] array2 = ClipLayout.Files(modFolder, pool); foreach (string text in array2) { string text2 = FlatClips.Strip(ClipLayout.Stem(text), out var isFlat); int num = text2.IndexOf("__", StringComparison.Ordinal); if (num > 0) { text2 = text2.Substring(0, num); } int num2 = text2.IndexOf('_'); text2 = ((num2 > 0) ? (text2.Substring(0, num2).ToLowerInvariant() + "|" + Norm(text2.Substring(num2 + 1))) : Norm(text2)); AudioClip val = WavLoader.Load(text); if (!((Object)(object)val == (Object)null)) { FlatClips.Register(val, isFlat); if (!clips.TryGetValue(text2, out var value)) { value = (clips[text2] = new List()); } value.Add(val); Plugin.Log.LogInfo((object)("[Items] " + Path.GetFileName(text) + " -> " + text2)); } } } if (clips.Count > 0) { sfx = ScriptableObject.CreateInstance(); sfx.clips = (AudioClip[])(object)new AudioClip[0]; sfx.settings = new SFX_Settings(); } Plugin.Log.LogInfo((object)$"[Items] {Count} clip(s) over {clips.Count} key(s)."); } public static bool Has(string verb, string itemName) { return (Object)(object)sfx != (Object)null && clips.ContainsKey(verb + "|" + Norm(itemName)); } public static void Play(string verb, string itemName, Vector3 pos, Transform follow = null) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)sfx == (Object)null)) { StateSounds.Schedule(verb + "_" + Norm(itemName), pos, follow); PlayKey(verb + "|" + Norm(itemName), pos, follow); } } public static void PlayKey(string key, Vector3 pos, Transform follow = null) { //IL_015e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sfx == (Object)null) { return; } if (!clips.TryGetValue(key, out var value) || value.Count == 0) { if (Plugin.BoundConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)("[Items] no clip for '" + key + "'")); } return; } sfx.settings.volume = Plugin.BoundConfig.MasterVolume.Value; sfx.settings.range = 120f; sfx.settings.volume_Variation = 0f; sfx.settings.pitch = 1f; sfx.settings.pitch_Variation = 0f; sfx.settings.dopplerLevel = 0f; AudioClip val = value[Random.Range(0, value.Count)]; if (PlayGate.TryTake(key, follow, val)) { sfx.settings.spatialBlend = FlatClips.Blend(val, 0.8f); sfx.clips = (AudioClip[])(object)new AudioClip[1] { val }; sfx.settings.volume = ClipVolumes.Scaled(val); if (!SoundBus.Play(sfx, val, pos, follow, SoundLane.Normal)) { Plugin.Log.LogInfo((object)$"[Items] {key} held back; {SoundBus.Holder} has the floor"); } else { Plugin.Log.LogInfo((object)("[Items] " + key + " played")); } } } public static void PlayBare(string key, Vector3 pos, Transform follow = null) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sfx == (Object)null) { return; } StateSounds.Schedule(key.Replace('|', '_'), pos, follow); if (!clips.TryGetValue(key, out var value) || value.Count == 0) { return; } sfx.settings.volume = Plugin.BoundConfig.MasterVolume.Value; sfx.settings.range = 120f; sfx.settings.volume_Variation = 0f; sfx.settings.pitch = 1f; sfx.settings.pitch_Variation = 0f; sfx.settings.dopplerLevel = 0f; AudioClip val = value[Random.Range(0, value.Count)]; if (PlayGate.TryTake(key, follow, val)) { sfx.settings.spatialBlend = FlatClips.Blend(val, 0.8f); sfx.clips = (AudioClip[])(object)new AudioClip[1] { val }; sfx.settings.volume = ClipVolumes.Scaled(val); if (!SoundBus.Play(sfx, val, pos, follow, SoundLane.Normal)) { Plugin.Log.LogInfo((object)$"[Items] {key} held back; {SoundBus.Holder} has the floor"); } else { Plugin.Log.LogInfo((object)("[Items] " + key + " played")); } } } } [HarmonyPatch(typeof(Item), "FinishCastPrimary")] public static class PatchItemUse { private static void Postfix(Item __instance) { try { if (!((Object)(object)__instance == (Object)null) && __instance.UIData != null) { Character holderCharacter = __instance.holderCharacter; SoundNet.Fire(SoundPool.Items, "use|" + ItemSounds.Norm(__instance.UIData.itemName), holderCharacter); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Items] {arg}"); } } } internal static class HeldItemWatcher { private static string last = ""; internal static void Tick() { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null) { return; } Item currentItem = localCharacter.data.currentItem; string text = (((Object)(object)currentItem != (Object)null && currentItem.UIData != null) ? currentItem.UIData.itemName : ""); if (!(text == last)) { last = text; if (!string.IsNullOrEmpty(text)) { SoundNet.Fire(SoundPool.Items, "equip|" + ItemSounds.Norm(text), localCharacter); } } } } [HarmonyPatch(typeof(ItemCooking), "FinishCookingRPC")] public static class PatchItemCooked { private static void Postfix(ItemCooking __instance) { //IL_0050: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) try { Item val = (((Object)(object)__instance != (Object)null) ? ((ItemComponent)__instance).item : null); if (!((Object)(object)val == (Object)null) && val.UIData != null) { Character holderCharacter = val.holderCharacter; Vector3 pos = (((Object)(object)holderCharacter != (Object)null) ? holderCharacter.Center : ((Component)val).transform.position); Transform follow = (((Object)(object)holderCharacter != (Object)null) ? SoundSource.Of(holderCharacter) : ((Component)val).transform); ItemSounds.Play("cook", val.UIData.itemName, pos, follow); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Items] {arg}"); } } } [HarmonyPatch(typeof(PassportManager), "OnClose")] public static class PatchPassportClose { private static void Postfix() { try { Character localCharacter = Character.localCharacter; SoundNet.Fire(SoundPool.Items, "exit|passport", localCharacter); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Items] {arg}"); } } } internal static class LoadingJingle { public const string Key = "run_start"; public const string MusicTag = "Music"; private static AudioSource speaker; private static readonly HashSet reported = new HashSet(); internal static LoadingScreenType pending = (LoadingScreenType)0; private static bool singing; private static AudioSource Speaker() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown if ((Object)(object)speaker != (Object)null) { return speaker; } if ((Object)(object)Plugin.Instance == (Object)null) { return null; } GameObject val = new GameObject("CarlLoadingJingle"); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; speaker = val.AddComponent(); speaker.playOnAwake = false; speaker.loop = false; speaker.spatialBlend = 0f; speaker.dopplerLevel = 0f; speaker.ignoreListenerPause = true; speaker.outputAudioMixerGroup = null; speaker.bypassEffects = true; speaker.bypassListenerEffects = true; speaker.bypassReverbZones = true; return speaker; } public static void Take(LoadingScreen screen) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_006e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)screen == (Object)null || Plugin.BoundConfig == null || !Plugin.BoundConfig.Enabled.Value) { return; } if ((int)pending != 1) { if (Plugin.BoundConfig.DebugLog.Value) { Plugin.Log.LogInfo((object)$"[Jingle] {pending} screen; left alone."); } return; } JingleWatcher.watchUntil = Time.unscaledTime + 3f; bool flag = (Object)(object)screen != (Object)null && reported.Add(((Object)screen).GetInstanceID()); bool flag2 = Plugin.BoundConfig.DebugLog.Value && flag; int num = 0; foreach (AudioSource item in TaggedMusic(flag2)) { if (!((Object)(object)item == (Object)null)) { if (item.isPlaying) { item.Stop(); } item.volume = 0f; num++; } } AudioSource[] componentsInChildren = ((Component)screen).GetComponentsInChildren(true); foreach (AudioSource val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !val.loop) { if (flag2) { Plugin.Log.LogInfo((object)("[Jingle] child " + ((Object)val).name + " clip=" + string.Format("{0} playing={1}", ((Object)(object)val.clip != (Object)null) ? ((Object)val.clip).name : "none", val.isPlaying))); } if (val.isPlaying) { val.Stop(); } val.volume = 0f; num++; } } AudioClip[] array = StateSounds.Clips("run_start"); if (array == null || array.Length == 0) { Plugin.Log.LogWarning((object)(string.Format("[Jingle] hushed {0} source(s) but found NO '{1}' clip in ", num, "run_start") + "state\\ - the loading screen will be silent. " + $"StateSounds holds {StateSounds.Count} clip(s).")); return; } AudioSource val2 = Speaker(); if (!((Object)(object)val2 == (Object)null)) { AudioClip val3 = (val2.clip = array[Random.Range(0, array.Length)]); val2.volume = ClipVolumes.Scaled(val3); val2.pitch = 1f; val2.Play(); singing = true; Plugin.Log.LogInfo((object)(string.Format("[Jingle] {0} -> '{1}' {2:0.0}s at vol {3:0.00} ", "run_start", ((Object)val3).name, val3.length, val2.volume) + $"on {((Object)screen).name}; hushed {num} game source(s). " + $"playing={val2.isPlaying}")); } } internal static void EndWithScreen(float step) { if (singing && !((Object)(object)speaker == (Object)null) && !LoadingScreenHandler.loading) { AudioSource obj = speaker; obj.volume -= step; if (!(speaker.volume > 0f)) { speaker.Stop(); speaker.volume = 0f; singing = false; Plugin.Log.LogInfo((object)"[Jingle] loading finished; faded out."); } } } private static IEnumerable TaggedMusic(bool log) { GameObject[] tagged; try { tagged = GameObject.FindGameObjectsWithTag("Music"); } catch (Exception ex) { Exception ex2 = ex; if (log) { Plugin.Log.LogInfo((object)("[Jingle] no 'Music' tag: " + ex2.Message)); } yield break; } GameObject[] array = tagged; foreach (GameObject go in array) { if ((Object)(object)go == (Object)null) { continue; } AudioSource[] componentsInChildren = go.GetComponentsInChildren(true); foreach (AudioSource src in componentsInChildren) { if (log && (Object)(object)src != (Object)null) { Plugin.Log.LogInfo((object)("[Jingle] music " + ((Object)go).name + "/" + ((Object)src).name + " clip=" + (((Object)(object)src.clip != (Object)null) ? ((Object)src.clip).name : "none") + $" playing={src.isPlaying} loop={src.loop}")); } yield return src; } } } } [HarmonyPatch(typeof(LoadingScreenHandler), "GetLoadingScreenPrefab")] public static class PatchLoadingScreenType { private static void Postfix(LoadingScreenType type) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) LoadingJingle.pending = type; } } [HarmonyPatch(typeof(LoadingScreen), "Awake")] public static class PatchLoadingScreen { private static void Postfix(LoadingScreen __instance) { try { LoadingJingle.Take(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Jingle] {arg}"); } } } internal class JingleWatcher : MonoBehaviour { internal static float watchUntil; private const float FadeOut = 0.6f; private void Update() { LoadingJingle.EndWithScreen(Time.unscaledDeltaTime / 0.6f); if (Time.unscaledTime > watchUntil) { return; } try { GameObject[] array = GameObject.FindGameObjectsWithTag("Music"); foreach (GameObject val in array) { if ((Object)(object)val == (Object)null) { continue; } AudioSource[] componentsInChildren = val.GetComponentsInChildren(true); foreach (AudioSource val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && val2.isPlaying) { val2.Stop(); val2.volume = 0f; Plugin.Log.LogInfo((object)("[Jingle] late music " + ((Object)val).name + "/" + ((Object)val2).name + " hushed.")); } } } } catch { } } } internal static class LobbyMusic { public const string Key = "lobby_music"; public const string AirportScene = "Airport"; public const string MusicTag = "Music"; private static AudioSource speaker; private static bool wasInAirport; private static float nextSweep; private static AudioSource Speaker() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown if ((Object)(object)speaker != (Object)null) { return speaker; } if ((Object)(object)Plugin.Instance == (Object)null) { return null; } GameObject val = new GameObject("CarlLobbyMusic"); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; speaker = val.AddComponent(); speaker.playOnAwake = false; speaker.loop = true; speaker.spatialBlend = 0f; speaker.dopplerLevel = 0f; speaker.bypassEffects = true; speaker.bypassListenerEffects = true; speaker.bypassReverbZones = true; return speaker; } internal static void Tick() { //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) if (Plugin.BoundConfig == null) { return; } int num; if (Plugin.BoundConfig.Enabled.Value) { Scene activeScene = SceneManager.GetActiveScene(); num = ((((Scene)(ref activeScene)).name == "Airport") ? 1 : 0); } else { num = 0; } bool flag = (byte)num != 0; if (flag != wasInAirport) { wasInAirport = flag; if (!flag) { Stop(); } } if (flag && !(Time.unscaledTime < nextSweep)) { nextSweep = Time.unscaledTime + 1f; if (StateSounds.Clips("lobby_music") != null) { Hush(); Resume(); } } } private static void Hush() { try { GameObject[] array = GameObject.FindGameObjectsWithTag("Music"); foreach (GameObject val in array) { if ((Object)(object)val == (Object)null) { continue; } AudioSource[] componentsInChildren = val.GetComponentsInChildren(true); foreach (AudioSource val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)speaker)) { if (val2.isPlaying) { val2.Stop(); } val2.volume = 0f; } } } } catch { } } private static void Resume() { AudioClip[] array = StateSounds.Clips("lobby_music"); if (array == null || array.Length == 0) { return; } AudioSource val = Speaker(); if (!((Object)(object)val == (Object)null)) { if ((Object)(object)val.clip == (Object)null) { val.clip = array[Random.Range(0, array.Length)]; } val.volume = ClipVolumes.Scaled(val.clip); if (!val.isPlaying) { val.Play(); Plugin.Log.LogInfo((object)("[Lobby] lobby_music -> '" + ((Object)val.clip).name + "' looping in the airport.")); } } } private static void Stop() { if (!((Object)(object)speaker == (Object)null)) { if (speaker.isPlaying) { speaker.Stop(); } speaker.clip = null; Plugin.Log.LogInfo((object)"[Lobby] left the airport; music stopped."); } } } internal class LobbyMusicTicker : MonoBehaviour { private void Update() { try { LobbyMusic.Tick(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Lobby] {arg}"); } } } internal static class PlayGate { public const float AudibleOverlap = 0.5f; private static readonly Dictionary until = new Dictionary(); private static float nextSweep; public static float TailOverlap => 0.65f; private static long Id(string key, Transform owner) { int num = (((Object)(object)owner != (Object)null) ? ((Object)owner).GetInstanceID() : 0); return ((long)num << 32) ^ (uint)key.GetHashCode(); } public static bool TryTake(string key, Transform owner, AudioClip clip) { if (string.IsNullOrEmpty(key) || (Object)(object)clip == (Object)null) { return true; } float time = Time.time; Sweep(time); long key2 = Id(key, owner); if (until.TryGetValue(key2, out var value) && time < value) { return false; } float num = clip.length - TailOverlap; if (num <= 0f) { return true; } until[key2] = time + num; return true; } private static void Sweep(float now) { if (now < nextSweep) { return; } nextSweep = now + 30f; List list = null; foreach (KeyValuePair item in until) { if (now >= item.Value) { (list ?? (list = new List())).Add(item.Key); } } if (list == null) { return; } foreach (long item2 in list) { until.Remove(item2); } } public static void Clear() { until.Clear(); } } public class ModConfig { public readonly ConfigEntry Enabled; public readonly ConfigEntry MasterVolume; public readonly ConfigEntry DebugLog; public readonly ConfigEntry DumpSounds; public readonly ConfigEntry ChatterEnabled; public readonly ConfigEntry ChatterMinSeconds; public readonly ConfigEntry ChatterMaxSeconds; public ModConfig(ConfigFile cfg) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown cfg.SaveOnConfigSet = false; Enabled = cfg.Bind("CarlUnleashedMod", "Enabled", true, "Master switch for the whole mod."); MasterVolume = cfg.Bind("CarlUnleashedMod", "Master Volume", 0.5f, new ConfigDescription("Scales every Carl clip. They are all loudness-matched to each other, so this moves them together against the game's own audio.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ChatterEnabled = cfg.Bind("Chatter", "Idle Chatter", true, "Every so often somebody bursts into song. The pool is every favourited clip with no context assigned, so it fills up as clips are picked out and empties as they get tagged for a specific moment."); ChatterMinSeconds = cfg.Bind("Chatter", "Min Seconds", 40f, new ConfigDescription("Shortest wait between chatter lines. ONLY THE HOST'S VALUE MATTERS - the host schedules for the whole room, so your own setting does nothing unless you are hosting. Drop both to a second or two to audition the pool.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 600f), Array.Empty())); ChatterMaxSeconds = cfg.Bind("Chatter", "Max Seconds", 100f, new ConfigDescription("Longest wait between chatter lines, host's value only. Each wait is picked at random between the two, then shortened slightly in a fuller lobby to offset how often other sounds crowd chatter out. A value below the minimum is treated as equal to it.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 600f), Array.Empty())); DebugLog = cfg.Bind("Debug", "Debug Log", false, "Log every sound the game plays, with its name and pitch variation. Use it to find the exact name of a sound worth replacing."); DumpSounds = cfg.Bind("Debug", "Dump Sounds", false, "On next launch, export every sound effect in the game to a 'dumped_sounds' folder as .wav, then set this back to false."); ClearOrphanedEntries(cfg); cfg.Save(); cfg.SaveOnConfigSet = true; } private static void ClearOrphanedEntries(ConfigFile cfg) { PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries"); Dictionary dictionary = (Dictionary)propertyInfo.GetValue(cfg); dictionary.Clear(); } } [BepInPlugin("CarlUnleashedMod", "CarlUnleashedMod", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string Id = "CarlUnleashedMod"; private readonly Harmony harmony = new Harmony("CarlUnleashedMod"); internal static ManualLogSource Log { get; private set; } internal static Plugin Instance { get; private set; } internal static ModConfig BoundConfig { get; private set; } private void Awake() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)this); return; } Instance = this; Log = ((BaseUnityPlugin)this).Logger; BoundConfig = new ModConfig(((BaseUnityPlugin)this).Config); string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); DownedCues.Load(directoryName); ProgressSounds.Load(directoryName); ItemSounds.Load(directoryName); BingBongSounds.Load(directoryName); StateSounds.Load(directoryName); ChatterSounds.Load(directoryName); BingBongFace.Load(directoryName); GameObject val = new GameObject("CarlUnleashed"); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent(); val.AddComponent(); val.AddComponent(); val.AddComponent(); val.AddComponent(); val.AddComponent(); val.AddComponent(); val.AddComponent(); val.AddComponent(); harmony.PatchAll(); ItHurtsBridge.Apply(harmony); Log.LogInfo((object)($"CarlUnleashedMod loaded: {StateSounds.Count} state, {ItemSounds.Count} item, " + $"{ProgressSounds.Count} area, {DownedCues.Count} cue, " + $"{BingBongSounds.Count} Bing Bong clip(s). " + $"{FlatClips.Count} flagged 2D.")); ((MonoBehaviour)this).StartCoroutine(ItemAudit.RunLater()); if (BoundConfig.DumpSounds.Value) { ((MonoBehaviour)this).StartCoroutine(DumpAllSoundsDelayed()); } } private IEnumerator DumpAllSoundsDelayed() { yield return null; yield return null; yield return null; DumpAllSounds(); } private void DumpAllSounds() { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string text = Path.Combine(directoryName, "dumped_sounds"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } SFX_Instance[] array = Resources.FindObjectsOfTypeAll(); Log.LogInfo((object)$"[Dump] Found {array.Length} SFX_Instance objects. Dumping..."); int num = 0; int num2 = 0; HashSet hashSet = new HashSet(); SFX_Instance[] array2 = array; foreach (SFX_Instance val in array2) { if ((Object)(object)val == (Object)null || val.clips == null || val.clips.Length == 0) { continue; } string text2 = ((Object)val).name ?? "unknown"; for (int j = 0; j < val.clips.Length; j++) { AudioClip val2 = val.clips[j]; if ((Object)(object)val2 == (Object)null) { continue; } string text3 = ((Object)val2).name ?? $"clip{j}"; string text4 = ((val.clips.Length > 1) ? $"{text2}_{text3}_{j}.wav" : (text2 + "_" + text3 + ".wav")); char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text4 = text4.Replace(oldChar, '_'); } if (hashSet.Add(text4)) { try { WriteClipToWav(val2, Path.Combine(text, text4)); num++; } catch (Exception ex) { Log.LogWarning((object)("[Dump] Failed to export '" + text4 + "': " + ex.Message)); num2++; } } } } Log.LogInfo((object)$"[Dump] Done. Exported {num} clips to '{text}' ({num2} failed)."); Log.LogInfo((object)"[Dump] Set 'Dump Sounds' back to false to stop dumping on next launch."); } private static void WriteClipToWav(AudioClip clip, string filePath) { int num = clip.samples * clip.channels; float[] array = new float[num]; if (!clip.GetData(array, 0)) { throw new Exception("AudioClip.GetData returned false (clip may be compressed or streaming)."); } int frequency = clip.frequency; int channels = clip.channels; int value = frequency * channels * 2; int num2 = channels * 2; int num3 = num * 2; using FileStream output = new FileStream(filePath, FileMode.Create); using BinaryWriter binaryWriter = new BinaryWriter(output); binaryWriter.Write(new char[4] { 'R', 'I', 'F', 'F' }); binaryWriter.Write(36 + num3); binaryWriter.Write(new char[4] { 'W', 'A', 'V', 'E' }); binaryWriter.Write(new char[4] { 'f', 'm', 't', ' ' }); binaryWriter.Write(16); binaryWriter.Write((short)1); binaryWriter.Write((short)channels); binaryWriter.Write(frequency); binaryWriter.Write(value); binaryWriter.Write((short)num2); binaryWriter.Write((short)16); binaryWriter.Write(new char[4] { 'd', 'a', 't', 'a' }); binaryWriter.Write(num3); for (int i = 0; i < num; i++) { float num4 = Mathf.Clamp(array[i], -1f, 1f); binaryWriter.Write((short)(num4 * 32767f)); } } } internal static class ProgressSounds { public const string FolderName = "progress"; internal const byte ClaimEvent = 79; private static readonly Dictionary> clips = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static SFX_Instance sfx; private static readonly HashSet claimed = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly HashSet announced = new HashSet(StringComparer.OrdinalIgnoreCase); public static int Count => clips.Values.Sum((List l) => l.Count); public static string Key(string title) { if (string.IsNullOrEmpty(title)) { return ""; } StringBuilder stringBuilder = new StringBuilder(title.Length); foreach (char c in title) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } public static void Load(string modFolder) { //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown clips.Clear(); claimed.Clear(); announced.Clear(); string[] array = ClipLayout.Files(modFolder, "progress"); if (array.Length == 0) { Plugin.Log.LogInfo((object)"[Progress] no 'progress' clips; area sounds off."); return; } int num = 0; string[] array2 = array; foreach (string text in array2) { bool isFlat; string title = FlatClips.Strip(ClipLayout.Stem(text), out isFlat); if (isFlat) { num++; } string text2 = Key(title); AudioClip val = WavLoader.Load(text); if (!((Object)(object)val == (Object)null)) { if (!clips.TryGetValue(text2, out var value)) { value = (clips[text2] = new List()); } value.Add(val); Plugin.Log.LogInfo((object)("[Progress] " + Path.GetFileName(text) + " -> " + text2)); } } if (clips.Count > 0) { sfx = ScriptableObject.CreateInstance(); sfx.clips = (AudioClip[])(object)new AudioClip[0]; sfx.settings = new SFX_Settings(); } Plugin.Log.LogInfo((object)$"[Progress] {Count} clip(s) across {clips.Count} area(s)."); if (num > 0) { Plugin.Log.LogInfo((object)($"[Progress] {num} clip(s) carry the 2D flag; ignored here, since an " + "area line is sung by the player who got there first.")); } } public static void NewRun() { if (claimed.Count > 0 || announced.Count > 0) { Plugin.Log.LogInfo((object)$"[Progress] new run; forgetting {claimed.Count} claimed area(s)."); } claimed.Clear(); announced.Clear(); } public static bool Has(string title) { if ((Object)(object)sfx == (Object)null) { return false; } string k = Key(title); return clips.ContainsKey(k) || clips.Keys.Any((string x) => k.EndsWith(x) || k.StartsWith(x)); } private static List Pool(string title) { string k = Key(title); if (clips.TryGetValue(k, out var value)) { return value; } string text = clips.Keys.FirstOrDefault((string x) => k.EndsWith(x) || k.StartsWith(x)); return (text == null) ? null : clips[text]; } public static void Announce(string title) { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown if ((Object)(object)sfx == (Object)null || !Plugin.BoundConfig.Enabled.Value || !Has(title)) { return; } Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return; } string text = Key(title); if (announced.Add(text)) { int num = (((Object)(object)((MonoBehaviourPun)localCharacter).photonView != (Object)null) ? ((MonoBehaviourPun)localCharacter).photonView.ViewID : 0); if (PhotonNetwork.OfflineMode || !PhotonNetwork.InRoom) { Resolve(text, localCharacter); return; } PhotonNetwork.RaiseEvent((byte)79, (object)new object[2] { text, num }, new RaiseEventOptions { Receivers = (ReceiverGroup)1, CachingOption = (EventCaching)4 }, SendOptions.SendReliable); } } internal static void Resolve(string key, Character singer) { if (!string.IsNullOrEmpty(key) && claimed.Add(key)) { Play(key, singer); } } private static void Play(string key, Character singer) { //IL_0143: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sfx == (Object)null) { return; } List list = Pool(key); if (list != null && list.Count != 0) { sfx.settings.volume = Plugin.BoundConfig.MasterVolume.Value; sfx.settings.range = 500f; sfx.settings.volume_Variation = 0f; sfx.settings.pitch = 1f; sfx.settings.pitch_Variation = 0f; sfx.settings.dopplerLevel = 0f; sfx.settings.spatialBlend = 0.8f; AudioClip val = list[Random.Range(0, list.Count)]; sfx.clips = (AudioClip[])(object)new AudioClip[1] { val }; sfx.settings.volume = ClipVolumes.Scaled(val); if ((Object)(object)singer == (Object)null) { Plugin.Log.LogInfo((object)("[Progress] " + key + " claimed, but the singer is gone.")); return; } SoundBus.Play(sfx, sfx.clips[0], singer.Center, SoundSource.Of(singer), SoundLane.Progress); Plugin.Log.LogInfo((object)("[Progress] " + key + " sung by " + singer.characterName + (singer.IsLocal ? " (you)" : ""))); } } } internal class ProgressClaimSync : MonoBehaviour, IOnEventCallback { private void OnEnable() { PhotonNetwork.AddCallbackTarget((object)this); } private void OnDisable() { PhotonNetwork.RemoveCallbackTarget((object)this); } public void OnEvent(EventData photonEvent) { if (photonEvent.Code != 79) { return; } try { if (photonEvent.CustomData is object[] array && array.Length >= 2) { string key = array[0] as string; int num = Convert.ToInt32(array[1]); Character singer = null; Character.GetCharacterWithPhotonID(num, ref singer); ProgressSounds.Resolve(key, singer); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Progress] {arg}"); } } } [HarmonyPatch(typeof(MountainProgressHandler), "Start")] public static class PatchProgressNewRun { private static void Postfix() { try { ProgressSounds.NewRun(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Progress] {arg}"); } } } [HarmonyPatch(typeof(MountainProgressHandler), "TriggerReached")] public static class PatchTriggerReached { private static void Prefix(ProgressPoint progressPoint, ref AudioClip __state) { __state = null; try { if (progressPoint != null && Plugin.BoundConfig.Enabled.Value && ProgressSounds.Has(progressPoint.title)) { __state = progressPoint.clip; progressPoint.clip = null; } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Progress] {arg}"); } } private static void Postfix(ProgressPoint progressPoint, bool isFurthestPoint, AudioClip __state) { try { if (progressPoint != null) { if ((Object)(object)__state != (Object)null) { progressPoint.clip = __state; } if (!(Time.time <= 2f) && isFurthestPoint) { ProgressSounds.Announce(progressPoint.title); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"[Progress] {arg}"); } } } internal enum SoundLane : byte { Normal, Chatter, Progress } internal static class SoundBus { private static SoundLane lane; private static float until; private static SoundEffectHandle current; public static bool Busy => Time.time < until; public static SoundLane Holder => lane; public static float Remaining => Mathf.Max(0f, until - Time.time); public static bool Allows(SoundLane want) { if (!Busy) { return true; } return want switch { SoundLane.Progress => true, SoundLane.Chatter => false, _ => lane == SoundLane.Normal, }; } public static bool Play(SFX_Instance sfx, AudioClip clip, Vector3 pos, Transform follow, SoundLane want) { //IL_0072: 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) if ((Object)(object)sfx == (Object)null || (Object)(object)clip == (Object)null) { return false; } if (!Allows(want)) { return false; } if (want == SoundLane.Progress) { StopCurrent(); } if ((Object)(object)SFX_Player.instance != (Object)null) { current = SFX_Player.instance.PlaySFX(sfx, pos, follow, (SFX_Settings)null, 1f, false); } else { sfx.Play(pos, follow); } float num = Time.time + Mathf.Max(0.05f, clip.length - 0.15f); if (want != SoundLane.Normal) { lane = want; until = num; } else if (!Busy || lane == SoundLane.Normal) { lane = SoundLane.Normal; until = Mathf.Max(until, num); } return true; } private static void StopCurrent() { if (current != null) { try { SFX_Player.StopPlaying(current, 0.15f); } catch { } current = null; } } public static void Clear() { current = null; until = 0f; lane = SoundLane.Normal; } } internal enum SoundPool : byte { State, Items } internal static class SoundNet { internal const byte Code = 81; public static void Fire(SoundPool pool, string key, Character who) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown if (string.IsNullOrEmpty(key) || Plugin.BoundConfig == null || !Plugin.BoundConfig.Enabled.Value) { return; } if (PhotonNetwork.OfflineMode || !PhotonNetwork.InRoom) { Deliver((byte)pool, key, who); return; } if ((Object)(object)who == (Object)null) { who = Character.localCharacter; } int num = (((Object)(object)who != (Object)null && (Object)(object)((MonoBehaviourPun)who).photonView != (Object)null) ? ((MonoBehaviourPun)who).photonView.ViewID : 0); PhotonNetwork.RaiseEvent((byte)81, (object)new object[3] { (byte)pool, key, num }, new RaiseEventOptions { Receivers = (ReceiverGroup)1 }, SendOptions.SendReliable); } internal static void Deliver(byte pool, string key, Character who) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)who == (Object)null)) { Vector3 center = who.Center; Transform follow = SoundSource.Of(who); if (pool == 1) { StateSounds.Schedule(key.Replace('|', '_'), center, follow); ItemSounds.PlayKey(key, center, follow); } else { StateSounds.PlayAt(key, center, -1f, follow); } } } } internal class SoundNetWatcher : MonoBehaviour, IOnEventCallback { private void OnEnable() { PhotonNetwork.AddCallbackTarget((object)this); } private void OnDisable() { PhotonNetwork.RemoveCallbackTarget((object)this); } public void OnEvent(EventData photonEvent) { if (photonEvent.Code != 81) { return; } try { if (photonEvent.CustomData is object[] array && array.Length >= 3) { byte pool = Convert.ToByte(array[0]); string key = array[1] as string; int num = Convert.ToInt32(array[2]); Character who = default(Character); Character.GetCharacterWithPhotonID(num, ref who); SoundNet.Deliver(pool, key, who); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[SoundNet] {arg}"); } } } internal static class SoundSource { public const float Doppler = 0f; public static Transform Local => Of(Character.localCharacter); public static Transform Of(Character c) { if ((Object)(object)c == (Object)null) { return null; } CharacterRefs refs = c.refs; if (refs != null) { if ((Object)(object)refs.head != (Object)null) { return refs.head.transform; } if ((Object)(object)refs.hip != (Object)null) { return refs.hip.transform; } } return ((Component)c).transform; } } internal static class StateSounds { private class Pending { public float At; public string Key; public Vector3 Pos; public Transform Follow; } public const string FolderName = "state"; private const string PoolSep = "__"; private static readonly Dictionary> clips = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static SFX_Instance sfx; private static readonly Regex DelayRe = new Regex("^(\\d+(?:\\.\\d+)?)s_delay_(.+)$", RegexOptions.IgnoreCase); private static readonly List pending = new List(); internal static float lastGlideStamp = -999f; private static float glidingSince = -1f; private static bool glideFired; private static StatusFieldGloom gloomField; private static float gloomRescan = -999f; private static bool inGloom; private static bool stamFired; private static float climbSince = -1f; private static FieldInfo fiActiveFor; private static bool blizzardFired; private static WindChillZone[] windZones; private static float windRescan = -999f; public static int Count => clips.Values.Sum((List l) => l.Count); public static void Load(string modFolder) { //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Expected O, but got Unknown clips.Clear(); string[] array = ClipLayout.Files(modFolder, "state"); if (array.Length == 0) { Plugin.Log.LogInfo((object)"[State] no 'state' clips; state sounds off."); return; } string[] array2 = array; foreach (string text in array2) { bool isFlat; string text2 = FlatClips.Strip(ClipLayout.Stem(text), out isFlat); int num = text2.IndexOf("__", StringComparison.Ordinal); if (num > 0) { text2 = text2.Substring(0, num); } AudioClip val = WavLoader.Load(text); if (!((Object)(object)val == (Object)null)) { FlatClips.Register(val, isFlat); if (!clips.TryGetValue(text2, out var value)) { value = (clips[text2] = new List()); } value.Add(val); Plugin.Log.LogInfo((object)("[State] " + Path.GetFileName(text) + " -> " + text2)); } } if (clips.Count > 0) { sfx = ScriptableObject.CreateInstance(); sfx.clips = (AudioClip[])(object)new AudioClip[0]; sfx.settings = new SFX_Settings(); } Plugin.Log.LogInfo((object)$"[State] {Count} clip(s) over {clips.Count} state(s)."); } public static bool Has(string key) { return clips.ContainsKey(key); } public static void Schedule(string evt, Vector3 pos, Transform follow = null) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sfx == (Object)null || string.IsNullOrEmpty(evt)) { return; } foreach (string key in clips.Keys) { Match match = DelayRe.Match(key); if (match.Success) { string value = match.Groups[2].Value; if (Related(evt, value) && float.TryParse(match.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { pending.Add(new Pending { At = Time.time + result, Key = key, Pos = pos, Follow = follow }); Plugin.Log.LogInfo((object)$"[State] {key} queued for {result:0.#}s"); } } } } private static string Bare(string s) { if (string.IsNullOrEmpty(s)) { return ""; } StringBuilder stringBuilder = new StringBuilder(s.Length); foreach (char c in s) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } private static bool Related(string evt, string base_) { string text = Bare(evt); string text2 = Bare(base_); if (text.Length == 0 || text2.Length == 0) { return false; } return text.IndexOf(text2, StringComparison.Ordinal) >= 0 || text2.IndexOf(text, StringComparison.Ordinal) >= 0; } private static void TickPending() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (pending.Count == 0) { return; } float time = Time.time; for (int num = pending.Count - 1; num >= 0; num--) { if (!(time < pending[num].At)) { PlayAt(pending[num].Key, pending[num].Pos, -1f, pending[num].Follow); pending.RemoveAt(num); } } } public static void PlayAt(string key, Vector3 pos, float rangeOverride = -1f, Transform follow = null, float blendOverride = -1f, SoundLane lane = SoundLane.Normal) { //IL_0145: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sfx == (Object)null || !clips.TryGetValue(key, out var value) || value.Count == 0) { return; } sfx.settings.volume = Plugin.BoundConfig.MasterVolume.Value; sfx.settings.range = ((rangeOverride > 0f) ? rangeOverride : 120f); sfx.settings.volume_Variation = 0f; sfx.settings.pitch = 1f; sfx.settings.pitch_Variation = 0f; sfx.settings.dopplerLevel = 0f; AudioClip val = value[Random.Range(0, value.Count)]; if (PlayGate.TryTake(key, follow, val)) { sfx.settings.spatialBlend = ((blendOverride >= 0f) ? blendOverride : FlatClips.Blend(val, 0.8f)); sfx.clips = (AudioClip[])(object)new AudioClip[1] { val }; sfx.settings.volume = ClipVolumes.Scaled(val); if (!SoundBus.Play(sfx, val, pos, follow, lane)) { Plugin.Log.LogInfo((object)$"[State] {key} held back; {SoundBus.Holder} has the floor"); } else { Plugin.Log.LogInfo((object)("[State] " + key + " played (positioned)")); } } } public static AudioClip[] Clips(string key) { List value; return (clips.TryGetValue(key, out value) && value.Count > 0) ? value.ToArray() : null; } public static void PlayFor(string key, Character who) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)who == (Object)null)) { PlayAt(key, who.Center, -1f, SoundSource.Of(who)); } } public static void Announce(string key) { Character localCharacter = Character.localCharacter; if (!((Object)(object)localCharacter == (Object)null)) { SoundNet.Fire(SoundPool.State, key, localCharacter); } } internal static bool IsLocalMovement(CharacterMovement m) { Character localCharacter = Character.localCharacter; return (Object)(object)m != (Object)null && (Object)(object)localCharacter != (Object)null && localCharacter.refs != null && localCharacter.refs.movement == m; } internal static void Tick() { //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Invalid comparison between Unknown and I4 if ((Object)(object)sfx == (Object)null) { return; } float time = Time.time; if (time - lastGlideStamp < 0.25f) { if (glidingSince < 0f) { glidingSince = time; glideFired = false; } if (!glideFired && time - glidingSince >= 5f) { glideFired = true; Announce("gliding_5s"); } } else { glidingSince = -1f; glideFired = false; } if (time - windRescan > 5f) { windRescan = time; windZones = Object.FindObjectsByType((FindObjectsSortMode)0); } if (fiActiveFor == null) { fiActiveFor = AccessTools.Field(typeof(WindChillZone), "hasBeenActiveFor"); } bool flag = false; if (windZones != null) { WindChillZone[] array = windZones; foreach (WindChillZone val in array) { if ((Object)(object)val == (Object)null || !val.windActive || (int)val.statusType != 2 || !val.localCharacterInsideBounds) { continue; } float num = 0f; if (fiActiveFor != null) { try { num = (float)fiActiveFor.GetValue(val); } catch { } } if (num >= 3f) { flag = true; break; } } } if (flag) { if (!blizzardFired) { blizzardFired = true; Announce("blizzard_3s"); } } else { blizzardFired = false; } TickPending(); HeldItemWatcher.Tick(); Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter != (Object)null && (Object)(object)localCharacter.data != (Object)null) { float num2 = 1f; try { num2 = Mathf.Max(0.0001f, localCharacter.GetMaxStamina()); } catch { } float num3 = localCharacter.data.TotalStamina / num2; bool flag2 = num3 < 0.05f; bool isClimbingAnything = localCharacter.data.isClimbingAnything; if (!isClimbingAnything) { climbSince = -1f; } else if (climbSince < 0f) { climbSince = Time.time; } bool flag3 = isClimbingAnything && Time.time - climbSince >= 1f; if (flag2 && flag3 && !stamFired) { stamFired = true; Announce("stamina_5pc"); } else if (!flag2) { stamFired = false; } } if ((Object)(object)gloomField == (Object)null && time - gloomRescan > 5f) { gloomRescan = time; gloomField = Object.FindAnyObjectByType(); } Character localCharacter2 = Character.localCharacter; if ((Object)(object)gloomField != (Object)null && (Object)(object)localCharacter2 != (Object)null) { bool flag4 = false; try { flag4 = ((StatusFieldBase)gloomField).CharacterInField(localCharacter2); } catch { } if (flag4 && !inGloom) { Announce("sleepy_gloom"); } inGloom = flag4; } } } internal class StateTicker : MonoBehaviour { private void Update() { try { StateSounds.Tick(); } catch (Exception arg) { Plugin.Log.LogError((object)$"[State] {arg}"); } try { ClipVolumes.Preview(); } catch (Exception arg2) { Plugin.Log.LogError((object)$"[Volume] {arg2}"); } } } [HarmonyPatch(typeof(CharacterMovement), "ApplyGlider")] public static class PatchApplyGlider { private static void Postfix(CharacterMovement __instance) { if (StateSounds.IsLocalMovement(__instance)) { StateSounds.lastGlideStamp = Time.time; } } } [HarmonyPatch(typeof(CharacterMovement), "ApplyParasolDrag")] public static class PatchApplyParasol { private static void Postfix(CharacterMovement __instance) { if (StateSounds.IsLocalMovement(__instance)) { StateSounds.lastGlideStamp = Time.time; } } } [HarmonyPatch(typeof(CharacterCarrying), "RPCA_StartCarry")] public static class PatchStartCarry { private static void Postfix(CharacterCarrying __instance) { try { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter != (Object)null && localCharacter.refs != null && (Object)(object)__instance != (Object)null && localCharacter.refs.carriying == __instance) { StateSounds.PlayFor("carrying_player", ((Component)__instance).GetComponentInParent()); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[State] {arg}"); } } } [HarmonyPatch(typeof(ScoutCannon), "RPCA_LaunchTarget")] public static class PatchCannonLaunch { private static void Postfix(int targetID) { try { PhotonView photonView = PhotonNetwork.GetPhotonView(targetID); Character val = (((Object)(object)photonView != (Object)null) ? ((Component)photonView).GetComponent() : null); if ((Object)(object)val != (Object)null) { StateSounds.PlayFor("cannon_launch", val); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[State] {arg}"); } } } [HarmonyPatch(typeof(MoraleBoost), "SpawnMoraleBoost")] public static class PatchMoraleBoost { private static void Postfix(bool __result, Vector3 origin) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) try { if (__result) { float timeSinceLevelLoad = Time.timeSinceLevelLoad; float num = 15f; if (timeSinceLevelLoad < num) { Plugin.Log.LogInfo((object)$"[State] morale_boost suppressed ({timeSinceLevelLoad:0.0}s < {num:0.0}s)"); } else { StateSounds.PlayAt("morale_boost", origin); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"[State] {arg}"); } } } [HarmonyPatch(typeof(CharacterAfflictions), "RPC_PetrifyInstantly")] public static class PatchRitualDagger { private static void Postfix(CharacterAfflictions __instance, bool killedByDagger) { //IL_0030: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) try { if (killedByDagger) { Character val = (((Object)(object)__instance != (Object)null) ? __instance.character : null); Vector3 pos = (((Object)(object)val != (Object)null) ? val.Center : Vector3.zero); StateSounds.PlayAt("death_ritual_dagger", pos, -1f, SoundSource.Of(val)); StateSounds.Schedule("death_ritual_dagger", pos, SoundSource.Of(val)); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[State] {arg}"); } } } [HarmonyPatch(typeof(Breakable), "RPC_BroadcastBreak")] public static class PatchEggCrack { private static void Postfix(Breakable __instance) { //IL_001f: 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) try { if (__instance is BreakableEgg) { StateSounds.PlayAt("egg_crack", ((Component)__instance).transform.position); StateSounds.Schedule("egg_crack", ((Component)__instance).transform.position); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[State] {arg}"); } } } [HarmonyPatch(typeof(Flare), "TriggerHelicopter")] public static class PatchHelicopter { private static bool fired; private static void Postfix(Flare __instance) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) try { if (!fired) { fired = true; Vector3 pos = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).transform.position : Vector3.zero); Item val = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponent() : null); Transform follow = null; if ((Object)(object)val != (Object)null && (Object)(object)val.holderCharacter != (Object)null) { pos = val.holderCharacter.Center; follow = SoundSource.Of(val.holderCharacter); } StateSounds.PlayAt("flare_end", pos, 400f, follow); Plugin.Log.LogInfo((object)"[State] flare_end (helicopter summoned)"); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[State] {arg}"); } } } [HarmonyPatch(typeof(CharacterGrabbing), "RPCA_StartReaching")] public static class PatchStartReaching { private static void Postfix(CharacterGrabbing __instance) { try { Character val = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponentInParent() : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.data == (Object)null) && !val.data.isClimbingAnything && val.data.isGrounded) { StateSounds.PlayFor("reach", val); } } catch (Exception arg) { Plugin.Log.LogError((object)$"[State] {arg}"); } } } [HarmonyPatch(typeof(Item), "Consume")] public static class PatchConsume { private static readonly HashSet Ordinary = new HashSet(new string[31] { "halfcoconut", "redcrispberry", "yellowcrispberry", "greencrispberry", "honeycomb", "scorchberry", "purplekingberry", "yellowkingberry", "greenkingberry", "yellowberrynana", "pinkberrynana", "blueberrynana", "brownberrynana", "yellowclusterberry", "redclusterberry", "blackclusterberry", "orangewinterberry", "yellowwinterberry", "redprickleberry", "goldprickleberry", "egg", "tick", "bugleshroom", "buttonshroom", "chubbyshroom", "clustershroom", "airlinefood", "biglollipop", "granolabar", "scoutcookies", "trailmix" }, StringComparer.OrdinalIgnoreCase); private static void Postfix(Item __instance, int consumerID) { //IL_0076: 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) try { if (!((Object)(object)__instance == (Object)null) && __instance.UIData != null) { string itemName = __instance.UIData.itemName; Character holderCharacter = default(Character); Character.GetCharacterWithPhotonID(consumerID, ref holderCharacter); if ((Object)(object)holderCharacter == (Object)null) { holderCharacter = __instance.holderCharacter; } if (ItemSounds.Has("consume", itemName)) { ItemSounds.Play("consume", itemName, ((Object)(object)holderCharacter != (Object)null) ? holderCharacter.Center : ((Component)__instance).transform.position, SoundSource.Of(holderCharacter)); } else if (Ordinary.Contains(ItemSounds.Norm(itemName))) { StateSounds.PlayFor("consume", holderCharacter); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"[State] {arg}"); } } } internal static class Tuning { public const float Range = 120f; public const float SpatialBlend = 0.8f; public const float HelicopterRange = 400f; public const float ProgressRange = 500f; public const float ChatterRange = 120f; public const float ReplacementCooldown = 0.25f; public const float GlideSeconds = 5f; public const float BlizzardSeconds = 3f; public const float DownedFirstSeconds = 2f; public const float DownedSecondSeconds = 10f; public const float LongFallSeconds = 4f; public const float TailPad = 0.15f; public const float ChatterCrowdLift = 0.25f; public const float StaminaFraction = 0.05f; public const float StaminaClimbSeconds = 1f; public const float MoraleIgnoreFirstSeconds = 15f; public const float FaceScale = 0.525f; public const float FaceRoll = -90f; public const float FaceDepth = 1.02f; public const float FaceCurvature = 0.9f; public const float FaceMouthLift = 0.2f; public const float FaceFallbackSign = 1f; public static readonly bool FaceHideLips = true; } internal static class WavLoader { public static string[] Files(string dir) { string[] files = Directory.GetFiles(dir, "*.wav"); string[] files2 = Directory.GetFiles(dir, "*.ogg"); if (files.Length == 0) { return files2; } if (files2.Length == 0) { return files; } string[] array = new string[files.Length + files2.Length]; files.CopyTo(array, 0); files2.CopyTo(array, files.Length); return array; } public static AudioClip Load(string file) { AudioClip val = null; if (file.EndsWith(".ogg", StringComparison.OrdinalIgnoreCase)) { val = ViaWebRequest(file, (AudioType)14); if ((Object)(object)val != (Object)null) { ClipVolumes.Register(val); } return val; } try { val = FromBytes(File.ReadAllBytes(file), ClipLayout.Stem(file)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Carl] " + Path.GetFileName(file) + ": " + ex.Message)); } if ((Object)(object)val == (Object)null) { val = ViaWebRequest(file, (AudioType)20); } if ((Object)(object)val != (Object)null) { ClipVolumes.Register(val); } return val; } private static AudioClip FromBytes(byte[] b, string name) { if (b == null || b.Length < 44) { throw new Exception("too short to be a wav"); } if (b[0] != 82 || b[1] != 73 || b[2] != 70 || b[3] != 70) { throw new Exception("not RIFF"); } if (b[8] != 87 || b[9] != 65 || b[10] != 86 || b[11] != 69) { throw new Exception("not WAVE"); } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = -1; int len = 0; int num6 = 12; while (num6 + 8 <= b.Length) { char c = (char)b[num6]; string text = c.ToString(); c = (char)b[num6 + 1]; string text2 = c.ToString(); c = (char)b[num6 + 2]; string text3 = c.ToString(); c = (char)b[num6 + 3]; string text4 = text + text2 + text3 + c; int num7 = BitConverter.ToInt32(b, num6 + 4); int num8 = num6 + 8; if (num7 < 0 || num8 + num7 > b.Length) { num7 = b.Length - num8; } if (text4 == "fmt ") { num = BitConverter.ToUInt16(b, num8); num2 = BitConverter.ToUInt16(b, num8 + 2); num3 = BitConverter.ToInt32(b, num8 + 4); num4 = BitConverter.ToUInt16(b, num8 + 14); if (num == 65534 && num7 >= 26) { num = BitConverter.ToUInt16(b, num8 + 24); } } else if (text4 == "data") { num5 = num8; len = num7; break; } num6 = num8 + num7 + (num7 & 1); } if (num5 < 0) { throw new Exception("no data chunk"); } if (num2 <= 0 || num3 <= 0) { throw new Exception("bad fmt chunk"); } float[] array = Decode(b, num5, len, num, num4); if (array == null) { throw new Exception($"unsupported format {num} at {num4} bits"); } int num9 = array.Length / num2; if (num9 <= 0) { throw new Exception("no samples"); } AudioClip val = AudioClip.Create(name, num9, num2, num3, false); val.SetData(array, 0); return val; } private static float[] Decode(byte[] b, int at, int len, int format, int bits) { if (format == 3 && bits == 32) { float[] array = new float[len / 4]; for (int i = 0; i < array.Length; i++) { array[i] = BitConverter.ToSingle(b, at + i * 4); } return array; } if (format != 1) { return null; } switch (bits) { case 8: { float[] array4 = new float[len]; for (int l = 0; l < array4.Length; l++) { array4[l] = (float)(b[at + l] - 128) / 128f; } return array4; } case 16: { float[] array3 = new float[len / 2]; for (int k = 0; k < array3.Length; k++) { array3[k] = (float)BitConverter.ToInt16(b, at + k * 2) / 32768f; } return array3; } case 24: { float[] array5 = new float[len / 3]; for (int m = 0; m < array5.Length; m++) { int num = at + m * 3; int num2 = b[num] | (b[num + 1] << 8) | (b[num + 2] << 16); if ((num2 & 0x800000) != 0) { num2 |= -16777216; } array5[m] = (float)num2 / 8388608f; } return array5; } case 32: { float[] array2 = new float[len / 4]; for (int j = 0; j < array2.Length; j++) { array2[j] = (float)BitConverter.ToInt32(b, at + j * 4) / 2.1474836E+09f; } return array2; } default: return null; } } private static AudioClip ViaWebRequest(string file, AudioType type) { //IL_001e: 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_0047: Invalid comparison between Unknown and I4 //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 try { string text = new Uri(file).AbsoluteUri.Replace("+", "%2B"); UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip(text, type); try { UnityWebRequestAsyncOperation val = audioClip.SendWebRequest(); while (!((AsyncOperation)val).isDone) { } if ((int)audioClip.result == 2 || (int)audioClip.result == 3) { Plugin.Log.LogError((object)("[Carl] Error loading " + file + ": " + audioClip.error)); return null; } AudioClip content = DownloadHandlerAudioClip.GetContent(audioClip); if ((Object)(object)content != (Object)null) { ((Object)content).name = ClipLayout.Stem(file); } return content; } finally { ((IDisposable)audioClip)?.Dispose(); } } catch (Exception ex) { Plugin.Log.LogError((object)("[Carl] Failed to load " + file + ": " + ex.Message)); return null; } } } }