Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Manual Extraction Confirm v0.1.0
ManualExtractionConfirm.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Photon.Pun; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace ExtractionConfirm { [BepInPlugin("yourname.extractionconfirm", "Extraction Confirm", "2.0.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "yourname.extractionconfirm"; public const string PluginName = "Extraction Confirm"; public const string PluginVersion = "2.0.0"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("yourname.extractionconfirm"); try { _harmony.PatchAll(); } catch (Exception ex) { Log.LogError((object)"ExtractionConfirm: PatchAll threw — patches may be partially or fully unapplied."); Log.LogError((object)ex.ToString()); } Fields.ValidateOrLog(); Log.LogInfo((object)string.Format("{0} v{1} loaded.", "Extraction Confirm", "2.0.0")); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } internal static bool IsHost() { if (!PhotonNetwork.IsConnected) { return true; } return PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient; } } internal static class Fields { private static readonly FieldInfo CurrentStateField = AccessTools.Field(typeof(ExtractionPoint), "currentState"); private static readonly FieldInfo IsShopField = AccessTools.Field(typeof(ExtractionPoint), "isShop"); private static readonly FieldInfo HaulCurrentField = AccessTools.Field(typeof(ExtractionPoint), "haulCurrent"); private static readonly FieldInfo HaulGoalField = AccessTools.Field(typeof(ExtractionPoint), "haulGoal"); private static readonly MethodInfo StateSetMethod = AccessTools.Method(typeof(ExtractionPoint), "StateSet", new Type[1] { typeof(State) }, (Type[])null); private static readonly FieldInfo TruckScreenInstanceField = AccessTools.Field(typeof(TruckScreenText), "instance"); private static readonly FieldInfo ChargeChatLoopField = AccessTools.Field(typeof(TruckScreenText), "chargeChatLoop"); private static readonly FieldInfo EngineRevSoundField = AccessTools.Field(typeof(TruckScreenText), "engineRevSound"); private static readonly FieldInfo ChatMessageResultSuccessField = AccessTools.Field(typeof(TruckScreenText), "chatMessageResultSuccess"); private static readonly FieldInfo ChatMessageResultFailField = AccessTools.Field(typeof(TruckScreenText), "chatMessageResultFail"); private static readonly FieldInfo PlatformField = AccessTools.Field(typeof(ExtractionPoint), "platform"); private static readonly FieldInfo HaulGoalScreenField = AccessTools.Field(typeof(ExtractionPoint), "haulGoalScreen"); private static readonly FieldInfo TubeScreenTextField = AccessTools.Field(typeof(ExtractionPoint), "tubeScreenText"); private static readonly FieldInfo EmojiScreenField = AccessTools.Field(typeof(ExtractionPoint), "emojiScreen"); private static readonly FieldInfo EmojiLightField = AccessTools.Field(typeof(ExtractionPoint), "emojiLight"); private static readonly FieldInfo EmojiLightIntensityField = AccessTools.Field(typeof(ExtractionPoint), "emojiLightIntensity"); private static readonly FieldInfo GrabbedField = AccessTools.Field(typeof(PhysGrabObject), "grabbed"); private static readonly FieldInfo StaticGrabbedField = AccessTools.Field(typeof(StaticGrabObject), "grabbed"); public static bool IsGrabbed(StaticGrabObject obj) { if (StaticGrabbedField == null || (Object)(object)obj == (Object)null) { return false; } return (bool)StaticGrabbedField.GetValue(obj); } public static bool IsGrabbed(PhysGrabObject obj) { if (GrabbedField == null || (Object)(object)obj == (Object)null) { return false; } return (bool)GrabbedField.GetValue(obj); } public static Transform GetPlatform(ExtractionPoint point) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if (PlatformField == null) { return null; } return (Transform)PlatformField.GetValue(point); } public static Transform GetHaulGoalScreenTransform(ExtractionPoint point) { if (HaulGoalScreenField == null) { return null; } object value = HaulGoalScreenField.GetValue(point); Component val = (Component)((value is Component) ? value : null); return (!((Object)(object)val != (Object)null)) ? null : val.transform; } public static Transform GetTubeScreenTextTransform(ExtractionPoint point) { if (TubeScreenTextField == null) { return null; } object value = TubeScreenTextField.GetValue(point); Component val = (Component)((value is Component) ? value : null); return (!((Object)(object)val != (Object)null)) ? null : val.transform; } public static Transform GetEmojiScreenTransform(ExtractionPoint point) { if (EmojiScreenField == null) { return null; } object value = EmojiScreenField.GetValue(point); Component val = (Component)((value is Component) ? value : null); return (!((Object)(object)val != (Object)null)) ? null : val.transform; } public static object GetHaulGoalScreenComponent(ExtractionPoint point) { if (HaulGoalScreenField == null) { return null; } return HaulGoalScreenField.GetValue(point); } public static Light GetEmojiLight(ExtractionPoint point) { if (EmojiLightField == null) { return null; } object? value = EmojiLightField.GetValue(point); return (Light)((value is Light) ? value : null); } public static float GetEmojiLightIntensity(ExtractionPoint point) { if (EmojiLightIntensityField == null) { return 1f; } return (float)EmojiLightIntensityField.GetValue(point); } public static Color? GetColorReflective(object component) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (component == null) { return null; } PropertyInfo property = component.GetType().GetProperty("color"); if (property == null) { return null; } return (Color)property.GetValue(component, null); } public static void SetColorReflective(object component, Color color) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (component != null) { PropertyInfo property = component.GetType().GetProperty("color"); if (property != null) { property.SetValue(component, color, null); } } } public static void ValidateOrLog() { LogIfNull(CurrentStateField, "currentState"); LogIfNull(IsShopField, "isShop"); LogIfNull(HaulCurrentField, "haulCurrent"); LogIfNull(HaulGoalField, "haulGoal"); LogIfNull(StateSetMethod, "StateSet"); LogIfNull(PlatformField, "platform"); LogIfNull(HaulGoalScreenField, "haulGoalScreen"); LogIfNull(TubeScreenTextField, "tubeScreenText"); LogIfNull(TruckScreenInstanceField, "TruckScreenText.instance"); LogIfNull(ChargeChatLoopField, "TruckScreenText.chargeChatLoop"); LogIfNull(EngineRevSoundField, "TruckScreenText.engineRevSound"); LogIfNull(ChatMessageResultSuccessField, "TruckScreenText.chatMessageResultSuccess"); LogIfNull(ChatMessageResultFailField, "TruckScreenText.chatMessageResultFail"); LogIfNull(GrabbedField, "PhysGrabObject.grabbed"); LogIfNull(StaticGrabbedField, "StaticGrabObject.grabbed"); LogIfNull(EmojiScreenField, "emojiScreen"); LogIfNull(EmojiLightField, "emojiLight"); LogIfNull(EmojiLightIntensityField, "emojiLightIntensity"); } private static void LogIfNull(object member, string name) { if (member == null) { Plugin.Log.LogError((object)("ExtractionConfirm: reflection lookup failed for '" + name + "' — field/method name may have changed.")); } } public static State CurrentState(ExtractionPoint point) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) return (State)CurrentStateField.GetValue(point); } public static bool IsShop(ExtractionPoint point) { return (bool)IsShopField.GetValue(point); } public static bool QuotaReached(ExtractionPoint point) { int num = (int)HaulCurrentField.GetValue(point); int num2 = (int)HaulGoalField.GetValue(point); return num >= num2; } public static void CallStateSet(ExtractionPoint point, State value) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (StateSetMethod == null) { Plugin.Log.LogError((object)"ExtractionConfirm: could not find ExtractionPoint.StateSet via reflection."); return; } StateSetMethod.Invoke(point, new object[1] { value }); } public static Sound GetTruckChargeSound() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown if (TruckScreenInstanceField == null || ChargeChatLoopField == null) { return null; } object value = TruckScreenInstanceField.GetValue(null); if (value == null) { return null; } return (Sound)ChargeChatLoopField.GetValue(value); } public static Sound GetTruckApproveSound() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown if (TruckScreenInstanceField == null || ChatMessageResultSuccessField == null) { return null; } object value = TruckScreenInstanceField.GetValue(null); if (value == null) { return null; } return (Sound)ChatMessageResultSuccessField.GetValue(value); } public static Sound GetTruckDenySound() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown if (TruckScreenInstanceField == null || ChatMessageResultFailField == null) { return null; } object value = TruckScreenInstanceField.GetValue(null); if (value == null) { return null; } return (Sound)ChatMessageResultFailField.GetValue(value); } } internal class HiddenState { public GameObject SpawnedObject; public PhysGrabObject GrabObject; public float? HoldStartTime; public bool Confirmed; public bool Despawned; public AudioSource HoldSound; public float LastSpawnAttempt = float.NegativeInfinity; public float LastGrabbedTime = float.NegativeInfinity; public bool AwaitingRelease; } internal static class ConfirmTracker { private static readonly Dictionary<ExtractionPoint, HiddenState> States = new Dictionary<ExtractionPoint, HiddenState>(); private static readonly HashSet<ExtractionPoint> AllowNextSuccess = new HashSet<ExtractionPoint>(); public static HiddenState GetOrCreate(ExtractionPoint point) { if (!States.TryGetValue(point, out var value)) { value = new HiddenState(); States[point] = value; } return value; } public static void Allow(ExtractionPoint point) { AllowNextSuccess.Add(point); } public static bool ConsumeAllow(ExtractionPoint point) { return AllowNextSuccess.Remove(point); } } internal static class HiddenSpawner { private const string ResourcePath = "enemies/Enemy - Hidden"; private const float GrabHitboxWidenFactor = 3f; public static GameObject SpawnNeutralized(Vector3 position, Quaternion rotation, out PhysGrabObject grabObject) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) grabObject = null; GameObject val; if (PhotonNetwork.IsConnected) { Plugin.Log.LogInfo((object)"ExtractionConfirm: spawning via PhotonNetwork.Instantiate (connected)."); val = PhotonNetwork.Instantiate("enemies/Enemy - Hidden", position, rotation, (byte)0, (object[])null); } else { Plugin.Log.LogInfo((object)"ExtractionConfirm: spawning via local Instantiate (not connected to Photon)."); GameObject val2 = Resources.Load<GameObject>("enemies/Enemy - Hidden"); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogError((object)"ExtractionConfirm: Resources.Load<GameObject>(\"enemies/Enemy - Hidden\") returned null."); return null; } val = Object.Instantiate<GameObject>(val2, position, rotation); } if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"ExtractionConfirm: failed to spawn enemies/Enemy - Hidden"); return null; } MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val3 in componentsInChildren) { if (!(val3 is PhysGrabObject) && !(val3 is PhotonView)) { ((Behaviour)val3).enabled = false; } } AudioSource[] componentsInChildren2 = val.GetComponentsInChildren<AudioSource>(true); foreach (AudioSource val4 in componentsInChildren2) { val4.mute = true; } Rigidbody[] componentsInChildren3 = val.GetComponentsInChildren<Rigidbody>(true); foreach (Rigidbody val5 in componentsInChildren3) { val5.isKinematic = true; } int num = 0; int num2 = 0; int num3 = 0; HashSet<Transform> hashSet = new HashSet<Transform>(); Collider[] componentsInChildren4 = val.GetComponentsInChildren<Collider>(true); foreach (Collider val6 in componentsInChildren4) { num++; MeshCollider val7 = (MeshCollider)(object)((val6 is MeshCollider) ? val6 : null); if ((Object)(object)val7 != (Object)null && !val7.convex) { val6.enabled = false; num2++; hashSet.Add(((Component)val6).transform); continue; } num3++; CapsuleCollider val8 = (CapsuleCollider)(object)((val6 is CapsuleCollider) ? val6 : null); if ((Object)(object)val8 != (Object)null) { BoxCollider val9 = ((Component)val6).gameObject.AddComponent<BoxCollider>(); val9.center = val8.center; val9.size = new Vector3(val8.radius * 2f * 3f, val8.height, val8.radius * 2f); } } int num4 = 0; Renderer[] componentsInChildren5 = val.GetComponentsInChildren<Renderer>(true); foreach (Renderer val10 in componentsInChildren5) { if (hashSet.Contains(((Component)val10).transform)) { val10.enabled = false; continue; } ((Component)val10).gameObject.SetActive(true); val10.enabled = true; num4++; } Plugin.Log.LogInfo((object)$"ExtractionConfirm: colliders total={num} disabled(concave)={num2} left-solid={num3}, renderers={num4}"); grabObject = val.GetComponentInChildren<PhysGrabObject>(true); if ((Object)(object)grabObject == (Object)null) { Plugin.Log.LogError((object)"ExtractionConfirm: spawned Hidden but found no PhysGrabObject — it won't be grabbable."); } return val; } } } namespace ExtractionConfirm.Patches { [HarmonyPatch(typeof(ExtractionPoint), "StateSet")] internal static class StateSetGatePatch { private static bool Prefix(ExtractionPoint __instance, State newState) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 try { if (!Plugin.IsHost()) { return true; } if ((int)newState != 3) { return true; } if (Fields.IsShop(__instance)) { return true; } if ((int)Fields.CurrentState(__instance) != 2) { return true; } if (ConfirmTracker.ConsumeAllow(__instance)) { return true; } Plugin.Log.LogInfo((object)"Quota reached automatically — holding for manual confirm instead."); return false; } catch (Exception ex) { Plugin.Log.LogError((object)("ExtractionConfirm: StateSetGatePatch threw, letting original run: " + ex)); return true; } } } [HarmonyPatch(typeof(ExtractionPoint), "Update")] internal static class ExtractionPointUpdatePatch { private const float HoldSeconds = 2f; private const float SpawnRetrySeconds = 1f; private const float ReleaseGraceSeconds = 0.3f; private const float StartPitch = 1f; private const float EndPitch = 2f; private static bool _loggedUpdateError; private static bool _loggedDiagnostic; private static readonly HashSet<ExtractionPoint> _loggedPerPoint = new HashSet<ExtractionPoint>(); private static void Postfix(ExtractionPoint __instance) { if (!_loggedDiagnostic) { _loggedDiagnostic = true; Plugin.Log.LogInfo((object)$"ExtractionConfirm DIAGNOSTIC: InRoom={PhotonNetwork.InRoom} IsMasterClient={PhotonNetwork.IsMasterClient} IsConnected={PhotonNetwork.IsConnected} OfflineMode={PhotonNetwork.OfflineMode} IsHost()={Plugin.IsHost()}"); } try { RunLogic(__instance); } catch (Exception ex) { if (!_loggedUpdateError) { _loggedUpdateError = true; Plugin.Log.LogError((object)("ExtractionConfirm: ExtractionPointUpdatePatch threw (further occurrences suppressed): " + ex)); } } } private static void RunLogic(ExtractionPoint __instance) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_006b: Invalid comparison between Unknown and I4 //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Invalid comparison between Unknown and I4 if (!Plugin.IsHost()) { return; } bool flag = Fields.IsShop(__instance); State val = Fields.CurrentState(__instance); if (!_loggedPerPoint.Contains(__instance)) { _loggedPerPoint.Add(__instance); Plugin.Log.LogInfo((object)$"ExtractionConfirm DIAGNOSTIC per-point: name={((Object)__instance).name} isShop={flag} currentState={val}"); } if (flag) { return; } HiddenState orCreate = ConfirmTracker.GetOrCreate(__instance); if ((int)val == 2 && (Object)(object)orCreate.SpawnedObject == (Object)null && !orCreate.Despawned) { float time = Time.time; if (time - orCreate.LastSpawnAttempt >= 1f) { orCreate.LastSpawnAttempt = time; SpawnHidden(__instance, orCreate); } } if ((Object)(object)orCreate.SpawnedObject != (Object)null && !orCreate.Confirmed) { UpdateHold(__instance, orCreate); } if ((Object)(object)orCreate.SpawnedObject != (Object)null && !orCreate.Despawned && (int)val == 7) { DespawnHidden(orCreate); } } private static void UpdateHold(ExtractionPoint point, HiddenState state) { bool flag = Fields.IsGrabbed(state.GrabObject); if (flag) { state.LastGrabbedTime = Time.time; } bool flag2 = Time.time - state.LastGrabbedTime > 0.3f; if (flag && !state.HoldStartTime.HasValue && !state.AwaitingRelease) { state.HoldStartTime = Time.time; StartHoldSound(state); return; } if (!flag && flag2 && state.HoldStartTime.HasValue) { state.HoldStartTime = null; StopHoldSound(state); return; } if (!flag && flag2 && state.AwaitingRelease) { state.AwaitingRelease = false; } if (!state.HoldStartTime.HasValue || (!flag && flag2)) { return; } float num = Time.time - state.HoldStartTime.Value; if ((Object)(object)state.HoldSound != (Object)null) { float num2 = Mathf.Clamp01(num / 2f); state.HoldSound.pitch = Mathf.Lerp(1f, 2f, num2); } if (num >= 2f) { Transform transform = ((Component)state.GrabObject).transform; if (Fields.QuotaReached(point)) { state.Confirmed = true; StopHoldSound(state); ConfirmTracker.Allow(point); Fields.CallStateSet(point, (State)3); Plugin.Log.LogInfo((object)"Extraction confirmed via hold."); } else { state.HoldStartTime = null; state.AwaitingRelease = true; StopHoldSound(state); PlayOutcomeSound(Fields.GetTruckDenySound(), transform, "deny"); Fields.CallStateSet(point, (State)5); } } } private static void StartHoldSound(HiddenState state) { Sound truckChargeSound = Fields.GetTruckChargeSound(); if (truckChargeSound == null) { Plugin.Log.LogWarning((object)"ExtractionConfirm: GetTruckChargeSound() returned null — no hold sound will play."); return; } AudioSource val = truckChargeSound.Play(((Component)state.GrabObject).transform, 1f, 1f, 1f, 1f); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.clip == (Object)null)) { AudioClip clip = val.clip; float volume = Mathf.Max(val.volume, 0.7f); val.Stop(); AudioSource val2 = ((Component)state.GrabObject).gameObject.AddComponent<AudioSource>(); val2.clip = clip; val2.loop = true; val2.spatialBlend = 1f; val2.volume = volume; val2.pitch = 1f; val2.Play(); state.HoldSound = val2; } } private static void SpawnHidden(ExtractionPoint point, HiddenState state) { //IL_001b: 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_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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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) Transform emojiScreenTransform = Fields.GetEmojiScreenTransform(point); Transform haulGoalScreenTransform = Fields.GetHaulGoalScreenTransform(point); Vector3 val; Quaternion rotation; string arg; if ((Object)(object)emojiScreenTransform != (Object)null) { val = emojiScreenTransform.position + new Vector3(0f, -1.25f, 0f); rotation = emojiScreenTransform.rotation; arg = "emojiScreen (lowered 1.25m)"; } else if ((Object)(object)haulGoalScreenTransform != (Object)null) { val = haulGoalScreenTransform.position; rotation = haulGoalScreenTransform.rotation; arg = "haulGoalScreen"; } else { Plugin.Log.LogWarning((object)"ExtractionConfirm: no screen transform found, falling back to platform."); Transform val2 = Fields.GetPlatform(point) ?? ((Component)point).transform; val = val2.position + Vector3.up * 0.5f; rotation = val2.rotation; arg = "platform/fallback"; } PhysGrabObject grabObject; GameObject val3 = HiddenSpawner.SpawnNeutralized(val, rotation, out grabObject); if (!((Object)(object)val3 == (Object)null)) { Plugin.Log.LogInfo((object)$"ExtractionConfirm: Hidden spawned via {arg}. spawnPos={val}"); state.SpawnedObject = val3; state.GrabObject = grabObject; } } private static void DespawnHidden(HiddenState state) { StopHoldSound(state); if ((Object)(object)state.SpawnedObject != (Object)null) { if (PhotonNetwork.IsConnected) { PhotonView component = state.SpawnedObject.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && component.IsMine) { PhotonNetwork.Destroy(state.SpawnedObject); } else { Object.Destroy((Object)(object)state.SpawnedObject); } } else { Object.Destroy((Object)(object)state.SpawnedObject); } } state.SpawnedObject = null; state.GrabObject = null; state.Despawned = true; } private static void StopHoldSound(HiddenState state) { if ((Object)(object)state.HoldSound != (Object)null) { state.HoldSound.Stop(); Object.Destroy((Object)(object)state.HoldSound); state.HoldSound = null; } } private static void PlayOutcomeSound(Sound sound, Transform at, string label) { if (sound == null) { Plugin.Log.LogWarning((object)("ExtractionConfirm: " + label + " sound unavailable.")); } else { sound.Play(at, 1f, 1f, 1f, 1f); } } } }