C#:
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System.Collections.Generic;
using UnityEngine;
using System.Collections;
using Oxide.Core;
using System.Linq;
using Newtonsoft.Json;
using System;
namespace Oxide.Plugins
{
[Info("MBReward", "Barry_Allenn", "1.0.0")]
[Description("MBReward")]
public class MBReward : RustPlugin
{
[PluginReference] private Plugin ImageLibrary, MBMainMenu;
private ImageUI imageUI = new ImageUI();
private ConfigData configData;
private class ConfigData
{
[JsonProperty("Команды")]
public List<CommandEntry> Commands = new();
[JsonProperty("Интервал в минутах для награды за онлайн")]
public float OnlineRewardIntervalMinutes = 360f; // 6 часов
[JsonProperty("Количество монет за онлайн")]
public int OnlineRewardAmount = 1;
[JsonProperty("Количество монет за первый вход")]
public int FirstJoinRewardAmount = 5;
}
public class CommandEntry
{
public string Command;
public string LangKey;
}
private Dictionary<ulong, double> onlineTimes = new();
private Dictionary<ulong, double> lastCheckTimes = new();
private const string DataFileName = "MBReward_OnlineData";
private class OnlineData
{
public Dictionary<ulong, double> OnlineSeconds = new();
public Dictionary<ulong, bool> FirstJoinGiven = new();
}
private OnlineData onlineData = new();
protected override void LoadConfig()
{
base.LoadConfig();
configData = Config.ReadObject<ConfigData>();
}
protected override void SaveConfig()
{
Config.WriteObject(configData, true);
}
protected override void LoadDefaultConfig()
{
configData = new ConfigData
{
Commands = new List<CommandEntry>
{
new CommandEntry { Command = "+1", LangKey = "Command_Home_Desc" },
new CommandEntry { Command = "+5", LangKey = "Command_Warp_Desc" }
},
OnlineRewardIntervalMinutes = 360f,
OnlineRewardAmount = 1,
FirstJoinRewardAmount = 5
};
SaveConfig();
}
void Init()
{
LoadConfig(); // Чтение конфига
LoadOnlineData(); // Чтение базы
}
private void OnServerInitialized()
{
imageUI = new ImageUI();
imageUI.DownloadImages();
timer.Every(60f, TrackOnlineTime);
// LoadOnlineData();
}
void Unload()
{
foreach (var player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, "MBReward_UI");
}
imageUI?.UnloadImages();
SaveOnlineData();
}
private void GiveCoins(BasePlayer player, int amount)
{
Server.Command($"mb.give {player.UserIDString} {amount}");
}
private float saveInterval = 300f; // Интервал сохранения в секундах (10 минут)
private double lastSaveTime = 0;
private void TrackOnlineTime()
{
double now = Interface.Oxide.Now;
foreach (var player in BasePlayer.activePlayerList)
{
if (player == null || !player.IsConnected) continue;
ulong userId = player.userID;
if (!lastCheckTimes.ContainsKey(userId))
lastCheckTimes[userId] = now;
double elapsed = now - lastCheckTimes[userId];
lastCheckTimes[userId] = now;
if (!onlineData.OnlineSeconds.ContainsKey(userId))
onlineData.OnlineSeconds[userId] = 0;
onlineData.OnlineSeconds[userId] += elapsed;
float minutesOnline = (float)(onlineData.OnlineSeconds[userId] / 60f);
if (minutesOnline >= configData.OnlineRewardIntervalMinutes)
{
GiveCoins(player, configData.OnlineRewardAmount);
onlineData.OnlineSeconds[userId] = 0;
player.ChatMessage($"Вы получили {configData.OnlineRewardAmount} монет за {configData.OnlineRewardIntervalMinutes} мин.онлайна!");
}
}
// ⏳ Автосохранение раз в saveInterval секунд
if (now - lastSaveTime >= saveInterval)
{
SaveOnlineData();
lastSaveTime = now;
}
}
private void SaveOnlineData()
{
Interface.Oxide.DataFileSystem.WriteObject(DataFileName, onlineData);
}
private void OnPlayerConnected(BasePlayer player)
{
ulong userId = player.userID;
if (!onlineData.FirstJoinGiven.ContainsKey(userId))
{
int amount = configData.FirstJoinRewardAmount;
if (amount > 0)
{
GiveCoins(player, amount);
player.ChatMessage(Msg("MB_FirstJoinReward", player.UserIDString, amount));
}
onlineData.FirstJoinGiven[userId] = true;
SaveOnlineData();
Puts($"[MBReward] {player.displayName} получил награду за первый вход");
}
}
private void LoadOnlineData()
{
onlineData = Interface.Oxide.DataFileSystem.ReadObject<OnlineData>(DataFileName) ?? new OnlineData();
}
// В классе MBReward добавьте этот метод
[HookMethod("GetTimeToReward")]
public object GetTimeToReward(BasePlayer player)
{
if (!onlineData.OnlineSeconds.ContainsKey(player.userID))
return Msg("MB_Reward_Zero", player.UserIDString); // 0хв
double secondsPlayed = onlineData.OnlineSeconds[player.userID];
double secondsNeeded = configData.OnlineRewardIntervalMinutes * 60;
double secondsRemaining = secondsNeeded - secondsPlayed;
if (secondsRemaining <= 0)
return Msg("MB_Reward_Ready", player.UserIDString); // готово
TimeSpan timeLeft = TimeSpan.FromSeconds(secondsRemaining);
if (timeLeft.Hours > 0)
return Msg("MB_Reward_Format_HM", player.UserIDString, timeLeft.Hours, timeLeft.Minutes);
else
return Msg("MB_Reward_Format_M", player.UserIDString, timeLeft.Minutes);
}
private void ShowCoinsUI(BasePlayer player)
{
CuiHelper.DestroyUi(player, "MBReward_UI");
var container = new CuiElementContainer();
string root = container.Add(new CuiPanel
{
Image = { Color = "0 0 0 0" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
CursorEnabled = true
}, "Overlay", "MBReward_UI");
string backgroundPanel = container.Add(new CuiPanel
{
Image = { Color = "0 0 0 0.7", Material = "assets/content/ui/uibackgroundblur.mat" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
CursorEnabled = true
}, root);
container.Add(new CuiButton
{
Button = { Color = "0 0 0 0", Close = "MBReward_UI" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "", FontSize = 0 }
}, root);
string imageId = imageUI?.GetImage("MBWelcome_Seven_Os");
if (!string.IsNullOrEmpty(imageId))
{
container.Add(new CuiElement
{
Name = CuiHelper.GetGuid(),
Parent = root,
Components =
{
new CuiRawImageComponent { Png = imageId },
new CuiRectTransformComponent { AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-558 -314", OffsetMax = "558 315" }
}
});
}
container.Add(new CuiButton
{
Button = { Color = "0 0 0 0", Close = "MBReward_UI" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = "", FontSize = 0 }
}, root);
string centerPanel = container.Add(new CuiPanel
{
Image = { Color = "0.1 0.1 0.1 0" },
RectTransform = {
AnchorMin = "0.5 0.5",
AnchorMax = "0.5 0.5",
OffsetMin = "-438 -265", // ширина 800, высота 600
OffsetMax = "440 265"
},
CursorEnabled = true
}, root);
// // Панель-фон
// container.Add(new CuiPanel
// {
// Image = { Color = "0 0 0 0.9" }, // Темно-сірий прозорий фон
// RectTransform = { AnchorMin = "0.3 0.09", AnchorMax = "0.7 0.11" }
// }, root);
// // Панель-фон
// container.Add(new CuiPanel
// {
// Image = { Color = "1 1 1 0.4" }, // Темно-сірий прозорий фон
// RectTransform = { AnchorMin = "0.2 -0.06", AnchorMax = "0.8 -0.02" }
// }, centerPanel);
container.Add(new CuiLabel
{
RectTransform = { AnchorMin = "0.341 0.86", AnchorMax = "0.75 0.955" },
Text = { Text = Msg("MB_Coins_Player", player.userID.ToString()), Color = "1 1 1 0.9", Align = TextAnchor.MiddleLeft, FontSize = 30 }
}, centerPanel);
container.Add(new CuiLabel
{
RectTransform = { AnchorMin = "0.341 0.8", AnchorMax = "0.8 0.868" },
Text = { Text = Msg("MB_Coins_Player_2", player.userID.ToString()), Color = "1 1 1 0.9", Align = TextAnchor.MiddleLeft, FontSize = 21, Font = "permanentmarker.ttf" }
}, centerPanel);
container.Add(new CuiLabel
{
Text = { Text = Msg("MB_Close_Coins", player.userID.ToString()), FontSize = 13, Color = "1 1 1 0.4", Align = TextAnchor.MiddleCenter },
RectTransform = { AnchorMin = "0.2 -0.06", AnchorMax = "0.8 -0.02" }
}, centerPanel);
container.Add(new CuiButton
{
Button = {
Command = "mbcoins.back", // кастомная команда
Color = "0.2 0.5 0.8 0"
},
RectTransform = { AnchorMin = "0.865 0.04", AnchorMax = "0.947 0.12" },
Text = {
Text = Msg("MB_Back", player.UserIDString),
FontSize = 15,
Align = TextAnchor.MiddleCenter,
Color = "1 1 1 1"
}
}, centerPanel);
// 📐 Настройки ячеек
float blockWidth = 175f;
float blockHeight = 132f;
float blockMargin = 8f;
int totalCommands = configData.Commands.Count;
int rows = 2;
int columns = Mathf.CeilToInt(totalCommands / (float)rows);
// 📏 Общая ширина контента
float scrollContentWidth = columns * (blockWidth + blockMargin);
scrollContentWidth = Mathf.Max(scrollContentWidth, 600f); // минимальная ширина
// 🖼️ ScrollView (видимая область) с адаптивным якорем
container.Add(new CuiElement
{
Name = "MBCoinsArea",
Parent = centerPanel,
Components =
{
new CuiScrollViewComponent
{
Horizontal = true,
Vertical = false,
Inertia = true,
Elasticity = 0.2f,
DecelerationRate = 0.2f,
ScrollSensitivity = 30f,
ContentTransform = new CuiRectTransform
{
AnchorMin = "0 0.015",
AnchorMax = "0 1",
OffsetMin = "0 0",
OffsetMax = $"{scrollContentWidth} 0"
},
HorizontalScrollbar = new CuiScrollbar
{
Size = 5f,
AutoHide = false,
HandleColor = "0.3 0.6 0.9 0.7",
TrackColor = "0.1 0.1 0.1 0.6"
}
},
new CuiRectTransformComponent
{
AnchorMin = "0.34 0.235", AnchorMax = "0.74 0.75"
},
new CuiNeedsCursorComponent()
}
});
// 🔲 Контейнер содержимого — это то, что листается
container.Add(new CuiPanel
{
Image = { Color = "0 0 0 0" },
RectTransform = {
AnchorMin = "0 0",
AnchorMax = "0 1",
OffsetMin = "0 0",
OffsetMax = $"{scrollContentWidth} 0"
}
}, "MBCoinsArea", "MBCoinsContent");
float startOffsetX = 3f; // 👈 отступ слева перед первым блоком
// 🧩 Генерация ячеек
for (int i = 0; i < totalCommands; i++)
{
int column = i / rows;
int row = i % rows;
float left = startOffsetX + column * (blockWidth + blockMargin);
float right = left + blockWidth;
float top = (row == 0) ? blockHeight + blockMargin : 0;
float bottom = top + blockHeight;
var entry = configData.Commands[i];
string cmd = entry.Command;
string desc = Msg(entry.LangKey, player.UserIDString);
string backgroundImg = imageUI.GetImage("MBCoins_BG");
string containerName = $"MBCoinsContent_{i}";
// 📦 Контейнер блока
container.Add(new CuiPanel
{
Image = { Color = "0 0 0 0" },
RectTransform = {
AnchorMin = "0 0",
AnchorMax = "0 0",
OffsetMin = $"{left} {top}",
OffsetMax = $"{right} {bottom}"
}
}, "MBCoinsContent", containerName);
// 🖼️ Фон
if (!string.IsNullOrEmpty(backgroundImg))
{
container.Add(new CuiElement
{
Name = $"MBCommandBG_{i}",
Parent = containerName,
Components =
{
new CuiRawImageComponent
{
Png = backgroundImg,
Sprite = "assets/content/textures/generic/fulltransparent.tga",
Color = "1 1 1 1"
},
new CuiRectTransformComponent
{
AnchorMin = "0 0",
AnchorMax = "1 1"
}
}
});
}
// 🏷️ Название команды
container.Add(new CuiLabel
{
Text = {
Text = cmd,
FontSize = 32,
Align = TextAnchor.MiddleRight,
Color = "1 1 1 0.8"
},
RectTransform = {
AnchorMin = "0.05 0.5", AnchorMax = "0.5 0.78"
}
}, containerName);
// Панель-фон
container.Add(new CuiPanel
{
Image = { Color = "0.67 0.87 0.80 0" }, // Темно-сірий прозорий фон
RectTransform = { AnchorMin = "0.15 0.1", AnchorMax = "0.85 0.45" }
}, containerName);
// 📝 Описание команды
container.Add(new CuiLabel
{
Text = {
Text = desc,
FontSize = 10,
Align = TextAnchor.UpperCenter,
Color = "1 1 1 0.5"
},
RectTransform = {
AnchorMin = "0.15 0.1", AnchorMax = "0.85 0.45"
}
}, containerName);
}
CuiHelper.AddUi(player, container);
}
[ConsoleCommand("mbcoins.back")]
private void CmdCoinsBack(ConsoleSystem.Arg arg)
{
var player = arg.Connection?.player as BasePlayer;
if (player == null) return;
CuiHelper.DestroyUi(player, "MBReward_UI");
MBMainMenu?.Call("ShowMainMenu", player);
}
[ChatCommand("mb")]
private void CmdMBReward(BasePlayer player, string command, string[] args)
{
ShowCoinsUI(player);
}
private class ImageUI
{
private const string PathFolder = "MBSystem/MBReward/Images/";
private readonly Dictionary<string, ImageData> _images = new()
{
{ "MBWelcome_Seven_Os", new ImageData() },
{ "MBCoins_BG", new ImageData() }
};
private enum ImageStatus { NotLoaded, Loaded, Failed }
private class ImageData
{
public ImageStatus Status = ImageStatus.NotLoaded;
public string Id { get; set; }
}
public string GetImage(string name)
{
if (_images.TryGetValue(name, out var image) && image.Status == ImageStatus.Loaded)
return image.Id;
return null;
}
public void DownloadImages()
{
string fullPath = Interface.Oxide.DataDirectory + System.IO.Path.DirectorySeparatorChar + PathFolder;
if (!System.IO.Directory.Exists(fullPath))
{
System.IO.Directory.CreateDirectory(fullPath);
Interface.Oxide.LogInfo($"[MBReward] Создана папка: {fullPath}");
}
var nextImage = _images.FirstOrDefault(img => img.Value.Status == ImageStatus.NotLoaded);
if (!string.IsNullOrEmpty(nextImage.Key))
ServerMgr.Instance.StartCoroutine(LoadImage(nextImage));
else
Interface.Oxide.LogInfo("[MBReward] Все изображения загружены!");
}
private IEnumerator LoadImage(KeyValuePair<string, ImageData> image)
{
string filePath = "file://" + Interface.Oxide.DataDirectory + System.IO.Path.DirectorySeparatorChar + PathFolder + image.Key + ".png";
using (var www = UnityEngine.Networking.UnityWebRequestTexture.GetTexture(filePath))
{
yield return www.SendWebRequest();
if (www.result != UnityEngine.Networking.UnityWebRequest.Result.Success)
{
image.Value.Status = ImageStatus.Failed;
Interface.Oxide.LogWarning($"[MBReward] Ошибка загрузки {image.Key}");
}
else
{
Texture2D texture = UnityEngine.Networking.DownloadHandlerTexture.GetContent(www);
image.Value.Id = FileStorage.server.Store(texture.EncodeToPNG(), FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID).ToString();
image.Value.Status = ImageStatus.Loaded;
UnityEngine.Object.DestroyImmediate(texture);
}
DownloadImages();
}
}
public void UnloadImages()
{
foreach (var img in _images)
{
if (img.Value.Status == ImageStatus.Loaded && img.Value.Id != null)
FileStorage.server.Remove(uint.Parse(img.Value.Id), FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);
}
_images.Clear();
}
}
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["MB_Close_Coins"] = "Click on an empty space to close the menu",
["Command_OnlineReward_Desc"] = "Play for 6 hours and receive 1 MBCoin!",
["MB_Coins_Player"] = "FARM MBCOINS",
["MB_Coins_Player_2"] = "<color=#FFD700>Get rich</color> with every step!",
["MB_Reward_Time"] = "Time until reward: {0}",
["MB_Reward_Ready"] = "Reward is available!",
["MB_Reward_Zero"] = "0m",
["MB_Reward_Ready"] = "ready",
["MB_Reward_Format_HM"] = "{0}h {1}m",
["MB_Reward_Format_M"] = "{0}m",
["Command_Home_Desc"] = "6-hour session in the game",
["Command_Warp_Desc"] = "First time joining the server",
["MB_Back"] = "Back",
["MB_FirstJoinReward"] = "You received {0} coins for your first login on the server!"
}, this);
lang.RegisterMessages(new Dictionary<string, string>
{
["MB_Close_Coins"] = "Натисніть на порожнє місце, щоб закрити меню",
["Command_OnlineReward_Desc"] = "6-ти годинний сеанс в грі",
["MB_Coins_Player"] = "ФАРМ MBCOINS",
["MB_Coins_Player_2"] = "<color=#FFD700>Збагачуйся</color> з кожним кроком!",
["MB_Reward_Time"] = "До нагороди: {0}",
["MB_Reward_Ready"] = "Нагорода доступна!",
["MB_Reward_Zero"] = "0хв",
["MB_Reward_Ready"] = "готово",
["MB_Reward_Format_HM"] = "{0}г {1}хв",
["MB_Reward_Format_M"] = "{0}хв",
["Command_Home_Desc"] = "6-ти годинний сеанс в грі",
["Command_Warp_Desc"] = "Вперше зайшов на сервер",
["MB_Back"] = "Назад",
["MB_FirstJoinReward"] = "Ви отримали {0} монет за перший вхід на сервер!"
}, this, "uk");
lang.RegisterMessages(new Dictionary<string, string>
{
["MB_Close_Coins"] = "Нажмите на пустое место, чтобы закрыть меню",
["Command_OnlineReward_Desc"] = "6-часовой игровой сеанс",
["MB_Coins_Player"] = "ФАРМ MBCOINS",
["MB_Coins_Player_2"] = "<color=#FFD700>Обогащайся</color> с каждым шагом!",
["MB_Reward_Time"] = "До награды: {0}",
["MB_Reward_Ready"] = "Награда доступна!",
["MB_Reward_Zero"] = "0мин",
["MB_Reward_Ready"] = "готово",
["MB_Reward_Format_HM"] = "{0}ч {1}мин",
["MB_Reward_Format_M"] = "{0}мин",
["Command_Home_Desc"] = "6-часовой игровой сеанс",
["Command_Warp_Desc"] = "Первый вход на сервер",
["MB_Back"] = "Назад",
["MB_FirstJoinReward"] = "Вы получили {0} монет за первый вход на сервер!"
}, this, "ru");
}
private string Msg(string key, string userid = null, params object[] obj)
{
return string.Format(lang.GetMessage(key, this, userid), obj);
}
}
}