Decompiled source of YourThirsty v2.0.0
YourThirsty.dll
Decompiled 12 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; using YourThirsty; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal class Barrel : MonoBehaviour, Interactable, Hoverable { [CompilerGenerated] private sealed class <PourBucketRoutine>d__15 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Player player; public ItemData bucket; public Barrel <>4__this; private float <timer>5__1; private bool <bucketDirty>5__2; private int <totalWater>5__3; private int <remaining>5__4; private int <added>5__5; private float <fraction>5__6; private int <partialWater>5__7; private int <space>5__8; private int <toAdd>5__9; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <PourBucketRoutine>d__15(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; <>1__state = -3; <timer>5__1 = 0f; <bucketDirty>5__2 = BucketItem.IsDirtyWater(bucket); <totalWater>5__3 = BucketItem.GetWater(bucket); SFXManager.PlayRefill(player); break; case 1: <>1__state = -3; break; } if (<timer>5__1 < 5f) { if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsSwimming()) { goto IL_00bc; } Vector3 velocity = ((Character)player).GetVelocity(); if (((Vector3)(ref velocity)).magnitude > 2f) { goto IL_00bc; } <timer>5__1 += Time.deltaTime; UIManager.SetFillProgress(<timer>5__1 / 5f); <>2__current = null; <>1__state = 1; result = true; } else { <remaining>5__4 = <>4__this.MaxWater - <>4__this.GetStoredWater(); <added>5__5 = Mathf.Min(<totalWater>5__3, <remaining>5__4); if (<added>5__5 > 0) { <>4__this.AddWater(<added>5__5, <bucketDirty>5__2); BucketItem.SetWater(bucket, <totalWater>5__3 - <added>5__5); if (<totalWater>5__3 - <added>5__5 <= 0) { BucketItem.SetDirty(bucket, dirty: false); } } UIManager.HideFillBar(); SFXManager.PlayRefill(player); ((Character)player).Message((MessageType)2, $"Poured water into barrel ({<>4__this.GetStoredWater()}/{<>4__this.MaxWater})", 0, (Sprite)null); <>m__Finally1(); result = false; } goto end_IL_0000; IL_00bc: <fraction>5__6 = <timer>5__1 / 5f; if (<fraction>5__6 > 0.05f) { <partialWater>5__7 = Mathf.Max(1, Mathf.FloorToInt((float)<totalWater>5__3 * <fraction>5__6)); <space>5__8 = <>4__this.MaxWater - <>4__this.GetStoredWater(); <toAdd>5__9 = Mathf.Min(<partialWater>5__7, <space>5__8); if (<toAdd>5__9 > 0) { <>4__this.AddWater(<toAdd>5__9, <bucketDirty>5__2); BucketItem.SetWater(bucket, <totalWater>5__3 - <toAdd>5__9); if (<totalWater>5__3 - <toAdd>5__9 <= 0) { BucketItem.SetDirty(bucket, dirty: false); } } } UIManager.HideFillBar(); result = false; <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; <>4__this._fillRoutine = null; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <PourContainerRoutine>d__18 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Player player; public ContainerState state; public Barrel <>4__this; private float <timer>5__1; private bool <containerDirty>5__2; private int <totalWater>5__3; private int <remaining>5__4; private int <added>5__5; private float <fraction>5__6; private int <partialWater>5__7; private int <space>5__8; private int <toAdd>5__9; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <PourContainerRoutine>d__18(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; <>1__state = -3; <timer>5__1 = 0f; <containerDirty>5__2 = state.IsDirty; <totalWater>5__3 = state.CurrentAmount; SFXManager.PlayRefill(player); break; case 1: <>1__state = -3; break; } if (<timer>5__1 < 4f) { if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsSwimming()) { goto IL_00bc; } Vector3 velocity = ((Character)player).GetVelocity(); if (((Vector3)(ref velocity)).magnitude > 2f) { goto IL_00bc; } <timer>5__1 += Time.deltaTime; UIManager.SetFillProgress(<timer>5__1 / 4f); <>2__current = null; <>1__state = 1; result = true; } else { <remaining>5__4 = <>4__this.MaxWater - <>4__this.GetStoredWater(); <added>5__5 = Mathf.Min(<totalWater>5__3, <remaining>5__4); if (<added>5__5 > 0) { <>4__this.AddWater(<added>5__5, <containerDirty>5__2); state.CurrentAmount = <totalWater>5__3 - <added>5__5; ItemManager.SaveContainerState(player, state); } UIManager.HideFillBar(); SFXManager.PlayRefill(player); ((Character)player).Message((MessageType)2, $"Poured water into barrel ({<>4__this.GetStoredWater()}/{<>4__this.MaxWater})", 0, (Sprite)null); <>m__Finally1(); result = false; } goto end_IL_0000; IL_00bc: <fraction>5__6 = <timer>5__1 / 4f; if (<fraction>5__6 > 0.05f) { <partialWater>5__7 = Mathf.Max(1, Mathf.FloorToInt((float)<totalWater>5__3 * <fraction>5__6)); <space>5__8 = <>4__this.MaxWater - <>4__this.GetStoredWater(); <toAdd>5__9 = Mathf.Min(<partialWater>5__7, <space>5__8); if (<toAdd>5__9 > 0) { <>4__this.AddWater(<toAdd>5__9, <containerDirty>5__2); state.CurrentAmount = <totalWater>5__3 - <toAdd>5__9; ItemManager.SaveContainerState(player, state); } } UIManager.HideFillBar(); result = false; <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; <>4__this._fillRoutine = null; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string ZDO_Water = "YT_BarrelWater"; private const string ZDO_Dirty = "YT_BarrelDirty"; private ZNetView m_nview; private Coroutine _fillRoutine; public int MaxWater => ConfigManager.BarrelCapacity.Value; private void Awake() { m_nview = ((Component)this).GetComponent<ZNetView>(); } public int GetStoredWater() { if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid()) { return 0; } return m_nview.GetZDO().GetInt("YT_BarrelWater", 0); } private void SetStoredWater(int amount) { if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid()) { m_nview.GetZDO().Set("YT_BarrelWater", Mathf.Clamp(amount, 0, MaxWater)); } } public bool IsDirty() { if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid()) { return false; } return m_nview.GetZDO().GetBool("YT_BarrelDirty", false); } private void SetDirty(bool dirty) { if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid()) { m_nview.GetZDO().Set("YT_BarrelDirty", dirty); } } public int AddWater(int amount, bool dirty = false) { int storedWater = GetStoredWater(); int num = MaxWater - storedWater; int num2 = Mathf.Min(amount, num); if (num2 > 0) { SetStoredWater(storedWater + num2); if (dirty) { SetDirty(dirty: true); } } return num2; } public int AddWater(float amount) { return AddWater(Mathf.FloorToInt(amount)); } public int TakeWater(int amount) { int storedWater = GetStoredWater(); int num = Mathf.Min(amount, storedWater); if (num > 0) { SetStoredWater(storedWater - num); if (GetStoredWater() <= 0) { SetDirty(dirty: false); } } return num; } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold) { return false; } if (_fillRoutine != null || DrinkSystem.IsBusy) { return false; } Player val = (Player)(object)((user is Player) ? user : null); if (!Object.op_Implicit((Object)(object)val)) { return false; } ItemData equippedBucket = BucketItem.GetEquippedBucket(val); if (equippedBucket != null && !BucketItem.IsEmpty(equippedBucket) && !BucketItem.IsBoiling(equippedBucket)) { int num = MaxWater - GetStoredWater(); if (num <= 0) { ((Character)val).Message((MessageType)2, "Barrel is full.", 0, (Sprite)null); return false; } _fillRoutine = ((MonoBehaviour)this).StartCoroutine(PourBucketRoutine(val, equippedBucket)); return true; } if (ItemManager.CanFill(val, dirty: false, out var state)) { int storedWater = GetStoredWater(); if (storedWater > 0) { bool dirty = IsDirty(); int amount = state.MaxAmount - state.CurrentAmount; int num2 = TakeWater(amount); if (num2 > 0) { ItemManager.Fill(val, dirty); SFXManager.PlayRefill(val); ((Character)val).Message((MessageType)2, $"Filled from barrel ({GetStoredWater()}/{MaxWater}).", 0, (Sprite)null); return true; } } ((Character)val).Message((MessageType)2, "Barrel is empty.", 0, (Sprite)null); return false; } if (ItemManager.CanFillFromInventory(val, dirty: false, out var state2)) { int storedWater2 = GetStoredWater(); if (storedWater2 > 0) { bool dirty2 = IsDirty(); int amount2 = state2.MaxAmount - state2.CurrentAmount; int num3 = TakeWater(amount2); if (num3 > 0) { ItemManager.FillFromInventory(val, dirty2); SFXManager.PlayRefill(val); ((Character)val).Message((MessageType)2, $"Filled from barrel ({GetStoredWater()}/{MaxWater}).", 0, (Sprite)null); return true; } } ((Character)val).Message((MessageType)2, "Barrel is empty.", 0, (Sprite)null); return false; } ((Character)val).Message((MessageType)2, "You need a Water Bucket to pour, or a Waterskin/Horn to fill.", 0, (Sprite)null); return false; } [IteratorStateMachine(typeof(<PourBucketRoutine>d__15))] private IEnumerator PourBucketRoutine(Player player, ItemData bucket) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <PourBucketRoutine>d__15(0) { <>4__this = this, player = player, bucket = bucket }; } public bool StartPourBucket(Player player, ItemData bucket) { if (_fillRoutine != null || DrinkSystem.IsBusy) { ((Character)player).Message((MessageType)2, "Busy — wait for current action to finish.", 0, (Sprite)null); return false; } int num = MaxWater - GetStoredWater(); if (num <= 0) { ((Character)player).Message((MessageType)2, "Barrel is full.", 0, (Sprite)null); return false; } _fillRoutine = ((MonoBehaviour)this).StartCoroutine(PourBucketRoutine(player, bucket)); return true; } public bool StartPourContainer(Player player, ContainerState state) { if (_fillRoutine != null || DrinkSystem.IsBusy) { ((Character)player).Message((MessageType)2, "Busy — wait for current action to finish.", 0, (Sprite)null); return false; } int num = MaxWater - GetStoredWater(); if (num <= 0) { ((Character)player).Message((MessageType)2, "Barrel is full.", 0, (Sprite)null); return false; } if (state == null || state.CurrentAmount <= 0) { return false; } _fillRoutine = ((MonoBehaviour)this).StartCoroutine(PourContainerRoutine(player, state)); return true; } [IteratorStateMachine(typeof(<PourContainerRoutine>d__18))] private IEnumerator PourContainerRoutine(Player player, ContainerState state) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <PourContainerRoutine>d__18(0) { <>4__this = this, player = player, state = state }; } public bool UseItem(Humanoid user, ItemData item) { return false; } public string GetHoverText() { int storedWater = GetStoredWater(); string text = $"Rain Barrel ({storedWater}/{MaxWater})"; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return text; } ItemData equippedBucket = BucketItem.GetEquippedBucket(localPlayer); if (equippedBucket != null && !BucketItem.IsEmpty(equippedBucket) && !BucketItem.IsBoiling(equippedBucket)) { return text + "\n<color=yellow>[G]</color> Fill Barrel"; } bool flag = false; bool flag2 = false; bool flag3 = false; if (ItemManager.CanFill(localPlayer, dirty: false, out var _)) { flag = true; flag3 = true; } if (ItemManager.CanDrinkFromContainer(localPlayer, out var _)) { flag = true; flag2 = true; } if (flag) { if (storedWater > 0 && flag3) { text += "\n<color=yellow>[E]</color> Fill Container"; } if (flag2) { text += "\n<color=yellow>[G]</color> Fill Barrel"; } } return text; } public string GetHoverName() { return "Rain Barrel"; } } internal static class Horn { internal static bool Use(Player player, ItemData item) { if (ItemManager.CanDrinkFromContainer(player, out var _)) { DrinkSystem.TryDrink(); return true; } SFXManager.PlayEmpty(player); return true; } } [HarmonyPatch(typeof(Player), "Update")] internal static class PlayerPatch { private static SE_Stats _seBucketSlow; private static bool _penaltyActive; private static float _checkTimer; private const float CheckInterval = 0.5f; [HarmonyPostfix] private static void Postfix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } _checkTimer -= Time.deltaTime; if (!(_checkTimer > 0f)) { _checkTimer = 0.5f; ItemData nonEmptyBucketInInventory = BucketItem.GetNonEmptyBucketInInventory(__instance); bool flag = nonEmptyBucketInInventory != null && BucketItem.IsFull(nonEmptyBucketInInventory); if (flag && !_penaltyActive) { EnsureStatusEffect(); ((Character)__instance).GetSEMan().AddStatusEffect((StatusEffect)(object)_seBucketSlow, false, 0, 0f); _penaltyActive = true; } else if (!flag && _penaltyActive) { ((Character)__instance).GetSEMan().RemoveStatusEffect((StatusEffect)(object)_seBucketSlow, false); _penaltyActive = false; } } } private static void EnsureStatusEffect() { if (!((Object)(object)_seBucketSlow != (Object)null)) { _seBucketSlow = ScriptableObject.CreateInstance<SE_Stats>(); ((Object)_seBucketSlow).name = "SE_YT_BucketSlow"; ((StatusEffect)_seBucketSlow).m_name = "Carrying Water"; ((StatusEffect)_seBucketSlow).m_tooltip = "Heavy Water Bucket slows you down"; ((StatusEffect)_seBucketSlow).m_ttl = 0f; _seBucketSlow.m_speedModifier = (0f - ConfigManager.BucketSpeedPenalty.Value) / 10f; } } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class PlayerSpawnedPatch { [HarmonyPostfix] private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { YourThirstyPlugin.Log.LogInfo((object)"PlayerSpawnedPatch: Player spawned — refreshing piece requirements."); ItemRegistration.RefreshPieceRequirements(); } } } internal static class Waterskin { internal static bool Use(Player player, ItemData item) { if (ItemManager.CanDrinkFromContainer(player, out var _)) { DrinkSystem.TryDrink(); return true; } SFXManager.PlayEmpty(player); return true; } } internal class Well : MonoBehaviour, Interactable, Hoverable { private const string ZDO_Water = "YT_WellWater"; private ZNetView m_nview; public int MaxWater => 999; private void Awake() { m_nview = ((Component)this).GetComponent<ZNetView>(); } private void Start() { if ((Object)(object)m_nview != (Object)null && m_nview.IsValid() && m_nview.IsOwner() && m_nview.GetZDO().GetInt("YT_WellWater", -1) < 0) { m_nview.GetZDO().Set("YT_WellWater", MaxWater); } } public int GetStoredWater() { if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid()) { return 0; } int @int = m_nview.GetZDO().GetInt("YT_WellWater", MaxWater); return Mathf.Clamp(@int, 0, MaxWater); } private void SetStoredWater(int amount) { if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid()) { m_nview.GetZDO().Set("YT_WellWater", Mathf.Clamp(amount, 0, MaxWater)); } } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold) { return false; } Player val = (Player)(object)((user is Player) ? user : null); if (!Object.op_Implicit((Object)(object)val)) { return false; } ItemData equippedBucket = BucketItem.GetEquippedBucket(val); if (equippedBucket != null && !BucketItem.IsFull(equippedBucket)) { int num = BucketItem.Capacity - BucketItem.CurrentWater(equippedBucket); BucketItem.SetWater(equippedBucket, BucketItem.CurrentWater(equippedBucket) + num); SFXManager.PlayRefill(val); ((Character)val).Message((MessageType)2, "Water Bucket filled with clean water.", 0, (Sprite)null); return true; } if (ItemManager.CanFill(val, dirty: false, out var _)) { ItemManager.Fill(val, dirty: false); SFXManager.PlayRefill(val); ((Character)val).Message((MessageType)2, "Filled with clean water from the well.", 0, (Sprite)null); return true; } ((Character)val).Message((MessageType)2, "Equip a Water Bucket or container to fill from the well.", 0, (Sprite)null); return false; } public bool UseItem(Humanoid user, ItemData item) { return false; } public string GetHoverText() { return "<color=yellow>[E]</color> Well (Always Full)"; } public string GetHoverName() { return "Well"; } } internal class BoilingBucketPiece : MonoBehaviour, Interactable, Hoverable { private const string ZDO_CoolStart = "YT_CoolStart"; private const string ZDO_CoolActive = "YT_CoolActive"; private ZNetView m_nview; private void Awake() { m_nview = ((Component)this).GetComponent<ZNetView>(); if ((Object)(object)m_nview != (Object)null && m_nview.IsValid() && !m_nview.GetZDO().GetBool("YT_CoolActive", false)) { StartCooling(); } } public float GetTimeRemaining() { if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid()) { return 0f; } long @long = m_nview.GetZDO().GetLong("YT_CoolStart", 0L); if (@long <= 0) { return 0f; } long ticks = ZNet.instance.GetTime().Ticks; float num = (float)((double)(ticks - @long) / 10000000.0); float value = ConfigManager.CoolDurationSeconds.Value; return Mathf.Max(0f, value - num); } public bool IsCooled() { if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid()) { return false; } if (!m_nview.GetZDO().GetBool("YT_CoolActive", false)) { return false; } return GetTimeRemaining() <= 0f; } private void StartCooling() { if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid()) { long ticks = ZNet.instance.GetTime().Ticks; m_nview.GetZDO().Set("YT_CoolStart", ticks); m_nview.GetZDO().Set("YT_CoolActive", true); } } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold) { return false; } Player val = (Player)(object)((user is Player) ? user : null); if (!Object.op_Implicit((Object)(object)val)) { return false; } if (!IsCooled()) { float timeRemaining = GetTimeRemaining(); int num = Mathf.CeilToInt(timeRemaining); ((Character)val).Message((MessageType)2, $"Water is still cooling... {num}s", 0, (Sprite)null); return false; } GameObject bucket = PrefabLoader.Bucket; if ((Object)(object)bucket == (Object)null) { ((Character)val).Message((MessageType)2, "Error: bucket prefab missing.", 0, (Sprite)null); return false; } ItemDrop component = bucket.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null) { return false; } ItemData val2 = ((Humanoid)val).GetInventory().AddItem(((Object)bucket).name, 1, component.m_itemData.m_quality, component.m_itemData.m_variant, 0L, "", false); if (val2 == null) { ((Character)val).Message((MessageType)2, "Inventory full!", 0, (Sprite)null); return false; } BucketItem.SetWater(val2, BucketItem.Capacity); BucketItem.SetDirty(val2, dirty: false); BucketItem.SetBoiling(val2, boiling: false); SFXManager.PlayRefill(val); ((Character)val).Message((MessageType)2, "Collected purified clean water!", 0, (Sprite)null); if ((Object)(object)m_nview != (Object)null && m_nview.IsValid()) { WearNTear component2 = ((Component)this).GetComponent<WearNTear>(); if ((Object)(object)component2 != (Object)null) { component2.Remove(false); } else { ZNetScene.instance.Destroy(((Component)this).gameObject); } } return true; } public bool UseItem(Humanoid user, ItemData item) { return false; } public string GetHoverText() { if (IsCooled()) { return "<color=yellow>[E]</color> Collect Purified Water"; } float timeRemaining = GetTimeRemaining(); int num = Mathf.FloorToInt(timeRemaining / 60f); int num2 = Mathf.FloorToInt(timeRemaining % 60f); string text = ((num > 0) ? $"{num}:{num2:D2}" : $"{num2}s"); return "<color=#AAAAAA>Cooling... " + text + "</color>"; } public string GetHoverName() { return "Boiling Water Bucket"; } } internal class CauldronBoiling : MonoBehaviour { private const string ZDO_BoilStart = "YT_BoilStart"; private const string ZDO_BoilActive = "YT_BoilActive"; private ZNetView m_nview; private void Awake() { m_nview = ((Component)this).GetComponent<ZNetView>(); } private bool EnsureNView() { if ((Object)(object)m_nview == (Object)null) { m_nview = ((Component)this).GetComponent<ZNetView>(); } return (Object)(object)m_nview != (Object)null && m_nview.IsValid(); } public bool IsBoilActive() { if (!EnsureNView()) { return false; } ZDO zDO = m_nview.GetZDO(); if (zDO == null) { return false; } return zDO.GetBool("YT_BoilActive", false); } public bool IsReady() { if (!IsBoilActive()) { return false; } return GetTimeRemaining() <= 0f; } public float GetTimeRemaining() { if (!EnsureNView()) { return 0f; } ZDO zDO = m_nview.GetZDO(); if (zDO == null) { return 0f; } long @long = zDO.GetLong("YT_BoilStart", 0L); if (@long <= 0) { return 0f; } if ((Object)(object)ZNet.instance == (Object)null) { return 0f; } long ticks = ZNet.instance.GetTime().Ticks; float num = (float)((double)(ticks - @long) / 10000000.0); float value = ConfigManager.BoilDurationSeconds.Value; return Mathf.Max(0f, value - num); } public bool StartBoil() { if (!EnsureNView()) { YourThirstyPlugin.Log.LogWarning((object)"CauldronBoiling.StartBoil: m_nview null/invalid — boil NOT started."); return false; } ZDO zDO = m_nview.GetZDO(); if (zDO == null) { YourThirstyPlugin.Log.LogWarning((object)"CauldronBoiling.StartBoil: ZDO is null — boil NOT started."); return false; } if ((Object)(object)ZNet.instance == (Object)null) { YourThirstyPlugin.Log.LogWarning((object)"CauldronBoiling.StartBoil: ZNet.instance is null — boil NOT started."); return false; } long ticks = ZNet.instance.GetTime().Ticks; zDO.Set("YT_BoilStart", ticks); zDO.Set("YT_BoilActive", true); YourThirstyPlugin.Log.LogInfo((object)string.Format("CauldronBoiling.StartBoil: Boil started at ticks={0}, BoilActive={1}", ticks, zDO.GetBool("YT_BoilActive", false))); return true; } public void ResetBoil() { if (EnsureNView()) { ZDO zDO = m_nview.GetZDO(); if (zDO != null) { zDO.Set("YT_BoilActive", false); zDO.Set("YT_BoilStart", 0L); } } } } namespace YourThirsty { internal static class PlayerAnimationInjector { public enum DrinkAnimMode { NaturalWater, ContainerDrink, ContainerRefill } [CompilerGenerated] private sealed class <DrinkTimerRoutine>d__13 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Player player; public float duration; public DrinkAnimMode mode; private float <elapsed>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DrinkTimerRoutine>d__13(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; _playing = true; YourThirstyPlugin.Log.LogInfo((object)$"AnimationInjector: Drink animation started ({duration}s, {mode})"); <elapsed>5__1 = 0f; break; case 1: <>1__state = -1; break; } if (<elapsed>5__1 < duration) { if (!((Object)(object)player == (Object)null) && !((Character)player).IsDead() && !((Character)player).IsSwimming()) { Vector3 velocity = ((Character)player).GetVelocity(); if (!(((Vector3)(ref velocity)).magnitude > 2f)) { <elapsed>5__1 += Time.deltaTime; <>2__current = null; <>1__state = 1; return true; } } Stop(); return false; } RestoreAnimatorSpeed(); try { if ((Object)(object)player != (Object)null) { InvokeStopEmote(player); } } catch { } _playing = false; _activeRoutine = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static bool _playing; private static Coroutine _activeRoutine; private static MethodInfo _stopEmote; private static float _savedAnimSpeed = 1f; private static bool _speedOverridden; public static bool IsPlaying => _playing; public static void Init() { _playing = false; _activeRoutine = null; _speedOverridden = false; YourThirstyPlugin.Log.LogInfo((object)"AnimationInjector: initialized."); } public static void Inject(Player player) { } public static void PlayDrinkAnimation(Player player, DrinkAnimMode mode) { if ((Object)(object)player == (Object)null) { return; } Stop(); switch (mode) { case DrinkAnimMode.NaturalWater: { Animator componentInChildren = ((Component)player).GetComponentInChildren<Animator>(); if ((Object)(object)componentInChildren != (Object)null) { _savedAnimSpeed = componentInChildren.speed; componentInChildren.speed = 0.4f; _speedOverridden = true; } try { ZSyncAnimation component2 = ((Component)player).GetComponent<ZSyncAnimation>(); if ((Object)(object)component2 != (Object)null) { component2.SetTrigger("interact"); component2.SetTrigger("eat"); } } catch { } break; } case DrinkAnimMode.ContainerDrink: try { player.StartEmote("drink", true); } catch { } break; case DrinkAnimMode.ContainerRefill: try { ZSyncAnimation component = ((Component)player).GetComponent<ZSyncAnimation>(); if ((Object)(object)component != (Object)null) { component.SetTrigger("interact"); } } catch { } break; } _activeRoutine = ((MonoBehaviour)CoroutineHost.Instance).StartCoroutine(DrinkTimerRoutine(player, ConfigManager.DrinkDurationSeconds.Value, mode)); } public static void Stop() { if (_activeRoutine != null) { try { ((MonoBehaviour)CoroutineHost.Instance).StopCoroutine(_activeRoutine); } catch { } _activeRoutine = null; } _playing = false; RestoreAnimatorSpeed(); try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { InvokeStopEmote(localPlayer); } } catch { } } private static void RestoreAnimatorSpeed() { if (!_speedOverridden) { return; } try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { Animator componentInChildren = ((Component)localPlayer).GetComponentInChildren<Animator>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.speed = _savedAnimSpeed; } } } catch { } _savedAnimSpeed = 1f; _speedOverridden = false; } [IteratorStateMachine(typeof(<DrinkTimerRoutine>d__13))] private static IEnumerator DrinkTimerRoutine(Player player, float duration, DrinkAnimMode mode) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DrinkTimerRoutine>d__13(0) { player = player, duration = duration, mode = mode }; } private static void InvokeStopEmote(Player player) { if (_stopEmote == null) { _stopEmote = AccessTools.Method(typeof(Player), "StopEmote", (Type[])null, (Type[])null); } _stopEmote?.Invoke(player, null); } } internal static class BiomeWaterQuality { private class BiomeState { public bool IsGood; public float Timer; public (float, float)[] Brackets; public BiomeState((float, float)[] brackets) { Brackets = brackets; IsGood = true; Timer = PickDuration(brackets); } } private static readonly (float min, float max)[] BlackForestBrackets = new(float, float)[4] { (180f, 300f), (360f, 480f), (600f, 720f), (900f, 900f) }; private static readonly (float min, float max)[] PlainsBrackets = new(float, float)[4] { (120f, 240f), (300f, 420f), (480f, 600f), (720f, 900f) }; private static readonly (float min, float max)[] MistlandsBrackets = new(float, float)[4] { (300f, 480f), (600f, 840f), (900f, 1200f), (1500f, 1800f) }; private static readonly Dictionary<Biome, BiomeState> _states = new Dictionary<Biome, BiomeState>(); private static bool _initialized; public static void Init() { _states.Clear(); _states[(Biome)8] = new BiomeState(BlackForestBrackets); _states[(Biome)16] = new BiomeState(PlainsBrackets); _states[(Biome)512] = new BiomeState(MistlandsBrackets); _initialized = true; YourThirstyPlugin.Log.LogInfo((object)"BiomeWaterQuality: initialized (BlackForest, Plains, Mistlands cycling)."); } public static void Update() { //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { return; } float deltaTime = Time.deltaTime; foreach (KeyValuePair<Biome, BiomeState> state in _states) { BiomeState value = state.Value; value.Timer -= deltaTime; if (value.Timer <= 0f) { value.IsGood = !value.IsGood; value.Timer = PickDuration(value.Brackets); YourThirstyPlugin.Log.LogInfo((object)($"BiomeWaterQuality: {state.Key} water is now " + string.Format("{0} for {1:F0}s", value.IsGood ? "GOOD" : "BAD", value.Timer))); } } } public static bool IsBadWater(Biome biome) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (_states.TryGetValue(biome, out var value)) { return !value.IsGood; } return false; } public static bool HasCycling(Biome biome) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return _states.ContainsKey(biome); } private static float PickDuration((float min, float max)[] brackets) { int num = Random.Range(0, brackets.Length); return Random.Range(brackets[num].min, brackets[num].max); } } internal enum DifficultyLevel { Easy, Casual, Hard, Custom } internal static class ConfigManager { public static ConfigEntry<DifficultyLevel> Difficulty; public static ConfigEntry<float> ThirstDurationMinutes; public static ConfigEntry<float> CleanHydrationAmount; public static ConfigEntry<float> DirtyHydrationAmount; public static ConfigEntry<float> DrinkDurationSeconds; public static ConfigEntry<float> HydratedDuration; public static ConfigEntry<float> SicknessDuration; public static ConfigEntry<float> DirtyWaterSpeedPenalty; public static ConfigEntry<int> WaterskinCapacity; public static ConfigEntry<int> HornCapacity; public static ConfigEntry<int> BucketCapacity; public static ConfigEntry<int> BarrelCapacity; public static ConfigEntry<float> BucketSpeedPenalty; public static ConfigEntry<float> BoilDurationSeconds; public static ConfigEntry<float> CoolDurationSeconds; public static void Init(ConfigFile config) { Difficulty = config.Bind<DifficultyLevel>("Difficulty", "DifficultyLevel", DifficultyLevel.Casual, "Easy = forgiving (slow thirst, big drinks, fast boil).\nCasual = balanced (normal thirst, moderate drinks).\nHard = punishing (fast thirst, small drinks, slow boil, harsh debuffs).\nCustom = use the manual values below without overwriting them."); ThirstDurationMinutes = config.Bind<float>("Thirst", "ThirstDurationMinutes", 35f, "Time in minutes to go from 0 to max thirst."); CleanHydrationAmount = config.Bind<float>("Thirst", "CleanHydrationAmount", 20f, "Thirst reduced per drink (20 = 20% hydration restored)."); DirtyHydrationAmount = config.Bind<float>("Thirst", "DirtyHydrationAmount", 20f, "Thirst reduced per dirty water drink (20 = 20% hydration restored, before dirty penalty)."); DrinkDurationSeconds = config.Bind<float>("Thirst", "DrinkDurationSeconds", 4f, "Duration of drink animation."); HydratedDuration = config.Bind<float>("Buffs", "HydratedDuration", 160f, "Duration in seconds of the Hydrated buff after drinking clean water (160 = 2m 40s)."); SicknessDuration = config.Bind<float>("Buffs", "SicknessDuration", 160f, "Duration in seconds of the Dirty Water debuff from dirty water (160 = 2m 40s)."); DirtyWaterSpeedPenalty = config.Bind<float>("Buffs", "DirtyWaterSpeedPenalty", 0.2f, "Movement speed reduction fraction when sick from dirty water (0.20 = 20%)."); WaterskinCapacity = config.Bind<int>("Containers", "WaterskinCapacity", 5, "Units of water a waterskin holds."); HornCapacity = config.Bind<int>("Containers", "HornCapacity", 3, "Units of water a horn holds."); BucketCapacity = config.Bind<int>("Containers", "BucketCapacity", 6, "Units of water a bucket carries."); BarrelCapacity = config.Bind<int>("Containers", "BarrelCapacity", 12, "Units of water a barrel stores (12 = 2 full bucket pours)."); BucketSpeedPenalty = config.Bind<float>("Containers", "BucketSpeedPenalty", 3f, "Movement speed reduction when carrying a full bucket."); BoilDurationSeconds = config.Bind<float>("Boiling", "BoilDurationSeconds", 150f, "Time in seconds for water to boil in the cauldron (150 = 2 min 30 sec)."); CoolDurationSeconds = config.Bind<float>("Boiling", "CoolDurationSeconds", 60f, "Time in seconds for boiling water to cool after being placed (60 = 1 min)."); ApplyDifficultyPreset(); } public static void ApplyDifficultyPreset() { DifficultyLevel value = Difficulty.Value; switch (value) { case DifficultyLevel.Custom: return; case DifficultyLevel.Easy: ThirstDurationMinutes.Value = 45f; CleanHydrationAmount.Value = 25f; DirtyHydrationAmount.Value = 20f; DrinkDurationSeconds.Value = 3f; HydratedDuration.Value = 200f; SicknessDuration.Value = 90f; DirtyWaterSpeedPenalty.Value = 0.1f; WaterskinCapacity.Value = 6; HornCapacity.Value = 4; BucketCapacity.Value = 8; BarrelCapacity.Value = 16; BucketSpeedPenalty.Value = 2f; BoilDurationSeconds.Value = 90f; CoolDurationSeconds.Value = 30f; break; case DifficultyLevel.Casual: ThirstDurationMinutes.Value = 35f; CleanHydrationAmount.Value = 20f; DirtyHydrationAmount.Value = 20f; DrinkDurationSeconds.Value = 4f; HydratedDuration.Value = 160f; SicknessDuration.Value = 160f; DirtyWaterSpeedPenalty.Value = 0.2f; WaterskinCapacity.Value = 5; HornCapacity.Value = 3; BucketCapacity.Value = 6; BarrelCapacity.Value = 12; BucketSpeedPenalty.Value = 3f; BoilDurationSeconds.Value = 150f; CoolDurationSeconds.Value = 60f; break; case DifficultyLevel.Hard: ThirstDurationMinutes.Value = 22f; CleanHydrationAmount.Value = 15f; DirtyHydrationAmount.Value = 12f; DrinkDurationSeconds.Value = 5f; HydratedDuration.Value = 100f; SicknessDuration.Value = 240f; DirtyWaterSpeedPenalty.Value = 0.3f; WaterskinCapacity.Value = 3; HornCapacity.Value = 2; BucketCapacity.Value = 5; BarrelCapacity.Value = 10; BucketSpeedPenalty.Value = 4f; BoilDurationSeconds.Value = 210f; CoolDurationSeconds.Value = 90f; break; } YourThirstyPlugin.Log.LogInfo((object)$"ConfigManager: Applied '{value}' difficulty preset."); } } internal class CoroutineHost : MonoBehaviour { private static CoroutineHost _instance; public static CoroutineHost Instance => EnsureExists(); public static CoroutineHost EnsureExists() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("YourThirsty_CoroutineHost"); Object.DontDestroyOnLoad((Object)(object)val); _instance = val.AddComponent<CoroutineHost>(); return _instance; } } internal static class DrinkSystem { [CompilerGenerated] private sealed class <ContainerDrinkRoutine>d__7 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Player player; public bool isDirty; private float <timer>5__1; private float <duration>5__2; private float <amount>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ContainerDrinkRoutine>d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; <>1__state = -3; <timer>5__1 = 0f; <duration>5__2 = ConfigManager.DrinkDurationSeconds.Value; if (isDirty) { SFXManager.PlayDirty(player); } else { SFXManager.PlayDrink(player); } break; case 1: <>1__state = -3; break; } if (<timer>5__1 < <duration>5__2) { if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsSwimming()) { goto IL_00c0; } Vector3 velocity = ((Character)player).GetVelocity(); if (((Vector3)(ref velocity)).magnitude > 2f) { goto IL_00c0; } <timer>5__1 += Time.deltaTime; UIManager.SetFillProgress(<timer>5__1 / <duration>5__2); <>2__current = null; <>1__state = 1; result = true; } else { UIManager.HideFillBar(); ItemManager.Drink(player); <amount>5__3 = (isDirty ? ConfigManager.DirtyHydrationAmount.Value : ConfigManager.CleanHydrationAmount.Value); ThirstSystem.ApplyDrink(<amount>5__3, isDirty); BuffManager.ApplyDrinkEffects(player, isDirty); if (isDirty) { UIManager.FlashDirtyWater(); } <>m__Finally1(); result = false; } goto end_IL_0000; IL_00c0: PlayerAnimationInjector.Stop(); UIManager.HideFillBar(); result = false; <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; _drinkRoutine = null; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <FillBucketRoutine>d__10 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Player player; public ItemData bucket; public bool isDirty; private float <timer>5__1; private float <fraction>5__2; private int <partialWater>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <FillBucketRoutine>d__10(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; <>1__state = -3; <timer>5__1 = 0f; SFXManager.PlayRefill(player); break; case 1: <>1__state = -3; break; } if (<timer>5__1 < 5f) { if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsSwimming()) { goto IL_0097; } Vector3 velocity = ((Character)player).GetVelocity(); if (((Vector3)(ref velocity)).magnitude > 2f) { goto IL_0097; } <timer>5__1 += Time.deltaTime; UIManager.SetFillProgress(<timer>5__1 / 5f); <>2__current = null; <>1__state = 1; result = true; } else { BucketItem.Fill(bucket, isDirty); UIManager.HideFillBar(); <>m__Finally1(); result = false; } goto end_IL_0000; IL_0097: <fraction>5__2 = <timer>5__1 / 5f; if (<fraction>5__2 > 0.05f) { <partialWater>5__3 = Mathf.Max(1, Mathf.FloorToInt((float)BucketItem.Capacity * <fraction>5__2)); BucketItem.SetWater(bucket, <partialWater>5__3); BucketItem.SetDirty(bucket, isDirty); } PlayerAnimationInjector.Stop(); UIManager.HideFillBar(); result = false; <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; _drinkRoutine = null; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <FillEquippedRoutine>d__8 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Player player; public bool isDirty; private float <timer>5__1; private float <fraction>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <FillEquippedRoutine>d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; <>1__state = -3; <timer>5__1 = 0f; SFXManager.PlayRefill(player); break; case 1: <>1__state = -3; break; } if (<timer>5__1 < 4f) { if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsSwimming()) { goto IL_0097; } Vector3 velocity = ((Character)player).GetVelocity(); if (((Vector3)(ref velocity)).magnitude > 2f) { goto IL_0097; } <timer>5__1 += Time.deltaTime; UIManager.SetFillProgress(<timer>5__1 / 4f); <>2__current = null; <>1__state = 1; result = true; } else { ItemManager.Fill(player, isDirty); UIManager.HideFillBar(); <>m__Finally1(); result = false; } goto end_IL_0000; IL_0097: <fraction>5__2 = <timer>5__1 / 4f; if (<fraction>5__2 > 0.05f) { ItemManager.PartialFill(player, isDirty, <fraction>5__2); } PlayerAnimationInjector.Stop(); UIManager.HideFillBar(); result = false; <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; _drinkRoutine = null; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <NaturalDrinkRoutine>d__6 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Player player; public bool isDirty; private float <timer>5__1; private float <duration>5__2; private float <amount>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <NaturalDrinkRoutine>d__6(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; <>1__state = -3; <timer>5__1 = 0f; <duration>5__2 = ConfigManager.DrinkDurationSeconds.Value; if (isDirty) { SFXManager.PlayDirty(player); } else { SFXManager.PlayDrink(player); } break; case 1: <>1__state = -3; break; } if (<timer>5__1 < <duration>5__2) { if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsSwimming()) { goto IL_00ce; } Vector3 velocity = ((Character)player).GetVelocity(); if (((Vector3)(ref velocity)).magnitude > 2f || !WaterSourceDetector.IsNearWater(player)) { goto IL_00ce; } <timer>5__1 += Time.deltaTime; UIManager.SetFillProgress(<timer>5__1 / <duration>5__2); <>2__current = null; <>1__state = 1; result = true; } else { UIManager.HideFillBar(); <amount>5__3 = (isDirty ? ConfigManager.DirtyHydrationAmount.Value : ConfigManager.CleanHydrationAmount.Value); ThirstSystem.ApplyDrink(<amount>5__3, isDirty); BuffManager.ApplyDrinkEffects(player, isDirty); if (isDirty) { UIManager.FlashDirtyWater(); } <>m__Finally1(); result = false; } goto end_IL_0000; IL_00ce: PlayerAnimationInjector.Stop(); UIManager.HideFillBar(); result = false; <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; _drinkRoutine = null; } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static Coroutine _drinkRoutine; public static bool IsBusy => _drinkRoutine != null; public static void TryDrink() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsSwimming() && _drinkRoutine == null) { if (!ItemManager.CanDrinkFromContainer(localPlayer, out var state)) { SFXManager.PlayEmpty(localPlayer); return; } bool isDirty = state.IsDirty; AnimationManager.PrepareDrink(localPlayer, PlayerAnimationInjector.DrinkAnimMode.ContainerDrink); _drinkRoutine = ((MonoBehaviour)CoroutineHost.Instance).StartCoroutine(ContainerDrinkRoutine(localPlayer, isDirty)); } } public static void TryDrinkNatural() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsSwimming() && _drinkRoutine == null && WaterSourceDetector.CanInteractWithWater(localPlayer)) { bool isDirty = WaterSourceDetector.IsDirtyWater(localPlayer); AnimationManager.PrepareDrink(localPlayer, PlayerAnimationInjector.DrinkAnimMode.NaturalWater); _drinkRoutine = ((MonoBehaviour)CoroutineHost.Instance).StartCoroutine(NaturalDrinkRoutine(localPlayer, isDirty)); } } public static void TryFillEquipped(bool isDirty) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && _drinkRoutine == null && ItemManager.CanFill(localPlayer, isDirty, out var _)) { AnimationManager.PrepareDrink(localPlayer, PlayerAnimationInjector.DrinkAnimMode.ContainerRefill); _drinkRoutine = ((MonoBehaviour)CoroutineHost.Instance).StartCoroutine(FillEquippedRoutine(localPlayer, isDirty)); } } [IteratorStateMachine(typeof(<NaturalDrinkRoutine>d__6))] private static IEnumerator NaturalDrinkRoutine(Player player, bool isDirty) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <NaturalDrinkRoutine>d__6(0) { player = player, isDirty = isDirty }; } [IteratorStateMachine(typeof(<ContainerDrinkRoutine>d__7))] private static IEnumerator ContainerDrinkRoutine(Player player, bool isDirty) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ContainerDrinkRoutine>d__7(0) { player = player, isDirty = isDirty }; } [IteratorStateMachine(typeof(<FillEquippedRoutine>d__8))] private static IEnumerator FillEquippedRoutine(Player player, bool isDirty) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <FillEquippedRoutine>d__8(0) { player = player, isDirty = isDirty }; } public static void TryFillBucket(bool isDirty) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && _drinkRoutine == null) { ItemData firstBucketInInventory = BucketItem.GetFirstBucketInInventory(localPlayer); if (firstBucketInInventory != null && !BucketItem.IsFull(firstBucketInInventory)) { AnimationManager.PrepareDrink(localPlayer, PlayerAnimationInjector.DrinkAnimMode.ContainerRefill); _drinkRoutine = ((MonoBehaviour)CoroutineHost.Instance).StartCoroutine(FillBucketRoutine(localPlayer, firstBucketInInventory, isDirty)); } } } [IteratorStateMachine(typeof(<FillBucketRoutine>d__10))] private static IEnumerator FillBucketRoutine(Player player, ItemData bucket, bool isDirty) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <FillBucketRoutine>d__10(0) { player = player, bucket = bucket, isDirty = isDirty }; } public static void TryDrinkFromWell() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && _drinkRoutine == null) { float value = ConfigManager.CleanHydrationAmount.Value; ThirstSystem.ApplyDrink(value, dirty: false); SFXManager.PlayDrink(localPlayer); } } } internal class GroundLock : MonoBehaviour { private float _groundY; private Rigidbody _rb; public void Lock() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) _groundY = ((Component)this).transform.position.y; _rb = ((Component)this).GetComponent<Rigidbody>(); } private void LateUpdate() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)this).transform.position; if (position.y > _groundY + 0.01f) { ((Component)this).transform.position = new Vector3(position.x, _groundY, position.z); } if ((Object)(object)_rb != (Object)null) { Vector3 linearVelocity = _rb.linearVelocity; if (linearVelocity.y > 0.01f) { _rb.linearVelocity = new Vector3(linearVelocity.x, 0f, linearVelocity.z); } } } public void Unlock() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)this).transform.position; if (Mathf.Abs(position.y - _groundY) > 0.01f) { ((Component)this).transform.position = new Vector3(position.x, _groundY, position.z); } Object.Destroy((Object)(object)this); } } [HarmonyPatch(typeof(Hud), "Update")] internal static class HudPatches { private static GameObject _thirstRoot; private static Text _thirstText; private static Image _thirstIcon; private static Sprite _spriteHydrated; private static Sprite _spriteThirsty; private static GameObject _hoverRoot; private static Text _hoverText; private static void Postfix(Hud __instance) { try { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { UpdateThirstHud(__instance); UpdateWaterHoverHud(__instance); UIManager.DrawFillBar(); } } catch (Exception ex) { YourThirstyPlugin.Log.LogError((object)("HudPatches error: " + ex.Message)); } } private static void UpdateThirstHud(Hud hud) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_thirstRoot == (Object)null) { CreateThirstHud(hud); } if ((Object)(object)_thirstRoot == (Object)null) { return; } float currentThirst = ThirstSystem.CurrentThirst; if ((Object)(object)_thirstText != (Object)null) { int num = Mathf.RoundToInt(100f - currentThirst); _thirstText.text = $"{num}%"; if (currentThirst >= 80f) { ((Graphic)_thirstText).color = Color.red; } else if (currentThirst >= 60f) { ((Graphic)_thirstText).color = Color.yellow; } else { ((Graphic)_thirstText).color = new Color(0.5f, 0.85f, 1f); } } if ((Object)(object)_thirstIcon != (Object)null) { _thirstIcon.sprite = ((currentThirst >= 60f) ? _spriteThirsty : _spriteHydrated); } } private static string GetContainerDrinkName(Player player) { if (ItemManager.CanDrinkFromContainer(player, out var state) && state != null && state.Item != null) { string text = state.Item.m_shared?.m_name; if (text == "YourThirsty_Waterskin") { return "Drink from Waterskin"; } if (text == "YourThirsty_Horn") { return "Drink from Horn"; } } return "Drink from Container"; } private static void UpdateWaterHoverHud(Hud hud) { if ((Object)(object)_hoverRoot == (Object)null) { CreateHoverHud(hud); } if ((Object)(object)_hoverRoot == (Object)null) { return; } bool flag = UIManager.IsShowingDrinkWater(); bool flag2 = UIManager.IsShowingRefillPrompt(); bool flag3 = UIManager.IsShowingDrinkContainer(); bool flag4 = UIManager.IsShowingEmptyContainer(); bool flag5 = UIManager.IsShowingFillBucket(); Player localPlayer = Player.m_localPlayer; if (flag || flag2 || flag3 || flag4 || flag5) { _hoverRoot.SetActive(true); if (!((Object)(object)_hoverText != (Object)null)) { return; } string drinkKeyName = YourThirstyPlugin.DrinkKeyName; string refillKeyName = YourThirstyPlugin.RefillKeyName; string text = ""; if (flag5) { text = text + "<color=yellow>[" + drinkKeyName + "]</color> Fill Bucket"; } if (flag) { text = text + "<color=yellow>[" + drinkKeyName + "]</color> Drink"; } if (flag2) { text = text + "<color=yellow>[" + drinkKeyName + "]</color> Refill"; } if (flag4 && !flag && !flag2) { text = text + "<color=yellow>[" + drinkKeyName + "]</color> Empty Container"; } if (flag3) { if (text.Length > 0) { text += "\n"; } text = text + "<color=yellow>[" + refillKeyName + "]</color> " + GetContainerDrinkName(localPlayer); } _hoverText.text = text; } else { _hoverRoot.SetActive(false); } } private static void CreateThirstHud(Hud hud) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Expected O, but got Unknown //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Expected O, but got Unknown //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) GameObject rootObject = hud.m_rootObject; if ((Object)(object)rootObject == (Object)null) { return; } if ((Object)(object)YourThirstyPlugin.AssetBundle != (Object)null) { Texture2D val = YourThirstyPlugin.AssetBundle.LoadAsset<Texture2D>("Hydrated"); if ((Object)(object)val != (Object)null) { _spriteHydrated = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), Vector2.one * 0.5f); } Texture2D val2 = YourThirstyPlugin.AssetBundle.LoadAsset<Texture2D>("Thirsty"); if ((Object)(object)val2 != (Object)null) { _spriteThirsty = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), Vector2.one * 0.5f); } } _thirstRoot = new GameObject("YT_ThirstHud"); _thirstRoot.transform.SetParent(rootObject.transform, false); RectTransform val3 = _thirstRoot.AddComponent<RectTransform>(); val3.anchorMin = new Vector2(0.85f, 0.02f); val3.anchorMax = new Vector2(0.95f, 0.07f); val3.offsetMin = Vector2.zero; val3.offsetMax = Vector2.zero; if ((Object)(object)_spriteHydrated != (Object)null) { GameObject val4 = new GameObject("Icon"); val4.transform.SetParent(_thirstRoot.transform, false); RectTransform val5 = val4.AddComponent<RectTransform>(); val5.anchorMin = new Vector2(0f, 0f); val5.anchorMax = new Vector2(0.3f, 1f); val5.offsetMin = Vector2.zero; val5.offsetMax = Vector2.zero; _thirstIcon = val4.AddComponent<Image>(); _thirstIcon.sprite = _spriteHydrated; _thirstIcon.preserveAspect = true; } GameObject val6 = new GameObject("Text"); val6.transform.SetParent(_thirstRoot.transform, false); RectTransform val7 = val6.AddComponent<RectTransform>(); val7.anchorMin = new Vector2(0.35f, 0f); val7.anchorMax = new Vector2(1f, 1f); val7.offsetMin = Vector2.zero; val7.offsetMax = Vector2.zero; _thirstText = val6.AddComponent<Text>(); _thirstText.font = GetDefaultFont(hud); _thirstText.fontSize = 16; ((Graphic)_thirstText).color = new Color(0.5f, 0.85f, 1f); _thirstText.text = "100%"; YourThirstyPlugin.Log.LogInfo((object)"HudPatches: Thirst HUD created."); } private static Font GetDefaultFont(Hud hud) { Text componentInChildren = hud.m_rootObject.GetComponentInChildren<Text>(true); if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.font != (Object)null) { return componentInChildren.font; } return Resources.GetBuiltinResource<Font>("Arial.ttf"); } private static void CreateHoverHud(Hud hud) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) GameObject rootObject = hud.m_rootObject; if (!((Object)(object)rootObject == (Object)null)) { _hoverRoot = new GameObject("YT_WaterHover"); _hoverRoot.transform.SetParent(rootObject.transform, false); _hoverRoot.SetActive(false); RectTransform val = _hoverRoot.AddComponent<RectTransform>(); val.anchorMin = new Vector2(0.35f, 0.38f); val.anchorMax = new Vector2(0.65f, 0.45f); val.offsetMin = Vector2.zero; val.offsetMax = Vector2.zero; _hoverText = _hoverRoot.AddComponent<Text>(); _hoverText.font = GetDefaultFont(hud); _hoverText.fontSize = 16; _hoverText.alignment = (TextAnchor)4; _hoverText.supportRichText = true; ((Graphic)_hoverText).color = Color.white; _hoverText.text = ""; Outline val2 = _hoverRoot.AddComponent<Outline>(); ((Shadow)val2).effectColor = Color.black; ((Shadow)val2).effectDistance = new Vector2(1f, -1f); YourThirstyPlugin.Log.LogInfo((object)"HudPatches: Water hover HUD created."); } } } internal static class InteractionManager { public static void Init() { } public static void Update() { } } internal class ItemGroundGuard : MonoBehaviour { private Rigidbody _rb; private bool _physicsStarted; private float _settledTimer; private const float SettleTime = 6f; private const float MinHeightAboveTerrain = 0.1f; private void Awake() { _rb = ((Component)this).GetComponent<Rigidbody>(); } private void FixedUpdate() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_rb == (Object)null) { Object.Destroy((Object)(object)this); } else if (!_rb.isKinematic) { if (!_physicsStarted) { _physicsStarted = true; _rb.collisionDetectionMode = (CollisionDetectionMode)2; } Vector3 position = ((Component)this).transform.position; float num = 0f; if ((Object)(object)ZoneSystem.instance != (Object)null) { ZoneSystem.instance.GetGroundHeight(position, ref num); } float num2 = num + 0.1f; if (position.y < num2) { position.y = num2; ((Component)this).transform.position = position; _rb.linearVelocity = Vector3.zero; _rb.angularVelocity = Vector3.zero; } Vector3 linearVelocity = _rb.linearVelocity; if (((Vector3)(ref linearVelocity)).sqrMagnitude < 0.01f) { _settledTimer += Time.fixedDeltaTime; } else { _settledTimer = 0f; } if (_settledTimer > 6f) { Object.Destroy((Object)(object)this); } } } } internal static class ItemManager { private const string Key_Amount = "YT_Amount"; private const string Key_Dirty = "YT_Dirty"; private static readonly FieldRef<Humanoid, ItemData> _rightItemRef = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_rightItem"); public static void Init() { YourThirstyPlugin.Log.LogInfo((object)"ItemManager initialized."); } public static bool HasContainer(Player player) { return GetContainer(player) != null; } public static bool CanFill(Player player, bool dirty, out ContainerState state) { state = GetEquippedContainer(player); return state != null && state.CurrentAmount < state.MaxAmount; } public static bool CanDrinkFromContainer(Player player, out ContainerState state) { state = GetEquippedContainer(player); return state != null && state.CurrentAmount > 0; } public static bool CanDrinkFromInventory(Player player, out ContainerState state) { state = GetInventoryContainer(player); return state != null && state.CurrentAmount > 0; } public static bool CanFillFromInventory(Player player, bool dirty, out ContainerState state) { state = GetInventoryContainer(player); return state != null && state.CurrentAmount < state.MaxAmount; } public static void DrinkFromInventory(Player player) { ContainerState inventoryContainer = GetInventoryContainer(player); if (inventoryContainer == null || inventoryContainer.CurrentAmount <= 0) { SFXManager.PlayEmpty(player); return; } inventoryContainer.CurrentAmount--; SaveState(player, inventoryContainer); SyncManager.SyncContainer(player, inventoryContainer.CurrentAmount, inventoryContainer.IsDirty); } public static void FillFromInventory(Player player, bool dirty) { ContainerState inventoryContainer = GetInventoryContainer(player); if (inventoryContainer != null) { inventoryContainer.CurrentAmount = inventoryContainer.MaxAmount; inventoryContainer.IsDirty = dirty; SaveState(player, inventoryContainer); SyncManager.SyncContainer(player, inventoryContainer.CurrentAmount, inventoryContainer.IsDirty); } } private static ContainerState GetEquippedContainer(Player player) { return FindContainer(player, equippedOnly: true); } private static ContainerState GetInventoryContainer(Player player) { return FindContainer(player, equippedOnly: false); } private static ContainerState FindContainer(Player player, bool equippedOnly) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return null; } ItemData val = (equippedOnly ? _rightItemRef.Invoke((Humanoid)(object)player) : null); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem == null || allItem.m_shared == null || (equippedOnly && (!allItem.m_equipped || allItem != val))) { continue; } int value; if (allItem.m_shared.m_name == "YourThirsty_Waterskin") { value = ConfigManager.WaterskinCapacity.Value; } else { if (!(allItem.m_shared.m_name == "YourThirsty_Horn")) { continue; } value = ConfigManager.HornCapacity.Value; } Dictionary<string, string> customData = allItem.m_customData; int result = 0; bool isDirty = false; if (customData != null) { if (customData.TryGetValue("YT_Amount", out var value2)) { int.TryParse(value2, out result); } if (customData.TryGetValue("YT_Dirty", out var value3)) { isDirty = value3 == "1"; } } return new ContainerState(allItem, value) { CurrentAmount = result, IsDirty = isDirty }; } return null; } public static void Fill(Player player, bool dirty) { ContainerState equippedContainer = GetEquippedContainer(player); if (equippedContainer != null) { equippedContainer.CurrentAmount = equippedContainer.MaxAmount; equippedContainer.IsDirty = dirty; SaveState(player, equippedContainer); SyncManager.SyncContainer(player, equippedContainer.CurrentAmount, equippedContainer.IsDirty); } } public static void PartialFill(Player player, bool dirty, float fraction) { ContainerState equippedContainer = GetEquippedContainer(player); if (equippedContainer != null) { int num = equippedContainer.MaxAmount - equippedContainer.CurrentAmount; int num2 = Mathf.Max(1, Mathf.FloorToInt((float)num * fraction)); equippedContainer.CurrentAmount = Mathf.Min(equippedContainer.CurrentAmount + num2, equippedContainer.MaxAmount); equippedContainer.IsDirty = dirty; SaveState(player, equippedContainer); SyncManager.SyncContainer(player, equippedContainer.CurrentAmount, equippedContainer.IsDirty); } } public static void Drink(Player player) { ContainerState equippedContainer = GetEquippedContainer(player); if (equippedContainer == null || equippedContainer.CurrentAmount <= 0) { SFXManager.PlayEmpty(player); return; } equippedContainer.CurrentAmount--; SaveState(player, equippedContainer); SyncManager.SyncContainer(player, equippedContainer.CurrentAmount, equippedContainer.IsDirty); } public static bool CanEmptyContainer(Player player, out ContainerState state) { state = GetEquippedContainer(player); return state != null && state.CurrentAmount > 0; } public static void EmptyContainer(Player player) { ContainerState equippedContainer = GetEquippedContainer(player); if (equippedContainer != null && equippedContainer.CurrentAmount > 0) { equippedContainer.CurrentAmount = 0; equippedContainer.IsDirty = false; SaveState(player, equippedContainer); SyncManager.SyncContainer(player, 0, dirty: false); } } private static ContainerState GetContainer(Player player) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return null; } foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem == null || allItem.m_shared == null) { continue; } int value; if (allItem.m_shared.m_name == "YourThirsty_Waterskin") { value = ConfigManager.WaterskinCapacity.Value; } else { if (!(allItem.m_shared.m_name == "YourThirsty_Horn")) { continue; } value = ConfigManager.HornCapacity.Value; } Dictionary<string, string> customData = allItem.m_customData; int result = 0; bool isDirty = false; if (customData != null) { if (customData.TryGetValue("YT_Amount", out var value2)) { int.TryParse(value2, out result); } if (customData.TryGetValue("YT_Dirty", out var value3)) { isDirty = value3 == "1"; } } return new ContainerState(allItem, value) { CurrentAmount = result, IsDirty = isDirty }; } return null; } private static void SaveState(Player player, ContainerState state) { if (state != null && state.Item != null) { if (state.Item.m_customData == null) { state.Item.m_customData = new Dictionary<string, string>(); } state.Item.m_customData["YT_Amount"] = state.CurrentAmount.ToString(); state.Item.m_customData["YT_Dirty"] = (state.IsDirty ? "1" : "0"); } } public static void SaveContainerState(Player player, ContainerState state) { SaveState(player, state); SyncManager.SyncContainer(player, state.CurrentAmount, state.IsDirty); } public static string GetContainerDisplayName(Player player) { ContainerState equippedContainer = GetEquippedContainer(player); if (equippedContainer?.Item?.m_shared != null) { if (equippedContainer.Item.m_shared.m_name == "YourThirsty_Waterskin") { return "Waterskin"; } if (equippedContainer.Item.m_shared.m_name == "YourThirsty_Horn") { return "Horn"; } } return "Container"; } } internal class ContainerState { public ItemData Item; public int CurrentAmount; public int MaxAmount; public bool IsDirty; public ContainerState(ItemData item, int max) { Item = item; MaxAmount = max; CurrentAmount = 0; IsDirty = false; } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDB_Patch { [HarmonyPostfix] private static void Postfix() { if (!((Object)(object)ObjectDB.instance == (Object)null) && ObjectDB.instance.m_items.Count != 0 && !((Object)(object)ZNetScene.instance == (Object)null)) { PrefabLoader.FixAllShaders(); ItemRegistration.Init(); } } } internal static class ItemRegistration { private struct PieceReqSpec { public GameObject Prefab; public (string, int)[] Requirements; } private const string WorkbenchPiecePrefabName = "piece_workbench"; private const string HammerItemPrefabName = "Hammer"; private static ObjectDB _lastRegisteredDB; private static bool _piecesValid; private static Dictionary<int, GameObject> _namedPrefabs; private static Dictionary<int, GameObject> _itemByHash; private static readonly List<PieceReqSpec> _pieceReqSpecs = new List<PieceReqSpec>(); private const string CauldronPiecePrefabName = "piece_cauldron"; public static void Init() { if ((Object)(object)ObjectDB.instance == (Object)null) { YourThirstyPlugin.Log.LogError((object)"ItemRegistration: ObjectDB not ready."); return; } if ((Object)(object)_lastRegisteredDB == (Object)(object)ObjectDB.instance && _piecesValid) { YourThirstyPlugin.Log.LogInfo((object)"ItemRegistration: Already registered for this ObjectDB instance."); return; } _lastRegisteredDB = ObjectDB.instance; _namedPrefabs = null; _itemByHash = null; RegisterToZNetScene(PrefabLoader.Waterskin); RegisterToZNetScene(PrefabLoader.Horn); RegisterToZNetScene(PrefabLoader.Bucket); RegisterToZNetScene(PrefabLoader.Barrel); RegisterToZNetScene(PrefabLoader.Well); RegisterToZNetScene(PrefabLoader.BoilingBucket); RegisterItem(PrefabLoader.Waterskin); RegisterItem(PrefabLoader.Horn); RegisterItem(PrefabLoader.Bucket); RegisterItem(PrefabLoader.BoilWaterItem); RegisterItem(PrefabLoader.CollectBoiledWaterItem); RegisterRecipes(); RegisterCauldronRecipes(); RegisterBuildPieces(); _piecesValid = ValidatePieceRequirements(); YourThirstyPlugin.Log.LogInfo((object)$"ItemRegistration: Complete. PiecesValid={_piecesValid}"); } private static void RegisterToZNetScene(GameObject prefab) { if ((Object)(object)prefab == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return; } if ((Object)(object)prefab.GetComponent<ZNetView>() == (Object)null) { YourThirstyPlugin.Log.LogWarning((object)("Skipping ZNetScene for '" + ((Object)prefab).name + "' — no ZNetView")); return; } if (!ZNetScene.instance.m_prefabs.Contains(prefab)) { ZNetScene.instance.m_prefabs.Add(prefab); } if (_namedPrefabs == null) { _namedPrefabs = AccessTools.Field(typeof(ZNetScene), "m_namedPrefabs")?.GetValue(ZNetScene.instance) as Dictionary<int, GameObject>; } if (_namedPrefabs == null) { YourThirstyPlugin.Log.LogWarning((object)("Cannot access m_namedPrefabs — spawn/placement may not work for '" + ((Object)prefab).name + "'")); return; } int stableHashCode = GetStableHashCode(((Object)prefab).name); if (!_namedPrefabs.ContainsKey(stableHashCode)) { _namedPrefabs[stableHashCode] = prefab; YourThirstyPlugin.Log.LogInfo((object)$"Registered ZNetScene prefab: {((Object)prefab).name} (hash {stableHashCode})"); } } private static int GetStableHashCode(string s) { int num = 5381; int num2 = num; for (int i = 0; i < s.Length; i += 2) { num = ((num << 5) + num) ^ s[i]; if (i + 1 < s.Length) { num2 = ((num2 << 5) + num2) ^ s[i + 1]; } } return num + num2 * 1566083941; } private static void RegisterItem(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return; } ItemDrop component = prefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { YourThirstyPlugin.Log.LogWarning((object)("Skipping item '" + ((Object)prefab).name + "' — missing ItemDrop or SharedData")); return; } if (!ObjectDB.instance.m_items.Contains(prefab)) { ObjectDB.instance.m_items.Add(prefab); YourThirstyPlugin.Log.LogInfo((object)("Registered item: " + ((Object)prefab).name)); } try { GameObject dropPrefab = component.m_itemData.m_dropPrefab; YourThirstyPlugin.Log.LogInfo((object)("ItemRegistration: '" + ((Object)prefab).name + "' dropPrefab = " + (((Object)(object)dropPrefab != (Object)null) ? ((Object)dropPrefab).name : "null"))); if ((Object)(object)dropPrefab != (Object)null) { Rigidbody component2 = dropPrefab.GetComponent<Rigidbody>(); Collider component3 = dropPrefab.GetComponent<Collider>(); YourThirstyPlugin.Log.LogInfo((object)("ItemRegistration: dropPrefab Rigidbody isKinematic=" + (((component2 != null) ? component2.isKinematic.ToString() : null) ?? "null") + ", useGravity=" + (((component2 != null) ? component2.useGravity.ToString() : null) ?? "null"))); YourThirstyPlugin.Log.LogInfo((object)("ItemRegistration: dropPrefab Collider present=" + ((Object)(object)component3 != (Object)null))); } } catch (Exception ex) { YourThirstyPlugin.Log.LogWarning((object)("ItemRegistration: Failed to inspect drop prefab for '" + ((Object)prefab).name + "': " + ex.Message)); } if (_itemByHash == null) { _itemByHash = AccessTools.Field(typeof(ObjectDB), "m_itemByHash")?.GetValue(ObjectDB.instance) as Dictionary<int, GameObject>; } if (_itemByHash != null) { int stableHashCode = GetStableHashCode(((Object)prefab).name); _itemByHash[stableHashCode] = prefab; YourThirstyPlugin.Log.LogInfo((object)$"Registered item hash: {((Object)prefab).name} (hash {stableHashCode})"); } else { YourThirstyPlugin.Log.LogWarning((object)("Cannot access m_itemByHash — dropping may not work for '" + ((Object)prefab).name + "'")); } } private static void RegisterRecipes() { AddRecipe("Waterskin", new(string, int)[2] { ("DeerHide", 4), ("LeatherScraps", 3) }); AddRecipe("Horn", new(string, int)[1] { ("TrophyBoar", 3) }); AddRecipe("WaterBucket", new(string, int)[1] { ("Wood", 5) }); } private static void RegisterCauldronRecipes() { CraftingStation val = FindCraftingStation("piece_cauldron"); if ((Object)(object)val == (Object)null) { YourThirstyPlugin.Log.LogWarning((object)"RegisterCauldronRecipes: Cauldron CraftingStation not found. Boil recipes will NOT be available."); return; } AddCauldronRecipe(PrefabLoader.BoilWaterItem, "Recipe_BoilWater", val); AddCauldronRecipe(PrefabLoader.CollectBoiledWaterItem, "Recipe_CollectBoiledWater", val); } private static void AddCauldronRecipe(GameObject dummyItem, string recipeName, CraftingStation station) { if ((Object)(object)dummyItem == (Object)null) { return; } foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if ((Object)(object)recipe != (Object)null && ((Object)recipe).name == recipeName) { YourThirstyPlugin.Log.LogInfo((object)("Cauldron recipe already registered: " + recipeName)); return; } } ItemDrop component = dummyItem.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null)) { Recipe val = ScriptableObject.CreateInstance<Recipe>(); ((Object)val).name = recipeName; val.m_item = component; val.m_craftingStation = station; val.m_amount = 1; val.m_enabled = true; val.m_resources = (Requirement[])(object)new Requirement[0]; ObjectDB.instance.m_recipes.Add(val); YourThirstyPlugin.Log.LogInfo((object)("Registered cauldron recipe: " + recipeName)); } } private static void RegisterBuildPieces() { CraftingStation val = FindCraftingStation("piece_workbench"); if ((Object)(object)val == (Object)null) { YourThirstyPlugin.Log.LogWarning((object)"RegisterBuildPieces: Workbench not found — pieces will be buildable anywhere!"); } AddPiece(PrefabLoader.Barrel, (PieceCategory)0, "BarreICON", new(string, int)[1] { ("Wood", 10) }, val); AddPiece(PrefabLoader.Well, (PieceCategory)0, "WellICON", new(string, int)[2] { ("Stone", 20), ("Wood", 10) }, val); AddPieceToHammer(PrefabLoader.BoilingBucket); if ((Object)(object)PrefabLoader.BoilingBucket != (Object)null && (Object)(object)val != (Object)null) { Piece component = PrefabLoader.BoilingBucket.GetComponent<Piece>(); if ((Object)(object)component != (Object)null) { component.m_craftingStation = val; } } } private static void AddRecipe(string itemName, (string, int)[] reqs) { foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if ((Object)(object)recipe != (Object)null && ((Object)recipe).name == "Recipe_" + itemName) { YourThirstyPlugin.Log.LogInfo((object)("Recipe already registered: " + itemName)); return; } } GameObject val = FindItemPrefab(itemName); if ((Object)(object)val == (Object)null) { YourThirstyPlugin.Log.LogError((object)("AddRecipe: Item '" + itemName + "' not found in ObjectDB.m_items.")); return; } ItemDrop component = val.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { YourThirstyPlugin.Log.LogError((object)("AddRecipe: '" + itemName + "' has no ItemDrop.")); return; } CraftingStation val2 = FindCraftingStation("piece_workbench"); if ((Object)(object)val2 == (Object)null) { YourThirstyPlugin.Log.LogError((object)"AddRecipe: CraftingStation 'piece_workbench' not found."); return; } Recipe val3 = ScriptableObject.CreateInstance<Recipe>(); ((Object)val3).name = "Recipe_" + itemName; val3.m_item = component; val3.m_craftingStation = val2; val3.m_amount = 1; val3.m_enabled = true; val3.m_resources = BuildReqs(reqs); ObjectDB.instance.m_recipes.Add(val3); YourThirstyPlugin.Log.LogInfo((object)("Registered recipe: " + itemName)); } private static CraftingStation FindCraftingStation(string piecePrefabName) { if ((Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab(piecePrefabName); if ((Object)(object)prefab != (Object)null) { CraftingStation component = prefab.GetComponent<CraftingStation>(); if ((Object)(object)component != (Object)null) { return component; } } foreach (GameObject prefab2 in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab2 != (Object)null && ((Object)prefab2).name == piecePrefabName) { CraftingStation component2 = prefab2.GetComponent<CraftingStation>(); if ((Object)(object)component2 != (Object)null) { YourThirstyPlugin.Log.LogInfo((object)("FindCraftingStation: Found '" + piecePrefabName + "' via prefab list fallback.")); return component2; } } } } GameObject val = FindItemPrefab(piecePrefabName); return ((Object)(object)val != (Object)null) ? val.GetComponent<CraftingStation>() : null; } private static void AddPiece(GameObject prefab, PieceCategory category, string iconName, (string, int)[] reqs, CraftingStation station = null) { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null) { YourThirstyPlugin.Log.LogError((object)"AddPiece: Prefab is null."); return; } bool flag = false; for (int i = 0; i < _pieceReqSpecs.Count; i++) { if ((Object)(object)_pieceReqSpecs[i].Prefab == (Object)(object)prefab) { _pieceReqSpecs[i] = new PieceReqSpec { Prefab = prefab, Requirements = reqs }; flag = true; break; } } if (!flag) { _pieceReqSpecs.Add(new PieceReqSpec { Prefab = prefab, Requirements = reqs }); } Piece val = prefab.GetComponent<Piece>(); if ((Object)(object)val == (Object)null) { val = prefab.AddComponent<Piece>(); val.m_name = ((Object)prefab).name; val.m_description = ""; YourThirstyPlugin.Log.LogInfo((object)("AddPiece: Added Piece component to '" + ((Object)prefab).name + "'.")); } val.m_category = category; val.m_craftingStation = station; val.m_groundPiece = false; val.m_groundOnly = false; Requirement[] array = (val.m_resources = BuildReqs(reqs)); if (array.Length == 0 && reqs.Length != 0) { YourThirstyPlugin.Log.LogError((object)($"AddPiece: '{((Object)prefab).name}' has 0 requirements (expected {reqs.Length}) — " + "vanilla items not found! Piece will be FREE to build.")); } else { YourThirstyPlugin.Log.LogInfo((object)($"AddPiece: '{((Object)prefab).name}' — {array.Length} requirements, " + "station=" + (((station != null) ? ((Object)station).name : null) ?? "NONE"))); } if ((Object)(object)YourThirstyPlugin.AssetBundle != (Object)null && !string.IsNullOrEmpty(iconName)) { Texture2D val2 = YourThirstyPlugin.AssetBundle.LoadAsset<Texture2D>(iconName); if ((Object)(object)val2 != (Object)null) { val.m_icon = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), Vector2.one * 0.5f); } else { YourThirstyPlugin.Log.LogWarning((object)("AddPiece: Icon '" + iconName + "' not found for '" + ((Object)prefab).name + "'")); } } GameObject val3 = FindItemPrefab("Hammer"); if ((Object)(object)val3 == (Object)null) { YourThirstyPlugin.Log.LogError((object)"AddPiece: Hammer prefab not found."); return; } ItemDrop component = val3.GetComponent<ItemDrop>(); PieceTable val4 = (((Object)(object)component != (Object)null) ? component.m_itemData.m_shared.m_buildPieces : null); if ((Object)(object)val4 == (Object)null) { YourThirstyPlugin.Log.LogError((object)"AddPiece: Hammer has no build piece table."); } else if (!val4.m_pieces.Contains(prefab)) { val4.m_pieces.Add(prefab); YourThirstyPlugin.Log.LogInfo((object)("Registered build piece: " + ((Object)prefab).name)); } } private static GameObject FindItemPrefab(string name) { if ((Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab(name); if ((Object)(object)prefab != (Object)null) { return prefab; } } if ((Object)(object)ObjectDB.instance != (Object)null) { List<GameObject> items = ObjectDB.instance.m_items; for (int i = 0; i < items.Count; i++) { GameObject val = items[i]; if ((Object)(object)val != (Object)null && ((Object)val).name == name) { return val; } } } if ((Object)(object)ObjectDB.instance != (Object)null) { try { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(name); if ((Object)(object)itemPrefab != (Object)null) { YourThirstyPlugin.Log.LogInfo((object)("FindItemPrefab: Found '" + name + "' via ObjectDB.GetItemPrefab.")); return itemPrefab; } } catch { } } if ((Object)(object)ZNetScene.instance != (Object)null) { string text = name.ToLowerInvariant(); foreach (GameObject prefab2 in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab2 != (Object)null && ((Object)prefab2).name.ToLowerInvariant() == text) { YourThirstyPlugin.Log.LogInfo((object)("FindItemPrefab: Found '" + name + "' via case-insensitive match '" + ((Object)prefab2).name + "'.")); return prefab2; } } } if ((Object)(object)ZNetScene.instance != (Object)null) { foreach (GameObject prefab3 in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab3 != (Object)null && ((Object)prefab3).name == name) { YourThirstyPlugin.Log.LogInfo((object)("FindItemPrefab: Found '" + name + "' via brute-force prefab list (hash may be stale).")); return prefab3; } } } YourThirstyPlugin.Log.LogWarning((object)("FindItemPrefab: '" + name + "' not found in ZNetScene or ObjectDB.")); return null; } private static void AddPieceToHammer(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return; } GameObject val = FindItemPrefab("Hammer"); if ((Object)(object)val == (Object)null) { YourThirstyPlugin.Log.LogError((object)"AddPieceToHammer: Hammer prefab not found."); return; } ItemDrop component = val.GetComponent<ItemDrop>(); PieceTable val2 = (((Object)(object)component != (Object)null) ? component.m_itemData.m_shared.m_buildPieces : null); if ((Object)(object)val2 == (Object)null) { YourThirstyPlugin.Log.LogError((object)"AddPieceToHammer: Hammer has no build piece table."); } else if (!val2.m_pieces.Contains(prefab)) { val2.m_pieces.Add(prefab); YourThirstyPlugin.Log.LogInfo((object)("Registered build piece (pre-configured): " + ((Object)prefab).name)); } } private static Requirement[] BuildReqs((string, int)[] reqs) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown List<Requirement> list = new List<Requirement>(); for (int i = 0; i < reqs.Length; i++) { (string, int) tuple = reqs[i]; string item = tuple.Item1; int item2 = tuple.Item2; GameObject val = FindItemPrefab(item); if ((Object)(object)val == (Object)null) { YourThirstyPlugin.Log.LogError((object)("BuildReqs: Missing item '" + item + "' — piece will be free of this resource!")); continue; } ItemDrop component = val.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { YourThirstyPlugin.Log.LogError((object)("BuildReqs: '" + item + "' (prefab '" + ((Object)val).name + "') has no ItemDrop — skipping.")); } else { list.Add(new Requirement { m_resItem = component, m_amount = item2, m_recover = true }); YourThirstyPlugin.Log.LogInfo((object)$"BuildReqs: Added requirement '{item}' x{item2} (prefab '{((Object)val).name}')"); } } return list.ToArray(); } private static bool ValidatePieceRequirements() { bool allValid = true; Check(PrefabLoader.Barrel, 1); Check(PrefabLoader.Well, 2); if (!allValid) { YourThirstyPlugin.Log.LogWarning((object)"ValidatePieceReqs: Stale/missing requirements detected — refreshing from current ZNetScene."); RefreshPieceRequirements(); } return allValid; void Check(GameObject prefab, int expectedReqs) { if (!((Object)(object)prefab == (Object)null)) { Piece component = prefab.GetComponent<Piece>(); if ((Object)(object)component == (Object)null) { YourThirstyPlugin.Log.LogError((object)("ValidatePieceReqs: '" + ((Object)prefab).name + "' has no Piece component!")); allValid = false; } else { Requirement[] resources = component.m_resources; int num = ((resources != null) ? resources.Length : 0); if (num < expectedReqs) { YourThirstyPlugin.Log.LogError((object)($"ValidatePieceReqs: '{((Object)prefab).name}' has {num}/{expectedReqs} requirements — " + "materials will be missing! Will retry on next Init.")); allValid = false; } else { for (int i = 0; i < component.m_resources.Length; i++) { Requirement val = component.m_resources[i]; if ((Object)(object)val.m_resItem == (Object)null) { YourThirstyPlugin.Log.LogError((object)$"ValidatePieceReqs: '{((Object)prefab).name}' requirement[{i}] has null/destroyed m_resItem!"); allValid = false; } else { YourThirstyPlugin.Log.LogInfo((object)string.Format("ValidatePieceReqs: '{0}' req[{1}] = '{2}' x{3} ✓", ((Object)prefab).name, i, val.m_resItem.m_itemData?.m_shared?.m_name ?? "?", val.m_amount)); } } } } } } } public static void RefreshPieceRequirements() { if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { YourThirstyPlugin.Log.LogWarning((object)"RefreshPieceRequirements: ZNetScene/ObjectDB not ready."); return; } int num = 0; foreach (PieceReqSpec pieceReqSpec in _pieceReqSpecs) { if ((Object)(object)pieceReqSpec.Prefab == (Object)null) { continue; } Piece component = pieceReqSpec.Prefab.GetComponent<Piece>(); if (!((Object)(object)component == (Object)null)) { Requirement[] array = BuildReqs(pieceReqSpec.Requirements); if (array.Length >= pieceReqSpec.Requirements.Length) {