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

FREE RandomTrader : 1.0.7 1.0.7

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using WebSocketSharp;
/*
 * Add an item to the vending machine listing so they dont show x.*/
/* 1.0.7
 * Added support for custom icon colours based on the shop profile.* Added config option to disable chat announcements.* Added console commands for spawntrader and despawntrader.*/
namespace Oxide.Plugins {
    [Info("Random Trader", "imthenewguy", "1.0.7")]
    [Description("Spawns a random vending machine along the road with a random selection of items.")]
    class RandomTrader : RustPlugin {
#region Config
        private Configuration config;
        public class Configuration {
            [JsonProperty("Notification settings")]
            public NotificationsInfo notificationSettings = new NotificationsInfo();
            [JsonProperty("Currency type [scrap, economics, serverrewards]")]
            public string currency = "scrap";
            [JsonProperty("Trader Information")]
            public SpawnInfo trader_info = new SpawnInfo();
            [JsonProperty("Shop Information - each entry requires its own unique CopyPaste fileName.")]
            public Dictionary<string, ShopInfo> shop_info = new Dictionary<string, ShopInfo>(StringComparer.InvariantCultureIgnoreCase);
            public string ToJson() => JsonConvert.SerializeObject(this);
            public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
        }
        public class SpawnInfo {
            [JsonProperty("How many traders can spawn at a time?")]
            public int max_spawned = 1;
            [JsonProperty("Minimum time that a trader will stay for [seconds]?")]
            public float min_lifetime = 300f;
            [JsonProperty("Maximum time that a trader will stay for [seconds]?")]
            public float max_lifetime = 600f;
            [JsonProperty("Minmum time between attempts to spawn a new store [seconds]")]
            public float spawn_min_delay = 1800f;
            [JsonProperty("Maximum time between attempts to spawn a new store [seconds]")]
            public float spawn_max_delay = 3600f;
            [JsonProperty("Distance from the side of the road to spawn the trader")]
            public float trader_distance = 15f;
            [JsonProperty("Height adjustment of spawned prefabs")]
            public float prefab_height_adjustment = 0.5f;
        }
        public class ShopInfo {
            [JsonProperty("Enable this shop?")]
            public bool enabled = true;
            [JsonProperty("Unique CopyPaste file name for this list.")]
            public string file_name;
            [JsonProperty("The name of the shop.")]
            public string shop_name;
            [JsonProperty("Limit of purchases per player [0 = no limit]")]
            public int shop_purchase_limit = 0;
            [JsonProperty("How many items from the list should we randomly pick? [0 = all]")]
            public int shop_items_picked = 16;
            [JsonProperty("List of items that you wish to list in this store.")]
            public List<ItemInfo> items = new List<ItemInfo>();
            [JsonProperty("Map icon settings")]
            public IconSettings iconSettings = new IconSettings();
        }
        public class IconSettings {
            [JsonProperty("Inner icon colour")]
            public ColorSettings color_inner = new ColorSettings(0f, 0f, 1f);
            [JsonProperty("Outer icon colour")]
            public ColorSettings color_outer = new ColorSettings(1f, 0f, 1f);
            [JsonProperty("Icon alpha")]
            public float alpha = 1f;
            [JsonProperty("Icon radius")]
            public float radius = 0.25f;
        }
        public class ColorSettings {
            public float red;
            public float green;
            public float blue;
            public ColorSettings(float r, float g, float b) {
                this.red = r;
                this.green = g;
                this.blue = b;
            }
        }
        public class NotificationsInfo {
            [JsonProperty("Send announcements via in-game chat?")]
            public bool send_chat_announcements = true;
            [JsonProperty("GUIAnnouncements plugin settings")]
            public GUIAnnouncementsInfo GUIAnnouncements = new GUIAnnouncementsInfo();
        }
        public class GUIAnnouncementsInfo {
            [JsonProperty("Send announcements using the GUIAnnouncements plugin?")]
            public bool enabled = true;
            [JsonProperty("Banner colour - see GUIAnnouncements for colours")]
            public string banner_colour = "Purple";
            [JsonProperty("Text colour - see GUIAnnouncements for colours")]
            public string text_colour = "Yellow";
            [JsonProperty("Position adjustment")]
            public float position_adjustment = 0;
        }
        public class ItemInfo {
            public string name;
            public ulong skin;
            public int max_quantity;
            public int min_quantity;
            public bool use_random_quantity;
            public string shortname;
            public KeyValuePair<string, string> img_url;
            public int cost_min;
            public int cost_max;
            public string store_display_name;
            public string text;
            [JsonConstructor]
            public ItemInfo() {
            }
            public ItemInfo(string shortname, ulong skin, int min_quantity, int max_quantity, int cost_min, int cost_max, bool use_random_quantity = true, string name = null, KeyValuePair<string, string> img_url = new KeyValuePair<string, string>(), string store_display_name = null, string text = null) {
                this.shortname = shortname;
                this.skin = skin;
                this.min_quantity = min_quantity;
                this.max_quantity = max_quantity;
                this.cost_min = cost_min;
                this.cost_max = cost_max;
                this.use_random_quantity = use_random_quantity;
                this.name = name;
                this.img_url = img_url;
                this.store_display_name = store_display_name;
                this.text = text;
            }
        }
        protected override void LoadDefaultConfig() {
            config = new Configuration();
            Config_Item_Defs.AddRange(ItemManager.GetItemDefinitions());
            config.shop_info.Add("items", new ShopInfo() {
                file_name = "RoadsideVending", shop_name = "Random Item Trader", items = GetRandomItems()
            }
            );
            config.shop_info.Add("weapons", new ShopInfo() {
                file_name = "WeaponVending", shop_name = "Random Weapon Trader", items = GetRandomItemsOfType(ItemCategory.Weapon)
            }
            );
            config.shop_info.Add("clothing", new ShopInfo() {
                file_name = "ClothingVending", shop_name = "Random Clothing Trader", items = GetRandomItemsOfType(ItemCategory.Attire)
            }
            );
            config.shop_info.Add("tools", new ShopInfo() {
                file_name = "ToolVending", shop_name = "Random Tool Trader", items = GetRandomItemsOfType(ItemCategory.Tool)
            }
            );
            Config_Item_Defs.Clear();
        }
        List<ItemDefinition> Config_Item_Defs = new List<ItemDefinition>();
        List<ItemInfo> GetRandomItemsOfType(ItemCategory catagory) {
            var result = new List<ItemInfo>();
            if (Config_Item_Defs.Count == 0) Config_Item_Defs = ItemManager.GetItemDefinitions();
            var itemDefs = Config_Item_Defs.Where(x => x.category == catagory);
            foreach (var item in itemDefs) {
                if ((item.category == ItemCategory.Weapon)) {
                    if (item.isHoldable && (item.occupySlots == ItemSlot.None || item.occupySlots == 0)) result.Add(new ItemInfo(item.shortname, 0, 1, 5, 100, 1000));
                }
                else if ((item.category == ItemCategory.Tool)) {
                    if (item.isHoldable && (item.occupySlots == ItemSlot.None || item.occupySlots == 0)) result.Add(new ItemInfo(item.shortname, 0, 1, 3, 25, 300));
                }
                else if (item.category == ItemCategory.Attire) {
                    if (item.isWearable && ((item.Blueprint != null && item.Blueprint.userCraftable) || item.condition.repairable)) result.Add(new ItemInfo(item.shortname, 0, 1, 5, 50, 500));
                }
            }
            return result;
        }
        // Adds 16 random definitions to the default config.List<ItemInfo> GetRandomItems()
        {
            var result = new List<ItemInfo>();
            if (Config_Item_Defs.Count == 0) Config_Item_Defs = ItemManager.GetItemDefinitions();
            var itemDefs = Config_Item_Defs;
            List<ItemDefinition> randomDefs = Pool.GetList<ItemDefinition>();
            List<ItemDefinition> uniqueDefs = Pool.GetList<ItemDefinition>();
            for (int i = 0; i < 128; i++) {
                uniqueDefs.AddRange(itemDefs.Where(x => !randomDefs.Contains(x)));
                randomDefs.Add(uniqueDefs.GetRandom());
                uniqueDefs.Clear();
            }
            Pool.FreeList(ref uniqueDefs);
            foreach (var def in randomDefs) {
                result.Add(new ItemInfo(def.shortname, 0, def.category == ItemCategory.Resources ? 100 : 1, def.category == ItemCategory.Resources ? 5000 : 10, def.category == ItemCategory.Resources ? 1 : 10, def.category == ItemCategory.Resources ? 10 : 1000));
            }
            Pool.FreeList(ref randomDefs);
            return result;
        }
        protected override void LoadConfig() {
            base.LoadConfig();
            try {
                config = Config.ReadObject<Configuration>();
                if (config == null) {
                    throw new JsonException();
                }
                if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) {
                    PrintToConsole("Configuration appears to be outdated; updating and saving");
                    SaveConfig();
                }
            }
            catch {
                PrintToConsole($"Configuration file {Name}.json is invalid; using defaults");
                LoadDefaultConfig();
            }
            SaveConfig();
        }
        protected override void SaveConfig() {
            PrintToConsole($"Configuration changes saved to {Name}.json");
            Config.WriteObject(config, true);
        }
#endregion
#region Data
        PlayerEntity pcdData;
        private DynamicConfigFile PCDDATA;
        public string CurrencyTypeString;
        const string perm_admin = "randomtrader.admin";
        const string perm_use = "randomtrader.use";
        void Init() {
            permission.RegisterPermission(perm_admin, this);
            permission.RegisterPermission(perm_use, this);
            PCDDATA = Interface.Oxide.DataFileSystem.GetFile(this.Name);
            LoadData();
            foreach (var def in ItemManager.GetItemDefinitions()) {
                if (!ItemDefs.ContainsKey(def.shortname)) ItemDefs.Add(def.shortname, def);
            }
            Puts($"ItemDefs length: {ItemDefs.Count}");
            CurrencyTypeString = config.currency.Equals("scrap", StringComparison.OrdinalIgnoreCase) ? "{0} scrap" : config.currency.Equals("economics", StringComparison.OrdinalIgnoreCase) ? "${0}" : "{0} points";
        }
        Dictionary<string, ItemDefinition> ItemDefs = new Dictionary<string, ItemDefinition>();
        void Unload() {
            foreach (var kvp in trade_entities) {
                try {
                    ClearShop(kvp.Key);
                }
                catch {
                }
            }
            SaveData();
            foreach (var player in BasePlayer.activePlayerList) {
                DestroyUI(player);
            }
            ProfileDownloads.Clear();
        }
        void SaveData() {
            PCDDATA.WriteObject(pcdData);
        }
        void LoadData() {
            try {
                pcdData = Interface.Oxide.DataFileSystem.ReadObject<PlayerEntity>(this.Name);
            }
            catch {
                Puts("Couldn't load player data, creating new Playerfile");
                pcdData = new PlayerEntity();
            }
        }
        class PlayerEntity {
            public Dictionary<ulong, PCDInfo> pEntity = new Dictionary<ulong, PCDInfo>();
            public List<ulong> spawned_entities = new List<ulong>();
        }
        class PCDInfo {
        }
#endregion;
#region Localization
        protected override void LoadDefaultMessages() {
            lang.RegisterMessages(new Dictionary<string, string> {
                ["SpawnUINotValid"]
                = "{0} is not a valid profile.", ["SpawnUIUsage"] = "Usage: /spawntrader <profile name>\n Valid profiles: {0}", ["despawntrader_NoTraders"] = "There are no traders spawned right now.", ["despawntrader_Usage"] = "Usage (case sensitive): /despawntrader <profile name>\nSpawned traders: {0}", ["despawntrader_confirmed"] = "Despawned: {0}.", ["rtremovestoredata_Usage"] = "This command will remove the desired shop data from the config.\nUsage: /rtremovestoredata <store name>\nValid stores: {0}", ["rtremovestoredata_Removed"] = "Removed data for: {0}", ["rtfindnewshops_added"] = "Added {0} new to the config.", ["rtfindnewshops_Failed"] = "Could not find any new shops.", ["CreateBuilding_Failed"] = "Attempted to spawn the {0} trader, but it is already spawned.", ["CreateBuilding_Failed_At_Limit"] = "Could not spawn the trader as we are already at the trader limit [{0}]", ["BuildingCreated_Success"] = "A random trader has appeared at <color=#FF0000>{0}</color> and will be there for <color=#ffff00>{1} seconds</color>.", ["ClearShop_Left"] = "The trader at <color=#FF0000>{0}</color> has left.", ["MenuDelayWait"] = "Please wait a moment before attempting to buy something else.", ["PurchasedMax"] = "You have purchased the maximum amount of items from this vendor.", ["NoStock"] = "This vendor has no stock left for {0}.", ["CannotAfford"] = "You cannot afford this item.", ["EconErrorCash"] = "You do not have enough cash to purchase this item.", ["SRNoPoints"] = "You do not have enough points to purchase this item.", ["SRPointError"] = "Error taking points.", ["UIName"] = "Name", ["UIStock"] = "Stock", ["UIPrice"] = "Price", ["UIPurchaseLimitDisplayed"] = "<color=#8f0303>Purchase limit: {0}</color>", ["UIBuy"] = "BUY"
            }
            , this);
        }
#endregion
#region hooks
        void SendGUIAnnouncement(BasePlayer player, string message) {
            if (!config.notificationSettings.GUIAnnouncements.enabled || GUIAnnouncements == null || !GUIAnnouncements.IsLoaded) return;
            GUIAnnouncements.Call("CreateAnnouncement", message, config.notificationSettings.GUIAnnouncements.banner_colour, config.notificationSettings.GUIAnnouncements.text_colour, player, config.notificationSettings.GUIAnnouncements.position_adjustment);
        }
        void ResetSpawnTimer() {
            if (SpawnTimer != null && !SpawnTimer.Destroyed) SpawnTimer.Destroy();
            var randomTime = UnityEngine.Random.Range(config.trader_info.spawn_min_delay, config.trader_info.spawn_max_delay);
            SpawnTimer = timer.Once(randomTime, () => {
                try {
                    SpawnRandomTrader();
                }
                catch {
                }
                ResetSpawnTimer();
            }
            );
        }
        Timer SpawnTimer;
        private Dictionary<string, string> loadOrder = new Dictionary<string, string>();
        void OnServerInitialized(bool initial) {
            if (ImageLibrary == null) {
                Puts("ImageLibrary is required to run this plugin.");
                Interface.Oxide.UnloadPlugin(Name);
                return;
            }
            if (CopyPaste == null) {
                Puts("CopyPaste is required to run this plugin.");
                Interface.Oxide.UnloadPlugin(Name);
                return;
            }
            bool DoSave = false;
            if (config.currency.Equals("economics", StringComparison.OrdinalIgnoreCase) && (Economics == null)) {
                Puts("Reset currency to scrap as Economics is not loaded.");
                config.currency = "scrap";
                DoSave = true;
            }
            else if (config.currency.Equals("serverrewards", StringComparison.OrdinalIgnoreCase) && (ServerRewards == null)) {
                Puts("Reset currency to scrap as ServerRewards is not loaded.");
                config.currency = "scrap";
                DoSave = true;
            }
            AddFiles();
            DeploymentPoints = GetDeploymentPoints();
            bool foundEnabled = false;
            foreach (var entry in config.shop_info) {
                if (entry.Value.enabled) foundEnabled = true;
                foreach (var shopItem in entry.Value.items) {
                    if (!string.IsNullOrEmpty(shopItem.img_url.Key)) loadOrder.Add(shopItem.img_url.Key, shopItem.img_url.Value);
                }
                if (entry.Value.iconSettings == null) {
                    entry.Value.iconSettings = new IconSettings();
                    DoSave = true;
                }
                if (!IsColourValid(entry.Value.iconSettings.color_outer)) {
                    Puts($"Found invalid colour settings for {entry.Key} outer colour - resetting.");
                    entry.Value.iconSettings.color_outer = new ColorSettings(1f, 0f, 1f);
                    DoSave = true;
                }
                if (!IsColourValid(entry.Value.iconSettings.color_inner)) {
                    Puts($"Found invalid colour settings for {entry.Key} inner colour - resetting.");
                    entry.Value.iconSettings.color_inner = new ColorSettings(0f, 0f, 1f);
                    DoSave = true;
                }
                if (entry.Value.iconSettings.alpha < 0 || entry.Value.iconSettings.alpha > 1) {
                    Puts($"Found invalid alpha for {entry.Key} - resetting.");
                    entry.Value.iconSettings.alpha = 1f;
                    DoSave = true;
                }
                if (entry.Value.iconSettings.radius < 0) {
                    Puts($"Found invalid radius for {entry.Key} - resetting.");
                    entry.Value.iconSettings.radius = 0.25f;
                    DoSave = true;
                }
            }
            if (!foundEnabled) {
                foreach (var kvp in config.shop_info) {
                    kvp.Value.enabled = true;
                }
                DoSave = true;
            }
            if (DoSave) SaveConfig();
            Puts($"Loading {loadOrder.Count} images into ImageLibrary");
            ImageLibrary.Call("ImportImageList", this.Name, loadOrder, 0ul, false, new Action(ImagesReady));
            ResetSpawnTimer();
            timer.Once(30f, () => {
                Puts($"Checking for new item lists.");
                List<string> current_stores = Pool.GetList<string>();
                current_stores.AddRange(config.shop_info.Keys);
                Interface.CallHook("RandomTraderReady", current_stores);
                Pool.FreeList(ref current_stores);
            }
            );
            foreach (var entity in BaseNetworkable.serverEntities) {
                if (entity != null && entity.isSpawned && pcdData.spawned_entities.Contains(entity.net.ID.Value)) {
                    entity.Kill();
                }
            }
            pcdData.spawned_entities.Clear();
        }
        bool IsColourValid(ColorSettings colors) {
            if (colors == null) return false;
            if (colors.red < 0 || colors.red > 1.0) return false;
            if (colors.green < 0 || colors.green > 1.0) return false;
            if (colors.blue < 0 || colors.blue > 1.0) return false;
            return true;
        }
        private void ImagesReady() {
            loadOrder.Clear();
            loadOrder = null;
            Puts($"Loaded all images for {this.Name}.");
        }
        object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info) {
            if (entity != null && entity.OwnerID == 69) {
                if (info != null && info.damageTypes != null) info.damageTypes.ScaleAll(0.0f);
                return true;
            }
            return null;
        }
        object CanPickupEntity(BasePlayer player, VendingMachine vm) {
            if (vending_keys.ContainsKey(vm)) return false;
            return null;
        }
