Загрузка данных
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Kits", "Classic Ultimate Release", "1.3.0")]
class Kits : RustPlugin
{
private List<Kit> kitsList = new List<Kit>();
private Dictionary<ulong, Dictionary<string, KitData>> PlayersData = new Dictionary<ulong, Dictionary<string, KitData>>();
// Словарь для отслеживания текущей страницы меню у каждого игрока
private Dictionary<ulong, int> playerPages = new Dictionary<ulong, int>();
class PluginConfig
{
[JsonProperty("Префикс чата")] public string DefaultPrefix = "[Kit]";
}
public class Kit
{
public string Name { get; set; }
public string DisplayName { get; set; }
public bool Hide { get; set; }
public string Color { get; set; }
public List<KitItem> Items { get; set; }
public Kit()
{
Name = string.Empty;
DisplayName = string.Empty;
Color = "0.41 0.51 0.32 1.0";
Items = new List<KitItem>();
}
}
public class KitItem
{
public int ItemID { get; set; }
public int Amount { get; set; }
public string Container { get; set; }
public int Slot { get; set; }
public float Condition { get; set; }
public KitItem()
{
Container = string.Empty;
}
}
public class KitData
{
public int Amount { get; set; }
public double Cooldown { get; set; }
}
void Loaded()
{
try { kitsList = Interface.Oxide.DataFileSystem.ReadObject<List<Kit>>("Kits/KitsList"); } catch { kitsList = new List<Kit>(); }
try { PlayersData = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, Dictionary<string, KitData>>>("Kits/PlayersData"); } catch { PlayersData = new Dictionary<ulong, Dictionary<string, KitData>>(); }
if (kitsList == null) kitsList = new List<Kit>();
if (PlayersData == null) PlayersData = new Dictionary<ulong, Dictionary<string, KitData>>();
}
void OnPlayerDisconnected(BasePlayer player, string reason)
{
if (player != null) playerPages.Remove(player.userID);
}
void OnPlayerRespawned(BasePlayer player)
{
if (player == null || player.IsDead() || player.IsBot) return;
if (player.userID < 76561197960265728L && player.userID > 0) return;
Kit autoKit = null;
foreach (var k in kitsList)
{
if (k.Name != null && k.Name.ToLower() == "autokit")
{
autoKit = k;
break;
}
}
if (autoKit == null) return;
timer.Once(0.5f, () =>
{
if (player == null || !player.IsConnected) return;
player.inventory.Strip();
GiveItems(player, autoKit);
SendReply(player, "Вы автоматически получили стартовый набор!");
});
}
[ChatCommand("kit")]
void KitCommand(BasePlayer player, string command, string[] args)
{
if (player.IsAdmin && args != null && args.Length > 0 && args[0].ToLower() == "create")
{
if (args.Length < 2) { SendReply(player, "Использование: /kit create [имя]"); return; }
string kitName = args[1].ToLower();
string displayName = kitName.ToUpper();
Kit newKit = new Kit
{
Name = kitName,
DisplayName = displayName,
Hide = (kitName == "autokit"),
Items = GetPlayerItems(player)
};
for (int i = kitsList.Count - 1; i >= 0; i--)
{
if (kitsList[i].Name != null && kitsList[i].Name.ToLower() == kitName)
{
kitsList.RemoveAt(i);
}
}
kitsList.Add(newKit);
SaveKits();
SendReply(player, $"Кит {kitName} успешно создан!");
return;
}
playerPages[player.userID] = 0; // Сбрасываем на первую страницу при открытии через чат
OpenKitMenu(player);
}
[ConsoleCommand("kit.closeui")]
void CloseUiCommand(ConsoleSystem.Arg systemArg)
{
if (systemArg == null) return;
BasePlayer p = systemArg.Player();
if (p != null) { DestroyAllKitUi(p); playerPages.Remove(p.userID); }
}
[ConsoleCommand("kit.backtomenu")]
void BackToMenuCommand(ConsoleSystem.Arg systemArg)
{
if (systemArg == null) return;
BasePlayer p = systemArg.Player();
if (p != null) OpenKitMenu(p);
}
// Консольная команда для переключения страниц меню наборов
[ConsoleCommand("kit.changepage")]
void ChangePageCommand(ConsoleSystem.Arg systemArg)
{
if (systemArg == null) return;
BasePlayer p = systemArg.Player(); if (p == null) return;
int targetPage = systemArg.GetInt(0, 0);
playerPages[p.userID] = targetPage;
OpenKitMenu(p);
}
[ConsoleCommand("kit.claim")]
void ClaimKitCommand(ConsoleSystem.Arg systemArg)
{
if (systemArg == null) return;
BasePlayer p = systemArg.Player(); if (p == null) return;
string kitName = systemArg.GetString(0, "").ToLower();
if (string.IsNullOrEmpty(kitName)) return;
Kit kit = null;
foreach (var k in kitsList)
{
if (k.Name != null && k.Name.ToLower() == kitName)
{
kit = k;
break;
}
}
if (kit != null)
{
GiveItems(p, kit);
SendReply(p, $"Вы успешно получили: {kit.DisplayName}");
}
DestroyAllKitUi(p);
playerPages.Remove(p.userID);
}
[ConsoleCommand("kit.previewui")]
void PreviewUiCommand(ConsoleSystem.Arg systemArg)
{
if (systemArg == null) return;
BasePlayer p = systemArg.Player(); if (p == null) return;
string kitName = systemArg.GetString(0, "").ToLower();
if (string.IsNullOrEmpty(kitName)) return;
OpenKitPreview(p, kitName);
}
private void DestroyAllKitUi(BasePlayer player)
{
CuiHelper.DestroyUi(player, "KitMenu");
CuiHelper.DestroyUi(player, "KitPreviewMenu");
}
private List<KitItem> GetPlayerItems(BasePlayer player)
{
var items = new List<KitItem>();
if (player.inventory.containerMain != null) AddItemsFromContainer(player.inventory.containerMain, "main", items);
if (player.inventory.containerBelt != null) AddItemsFromContainer(player.inventory.containerBelt, "belt", items);
if (player.inventory.containerWear != null) AddItemsFromContainer(player.inventory.containerWear, "wear", items);
return items;
}
private void AddItemsFromContainer(ItemContainer container, string label, List<KitItem> list)
{
if (container.itemList == null) return;
foreach (var item in container.itemList)
{
if (item == null || item.info == null) continue;
list.Add(new KitItem
{
ItemID = item.info.itemid,
Amount = item.amount,
Slot = item.position,
Condition = item.condition,
Container = label
});
}
}
private void GiveItems(BasePlayer player, Kit kit)
{
if (player == null || kit == null || kit.Items == null) return;
foreach (var item in kit.Items)
{
if (item == null) continue;
Item createdItem = ItemManager.CreateByItemID(item.ItemID, item.Amount);
if (createdItem == null) continue;
bool moved = false;
if (item.Container == "wear" && player.inventory.containerWear != null)
moved = createdItem.MoveToContainer(player.inventory.containerWear, item.Slot, true);
else if (item.Container == "belt" && player.inventory.containerBelt != null)
moved = createdItem.MoveToContainer(player.inventory.containerBelt, item.Slot, true);
if (!moved && player.inventory.containerMain != null)
{
createdItem.MoveToContainer(player.inventory.containerMain, -1, true);
}
}
player.inventory.SendSnapshot();
player.Command("inventory.update");
}
private void OpenKitMenu(BasePlayer player)
{
DestroyAllKitUi(player);
CuiElementContainer container = new CuiElementContainer();
string bgPanel = container.Add(new CuiPanel {
Image = { Color = "0 0 0 0.4" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
CursorEnabled = true
}, "Overlay", "KitMenu");
container.Add(new CuiButton {
Button = { Command = "kit.closeui", Color = "0 0 0 0" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "" }
}, bgPanel, "KitMenuBackgroundButton");
container.Add(new CuiPanel {
Image = { Color = "0.18 0.18 0.18 0.75" },
RectTransform = { AnchorMin = "0.08 0.44", AnchorMax = "0.92 0.56" }
}, bgPanel, "KitMenuInner");
List<Kit> visibleKits = new List<Kit>();
foreach (var k in kitsList)
{
if (!k.Hide) visibleKits.Add(k);
}
// Логика постраничной пагинации наборов
int currentPage = 0;
playerPages.TryGetValue(player.userID, out currentPage);
int itemsPerPage = 4;
int totalKits = visibleKits.Count;
int startIdx = currentPage * itemsPerPage;
// Если вдруг страница уехала за пределы, сбрасываем
if (startIdx >= totalKits && currentPage > 0)
{
currentPage = 0;
playerPages[player.userID] = 0;
startIdx = 0;
}
int countOnPage = Math.Min(itemsPerPage, totalKits - startIdx);
bool hasNextPage = totalKits > (currentPage + 1) * itemsPerPage;
bool hasPrevPage = currentPage > 0;
if (totalKits == 0) { CuiHelper.AddUi(player, container); return; }
// Высчитываем анкоры в зависимости от того, сколько наборов конкретно НА ДАННОЙ странице
string[] minAnchors = new string[] { "0.38 0.35", "0.24 0.35", "0.10 0.35", "0.03 0.35" };
string[] maxAnchors = new string[] { "0.62 0.90", "0.48 0.90", "0.34 0.90", "0.24 0.90" };
if (countOnPage == 1) { minAnchors[0] = "0.38 0.35"; maxAnchors[0] = "0.62 0.90"; }
else if (countOnPage == 2) { minAnchors[0] = "0.24 0.35"; maxAnchors[0] = "0.48 0.90"; minAnchors[1] = "0.52 0.35"; maxAnchors[1] = "0.76 0.90"; }
else if (countOnPage == 3) {
minAnchors[0] = "0.10 0.35"; maxAnchors[0] = "0.34 0.90";
minAnchors[1] = "0.38 0.35"; maxAnchors[1] = "0.62 0.90";
minAnchors[2] = "0.66 0.35"; maxAnchors[2] = "0.90 0.90";
}
else if (countOnPage == 4) {
minAnchors[0] = "0.03 0.35"; maxAnchors[0] = "0.24 0.90";
minAnchors[1] = "0.26 0.35"; maxAnchors[1] = "0.47 0.90";
minAnchors[2] = "0.49 0.35"; maxAnchors[2] = "0.70 0.90";
minAnchors[3] = "0.72 0.35"; maxAnchors[3] = "0.93 0.90";
}
for (int i = 0; i < countOnPage; i++)
{
var kit = visibleKits[startIdx + i];
string btnColor = string.IsNullOrEmpty(kit.Color) ? "0.41 0.51 0.32 1.0" : kit.Color;
string kitBtn = container.Add(new CuiButton {
Button = { Command = "kit.claim " + kit.Name, Color = btnColor },
RectTransform = { AnchorMin = minAnchors[i], AnchorMax = maxAnchors[i] },
Text = { Text = "" }
}, "KitMenuInner", "KitBlock_" + kit.Name);
string kitText = (kit.DisplayName != null) ? kit.DisplayName.ToUpper() : "";
container.Add(new CuiLabel {
Text = { Text = kitText, FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
RectTransform = { AnchorMin = "0.05 0.50", AnchorMax = "0.95 0.95" }
}, kitBtn);
container.Add(new CuiLabel {
Text = { Text = "МОЖНО БРАТЬ", FontSize = 13, Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.6" },
RectTransform = { AnchorMin = "0.05 0.05", AnchorMax = "0.95 0.50" }
}, kitBtn);
string[] minParts = minAnchors[i].Split(' ');
string[] maxParts = maxAnchors[i].Split(' ');
string previewMin = minParts[0] + " 0.02";
string previewMax = maxParts[0] + " 0.26";
container.Add(new CuiButton {
Button = { Command = "kit.previewui " + kit.Name, Color = "0.11 0.11 0.11 0.95" },
RectTransform = { AnchorMin = previewMin, AnchorMax = previewMax },
Text = { Text = "ПОДРОБНЕЕ", FontSize = 10, Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.9" }
}, "KitMenuInner");
}
// Динамические кнопки переключения страниц
if (hasNextPage)
{
container.Add(new CuiButton {
Button = { Command = $"kit.changepage {currentPage + 1}", Color = "0.25 0.25 0.25 1.0" },
RectTransform = { AnchorMin = "0.94 0.15", AnchorMax = "0.99 0.45" },
Text = { Text = "▶", FontSize = 12, Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.8" }
}, "KitMenuInner");
}
if (hasPrevPage)
{
container.Add(new CuiButton {
Button = { Command = $"kit.changepage {currentPage - 1}", Color = "0.25 0.25 0.25 1.0" },
RectTransform = { AnchorMin = "0.01 0.15", AnchorMax = "0.06 0.45" },
Text = { Text = "◀", FontSize = 12, Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.8" }
}, "KitMenuInner");
}
CuiHelper.AddUi(player, container);
}
private void OpenKitPreview(BasePlayer player, string kitName)
{
DestroyAllKitUi(player);
Kit kit = null;
foreach (var k in kitsList)
{
if (k.Name != null && k.Name.ToLower() == kitName) { kit = k; break; }
}
if (kit == null) return;
CuiElementContainer container = new CuiElementContainer();
string bgPanel = container.Add(new CuiPanel {
Image = { Color = "0 0 0 0.4" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
CursorEnabled = true
}, "Overlay", "KitPreviewMenu");
container.Add(new CuiButton { Button = { Command = "kit.closeui", Color = "0 0 0 0" }, RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" } }, bgPanel);
container.Add(new CuiPanel {
Image = { Color = "0.14 0.14 0.14 0.95" },
RectTransform = { AnchorMin = "0.08 0.44", AnchorMax = "0.92 0.56" }
}, bgPanel, "KitPreviewInner");
container.Add(new CuiButton {
Button = { Command = "kit.backtomenu", Color = "0.21 0.21 0.21 1.0" },
RectTransform = { AnchorMin = "0.02 0.25", AnchorMax = "0.10 0.75" },
Text = { Text = "НАЗАД", FontSize = 11, Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.9" }
}, "KitPreviewInner");
// Координатная сетка для двух рядов (по 6 предметов в ряд, всего до 12 предметов)
string[] row1Min = new string[] { "0.13 0.53", "0.26 0.53", "0.39 0.53", "0.52 0.53", "0.65 0.53", "0.78 0.53" };
string[] row1Max = new string[] { "0.23 0.91", "0.36 0.91", "0.49 0.91", "0.62 0.91", "0.75 0.91", "0.88 0.91" };
string[] row2Min = new string[] { "0.13 0.11", "0.26 0.11", "0.39 0.11", "0.52 0.11", "0.65 0.11", "0.78 0.11" };
string[] row2Max = new string[] { "0.23 0.49", "0.36 0.49", "0.49 0.49", "0.62 0.49", "0.75 0.49", "0.88 0.49" };
int itemIndex = 0;
int totalItems = kit.Items.Count;
// Если предметов мало (до 6), выстраиваем их строго по центру по вертикали, как и раньше
bool useTwoRows = totalItems > 6;
foreach (var item in kit.Items)
{
if (itemIndex >= 12) break; // Лимит отображения в превью — 12 предметов
ItemDefinition def = ItemManager.FindItemDefinition(item.ItemID);
if (def == null) continue;
string minAnchor = "";
string maxAnchor = "";
string labelMin = "";
string labelMax = "";
if (useTwoRows)
{
// Логика двух рядов
if (itemIndex < 6)
{
minAnchor = row1Min[itemIndex];
maxAnchor = row1Max[itemIndex];
// Текст количества прижимаем к низу иконки внутри первого ряда
string[] pMin = minAnchor.Split(' ');
string[] pMax = maxAnchor.Split(' ');
labelMin = pMin[0] + " 0.53";
labelMax = pMax[0] + " 0.65";
}
else
{
minAnchor = row2Min[itemIndex - 6];
maxAnchor = row2Max[itemIndex - 6];
string[] pMin = minAnchor.Split(' ');
string[] pMax = maxAnchor.Split(' ');
labelMin = pMin[0] + " 0.11";
labelMax = pMax[0] + " 0.23";
}
}
else
{
// Один ряд по центру (если предметов <= 6)
string[] singleRowMin = new string[] { "0.13 0.25", "0.25 0.25", "0.37 0.25", "0.49 0.25", "0.61 0.25", "0.73 0.25" };
string[] singleRowMax = new string[] { "0.22 0.75", "0.34 0.75", "0.46 0.75", "0.58 0.75", "0.70 0.75", "0.82 0.75" };
minAnchor = singleRowMin[itemIndex];
maxAnchor = singleRowMax[itemIndex];
string[] pMin = minAnchor.Split(' ');
string[] pMax = maxAnchor.Split(' ');
labelMin = pMin[0] + " 0.06";
labelMax = pMax[0] + " 0.24";
}
// Отрисовка самой иконки предмета
container.Add(new CuiElement
{
Parent = "KitPreviewInner",
Components = {
new CuiImageComponent { Sprite = "assets/content/textures/generic/fulltransparent.tga", ItemId = item.ItemID },
new CuiRectTransformComponent { AnchorMin = minAnchor, AnchorMax = maxAnchor }
}
});
// Отрисовка надписи количества (например: x25)
container.Add(new CuiLabel {
Text = { Text = "x" + item.Amount, FontSize = 10, Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.85" },
RectTransform = { AnchorMin = labelMin, AnchorMax = labelMax }
}, "KitPreviewInner");
itemIndex++;
}
CuiHelper.AddUi(player, container);
}
private void SaveKits() => Interface.Oxide.DataFileSystem.WriteObject("Kits/KitsList", kitsList);
private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject("Kits/PlayersData", PlayersData);
void OnServerSave() { SaveData(); SaveKits(); }
}
}