Decompiled source of TruckScreenDisplay v1.1.0

BepInEx\plugins\TruckScreenDisplay\TruckScreenDisplay.dll

Decompiled 6 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TruckScreenDisplay")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TruckScreenDisplay")]
[assembly: AssemblyTitle("TruckScreenDisplay")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TruckScreenDisplay
{
	[BepInPlugin("repo.truck.screen.display", "Truck Screen Display", "1.1.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(TruckScreenText), "MessageSendCustomRPC")]
		private static class TruckScreenTextMessagePatch
		{
			private static bool Prefix(string playerName, string message)
			{
				if (message == null || !message.StartsWith("<size=0>[[TSD_SYNC_V1]]</size>", StringComparison.Ordinal))
				{
					return true;
				}
				string text = message.Substring("<size=0>[[TSD_SYNC_V1]]</size>".Length);
				instance?.SetActiveMessage(playerName, text, resetVanillaBroadcast: false);
				return false;
			}
		}

		[HarmonyPatch(typeof(CameraAim), "Update")]
		private static class CameraAimPatch
		{
			private static bool Prefix()
			{
				return !IsEditorBlockingInput();
			}
		}

		private const string PluginGuid = "repo.truck.screen.display";

		private const string PluginName = "Truck Screen Display";

		private const string PluginVersion = "1.1.0";

		private const string SyncPrefix = "<size=0>[[TSD_SYNC_V1]]</size>";

		private const string RoomMessageKey = "TSD_Message";

		private const string RoomPlayerNameKey = "TSD_PlayerName";

		private static Plugin instance;

		private ConfigEntry<KeyCode> toggleKey;

		private ConfigEntry<int> maxCharacters;

		private ConfigEntry<bool> sendAsPlayer;

		private ConfigEntry<bool> permanentDisplay;

		private ConfigEntry<bool> chineseUi;

		private ConfigEntry<bool> vanillaClientRebroadcast;

		private ConfigEntry<string> savedMessage;

		private ConfigEntry<string> savedPlayerName;

		private bool editorOpen;

		private string draftMessage = "";

		private string activeMessage = "";

		private string activePlayerName = "";

		private Rect windowRect = new Rect(40f, 40f, 620f, 430f);

		private Vector2 scroll;

		private TruckScreenText lastBroadcastScreen;

		private TruckScreenText pendingVanillaBroadcastScreen;

		private int pendingVanillaBroadcastStep;

		private float nextVanillaBroadcastTime;

		private readonly float[] vanillaBroadcastDelays = new float[6] { 1f, 3f, 5f, 10f, 20f, 30f };

		private bool savedCursorState;

		private bool previousCursorVisible;

		private CursorLockMode previousCursorLockState;

		private Harmony harmony;

		private void Awake()
		{
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Expected O, but got Unknown
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			instance = this;
			toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Input", "ToggleKey", (KeyCode)278, "Open or close the truck screen text UI. Use Home or F1-F12.");
			maxCharacters = ((BaseUnityPlugin)this).Config.Bind<int>("Text", "MaxCharacters", 300, "Maximum characters allowed per message.");
			sendAsPlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("Text", "SendAsPlayer", true, "Show the local player name before the message when possible.");
			permanentDisplay = ((BaseUnityPlugin)this).Config.Bind<bool>("Text", "PermanentDisplay", true, "Keep the last mod message permanently visible on the truck screen.");
			vanillaClientRebroadcast = ((BaseUnityPlugin)this).Config.Bind<bool>("Text", "VanillaClientRebroadcast", true, "Rebroadcast text through vanilla truck screen RPC so clients without this mod can see it after level transitions.");
			savedMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Saved", "Message", "", "Last confirmed truck screen text. Restored on game restart.");
			savedPlayerName = ((BaseUnityPlugin)this).Config.Bind<string>("Saved", "PlayerName", "", "Player name used with the last confirmed truck screen text.");
			chineseUi = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ChineseUi", false, "Use Chinese labels in the in-game UI.");
			if (!string.IsNullOrWhiteSpace(savedMessage.Value))
			{
				activePlayerName = savedPlayerName.Value ?? "";
				activeMessage = savedMessage.Value;
				draftMessage = savedMessage.Value;
			}
			harmony = new Harmony("repo.truck.screen.display");
			harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Press {2} to open the UI.", "Truck Screen Display", "1.1.0", toggleKey.Value));
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)instance == (Object)(object)this)
			{
				instance = null;
			}
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(toggleKey.Value))
			{
				SetEditorOpen(!editorOpen);
				return;
			}
			if (editorOpen)
			{
				BlockGameInputWhileEditing();
				if (Input.GetKeyDown((KeyCode)27))
				{
					SetEditorOpen(open: false);
				}
			}
			if (permanentDisplay.Value)
			{
				RestoreFromRoomProperties();
				ApplyPermanentDisplay();
				RebroadcastForVanillaClientsIfNeeded();
			}
		}

		private void LateUpdate()
		{
			if (editorOpen)
			{
				ForceCursorForUi();
			}
		}

		private void OnGUI()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			if (editorOpen)
			{
				ForceCursorForUi();
				windowRect = GUI.Window(((Object)this).GetInstanceID(), windowRect, new WindowFunction(DrawWindow), T("Truck Screen Display UI", "卡车屏幕显示 UI"));
			}
		}

		private void DrawWindow(int windowId)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_049b: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label(T("Truck screen text UI", "卡车屏幕文字 UI"), Array.Empty<GUILayoutOption>());
			GUILayout.Label(string.Format(T("Open key: {0}    Esc: close", "打开键: {0}    Esc: 关闭"), toggleKey.Value), Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button(T("CONFIRM DISPLAY", "确认显示"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(42f) }))
			{
				ConfirmAndSyncMessage();
			}
			if (GUILayout.Button(T("CLOSE UI", "关闭 UI"), (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Height(42f),
				GUILayout.Width(160f)
			}))
			{
				SetEditorOpen(open: false);
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button(chineseUi.Value ? "English" : "中文", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }))
			{
				chineseUi.Value = !chineseUi.Value;
				((BaseUnityPlugin)this).Config.Save();
			}
			if (GUILayout.Toggle(permanentDisplay.Value, T("Permanent display", "永久显示"), Array.Empty<GUILayoutOption>()) != permanentDisplay.Value)
			{
				permanentDisplay.Value = !permanentDisplay.Value;
				((BaseUnityPlugin)this).Config.Save();
			}
			if (GUILayout.Toggle(sendAsPlayer.Value, T("Show player name", "显示玩家名"), Array.Empty<GUILayoutOption>()) != sendAsPlayer.Value)
			{
				sendAsPlayer.Value = !sendAsPlayer.Value;
				((BaseUnityPlugin)this).Config.Save();
			}
			if (GUILayout.Toggle(vanillaClientRebroadcast.Value, T("Vanilla clients", "原版客机"), Array.Empty<GUILayoutOption>()) != vanillaClientRebroadcast.Value)
			{
				vanillaClientRebroadcast.Value = !vanillaClientRebroadcast.Value;
				((BaseUnityPlugin)this).Config.Save();
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(4f);
			GUILayout.Label(T("Shortcut", "快捷键"), Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			DrawShortcutButton((KeyCode)278);
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			for (int i = 1; i <= 6; i++)
			{
				DrawShortcutButton((KeyCode)Enum.Parse(typeof(KeyCode), "F" + i));
			}
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			for (int j = 7; j <= 12; j++)
			{
				DrawShortcutButton((KeyCode)Enum.Parse(typeof(KeyCode), "F" + j));
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(4f);
			GUILayout.Label(T("Text. English and Chinese input are supported.", "文本。支持英文和中文输入。"), Array.Empty<GUILayoutOption>());
			scroll = GUILayout.BeginScrollView(scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(120f) });
			GUI.SetNextControlName("TruckScreenDisplayTextArea");
			string text = GUILayout.TextArea(draftMessage, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			draftMessage = ((text.Length <= maxCharacters.Value) ? text : text.Substring(0, maxCharacters.Value));
			GUILayout.EndScrollView();
			GUILayout.Label($"{draftMessage.Length}/{maxCharacters.Value}", Array.Empty<GUILayoutOption>());
			GUILayout.Label(T("Press Confirm to display the current text.", "点击确认显示当前文本。"), Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button(T("Confirm display", "确认显示"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
			{
				ConfirmAndSyncMessage();
			}
			if (GUILayout.Button(T("Clear screen", "清除屏幕"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) }))
			{
				ClearAndSyncMessage();
			}
			if (GUILayout.Button(T("Close", "关闭"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) }))
			{
				SetEditorOpen(open: false);
			}
			GUILayout.EndHorizontal();
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 24f));
			GUI.FocusControl("TruckScreenDisplayTextArea");
		}

		private unsafe void DrawShortcutButton(KeyCode key)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			bool num = toggleKey.Value == key;
			GUI.enabled = !num;
			if (GUILayout.Button(num ? $"[{key}]" : ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }))
			{
				toggleKey.Value = key;
				((BaseUnityPlugin)this).Config.Save();
			}
			GUI.enabled = true;
		}

		private void ConfirmAndSyncMessage()
		{
			string text = draftMessage.Trim();
			if (text.Length == 0)
			{
				ClearAndSyncMessage();
				return;
			}
			string playerName = (sendAsPlayer.Value ? TryGetLocalPlayerName() : "");
			ConfirmAndSyncText(playerName, text);
		}

		private void ConfirmAndSyncText(string playerName, string text)
		{
			ConfirmAndSyncText(playerName, text, resetVanillaBroadcast: true);
		}

		private void ConfirmAndSyncText(string playerName, string text, bool resetVanillaBroadcast)
		{
			if (!string.IsNullOrWhiteSpace(text))
			{
				SetActiveMessage(playerName, text, resetVanillaBroadcast);
				SaveLocalText(playerName, text);
				SaveRoomProperties(playerName, text);
				BroadcastSync(playerName, text);
				if (resetVanillaBroadcast)
				{
					lastBroadcastScreen = TruckScreenText.instance;
				}
			}
		}

		private void ClearAndSyncMessage()
		{
			SetActiveMessage("", "", resetVanillaBroadcast: true);
			SaveLocalText("", "");
			SaveRoomProperties("", "");
			BroadcastSync("", "");
			lastBroadcastScreen = null;
		}

		private void BroadcastSync(string playerName, string text)
		{
			if ((Object)(object)TruckScreenText.instance == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"TruckScreenText.instance was not found. Enter a level/truck scene before sending text.");
			}
			else
			{
				TruckScreenText.instance.MessageSendCustom(playerName, "<size=0>[[TSD_SYNC_V1]]</size>" + text, 0);
			}
		}

		private void SetActiveMessage(string playerName, string text, bool resetVanillaBroadcast)
		{
			activePlayerName = playerName ?? "";
			activeMessage = text ?? "";
			if (resetVanillaBroadcast)
			{
				lastBroadcastScreen = null;
			}
			ApplyPermanentDisplay();
		}

		private void RebroadcastForVanillaClientsIfNeeded()
		{
			if (!vanillaClientRebroadcast.Value || string.IsNullOrWhiteSpace(activeMessage) || (Object)(object)TruckScreenText.instance == (Object)null)
			{
				return;
			}
			if ((Object)(object)lastBroadcastScreen != (Object)(object)TruckScreenText.instance)
			{
				lastBroadcastScreen = TruckScreenText.instance;
				pendingVanillaBroadcastScreen = TruckScreenText.instance;
				pendingVanillaBroadcastStep = 0;
				nextVanillaBroadcastTime = Time.time + vanillaBroadcastDelays[0];
			}
			else if (!((Object)(object)pendingVanillaBroadcastScreen != (Object)(object)TruckScreenText.instance) && pendingVanillaBroadcastStep >= 0 && !(Time.time < nextVanillaBroadcastTime))
			{
				ConfirmAndSyncText(TryGetLocalPlayerName(), activeMessage, resetVanillaBroadcast: false);
				BroadcastVanilla("", activeMessage);
				pendingVanillaBroadcastStep++;
				if (pendingVanillaBroadcastStep < vanillaBroadcastDelays.Length)
				{
					nextVanillaBroadcastTime = Time.time + (vanillaBroadcastDelays[pendingVanillaBroadcastStep] - vanillaBroadcastDelays[pendingVanillaBroadcastStep - 1]);
					return;
				}
				pendingVanillaBroadcastStep = -1;
				pendingVanillaBroadcastScreen = null;
			}
		}

		private void BroadcastVanilla(string playerName, string text)
		{
			if (!((Object)(object)TruckScreenText.instance == (Object)null))
			{
				TruckScreenText.instance.MessageSendCustom(playerName ?? "", text ?? "", 0);
			}
		}

		private void SaveRoomProperties(string playerName, string text)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null)
			{
				Hashtable val = new Hashtable
				{
					[(object)"TSD_PlayerName"] = playerName ?? "",
					[(object)"TSD_Message"] = text ?? ""
				};
				PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
			}
		}

		private void SaveLocalText(string playerName, string text)
		{
			savedPlayerName.Value = playerName ?? "";
			savedMessage.Value = text ?? "";
			((BaseUnityPlugin)this).Config.Save();
		}

		private void RestoreFromRoomProperties()
		{
			if (!string.IsNullOrWhiteSpace(activeMessage) || !PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null || !((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"TSD_Message", out object value))
			{
				return;
			}
			string text = value as string;
			if (!string.IsNullOrWhiteSpace(text))
			{
				string playerName = "";
				if (((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"TSD_PlayerName", out object value2))
				{
					playerName = (value2 as string) ?? "";
				}
				SetActiveMessage(playerName, text, resetVanillaBroadcast: true);
			}
		}

		private void ApplyPermanentDisplay()
		{
			if (!((Object)(object)TruckScreenText.instance == (Object)null) && !((Object)(object)TruckScreenText.instance.textMesh == (Object)null) && !string.IsNullOrWhiteSpace(activeMessage))
			{
				string text = (string.IsNullOrWhiteSpace(activePlayerName) ? activeMessage : ("<b>" + activePlayerName + ":</b>\n" + activeMessage));
				((TMP_Text)TruckScreenText.instance.textMesh).text = SemiFunc.EmojiText(text);
			}
		}

		private void SetEditorOpen(bool open)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (open && !editorOpen)
			{
				previousCursorVisible = Cursor.visible;
				previousCursorLockState = Cursor.lockState;
				savedCursorState = true;
			}
			editorOpen = open;
			Input.imeCompositionMode = (IMECompositionMode)(open ? 1 : 0);
			if (open)
			{
				ForceCursorForUi();
			}
			else if (savedCursorState)
			{
				Cursor.visible = previousCursorVisible;
				Cursor.lockState = previousCursorLockState;
				savedCursorState = false;
			}
		}

		private void ForceCursorForUi()
		{
			Cursor.visible = true;
			Cursor.lockState = (CursorLockMode)0;
		}

		private void BlockGameInputWhileEditing()
		{
			MenuManager obj = MenuManager.instance;
			if (obj != null)
			{
				obj.TextInputActive();
			}
			GameDirector obj2 = GameDirector.instance;
			if (obj2 != null)
			{
				obj2.SetDisableInput(0.1f);
			}
			PlayerController obj3 = PlayerController.instance;
			if (obj3 != null)
			{
				obj3.InputDisable(0.1f);
			}
			if ((Object)(object)InputManager.instance != (Object)null)
			{
				SemiFunc.InputDisableMovement();
				SemiFunc.InputDisableAiming();
			}
		}

		private static bool IsEditorBlockingInput()
		{
			if ((Object)(object)instance != (Object)null)
			{
				return instance.editorOpen;
			}
			return false;
		}

		private string T(string english, string chinese)
		{
			if (!chineseUi.Value)
			{
				return english;
			}
			return chinese;
		}

		private static string TryGetLocalPlayerName()
		{
			try
			{
				Type typeFromHandle = typeof(PlayerController);
				object obj = typeFromHandle.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj == null)
				{
					return "";
				}
				return (typeFromHandle.GetField("playerName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) as string) ?? "";
			}
			catch (Exception)
			{
				return "";
			}
		}
	}
}