C#:
using System;
using System.Collections.Generic;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System.Linq;
using System.Globalization;
using Newtonsoft.Json;
using UnityEngine;
using ConVar;
using Oxide.Core.Libraries;
using Color = UnityEngine.Color;
namespace Oxide.Plugins {
[Info("SkinDrops", "Oxide Россия | xkrystalll", "2.0.13")]
public class SkinDrops : RustPlugin {
const bool en = false;
private static SkinDrops _ins;
[PluginReference]
private Plugin ImageLibrary;
const bool debug = false;
#region Config, data, initialization, etc.
private PluginConfig cfg;
public class PluginConfig {
[JsonProperty(en ? "Disable exp gather notification?" : "Отключить уведомление о получении exp?")]
public bool DisableExpNotify;
[JsonProperty(en ? "Wipe exp data on server wipe?" : "Очищать дату exp при вайпе сервера?")]
public bool WipeExpOnServerWipe;
[JsonProperty(en ? "Access settings" : "Настройки доступа")]
public Access _access = new Access();
[JsonProperty(en ? "API settings" : "Настройки API")]
public API _api = new API();
[JsonProperty(en ? "Settings for EXP" : "Настройки выдачи EXP")]
public EXP _exp = new EXP();
[JsonProperty(en ? "Drops settings" : "Настройки дропсов")]
public DROPS _drops = new DROPS();
[JsonProperty(en ? "Advanced settings" : "Дополнительные настройки")]
public ADVANCED _advanced = new ADVANCED();
public class Access {
[JsonProperty(en ? "Enable access only to players with a privilege?" : "Включить доступ только игрокам с привелегией?")]
public bool privateuse = false;
[JsonProperty(en ? "Access privilege" : "Привелегия доступа")]
public string perm = "skindrops.use";
[JsonProperty(en ? "Menu open command" : "Команда для открытия меню")]
public string commands = "skindrops";
}
public class API {
[JsonProperty(en ? "Secret key" : "Секретный ключ")]
public string secretkey = "";
}
public class EXP {
[JsonProperty(en ? "EXP at the first login to the server" : "EXP при первом входе на сервер")]
public float defaultexp = 0f;
[JsonProperty(en ? "Give out EXP for loot boxes?" : "Выдавать EXP за лут ящиков?")]
public bool crates = true;
[JsonProperty(en ? "EXP for loot boxes" : "EXP за лут ящиков", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, float> _crates = new Dictionary<string, float>() {
["crate_normal_2"]
= 0.1f, ["crate_normal"] = 0.025f, ["crate_elite"] = 0.35f, ["codelockedhackablecrate"] = 0.55f
}
;
[JsonProperty(en ? "Give out EXP for breaking barrels?" : "Выдавать EXP за разбивание бочек?")]
public bool barrel = true;
[JsonProperty(en ? "EXP for breaking barrels" : "EXP за разбивание бочек", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, float> _barrel = new Dictionary<string, float>() {
["loot-barrel-1"]
= 0.01f, ["loot-barrel-2"] = 0.01f, ["loot_barrel_1"] = 0.01f, ["loot_barrel_2"] = 0.01f, ["oil_barrel"] = 0.01f
}
;
[JsonProperty(en ? "Give out EXP for picking up resources" : "Выдавать EXP за подбор ресурсов")]
public bool collectible = true;
[JsonProperty(en ? "EXP for picking up resources" : "EXP за подбор ресурсов", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, float> _collectible = new Dictionary<string, float>() {
["wood"]
= 0.01f, ["stones"] = 0.02f, ["sulfur.ore"] = 0.05f, ["metal.ore"] = 0.03f, ["cloth"] = 0.01f
}
;
[JsonProperty(en ? "Give out EXP for gather resources" : "Выдавать EXP за добычу ресурсов")]
public bool gather = true;
[JsonProperty(en ? "EXP for gather resources" : "EXP за добычу ресурсов", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, float> _gather = new Dictionary<string, float>() {
["wood"]
= 0.01f, ["stones"] = 0.02f, ["sulfur.ore"] = 0.05f, ["metal.ore"] = 0.03f,
}
;
[JsonProperty(en ? "Give out EXP for killing players" : "Выдавать EXP за убийство игроков?")]
public bool killplayer = true;
[JsonProperty(en ? "Consider teammates?" : "Учитывать тиммейтов?")]
public bool teammatecheck = false;
[JsonProperty(en ? "EXP for killing players" : "EXP за убийство игроков")]
public float killplayer_exp = 0.01f;
[JsonProperty(en ? "Give out EXP for killing NPC" : "Выдавать EXP за убийство NPC?")]
public bool killnpc = true;
[JsonProperty(en ? "EXP for killing NPC" : "EXP за убийство NPC")]
public float killnpc_exp = 0.02f;
}
public class DROPS {
[JsonProperty(en ? "Minimum number of online for the start of the draw (if not needed, put 0)" : "Минимальное количество онлайна для старта розыгрыша (если не нужно, ставим 0)")]
public int minimumonline = 0;
[JsonProperty(en ? "How much EXP is needed for the draw" : "Сколько нужно EXP для розыгрыша")]
public float exp = 500f;
[JsonProperty(en ? "How much EXP a player will pay to participate" : "Сколько EXP заплатит игрок для участия")]
public float pay = 1f;
[JsonProperty(en ? "Random price settings (chance in %)" : "Настройки рандома цены (шанс в %)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<Price> randomprice = new List<Price>() {
new Price {
chance = 80f, min_price = 0.01f, max_price = 1f
}
, new Price {
chance = 10f, min_price = 1f, max_price = 2f
}
, new Price {
chance = 7f, min_price = 2f, max_price = 5f
}
, new Price {
chance = 3f, min_price = 5f, max_price = 10f
}
,
}
;
[JsonProperty(en ? "Notify the chat about winning a skin (the message is visible to all)" : "Оповещать в чат о выигрыше скина (сообщение видно всем)")]
public bool chatmessage = true;
[JsonProperty(en ? "Notify the chat about winning a skin (personally to player)" : "Оповещать в чат о выигрыше скина (лично игроку)")]
public bool player_chatmessage = true;
[JsonProperty(en ? "Play sound when skin is won" : "Воспроизводить звук при выигрыше скина")]
public bool sound = true;
[JsonProperty(en ? "Sound prefab" : "Префаб звука")]
public string soundprefab = "assets/prefabs/misc/easter/painted eggs/effects/eggpickup.prefab";
[JsonProperty(en ? "Notify the discord about winning a skin" : "Оповещать в дискорд о выигрыше скина")]
public bool discordmessage = false;
[JsonProperty("Discord Webhook")]
public string webhook = "";
[JsonProperty(en ? "Notify admins in discord that the money ran out or a service error" : "Оповещать админов в дискорд о том что закончились деньги или ошибка сервиса")]
public bool dlogmessage = false;
[JsonProperty("Discord Log Webhook")]
public string weblog = "";
}
public class ADVANCED {
[JsonProperty(en ? "Show skins list" : "Показывать список скинов которые может выиграть игрок")]
public bool showskinlist = true;
[JsonProperty(en ? "Sorting method (1 - min. price, 2 - max. price, 3 - random)" : "Метод сортировки (1 - мин. цена, 2 - макс. цена, 3 - рандом)")]
public int sortmethod = 2;
}
}
public class Price {
[JsonProperty(en ? "Chance" : "Шанс выпадения")]
public float chance = 100f;
[JsonProperty(en ? "Minimum price" : "Минимальная цена")]
public float min_price = 0.01f;
[JsonProperty(en ? "Maximum price" : "Максимальная цена")]
public float max_price = 1f;
}
private void Init() {
_ins = this;
cfg = Config.ReadObject<PluginConfig>();
//if (cfg._drops.price_min <= 0) cfg._drops.price_min = 0.01f;
//if (cfg._drops.price_max <= cfg._drops.price_min) cfg._drops.price_max = cfg._drops.price_min + 0.01f;
if (cfg._advanced.sortmethod != 1 && cfg._advanced.sortmethod != 2 && cfg._advanced.sortmethod != 3) cfg._advanced.sortmethod = 1;
if (cfg._drops.randomprice == null || cfg._drops.randomprice.Count < 1) {
cfg._drops.randomprice = new List<Price>() {
new Price {
chance = 80f, min_price = 0.01f, max_price = 1f
}
, new Price {
chance = 10f, min_price = 1f, max_price = 2f
}
, new Price {
chance = 7f, min_price = 2f, max_price = 5f
}
, new Price {
chance = 3f, min_price = 5f, max_price = 10f
}
,
}
;
}
Config.WriteObject(cfg);
LoadData();
}
private void Unload() {
foreach (var player in BasePlayer.activePlayerList) CuiHelper.DestroyUi(player, Layer);
SaveData();
_ins = null;
}
private void OnServerSave() {
SaveData();
}
private void OnNewSave() {
if (cfg.WipeExpOnServerWipe) {
WipeEXP();
SaveData();
PrintWarning("Data was wiped successfully!");
}
}
protected override void LoadDefaultConfig() {
Config.WriteObject(new PluginConfig(), true);
}
public Dictionary<ulong, Data> PlayerData = new Dictionary<ulong, Data>();
public List<Draw> DrawData = new List<Draw>();
public class Draw {
public string winnername;
public ulong winnner;
public string image;
public string name;
}
public class Data {
public float EXP;
public string tradeurl;
public string playername;
public List<Skin> inventory = new List<Skin>();
}
public class Skin {
public string techname;
public string skinname;
public string url;
public int price = 0;
public float min;
public float max;
}
private void SaveData() {
Interface.Oxide.DataFileSystem.WriteObject($"{Title}/PlayerData", PlayerData);
Interface.Oxide.DataFileSystem.WriteObject($"{Title}/Draws", DrawData);
Interface.Oxide.DataFileSystem.WriteObject($"{Title}/ActiveDraw", users);
}
private void LoadData() {
try {
DrawData = Interface.Oxide.DataFileSystem.ReadObject<List<Draw>>($"{Title}/Draws");
PlayerData = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, Data>>($"{Title}/PlayerData");
users = Interface.Oxide.DataFileSystem.ReadObject<List<ulong>>($"{Title}/ActiveDraw");
}
catch (Exception e) {
PrintError(e.ToString());
}
if (DrawData == null) DrawData = new List<Draw>();
if (PlayerData == null) PlayerData = new Dictionary<ulong, Data>();
if (users == null) users = new List<ulong>();
}
void OnServerInitialized() {
if (string.IsNullOrEmpty(cfg._api.secretkey)) {
PrintError(en ? "Specify the secret key in the plugin configuration" : "Укажите секретный ключ в конфигурации плагина!");
// Interface.Oxide.UnloadPlugin(Title);
return;
}
TryConnectToAPI(true);
LoadImages();
cmd.AddChatCommand(cfg._access.commands, this, nameof(SkinsCMD));
if (cfg._access.privateuse && !string.IsNullOrEmpty(cfg._access.perm)) permission.RegisterPermission(cfg._access.perm, this);
foreach (var player in BasePlayer.activePlayerList) OnPlayerConnected(player);
}
void OnPlayerConnected(BasePlayer player) {
if (player.IsReceivingSnapshot) {
NextTick(() => OnPlayerConnected(player));
return;
}
if (!PlayerData.ContainsKey(player.userID)) PlayerData.Add(player.userID, new Data {
EXP = cfg._exp.defaultexp, playername = player.displayName
}
);
if (PlayerData[player.userID].playername != player.displayName) PlayerData[player.userID].playername = player.displayName;
}
void OnPlayerDisconnected(BasePlayer player) {
if (openui.Contains(player.userID)) openui.Remove(player.userID);
}
#endregion
#region Images
void LoadImages() {
if (!plugins.Exists("ImageLibrary")) {
PrintError(en ? "The mandatory ImageLibrary plugin was not found." : "Не найден обязательный плагин ImageLibrary.");
Interface.Oxide.UnloadPlugin(Title);
return;
}
foreach (var skin in DrawData) {
if (!ImageLibrary.Call<bool>("HasImage", skin.image + "/58fx58f")) ImageLibrary.Call("AddImage", skin.image + "/58fx58f", skin.image + "/58fx58f");
}
if (!ImageLibrary.Call<bool>("HasImage", "skindrops.icon")) ImageLibrary.Call("AddImage", "https://skindrops.org/storage/plugins/rust/skindrops/skindrops.icon.png", "skindrops.icon");
if (!ImageLibrary.Call<bool>("HasImage", "skindrops.settings")) ImageLibrary.Call("AddImage", "https://skindrops.org/storage/plugins/rust/skindrops/skindrops.settings.png", "skindrops.settings");
if (!ImageLibrary.Call<bool>("HasImage", "skindrops.notify")) ImageLibrary.Call("AddImage", "https://skindrops.org/storage/plugins/rust/skindrops/skindrops.notify.png", "skindrops.notify");
if (!ImageLibrary.Call<bool>("HasImage", "skindrops.unknownuser")) ImageLibrary.Call("AddImage", "https://skindrops.org/storage/plugins/rust/skindrops/skindrops.unknownuser.png", "skindrops.unknownuser");
}
#endregion
#region EXP hooks
void OnPlayerDeath(BasePlayer player, HitInfo info) {
if (player == null || info == null) return;
if (info.InitiatorPlayer == null) return;
if (info.InitiatorPlayer.IsNpc) return;
if (player.IsNpc && cfg._exp.killnpc) {
var killer = info.InitiatorPlayer;
if (killer != null) {
if (killer != player) GiveEXP(killer.userID, cfg._exp.killnpc_exp);
}
}
else if (cfg._exp.killplayer) {
var killer = info.InitiatorPlayer;
if (killer != null) {
if (!cfg._exp.teammatecheck) {
if (player.Team != null && killer.Team != null && player.Team == killer.Team) return;
}
if (killer != player) {
if (!player.IsNpc) GiveEXP(killer.userID, cfg._exp.killplayer_exp);
}
}
}
}
public List<NetworkableId> LootedBoxes = new List<NetworkableId>();
private void OnLootEntity(BasePlayer player, BaseEntity entity) {
if (player == null || entity == null || entity.net == null || !cfg._exp.crates) return;
if (player.IsNpc) return;
if (!cfg._exp._crates.ContainsKey(entity.ShortPrefabName)) return;
if (LootedBoxes.Contains(entity.net.ID)) return;
GiveEXP(player.userID, cfg._exp._crates[entity.ShortPrefabName]);
if (!LootedBoxes.Contains(entity.net.ID)) LootedBoxes.Add(entity.net.ID);
}
void OnCollectiblePickup(CollectibleEntity collectible, BasePlayer player) {
if (player == null || collectible == null || collectible.itemList == null || !cfg._exp.collectible) return;
foreach (var item in collectible.itemList) {
if (!cfg._exp._collectible.ContainsKey(item.itemDef.shortname)) return;
GiveEXP(player.userID, cfg._exp._collectible[item.itemDef.shortname]);
}
}
void OnGrowableGathered(GrowableEntity collectible, Item item, BasePlayer player) {
if (player == null || collectible == null || item == null || !cfg._exp.collectible) return;
if (!cfg._exp._collectible.ContainsKey(item.info.shortname)) return;
GiveEXP(player.userID, cfg._exp._collectible[item.info.shortname]);
}
void OnDispenserBonus(ResourceDispenser dispenser, BasePlayer player, Item item) {
if (dispenser == null || player == null || item == null || !cfg._exp.gather) return;
if (!cfg._exp._gather.ContainsKey(item.info.shortname)) return;
GiveEXP(player.userID, cfg._exp._gather[item.info.shortname]);
}
void OnEntityDeath(BaseEntity entity, HitInfo info) {
if (entity == null || info == null || info.InitiatorPlayer == null) return;
if (cfg._exp.barrel && cfg._exp._barrel.ContainsKey(entity.ShortPrefabName)) GiveEXP(info.InitiatorPlayer.userID, cfg._exp._barrel[entity.ShortPrefabName]);
}
#endregion
#region Commands
[ConsoleCommand("skindrops.giveskinbyname")]
private void cmdGiveSkinByName(ConsoleSystem.Arg arg) {
if (arg.Player() != null) if (!arg.Player().IsAdmin) return;
if (!arg.HasArgs(2)) {
PrintError("Usage: skindrops.giveskinbyname %STEAMID% %ITEM_NAME_FROM_SITE%");
return;
}
if (!ulong.TryParse(arg.Args[0], out var targetId)) {
PrintError("Wrong player ID!");
return;
}
if (!PlayerData.TryGetValue(targetId, out var playerData)) {
PrintError($"Player {targetId} not found in data!");
return;
}
if (string.IsNullOrEmpty(playerData.tradeurl)) {
PrintError($"Player {targetId} not configured their trade URL!");
return;
}
var skinName = string.Join(" ", arg.Args.Skip(1));
Dictionary<string, string> headers = new Dictionary<string, string> {
{
"Authorization", $"Bearer {cfg._api.secretkey}"
}
}
;
webrequest.Enqueue(api + "offers/create?" + playerData.tradeurl.Replace("https://steamcommunity.com/tradeoffer/new/?", "") + "&name=" + skinName.Replace(" ", "%20") + "&game=rust", null, (code, response) => {
if (code != 200) {
PrintError($"Error on sending request. Please, contact with us");
return;
}
var json = JsonConvert.DeserializeObject<Dictionary<string, object>>(response);
if ((bool)(json["success"]) != true) {
PrintError($"Error on sending request. Please, contact with us!\nError: {json["message"]}");
return;
}
PrintWarning($"Gived skin {skinName} to player {targetId}");
}
, this, RequestMethod.GET, headers);
}
void SkinsCMD(BasePlayer player, string command, string[] args) {
if (player == null) return;
if (cfg._access.privateuse && !permission.UserHasPermission(player.UserIDString, cfg._access.perm)) {
ChatMessage(player, GetMsg("NO_PERM", player.UserIDString));
return;
}
if (args.Length > 1) {
if (args[0] == "wipe") {
if (player.IsAdmin) {
WipeEXP();
ChatMessage(player, GetMsg("EXP_WIPED", player.UserIDString));
}
else {
ChatMessage(player, GetMsg("NO_PERM", player.UserIDString));
return;
}
}
}
DrawUI(player);
}
bool IsSteamID(string id) {
if (id.StartsWith("7656")) return true;
return false;
}
[ConsoleCommand("skindrops.giveskin")]
private void GiveSkin(ConsoleSystem.Arg args) {
if (args.Args.Length < 1) return;
if (!args.IsAdmin) return;
if (!IsSteamID(args.Args[0])) {
PrintError("Enter valid steamid to give skin");
return;
}
ulong winner = ulong.Parse(args.Args[0]);
if (!PlayerData.ContainsKey(winner)) PlayerData.Add(winner, new Data {
playername = "???"
}
);
if (args.Args.Length > 1) {
if (!float.TryParse(args.Args.ElementAtOrDefault(1)?.Replace(",", "."), out var minPrice)) {
PrintError("Invalid min-max price range. Usage: skindrops.giveskin <userid> <minprice> <maxprice>");
return;
}
if (!float.TryParse(args.Args.ElementAtOrDefault(2)?.Replace(",", "."), out var maxPrice)) {
PrintError("Invalid min-max price range. Usage: skindrops.giveskin <userid> <minprice> <maxprice>");
return;
}
if (minPrice > maxPrice) {
PrintError($"Min price ({minPrice}) must be greater than max price ({maxPrice}). Usage: skindrops.giveskin <userid> <minprice> <maxprice>");
return;
}
if (maxPrice - minPrice < 0.2f) {
PrintError($"Price range must be >= 0.2. Now - {maxPrice - minPrice}");
return;
}
if (minPrice < 0.1f) {
PrintError($"Price must be greater than 0.1!");
return;
}
var rangedPrice = new Price {
chance = 100, min_price = minPrice, max_price = maxPrice
}
;
if (rangedPrice == null) {
if (debug) Puts("GiveSkin - price is null in ranged price method");
CancelDraw(true);
return;
}
if (debug) Puts($"Ranged min - {rangedPrice.min_price}, ranged max - {rangedPrice.max_price}");
API_GetItem(winner, rangedPrice.min_price, rangedPrice.max_price);
PrintError($"Successfully gived a skin for {winner} in ranged price | Min - {minPrice}, Max - {maxPrice}");
return;
}
Price price = GetRandomPrice();
if (price == null) {
if (debug) Puts("GiveSkin - price is null");
CancelDraw(true);
return;
}
if (debug) Puts($"min - {price.min_price}, max - {price.max_price}");
API_GetItem(winner, price.min_price, price.max_price);
PrintError($"Successfully gived a skin for {winner}");
}
[ConsoleCommand("skindrops.wipe")]
private void WipeCMD(ConsoleSystem.Arg args) {
if (!args.IsAdmin) return;
if (args.IsClientside) {
BasePlayer player = args.Player();
if (player == null) return;
if (!player.IsAdmin) return;
WipeEXP();
}
else {
WipeEXP();
PrintWarning("EXP wiped.");
}
}
[ConsoleCommand("skindrops.open")]
private void OpenPage(ConsoleSystem.Arg args) {
BasePlayer player = args.Player();
if (player == null) return;
if (cfg._access.privateuse && !permission.UserHasPermission(player.UserIDString, cfg._access.perm)) return;
if (args.Args.Length < 1) return;
if (args.Args[0] == "drops") DrawUI(player, args.Args[0], "skins");
if (args.Args[0] == "inventory") DrawUI(player, args.Args[0], "skins");
}
[ConsoleCommand("skindrops.subpage")]
private void Subpage(ConsoleSystem.Arg args) {
BasePlayer player = args.Player();
if (player == null) return;
if (cfg._access.privateuse && !permission.UserHasPermission(player.UserIDString, cfg._access.perm)) return;
if (args.Args.Length < 1) return;
if (args.Args[0] != "skins" && args.Args[0] != "settings") return;
DrawUI(player, "drops", args.Args[0]);
}
[ConsoleCommand("skindrops.settradeurl")]
private void SetTradeURL(ConsoleSystem.Arg args) {
BasePlayer player = args.Player();
if (player == null) return;
if (cfg._access.privateuse && !permission.UserHasPermission(player.UserIDString, cfg._access.perm)) return;
if (args.Args.Length < 1) return;
if (args.Args[0].Contains("https://steamcommunity.com/tradeoffer/new/?partner=") && args.Args[0].Contains("&token=")) {
if (PlayerData.ContainsKey(player.userID)) PlayerData[player.userID].tradeurl = args.Args[0];
else PlayerData.Add(player.userID, new Data {
EXP = cfg._exp.defaultexp, playername = player.displayName, tradeurl = args.Args[0]
}
);
}
else {
player.SendConsoleCommand("skindrops.close");
ChatMessage(player, GetMsg("TRADEURL_ERROR", player.UserIDString));
return;
}
}
[ConsoleCommand("skindrops.payfordraw")]
private void PayForDraw(ConsoleSystem.Arg args) {
BasePlayer player = args.Player();
if (player == null) return;
if (cfg._access.privateuse && !permission.UserHasPermission(player.UserIDString, cfg._access.perm)) return;
CheckValidTradeURL(player);
}
[ConsoleCommand("skindrops.close")]
private void CloseUI(ConsoleSystem.Arg args) {
BasePlayer player = args.Player();
if (player == null) return;
if (cfg._access.privateuse && !permission.UserHasPermission(player.UserIDString, cfg._access.perm)) return;
if (openui.Contains(player.userID)) openui.Remove(player.userID);
CuiHelper.DestroyUi(player, Layer);
}
[ConsoleCommand("skindrops.getskin")]
private void GetSkin(ConsoleSystem.Arg args) {
BasePlayer player = args.Player();
if (player == null) return;
if (args.Args.Length < 1) return;
var name = string.Join(" ", args.Args);
if (!PlayerData.ContainsKey(player.userID)) return;
if (string.IsNullOrEmpty(PlayerData[player.userID].tradeurl) || !PlayerData[player.userID].tradeurl.StartsWith("https://steamcommunity.com/tradeoffer/new/?")) {
player.SendConsoleCommand("skindrops.close");
ChatMessage(player, GetMsg("TRADEURL_ERROR", player.UserIDString));
return;
}
var data = PlayerData[player.userID].inventory;
for (int i = 0; i < data.Count; i++) {
var item = data[i];
if (item == null) continue;
if (item.skinname == name) {
data.Remove(item);
DrawUI(player, "inventory", "skins");
ChatMessage(player, GetMsg("CHAT_ALERT_TRADE", player.UserIDString).Replace("{0}", item.skinname));
player.SendConsoleCommand($"note.inv -1685290200 -1 {item.skinname}");
SendTradeOffer(player.userID, PlayerData[player.userID].tradeurl, item.skinname, item.min, item.max);
return;
}
}
}
#endregion
#region Draw
void WipeEXP() {
foreach (var players in PlayerData) players.Value.EXP = 0;
SaveData();
}
public List<ulong> users = new List<ulong>();
void JoinDraw(BasePlayer player) {
if (cfg._access.privateuse && !permission.UserHasPermission(player.UserIDString, cfg._access.perm)) return;
if ((users.Count * cfg._drops.pay) >= cfg._drops.exp || users.Contains(player.userID)) return;
if (!LoseEXP(player.userID, cfg._drops.pay)) {
player.SendConsoleCommand("skindrops.close");
return;
}
if (!users.Contains(player.userID)) users.Add(player.userID);
if (openui.Contains(player.userID)) DrawUI(player);
ulong winner = GetWinner();
Price price = GetRandomPrice();
if (price == null) {
if (debug) Puts("JoinDraw - price is null");
CancelDraw(true);
return;
}
if (debug) Puts($"min - {price.min_price}, max - {price.max_price}");
if ((users.Count * cfg._drops.pay) >= cfg._drops.exp) API_GetItem(winner, price.min_price, price.max_price);
}
void GoDraw(ulong winner, BotItem item, float minprice, float maxprice) {
if (!PlayerData.ContainsKey(winner)) PlayerData.Add(winner, new Data());
var player = BasePlayer.FindByID(winner);
foreach (var p in users) {
var players = BasePlayer.FindByID(p);
if (players != null) CuiHelper.DestroyUi(players, Layer + "rafflebutton");
}
if (PlayerData.ContainsKey(winner)) {
string techname = GenerateName(item.name);
Skin skin = new Skin {
techname = techname, skinname = item.name, url = item.image + "/120fx120f", min = minprice, max = maxprice
}
;
if (!ImageLibrary.Call<bool>("HasImage", item.image + "/120fx120f")) ImageLibrary.Call("AddImage", item.image + "/120fx120f", item.image + "/120fx120f");
if (PlayerData[winner].inventory == null) PlayerData[winner].inventory = new List<Skin> {
skin
}
;
else PlayerData[winner].inventory.Add(skin);
}
if (DrawData.Count >= 2) DrawData.RemoveAt(0);
DrawData.Add(new Draw {
winnner = winner, winnername = PlayerData[winner].playername, image = item.image, name = item.name
}
);
if (cfg._drops.sound) {
foreach (var players in BasePlayer.activePlayerList) SendEffect(players, cfg._drops.soundprefab);
}
if (cfg._drops.chatmessage) {
foreach (var players in BasePlayer.activePlayerList) ChatMessage(players, GetMsg("CHAT_ALERT", player.UserIDString).Replace("{0}", PlayerData[winner].playername).Replace("{1}", item.name).Replace("{2}", item.price.ToString()));
}
if (cfg._drops.player_chatmessage && player != null) {
ChatMessage(player, GetMsg("CHAT_ALERT_PLAYER", player.UserIDString).Replace("{0}", item.name).Replace("{1}", item.price.ToString()));
}
if (player != null) player.SendConsoleCommand($"note.inv -1036635990 1 {item.name}");
if (cfg._drops.discordmessage) DMessage(PlayerData[winner].playername, winner, item.name, item.price + "$", item.image);
users.Clear();
timer.Once(1f, () => {
foreach (var players in BasePlayer.activePlayerList) {
if (openui.Contains(players.userID)) DrawUI(players);
}
}
);
}
void CancelDraw(bool notify = false, bool minimum = false) {
foreach (var player in users) GiveEXP(player, cfg._drops.pay);
if (!minimum) {
foreach (var player in BasePlayer.activePlayerList) ChatMessage(player, GetMsg("API_NOITEMS", player.UserIDString));
}
else {
foreach (var player in BasePlayer.activePlayerList) ChatMessage(player, GetMsg("API_NOPLAYERS", player.UserIDString));
}
users.Clear();
if (notify) DLog();
foreach (var player in BasePlayer.activePlayerList) if (openui.Contains(player.userID)) DrawUI(player);
}
ulong GetWinner() {
ulong winner = 0;
var list = users.Where(x => BasePlayer.FindByID(x) != null && PlayerData[x].inventory.Count < 18 && ParticipatesInDraw(x) && !string.IsNullOrEmpty(PlayerData[x].tradeurl) && PlayerData[x].tradeurl.StartsWith("https://steamcommunity.com/tradeoffer/new/?"));
winner = list.ElementAt(Oxide.Core.Random.Range(0, list.Count()));
return winner;
}
bool ParticipatesInDraw(ulong player) {
bool part = false;
if (cfg._access.privateuse) {
if (!permission.UserHasPermission(player.ToString(), cfg._access.perm)) part = false;
else part = true;
}
else part = true;
return part;
}
void SendTradeOffer(ulong player, string offer, string item, float minprice, float maxprice) {
TryConnectToAPI(false);
if (!connect) {
CancelDraw();
DLog();
return;
}
Dictionary<string, string> headers = new Dictionary<string, string> {
{
"Authorization", $"Bearer {cfg._api.secretkey}"
}
}
;
webrequest.Enqueue(api + "offers/create?" + offer.Replace("https://steamcommunity.com/tradeoffer/new/?", "") + "&name=" + item.Replace(" ", "%20") + "&game=rust", null, (code, response) => {
Log("SendTradeOffer" + api + "offers/create?" + offer.Replace("https://steamcommunity.com/tradeoffer/new/?", "") + "&name=" + item.Replace(" ", "%20") + "&game=rust" + " - code: " + code);
if (debug) Puts("SendTradeOffer code: " + code);
if (code != 200) return;
Offer myDeserializedClass = JsonConvert.DeserializeObject<Offer>(response);
if (debug) {
Log("SendTradeOffer message: " + myDeserializedClass.message);
Log("SendTradeOffer success: " + myDeserializedClass.success);
Puts("SendTradeOffer message: " + myDeserializedClass.message);
Puts("SendTradeOffer success: " + myDeserializedClass.success);
}
if (!myDeserializedClass.success) {
if (myDeserializedClass.message == "skin_unavailable") {
API_GetItem(player, minprice, maxprice);
return;
}
if (myDeserializedClass.message == "insufficient_funds" || myDeserializedClass.message == "offer_unavailable") {
CancelDraw();
DLog();
return;
}
if (myDeserializedClass.message == "trade_create_error") {
CancelDraw();
DLog();
return;
}
return;
}
Puts($"SendTradeOffer - Успешно отправлен трейд игроку {player}");
if (debug) Puts("SendTradeOffer " + $"Успешно отправлен трейд игроку {player}");
}
, this, Core.Libraries.RequestMethod.GET, headers);
}
public class Offer {
public bool success {
get;
set;
}
public string message {
get;
set;
}
}
float GetPrice(bool type = false) {
float price = 0;
if (cfg._drops.randomprice.Count < 1) return 0;
if (type == false) {
price = cfg._drops.randomprice.Min(p => p.min_price);
}
else {
price = cfg._drops.randomprice.Max(p => p.max_price);
}
return price;
}
public Price GetRandomPrice() {
var randomprice = cfg._drops.randomprice;
float totalChance = randomprice.Sum(x => x.chance);
float rand = UnityEngine.Random.Range(0f, totalChance);
float cumulativeChance = 0;
Price chosenPrice = null;
foreach (Price price in randomprice) {
cumulativeChance += price.chance;
if (rand < cumulativeChance) {
chosenPrice = price;
break;
}
}
if (chosenPrice != null) return chosenPrice;
else return randomprice[0];
}
public string GenerateName(string subname) {
var name = 0;
name = Oxide.Core.Random.Range(0, 100000);
return subname + name.ToString() + subname;
}
#endregion
#region API Works
List<Cache> skincache = new List<Cache>();
public class Cache {
public string image;
public double price;
}
void LoadSkinCache() {
if (!cfg._advanced.showskinlist || !connect) return;
if (cfg._advanced.sortmethod != 1 && cfg._advanced.sortmethod != 2 && cfg._advanced.sortmethod != 3) return;
var price_min = GetPrice(false);
var price_max = GetPrice(true);
if (price_min == 0 || price_max == 0) return;
skincache.Clear();
string apicache = api;
if (cfg._advanced.sortmethod == 2) apicache = api + $"items?sort=-items.price&filter[items.game]=rust&filter[max_price]={price_max}&page[size]=5";
if (cfg._advanced.sortmethod == 1) apicache = api + $"items?sort=items.price&filter[items.game]=rust&filter[min_price]={price_min}&page[size]=5";
if (cfg._advanced.sortmethod == 3) apicache = api + $"items?sort=items.skin_id&filter[items.game]=rust&filter[min_price]={price_min}&filter[max_price]={price_max}&page[size]=30";
Dictionary<string, string> headers = new Dictionary<string, string> {
{
"Authorization", $"Bearer {cfg._api.secretkey}"
}
}
;
webrequest.Enqueue(apicache, null, (codes, responses) => {
Log($"LoadSkinCache + {apicache} = code {codes}");
Log($"LoadSkinCache + {apicache} = responses {responses}");
if (debug) Puts($"LoadSkinCache + {apicache} " + codes);
if (debug) Puts($"LoadSkinCache + {apicache} " + responses);
if (codes != 200) {
connect = false;
PrintError(en ? "An API connection error occurred. Contact technical support to resolve the problem" : "Произошла ошибка подключения к API. Свяжитесь с технической поддержкой для решения проблемы");
return;
}
BotItems response_deserializeds = JsonConvert.DeserializeObject<BotItems>(responses);
if (response_deserializeds == null) {
connect = false;
Log($"LoadSkinCache + {apicache} = response_deserializeds null");
PrintError(en ? "An API connection error occurred. Contact technical support to resolve the problem" : "Произошла ошибка подключения к API. Свяжитесь с технической поддержкой для решения проблемы");
return;
}
if (response_deserializeds.items.Count < 5) {
Log($"LoadSkinCache + {apicache} = response_deserializeds response_deserializeds.items.Count {response_deserializeds.items.Count}");
PrintError(en ? "Failed to load cache of playable skins" : "Не удалось загрузить кэш разыгрываемых скинов");
cfg._advanced.showskinlist = false;
return;
}
List<BotItem> list = null;
if (cfg._advanced.sortmethod == 3) {
list = response_deserializeds.items.GroupBy(x => x.name) .Select(g => g.First()) .ToList().Take(5).ToList();
}
else list = response_deserializeds.items;
foreach (var item in list) {
skincache.Add(new Cache {
image = item.image, price = item.price
}
);
ImageLibrary.Call("AddImage", item.image + "/145fx145f", item.image + "/145fx145f");
}
Log($"Successfully obtained cache of skins to be played");
PrintWarning(en ? "Successfully obtained cache of skins to be played" : "Успешно получен кэш разыгрываемых скинов");
}
, this, Core.Libraries.RequestMethod.GET, headers);
}
bool connect = false;
string api = "https://skindrops.org/api/v1/";
Timer timers = null;
int tryes = 0;
void TryConnectToAPI(bool init = false) {
if (string.IsNullOrEmpty(cfg._api.secretkey)) {
PrintError(en ? "Specify the secret key in the plugin configuration" : "Укажите секретный ключ в конфигурации плагина!");
Interface.Oxide.UnloadPlugin(Title);
return;
}
if (init) PrintWarning(en ? "Trying to connect to the API.." : "Пытаемся подключиться к API..");
Dictionary<string, string> headers = new Dictionary<string, string> {
{
"Authorization", $"Bearer {cfg._api.secretkey}"
}
}
;
webrequest.Enqueue(api + "status", null, (code, response) => {
Log("status code: " + code);
Log("status response: " + response);
if (debug) Puts("status " + code);
if (debug) Puts("status " + response);
if (code != 200 || response == null || response.ToString() == "Error: NameResolutionFailure") {
connect = false;
if (init) PrintError(en ? "An API connection error occurred. Contact technical support to resolve the problem" : "Произошла ошибка подключения к API. Свяжитесь с технической поддержкой для решения проблемы");
else DLog();
if (tryes == 3) {
PrintError(en ? "An API connection error occurred. Contact technical support to resolve the problem" : "Произошла ошибка подключения к API. Свяжитесь с технической поддержкой для решения проблемы");
Interface.Oxide.UnloadPlugin(Title);
return;
}
tryes++;
PrintError(en ? $"[{tryes}/3] Reconnecting in 10 sec..." : $"[{tryes}/3] Переподключаемся через 10 сек...");
timers = timer.Once(10f, () => {
TryConnectToAPI(true);
}
);
return;
}
if (timers != null) {
timer.Destroy(ref timers);
timers = null;
}
Connection response_deserialized = JsonConvert.DeserializeObject<Connection>(response);
if (!response_deserialized.success) {
connect = false;
Log("status response_deserialized.success: " + response_deserialized.success);
if (init) PrintError(en ? "An API connection error occurred. Contact technical support to resolve the problem" : "Произошла ошибка подключения к API. Свяжитесь с технической поддержкой для решения проблемы");
else DLog();
return;
}
connect = true;
if (init) PrintWarning(en ? "Successfully connected to the API" : "Успешно подключились к API. Получаем список предметов..");
if (init) {
webrequest.Enqueue(api + $"items?sort=items.skin_id&filter[items.game]=rust&page[size]=1", null, (codes, responses) => {
Log("status + items?sort=items.skin_id&filter[items.game]=rust&page[size]=1 codes: " + codes);
Log("status + items?sort=items.skin_id&filter[items.game]=rust&page[size]=1 responses: " + responses);
if (debug) Puts("status + items?sort=items.skin_id&filter[items.game]=rust&page[size]=1 " + codes);
if (codes != 200) {
connect = false;
PrintError(en ? "An API connection error occurred. Contact technical support to resolve the problem" : "Произошла ошибка подключения к API. Свяжитесь с технической поддержкой для решения проблемы");
return;
}
BotItems response_deserializeds = JsonConvert.DeserializeObject<BotItems>(responses);
if (response_deserializeds == null) {
connect = false;
PrintError(en ? "An API connection error occurred. Contact technical support to resolve the problem" : "Произошла ошибка подключения к API. Свяжитесь с технической поддержкой для решения проблемы");
return;
}
if (init) PrintWarning(en ? $"Successfully obtained a list of items - available {response_deserializeds.count} items" : $"Успешно получили список предметов - доступно {response_deserializeds.count} предметов"
// ... [Code preview truncated due to length. Full source in attached file] ...