Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of DynamicStorage Mead Hall v1.0.2
plugins/DynamicStorageMeadHall/DynamicStorageMeadHall.dll
Decompiled 2 months ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Extensions; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("DynamicStorageMeadHall")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: AssemblyInformationalVersion("1.0.2")] [assembly: AssemblyProduct("DynamicStorageMeadHall")] [assembly: AssemblyTitle("DynamicStorageMeadHall")] [assembly: AssemblyVersion("1.0.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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; } } } namespace DynamicStorageMeadHall { internal sealed class ConfigurationManagerAttributes { public int? Order; public bool? IsAdminOnly; public bool? ReadOnly; public bool? Browsable; public bool? IsAdvanced; } [BepInPlugin("com.muji.DynamicStorageMeadHall", "DynamicStorage Mead Hall", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal sealed class DynamicStorageMeadHallPlugin : BaseUnityPlugin { public const string PluginGuid = "com.muji.DynamicStorageMeadHall"; public const string PluginName = "DynamicStorage Mead Hall"; public const string PluginVersion = "1.0.2"; public const string KegPrefabName = "DSMK_MeadKeg"; private ConfigEntry<int> kegSlots; private ConfigEntry<int> quickGrabAmount; private ConfigEntry<KeyboardShortcut> quickGrabShortcut; private ConfigEntry<float> kegScale; private ConfigEntry<float> kegHeightOffset; private ConfigEntry<bool> enableIdleEffect; private ConfigEntry<string> barrelLabel; private ConfigEntry<bool> loopTapEffectPreview; private ConfigEntry<float> tapX; private ConfigEntry<float> tapY; private ConfigEntry<float> tapZ; private ConfigEntry<float> tapScale; private ConfigEntry<float> labelX; private ConfigEntry<float> labelY; private ConfigEntry<float> labelZ; private ConfigEntry<float> labelRotX; private ConfigEntry<float> labelRotY; private ConfigEntry<float> labelRotZ; private ConfigEntry<float> labelScale; private ConfigEntry<bool> labelFlipX; private ConfigEntry<bool> labelFlipY; private ConfigEntry<float> idleX; private ConfigEntry<float> idleY; private ConfigEntry<float> idleZ; private ConfigEntry<float> idleScale; private ConfigEntry<bool> showStorageDebugTooltip; private Harmony harmony; private GameObject registeredKegPrefab; private Vector3 kegBaseScale = Vector3.one; internal static DynamicStorageMeadHallPlugin Current { get; private set; } internal int KegStorageCapacity => NormalizeStorageCapacity((kegSlots != null) ? kegSlots.Value : 20); internal int QuickGrabAmount => Mathf.Clamp((quickGrabAmount != null) ? quickGrabAmount.Value : 5, 1, 50); internal KeyboardShortcut QuickGrabShortcut { get { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (quickGrabShortcut == null) { return new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }); } return quickGrabShortcut.Value; } } internal float KegScale => Mathf.Clamp((kegScale != null) ? kegScale.Value : 1f, 0.5f, 3f); internal float KegHeightOffset => Mathf.Clamp((kegHeightOffset != null) ? kegHeightOffset.Value : 0f, -2f, 2f); internal string LabelAtlasPath => Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? string.Empty, "Assets", "labels_atlas.png"); internal bool EnableIdleEffect { get { if (enableIdleEffect != null) { return enableIdleEffect.Value; } return false; } } internal string BarrelLabelTemplateMode => MeadCatalog.LabelModeFromConfig((barrelLabel != null) ? barrelLabel.Value : "Dynamic"); internal bool LoopTapEffectPreview { get { if (loopTapEffectPreview != null) { return loopTapEffectPreview.Value; } return false; } } internal float TapEffectLocalX { get { if (tapX == null) { return 0f; } return tapX.Value; } } internal float TapEffectLocalY { get { if (tapY == null) { return 0f; } return tapY.Value; } } internal float TapEffectLocalZ { get { if (tapZ == null) { return 0f; } return tapZ.Value; } } internal float TapEffectScaleMultiplier { get { if (tapScale == null) { return 1f; } return Mathf.Clamp(tapScale.Value, 0.1f, 2f); } } internal float LabelLocalX { get { if (labelX == null) { return 0f; } return labelX.Value; } } internal float LabelLocalY { get { if (labelY == null) { return 0.01f; } return labelY.Value; } } internal float LabelLocalZ { get { if (labelZ == null) { return -0.3f; } return labelZ.Value; } } internal float LabelRotationX { get { if (labelRotX == null) { return 0f; } return labelRotX.Value; } } internal float LabelRotationY { get { if (labelRotY == null) { return 0f; } return labelRotY.Value; } } internal float LabelRotationZ { get { if (labelRotZ == null) { return 0f; } return labelRotZ.Value; } } internal float LabelScale { get { if (labelScale == null) { return 0.95f; } return Mathf.Clamp(labelScale.Value, 0.1f, 2f); } } internal bool LabelFlipX { get { if (labelFlipX != null) { return labelFlipX.Value; } return false; } } internal bool LabelFlipY { get { if (labelFlipY != null) { return labelFlipY.Value; } return false; } } internal float IdleEffectLocalX { get { if (idleX == null) { return 0f; } return idleX.Value; } } internal float IdleEffectLocalY { get { if (idleY == null) { return 0f; } return idleY.Value; } } internal float IdleEffectLocalZ { get { if (idleZ == null) { return 0f; } return idleZ.Value; } } internal float IdleEffectScale { get { if (idleScale == null) { return 0.2f; } return Mathf.Clamp(idleScale.Value, 0.02f, 1f); } } internal bool ShowStorageDebugTooltip { get { if (showStorageDebugTooltip != null) { return showStorageDebugTooltip.Value; } return false; } } private void Awake() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown Current = this; BindConfig(); harmony = new Harmony("com.muji.DynamicStorageMeadHall"); MeadKegInventoryPatches.Install(harmony); MeadKegCapacityPatches.Install(harmony); MeadKegInteractionPatches.Install(harmony); MeadKegPlacementGhostPatches.Install(harmony); MeadKegAppearancePlacementPatches.Install(harmony); PrefabManager.OnPrefabsRegistered += OnPrefabsRegistered; ((BaseUnityPlugin)this).Logger.LogInfo((object)"DynamicStorage Mead Hall 1.0.2 loaded."); } private void OnDestroy() { MeadKegEffects.StopTapPreview(); PrefabManager.OnPrefabsRegistered -= OnPrefabsRegistered; if (harmony != null) { try { harmony.UnpatchSelf(); } catch { } } if ((Object)(object)Current == (Object)(object)this) { Current = null; } } private void Update() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 MeadKegEffects.UpdateTapPreview(LoopTapEffectPreview); KeyboardShortcut val = QuickGrabShortcut; if ((int)((KeyboardShortcut)(ref val)).MainKey == 0 || (int)((KeyboardShortcut)(ref val)).MainKey == 101 || !((KeyboardShortcut)(ref val)).IsDown()) { return; } Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer) && !InventoryGui.IsVisible() && !Menu.IsVisible() && !TextInput.IsVisible() && !Console.IsVisible()) { GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject(); MeadKegStorage meadKegStorage = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<MeadKegStorage>() : null); if (Object.op_Implicit((Object)(object)meadKegStorage)) { meadKegStorage.TryQuickGrab(localPlayer, QuickGrabAmount); } } } private void BindConfig() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Expected O, but got Unknown //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Expected O, but got Unknown //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Expected O, but got Unknown //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Expected O, but got Unknown //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Expected O, but got Unknown //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Expected O, but got Unknown //IL_048d: Unknown result type (might be due to invalid IL or missing references) //IL_0497: Expected O, but got Unknown //IL_0563: Unknown result type (might be due to invalid IL or missing references) //IL_056d: Expected O, but got Unknown AcceptableValueList<int> val = new AcceptableValueList<int>(new int[4] { 10, 20, 40, 60 }); kegSlots = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "Server Settings", "Mead Keg Slots", 20, "Server-controlled Mead Keg inventory capacity. Existing kegs always preserve stored items first; unsafe reductions are deferred or compacted safely. Options: 10, 20, 40, or 60 slots.", true, (int?)100, (AcceptableValueBase)(object)val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); quickGrabAmount = ((BaseUnityPlugin)this).Config.Bind<int>("Quick Grab", "Quick Grab Amount", 5, new ConfigDescription("Number of bottles requested by the Quick Grab action.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), new object[1] { new ConfigurationManagerAttributes { Order = 100 } })); quickGrabShortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Quick Grab", "Quick Grab Key", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), new ConfigDescription("Shortcut used while looking at an assigned Mead Keg.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 90 } })); kegScale = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "Keg Scale", 1f, new ConfigDescription("Template scale for newly placed kegs. Each placed keg saves its own scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f), new object[1] { new ConfigurationManagerAttributes { Order = 100 } })); kegHeightOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "Keg Height Offset", 0f, new ConfigDescription("Vertical offset saved to newly placed kegs. Negative values sink large kegs into the ground.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), new object[1] { new ConfigurationManagerAttributes { Order = 95 } })); barrelLabel = ((BaseUnityPlugin)this).Config.Bind<string>("Appearance", "Barrel Label", "Dynamic", new ConfigDescription("Placement template for the keg label. Dynamic follows the actual stored mead; Empty or any named label stays fixed on that placed keg. The choice is saved into the keg ZDO and replicated to other clients.", (AcceptableValueBase)(object)new AcceptableValueList<string>(MeadCatalog.LabelConfigOptions), new object[1] { new ConfigurationManagerAttributes { Order = 92 } })); enableIdleEffect = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance", "Subtle Idle Effect", false, new ConfigDescription("Optional client-side visual: shows a scaled-down copy of the vanilla fermenting effect while a keg is assigned.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 90 } })); labelX = BindTuning("Label Local X", 0f, 80); labelY = BindTuning("Label Local Y", 0.01f, 79); labelZ = BindTuning("Label Local Z", -0.3f, 78); labelRotX = BindTuning("Label Rotation X", 0f, 77, -180f, 180f); labelRotY = BindTuning("Label Rotation Y", 0f, 76, -180f, 180f); labelRotZ = BindTuning("Label Rotation Z", 0f, 75, -180f, 180f); labelScale = BindTuning("Label Scale", 0.95f, 74, 0.1f); labelFlipX = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance - Debug Tuning", "Label Flip Horizontal", true, new ConfigDescription("Advanced face-local debug toggle. Flips the label artwork left/right via UVs without turning the disc around.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 73, IsAdvanced = true } })); labelFlipY = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance - Debug Tuning", "Label Flip Vertical", false, new ConfigDescription("Advanced face-local debug toggle. Flips the label artwork up/down via UVs without turning the disc around.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 72, IsAdvanced = true } })); loopTapEffectPreview = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance - Debug Tuning", "Loop Tap Effect Preview", false, new ConfigDescription("Advanced tuning aid for the QUICK GRAB POUR VFX only. Repeats one safe preview instance on a nearby keg, never overlaps preview instances, and does not repeat the sound.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 69, IsAdvanced = true } })); tapX = BindTapTuning("Tap Effect Local X", 0f, 68); tapY = BindTapTuning("Tap Effect Local Y", 0f, 67); tapZ = BindTapTuning("Tap Effect Local Z", 0f, 66); tapScale = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance - Debug Tuning", "Tap Effect Scale", 1f, new ConfigDescription("Multiplier for the Quick Grab pour VFX size. 1.0 uses the native vanilla effect size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), new object[1] { new ConfigurationManagerAttributes { Order = 65, IsAdvanced = true } })); idleX = BindTuning("Idle Effect Local X", 0f, 60); idleY = BindTuning("Idle Effect Local Y", 0f, 59); idleZ = BindTuning("Idle Effect Local Z", 0f, 58); idleScale = BindTuning("Idle Effect Scale", 0.2f, 57, 0.02f, 1f); showStorageDebugTooltip = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced Debug", "Show Storage Debug Tooltip", false, new ConfigDescription("Advanced/debug only. Shows live Mead Keg capacity, grid and item-safety diagnostics in the hover text. Real storage-safety warnings remain visible even when this is disabled.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 100, IsAdvanced = true } })); MigrateQuickGrabDefault(); MigratePocLabelDefaults(); MigratePocEffectDefaults(); MigrateTapCalibrationDefaults(); kegSlots.SettingChanged += delegate { MeadKegCapacity.RefreshAll(); }; kegScale.SettingChanged += delegate { RefreshPlacementTemplateScale(); }; EventHandler eventHandler = delegate { MeadKegVisual.RefreshAllTransforms(); }; labelX.SettingChanged += eventHandler; labelY.SettingChanged += eventHandler; labelZ.SettingChanged += eventHandler; labelRotX.SettingChanged += eventHandler; labelRotY.SettingChanged += eventHandler; labelRotZ.SettingChanged += eventHandler; labelScale.SettingChanged += eventHandler; labelFlipX.SettingChanged += eventHandler; labelFlipY.SettingChanged += eventHandler; tapX.SettingChanged += eventHandler; tapY.SettingChanged += eventHandler; tapZ.SettingChanged += eventHandler; tapScale.SettingChanged += eventHandler; idleX.SettingChanged += eventHandler; idleY.SettingChanged += eventHandler; idleZ.SettingChanged += eventHandler; idleScale.SettingChanged += eventHandler; enableIdleEffect.SettingChanged += eventHandler; barrelLabel.SettingChanged += eventHandler; loopTapEffectPreview.SettingChanged += delegate { if (!LoopTapEffectPreview) { MeadKegEffects.StopTapPreview(); } }; } private void MigrateQuickGrabDefault() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0068: Unknown result type (might be due to invalid IL or missing references) if (quickGrabShortcut != null) { string a = ((object)quickGrabShortcut.Value/*cast due to .constrained prefix*/).ToString(); string b = ((object)new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 })/*cast due to .constrained prefix*/).ToString(); if (string.Equals(a, b, StringComparison.Ordinal)) { quickGrabShortcut.Value = new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }); ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated untouched Quick Grab shortcut default from Left Alt + E to Left Shift + E."); } } } private void MigratePocLabelDefaults() { if (labelX == null || labelY == null || labelZ == null || labelRotX == null || labelRotY == null || labelRotZ == null) { return; } bool flag = Mathf.Approximately(labelX.Value, 0f) && Mathf.Approximately(labelY.Value, 0.08f) && Mathf.Approximately(labelZ.Value, 0.015f) && Mathf.Approximately(labelRotX.Value, 0f) && Mathf.Approximately(labelRotY.Value, 0f) && Mathf.Approximately(labelRotZ.Value, 0f) && labelScale != null && Mathf.Approximately(labelScale.Value, 0.55f); if (((Mathf.Approximately(labelX.Value, 0.72f) || Mathf.Approximately(labelX.Value, 0.92f)) && Mathf.Approximately(labelY.Value, 0.03f) && Mathf.Approximately(labelZ.Value, 0f) && Mathf.Approximately(labelRotY.Value, -90f)) || (Mathf.Approximately(labelX.Value, 0.94f) && Mathf.Approximately(labelY.Value, 0.58f) && Mathf.Approximately(labelZ.Value, 0f) && Mathf.Approximately(labelRotY.Value, 90f)) || (Mathf.Approximately(labelX.Value, -0.94f) && Mathf.Approximately(labelY.Value, 0.58f) && Mathf.Approximately(labelZ.Value, 0f) && Mathf.Approximately(labelRotY.Value, -90f)) || flag) { labelX.Value = 0f; labelY.Value = 0.01f; labelZ.Value = -0.3f; labelRotX.Value = 0f; labelRotY.Value = 0f; labelRotZ.Value = 0f; if (labelScale != null) { labelScale.Value = 0.95f; } if (labelFlipX != null) { labelFlipX.Value = true; } if (labelFlipY != null) { labelFlipY.Value = false; } ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated untouched prototype label settings to the release face-local defaults."); } } private void MigratePocEffectDefaults() { if (idleX != null && idleY != null && idleZ != null && ((Mathf.Approximately(idleX.Value, 0.08f) && Mathf.Approximately(idleY.Value, -0.25f) && Mathf.Approximately(idleZ.Value, -0.18f)) || (Mathf.Approximately(idleX.Value, 1.05f) && Mathf.Approximately(idleY.Value, 0.28f) && Mathf.Approximately(idleZ.Value, 0f)) || (Mathf.Approximately(idleX.Value, -1.05f) && Mathf.Approximately(idleY.Value, 0.28f) && Mathf.Approximately(idleZ.Value, 0f)))) { idleX.Value = 0f; idleY.Value = 0f; idleZ.Value = 0f; ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated untouched prototype effect settings to the auto-detected tap anchor."); } } private void MigrateTapCalibrationDefaults() { if (tapX != null && tapY != null && tapZ != null) { bool num = Mathf.Approximately(tapX.Value, 0f) && Mathf.Approximately(tapY.Value, -0.6f) && Mathf.Approximately(tapZ.Value, -1.2f); bool flag = Mathf.Approximately(tapX.Value, 0f) && Mathf.Approximately(tapY.Value, -0.4f) && Mathf.Approximately(tapZ.Value, -0.6f); if (num || flag) { tapX.Value = 0f; tapY.Value = 0f; tapZ.Value = 0f; ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated measured tap calibration values to the 1.0 scale-aware tap defaults."); } } } private ConfigEntry<float> BindTapTuning(string key, float value, int order) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown return ((BaseUnityPlugin)this).Config.Bind<float>("Appearance - Debug Tuning", key, value, new ConfigDescription("Advanced fine adjustment added after the automatic scale-aware Quick Grab tap position. X = left/right across the keg face, Y = up/down, Z = outward/inward.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), new object[1] { new ConfigurationManagerAttributes { Order = order, IsAdvanced = true } })); } private ConfigEntry<float> BindTuning(string key, float value, int order, float min = -2f, float max = 2f) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown return ((BaseUnityPlugin)this).Config.Bind<float>("Appearance - Debug Tuning", key, value, new ConfigDescription("Advanced face-local debug tuning control for label and effect alignment. X = left/right across the keg face, Y = up/down on the face, Z = outward/inward from the face.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1] { new ConfigurationManagerAttributes { Order = order, IsAdvanced = true } })); } private void OnPrefabsRegistered() { PrefabManager.OnPrefabsRegistered -= OnPrefabsRegistered; MeadCatalog.Initialize(); MeadKegEffects.Initialize(); RegisterKeg(); } private void RegisterKeg() { //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Expected O, but got Unknown //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Expected O, but got Unknown //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Expected O, but got Unknown GameObject prefab = PrefabManager.Instance.GetPrefab("dvergrprops_barrel"); GameObject prefab2 = PrefabManager.Instance.GetPrefab("piece_chest_wood"); if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)prefab2)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not create Mead Keg: dvergrprops_barrel or piece_chest_wood was not found."); return; } GameObject val = PrefabManager.Instance.CreateClonedPrefab("DSMK_MeadKeg", prefab); if (!Object.op_Implicit((Object)(object)val)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not create Mead Keg clone."); return; } RemoveComponentIfPresent<Destructible>(val); RemoveComponentIfPresent<DropOnDestroyed>(val); RemoveComponentIfPresent<Container>(val); SetLayerRecursively(val, prefab2.layer); ZNetView component = val.GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component)) { ZNetView component2 = prefab2.GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component2)) { ExposedGameObjectExtension.AddComponentCopy<ZNetView>(val, component2); } component = val.GetComponent<ZNetView>(); } if (!Object.op_Implicit((Object)(object)component)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Mead Keg could not obtain a ZNetView."); return; } component.m_persistent = true; Piece component3 = prefab2.GetComponent<Piece>(); if (!Object.op_Implicit((Object)(object)val.GetComponent<Piece>()) && Object.op_Implicit((Object)(object)component3)) { ExposedGameObjectExtension.AddComponentCopy<Piece>(val, component3); } WearNTear component4 = prefab2.GetComponent<WearNTear>(); if (!Object.op_Implicit((Object)(object)val.GetComponent<WearNTear>()) && Object.op_Implicit((Object)(object)component4)) { ExposedGameObjectExtension.AddComponentCopy<WearNTear>(val, component4); } Piece component5 = val.GetComponent<Piece>(); if (!Object.op_Implicit((Object)(object)component5)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Mead Keg could not obtain a Piece component."); return; } component5.m_name = "Mead Keg"; component5.m_description = "Stores one type of finished mead at a time."; WearNTear component6 = val.GetComponent<WearNTear>(); if (Object.op_Implicit((Object)(object)component6)) { component6.m_autoCreateFragments = false; } val.AddComponent<MeadKegCapacity>(); Container obj = val.AddComponent<Container>(); obj.m_name = "Mead Keg"; int width; int height; if (MeadKegCapacityPatches.ProtectionAvailable) { GetStorageGrid(KegStorageCapacity, out width, out height); } else { width = 10; height = 10; } obj.m_width = width; obj.m_height = height; val.AddComponent<MeadKegStorage>(); val.AddComponent<MeadKegVisual>(); kegBaseScale = val.transform.localScale; val.AddComponent<MeadKegScale>().Setup(kegBaseScale); val.transform.localScale = Multiply(kegBaseScale, KegScale); registeredKegPrefab = val; PieceConfig val2 = new PieceConfig { PieceTable = PieceTables.Hammer, Category = PieceCategories.Misc }; val2.AddRequirement(new RequirementConfig("FineWood", 10, 0, true)); val2.AddRequirement(new RequirementConfig("Bronze", 2, 0, true)); TryAssignRenderedPieceIcon(prefab, component5); CustomPiece val3 = new CustomPiece(val, true, val2); if (!PieceManager.Instance.AddPiece(val3)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Jotunn PieceManager rejected the Mead Keg piece."); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered DynamicStorage Mead Hall Mead Keg."); } } private void TryAssignRenderedPieceIcon(GameObject iconSource, Piece piece) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)iconSource) || !Object.op_Implicit((Object)(object)piece)) { return; } try { RenderRequest val = new RenderRequest(iconSource); val.Rotation = RenderManager.IsometricRotation; val.UseCache = true; val.TargetPlugin = ((BaseUnityPlugin)this).Info.Metadata; Sprite val2 = RenderManager.Instance.Render(val); if (Object.op_Implicit((Object)(object)val2)) { piece.m_icon = val2; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Rendered Mead Keg hammer icon from clean dvergrprops_barrel source."); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Mead Keg icon renderer returned no sprite; inherited donor icon will remain as a fallback."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not render Mead Keg hammer icon; inherited donor icon will remain as a fallback: " + ex.Message)); } } private void RefreshPlacementTemplateScale() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)registeredKegPrefab)) { registeredKegPrefab.transform.localScale = Multiply(kegBaseScale, KegScale); } } private static Vector3 Multiply(Vector3 value, float multiplier) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) return new Vector3(value.x * multiplier, value.y * multiplier, value.z * multiplier); } internal static int NormalizeStorageCapacity(int value) { if (value == 10) { return 10; } if (value == 20) { return 20; } if (value == 40) { return 40; } if (value == 60) { return 60; } if (value >= 60) { return 60; } return 20; } private static int NormalizeSafeCapacity(int value) { switch (value) { case 10: return 10; case 20: return 20; case 40: return 40; case 60: return 60; default: _ = 80; return 80; } } internal static void GetSafeStorageGrid(int capacity, out int width, out int height) { switch (NormalizeSafeCapacity(capacity)) { case 10: width = 5; height = 2; break; case 20: width = 5; height = 4; break; case 40: width = 8; height = 5; break; case 60: width = 6; height = 10; break; case 80: width = 8; height = 10; break; default: width = 8; height = 10; break; } } internal static void GetStorageGrid(int capacity, out int width, out int height) { GetSafeStorageGrid(capacity, out width, out height); } private static void SetLayerRecursively(GameObject root, int layer) { if (!Object.op_Implicit((Object)(object)root)) { return; } Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (Object.op_Implicit((Object)(object)val)) { ((Component)val).gameObject.layer = layer; } } } private static void RemoveComponentIfPresent<T>(GameObject gameObject) where T : Component { T val = (Object.op_Implicit((Object)(object)gameObject) ? gameObject.GetComponent<T>() : default(T)); if (Object.op_Implicit((Object)(object)val)) { Object.DestroyImmediate((Object)(object)val); } } } internal static class MeadKegPlacementGhostPatches { private static readonly FieldInfo PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost"); internal static bool Install(Harmony harmony) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown if (harmony == null) { return false; } MethodInfo methodInfo = AccessTools.Method(typeof(Player), "UpdatePlacementGhost", new Type[1] { typeof(bool) }, (Type[])null) ?? AccessTools.Method(typeof(Player), "UpdatePlacementGhost", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(MeadKegPlacementGhostPatches), "UpdatePlacementGhostPostfix", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Logger.LogWarning((object)"Mead Keg placement-ghost scaling hook was not found in this Valheim build."); return false; } try { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Logger.LogWarning((object)("Mead Keg placement-ghost scaling hook failed: " + ex.Message)); return false; } } private static void UpdatePlacementGhostPostfix(Player __instance) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_0097: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)__instance) || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || PlacementGhostField == null) { return; } GameObject val = null; try { object? value = PlacementGhostField.GetValue(__instance); val = (GameObject)((value is GameObject) ? value : null); } catch { return; } if (!Object.op_Implicit((Object)(object)val)) { return; } MeadKegScale component = val.GetComponent<MeadKegScale>(); if (Object.op_Implicit((Object)(object)component)) { DynamicStorageMeadHallPlugin current = DynamicStorageMeadHallPlugin.Current; if (!((Object)(object)current == (Object)null)) { component.ApplyPlacementPreview(current.KegScale, 0f, current.BarrelLabelTemplateMode); Transform transform = val.transform; transform.position += Vector3.up * current.KegHeightOffset; } } } } internal static class MeadKegAppearancePlacementPatches { internal static bool Install(Harmony harmony) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown if (harmony == null) { return false; } MethodInfo methodInfo = AccessTools.Method(typeof(MeadKegAppearancePlacementPatches), "SetCreatorPostfix", (Type[])null, (Type[])null); MethodInfo[] methods = typeof(Piece).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); bool flag = false; MethodInfo[] array = methods; foreach (MethodInfo methodInfo2 in array) { ParameterInfo[] parameters = methodInfo2.GetParameters(); if (!(methodInfo2.Name != "SetCreator") && parameters.Length != 0 && !(parameters[0].ParameterType != typeof(long))) { try { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); flag = true; } catch (Exception ex) { Logger.LogWarning((object)("Mead Keg placement appearance hook failed: " + ex.Message)); } } } if (!flag) { Logger.LogWarning((object)"No compatible Piece.SetCreator overload was found for Mead Keg placement snapshots."); } return flag; } private static void SetCreatorPostfix(Piece __instance) { try { if (Object.op_Implicit((Object)(object)__instance) && Object.op_Implicit((Object)(object)Player.m_localPlayer)) { MeadKegScale component = ((Component)__instance).GetComponent<MeadKegScale>(); if (Object.op_Implicit((Object)(object)component)) { component.SavePlacementDefaults(); } MeadKegCapacity component2 = ((Component)__instance).GetComponent<MeadKegCapacity>(); if (Object.op_Implicit((Object)(object)component2)) { component2.InitializeNewPlacement(); } } } catch (Exception ex) { Logger.LogWarning((object)("Mead Keg placement appearance snapshot failed: " + ex.Message)); } } } internal static class MeadKegInteractionPatches { internal static bool Install(Harmony harmony) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown bool result = true; MethodInfo methodInfo = AccessTools.Method(typeof(Container), "GetHoverText", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(MeadKegInteractionPatches), "GetHoverTextPostfix", (Type[])null, (Type[])null); if (methodInfo != null && methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { result = false; } MethodInfo methodInfo3 = AccessTools.Method(typeof(Container), "Interact", new Type[3] { typeof(Humanoid), typeof(bool), typeof(bool) }, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(typeof(MeadKegInteractionPatches), "InteractPrefix", (Type[])null, (Type[])null); if (methodInfo3 != null && methodInfo4 != null) { harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { result = false; } return result; } private static void GetHoverTextPostfix(Container __instance, ref string __result) { if (!Object.op_Implicit((Object)(object)__instance)) { return; } MeadKegStorage component = ((Component)__instance).GetComponent<MeadKegStorage>(); if (Object.op_Implicit((Object)(object)component)) { __result = component.BuildHoverText(__result); } MeadKegCapacity component2 = ((Component)__instance).GetComponent<MeadKegCapacity>(); if (Object.op_Implicit((Object)(object)component2)) { __result += component2.SafetyWarning; DynamicStorageMeadHallPlugin current = DynamicStorageMeadHallPlugin.Current; if ((Object)(object)current != (Object)null && current.ShowStorageDebugTooltip) { __result += component2.DebugStatus; } } } private static bool InteractPrefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Invalid comparison between Unknown and I4 MeadKegStorage meadKegStorage = (Object.op_Implicit((Object)(object)__instance) ? ((Component)__instance).GetComponent<MeadKegStorage>() : null); Player val = (Player)(object)((character is Player) ? character : null); DynamicStorageMeadHallPlugin current = DynamicStorageMeadHallPlugin.Current; if (!Object.op_Implicit((Object)(object)meadKegStorage) || !Object.op_Implicit((Object)(object)val) || (Object)(object)val != (Object)(object)Player.m_localPlayer || (Object)(object)current == (Object)null) { return true; } KeyboardShortcut quickGrabShortcut = current.QuickGrabShortcut; if ((int)((KeyboardShortcut)(ref quickGrabShortcut)).MainKey != 101 || !((KeyboardShortcut)(ref quickGrabShortcut)).IsDown()) { return true; } if (!hold) { meadKegStorage.TryQuickGrab(val, current.QuickGrabAmount); } __result = true; return false; } } internal sealed class MeadDefinition { internal string PrefabName { get; } internal string LabelKey { get; } internal int AtlasIndex { get; } internal string LabelName { get; } internal MeadDefinition(string prefabName, string labelKey, int atlasIndex, string labelName) { PrefabName = prefabName; LabelKey = labelKey; AtlasIndex = atlasIndex; LabelName = labelName; } } internal static class MeadCatalog { internal static readonly MeadDefinition[] Definitions = new MeadDefinition[20] { new MeadDefinition("BarleyWine", "fire_resistance_barley_wine", 1, "Fire Resistance Barley Wine"), new MeadDefinition("MeadBugRepellent", "anti_sting_concoction", 2, "Anti-Sting Concoction"), new MeadDefinition("MeadBzerker", "berserkir_mead", 3, "Berserkir Mead"), new MeadDefinition("MeadEitrLingering", "lingering_eitr_mead", 4, "Lingering Eitr Mead"), new MeadDefinition("MeadEitrMinor", "minor_eitr_mead", 5, "Minor Eitr Mead"), new MeadDefinition("MeadFrostResist", "frost_resistance_mead", 6, "Frost Resistance Mead"), new MeadDefinition("MeadHasty", "tonic_of_ratatosk", 7, "Tonic of Ratatosk"), new MeadDefinition("MeadHealthLingering", "lingering_healing_mead", 8, "Lingering Healing Mead"), new MeadDefinition("MeadHealthMajor", "major_healing_mead", 9, "Major Healing Mead"), new MeadDefinition("MeadHealthMedium", "medium_healing_mead", 10, "Medium Healing Mead"), new MeadDefinition("MeadHealthMinor", "minor_healing_mead", 11, "Minor Healing Mead"), new MeadDefinition("MeadLightfoot", "lightfoot_mead", 12, "Lightfoot Mead"), new MeadDefinition("MeadPoisonResist", "poison_resistance_mead", 13, "Poison Resistance Mead"), new MeadDefinition("MeadStaminaLingering", "lingering_stamina_mead", 14, "Lingering Stamina Mead"), new MeadDefinition("MeadStaminaMedium", "medium_stamina_mead", 15, "Medium Stamina Mead"), new MeadDefinition("MeadStaminaMinor", "minor_stamina_mead", 16, "Minor Stamina Mead"), new MeadDefinition("MeadStrength", "mead_of_troll_endurance", 17, "Mead of Troll Endurance"), new MeadDefinition("MeadSwimmer", "draught_of_vananidir", 18, "Draught of Vananidir"), new MeadDefinition("MeadTamer", "brew_of_animal_whispers", 19, "Brew of Animal Whispers"), new MeadDefinition("MeadTasty", "tasty_mead", 20, "Tasty Mead") }; private static readonly Dictionary<string, MeadDefinition> ByPrefab = new Dictionary<string, MeadDefinition>(StringComparer.Ordinal); internal const string DynamicLabelOption = "Dynamic"; internal const string EmptyLabelOption = "Empty"; internal static readonly string[] LabelConfigOptions = BuildLabelConfigOptions(); private static string[] BuildLabelConfigOptions() { List<string> list = new List<string> { "Dynamic", "Empty" }; MeadDefinition[] definitions = Definitions; foreach (MeadDefinition meadDefinition in definitions) { list.Add(meadDefinition.LabelName); } return list.ToArray(); } internal static string LabelModeFromConfig(string option) { if (string.IsNullOrEmpty(option) || string.Equals(option, "Dynamic", StringComparison.OrdinalIgnoreCase)) { return "Dynamic"; } if (string.Equals(option, "Empty", StringComparison.OrdinalIgnoreCase)) { return "Empty"; } MeadDefinition[] definitions = Definitions; foreach (MeadDefinition meadDefinition in definitions) { if (string.Equals(option, meadDefinition.LabelName, StringComparison.OrdinalIgnoreCase) || string.Equals(option, meadDefinition.PrefabName, StringComparison.Ordinal)) { return meadDefinition.PrefabName; } } return "Dynamic"; } internal static string NormalizeLabelMode(string mode) { if (string.Equals(mode, "Empty", StringComparison.OrdinalIgnoreCase)) { return "Empty"; } if (string.Equals(mode, "Dynamic", StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(mode)) { return "Dynamic"; } MeadDefinition[] definitions = Definitions; foreach (MeadDefinition meadDefinition in definitions) { if (string.Equals(mode, meadDefinition.PrefabName, StringComparison.Ordinal)) { return meadDefinition.PrefabName; } } return "Dynamic"; } internal static int AtlasIndexForLabelMode(string mode, string assignedPrefab) { mode = NormalizeLabelMode(mode); if (string.Equals(mode, "Dynamic", StringComparison.Ordinal)) { return AtlasIndexFor(assignedPrefab); } if (string.Equals(mode, "Empty", StringComparison.Ordinal)) { return 0; } MeadDefinition[] definitions = Definitions; foreach (MeadDefinition meadDefinition in definitions) { if (string.Equals(mode, meadDefinition.PrefabName, StringComparison.Ordinal)) { return meadDefinition.AtlasIndex; } } return 0; } internal static void Initialize() { ByPrefab.Clear(); MeadDefinition[] definitions = Definitions; foreach (MeadDefinition meadDefinition in definitions) { if (Object.op_Implicit((Object)(object)PrefabManager.Instance.GetPrefab(meadDefinition.PrefabName))) { ByPrefab[meadDefinition.PrefabName] = meadDefinition; } else { Logger.LogWarning((object)("Mead keg support item '" + meadDefinition.PrefabName + "' was not found in this game build.")); } } } internal static bool IsSupported(string prefabName) { if (!string.IsNullOrEmpty(prefabName)) { return ByPrefab.ContainsKey(prefabName); } return false; } internal static string LabelKeyFor(string prefabName) { if (prefabName == null || !ByPrefab.TryGetValue(prefabName, out var value)) { return "empty"; } return value.LabelKey; } internal static int AtlasIndexFor(string prefabName) { if (prefabName == null || !ByPrefab.TryGetValue(prefabName, out var value)) { return 0; } return value.AtlasIndex; } internal static string PrefabName(ItemData item) { if (item == null || !Object.op_Implicit((Object)(object)item.m_dropPrefab)) { return null; } return ((Object)item.m_dropPrefab).name; } internal static string DisplayName(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return ""; } GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName); ItemDrop val = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<ItemDrop>() : null); string text = (((Object)(object)val != (Object)null && val.m_itemData != null && val.m_itemData.m_shared != null) ? val.m_itemData.m_shared.m_name : null); if (!string.IsNullOrEmpty(text) && Localization.instance != null) { return Localization.instance.Localize(text); } return prefabName; } } public sealed class MeadKegCapacity : MonoBehaviour { private sealed class ItemSnapshot { internal ItemData Item; internal Vector2i Position; internal string PrefabName; internal int Stack; internal int Quality; internal float Durability; internal int Variant; internal long CrafterId; internal string CrafterName; } internal const int MaximumRecoveryCapacity = 80; internal const int UiSafeMaximumWidth = 8; internal const int MigrationEnvelopeWidth = 10; internal const int MigrationEnvelopeHeight = 10; private static readonly HashSet<MeadKegCapacity> Active = new HashSet<MeadKegCapacity>(); private static readonly FieldInfo InventoryWidth = AccessTools.Field(typeof(Inventory), "m_width"); private static readonly FieldInfo InventoryHeight = AccessTools.Field(typeof(Inventory), "m_height"); private static readonly MethodInfo ContainerIsInUse = AccessTools.Method(typeof(Container), "IsInUse", Type.EmptyTypes, (Type[])null); private static readonly MethodInfo InventoryChangedMethod = ResolveInventoryChangedMethod(); private Container container; private Inventory inventory; private ZNetView nview; private float nextRefresh; private bool newPlacementConfirmed; private bool interactionConfirmed; private bool observedNonEmptyInventory; private bool warnedMissingChanged; private int lastKnownInaccessibleStacks; private string lastSafetyAction = "None"; internal string SafetyWarning { get { CacheReferences(); int num = RefreshHiddenStackObservation(); if (num <= 0) { return string.Empty; } int num2 = CountStacks(inventory); int num3 = Mathf.Max(0, num2 - 80); if (num3 > 0) { return "\n<color=#FF5555><b>STORAGE SAFETY WARNING</b></color>\n<color=#FF7777>" + num + " stored stack(s) are currently inaccessible in the container UI. The items are still present.</color>\n<color=#FFAAAA>Remove at least " + num3 + " stack(s) so every item can be restored to visible slots safely.</color>"; } return "\n<color=#FF5555><b>STORAGE SAFETY WARNING</b></color>\n<color=#FF7777>" + num + " stored stack(s) are currently inaccessible in the container UI. The items are still present.</color>\n<color=#FFAAAA>Automatic recovery will move them into visible slots only after a fully validated layout is available and the keg is closed. If this warning persists, enable Advanced Debug -> Show Storage Debug Tooltip and check Last safety action.</color>"; } } internal string DebugStatus { get { CacheReferences(); DynamicStorageMeadHallPlugin current = DynamicStorageMeadHallPlugin.Current; int num = (((Object)(object)current != (Object)null) ? current.KegStorageCapacity : (-1)); int width = ((inventory != null) ? ReadDimension(InventoryWidth, inventory, Object.op_Implicit((Object)(object)container) ? container.m_width : 0) : (Object.op_Implicit((Object)(object)container) ? container.m_width : 0)); int height = ((inventory != null) ? ReadDimension(InventoryHeight, inventory, Object.op_Implicit((Object)(object)container) ? container.m_height : 0) : (Object.op_Implicit((Object)(object)container) ? container.m_height : 0)); int num2 = CountStacks(inventory); GetOccupiedDiagnostics(inventory, width, height, out var bounds, out var outsideLiveGrid, out var inaccessibleUi); int num3 = ((inventory != null) ? ((object)inventory).GetHashCode() : 0); int num4 = (Object.op_Implicit((Object)(object)container) ? container.m_width : (-1)); int num5 = (Object.op_Implicit((Object)(object)container) ? container.m_height : (-1)); bool known; bool flag = IsContainerInUse(out known); string text = ((!Object.op_Implicit((Object)(object)nview) || !nview.IsValid()) ? "Unknown" : (nview.IsOwner() ? "Yes" : "No")); return "\n<color=#8FD3FF><b>MEAD KEG STORAGE DEBUG</b></color>\n<color=#B8E3FF>Desired:</color> <color=#FFFFFF>" + num + " slots</color>\n<color=#B8E3FF>Inventory grid:</color> <color=#FFFFFF>" + width + "x" + height + "</color>\n<color=#B8E3FF>Container grid:</color> <color=#FFFFFF>" + num4 + "x" + num5 + "</color>\n<color=#B8E3FF>Stacks:</color> <color=#FFFFFF>" + num2 + "</color>\n<color=#B8E3FF>Occupied bounds:</color> <color=#FFFFFF>" + bounds + "</color>\n<color=#B8E3FF>Outside live grid:</color> <color=#FFFFFF>" + outsideLiveGrid + "</color>\n<color=#FF8E8E>Inaccessible in current UI:</color> <color=#FFFFFF>" + inaccessibleUi + "</color>\n<color=#B8E3FF>Authoritative owner:</color> <color=#FFFFFF>" + text + "</color>\n<color=#B8E3FF>Container open:</color> <color=#FFFFFF>" + ((!known) ? "Unknown" : (flag ? "Yes" : "No")) + "</color>\n<color=#B8E3FF>Inventory ID:</color> <color=#FFFFFF>" + num3 + "</color>\n<color=#B8E3FF>Last safety action:</color> <color=#FFFFFF>" + lastSafetyAction + "</color>"; } } private void OnEnable() { Active.Add(this); nextRefresh = 0f; } private void OnDisable() { Active.Remove(this); } private void OnDestroy() { Active.Remove(this); } private void Start() { CacheReferences(); ReconcileCapacity(); } private void Update() { if (!(Time.unscaledTime < nextRefresh)) { nextRefresh = Time.unscaledTime + 0.35f; ReconcileCapacity(); } } internal static void RefreshAll() { MeadKegCapacity[] array = new MeadKegCapacity[Active.Count]; Active.CopyTo(array); MeadKegCapacity[] array2 = array; foreach (MeadKegCapacity meadKegCapacity in array2) { if (Object.op_Implicit((Object)(object)meadKegCapacity)) { meadKegCapacity.nextRefresh = 0f; meadKegCapacity.ReconcileCapacity(); } } } internal void InitializeNewPlacement() { CacheReferences(); newPlacementConfirmed = true; nextRefresh = 0f; ReconcileCapacity(); } internal void NotifyContainerInteraction() { CacheReferences(); interactionConfirmed = true; nextRefresh = 0f; ReconcileCapacity(); } private void ReconcileCapacity() { DynamicStorageMeadHallPlugin current = DynamicStorageMeadHallPlugin.Current; if ((Object)(object)current == (Object)null) { return; } CacheReferences(); if (Object.op_Implicit((Object)(object)container) && inventory != null) { if (CountStacks(inventory) > 0) { observedNonEmptyInventory = true; } ApplySafeCapacity(current.KegStorageCapacity); } } private void ApplySafeCapacity(int desiredCapacity) { int num = CountStacks(inventory); if (num < 0) { return; } if (!MeadKegCapacityPatches.ProtectionAvailable || InventoryWidth == null || InventoryHeight == null) { EnsureAtLeastMigrationEnvelope(); lastSafetyAction = "Fail-closed: capacity load protection unavailable"; return; } if (!observedNonEmptyInventory && !newPlacementConfirmed && !interactionConfirmed && num == 0) { EnsureAtLeastMigrationEnvelope(); lastSafetyAction = "Waiting for positive inventory observation"; return; } int num2 = SmallestSupportedCapacityForStacks(num); int num3 = DynamicStorageMeadHallPlugin.NormalizeStorageCapacity(desiredCapacity); int num4 = Mathf.Max(num3, num2); if (num4 > 80) { EnsureAtLeastMigrationEnvelope(); lastSafetyAction = "Deferred: more than 80 stacks require the recovery envelope"; return; } DynamicStorageMeadHallPlugin.GetSafeStorageGrid(num4, out var width, out var height); if (width > 8) { EnsureAtLeastMigrationEnvelope(); lastSafetyAction = "Refused non-UI-safe target width " + width; Logger.LogError((object)(((Object)((Component)this).gameObject).name + ": Capacity safety refused target wider than 8 columns.")); return; } int num5 = ReadDimension(InventoryWidth, inventory, container.m_width); int num6 = ReadDimension(InventoryHeight, inventory, container.m_height); if (num5 == width && num6 == height && container.m_width == width && container.m_height == height && CountInaccessibleStacks(inventory, width, height) == 0) { RefreshHiddenStackObservation(); lastSafetyAction = ((num4 == num3) ? "Grid stable" : "Holding larger grid because stack count exceeds requested capacity"); return; } bool known; bool flag = IsContainerInUse(out known); if (!known) { lastSafetyAction = "Deferred: could not prove container is closed"; return; } if (flag) { lastSafetyAction = "Deferred while container is open"; return; } List<ItemSnapshot> list = CaptureSnapshots(); if (list == null) { lastSafetyAction = "Deferred: could not snapshot inventory"; return; } if (!TryBuildRelocationPlan(list, width, height, out var plan)) { lastSafetyAction = "Deferred: no validated destination for every stack"; return; } int num7 = CountPlannedMoves(plan); if (num7 > 0 && !CanMutateInventory()) { lastSafetyAction = "Deferred relocation: waiting for authoritative owner"; return; } if (num7 > 0 && InventoryChangedMethod == null) { if (!warnedMissingChanged) { warnedMissingChanged = true; Logger.LogError((object)"Mead Keg cannot resolve Inventory.Changed; item-coordinate migration is disabled to preserve items."); } lastSafetyAction = "Deferred: persistence callback unavailable"; return; } EnsureAtLeastMigrationEnvelope(); if (num7 > 0) { ApplyPlan(plan); if (!ValidateSnapshots(list, width, height)) { RestoreSnapshots(list); lastSafetyAction = "Rollback: validation failed before persistence"; Logger.LogError((object)(((Object)((Component)this).gameObject).name + ": Capacity relocation validation failed; original positions restored.")); return; } if (!NotifyInventoryChanged(inventory)) { RestoreSnapshots(list); NotifyInventoryChanged(inventory); lastSafetyAction = "Rollback: could not persist relocated coordinates"; Logger.LogError((object)(((Object)((Component)this).gameObject).name + ": Capacity relocation persistence failed; original positions restored.")); return; } if (!ValidateSnapshots(list, width, height)) { RestoreSnapshots(list); NotifyInventoryChanged(inventory); lastSafetyAction = "Rollback: post-persistence validation failed"; Logger.LogError((object)(((Object)((Component)this).gameObject).name + ": Capacity relocation post-persistence validation failed; original positions restored.")); return; } } container.m_width = width; container.m_height = height; WriteDimension(InventoryWidth, inventory, width); WriteDimension(InventoryHeight, inventory, height); RefreshHiddenStackObservation(); lastSafetyAction = ((num7 > 0) ? $"Restored {num7} stack(s); committed {width}x{height}" : $"Committed UI-safe grid {width}x{height}"); Logger.LogInfo((object)(((Object)((Component)this).gameObject).name + $": safe grid committed {num5}x{num6} -> {width}x{height}; " + $"stacks={num}, moved={num7}, requested={num3}, effective={num4}.")); } private void EnsureAtLeastMigrationEnvelope() { if (Object.op_Implicit((Object)(object)container) && inventory != null) { int num = ReadDimension(InventoryWidth, inventory, container.m_width); int num2 = ReadDimension(InventoryHeight, inventory, container.m_height); int num3 = Mathf.Max(num, 10); int num4 = Mathf.Max(num2, 10); container.m_width = num3; container.m_height = num4; WriteDimension(InventoryWidth, inventory, num3); WriteDimension(InventoryHeight, inventory, num4); } } private bool CanMutateInventory() { if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null) { return false; } return nview.IsOwner(); } private bool IsContainerInUse(out bool known) { known = false; if (!Object.op_Implicit((Object)(object)container) || ContainerIsInUse == null) { return false; } try { object obj = ContainerIsInUse.Invoke(container, null); if (obj is bool) { known = true; return (bool)obj; } } catch { } return false; } private List<ItemSnapshot> CaptureSnapshots() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (inventory == null) { return null; } List<ItemData> allItems = inventory.GetAllItems(); if (allItems == null) { return new List<ItemSnapshot>(); } List<ItemSnapshot> list = new List<ItemSnapshot>(allItems.Count); foreach (ItemData item in allItems) { if (item != null) { list.Add(new ItemSnapshot { Item = item, Position = item.m_gridPos, PrefabName = (Object.op_Implicit((Object)(object)item.m_dropPrefab) ? ((Object)item.m_dropPrefab).name : string.Empty), Stack = item.m_stack, Quality = item.m_quality, Durability = item.m_durability, Variant = item.m_variant, CrafterId = item.m_crafterID, CrafterName = (item.m_crafterName ?? string.Empty) }); } } return list; } private static bool TryBuildRelocationPlan(List<ItemSnapshot> snapshots, int width, int height, out Dictionary<ItemData, Vector2i> plan) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) plan = new Dictionary<ItemData, Vector2i>(); if (snapshots == null) { return false; } if (snapshots.Count > width * height) { return false; } bool[,] array = new bool[width, height]; List<ItemSnapshot> list = new List<ItemSnapshot>(); foreach (ItemSnapshot snapshot in snapshots) { if (snapshot == null || snapshot.Item == null) { return false; } int x = snapshot.Position.x; int y = snapshot.Position.y; if (x >= 0 && y >= 0 && x < width && y < height && !array[x, y]) { array[x, y] = true; plan[snapshot.Item] = snapshot.Position; } else { list.Add(snapshot); } } int num = 0; int num2 = 0; foreach (ItemSnapshot item in list) { bool flag = false; for (int i = num2; i < height; i++) { if (flag) { break; } for (int j = ((i == num2) ? num : 0); j < width; j++) { if (!array[j, i]) { array[j, i] = true; plan[item.Item] = new Vector2i(j, i); num = j + 1; num2 = i; if (num >= width) { num = 0; num2++; } flag = true; break; } } } if (!flag) { return false; } } return plan.Count == snapshots.Count; } private static int CountPlannedMoves(Dictionary<ItemData, Vector2i> plan) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (plan == null) { return 0; } int num = 0; foreach (KeyValuePair<ItemData, Vector2i> item in plan) { ItemData key = item.Key; if (key != null) { Vector2i value = item.Value; if (key.m_gridPos.x != value.x || key.m_gridPos.y != value.y) { num++; } } } return num; } private static void ApplyPlan(Dictionary<ItemData, Vector2i> plan) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<ItemData, Vector2i> item in plan) { if (item.Key != null) { item.Key.m_gridPos = item.Value; } } } private static void RestoreSnapshots(List<ItemSnapshot> snapshots) { //IL_0027: 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) if (snapshots == null) { return; } foreach (ItemSnapshot snapshot in snapshots) { if (snapshot != null && snapshot.Item != null) { snapshot.Item.m_gridPos = snapshot.Position; } } } private bool ValidateSnapshots(List<ItemSnapshot> snapshots, int width, int height) { if (snapshots == null || inventory == null) { return false; } List<ItemData> allItems = inventory.GetAllItems(); if (allItems == null) { return snapshots.Count == 0; } int num = 0; HashSet<ItemData> hashSet = new HashSet<ItemData>(); HashSet<int> hashSet2 = new HashSet<int>(); foreach (ItemData item3 in allItems) { if (item3 != null) { num++; hashSet.Add(item3); int x = item3.m_gridPos.x; int y = item3.m_gridPos.y; if (x < 0 || y < 0 || x >= width || y >= height) { return false; } int item = y * width + x; if (!hashSet2.Add(item)) { return false; } } } if (num != snapshots.Count) { return false; } foreach (ItemSnapshot snapshot in snapshots) { ItemData item2 = snapshot.Item; if (item2 == null || !hashSet.Contains(item2)) { return false; } if (!string.Equals(Object.op_Implicit((Object)(object)item2.m_dropPrefab) ? ((Object)item2.m_dropPrefab).name : string.Empty, snapshot.PrefabName, StringComparison.Ordinal) || item2.m_stack != snapshot.Stack || item2.m_quality != snapshot.Quality || Math.Abs(item2.m_durability - snapshot.Durability) > 0.0001f || item2.m_variant != snapshot.Variant || item2.m_crafterID != snapshot.CrafterId || !string.Equals(item2.m_crafterName ?? string.Empty, snapshot.CrafterName, StringComparison.Ordinal)) { return false; } } return true; } private static int SmallestSupportedCapacityForStacks(int stacks) { if (stacks <= 10) { return 10; } if (stacks <= 20) { return 20; } if (stacks <= 40) { return 40; } if (stacks <= 60) { return 60; } if (stacks <= 80) { return 80; } return int.MaxValue; } private void CacheReferences() { if (!Object.op_Implicit((Object)(object)container)) { container = ((Component)this).GetComponent<Container>(); } if (!Object.op_Implicit((Object)(object)nview)) { nview = ((Component)this).GetComponent<ZNetView>(); } if (Object.op_Implicit((Object)(object)container)) { Inventory val = container.GetInventory(); if (val != null) { inventory = val; } } } private int RefreshHiddenStackObservation() { int num = ((inventory != null) ? ReadDimension(InventoryWidth, inventory, Object.op_Implicit((Object)(object)container) ? container.m_width : 0) : 0); int liveHeight = ((inventory != null) ? ReadDimension(InventoryHeight, inventory, Object.op_Implicit((Object)(object)container) ? container.m_height : 0) : 0); int num2 = CountInaccessibleStacks(inventory, num, liveHeight); if (num2 > 0) { lastKnownInaccessibleStacks = num2; return num2; } if (num > 0 && num <= 8) { lastKnownInaccessibleStacks = 0; } return lastKnownInaccessibleStacks; } private static int CountInaccessibleStacks(Inventory target, int liveWidth, int liveHeight) { if (target == null) { return 0; } List<ItemData> allItems = target.GetAllItems(); if (allItems == null) { return 0; } int num = 0; foreach (ItemData item in allItems) { if (item != null) { int x = item.m_gridPos.x; int y = item.m_gridPos.y; bool num2 = x < 0 || y < 0 || x >= liveWidth || y >= liveHeight; bool flag = x >= 8; if (num2 || flag) { num++; } } } return num; } private static void GetOccupiedDiagnostics(Inventory target, int width, int height, out string bounds, out int outsideLiveGrid, out int inaccessibleUi) { outsideLiveGrid = 0; inaccessibleUi = 0; if (target == null) { bounds = "None"; return; } List<ItemData> allItems = target.GetAllItems(); if (allItems == null || allItems.Count == 0) { bounds = "Empty"; return; } int num = int.MaxValue; int num2 = int.MaxValue; int num3 = int.MinValue; int num4 = int.MinValue; int num5 = 0; foreach (ItemData item in allItems) { if (item == null) { continue; } num5++; int x = item.m_gridPos.x; int y = item.m_gridPos.y; if (x < num) { num = x; } if (y < num2) { num2 = y; } if (x > num3) { num3 = x; } if (y > num4) { num4 = y; } int num6; if (x >= 0 && y >= 0 && x < width) { num6 = ((y >= height) ? 1 : 0); if (num6 == 0) { goto IL_00cc; } } else { num6 = 1; } outsideLiveGrid++; goto IL_00cc; IL_00cc: if (num6 != 0 || x >= 8) { inaccessibleUi++; } } bounds = ((num5 == 0) ? "Empty" : $"x={num}..{num3}, y={num2}..{num4}"); } private static int CountStacks(Inventory target) { if (target == null) { return -1; } List<ItemData> allItems = target.GetAllItems(); if (allItems == null) { return 0; } int num = 0; foreach (ItemData item in allItems) { if (item != null) { num++; } } return num; } private static MethodInfo ResolveInventoryChangedMethod() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Inventory))) { if (declaredMethod == null || declaredMethod.Name != "Changed") { continue; } ParameterInfo[] parameters = declaredMethod.GetParameters(); bool flag = true; ParameterInfo[] array = parameters; foreach (ParameterInfo parameterInfo in array) { if (!parameterInfo.HasDefaultValue && !(parameterInfo.ParameterType == typeof(bool))) { flag = false; break; } } if (flag) { return declaredMethod; } } return null; } private static bool NotifyInventoryChanged(Inventory target) { if (target == null || InventoryChangedMethod == null) { return false; } try { ParameterInfo[] parameters = InventoryChangedMethod.GetParameters(); object[] array = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { ParameterInfo parameterInfo = parameters[i]; array[i] = (parameterInfo.HasDefaultValue ? parameterInfo.DefaultValue : ((object)false)); } InventoryChangedMethod.Invoke(target, array); return true; } catch (Exception ex) { Logger.LogWarning((object)("Mead Keg inventory change notification failed: " + ex.Message)); return false; } } private static int ReadDimension(FieldInfo field, Inventory target, int fallback) { if (field == null || target == null) { return fallback; } try { return (int)field.GetValue(target); } catch { return fallback; } } private static void WriteDimension(FieldInfo field, Inventory target, int value) { if (field == null || target == null) { return; } try { field.SetValue(target, value); } catch (Exception ex) { Logger.LogWarning((object)("Mead Keg inventory dimension update failed: " + ex.Message)); } } } internal static class MeadKegCapacityPatches { private static bool installed; internal static bool ProtectionAvailable { get; private set; } internal static bool Install(Harmony harmony) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown if (installed) { return ProtectionAvailable; } if (harmony == null) { return false; } try { MethodInfo methodInfo = AccessTools.Method(typeof(Container), "Awake", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(MeadKegCapacityPatches), "ContainerAwakePrefix", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Logger.LogError((object)"Mead Keg capacity safety could not resolve Container.Awake. Capacity changes will fail closed."); installed = true; ProtectionAvailable = false; return false; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo3 = AccessTools.Method(typeof(MeadKegCapacityPatches), "ContainerInteractPrefix", (Type[])null, (Type[])null); int num = 0; if (methodInfo3 != null) { MethodInfo[] methods = typeof(Container).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo4 in methods) { if (!(methodInfo4 == null) && !(methodInfo4.Name != "Interact")) { harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } } } installed = true; ProtectionAvailable = true; Logger.LogInfo((object)("Mead Keg capacity safety installed. Container load envelope is 10x10; final player-facing grids are hard-capped at 8 columns. " + num + " Container.Interact overload(s) observed.")); return true; } catch (Exception ex) { installed = true; ProtectionAvailable = false; Logger.LogError((object)("Mead Keg capacity safety patch installation failed; capacity changes will fail closed: " + ex.Message)); return false; } } private static void ContainerAwakePrefix(Container __instance) { if (Object.op_Implicit((Object)(object)__instance) && Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<MeadKegCapacity>())) { __instance.m_width = 10; __instance.m_height = 10; } } private static void ContainerInteractPrefix(Container __instance) { if (Object.op_Implicit((Object)(object)__instance)) { MeadKegCapacity component = ((Component)__instance).GetComponent<MeadKegCapacity>(); if (Object.op_Implicit((Object)(object)component)) { component.NotifyContainerInteraction(); } } } } public sealed class MeadKegScale : MonoBehaviour { private const string ZdoScaleKey = "DSMK.Scale"; private const string ZdoHeightOffsetKey = "DSMK.HeightOffset"; private const string ZdoLabelModeKey = "DSMK.LabelMode"; [SerializeField] private Vector3 baseScale = Vector3.one; private ZNetView nview; private bool applied; private float nextCheck; private float currentHeightOffset; private float currentScaleMultiplier = 1f; private string currentLabelMode = "Dynamic"; private bool pendingPlacementSnapshot; private float pendingScale; private float pendingHeightOffset; private string pendingLabelMode; private float pendingDeadline; private float startedAt; internal float CurrentHeightOffset => currentHeightOffset; internal float CurrentScaleMultiplier => currentScaleMultiplier; internal string CurrentLabelMode => MeadCatalog.NormalizeLabelMode(currentLabelMode); internal void Setup(Vector3 originalBaseScale) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) baseScale = originalBaseScale; } private void Start() { startedAt = Time.time; nview = ((Component)this).GetComponent<ZNetView>(); ApplyOrSave(); } private void Update() { if (!applied && !(Time.unscaledTime < nextCheck)) { nextCheck = Time.unscaledTime + 0.25f; ApplyOrSave(); } } private void ApplyOrSave() { if (!Object.op_Implicit((Object)(object)nview)) { nview = ((Component)this).GetComponent<ZNetView>(); } if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null) { return; } if (pendingPlacementSnapshot) { TryCommitPendingPlacementSnapshot(); } ZDO zDO = nview.GetZDO(); float num = zDO.GetFloat("DSMK.Scale", -1f); bool flag = num > 0f; float num2 = zDO.GetFloat("DSMK.HeightOffset", float.NaN); bool flag2 = !float.IsNaN(num2); string text = zDO.GetString("DSMK.LabelMode", ""); bool flag3 = !string.IsNullOrEmpty(text); if ((!flag || !flag2 || !flag3) && nview.IsOwner()) { DynamicStorageMeadHallPlugin current = DynamicStorageMeadHallPlugin.Current; if (!flag) { num = (((Object)(object)current != (Object)null) ? current.KegScale : 1f); zDO.Set("DSMK.Scale", num); } if (!flag2) { num2 = (((Object)(object)current != (Object)null) ? current.KegHeightOffset : 0f); zDO.Set("DSMK.HeightOffset", num2); } if (!flag3) { if (Time.time - startedAt < 1f && !pendingPlacementSnapshot) { return; } text = "Dynamic"; zDO.Set("DSMK.LabelMode", text); } } if (!flag && num <= 0f) { num = 1f; } if (!flag2 && float.IsNaN(num2)) { num2 = 0f; } if (string.IsNullOrEmpty(text)) { text = "Dynamic"; } ApplyScaleAndOffset(num, num2, text); applied = true; } internal void SavePlacementDefaults() { DynamicStorageMeadHallPlugin current = DynamicStorageMeadHallPlugin.Current; if (!((Object)(object)current == (Object)null)) { pendingScale = current.KegScale; pendingHeightOffset = current.KegHeightOffset; pendingLabelMode = current.BarrelLabelTemplateMode; pendingPlacementSnapshot = true; pendingDeadline = Time.time + 15f; applied = false; TryCommitPendingPlacementSnapshot(); } } private bool TryCommitPendingPlacementSnapshot() { if (!pendingPlacementSnapshot) { return false; } if (Time.time > pendingDeadline) { Logger.LogWarning((object)(((Object)((Component)this).gameObject).name + ": Mead Keg placement appearance snapshot timed out before ZDO ownership became ready.")); pendingPlacementSnapshot = false; return false; } if (!Object.op_Implicit((Object)(object)nview)) { nview = ((Component)this).GetComponent<ZNetView>(); } if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null || !nview.IsOwner()) { return false; } ZDO zDO = nview.GetZDO(); zDO.Set("DSMK.Scale", Mathf.Clamp(pendingScale, 0.5f, 3f)); zDO.Set("DSMK.HeightOffset", Mathf.Clamp(pendingHeightOffset, -2f, 2f)); zDO.Set("DSMK.LabelMode", MeadCatalog.NormalizeLabelMode(pendingLabelMode)); pendingPlacementSnapshot = false; Logger.LogInfo((object)(((Object)((Component)this).gameObject).name + ": saved per-keg scale/height/label placement snapshot.")); return true; } internal void ApplyPlacementPreview(float multiplier, float heightOffset, string labelMode) { ApplyScaleAndOffset(multiplier, heightOffset, labelMode); } private void ApplyScaleAndOffset(float multiplier, float heightOffset, string labelMode) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) multiplier = Mathf.Clamp(multiplier, 0.5f, 3f); currentScaleMultiplier = multiplier; currentHeightOffset = Mathf.Clamp(heightOffset, -2f, 2f); currentLabelMode = MeadCatalog.NormalizeLabelMode(labelMode); ((Component)this).transform.localScale = new Vector3(baseScale.x * multiplier, baseScale.y * multiplier, baseScale.z * multiplier); MeadKegVisual component = ((Component)this).GetComponent<MeadKegVisual>(); if (Object.op_Implicit((Object)(object)component)) { MeadKegStorage component2 = ((Component)this).GetComponent<MeadKegStorage>(); component.Refresh(Object.op_Implicit((Object)(object)component2) ? component2.AssignedPrefabName : null); } } } public sealed class MeadKegStorage : MonoBehaviour { private const string ZdoAssignedKey = "DSMK.AssignedMead"; private static readonly Dictionary<Inventory, MeadKegStorage> Registry = new Dictionary<Inventory, MeadKegStorage>(); private Container container; private Inventory inventory; private ZNetView nview; private string assignedPrefab; private float nextRefresh; private bool mixedWarningShown; private float lastRejectMessageTime = -10f; internal string AssignedPrefabName => assignedPrefab; internal Container Container { get { if (!Object.op_Implicit((Object)(object)container)) { return container = ((Component)this).GetComponent<Container>(); } return container; } } private void OnEnable() { nextRefresh = 0f; } private void Start() { container = ((Component)this).GetComponent<Container>(); nview = ((Component)this).GetComponent<ZNetView>(); RefreshState(forceVisual: true); } private void Update() { if (!(Time.unscaledTime < nextRefresh)) { nextRefresh = Time.unscaledTime + 0.25f; RefreshState(forceVisual: false); } } private void OnDisable() { UnregisterInventory(); } private void OnDestroy() { UnregisterInventory(); } private void RefreshState(bool forceVisual) { if (!Object.op_Implicit((Object)(object)container)) { container = ((Component)this).GetComponent<Container>(); } if (!Object.op_Implicit((Object)(object)nview)) { nview = ((Component)this).GetComponent<ZNetView>(); } if (!Object.op_Implicit((Object)(object)container)) { return; } Inventory val = container.GetInventory(); if (inventory != val) { UnregisterInventory(); inventory = val; if (inventory != null) { Registry[inventory] = this; } } bool mixed; string text = ResolveAssignmentFromInventory(out mixed); string text2 = ReadAssignedFromZdo(); string b = ((!string.IsNullOrEmpty(text)) ? text : text2); if (inventory != null && inventory.GetAllItems().Count == 0) { b = null; } if (mixed && !mixedWarningShown) { mixedWarningShown = true; Logger.LogWarning((object)(((Object)((Component)this).gameObject).name + ": multiple mead types were already present. New inserts will be restricted to the first detected type until the keg is emptied.")); } if (!mixed) { mixedWarningShown = false; } bool num = !string.Equals(assignedPrefab, b, StringComparison.Ordinal); assignedPrefab = b; if (Object.op_Implicit((Object)(object)nview) && nview.IsValid() && nview.IsOwner() && nview.GetZDO() != null) { string a = nview.GetZDO().GetString("DSMK.AssignedMead", ""); string text3 = assignedPrefab ?? ""; if (!string.Equals(a, text3, StringComparison.Ordinal)) { nview.GetZDO().Set("DSMK.AssignedMead", text3); } } if (num || forceVisual) { MeadKegVisual component = ((Component)this).GetComponent<MeadKegVisual>(); if (Object.op_Implicit((Object)(object)component)) { component.Refresh(assignedPrefab); } } } private string ReadAssignedFromZdo() { if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null) { return null; } string text = nview.GetZDO().GetString("DSMK.AssignedMead", ""); if (!MeadCatalog.IsSupported(text)) { return null; } return text; } private string ResolveAssignmentFromInventory(out bool mixed) { mixed = false; if (inventory == null) { return null; } string text = null; foreach (ItemData allItem in inventory.GetAllItems()) { string text2 = MeadCatalog.PrefabName(allItem); if (!string.IsNullOrEmpty(text2)) { if (text == null) { text = text2; } else if (!string.Equals(text, text2, StringComparison.Ordinal)) { mixed = true; } } } return text; } internal bool Allows(ItemData item) { if (item == null) { return true; } return AllowsPrefab(MeadCatalog.PrefabName(item)); } internal bool AllowsPrefab(GameObject prefab) { if (Object.op_Implicit((Object)(object)prefab)) { return AllowsPrefab(((Object)prefab).name); } return false; } internal bool AllowsPrefab(string prefabName) { if (!MeadCatalog.IsSupported(prefabName)) { return false; } bool mixed; string text = ResolveAssignmentFromInventory(out mixed); if (string.IsNullOrEmpty(text)) { text = assignedPrefab; } if (!string.IsNullOrEmpty(text)) { return string.Equals(text, prefabName, StringComparison.Ordinal); } return true; } internal void NotifyRejected(string attemptedPrefab) { if (Time.unscaledTime - lastRejectMessageTime < 0.6f) { return; } lastRejectMessageTime = Time.unscaledTime; string text; if (!MeadCatalog.IsSupported(attemptedPrefab)) { text = "Only finished mead can be stored in this keg."; } else { bool mixed; string text2 = ResolveAssignmentFromInventory(out mixed); if (string.IsNullOrEmpty(text2)) { text2 = assignedPrefab; } string text3 = MeadCatalog.DisplayName(text2); text = (string.IsNullOrEmpty(text3) ? "This keg already contains a different mead." : ("This keg can only hold " + text3 + ".")); } Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer)) { ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false); } } internal int TryQuickGrab(Player player, int requested) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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_010f: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)player) || requested <= 0 || !Object.op_Implicit((Object)(object)container)) { return 0; } Inventory val = container.GetInventory(); Inventory val2 = ((Humanoid)player).GetInventory(); if (val == null || val2 == null) { return 0; } bool mixed; string text = ResolveAssignmentFromInventory(out mixed); if (string.IsNullOrEmpty(text)) { return 0; } int num = requested; int num2 = 0; foreach (ItemData item in new List<ItemData>(val.GetAllItems())) { if (num <= 0) { break; } if (item == null || item.m_stack <= 0 || !string.Equals(MeadCatalog.PrefabName(item), text, StringComparison.Ordinal)) { continue; } int num3 = Math.Min(num, item.m_stack); int num4 = 0; for (int num5 = num3; num5 >= 1; num5--) { ItemData val3 = item.Clone(); val3.m_stack = num5; val3.m_gridPos = new Vector2i(-1, -1); if (val2.CanAddItem(val3, num5)) { num4 = num5; break; } } if (num4 <= 0) { continue; } ItemData val4 = item.Clone(); val4.m_stack = num4; val4.m_gridPos = new Vector2i(-1, -1); if (val2.AddItem(val4)) { if (!val.RemoveItem(item, num4)) { Logger.LogWarning((object)$"{((Object)((Component)this).gameObject).name}: quick grab added {num4} item(s) to the player but keg removal unexpectedly failed."); break; } num2 += num4; num -= num4; } } if (num2 > 0) { RefreshState(forceVisual: true); MeadKegEffects.PlayTapEffect(((Component)this).GetComponent<MeadKegVisual>()); } return num2; } internal string BuildHoverText(string vanillaText) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) string text = (string.IsNullOrEmpty(assignedPrefab) ? null : MeadCatalog.DisplayName(assignedPrefab)); string text2 = (string.IsNullOrEmpty(text) ? "Mead Keg" : ("Mead Keg - " + text)); string text3 = vanillaText ?? ""; int num = text3.IndexOf('\n'); if (num >= 0) { text3 = text3.Substring(num + 1); } else if (!string.IsNullOrWhiteSpace(text3)) { text3 = "[<color=yellow><b>$KEY_Use</b></color>] Open"; } if (string.IsNullOrEmpty(text3)) { text3 = "[<color=yellow><b>$KEY_Use</b></color>] Open"; } if (!string.IsNullOrEmpty(text)) { DynamicStorageMeadHallPlugin current = DynamicStorageMeadHallPlugin.Current; int num2 = (((Object)(object)current != (Object)null) ? current.QuickGrabAmount : 5); string text4 = (((Object)(object)current != (Object)null) ? ((object)current.QuickGrabShortcut/*cast due to .constrained prefix*/).ToString() : "LeftShift + E"); text3 = text3 + "\n[<color=yellow><b>" + text4 + "</b></color>] Grab " + num2 + " " + text; } return text2 + "\n" + text3; } private void UnregisterInventory() { if (inventory != null) { if (Registry.TryGetValue(inventory, out var value) && (Object)(object)value == (Object)(object)this) { Registry.Remove(inventory); } inventory = null; } } internal static MeadKegStorage ForInventory(Inventory target) { if (target == null) { return null; } if (!Registry.TryGetValue(target, out var value)) { return null; } return value; } } internal static class MeadKegInventoryPatches { internal static bool Install(Harmony harmony) { if (harmony == null) { return false; } return (byte)(1u & (PatchPrefix(harmony, AccessTools.Method(typeof(Inventory), "CanAddItem", new Type[2] { typeof(ItemData), typeof(int) }, (Type[])null), "CanAddItemData") ? 1u : 0u) & (PatchPrefix(harmony, AccessTools.Method(typeof(Inventory), "CanAddItem", new Type[2] { typeof(GameObject), typeof(int) }, (Type[])null), "CanAddPrefab") ? 1u : 0u) & (PatchPrefix(harmony, AccessTools.Method(typeof(Inventory), "AddItem", new Type[1] { typeof(ItemData) }, (Type[])null), "AddItemData") ? 1u : 0u) & (PatchPrefix(harmony, AccessTools.Method(typeof(Inventory), "AddItem", new Type[5] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) }, (Type[])null), "AddItemAt") ? 1u : 0u) & (PatchPrefix(harmony, AccessTools.Method(typeof(Inventory), "MoveItemToThis", new Type[2] { typeof(Inventory), typeof(ItemData) }, (Type[])null), "MoveItemToThisSimple") ? 1u : 0u) & (PatchPrefix(harmony, AccessTools.Method(typeof(Inventory), "MoveItemToThis", new Type[5] { typeof(Inventory), typeof(ItemData), typeof(int), typeof(int), typeof(int) }, (Type[])null), "MoveItemToThisAt") ? 1u : 0u)) != 0; } private static bool PatchPrefix(Harmony harmony, MethodBase target, string prefixName) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown if (target == null) { Logger.LogWarning((object)"A mead-keg inventory restriction hook was not found in this Valheim build."); return false; } MethodInfo methodInfo = AccessTools.Method(typeof(MeadKegInventoryPatches), prefixName, (Type[])null, (Type[])null); try { harmony.Patch(target, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Logger.LogWarning((object)("Mead-keg restriction hook failed: " + ex.Message)); return false; } } private static bool CanAddItemData(Inventory __instance, ItemData item, ref bool __result) { MeadKegStorage meadKegStorage = MeadKegStorage.ForInventory(__instance); if (!Object.op_Implicit((Object)(object)meadKegStorage) || meadKegStorage.Allows(item)) { return true; } __result = false; return false; } private static bool CanAddPrefab(Inventory __instance, GameObject prefab, ref bool __result) { MeadKegStorage meadKegStorage = MeadKegStorage.ForInventory(__instance); if (!Object.op_Implicit((Object)(object)meadKegStorage) || meadKegStorage.AllowsPrefab(prefab)) { return true; } __result = false; return false; } private static bool AddItemData(Inventory __instance, ItemData item, ref bool __result) { MeadKegStorage meadKegStorage = MeadKegStorage.ForInventory(__instance); if (!Object.op_Implicit((Object)(object)meadKegStorage) || meadKegStorage.Allows(item)) { return true; } meadKegStorage.NotifyRejected(MeadCatalog.PrefabName(item)); __result = false; return false; } private static bool AddItemAt(Inventory __instance, ItemData item, ref bool __result) { MeadKegStorage meadKegStorage = MeadKegStorage.ForInventory(__instance); if (!Object.op_Implicit((Object)(object)meadKegStorage) || meadKegStorage.Allows(item)) { return true; } meadKegStorage.NotifyRejected(MeadCatalog.PrefabName(item)); __result = false; return false; } private static bool MoveItemToThisSimple(Inventory __instance, Inventory fromInventory, ItemData item) { MeadKegStorage meadKegStorage = MeadKegStorage.ForInventory(__instance); if ((Object)(object)meadKegStorage == (Object)null || fromInventory == __instance || meadKegStorage.Allows(item)) { return true; } RejectMoveAndRestore(meadKegStorage, fromInventory, item); return false; } private static bool MoveItemToThisAt(Inventory __instance, Inventory fromInventory, ItemData item) { MeadKegStorage meadKegStorage = MeadKegStorage.ForInventory(__instance); if ((Object)(object)meadKegStorage == (Object)null || fromInventory == __instance || meadKegStorage.Allows(item)) { return true; } RejectMoveAndRestore(meadKegStorage, fromInventory, item); return false; } private static void RejectMoveAndRestore(MeadKegStorage storage, Inventory fromInventory, ItemData item) { //IL_0027: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)storage)) { storage.NotifyRejected(MeadCatalog.PrefabName(item)); } if (fromInventory == null || item == null || ContainsReference(fromInventory, item)) { return; } try { Vector2i gridPos = item.m_gridPos; if (fromInventory.AddItem(item)) { Logger.LogInfo((object)("Rejected incompatible Mead Keg swap and restored '" + MeadCatalog.PrefabName(item) + "' " + $"to the source inventory (previous grid {gridPos.x},{gridPos.y}).")); } else { Logger.LogError((object)("Rejected incompatible Mead Keg swap but could not restore '" + MeadCatalog.PrefabName(item) + "' to the source inventory. Leave the inventories open and report this log before moving more items.")); } } catch (Exception ex) { Logger.LogError((object)("Rejected incompatible Mead Keg swap restoration failed: " + ex)); } } private static bool ContainsReference(Inventory inventory, ItemData item) { if (inventory == null || item == null) { return false; } List<ItemData> allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item2 in allItems) { if (item2 == item) { return true; } } return false; } } public sealed class MeadKegVisual : MonoBehaviour { private const int DiscSegments = 32; private static readonly HashSet<MeadKegVisual> Active = new HashSet<MeadKegVisual>(); private Transform labelAnchor; private Transform tapEffectAnchor; private Transform idleEffectAnchor; private MeshRenderer labelRenderer; private MeshFilter labelFilter; private Mesh labelMesh; private Material labelMaterial; private GameObject idleEffect; private int currentAtlasIndex = -1; private bool faceFrameResolved; private Vector3 faceCenterLocal; private Vector3 faceOutwardLocal = Vector3.forward; private Vector3 faceRightLocal = Vector3.right; private Vector3 faceUpLocal = Vector3.up; private Vector3 tapPointLocal; private Quaternion faceRotationLocal = Quaternion.identity; internal Transform TapEffectAnchor { get { EnsureLabel(); if (!Object.op_Implicit((Object)(object)tapEffectAnchor)) { return ((Component)this).transform; } return tapEffectAnchor; } } internal Transform IdleEffectAnchor { get { EnsureLabel(); if (!Object.op_Implicit((Object)(object)idleEffectAnchor)) { return ((Component)this).transform; } return idleEffectAnchor; } } internal float TapEffectPlaybackScale { get { DynamicStorageMeadHallPlugin current = DynamicStorageMeadHallPlugin.Current; if (!((Object)(object)current != (Object)null)) { return 1f; } return current.TapEffectScaleMultiplier; } } private void OnEnable() { Active.Add(this); } private void OnDisable() { Active.Remove(this); } private void Start() { if (!string.Equals(((Object)((Component)this).gameObject).name, "DSMK_MeadKeg", StringComparison.Ordinal)) { EnsureLabel(); MeadKegStorage component = ((Component)this).GetComponent<MeadKegStorage>(); Refresh(Object.op_Implicit((Object)(object)component) ? component.AssignedPrefabName : null); } } private void OnDestroy() { Active.Remove(this); if (Object.op_Implicit((Object)(object)labelMaterial)) { Object.Destroy((Object)(object)labelMaterial); } if (Object.op_Implicit((Object)(object)labelMesh)) { Object.Destroy((Object)(object)labelMesh); } } internal static void RefreshAllTransforms() { MeadKegVisual[] array = Active.ToArray(); foreach (MeadKegVisual meadKegVisual in array) { if (Object.op_Implicit((Object)(object)meadKegVisual)) { MeadKegStorage component = ((Component)meadKegVisual).GetComponent<MeadKegStorage>(); meadKegVisual.Refresh(Object.op_Implicit((Object)(object)component) ? component.AssignedPrefabName : null); } } } internal static MeadKegVisual FindNearestPlaced(Vector3 worldPosition, float maxDistance) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) MeadKegVisual result = null; float num = maxDistance * maxDistance; MeadKegVisual[] array = Active.ToArray(); foreach (MeadKegVisual meadKegVisual in array) { if (!Object.op_Implicit((Object)(object)meadKegVisual) || string.Equals(((Object)((Component)meadKegVisual).gameObject).name, "DSMK_MeadKeg", StringComparison.Ordinal)) { continue; } ZNetView component = ((Component)meadKegVisual).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.GetZDO() != null) { Vector3 val = ((Component)meadKegVisual).transform.position - worldPosition; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude <= num) { num = sqrMagnitude; result = meadKegVisual; } } } return result; } internal void Refresh(string assignedPrefab) { EnsureLabel(); MeadKegScale component = ((Component)this).GetComponent<MeadKegScale>(); int num = MeadCatalog.AtlasIndexForLabelMode(Object.op_Implicit((Object)(object)component) ? component.CurrentLabelMode : "Dynamic", assignedPrefab); if (currentAtlasIndex != num) { currentAtlasIndex = num; ApplyAtlasIndex(num); } RefreshTransform(); RefreshIdleEffect(!string.IsNullOrEmpty(assignedPrefab)); } internal void RefreshTransform() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due