Decompiled source of EarlyBirdSacrifice v1.2.4

EarlyBirdSacrifice.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using On.RoR2;
using On.RoR2.Artifacts;
using On.RoR2.UI;
using RoR2;
using RoR2.Artifacts;
using RoR2.Networking;
using RoR2.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EarlyBirdSacrifice")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.4.0")]
[assembly: AssemblyInformationalVersion("1.2.4")]
[assembly: AssemblyProduct("EarlyBirdSacrifice")]
[assembly: AssemblyTitle("EarlyBirdSacrifice")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.4.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;
		}
	}
}
namespace EarlyBirdSacrifice
{
	[BepInPlugin("com.FortressForce.earlybirdsacrifice", "EarlyBirdSacrifice", "1.2.4")]
	public class Plugin : BaseUnityPlugin
	{
		public class SyncSacrificeMessage : MessageBase
		{
			public float currentKillProgress;

			public int itemsDroppedInStage;

			public float killsNeededPerDrop;

			public int bufferRemainingInStage;

			public override void Serialize(NetworkWriter writer)
			{
				writer.Write(currentKillProgress);
				writer.Write(itemsDroppedInStage);
				writer.Write(killsNeededPerDrop);
				writer.Write(bufferRemainingInStage);
			}

			public override void Deserialize(NetworkReader reader)
			{
				currentKillProgress = reader.ReadSingle();
				itemsDroppedInStage = reader.ReadInt32();
				killsNeededPerDrop = reader.ReadSingle();
				bufferRemainingInStage = reader.ReadInt32();
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static Action<NetworkClient> <0>__OnStartClient;

			public static NetworkMessageDelegate <1>__OnSyncMessage;
		}

		public const string PluginGUID = "com.FortressForce.earlybirdsacrifice";

		public const string PluginName = "EarlyBirdSacrifice";

		public const string PluginVersion = "1.2.4";

		internal static ManualLogSource Log;

		public static ConfigEntry<int> BufferCount;

		public static ConfigEntry<int> DropsUntilTaxIncrease;

		public static ConfigEntry<int> BaseKillRequirement;

		public static ConfigEntry<int> KillTaxPerDrop;

		public static ConfigEntry<string> EliteBossDropMode;

		public static ConfigEntry<int> EliteKillWeight;

		public static ConfigEntry<int> BossKillWeight;

		public static ConfigEntry<float> EliteExtraDropChance;

		public static ConfigEntry<float> BossExtraDropChance;

		public static ConfigEntry<bool> ForceConsoleLogging;

		public static ConfigEntry<string> AcceptedBodyTypes;

		public static ConfigEntry<int> StageBufferBonus;

		public static ConfigEntry<int> StageBonusTax;

		public static ConfigEntry<string> StageTransitionTaxMode;

		public static ConfigEntry<float> StageTransitionTaxReduction;

		public static ConfigEntry<string> DoppelgangerDropMode;

		public static ConfigEntry<float> DoppelgangerDropChance;

		public static ConfigEntry<float> DoppelgangerBossWeight;

		public static ConfigEntry<float> DoppelgangerLegendaryWeight;

		public static ConfigEntry<float> DoppelgangerUncommonWeight;

		public static ConfigEntry<float> DoppelgangerVoidUncommonWeight;

		public static ConfigEntry<bool> EnableGUI;

		public static ConfigEntry<float> GUIPositionX;

		public static ConfigEntry<float> GUIPositionY;

		public static ConfigEntry<float> GUIFontSize;

		public static ConfigEntry<bool> AutoScaleForSwarms;

		public static ConfigEntry<bool> AutoScaleForSoul;

		private static float currentKillProgress = 0f;

		private static int itemsDroppedInStage = 0;

		private static float killsNeededPerDrop = 1f;

		private static int bufferRemainingInStage = 5;

		private static HGTextMeshProUGUI hudTextComponent = null;