#endregion
#region Vending
        //Used to identify which shop to send.Dictionary<VendingMachine, string> vending_keys = new Dictionary<VendingMachine, string>();
        object OnVendingShopOpen(VendingMachine machine, BasePlayer player) {
            string shop;
            if (!vending_keys.TryGetValue(machine, out shop) || !trade_entities.ContainsKey(shop)) return null;
            SendShop(player, shop);
            return true;
        }
#endregion
#region chat commands
        [ChatCommand("spawntrader")]
        void SpawnTraderChat(BasePlayer player, string command, string[] args) {
            if (!HasPerm(player, true)) return;
            if (args == null || args.Length == 0 || !config.shop_info.ContainsKey(args[0]) || !config.shop_info[args[0]].enabled) {
                PrintToChat(player, (args.Length > 0 ? string.Format(lang.GetMessage("SpawnUINotValid", this, player.UserIDString), args[0]) : null) + string.Format(lang.GetMessage("SpawnUIUsage", this, player.UserIDString), string.Join(", ", config.shop_info.Where(x => x.Value.enabled).Select(y => y.Key))));
                return;
            }
            CreateBuilding(args[0], player);
        }
        [ConsoleCommand("spawntrader")]
        void SpawnTraderConsole(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player != null && !HasPerm(player, true)) return;
            if (arg.Args == null || arg.Args.Length == 0 || !config.shop_info.ContainsKey(arg.Args[0]) || !config.shop_info[arg.Args[0]].enabled) {
                arg.ReplyWith((arg.Args != null && arg.Args.Length > 0 ? string.Format(lang.GetMessage("SpawnUINotValid", this, player?.UserIDString ?? null), arg.Args[0]) : null) + string.Format(lang.GetMessage("SpawnUIUsage", this, player?.UserIDString ?? null), string.Join(", ", config.shop_info.Where(x => x.Value.enabled).Select(y => y.Key))));
                return;
            }
            CreateBuilding(arg.Args[0], player);
        }
        [ChatCommand("despawntrader")]
        void DespawnTrader(BasePlayer player, string command, string[] args) {
            if (!HasPerm(player, true)) return;
            if (args == null || args.Length == 0 || !trade_entities.ContainsKey(args[0])) {
                if (trade_entities.Count == 0) PrintToChat(player, lang.GetMessage("despawntrader_NoTraders", this, player.UserIDString));
                PrintToChat(player, string.Format(lang.GetMessage("despawntrader_Usage", this, player.UserIDString), string.Join(", ", trade_entities.Keys)));
                return;
            }
            ClearShop(args[0], true);
            PrintToChat(player, string.Format(lang.GetMessage("despawntrader_confirmed", this, player.UserIDString), args[0]));
        }
        [ConsoleCommand("despawntrader")]
        void DespawnTraderConsole(ConsoleSystem.Arg arg) {
            var player = arg.Player();
            if (player != null && !HasPerm(player, true)) return;
            if (arg.Args == null || arg.Args.Length == 0 || !trade_entities.ContainsKey(arg.Args[0])) {
                if (trade_entities.Count == 0) arg.ReplyWith(lang.GetMessage("despawntrader_NoTraders", this, player?.UserIDString));
                arg.ReplyWith(string.Format(lang.GetMessage("despawntrader_Usage", this, player?.UserIDString), string.Join(", ", trade_entities.Keys)));
                return;
            }
            ClearShop(arg.Args[0], true);
            arg.ReplyWith(string.Format(lang.GetMessage("despawntrader_confirmed", this, player?.UserIDString), arg.Args[0]));
        }
        // Clears the shop data.[ChatCommand("rtremovestoredata")]
        void RemoveStoreData(BasePlayer player, string command, string[] args) {
            if (!HasPerm(player, true)) return;
            string shop = args.Length > 0 ? config.shop_info.Keys.FirstOrDefault(x => x.Equals(args[0], StringComparison.OrdinalIgnoreCase)) : null;
            if (string.IsNullOrEmpty(shop)) {
                PrintToChat(player, string.Format(lang.GetMessage("rtremovestoredata_Usage", this, player.UserIDString), string.Join(", ", config.shop_info.Keys)));
                return;
            }
            config.shop_info.Remove(shop);
            SaveConfig();
            PrintToChat(player, string.Format(lang.GetMessage("rtremovestoredata_Removed", this, player.UserIDString), shop));
        }
        [ChatCommand("rtfindnewshops")]
        void FindNewShops(BasePlayer player) {
            var old_shop_count = config.shop_info.Count;
            if (!HasPerm(player, true)) return;
            List<string> current_stores = Pool.GetList<string>();
            current_stores.AddRange(config.shop_info.Keys);
            Interface.CallHook("RandomTraderReady", current_stores);
            Pool.FreeList(ref current_stores);
            int newShops = 0;
            if ((newShops = config.shop_info.Count - old_shop_count) > 0) PrintToChat(player, string.Format(lang.GetMessage("rtfindnewshops_added", this, player.UserIDString), newShops));
            else PrintToChat(player, lang.GetMessage("rtfindnewshops_Failed", this, player.UserIDString));
        }
