Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CruiserChargingStation v1.0.0
CruiserChargingStation.dll
Decompiled 6 hours agousing System; using System.Collections; using System.Collections.Generic; 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 GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("CruiserChargingStation")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("CruiserChargingStation")] [assembly: AssemblyTitle("CruiserChargingStation")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 CruiserChargingStation { public class ChargingStationController : MonoBehaviour { private VehicleController _vc; private GameObject _stationObj; private Renderer _stationRenderer; private Light _stationLight; private AudioSource _audioSource; private Animator _stationAnimator; private float _lastChargeTime = -999f; private static readonly Color IdleColor = new Color(0.15f, 0.45f, 0.9f); private static readonly Color FlashColor = new Color(0.6f, 0.9f, 1f); private bool _showPrompt; private string _promptItemName; private void Awake() { _vc = ((Component)this).GetComponent<VehicleController>(); } private void Start() { if (Plugin.UseVanillaModelConfig.Value) { SpawnVanillaStation(); } if ((Object)(object)_stationObj == (Object)null) { SpawnPlaceholderStation(); } if ((Object)(object)_audioSource == (Object)null || (Object)(object)_audioSource.clip == (Object)null) { TryBorrowShipChargeSound(); } } private void OnDestroy() { if ((Object)(object)_stationObj != (Object)null) { Object.Destroy((Object)(object)_stationObj); } } private void SpawnVanillaStation() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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) ItemCharger val = Object.FindObjectOfType<ItemCharger>(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"[CruiserChargingStation] Could not find the ship's ItemCharger to clone; falling back to the placeholder model."); return; } Vector3 position = ((Component)val).transform.position; LogHierarchy("shipCharger.gameObject", ((Component)val).gameObject); if ((Object)(object)((Component)val).transform.parent != (Object)null) { LogHierarchy("shipCharger.transform.parent", ((Component)((Component)val).transform.parent).gameObject); } GameObject val2 = FindNearestVisibleRenderer(position, 4f); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)"[CruiserChargingStation] No visible renderer found near the charging station trigger; falling back to the placeholder model."); return; } GameObject val3 = Object.Instantiate<GameObject>(val2); ((Object)val3).name = "CruiserChargingStation_VanillaClone"; NetworkBehaviour[] componentsInChildren = val3.GetComponentsInChildren<NetworkBehaviour>(true); foreach (NetworkBehaviour val4 in componentsInChildren) { Object.Destroy((Object)(object)val4); } NetworkObject[] componentsInChildren2 = val3.GetComponentsInChildren<NetworkObject>(true); foreach (NetworkObject val5 in componentsInChildren2) { Object.Destroy((Object)(object)val5); } val3.transform.SetParent(((Component)_vc).transform, false); _stationObj = val3; ApplyPlacement(); _stationAnimator = val3.GetComponentInChildren<Animator>(true); _audioSource = val3.GetComponentInChildren<AudioSource>(true); if ((Object)(object)_audioSource == (Object)null) { _audioSource = val3.AddComponent<AudioSource>(); _audioSource.spatialBlend = 1f; _audioSource.playOnAwake = false; } Renderer[] componentsInChildren3 = val3.GetComponentsInChildren<Renderer>(true); Plugin.Log.LogInfo((object)$"[CruiserChargingStation] Clone spawned from '{((Object)val2).name}': renderers={componentsInChildren3.Length}, animator={(Object)(object)_stationAnimator != (Object)null}, audioSource={(Object)(object)_audioSource != (Object)null}"); Renderer[] array = componentsInChildren3; foreach (Renderer val6 in array) { Plugin.Log.LogInfo((object)$"[CruiserChargingStation] renderer '{((Object)((Component)val6).gameObject).name}' enabled={val6.enabled} activeInHierarchy={((Component)val6).gameObject.activeInHierarchy}"); } bool flag = false; Renderer[] array2 = componentsInChildren3; foreach (Renderer val7 in array2) { if (val7.enabled && ((Component)val7).gameObject.activeInHierarchy) { flag = true; break; } } if (!flag) { Plugin.Log.LogWarning((object)"[CruiserChargingStation] Clone has no enabled/visible renderers - falling back to the placeholder model instead."); Object.Destroy((Object)(object)val3); _stationObj = null; _stationAnimator = null; _audioSource = null; } else { if ((Object)(object)_stationAnimator == (Object)null) { Plugin.Log.LogWarning((object)"[CruiserChargingStation] Cloned station has no Animator - the zap animation won't play, only the sound/charge."); } Plugin.Log.LogInfo((object)"[CruiserChargingStation] Using the vanilla charging station model."); } } private static GameObject FindNearestVisibleRenderer(Vector3 worldPos, float radius) { //IL_003e: 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_0140: Unknown result type (might be due to invalid IL or missing references) List<(Renderer, float)> list = new List<(Renderer, float)>(); Renderer[] array = Object.FindObjectsOfType<Renderer>(); foreach (Renderer val in array) { if (val.enabled && ((Component)val).gameObject.activeInHierarchy) { float num = Vector3.Distance(((Component)val).transform.position, worldPos); if (num <= radius) { list.Add((val, num)); } } } list.Sort(((Renderer renderer, float dist) a, (Renderer renderer, float dist) b) => a.dist.CompareTo(b.dist)); Plugin.Log.LogInfo((object)$"[CruiserChargingStation] Found {list.Count} enabled renderer(s) within {radius}m of the charging trigger:"); foreach (var (val2, num2) in list) { Plugin.Log.LogInfo((object)string.Format("[CruiserChargingStation] '{0}' (parent: '{1}') dist={2:0.00}m bounds={3}", ((Object)((Component)val2).gameObject).name, ((Object)(object)((Component)val2).transform.parent != (Object)null) ? ((Object)((Component)val2).transform.parent).name : "-", num2, val2.bounds)); } if (list.Count == 0) { return null; } foreach (var item3 in list) { Renderer item = item3.Item1; if (((Object)((Component)item).gameObject).name.IndexOf("ChargeStation", StringComparison.OrdinalIgnoreCase) >= 0 && (Object)(object)((Component)item).gameObject.GetComponent<Animator>() != (Object)null) { Plugin.Log.LogInfo((object)("[CruiserChargingStation] Selected '" + ((Object)((Component)item).gameObject).name + "' by name+Animator match.")); return ((Component)item).gameObject; } } foreach (var item4 in list) { Renderer item2 = item4.Item1; Transform val3 = (((Object)(object)((Component)item2).transform.root == (Object)(object)((Component)item2).transform) ? ((Component)item2).transform : FindReasonableRoot(((Component)item2).transform)); int num3 = ((Component)val3).GetComponentsInChildren<Renderer>(true).Length; if (num3 > 1 && num3 <= 15) { Plugin.Log.LogInfo((object)$"[CruiserChargingStation] Selected '{((Object)val3).name}' as a reasonably-sized parent ({num3} renderers)."); return ((Component)val3).gameObject; } } Plugin.Log.LogInfo((object)("[CruiserChargingStation] Falling back to closest single renderer: '" + ((Object)((Component)list[0].Item1).gameObject).name + "'.")); return ((Component)list[0].Item1).gameObject; } private static Transform FindReasonableRoot(Transform t) { Transform val = t; for (int i = 0; i < 3; i++) { if ((Object)(object)val.parent == (Object)null) { break; } if ((Object)(object)((Component)val.parent).GetComponent<NetworkObject>() != (Object)null) { break; } val = val.parent; } return val; } private static void LogHierarchy(string label, GameObject root) { Plugin.Log.LogInfo((object)("[CruiserChargingStation] --- Hierarchy dump: " + label + " = '" + ((Object)root).name + "' ---")); DumpRecursive(root.transform, 0); } private static void DumpRecursive(Transform t, int depth) { string text = new string(' ', depth * 2); Component[] components = ((Component)t).GetComponents<Component>(); List<string> list = new List<string>(); Component[] array = components; foreach (Component val in array) { if ((Object)(object)val != (Object)null) { list.Add(((object)val).GetType().Name); } } Plugin.Log.LogInfo((object)string.Format("[CruiserChargingStation] {0}{1} [{2}] active={3}", text, ((Object)t).name, string.Join(", ", list), ((Component)t).gameObject.activeSelf)); for (int j = 0; j < t.childCount; j++) { DumpRecursive(t.GetChild(j), depth + 1); } } private void SpawnPlaceholderStation() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) _stationObj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)_stationObj).name = "CruiserChargingStation_Placeholder"; _stationObj.transform.SetParent(((Component)_vc).transform, false); Collider component = _stationObj.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } _stationObj.transform.localScale = new Vector3(0.4f, 0.5f, 0.3f); ApplyPlacement(); _stationRenderer = _stationObj.GetComponent<Renderer>(); Shader val = Shader.Find("HDRP/Lit") ?? Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard"); Material material = new Material(val); _stationRenderer.material = material; SetColor(IdleColor); GameObject val2 = new GameObject("Light"); val2.transform.SetParent(_stationObj.transform, false); val2.transform.localPosition = new Vector3(0f, 0.3f, 0f); _stationLight = val2.AddComponent<Light>(); _stationLight.type = (LightType)2; _stationLight.range = 2f; _stationLight.intensity = 0.6f; _stationLight.color = IdleColor; _audioSource = _stationObj.AddComponent<AudioSource>(); _audioSource.spatialBlend = 1f; _audioSource.playOnAwake = false; _audioSource.volume = 0.8f; } private void ApplyPlacement() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_stationObj == (Object)null)) { _stationObj.transform.localPosition = new Vector3(Plugin.OffsetXConfig.Value, Plugin.OffsetYConfig.Value, Plugin.OffsetZConfig.Value); _stationObj.transform.localRotation = Quaternion.Euler(0f, Plugin.OffsetYawConfig.Value, 0f); } } private void TryBorrowShipChargeSound() { if (Plugin.ReuseShipChargeSoundConfig.Value) { ItemCharger val = Object.FindObjectOfType<ItemCharger>(); if ((Object)(object)val != (Object)null && (Object)(object)val.zapAudio != (Object)null && (Object)(object)val.zapAudio.clip != (Object)null) { _audioSource.clip = val.zapAudio.clip; Plugin.Log.LogInfo((object)"[CruiserChargingStation] Reusing the ship's charge station sound effect."); } else { Plugin.Log.LogWarning((object)"[CruiserChargingStation] Could not find the ship's ItemCharger to borrow its sound; charging will be silent."); } } } private void Update() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_vc == (Object)null || (Object)(object)_stationObj == (Object)null) { return; } ApplyPlacement(); if (!TryGetLocalPlayer(out var player)) { return; } float num = Vector3.Distance(((Component)player).transform.position, _stationObj.transform.position); bool flag = num <= Plugin.ChargeRangeConfig.Value; GrabbableObject currentlyHeldObjectServer = player.currentlyHeldObjectServer; bool flag2 = (Object)(object)currentlyHeldObjectServer != (Object)null && (Object)(object)currentlyHeldObjectServer.itemProperties != (Object)null && currentlyHeldObjectServer.itemProperties.requiresBattery; bool flag3 = Time.time - _lastChargeTime < Plugin.ChargeCooldownConfig.Value; bool flag4 = flag && flag2 && !flag3; if (Plugin.ShowPromptConfig.Value) { _showPrompt = flag && flag2 && !flag3; _promptItemName = (flag2 ? currentlyHeldObjectServer.itemProperties.itemName : null); } else { _showPrompt = false; } if (flag4 && TryGetKey(Plugin.InteractKeyConfig.Value, out var key)) { Keyboard current = Keyboard.current; if (current != null && ((ButtonControl)current[key]).wasPressedThisFrame) { ChargeHeldItem(currentlyHeldObjectServer); } } } private static bool TryGetLocalPlayer(out PlayerControllerB player) { player = null; try { player = GameNetworkManager.Instance.localPlayerController; return (Object)(object)player != (Object)null; } catch { return false; } } private static bool TryGetKey(string name, out Key key) { return Enum.TryParse<Key>(name, ignoreCase: true, out key); } private void ChargeHeldItem(GrabbableObject item) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown _lastChargeTime = Time.time; item.insertedBattery = new Battery(false, 1f); item.SyncBatteryServerRpc(100); if ((Object)(object)_audioSource != (Object)null && (Object)(object)_audioSource.clip != (Object)null) { _audioSource.PlayOneShot(_audioSource.clip); } ((MonoBehaviour)this).StopAllCoroutines(); ((MonoBehaviour)this).StartCoroutine(FlashRoutine()); Plugin.Log.LogInfo((object)("[CruiserChargingStation] Charged '" + item.itemProperties.itemName + "'.")); } private IEnumerator FlashRoutine() { if ((Object)(object)_stationAnimator != (Object)null) { _stationAnimator.SetTrigger("zap"); } else { SetColor(FlashColor); if ((Object)(object)_stationLight != (Object)null) { _stationLight.intensity = 2.5f; } } yield return (object)new WaitForSeconds(0.3f); if ((Object)(object)_stationAnimator == (Object)null) { SetColor(IdleColor); if ((Object)(object)_stationLight != (Object)null) { _stationLight.intensity = 0.6f; } } } private void SetColor(Color c) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_stationRenderer == (Object)null)) { Material material = _stationRenderer.material; material.color = c; if (material.HasProperty("_EmissiveColor")) { material.SetColor("_EmissiveColor", c * 1.5f); } if (material.HasProperty("_EmissionColor")) { material.SetColor("_EmissionColor", c * 1.5f); } } } private void OnGUI() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_0084: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (_showPrompt) { string text = (string.IsNullOrEmpty(_promptItemName) ? ("Press " + Plugin.InteractKeyConfig.Value + " to charge") : ("Press " + Plugin.InteractKeyConfig.Value + " to charge " + _promptItemName)); GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 20, alignment = (TextAnchor)4, fontStyle = (FontStyle)1 }; val.normal.textColor = Color.white; GUI.Label(new Rect((float)Screen.width / 2f - 250f, (float)(Screen.height - 140), 500f, 30f), text, val); } } } [HarmonyPatch(typeof(VehicleController))] internal static class VehicleControllerPatches { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void Awake_Postfix(VehicleController __instance) { if ((Object)(object)((Component)__instance).GetComponent<ChargingStationController>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ChargingStationController>(); } } } [BepInPlugin("justrat.cruiserchargingstation", "Cruiser Charging Station", "1.5.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "justrat.cruiserchargingstation"; public const string PluginName = "Cruiser Charging Station"; public const string PluginVersion = "1.5.0"; internal static ManualLogSource Log; internal static ConfigEntry<string> InteractKeyConfig; internal static ConfigEntry<float> ChargeRangeConfig; internal static ConfigEntry<float> ChargeCooldownConfig; internal static ConfigEntry<float> OffsetXConfig; internal static ConfigEntry<float> OffsetYConfig; internal static ConfigEntry<float> OffsetZConfig; internal static ConfigEntry<float> OffsetYawConfig; internal static ConfigEntry<bool> ShowPromptConfig; internal static ConfigEntry<bool> ReuseShipChargeSoundConfig; internal static ConfigEntry<bool> UseVanillaModelConfig; private void Awake() { //IL_016d: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; InteractKeyConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Keybinds", "InteractKey", "E", "Key used to charge the item currently in your hands (key name from UnityEngine.InputSystem.Key)."); ChargeRangeConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Charging", "ChargeRange", 2.5f, "How close (meters) you need to be to the charging station to use it."); ChargeCooldownConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Charging", "ChargeCooldown", 1f, "Minimum seconds between charges, to avoid accidental double-charging."); OffsetXConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "OffsetX", -1.1f, "Local X offset of the charging station relative to the cruiser (confirmed good spot: left side, near the window)."); OffsetYConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "OffsetY", 0.8f, "Local Y offset (height) of the charging station relative to the cruiser."); OffsetZConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "OffsetZ", -0.1f, "Local Z offset (forward/back) of the charging station relative to the cruiser."); OffsetYawConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "OffsetYaw", 180f, "Extra rotation (degrees) applied to the charging station around the vertical axis, so it faces into the cabin."); ShowPromptConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "ShowPrompt", true, "Show an on-screen 'Press E to charge <item>' prompt when in range holding a chargeable item."); ReuseShipChargeSoundConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "ReuseShipChargeSound", true, "Try to reuse the ship's own charging station zap sound instead of playing no sound at all."); UseVanillaModelConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "UseVanillaModel", true, "Clone the ship's real charging station model/animation instead of using a simple placeholder cube."); new Harmony("justrat.cruiserchargingstation").PatchAll(); Log.LogInfo((object)"Cruiser Charging Station v1.5.0 loaded."); } } }