using System;
using System.Collections.Generic;
using Oxide.Core;
using System.Linq;
namespace Oxide.Plugins
{
[Info("Magic Sets", "Norn", "0.2.0")]
[Description("Allows users to store and create custom gearsets with one command")]
class MagicSets : RustPlugin
{
string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
private const string PERMISSION_DEFAULT = "MagicSets.default";
private const string PERMISSION_VIP = "MagicSets.vip1";
private const string PERMISSION_VIP = "MagicSets.vip2";
private const string PERMISSION_VIP = "MagicSets.vip3";
private const string PERMISSION_VIP = "MagicSets.vip4";
private List<SetInfo> Sets = new List<SetInfo>();
class SetInfo
{
public int ID;
public ulong OwnerID;
public string Name;
public bool Public;
public int TimesUsed;
public long LastUsed;
public List<InternalItemInfo> Contents = new List<InternalItemInfo>();
public SetInfo() { }
}
class InternalItemInfo
{
public string Shortname;
public ulong SkinID;
public InternalItemInfo() { }
}
void Init()
{
permission.RegisterPermission(PERMISSION_DEFAULT, this);
permission.RegisterPermission(PERMISSION_VIP1, this);
permission.RegisterPermission(PERMISSION_VIP2, this);
permission.RegisterPermission(PERMISSION_VIP3, this);
permission.RegisterPermission(PERMISSION_VIP4, this);
LoadData();
}
void Unload() { SaveData(); }
private long UnixTimestamp() => DateTimeOffset.UtcNow.ToUnixTimeSeconds();
private DateTime UnixToDateTime(long unixTime) { DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); return dt.AddSeconds(unixTime).ToLocalTime(); }
private long DateTimeToUnixTimestamp(DateTime dateTime) => (long)(TimeZoneInfo.ConvertTimeToUtc(dateTime) - new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc)).TotalSeconds;
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["NoValue"] = "<color=#e5f441>USAGE:</color> /sets [Add | Remove | Create | Reskin | List | Public | Clear]",
["ClearSuccess"] = "<color=#e5f441>INFO:</color> You have <color=#41f45e>successfully</color> removed {0} sets.",
["NoSets"] = "<color=#e5f441>INFO:</color> You have <color=#f44141>no</color> sets.",
["SetAlreadyExists"] = "<color=#e5f441>INFO:</color> A set with the name <color=#f44141>{0}</color> already exists!",
["SetAdded"] = "<color=#e5f441>INFO:</color> You have <color=#41f45e>added</color> a new set! (<color=#e5f441>{0}</color>)\n<color=#e5f441>/sets create <color=#41f45e>{1}</color> to create it</color>",
["SetAddUsage"] = "<color=#e5f441>USAGE:</color> /sets add <color=#41f45e>name</color>",
["SetNotExist"] = "<color=#e5f441>INFO:</color> A set with the name <color=#f44141>{0}</color> does not exist.",
["SetNotExistID"] = "<color=#e5f441>INFO:</color> Set with the ID <color=#f44141>{0}</color> does not exist.",
["SetNotPublic"] = "<color=#e5f441>INFO:</color> Set with id <color=#f44141>{0}</color> is not public.",
["SetRemoved"] = "<color=#e5f441>INFO:</color> You have successfully removed the set <color=#f44141>{0}</color>.",
["SetPublicUpdated"] = "<color=#e5f441>INFO:</color> Set <color=#f44141>{0}</color> is now <color=#e5f441>Public:</color> {1}.",
["RemoveUsage"] = "<color=#e5f441>USAGE:</color> /sets remove <color=#41f45e>name</color>",
["ReskinUsage"] = "<color=#e5f441>USAGE:</color> /sets reskin <color=#41f45e>name</color> or /sets reskin id <color=#41f45e>id</color> to reskin wearable items to a specific set.",
["ReskinSuccessful"] = "<color=#e5f441>INFO:</color> Successfully reskinned <color=#41f45e>{0}</color> items using set: <color=#e5f441>{1}</color>, ID: <color=#e5f441>{2}</color>.",
["PublicUsage"] = "<color=#e5f441>USAGE:</color> /sets public <color=#41f45e>name</color>",
["MaxSets"] = "You have reached your <color=#e5f441>maximum</color> allowed sets. (<color=#f44141>{0}</color>)",
["ListFormat"] = "<color=#42ebf4>{0}</color>\n<color=#e5f441>ID:</color> {1}, <color=#e5f441>Times Used:</color> {2}, <color=#e5f441>Public:</color> {3},\n<color=#e5f441>Contents:</color> {4}.",
["SetCreated"] = "<color=#e5f441>INFO:</color> You have successfully created a set!\n<color=#e5f441>Name:</color> {0}, <color=#e5f441>ID:</color> {1}.",
["NotEnoughResources"] = "<color=#f44141>ERROR:</color> You do not have enough resources to create set (<color=#41f45e>{0}</color>).",
["NOBPS"] = "<color=#f44141>ERROR:</color> You do not have the blueprints required to craft any of the items in this set or the set has corrupted.",
["MissingBPS"] = "<color=#e5f441>INFO:</color> <color=#f44141>{0}</color> items weren't crafted due to missing blueprints.",
["SetCreateUsage"] = "<color=#e5f441>USAGE:</color> /sets create <color=#41f45e>name</color> or /sets create id <color=#41f45e>id</color>",
["NoItems"] = "<color=#e5f441>INFO:</color> You are <color=#f44141>not</color> wearing any <color=#e5f441>suitable</color> gear. Please equip the items you want to add to a set.",
["NoPermission"] = "<color=#e5f441>INFO:</color> You <color=#f44141>don't</color> have access to this command.",
["NoWearables"] = "<color=#e5f441>INFO:</color> You <color=#f44141>must</color> be wearing attire to be able to reskin.",
["ReskinFailed"] = "<color=#e5f441>INFO:</color> Reskin has failed, you have <color=#f44141>0</color> items that match the set."
}, this);
}
protected override void LoadDefaultConfig()
{
Puts("No configuration file found, generating...");
Config["General", "RequireBP"] = true;
Config["General", "RemoveOldEntries"] = true;
Config["General", "ChargeForSets"] = true;
Config["Limits", "Limit"] = 1;
Config["Limits", "LimitVIP"] = 2;
Config["Limits", "LimitVIP"] = 3;
Config["Limits", "LimitVIP"] = 4;
Config["Limits", "LimitVIP"] = 5;
Config["Limits", "DaysUntilRemoval"] = 30;
Config["Misc", "ForceWear"] = false;
}
private List<ItemAmount> ReturnIngredients(ItemDefinition item)
{
var bp = ItemManager.FindBlueprint(item);
if (bp == null) return null;
return bp.ingredients;
}
void LoadData()
{
Sets = Interface.Oxide.DataFileSystem.ReadObject<List<SetInfo>>(Name);
if(Config["General", "ChargeForSets"] == null) { Config["General", "ChargeForSets"] = true; SaveConfig(); }
Puts("Loaded " + Sets.Count + " sets from /data/" + Name + ".json");
Puts("Users require blueprints to craft set items: " + Convert.ToString(Config["General", "RequireBP"]).ToUpper() + ".");
if (Convert.ToBoolean(Config["General", "RemoveOldEntries"])) { Puts("Old entries will be removed after " + Convert.ToString(Config["Limits", "DaysUntilRemoval"]) + " days."); }
RemoveOldEntries();
}
void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, Sets);
void RemoveOldEntries()
{
if (Convert.ToBoolean(Config["General", "RemoveOldEntries"])) {
DateTime expiryDate = DateTime.Now - TimeSpan.FromDays(Convert.ToInt32(Config["Limits", "DaysUntilRemoval"]));
int count = Sets.Count(x => x.LastUsed <= DateTimeToUnixTimestamp(expiryDate));
if (count != 0) { Sets.RemoveAll(x => x.LastUsed <= DateTimeToUnixTimestamp(expiryDate)); Puts("Removed " + count.ToString() + " old entries from /data/" + Name + ".json"); } else { Puts("No outdated entries to remove."); }
}
}
private bool HasItem(BasePlayer player, string item, int amount)
{
if (amount <= 0) return false;
var definition = ItemManager.FindItemDefinition(item);
if (definition == null) return false;
var pamount = player.inventory.GetAmount(definition.itemid);
if (pamount < amount) return false;
return true;
}
private bool TakeItem(BasePlayer player, string item, int amount)
{
if(!HasItem(player, item, amount)) { return false; }
player.inventory.Take(null, ItemManager.FindItemDefinition(item).itemid, amount);
return true;
}
private bool GiveSetItem(BasePlayer player, int itemid, ulong skinid, int amount, bool wear=false)
{
Item itemToGive;
if (!player.IsConnected) { return false; }
itemToGive = ItemManager.CreateByItemID(itemid, amount, skinid);
if (itemToGive == null) { return false; }
if (Convert.ToBoolean(Config["Misc", "ForceWear"]))
{
if (!itemToGive.MoveToContainer(player.inventory.containerWear)
&& !itemToGive.MoveToContainer(player.inventory.containerMain)
&& !itemToGive.MoveToContainer(player.inventory.containerBelt))
{ itemToGive.Drop(player.eyes.position, player.eyes.BodyForward() * 2f); }
}
else
{
if (!itemToGive.MoveToContainer(player.inventory.containerMain)
&& !itemToGive.MoveToContainer(player.inventory.containerBelt))
{ itemToGive.Drop(player.eyes.position, player.eyes.BodyForward() * 2f); }
}
return true;
}
[ChatCommand("sets")]
private void SetsCommand(BasePlayer player, string command, string[] args) => SetsCommandHandler(player, args);
private void SetsCommandHandler(BasePlayer player, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, PERMISSION_DEFAULT)) { PrintToChat(player, Lang("NoPermission", player.UserIDString)); return; }
if (args.Length == 0 || args.Length > 3) { PrintToChat(player, Lang("NoValue", player.UserIDString)); return; }
string cmd = args[0].ToLower();
switch (cmd)
{
case "clear":
ClearSets(player);
break;
case "add":
AddFromArgs(player, args);
break;
case "remove":
RemoveFromArgs(player, args);
break;
case "public":
PublicStatusFromArgs(player, args);
break;
case "list":
SetList(player);
break;
case "create":
CreateFromArgs(player, args);
break;
case "reskin":
ReskinFromArgs(player, args);
break;
default:
PrintToChat(player, Lang("NoValue", player.UserIDString));
break;
}
}
private void Reskin(BasePlayer player, int id)
{
if (!player.IsConnected) { return; }
SetInfo Set = SetFromID(id);
if(Set == null) { PrintToChat(player, Lang("SetNotExistID", player.UserIDString, id)); return; }
if(player.inventory.containerWear.itemList.Count == 0) { PrintToChat(player, Lang("NoWearables", player.UserIDString)); return; }
int updateCount = 0;
foreach(InternalItemInfo item in Set.Contents)
{
Item itemToUpdate = player.inventory.containerWear.itemList.FirstOrDefault(x => x.info.shortname.ToLower() == item.Shortname.ToLower());
if(itemToUpdate == null) { continue; }
itemToUpdate.skin = item.SkinID;
itemToUpdate.MarkDirty();
updateCount++;
}
if(updateCount != 0) { PrintToChat(player, Lang("ReskinSuccessful", player.UserIDString, updateCount.ToString(), Set.Name, Set.ID.ToString())); Set.TimesUsed++; Set.LastUsed = UnixTimestamp(); }
else { PrintToChat(player, Lang("ReskinFailed", player.UserIDString)); }
}
private void ReskinFromArgs(BasePlayer player, string[] args)
{
if(args.Length == 3)
{
if (args[1].ToLower() == "id")
{
int id = 0;
if (Int32.TryParse(args[2], out id))
{
SetInfo set = SetFromID(id);
if (set == null) { PrintToChat(player, Lang("SetNotExistID", player.UserIDString, id)); return; }
if (set.Public == false && set.OwnerID != player.userID) { PrintToChat(player, Lang("SetNotPublic", player.UserIDString, id.ToString())); return; }
Reskin(player, set.ID);
} else { PrintToChat(player, Lang("ReskinUsage", player.UserIDString)); }
}
}
else if (args.Length == 2)
{
int setID = SetIDFromName(player, args[1]);
if (setID == 0) { PrintToChat(player, Lang("SetNotExist", player.UserIDString, args[1].ToLower())); return; }
Reskin(player, setID);
}
else { PrintToChat(player, Lang("ReskinUsage", player.UserIDString)); }
}
private void CreateFromArgs(BasePlayer player, string[] args)
{
if (args.Length == 3)
{
if (args[1].ToLower() == "id")
{
int id = 0;
if (Int32.TryParse(args[2], out id))
{
SetInfo set = SetFromID(id);
if (set == null) { PrintToChat(player, Lang("SetNotExistID", player.UserIDString, id)); return; }
if (set.Public == false && set.OwnerID != player.userID) { PrintToChat(player, Lang("SetNotPublic", player.UserIDString, id.ToString())); return; }
CreateSet(player, set.ID);
}
else { PrintToChat(player, Lang("SetCreateUsage", player.UserIDString)); }
}
}
else if (args.Length == 2)
{
int setID = SetIDFromName(player, args[1]);
if (setID == 0) { PrintToChat(player, Lang("SetNotExist", player.UserIDString, args[1])); return; }
CreateSet(player, setID);
}
else { PrintToChat(player, Lang("SetCreateUsage", player.UserIDString)); }
}
private void PublicStatusFromArgs(BasePlayer player, string[] args)
{
if (args.Length != 2) { PrintToChat(player, Lang("PublicUsage", player.UserIDString)); return; }
UpdatePublicStatus(player, args[1]);
}
private void RemoveFromArgs(BasePlayer player, string[] args)
{
if (args.Length != 2) { PrintToChat(player, Lang("RemoveUsage", player.UserIDString)); return; }
RemoveSet(player, args[1]);
}
private void AddFromArgs(BasePlayer player, string[] args)
{
if (args.Length != 2) { PrintToChat(player, Lang("SetAddUsage", player.UserIDString)); return; }
AddSet(player, args[1]);
}
private void UpdatePublicStatus(BasePlayer player, string args)
{
if (!player.IsConnected) { return; }
if (string.IsNullOrEmpty(args)) { PrintToChat(player, Lang("PublicUsage", player.UserIDString)); return; }
string setname = args.ToLower();
if (!SetExistsFromName(player, setname)) { PrintToChat(player, Lang("SetNotExist", player.UserIDString, setname)); return; }
SetInfo setToUpdate = SetFromName(player, setname);
if (setToUpdate == null) { return; }
if (setToUpdate.Public) { setToUpdate.Public = false; } else { setToUpdate.Public = true; }
PrintToChat(player, Lang("SetPublicUpdated", player.UserIDString, setname, FormatPublicString(setToUpdate.Public)));
}
private void CreateSet(BasePlayer player, int id)
{
if (!player.IsConnected) { return; }
List<InternalItemInfo> filteredItemList = new List<InternalItemInfo>();
Dictionary<ItemDefinition, float> Costs = new Dictionary<ItemDefinition, float>();
SetInfo setToCreate = SetFromID(id);
if (setToCreate == null) { return; }
int missingBlueprints = 0;
if (Convert.ToBoolean(Config["General", "RequireBP"]))
{
foreach (var item in setToCreate.Contents) // BP Check
{
var def = ItemManager.FindItemDefinition(item.Shortname);
if (player.blueprints.HasUnlocked(def)) { filteredItemList.Add(item); } else { missingBlueprints++; }
}
}
else { filteredItemList = setToCreate.Contents; }
if (filteredItemList.Count == 0) { PrintToChat(player, Lang("NOBPS", player.UserIDString)); return; }
if (Convert.ToBoolean(Config["General", "ChargeForSets"]))
{
foreach (InternalItemInfo item in filteredItemList)
{
List<ItemAmount> ingredientsList = ReturnIngredients(ItemManager.FindItemDefinition(item.Shortname));
foreach (ItemAmount ingredients in ingredientsList)
{
if (!Costs.ContainsKey(ingredients.itemDef)) { Costs.Add(ingredients.itemDef, ingredients.amount); }
else { Costs[ingredients.itemDef] += ingredients.amount; }
}
}
int requiredCostItems = Costs.Count, currentItems = 0;
foreach (var individualCost in Costs) { if (HasItem(player, individualCost.Key.shortname, Convert.ToInt32(individualCost.Value))) { currentItems++; } }
if (currentItems != requiredCostItems) { PrintToChat(player, Lang("NotEnoughResources", player.UserIDString, setToCreate.Name)); return; }
foreach (var individualCost in Costs) { TakeItem(player, individualCost.Key.shortname, Convert.ToInt32(individualCost.Value)); }
}
foreach (InternalItemInfo item in filteredItemList) { GiveSetItem(player, ItemManager.FindItemDefinition(item.Shortname).itemid, item.SkinID, 1); }
PrintToChat(player, Lang("SetCreated", player.UserIDString, setToCreate.Name, setToCreate.ID));
setToCreate.TimesUsed++;
setToCreate.LastUsed = UnixTimestamp();
int itemcount = setToCreate.Contents.Count - filteredItemList.Count;
if (itemcount != 0 && Convert.ToBoolean(Config["General", "RequireBP"])) { PrintToChat(player, Lang("MissingBPS", player.UserIDString, itemcount.ToString())); }
}
private SetInfo SetFromName(BasePlayer player, string setname) => Sets.FirstOrDefault(x => x.OwnerID == player.userID && x.Name == setname);
private SetInfo SetFromID(int id) => Sets.FirstOrDefault(x => x.ID == id);
private bool SetExistsFromName(BasePlayer player, string setname) => Sets.Any(x => x.OwnerID == player.userID && x.Name == setname);
private bool SetExistsFromID(int id) => Sets.Any(x => x.ID == id);
private int SetIDFromName(BasePlayer player, string setname) { var set = Sets.Where(x => x.OwnerID == player.userID && x.Name == setname).FirstOrDefault(); if(set == null) { return 0; } return set.ID; }
private IEnumerable<SetInfo> ReturnSets(BasePlayer player) { if (!HasSets(player) || !player.IsConnected) { return null; } return Sets.Where(x => x.OwnerID == player.userID); }
private string FormatPublicString(bool status) { if (status) { return "<color=#41f45e>true</color>"; } else { return "<color=#f44141>false</color>"; } }
private bool HasSets(BasePlayer player) => Sets.Any(x => x.OwnerID == player.userID);
private int SetsNextID() { List<int> currentIdList = Sets.ConvertAll<int>(set => set.ID); return Enumerable.Range(1, Int32.MaxValue).Except(currentIdList).First(); }
private int SetCount(BasePlayer player) => Sets.Count(x => x.OwnerID == player.userID);
private int RemoveAllSets(BasePlayer player) { if (!player.IsConnected) { return 0; } int removed = SetCount(player); Sets.RemoveAll(x => x.OwnerID == player.userID); return removed; }
private void SetList(BasePlayer player)
{
if (!player.IsConnected) { return; }
if (!HasSets(player)) { PrintToChat(player, Lang("NoSets", player.UserIDString)); return; }
foreach (var set in ReturnSets(player)) {
string contents = string.Empty;
foreach (var item in set.Contents.ToList())
{
var defFromShortname = ItemManager.FindItemDefinition(item.Shortname);
if (defFromShortname == null) { set.Contents.Remove(item); continue; }
if (contents.Length == 0) { contents = defFromShortname.displayName.english; }
else { contents = contents + ", " + defFromShortname.displayName.english; }
}
string publicStatus = FormatPublicString(set.Public);
PrintToChat(player, Lang("ListFormat", player.UserIDString, set.Name, set.ID.ToString(), set.TimesUsed.ToString(), publicStatus, contents));
}
}
private void RemoveSet(BasePlayer player, string arg)
{
if (!player.IsConnected) { return; }
if (string.IsNullOrEmpty(arg)) { PrintToChat(player, Lang("RemoveUsage", player.UserIDString)); return; }
string setname = arg.ToLower();
if (!HasSets(player)) { PrintToChat(player, Lang("NoSets", player.UserIDString)); return; }
var setToRemove = SetFromName(player, setname);
if (setToRemove == null) { PrintToChat(player, Lang("SetNotExist", player.UserIDString, setname)); return; }
Sets.Remove(setToRemove);
PrintToChat(player, Lang("SetRemoved", player.UserIDString, setname));
}
private void AddSet(BasePlayer player, string arg)
{
if (!player.IsConnected) { return; }
if (string.IsNullOrEmpty(arg)) { PrintToChat(player, Lang("SetAddUsage", player.UserIDString)); return; }
if(player.inventory.containerWear.itemList.Count == 0) { PrintToChat(player, Lang("NoItems", player.UserIDString)); return; }
int count = SetCount(player);
if (!permission.UserHasPermission(player.UserIDString, PERMISSION_VIP1 | PERMISSION_VIP2 | PERMISSION_VIP3 | PERMISSION_VIP4 | PERMISSION_VIP5)
&& count >= Convert.ToInt16(Config["Limits", "Limit"])) {
PrintToChat(player, Lang("MaxSets", player.UserIDString, Config["Limits", "Limit"].ToString()));
return;
} else if(permission.UserHasPermission(player.UserIDString, PERMISSION_VIP1)
&& count >= Convert.ToInt16(Config["Limits", "LimitVIP1"])) {
PrintToChat(player, Lang("MaxSets", player.UserIDString, Config["Limits", "LimitVIP1"].ToString()));
return;
} else if(permission.UserHasPermission(player.UserIDString, PERMISSION_VIP2)
&& count >= Convert.ToInt16(Config["Limits", "LimitVIP2"])) {
PrintToChat(player, Lang("MaxSets", player.UserIDString, Config["Limits", "LimitVIP2"].ToString()));
return;
} else if(permission.UserHasPermission(player.UserIDString, PERMISSION_VIP3)
&& count >= Convert.ToInt16(Config["Limits", "LimitVIP3"])) {
PrintToChat(player, Lang("MaxSets", player.UserIDString, Config["Limits", "LimitVIP3"].ToString()));
return;
} else if(permission.UserHasPermission(player.UserIDString, PERMISSION_VIP4)
&& count >= Convert.ToInt16(Config["Limits", "LimitVIP4"])) {
PrintToChat(player, Lang("MaxSets", player.UserIDString, Config["Limits", "LimitVIP4"].ToString()));
return;
} else if(permission.UserHasPermission(player.UserIDString, PERMISSION_VIP5)
&& count >= Convert.ToInt16(Config["Limits", "LimitVIP5"])) {
PrintToChat(player, Lang("MaxSets", player.UserIDString, Config["Limits", "LimitVIP5"].ToString()));
return;
}
string setname = arg.ToLower();
if(SetExistsFromName(player, setname)) { PrintToChat(player, Lang("SetAlreadyExists", player.UserIDString, setname)); return; }
SetInfo newSet = new SetInfo();
newSet.OwnerID = player.userID;
newSet.Name = setname;
newSet.TimesUsed = 0;
newSet.LastUsed = UnixTimestamp();
newSet.ID = SetsNextID();
newSet.Public = false;
int itemsToAdd = 0;
foreach (var wearItem in player.inventory.containerWear.itemList) {
if (ReturnIngredients(wearItem.info) == null) { continue; }
InternalItemInfo setItem = new InternalItemInfo();
setItem.Shortname = wearItem.info.shortname;
setItem.SkinID = wearItem.skin;
newSet.Contents.Add(setItem);
itemsToAdd++;
}
if(itemsToAdd != 0) { Sets.Add(newSet); PrintToChat(player, Lang("SetAdded", player.UserIDString, newSet.Name, newSet.Name)); }
else { PrintToChat(player, Lang("NoItems", player.UserIDString)); }
}
private void ClearSets(BasePlayer player)
{
if (!player.IsConnected) { return; }
if(!HasSets(player)) { PrintToChat(player, Lang("NoSets", player.UserIDString)); return; }
PrintToChat(player, Lang("ClearSuccess", player.UserIDString, RemoveAllSets(player).ToString()));
}
}
}