Decompiled source of LootDropBugfix v2.1.0

Mods/LootDropBugfix.dll

Decompiled 5 months ago
using 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;
		}
	}
}