C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Network;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using UnityEngine;
using Formatter = Oxide.Core.Libraries.Covalence.Formatter;
namespace Oxide.Plugins {
[Info("RatesController", "Vlad-00003", "3.0.4")]
[Description("All-in-one customizable rate system for the server")]
/*
* Author info:
* E-mail: Vlad-00003@mail.ru
* Vk: vk.com/vlad_00003
*/
class RatesController : RustPlugin {
#region Vars
private static PluginConfig _config;
private static RatesController _ratesController;
private PluginData _data;
private bool _isDay = true;
private Dictionary<ItemModCookable, float> _smeltRatesBackup = new Dictionary<ItemModCookable, float>();
private Dictionary<uint, string> _quarryOwners = new Dictionary<uint, string>();
private Dictionary<Item, CustomCookable> _customCookables = new Dictionary<Item, CustomCookable>();
private CoalConfig _defaultCoal;
private static class Constants {
public const string ExcavatorArmPrefab = "assets/content/structures/excavator/prefabs/excavator_yaw.prefab";
}
#endregion
#region Configuration
#region Rates
private sealed class RateType {
private readonly string _name;
private RateType(string name) {
_name = name;
}
public override string ToString() {
return _name;
}
public static implicit operator string(RateType type) => type._name;
public static readonly RateType Quarry = new RateType("Добываемые ресурсы в карьере");
public static readonly RateType Excavator = new RateType("Добываемые ресурсы в экскаваторе");
public static readonly RateType Gather = new RateType("Добываемые ресурсы");
public static readonly RateType Loot = new RateType("Получаемый лут");
public static readonly RateType Pickup = new RateType("Подбираемые ресурсы");
public static readonly RateType OvenSpeed = new RateType("Скорость печей");
public static readonly RateType[] Available = {
Quarry, Excavator, Gather, Loot, Pickup, OvenSpeed
}
;
}
private class Rates {
[JsonProperty("Днём")]
private Dictionary<string, float> _day = new Dictionary<string, float>();
[JsonProperty("Ночью")]
private Dictionary<string, float> _night = new Dictionary<string, float>();
public float GetRate(RateType type, bool day = true) {
var dict = day ? _day : _night;
float val;
return dict.TryGetValue(type, out val) ? val : 1f;
}
#region Default Сonfig
public static Rates Default => DefaultWith(1f);
public static Rates DefaultWith(float rate) => new Rates {
_day = RateType.Available.ToDictionary(x => (string) x, x => rate), _night = RateType.Available.ToDictionary(x => (string) x, x => rate)
}
;
#endregion
}
#endregion
#region Server Time Config
private class SavedDateConfig {
[JsonProperty("Устанавливать дату при запуске плагина")]
public bool Use;
[JsonProperty("День")]
private int _day;
[JsonProperty("Месяц")]
private int _month;
[JsonProperty("Год")]
private int _year;
[JsonProperty("Час")]
private int _hour;
[JsonProperty("Минута")]
private int _minute;
[JsonProperty("Секунда")]
private int _second;
[JsonIgnore]
public DateTime Date;
#region Default Config
public static SavedDateConfig DefaultConfig {
get {
var now = DateTime.Now;
return new SavedDateConfig {
Use = false, _day = now.Day, _month = now.Month, _year = now.Year, _hour = 12, _minute = 0, _second = 0
}
;
}
}
#endregion
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {
Date = new DateTime(_year, _month, _day, _hour, _minute, _second);
}
}
private class DayNightSwitchConfig {
[JsonProperty("Рассчитывать время по закату/восходу (false - по часам)")]
public bool UseSun;
[JsonProperty("Час начала дня (игровое время)")]
public int DayStartHour;
[JsonProperty("Час начала ночи (игровое время)")]
public int NightStartHour;
#region Default Config
public static DayNightSwitchConfig DefaultConfig => new DayNightSwitchConfig {
UseSun = true, DayStartHour = 6, NightStartHour = 18
}
;
#endregion
[JsonIgnore]
public float DayMultiplier;
[JsonIgnore]
public float NightMultiplier;
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {
DayMultiplier = 24.0f / (NightStartHour - DayStartHour);
NightMultiplier = 24.0f / (24.0f - (NightStartHour - DayStartHour));
}
}
private class TimeConfig {
[JsonProperty("Длина дня (в минутах)")]
public int DayLength;
[JsonProperty("Длина ночи (в минутах)")]
public int NightLength;
[JsonProperty("Настройки смены дня/ночи")]
public DayNightSwitchConfig DayNightSwitchConfig;
[JsonProperty("Восстанавливать состояние времени при запуске плагина")]
public bool RestoreState;
[JsonProperty("Устанавливаемая при запуске плагина дата")]
public SavedDateConfig SavedDateConfig;
#region Default Config
public static TimeConfig DefaultConfig => new TimeConfig {
DayLength = 30, NightLength = 30, DayNightSwitchConfig = DayNightSwitchConfig.DefaultConfig, RestoreState = true, SavedDateConfig = SavedDateConfig.DefaultConfig
}
;
#endregion
}
#endregion
#region General Options
private class ChatConfig {
[JsonProperty("Выводить сообщения в чат о начале дня или ночи")]
public bool NotifyTod;
[JsonProperty("Формат сообщений в чате")]
private string _chatFormat;
[JsonIgnore]
public string ChatFormat;
#region Default Config
public static ChatConfig DefaultConfig => new ChatConfig {
NotifyTod = true, _chatFormat = "<color=#ff0000>[RatesController]</color>: {0}"
}
;
#endregion
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {
ChatFormat = Formatter.ToUnity(_chatFormat);
}
}
[JsonObject(MemberSerialization.OptIn)]
private class DefaultModifiers {
[JsonProperty("Время переработки ресурсов (в секундах)")]
public Dictionary<string, float> SmeltRates;
[JsonProperty("Множители добываемых ресурсов")]
private Dictionary<string, float> _gatherRates;
[JsonProperty("Множители добываемых ресурсов в карьере")]
private Dictionary<string, float> _quarryRates;
[JsonProperty("Множители добываемых ресурсов в экскаваторе")]
private Dictionary<string, float> _excavatorRates;
[JsonProperty("Множители подбираемых ресурсов")]
private Dictionary<string, float> _pickupRates;
#region Default Config
public static DefaultModifiers DefaultConfig {
get {
var modifiers = new DefaultModifiers {
SmeltRates = new Dictionary<string, float>(), _gatherRates = new Dictionary<string, float> {
["Animal Fat"]
= 1.0f, ["Bear Meat"] = 1.0f, ["Bone Fragments"] = 1.0f, ["Cloth"] = 1.0f, ["High Quality Metal Ore"] = 1.0f, ["Human Skull"] = 1.0f, ["Leather"] = 1.0f, ["Metal Ore"] = 1.0f, ["Pork"] = 1.0f, ["Raw Chicken Breast"] = 1.0f, ["Raw Human Meat"] = 1.0f, ["Raw Wolf Meat"] = 1.0f, ["Stones"] = 1.0f, ["Sulfur Ore"] = 1.0f, ["Wolf Skull"] = 1.0f, ["Wood"] = 1.0f, ["Raw Deer Meat"] = 1.0f, ["Cactus Flesh"] = 1.0f
}
, _quarryRates = new Dictionary<string, float> {
["High Quality Metal Ore"]
= 1.0f, ["Sulfur Ore"] = 1.0f, ["Stones"] = 1.0f, ["Metal Fragments"] = 1.0f, ["Crude Oil"] = 1.0f
}
, _excavatorRates = GameManager.server.FindPrefab(Constants.ExcavatorArmPrefab).GetComponent<ExcavatorArm>()?.resourcesToMine.ToDictionary(x => x.itemDef.displayName.english, x => 1f), _pickupRates = new Dictionary<string, float> {
["Metal Ore"]
= 1.0f, ["Stones"] = 1.0f, ["Sulfur Ore"] = 1.0f, ["Wood"] = 1.0f, ["Hemp Seed"] = 1.0f, ["Corn Seed"] = 1.0f, ["Pumpkin Seed"] = 1.0f, ["Cloth"] = 1.0f, ["Pumpkin"] = 1.0f, ["Corn"] = 1.0f, ["Wolf Skull"] = 1.0f
}
}
;
ItemManager.GetItemDefinitions().ForEach(def => {
var cookable = def.GetComponent<ItemModCookable>();
if (!cookable) return;
modifiers.SmeltRates[def.displayName.english] = cookable.cookTime;
}
);
return modifiers;
}
}
#endregion
public float GetModifier(RateType type, Item item) {
if (type == RateType.Gather) return GetModifier(item.info, _gatherRates);
if (type == RateType.Quarry) return GetModifier(item.info, _quarryRates);
if (type == RateType.Excavator) return GetModifier(item.info, _excavatorRates);
if (type == RateType.Pickup) return GetModifier(item.info, _pickupRates);
return 1f;
}
private float GetModifier(ItemDefinition def, Dictionary<string, float> data) {
float mod;
if (data.TryGetValue(def.displayName.english, out mod) || data.TryGetValue(def.shortname, out mod)) return mod;
return 1f;
}
}
#endregion
#region Coal and HQM
private class HqmConfig {
[JsonProperty("Добавить МВК в список бонусов всех рудных жил")]
public bool AddHqm;
[JsonProperty("Количество МВК в жиле")]
private int _amount;
[JsonIgnore]
public ItemDefinition HqmDefinition;
[JsonIgnore]
public ItemAmount ItemAmount => new ItemAmount(HqmDefinition, _amount);
#region Default Config
public static HqmConfig DefaultConfig => new HqmConfig {
AddHqm = false, _amount = 2
}
;
#endregion
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {
HqmDefinition = ItemManager.FindItemDefinition("hq.metal.ore");
}
}
private class CoalConfig {
[JsonProperty("Шанс производства")]
private int _chance;
[JsonProperty("Количество")]
public int Amount;
//UnityEngine.Random.Range(0f, 1f) > burnable.byproductChance
[JsonIgnore]
public float Chance;
#region Default Config
public static CoalConfig DefaultConfig {
get {
var coal = ItemManager.FindItemDefinition("wood").GetComponent<ItemModBurnable>();
return new CoalConfig {
_chance = Mathf.RoundToInt(100 - coal.byproductChance*100), Amount = coal.byproductAmount
}
;
}
}
#endregion
public static CoalConfig Save() {
var coal = ItemManager.FindItemDefinition("wood").GetComponent<ItemModBurnable>();
return new CoalConfig {
Amount = coal.byproductAmount, Chance = coal.byproductChance
}
;
}
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {
if (Amount < 0) Amount = 0;
if (_chance <= 0 || Amount <= 0) Chance = 2;
else Chance = (100 - _chance) / 100f;
}
}
private class CoalConfigs {
[JsonProperty("Днём")]
public CoalConfig Day;
[JsonProperty("Ночью")]
public CoalConfig Night;
#region Default Config
public static CoalConfigs DefaultConfig => new CoalConfigs {
Day = CoalConfig.DefaultConfig, Night = CoalConfig.DefaultConfig
}
;
#endregion
public CoalConfig Get(bool isDay) => isDay ? Day : Night;
}
#endregion
#region Loot
private class AllowedItemsConfig {
[JsonProperty("Тип списка (0 - никак не управлять предметами, 1 - чёрный список, 2 - белый список).")]
private int _type;
[JsonProperty("Список предметов")]
public List<string> Items;
#region Default Config
public static AllowedItemsConfig DefaultConfig => new AllowedItemsConfig {
_type = 1, Items = new List<string> {
"Blueprint", "Rotten Apple", "Spoiled Wolf Meat", "Spoiled Chicken", "Spoiled Human Meat"
}
}
;
#endregion
public bool ShouldChange(Item item) {
switch (_type) {
case 1 : return !Items.Any(x => x == item.info.displayName.english || x == item.info.shortname);
case 2 : return Items.Any(x => x == item.info.displayName.english || x == item.info.shortname);
default : return true;
}
}
}
private class LootConfig {
[JsonProperty("Предметы, на которые действуют множители")]
public AllowedItemsConfig AllowedItems;
[JsonProperty("Предметы, которые не будут выпадать вообще")]
public List<string> BlackList;
#region Default Config
public static LootConfig DefaultConfig => new LootConfig {
AllowedItems = AllowedItemsConfig.DefaultConfig, BlackList = new List<string> {
"Rotten Apple"
}
}
;
#endregion
public bool ShouldDrop(Item item) => !BlackList.Any(x => x == item.info.displayName.english || x == item.info.shortname);
}
#endregion
private class PluginConfig {
[JsonProperty("Настройки сообщений в чате")]
public ChatConfig ChatConfig;
[JsonProperty("Настройки даты и времени")]
public TimeConfig TimeConfig;
[JsonProperty("Металл высокого качества")]
public HqmConfig HqmConfig;
[JsonProperty("Производство угля при сжигании дерева")]
public CoalConfigs CoalConfigs;
[JsonProperty("Настройки лута")]
public LootConfig LootConfig;
[JsonProperty("Общие множители")]
public Rates DefaultRates;
[JsonProperty("Множители по привилегиям")]
public Dictionary<string, Rates> CustomRates;
[JsonProperty("Множители ресурсов")]
public DefaultModifiers DefaultModifiers;
#region Default Config
public static PluginConfig DefaultConfig => new PluginConfig {
ChatConfig = ChatConfig.DefaultConfig, TimeConfig = TimeConfig.DefaultConfig, HqmConfig = HqmConfig.DefaultConfig, CoalConfigs = CoalConfigs.DefaultConfig, LootConfig = LootConfig.DefaultConfig, DefaultRates = Rates.Default, CustomRates = new Dictionary<string, Rates> {
[nameof(RatesController)+".vip"]
= Rates.DefaultWith(2f), [nameof(RatesController)+".premium"] = Rates.DefaultWith(3f)
}
, DefaultModifiers = DefaultModifiers.DefaultConfig
}
;
#endregion
}
#endregion
#region Data
private class PluginData {
public bool IsFrozen;
}
#endregion
#region Config and Data initialization
#region Data
private void LoadData() {
try {
_data = Interface.Oxide.DataFileSystem.ReadObject<PluginData>(Title);
}
catch (Exception ex) {
PrintError($"Failed to load data (is the file corrupt?) - no previously created recycles would work ({ex.Message})");
_data = new PluginData();
}
}
private void SaveData() {
if(TimeController.Initialized) _data.IsFrozen = TimeController.Instance.IsFrozen;
Interface.Oxide.DataFileSystem.WriteObject(Title, _data);
}
#endregion
#region Config
protected override void LoadConfig() {
base.LoadConfig();
_config = Config.ReadObject<PluginConfig>();
foreach (var customRate in _config.CustomRates) {
permission.RegisterPermission(customRate.Key, this);
}
if (CheckConfig()) SaveConfig();
LoadData();
}
private bool CheckConfig() {
bool res = false;
if (_config.TimeConfig.DayLength < 0) {
PrintWarning("Day length can't be less then 0, setting to 0");
_config.TimeConfig.DayLength = 0;
res = true;
}
if (_config.TimeConfig.NightLength < 0) {
PrintWarning("Night length can't be less then 0, setting to 0");
_config.TimeConfig.NightLength = 0;
res = true;
}
if (_config.TimeConfig.DayLength == 0 && _config.TimeConfig.NightLength == 0) {
PrintWarning("Both night and day length can't be 0 at the same time, setting day length to 30");
_config.TimeConfig.DayLength = 30;
res = true;
}
return res;
}
protected override void LoadDefaultConfig() {
_config = PluginConfig.DefaultConfig;
}
protected override void SaveConfig() {
Config.WriteObject(_config);
}
#endregion
#endregion
#region Localization
private static string GetMessage(string langKey, string playerId = null) {
return _ratesController.lang.GetMessage(langKey, _ratesController, playerId);
}
private static string GetMessage(string langKey, string playerId = null, params object[] args) {
return args.Length == 0 ? GetMessage(langKey, playerId) : string.Format(GetMessage(langKey, playerId), args);
}
private static void SendResponse(BasePlayer player, string langKey, params object[] args) {
if (!player || !player.IsConnected) return;
player.ChatMessage(string.Format(_config.ChatConfig.ChatFormat, GetMessage(langKey, player.UserIDString, args)));
}
private static void SendResponse(IPlayer player, string langKey, params object[] args) {
player.Reply(string.Format(_config.ChatConfig.ChatFormat, GetMessage(langKey, player.Id, args)));
}
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
[RateType.Quarry]
= "Quarry gather rates: {0}", [RateType.Excavator] = "Excavator gather rates: {0}", [RateType.Gather] = "Gather rates: {0}", [RateType.Loot] = "Loot rates: {0}", [RateType.Pickup] = "Pickup rates: {0}", [RateType.OvenSpeed] = "Oven speed: {0}", ["RatesPersonal"] = "Your personal rates:\n{0}", ["DayStarted"] = "The day has come!\n{0}", ["NightStarted"] ="The night has come!\n{0}", ["PositiveRate"] = "<color=green>{0:P0}</color>", ["NegativeRate"] = "<color=red>{0:P0}</color>", ["NeutralRate"] = "{0:P0}"
}
, this);
lang.RegisterMessages(new Dictionary<string, string> {
[RateType.Quarry]
= "Добываемые ресурсы в карьере: {0}", [RateType.Excavator] = "Добываемые ресурсы в экскаваторе: {0}", [RateType.Gather] = "Добываемые ресурсы: {0}", [RateType.Loot] = "Получаемый лут: {0}", [RateType.Pickup] = "Подбираемые ресурсы: {0}", [RateType.OvenSpeed] = "Скорость печей: {0}", ["RatesPersonal"] = "Ваши личные рейты:\n{0}", ["DayStarted"] = "Наступает день!\n{0}", ["NightStarted"] ="Наступает ночь!\n{0}", ["PositiveRate"] = "<color=green>{0:P0}</color>", ["NegativeRate"] = "<color=red>{0:P0}</color>", ["NeutralRate"] = "{0:P0}"
}
, this, "ru");
}
#endregion
#region Time controller
private class TimeController : FacepunchBehaviour {
private int _attempt;
private TOD_Sky _sky;
private TOD_Time _time;
private bool _isDaySet;
private float _lastKnownTime;
//env.time set workaround
private bool _initialized;
public static TimeController Instance {
get;
private set;
}
public static bool Initialized => Instance && Instance._initialized;
#region Instance public properties
public bool IsFrozen {
get {
return !_time.ProgressTime;
}
set {
_time.ProgressTime = !value;
}
}
#endregion
#region Private properties
private bool IsDay => DayStart <= _sky.Cycle.Hour && _sky.Cycle.Hour < NightStart;
private float DayStart {
get {
if (!_config.TimeConfig.DayNightSwitchConfig.UseSun) return _config.TimeConfig.DayNightSwitchConfig.DayStartHour;
return _sky.SunriseTime;
}
}
private float NightStart {
get {
if (!_config.TimeConfig.DayNightSwitchConfig.UseSun) return _config.TimeConfig.DayNightSwitchConfig.NightStartHour;
return _sky.SunsetTime;
}
}
private float DayLength {
get {
if (!_config.TimeConfig.DayNightSwitchConfig.UseSun) return _config.TimeConfig.DayLength * _config.TimeConfig.DayNightSwitchConfig.DayMultiplier;
return _config.TimeConfig.DayLength * (24.0f / (_sky.SunsetTime - _sky.SunriseTime));
}
}
private float NightLength {
get {
if (!_config.TimeConfig.DayNightSwitchConfig.UseSun) return _config.TimeConfig.NightLength * _config.TimeConfig.DayNightSwitchConfig.NightMultiplier;
return _config.TimeConfig.NightLength * (24.0f / (24.0f - (_sky.SunsetTime - _sky.SunriseTime)));
}
}
#endregion
#region Initialization
public void Initialize() {
_sky = TOD_Sky.Instance;
if (_sky == null) {
if (_attempt++ < 10) {
Invoke(Initialize, 1f);
return;
}
_ratesController.PrintError("Failed to get TOD_Sky instance in {0} attempts.");
return;
}
_ratesController.Puts("TOD_Sky found after {0} attempts", _attempt);
_time = _sky.Components.Time;
if (_time == null) {
_ratesController.PrintError("Can't find time component!");
return;
}
_initialized = true;
SetData();
if(IsDay) OnDayStarted();
else OnNightStarted();
_time.OnHour += OnHour;
}
private void SetData() {
_time.UseTimeCurve = false;
if (_config.TimeConfig.SavedDateConfig.Use) _sky.Cycle.DateTime = _config.TimeConfig.SavedDateConfig.Date;
if (_config.TimeConfig.RestoreState) _time.ProgressTime = !_ratesController._data.IsFrozen;
_lastKnownTime = _sky.Cycle.Hour;
}
#endregion
#region Day/Night Switch
private void OnDayStarted() {
if (!Initialized) return;
var dayLength = DayLength;
if (dayLength <= 0) {
print("Day skipped");
_sky.Cycle.Hour = NightStart;
_lastKnownTime = _sky.Cycle.Hour;
OnNightStarted();
return;
}
_time.DayLengthInMinutes = dayLength;
Interface.CallHook("OnDayStarted");
_isDaySet = true;
}
private void OnNightStarted() {
if (!Initialized) return;
var nightLength = NightLength;
if (nightLength <= 0) {
print("Night skipped");
_sky.Cycle.Hour += 24 - _sky.Cycle.Hour + DayStart;
_lastKnownTime = _sky.Cycle.Hour;
OnDayStarted();
return;
}
_time.DayLengthInMinutes = nightLength;
Interface.CallHook("OnNightStarted");
_isDaySet = false;
}
#endregion
#region Actions
private void OnHour() {
if(IsDay && !_isDaySet) OnDayStarted();
if(!IsDay && _isDaySet) OnNightStarted();
}
//env.time set workaround
private void Update() {
if (Math.Abs(_sky.Cycle.Hour - _lastKnownTime) > Time.deltaTime) OnHour();
_lastKnownTime = _sky.Cycle.Hour;
}
#endregion
public void Kill() {
_time.OnHour -= OnHour;
Destroy(gameObject);
Instance = null;
}
public static TimeController Create() {
if (Instance) {
_ratesController.PrintWarning("TimeController wasn't destroyed");
Instance.Kill();
}
var go = new GameObject(nameof(RatesController) + "." + nameof(TimeController));
DontDestroyOnLoad(go);
Instance = go.AddComponent<TimeController>();
return Instance;
}
}
#endregion
#region Initialization and quitting
private void Init() {
_ratesController = this;
}
private void Unload() {
OvenController.KillAll();
LootController.KillAll();
TimeController.Instance?.Kill();
UpdateCoalRates(true);
OnServerSave();
foreach (var pair in _smeltRatesBackup) pair.Key.cookTime = pair.Value;
_config = null;
_ratesController = null;
_data = null;
_defaultCoal = null;
_smeltRatesBackup.Clear();
_smeltRatesBackup = null;
_quarryOwners.Clear();
_quarryOwners = null;
_customCookables.Clear();
_customCookables = null;
}
private void OnServerSave() => SaveData();
private void OnServerInitialized() {
_defaultCoal = CoalConfig.Save();
TimeController.Create().Initialize();
ItemManager.GetItemDefinitions().ForEach(SetCookableDefinition);
UpdateCoalRates();
foreach (var oven in BaseNetworkable.serverEntities.OfType<BaseOven>()) OvenController.GetOrAdd(oven).TryRestart();
}
#endregion
#region Time Hooks
private void OnDayStarted() {
_isDay = true;
OvenController.TryRestartAll();
UpdateCoalRates();
if (!_config.ChatConfig.NotifyTod) return;
foreach (var player in BasePlayer.activePlayerList) SendRates(player, "DayStarted");
}
private void OnNightStarted() {
_isDay = false;
OvenController.TryRestartAll();
UpdateCoalRates();
if (!_config.ChatConfig.NotifyTod) return;
foreach (var player in BasePlayer.activePlayerList) SendRates(player, "NightStarted");
}
#endregion
#region Oxide Hooks (Sub)
void OnItemRemove(Item item) {
_customCookables.Remove(item);
}
void OnQuarryToggled(MiningQuarry quarry, BasePlayer player) {
if (!player || !quarry || !quarry.IsOn() || quarry.OwnerID != 0) return;
_quarryOwners[quarry.net.ID] = player.UserIDString;
}
void OnExcavatorResourceSet(ExcavatorArm arm, string resource, BasePlayer player) {
if (!player || !arm || arm.OwnerID != 0) return;
_quarryOwners[arm.net.ID] = player.UserIDString;
}
private void CanMoveItem(Item item, PlayerInventory playerLoot, uint targetContainer, int targetSlot, int amount) {
var container = item.GetRootContainer()?.entityOwner as LootContainer;
if (!container) return;
LootController.GetOrAdd(container).OnMove(item.position, playerLoot, amount);
}
private void OnItemAddedToContainer(ItemContainer inventory, Item item) {
var container = item.GetRootContainer()?.entityOwner as LootContainer;
if (!container) return;
LootController.GetOrAdd(container).OnRefill(inventory);
}
#endregion
#region Oxide Hooks (Rates)
void OnQuarryGather(MiningQuarry quarry, Item item) {
UpdateAmount(item, RateType.Quarry, GetQuarryOwner(quarry));
}
void OnExcavatorGather(ExcavatorArm arm, Item item) {
UpdateAmount(item, RateType.Excavator, GetQuarryOwner(arm));
}
void OnDispenserGather(ResourceDispenser dispenser, BasePlayer player, Item item) {
UpdateAmount(item, RateType.Gather, player.UserIDString);
if (!_config.HqmConfig.AddHqm || dispenser.gatherType != ResourceDispenser.GatherType.Ore) return;
if (dispenser.finishBonus.Any(x => x.itemDef == _config.HqmConfig.HqmDefinition)) return;
dispenser.finishBonus.Add(_config.HqmConfig.ItemAmount);
}
void OnDispenserBonus(ResourceDispenser dispenser, BasePlayer player, Item item) {
UpdateAmount(item, RateType.Gather, player.UserIDString);
}
void OnCollectiblePickup(Item item, BasePlayer player, CollectibleEntity collectible) {
UpdateAmount(item, RateType.Pickup, player.UserIDString);
}
void OnGrowableGathered(GrowableEntity plant, Item item, BasePlayer player) {
string owner = player.UserIDString;
if (plant.OwnerID != 0) owner = plant.OwnerID.ToString();
UpdateAmount(item, RateType.Pickup, owner);
}
private object OnOvenToggle(BaseOven oven, BasePlayer player) {
if (oven.ShortPrefabName.Contains("furnace")) return null;
return OvenController.GetOrAdd(oven).Switch(player);
}
private void OnLootEntity(BasePlayer player, LootContainer container) {
LootController.GetOrAdd(container).OnOpen(player);
}
private void OnLootEntityEnd(BasePlayer player, LootContainer container) {
LootController.GetOrAdd(container).OnClose(player);
}
private void OnContainerDropItems(ItemContainer container) {
var lootContainer = container?.entityOwner as LootContainer;
if (lootContainer == null) return;
LootController.GetOrAdd(lootContainer).OnDropItems();
}
#endregion
#region Console Commands
[ConsoleCommand("env.freeze")]
private void FreezeCmd(ConsoleSystem.Arg arg) {
if (arg.Connection != null && arg.Connection.authLevel < 2) return;
if (!TimeController.Initialized) return;
TimeController.Instance.IsFrozen = !TimeController.Instance.IsFrozen;
arg.ReplyWith(TimeController.Instance.IsFrozen ? "Frozen" : "Unfrozen");
}
#endregion
#region Chat Commands
[ChatCommand("Rates")]
private void RatesCmd(BasePlayer player, string command, string[] args) {
SendRates(player, "RatesPersonal");
}
#endregion
#region CustomCookable (cooktimeLeft bypass)
private class CustomCookable {
private float _cookTimeLeft;
private readonly ItemModCookable _cookable;
private readonly Item _item;
public CustomCookable(Item item, ItemModCookable cookable) {
_cookable = cookable;
_cookTimeLeft = _cookable.cookTime;
_item = item;
}
public void Cycle(float delta) {
var temperature = _item.temperature;
if (temperature < _cookable.lowTemp || temperature > _cookable.highTemp || _cookTimeLeft < 0f) {
if (_cookable.setCookingFlag && _item.HasFlag(global : : Item.Flag.Cooking)) {
_item.SetFlag(global : : Item.Flag.Cooking, false);
_item.MarkDirty();
}
return;
}
if (_cookable.setCookingFlag && !_item.HasFlag(global : : Item.Flag.Cooking)) {
_item.SetFlag(global : : Item.Flag.Cooking, true);
_item.MarkDirty();
}
foreach (var step in SplitDeltaBy(delta, _cookable.cookTime)) {
if (!InnerCycle(step)) return;
}
}
#region Static methods
public static CustomCookable GetOrCreate(Item item) {
var component = item.info.GetComponent<ItemModCookable>();
if (!component) return null;
CustomCookable cookable;
if (_ratesController._customCookables.TryGetValue(item, out cookable)) return cookable;
cookable = new CustomCookable(item, component);
_ratesController._customCookables[item] = cookable;
return cookable;
}
#endregion
#region Private methods
private bool InnerCycle(float delta) {
_cookTimeLeft -= delta;
if (_cookTimeLeft > 0f) return true;
if (_item.amount > 1) {
_cookTimeLeft += _cookable.cookTime;
_item.amount--;
_item.MarkDirty();
SpawnCookable();
return true;
}
_item.Remove();
SpawnCookable();
return false;
}
private void SpawnCookable() {
if (_cookable.becomeOnCooked == null) return;
Item item2 = ItemManager.Create(_cookable.becomeOnCooked, _cookable.amountOfBecome);
if (item2 == null || item2.MoveToContainer(_item.parent, _item.position) || item2.MoveToContainer(_item.parent)) return;
item2.Drop(_item.parent.dropPosition, _item.parent.dropVelocity);
if (!_item.parent.entityOwner) return;
var controller = _item.parent.entityOwner.GetComponent<OvenController>();
if (controller != null) {
controller.OvenFull();
}
}
#endregion
}
#endregion
#region Oven Controller
private class OvenController : FacepunchBehaviour {
private static readonly Dictionary<BaseOven, OvenCon
// ... [Code preview truncated due to length. Full source in attached file] ...