using System; using System.Collections.Generic; using Newtonsoft.Json; using UnityEngine; using Random = UnityEngine.Random; namespace Oxide.Plugins { [Info("Quick Smelt", "Iv Misticos", "5.1.3")] [Description("Increases the speed of the furnace smelting")] class QuickSmelt : RustPlugin { #region Variables private static QuickSmelt _instance; private const string PermissionUse = "quicksmelt.use"; #endregion #region Configuration private static Configuration _config; private class Configuration { [JsonProperty(PropertyName = "Use Permission")] public bool UsePermission = true; [JsonProperty(PropertyName = "Speed Multipliers", ObjectCreationHandling = ObjectCreationHandling.Replace)] public Dictionary SpeedMultipliers = new Dictionary { {"global", 1.0f}, {"furnace.shortname", 1.0f} }; [JsonProperty(PropertyName = "Fuel Usage Speed Multipliers", ObjectCreationHandling = ObjectCreationHandling.Replace)] public Dictionary FuelSpeedMultipliers = new Dictionary { {"global", 1.0f}, {"furnace.shortname", 1.0f} }; [JsonProperty(PropertyName = "Fuel Usage Multipliers", ObjectCreationHandling = ObjectCreationHandling.Replace)] public Dictionary FuelUsageMultipliers = new Dictionary { {"global", 1}, {"furnace.shortname", 1} }; [JsonProperty(PropertyName = "Output Multipliers", ObjectCreationHandling = ObjectCreationHandling.Replace)] public Dictionary> OutputMultipliers = new Dictionary> { { "global", new Dictionary { {"global", 1.0f} } }, { "furnace.shortname", new Dictionary { {"item.shortname", 1.0f} } } }; [JsonProperty(PropertyName = "Whitelist", ObjectCreationHandling = ObjectCreationHandling.Replace)] public Dictionary> Whitelist = new Dictionary> { { "global", new List { "item.shortname" } }, { "furnace.shortname", new List { "item.shortname" } } }; [JsonProperty(PropertyName = "Blacklist", ObjectCreationHandling = ObjectCreationHandling.Replace)] public Dictionary> Blacklist = new Dictionary> { { "global", new List { "item.shortname" } }, { "furnace.shortname", new List { "item.shortname" } } }; [JsonProperty(PropertyName = "Smelting Frequencies (Smelt items every N smelting ticks)", ObjectCreationHandling = ObjectCreationHandling.Replace)] public Dictionary SmeltingFrequencies = new Dictionary { {"global", 1}, {"furnace.shortname", 1} }; [JsonProperty(PropertyName = "Debug")] public bool Debug = false; } protected override void LoadConfig() { base.LoadConfig(); try { _config = Config.ReadObject(); if (_config == null) throw new Exception(); } catch { PrintError("Your configuration file contains an error. Using default configuration values."); LoadDefaultConfig(); } } protected override void LoadDefaultConfig() => _config = new Configuration(); protected override void SaveConfig() => Config.WriteObject(_config); #endregion #region Hooks private void Unload() { var ovens = UnityEngine.Object.FindObjectsOfType(); PrintDebug($"Processing BaseOven(s).. Amount: {ovens.Length}."); for (var i = 0; i < ovens.Length; i++) { var oven = ovens[i]; var component = oven.GetComponent(); if (oven.IsOn()) { PrintDebug("Oven is on. Restarted cooking"); component.StopCooking(); oven.StartCooking(); } UnityEngine.Object.Destroy(component); } PrintDebug("Done."); } private void OnServerInitialized() { _instance = this; permission.RegisterPermission(PermissionUse, this); var ovens = UnityEngine.Object.FindObjectsOfType(); PrintDebug($"Processing BaseOven(s).. Amount: {ovens.Length}."); for (var i = 0; i < ovens.Length; i++) { var oven = ovens[i]; OnEntitySpawned(oven); } timer.Once(1f, () => { for (var i = 0; i < ovens.Length; i++) { var oven = ovens[i]; var component = oven.gameObject.GetComponent(); if (oven == null || oven.IsDestroyed || !oven.IsOn() || !CanUse(oven.OwnerID)) continue; component.StartCooking(); } }); } private void OnEntitySpawned(BaseNetworkable entity) { var oven = entity as BaseOven; if (oven == null) return; oven.gameObject.AddComponent(); } private object OnOvenToggle(StorageContainer oven, BasePlayer player) { if (oven is BaseFuelLightSource || oven.needsBuildingPrivilegeToUse && !player.CanBuild()) return null; PrintDebug("OnOvenToggle called"); var component = oven.gameObject.GetComponent(); var canUse = CanUse(oven.OwnerID) || CanUse(player.userID); if (oven.IsOn()) { component.StopCooking(); } else { if (canUse) component.StartCooking(); else { PrintDebug($"No permission ({player.userID})"); return null; } } return false; } #endregion #region Helpers private bool CanUse(ulong id) => !_config.UsePermission || permission.UserHasPermission(id.ToString(), PermissionUse); private static void PrintDebug(string message) { if (_config.Debug) Debug.Log($"DEBUG ({_instance.Name}) > " + message); } #endregion #region Controller public class FurnaceController : FacepunchBehaviour { private int _ticks; private BaseOven _oven; private BaseOven Furnace { get { if (_oven == null) _oven = GetComponent(); return _oven; } } private float _speedMultiplier; private float _fuelSpeedMultiplier; private int _fuelUsageMultiplier; private int _smeltingFrequency; private Dictionary _outputModifiers; private float OutputMultiplier(string shortname) { float modifier; if (_outputModifiers == null || !_outputModifiers.TryGetValue(shortname, out modifier) && !_outputModifiers.TryGetValue("global", out modifier)) modifier = 1.0f; PrintDebug($"{shortname} modifier: {modifier}"); return modifier; } private List _blacklist; private List _whitelist; private bool? IsAllowed(string shortname) { if (_blacklist != null && _blacklist.Contains(shortname)) return false; if (_whitelist != null && _whitelist.Contains(shortname)) return true; return null; } private void Awake() { // Well, sorry for my complicated code. But that should work faster! :) float modifierF; // float modifier int modifierI; // int modifier if (!_config.SpeedMultipliers.TryGetValue(Furnace.ShortPrefabName, out modifierF) && !_config.SpeedMultipliers.TryGetValue("global", out modifierF)) modifierF = 1.0f; _speedMultiplier = 0.5f / modifierF; if (!_config.FuelSpeedMultipliers.TryGetValue(Furnace.ShortPrefabName, out modifierF) && !_config.FuelSpeedMultipliers.TryGetValue("global", out modifierF)) modifierF = 1.0f; _fuelSpeedMultiplier = modifierF; if (!_config.FuelUsageMultipliers.TryGetValue(Furnace.ShortPrefabName, out modifierI) && !_config.FuelUsageMultipliers.TryGetValue("global", out modifierI)) modifierI = 1; _fuelUsageMultiplier = modifierI; if (!_config.SmeltingFrequencies.TryGetValue(Furnace.ShortPrefabName, out modifierI) && !_config.SmeltingFrequencies.TryGetValue("global", out modifierI)) modifierI = 1; _smeltingFrequency = modifierI; if (!_config.OutputMultipliers.TryGetValue(Furnace.ShortPrefabName, out _outputModifiers) && !_config.OutputMultipliers.TryGetValue("global", out _outputModifiers)) { // ignored } if ((!_config.Blacklist.TryGetValue(Furnace.ShortPrefabName, out _blacklist) && !_config.Blacklist.TryGetValue("global", out _blacklist)) & (!_config.Whitelist.TryGetValue(Furnace.ShortPrefabName, out _whitelist) && !_config.Whitelist.TryGetValue("global", out _whitelist))) { // ignored } } private Item FindBurnable() { if (Furnace.inventory == null) return null; foreach (var item in Furnace.inventory.itemList) { var component = item.info.GetComponent(); if (component && (Furnace.fuelType == null || item.info == Furnace.fuelType)) { return item; } } return null; } public void Cook() { var item = FindBurnable(); if (item == null) { StopCooking(); return; } SmeltItems(); var slot = Furnace.GetSlot(BaseEntity.Slot.FireMod); if (slot) { slot.SendMessage("Cook", 0.5f, SendMessageOptions.DontRequireReceiver); } var component = item.info.GetComponent(); item.fuel -= 0.5f * (Furnace.cookingTemperature / 200f) * _fuelSpeedMultiplier; if (!item.HasFlag(global::Item.Flag.OnFire)) { item.SetFlag(global::Item.Flag.OnFire, true); item.MarkDirty(); } if (item.fuel <= 0f) { ConsumeFuel(item, component); } _ticks++; } private void ConsumeFuel(Item fuel, ItemModBurnable burnable) { if (Furnace.allowByproductCreation && burnable.byproductItem != null && Random.Range(0f, 1f) > burnable.byproductChance) { var def = burnable.byproductItem; var item = ItemManager.Create(def, (int) (burnable.byproductAmount * OutputMultiplier(def.shortname))); // It's fuel multiplier if (!item.MoveToContainer(Furnace.inventory)) { StopCooking(); item.Drop(Furnace.inventory.dropPosition, Furnace.inventory.dropVelocity); } } if (fuel.amount <= 1) { fuel.Remove(); return; } fuel.amount -= _fuelUsageMultiplier; fuel.fuel = burnable.fuelAmount; fuel.MarkDirty(); } private void SmeltItems() { if (_ticks % _smeltingFrequency != 0) return; for (var i = 0; i < Furnace.inventory.itemList.Count; i++) { // Getting item and checking if it's valid var item = Furnace.inventory.itemList[i]; if (item == null || !item.IsValid()) continue; // Getting cookable var cookable = item.info.GetComponent(); if (cookable == null) continue; // Checking if item's cooking is allowed var isAllowed = IsAllowed(item.info.shortname); if (isAllowed != null && !isAllowed.Value) // Allowed is false? Okay, no problem. Don't cook this item continue; // What about temperature? // This lets us deny cooking, for example, meat in furnaces var temperature = item.temperature; if ((temperature < cookable.lowTemp || temperature > cookable.highTemp) && isAllowed == null) // Not allowed, not denied? That's our case! Because if it's allowed, this function won't be executed :P { if (!cookable.setCookingFlag || !item.HasFlag(global::Item.Flag.Cooking)) continue; item.SetFlag(global::Item.Flag.Cooking, false); item.MarkDirty(); continue; } // So, so.. what about items' cooking speed (time)? if (cookable.cookTime > 0 && _ticks * 1f / _smeltingFrequency % cookable.cookTime > 0) continue; // Setting cooking flag if (cookable.setCookingFlag && !item.HasFlag(global::Item.Flag.Cooking)) { item.SetFlag(global::Item.Flag.Cooking, true); item.MarkDirty(); } // Changing amount var position = item.position; if (item.amount > 1) { item.amount--; item.MarkDirty(); } else { item.Remove(); } // What if nothing is produced? if (cookable.becomeOnCooked == null) continue; // Let's create an item! var item2 = ItemManager.Create(cookable.becomeOnCooked, (int) (cookable.amountOfBecome * OutputMultiplier(cookable.becomeOnCooked.shortname))); // It's an another one output multiplier, but not for fuel // Some checks if (item2 == null || item2.MoveToContainer(item.parent, position) || item2.MoveToContainer(item.parent)) continue; // Dropping item and stopping cooking if oven is full item2.Drop(item.parent.dropPosition, item.parent.dropVelocity); if (!item.parent.entityOwner) continue; StopCooking(); } } public void StartCooking() { if (FindBurnable() == null) { PrintDebug("No burnable."); return; } StopCooking(); PrintDebug("Starting cooking.."); Furnace.inventory.temperature = Furnace.cookingTemperature; Furnace.UpdateAttachmentTemperature(); PrintDebug($"Speed Multiplier: {_speedMultiplier}"); Furnace.InvokeRepeating(Cook, _speedMultiplier, _speedMultiplier); Furnace.SetFlag(BaseEntity.Flags.On, true); } public void StopCooking() { PrintDebug("Stopping cooking.."); Furnace.CancelInvoke(Cook); Furnace.StopCooking(); } } #endregion } }