C#:
using System.Collections.Generic;
using System.Collections;
using Oxide.Core.Configuration;
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using UnityEngine;
using System.Linq;
using Oxide.Core;
using System;
using System.Globalization;
using Steamworks;
using Network;
namespace Oxide.Plugins {
[Info("MonumentMusic", "Razor", "1.0.1")]
[Description("Add some music to monuments.")]
public class MonumentMusic : RustPlugin {
private static MonumentMusic _;
private List<DeployableBoomBox> spawnedEntity = new List<DeployableBoomBox>();
private static string permAdmin = "monumentmusic.admin";
private string mapName;
private List<ulong> hammerHit = new List<ulong>();
private int speakerCount;
#region Init
private void Init() {
_ = this;
RegisterPermissions();
}
private void OnServerInitialized() {
if (configData.settings.Monuments.Count <= 0) {
configData.settings.Monuments.Add("assets/bundled/prefabs/autospawn/monument/medium/compound.prefab", new List<monumentSpeakers>() {
new monumentSpeakers() {
enabled = false, locateID = 8675309, location = "-3.8, 1.7, 20.1", rotation = 2.034912f, URLS = new List<string>() {
"http://stream.dotpoint.nl:8000/hothitz", "https://streams.radiomast.io/8846a94e-9874-4692-a1a0-ec7aadbe2771"
}
}
}
);
SaveConfig();
}
mapName = World.GetServerBrowserMapName();
GetMapName();
}
private void GetMapName(int retries = 0) {
if (string.IsNullOrEmpty(mapName) || retries > 10) {
if (string.IsNullOrEmpty(SteamServer.MapName)) {
retries++;
PrintWarning($"Retrying to get map name in 10 seconds!");
timer.Once(10f, () => {
GetMapName(retries);
}
);
return;
}
else {
if (!string.IsNullOrEmpty(SteamServer.MapName)) {
mapName = SteamServer.MapName;
}
else {
mapName = "Unknown";
}
}
}
if (string.IsNullOrEmpty(mapName)) mapName = "Unknown";
PrintWarning($"Loaded on map name: {mapName}");
findMonuments();
}
private void Unload() {
foreach (DeployableBoomBox speaker in spawnedEntity) if (speaker != null) speaker.Kill();
_ = null;
}
#endregion Init
#region Classes
public class monumentSpeakers {
public bool enabled;
public ulong locateID;
public string location;
public float rotation;
public List<string> URLS;
}
#endregion
#region Hooks
private void findMonuments() {
List<monumentSpeakers> joinList = new List<monumentSpeakers>();
if (configData.vectorsettings.Vectors.Count > 0) SpawnSpeakersConfigVector("custom", configData.vectorsettings.Vectors);
foreach (var monument in TerrainMeta.Path.Monuments) {
string name = GetMonumentName(monument);
if (name == null || name == string.Empty) continue;
if (configData.settings.Monuments.ContainsKey(name)) joinList.AddRange(configData.settings.Monuments[name]);
if (!string.IsNullOrEmpty(mapName) && configData.customsettings.MapNames.ContainsKey(mapName) && configData.customsettings.MapNames[mapName].ContainsKey(name)) joinList.AddRange(configData.customsettings.MapNames[mapName][name]);
SpawnSpeakersConfig(name, monument, joinList);
joinList.Clear();
}
PrintWarning($"Spawned a total of {speakerCount} speakers.");
}
private void SpawnSpeakersConfig(string name, MonumentInfo monument, List<monumentSpeakers> speakerLocations) {
if (speakerLocations.Count <= 0) return;
foreach (monumentSpeakers speaker in speakerLocations) {
if (speaker.URLS == null || speaker.URLS.Count <= 0 || !speaker.enabled) continue;
Vector3 location = monument.transform.TransformPoint(ConvertFromString(speaker.location));
Quaternion rotation = Quaternion.Euler(monument.transform.localEulerAngles.x, monument.transform.localEulerAngles.y + speaker.rotation, monument.transform.localEulerAngles.z);
SpawnBoomBox(location, rotation, speaker.URLS.GetRandom(), speaker.locateID);
}
}
private void SpawnSpeakersConfigVector(string name, List<monumentSpeakers> speakerLocations) {
if (speakerLocations.Count <= 0) return;
foreach (monumentSpeakers speaker in speakerLocations) {
if (speaker.URLS == null || speaker.URLS.Count <= 0 || !speaker.enabled) continue;
Vector3 location = ConvertFromString(speaker.location);
Quaternion rotation = Quaternion.Euler(0, speaker.rotation, 0);
SpawnBoomBox(location, rotation, speaker.URLS.GetRandom(), speaker.locateID);
}
}
private void OnHammerHit(BasePlayer player, HitInfo info) {
if (!permission.UserHasPermission(player.UserIDString, permAdmin)) return;
DeployableBoomBox boomBox = info?.HitEntity as DeployableBoomBox;
if (boomBox != null) {
if (hammerHit.Contains(player.userID)) {
if (removeSpeaker(boomBox.OwnerID)) {
NextTick(() => {
boomBox?.Kill();
GameTips(player, lang.GetMessage("removed", this, player.UserIDString));
if (hammerHit.Contains(player.userID)) hammerHit.Remove(player.userID);
}
);
}
else {
GameTips(player, lang.GetMessage("notremoved", this, player.UserIDString));
}
}
else {
hammerHit.Add(player.userID);
GameTips(player, lang.GetMessage("hammer", this, player.UserIDString));
timer.Once(3f, () => {
if (hammerHit.Contains(player.userID)) hammerHit.Remove(player.userID);
}
);
}
}
}
private bool removeSpeaker(ulong locationID) {
foreach (var key in configData.settings.Monuments.Values) {
foreach (var id in key) {
if (id.locateID == locationID) {
key.Remove(id);
SaveConfig();
return true;
}
}
}
foreach (var key in configData.settings.Monuments.Values) {
foreach (var id in key) {
if (id.locateID == locationID) {
key.Remove(id);
SaveConfig();
return true;
}
}
}
foreach (var id in configData.vectorsettings.Vectors) {
if (id.locateID == locationID) {
configData.vectorsettings.Vectors.Remove(id);
SaveConfig();
return true;
}
}
return false;
}
public void GameTips(BasePlayer player, string message) {
if (player != null && player.userID.IsSteamId()) {
player?.SendConsoleCommand("gametip.hidegametip");
player?.SendConsoleCommand("gametip.showgametip", message);
timer.Once(4f, () => player?.SendConsoleCommand("gametip.hidegametip"));
}
}
private void SpawnBoomBox(Vector3 location, Quaternion rotation, string URL, ulong locationID) {
DeployableBoomBox speakerEntity = GameManager.server.CreateEntity("assets/prefabs/voiceaudio/boombox/boombox.deployed.prefab", location, rotation) as DeployableBoomBox;
if (speakerEntity != null) {
speakerEntity.enableSaving = false;
speakerEntity.IsStatic = true;
RemoveGroundWatch(speakerEntity);
speakerEntity.Spawn();
speakerEntity.OwnerID = locationID;
spawnedEntity.Add(speakerEntity);
RemoveGroundWatch(speakerEntity);
BoomBox boomBox = speakerEntity.BoxController;
Vector3 loc = new Vector3(-107.3504f, 12.1489f, -107.7641f);
speakerCount++;
if (boomBox != null) {
boomBox.CurrentRadioIp = URL;
if (boomBox.IsOn()) boomBox.ServerTogglePlay(false);
speakerEntity.ToEntity().UpdateFromInput(5, 0);
boomBox.SetFlag(BaseEntity.Flags.Busy, true);
NextTick(() => {
boomBox.baseEntity.ClientRPC<string>((Connection)null, "OnRadioIPChanged", boomBox.CurrentRadioIp);
if (!boomBox.IsOn()) boomBox.ServerTogglePlay(true);
if (!boomBox.IsInvoking(new Action(boomBox.DeductCondition))) return;
boomBox.CancelInvoke(new Action(boomBox.DeductCondition));
}
);
}
}
}
private void RemoveGroundWatch(BaseEntity entity) {
if (entity == null) return;
if (entity.GetComponent<GroundWatch>() != null) UnityEngine.Object.DestroyImmediate(entity.GetComponent<GroundWatch>());
if (entity.GetComponent<DestroyOnGroundMissing>() != null) UnityEngine.Object.DestroyImmediate(entity.GetComponent<DestroyOnGroundMissing>());
}
private ulong GenerateNewID() {
return (ulong)UnityEngine.Random.Range(11111111, 99999999999);
}
private void AddNewSpeakerLocationMapName(BasePlayer player) {
SendReply(player, "May cause lag while finding monument.. Run only once untell you see output!");
MonumentInfo monument = getClosestMonument(player);
if (monument == null) {
SendReply(player, "Could not find closest monument");
return;
}
string mname = GetMonumentName(monument);
if (string.IsNullOrEmpty(mapName)) {
SendReply(player, "Could not find the maps name!");
return;
}
if (mapName == "Procedural Map") {
SendReply(player, $"This is not detected as custom map MapName: {mapName}");
return;
}
if (string.IsNullOrEmpty(mname)) {
SendReply(player, "Could not find closest monument's name");
return;
}
if (!configData.customsettings.MapNames.ContainsKey(mapName)) configData.customsettings.MapNames.Add(mapName, new Dictionary<string, List<monumentSpeakers>>());
if (!configData.customsettings.MapNames[mapName].ContainsKey(mname)) configData.customsettings.MapNames[mapName].Add(mname, new List<monumentSpeakers>());
string localPos = monument.transform.InverseTransformPoint(player.transform.position).ToString().Replace("f", string.Empty).Replace("(", string.Empty).Replace(")", string.Empty);
var localY = player.viewAngles.y - monument.transform.rotation.eulerAngles.y;
ulong locationID = GenerateNewID();
configData.customsettings.MapNames[mapName][mname].Add(new monumentSpeakers() {
locateID = locationID, location = localPos, rotation = localY, enabled = true, URLS = new List<string>() {
"http://stream.dotpoint.nl:8000/hothitz", "https://streams.radiomast.io/8846a94e-9874-4692-a1a0-ec7aadbe2771"
}
}
);
SaveConfig();
PrintWarning($"Added new speaker to MapName:{SteamServer.MapName} MonumentName:{mname} Location:{localPos} Rotation:{localY}");
SendReply(player, $"Added new speaker to MapName:{SteamServer.MapName} MonumentName:{mname} Location:{localPos} Rotation:{localY}");
NextTick(() => {
Vector3 location = monument.transform.TransformPoint(ConvertFromString(localPos));
Quaternion rotation = Quaternion.Euler(monument.transform.localEulerAngles.x, monument.transform.localEulerAngles.y + localY, monument.transform.localEulerAngles.z);
SpawnBoomBox(location, rotation, "http://stream.dotpoint.nl:8000/hothitz", locationID);
}
);
}
private void AddNewSpeakerLocationVector(BasePlayer player) {
if (configData.vectorsettings.Vectors == null) configData.vectorsettings.Vectors = new List<monumentSpeakers>();
string localPos = player.transform.position.ToString().Replace("f", string.Empty).Replace("(", string.Empty).Replace(")", string.Empty);
var localY = player.viewAngles.y;
ulong locationID = GenerateNewID();
configData.vectorsettings.Vectors.Add(new monumentSpeakers() {
locateID = locationID, location = localPos, rotation = localY, enabled = true, URLS = new List<string>() {
"http://stream.dotpoint.nl:8000/hothitz", "https://streams.radiomast.io/8846a94e-9874-4692-a1a0-ec7aadbe2771"
}
}
);
SaveConfig();
PrintWarning($"Added new speaker to Vector Location:{localPos} Rotation:{localY} LocationID:{locationID}");
SendReply(player, $"Added new speaker to Vector Location:{localPos} Rotation:{localY} LocationID:{locationID}");
NextTick(() => {
Vector3 location = ConvertFromString(localPos);
Quaternion rotation = Quaternion.Euler(0, localY, 0);
SpawnBoomBox(location, rotation, "http://stream.dotpoint.nl:8000/hothitz", locationID);
}
);
}
private void AddNewSpeakerLocationMonument(BasePlayer player) {
SendReply(player, "May cause lag while finding monument.. Run only once untell you see output!");
MonumentInfo monument = getClosestMonument(player);
if (monument == null) {
SendReply(player, "Could not find closest monument");
return;
}
string mname = GetMonumentName(monument);
if (string.IsNullOrEmpty(mname)) {
SendReply(player, "Could not find closest monument's name");
return;
}
if (!configData.settings.Monuments.ContainsKey(mname)) configData.settings.Monuments.Add(mname, new List<monumentSpeakers>());
string localPos = monument.transform.InverseTransformPoint(player.transform.position).ToString().Replace("f", string.Empty).Replace("(", string.Empty).Replace(")", string.Empty);
var localY = player.viewAngles.y - monument.transform.rotation.eulerAngles.y;
ulong locationID = GenerateNewID();
configData.settings.Monuments[mname].Add(new monumentSpeakers() {
locateID = locationID, location = localPos, rotation = localY, enabled = true, URLS = new List<string>() {
"http://stream.dotpoint.nl:8000/hothitz", "https://streams.radiomast.io/8846a94e-9874-4692-a1a0-ec7aadbe2771"
}
}
);
SaveConfig();
PrintWarning($"Added new speaker to MonumentName:{mname} Location:{localPos} Rotation:{localY} LocationID:{locationID}");
SendReply(player, $"Added new speaker to MonumentName:{mname} Location:{localPos} Rotation:{localY} LocationID:{locationID}");
NextTick(() => {
Vector3 location = monument.transform.TransformPoint(ConvertFromString(localPos));
Quaternion rotation = Quaternion.Euler(monument.transform.localEulerAngles.x, monument.transform.localEulerAngles.y + localY, monument.transform.localEulerAngles.z);
SpawnBoomBox(location, rotation, "http://stream.dotpoint.nl:8000/hothitz", locationID);
}
);
}
private MonumentInfo getClosestMonument(BasePlayer player) {
float lowestDist = float.MaxValue;
MonumentInfo closest = null;
string name = "Unknown";
//UnityEngine.Object.FindObjectsOfType<MonumentInfo>()
foreach (var monument in TerrainMeta.Path.Monuments) {
if (GetMonumentName(monument) == null || GetMonumentName(monument).Contains("substation")) continue;
float dist = Vector3.Distance(player.transform.position, monument.transform.position);
if (dist < lowestDist) {
lowestDist = dist;
closest = monument;
name = GetMonumentName(monument);
if (name.Contains("monument_marker.prefab")) name = "\"" + monument.gameObject?.gameObject?.transform?.parent?.gameObject?.transform?.root?.name + "\"";
}
}
return closest;
}
private Vector3 ConvertFromString(string input) {
if (input != null) {
var vals = input.Split(',').Select(s => s.Trim()).ToArray();
if (vals.Length == 3) {
Single v1, v2, v3;
if (Single.TryParse(vals[0], out v1) && Single.TryParse(vals[1], out v2) && Single.TryParse(vals[2], out v3)) return new Vector3(v1, v2, v3);
else throw new ArgumentException();
}
else throw new ArgumentException();
}
else throw new ArgumentException();
}
public string GetMonumentName(MonumentInfo monument) {
if (monument == null) return string.Empty;
var gameObject = monument.gameObject;
while (gameObject.name.StartsWith("assets/") == false && gameObject.transform.parent != null) {
gameObject = gameObject.transform.parent.gameObject;
}
var monumentName = gameObject?.name;
if (monumentName.Contains("monument_marker.prefab")) monumentName = monument.gameObject?.gameObject?.transform?.parent?.gameObject?.transform?.root?.name;
return monumentName;
}
private void RegisterPermissions() {
permission.RegisterPermission(permAdmin, this);
}
#endregion
#region Commands
[ChatCommand("mmc")]
private void AddSpeakerToMap(BasePlayer player, string command, string[] args) {
if (!permission.UserHasPermission(player.UserIDString, permAdmin)) {
SendReply(player, lang.GetMessage("noPerm", this, player.UserIDString));
return;
}
if (args.Length <= 0) {
SendReply(player, lang.GetMessage("usage", this, player.UserIDString));
return;
}
switch (args[0].ToLower()) {
case "maponly" : AddNewSpeakerLocationMapName(player);
return;
case "mylocation" : AddNewSpeakerLocationVector(player);
return;
case "monument" : AddNewSpeakerLocationMonument(player);
return;
default : SendReply(player, lang.GetMessage("usage", this, player.UserIDString));
break;
}
}
#endregion
#region Config
private ConfigData configData;
class ConfigData {
[JsonProperty(PropertyName = "Monument spawn locations")]
public Settings settings {
get;
set;
}
[JsonProperty(PropertyName = "Custom map monument spawn locations")]
public customSettings customsettings {
get;
set;
}
[JsonProperty(PropertyName = "Vector3 spawn locations")]
public vectorSettings vectorsettings {
get;
set;
}
public class Settings {
public Dictionary<string, List<monumentSpeakers>> Monuments {
get;
set;
}
}
public class customSettings {
public Dictionary<string, Dictionary<string, List<monumentSpeakers>>> MapNames {
get;
set;
}
}
public class vectorSettings {
public List<monumentSpeakers> Vectors {
get;
set;
}
}
public Oxide.Core.VersionNumber Version {
get;
set;
}
}
protected override void LoadConfig() {
base.LoadConfig();
configData = Config.ReadObject<ConfigData>();
if (configData.Version < Version) UpdateConfigValues();
Config.WriteObject(configData, true);
}
protected override void LoadDefaultConfig() => configData = GetBaseConfig();
private ConfigData GetBaseConfig() {
return new ConfigData {
settings = new ConfigData.Settings {
Monuments = new Dictionary<string, List<monumentSpeakers>>()
}
, customsettings = new ConfigData.customSettings {
MapNames = new Dictionary<string, Dictionary<string, List<monumentSpeakers>>>()
}
, vectorsettings = new ConfigData.vectorSettings {
Vectors = new List<monumentSpeakers>()
}
, Version = Version
}
;
}
protected override void SaveConfig() => Config.WriteObject(configData, true);
private void UpdateConfigValues() {
PrintWarning("Config update detected! Updating config values...");
ConfigData baseConfig = GetBaseConfig();
if (configData.Version < new VersionNumber(1, 0, 0)) configData = baseConfig;
configData.Version = Version;
PrintWarning("Config update completed!");
}
#endregion Config
#region Lang
private new void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["usage"]
= "Adds a music speaker to locations\n\n<color=#FFFF00>/mmc monument</color> - Adds speaker to monument.\n<color=#FFFF00>/mmc mylocation</color> - Adds speaker to your world location.\n<color=#FFFF00>/mmc maponly</color> - Adds speaker to monument for map only.\n<color=#FFFF00>/mmc</color> - View this help text.", ["noPerm"] = "<color=#ce422b>You do not have the correct permission to use this command.</color>", ["hammer"] = "Hit again with in 3 seconds to remove speaker.", ["removed"] = "Speaker located and removed from config.", ["notremoved"] = "Speaker not found in config."
}
, this);
}
#endregion
}
}