C#:
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;
using Newtonsoft.Json;
using Oxide.Game.Rust.Cui;
namespace Oxide.Plugins {
[Info("PirateGold", "Я и Я", "1.0.8")]
class PirateGold : RustPlugin {
#region Variables
[PluginReference]
private Plugin ImageLibrary, ComponentPlus;
private static System.Random Rnd = new System.Random();
private static List<ulong> OpenButtons = new List<ulong>();
private static Vector3 ShopPos = default(Vector3);
#endregion
#region Hooks
private void Init() {
LoadVariables();
LoadData();
}
private void OnServerInitialized() {
OpenButtons.Clear();
if (data.GoldGoods == null || data.GoldGoods.Count == 0) OnNewSave();
if (!ImageLibrary) PrintError("ImageLibrary не найдена!");
else foreach (var itemList in data.GoldGoods) foreach (var item in itemList) ImageLibrary?.Call("GetImage", item.ItemName);
var vending = BaseNetworkable.serverEntities.OfType<NPCVendingMachine>().FirstOrDefault(x=> x != null && x.shopName == configData.ShopName);
if (vending == null) PrintWarning("Не найден магазин для пиратских товаров!");
else ShopPos = vending.transform.position;
DownloadImages();
var lootList = BaseNetworkable.serverEntities.OfType<LootContainer>().Where(x=> x != null && x.ShortPrefabName == "crate_underwater_basic").ToList();
foreach (var loot in lootList) FillLoot(loot);
data.TouchedLoot.Clear();
foreach (var loot in lootList) data.TouchedLoot.Add(loot.net.ID);
timer.Once(250f, ()=> SpawnPirateBoxes());
if (ShopPos != default(Vector3)) CheckButtonClose();
}
private void OnServerSave() => SaveData();
private void OnNewSave() {
data = new PirateData();
foreach (var itemList in configData.GoldGoods) {
int cnt = 0;
var list = new List<BlackItem>();
foreach (var item in itemList.OrderBy(x=> Rnd.Next())) {
if (cnt >= 5) break;
list.Add(item);
cnt++;
}
data.GoldGoods.Add(list);
}
SaveData();
}
private void Unload() {
foreach (var player in BasePlayer.activePlayerList) {
CuiHelper.DestroyUi(player, MainPanel);
CuiHelper.DestroyUi(player, ButtonPanel);
}
SaveData();
}
private void OnServerShutdown() {
// удаляем все пиратские ящики, что бы после рестарта иметь возможность заново зареспавнить ящики с не нулевым количеством в точках спавна
var boxes = BaseNetworkable.serverEntities.OfType<StorageContainer>().Where(x=> x != null && x.skinID == configData.BoxSkinID).ToList();
foreach (var box in boxes) if (box != null && !box.IsDestroyed) box.Kill();
data.RockPirateBoxes.Clear();
data.ShipPirateBoxes.Clear();
SaveData();
}
private void OnLootEntity(BasePlayer player, StorageContainer entity) {
if (entity == null || entity.skinID != configData.BoxSkinID) return;
ComponentPlus?.Call("ChangeContLoot", player, entity, true);
}
private bool? OnLootSpawn(LootContainer container) {
if (container == null || !(container.ShortPrefabName == "crate_underwater_basic")) return null;
FillLoot(container);
return false;
}
private object OnItemSplit(Item item, int split_Amount) {
if (item == null) return null;
if (item.skin == configData.GoldSkinID) {
var byItemId = ItemManager.CreateByName(configData.GoldItemName, 1, configData.GoldSkinID);
item.amount -= split_Amount;
byItemId.amount = split_Amount;
byItemId.name = item.name;
item.MarkDirty();
return byItemId;
}
if (item.skin == configData.KeySkinID) {
var byItemId = ItemManager.CreateByName(configData.KeyItemName, 1, configData.KeySkinID);
item.amount -= split_Amount;
byItemId.amount = split_Amount;
byItemId.name = item.name;
item.MarkDirty();
return byItemId;
}
return null;
}
private bool? CanCombineDroppedItem(DroppedItem drItem, DroppedItem anotherDrItem) {
var item = drItem.item;
var anotherItem = anotherDrItem.item;
return CanStackItem(item, anotherItem);
}
private bool? CanStackItem(Item item, Item anotherItem) {
if (item.info.shortname == configData.GoldItemName && anotherItem.info.shortname == configData.GoldItemName) {
if (item.skin == configData.GoldSkinID && anotherItem.skin != configData.GoldSkinID) return false;
if (anotherItem.skin == configData.GoldSkinID && item.skin != configData.GoldSkinID) return false;
if (item.skin == 0 && anotherItem.skin == 0) // запрещаем стак угля, т.к.он всегда по 1 штуке стакается
return false;
}
if (item.info.shortname == configData.KeyItemName && anotherItem.info.shortname == configData.KeyItemName) {
if (item.skin == configData.KeySkinID && anotherItem.skin != configData.KeySkinID) return false;
if (anotherItem.skin == configData.KeySkinID && item.skin != configData.KeySkinID) return false;
}
return null;
}
private bool? CanPickupEntity(BasePlayer player, StorageContainer entity) {
if (entity == null) return null;
if (entity.skinID == configData.BoxSkinID) return false;
return null;
}
private void OnEntityTakeDamage(StorageContainer entity, HitInfo info) {
if (entity == null || info == null || entity.skinID != configData.BoxSkinID) return;
info.damageTypes.ScaleAll(0);
}
/*private void OnEntityKill(StorageContainer entity, HitInfo info)
{
if (entity == null || info == null || entity.skinID != configData.BoxSkinID) return;
foreach (var item in entity.inventory.itemList.ToList())
{
if (item != null)
{
item.RemoveFromContainer();
item.Remove(0f);
}
}
}*/
private bool? CanDeployItem(BasePlayer player, Deployer deployer, uint entityId) {
if (entityId == 0) return null;
foreach (var pair in data.ShipPirateBoxes) if (pair.Value.Contains(entityId)) return false;
foreach (var pair in data.RockPirateBoxes) if (pair.Value.Contains(entityId)) return false;
return null;
}
private void OnPlayerLootEnd(PlayerLoot inventory) {
if (inventory?.entitySource == null) return;
if (inventory.entitySource.skinID == configData.BoxSkinID) {
var box = inventory?.entitySource as StorageContainer;
if (box != null && !box.IsDestroyed && box.inventory.itemList.Count == 0) box.Die();
return;
}
if (inventory.entitySource is NPCVendingMachine) {
var player = inventory.GetComponent<BasePlayer>();
if (player != null) {
CuiHelper.DestroyUi(player, ButtonPanel);
try {
OpenButtons.Remove(player.userID);
}
catch {
}
}
}
}
// кривой хук, он запретит работу рекуклера, даже если там есть разрешенные к переработке предметы
private bool? CanRecycle(Recycler rec, Item item) {
if (rec == null || item == null) return null;
if (item.skin == configData.GoldSkinID || item.skin == configData.KeySkinID) return false;
return null;
}
private bool? CanLootEntity(BasePlayer player, StorageContainer cont) {
if (player == null || cont == null || cont.skinID != configData.BoxSkinID) return null;
var slot = cont.GetSlot(BaseEntity.Slot.Lock) as KeyLock;
if (slot == null) return null;
var item = player.GetActiveItem();
if (item == null || item.skin != configData.KeySkinID) {
SendReply(player, "Что бы открыть пиратский ящик, у вас в активном слоте должна быть отмычка.");
return false;
}
item.UseItem();
Effect.server.Run("assets/bundled/prefabs/fx/item_break.prefab", player, 0, new Vector3(0f, 1.5f, 0f), Vector3.zero, null, false);
slot.Kill();
if (!data.GoldMiners.ContainsKey(player.userID)) data.GoldMiners.Add(player.userID, 0);
data.GoldMiners[player.userID] += cont.inventory.itemList.Where(x=> x.info.shortname == configData.GoldItemName && x.skin == configData.GoldSkinID).Sum(x=> x.amount);
return null;
}
private void CanUseVending(BasePlayer player, NPCVendingMachine vending) {
if (!(vending.shopName == configData.ShopName)) return;
DrawButton(player);
}
#endregion
#region Loot
private void FillLoot(LootContainer cont) {
if (cont == null || data.TouchedLoot.Contains(cont.net.ID)) return;
data.TouchedLoot.Add(cont.net.ID);
cont.inventory.Clear();
ItemManager.DoRemoves();
cont.PopulateLoot();
var needSpawn = Rnd.Next(0, 1001)/10f <= configData.SpawnKeyPercent;
if (!needSpawn) return;
var item = ItemManager.CreateByName(configData.KeyItemName, 1, configData.KeySkinID);
item.name = configData.KeyText;
if (cont.inventory.capacity < cont.inventory.itemList.Count()+1) cont.inventory.capacity++;
item.MoveToContainer(cont.inventory, -1, true);
}
private List<BlackItem> GetCurrentBlackItems() {
var tmpWipeDt = SaveRestore.SaveCreatedTime.ToLocalTime();
var wipeDt = new DateTime(tmpWipeDt.Year, tmpWipeDt.Month, tmpWipeDt.Day, configData.TriggerHour, 0, 0);
var index = (DateTime.Now - wipeDt).Days;
var item = data.GoldGoods.ElementAtOrDefault(index);
return item == null ? data.GoldGoods.LastOrDefault() : item;
}
#endregion
#region PirateBoxes
private void FillPirateLoot(StorageContainer cont) {
if (cont == null) return;
foreach (var pack2 in configData.LootPacks) {
var pack3 = pack2.OrderBy(x=> Rnd.Next()).FirstOrDefault();
var loot = pack3.OrderBy(x=> Rnd.Next()).FirstOrDefault();
var itemName = loot.LootName == "{GOLD}" ? configData.GoldItemName : loot.LootName;
var itemText = loot.LootName == "{GOLD}" ? configData.GoldText : "";
var itemSkin = loot.LootName == "{GOLD}" ? configData.GoldSkinID : 0ul;
var itemAmount = Rnd.Next(loot.MinAmount, loot.MaxAmount+1);
if (itemAmount <= 0) continue;
var item = ItemManager.CreateByName(itemName, itemAmount, itemSkin);
if (!string.IsNullOrEmpty(itemText)) item.name = itemText;
item.MoveToContainer(cont.inventory, -1, true);
}
cont.inventory.capacity = cont.inventory.itemList.Count;
cont.inventorySlots = cont.inventory.itemList.Count;
cont.inventory.SetFlag(ItemContainer.Flag.NoItemInput, true);
cont.inventory.MarkDirty();
}
private void SpawnPirateBoxes() {
CommunityEntity.ServerInstance.StartCoroutine(RunRefillPirateLoot());
timer.Once(configData.RespawnMinutes * 60f, SpawnPirateBoxes);
}
private IEnumerator RunRefillPirateLoot() {
int count = 0;
//Puts("RunRefillPirateLoot Начало");
var devs = BaseNetworkable.serverEntities.OfType<DiveSite>().ToList();
var mons = new List<Transform>();
foreach (var mon_ in GameObject.FindObjectsOfType<Transform>()) {
if (mon_ != null && mon_.ToString().Contains("rock_formation") && mon_.ToString().Contains("_underwater")) mons.Add(mon_);
if (count++ % 1000 == 0) yield return null;
}
var boxes = BaseNetworkable.serverEntities.OfType<StorageContainer>().Where(x=> x != null && x.skinID == configData.BoxSkinID).ToList();
// чистка списка пиратских ящиков от удаленных-залутаных ящиков (катера и баржи)
count = 0;
var newShipPirateBoxes = new Dictionary<uint, List<uint>>();
foreach (var pair in data.ShipPirateBoxes) {
foreach (var boxID in pair.Value) {
if (boxID == 0) {
if (!newShipPirateBoxes.ContainsKey(pair.Key)) newShipPirateBoxes.Add(pair.Key, new List<uint>());
newShipPirateBoxes[pair.Key].Add(boxID);
}
else {
var box = boxes.FirstOrDefault(x=> x != null && x.net != null && x.net.ID == boxID);
if (box != null && !box.IsDestroyed) {
if (!newShipPirateBoxes.ContainsKey(pair.Key)) newShipPirateBoxes.Add(pair.Key, new List<uint>());
newShipPirateBoxes[pair.Key].Add(boxID);
}
yield return null;
}
}
}
// чистка списка пиратских ящиков от удаленных-залутаных ящиков (скалы)
count = 0;
var newRockPirateBoxes = new Dictionary<string, List<uint>>();
foreach (var pair in data.RockPirateBoxes) {
foreach (var boxID in pair.Value) {
if (boxID == 0) {
if (!newRockPirateBoxes.ContainsKey(pair.Key)) newRockPirateBoxes.Add(pair.Key, new List<uint>());
newRockPirateBoxes[pair.Key].Add(boxID);
}
else {
var box = boxes.FirstOrDefault(x=> x != null && x.net != null && x.net.ID == boxID);
if (box != null && !box.IsDestroyed) {
if (!newRockPirateBoxes.ContainsKey(pair.Key)) newRockPirateBoxes.Add(pair.Key, new List<uint>());
newRockPirateBoxes[pair.Key].Add(boxID);
}
yield return null;
}
}
}
// чистка пиратских ящиков без точки спавна, только катера и баржи (а скалы не надо, они всегда существуют)
count = 0;
var newShipPirateBoxes2 = new Dictionary<uint, List<uint>>();
foreach (var pair in newShipPirateBoxes) {
var exists = false;
foreach (var dev in devs) {
if (dev == null || dev.IsDestroyed) continue;
if (pair.Key == dev.net.ID) {
exists = true;
break;
}
if (count++ % 100 == 0) yield return null;
}
if (!exists) {
foreach (var boxID in pair.Value.Where(x=> x > 0)) {
var box = boxes.FirstOrDefault(x=> x != null && x.net != null && x.net.ID == boxID);
if (box != null && !box.IsDestroyed) {
box.Kill();
yield return new WaitForSeconds(0.1f);
}
else if (count++ % 50 == 0) yield return null;
}
}
else newShipPirateBoxes2.Add(pair.Key, pair.Value);
}
// добавляем новые ящики к новым точкам спавна (катера и баржи)
foreach (var dev in devs) {
if (dev == null || dev.IsDestroyed) continue;
var devID = dev.net.ID;
if (!newShipPirateBoxes2.ContainsKey(devID)) {
newShipPirateBoxes2.Add(devID, new List<uint>());
var cfg = configData.SpawnLimits[dev.ShortPrefabName == "divesite_b" ? "баржа" : "катер"];
var minAmount = Convert.ToInt32(cfg.Split('|')[0]);
var maxAmount = Convert.ToInt32(cfg.Split('|')[1]);
var amount = Rnd.Next(minAmount, maxAmount+1);
if (amount <= 0) newShipPirateBoxes2[devID].Add(0);
else for (int ii = 0; ii < amount; ii++) {
var box = SpawnPirateBox(dev.transform.position, configData.SpawnRadius[dev.ShortPrefabName == "divesite_b" ? "баржа" : "катер"], configData.SpawnDelta[dev.ShortPrefabName == "divesite_b" ? "баржа" : "катер"]);
if (box != null) {
newShipPirateBoxes2[devID].Add(box.net.ID);
yield return new WaitForSeconds(0.02f);
}
}
if (newShipPirateBoxes2[devID].Count == 0) newShipPirateBoxes2.Remove(devID);
}
}
// добавляем новые ящики к новым точкам спавна (скалы)
count = 0;
foreach (var mon in mons) {
var monPos = $"{mon.position.x} {mon.position.y} {mon.position.z}";
if (!newRockPirateBoxes.ContainsKey(monPos)) {
newRockPirateBoxes.Add(monPos, new List<uint>());
var cfg = configData.SpawnLimits["скала"];
var minAmount = Convert.ToInt32(cfg.Split('|')[0]);
var maxAmount = Convert.ToInt32(cfg.Split('|')[1]);
var amount = Rnd.Next(minAmount, maxAmount+1);
if (amount <= 0) newRockPirateBoxes[monPos].Add(0);
else for (int ii = 0; ii < amount; ii++) {
var box = SpawnPirateBox(mon.position, configData.SpawnRadius["скала"], configData.SpawnDelta["скала"]);
if (box != null) {
newRockPirateBoxes[monPos].Add(box.net.ID);
yield return new WaitForSeconds(0.1f);
}
}
if (newRockPirateBoxes[monPos].Count == 0) newRockPirateBoxes.Remove(monPos);
}
yield return null;
}
try {
data.ShipPirateBoxes = newShipPirateBoxes2;
}
catch {
}
try {
data.RockPirateBoxes = newRockPirateBoxes;
}
catch {
}
//Puts("RunRefillPirateLoot Конец");
}
private StorageContainer SpawnPirateBox(Vector3 pos, float radius, float delta) {
Vector3 newPos = default(Vector3);
for (int ii = 0; ii < 100; ii++) {
var tmpPos = FindNewPosition(pos, radius);
if (tmpPos == default(Vector3)) continue;
newPos = tmpPos;
break;
}
if (newPos == default(Vector3)) return null;
newPos = new Vector3(newPos.x, newPos.y + delta, newPos.z);
var storage = GameManager.server.CreateEntity("assets/prefabs/deployable/large wood storage/box.wooden.large.prefab", newPos, Quaternion.Euler(0, UnityEngine.Random.Range(0.0f, 360.0f), 0)) as StorageContainer;
storage.skinID = configData.BoxSkinID;
storage.dropChance = 0f;
if (storage == null) return null;
storage.Spawn();
timer.Once(0.1f, ()=> {
if (storage != null) {
var code = GameManager.server.CreateEntity("assets/prefabs/locks/keylock/lock.key.prefab") as KeyLock;
if (code != null) {
code.Spawn();
code.SetParent(storage, storage.GetSlotAnchorName(BaseEntity.Slot.Lock));
storage.SetSlot(BaseEntity.Slot.Lock, code);
code.SetFlag(BaseEntity.Flags.Locked, true);
}
FillPirateLoot(storage);
}
}
);
return storage;
}
private Vector3 FindNewPosition(Vector3 position, float radius) {
var targetPos = UnityEngine.Random.insideUnitCircle * radius;
var sourcePos = new Vector3(position.x + targetPos.x, 300, position.z + targetPos.y);
var hits = UnityEngine.Physics.SphereCastAll(sourcePos, 1.5f, Vector3.down, 500f);
foreach (var hit in hits.OrderBy(x=> x.distance)) {
var name = hit.collider?.name?.ToString();
if (name.Contains("Collider")) continue;
if (name.Contains("Terrain")) return new Vector3(sourcePos.x, hit.point.y, sourcePos.z);
return default(Vector3);
}
return default(Vector3);
}
private string GetPlayerName(ulong userID) {
var data = permission.GetUserData(userID.ToString());
return data.LastSeenNickname;
}
#endregion
#region Images
private static Dictionary<string, string> Images = new Dictionary<string, string>();
private static bool AreImagesLoaded() => Images.Count >= 1;
private void DownloadImages() {
ServerMgr.Instance.StartCoroutine(DownloadImage(configData.GoldURL));
}
private IEnumerator DownloadImage(string url) {
using (var www = new WWW(url)) {
yield return www;
if (www.error != null) PrintWarning($"Ошибка добавления изображения.Неверная ссылка на изображение:\n {url}");
else {
var tex = www.texture;
byte[] bytes = tex.EncodeToPNG();
var image = FileStorage.server.Store(bytes, FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID).ToString();
if (!Images.ContainsKey(url)) Images.Add(url, image);
else Images[url] = image;
UnityEngine.Object.DestroyImmediate(tex);
yield break;
}
}
}
#endregion
#region Test
[ChatCommand("pg.loot")]
private void cmdBox(BasePlayer player, string command, string[] args) {
if (player == null || !player.IsAdmin) return;
var boxes = BaseNetworkable.serverEntities.OfType<StorageContainer>().Where(x=> x != null && x.skinID == configData.BoxSkinID).ToList();
foreach (var box in boxes) player.SendConsoleCommand("ddraw.text", 60f, Color.magenta, box.transform.position + new Vector3(0f, 2f, 0f), "ТУТ");
}
[ChatCommand("pg.give")]
private void cmdBox2(BasePlayer player, string command, string[] args) {
if (player == null || !player.IsAdmin) return;
var item = ItemManager.CreateByName(configData.KeyItemName, 10, configData.KeySkinID);
item.name = configData.KeyText;
player.GiveItem(item);
var item2 = ItemManager.CreateByName(configData.GoldItemName, 100, configData.GoldSkinID);
item2.name = configData.GoldText;
player.GiveItem(item2);
}
[ConsoleCommand("pg.lock")]
private void consoleLock(ConsoleSystem.Arg args) {
if (args?.Player() != null) return;
var boxes = BaseNetworkable.serverEntities.OfType<StorageContainer>().Where(x=> x != null && x.skinID == configData.BoxSkinID).ToList();
foreach (var loot in boxes.Where(x=> x.GetSlot(BaseEntity.Slot.Lock) == null)) {
Puts(loot.transform.position.ToString());
}
}
[ChatCommand("gold_top")]
private void CommandGoldTop(BasePlayer player, string command, string[] args) {
if (player == null) return;
int num = 0;
var result = "ТОП собирателей золота за текущий вайп:\n";
foreach (var pair in data.GoldMiners.OrderByDescending(x=> x.Value)) {
result += $"{++num}."+"<color=#56f0f0>" + GetPlayerName(pair.Key) + "</color>" + $": {pair.Value} шт\n";
if (num >= 10) break;
}
SendReply(player, "<size=15>"+result+"</size>");
}
#endregion
#region GUI + Button
private void CheckButtonClose() {
try {
foreach (var userID in OpenButtons.ToList()) {
var player = BasePlayer.FindByID(userID);
if (player == null) OpenButtons.Remove(userID);
else if ((player.transform.position-ShopPos).sqrMagnitude > 16f) {
CuiHelper.DestroyUi(player, ButtonPanel);
OpenButtons.Remove(userID);
}
}
}
catch {
}
timer.Once(5f, CheckButtonClose);
}
private void DrawButton(BasePlayer player) {
if (player == null) return;
var container = new CuiElementContainer();
UI_MainPanel(ref container, "0 0 0 0", "0.73 0.78", "0.87 0.84", false, false, false, ButtonPanel);
UI_Button(ref container, "0.45 0.55 0.28 0.99", "<color=#b3f03d>КОНТРАБАНДА</color>", 18, "0 0", "1 1", "pg_349dfg.open", "robotocondensed-bold.ttf", TextAnchor.MiddleCenter, ButtonPanel);
CuiHelper.DestroyUi(player, ButtonPanel);
CuiHelper.AddUi(player, container);
if (!OpenButtons.Contains(player.userID)) OpenButtons.Add(player.userID);
}
[ConsoleCommand("pg_349dfg.open")]
private void cmdGUIOpen(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Connection.player as BasePlayer;
if (player == null) return;
//CuiHelper.DestroyUi(player, ButtonPanel);
//try { OpenButtons.Remove(player.userID); } catch {}
player.EndLooting();
DrawPanel(player);
}
#endregion
#region GUI + Shop
private void DrawPanel(BasePlayer player, bool refresh = false) {
if (player == null || !AreImagesLoaded()) return;
var container = new CuiElementContainer();
float minY = 217, deltaY = 3f, sizeY = 90f;
if (!refresh) {
UI_MainPanel(ref container, "0 0 0 0", "0 0", "1 1", false, true, false, MainPanel);
UI_Button(ref container, "0 0 0 0", "", 15, "0 0", "1 1", "pg_349dfg.close", "robotocondensed-regular.ttf", TextAnchor.MiddleCenter, MainPanel);
UI_Panel(ref container, "0.36 0.34 0.32 0.95", "0.5 0.5", "0.5 0.5", MainPanel, "PG_Shop", $"-205 {minY - GetCurrentBlackItems().Count * (sizeY + deltaY) - deltaY+1}", "205 255", 0.1f, 0.1f);
UI_Panel(ref container, "0 0 0 0", "0.5 0.5", "0.5 0.5", MainPanel, "PG_Shop_Title", "-200 220", "200 250", 0.1f, 0.1f);
UI_FLabel(ref container, "<color=#CFCFCF>КОНТРАБАНДНЫЕ ТОВАРЫ ЗА ЗОЛОТО</color>", "0 0 0 0.1", 15, "-0.1 -0.1", "1.1 1.1", 0.1f, 0.1f, TextAnchor.MiddleCenter, "robotocondensed-bold.ttf", "PG_Shop_Title");
}
var goldAmount = GetGoldAmount(player);
int num = 0;
foreach (var item in GetCurrentBlackItems()) {
if (!refresh) {
var png = (string)ImageLibrary?.Call("GetImage", item.ItemName);
UI_Panel(ref container, "0.27 0.25 0.22 0.85", "0.5 0.5", "0.5 0.5", MainPanel, $"PG_Shop_{num}", $"-200 {minY-sizeY}", $"200 {minY}", 0.1f, 0.1f);
if (!string.IsNullOrEmpty(png)) UI_Image(ref container, png, "0.025 0.1", "0.21 0.9", 0.1f, 0.1f, $"PG_Shop_{num}");
else UI_FLabel(ref container, $"<color=#CFCFCF>{item.ItemName}</color>", "0 0 0 0.1", 14, "0 0.1", "0.25 0.9", 0.1f, 0.1f, TextAnchor.MiddleCenter, "robotocondensed-bold.ttf", $"PG_Shop_{num}");
if (item.ItemAmount > 1) UI_FLabel(ref container, $"<color=#CFCFCF>x{item.ItemAmount}</color>", "0 0 0 0.1", 15, "0.025 0.05", "0.21 0.9", 0.1f, 0.1f, TextAnchor.LowerRight, "robotocondensed-bold.ttf", $"PG_Shop_{num}");
UI_Image(ref container, Images[configData.GoldURL], "0.36 0.1", "0.54 0.9", 0.1f, 0.1f, $"PG_Shop_{num}");
if (item.GoldAmount > 1) UI_FLabel(ref container, $"<color=#CFCFCF>x{item.GoldAmount}</color>", "0 0 0 0.1", 15, "0.36 0.05", "0.54 0.9", 0.1f, 0.1f, TextAnchor.LowerRight, "robotocondensed-bold.ttf", $"PG_Shop_{num}");
}
else CuiHelper.DestroyUi(player, $"PG_Shop_{num}_button");
if (goldAmount >= item.GoldAmount) UI_Button(ref container, "0.45 0.55 0.28 0.99", "<color=#b3f03d>КУПИТЬ</color>", 15, "0.75 0.3", "0.95 0.7", $"pg_349dfg.buy {num}", "robotocondensed-bold.ttf", TextAnchor.MiddleCenter, $"PG_Shop_{num}", $"PG_Shop_{num}_button", 0f, 0f);
else UI_Button(ref container, "0.33 0.20 0.16 0.99", "<color=#c03f36>не хватает\nзолота</color>", 13, "0.75 0.3", "0.95 0.7", "pg_349dfg.empty", "robotocondensed-bold.ttf", TextAnchor.MiddleCenter, $"PG_Shop_{num}", $"PG_Shop_{num}_button", 0f, 0f);
num++;
minY -= sizeY + deltaY;
}
if (!refresh) CuiHelper.DestroyUi(player, MainPanel);
CuiHelper.AddUi(player, container);
}
[ConsoleCommand("pg_349dfg.close")]
private void cmdGUIClose(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Connection.player as BasePlayer;
if (player == null) return;
CuiHelper.DestroyUi(player, MainPanel);
}
[ConsoleCommand("pg_349dfg.buy")]
private void cmdGUIBuy(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Connection.player as BasePlayer;
if (player == null) return;
int num = Convert.ToInt32(arg.Args[0]);
var item = GetCurrentBlackItems()[num];
var goldAmount = GetGoldAmount(player);
if (goldAmount >= item.GoldAmount) {
ProcessBuy(player, item);
DrawPanel(player, true);
ShowMessage(player, "Покупка успешно совершена.", true);
}
else {
DrawPanel(player, true);
ShowMessage(player, "У вас не хватает золота.", false);
}
}
private static int GetGoldAmount(BasePlayer player) {
if (player == null) return 0;
int amount = 0;
foreach (var item in player.inventory.AllItems()) {
if (item.info.shortname == configData.GoldItemName && item.skin == configData.GoldSkinID) amount += item.amount;
}
return amount;
}
private static void ProcessBuy(BasePlayer player, BlackItem item) {
var newItem = ItemManager.CreateByName(item.ItemName, item.ItemAmount);
int need = item.GoldAmount;
foreach (var invItem in player.inventory.AllItems()) {
if (invItem.info.shortname == configData.GoldItemName && invItem.skin == configData.GoldSkinID) {
if (invItem.amount > need) {
invItem.amount -= need;
invItem.MarkDirty();
break;
}
else if (invItem.amount == need) {
invItem.RemoveFromContainer();
invItem.Remove(0f);
break;
}
else {
need -= invItem.amount;
invItem.RemoveFromContainer();
invItem.Remove(0f);
}
}
}
player.GiveItem(newItem);
}
#endregion
#region GUI Message
private static Dictionary<ulong, Timer> SMTimer = new Dictionary<ulong, Timer>();
private void ShowMessage(BasePlayer player, string message, bool isOk) {
if (player == null || string.IsNullOrEmpty(message)) return;
ClearMessages(player);
var container = new CuiElementContainer();
if (isOk) UI_Panel(ref container, "0.5 0.5 0.5 0.9", "0.35 0.87", "0.65 0.93", MainPanel, MainPanel + "_sm_container");
else UI_Panel(ref container, "0.6 0.1 0.1 0.9", "0.35 0.87", "0.65 0.93", MainPanel, MainPanel + "_sm_container");
UI_Label(ref container, message, 18, "0 0", "1 1", 0.1f, 0.1f, TextAnchor.MiddleCenter, "robotocondensed-bold.ttf", MainPanel + "_sm_container");
CuiHelper.AddUi(player, container);
if (!SMTimer.ContainsKey(player.userID)) SMTimer.Add(player.userID, null);
SMTimer[player.userID] = timer.Once(5f, ()=> CuiHelper.DestroyUi(player, MainPanel + "_sm_container"));
}
private static void ClearMessages(BasePlayer player) {
if (player == null) return;
if (SMTimer.ContainsKey(player.userID) && SMTimer[player.userID] != null) {
SMTimer[player.userID].Destroy();
SMTimer[player.userID] = null;
}
CuiHelper.DestroyUi(player, MainPanel + "_sm_container");
}
#endregion
#region GUI Helpers
private const string MainPanel = "PG_MainPanel";
private const string ButtonPanel = "PG_ButtonPanel";
private static void UI_MainPanel(ref CuiElementContainer container, string color, string aMin, string aMax, bool isHud = true, bool isNeedCursor = true, bool isBlur = false, string panel = MainPanel, string oMin = "0.0 0.0", string oMax = "0.0 0.0") {
container.Add(new CuiPanel {
Image = {
Color = color, Material = isBlur ? "assets/content/ui/uibackgroundblur.mat" : "Assets/Icons/IconMaterial.mat"
}
, RectTransform = {
AnchorMin = aMin, AnchorMax = aMax, OffsetMin = oMin, OffsetMax = oMax
}
, CursorEnabled = isNeedCursor
}
, isHud ? "Hud" : "Overlay", panel);
}
private static void UI_Panel(ref CuiElementContainer container, string color, string aMin, string aMax, string panel = MainPanel, string name = null, string oMin = "0.0 0.0", string oMax = "0.0 0.0", float fadeIn = 0f, float fadeOut = 0f) {
container.Add(new CuiPanel {
FadeOut = fadeOut, Image = {
Color = color, FadeIn = fadeIn, Material = "Assets/Content/UI/UI.Background.Tile.psd"
}
, RectTransform = {
AnchorMin = aMin, AnchorMax = aMax, OffsetMin = oMin, OffsetMax = oMax
}
}
, panel, name);
}
private static void UI_Label(ref CuiElementContainer container, string text, int size, string aMin, string aMax, float fadeIn = 0f, float fadeOut = 0f, TextAnchor align = TextAnchor.MiddleCenter, string font = "robotocondensed-regular.ttf", string panel = MainPanel, string name = null) {
container.Add(new CuiElement {
Name = !string.IsNullOrEmpty(name) ? name : CuiHelper.GetGuid(), Parent = panel, FadeOut = fadeOut, Components = {
new CuiTextComponent {
FontSize = size, Align = align, Text = text, Font = font, FadeIn = fadeIn
}
, new CuiRectTransformComponent {
AnchorMin = aMin, AnchorMax = aMax
}
}
}
);
}
private static void UI_FLabel(ref CuiElementContainer container, string text, string fcolor, int size, string aMin, string aMax, float fadeIn = 0f, float fadeOut = 0f, TextAnchor align = TextAnchor.MiddleCenter, string font = "robotocondensed-regular.ttf", string panel = MainPanel, string name = null) {
if (string.IsNullOrEmpty(fcolor)) fcolor = "0.0 0.0 0.0 1.0";
container.Add(new CuiElement {
Name = !string.IsNullOrEmpty(name) ? name : CuiHelper.GetGuid(), Parent = panel, FadeOut = fadeOut, Components = {
new CuiTextComponent {
FontSize = size, Align = align, Text = text, Font = font, FadeIn = fadeIn
}
, new CuiRectTransformComponent {
AnchorMin = aMin, AnchorMax = aMax
}
, new CuiOutlineComponent {
Distance = "1 1", Color = fcolor
}
}
}
);
}
private static void UI_Button(ref CuiElementContainer container, string color, string text, int size, string aMin, string aMax, string command, string font = "robotocondensed-regular.ttf", TextAnchor align = TextAnchor.MiddleCenter, string panel = MainPanel, string name = null, float fadeIn = 0f, float fadeOut = 0f) {
if (string.IsNullOrEmpty(color)) color = "0 0 0 0";
container.Add(new CuiButton {
FadeOut = fadeOut, Button = {
Color = color, Command = command, FadeIn = fadeIn, Material = "Assets/Content/UI/UI.Background.Tile.psd"
}
, RectTransform = {
AnchorMin = aMin, AnchorMax = aMax
}
, Text = {
Text = text, FontSize = size, Align = align, Font = font
}
}
, panel, name);
}
private static void UI_Image(ref CuiElementContainer container, string png, string aMin, string aMax, float fadeIn = 0f, float fadeOut = 0f, string panel = MainPanel, string name = null, string oMin = null, string oMax = null) {
container.Add(new CuiElement {
Name = !string.IsNullOrEmpty(name) ? name : CuiHelper.GetGuid(), Parent = panel, FadeOut = fadeOut, Components = {
(png.Contains("https://") || png.Contains("http://")) ? new CuiRawImageComponent {
Sprite = "assets/content/textures/generic/fulltransparent.tga", Url = png, FadeIn = fadeIn
}
: new CuiRawImageComponent {
Sprite = "assets/content/textures/generic/fulltransparent.tga", Png = png, FadeIn = fadeIn
}
, string.IsNullOrEmpty(oMin) ? new CuiRectTransformComponent {
AnchorMin = aMin, AnchorMax = aMax
}
: new CuiRectTransformComponent {
AnchorMin = aMin, AnchorMax = aMax, OffsetMin = oMin, OffsetMax = oMax
}
}
}
);
}
#endregion
#region Config
private static ConfigData configData;
private class ConfigData {
[JsonProperty(PropertyName = "Скин пиратского ящика")]
public ulong BoxSkinID;
[JsonProperty(PropertyName = "Предмет для отмычки (shortname)")]
public string KeyItemName;
[JsonProperty(PropertyName = "Скин отмычки")]
public ulong KeySkinID;
[JsonProperty(PropertyName = "Текст для отмычки")]
public string KeyText;
[JsonProperty(PropertyName = "Предмет для золота (shortname)")]
public string GoldItemName;
[JsonProperty(PropertyName = "Скин золота")]
public ulong GoldSkinID;
[JsonProperty(PropertyName = "Текст для золота")]
public string GoldText;
[JsonProperty(PropertyName = "Максимальный радиус спавна пиратских ящиков")]
public Dictionary<string, int> SpawnRadius;
[JsonProperty(PropertyName = "Вертикальная дельта спавна пиратских ящиков")]
public Dictionary<string, float> SpawnDelta;
[JsonProperty(PropertyName = "Через сколько минут заново респавнить пиратские ящики")]
public float RespawnMinutes;
[JsonProperty(PropertyName = "Сколько минимум и максимум пиратских ящиков спавнить для каждого типа локации")]
public Dictionary<string, string> SpawnLimits;
[JsonProperty(PropertyName = "Вероятность спавна отмычки в малом подводном ящике")]
public float SpawnKeyPercent;
[JsonProperty(PropertyName = "Пакеты с лутом для пиратского ящика")]
public List<List<List<Loot>>> LootPacks;
[JsonProperty(PropertyName = "Ссылка на картинку с золотом")]
public string GoldURL;
[JsonProperty(PropertyName = "Игровое название магазина торговца")]
public string ShopName;
[JsonProperty(PropertyName = "Час когда нужно переключить группу товаров (раз в день)")]
public int TriggerHour;
[JsonProperty(PropertyName = "Товары в обмен на золото")]
public List<List<BlackItem>> GoldGoods;
}
private class Loot {
[JsonProperty(PropertyName = "Название лута (shortname)")]
public string LootName;
[JsonProperty(PropertyName = "Минимум штук")]
public int MinAmount;
[JsonProperty(PropertyName = "Максимум штук")]
public int MaxAmount;
}
private class BlackItem {
[JsonProperty(PropertyName = "Название товара (shortname)")]
public string ItemName;
[JsonProperty(PropertyName = "Штук товара")]
public int ItemAmount;
[JsonProperty(PropertyName = "Стоимость золота")]
public int GoldAmount;
}
private void LoadVariables() => configData = Config.ReadObject<ConfigData>();
protected override void LoadDefaultConfig() {
configData = new ConfigData {
BoxSkinID = 1382717194, KeyItemName = "sticks", KeySkinID = 2128568391, KeyText = "Отмычка", GoldItemName = "coal", GoldSkinID = 2128569139, GoldText = "Пиратское золото", SpawnDelta = new Dictionary<string, float>() {
{
"катер", 0.75f
}
, {
"баржа", 0.75f
}
, {
"скала", 0.1f
}
}
, SpawnRadius = new Dictionary<string, int>() {
{
"катер", 10
}
, {
"баржа", 25
}
, {
"скала", 25
}
}
, RespawnMinutes = 20, SpawnLimits = new Dictionary<string, string>() {
{
"катер", "0|2"
}
, {
"баржа", "1|2"
}
, {
"скала", "1|2"
}
}
, SpawnKeyPercent = 50, GoldURL = "https://i.imgur.com/aaVdAA6.png", ShopName = "Food Market", TriggerHour = 11, GoldGoods = new List<List<BlackItem>>() {
new List<BlackItem>() {
new BlackItem() {
ItemName = "pistol.revolver", ItemAmount = 1, GoldAmount = 50
}
, new BlackItem() {
ItemName = "shotgun.double", ItemAmount = 1, GoldAmount = 70
}
, new BlackItem() {
ItemName = "pistol.semiauto", ItemAmount = 1, GoldAmount = 100
}
, new BlackItem() {
ItemName = "pistol.python", ItemAmount = 1, GoldAmount = 100
}
, new BlackItem() {
ItemName = "grenade.beancan", ItemAmount = 1, GoldAmount = 80
}
, new BlackItem() {
ItemName = "bed", ItemAmount = 1, GoldAmount = 50
}
, new BlackItem() {
ItemName = "scrap", ItemAmount = 100, GoldAmount = 100
}
, new BlackItem() {
ItemName = "ladder.wooden.wall", ItemAmount = 1, GoldAmount = 70
}
, new BlackItem() {
ItemName = "wall.frame.garagedoor", ItemAmount = 1, GoldAmount = 150
}
, new BlackItem() {
ItemName = "wall.window.glass.reinforced", ItemAmount = 1, GoldAmount = 70
}
}
, new List<BlackItem>() {
new BlackItem() {
ItemName = "pistol.semiauto", ItemAmount = 1, GoldAmount = 100
}
, new BlackItem() {
ItemName = "pistol.python", ItemAmount = 1, GoldAmount = 100
}
, new BlackItem() {
ItemName = "smg.thompson", ItemAmount = 1, GoldAmount = 150
}
, new BlackItem() {
ItemName = "rifle.semiauto", ItemAmount = 1, GoldAmount = 150
}
, new BlackItem() {
ItemName = "explosive.satchel", ItemAmount = 1, GoldAmount = 200
}
, new BlackItem() {
ItemName = "flamethrower", ItemAmount = 1, GoldAmount = 150
}
, new BlackItem() {
ItemName = "wall.frame.garagedoor", ItemAmount = 1, GoldAmount = 150
}
, new BlackItem() {
ItemName = "floor.ladder.hatch", ItemAmount = 1, GoldAmount = 100
}
, new BlackItem() {
ItemName = "floor.triangle.ladder.hatch", ItemAmount = 1, GoldAmount = 100
}
, new BlackItem() {
ItemName = "scrap", ItemAmount = 100, GoldAmount = 100
}
}
, new List<BlackItem>() {
// ... [Code preview truncated due to length. Full source in attached file] ...