using System; using System.Globalization; using System.Collections.Generic; using Oxide.Core; using Newtonsoft.Json; using Oxide.Core.Plugins; using Oxide.Game.Rust.Cui; using UnityEngine; namespace Oxide.Plugins { [Info("COxygenUI", "TF Crazy", "1.0.3")] [Description("Show oxygen amount in diving tank")] public class COxygenUI : RustPlugin { #region Fields [PluginReference] Plugin ImageLibrary; private static COxygenUI _ins; private static Dictionary _pConf; private Dictionary _data; private const string shortname = "diving.tank"; private Dictionary _images = new Dictionary() { {"coxygenui.bg","https://i49.servimg.com/u/f49/14/08/21/53/oxygen12.png"}, }; #endregion #region Server /// /// Instance and initialize fields /// private void OnServerInitialized() { _ins = this; _pConf = new Dictionary(); _data = new Dictionary(); LoadData(); cmd.AddChatCommand(cfg.config.command, this, nameof(COxygenUIPlayerConfCommand)); if (!ImageLibrary) { PrintError("ImageLibrary not found, image not loaded !"); return; } LoadServerImage(); foreach (BasePlayer player in BasePlayer.activePlayerList) OnPlayerConnected(player); } /// /// Destroy OxygenUI player's object /// private void OnPlayerDisconnected(BasePlayer player) { if(_data.ContainsKey(player.userID)) UnityEngine.Object.Destroy(_data[player.userID]); } /// /// Destroy all OxygenUI object /// private void Unload() { SaveData(); OxygenUI[] objects = UnityEngine.Object.FindObjectsOfType(); if (objects != null) { foreach (var obj in objects) UnityEngine.Object.Destroy(obj); } } /// /// Instance and initialize fields /// private void OnPlayerConnected(BasePlayer player) { if(player == null) return; LoadPlayerImage(player); if(cfg.config.alltime && !player.GetComponent()) _data[player.userID] = player.gameObject.AddComponent(); } /// /// Create object OxygenUI when player equiped and underwater /// private void OnLoseCondition(Item item, ref float amount) { if(cfg.config.alltime) return; if (item.info.shortname != shortname) return; BasePlayer player = item?.GetOwnerPlayer(); if (!player || !player.IsSwimming()) return; if (!player.GetComponent()) _data[player.userID] = player.gameObject.AddComponent(); } #endregion #region Component /// /// OxygenUI is object attached to player when item is equiped and underwater /// private class OxygenUI : FacepunchBehaviour { public BasePlayer player; public Item item; public bool uiProgress; public bool created; /// /// Initialize component /// private void Awake() { player = GetComponent(); item = GetItem() as Item; if(!cfg.config.alltime) CreateUI(true); InvokeRepeating("Instance", 0f, 1f); } /// /// Update component /// private void Update() { if(_pConf.ContainsKey(player.userID) && !_pConf[player.userID]) Destroy(this); } /// /// Get diving tank item /// private Item GetItem() { foreach (Item item in player.inventory.containerWear.itemList) { if (item.info.shortname != shortname) continue; return item; } return null; } /// /// Start coroutine. /// Refresh UI for progress bar /// public void Instance() { if(!cfg.config.alltime) { if (!player.IsSwimming() || player == null || player.IsSleeping() || player.IsDead() || !player.IsConnected || item == null || item.condition == 0 || !player.inventory.containerWear.itemList.Contains(item)) Destroy(this); DestroyUI(); CreateUI(); } else { if (player == null || player.IsSleeping() || player.IsDead() || !player.IsConnected || item == null || !player.inventory.containerWear.itemList.Contains(item)) { DestroyUI(true); created = false; return; } if(!created) { CreateUI(true); created = true; } DestroyUI(); CreateUI(); } } /// /// Create UI, (bool) true for create all UI /// private void CreateUI(bool all = false) { var progressCount = 100 * item.condition / item.maxCondition; if(all) { var container = UI.CreateElementContainer("coxygenui.static", "0 0 0 0", "0.45 0.12", "0.53 0.21"); UI.Image(ref container, "coxygenui.static", GetImage("coxygenui.bg"), "0 0", "1 1"); CuiHelper.AddUi(player, container); } else { string progressLayer = string.Empty; if(uiProgress) { progressLayer = "coxygenui.progressbarA"; uiProgress = false; } else { progressLayer = "coxygenui.progressbarB"; uiProgress = true; } var container = UI.CreateElementContainer(progressLayer, "0 0 0 0", "0.4671 0.12", "0.526 0.21"); UI.ImageColor(ref container, progressLayer, HexToRustFormat("#5197bf"), "0 0.25", $"{progressCount / 100.0f} 0.30", "0 0", "0 0"); UI.Label(ref container, progressLayer, $"{item.condition}s", 12, "0.57 0.65", "0.87 0.85"); CuiHelper.AddUi(player, container); } } /// /// Destroy UI, (bool) true for destroy all UI /// private void DestroyUI(bool all = false) { if(all) { CuiHelper.DestroyUi(player, "coxygenui.static"); CuiHelper.DestroyUi(player, "coxygenui.progressbarA"); CuiHelper.DestroyUi(player, "coxygenui.progressbarB"); } else { if(uiProgress) CuiHelper.DestroyUi(player, "coxygenui.progressbarB"); else CuiHelper.DestroyUi(player, "coxygenui.progressbarA"); } } /// /// Called when object is destroy. Clear all UI /// private void OnDestroy() => DestroyUI(true); } #endregion #region Command private void COxygenUIPlayerConfCommand(BasePlayer player, string command, string[] args) { if(!_pConf.ContainsKey(player.userID)) _pConf.Add(player.userID, false); if(!_pConf[player.userID]) { _pConf[player.userID] = true; Message(player, "UIOn"); } else { _pConf[player.userID] = false; Message(player, "UIOff"); } } #endregion #region Localization protected override void LoadDefaultMessages() { //English lang.RegisterMessages(new Dictionary { ["UIOn"] = "Display activated", ["UIOff"] = "Display disabled", }, this); //French lang.RegisterMessages(new Dictionary { ["UIOn"] = "Affichage activé", ["UIOff"] = "Affichage désactivé", }, this, "fr"); } #endregion #region UI /// /// UI helpers tool /// static class UI { public static CuiElementContainer CreateElementContainer(string name, string color, string aMin, string aMax) { var element = new CuiElementContainer() { { new CuiPanel { Image = { Color = color }, RectTransform = { AnchorMin = aMin, AnchorMax = aMax }, CursorEnabled = false }, new CuiElement().Parent = "Hud", name } }; return element; } public static void Panel(ref CuiElementContainer container, string panel, string color, string aMin, string aMax, bool cursor = false) { container.Add(new CuiPanel { Image = { Color = color }, RectTransform = { AnchorMin = aMin, AnchorMax = aMax }, CursorEnabled = cursor }, panel, CuiHelper.GetGuid()); } public static void Label(ref CuiElementContainer container, string panel, string text, int size, string aMin, string aMax, TextAnchor align = TextAnchor.MiddleCenter, string color = null) { container.Add(new CuiLabel { Text = { FontSize = size, Font = "robotocondensed-regular.ttf",Align = align, Text = text }, RectTransform = { AnchorMin = aMin, AnchorMax = aMax } }, panel, CuiHelper.GetGuid()); } public static void Image(ref CuiElementContainer container, string panel, string png, string aMin, string aMax) { container.Add(new CuiElement { Name = CuiHelper.GetGuid(), Parent = panel, Components = { new CuiRawImageComponent { Png = png }, new CuiRectTransformComponent { AnchorMin = aMin, AnchorMax = aMax } } }); } public static void ImageColor(ref CuiElementContainer container, string panel, string color, string aMin, string aMax, string oMin, string oMax) { container.Add(new CuiElement { Name = CuiHelper.GetGuid(), Parent = panel, Components = { new CuiImageComponent { Color = color }, new CuiRectTransformComponent { AnchorMin = aMin, AnchorMax = aMax, OffsetMin = oMin, OffsetMax = oMax } } }); } } #endregion #region Config private static ConfigData cfg; private class ConfigData { [JsonProperty(PropertyName = "CONFIG")] public ConfigSettings config { get; set; } [JsonProperty(PropertyName = "VERSION")] public VersionNumber version { get; set; } } private class ConfigSettings { [JsonProperty(PropertyName = "» Activate outside the water?")] public bool alltime { get; set; } [JsonProperty(PropertyName = "» Chat command")] public string command { get; set; } [JsonProperty(PropertyName = "» Prefix")] public string prefix { get; set; } [JsonProperty(PropertyName = "» Icon")] public ulong icon { get; set; } } private ConfigData GetBaseConfig() { return new ConfigData { config = new ConfigSettings { alltime = false, command = "oxygen", prefix = "• SERVER » FUEL UI : ", icon = 0 }, version = Version }; } protected override void LoadConfig() { base.LoadConfig(); cfg = Config.ReadObject(); if (cfg.version < Version) UpdateConfigValues(); Config.WriteObject(cfg, true); } protected override void LoadDefaultConfig() => cfg = GetBaseConfig(); protected override void SaveConfig() => Config.WriteObject(cfg, true); private void UpdateConfigValues() { PrintWarning("Config update detected! Updating config values..."); ConfigData baseConfig = GetBaseConfig(); cfg.version = Version; cfg.config.command = "oxygen"; cfg.config.prefix = "• SERVER » FUEL UI : "; cfg.config.icon = (ulong) 0; PrintWarning("Config update completed!"); } #endregion #region Helpers /// /// Get image in ImageLibrary plugin /// /// Contains name of image /// Return string formated or empty public static string GetImage(string imageName) => (string)_ins.ImageLibrary?.Call("GetImage", $"{imageName}"); /// /// Load Image in ImageLibrary plugin /// /// Contains name of image /// Return string formated or empty private void LoadServerImage() { foreach(var x in _images) ImageLibrary.Call("AddImage", x.Value, x.Key); } /// /// Pre-load image for player in ImageLibrary plugin /// /// Contains name of image private void LoadPlayerImage(BasePlayer player) { foreach(var x in _images) GetImage(x.Key); } /// /// Get text in player language /// /// Dictionary key /// player id for localisation /// Optional parameters /// Return string translate private string GetLang(string langKey, string playerId = null, params object[] args) => string.Format(lang.GetMessage(langKey, this, playerId), args); /// /// Load player data /// private void LoadData() => _pConf = Interface.Oxide.DataFileSystem.ReadObject>("COxygenUI/playersConf"); /// /// Save player data /// private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject("COxygenUI/playersConf", _pConf); /// /// Send messsage to player /// /// Player informations /// Dictionary key /// Optional parameters private static void Message(BasePlayer player, string langKey, params object[] args) { if (player.IsConnected) _ins.Player.Message(player, cfg.config.prefix + _ins.GetLang(langKey, player.IPlayer.Id, args), cfg.config.icon); } /// /// Convert HEX color to Rust format /// /// Contain string Hex color /// Return string formated color private static string HexToRustFormat(string hex) { if (string.IsNullOrEmpty(hex)) hex = "#FFFFFFFF"; var str = hex.Trim('#'); if (str.Length == 6) str += "FF"; if (str.Length != 8) { throw new Exception(hex); throw new InvalidOperationException("Cannot convert a wrong format."); } var r = byte.Parse(str.Substring(0, 2), NumberStyles.HexNumber); var g = byte.Parse(str.Substring(2, 2), NumberStyles.HexNumber); var b = byte.Parse(str.Substring(4, 2), NumberStyles.HexNumber); var a = byte.Parse(str.Substring(6, 2), NumberStyles.HexNumber); Color color = new Color32(r, g, b, a); return string.Format("{0:F2} {1:F2} {2:F2} {3:F2}", color.r, color.g, color.b, color.a); } #endregion } }