Button has no function

The button that used to activate/deactivate the radio is not functioning anymore.
Instead one has to look back to directly do it via the radio itself.

I'm okay with the player looking back and turning the radio ON (you need anyways to look behind and change the station). That said, I couldn't understand what was the usage of the button when I initially installed the plugin because it wasn't working, as you confirmed. I'd like to know if I can delete the button from spawning on vehicles with the radio. Can you usually access the radio on all of the cars with your back (free look)?

I can only say that one can reach it on the mini. I didn't try any other vehicles.
The button always worked for me except that one had to push it twice after changing the radio station.
But now it has become useless and could just go I think.

CarRadio.cs

using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Oxide.Core; using Oxide.Core.Libraries.Covalence; using Rust.Instruments; using System.Collections.Generic; using System.Linq; using UnityEngine; using static InstrumentKeyController; namespace Oxide.Plugins { [Info("Car Radio", "TCM420G", "1.0.68")] [Description("Allows players to attach radios to vehicles")] class CarRadio : CovalencePlugin { #region variables private const string PERMISSION_ATTACHRADIO = "carradio.attachcarradio"; private const string PERMISSION_DETACHRADIO = "carradio.detachcarradio"; private const string PERMISSION_ATTACHRADIO_GLOBAL = "carradio.attachallcarradio"; private const string PERMISSION_DETACHRADIO_GLOBAL = "carradio.detachallcarradio"; private const string I18N_MISSING_SIREN = "NoRadioForName"; private const string I18N_COULD_NOT_ATTACH = "CouldNotAttach"; private const string I18N_NOT_SUPPORTED = "NotSupported"; private const string I18N_ATTACHED = "Attached"; private const string I18N_ATTACHED_GLOBAL = "AttachedGlobal"; private const string I18N_DETACHED = "Detached"; private const string I18N_DETACHED_GLOBAL = "DetachedGlobal"; private const string I18N_NOT_A_VEHICLE = "NotAVehicle"; private const string I18N_RADIO = "Radios"; private const string I18N_PLAYERS_ONLY = "PlayersOnly"; // Initial prefabs private const string PREFAB_COCKPIT = "assets/content/vehicles/modularcar/module_entities/1module_cockpit.prefab"; private const string PREFAB_COCKPIT_ARMORED = "assets/content/vehicles/modularcar/module_entities/1module_cockpit_armored.prefab"; private const string PREFAB_COCKPIT_WITH_ENGINE = "assets/content/vehicles/modularcar/module_entities/1module_cockpit_with_engine.prefab"; private const string PREFAB_BUTTON = null; private const string PREFAB_FLASHERLIGHT = null; private const string PREFAB_SIRENLIGHT = null; private const string PREFAB_SPOTLIGHT = null; private const string PREFAB_RADIO = "assets/prefabs/voiceaudio/boombox/boombox.static.prefab"; // Vehicles private const string PREFAB_KAYAK = "assets/content/vehicles/boats/kayak/kayak.prefab"; private const string PREFAB_TUGBOAT = "assets/content/vehicles/boats/tugboat/tugboat.prefab"; private const string PREFAB_ROWBOAT = "assets/content/vehicles/boats/rowboat/rowboat.prefab"; private const string PREFAB_RHIB = "assets/content/vehicles/boats/rhib/rhib.prefab"; private const string PREFAB_SEDAN = "assets/content/vehicles/sedan_a/sedantest.entity.prefab"; private const string PREFAB_SEDANRAIL = "assets/content/vehicles/sedan_a/sedanrail.entity.prefab"; private const string PREFAB_MINICOPTER = "assets/content/vehicles/minicopter/minicopter.entity.prefab"; private const string PREFAB_ATTACKHELI = "assets/content/vehicles/attackhelicopter/attackhelicopter.entity.prefab"; private const string PREFAB_TRANSPORTHELI = "assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab"; private const string PREFAB_CHINOOK = "assets/prefabs/npc/ch47/ch47.entity.prefab"; private const string PREFAB_MAGNETCRANE = "assets/content/vehicles/crane_magnet/magnetcrane.entity.prefab"; private const string PREFAB_SUBMARINESOLO = "assets/content/vehicles/submarine/submarinesolo.entity.prefab"; private const string PREFAB_SUBMARINEDUO = "assets/content/vehicles/submarine/submarineduo.entity.prefab"; private const string PREFAB_SNOWMOBILE = "assets/content/vehicles/snowmobiles/snowmobile.prefab"; private const string PREFAB_SNOWMOBILETOMAHA = "assets/content/vehicles/snowmobiles/tomahasnowmobile.prefab"; // Train Engine private const string PREFAB_WORKCART = "assets/content/vehicles/trains/workcart/workcart.entity.prefab"; private const string PREFAB_TRAINENGINE = "assets/content/vehicles/trains/workcart/workcart_aboveground.entity.prefab"; private const string PREFAB_TRAINENGINE_COVERED = "assets/content/vehicles/trains/workcart/workcart_aboveground2.entity.prefab"; private const string PREFAB_TRAINENGINE_LOCOMOTIVE = "assets/content/vehicles/trains/locomotive/locomotive.entity.prefab"; // Other Vehicles private const string PREFAB_HORSE = "assets/rust.ai/nextai/testridablehorse.prefab"; private const string PREFAB_HOTAIRBALLOON = "assets/prefabs/deployable/hot air balloon/hotairballoon.prefab"; private const string KEY_MODULAR_CAR = "MODULAR_CAR"; private const string DATAPATH_RADIO = "carradio/"; // Preconfigured carradio private static readonly Radio SIREN_DEFAULT = new Radio("Car-Radio", new Dictionary<string, Attachment[]> { [PREFAB_COCKPIT] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(-0.00f, 0.58f, 0.2f), new Vector3(330f, 180f, 0f)) }, [PREFAB_COCKPIT_ARMORED] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(-0.00f, 0.58f, 0.2f), new Vector3(330f, 180f, 0f)) }, [PREFAB_COCKPIT_WITH_ENGINE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(-0.00f, 0.58f, 0.2f), new Vector3(330f, 180f, 0f)) } }, new Dictionary<string, Attachment[]> { [PREFAB_KAYAK] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(-0.1f, 0.25f, -0.7f), new Vector3(0f, 0f, 0f)) }, [PREFAB_ROWBOAT] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.8f, 2.18f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TUGBOAT] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 8.0f, 0.7f), new Vector3(0f, 0f, 0f)), // Cockpit Radio new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.0f, 5.3f), new Vector3(0f, 180f, 0f)), // Lower Deck Radio new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.75f, -10.5f), new Vector3(0f, 0f, 0f)) // Outer Deck Radio }, [PREFAB_RHIB] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.83f, 0.62f), new Vector3(0f, 180f, 0f)) }, [PREFAB_SEDAN] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.53f, 1.25f), new Vector3(315f, 180f, 0f)) }, [PREFAB_SEDANRAIL] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.76f, 0.48f), new Vector3(315f, 180f, 0f)) }, [PREFAB_MINICOPTER] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.4f, 0.0f), new Vector3(0f, 0f, 0f)) }, [PREFAB_ATTACKHELI] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.15f, -0.45f), new Vector3(0f, 0f, 0f)) }, [PREFAB_TRANSPORTHELI] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.51f, 1.53f), new Vector3(90f, 0f, 0f)) }, [PREFAB_CHINOOK] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 3.5f, -1.6f), new Vector3(100f, 180f, 0f)), // Back Radio new Attachment(PREFAB_RADIO, new Vector3(0.0f, 3.4f, 6.35f), new Vector3(90f, 0f, 0f)) // Cockpit Radio }, [PREFAB_MAGNETCRANE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.95f, 3.5f, 0.0f), new Vector3(0f, 180f, 0f), "Top") }, [PREFAB_SUBMARINESOLO] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.85f, 0.45f), new Vector3(0f, 180f, 0f)) }, [PREFAB_SUBMARINEDUO] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.25f, -1.48f), new Vector3(0f, 0f, 0f)) }, [PREFAB_SNOWMOBILE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.9f, -1.1f), new Vector3(0f, 0f, 0f)) }, [PREFAB_SNOWMOBILETOMAHA] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.5f, -1.25f), new Vector3(0f, 0f, 0f)) }, [PREFAB_WORKCART] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.7f, 2.6f, 0.0f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TRAINENGINE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.7f, 2.6f, 0.0f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TRAINENGINE_COVERED] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.7f, 2.6f, 0.0f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TRAINENGINE_LOCOMOTIVE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(1.1f, 3.5f, 3.34f), new Vector3(0f, 0f, 0f)), // Cockpit Radio new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.55f, -9.4f), new Vector3(0f, 180f, 0f)) // Back Radio }, [PREFAB_HORSE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.55f, -0.8f), new Vector3(260f, 0f, 180f)) }, [PREFAB_HOTAIRBALLOON] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.4f, 0.0f), new Vector3(0f, 0f, 0f)) } }, new Tone(Notes.A, NoteType.Regular, 4, 1f), new Tone(Notes.D, NoteType.Regular, 5, 1f)); private static readonly Radio SIREN_SILENT = new Radio("test-radio", new Dictionary<string, Attachment[]> { //Erased Dictionary to remove bulk since it was a duplicate of the above }, new Dictionary<string, Attachment[]> { //Erased Dictionary to remove bulk since it was a duplicate of the above }); #endregion variables #region data private class DataContainer { // Map BaseVehicle.net.ID -> RadioInfos public Dictionary<ulong, VehicleContainer> VehicleRadioMap = new Dictionary<ulong, VehicleContainer>(); } private class VehicleContainer { public string RadioName = SIREN_DEFAULT.Name; public RadioController.States State = RadioController.States.OFF; public HashSet NetIDs = new HashSet(); public VehicleContainer() { } public VehicleContainer(string aRadioName, RadioController.States aState, IEnumerable someNetIDs) { RadioName = aRadioName; State = aState; NetIDs.UnionWith(someNetIDs); } } #endregion data #region configuration private Configuration config; private IDictionary<string, Radio> RadioDictionary { get; } = new Dictionary<string, Radio>(); private class Configuration { [JsonProperty("MountNeeded")] public bool MountNeeded = true; [JsonProperty("SoundEnabled")] public bool SoundEnabled = true; [JsonProperty("RadioSpawnProbability")] public Dictionary<string, float> RadioSpawnProbability = new Dictionary<string, float> { [KEY_MODULAR_CAR] = 0f, [PREFAB_HORSE] = 0f, [PREFAB_MINICOPTER] = 0f, [PREFAB_SEDAN] = 0f, [PREFAB_TRANSPORTHELI] = 0f }; [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("DefaultState")] public RadioController.States DefaultState = RadioController.States.OFF; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson()); } private class Tone { public Tone(Notes aNote = Notes.A, NoteType aNoteType = NoteType.Regular, int anOctave = 4, float aDuration = 1f) { Note = aNote; NoteType = aNoteType; Octave = anOctave; Duration = aDuration; } [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("Note")] public Notes Note; [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("NoteType")] public NoteType NoteType; [JsonProperty("Octave")] public int Octave; [JsonProperty("Duration")] public float Duration; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson()); } private class Radio { public Radio(string aName, Dictionary<string, Attachment[]> someModules, Dictionary<string, Attachment[]> someVehicles, params Tone[] someTones) { Name = aName; Modules = someModules; Vehicles = someVehicles; Tones = someTones; } [JsonProperty("Name")] public string Name; [JsonProperty("Tones")] public Tone[] Tones; [JsonProperty("Modules")] public Dictionary<string, Attachment[]> Modules; [JsonProperty("Vehicles")] public Dictionary<string, Attachment[]> Vehicles; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson()); } private class Attachment { public Attachment(string aPrefab, Vector3 aPosition, Vector3 anAngle = new Vector3(), string aBone = null) { Prefab = aPrefab; Position = aPosition; Angle = anAngle; Bone = aBone; } [JsonProperty("Prefab")] public string Prefab; [JsonProperty("Position")] public Vector3 Position; [JsonProperty("Angle")] public Vector3 Angle; [JsonProperty("Bone")] public string Bone; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson()); } protected override void LoadDefaultConfig() { config = new Configuration(); RadioDictionary.Clear(); RadioDictionary.Add(SIREN_DEFAULT.Name, SIREN_DEFAULT); RadioDictionary.Add(SIREN_SILENT.Name, SIREN_SILENT); } protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) { throw new JsonException(); } try { foreach (string eachRadioFile in Interface.Oxide.DataFileSystem.GetFiles(DATAPATH_RADIO, "*.json")) { string theFilename = eachRadioFile.Basename(".json"); try { Radio theRadio = Interface.Oxide.DataFileSystem.ReadObject(DATAPATH_RADIO + theFilename); RadioDictionary.Add(theRadio.Name, theRadio); } catch { PrintWarning($"Radio file {theFilename}.json is invalid; ignoring"); } } } catch { } Puts("Loaded carradio: " + string.Join(", ", RadioDictionary.Keys)); if (RadioDictionary.IsEmpty()) { PrintWarning("Configuration appears to be missing carradio; using defaults"); RadioDictionary.Add(SIREN_DEFAULT.Name, SIREN_DEFAULT); RadioDictionary.Add(SIREN_SILENT.Name, SIREN_SILENT); SaveConfig(); } if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) { PrintWarning("Configuration appears to be outdated; updating and saving"); SaveConfig(); } } catch { PrintWarning($"Configuration file {Name}.json is invalid; using defaults"); LoadDefaultConfig(); } } protected override void SaveConfig() { PrintWarning($"Configuration changes saved to {Name}.json"); Config.WriteObject(config, true); foreach (Radio eachRadio in RadioDictionary.Values) { Interface.Oxide.DataFileSystem.WriteObject(DATAPATH_RADIO + eachRadio.Name, eachRadio); } } #endregion configuration #region localization protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary<string, string> { [I18N_MISSING_SIREN] = "No radio was found for the given name (using {0} instead)", [I18N_COULD_NOT_ATTACH] = "Could not attach '{0}'", [I18N_ATTACHED] = "Attached radio '{0}'", [I18N_ATTACHED_GLOBAL] = "Attached radio '{0}' to all existing cars", [I18N_DETACHED] = "Detached radio", [I18N_DETACHED_GLOBAL] = "Detached all existing carradio", [I18N_NOT_A_VEHICLE] = "This entity is not a (supported) vehicle", [I18N_RADIO] = "Available carradio: {0}", [I18N_PLAYERS_ONLY] = "Command '{0}' can only be used by a player", [I18N_NOT_SUPPORTED] = "The radio '{0}' has no configuration for '{1}'" }, this); } #endregion localization #region commands [Command("attachradio"), Permission(PERMISSION_ATTACHRADIO)] private void AttachCarRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { if (aPlayer.IsServer) { Message(aPlayer, I18N_PLAYERS_ONLY, aCommand); return; } BaseVehicle theVehicle = RaycastVehicle(aPlayer); if (theVehicle) { Radio theRadio = someArgs.Length > 0 ? FindRadioForName(someArgs[0], aPlayer) : RadioDictionary.Values.First(); AttachRadios(theVehicle, theRadio, config.DefaultState, aPlayer); Message(aPlayer, I18N_ATTACHED, theRadio.Name); } } [Command("removeradio"), Permission(PERMISSION_DETACHRADIO)] private void DetachCarRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { if (aPlayer.IsServer) { Message(aPlayer, I18N_PLAYERS_ONLY, aCommand); return; } BaseVehicle theVehicle = RaycastVehicle(aPlayer); if (theVehicle && DetachRadios(theVehicle)) { Message(aPlayer, I18N_DETACHED); } } [Command("attachallcarradio"), Permission(PERMISSION_ATTACHRADIO_GLOBAL)] private void AttachAllCarRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { Radio theRadio = someArgs.Length > 0 ? FindRadioForName(someArgs[0], aPlayer) : RadioDictionary.Values.First(); foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType()) { AttachRadios(eachVehicle, theRadio, config.DefaultState, aPlayer); } Message(aPlayer, I18N_ATTACHED_GLOBAL, theRadio.Name); } [Command("detachallcarradio"), Permission(PERMISSION_DETACHRADIO_GLOBAL)] private void DetachAllCarRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType()) { DetachRadios(eachVehicle); } Message(aPlayer, I18N_DETACHED_GLOBAL); } [Command("togglecarradio")] private void ToggleRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { if (aPlayer.IsServer) { Message(aPlayer, I18N_PLAYERS_ONLY, aCommand); return; } BasePlayer thePlayer = aPlayer.Object as BasePlayer; BaseVehicle theVehicle = thePlayer?.GetMountedVehicle(); if (theVehicle) { theVehicle.GetComponent()?.ChangeState(); } else if (!config.MountNeeded) { RaycastVehicle(aPlayer)?.GetComponent()?.ChangeState(); ; } } #endregion commands #region hooks private void Unload() { OnServerSave(); foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType()) { DetachRadios(eachVehicle); } } private void OnServerSave() { DataContainer thePersistentData = new DataContainer(); foreach (BaseVehicle eachCar in BaseNetworkable.serverEntities.OfType()) { RadioController theController = eachCar.GetComponent(); thePersistentData.VehicleRadioMap.Add(eachCar.net.ID.Value, theController ? new VehicleContainer(theController.Radio.Name, theController.State, theController.NetIDs) : null); } Interface.Oxide.DataFileSystem.WriteObject(Name, thePersistentData); } private void OnServerInitialized(bool anInitialFlag) { bool theSpawnRandomlyFlag = config.RadioSpawnProbability.Any(entry => entry.Value > 0f); if (!theSpawnRandomlyFlag) { Unsubscribe("OnEntitySpawned"); } // Reattach on server restart DataContainer thePersistentData = Interface.Oxide.DataFileSystem.ReadObject(Name); foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType()) { VehicleContainer theContainer; if (thePersistentData.VehicleRadioMap.TryGetValue(eachVehicle.net.ID.Value, out theContainer)) { if (theContainer != null) { Radio theRadio; if (RadioDictionary.TryGetValue(theContainer.RadioName, out theRadio)) { CreateRadioController(eachVehicle, theRadio, theContainer.NetIDs); AttachRadios(eachVehicle, theRadio, theContainer.State); } else { CreateRadioController(eachVehicle, null, theContainer.NetIDs); DetachRadios(eachVehicle); PrintWarning($"Missing radio for name \"{theContainer.RadioName}\". Ignoring..."); } } } else if (theSpawnRandomlyFlag) { RadioController theController = eachVehicle.GetComponent(); if (!theController) { float theProbability; if (config.RadioSpawnProbability.TryGetValue(eachVehicle is ModularCar ? KEY_MODULAR_CAR : eachVehicle.PrefabName, out theProbability) && Core.Random.Range(0f, 1f) < theProbability) { AttachRadios(eachVehicle, RadioDictionary.Values.First(), config.DefaultState); } } } } } private object OnButtonPress(PressButton aButton, BasePlayer aPlayer) { BaseVehicle theVehicle = aButton.GetComponentInParent()?.VehicleParent(); theVehicle = theVehicle ? theVehicle : aButton.GetComponentInParent(); theVehicle = aPlayer?.GetMountedVehicle(); if (theVehicle) { RadioController theController = theVehicle.GetComponent(); if (theController) { if ((config.MountNeeded && aPlayer.GetMountedVehicle() != theVehicle) || !theController.NetIDs.Contains(aButton.net.ID.Value)) { return false; } theController.ChangeState(); } } return null; } private void OnEntitySpawned(BaseVehicle aVehicle) { RadioController theController = aVehicle.GetComponent(); if (!theController) { float theProbability; if (config.RadioSpawnProbability.TryGetValue(aVehicle is ModularCar ? KEY_MODULAR_CAR : aVehicle.PrefabName, out theProbability) && Core.Random.Range(0f, 1f) < theProbability) { AttachRadios(aVehicle, RadioDictionary.Values.First(), config.DefaultState); } } } #endregion hooks #region methods ////// Tries to attach the given radio to the vehicle, replacing any existing radio. //////The vehicle. ///The radio. ///The initial radio state. ///The calling player. private void AttachRadios(BaseVehicle aVehicle, Radio aRadio, RadioController.States anInitialState, IPlayer aPlayer = null) { DetachRadios(aVehicle); RadioController theController = CreateRadioController(aVehicle, aRadio); if (aVehicle as ModularCar) { if (aRadio.Modules == null) { Message(aPlayer, I18N_NOT_SUPPORTED, aRadio.Name, KEY_MODULAR_CAR); DetachRadios(aVehicle); return; } foreach (BaseVehicleModule eachModule in aVehicle.GetComponentsInChildren()) { SpawnAttachments(aRadio.Modules, aPlayer, theController, eachModule); } } else if (!SpawnAttachments(aRadio.Vehicles, aPlayer, theController, aVehicle)) { Message(aPlayer, I18N_NOT_SUPPORTED, aRadio.Name, aVehicle.PrefabName); DetachRadios(aVehicle); return; } theController.SetState(anInitialState); } ////// Spawns the attachments for the given dictionary for the given parent entity. //////The dictionary. ///The calling player. ///The RadioController of the Parent. ///The Parent. /// True, if the parent has an entry in the dictionary with at least one Attachment. private bool SpawnAttachments(IDictionary<string, Attachment[]> someAttachments, IPlayer aPlayer, RadioController theController, BaseEntity aParent) { if (someAttachments == null) { return false; } Attachment[] theAttachments; if (someAttachments.TryGetValue(aParent.PrefabName, out theAttachments)) { foreach (Attachment eachAttachment in theAttachments) { BaseEntity theNewEntity = AttachEntity(aParent, eachAttachment.Prefab, eachAttachment.Position, eachAttachment.Angle, eachAttachment.Bone); if (theNewEntity) { theController.NetIDs.Add(theNewEntity.net.ID.Value); } else if (aPlayer != null) { Message(aPlayer, I18N_COULD_NOT_ATTACH, eachAttachment.Prefab); } } return !theAttachments.IsEmpty(); } return false; } ////// Creates or replaces the RadioController of the given vehicle. //////The vehicle. ///The Radio. ///Already existing radio entities. /// The newly created RadioController. private RadioController CreateRadioController(BaseVehicle aVehicle, Radio aRadio, IEnumerable someNetIDs = null) { RadioController theController = aVehicle.GetComponent(); if (theController) { UnityEngine.Object.DestroyImmediate(theController); } theController = aVehicle.gameObject.AddComponent(); theController.Config = config; theController.Radio = aRadio; if (someNetIDs != null) { theController.NetIDs.UnionWith(someNetIDs); } return theController; } ////// Detaches the radio from a vehicle and removes all corresponding entities. //////The vehicle. /// True, if a radio was removed. private bool DetachRadios(BaseVehicle aVehicle) { RadioController theController = aVehicle.GetComponent(); if (theController) { foreach (BaseEntity eachEntity in aVehicle.GetComponentsInChildren()) { if (theController.NetIDs.Contains(eachEntity.net.ID.Value)) { Destroy(eachEntity); } } UnityEngine.Object.DestroyImmediate(theController); return true; } return false; } ////// Destroys the entity. //////The entity. private static void Destroy(BaseEntity anEntity) { if (!anEntity.IsDestroyed) { anEntity.Kill(); } } ////// Attaches the prefab entity at the given local position and angles to the parent. //////The parent. ///The prefab for the new entity. ///The local position. ///The local angles. /// private BaseEntity AttachEntity(BaseEntity aParent, string aPrefab, Vector3 aPosition, Vector3 anAngle = new Vector3(), string aBone = null) { BaseEntity theNewEntity = GameManager.server.CreateEntity(aPrefab, aParent.transform.position); if (!theNewEntity) { return null; } theNewEntity.Spawn(); Transform theBone = aParent.FindBone(aBone); if (theBone == null && aBone != null) { PrintWarning($"No bone found for name '{aBone}'"); PrintWarning("Valid bone names: " + string.Join(", ", aParent.GetBones().Select(eachBone => eachBone.name))); } if (theBone != null && theBone != aParent.transform) { theNewEntity.SetParent(aParent, theBone.name); theNewEntity.transform.localPosition = theBone.InverseTransformPoint(aParent.transform.TransformPoint(aPosition)); theNewEntity.transform.localRotation = Quaternion.Inverse(theBone.rotation) * (aParent.transform.rotation * Quaternion.Euler(anAngle)); } else { theNewEntity.transform.localPosition = aPosition; theNewEntity.transform.localEulerAngles = anAngle; theNewEntity.SetParent(aParent); } //Puts(theNewEntity.ShortPrefabName + ": (" + theNewEntity.GetComponents().Length + ") " + string.Join(", ", theNewEntity.GetComponents().Select(eachComp => eachComp.GetType().Name))); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent()); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent()); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent()); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent()); theNewEntity.OwnerID = 0; BaseCombatEntity theCombatEntity = theNewEntity as BaseCombatEntity; if (theCombatEntity) { theCombatEntity.pickup.enabled = false; } PressButton theButton = theNewEntity as PressButton; if (theButton) { theButton.pressDuration = 0.2f; } theNewEntity.EnableSaving(true); theNewEntity.SendNetworkUpdateImmediate(); return theNewEntity; } ////// Toggles the IOEntity. //////The IOEntity. ///The new state. private static void ToogleRadios(IOEntity anIOEntity, bool theEnabledFlag) { anIOEntity.UpdateHasPower(theEnabledFlag ? anIOEntity.ConsumptionAmount() : 0, 0); anIOEntity.SetFlag(BaseEntity.Flags.On, theEnabledFlag); } #endregion methods #region helpers private BaseVehicle RaycastVehicle(IPlayer aPlayer) { RaycastHit theHit; if (!Physics.Raycast((aPlayer.Object as BasePlayer).eyes.HeadRay(), out theHit, 5f)) { return null; } BaseVehicle theVehicle = theHit.GetEntity()?.GetComponentInParent(); if (!theVehicle) { Message(aPlayer, I18N_NOT_A_VEHICLE); } return theVehicle; } private Radio FindRadioForName(string aName, IPlayer aPlayer) { Radio theRadio; if (!RadioDictionary.TryGetValue(aName, out theRadio)) { theRadio = RadioDictionary.Values.First(); Message(aPlayer, I18N_MISSING_SIREN, theRadio.Name); } return theRadio; } private string GetText(string aKey, string aPlayerId = null, params object[] someArgs) => string.Format(lang.GetMessage(aKey, this, aPlayerId), someArgs); private void Message(IPlayer aPlayer, string anI18nKey, params object[] someArgs) { if (aPlayer.IsConnected) { string theText = GetText(anI18nKey, aPlayer.Id, someArgs); aPlayer.Reply(theText != anI18nKey ? theText : anI18nKey); } } private void Message(BasePlayer aPlayer, string anI18nKey, params object[] someArgs) { if (aPlayer.IsConnected) { string theText = GetText(anI18nKey, aPlayer.UserIDString, someArgs); aPlayer.ChatMessage(theText != anI18nKey ? theText : anI18nKey); } } #endregion helpers #region controllers private class RadioController : FacepunchBehaviour { public enum States { OFF, ON, LIGHTS_ONLY } private BaseVehicle vehicle; private InstrumentTool trumpet; public Configuration Config { get; set; } public States State { get; private set; } public Radio Radio { get; set; } public ISet NetIDs { get; } = new HashSet(); public States ChangeState() { SetState(State >= States.LIGHTS_ONLY ? States.OFF : State + 1); return State; } public void SetState(States aState) { State = aState; if ((!Config.SoundEnabled || Radio?.Tones?.Length < 1 || !GetTrumpet()) && State == States.ON) { State++; } RefreshRadioState(); } public void RefreshRadioState() { if (State == States.ON) { PlayTone(0); } bool theLightsOnFlag = State > States.OFF; foreach (IOEntity eachEntity in GetVehicle().GetComponentsInChildren()) { //if (NetIDs.Contains(eachEntity.net.ID.Value) && !(eachEntity is PressButton)) -- Original Code for button functionality if (eachEntity is PressButton) { ToogleRadios(eachEntity, theLightsOnFlag); } } } private InstrumentTool GetTrumpet() { if (trumpet == null || trumpet.IsDestroyed) { trumpet = GetVehicle().GetComponentInChildren(); } return trumpet; } private BaseVehicle GetVehicle() { if (vehicle == null) { vehicle = GetComponentInParent(); } return vehicle; } private void PlayTone(int anIndex) { if (State != States.ON || !GetTrumpet()) { return; } if (anIndex >= Radio.Tones.Length) { anIndex = 0; } Tone theTone = Radio.Tones[anIndex]; GetTrumpet().ClientRPC(null, "Client_PlayNote", (int)theTone.Note, (int)theTone.NoteType, theTone.Octave, 1f); Invoke(() => GetTrumpet().ClientRPC(null, "Client_StopNote", (int)theTone.Note, (int)theTone.NoteType, theTone.Octave), theTone.Duration); Invoke(() => PlayTone(++anIndex), theTone.Duration); } } #endregion controllers } }

 

to remove the buttons

and to add radios to every vehicle.

config CarRadio.json

{
  "MountNeeded": true,
  "SoundEnabled": true,
  "RadioSpawnProbability": {
    "MODULAR_CAR": 0.0,
    "assets/content/vehicles/modularcar/module_entities/1module_cockpit.prefab": 1.0,
	"assets/content/vehicles/modularcar/module_entities/1module_cockpit_armored.prefab": 1.0,
	"assets/content/vehicles/modularcar/module_entities/1module_cockpit_with_engine.prefab": 1.0,
	"assets/prefabs/voiceaudio/boombox/boombox.static.prefab": 1.0,
	"assets/content/vehicles/boats/kayak/kayak.prefab": 1.0,
	"assets/content/vehicles/boats/tugboat/tugboat.prefab": 1.0,
	"assets/content/vehicles/boats/rowboat/rowboat.prefab": 1.0,
	"assets/content/vehicles/boats/rhib/rhib.prefab": 1.0,
	"assets/content/vehicles/sedan_a/sedantest.entity.prefab": 1.0,
	"assets/content/vehicles/sedan_a/sedanrail.entity.prefab": 1.0,
	"assets/content/vehicles/minicopter/minicopter.entity.prefab": 1.0,
	"assets/content/vehicles/attackhelicopter/attackhelicopter.entity.prefab": 1.0,
	"assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab": 1.0,
	"assets/prefabs/npc/ch47/ch47.entity.prefab": 1.0,
	"assets/content/vehicles/crane_magnet/magnetcrane.entity.prefab": 1.0,
	"assets/content/vehicles/submarine/submarinesolo.entity.prefab": 1.0,
	"assets/content/vehicles/submarine/submarineduo.entity.prefab": 1.0,
	"assets/content/vehicles/snowmobiles/snowmobile.prefab": 1.0,
	"assets/content/vehicles/snowmobiles/tomahasnowmobile.prefab": 1.0,
	"assets/content/vehicles/trains/workcart/workcart.entity.prefab": 1.0,
	"assets/content/vehicles/trains/workcart/workcart_aboveground.entity.prefab": 1.0,
	"assets/content/vehicles/trains/workcart/workcart_aboveground2.entity.prefab": 1.0,
	"assets/content/vehicles/trains/locomotive/locomotive.entity.prefab": 1.0,
	"assets/rust.ai/nextai/testridablehorse.prefab": 1.0,
	"assets/prefabs/deployable/hot air balloon/hotairballoon.prefab": 1.0
  },
  "DefaultState": "OFF"
}

Thanks!

I don't want to add the radios by defualt when they're spawned. Is this just a config to just allow command usage on every vehicle?

Wait! If you remove the button this would mean we could finally be using both seats on minis, right? :D
Yes, please remove it!

Merged post

Ok. Big problem though.
The new position of the radio on the mini makes it impossible to look behind and check the fuel. This is very bad.

Merged post

Aaaand it can make it hard to mount instead of pushing.

SynfulGrowing

CarRadio.cs

using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Oxide.Core; using Oxide.Core.Libraries.Covalence; using Rust.Instruments; using System.Collections.Generic; using System.Linq; using UnityEngine; using static InstrumentKeyController; namespace Oxide.Plugins { [Info("Car Radio", "TCM420G", "1.0.68")] [Description("Allows players to attach radios to vehicles")] class CarRadio : CovalencePlugin { #region variables private const string PERMISSION_ATTACHRADIO = "carradio.attachcarradio"; private const string PERMISSION_DETACHRADIO = "carradio.detachcarradio"; private const string PERMISSION_ATTACHRADIO_GLOBAL = "carradio.attachallcarradio"; private const string PERMISSION_DETACHRADIO_GLOBAL = "carradio.detachallcarradio"; private const string I18N_MISSING_SIREN = "NoRadioForName"; private const string I18N_COULD_NOT_ATTACH = "CouldNotAttach"; private const string I18N_NOT_SUPPORTED = "NotSupported"; private const string I18N_ATTACHED = "Attached"; private const string I18N_ATTACHED_GLOBAL = "AttachedGlobal"; private const string I18N_DETACHED = "Detached"; private const string I18N_DETACHED_GLOBAL = "DetachedGlobal"; private const string I18N_NOT_A_VEHICLE = "NotAVehicle"; private const string I18N_RADIO = "Radios"; private const string I18N_PLAYERS_ONLY = "PlayersOnly"; // Initial prefabs private const string PREFAB_COCKPIT = "assets/content/vehicles/modularcar/module_entities/1module_cockpit.prefab"; private const string PREFAB_COCKPIT_ARMORED = "assets/content/vehicles/modularcar/module_entities/1module_cockpit_armored.prefab"; private const string PREFAB_COCKPIT_WITH_ENGINE = "assets/content/vehicles/modularcar/module_entities/1module_cockpit_with_engine.prefab"; private const string PREFAB_BUTTON = null; private const string PREFAB_FLASHERLIGHT = null; private const string PREFAB_SIRENLIGHT = null; private const string PREFAB_SPOTLIGHT = null; private const string PREFAB_RADIO = "assets/prefabs/voiceaudio/boombox/boombox.static.prefab"; // Vehicles private const string PREFAB_KAYAK = "assets/content/vehicles/boats/kayak/kayak.prefab"; private const string PREFAB_TUGBOAT = "assets/content/vehicles/boats/tugboat/tugboat.prefab"; private const string PREFAB_ROWBOAT = "assets/content/vehicles/boats/rowboat/rowboat.prefab"; private const string PREFAB_RHIB = "assets/content/vehicles/boats/rhib/rhib.prefab"; private const string PREFAB_SEDAN = "assets/content/vehicles/sedan_a/sedantest.entity.prefab"; private const string PREFAB_SEDANRAIL = "assets/content/vehicles/sedan_a/sedanrail.entity.prefab"; private const string PREFAB_MINICOPTER = "assets/content/vehicles/minicopter/minicopter.entity.prefab"; private const string PREFAB_ATTACKHELI = "assets/content/vehicles/attackhelicopter/attackhelicopter.entity.prefab"; private const string PREFAB_TRANSPORTHELI = "assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab"; private const string PREFAB_CHINOOK = "assets/prefabs/npc/ch47/ch47.entity.prefab"; private const string PREFAB_MAGNETCRANE = "assets/content/vehicles/crane_magnet/magnetcrane.entity.prefab"; private const string PREFAB_SUBMARINESOLO = "assets/content/vehicles/submarine/submarinesolo.entity.prefab"; private const string PREFAB_SUBMARINEDUO = "assets/content/vehicles/submarine/submarineduo.entity.prefab"; private const string PREFAB_SNOWMOBILE = "assets/content/vehicles/snowmobiles/snowmobile.prefab"; private const string PREFAB_SNOWMOBILETOMAHA = "assets/content/vehicles/snowmobiles/tomahasnowmobile.prefab"; // Train Engine private const string PREFAB_WORKCART = "assets/content/vehicles/trains/workcart/workcart.entity.prefab"; private const string PREFAB_TRAINENGINE = "assets/content/vehicles/trains/workcart/workcart_aboveground.entity.prefab"; private const string PREFAB_TRAINENGINE_COVERED = "assets/content/vehicles/trains/workcart/workcart_aboveground2.entity.prefab"; private const string PREFAB_TRAINENGINE_LOCOMOTIVE = "assets/content/vehicles/trains/locomotive/locomotive.entity.prefab"; // Other Vehicles private const string PREFAB_HORSE = "assets/rust.ai/nextai/testridablehorse.prefab"; private const string PREFAB_HOTAIRBALLOON = "assets/prefabs/deployable/hot air balloon/hotairballoon.prefab"; private const string KEY_MODULAR_CAR = "MODULAR_CAR"; private const string DATAPATH_RADIO = "carradio/"; // Preconfigured carradio private static readonly Radio SIREN_DEFAULT = new Radio("Car-Radio", new Dictionary<string, Attachment[]> { [PREFAB_COCKPIT] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(-0.00f, 0.58f, 0.2f), new Vector3(330f, 180f, 0f)) }, [PREFAB_COCKPIT_ARMORED] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(-0.00f, 0.58f, 0.2f), new Vector3(330f, 180f, 0f)) }, [PREFAB_COCKPIT_WITH_ENGINE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(-0.00f, 0.58f, 0.2f), new Vector3(330f, 180f, 0f)) } }, new Dictionary<string, Attachment[]> { [PREFAB_KAYAK] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(-0.1f, 0.25f, -0.7f), new Vector3(0f, 0f, 0f)) }, [PREFAB_ROWBOAT] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.8f, 2.18f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TUGBOAT] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 8.0f, 0.7f), new Vector3(0f, 0f, 0f)), // Cockpit Radio new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.0f, 5.3f), new Vector3(0f, 180f, 0f)), // Lower Deck Radio new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.75f, -10.5f), new Vector3(0f, 0f, 0f)) // Outer Deck Radio }, [PREFAB_RHIB] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.83f, 0.62f), new Vector3(0f, 180f, 0f)) }, [PREFAB_SEDAN] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.53f, 1.25f), new Vector3(315f, 180f, 0f)) }, [PREFAB_SEDANRAIL] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.76f, 0.48f), new Vector3(315f, 180f, 0f)) }, [PREFAB_MINICOPTER] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.4f, 0.0f), new Vector3(0f, 0f, 0f)) }, [PREFAB_ATTACKHELI] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.15f, -0.45f), new Vector3(0f, 0f, 0f)) }, [PREFAB_TRANSPORTHELI] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.51f, 1.53f), new Vector3(90f, 0f, 0f)) }, [PREFAB_CHINOOK] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 3.5f, -1.6f), new Vector3(100f, 180f, 0f)), // Back Radio new Attachment(PREFAB_RADIO, new Vector3(0.0f, 3.4f, 6.35f), new Vector3(90f, 0f, 0f)) // Cockpit Radio }, [PREFAB_MAGNETCRANE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.95f, 3.5f, 0.0f), new Vector3(0f, 180f, 0f), "Top") }, [PREFAB_SUBMARINESOLO] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.85f, 0.45f), new Vector3(0f, 180f, 0f)) }, [PREFAB_SUBMARINEDUO] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.25f, -1.48f), new Vector3(0f, 0f, 0f)) }, [PREFAB_SNOWMOBILE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.9f, -1.1f), new Vector3(0f, 0f, 0f)) }, [PREFAB_SNOWMOBILETOMAHA] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.5f, -1.25f), new Vector3(0f, 0f, 0f)) }, [PREFAB_WORKCART] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.7f, 2.6f, 0.0f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TRAINENGINE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.7f, 2.6f, 0.0f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TRAINENGINE_COVERED] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.7f, 2.6f, 0.0f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TRAINENGINE_LOCOMOTIVE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(1.1f, 3.5f, 3.34f), new Vector3(0f, 0f, 0f)), // Cockpit Radio new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.55f, -9.4f), new Vector3(0f, 180f, 0f)) // Back Radio }, [PREFAB_HORSE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.55f, -0.8f), new Vector3(260f, 0f, 180f)) }, [PREFAB_HOTAIRBALLOON] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.4f, 0.0f), new Vector3(0f, 0f, 0f)) } }, new Tone(Notes.A, NoteType.Regular, 4, 1f), new Tone(Notes.D, NoteType.Regular, 5, 1f)); private static readonly Radio SIREN_SILENT = new Radio("test-radio", new Dictionary<string, Attachment[]> { //Erased Dictionary to remove bulk since it was a duplicate of the above }, new Dictionary<string, Attachment[]> { //Erased Dictionary to remove bulk since it was a duplicate of the above }); #endregion variables #region data private class DataContainer { // Map BaseVehicle.net.ID -> RadioInfos public Dictionary<ulong, VehicleContainer> VehicleRadioMap = new Dictionary<ulong, VehicleContainer>(); } private class VehicleContainer { public string RadioName = SIREN_DEFAULT.Name; public RadioController.States State = RadioController.States.OFF; public HashSet NetIDs = new HashSet(); public VehicleContainer() { } public VehicleContainer(string aRadioName, RadioController.States aState, IEnumerable someNetIDs) { RadioName = aRadioName; State = aState; NetIDs.UnionWith(someNetIDs); } } #endregion data #region configuration private Configuration config; private IDictionary<string, Radio> RadioDictionary { get; } = new Dictionary<string, Radio>(); private class Configuration { [JsonProperty("MountNeeded")] public bool MountNeeded = true; [JsonProperty("SoundEnabled")] public bool SoundEnabled = true; [JsonProperty("RadioSpawnProbability")] public Dictionary<string, float> RadioSpawnProbability = new Dictionary<string, float> { [KEY_MODULAR_CAR] = 0f, [PREFAB_HORSE] = 0f, [PREFAB_MINICOPTER] = 0f, [PREFAB_SEDAN] = 0f, [PREFAB_TRANSPORTHELI] = 0f }; [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("DefaultState")] public RadioController.States DefaultState = RadioController.States.OFF; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson()); } private class Tone { public Tone(Notes aNote = Notes.A, NoteType aNoteType = NoteType.Regular, int anOctave = 4, float aDuration = 1f) { Note = aNote; NoteType = aNoteType; Octave = anOctave; Duration = aDuration; } [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("Note")] public Notes Note; [JsonConverter(typeof(StringEnumConverter))] [JsonProperty("NoteType")] public NoteType NoteType; [JsonProperty("Octave")] public int Octave; [JsonProperty("Duration")] public float Duration; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson()); } private class Radio { public Radio(string aName, Dictionary<string, Attachment[]> someModules, Dictionary<string, Attachment[]> someVehicles, params Tone[] someTones) { Name = aName; Modules = someModules; Vehicles = someVehicles; Tones = someTones; } [JsonProperty("Name")] public string Name; [JsonProperty("Tones")] public Tone[] Tones; [JsonProperty("Modules")] public Dictionary<string, Attachment[]> Modules; [JsonProperty("Vehicles")] public Dictionary<string, Attachment[]> Vehicles; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson()); } private class Attachment { public Attachment(string aPrefab, Vector3 aPosition, Vector3 anAngle = new Vector3(), string aBone = null) { Prefab = aPrefab; Position = aPosition; Angle = anAngle; Bone = aBone; } [JsonProperty("Prefab")] public string Prefab; [JsonProperty("Position")] public Vector3 Position; [JsonProperty("Angle")] public Vector3 Angle; [JsonProperty("Bone")] public string Bone; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson()); } protected override void LoadDefaultConfig() { config = new Configuration(); RadioDictionary.Clear(); RadioDictionary.Add(SIREN_DEFAULT.Name, SIREN_DEFAULT); RadioDictionary.Add(SIREN_SILENT.Name, SIREN_SILENT); } protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) { throw new JsonException(); } try { foreach (string eachRadioFile in Interface.Oxide.DataFileSystem.GetFiles(DATAPATH_RADIO, "*.json")) { string theFilename = eachRadioFile.Basename(".json"); try { Radio theRadio = Interface.Oxide.DataFileSystem.ReadObject(DATAPATH_RADIO + theFilename); RadioDictionary.Add(theRadio.Name, theRadio); } catch { PrintWarning($"Radio file {theFilename}.json is invalid; ignoring"); } } } catch { } Puts("Loaded carradio: " + string.Join(", ", RadioDictionary.Keys)); if (RadioDictionary.IsEmpty()) { PrintWarning("Configuration appears to be missing carradio; using defaults"); RadioDictionary.Add(SIREN_DEFAULT.Name, SIREN_DEFAULT); RadioDictionary.Add(SIREN_SILENT.Name, SIREN_SILENT); SaveConfig(); } if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) { PrintWarning("Configuration appears to be outdated; updating and saving"); SaveConfig(); } } catch { PrintWarning($"Configuration file {Name}.json is invalid; using defaults"); LoadDefaultConfig(); } } protected override void SaveConfig() { PrintWarning($"Configuration changes saved to {Name}.json"); Config.WriteObject(config, true); foreach (Radio eachRadio in RadioDictionary.Values) { Interface.Oxide.DataFileSystem.WriteObject(DATAPATH_RADIO + eachRadio.Name, eachRadio); } } #endregion configuration #region localization protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary<string, string> { [I18N_MISSING_SIREN] = "No radio was found for the given name (using {0} instead)", [I18N_COULD_NOT_ATTACH] = "Could not attach '{0}'", [I18N_ATTACHED] = "Attached radio '{0}'", [I18N_ATTACHED_GLOBAL] = "Attached radio '{0}' to all existing cars", [I18N_DETACHED] = "Detached radio", [I18N_DETACHED_GLOBAL] = "Detached all existing carradio", [I18N_NOT_A_VEHICLE] = "This entity is not a (supported) vehicle", [I18N_RADIO] = "Available carradio: {0}", [I18N_PLAYERS_ONLY] = "Command '{0}' can only be used by a player", [I18N_NOT_SUPPORTED] = "The radio '{0}' has no configuration for '{1}'" }, this); } #endregion localization #region commands [Command("attachradio"), Permission(PERMISSION_ATTACHRADIO)] private void AttachCarRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { if (aPlayer.IsServer) { Message(aPlayer, I18N_PLAYERS_ONLY, aCommand); return; } BaseVehicle theVehicle = RaycastVehicle(aPlayer); if (theVehicle) { Radio theRadio = someArgs.Length > 0 ? FindRadioForName(someArgs[0], aPlayer) : RadioDictionary.Values.First(); AttachRadios(theVehicle, theRadio, config.DefaultState, aPlayer); Message(aPlayer, I18N_ATTACHED, theRadio.Name); } } [Command("removeradio"), Permission(PERMISSION_DETACHRADIO)] private void DetachCarRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { if (aPlayer.IsServer) { Message(aPlayer, I18N_PLAYERS_ONLY, aCommand); return; } BaseVehicle theVehicle = RaycastVehicle(aPlayer); if (theVehicle && DetachRadios(theVehicle)) { Message(aPlayer, I18N_DETACHED); } } [Command("attachallcarradio"), Permission(PERMISSION_ATTACHRADIO_GLOBAL)] private void AttachAllCarRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { Radio theRadio = someArgs.Length > 0 ? FindRadioForName(someArgs[0], aPlayer) : RadioDictionary.Values.First(); foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType()) { AttachRadios(eachVehicle, theRadio, config.DefaultState, aPlayer); } Message(aPlayer, I18N_ATTACHED_GLOBAL, theRadio.Name); } [Command("detachallcarradio"), Permission(PERMISSION_DETACHRADIO_GLOBAL)] private void DetachAllCarRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType()) { DetachRadios(eachVehicle); } Message(aPlayer, I18N_DETACHED_GLOBAL); } [Command("togglecarradio")] private void ToggleRadios(IPlayer aPlayer, string aCommand, string[] someArgs) { if (aPlayer.IsServer) { Message(aPlayer, I18N_PLAYERS_ONLY, aCommand); return; } BasePlayer thePlayer = aPlayer.Object as BasePlayer; BaseVehicle theVehicle = thePlayer?.GetMountedVehicle(); if (theVehicle) { theVehicle.GetComponent()?.ChangeState(); } else if (!config.MountNeeded) { RaycastVehicle(aPlayer)?.GetComponent()?.ChangeState(); ; } } #endregion commands #region hooks private void Unload() { OnServerSave(); foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType()) { DetachRadios(eachVehicle); } } private void OnServerSave() { DataContainer thePersistentData = new DataContainer(); foreach (BaseVehicle eachCar in BaseNetworkable.serverEntities.OfType()) { RadioController theController = eachCar.GetComponent(); thePersistentData.VehicleRadioMap.Add(eachCar.net.ID.Value, theController ? new VehicleContainer(theController.Radio.Name, theController.State, theController.NetIDs) : null); } Interface.Oxide.DataFileSystem.WriteObject(Name, thePersistentData); } private void OnServerInitialized(bool anInitialFlag) { bool theSpawnRandomlyFlag = config.RadioSpawnProbability.Any(entry => entry.Value > 0f); if (!theSpawnRandomlyFlag) { Unsubscribe("OnEntitySpawned"); } // Reattach on server restart DataContainer thePersistentData = Interface.Oxide.DataFileSystem.ReadObject(Name); foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType()) { VehicleContainer theContainer; if (thePersistentData.VehicleRadioMap.TryGetValue(eachVehicle.net.ID.Value, out theContainer)) { if (theContainer != null) { Radio theRadio; if (RadioDictionary.TryGetValue(theContainer.RadioName, out theRadio)) { CreateRadioController(eachVehicle, theRadio, theContainer.NetIDs); AttachRadios(eachVehicle, theRadio, theContainer.State); } else { CreateRadioController(eachVehicle, null, theContainer.NetIDs); DetachRadios(eachVehicle); PrintWarning($"Missing radio for name \"{theContainer.RadioName}\". Ignoring..."); } } } else if (theSpawnRandomlyFlag) { RadioController theController = eachVehicle.GetComponent(); if (!theController) { float theProbability; if (config.RadioSpawnProbability.TryGetValue(eachVehicle is ModularCar ? KEY_MODULAR_CAR : eachVehicle.PrefabName, out theProbability) && Core.Random.Range(0f, 1f) < theProbability) { AttachRadios(eachVehicle, RadioDictionary.Values.First(), config.DefaultState); } } } } } private object OnButtonPress(PressButton aButton, BasePlayer aPlayer) { BaseVehicle theVehicle = aButton.GetComponentInParent()?.VehicleParent(); theVehicle = theVehicle ? theVehicle : aButton.GetComponentInParent(); theVehicle = aPlayer?.GetMountedVehicle(); if (theVehicle) { RadioController theController = theVehicle.GetComponent(); if (theController) { if ((config.MountNeeded && aPlayer.GetMountedVehicle() != theVehicle) || !theController.NetIDs.Contains(aButton.net.ID.Value)) { return false; } theController.ChangeState(); } } return null; } private void OnEntitySpawned(BaseVehicle aVehicle) { RadioController theController = aVehicle.GetComponent(); if (!theController) { float theProbability; if (config.RadioSpawnProbability.TryGetValue(aVehicle is ModularCar ? KEY_MODULAR_CAR : aVehicle.PrefabName, out theProbability) && Core.Random.Range(0f, 1f) < theProbability) { AttachRadios(aVehicle, RadioDictionary.Values.First(), config.DefaultState); } } } #endregion hooks #region methods ////// Tries to attach the given radio to the vehicle, replacing any existing radio. //////The vehicle. ///The radio. ///The initial radio state. ///The calling player. private void AttachRadios(BaseVehicle aVehicle, Radio aRadio, RadioController.States anInitialState, IPlayer aPlayer = null) { DetachRadios(aVehicle); RadioController theController = CreateRadioController(aVehicle, aRadio); if (aVehicle as ModularCar) { if (aRadio.Modules == null) { Message(aPlayer, I18N_NOT_SUPPORTED, aRadio.Name, KEY_MODULAR_CAR); DetachRadios(aVehicle); return; } foreach (BaseVehicleModule eachModule in aVehicle.GetComponentsInChildren()) { SpawnAttachments(aRadio.Modules, aPlayer, theController, eachModule); } } else if (!SpawnAttachments(aRadio.Vehicles, aPlayer, theController, aVehicle)) { Message(aPlayer, I18N_NOT_SUPPORTED, aRadio.Name, aVehicle.PrefabName); DetachRadios(aVehicle); return; } theController.SetState(anInitialState); } ////// Spawns the attachments for the given dictionary for the given parent entity. //////The dictionary. ///The calling player. ///The RadioController of the Parent. ///The Parent. /// True, if the parent has an entry in the dictionary with at least one Attachment. private bool SpawnAttachments(IDictionary<string, Attachment[]> someAttachments, IPlayer aPlayer, RadioController theController, BaseEntity aParent) { if (someAttachments == null) { return false; } Attachment[] theAttachments; if (someAttachments.TryGetValue(aParent.PrefabName, out theAttachments)) { foreach (Attachment eachAttachment in theAttachments) { BaseEntity theNewEntity = AttachEntity(aParent, eachAttachment.Prefab, eachAttachment.Position, eachAttachment.Angle, eachAttachment.Bone); if (theNewEntity) { theController.NetIDs.Add(theNewEntity.net.ID.Value); } else if (aPlayer != null) { Message(aPlayer, I18N_COULD_NOT_ATTACH, eachAttachment.Prefab); } } return !theAttachments.IsEmpty(); } return false; } ////// Creates or replaces the RadioController of the given vehicle. //////The vehicle. ///The Radio. ///Already existing radio entities. /// The newly created RadioController. private RadioController CreateRadioController(BaseVehicle aVehicle, Radio aRadio, IEnumerable someNetIDs = null) { RadioController theController = aVehicle.GetComponent(); if (theController) { UnityEngine.Object.DestroyImmediate(theController); } theController = aVehicle.gameObject.AddComponent(); theController.Config = config; theController.Radio = aRadio; if (someNetIDs != null) { theController.NetIDs.UnionWith(someNetIDs); } return theController; } ////// Detaches the radio from a vehicle and removes all corresponding entities. //////The vehicle. /// True, if a radio was removed. private bool DetachRadios(BaseVehicle aVehicle) { RadioController theController = aVehicle.GetComponent(); if (theController) { foreach (BaseEntity eachEntity in aVehicle.GetComponentsInChildren()) { if (theController.NetIDs.Contains(eachEntity.net.ID.Value)) { Destroy(eachEntity); } } UnityEngine.Object.DestroyImmediate(theController); return true; } return false; } ////// Destroys the entity. //////The entity. private static void Destroy(BaseEntity anEntity) { if (!anEntity.IsDestroyed) { anEntity.Kill(); } } ////// Attaches the prefab entity at the given local position and angles to the parent. //////The parent. ///The prefab for the new entity. ///The local position. ///The local angles. /// private BaseEntity AttachEntity(BaseEntity aParent, string aPrefab, Vector3 aPosition, Vector3 anAngle = new Vector3(), string aBone = null) { BaseEntity theNewEntity = GameManager.server.CreateEntity(aPrefab, aParent.transform.position); if (!theNewEntity) { return null; } theNewEntity.Spawn(); Transform theBone = aParent.FindBone(aBone); if (theBone == null && aBone != null) { PrintWarning($"No bone found for name '{aBone}'"); PrintWarning("Valid bone names: " + string.Join(", ", aParent.GetBones().Select(eachBone => eachBone.name))); } if (theBone != null && theBone != aParent.transform) { theNewEntity.SetParent(aParent, theBone.name); theNewEntity.transform.localPosition = theBone.InverseTransformPoint(aParent.transform.TransformPoint(aPosition)); theNewEntity.transform.localRotation = Quaternion.Inverse(theBone.rotation) * (aParent.transform.rotation * Quaternion.Euler(anAngle)); } else { theNewEntity.transform.localPosition = aPosition; theNewEntity.transform.localEulerAngles = anAngle; theNewEntity.SetParent(aParent); } //Puts(theNewEntity.ShortPrefabName + ": (" + theNewEntity.GetComponents().Length + ") " + string.Join(", ", theNewEntity.GetComponents().Select(eachComp => eachComp.GetType().Name))); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent()); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent()); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent()); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent()); theNewEntity.OwnerID = 0; BaseCombatEntity theCombatEntity = theNewEntity as BaseCombatEntity; if (theCombatEntity) { theCombatEntity.pickup.enabled = false; } PressButton theButton = theNewEntity as PressButton; if (theButton) { theButton.pressDuration = 0.2f; } theNewEntity.EnableSaving(true); theNewEntity.SendNetworkUpdateImmediate(); return theNewEntity; } ////// Toggles the IOEntity. //////The IOEntity. ///The new state. private static void ToogleRadios(IOEntity anIOEntity, bool theEnabledFlag) { anIOEntity.UpdateHasPower(theEnabledFlag ? anIOEntity.ConsumptionAmount() : 0, 0); anIOEntity.SetFlag(BaseEntity.Flags.On, theEnabledFlag); } #endregion methods #region helpers private BaseVehicle RaycastVehicle(IPlayer aPlayer) { RaycastHit theHit; if (!Physics.Raycast((aPlayer.Object as BasePlayer).eyes.HeadRay(), out theHit, 5f)) { return null; } BaseVehicle theVehicle = theHit.GetEntity()?.GetComponentInParent(); if (!theVehicle) { Message(aPlayer, I18N_NOT_A_VEHICLE); } return theVehicle; } private Radio FindRadioForName(string aName, IPlayer aPlayer) { Radio theRadio; if (!RadioDictionary.TryGetValue(aName, out theRadio)) { theRadio = RadioDictionary.Values.First(); Message(aPlayer, I18N_MISSING_SIREN, theRadio.Name); } return theRadio; } private string GetText(string aKey, string aPlayerId = null, params object[] someArgs) => string.Format(lang.GetMessage(aKey, this, aPlayerId), someArgs); private void Message(IPlayer aPlayer, string anI18nKey, params object[] someArgs) { if (aPlayer.IsConnected) { string theText = GetText(anI18nKey, aPlayer.Id, someArgs); aPlayer.Reply(theText != anI18nKey ? theText : anI18nKey); } } private void Message(BasePlayer aPlayer, string anI18nKey, params object[] someArgs) { if (aPlayer.IsConnected) { string theText = GetText(anI18nKey, aPlayer.UserIDString, someArgs); aPlayer.ChatMessage(theText != anI18nKey ? theText : anI18nKey); } } #endregion helpers #region controllers private class RadioController : FacepunchBehaviour { public enum States { OFF, ON, LIGHTS_ONLY } private BaseVehicle vehicle; private InstrumentTool trumpet; public Configuration Config { get; set; } public States State { get; private set; } public Radio Radio { get; set; } public ISet NetIDs { get; } = new HashSet(); public States ChangeState() { SetState(State >= States.LIGHTS_ONLY ? States.OFF : State + 1); return State; } public void SetState(States aState) { State = aState; if ((!Config.SoundEnabled || Radio?.Tones?.Length < 1 || !GetTrumpet()) && State == States.ON) { State++; } RefreshRadioState(); } public void RefreshRadioState() { if (State == States.ON) { PlayTone(0); } bool theLightsOnFlag = State > States.OFF; foreach (IOEntity eachEntity in GetVehicle().GetComponentsInChildren()) { //if (NetIDs.Contains(eachEntity.net.ID.Value) && !(eachEntity is PressButton)) -- Original Code for button functionality if (eachEntity is PressButton) { ToogleRadios(eachEntity, theLightsOnFlag); } } } private InstrumentTool GetTrumpet() { if (trumpet == null || trumpet.IsDestroyed) { trumpet = GetVehicle().GetComponentInChildren(); } return trumpet; } private BaseVehicle GetVehicle() { if (vehicle == null) { vehicle = GetComponentInParent(); } return vehicle; } private void PlayTone(int anIndex) { if (State != States.ON || !GetTrumpet()) { return; } if (anIndex >= Radio.Tones.Length) { anIndex = 0; } Tone theTone = Radio.Tones[anIndex]; GetTrumpet().ClientRPC(null, "Client_PlayNote", (int)theTone.Note, (int)theTone.NoteType, theTone.Octave, 1f); Invoke(() => GetTrumpet().ClientRPC(null, "Client_StopNote", (int)theTone.Note, (int)theTone.NoteType, theTone.Octave), theTone.Duration); Invoke(() => PlayTone(++anIndex), theTone.Duration); } } #endregion controllers } }

 

to remove the buttons

and to add radios to every vehicle.

config CarRadio.json

{
  "MountNeeded": true,
  "SoundEnabled": true,
  "RadioSpawnProbability": {
    "MODULAR_CAR": 0.0,
    "assets/content/vehicles/modularcar/module_entities/1module_cockpit.prefab": 1.0,
	"assets/content/vehicles/modularcar/module_entities/1module_cockpit_armored.prefab": 1.0,
	"assets/content/vehicles/modularcar/module_entities/1module_cockpit_with_engine.prefab": 1.0,
	"assets/prefabs/voiceaudio/boombox/boombox.static.prefab": 1.0,
	"assets/content/vehicles/boats/kayak/kayak.prefab": 1.0,
	"assets/content/vehicles/boats/tugboat/tugboat.prefab": 1.0,
	"assets/content/vehicles/boats/rowboat/rowboat.prefab": 1.0,
	"assets/content/vehicles/boats/rhib/rhib.prefab": 1.0,
	"assets/content/vehicles/sedan_a/sedantest.entity.prefab": 1.0,
	"assets/content/vehicles/sedan_a/sedanrail.entity.prefab": 1.0,
	"assets/content/vehicles/minicopter/minicopter.entity.prefab": 1.0,
	"assets/content/vehicles/attackhelicopter/attackhelicopter.entity.prefab": 1.0,
	"assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab": 1.0,
	"assets/prefabs/npc/ch47/ch47.entity.prefab": 1.0,
	"assets/content/vehicles/crane_magnet/magnetcrane.entity.prefab": 1.0,
	"assets/content/vehicles/submarine/submarinesolo.entity.prefab": 1.0,
	"assets/content/vehicles/submarine/submarineduo.entity.prefab": 1.0,
	"assets/content/vehicles/snowmobiles/snowmobile.prefab": 1.0,
	"assets/content/vehicles/snowmobiles/tomahasnowmobile.prefab": 1.0,
	"assets/content/vehicles/trains/workcart/workcart.entity.prefab": 1.0,
	"assets/content/vehicles/trains/workcart/workcart_aboveground.entity.prefab": 1.0,
	"assets/content/vehicles/trains/workcart/workcart_aboveground2.entity.prefab": 1.0,
	"assets/content/vehicles/trains/locomotive/locomotive.entity.prefab": 1.0,
	"assets/rust.ai/nextai/testridablehorse.prefab": 1.0,
	"assets/prefabs/deployable/hot air balloon/hotairballoon.prefab": 1.0
  },
  "DefaultState": "OFF"
}

Can you send me the plugin file somehow? This pasted formatting in here cause compile issues -_-

diChS5GRDpI7e6E.jpg Flammable

Can you send me the plugin file somehow? This pasted formatting in here cause compile issues -_-

 

Yeah, dropbox work?

CarRadio.cs

guess i need more words.

Kleementin

Ok. Big problem though.
The new position of the radio on the mini makes it impossible to look behind and check the fuel. This is very bad.

 

you can reposition the radio.

tweak the f values

new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.4f, 0.0f), new Vector3(0f, 0f, 0f))

 

trying it now, not working as i thought it would.

nvm i said anything, i thought by adjusting the f values i could get movement, no matter what value, doesnt budge.

SynfulGrowing

 

Yeah, dropbox work?

CarRadio.cs

guess i need more words.

Thanks <3

I have just remove the buttons in the cs file and that works if you delete your data files of car radio

Please update the Car Radio plugin, either cancel or remove the non-functional button.

Thank you

supports all other vehicles as well from the previous file?

Its the same as the normal one but without the buttons and add carboose to it ,so that people can listen radio there also