C#:
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using Facepunch;
/**
* This plugin will not do anything standalone.
*
* You need latest Epic Items or Item Perks installed.
* - configurations for epic buffs are maintained in Epic Items
* - epic buff effects are handled by Epic Items
* - configurations for perk buffs are maintained in Item Perks
* - perk buff effects are handled by Item Perks
* - most translations are used from Epic Items and Item Perks to make transformation easier
*
* TODOs by priority:
* - remove inconsitencies when using CuiPanel etc
* - revise UI component names and coords
*
* possible Features/Ideas
* - support for Item Retriever
* - support for economics and stuff -> might be needed by some servers (even when i dont like it^^)
* - Crafting XP -> used to unlock features?
* - Admin Inventory Inspector -> to look into players inventory and validate items
* - dont show costs if permission "free" is assigned -> maybe alternate text?
**/
namespace Oxide.Plugins {
[Info("AdvancedItemCrafting", "molokatan", "1.0.10"), Description("User Interface and advanced crafting options for Item Perks and Epic Loot")]
class AdvancedItemCrafting : RustPlugin {
[PluginReference]
private Plugin EpicLoot, ItemPerks, ImageLibrary;
// permissions for perk crafting
const string perm_perk_add = "advanceditemcrafting.perk.add";
const string perm_perk_remove = "advanceditemcrafting.perk.remove";
const string perm_perk_randomize = "advanceditemcrafting.perk.randomize";
// permission to bypass weight system
const string perm_perk_bypass_weighting = "advanceditemcrafting.perk.bypass_weighting";
const string perm_named_unveil = "advanceditemcrafting.named.unveil";
// amount usable kits for perk crafts if weighted
const string perm_kit_2 = "advanceditemcrafting.perk.kit2";
const string perm_kit_3 = "advanceditemcrafting.perk.kit3";
// permissions for epic items
const string perm_salvage = "advanceditemcrafting.epic.salvage";
const string perm_enhance = "advanceditemcrafting.epic.enhance";
// FIXME: need to check behavior for kits
const string perm_enhance_free = "advanceditemcrafting.free";
const string perm_settings_hide_button = "advanceditemcrafting.settings.hide_button";
// FIXME: this instance should be removed if possible
public static AdvancedItemCrafting Instance {
get;
set;
}
#region Hooks
string btn_icon = "assets/icons/inventory.png";
void OnServerInitialized() {
Instance = this;
if (config.user_experience.main_button.Enabled && !string.IsNullOrEmpty(config.user_experience.main_button.Icon)) {
if (config.user_experience.main_button.Icon.StartsWith("http")) {
if (ImageLibrary == null || !ImageLibrary.IsLoaded) {
Puts("Image Library has to be installed to support web images");
Interface.Oxide.UnloadPlugin(Name);
return;
}
else {
ImageLibrary?.Call("AddImage", config.user_experience.main_button.Icon, "ai_btn_img");
}
}
btn_icon = config.user_experience.main_button.Icon;
}
if (EpicLoot == null || !EpicLoot.IsLoaded) {
Puts("You must have Epic Loot installed to run this features.");
// Interface.Oxide.UnloadPlugin(Name);
}
else {
LoadEpicConfiguration();
}
if (ItemPerks == null || !ItemPerks.IsLoaded) {
Puts("You must have Item Perks installed to run this features.");
// Interface.Oxide.UnloadPlugin(Name);
}
else {
LoadItemPerksConfiguration();
}
RegisterPermissions();
if (BasePlayer.activePlayerList != null && config.user_experience.main_button.Enabled) {
foreach (var player in BasePlayer.activePlayerList) CreateMainButton(player);
}
if (!string.IsNullOrEmpty(config.chatCmd)) cmd.AddChatCommand(config.chatCmd, this, "ChatOpenInventory");
}
void OnPlayerDeath(BasePlayer player) {
CuiHelper.DestroyUi(player, MAIN_BUTTON);
CuiHelper.DestroyUi(player, BACKDROP_PANEL);
}
void OnPlayerRespawned(BasePlayer player) {
CreateMainButton(player);
}
void OnPlayerSleepEnded(BasePlayer player) {
CreateMainButton(player);
}
void Unload() {
foreach (var player in BasePlayer.activePlayerList) {
CuiHelper.DestroyUi(player, MAIN_BUTTON);
CuiHelper.DestroyUi(player, BACKDROP_PANEL);
}
if (!string.IsNullOrEmpty(config.chatCmd)) cmd.RemoveChatCommand(config.chatCmd, this);
}
#endregion Hooks
#region Permissions
void RegisterPermissions() {
permission.RegisterPermission(perm_perk_add, this);
permission.RegisterPermission(perm_perk_remove, this);
permission.RegisterPermission(perm_perk_randomize, this);
permission.RegisterPermission(perm_perk_bypass_weighting, this);
permission.RegisterPermission(perm_named_unveil, this);
permission.RegisterPermission(perm_kit_2, this);
permission.RegisterPermission(perm_kit_3, this);
permission.RegisterPermission(perm_salvage, this);
permission.RegisterPermission(perm_enhance, this);
permission.RegisterPermission(perm_enhance_free, this);
permission.RegisterPermission(perm_settings_hide_button, this);
}
int GetMaxKits(BasePlayer player) {
if (permission.UserHasPermission(player.UserIDString, perm_kit_3)) return 3;
if (permission.UserHasPermission(player.UserIDString, perm_kit_2)) return 2;
return 1;
}
#endregion Permissions
#region Commands
#region Commands:Inventory
void ChatOpenInventory(BasePlayer player) {
if (player == null) return;
var playerState = new PlayerState(player);
var builder = new ExtendedCuiElementContainer();
CreateInventoryBase(builder, player);
CreateInventoryItems(builder, playerState);
AddPlayerBuffsPanel(builder, player, playerState);
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdopeninventory")]
void CmdOpenInventory(ConsoleSystem.Arg arg) {
var player = arg.Player();
ChatOpenInventory(player);
}
[ConsoleCommand("cmdcloseinventory")]
void CmdCloseInventory(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
CuiHelper.DestroyUi(player, BACKDROP_PANEL);
}
#endregion Commands:Inventory
#region Commands:Settings
[ConsoleCommand("cmdopenaisettings")]
void CmdOpenSettings(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
var builder = new ExtendedCuiElementContainer();
CreateSettingsBox(builder, player);
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdcloseaisettings")]
void CmdCloseSettings(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
CuiHelper.DestroyUi(player, "AI_SETTINGS_BOX");
}
[ConsoleCommand("cmdtoggleaisetting")]
void CmdToggleSettings(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
string setting = arg.GetString(0);
switch (setting) {
case "mainbutton" : TogglePermission(player, perm_settings_hide_button);
CreateMainButton(player);
break;
// might add more settings later
}
var playerState = new PlayerState(player);
var builder = new ExtendedCuiElementContainer();
CreateInventoryBase(builder, player);
CreateInventoryItems(builder, playerState);
AddPlayerBuffsPanel(builder, player, playerState);
CreateSettingsBox(builder, player);
CuiHelper.AddUi(player, builder);
}
private void TogglePermission(BasePlayer player, string perm) {
if (permission.UserHasPermission(player.UserIDString, perm)) permission.RevokeUserPermission(player.UserIDString, perm);
else permission.GrantUserPermission(player.UserIDString, perm, null);
}
#endregion Commands:Settings
[ConsoleCommand("cmdselectitem")]
void CmdSelectItem(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
uint selectedItemUID = arg.GetUInt(0);
var item = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
if (item == null) return;
var baseItem = new BaseItem(item);
var builder = new ExtendedCuiElementContainer();
CreateItemDetailsBase(builder, player, baseItem);
CreateItemActions(builder, player, baseItem);
AddEpicBuffDetailsPanel(builder);
if (baseItem.buff != null && EpicLoot != null) {
EpicBuffDescription(builder, player, baseItem.buff.Buff);
}
HighlightSelectedItem(builder, baseItem);
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdcloseinfobox")]
void CmdCloseInfoBox(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
CuiHelper.DestroyUi(player, "AI_INFO_BOX");
}
#region Commands:Epic
[ConsoleCommand("cmdshowepicbuffselection")]
void CmdShowEpicBuffSelection(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
uint selectedItemUID = arg.GetUInt(0);
var item = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
if (item == null) return;
var selectedBuff = Buff.None;
if (!Enum.TryParse(arg.GetString(1), out selectedBuff)) return;
var opened = arg.GetBool(2);
var baseItem = new BaseItem(item);
var builder = new ExtendedCuiElementContainer();
AddEpicBuffDetailsPanel(builder);
AddEpicBuffSelector(builder, player, baseItem, selectedBuff, opened);
if (!opened) {
int cost;
if(selectedBuff == Buff.None || !epicConfig.scrapper_settings.enhancement_cost.TryGetValue(selectedBuff, out cost)) cost = 0;
var hasPayment = ItemAmountAvailable(player, epicConfig.scrapper_settings.currency_shortname, epicConfig.scrapper_settings.currency_skin, epicConfig.scrapper_settings.currency_name);
EpicBuffDescription(builder, player, selectedBuff);
AddEpicBuffConfirmPanel(builder, player, baseItem, selectedBuff, hasPayment >= cost);
}
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdconfirmepicselection")]
void CmdConfirmEpicSelection(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
CuiHelper.DestroyUi(player, EPIC_BUFF_DETAILS_PANEL);
var selectedItemUID = arg.GetUInt64(0);
var itemToEnhance = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
if (itemToEnhance == null) return;
var selectedBuff = Buff.None;
if (!Enum.TryParse(arg.GetString(1), out selectedBuff)) return;
int cost;
if(selectedBuff == Buff.None || !epicConfig.scrapper_settings.enhancement_cost.TryGetValue(selectedBuff, out cost)) return;
// FIXME: show an error message if it did not work?
if (!PayItems(player, epicConfig.scrapper_settings.currency_shortname, epicConfig.scrapper_settings.currency_skin, epicConfig.scrapper_settings.currency_name, cost)) return;
// we want to make sure only 1 item gets the epic buff
if (itemToEnhance.amount > 1) itemToEnhance = itemToEnhance.SplitItem(1);
EpicLoot?.Call<string>("GenerateItem", player, selectedBuff.ToString(), new List<string> {
itemToEnhance.info.shortname
}
, null, true, itemToEnhance);
itemToEnhance.SetItemOwnership(lang.GetMessage("OWNERSHIP_TAG_EPIC", this), GetOwnershipHeader(player));
// refeshing inventory to remove payments and show the new item
var playerState = new PlayerState(player);
var baseItem = new BaseItem(itemToEnhance);
var builder = new ExtendedCuiElementContainer();
CreateInventoryBase(builder, player);
CreateInventoryItems(builder, playerState);
AddPlayerBuffsPanel(builder, player, playerState);
CreateItemDetailsBase(builder, player, baseItem);
CreateItemActions(builder, player, baseItem);
AddEpicBuffDetailsPanel(builder);
EpicBuffDescription(builder, player, baseItem.buff.Buff);
HighlightSelectedItem(builder, baseItem);
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdcancelepicselection")]
void CmdCancelEpicSelection(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
CuiHelper.DestroyUi(player, EPIC_BUFF_DETAILS_PANEL);
}
[ConsoleCommand("cmdsalvageepicitem")]
void CmdSalvageEpicItem(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
var selectedItemUID = arg.GetUInt64(0);
var itemToSalvage = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
if (itemToSalvage == null) return;
var baseItem = new BaseItem(itemToSalvage);
int amount;
if (!epicConfig.scrapper_settings.scrapper_value.TryGetValue(baseItem.buff.Tier, out amount)) return;
if (itemToSalvage.amount > 1) itemToSalvage = itemToSalvage.SplitItem(1);
itemToSalvage.RemoveFromContainer();
var payment = ItemManager.CreateByName(epicConfig.scrapper_settings.currency_shortname, Math.Max(amount, 1), epicConfig.scrapper_settings.currency_skin);
if (payment == null) return;
if (!string.IsNullOrEmpty(epicConfig.scrapper_settings.currency_name)) payment.name = epicConfig.scrapper_settings.currency_name;
player.GiveItem(payment);
NextTick(() => itemToSalvage.Remove());
// refeshing inventory to remove item and add currency
var playerState = new PlayerState(player);
var builder = new ExtendedCuiElementContainer();
CreateInventoryBase(builder, player);
CreateInventoryItems(builder, playerState);
AddPlayerBuffsPanel(builder, player, playerState);
CuiHelper.AddUi(player, builder);
}
#endregion Commands:Epic
#region Commands:Perks
// select kits that increase chance for actions
[ConsoleCommand("cmdselectperkbuffs")]
void CmdSelectPerkBuffs(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
var selectedItemUID = arg.GetUInt64(0);
var itemToMod = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
if (itemToMod == null) return;
string action = arg.GetString(1);
var selectedPerks = CLI.Deserialize<List<Perk>>(arg.GetString(2));
BaseItem baseItem = new BaseItem(itemToMod);
CraftItem craftItem = GetCraftItem(action);
int additionalCost = GetCraftItemAmountRequired(craftItem, selectedPerks);
bool hasPayment = craftItem != null ? CraftItemAmountAvailable(player, craftItem) >= additionalCost : true;
bool isWeighted = IsWeightedAction(player, action);
var maxKits = 1;
if (isWeighted) maxKits = GetMaxKits(player);
else if (action == "cmdrandomizeperkvalues" || action == "cmdunveilperk") maxKits = 0;
string headerText = lang.GetMessage(action.ToUpper() + "_HEADER", this, player.UserIDString);
string infoMessageKey = $"{action.ToUpper()}_INFO{(maxKits > 1 ? "" : $"_ {
maxKits
}
")}";
string infoText = string.Format(lang.GetMessage(infoMessageKey, this, player.UserIDString), maxKits) + (isWeighted ? "\n"+lang.GetMessage("CAN_FAIL", this, player.UserIDString) : "");
ExtendedCuiElementContainer builder = new ExtendedCuiElementContainer();
SelectPerkBuffsPanel(builder, player, headerText, infoText, baseItem, selectedPerks, maxKits, action, hasPayment);
if (maxKits > 0) SelectKitPanel(builder, player, SELECT_PERK_BUFF_PANEL, 150, baseItem, selectedPerks, maxKits, action);
if (craftItem != null && additionalCost > 0) AdditionalCostPanel(builder, player, SELECT_PERK_BUFF_PANEL, craftItem, additionalCost, hasPayment);
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdcloseselectperkbuffs")]
void CmdCloseSelectPerkBuffs(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
// reload to make sure that inventory is updated
var playerState = new PlayerState(player);
var builder = new ExtendedCuiElementContainer();
CreateInventoryBase(builder, player);
CreateInventoryItems(builder, playerState);
AddPlayerBuffsPanel(builder, player, playerState);
if (arg.Args.Count() > 0) {
var selectedItemUID = arg.GetUInt64(0);
var itemToMod = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
var baseItem = new BaseItem(itemToMod);
CreateItemDetailsBase(builder, player, baseItem);
CreateItemActions(builder, player, baseItem);
HighlightSelectedItem(builder, baseItem);
}
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdunveilperk")]
void CmdUnveilPerk(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
var selectedItemUID = arg.GetUInt64(0);
var itemToMod = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
if (itemToMod == null) return;
if (!UnveilPerk(player, itemToMod)) return;
// refeshing inventory to remove payments
var playerState = new PlayerState(player);
var baseItem = new BaseItem(itemToMod);
// FIXME: can we reuse open inventory & select item?
var builder = new ExtendedCuiElementContainer();
CreateInventoryBase(builder, player);
CreateInventoryItems(builder, playerState);
AddPlayerBuffsPanel(builder, player, playerState);
CreateItemDetailsBase(builder, player, baseItem);
CreateItemActions(builder, player, baseItem);
HighlightSelectedItem(builder, baseItem);
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdaddperk")]
void CmdAddPerk(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
var selectedItemUID = arg.GetUInt64(0);
var itemToMod = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
if (itemToMod == null) return;
var selectedPerks = CLI.Deserialize<List<Perk>>(arg.GetString(1));
if (IsWeightedAction(player, "cmdaddperk")) {
if (!AddWeightedPerk(player, itemToMod, selectedPerks)) return;
}
else if (!AddUnweightedPerk(player, itemToMod, selectedPerks.First())) return;
// refeshing inventory to remove payments
var playerState = new PlayerState(player);
var baseItem = new BaseItem(itemToMod);
// FIXME: can we reuse open inventory & select item?
var builder = new ExtendedCuiElementContainer();
CreateInventoryBase(builder, player);
CreateInventoryItems(builder, playerState);
AddPlayerBuffsPanel(builder, player, playerState);
CreateItemDetailsBase(builder, player, baseItem);
CreateItemActions(builder, player, baseItem);
HighlightSelectedItem(builder, baseItem);
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdrandomizeperkvalues")]
void CmdRandomizePerkValues(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
var selectedItemUID = arg.GetUInt64(0);
var itemToMod = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
if (itemToMod == null) return;
var selectedPerks = CLI.Deserialize<List<Perk>>(arg.GetString(1));
if (!RandomizePerkValues(player, itemToMod, selectedPerks)) return;
// now we reopen the selection
player.SendConsoleCommand("cmdselectperkbuffs", itemToMod.uid.Value, "cmdrandomizeperkvalues", CLI.Serialize(new List<Perk>()));
}
[ConsoleCommand("cmdremoveperk")]
void CmdRemovePerk(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
var selectedItemUID = arg.GetUInt64(0);
var itemToMod = player.inventory.FindItemByUID(new ItemId(selectedItemUID));
if (itemToMod == null) return;
var selectedPerks = CLI.Deserialize<List<Perk>>(arg.GetString(1));
if (IsWeightedAction(player, "cmdremoveperk")) {
if (!RemoveWeightedPerk(player, itemToMod, selectedPerks)) return;
}
else if (!RemoveUnweightedPerk(player, itemToMod, selectedPerks.First()));
// refeshing inventory to remove payments
var playerState = new PlayerState(player);
var baseItem = new BaseItem(itemToMod);
// FIXME: can we reuse open inventory & select item?
var builder = new ExtendedCuiElementContainer();
CreateInventoryBase(builder, player);
CreateInventoryItems(builder, playerState);
AddPlayerBuffsPanel(builder, player, playerState);
CreateItemDetailsBase(builder, player, baseItem);
CreateItemActions(builder, player, baseItem);
CuiHelper.AddUi(player, builder);
}
[ConsoleCommand("cmdshowperkinfopanel")]
void CmdShowPerkInfoPanel(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
var perk = Perk.None;
if (!Enum.TryParse(arg.GetString(0), out perk)) return;
var builder = new ExtendedCuiElementContainer();
builder.AddInfoBox(lang.GetMessage("UI" + perk.ToString(), ItemPerks, player.UserIDString), GetPerkDescription(player, perk), lang.GetMessage("UI_OK", this, player.UserIDString));
CuiHelper.AddUi(player, builder);
}
#endregion Commands:Perks
#endregion Commands
#region Actions
#region Actions: Unveil
public bool UnveilPerk(BasePlayer player, Item itemToMod) {
BaseItem baseItem = new BaseItem(itemToMod);
if (!CanUnveilPerkBuff(baseItem)) return false;
CraftItem craftItem = config.craft_settings.unveil_perk_settings.craft_item;
int additionalCost = GetCraftItemAmountRequired(craftItem);
bool hasPayment = craftItem != null ? CraftItemAmountAvailable(player, craftItem) >= additionalCost : true;
if (!hasPayment) return false;
// if unrestricted, it will bypass white and blacklists, so we can unveil all perks -> makes them stronger
var list = config.craft_settings.unveil_perk_settings.unrestricted ? GetEnabledPerkSettings() : GetAvailablePerkSettingsForItem(baseItem);
if (list.Count == 0) return false;
// we can guarantee that perks with 0 weight are used for those items
var minWeight = config.craft_settings.unveil_perk_settings.min_weight_for_perks;
var perkWeights = list .ToDictionary(el => el.Key, el => el.Value.perkWeight >= minWeight ? el.Value.perkWeight : minWeight);
var totalWeight = perkWeights.Sum(a => a.Value);
var weighthit = new System.Random().Next(totalWeight);
Perk perkToAdd = Perk.None;
foreach (var perkWeight in perkWeights) {
weighthit -= perkWeight.Value;
if (weighthit <= 0) {
perkToAdd = perkWeight.Key;
break;
}
}
// FIXME: notify that adding perk failed -> NONE selected
if (perkToAdd.Equals(Perk.None)) return false;
PerkSettings perkMods;
if (!perkConfig.enhancementSettings.perk_settings.TryGetValue(perkToAdd, out perkMods)) return false;
float mod = (float)Math.Round(UnityEngine.Random.Range(perkMods.min_mod, perkMods.max_mod), 4);
// get payment
if (craftItem != null) if (!PayItems(player, craftItem.shortname, craftItem.skin, craftItem.display_name, additionalCost)) return false;
baseItem.perks.Add(new PerkEntry {
Perk = perkToAdd, Value = mod
}
);
// recreate new perk string
string perkString = "";
foreach (var perk in baseItem.perks) {
perkString += $"[{perk.Perk} {perk.Value}]";
}
if (baseItem.buff == null && string.IsNullOrEmpty(itemToMod.name)) {
if (baseItem.named) itemToMod.name = baseItem.DisplayName;
else itemToMod.name = $"{perkConfig.enhancementSettings.item_name_prefix} {itemToMod.info.displayName?.english}";
}
itemToMod.text = perkString;
itemToMod.text += "[named]";
itemToMod.SetItemOwnership(lang.GetMessage("OWNERSHIP_TAG_LEGENDARY", this), GetOwnershipHeader(player) + GetPerkOwnershipDescription(itemToMod));
itemToMod.MarkDirty();
if (config.craft_settings.unveil_perk_settings.success_effect != null) EffectNetwork.Send(new Effect(config.craft_settings.unveil_perk_settings.success_effect, player.transform.position, player.transform.position), player.net.connection);
return true;
}
#endregion
#region Actions: Perks
public bool AddUnweightedPerk(BasePlayer player, Item itemToMod, Perk selectedPerk) {
BaseItem baseItem = new BaseItem(itemToMod);
if (!CanReceivePerkBuff(baseItem)) return false;
CraftItem craftItem = config.craft_settings.add_perk_settings.craft_item;
int additionalCost = GetCraftItemAmountRequired(craftItem, new List<Perk> {
selectedPerk
}
);
bool hasPayment = craftItem != null ? CraftItemAmountAvailable(player, craftItem) >= additionalCost : true;
if (!hasPayment) return false;
var requiredKits = GetRequiredKitAmount(new List<Perk> {
selectedPerk
}
);
if (!HasKits(player, requiredKits)) return false;
PerkSettings perkMods;
if (!perkConfig.enhancementSettings.perk_settings.TryGetValue(selectedPerk, out perkMods)) return false;
if (!perkMods.enabled) return false;
if (perkMods.blacklist != null && perkMods.blacklist.Count > 0 && perkMods.blacklist.Contains(baseItem.shortname)) return false;
if (perkMods.whitelist != null && perkMods.whitelist.Count > 0 && !perkMods.whitelist.Contains(baseItem.shortname)) return false;
float mod = (float)Math.Round(UnityEngine.Random.Range(perkMods.min_mod, perkMods.max_mod), 4);
// get payment
if (craftItem != null) if (!PayItems(player, craftItem.shortname, craftItem.skin, craftItem.display_name, additionalCost)) return false;
if (!PayKits(player, requiredKits)) return false;
baseItem.perks.Add(new PerkEntry {
Perk = selectedPerk, Value = mod
}
);
// recreate new perk string
string perkString = "";
foreach (var perk in baseItem.perks) {
perkString += $"[{perk.Perk} {perk.Value}]";
}
// we want to make sure only 1 item gets the perk buff
if (itemToMod.amount > 1) itemToMod = itemToMod.SplitItem(1);
if (baseItem.buff == null && !string.IsNullOrEmpty(perkConfig.enhancementSettings.item_name_prefix) && string.IsNullOrEmpty(itemToMod.name)) itemToMod.name = $"{perkConfig.enhancementSettings.item_name_prefix} {itemToMod.info.displayName?.english}";
itemToMod.text = perkString;
itemToMod.SetItemOwnership(lang.GetMessage("OWNERSHIP_TAG_PERK", this), GetOwnershipHeader(player) + GetPerkOwnershipDescription(itemToMod));
itemToMod.MarkDirty();
return true;
}
public bool AddWeightedPerk(BasePlayer player, Item itemToMod, List<Perk> selectedPerks) {
BaseItem baseItem = new BaseItem(itemToMod);
if (!CanReceivePerkBuff(baseItem)) return false;
CraftItem craftItem = config.craft_settings.add_perk_settings.craft_item;
int additionalCost = GetCraftItemAmountRequired(craftItem, selectedPerks);
bool hasPayment = craftItem != null ? CraftItemAmountAvailable(player, craftItem) >= additionalCost : true;
if (!hasPayment) return false;
var requiredKits = GetRequiredKitAmount(selectedPerks);
if (!HasKits(player, requiredKits)) return false;
var perkMultiplier = GetPerkMultiplier(selectedPerks.Count, "cmdaddperk");
var perkWeights = GetAvailablePerkSettingsForItem(baseItem) .ToDictionary(el => el.Key, el => el.Value.perkWeight);
// modify perk weights with selected kits
foreach (var kit in requiredKits) perkWeights[kit.Key] += (int)(perkWeights[kit.Key] * requiredKits[kit.Key] * perkMultiplier);
var totalWeight = perkWeights.Sum(a => a.Value);
var weighthit = new System.Random().Next((totalWeight));
Perk perkToAdd = Perk.None;
foreach (var perkWeight in perkWeights) {
weighthit -= perkWeight.Value;
if (weighthit <= 0) {
perkToAdd = perkWeight.Key;
break;
}
}
// FIXME: notify that adding perk failed -> NONE selected
if (perkToAdd.Equals(Perk.None)) return false;
PerkSettings perkMods;
if (!perkConfig.enhancementSettings.perk_settings.TryGetValue(perkToAdd, out perkMods)) return false;
float mod = (float)Math.Round(UnityEngine.Random.Range(perkMods.min_mod, perkMods.max_mod), 4);
// get payment
if (craftItem != null) if (!PayItems(player, craftItem.shortname, craftItem.skin, craftItem.display_name, additionalCost)) return false;
if (!PayKits(player, requiredKits)) return false;
baseItem.perks.Add(new PerkEntry {
Perk = perkToAdd, Value = mod
}
);
// recreate new perk string
string perkString = "";
foreach (var perk in baseItem.perks) {
perkString += $"[{perk.Perk} {perk.Value}]";
}
// we want to make sure only 1 item gets the perk buff
if (itemToMod.amount > 1) itemToMod = itemToMod.SplitItem(1);
if (baseItem.buff == null && !string.IsNullOrEmpty(perkConfig.enhancementSettings.item_name_prefix) && string.IsNullOrEmpty(itemToMod.name)) itemToMod.name = $"{perkConfig.enhancementSettings.item_name_prefix} {itemToMod.info.displayName?.english}";
itemToMod.text = perkString;
itemToMod.SetItemOwnership(lang.GetMessage("OWNERSHIP_TAG_PERK", this), GetOwnershipHeader(player) + GetPerkOwnershipDescription(itemToMod));
itemToMod.MarkDirty();
if (config.craft_settings.add_perk_settings.weight_system.success_effect != null && (selectedPerks.Count == 0 || (selectedPerks.Count > 0 && selectedPerks.Contains(perkToAdd)))) EffectNetwork.Send(new Effect(config.craft_settings.add_perk_settings.weight_system.success_effect, player.transform.position, player.transform.position), player.net.connection);
else if (config.craft_settings.add_perk_settings.weight_system.fail_effect != null && selectedPerks.Count > 0) EffectNetwork.Send(new Effect(config.craft_settings.add_perk_settings.weight_system.fail_effect, player.transform.position, player.transform.position), player.net.connection);
return true;
}
public bool RemoveUnweightedPerk(BasePlayer player, Item itemToMod, Perk selectedPerk) {
BaseItem baseItem = new BaseItem(itemToMod);
if (!CanRemovePerkBuff(baseItem)) return false;
CraftItem craftItem = config.craft_settings.remove_perk_settings.craft_item;
int additionalCost = GetCraftItemAmountRequired(craftItem, new List<Perk> {
selectedPerk
}
);
bool hasPayment = craftItem != null ? CraftItemAmountAvailable(player, craftItem) >= additionalCost : true;
if (!hasPayment) return false;
var requiredKits = GetRequiredKitAmount(new List<Perk> {
selectedPerk
}
);
if (!HasKits(player, requiredKits)) return false;
PerkSettings perkMods;
if (!perkConfig.enhancementSettings.perk_settings.TryGetValue(selectedPerk, out perkMods)) return false;
if (!perkMods.enabled) return false;
var itemPerkKeys = baseItem.perks.Select(el => el.Perk);
// FIXME: notify that removing perk failed -> NONE selected
if (!itemPerkKeys.Contains(selectedPerk) || selectedPerk.Equals(Perk.None)) return false;
// get payment
if (craftItem != null) if (!PayItems(player, craftItem.shortname, craftItem.skin, craftItem.display_name, additionalCost)) return false;
if (!PayKits(player, requiredKits)) return false;
baseItem.perks.RemoveAt(baseItem.perks.FindIndex(i => i.Perk.Equals(selectedPerk)));
// recreate new perk string
string perkString = "";
foreach (var perk in baseItem.perks) {
perkString += $"[{perk.Perk} {perk.Value}]";
}
itemToMod.text = perkString;
itemToMod.SetItemOwnership(lang.GetMessage("OWNERSHIP_TAG_PERK", this), GetOwnershipHeader(player) + GetPerkOwnershipDescription(itemToMod));
itemToMod.MarkDirty();
return true;
}
public bool RemoveWeightedPerk(BasePlayer player, Item itemToMod, List<Perk> selectedPerks) {
BaseItem baseItem = new BaseItem(itemToMod);
if (!CanRemovePerkBuff(baseItem)) return false;
CraftItem craftItem = config.craft_settings.remove_perk_settings.craft_item;
int additionalCost = GetCraftItemAmountRequired(craftItem, selectedPerks);
bool hasPayment = craftItem != null ? CraftItemAmountAvailable(player, craftItem) >= additionalCost : true;
if (!hasPayment) return false;
var requiredKits = GetRequiredKitAmount(selectedPerks);
if (!HasKits(player, requiredKits)) return false;
var perkMultiplier = GetPerkMultiplier(selectedPerks.Count, "cmdremoveperk");
var itemPerkKeys = baseItem.perks.Select(el => el.Perk);
var perkWeights = GetAvailablePerkSettingsForItem(baseItem) .Where(el => itemPerkKeys.Contains(el.Key)) .ToDictionary(el => el.Key, el => el.Value.perkWeight);
// modify perk weights with selected kits
foreach (var kit in requiredKits) perkWeights[kit.Key] += (int)(perkWeights[kit.Key] * requiredKits[kit.Key] * perkMultiplier);
var totalWeight = perkWeights.Sum(a => a.Value);
var weighthit = new System.Random().Next((totalWeight));
Perk perkToRemove = Perk.None;
foreach (var perkWeight in perkWeights) {
weighthit -= perkWeight.Value;
if (weighthit <= 0) {
perkToRemove = perkWeight.Key;
break;
}
}
// FIXME: notify that removing perk failed -> NONE selected
if (perkToRemove.Equals(Perk.None)) return false;
// get payment
if (craftItem != null) if (!PayItems(player, craftItem.shortname, craftItem.skin, craftItem.display_name, additionalCost)) return false;
if (!PayKits(player, requiredKits)) return false;
baseItem.perks.RemoveAt(baseItem.perks.FindIndex(i => i.Perk.Equals(perkToRemove)));
// recreate new perk string
string perkString = "";
foreach (var perk in baseItem.perks) {
perkString += $"[{perk.Perk} {perk.Value}]";
}
itemToMod.text = perkString;
itemToMod.SetItemOwnership(lang.GetMessage("OWNERSHIP_TAG_PERK", this), GetOwnershipHeader(player) + GetPerkOwnershipDescription(itemToMod));
itemToMod.MarkDirty();
if (config.craft_settings.remove_perk_settings.weight_system.success_effect != null && (selectedPerks.Count == 0 || (selectedPerks.Count > 0 && selectedPerks.Contains(perkToRemove)))) EffectNetwork.Send(new Effect(config.craft_settings.remove_perk_settings.weight_system.success_effect, player.transform.position, player.transform.position), player.net.connection);
else if (config.craft_settings.remove_perk_settings.weight_system.fail_effect != null && selectedPerks.Count > 0) EffectNetwork.Send(new Effect(config.craft_settings.remove_perk_settings.weight_system.fail_effect, player.transform.position, player.transform.position), player.net.connection);
return true;
}
public bool RandomizePerkValues(BasePlayer player, Item itemToMod, List<Perk> selectedPerks) {
BaseItem baseItem = new BaseItem(itemToMod);
if (baseItem.perks.Count < 1) return false;
CraftItem craftItem = config.craft_settings.randomize_perk_settings.randomize_perk_item;
int additionalCost = GetCraftItemAmountRequired(craftItem, selectedPerks);
bool hasPayment = craftItem != null ? CraftItemAmountAvailable(player, craftItem) >= additionalCost : true;
if (!hasPayment) return false;
var requiredKits = GetRequiredKitAmount(selectedPerks);
if (!HasKits(player, requiredKits)) return false;
// get payment
if (craftItem != null) if (!PayItems(player, craftItem.shortname, craftItem.skin, craftItem.display_name, additionalCost)) return false;
if (!PayKits(player, requiredKits)) return false;
string perkString = string.Empty;
foreach (var entry in baseItem.perks) {
PerkSettings perkMods;
if (!perkConfig.enhancementSettings.perk_settings.TryGetValue(entry.Perk, out perkMods)) continue;
double mod = perkMods.min_mod;
int rollCount = 1;
if (requiredKits.TryGetValue(entry.Perk, out int amount) && amount > 0) {
rollCount++;
requiredKits[entry.Perk]--;
}
;
for (int i = 0; i<rollCount; i++) {
var roll = Math.Round(UnityEngine.Random.Range(perkMods.min_mod, perkMods.max_mod), 4);
if (roll <= mod) continue;
mod = roll;
}
perkString += $"[{entry.Perk.ToString()} {mod}]";
}
itemToMod.text = perkString;
itemToMod.SetItemOwnership(lang.GetMessage("OWNERSHIP_TAG_PERK", this), GetOwnershipHeader(player) + GetPerkOwnershipDescription(itemToMod));
itemToMod.MarkDirty();
if (config.craft_settings.randomize_perk_settings.success_effect != null) EffectNetwork.Send(new Effect(config.craft_settings.randomize_perk_settings.success_effect, player.transform.position, player.transform.position), player.net.connection);
return true;
}
public string GetOwnershipHeader(BasePlayer player) => string.Format(lang.GetMessage("OWNERSHIP_DESCRIPTION_HEADER", this), lang.GetMessage("OWNERSHIP_CREATED", this), player?.displayName);
public string GetPerkOwnershipDescription(Item item) {
List<PerkEntry> perks = new BaseItem(item).perks;
// FIXME: we want to keep the original creator of the item.
string perkString = string.Empty;
foreach(var perk in perks) {
string perkName = lang.GetMessage("UI" + perk.Perk.ToString(), ItemPerks);
perkString += string.Format(lang.GetMessage("OWNERSHIP_DESCRIPTION_PERK_LINE", this), perkName, GetPerkValue(perk.Value, perk.Perk), GetPerkTypeString(perk.Perk));
}
return string.Format(lang.GetMessage("OWNERSHIP_DESCRIPTION_PERKS", this), perkString);
}
public bool HasKits(BasePlayer player, Dictionary<Perk, int> requiredAmount) {
if (requiredAmount.Count == 0) return true;
foreach(var requiredPerk in requiredAmount) if (ItemAmountAvailable(player, perkConfig.enhancementSettings.enhancement_kit_settings.shortname, perkConfig.enhancementSettings.enhancement_kit_settings.skin, $"{perkConfig.enhancementSettings.enhancement_kit_settings.displayName} {requiredPerk.Key}") < requiredPerk.Value) return false;
return true;
}
public Dictionary<Perk, int> GetRequiredKitAmount(List<Perk> selectedPerks) {
Dictionary<Perk, int> requiredAmount = new Dictionary<Perk, int>();
foreach(var perk in selectedPerks) {
if (!requiredAmount.ContainsKey(perk)) requiredAmount.Add(perk, 1);
else requiredAmount[perk]++;
}
return requiredAmount;
}
public float GetPerkMultiplier(int count, string action) {
WeightRolls actionWeights = null;
switch (action) {
case "cmdaddperk" : actionWeights = config.craft_settings.add_perk_settings.weight_system;
break;
case "cmdremoveperk" : actionWeights = config.craft_settings.remove_perk_settings.weight_system;
break;
default : return 1f;
}
switch (count) {
case 1 : return actionWeights.multiplier_1;
case 2 : return actionWeights.multiplier_2;
case 3 : return actionWeights.multiplier_3;
default : return 1f;
}
}
public Dictionary<Perk, int> GetPerkWeights(string action, BaseItem baseItem, List<Perk> selectedPerks) {
var requiredKits = GetRequiredKitAmount(selectedPerks);
var perkMultiplier = GetPerkMultiplier(selectedPerks.Count, action);
var itemPerkKeys = baseItem.perks.Select(el => el.Perk);
var perkWeights = GetAvailablePerkSettingsForItem(baseItem) .Where(el => action == "cmdaddperk" || itemPerkKeys.Contains(el.Key)) .ToDictionary(el => el.Key, el => el.Value.perkWeight);
// modify perk weights with selected kits
foreach (var kit in requiredKits) perkWeights[kit.Key] += (int)(perkWeights[kit.Key] * requiredKits[kit.Key] * perkMultiplier);
return perkWeights;
}
public bool IsWeightedAction(BasePlayer player, string action) {
if (permission.UserHasPermission(player.UserIDString, perm_perk_bypass_weighting)) return false;
switch (action) {
case "cmdaddperk" : return config.craft_settings.add_perk_settings.weight_system.enabled;
case "cmdremoveperk" : return config.craft_settings.remove_perk_settings.weight_system.enabled;
case "cmdrandomizeperkvalues" : return config.craft_settings.randomize_perk_settings.allow_lucky_rolls;
}
return false;
}
public bool RequiresKit(string action) {
switch (action) {
case "cmdaddperk" : return config.craft_settings.add_perk_settings.weight_system.requires_kit;
case "cmdremoveperk" : return config.craft_settings.remove_perk_settings.weight_system.requires_kit;
case "cmdrandomizeperkvalues" : return config.craft_settings.randomize_perk_settings.requires_kit;
}
return false;
}
#endregion Actions: Perks
#region Actions:Payments
CraftItem GetCraftItem(string action) {
CraftItem item = null;
switch (action) {
case "cmdaddperk" : return config.craft_settings.add_perk_settings.craft_item;
case "cmdremoveperk" : return config.craft_settings.remove_perk_settings.craft_item;
case "cmdrandomizeperkvalues" : return config.craft_settings.randomize_perk_settings.randomize_perk_item;
case "cmdunveilperk" : return config.craft_settings.unveil_perk_settings.craft_item;
default : return null;
}
}
int GetCraftItemAmountRequired(CraftItem item, List<Perk> selectedPerks = null) {
if (item == null) return 0;
int amountRequired = item.amount;
if (selectedPerks == null) return amountRequired;
foreach (Perk perk in selectedPerks) if (item.cost_per_kit.TryGetValue(perk, out var value)) amountRequired += value;
return amountRequired;
}
int CraftItemAmountAvailable(BasePlayer player, CraftItem item) {
if (item == null) return 0;
return ItemAmountAvailable(player, item.shortname, item.skin, item.display_name);
}
int ItemAmountAvailable(BasePlayer player, string shortname, ulong skin, string name) {
// ... [Code preview truncated due to length. Full source in attached file] ...