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 Ammunition Piles v1.0.3
plugins/DynamicStorageAmmunitionPiles/DynamicStorageAmmunitionPiles.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.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; [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("DynamicStorageAmmunitionPiles")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: AssemblyInformationalVersion("1.0.2")] [assembly: AssemblyProduct("DynamicStorageAmmunitionPiles")] [assembly: AssemblyTitle("DynamicStorageAmmunitionPiles")] [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 DynamicStorageAmmunitionPiles { public sealed class AmmunitionBarrelAppearance : MonoBehaviour { private const string ZdoScaleKey = "DSAP.Appearance.Scale"; private const string ZdoHeightOffsetKey = "DSAP.Appearance.HeightOffset"; private const string ZdoLooksFullAtKey = "DSAP.Appearance.LooksFullAt"; private const string ZdoAmmoModelScaleKey = "DSAP.Appearance.AmmoModelScale"; private const string ZdoFillBasisKey = "DSAP.Appearance.FillBasis"; internal const string DefaultLooksFullAt = "50%"; internal const string DefaultFillBasis = "Occupied Slots"; internal const float DefaultAmmoModelScale = 0.8f; internal static readonly string[] LooksFullAtOptions = new string[5] { "25%", "50%", "75%", "100%", "Always Full" }; internal static readonly string[] FillBasisOptions = new string[2] { "Occupied Slots", "Storage Capacity" }; [SerializeField] private Vector3 baseScale = Vector3.one; private ZNetView nview; private AmmunitionBarrelFamily family; private bool applied; private float nextCheck; private float currentScaleMultiplier = 1f; private float currentHeightOffset; private float currentAmmoModelScale = 0.8f; private string currentFillBasis = "Occupied Slots"; private string currentLooksFullAt = "50%"; private bool pendingPlacementSnapshot; private float pendingScale; private float pendingHeightOffset; private float pendingAmmoModelScale; private string pendingFillBasis; private string pendingLooksFullAt; private float pendingDeadline; private float startedAt; internal float CurrentScaleMultiplier => currentScaleMultiplier; internal float CurrentHeightOffset => currentHeightOffset; internal float CurrentAmmoModelScale => currentAmmoModelScale; internal string CurrentFillBasis => NormalizeFillBasis(currentFillBasis); internal string CurrentLooksFullAt => NormalizeLooksFullAt(currentLooksFullAt); internal AmmunitionKind FamilyKind { get { if (!Object.op_Implicit((Object)(object)family)) { return AmmunitionKind.Arrow; } return family.Kind; } } 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>(); family = ((Component)this).GetComponent<AmmunitionBarrelFamily>(); ApplyOrSave(); } private void Update() { if (!applied && !(Time.unscaledTime < nextCheck)) { nextCheck = Time.unscaledTime + 0.25f; ApplyOrSave(); } } private BarrelAppearanceTemplate CurrentTemplate() { if (!Object.op_Implicit((Object)(object)family)) { family = ((Component)this).GetComponent<AmmunitionBarrelFamily>(); } DynamicStorageAmmunitionPilesPlugin current = DynamicStorageAmmunitionPilesPlugin.Current; if ((Object)(object)current != (Object)null) { return current.GetAppearanceTemplate(Object.op_Implicit((Object)(object)family) ? family.Kind : AmmunitionKind.Arrow); } return new BarrelAppearanceTemplate(1f, 0f, 0.8f, "Occupied Slots", "50%"); } private void ApplyOrSave() { if (!Object.op_Implicit((Object)(object)nview)) { nview = ((Component)this).GetComponent<ZNetView>(); } if (!Object.op_Implicit((Object)(object)family)) { family = ((Component)this).GetComponent<AmmunitionBarrelFamily>(); } if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null) { return; } if (pendingPlacementSnapshot) { TryCommitPendingPlacementSnapshot(); } BarrelAppearanceTemplate barrelAppearanceTemplate = CurrentTemplate(); ZDO zDO = nview.GetZDO(); float num = zDO.GetFloat("DSAP.Appearance.Scale", -1f); bool flag = num > 0f; float num2 = zDO.GetFloat("DSAP.Appearance.HeightOffset", float.NaN); bool flag2 = !float.IsNaN(num2); float num3 = zDO.GetFloat("DSAP.Appearance.AmmoModelScale", -1f); bool flag3 = num3 > 0f; string text = zDO.GetString("DSAP.Appearance.FillBasis", ""); bool flag4 = !string.IsNullOrEmpty(text); string text2 = zDO.GetString("DSAP.Appearance.LooksFullAt", ""); bool flag5 = !string.IsNullOrEmpty(text2); if ((!flag || !flag2 || !flag3 || !flag4 || !flag5) && nview.IsOwner()) { if (!flag) { num = barrelAppearanceTemplate.BarrelScale; zDO.Set("DSAP.Appearance.Scale", num); } if (!flag2) { num2 = barrelAppearanceTemplate.HeightOffset; zDO.Set("DSAP.Appearance.HeightOffset", num2); } if (!flag3) { num3 = barrelAppearanceTemplate.AmmoModelScale; zDO.Set("DSAP.Appearance.AmmoModelScale", num3); } if (!flag4) { text = NormalizeFillBasis(barrelAppearanceTemplate.FillBasis); zDO.Set("DSAP.Appearance.FillBasis", text); } if (!flag5) { if (Time.time - startedAt < 1f && !pendingPlacementSnapshot) { return; } text2 = NormalizeLooksFullAt(barrelAppearanceTemplate.LooksFullAt); zDO.Set("DSAP.Appearance.LooksFullAt", text2); } } if (num <= 0f) { num = barrelAppearanceTemplate.BarrelScale; } if (float.IsNaN(num2)) { num2 = barrelAppearanceTemplate.HeightOffset; } if (num3 <= 0f) { num3 = barrelAppearanceTemplate.AmmoModelScale; } text = NormalizeFillBasis(string.IsNullOrEmpty(text) ? barrelAppearanceTemplate.FillBasis : text); text2 = NormalizeLooksFullAt(string.IsNullOrEmpty(text2) ? barrelAppearanceTemplate.LooksFullAt : text2); ApplyAppearance(num, num2, num3, text, text2); applied = true; } internal void SavePlacementDefaults() { BarrelAppearanceTemplate barrelAppearanceTemplate = CurrentTemplate(); pendingScale = barrelAppearanceTemplate.BarrelScale; pendingHeightOffset = barrelAppearanceTemplate.HeightOffset; pendingAmmoModelScale = barrelAppearanceTemplate.AmmoModelScale; pendingFillBasis = barrelAppearanceTemplate.FillBasis; pendingLooksFullAt = barrelAppearanceTemplate.LooksFullAt; 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 + ": Ammunition Barrel 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("DSAP.Appearance.Scale", Mathf.Clamp(pendingScale, 0.5f, 3f)); zDO.Set("DSAP.Appearance.HeightOffset", Mathf.Clamp(pendingHeightOffset, -2f, 2f)); zDO.Set("DSAP.Appearance.AmmoModelScale", Mathf.Clamp(pendingAmmoModelScale, 0.35f, 2.5f)); zDO.Set("DSAP.Appearance.FillBasis", NormalizeFillBasis(pendingFillBasis)); zDO.Set("DSAP.Appearance.LooksFullAt", NormalizeLooksFullAt(pendingLooksFullAt)); pendingPlacementSnapshot = false; Logger.LogInfo((object)(((Object)((Component)this).gameObject).name + ": saved per-barrel scale/height/ammo-model/fill-basis/looks-full-at placement snapshot.")); return true; } internal void ApplyPlacementPreview(float scaleMultiplier) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) scaleMultiplier = Mathf.Clamp(scaleMultiplier, 0.5f, 3f); currentScaleMultiplier = scaleMultiplier; ((Component)this).transform.localScale = new Vector3(baseScale.x * scaleMultiplier, baseScale.y * scaleMultiplier, baseScale.z * scaleMultiplier); } internal bool ApplyCurrentTemplateFromConfig(out string result) { //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)nview)) { nview = ((Component)this).GetComponent<ZNetView>(); } if (!Object.op_Implicit((Object)(object)family)) { family = ((Component)this).GetComponent<AmmunitionBarrelFamily>(); } if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null) { result = "Placed barrel has no writable ZDO."; return false; } if (!nview.IsOwner()) { result = "Cannot apply: this client is not the ZDO owner."; return false; } BarrelAppearanceTemplate barrelAppearanceTemplate = CurrentTemplate(); float num = Mathf.Clamp(barrelAppearanceTemplate.BarrelScale, 0.5f, 3f); float num2 = Mathf.Clamp(barrelAppearanceTemplate.HeightOffset, -2f, 2f); float num3 = Mathf.Clamp(barrelAppearanceTemplate.AmmoModelScale, 0.35f, 2.5f); string text = NormalizeFillBasis(barrelAppearanceTemplate.FillBasis); string text2 = NormalizeLooksFullAt(barrelAppearanceTemplate.LooksFullAt); float num4 = num2 - currentHeightOffset; ZDO zDO = nview.GetZDO(); zDO.Set("DSAP.Appearance.Scale", num); zDO.Set("DSAP.Appearance.HeightOffset", num2); zDO.Set("DSAP.Appearance.AmmoModelScale", num3); zDO.Set("DSAP.Appearance.FillBasis", text); zDO.Set("DSAP.Appearance.LooksFullAt", text2); if (Mathf.Abs(num4) > 0.0001f) { Transform transform = ((Component)this).transform; transform.position += Vector3.up * num4; try { zDO.SetPosition(((Component)this).transform.position); } catch (Exception ex) { Logger.LogWarning((object)(((Object)((Component)this).gameObject).name + ": applied height offset locally but could not write ZDO position: " + ex.Message)); } } ApplyAppearance(num, num2, num3, text, text2); applied = true; string text3 = AmmunitionCatalog.FamilyDisplayName(FamilyKind); result = "Applied current " + text3 + " barrel appearance configuration."; Logger.LogInfo((object)(((Object)((Component)this).gameObject).name + ": " + result)); return true; } private void ApplyAppearance(float scaleMultiplier, float heightOffset, float ammoModelScale, string fillBasis, string looksFullAt) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) scaleMultiplier = Mathf.Clamp(scaleMultiplier, 0.5f, 3f); currentScaleMultiplier = scaleMultiplier; currentHeightOffset = Mathf.Clamp(heightOffset, -2f, 2f); currentAmmoModelScale = Mathf.Clamp(ammoModelScale, 0.35f, 2.5f); currentFillBasis = NormalizeFillBasis(fillBasis); currentLooksFullAt = NormalizeLooksFullAt(looksFullAt); ((Component)this).transform.localScale = new Vector3(baseScale.x * scaleMultiplier, baseScale.y * scaleMultiplier, baseScale.z * scaleMultiplier); AmmunitionBarrelVisual component = ((Component)this).GetComponent<AmmunitionBarrelVisual>(); AmmunitionBarrelStorage component2 = ((Component)this).GetComponent<AmmunitionBarrelStorage>(); if (Object.op_Implicit((Object)(object)component)) { component.Refresh(Object.op_Implicit((Object)(object)component2) ? component2.AssignedPrefabName : null, Object.op_Implicit((Object)(object)component2) ? component2.StoredCount : 0); } } internal static string NormalizeFillBasis(string value) { if (string.Equals(value, "Occupied Slots", StringComparison.OrdinalIgnoreCase)) { return "Occupied Slots"; } if (string.Equals(value, "Stored Ammo Count", StringComparison.OrdinalIgnoreCase)) { return "Occupied Slots"; } if (string.Equals(value, "Stored Ammunition Count", StringComparison.OrdinalIgnoreCase)) { return "Occupied Slots"; } if (string.Equals(value, "Stored Item Count", StringComparison.OrdinalIgnoreCase)) { return "Occupied Slots"; } string.Equals(value, "Occupied Slots", StringComparison.OrdinalIgnoreCase); return "Occupied Slots"; } internal static string NormalizeLooksFullAt(string value) { if (string.Equals(value, "25%", StringComparison.OrdinalIgnoreCase)) { return "25%"; } if (string.Equals(value, "75%", StringComparison.OrdinalIgnoreCase)) { return "75%"; } if (string.Equals(value, "100%", StringComparison.OrdinalIgnoreCase)) { return "100%"; } if (string.Equals(value, "Always Full", StringComparison.OrdinalIgnoreCase)) { return "Always Full"; } return "50%"; } internal static float LooksFullFraction(string value) { value = NormalizeLooksFullAt(value); return value switch { "25%" => 0.25f, "75%" => 0.75f, "100%" => 1f, "Always Full" => 0f, _ => 0.5f, }; } } internal static class AmmunitionBarrelAppearanceEditorPatches { private static int lastApplyFrame = -1; internal static bool Install(Harmony harmony) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown if (harmony == null) { return false; } MethodInfo methodInfo = AccessTools.Method(typeof(Container), "GetHoverText", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(AmmunitionBarrelAppearanceEditorPatches), "GetHoverTextPostfix", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Logger.LogWarning((object)"Container.GetHoverText() not found; Ammunition Barrel appearance editor is unavailable."); 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)("Could not install Ammunition Barrel appearance editor hover: " + ex.Message)); return false; } } private static void GetHoverTextPostfix(Container __instance, ref string __result) { try { if (!Object.op_Implicit((Object)(object)__instance)) { return; } DynamicStorageAmmunitionPilesPlugin current = DynamicStorageAmmunitionPilesPlugin.Current; AmmunitionBarrelCapacity component = ((Component)__instance).GetComponent<AmmunitionBarrelCapacity>(); if (Object.op_Implicit((Object)(object)component)) { __result += component.SafetyWarning; if ((Object)(object)current != (Object)null && current.ShowStorageDebugTooltip) { __result += component.DebugStatus; } } if ((Object)(object)current == (Object)null || !current.AppearanceEditorEnabled) { return; } AmmunitionBarrelAppearance component2 = ((Component)__instance).GetComponent<AmmunitionBarrelAppearance>(); if (!Object.op_Implicit((Object)(object)component2)) { return; } if (Input.GetKeyDown((KeyCode)114) && Time.frameCount != lastApplyFrame) { lastApplyFrame = Time.frameCount; string result; bool num = component2.ApplyCurrentTemplateFromConfig(out result); Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer)) { ((Character)localPlayer).Message((MessageType)2, result, 0, (Sprite)null, false); } if (!num) { Logger.LogWarning((object)(((Object)((Component)__instance).gameObject).name + ": appearance editor apply rejected: " + result)); } } __result += BuildEditorHoverText(component2); } catch (Exception ex) { Logger.LogWarning((object)("Ammunition Barrel hover extension failed: " + ex.Message)); } } private static string BuildEditorHoverText(AmmunitionBarrelAppearance appearance) { if (!Object.op_Implicit((Object)(object)appearance)) { return string.Empty; } string text = FamilyTitle(appearance.FamilyKind); DynamicStorageAmmunitionPilesPlugin current = DynamicStorageAmmunitionPilesPlugin.Current; int num = (((Object)(object)current != (Object)null) ? current.BarrelStorageCapacity : 20); return "\n<color=#5EC8FF><b>BARREL APPEARANCE EDITOR</b></color>\n<color=#AAB4BC>" + text.ToUpperInvariant() + "</color>\n<color=#8AD7FF>Barrel scale:</color> <color=#FFFFFF>" + appearance.CurrentScaleMultiplier.ToString("0.##") + "</color>\n<color=#8AD7FF>Ammo model:</color> <color=#FFFFFF>" + appearance.CurrentAmmoModelScale.ToString("0.##") + "</color>\n<color=#8AD7FF>Height offset:</color> <color=#FFFFFF>" + FormatSigned(appearance.CurrentHeightOffset) + "</color>\n<color=#8AD7FF>Visual fill:</color> <color=#FFFFFF>" + appearance.CurrentFillBasis + " · " + appearance.CurrentLooksFullAt + "</color>\n<color=#8AD7FF>Server capacity:</color> <color=#FFFFFF>" + num + " slots</color>\n<color=#FFD166><b>[R]</b></color> <color=#AAB4BC>Apply current " + text + " config</color>"; } private static string FamilyTitle(AmmunitionKind kind) { return kind switch { AmmunitionKind.CrossbowBolt => "Bolt Barrel", AmmunitionKind.BallistaMissile => "Missile Barrel", _ => "Arrow Barrel", }; } private static string FormatSigned(float value) { if (Mathf.Abs(value) < 0.0005f) { return "0"; } if (!(value > 0f)) { return value.ToString("0.###"); } return "+" + value.ToString("0.###"); } } public sealed class AmmunitionBarrelCapacity : 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<AmmunitionBarrelCapacity> Active = new HashSet<AmmunitionBarrelCapacity>(); 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 barrel is closed. If this warning persists, enable Advanced Debug -> Show Storage Debug Tooltip and check Last safety action.</color>"; } } internal string DebugStatus { get { CacheReferences(); DynamicStorageAmmunitionPilesPlugin current = DynamicStorageAmmunitionPilesPlugin.Current; int num = (((Object)(object)current != (Object)null) ? current.BarrelStorageCapacity : (-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>AMMO BARREL 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() { AmmunitionBarrelCapacity[] array = new AmmunitionBarrelCapacity[Active.Count]; Active.CopyTo(array); AmmunitionBarrelCapacity[] array2 = array; foreach (AmmunitionBarrelCapacity ammunitionBarrelCapacity in array2) { if (Object.op_Implicit((Object)(object)ammunitionBarrelCapacity)) { ammunitionBarrelCapacity.nextRefresh = 0f; ammunitionBarrelCapacity.ReconcileCapacity(); ammunitionBarrelCapacity.RefreshVisualFill(); } } } internal void InitializeNewPlacement() { CacheReferences(); newPlacementConfirmed = true; nextRefresh = 0f; ReconcileCapacity(); } internal void NotifyContainerInteraction() { CacheReferences(); interactionConfirmed = true; nextRefresh = 0f; ReconcileCapacity(); } private void ReconcileCapacity() { DynamicStorageAmmunitionPilesPlugin current = DynamicStorageAmmunitionPilesPlugin.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.BarrelStorageCapacity); } } private void ApplySafeCapacity(int desiredCapacity) { int num = CountStacks(inventory); if (num < 0) { return; } if (!AmmunitionBarrelCapacityPatches.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 = DynamicStorageAmmunitionPilesPlugin.NormalizeStorageCapacity(desiredCapacity); int num4 = Mathf.Max(num3, num2); if (num4 > 80) { EnsureAtLeastMigrationEnvelope(); lastSafetyAction = "Deferred: more than 80 stacks require the recovery envelope"; return; } DynamicStorageAmmunitionPilesPlugin.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)"Ammunition Barrel 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 RefreshVisualFill() { AmmunitionBarrelStorage component = ((Component)this).GetComponent<AmmunitionBarrelStorage>(); AmmunitionBarrelVisual component2 = ((Component)this).GetComponent<AmmunitionBarrelVisual>(); if (Object.op_Implicit((Object)(object)component2)) { component2.Refresh(Object.op_Implicit((Object)(object)component) ? component.AssignedPrefabName : null, Object.op_Implicit((Object)(object)component) ? component.StoredCount : 0); } } 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)("Ammunition Barrel 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)("Ammunition Barrel inventory dimension update failed: " + ex.Message)); } } } internal static class AmmunitionBarrelCapacityPatches { 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(AmmunitionBarrelCapacityPatches), "ContainerAwakePrefix", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Logger.LogError((object)"Ammunition Barrel 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(AmmunitionBarrelCapacityPatches), "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)("Ammunition Barrel 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)("Ammunition Barrel 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<AmmunitionBarrelCapacity>())) { __instance.m_width = 10; __instance.m_height = 10; } } private static void ContainerInteractPrefix(Container __instance) { if (Object.op_Implicit((Object)(object)__instance)) { AmmunitionBarrelCapacity component = ((Component)__instance).GetComponent<AmmunitionBarrelCapacity>(); if (Object.op_Implicit((Object)(object)component)) { component.NotifyContainerInteraction(); } } } } public sealed class AmmunitionBarrelFamily : MonoBehaviour { [SerializeField] private AmmunitionKind kind; internal AmmunitionKind Kind => kind; internal void Setup(AmmunitionKind value) { kind = value; } } internal struct BarrelAppearanceTemplate { internal float BarrelScale; internal float HeightOffset; internal float AmmoModelScale; internal string FillBasis; internal string LooksFullAt; internal BarrelAppearanceTemplate(float barrelScale, float heightOffset, float ammoModelScale, string fillBasis, string looksFullAt) { BarrelScale = barrelScale; HeightOffset = heightOffset; AmmoModelScale = ammoModelScale; FillBasis = fillBasis; LooksFullAt = looksFullAt; } } public sealed class AmmunitionBarrelStorage : MonoBehaviour { private const string ZdoAssignedKey = "DSAP.AssignedAmmo"; private static readonly Dictionary<Inventory, AmmunitionBarrelStorage> Registry = new Dictionary<Inventory, AmmunitionBarrelStorage>(); private Container container; private Inventory inventory; private ZNetView nview; private AmmunitionBarrelFamily family; private string assignedPrefab; private int storedCount; private int occupiedSlotCount; private float nextRefresh; private bool mixedWarningShown; private float lastRejectMessageTime = -10f; internal string AssignedPrefabName => assignedPrefab; internal int StoredCount => storedCount; internal int OccupiedSlotCount => occupiedSlotCount; internal AmmunitionKind AllowedKind { get { if (!Object.op_Implicit((Object)(object)family)) { return AmmunitionKind.Arrow; } return family.Kind; } } 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>(); family = ((Component)this).GetComponent<AmmunitionBarrelFamily>(); RefreshState(forceVisual: true); } private void Update() { if (!(Time.unscaledTime < nextRefresh)) { nextRefresh = Time.unscaledTime + 0.2f; 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)family)) { family = ((Component)this).GetComponent<AmmunitionBarrelFamily>(); } 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; int count; int usedSlots; string text = ResolveAssignmentFromInventory(out mixed, out count, out usedSlots); string text2 = ReadAssignedFromZdo(); string b = ((!string.IsNullOrEmpty(text)) ? text : text2); if (inventory != null && inventory.GetAllItems().Count == 0) { b = null; count = 0; usedSlots = 0; } if (mixed && !mixedWarningShown) { mixedWarningShown = true; Logger.LogWarning((object)(((Object)((Component)this).gameObject).name + ": multiple ammunition types were already present. New inserts will be restricted to the first detected type until the barrel is emptied.")); } if (!mixed) { mixedWarningShown = false; } bool num = !string.Equals(assignedPrefab, b, StringComparison.Ordinal); bool flag = storedCount != count; bool flag2 = occupiedSlotCount != usedSlots; assignedPrefab = b; storedCount = count; occupiedSlotCount = usedSlots; if (Object.op_Implicit((Object)(object)nview) && nview.IsValid() && nview.IsOwner() && nview.GetZDO() != null) { string a = nview.GetZDO().GetString("DSAP.AssignedAmmo", ""); string text3 = assignedPrefab ?? ""; if (!string.Equals(a, text3, StringComparison.Ordinal)) { nview.GetZDO().Set("DSAP.AssignedAmmo", text3); } } if (num || flag || flag2 || forceVisual) { AmmunitionBarrelVisual component = ((Component)this).GetComponent<AmmunitionBarrelVisual>(); if (Object.op_Implicit((Object)(object)component)) { component.Refresh(assignedPrefab, storedCount); } } } private string ReadAssignedFromZdo() { if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid() || nview.GetZDO() == null) { return null; } string text = nview.GetZDO().GetString("DSAP.AssignedAmmo", ""); if (!AmmunitionCatalog.IsSupported(text)) { return null; } return text; } private string ResolveAssignmentFromInventory(out bool mixed, out int count, out int usedSlots) { mixed = false; count = 0; usedSlots = 0; if (inventory == null) { return null; } string text = null; foreach (ItemData allItem in inventory.GetAllItems()) { string text2 = AmmunitionCatalog.PrefabName(allItem); if (!string.IsNullOrEmpty(text2)) { if (text == null) { text = text2; } else if (!string.Equals(text, text2, StringComparison.Ordinal)) { mixed = true; } if (string.Equals(text, text2, StringComparison.Ordinal)) { count += Mathf.Max(0, allItem.m_stack); usedSlots++; } } } return text; } internal bool Allows(ItemData item) { if (item == null) { return true; } return AllowsPrefab(AmmunitionCatalog.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) { prefabName = AmmunitionCatalog.NormalizePrefabName(prefabName); if (!AmmunitionCatalog.IsSupported(prefabName)) { return false; } if (!AmmunitionCatalog.TryGetKind(prefabName, out var kind) || kind != AllowedKind) { return false; } bool mixed; int count; int usedSlots; string text = ResolveAssignmentFromInventory(out mixed, out count, out usedSlots); 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 = AmmunitionCatalog.FamilyDisplayName(AllowedKind); string text2; if (!AmmunitionCatalog.IsSupported(attemptedPrefab) || !AmmunitionCatalog.TryGetKind(attemptedPrefab, out var kind) || kind != AllowedKind) { text2 = "This barrel only accepts " + text + "."; } else { bool mixed; int count; int usedSlots; string text3 = ResolveAssignmentFromInventory(out mixed, out count, out usedSlots); if (string.IsNullOrEmpty(text3)) { text3 = assignedPrefab; } string text4 = AmmunitionCatalog.DisplayName(text3); text2 = (string.IsNullOrEmpty(text4) ? "This barrel already contains a different ammunition type." : ("This barrel can only hold " + text4 + ".")); } Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer)) { ((Character)localPlayer).Message((MessageType)2, text2, 0, (Sprite)null, false); } } 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 AmmunitionBarrelStorage ForInventory(Inventory target) { if (target == null) { return null; } if (!Registry.TryGetValue(target, out var value)) { return null; } return value; } } internal static class AmmunitionBarrelInventoryPatches { 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)"An Ammunition Barrel inventory restriction hook was not found in this Valheim build."); return false; } MethodInfo methodInfo = AccessTools.Method(typeof(AmmunitionBarrelInventoryPatches), 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)("Ammunition Barrel restriction hook failed: " + ex.Message)); return false; } } private static bool CanAddItemData(Inventory __instance, ItemData item, ref bool __result) { AmmunitionBarrelStorage ammunitionBarrelStorage = AmmunitionBarrelStorage.ForInventory(__instance); if (!Object.op_Implicit((Object)(object)ammunitionBarrelStorage) || ammunitionBarrelStorage.Allows(item)) { return true; } __result = false; return false; } private static bool CanAddPrefab(Inventory __instance, GameObject prefab, ref bool __result) { AmmunitionBarrelStorage ammunitionBarrelStorage = AmmunitionBarrelStorage.ForInventory(__instance); if (!Object.op_Implicit((Object)(object)ammunitionBarrelStorage) || ammunitionBarrelStorage.AllowsPrefab(prefab)) { return true; } __result = false; return false; } private static bool AddItemData(Inventory __instance, ItemData item, ref bool __result) { AmmunitionBarrelStorage ammunitionBarrelStorage = AmmunitionBarrelStorage.ForInventory(__instance); if (!Object.op_Implicit((Object)(object)ammunitionBarrelStorage) || ammunitionBarrelStorage.Allows(item)) { return true; } ammunitionBarrelStorage.NotifyRejected(AmmunitionCatalog.PrefabName(item)); __result = false; return false; } private static bool AddItemAt(Inventory __instance, ItemData item, ref bool __result) { AmmunitionBarrelStorage ammunitionBarrelStorage = AmmunitionBarrelStorage.ForInventory(__instance); if (!Object.op_Implicit((Object)(object)ammunitionBarrelStorage) || ammunitionBarrelStorage.Allows(item)) { return true; } ammunitionBarrelStorage.NotifyRejected(AmmunitionCatalog.PrefabName(item)); __result = false; return false; } private static bool MoveItemToThisSimple(Inventory __instance, Inventory fromInventory, ItemData item) { AmmunitionBarrelStorage ammunitionBarrelStorage = AmmunitionBarrelStorage.ForInventory(__instance); if ((Object)(object)ammunitionBarrelStorage == (Object)null || fromInventory == __instance || ammunitionBarrelStorage.Allows(item)) { return true; } RejectMoveAndRestore(ammunitionBarrelStorage, fromInventory, item); return false; } private static bool MoveItemToThisAt(Inventory __instance, Inventory fromInventory, ItemData item) { AmmunitionBarrelStorage ammunitionBarrelStorage = AmmunitionBarrelStorage.ForInventory(__instance); if ((Object)(object)ammunitionBarrelStorage == (Object)null || fromInventory == __instance || ammunitionBarrelStorage.Allows(item)) { return true; } RejectMoveAndRestore(ammunitionBarrelStorage, fromInventory, item); return false; } private static void RejectMoveAndRestore(AmmunitionBarrelStorage 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(AmmunitionCatalog.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 Ammunition Barrel swap and restored '" + AmmunitionCatalog.PrefabName(item) + "' " + $"to the source inventory (previous grid {gridPos.x},{gridPos.y}).")); } else { Logger.LogError((object)("Rejected incompatible Ammunition Barrel swap but could not restore '" + AmmunitionCatalog.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 Ammunition Barrel 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; } } internal static class AmmunitionBarrelUnlocks { private static readonly Dictionary<AmmunitionKind, Piece> FamilyPieces = new Dictionary<AmmunitionKind, Piece>(); private static readonly FieldInfo AvailableByCategoryField = AccessTools.Field(typeof(PieceTable), "m_availablePiecesByCategory"); private static readonly MethodInfo UpdateAvailablePiecesMethod = AccessTools.Method(typeof(Player), "UpdateAvailablePiecesList", Type.EmptyTypes, (Type[])null); internal static void Install(Harmony harmony) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0076: Expected O, but got Unknown //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown if (harmony != null) { MethodInfo methodInfo = AccessTools.Method(typeof(Player), "AddKnownItem", new Type[1] { typeof(ItemData) }, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(AmmunitionBarrelUnlocks), "AddKnownItemPrefix", (Type[])null, (Type[])null)), new HarmonyMethod(AccessTools.Method(typeof(AmmunitionBarrelUnlocks), "AddKnownItemPostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Logger.LogWarning((object)"Could not install Ammunition Barrel discovery hook: Player.AddKnownItem(ItemData) was not found."); } MethodInfo methodInfo2 = AccessTools.Method(typeof(PieceTable), "UpdateAvailable", new Type[4] { typeof(HashSet<string>), typeof(Player), typeof(bool), typeof(bool) }, (Type[])null); if (methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(AmmunitionBarrelUnlocks), "PieceTableUpdateAvailablePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Logger.LogWarning((object)"Could not install Ammunition Barrel build-menu discovery hook: PieceTable.UpdateAvailable was not found."); } } } internal static void RegisterPiece(AmmunitionKind kind, Piece piece) { if (Object.op_Implicit((Object)(object)piece)) { FamilyPieces[kind] = piece; } } private static void AddKnownItemPrefix(Player __instance, ItemData item, ref bool __state) { __state = false; if (Object.op_Implicit((Object)(object)__instance) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && item != null && AmmunitionCatalog.TryGetKind(item, out var kind)) { __state = AmmunitionCatalog.IsFamilyKnown(__instance, kind); } } private static void AddKnownItemPostfix(Player __instance, ItemData item, bool __state) { if (Object.op_Implicit((Object)(object)__instance) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && item != null && AmmunitionCatalog.TryGetKind(item, out var kind) && !__state && AmmunitionCatalog.IsFamilyKnown(__instance, kind)) { RefreshBuildPieces(__instance); if (FamilyPieces.TryGetValue(kind, out var value) && Object.op_Implicit((Object)(object)value)) { ((Character)__instance).Message((MessageType)2, "New build piece: " + value.m_name, 0, (Sprite)null, false); } } } private static void PieceTableUpdateAvailablePostfix(PieceTable __instance, Player __1) { //IL_0129: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)__instance) || !Object.op_Implicit((Object)(object)__1) || (Object)(object)__1 != (Object)(object)Player.m_localPlayer) { return; } List<List<Piece>> list = null; if (AvailableByCategoryField != null) { try { list = AvailableByCategoryField.GetValue(__instance) as List<List<Piece>>; } catch { } } foreach (KeyValuePair<AmmunitionKind, Piece> familyPiece in FamilyPieces) { Piece value = familyPiece.Value; if (!Object.op_Implicit((Object)(object)value) || !Object.op_Implicit((Object)(object)((Component)value).gameObject) || !__instance.m_pieces.Contains(((Component)value).gameObject)) { continue; } __instance.m_availablePieces.Remove(value); __instance.m_enabledPieces.Remove(value); if (list != null) { foreach (List<Piece> item in list) { item?.Remove(value); } } if (!AmmunitionCatalog.IsFamilyKnown(__1, familyPiece.Key)) { continue; } __instance.m_availablePieces.Add(value); __instance.m_enabledPieces.Add(value); if (list == null) { continue; } int num = __instance.m_categories.IndexOf(value.m_category); if (num >= 0 && num < list.Count) { List<Piece> list2 = list[num]; if (list2 != null && !list2.Contains(value)) { list2.Add(value); } } } } private static void RefreshBuildPieces(Player player) { if (!Object.op_Implicit((Object)(object)player) || UpdateAvailablePiecesMethod == null) { return; } try { UpdateAvailablePiecesMethod.Invoke(player, null); } catch (Exception ex) { Logger.LogWarning((object)("Could not refresh Ammunition Barrel discovery list: " + ex.GetBaseException().Message)); } } } public sealed class AmmunitionBarrelVisual : MonoBehaviour { internal const string BarrelVisualName = "DSAP_OpenBarrelVisual"; private const string VisualRootName = "DSAP_AmmunitionVisuals"; private const int MaxVisibleProjectiles = 20; private Transform barrelVisual; private Transform visualRoot; private readonly List<GameObject> visualInstances = new List<GameObject>(); private string builtPrefabName; private float builtModelScale = -1f; private int pendingStoredCount; private string pendingPrefabName; private bool started; private void Start() { barrelVisual = FindDescendant(((Component)this).transform, "DSAP_OpenBarrelVisual"); EnsureVisualRoot(); started = true; Refresh(pendingPrefabName, pendingStoredCount); } internal void Refresh(string prefabName, int storedCount) { pendingPrefabName = AmmunitionCatalog.NormalizePrefabName(prefabName); pendingStoredCount = Mathf.Max(0, storedCount); if (!started) { return; } if (!Object.op_Implicit((Object)(object)barrelVisual)) { barrelVisual = FindDescendant(((Component)this).transform, "DSAP_OpenBarrelVisual"); } EnsureVisualRoot(); if (!Object.op_Implicit((Object)(object)visualRoot)) { SetVisibleCount(0); return; } if (string.IsNullOrEmpty(pendingPrefabName) || pendingStoredCount <= 0) { SetVisibleCount(0); builtPrefabName = pendingPrefabName; return; } AmmunitionBarrelAppearance component = ((Component)this).GetComponent<AmmunitionBarrelAppearance>(); float num = (Object.op_Implicit((Object)(object)component) ? component.CurrentAmmoModelScale : 0.8f); if (!string.Equals(builtPrefabName, pendingPrefabName, StringComparison.Ordinal) || visualInstances.Count == 0 || Mathf.Abs(builtModelScale - num) > 0.0001f) { RebuildVisuals(pendingPrefabName); } SetVisibleCount(CalculateVisibleCount(pendingPrefabName, pendingStoredCount)); } private void EnsureVisualRoot() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown if (Object.op_Implicit((Object)(object)visualRoot)) { return; } if (!Object.op_Implicit((Object)(object)barrelVisual)) { barrelVisual = FindDescendant(((Component)this).transform, "DSAP_OpenBarrelVisual"); } if (Object.op_Implicit((Object)(object)barrelVisual)) { Transform val = barrelVisual.Find("DSAP_AmmunitionVisuals"); if (Object.op_Implicit((Object)(object)val)) { visualRoot = val; return; } GameObject val2 = new GameObject("DSAP_AmmunitionVisuals"); val2.transform.SetParent(barrelVisual, false); val2.layer = ((Component)barrelVisual).gameObject.layer; visualRoot = val2.transform; } } private int CalculateVisibleCount(string prefabName, int storedCount) { if (storedCount <= 0) { return 0; } AmmunitionBarrelAppearance component = ((Component)this).GetComponent<AmmunitionBarrelAppearance>(); string text = (Object.op_Implicit((Object)(object)component) ? component.CurrentLooksFullAt : "50%"); if (text == "Always Full") { return 20; } DynamicStorageAmmunitionPilesPlugin current = DynamicStorageAmmunitionPilesPlugin.Current; int num = (((Object)(object)current != (Object)null) ? current.BarrelStorageCapacity : 20); float num2 = AmmunitionBarrelAppearance.LooksFullFraction(text); float num3; float num4; if ((Object.op_Implicit((Object)(object)component) ? component.CurrentFillBasis : "Occupied Slots") == "Occupied Slots") { AmmunitionBarrelStorage component2 = ((Component)this).GetComponent<AmmunitionBarrelStorage>(); num3 = (Object.op_Implicit((Object)(object)component2) ? ((float)component2.OccupiedSlotCount) : 1f); num4 = Mathf.Max(1f, (float)num * num2); } else { int maxStackSize = AmmunitionCatalog.GetMaxStackSize(prefabName); num3 = storedCount; num4 = Mathf.Max(1f, (float)(num * maxStackSize) * num2); } return Mathf.Clamp(Mathf.CeilToInt(Mathf.Clamp01(num3 / num4) * 20f), 1, 20); } private void RebuildVisuals(string prefabName) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Expected O, but got Unknown //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_040a: Unknown result type (might be due to invalid IL or missing references) //IL_040c: Unknown result type (might be due to invalid IL or missing references) //IL_0466: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_046d: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_0485: Unknown result type (might be due to invalid IL or missing references) //IL_04f4: Unknown result type (might be due to invalid IL or missing references) ClearVisuals(); builtPrefabName = prefabName; GameObject prefab = AmmunitionCatalog.GetPrefab(prefabName); if (!Object.op_Implicit((Object)(object)prefab)) { Logger.LogWarning((object)("Could not build Ammunition Barrel visuals because prefab was not found: " + prefabName)); return; } if (!Object.op_Implicit((Object)(object)barrelVisual)) { barrelVisual = FindDescendant(((Component)this).transform, "DSAP_OpenBarrelVisual"); } if (!Object.op_Implicit((Object)(object)barrelVisual) || !Object.op_Implicit((Object)(object)visualRoot)) { Logger.LogWarning((object)(((Object)((Component)this).gameObject).name + ": open barrel visual anchor was not available for ammunition rendering.")); return; } MeshFilter component = ((Component)barrelVisual).GetComponent<MeshFilter>(); Mesh val = (Object.op_Implicit((Object)(object)component) ? component.sharedMesh : null); Bounds val2 = (Bounds)(Object.op_Implicit((Object)(object)val) ? val.bounds : new Bounds(new Vector3(0f, 0.5f, 0f), new Vector3(0.88f, 1f, 0.89f))); AmmunitionBarrelAppearance component2 = ((Component)this).GetComponent<AmmunitionBarrelAppearance>(); float num = (Object.op_Implicit((Object)(object)component2) ? component2.CurrentAmmoModelScale : 0.8f); num = (builtModelScale = Mathf.Clamp(num, 0.35f, 2.5f)); GameObject val3 = new GameObject("DSAP_AmmoVisualTemplate"); val3.transform.SetParent(visualRoot, false); val3.layer = ((Component)barrelVisual).gameObject.layer; val3.SetActive(false); GameObject val4 = new GameObject("Geometry"); val4.transform.SetParent(val3.transform, false); val4.layer = val3.layer; int meshRendererCount = 0; int skinnedRendererCount = 0; CloneVisualHierarchy(prefab.transform, val4.transform, val3.layer, isPrefabRoot: true, ref meshRendererCount, ref skinnedRendererCount); if (meshRendererCount + skinnedRendererCount <= 0) { Object.Destroy((Object)(object)val3); Logger.LogWarning((object)("Could not find a renderable ammunition hierarchy for: " + prefabName)); return; } if (!TryCalculateCombinedBounds(val4.transform, out var bounds)) { Object.Destroy((Object)(object)val3); Logger.LogWarning((object)("Could not calculate complete visual bounds for: " + prefabName)); return; } Vector3 size = ((Bounds)(ref bounds)).size; Vector3 val5; float num2; if (size.x >= size.y && size.x >= size.z) { val5 = Vector3.right; num2 = size.x; } else if (size.z >= size.x && size.z >= size.y) { val5 = Vector3.forward; num2 = size.z; } else { val5 = Vector3.up; num2 = size.y; } Quaternion val6 = Quaternion.FromToRotation(val5, Vector3.up); val3.transform.localRotation = val6; val3.transform.localScale = Vector3.one * num; val4.transform.localPosition = -((Bounds)(ref bounds)).center; float num3 = Mathf.Max(0.1f, ((Bounds)(ref val2)).size.y); float num4 = Mathf.Max(0.01f, num2 * num); float num5 = Mathf.Min(((Bounds)(ref val2)).extents.x, ((Bounds)(ref val2)).extents.z) * 0.56f; for (int i = 0; i < 20; i++) { GameObject val7 = Object.Instantiate<GameObject>(val3, visualRoot, false); ((Object)val7).name = "AmmoVisual_" + i.ToString("00"); val7.layer = ((Component)barrelVisual).gameObject.layer; float num6 = (float)i * 137.50777f * ((float)Math.PI / 180f); float num7 = Mathf.Sqrt(((float)i + 0.5f) / 20f); float num8 = num5 * num7; float num9 = Mathf.Cos(num6) * num8; float num10 = Mathf.Sin(num6) * num8; float num11 = num3 * 0.08f; float num12 = ((Bounds)(ref val2)).min.y + num11 + num4 * 0.5f; num12 += ((float)(i * 7 % 9) - 4f) * num3 * 0.018f; val7.transform.localPosition = new Vector3(((Bounds)(ref val2)).center.x + num9, num12, ((Bounds)(ref val2)).center.z + num10); Quaternion val8 = val6; float num13 = Mathf.Repeat((float)i * 137.50777f + 19f, 360f); float num14 = Mathf.Sin((float)(i + 1) * 2.173f) * 10.5f; float num15 = Mathf.Cos((float)(i + 1) * 1.619f) * 9f; val7.transform.localRotation = Quaternion.Euler(num14, num13, num15) * val8; val7.transform.localScale = Vector3.one * num; val7.SetActive(false); visualInstances.Add(val7); } Object.Destroy((Object)(object)val3); Logger.LogInfo((object)($"Built {visualInstances.Count} exact-size dynamic ammunition visuals for {prefabName} " + $"(20-at-full layout; complete hierarchy: MeshRenderers={meshRendererCount}, SkinnedMeshRenderers={skinnedRendererCount}, " + $"vanillaBounds={((Bounds)(ref bounds)).size}, majorLength={num2:0.###}, modelScale={num:0.###}; 1.0 = vanilla gameplay size).")); } private static void CloneVisualHierarchy(Transform source, Transform destinationParent, int layer, bool isPrefabRoot, ref int meshRendererCount, ref int skinnedRendererCount) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)source) && Object.op_Implicit((Object)(object)destinationParent)) { GameObject val = new GameObject(((Object)source).name); Transform transform = val.transform; transform.SetParent(destinationParent, false); transform.localPosition = source.localPosition; transform.localRotation = source.localRotation; transform.localScale = source.localScale; val.layer = layer; val.SetActive(isPrefabRoot || ((Component)source).gameObject.activeSelf); MeshFilter component = ((Component)source).GetComponent<MeshFilter>(); MeshRenderer component2 = ((Component)source).GetComponent<MeshRenderer>(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.sharedMesh) && Object.op_Implicit((Object)(object)component2)) { val.AddComponent<MeshFilter>().sharedMesh = component.sharedMesh; MeshRenderer destination = val.AddComponent<MeshRenderer>(); CopyRendererSettings(component2, destination); meshRendererCount++; } SkinnedMeshRenderer component3 = ((Component)source).GetComponent<SkinnedMeshRenderer>(); if (Object.op_Implicit((Object)(object)component3) && Object.op_Implicit((Object)(object)component3.sharedMesh)) { SkinnedMeshRenderer obj = val.AddComponent<SkinnedMeshRenderer>(); obj.sharedMesh = component3.sharedMesh; ((Renderer)obj).localBounds = ((Renderer)component3).localBounds; ((Renderer)obj).sharedMaterials = ((Renderer)component3).sharedMaterials; ((Renderer)obj).enabled = ((Renderer)component3).enabled; ((Renderer)obj).shadowCastingMode = ((Renderer)component3).shadowCastingMode; ((Renderer)obj).receiveShadows = ((Renderer)component3).receiveShadows; ((Renderer)obj).lightProbeUsage = ((Renderer)component3).lightProbeUsage; ((Renderer)obj).reflectionProbeUsage = ((Renderer)component3).reflectionProbeUsage; ((Renderer)obj).renderingLayerMask = ((Renderer)component3).renderingLayerMask; skinnedRendererCount++; } for (int i = 0; i < source.childCount; i++) { CloneVisualHierarchy(source.GetChild(i), transform, layer, isPrefabRoot: false, ref meshRendererCount, ref skinnedRendererCount); } } } private static void CopyRendererSettings(MeshRenderer source, MeshRenderer destination) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) ((Renderer)destination).sharedMaterials = ((Renderer)source).sharedMaterials; ((Renderer)destination).enabled = ((Renderer)source).enabled; ((Renderer)destination).shadowCastingMode = ((Renderer)source).shadowCastingMode; ((Renderer)destination).receiveShadows = ((Renderer)source).receiveShadows; ((Renderer)destination).lightProbeUsage = ((Renderer)source).lightProbeUsage; ((Renderer)destination).reflectionProbeUsage = ((Renderer)source).reflectionProbeUsage; ((Renderer)destination).renderingLayerMask = ((Renderer)source).renderingLayerMask; } private static bool TryCalculateCombinedBounds(Transform root, out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) bounds = new Bounds(Vector3.zero, Vector3.zero); if (!Object.op_Implicit((Object)(object)root)) { return false; } bool initialized = false; Matrix4x4 worldToLocalMatrix = root.worldToLocalMatrix; MeshFilter[] componentsInChildren = ((Component)root).GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val in componentsInChildren) { if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.sharedMesh)) { MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>(); if (Object.op_Implicit((Object)(object)component) && ((Renderer)component).enabled && IsActiveSelfChain(((Component)val).transform, root)) { EncapsulateTransformedBounds(val.sharedMesh.bounds, worldToLocalMatrix * ((Component)val).transform.localToWorldMatrix, ref bounds, ref initialized); } } } SkinnedMeshRenderer[] componentsInChildren2 = ((Component)root).GetComponentsInChildren<SkinnedMeshRenderer>(true); foreach (SkinnedMeshRenderer val2 in componentsInChildren2) { if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.sharedMesh) && ((Renderer)val2).enabled && IsActiveSelfChain(((Component)val2).transform, root)) { EncapsulateTransformedBounds(((Renderer)val2).localBounds, worldToLocalMatrix * ((Component)val2).transform.localToWorldMatrix, ref bounds, ref initialized); } } return initialized; } private static bool IsActiveSelfChain(Transform transform, Transform root) { if (!Object.op_Implicit((Object)(object)transform) || !Object.op_Implicit((Object)(object)root)) { return false; } Transform val = transform; while (Object.op_Implicit((Object)(object)val)) { if (!((Component)val).gameObject.activeSelf) { return false; } if ((Object)(object)val == (Object)(object)root) { return true; } val = val.parent; } return false; } private static void EncapsulateTransformedBounds(Bounds sourceBounds, Matrix4x4 matrix, ref Bounds combined, ref bool initialized) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0036: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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) //IL_0077: Unknown result type (might be due to invalid IL or missing references) Vector3 min = ((Bounds)(ref sourceBounds)).min; Vector3 max = ((Bounds)(ref sourceBounds)).max; Vector3 val = default(Vector3); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { ((Vector3)(ref val))..ctor((i == 0) ? min.x : max.x, (j == 0) ? min.y : max.y, (k == 0) ? min.z : max.z); Vector3 val2 = ((Matrix4x4)(ref matrix)).MultiplyPoint3x4(val); if (!initialized) { combined = new Bounds(val2, Vector3.zero); initialized = true; } else { ((Bounds)(ref combined)).Encapsulate(val2); } } } } } private void SetVisibleCount(int count) { count = Mathf.Clamp(count, 0, visualInstances.Count); for (int i = 0; i < visualInstances.Count; i++) { GameObject val = visualInstances[i]; if (Object.op_Implicit((Object)(object)val) && val.activeSelf != i < count) { val.SetActive(i < count); } } } private void ClearVisuals() { foreach (GameObject visualInstance in visualInstances) { if (Object.op_Implicit((Object)(object)visualInstance)) { Object.Destroy((Object)(object)visualInstance); } } visualInstances.Clear(); if (!Object.op_Implicit((Object)(object)visualRoot)) { return; } for (int num = visualRoot.childCount - 1; num >= 0; num--) { Transform child = visualRoot.GetChild(num); if (Object.op_Implicit((Object)(object)child)) { Object.Destroy((Object)(object)((Component)child).gameObject); } } } private static Transform FindDescendant(Transform root, string name) { if (!Object.op_Implicit((Object)(object)root)) { return null; } Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (Object.op_Implicit((Object)(object)val) && string.Equals(((Object)val).name, name, StringComparison.Ordinal)) { return val; } } return null; } } internal enum AmmunitionKind { Arrow, CrossbowBolt, BallistaMissile } internal sealed class AmmunitionDefinition { internal readonly string PrefabName; internal readonly string DisplayName; internal readonly AmmunitionKind Kind; internal AmmunitionDefinition(string prefabName, string displayName, AmmunitionKind kind) { PrefabName = prefabName; DisplayName = displayName; Kind = kind; } } internal static class AmmunitionCatalog { private static readonly AmmunitionDefinition[] Candidates = new AmmunitionDefinition[24] { new AmmunitionDefinition("ArrowWood", "Wood arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowFlint", "Flinthead arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowBronze", "Bronzehead arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowFire", "Fire arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowIron", "Ironhead arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowPoison", "Poison arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowObsidian", "Obsidian arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowFrost", "Frost arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowSilver", "Silver arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowNeedle", "Needle arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowCarapace", "Carapace arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowCharred", "Charred arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("ArrowBloodGold", "Bloodgold arrow", AmmunitionKind.Arrow), new AmmunitionDefinition("BoltBone", "Bone bolt", AmmunitionKind.CrossbowBolt), new AmmunitionDefinition("BoltIron", "Iron bolt", AmmunitionKind.CrossbowBolt), new AmmunitionDefinition("BoltBlackmetal", "Black metal bolt", AmmunitionKind.CrossbowBolt), new AmmunitionDefinition("BoltCarapace", "Carapace bolt", AmmunitionKind.CrossbowBolt), new AmmunitionDefinition("BoltCharred", "Charred bolt", AmmunitionKind.CrossbowBolt), new AmmunitionDefinition("BoltBloodGold", "Bloodgold bolt", AmmunitionKind.CrossbowBolt), new AmmunitionDefinition("TurretBoltWood", "Wooden missile", AmmunitionKind.BallistaMissile), new AmmunitionDefinition("TurretBolt", "Black metal missile", AmmunitionKind.BallistaMissile), new AmmunitionDefinition("TurretBoltFlametal", "Flametal missile", AmmunitionKind.BallistaMissile), new AmmunitionDefinition("TurretBoltBloodgold", "Bloodgold missile", AmmunitionKind.BallistaMissile), new AmmunitionDefinition("TurretBoltBone", "Bone missile", AmmunitionKind.BallistaMissile) }; private static readonly Dictionary<string, AmmunitionDefinition> Supported = new Dictionary<string, AmmunitionDefinition>(StringComparer.Ordinal); internal static int SupportedCount => Supported.Count; internal static void Initialize() { Supported.Clear(); AmmunitionDefinition[] candidates = Candidates; foreach (AmmunitionDefinition ammunitionDefinition in candidates) { GameObject prefab = PrefabManager.Instance.GetPrefab(ammunitionDefinition.PrefabName); if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)prefab.GetComponent<ItemDrop>())) { Logger.LogWarning((object)("Ammunition prefab not found or not an ItemDrop: " + ammunitionDefinition.PrefabName)); } else { Supported[ammunitionDefinition.PrefabName] = ammunitionDefinition; } } Logger.LogInfo((object)$"Ammunition catalog initialized: {Supported.Count}/{Candidates.Length} supported vanilla ammunition prefabs found."); } internal static bool IsSupported(string prefabName) { prefabName = NormalizePrefabName(prefabName); if (!string.IsNullOrEmpty(prefabName)) { return Supported.ContainsKey(prefabName); } return false; } internal static bool TryGetKind(string prefabName, out AmmunitionKind kind) { prefabName = NormalizePrefabName(prefabName); if (!string.IsNullOrEmpty(prefabName) && Supported.TryGetValue(prefabName, out var value)) { kind = value.Kind; return true; } kind = AmmunitionKind.Arrow; return false; } internal static bool TryGetKind(ItemData item, out AmmunitionKind kind) { return TryGetKind(PrefabName(item), out kind); } internal static bool IsKind(string prefabName, AmmunitionKind kind) { if (TryGetKind(prefabName, out var kind2)) { return kind2 == kind; } return false; } internal static bool IsFamilyKnown(Player player, AmmunitionKind kind) { if (!Object.op_Implicit((Object)(object)player)) { return false; } foreach (AmmunitionDefinition value in Supported.Values) { if (value.Kind == kind) { GameObject prefab = PrefabManager.Instance.GetPrefab(value.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) && player.IsMaterialKnown(text)) { return true; } } } return false; } internal static string FamilyDisplayName(AmmunitionKind kind) { return kind switch { AmmunitionKind.CrossbowBolt => "crossbow bolts", AmmunitionKind.BallistaMissile => "Ballista missiles", _ => "arrows", }; } internal static string DisplayName(string prefabName) { prefabName = NormalizePrefabName(prefabName); if (string.IsNullOrEmpty(prefabName) || !Supported.TryGetValue(prefabName, out var value)) { return prefabName; } return value.DisplayName; } internal static GameObject GetPrefab(string prefabName) { prefabName = NormalizePrefabName(prefabName); if (!IsSupported(prefabName)) { return null; } return PrefabManager.Instance.GetPrefab(prefabName); } internal static int GetMaxStackSize(string prefabName) { GameObject prefab = GetPrefab(prefabName); ItemDrop val = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null) { return 100; } return Mathf.Max(1, val.m_itemData.m_shared.m_maxStackSize); } internal static string PrefabName(ItemData item) { if (item == null) { return null; } if (Object.op_Implicit((Object)(object)item.m_dropPrefab)) { return NormalizePrefabName(((Object)item.m_dropPrefab).name); } return null; } internal static string NormalizePrefabName(string value) { if (string.IsNullOrEmpty(value)) { return null; } if (value.EndsWith("(Clone)", StringComparison.Ordinal)) { value = value.Substring(0, value.Length - "(Clone)".Length); } return value.Trim(); } } internal sealed class ConfigurationManagerAttributes { public int? Order; public bool? IsAdminOnly; public bool? ReadOnly; public bool? Browsable; public bool? IsAdvanced; } [BepInPlugin("com.muji.DynamicStorageAmmunitionPiles", "DynamicStorage Ammunition Piles", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal sealed class DynamicStorageAmmunitionPilesPlugin : BaseUnityPlugin { private sealed class BarrelDefinition { internal readonly string PrefabName; internal readonly string DisplayName; internal readonly string Description; internal readonly AmmunitionKind Kind; internal BarrelDefinition(string prefabName, string displayName, string description, AmmunitionKind kind) { PrefabName = prefabName; DisplayName = displayName; Description = description; Kind = kind; } } public const string PluginGuid = "com.muji.DynamicStorageAmmunitionPiles"; public const string PluginName = "DynamicStorage Ammunition Piles"; public const string PluginVersion = "1.0.2"; private const string VanillaBaseName = "piece_chest_barrel"; private static readonly BarrelDefinition[] BarrelDefinitions = new BarrelDefinition[3] { new BarrelDefinition("DSAP_ArrowBarrel", "Arrow Barrel", "Stores one arrow type at a time. Revealed after discovering any arrow.", AmmunitionKind.Arrow), new BarrelDefinition("DSAP_BoltBarrel", "Bolt Barrel", "Stores one crossbow bolt type at a time. Revealed after discovering any crossbow bolt.", AmmunitionKind.CrossbowBolt), new BarrelDefinition("DSAP_MissileBarrel", "Missile Barrel", "Stores one Ballista missile type at a time. Revealed after discovering any Ballista missile.", AmmunitionKind.BallistaMissile) }; private ConfigEntry<int> barrelSlots; private ConfigEntry<bool> barrelAppearanceEditor; private ConfigEntry<bool> showStorageDebugTooltip; private ConfigEntry<float> arrowBarrelScale; private ConfigEntry<float> arrowBarrelHeightOffset; private ConfigEntry<float> arrowAmmoModelScale; private ConfigEntry<string> arrowFillBasis; private ConfigEntry<string> arrowLooksFullAt; private ConfigEntry<float> boltBarrelScale; private ConfigEntry<float> boltBarrelHeightOffset; private ConfigEntry<float> boltAmmoModelScale; private ConfigEntry<string> boltFillBasis; private ConfigEntry<string> boltLooksFullAt; private ConfigEntry<float> missileBarrelScale; private ConfigEntry<float> missileBarrelHeightOffset; private ConfigEntry<float> missileAmmoModelScale; private ConfigEntry<string> missileFillBasis; private ConfigEntry<string> missileLooksFullAt; private Harmony harmony; private readonly Dictionary<AmmunitionKind, GameObject> registeredPrefabs = new Dictionary<AmmunitionKind, GameObject>(); private readonly Dictionary<AmmunitionKind, Vector3> registeredBaseScales = new Dictionary<AmmunitionKind, Vector3>(); internal static DynamicStorageAmmunitionPilesPlugin Current { get; private set; } internal int BarrelStorageCapacity => NormalizeStorageCapacity((barrelSlots != null) ? barrelSlots.Value : 20); internal bool AppearanceEditorEnabled { get { if (barrelAppearanceEditor != null) { return barrelAppearanceEditor.Value; } return false; } } 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(); harmon