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<bool> _logThrowDebug;
private ConfigEntry<float> _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<bool>("Debug", "LogThrowDebug", false, "When true, logs throw hit detection and attach attempts.");
_throwSearchRadius = ((BaseUnityPlugin)this).Config.Bind<float>("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<int, float> LastHitProcessTimeByCharacterId = new Dictionary<int, float>();
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<BugPhobia>() != (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<Bugfix, Character> 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<Item>();
if ((Object)(object)result != (Object)null)
{
return result;
}
}
return collision.gameObject.GetComponentInParent<Item>();
}
public static bool IsRecentlyThrownTick(Item item, float thrownWindowSeconds = 8f)
{
if ((Object)(object)item == (Object)null)
{
return false;
}
TickThrowHandler component = ((Component)item).GetComponent<TickThrowHandler>();
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<float>();
}
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<TickThrowHandler>();
if ((Object)(object)tickThrowHandler == (Object)null)
{
tickThrowHandler = ((Component)item).gameObject.AddComponent<TickThrowHandler>();
}
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<PhotonView>();
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<Character>();
}
}
[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<Character>();
}
}