using Oxide.Core; using Oxide.Core.Libraries.Covalence; using Oxide.Game.Rust.Cui; using Oxide.Core.Plugins; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using Newtonsoft.Json; namespace Oxide.Plugins { [Info("Building Skins", "Marat", "2.0.9")] [Description("Automatic application of DLC skins for building blocks")] class BuildingSkins : RustPlugin { [PluginReference] private Plugin ImageLibrary; #region Field private const string InitialLayer = "UI_BuildingLayer"; private const string CupboardLayer = "UI_CupboardLayer"; private const string permissionUse = "buildingskins.use"; private const string permissionAll = "buildingskins.all"; private const string permissionBuild = "buildingskins.build"; private const string permissionTc = "buildingskins.tc"; private const string permissionAdmin = "buildingskins.admin"; private readonly Dictionary runningCoroutines = new(); private readonly Dictionary> gradesSkin = new(); private readonly Dictionary colors = new() { [1] = "0.38 0.56 0.74 1.0", [2] = "0.45 0.71 0.34 1.0", [3] = "0.57 0.29 0.83 1.0", [4] = "0.42 0.17 0.11 1.0", [5] = "0.82 0.46 0.13 1.0", [6] = "0.87 0.87 0.87 1.0", [7] = "0.20 0.20 0.18 1.0", [8] = "0.40 0.33 0.27 1.0", [9] = "0.20 0.22 0.34 1.0", [10] = "0.24 0.35 0.20 1.0", [11] = "0.73 0.30 0.18 1.0", [12] = "0.78 0.53 0.39 1.0", [13] = "0.84 0.66 0.22 1.0", [14] = "0.34 0.33 0.31 1.0", [15] = "0.21 0.34 0.37 1.0", [16] = "0.66 0.61 0.56 1.0" }; #endregion #region Oxide Hooks private void OnServerInitialized() { if (ImageLibrary == null) { PrintError("[ImageLibrary] not found! Plugin is disabled!"); Interface.Oxide.UnloadPlugin(Title); return; } LoadData(); AddCovalenceCommand(config.Commands, nameof(CmdChangeSkin)); permission.RegisterPermission(permissionUse, this); permission.RegisterPermission(permissionAll, this); permission.RegisterPermission(permissionBuild, this); permission.RegisterPermission(permissionTc, this); permission.RegisterPermission(permissionAdmin, this); foreach (var list in config.BuildingImages) { var skinId = list.Value.Select(x => x.SkinId).ToList(); gradesSkin.Add((BuildingGrade.Enum)(list.Key + 1), skinId); foreach (var info in list.Value.Where(x => !string.IsNullOrEmpty(x.Url))) { ImageLibrary.Call("AddImage", info.Url, info.Title); if (config.SeparatePermissions && info.SkinId != 0 && !string.IsNullOrEmpty(info.Title)) { if (!permission.PermissionExists(info.Title)) { permission.RegisterPermission($"buildingskins.{info.Title}".ToLower(), this); } } } } for (int i = 0; i < BasePlayer.activePlayerList.Count; i++) { OnPlayerConnected(BasePlayer.activePlayerList[i]); } } private void Unload() { for (int i = 0; i < BasePlayer.activePlayerList.Count; i++) { var player = BasePlayer.activePlayerList[i]; CuiHelper.DestroyUi(player, InitialLayer); CuiHelper.DestroyUi(player, CupboardLayer); StopCoroutine(player); } SaveData(); runningCoroutines.Clear(); config = null; } private void OnServerSave() => SaveData(); private void OnPlayerConnected(BasePlayer player) { if (player == null || !player.IsConnected) return; if (player.IsReceivingSnapshot) { timer.Once(1f, () => OnPlayerConnected(player)); return; } StartCoroutine(player, PreloadImages(player)); if (!storedData.PlayerData.TryGetValue(player.userID.Get(), out var data)) { data = new Data { ChangeHammer = true, NeedsRepair = true, EnableAnimation = true, RandomColor = false, Color = 9 }; storedData.PlayerData[player.userID] = data; } player.SetInfo("client.SelectedShippingContainerBlockColour", data.RandomColor ? data.Color.ToString() : player.GetInfoString("client.SelectedShippingContainerBlockColour", "0")); } private void OnPlayerDisconnected(BasePlayer player) => StopCoroutine(player); private void OnPlayerRespawned(BasePlayer player) { if (player == null || !player.IsConnected) return; var data = storedData.PlayerData.TryGetValue(player.userID, out var playerData) ? playerData : null; if(data == null) player.SetInfo("client.SelectedShippingContainerBlockColour", player.GetInfoString("client.SelectedShippingContainerBlockColour", "0")); else player.SetInfo("client.SelectedShippingContainerBlockColour", data.RandomColor ? data.Color.ToString() : player.GetInfoString("client.SelectedShippingContainerBlockColour", "0")); } private void OnHammerHit(BasePlayer player, HitInfo info) { if (player == null || info == null || info.HitEntity == null) return; if (!permission.UserHasPermission(player.UserIDString, permissionUse)) return; var block = info.HitEntity as BuildingBlock; if (block == null || !gradesSkin.TryGetValue(block.grade, out var skinId) || skinId == null) return; var skinID = GetPlayerSkinID(player, block.grade); var playerData = storedData.PlayerData[player.userID]; if (block.SecondsSinceAttacked <= 30f) return; if (block.skinID != 0 && block.grade.ToString() == "Metal" && !playerData.RandomColor) block.SetCustomColour((uint)player.GetInfoInt("client.SelectedShippingContainerBlockColour", 0)); if (block.skinID == skinID) return; if ((config.BuildingBlocked && !player.CanBuild() || block.OwnerID != player.userID) && !permission.UserHasPermission(player.UserIDString, permissionAdmin)) return; if (!playerData.ChangeHammer || block.health != block.MaxHealth() && !playerData.NeedsRepair) return; block.skinID = skinID; block.ChangeGradeAndSkin(block.grade, skinID, true, true); if (playerData.EnableAnimation) block.ClientRPC(null, "DoUpgradeEffect", (int)block.grade, skinID); } private object OnStructureUpgrade(BuildingBlock block, BasePlayer player, BuildingGrade.Enum grade, ulong skin) { if (player == null || block == null) return null; if (!permission.UserHasPermission(player.UserIDString, permissionUse)) return null; if (skin != 0 && player.blueprints.steamInventory.HasItem((int)skin)) return null; if (!gradesSkin.TryGetValue(grade, out var skinId) || skinId == null) return null; var skinID = GetPlayerSkinID(player, grade); var playerData = storedData.PlayerData[player.userID]; if (block.SecondsSinceAttacked <= 30f) { block.OnRepairFailed(player, string.Format("Unable to repair: Recently damaged. Repairable in: {0:N0}s.", 30f - block.SecondsSinceAttacked)); return false; } if (block.skinID != 0 && grade.ToString() == "Metal" && !playerData.RandomColor) block.SetCustomColour((uint)player.GetInfoInt("client.SelectedShippingContainerBlockColour", 0)); if (block.skinID == skinID && block.grade == grade) return false; NextTick(() => { if (block == null || block.IsDestroyed) return; block.skinID = skinID; block.ChangeGradeAndSkin(block.grade, skinID, true, true); if (playerData.EnableAnimation) block.ClientRPC(null, "DoUpgradeEffect", (int)block.grade, skinID); ///for plugin BuildingGrades if (block.skinID != 0 && grade.ToString() == "Metal" && !playerData.RandomColor) block.SetCustomColour((uint)player.GetInfoInt("client.SelectedShippingContainerBlockColour", 0)); }); return null; } ///for plugin BuildingGrades private void OnStructureGradeUpdated(BuildingBlock block, BasePlayer player, BuildingGrade.Enum oldGrade, BuildingGrade.Enum newGrade) { OnStructureUpgrade(block, player, newGrade, block.skinID); } private void OnLootEntity(BasePlayer player, BaseEntity entity) { if (player == null || entity == null) return; var cupboard = entity.gameObject.GetComponent(); if (cupboard == null || !permission.UserHasPermission(player.UserIDString, permissionTc)) return; if (config.ChangeSkinTC && cupboard.OwnerID != player.userID) return; TCupboardLayer(player); } private void OnLootEntityEnd(BasePlayer player, BaseCombatEntity entity) { if (player == null || entity == null) return; if (entity is BuildingPrivlidge) { CuiHelper.DestroyUi(player, CupboardLayer); } } #endregion #region Configuration private static PluginConfig config; private class PluginConfig { [JsonProperty("Building skin change commands")] public string[] Commands; [JsonProperty("Block building skin in building blocked")] public bool BuildingBlocked; [JsonProperty("Changing skin from tool cupboard only by owner")] public bool ChangeSkinTC; [JsonProperty("Number of blocks updated per tick")] public int UpdatesPerTick; [JsonProperty("Use separate permissions for skins")] public bool SeparatePermissions; [JsonProperty("Image and description settings")] public Dictionary> BuildingImages; public Oxide.Core.VersionNumber Version; } protected override void LoadDefaultConfig() { config = new PluginConfig { Commands = new string[] { "bskin", "building.skin" }, BuildingBlocked = true, ChangeSkinTC = true, UpdatesPerTick = 5, SeparatePermissions = false, BuildingImages = new Dictionary> { [0] = new List { new("Wood", "https://i.ibb.co/yqsWpbp/wood.png", 0), new("Frontier", "https://i.ibb.co/b2bZFXj/frontier.png", 10232), new("Gingerbread", "https://i.ibb.co/Tw67yBM/gingerbread.png", 2) }, [1] = new List { new("Stone", "https://i.ibb.co/jw9FJFP/stone.png", 0), new("Adobe", "https://i.ibb.co/Ky1MBJ7/adobe.png", 10220), new("Brick", "https://i.ibb.co/vjqh3Hj/brick.png", 10223), new("Brutalist", "https://i.ibb.co/86bpvS2/brutalist.png", 10225) }, [2] = new List { new("Metal", "https://i.ibb.co/M9RPSZ2/metal.png", 0), new("Container", "https://i.ibb.co/YWzfwS4/container.png", 10221) }, [3] = new List { new("TopTier", "https://i.ibb.co/T0Nwfvp/toptire.png", 0) } }, Version = Version }; } private class BlockInfo { public string Title; public string Url; public ulong SkinId; public BlockInfo(string title, string url, ulong skinId) { Title = title; Url = url; SkinId = skinId; } } protected override void SaveConfig() => Config.WriteObject(config); protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) LoadDefaultConfig(); if (config.Version < Version) { PrintWarning("Config update detected! Updating config values..."); if (config.Version < new Core.VersionNumber(2, 0, 0)) { LoadDefaultConfig(); } if (config.Version < new Core.VersionNumber(2, 0, 3)) { if (config.BuildingImages[0][3].SkinId == 10225) { config.BuildingImages[0][3].SkinId = 3; } } if (config.Version < new Core.VersionNumber(2, 0, 4)) { config.ChangeSkinTC = true; } config.Version = Version; PrintWarning("Config update completed!"); } } catch { PrintWarning("The config file contains an error and has been replaced with the default config."); LoadDefaultConfig(); } SaveConfig(); } #endregion #region Commands [ConsoleCommand("UI_BuildingController")] private void CmdConsoleHandler(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null || !arg.HasArgs(1)) return; var playerData = storedData.PlayerData[player.userID]; SoundEffect(player, "assets/bundled/prefabs/fx/notice/loot.drag.grab.fx.prefab"); int index, skinIndex; switch (arg.Args[0].ToLower()) { case "open": { InitializeLayers(player, true); break; } case "build": { var blocks = player.GetBuildingPrivilege().GetBuilding()?.buildingBlocks.ToArray(); if (blocks == null) return; PrintToChat(player, GetMessage("Lang_UpdateBuilding", player)); StartCoroutine(player, UpgradeSkin(player, blocks)); TCupboardLayer(player); SoundEffect(player, "assets/prefabs/deployable/repair bench/effects/skinchange_spraypaint.prefab"); break; } case "change": { if (!int.TryParse(arg.Args[1], out index) || !int.TryParse(arg.Args[2], out skinIndex)) return; ImageLayers(player, index, skinIndex); break; } case "choose": { if (!int.TryParse(arg.Args[1], out index) || !int.TryParse(arg.Args[2], out skinIndex)) return; var grades = gradesSkin.ElementAt(index); playerData.GetType().GetField(grades.Key.ToString())?.SetValue(playerData, grades.Value[skinIndex]); ImageLayers(player, index, skinIndex); SoundEffect(player, "assets/prefabs/missions/effects/mission_objective_complete.prefab"); break; } case "settings": { SettingsLayer(player); break; } case "colors": { if (!int.TryParse(arg.Args[1], out index)) return; ColorLayer(player, index); break; } case "setcolor": { if (!int.TryParse(arg.Args[1], out index) || !uint.TryParse(arg.Args[2], out uint colorId)) return; player.SetInfo("client.SelectedShippingContainerBlockColour", colorId.ToString()); playerData.Color = colorId; ColorLayer(player, index); SoundEffect(player, "assets/prefabs/deployable/repair bench/effects/skinchange_spraypaint.prefab"); break; } case "randomcolor": { if (!int.TryParse(arg.Args[1], out index)) return; var randomColor = !playerData.RandomColor; playerData.RandomColor = randomColor; player.SetInfo("client.SelectedShippingContainerBlockColour", randomColor ? "0" : playerData.Color.ToString()); ColorLayer(player, index); break; } case "hammer": { var changeHammer = !playerData.ChangeHammer; playerData.ChangeHammer = changeHammer; SettingsLayer(player); break; } case "repair": { var needsRepair = !playerData.NeedsRepair; playerData.NeedsRepair = needsRepair; SettingsLayer(player); break; } case "animation": { var enableAnimation = !playerData.EnableAnimation; playerData.EnableAnimation = enableAnimation; SettingsLayer(player); break; } } } private void CmdChangeSkin(IPlayer ipPlayer, string command, string[] arg) { var player = ipPlayer?.Object as BasePlayer; if (player == null) return; if (!permission.UserHasPermission(player.UserIDString, permissionUse)) { PrintToChat(player, GetMessage("Lang_NoPermissions", player)); return; } if (arg.Length == 0) { InitializeLayers(player, true); return; } if (runningCoroutines.ContainsKey(player.userID)) { PrintToChat(player, GetMessage("Lang_UpdateProgress", player)); return; } if (arg.Length > 0 && config.BuildingBlocked && !player.CanBuild() && !permission.UserHasPermission(player.UserIDString, permissionAdmin)) { PrintToChat(player, GetMessage("Lang_BuildingBlocked", player)); return; } switch (arg[0].ToLower()) { case "build": { if (!permission.UserHasPermission(player.UserIDString, permissionBuild)) { PrintToChat(player, GetMessage("Lang_NoPermissions", player)); return; } var entity = GetLookEntity(player); if (entity == null) { PrintToChat(player, GetMessage("Lang_NotFoundBuilding", player)); return; } if (entity.OwnerID != player.userID && !permission.UserHasPermission(player.UserIDString, permissionAdmin)) { PrintToChat(player, GetMessage("Lang_NotOwnerBuilding", player)); return; } var blocks = entity.GetBuilding()?.buildingBlocks.ToArray(); if (blocks == null) return; PrintToChat(player, GetMessage("Lang_UpdateBuilding", player)); StartCoroutine(player, UpgradeSkin(player, blocks)); break; } case "all": { if (!permission.UserHasPermission(player.UserIDString, permissionAll)) { PrintToChat(player, GetMessage("Lang_NoPermissions", player)); return; } if (arg.Length > 1 && !ulong.TryParse(arg[1], out var owner)) { PrintToChat(player, GetMessage("Lang_NotFoundPlayer", player)); return; } var targetOwner = arg.Length > 1 ? ulong.Parse(arg[1]) : player.userID.Get(); if (!permission.UserHasPermission(player.UserIDString, permissionAdmin) && targetOwner != player.userID) { PrintToChat(player, GetMessage("Lang_NoPermissions", player)); return; } var blockOwner = BaseNetworkable.serverEntities.OfType().Where(x => x.OwnerID == targetOwner).ToArray(); if (blockOwner.Length == 0) { PrintToChat(player, GetMessage("Lang_NotFoundBlocks", player)); return; } PrintToChat(player, GetMessage(targetOwner != player.userID.Get() ? "Lang_UpdateAllTarget" : "Lang_UpdateAll", player)); StartCoroutine(player, UpgradeSkin(player, blockOwner)); break; } } } #endregion #region Methods private void StartCoroutine(BasePlayer player, IEnumerator routine) { if (runningCoroutines.ContainsKey(player.userID.Get())) return; var coroutine = ServerMgr.Instance?.StartCoroutine(routine); if (coroutine != null) runningCoroutines[player.userID.Get()] = coroutine; } private void StopCoroutine(BasePlayer player) { if (!runningCoroutines.ContainsKey(player.userID.Get())) return; var coroutine = runningCoroutines[player.userID.Get()]; if (coroutine != null) ServerMgr.Instance?.StopCoroutine(coroutine); runningCoroutines.Remove(player.userID.Get()); var entity = player?.inventory?.loot?.entitySource; if (entity == null || !permission.UserHasPermission(player.UserIDString, permissionTc)) return; if (entity is BuildingPrivlidge) { if (config.ChangeSkinTC && entity.OwnerID != player.userID.Get()) return; TCupboardLayer(player); } } private IEnumerator UpgradeSkin(BasePlayer player, BuildingBlock[] blocks) { var count = 0; for (int i = 0; i < blocks.Length; i++) { var block = blocks[i]; if (block == null || block.IsDestroyed) continue; if (!gradesSkin.TryGetValue(block.grade, out var skinId) || skinId == null) continue; var skinID = GetPlayerSkinID(player, block.grade); var playerData = storedData.PlayerData[player.userID.Get()]; if (skinID != 0 && player.blueprints.steamInventory.HasItem((int)skinID)) continue; if (block.SecondsSinceAttacked < 30f) continue; if (block.skinID != 0 && block.grade.ToString() == "Metal" && !playerData.RandomColor) block.SetCustomColour((uint)player.GetInfoInt("client.SelectedShippingContainerBlockColour", 0)); if (block.skinID == skinID) continue; block.skinID = skinID; block.ChangeGradeAndSkin(block.grade, skinID, true, true); count++; if (i % config.UpdatesPerTick == 0) yield return CoroutineEx.waitForFixedUpdate; } if (count == 0) PrintToChat(player, GetMessage("Lang_UpdateNotRequired", player)); else PrintToChat(player, GetMessage("Lang_UpdateCompleted", player), count, blocks.Length); StopCoroutine(player); } private bool HasPermission(BasePlayer player, string name) { foreach (var blockInfoList in config.BuildingImages.Values) { foreach (var blockInfo in blockInfoList) { if (blockInfo.Title == name && permission.UserHasPermission(player.UserIDString, $"buildingskins.{blockInfo.Title}".ToLower())) { return true; } } } return false; } private ulong GetPlayerSkinID(BasePlayer player, BuildingGrade.Enum grade) { var playerData = storedData.PlayerData.TryGetValue(player.userID.Get(), out var data) ? data : null; return (ulong)(playerData?.GetType().GetField(grade.ToString()).GetValue(playerData) ?? 0); } private BuildingBlock GetLookEntity(BasePlayer player) { return Physics.Raycast(player.eyes.HeadRay(), out RaycastHit raycastHit, 4f, Rust.Layers.Mask.Construction) ? raycastHit.GetEntity() as BuildingBlock : null; } private static void SoundEffect(BasePlayer player, string effect = null) { EffectNetwork.Send(new Effect(effect, player, 0, new Vector3(), new Vector3()), player.Connection); } private string GetMessage(string key, BasePlayer player) { return lang.GetMessage(key, this, player.UserIDString); } #endregion #region Interfaces private void InitializeLayers(BasePlayer player, bool update) { float fade = !update ? 0f : 0.25f; CuiElementContainer container = new CuiElementContainer(); container.Add(new CuiElement() { Parent = "Overlay", Name = InitialLayer, DestroyUi = InitialLayer, Components = { new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }, new CuiImageComponent { Color = "0.235 0.227 0.2 0.9" }, new CuiNeedsCursorComponent() } }); container.Add(new CuiButton() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }, Button = { Close = InitialLayer, Color = "0.141 0.137 0.096 0.98", Sprite = "assets/content/ui/ui.background.transparent.radial.psd" } }, InitialLayer); container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = $"-250 -320", OffsetMax = $"250 300" }, Image = { Color = "0.117 0.121 0.109 0.8", Material = "assets/content/ui/uibackgroundblur.mat", FadeIn = fade } }, InitialLayer, InitialLayer + ".Main"); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 1", AnchorMax = "1 1", OffsetMin = "30 -60", OffsetMax = "-30 -2" }, Text = { Text = GetMessage("Lang_InterfaceTitle", player).ToUpper(), Color = "0.78 0.74 0.70 1.0", FontSize = 20, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter, FadeIn = fade } }, InitialLayer + ".Main"); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = "0 5", OffsetMax = "0 25" }, Text = { Text = $"• {GetMessage("Lang_InterfaceDescr", player)}".ToUpper(), Color = "0.78 0.74 0.70 0.6", FontSize = 12, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter, FadeIn = fade } }, InitialLayer + ".Main"); container.Add(new CuiButton() { RectTransform = { AnchorMin = "1 1", AnchorMax = "1 1", OffsetMin = "-25 -25", OffsetMax = "-5 -5" }, Button = { Close = InitialLayer, Color = "0.71 0.22 0.15 1.0", Sprite = "assets/icons/close.png" } }, InitialLayer + ".Main"); container.Add(new CuiButton() { RectTransform = { AnchorMin = "0 1", AnchorMax = "0 1", OffsetMin = "5 -25", OffsetMax = "25 -5" }, Button = { Command = $"UI_BuildingController settings", Color = "0.31 0.64 0.89 0.5", Sprite = "assets/icons/gear.png" } }, InitialLayer + ".Main", InitialLayer + ".Settings"); CuiHelper.AddUi(player, container); for (var i = 0; i < config.BuildingImages.Count; i++) { var grades = gradesSkin.ElementAt(i); var skinID = GetPlayerSkinID(player, grades.Key); var skinIndex = grades.Value.IndexOf(skinID); ImageLayers(player, i, skinIndex); } } private void ImageLayers(BasePlayer player, int index, int skinIndex) { var playerData = storedData.PlayerData[player.userID.Get()]; var listIndex = config.BuildingImages[index]; var nextIndex = (skinIndex + 1) % listIndex.Count; var grades = gradesSkin.ElementAt(index); var skinID = GetPlayerSkinID(player, grades.Key); var selected = listIndex[skinIndex].SkinId == skinID; var hasSkin = listIndex.Any(x => !string.IsNullOrEmpty(x.Url) && x.SkinId != 0); var hasPermission = HasPermission(player, listIndex[skinIndex].Title); var isBlocked = config.SeparatePermissions && !hasPermission && skinIndex != 0; if (isBlocked && selected) { playerData.GetType().GetField(grades.Key.ToString())?.SetValue(playerData, grades.Value[0]); ImageLayers(player, index, 0); return; } const int marginTop = 10, margin = 6, width = 200, height = 250; var offsetX = (index % 2 == 0 ? -1 : 1) * ((width + margin) / 2 + margin); var offsetY = (index < 2 ? 1 : -1) * ((height + margin) / 2 + margin) - marginTop; CuiElementContainer container = new CuiElementContainer(); container.Add(new CuiButton() { RectTransform = { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = $"{offsetX - width / 2} {offsetY - height / 2}", OffsetMax = $"{offsetX + width / 2} {offsetY + height / 2}" }, Button = { Command = hasSkin ? $"UI_BuildingController change {index} {nextIndex}" : "", Color = "0.22 0.25 0.16 0.9", Material = "assets/content/ui/uibackgroundblur.mat" }, }, InitialLayer + ".Main", InitialLayer + $".Button.{index}"); container.Add(new CuiElement() { Parent = InitialLayer + $".Button.{index}", Name = InitialLayer + $".Image.{index}", DestroyUi = InitialLayer + $".Image.{index}", Components = { new CuiRectTransformComponent { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-85 -72", OffsetMax = "85 98" }, new CuiRawImageComponent { Png = (string) ImageLibrary.Call("GetImage", listIndex[skinIndex].Title), Color = hasSkin && !isBlocked ? "1 1 1 1" : "0.8 0.8 0.8 0.5" } } }); if (hasSkin) { container.Add(new CuiPanel() { RectTransform = { AnchorMin = "1 1", AnchorMax = "1 1", OffsetMin = "-28 -28", OffsetMax = "-5 -5" }, Image = { Color = "0.81 0.77 0.74 0.8", Sprite = "assets/icons/refresh.png" } }, InitialLayer + $".Button.{index}"); } if (!hasSkin || isBlocked) { container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-22 -30", OffsetMax = "22 30" }, Image = { Color = "0.81 0.77 0.74 0.8", Sprite = "assets/content/ui/lock@4x.png" } }, InitialLayer + $".Image.{index}"); } if (grades.Key.ToString() == "Metal" && !isBlocked) { if (skinIndex != 0) { var colorIcon = colors.TryGetValue((uint)player.GetInfoInt("client.SelectedShippingContainerBlockColour", 0), out var value) ? value : "0.20 0.30 0.40 1.0"; container.Add(new CuiButton() { RectTransform = { AnchorMin = "0 1", AnchorMax = "0 1", OffsetMin = "5 -28", OffsetMax = "28 -5" }, Button = { Command = $"UI_BuildingController colors {index}", Color = colorIcon, Sprite = "assets/icons/circle_closed.png" } }, InitialLayer + $".Button.{index}", InitialLayer + ".Color"); container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "-2 -2", OffsetMax = "2 2" }, Image = { Color = "0.81 0.77 0.74 1.0", Sprite = "assets/icons/workshop.png" } }, InitialLayer + ".Color"); } else CuiHelper.DestroyUi(player, InitialLayer + ".Color"); } container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = "0 0", OffsetMax = "0 26" }, Image = { Color = "0 0 0 0.5" } }, InitialLayer + $".Button.{index}", InitialLayer + $".Title.{index}"); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = "10 0", OffsetMax = "125 26" }, Text = { Text = GetMessage(listIndex[skinIndex].Title, player).ToUpper(), Color = "0.81 0.77 0.74 1.0", FontSize = 11, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleLeft } }, InitialLayer + $".Title.{index}"); var textTitle = GetMessage(selected ? "Lang_SkinInstalled" : (isBlocked ? "Lang_Unavailable" : "Lang_InterfaceApply"), player); var colorTitle = selected ? "0.59 0.84 0.18 1.0" : isBlocked ? "0.81 0.77 0.74 1.0" : "0.30 0.65 0.90 1.0"; var colorButton = selected ? "0.30 0.36 0.16 1.0" : isBlocked ? "0.34 0.33 0.31 1.0" : "0.20 0.30 0.40 1.0"; container.Add(new CuiButton() { RectTransform = { AnchorMin = "1 0", AnchorMax = "1 0", OffsetMin = "-75 0", OffsetMax = "0 26" }, Button = { Command = hasSkin && !isBlocked && !selected ? $"UI_BuildingController choose {index} {skinIndex}" : "", Color = colorButton, Material = "assets/content/ui/uibackgroundblur.mat" }, Text = { Text = textTitle.ToUpper(), Color = colorTitle, FontSize = 11, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter } }, InitialLayer + $".Title.{index}"); CuiHelper.AddUi(player, container); } private void SettingsLayer(BasePlayer player) { CuiElementContainer container = new CuiElementContainer(); container.Add(new CuiElement() { Parent = InitialLayer + ".Settings", Name = InitialLayer + ".SettingsMenu", DestroyUi = InitialLayer + ".SettingsMenu", Components = { new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "-350 -5", OffsetMax = "5 5" }, new CuiImageComponent { Color = "0.20 0.30 0.40 1.0", Material = "assets/content/ui/uibackgroundblur.mat" } } }); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "10 0", OffsetMax = "0 0" }, Text = { Text = GetMessage("Lang_MenuSettings", player).ToUpper(), Color = "0.78 0.74 0.70 1.0", FontSize = 20, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleLeft } }, InitialLayer + ".SettingsMenu"); container.Add(new CuiButton() { RectTransform = { AnchorMin = "1 1", AnchorMax = "1 1", OffsetMin = "-25 -25", OffsetMax = "-5 -5" }, Button = { Close = InitialLayer + ".SettingsMenu", Color = "0.71 0.22 0.15 1.0", Sprite = "assets/icons/close.png" } }, InitialLayer + ".SettingsMenu"); container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = "2 -150", OffsetMax = "-35 0" }, Image = { Color = "0.117 0.121 0.109 0.8", Material = "assets/content/ui/uibackgroundblur.mat" } }, InitialLayer + ".SettingsMenu", InitialLayer + ".SettingsButton"); string[] langKeys = { "Lang_ChangeHammer", "Lang_NeedsRepair", "Lang_EnableAnimation" }; string[] commands = { "UI_BuildingController hammer", "UI_BuildingController repair", "UI_BuildingController animation" }; var playerData = storedData.PlayerData[player.userID.Get()]; var margin = 0; for (var i = 0; i < 3; i++) { var active = new[] { playerData.ChangeHammer, playerData.ChangeHammer && playerData.NeedsRepair, playerData.EnableAnimation }[i]; var text = GetMessage($"Lang_Setting{(active ? "Enable" : "Disable")}", player); var textlang = GetMessage(langKeys[i], player); container.Add(new CuiButton() { RectTransform = { AnchorMin = "0 1", AnchorMax = "1 1", OffsetMin = $"10 {-50 - margin}", OffsetMax = $"-10 {-15 - margin}" }, Button = { Command = commands[i], Color = "0.34 0.33 0.31 1.0", Material = "assets/content/ui/uibackgroundblur.mat" }, }, InitialLayer + ".SettingsButton", InitialLayer + $".SettingsButton.{i}"); container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "0 1", OffsetMin = "0 0", OffsetMax = "50 0" }, Image = { Color = active ? "0.36 0.44 0.22 1.0" : "0.71 0.22 0.15 1.0", Material = "assets/content/ui/uibackgroundblur.mat" }, }, InitialLayer + $".SettingsButton.{i}", InitialLayer + $".TextButton"); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }, Text = { Text = text.ToUpper(), Color = active ? "0.78 0.74 0.70 1.0" : "0.78 0.74 0.70 0.6", FontSize = 16, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter } }, InitialLayer + $".TextButton"); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "60 0", OffsetMax = "-5 0" }, Text = { Text = textlang.ToUpper(), Color = "0.78 0.74 0.70 1.0", FontSize = 12, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleLeft } }, InitialLayer + $".SettingsButton.{i}"); margin += 45; } CuiHelper.AddUi(player, container); } private void ColorLayer(BasePlayer player, int index) { var playerData = storedData.PlayerData[player.userID.Get()]; var playerColor = (uint)player.GetInfoInt("client.SelectedShippingContainerBlockColour", 0); CuiElementContainer container = new CuiElementContainer(); container.Add(new CuiElement() { Parent = InitialLayer + ".Color", Name = InitialLayer + ".SetColor", DestroyUi = InitialLayer + ".SetColor", Components = { new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "-250 -5", OffsetMax = "5 5" }, new CuiImageComponent { Color = colors.TryGetValue(playerColor, out var value) ? value : "0.20 0.30 0.40 1.0", Material = "assets/content/ui/uibackgroundblur.mat" } } }); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "10 0", OffsetMax = "0 0" }, Text = { Text = GetMessage("Lang_ColorSettings", player).ToUpper(), Color = "0.78 0.74 0.70 1.0", FontSize = 20, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleLeft } }, InitialLayer + ".SetColor"); container.Add(new CuiButton() { RectTransform = { AnchorMin = "1 1", AnchorMax = "1 1", OffsetMin = "-25 -25", OffsetMax = "-5 -5" }, Button = { Close = InitialLayer + ".SetColor", Color = "0.71 0.22 0.15 1.0", Sprite = "assets/icons/close.png" } }, InitialLayer + ".SetColor"); container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = $"2 {(playerData.RandomColor ? -42 : -228)}", OffsetMax = "-80 0" }, Image = { Color = "0.117 0.121 0.109 0.8", Material = "assets/content/ui/uibackgroundblur.mat" } }, InitialLayer + ".SetColor", InitialLayer + ".ColorButton"); container.Add(new CuiButton() { RectTransform = { AnchorMin = "0.5 1", AnchorMax = "0.5 1", OffsetMin = "-89 -35", OffsetMax = "89 -6" }, Button = { Command = $"UI_BuildingController randomcolor {index}", Color = "0.34 0.33 0.31 1.0", Material = "assets/content/ui/uibackgroundblur.mat" } }, InitialLayer + ".ColorButton", InitialLayer + ".RandomButton"); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0.25 0", AnchorMax = "0.9 1", OffsetMin = "0 0", OffsetMax = "0 0" }, Text = { Text = GetMessage("Lang_Randomcolor", player).ToUpper(), Color = "0.78 0.74 0.70 1.0", FontSize = 12, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter } }, InitialLayer + ".RandomButton"); var sprite = playerData.RandomColor ? "assets/icons/circle_closed.png" : "assets/icons/circle_open.png"; container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "0 1", OffsetMin = "23 5", OffsetMax = "42 -5" }, Image = { Color = "0.85 0.85 0.85 0.8", Sprite = sprite } }, InitialLayer + ".RandomButton", InitialLayer + ".Panel"); if (playerData.RandomColor) { container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "3 3", OffsetMax = "-3 -3" }, Image = { Color = "0 0 0 1", Sprite = "assets/icons/check.png" } }, InitialLayer + ".Panel"); } else { const int marginTop = 2, margin = 6, width = 40, height = 40; for (var i = 0; i < colors.Count; i++) { var colorIndex = colors.ElementAt(i).Key; var colorValue = colors.ElementAt(i).Value; var offsetX = i % 4 * (width + margin) - (2 * width + 1.5 * margin); var offsetY = -i / 4 * (height + margin) + 35 - marginTop; container.Add(new CuiButton { RectTransform = { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = $"{offsetX} {offsetY}", OffsetMax = $"{offsetX + width} {offsetY + height}" }, Button = { Command = colorIndex != playerColor ? $"UI_BuildingController setcolor {index} {colorIndex}" : "", Color = colorValue, Material = "assets/content/ui/uibackgroundblur.mat" } }, InitialLayer + ".ColorButton", InitialLayer + ".PanelButton"); if (colorIndex == playerColor) { container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "2 2", OffsetMax = "-2 -2" }, Image = { Color = "0.59 0.84 0.18 1.0", Sprite = "assets/icons/vote_up.png" } }, InitialLayer + ".PanelButton"); } } } CuiHelper.AddUi(player, container); } private void TCupboardLayer(BasePlayer player) { var isRunning = runningCoroutines.ContainsKey(player.userID.Get()); CuiElementContainer container = new CuiElementContainer(); container.Add(new CuiElement() { Parent = "Overlay", Name = CupboardLayer, DestroyUi = CupboardLayer, Components = { new CuiRectTransformComponent { AnchorMin = "0.5 0.0", AnchorMax = "0.5 0.0", OffsetMin = "400 595", OffsetMax = "572 616" }, new CuiImageComponent { Color = isRunning ? "0.90 0.53 0.05 1.0" : "0.40 0.50 0.20 1.0", Material = "assets/content/ui/uibackgroundblur.mat" } } }); var text = GetMessage(isRunning ? "Lang_TcWait" : "Lang_TcChangeSkin", player); var color = isRunning ? "1 1 1 1" : "0.70 0.90 0.45 1.0"; var sprite = isRunning ? "assets/icons/refresh.png" : "assets/icons/brush.png"; container.Add(new CuiButton { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "22 0", OffsetMax = "0 0" }, Button = { Command = !isRunning ? "UI_BuildingController build" : "", Color = "0 0 0 0" }, Text = { Text = text.ToUpper(), Color = color, FontSize = 11, Align = TextAnchor.MiddleCenter } }, CupboardLayer, CupboardLayer + ".Button"); container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "0 1", OffsetMin = "4 2", OffsetMax = "22 -2" }, Image = { Color = "1 1 1 1", Sprite = sprite } }, CupboardLayer); if (!isRunning) { container.Add(new CuiButton() { RectTransform = { AnchorMin = "1 1", AnchorMax = "1 1", OffsetMin = "5 -20", OffsetMax = "25 0" }, Button = { Command = $"UI_BuildingController open", Color = "0.31 0.64 0.89 0.5", Sprite = "assets/icons/gear.png" } }, CupboardLayer); } CuiHelper.AddUi(player, container); } private IEnumerator PreloadImages(BasePlayer player) { if (player == null || !player.IsConnected) yield break; for (var i = 0; i < config.BuildingImages.Count; i++) { CuiElementContainer temp = new CuiElementContainer(); foreach (var blockInfo in config.BuildingImages[i]) { if (string.IsNullOrEmpty(blockInfo.Url)) continue; temp.Add(new CuiElement() { Parent = "Hud", Name = $".{i}", DestroyUi = $".{i}", Components = { new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "0 0" }, new CuiRawImageComponent { Png = (string) ImageLibrary?.Call("GetImage", blockInfo.Title) } } }); CuiHelper.AddUi(player, temp); yield return new WaitForSeconds(1.0f); CuiHelper.DestroyUi(player, $".{i}"); } } StopCoroutine(player); } #endregion #region Language protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["Lang_UpdateAll"] = "Skin update for all your buildings has started...", ["Lang_UpdateBuilding"] = "Skin update for selected building has started...", ["Lang_UpdateAllTarget"] = "Skin update for all players buildings has started...", ["Lang_UpdateProgress"] = "Please wait for the building skin update to finish.", ["Lang_UpdateCompleted"] = "Building skin update completed.\nUpdated {0} of {1} building blocks.", ["Lang_UpdateNotRequired"] = "All building blocks already have your chosen skin.", ["Lang_NotFoundBlocks"] = "No available buildings found for the selected player.", ["Lang_NotFoundPlayer"] = "Player not found. Use only the Steam Id of the player.", ["Lang_NotFoundBuilding"] = "Building not found. Get closer to the building and repeat again.", ["Lang_NotOwnerBuilding"] = "You are not the owner of this building.", ["Lang_BuildingBlocked"] = "You can't use this command if the building is blocked.", ["Lang_NoPermissions"] = "You don't have permission to use this command.", ["Lang_InterfaceTitle"] = "Choose a default skin for the building block", ["Lang_InterfaceDescr"] = "The skin will be automatically applied to building blocks.", ["Lang_ChangeHammer"] = "Change the skin of a building block with a hammer", ["Lang_NeedsRepair"] = "Allow skin changing with a hammer if a building needs repair", ["Lang_EnableAnimation"] = "Allow building block skin update animation", ["Lang_InterfaceApply"] = "Apply", ["Lang_SkinInstalled"] = "Installed", ["Lang_Unavailable"] = "Unavailable", ["Lang_MenuSettings"] = "Settings", ["Lang_ColorSettings"] = "Set color", ["Lang_Randomcolor"] = "Use random color", ["Lang_SettingEnable"] = "On", ["Lang_SettingDisable"] = "Off", ["Lang_TcChangeSkin"] = "Change building skin", ["Lang_TcWait"] = "Wait...", ["Wood"] = "Wood skin", ["Stone"] = "Stone skin", ["Metal"] = "Metal skin", ["TopTier"] = "TopTier skin", ["Adobe"] = "Adobe skin", ["Brick"] = "Brick skin", ["Brutalist"] = "Brutalist skin", ["Container"] = "Container skin", ["Frontier"] = "Frontier skin", ["Gingerbread"] = "Gingerbread skin" }, this); lang.RegisterMessages(new Dictionary { ["Lang_UpdateAll"] = "Обновление скина для всех ваших построек началось...", ["Lang_UpdateBuilding"] = "Обновление скина для выбранной постройки началось...", ["Lang_UpdateAllTarget"] = "Обновление скина для всех построек игрока началось...", ["Lang_UpdateProgress"] = "Пожалуйста, дождитесь завершения обновления скина построек.", ["Lang_UpdateCompleted"] = "Обновление скина построек завершено.\nОбновлено {0} из {1} строительных блоков.", ["Lang_UpdateNotRequired"] = "Все строительные блоки уже имеют выбранный вами скин.", ["Lang_NotFoundBlocks"] = "Не найдено доступных построек для выбранного игрока.", ["Lang_NotFoundPlayer"] = "Игрок не найден. Используйте только Steam Id игрока.", ["Lang_NotFoundBuilding"] = "Постройка не найдена. Подойдите ближе к постройке и повторите снова.", ["Lang_NotOwnerBuilding"] = "Вы не являетесь владельцем этой постройки.", ["Lang_BuildingBlocked"] = "Вы не можете использовать эту команду в зоне блокировки строительства.", ["Lang_NoPermissions"] = "У вас нет разрешения на использование этой команды.", ["Lang_InterfaceTitle"] = "Выберите скин по умолчанию для строительного блока", ["Lang_InterfaceDescr"] = "Скин будет автоматически применяться к постройке.", ["Lang_ChangeHammer"] = "Изменять скин постройки при помощи киянки", ["Lang_NeedsRepair"] = "Разрешить смену скина киянкой, если требуется ремонт постройки", ["Lang_EnableAnimation"] = "Разрешить анимацию обновления скина постройки", ["Lang_InterfaceApply"] = "Применить", ["Lang_SkinInstalled"] = "Установлен", ["Lang_Unavailable"] = "Недоступен", ["Lang_MenuSettings"] = "Настройки", ["Lang_ColorSettings"] = "Выбор цвета", ["Lang_Randomcolor"] = "Случайный цвет", ["Lang_SettingEnable"] = "Вкл", ["Lang_SettingDisable"] = "Выкл", ["Lang_TcChangeSkin"] = "Изменить скин постройки", ["Lang_TcWait"] = "Ожидайте...", ["Wood"] = "Деревянный скин", ["Stone"] = "Каменный скин", ["Metal"] = "Металлический скин", ["TopTier"] = "МВК скин", ["Adobe"] = "Саманный скин", ["Brick"] = "Кирпичный скин", ["Brutalist"] = "Брутализм скин", ["Container"] = "Контейнерный скин", ["Frontier"] = "Фронтир скин", ["Gingerbread"] = "Пряничный скин" }, this, "ru"); } #endregion #region Data private StoredData storedData; private class StoredData { public Dictionary PlayerData = new Dictionary(); } private class Data { [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public bool ChangeHammer; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public bool NeedsRepair; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public bool EnableAnimation; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public bool RandomColor; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public uint Color; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public ulong Wood; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public ulong Stone; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public ulong Metal; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public ulong TopTier; } private void SaveData() { if (storedData != null) { Interface.Oxide.DataFileSystem.WriteObject($"{Name}_Data", storedData, true); } } private void LoadData() { storedData = Interface.Oxide.DataFileSystem.ReadObject($"{Name}_Data"); if (storedData == null) { storedData = new StoredData(); SaveData(); } foreach (var playerData in storedData.PlayerData.Values) { playerData.Wood = config.BuildingImages[0].Any(b => b.SkinId == playerData.Wood) ? playerData.Wood : 0; playerData.Stone = config.BuildingImages[1].Any(b => b.SkinId == playerData.Stone) ? playerData.Stone : 0; playerData.Metal = config.BuildingImages[2].Any(b => b.SkinId == playerData.Metal) ? playerData.Metal : 0; playerData.TopTier = config.BuildingImages[3].Any(b => b.SkinId == playerData.TopTier) ? playerData.TopTier : 0; } } #endregion } }