#endregion
#region Spawning traders
        void SpawnRandomTrader() {
            List<string> random_traders = Pool.GetList<string>();
            foreach (var kvp in config.shop_info) {
                if (trade_entities.ContainsKey(kvp.Key) || !kvp.Value.enabled) continue;
                random_traders.Add(kvp.Key);
            }
            if (random_traders.Count > 0) CreateBuilding(random_traders.GetRandom(), null);
            Pool.FreeList(ref random_traders);
        }
        void CreateBuilding(string name, BasePlayer player = null) {
            Puts($"Attempting to spawn {name} trader.");
            if (trade_entities.ContainsKey(name)) {
                if (player != null) PrintToChat(player, string.Format(lang.GetMessage("CreateBuilding_Failed", this, player.UserIDString), name));
                else Puts(string.Format(lang.GetMessage("CreateBuilding_Failed", this, player.UserIDString), name));
                return;
            }
            if (trade_entities.Count >= config.trader_info.max_spawned) {
                if (player != null) PrintToChat(player, string.Format(lang.GetMessage("CreateBuilding_Failed_At_Limit", this, player.UserIDString), config.trader_info.max_spawned));
                else Puts(string.Format(lang.GetMessage("CreateBuilding_Failed_At_Limit", this, player.UserIDString), config.trader_info.max_spawned));
                return;
            }
            ShopInfo si;
            if (!config.shop_info.TryGetValue(name, out si)) return;
            Vector3 pos = DeploymentPoints.GetRandom();
            for (int i = 0; i < DeploymentPoints.Count; i++) {
                var prospectivePos = DeploymentPoints.GetRandom();
                if (!IsGround(prospectivePos)) continue;
                Puts($"Found after: {i} attempts.");
                pos = prospectivePos;
                break;
            }
            //pos = DeploymentPoints.GetRandom();
            Puts($"Spawning {name} trader at {pos}.");
            CopyPaste.Call("TryPasteFromVector3", pos, 0f, si.file_name, new string[] {
                "auth", "false", "stability", "false"
            }
            );
        }
        List<Vector3> GetDeploymentPoints() {
            var result = new List<Vector3>();
            List<PathList> paths = Pool.GetList<PathList>();
            paths.AddRange(TerrainMeta.Path.Roads);
            foreach (var path in paths) {
                if (path.Path?.Points.Length > 0) {
                    for (int i = 0; i < path.Path.Points.Length; i++) {
                        if (i > 0) {
                            float spacing = path.Width + config.trader_info.trader_distance;
                            var pos_right = AdjustForHeight(path.Path.Points[i] + Vector3.right * spacing);
                            if (IsValidTopology(pos_right)) result.Add(pos_right);
                            var pos_left = AdjustForHeight(path.Path.Points[i] + Vector3.left * spacing);
                            if (IsValidTopology(pos_left)) result.Add(pos_left);
                        }
                    }
                }
            }
            Pool.FreeList(ref paths);
            Puts($"Found {result.Count} valid points to spawn traders.");
            return result;
        }
        [ChatCommand("rtcheckpoints")]
        void CheckPointS(BasePlayer player) {
            if (!HasPerm(player, true)) return;
            var wasAdmin = player.IsAdmin;
            if (!wasAdmin) {
                player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, true);
                player.SendNetworkUpdateImmediate();
            }
            foreach (var point in DeploymentPoints) {
                player.SendConsoleCommand("ddraw.text", 20f, Color.red, point, "X");
            }
            if (!wasAdmin) {
                player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, false);
                player.SendNetworkUpdateImmediate();
            }
        }
        //Credit to Nivex for GetSpawnHeight snippet.private const int targetMask = Layers.Mask.World | Layers.Mask.Terrain | Layers.Mask.Default;
        public static float GetSpawnHeight(Vector3 target) {
            float y = TerrainMeta.HeightMap.GetHeight(target);
            float w = TerrainMeta.WaterMap.GetHeight(target);
            float p = TerrainMeta.HighestPoint.y + 250f;
            RaycastHit hit;
            if (Physics.Raycast(target.WithY(p), Vector3.down, out hit, ++p, targetMask, QueryTriggerInteraction.Ignore)) {
                y = Mathf.Max(y, hit.point.y);
            }
            return Mathf.Max(y, w);
        }
        List<Vector3> DeploymentPoints;
        bool IsValidTopology(Vector3 pos) {
            if (!IsGround(pos)) return false;
            if (ContainsTopology(TerrainTopology.Enum.Monument, pos, config.trader_info.trader_distance)) return false;
            if (TerrainMeta.TopologyMap.GetTopology(pos, TerrainTopology.ROAD)) return false;
            if (TerrainMeta.TopologyMap.GetTopology(pos, TerrainTopology.CLIFFSIDE)) return false;
            if (TerrainMeta.TopologyMap.GetTopology(pos, TerrainTopology.RAIL)) return false;
            if (TerrainMeta.TopologyMap.GetTopology(pos, TerrainTopology.MONUMENT)) return false;
            return true;
        }
        bool IsGround(Vector3 pos) {
            RaycastHit hitInfo;
            if (!Physics.Raycast(pos + new Vector3(0, 60f, 0), Vector3.down, out hitInfo, 66f, LayerMask.GetMask("Terrain", "World", "Construction", "Water"), QueryTriggerInteraction.Ignore) || hitInfo.collider == null) return false;
            //if (hitInfo.collider.name.ToLower().Contains("road")) return true;
            //Puts($"Hit info: {hitInfo.collider.name}");
            if (!hitInfo.collider.name.Equals("Terrain", StringComparison.OrdinalIgnoreCase)) return false;
            int hits = Physics.OverlapSphereNonAlloc(pos, 5f, Vis.colBuffer, -1, QueryTriggerInteraction.Collide);
            bool result = true;
            for (int i = 0; i < hits; i++) {
                if (result) {
                    if (Convert.ToBoolean(Vis.colBuffer[i]?.name.Contains("prevent_building_sphere"))) result = false;
                    if (Convert.ToBoolean(Vis.colBuffer[i]?.name.Contains("Cliff"))) result = false;
                    if (Convert.ToBoolean(Vis.colBuffer[i]?.name.Contains("PreventBuilding"))) result = false;
                    if (Convert.ToBoolean(Vis.colBuffer[i]?.name.Contains("formation"))) result = false;
                }
                Vis.colBuffer[i] = null;
            }
            return result;
        }
        // Credit Nivex
        public static bool ContainsTopology(TerrainTopology.Enum mask, Vector3 position, float radius) {
            return (TerrainMeta.TopologyMap.GetTopology(position, radius) & (int)mask) != 0;
        }
        Vector3 AdjustForHeight(Vector3 pos) {
            pos.y = GetSpawnHeight(pos) + config.trader_info.prefab_height_adjustment;
            return new Vector3(pos.x, TerrainMeta.HeightMap.GetHeight(pos), pos.z);
        }
