using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using HarmonyLib;
using HawChow.ReturnToLobbyAnyway;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using SNetwork;
using TMPro;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[BepInPlugin("QingJu.ReturnToLobbyAnyway", "ReturnToLobbyAnyway", "1.0.0")]
public sealed class Plugin : BasePlugin
{
internal static ManualLogSource Logger;
internal static ConfigEntry<string> Language;
public override void Load()
{
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Expected O, but got Unknown
Logger = ((BasePlugin)this).Log;
Language = ((BasePlugin)this).Config.Bind<string>("General", "Language", "zh", "Button copy. zh (default) = 长按 1 秒返回大厅, en = HOLD TO RETURN TO LOBBY. Chinese needs a CJK-capable system font; if none can be created the button falls back to English automatically.");
ClassInjector.RegisterTypeInIl2Cpp<ReturnLobbyButton>();
((BasePlugin)this).AddComponent<ReturnLobbyButton>();
Harmony val = new Harmony("QingJu.ReturnToLobbyAnyway");
val.PatchAll();
Logger.LogInfo((object)"Loaded: guest-only hold-to-return patches are active.");
Logger.LogInfo((object)("patch state: " + DescribePatches(val)));
}
private static string DescribePatches(Harmony harmony)
{
try
{
return "fail.Update=" + Describe(harmony, typeof(CM_PageExpeditionFail)) + " | success.Update=" + Describe(harmony, typeof(CM_PageExpeditionSuccess));
}
catch (Exception ex)
{
return "unavailable (" + ex.Message + ")";
}
}
private static string Describe(Harmony harmony, Type page)
{
MethodBase methodBase = AccessTools.Method(page, "Update", (Type[])null, (Type[])null);
if (methodBase == null)
{
return "target-missing";
}
Patches patchInfo = Harmony.GetPatchInfo(methodBase);
if (patchInfo == null)
{
return "no-patch-info";
}
return "pre:" + patchInfo.Prefixes.Count + ",post:" + patchInfo.Postfixes.Count;
}
}
namespace HawChow.ReturnToLobbyAnyway;
public static class ButtonTextPolicy
{
public const string ChineseLabel = "长按 1 秒返回大厅";
public const string EnglishLabel = "HOLD TO RETURN TO LOBBY";
public static bool IsEnglish(string language)
{
if (string.IsNullOrWhiteSpace(language))
{
return false;
}
string text = language.Trim().ToLowerInvariant();
switch (text)
{
default:
return text == "english";
case "en":
case "en-us":
case "en-gb":
return true;
}
}
public static string Label(string language, int percent, bool cjkFontAvailable)
{
int num = ((percent >= 0) ? ((percent > 100) ? 100 : percent) : 0);
return ((IsEnglish(language) || !cjkFontAvailable) ? "HOLD TO RETURN TO LOBBY" : "长按 1 秒返回大厅") + " (" + num + "%)";
}
}
internal static class SettlementPageTracker
{
internal static CM_PageBase Page { get; private set; }
internal static TextMeshPro WaitingText { get; private set; }
internal static int Revision { get; private set; }
internal static void Observe(CM_PageExpeditionFail page)
{
TextMeshPro text = null;
if (IsPageActive((CM_PageBase)(object)page))
{
text = page.m_returnToLobby_text;
if (!IsTextVisible(text))
{
text = FindWaitingText((CM_PageBase)(object)page);
}
}
Set((CM_PageBase)(object)page, text);
}
internal static void Observe(CM_PageExpeditionSuccess page)
{
TextMeshPro text = (IsPageActive((CM_PageBase)(object)page) ? FindWaitingText((CM_PageBase)(object)page) : null);
Set((CM_PageBase)(object)page, text);
}
internal static void Clear()
{
Set(null, null);
}
internal static bool IsPageActive(CM_PageBase page)
{
if ((Object)(object)page != (Object)null && ((Behaviour)page).isActiveAndEnabled && (Object)(object)((Component)page).gameObject != (Object)null && ((Component)page).gameObject.activeInHierarchy)
{
return page.m_isActive;
}
return false;
}
internal static bool IsTextVisible(TextMeshPro text)
{
if ((Object)(object)text != (Object)null && ((Behaviour)text).enabled && (Object)(object)((Component)text).gameObject != (Object)null && ((Component)text).gameObject.activeInHierarchy)
{
return !string.IsNullOrWhiteSpace(((TMP_Text)text).text);
}
return false;
}
private static void Set(CM_PageBase page, TextMeshPro text)
{
int num = ((!((Object)(object)page == (Object)null)) ? ((Object)page).GetInstanceID() : 0);
int num2 = ((!((Object)(object)text == (Object)null)) ? ((Object)text).GetInstanceID() : 0);
int num3 = ((!((Object)(object)Page == (Object)null)) ? ((Object)Page).GetInstanceID() : 0);
int num4 = ((!((Object)(object)WaitingText == (Object)null)) ? ((Object)WaitingText).GetInstanceID() : 0);
Page = page;
WaitingText = text;
if (num != num3 || num2 != num4)
{
Revision++;
}
}
private static TextMeshPro FindWaitingText(CM_PageBase page)
{
Il2CppReferenceArray<iCellMenuCursorItem> allItems = page.m_allItems;
if (allItems == null)
{
return null;
}
for (int i = 0; i < ((Il2CppArrayBase<iCellMenuCursorItem>)(object)allItems).Length; i++)
{
CM_Item val = ((((Il2CppArrayBase<iCellMenuCursorItem>)(object)allItems)[i] == null) ? null : ((Il2CppObjectBase)((Il2CppArrayBase<iCellMenuCursorItem>)(object)allItems)[i]).TryCast<CM_Item>());
if ((Object)(object)val == (Object)null)
{
continue;
}
Il2CppReferenceArray<TextMeshPro> texts = val.GetTexts();
if (texts == null)
{
continue;
}
for (int j = 0; j < ((Il2CppArrayBase<TextMeshPro>)(object)texts).Length; j++)
{
TextMeshPro val2 = ((Il2CppArrayBase<TextMeshPro>)(object)texts)[j];
if (IsTextVisible(val2) && IsWaitingCopy(((TMP_Text)val2).text))
{
return val2;
}
}
}
return null;
}
private static bool IsWaitingCopy(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
bool num = value.IndexOf("wait", StringComparison.OrdinalIgnoreCase) >= 0 && value.IndexOf("host", StringComparison.OrdinalIgnoreCase) >= 0 && (value.IndexOf("lobby", StringComparison.OrdinalIgnoreCase) >= 0 || value.IndexOf("return", StringComparison.OrdinalIgnoreCase) >= 0);
bool flag = value.Contains("等待") && (value.Contains("厅主") || value.Contains("房主") || value.Contains("主机")) && (value.Contains("大厅") || value.Contains("返回") || value.Contains("送回"));
return num || flag;
}
}
[HarmonyPatch(typeof(CM_PageExpeditionFail), "Update")]
internal static class ExpeditionFailPagePatch
{
[HarmonyPostfix]
private static void Postfix(CM_PageExpeditionFail __instance)
{
SettlementPageTracker.Observe(__instance);
}
}
[HarmonyPatch(typeof(CM_PageExpeditionSuccess), "Update")]
internal static class ExpeditionSuccessPagePatch
{
[HarmonyPostfix]
private static void Postfix(CM_PageExpeditionSuccess __instance)
{
SettlementPageTracker.Observe(__instance);
}
}
public sealed class ReturnLobbyButton : MonoBehaviour
{
private const float HoldSeconds = 1f;
private const float ButtonWidth = 300f;
private const float ButtonHeight = 54f;
private const float GapFromLabel = 14f;
private const int ButtonFontSize = 19;
private static readonly string[] CjkFontNames = new string[5] { "Microsoft YaHei UI", "Microsoft YaHei", "SimHei", "SimSun", "Noto Sans CJK SC" };
private static Font cjkFont;
private static GUIStyle buttonStyle;
private static bool fontResolved;
private static bool fontLogged;
private readonly HoldController hold = new HoldController();
private Rect buttonRect;
private int observedRevision = -1;
private bool visible;
private bool pointerStartedInside;
private bool leaveRequested;
public ReturnLobbyButton(IntPtr pointer)
: base(pointer)
{
}
private void Update()
{
//IL_0074: 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_007c: 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)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
if (observedRevision != SettlementPageTracker.Revision)
{
observedRevision = SettlementPageTracker.Revision;
hold.Reset();
pointerStartedInside = false;
leaveRequested = false;
}
visible = CanShowButton() && TryPlaceBesideWaitingText(SettlementPageTracker.WaitingText, out buttonRect);
if (!visible)
{
pointerStartedInside = false;
hold.Tick(eligible: false, pressed: false, 0f);
return;
}
Vector3 mousePosition = Input.mousePosition;
Vector2 val = default(Vector2);
((Vector2)(ref val))..ctor(mousePosition.x, (float)Screen.height - mousePosition.y);
bool flag = ((Rect)(ref buttonRect)).Contains(val);
if (Input.GetMouseButtonDown(0))
{
pointerStartedInside = flag;
}
bool mouseButton = Input.GetMouseButton(0);
bool pressed = mouseButton && pointerStartedInside && flag && !leaveRequested;
if (hold.Tick(!leaveRequested, pressed, Time.unscaledDeltaTime))
{
leaveRequested = true;
LeaveCurrentLobby();
}
if (!mouseButton || Input.GetMouseButtonUp(0))
{
pointerStartedInside = false;
}
}
private void OnGUI()
{
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
if (visible && !leaveRequested)
{
GUIStyle val = Style();
int percent = Mathf.RoundToInt(hold.Progress * 100f);
string language = ((Plugin.Language == null) ? "zh" : Plugin.Language.Value);
GUI.Button(buttonRect, ButtonTextPolicy.Label(language, percent, (Object)(object)cjkFont != (Object)null), val);
}
}
private static GUIStyle Style()
{
//IL_0038: 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_003e: Expected O, but got Unknown
Font val = CjkFont();
if (buttonStyle != null && fontResolved)
{
return buttonStyle;
}
GUIStyle val2 = (((Object)(object)GUI.skin == (Object)null) ? new GUIStyle() : new GUIStyle(GUI.skin.button));
val2.alignment = (TextAnchor)4;
val2.wordWrap = false;
val2.fontSize = 19;
if ((Object)(object)val != (Object)null)
{
val2.font = val;
}
buttonStyle = val2;
fontResolved = true;
return buttonStyle;
}
private static Font CjkFont()
{
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Expected O, but got Unknown
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Expected O, but got Unknown
if ((Object)(object)cjkFont != (Object)null && ((Il2CppObjectBase)cjkFont).Pointer != IntPtr.Zero)
{
return cjkFont;
}
try
{
cjkFont = new Font();
Il2CppStringArray val = new Il2CppStringArray(CjkFontNames);
Font.Internal_CreateDynamicFont(cjkFont, val, 19);
if ((Object)(object)cjkFont == (Object)null || ((Il2CppObjectBase)cjkFont).Pointer == IntPtr.Zero)
{
cjkFont = null;
LogFontOnce("[DIAG] no CJK-capable system font was created; the button falls back to English copy.");
return null;
}
if (!fontLogged)
{
fontLogged = true;
ManualLogSource logger = Plugin.Logger;
if (logger != null)
{
logger.LogInfo((object)("[DIAG] button font: '" + ((Object)cjkFont).name + "' loaded from the operating system (CJK capable)."));
}
}
return cjkFont;
}
catch (Exception ex)
{
cjkFont = null;
LogFontOnce("[DIAG] CJK font creation failed, the button falls back to English copy: " + ex.Message);
return null;
}
}
private static void LogFontOnce(string message)
{
if (!fontLogged)
{
fontLogged = true;
ManualLogSource logger = Plugin.Logger;
if (logger != null)
{
logger.LogWarning((object)message);
}
}
}
private static bool CanShowButton()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Invalid comparison between Unknown and I4
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Invalid comparison between Unknown and I4
CM_PageBase page = SettlementPageTracker.Page;
TextMeshPro waitingText = SettlementPageTracker.WaitingText;
eGameStateName currentStateName = GameStateManager.CurrentStateName;
bool flag = (int)currentStateName == 14 || (int)currentStateName == 15;
bool flag2 = SettlementPageTracker.IsPageActive(page);
bool waitingLabelVisible = SettlementPageTracker.IsTextVisible(waitingText);
return VisibilityPolicy.ShouldShow(SNet.IsOnline, SNet.IsMaster, flag && flag2, waitingLabelVisible);
}
private static bool TryPlaceBesideWaitingText(TextMeshPro text, out Rect rect)
{
//IL_0001: 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_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: 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_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: 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_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: 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_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_010a: Unknown result type (might be due to invalid IL or missing references)
rect = default(Rect);
if (!SettlementPageTracker.IsTextVisible(text) || (Object)(object)((TMP_Text)text).rectTransform == (Object)null)
{
return false;
}
Camera val = CM_PageBase.Camera;
if ((Object)(object)val == (Object)null)
{
val = Camera.main;
}
if ((Object)(object)val == (Object)null)
{
return false;
}
RectTransform rectTransform = ((TMP_Text)text).rectTransform;
Rect rect2 = rectTransform.rect;
Vector3 val2 = ((Transform)rectTransform).TransformPoint(new Vector3(((Rect)(ref rect2)).xMax, ((Rect)(ref rect2)).center.y, 0f));
Vector3 val3 = val.WorldToScreenPoint(val2);
if (val3.z <= 0f)
{
return false;
}
float num = (float)Screen.width - val3.x - 14f - 16f;
if (num < 210f)
{
return false;
}
float num2 = Mathf.Min(300f, num);
float num3 = val3.x + 14f;
float num4 = Mathf.Clamp((float)Screen.height - val3.y - 27f, 16f, (float)Screen.height - 54f - 16f);
rect = new Rect(num3, num4, num2, 54f);
return true;
}
private static void LeaveCurrentLobby()
{
try
{
SNet_LobbyManager lobbies = SNet.Lobbies;
if ((Object)(object)lobbies == (Object)null)
{
Plugin.Logger.LogError((object)"Long-hold completed, but SNet.Lobbies was unavailable.");
return;
}
lobbies.LeaveLobby();
Plugin.Logger.LogInfo((object)"Guest requested the native local lobby-leave flow.");
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)("Native lobby leave failed: " + ex));
}
}
}
public sealed class HoldController
{
private readonly float requiredSeconds;
private float elapsedSeconds;
private bool triggered;
public float Progress => Math.Min(1f, elapsedSeconds / requiredSeconds);
public bool IsHolding
{
get
{
if (elapsedSeconds > 0f)
{
return !triggered;
}
return false;
}
}
public HoldController(float requiredSeconds = 1f)
{
this.requiredSeconds = Math.Max(0.01f, requiredSeconds);
}
public bool Tick(bool eligible, bool pressed, float deltaSeconds)
{
if (!eligible || !pressed)
{
Reset();
return false;
}
if (triggered)
{
return false;
}
if (deltaSeconds > 0f && !float.IsNaN(deltaSeconds) && !float.IsInfinity(deltaSeconds))
{
elapsedSeconds += deltaSeconds;
}
if (elapsedSeconds < requiredSeconds)
{
return false;
}
elapsedSeconds = requiredSeconds;
triggered = true;
return true;
}
public void Reset()
{
elapsedSeconds = 0f;
triggered = false;
}
}
internal static class PluginInfo
{
internal const string Guid = "QingJu.ReturnToLobbyAnyway";
internal const string Name = "ReturnToLobbyAnyway";
internal const string Version = "1.0.0";
}
public static class VisibilityPolicy
{
public static bool ShouldShow(bool isOnline, bool isMaster, bool settlementPageActive, bool waitingLabelVisible)
{
return isOnline && !isMaster && settlementPageActive && waitingLabelVisible;
}
}