		private void Awake()
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Expected O, but got Unknown
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Expected O, but got Unknown
			//IL_043d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0447: Expected O, but got Unknown
			//IL_044e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0458: Expected O, but got Unknown
			//IL_045f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0469: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BufferCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "BufferCount", 5, "Number of initial 'free' drops before the kill tax increases.");
			DropsUntilTaxIncrease = ((BaseUnityPlugin)this).Config.Bind<int>("General", "DropsUntilTaxIncrease", 3, "The number of drops it takes to trigger an increase in the kill tax (after the initial buffer is depleted).");
			BaseKillRequirement = ((BaseUnityPlugin)this).Config.Bind<int>("General", "BaseKillRequirement", 1, "The initial number of kills required per drop during the first buffer.");
			KillTaxPerDrop = ((BaseUnityPlugin)this).Config.Bind<int>("General", "KillTaxPerDrop", 1, "The additional kills added to the requirement each time the buffer is depleted.");
			EliteBossDropMode = ((BaseUnityPlugin)this).Config.Bind<string>("General", "EliteBossDropMode", "percent", new ConfigDescription("Determines how Elite and Boss/Champion deaths are handled regarding item drops and kill progression.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "percent", "extra credit", "both" }), Array.Empty<object>()));
			EliteKillWeight = ((BaseUnityPlugin)this).Config.Bind<int>("General", "EliteKillWeight", 2, "How many normal kills an Elite enemy counts as toward the progress bar (only active if EliteBossDropMode is 'extra credit' or 'both').");
			BossKillWeight = ((BaseUnityPlugin)this).Config.Bind<int>("General", "BossKillWeight", 5, "How many normal kills a Boss/Champion enemy counts as toward the progress bar (only active if EliteBossDropMode is 'extra credit' or 'both').");
			EliteExtraDropChance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "EliteExtraDropChance", 3f, "Percent chance (0-100) that an Elite enemy triggers an extra lucky drop on death (enabled by default).");
			BossExtraDropChance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BossExtraDropChance", 30f, "Percent chance (0-100) that a Boss/Champion triggers an extra lucky drop on death (enabled by default).");
			ForceConsoleLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForceConsoleLogging", false, "Set to true to force progress updates as Info logs (visible by default) in the BepInEx console.");
			AcceptedBodyTypes = ((BaseUnityPlugin)this).Config.Bind<string>("General", "AcceptedBodyTypes", "", "Comma-separated list of body names that will ALWAYS count towards kill progress, even if they yield 0 exp/gold (e.g. custom modded enemies, jars, etc.). WispBody is hardcoded to always count.");
			StageBufferBonus = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StageBufferBonus", 2, "Additional free drops added to the stage buffer for each stage cleared in the run.");
			StageBonusTax = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StageBonusTax", 1, "Additional base kills required per drop for each stage cleared in the run.");
			StageTransitionTaxMode = ((BaseUnityPlugin)this).Config.Bind<string>("General", "StageTransitionTaxMode", "Carry Over", new ConfigDescription("How the accumulated kill tax is handled when entering a new stage.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Reset", "Carry Over" }), Array.Empty<object>()));
			StageTransitionTaxReduction = ((BaseUnityPlugin)this).Config.Bind<float>("General", "StageTransitionTaxReduction", 10f, "If StageTransitionTaxMode is 'Carry Over', the accumulated kill tax will be reduced by this flat amount (but will not go below the stage's baseline tax).");
			DoppelgangerDropMode = ((BaseUnityPlugin)this).Config.Bind<string>("Vengeance", "DoppelgangerDropMode", "Boss", new ConfigDescription("The tier of item dropped by Vengeance doppelgangers on death.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[5] { "Boss", "Legendary", "Void Uncommon", "Uncommon", "Random" }), Array.Empty<object>()));
			DoppelgangerDropChance = ((BaseUnityPlugin)this).Config.Bind<float>("Vengeance", "DoppelgangerDropChance", 100f, "Percent chance (0-100) that a Vengeance doppelganger will drop its configured special item on death.");
			DoppelgangerBossWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Vengeance Weights", "DoppelgangerBossWeight", 40f, "Percentage weight for dropping a Boss item when DoppelgangerDropMode is 'Random'.");
			DoppelgangerLegendaryWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Vengeance Weights", "DoppelgangerLegendaryWeight", 30f, "Percentage weight for dropping a Legendary item when DoppelgangerDropMode is 'Random'.");
			DoppelgangerUncommonWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Vengeance Weights", "DoppelgangerUncommonWeight", 20f, "Percentage weight for dropping an Uncommon item when DoppelgangerDropMode is 'Random'.");
			DoppelgangerVoidUncommonWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Vengeance Weights", "DoppelgangerVoidUncommonWeight", 10f, "Percentage weight for dropping a Void Uncommon item when DoppelgangerDropMode is 'Random'.");
			EnableGUI = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "EnableGUI", true, "Should the on-screen HUD progress text be visible?");
			GUIPositionX = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "GUIPositionX", -40f, "Horizontal anchored position from the right side of the screen.");
			GUIPositionY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "GUIPositionY", -360f, "Vertical anchored position from the top of the screen (sits perfectly below the teleporter tracker).");
			GUIFontSize = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "GUIFontSize", 14f, "The font size of the HUD tracker text.");
			AutoScaleForSwarms = ((BaseUnityPlugin)this).Config.Bind<bool>("Balance Scaling", "AutoScaleForSwarms", true, "If true, automatically halves the required kills and taxes if the Artifact of Swarms is NOT active (since Swarms doubles spawns).");
			AutoScaleForSoul = ((BaseUnityPlugin)this).Config.Bind<bool>("Balance Scaling", "AutoScaleForSoul", true, "If true, automatically halves the required kills and taxes if the Artifact of Soul is NOT active (since Soul wisps yield extra kills).");
			SacrificeArtifactManager.OnServerCharacterDeath += new hook_OnServerCharacterDeath(SacrificeArtifactManager_OnServerCharacterDeath);
			Run.Start += new hook_Start(Run_Start);
			HUD.Awake += new hook_Awake(HUD_Awake);
			Stage.onStageStartGlobal += OnStageStart;
			NetworkManagerSystem.onStartClientGlobal += OnStartClient;
			Log.LogInfo((object)"EarlyBirdSacrifice has successfully initialized!");
		}

		private void OnDestroy()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			Stage.onStageStartGlobal -= OnStageStart;
			NetworkManagerSystem.onStartClientGlobal -= OnStartClient;
			SacrificeArtifactManager.OnServerCharacterDeath -= new hook_OnServerCharacterDeath(SacrificeArtifactManager_OnServerCharacterDeath);
			Run.Start -= new hook_Start(Run_Start);
			HUD.Awake -= new hook_Awake(HUD_Awake);
		}

		private static void OnStartClient(NetworkClient client)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			try
			{
				short num = 1337;
				if (!client.handlers.ContainsKey(num))
				{
					object obj = <>O.<1>__OnSyncMessage;
					if (obj == null)
					{
						NetworkMessageDelegate val = OnSyncMessage;
						<>O.<1>__OnSyncMessage = val;
						obj = (object)val;
					}
					client.RegisterHandler(num, (NetworkMessageDelegate)obj);
					Log.LogInfo((object)"Registered client network sync message handler successfully on client start.");
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to register client message handler: " + ex.Message));
			}
		}

		private static int GetInitialStageBufferCount(int stageClearCount)
		{
			int num = ((!((Object)(object)Run.instance != (Object)null)) ? 1 : Run.instance.participatingPlayerCount);
			if (num < 1)
			{
				num = 1;
			}
			return (BufferCount.Value + stageClearCount * StageBufferBonus.Value) * num;
		}

		private static int GetRefillBufferCount()
		{
			int num = ((!((Object)(object)Run.instance != (Object)null)) ? 1 : Run.instance.participatingPlayerCount);
			if (num < 1)
			{
				num = 1;
			}
			return DropsUntilTaxIncrease.Value * num;
		}

		private static void LogProgress(string message)
		{
			if (ForceConsoleLogging.Value)
			{
				Log.LogInfo((object)message);
			}
			else
			{
				Log.LogDebug((object)message);
			}
		}

		private void Run_Start(orig_Start orig, Run self)
		{
			orig.Invoke(self);
			currentKillProgress = 0f;
			itemsDroppedInStage = 0;
			killsNeededPerDrop = 1f;
			bufferRemainingInStage = BufferCount.Value;
			if (NetworkServer.active)
			{
				int num = (((Object)(object)self != (Object)null) ? self.stageClearCount : 0);
				float artifactScaleFactor = GetArtifactScaleFactor();
				float num2 = (float)(BaseKillRequirement.Value + num * StageBonusTax.Value) * artifactScaleFactor;
				killsNeededPerDrop = Mathf.Max(1f, Mathf.Floor(num2));
				bufferRemainingInStage = GetInitialStageBufferCount(num);
				LogProgress($"New run started! Resetting Early Bird Sacrifice counters. Clear count: {num}. Artifact Scale: {artifactScaleFactor:F2}. Required kills: {killsNeededPerDrop:F1}, Stage buffer size: {bufferRemainingInStage}");
				UpdateHUDText();
				SyncState();
			}
		}

		private void OnStageStart(Stage stage)
		{
			currentKillProgress = 0f;
			itemsDroppedInStage = 0;
			if (NetworkServer.active)
			{
				int num = (((Object)(object)Run.instance != (Object)null) ? Run.instance.stageClearCount : 0);
				float artifactScaleFactor = GetArtifactScaleFactor();
				float num2 = (float)(BaseKillRequirement.Value + num * StageBonusTax.Value) * artifactScaleFactor;
				if (StageTransitionTaxMode.Value == "Carry Over")
				{
					killsNeededPerDrop = Mathf.Max(Mathf.Floor(num2), killsNeededPerDrop - StageTransitionTaxReduction.Value);
					killsNeededPerDrop = Mathf.Max(1f, killsNeededPerDrop);
				}
				else
				{
					killsNeededPerDrop = Mathf.Max(1f, Mathf.Floor(num2));
				}
				bufferRemainingInStage = GetInitialStageBufferCount(num);
				string text = (((Object)(object)stage != (Object)null && (Object)(object)stage.sceneDef != (Object)null) ? stage.sceneDef.cachedName : "Unknown");
				LogProgress($"New stage entered ({text})! Resetting Early Bird Sacrifice counters. Clear count: {num}. Artifact Scale: {artifactScaleFactor:F2}. Required kills: {killsNeededPerDrop:F1}, Stage buffer size: {bufferRemainingInStage}");
				UpdateHUDText();
				SyncState();
			}
		}

		private static Transform FindChildRecursive(Transform parent, string name)
		{
			if (((Object)parent).name == name)
			{
				return parent;
			}
			for (int i = 0; i < parent.childCount; i++)
			{
				Transform val = FindChildRecursive(parent.GetChild(i), name);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private void HUD_Awake(orig_Awake orig, HUD self)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			orig.Invoke(self);
			if (!EnableGUI.Value)
			{
				return;
			}
			try
			{
				GameObject val = new GameObject("EarlyBirdSacrificeHUDText");
				val.layer = 5;
				RectTransform val2 = val.AddComponent<RectTransform>();
				Transform val3 = null;
				ObjectivePanelController componentInChildren = ((Component)self).GetComponentInChildren<ObjectivePanelController>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					val3 = ((Component)componentInChildren).transform;
					LogProgress("ObjectivePanelController component found successfully.");
				}
				else
				{
					val3 = FindChildRecursive(((Component)self).transform, "ObjectiveCluster");
					if ((Object)(object)val3 == (Object)null)
					{
						val3 = FindChildRecursive(((Component)self).transform, "ObjectivePanel");
					}
					if ((Object)(object)val3 != (Object)null)
					{
						LogProgress("ObjectiveCluster found via recursive name search: " + ((Object)val3).name);
					}
				}
				if ((Object)(object)val3 != (Object)null)
				{
					val.transform.SetParent(val3, false);
					val.transform.SetAsLastSibling();
					LayoutElement obj = val.AddComponent<LayoutElement>();
					obj.minHeight = 65f;
					obj.preferredHeight = 65f;
					val2.anchorMin = new Vector2(0f, 0f);
					val2.anchorMax = new Vector2(1f, 1f);
					val2.pivot = new Vector2(0.5f, 0.5f);
					LogProgress("HUD Text attached as child of Objective panel.");
				}
				else
				{
					val.transform.SetParent(self.mainContainer.transform, false);
					val2.anchorMin = new Vector2(1f, 1f);
					val2.anchorMax = new Vector2(1f, 1f);
					val2.pivot = new Vector2(1f, 1f);
					val2.anchoredPosition = new Vector2(GUIPositionX.Value, GUIPositionY.Value);
					val2.sizeDelta = new Vector2(250f, 85f);
					LogProgress("Objective Cluster container not found. Falling back to mainContainer manual offsets.");
				}
				hudTextComponent = val.AddComponent<HGTextMeshProUGUI>();
				TMP_FontAsset val4 = null;
				HGTextMeshProUGUI[] componentsInChildren = ((Component)self).GetComponentsInChildren<HGTextMeshProUGUI>(true);
				foreach (HGTextMeshProUGUI val5 in componentsInChildren)
				{
					if ((Object)(object)val5 != (Object)null && (Object)(object)((TMP_Text)val5).font != (Object)null)
					{
						val4 = ((TMP_Text)val5).font;
						break;
					}
				}
				if ((Object)(object)val4 != (Object)null)
				{
					((TMP_Text)hudTextComponent).font = val4;
				}
				((TMP_Text)hudTextComponent).fontSize = GUIFontSize.Value;
				((TMP_Text)hudTextComponent).alignment = (TextAlignmentOptions)258;
				((Graphic)hudTextComponent).color = new Color(0.9f, 0.9f, 0.9f, 1f);
				val.SetActive(true);
				UpdateHUDText();
				LogProgress("HUD Text initialized successfully using HGTextMeshProUGUI.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to initialize HUD text: " + ex.Message));
			}
		}

		private static void UpdateHUDText()
		{
			if (!((Object)(object)hudTextComponent == (Object)null))
			{
				if ((Object)(object)Run.instance == (Object)null || (Object)(object)RunArtifactManager.instance == (Object)null || !RunArtifactManager.instance.IsArtifactEnabled(Artifacts.Sacrifice))
				{
					((TMP_Text)hudTextComponent).text = "";
					return;
				}
				int stageClearCount = Run.instance.stageClearCount;
				float artifactScaleFactor = GetArtifactScaleFactor();
				Mathf.Max(1f, Mathf.Floor((float)(BaseKillRequirement.Value + stageClearCount * StageBonusTax.Value) * artifactScaleFactor));
				int initialStageBufferCount = GetInitialStageBufferCount(stageClearCount);
				string text = "";
				float num = Mathf.Max(1f, Mathf.Floor(killsNeededPerDrop));
				text = ((itemsDroppedInStage >= initialStageBufferCount) ? $"Sacrifice:\n{Mathf.Floor(currentKillProgress):F0}/{num:F0} kills needed\nDrops to next tax: {bufferRemainingInStage}" : $"Sacrifice:\n{itemsDroppedInStage}/{initialStageBufferCount} initial drops");
				((TMP_Text)hudTextComponent).text = text;
			}
		}

		private void SacrificeArtifactManager_OnServerCharacterDeath(orig_OnServerCharacterDeath orig, DamageReport damageReport)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Invalid comparison between Unknown and I4
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Invalid comparison between Unknown and I4
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Invalid comparison between Unknown and I4
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			orig.Invoke(damageReport);
			if (damageReport == null || (Object)(object)damageReport.victimBody == (Object)null)
			{
				return;
			}
			if (ForceConsoleLogging.Value)
			{
				try
				{
					DeathRewards component = ((Component)damageReport.victimBody).GetComponent<DeathRewards>();
					if ((!((Object)(object)component != (Object)null) || (component.expReward == 0 && component.goldReward == 0)) && (int)damageReport.victimTeamIndex != 1)
					{
						Log.LogInfo((object)$"[EBS-DEBUG] ZERO-VALUE DEATH: Name='{((Object)damageReport.victimBody).name}', BodyIndex={damageReport.victimBody.bodyIndex}, HasMaster={(Object)(object)damageReport.victimMaster != (Object)null}");
					}
				}
				catch
				{
				}
			}
			if ((int)damageReport.victimTeamIndex == 1 || (Object)(object)damageReport.victimMaster == (Object)null)
			{
				return;
			}
			bool flag = false;
			try
			{
				if ((Object)(object)damageReport.victimBody.inventory != (Object)null && damageReport.victimBody.inventory.GetItemCountEffective(Items.InvadingDoppelganger) > 0)
				{
					flag = true;
				}
			}
			catch (Exception)
			{
			}
			DeathRewards component2 = ((Component)damageReport.victimBody).GetComponent<DeathRewards>();
			bool flag2 = (Object)(object)component2 != (Object)null && (component2.expReward != 0 || component2.goldReward != 0);
			bool flag3 = false;
			try
			{
				BodyIndex bodyIndex = damageReport.victimBody.bodyIndex;
				string name = ((Object)damageReport.victimBody).name;
				if (bodyIndex == BodyCatalog.FindBodyIndex("WispBody") || name.IndexOf("wisp", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					flag3 = true;
				}
				if (!flag3)
				{
					string value = AcceptedBodyTypes.Value;
					if (!string.IsNullOrWhiteSpace(value))
					{
						string[] array = value.Split(',');
						for (int i = 0; i < array.Length; i++)
						{
							string text = array[i].Trim();
							if (!string.IsNullOrEmpty(text))
							{
								BodyIndex val = BodyCatalog.FindBodyIndex(text);
								if ((int)val != -1 && bodyIndex == val)
								{
									flag3 = true;
									break;
								}
								if (name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
								{
									flag3 = true;
									break;
								}
							}
						}
					}
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Error in Accepted Body Type check: {arg}");
			}
			if (!flag2 && !flag && !flag3)
			{
				return;
			}
			if (flag)
			{
				float num = Random.Range(0f, 100f);
				if (num <= DoppelgangerDropChance.Value)
				{
					SpawnSpecialDoppelgangerDrop(damageReport);
				}
				else
				{
					LogProgress($"Vengeance doppelganger killed, but rolled no-drop ({num:F1}% vs {DoppelgangerDropChance.Value}% chance).");
				}
				return;
			}
			string text2 = (EliteBossDropMode.Value ?? "percent").Trim().ToLowerInvariant();
			bool flag4 = text2 == "extra credit" || text2 == "both";
			bool flag5 = text2 == "percent" || text2 == "both";
			float num2 = 1f;
			if (damageReport.victimBody.isElite)
			{
				if (flag4)
				{
					num2 = EliteKillWeight.Value;
					LogProgress($"Elite killed! Added {num2} to progress (Drop Mode: {text2}).");
				}
				else
				{
					LogProgress("Elite killed! Added 1.0 to progress (Drop Mode: " + text2 + ").");
				}
				if (flag5)
				{
					float num3 = Random.Range(0f, 100f);
					if (num3 <= EliteExtraDropChance.Value)
					{
						SpawnSacrificeDrop(damageReport);
						Log.LogInfo((object)$"Elite lucky drop triggered ({num3:F1}% roll vs {EliteExtraDropChance.Value}% limit)! Spawned bonus item.");
					}
				}
			}
			else if (damageReport.victimBody.isChampion)
			{
				if (flag4)
				{
					num2 = BossKillWeight.Value;
					LogProgress($"Boss/Champion killed! Added {num2} to progress (Drop Mode: {text2}).");
				}
				else
				{
					LogProgress("Boss/Champion killed! Added 1.0 to progress (Drop Mode: " + text2 + ").");
				}
				if (flag5)
				{
					float num4 = Random.Range(0f, 100f);
					if (num4 <= BossExtraDropChance.Value)
					{
						SpawnSacrificeDrop(damageReport);
						Log.LogInfo((object)$"Boss lucky drop triggered ({num4:F1}% roll vs {BossExtraDropChance.Value}% limit)! Spawned bonus item.");
					}
				}
			}
			currentKillProgress += num2;
			float num5 = Mathf.Max(1f, Mathf.Floor(killsNeededPerDrop));
			LogProgress($"Progress: {currentKillProgress:F1} / {killsNeededPerDrop:F1} (threshold: {num5}, Buffer Remaining: {bufferRemainingInStage}, Stage Drops: {itemsDroppedInStage})");
			while (currentKillProgress >= num5)
			{
				currentKillProgress -= num5;
				itemsDroppedInStage++;
				SpawnSacrificeDrop(damageReport);
				bufferRemainingInStage--;
				if (bufferRemainingInStage <= 0)
				{
					bufferRemainingInStage = GetRefillBufferCount();
					float artifactScaleFactor = GetArtifactScaleFactor();
					int num6 = ((!((Object)(object)Run.instance != (Object)null)) ? 1 : Run.instance.participatingPlayerCount);
					if (num6 < 1)
					{
						num6 = 1;
					}
					float num7 = (float)KillTaxPerDrop.Value * artifactScaleFactor / (float)num6;
					killsNeededPerDrop += num7;
					num5 = Mathf.Max(1f, Mathf.Floor(killsNeededPerDrop));
					LogProgress($"Buffer depleted! Refilling buffer to {bufferRemainingInStage} and increasing required kills to {killsNeededPerDrop:F1} (threshold: {num5}, added tax: {num7:F2}).");
				}
			}
			UpdateHUDText();
			SyncState();
		}

		private void SpawnSacrificeDrop(DamageReport damageReport)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				PickupDropTable val = (PickupDropTable)typeof(SacrificeArtifactManager).GetField("dropTable", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null);
				Xoroshiro128Plus val2 = (Xoroshiro128Plus)typeof(SacrificeArtifactManager).GetField("treasureRng", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null);
				if ((Object)(object)val != (Object)null && val2 != null)
				{
					UniquePickup val3 = val.GeneratePickup(val2);
					if (val3.pickupIndex != PickupIndex.none)
					{
						Vector3 corePosition = damageReport.victimBody.corePosition;
						Vector3 val4 = Vector3.up * 15f + Random.insideUnitSphere * 2f;
						CreatePickupInfo val5 = default(CreatePickupInfo);
						((CreatePickupInfo)(ref val5)).pickup = val3;
						val5.position = corePosition;
						val5.rotation = Quaternion.identity;
						PickupDropletController.CreatePickupDroplet(val5, corePosition, val4);
					}
				}
				else
				{
					Log.LogError((object)"Could not retrieve Sacrifice drop table or RNG via reflection.");
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to spawn deterministic Sacrifice drop: " + ex.Message));
			}
		}

		private void SpawnSpecialDoppelgangerDrop(DamageReport damageReport)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: 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_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Xoroshiro128Plus val = (Xoroshiro128Plus)typeof(SacrificeArtifactManager).GetField("treasureRng", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null);
				if (val == null)
				{
					val = Run.instance.treasureRng;
				}
				string text = (DoppelgangerDropMode.Value ?? "Boss").Trim().ToLowerInvariant();
				List<PickupIndex> list = null;
				string text2 = "";
				switch (text)
				{
				case "boss":
					list = Run.instance.availableBossDropList;
					text2 = "Boss";
					break;
				case "legendary":
					list = Run.instance.availableTier3DropList;
					text2 = "Legendary";
					break;
				case "uncommon":
					list = Run.instance.availableTier2DropList;
					text2 = "Uncommon";
					break;
				case "void uncommon":
				{
					List<PickupIndex> availableVoidTier2DropList2 = Run.instance.availableVoidTier2DropList;
					if (availableVoidTier2DropList2 != null && availableVoidTier2DropList2.Count > 0)
					{
						list = availableVoidTier2DropList2;
						text2 = "Void Uncommon";
					}
					else
					{
						list = Run.instance.availableTier2DropList;
						text2 = "Uncommon (Void Uncommon DLC not active fallback)";
					}
					break;
				}
				case "random":
				{
					float num = Mathf.Max(0f, DoppelgangerBossWeight.Value);
					float num2 = Mathf.Max(0f, DoppelgangerLegendaryWeight.Value);
					float num3 = Mathf.Max(0f, DoppelgangerUncommonWeight.Value);
					float num4 = Mathf.Max(0f, DoppelgangerVoidUncommonWeight.Value);
					List<PickupIndex> availableVoidTier2DropList = Run.instance.availableVoidTier2DropList;
					if (availableVoidTier2DropList == null || availableVoidTier2DropList.Count == 0)
					{
						num4 = 0f;
					}
					float num5 = num + num2 + num3 + num4;
					if (num5 <= 0f)
					{
						num = 1f;
						num5 = 1f;
					}
					float num6 = Random.Range(0f, num5);
					if (num6 < num)
					{
						list = Run.instance.availableBossDropList;
						text2 = "Boss (Random Weight)";
					}
					else if (num6 < num + num2)
					{
						list = Run.instance.availableTier3DropList;
						text2 = "Legendary (Random Weight)";
					}
					else if (num6 < num + num2 + num3)
					{
						list = Run.instance.availableTier2DropList;
						text2 = "Uncommon (Random Weight)";
					}
					else
					{
						list = availableVoidTier2DropList;
						text2 = "Void Uncommon (Random Weight)";
					}
					break;
				}
				}
				if (list != null && list.Count > 0)
				{
					int index = val.RangeInt(0, list.Count);
					PickupIndex val2 = list[index];
					if (val2 != PickupIndex.none)
					{
						Vector3 corePosition = damageReport.victimBody.corePosition;
						Vector3 val3 = Vector3.up * 15f + Random.insideUnitSphere * 2f;
						UniquePickup pickup = default(UniquePickup);
						pickup.pickupIndex = val2;
						CreatePickupInfo val4 = default(CreatePickupInfo);
						((CreatePickupInfo)(ref val4)).pickup = pickup;
						val4.position = corePosition;
						val4.rotation = Quaternion.identity;
						PickupDropletController.CreatePickupDroplet(val4, corePosition, val3);
						Log.LogInfo((object)$"Vengeance doppelganger killed! Spawned special item drop. Tier: {text2}, Item: {val2}.");
					}
				}
				else
				{
					Log.LogError((object)("Vengeance doppelganger drop failed: Selected target pool '" + text2 + "' was null or empty."));
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to spawn doppelganger drop: " + ex.Message));
			}
		}

		private static void OnSyncMessage(NetworkMessage netMsg)
		{
			try
			{
				SyncSacrificeMessage syncSacrificeMessage = netMsg.ReadMessage<SyncSacrificeMessage>();
				if (syncSacrificeMessage != null)
				{
					currentKillProgress = syncSacrificeMessage.currentKillProgress;
					itemsDroppedInStage = syncSacrificeMessage.itemsDroppedInStage;
					killsNeededPerDrop = syncSacrificeMessage.killsNeededPerDrop;
					bufferRemainingInStage = syncSacrificeMessage.bufferRemainingInStage;
					UpdateHUDText();
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to handle SyncSacrificeMessage: " + ex.Message));
			}
		}

		private static void SyncState()
		{
			if (!NetworkServer.active)
			{
				return;
			}
			try
			{
				SyncSacrificeMessage syncSacrificeMessage = new SyncSacrificeMessage
				{
					currentKillProgress = currentKillProgress,
					itemsDroppedInStage = itemsDroppedInStage,
					killsNeededPerDrop = killsNeededPerDrop,
					bufferRemainingInStage = bufferRemainingInStage
				};
				NetworkServer.SendToAll((short)1337, (MessageBase)(object)syncSacrificeMessage);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to send SyncSacrificeMessage: " + ex.Message));
			}
		}

		private static float GetArtifactScaleFactor()
		{
			float num = 1f;
			if ((Object)(object)RunArtifactManager.instance != (Object)null)
			{
				if (AutoScaleForSwarms != null && AutoScaleForSwarms.Value && !RunArtifactManager.instance.IsArtifactEnabled(Artifacts.Swarms))
				{
					num *= 0.5f;
				}
				if (AutoScaleForSoul != null && AutoScaleForSoul.Value && !RunArtifactManager.instance.IsArtifactEnabled(Artifacts.WispOnDeath))
				{
					num *= 0.5f;
				}
			}
			return num;
		}
	}
}