C#:
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Kits", "k1lly0u", "4.4.10"), Description("Create kits containing items that players can redeem")]
class Kits : RustPlugin
{
#region Fields
[PluginReference]
private Plugin CopyPaste, ImageLibrary, ServerRewards, Economics, PlayerDLCAPI;
private DateTime _deprecatedHookTime = new DateTime(2021, 12, 31);
private Hash<ulong, KitData.Kit> _kitCreators = new Hash<ulong, KitData.Kit>();
private static Func<BasePlayer, ulong, bool> _canUseSkin;
private static Func<BasePlayer, int, int> _getRedirectedIfNotOwned;
private const string ADMIN_PERMISSION = "kits.admin";
private const string BLUEPRINT_BASE = "blueprintbase";
#endregion
#region Oxide Hooks
private void Loaded()
{
_canUseSkin = CanUseSkin;
_getRedirectedIfNotOwned = GetRedirectedItemIdIfNotOwned;
LoadData();
permission.RegisterPermission(ADMIN_PERMISSION, this);
kitData.RegisterPermissions(permission, this);
_costType = ParseType<CostType>(Configuration.Currency);
cmd.AddChatCommand(Configuration.Command, this, cmdKit);
cmd.AddConsoleCommand(Configuration.Command, this, "ccmdKit");
}
protected override void LoadDefaultMessages() => lang.RegisterMessages(Messages, this);
private void OnServerInitialized()
{
LastWipeTime = SaveRestore.SaveCreatedTime.Subtract(Epoch).TotalSeconds;
kitData.RegisterImages(ImageLibrary);
CheckForShortnameUpdates();
if (Configuration.AutoKits.Count == 0)
Unsubscribe(nameof(OnPlayerRespawned));
if (!Configuration.OwnedSkins)
Debug.LogWarning("[Kits] WARNING! As of August 7th 2025, granting access to paid skins that users do not own is against Rust's Terms of Service and can result in your server being delisted or worse.\n" +
"If you continue to allow users to use paid skins, you do so at your own risk!\n" +
"You can prevent users access to skins they do not own by enabling 'Only show/give players skins that they are allowed to use' in the config\n" +
"https://facepunch.com/legal/servers");
else if (!PlayerDLCAPI)
Debug.LogWarning("[Kits] - PlayerDLCAPI plugin is not loaded, skin ownership checks will not work!");
}
private void OnNewSave(string filename)
{
if (Configuration.WipeData)
playerData.Wipe();
}
private void OnServerSave() => SavePlayerData();
private void OnPlayerRespawned(BasePlayer player)
{
if (!player)
return;
if ((Interface.Oxide.CallDeprecatedHook("canRedeemKit", "CanRedeemKit", _deprecatedHookTime, player) ?? Interface.Oxide.CallHook("CanRedeemKit", player)) != null)
return;
if (Interface.Oxide.CallHook("CanRedeemAutoKit", player) != null)
return;
if (Configuration.AllowAutoToggle && !playerData[player.userID].ClaimAutoKits)
{
player.ChatMessage(Message("Error.AutoKitDisabled", player.userID));
return;
}
for (int i = 0; i < Configuration.AutoKits.Count; i++)
{
if (!kitData.Find(Configuration.AutoKits[i], out KitData.Kit kit))
continue;
object success = CanClaimKit(player, kit, true);
if (success != null)
continue;
player.inventory.Strip();
success = GiveKit(player, kit);
if (success is string)
continue;
OnKitReceived(player, kit);
return;
}
}
private void Unload()
{
if (!Interface.Oxide.IsShuttingDown)
SavePlayerData();
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, UI_MENU);
CuiHelper.DestroyUi(player, UI_POPUP);
}
Configuration = null;
}
#endregion
#region Kit Claiming
private bool TryClaimKit(BasePlayer player, string name, bool usingUI)
{
if (string.IsNullOrEmpty(name))
{
if (usingUI)
CreateMenuPopup(player, Message("Error.EmptyKitName", player.userID));
else player.ChatMessage(Message("Error.EmptyKitName", player.userID));
return false;
}
if (!kitData.Find(name, out KitData.Kit kit))
{
if (usingUI)
CreateMenuPopup(player, Message("Error.InvalidKitName", player.userID));
else player.ChatMessage(Message("Error.InvalidKitName", player.userID));
return false;
}
object success = CanClaimKit(player, kit) ?? GiveKit(player, kit);
if (success is string s)
{
if (usingUI)
CreateMenuPopup(player, s);
else player.ChatMessage(s);
return false;
}
OnKitReceived(player, kit);
return true;
}
private object CanClaimKit(BasePlayer player, KitData.Kit kit, bool ignoreAuthCost = false)
{
object success = Interface.Oxide.CallDeprecatedHook("canRedeemKit", "CanRedeemKit", _deprecatedHookTime, player) ?? Interface.Oxide.CallHook("CanRedeemKit", player);
if (success != null)
{
if (success is string s)
return s;
return Message("Error.CantClaimNow", player.userID);
}
if (!ignoreAuthCost && kit.RequiredAuth > 0 && player.net.connection.authLevel < kit.RequiredAuth)
return Message("Error.CanClaim.Auth", player.userID);
if (Configuration.AdminIgnoreRestrictions && IsAdmin(player))
{
if (!kit.HasSpaceForItems(player))
return Message("Error.CanClaim.InventorySpace", player.userID);
return null;
}
if (!string.IsNullOrEmpty(kit.RequiredPermission) && !permission.UserHasPermission(player.UserIDString, kit.RequiredPermission))
return Message("Error.CanClaim.Permission", player.userID);
if (Configuration.WipeCooldowns.TryGetValue(kit.Name, out int wipeCooldownTime))
{
if (kitData.IsOnWipeCooldown(wipeCooldownTime, out wipeCooldownTime))
return string.Format(Message("Error.CanClaim.WipeCooldown", player.userID), FormatTime(wipeCooldownTime));
}
if (playerData.Find(player.userID, out PlayerData.PlayerUsageData playerUsageData))
{
if (kit.Cooldown > 0)
{
double cooldownRemaining = playerUsageData.GetCooldownRemaining(kit.Name);
if (cooldownRemaining > 0)
return string.Format(Message("Error.CanClaim.Cooldown", player.userID), FormatTime(cooldownRemaining));
}
if (kit.MaximumUses > 0)
{
int currentUses = playerUsageData.GetKitUses(kit.Name);
if (currentUses >= kit.MaximumUses)
return Message("Error.CanClaim.MaxUses", player.userID);
}
}
if (!kit.HasSpaceForItems(player))
return Message("Error.CanClaim.InventorySpace", player.userID);
if (!ignoreAuthCost && kit.Cost > 0)
{
if (!ChargePlayer(player, kit.Cost))
return string.Format(Message("Error.CanClaim.InsufficientFunds", player.userID), kit.Cost, Message($"Cost.{_costType}", player.userID));
}
return null;
}
private object GiveKit(BasePlayer player, KitData.Kit kit)
{
if (!string.IsNullOrEmpty(kit.CopyPasteFile))
{
object success = CopyPaste?.CallHook("TryPasteFromSteamId", player.userID, kit.CopyPasteFile, Configuration.CopyPasteParams, null);
if (success != null)
return success;
}
kit.GiveItemsTo(player);
return true;
}
private void OnKitReceived(BasePlayer player, KitData.Kit kit)
{
playerData[player.userID].OnKitClaimed(kit);
Interface.CallHook("OnKitRedeemed", player, kit.Name);
if (Configuration.LogKitsGiven)
LogToFile("Kits_Received", $"{player.displayName} ({player.userID}) - Received {kit.Name}", this);
}
#endregion
#region Purchase Costs
private CostType _costType;
private enum CostType { Scrap, ServerRewards, Economics }
private const int SCRAP_ITEM_ID = -932201673;
private bool ChargePlayer(BasePlayer player, int amount)
{
if (amount == 0)
return true;
switch (_costType)
{
case CostType.Scrap:
if (amount <= player.inventory.GetAmount(SCRAP_ITEM_ID))
{
player.inventory.Take(null, SCRAP_ITEM_ID, amount);
return true;
}
return false;
case CostType.ServerRewards:
{
if ((ServerRewards?.Call<int>("CheckPoints", player.UserIDString) ?? 0) < amount)
return false;
return (bool)ServerRewards?.Call("TakePoints", player.userID, amount);
}
case CostType.Economics:
return (bool)Economics?.Call("Withdraw", player.UserIDString, (double)amount);
}
return false;
}
#endregion
#region Helpers
private T ParseType<T>(string type)
{
try
{
return (T)Enum.Parse(typeof(T), type, true);
}
catch
{
return default(T);
}
}
private string FormatTime(double time)
{
TimeSpan dateDifference = TimeSpan.FromSeconds(time);
int days = dateDifference.Days;
int hours = dateDifference.Hours;
int mins = dateDifference.Minutes;
int secs = dateDifference.Seconds;
if (days > 0)
return $"~{days:00}d:{hours:00}h";
if (hours > 0)
return $"~{hours:00}h:{mins:00}m";
return mins > 0 ? $"{mins:00}m:{secs:00}s" : $"{secs}s";
}
private void GetUserValidKits(BasePlayer player, List<KitData.Kit> list, ulong npcId = 0UL)
{
bool isAdmin = IsAdmin(player);
bool viewPermissionKits = Configuration.ShowPermissionKits;
if (npcId != 0UL)
{
if (Configuration.NPCKitMenu.TryGetValue(npcId, out ConfigData.NPCKit npcKit))
{
npcKit.Kits.ForEach(kitName =>
{
if (kitData.Find(kitName, out KitData.Kit kit))
{
if (!viewPermissionKits && !string.IsNullOrEmpty(kit.RequiredPermission) && !permission.UserHasPermission(player.UserIDString, kit.RequiredPermission) && !isAdmin)
return;
if (player.net.connection.authLevel < kit.RequiredAuth)
return;
list.Add(kit);
}
});
}
}
else
{
kitData.ForEach(kit =>
{
if (kit.IsHidden && !isAdmin)
return;
if (!viewPermissionKits && !string.IsNullOrEmpty(kit.RequiredPermission) && !permission.UserHasPermission(player.UserIDString, kit.RequiredPermission) && !isAdmin)
return;
if (player.net.connection.authLevel < kit.RequiredAuth)
return;
list.Add(kit);
});
}
}
private bool IsAdmin(BasePlayer player) => permission.UserHasPermission(player.UserIDString, ADMIN_PERMISSION);
private BasePlayer FindPlayer(string partialNameOrID) => BasePlayer.allPlayerList.FirstOrDefault(x => x.UserIDString.Equals(partialNameOrID) ||
x.displayName.Equals(partialNameOrID, StringComparison.OrdinalIgnoreCase) ||
x.displayName.Contains(partialNameOrID, CompareOptions.OrdinalIgnoreCase));
private BasePlayer RaycastPlayer(BasePlayer player)
{
if (!Physics.Raycast(new Ray(player.eyes.position, Quaternion.Euler(player.serverInput.current.aimAngles) * Vector3.forward), out RaycastHit raycastHit, 5f))
return null;
return raycastHit.collider.GetComponentInParent<BasePlayer>();
}
private bool CanUseSkin(BasePlayer player, ulong skinID)
{
if (skinID == 0UL)
return true;
if (Configuration.NPCSkins && (player.IsNpc || !player.userID.IsSteamId()))
return Configuration.NPCSkins;
if (PlayerDLCAPI != null && Configuration.OwnedSkins)
return PlayerDLCAPI.Call<bool>("IsOwnedOrFreeSkin", player, skinID);
return true;
}
private int GetRedirectedItemIdIfNotOwned(BasePlayer player, int itemId)
{
if (Configuration.NPCSkins && (player.IsNpc || !player.userID.IsSteamId()))
return itemId;
if (PlayerDLCAPI != null && Configuration.OwnedSkins)
return PlayerDLCAPI.Call<int>("GetRedirectedItemIdIfNotOwned", player, itemId);
return itemId;
}
private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0);
private static double LastWipeTime;
private static double CurrentTime => DateTime.UtcNow.Subtract(Epoch).TotalSeconds;
#endregion
#region ImageLibrary
private void RegisterImage(string name, string url) => ImageLibrary?.Call("AddImage", url, name.Replace(" ", ""), 0UL, null);
private string GetImage(string name, ulong skinId = 0UL) => ImageLibrary?.Call<string>("GetImage", name.Replace(" ", ""), skinId, false);
#endregion
#region HumanNPC
private void OnUseNPC(BasePlayer npcPlayer, BasePlayer player)
{
if (Configuration.NPCKitMenu.ContainsKey(npcPlayer.userID))
OpenKitGrid(player, 0, npcPlayer.userID);
}
#endregion
#region Deprecated API
[HookMethod("isKit")]
public bool isKit(string name) => IsKit(name);
[HookMethod("GetAllKits")]
public string[] GetAllKits() => kitData.Keys.ToArray();
[HookMethod("KitImage")]
public string KitImage(string name) => GetKitImage(name);
[HookMethod("KitDescription")]
public string KitDescription(string name) => GetKitDescription(name);
[HookMethod("KitMax")]
public int KitMax(string name) => GetKitMaxUses(name);
[HookMethod("KitCooldown")]
public double KitCooldown(string name) => GetKitCooldown(name);
[HookMethod("PlayerKitMax")]
public int PlayerKitMax(ulong playerId, string name) => GetPlayerKitUses(playerId, name);
[HookMethod("PlayerKitCooldown")]
public double PlayerKitCooldown(ulong playerId, string name) => GetPlayerKitCooldown(playerId, name);
[HookMethod("GetKitContents")]
public string[] GetKitContents(string name)
{
if (!kitData.Find(name, out KitData.Kit kit))
return null;
List<string> items = Pool.Get<List<string>>();
void Append(List<string> items, ItemData[] itemData)
{
for (int i = 0; i < itemData.Length; i++)
{
ItemData item = itemData[i];
string itemstring = $"{item.ItemID}_{item.Amount}";
if (item.Contents is { Length: > 0 })
{
for (int j = 0; j < item.Contents.Length; j++)
itemstring += $"_{item.Contents[j].ItemID}";
}
if (item.Container != null && item.Container.contents.Count > 0)
{
for (int j = 0; j < item.Container.contents.Count; j++)
itemstring += $"_{item.Container.contents[j].ItemID}";
}
items.Add(itemstring);
}
}
Append(items, kit.BeltItems);
Append(items, kit.MainItems);
Append(items, kit.WearItems);
string[] array = items.ToArray();
Pool.FreeUnmanaged(ref items);
return array;
}
[HookMethod("GetKitInfo")]
public object GetKitInfo(string name)
{
if (kitData.Find(name, out KitData.Kit kit))
{
JObject obj = new JObject
{
["name"] = kit.Name,
["permission"] = kit.RequiredPermission,
["max"] = kit.MaximumUses,
["image"] = kit.KitImage,
["hide"] = kit.IsHidden,
["description"] = kit.Description,
["cooldown"] = kit.Cooldown,
["building"] = kit.CopyPasteFile,
["authlevel"] = kit.RequiredAuth
};
JArray array = new JArray();
GetItemObject_Old(ref array, kit.BeltItems, "belt");
GetItemObject_Old(ref array, kit.MainItems, "main");
GetItemObject_Old(ref array, kit.WearItems, "wear");
obj["items"] = array;
return obj;
}
return null;
}
private void GetItemObject_Old(ref JArray array, ItemData[] items, string container)
{
for (int i = 0; i < items.Length; i++)
{
ItemData itemData = items[i];
JObject item = new JObject
{
["amount"] = itemData.Amount,
["container"] = container,
["itemid"] = itemData.ItemID,
["skinid"] = itemData.Skin,
["weapon"] = !string.IsNullOrEmpty(itemData.Ammotype),
["blueprint"] = itemData.BlueprintItemID
};
item["mods"] = new JArray();
if (itemData.Contents is { Length: > 0 })
{
for (int i1 = 0; i1 < itemData.Contents?.Length; i1++)
(item["mods"] as JArray).Add(itemData.Contents[i1].ItemID);
}
if (itemData.Container != null && itemData.Container.contents.Count > 0)
{
for (int i3 = 0; i3 < itemData.Container.contents.Count; i3++)
(item["mods"] as JArray).Add(itemData.Container.contents[i3].ItemID);
}
array.Add(item);
}
}
#endregion
#region API
[HookMethod("GiveKit")]
public object GiveKit(BasePlayer player, string name)
{
if (!player)
return null;
if (string.IsNullOrEmpty(name))
return Message("Error.EmptyKitName", player.userID);
if (!kitData.Find(name, out KitData.Kit kit))
return Message("Error.InvalidKitName", player.userID);
return GiveKit(player, kit);
}
[HookMethod("IsKit")]
public bool IsKit(string name) => !string.IsNullOrEmpty(name) && kitData.Exists(name);
[HookMethod("GetKitNames")]
public void GetKitNames(List<string> list) => list.AddRange(kitData.Keys);
[HookMethod("GetKitImage")]
public string GetKitImage(string name) => kitData[name]?.KitImage ?? string.Empty;
[HookMethod("GetKitDescription")]
public string GetKitDescription(string name) => kitData[name]?.Description ?? string.Empty;
[HookMethod("GetKitMaxUses")]
public int GetKitMaxUses(string name) => kitData[name]?.MaximumUses ?? 0;
[HookMethod("GetKitCooldown")]
public int GetKitCooldown(string name) => kitData[name]?.Cooldown ?? 0;
[HookMethod("GetPlayerKitUses")]
public int GetPlayerKitUses(ulong playerId, string name) => playerData.Exists(playerId) ? playerData[playerId].GetKitUses(name) : 0;
[HookMethod("SetPlayerKitUses")]
public void SetPlayerKitUses(ulong playerId, string name, int amount)
{
if (playerData.Exists(playerId))
playerData[playerId].SetKitUses(name, amount);
}
[HookMethod("GetPlayerKitCooldown")]
public double GetPlayerKitCooldown(ulong playerId, string name) => playerData.Exists(playerId) ? playerData[playerId].GetCooldownRemaining(name) : 0;
[HookMethod("SetPlayerKitCooldown")]
public void SetPlayerCooldown(ulong playerId, string name, double seconds)
{
if (playerData.Exists(playerId))
playerData[playerId].SetCooldownRemaining(name, seconds);
}
[HookMethod("GetKitObject")]
public JObject GetKitObject(string name)
{
if (!kitData.Find(name, out KitData.Kit kit))
return null;
return kit.ToJObject;
}
[HookMethod("CreateKitItems")]
public IEnumerable<Item> CreateKitItems(string name)
{
if (!kitData.Find(name, out KitData.Kit kit))
yield break;
foreach (Item item in kit.CreateItems())
yield return item;
}
#endregion
#region UI
private const string UI_MENU = "kits.menu";
private const string UI_POPUP = "kits.popup";
private const string DEFAULT_ICON = "kits.defaultkiticon";
private const string MAGNIFY_ICON = "kits.magnifyicon";
#region Kit Grid View
private void OpenKitGrid(BasePlayer player, int page = 0, ulong npcId = 0UL)
{
CuiElementContainer container = UI.Container(UI_MENU, "0 0 0 0.9", new UI4(0.2f, 0.15f, 0.8f, 0.85f), true, "Hud");
UI.Panel(container, UI_MENU, Configuration.Menu.Panel.Get, new UI4(0.005f, 0.93f, 0.995f, 0.99f));
UI.Label(container, UI_MENU, Message("UI.Title", player.userID), 20, new UI4(0.015f, 0.93f, 0.99f, 0.99f), TextAnchor.MiddleLeft);
UI.Button(container, UI_MENU, Configuration.Menu.Color3.Get, "<b>×</b>", 20, new UI4(0.9575f, 0.9375f, 0.99f, 0.9825f), "kits.close");
if (IsAdmin(player) && npcId == 0UL)
UI.Button(container, UI_MENU, Configuration.Menu.Color2.Get, Message("UI.CreateNew", player.userID), 14, new UI4(0.85f, 0.9375f, 0.9525f, 0.9825f), "kits.create");
CreateGridView(player, container, page, npcId);
CuiHelper.DestroyUi(player, UI_MENU);
CuiHelper.AddUi(player, container);
}
private void CreateGridView(BasePlayer player, CuiElementContainer container, int page = 0, ulong npcId = 0UL)
{
List<KitData.Kit> list = Pool.Get<List<KitData.Kit>>();
GetUserValidKits(player, list, npcId);
if (list.Count == 0)
{
UI.Label(container, UI_MENU, Message("UI.NoKitsAvailable", player.userID), 14, new UI4(0.015f, 0.88f, 0.99f, 0.92f), TextAnchor.MiddleLeft);
return;
}
PlayerData.PlayerUsageData playerUsageData = playerData[player.userID];
int max = Mathf.Min(list.Count, (page + 1) * 8);
int count = 0;
for (int i = page * 8; i < max; i++)
{
CreateKitEntry(player, playerUsageData, container, list[i], count, page, npcId);
count += 1;
}
if (page > 0)
UI.Button(container, UI_MENU, Configuration.Menu.Color1.Get, "◀\n\n◀\n\n◀", 16, new UI4(0.005f, 0.35f, 0.03f, 0.58f), $"kits.gridview page {page - 1} {npcId}");
if (max < list.Count)
UI.Button(container, UI_MENU, Configuration.Menu.Color1.Get, "▶\n\n▶\n\n▶", 16, new UI4(0.97f, 0.35f, 0.995f, 0.58f), $"kits.gridview page {page + 1} {npcId}");
Pool.FreeUnmanaged(ref list);
}
private void CreateKitEntry(BasePlayer player, PlayerData.PlayerUsageData playerUsageData, CuiElementContainer container, KitData.Kit kit, int index, int page, ulong npcId)
{
UI4 position = KitAlign.Get(index);
UI.Panel(container, UI_MENU, Configuration.Menu.Color4.Get, new UI4(position.xMin, position.yMax, position.xMax, position.yMax + 0.04f));
UI.Label(container, UI_MENU, kit.Name, 14, new UI4(position.xMin, position.yMax, position.xMax, position.yMax + 0.04f));
UI.Panel(container, UI_MENU, Configuration.Menu.Panel.Get, position);
string imageId = string.IsNullOrEmpty(kit.KitImage) ? GetImage(DEFAULT_ICON) : GetImage(kit.Name);
UI.Image(container, UI_MENU, imageId, new UI4(position.xMin + 0.005f, position.yMax - 0.3f, position.xMax - 0.005f, position.yMax - 0.0075f));
UI.Button(container, UI_MENU, "0 0 0 0", string.Empty, 0, new UI4(position.xMin + 0.005f, position.yMax - 0.3f, position.xMax - 0.005f, position.yMax - 0.0075f), $"kits.gridview inspect {CommandSafe(kit.Name)} {page} {npcId}");
string buttonText;
string buttonCommand = string.Empty;
string buttonColor;
double cooldown = playerUsageData.GetCooldownRemaining(kit.Name);
int currentUses = playerUsageData.GetKitUses(kit.Name);
if (Configuration.AdminIgnoreRestrictions && IsAdmin(player))
{
buttonText = Message("UI.Redeem", player.userID);
buttonColor = Configuration.Menu.Color2.Get;
buttonCommand = $"kits.gridview redeem {CommandSafe(kit.Name)} {page} {npcId}";
}
else
{
if (!string.IsNullOrEmpty(kit.RequiredPermission) && !permission.UserHasPermission(player.UserIDString, kit.RequiredPermission))
{
buttonText = Message("UI.NeedsPermission", player.userID);
buttonColor = Configuration.Menu.Disabled.Get;
}
else if (kit.Cooldown > 0 && cooldown > 0)
{
UI.Label(container, UI_MENU, string.Format(Message("UI.Cooldown", player.userID), FormatTime(cooldown)), 12,
new UI4(position.xMin + 0.005f, position.yMin + 0.0475f, position.xMax - 0.005f, position.yMax - 0.3f), TextAnchor.MiddleLeft);
buttonText = Message("UI.OnCooldown", player.userID);
buttonColor = Configuration.Menu.Disabled.Get;
}
else if (kit.MaximumUses > 0 && currentUses >= kit.MaximumUses)
{
buttonText = Message("UI.MaximumUses", player.userID);
buttonColor = Configuration.Menu.Disabled.Get;
}
else if (kit.Cost > 0)
{
UI.Label(container, UI_MENU, string.Format(Message("UI.Cost", player.userID), kit.Cost, Message($"Cost.{_costType}", player.userID)), 12,
new UI4(position.xMin + 0.005f, position.yMin + 0.0475f, position.xMax - 0.005f, position.yMax - 0.3f), TextAnchor.MiddleLeft);
buttonText = Message("UI.Purchase", player.userID);
buttonColor = Configuration.Menu.Color2.Get;
buttonCommand = $"kits.gridview redeem {CommandSafe(kit.Name)} {page} {npcId}";
}
else
{
buttonText = Message("UI.Redeem", player.userID);
buttonColor = Configuration.Menu.Color2.Get;
buttonCommand = $"kits.gridview redeem {CommandSafe(kit.Name)} {page} {npcId}";
}
}
UI.Button(container, UI_MENU, buttonColor, buttonText, 14,
new UI4(position.xMin + 0.038f, position.yMin + 0.0075f, position.xMax - 0.005f, position.yMin + 0.0475f), buttonCommand);
UI.Button(container, UI_MENU, ICON_BACKGROUND_COLOR, GetImage(MAGNIFY_ICON),
new UI4(position.xMin + 0.005f, position.yMin + 0.0075f, position.xMin + 0.033f, position.yMin + 0.0475f), $"kits.gridview inspect {CommandSafe(kit.Name)} {page} {npcId}");
}
#endregion
#region Kit View
private void OpenKitView(BasePlayer player, string name, int page, ulong npcId)
{
if (!kitData.Find(name, out KitData.Kit kit))
{
OpenKitGrid(player);
return;
}
CuiElementContainer container = UI.Container(UI_MENU, "0 0 0 0.9", new UI4(0.2f, 0.15f, 0.8f, 0.85f), true, "Hud");
UI.Panel(container, UI_MENU, Configuration.Menu.Panel.Get, new UI4(0.005f, 0.93f, 0.995f, 0.99f));
UI.Label(container, UI_MENU, $"{Message("UI.Title", player.userID)} - {name}", 20, new UI4(0.015f, 0.93f, 0.99f, 0.99f), TextAnchor.MiddleLeft);
UI.Button(container, UI_MENU, Configuration.Menu.Color3.Get, "<b>×</b>", 20, new UI4(0.9575f, 0.9375f, 0.99f, 0.9825f), $"kits.gridview page 0 {npcId}");
bool isAdmin = IsAdmin(player);
if (isAdmin)
{
UI.Button(container, UI_MENU, Configuration.Menu.Color2.Get, Message("UI.EditKit", player.userID), 14, new UI4(0.7525f, 0.9375f, 0.845f, 0.9825f), $"kits.edit {CommandSafe(name)}");
UI.Button(container, UI_MENU, Configuration.Menu.Color2.Get, Message("UI.CreateNew", player.userID), 14, new UI4(0.85f, 0.9375f, 0.9525f, 0.9825f), "kits.create");
}
PlayerData.PlayerUsageData playerUsageData = playerData[player.userID];
int i = -1;
if (!string.IsNullOrEmpty(kit.KitImage))
{
UI.Image(container, UI_MENU, GetImage(kit.Name), new UI4(0.15f, 0.62f, 0.35f, 0.92f));
i = 6;
}
AddTitleSperator(container, i += 1, Message("UI.Details", player.userID));
AddLabelField(container, i += 1, Message("UI.Name", player.userID), kit.Name);
if (!string.IsNullOrEmpty(kit.Description))
{
int descriptionSlots = Mathf.Min(Mathf.CeilToInt((kit.Description.Length / 38f) / 1.25f), 4);
AddLabelField(container, i += 1, Message("UI.Description", player.userID), kit.Description, descriptionSlots - 1);
i += descriptionSlots - 1;
}
string buttonText = string.Empty;
string buttonCommand = string.Empty;
string buttonColor = string.Empty;
if (kit.Cooldown != 0 || kit.MaximumUses != 0 || kit.Cost != 0)
{
AddTitleSperator(container, i += 1, Message("UI.Usage", player.userID));
if (kit.MaximumUses != 0)
{
int playerUses = playerUsageData.GetKitUses(kit.Name);
AddLabelField(container, i += 1, Message("UI.MaxUses", player.userID), kit.MaximumUses.ToString());
AddLabelField(container, i += 1, Message("UI.YourUses", player.userID), playerUses.ToString());
if (playerUses >= kit.MaximumUses)
{
buttonText = Message("UI.MaximumUses", player.userID);
buttonColor = Configuration.Menu.Disabled.Get;
}
}
if (kit.Cooldown != 0)
{
double cooldownRemaining = playerUsageData.GetCooldownRemaining(kit.Name);
AddLabelField(container, i += 1, Message("UI.CooldownTime", player.userID), FormatTime(kit.Cooldown));
AddLabelField(container, i += 1, Message("UI.CooldownRemaining", player.userID), cooldownRemaining == 0 ? Message("UI.None", player.userID) :
FormatTime(cooldownRemaining));
if (string.IsNullOrEmpty(buttonText) && cooldownRemaining > 0)
{
buttonText = Message("UI.OnCooldown", player.userID);
buttonColor = Configuration.Menu.Disabled.Get;
}
}
if (kit.Cost != 0)
{
AddLabelField(container, i += 1, Message("UI.PurchaseCost", player.userID), $"{kit.Cost} {(Message($"Cost.{_costType}", player.userID))}");
if (string.IsNullOrEmpty(buttonText))
{
buttonText = Message("UI.Purchase", player.userID);
buttonColor = Configuration.Menu.Color2.Get;
buttonCommand = $"kits.gridview redeem {CommandSafe(kit.Name)} {page} {npcId}";
}
}
}
if (!string.IsNullOrEmpty(kit.RequiredPermission) && !permission.UserHasPermission(player.UserIDString, kit.RequiredPermission))
{
buttonText = Message("UI.NeedsPermission", player.userID);
buttonColor = Configuration.Menu.Disabled.Get;
}
if (i <= 16 && !string.IsNullOrEmpty(kit.CopyPasteFile))
{
AddTitleSperator(container, i += 1, Message("UI.CopyPaste", player.userID));
AddLabelField(container, i += 1, Message("UI.FileName", player.userID), kit.CopyPasteFile);
}
if ((Configuration.AdminIgnoreRestrictions && isAdmin) || string.IsNullOrEmpty(buttonText))
{
buttonText = Message("UI.Redeem", player.userID);
buttonCommand = $"kits.gridview redeem {CommandSafe(kit.Name)} {page} {npcId}";
buttonColor = Configuration.Menu.Color2.Get;
}
CreateKitLayout(player, container, kit);
UI.Button(container, UI_MENU, buttonColor, buttonText, 14, new UI4(0.005f, 0.005f, 0.495f, 0.045f), buttonCommand);
CuiHelper.DestroyUi(player, UI_MENU);
CuiHelper.AddUi(player, container);
}
#endregion
#region Kit Layout
private const string ICON_BACKGROUND_COLOR = "1 1 1 0.15";
private void CreateKitLayout(BasePlayer player, CuiElementContainer container, KitData.Kit kit)
{
UI.Panel(container, UI_MENU, Configuration.Menu.Color1.Get, new UI4(0.505f, 0.88f, 0.995f, 0.92f));
UI.Label(container, UI_MENU, Message("UI.KitItems", player.userID), 14, new UI4(0.51f, 0.88f, 0.995f, 0.92f), TextAnchor.MiddleLeft);
// Main Items
UI.Panel(container, UI_MENU, Configuration.Menu.Color4.Get, new UI4(0.505f, 0.835f, 0.995f, 0.875f));
UI.Label(container, UI_MENU, Message("UI.MainItems", player.userID), 14, new UI4(0.51f, 0.835f, 0.995f, 0.875f), TextAnchor.MiddleLeft);
CreateInventoryItems(player, container, MainAlign, kit.MainItems, 24);
// Wear Items
UI.Panel(container, UI_MENU, Configuration.Menu.Color4.Get, new UI4(0.505f, 0.365f, 0.995f, 0.405f));
UI.Label(container, UI_MENU, Message("UI.WearItems", player.userID), 14, new UI4(0.51f, 0.365f, 0.995f, 0.405f), TextAnchor.MiddleLeft);
CreateInventoryItems(player, container, WearAlign, kit.WearItems, 8);
/*// Backpack slot
UI.Panel(container, UI_MENU, ICON_BACKGROUND_COLOR, new UI4(0.97f, 0.3675f, 0.9925f, 0.4025f));
ItemData itemData = kit.GetBackpackSlot();
if (itemData != null)
UI.Image(container, UI_MENU, itemData.ItemID, itemData.Skin, new UI4(0.97f, 0.3675f, 0.9925f, 0.4025f));*/
// Belt Items
UI.Panel(container, UI_MENU, Configuration.Menu.Color4.Get, new UI4(0.505f, 0.2225f, 0.995f, 0.2625f));
UI.Label(container, UI_MENU, Message("UI.BeltItems", player.userID), 14, new UI4(0.51f, 0.2225f, 0.995f, 0.2625f), TextAnchor.MiddleLeft);
CreateInventoryItems(player, container, BeltAlign, kit.BeltItems, 6);
}
#region Item Layout Helpers
private void CreateInventoryItems(BasePlayer player, CuiElementContainer container, GridAlignment alignment, ItemData[] items, int capacity)
{
for (int i = 0; i < capacity; i++)
UI.Panel(container, UI_MENU, ICON_BACKGROUND_COLOR, alignment.Get(i));
for (int i = 0; i < items.Length; i++)
{
ItemData itemData = items[i];
if (itemData.Position > capacity - 1)
continue;
int itemId = GetRedirectedItemIdIfNotOwned(player, itemData.ItemID);
ulong skinId = CanUseSkin(player, itemData.Skin) ? itemData.Skin : 0UL;
UI4 position = alignment.Get(itemData.Position);
UI.Image(container, UI_MENU, itemId, skinId, position);
if (itemData.IsBlueprint && !string.IsNullOrEmpty(itemData.BlueprintShortname))
UI.Image(container, UI_MENU, itemData.BlueprintItemID, 0UL, position);
if (itemData.Amount > 1)
UI.Label(container, UI_MENU, $"x{itemData.Amount}", 10, position, TextAnchor.LowerRight);
}
}
#endregion
#endregion
#region Kit Editor
private void OpenKitsEditor(BasePlayer player, bool overwrite = false)
{
if (!_kitCreators.TryGetValue(player.userID, out KitData.Kit kit))
return;
CuiElementContainer container = UI.Container(UI_MENU, "0 0 0 0.9", new UI4(0.2f, 0.15f, 0.8f, 0.85f), true, "Hud");
UI.Panel(container, UI_MENU, Configuration.Menu.Panel.Get, new UI4(0.005f, 0.93f, 0.995f, 0.99f));
UI.Label(container, UI_MENU, Message("UI.Title.Editor", player.userID), 20, new UI4(0.015f, 0.93f, 0.99f, 0.99f), TextAnchor.MiddleLeft);
UI.Button(container, UI_MENU, Configuration.Menu.Color3.Get, "<b>×</b>", 20, new UI4(0.9575f, 0.9375f, 0.99f, 0.9825f), "kits.close");
// Kit Options
AddTitleSperator(container, 0, Message("UI.Details", player.userID));
AddInputField(container, 1, Message("UI.Name", player.userID), "name", kit.Name);
AddInputField(container, 2, Message("UI.Description", player.userID), "description", kit.Description, 3);
AddInputField(container, 6, Message("UI.IconURL", player.userID), "image", kit.KitImage);
AddTitleSperator(container, 7, Message("UI.UsageAuthority", player.userID));
AddInputField(container, 8, Message("UI.Permission", player.userID), "permission", kit.RequiredPermission);
AddInputField(container, 9, Message("UI.AuthLevel", player.userID), "authLevel", kit.RequiredAuth);
AddToggleField(container, 10, Message("UI.IsHidden", player.userID), "isHidden", kit.IsHidden);
AddTitleSperator(container, 11, Message("UI.Usage", player.userID));
AddInputField(container, 12, Message("UI.MaxUses", player.userID), "maximumUses", kit.MaximumUses);
AddInputField(container, 13, Message("UI.CooldownSeconds", player.userID), "cooldown", kit.Cooldown);
AddInputField(container, 14, Message("UI.PurchaseCost", player.userID), "cost", kit.Cost);
AddTitleSperator(container, 15, Message("UI.CopyPaste", player.userID));
AddInputField(container, 16, Message("UI.FileName", player.userID), "copyPaste", kit.CopyPasteFile);
// Kit Items
CreateKitLayout(player, container, kit);
// Kit Saving
UI.Button(container, UI_MENU, Configuration.Menu.Color2.Get, Message("UI.SaveKit", player.userID), 14, new UI4(0.005f, 0.005f, 0.2475f, 0.045f), $"kits.savekit {overwrite}");
UI.Toggle(container, UI_MENU, ICON_BACKGROUND_COLOR, 14, new UI4(0.2525f, 0.005f, 0.2825f, 0.045f), $"kits.toggleoverwrite {overwrite}", overwrite);
UI.Label(container, UI_MENU, Message("UI.Overwrite", player.userID), 14, new UI4(0.2875f, 0.005f, 0.495f, 0.045f), TextAnchor.MiddleLeft);
// Item Management
UI.Button(container, UI_MENU, Configuration.Menu.Color3.Get, Message("UI.ClearItems", player.userID), 14, new UI4(0.505f, 0.005f, 0.7475f, 0.045f), $"kits.clearitems {overwrite}");
UI.Button(container, UI_MENU, Configuration.Menu.Color2.Get, Message("UI.CopyInv", player.userID), 14, new UI4(0.7525f, 0.005f, 0.995f, 0.045f), $"kits.copyinv {overwrite}");
CuiHelper.DestroyUi(player, UI_MENU);
CuiHelper.AddUi(player, container);
}
#region Editor Helpers
private const float EDITOR_ELEMENT_HEIGHT = 0.04f;
private void AddInputField(CuiElementContainer container, int index, string title, string fieldName, object currentValue, int additionalHeight = 0)
{
float yMin = GetVerticalPos(index, 0.88f);
float yMax = yMin + EDITOR_ELEMENT_HEIGHT;
if (additionalHeight != 0)
yMin = GetVerticalPos(index + additionalHeight, 0.88f);
UI.Panel(container, UI_MENU, Configuration.Menu.Color4.Get, new UI4(0.005f, yMin, 0.175f, yMax));
UI.Label(container, UI_MENU, title, 12, new UI4(0.01f, yMin, 0.175f, yMax - 0.0075f), TextAnchor.UpperLeft);
UI.Panel(container, UI_MENU, ICON_BACKGROUND_COLOR, new UI4(0.175f, yMin, 0.495f, yMax));
string label = GetInputLabel(currentValue);
if (!string.IsNullOrEmpty(label))
{
UI.Label(container, UI_MENU, label, 12, new UI4(0.18f, yMin, 0.47f, yMax - 0.0075f), TextAnchor.UpperLeft);
UI.Button(container, UI_MENU, Configuration.Menu.Color3.Get, "X", 14, new UI4(0.47f, yMax - EDITOR_ELEMENT_HEIGHT, 0.495f, yMax), $"kits.clear {fieldName}");
}
else UI.Input(container, UI_MENU, string.Empty, 12, $"kits.creator {fieldName}", new UI4(0.18f, yMin, 0.495f, yMax - 0.0075f), TextAnchor.UpperLeft);
}
private void AddTitleSperator(CuiElementContainer container, int index, string title)
{
float yMin = GetVerticalPos(index, 0.88f);
float yMax = yMin + EDITOR_ELEMENT_HEIGHT;
UI.Panel(container, UI_MENU, Configuration.Menu.Color1.Get, new UI4(0.005f, yMin, 0.495f, yMax));
UI.Label(container, UI_MENU, title, 14, new UI4(0.01f, yMin, 0.495f, yMax), TextAnchor.MiddleLeft);
}
private void AddLabelField(CuiElementContainer container, int index, string title, string value, int additionalHeight = 0)
{
float yMin = GetVerticalPos(index, 0.88f);
float yMax = yMin + EDITOR_ELEMENT_HEIGHT;
if (additionalHeight != 0)
yMin = GetVerticalPos(index + additionalHeight, 0.88f);
UI.Panel(container, UI_MENU, Configuration.Menu.Color4.Get, new UI4(0.005f, yMin, 0.175f, yMax));
UI.Label(container, UI_MENU, title, 12, new UI4(0.01f, yMin, 0.175f, yMax - 0.0075f), TextAnchor.UpperLeft);
UI.Panel(container, UI_MENU, ICON_BACKGROUND_COLOR, new UI4(0.175f, yMin, 0.495f, yMax));
UI.Label(container, UI_MENU, value, 12, new UI4(0.18f, yMin, 0.495f, yMax - 0.0075f), TextAnchor.UpperLeft);
}
private void AddToggleField(CuiElementContainer container, int index, string title, string fieldName, bool currentValue)
{
float yMin = GetVerticalPos(index, 0.88f);
float yMax = yMin + EDITOR_ELEMENT_HEIGHT;
UI.Panel(container, UI_MENU, Configuration.Menu.Color4.Get, new UI4(0.005f, yMin, 0.175f, yMax));
UI.Label(container, UI_MENU, title, 12, new UI4(0.01f, yMin, 0.175f, yMax), TextAnchor.MiddleLeft);
UI.Toggle(container, UI_MENU, ICON_BACKGROUND_COLOR, 14, new UI4(0.175f, yMin, 0.205f, yMax), $"kits.creator {fieldName} {!currentValue}", currentValue);
}
private string GetInputLabel(object obj)
{
if (obj is string s)
return string.IsNullOrEmpty(s) ? null : s;
if (obj is int i)
return i <= 0 ? null : i.ToString();
if (obj is float f)
return f <= 0 ? null : f.ToString();
return null;
}
private float GetVerticalPos(int i, float start = 0.9f) => start - (i * (EDITOR_ELEMENT_HEIGHT + 0.005f));
#endregion
#endregion
#region Popup Messages
private void CreateMenuPopup(BasePlayer player, string text, float duration = 5f)
{
CuiElementContainer container = UI.Container(UI_POPUP, Configuration.Menu.Color4.Get, n
// ... [Code preview truncated due to length. Full source in attached file] ...