#endregion
#region Instance handling
        [PluginReference]
        private Plugin CopyPaste, Economics, ServerRewards, ImageLibrary, GUIAnnouncements;
        //Name - TraderEntities
        Dictionary<string, TraderEntities> trade_entities = new Dictionary<string, TraderEntities>(StringComparer.InvariantCultureIgnoreCase);
        public class TraderEntities {
            public List<BaseEntity> pasted_entities = new List<BaseEntity>();
            public VendingMachineMapMarker vending_machine_marker;
            public MapMarkerGenericRadius vending_map_markers;
            public List<VendingMachine> machines = new List<VendingMachine>();
            public List<TradeInstance> shop = new List<TradeInstance>();
            public Dictionary<BasePlayer, int> purchases = new Dictionary<BasePlayer, int>();
            public Vector3 pos;
        }
        public class TradeInstance {
            public string shortname;
            public ulong skin;
            public int stock;
            public string name;
            public KeyValuePair<string, string> img_url;
            public int cost;
            public string store_display_name;
            public string text;
            public TradeInstance(string shortname, ulong skin, int stock, int cost, string name = null, KeyValuePair<string, string> img_url = new KeyValuePair<string, string>(), string store_display_name = null, string text = null) {
                this.shortname = shortname;
                this.skin = skin;
                this.stock = stock;
                this.cost = cost;
                this.name = name;
                this.img_url = img_url;
                this.store_display_name = store_display_name;
                this.text = text;
            }
        }
        void RemovePastedEntities(string key) {
            TraderEntities te;
            if (!trade_entities.TryGetValue(key, out te)) return;
            Puts("Removing old paste");
            foreach (var entity in te.pasted_entities) {
                if (entity == null || !entity.isSpawned) continue;
                pcdData.pEntity.Remove(entity.net.ID.Value);
                entity.Kill();
            }
            te.pasted_entities.Clear();
        }
        void RemovePastedEntities(TraderEntities te) {
            if (te == null) return;
            Puts("Removing old paste");
            foreach (var entity in te.pasted_entities) {
                try {
                    entity.KillMessage();
                }
                catch {
                }
            }
            te.pasted_entities.Clear();
        }
        void RemoveMapMarkers(VendingMachineMapMarker vending_machine_marker, MapMarkerGenericRadius vending_map_markers) {
            if (vending_machine_marker != null) vending_machine_marker.KillMessage();
            if (vending_map_markers != null) vending_map_markers.KillMessage();
        }
        VendingMachineMapMarker CreateVendingMachineMapMarker(Vector3 pos, ShopInfo shopInfo) {
            var Vmarker = GameManager.server.CreateEntity("assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab", pos) as VendingMachineMapMarker;
            Vmarker.markerShopName = shopInfo.shop_name;
            Vmarker.enabled = false;
            Vmarker.Spawn();
            return Vmarker;
        }
        MapMarkerGenericRadius CreateGenericMapMarker(Vector3 pos, ShopInfo shopInfo) {
            var marker = GameManager.server.CreateEntity("assets/prefabs/tools/map/genericradiusmarker.prefab", pos) as MapMarkerGenericRadius;
            marker.color2 = new Color(shopInfo.iconSettings.color_outer.red, shopInfo.iconSettings.color_outer.green, shopInfo.iconSettings.color_outer.blue);
            //marker.color2 = Color.magenta;
            marker.color1 = new Color(shopInfo.iconSettings.color_inner.red, shopInfo.iconSettings.color_inner.green, shopInfo.iconSettings.color_inner.blue);
            //marker.color1 = Color.blue;
            marker.alpha = shopInfo.iconSettings.alpha;
            marker.radius = shopInfo.iconSettings.radius;
            marker.enabled = true;
            marker.Spawn();
            marker.SendUpdate();
            return marker;
        }
        void DestroyUI(BasePlayer player) {
            // Add UI here
            CuiHelper.DestroyUi(player, "RandomTraderShop");
        }
        void RemoveSavingFromChildren(BaseEntity entity) {
            foreach (var child in entity.children) {
                child.EnableSaving(false);
                if (child.children != null && child.children.Count > 0) RemoveSavingFromChildren(child);
            }
        }
        void OnPasteFinished(List<BaseEntity> pastedEntities, string filename) {
            var kvp = config.shop_info.FirstOrDefault(x => x.Value.file_name == filename);
            if (string.IsNullOrEmpty(kvp.Key)) return;
            TraderEntities te;
            if (trade_entities.ContainsKey(kvp.Key)) {
                ClearShop(kvp.Key, true);
            }
            trade_entities.Add(kvp.Key, te = new TraderEntities());
            // Setting owner id to 69 means we can identify it when it takes damage and prevent it.foreach (var entity in pastedEntities)
            {
                try {
                    entity.EnableSaving(false);
                    RemoveSavingFromChildren(entity);
                    entity.OwnerID = 69;
                    entity.SendNetworkUpdateImmediate();
                }
                catch {
                }
            }
            te.pasted_entities.AddRange(pastedEntities);
            foreach (var entity in pastedEntities) {
                try {
                    pcdData.spawned_entities.Add(entity.net.ID.Value);
                }
                catch {
                }
            }
            var pos = te.pasted_entities.First().transform.position;
            te.pos = pos;
            te.vending_machine_marker = CreateVendingMachineMapMarker(pos, kvp.Value);
            te.vending_map_markers = CreateGenericMapMarker(pos, kvp.Value);
            te.machines.AddRange(pastedEntities.OfType<VendingMachine>());
            foreach (var vendingMachine in te.machines) {
                vending_keys.Add(vendingMachine, kvp.Key);
            }
            // Setup shop
            te.shop.Clear();
            List<ItemInfo> random_items = Pool.GetList<ItemInfo>();
            List<ItemInfo> temp_list = Pool.GetList<ItemInfo>();
            for (int i = 0; i < (kvp.Value.items.Count > kvp.Value.shop_items_picked ? kvp.Value.shop_items_picked : kvp.Value.items.Count);
            i++) {
                temp_list.AddRange(kvp.Value.items.Where(x => !random_items.Contains(x)));
                random_items.Add(temp_list.GetRandom());
                temp_list.Clear();
            }
            Pool.FreeList(ref temp_list);
            foreach (var item in random_items) {
                if (item == null) Puts("Found null item.");
                te.shop.Add(new TradeInstance(item.shortname, item.skin, item.use_random_quantity ? UnityEngine.Random.Range(item.min_quantity, item.max_quantity + 1) : item.max_quantity, UnityEngine.Random.Range(item.cost_min, item.cost_max + 1), item.name, item.img_url, item.store_display_name, item.text));
            }
            Pool.FreeList(ref random_items);
            var randomTime = UnityEngine.Random.Range(config.trader_info.min_lifetime, config.trader_info.max_lifetime);
            foreach (var player in BasePlayer.activePlayerList) {
                string str = string.Format(lang.GetMessage("BuildingCreated_Success", this, player.UserIDString), GetGrid(pos, false), Math.Round(randomTime, 0));
                if (config.notificationSettings.send_chat_announcements) PrintToChat(player, str);
                SendGUIAnnouncement(player, str);
            }
            ClearActiveShopTimer(kvp.Key);
            Shop_Spawn_Timer.Add(kvp.Key, timer.Once(randomTime, () => {
                ClearShop(kvp.Key, true);
            }
            ));
            //var randomTime = UnityEngine.Random.Range(config.trader_info.min_lifetime, config.trader_info.max_lifetime);
            //string name = kvp.Key;
            //te._timer = timer.Once(randomTime, () =>
            //{
            //    ClearShop(name, true);
            //});
            //PrintToChat($"A random trader has appeared at <color=#FF0000>{GetGrid(pos, false)}</color> and will be there for <color=#ffff00>{Math.Round(randomTime, 0)} seconds</color>.");
        }
        void ClearActiveShopTimer(string shop) {
            if (!Shop_Spawn_Timer.ContainsKey(shop)) return;
            if (Shop_Spawn_Timer[shop] != null && !Shop_Spawn_Timer[shop].Destroyed) Shop_Spawn_Timer[shop].Destroy();
            Shop_Spawn_Timer.Remove(shop);
        }
        Dictionary<string, Timer> Shop_Spawn_Timer = new Dictionary<string, Timer>();
        void ClearShop(string name, bool remove_entry = false) {
            ClearActiveShopTimer(name);
            TraderEntities te;
            if (!trade_entities.TryGetValue(name, out te)) return;
            if (te.machines.Count > 0) {
                foreach (var player in BasePlayer.activePlayerList) {
                    var message = string.Format(lang.GetMessage("ClearShop_Left", this, player.UserIDString), GetGrid(te.machines.First().transform.position, false));
                    if (config.notificationSettings.send_chat_announcements) PrintToChat(player, message);
                    SendGUIAnnouncement(player, message);
                }
            }
            foreach (var machine in te.machines) {
                vending_keys.Remove(machine);
            }
            RemoveMapMarkers(te.vending_machine_marker, te.vending_map_markers);
            RemovePastedEntities(te);
            // Set a timer to respawn
            if (remove_entry) trade_entities.Remove(name);
        }
