using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Zichen-AutoExtractionPoint")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+dc375af9d5c35b5e6a48e905223c04d40e17b965")] [assembly: AssemblyProduct("Zichen-AutoExtractionPoint")] [assembly: AssemblyTitle("Zichen-AutoExtractionPoint")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } [BepInPlugin("zichen.autoextractionpoint", "A.Auto Extraction Point", "1.0.0")] public sealed class WzcAutoExtractionPointPlugin : BaseUnityPlugin { public const string PluginGuid = "zichen.autoextractionpoint"; public const string PluginName = "A.Auto Extraction Point"; public const string PluginVersion = "1.0.0"; private const string InfoSection = "模组信息"; private const string FeatureSection = "A.自动开启提取点"; private const string PriorityFarthest = "最远"; private const string PriorityNearest = "最近"; private static readonly FieldInfo RoundDirectorExtractionPointListField = typeof(RoundDirector).GetField("extractionPointList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo RoundDirectorExtractionPointActiveField = typeof(RoundDirector).GetField("extractionPointActive", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo RoundDirectorExtractionPointsCompletedField = typeof(RoundDirector).GetField("extractionPointsCompleted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo RoundDirectorExtractionPointsField = typeof(RoundDirector).GetField("extractionPoints", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo RoundDirectorAllExtractionPointsCompletedField = typeof(RoundDirector).GetField("allExtractionPointsCompleted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo RoundDirectorExtractionPointCurrentField = typeof(RoundDirector).GetField("extractionPointCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ExtractionPointCurrentStateField = typeof(ExtractionPoint).GetField("currentState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ExtractionPointIsShopField = typeof(ExtractionPoint).GetField("isShop", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ExtractionPointIsLockedField = typeof(ExtractionPoint).GetField("isLocked", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo ExtractionPointOnClickMethod = typeof(ExtractionPoint).GetMethod("OnClick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private readonly Dictionary extractionDistanceMap = new Dictionary(); private ConfigEntry moduleNameInfo; private ConfigEntry moduleVersionInfo; private ConfigEntry contactInfo; private ConfigEntry featureEnabled; private ConfigEntry priorityMode; private Harmony harmony; private RoundDirector lastRoundDirector; private bool firstExtractionPointCaptured; private Vector3 firstExtractionPointPosition; private bool activationQueued; private ExtractionPoint queuedExtractionPoint; private float queuedActivationTime; private float nextAutoScanTime; private const float AutoScanInterval = 0.75f; private const float QueuedActivationDelay = 0.35f; public static WzcAutoExtractionPointPlugin Instance { get; private set; } private void Awake() { Instance = this; BindConfig(); ApplyPatches(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"zichen-auto-extraction-point loaded."); } private void Update() { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)RoundDirector.instance != (Object)(object)lastRoundDirector) { lastRoundDirector = RoundDirector.instance; ResetRuntimeState(); } if (!IsHostAuthority()) { return; } if (!IsFeatureEnabled()) { ResetQueuedActivation(); } else if ((Object)(object)RoundDirector.instance == (Object)null || !SemiFunc.RunIsLevel()) { ResetQueuedActivation(); } else if (!activationQueued) { TryAutoQueueNextExtractionPoint(); } else { if (Time.time < queuedActivationTime || GetExtractionPointActive(RoundDirector.instance) || GetAllExtractionPointsCompleted(RoundDirector.instance)) { return; } ExtractionPoint val = queuedExtractionPoint; ResetQueuedActivation(); if (!((Object)(object)val == (Object)null)) { State val3 = (State)((ExtractionPointCurrentStateField?.GetValue(val) is State val2) ? ((int)val2) : 0); object obj = ExtractionPointIsLockedField?.GetValue(val); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } bool flag2 = (byte)((uint)num & (flag ? 1u : 0u)) != 0; try { ExtractionPointOnClickMethod?.Invoke(val, null); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Auto activated next extraction point via native OnClick. Candidate={((Object)val).name}, State={val3}, Locked={flag2}"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to auto activate next extraction point: " + ex.GetType().Name + ": " + ex.Message)); } } } } private void OnDestroy() { if (harmony != null) { harmony.UnpatchSelf(); harmony = null; } if (Instance == this) { Instance = null; } } private void BindConfig() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Expected O, but got Unknown //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Expected O, but got Unknown moduleNameInfo = ((BaseUnityPlugin)this).Config.Bind("模组信息", "模组名称", "Zichen_自动开启提取点", new ConfigDescription("当前模组的中文名称。此处仅用于显示,不影响功能。", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 1000, CustomDrawer = DrawInfo, ReadOnly = true } })); moduleNameInfo.Value = "Auto Extraction Point / 自动开启提取点"; moduleVersionInfo = ((BaseUnityPlugin)this).Config.Bind("模组信息", "模组版本号", "1.0.0", new ConfigDescription("当前模组版本号。此处仅用于显示,不影响功能。", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 990, CustomDrawer = DrawInfo, ReadOnly = true } })); moduleVersionInfo.Value = "1.0.0"; contactInfo = ((BaseUnityPlugin)this).Config.Bind("模组信息", "REPO交流QQ群", "824639225", new ConfigDescription("REPO 游戏交流、BUG 反馈、优化建议、功能请求请加 QQ 群。此处仅用于显示,不影响功能。", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 980, CustomDrawer = DrawInfo, ReadOnly = true } })); contactInfo.Value = "824639225"; featureEnabled = ((BaseUnityPlugin)this).Config.Bind("A.自动开启提取点", "启用", true, ConfigDescriptionWithOrder("只需主机安装即可生效。第一个提取点仍然手动开启,完成后自动开启下一个提取点。", 900)); priorityMode = ((BaseUnityPlugin)this).Config.Bind("A.自动开启提取点", "打开提取点优先", "最远", new ConfigDescription("以第一个手动开启的提取点作为距离映射原点,后续自动开启下一个提取点时按最远或最近优先。默认最远。", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "最远", "最近" }), new object[1] { new ConfigurationManagerAttributes { Order = 890 } })); } private void ApplyPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown harmony = new Harmony("zichen.autoextractionpoint.patch"); MethodInfo methodInfo = AccessTools.Method(typeof(RoundDirector), "ExtractionPointsUnlockRPC", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(ExtractionPoint), "OnClick", (Type[])null, (Type[])null); if (methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(WzcAutoExtractionPointPlugin), "ExtractionPointOnClickPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(WzcAutoExtractionPointPlugin), "ExtractionPointsUnlockRpcPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private bool IsFeatureEnabled() { if (featureEnabled != null) { return featureEnabled.Value; } return false; } private static bool IsHostAuthority() { try { return SemiFunc.IsMasterClientOrSingleplayer(); } catch { return false; } } private void ResetRuntimeState() { //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) extractionDistanceMap.Clear(); firstExtractionPointCaptured = false; firstExtractionPointPosition = Vector3.zero; ResetQueuedActivation(); nextAutoScanTime = 0f; } private void ResetQueuedActivation() { activationQueued = false; queuedExtractionPoint = null; queuedActivationTime = 0f; } private void TryAutoQueueNextExtractionPoint() { if ((Object)(object)RoundDirector.instance == (Object)null || Time.time < nextAutoScanTime) { return; } nextAutoScanTime = Time.time + 0.75f; if (firstExtractionPointCaptured) { int extractionPointsCompleted = GetExtractionPointsCompleted(RoundDirector.instance); int extractionPointsTotal = GetExtractionPointsTotal(RoundDirector.instance); if (extractionPointsCompleted > 0 && extractionPointsTotal > 0 && extractionPointsCompleted < extractionPointsTotal && !GetAllExtractionPointsCompleted(RoundDirector.instance) && !GetExtractionPointActive(RoundDirector.instance)) { QueueNextExtractionPointActivation(fromUnlockEvent: false); } } } private void CaptureFirstExtractionPoint(ExtractionPoint extractionPoint) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!firstExtractionPointCaptured && !((Object)(object)extractionPoint == (Object)null)) { firstExtractionPointCaptured = true; firstExtractionPointPosition = ((Component)extractionPoint).transform.position; RebuildDistanceMap(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Captured first manual extraction point as distance origin."); } } private void RebuildDistanceMap() { //IL_003c: 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) extractionDistanceMap.Clear(); List extractionPoints = GetExtractionPoints(); for (int i = 0; i < extractionPoints.Count; i++) { ExtractionPoint val = extractionPoints[i]; if (!((Object)(object)val == (Object)null) && !IsShopExtractionPoint(val)) { extractionDistanceMap[((Object)val).GetInstanceID()] = Vector3.Distance(firstExtractionPointPosition, ((Component)val).transform.position); } } } private List GetExtractionPoints() { List list = new List(); if ((Object)(object)RoundDirector.instance == (Object)null || !(RoundDirectorExtractionPointListField?.GetValue(RoundDirector.instance) is List list2)) { return list; } for (int i = 0; i < list2.Count; i++) { GameObject val = list2[i]; if (!((Object)(object)val == (Object)null)) { ExtractionPoint component = val.GetComponent(); if ((Object)(object)component != (Object)null) { list.Add(component); } } } return list; } private ExtractionPoint ChooseNextExtractionPoint() { List extractionPoints = GetExtractionPoints(); ExtractionPoint val = null; float num = 0f; bool flag = string.Equals(priorityMode?.Value, "最近", StringComparison.Ordinal); for (int i = 0; i < extractionPoints.Count; i++) { ExtractionPoint val2 = extractionPoints[i]; if (!IsCandidateExtractionPoint(val2)) { continue; } float mappedDistance = GetMappedDistance(val2); if ((Object)(object)val == (Object)null) { val = val2; num = mappedDistance; } else if (flag) { if (mappedDistance < num) { val = val2; num = mappedDistance; } } else if (mappedDistance > num) { val = val2; num = mappedDistance; } } return val; } private bool IsCandidateExtractionPoint(ExtractionPoint extractionPoint) { //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: Invalid comparison between Unknown and I4 if ((Object)(object)extractionPoint == (Object)null || IsShopExtractionPoint(extractionPoint)) { return false; } if (!(ExtractionPointCurrentStateField?.GetValue(extractionPoint) is State val)) { return false; } return (int)val == 1; } private float GetMappedDistance(ExtractionPoint extractionPoint) { //IL_0029: 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) if ((Object)(object)extractionPoint == (Object)null) { return float.MaxValue; } int instanceID = ((Object)extractionPoint).GetInstanceID(); if (extractionDistanceMap.TryGetValue(instanceID, out var value)) { return value; } value = Vector3.Distance(firstExtractionPointPosition, ((Component)extractionPoint).transform.position); extractionDistanceMap[instanceID] = value; return value; } private bool GetExtractionPointActive(RoundDirector roundDirector) { bool flag = default(bool); int num; if ((Object)(object)roundDirector != (Object)null && RoundDirectorExtractionPointActiveField != null) { object value = RoundDirectorExtractionPointActiveField.GetValue(roundDirector); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private int GetExtractionPointsCompleted(RoundDirector roundDirector) { if ((Object)(object)roundDirector != (Object)null) { object obj = RoundDirectorExtractionPointsCompletedField?.GetValue(roundDirector); if (obj is int) { return (int)obj; } } return 0; } private int GetExtractionPointsTotal(RoundDirector roundDirector) { if ((Object)(object)roundDirector != (Object)null && RoundDirectorExtractionPointsField?.GetValue(roundDirector) is int num && num > 0) { return num; } int num2 = 0; List extractionPoints = GetExtractionPoints(); for (int i = 0; i < extractionPoints.Count; i++) { if ((Object)(object)extractionPoints[i] != (Object)null) { num2++; } } return num2; } private bool GetAllExtractionPointsCompleted(RoundDirector roundDirector) { bool flag = default(bool); int num; if ((Object)(object)roundDirector != (Object)null && RoundDirectorAllExtractionPointsCompletedField != null) { object value = RoundDirectorAllExtractionPointsCompletedField.GetValue(roundDirector); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static bool IsShopExtractionPoint(ExtractionPoint extractionPoint) { bool flag = default(bool); int num; if ((Object)(object)extractionPoint != (Object)null && ExtractionPointIsShopField != null) { object value = ExtractionPointIsShopField.GetValue(extractionPoint); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private void QueueNextExtractionPointActivation(bool fromUnlockEvent) { if (!firstExtractionPointCaptured || (Object)(object)RoundDirector.instance == (Object)null) { return; } ExtractionPoint val = ChooseNextExtractionPoint(); if (!((Object)(object)val == (Object)null)) { if (ExtractionPointOnClickMethod == null) { float mappedDistance = GetMappedDistance(val); ((BaseUnityPlugin)this).Logger.LogWarning((object)$"Failed to queue next extraction point because native OnClick method was unavailable. Candidate={((Object)val).name}, Distance={mappedDistance:0.##}"); } else if (!activationQueued || !((Object)(object)queuedExtractionPoint == (Object)(object)val)) { queuedExtractionPoint = val; queuedActivationTime = Time.time + 0.35f; activationQueued = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("Queued next extraction point auto activation. Source={0}, Priority={1}, Candidate={2}, Distance={3:0.##}", fromUnlockEvent ? "UnlockEvent" : "PeriodicScan", priorityMode?.Value, ((Object)val).name, GetMappedDistance(val))); } } } private void DrawInfo(ConfigEntryBase entry) { GUILayout.Label(entry.BoxedValue?.ToString() ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); } private static ConfigDescription ConfigDescriptionWithOrder(string description, int order) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = order } }); } private static void ExtractionPointOnClickPostfix(ExtractionPoint __instance) { WzcAutoExtractionPointPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.IsFeatureEnabled() && IsHostAuthority() && !((Object)(object)__instance == (Object)null) && !((Object)(object)RoundDirector.instance == (Object)null) && SemiFunc.RunIsLevel() && !instance.firstExtractionPointCaptured && instance.GetExtractionPointsCompleted(RoundDirector.instance) <= 0) { ((BaseUnityPlugin)instance).Logger.LogInfo((object)"ExtractionPoint.OnClick observed on first extraction point candidate."); instance.CaptureFirstExtractionPoint(__instance); } } private static void ExtractionPointsUnlockRpcPostfix(RoundDirector __instance) { WzcAutoExtractionPointPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.IsFeatureEnabled() && IsHostAuthority() && !((Object)(object)__instance == (Object)null) && SemiFunc.RunIsLevel()) { int extractionPointsCompleted = instance.GetExtractionPointsCompleted(__instance); int extractionPointsTotal = instance.GetExtractionPointsTotal(__instance); bool allExtractionPointsCompleted = instance.GetAllExtractionPointsCompleted(__instance); ((BaseUnityPlugin)instance).Logger.LogInfo((object)$"Extraction points unlocked. Completed={extractionPointsCompleted}, Total={extractionPointsTotal}, Active={instance.GetExtractionPointActive(__instance)}, AllCompleted={allExtractionPointsCompleted}"); if (!(extractionPointsCompleted <= 0 || extractionPointsTotal <= 0 || extractionPointsCompleted >= extractionPointsTotal || allExtractionPointsCompleted)) { instance.QueueNextExtractionPointActivation(fromUnlockEvent: true); } } } } internal sealed class ConfigurationManagerAttributes { public bool? ShowRangeAsPercent; public Action CustomDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; }