using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Extensions;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using NoPitStops.GiveUp;
using NoPitStops.Patches;
using NoPitStops.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("NoPitStops")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.8.1.0")]
[assembly: AssemblyInformationalVersion("0.8.1+d714dee1f04624388c3cf520916753476a775b1f")]
[assembly: AssemblyProduct("NoPitStops")]
[assembly: AssemblyTitle("NoPitStops")]
[assembly: AssemblyVersion("0.8.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 NoPitStops
{
[BepInPlugin("com.saltedbyte.nopitstops", "NoPitStops", "0.8.0")]
public class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "com.saltedbyte.nopitstops";
public const string PluginName = "NoPitStops";
public const string PluginVersion = "0.8.0";
internal static ManualLogSource Log = null;
internal static ConfigEntry<bool> AutoVoteEnabled = null;
internal static ConfigEntry<float> AutoVoteDelay = null;
internal static ConfigEntry<bool> ForceVoteHotkey = null;
internal static ConfigEntry<bool> FastCountdownEnabled = null;
internal static ConfigEntry<float> CountdownSeconds = null;
internal static ConfigEntry<bool> PlayerThresholdEnabled = null;
internal static ConfigEntry<float> PlayerThresholdPercent = null;
internal static ConfigEntry<bool> SoloTrigger = null;
internal static ConfigEntry<bool> SkipPitOnFullLoss = null;
internal static ConfigEntry<bool> ShowToasts = null;
internal static ConfigEntry<bool> GiveUpEnabled = null;
internal static ConfigEntry<float> GiveUpThreshold = null;
private static readonly Type[] PatchClasses = new Type[7]
{
typeof(SkipUI_AutoVote),
typeof(AllPlayersTriggerZone_FastCountdown),
typeof(AllPlayersTriggerZone_PlayerThreshold),
typeof(GameManager_SkipLoseStateScene),
typeof(EscapeMenu_TrackOpen),
typeof(EscapeMenu_AddGiveUpButton),
typeof(CustomNetworkManager_HandlerInit)
};
private void Awake()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
BindConfig();
Log.LogInfo((object)"NoPitStops v0.8.0 loaded.");
Toast.Bootstrap();
GiveUpVote.InstallSerializers();
Harmony val = new Harmony("com.saltedbyte.nopitstops");
int num = 0;
Type[] patchClasses = PatchClasses;
foreach (Type type in patchClasses)
{
try
{
val.CreateClassProcessor(type).Patch();
Log.LogInfo((object)(" ✔ patched " + type.Name));
num++;
}
catch (Exception arg)
{
Log.LogError((object)$" ✘ FAILED to patch {type.Name}: {arg}");
}
}
Log.LogInfo((object)$"Harmony patches applied: {num}/{PatchClasses.Length}");
if (ShowToasts.Value)
{
Toast.Show($"No Pit Stops loaded — {num}/{PatchClasses.Length} patches active");
}
}
private void BindConfig()
{
AutoVoteEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("AutoVote", "Enabled", true, "Auto-vote skip whenever SkipUI becomes available (end-of-day, ending sequence, etc.). When all clients run the mod, the skip fires the moment SkipUI appears.");
AutoVoteDelay = ((BaseUnityPlugin)this).Config.Bind<float>("AutoVote", "DelaySeconds", 0.5f, "Delay between SkipUI appearing and the auto-vote being sent. Lets you visually confirm the skip is happening.");
ForceVoteHotkey = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotkeys", "EnableF9ForceVote", true, "Press F9 to force-send a skip vote on any active SkipUI in the scene (debug/manual fallback).");
FastCountdownEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("FastCountdown", "Enabled", true, "Shorten the 'wait for all players to gather, then countdown' delay on AllPlayersTriggerZone (the car, the pit-entry, etc.). Host-side only.");
CountdownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("FastCountdown", "Seconds", 0.25f, "How long the countdown should be once the player threshold is met. Clamped to a minimum of 0.05s. Only ever shortens.");
PlayerThresholdEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("PlayerThreshold", "Enabled", true, "Override the 'every player must be in the trigger zone' gate. When disabled, vanilla all-players-required logic is used. Host-side only.");
PlayerThresholdPercent = ((BaseUnityPlugin)this).Config.Bind<float>("PlayerThreshold", "PercentRequired", 0.5f, "Fraction (0..1) of players who must be in the zone for the countdown to start. 0.5 = half. Always rounds up and requires at least 1.");
SoloTrigger = ((BaseUnityPlugin)this).Config.Bind<bool>("PlayerThreshold", "SoloTrigger", false, "If true, only one player needs to be in the zone (overrides PercentRequired).");
SkipPitOnFullLoss = ((BaseUnityPlugin)this).Config.Bind<bool>("SkipPit", "Enabled", true, "Hard-skip the LoseStateScene cutscene (the body-shredder 'pit' on a full loss). Host-side only.");
ShowToasts = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowToasts", true, "Display in-game toast notifications (top-right of screen) whenever a skip event fires.");
GiveUpEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("GiveUp", "Enabled", true, "Show a 'Give Up Day' button in the in-game escape menu. Clicking it casts a vote; when the threshold is met across all players, the day ends immediately (calls GameManager.ProgressGame).");
GiveUpThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("GiveUp", "Threshold", 1f, "Fraction (0..1) of players who must vote 'Give Up' for the day to end. 1.0 = unanimous (default), 0.5 = half, etc. Always rounds up; requires at least 1.");
}
private void Update()
{
Keyboard current = Keyboard.current;
if (current == null)
{
return;
}
if (((ButtonControl)current[(Key)101]).wasPressedThisFrame)
{
Log.LogInfo((object)"[NoPitStops] F8 → toast self-test");
Toast.Show("F8 self-test — mod is alive");
}
if (ForceVoteHotkey.Value && ((ButtonControl)current[(Key)102]).wasPressedThisFrame)
{
SkipUI val = Object.FindAnyObjectByType<SkipUI>();
if ((Object)(object)val != (Object)null)
{
Log.LogInfo((object)"[NoPitStops] F9 → force-vote on active SkipUI");
Toast.Show("F9: force-voting skip");
SkipUI_AutoVote.TryRequestSkip(val);
}
else
{
Log.LogInfo((object)"[NoPitStops] F9 pressed but no active SkipUI");
Toast.Show("F9: no SkipUI in scene");
}
}
}
}
internal sealed class Toast : MonoBehaviour
{
private struct Entry
{
public string Text;
public float ExpiresAt;
}
private static Toast _instance;
private readonly List<Entry> _entries = new List<Entry>();
private GUIStyle _style;
public static void Bootstrap()
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
if (!((Object)(object)_instance != (Object)null))
{
GameObject val = new GameObject("NoPitStops.Toast");
Object.DontDestroyOnLoad((Object)val);
_instance = val.AddComponent<Toast>();
}
}
public static void Show(string text, float seconds = 3f)
{
if ((Object)(object)_instance == (Object)null)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogDebug((object)("[Toast pre-init] " + text));
}
}
else
{
_instance._entries.Add(new Entry
{
Text = text,
ExpiresAt = Time.unscaledTime + seconds
});
}
}
private void Update()
{
float unscaledTime = Time.unscaledTime;
for (int num = _entries.Count - 1; num >= 0; num--)
{
if (unscaledTime >= _entries[num].ExpiresAt)
{
_entries.RemoveAt(num);
}
}
}
private void OnGUI()
{
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
if (_entries.Count != 0)
{
EnsureStyle();
float num = 14f;
for (int i = 0; i < _entries.Count; i++)
{
GUI.Box(new Rect((float)Screen.width - 420f - 14f, num, 420f, 32f), _entries[i].Text, _style);
num += 36f;
}
}
}
private void EnsureStyle()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: 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_0057: 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_0068: Expected O, but got Unknown
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: 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_009d: Expected O, but got Unknown
if (_style == null)
{
Texture2D val = new Texture2D(1, 1);
val.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.78f));
val.Apply();
GUIStyle val2 = new GUIStyle(GUI.skin.box)
{
fontSize = 15,
alignment = (TextAnchor)3,
padding = new RectOffset(12, 12, 6, 6)
};
val2.normal.textColor = new Color(0.95f, 0.95f, 0.95f, 1f);
val2.normal.background = val;
_style = val2;
}
}
}
}
namespace NoPitStops.UI
{
internal class GiveUpButtonController : MonoBehaviour
{
public GameObject ButtonGo;
public Button Button;
public TMP_Text[] TmpLabels;
public Text[] UiLabels;
private void Update()
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Invalid comparison between Unknown and I4
//IL_020b: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_0220: Unknown result type (might be due to invalid IL or missing references)
//IL_022c: Unknown result type (might be due to invalid IL or missing references)
//IL_0238: Unknown result type (might be due to invalid IL or missing references)
//IL_0247: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_0160: Unknown result type (might be due to invalid IL or missing references)
//IL_016c: Unknown result type (might be due to invalid IL or missing references)
//IL_017b: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)ButtonGo == (Object)null)
{
return;
}
GameManager instance = NetworkSingleton<GameManager>.Instance;
bool flag = Plugin.GiveUpEnabled.Value && (Object)(object)instance != (Object)null && (int)instance.state == 1;
if (ButtonGo.activeSelf != flag)
{
ButtonGo.SetActive(flag);
}
if (!flag)
{
return;
}
string text;
bool flag2;
if (GiveUpVote.LocalVoted)
{
text = $"VOTED — waiting ({GiveUpVote.CurrentVotes}/{Math.Max(1, GiveUpVote.CurrentNeeded)})";
flag2 = false;
}
else if (GiveUpVote.CurrentVotes > 0)
{
text = $"GIVE UP DAY ({GiveUpVote.CurrentVotes}/{GiveUpVote.CurrentNeeded})";
flag2 = true;
}
else
{
text = "GIVE UP DAY";
flag2 = true;
}
if ((Object)(object)Button != (Object)null && ((Selectable)Button).interactable != flag2)
{
((Selectable)Button).interactable = flag2;
}
if (TmpLabels != null)
{
for (int i = 0; i < TmpLabels.Length; i++)
{
TMP_Text val = TmpLabels[i];
if (!((Object)(object)val == (Object)null))
{
if (val.text != text)
{
val.text = text;
}
if (val.maxVisibleCharacters < text.Length)
{
val.maxVisibleCharacters = int.MaxValue;
}
if (((Graphic)val).color.a < 0.99f)
{
((Graphic)val).color = new Color(((Graphic)val).color.r, ((Graphic)val).color.g, ((Graphic)val).color.b, 1f);
}
CanvasRenderer canvasRenderer = ((Graphic)val).canvasRenderer;
if ((Object)(object)canvasRenderer != (Object)null && canvasRenderer.GetAlpha() < 0.99f)
{
canvasRenderer.SetAlpha(1f);
}
}
}
}
if (UiLabels == null)
{
return;
}
for (int j = 0; j < UiLabels.Length; j++)
{
Text val2 = UiLabels[j];
if (!((Object)(object)val2 == (Object)null))
{
if (val2.text != text)
{
val2.text = text;
}
if (((Graphic)val2).color.a < 0.99f)
{
((Graphic)val2).color = new Color(((Graphic)val2).color.r, ((Graphic)val2).color.g, ((Graphic)val2).color.b, 1f);
}
CanvasRenderer canvasRenderer2 = ((Graphic)val2).canvasRenderer;
if ((Object)(object)canvasRenderer2 != (Object)null && canvasRenderer2.GetAlpha() < 0.99f)
{
canvasRenderer2.SetAlpha(1f);
}
}
}
}
}
}
namespace NoPitStops.Patches
{
[HarmonyPatch]
internal static class AllPlayersTriggerZone_FastCountdown
{
private static readonly FieldInfo DelayField = AccessTools.Field(typeof(AllPlayersTriggerZone), "delayBeforeEvent");
[HarmonyPostfix]
[HarmonyPatch(typeof(AllPlayersTriggerZone), "OnStartClient")]
private static void Postfix_OnStartClient(AllPlayersTriggerZone __instance)
{
if ((Object)(object)__instance == (Object)null || !Plugin.FastCountdownEnabled.Value)
{
return;
}
if (DelayField == null)
{
Plugin.Log.LogError((object)"[NoPitStops] AllPlayersTriggerZone.delayBeforeEvent missing — game updated?");
return;
}
float num = (float)DelayField.GetValue(__instance);
float num2 = Mathf.Max(0.05f, Plugin.CountdownSeconds.Value);
if (!(num2 >= num))
{
DelayField.SetValue(__instance, num2);
string text = $"Countdown '{((Object)((Component)__instance).gameObject).name}' {num:0.##}s → {num2:0.##}s";
Plugin.Log.LogInfo((object)("[NoPitStops] " + text));
if (Plugin.ShowToasts.Value)
{
Toast.Show(text);
}
}
}
}
[HarmonyPatch]
internal static class AllPlayersTriggerZone_PlayerThreshold
{
private static readonly FieldInfo IsActiveField = AccessTools.Field(typeof(AllPlayersTriggerZone), "isActive");
private static readonly FieldInfo HasTriggeredField = AccessTools.Field(typeof(AllPlayersTriggerZone), "_hasTriggered");
private static readonly FieldInfo CheckColliderField = AccessTools.Field(typeof(AllPlayersTriggerZone), "checkCollider");
private static readonly FieldInfo IntervalField = AccessTools.Field(typeof(AllPlayersTriggerZone), "colliderCheckInterval");
private static readonly FieldInfo LastCheckTimeField = AccessTools.Field(typeof(AllPlayersTriggerZone), "_lastCheckTime");
private static readonly FieldInfo CountdownRoutineField = AccessTools.Field(typeof(AllPlayersTriggerZone), "_countdownRoutine");
private static readonly MethodInfo RpcUpdateCountdownText = AccessTools.Method(typeof(AllPlayersTriggerZone), "RpcUpdateCountdownText", (Type[])null, (Type[])null);
private static readonly MethodInfo CountdownRoutineMethod = AccessTools.Method(typeof(AllPlayersTriggerZone), "CountdownRoutine", (Type[])null, (Type[])null);
[HarmonyPrefix]
[HarmonyPatch(typeof(AllPlayersTriggerZone), "CheckPlayers")]
private static bool Prefix(AllPlayersTriggerZone __instance)
{
//IL_0124: Unknown result type (might be due to invalid IL or missing references)
//IL_0129: Unknown result type (might be due to invalid IL or missing references)
//IL_012e: 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_01c1: Unknown result type (might be due to invalid IL or missing references)
//IL_01c8: Expected O, but got Unknown
if (!Plugin.PlayerThresholdEnabled.Value)
{
return true;
}
if (IsActiveField == null || HasTriggeredField == null || CheckColliderField == null || IntervalField == null || LastCheckTimeField == null || CountdownRoutineField == null || RpcUpdateCountdownText == null || CountdownRoutineMethod == null)
{
Plugin.Log.LogError((object)"[NoPitStops] AllPlayersTriggerZone reflection setup failed; falling back to vanilla logic");
return true;
}
bool num = (bool)IsActiveField.GetValue(__instance);
bool flag = (bool)HasTriggeredField.GetValue(__instance);
LocalManager instance = MonoSingleton<LocalManager>.Instance;
if (!num || flag || (Object)(object)instance == (Object)null || instance.players == null || instance.players.Count <= 0)
{
return false;
}
float num2 = (float)LastCheckTimeField.GetValue(__instance);
float num3 = (float)IntervalField.GetValue(__instance);
if (Time.time - num2 < num3)
{
return false;
}
LastCheckTimeField.SetValue(__instance, Time.time);
Bounds bounds = ((Collider)CheckColliderField.GetValue(__instance)).bounds;
int count = instance.players.Count;
int num4 = 0;
foreach (PlayerReferences player in instance.players)
{
if (((Bounds)(ref bounds)).Contains(player.transform.position))
{
num4++;
}
}
int num5 = (Plugin.SoloTrigger.Value ? 1 : Mathf.Max(1, Mathf.CeilToInt(Plugin.PlayerThresholdPercent.Value * (float)count)));
Coroutine val = (Coroutine)CountdownRoutineField.GetValue(__instance);
if (num4 < num5)
{
if (val != null)
{
((MonoBehaviour)__instance).StopCoroutine(val);
CountdownRoutineField.SetValue(__instance, null);
RpcUpdateCountdownText.Invoke(__instance, new object[1] { false });
}
return false;
}
if (val == null)
{
IEnumerator enumerator2 = (IEnumerator)CountdownRoutineMethod.Invoke(__instance, null);
Coroutine value = ((MonoBehaviour)__instance).StartCoroutine(enumerator2);
CountdownRoutineField.SetValue(__instance, value);
Plugin.Log.LogDebug((object)$"[NoPitStops] Threshold met on '{((Object)((Component)__instance).gameObject).name}' ({num4}/{count} ≥ {num5}); countdown started");
}
return false;
}
}
[HarmonyPatch]
internal static class CustomNetworkManager_HandlerInit
{
[HarmonyPostfix]
[HarmonyPatch(typeof(CustomNetworkManager), "OnStartServer")]
private static void Postfix_OnStartServer()
{
GiveUpVote.RegisterServerHandlers();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(CustomNetworkManager), "OnClientConnect")]
private static void Postfix_OnClientConnect()
{
GiveUpVote.RegisterClientHandlers();
}
}
[HarmonyPatch]
internal static class EscapeMenu_AddGiveUpButton
{
private struct RectTransformSnapshot
{
public Vector2 AnchorMin;
public Vector2 AnchorMax;
public Vector2 Pivot;
public Vector2 SizeDelta;
public Vector2 AnchoredPosition;
}
private struct LabelSnapshot
{
public RectTransformSnapshot Rect;
public int SiblingIndex;
public LayoutElementSnapshot LayoutElement;
}
private class LayoutElementSnapshot
{
public float MinWidth;
public float MinHeight;
public float PreferredWidth;
public float PreferredHeight;
public float FlexibleWidth;
public float FlexibleHeight;
}
[CompilerGenerated]
private static class <>O
{
public static UnityAction <0>__SendLocalVote;
}
private const string GiveUpButtonName = "NoPitStops_GiveUpButton";
private const string LabelName = "NoPitStops_Label";
private const string ButtonText = "GIVE UP DAY";
[HarmonyPostfix]
[HarmonyPatch(typeof(EscapeMenu), "Awake")]
private static void Postfix(EscapeMenu __instance)
{
//IL_0111: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Expected O, but got Unknown
if (!Plugin.GiveUpEnabled.Value)
{
return;
}
try
{
if ((Object)(object)((Component)__instance).GetComponent<GiveUpButtonController>() != (Object)null)
{
return;
}
Button[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<Button>(true);
if (componentsInChildren.Length == 0)
{
Plugin.Log.LogWarning((object)"[NoPitStops] EscapeMenu has no child Button — Give Up button skipped");
return;
}
Button val = null;
Button[] array = componentsInChildren;
foreach (Button val2 in array)
{
if ((Object)(object)((Component)val2).GetComponentInChildren<TMP_Text>(true) != (Object)null)
{
val = val2;
break;
}
}
if ((Object)(object)val == (Object)null)
{
Plugin.Log.LogWarning((object)("[NoPitStops] No TMP-bearing button in EscapeMenu; falling back to '" + ((Object)componentsInChildren[0]).name + "' (label may be unstyled)"));
val = componentsInChildren[0];
}
Transform parent = ((Component)val).transform.parent;
GameObject val3 = Object.Instantiate<GameObject>(((Component)val).gameObject, parent);
((Object)val3).name = "NoPitStops_GiveUpButton";
val3.transform.SetSiblingIndex(parent.childCount - 1);
Button component = val3.GetComponent<Button>();
ClearPersistentListeners((UnityEvent)(object)component.onClick);
((UnityEventBase)component.onClick).RemoveAllListeners();
ButtonClickedEvent onClick = component.onClick;
object obj = <>O.<0>__SendLocalVote;
if (obj == null)
{
UnityAction val4 = GiveUpVote.SendLocalVote;
<>O.<0>__SendLocalVote = val4;
obj = (object)val4;
}
((UnityEvent)onClick).AddListener((UnityAction)obj);
TMP_Text val5 = ReplaceLabelChild(val3);
GiveUpButtonController giveUpButtonController = ((Component)__instance).gameObject.AddComponent<GiveUpButtonController>();
giveUpButtonController.ButtonGo = val3;
giveUpButtonController.Button = component;
giveUpButtonController.TmpLabels = (TMP_Text[])((!((Object)(object)val5 != (Object)null)) ? ((Array)Array.Empty<TMP_Text>()) : ((Array)new TMP_Text[1] { val5 }));
giveUpButtonController.UiLabels = Array.Empty<Text>();
Plugin.Log.LogInfo((object)("[NoPitStops] Give Up button injected (template='" + ((Object)val).name + "', fresh-label-replaced)"));
}
catch (Exception arg)
{
Plugin.Log.LogError((object)$"[NoPitStops] Failed to inject Give Up button: {arg}");
}
}
private static TMP_Text ReplaceLabelChild(GameObject buttonGo)
{
//IL_005f: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_0118: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
//IL_020a: Unknown result type (might be due to invalid IL or missing references)
TMP_Text componentInChildren = buttonGo.GetComponentInChildren<TMP_Text>(true);
if ((Object)(object)componentInChildren == (Object)null)
{
Plugin.Log.LogWarning((object)"[NoPitStops] Cloned button has no TMP child — building label from scratch with no font carry-over");
return CreateFreshLabel(buttonGo.transform, null, 24f, (TextAlignmentOptions)514, default(LabelSnapshot));
}
GameObject gameObject = ((Component)componentInChildren).gameObject;
RectTransform component = gameObject.GetComponent<RectTransform>();
TMP_FontAsset font = componentInChildren.font;
float fontSize = componentInChildren.fontSize;
TextAlignmentOptions alignment = componentInChildren.alignment;
Transform parent = gameObject.transform.parent;
int siblingIndex = gameObject.transform.GetSiblingIndex();
RectTransformSnapshot rect = new RectTransformSnapshot
{
AnchorMin = (Vector2)(((Object)(object)component != (Object)null) ? component.anchorMin : new Vector2(0.5f, 0.5f)),
AnchorMax = (Vector2)(((Object)(object)component != (Object)null) ? component.anchorMax : new Vector2(0.5f, 0.5f)),
Pivot = (Vector2)(((Object)(object)component != (Object)null) ? component.pivot : new Vector2(0.5f, 0.5f)),
SizeDelta = (Vector2)(((Object)(object)component != (Object)null) ? component.sizeDelta : new Vector2(200f, 50f)),
AnchoredPosition = (((Object)(object)component != (Object)null) ? component.anchoredPosition : Vector2.zero)
};
LayoutElementSnapshot layoutElement = null;
LayoutElement component2 = gameObject.GetComponent<LayoutElement>();
if ((Object)(object)component2 != (Object)null)
{
layoutElement = new LayoutElementSnapshot
{
MinWidth = component2.minWidth,
MinHeight = component2.minHeight,
PreferredWidth = component2.preferredWidth,
PreferredHeight = component2.preferredHeight,
FlexibleWidth = component2.flexibleWidth,
FlexibleHeight = component2.flexibleHeight
};
}
Plugin.Log.LogInfo((object)$"[NoPitStops] Replacing label child '{((Object)gameObject).name}' (font={((font != null) ? ((Object)font).name : null)}, size={fontSize})");
Object.DestroyImmediate((Object)(object)gameObject);
LabelSnapshot layoutSnapshot = new LabelSnapshot
{
Rect = rect,
SiblingIndex = siblingIndex,
LayoutElement = layoutElement
};
return CreateFreshLabel(parent, font, fontSize, alignment, layoutSnapshot);
}
private static TMP_Text CreateFreshLabel(Transform parent, TMP_FontAsset font, float fontSize, TextAlignmentOptions alignment, LabelSnapshot layoutSnapshot)
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: 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)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: 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)
//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_007b: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0136: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("NoPitStops_Label", new Type[1] { typeof(RectTransform) });
val.transform.SetParent(parent, false);
if (layoutSnapshot.SiblingIndex >= 0)
{
val.transform.SetSiblingIndex(layoutSnapshot.SiblingIndex);
}
RectTransform component = val.GetComponent<RectTransform>();
RectTransformSnapshot rect = layoutSnapshot.Rect;
if (rect.SizeDelta != Vector2.zero || rect.AnchorMin != Vector2.zero || rect.AnchorMax != Vector2.zero)
{
component.anchorMin = rect.AnchorMin;
component.anchorMax = rect.AnchorMax;
component.pivot = rect.Pivot;
component.sizeDelta = rect.SizeDelta;
component.anchoredPosition = rect.AnchoredPosition;
}
else
{
component.anchorMin = Vector2.zero;
component.anchorMax = Vector2.one;
component.offsetMin = Vector2.zero;
component.offsetMax = Vector2.zero;
}
TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
if ((Object)(object)font != (Object)null)
{
((TMP_Text)val2).font = font;
}
((TMP_Text)val2).text = "GIVE UP DAY";
((TMP_Text)val2).fontSize = ((fontSize > 0f) ? fontSize : 24f);
((TMP_Text)val2).alignment = alignment;
((Graphic)val2).color = Color.white;
((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0;
((TMP_Text)val2).overflowMode = (TextOverflowModes)0;
((Graphic)val2).raycastTarget = false;
if (layoutSnapshot.LayoutElement != null)
{
LayoutElement obj = val.AddComponent<LayoutElement>();
obj.minWidth = layoutSnapshot.LayoutElement.MinWidth;
obj.minHeight = layoutSnapshot.LayoutElement.MinHeight;
obj.preferredWidth = layoutSnapshot.LayoutElement.PreferredWidth;
obj.preferredHeight = layoutSnapshot.LayoutElement.PreferredHeight;
obj.flexibleWidth = layoutSnapshot.LayoutElement.FlexibleWidth;
obj.flexibleHeight = layoutSnapshot.LayoutElement.FlexibleHeight;
}
try
{
((TMP_Text)val2).ForceMeshUpdate(true, true);
}
catch
{
}
return (TMP_Text)(object)val2;
}
private static void ClearPersistentListeners(UnityEvent ev)
{
if (ev == null)
{
return;
}
try
{
FieldInfo field = typeof(UnityEventBase).GetField("m_PersistentCalls", BindingFlags.Instance | BindingFlags.NonPublic);
if (!(field == null))
{
object value = field.GetValue(ev);
value?.GetType().GetMethod("Clear", BindingFlags.Instance | BindingFlags.Public)?.Invoke(value, null);
}
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("[NoPitStops] Couldn't clear persistent listeners: " + ex.Message));
}
}
}
[HarmonyPatch]
internal static class EscapeMenu_TrackOpen
{
private static readonly FieldInfo IsOpenField = AccessTools.Field(typeof(EscapeMenu), "_isOpen");
public static bool IsOpen { get; private set; }
public static EscapeMenu Current { get; private set; }
[HarmonyPostfix]
[HarmonyPatch(typeof(EscapeMenu), "ToggleEscapeMenu")]
private static void Postfix_Toggle(EscapeMenu __instance)
{
if (!(IsOpenField == null))
{
IsOpen = (bool)IsOpenField.GetValue(__instance);
Current = __instance;
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(EscapeMenu), "OnDisable")]
private static void Postfix_OnDisable(EscapeMenu __instance)
{
if ((Object)(object)Current == (Object)(object)__instance)
{
IsOpen = false;
Current = null;
}
}
}
[HarmonyPatch]
internal static class GameManager_SkipLoseStateScene
{
private const string LoseScene = "LoseStateScene";
private const string HomeScene = "HomeScene";
[HarmonyPrefix]
[HarmonyPatch(typeof(NetworkManager), "ServerChangeScene", new Type[] { typeof(string) })]
private static void Prefix(ref string newSceneName)
{
if (!Plugin.SkipPitOnFullLoss.Value || newSceneName != "LoseStateScene" || !NetworkServer.active)
{
return;
}
Plugin.Log.LogInfo((object)"[NoPitStops] Full-loss detected; redirecting 'LoseStateScene' → 'HomeScene' and resetting save");
if (Plugin.ShowToasts.Value)
{
Toast.Show("PIT SKIP — redirecting LoseStateScene → HomeScene");
}
SaveManager instance = NetworkSingleton<SaveManager>.Instance;
if ((Object)(object)instance != (Object)null)
{
try
{
instance.ResetCurrentSaveToDefaults();
instance.LoadGame();
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("[NoPitStops] Save reset during pit-skip failed: " + ex.Message + ". Letting scene change proceed anyway."));
}
}
else
{
Plugin.Log.LogWarning((object)"[NoPitStops] SaveManager singleton not available; skipping save reset (game may have stale data on next round)");
}
newSceneName = "HomeScene";
}
}
[HarmonyPatch]
internal static class SkipUI_AutoVote
{
private static readonly MethodInfo RequestSkipMethod = AccessTools.Method(typeof(SkipUI), "RequestSkip", (Type[])null, (Type[])null);
[HarmonyPostfix]
[HarmonyPatch(typeof(SkipUI), "SetSkippableForLocal")]
private static void OnLocalSkipEnabled(SkipUI __instance)
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
if (!Plugin.AutoVoteEnabled.Value)
{
Plugin.Log.LogDebug((object)"[NoPitStops] AutoVote disabled in config; ignoring SkipUI ready");
return;
}
Scene scene = ((Component)__instance).gameObject.scene;
string name = ((Scene)(ref scene)).name;
Plugin.Log.LogInfo((object)$"[NoPitStops] SkipUI local-ready in scene '{name}'; auto-vote in {Plugin.AutoVoteDelay.Value:0.##}s");
((MonoBehaviour)__instance).StartCoroutine(VoteAfterDelay(__instance));
}
private static IEnumerator VoteAfterDelay(SkipUI instance)
{
yield return (object)new WaitForSeconds(Plugin.AutoVoteDelay.Value);
TryRequestSkip(instance);
}
internal static void TryRequestSkip(SkipUI instance)
{
if ((Object)(object)instance == (Object)null)
{
Plugin.Log.LogDebug((object)"[NoPitStops] SkipUI was destroyed before vote could send");
return;
}
if (!NetworkClient.active)
{
Plugin.Log.LogDebug((object)"[NoPitStops] NetworkClient inactive — not sending vote");
return;
}
if (RequestSkipMethod == null)
{
Plugin.Log.LogError((object)"[NoPitStops] SkipUI.RequestSkip not found via reflection — game updated?");
return;
}
try
{
RequestSkipMethod.Invoke(instance, new object[1]);
Plugin.Log.LogInfo((object)"[NoPitStops] Skip vote sent to server");
if (Plugin.ShowToasts.Value)
{
Toast.Show("Auto-voted skip");
}
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("[NoPitStops] RequestSkip invoke failed: " + ex.Message));
if (Plugin.ShowToasts.Value)
{
Toast.Show("Auto-vote FAILED: " + ex.Message);
}
}
}
}
}
namespace NoPitStops.GiveUp
{
[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct GiveUpRequestMsg : NetworkMessage
{
}
public struct GiveUpUpdateMsg : NetworkMessage
{
public int Votes;
public int Total;
public int Needed;
public bool Passed;
}
internal static class GiveUpVote
{
private static readonly HashSet<int> ServerVotedConnIds = new HashSet<int>();
private static GameState _lastObservedState = (GameState)0;
public static int CurrentVotes;
public static int CurrentTotal;
public static int CurrentNeeded;
public static bool VotePassed;
public static bool LocalVoted;
public static void InstallSerializers()
{
Writer<GiveUpRequestMsg>.write = delegate
{
};
Reader<GiveUpRequestMsg>.read = (NetworkReader _) => default(GiveUpRequestMsg);
Writer<GiveUpUpdateMsg>.write = delegate(NetworkWriter writer, GiveUpUpdateMsg msg)
{
NetworkWriterExtensions.WriteInt(writer, msg.Votes);
NetworkWriterExtensions.WriteInt(writer, msg.Total);
NetworkWriterExtensions.WriteInt(writer, msg.Needed);
NetworkWriterExtensions.WriteBool(writer, msg.Passed);
};
Reader<GiveUpUpdateMsg>.read = delegate(NetworkReader reader)
{
GiveUpUpdateMsg result = default(GiveUpUpdateMsg);
result.Votes = NetworkReaderExtensions.ReadInt(reader);
result.Total = NetworkReaderExtensions.ReadInt(reader);
result.Needed = NetworkReaderExtensions.ReadInt(reader);
result.Passed = NetworkReaderExtensions.ReadBool(reader);
return result;
};
Plugin.Log.LogInfo((object)"[NoPitStops] GiveUp serializers installed");
}
public static void RegisterServerHandlers()
{
try
{
NetworkServer.ReplaceHandler<GiveUpRequestMsg>((Action<NetworkConnectionToClient, GiveUpRequestMsg>)OnServerVote, true);
Plugin.Log.LogInfo((object)"[NoPitStops] GiveUp server handler registered");
}
catch (Exception arg)
{
Plugin.Log.LogError((object)$"[NoPitStops] GiveUp server handler registration failed: {arg}");
}
}
public static void RegisterClientHandlers()
{
try
{
NetworkClient.ReplaceHandler<GiveUpUpdateMsg>((Action<GiveUpUpdateMsg>)OnClientUpdate, true);
Plugin.Log.LogInfo((object)"[NoPitStops] GiveUp client handler registered");
}
catch (Exception arg)
{
Plugin.Log.LogError((object)$"[NoPitStops] GiveUp client handler registration failed: {arg}");
}
}
private static void OnServerVote(NetworkConnectionToClient conn, GiveUpRequestMsg _)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Invalid comparison between Unknown and I4
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
GameManager instance = NetworkSingleton<GameManager>.Instance;
if ((Object)(object)instance == (Object)null)
{
Plugin.Log.LogWarning((object)"[NoPitStops] GiveUp vote dropped — GameManager singleton not available");
return;
}
if ((int)instance.state != 1)
{
Plugin.Log.LogInfo((object)$"[NoPitStops] GiveUp vote dropped — state is {instance.state}, not Game");
return;
}
if (_lastObservedState != instance.state)
{
ServerVotedConnIds.Clear();
_lastObservedState = instance.state;
}
ServerVotedConnIds.Add(conn.connectionId);
int valueOrDefault = (MonoSingleton<LocalManager>.Instance?.players?.Count).GetValueOrDefault(1);
int num = Math.Max(1, Mathf.CeilToInt(Plugin.GiveUpThreshold.Value * (float)valueOrDefault));
bool flag = ServerVotedConnIds.Count >= num;
GiveUpUpdateMsg giveUpUpdateMsg = default(GiveUpUpdateMsg);
giveUpUpdateMsg.Votes = ServerVotedConnIds.Count;
giveUpUpdateMsg.Total = valueOrDefault;
giveUpUpdateMsg.Needed = num;
giveUpUpdateMsg.Passed = flag;
GiveUpUpdateMsg giveUpUpdateMsg2 = giveUpUpdateMsg;
NetworkServer.SendToAll<GiveUpUpdateMsg>(giveUpUpdateMsg2, 0, false);
Plugin.Log.LogInfo((object)$"[NoPitStops] GiveUp vote {giveUpUpdateMsg2.Votes}/{giveUpUpdateMsg2.Needed} (total {giveUpUpdateMsg2.Total}) — passed:{flag}");
if (flag)
{
ServerVotedConnIds.Clear();
instance.ProgressGame();
Plugin.Log.LogInfo((object)"[NoPitStops] GiveUp PASSED — calling GameManager.ProgressGame()");
}
}
private static void OnClientUpdate(GiveUpUpdateMsg msg)
{
CurrentVotes = msg.Votes;
CurrentTotal = msg.Total;
CurrentNeeded = msg.Needed;
VotePassed = msg.Passed;
if (Plugin.ShowToasts.Value)
{
Toast.Show(msg.Passed ? $"GIVE UP — vote passed ({msg.Votes}/{msg.Needed})" : $"Give Up vote: {msg.Votes}/{msg.Needed} (of {msg.Total})");
}
if (msg.Passed)
{
LocalVoted = false;
CurrentVotes = 0;
CurrentNeeded = 0;
}
}
public static void SendLocalVote()
{
if (!NetworkClient.active)
{
Toast.Show("Give Up: not connected");
return;
}
if (LocalVoted)
{
Toast.Show("Give Up: already voted");
return;
}
try
{
NetworkClient.Send<GiveUpRequestMsg>(default(GiveUpRequestMsg), 0);
LocalVoted = true;
Toast.Show("Give Up vote sent");
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("[NoPitStops] GiveUp send failed: " + ex.Message));
Toast.Show("Give Up failed: " + ex.Message);
}
}
}
}