#endregion
#region UI
        bool HasPerm(BasePlayer player, bool require_admin = false) {
            if (!require_admin) return permission.UserHasPermission(player.UserIDString, perm_admin) || permission.UserHasPermission(player.UserIDString, perm_use);
            else return permission.UserHasPermission(player.UserIDString, perm_admin);
        }
        private void SendShop(BasePlayer player, string name, int firstIndex = 0) {
            if (!HasPerm(player) || !config.shop_info.ContainsKey(name)) return;
            TraderEntities te;
            if (!trade_entities.TryGetValue(name, out te)) {
                if (firstIndex > 0) CuiHelper.DestroyUi(player, "RandomTraderShop");
                return;
            }
            var container = new CuiElementContainer();
            container.Add(new CuiPanel {
                CursorEnabled = true, Image = {
                    Color = "0 0 0 0.99"
                }
                , RectTransform = {
                    AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "-0.351 0.334", OffsetMax = "0.349 -0.326"
                }
            }
            , "Overlay", "RandomTraderShop");
            container.Add(new CuiElement {
                Name = "title", Parent = "RandomTraderShop", Components = {
                    new CuiTextComponent {
                        Text = config.shop_info[name].shop_name?.ToUpper() ?? "RANDOM SHOP", Font = "robotocondensed-bold.ttf", FontSize = 24, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1"
                    }
                    , new CuiRectTransformComponent {
                        AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-520.699 218.705", OffsetMax = "520.701 263.695"
                    }
                }
            }
            );
            container.Add(new CuiPanel {
                CursorEnabled = false, Image = {
                    Color = "1 1 1 0"
                }
                , RectTransform = {
                    AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-208.374 178.5", OffsetMax = "208.376 203.18"
                }
            }
            , "RandomTraderShop", "TitlesPanel");
            container.Add(new CuiElement {
                Name = "Name", Parent = "TitlesPanel", Components = {
                    new CuiTextComponent {
                        Text = lang.GetMessage("UIName", this, player.UserIDString), Font = "robotocondensed-bold.ttf", FontSize = 14, Align = TextAnchor.MiddleLeft, Color = "1 1 1 1"
                    }
                    , new CuiRectTransformComponent {
                        AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-138.916 -12.34", OffsetMax = "-32.126 12.34"
                    }
                }
            }
            );
            container.Add(new CuiElement {
                Name = "Stock", Parent = "TitlesPanel", Components = {
                    new CuiTextComponent {
                        Text = lang.GetMessage("UIStock", this, player.UserIDString), Font = "robotocondensed-bold.ttf", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1"
                    }
                    , new CuiRectTransformComponent {
                        AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-32.126 -12.34", OffsetMax = "17.251 12.34"
                    }
                }
            }
            );
            container.Add(new CuiElement {
                Name = "Price", Parent = "TitlesPanel", Components = {
                    new CuiTextComponent {
                        Text = lang.GetMessage("UIPrice", this, player.UserIDString), Font = "robotocondensed-bold.ttf", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1"
                    }
                    , new CuiRectTransformComponent {
                        AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "52.938 -12.34", OffsetMax = "115.21 12.34"
                    }
                }
            }
   
// ... [Code preview truncated due to length. Full source in attached file] ...

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