Latest versions of MelonLoader are known to have issues with some games. Use version 0.5.4 until the issue has been fixed!
Decompiled source of LootDropBugfix v2.1.0
Mods/LootDropBugfix.dll
Decompiled 5 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using HarmonyLib; using Il2CppSystem; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using Sst.LootDropBugfix; using Sst.Utilities; using StressLevelZero.Combat; using StressLevelZero.Data; using StressLevelZero.Pool; using StressLevelZero.Props; using StressLevelZero.Rig; using UnhollowerBaseLib; using UnityEngine; using Valve.VR; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("LootDropBugfix")] [assembly: AssemblyDescription("Fixes bug where dropped loot sometimes does not spawn.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(null)] [assembly: AssemblyProduct("LootDropBugfix")] [assembly: AssemblyCopyright("Created by jakzo")] [assembly: AssemblyTrademark(null)] [assembly: ComVisible(false)] [assembly: AssemblyFileVersion("2.1.0")] [assembly: NeutralResourcesLanguage("en")] [assembly: MelonInfo(typeof(Mod), "LootDropBugfix", "2.1.0", "jakzo", "https://boneworks.thunderstore.io/package/jakzo/LootDropBugfix/")] [assembly: MelonGame("Stress Level Zero", "BONEWORKS")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } public class IsUnmanagedAttribute { } } namespace Sst { public class Metadata { public const string AUTHOR = "jakzo"; public const string COMPANY = null; public const string DEVELOPER = "Stress Level Zero"; public const string GAME = "BONELAB"; public const string GAME_BONEWORKS = "BONEWORKS"; } public class Dbg { private static MelonPreferences_Entry<bool> _prefPrintDebugLogs; public static void Init(string prefCategoryId) { _prefPrintDebugLogs = MelonPreferences.CreateCategory(prefCategoryId).CreateEntry<bool>("printDebugLogs", false, "Print debug logs", "Print debug logs to console", false, true, (ValueValidator)null, (string)null); } public static void Log(params object[] data) { if (_prefPrintDebugLogs.Value) { string text = string.Join(" ", data.Select((object d) => (d != null) ? d.ToString() : "")); MelonLogger.Msg("dbg: " + text); } } } } namespace Sst.Utilities { public class Il2CppNullable<T> : ValueType where T : unmanaged { private static readonly IntPtr classPtr; private static readonly IntPtr TClassPtr; private static readonly int valueSize; private static readonly int marshalSize; private static readonly int hasValueOffset; public IntPtr ValuePtr => IL2CPP.il2cpp_object_unbox(((Il2CppObjectBase)this).Pointer); public unsafe bool HasValue { get { return *(byte*)(void*)(((Il2CppObjectBase)this).Pointer + hasValueOffset) != 0; } set { *(bool*)(void*)(((Il2CppObjectBase)this).Pointer + hasValueOffset) = value; } } public unsafe T Value { get { if (marshalSize == valueSize) { return *(T*)(void*)ValuePtr; } if (valueSize == 1 && marshalSize > 0) { T result = default(T); *(sbyte*)(&result) = *(sbyte*)(void*)ValuePtr; return result; } throw new InvalidOperationException("Interop done goof?"); } set { if (marshalSize == valueSize) { *(T*)(void*)ValuePtr = value; return; } if (valueSize == 1 && marshalSize > 0) { *(sbyte*)(void*)ValuePtr = *(sbyte*)(&value); return; } throw new InvalidOperationException("Interop done goof?"); } } static Il2CppNullable() { classPtr = Il2CppClassPointerStore<Nullable<T>>.NativeClassPtr; TClassPtr = Il2CppClassPointerStore<T>.NativeClassPtr; uint num = 0u; hasValueOffset = (int)IL2CPP.il2cpp_field_get_offset(IL2CPP.GetIl2CppField(classPtr, "has_value")); valueSize = IL2CPP.il2cpp_class_value_size(TClassPtr, ref num); marshalSize = Marshal.SizeOf(typeof(T)); } public Il2CppNullable(T? nullable) { IntPtr intPtr = IL2CPP.il2cpp_object_new(classPtr); uint num = (RuntimeSpecificsStore.ShouldUseWeakRefs(classPtr) ? IL2CPP.il2cpp_gchandle_new_weakref(intPtr, false) : IL2CPP.il2cpp_gchandle_new(intPtr, false)); AccessTools.Field(typeof(Il2CppObjectBase), "myGcHandle").SetValue(this, num); if (nullable.HasValue) { Value = nullable.Value; HasValue = true; } else { Value = default(T); HasValue = false; } } public unsafe Il2CppNullable(T value) { IntPtr intPtr = IL2CPP.il2cpp_object_new(classPtr); IntPtr intPtr2 = IL2CPP.il2cpp_object_unbox(intPtr); if (value is bool) { object obj = value; bool flag = (bool)((obj is bool) ? obj : null); Set(intPtr2, flag ? ((byte)1) : ((byte)0)); } else { Set(intPtr2, value); } *(sbyte*)(void*)(intPtr2 + hasValueOffset) = 1; uint num = (RuntimeSpecificsStore.ShouldUseWeakRefs(classPtr) ? IL2CPP.il2cpp_gchandle_new_weakref(intPtr, false) : IL2CPP.il2cpp_gchandle_new(intPtr, false)); AccessTools.Field(typeof(Il2CppObjectBase), "myGcHandle").SetValue(this, num); } private unsafe static void Set<U>(IntPtr tgt, U value) where U : unmanaged { *(U*)(void*)tgt = value; } public static implicit operator Nullable<T>(Il2CppNullable<T> me) { return new Nullable<T>(((Il2CppObjectBase)me).Pointer); } } } namespace Sst.LootDropBugfix { internal static class AppVersion { public const string Value = "2.1.0"; } public class AmmoDebugger { private static Nullable<bool> _emptyNullableBool = new Il2CppNullable<bool>(null); private static Nullable<Color> _emptyNullableColor = new Il2CppNullable<Color>((Color?)null); private MelonPreferences_Entry<bool> _prefTest; private MelonPreferences_Entry<float> _prefTestSpeed; public AmmoDebugger(MelonPreferences_Category prefCategory) { _prefTest = prefCategory.CreateEntry<bool>("test", false, "Test", "Spawns and breaks ammo boxes to test the fix", false, false, (ValueValidator)null, (string)null); _prefTestSpeed = prefCategory.CreateEntry<float>("test_speed", 1f, "Test speed", "Rate at which the test ammo boxes spawn and break", false, false, (ValueValidator)null, (string)null); _prefTest.OnValueChanged += delegate { OnLevelStart(); }; } public void OnLevelStart() { if (_prefTest.Value) { MelonCoroutines.Start(SpawnAndBreakAmmoCrates()); } } public IEnumerator SpawnAndBreakAmmoCrates() { RigManager obj2 = Object.FindObjectOfType<RigManager>(); object obj3; if (obj2 == null) { obj3 = null; } else { PhysicsRig physicsRig = obj2.physicsRig; obj3 = ((physicsRig != null) ? ((Rig)physicsRig).m_head : null); } Transform head = (Transform)obj3; ObjectDestructable? obj4 = ((IEnumerable<ObjectDestructable>)Object.FindObjectsOfType<ObjectDestructable>()).FirstOrDefault((Func<ObjectDestructable, bool>)delegate(ObjectDestructable obj) { LootTableData lootTable = obj.lootTable; return lootTable != null && ((Object)lootTable).name.StartsWith("AmmoCrateTable_"); }); GameObject cratePrefab = ((obj4 != null) ? ((Component)obj4).gameObject : null); int numDestroyed = 0; int numMissingLootItem = 0; while ((Object)(object)head != (Object)null && (Object)(object)cratePrefab != (Object)null && _prefTest.Value) { GameObject crate = Object.Instantiate<GameObject>(cratePrefab.gameObject, head.position + head.rotation * new Vector3(0f, 0f, 2f), Quaternion.identity); yield return (object)new WaitForSeconds(0.2f / _prefTestSpeed.Value); if ((Object)(object)crate == (Object)null) { yield return (object)new WaitForSeconds(0.5f / _prefTestSpeed.Value); continue; } ObjectDestructable component = crate.GetComponent<ObjectDestructable>(); IEnumerable<string> prefabNames = ((IEnumerable<LootItem>)component.lootTable.items).Select((LootItem item) => ((Object)item.spawnable.prefab).name); Vector3 position = component.spawnTarget.position; component.TakeDamage(Vector3.back, 100f, false, (AttackType)1); numDestroyed++; GameObject spawnedLootItem = FindSpawnedLootItem(prefabNames, position, checkExactPosition: false); if ((Object)(object)spawnedLootItem == (Object)null) { numMissingLootItem++; } MelonLogger.Msg($"Loot bugs: {numMissingLootItem} / {numDestroyed}"); yield return (object)new WaitForSeconds(0.5f / _prefTestSpeed.Value); if (spawnedLootItem != null) { spawnedLootItem.GetComponent<Poolee>().Despawn(_emptyNullableBool, _emptyNullableColor); } yield return (object)new WaitForSeconds(0.2f / _prefTestSpeed.Value); } } private GameObject FindSpawnedLootItem(IEnumerable<string> prefabNames, Vector3 spawnPosition, bool checkExactPosition) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) foreach (Collider item in (Il2CppArrayBase<Collider>)(object)Physics.OverlapSphere(spawnPosition, 0.5f)) { Transform val = ((Component)item).transform; while (Object.op_Implicit((Object)(object)val.parent)) { val = val.parent; } if (checkExactPosition) { Vector3 val2 = val.position - spawnPosition; if (!((double)((Vector3)(ref val2)).sqrMagnitude < 1E-09)) { continue; } } if (prefabNames.Any(((Object)val).name.StartsWith)) { return ((Component)val).gameObject; } } return null; } public static bool IsLootGuaranteed(LootTableData lootTable) { return ((IEnumerable<LootItem>)lootTable.items).Aggregate(0f, (float total, LootItem item) => total + item.percentage) >= 100f; } public void OnGetLootItem(LootTableData lootTable, SpawnableObject result) { if (_prefTest.Value && IsLootGuaranteed(lootTable) && (Object)(object)result == (Object)null) { MelonLogger.Warning("LootTableData.GetLootItem just returned null when loot chances add up to 100%!"); } } } public static class BuildInfo { public const string NAME = "LootDropBugfix"; public const string DESCRIPTION = "Fixes bug where dropped loot sometimes does not spawn."; } public class Mod : MelonMod { [HarmonyPatch(typeof(CVRCompositor), "FadeGrid")] private class CVRCompositor_FadeGrid_Patch { [HarmonyPrefix] internal static void Prefix(bool bFadeIn) { if (bFadeIn) { Instance._isLoading = true; return; } Instance._isLoading = false; Instance._ammoDebugger.OnLevelStart(); } } [HarmonyPatch(typeof(LootTableData), "GetLootItem")] private class LootTableData_GetLootItem_Patch { [HarmonyPrefix] internal static bool Prefix(LootTableData __instance, ref SpawnableObject __result) { if (!Instance._prefEnabled.Value) { return true; } __result = GetLootItemFixed(__instance); return false; } [HarmonyPostfix] internal static void Postfix(LootTableData __instance, SpawnableObject __result) { Instance._ammoDebugger.OnGetLootItem(__instance, __result); } } [HarmonyPatch(typeof(ObjectDestructable), "TakeDamage")] private class ObjectDestructable_TakeDamage_Patch { [HarmonyPrefix] internal static void Prefix(ObjectDestructable __instance, float damage, ref float __state) { if (Instance._isLoading) { if (damage > __instance._health) { Dbg.Log("Item would have broken but is indestructible before load: " + ((Object)__instance).name); } __state = __instance._health; __instance._health = float.PositiveInfinity; } } [HarmonyFinalizer] internal static void Finalizer(ObjectDestructable __instance, float __state) { if (Instance._isLoading) { __instance._health = __state; } } } public static Mod Instance; private MelonPreferences_Entry<bool> _prefEnabled; private AmmoDebugger _ammoDebugger; private bool _isLoading; public Mod() { Instance = this; } public override void OnApplicationStart() { MelonPreferences_Category val = MelonPreferences.CreateCategory("LootDropBugfix"); _prefEnabled = val.CreateEntry<bool>("enabled", true, "Enabled", "Activates the fix for loot drops", false, false, (ValueValidator)null, (string)null); _ammoDebugger = new AmmoDebugger(val); } public static SpawnableObject GetLootItemFixed(LootTableData lootTable) { int num = Random.RandomRange(0, 100); float num2 = 0f; for (int i = 0; i < ((Il2CppArrayBase<LootItem>)(object)lootTable.items).Length; i++) { LootItem val = ((Il2CppArrayBase<LootItem>)(object)lootTable.items)[i]; float num3 = num2 + val.percentage; if (num2 <= (float)num && (float)num < num3) { return val.spawnable; } num2 = num3; } return null; } } }