C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.RegularExpressions;
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Oxide.Plugins.ShopExtensionMethods;
using UnityEngine;
using Debug = UnityEngine.Debug;
using Random = UnityEngine.Random;
namespace Oxide.Plugins
{
[Info("Shop", "Sempai#3239", "1.2.12")]
public class Shop : RustPlugin
{
#region Fields
[PluginReference] private Plugin
ImageLibrary = null,
ItemCostCalculator = null,
Notify = null,
UINotify = null,
NoEscape = null,
Duel = null,
Duelist = null;
private static Shop _instance;
private bool _enabledImageLibrary;
private readonly Dictionary<int, ShopItem> _shopItems = new Dictionary<int, ShopItem>();
private readonly Dictionary<BasePlayer, Coroutine> _coroutines = new Dictionary<BasePlayer, Coroutine>();
private readonly Dictionary<string, List<KeyValuePair<int, string>>> _itemsCategories =
new Dictionary<string, List<KeyValuePair<int, string>>>();
private readonly List<string> _images = new List<string>();
private List<ulong> _openedUI = new List<ulong>();
private const string Layer = "UI.Shop";
private const string ModalLayer = "UI.Shop.Modal";
private const string EditingLayer = "UI.Shop.Editing";
private const string PermAdmin = "Shop.admin";
private const string PermFreeBypass = "Shop.free";
private const string PermSetVM = "Shop.setvm";
private const string PermSetNPC = "Shop.setnpc";
private const int _itemsPerTick = 10;
private readonly Dictionary<ulong, DataCart> _carts = new Dictionary<ulong, DataCart>();
private readonly Dictionary<ulong, Dictionary<string, DataCart>> _cartsNPC =
new Dictionary<ulong, Dictionary<string, DataCart>>();
private readonly Dictionary<BasePlayer, NPCShop> _openedShops = new Dictionary<BasePlayer, NPCShop>();
private readonly Dictionary<BasePlayer, Dictionary<string, object>> _itemEditing =
new Dictionary<BasePlayer, Dictionary<string, object>>();
private Timer _updateController;
#endregion
#region Colors
private string _firstColor;
private string _secondColor;
private string _thirdColor;
private string _fourthColor;
private string _fifthColor;
private string _sixthColor;
private string _seventhColor;
#endregion
#region Config
private static Configuration _config;
private class Configuration
{
#region Fields
[JsonProperty(PropertyName = "Commands", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public string[] Commands = {"shop", "shops"};
[JsonProperty(PropertyName = "Enable money transfers between players??")]
public bool Transfer = true;
[JsonProperty(PropertyName = "Enable logging to the console?")]
public bool LogToConsole = true;
[JsonProperty(PropertyName = "Enable logging to the file?")]
public bool LogToFile = true;
[JsonProperty(PropertyName = "Load images when logging into the server?")]
public bool LoginImages = true;
[JsonProperty(PropertyName = "Work with Notify?")]
public bool UseNotify = true;
[JsonProperty(PropertyName = "Can admins edit? (by flag)")]
public bool FlagAdmin = true;
[JsonProperty(PropertyName = "Block (NoEscape)")]
public bool BlockNoEscape = false;
[JsonProperty(PropertyName = "Wipe Block")]
public bool WipeCooldown = false;
[JsonProperty(PropertyName = "Wipe Cooldown")]
public float WipeCooldownTimer = 3600;
[JsonProperty(PropertyName = "Respawn Block")]
public bool RespawnCooldown = true;
[JsonProperty(PropertyName = "Respawn Cooldown")]
public float RespawnCooldownTimer = 60;
[JsonProperty(PropertyName = "Blocking the opening in duels?")]
public bool UseDuels = false;
[JsonProperty(PropertyName = "Delay between loading images")]
public float ImagesDelay = 1f;
[JsonProperty(PropertyName = "Economy")]
public EconomyConf Economy = new EconomyConf
{
Type = EconomyType.Plugin,
AddHook = "Deposit",
BalanceHook = "Balance",
RemoveHook = "Withdraw",
Plug = "Economics",
ShortName = "scrap",
DisplayName = string.Empty,
Skin = 0,
TitleLangKey = "LangTitle",
BalanceLangKey = "BalanceTitle"
};
[JsonProperty(PropertyName = "Additional Economics",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<AdditionalEconomy> AdditionalEconomics = new List<AdditionalEconomy>
{
new AdditionalEconomy
{
ID = 1,
Enabled = true,
Type = EconomyType.Plugin,
AddHook = "AddPoints",
BalanceHook = "CheckPoints",
RemoveHook = "TakePoints",
Plug = "ServerRewards",
ShortName = "scrap",
DisplayName = string.Empty,
Skin = 0,
TitleLangKey = "sr_title",
BalanceLangKey = "sr_balance"
}
};
[JsonProperty(PropertyName = "Shop", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ShopCategory> Shop = new List<ShopCategory>();
[JsonProperty(PropertyName = "First Color")]
public string FirstColor = "#161617";
[JsonProperty(PropertyName = "Second Color")]
public string SecondColor = "#4B68FF";
[JsonProperty(PropertyName = "Third Color")]
public string ThirdColor = "#0E0E10";
[JsonProperty(PropertyName = "Fourth Color")]
public string FourthColor = "#A0A935";
[JsonProperty(PropertyName = "Fifth Color")]
public string FifthColor = "#FF4B4B";
[JsonProperty(PropertyName = "Sixth Color")]
public string SixthColor = "#324192";
[JsonProperty(PropertyName = "Seventh Color")]
public string SeventhColor = "#CD3838";
[JsonProperty(PropertyName = "NPC Shops (NPC ID - shop categories)",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, NPCShop> NPCs = new Dictionary<string, NPCShop>
{
["1234567"] = new NPCShop
{
Shops = new List<string>
{
"Tool",
"Food"
}
},
["7654321"] = new NPCShop
{
Shops = new List<string>
{
"Weapon",
"Ammunition"
}
},
["4644687478"] = new NPCShop
{
Shops = new List<string>
{
"*"
}
}
};
[JsonProperty(PropertyName = "Interface")]
public UserInterface UI = new UserInterface
{
DisplayType = "Overlay",
Width = 770,
Height = 500,
CategoriesOnString = 9,
CategoriesMargin = 7.5f,
CategoriesHeight = 40,
ItemsOnString = 4,
Strings = 2,
ItemWidth = 150,
ItemHeight = 165,
Margin = 35,
UseScrollCategories = true,
EnableSearch = true,
RoundDigits = 5,
SelectCurrency = new SelectCurrencyUI
{
Title = new LabelSettings
{
Align = TextAnchor.MiddleCenter,
Font = "robotocondensed-bold.ttf",
FontSize = 14,
Color = new IColor("#FFFFFF")
},
TitlePosition = new InterfacePosition
{
AnchorMin = "0 1", AnchorMax = "1 1",
OffsetMin = "0 -30", OffsetMax = "0 0"
},
EconomyTitle = new LabelSettings
{
Align = TextAnchor.MiddleCenter,
Font = "robotocondensed-regular.ttf",
FontSize = 12,
Color = new IColor("#FFFFFF")
},
SelectedEconomyColor = new IColor("#4B68FF"),
UnselectedEconomyColor = new IColor("#4B68FF", 33),
EconomyWidth = 105f,
EconomyHeight = 25f,
EconomyMargin = 5f,
EconomyIndent = 5f,
FrameWidth = 10,
FrameIndent = 15,
FrameHeader = 35,
CloseAfterChange = true
}
};
[JsonProperty(PropertyName = "Blocked skins for sell",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, List<ulong>> BlockedSkins = new Dictionary<string, List<ulong>>
{
["short name"] = new List<ulong>
{
52,
25
},
["short name 2"] = new List<ulong>
{
52,
25
}
};
[JsonProperty(PropertyName = "Auto-Wipe Settings")]
public WipeSettings Wipe = new WipeSettings
{
Cooldown = true,
Players = true,
Limits = true
};
[JsonProperty(PropertyName = "Custom Vending Machines (Entity ID - settings)",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<uint, CustomVendingConf> CustomVending =
new Dictionary<uint, CustomVendingConf>
{
[123343941] = new CustomVendingConf
{
Categories = new List<string>
{
"Cars", "Misc"
}
}
};
[JsonProperty(PropertyName = "Settings available containers for selling item")]
public SellContainers SellContainers = new SellContainers
{
Enabled = true,
Containers = new List<string>
{
"main",
"belt"
}
};
public VersionNumber Version;
#endregion
#region Classes
public enum SortType
{
None,
Name,
Amount,
PriceDecrease,
PriceIncrease
}
#endregion
}
private class SellContainers
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Available Containers (main, belt, wear)",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> Containers = new List<string>();
public List<ItemContainer> GetContainers(BasePlayer player)
{
if (player == null || player.inventory == null)
return new List<ItemContainer>();
var list = new List<ItemContainer>();
Containers.ForEach(cont =>
{
switch (cont)
{
case "main":
{
list.Add(player.inventory.containerMain);
break;
}
case "belt":
{
list.Add(player.inventory.containerBelt);
break;
}
case "wear":
{
list.Add(player.inventory.containerWear);
break;
}
}
});
return list;
}
public Item[] AllItems(BasePlayer player)
{
var items = new List<Item>();
GetContainers(player).ForEach(cont => items.AddRange(cont.itemList));
return items.ToArray();
}
}
private class SelectCurrencyUI
{
[JsonProperty(PropertyName = "Title")] public LabelSettings Title;
[JsonProperty(PropertyName = "Title Position")]
public InterfacePosition TitlePosition;
[JsonProperty(PropertyName = "Economy Title")]
public LabelSettings EconomyTitle;
[JsonProperty(PropertyName = "Selected Economy Color")]
public IColor SelectedEconomyColor;
[JsonProperty(PropertyName = "Unselected Economy Color")]
public IColor UnselectedEconomyColor;
[JsonProperty(PropertyName = "Economy Width")]
public float EconomyWidth;
[JsonProperty(PropertyName = "Economy Height")]
public float EconomyHeight;
[JsonProperty(PropertyName = "Economy Margin")]
public float EconomyMargin;
[JsonProperty(PropertyName = "Economy Indent")]
public float EconomyIndent;
[JsonProperty(PropertyName = "Frame Width")]
public float FrameWidth;
[JsonProperty(PropertyName = "Frame Indent")]
public float FrameIndent;
[JsonProperty(PropertyName = "Frame Header")]
public float FrameHeader;
[JsonProperty(PropertyName = "Close the menu after a currency change?")]
public bool CloseAfterChange;
}
private class InterfacePosition
{
public string AnchorMin;
public string AnchorMax;
public string OffsetMin;
public string OffsetMax;
}
private class LabelSettings
{
[JsonProperty(PropertyName = "FontSize")]
public int FontSize;
[JsonProperty(PropertyName = "Align")] [JsonConverter(typeof(StringEnumConverter))]
public TextAnchor Align;
[JsonProperty(PropertyName = "Color")] public IColor Color;
[JsonProperty(PropertyName = "Font")] public string Font;
}
private class IColor
{
[JsonProperty(PropertyName = "HEX")] public string Hex;
[JsonProperty(PropertyName = "Opacity (0 - 100)")]
public readonly float Alpha;
[JsonIgnore] private string _color;
[JsonIgnore]
public string Get
{
get
{
if (string.IsNullOrEmpty(_color))
_color = GetColor();
return _color;
}
}
private string GetColor()
{
if (string.IsNullOrEmpty(Hex)) Hex = "#FFFFFF";
var str = Hex.Trim('#');
if (str.Length != 6) throw new Exception(Hex);
var r = byte.Parse(str.Substring(0, 2), NumberStyles.HexNumber);
var g = byte.Parse(str.Substring(2, 2), NumberStyles.HexNumber);
var b = byte.Parse(str.Substring(4, 2), NumberStyles.HexNumber);
return $"{(double) r / 255} {(double) g / 255} {(double) b / 255} {Alpha / 100}";
}
public IColor()
{
}
public IColor(string hex, float alpha = 100)
{
Hex = hex;
Alpha = alpha;
}
}
private class CustomVendingConf
{
[JsonProperty(PropertyName = "Categories (Titles) [* - all]",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> Categories;
}
private class WipeSettings
{
[JsonProperty(PropertyName = "Wipe Cooldowns?")]
public bool Cooldown;
[JsonProperty(PropertyName = "Wipe Players?")]
public bool Players;
[JsonProperty(PropertyName = "Wipe Limits?")]
public bool Limits;
}
private class UserInterface
{
[JsonProperty(PropertyName = "Display type (Overlay/Hud)")]
public string DisplayType;
[JsonProperty(PropertyName = "Height")]
public float Height;
[JsonProperty(PropertyName = "Width")] public float Width;
[JsonProperty(PropertyName = "Categories On Page")]
public int CategoriesOnString;
[JsonProperty(PropertyName = "Categories Margin")]
public float CategoriesMargin;
[JsonProperty(PropertyName = "Categories Height")]
public float CategoriesHeight;
[JsonProperty(PropertyName = "Items On String")]
public int ItemsOnString;
[JsonProperty(PropertyName = "Strings")]
public int Strings;
[JsonProperty(PropertyName = "Item Height")]
public float ItemHeight;
[JsonProperty(PropertyName = "Item Width")]
public float ItemWidth;
[JsonProperty(PropertyName = "Margin")]
public float Margin;
[JsonProperty(PropertyName = "Use scroll in categories?")]
public bool UseScrollCategories;
[JsonProperty(PropertyName = "Enable search?")]
public bool EnableSearch;
[JsonProperty(PropertyName = "Number of digits after decimal point for rounding prices")]
public int RoundDigits;
[JsonProperty(PropertyName = "Select Currency Settings")]
public SelectCurrencyUI SelectCurrency;
}
private class NPCShop
{
[JsonProperty(PropertyName = "Categories (Titles) [* - all]",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> Shops;
[JsonIgnore] public string BotID;
}
private class ShopCategory
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Title")] public string Title;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
[JsonProperty(PropertyName = "Sort Type")]
[JsonConverter(typeof(StringEnumConverter))]
[DefaultValue(Configuration.SortType.None)]
public Configuration.SortType SortType;
[JsonProperty(PropertyName = "Items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ShopItem> Items;
[JsonIgnore] public List<ShopItem> SortedItems;
[JsonIgnore]
public List<ShopItem> GetItems
{
get
{
switch (SortType)
{
case Configuration.SortType.None:
return Items;
default:
if (SortedItems == null) SortItems();
return SortedItems;
}
}
}
public void SortItems()
{
switch (SortType)
{
case Configuration.SortType.Name:
{
SortedItems = Items.ToList();
SortedItems.Sort((x, y) =>
string.Compare(x.PublicTitle, y.PublicTitle, StringComparison.Ordinal));
break;
}
case Configuration.SortType.Amount:
{
SortedItems = Items.ToList();
SortedItems.Sort((x, y) => x.Amount.CompareTo(y.Amount));
break;
}
case Configuration.SortType.PriceIncrease:
{
SortedItems = Items.ToList();
SortedItems.Sort((x, y) => x.Amount.CompareTo(y.Price));
break;
}
case Configuration.SortType.PriceDecrease:
{
SortedItems = Items.ToList();
SortedItems.Sort((x, y) => y.Amount.CompareTo(x.Price));
break;
}
default:
{
SortedItems = Items.ToList();
break;
}
}
LoadIDs(true);
}
public void LoadIDs(bool sort = false)
{
if (sort)
Items.ForEach(item => _instance._shopItems.Remove(item.ID));
GetItems.ForEach(item =>
{
var id = item.ID;
if (_instance._shopItems.ContainsKey(item.ID))
id = _instance.GetId();
_instance._shopItems.Add(id, item);
if (item.Discount != null)
foreach (var check in item.Discount)
if (!string.IsNullOrEmpty(check.Key) && !_instance.permission.PermissionExists(check.Key))
_instance.permission.RegisterPermission(check.Key, _instance);
if (item.Type == ItemType.Item && item.Definition == null && !string.IsNullOrEmpty(item.ShortName))
item.Definition = !string.IsNullOrEmpty(item.ShortName)
? ItemManager.FindItemDefinition(item.ShortName)
: null;
});
}
}
private enum ItemType
{
Item,
Command,
Plugin,
Kit
}
private class ShopItem
{
[JsonProperty(PropertyName = "Type")] [JsonConverter(typeof(StringEnumConverter))]
public ItemType Type;
[JsonProperty(PropertyName = "ID")] public int ID;
[JsonProperty(PropertyName = "Image")] public string Image;
[JsonProperty(PropertyName = "Title")] public string Title;
[JsonProperty(PropertyName = "Description")]
public string Description = string.Empty;
[JsonProperty(PropertyName = "Command (%steamid%)")]
public string Command;
[JsonProperty(PropertyName = "Kit")] public string Kit = string.Empty;
[JsonProperty(PropertyName = "Plugin")]
public PluginItem Plugin;
[JsonProperty(PropertyName = "DisplayName (empty - default)")]
public string DisplayName;
[JsonProperty(PropertyName = "ShortName")]
public string ShortName;
[JsonProperty(PropertyName = "Skin")] public ulong Skin;
[JsonProperty(PropertyName = "Is Blueprint")]
public bool Blueprint;
[JsonProperty(PropertyName = "Amount")]
public int Amount;
[JsonProperty(PropertyName = "Price")] public double Price;
[JsonProperty(PropertyName = "Sell Price")]
public double SellPrice;
[JsonProperty(PropertyName = "Buy Cooldown (0 - disable)")]
public float BuyCooldown;
[JsonProperty(PropertyName = "Sell Cooldown (0 - disable)")]
public float SellCooldown;
[JsonProperty(PropertyName = "Discount (%)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, int> Discount = new Dictionary<string, int>();
[JsonProperty(PropertyName = "Sell Limits (0 - no limit)",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, int> SellLimits = new Dictionary<string, int>();
[JsonProperty(PropertyName = "Buy Limits (0 - no limit)",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, int> BuyLimits = new Dictionary<string, int>();
[JsonProperty(PropertyName = "Daily Buy Limits (0 - no limit)",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, int> DailyBuyLimits = new Dictionary<string, int>();
[JsonProperty(PropertyName = "Daily Sell Limits (0 - no limit)",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, int> DailySellLimits = new Dictionary<string, int>();
[JsonProperty(PropertyName = "Max Buy Amount (0 - disable)")]
public int BuyMaxAmount;
[JsonProperty(PropertyName = "Max Sell Amount (0 - disable)")]
public int SellMaxAmount;
[JsonProperty(PropertyName = "Force Buy")]
public bool ForceBuy;
[JsonProperty(PropertyName = "Prohibit splitting item into stacks?")]
public bool ProhibitSplit;
[JsonIgnore] private int _itemId = -1;
[JsonIgnore]
public int itemId
{
get
{
if (_itemId == -1)
_itemId = ItemManager.FindItemDefinition(ShortName)?.itemid ?? -1;
return _itemId;
}
}
[JsonIgnore] private string _publicTitle;
[JsonIgnore]
public string PublicTitle
{
get
{
if (string.IsNullOrEmpty(_publicTitle))
_publicTitle = GetName();
return _publicTitle;
}
}
[JsonIgnore] private ICuiComponent _image;
public CuiElement GetImage(string aMin, string aMax, string oMin, string oMax, string parent,
string name = null)
{
if (_image == null)
{
if (_instance._enabledImageLibrary && !string.IsNullOrEmpty(Image))
_image = new CuiRawImageComponent
{
Png = _instance.ImageLibrary.Call<string>("GetImage", Image)
};
else
_image = new CuiImageComponent
{
ItemId = itemId,
SkinId = Skin
};
}
return new CuiElement
{
Name = string.IsNullOrEmpty(name) ? CuiHelper.GetGuid() : name,
Parent = parent,
Components =
{
_image,
new CuiRectTransformComponent
{
AnchorMin = aMin, AnchorMax = aMax,
OffsetMin = oMin, OffsetMax = oMax
}
}
};
}
private string GetName()
{
if (!string.IsNullOrEmpty(Title))
return Title;
if (!string.IsNullOrEmpty(DisplayName))
return DisplayName;
var def = ItemManager.FindItemDefinition(ShortName);
if (!string.IsNullOrEmpty(ShortName) && def != null)
return def.displayName.translated;
return string.Empty;
}
[JsonIgnore] public ItemDefinition Definition;
public Dictionary<string, object> ToDictionary()
{
return new Dictionary<string, object>
{
["Generated"] = false,
["ID"] = ID,
["Type"] = Type,
["Image"] = Image,
["Title"] = Title,
["Command"] = Command,
["DisplayName"] = DisplayName,
["ShortName"] = ShortName,
["Skin"] = Skin,
["Blueprint"] = Blueprint,
["Amount"] = Amount,
["Price"] = Price,
["SellPrice"] = SellPrice,
["Plugin_Hook"] = Plugin.Hook,
["Plugin_Name"] = Plugin.Plugin,
["Plugin_Amount"] = Plugin.Amount
};
}
public ShopItem()
{
}
public ShopItem(Dictionary<string, object> dictionary)
{
ID = (int) dictionary["ID"];
Type = (ItemType) dictionary["Type"];
Image = (string) dictionary["Image"];
Title = (string) dictionary["Title"];
Command = (string) dictionary["Command"];
DisplayName = (string) dictionary["DisplayName"];
ShortName = (string) dictionary["ShortName"];
Skin = (ulong) dictionary["Skin"];
Blueprint = (bool) dictionary["Blueprint"];
Amount = (int) dictionary["Amount"];
Price = (double) dictionary["Price"];
SellPrice = (double) dictionary["SellPrice"];
Plugin = new PluginItem
{
Hook = (string) dictionary["Plugin_Hook"],
Plugin = (string) dictionary["Plugin_Name"],
Amount = (int) dictionary["Plugin_Amount"]
};
Discount = new Dictionary<string, int>();
}
public double GetPrice(BasePlayer player)
{
var discount = GetDiscount(player);
var price = Price;
return Math.Round(discount != 0 ? price * (1f - discount / 100f) : price,
_config.UI.RoundDigits);
}
public int GetDiscount(BasePlayer player)
{
var result = 0;
Discount.Where(check => player.HasPermission(check.Key)).ForEach(
check =>
{
if (check.Value > result)
result = check.Value;
});
return result;
}
public int GetLimit(BasePlayer player, bool buy = true, bool daily = false)
{
var dict = daily ? buy ? DailyBuyLimits : DailySellLimits
: buy ? BuyLimits : SellLimits;
if (dict.Count == 0)
return 0;
var result = 0;
dict.Where(check => player.HasPermission(check.Key)).ForEach(
check =>
{
if (check.Value > result)
result = check.Value;
});
return result;
}
public void Get(BasePlayer player, int count = 1)
{
switch (Type)
{
case ItemType.Item:
ToItem(player, count);
break;
case ItemType.Command:
ToCommand(player, count);
break;
case ItemType.Plugin:
Plugin.Get(player, count);
break;
case ItemType.Kit:
ToKit(player, count);
break;
}
}
private void ToKit(BasePlayer player, int count)
{
if (string.IsNullOrEmpty(Kit)) return;
for (var i = 0; i < count; i++)
Interface.Oxide.CallHook("GiveKit", player, Kit);
}
[JsonIgnore] private ItemDefinition _itemDefinition;
[JsonIgnore]
public ItemDefinition ItemDefinition
{
get
{
if (_itemDefinition == null) _itemDefinition = ItemManager.FindItemDefinition(ShortName);
return _itemDefinition;
}
}
private void ToItem(BasePlayer player, int count)
{
if (ItemDefinition == null)
{
Debug.LogError($"Error creating item with ShortName '{ShortName}'");
return;
}
if (Blueprint)
{
GiveBlueprint(Amount * count, player);
}
else
{
if (ProhibitSplit)
GiveItem(Amount * count, player);
else
GetStacks(count)?.ForEach(stack => GiveItem(stack, player));
}
}
private void GiveBlueprint(int count, BasePlayer player)
{
for (var i = 0; i < count; i++) GiveBlueprint(player);
}
private void GiveBlueprint(BasePlayer player)
{
var bp = ItemManager.CreateByName("blueprintbase");
if (bp == null)
{
_instance?.PrintError("Error creating blueprintbase");
return;
}
bp.blueprintTarget = ItemManager.FindItemDefinition(ShortName).itemid;
if (!string.IsNullOrEmpty(DisplayName)) bp.name = DisplayName;
player.GiveItem(bp, BaseEntity.GiveItemReason.PickedUp);
}
private void GiveItem(int amount, BasePlayer player)
{
var newItem = ItemManager.Create(ItemDefinition, amount, Skin);
if (newItem == null)
{
_instance?.PrintError($"Error creating item with ShortName '{ShortName}'");
return;
}
if (!string.IsNullOrEmpty(DisplayName)) newItem.name = DisplayName;
player.GiveItem(newItem, BaseEntity.GiveItemReason.PickedUp);
}
private void ToCommand(BasePlayer player, int count)
{
for (var i = 0; i < count; i++)
{
var command = Command.Replace("\n", "|").Replace("%steamid%", player.UserIDString, StringComparison.OrdinalIgnoreCase).Replace(
"%username%",
player.displayName, StringComparison.OrdinalIgnoreCase);
foreach (var check in command.Split('|'))
if (check.Contains("chat.say"))
{
var args = check.Split(' ');
player.SendConsoleCommand(
$"{args[0]} \" {string.Join(" ", args.ToList().GetRange(1, args.Length - 1))}\" 0");
}
else
{
_instance?.Server.Command(check);
}
}
}
public List<int> GetStacks(int amount)
{
amount *= Amount;
var maxStack = ItemDefinition.stackable;
var list = new List<int>();
if (maxStack == 0) maxStack = 1;
while (amount > maxStack)
{
amount -= maxStack;
list.Add(maxStack);
}
list.Add(amount);
return list;
}
public override string ToString()
{
switch (Type)
{
case ItemType.Item:
return $"[ITEM-{ID}] {ShortName}x{Amount}(DN: {DisplayName}, SKIN: {Skin})";
case ItemType.Command:
return $"[COMMAND-{ID}] {Command}";
case ItemType.Plugin:
return
$"[PLUGIN-{ID}] Name: {Plugin?.Plugin}, Hook: {Plugin?.Hook}, Amount: {Plugin?.Amount ?? 0}";
case ItemType.Kit:
return $"[KIT-{ID}] {Kit}";
default:
return base.ToString();
}
}
public static ShopItem GetDefault(int id, double itemCost, string shortName)
{
return new ShopItem
{
Type = ItemType.Item,
ID = id,
Price = itemCost,
SellPrice = itemCost,
Image = string.Empty,
Title = string.Empty,
Command = string.Empty,
Plugin = new PluginItem(),
DisplayName = string.Empty,
ShortName = shortName,
Skin = 0,
Blueprint = false,
Amount = 1,
Discount = new Dictionary<string, int>
{
["shop.default"] = 0,
["shop.vip"] = 10
},
SellLimits = new Dictionary<string, int>
{
["shop.default"] = 0,
["shop.vip"] = 0
},
BuyLimits = new Dictionary<string, int>
{
["shop.default"] = 0,
["shop.vip"] = 0
},
DailyBuyLimits = new Dictionary<string, int>
{
["shop.default"] = 0,
["shop.vip"] = 0
},
DailySellLimits = new Dictionary<string, int>
{
["shop.default"] = 0,
["shop.vip"] = 0
},
BuyMaxAmount = 0,
SellMaxAmount = 0,
ForceBuy = false,
ProhibitSplit = false
};
}
}
private class PluginItem
{
[JsonProperty(PropertyName = "Hook")] public string Hook = string.Empty;
[JsonProperty(PropertyName = "Plugin Name")]
public string Plugin = string.Empty;
[JsonProperty(PropertyName = "Amount")]
public int Amount;
public void Get(BasePlayer player, int count = 1)
{
var plug = _instance?.plugins.Find(Plugin);
if (plug == null)
{
_instance?.PrintError($"Plugin '{Plugin}' not found !!! ");
return;
}
switch (Plugin)
{
case "Economics":
{
plug.Call(Hook, player.userID, (double) Amount * count);
break;
}
default:
{
plug.Call(Hook, player.userID, Amount * count);
break;
}
}
}
}
private class AdditionalEconomy : EconomyConf
{
[JsonProperty(PropertyName = "ID")] public int ID;
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
public bool IsSame(EconomyConf configEconomy)
{
return Type == configEconomy.Type &&
Plug == configEconomy.Plug &&
ShortName == configEconomy.ShortName &&
Skin == configEconomy.Skin;
}
public AdditionalEconomy(EconomyConf configEconomy)
{
Type = configEconomy.Type;
Plug = configEconomy.Plug;
AddHook = configEconomy.AddHook;
RemoveHook = configEconomy.RemoveHook;
BalanceHook = configEconomy.BalanceHook;
ShortName = configEconomy.ShortName;
DisplayName = configEconomy.DisplayName;
Skin = configEconomy.Skin;
TitleLangKey = configEconomy.TitleLangKey;
BalanceLangKey = configEconomy.BalanceLangKey;
ID = 0;
Enabled = true;
}
[JsonConstructor]
public AdditionalEconomy()
{
}
}
private enum EconomyType
{
Plugin,
Item
}
private class EconomyConf
{
[JsonProperty(PropertyName = "Type (Plugin/Item)")] [JsonConverter(typeof(StringEnumConverter))]
public EconomyType Type;
[JsonProperty(PropertyName = "Plugin name")]
public string Plug;
[JsonProperty(PropertyName = "Balance add hook")]
public string AddHook;
[JsonProperty(PropertyName = "Balance remove hook")]
public string RemoveHook;
[JsonProperty(PropertyName = "Balance show hook")]
public string BalanceHook;
[JsonProperty(PropertyName = "ShortName")]
public string ShortName;
[JsonProperty(PropertyName = "Display Name (empty - default)")]
public string DisplayName;
[JsonProperty(PropertyName = "Skin")] public ulong Skin;
[JsonProperty(PropertyName = "Lang Key (for Title)")]
public string TitleLangKey;
[JsonProperty(PropertyName = "Lang Key (for Balance)")]
public string BalanceLangKey;
public string GetTitle(BasePlayer player)
{
return _instance.Msg(player, TitleLangKey);
}
public string GetBalanceTitle(BasePlayer player)
{
return _instance.Msg(player, BalanceLangKey, ShowBalance(player));
}
public double ShowBalance(BasePlayer player)
{
switch (Type)
{
case EconomyType.Plugin:
{
var plugin = _instance?.plugins?.Find(Plug);
if (plugin == null) return 0;
return Math.Round(Convert.ToDouble(plugin.Call(BalanceHook, player.userID)), 2);
}
case EconomyType.Item:
{
return ItemCount(player.inventory.AllItems(), ShortName, Skin);
}
default:
return 0;
}
}
public void AddBalance(BasePlayer player, double amount)
{
switch (Type)
{
case EconomyType.Plugin:
{
var plugin = _instance?.plugins?.Find(Plug);
if (plugin == null) return;
switch (Plug)
{
case "BankSystem":
case "ServerRewards":
case "IQEconomic":
plugin.Call(AddHook, player.userID, (int) amount);
break;
default:
plugin.Call(AddHook, player.userID, amount);
break;
}
break;
}
case EconomyType.Item:
{
var am = (int) amount;
var item = ToItem(am);
if (item == null) return;
player.GiveItem(item);
break;
}
}
}
public bool RemoveBalance(BasePlayer player, double amount)
{
switch (Type)
{
case EconomyType.Plugin:
{
if (ShowBalance(player) < amount) return false;
var plugin = _instance?.plugins.Find(Plug);
if (plugin == null) return false;
switch (Plug)
{
case "BankSystem":
case "ServerRewards":
case "IQEconomic":
plugin.Call(RemoveHook, player.userID, (int) amount);
break;
default:
plugin.Call(RemoveHook, player.userID, amount);
break;
}
return true;
}
case EconomyType.Item:
{
var playerItems = player.inventory.AllItems();
var am = (int) amount;
if (ItemCount(playerItems, ShortName, Skin) < am) return false;
Take(playerItems, ShortName, Skin, am);
return true;
}
default:
return false;
}
}
public bool Transfer(BasePlayer player, BasePlayer targetPlayer, double amount)
{
if (!RemoveBalance(player, amount))
return false;
AddBalance(targetPlayer, amount);
return true;
}
private Item ToItem(int amount)
{
var item = ItemManager.CreateByName(ShortName, amount, Skin);
if (item == null)
{
Debug.LogError($"Error creating item with ShortName: '{ShortName}'");
return null;
}
if (!string.IsNullOrEmpty(DisplayName)) item.name = DisplayName;
return item;
}
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_config = Config.ReadObject<Configuration>();
if (_config == null) throw new Exception();
if (_config.Version < Version)
UpdateConfigValues();
SaveConfig();
}
catch
{
PrintError("Your configuration file contains an error.Using default configuration values.");
LoadDefaultConfig();
}
}
protected override void SaveConfig()
{
Config.WriteObject(_config);
}
protected override void LoadDefaultConfig()
{
_config = new Configuration();
}
private void UpdateConfigValues()
{
PrintWarning("Config update detected! Updating config values...");
var baseConfig = new Configuration();
if (_config.Version == default(VersionNumber) && _config.Version < new VersionNumber(1, 0, 21))
{
_data = Interface.Oxide.DataFileSystem.ReadObject<PluginData>(Name);
if (_data != null)
Interface.Oxide.DataFileSystem.WriteObject($"{Name}/Players", _data);
}
if (_config.Version < new VersionNumber(1, 0, 21))
_config.Shop.ForEach(shop =>
{
shop.Items.ForEach(item =>
{
item.SellLimits = new Dictionary<string, int>
{
["shop.default"] = 0,
["shop.vip"] = 0
};
item.BuyLimits = new Dictionary<string, int>
{
["shop.default"] = 0,
["shop.vip"] = 0
};
item.DailyBuyLimits = new Dictionary<string, int>
{
["shop.default"] = 0,
["shop.vip"] = 0
};
item.DailySellLimits = new Dictionary<string, int>
{
["shop.default"] = 0,
["shop.vip"] = 0
};
});
});
if (_config.Version < new VersionNumber(1, 0, 24)) _config.UI.DisplayType = baseConfig.UI.DisplayType;
_config.Version = Version;
PrintWarning("Config update completed!");
}
#endregion
#region Data
private PluginData _data;
private void SaveData()
{
SavePlayers();
SaveCooldown();
SaveLimits();
SaveSelectEconomy();
}
private void SavePlayers()
{
_data.PlayerCarts.Clear();
foreach (var check in _carts) _data.PlayerCarts.Add(check.Key, check.Value.ToPlayerCart());
foreach (var cartData in _cartsNPC)
{
PlayerCart cart;
if (!_data.PlayerCarts.TryGetValue(cartData.Key, out cart))
_data.PlayerCarts.Add(cartData.Key, cart = new PlayerCart());
foreach (var npcData in cartData.Value)
cart.NPCCart[npcData.Key] = new NPCCart
{
Items = npcData.Value.GetPlayerCartItems()
};
}
Interface.Oxide.DataFileSystem.WriteObject($"{Name}/Players", _data);
}
private void SaveCooldown()
{
Interface.Oxide.DataFileSystem.WriteObject($"{Name}/Cooldown", _cooldown);
}
private void SaveLimits()
{
Interface.Oxide.DataFileSystem.WriteObject($"{Name}/Limits", _limits);
}
private void SaveSelectEconomy()
{
Interface.Oxide.DataFileSystem.WriteObject($"{Name}/EconomyChoice", _economyChoice);
}
private void LoadData()
{
try
{
_data = Interface.Oxide.DataFileSystem.ReadObject<PluginData>($"{Name}/Players");
_cooldown =
Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, CooldownInfo>>($"{Name}/Cooldown");
_limits = Interface.Oxide.DataFileSystem.ReadObject<PlayerLimits>($"{Name}/Limits");
_economyChoice = Interface.Oxide.DataFileSystem.ReadObject<EconomyChoice>($"{Name}/EconomyChoice");
}
catch (Exception e)
{
PrintError(e.ToString());
}
if (_data == null) _data = new PluginData();
if (_cooldown == null) _cooldown = new Dictionary<ulong, CooldownInfo>();
if (_limits == null) _limits = new PlayerLimits();
if (_economyChoice == null) _economyChoice = new EconomyChoice();
}
private class PluginData
{
[JsonProperty(PropertyName = "Players", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, PlayerCart> PlayerCarts = new Dictionary<ulong, PlayerCart>();
}
private class PlayerCart
{
[JsonProperty(PropertyName = "Items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<int, int> Items = new Dictionary<int, int>();
[JsonProperty(PropertyName = "NPC Carts", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, NPCCart> NPCCart = new Dictionary<string, NPCCart>();
}
private class NPCCart
{
[JsonProperty(PropertyName = "Items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<int, int> Items = new Dictionary<int, int>();
}
private class DataCart
{
public readonly Dictionary<ShopItem, int> Items = new Dictionary<ShopItem, int>();
public void AddCartItem(ShopItem item, BasePlayer player)
{
int result;
int amount;
if (Items.TryGetValue(item, out amount))
{
if (item.BuyMaxAmount > 0 && amount >= item.BuyMaxAmount) return;
if (!CanCartAdd(player, item, amount + 1, out result))
{
_instance.SendNotify(player, result == 1 ? BuyLimitReached : DailyBuyLimitReached, 1,
item.PublicTitle);
return;
}
Items[item]++;
}
else
{
if (!CanCartAdd(player, item, 1, out result))
{
_instance.SendNotify(player, result == 1 ? BuyLimitReached : DailyBuyLimitReached, 1,
item.PublicTitle);
return;
}
Items.Add(item, 1);
}
}
private bool CanCartAdd(BasePlayer player, ShopItem item, int amount, out int result)
{
int leftLimit;
if (HasLimit(player, item, true, out leftLimit) && amount >= leftLimit) //total Limit
{
result = 1;
return false;
}
if (HasLimit(player, item, true, out leftLimit, true) && amount > leftLimit) //daily Limit
{
result = 2;
return false;
}
result = 0;
return true;
}
public void RemoveCartItem(ShopItem item)
{
Items.Remove(item);
}
public void ChangeAmountItem(BasePlayer player, ShopItem item, int amount)
{
if (amount > 0)
{
int totalLimit;
if (HasLimit(player, item, true, out totalLimit) && amount >= totalLimit)
amount = Math.Min(totalLimit, amount);
int dailyLimit;
if (HasLimit(player, item, true, out dailyLimit, true) && amount >= dailyLimit)
amount = Math.Min(dailyLimit, amount);
if (amount <= 0) return;
Items[item] = amount;
}
else
{
Items.Remove(item);
}
}
public int GetAmount()
{
return Items.Sum(x => x.Key.Amount * x.Value);
}
public double GetPrice(BasePlayer player)
{
return Items.Sum(x => x.Key.GetPrice(player) * x.Value);
}
public int GetTotalAmount()
{
return
Items.Sum(check => check.Key.Type == ItemType.Item && check.Key.Definition != null
? check.Key.ProhibitSplit
? 1
: check.Key.GetStacks(check.Value).Count
: 0);
}
public void ClearItems()
{
Items.Clear();
}
#region Data
public PlayerCart ToPlayerCart()
{
return new PlayerCart
{
Items = Items.ToDictionary(x => x.Key.ID, y => y.Value)
};
}
public Dictionary<int, int> GetPlayerCartItems()
{
return Items.ToDictionary(x => x.Key.ID, y => y.Value);
}
public DataCart()
{
}
public DataCart(PlayerCart cart)
{
Items = cart.Items.ToDictionary(x => _instance.FindItemById(x.Key), y => y.Value);
}
public DataCart(Dictionary<int, int> items)
{
Items = items.ToDictionary(x => _instance.FindItemById(x.Key), y => y.Value);
}
#endregion
}
#endregion
#region Hooks
private void Init()
{
_instance = this;
LoadData();
LoadColors();
RegisterPermissions();
CheckOnDuplicates();
LoadEconomics();
}
private void OnServerInitialized()
{
PrintWarning("\n-----------------------------\n" +
" Author - Sempai#3239\n" +
" VK - https://vk.com/rustnastroika/n" +
" Discord - https://discord.gg/5DPTsRmd3G/n"
FillCategories();
LoadItems();
LoadNPCs();
LoadImages();
ItemsToDict();
CacheImages();
LoadCarts();
LoadPlayers();
LoadCustomVMs();
RegisterCommands();
CheckUpdateController();
LoadCacheUI();
}
private void Unload()
{
foreach (var player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, Layer);
CuiHelper.DestroyUi(player, ModalLayer);
CuiHelper.DestroyUi(player, EditingLayer);
OnPlayerDisconnected(player);
}
SaveData();
_config = null;
_instance = null;
}
private void OnPlayerConnected(BasePlayer player)
{
if (player == null) return;
GetAvatar(player.userID,
avatar => ImageLibrary?.Call("AddImage", avatar, $"avatar_{player.UserIDString}"));
if (_config.LoginImages)
_coroutines[player] = ServerMgr.Instance.StartCoroutine(LoadImages(player));
}
private void OnPlayerDisconnected(BasePlayer player)
{
if (player == null) return;
_itemsToUpdate.Remove(player);
_openedShops.Remove(player);
_openSHOP.Remove(player);
CheckUpdateController();
_itemEditing.Remove(player);
Coroutine coroutine;
if (_coroutines.TryGetValue(player, out coroutine) && coroutine != null)
ServerMgr.Instance.StopCoroutine(coroutine);
}
private void OnUseNPC(BasePlayer npc, BasePlayer player)
{
if (npc == null || player == null) return;
NPCShop npcShop;
if (!_config.NPCs.TryGetValue(npc.UserIDString, out npcShop) || npcShop == null) return;
_openedShops[player] = npcShop;
MainUi(player, first: true);
}
private void OnNewSave()
{
if (_config.Wipe.Players)
_data.PlayerCarts.Clear();
if (_config.Wipe.Cooldown)
_cooldown.Clear();
if (_config.Wipe.Limits)
_limits.Players.Clear();
SaveData();
}
#region Image Library
private void OnPluginLoaded(Plugin plugin)
{
if (plugin.Name == "ImageLibrary") _enabledImageLibrary = true;
}
private void OnPluginUnloaded(Plugin plugin)
{
if (plugin.Name == "ImageLibrary") _enabledImageLibrary = false;
}
#endregion
#region Vending Machine
private object CanLootEntity(BasePlayer player, VendingMachine vendingMachine)
{
if (player == null || vendingMachine == null)
return null;
CustomVendingConf customVendingConf;
if (_config.CustomVending.TryGetValue(vendingMachine.net.ID, out customVendingConf))
{
_openedCustomVending[player] = customVendingConf;
MainUi(player, first: true);
return false;
}
return null;
}
#endregion
#endregion
#region Commands
[ConsoleCommand("openshopUI")]
private void OpenShopUI(ConsoleSystem.Arg arg)
{
var player = arg?.Player();
if (player == null) return;
if (_openedUI.Contains(player.userID))
{
CuiHelper.DestroyUi(player, Layer);
_openedUI.Remove(player.userID);
}
else
{
MainUi(player, first: true);
_openedUI.Add(player.userID);
}
}
private void CmdShopOpen(IPlayer cov, string command, string[] args)
{
var player = cov?.Object as BasePlayer;
if (player == null) return;
if (_config.UseDuels && InDuel(player))
{
SendNotify(player, NoUseDuel, 1);
return;
}
MainUi(player, first: true);
}
private void CmdSetCustomVM(IPlayer cov, string command, string[] args)
{
var player = cov?.Object as BasePlayer;
if (player == null) return;
if (!player.HasPermission(PermSetVM))
{
SendNotify(player, NoPermission, 1);
return;
}
if (args.Length == 0)
{
SendNotify(player, ErrorSyntax, 1, $"{command} [categories: cat1 cat2...]");
return;
}
var categories = args.ToList();
categories.RemoveAll(cat => !_config.Shop.Exists(confCat => confCat.Title == cat));
if (categories.Count == 0)
{
SendNotify(player, VMNotFoundCategories, 1);
return;
}
var workbench = GetLookVM(player);
if (workbench == null)
{
SendNotify(player, VMNotFound, 1);
return;
}
if (_config.CustomVending.ContainsKey(workbench.net.ID))
{
SendNotify(player, VMExists, 1);
return;
}
var conf = new CustomVendingConf
{
Categories = categories
};
_config.CustomVending[workbench.net.ID] = conf;
SaveConfig();
SendNotify(player, VMInstalled, 0);
Subscribe(nameof(CanLootEntity));
}
private void CmdSetShopNPC(IPlayer cov, string command, string[] args)
{
var player = cov?.Object as BasePlayer;
if (player == null) return;
if (!player.HasPermission(PermSetNPC))
{
SendNotify(player, NoPermission, 1);
return;
}
if (args.Length == 0)
{
SendNotify(player, ErrorSyntax, 1, $"{command} [categories: cat1 cat2...]");
return;
}
var categories = args.ToList();
categories.RemoveAll(cat => !_config.Shop.Exists(confCat => confCat.Title == cat));
if (categories.Count == 0)
{
SendNotify(player, VMNotFoundCategories, 1);
return;
}
var npc = GetLookNPC(player);
if (npc == null)
{
SendNotify(player, NPCNotFound, 1);
return;
}
if (_config.NPCs.ContainsKey(npc.UserIDString))
{
SendNotify(player, VMExists, 1);
return;
}
var conf = new NPCShop
{
Shops = categories,
BotID = npc.UserIDString
};
_config.NPCs[npc.UserIDString] = conf;
SaveConfig();
SendNotify(player, NPCInstalled, 0);
}
[ConsoleCommand("UI_Sho
// ... [Code preview truncated due to length. Full source in attached file] ...