Decompiled source of PillBottleMod v1.0.0

PillBottleMod.dll

Decompiled 21 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalLib.Modules;
using Microsoft.CodeAnalysis;
using PillBottleMod.Items;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PillBottleMod")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("A consumable pill bottle shop item with a speed/stamina buff, hangover, withdrawal, and overdose mechanic.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PillBottleMod")]
[assembly: AssemblyTitle("PillBottleMod")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PillBottleMod
{
	public class PillBottleHud : MonoBehaviour
	{
		public static PillBottleHud Instance;

		private Texture2D _pixel;

		private GUIStyle _statusStyle;

		private GUIStyle _smallStyle;

		private bool _loggedFirstGui;

		private bool _loggedFirstDraw;

		public static void EnsureExists()
		{
			//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))
			{
				GameObject val = new GameObject("PillBottleHud");
				Object.DontDestroyOnLoad((Object)(object)val);
				val.AddComponent<PillBottleHud>();
			}
		}

		private void Awake()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			_pixel = new Texture2D(1, 1);
			_pixel.SetPixel(0, 0, Color.white);
			_pixel.Apply();
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)"[PillBottle] HUD initialised.");
			}
		}

		private static bool ShouldDraw()
		{
			if (!PillBottleEffectManager.LocalHasState)
			{
				return false;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			PlayerControllerB localPlayerController = instance.localPlayerController;
			if ((Object)(object)localPlayerController == (Object)null)
			{
				return false;
			}
			if (localPlayerController.isPlayerDead)
			{
				return false;
			}
			return true;
		}

		private void OnGUI()
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			if (!_loggedFirstGui)
			{
				_loggedFirstGui = true;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[PillBottle] HUD OnGUI is running.");
				}
			}
			if (!ShouldDraw())
			{
				return;
			}
			if (!_loggedFirstDraw)
			{
				_loggedFirstDraw = true;
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"[PillBottle] HUD draw condition met (buff={PillBottleEffectManager.LocalBuffActive}, addiction={PillBottleEffectManager.LocalAddiction:0}%).");
				}
			}
			Rect r = default(Rect);
			((Rect)(ref r))..ctor(0f, 0f, (float)Screen.width, (float)Screen.height);
			if (PillBottleEffectManager.LocalBuffActive)
			{
				Fill(r, new Color(0.12f, 0.42f, 1f, 0.28f));
			}
			else if (PillBottleEffectManager.LocalHangoverActive)
			{
				Fill(r, new Color(0.25f, 0.35f, 0.15f, 0.22f));
			}
			float localAddiction = PillBottleEffectManager.LocalAddiction;
			if (localAddiction > 25f)
			{
				float num = Mathf.Lerp(0.05f, 0.35f, Mathf.InverseLerp(25f, 100f, localAddiction));
				Fill(r, new Color(0.6f, 0f, 0f, num));
			}
			DrawMeters(localAddiction);
		}

		private void DrawMeters(float addiction)
		{
			//IL_001c: 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_002a: 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_003f: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			if (_statusStyle == null)
			{
				_statusStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 16,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4
				};
				_smallStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 12,
					alignment = (TextAnchor)4
				};
			}
			float localOverdose = PillBottleEffectManager.LocalOverdose;
			bool flag = localOverdose > 0.001f;
			int num = ((!flag) ? 1 : 2);
			float num2 = 26f + (float)num * 36f + 8f;
			float num3 = ((float)Screen.width - 260f) * 0.5f;
			float num4 = (float)Screen.height - 40f - num2;
			Fill(new Rect(num3 - 10f, num4, 280f, num2), new Color(0f, 0f, 0f, 0.5f));
			float num5 = num4 + 6f;
			string text = (PillBottleEffectManager.LocalBuffActive ? "BUFFED" : (PillBottleEffectManager.LocalHangoverActive ? "HANGOVER" : ((addiction > 0f) ? "WITHDRAWAL" : "CLEAN")));
			if (PillBottleEffectManager.LocalStaminaActive)
			{
				text += "   +STAMINA";
			}
			GUI.color = Color.white;
			GUI.Label(new Rect(num3, num5, 260f, 20f), text, _statusStyle);
			num5 += 26f;
			Color fill = Color.Lerp(new Color(0.25f, 0.8f, 0.25f), new Color(0.9f, 0.12f, 0.12f), Mathf.Clamp01(addiction / 100f));
			DrawLabeledBar(num3, num5, 260f, 16f, Mathf.Clamp01(addiction / 100f), fill, $"Addiction {addiction:0}%");
			num5 += 36f;
			if (flag)
			{
				Color fill2 = Color.Lerp(new Color(1f, 0.6f, 0f), new Color(1f, 0.1f, 0.1f), localOverdose);
				DrawLabeledBar(num3, num5, 260f, 16f, localOverdose, fill2, $"Overdose {localOverdose * 100f:0}%");
			}
		}

		private void DrawLabeledBar(float x, float y, float w, float h, float frac, Color fill, string label)
		{
			//IL_0007: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			Fill(new Rect(x, y, w, h), new Color(0.12f, 0.12f, 0.12f, 0.95f));
			Fill(new Rect(x, y, w * Mathf.Clamp01(frac), h), fill);
			GUI.color = Color.white;
			GUI.Label(new Rect(x, y + h, w, 16f), label, _smallStyle);
			GUI.color = Color.white;
		}

		private void Fill(Rect r, Color c)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Color color = GUI.color;
			GUI.color = c;
			GUI.DrawTexture(r, (Texture)(object)_pixel);
			GUI.color = color;
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("yourname.pillbottlemod", "Pill Bottle Mod", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGUID = "yourname.pillbottlemod";

		public const string ModName = "Pill Bottle Mod";

		public const string ModVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<int> PillBottlePrice;

		internal static ConfigEntry<float> AddictionSecondsToMax;

		private readonly Harmony _harmony = new Harmony("yourname.pillbottlemod");

		private void Awake()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"Pill Bottle Mod v1.0.0 loading...");
			PillBottlePrice = ((BaseUnityPlugin)this).Config.Bind<int>("Shop", "Price", 60, "Cost of the Pill Bottle in the terminal store, in credits.");
			AddictionSecondsToMax = ((BaseUnityPlugin)this).Config.Bind<float>("Addiction", "SecondsToMaxAddiction", 900f, "Seconds of not dosing to climb from 0% to 100% addiction. Default 900 (15 min). Set low (e.g. 30) to test the withdrawal debuffs quickly.");
			PillBottleEffectManager.AddictionSecondsToMax = AddictionSecondsToMax.Value;
			_harmony.PatchAll();
			GameObject val = new GameObject("PillBottleHud");
			val.AddComponent<PillBottleHud>();
			Log.LogInfo((object)"Pill Bottle Mod loaded successfully.");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "PillBottleMod";

		public const string PLUGIN_NAME = "PillBottleMod";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace PillBottleMod.Items
{
	public class PillBottleState
	{
		public float buffEndTime = -1f;

		public float hangoverEndTime = -1f;

		public float addictionLevel = 0f;

		public int totalDosesTaken = 0;

		public readonly List<float> recentDoseTimes = new List<float>();

		public float nextAddictionDamageTick = -1f;

		public float staminaEndTime = -1f;

		public float baseMoveSpeed;

		public float baseJumpForce;

		public bool baseValuesCached = false;
	}
	public static class PillBottleEffectManager
	{
		private const float BuffDuration = 60f;

		private const float HangoverDuration = 180f;

		public static float AddictionSecondsToMax = 900f;

		private const int DosesBeforeAddictionStarts = 2;

		private const float OverdoseWindowSeconds = 60f;

		private const int OverdoseDoseThreshold = 3;

		private const float BuffSpeedMultiplier = 1.5f;

		private const float BuffJumpMultiplier = 1.4f;

		private const float HangoverSpeedMultiplier = 0.5f;

		private const float StaminaBoostDuration = 15f;

		private static readonly Dictionary<PlayerControllerB, PillBottleState> States = new Dictionary<PlayerControllerB, PillBottleState>();

		public static bool LocalHasState = false;

		public static bool LocalBuffActive = false;

		public static bool LocalHangoverActive = false;

		public static float LocalAddiction = 0f;

		public static float LocalOverdose = 0f;

		public static bool LocalStaminaActive = false;

		private static PillBottleState GetOrCreateState(PlayerControllerB player)
		{
			if (!States.TryGetValue(player, out var value))
			{
				value = new PillBottleState();
				States[player] = value;
			}
			return value;
		}

		public static void TakeDose(PlayerControllerB player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				PillBottleState orCreateState = GetOrCreateState(player);
				float now = Time.time;
				CacheBaseValuesIfNeeded(player, orCreateState);
				orCreateState.recentDoseTimes.Add(now);
				orCreateState.recentDoseTimes.RemoveAll((float t) => now - t > 60f);
				if (orCreateState.recentDoseTimes.Count >= 3)
				{
					KillPlayerFromOverdose(player);
					return;
				}
				orCreateState.totalDosesTaken++;
				orCreateState.addictionLevel = 0f;
				orCreateState.buffEndTime = now + 60f;
				orCreateState.hangoverEndTime = -1f;
				orCreateState.staminaEndTime = now + 15f;
				Plugin.Log.LogInfo((object)$"[PillBottle] {player.playerUsername} took a dose. Total: {orCreateState.totalDosesTaken}, Addiction: {orCreateState.addictionLevel:F0}%");
			}
		}

		private static void KillPlayerFromOverdose(PlayerControllerB player)
		{
			//IL_0022: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Log.LogInfo((object)("[PillBottle] " + player.playerUsername + " overdosed."));
			player.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 0, default(Vector3), false);
		}

		private static void CacheBaseValuesIfNeeded(PlayerControllerB player, PillBottleState state)
		{
			if (!state.baseValuesCached)
			{
				state.baseMoveSpeed = player.movementSpeed;
				state.baseJumpForce = player.jumpForce;
				state.baseValuesCached = true;
			}
		}

		public static void Tick(PlayerControllerB player)
		{
			if (!((Object)(object)player == (Object)null) && States.TryGetValue(player, out var value) && value.baseValuesCached)
			{
				float time = Time.time;
				bool flag = value.totalDosesTaken > 2;
				bool flag2 = value.buffEndTime > time;
				if (flag && !flag2)
				{
					float num = 100f / Mathf.Max(1f, AddictionSecondsToMax);
					value.addictionLevel = Mathf.Clamp(value.addictionLevel + num * Time.deltaTime, 0f, 100f);
				}
				bool flag3 = value.buffEndTime > time;
				bool hangoverActive = !flag3 && value.hangoverEndTime > time;
				if (!flag3 && value.buffEndTime > 0f && value.hangoverEndTime < 0f)
				{
					value.hangoverEndTime = time + 180f;
					hangoverActive = true;
					value.buffEndTime = -1f;
				}
				if (value.hangoverEndTime > 0f && time > value.hangoverEndTime)
				{
					value.hangoverEndTime = -1f;
					hangoverActive = false;
				}
				ApplyMovementEffects(player, value, flag3, hangoverActive);
				ApplyAddictionEffects(player, value);
				ApplyScreenEffects(player, value, flag3, hangoverActive);
			}
		}

		private static void ApplyMovementEffects(PlayerControllerB player, PillBottleState state, bool buffActive, bool hangoverActive)
		{
			float num = 1f;
			float num2 = 1f;
			if (buffActive)
			{
				num *= 1.5f;
				num2 *= 1.4f;
			}
			else if (hangoverActive)
			{
				num *= 0.5f;
			}
			num *= GetAddictionSpeedMultiplier(state);
			player.movementSpeed = state.baseMoveSpeed * num;
			player.jumpForce = state.baseJumpForce * num2;
			if (state.staminaEndTime > Time.time)
			{
				player.sprintMeter = 1f;
			}
		}

		private static float GetAddictionSpeedMultiplier(PillBottleState state)
		{
			if (state.addictionLevel >= 100f)
			{
				return 0.15f;
			}
			if (state.addictionLevel >= 75f)
			{
				return 0.25f;
			}
			if (state.addictionLevel >= 50f)
			{
				return 0.45f;
			}
			if (state.addictionLevel >= 25f)
			{
				return 0.7f;
			}
			return 1f;
		}

		private static void ApplyAddictionEffects(PlayerControllerB player, PillBottleState state)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			if (state.addictionLevel >= 75f && Random.value < 0.15f * Time.deltaTime)
			{
				TryDropHeldItem(player);
			}
			if (state.addictionLevel >= 100f)
			{
				if (state.nextAddictionDamageTick < 0f)
				{
					state.nextAddictionDamageTick = time + 5f;
				}
				if (time >= state.nextAddictionDamageTick)
				{
					player.DamagePlayer(5, true, true, (CauseOfDeath)0, 0, false, default(Vector3));
					state.nextAddictionDamageTick = time + 5f;
				}
			}
			else
			{
				state.nextAddictionDamageTick = -1f;
			}
		}

		private static void TryDropHeldItem(PlayerControllerB player)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player.currentlyHeldObjectServer != (Object)null)
			{
				player.DiscardHeldObject(false, (NetworkObject)null, default(Vector3), true);
			}
		}

		private static void ApplyScreenEffects(PlayerControllerB player, PillBottleState state, bool buffActive, bool hangoverActive)
		{
			if (!((Object)(object)StartOfRound.Instance == (Object)null) && !((Object)(object)player != (Object)(object)StartOfRound.Instance.localPlayerController))
			{
				float now = Time.time;
				state.recentDoseTimes.RemoveAll((float t) => now - t > 60f);
				float num = 0f;
				for (int num2 = 0; num2 < state.recentDoseTimes.Count; num2++)
				{
					float num3 = now - state.recentDoseTimes[num2];
					num += Mathf.Clamp01(1f - num3 / 60f);
				}
				LocalHasState = true;
				LocalBuffActive = buffActive;
				LocalHangoverActive = hangoverActive;
				LocalAddiction = state.addictionLevel;
				LocalOverdose = Mathf.Clamp01(num / 3f);
				LocalStaminaActive = state.staminaEndTime > now;
			}
		}
	}
	[HarmonyPatch]
	public static class PillBottleSetup
	{
		public const string TargetItemName = "Pill Bottle";

		private static bool _configured;

		public static Item PillBottleItem { get; private set; }

		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		[HarmonyPostfix]
		public static void StartOfRound_Awake_Postfix(StartOfRound __instance)
		{
			PillBottleHud.EnsureExists();
			if (_configured || (Object)(object)__instance == (Object)null || (Object)(object)__instance.allItemsList == (Object)null || __instance.allItemsList.itemsList == null)
			{
				return;
			}
			Item val = ((IEnumerable<Item>)__instance.allItemsList.itemsList).FirstOrDefault((Func<Item, bool>)((Item it) => (Object)(object)it != (Object)null && string.Equals(it.itemName, "Pill Bottle", StringComparison.OrdinalIgnoreCase)));
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)"[PillBottle] Could not find an item named \"Pill Bottle\". Dumping every item name so you can set TargetItemName to the exact string:");
				{
					foreach (Item items in __instance.allItemsList.itemsList)
					{
						if ((Object)(object)items != (Object)null)
						{
							Plugin.Log.LogError((object)("[PillBottle]   itemName = \"" + items.itemName + "\""));
						}
					}
					return;
				}
			}
			PillBottleItem = val;
			int num = ((Plugin.PillBottlePrice != null) ? Plugin.PillBottlePrice.Value : 60);
			Items.RegisterShopItem(val, (TerminalNode)null, (TerminalNode)null, (TerminalNode)null, num);
			Plugin.Log.LogInfo((object)$"[PillBottle] Registered \"{val.itemName}\" in the store for {num} credits.");
			_configured = true;
		}
	}
	[HarmonyPatch]
	public static class PillBottlePatches
	{
		[HarmonyPatch(typeof(GrabbableObject), "ItemActivate")]
		[HarmonyPostfix]
		public static void GrabbableObject_ItemActivate_Postfix(GrabbableObject __instance, bool used, bool buttonDown)
		{
			if (buttonDown && !((Object)(object)__instance.itemProperties == (Object)null) && string.Equals(__instance.itemProperties.itemName, "Pill Bottle", StringComparison.OrdinalIgnoreCase))
			{
				PlayerControllerB playerHeldBy = __instance.playerHeldBy;
				if (!((Object)(object)playerHeldBy == (Object)null) && !((Object)(object)StartOfRound.Instance == (Object)null) && !((Object)(object)playerHeldBy != (Object)(object)StartOfRound.Instance.localPlayerController))
				{
					PillBottleEffectManager.TakeDose(playerHeldBy);
					playerHeldBy.DestroyItemInSlotAndSync(playerHeldBy.currentItemSlot);
				}
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Update")]
		[HarmonyPostfix]
		public static void PlayerControllerB_Update_Postfix(PlayerControllerB __instance)
		{
			PillBottleEffectManager.Tick(__instance);
		}
	}
}