using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Photon.Pun; using Photon.Realtime; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyTitle("Death Lick")] [assembly: AssemblyDescription("Frog tongue hits kill you instantly.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Death Lick")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("c89a9484-ca9e-4ece-b455-f46a558dce1e")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace Death_Lick { [BepInPlugin("tony4twentys.Death_Lick", "Death Lick", "1.0.0")] [BepInProcess("PEAK.exe")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "tony4twentys.Death_Lick"; public const string PluginName = "Death Lick"; public const string PluginVersion = "1.0.0"; internal const byte EventCode = 192; internal const int ProtocolVersion = 1; internal const string EventMagic = "tony4twentys.Death_Lick"; internal static ConfigEntry Enabled; private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static bool IsEnabled { get { if (DeathLickNet.HostControlsConfig) { return DeathLickNet.HostEnabled; } return Enabled != null && Enabled.Value; } } private void Awake() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "If on, a frog tongue hit plays the connect sound and kills you instantly with no pull. If off, frogs use vanilla tongue behavior. Host setting is synced to clients who have the mod; clients cannot override it while the host has Death Lick. If the host does not have the mod, this local value is used."); Enabled.SettingChanged += delegate { DeathLickNet.OnLocalSettingChanged(); }; _harmony = new Harmony("tony4twentys.Death_Lick"); _harmony.PatchAll(typeof(Plugin).Assembly); DeathLickNet.Ensure(); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} v{1} loaded. Photon {2}.", "Death Lick", "1.0.0", (byte)192)); } } internal static class DeathLick { private struct RestPose { public Vector3 EndPos; public Quaternion EndRot; public Vector3 AimPos; public Quaternion AimRot; public bool HasEnd; public bool HasAim; } private static readonly Dictionary Rest = new Dictionary(); private static readonly Dictionary Running = new Dictionary(); private static readonly Dictionary Frogs = new Dictionary(); private static bool _loggedError; internal static void AbortAll() { List list = new List(Frogs.Values); for (int i = 0; i < list.Count; i++) { Abort(list[i]); } } internal static void AbortIfRunning(FrogTongue frog) { if (!((Object)(object)frog == (Object)null) && Running.ContainsKey(((Object)frog).GetInstanceID())) { Abort(frog); } } internal static void CacheRest(FrogTongue frog) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)frog == (Object)null) { return; } int instanceID = ((Object)frog).GetInstanceID(); if (!Rest.ContainsKey(instanceID)) { RestPose value = default(RestPose); if ((Object)(object)frog.tongueEnd != (Object)null) { value.HasEnd = true; value.EndPos = frog.tongueEnd.localPosition; value.EndRot = frog.tongueEnd.localRotation; } if ((Object)(object)frog.tongueRotation != (Object)null) { value.HasAim = true; value.AimPos = frog.tongueRotation.localPosition; value.AimRot = frog.tongueRotation.localRotation; } Rest[instanceID] = value; } } internal static void Abort(FrogTongue frog) { if (!((Object)(object)frog == (Object)null)) { int instanceID = ((Object)frog).GetInstanceID(); if (Running.TryGetValue(instanceID, out var value) && value != null) { ((MonoBehaviour)frog).StopCoroutine(value); } Running.Remove(instanceID); Frogs.Remove(instanceID); ClearTarget(frog); frog._tongueLength = 0f; RestoreTongue(frog); } } internal static void ClearTarget(FrogTongue frog) { if (!((Object)(object)frog == (Object)null)) { frog._targetRig = null; frog._targetCharacter = null; frog._isPulling = false; } } internal static void LogOnce(string message) { if (!_loggedError) { _loggedError = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)message); } } } private static void RestoreTongue(FrogTongue frog) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)frog == (Object)null) && Rest.TryGetValue(((Object)frog).GetInstanceID(), out var value)) { if (value.HasEnd && (Object)(object)frog.tongueEnd != (Object)null) { frog.tongueEnd.localPosition = value.EndPos; frog.tongueEnd.localRotation = value.EndRot; } if (value.HasAim && (Object)(object)frog.tongueRotation != (Object)null) { frog.tongueRotation.localPosition = value.AimPos; frog.tongueRotation.localRotation = value.AimRot; } } } private static void Finish(FrogTongue frog, int id) { Running.Remove(id); Frogs.Remove(id); if (!((Object)(object)frog == (Object)null)) { ClearTarget(frog); frog._tongueLength = 0f; RestoreTongue(frog); } } internal static bool TryStartAttack(FrogTongue frog, PhotonView characterView, float extraDelay, bool thrown) { Abort(frog); CacheRest(frog); Coroutine value = ((MonoBehaviour)frog).StartCoroutine(LickAndKill(frog, characterView, extraDelay, thrown)); int instanceID = ((Object)frog).GetInstanceID(); Running[instanceID] = value; Frogs[instanceID] = frog; return false; } private static IEnumerator LickAndKill(FrogTongue frog, PhotonView characterView, float extraDelay, bool thrown) { int id = ((Object)frog).GetInstanceID(); if (extraDelay > 0f) { yield return (object)new WaitForSeconds(extraDelay); } if (!StillRunning(frog, id, characterView)) { Finish(frog, id); yield break; } ClearTarget(frog); frog._tongueLength = 0f; RestoreTongue(frog); Character target = ((Component)characterView).GetComponent(); if ((Object)(object)target == (Object)null || (Object)(object)target.data == (Object)null || target.data.dead) { Finish(frog, id); yield break; } Vector3 dir = target.Center - ((Component)frog).transform.position; if (!thrown) { dir.y = 0f; } if (((Vector3)(ref dir)).sqrMagnitude < 0.0001f) { dir = ((Component)frog).transform.forward; } dir = ((Vector3)(ref dir)).normalized; ((MonoBehaviour)frog).StartCoroutine(frog.JumpRoutine(Quaternion.LookRotation(dir, Vector3.up), 0.5f)); if ((Object)(object)((Mob)frog).anim != (Object)null) { ((Mob)frog).anim.SetTrigger("Lick"); } yield return (object)new WaitForSeconds(0.2f); if (!StillRunning(frog, id, characterView) || (Object)(object)target == (Object)null || (Object)(object)target.data == (Object)null || target.data.dead) { Finish(frog, id); yield break; } frog._targetCharacter = target; while ((Object)(object)frog != (Object)null && frog._tongueLength < 1f && StillRunning(frog, id, characterView) && !((Object)(object)target.data == (Object)null) && !target.data.dead) { frog._tongueLength += Time.deltaTime * 4f; Vector3 fromLook = HelperFunctions.LookToDirection(Vector2.op_Implicit(target.data.lookValues), Vector3.forward); target.data.lookValues = Vector2.op_Implicit(HelperFunctions.DirectionToLook(Vector3.Slerp(fromLook, -dir, frog._tongueLength))); yield return null; } if (!StillRunning(frog, id, characterView)) { Finish(frog, id); yield break; } if ((Object)(object)target != (Object)null && (Object)(object)target.data != (Object)null && !target.data.dead) { Vector3 pos = target.Center; if ((Object)(object)frog.tongueConnectSFX != (Object)null) { frog.tongueConnectSFX.Play(pos); } if (target.IsLocal && (Object)(object)frog.tongueConnectSFXLocal != (Object)null) { frog.tongueConnectSFXLocal.Play(pos); } target.data.lastFroggedTime = Time.time; try { if (target.IsLocal) { target.DieInstantly(); } } catch (Exception ex) { LogOnce("DieInstantly failed once: " + ex); } } ClearTarget(frog); frog._tongueLength = 0f; RestoreTongue(frog); yield return (object)new WaitForSeconds(frog.hopAwayYieldTime); if (StillRunning(frog, id, characterView)) { frog.HopAway(-dir); } Finish(frog, id); } private static bool StillRunning(FrogTongue frog, int id, PhotonView characterView) { return Plugin.IsEnabled && (Object)(object)frog != (Object)null && (Object)(object)characterView != (Object)null && Running.ContainsKey(id); } } [HarmonyPatch(typeof(FrogTongue), "OnEnable")] internal static class Patch_OnEnable { private static void Postfix(FrogTongue __instance) { try { DeathLick.CacheRest(__instance); } catch (Exception ex) { DeathLick.LogOnce("OnEnable failed once: " + ex); } } } [HarmonyPatch(typeof(FrogTongue), "OnDisable")] internal static class Patch_OnDisable { private static void Postfix(FrogTongue __instance) { try { DeathLick.AbortIfRunning(__instance); } catch (Exception ex) { DeathLick.LogOnce("OnDisable failed once: " + ex); } } } [HarmonyPatch(typeof(FrogTongue), "LateUpdate")] internal static class Patch_LateUpdate { private static void Prefix(FrogTongue __instance) { try { if (Plugin.IsEnabled && !((Object)(object)__instance == (Object)null)) { Character targetCharacter = __instance._targetCharacter; if ((Object)(object)targetCharacter != (Object)null && ((Object)(object)targetCharacter.data == (Object)null || targetCharacter.data.dead)) { DeathLick.ClearTarget(__instance); } } } catch (Exception ex) { DeathLick.LogOnce("LateUpdate failed once: " + ex); } } } [HarmonyPatch(typeof(FrogTongue), "Attack")] internal static class Patch_Attack { private static bool Prefix(FrogTongue __instance, PhotonView characterView, float extraDelay, bool thrown) { try { if (!Plugin.IsEnabled || (Object)(object)__instance == (Object)null) { return true; } return DeathLick.TryStartAttack(__instance, characterView, extraDelay, thrown); } catch (Exception ex) { DeathLick.LogOnce("Attack prefix failed once: " + ex); return true; } } } internal sealed class DeathLickNet : MonoBehaviourPunCallbacks, IOnEventCallback { private const byte SubConfig = 1; private const byte SubConfigRequest = 2; private const float HostSyncTimeoutSeconds = 12f; private const float HostSyncRetrySeconds = 2f; internal static bool HostModConfirmed; internal static bool HostEnabled; private Coroutine _debounce; private Coroutine _hostSyncWatch; private bool _syncingFromNet; internal static DeathLickNet Instance { get; private set; } internal static bool HostControlsConfig { get { try { return PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient && HostModConfirmed; } catch { return false; } } } internal static void Ensure() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if ((Object)(object)Instance != (Object)null) { return; } GameObject val = new GameObject("DeathLick_Net"); Object.DontDestroyOnLoad((Object)(object)val); Instance = val.AddComponent(); try { if (PhotonNetwork.InRoom) { Instance.HandleJoinedRoom(); } } catch { } } private void Awake() { Instance = this; } public override void OnEnable() { ((MonoBehaviourPunCallbacks)this).OnEnable(); PhotonNetwork.AddCallbackTarget((object)this); } public override void OnDisable() { PhotonNetwork.RemoveCallbackTarget((object)this); ((MonoBehaviourPunCallbacks)this).OnDisable(); } public override void OnJoinedRoom() { HandleJoinedRoom(); } public override void OnLeftRoom() { StopHostSyncWatch(); HostModConfirmed = false; HostEnabled = false; } public override void OnMasterClientSwitched(Player newMasterClient) { if (PhotonNetwork.IsMasterClient) { HostModConfirmed = true; BroadcastNow(); StopHostSyncWatch(); ApplyEnabledSideEffects(); } else { HostModConfirmed = false; RequestHostSync(); RestartHostSyncWatch(); } } public override void OnPlayerEnteredRoom(Player newPlayer) { if (PhotonNetwork.IsMasterClient) { ((MonoBehaviour)this).StartCoroutine(BroadcastAfterDelay(0.5f)); } } public void OnEvent(EventData photonEvent) { if (photonEvent == null || photonEvent.Code != 192 || !(photonEvent.CustomData is object[] array) || array.Length < 3 || !(array[0] is string text) || text != "tony4twentys.Death_Lick" || ToInt(array[1]) != 1) { return; } byte b = ToByte(array[2]); if (b == 2 && PhotonNetwork.IsMasterClient) { BroadcastNow(); } else { if (b != 1 || array.Length < 4) { return; } try { _syncingFromNet = true; StopHostSyncWatch(); HostModConfirmed = true; HostEnabled = Convert.ToBoolean(array[3]); ApplyEnabledSideEffects(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[Death Lick] Host config sync applied. Enabled=" + HostEnabled)); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("[Death Lick] Config sync failed: " + ex.Message)); } } finally { _syncingFromNet = false; } } } internal static void OnLocalSettingChanged() { if (!((Object)(object)Instance != (Object)null) || !Instance._syncingFromNet) { if (!Plugin.IsEnabled) { DeathLick.AbortAll(); } ScheduleBroadcast(); } } internal static void ScheduleBroadcast() { if (!((Object)(object)Instance == (Object)null) && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient) { if (Instance._debounce != null) { ((MonoBehaviour)Instance).StopCoroutine(Instance._debounce); } Instance._debounce = ((MonoBehaviour)Instance).StartCoroutine(Instance.DebounceBroadcast()); } } private void HandleJoinedRoom() { if (PhotonNetwork.IsMasterClient) { HostModConfirmed = true; BroadcastNow(); StopHostSyncWatch(); } else { HostModConfirmed = false; RequestHostSync(); RestartHostSyncWatch(); } } private static void RequestHostSync() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { PhotonNetwork.RaiseEvent((byte)192, (object)new object[3] { "tony4twentys.Death_Lick", 1, (byte)2 }, new RaiseEventOptions { Receivers = (ReceiverGroup)2 }, SendOptions.SendReliable); } } private void RestartHostSyncWatch() { StopHostSyncWatch(); if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { _hostSyncWatch = ((MonoBehaviour)this).StartCoroutine(WatchForHostSync()); } } private void StopHostSyncWatch() { if (_hostSyncWatch != null) { ((MonoBehaviour)this).StopCoroutine(_hostSyncWatch); _hostSyncWatch = null; } } private IEnumerator WatchForHostSync() { float elapsed = 0f; float nextRetry = 2f; while (elapsed < 12f) { if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient || HostModConfirmed) { yield break; } elapsed += Time.unscaledDeltaTime; if (elapsed >= nextRetry) { RequestHostSync(); nextRetry += 2f; } yield return null; } if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient && !HostModConfirmed) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[Death Lick] Host does not appear to have Death Lick — using local config."); } } _hostSyncWatch = null; } private IEnumerator DebounceBroadcast() { yield return (object)new WaitForSeconds(0.25f); BroadcastNow(); _debounce = null; } private IEnumerator BroadcastAfterDelay(float seconds) { yield return (object)new WaitForSeconds(seconds); BroadcastNow(); } private static void BroadcastNow() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_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_0072: Expected O, but got Unknown if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient) { bool flag = Plugin.Enabled != null && Plugin.Enabled.Value; PhotonNetwork.RaiseEvent((byte)192, (object)new object[4] { "tony4twentys.Death_Lick", 1, (byte)1, flag }, new RaiseEventOptions { Receivers = (ReceiverGroup)0 }, SendOptions.SendReliable); } } private static void ApplyEnabledSideEffects() { if (!Plugin.IsEnabled) { DeathLick.AbortAll(); } } private static int ToInt(object value) { return Convert.ToInt32(value); } private static byte ToByte(object value) { return Convert.ToByte(value); } } }