Decompiled source of PEAK Ticks v1.1.0
tony4twentys-PEAK Ticks.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using PEAKLib.Core; using Peak.Afflictions; using Photon.Pun; using Photon.Realtime; using PhotonCustomPropsUtils; using UnityEngine; using Zorro.Core; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyTitle("PEAK Ticks")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("PEAK Ticks")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("30fd5e37-dd72-4b02-9e20-7eeeee2178cc")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace PEAK_Ticks { public static class PeakticksInterop { public const string PluginGuid = "tony4twentys.PEAK_Ticks"; public static bool IsGameplayEnabled => TickModAuthorizationService.IsGameplayEnabled; public static bool CanAttachAnotherTick(Character character) { return TickAttachService.CanAttachAnotherTick(character); } public static int CountTicksOnCharacter(Character character) { return TickAttachService.CountTicksOnCharacter(character); } public static bool TryAttachTick(Character target, string attachSource, out int tickNumber) { return TickAttachService.TrySpawnAttachedTick(target, attachSource, null, out tickNumber); } } [BepInPlugin("tony4twentys.PEAK_Ticks", "PEAK Ticks", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class PEAKTicksPlugin : BaseUnityPlugin { public const string PluginGuid = "tony4twentys.PEAK_Ticks"; public const string PluginName = "PEAK Ticks"; public const string PluginVersion = "1.1.0"; public const string PhotonCustomPropsPluginGuid = "com.snosz.photoncustompropsutils"; public const float VanillaGrowthDurationSeconds = 300f; public const float VanillaMaxScale = 1f; public const float VanillaPoisonPulseIntervalSeconds = 29f; public const float VanillaPoisonPerPulse = 0.05f; public const float VanillaMaxPoisonPerTick = 0.5f; public const float VanillaVegetationAttachChance = 0.01f; private const string ExtrasConfigSection = "Extras"; private const string PoisonConfigSection = "Poison"; private const float MaxGrowthDurationConfigSeconds = 999999f; private const float MaxScaleConfigValue = 999999f; private const float MaxPoisonPulseIntervalConfigSeconds = 999999f; private const float MaxPoisonPerPulseConfigValue = 999999f; private const float MaxPoisonPerTickConfigValue = 999999f; private const float MaxPoisonScaleTierSizeConfigValue = 999999f; private const float MinPoisonScaleTierSizeConfigValue = 0.01f; private Harmony _harmony; private ConfigEntry<int> _maxTicksPerPlayer; private ConfigEntry<string> _attachBodyparts; private ConfigEntry<bool> _enableVegetationAttach; private ConfigEntry<float> _vegetationAttachChance; private ConfigEntry<bool> _enableTickBomb; private ConfigEntry<float> _tickBombChance; private ConfigEntry<int> _tickBombMinTicks; private ConfigEntry<int> _tickBombMaxTicks; private ConfigEntry<float> _vegetationTouchCooldownSeconds; private ConfigEntry<bool> _logVegetationTouch; private ConfigEntry<float> _poisonPulseIntervalSeconds; private ConfigEntry<bool> _useInjuryInsteadOfPoison; private ConfigEntry<float> _poisonPerPulse; private ConfigEntry<float> _maxPoisonPerTick; private ConfigEntry<bool> _uncappedPoisonPerTick; private ConfigEntry<bool> _sizeBasedPoison; private ConfigEntry<float> _startingPoisonPerPulse; private ConfigEntry<float> _poisonScaleTierSize; private ConfigEntry<float> _growthDurationSeconds; private ConfigEntry<float> _maxScale; private ConfigEntry<bool> _uncappedGrowth; private Coroutine _segmentProbeCoroutine; private Coroutine _runtimeCoroutine; private int _lastPolledMapSegmentKey = int.MinValue; private int _lastPolledVegetationKey = int.MinValue; private bool _pendingIncrementalPass; private float _nextProbeRetryTime; private static readonly WaitForSeconds RuntimePollWait = new WaitForSeconds(1f); public static PEAKTicksPlugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Expected O, but got Unknown //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Expected O, but got Unknown //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Expected O, but got Unknown //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Expected O, but got Unknown //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Expected O, but got Unknown //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Expected O, but got Unknown //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Expected O, but got Unknown //IL_03f4: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Expected O, but got Unknown //IL_044c: Unknown result type (might be due to invalid IL or missing references) //IL_0456: Expected O, but got Unknown //IL_04d0: Unknown result type (might be due to invalid IL or missing references) //IL_04de: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _maxTicksPerPlayer = ((BaseUnityPlugin)this).Config.Bind<int>("Attach", "MaxTicksPerPlayer", 18, new ConfigDescription("Maximum number of ticks that can be attached to one player at once. Vanilla is 1.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 18), Array.Empty<object>())); _attachBodyparts = ((BaseUnityPlugin)this).Config.Bind<string>("Attach", "AttachBodyparts", "Knee_R,Knee_L,Hip_R,Hip_L,Hip,Torso,Shoulder_R,Shoulder_L,Elbow_R,Elbow_L,Arm_L,Arm_R,Foot_R,Foot_L,Neck,Head,Hand_R,Hand_L", "Comma-separated bodyparts eligible for random tick attach. Each bodypart holds at most one tick."); _enableVegetationAttach = ((BaseUnityPlugin)this).Config.Bind<bool>("Vegetation", "EnableVegetationAttach", true, "When true, walking through whitelisted plants can attach ticks in Shore, Tropics, Roots, Alpine, Mesa, and Gloom (Swamp)."); _vegetationAttachChance = ((BaseUnityPlugin)this).Config.Bind<float>("Vegetation", "AttachChance", 0.01f, new ConfigDescription("Chance (0-1) that touching whitelisted vegetation triggers a tick attach burst. Vanilla TickTrigger tickChance is 0.01.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); _enableTickBomb = ((BaseUnityPlugin)this).Config.Bind<bool>("Vegetation", "EnableTickBomb", true, "When true, successful attach bursts can rarely roll a tick bomb instead of a normal burst."); _tickBombChance = ((BaseUnityPlugin)this).Config.Bind<float>("Vegetation", "TickBombChance", 0.01f, new ConfigDescription("Chance (0-1) of a tick bomb on each successful attach burst (only if EnableTickBomb is true). Defaults to AttachChance (vanilla tickChance 0.01).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); _tickBombMinTicks = ((BaseUnityPlugin)this).Config.Bind<int>("Vegetation", "TickBombMinTicks", 2, new ConfigDescription("Minimum ticks attached during a tick bomb burst.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 18), Array.Empty<object>())); _tickBombMaxTicks = ((BaseUnityPlugin)this).Config.Bind<int>("Vegetation", "TickBombMaxTicks", 18, new ConfigDescription("Maximum ticks attached during a tick bomb burst.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 18), Array.Empty<object>())); _vegetationTouchCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Vegetation", "TouchCooldownSeconds", 60f, new ConfigDescription("Seconds before the same player can trigger another vegetation attach burst.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>())); _logVegetationTouch = ((BaseUnityPlugin)this).Config.Bind<bool>("Vegetation", "LogVegetationTouch", false, "When true, logs each vegetation overlap/bounds touch detection (useful for debugging)."); _poisonPulseIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Poison", "PoisonPulseIntervalSeconds", 29f, new ConfigDescription("Seconds between tick status pulses. Vanilla is 29.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 999999f), Array.Empty<object>())); _useInjuryInsteadOfPoison = ((BaseUnityPlugin)this).Config.Bind<bool>("Poison", "UseInjuryInsteadOfPoison", false, "When true, tick pulses apply Injury instead of Poison. All other Poison settings still control pulse rate, amount, caps, and size tiers."); _poisonPerPulse = ((BaseUnityPlugin)this).Config.Bind<float>("Poison", "PoisonPerPulse", 0.05f, new ConfigDescription("Flat poison added on each pulse. Ignored when SizeBasedPoison is true. Vanilla is 0.05.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 999999f), Array.Empty<object>())); _sizeBasedPoison = ((BaseUnityPlugin)this).Config.Bind<bool>("Poison", "SizeBasedPoison", false, "When true, poison per pulse scales with tick size instead of using PoisonPerPulse."); _startingPoisonPerPulse = ((BaseUnityPlugin)this).Config.Bind<float>("Poison", "StartingPoisonPerPulse", 0.1f, new ConfigDescription("Base poison per pulse at tier 0 when SizeBasedPoison is true. Each tier adds another full step (0.1 -> 0.2 -> 0.3).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 999999f), Array.Empty<object>())); _poisonScaleTierSize = ((BaseUnityPlugin)this).Config.Bind<float>("Poison", "PoisonScaleTierSize", 0.5f, new ConfigDescription("Each time tick scale rises by this amount, poison per pulse increases by StartingPoisonPerPulse. Default 0.5 (tier 1 at scale 0.5, ~150s with vanilla 300s growth).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 999999f), Array.Empty<object>())); _maxPoisonPerTick = ((BaseUnityPlugin)this).Config.Bind<float>("Poison", "MaxPoisonPerTick", 0.5f, new ConfigDescription("Maximum poison status one tick can contribute over its lifetime. Ignored when UncappedPoisonPerTick is true. Vanilla is 0.5.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 999999f), Array.Empty<object>())); _uncappedPoisonPerTick = ((BaseUnityPlugin)this).Config.Bind<bool>("Poison", "UncappedPoisonPerTick", false, "When true, each tick keeps adding poison every pulse and MaxPoisonPerTick is ignored."); _growthDurationSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Extras", "GrowthDurationSeconds", 300f, new ConfigDescription("Seconds for an attached tick to reach MaxScale. Vanilla is 300 (5 minutes). Minimum 1.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 999999f), Array.Empty<object>())); _maxScale = ((BaseUnityPlugin)this).Config.Bind<float>("Extras", "MaxScale", 1f, new ConfigDescription("Target scale when growth reaches GrowthDurationSeconds. Vanilla is 1. Ignored when UncappedGrowth is true.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 999999f), Array.Empty<object>())); _uncappedGrowth = ((BaseUnityPlugin)this).Config.Bind<bool>("Extras", "UncappedGrowth", false, "When true, ticks keep growing forever and MaxScale is ignored. When false, growth stops at MaxScale."); ((BaseUnityPlugin)this).Config.Save(); ApplySnapshot(BuildSnapshot()); BindConfigChangedHandlers(); TickModAuthorizationService.Initialize(); _harmony = new Harmony("tony4twentys.PEAK_Ticks"); MethodInfo methodInfo = AccessTools.Method(typeof(Interaction), "DoInteractableRaycasts", new Type[3] { typeof(IInteractible).MakeByRefType(), typeof(float), typeof(bool) }, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"[PEAK Ticks] Failed to find Interaction.DoInteractableRaycasts for patching."); } else { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(InteractionLookDownTickRaycastPrefixPatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } _harmony.PatchAll(typeof(BugfixTryFindCharacterAndAttachBugPatch)); _harmony.PatchAll(typeof(BugfixUnregisterAssignedBodypartPatch)); _harmony.PatchAll(typeof(BugfixTickRemovalIsInteractiblePatch)); _harmony.PatchAll(typeof(MapHandlerSegmentVegetationRefreshPatch)); _harmony.PatchAll(typeof(VanillaTickTriggerAttachPatch)); _runtimeCoroutine = ((MonoBehaviour)this).StartCoroutine(RuntimeLoop()); try { _harmony.PatchAll(typeof(TickScaleDurationTranspiler)); _harmony.PatchAll(typeof(TickScalePatch)); ((MonoBehaviour)this).StartCoroutine(TickScaleService.ApplyTickScaleEndOfFrame()); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[PEAK Ticks] Tick growth patches failed to apply: " + ex)); } try { _harmony.PatchAll(typeof(TickPoisonLateUpdatePrefixPatch)); _harmony.PatchAll(typeof(TickPoisonLateUpdateTranspiler)); _harmony.PatchAll(typeof(SkipPreventPoisonHealingWhenInjuryModePatch)); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("[PEAK Ticks] Tick poison patches failed to apply: " + ex2)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("PEAK Ticks v1.1.0 loaded. Host handshake + config sync enabled. Growth: " + TickScaleService.FormatStartupSummary() + ". Status: " + TickPoisonService.FormatStartupSummary() + ".")); if (TickVegetationAttachService.Enabled) { TickVegetationAttachService.DestroyRegisteredPlantTriggers(); } } internal TickConfigSnapshot BuildSnapshot() { return new TickConfigSnapshot { MaxTicksPerPlayer = _maxTicksPerPlayer.Value, AttachBodypartsCsv = _attachBodyparts.Value, EnableVegetationAttach = _enableVegetationAttach.Value, VegetationAttachChance = _vegetationAttachChance.Value, EnableTickBomb = _enableTickBomb.Value, TickBombChance = _tickBombChance.Value, TickBombMinTicks = _tickBombMinTicks.Value, TickBombMaxTicks = _tickBombMaxTicks.Value, VegetationTouchCooldownSeconds = _vegetationTouchCooldownSeconds.Value, LogVegetationTouch = _logVegetationTouch.Value, PoisonPulseIntervalSeconds = _poisonPulseIntervalSeconds.Value, UseInjuryInsteadOfPoison = _useInjuryInsteadOfPoison.Value, PoisonPerPulse = _poisonPerPulse.Value, SizeBasedPoison = _sizeBasedPoison.Value, StartingPoisonPerPulse = _startingPoisonPerPulse.Value, PoisonScaleTierSize = _poisonScaleTierSize.Value, MaxPoisonPerTick = _maxPoisonPerTick.Value, UncappedPoisonPerTick = _uncappedPoisonPerTick.Value, GrowthDurationSeconds = _growthDurationSeconds.Value, MaxScale = _maxScale.Value, UncappedGrowth = _uncappedGrowth.Value }; } internal void ApplySnapshot(TickConfigSnapshot snapshot) { if (snapshot != null) { TickAttachService.RefreshConfig(snapshot.MaxTicksPerPlayer, snapshot.AttachBodypartsCsv); TickScaleService.RefreshConfig(snapshot.GrowthDurationSeconds, snapshot.MaxScale, snapshot.UncappedGrowth); TickPoisonService.RefreshConfig(snapshot.PoisonPulseIntervalSeconds, snapshot.PoisonPerPulse, snapshot.MaxPoisonPerTick, snapshot.UncappedPoisonPerTick, snapshot.SizeBasedPoison, snapshot.StartingPoisonPerPulse, snapshot.PoisonScaleTierSize, snapshot.UseInjuryInsteadOfPoison); TickVegetationAttachService.RefreshConfig(snapshot.EnableVegetationAttach, snapshot.VegetationAttachChance, snapshot.EnableTickBomb, snapshot.TickBombChance, snapshot.TickBombMinTicks, snapshot.TickBombMaxTicks, snapshot.VegetationTouchCooldownSeconds); TickVegetationAttachService.LogVegetationTouch = snapshot.LogVegetationTouch; } } private void BindConfigChangedHandlers() { _maxTicksPerPlayer.SettingChanged += OnGameplayConfigChanged; _attachBodyparts.SettingChanged += OnGameplayConfigChanged; _enableVegetationAttach.SettingChanged += OnGameplayConfigChanged; _vegetationAttachChance.SettingChanged += OnGameplayConfigChanged; _enableTickBomb.SettingChanged += OnGameplayConfigChanged; _tickBombChance.SettingChanged += OnGameplayConfigChanged; _tickBombMinTicks.SettingChanged += OnGameplayConfigChanged; _tickBombMaxTicks.SettingChanged += OnGameplayConfigChanged; _vegetationTouchCooldownSeconds.SettingChanged += OnGameplayConfigChanged; _logVegetationTouch.SettingChanged += OnGameplayConfigChanged; _poisonPulseIntervalSeconds.SettingChanged += OnGameplayConfigChanged; _useInjuryInsteadOfPoison.SettingChanged += OnGameplayConfigChanged; _poisonPerPulse.SettingChanged += OnGameplayConfigChanged; _sizeBasedPoison.SettingChanged += OnGameplayConfigChanged; _startingPoisonPerPulse.SettingChanged += OnGameplayConfigChanged; _poisonScaleTierSize.SettingChanged += OnGameplayConfigChanged; _maxPoisonPerTick.SettingChanged += OnGameplayConfigChanged; _uncappedPoisonPerTick.SettingChanged += OnGameplayConfigChanged; _growthDurationSeconds.SettingChanged += OnGameplayConfigChanged; _maxScale.SettingChanged += OnGameplayConfigChanged; _uncappedGrowth.SettingChanged += OnGameplayConfigChanged; } private void OnGameplayConfigChanged(object sender, EventArgs args) { TickModAuthorizationService.OnHostConfigChangedLocally(); } public static bool IsVegetationProbeSetupRunning() { if ((Object)(object)Instance != (Object)null) { return Instance._segmentProbeCoroutine != null; } return false; } public static void ScheduleVegetationProbeSetup(bool forceRestart = true) { if ((Object)(object)Instance == (Object)null) { return; } Instance._pendingIncrementalPass = false; if (Instance._segmentProbeCoroutine != null) { if (!forceRestart) { return; } ((MonoBehaviour)Instance).StopCoroutine(Instance._segmentProbeCoroutine); Instance._segmentProbeCoroutine = null; } Instance._segmentProbeCoroutine = ((MonoBehaviour)Instance).StartCoroutine(Instance.SegmentProbeSetupCoroutine()); } public static void ScheduleVegetationIncrementalSetup() { if (!((Object)(object)Instance == (Object)null)) { if (Instance._segmentProbeCoroutine != null) { Instance._pendingIncrementalPass = true; } else { Instance._segmentProbeCoroutine = ((MonoBehaviour)Instance).StartCoroutine(Instance.IncrementalProbeSetupCoroutine()); } } } private IEnumerator IncrementalProbeSetupCoroutine() { yield return null; if (!TickModAuthorizationService.IsLocalVegetationActive || !TickVegetationAttachService.Enabled || !TickVegetationAttachService.IsCurrentBiomeAllowed()) { _segmentProbeCoroutine = null; yield break; } yield return TickVegetationAttachService.SetupVegetationProbesAsync(incrementalOnly: true); _segmentProbeCoroutine = null; if (_pendingIncrementalPass) { _pendingIncrementalPass = false; ScheduleVegetationIncrementalSetup(); } } private IEnumerator SegmentProbeSetupCoroutine() { yield return null; yield return (object)new WaitForEndOfFrame(); if (!TickModAuthorizationService.IsLocalVegetationActive || !TickVegetationAttachService.Enabled) { _segmentProbeCoroutine = null; yield break; } if (!TickVegetationAttachService.IsCurrentBiomeAllowed()) { TickVegetationAttachService.LogMapHandlerStateForDiagnostics("probe setup skipped", force: true); _segmentProbeCoroutine = null; yield break; } for (int attempt = 0; attempt < 24; attempt++) { if (TickVegetationAttachService.GetActiveRegistrationRoots().Length != 0) { break; } yield return (object)new WaitForSeconds(0.5f); } if (TickVegetationAttachService.ShouldRunDelayedVegetationPass()) { yield return TickVegetationAttachService.RunSwampGloomSetupWithRetriesAsync(); } else { yield return TickVegetationAttachService.SetupVegetationProbesAsync(); } _segmentProbeCoroutine = null; if (_pendingIncrementalPass) { ScheduleVegetationIncrementalSetup(); } } private void TryRefreshVegetationProbes() { if (!TickModAuthorizationService.IsLocalVegetationActive || !TickVegetationAttachService.Enabled || (Object)(object)Singleton<MapHandler>.Instance == (Object)null) { return; } int currentSegmentCacheKey = TickVegetationProbeService.GetCurrentSegmentCacheKey(); int vegetationCacheKey = TickVegetationProbeService.GetVegetationCacheKey(); TickVegetationAttachService.LogMapHandlerStateForDiagnostics("segment poll"); if (TickVegetationAttachService.IsCurrentBiomeAllowed() && (vegetationCacheKey != _lastPolledVegetationKey || currentSegmentCacheKey != _lastPolledMapSegmentKey || !TickVegetationProbeService.IsReadyForCurrentSegment())) { if (vegetationCacheKey != _lastPolledVegetationKey || currentSegmentCacheKey != _lastPolledMapSegmentKey) { _lastPolledVegetationKey = vegetationCacheKey; _lastPolledMapSegmentKey = currentSegmentCacheKey; TickVegetationAttachService.RequestSegmentVegetationRefresh(); } else if (!TickVegetationProbeService.IsReadyForCurrentSegment() && !IsVegetationProbeSetupRunning() && !(Time.time < _nextProbeRetryTime)) { _nextProbeRetryTime = Time.time + 5f; ScheduleVegetationProbeSetup(forceRestart: false); } } } internal static void NotifySegmentProbeSetupScheduled(int mapSegmentKey) { if ((Object)(object)Instance != (Object)null) { Instance._lastPolledMapSegmentKey = mapSegmentKey; Instance._lastPolledVegetationKey = TickVegetationProbeService.GetVegetationCacheKey(); } } private IEnumerator RuntimeLoop() { while (true) { TickModAuthorizationService.EnsureMasterClientAuthorized(); if (TickModAuthorizationService.IsLocalVegetationActive && TickVegetationAttachService.Enabled) { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter != (Object)null) { PeakTickVegetationScanner.AttachToCharacter(localCharacter); } } TryRefreshVegetationProbes(); yield return RuntimePollWait; } } private void OnDestroy() { if (_runtimeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_runtimeCoroutine); _runtimeCoroutine = null; } if (_segmentProbeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_segmentProbeCoroutine); _segmentProbeCoroutine = null; } Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } Log = null; } } internal static class TickVegetationAttachService { internal struct VegetationDetectionCandidate { public GameObject DetectionHost; public GameObject SourceRoot; public string SourceLabel; public float MaxBoundsExtent; public bool BoundsOnly; } private const float MaxSpreadTagBoundsExtent = 1.25f; private const float MaxFoliageTagBoundsExtent = 2.5f; private static readonly string[] SpreadGroupKeywords = new string[9] { "beachgrass", "fern", "driftwood", "vine", "weed", "bushes", "shrub", "dead grass", "cattails" }; private static readonly string[] GloomPropFolderNames = new string[3] { "Cattails", "VenusLeaves", "Vines" }; private static float _nextBurstAllowedTime; private static int _burstCharacterId = int.MinValue; private static int _vegetationBurstCounter; private static int _lastCooldownAppliedBurstId = -1; private static float _nextBurstSkipLogTime; private static string _lastBurstSkipReason; private static readonly string[] VegetationAttachKeywords = new string[18] { "poisonivy", "bushleaves", "berrybush", "beachgrass", "driftwood", "fern", "vine", "hangingvine", "jungle_tiny", "jungle_minibanana", "jungle_sharpplant", "monstera", "ice_horsetail", "ice_deadshrub", "ice_pinebushleaves", "jungle_weed", "cattails", "venusleaves" }; private static readonly string[] NamedVegetationTriggerKeywords = new string[20] { "beachgrass", "fern", "driftwood", "vine", "weed", "monstera", "poisonivy", "bushleaves", "hangingvine", "jungle_tiny", "jungle_minibanana", "jungle_sharpplant", "shrub", "dead grass", "ice_horsetail", "ice_deadshrub", "ice_pinebushleaves", "jungle_weed", "cattails", "venusleaves" }; private static readonly SpawnPool[] AllowedAttachSpawnPools = (SpawnPool[])(object)new SpawnPool[2] { (SpawnPool)8, (SpawnPool)16 }; private const int NormalBurstTickCount = 1; private static int _lastLoggedMapHandlerKey = int.MinValue; private static int _lastSegmentRefreshKey = int.MinValue; private static int _lastMapSegmentKey = int.MinValue; public static bool Enabled { get; private set; } = true; public static float AttachChance { get; private set; } = 0.01f; public static bool EnableTickBomb { get; private set; } = true; public static float TickBombChance { get; private set; } = 0.01f; public static int TickBombMinTicks { get; private set; } = 2; public static int TickBombMaxTicks { get; private set; } = 18; public static float TouchCooldownSeconds { get; private set; } = 60f; public static bool LogVegetationTouch { get; set; } public static void RefreshConfig(bool enabled, float attachChance, bool enableTickBomb, float tickBombChance, int tickBombMinTicks, int tickBombMaxTicks, float touchCooldownSeconds) { Enabled = enabled; AttachChance = Mathf.Clamp01(attachChance); EnableTickBomb = enableTickBomb; TickBombChance = Mathf.Clamp01(tickBombChance); TickBombMinTicks = Mathf.Clamp(tickBombMinTicks, 1, 18); TickBombMaxTicks = Mathf.Clamp(tickBombMaxTicks, 1, 18); if (TickBombMaxTicks < TickBombMinTicks) { int tickBombMinTicks2 = TickBombMinTicks; TickBombMinTicks = TickBombMaxTicks; TickBombMaxTicks = tickBombMinTicks2; } TouchCooldownSeconds = Mathf.Max(0f, touchCooldownSeconds); } public static bool IsCurrentBiomeAllowed() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)Singleton<MapHandler>.Instance == (Object)null) { return false; } return TickBiomeRules.IsAllowedTickBiome(Singleton<MapHandler>.Instance.GetCurrentBiome()); } catch { return false; } } public static bool RollAttachChance() { return Random.value <= AttachChance; } private static bool RollTickBomb() { if (EnableTickBomb) { return Random.value <= TickBombChance; } return false; } private static int RollBurstTickCount(out bool isTickBomb) { isTickBomb = RollTickBomb(); if (isTickBomb) { return Random.Range(TickBombMinTicks, TickBombMaxTicks + 1); } return 1; } public static string GetCurrentBiomeNameForLog() { return GetCurrentBiomeName(); } public static void LogMapHandlerStateForDiagnostics(string context, bool force = false) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected I4, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Singleton<MapHandler>.Instance == (Object)null) { return; } int currentSegmentCacheKey = TickVegetationProbeService.GetCurrentSegmentCacheKey(); if (!force && currentSegmentCacheKey == _lastLoggedMapHandlerKey) { return; } _lastLoggedMapHandlerKey = currentSegmentCacheKey; string currentSegmentName = GetCurrentSegmentName(); string currentBiomeName = GetCurrentBiomeName(); int num = (int)Singleton<MapHandler>.Instance.GetCurrentBiome(); if (TickBiomeRules.IsAllowedTickBiome(Singleton<MapHandler>.Instance.GetCurrentBiome())) { ManualLogSource log = PEAKTicksPlugin.Log; if (log != null) { log.LogInfo((object)("[PEAK Ticks] MapHandler state (" + context + "): segment=" + currentSegmentName + ", biome=" + currentBiomeName + " (enum " + num + "), key=" + currentSegmentCacheKey + ", vegetation=allowed.")); } } else { ManualLogSource log2 = PEAKTicksPlugin.Log; if (log2 != null) { log2.LogWarning((object)("[PEAK Ticks] MapHandler state (" + context + "): segment=" + currentSegmentName + ", biome=" + currentBiomeName + " (enum " + num + "), key=" + currentSegmentCacheKey + ", vegetation=BLOCKED (not in AllowedTickBiomes).")); } } } public static Transform[] GetActiveRegistrationRoots() { if ((Object)(object)Singleton<MapHandler>.Instance == (Object)null || !IsCurrentBiomeAllowed()) { return (Transform[])(object)new Transform[0]; } if (IsSwampBiomeSegment()) { return GetSwampGloomPropFolderRoots(); } List<Transform> list = new List<Transform>(2); try { MapSegment currentMapSegment = MapHandler.CurrentMapSegment; AddRegistrationRoot(list, currentMapSegment.segmentParent); AddRegistrationRoot(list, currentMapSegment.segmentCampfire); } catch (Exception ex) { ManualLogSource log = PEAKTicksPlugin.Log; if (log != null) { log.LogWarning((object)("[PEAK Ticks] Failed to resolve active segment registration roots: " + ex.Message)); } } return list.ToArray(); } public static Transform[] GetSwampGloomPropFolderRoots() { List<Transform> list = new List<Transform>(GloomPropFolderNames.Length); CollectSwampGloomPropFolderRoots(list); return list.ToArray(); } private static Transform FindSwampSegmentPropsTransform() { Transform val = FindMapTransform(); if ((Object)(object)val == (Object)null) { return null; } Stack<Transform> stack = new Stack<Transform>(128); stack.Push(val); while (stack.Count > 0) { Transform val2 = stack.Pop(); for (int num = val2.childCount - 1; num >= 0; num--) { Transform child = val2.GetChild(num); if ((Object)(object)child != (Object)null) { stack.Push(child); } } if (string.Equals(((Object)val2).name, "Props", StringComparison.OrdinalIgnoreCase)) { string vegetationHierarchyPath = GetVegetationHierarchyPath(val2); if (vegetationHierarchyPath.IndexOf("/Swamp_Segment/", StringComparison.OrdinalIgnoreCase) >= 0 && vegetationHierarchyPath.IndexOf("/Gloom/", StringComparison.OrdinalIgnoreCase) >= 0) { return val2; } } } return null; } private static void CollectSwampGloomPropFolderRoots(List<Transform> roots) { Transform val = FindSwampSegmentPropsTransform(); if ((Object)(object)val == (Object)null) { ManualLogSource log = PEAKTicksPlugin.Log; if (log != null) { log.LogWarning((object)"[PEAK Ticks] Swamp Props folder not found (expected Map/.../Gloom/Swamp_Segment/Props)."); } return; } int num = 0; for (int i = 0; i < val.childCount; i++) { Transform child = val.GetChild(i); if (!((Object)(object)child == (Object)null) && ((Component)child).gameObject.activeInHierarchy && TryMatchGloomPropFolder(child, out var _)) { AddRegistrationRoot(roots, ((Component)child).gameObject); num++; } } if (num == 0) { ManualLogSource log2 = PEAKTicksPlugin.Log; if (log2 != null) { log2.LogWarning((object)("[PEAK Ticks] Swamp Props found at " + GetVegetationHierarchyPath(val) + " but Cattails/VenusLeaves/Vines folders are not ready yet.")); } return; } ManualLogSource log3 = PEAKTicksPlugin.Log; if (log3 != null) { log3.LogInfo((object)("[PEAK Ticks] Swamp gloom prop folders: " + num + " under " + GetVegetationHierarchyPath(val) + ".")); } } private static Transform FindMapTransform() { GameObject val = GameObject.Find("Map"); if ((Object)(object)val != (Object)null) { return val.transform; } try { MapSegment currentMapSegment = MapHandler.CurrentMapSegment; Transform val2 = (((Object)(object)currentMapSegment.segmentParent != (Object)null) ? currentMapSegment.segmentParent.transform : null); while ((Object)(object)val2 != (Object)null) { if (string.Equals(((Object)val2).name, "Map", StringComparison.OrdinalIgnoreCase)) { return val2; } val2 = val2.parent; } } catch { } return null; } public static IEnumerator RunSwampGloomSetupWithRetriesAsync() { float[] retryDelaysSeconds = new float[5] { 0f, 2f, 4f, 8f, 15f }; for (int attempt = 0; attempt < retryDelaysSeconds.Length; attempt++) { if (attempt > 0) { yield return (object)new WaitForSeconds(retryDelaysSeconds[attempt]); } if (GetSwampGloomPropFolderRoots().Length == 0) { ManualLogSource log = PEAKTicksPlugin.Log; if (log != null) { log.LogWarning((object)("[PEAK Ticks] Swamp gloom setup retry " + (attempt + 1) + "/" + retryDelaysSeconds.Length + ": prop folders not ready.")); } continue; } bool incrementalOnly = attempt > 0; yield return SetupVegetationProbesAsync(incrementalOnly); if (TickVegetationProbeService.TouchTargetCount < 50) { ManualLogSource log2 = PEAKTicksPlugin.Log; if (log2 != null) { log2.LogWarning((object)("[PEAK Ticks] Swamp gloom setup retry " + (attempt + 1) + "/" + retryDelaysSeconds.Length + ": registered " + TickVegetationProbeService.TouchTargetCount + " targets so far.")); } continue; } break; } } public static IEnumerator EnumerateSwampGloomPropDiscoveryAsync(Transform[] propFolderRoots, Action<VegetationDetectionCandidate> onCandidate, HashSet<int> seenSourceRootIds, Func<bool> shouldYield) { if (propFolderRoots == null || propFolderRoots.Length == 0 || onCandidate == null) { yield break; } foreach (Transform val in propFolderRoots) { if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy) { if (!TryMatchGloomPropFolder(val, out var folderName)) { folderName = ((Object)val).name; } IEnumerator gloomPass = CollectGloomPropFolderCandidatesAsync(val, folderName, onCandidate, seenSourceRootIds, shouldYield); while (gloomPass.MoveNext()) { yield return gloomPass.Current; } } } } private static void AddRegistrationRoot(List<Transform> roots, GameObject rootObject) { if ((Object)(object)rootObject == (Object)null || !rootObject.activeInHierarchy) { return; } Transform transform = rootObject.transform; for (int i = 0; i < roots.Count; i++) { if ((Object)(object)roots[i] == (Object)(object)transform) { return; } } roots.Add(transform); } public static void DestroyRegisteredPlantTriggers() { ClearModProbesInSegment(); } public static void RequestSegmentVegetationRefresh() { int vegetationCacheKey = TickVegetationProbeService.GetVegetationCacheKey(); int currentSegmentCacheKey = TickVegetationProbeService.GetCurrentSegmentCacheKey(); if (vegetationCacheKey == _lastSegmentRefreshKey) { if (currentSegmentCacheKey != _lastMapSegmentKey) { _lastMapSegmentKey = currentSegmentCacheKey; if (IsSwampBiomeSegment()) { if (TickVegetationProbeService.IsReadyForCurrentSegment()) { ManualLogSource log = PEAKTicksPlugin.Log; if (log != null) { log.LogInfo((object)("[PEAK Ticks] Swamp segment change skipped incremental scan (" + TickVegetationProbeService.TouchTargetCount + " targets ready).")); } } else { PEAKTicksPlugin.ScheduleVegetationIncrementalSetup(); } return; } } if (!TickVegetationProbeService.IsReadyForCurrentSegment() && !PEAKTicksPlugin.IsVegetationProbeSetupRunning()) { PEAKTicksPlugin.ScheduleVegetationProbeSetup(forceRestart: false); } } else { _lastSegmentRefreshKey = vegetationCacheKey; _lastMapSegmentKey = currentSegmentCacheKey; LogMapHandlerStateForDiagnostics("segment refresh", force: true); ClearModProbesInSegment(); TickVegetationProbeService.PrepareForSegmentChange(vegetationCacheKey); PEAKTicksPlugin.NotifySegmentProbeSetupScheduled(currentSegmentCacheKey); PEAKTicksPlugin.ScheduleVegetationProbeSetup(); } } public static void LogVegetationTouchDetected(string sourceLabel, string method) { if (LogVegetationTouch) { ManualLogSource log = PEAKTicksPlugin.Log; if (log != null) { log.LogInfo((object)("[PEAK Ticks] Vegetation touch: " + sourceLabel + " (" + method + ").")); } } } public static IEnumerator SetupVegetationProbesAsync(bool incrementalOnly = false) { yield return TickVegetationProbeService.SetupProbesForCurrentSegmentAsync(incrementalOnly); } public static bool HostNeedsFoliageProbe(GameObject host) { return HostNeedsFoliageAssist(host); } public static bool IsBerryBushTouchTarget(GameObject sourceRoot, string sourceLabel) { if ((Object)(object)sourceRoot != (Object)null && (Object)(object)sourceRoot.GetComponent<BerryBush>() != (Object)null) { return true; } return sourceLabel?.StartsWith("BerryBush:", StringComparison.Ordinal) ?? false; } public static bool TryComputeFoliageBounds(GameObject host, float maxExtent, out Bounds bounds) { return TryComputeTagBounds(host, maxExtent, out bounds); } public static bool IsSwampBiomeSegment() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 try { return (Object)(object)Singleton<MapHandler>.Instance != (Object)null && (int)Singleton<MapHandler>.Instance.GetCurrentBiome() == 8; } catch { return false; } } public static bool ShouldRunDelayedVegetationPass() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 try { if ((Object)(object)Singleton<MapHandler>.Instance == (Object)null) { return false; } return (int)Singleton<MapHandler>.Instance.GetCurrentBiome() == 8; } catch { return false; } } public static IEnumerator EnumerateVegetationDiscoveryAsync(Transform[] roots, Action<VegetationDetectionCandidate> onCandidate, HashSet<int> seenSourceRootIds, Func<bool> shouldYield, bool spreadAndGloomOnly = false) { if (roots == null || roots.Length == 0 || onCandidate == null) { yield break; } Stack<Transform> stack = new Stack<Transform>(512); HashSet<int> processedSpreadGroupIds = new HashSet<int>(); HashSet<int> processedGloomFolderIds = new HashSet<int>(); foreach (Transform val in roots) { if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy) { stack.Push(val); } } while (stack.Count > 0) { Transform current = stack.Pop(); if ((Object)(object)current == (Object)null || !((Component)current).gameObject.activeInHierarchy) { continue; } for (int num = current.childCount - 1; num >= 0; num--) { Transform child = current.GetChild(num); if ((Object)(object)child != (Object)null) { stack.Push(child); } } if (TryMatchGloomPropFolder(current, out var folderName)) { int instanceID = ((Object)current).GetInstanceID(); if (processedGloomFolderIds.Add(instanceID)) { IEnumerator gloomPass = CollectGloomPropFolderCandidatesAsync(current, folderName, onCandidate, seenSourceRootIds, shouldYield); while (gloomPass.MoveNext()) { yield return gloomPass.Current; } } } if (IsKeywordSpreadGroup(((Object)current).name, out var _) && IsAllowedAttachPath(current)) { int instanceID2 = ((Object)current).GetInstanceID(); if (processedSpreadGroupIds.Add(instanceID2)) { IEnumerator gloomPass = CollectSpreadGroupCandidatesAsync(current, onCandidate, seenSourceRootIds, shouldYield); while (gloomPass.MoveNext()) { yield return gloomPass.Current; } } } if (spreadAndGloomOnly) { if (shouldYield != null && shouldYield()) { yield return null; } continue; } Renderer component = ((Component)current).GetComponent<Renderer>(); if ((Object)(object)component != (Object)null && component.enabled) { if (TryBuildDetectionCandidate(((Component)component).gameObject, out var candidate)) { TryEmitDetectionCandidate(candidate, seenSourceRootIds, onCandidate); } if (shouldYield != null && shouldYield()) { yield return null; } } Collider component2 = ((Component)current).GetComponent<Collider>(); if ((Object)(object)component2 != (Object)null && component2.enabled) { if (TryBuildDetectionCandidate(((Component)component2).gameObject, out var candidate2)) { TryEmitDetectionCandidate(candidate2, seenSourceRootIds, onCandidate); } if (shouldYield != null && shouldYield()) { yield return null; } } } } private static bool MatchesGloomPropFolderName(string transformName, string folderName) { if (string.IsNullOrEmpty(transformName) || string.IsNullOrEmpty(folderName)) { return false; } if (string.Equals(transformName, folderName, StringComparison.OrdinalIgnoreCase)) { return true; } if (string.Equals(folderName, "Vines", StringComparison.OrdinalIgnoreCase) && transformName.StartsWith("Vines", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } private static bool TryMatchGloomPropFolder(Transform transform, out string folderName) { folderName = null; if ((Object)(object)transform == (Object)null) { return false; } for (int i = 0; i < GloomPropFolderNames.Length; i++) { string text = GloomPropFolderNames[i]; if (MatchesGloomPropFolderName(((Object)transform).name, text)) { string vegetationHierarchyPath = GetVegetationHierarchyPath(transform); if (vegetationHierarchyPath.IndexOf("/Props/", StringComparison.OrdinalIgnoreCase) >= 0 && (vegetationHierarchyPath.IndexOf("/Swamp_Segment/", StringComparison.OrdinalIgnoreCase) >= 0 || vegetationHierarchyPath.IndexOf("/Gloom/", StringComparison.OrdinalIgnoreCase) >= 0)) { folderName = text; return true; } } } return false; } private static IEnumerator CollectGloomPropFolderCandidatesAsync(Transform folderTransform, string folderName, Action<VegetationDetectionCandidate> onCandidate, HashSet<int> seenSourceRootIds, Func<bool> shouldYield) { string sourceLabel = GetGloomPropSourceLabel(folderName); for (int childIndex = 0; childIndex < folderTransform.childCount; childIndex++) { Transform child = folderTransform.GetChild(childIndex); if ((Object)(object)child == (Object)null || !((Component)child).gameObject.activeInHierarchy || !IsAllowedAttachPath(child) || ShouldSkipSpreadClumpHost(((Component)child).gameObject)) { continue; } if (IsKeywordSpreadGroup(((Object)child).name, out var _)) { IEnumerator spreadPass = CollectSpreadGroupCandidatesAsync(child, onCandidate, seenSourceRootIds, shouldYield); while (spreadPass.MoveNext()) { yield return spreadPass.Current; } continue; } TryEmitDetectionCandidate(BuildSpreadCandidate(((Component)child).gameObject, ((Component)child).gameObject, sourceLabel), seenSourceRootIds, onCandidate); if (shouldYield != null && shouldYield()) { yield return null; } } IEnumerator deepPass = CollectGloomFolderDeepClumpsAsync(folderTransform, sourceLabel, onCandidate, seenSourceRootIds, shouldYield); while (deepPass.MoveNext()) { yield return deepPass.Current; } } private static IEnumerator CollectGloomFolderDeepClumpsAsync(Transform folderTransform, string sourceLabel, Action<VegetationDetectionCandidate> onCandidate, HashSet<int> seenSourceRootIds, Func<bool> shouldYield) { Stack<Transform> stack = new Stack<Transform>(); for (int i = 0; i < folderTransform.childCount; i++) { Transform child = folderTransform.GetChild(i); if ((Object)(object)child != (Object)null) { stack.Push(child); } } while (stack.Count > 0) { Transform val = stack.Pop(); for (int num = val.childCount - 1; num >= 0; num--) { stack.Push(val.GetChild(num)); } Renderer component = ((Component)val).GetComponent<Renderer>(); if ((Object)(object)component == (Object)null || !IsAllowedAttachPath(((Component)component).transform)) { continue; } Transform val2 = val; while ((Object)(object)val2.parent != (Object)null && (Object)(object)val2.parent != (Object)(object)folderTransform) { val2 = val2.parent; } if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.parent != (Object)(object)folderTransform) && !ShouldSkipSpreadClumpHost(((Component)val2).gameObject)) { TryEmitDetectionCandidate(BuildSpreadCandidate(((Component)val2).gameObject, ((Component)val2).gameObject, sourceLabel), seenSourceRootIds, onCandidate); if (shouldYield != null && shouldYield()) { yield return null; } } } } private static bool ShouldSkipSpreadClumpHost(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return true; } BerryBush component = gameObject.GetComponent<BerryBush>(); if ((Object)(object)component != (Object)null && QualifiesBerryBush(component)) { return true; } if ((Object)(object)gameObject.GetComponent<BerryVine>() != (Object)null || (Object)(object)gameObject.GetComponent<JungleVine>() != (Object)null) { return true; } if (TryEvaluateWhitelistedGameObject(gameObject, out var _, out var sourceLabel) && !IsBoundsOnlySourceLabel(sourceLabel)) { return true; } return false; } private static IEnumerator CollectSpreadGroupCandidatesAsync(Transform groupTransform, Action<VegetationDetectionCandidate> onCandidate, HashSet<int> seenSourceRootIds, Func<bool> shouldYield) { if ((Object)(object)groupTransform == (Object)null || !((Component)groupTransform).gameObject.activeInHierarchy) { yield break; } if (!TryEvaluateWhitelistedGameObject(((Component)groupTransform).gameObject, out var _, out var sourceLabel)) { sourceLabel = GetSpreadGroupSourceLabel(GetMatchedVegetationKeyword(((Object)groupTransform).name, SpreadGroupKeywords)); } bool any = false; for (int childIndex = 0; childIndex < groupTransform.childCount; childIndex++) { Transform child = groupTransform.GetChild(childIndex); if (!((Object)(object)child == (Object)null) && ((Component)child).gameObject.activeInHierarchy && IsAllowedAttachPath(child) && !ShouldSkipSpreadClumpHost(((Component)child).gameObject)) { if (TryEmitDetectionCandidate(BuildSpreadCandidate(((Component)child).gameObject, ((Component)child).gameObject, sourceLabel), seenSourceRootIds, onCandidate)) { any = true; } if (shouldYield != null && shouldYield()) { yield return null; } } } if (!any) { TryEmitDetectionCandidate(BuildSpreadCandidate(((Component)groupTransform).gameObject, ((Component)groupTransform).gameObject, sourceLabel), seenSourceRootIds, onCandidate); } } private static VegetationDetectionCandidate BuildSpreadCandidate(GameObject detectionHost, GameObject sourceRoot, string sourceLabel) { return new VegetationDetectionCandidate { DetectionHost = detectionHost, SourceRoot = sourceRoot, SourceLabel = sourceLabel, MaxBoundsExtent = 1.25f, BoundsOnly = true }; } private static bool TryBuildDetectionCandidate(GameObject hitObject, out VegetationDetectionCandidate candidate) { candidate = default(VegetationDetectionCandidate); if ((Object)(object)hitObject == (Object)null || !hitObject.activeInHierarchy || !IsAllowedAttachPath(hitObject.transform)) { return false; } if ((Object)(object)hitObject.GetComponentInParent<PeakTickVegetationScanner>() != (Object)null) { return false; } if (!TryEvaluateWhitelistedGameObject(hitObject, out var sourceRoot, out var sourceLabel)) { return false; } if (!IsBoundsOnlySourceLabel(sourceLabel)) { candidate = new VegetationDetectionCandidate { DetectionHost = sourceRoot, SourceRoot = sourceRoot, SourceLabel = sourceLabel, MaxBoundsExtent = 2.5f, BoundsOnly = false }; return true; } Transform val = FindAncestorSpreadGroup(hitObject.transform); if ((Object)(object)val != (Object)null) { Renderer component = hitObject.GetComponent<Renderer>(); GameObject val2 = (((Object)(object)component != (Object)null) ? ResolveFoliageTagHost(component, ((Component)val).gameObject) : sourceRoot); if (!TryEvaluateWhitelistedGameObject(val2, out sourceRoot, out sourceLabel)) { sourceRoot = val2; sourceLabel = GetSpreadGroupSourceLabel(GetMatchedVegetationKeyword(((Object)val).name, SpreadGroupKeywords)); } candidate = BuildSpreadCandidate(val2, sourceRoot, sourceLabel); return true; } candidate = new VegetationDetectionCandidate { DetectionHost = sourceRoot, SourceRoot = sourceRoot, SourceLabel = sourceLabel, MaxBoundsExtent = 2.5f, BoundsOnly = IsBoundsOnlySourceLabel(sourceLabel) }; return true; } private static bool IsBoundsOnlySourceLabel(string sourceLabel) { if (string.IsNullOrEmpty(sourceLabel)) { return false; } if (!sourceLabel.StartsWith("Group:", StringComparison.Ordinal) && !sourceLabel.StartsWith("Grass:", StringComparison.Ordinal)) { return sourceLabel.StartsWith("Keyword:", StringComparison.Ordinal); } return true; } private static bool TryEmitDetectionCandidate(VegetationDetectionCandidate candidate, HashSet<int> seenSourceRootIds, Action<VegetationDetectionCandidate> onCandidate) { if ((Object)(object)candidate.SourceRoot == (Object)null || !candidate.SourceRoot.activeInHierarchy || (Object)(object)candidate.DetectionHost == (Object)null || !candidate.DetectionHost.activeInHierarchy) { return false; } if (!seenSourceRootIds.Add(((Object)candidate.SourceRoot).GetInstanceID())) { return false; } onCandidate(candidate); return true; } private static bool TryEvaluateNamedGloomPropTouch(GameObject hitObject, out GameObject sourceRoot, out string sourceLabel) { sourceRoot = null; sourceLabel = null; if ((Object)(object)hitObject == (Object)null) { return false; } string vegetationHierarchyPath = GetVegetationHierarchyPath(hitObject.transform); if (vegetationHierarchyPath.IndexOf("/Swamp_Segment/", StringComparison.OrdinalIgnoreCase) < 0 && vegetationHierarchyPath.IndexOf("/Gloom/", StringComparison.OrdinalIgnoreCase) < 0) { return false; } for (int i = 0; i < GloomPropFolderNames.Length; i++) { string text = GloomPropFolderNames[i]; string value = "/Props/" + text; if (vegetationHierarchyPath.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { Transform val = FindNamedPropFolderAncestor(hitObject.transform, text); if (!((Object)(object)val == (Object)null) && IsAllowedAttachPath(val)) { sourceRoot = ((Component)val).gameObject; sourceLabel = GetGloomPropSourceLabel(text); return true; } } } return false; } private static Transform FindNamedPropFolderAncestor(Transform start, string folderName) { Transform val = start; while ((Object)(object)val != (Object)null && IsActiveVegetationHierarchyTransform(val)) { if (MatchesGloomPropFolderName(((Object)val).name, folderName)) { return val; } val = val.parent; } return null; } private static string GetGloomPropSourceLabel(string folderName) { if (string.IsNullOrEmpty(folderName)) { return "Group:vegetation"; } if (string.Equals(folderName, "VenusLeaves", StringComparison.OrdinalIgnoreCase)) { return "Keyword:venusleaves"; } if (string.Equals(folderName, "Vines", StringComparison.OrdinalIgnoreCase)) { return "Group:vine"; } return "Group:" + folderName.ToLowerInvariant(); } public static string FormatRegistrationRootsForLog(Transform[] roots) { if (roots == null || roots.Length == 0) { return "(none)"; } List<string> list = new List<string>(roots.Length); foreach (Transform val in roots) { if (!((Object)(object)val == (Object)null)) { list.Add(((Object)val).name + "@" + GetVegetationHierarchyPathForLog(val)); } } if (list.Count != 0) { return string.Join("; ", list); } return "(none)"; } public static string GetVegetationHierarchyPathForLog(Transform transform) { return GetVegetationHierarchyPath(transform); } private static GameObject ResolveFoliageTagHost(Renderer renderer, GameObject sourceRoot) { if ((Object)(object)renderer == (Object)null) { return sourceRoot; } GameObject gameObject = ((Component)renderer).gameObject; if ((Object)(object)sourceRoot == (Object)null) { return gameObject; } if (!IsKeywordSpreadGroup(((Object)sourceRoot).name, out var _)) { return gameObject; } Transform val = ((Component)renderer).transform; while ((Object)(object)val != (Object)null && (Object)(object)val.parent != (Object)null && (Object)(object)((Component)val.parent).gameObject != (Object)(object)sourceRoot) { val = val.parent; } if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)(object)sourceRoot) { return ((Component)val).gameObject; } return gameObject; } private static bool IsAllowedAttachPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return false; } if (TickBiomeRules.IsPathInAllowedTickBiome(GetVegetationHierarchyPath(transform))) { return true; } return IsUnderActiveRegistrationRoots(transform); } private static bool IsUnderActiveRegistrationRoots(Transform transform) { if ((Object)(object)transform == (Object)null || !IsCurrentBiomeAllowed()) { return false; } Transform[] activeRegistrationRoots = GetActiveRegistrationRoots(); foreach (Transform val in activeRegistrationRoots) { if (!((Object)(object)val == (Object)null) && ((Object)(object)transform == (Object)(object)val || transform.IsChildOf(val))) { return true; } } return false; } private static bool IsKeywordSpreadGroup(string objectName, out string matchedKeyword) { matchedKeyword = null; if (string.IsNullOrEmpty(objectName)) { return false; } if (objectName.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase) >= 0) { return false; } if (IsGenericVegetationMeshName(objectName)) { return false; } matchedKeyword = GetMatchedVegetationKeyword(objectName, SpreadGroupKeywords); return matchedKeyword != null; } public static bool TryEvaluateWhitelistedVegetation(Collider collider, out GameObject sourceRoot, out string sourceLabel) { sourceRoot = null; sourceLabel = null; if ((Object)(object)collider == (Object)null || !collider.enabled || (Object)(object)((Component)collider).gameObject == (Object)null) { return false; } Character val = default(Character); if (CharacterRagdoll.TryGetCharacterFromCollider(collider, ref val)) { return false; } return TryEvaluateWhitelistedGameObject(ResolveVegetationHitObject(((Component)collider).gameObject), out sourceRoot, out sourceLabel); } public static bool TryEvaluateWhitelistedGameObject(GameObject hitObject, out GameObject sourceRoot, out string sourceLabel) { sourceRoot = null; sourceLabel = null; if ((Object)(object)hitObject == (Object)null) { return false; } if ((Object)(object)hitObject.GetComponentInParent<Bugfix>() != (Object)null) { return false; } if (IsHarvestedFoodPickup(hitObject)) { return false; } if (TryEvaluateNamedGloomPropTouch(hitObject, out sourceRoot, out sourceLabel)) { return true; } if (TryResolveTickTriggerHost(hitObject, out var host)) { sourceRoot = host; sourceLabel = "TickTriggerHost"; return true; } if (TryEvaluateGrassTouch(hitObject, out var sourceRoot2, out var sourceLabel2)) { sourceRoot = sourceRoot2; sourceLabel = sourceLabel2; return true; } if (TryEvaluateCapybaraTouch(hitObject, out var sourceRoot3, out var sourceLabel3)) { sourceRoot = sourceRoot3; sourceLabel = sourceLabel3; return true; } BerryBush componentInParent = hitObject.GetComponentInParent<BerryBush>(); if ((Object)(object)componentInParent != (Object)null && QualifiesBerryBush(componentInParent)) { sourceRoot = ((Component)componentInParent).gameObject; sourceLabel = "BerryBush:" + ((object)Unsafe.As<SpawnPool, SpawnPool>(ref ((Spawner)componentInParent).spawnPool)/*cast due to .constrained prefix*/).ToString(); return true; } BerryVine componentInParent2 = hitObject.GetComponentInParent<BerryVine>(); if ((Object)(object)componentInParent2 != (Object)null && !IsExcludedVegetationText(GetCombinedVegetationText(((Component)componentInParent2).gameObject))) { sourceRoot = ((Component)componentInParent2).gameObject; sourceLabel = "Vine"; return true; } JungleVine componentInParent3 = hitObject.GetComponentInParent<JungleVine>(); if ((Object)(object)componentInParent3 != (Object)null) { string combinedVegetationText = GetCombinedVegetationText(((Component)componentInParent3).gameObject); if (!IsExcludedVegetationText(combinedVegetationText) && combinedVegetationText.Contains("vine")) { sourceRoot = ((Component)componentInParent3).gameObject; sourceLabel = "Vine"; return true; } } string combinedVegetationText2 = GetCombinedVegetationText(hitObject); if (!MatchesVegetationKeyword(combinedVegetationText2)) { return false; } Transform val = ResolveVegetationTriggerRoot(hitObject.transform, combinedVegetationText2); sourceRoot = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : hitObject); sourceLabel = GetVegetationKeywordSourceLabel(combinedVegetationText2); Transform val2 = FindOutermostKeywordNamedAncestor(hitObject.transform); if ((Object)(object)val2 != (Object)null) { string matchedVegetationKeyword = GetMatchedVegetationKeyword(((Object)val2).name, NamedVegetationTriggerKeywords); if (!string.IsNullOrEmpty(matchedVegetationKeyword)) { sourceRoot = ((Component)val2).gameObject; sourceLabel = "Group:" + matchedVegetationKeyword; } } if ((Object)(object)sourceRoot == (Object)null || !IsAllowedAttachPath(sourceRoot.transform)) { sourceRoot = null; sourceLabel = null; return false; } return true; } private static GameObject ResolveVegetationHitObject(GameObject hitObject) { if ((Object)(object)hitObject == (Object)null) { return null; } if (((Object)hitObject).name == "PEAKTicks_VegetationScanner" || PeakTickVegetationLegacyNames.IsLegacyModObject(((Object)hitObject).name)) { return null; } if (((Object)hitObject).name == "PEAKTicks_VegetationTrigger") { Transform parent = hitObject.transform.parent; if (!((Object)(object)parent != (Object)null)) { return null; } return ((Component)parent).gameObject; } TickTrigger val = hitObject.GetComponent<TickTrigger>(); if ((Object)(object)val == (Object)null) { val = hitObject.GetComponentInParent<TickTrigger>(); } if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).transform.parent != (Object)null) { return ((Component)((Component)val).transform.parent).gameObject; } return hitObject; } private static bool TryResolveTickTriggerHost(GameObject hitObject, out GameObject host) { host = null; if ((Object)(object)hitObject == (Object)null) { return false; } TickTrigger val = hitObject.GetComponent<TickTrigger>(); if ((Object)(object)val == (Object)null) { val = hitObject.GetComponentInParent<TickTrigger>(); } if ((Object)(object)val == (Object)null) { return false; } host = ResolveTickTriggerHost(val); if ((Object)(object)host == (Object)null || !IsAllowedAttachPath(host.transform)) { host = null; return false; } return true; } internal static GameObject ResolveTickTriggerHost(TickTrigger trigger) { if ((Object)(object)trigger == (Object)null) { return null; } if ((Object)(object)((Component)trigger).transform.parent != (Object)null) { return ((Component)((Component)trigger).transform.parent).gameObject; } return ((Component)trigger).gameObject; } public static bool TryApplyVegetationBurstAttach(Character character, string sourceLabel, GameObject sourceObject = null) { if (!Enabled || (Object)(object)character == (Object)null || !character.IsLocal || (Object)(object)character.data == (Object)null || character.data.dead) { return false; } if (!TickModAuthorizationService.IsLocalVegetationActive) { return false; } if (!IsCurrentBiomeAllowed() || !CanStartVegetationBurst(character)) { LogBurstSkipped(sourceLabel, (!IsCurrentBiomeAllowed()) ? "biome blocked" : "touch cooldown"); return false; } if (!TickAttachService.CanAttachAnotherTick(character) || !RollAttachChance()) { LogBurstSkipped(sourceLabel, (!TickAttachService.CanAttachAnotherTick(character)) ? "max ticks" : "chance roll"); return false; } int vegetationBurstId = ++_vegetationBurstCounter; int num = TickAttachService.CountTicksOnCharacter(character); bool isTickBomb; int num2 = RollBurstTickCount(out isTickBomb); string attachSource = (isTickBomb ? (sourceLabel + " | tick bomb") : sourceLabel); string attachDetail = TickAttachService.FormatSourceObjectDetail(sourceObject); int num3 = 0; for (int i = 0; i < num2; i++) { if (!TickAttachService.CanAttachAnotherTick(character)) { break; } if (!TickAttachService.TrySpawnAttachedTick(character, attachSource, attachDetail, out var _, vegetationBurstId)) { LogBurstSkipped(sourceLabel, "spawn failed"); break; } num3++; } if (num3 > 0) { MarkVegetationBurst(character); } else if (TickAttachService.CountTicksOnCharacter(character) > num) { MarkVegetationBurst(character); } if (num3 <= 0) { return TickAttachService.CountTicksOnCharacter(character) > num; } return true; } private static void LogBurstSkipped(string sourceLabel, string reason) { if (!string.IsNullOrEmpty(reason) && (!(_lastBurstSkipReason == reason) || !(Time.time < _nextBurstSkipLogTime))) { _lastBurstSkipReason = reason; _nextBurstSkipLogTime = Time.time + 8f; ManualLogSource log = PEAKTicksPlugin.Log; if (log != null) { log.LogInfo((object)("[PEAK Ticks] Vegetation attach skipped (" + reason + ") from " + (sourceLabel ?? "unknown") + ".")); } } } public static void NotifyVegetationAttachSucceeded(Character character, int burstId) { if (!((Object)(object)character == (Object)null) && burstId > 0 && burstId != _lastCooldownAppliedBurstId) { _lastCooldownAppliedBurstId = burstId; MarkVegetationBurst(character); } } private static bool CanStartVegetationBurst(Character character) { if (TouchCooldownSeconds <= 0f) { return true; } if (((Object)character).GetInstanceID() != _burstCharacterId) { return true; } return Time.time >= _nextBurstAllowedTime; } public static float GetVegetationBurstCooldownRemaining(Character character) { if (TouchCooldownSeconds <= 0f || (Object)(object)character == (Object)null) { return 0f; } if (((Object)character).GetInstanceID() != _burstCharacterId) { return 0f; } return Mathf.Max(0f, _nextBurstAllowedTime - Time.time); } private static void MarkVegetationBurst(Character character) { _burstCharacterId = ((Object)character).GetInstanceID(); _nextBurstAllowedTime = Time.time + TouchCooldownSeconds; } private static string GetCurrentSegmentName() { //IL_001a: 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) try { if ((Object)(object)Singleton<MapHandler>.Instance == (Object)null) { return "Unknown"; } return ((object)Singleton<MapHandler>.Instance.GetCurrentSegment()/*cast due to .constrained prefix*/).ToString(); } catch { return "Unknown"; } } private static string GetCurrentBiomeName() { //IL_001a: 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) try { if ((Object)(object)Singleton<MapHandler>.Instance == (Object)null) { return "Unknown"; } return ((object)Singleton<MapHandler>.Instance.GetCurrentBiome()/*cast due to .constrained prefix*/).ToString(); } catch { return "Unknown"; } } private static bool QualifiesKeywordNamedRoot(Transform transform) { if ((Object)(object)transform == (Object)null) { return false; } if ((Object)(object)((Component)transform).GetComponent<BerryBush>() != (Object)null) { return false; } string name = ((Object)transform).name; if (name.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase) >= 0) { return false; } if (IsGenericVegetationMeshName(name)) { return false; } return GetMatchedVegetationKeyword(name, NamedVegetationTriggerKeywords) != null; } private static bool IsGenericVegetationMeshName(string objectName) { if (string.IsNullOrEmpty(objectName)) { return true; } string text = objectName.ToLowerInvariant(); if (text == "mesh" || text.StartsWith("mesh ", StringComparison.Ordinal) || text.StartsWith("mesh(", StringComparison.Ordinal) || text.Contains("autolod") || text.Contains("_lod_") || text.Contains("lod_") || text.StartsWith("collider", StringComparison.Ordinal) || text == "peakticks_vegetationtrigger") { return true; } return false; } private static Transform FindOutermostKeywordNamedAncestor(Transform start) { if ((Object)(object)start == (Object)null) { return null; } Transform result = null; Transform val = start; while ((Object)(object)val != (Object)null && IsActiveVegetationHierarchyTransform(val)) { if (QualifiesKeywordNamedRoot(val)) { result = val; } val = val.parent; } return result; } private static Transform FindOutermostPathKeywordAncestor(Transform start, string combinedText) { if ((Object)(object)start == (Object)null) { return null; } string matchedVegetationKeyword = GetMatchedVegetationKeyword(combinedText, VegetationAttachKeywords); if (matchedVegetationKeyword == null) { return null; } Transform result = null; Transform val = start; while ((Object)(object)val != (Object)null && IsActiveVegetationHierarchyTransform(val)) { if ((Object)(object)((Component)val).GetComponent<BerryBush>() != (Object)null) { val = val.parent; continue; } if (GetMatchedVegetationKeyword(GetVegetationHierarchyPath(val), VegetationAttachKeywords) == matchedVegetationKeyword) { result = val; } val = val.parent; } return result; } private static Transform ResolveVegetationTriggerRoot(Transform colliderTransform, string combinedText) { Transform val = FindOutermostKeywordNamedAncestor(colliderTransform); if ((Object)(object)val != (Object)null) { return val; } return FindOutermostPathKeywordAncestor(colliderTransform, combinedText); } private static bool IsActiveVegetationHierarchyTransform(Transform transform) { if ((Object)(object)transform != (Object)null) { return ((Component)transform).gameObject.activeInHierarchy; } return false; } private static string GetMatchedVegetationKeyword(string text, string[] keywords) { if (string.IsNullOrEmpty(text)) { return null; } string text2 = text.ToLowerInvariant(); for (int i = 0; i < keywords.Length; i++) { if (text2.Contains(keywords[i])) { return keywords[i]; } } return null; } private static string GetCombinedVegetationText(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return string.Empty; } return (((Object)gameObject).name + " " + GetVegetationHierarchyPath(gameObject.transform)).ToLowerInvariant(); } private static bool IsExcludedVegetationText(string combined) { if (string.IsNullOrEmpty(combined)) { return true; } if (IsExcludedAttachPath(combined)) { return true; } if (!combined.Contains("luggage") && !combined.Contains("spawner") && !combined.Contains("/trees/") && !combined.Contains("treecrown") && !combined.Contains("palmtree") && !combined.Contains("palmmesh") && !combined.Contains("mushroom tree") && !combined.Contains("mushroom cluster") && !combined.Contains("jungle_thorn") && !combined.Contains("junglethorn") && !combined.Contains("peakticks_vegetationtrigger")) { return combined.Contains("ticktrigger"); } return true; } private static bool MatchesVegetationKeyword(string combined) { if (IsExcludedVegetationText(combined)) { return false; } return GetMatchedVegetationKeyword(combined, VegetationAttachKeywords) != null; } private static string GetVegetationKeywordSourceLabel(string combined) { string matchedVegetationKeyword = GetMatchedVegetationKeyword(combined, VegetationAttachKeywords); if (string.IsNullOrEmpty(matchedVegetationKeyword)) { return "Keyword:vegetation"; } return "Keyword:" + matchedVegetationKeyword; } private static bool TryEvaluateCapybaraTouch(GameObject hitObject, out GameObject sourceRoot, out string sourceLabel) { sourceRoot = null; sourceLabel = null; if ((Object)(object)hitObject == (Object)null) { return false; } string vegetationHierarchyPath = GetVegetationHierarchyPath(hitObject.transform); if (vegetationHierarchyPath.IndexOf("/Biome_3/", StringComparison.OrdinalIgnoreCase) < 0) { return false; } if (vegetationHierarchyPath.IndexOf("/Alpine/", StringComparison.OrdinalIgnoreCase) < 0 && vegetationHierarchyPath.IndexOf("/Mesa/", StringComparison.OrdinalIgnoreCase) < 0) { return false; } Transform val = null; Transform val2 = hitObject.transform; while ((Object)(object)val2 != (Object)null && IsActiveVegetationHierarchyTransform(val2)) { if (((Object)val2).name.IndexOf("capybara", StringComparison.OrdinalIgnoreCase) >= 0) { val = val2; } val2 = val2.parent; } if ((Object)(object)val == (Object)null) { return false; } sourceRoot = ((Component)val).gameObject; sourceLabel = "Capybara"; return true; } private static bool TryEvaluateGrassTouch(GameObject hitObject, out GameObject sourceRoot, out string sourceLabel) { sourceRoot = null; sourceLabel = null; if ((Object)(object)hitObject == (Object)null) { return false; } Transform val = FindAncestorSpreadGroup(hitObject.transform); if ((Object)(object)val != (Object)null && ((Object)val).name.IndexOf("beachgrass", StringComparison.OrdinalIgnoreCase) >= 0) { sourceRoot = ((Component)val).gameObject; sourceLabel = "Grass:beachgrass"; return true; } if (TryEvaluateShoreSegmentGrassProp(hitObject, out sourceRoot, out sourceLabel)) { return true; } string combinedVegetationText = GetCombinedVegetationText(hitObject); if (GetMatchedVegetationKeyword(combinedVegetationText, new string[1] { "beachgrass" }) == null || IsExcludedVegetationText(combinedVegetationText)) { return false; } Transform val2 = ResolveVegetationTriggerRoot(hitObject.transform, combinedVegetationText); sourceRoot = (((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : hitObject); sourceLabel = "Grass:beachgrass"; return true; } private static bool TryEvaluateShoreSegmentGrassProp(GameObject hitObject, out GameObject sourceRoot, out string sourceLabel) { sourceRoot = null; sourceLabel = null; if ((Object)(object)hitObject == (Object)null) { return false; } string vegetationHierarchyPath = GetVegetationHierarchyPath(hitObject.transform); if (vegetationHierarchyPath.IndexOf("/Beach_Segment/", StringComparison.OrdinalIgnoreCase) < 0) { return false; } if (vegetationHierarchyPath.IndexOf("/WallProps/", StringComparison.OrdinalIgnoreCase) < 0 && vegetationHierarchyPath.IndexOf("/PlateauProps/", StringComparison.OrdinalIgnoreCase) < 0) { return false; } string combinedVegetationText = GetCombinedVegetationText(hitObject); if (GetMatchedVegetationKeyword(combinedVegetationText, new string[2] { "grass", "weed" }) == null || IsExcludedVegetationText(combinedVegetationText)) { return false; } if (combinedVegetationText.Contains("palmtree") || combinedVegetationText.Contains("palmmesh") || combinedVegetationText.Contains("berrybush")) { return false; } Transform val = ResolveVegetationTriggerRoot(hitObject.transform, combinedVegetationText); sourceRoot = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : hitObject); sourceLabel = "Grass:shore"; return true; } private static Transform FindAncestorSpreadGroup(Transform start) { Transform val = start; while ((Object)(object)val != (Object)null && IsActiveVegetationHierarchyTransform(val)) { if (IsKeywordSpreadGroup(((Object)val).name, out var _)) { return val; } val = val.parent; } return null; } private static string GetSpreadGroupSourceLabel(string matchedKeyword) { if (string.IsNullOrEmpty(matchedKeyword)) { return "Group:vegetation"; } return matchedKeyword switch { "beachgrass" => "Grass:beachgrass", "fern" => "Group:fern", "driftwood" => "Group:driftwood", "vine" => "Group:vine", "bushes" => "Group:bushes", _ => "Group:" + matchedKeyword, }; } private static bool IsSpreadVegetationContainer(Transform transform) { if ((Object)(object)transform == (Object)null || transform.childCount == 0) { return false; } return ((Object)transform).name.ToLowerInvariant().Contains("beachgrass"); } private static bool IsGrassSystemChunk(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return false; } if ((Object)(object)gameObject.GetComponent<GrassChunk>() != (Object)null) { return true; } return GetVegetationHierarchyPath(gameObject.transform).IndexOf("/GrassSystem/", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool HostNeedsFoliageAssist(GameObject host) { if ((Object)(object)host == (Object)null || !host.activeInHierarchy) { return false; } if (IsGrassSystemChunk(host)) { return false; } Collider[] componentsInChildren = host.GetComponentsInChildren<Collider>(true); foreach (Collider val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && val.enabled && !(((Object)((Component)val).gameObject).name == "PEAKTicks_VegetationTrigger") && !((Object)(object)((Component)val).GetComponentInParent<PeakTickVegetationScanner>() != (Object)null) && !val.isTrigger) { return false; } } return true; } private static bool BoundsQualifiesForTag(Bounds bounds) { //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) Vector3 size = ((Bounds)(ref bounds)).size; return ((Vector3)(ref size)).sqrMagnitude > 0.0001f; } private static Bounds ClampTagBounds(Bounds bounds, float maxExtent) { //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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_0031: 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_0045: Unknown result type (might be due to invalid IL or missing references) Vector3 extents = ((Bounds)(ref bounds)).extents; float num = Mathf.Max(extents.x, Mathf.Max(extents.y, extents.z)); if (num <= maxExtent || num <= 0.0001f) { return bounds; } float num2 = maxExtent / num; ((Bounds)(ref bounds)).extents = extents * num2; return bounds; } private static bool TryComputeTagBounds(GameObject tagHost, float maxExtent, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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) //IL_0068: 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_0042: 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_0037: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); if ((Object)(object)tagHost == (Object)null) { return false; } Renderer[] componentsInChildren = tagHost.GetComponentsInChildren<Renderer>(true); bool flag = false; foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { if (!flag) { bounds = val.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } if (!flag) { return false; } bounds = ClampTagBounds(bounds, maxExtent); return BoundsQualifiesForTag(bounds); } private static bool QualifiesBerryBush(BerryBush bush) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bush == (Object)null) { return false; } string vegetationHierarchyPath = GetVegetationHierarchyPath(((Component)bush).transform); if (IsExcludedAttachPath(vegetationHierarchyPath) || (int)((Spawner)bush).spawnPool == 33554432) { return false; } if (IsAllowedAttachSpawnPool(((Spawner)bush).spawnPool)) { return true; } string name = ((Object)((Component)bush).gameObject).name; if (vegetationHierarchyPath.IndexOf("bushes", StringComparison.OrdinalIgnoreCase) < 0 && GetMatchedVegetationKeyword(name, new string[1] { "berrybush" }) == null) { return GetMatchedVegetationKeyword(vegetationHierarchyPath, new string[1] { "berrybush" }) != null; } return true; } private static bool IsHarvestedFoodPickup(GameObject gameObject) { string vegetationHierarchyPath = GetVegetationHierarchyPath(gameObject.transform); if (vegetationHierarchyPath.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase) < 0) { return false; } string text = vegetationHierarchyPath.ToLowerInvariant(); if (text.Contains("apple berry") || text.Contains("pepper berry") || text.Contains("bananna berry") || text.Contains("berrynana") || text.Contains("cluster berry") || text.Contains("clusterberry") || text.Contains("pine berry") || text.Contains("kingberry") || text.Contains("raspberry") || text.Contains("shroomberry")) { return true; } if (text.Contains("mushroom chubby") || text.Contains("mushroom normie")) { return true; } if (text.Contains("mushroom cluster")) { return !text.Contains("mushroom tree"); } return false; } private static bool IsExcludedAttachPath(string path) { if (string.IsNullOrEmpty(path)) { return true; } if (path.IndexOf("/Mallows/", StringComparison.OrdinalIgnoreCase) < 0 && path.IndexOf("CampfireFood", StringComparison.OrdinalIgnoreCase) < 0) { return path.IndexOf("/crashed plane/", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static bool IsAllowedAttachSpawnPool(SpawnPool pool) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between I4 and Unknown for (int i = 0; i < AllowedAttachSpawnPools.Length; i++) { if ((int)AllowedAttachSpawnPools[i] == (int)pool) { return true; } } return false; } private static void ClearModProbesInSegment() { Transform[] activeRegistrationRoots = GetActiveRegistrationRoots(); foreach (Transform val in activeRegistrationRoots) { if ((Object)(object)val == (Object)null) { continue; } Transform[] componentsInChildren = ((Component)val).GetComponentsInChildren<Transform>(true); foreach (Transform val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null && ((Object)val2).name == "PEAKTicks_VegetationTrigger") { Object.Destroy((Object)(object)((Component)val2).gameObject); } } } } private static string GetVegetationHierarchyPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return string.Empty; } List<string> list = new List<string>(); Transform val = transform; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list); } } internal static class TickVegetationProbeService { private sealed class SegmentVegetationCache { public List<VegetationTouchTarget> TouchTargets = new List<VegetationTouchTarget>(); public int ProbeCount; public int SpreadBoundsTargets; } private struct VegetationTouchTarget { public GameObject SourceRoot; public string SourceLabel; public Bounds WorldBounds; } public const string ProbeObjectName = "PEAKTicks_VegetationTrigger"; private const int DiscoveryWorkBudgetPerFrame = 120; private const int ProbesInstalledPerFrame = 25; private const float MinProbeRadius = 0.12f; private const float MaxProbeRadius = 1.25f; private const float MaxBoundsAttachGap = 0.35f; private const float MaxSpreadProbeBoundsExtent = 1.25f; private static int _segmentKey = int.MinValue; private static int _probeCount; private static int _touchTargetCount; private static bool _setupCompleted; private static int _spreadBoundsTargets; private static readonly List<VegetationTouchTarget> TouchTargets = new List<VegetationTouchTarget>(4096); private static readonly Dictionary<int, SegmentVegetationCache> SegmentCaches = new Dictionary<int, SegmentVegetationCache>(); public static int ProbeCount => _probeCount; public static int TouchTargetCount => _touchTargetCount; public static int SpreadBoundsTargetCount => _spreadBoundsTargets; public static bool IsReadyForCurrentSegment() { if (_segmentKey == GetVegetationCacheKey()) { return _setupCompleted; } return false; } public static void Clear() { _segmentKey = int.MinValue; _probeCount = 0; _touchTargetCount = 0; _spreadBoundsTargets = 0; _setupCompleted = false; TouchTargets.Clear(); SegmentCaches.Clear(); } public static void PrepareForSegmentChange(int segmentKey) { _segmentKey = segmentKey; _probeCount = 0; _touchTargetCount = 0; _spreadBoundsTargets = 0; _setupCompleted = false; TouchTargets.Clear(); } private static bool TryRestoreSegmentCache(int segmentKey) { if (!SegmentCaches.TryGetValue(segmentKey, out var value) || value.TouchTargets.Count == 0) { return false; } TouchTargets.Clear(); int num = 0; for (int i = 0; i < value.TouchTargets.Count; i++) { VegetationTouchTarget item = value.TouchTargets[i]; if (!((Object)(object)item.SourceRoot == (Object)null) && item.SourceRoot.activeInHierarchy) { TouchTargets.Add(item); num++; } } if (num == 0) { TouchTargets.Clear(); SegmentCaches.Remove(segmentKey); return false; } _probeCount = value.ProbeCount; _touchTargetCount = TouchTargets.Count; _spreadBoundsTargets = value.SpreadBoundsTargets; _segmentKey = segmentKey; _setupCompleted = true; return true; } private static void SaveSegmentCache(int segmentKey) { SegmentVegetationCache segmentVegetationCache = new SegmentVegetationCache(); segmentVegetationCache.TouchTargets.AddRange(TouchTargets); segmentVegetationCache.ProbeCount = _probeCount; segmentVegetationCache.SpreadBoundsTargets = _spreadBoundsTargets; SegmentCaches[segmentKey] = segmentVegetationCache; } public static IEnumerator SetupProbesForCurrentSegmentAsync(bool incrementalOnly = false) { int segmentKey = GetVegetationCacheKey(); bool flag = IsSwampVegetationRegionKey(segmentKey); if (incrementalOnly && (!_setupCompleted || _segmentKey != segmentKey) && (!flag || _segmentKey != segmentKey)) { incrementalOnly = false; } if (!TickVegetationAttachService.Enabled || !PhotonNetwork.InRoom) { yield break; } if (!TickVegetationAttachService.IsCurrentBiomeAllowed()) { TickVegetationAttachService.LogMapHandlerStateForDiagnostics("probe registration skipped", force: true); yield break; } if (!incrementalOnly && TryRestoreSegmentCache(segmentKey)) { ManualLogSource log = PEAKTicksPlugin.Log; if (log != null) { log.LogInfo((object)("[PEAK Ticks] Vegetation touch targets restored from cache: " + _touchTargetCount + " targets, " + _probeCount + " probes.")); } yield break; } Transform[] roots = TickVegetationAttachService.GetActiveRegistrationRoots(); if (roots.Length == 0) { ManualLogSource log2 = PEAKTicksPlugin.Log; if (log2 != null) { log2.LogWarning((object)"[PEAK Ticks] Vegetation probe setup deferred (segment roots not ready)."); } yield break; } if (!incrementalOnly) { _probeCount = 0; _touchTargetCount = 0; _spreadBoundsTargets = 0; _setupCompleted = false; TouchTargets.Clear(); } HashSet<int> hashSet = new HashSet<int>(); if (incrementalOnly) { for (int i = 0; i < TouchTargets.Count; i++) { GameObject sourceRoot = TouchTargets[i].SourceRoot; if ((Object)(object)sourceRoot != (Object)null) { hashSet.Add(((Object)sourceRoot).GetInstanceID()); } } } int installed = (incrementalOnly ? _probeCount : 0); int registered = (incrementalOnly ? _touchTargetCount : 0); int spreadBoundsOnly = (incrementalOnly ? _spreadBoundsTargets : 0); int installedThisPass = 0; int registeredThisPass = 0; int spreadBoundsThisPass = 0; int discoveryWorkThisFrame = 0; IEnumerator discovery = ((!TickVegetationAttachService.IsSwampBiomeSegment()) ? TickVegetationAttachService.EnumerateVegetationDiscoveryAsync(roots, delegate(TickVegetationAttachService.VegetationDetectionCandidate candidate) { if (TryRegisterLogicalPlant(candidate)) { registeredThisPass++; registered++; if (candidate.BoundsOnly) { spreadBoundsThisPass++; spreadBoundsOnly++; } } if (!candidate.BoundsOnly && TryInstallProbe(candidate.DetectionHost, candidate.MaxBoundsExtent)) { installedThisPass++; installed++; } }, hashSet, delegate { discoveryWorkThisFrame++; if (discoveryWorkThisFrame >= 120) { discoveryWorkThisFrame = 0; return true; } return (installedThisPass + registeredThisPass) % 25 == 0; }, incrementalOnly) : TickVegetationAttachService.EnumerateSwampGloomPropDiscoveryAsync(roots, delegate(TickVegetationAttachService.VegetationDetectionCandidate candidate) { if (TryRegisterLogicalPlant(candidate)) { registeredThisPass++; registered++; if (candidate.BoundsOnly) { spreadBoundsThisPass++; spreadBoundsOnly++; } } }, hashSet, delegate { discoveryWorkThisFrame++; if (discoveryWorkThisFrame >= 120) { discoveryWorkThisFrame = 0; return true; } return registeredThisPass % 25 == 0; })); while (discovery.MoveNext()) { yield return discovery.Current; } _probeCount = installed; _touchTargetCount = registered; _spreadBoundsTargets = spreadBoundsOnly; _segmentKey = segmentKey; _setupCompleted = registered > 0 || installed > 0; if (_setupCompleted) { SaveSegmentCache(segmentKey); } else if (!incrementalOnly) { SegmentCaches.Remove(segmentKey); } if (registeredThisPass == 0 && installedThisPass == 0) { string text = (incrementalOnly ? "incremental" : "full"); ManualLogSource log3 = PEAKTicksPlugin.Log; if (log3 != null) { log3.LogWarning((object)("[PEAK Ticks] Vegetation touch setup empty (" + text + "; biome=" + TickVegetationAttachService.GetCurrentBiomeNameForLog() + "; roots=" + TickVegetationAttachService.FormatRegistrationRootsForLog(roots) + ").")); } } else if (registeredThisPass > 0 || installedThisPass > 0) { string text2 = (incrementalOnly ? "incremental" : "full"); ManualLogSource log4 = PEAKTicksPlugin.Log; if (log4 != null) { log4.LogInfo((object)("[PEAK Ticks] Vegetation touch targets: " + registered + ", probes installed: " + installed + " (" + text2 + "; biome=" + TickVegetationAttachService.GetCurrentBiomeNameForLog() + "; regionKey=" + segmentKey + "; roots=" + TickVegetationAttachService.FormatRegistrationRootsForLog(roots) + "; new targets=" + registeredThisPass + ", new probes=" + installedThisPass + ", spread bounds-only=" + spreadBoundsOnly + ").")); } } } private static bool TryRegisterLogicalPlant(TickVegetationAttachService.VegetationDetectionCandidate candidate) { //IL_00b9: 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) if ((Object)(object)candidate.SourceRoot == (Object)null || !candidate.SourceRoot.activeInHierarchy || (Object)(object)candidate.DetectionHost == (Object)null || !candidate.DetectionHost.activeInHierarchy) { return false; } if ((Object)(object)candidate.SourceRoot.GetComponentInParent<PeakTickVegetationScanner>() != (Object)null) { return false; } if (TickVegetationAttachService.IsBerryBushTouchTarget(candidate.SourceRoot, candidate.SourceLabel)) { return false; } if (!candidate.BoundsOnly && !TickVegetationAttachService.HostNeedsFoliageProbe(candidate.DetectionHost)) { return false; } if (!TickVegetationAttachService.TryComputeFoliageBounds(candidate.DetectionHost, candidate.MaxBoundsExtent, out var bounds)) { return false; } TouchTargets.Add(new VegetationTouchTarget { SourceRoot = candidate.SourceRoot, SourceLabel = candidate.SourceLabel, WorldBounds = bounds }); return true; } public static bool TryFindTouchTargetInDetectorBox(Transform boxTransform, Vector3 localCenter, Vector3 localHalfExtents, out GameObject sourceRoot, out string sourceLabel) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //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) //IL_009b: 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_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) sourceRoot = null; sourceLabel = null; if ((Object)(object)boxTransform == (Object)null || TouchTargets.Count == 0) { return false; } Vector3 val = boxTransform.TransformPoint(localCenter); float num = ((Vector3)(ref localHalfExtents)).magnitude + 0.35f; float num2 = float.MaxValue; VegetationTouchTarget vegetationTouchTarget = default(VegetationTouchTarget); bool flag = false; for (int i = 0; i < TouchTargets.Count; i++) { VegetationTouchTarget vegetationTouchTarget2 = TouchTargets[i]; if ((Object)(object)vegetationTouchTarget2.SourceRoot == (Object)null || !vegetationTouchTarget2.SourceRoot.activeInHierarchy || TickVegetationAttachService.IsBerryBushTouchTarget(vegetationTouchTarget2.SourceRoot, vegetationTouchTarget2.SourceLabel)) { continue; } Vector3 val2 = ((Bounds)(ref vegetationTouchTarget2.WorldBounds)).extents; float num3 = num + ((Vector3)(ref val2)).magnitude; val2 = ((Bounds)(ref vegetationTouchTarget2.WorldBounds)).center - val; if (!(((Vector3)(ref val2)).sqrMagnitude > num3 * num3) && BoundsIntersectDetectorBox(vegetationTouchTarget2.WorldBounds, boxTransform, localCenter, localHalfExtents)) { float num4 = Vector3.Distance(val, ((Bounds)(ref vegetationTouchTarget2.WorldBounds)).ClosestPoint(val)); if (!(num4 >= num2)) { num2 = num4; vegetationTouchTarget = vegetationTouchTarget2; flag = true; } } } if (!flag) { return false; } sourceRoot = vegetationTouchTarget.SourceRoot; sourceLabel = vegetationTouchTarget.SourceLabel; return true; } private static bool BoundsIntersectDetectorBox(Bounds worldBounds, Transform boxTransform, Vector3 localCenter, Vector3 localHalfExtents) { //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_0062: 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) //IL_0068: 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_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) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_0038: 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) Vector3 center = ((Bounds)(ref worldBounds)).center; Vector3 extents = ((Bounds)(ref worldBounds)).extents; for (int i = -1; i <= 1; i += 2) { for (int j = -1; j <= 1; j += 2) { for (int k = -1; k <= 1; k += 2) { if (IsWorldPointInsideDetectorBox(center + Vector3.Scale(extents, new Vector3((float)i, (float)j, (float)k)), boxTransform, localCenter, localHalfExtents)) { return true; } } } } Vector3 val = boxTransform.TransformPoint(localCenter); return ((Bounds)(ref worldBounds)).Contains(val); } private static bool IsWorldPointInsideDetectorBox(Vector3 worldPoint, Transform boxTransform, Vector3 localCenter, Vector3 localHalfExtents) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_0034: 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) Vector3 val = boxTransform.InverseTransformPoint(worldPoint) - localCenter; if (Mathf.Abs(val.x) <= localHalfExtents.x && Mathf.Abs(val.y) <= localHalfExtents.y) { return Mathf.Abs(val.z) <= localHalfExtents.z; } return false; } private static bool TryInstallProbe(GameObject host, float maxBoundsExtent) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: U