C#:
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("OGFurnace", "Whispers88", "1.1.0")]
[Description("Brings back the original rust ovens with customizable inventory space")]
class OGFurnace : CovalencePlugin
{
private Configuration config;
public static OGFurnace _OgFurnace;
[PluginReference]
private Plugin FurnaceLevels;
[PluginReference]
private Plugin QuickSmelt;
[PluginReference]
private Plugin PowerSmelt;
#region Config
public class Configuration
{
[JsonProperty("Configure oven capacity", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, int> OvenCapacity = new Dictionary<string, int>();
[JsonProperty("Show oven toggle UI")]
public bool ShowUI = true;
[JsonProperty("UI Button Toggle Min Position")]
public string UIToggleBtnPosMin = "0.65 0.07";
[JsonProperty("UI Button Toggle Max Position")]
public string UIToggleBtnPosMax = "0.73 0.12";
[JsonProperty("UI Button Toggle Font Size")]
public int UIToggleBtnFontSize = 17;
[JsonProperty("Configure cookable items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, ModCookable> _modCookables = new Dictionary<string, ModCookable>();
[JsonProperty("Configure burnable items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, ModBurnable> _modBurnables = new Dictionary<string, ModBurnable>();
[JsonProperty("Custom Capacity (Set custom capacities per furnace via permission-name : capacities)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, Dictionary<string, int>> _customCapacity = new Dictionary<string, Dictionary<string, int>>() { { "customperm1", new Dictionary<string, int>() } };
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);
}
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["OvenToggleCMD"] = "OvenToggleCMD"
}, this);
}
#endregion Config
public class ModCookable
{
public float cooktime;
public int amountToBecome;
public ModCookable()
{
cooktime = 5;
amountToBecome = 1;
}
}
public class ModBurnable
{
public int byproductAmount;
public float byproductChance;
public float fuelAmount;
public ModBurnable()
{
byproductAmount = 1;
byproductChance = 1;
fuelAmount = 20;
}
}
private Dictionary<string, ModCookable> _defaultModCookables = new Dictionary<string, ModCookable>();
private Dictionary<string, ModBurnable> _defaultModBurnables = new Dictionary<string, ModBurnable>();
private static HashSet<int> _burnableItems;
#region Init
private void Init()
{
_OgFurnace = this;
_burnableItems = new HashSet<int>();
foreach (var prefab in GameManifest.Current.entities)
{
BaseOven oven = GameManager.server.FindPrefab(prefab.ToLower())?.GetComponent<BaseOven>();
if (oven == null)
continue;
if (config.OvenCapacity.ContainsKey(oven.ShortPrefabName))
continue;
config.OvenCapacity.Add(oven.ShortPrefabName, oven.inventorySlots);
}
foreach (var perm in config._customCapacity)
{
permission.RegisterPermission($"ogfurnace.{perm.Key}", this);
foreach (var ovenCap in config.OvenCapacity)
{
if (perm.Value.ContainsKey(ovenCap.Key)) continue;
perm.Value.Add(ovenCap.Key, ovenCap.Value);
}
}
foreach (ItemDefinition def in ItemManager.GetItemDefinitions())
{
ItemModBurnable fuel = def?.GetComponent<ItemModBurnable>();
if (fuel != null)
{
_burnableItems.Add(def.itemid);
ModBurnable modBurnable;
if (config._modBurnables.TryGetValue(def.shortname, out modBurnable))
{
fuel.byproductAmount = modBurnable.byproductAmount;
fuel.fuelAmount = modBurnable.fuelAmount;
fuel.byproductChance = modBurnable.byproductChance;
}
else
{
config._modBurnables.Add(def.shortname, new ModBurnable { byproductAmount = fuel.byproductAmount, byproductChance = fuel.byproductChance, fuelAmount = fuel.fuelAmount });
}
if (!_defaultModBurnables.ContainsKey(def.shortname))
_defaultModBurnables.Add(def.shortname, new ModBurnable { byproductAmount = fuel.byproductAmount, byproductChance = fuel.byproductChance, fuelAmount = fuel.fuelAmount });
}
ItemModCookable cookable = def?.GetComponent<ItemModCookable>();
if (cookable != null)
{
ModCookable modCookable;
if (config._modCookables.TryGetValue(def.shortname, out modCookable))
{
cookable.amountOfBecome = modCookable.amountToBecome;
cookable.cookTime = modCookable.cooktime;
}
else
{
config._modCookables.Add(def.shortname, new ModCookable { cooktime = cookable.cookTime, amountToBecome = cookable.amountOfBecome });
}
if (!_defaultModCookables.ContainsKey(def.shortname))
_defaultModCookables.Add(def.shortname, new ModCookable { cooktime = cookable.cookTime, amountToBecome = cookable.amountOfBecome });
}
}
SaveConfig();
AddLocalizedCommand(nameof(OvenToggleCMD));
_UIButtonOn = CreateSplitterUI("On");
_UIButtonOff = CreateSplitterUI("Off");
}
private void Unload()
{
foreach (var player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, "OvenToggleUI");
}
foreach (BaseOvenComponent baseOvenComponent in UnityEngine.Object.FindObjectsOfType<BaseOvenComponent>()) UnityEngine.Object.Destroy(baseOvenComponent);
//Set cooking to default values
foreach (ItemDefinition def in ItemManager.GetItemDefinitions())
{
ItemModBurnable fuel = def?.GetComponent<ItemModBurnable>();
if (fuel != null)
{
ModBurnable modBurnable;
if (_defaultModBurnables.TryGetValue(def.shortname, out modBurnable))
{
fuel.byproductAmount = modBurnable.byproductAmount;
fuel.fuelAmount = modBurnable.fuelAmount;
fuel.byproductChance = modBurnable.byproductChance;
}
}
ItemModCookable cookable = def?.GetComponent<ItemModCookable>();
if (cookable != null)
{
ModCookable modCookable;
if (_defaultModCookables.TryGetValue(def.shortname, out modCookable))
{
cookable.amountOfBecome = modCookable.amountToBecome;
cookable.cookTime = modCookable.cooktime;
}
}
}
}
#endregion Init
#region Hooks
private object CanLootEntity(BasePlayer player, BaseOven oven)
{
BaseOvenComponent baseOvenComponent = null;
if (oven.TryGetComponent<BaseOvenComponent>(out baseOvenComponent))
{
if (baseOvenComponent.looter == player)
return null;
}
StartLooting(player, oven);
return false;
}
object OnOvenToggle(BaseOven oven, BasePlayer player)
{
BaseOvenComponent baseOvenComponent;
if (!oven.TryGetComponent<BaseOvenComponent>(out baseOvenComponent))
{
baseOvenComponent = oven.gameObject.AddComponent<BaseOvenComponent>();
}
if (QuickSmelt != null || FurnaceLevels != null || PowerSmelt != null) return null;
baseOvenComponent.ToggleOven();
return true;
}
void OnLootEntityEnd(BasePlayer player, BaseOven oven)
{
CuiHelper.DestroyUi(player, "OvenToggleUI");
BaseOvenComponent baseOvenComponent;
if (oven.TryGetComponent<BaseOvenComponent>(out baseOvenComponent))
{
baseOvenComponent.looter = null;
}
}
void OnEntitySpawned(BaseOven oven)
{
if (oven.OwnerID == 0) return;
foreach (var perm in config._customCapacity)
{
if (HasPerm(oven.OwnerID.ToString(), $"ogfurnace.{perm.Key}"))
{
int newovencapacity = 0;
if (!perm.Value.TryGetValue(oven.ShortPrefabName, out newovencapacity)) continue;
if (newovencapacity > oven.inventory.capacity)
{
oven.inventory.capacity = newovencapacity;
}
}
}
}
#endregion Hooks
#region UI
CuiElementContainer _UIButtonOn = null;
CuiElementContainer _UIButtonOff = null;
CuiElementContainer CreateSplitterUI(string Toggle)
{
string btncolor = "0.55 0.1 0.1 1";
string txtcolor = "1.0 1.0 1.0 0.3";
if (Toggle == "On")
{
btncolor = "0.3 0.4 0.1 1";
txtcolor = "1.0 1.0 1.0 0.3";
}
CuiElementContainer elements = new CuiElementContainer();
string panel = elements.Add(new CuiPanel
{
Image = { Color = "0.5 0.5 0.5 0.0" },
RectTransform = { AnchorMin = config.UIToggleBtnPosMin, AnchorMax = config.UIToggleBtnPosMax }
}, "Hud.Menu", "OvenToggleUI");
elements.Add(new CuiButton
{
Button = { Command = "OvenToggleCMD", Color = btncolor },
Text = { Text = "Turn " + Toggle, FontSize = config.UIToggleBtnFontSize, Align = TextAnchor.MiddleCenter, Color = txtcolor },
RectTransform = { AnchorMin = "0.00 0.00", AnchorMax = "1.0 1.0" }
}, panel);
return elements;
}
#endregion UI
#region COMMANDS
private void OvenToggleCMD(IPlayer iplayer, string command, string[] args)
{
BasePlayer player = (BasePlayer)iplayer.Object;
BaseOven oven = (player.inventory?.loot?.entitySource ?? null) as BaseOven;
if (oven == null) return;
BaseOvenComponent baseOvenComponent;
if (!oven.TryGetComponent<BaseOvenComponent>(out baseOvenComponent))
{
baseOvenComponent = oven.gameObject.AddComponent<BaseOvenComponent>();
}
if (QuickSmelt != null || FurnaceLevels != null || PowerSmelt != null)
{
if (oven.HasFlag(BaseEntity.Flags.On))
{
oven.StopCooking();
}
else
{
oven.StartCooking();
}
return;
}
baseOvenComponent.ToggleOven();
}
#endregion COMMANDS
#region Methods
private void StartLooting(BasePlayer player, BaseOven oven)
{
string panel = "generic_resizable";
if (config.OvenCapacity.TryGetValue(oven.ShortPrefabName, out int ovenCapacity))
{
oven.inventory.capacity = ovenCapacity;
if (ovenCapacity == 1)
panel = oven.panelName;
}
BaseOvenComponent baseOvenComponent;
if (!oven.TryGetComponent<BaseOvenComponent>(out baseOvenComponent))
{
baseOvenComponent = oven.gameObject.AddComponent<BaseOvenComponent>();
baseOvenComponent.oginputSlotIndex = oven._inputSlotIndex;
baseOvenComponent.oginputSlots = oven.inputSlots;
baseOvenComponent.ogoutputSlots = oven.outputSlots;
baseOvenComponent.ogoutputSlotIndex = oven._outputSlotIndex;
baseOvenComponent.ogfuelslots = oven.fuelSlots;
}
baseOvenComponent.looter = player;
oven.inputSlots = oven.inventory.capacity;
oven.outputSlots = oven.inventory.capacity;
oven.fuelSlots = oven.inventory.capacity;
oven._inputSlotIndex = 0;
oven._outputSlotIndex = 0;
oven.SendAsSnapshot(player.Connection);
player.inventory.loot.entitySource = oven;
player.inventory.loot.AddContainer(oven.inventory);
player.inventory.loot.MarkDirty();
player.inventory.loot.SendImmediate();
player.ClientRPC<string>(RpcTarget.Player("RPC_OpenLootPanel", player), panel);
baseOvenComponent.UpdateLooterUI();
Interface.CallHook("OnLootEntity", player, oven);
}
#endregion Methods
#region BaseOven Behaviour
public class BaseOvenComponent : FacepunchBehaviour
{
private BaseOven baseOven;
private ItemContainer inventory;
public BasePlayer looter = null;
public int oginputSlots;
public int ogoutputSlots;
public int ogfuelslots;
public int ogoutputSlotIndex;
public int oginputSlotIndex;
private void Awake()
{
baseOven = GetComponent<BaseOven>();
inventory = baseOven.inventory;
inventory.onItemAddedRemoved = new Action<Item, bool>(OnItemAddedOrRemoved);
inventory.canAcceptItem = new Func<Item, int, bool>(CanAcceptItem);
}
private bool CanAcceptItem(Item item, int targetPos)
{
if (inventory.availableSlots != null && inventory.availableSlots.Count > 0)
{
if ((int)item.info.occupySlots == 0 || item.info.occupySlots == ItemSlot.None)
{
return false;
}
int[] numArray = new int[32];
foreach (ItemSlot availableSlot in inventory.availableSlots)
{
numArray[(int)Mathf.Log((float)availableSlot, 2f)]++;
}
foreach (Item item1 in inventory.itemList)
{
for (int i = 0; i < 32; i++)
{
if (((int)item1.info.occupySlots & 1 << (i & 31)) != 0)
{
numArray[i]--;
}
}
}
for (int j = 0; j < 32; j++)
{
if (((int)item.info.occupySlots & 1 << (j & 31)) != 0 && numArray[j] <= 0)
{
return false;
}
}
}
object obj = Interface.CallHook("CanAcceptItem", this, item, targetPos);
if (!(obj is ItemContainer.CanAcceptResult))
{
return true;
}
if (obj != null)
{
return false;
}
return true;
}
#region Commands
public void StartCooking()
{
if (FindBurnable() == null)
{
return;
}
inventory.temperature = baseOven.cookingTemperature;
UpdateAttachmentTemperature();
InvokeRepeating(StartCook, 0.5f, 0.5f);
baseOven.SetFlag(BaseEntity.Flags.On, true, false, true);
UpdateLooterUI();
}
public void StopCooking()
{
UpdateAttachmentTemperature();
if (baseOven.inventory != null)
{
baseOven.inventory.temperature = 15f;
foreach (Item item in inventory.itemList)
{
item.SetFlag(Item.Flag.Cooking, false);
item.SetFlag(Item.Flag.OnFire, false);
item.MarkDirty();
}
}
CancelInvoke(StartCook);
baseOven.SetFlag(BaseEntity.Flags.On, false, false, true);
UpdateLooterUI();
}
#endregion Commands
#region Cooking Methods
private void StartCook()
{
Item item = FindBurnable();
if (item == null)
{
StopCooking();
return;
}
BaseEntity slot = baseOven.GetSlot(BaseEntity.Slot.FireMod);
if (slot)
{
slot.SendMessage("Cook", 0.5f, SendMessageOptions.DontRequireReceiver);
}
ItemModBurnable component = item.info.GetComponent<ItemModBurnable>();
Item item1 = item;
item1.fuel = item1.fuel - 0.5f * (baseOven.cookingTemperature / 200f);
if (!item.HasFlag(Item.Flag.OnFire))
{
item.SetFlag(Item.Flag.OnFire, true);
item.MarkDirty();
}
for (int i = 0; i < inventory.capacity; i++)
{
var cookable = inventory.GetSlot(i);
if (cookable == null) continue;
if (cookable.info == baseOven.fuelType) continue;
ItemModCookable itemModCookable = cookable.info.GetComponent<ItemModCookable>();
if (itemModCookable == null) continue;
if (inventory.temperature <= (float)itemModCookable.lowTemp)
continue;
if (inventory.temperature > (float)itemModCookable.highTemp)
continue;
if (!cookable.HasFlag(Item.Flag.Cooking))
{
cookable.SetFlag(Item.Flag.Cooking, true);
cookable.MarkDirty();
StartCoroutine(CookItem(cookable, itemModCookable));
}
}
if (item.fuel <= 0f)
{
ConsumeFuel(item, component);
}
}
private void UpdateAttachmentTemperature()
{
BaseEntity slot = baseOven.GetSlot(BaseEntity.Slot.FireMod);
if (slot)
{
slot.SendMessage("ParentTemperatureUpdate", inventory.temperature, SendMessageOptions.DontRequireReceiver);
}
}
private void ConsumeFuel(Item fuel, ItemModBurnable burnable)
{
if (baseOven.allowByproductCreation && burnable.byproductItem != null && UnityEngine.Random.Range(0f, 1f) > burnable.byproductChance)
{
Item item = ItemManager.Create(burnable.byproductItem, burnable.byproductAmount, (ulong)0);
if (!item.MoveToContainer(inventory, -1, true))
{
//oven full
StopCooking();
item.Drop(inventory.dropPosition, inventory.dropVelocity, new Quaternion());
}
}
if (fuel.amount <= 1)
{
fuel.Remove(0f);
return;
}
fuel.amount--;
fuel.fuel = burnable.fuelAmount;
fuel.MarkDirty();
}
#endregion Cooking Methods
#region Helpers
private void OnItemAddedOrRemoved(Item item, bool bAdded)
{
if (item != null)
{
item.SetFlag(Item.Flag.Cooking, false);
item.SetFlag(Item.Flag.OnFire, false);
item.MarkDirty();
}
}
private IEnumerator CookItem(Item item, ItemModCookable itemMod)
{
yield return CoroutineEx.waitForSeconds(itemMod.cookTime);
if (!item.HasFlag(Item.Flag.Cooking)) yield break;
if (item.amount <= 1)
{
item.Remove(0f);
}
else
{
item.amount--;
item.MarkDirty();
}
int num = item.position;
if (itemMod.becomeOnCooked == null || item.amount < 1) yield break;
Item item1 = ItemManager.Create(itemMod.becomeOnCooked, itemMod.amountOfBecome, (ulong)0);
if (item1 != null && !item1.MoveToContainer(item.parent, num, true) &&
!item1.MoveToContainer(item.parent, -1, true))
{
item1.Drop(item.parent.dropPosition, item.parent.dropVelocity, new Quaternion());
if (item.parent.entityOwner)
{
BaseOven component = item.parent.entityOwner.GetComponent<BaseOven>();
if (component != null)
{
StopCooking();
}
}
}
item.SetFlag(Item.Flag.Cooking, false);
}
private Item FindBurnable()
{
if (inventory == null)
{
return null;
}
foreach (Item current in inventory.itemList)
{
if ( !_burnableItems.Contains(current.info.itemid) || (baseOven.fuelType != null && current.info != baseOven.fuelType))
{
continue;
}
return current;
}
return null;
}
public void ToggleOven()
{
if (baseOven.HasFlag(BaseEntity.Flags.On))
{
StopCooking();
}
else
{
StartCooking();
}
}
public void UpdateLooterUI()
{
if (!_OgFurnace.config.ShowUI || looter == null) return;
CuiHelper.DestroyUi(looter, "OvenToggleUI");
if (baseOven.HasFlag(BaseEntity.Flags.On))
{
CuiHelper.AddUi(looter, _OgFurnace._UIButtonOff);
}
else
{
CuiHelper.AddUi(looter, _OgFurnace._UIButtonOn);
}
}
#endregion Helpers
void OnDestroy()
{
CancelInvoke(StartCook);
baseOven.inputSlots = oginputSlots;
baseOven.fuelSlots = ogfuelslots;
baseOven.outputSlots = ogoutputSlots;
baseOven._inputSlotIndex = oginputSlotIndex;
baseOven._outputSlotIndex = ogoutputSlotIndex;
}
}
#endregion BaseOven Behaviour
#region Helpers
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
}
}