C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
namespace Oxide.Plugins
{
[Info("Item Cost Calculator", "Absolut/Arainrr", "2.0.15", ResourceId = 2109)]
internal class ItemCostCalculator : RustPlugin
{
#region Fields
[PluginReference]
private Plugin ImageLibrary, RustTranslationAPI;
private readonly Dictionary<ItemDefinition, double> itemsCost = new Dictionary<ItemDefinition, double>();
private enum FileType
{
GUIShop,
ServerReward,
Ingredient,
}
#endregion Fields
#region Oxide Hooks
private void OnServerInitialized()
{
foreach (var itemDefinition in ItemManager.GetItemDefinitions())
{
if (!configData.displayNames.ContainsKey(itemDefinition.shortname))
{
configData.displayNames.Add(itemDefinition.shortname, itemDefinition.displayName.english);
}
var itemBlueprint = ItemManager.FindBlueprint(itemDefinition);
if (itemBlueprint != null)
{
foreach (var itemAmount in itemBlueprint.ingredients)
{
if (!configData.materials.ContainsKey(itemAmount.itemDef.shortname))
{
configData.materials.Add(itemAmount.itemDef.shortname, 1);
}
}
}
else if (!configData.noMaterials.ContainsKey(itemDefinition.shortname))
{
configData.noMaterials.Add(itemDefinition.shortname, 1);
}
}
foreach (var material in configData.materials)
{
if (configData.noMaterials.ContainsKey(material.Key))
{
configData.noMaterials.Remove(material.Key);
}
}
SaveConfig();
CalculateItemsCost();
foreach (FileType value in Enum.GetValues(typeof(FileType)))
{
CreateDataFile(value, lang.GetServerLanguage());
}
}
#endregion Oxide Hooks
#region Methods
private void CalculateItemsCost()
{
foreach (var itemDefinition in ItemManager.GetItemDefinitions())
{
double amount;
if (configData.materials.TryGetValue(itemDefinition.shortname, out amount))
{
itemsCost.Add(itemDefinition, amount);
continue;
}
if (configData.noMaterials.TryGetValue(itemDefinition.shortname, out amount))
{
itemsCost.Add(itemDefinition, amount);
continue;
}
var itemBlueprint = ItemManager.FindBlueprint(itemDefinition);
if (itemBlueprint == null)
continue;
double cost = 0;
var ingredients = GetItemIngredients(itemBlueprint);
foreach (var ingredient in ingredients)
{
if (configData.materials.TryGetValue(ingredient.Key.shortname, out amount))
{
cost += ingredient.Value * amount;
}
else
{
if (itemsCost.TryGetValue(ingredient.Key, out amount))
{
cost += ingredient.Value * amount;
}
}
}
if (cost > 0)
{
cost /= itemBlueprint.amountToCreate;
if (configData.gatherRateOffset > 0f)
{
cost *= configData.gatherRateOffset;
}
int rarity;
if (configData.rarityList.TryGetValue(itemDefinition.shortname, out rarity) && rarity > 0f)
{
cost += cost * (rarity / 100d);
}
float level;
if (configData.workbenchMultiplier.TryGetValue(itemBlueprint.workbenchLevelRequired, out level) && level > 0f)
{
cost += cost * (level / 100d);
}
itemsCost.Add(itemDefinition, cost);
}
}
}
private Dictionary<ItemDefinition, int> GetItemIngredients(ItemBlueprint itemBlueprint)
{
var ingredients = new Dictionary<ItemDefinition, int>();
Dictionary<string, int> ingredientsOverride;
if (configData.ingredientsOverride.TryGetValue(itemBlueprint.targetItem.shortname, out ingredientsOverride))
{
foreach (var ingredientOverride in ingredientsOverride)
{
var itemDefinition = ItemManager.FindItemDefinition(ingredientOverride.Key);
if (itemDefinition != null)
{
ingredients.Add(itemDefinition, ingredientOverride.Value);
}
}
return ingredients;
}
foreach (var itemAmount in itemBlueprint.ingredients)
{
ingredients.Add(itemAmount.itemDef, (int)itemAmount.amount);
}
return ingredients;
}
private void CreateDataFile(FileType fileType, string language = null)
{
if (!string.IsNullOrEmpty(language) && RustTranslationAPI != null && !IsSupportedLanguage(language))
{
language = "en";
}
switch (fileType)
{
case FileType.GUIShop:
{
var guiShopData = new ShopData();
var itemDisplayNames = new Dictionary<string, string>();
foreach (var entry in itemsCost)
{
var displayName = GetItemDisplayName(language, entry.Key);
if (string.IsNullOrEmpty(displayName))
continue;
var displayNameKey = displayName;
var imageUrl = GetImageUrl(entry.Key);
if (guiShopData.ShopItems.ContainsKey(displayNameKey))
{
displayNameKey += $"_Repeat_{UnityEngine.Random.Range(0, 9999)}";
}
if (!guiShopData.ShopItems.ContainsKey(displayNameKey))
{
itemDisplayNames.Add(entry.Key.shortname, displayNameKey);
guiShopData.ShopItems.Add(displayNameKey, new ShopItem
{
DisplayName = displayName,
Shortname = entry.Key.shortname,
EnableBuy = true,
EnableSell = true,
BuyPrice = Math.Round(entry.Value, configData.keepdecimal),
SellPrice = Math.Round(entry.Value * configData.recoveryRate, configData.keepdecimal),
Image = !string.IsNullOrEmpty(imageUrl) ? imageUrl : $"https://rustlabs.com/img/items180/{entry.Key.shortname}.png",
});
}
}
guiShopData.ShopItems = guiShopData.ShopItems.OrderBy(p => p.Key).ToDictionary(p => p.Key, o => o.Value);
foreach (var itemDefinition in ItemManager.GetItemDefinitions())
{
ShopCategory shopCategory;
var categoryKey = itemDefinition.category.ToString();
if (!guiShopData.ShopCategories.TryGetValue(categoryKey, out shopCategory))
{
shopCategory = new ShopCategory
{
DisplayName = categoryKey,
EnabledCategory = true,
Description = "You currently have {0} coins to spend in the " + categoryKey + " shop",
};
guiShopData.ShopCategories.Add(categoryKey, shopCategory);
}
string displayName;
if (!itemDisplayNames.TryGetValue(itemDefinition.shortname, out displayName))
{
displayName = itemDefinition.displayName.english;
}
if (!string.IsNullOrEmpty(displayName))
{
shopCategory.Items.Add(displayName);
}
}
SaveData("GUIShop", guiShopData);
PrintWarning("GUIShop successfully created, data file path: data/ItemCostCalculator/ItemCostCalculator_GUIShop.json");
return;
}
case FileType.ServerReward:
{
var serverRewardsData = new Dictionary<string, RewardData>();
var skin = 0UL;
foreach (var entry in itemsCost)
{
var displayName = GetItemDisplayName(language, entry.Key);
if (string.IsNullOrEmpty(displayName))
continue;
var shortName = $"{entry.Key.shortname}_{skin}";
Category category;
if (!Enum.TryParse(entry.Key.category.ToString(), out category))
{
category = Category.None;
}
if (!serverRewardsData.ContainsKey(shortName))
{
serverRewardsData.Add(shortName, new RewardData
{
shortname = entry.Key.shortname,
amount = 1,
skinId = skin,
isBp = false,
category = category,
displayName = displayName,
cost = (int)Math.Round(entry.Value),
cooldown = 0,
});
}
}
serverRewardsData = serverRewardsData.OrderBy(p => p.Key).ToDictionary(p => p.Key, o => o.Value);
SaveData("ServerRewards", serverRewardsData);
PrintWarning("ServerRewards successfully created, data file path: data/ItemCostCalculator/ItemCostCalculator_ServerRewards.json");
return;
}
case FileType.Ingredient:
{
var itemIngredients = new Dictionary<string, Ingredient>();
foreach (var itemDefinition in ItemManager.GetItemDefinitions())
{
var itemBlueprint = ItemManager.FindBlueprint(itemDefinition);
if (itemBlueprint != null)
{
Ingredient ingredient = new Ingredient();
ingredient.description = $"{string.Join(", ", itemBlueprint.ingredients.Select(x => $"'{x.itemDef.shortname} x{x.amount}"))}' to craft '{itemDefinition.shortname} x{itemBlueprint.amountToCreate}'.";
ingredient.amountToCreate = itemBlueprint.amountToCreate;
ingredient.ingredients = new Dictionary<string, int>();
foreach (var itemAmount in itemBlueprint.ingredients)
{
ingredient.ingredients.Add(itemAmount.itemDef.shortname, (int)itemAmount.amount);
}
itemIngredients.Add(itemDefinition.shortname, ingredient);
}
}
SaveData("ItemIngredients", itemIngredients);
PrintWarning("ItemIngredients successfully created, data file path: data/ItemCostCalculator/ItemCostCalculator_ItemIngredients.json");
return;
}
}
}
private struct Ingredient
{
public string description;
public int amountToCreate;
public Dictionary<string, int> ingredients;
}
private string GetImageUrl(ItemDefinition itemDefinition, ulong skin = 0)
{
if (ImageLibrary == null)
return null;
return (string)ImageLibrary.Call("GetImageURL", itemDefinition.shortname, skin);
}
#region RustTranslationAPI
private bool IsSupportedLanguage(string language) => (bool)RustTranslationAPI.Call("IsSupportedLanguage", language);
private string GetItemTranslationByShortName(string language, string itemShortName) => (string)RustTranslationAPI.Call("GetItemTranslationByShortName", language, itemShortName);
private string GetItemDisplayName(string language, ItemDefinition itemDefinition)
{
string displayName;
if (RustTranslationAPI != null)
{
displayName = GetItemTranslationByShortName(language, itemDefinition.shortname);
if (!string.IsNullOrEmpty(displayName))
{
return displayName;
}
}
if (configData.displayNames.TryGetValue(itemDefinition.shortname, out displayName))
{
return displayName;
}
return itemDefinition.displayName.english;
}
#endregion RustTranslationAPI
#endregion Methods
#region API
private double GetItemCost(string shortname)
{
var itemDefinition = ItemManager.FindItemDefinition(shortname);
if (itemDefinition == null)
return -1;
return GetItemCost(itemDefinition);
}
private double GetItemCost(int itemID)
{
var itemDefinition = ItemManager.FindItemDefinition(itemID);
if (itemDefinition == null)
return -1;
return GetItemCost(itemDefinition);
}
private double GetItemCost(ItemDefinition itemDefinition)
{
double cost;
if (!itemsCost.TryGetValue(itemDefinition, out cost))
{
return -1;
}
return cost;
}
private Dictionary<string, double> GetItemsCostByShortName()
{
return itemsCost.ToDictionary(x => x.Key.shortname, y => y.Value);
}
private Dictionary<int, double> GetItemsCostByID()
{
return itemsCost.ToDictionary(x => x.Key.itemid, y => y.Value);
}
private Dictionary<ItemDefinition, double> GetItemsCostByDefinition()
{
return itemsCost.ToDictionary(x => x.Key, y => y.Value);
}
#endregion API
#region Commands
[ConsoleCommand("costfile")]
private void CmdCostFile(ConsoleSystem.Arg arg)
{
if (!arg.HasArgs() || !arg.IsAdmin)
{
goto SyntaxError;
}
var language = arg.Args.Length > 1 ? arg.Args[1].ToLower() : null;
switch (arg.Args[0].ToLower())
{
case "shop":
CreateDataFile(FileType.GUIShop, language);
return;
case "reward":
CreateDataFile(FileType.ServerReward, language);
return;
case "ingredients":
CreateDataFile(FileType.Ingredient, language);
return;
}
SyntaxError:
SendReply(arg, "Syntax error, please type 'costfile <shop / reward / ingredients> [language]'");
}
#endregion Commands
#region Configuration
private ConfigData configData;
private class ConfigData
{
[JsonProperty(PropertyName = "GUIShop - Recovery rate (Sell / Buy)")]
public float recoveryRate = 0.5f;
[JsonProperty(PropertyName = "GUIShop - Keep decimal")]
public int keepdecimal = 2;
[JsonProperty(PropertyName = "Gather rate offset")]
public float gatherRateOffset = 1f;
[JsonProperty(PropertyName = "Materials list")]
public Dictionary<string, double> materials = new Dictionary<string, double>();
[JsonProperty(PropertyName = "No materials list")]
public Dictionary<string, double> noMaterials = new Dictionary<string, double>();
[JsonProperty(PropertyName = "Rarity list", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, int> rarityList = new Dictionary<string, int>
{
["timed.explosive"] = 50,
["rifle.bolt"] = 20,
["hammer"] = 0
};
[JsonProperty(PropertyName = "Workbench level multiplier")]
public Dictionary<int, float> workbenchMultiplier = new Dictionary<int, float>
{
[0] = 0,
[1] = 0,
[2] = 0,
[3] = 0
};
[JsonProperty(PropertyName = "Item ingredients override", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, Dictionary<string, int>> ingredientsOverride = new Dictionary<string, Dictionary<string, int>>
{
["horse.shoes.basic"] = new Dictionary<string, int>
{
["metal.fragments"] = 50,
}
};
[JsonProperty(PropertyName = "Item displayNames")]
public Dictionary<string, string> displayNames = new Dictionary<string, string>();
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
configData = Config.ReadObject<ConfigData>();
if (configData == null)
LoadDefaultConfig();
}
catch (Exception ex)
{
PrintError($"The configuration file is corrupted.\n{ex}");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void LoadDefaultConfig()
{
PrintWarning("Creating a new configuration file");
configData = new ConfigData();
}
protected override void SaveConfig()
{
//configData.materials = configData.materials.OrderBy(p => p.Key).ToDictionary(p => p.Key, o => o.Value);
//configData.noMaterials = configData.noMaterials.OrderBy(p => p.Key).ToDictionary(p => p.Key, o => o.Value);
Config.WriteObject(configData);
}
#endregion Configuration
#region DataFile
//From Server Rewards
private enum Category
{
None,
Weapon,
Construction,
Items,
Resources,
Attire,
Tool,
Medical,
Food,
Ammunition,
Traps,
Misc,
Component,
Electrical,
Fun
}
private class RewardData
{
public string shortname;
public string customIcon;
public int amount;
public ulong skinId;
public bool isBp;
public Category category;
public string displayName;
public int cost;
public int cooldown;
}
//From GUI Shop
private class ShopData
{
[JsonProperty("Shop - Shop Categories")]
public Dictionary<string, ShopCategory> ShopCategories = new Dictionary<string, ShopCategory>();
[JsonProperty("Shop - Shop List")]
public Dictionary<string, ShopItem> ShopItems = new Dictionary<string, ShopItem>();
}
private class ShopItem
{
public string DisplayName;
public bool CraftAsDisplayName = false;
public string Shortname;
public int ItemId;
public bool MakeBlueprint = false;
public bool AllowSellOfUsedItems = false;
public float Condition;
public bool EnableBuy = true;
public bool EnableSell = true;
public string Image = "";
public double SellPrice;
public double BuyPrice;
public int BuyCooldown;
public int SellCooldown;
public int[] BuyQuantity = { 1, 10, 100, 1000 };
public int[] SellQuantity = { 1, 10, 100, 1000 };
public int BuyLimit = 0;
public int BuyLimitResetCoolDown = 0;
public bool SwapLimitToQuantityBuyLimit;
public int SellLimit = 0;
public int SellLimitResetCoolDown = 0;
public bool SwapLimitToQuantitySoldLimit;
public string KitName = "";
public List<string> Command = new List<string>();
public bool RunCommandAndCustomShopItem = false;
public List<char> GeneTypes = new List<char>();
public ulong SkinId;
}
private class ShopCategory
{
public string DisplayName;
public string DisplayNameColor = null;
public string Description;
public string DescriptionColor = null;
public string Permission = "";
public string Currency = "";
public bool CustomCurrencyAllowSellOfUsedItems;
public string CustomCurrencyNames = "";
public int CustomCurrencyIDs = -0;
public ulong CustomCurrencySkinIDs = 0;
public bool EnabledCategory;
public bool EnableNPC;
public string NPCId = "";
public HashSet<string> NpcIds = new HashSet<string>();
public HashSet<string> Items = new HashSet<string>();
}
private void SaveData<T>(string name, T data) => Interface.Oxide.DataFileSystem.WriteObject(Name + "/" + Name + "_" + name, data);
#endregion DataFile
}
}