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

FREE BuildingHealthManager : 1.0.6 1.0.6

Нет прав для скачивания
C#:
using System.Collections.Generic;
using System.Collections;
using System.Globalization;
using System.Linq;
using System.IO;
using System;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
namespace Oxide.Plugins {
    [Info("Building Health Manager", "Miho", "1.0.6")]
    [Description("Managing the Health of Construction")]
    public class BuildingHealthManager : RustPlugin {
        [PluginReference]
        private Plugin RaidableBases;
#region Fields
        private const string PermDefault = "buildinghealthmanager.default";
        private const string PermVIP = "buildinghealthmanager.vip";
        private const string PermVVIP = "buildinghealthmanager.vvip";
        private const string PermAdmin = "buildinghealthmanager.admin";
        private const string PanelName = "bhm_panel";
        private const string ConfigVersion = "1.0.6";
        private const float LegacyHealthMarker = 100f;
        private const string NameKeyPrefix = "name.";
        private const int Columns = 4;
        private const int Rows = 2;
        private const int PerPage = Columns * Rows;
        private const double MenuLeft = 0.02;
        private const double MenuRight = 0.2;
        private const double MenuBottom = 0.715;
        private const double MenuHeight = 0.085;
        private const double MenuPitch = 0.115;
        private const double ContentLeft = 0.225;
        private const double ColPitch = 0.19;
        private const double TileWidth = 0.175;
        private const double RowTop = 0.855;
        private const double RowPitch = 0.42;
        private const double TileHeight = 0.39;
        private const double NameHeight = 0.038;
        private const double ImageTop = 0.046;
        private const double ImageHeight = 0.145;
        private const double ImageWidth = 0.086;
        private const double TierTop = 0.199;
        private const double TierHeight = 0.033;
        private const double SaveTop = 0.339;
        private const double SaveHeight = 0.045;
        private const double SaveWidth = 0.09;
        private ConfigData config;
        private readonly Dictionary<string, string> prefabCategory = new Dictionary<string, string>();
        private readonly Dictionary<string, List<string>> categoryPrefabs = new Dictionary<string, List<string>>();
        private readonly Dictionary<ulong, float> originalMaxHealth = new Dictionary<ulong, float>();
        private readonly HashSet<string> multiplierApplied = new HashSet<string>();
        private readonly Dictionary<string, string> imageIds = new Dictionary<string, string>();
        private readonly Dictionary<ulong, int> playerPage = new Dictionary<ulong, int>();
        private readonly Dictionary<ulong, Dictionary<string, string>> inputValues = new Dictionary<ulong, Dictionary<string, string>>();
        private Coroutine imageRoutine;
        private int statChanged;
        private int statNoPermission;
        private int statNoTarget;
        private int statBlocksScaled;
        private int statBlocksBroken;
        private int statRestored;
#endregion
#region Category tables
        private static readonly string[] BuildingBlocks = {
            "foundation", "foundation.triangle", "wall", "wall.frame", "wall.window", "wall.doorway", "floor", "floor.frame", "floor.triangle", "floor.triangle.frame", "roof", "roof.triangle", "wall.half", "wall.low", "ramp", "block.stair.ushape", "block.stair.lshape", "foundation.steps", "block.stair.spiral.triangle", "block.stair.spiral"
        }
        ;
        private static readonly Dictionary<string, int> DoorDefaults = new Dictionary<string, int> {
            {
                "door.hinged.wood", 200
            }
            , {
                "door.hinged.metal", 250
            }
            , {
                "door.hinged.toptier", 1000
            }
            , {
                "door.double.hinged.wood", 200
            }
            , {
                "door.double.hinged.metal", 250
            }
            , {
                "door.double.hinged.toptier", 1000
            }
            , {
                "wall.frame.garagedoor", 600
            }
            , {
                "door.hinged.industrial.d", 250
            }
            , {
                "gates.external.high.wood", 500
            }
            , {
                "gates.external.high.stone", 500
            }
            , {
                "shutter.wood.a", 200
            }
            , {
                "floor.ladder.hatch", 250
            }
            , {
                "floor.ladder.hatch.toptier", 1000
            }
            , {
                "wall.frame.cell.gate", 300
            }
            , {
                "floor.triangle.ladder.hatch", 250
            }
            , {
                "floor.triangle.ladder.hatch.toptier", 1000
            }
            , {
                "wall.frame.fence.gate", 75
            }
            , {
                "wall.frame.shopfront", 500
            }
        }
        ;
        private static readonly Dictionary<string, int> ExternalDefaults = new Dictionary<string, int> {
            {
                "wall.external.high.wood", 500
            }
            , {
                "wall.external.high.stone", 500
            }
            , {
                "wall.external.high.ice", 500
            }
            , {
                "icewall", 200
            }
        }
        ;
        private static readonly Dictionary<string, int> BarricadeDefaults = new Dictionary<string, int> {
            {
                "barricade.woodwire", 400
            }
            , {
                "barricade.concrete", 500
            }
            , {
                "barricade.metal", 600
            }
            , {
                "barricade.sandbags", 200
            }
            , {
                "barricade.stone", 100
            }
            , {
                "barricade.wood", 250
            }
            , {
                "barricade.cover.wood", 120
            }
        }
        ;
        private static readonly Dictionary<string, int> WindowDefaults = new Dictionary<string, int> {
            {
                "shutter.metal.embrasure.b", 500
            }
            , {
                "wall.window.bars.metal", 500
            }
            , {
                "shutter.metal.embrasure.a", 500
            }
            , {
                "wall.window.bars.toptier", 500
            }
            , {
                "wall.window.glass.reinforced", 350
            }
            , {
                "wall.window.bars.wood", 250
            }
        }
        ;
        private static readonly Dictionary<string, int> FrameDefaults = new Dictionary<string, int> {
            {
                "wall.frame.fence", 100
            }
            , {
                "floor.grill", 250
            }
            , {
                "floor.triangle.grill", 250
            }
            , {
                "wall.frame.shopfront.metal", 750
            }
            , {
                "wall.frame.netting", 100
            }
            , {
                "wall.frame.cell", 300
            }
            , {
                "watchtower.wood", 200
            }
            , {
                "ladder.wooden.wall", 100
            }
        }
        ;
        private static readonly KeyValuePair<string, string>[] Tabs = {
            new KeyValuePair<string, string>("Building", "Building Block"), new KeyValuePair<string, string>("Door", "Door"), new KeyValuePair<string, string>("External", "External Wall"), new KeyValuePair<string, string>("Barricade", "Barricade"), new KeyValuePair<string, string>("Window", "Window"), new KeyValuePair<string, string>("Frame", "Frame")
        }
        ;
        private static readonly string[] TierNames = {
            "Default", "VIP", "VVIP", "RaidableBase"
        }
        ;
        private static readonly Dictionary<string, string> DisplayNames = new Dictionary<string, string> {
            {
                "foundation", "Foundation"
            }
            , {
                "foundation.triangle", "Triangle Foundation"
            }
            , {
                "wall", "Wall"
            }
            , {
                "wall.frame", "Wall Frame"
            }
            , {
                "wall.window", "Window Wall"
            }
            , {
                "wall.doorway", "Doorway"
            }
            , {
                "floor", "Floor"
            }
            , {
                "floor.frame", "Floor Frame"
            }
            , {
                "floor.triangle", "Triangle Floor"
            }
            , {
                "floor.triangle.frame", "Triangle Floor Frame"
            }
            , {
                "roof", "Roof"
            }
            , {
                "roof.triangle", "Triangle Roof"
            }
            , {
                "wall.half", "Half Wall"
            }
            , {
                "wall.low", "Low Wall"
            }
            , {
                "ramp", "Ramp"
            }
            , {
                "block.stair.ushape", "U Shaped Stairs"
            }
            , {
                "block.stair.lshape", "L Shaped Stairs"
            }
            , {
                "foundation.steps", "Foundation Steps"
            }
            , {
                "block.stair.spiral.triangle", "Triangle Spiral Stairs"
            }
            , {
                "block.stair.spiral", "Spiral Stairs"
            }
            , {
                "door.hinged.wood", "Wooden Door"
            }
            , {
                "door.hinged.metal", "Sheet Metal Door"
            }
            , {
                "door.hinged.toptier", "Armored Door"
            }
            , {
                "door.double.hinged.wood", "Wood Double Door"
            }
            , {
                "door.double.hinged.metal", "Sheet Metal Double Door"
            }
            , {
                "door.double.hinged.toptier", "Armored Double Door"
            }
            , {
                "wall.frame.garagedoor", "Garage Door"
            }
            , {
                "door.hinged.industrial.d", "Industrial Door"
            }
            , {
                "gates.external.high.wood", "High External Wooden Gate"
            }
            , {
                "gates.external.high.stone", "High External Stone Gate"
            }
            , {
                "shutter.wood.a", "Wooden Shutters"
            }
            , {
                "floor.ladder.hatch", "Ladder Hatch"
            }
            , {
                "floor.ladder.hatch.toptier", "Armored Ladder Hatch"
            }
            , {
                "wall.frame.cell.gate", "Prison Cell Gate"
            }
            , {
                "floor.triangle.ladder.hatch", "Triangle Ladder Hatch"
            }
            , {
                "floor.triangle.ladder.hatch.toptier", "Armored Triangle Ladder Hatch"
            }
            , {
                "wall.frame.fence.gate", "Chainlink Fence Gate"
            }
            , {
                "wall.frame.shopfront", "Shop Front"
            }
            , {
                "wall.external.high.wood", "High External Wooden Wall"
            }
            , {
                "wall.external.high.stone", "High External Stone Wall"
            }
            , {
                "wall.external.high.ice", "High External Ice Wall"
            }
            , {
                "icewall", "Ice Wall"
            }
            , {
                "barricade.woodwire", "Barbed Wooden Barricade"
            }
            , {
                "barricade.concrete", "Concrete Barricade"
            }
            , {
                "barricade.metal", "Metal Barricade"
            }
            , {
                "barricade.sandbags", "Sandbag Barricade"
            }
            , {
                "barricade.stone", "Stone Barricade"
            }
            , {
                "barricade.wood", "Wooden Barricade"
            }
            , {
                "barricade.cover.wood", "Wood Barricade Cover"
            }
            , {
                "shutter.metal.embrasure.a", "Metal Horizontal Embrasure"
            }
            , {
                "shutter.metal.embrasure.b", "Metal Vertical Embrasure"
            }
            , {
                "wall.window.bars.metal", "Metal Window Bars"
            }
            , {
                "wall.window.bars.toptier", "Reinforced Window Bars"
            }
            , {
                "wall.window.bars.wood", "Wooden Window Bars"
            }
            , {
                "wall.window.glass.reinforced", "Reinforced Glass Window"
            }
            , {
                "wall.frame.fence", "Chainlink Fence"
            }
            , {
                "floor.grill", "Floor Grill"
            }
            , {
                "floor.triangle.grill", "Triangle Floor Grill"
            }
            , {
                "wall.frame.shopfront.metal", "Metal Shop Front"
            }
            , {
                "wall.frame.netting", "Netting"
            }
            , {
                "wall.frame.cell", "Prison Cell Wall"
            }
            , {
                "watchtower.wood", "Watch Tower"
            }
            , {
                "ladder.wooden.wall", "Wooden Ladder"
            }
        }
        ;
        private static readonly Dictionary<string, string> BlockImageSlugs = new Dictionary<string, string> {
            {
                "foundation", "foundation0"
            }
            , {
                "foundation.triangle", "tr-foundation0"
            }
            , {
                "wall", "wall0"
            }
            , {
                "wall.frame", "wall-frame0"
            }
            , {
                "wall.window", "window0"
            }
            , {
                "wall.doorway", "doorway0"
            }
            , {
                "floor", "floor0"
            }
            , {
                "floor.frame", "floor-frame0"
            }
            , {
                "floor.triangle", "tr-floor0"
            }
            , {
                "floor.triangle.frame", "triangle-frame1"
            }
            , {
                "roof", "roof0"
            }
            , {
                "roof.triangle", "tr-roof0"
            }
            , {
                "wall.half", "half-wall0"
            }
            , {
                "wall.low", "low-wall0"
            }
            , {
                "ramp", "ramp1"
            }
            , {
                "block.stair.ushape", "u-stair0"
            }
            , {
                "block.stair.lshape", "l-stair0"
            }
            , {
                "foundation.steps", "stairs0"
            }
            , {
                "block.stair.spiral.triangle", "triangle-spiral1"
            }
            , {
                "block.stair.spiral", "block-spiral1"
            }
        }
        ;
        private static List<string> DefaultPrefabOrder(string category) {
            switch (category) {
                case "Building" : return BuildingBlocks.ToList();
                case "Door" : return DoorDefaults.Keys.ToList();
                case "External" : return ExternalDefaults.Keys.ToList();
                case "Barricade" : return BarricadeDefaults.Keys.ToList();
                case "Window" : return WindowDefaults.Keys.ToList();
                case "Frame" : return FrameDefaults.Keys.ToList();
                default : return null;
            }
        }
        private List<string> CategoryPrefabs(string category) {
            List<string> prefabs;
            return categoryPrefabs.TryGetValue(category, out prefabs) ? prefabs : null;
        }
        private PermissionSetting<Dictionary<string, int>> CategorySetting(string category) {
            switch (category) {
                case "Door" : return config.DoorHealth;
                case "External" : return config.ExternalWallHealth;
                case "Barricade" : return config.BarricadeHealth;
                case "Window" : return config.WindowHealth;
                case "Frame" : return config.FrameHealth;
                default : return null;
            }
        }
        private static Dictionary<string, int> Tier(PermissionSetting<Dictionary<string, int>> setting, int tier) {
            if (setting == null) return null;
            switch (tier) {
                case 0 : return setting.DefaultSettings;
                case 1 : return setting.VIPSettings;
                case 2 : return setting.VVIPSettings;
                case 3 : return setting.RaidableBasesSettings;
                default : return null;
            }
        }
#endregion
#region Configuration
        public class ConfigData {
            [JsonProperty(PropertyName = "Building Block Health")]
            public Dictionary<string, float> BuildingBlockHealth;
            [JsonProperty(PropertyName = "Door Health")]
            public PermissionSetting<Dictionary<string, int>> DoorHealth;
            [JsonProperty(PropertyName = "External Wall Health")]
            public PermissionSetting<Dictionary<string, int>> ExternalWallHealth;
            [JsonProperty(PropertyName = "Barricade Health")]
            public PermissionSetting<Dictionary<string, int>> BarricadeHealth;
            [JsonProperty(PropertyName = "Window Health")]
            public PermissionSetting<Dictionary<string, int>> WindowHealth;
            [JsonProperty(PropertyName = "Frame Health")]
            public PermissionSetting<Dictionary<string, int>> FrameHealth;
            [JsonProperty(PropertyName = "Image Settings")]
            public ImageSettings Images;
            [JsonProperty(PropertyName = "Version")]
            public string Version;
        }
        public class PermissionSetting<T> {
            [JsonProperty(PropertyName = "Default settings")]
            public T DefaultSettings;
            [JsonProperty(PropertyName = "VIP settings")]
            public T VIPSettings;
            [JsonProperty(PropertyName = "VVIP settings")]
            public T VVIPSettings;
            [JsonProperty(PropertyName = "Raidable Bases settings")]
            public T RaidableBasesSettings;
        }
        public class ImageSettings {
            [JsonProperty(PropertyName = "Item icon URL format ({0} = item shortname)")]
            public string ItemUrlFormat;
            [JsonProperty(PropertyName = "Building block screenshot URL format ({0} = screenshot slug)")]
            public string BlockUrlFormat;
            [JsonProperty(PropertyName = "Overrides (prefab name -> full URL, or just the item shortname)")]
            public Dictionary<string, string> Overrides;
            [JsonProperty(PropertyName = "Re-download images already cached on disk")]
            public bool ForceRedownload;
            [JsonProperty(PropertyName = "Seconds to wait between downloads")]
            public float DownloadDelay;
            [JsonProperty(PropertyName = "Download timeout (seconds)")]
            public int Timeout;
        }
        private ConfigData GetDefaultConfig() {
            return new ConfigData {
                BuildingBlockHealth = BuildingBlocks.ToDictionary(name => name, name => 100f), DoorHealth = NewPermissionSetting(DoorDefaults), ExternalWallHealth = NewPermissionSetting(ExternalDefaults), BarricadeHealth = NewPermissionSetting(BarricadeDefaults), WindowHealth = NewPermissionSetting(WindowDefaults), FrameHealth = NewPermissionSetting(FrameDefaults), Images = GetDefaultImageSettings(), Version = ConfigVersion
            }
            ;
        }
        private static ImageSettings GetDefaultImageSettings() {
            return new ImageSettings {
                ItemUrlFormat = "https://wiki.rustclash.com/img/items180/{0}.png", BlockUrlFormat = "https://wiki.rustclash.com/img/screenshots/{0}.png", Overrides = new Dictionary<string, string> {
                    {
                        "wall.external.high.wood", "wall.external.high"
                    }
                    , {
                        "icewall", "wall.ice.wall"
                    }
                    , {
                        "barricade.cover.wood", "barricade.wood.cover"
                    }
                    , {
                        "door.hinged.industrial.d", "https://wiki.rustclash.com/img/skins/324/10189.png"
                    }
                }
                , ForceRedownload = false, DownloadDelay = 0.15f, Timeout = 20
            }
            ;
        }
        private static PermissionSetting<Dictionary<string, int>> NewPermissionSetting(Dictionary<string, int> defaults) {
            return new PermissionSetting<Dictionary<string, int>> {
                DefaultSettings = new Dictionary<string, int>(defaults), VIPSettings = new Dictionary<string, int>(defaults), VVIPSettings = new Dictionary<string, int>(defaults), RaidableBasesSettings = new Dictionary<string, int>(defaults)
            }
            ;
        }
        protected override void LoadConfig() {
            base.LoadConfig();
            var defaults = GetDefaultConfig();
            try {
                config = Config.ReadObject<ConfigData>();
            }
            catch (Exception ex) {
                PrintError("Could not read the configuration file: {0}", ex.Message);
                config = null;
            }
            if (config == null) {
                PrintWarning("Creating a new configuration file");
                config = defaults;
            }
            else {
                if (config.Version != ConfigVersion) {
                    PrintWarning("Configuration file is version {0}, updating it to {1}...", string.IsNullOrEmpty(config.Version) ? "unknown" : config.Version, ConfigVersion);
                }
                MergeConfig(config, defaults);
            }
            SaveConfig();
        }
        protected override void LoadDefaultConfig() {
            config = GetDefaultConfig();
        }
        protected override void SaveConfig() {
            Config.WriteObject(config);
        }
        private static void MergeConfig(ConfigData current, ConfigData defaults) {
            current.BuildingBlockHealth = Merge(current.BuildingBlockHealth, defaults.BuildingBlockHealth);
            current.DoorHealth = Merge(current.DoorHealth, defaults.DoorHealth);
            current.ExternalWallHealth = Merge(current.ExternalWallHealth, defaults.ExternalWallHealth);
            current.BarricadeHealth = Merge(current.BarricadeHealth, defaults.BarricadeHealth);
            current.WindowHealth = Merge(current.WindowHealth, defaults.WindowHealth);
            current.FrameHealth = Merge(current.FrameHealth, defaults.FrameHealth);
            current.Images = MergeImages(current.Images, defaults.Images);
            current.Version = defaults.Version;
        }
        private static ImageSettings MergeImages(ImageSettings current, ImageSettings defaults) {
            if (current == null) return defaults;
            if (string.IsNullOrEmpty(current.ItemUrlFormat)) current.ItemUrlFormat = defaults.ItemUrlFormat;
            if (string.IsNullOrEmpty(current.BlockUrlFormat)) current.BlockUrlFormat = defaults.BlockUrlFormat;
            if (current.Overrides == null) current.Overrides = defaults.Overrides;
            if (current.DownloadDelay <= 0f) current.DownloadDelay = defaults.DownloadDelay;
            if (current.Timeout <= 0) current.Timeout = defaults.Timeout;
            return current;
        }
        private static Dictionary<string, TValue> Merge<TValue>(Dictionary<string, TValue> current, Dictionary<string, TValue> defaults) {
            var merged = new Dictionary<string, TValue>(defaults);
            if (current == null) return merged;
            foreach (var entry in current) {
                merged[entry.Key] = entry.Value;
            }
            return merged;
        }
        private static PermissionSetting<Dictionary<string, int>> Merge(PermissionSetting<Dictionary<string, int>> current, PermissionSetting<Dictionary<string, int>> defaults) {
            if (current == null) return defaults;
            current.DefaultSettings = Merge(current.DefaultSettings, defaults.DefaultSettings);
            current.VIPSettings = Merge(current.VIPSettings, defaults.VIPSettings);
            current.VVIPSettings = Merge(current.VVIPSettings, defaults.VVIPSettings);
            current.RaidableBasesSettings = Merge(current.RaidableBasesSettings, defaults.RaidableBasesSettings);
            return current;
        }
#endregion
#region Hooks
        private void Init() {
            permission.RegisterPermission(PermDefault, this);
            permission.RegisterPermission(PermVIP, this);
            permission.RegisterPermission(PermVVIP, this);
            permission.RegisterPermission(PermAdmin, this);
            BuildPrefabIndex();
        }
        private void OnServerInitialized() {
            ApplyAll(true);
            LoadImages(config.Images.ForceRedownload);
        }
        private void Unload() {
            StopImageRoutine();
            ApplyAll(false);
            foreach (var player in BasePlayer.activePlayerList) {
                CuiHelper.DestroyUi(player, PanelName);
            }
        }
        private void OnPlayerDisconnected(BasePlayer player) {
            if (player == null) return;
            playerPage.Remove(player.userID);
            inputValues.Remove(player.userID);
        }
        private void OnEntityBuilt(Planner plan, GameObject go) {
            var block = go == null ? null : go.ToBaseEntity() as BuildingBlock;
            if (block == null || !block.IsValid()) return;
            ApplySingleBlock(block, true);
        }
        private void OnEntitySpawned(BaseNetworkable networkable) {
            var entity = networkable as BaseCombatEntity;
            if (entity == null || entity is BuildingBlock) return;
            string category;
            if (!prefabCategory.TryGetValue(entity.ShortPrefabName, out category)) return;
            ApplyEntityHealth(entity, category);
        }
        private void OnEntityKill(BaseNetworkable networkable) {
            var entity = networkable as BaseCombatEntity;
            if (entity == null || entity.net == null) return;
            originalMaxHealth.Remove(entity.net.ID.Value);
        }
#endregion
#region Health application
        private void BuildPrefabIndex() {
            prefabCategory.Clear();
            categoryPrefabs.Clear();
            foreach (var tab in Tabs) {
                var prefabs = DefaultPrefabOrder(tab.Key);
                categoryPrefabs[tab.Key] = prefabs;
                if (tab.Key == "Building") continue;
                foreach (var prefab in prefabs) {
                    prefabCategory[prefab] = tab.Key;
                }
            }
        }
        private void ApplyAll(bool load) {
            multiplierApplied.Clear();
            statChanged = 0;
            statNoPermission = 0;
            statNoTarget = 0;
            statBlocksScaled = 0;
            statBlocksBroken = 0;
            statRestored = 0;
            var blocks = new List<BuildingBlock>();
            foreach (var networkable in BaseNetworkable.serverEntities) {
                var entity = networkable as BaseCombatEntity;
                if (entity == null || entity.IsDestroyed) continue;
                var block = entity as BuildingBlock;
                if (block != null) {
                    blocks.Add(block);
                    continue;
                }
                if (!load) {
                    RestoreEntityHealth(entity);
                    continue;
                }
                string category;
                if (!prefabCategory.TryGetValue(entity.ShortPrefabName, out category)) continue;
                ApplyEntityHealth(entity, category);
            }
            ApplyBlocks(blocks, load);
            if (!load) {
                Puts("Restored {0} structure(s) to their original health.", statRestored);
                return;
            }
            Puts("Health applied to {0} structure(s) and {1} building block(s).", statChanged, statBlocksScaled);
            if (statNoPermission > 0) {
                PrintWarning("{0} structure(s) were left at vanilla health because their owner holds none of {1} / {2} / {3}. Grant one, e.g.: oxide.grant group default {1}", statNoPermission, PermDefault, PermVIP, PermVVIP);
            }
            if (statBlocksBroken > 0) {
                PrintWarning("{0} building block(s) have no usable grade definition and were left untouched.", statBlocksBroken);
            }
        }
        private void ApplyBlocks(List<BuildingBlock> blocks, bool load) {
            var scales = new Dictionary<string, float>();
            foreach (var block in blocks) {
                if (!IsConfiguredBlock(block)) continue;
                string prefab = block.ShortPrefabName;
                if (scales.ContainsKey(prefab)) continue;
                scales[prefab] = ApplyBlockMultiplier(block, load);
            }
            foreach (var block in blocks) {
                if (!IsConfiguredBlock(block)) continue;
                float scale;
                if (!scales.TryGetValue(block.ShortPrefabName, out scale)) continue;
                ScaleBlockHealth(block, scale);
            }
        }
        private void ScaleBlockHealth(BuildingBlock block, float scale) {
            try {
                if (Math.Abs(scale - 1f) > 0.0001f) block.health *= scale;
                block.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
                statBlocksScaled++;
            }
            catch (Exception) {
                statBlocksBroken++;
            }
        }
        private float ApplyBlockMultiplier(BuildingBlock block, bool load) {
            if (!IsConfiguredBlock(block)) return 1f;
            string prefab = block.ShortPrefabName;
            if (load && !multiplierApplied.Add(prefab)) return 1f;
            if (!load) multiplierApplied.Remove(prefab);
            var definition = block.blockDefinition;
            float previous = definition.healthMultiplier;
            float current = load ? config.BuildingBlockHealth[prefab] / 100f : 1f;
            definition.healthMultiplier = current;
            return previous > 0f ? current / previous : 1f;
        }
        private void ApplySingleBlock(BuildingBlock block, bool load) {
            if (!IsConfiguredBlock(block)) return;
            ScaleBlockHealth(block, ApplyBlockMultiplier(block, load));
        }
        private bool IsConfiguredBlock(BuildingBlock block) {
            if (block == null || block.IsDestroyed || block.net == null) return false;
            if (block.blockDefinition == null) return false;
            float percent;
            return config.BuildingBlockHealth.TryGetValue(block.ShortPrefabName, out percent) && percent > 0f;
        }
        private void ApplyEntityHealth(BaseCombatEntity entity, string category) {
            if (entity == null || entity.IsDestroyed || entity.net == null) return;
            var setting = CategorySetting(category);
            if (setting == null) return;
            Dictionary<string, int> tier;
            if (entity.OwnerID != 0) {
                tier = TierForOwner(setting, entity.OwnerID);
                if (tier == null) {
                    statNoPermission++;
                    return;
                }
            }
            else {
                if (!IsRaidableTerritory(entity)) return;
                tier = setting.RaidableBasesSettings;
            }
            int target;
            if (!tier.TryGetValue(entity.ShortPrefabName, out target) || target <= 0) {
                statNoTarget++;
                return;
            }
            ulong id = entity.net.ID.Value;
            float before = entity._maxHealth;
            if (!SetMaxHealth(entity, target)) return;
            statChanged++;
            if (!originalMaxHealth.ContainsKey(id)) originalMaxHealth[id] = before;
        }
        private void RestoreEntityHealth(BaseCombatEntity entity) {
            if (entity == null || entity.net == null) return;
            ulong id = entity.net.ID.Value;
            float original;
            if (!originalMaxHealth.TryGetValue(id, out original)) return;
            SetMaxHealth(entity, original);
            originalMaxHealth.Remove(id);
            statRestored++;
        }
        private Dictionary<string, int> TierForOwner(PermissionSetting<Dictionary<string, int>> setting, ulong ownerId) {
            string owner = ownerId.ToString();
            if (permission.UserHasPermission(owner, PermVVIP)) return setting.VVIPSettings;
            if (permission.UserHasPermission(owner, PermVIP)) return setting.VIPSettings;
            if (permission.UserHasPermission(owner, PermDefault)) return setting.DefaultSettings;
            return null;
        }
        private static bool SetMaxHealth(BaseCombatEntity entity, float max) {
            if (max <= 0f) return false;
            float previous = entity._maxHealth;
            if (Math.Abs(previous - max) < 0.01f) {
                if (entity.health <= max + 0.01f) return false;
                entity.health = max;
                entity.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
                return true;
            }
            float ratio = previous > 0f ? Mathf.Clamp01(entity.health / previous) : 1f;
            entity._maxHealth = max;
            entity.health = ratio * max;
            entity.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
            return true;
        }
        private bool IsRaidableTerritory(BaseEntity entity) {
            if (RaidableBases == null || entity == null || entity.transform == null) return false;
            var result = RaidableBases.Call("EventTerritory", entity.transform.position);
            return result is bool && (bool)result;
        }
#endregion
#region Images
        private string ImageFolder {
            get {
                return Path.Combine(Interface.Oxide.DataDirectory, "BuildingHealthManager", "images");
            }
        }
        private string ImageUrl(string prefab) {
            string over;
            if (config.Images.Overrides.TryGetValue(prefab, out over) && !string.IsNullOrEmpty(over)) {
                return over.StartsWith("http") ? over : string.Format(config.Images.ItemUrlFormat, over);
            }
            string slug;
            if (BlockImageSlugs.TryGetValue(prefab, out slug)) {
                return string.Format(config.Images.BlockUrlFormat, slug);
            }
            return string.Format(config.Images.ItemUrlFormat, prefab);
        }
        private void LoadImages(bool force) {
            StopImageRoutine();
            if (ServerMgr.Instance == null) return;
            imageRoutine = ServerMgr.Instance.StartCoroutine(ImageRoutine(force));
        }
        private void StopImageRoutine() {
            if (imageRoutine == null || ServerMgr.Instance == null) return;
            ServerMgr.Instance.StopCoroutine(imageRoutine);
            imageRoutine = null;
        }
        private IEnumerator ImageRoutine(bool force) {
            string folder = ImageFolder;
            if (!EnsureFolder(folder)) {
                imageRoutine = null;
                yield break;
            }
            var wanted = new List<string>();
            foreach (var tab in Tabs) {
                foreach (var prefab in CategoryPrefabs(tab.Key)) {
                    if (!wanted.Contains(prefab)) wanted.Add(prefab);
                }
            }
            int cached = 0, downloaded = 0, failed = 0;
            var wait = new WaitForSeconds(config.Images.DownloadDelay);
            foreach (var prefab in wanted) {
                string path = Path.Combine(folder, prefab + ".png");
                byte[] bytes = force ? null : ReadFile(path);
                if (bytes == null) {
                    string url = ImageUrl(prefab);
                    var request = UnityWebRequest.Get(url);
                    request.timeout = config.Images.Timeout;
                    yield return request.SendWebRequest();
                    bytes = ResponseBytes(request, prefab, url);
                    request.Dispose();
                    if (bytes == null) {
                        failed++;
                        yield return wait;
                        continue;
                    }
                    WriteFile(path, bytes);
                    downloaded++;
                    yield return wait;
                }
                else {
                    cached++;
                }
                StoreImage(prefab, bytes);
            }
            imageRoutine = null;
            Puts("Images ready: {0} from cache, {1} downloaded, {2} failed (cache: {3})", cached, downloaded, failed, folder);
            if (failed > 0) {
                PrintWarning("Some images could not be downloaded. Check 'Image Settings' in the config file if the wiki changed its URLs.");
            }
        }
        private byte[] ResponseBytes(UnityWebRequest request, string prefab, string url) {
            if (!string.IsNullOrEmpty(request.error)) {
                PrintWarning("Image download failed [{0}]: {1} ({2})", prefab, request.error, url);
                return null;
            }
            var data = request.downloadHandler == null ? null : request.downloadHandler.data;
            if (data == null || data.Length == 0) {
                PrintWarning("Image download was empty [{0}]: {1}", prefab, url);
                return null;
            }
            return data;
        }
        private void StoreImage(string prefab, byte[] bytes) {
            if (CommunityEntity.ServerInstance == null || CommunityEntity.ServerInstance.net == null) return;
            imageIds[prefab] = FileStorage.server.Store(bytes, FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID).ToString();
        }
        private bool EnsureFolder(string folder) {
            try {
                if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
                return true;
            }
            catch (Exception ex) {
                PrintError("Could not create the image folder '{0}': {1}", folder, ex.Message);
                return false;
            }
        }
        private byte[] ReadFile(string path) {
            try {
                return File.Exists(path) ? File.ReadAllBytes(path) : null;
            }
            catch (Exception ex) {
                PrintWarning("Could not read cached image '{0}': {1}", path, ex.Message);
                return null;
            }
        }
        private void WriteFile(string path, byte[] bytes) {
            try {
                File.WriteAllBytes(path, bytes);
            }
            catch (Exception ex) {
                PrintWarning("Could not cache image '{0}': {1}", path, ex.Message);
            }
        }
#endregion
#region CUI
        private static string Anchor(double x, double y) {
            return x.ToString("0.####", CultureInfo.InvariantCulture) + " " + y.ToString("0.####", CultureInfo.InvariantCulture);
        }
        private void OpenPanel(BasePlayer player, string activeCategory) {
            var container = new CuiElementContainer();
            var panel = container.Add(new CuiPanel {
                Image = {
                    Color = "0.07 0.07 0.08 0.99"
                }
                , RectTransform = {
                    AnchorMin = "0.13 0.11", AnchorMax = "0.87 0.89"
                }
                , CursorEnabled = true
            }
            , "Overlay", PanelName);
            container.Add(new CuiLabel {
                Text = {
                    Text = "Building Health Manager", Align = TextAnchor.MiddleLeft, FontSize = 22, Color = "0.92 0.92 0.92 1"
                }
                , RectTransform = {
                    AnchorMin = "0.02 0.9", AnchorMax = "0.7 0.98"
                }
            }
            , panel);
            container.Add(new CuiPanel {
                Image = {
                    Color = "0.35 0.55 0.35 0.9"
                }
                , RectTransform = {
                    AnchorMin = "0.02 0.885", AnchorMax = "0.98 0.888"
                }
            }
            , panel);
            container.Add(new CuiButton {
                Button = {
                    Command = "bhc", Color = "0.62 0.22 0.22 0.95"
                }
                , RectTransform = {
                    AnchorMin = "0.94 0.905", AnchorMax = "0.98 0.972"
                }
                , Text = {
                    Text = "X", FontSize = 18, Align = TextAnchor.MiddleCenter, Color = "0.95 0.9 0.9 1"
                }
            }
            , panel);
            container.Add(new CuiLabel {
                Text = {
                    Text = "Save, then reload the plugin to apply", Align = TextAnchor.MiddleLeft, FontSize = 11, Color = "0.6 0.6 0.62 1"
                }
                , RectTransform = {
                    AnchorMin = "0.02 0.83", AnchorMax = "0.22 0.868"
                }
            }
            , panel);
            for (int i = 0; i < Tabs.Length; i++) {
                bool active = Tabs[i].Key == activeCategory;
                container.Add(new CuiButton {
                    Button = {
                        Command = string.Format("bh.showdetails {0} 0", Tabs[i].Key), Color = active ? "0.35 0.55 0.35 0.95" : "0.17 0.17 0.19 0.9"
                    }
                    , RectTransform = {
                        AnchorMin = Anchor(MenuLeft, MenuBottom - i * MenuPitch), AnchorMax = Anchor(MenuRight, MenuBottom + MenuHeight - i * MenuPitch)
                    }
                    , Text = {
                        Text = Tabs[i].Value, FontSize = 14, Align = TextAnchor.MiddleCenter, Color = active ? "1 1 1 1" : "0.75 0.75 0.78 1"
                    }
                }
                , panel);
            }
            CuiHelper.AddUi(player, container);
        }
        private void ShowCategory(BasePlayer player, string category, int page) {
            var prefabs = CategoryPrefabs(category);
            if (prefabs == null) return;
            if (page < 0) page = 0;
            int lastPage = Math.Max(0, (prefabs.Count - 1) / PerPage);
            if (page > lastPage) page = lastPage;
            var container = new CuiElementContainer();
            int startIndex = page * PerPage;
            for (int i = 0; i < PerPage && startIndex + i < prefabs.Count; i++) {
                AddTile(container, category, prefabs[startIndex + i], page, i);
            }
            container.Add(new CuiButton {
                Button = {
                    Command = page > 0 ? string.Format("bh.showdetails {0} {1}", category, page - 1) : string.Empty, Color = page > 0 ? "0.2 0.2 0.22 0.95" : "0.13 0.13 0.14 0.6"
                }
                , RectTransform = {
                    AnchorMin = "0.02 0.035", AnchorMax = "0.075 0.095"
                }
                , Text = {
                    Text = "<", FontSize = 16, Align = TextAnchor.MiddleCenter, Color = page > 0 ? "0.9 0.9 0.9 1" : "0.4 0.4 0.4 1"
                }
            }
            , PanelName);
            container.Add(new CuiLabel {
                Text = {
                    Text = string.Format("{0} / {1}", page + 1, lastPage + 1), Align = TextAnchor.MiddleCenter, FontSize = 12, Color = "0.7 0.7 0.72 1"
                }
                , RectTransform = {
                    AnchorMin = "0.075 0.035", AnchorMax = "0.145 0.095"
                }
            }
            , PanelName);
            container.Add(new CuiButton {
                Button = {
                    Command = page < lastPage ? string.Format("bh.showdetails {0} {1}", category, page + 1) : string.Empty, Color = page < lastPage ? "0.2 0.2 0.22 0.95" : "0.13 0.13 0.14 0.6"
                }
                , RectTransform = {
                    AnchorMin = "0.145 0.035", AnchorMax = "0.2 0.095"
                }
                , Text = {
                    Text = ">", FontSize = 16, Align = TextAnchor.MiddleCenter, Color = page < lastPage ? "0.9 0.9 0.9 1" : "0.4 0.4 0.4 1"
                }
            }
            , PanelName);
            CuiHelper.AddUi(player, container);
        }
        private void AddTile(CuiElementContainer container, string category, string prefab, int page, int slot) {
            int row = slot / Columns;
            int column = slot % Columns;
            double left = ContentLeft + column * ColPitch;
            double right = left + TileWidth;
            double top = RowTop - row * RowPitch;
            container.Add(new CuiPanel {
                Image = {
                    Color = "0.145 0.145 0.165 0.9"
                }
                , RectTransform = {
                    AnchorMin = Anchor(left, top - TileHeight), AnchorMax = Anchor(right, top)
                }
            }
            , PanelName);
            container.Add(new CuiLabel {
                Text = {
                    Text = DisplayName(prefab), Align = TextAnchor.MiddleCenter, FontSize = 11, Color = "0.95 0.95 0.95 1"
                }
                , RectTransform = {
                    AnchorMin = Anchor(left + 0.004, top - NameHeight), AnchorMax = Anchor(right - 0.004, top)
                }
            }
            , PanelName);
            string imageId;
            if (imageIds.TryGetValue(prefab, out imageId)) {
                double imageLeft = left + (TileWidth - ImageWidth) / 2.0;
                container.Add(new CuiElement {
                    Parent = PanelName, Components = {
                        new CuiRawImageComponent {
                            Png = imageId, Sprite = "assets/content/textures/generic/fulltransparent.tga"
                        }
                        , new CuiRectTransformComponent {
                            AnchorMin = Anchor(imageLeft, top - ImageTop - ImageHeight), AnchorMax = Anchor(imageLeft + ImageWidth, top - ImageTop)
                        }
                    }
                }
                );
            }
            AddTierInputs(container, category, prefab, page, slot, left, right, top);
            double saveLeft = left + (TileWidth - SaveWidth) / 2.0;
            container.Add(new CuiButton {
                Button = {
                    Command = stri
// ... [Code preview truncated due to length. Full source in attached file] ...

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