using System.Collections.Generic; using Newtonsoft.Json; using Oxide.Core; using Oxide.Core.Libraries.Covalence; using UnityEngine; using Rust; namespace Oxide.Plugins { [Info("Monument Helper", "Bazz3l", "1.0.2")] [Description("")] public class MonumentHelper : CovalencePlugin { #region Fields private const string PERM_USE = "monumenthelper.use"; private readonly HashSet _controllers = new HashSet(); private PluginConfig _config; private PluginData _data; #endregion #region Config protected override void LoadDefaultConfig() => _config = PluginConfig.DefaultConfig(); protected override void LoadConfig() { base.LoadConfig(); try { _config = Config.ReadObject(); if (_config == null) throw new JsonException(); } catch { LoadDefaultConfig(); PrintWarning("Loaded default config."); } } protected override void SaveConfig() => Config.WriteObject(_config, true); private class PluginConfig { [JsonProperty("Chat command")] public string Command; [JsonProperty("Display message prefix")] public bool EnablePrefix; [JsonProperty("Display message when players enter a monument")] public bool EnableEnter; [JsonProperty("Display message when players a leave monument")] public bool EnableLeave; [JsonProperty("Wipe player toggles on new map save")] public bool EnableWipeToggles = false; [JsonProperty("Monuments")] public Dictionary Monuments; public static PluginConfig DefaultConfig() { return new PluginConfig { Command = "mh", EnablePrefix = true, EnableEnter = true, EnableLeave = true, Monuments = new Dictionary() }; } } private class MonumentConfig { private string[] _messageArgs; public string DisplayName; public string Description; public bool Enabled; public Vector3 Center; public Vector3 Bounds; [JsonIgnore] public string[] MessageArgs { get { if (_messageArgs == null) _messageArgs = new [] { DisplayName, Description }; return _messageArgs; } } } #endregion #region Storage private PluginData LoadData() => Interface.Oxide.DataFileSystem.ReadObject(nameof(MonumentHelper)); private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(nameof(MonumentHelper), _data); private class PluginData { public HashSet PlayerToggles = new HashSet(); public bool HasToggled(string userID) => PlayerToggles.Contains(userID); public string Toggled(string userID) { if (!PlayerToggles.Remove(userID)) { PlayerToggles.Add(userID); return LangMessages.TOGGLE_DISABLE; } return LangMessages.TOGGLE_ENABLED; } } #endregion #region Lang protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { { LangMessages.PREFIX, "Monument Helper: type /mh to toggle on and off.\n" }, { LangMessages.MONUMENT_ENTER, "You have entered {0}.\n{1}" }, { LangMessages.MONUMENT_LEAVE, "You left {0}" }, { LangMessages.TOGGLE_ENABLED, "Enabled" }, { LangMessages.TOGGLE_DISABLE, "Disabled" }, { LangMessages.TOGGLED, "was {0}" }, }, this); } private void MessagePlayer(IPlayer player, string key, params object[] args) { player?.Message(_config.EnablePrefix ? string.Format(lang.GetMessage(LangMessages.PREFIX, this, player.Id) + lang.GetMessage(key, this, player.Id), args) : string.Format(lang.GetMessage(key, this, player.Id), args)); } private static class LangMessages { public const string PREFIX = "Prefix"; public const string MONUMENT_LEAVE = "MonumentLeave"; public const string MONUMENT_ENTER = "MonumentEnter"; public const string TOGGLE_ENABLED = "ToggleEnabled"; public const string TOGGLE_DISABLE = "ToggleDisable"; public const string TOGGLED = "Toggled"; } #endregion #region Oxide private void OnServerInitialized() { AddCovalenceCommand(_config.Command, nameof(ToggleCommand), PERM_USE); UpdateAndCreate(); } private void Init() => _data = LoadData(); private void Unload() { foreach (var controller in _controllers) controller.Destroy(); SaveData(); } private void OnNewSave(string filename) { if (!_config.EnableWipeToggles) return; _data.PlayerToggles.Clear(); SaveData(); } #endregion #region Monument private readonly Dictionary _defaultDescriptions = new Dictionary { {"Harbor", "Low-tier loot and a recycler can be found here."}, {"Fishing Village", "A safe zone to fish, purchase items and vehicles for the water."}, {"Large Fishing Village", "A safe zone to fish, purchase items and vehicles for the water."}, {"Abandoned Military Base", "Medium-value loot and key cards can be found here. Watch out for armed scientists."}, {"Arctic Research Base", "Medium-value loot, key cards and snowmobiles can be found here. Watch out for armed scientists"}, {"Bandit Camp\n", "Here you can gamble scrap, recycle items and purchase goods. Be careful not to inflict damage around these parts."}, {"Outpost", "Here you can gamble scrap, recycle items and purchase goods. Be careful not to inflict damage around these parts."}, {"Junkyard", "A useful place to find loot and crush vehicles."}, {"Ranch", "Yeehaw Cowboy!! Here you can get yourself a fast set of legs."}, {"Military Tunnel", "High-value loot can be found in this area. Watch out for armed scientists and radiation."}, {"Train Yard", "Medium-value loot can be found in this area. Watch out for armed scientists."}, {"Sulfur Quarry", "Place low grade fuel in the quarry to start mining sulfur."}, {"Satellite Dish", "A useful monument for loot, key cards and recycling items."}, {"HQM Quarry", "Place low-grade fuel in the quarry to start mining high-quality metal."}, {"The Dome", "Loot can be found at the top, but watch your step its a long way down."}, {"Stone Quarry", "Place low-grade fuel in the quarry to start mining stone."}, {"Water Well", "Feeling a little parched? Pump the well to quench your thirst."}, {"Abandoned Cabins", "Nobody knows what happened to the people that lived here."}, {"Wild Swamp", "Murky water... be sure to filter the water before drinking."}, {"Ice Lake", ""}, {"Train Tunnel", "Medium-value loot can be found in this area. Watch out for armed scientists."}, {"Abandoned Supermarket", "A useful place to find loot, food, recycle items and find key cards to progress."}, {"Mining Outpost", "Low-value loot and a recycler can be found here."}, {"Oxum's Gas Station", "A useful place to find loot, food, recycle items and find key cards to progress."}, {"Substation", ""}, {"Underwater Lab", "High-value loot can be found in this area. Watch out for armed scientists."}, {"Oil Rig", "High-value loot can be found in this area. Watch out for armed scientists."}, {"Large Oil Rig", "High-value loot can be found in this area. Watch out for armed scientists."}, {"Lighthouse", "Low-value loot, key cards and a recycler can be found here."} }; private readonly List _blacklist = new List { "Substation", "Ice Lake" }; private string FindDefaultDescription(string monumentName) { string description; return _defaultDescriptions.TryGetValue(monumentName, out description) ? description : string.Empty; } private void UpdateAndCreate() { var hasChanged = false; foreach (var monumentInfo in TerrainMeta.Path.Monuments) { var displayName = monumentInfo.displayPhrase.english; if (string.IsNullOrEmpty(displayName) || _blacklist.Contains(displayName)) continue; if (!_config.Monuments.ContainsKey(displayName)) { _config.Monuments[displayName] = new MonumentConfig { DisplayName = displayName, Description = FindDefaultDescription(displayName), Enabled = monumentInfo.isActiveAndEnabled, Center = monumentInfo.Bounds.center, Bounds = monumentInfo.Bounds.size }; hasChanged = true; } var monumentSetting = _config.Monuments[displayName]; if (monumentSetting != null) CreateNotifier(monumentInfo.transform, monumentSetting); } if (!hasChanged) return; SaveConfig(); } private void CreateNotifier(Transform transform, MonumentConfig monumentConfig) { if (!monumentConfig.Enabled || monumentConfig.Bounds == Vector3.zero) return; var gameObject = new GameObject { layer = (int)Layer.Reserved2, transform = { position = transform.position, rotation = transform.rotation } }; var collider = gameObject.AddComponent(); collider.isTrigger = true; collider.center = monumentConfig.Center; collider.size = monumentConfig.Bounds; var component = gameObject.AddComponent(); component.MonumentConfig = monumentConfig; if (_config.EnableEnter) component.OnEnter += OnEnterMonument; if (_config.EnableLeave) component.OnLeave += OnLeaveMonument; _controllers.Add(component); } private void OnEnterMonument(IPlayer player, string[] message) { if (!player.HasPermission(PERM_USE)) return; if (_data.HasToggled(player.Id)) return; MessagePlayer(player, LangMessages.MONUMENT_ENTER, message[0], message[1]); } private void OnLeaveMonument(IPlayer player, string[] message) { if (!player.HasPermission(PERM_USE)) return; if (_data.HasToggled(player.Id)) return; MessagePlayer(player, LangMessages.MONUMENT_LEAVE, message[0]); } private class NotifierController : MonoBehaviour { public MonumentConfig MonumentConfig; public delegate void EventHandler(TSender sender, TArgs args); public event EventHandler OnEnter; public event EventHandler OnLeave; private void OnTriggerEnter(Collider collider) { var player = collider?.ToBaseEntity() as BasePlayer; if (player == null || player.IsNpc || !player.userID.IsSteamId()) return; OnEnter?.Invoke(player.IPlayer, MonumentConfig.MessageArgs); } private void OnTriggerExit(Collider collider) { var player = collider?.ToBaseEntity() as BasePlayer; if (player == null || player.IsNpc || !player.userID.IsSteamId()) return; OnLeave?.Invoke(player.IPlayer, MonumentConfig.MessageArgs); } public void Destroy() { if (!gameObject.IsUnityNull()) DestroyImmediate(gameObject); } } #endregion #region Command private void ToggleCommand(IPlayer player, string command, string[] args) { var option = _data.Toggled(player.Id); MessagePlayer(player, LangMessages.TOGGLED, lang.GetMessage(option, this, player.Id)); } #endregion } }