Иконка ресурса

FREE MonumentHelper : 1.0.4 1.0.4

Нет прав для скачивания
C#:
/*
 * Copyright (c) 2026 Bazz3l
 * Author Bazz3l
 * Discord bazz3l
 *
 * Monument Helper cannot be copied, edited and/or (re)distributed without the express permission of the Author.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

using System.Collections.Generic;
using System.IO;
using System;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Configuration;
using Oxide.Core;
using Newtonsoft.Json;
using UnityEngine;

namespace Oxide.Plugins;

[Info("Monument Helper", "Bazz3l", "1.0.4")]
[Description("")]
internal class MonumentHelper : CovalencePlugin
{
    #region Fields

    private const string PERM_USE = "monumenthelper.use";
    private BaseStorage<PlayerData> _playerData;
    private PluginConfig _config;

    private static Dictionary<string, string> GetMonumentDetails()
    {
        return new Dictionary<string, string>
        {
            {"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<br>", "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."},
            {"Ferry Terminal", "A coastal monument with crates, barrels and recycler. No radiation or scientists."},
            {"Launch Site", "High-value loot, key cards, and blueprints are available. Armed scientists and radiation make this location dangerous."},
            {"Missile Silo", "High-tier loot and blueprints inside a highly fortified structure. Expect armed scientists."}
        };
    }

    private static List<string> GetBlacklistDefaults()
    {
        return new List<string>
        {
            "Substation",
            "Ice Lake"
        };
    }
    
    #endregion
    
    #region Lang

    protected override void LoadDefaultMessages()
    {
        lang.RegisterMessages(new Dictionary<string, string>
        {
            { LangKeys.PREFIX, "<color=#3669CF>Monument Helper</color>: type /mh to toggle on and off.<br>" },
            { LangKeys.MONUMENT_ENTER, "You have entered <color=#3BA55C>{0}</color>.<br>{1}" },
            { LangKeys.MONUMENT_LEAVE, "You left <color=#ED4245>{0}</color>" },
            { LangKeys.TOGGLE_ENABLED, "<color=#3BA55C>Enabled</color>" },
            { LangKeys.TOGGLE_DISABLE, "<color=#ED4245>Disabled</color>" },
            { LangKeys.TOGGLED, "was {0}" },
        }, this);
    }

    private static class LangKeys
    {
        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";
    }
    
    private void MessagePlayer(IPlayer? player, string langKey, params object[] args)
    {
        if (player == null || !player.IsConnected) return;
        string message = string.Format(lang.GetMessage(langKey, this, player.Id), args);
        player.Message(_config.EnablePrefix ? string.Format(lang.GetMessage(LangKeys.PREFIX, this, player.Id) + message) : message);
    }

    #endregion
    
    #region Config

    protected override void LoadDefaultConfig() => _config = PluginConfig.DefaultConfig();

    protected override void LoadConfig()
    {
        base.LoadConfig();

        try
        {
            _config = Config.ReadObject<PluginConfig>() ?? throw new JsonException();
        }
        catch(Exception e)
        {
            LoadDefaultConfig();
            PrintWarning("Loaded default config: {0}", e.Message);
        }
    }

    protected override void SaveConfig() => Config.WriteObject(_config, true);

    private class PluginConfig
    {
        [JsonProperty("Chat command")]
        public string Command { get; set; }
        [JsonProperty("Display message prefix")]
        public bool EnablePrefix { get; set; }
        [JsonProperty("Display message when players enter a monument")]
        public bool EnableEnter { get; set; }
        [JsonProperty("Display message when players a leave monument")]
        public bool EnableLeave { get; set; }
        [JsonProperty("Wipe player toggles on new map save")]
        public bool EnableWipeToggles { get; set; }
        [JsonProperty("Monuments")]
        public Dictionary<string, MonumentConfig> Monuments;

        public static PluginConfig DefaultConfig()
        {
            return new PluginConfig
            {
                Command = "mh",
                EnablePrefix = true,
                EnableEnter = true,
                EnableLeave = true,
                Monuments = new Dictionary<string, MonumentConfig>()
            };
        }
    }

    private class MonumentConfig
    {
        public string DisplayName { get; set; }
        public string Description { get; set; }
        public bool Enabled { get; set; }
        public Vector3 Center { get; set; }
        public Vector3 Bounds { get; set; }

        private string[]? _messageArgs;
        
        [JsonIgnore]
        public string[] MessageArgs
        {
            get
            {
                _messageArgs ??= new[] { DisplayName, Description };
                return _messageArgs;
            }
        }

        public static MonumentConfig New(bool enabled, string displayName, string description, Vector3 center, Vector3 bounds)
        {
            return new MonumentConfig
            {
                Enabled = enabled,
                DisplayName = displayName,
                Description = description,
                Center = center,
                Bounds = bounds
            };
        }
    }

    #endregion

    #region Storage
    
    private class BaseStorage<T> where T : new()
    {
        private DynamicConfigFile _cfg;
        public T Data;
        public bool IsDirty;
            
        public void Load(params string[] paths)
        {
            string filepath = Path.Combine(paths);
            _cfg = Interface.Oxide.DataFileSystem.GetFile(filepath);
                
            try
            {
                Data = _cfg.ReadObject<T>() ?? throw new Exception();
            }
            catch
            {
                Data = new T();
                Interface.Oxide.LogDebug("[BankHeist] - Failed to load {0}; loaded default data.", filepath);
            }
        }
            
        public void Save()
        {
            if (!IsDirty)
                return;

            IsDirty = false;
            
            _cfg.WriteObject(Data, true);
        }
    }
    
    private class PlayerData
    {
        public HashSet<string> PlayerToggles = new();

        public bool HasToggled(string userID) => PlayerToggles.Contains(userID);
        
        public string Toggled(string userID)
        {
            if (!PlayerToggles.Remove(userID))
            {
                PlayerToggles.Add(userID);
                
                return LangKeys.TOGGLE_DISABLE;
            }
            
            return LangKeys.TOGGLE_ENABLED;
        }
    }

    private void LoadData()
    {
        _playerData = new BaseStorage<PlayerData>();
        _playerData.Load(Name, "PlayerData");
    }
    
    private void SaveData()
    {
        if (_playerData != null)
            _playerData.Save();
    }

    private void WipeData()
    {
        if (_playerData == null) 
            return;
        
        _playerData.Data.PlayerToggles.Clear();
        _playerData.IsDirty = true;
        _playerData.Save();
    }
    
    #endregion

    #region Oxide Hooks

    private void OnServerInitialized()
    {
        AddCovalenceCommand(_config.Command, nameof(ToggleCommand), PERM_USE);
        
        UpdateAndCreate();
    }

    private void Init()
    {
        permission.RegisterPermission(PERM_USE, this);
        
        LoadData();
    }

    private void Unload()
    {
        MonumentTrigger.OnUnload();
        
        SaveData();
    }

    private void OnNewSave(string filename)
    {
        if (!_config.EnableWipeToggles)
            return;

        WipeData();
    }

    #endregion
    
    #region Trigger Management
    
    private class MonumentTrigger : TriggerBase
    {
        public static readonly List<MonumentTrigger> MonumentTriggers = new();
        public Func<BaseNetworkable, string[], bool> OnEnter;
        public Func<BaseNetworkable, string[], bool> OnLeave;
        public MonumentConfig MonumentConfig;

        public void Initialize()
        {
            OnEntityEnterTrigger = (networkable) => OnEnter(networkable, MonumentConfig.MessageArgs);
            OnEntityLeaveTrigger = (networkable) => OnLeave(networkable, MonumentConfig.MessageArgs);
        }
        
        public void Destroy()
        {
            OnEntityEnterTrigger = null;
            OnEntityLeaveTrigger = null;
            
            MonumentTrigger.DestroyMonumentTrigger(this);
        }
        
        public override GameObject? InterestedInObject(GameObject obj)
        {
            obj = base.InterestedInObject(obj);
            if (obj == null) return null;
            
            BasePlayer? baseEntity = obj.ToBaseEntity() as BasePlayer;
            return baseEntity != null && baseEntity.isServer ? baseEntity.gameObject : null;
        }

        public static void OnUnload()
        {
            if (Rust.Application.isQuitting) 
                return;
            
            foreach (MonumentTrigger trigger in new List<MonumentTrigger>(MonumentTriggers))
                trigger.Destroy();
        }

        public static void DestroyMonumentTrigger(MonumentTrigger monumentTrigger)
        {
            if (monumentTrigger == null)
                return;
            
            DestroyImmediate(monumentTrigger?.gameObject);
        }
    }

    private void UpdateAndCreate()
    {
        bool hasChanged = false;

        Dictionary<string, string> monumentDetails = GetMonumentDetails();
        List<string> blockedList = GetBlacklistDefaults();
        
        foreach (MonumentInfo monumentInfo in TerrainMeta.Path.Monuments)
        {
            string? displayName = monumentInfo?.displayPhrase?.english;
            if (string.IsNullOrEmpty(displayName) || blockedList.Contains(displayName)) 
                continue;
            
            if (!_config.Monuments.TryGetValue(displayName, out MonumentConfig? monumentConfig))
            {
                monumentDetails.TryGetValue(displayName, out string? description);
                monumentConfig = MonumentConfig.New(monumentInfo.isActiveAndEnabled, displayName, description ?? string.Empty, monumentInfo.Bounds.center, monumentInfo.Bounds.size);
                
                _config.Monuments[displayName] = monumentConfig;
                
                hasChanged = true;
            }

            if (monumentConfig == null) 
                continue;
            
            CreateMonumentTrigger(monumentInfo.transform, monumentConfig);
        }
        
        if (!hasChanged)
            return;
        
        SaveConfig();
    }
    
    private void CreateMonumentTrigger(Transform transform, MonumentConfig config)
    {
        if (!config.Enabled || config.Bounds == Vector3.zero)
            return;

        GameObject gameObject = new GameObject("Monument_Trigger");
        gameObject.layer = (int)Rust.Layer.Trigger;
        gameObject.transform.SetPositionAndRotation(transform.position, transform.rotation);
        
        BoxCollider collider = gameObject.AddComponent<BoxCollider>();
        collider.isTrigger = true;
        collider.center = config.Center;
        collider.size = config.Bounds;
        
        MonumentTrigger trigger = gameObject.AddComponent<MonumentTrigger>();
        trigger.MonumentConfig = config;
        if (_config.EnableEnter) trigger.OnEnter = OnMonumentTriggerEnter;
        if (_config.EnableLeave) trigger.OnLeave = OnMonumentTriggerLeave;
        trigger.InterestLayers = Rust.Layers.Server.Players;
        trigger.Initialize();
        
        MonumentTrigger.MonumentTriggers.Add(trigger);
    }

    private bool OnMonumentTriggerEnter(BaseNetworkable networkable, string[] message)
    {
        BasePlayer? player = networkable as BasePlayer;
        if (player == null || player.IsNpc || player.IPlayer == null)
            return false;
        
        if (!player.IPlayer.HasPermission(PERM_USE))
            return false;

        if (_playerData.Data.HasToggled(player.UserIDString))
            return false;

        MessagePlayer(player.IPlayer, LangKeys.MONUMENT_ENTER, message[0], message[1]);
        return true;
    }
    
    private bool OnMonumentTriggerLeave(BaseNetworkable networkable, string[] message)
    {
        BasePlayer? player = networkable as BasePlayer;
        if (player == null || player.IsNpc || player.IPlayer == null)
            return false;
        
        if (!player.IPlayer.HasPermission(PERM_USE))
            return false;

        if (_playerData.Data.HasToggled(player.UserIDString))
            return false;
        
        MessagePlayer(player.IPlayer, LangKeys.MONUMENT_LEAVE, message[0]);
        return true;
    }

    #endregion
    
    #region Chat Command

    private void ToggleCommand(IPlayer player, string command, string[] args)
    {
        string option = _playerData.Data.Toggled(player.Id);
        MessagePlayer(player, LangKeys.TOGGLED, lang.GetMessage(option, this, player.Id));
    }

    #endregion
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху