using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using PEAK_Ticks; using Photon.Pun; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Toss a Tick")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Toss a Tick")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("361e37c6-51ce-4513-a083-416b8a7fc050")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace Toss_a_Tick; internal static class PeakticksIntegration { public static bool ShouldUsePeakticksRules() { if (!Chainloader.PluginInfos.TryGetValue("tony4twentys.PEAK_Ticks", out var value) || (Object)(object)value.Instance == (Object)null) { return false; } return PeakticksInterop.IsGameplayEnabled; } public static bool CanAttachAnotherTick(Character character) { if (ShouldUsePeakticksRules()) { return PeakticksInterop.CanAttachAnotherTick(character); } return !TickAttachHelper.CharacterHasAttachedTick(character); } public static bool TryAttachTick(Character target, out int tickNumber) { tickNumber = 0; if (ShouldUsePeakticksRules()) { return PeakticksInterop.TryAttachTick(target, "Toss a Tick", ref tickNumber); } return TickAttachHelper.SpawnAttachedTickVanilla(target); } } [BepInPlugin("tony4twentys.Toss_a_Tick", "Toss a Tick", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class TossATickPlugin : BaseUnityPlugin { public const string PluginGuid = "tony4twentys.Toss_a_Tick"; public const string PluginName = "Toss a Tick"; public const string PluginVersion = "1.0.0"; private Harmony _harmony; private ConfigEntry _logThrowDebug; private ConfigEntry _throwSearchRadius; public static ManualLogSource Log { get; private set; } public static bool LogThrowDebug { get; private set; } public static float ThrowSearchRadius { get; private set; } = 2.5f; private void Awake() { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _logThrowDebug = ((BaseUnityPlugin)this).Config.Bind("Debug", "LogThrowDebug", false, "When true, logs throw hit detection and attach attempts."); _throwSearchRadius = ((BaseUnityPlugin)this).Config.Bind("Throw", "ThrowSearchRadius", 2.5f, "If a player is hit but the tick collider is unclear, search this radius (meters) for a recently thrown tick to consume."); LogThrowDebug = _logThrowDebug.Value; ThrowSearchRadius = Mathf.Max(0.5f, _throwSearchRadius.Value); _logThrowDebug.SettingChanged += delegate { LogThrowDebug = _logThrowDebug.Value; }; _throwSearchRadius.SettingChanged += delegate { ThrowSearchRadius = Mathf.Max(0.5f, _throwSearchRadius.Value); }; _harmony = new Harmony("tony4twentys.Toss_a_Tick"); _harmony.PatchAll(); bool flag = Chainloader.PluginInfos.ContainsKey("tony4twentys.PEAK_Ticks"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Toss a Tick v1.0.0 loaded. Pick off a tick, throw it at a player to attach (host required). PEAK Ticks: " + (flag ? "detected (uses host rules when authorized)" : "not installed") + ". Throw debug logging: " + (LogThrowDebug ? "on" : "off") + ". " + $"Throw search radius: {ThrowSearchRadius:0.#}m.")); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Log = null; } } internal static class TickAttachHelper { public const string AttachedTickPrefab = "BugfixOnYou"; public const float DefaultThrownWindowSeconds = 8f; public const float DefaultSearchRadius = 2.5f; private static readonly Dictionary LastHitProcessTimeByCharacterId = new Dictionary(); public static bool IsThrowableTickItem(Item item) { if ((Object)(object)item == (Object)null) { return false; } string name = ((Object)((Component)item).gameObject).name; if (name.IndexOf("OnYou", StringComparison.OrdinalIgnoreCase) >= 0) { return false; } if ((Object)(object)((Component)item).GetComponent() != (Object)null) { return true; } return name.IndexOf("Bugfix", StringComparison.OrdinalIgnoreCase) >= 0; } public static bool CharacterHasAttachedTick(Character character) { if ((Object)(object)character == (Object)null) { return false; } foreach (KeyValuePair allAttachedBug in Bugfix.AllAttachedBugs) { if ((Object)(object)allAttachedBug.Value == (Object)(object)character) { return true; } } return false; } public static Item ResolveItemFromCollision(Collision collision) { if (collision == null) { return null; } Item result = default(Item); if (Item.TryGetItemFromCollider(collision.collider, ref result)) { return result; } if ((Object)(object)collision.rigidbody != (Object)null) { result = ((Component)collision.rigidbody).GetComponent(); if ((Object)(object)result != (Object)null) { return result; } } return collision.gameObject.GetComponentInParent(); } public static bool IsRecentlyThrownTick(Item item, float thrownWindowSeconds = 8f) { if ((Object)(object)item == (Object)null) { return false; } TickThrowHandler component = ((Component)item).GetComponent(); if ((Object)(object)component != (Object)null && component.IsWithinThrownWindow()) { return true; } return Time.time - GetLastThrownTime(item) <= thrownWindowSeconds; } private static float GetLastThrownTime(Item item) { if ((Object)(object)item == (Object)null) { return 0f; } return Traverse.Create((object)item).Field("lastThrownTime").GetValue(); } public static Item FindNearbyThrownTick(Character target, float searchRadius, float thrownWindowSeconds) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return null; } Vector3 val = (((Object)(object)target.refs?.hip != (Object)null) ? ((Component)target.refs.hip).transform.position : ((Component)target).transform.position); Item result = null; float num = searchRadius; for (int i = 0; i < Item.ALL_ACTIVE_ITEMS.Count; i++) { Item val2 = Item.ALL_ACTIVE_ITEMS[i]; if (IsThrownTickCandidate(val2, thrownWindowSeconds)) { float num2 = Vector3.Distance(((Component)val2).transform.position, val); if (!(num2 > num)) { num = num2; result = val2; } } } return result; } public static bool TryProcessTickHit(Character target, Collision collision, Item preferredTickItem, float searchRadius, float thrownWindowSeconds, float minHitVelocity) { //IL_00d2: 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) if (!PhotonNetwork.IsMasterClient) { return false; } if ((Object)(object)target == (Object)null || (Object)(object)target.data == (Object)null || target.data.dead) { return false; } if (!PeakticksIntegration.CanAttachAnotherTick(target)) { if (TossATickPlugin.LogThrowDebug) { ManualLogSource log = TossATickPlugin.Log; if (log != null) { log.LogInfo((object)("[Toss a Tick] Hit " + target.characterName + ", but they cannot receive another tick.")); } } return false; } int instanceID = ((Object)target).GetInstanceID(); if (LastHitProcessTimeByCharacterId.TryGetValue(instanceID, out var value) && Time.time <= value + 0.25f) { return false; } if (collision != null) { Vector3 relativeVelocity = collision.relativeVelocity; if (((Vector3)(ref relativeVelocity)).magnitude < minHitVelocity) { return false; } } Item val = ResolveTickItemForHit(target, collision, preferredTickItem, searchRadius, thrownWindowSeconds); if ((Object)(object)val == (Object)null) { return false; } LastHitProcessTimeByCharacterId[instanceID] = Time.time; if (!PeakticksIntegration.TryAttachTick(target, out var _)) { ManualLogSource log2 = TossATickPlugin.Log; if (log2 != null) { log2.LogWarning((object)("[Toss a Tick] Tick hit on " + target.characterName + ", but attach failed.")); } return false; } if ((Object)(object)((MonoBehaviourPun)val).photonView != (Object)null && ((MonoBehaviourPun)val).photonView.IsMine) { PhotonNetwork.Destroy(((Component)val).gameObject); } ManualLogSource log3 = TossATickPlugin.Log; if (log3 != null) { log3.LogInfo((object)("[Toss a Tick] Thrown tick attached to " + target.characterName + " and removed from the world.")); } return true; } public static void ArmThrownTickItem(Item item) { if ((Object)(object)item == (Object)null || !IsThrowableTickItem(item) || !PhotonNetwork.IsMasterClient || (Object)(object)((MonoBehaviourPun)item).photonView == (Object)null || !((MonoBehaviourPun)item).photonView.IsMine) { return; } TickThrowHandler tickThrowHandler = ((Component)item).GetComponent(); if ((Object)(object)tickThrowHandler == (Object)null) { tickThrowHandler = ((Component)item).gameObject.AddComponent(); } tickThrowHandler.Initialize(item); tickThrowHandler.MarkAsThrown(); if (TossATickPlugin.LogThrowDebug) { ManualLogSource log = TossATickPlugin.Log; if (log != null) { log.LogInfo((object)("[Toss a Tick] Tracking thrown tick: " + ((Object)item).name + ".")); } } } private static Item ResolveTickItemForHit(Character target, Collision collision, Item preferredTickItem, float searchRadius, float thrownWindowSeconds) { Item val = null; if (IsThrownTickCandidate(preferredTickItem, thrownWindowSeconds)) { val = preferredTickItem; } if ((Object)(object)val == (Object)null && collision != null) { Item val2 = ResolveItemFromCollision(collision); if (IsThrownTickCandidate(val2, thrownWindowSeconds)) { val = val2; } } if ((Object)(object)val == (Object)null) { val = FindNearbyThrownTick(target, searchRadius, thrownWindowSeconds); if ((Object)(object)val != (Object)null && TossATickPlugin.LogThrowDebug) { ManualLogSource log = TossATickPlugin.Log; if (log != null) { log.LogInfo((object)("[Toss a Tick] Resolved tick near " + target.characterName + " via nearby search: " + ((Object)val).name + ".")); } } } if ((Object)(object)val == (Object)null) { if (TossATickPlugin.LogThrowDebug && collision != null) { Character val3 = ResolveCharacterFromCollision(collision); if ((Object)(object)val3 != (Object)null) { ManualLogSource log2 = TossATickPlugin.Log; if (log2 != null) { log2.LogInfo((object)("[Toss a Tick] Player " + val3.characterName + " was hit, but no thrown tick was found.")); } } } return null; } if (TossATickPlugin.LogThrowDebug) { ManualLogSource log3 = TossATickPlugin.Log; if (log3 != null) { log3.LogInfo((object)("[Toss a Tick] Player hit with thrown tick item: " + ((Object)val).name + ".")); } } return val; } private static bool IsThrownTickCandidate(Item item, float thrownWindowSeconds) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 if ((Object)(object)item == (Object)null || (Object)(object)((MonoBehaviourPun)item).photonView == (Object)null || !((MonoBehaviourPun)item).photonView.IsMine) { return false; } if (!IsThrowableTickItem(item)) { return false; } if ((int)item.itemState > 0) { return false; } return IsRecentlyThrownTick(item, thrownWindowSeconds); } internal static bool SpawnAttachedTickVanilla(Character target) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) if (!PhotonNetwork.IsMasterClient) { return false; } if ((Object)(object)target == (Object)null || (Object)(object)target.refs?.view == (Object)null || (Object)(object)target.data == (Object)null) { return false; } if (target.data.dead || CharacterHasAttachedTick(target)) { return false; } GameObject val = PhotonNetwork.Instantiate("BugfixOnYou", Vector3.zero, Quaternion.identity, (byte)0, (object[])null); if ((Object)(object)val == (Object)null) { ManualLogSource log = TossATickPlugin.Log; if (log != null) { log.LogWarning((object)"[Toss a Tick] Failed to instantiate BugfixOnYou."); } return false; } PhotonView component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log2 = TossATickPlugin.Log; if (log2 != null) { log2.LogWarning((object)"[Toss a Tick] BugfixOnYou has no PhotonView."); } return false; } component.RPC("AttachBug", (RpcTarget)0, new object[1] { target.refs.view.ViewID }); return true; } private static Character ResolveCharacterFromCollision(Collision collision) { Character result = default(Character); if (CharacterRagdoll.TryGetCharacterFromCollider(collision.collider, ref result)) { return result; } return collision.gameObject.GetComponentInParent(); } } [HarmonyPatch(typeof(GlobalEvents), "TriggerItemThrown")] internal static class TickThrownGlobalPatch { private static void Postfix(Item item) { if (PhotonNetwork.IsMasterClient) { TickAttachHelper.ArmThrownTickItem(item); } } } internal class TickThrowHandler : MonoBehaviour { private const float MinHitVelocity = 0.5f; private Item _item; private float _armedAtTime = -999f; public void Initialize(Item item) { _item = item; } public void MarkAsThrown() { _armedAtTime = Time.time; } public bool IsWithinThrownWindow() { return (Object)(object)_item != (Object)null && Time.time - _armedAtTime <= 8f; } private void OnCollisionEnter(Collision collision) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_item == (Object)null) && !((Object)(object)((MonoBehaviourPun)_item).photonView == (Object)null) && ((MonoBehaviourPun)_item).photonView.IsMine && !((Object)(object)_item.rig == (Object)null) && IsWithinThrownWindow() && (LayerMask.op_Implicit(HelperFunctions.terrainMapMask) & (1 << ((Component)collision.transform).gameObject.layer)) == 0) { Character val = ResolveCharacterFromCollision(collision); if (!((Object)(object)val == (Object)null)) { TickAttachHelper.TryProcessTickHit(val, collision, _item, TossATickPlugin.ThrowSearchRadius, 8f, 0.5f); } } } private static Character ResolveCharacterFromCollision(Collision collision) { Character result = default(Character); if (CharacterRagdoll.TryGetCharacterFromCollider(collision.collider, ref result)) { return result; } return collision.gameObject.GetComponentInParent(); } }