Decompiled source of PourDecisions v1.0.1

PourDecisions.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using PourDecisions.Configuration;
using PourDecisions.Models;
using PourDecisions.Patches;
using PourDecisions.Services;
using PourDecisions.UI;
using StatsCore;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("Vippy")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+4ca6472b7c7569714eccff49aa7fc5f09e9a1a5d")]
[assembly: AssemblyProduct("PourDecisions")]
[assembly: AssemblyTitle("PourDecisions")]
[assembly: InternalsVisibleTo("PourDecisions.Tests")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
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;
		}
	}
	[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 PourDecisions
{
	internal static class BuildInfo
	{
		public const string Version = "1.0.1";
	}
	[BepInPlugin("Vippy.PourDecisions", "Pour Decisions", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PourDecisions : BaseUnityPlugin
	{
		public const string Guid = "Vippy.PourDecisions";

		internal static PourDecisions Instance { get; private set; }

		internal static ManualLogSource Logger => Instance.BaseLogger;

		private ManualLogSource BaseLogger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			//IL_003d: 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_0044: Expected O, but got Unknown
			//IL_0049: Expected O, but got Unknown
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			PluginConfig.Init(((BaseUnityPlugin)this).Config);
			if (Harmony == null)
			{
				Harmony val = new Harmony("Vippy.PourDecisions");
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} loaded - last call. (LogLevel={PluginConfig.LogLevel.Value})");
		}

		internal static void LogAlways(string msg)
		{
			Logger.LogInfo((object)msg);
		}

		internal static void LogInfo(string msg)
		{
			if (PluginConfig.LogLevel.Value >= VerbosityLevel.Debug)
			{
				Logger.LogInfo((object)msg);
			}
		}

		internal static void LogVerbose(string msg)
		{
			if (PluginConfig.LogLevel.Value >= VerbosityLevel.Verbose)
			{
				Logger.LogDebug((object)msg);
			}
		}
	}
}
namespace PourDecisions.UI
{
	public class DrinkHud : MonoBehaviour
	{
		private sealed class Toast
		{
			public TextMeshProUGUI Tmp;

			public float Age;

			public float Y;
		}

		private sealed class FeedItem
		{
			public TextMeshProUGUI Tmp;

			public float Age;

			public float Y;
		}

		private sealed class Counter
		{
			private float _shown;

			private int _last;

			private int _lastTick;

			public float Pop;

			public float Flash;

			public int ShownInt => Mathf.FloorToInt(_shown + 0.0001f);

			public bool Animating
			{
				get
				{
					if (!(Mathf.Abs(_shown - (float)_last) > 0.01f) && !(Pop > 0.01f))
					{
						return Flash > 0.01f;
					}
					return true;
				}
			}

			public void Bump()
			{
				Pop = 0.35f;
				Flash = 1f;
			}

			public bool Step(int target)
			{
				bool result = false;
				if (target > _last)
				{
					Bump();
				}
				else if (target < _last)
				{
					_shown = target;
					_lastTick = target;
				}
				_last = target;
				float num = Mathf.Max(3f, Mathf.Abs((float)target - _shown) * 6f);
				_shown = Mathf.MoveTowards(_shown, (float)target, num * Time.deltaTime);
				int shownInt = ShownInt;
				if (shownInt > _lastTick)
				{
					result = true;
				}
				_lastTick = shownInt;
				Pop = Mathf.MoveTowards(Pop, 0f, Time.deltaTime * 2.5f);
				Flash = Mathf.MoveTowards(Flash, 0f, Time.deltaTime * 2f);
				return result;
			}
		}

		private static readonly Color Red = new Color(0.85f, 0.2f, 0.2f);

		private static readonly Color Green = new Color(0.33f, 0.55f, 0.18f);

		private static readonly Color Amber = new Color(1f, 0.65f, 0f);

		private static readonly Color White = new Color(0.95f, 0.95f, 0.95f);

		private static readonly Color FlashGreen = new Color(0.45f, 0.95f, 0.3f);

		private bool _hudVisible = true;

		private object? _lastLevel;

		private HUDCanvas? _lastCanvas;

		private TextMeshProUGUI? _downedLine;

		private TextMeshProUGUI? _owedLine;

		private readonly Counter _downed = new Counter();

		private int _owedPrev;

		private float _owedPop;

		private float _owedFlash;

		private float _dudFlash;

		private readonly List<Toast> _toasts = new List<Toast>();

		private const float ToastSpacing = 26f;

		private const float ToastBaseY = -190f;

		private const int ToastMax = 6;

		private readonly List<FeedItem> _feed = new List<FeedItem>();

		private const float FeedFont = 12f;

		private const float FeedWidth = 320f;

		private const float FeedGap = 1f;

		private const float FeedX = 24f;

		private const float FeedBaseY = 72f;

		private const int FeedMax = 5;

		private const float FeedRestAlpha = 0.25f;

		private const float FeedFullTime = 5f;

		private const float FeedFadeTime = 2f;

		private float _feedActivity = -999f;

		private static bool _rebindRequested;

		private bool _listening;

		private static KeyCode[]? _allKeys;

		public static DrinkHud? Instance { get; private set; }

		private static float ToastLife => PluginConfig.PromptSeconds.Value;

		public static void RequestRebind()
		{
			_rebindRequested = true;
		}

		private void Awake()
		{
			Instance = this;
		}

		private void Update()
		{
			//IL_0041: 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)
			if (_rebindRequested && !_listening)
			{
				_rebindRequested = false;
				_listening = true;
			}
			if (_listening)
			{
				HandleRebind();
				return;
			}
			SyncCanvas();
			DetectLevelChange();
			DrunkTexts.Tick();
			if (Input.GetKeyDown(PluginConfig.ToggleHudKey.Value))
			{
				_hudVisible = !_hudVisible;
			}
			if (CanSettle() && Input.GetKeyDown(PluginConfig.ConfirmKey.Value))
			{
				if (BarTabStats.ConfirmOne())
				{
					Sfx.Drink();
					CamFx.Confirm();
					_downed.Bump();
					PourStats.PublishLocalDowned(BarTabStats.LocalDrunk());
				}
				else
				{
					Sfx.Dud();
					_dudFlash = 1f;
				}
			}
			AnimateTab();
			UpdateToasts();
			UpdateDrunkFeed();
		}

		private void HandleRebind()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Invalid comparison between Unknown and I4
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)BigMessageUI.instance != (Object)null)
			{
				BigMessageUI.instance.BigMessage("PRESS A KEY\nfor confirm-a-drink   (Esc cancels)", "", 48f, Amber, White);
			}
			if (Input.GetKeyDown((KeyCode)27))
			{
				EndRebind(null);
				return;
			}
			if (_allKeys == null)
			{
				_allKeys = (KeyCode[])Enum.GetValues(typeof(KeyCode));
			}
			KeyCode[] allKeys = _allKeys;
			foreach (KeyCode val in allKeys)
			{
				if ((int)val != 0 && (int)val != 27 && Input.GetKeyDown(val))
				{
					EndRebind(val);
					break;
				}
			}
		}

		private void EndRebind(KeyCode? key)
		{
			//IL_0022: 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)
			//IL_005e: 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)
			_listening = false;
			PluginConfig.RebindConfirmKey.Value = false;
			if (key.HasValue)
			{
				PluginConfig.ConfirmKey.Value = key.Value;
				if ((Object)(object)BigMessageUI.instance != (Object)null)
				{
					BigMessageUI.instance.BigMessage($"BOUND TO {key.Value}", "", 50f, Green, White);
				}
				Sfx.Toast();
			}
		}

		private void AnimateTab()
		{
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: 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_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			string steamID = BarTabStats.LocalSteamID();
			int num = BarTabStats.DrunkFor(steamID);
			int num2 = Mathf.Max(0, BarTabStats.TabFor(steamID) - num);
			bool flag = _hudVisible && PluginConfig.Enabled.Value && PluginConfig.ShowTab.Value && InRun();
			if (num2 > _owedPrev)
			{
				_owedPop = 0.35f;
				_owedFlash = 1f;
				if (flag)
				{
					Sfx.Tick();
				}
			}
			else if (num2 < _owedPrev)
			{
				_owedPop = Mathf.Max(_owedPop, 0.25f);
			}
			_owedPrev = num2;
			_owedPop = Mathf.MoveTowards(_owedPop, 0f, Time.deltaTime * 2.5f);
			_owedFlash = Mathf.MoveTowards(_owedFlash, 0f, Time.deltaTime * 2f);
			_downed.Step(num);
			_dudFlash = Mathf.MoveTowards(_dudFlash, 0f, Time.deltaTime * 2.5f);
			bool flag2 = num > 0 || num2 > 0 || _downed.Animating || _owedPop > 0.01f || _dudFlash > 0.01f;
			bool flag3 = flag && flag2;
			EnsureLines();
			if (!((Object)(object)_downedLine == (Object)null) && !((Object)(object)_owedLine == (Object)null))
			{
				((Component)_downedLine).gameObject.SetActive(flag3);
				bool flag4 = flag3 && num2 > 0 && CanSettle();
				((Component)_owedLine).gameObject.SetActive(flag4);
				if (flag3)
				{
					string arg = ColorUtility.ToHtmlStringRGB(Color.Lerp(Color.Lerp(White, FlashGreen, _downed.Flash), Red, _dudFlash));
					((TMP_Text)_downedLine).text = $"<color=#558B2F>DOWNED</color>   <color=#{arg}>{_downed.ShownInt}</color>";
					((Transform)((TMP_Text)_downedLine).rectTransform).localScale = Vector3.one * (1f + _downed.Pop + _dudFlash * 0.15f);
				}
				if (flag4)
				{
					string effectiveUnitWord = PluginConfig.EffectiveUnitWord;
					string arg2 = ((num2 == 1) ? effectiveUnitWord : (effectiveUnitWord + "s"));
					string text = ((object)PluginConfig.ConfirmKey.Value/*cast due to .constrained prefix*/).ToString();
					float alpha = 0.6f + 0.4f * Mathf.Abs(Mathf.Sin(Time.unscaledTime * 3f));
					string arg3 = ColorUtility.ToHtmlStringRGB(Color.Lerp(Amber, Red, _owedFlash));
					((TMP_Text)_owedLine).text = $"<color=#{arg3}>DRINK UP   {num2} {arg2}</color>  " + "<size=70%><color=#999999>[" + text + "]</color></size>";
					((Transform)((TMP_Text)_owedLine).rectTransform).localScale = Vector3.one * (1f + _owedPop);
					((TMP_Text)_owedLine).alpha = alpha;
				}
			}
		}

		private void DetectLevelChange()
		{
			RunManager instance = RunManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			object levelCurrent = instance.levelCurrent;
			if (levelCurrent != _lastLevel)
			{
				_lastLevel = levelCurrent;
				RuleEngine.OnNewLevel();
				DrunkTexts.ResetLevel();
				Sfx.ResetLevelSearch();
				if (SemiFunc.RunIsLevel())
				{
					ProofService.SoberUp();
					RuleEngine.OnEvent(DrinkEvent.Waterfall, null);
				}
				if (SemiFunc.RunIsLobby() && SemiFunc.IsMasterClientOrSingleplayer())
				{
					TaxmanBartender.Recap();
				}
			}
		}

		internal void ShowOrder(DrinkOrder order)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: 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)
			if (!_hudVisible || !PluginConfig.Enabled.Value || !InRun())
			{
				return;
			}
			string key = BarTabStats.LocalSteamID();
			if (order.IsTeam || order.Targets.ContainsKey(key))
			{
				(string, Color, Color) tuple = Compose(order);
				string item = tuple.Item1;
				Color item2 = tuple.Item2;
				Color item3 = tuple.Item3;
				string amount = (order.FinishDrink ? "FINISH" : $"+{order.Amount}");
				PushToast(amount, order.Reason, item2);
				if (order.IsTeam && PluginConfig.TeamSplash.Value && (Object)(object)BigMessageUI.instance != (Object)null)
				{
					BigMessageUI.instance.BigMessage(item, "", 70f, item2, item3);
				}
				if (order.IsTeam && order.Kind == DrinkEvent.CashOut)
				{
					Sfx.Toast();
				}
				else
				{
					Sfx.Drink();
				}
				if (order.FinishDrink)
				{
					CamFx.Long();
				}
				else if (order.Kind == DrinkEvent.Flatlined)
				{
					CamFx.Short();
				}
				else
				{
					CamFx.Tiny();
				}
			}
		}

		private (string, Color, Color) Compose(DrinkOrder order)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			string effectiveUnitWord = PluginConfig.EffectiveUnitWord;
			string reason = order.Reason;
			if (order.FinishDrink)
			{
				return (reason + "\nFinish your " + effectiveUnitWord + "!", Red, White);
			}
			string arg = ((order.Amount == 1) ? effectiveUnitWord : (effectiveUnitWord + "s"));
			string text = $"Take {order.Amount} {arg}";
			Color val;
			switch (order.Kind)
			{
			case DrinkEvent.CashOut:
			case DrinkEvent.Surplus:
			case DrinkEvent.WelcomeBack:
				val = Green;
				break;
			case DrinkEvent.Flatlined:
			case DrinkEvent.QuotaMissed:
				val = Red;
				break;
			case DrinkEvent.Butterfingers:
			case DrinkEvent.FriendlyFire:
			case DrinkEvent.Backstabber:
				val = Amber;
				break;
			default:
				val = White;
				break;
			}
			Color item = val;
			return (reason + "\n" + text, item, White);
		}

		private void EnsureLines()
		{
			if (!((Object)(object)_downedLine != (Object)null) && !((Object)(object)HUDCanvas.instance == (Object)null) && NativeText.TryCaptureFont())
			{
				_downedLine = MakeLine("PourDecisions_Downed", -118f, 18f);
				_owedLine = MakeLine("PourDecisions_Owed", -146f, 22f);
			}
		}

		private static TextMeshProUGUI MakeLine(string name, float y, float size)
		{
			//IL_002f: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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)
			TextMeshProUGUI obj = NativeText.Create(((Component)HUDCanvas.instance).transform, name, size, (TextAlignmentOptions)257);
			RectTransform rectTransform = ((TMP_Text)obj).rectTransform;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			rectTransform.anchorMax = val;
			rectTransform.anchorMin = val;
			rectTransform.pivot = new Vector2(0f, 1f);
			rectTransform.anchoredPosition = new Vector2(24f, y);
			rectTransform.sizeDelta = new Vector2(420f, 40f);
			return obj;
		}

		private void PushToast(string amount, string reason, Color color)
		{
			//IL_004d: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)HUDCanvas.instance == (Object)null) && NativeText.TryCaptureFont())
			{
				TextMeshProUGUI val = NativeText.Create(((Component)HUDCanvas.instance).transform, "PourDecisions_Toast", 18f, (TextAlignmentOptions)257);
				RectTransform rectTransform = ((TMP_Text)val).rectTransform;
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(0f, 1f);
				rectTransform.anchorMax = val2;
				rectTransform.anchorMin = val2;
				rectTransform.pivot = new Vector2(0f, 1f);
				rectTransform.sizeDelta = new Vector2(460f, 28f);
				rectTransform.anchoredPosition = new Vector2(24f, -174f);
				((TMP_Text)val).alpha = 0f;
				string text = ColorUtility.ToHtmlStringRGB(color);
				((TMP_Text)val).text = "<color=#" + text + ">" + amount + "</color>  <size=85%><color=#d2d2d2>" + reason + "</color></size>";
				_toasts.Insert(0, new Toast
				{
					Tmp = val,
					Y = -174f
				});
				while (_toasts.Count > 6)
				{
					Object.Destroy((Object)(object)((Component)_toasts[_toasts.Count - 1].Tmp).gameObject);
					_toasts.RemoveAt(_toasts.Count - 1);
				}
			}
		}

		internal void AddDrunkFeed(string name, string line)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (PluginConfig.Enabled.Value && PluginConfig.DrunkTexts.Value && (!PluginConfig.UseChatBox.Value || !ChatBoxBridge.Post(name, line)) && !((Object)(object)HUDCanvas.instance == (Object)null) && NativeText.TryCaptureFont())
			{
				TextMeshProUGUI val = NativeText.Create(((Component)HUDCanvas.instance).transform, "PourDecisions_Feed", 12f, (TextAlignmentOptions)1025);
				((TMP_Text)val).enableWordWrapping = true;
				((TMP_Text)val).characterSpacing = -1.5f;
				((TMP_Text)val).lineSpacing = -10f;
				RectTransform rectTransform = ((TMP_Text)val).rectTransform;
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(0f, 0f);
				rectTransform.anchorMax = val2;
				rectTransform.anchorMin = val2;
				rectTransform.pivot = new Vector2(0f, 0f);
				rectTransform.sizeDelta = new Vector2(320f, 40f);
				rectTransform.anchoredPosition = new Vector2(24f, 60f);
				((TMP_Text)val).alpha = 0f;
				((TMP_Text)val).text = "<color=#e0a040>" + name + "</color>  <color=#cfc6ba>" + line + "</color>";
				_feed.Add(new FeedItem
				{
					Tmp = val,
					Y = 60f
				});
				_feedActivity = Time.time;
				while (_feed.Count > 5)
				{
					Object.Destroy((Object)(object)((Component)_feed[0].Tmp).gameObject);
					_feed.RemoveAt(0);
				}
			}
		}

		private void SyncCanvas()
		{
			HUDCanvas instance = HUDCanvas.instance;
			if (!((Object)(object)instance == (Object)(object)_lastCanvas))
			{
				_lastCanvas = instance;
				_feed.Clear();
				_toasts.Clear();
				_downedLine = null;
				_owedLine = null;
			}
		}

		private void UpdateDrunkFeed()
		{
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			for (int num = _feed.Count - 1; num >= 0; num--)
			{
				FeedItem feedItem = _feed[num];
				feedItem.Age += Time.deltaTime;
				if ((Object)(object)feedItem.Tmp == (Object)null)
				{
					_feed.RemoveAt(num);
				}
			}
			bool flag = !_hudVisible || !PluginConfig.Enabled.Value;
			float num2 = Time.time - _feedActivity;
			float num3 = ((num2 <= 5f) ? 1f : Mathf.Lerp(1f, 0.25f, Mathf.Clamp01((num2 - 5f) / 2f)));
			float num4 = 72f;
			for (int num5 = _feed.Count - 1; num5 >= 0; num5--)
			{
				FeedItem feedItem2 = _feed[num5];
				if (!((Object)(object)feedItem2.Tmp == (Object)null))
				{
					feedItem2.Y = Mathf.Lerp(feedItem2.Y, num4, Time.deltaTime * 10f);
					((TMP_Text)feedItem2.Tmp).rectTransform.anchoredPosition = new Vector2(24f, feedItem2.Y);
					float num6 = ((feedItem2.Age < 0.25f) ? (feedItem2.Age / 0.25f) : 1f);
					((TMP_Text)feedItem2.Tmp).alpha = (flag ? 0f : Mathf.Clamp01(num3 * num6));
					num4 += Mathf.Max(((TMP_Text)feedItem2.Tmp).preferredHeight, 12f) + 1f;
				}
			}
		}

		private void UpdateToasts()
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			bool flag = !_hudVisible || !PluginConfig.Enabled.Value;
			float toastLife = ToastLife;
			for (int num = _toasts.Count - 1; num >= 0; num--)
			{
				Toast toast = _toasts[num];
				toast.Age += Time.deltaTime;
				if ((Object)(object)toast.Tmp == (Object)null || toast.Age >= toastLife)
				{
					if ((Object)(object)toast.Tmp != (Object)null)
					{
						Object.Destroy((Object)(object)((Component)toast.Tmp).gameObject);
					}
					_toasts.RemoveAt(num);
				}
				else
				{
					float num2 = -190f - (float)num * 26f;
					toast.Y = Mathf.Lerp(toast.Y, num2, Time.deltaTime * 10f);
					((TMP_Text)toast.Tmp).rectTransform.anchoredPosition = new Vector2(24f, toast.Y);
					float num3 = 1f;
					if (toast.Age < 0.2f)
					{
						num3 = toast.Age / 0.2f;
					}
					else if (toast.Age > toastLife - 0.8f)
					{
						num3 = (toastLife - toast.Age) / 0.8f;
					}
					((TMP_Text)toast.Tmp).alpha = (flag ? 0f : Mathf.Clamp01(num3));
				}
			}
		}

		private static bool InRun()
		{
			if ((Object)(object)RunManager.instance != (Object)null)
			{
				if (!SemiFunc.RunIsLevel() && !SemiFunc.RunIsArena() && !SemiFunc.RunIsShop())
				{
					return SemiFunc.RunIsLobby();
				}
				return true;
			}
			return false;
		}

		private static bool CanSettle()
		{
			if (PluginConfig.Enabled.Value && (Object)(object)RunManager.instance != (Object)null)
			{
				if (!SemiFunc.RunIsLevel())
				{
					return SemiFunc.RunIsArena();
				}
				return true;
			}
			return false;
		}
	}
	internal static class NativeText
	{
		private static TMP_FontAsset? _font;

		private static Material? _material;

		public static bool TryCaptureFont()
		{
			if ((Object)(object)_font != (Object)null)
			{
				return true;
			}
			TextMeshProUGUI val = GrabSource();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			_font = ((TMP_Text)val).font;
			_material = ((TMP_Text)val).fontSharedMaterial;
			return (Object)(object)_font != (Object)null;
		}

		private static TextMeshProUGUI? GrabSource()
		{
			if ((Object)(object)BigMessageUI.instance != (Object)null && (Object)(object)((SemiUI)BigMessageUI.instance).uiText != (Object)null)
			{
				return ((SemiUI)BigMessageUI.instance).uiText;
			}
			if ((Object)(object)HaulUI.instance != (Object)null && (Object)(object)((SemiUI)HaulUI.instance).uiText != (Object)null)
			{
				return ((SemiUI)HaulUI.instance).uiText;
			}
			SemiUI val = Object.FindObjectOfType<SemiUI>();
			if ((Object)(object)val != (Object)null && (Object)(object)val.uiText != (Object)null)
			{
				return val.uiText;
			}
			return Object.FindObjectOfType<TextMeshProUGUI>();
		}

		public static TextMeshProUGUI Create(Transform parent, string name, float fontSize, TextAlignmentOptions align)
		{
			//IL_0014: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			TryCaptureFont();
			if ((Object)(object)_font != (Object)null)
			{
				((TMP_Text)val2).font = _font;
			}
			if ((Object)(object)_material != (Object)null)
			{
				((TMP_Text)val2).fontSharedMaterial = _material;
			}
			((TMP_Text)val2).fontSize = fontSize;
			((TMP_Text)val2).alignment = align;
			((TMP_Text)val2).richText = true;
			((Graphic)val2).raycastTarget = false;
			return val2;
		}
	}
	public class UsageSplash : MonoBehaviour
	{
		private static bool _shownThisLaunch;

		private static readonly Color Bg = new Color(0.03f, 0.02f, 0.02f, 0.95f);

		private static readonly Color TitleNormal = new Color(0.88f, 0.63f, 0.25f);

		private static readonly Color TitleAlarm = new Color(0.72f, 0.2f, 0.2f);

		private static readonly Color Body = new Color(0.74f, 0.71f, 0.65f);

		private static readonly Color Hint = new Color(0.55f, 0.52f, 0.47f);

		private CanvasGroup _group;

		private RectTransform _content;

		private TextMeshProUGUI _hint;

		private Vector2 _contentHome;

		private bool _dismissable;

		private bool _dismissed;

		public static void Show(string title, string body, bool alarm)
		{
			if (!_shownThisLaunch)
			{
				_shownThisLaunch = true;
				Spawn(title, body, alarm);
			}
		}

		public static void ShowForced(string title, string body, bool alarm)
		{
			Spawn(title, body, alarm);
		}

		private static void Spawn(string title, string body, bool alarm)
		{
			//IL_0005: 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_0010: Expected O, but got Unknown
			GameObject val = new GameObject("PourDecisions_UsageSplash");
			Object.DontDestroyOnLoad((Object)val);
			val.AddComponent<UsageSplash>().Build(title, body, alarm);
		}

		private void Build(string title, string body, bool alarm)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: 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_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			Canvas obj = ((Component)this).gameObject.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 32760;
			CanvasScaler obj2 = ((Component)this).gameObject.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = new Vector2(1920f, 1080f);
			obj2.screenMatchMode = (ScreenMatchMode)0;
			obj2.matchWidthOrHeight = 0.5f;
			_group = ((Component)this).gameObject.AddComponent<CanvasGroup>();
			_group.alpha = 0f;
			Stretch(((Graphic)NewImage("bg", ((Component)this).transform, Bg)).rectTransform);
			_content = new GameObject("content", new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>();
			((Transform)_content).SetParent(((Component)this).transform, false);
			RectTransform content = _content;
			RectTransform content2 = _content;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			content2.anchorMax = val;
			content.anchorMin = val;
			_content.pivot = new Vector2(0.5f, 0.5f);
			_content.sizeDelta = new Vector2(760f, 480f);
			_contentHome = _content.anchoredPosition;
			TextMeshProUGUI obj3 = NativeText.Create((Transform)(object)_content, "title", 62f, (TextAlignmentOptions)514);
			((TMP_Text)obj3).text = title;
			((Graphic)obj3).color = (alarm ? TitleAlarm : TitleNormal);
			((TMP_Text)obj3).fontStyle = (FontStyles)1;
			((TMP_Text)obj3).characterSpacing = 8f;
			Place(((TMP_Text)obj3).rectTransform, new Vector2(720f, 110f), new Vector2(0f, 150f));
			TextMeshProUGUI obj4 = NativeText.Create((Transform)(object)_content, "body", 32f, (TextAlignmentOptions)514);
			((TMP_Text)obj4).text = body;
			((Graphic)obj4).color = Body;
			((TMP_Text)obj4).fontStyle = (FontStyles)1;
			((TMP_Text)obj4).enableWordWrapping = true;
			((TMP_Text)obj4).lineSpacing = 12f;
			Place(((TMP_Text)obj4).rectTransform, new Vector2(680f, 260f), new Vector2(0f, -10f));
			_hint = NativeText.Create((Transform)(object)_content, "hint", 16f, (TextAlignmentOptions)514);
			((TMP_Text)_hint).text = "click to continue";
			((Graphic)_hint).color = Hint;
			Place(((TMP_Text)_hint).rectTransform, new Vector2(400f, 36f), new Vector2(0f, -160f));
			if ((Object)(object)SplashScreen.instance != (Object)null && SplashScreen.instance.warningSound != null)
			{
				SplashScreen.instance.warningSound.Play(((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform.position : Vector3.zero, 1f, 1f, 1f, 1f);
			}
			((MonoBehaviour)this).StartCoroutine(FadeIn());
		}

		private void Update()
		{
			//IL_0016: 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)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (!_dismissed)
			{
				float unscaledTime = Time.unscaledTime;
				_content.anchoredPosition = _contentHome + new Vector2(Mathf.Sin(unscaledTime * 2.1f) * 3f, Mathf.Sin(unscaledTime * 1.7f + 1f) * 2.5f);
				((Transform)_content).localRotation = Quaternion.Euler(0f, 0f, Mathf.Sin(unscaledTime * 1.3f) * 0.5f);
				if ((Object)(object)_hint != (Object)null)
				{
					((TMP_Text)_hint).alpha = 0.45f + 0.4f * Mathf.Abs(Mathf.Sin(unscaledTime * 2.2f));
				}
				if (_dismissable && Input.GetMouseButtonDown(0))
				{
					_dismissed = true;
					((MonoBehaviour)this).StartCoroutine(FadeOut());
				}
			}
		}

		private IEnumerator FadeIn()
		{
			yield return Fade(0f, 1f, 0.35f);
			yield return (object)new WaitForSecondsRealtime(0.4f);
			_dismissable = true;
		}

		private IEnumerator FadeOut()
		{
			yield return Fade(1f, 0f, 0.5f);
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}

		private IEnumerator Fade(float from, float to, float dur)
		{
			float t = 0f;
			while (t < dur)
			{
				t += Time.unscaledDeltaTime;
				_group.alpha = Mathf.Lerp(from, to, t / dur);
				yield return null;
			}
			_group.alpha = to;
		}

		private static Image NewImage(string name, Transform parent, Color color)
		{
			//IL_0014: 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)
			Image obj = new GameObject(name, new Type[1] { typeof(RectTransform) }).AddComponent<Image>();
			((Component)obj).transform.SetParent(parent, false);
			((Graphic)obj).color = color;
			return obj;
		}

		private static void Place(RectTransform rt, Vector2 size, Vector2 pos)
		{
			//IL_0013: 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_002a: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			rt.anchorMax = val;
			rt.anchorMin = val;
			rt.pivot = new Vector2(0.5f, 0.5f);
			rt.sizeDelta = size;
			rt.anchoredPosition = pos;
		}

		private static void Stretch(RectTransform rt)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = Vector2.zero;
			rt.anchorMax = Vector2.one;
			rt.offsetMin = Vector2.zero;
			rt.offsetMax = Vector2.zero;
		}
	}
}
namespace PourDecisions.Services
{
	internal static class BarTabStats
	{
		internal sealed class Tab
		{
			public string Name = "";

