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 v1.1.0
ManualExtractionConfirm.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Logging; using ExtractionConfirm.Patches; 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.1.1")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "yourname.extractionconfirm"; public const string PluginName = "Extraction Confirm"; public const string PluginVersion = "2.1.1"; 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"); PatchOne(typeof(StateSetGatePatch)); PatchOne(typeof(ExtractionPointUpdatePatch)); BreakGuard.TryPatch(_harmony); HalloweenCompat.TryPatch(_harmony); Fields.ValidateOrLog(); Log.LogInfo((object)string.Format("{0} v{1} loaded.", "Extraction Confirm", "2.1.1")); } private void PatchOne(Type patchClass) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) try { new PatchClassProcessor(_harmony, patchClass).Patch(); Log.LogInfo((object)$"Extraction Confirm: patched {patchClass.Name}."); } catch (Exception ex) { Log.LogError((object)($"Extraction Confirm: FAILED to patch {patchClass.Name} — " + "that feature is off, the rest of the mod still runs.")); Log.LogError((object)ex.ToString()); } } 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 EpCurrentState = AccessTools.Field(typeof(ExtractionPoint), "currentState"); private static readonly FieldInfo EpIsShop = AccessTools.Field(typeof(ExtractionPoint), "isShop"); private static readonly FieldInfo EpHaulCurrent = AccessTools.Field(typeof(ExtractionPoint), "haulCurrent"); private static readonly FieldInfo EpHaulGoal = AccessTools.Field(typeof(ExtractionPoint), "haulGoal"); private static readonly FieldInfo EpPlatform = AccessTools.Field(typeof(ExtractionPoint), "platform"); private static readonly FieldInfo EpHaulGoalScreen = AccessTools.Field(typeof(ExtractionPoint), "haulGoalScreen"); private static readonly FieldInfo EpTubeScreenText = AccessTools.Field(typeof(ExtractionPoint), "tubeScreenText"); private static readonly FieldInfo EpEmojiScreen = AccessTools.Field(typeof(ExtractionPoint), "emojiScreen"); private static readonly MethodInfo EpStateSet = AccessTools.Method(typeof(ExtractionPoint), "StateSet", new Type[1] { typeof(State) }, (Type[])null); private static readonly FieldInfo TstInstance = AccessTools.Field(typeof(TruckScreenText), "instance"); private static readonly FieldInfo TstChargeChatLoop = AccessTools.Field(typeof(TruckScreenText), "chargeChatLoop"); private static readonly FieldInfo TstResultFail = AccessTools.Field(typeof(TruckScreenText), "chatMessageResultFail"); private static readonly FieldInfo PgoGrabbed = AccessTools.Field(typeof(PhysGrabObject), "grabbed"); public static State CurrentState(ExtractionPoint p) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (EpCurrentState == null || (Object)(object)p == (Object)null) { return (State)0; } return (State)EpCurrentState.GetValue(p); } public static bool IsShop(ExtractionPoint p) { if (EpIsShop == null || (Object)(object)p == (Object)null) { return false; } return (bool)EpIsShop.GetValue(p); } public static bool QuotaReached(ExtractionPoint p) { if (EpHaulCurrent == null || EpHaulGoal == null || (Object)(object)p == (Object)null) { return false; } int num = (int)EpHaulCurrent.GetValue(p); int num2 = (int)EpHaulGoal.GetValue(p); return num >= num2; } public static void CallStateSet(ExtractionPoint p, State state) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (!(EpStateSet == null) && !((Object)(object)p == (Object)null)) { EpStateSet.Invoke(p, new object[1] { state }); } } public static bool IsGrabbed(PhysGrabObject o) { if (PgoGrabbed == null || (Object)(object)o == (Object)null) { return false; } return (bool)PgoGrabbed.GetValue(o); } public static Transform GetPlatform(ExtractionPoint p) { if (EpPlatform == null || (Object)(object)p == (Object)null) { return null; } object? value = EpPlatform.GetValue(p); return (Transform)((value is Transform) ? value : null); } private static Transform ComponentTransform(FieldInfo f, ExtractionPoint p) { if (f == null || (Object)(object)p == (Object)null) { return null; } object? value = f.GetValue(p); Component val = (Component)((value is Component) ? value : null); return (!((Object)(object)val == (Object)null)) ? val.transform : null; } public static Transform GetHaulGoalScreenTransform(ExtractionPoint p) { return ComponentTransform(EpHaulGoalScreen, p); } public static Transform GetTubeScreenTextTransform(ExtractionPoint p) { return ComponentTransform(EpTubeScreenText, p); } public static Transform GetEmojiScreenTransform(ExtractionPoint p) { return ComponentTransform(EpEmojiScreen, p); } private static object TruckScreenInstance() { if (TstInstance == null) { return null; } return TstInstance.GetValue(null); } public static Sound GetTruckChargeSound() { object obj = TruckScreenInstance(); if (obj == null || TstChargeChatLoop == null) { return null; } object? value = TstChargeChatLoop.GetValue(obj); return (Sound)((value is Sound) ? value : null); } public static Sound GetTruckDenySound() { object obj = TruckScreenInstance(); if (obj == null || TstResultFail == null) { return null; } object? value = TstResultFail.GetValue(obj); return (Sound)((value is Sound) ? value : null); } public static void ValidateOrLog() { Warn(EpCurrentState, "ExtractionPoint.currentState"); Warn(EpIsShop, "ExtractionPoint.isShop"); Warn(EpHaulCurrent, "ExtractionPoint.haulCurrent"); Warn(EpHaulGoal, "ExtractionPoint.haulGoal"); Warn(EpEmojiScreen, "ExtractionPoint.emojiScreen"); Warn(EpStateSet, "ExtractionPoint.StateSet(State)"); Warn(TstInstance, "TruckScreenText.instance"); Warn(TstChargeChatLoop, "TruckScreenText.chargeChatLoop"); Warn(TstResultFail, "TruckScreenText.chatMessageResultFail"); Warn(PgoGrabbed, "PhysGrabObject.grabbed"); } private static void Warn(object member, string name) { if (member == null) { Plugin.Log.LogWarning((object)$"Extraction Confirm: could not resolve {name} — that feature is disabled."); } } } internal class HiddenState { public GameObject SpawnedObject; public PhysGrabObject GrabObject; public float? HoldStartTime; public bool Despawned; public AudioSource HoldSound; public float LastSpawnAttempt = float.NegativeInfinity; public float LastGrabbedTime = float.NegativeInfinity; public bool AwaitingRelease; } internal static class ConfirmTracker { public 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 ((Object)(object)point == (Object)null) { return null; } if (!States.TryGetValue(point, out var value)) { value = new HiddenState(); States[point] = value; } return value; } public static void Allow(ExtractionPoint point) { if ((Object)(object)point != (Object)null) { AllowNextSuccess.Add(point); } } public static bool ConsumeAllow(ExtractionPoint point) { if ((Object)(object)point == (Object)null) { return false; } return AllowNextSuccess.Remove(point); } } internal static class ConfirmHandle { private static readonly List<GameObject> Roots = new List<GameObject>(); private static readonly HashSet<PhysGrabObject> GrabObjects = new HashSet<PhysGrabObject>(); private static readonly List<Collider> Colliders = new List<Collider>(); public static int ColliderCount { [CompilerGenerated] get { return Colliders.Count; } } public static void Register(GameObject root, PhysGrabObject grabObject) { if ((Object)(object)root == (Object)null) { return; } if (!Roots.Contains(root)) { Roots.Add(root); } if ((Object)(object)grabObject != (Object)null) { GrabObjects.Add(grabObject); } Collider[] componentsInChildren = root.GetComponentsInChildren<Collider>(true); foreach (Collider val in componentsInChildren) { if ((Object)(object)val != (Object)null && !Colliders.Contains(val)) { Colliders.Add(val); } } } public static void Unregister(GameObject root) { if (!((Object)(object)root == (Object)null)) { Roots.Remove(root); Colliders.RemoveAll((Collider c) => (Object)(object)c == (Object)null || IsUnder(((Component)c).transform, root.transform)); GrabObjects.RemoveWhere((PhysGrabObject o) => (Object)(object)o == (Object)null || IsUnder(((Component)o).transform, root.transform)); } } private static bool IsUnder(Transform t, Transform root) { return (Object)(object)t != (Object)null && (Object)(object)root != (Object)null && t.IsChildOf(root); } public static bool IsCollider(Collider c) { if ((Object)(object)c == (Object)null) { return false; } if (Colliders.Contains(c)) { return true; } return IsTransform(((Component)c).transform); } public static bool IsGrabObject(PhysGrabObject o) { if ((Object)(object)o == (Object)null) { return false; } if (GrabObjects.Contains(o)) { return true; } return IsTransform(((Component)o).transform); } public static bool IsTransform(Transform t) { if ((Object)(object)t == (Object)null || Roots.Count == 0) { return false; } for (int i = 0; i < Roots.Count; i++) { GameObject val = Roots[i]; if ((Object)(object)val != (Object)null && t.IsChildOf(val.transform)) { return true; } } return false; } public static void MaskColliders(List<Collider> masked) { masked.Clear(); for (int i = 0; i < Colliders.Count; i++) { Collider val = Colliders[i]; if (!((Object)(object)val == (Object)null) && val.enabled) { val.enabled = false; masked.Add(val); } } } public static void RestoreColliders(List<Collider> masked) { for (int i = 0; i < masked.Count; i++) { Collider val = masked[i]; if ((Object)(object)val != (Object)null) { val.enabled = true; } } masked.Clear(); } public static bool EnsureCollidersEnabled() { bool result = false; MeshCollider val2 = default(MeshCollider); for (int i = 0; i < Colliders.Count; i++) { Collider val = Colliders[i]; if (!((Object)(object)val == (Object)null) && !val.enabled) { object obj = ((val is MeshCollider) ? val : null); if (obj != null) { val2 = (MeshCollider)obj; obj = val2; } if (obj == null || val2.convex) { val.enabled = true; result = true; } } } return result; } } internal static class HiddenSpawner { private const string HiddenResourcePath = "enemies/Enemy - Hidden"; private const float GrabHitboxWidenFactor = 3f; private const bool DisablePhysicalCollision = false; public static GameObject Spawn(Vector3 position, Quaternion rotation, out PhysGrabObject grabObject) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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) grabObject = null; GameObject val = null; if (PhotonNetwork.IsConnected && PhotonNetwork.InRoom) { val = PhotonNetwork.Instantiate("enemies/Enemy - Hidden", position, rotation, (byte)0, (object[])null); } else { GameObject val2 = Resources.Load<GameObject>("enemies/Enemy - Hidden"); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogError((object)string.Format("Extraction Confirm: could not load {0}.", "enemies/Enemy - Hidden")); return null; } val = Object.Instantiate<GameObject>(val2, position, rotation); } if ((Object)(object)val == (Object)null) { return null; } Neutralize(val); grabObject = val.GetComponentInChildren<PhysGrabObject>(true); if ((Object)(object)grabObject == (Object)null) { Plugin.Log.LogWarning((object)"Extraction Confirm: spawned confirm handle has no PhysGrabObject."); } ConfirmHandle.Register(val, grabObject); return val; } private static void Neutralize(GameObject root) { //IL_01c3: 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) MonoBehaviour[] componentsInChildren = root.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !(val is PhysGrabObject) && !(val is PhotonView)) { ((Behaviour)val).enabled = false; } } AudioSource[] componentsInChildren2 = root.GetComponentsInChildren<AudioSource>(true); foreach (AudioSource val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null)) { val2.Stop(); val2.volume = 0f; val2.mute = true; val2.playOnAwake = false; ((Behaviour)val2).enabled = false; } } Rigidbody[] componentsInChildren3 = root.GetComponentsInChildren<Rigidbody>(true); foreach (Rigidbody val3 in componentsInChildren3) { if (!((Object)(object)val3 == (Object)null)) { val3.isKinematic = true; } } HashSet<Transform> hashSet = new HashSet<Transform>(); List<CapsuleCollider> list = new List<CapsuleCollider>(); Collider[] componentsInChildren4 = root.GetComponentsInChildren<Collider>(true); MeshCollider val5 = default(MeshCollider); CapsuleCollider val6 = default(CapsuleCollider); foreach (Collider val4 in componentsInChildren4) { if ((Object)(object)val4 == (Object)null) { continue; } object obj = ((val4 is MeshCollider) ? val4 : null); if (obj != null) { val5 = (MeshCollider)obj; obj = val5; } if (obj != null && !val5.convex) { ((Collider)val5).enabled = false; hashSet.Add(((Component)val5).transform); continue; } object obj2 = ((val4 is CapsuleCollider) ? val4 : null); if (obj2 != null) { val6 = (CapsuleCollider)obj2; obj2 = val6; } if (obj2 != null) { list.Add(val6); } } foreach (CapsuleCollider item in list) { BoxCollider val7 = ((Component)item).gameObject.AddComponent<BoxCollider>(); val7.center = item.center; val7.size = new Vector3(item.radius * 2f * 3f, item.height, item.radius * 2f); } Renderer[] componentsInChildren5 = root.GetComponentsInChildren<Renderer>(true); foreach (Renderer val8 in componentsInChildren5) { if (!((Object)(object)val8 == (Object)null) && !hashSet.Contains(((Component)val8).transform)) { val8.enabled = true; } } } public static void Despawn(GameObject spawned) { if ((Object)(object)spawned == (Object)null) { return; } ConfirmHandle.Unregister(spawned); if (PhotonNetwork.IsConnected && PhotonNetwork.InRoom) { PhotonView component = spawned.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && component.IsMine) { PhotonNetwork.Destroy(spawned); return; } } Object.Destroy((Object)(object)spawned); } } internal static class HalloweenCompat { private static readonly List<Collider> Masked = new List<Collider>(); private static bool _patched; public static bool IsMasking { [CompilerGenerated] get { return Masked.Count > 0; } } public static void TryPatch(Harmony harmony) { if (_patched || harmony == null) { return; } Type type = AccessTools.TypeByName("ExtractionHalloweenTower"); if (type == null) { Plugin.Log.LogInfo((object)"Extraction Confirm: no Halloween tower in this build — stacking compatibility not needed."); return; } try { MethodInfo methodInfo = AccessTools.Method(type, "TowerEvaluate", (Type[])null, (Type[])null); if (methodInfo != null) { MethodInfo methodInfo2 = methodInfo; HarmonyMethod val = Hook("MaskPrefix"); HarmonyMethod val2 = Hook("UnmaskFinalizer"); harmony.Patch((MethodBase)methodInfo2, val, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null); } else { Plugin.Log.LogWarning((object)"Extraction Confirm: ExtractionHalloweenTower.TowerEvaluate not found — valuable stacking may misbehave."); } MethodInfo methodInfo3 = AccessTools.Method(type, "TowerReject", (Type[])null, (Type[])null); if (methodInfo3 != null) { harmony.Patch((MethodBase)methodInfo3, Hook("TowerRejectPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } Type type2 = AccessTools.TypeByName("ExtractionHalloweenContact"); if (type2 != null) { MethodInfo methodInfo4 = AccessTools.Method(type2, "SupportUpdate", (Type[])null, (Type[])null); if (methodInfo4 != null) { harmony.Patch((MethodBase)methodInfo4, Hook("SupportUpdatePrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } _patched = true; Plugin.Log.LogInfo((object)"Extraction Confirm: Halloween stacking compatibility applied."); } catch (Exception ex) { Plugin.Log.LogError((object)"Extraction Confirm: failed to apply Halloween stacking compatibility."); Plugin.Log.LogError((object)ex.ToString()); } } private static HarmonyMethod Hook(string name) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return new HarmonyMethod(AccessTools.Method(typeof(HalloweenCompat), name, (Type[])null, (Type[])null)); } private static void MaskPrefix() { if (Masked.Count > 0) { ConfirmHandle.RestoreColliders(Masked); } if (ConfirmHandle.ColliderCount != 0) { ConfirmHandle.MaskColliders(Masked); } } private static void UnmaskFinalizer() { ConfirmHandle.RestoreColliders(Masked); } private static bool TowerRejectPrefix(object __0, ref bool __result) { object obj = ((__0 is PhysGrabObject) ? __0 : null); PhysGrabObject val = default(PhysGrabObject); if (obj != null) { val = (PhysGrabObject)obj; obj = val; } if (obj != null && ConfirmHandle.IsGrabObject(val)) { __result = false; return false; } return true; } private static bool SupportUpdatePrefix(Collision __0) { if (__0 == null) { return true; } return !ConfirmHandle.IsCollider(__0.collider); } } internal static class BreakGuard { private static readonly string[] Targets = new string[5] { "Break", "BreakHeavy", "BreakMedium", "BreakLight", "BreakRPC" }; public static void TryPatch(Harmony harmony) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if (harmony == null) { return; } HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(BreakGuard), "SkipForConfirmHandle", (Type[])null, (Type[])null)); int num = 0; string[] targets = Targets; foreach (string text in targets) { try { MethodInfo methodInfo = AccessTools.Method(typeof(PhysGrabObjectImpactDetector), text, (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogWarning((object)$"Extraction Confirm: PhysGrabObjectImpactDetector.{text} not found, skipping."); continue; } harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch (Exception ex) { Plugin.Log.LogError((object)$"Extraction Confirm: could not guard PhysGrabObjectImpactDetector.{text}: {ex.Message}"); } } Plugin.Log.LogInfo((object)$"Extraction Confirm: break guard applied to {num}/{Targets.Length} methods."); } private static bool SkipForConfirmHandle(PhysGrabObjectImpactDetector __instance) { if ((Object)(object)__instance == (Object)null) { return true; } return !ConfirmHandle.IsTransform(((Component)__instance).transform); } } } namespace ExtractionConfirm.Patches { [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 readonly Vector3 SpawnOffset = new Vector3(0f, -1.25f, 0f); private static void Postfix(ExtractionPoint __instance) { //IL_0043: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Invalid comparison between Unknown and I4 try { if ((Object)(object)__instance == (Object)null || !Plugin.IsHost() || Fields.IsShop(__instance)) { return; } HiddenState orCreate = ConfirmTracker.GetOrCreate(__instance); if (orCreate == null) { return; } State val = Fields.CurrentState(__instance); if ((int)val == 7) { if ((Object)(object)orCreate.SpawnedObject != (Object)null) { StopHoldSound(orCreate); HiddenSpawner.Despawn(orCreate.SpawnedObject); orCreate.SpawnedObject = null; orCreate.GrabObject = null; orCreate.Despawned = true; } } else { if ((int)val != 2) { return; } if ((Object)(object)orCreate.SpawnedObject == (Object)null && !orCreate.Despawned) { TrySpawn(__instance, orCreate); return; } if (!HalloweenCompat.IsMasking && ConfirmHandle.EnsureCollidersEnabled()) { Plugin.Log.LogWarning((object)"Extraction Confirm: re-enabled confirm handle colliders that were left disabled."); } UpdateHold(__instance, orCreate); } } catch (Exception ex) { Plugin.Log.LogError((object)("Extraction Confirm: ExtractionPointUpdatePatch threw: " + ex)); } } private static void TrySpawn(ExtractionPoint point, HiddenState state) { //IL_0046: 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_0050: 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_0056: 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) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) if (Time.time - state.LastSpawnAttempt < 1f) { return; } state.LastSpawnAttempt = Time.time; Transform emojiScreenTransform = Fields.GetEmojiScreenTransform(point); if ((Object)(object)emojiScreenTransform == (Object)null) { Plugin.Log.LogWarning((object)"Extraction Confirm: no emoji screen on this point yet, retrying."); return; } Vector3 val = emojiScreenTransform.position + SpawnOffset; PhysGrabObject grabObject; GameObject val2 = HiddenSpawner.Spawn(val, emojiScreenTransform.rotation, out grabObject); if (!((Object)(object)val2 == (Object)null)) { state.SpawnedObject = val2; state.GrabObject = grabObject; state.HoldStartTime = null; state.AwaitingRelease = false; Plugin.Log.LogInfo((object)$"Extraction Confirm: confirm handle placed at {val}."); } } private static void UpdateHold(ExtractionPoint point, HiddenState state) { if ((Object)(object)state.GrabObject == (Object)null) { return; } bool flag = Fields.IsGrabbed(state.GrabObject); if (flag) { state.LastGrabbedTime = Time.time; } if (!flag && !(Time.time - state.LastGrabbedTime <= 0.3f)) { state.HoldStartTime = null; state.AwaitingRelease = false; StopHoldSound(state); } else { if (state.AwaitingRelease) { return; } if (!state.HoldStartTime.HasValue) { state.HoldStartTime = Time.time; Plugin.Log.LogInfo((object)"Extraction Confirm: confirm hold started."); StartHoldSound(state); } float num = Time.time - state.HoldStartTime.Value; if ((Object)(object)state.HoldSound != (Object)null) { state.HoldSound.pitch = Mathf.Lerp(1f, 2f, Mathf.Clamp01(num / 2f)); } if (!(num < 2f)) { StopHoldSound(state); state.HoldStartTime = null; if (Fields.QuotaReached(point)) { Plugin.Log.LogInfo((object)"Extraction Confirm: confirmed, starting extraction."); ConfirmTracker.Allow(point); Fields.CallStateSet(point, (State)3); } else { Plugin.Log.LogInfo((object)"Extraction Confirm: quota not met, denying."); state.AwaitingRelease = true; PlayDenySound(state); Fields.CallStateSet(point, (State)5); } } } } private static void StartHoldSound(HiddenState state) { StopHoldSound(state); if ((Object)(object)state.SpawnedObject == (Object)null) { return; } Sound truckChargeSound = Fields.GetTruckChargeSound(); if (truckChargeSound != null) { AudioClip val = null; if (truckChargeSound.Sounds != null && truckChargeSound.Sounds.Length > 0) { val = truckChargeSound.Sounds[0]; } if (!((Object)(object)val == (Object)null)) { AudioSource val2 = state.SpawnedObject.AddComponent<AudioSource>(); val2.clip = val; val2.loop = true; val2.spatialBlend = 1f; val2.volume = Mathf.Max(truckChargeSound.Volume, 0.7f); val2.pitch = 1f; val2.Play(); state.HoldSound = val2; } } } 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 PlayDenySound(HiddenState state) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) Sound truckDenySound = Fields.GetTruckDenySound(); if (truckDenySound != null && !((Object)(object)state.SpawnedObject == (Object)null)) { AudioClip val = null; if (truckDenySound.Sounds != null && truckDenySound.Sounds.Length > 0) { val = truckDenySound.Sounds[0]; } if (!((Object)(object)val == (Object)null)) { AudioSource.PlayClipAtPoint(val, state.SpawnedObject.transform.position, Mathf.Max(truckDenySound.Volume, 0.7f)); } } } } [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)("Extraction Confirm: StateSetGatePatch threw, letting original run: " + ex)); return true; } } } }