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("[[TSD_SYNC_V1]]", StringComparison.Ordinal)) { return true; } string text = message.Substring("[[TSD_SYNC_V1]]".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 = "[[TSD_SYNC_V1]]"; private const string RoomMessageKey = "TSD_Message"; private const string RoomPlayerNameKey = "TSD_PlayerName"; private static Plugin instance; private ConfigEntry toggleKey; private ConfigEntry maxCharacters; private ConfigEntry sendAsPlayer; private ConfigEntry permanentDisplay; private ConfigEntry chineseUi; private ConfigEntry vanillaClientRebroadcast; private ConfigEntry savedMessage; private ConfigEntry 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("Input", "ToggleKey", (KeyCode)278, "Open or close the truck screen text UI. Use Home or F1-F12."); maxCharacters = ((BaseUnityPlugin)this).Config.Bind("Text", "MaxCharacters", 300, "Maximum characters allowed per message."); sendAsPlayer = ((BaseUnityPlugin)this).Config.Bind("Text", "SendAsPlayer", true, "Show the local player name before the message when possible."); permanentDisplay = ((BaseUnityPlugin)this).Config.Bind("Text", "PermanentDisplay", true, "Keep the last mod message permanently visible on the truck screen."); vanillaClientRebroadcast = ((BaseUnityPlugin)this).Config.Bind("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("Saved", "Message", "", "Last confirmed truck screen text. Restored on game restart."); savedPlayerName = ((BaseUnityPlugin)this).Config.Bind("Saved", "PlayerName", "", "Player name used with the last confirmed truck screen text."); chineseUi = ((BaseUnityPlugin)this).Config.Bind("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()); GUILayout.Label(string.Format(T("Open key: {0} Esc: close", "打开键: {0} Esc: 关闭"), toggleKey.Value), Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); 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()); 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()) != permanentDisplay.Value) { permanentDisplay.Value = !permanentDisplay.Value; ((BaseUnityPlugin)this).Config.Save(); } if (GUILayout.Toggle(sendAsPlayer.Value, T("Show player name", "显示玩家名"), Array.Empty()) != sendAsPlayer.Value) { sendAsPlayer.Value = !sendAsPlayer.Value; ((BaseUnityPlugin)this).Config.Save(); } if (GUILayout.Toggle(vanillaClientRebroadcast.Value, T("Vanilla clients", "原版客机"), Array.Empty()) != vanillaClientRebroadcast.Value) { vanillaClientRebroadcast.Value = !vanillaClientRebroadcast.Value; ((BaseUnityPlugin)this).Config.Save(); } GUILayout.EndHorizontal(); GUILayout.Space(4f); GUILayout.Label(T("Shortcut", "快捷键"), Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawShortcutButton((KeyCode)278); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); for (int i = 1; i <= 6; i++) { DrawShortcutButton((KeyCode)Enum.Parse(typeof(KeyCode), "F" + i)); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); 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()); 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()); GUILayout.Label(T("Press Confirm to display the current text.", "点击确认显示当前文本。"), Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); 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, "[[TSD_SYNC_V1]]" + 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)((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)((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 : ("" + activePlayerName + ":\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 ""; } } } }