C#:
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("OGSplitter", "Whispers88", "1.1.3")]
[Description("Split cookable items")]
class OGSplitter : CovalencePlugin
{
private Configuration config;
public static OGSplitter _ogSplitter;
[PluginReference]
private Plugin OGFurnace;
public class Configuration
{
[JsonProperty("UI Button Splitter Button Min Position")]
public string UISplitterBtnPosMin = "0.65 0.01";
[JsonProperty("UI Button Splitter Button Max Position")]
public string UISplitterBtnPosMax = "0.73 0.06";
[JsonProperty("UI Button Splitter Button Font Size")]
public int UISplitterBtnFontSize = 16;
[JsonProperty("UI Button Splitter Button Text")]
public string UISplitterBtnText = "Run Splitter";
[JsonProperty("UI Button Splitter Button Color")]
public string UISplitterBtnClr = "0.5 0.5 0.5 0.3";
[JsonProperty("UI Button Splitter Text Color")]
public string UISplitterBtnTextClr = "0.8 0.8 0.8 0.75";
public string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
}
protected override void LoadDefaultConfig() => config = new Configuration();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<Configuration>();
if (config == null)
{
throw new JsonException();
}
if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys))
{
LogWarning("Configuration appears to be outdated; updating and saving");
SaveConfig();
}
}
catch
{
LogWarning($"Configuration file {Name}.json is invalid; using defaults");
LoadDefaultConfig();
}
}
protected override void SaveConfig()
{
LogWarning($"Configuration changes saved to {Name}.json");
Config.WriteObject(config, true);
}
#region INIT
public string permuse = "ogsplitter.use";
CuiElementContainer cachedSplitterUI = null;
private void Init()
{
_ogSplitter = this;
permission.RegisterPermission(permuse, this);
AddLocalizedCommand(nameof(SplitterCommand));
// Create a cached version of the UI
cachedSplitterUI = CreateSplitterUI();
}
private void Unload()
{
foreach (var player in BasePlayer.activePlayerList)
CuiHelper.DestroyUi(player, "SplitterUI");
}
#endregion INIT
#region Localization
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["SplitterCommand"] = "OvenSplitter"
}, this);
}
#endregion Localization
#region HOOKS
private void OnLootEntity(BasePlayer player, BaseOven oven)
{
if (!HasPerm(player.UserIDString, permuse)) return;
CuiHelper.AddUi(player, cachedSplitterUI);
}
void OnLootEntityEnd(BasePlayer player, BaseOven oven) => CuiHelper.DestroyUi(player, "SplitterUI");
#endregion HOOKS
#region SPLITTER METHOD
public class CookableItem
{
public int amount;
public ItemModCookable cookableMod;
}
void OvenSplitter(BaseOven oven, BasePlayer player)
{
Dictionary<ItemDefinition, CookableItem> cookableItems = new Dictionary<ItemDefinition, CookableItem>();
int ovenFuel = 0;
ItemContainer ovencon = oven.inventory;
List<Item> returnitems = new List<Item>();
//Check current oven inventory
foreach (var item in oven.inventory.itemList)
{
if (item.info == oven.fuelType)
{
ovenFuel += item.amount;
continue;
}
ItemModCookable itemModCookable;
if (!item.info.shortname.Contains("cooked") && item.info.TryGetComponent<ItemModCookable>(out itemModCookable))
{
CookableItem cookableItem;
if (cookableItems.TryGetValue(item.info, out cookableItem))
{
cookableItem.amount += item.amount;
}
else
{
cookableItems.Add(item.info, new CookableItem() { amount = item.amount, cookableMod = itemModCookable });
}
continue;
}
//Return non-cookables
returnitems.Add(item);
}
foreach (var item in returnitems) player.GiveItem(item);
int cookableTypesCount = cookableItems.Count;
//Return items if no cookables present
if (cookableTypesCount == 0)
{
foreach (var item in ovencon.itemList)
{
player.GiveItem(item);
}
return;
}
//Calculate cookable slots per cookable
int cookableSlots;
int remainder;
if (OGFurnace == null)
{
cookableSlots = (int)Math.Ceiling((decimal)(oven.inventory.capacity - oven.fuelSlots - oven.outputSlots) / cookableTypesCount);
remainder = cookableSlots * cookableTypesCount - (oven.inventory.capacity - oven.fuelSlots - oven.outputSlots);
}
else
{
cookableSlots = (int)Math.Ceiling((decimal)(oven.inventory.capacity - cookableTypesCount - 2) / cookableTypesCount);
remainder = (cookableSlots * cookableTypesCount) - (oven.inventory.capacity - cookableTypesCount - 2);
}
//If no slots left to cook
if (cookableSlots < 1)
{
foreach (var item in ovencon.itemList)
{
player.GiveItem(item);
}
return;
}
float smelttime = 0f;
int l = 0;
//Remove items in oven
oven.inventory.Clear();
ItemManager.DoRemoves();
//Split Cookables
foreach (var cookable in cookableItems)
{
l++;
int slotamt = (int)Math.Ceiling((decimal)(cookable.Value.amount / cookableSlots));
//If more ore than room in oven
if (slotamt > cookable.Key.stackable)
{
GivePlayerItem(player, cookable.Key, cookable.Value.amount - (cookableSlots * cookable.Key.stackable));
cookable.Value.amount = cookableSlots * cookable.Key.stackable;
slotamt = cookable.Key.stackable;
}
for (var i = 0; i < cookableSlots; i++)
{
bool j = (slotamt * cookableSlots + i < cookable.Value.amount);
Item splitItem = ItemManager.Create(cookable.Key, slotamt + (j ? 1 : 0), 0uL);
if (OGFurnace != null)
splitItem.MoveToContainer(ovencon, i + (l - 1) * (cookableSlots + (l > remainder && remainder != 0 ? 1 : 0)), false);
else
splitItem.MoveToContainer(ovencon, i + oven.fuelSlots + (l - 1) * (cookableSlots + (l > remainder && remainder != 0 ? 1 : 0)), false);
}
//If cookable slots is not evenly distributed
if (cookableSlots != 1 && l == remainder && remainder != 0) cookableSlots -= 1;
//Get highest cooktime
bool k = ((slotamt * cookableSlots) < cookable.Value.amount);
float cooktime = cookable.Value.cookableMod.cookTime * (slotamt + (k ? 1 : 0));
if (cooktime > smelttime) smelttime = cooktime;
}
float fuelUnits = oven.fuelType.GetComponent<ItemModBurnable>().fuelAmount;
int neededFuel = (int)Math.Ceiling(smelttime * (oven.cookingTemperature / 200f) / fuelUnits);
//Check fuel required is not greater than max stack
if (OGFurnace != null)
{
if (neededFuel > oven.fuelType.stackable) neededFuel = oven.fuelType.stackable;
}
else if (neededFuel > oven.fuelType.stackable * oven.fuelSlots) neededFuel = oven.fuelType.stackable * oven.fuelSlots;
if (neededFuel < 1) //This shouldn't happen
{
if (ovenFuel > 0)
GivePlayerItem(player, oven.fuelType, ovenFuel);
return;
}
//If enough fuel is in the furnace
if (ovenFuel >= neededFuel)
{
if (OGFurnace == null)
{
int fueltogive = neededFuel;
for (int i = 0; i < oven.fuelSlots; i++)
{
Item fuelItem = (i == oven.fuelSlots) ? ItemManager.Create(oven.fuelType, fueltogive, 0uL) : ItemManager.Create(oven.fuelType, (int)Math.Floor((decimal)neededFuel / oven.fuelSlots), 0uL);
fueltogive -= fuelItem.amount;
fuelItem.MoveToContainer(ovencon, i, false);
}
}
else
{
oven.inventory.AddItem(oven.fuelType, neededFuel, 0UL);
}
if (ovenFuel - neededFuel > 0)
GivePlayerItem(player, oven.fuelType, ovenFuel - neededFuel);
}
//else check players inventory for more fuel
else
{
int playerFuelAmt = 0;
List<Item> playerFuel = Pool.Get<List<Item>>();
player.inventory.FindItemsByItemID(playerFuel, oven.fuelType.itemid);
//count total fuel in the players inventory
foreach (Item i in playerFuel)
{
playerFuelAmt += i.amount;
i.Remove();
}
ItemManager.DoRemoves();
//if inventory fuel + oven fuel is less than required
if (neededFuel >= playerFuelAmt + ovenFuel)
{
neededFuel = playerFuelAmt + ovenFuel;
}
//if inventory fuel + oven fuel is more than required
if (OGFurnace == null)
{
int fueltogive = neededFuel;
for (int i = 0; i < oven.fuelSlots; i++)
{
Item fuelItem = (i == oven.fuelSlots) ? ItemManager.Create(oven.fuelType, fueltogive, 0uL) : ItemManager.Create(oven.fuelType, (int)Math.Floor((decimal)neededFuel / oven.fuelSlots), 0uL);
fueltogive -= fuelItem.amount;
fuelItem.MoveToContainer(ovencon, i, false);
}
}
else
oven.inventory.AddItem(oven.fuelType, neededFuel, 0UL);
if (ovenFuel + playerFuelAmt - neededFuel > 0)
GivePlayerItem(player, oven.fuelType, ovenFuel + playerFuelAmt - neededFuel);
Pool.FreeUnmanaged<Item>(ref playerFuel);
}
}
#endregion SPLITTER METHOD
#region COMMANDS
private void SplitterCommand(IPlayer iplayer, string command, string[] args)
{
BasePlayer player = (BasePlayer)iplayer.Object;
BaseOven oven = (player.inventory?.loot?.entitySource as BaseOven) ?? null;
if (oven == null || !HasPerm(player.UserIDString, permuse)) return;
OvenSplitter(oven, player);
}
#endregion COMMANDS
#region GUI Panel
public CuiElementContainer CreateSplitterUI()
{
CuiElementContainer elements = new CuiElementContainer();
string panel = elements.Add(new CuiPanel
{
Image = { Color = config.UISplitterBtnClr },
RectTransform = { AnchorMin = config.UISplitterBtnPosMin, AnchorMax = config.UISplitterBtnPosMax }
}, "Hud.Menu", "SplitterUI");
elements.Add(new CuiButton
{
Button = { Command = "OvenSplitter", Color = "0.5 0.5 0.5 0.0" },
Text = { Text = config.UISplitterBtnText, FontSize = config.UISplitterBtnFontSize, Align = TextAnchor.MiddleCenter, Color = config.UISplitterBtnTextClr },
RectTransform = { AnchorMin = "0.00 0.00", AnchorMax = "1.0 1.0" }
}, panel);
return elements;
}
#endregion GUI Panel
#region HELPERS
private void GivePlayerItem(BasePlayer player, ItemDefinition itemDef, int amount)
{
int freeslots = FreeSlots(player);
List<int> stacks = GetStacks(itemDef, amount);
foreach (var stack in stacks)
{
if (freeslots < 1)
{
DropItem(player, itemDef, stack);
continue;
}
freeslots -= 1;
player.GiveItem(ItemManager.Create(itemDef, stack, 0uL));
}
}
private void DropItem(BasePlayer player, ItemDefinition item, int amount)
{
Item itemgive = ItemManager.Create(ItemManager.FindItemDefinition(item.itemid), amount, 0uL);
Vector3 dropPosition = player.GetDropPosition();
Vector3 dropVelocity = player.GetDropVelocity();
itemgive.Drop(dropPosition, dropVelocity, Quaternion.identity);
}
private int FreeSlots(BasePlayer player)
{
var slots = player.inventory.containerMain.capacity + player.inventory.containerBelt.capacity;
var taken = player.inventory.containerMain.itemList.Count + player.inventory.containerBelt.itemList.Count;
return slots - taken;
}
private List<int> GetStacks(ItemDefinition item, int amount)
{
var list = new List<int>();
var maxStack = item.stackable;
while (amount > maxStack)
{
amount -= maxStack;
list.Add(maxStack);
}
list.Add(amount);
return list;
}
private bool HasPerm(string id, string perm) => permission.UserHasPermission(id, perm);
private void AddLocalizedCommand(string command)
{
foreach (string language in lang.GetLanguages(this))
{
Dictionary<string, string> messages = lang.GetMessages(language, this);
foreach (KeyValuePair<string, string> message in messages)
{
if (message.Key.Equals(command))
{
if (!string.IsNullOrEmpty(message.Value))
{
AddCovalenceCommand(message.Value, command);
}
}
}
}
}
#endregion HELPERS
}
}