using Oxide.Core; using Oxide.Core.Libraries.Covalence; using Oxide.Game.Rust.Cui; using Oxide.Core.Plugins; using Rust; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using Newtonsoft.Json; namespace Oxide.Plugins { [Info("Building Skins", "Kaysharp & Marat", "1.1.6")] [Description("Automatic application of DLC skins for building blocks")] class BuildingSkins : RustPlugin { [PluginReference] private Plugin ImageLibrary; #region Field private const string InitialLayer = "UI_Layer"; private const string permissionUse = "buildingskins.use"; private const string permissionAll = "buildingskins.all"; private const string permissionBuild = "buildingskins.build"; private const string permissionAdmin = "buildingskins.admin"; private const string permissionAutoSkin = "buildingskins.autoSkin"; private readonly Dictionary runningCoroutines = new Dictionary(); private readonly Dictionary> skinInfo = new Dictionary>(); private readonly Dictionary> gradesSkin = new Dictionary>() { { BuildingGrade.Enum.Wood, new List { 10224 } }, { BuildingGrade.Enum.Stone, new List { 10220, 10223, 10225 } }, { BuildingGrade.Enum.Metal, new List { 10221 } }, { BuildingGrade.Enum.TopTier, new List { 0 } } }; #endregion #region Oxide Hooks private void OnServerInitialized() { if (ImageLibrary == null) { PrintError("[ImageLibrary] not found! Plugin is disabled!"); Interface.Oxide.UnloadPlugin(Title); return; } LoadData(); permission.RegisterPermission(permissionUse, this); permission.RegisterPermission(permissionAll, this); permission.RegisterPermission(permissionBuild, this); permission.RegisterPermission(permissionAdmin, this); permission.RegisterPermission(permissionAutoSkin, this); AddCovalenceCommand(config.Commands, nameof(CmdChangeSkin)); foreach (var buildingImage in config.BuildingImages) { if (!string.IsNullOrEmpty(buildingImage.NoSkinUrl.Url)) ImageLibrary.Call("AddImage", buildingImage.NoSkinUrl.Url, $"NoSkinUrl.{buildingImage.Index}"); foreach (var skin in buildingImage.SkinUrls.Where(skin => !string.IsNullOrEmpty(skin.Url))) { ImageLibrary.Call("AddImage", skin.Url, $"SkinUrl.{skin.Title}"); } } foreach (var t in BasePlayer.activePlayerList) { OnPlayerConnected(t); } } private void Unload() { foreach (var player in BasePlayer.activePlayerList) { CuiHelper.DestroyUi(player, InitialLayer); StopCoroutine(player); } SaveData(); runningCoroutines.Clear(); skinInfo.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)); storedData.PlayerData.TryAdd(player.userID, new Data()); if (!skinInfo.ContainsKey(player)) skinInfo.Add(player, new Dictionary()); if (!config.SkinOnConnection || permission.UserHasPermission(player.UserIDString, permissionAutoSkin)) return; var playerData = storedData.PlayerData[player.userID]; foreach (var grade in gradesSkin) { playerData.GetType().GetField(grade.Key.ToString())?.SetValue(playerData, grade.Value); permission.GrantUserPermission(player.UserIDString, permissionAutoSkin, null); } } private void OnPlayerDisconnected(BasePlayer player) { StopCoroutine(player); if (skinInfo.ContainsKey(player)) skinInfo.Remove(player); } private void OnHammerHit(BasePlayer player, HitInfo info) { if (player == null || info.HitEntity == null) return; if (!permission.UserHasPermission(player.UserIDString, permissionUse)) return; if (storedData.PlayerData[player.userID].Disabled) return; var block = info.HitEntity as BuildingBlock; if (block == null || !gradesSkin.ContainsKey(block.grade)) return; var playerData = storedData.PlayerData[player.userID]; if (!playerData.ChangeHammer || block.health != block.MaxHealth() && !playerData.NeedsRepair) return; if ((config.BuildingBlocked && !player.CanBuild() || block.OwnerID != player.userID) && !permission.UserHasPermission(player.UserIDString, permissionAdmin)) return; var skinID = GetPlayerSkinID(player, block.grade); if (gradesSkin[block.grade].IsEmpty() && player.blueprints.steamInventory.HasItem((int)skinID)) return; if (block.skinID == skinID) return; if (playerData.EnableAnimation) block.ClientRPC(null, "DoUpgradeEffect", (int)block.grade, skinID); block.skinID = skinID; if (config.ApplyPaintColor) block.playerCustomColourToApply = player.LastBlockColourChangeId; block.ChangeGradeAndSkin(block.grade, skinID, true, true); } private object OnStructureUpgrade(BuildingBlock block, BasePlayer player, BuildingGrade.Enum grade, ulong skin) { if (player == null || block == null || !gradesSkin.ContainsKey(grade)) return null; if (skin != 0 && player.blueprints.steamInventory.HasItem((int)skin)) return null; if (permission.UserHasPermission(player.UserIDString, "buildingskins.disabled")) return null; if (!permission.UserHasPermission(player.UserIDString, permissionUse)) return null; if (storedData.PlayerData[player.userID].Disabled) return null; var playerData = storedData.PlayerData[player.userID]; var skinID = GetPlayerSkinID(player, grade); if (gradesSkin[grade].IsEmpty() && player.blueprints.steamInventory.HasItem((int)skinID)) return null; if (block.skinID != 0 && block.skinID == skinID) return false; if (playerData.EnableAnimation) block.ClientRPC(null, "DoUpgradeEffect", (int)grade, skinID); block.skinID = skinID; if (config.ApplyPaintColor) block.playerCustomColourToApply = player.LastBlockColourChangeId; NextTick(() => { if (block == null || block.IsDestroyed) return; block.ChangeGradeAndSkin(block.grade, skinID, true, true); }); return null; } #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("Automatically activate skin when player connects")] public bool SkinOnConnection; [JsonProperty("Apply selected paint color to all building blocks")] public bool ApplyPaintColor; [JsonProperty("Number of blocks updated per tick")] public int UpdatesPerTick; [JsonProperty("Image and description settings")] public List BuildingImages; public Oxide.Core.VersionNumber Version; } protected override void LoadDefaultConfig() { config = new PluginConfig { Commands = new string[] { "bskin", "building.skin" }, BuildingBlocked = true, SkinOnConnection = false, ApplyPaintColor = true, UpdatesPerTick = 5, BuildingImages = new List() { new BuildingImage(0, new BlockInfo( "https://www.dropbox.com/scl/fi/rxxqoikeuaobzg8kl4zqs/Hf0iXki.png?rlkey=hjb49erketuciu8nkjmtbif5n&dl=1", "Wooden skin", -1), new List { new BlockInfo( "https://cdn.discordapp.com/attachments/1069410775862607874/1159902339013029960/Frontier.png", "Frontier skin", 0) }), new BuildingImage(1, new BlockInfo( "https://www.dropbox.com/scl/fi/nkph44q9kr47dvscyudpc/wDMwXFh.png?rlkey=7alc1stjxp24w1qrtuw4rffss&dl=1", "Stone skin", -1), new List { new BlockInfo( "https://www.dropbox.com/scl/fi/j6lemhly0m0itvq2ot6m1/6CcMIYE.png?rlkey=enbg6ks49n1f610qr7aq49lmq&dl=1", "Adobe skin", 0), new BlockInfo( "https://www.dropbox.com/scl/fi/ima6fwnv4kwwdfoj3p09i/ASFfa-min.png?rlkey=kiemd61l9li7o82rka7sxxgb5&dl=1", "Brick skin", 1), new BlockInfo( "https://cdn.discordapp.com/attachments/1069410775862607874/1159600644097843200/Screenshot_3498.png", "Brutalist skin", 2) }), new BuildingImage(2, new BlockInfo( "https://www.dropbox.com/scl/fi/ej4j7ns7pbe49nuym3xvf/eKiQ1zc.png?rlkey=bvfo9vuc61gxgy72w2wqzplk3&dl=1", "Metal skin", -1), new List { new BlockInfo( "https://www.dropbox.com/scl/fi/ppktz6licibeisk2kb72x/A0OHBvj.png?rlkey=huxyypf419ujr1qo42fr00nh4&dl=1", "Container skin", 0) }), new BuildingImage(3, new BlockInfo( "https://www.dropbox.com/scl/fi/9neksdfpevwyb8d9azoc7/ZvXnpO4.png?rlkey=ajzltym4cvudizfswku9s0ahv&dl=1", "TopTier skin", -1), new List { new BlockInfo("", "Brutalist skin", 0) }) }, Version = Version }; } 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(1, 1, 0)) { LoadDefaultConfig(); } if (config.Version < new Core.VersionNumber(1, 1, 1)) { config.ApplyPaintColor = 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 Classes private class BuildingImage { public int Index; public BlockInfo NoSkinUrl; public List SkinUrls; public BuildingImage(int index, BlockInfo noSkinUrl, List skinUrls) { Index = index; NoSkinUrl = noSkinUrl; SkinUrls = skinUrls; } } private class BlockInfo { public string Url; public string Title; public int index; public BlockInfo(string url, string title, int index) { Url = url; Title = title; this.index = index; } } #endregion #region Commands [ConsoleCommand("UI_Controller")] private void CmdConsoleHandler(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null || !arg.HasArgs(1)) return; var playerData = storedData.PlayerData[player.userID]; EffectNetwork.Send( new Effect("assets/bundled/prefabs/fx/notice/loot.drag.grab.fx.prefab", player, 0, new Vector3(), new Vector3()), player.Connection); int index; int skinIndex; switch (arg.Args[0].ToLower()) { case "choose": if (!int.TryParse(arg.Args[1], out index)) return; if (!int.TryParse(arg.Args[2], out skinIndex)) return; var grades = gradesSkin.ElementAt(index); playerData.GetType().GetField(grades.Key.ToString()) ?.SetValue(playerData, !skinInfo[player][index] ? gradesSkin[grades.Key][skinIndex - 1] : 0); break; case "change": if (!int.TryParse(arg.Args[1], out index)) return; if (!int.TryParse(arg.Args[2], out skinIndex)) return; if (skinIndex > config.BuildingImages[index].SkinUrls.Count - 1) skinIndex = -1; ImageLayers(player, index, skinIndex); break; case "settings": SettingsLayer(player); 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; case "disablebs": playerData.Disabled = !playerData.Disabled; 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, lang.GetMessage("Lang_NoPermissions", this, player.UserIDString)); return; } if (arg.Length == 0) { InitializeLayers(player, false); return; } if (runningCoroutines.ContainsKey(player.userID)) { PrintToChat(player, lang.GetMessage("Lang_UpdateProgress", this, player.UserIDString)); return; } if (arg.Length > 0 && config.BuildingBlocked && !player.CanBuild() && !permission.UserHasPermission(player.UserIDString, permissionAdmin)) { PrintToChat(player, lang.GetMessage("Lang_BuildingBlocked", this, player.UserIDString)); return; } switch (arg[0].ToLower()) { case "toggle": { storedData.PlayerData[player.userID].Disabled = !storedData.PlayerData[player.userID].Disabled; if (storedData.PlayerData[player.userID].Disabled) PrintToChat(player, lang.GetMessage("Lang_Disabled", this, player.UserIDString)); else PrintToChat(player, lang.GetMessage("Lang_Enabled", this, player.UserIDString)); break; } case "build": { if (!permission.UserHasPermission(player.UserIDString, permissionBuild)) { PrintToChat(player, lang.GetMessage("Lang_NoPermissions", this, player.UserIDString)); return; } var entity = GetLookEntity(player); if (entity == null) { PrintToChat(player, lang.GetMessage("Lang_NotFoundBuilding", this, player.UserIDString)); return; } if (entity.OwnerID != player.userID && !permission.UserHasPermission(player.UserIDString, permissionAdmin)) { PrintToChat(player, lang.GetMessage("Lang_NotOwnerBuilding", this, player.UserIDString)); return; } var blocks = entity.GetBuilding()?.buildingBlocks.ToArray(); if (blocks == null) return; PrintToChat(player, lang.GetMessage("Lang_UpdateBuilding", this, player.UserIDString)); StartCoroutine(player, UpgradeSkin(player, blocks)); break; } case "all": { if (!permission.UserHasPermission(player.UserIDString, permissionAll)) { PrintToChat(player, lang.GetMessage("Lang_NoPermissions", this, player.UserIDString)); return; } ulong owner = 0; if (arg.Length > 1 && !ulong.TryParse(arg[1], out owner)) { PrintToChat(player, lang.GetMessage("Lang_NotFoundPlayer", this, player.UserIDString)); return; } var targetOwner = arg.Length > 1 ? ulong.Parse(arg[1]) : player.userID; if (!permission.UserHasPermission(player.UserIDString, permissionAdmin) && targetOwner != player.userID) { PrintToChat(player, lang.GetMessage("Lang_NoPermissions", this, player.UserIDString)); return; } var blockOwner = BaseNetworkable.serverEntities.OfType() .Where(x => x.OwnerID == targetOwner).ToArray(); if (blockOwner.Length == 0) { PrintToChat(player, lang.GetMessage("Lang_NotFoundBlocks", this, player.UserIDString)); return; } PrintToChat(player, targetOwner != player.userID ? lang.GetMessage("Lang_UpdateAllTarget", this, player.UserIDString) : lang.GetMessage("Lang_UpdateAll", this, player.UserIDString)); StartCoroutine(player, UpgradeSkin(player, blockOwner)); break; } } } #endregion #region Methods private void StartCoroutine(BasePlayer player, IEnumerator routine) { if (runningCoroutines.ContainsKey(player.userID)) return; var coroutine = ServerMgr.Instance?.StartCoroutine(routine); if (coroutine != null) runningCoroutines[player.userID] = coroutine; } private void StopCoroutine(BasePlayer player) { if (!runningCoroutines.ContainsKey(player.userID)) return; var coroutine = runningCoroutines[player.userID]; if (coroutine != null) ServerMgr.Instance?.StopCoroutine(coroutine); runningCoroutines.Remove(player.userID); } 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 || !gradesSkin.ContainsKey(block.grade)) continue; var skinID = GetPlayerSkinID(player, block.grade); if (block.skinID == skinID) continue; block.skinID = skinID; if (config.ApplyPaintColor) block.playerCustomColourToApply = player.LastBlockColourChangeId; block.ChangeGradeAndSkin(block.grade, skinID, true, true); count++; if (i % config.UpdatesPerTick == 0) yield return CoroutineEx.waitForFixedUpdate; } if (count == 0) PrintToChat(player, lang.GetMessage("Lang_UpdateNotRequired", this, player.UserIDString)); else PrintToChat(player, lang.GetMessage("Lang_UpdateCompleted", this, player.UserIDString), count, blocks.Length); StopCoroutine(player); } private ulong GetPlayerSkinID(BasePlayer player, BuildingGrade.Enum grade) { var playerData = storedData.PlayerData[player.userID]; if (playerData != null) { var fieldName = grade.ToString(); return (ulong)playerData.GetType().GetField(fieldName).GetValue(playerData); } return 0; } private BuildingBlock GetLookEntity(BasePlayer player) { RaycastHit raycastHit; return Physics.Raycast(player.eyes.HeadRay(), out raycastHit, 4f, Layers.Mask.Construction) ? raycastHit.GetEntity() as BuildingBlock : null; } #endregion #region Interfaces private void InitializeLayers(BasePlayer player, bool update) { 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); var fade = !update ? 0f : 0.25f; 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 + ".M"); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 1", AnchorMax = "1 1", OffsetMin = "30 -60", OffsetMax = "-30 -2" }, Text = { Text = lang.GetMessage("Lang_InterfaceTitle", this, player.UserIDString).ToUpper(), Color = "0.78 0.74 0.70 1.0", FontSize = 20, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter, FadeIn = fade } }, InitialLayer + ".M"); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = "0 5", OffsetMax = "0 25" }, Text = { Text = $"• {lang.GetMessage("Lang_InterfaceDescr", this, player.UserIDString)}".ToUpper(), Color = "0.78 0.74 0.70 0.6", FontSize = 12, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter, FadeIn = fade } }, InitialLayer + ".M"); container.Add(new CuiButton() { RectTransform = { AnchorMin = "0 1", AnchorMax = "0 1", OffsetMin = "5 -25", OffsetMax = "25 -5" }, Button = { Command = $"UI_Controller settings", Color = "0.31 0.64 0.89 0.5", Sprite = "assets/icons/gear.png" } }, InitialLayer + ".M", InitialLayer + ".S"); 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 + ".M"); const int marginTop = 10; const int margin = 6; const int width = 200; const int height = 250; for (var i = 0; i < config.BuildingImages.Count; i++) { var offsetX = (i % 2 == 0 ? -1 : 1) * ((width + margin) / 2 + margin); var offsetY = (i < 2 ? 1 : -1) * ((height + margin) / 2 + margin) - marginTop; var grades = gradesSkin.ElementAt(i); var skinIndex = grades.Value.IndexOf(GetPlayerSkinID(player, grades.Key)); player.SendConsoleCommand($"UI_Controller change {i} {skinIndex}"); var hasUrl = !string.IsNullOrEmpty(config.BuildingImages[i].SkinUrls[0].Url); 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 = hasUrl ? $"UI_Controller change {i} 0" : "", Color = "0.22 0.25 0.16 0.9", Material = "assets/content/ui/uibackgroundblur.mat", FadeIn = fade }, }, InitialLayer + ".M", InitialLayer + $".BI.{i}"); } CuiHelper.AddUi(player, container); } private void ImageLayers(BasePlayer player, int index, int skinIndex) { var skin = GetPlayerSkinID(player, gradesSkin.ElementAt(index).Key); string image; string text; var hasUrl = true; if (skinIndex > -1) { hasUrl = !string.IsNullOrEmpty(config.BuildingImages[index].SkinUrls[skinIndex].Url); skinInfo[player][index] = false; image = hasUrl ? $"SkinUrl.{config.BuildingImages[index].SkinUrls[skinIndex].Title}" : $"NoSkinUrl.{index}"; text = hasUrl ? $"{config.BuildingImages[index].SkinUrls[skinIndex].Title}" : $"{config.BuildingImages[index].NoSkinUrl.Title}"; skinIndex++; } else { skinInfo[player][index] = true; hasUrl = !string.IsNullOrEmpty(config.BuildingImages[index].SkinUrls[0].Url); image = $"NoSkinUrl.{index}"; text = $"{config.BuildingImages[index].NoSkinUrl.Title}"; skinIndex = 0; } CuiElementContainer container = new CuiElementContainer(); const int marginTop = 10; const int margin = 6; const int width = 200; const int height = 250; var offsetX = (index % 2 == 0 ? -1 : 1) * ((width + margin) / 2 + margin); var offsetY = (index < 2 ? 1 : -1) * ((height + margin) / 2 + margin) - marginTop; 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 = hasUrl ? $"UI_Controller change {index} {skinIndex}" : "", Color = "0.22 0.25 0.16 0.9", Material = "assets/content/ui/uibackgroundblur.mat", FadeIn = 0.25f }, }, InitialLayer + ".M", InitialLayer + $".BI.{index}"); container.Add(new CuiElement() { Parent = InitialLayer + $".BI.{index}", Name = InitialLayer + $".BI.{index}" + ".I", DestroyUi = InitialLayer + $".BI.{index}" + ".I", 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", image) } } }); if (hasUrl) { 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 + $".BI.{index}"); } else { 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 + $".BI.{index}" + ".I"); } container.Add(new CuiElement() { Parent = InitialLayer + $".BI.{index}", Name = InitialLayer + $".BI.{index}" + ".P", DestroyUi = InitialLayer + $".BI.{index}" + ".P", Components = { new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = "0 0", OffsetMax = "0 26" }, new CuiImageComponent { Color = "0 0 0 0.5" } } }); container.Add(new CuiLabel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = "10 0", OffsetMax = "150 27" }, Text = { Text = text.ToUpper(), Color = "0.81 0.77 0.74 1", FontSize = 11, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleLeft } }, InitialLayer + $".BI.{index}" + ".P"); container.Add(new CuiButton() { RectTransform = { AnchorMin = "1 0", AnchorMax = "1 0", OffsetMin = "-75 0", OffsetMax = "0 26" }, Button = { Command = hasUrl ? $"UI_Controller choose {index} {skinIndex}" : "", Color = "0.29 0.36 0.16 1.0", Material = "assets/content/ui/uibackgroundblur.mat" }, Text = { Text = lang.GetMessage("Lang_InterfaceApply", this, player.UserIDString).ToUpper(), Color = "0.59 0.84 0.18 1.0", FontSize = 11, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter } }, InitialLayer + $".BI.{index}" + ".P"); CuiHelper.AddUi(player, container); } private void SettingsLayer(BasePlayer player) { CuiElementContainer container = new CuiElementContainer(); container.Add(new CuiElement() { Parent = InitialLayer + ".S", Name = InitialLayer + ".SM", DestroyUi = InitialLayer + ".SM", 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 = lang.GetMessage("Lang_MenuSettings", this, player.UserIDString).ToUpper(), Color = "0.78 0.74 0.70 1.0", FontSize = 20, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleLeft } }, InitialLayer + ".SM"); container.Add(new CuiButton() { RectTransform = { AnchorMin = "1 1", AnchorMax = "1 1", OffsetMin = "-25 -25", OffsetMax = "-5 -5" }, Button = { Close = InitialLayer + ".SM", Color = "0.78 0.74 0.70 1.0", Sprite = "assets/icons/gear.png" } }, InitialLayer + ".SM"); container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = "2 -200", OffsetMax = "-35 0" }, Image = { Color = "0.117 0.121 0.109 0.8", Material = "assets/content/ui/uibackgroundblur.mat" } }, InitialLayer + ".SM", InitialLayer + ".BM"); string[] langKeys = { "Lang_ChangeHammer", "Lang_NeedsRepair", "Lang_EnableAnimation", "Lan_DisableBS" }; string[] commands = { "UI_Controller hammer", "UI_Controller repair", "UI_Controller animation", "UI_Controller DisableBS" }; var playerData = storedData.PlayerData[player.userID]; var margin = 0; for (var i = 0; i < 4; i++) { var active = new[] { playerData.ChangeHammer, playerData.ChangeHammer && playerData.NeedsRepair, playerData.EnableAnimation, playerData.Disabled }[i]; var text = lang.GetMessage($"Lang_Setting{(active ? "Enable" : "Disable")}", this, player.UserIDString); var textlang = lang.GetMessage(langKeys[i], this, player.UserIDString); var command = commands[i]; container.Add(new CuiPanel() { RectTransform = { AnchorMin = "0 1", AnchorMax = "1 1", OffsetMin = $"10 {-50 - margin}", OffsetMax = $"-10 {-15 - margin}" }, Image = { Color = "0.30 0.27 0.25 0.6", Material = "assets/content/ui/uibackgroundblur.mat" } }, InitialLayer + ".BM", InitialLayer + $".BM.{i}"); container.Add(new CuiButton() { RectTransform = { AnchorMin = "0 0", AnchorMax = "0 1", OffsetMin = "0 0", OffsetMax = "50 0" }, Button = { Command = command, Color = active ? "0.36 0.44 0.22 1.0" : "0.71 0.22 0.15 1.0", Material = "assets/content/ui/uibackgroundblur.mat" }, 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 + $".BM.{i}"); 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 + $".BM.{i}"); margin += 45; } CuiHelper.AddUi(player, container); } private IEnumerator PreloadImages(BasePlayer player) { if (player != null && player.IsConnected) { for (var i = 0; i < config.BuildingImages.Count; i++) { CuiElementContainer temp = new CuiElementContainer(); for (var j = 0; j < config.BuildingImages[i].SkinUrls.Count; j++) { if (!string.IsNullOrEmpty(config.BuildingImages[i].SkinUrls[j].Url)) { 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", $"SkinUrl.{config.BuildingImages[i].SkinUrls[j].Title}") } } }); } } if (!string.IsNullOrEmpty(config.BuildingImages[i].NoSkinUrl.Url)) { 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", $"NoSkinUrl.{i}") } } }); } 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_MenuSettings"] = "Settings", ["Lang_Disabled"] = "Building skins disabled. /bskin toggle to enable it again.", ["Lan_DisableBS"] = "Disable building skin", ["Lang_Enabled"] = "Building skins enabled. /bskin toggle to disable it again.", ["Lang_SettingEnable"] = "On", ["Lang_SettingDisable"] = "Off" }, 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_MenuSettings"] = "Настройки", ["Lan_DisableBS"] = "Отключить скин здания", ["Lang_Disabled"] = "Построение скинов отключено. /bskin toggle, чтобы снова включить его.", ["Lang_SettingEnable"] = "Вкл", ["Lang_Enabled"] = "Включение скинов здания. /bskin toggle, чтобы снова отключить его.", ["Lang_SettingDisable"] = "Выкл" }, 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 Disabled = false; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public bool ChangeHammer = true; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public bool NeedsRepair = true; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public bool EnableAnimation = true; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public ulong Wood = 0; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public ulong Stone = 0; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public ulong Metal = 0; [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public ulong TopTier = 0; } 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(); } } #endregion } }