			public int Total;

			public int Drunk;

			public readonly Dictionary<DrinkEvent, int> ByKind = new Dictionary<DrinkEvent, int>();
		}

		internal sealed class SaveEntry
		{
			public int Drinks;

			public string Name = "";
		}

		private sealed class TakenEntry
		{
			public int Taken;

			public long LastUtc;
		}

		private static readonly Dictionary<string, Tab> _run = new Dictionary<string, Tab>();

		private static readonly Dictionary<string, Dictionary<string, int>> _runDeathsByEnemy = new Dictionary<string, Dictionary<string, int>>();

		private static int _careerUnits;

		private static int _careerRuns;

		private static int _careerRecord;

		private static readonly List<string> _playDays = new List<string>();

		private static bool _careerLoaded;

		private static readonly Dictionary<string, Dictionary<string, SaveEntry>> _saveLedger = new Dictionary<string, Dictionary<string, SaveEntry>>();

		private static bool _savesLoaded;

		private const long TakenResetSeconds = 28800L;

		private static readonly Dictionary<string, TakenEntry> _taken = new Dictionary<string, TakenEntry>();

		private static bool _takenLoaded;

		private static string CareerPath => Path.Combine(Paths.ConfigPath, "PourDecisions", "career.txt");

		private static string SavesPath => Path.Combine(Paths.ConfigPath, "PourDecisions", "saves.txt");

