using System;
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 CMF;
using DevModeQoL.Patches;
using HarmonyLib;
using PerfectRandom.Sulfur.Core;
using PerfectRandom.Sulfur.Core.DevTools;
using PerfectRandom.Sulfur.Core.Movement;
using PerfectRandom.Sulfur.Core.Units;
using UnityEngine;
using UnityEngine.InputSystem;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Dev Mode QoL")]
[assembly: AssemblyDescription("Developer-mode quality of life tweaks for SULFUR.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Dev Mode QoL")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("da9f75b7-f2c4-4de3-b0c0-8811f2bf5454")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace DevModeQoL
{
internal sealed class DevToolsEscapeInput : IDisposable
{
private InputAction closeAction;
private bool spawnMenuOpen;
private int lastCancelFrame = -1;
public void Initialize()
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Expected O, but got Unknown
if (closeAction == null)
{
closeAction = new InputAction("DevModeQoL/CloseDevTools", (InputActionType)1, "<Keyboard>/escape", (string)null, (string)null, (string)null);
closeAction.performed += OnClosePerformed;
closeAction.Enable();
}
}
public void OnFreeCameraStateChanged()
{
spawnMenuOpen = false;
}
public void OnSpawnMenuOpened(DevToolsManager manager)
{
spawnMenuOpen = (Object)(object)manager != (Object)null && manager.shouldShow;
}
public void OnSpawnMenuClosed()
{
spawnMenuOpen = false;
}
public void OnCancel(DevToolsManager manager, bool byGamepad)
{
if (!byGamepad && !((Object)(object)manager == (Object)null) && manager.shouldShow)
{
lastCancelFrame = Time.frameCount;
if (Plugin.CloseWithEscape.Value && DevToolsInternals.IsLevelSelectOpen(manager) == false)
{
Plugin.LogVerbose("Escape closed the dev tools spawn menu.");
manager.HideOptions();
}
}
}
private void OnClosePerformed(CallbackContext context)
{
if (Plugin.CloseWithEscape.Value)
{
DevToolsManager instance = StaticInstance<DevToolsManager>.Instance;
if (!((Object)(object)instance == (Object)null) && instance.shouldShow && !spawnMenuOpen && lastCancelFrame != Time.frameCount)
{
Plugin.LogVerbose("Escape closed the developer overlay.");
instance.Toggle(false);
}
}
}
public void Dispose()
{
if (closeAction != null)
{
closeAction.performed -= OnClosePerformed;
if (closeAction.enabled)
{
closeAction.Disable();
}
closeAction.Dispose();
closeAction = null;
}
}
}
internal static class DevToolsInternals
{
private static FieldInfo cameraControllerField;
private static FieldInfo levelPanelField;
private static bool resolved;
internal static ExtendedCameraController GetCameraController(DevToolsManager manager)
{
Resolve();
if ((Object)(object)manager == (Object)null || cameraControllerField == null)
{
return null;
}
try
{
object? value = cameraControllerField.GetValue(manager);
return (ExtendedCameraController)((value is ExtendedCameraController) ? value : null);
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Could not read the dev camera controller: " + ex.Message));
return null;
}
}
internal static bool? IsLevelSelectOpen(DevToolsManager manager)
{
Resolve();
if ((Object)(object)manager == (Object)null || levelPanelField == null)
{
return null;
}
try
{
object? value = levelPanelField.GetValue(manager);
Transform val = (Transform)((value is Transform) ? value : null);
if ((Object)(object)val == (Object)null)
{
return null;
}
return ((Component)val).gameObject.activeSelf;
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Could not read the level select state: " + ex.Message));
return null;
}
}
private static void Resolve()
{
if (!resolved)
{
resolved = true;
cameraControllerField = AccessTools.Field(typeof(DevToolsManager), "cameraController");
if (cameraControllerField == null)
{
Plugin.Log.LogWarning((object)"DevToolsManager.cameraController not found; using component lookup only.");
}
levelPanelField = AccessTools.Field(typeof(DevToolsManager), "levelPanel");
if (levelPanelField == null)
{
Plugin.Log.LogWarning((object)"DevToolsManager.levelPanel not found; Escape will not close the spawn menu.");
}
}
}
}
internal sealed class FreeCameraOrientation
{
private bool teleportRequested;
public void OnFreeCameraEnabled(DevToolsManager manager)
{
//IL_011d: 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_0134: 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)
teleportRequested = false;
if (!Plugin.InheritOrientationOnEnter.Value)
{
return;
}
Player player = GetPlayer();
if ((Object)(object)player == (Object)null)
{
return;
}
ExtendedCameraController val = ResolveDevCameraController(manager);
if ((Object)(object)val == (Object)null)
{
Plugin.Log.LogWarning((object)"Free camera controller not found; entry orientation left untouched.");
return;
}
Transform playerViewTransform = GetPlayerViewTransform(player);
if ((Object)(object)playerViewTransform == (Object)null)
{
Plugin.Log.LogWarning((object)"Player view transform not found; entry orientation left untouched.");
return;
}
if (Plugin.VerboseLogging.Value)
{
Plugin.Log.LogInfo((object)("Free camera entry: view " + Describe(playerViewTransform) + " " + Describe(playerViewTransform.forward) + ", cameraRoot " + (((Object)(object)player.cameraRoot != (Object)null) ? Describe(player.cameraRoot.forward) : "none") + ", target " + Describe(((Component)val).transform) + ", camera " + Describe(((Component)manager.GetCamera()).transform) + "."));
}
if (ApplyLookDirection(val, playerViewTransform.forward))
{
Plugin.LogVerbose("Free camera now faces " + Describe(((Component)val).transform.forward) + ".");
}
}
public void OnTeleportRequested()
{
if (!teleportRequested)
{
teleportRequested = true;
Plugin.LogVerbose("Teleport requested; the player view direction will follow the free camera on exit.");
}
}
public void OnFreeCameraDisabled(DevToolsManager manager)
{
//IL_0057: 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)
bool num = teleportRequested;
teleportRequested = false;
if (!num || !Plugin.InheritOrientationOnExit.Value)
{
return;
}
Camera camera = manager.GetCamera();
if (!((Object)(object)camera == (Object)null))
{
Player player = GetPlayer();
if (!((Object)(object)player == (Object)null) && !((Object)(object)player.playerCamController == (Object)null) && ApplyLookDirection(player.playerCamController, ((Component)camera).transform.forward))
{
Plugin.LogVerbose("Player view direction inherited from the free camera " + Describe(((Component)camera).transform.forward) + ".");
}
}
}
public void Reset()
{
teleportRequested = false;
}
private static Player GetPlayer()
{
GameManager instance = StaticInstance<GameManager>.Instance;
if ((Object)(object)instance == (Object)null)
{
return null;
}
Player playerScript = instance.PlayerScript;
if (!((Object)(object)playerScript != (Object)null))
{
return null;
}
return playerScript;
}
private static Transform GetPlayerViewTransform(Player player)
{
if ((Object)(object)player.playerCamController != (Object)null)
{
return ((Component)player.playerCamController).transform;
}
if (!((Object)(object)player.cameraControls != (Object)null))
{
return null;
}
return player.cameraControls;
}
private static string Describe(Vector3 direction)
{
return "(" + direction.x.ToString("0.00") + ", " + direction.y.ToString("0.00") + ", " + direction.z.ToString("0.00") + ")";
}
private static string Describe(Transform transform)
{
string text = ((Object)transform).name;
Transform parent = transform.parent;
while ((Object)(object)parent != (Object)null)
{
text = ((Object)parent).name + "/" + text;
parent = parent.parent;
}
return text;
}
private static ExtendedCameraController ResolveDevCameraController(DevToolsManager manager)
{
Camera camera = manager.GetCamera();
if ((Object)(object)camera == (Object)null)
{
return null;
}
ExtendedCameraController val = ((Component)camera).GetComponent<ExtendedCameraController>();
if ((Object)(object)val == (Object)null)
{
val = ((Component)camera).GetComponentInParent<ExtendedCameraController>();
}
if ((Object)(object)val == (Object)null)
{
val = DevToolsInternals.GetCameraController(manager);
}
return val;
}
private static bool ApplyLookDirection(ExtendedCameraController controller, Vector3 worldDirection)
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
if (((Vector3)(ref worldDirection)).sqrMagnitude < 1E-06f)
{
return false;
}
controller.RotateTowardDirection(ClampPitch(controller, ((Vector3)(ref worldDirection)).normalized), 0f);
return true;
}
private static Vector3 ClampPitch(ExtendedCameraController controller, Vector3 worldDirection)
{
//IL_0019: 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_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: 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_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: 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)
//IL_00a6: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
Transform parent = ((Component)controller).transform.parent;
Vector3 val = (((Object)(object)parent != (Object)null) ? parent.InverseTransformDirection(worldDirection) : worldDirection);
float num = (0f - Mathf.Asin(Mathf.Clamp(val.y, -1f, 1f))) * 57.29578f;
float num2 = Mathf.Clamp(num, 0f - ((CameraController)controller).upperVerticalLimit, ((CameraController)controller).lowerVerticalLimit);
if (Mathf.Abs(num2 - num) < 0.01f)
{
return worldDirection;
}
float num3 = Mathf.Atan2(val.x, val.z) * 57.29578f;
Vector3 val2 = Quaternion.Euler(num2, num3, 0f) * Vector3.forward;
if (!((Object)(object)parent != (Object)null))
{
return val2;
}
return parent.TransformDirection(val2);
}
}
[BepInPlugin("ryuka.sulfur.dev_mode_qol", "Dev Mode QoL", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "ryuka.sulfur.dev_mode_qol";
public const string PluginName = "Dev Mode QoL";
public const string PluginVersion = "1.0.0";
internal static ConfigEntry<bool> InheritOrientationOnEnter;
internal static ConfigEntry<bool> InheritOrientationOnExit;
internal static ConfigEntry<bool> CloseWithEscape;
internal static ConfigEntry<bool> VerboseLogging;
private Harmony harmony;
internal static ManualLogSource Log { get; private set; }
internal static FreeCameraOrientation Orientation { get; private set; }
internal static DevToolsEscapeInput EscapeInput { get; private set; }
private void Awake()
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
BindConfig();
Orientation = new FreeCameraOrientation();
EscapeInput = new DevToolsEscapeInput();
EscapeInput.Initialize();
harmony = new Harmony("ryuka.sulfur.dev_mode_qol");
harmony.PatchAll(typeof(DevToolsManagerPatches));
Log.LogInfo((object)"Dev Mode QoL v1.0.0 loaded.");
}
private void OnDestroy()
{
if (harmony != null)
{
harmony.UnpatchSelf();
harmony = null;
}
if (EscapeInput != null)
{
EscapeInput.Dispose();
EscapeInput = null;
}
Orientation = null;
}
private void BindConfig()
{
InheritOrientationOnEnter = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Free Camera", "InheritOrientationOnEnter", true, "Entering the developer free camera (F3) keeps the direction the player was looking at.");
InheritOrientationOnExit = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Free Camera", "InheritOrientationOnExit", true, "Leaving the developer free camera hands the camera direction back to the player, but only when the player was teleported (T) during that free camera session. Without a teleport the player returns to the original spot and keeps the original view direction.");
CloseWithEscape = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Menu", "CloseWithEscape", true, "Escape closes the developer overlay. While the spawn menu is open Escape closes that menu first, and while the level select list is open the game's own cancel handling closes the list first.");
VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Diagnostics", "VerboseLogging", false, "Log every camera hand-over and overlay close. Diagnostics only - does not change behaviour.");
}
internal static void LogVerbose(string message)
{
if (VerboseLogging != null && VerboseLogging.Value && Log != null)
{
Log.LogInfo((object)message);
}
}
}
}
namespace DevModeQoL.Patches
{
[HarmonyPatch(typeof(DevToolsManager))]
internal static class DevToolsManagerPatches
{
[HarmonyPostfix]
[HarmonyPatch("TurnOn")]
private static void TurnOnPostfix(DevToolsManager __instance)
{
if (!((Object)(object)__instance == (Object)null) && __instance.shouldShow)
{
Run("TurnOn", delegate
{
Plugin.EscapeInput.OnFreeCameraStateChanged();
Plugin.Orientation.OnFreeCameraEnabled(__instance);
});
}
}
[HarmonyPostfix]
[HarmonyPatch("TurnOff")]
private static void TurnOffPostfix(DevToolsManager __instance)
{
if (!((Object)(object)__instance == (Object)null) && !__instance.shouldShow)
{
Run("TurnOff", delegate
{
Plugin.EscapeInput.OnFreeCameraStateChanged();
Plugin.Orientation.OnFreeCameraDisabled(__instance);
});
}
}
[HarmonyPostfix]
[HarmonyPatch("TeleportPlayer")]
private static void TeleportPlayerPostfix()
{
Run("TeleportPlayer", delegate
{
Plugin.Orientation.OnTeleportRequested();
});
}
[HarmonyPostfix]
[HarmonyPatch("ShowOptions")]
private static void ShowOptionsPostfix(DevToolsManager __instance)
{
Run("ShowOptions", delegate
{
Plugin.EscapeInput.OnSpawnMenuOpened(__instance);
});
}
[HarmonyPostfix]
[HarmonyPatch("HideOptions")]
private static void HideOptionsPostfix()
{
Run("HideOptions", delegate
{
Plugin.EscapeInput.OnSpawnMenuClosed();
});
}
[HarmonyPrefix]
[HarmonyPatch("SpecialCancel")]
private static void SpecialCancelPrefix(DevToolsManager __instance, bool byGamepad)
{
Run("SpecialCancel", delegate
{
Plugin.EscapeInput.OnCancel(__instance, byGamepad);
});
}
private static void Run(string hook, Action action)
{
if (Plugin.Orientation == null || Plugin.EscapeInput == null)
{
return;
}
try
{
action();
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Dev Mode QoL hook '" + hook + "' failed: " + ex));
}
}
}
}