C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
/*
* < ----- End-User License Agreement ----->
*
* You may not copy, modify, merge, publish, distribute, sublicense, or sell copies of this software without the developer’s consent.*
* THIS SOFTWARE IS PROVIDED BY IIIaKa AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*
* Developer: IIIaKa
* https://t.me/iiiaka
* Discord: @iiiaka
* https://github.com/IIIaKa
* https://umod.org/user/IIIaKa
* https://codefling.com/iiiaka
* https://lone.design/vendor/iiiaka/
* https://www.patreon.com/iiiaka
* https://boosty.to/iiiaka
* Codefling plugin page: https://codefling.com/plugins/advanced-status
* Codefling license: https://codefling.com/plugins/advanced-status?tab=downloads_field_4
*
* Lone.Design plugin page: https://lone.design/product/advanced-status/
*
* Copyright © 2023-2025 IIIaKa
*/
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using Network;
using Facepunch;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Oxide.Core.Libraries.Covalence;
namespace Oxide.Plugins {
[Info("Advanced Status", "IIIaKa", "0.1.18")]
[Description("A useful API plugin that mimics in-game status bars and allows the addition of custom ones.")]
class AdvancedStatus : RustPlugin {
//TODO
//When the ability to change the hook call order is added, remove InitPlayer from the CreateBar method
//TODO
//When the in-game HUD is disabled, the bars remain enabled.Need to find a way to track this
//TODO
//ToolsHUDUI
//GameUI.Hud.Vitals.Main - hp bar
//GameUI.Hud.Vitals - status bars
[PluginReference]
private Plugin ImageLibrary;
#region ~Variables~
private static AdvancedStatus Instance {
get;
set;
}
private bool _isReady = false;
private const string PERMISSION_ADMIN = "advancedstatus.admin", AdvancedStatusName = "AdvancedStatus", DefaultImage = "AdvancedStatus_Default", GetImage = "GetImage", NoteInv = "note.inv", ImagesPath = $@"{AdvancedStatusName}\Images", ImagesPattern = "*.png", Hooks_OnLoaded = $"On{AdvancedStatusName}Loaded", Hooks_OnBarDeleted = "OnAdvancedBarDeleted", Hooks_GainedBuilding = "OnPlayerGainedBuildingPrivilege", Hooks_LostBuilding = "OnPlayerLostBuildingPrivilege", Placeholders_UserId = "*userId*", Placeholders_UserName = "*userName*";
private readonly string[] HttpScheme = new string[2] {
"http://", "https://"
}
;
private string ImagePath = string.Empty;
public Hash<ulong, PlayerWatcher> _playersList = new Hash<ulong, PlayerWatcher>();
public Dictionary<string, string> _imagesList = new Dictionary<string, string>();
public string[] _cachedBarsContainer = new string[25];
private readonly HashSet<ItemCategory> _buildingItemsExceptedCategories = new HashSet<ItemCategory> {
ItemCategory.Attire, ItemCategory.Medical, ItemCategory.Component
}
;
private readonly Dictionary<ItemCategory, HashSet<int>> _buildingItems = new Dictionary<ItemCategory, HashSet<int>>() {
{
ItemCategory.Weapon, new HashSet<int>() {
1714509152, -187304968, 1145722690, -759279626, -1290278434
}
}
, {
ItemCategory.Resources, new HashSet<int>() {
634478325
}
}
, {
ItemCategory.Tool, new HashSet<int>() {
1803831286, 200773292
}
}
, {
ItemCategory.Food, new HashSet<int>() {
122783240, 1911552868, 838831151, 803954639, -778875547, 998894949, 1512054436, -2084071424, -1305326964, -1776128552, -886280491, -237809779, 1898094925, -1511285251, 2133269020, 830839496, 390728933, -520133715, 1533551194, -992286106
}
}
, {
ItemCategory.Ammunition, new HashSet<int>() {
-484006286, -1827561369
}
}
, {
ItemCategory.Misc, new HashSet<int>() {
573676040, 1242522330, -1973785141, -695124222, 282103175, 809199956, -1679267738, -489848205, -2058362263, 1358643074, -173268125, -173268126, -173268128, -173268129, -173268131, -173268132, 882559853, 1885488976, 2104517339, 699075597
}
}
}
;
private readonly Dictionary<ItemCategory, HashSet<int>> _buildingItems2 = new Dictionary<ItemCategory, HashSet<int>>() {
{
ItemCategory.Items, new HashSet<int>() {
613961768, 696029452, -1501434104
}
}
, {
ItemCategory.Electrical, new HashSet<int>() {
363163265, -144513264, -566907190, -144417939
}
}
, {
ItemCategory.Fun, new HashSet<int>() {
-2124352573, -1379036069, -1530414568, -1049881973, -1961560162, -979951147, 476066818, -912398867, 1523403414, -583379016, -20045316, -2040817543, 273172220, 576509618, -2107018088, 1784406797, 204970153, 1094293920
}
}
}
;
private readonly HashSet<int> _stackableItems = new HashSet<int>() {
1055319033, 349762871, 915408809, -1023065463, -1234735557, 215754713, 14241751, 588596902, -2097376851, 785728077, 51984655, -1691396643, -1211166256, -1321651331, 605467368, 1712070256, -1685290200, -1036635990, -727717969, -1800345240, -1671551935, -1199897169, -1199897172, -1023374709, 1553078977, 1401987718, -89874794, -493159321, 1072924620, 1330084809, 926800282, -1802083073, 479143914, 1882709339, 95950017, 1199391518, 1414245522, 1234880403, -1994909036, -1021495308, 642482233, 2019042823, 73681876, 1744298439, 1324203999, -656349006, -7270019, -379734527, -1553999294, -280223496, -515830359, -1306288356, -1486461488, -99886070, 261913429, -454370658, -1538109120, -586342290, -568419968, 1770475779, 1668129151, 989925924, 1973684065, -1848736516, -1440987069, -751151717, -78533081, -1509851560, 1422530437, 1917703890, -1162759543, -1130350864, -682687162, 1536610005, -1709878924, 1272768630, -989755543, 1873897110, -1520560807, 1827479659, 813023040, -395377963, -1167031859, 1391703481, -242084766, 621915341, -996920608, 1819863051, -1770889433, -1824770114, 831955134, -1433390281, 1036321299, 1223900335, -602717596, -126305173, -888153050, 1364514421, -455286320, 1762167092, 1223729384, 1572152877, -282193997, 180752235, -1386082991, 70102328, 22947882, 81423963, 1058261682, -151387974, 550753330, -384243979, 1771755747, 122783240, 1911552868, 1112162468, 838831151, 803954639, 858486327, -1305326964, -1776128552, 1272194103, 2133269020, 830839496, 854447607, 1533551194, -992286106, 1660145984, 390728933, -520133715, 1367190888, -778875547, 998894949, -886280491, -237809779, -2086926071, 1512054436, -2084071424, -567909622, 1898094925, -1511285251, -1018587433, 1776460938, 1719978075, 634478325, -1938052175, -858312878, -321733511, 1568388703, -592016202, -930193596, -265876753, -1579932985, -1982036270, 317398316, 1381010055, -946369541, 69511070, -4031221, -1779183908, -804769727, -544317637, -277057363, -932201673, -2099697608, -1157596551, -1581843485, 1523195708, -1779180711, -151838493, -1366326648, 1735402444, -1673693549
}
;
private static readonly HashSet<string> _doubleMountsList = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {
"saddlerear", "saddletest", "bikedriverseat", "bikepassengerseat"
}
;
private static readonly HashSet<string> _mountsList = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {
"motorbikedriverseat", "motorbikepassengerseat", "modularcardriverseat", "modularcarpassengerseatleft", "modularcarpassengerseatright", "modularcarpassengerseatlesslegroomleft", "modularcarpassengerseatlesslegroomright", "modularcarpassengerseatsidewayleft", "miniheliseat", "minihelipassenger", "transporthelipilot", "transporthelicopilot", "attackhelidriver", "attackheligunner", "submarinesolodriverstanding", "submarineduodriverseat", "submarineduopassengerseat", "snowmobiledriverseat", "snowmobilepassengerseat", "snowmobilepassengerseat tomaha", "workcartdriver", "locomotivedriver", "craneoperator"
}
;
private static readonly string[] _layersList = new string[] {
"Under", "Overall", "Overlay", "Hud", "Hud.Menu"
}
;
private static readonly string[] _fontsList = new string[] {
"RobotoCondensed-Bold.ttf", "RobotoCondensed-Regular.ttf", "PermanentMarker.ttf", "DroidSansMono.ttf"
}
;
#endregion
#region ~Configuration~
private static Configuration _config;
private class Configuration {
[JsonProperty(PropertyName = "Chat command")]
public string Command = "bar";
[JsonProperty(PropertyName = "Is it worth enabling console notifications for the successful loading of local images?")]
public bool Notify_Images = false;
[JsonProperty(PropertyName = "Interval(in seconds) for counting in-game status bars")]
public float Count_Interval = 0.5f;
[JsonProperty(PropertyName = "Interval(in seconds) for counting Building Privilege status bars.Note: Calculating Building Privilege is significantly more resource-intensive than other counts")]
public float Count_Interval_Privilege = 1f;
[JsonProperty(PropertyName = "Bar - Display Layer.If you have button bars, it's advisable to use Hud(https://umod.org/guides/rust/basic-concepts-of-gui#layers)")]
public string Bar_Layer = "Under";
[JsonProperty(PropertyName = "Bar - Left to Right")]
public bool Bar_LeftToRight = true;
[JsonProperty(PropertyName = "Bar - Offset between status bars")]
public int Bar_Offset_Between = 2;
[JsonProperty(PropertyName = "Bar - Default Height")]
public int Bar_Height = 26;
[JsonProperty(PropertyName = "Main - Default Color")]
public string Bar_Main_Color = "#505F75";
[JsonProperty(PropertyName = "Main - Default Transparency")]
public float Bar_Main_Transparency = 0.7f;
[JsonProperty(PropertyName = "Main - Default Material(empty to disable)")]
public string Bar_Main_Material = "";
[JsonProperty(PropertyName = "Image - Default Image")]
public string Bar_Image = "AdvancedBar_Image";
[JsonProperty(PropertyName = "Image - Default Color")]
public string Bar_Image_Color = "#6B7E95";
[JsonProperty(PropertyName = "Image - Default Transparency")]
public float Bar_Image_Transparency = 1.0f;
[JsonProperty(PropertyName = "Image - Outline Default Color")]
public string Bar_Image_Outline_Color = "#000000";
[JsonProperty(PropertyName = "Image - Outline Default Transparency")]
public float Bar_Image_Outline_Transparency = 1.0f;
[JsonProperty(PropertyName = "Image - Outline Default Distance")]
public string Bar_Image_Outline_Distance = "0.75 0.75";
[JsonProperty(PropertyName = "Text - Default Size")]
public int Bar_Text_Size = 12;
[JsonProperty(PropertyName = "Text - Default Color")]
public string Bar_Text_Color = "#FFFFFF";
[JsonProperty(PropertyName = "Text - Default Transparency")]
public float Bar_Text_Transparency = 1f;
[JsonProperty(PropertyName = "Text - Default Font(https://umod.org/guides/rust/basic-concepts-of-gui#fonts)")]
public string Bar_Text_Font = "RobotoCondensed-Bold.ttf";
[JsonProperty(PropertyName = "Text - Default Offset Horizontal")]
public int Bar_Text_Offset_Horizontal = 0;
[JsonProperty(PropertyName = "Text - Outline Default Color")]
public string Bar_Text_Outline_Color = "#000000";
[JsonProperty(PropertyName = "Text - Outline Default Transparency")]
public float Bar_Text_Outline_Transparency = 1.0f;
[JsonProperty(PropertyName = "Text - Outline Default Distance")]
public string Bar_Text_Outline_Distance = "0.75 0.75";
[JsonProperty(PropertyName = "SubText - Default Size")]
public int Bar_SubText_Size = 12;
[JsonProperty(PropertyName = "SubText - Default Color")]
public string Bar_SubText_Color = "#FFFFFF";
[JsonProperty(PropertyName = "SubText - Default Transparency")]
public float Bar_SubText_Transparency = 1f;
[JsonProperty(PropertyName = "SubText - Default Font")]
public string Bar_SubText_Font = "RobotoCondensed-Bold.ttf";
[JsonProperty(PropertyName = "SubText - Outline Default Color")]
public string Bar_SubText_Outline_Color = "#000000";
[JsonProperty(PropertyName = "SubText - Outline Default Transparency")]
public float Bar_SubText_Outline_Transparency = 1.0f;
[JsonProperty(PropertyName = "SubText - Outline Default Distance")]
public string Bar_SubText_Outline_Distance = "0.75 0.75";
[JsonProperty(PropertyName = "Progress - Default Color")]
public string Bar_Progress_Color = "#89B840";
[JsonProperty(PropertyName = "Progress - Default Transparency")]
public float Bar_Progress_Transparency = 0.7f;
[JsonProperty(PropertyName = "Progress - Default OffsetMin")]
public string Bar_Progress_OffsetMin = "25 2.5";
[JsonProperty(PropertyName = "Progress - Default OffsetMax")]
public string Bar_Progress_OffsetMax = "-3.5 -3.5";
public Oxide.Core.VersionNumber Version;
}
protected override void LoadConfig() {
base.LoadConfig();
try {
_config = Config.ReadObject<Configuration>();
}
catch (Exception ex) {
PrintError($"{ex.Message}\n\n[{Title}] Your configuration file contains an error.");
}
if (_config == null || _config.Version == new VersionNumber()) {
PrintWarning("The configuration file is not found or contains errors.Creating a new one...");
LoadDefaultConfig();
}
else if (_config.Version < Version) {
PrintWarning($"Your configuration file version({_config.Version}) is outdated.Updating it to {Version}.");
_config.Version = Version;
PrintWarning($"The configuration file has been successfully updated to version {_config.Version}!");
}
if (!_layersList.Contains(_config.Bar_Layer)) _config.Bar_Layer = _layersList[0];
if (!IsStringHexColor(_config.Bar_Main_Color)) _config.Bar_Main_Color = "#505F75";
_config.Bar_Main_Transparency = Mathf.Clamp01(_config.Bar_Main_Transparency);
if (!IsStringHexColor(_config.Bar_Image_Color)) _config.Bar_Image_Color = "#6B7E95";
_config.Bar_Image_Transparency = Mathf.Clamp01(_config.Bar_Image_Transparency);
if (!IsStringHexColor(_config.Bar_Image_Outline_Color)) _config.Bar_Image_Outline_Color = "#000000";
_config.Bar_Image_Outline_Transparency = Mathf.Clamp01(_config.Bar_Image_Outline_Transparency);
_config.Bar_Text_Size = Math.Clamp(_config.Bar_Text_Size, 1, 25);
if (!IsStringHexColor(_config.Bar_Text_Color)) _config.Bar_Text_Color = "#FFFFFF";
_config.Bar_Text_Transparency = Mathf.Clamp01(_config.Bar_Text_Transparency);
_config.Bar_Text_Font = GetValidFont(_config.Bar_Text_Font);
if (!IsStringHexColor(_config.Bar_Text_Outline_Color)) _config.Bar_Text_Outline_Color = "#000000";
_config.Bar_Text_Outline_Transparency = Mathf.Clamp01(_config.Bar_Text_Outline_Transparency);
_config.Bar_SubText_Size = Math.Clamp(_config.Bar_SubText_Size, 1, 25);
if (!IsStringHexColor(_config.Bar_SubText_Color)) _config.Bar_SubText_Color = "#FFFFFF";
_config.Bar_SubText_Transparency = Mathf.Clamp01(_config.Bar_SubText_Transparency);
_config.Bar_SubText_Font = GetValidFont(_config.Bar_SubText_Font);
if (!IsStringHexColor(_config.Bar_SubText_Outline_Color)) _config.Bar_SubText_Outline_Color = "#000000";
_config.Bar_SubText_Outline_Transparency = Mathf.Clamp01(_config.Bar_SubText_Outline_Transparency);
if (!IsStringHexColor(_config.Bar_Progress_Color)) _config.Bar_Progress_Color = "#89B840";
_config.Bar_Progress_Transparency = Mathf.Clamp01(_config.Bar_Progress_Transparency);
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(_config);
protected override void LoadDefaultConfig() => _config = new Configuration() {
Version = Version
}
;
#endregion
#region ~Language~
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["MsgDays"]
= "d", ["MsgHours"] = "h", ["MsgMinutes"] = "m", ["MsgSeconds"] = "s"
}
, this);
lang.RegisterMessages(new Dictionary<string, string> {
["MsgDays"]
= "д", ["MsgHours"] = "ч", ["MsgMinutes"] = "м", ["MsgSeconds"] = "с"
}
, this, "ru");
}
#endregion
#region ~Methods~
private System.Collections.IEnumerator InitPlugin() {
for (int i = 0; i <= 24; i++) {
int y = 102 + (28 * i);
_cachedBarsContainer[i] = $@"[{{""name"":""{AdvancedStatusName}"",""parent"":""{_config.Bar_Layer}"",""components"":[{{""type"":""UnityEngine.UI.Image"",""color"":""0 0 0 0""}},
{{""type"":""RectTransform"",""anchormin"":""1 0"",""anchormax"":""1 0"",""offsetmin"":""-208 {y}"",""offsetmax"":""-16 {y + 1500}""}}]}}]";
}
yield return ServerMgr.Instance.StartCoroutine(InitImages());
yield return new WaitForSeconds(1);
foreach (var player in BasePlayer.activePlayerList) {
if (!player.IsNpc) InitPlayer(player);
}
Subscribe(nameof(OnPlayerConnected));
Subscribe(nameof(OnPlayerDisconnected));
Subscribe(nameof(OnPlayerSleep));
Subscribe(nameof(OnPlayerSleepEnded));
Subscribe(nameof(OnEntityDeath));
Subscribe(nameof(OnSendCommand));
Subscribe(nameof(OnEntityMounted));
Subscribe(nameof(OnEntityDismounted));
Subscribe(nameof(OnNpcConversationStart));
Subscribe(nameof(OnNpcConversationEnded));
_isReady = true;
yield return new WaitForSeconds(1);
Interface.CallHook(Hooks_OnLoaded, Version);
}
private System.Collections.IEnumerator InitImages() {
string[] images = Interface.Oxide.DataFileSystem.GetFiles(ImagesPath, ImagesPattern);
if (images.Any()) {
int lastBackslash, lastDot;
string name;
foreach (var path in images) {
if (string.IsNullOrWhiteSpace(path)) continue;
lastBackslash = path.LastIndexOf('\\');
lastDot = path.LastIndexOf('.');
name = path.Substring(lastBackslash + 1, lastDot - lastBackslash - 1);
yield return ServerMgr.Instance.StartCoroutine(StoreImage(name, path));
}
}
if (!_imagesList.ContainsKey(DefaultImage)) {
_imagesList[DefaultImage] = string.Empty;
PrintError($"The image folder is empty, or the default image is missing!\nFor proper functionality, the file ***{Path.DirectorySeparatorChar}data{Path.DirectorySeparatorChar}{ImagesPath}{Path.DirectorySeparatorChar}{DefaultImage}.png must be present! Please reload the plugin after adding it.");
}
}
private System.Collections.IEnumerator StoreImages(Dictionary<string, string> images) {
foreach (var kvp in images) yield return ServerMgr.Instance.StartCoroutine(StoreImage(kvp.Key, kvp.Value));
}
private System.Collections.IEnumerator StoreImage(string name, string path) {
using (UnityWebRequest request = UnityWebRequestTexture.GetTexture($"file://{path}")) {
yield return request.SendWebRequest();
if (request.result != UnityWebRequest.Result.Success) {
PrintError($"Failed to load the image '{name}' from {path}");
yield break;
}
Texture2D tex = DownloadHandlerTexture.GetContent(request);
if (_imagesList.ContainsKey(name)) FileStorage.server.Remove(uint.Parse(_imagesList[name]), FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);
_imagesList[name] = FileStorage.server.Store(tex.EncodeToPNG(), FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID).ToString();
if (_config.Notify_Images) Puts($"The image '{name}' was stored successfully!");
UnityEngine.Object.DestroyImmediate(tex);
}
}
private void InitPlayer(BasePlayer player) {
RemovePlayer(player);
_playersList[player.userID] = player.gameObject.AddComponent<PlayerWatcher>();
}
private void RemovePlayer(BasePlayer player) {
var playerWatcher = player.gameObject.GetComponent<PlayerWatcher>();
if (playerWatcher != null) UnityEngine.Object.DestroyImmediate(playerWatcher);
_playersList.Remove(player.userID);
}
private bool IsBuildingBlockItem(ItemDefinition itemDef) {
if (itemDef != null && !_buildingItemsExceptedCategories.Contains(itemDef.category)) {
if (_buildingItems.TryGetValue(itemDef.category, out var list)) return list.Contains(itemDef.itemid);
else if (_buildingItems2.TryGetValue(itemDef.category, out var list2)) return !list2.Contains(itemDef.itemid);
return true;
}
return false;
}
public static BuildingPrivlidge GetBuildingPrivilege(BasePlayer player) {
var obb = new OBB(player.transform.position, player.transform.lossyScale, player.transform.rotation, player.bounds);
BuildingBlock other = null;
BuildingPrivlidge result = null;
var obj2 = Pool.Get<List<BuildingBlock>>();
Vis.Entities(obb.position, 16f + obb.extents.magnitude, obj2, 2097152);
for (int i = 0; i < obj2.Count; i++) {
var buildingBlock = obj2[i];
if (buildingBlock.isServer != player.isServer || !buildingBlock.IsOlderThan(other) || obb.Distance(buildingBlock.WorldSpaceBounds()) > 16f) continue;
var building = buildingBlock.GetBuilding();
if (building != null) {
var dominatingBuildingPrivilege = building.GetDominatingBuildingPrivilege();
if (!(dominatingBuildingPrivilege == null)) {
other = buildingBlock;
result = dominatingBuildingPrivilege;
}
}
}
Pool.FreeUnmanaged(ref obj2);
return result;
}
public static bool TryGetEntityPrivilege(BasePlayer player, out EntityPrivilege result) {
var obb = new OBB(player.transform.position, player.transform.lossyScale, player.transform.rotation, player.bounds);
LegacyShelter other = null;
var shelterList = Pool.Get<List<LegacyShelter>>();
Vis.Entities(obb.position, 10f, shelterList, 2097152);
for (int i = 0; i < shelterList.Count; i++) {
var shelter = shelterList[i];
if (shelter.isServer != player.isServer || !shelter.IsOlderThan(other) || obb.Distance(shelter.WorldSpaceBounds()) > 3f) continue;
other = shelter;
}
Pool.FreeUnmanaged(ref shelterList);
result = other?.GetEntityBuildingPrivilege();
return result != null;
}
public static HashSet<VehiclePrivilege> GetVehiclePrivileges(BasePlayer player) {
var obb = new OBB(player.transform.position, player.transform.lossyScale, player.transform.rotation, player.bounds);
var result = new HashSet<VehiclePrivilege>();
var tugList = Pool.Get<List<Tugboat>>();
Vis.Entities(obb.position, 1f, tugList);
for (int i = 0; i < tugList.Count; i++) {
var tug = tugList[i];
if (tug.isServer != player.isServer || tug.children == null || obb.Distance(tug.WorldSpaceBounds()) > 3f) continue;
foreach (var child in tug.children) {
if (child is VehiclePrivilege privilege) {
result.Add(privilege);
break;
}
}
}
Pool.FreeUnmanaged(ref tugList);
return result;
}
public static void SortAdvancedBars(List<AdvancedBar> bars) {
bars.Sort((b1, b2) => {
int orderComparison = b1.Order.CompareTo(b2.Order);
return (orderComparison != 0) ? orderComparison : b1.Id.CompareTo(b2.Id);
}
);
}
public static bool IsStringHexColor(string hexColor) => !string.IsNullOrWhiteSpace(hexColor) && hexColor[0] == '#' && hexColor.Length == 7;
public static string StringColorFromHex(string hexColor, float transparent) {
if (string.IsNullOrWhiteSpace(hexColor)) return $"1 1 1 {transparent:F2}";
if (hexColor[0] != '#' || hexColor.Length < 7) return hexColor;
int red = Convert.ToInt32(hexColor.Substring(1, 2), 16);
int green = Convert.ToInt32(hexColor.Substring(3, 2), 16);
int blue = Convert.ToInt32(hexColor.Substring(5, 2), 16);
double redPercentage = (double)red / 255;
double greenPercentage = (double)green / 255;
double bluePercentage = (double)blue / 255;
return $"{redPercentage:F2} {greenPercentage:F2} {bluePercentage:F2} {transparent:F2}";
}
public static string GetValidFont(string font) {
for (int i = 0; i < _fontsList.Length; i++) {
if (font.Equals(_fontsList[i], StringComparison.OrdinalIgnoreCase)) return _fontsList[i];
}
return _fontsList[0];
}
#endregion
#region ~API~
private object IsReady() => _isReady ? true : null;
private void CreateBar(object obj, Dictionary<string, object> parameters) => CreateBar($"{obj}", parameters);
private void CreateBar(string userIDStr, Dictionary<string, object> parameters) {
if (ulong.TryParse(userIDStr, out var userID)) CreateBar(userID, parameters);
}
private void CreateBar(BasePlayer player, Dictionary<string, object> parameters) => CreateBar(player.userID.Get(), parameters);
private void CreateBar(ulong userID, Dictionary<string, object> parameters) {
if (parameters == null || !userID.IsSteamId() || !parameters.TryGetValue("Plugin", out var pNameObj) || pNameObj is not string pluginName || !parameters.TryGetValue("Id", out var bIdObj) || bIdObj is not string barId) return;
if (!_playersList.TryGetValue(userID, out var watcher)) {
if (BasePlayer.TryFindByID(userID, out var player)) {
InitPlayer(player);
watcher = _playersList[userID];
}
else return;
}
AdvancedBar bar = null;
for (int i = 0; i < watcher.Bars.Count; i++) {
var tBar = watcher.Bars[i];
if (tBar.Plugin == pluginName && tBar.Id == barId) {
bar = tBar;
break;
}
}
bool isOrder = true;
if (bar != null) bar.Update(parameters, out var isProgress, out var isText, out isOrder);
else {
bar = new AdvancedBar(pluginName, barId, parameters);
watcher.Bars.Add(bar);
}
if (isOrder) SortAdvancedBars(watcher.Bars);
bar.ShouldRemove = false;
if (watcher.CanShowBars) DrawBars(watcher, !watcher.IsUiActive ? 0 : watcher.Bars.IndexOf(bar));
}
private void UpdateContent(object obj, Dictionary<string, object> parameters) => UpdateContent($"{obj}", parameters);
private void UpdateContent(string userIDStr, Dictionary<string, object> parameters) {
if (ulong.TryParse(userIDStr, out var userID)) UpdateContent(userID, parameters);
}
private void UpdateContent(BasePlayer player, Dictionary<string, object> parameters) => UpdateContent(player.userID.Get(), parameters);
private void UpdateContent(ulong userID, Dictionary<string, object> parameters) {
if (parameters == null || !parameters.TryGetValue("Plugin", out var pNameObj) || pNameObj is not string pluginName || !parameters.TryGetValue("Id", out var bIdObj) || bIdObj is not string barId || !_playersList.TryGetValue(userID, out var watcher)) return;
AdvancedBar bar;
bool isOrder = false;
for (int i = 0; i < watcher.Bars.Count; i++) {
bar = watcher.Bars[i];
if (bar.Plugin == pluginName && bar.Id == barId) {
bar.Update(parameters, out var isProgress, out var isText, out isOrder);
if (watcher.IsUiActive) {
if (isProgress) DrawProgress(watcher.Player, bar);
if (isText) DrawText(watcher.Player, bar);
}
break;
}
}
if (isOrder) SortAdvancedBars(watcher.Bars);
}
private void CreateBar(object obj, Dictionary<int, object> parameters) => CreateBar($"{obj}", parameters);
private void CreateBar(string userIDStr, Dictionary<int, object> parameters) {
if (ulong.TryParse(userIDStr, out var userID)) CreateBar(userID, parameters);
}
private void CreateBar(BasePlayer player, Dictionary<int, object> parameters) => CreateBar(player.userID.Get(), parameters);
private void CreateBar(ulong userID, Dictionary<int, object> parameters) {
if (parameters == null || !userID.IsSteamId() || !parameters.TryGetValue(1, out var pNameObj) || pNameObj is not string pluginName || !parameters.TryGetValue(0, out var bIdObj) || bIdObj is not string barId) return;
if (!_playersList.TryGetValue(userID, out var watcher)) {
if (BasePlayer.TryFindByID(userID, out var player)) {
InitPlayer(player);
watcher = _playersList[userID];
}
else return;
}
AdvancedBar bar = null;
for (int i = 0; i < watcher.Bars.Count; i++) {
var tBar = watcher.Bars[i];
if (tBar.Plugin == pluginName && tBar.Id == barId) {
bar = tBar;
break;
}
}
bool isOrder = true;
if (bar != null) bar.Update(parameters, out var isProgress, out var isText, out isOrder);
else {
bar = new AdvancedBar(pluginName, barId, parameters);
watcher.Bars.Add(bar);
}
if (isOrder) SortAdvancedBars(watcher.Bars);
bar.ShouldRemove = false;
if (watcher.CanShowBars) DrawBars(watcher, !watcher.IsUiActive ? 0 : watcher.Bars.IndexOf(bar));
}
private void UpdateContent(object obj, Dictionary<int, object> parameters) => UpdateContent($"{obj}", parameters);
private void UpdateContent(string userIDStr, Dictionary<int, object> parameters) {
if (ulong.TryParse(userIDStr, out var userID)) UpdateContent(userID, parameters);
}
private void UpdateContent(BasePlayer player, Dictionary<int, object> parameters) => UpdateContent(player.userID.Get(), parameters);
private void UpdateContent(ulong userID, Dictionary<int, object> parameters) {
if (parameters == null || !parameters.TryGetValue(1, out var pNameObj) || pNameObj is not string pluginName || !parameters.TryGetValue(0, out var bIdObj) || bIdObj is not string barId || !_playersList.TryGetValue(userID, out var watcher)) return;
AdvancedBar bar;
bool isOrder = false;
for (int i = 0; i < watcher.Bars.Count; i++) {
bar = watcher.Bars[i];
if (bar.Plugin == pluginName && bar.Id == barId) {
bar.Update(parameters, out var isProgress, out var isText, out isOrder);
if (watcher.IsUiActive) {
if (isProgress) DrawProgress(watcher.Player, bar);
if (isText) DrawText(watcher.Player, bar);
}
break;
}
}
if (isOrder) SortAdvancedBars(watcher.Bars);
}
private void DeleteBar(object obj, string barId, string pluginName) => DeleteBar($"{obj}", barId, pluginName);
private void DeleteBar(string userIDStr, string barId, string pluginName) {
if (ulong.TryParse(userIDStr, out var userID)) DeleteBar(userID, barId, pluginName);
}
private void DeleteBar(BasePlayer player, string barId, string pluginName) => DeleteBar(player.userID.Get(), barId, pluginName);
private void DeleteBar(ulong userID, string barId, string pluginName) {
if (!_playersList.TryGetValue(userID, out var watcher)) return;
AdvancedBar bar;
for (int i = 0; i < watcher.Bars.Count; i++) {
bar = watcher.Bars[i];
if (bar.Plugin == pluginName && bar.Id == barId) {
bar.ShouldRemove = true;
break;
}
}
}
private void DeleteBarForAll(string barId, string pluginName) {
AdvancedBar bar;
foreach (var watcher in _playersList.Values) {
for (int i = 0; i < watcher.Bars.Count; i++) {
bar = watcher.Bars[i];
if (bar.Plugin == pluginName && bar.Id == barId) {
bar.ShouldRemove = true;
break;
}
}
}
}
private void DeleteCategory(object obj, string category, string pluginName) => DeleteCategory($"{obj}", category, pluginName);
private void DeleteCategory(string userIDStr, string category, string pluginName) {
if (ulong.TryParse(userIDStr, out var userID)) DeleteCategory(userID, category, pluginName);
}
private void DeleteCategory(BasePlayer player, string category, string pluginName) => DeleteCategory(player.userID.Get(), category, pluginName);
private void DeleteCategory(ulong userID, string category, string pluginName) {
if (!_playersList.TryGetValue(userID, out var watcher)) return;
AdvancedBar bar;
for (int i = 0; i < watcher.Bars.Count; i++) {
bar = watcher.Bars[i];
if (bar.Plugin == pluginName && bar.Category == category) bar.ShouldRemove = true;
}
}
private void DeleteCategoryForAll(string category, string pluginName) {
AdvancedBar bar;
foreach (var watcher in _playersList.Values) {
for (int i = 0; i < watcher.Bars.Count; i++) {
bar = watcher.Bars[i];
if (bar.Plugin == pluginName && bar.Category == category) bar.ShouldRemove = true;
}
}
}
private void DeleteAllBars(object obj, string pluginName) => DeleteAllBars($"{obj}", pluginName);
private void DeleteAllBars(string userIDStr, string pluginName) {
if (ulong.TryParse(userIDStr, out var userID)) DeleteAllBars(userID, pluginName);
}
private void DeleteAllBars(BasePlayer player, string pluginName) => DeleteAllBars(player.userID.Get(), pluginName);
private void DeleteAllBars(ulong userID, string pluginName) {
if (!_playersList.TryGetValue(userID, out var watcher)) return;
AdvancedBar bar;
for (int i = 0; i < watcher.Bars.Count; i++) {
bar = watcher.Bars[i];
if (bar.Plugin == pluginName) bar.ShouldRemove = true;
}
}
private void DeleteAllPluginBars(string pluginName) {
AdvancedBar bar;
foreach (var watcher in _playersList.Values) {
for (int i = 0; i < watcher.Bars.Count; i++) {
bar = watcher.Bars[i];
if (bar.Plugin == pluginName) bar.ShouldRemove = true;
}
}
}
private int GetTotalClientBars(object obj) => GetTotalClientBars($"{obj}");
private int GetTotalClientBars(string userIDStr) => ulong.TryParse(userIDStr, out var userID) ? GetTotalClientBars(userID) : 0;
private int GetTotalClientBars(BasePlayer player) => GetTotalClientBars(player.userID.Get());
private int GetTotalClientBars(ulong userID) => _playersList.TryGetValue(userID, out var watcher) ? Math.Max(watcher.ClientBars, 0) : 0;
private int GetTotalPlayerBars(object obj) => GetTotalPlayerBars($"{obj}");
private int GetTotalPlayerBars(string userIDStr) => ulong.TryParse(userIDStr, out var userID) ? GetTotalPlayerBars(userID) : 0;
private int GetTotalPlayerBars(BasePlayer player) => GetTotalPlayerBars(player.userID.Get());
private int GetTotalPlayerBars(ulong userID) {
int result = 0;
if (_playersList.TryGetValue(userID, out var watcher)) {
for (int i = 0; i < watcher.Bars.Count; i++) {
if (!watcher.Bars[i].ShouldRemove) result++;
}
}
return result;
}
private void LoadImages(List<string> list, bool force = false) {
Dictionary<string, string> images;
if (force) images = list.Where(i => !string.IsNullOrWhiteSpace(i)).ToDictionary(i => i, i => string.Format(ImagePath, i));
else {
string path;
images = new Dictionary<string, string>();
for (int i = 0; i < list.Count; i++) {
var name = list[i];
if (!string.IsNullOrWhiteSpace(name) && !_imagesList.ContainsKey(name)) {
path = string.Format(ImagePath, name);
if (!_imagesList.ContainsKey(name) && File.Exists(path)) images[name] = string.Format(ImagePath, name);
}
}
}
ServerMgr.Instance.StartCoroutine(StoreImages(images));
}
private void LoadImage(string name, bool force = false) {
if (string.IsNullOrWhiteSpace(name) || (!force && _imagesList.ContainsKey(name))) return;
string path = string.Format(ImagePath, name);
if (File.Exists(path)) ServerMgr.Instance.StartCoroutine(StoreImage(name, path));
}
private void CopyImage(string source, string newImage, bool force = false) {
if (string.IsNullOrWhiteSpace(source) || string.IsNullOrWhiteSpace(newImage)) return;
string sourcePath = string.Format(ImagePath, source), newPath = string.Format(ImagePath, newImage);
if (File.Exists(sourcePath) && (force || !File.Exists(newPath))) {
try {
File.Copy(sourcePath, newPath, overwrite : true);
ServerMgr.Instance.StartCoroutine(StoreImage(newImage, newPath));
}
catch {
}
}
}
private void DeleteImages(List<string> list, bool deleteFile = true) {
for (int i = 0; i < list.Count; i++) DeleteImage(list[i], deleteFile);
}
private void DeleteImage(string name, bool deleteFile = true) {
if (string.IsNullOrWhiteSpace(name)) return;
if (_imagesList.TryGetValue(name, out var crcStr)) {
uint crc = uint.Parse(crcStr);
FileStorage.server.Remove(crc, FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);
_imagesList.Remove(name);
}
if (deleteFile) {
try {
File.Delete(string.Format(ImagePath, name));
}
catch {
}
}
}
private bool BarExists(object obj, string barId, string pluginName) => BarExists($"{obj}", barId, pluginName);
private bool BarExists(string userIDStr, string barId, string pluginName) => ulong.TryParse(userIDStr, out var userID) ? BarExists(userID, barId, pluginName) : false;
private bool BarExists(BasePlayer player, string barId, string pluginName) => BarExists(player.userID.Get(), barId, pluginName);
private bool BarExists(ulong userID, string barId, string pluginName) {
if (_playersList.TryGetValue(userID, out var watcher)) {
AdvancedBar bar;
for (int i = 0; i < watcher.Bars.Count; i++) {
bar = watcher.Bars[i];
if (bar.Plugin == pluginName && bar.Id == barId) return true;
}
}
return false;
}
private bool InBuildingPrivilege(object obj) => InBuildingPrivilege($"{obj}");
private bool InBuildingPrivilege(string userIDStr) => ulong.TryParse(userIDStr, out var userID) ? InBuildingPrivilege(userID) : false;
private bool InBuildingPrivilege(BasePlayer player) => InBuildingPrivilege(player.userID.Get());
private bool InBuildingPrivilege(ulong userID) => _playersList.TryGetValue(userID, out var watcher) ? watcher.InOwnBuilding : false;
#endregion
#region ~Oxide Hooks~
void OnPlayerConnected(BasePlayer player) {
if (!_playersList.ContainsKey(player.userID)) InitPlayer(player);
}
void OnPlayerDisconnected(BasePlayer player, string reason) => RemovePlayer(player);
void OnPlayerSleep(BasePlayer player) {
if (_playersList.TryGetValue(player.userID, out var watcher)) watcher.OnDeathOrSleeping();
}
void OnPlayerSleepEnded(BasePlayer player) {
if (_playersList.TryGetValue(player.userID, out var watcher)) {
watcher.IsDeadOrSleeping = false;
watcher.StartCount();
}
}
void OnEntityDeath(BasePlayer player) {
if (_playersList.TryGetValue(player.userID, out var watcher)) watcher.OnDeathOrSleeping();
}
void OnSendCommand(Connection connection, string command, object[] args) {
if (connection == null || !command.StartsWith(NoteInv, StringComparison.OrdinalIgnoreCase) || !_playersList.TryGetValue(connection.userid, out var watcher)) return;
int itemId = 0;
if (!(command == NoteInv && int.TryParse(args[0].ToString(), out itemId))) {
string[] parts = command.Split(" ");
if (parts.Length > 1 && int.TryParse(parts[1], out itemId)) {
}
}
var realtimeSinceStartup = Time.realtimeSinceStartup;
if (!_stackableItems.Contains(itemId)) watcher.AddedItems[$"{itemId}_{realtimeSinceStartup}"] = realtimeSinceStartup + 3.7f;
else watcher.AddedItems[itemId.ToString()] = realtimeSinceStartup + 3.7f;
}
void OnEntityMounted(BaseMountable mount, BasePlayer player) {
if (_playersList.TryGetValue(player.userID, out var watcher)) watcher.OnMount(mount);
}
void OnEntityDismounted(BaseMountable mount, BasePlayer player) {
if (!player.isMounted && _playersList.TryGetValue(player.userID, out var watcher)) watcher.MountCount = 0;
}
void OnEntityMounted(ComputerStation computerStation, BasePlayer player) {
if (_playersList.TryGetValue(player.userID, out var watcher)) watcher.OnVanillaUi();
}
void OnEntityDismounted(ComputerStation computerStation, BasePlayer player) {
if (_playersList.TryGetValue(player.userID, out var watcher)) watcher.OnVanillaUi(false);
}
void OnNpcConversationStart(NPCTalking npcTalking, BasePlayer player, ConversationData conversationData) {
NextTick(() => {
//npcTalking.conversingPlayers.Contains(player) - Not the best solution, as the conversation UI will display the status bar UI.if (npcTalking.conversingPlayers.Contains(player) && _playersList.TryGetValue(player.userID, out var watcher))
watcher.OnVanillaUi();
}
);
}
void OnNpcConversationEnded(NPCTalking npcTalking, BasePlayer player) {
if (_playersList.TryGetValue(player.userID, out var watcher)) watcher.OnVanillaUi(false);
}
void OnPluginUnloaded(Plugin plugin) => DeleteAllPluginBars(plugin.Name);
void Init() {
Instance = this;
Unsubscribe(nameof(OnPlayerConnected));
Unsubscribe(nameof(OnPlayerDisconnected));
Unsubscribe(nameof(OnPlayerSleep));
Unsubscribe(nameof(OnPlayerSleepEnded));
Unsubscribe(nameof(OnEntityDeath));
Unsubscribe(nameof(OnSendCommand));
Unsubscribe(nameof(OnEntityMounted));
Unsubscribe(nameof(OnEntityDismounted));
Unsubscribe(nameof(OnNpcConv
// ... [Code preview truncated due to length. Full source in attached file] ...