C#:
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<ulong, bool> _pConf;
private Dictionary<ulong, OxygenUI> _data;
private const string shortname = "diving.tank";
private Dictionary<string, string> _images = new Dictionary<string, string>()
{
{"coxygenui.bg","https://i49.servimg.com/u/f49/14/08/21/53/oxygen12.png"},
};
#endregion
#region Server
/// <summary>
/// Instance and initialize fields
/// </summary>
private void OnServerInitialized()
{
_ins = this;
_pConf = new Dictionary<ulong, bool>();
_data = new Dictionary<ulong, OxygenUI>();
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);
}
/// <summary>
/// Destroy OxygenUI player's object
/// </summary>
private void OnPlayerDisconnected(BasePlayer player)
{
if(_data.ContainsKey(player.userID))
UnityEngine.Object.Destroy(_data[player.userID]);
}
/// <summary>
/// Destroy all OxygenUI object
/// </summary>
private void Unload()
{
SaveData();
OxygenUI[] objects = UnityEngine.Object.FindObjectsOfType<OxygenUI>();
if (objects != null)
{
foreach (var obj in objects)
UnityEngine.Object.Destroy(obj);
}
}
/// <summary>
/// Instance and initialize fields
/// </summary>
private void OnPlayerConnected(BasePlayer player)
{
if(player == null)
return;
LoadPlayerImage(player);
if(cfg.config.alltime && !player.GetComponent<OxygenUI>())
_data[player.userID] = player.gameObject.AddComponent<OxygenUI>();
}
/// <summary>
/// Create object OxygenUI when player equiped and underwater
/// </summary>
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<OxygenUI>())
_data[player.userID] = player.gameObject.AddComponent<OxygenUI>();
}
#endregion
#region Component
/// <summary>
/// OxygenUI is object attached to player when item is equiped and underwater
/// </summary>
private class OxygenUI : FacepunchBehaviour
{
public BasePlayer player;
public Item item;
public bool uiProgress;
public bool created;
/// <summary>
/// Initialize component
/// </summary>
private void Awake()
{
player = GetComponent<BasePlayer>();
item = GetItem() as Item;
if(!cfg.config.alltime)
CreateUI(true);
InvokeRepeating("Instance", 0f, 1f);
}
/// <summary>
/// Update component
/// </summary>
private void Update()
{
if(_pConf.ContainsKey(player.userID) && !_pConf[player.userID])
Destroy(this);
}
/// <summary>
/// Get diving tank item
/// </summary>
private Item GetItem()
{
foreach (Item item in player.inventory.containerWear.itemList)
{
if (item.info.shortname != shortname)
continue;
return item;
}
return null;
}
/// <summary>
/// Start coroutine.
/// Refresh UI for progress bar
/// </summary>
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();
}
}
/// <summary>
/// Create UI, (bool) true for create all UI
/// </summary>
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);
}
}
/// <summary>
/// Destroy UI, (bool) true for destroy all UI
/// </summary>
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");
}
}
/// <summary>
/// Called when object is destroy. Clear all UI
/// </summary>
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<string, string>
{
["UIOn"] = "Display <color=#90ee90>activated</color>",
["UIOff"] = "Display <color=#f08080>disabled</color>",
}, this);
//French
lang.RegisterMessages(new Dictionary<string, string>
{
["UIOn"] = "Affichage <color=#90ee90>activé</color>",
["UIOff"] = "Affichage <color=#f08080>désactivé</color>",
}, this, "fr");
}
#endregion
#region UI
/// <summary>
/// UI helpers tool
/// </summary>
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 = "<color=#aabf7d>• SERVER</color> <size=18>»</size> <color=#ebd077>FUEL UI</color> : ",
icon = 0
},
version = Version
};
}
protected override void LoadConfig()
{
base.LoadConfig();
cfg = Config.ReadObject<ConfigData>();
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 = "<color=#aabf7d>• SERVER</color> <size=18>»</size> <color=#ebd077>FUEL UI</color> : ";
cfg.config.icon = (ulong) 0;
PrintWarning("Config update completed!");
}
#endregion
#region Helpers
/// <summary>
/// Get image in ImageLibrary plugin
/// </summary>
/// <param name="imageName">Contains name of image</param>
/// <returns> Return string formated or empty</returns>
public static string GetImage(string imageName) => (string)_ins.ImageLibrary?.Call("GetImage", $"{imageName}");
/// <summary>
/// Load Image in ImageLibrary plugin
/// </summary>
/// <param name="imageName">Contains name of image</param>
/// <returns> Return string formated or empty</returns>
private void LoadServerImage()
{
foreach(var x in _images)
ImageLibrary.Call("AddImage", x.Value, x.Key);
}
/// <summary>
/// Pre-load image for player in ImageLibrary plugin
/// </summary>
/// <param name="imageName">Contains name of image</param>
private void LoadPlayerImage(BasePlayer player)
{
foreach(var x in _images)
GetImage(x.Key);
}
/// <summary>
/// Get text in player language
/// </summary>
/// <param name="langKey">Dictionary key</param>
/// <param name="playerId">player id for localisation</param>
/// <param name="args">Optional parameters</param>
/// <return>Return string translate</return>
private string GetLang(string langKey, string playerId = null, params object[] args) => string.Format(lang.GetMessage(langKey, this, playerId), args);
/// <summary>
/// Load player data
/// </summary>
private void LoadData() => _pConf = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, bool>>("COxygenUI/playersConf");
/// <summary>
/// Save player data
/// </summary>
private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject("COxygenUI/playersConf", _pConf);
/// <summary>
/// Send messsage to player
/// </summary>
/// <param name="player">Player informations</param>
/// <param name="langKey">Dictionary key</param>
/// <param name="args">Optional parameters</param>
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);
}
/// <summary>
/// Convert HEX color to Rust format
/// </summary>
/// <param name="hex">Contain string Hex color</param>
/// <returns> Return string formated color</returns>
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
}
}