Error while compiling CarRadio: Argument 1: cannot convert from '<null>' to 'RpcTarget' | Line: 922, Pos: 40
Update please
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.74")]
[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 = "assets/prefabs/deployable/playerioents/button/button.prefab";
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";
//BIKES
private const string PREFAB_PEDALBIKE = "assets/content/vehicles/bikes/pedalbike.prefab";
private const string PREFAB_PEDALTRIKE = "assets/content/vehicles/bikes/pedaltrike.prefab";
private const string PREFAB_MOTORBIKE = "assets/content/vehicles/bikes/motorbike.prefab";
private const string PREFAB_MOTORBIKE_SIDECAR = "assets/content/vehicles/bikes/motorbike_sidecar.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_BUTTON, new Vector3(0.05f, 1.85f, 0.62f), new Vector3(200f, 0f, 0f)),
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_BUTTON, new Vector3(0.05f, 1.85f, 0.62f), new Vector3(200f, 0f, 0f)),
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_BUTTON, new Vector3(0.05f, 1.85f, 0.62f), new Vector3(200f, 0f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(-0.00f, 0.58f, 0.2f), new Vector3(330f, 180f, 0f))
}
},
new Dictionary<string, Attachment[]>
{
[PREFAB_PEDALBIKE] = new Attachment[] {
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.8f, -0.35f), new Vector3(0f, 0f, 0f))
},
[PREFAB_PEDALTRIKE] = new Attachment[] {
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.8f, -0.35f), new Vector3(0f, 0f, 0f))
},
[PREFAB_MOTORBIKE] = new Attachment[] {
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.89f, -0.55f), new Vector3(0f, 0f, 0f))
},
[PREFAB_MOTORBIKE_SIDECAR] = new Attachment[] {
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.89f, -0.55f), new Vector3(0f, 0f, 0f))
},
[PREFAB_KAYAK] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(-1.0f, 0.2f, 1.5f), new Vector3(270f, 270f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(-0.1f, 0.25f, -0.7f), new Vector3(0f, 0f, 0f))
},
[PREFAB_ROWBOAT] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(-1.7f, 0.5f, -1.8f), new Vector3(270f, 270f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.8f, 2.18f), new Vector3(0f, 180f, 0f))
},
[PREFAB_TUGBOAT] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(-2.05f, 6.8f, 3.1f), new Vector3(230f, 320f, 270f)),
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_BUTTON, new Vector3(0.5f, 2.275f, 1.55f), new Vector3(251f, 0f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.83f, 0.62f), new Vector3(0f, 180f, 0f))
},
[PREFAB_SEDAN] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 2.07f, 1.98f), new Vector3(210f, 0f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.53f, 1.25f), new Vector3(315f, 180f, 0f))
},
[PREFAB_SEDANRAIL] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 2.3f, 1.22f), new Vector3(210f, 0f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.76f, 0.48f), new Vector3(315f, 180f, 0f))
},
[PREFAB_MINICOPTER] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 2.01f, 1.03f), new Vector3(180f, 0f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.4f, 0.0f), new Vector3(0f, 0f, 0f))
},
[PREFAB_ATTACKHELI] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 0.35f, 0.55f), new Vector3(0f, 180f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.15f, -0.45f), new Vector3(0f, 0f, 0f))
},
[PREFAB_TRANSPORTHELI] = new Attachment[]
{
// Radio properly on the dash, centered
new Attachment(
PREFAB_RADIO,
new Vector3(0.0f, 1.45f, 3.3f), // Y lower | Z further forward
new Vector3(0f, 180f, 0f)
)
},
[PREFAB_CHINOOK] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 1.9f, 8.0f), new Vector3(270f, 0f, 0f)),
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_BUTTON, new Vector3(1.08f, 3.98f, -2.15f), new Vector3(233f, 170f, 0f), "Top"),
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_BUTTON, new Vector3(-0.27f, 2.55f, -0.27f), new Vector3(180f, 225f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.85f, 0.45f), new Vector3(0f, 180f, 0f))
},
[PREFAB_SUBMARINEDUO] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.275f, 2.5f, 0.76f), new Vector3(172f, 90f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.25f, -1.48f), new Vector3(0f, 0f, 0f))
},
[PREFAB_SNOWMOBILE] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 1.8f, 0.9f), new Vector3(210f, 0f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.9f, -1.1f), new Vector3(0f, 0f, 0f))
},
[PREFAB_SNOWMOBILETOMAHA] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 1.21f, 0.88f), new Vector3(240f, 0f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.5f, -1.25f), new Vector3(0f, 0f, 0f))
},
[PREFAB_WORKCART] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.19f, 3.13f, 4.95f), new Vector3(235f, 0f, 0f)),
new Attachment(PREFAB_RADIO, new Vector3(0.7f, 2.6f, 0.0f), new Vector3(0f, 180f, 0f))
},
[PREFAB_TRAINENGINE] = new Attachment[] {
// new Attachment(PREFAB_BUTTON, new Vector3(0.19f, 3.13f, 4.95f), new Vector3(235f, 0f, 0f)),
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_BUTTON, new Vector3(0.19f, 3.13f, 4.95f), new Vector3(235f, 0f, 0f)),
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_BUTTON, new Vector3(-0.1f, 4.3f, 5.35f), new Vector3(200f, 270f, 0f)),
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_BUTTON, new Vector3(0.0f, 2.01f, 1.03f), new Vector3(180f, 0f, 0f)),
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[]>
{
// Empty / custom if you want a silent config
},
new Dictionary<string, Attachment[]>
{
// Empty / custom if you want a silent config
});
#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<ulong> NetIDs = new HashSet<ulong>();
public VehicleContainer()
{
}
public VehicleContainer(string aRadioName, RadioController.States aState, IEnumerable<ulong> 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<Configuration>();
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<Radio>(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<BaseVehicle>())
{
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<BaseVehicle>())
{
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<RadioController>()?.ChangeState();
}
else if (!config.MountNeeded)
{
RaycastVehicle(aPlayer)?.GetComponent<RadioController>()?.ChangeState();
}
}
#endregion commands
#region hooks
private void Unload()
{
OnServerSave();
foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType<BaseVehicle>())
{
DetachRadios(eachVehicle);
}
}
private void OnServerSave()
{
DataContainer thePersistentData = new DataContainer();
foreach (BaseVehicle eachCar in BaseNetworkable.serverEntities.OfType<BaseVehicle>())
{
RadioController theController = eachCar.GetComponent<RadioController>();
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<DataContainer>(Name);
foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType<BaseVehicle>())
{
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<RadioController>();
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 void OnEntitySpawned(BaseVehicle aVehicle)
{
RadioController theController = aVehicle.GetComponent<RadioController>();
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
/// <summary>
/// Tries to attach the given radio to the vehicle, replacing any existing radio.
/// </summary>
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<BaseVehicleModule>())
{
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);
}
/// <summary>
/// Spawns the attachments for the given dictionary for the given parent entity.
/// </summary>
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;
}
/// <summary>
/// Creates or replaces the RadioController of the given vehicle.
/// </summary>
private RadioController CreateRadioController(BaseVehicle aVehicle, Radio aRadio, IEnumerable<ulong> someNetIDs = null)
{
RadioController theController = aVehicle.GetComponent<RadioController>();
if (theController)
{
UnityEngine.Object.DestroyImmediate(theController);
}
theController = aVehicle.gameObject.AddComponent<RadioController>();
theController.Config = config;
theController.Radio = aRadio;
if (someNetIDs != null)
{
theController.NetIDs.UnionWith(someNetIDs);
}
return theController;
}
/// <summary>
/// Detaches the radio from a vehicle and removes all corresponding entities.
/// </summary>
private bool DetachRadios(BaseVehicle aVehicle)
{
RadioController theController = aVehicle.GetComponent<RadioController>();
if (theController)
{
foreach (BaseEntity eachEntity in aVehicle.GetComponentsInChildren<BaseEntity>())
{
if (theController.NetIDs.Contains(eachEntity.net.ID.Value))
{
Destroy(eachEntity);
}
}
UnityEngine.Object.DestroyImmediate(theController);
return true;
}
return false;
}
/// <summary>
/// Destroys the entity.
/// </summary>
private static void Destroy(BaseEntity anEntity)
{
if (!anEntity.IsDestroyed)
{
anEntity.Kill();
}
}
/// <summary>
/// Attaches the prefab entity at the given local position and angles to the parent.
/// Also strips ALL colliders so the radio cannot block mounts.
/// </summary>
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);
}
// Remove ALL colliders so the radio cannot block mounting / interaction
foreach (var col in theNewEntity.GetComponentsInChildren<Collider>())
{
UnityEngine.Object.DestroyImmediate(col);
}
UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent<DestroyOnGroundMissing>());
UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent<GroundWatch>());
UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent<InstrumentKeyController>());
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;
}
/// <summary>
/// Toggles the IOEntity.
/// </summary>
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<BaseVehicle>();
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<ulong> NetIDs { get; } = new HashSet<ulong>();
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<IOEntity>())
{
// 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<InstrumentTool>();
}
return trumpet;
}
private BaseVehicle GetVehicle()
{
if (vehicle == null)
{
vehicle = GetComponentInParent<BaseVehicle>();
}
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(RpcTarget.NetworkGroup("Client_PlayNote"), (int)theTone.Note, (int)theTone.NoteType, theTone.Octave, 1f);
Invoke(() => GetTrumpet().ClientRPC(RpcTarget.NetworkGroup("Client_StopNote"), (int)theTone.Note, (int)theTone.NoteType, theTone.Octave), theTone.Duration);
Invoke(() => PlayTone(++anIndex), theTone.Duration);
}
}
#endregion controllers
}
} orangemart
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.74")] [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 = "assets/prefabs/deployable/playerioents/button/button.prefab"; 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"; //BIKES private const string PREFAB_PEDALBIKE = "assets/content/vehicles/bikes/pedalbike.prefab"; private const string PREFAB_PEDALTRIKE = "assets/content/vehicles/bikes/pedaltrike.prefab"; private const string PREFAB_MOTORBIKE = "assets/content/vehicles/bikes/motorbike.prefab"; private const string PREFAB_MOTORBIKE_SIDECAR = "assets/content/vehicles/bikes/motorbike_sidecar.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_BUTTON, new Vector3(0.05f, 1.85f, 0.62f), new Vector3(200f, 0f, 0f)), 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_BUTTON, new Vector3(0.05f, 1.85f, 0.62f), new Vector3(200f, 0f, 0f)), 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_BUTTON, new Vector3(0.05f, 1.85f, 0.62f), new Vector3(200f, 0f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(-0.00f, 0.58f, 0.2f), new Vector3(330f, 180f, 0f)) } }, new Dictionary<string, Attachment[]> { [PREFAB_PEDALBIKE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.8f, -0.35f), new Vector3(0f, 0f, 0f)) }, [PREFAB_PEDALTRIKE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.8f, -0.35f), new Vector3(0f, 0f, 0f)) }, [PREFAB_MOTORBIKE] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.89f, -0.55f), new Vector3(0f, 0f, 0f)) }, [PREFAB_MOTORBIKE_SIDECAR] = new Attachment[] { new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.89f, -0.55f), new Vector3(0f, 0f, 0f)) }, [PREFAB_KAYAK] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(-1.0f, 0.2f, 1.5f), new Vector3(270f, 270f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(-0.1f, 0.25f, -0.7f), new Vector3(0f, 0f, 0f)) }, [PREFAB_ROWBOAT] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(-1.7f, 0.5f, -1.8f), new Vector3(270f, 270f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.8f, 2.18f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TUGBOAT] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(-2.05f, 6.8f, 3.1f), new Vector3(230f, 320f, 270f)), 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_BUTTON, new Vector3(0.5f, 2.275f, 1.55f), new Vector3(251f, 0f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.83f, 0.62f), new Vector3(0f, 180f, 0f)) }, [PREFAB_SEDAN] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 2.07f, 1.98f), new Vector3(210f, 0f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.53f, 1.25f), new Vector3(315f, 180f, 0f)) }, [PREFAB_SEDANRAIL] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 2.3f, 1.22f), new Vector3(210f, 0f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.76f, 0.48f), new Vector3(315f, 180f, 0f)) }, [PREFAB_MINICOPTER] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 2.01f, 1.03f), new Vector3(180f, 0f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.4f, 0.0f), new Vector3(0f, 0f, 0f)) }, [PREFAB_ATTACKHELI] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 0.35f, 0.55f), new Vector3(0f, 180f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 2.15f, -0.45f), new Vector3(0f, 0f, 0f)) }, [PREFAB_TRANSPORTHELI] = new Attachment[] { // Radio properly on the dash, centered new Attachment( PREFAB_RADIO, new Vector3(0.0f, 1.45f, 3.3f), // Y lower | Z further forward new Vector3(0f, 180f, 0f) ) }, [PREFAB_CHINOOK] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 1.9f, 8.0f), new Vector3(270f, 0f, 0f)), 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_BUTTON, new Vector3(1.08f, 3.98f, -2.15f), new Vector3(233f, 170f, 0f), "Top"), 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_BUTTON, new Vector3(-0.27f, 2.55f, -0.27f), new Vector3(180f, 225f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.85f, 0.45f), new Vector3(0f, 180f, 0f)) }, [PREFAB_SUBMARINEDUO] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.275f, 2.5f, 0.76f), new Vector3(172f, 90f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 1.25f, -1.48f), new Vector3(0f, 0f, 0f)) }, [PREFAB_SNOWMOBILE] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 1.8f, 0.9f), new Vector3(210f, 0f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.9f, -1.1f), new Vector3(0f, 0f, 0f)) }, [PREFAB_SNOWMOBILETOMAHA] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.0f, 1.21f, 0.88f), new Vector3(240f, 0f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.0f, 0.5f, -1.25f), new Vector3(0f, 0f, 0f)) }, [PREFAB_WORKCART] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.19f, 3.13f, 4.95f), new Vector3(235f, 0f, 0f)), new Attachment(PREFAB_RADIO, new Vector3(0.7f, 2.6f, 0.0f), new Vector3(0f, 180f, 0f)) }, [PREFAB_TRAINENGINE] = new Attachment[] { // new Attachment(PREFAB_BUTTON, new Vector3(0.19f, 3.13f, 4.95f), new Vector3(235f, 0f, 0f)), 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_BUTTON, new Vector3(0.19f, 3.13f, 4.95f), new Vector3(235f, 0f, 0f)), 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_BUTTON, new Vector3(-0.1f, 4.3f, 5.35f), new Vector3(200f, 270f, 0f)), 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_BUTTON, new Vector3(0.0f, 2.01f, 1.03f), new Vector3(180f, 0f, 0f)), 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[]> { // Empty / custom if you want a silent config }, new Dictionary<string, Attachment[]> { // Empty / custom if you want a silent config }); #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<ulong> NetIDs = new HashSet<ulong>(); public VehicleContainer() { } public VehicleContainer(string aRadioName, RadioController.States aState, IEnumerable<ulong> 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<Configuration>(); 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<Radio>(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<BaseVehicle>()) { 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<BaseVehicle>()) { 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<RadioController>()?.ChangeState(); } else if (!config.MountNeeded) { RaycastVehicle(aPlayer)?.GetComponent<RadioController>()?.ChangeState(); } } #endregion commands #region hooks private void Unload() { OnServerSave(); foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType<BaseVehicle>()) { DetachRadios(eachVehicle); } } private void OnServerSave() { DataContainer thePersistentData = new DataContainer(); foreach (BaseVehicle eachCar in BaseNetworkable.serverEntities.OfType<BaseVehicle>()) { RadioController theController = eachCar.GetComponent<RadioController>(); 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<DataContainer>(Name); foreach (BaseVehicle eachVehicle in BaseNetworkable.serverEntities.OfType<BaseVehicle>()) { 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<RadioController>(); 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 void OnEntitySpawned(BaseVehicle aVehicle) { RadioController theController = aVehicle.GetComponent<RadioController>(); 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 /// <summary> /// Tries to attach the given radio to the vehicle, replacing any existing radio. /// </summary> 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<BaseVehicleModule>()) { 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); } /// <summary> /// Spawns the attachments for the given dictionary for the given parent entity. /// </summary> 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; } /// <summary> /// Creates or replaces the RadioController of the given vehicle. /// </summary> private RadioController CreateRadioController(BaseVehicle aVehicle, Radio aRadio, IEnumerable<ulong> someNetIDs = null) { RadioController theController = aVehicle.GetComponent<RadioController>(); if (theController) { UnityEngine.Object.DestroyImmediate(theController); } theController = aVehicle.gameObject.AddComponent<RadioController>(); theController.Config = config; theController.Radio = aRadio; if (someNetIDs != null) { theController.NetIDs.UnionWith(someNetIDs); } return theController; } /// <summary> /// Detaches the radio from a vehicle and removes all corresponding entities. /// </summary> private bool DetachRadios(BaseVehicle aVehicle) { RadioController theController = aVehicle.GetComponent<RadioController>(); if (theController) { foreach (BaseEntity eachEntity in aVehicle.GetComponentsInChildren<BaseEntity>()) { if (theController.NetIDs.Contains(eachEntity.net.ID.Value)) { Destroy(eachEntity); } } UnityEngine.Object.DestroyImmediate(theController); return true; } return false; } /// <summary> /// Destroys the entity. /// </summary> private static void Destroy(BaseEntity anEntity) { if (!anEntity.IsDestroyed) { anEntity.Kill(); } } /// <summary> /// Attaches the prefab entity at the given local position and angles to the parent. /// Also strips ALL colliders so the radio cannot block mounts. /// </summary> 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); } // Remove ALL colliders so the radio cannot block mounting / interaction foreach (var col in theNewEntity.GetComponentsInChildren<Collider>()) { UnityEngine.Object.DestroyImmediate(col); } UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent<DestroyOnGroundMissing>()); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent<GroundWatch>()); UnityEngine.Object.DestroyImmediate(theNewEntity.GetComponent<InstrumentKeyController>()); 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; } /// <summary> /// Toggles the IOEntity. /// </summary> 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<BaseVehicle>(); 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<ulong> NetIDs { get; } = new HashSet<ulong>(); 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<IOEntity>()) { // 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<InstrumentTool>(); } return trumpet; } private BaseVehicle GetVehicle() { if (vehicle == null) { vehicle = GetComponentInParent<BaseVehicle>(); } 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(RpcTarget.NetworkGroup("Client_PlayNote"), (int)theTone.Note, (int)theTone.NoteType, theTone.Octave, 1f); Invoke(() => GetTrumpet().ClientRPC(RpcTarget.NetworkGroup("Client_StopNote"), (int)theTone.Note, (int)theTone.NoteType, theTone.Octave), theTone.Duration); Invoke(() => PlayTone(++anIndex), theTone.Duration); } } #endregion controllers } }
Thank you very much.