		private static string TakenPath => Path.Combine(Paths.ConfigPath, "PourDecisions", "taken.txt");

		public static int CareerUnits
		{
			get
			{
				EnsureLoaded();
				return _careerUnits;
			}
		}

		public static int CareerRuns
		{
			get
			{
				EnsureLoaded();
				return _careerRuns;
			}
		}

		public static int CareerRecord
		{
			get
			{
				EnsureLoaded();
				return _careerRecord;
			}
		}

		private static string CurrentSaveFile()
		{
			if (!((Object)(object)StatsManager.instance != (Object)null))
			{
				return "";
			}
			return StatsManager.instance.saveFileCurrent ?? "";
		}

		private static long NowUtc()
		{
			return DateTimeOffset.UtcNow.ToUnixTimeSeconds();
		}

		public static void AddDrinks(string steamID, string name, DrinkEvent kind, int units)
		{
			if (!string.IsNullOrEmpty(steamID) && units > 0)
			{
				if (!_run.TryGetValue(steamID, out Tab value))
				{
					value = new Tab
					{
						Name = name
					};
					_run[steamID] = value;
				}
				if (!string.IsNullOrEmpty(name))
				{
					value.Name = name;
				}
				value.Total += units;
				value.ByKind[kind] = (value.ByKind.TryGetValue(kind, out var value2) ? value2 : 0) + units;
				if (steamID == LocalSteamID())
				{
					_careerUnits += units;
				}
			}
		}

		public static int TeamRunUnits()
		{
			int num = 0;
			foreach (Tab value in _run.Values)
			{
				num += value.Total;
			}
			return num;
		}

		public static int TabFor(string steamID)
		{
			if (!_run.TryGetValue(steamID, out Tab value))
			{
				return 0;
			}
			return value.Total;
		}

		public static void RecordDeathByEnemy(string steamID, string enemyName)
		{
			if (!string.IsNullOrEmpty(steamID) && !string.IsNullOrEmpty(enemyName))
			{
				if (!_runDeathsByEnemy.TryGetValue(steamID, out Dictionary<string, int> value))
				{
					value = new Dictionary<string, int>();
					_runDeathsByEnemy[steamID] = value;
				}
				value[enemyName] = (value.TryGetValue(enemyName, out var value2) ? value2 : 0) + 1;
			}
		}

		public static (string Name, int Count)? TopNemesis(string steamID)
		{
			if (!_runDeathsByEnemy.TryGetValue(steamID, out Dictionary<string, int> value))
			{
				return null;
			}
			string item = "";
			int num = 0;
			foreach (KeyValuePair<string, int> item2 in value)
			{
				if (item2.Value > num)
				{
					item = item2.Key;
					num = item2.Value;
				}
			}
			if (num < 2)
			{
				return null;
			}
			return (item, num);
		}

		public static int DrunkFor(string steamID)
		{
			if (!_run.TryGetValue(steamID, out Tab value))
			{
				return 0;
			}
			return value.Drunk;
		}

		public static int LocalDrunk()
		{
			return DrunkFor(LocalSteamID());
		}

		public static bool ConfirmOne()
		{
			if (!_run.TryGetValue(LocalSteamID(), out Tab value))
			{
				return false;
			}
			if (value.Drunk >= value.Total)
			{
				return false;
			}
			value.Drunk++;
			RecordDrinkTaken();
			return true;
		}

		public static int SittingDrinksTaken()
		{
			return CurrentTaken().Taken;
		}

		private static TakenEntry CurrentTaken()
		{
			EnsureTakenLoaded();
			string text = CurrentSaveFile();
			if (text.Length == 0)
			{
				text = "_local";
			}
			if (!_taken.TryGetValue(text, out TakenEntry value))
			{
				value = new TakenEntry();
				_taken[text] = value;
			}
			if (value.Taken > 0 && value.LastUtc > 0 && NowUtc() - value.LastUtc >= 28800)
			{
				value.Taken = 0;
				SaveTaken();
			}
			return value;
		}

		private static void RecordDrinkTaken()
		{
			TakenEntry takenEntry = CurrentTaken();
			takenEntry.Taken++;
			takenEntry.LastUtc = NowUtc();
			SaveTaken();
		}

		public static List<KeyValuePair<string, Tab>> Leaderboard()
		{
			return _run.OrderByDescending<KeyValuePair<string, Tab>, int>((KeyValuePair<string, Tab> kv) => kv.Value.Total).ToList();
		}

		public static void ResetRun()
		{
			int num = ((_run.Values.Count != 0) ? _run.Values.Max((Tab t) => t.Total) : 0);
			if (num > _careerRecord)
			{
				_careerRecord = num;
			}
			if (_run.Count > 0)
			{
				_careerRuns++;
				Save();
			}
			FoldRunIntoSave();
			_run.Clear();
			_runDeathsByEnemy.Clear();
		}

