using 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(); } 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(); 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(val2); ((Object)val3).name = "CruiserChargingStation_VanillaClone"; NetworkBehaviour[] componentsInChildren = val3.GetComponentsInChildren(true); foreach (NetworkBehaviour val4 in componentsInChildren) { Object.Destroy((Object)(object)val4); } NetworkObject[] componentsInChildren2 = val3.GetComponentsInChildren(true); foreach (NetworkObject val5 in componentsInChildren2) { Object.Destroy((Object)(object)val5); } val3.transform.SetParent(((Component)_vc).transform, false); _stationObj = val3; ApplyPlacement(); _stationAnimator = val3.GetComponentInChildren(true); _audioSource = val3.GetComponentInChildren(true); if ((Object)(object)_audioSource == (Object)null) { _audioSource = val3.AddComponent(); _audioSource.spatialBlend = 1f; _audioSource.playOnAwake = false; } Renderer[] componentsInChildren3 = val3.GetComponentsInChildren(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(); 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() != (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(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() != (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(); List list = new List(); 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(); 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(); 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(); _stationLight.type = (LightType)2; _stationLight.range = 2f; _stationLight.intensity = 0.6f; _stationLight.color = IdleColor; _audioSource = _stationObj.AddComponent(); _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(); 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(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() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } [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 InteractKeyConfig; internal static ConfigEntry ChargeRangeConfig; internal static ConfigEntry ChargeCooldownConfig; internal static ConfigEntry OffsetXConfig; internal static ConfigEntry OffsetYConfig; internal static ConfigEntry OffsetZConfig; internal static ConfigEntry OffsetYawConfig; internal static ConfigEntry ShowPromptConfig; internal static ConfigEntry ReuseShipChargeSoundConfig; internal static ConfigEntry 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("Keybinds", "InteractKey", "E", "Key used to charge the item currently in your hands (key name from UnityEngine.InputSystem.Key)."); ChargeRangeConfig = ((BaseUnityPlugin)this).Config.Bind("Charging", "ChargeRange", 2.5f, "How close (meters) you need to be to the charging station to use it."); ChargeCooldownConfig = ((BaseUnityPlugin)this).Config.Bind("Charging", "ChargeCooldown", 1f, "Minimum seconds between charges, to avoid accidental double-charging."); OffsetXConfig = ((BaseUnityPlugin)this).Config.Bind("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("Placement", "OffsetY", 0.8f, "Local Y offset (height) of the charging station relative to the cruiser."); OffsetZConfig = ((BaseUnityPlugin)this).Config.Bind("Placement", "OffsetZ", -0.1f, "Local Z offset (forward/back) of the charging station relative to the cruiser."); OffsetYawConfig = ((BaseUnityPlugin)this).Config.Bind("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("Visuals", "ShowPrompt", true, "Show an on-screen 'Press E to charge ' prompt when in range holding a chargeable item."); ReuseShipChargeSoundConfig = ((BaseUnityPlugin)this).Config.Bind("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("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."); } } }