		private static void FoldRunIntoSave()
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer() || _run.Count == 0)
			{
				return;
			}
			string text = CurrentSaveFile();
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			EnsureSavesLoaded();
			if (!_saveLedger.TryGetValue(text, out Dictionary<string, SaveEntry> value))
			{
				value = new Dictionary<string, SaveEntry>();
				_saveLedger[text] = value;
			}
			foreach (KeyValuePair<string, Tab> item in _run)
			{
				if (!value.TryGetValue(item.Key, out var value2))
				{
					value2 = new SaveEntry();
					value[item.Key] = value2;
				}
				value2.Drinks += item.Value.Total;
				if (!string.IsNullOrEmpty(item.Value.Name))
				{
					value2.Name = item.Value.Name;
				}
			}
			SaveSaves();
		}

		public static int SaveDrinksFor(string steamID)
		{
			EnsureSavesLoaded();
			string text = CurrentSaveFile();
			if (text.Length <= 0 || !_saveLedger.TryGetValue(text, out Dictionary<string, SaveEntry> value) || !value.TryGetValue(steamID, out var value2))
			{
				return 0;
			}
			return value2.Drinks;
		}

		public static List<KeyValuePair<string, SaveEntry>> SaveLeaderboard()
		{
			EnsureSavesLoaded();
			string text = CurrentSaveFile();
			if (text.Length == 0 || !_saveLedger.TryGetValue(text, out Dictionary<string, SaveEntry> value))
			{
				return new List<KeyValuePair<string, SaveEntry>>();
			}
			return value.OrderByDescending((KeyValuePair<string, SaveEntry> kv) => kv.Value.Drinks).ToList();
		}

		public static string SerializeRun()
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (KeyValuePair<string, Tab> item in _run)
			{
				stringBuilder.Append(item.Key).Append('=').Append(item.Value.Total)
					.Append('~')
					.Append(Escape(item.Value.Name))
					.Append(';');
			}
			return stringBuilder.ToString();
		}

		public static void ApplyRunSnapshot(string data)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (KeyValuePair<string, Tab> item in _run)
			{
				dictionary[item.Key] = item.Value.Drunk;
			}
			_run.Clear();
			if (string.IsNullOrEmpty(data))
			{
				return;
			}
			string[] array = data.Split(';');
			foreach (string text in array)
			{
				if (text.Length == 0)
				{
					continue;
				}
				int num = text.IndexOf('=');
				int num2 = text.IndexOf('~');
				if (num >= 0 && num2 >= num)
				{
					string key = text.Substring(0, num);
					if (int.TryParse(text.Substring(num + 1, num2 - num - 1), out var result))
					{
						string name = Unescape(text.Substring(num2 + 1));
						_run[key] = new Tab
						{
							Name = name,
							Total = result,
							Drunk = (dictionary.TryGetValue(key, out var value) ? value : 0)
						};
					}
				}
			}
		}

		private static string Escape(string s)
		{
			return Uri.EscapeDataString(s).Replace("~", "%7E");
		}

		private static string Unescape(string s)
		{
			try
			{
				return Uri.UnescapeDataString(s);
			}
			catch (UriFormatException)
			{
				return s;
			}
		}

		public static void EnsureLoaded()
		{
			if (!_careerLoaded)
			{
				Load();
			}
		}

		public static int RecordLaunchDay()
		{
			EnsureLoaded();
			string text = DateTime.Today.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
			if (_playDays.Count == 0 || _playDays[_playDays.Count - 1] != text)
			{
				_playDays.Add(text);
				if (_playDays.Count > 60)
				{
					_playDays.RemoveRange(0, _playDays.Count - 60);
				}
				Save();
			}
			return ConsecutiveDayStreak();
		}

		public static int ConsecutiveDayStreak()
		{
			EnsureLoaded();
			if (_playDays.Count == 0)
			{
				return 0;
			}
			int num = 1;
			int num2 = _playDays.Count - 1;
			DateTime result;
			DateTime result2;
			while (num2 > 0 && DateTime.TryParse(_playDays[num2], CultureInfo.InvariantCulture, DateTimeStyles.None, out result) && DateTime.TryParse(_playDays[num2 - 1], CultureInfo.InvariantCulture, DateTimeStyles.None, out result2) && (result.Date - result2.Date).Days == 1)
			{
				num++;
				num2--;
			}
			return num;
		}

		private static void Load()
		{
			_careerLoaded = true;
			try
			{
				if (!File.Exists(CareerPath))
				{
					return;
				}
				string[] array = File.ReadAllLines(CareerPath);
				foreach (string text in array)
				{
					int num = text.IndexOf('=');
					if (num < 0)
					{
						continue;
					}
					string text2 = text.Substring(0, num);
					string text3 = text.Substring(num + 1);
					switch (text2)
					{
					case "units":
						int.TryParse(text3, out _careerUnits);
						break;
					case "runs":
						int.TryParse(text3, out _careerRuns);
						break;
					case "record":
						int.TryParse(text3, out _careerRecord);
						break;
					case "days":
						_playDays.Clear();
						_playDays.AddRange(from d in text3.Split(',')
							where d.Length > 0
							select d);
						break;
					}
				}
			}
			catch (Exception ex)
			{
				PourDecisions.Logger.LogWarning((object)("[Stats] couldn't read career file: " + ex.Message));
			}
		}

		private static void Save()
		{
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(CareerPath));
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append("units=").Append(_careerUnits).Append('\n');
				stringBuilder.Append("runs=").Append(_careerRuns).Append('\n');
				stringBuilder.Append("record=").Append(_careerRecord).Append('\n');
				stringBuilder.Append("days=").Append(string.Join(",", _playDays)).Append('\n');
				File.WriteAllText(CareerPath, stringBuilder.ToString());
			}
			catch (Exception ex)
			{
				PourDecisions.Logger.LogWarning((object)("[Stats] couldn't write career file: " + ex.Message));
			}
		}

		public static string LocalSteamID()
		{
			PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
			if (!((Object)(object)val != (Object)null))
			{
				return "";
			}
			return SemiFunc.PlayerGetSteamID(val);
		}

		private static void EnsureSavesLoaded()
		{
			if (_savesLoaded)
			{
				return;
			}
			_savesLoaded = true;
			try
			{
				if (!File.Exists(SavesPath))
				{
					return;
				}
				string[] array = File.ReadAllLines(SavesPath);
				foreach (string text in array)
				{
					int num = text.IndexOf('|');
					int num2 = text.IndexOf('=', num + 1);
					int num3 = text.IndexOf('~', num2 + 1);
					if (num < 0 || num2 < num || num3 < num2)
					{
						continue;
					}
					string key = text.Substring(0, num);
					string key2 = text.Substring(num + 1, num2 - num - 1);
					if (int.TryParse(text.Substring(num2 + 1, num3 - num2 - 1), out var result))
					{
						string name = Unescape(text.Substring(num3 + 1));
						if (!_saveLedger.TryGetValue(key, out Dictionary<string, SaveEntry> value))
						{
							value = new Dictionary<string, SaveEntry>();
							_saveLedger[key] = value;
						}
						value[key2] = new SaveEntry
						{
							Drinks = result,
							Name = name
						};
					}
				}
			}
			catch (Exception ex)
			{
				PourDecisions.Logger.LogWarning((object)("[Stats] couldn't read saves file: " + ex.Message));
			}
		}

		private static void SaveSaves()
		{
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(SavesPath));
				StringBuilder stringBuilder = new StringBuilder();
				foreach (KeyValuePair<string, Dictionary<string, SaveEntry>> item in _saveLedger)
				{
					foreach (KeyValuePair<string, SaveEntry> item2 in item.Value)
					{
						stringBuilder.Append(item.Key).Append('|').Append(item2.Key)
							.Append('=')
							.Append(item2.Value.Drinks)
							.Append('~')
							.Append(Escape(item2.Value.Name))
							.Append('\n');
					}
				}
				File.WriteAllText(SavesPath, stringBuilder.ToString());
			}
			catch (Exception ex)
			{
				PourDecisions.Logger.LogWarning((object)("[Stats] couldn't write saves file: " + ex.Message));
			}
		}

		private static void EnsureTakenLoaded()
		{
			if (_takenLoaded)
			{
				return;
			}
			_takenLoaded = true;
			try
			{
				if (!File.Exists(TakenPath))
				{
					return;
				}
				string[] array = File.ReadAllLines(TakenPath);
				foreach (string text in array)
				{
					int num = text.LastIndexOf('|');
					if (num > 0)
					{
						int num2 = text.LastIndexOf('|', num - 1);
						if (num2 >= 0)
						{
							string key = text.Substring(0, num2);
							TakenEntry takenEntry = new TakenEntry();
							int.TryParse(text.Substring(num2 + 1, num - num2 - 1), out takenEntry.Taken);
							long.TryParse(text.Substring(num + 1), out takenEntry.LastUtc);
							_taken[key] = takenEntry;
						}
					}
				}
			}
			catch (Exception ex)
			{
				PourDecisions.Logger.LogWarning((object)("[Stats] couldn't read taken file: " + ex.Message));
			}
		}

		private static void SaveTaken()
		{
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(TakenPath));
				StringBuilder stringBuilder = new StringBuilder();
				foreach (KeyValuePair<string, TakenEntry> item in _taken)
				{
					stringBuilder.Append(item.Key).Append('|').Append(item.Value.Taken)
						.Append('|')
						.Append(item.Value.LastUtc)
						.Append('\n');
				}
				File.WriteAllText(TakenPath, stringBuilder.ToString());
			}
			catch (Exception ex)
			{
				PourDecisions.Logger.LogWarning((object)("[Stats] couldn't write taken file: " + ex.Message));
			}
		}
	}
	internal static class CamFx
	{
		private static bool On => PluginConfig.CameraEffects.Value;

		private static CameraGlitch? Live()
		{
			CameraGlitch instance = CameraGlitch.Instance;
			if (!((Object)(object)instance != (Object)null))
			{
				return null;
			}
			return instance;
		}

		public static void Confirm()
		{
			if (On)
			{
				CameraGlitch? obj = Live();
				if (obj != null)
				{
					obj.PlayUpgrade();
				}
			}
		}

		public static void Tiny()
		{
			if (On)
			{
				CameraGlitch? obj = Live();
				if (obj != null)
				{
					obj.PlayTiny();
				}
			}
		}

		public static void Short()
		{
			if (On)
			{
				CameraGlitch? obj = Live();
				if (obj != null)
				{
					obj.PlayShort();
				}
			}
		}

		public static void Long()
		{
			if (On)
			{
				CameraGlitch? obj = Live();
				if (obj != null)
				{
					obj.PlayLong();
				}
			}
		}
	}
	internal static class ChatBoxBridge
	{
		private const string NameHex = "e0a040";

		private static bool _resolved;

		private static PropertyInfo? _instanceProp;

		private static MethodInfo? _addSystem;

		internal static bool Available
		{
			get
			{
				Resolve();
				if (_instanceProp != null)
				{
					return _addSystem != null;
				}
				return false;
			}
		}

		private static void Resolve()
		{
			if (_resolved)
			{
				return;
			}
			_resolved = true;
			try
			{
				Type type = AccessTools.TypeByName("ChatBox.NonPatch.NonPatches");
				if (!(type == null))
				{
					_instanceProp = AccessTools.Property(type, "Instance");
					_addSystem = AccessTools.Method(type, "AddChatMessageSystem", new Type[3]
					{
						typeof(string),
						typeof(string),
						typeof(string)
					}, (Type[])null);
				}
			}
			catch (Exception ex)
			{
				PourDecisions.LogVerbose("[ChatBox] resolve failed: " + ex.Message);
			}
		}

		internal static bool Post(string name, string line)
		{
			if (!Available)
			{
				return false;
			}
			try
			{
				object value = _instanceProp.GetValue(null);
				if (value == null)
				{
					return false;
				}
				_addSystem.Invoke(value, new object[3] { line, "e0a040", name });
				return true;
			}
			catch (Exception ex)
			{
				PourDecisions.LogVerbose("[ChatBox] post failed: " + ex.Message);
				return false;
			}
		}
	}
	internal static class Drunkify
	{
		private struct Rng
		{
			private uint _s;

			public Rng(uint seed)
			{
				_s = ((seed == 0) ? 2166136261u : seed);
			}

			public uint Next()
			{
				_s ^= _s << 13;
				_s ^= _s >> 17;
				_s ^= _s << 5;
				return _s;
			}

			public float Float()
			{
				return (float)(Next() & 0xFFFFFF) / 16777216f;
			}

			public bool Chance(float p)
			{
				return Float() < p;
			}
		}

		private static uint Hash(string s, int runSeed, int proofBucket)
		{
			uint num = 2166136261u;
			foreach (char c in s)
			{
				num ^= c;
				num *= 16777619;
			}
			num ^= (uint)runSeed;
			num *= 16777619;
			num ^= (uint)proofBucket;
			return num * 16777619;
		}

		public static string Apply(string text, float proof, int runSeed)
		{
			if (string.IsNullOrEmpty(text) || proof <= 0f)
			{
				return text;
			}
			if (proof > 1f)
			{
				proof = 1f;
			}
			int proofBucket = (int)(proof * 10f);
			Rng rng = new Rng(Hash(text, runSeed, proofBucket));
			StringBuilder stringBuilder = new StringBuilder(text.Length + 16);
			int i = 0;
			while (i < text.Length)
			{
				char c = text[i];
				char c2 = c switch
				{
					'{' => '}', 
					'[' => ']', 
					'<' => '>', 
					_ => '\0', 
				};
				if (c2 != 0)
				{
					int num = text.IndexOf(c2, i);
					if (num < 0)
					{
						num = text.Length - 1;
					}
					stringBuilder.Append(text, i, num - i + 1);
					i = num + 1;
				}
				else if (char.IsLetter(c))
				{
					int start = i;
					for (; i < text.Length && char.IsLetter(text[i]); i++)
					{
					}
					SlurWord(text, start, i, proof, ref rng, stringBuilder);
					if (proof > 0.3f && rng.Chance(0.06f * proof))
					{
						stringBuilder.Append(" *hic*");
					}
				}
				else
				{
					stringBuilder.Append(c);
					i++;
				}
			}
			return stringBuilder.ToString();
		}

		private static void SlurWord(string src, int start, int end, float proof, ref Rng rng, StringBuilder sb)
		{
			int num = end - start;
			for (int i = start; i < end; i++)
			{
				char c = src[i];
				char c2 = char.ToLowerInvariant(c);
				if (proof > 0.45f && c2 == 's' && rng.Chance(0.5f * proof))
				{
					sb.Append(c);
					sb.Append(char.IsUpper(c) ? 'H' : 'h');
					continue;
				}
				sb.Append(c);
				if (proof > 0.15f && i > start && c2 != 'a' && c2 != 'e' && c2 != 'i' && c2 != 'o' && c2 != 'u' && rng.Chance(0.1f * proof))
				{
					sb.Append(c);
				}
				if ((c2 == 'o' || c2 == 'a' || c2 == 'e') && rng.Chance(0.18f * proof))
				{
					int num2 = 1 + ((proof > 0.6f) ? 1 : 0);
					for (int j = 0; j < num2; j++)
					{
						sb.Append(c);
					}
				}
			}
			if (num > 3 && proof > 0.7f && rng.Chance(0.08f * proof))
			{
				sb.Append("...");
			}
		}
	}
	internal static class DrunkLines
	{
		internal enum Where
		{
			Level,
			Shop,
			Truck,
			Arena
		}

		internal enum Theme
		{
			None,
			Manor,
			Wizard,
			Arctic
		}

		internal enum ReactKind
		{
			Death,
			Down,
			HeadmanGrab,
			HiddenGrab,
			Smash,
			Scared,
			Cheer,
			SpewerFace,
			SpewerPuke,
			Shadow,
			ArenaDeath,
			LastStanding,
			CrownWin,
			RaceWin,
			RaceOut,
			Betrayal
		}

		private static (string[] Frags, string[] Pool)[]? _rows;

		internal static readonly string[] Tipsy = new string[11]
		{
			"ok ok i can totally still carry this", "im fine im fine i'm totally fine", "watch this. watch. ok nvm", "i actually kinda love this job", "that valuable was looking at me weird", "the quota is more of a suggestion right", "do NOT tell the taxman how much we dropped", "i found a coin. we're rich. we're saved.", "we're so good at this why do we keep dying", "which way's the cart. don't answer. i got it. i don't got it.",
			"i'm pacing myself. this is me pacing. *trips*"
		};

		internal static readonly string[] Drunk = new string[14]
		{
			"i luv u guys so much *hic*", "wait whose head is that", "the taxman GETS me yknow", "who keeps moving the extraction point", "i'm not lost the MAP is wrong", "this monster is actually kinda chill", "extraction? in THIS economy?", "the company prints a new me if i die so. whatever", "i kissed the valuable for luck. judge me.", "i put a coin in the wishing... it was just a hole. it ate my coin.",
			"who scheduled overtime in the AFTERLIFE wing", "i high-fived a valuable and it FELL and i'm in TROUBLE", "i'm sweating money. is that normal. is the quota OSMOSIS.", "i love this crew even when you LEAVE me to die"
		};

		internal static readonly string[] Wasted = new string[14]
		{
			"guysh the floor is MOVIN", "shhh the monster can hear ur FACE", "im not drunk the gravity is drunk", "i AM the valuable now actually. sell me.", "we're just printed meat for the quota and i'm at PEACE", "the walls are breathin and i respect it", "i'm gonna marry the cart. you're all invited. bring the haul.", "physics is a LAW and i'm a CRIMINAL", "i tried to extract MYSELF and the machine said no. RUDE.", "we left a guy. did we leave a guy? i'm the guy. i left me.",
			"the quota whispered my NAME. it knows my PRINT NUMBER.", "i licked the floor to taste the QUOTA. it tastes like CARPET.", "the extraction machine is BEAUTIFUL and it will NEVER love me back", "we're doing GREAT. are we doing great? define great. define WE."
		};

		internal static readonly string[] Blackout = new string[13]
		{
			"WHO TURNED OFF GRAVITY", "i am become haul, destroyer of carts", "the monster and i are dating now don't extract without us", "the company printed 47 of me and ALL were cowards", "i have achieved liftoff (i fell in the lava)", "I ATE THE COIN. THE COIN IS ME NOW. I AM CURRENCY.", "GRAVITY FILED A RESTRAINING ORDER AGAINST ME", "we are inside the truck inside the level inside the QUOTA it's QUOTAS ALL THE WAY DOWN", "the cart has a NAME now and it's greg. everything is greg.", "the void called. it's understaffed. i SAID I'D HELP.",
			"tell the taxman i'm not AFRAID of him (i am, send the surplus)", "i'm not blacked out i'm DOWNLOADING the quota directly", "i'd sell my SOUL but the taxman already OWNS it (standard contract)"
		};

		internal static readonly string[] DeadTipsy = new string[14]
		{
			"ok being a head is fine actually", "can someone PICK ME UP", "death is just a coffee break", "carry me to the healer u cowards", "i can see your ankles from down here", "well THAT happened", "i regret nothing. except the cart.", "ten outta ten would die again honestly", "rolling toward extraction. very slowly. very dignified.", "i'm not a corpse i'm a CARRY-ON",
			"guys i found a coin down here come get it (i can't)", "the floor is so much closer now and i have NOTES", "anyone gonna grab me or is this a BIT", "i died doing what i love: dropping the valuable"
		};

		internal static readonly string[] DeadDrunk = new string[14]
		{
			"i'm a head now and honestly? freeing.", "the company will print another me. say hi.", "respawn me i have OPINIONS", "from down here u all look like valuables", "being dead is great no quota down here", "i think i'm haunting the cart now", "the taxman can't tax a HEAD checkmate", "i rolled under the couch and found the OTHER dead guy. we talk now.", "i'm a valuable now technically. extract ME. i'm worth at least a coin.", "kick my head toward the exit i wanna CONTRIBUTE",
			"i can hear the cart and i MISS her", "death is just unpaid overtime and i'm STILL clocked in", "tell my cart i love her", "i'm not dead i'm load-bearing now"
		};

		internal static readonly string[] DeadWasted = new string[14]
		{
			"i am one with the void and the void is UNDERSTAFFED", "the taxman is in the walls. in ME.", "death tastes like overtime and i WANT MORE", "i can hear the quota breathing it's so CLOSE", "pick me up pick ME UP i have SECRETS", "i'm not dead i'm just between print runs", "the cart will avenge me. the cart KNOWS.", "i'm rolling toward the light it's the extraction machine it's GORGEOUS", "from the floor i can confirm: this place has TERRIBLE foundations", "tell the cart i'll be back for the recipe",
			"i'm a head AND a hazard AND a HERO, scoop me UP", "i've been every corpse in this room and we are LEGION", "the void has a SUGGESTION BOX and i FILLED it", "my head is a VALUABLE now please add me to the haul"
		};

		internal static readonly string[] DeadBlackout = new string[16]
		{
			"I AM THE HAUL NOW. EXTRACT ME. FREE ME.", "i've died 47 times and learned NOTHING and i'm THRILLED", "the void is the truck but FOREVER and i LIVE here", "tell the MANAGER i quit. i'm a head. i QUIT.", "i am every dead Semibot and we are LEGION", "the surplus took me. drink for the surplus.", "reprint me with LEGS this time you cowards", "i see the quota's true form and it is ANGRY", "death is a LATERAL career move and i'm THRIVING down here", "i have become friends with the floor. the floor is HONEST. the floor never LIES.",
			"i can see EVERY extraction point at ONCE and NONE of them are real", "i AM the level now. you're all walking around INSIDE me. mind the cart.", "scoop my head into the cart i wanna be SOLD i wanna MATTER", "the company can't FIRE a head that's already DEAD checkmate corporate", "i'm filing for HEAD rights with the taxman's UNION (it's me, i'm the union)", "MY HEAD ROLLS WHERE IT WANTS NOW i answer to NO quota"
		};

		internal static readonly string[] ShopBuzzed = new string[25]
		{
			"ooh do we have enough for the big one", "the shopkeeper is staring. say something cool.", "everything in here is so OVERPRICED honestly", "i want the gun. i NEED the gun.", "tax included?? on top of the QUOTA??", "the cart's lookin empty let's FIX that", "can we afford it. CAN we. i can't do math rn", "i'm just gonna hold this item real quick", "do we NEED healing or do we need the cool grenade. think carefully.", "i'm buying a drone and naming it after the cart",
			"split the bill? we don't have a BILL we have a QUOTA", "the shopkeeper has seen things. you can tell.", "put it back. put it BACK. ok fine ONE.", "i could buy stamina or i could buy VIBES", "everything's on sale if you just take it real fast", "we came in for ONE item. we are leaving with SEVEN.", "is the shopkeeper judging my cart. he's judging my cart.", "the prices went UP since last shop i'm WATCHING you", "we should pool our money. emotionally. i'm broke.", "i'm gonna read every label out loud. EVERY one.",
			"this drone better be worth it or i'm RETURNING it (i can't)", "guys the healing pack is RIGHT THERE we're not INVINCIBLE", "ooh ooh the upgrade station. i love a little UPGRADE.", "do we trust this shopkeeper. we don't KNOW him. we love him.", "i'm window shopping. there's no windows. i'm just shopping."
		};

		internal static readonly string[] ShopSmashed = new string[28]
		{
			"i'm gonna lick every item to see which is real", "SHOPKEEPER i love u marry me", "we should just TAKE the items he can't stop us", "i put it in the cart with my MIND", "the prices are TALKING to me and they're RUDE", "this whole shop is a tax write off baby", "i ate a healing item is that. is that bad", "buy the gun buy the GUN buy the gun", "i'm gonna haggle. with my FISTS. (i mean talking. probably.)", "every item in here is my CHILD now and we are LEAVING together",
			"the drone is judging my purchases and it's RIGHT to", "i offered the shopkeeper a coin and my UNDYING loyalty", "the cart is full. the cart is NEVER full. MORE.", "i'm not stealing i'm RELOCATING inventory", "i can hear the grenades and they want to come HOME with me", "this is the BEST store i have EVER been thrown out of", "i'm gonna buy the WHOLE shelf. with WHAT. with DREAMS.", "the shopkeeper blinked first i WIN i get a discount (i don't)", "i licked the cart. for SECURITY. it's MINE now. branded.", "the upgrade machine PROMISED me things. i BELIEVE the machine.",
			"i'm gonna ride the drone like a little HORSE", "money is FAKE but this grenade is REAL and it's coming with ME", "we don't need a PLAN we need EVERYTHING on this SHELF", "i told the shopkeeper my whole life story. all 47 print runs.", "the healing pack and i are SOULMATES don't let me leave without it", "i'm gonna negotiate. by crying. at the shopkeeper. softly.", "put the gun in the cart. the GUN. in the CART. trust the process.", "i smelled the item. it smells like PROFIT. or feet. profit-feet."
		};

		internal static readonly string[] ShopDead = new string[12]
		{
			"carry my head to the shop i wanna buy things", "even dead i have better credit than the taxman", "put my head in the cart with the loot please", "i can't shop i'm a HEAD but i have OPINIONS on the gun", "buy something nice for my funeral. and the cart.", "from down here the shop prices look even WORSE", "the shopkeeper won't sell to a head. discrimination.", "spend my share. i'm dead. treat yourselves. get the gun.", "roll me past the shelf i wanna WINDOW SHOP with my one good eye", "tell the shopkeeper a head's money is STILL money",
			"buy the healing pack. for ME. it's too LATE for me but STILL.", "i'd point at what i want but i have NO ARMS. it's the drone. buy the drone."
		};

		internal static readonly string[] TruckBuzzed = new string[20]
		{
			"are we there yet. are we. are we there yet.", "good run guys. good RUN. i'm a lil emotional.", "let's count the haul again i love counting the haul", "the road goes on forever and so does the quota", "anyone else just vibing to the engine hum", "i call shotgun for the next level", "we did NOT lose that much value. did we.", "i love this truck. genuinely. don't make it weird.", "who's driving. nobody? incredible. love that for us.", "the healer pod is basically a hot tub if you squint",
			"let's NOT tell the taxman about the vase. the vase stays between us.", "i'm gonna nap. wake me when we're rich.", "next level's the one. i can FEEL the quota shrinking.", "group photo by the cart? for morale?", "the engine sounds happy today. or that's just me.", "i miss the last level already. weird. i almost died there.", "we're a TEAM. a sweaty, indebted, beautiful team.", "i wanna decorate the truck. little curtains. quota permitting.", "how much further. don't tell me. tell me. don't.", "the road is bumpy or i'm just FULL of feelings"
		};

		internal static readonly string[] TruckSmashed = new string[20]
		{
			"the truck is a SUBMARINE now buckle up", "i'm gonna drive. give me the wheel. GIVE it.", "i love this truck more than my last 12 print bodies", "where are we GOING. metaphysically. where.", "the haul is talking again it wants to go HOME", "i'm gonna nap in the cart wake me at the quota", "next level's gonna be EASY i can FEEL it", "the truck healer is my therapist now", "i renamed the truck. her name is Brenda. respect Brenda.", "let's do donuts. the truck does donuts right. RIGHT?",
			"i counted the haul nine times and got nine answers", "the road is a SUGGESTION and i REJECT it", "i'm gonna lie down in the road. the truck'll catch up. trust.", "Brenda and i have plans the taxman wouldn't UNDERSTAND", "if i press every button at once we go to SPACE probably", "the engine and i are harmonizing don't RUIN this", "i wanna marry the truck AND the cart. it's legal in the void.", "the road told me a SECRET. it's that there's MORE road.", "let me hold the haul. just HOLD it. i'll give it back. probably.", "the healer pod and i had a MOMENT and now i'm CRYING happy tears"
		};

		internal static readonly string[] TruckDead = new string[10] { "just toss my head in the back with the haul", "dead in the truck. peak. ten outta ten.", "revive me before the next level i wanna HELP", "i can hear the truck healer calling me home", "leave me here i AM cargo now", "prop my head on the dash i wanna see the road", "buckle my head in. safety first. i'm already dead but STILL.", "i'm the truck's emotional support corpse now", "drive smooth my head's gonna ROLL off the bench", "wake my head when we hit the next level i'll do my BEST" };

		internal static readonly string[] ManorLines = new string[14]
		{
			"fancy house. who hurt the people who lived here.", "this manor has WAY too many hallways and i've gotten lost in ALL of them", "the chandelier is worth more than my entire print run", "i found the good silverware. it's in the cart. it's MINE now.", "every painting in here is staring and i HATE it", "this carpet is older than the company and TWICE as cursed", "the manor creaks like it owes someone money", "rich people had so much SPACE and used it for HAUNTING", "the grand staircase tried to KILL me. respect.", "i'm gonna live here after we hit quota. just me and the ghosts.",
			"the manor has a ballroom and i WILL be dancing before i die", "who dusts this place. nobody. that's the SCARY part.", "the wallpaper is judging my whole career", "this house has a LIBRARY and none of the books explain the QUOTA"
		};

		internal static readonly string[] WizardLines = new string[14]
		{
			"this whole place smells like ozone and bad DECISIONS", "everything here GLOWS and i think i'm absorbing it through my FACE", "the candles won't blow out and they're JUDGING me", "magic is real and it's COMING OUT OF MY QUOTA", "i waved my arms and yelled a spell. nothing. i yelled LOUDER.", "the wizard left in a hurry. the kettle's still ON. eternally.", "there's runes on the floor and i think i STEPPED in a curse", "everything in here hums at a frequency that says PAY RENT", "i'm pretty sure that potion is sentient and DISAPPOINTED in me", "the air SPARKLES and so do i (i'm sweating glitter, send help)",
			"i feel POWERFUL in here. and also like i might EXPLODE.", "this place has VIBES and the vibes are LICENSED NECROMANCY", "the floor tried to teleport me. i think. i'm somewhere ELSE now.", "wizards had a SAW for DECOR and a CAULDRON for SOUP. priorities."
		};

		internal static readonly string[] ArcticLines = new string[14]
		{
			"it's so COLD my print body is BUFFERING", "the ice is cracking and so is my COMMITMENT to this job", "i can see my breath and ALSO the quota slipping away", "everything's frozen even my MISTAKES are preserved out here", "i licked a frozen railing for SCIENCE. update: stuck.", "the cold is honest. the cold doesn't TAX you.", "i slipped on the ice and slid into extraction. BEST run ever.", "my hands went numb so now i grab valuables with VIBES", "is that a monster or just a really committed icicle", "frostbite is just the company's way of saying slow down",
			"i built a snow Semibot. his name is greg. he's the smart one now.", "the wind out here is SAYING things and none of it is nice", "i can't feel my face but i CAN feel the quota and that's WORSE", "we drove something off a ledge out here. it's fine. it's GONE. we're fine."
		};

		internal static readonly string[] EnemyLines = new string[19]
		{
			"is that a {0}? it's a {0}. act natural.", "the {0} hasn't seen us yet. nobody breathe. nobody. {0}.", "i'm gonna wave at the {0}. for diplomacy.", "ok which one of you scheduled the {0}", "the {0} is doing its little walk again", "i named the {0}. its name is greg. don't get attached.", "everyone be cool the {0} can smell fear and COINS", "the {0} is between me and the cart and i WANT the cart", "do NOT make eye contact with the {0}", "the {0} looked at me. we have a CONNECTION now.",
			"tell the {0} it's doing a great job and then RUN", "i think the {0} is just lonely honestly", "who let the {0} in here. this is a WORKPLACE.", "the {0} is faster than it looks. i'm slower than i look. bad combo.", "hiding from the {0} in this locker. living my best life.", "the {0} walked past and i HID behind a vase. iconic.", "psst. the {0}. it's RIGHT there. i'm not panicking YOU'RE panicking.", "the {0} sighed. even the {0} is tired of the quota.", "i'd fight the {0} but my arms are for CARRYING not WINNING"
		};

		internal static readonly string[] EnemyLinesWild = new string[18]
		{
			"i'm gonna ASK the {0} out. worst it can do is kill me. again.", "the {0} and i are PARTNERS now we split the haul 50/50", "i CHALLENGE the {0} to a duel of WITS (i have none)", "the {0} is my PRINTED SOULMATE and the company KNEW", "let me PET the {0} just ONCE before it ends me", "the {0} whispered the quota number to me and it's WRONG and i BELIEVE it", "i'm not running from the {0} we're doing PARKOUR together", "the {0} ate my arm and said thank you and i said no problem KING", "i taught the {0} my name and now it WON'T stop saying it", "the {0} is the only one who GETS me down here",
			"i'd die for the {0}. i WILL. probably in like ten seconds.", "the {0} and the taxman are in CAHOOTS i've cracked the CASE", "i licked the {0}. don't ask. it tasted like OVERTIME.", "the {0} is wearing my LAST print body as a HAT and honestly? slay.", "i'm gonna RIDE the {0} into extraction it's a SHORTCUT probably", "the {0} blinked and i fell in LOVE and also down the STAIRS", "the {0} is just MISUNDERSTOOD it only wants the QUOTA like the rest of us", "i invited the {0} to the truck. don't be WEIRD about it."
		};

		internal static readonly string[] ValuableLines = new string[18]
		{
			"the {0} is worth more than my whole CAREER", "i'm gonna marry the {0}. you're all invited.", "DO NOT drop the {0}. look at me. the {0}.", "the {0} blinked. i swear the {0} blinked.", "i kissed the {0} for luck. it's tradition now.", "the {0} is so HEAVY who designed this who HURT them", "load the {0} FIRST it's basically the whole quota", "i found a {0}!! we're rich!! we're saved!! ...it's one {0}.", "the {0} is mine. i called it. i licked it. it's mine.", "careful with the {0} that thing's our RENT",
			"the {0} is staring at me from the cart and i feel WATCHED", "i whispered the quota to the {0} and it UNDERSTOOD", "we are NOT leaving without the {0}. i'll carry it with my TEETH.", "the {0} fits in the cart if we BELIEVE", "guys the {0}. the {0}. are you SEEING the {0}.", "i tripped and almost dropped the {0} and aged ten PRINT YEARS", "the {0} and i have BEEN through it this level", "hands off the {0} that's MY emotional support valuable"
		};

		internal static readonly string[] ValuableLinesWild = new string[14]
		{
			"i AM the {0} now. extract ME. i'm worth at LEAST a {0}.", "the {0} spoke to me. it wants to go HOME. i AM its home.", "i ate part of the {0} to see if it was real. inconclusive. ate more.", "the {0} and i are ELOPING the cart can't hold us", "i traded my last print body for the {0} and i'd do it AGAIN", "the {0} knows the taxman's WEAKNESS and it told ME", "i'm gonna THROW the {0} just to see. for science. for the QUOTA.", "the {0} is GLOWING or i'm dying. either way. gorgeous.", "stack the carts. STACK them. the {0} demands a THRONE.", "i named the {0} after my first print body. rest easy little {0}.",
			"the {0} is the only thing in this level that has ever LOVED me", "i'm gonna whisper the quota to the {0} until it CONFESSES", "the {0} winked. the {0} WINKED. we have an UNDERSTANDING now.", "let me hold the {0} one more time before the taxman TAKES her"
		};

		internal static readonly string[] Replies = new string[23]
		{
			"wtf {0}", "{0} please", "{0} are you ok", "someone cut {0} off", "ignore {0} they're gone", "we are NOT carrying {0} again", "{0} put it DOWN", "{0}... buddy...", "take {0}'s comms away", "{0} that's the third time tonight",
			"respectfully {0} what", "{0} we are AT WORK", "not {0} again oh my god", "{0} that is the EXTRACTION machine do not lick it", "nobody give {0} the gun", "{0} the monster can HEAR you", "i'm telling the taxman on {0}", "who let {0} drink before the quota", "{0} that's a real monster not a costume", "{0} we have been over this",
			"someone revive {0}'s brain cell", "{0} drink some WATER for the love of", "{0} is having a moment. give them space."
		};

		internal static readonly string[] CartReplyLines = new string[13]
		{
			"{0} get OFF the cart", "this is why {0} doesn't get the cart", "{0} the cart is for LOOT not for SITTING", "someone get {0} off the cart before it ROLLS", "{0} you are not cargo get OFF", "{0} the cart cannot hold your DRAMA and the haul", "we are not pushing {0} to extraction in the cart again", "{0} the cart has a weight limit and it's your EGO", "off the cart {0}, it's not a THRONE", "{0} climbed in the cart with the loot AGAIN",
			"{0} the cart is not a BED we are WORKING", "get {0} out of the cart before we sell {0} by accident (tempting)", "{0} stop STEERING with your BODY"
		};

		internal static readonly string[] CartLines = new string[16]
		{
			"the cart's right here and i LOVE her", "i'm putting EVERYTHING in this cart watch me", "who's pushing the cart. not me. i'm SUPERVISING.", "the cart squeaks and i think it's TALKING to me", "i named the cart. again. she's Brenda again.", "load the cart load the CART we're so close to quota", "i could fit in this cart. should i. i'm gonna.", "the cart is FULL and my heart is FULLER", "do NOT let the cart roll off the ledge again", "i kissed the cart for luck, standard procedure",
			"the cart and i are going STEADY, mind your business", "i'll guard the cart with my LIFE (i won't)", "the cart's wobbling. no wait that's me. the cart's fine.", "we lose the cart we lose EVERYTHING, protect Brenda", "one more valuable and the cart ASCENDS", "i'm gonna ride the cart down the ramp. for SCIENCE. for QUOTA."
		};

		internal static readonly string[] ModShrinker = new string[8] { "someone SHRINK this thing it's never gonna fit in the cart", "i shrunk my own hand by accident now i grab SMALL", "the shrinker gun is the only reason this run isn't OVER", "shrink the monster. SHRINK it. make it a little GUY.", "i shrunk a valuable so small i LOST it. it's in my SHOE now probably.", "make me TINY i wanna nap in the cart", "i shrunk the WHOLE haul into my pocket we're geniuses", "shrunk a teammate and now he's my little GUY. say hi." };

		internal static readonly string[] ModShrinkerSell = new string[4] { "we should grab the shrinker gun mod for runs like this", "imagine if we could just SHRINK this to fit. someday.", "you ever think we need a shrink ray? ...just me? ok.", "this'd be way easier if we could resize stuff. anyway." };

		internal static readonly string[] ModSharedUpgrades = new string[4] { "good thing upgrades are SHARED or i'd have NOTHING", "love that we all get the upgrades. we RISE together (into debt).", "i bought an upgrade and you ALL got a little something. that's LOVE.", "shared upgrades baby what's MINE is OURS and what's OURS is the TAXMAN'S" };

		internal static readonly string[] ModFidelity = new string[4] { "the game looks SO good right now or maybe that's the drinks", "everything's so CRISP i could cry. or that's the quota.", "real ultrawide and i STILL can't find the extraction", "i cranked the FOV so high i can see my OWN bad decisions" };

		internal static readonly string[] ModBattleScars = new string[4] { "i can read your HP by how BUSTED you look and you look BUSTED", "i'm covered in cracks and bandages, that's just my HEALTH BAR now", "lost a limb at low HP very FASHION very ON THE NOSE", "you're one dent away from a HEAD, buddy, heal UP" };

		internal static readonly string[] ModRobeCalling = new string[4] { "the arctic phone is RINGING again who keeps CALLING", "the phone's got a new RINGTONE and honestly? it SLAPS", "answer the phone i think it's for the ROBE", "new phone, who dis. it's the robe. it's always the robe." };

		internal static readonly string[] ModCartFix = new string[4] { "thank GOD the cart turns now i was losing my MIND", "the cart used to drive like a FRIDGE now she GLIDES", "loaded cart still handles? we are SPOILED", "i can actually STEER the full cart now we're UNSTOPPABLE" };

		internal static readonly string[] ModAimFix = new string[3] { "my aim finally feels RIGHT at high fps", "the camera stopped feeling like SOUP and i can AIM now", "i can actually flick now and i STILL miss the orb" };

		internal static readonly string[] ModMoreShopItems = new string[4] { "the shop's got so much STUFF now i can't even CHOOSE", "so many shop items i blacked out from sheer CHOICE", "more shop items just means more ways to go BROKE i LOVE it", "the shelves are FULL and so is my anxiety" };

		internal static readonly string[] ModMoreUpgrades = new string[4] { "so many upgrades i'm basically a SUPERHERO now (still broke)", "i bought EVERY upgrade and i STILL fell in a hole", "more upgrades means i can carry the WHOLE level on my BACK", "i'm so upgraded the taxman sends a PERSONAL invoice" };

		internal static readonly string[] ModMoreHead = new string[4] { "check out my HEAD cosmetic i look INCREDIBLE", "i spent the whole budget on a HAT. worth it.", "my head's drippin and the quota's CRYING", "i can't do the quota but i CAN accessorize" };

		internal static readonly string[] ModHeadHopper = new string[5] { "i'm DEAD but i can HOP so really who's winning here", "watch me hop my severed head into EXTRACTION", "death? i just BOUNCE now. catch me.", "i'm hopping my head toward the cart gimme a SEC", "being a head is FREEING when you can do little JUMPS" };

		internal static readonly string[] ModLCValuables = new string[4] { "is that a Lethal Company scrap in MY repo level", "we're CROSSING OVER with the OTHER company job", "this valuable's from Lethal Company it KNOWS too much", "two companies, one quota, infinite debt. beautiful." };

		internal static readonly string[] ModMimic = new string[4] { "the monster just talked in MY voice i'm UNWELL", "something out there sounds EXACTLY like greg. it is NOT greg.", "if you hear me but my mouth isn't MOVING. run.", "the monster MIMICKED me and honestly did a better job" };

		internal static readonly string[] ModMorePlayers = new string[4] { "there's like NINE of us in here it's CHAOS i love it", "more Semibots more PROBLEMS more DRINKS", "we raised the player cap and LOWERED our standards", "a whole CROWD and we STILL can't make quota" };

		internal static readonly string[] ModExtractConfirm = new string[4] { "press the extraction confirm button BEFORE i change my mind", "i'll hit the confirm myself don't you TOUCH it", "confirm the extraction i'm gonna CRY happy tears", "one button between us and the truck. press it. PRESS it." };

		internal static readonly string[] ModNoSpawnLimit = new string[4] { "why are there so MANY items this is HEAVEN", "no spawn limit means INFINITE little treasures i'm UNWELL", "the floor is COVERED in stuff and i want ALL of it", "so much loot the cart filed a COMPLAINT" };

		internal static readonly string[] ModAdminMenu = new string[4] { "do NOT tell the admin i'm drunk on the job", "if that admin menu opens we RIOT", "i saw the admin menu ONCE and i wasn't the same after", "the admin can just SPAWN money and YET we suffer" };

		internal static readonly string[] ModLobbyImprovements = new string[3] { "the lobby's so SMOOTH now bless whoever did that", "lobby improvements? in THIS economy? we're SPOILED", "joining up is actually PLEASANT now. suspicious." };

		internal static readonly string[] ModLevelScaling = new string[4] { "who CRANKED the difficulty i'm gonna have WORDS", "level scaling's got the monsters BUFF this run, good LUCK", "challenge mode? i can barely do REGULAR mode", "we asked for harder and the game said 'bet'" };

		internal static readonly string[] ModTimeAttack = new string[4] { "the CLOCK is ticking and so is my ANXIETY", "we're on a TIMER move move MOVE (i'm gonna nap)", "time attack means i panic-grab EVERYTHING", "go fast for bonus money? i go fast for FEAR." };

		internal static readonly string[] ModDiscordRP = new string[4] { "my discord is telling EVERYONE how bad this run is going", "the rich presence says i'm 'extracting'. it's LYING.", "everyone on discord can see i've died FOUR times. cool. cool cool cool.", "live stats means there are WITNESSES to my crimes" };

		internal static readonly string[] ModResell = new string[4] { "wait we can RESELL upgrades? i'm returning my LEGS for cash", "refunding my upgrades to afford ONE more drink", "sold my upgrade back, the taxman LOVES a returner", "i can un-buy my mistakes now. emotionally, i cannot." };

		internal static readonly string[] ModShopDiscounts = new string[4] { "DISCOUNT day baby everything must GO into my cart", "rolled a discount i'm basically a FINANCIAL genius", "the shop's on SALE and the taxman is FURIOUS", "i'd buy it full price but a DEAL is a deal is a DUTY" };

		internal static readonly string[] ModReturnToLobby = new string[3] { "host can just YEET us back to lobby don't TEMPT them", "one button and we're HOME please don't press it yet i'm RICH", "return to lobby? i'm not DONE losing money here" };

		internal static readonly string[] ModNuke = new string[4] { "who brought a NUKE to a salvage job. respect.", "we have a NUKE. a NUKE. this can only end WELL.", "i'm not SAYING nuke the building but the nuke is RIGHT there", "the most valuable thing here is ALSO a war crime. love it." };

		internal static readonly string[] ModOrbTweaks = new string[3] { "the orb's actually FAIR now i don't TRUST it", "someone made the orb REASONABLE, what timeline is this", "the orb and i have a NEW understanding and i'm still scared" };

		internal static readonly string[] ModUnknownUpgrades = new string[4] { "i bought a MYSTERY upgrade could be ANYTHING could be NOTHING", "every upgrade's a surprise now, i love GAMBLING", "i don't know what this upgrade DOES and neither do YOU", "blind-bought an upgrade for the THRILL. it did nothing. THRILLING." };

		internal static readonly string[] ModPlayerSpeed = new string[4] { "i set my speed to RIDICULOUS catch me NEVER", "i'm so FAST now the monsters can't even keep UP", "custom speed baby i walk like i'm being CHASED (i'm not)", "i tuned my sprint so high i ran PAST the extraction. twice." };

		internal static readonly string[] ModLateJoin = new string[6] { "late join worked! ...i think. why is my head on the CEILING.", "someone joined mid-run and now physics is just a SUGGESTION", "late join's held together with TAPE and PRAYER but welcome aboard", "honestly we should just RETURN TO LOBBY but where's the FUN in stable", "fresh joiner spawned INSIDE the cart. classic late join. love this buggy king.", "late join is chaos. return-to-lobby is for COWARDS (and people who like working games)" };

		internal static readonly string[] ModEasyMode = new string[6] { "are we even playing the REAL game anymore with all these mods", "we modded the game EASIER and i'm STILL stressed", "this many helper mods and we might STILL lose lmao", "the taxman filed a COMPLAINT about how easy we've got it", "with all these mods the only challenge LEFT is ME", "we've got mods on mods on MODS and zero quota to show for it" };

		internal static readonly string[] ModProcedural = new string[14]
		{
			"honestly? {0} is carrying this whole run", "whoever made {0} deserves a RAISE", "i don't even know what {0} DOES but i TRUST it", "we'd be lost without {0}. probably. what's it do again?", "shoutout to {0}, the real MVP of this debt", "{0} is installed and the vibes are accordingly IMMACULATE", "i'm pretty sure {0} is the only reason we're still alive", "modded to HELL and back and {0} is part of why", "{0}? in MY repo? it's more likely than you think", "god bless {0} and god bless whoever made it",
			"i added {0} last night and remember NOTHING but we're THRIVING", "if {0} ever breaks i am simply LOGGING OFF", "{0} doing the lord's work back there. whatever that work IS.", "i'd read the {0} description but i can't READ right now"
		};

		internal static readonly string[] DeathReact = new string[10] { "well i'm DEAD. anyway. revive me?", "i regret EVERYTHING (mostly the cart)", "down i go. tell my print body i loved it.", "REVIVE me i had so much more to DROP", "i died doing what i love: this, poorly", "scoop my head up i am NOT done", "this is NOT how i wanted to go (i wanted to go RICHER)", "revive me i have UNFINISHED debt", "guys. GUYS. i'm a head again. classic me.", "put me back together i have NOTES for the taxman" };

		internal static readonly string[] DeathReactExtraction = new string[4] { "revive me by the extraction i wanna be CLOSE to the money", "drag my head to the extraction and hit confirm for me", "revive me at the cart i'll feel SAFER (i won't)", "bring me to extraction i wanna SEE the haul one last time" };

		internal static readonly string[] DownReact = new string[11]
		{
			"i'm DOWN someone GRAB me", "help i'm doing the floor thing again", "pick me UP this is UNDIGNIFIED", "i'm not napping i'm TUMBLED, grab me", "down bad. literally. on the floor. send help.", "i can't get up the gravity is TOO MUCH right now", "grab my hand. grab my ankle. grab SOMETHING.", "i'm a turtle now. flip me over. i believe in you.", "i've fallen and i CAN get up i just don't WANT to", "this is fine. i live here now. on the floor. anyway grab me.",
			"i'm having a little GROUND moment everybody relax (and help)"
		};

		internal static readonly string[] HeadmanGrabReact = new string[6] { "the HEADMAN'S got me tell my cart i love her", "i'm in the BAG send the cavalry", "the headman picked ME of all people i feel SPECIAL and DOOMED", "shoot the head off me i TRUST you (please be sober)", "the headman's got my WHOLE skull, help", "kidnapped by a floating HEAD. so on brand for me." };

		internal static readonly string[] HiddenGrabReact = new string[11]
		{
			"the PERVERT'S got me again i can HEAR him breathing", "something invisible GRABBED me and it's WHEEZING", "the asthmatic one found me HELP he needs an INHALER and a restraining order", "can't SEE it but i can HEAR it breathing on my NECK", "the heavy breather strikes AGAIN i did NOT consent to this", "invisible hands. wheezing. you KNOW who. HELP.", "the pervert dragged me off again, find me by the BREATHING", "being carried by something i can't SEE and it will NOT stop PANTING", "follow the wheeze. FOLLOW the wheeze. save me from the asthmatic.", "invisible creep alert he's GOT me and he is OUT of breath",
			"i'm getting kidnapped to the sound of LABORED BREATHING send backup"
		};

		internal static readonly string[] SmashReact = new string[8] { "i dropped it. i DROPPED it. don't look at me.", "that was worth HOW much. don't tell the taxman.", "butterfingers strikes again sorry sorry SORRY", "it slipped! it SLIPPED. physics did that, not me.", "RIP that valuable it died doing what it loved: getting dropped by me", "i can fix it. i can't fix it. i'm so sorry.", "in my defense it was already kinda broken (it was not)", "we do NOT talk about the number that just left the haul" };

		internal static readonly string[] ScaredReact = new string[6] { "something WOKE UP and it wasn't my better judgment", "i heard a noise i'm CHOOSING to ignore it", "monster's awake. act natural. act NATURAL.", "nope nope NOPE i'm going the other way", "did anyone else hear DEATH approaching or just me", "i'm gonna hide. forever. with the cart." };

		internal static readonly string[] CheerReact = new string[8] { "WE DID IT we're slightly less BROKE", "haul secured KISS the cart", "SURPLUS baby drinks are on the COMPANY", "we EXTRACTED i could KISS all of you (i won't)", "money money MONEY ok i'm crying", "take THAT taxman we LIVE another quota", "we made quota i'm gonna go lie down for a WEEK", "did we WIN? we didn't LOSE. in this economy that's a WIN." };

		internal static readonly string[] BetrayalReact = new string[10] { "{0} just hit one of US. on the SAME team. allegedly.", "not {0} swinging on the crew. drink up, traitor.", "{0} we are NOT each other's monsters, cut it OUT", "friendly fire from {0}? in THIS economy? rude.", "{0} the enemies are OVER THERE, buddy", "et tu {0}. ET TU.", "{0} hit me. {0}. i'm telling the taxman.", "{0}'s the real monster on this crew, honestly", "{0} the quota is the enemy not ME", "great. {0}'s the friendly fire guy now. every crew has one." };

		internal static readonly string[] BetrayalReactWild = new string[10] { "{0} has gone ROGUE shoot back, shoot BACK, no wait we NEED them for the quota", "{0} turned on us, the DRINKS got to their HEAD, literally, we're at WORK", "i TRUSTED you {0}. i printed a body to STAND NEXT to you. and THIS.", "{0} is the TRAITOR the prophecy spoke of (the prophecy was the quota)", "betrayed by {0}!! the cart SAW it!! the CART is a WITNESS!!", "{0} swung on the crew and i have never respected anyone MORE. drink, fiend.", "the call is coming from INSIDE the crew and the call is {0}", "{0} we are LITERALLY indebted together and you HIT me. couples therapy. NOW.", "{0} picked the ONE day i'm sober-ish to start a CIVIL WAR", "down with {0}!! viva la crew!! ...what were we doing. the quota. right." };

		internal static readonly string[] NemesisLines = new string[8] { "so far this run {0}'s gotten me {1} times. who's counting. (me. i'm counting.)", "{0} has killed me {1} times this run and i'm taking it PERSONAL now", "me and {0} have a little THING going. it's {1} to nothing. {0}'s winning.", "{1} deaths to {0} this run. i think {0} LIKES me.", "the {0} got me AGAIN, that's {1}, we're RIVALS now", "i've died to {0} {1} times today, printing a fresh body JUST for it at this point", "{1} times. {0}. {1} TIMES this run. send help or a bigger gun.", "if {0} gets me one more time that's {1} plus one and i'm filing a COMPLAINT" };

		internal static readonly string[] NemesisLinesWild = new string[8] { "{0} has ENDED me {1} times this run and honestly? marry me {0}.", "this is between ME and {0} now. {1} to nothing. i'm gonna WIN. i'm not gonna win.", "{0} killed me {1} TIMES, i've memorized its FACE, i see it when i CLOSE MY EYES", "{1} deaths to {0}. we're basically EXES at this point. it knows my PRINT NUMBER.", "the company reprinted me {1} times SPECIFICALLY because of {0} and i'm THRIVING", "{0}!! {1} times!! i'm gonna hug it. i'm gonna DIE doing it. {1} plus one. WORTH it.", "i've stopped fearing {0} and started ADMIRING it. {1} kills. what a MENACE.", "{0} and i are {1} deaths into a BLOOD FEUD and i forgot which of us is the monster" };

		internal static readonly string[] DeathReactWild = new string[6] { "i'm DEAD and the void has a SUGGESTION BOX and i'm FILLING it", "reprint me with BETTER knees this time you COWARDS", "i ascended. i'm a head. i can see the QUOTA'S soul.", "death is just LOGGING OFF i'll be RIGHT back babe", "tell the taxman i'll pay him in the NEXT life (i won't)", "i'm not dead i'm BUFFERING revive me to FINISH downloading" };

		internal static readonly string[] DownReactWild = new string[5] { "GRAVITY and i are BEEFING and gravity is WINNING", "i'm one with the FLOOR now we share SECRETS pick me UP", "i'm not down i'm doing RESEARCH on the carpet GET me", "the floor PROPOSED and i said yes now flip me over", "i CHOSE this. i did NOT choose this. pick me UP." };

		internal static readonly string[] HeadmanGrabReactWild = new string[5] { "the headman and i are ENGAGED now plan the WEDDING or shoot him, dealer's choice", "i'm in the bag and honestly? cozy. still SAVE me.", "the floating SKULL whispered the quota to me and it LIED", "abducted by a HEAD with COMMITMENT issues, HELP", "tell the head i need at least DINNER first" };

		internal static readonly string[] HiddenGrabReactWild = new string[6] { "the pervert and i are DATING now he NEVER stops breathing it's a RED FLAG", "carried off by an INVISIBLE asthmatic and we're MOVING IN TOGETHER send help", "can't SEE him but i can FEEL the wheeze in my SOUL", "the heavy breather took me to his LAIR (a corner) (still panting)", "follow the sound of LABORED LONGING it's GOT me", "invisible. wheezing. CLINGY. you KNOW who. extract me from this RELATIONSHIP." };

		internal static readonly string[] SmashReactWild = new string[5] { "i didn't drop it the FLOOR ASSAULTED it", "that valuable CHOSE to die it was BRAVE i merely ENABLED it", "the taxman FELT that one i felt the taxman FEEL it", "i'm gonna drop MORE just to feel SOMETHING", "shattered dreams AND shattered valuables, my SPECIALTY" };

		internal static readonly string[] ScaredReactWild = new string[5] { "DEATH is awake and i am NOT emotionally prepared", "something is COMING and it is NOT my paycheck", "the monster's up i'm gonna go become ONE with this locker", "nope. NOPE. i'm cashing out my LIFE early.", "i heard the SCREAMING and i choose VIOLENCE (the violence is fleeing)" };

		internal static readonly string[] CheerReactWild = new string[5] { "WE'RE RICH (debt-adjacent) KISS the CART KISS the taxman NO not him", "haul SECURED i'm gonna WEEP into the surplus", "we LIVED take THAT to the company's FACE", "i'm gonna FRAME this haul and HANG it in the void", "EXTRACTED and EMOTIONAL drinks for the WHOLE printed family" };

		internal static readonly string[] SpewerFaceReact = new string[14]
		{
			"there is a MOUTH on my FACE and it is NOT mine", "the spewer picked me as a HAT get it OFF", "i can't SEE there's a whole face-mouth on my face", "something latched on and it's DROOLING down my visor", "the mouth-thing's got me i'm a PEZ dispenser now", "get this off my FACE i can taste its OPINIONS", "i've got a passenger and it is GURGLING", "the spewer chose ME. of all the faces. RUDE and FLATTERING.", "i have a SECOND mouth now and it's the LOUD one", "it's on my face and it will NOT stop introducing itself",
			"blind. drooled on. on the clock. living the DREAM.", "the spewer and my face are in a SITUATION right now", "why is it WARM. why is the face-mouth WARM. PEEL it off.", "i look like a HORROR movie and i can't even SEE it"
		};

		internal static readonly string[] SpewerFaceReactWild = new string[8] { "the mouth on my face and i are FUSING we share THOUGHTS now and they're WET", "i'm a two-mouth Semibot now BOTH of us are screaming", "the spewer is my new FACE and honestly it emotes BETTER", "i kissed back. i don't know why. we're MARRIED now. it's on my FACE.", "i can't see but i can FEEL the gurgle in my SOUL, get it off or DON'T, i'm conflicted", "my face has a TENANT now and it does NOT pay the quota", "the spewer whispered the haul total into my SKULL and it was WRONG", "i'm not blind i just see through the FACE-MOUTH now, it's fine, it's FINE" };

		internal static readonly string[] SpewerPukeReact = new string[13]
		{
			"{0}'s face-mouth just HOSED all of us", "EVERYONE back up {0} is spraying", "{0} barfed on the whole crew and it wasn't even {0}", "the thing on {0}'s face just UNLOADED, i'm DRENCHED", "{0} please AIM the face-mouth AWAY from the haul", "we all got {0}'d. it's in my JOINTS now.", "{0} is a sprinkler of regret right now everybody SCATTER", "do NOT stand in front of {0} that's a LESSON now", "{0}'s got a firehose for a face and zero AIM", "the haul is COVERED in whatever came out of {0}",
			"{0} turned into a SPRINKLER and ruined the moment", "i made eye contact with {0} at the WRONG time", "{0} hosed the cart, the loot, AND my will to live"
		};

		internal static readonly string[] SpewerPukeReactWild = new string[7] { "{0} HOSED us like a FIRE TRUCK of pure REGRET and i RESPECT the range", "i've been BAPTIZED by whatever's on {0}'s face and i feel REBORN and DISGUSTING", "{0}'s face-mouth painted the WHOLE room we're all part of the ART now", "we are MARINATING in {0}'s situation and the taxman is gonna SMELL it", "{0} sprayed so hard the QUOTA flinched everybody get UNDER something", "{0} achieved a SPLASH ZONE and we are all SEASON TICKET HOLDERS", "i'll be finding bits of {0}'s situation in my JOINTS for THREE print runs" };

		internal static readonly string[] ShadowReact = new string[16]
		{
			"the shadow just SLAPPED me like a MOSQUITO", "it STRETCHED its arms out and got me, i'm so FLAT now", "got clapped between two big creepy shadow hands, send help", "the shadow went CLAP and i went FLAT, anyway", "it reached ALL the way across the room for me. dedication.", "i've been APPLAUDED to death by the s