Decompiled source of DamageSparks v1.2.0

DamageSparks.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using PerfectRandom.Sulfur.Core;
using PerfectRandom.Sulfur.Core.Stats;
using PerfectRandom.Sulfur.Core.Units;
using PerfectRandom.Sulfur.Gameplay;
using TMPro;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SulfurDamageNumbersMod")]
[assembly: AssemblyDescription("SULFUR damage numbers mod for BepInEx 5")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("ryuka_labs")]
[assembly: AssemblyProduct("SulfurDamageNumbersMod")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("c9ebec31-296a-4d97-9959-9804ea595a74")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.2.0.0")]
namespace SulfurDamageNumbersMod;

internal static class DamageLogPatch
{
	private struct PendingHit
	{
		public bool HasHit;

		public Unit Target;

		public DamageSourceData Source;

		public float Amount;

		public float Multiplier;

		public float Bonus;

		public int Sequence;
	}

	private static bool _applied;

	private static readonly List<PendingHit> PendingStack = new List<PendingHit>();

	private static int _depth;

	private static int _nextSequence;

	public static void Apply(Harmony harmony)
	{
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Expected O, but got Unknown
		//IL_009d: Expected O, but got Unknown
		//IL_009d: Expected O, but got Unknown
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Expected O, but got Unknown
		if (!_applied)
		{
			MethodBase methodBase = FindCreateDamageLog();
			MethodBase methodBase2 = FindReceiveDamage();
			if (methodBase == null || methodBase2 == null)
			{
				Plugin.Log.LogError((object)("SULFUR Damage Numbers failed: " + ((methodBase == null) ? "Unit.CreateDamageLog(float, float, DamageTypes, Hitmesh.Data, DamageSourceData)" : "Unit.ReceiveDamage(float, DamageSourceData, Hitmesh.Data, Vector3?)") + " was not found. No damage numbers will be shown."));
				return;
			}
			harmony.Patch(methodBase2, new HarmonyMethod(typeof(DamageLogPatch), "ReceiveDamagePrefix", (Type[])null), new HarmonyMethod(typeof(DamageLogPatch), "ReceiveDamagePostfix", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(DamageLogPatch), "ReceiveDamageFinalizer", (Type[])null), (HarmonyMethod)null);
			harmony.Patch(methodBase, (HarmonyMethod)null, new HarmonyMethod(typeof(DamageLogPatch), "CreateDamageLogPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_applied = true;
			Plugin.Log.LogInfo((object)"SULFUR Damage Numbers patched Unit.ReceiveDamage and Unit.CreateDamageLog.");
		}
	}

	public static void Reset()
	{
		_applied = false;
		PendingStack.Clear();
		_depth = 0;
		_nextSequence = 0;
		DamageNumbersIntegration.Reset();
	}

	private static MethodBase FindCreateDamageLog()
	{
		return AccessTools.GetDeclaredMethods(typeof(Unit)).FirstOrDefault(delegate(MethodInfo m)
		{
			if (m.Name != "CreateDamageLog")
			{
				return false;
			}
			ParameterInfo[] parameters = m.GetParameters();
			return parameters.Length == 5 && parameters[0].ParameterType == typeof(float) && parameters[1].ParameterType == typeof(float) && parameters[2].ParameterType == typeof(DamageTypes) && parameters[3].ParameterType == typeof(Data) && parameters[4].ParameterType == typeof(DamageSourceData);
		});
	}

	private static MethodBase FindReceiveDamage()
	{
		return AccessTools.GetDeclaredMethods(typeof(Unit)).FirstOrDefault(delegate(MethodInfo m)
		{
			if (m.Name != "ReceiveDamage" || m.ReturnType != typeof(bool))
			{
				return false;
			}
			ParameterInfo[] parameters = m.GetParameters();
			return parameters.Length == 4 && parameters[0].ParameterType == typeof(float) && parameters[1].ParameterType == typeof(DamageSourceData) && parameters[2].ParameterType == typeof(Data) && parameters[3].ParameterType == typeof(Vector3?);
		});
	}

	private static void ReceiveDamagePrefix(out int __state)
	{
		__state = _depth;
		if (_depth == PendingStack.Count)
		{
			PendingStack.Add(default(PendingHit));
		}
		else
		{
			PendingStack[_depth] = default(PendingHit);
		}
		_depth++;
	}

	private static void ReceiveDamagePostfix(bool __result, int __state)
	{
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		if (__state < 0 || __state >= _depth)
		{
			return;
		}
		PendingHit pendingHit = PendingStack[__state];
		PendingStack[__state] = default(PendingHit);
		_depth = __state;
		if (!pendingHit.HasHit)
		{
			return;
		}
		if (!__result)
		{
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)("[DamageNumbers] drop: the game did not apply the logged damage amount=" + pendingHit.Amount + " target=" + SafeName((Object)(object)pendingHit.Target)));
			}
		}
		else if (!((Object)(object)pendingHit.Target == (Object)null))
		{
			try
			{
				DamageNumberManager.RegisterDamage(pendingHit.Target, pendingHit.Source, pendingHit.Amount, pendingHit.Multiplier, pendingHit.Bonus, pendingHit.Sequence);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[DamageNumbers] ReceiveDamage postfix failed: " + ex));
			}
		}
	}

	private static Exception ReceiveDamageFinalizer(Exception __exception, int __state)
	{
		if (__state >= 0 && __state < _depth)
		{
			for (int i = __state; i < _depth; i++)
			{
				PendingStack[i] = default(PendingHit);
			}
			_depth = __state;
		}
		return __exception;
	}

	private unsafe static void CreateDamageLogPostfix(Unit __instance, float __0, Data __3, DamageSourceData __4)
	{
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0256: 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_01b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)__instance == (Object)null || !Plugin.Enabled.Value)
		{
			return;
		}
		try
		{
			if (_depth <= 0)
			{
				if (Plugin.DebugLog.Value)
				{
					Plugin.Log.LogInfo((object)("[DamageNumbers] ignore damage log outside ReceiveDamage target=" + SafeName((Object)(object)__instance)));
				}
				return;
			}
			if (DamageNumbersIntegration.IsResolvingRemoteHit)
			{
				if (Plugin.DebugLog.Value)
				{
					Plugin.Log.LogInfo((object)("[DamageNumbers] reject source: another player's hit target=" + SafeName((Object)(object)__instance)));
				}
				return;
			}
			float amount = __0;
			if (!IsShowableAmount(amount, __instance))
			{
				return;
			}
			if (!IsAllowedTarget(__instance, out var reason))
			{
				if (Plugin.DebugLog.Value)
				{
					Plugin.Log.LogInfo((object)("[DamageNumbers] reject target: " + reason + " target=" + SafeName((Object)(object)__instance)));
				}
				return;
			}
			if (!IsPlayerExecutedAttack(__4, out reason))
			{
				if (Plugin.DebugLog.Value)
				{
					Plugin.Log.LogInfo((object)("[DamageNumbers] reject source: " + reason + " target=" + SafeName((Object)(object)__instance) + " source=" + DescribeSource(__4)));
				}
				return;
			}
			float multiplier = (((int)((ShapeId)(ref __3.shapeId)).part == 0) ? 0f : Mathf.Max(0f, ((Data)(ref __3)).GetShapeMultiplier()));
			float currentHitBonus = DamageNumbersIntegration.CurrentHitBonus;
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)("[DamageNumbers] accept amount=" + amount + " multiplier=" + multiplier + " bonus=" + currentHitBonus + " part=" + ((object)((ShapeId)(ref __3.shapeId)).part/*cast due to .constrained prefix*/).ToString() + " damageType=" + ((object)(*(DamageTypes*)(&__4.damageType))/*cast due to .constrained prefix*/).ToString() + " critical=" + __4.isCritical + " target=" + SafeName((Object)(object)__instance) + " source=" + DescribeSource(__4)));
			}
			PendingStack[_depth - 1] = new PendingHit
			{
				HasHit = true,
				Target = __instance,
				Source = __4,
				Amount = amount,
				Multiplier = multiplier,
				Bonus = currentHitBonus,
				Sequence = ++_nextSequence
			};
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("[DamageNumbers] CreateDamageLog postfix failed: " + ex));
		}
	}

	internal static void ShowResolvedHit(ResolvedHit hit)
	{
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		if (!Plugin.Enabled.Value)
		{
			return;
		}
		try
		{
			object target = hit.Target;
			Unit val = (Unit)((target is Unit) ? target : null);
			if (!IsShowableAmount(hit.Damage, val))
			{
				return;
			}
			if ((Object)(object)val != (Object)null && !IsAllowedTarget(val, out var reason))
			{
				if (Plugin.DebugLog.Value)
				{
					Plugin.Log.LogInfo((object)("[DamageNumbers] reject target: " + reason + " target=" + SafeName((Object)(object)val)));
				}
				return;
			}
			object weapon = hit.Weapon;
			Object val2 = (Object)((weapon is Object) ? weapon : null);
			if (val2 == (Object)null && !hit.Melee && !Plugin.ShowPlayerOwnedSecondaryEffects.Value)
			{
				if (Plugin.DebugLog.Value)
				{
					Plugin.Log.LogInfo((object)("[DamageNumbers] reject source: player-owned secondary damage is disabled target=" + SafeName((Object)(object)val)));
				}
				return;
			}
			float bonus = DamageNumbersIntegration.SanitizeBonus(hit.BonusMultiplier);
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)("[DamageNumbers] accept resolved amount=" + hit.Damage + " multiplier=" + hit.HitMultiplier + " bonus=" + bonus + " damageType=" + ((object)(DamageTypes)(byte)hit.DamageType/*cast due to .constrained prefix*/).ToString() + " critical=" + hit.IsCritical + " target=" + SafeName((Object)(object)val) + " weapon=" + SafeName(val2)));
			}
			DamageNumberManager.RegisterResolvedDamage(val, hit.Position, (!(val2 == (Object)null)) ? val2.GetInstanceID() : 0, hit.Melee, (DamageTypes)(byte)hit.DamageType, hit.IsCritical, hit.Damage, Mathf.Max(0f, hit.HitMultiplier), bonus, (hit.DealtFrame > 0) ? hit.DealtFrame : Time.frameCount, ++_nextSequence);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("[DamageNumbers] Showing a resolved hit failed: " + ex));
		}
	}

	private static bool IsShowableAmount(float amount, Unit target)
	{
		if (float.IsNaN(amount) || float.IsInfinity(amount))
		{
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)("[DamageNumbers] reject non-finite damage amount=" + amount + " target=" + SafeName((Object)(object)target)));
			}
			return false;
		}
		if (amount < Plugin.MinDamageToShow.Value)
		{
			if (Plugin.DebugLog.Value)
			{
				Plugin.Log.LogInfo((object)("[DamageNumbers] reject low damage amount=" + amount + " target=" + SafeName((Object)(object)target)));
			}
			return false;
		}
		return true;
	}

	private static bool IsAllowedTarget(Unit target, out string reason)
	{
		if (target.isPlayer)
		{
			reason = "target is player";
			return false;
		}
		if (Plugin.RequireNpcTarget.Value && !(target is Npc))
		{
			reason = "target is not Npc";
			return false;
		}
		if (VanillaDamageText.UnitShowsOwnDamageText(target))
		{
			reason = "the game draws its own damage text for this unit (target dummy)";
			return false;
		}
		reason = null;
		return true;
	}

	private static bool IsPlayerExecutedAttack(DamageSourceData sourceData, out string reason)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: 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_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		bool flag = (Object)(object)sourceData.sourceUnit != (Object)null && sourceData.sourceUnit.isPlayer;
		bool isPeriodicModifier = sourceData.states.isPeriodicModifier;
		if (!sourceData.isPlayer && !flag)
		{
			reason = "sourceData.isPlayer is false and sourceUnit is not the player";
			return false;
		}
		if (isPeriodicModifier && !Plugin.ShowStatusEffectDamage.Value)
		{
			reason = "player-applied status damage is disabled";
			return false;
		}
		if (Plugin.RequireSourceUnitPlayer.Value && !flag)
		{
			reason = (((Object)(object)sourceData.sourceUnit == (Object)null) ? "sourceUnit is null" : "sourceUnit is not player");
			return false;
		}
		if ((Object)(object)sourceData.sourceWeapon != (Object)null)
		{
			reason = null;
			return true;
		}
		if (sourceData.melee)
		{
			reason = null;
			return true;
		}
		if (isPeriodicModifier)
		{
			reason = null;
			return true;
		}
		if (Plugin.ShowPlayerOwnedSecondaryEffects.Value && !sourceData.states.notCreatedByPlayer)
		{
			reason = null;
			return true;
		}
		reason = "no weapon/melee/player-owned secondary marker";
		return false;
	}

	private static string DescribeSource(DamageSourceData sourceData)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		string text = (((Object)(object)sourceData.sourceUnit == (Object)null) ? "null" : SafeName((Object)(object)sourceData.sourceUnit));
		string text2 = (((Object)(object)sourceData.sourceWeapon == (Object)null) ? "null" : ((Object)sourceData.sourceWeapon).name);
		return "isPlayer=" + sourceData.isPlayer + ", melee=" + sourceData.melee + ", periodic=" + sourceData.states.isPeriodicModifier + ", weapon=" + text2 + ", unit=" + text + ", instanceId=" + sourceData.instanceId + ", name=" + sourceData.name;
	}

	private static string SafeName(Object obj)
	{
		if (!(obj == (Object)null))
		{
			return obj.name;
		}
		return "null";
	}
}
internal sealed class DamageNumberManager : MonoBehaviour
{
	private struct AggregateEntry
	{
		public float Amount;

		public Vector3 Position;

		public float HighestMultiplier;

		public float LastMultiplier;

		public float WeightedMultiplierSum;

		public float HighestBonus;

		public DamageTypes DamageType;

		public bool AnyCritical;

		public int TargetId;

		public int FirstSequence;
	}

	private struct PerHitEntry
	{
		public Vector3 Position;

		public float Amount;

		public float Multiplier;

		public float Bonus;

		public DamageTypes DamageType;

		public bool Critical;

		public PerHitSpreadKey SpreadKey;

		public int Sequence;
	}

	private struct PerHitSpreadOffset
	{
		public static readonly PerHitSpreadOffset Zero = new PerHitSpreadOffset
		{
			WorldOffset = Vector3.zero,
			ScreenOffset = Vector2.zero
		};

		public Vector3 WorldOffset;

		public Vector2 ScreenOffset;
	}

	private const int HardMaxActiveNumbers = 256;

	private const int HardMaxNumbersPerFrame = 256;

	private const int HardMaxActiveVanillaNumbers = 64;

	private static DamageNumberManager _instance;

	private static readonly Comparison<AggregateEntry> AggregateBySequence = (AggregateEntry a, AggregateEntry b) => a.FirstSequence.CompareTo(b.FirstSequence);

	private static readonly Comparison<PerHitEntry> PerHitBySequence = (PerHitEntry a, PerHitEntry b) => a.Sequence.CompareTo(b.Sequence);

	private readonly Dictionary<AggregateKey, AggregateEntry> _aggregates = new Dictionary<AggregateKey, AggregateEntry>();

	private readonly List<AggregateEntry> _flushEntries = new List<AggregateEntry>();

	private readonly List<PerHitEntry> _perHitQueue = new List<PerHitEntry>();

	private readonly Dictionary<PerHitSpreadKey, int> _perHitSpreadCounters = new Dictionary<PerHitSpreadKey, int>();

	private readonly Dictionary<int, int> _shownRowsPerTarget = new Dictionary<int, int>();

	private float _screenLinePerPoint;

	private float _worldLinePerPoint;

	private float _vanillaLineAtUnitScale;

	private Canvas _screenCanvas;

	private bool _clearedForLoading;

	private readonly List<ScreenDamageNumberView> _screenPool = new List<ScreenDamageNumberView>();

	private readonly List<DamageNumberView> _worldPool = new List<DamageNumberView>();

	private readonly List<VanillaDamageNumberView> _vanillaPool = new List<VanillaDamageNumberView>();

	private readonly List<Vector2> _crowdingPositions = new List<Vector2>(256);

	private bool _vanillaReady;

	private bool _vanillaBoundaryBroken;

	private int _vanillaFlyIndex;

	private int _crowdingFrame = -1;

	private int _spawnBudgetFrame = -1;

	private int _spawnedThisFrame;

	private int _droppedThisFrame;

	private int _crowdingSkippedThisFrame;

	private float _lastBudgetLogTime = -999f;

	private bool _inputErrorLogged;

	private Camera _cachedCamera;

	private float _nextCameraLookupTime;

	private Material _screenSharedMaterial;

	private Material _worldSharedMaterial;

	private RenderMode _lastAppliedRenderMode;

	private bool _hasAppliedRenderMode;

	private RectTransform _screenRoot;

	private Transform _worldRoot;

	private Transform _vanillaRoot;

	public static void EnsureExists()
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		if (!((Object)(object)_instance != (Object)null))
		{
			GameObject val = new GameObject("SULFUR Damage Number Manager");
			Object.DontDestroyOnLoad((Object)val);
			_instance = val.AddComponent<DamageNumberManager>();
		}
	}

	public static void Shutdown()
	{
		if (!((Object)(object)_instance == (Object)null))
		{
			DamageNumberManager instance = _instance;
			_instance = null;
			Object.Destroy((Object)(object)((Component)instance).gameObject);
		}
	}

	public static void RegisterDamage(Unit target, DamageSourceData sourceData, float amount, float hitMultiplier, float bonus, int sequence)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)target == (Object)null))
		{
			EnsureExists();
			if ((Object)(object)_instance != (Object)null)
			{
				_instance.RegisterDamageInternal(target, sourceData, amount, hitMultiplier, bonus, sequence);
			}
		}
	}

	public static void RegisterResolvedDamage(Unit target, Vector3? fallbackPosition, int sourceId, bool melee, DamageTypes damageType, bool critical, float amount, float hitMultiplier, float bonus, int dealtFrame, int sequence)
	{
		//IL_0071: 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: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: 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)
		if (!((Object)(object)target == (Object)null) || fallbackPosition.HasValue)
		{
			EnsureExists();
			if (!((Object)(object)_instance == (Object)null))
			{
				_instance.RegisterHit(new DamageHit
				{
					TargetId = (((Object)(object)target != (Object)null) ? ((Object)target).GetInstanceID() : (int.MinValue + dealtFrame)),
					Frame = dealtFrame,
					Position = (((Object)(object)target != (Object)null) ? GetDamagePosition(target) : fallbackPosition.Value),
					SourceId = sourceId,
					Melee = melee,
					DamageType = damageType,
					Critical = critical,
					Amount = amount,
					Multiplier = hitMultiplier,
					Bonus = bonus,
					Sequence = sequence
				});
			}
		}
	}

	public static void SpawnScreenTestNumber()
	{
		//IL_0034: 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)
		EnsureExists();
		if ((Object)(object)_instance != (Object)null)
		{
			_instance.SpawnScreenFixed("999", new Vector2((float)Screen.width * 0.5f, (float)Screen.height * 0.45f), DamageNumberColor.ForMultiplier(0f));
		}
	}

	private void Update()
	{
		if (!Plugin.DebugSpawnTestOnF8.Value)
		{
			return;
		}
		try
		{
			if (Input.GetKeyDown((KeyCode)289))
			{
				SpawnScreenTestNumber();
				if (Plugin.DebugLog.Value)
				{
					Plugin.Log.LogInfo((object)"[DamageNumbers] F8 test number spawned.");
				}
			}
		}
		catch (Exception ex)
		{
			if (!_inputErrorLogged)
			{
				_inputErrorLogged = true;
				Plugin.Log.LogWarning((object)("[DamageNumbers] F8 debug input is unavailable: " + ex.Message));
			}
		}
	}

	private void RegisterDamageInternal(Unit target, DamageSourceData sourceData, float amount, float hitMultiplier, float bonus, int sequence)
	{
		//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_0031: 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_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: 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)
		RegisterHit(new DamageHit
		{
			TargetId = ((Object)target).GetInstanceID(),
			Frame = Time.frameCount,
			Position = GetDamagePosition(target),
			SourceId = DamageHit.SourceIdOf(sourceData),
			Melee = sourceData.melee,
			DamageType = sourceData.damageType,
			Critical = sourceData.isCritical,
			Amount = amount,
			Multiplier = hitMultiplier,
			Bonus = bonus,
			Sequence = sequence
		});
	}

	private void RegisterHit(DamageHit hit)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//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_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: 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)
		if (Plugin.Mode.Value == DisplayMode.PerHit)
		{
			_perHitQueue.Add(new PerHitEntry
			{
				Position = hit.Position,
				Amount = hit.Amount,
				Multiplier = hit.Multiplier,
				Bonus = hit.Bonus,
				DamageType = hit.DamageType,
				Critical = hit.Critical,
				SpreadKey = PerHitSpreadKey.Create(hit),
				Sequence = hit.Sequence
			});
			return;
		}
		AggregateKey key = AggregateKey.Create(hit);
		if (!_aggregates.TryGetValue(key, out var value))
		{
			value = new AggregateEntry
			{
				Amount = 0f,
				Position = hit.Position,
				HighestMultiplier = 0f,
				LastMultiplier = 0f,
				WeightedMultiplierSum = 0f,
				HighestBonus = 0f,
				DamageType = hit.DamageType,
				AnyCritical = false,
				TargetId = hit.TargetId,
				FirstSequence = hit.Sequence
			};
		}
		value.Amount += hit.Amount;
		value.Position = hit.Position;
		value.LastMultiplier = hit.Multiplier;
		value.AnyCritical = value.AnyCritical || hit.Critical;
		value.WeightedMultiplierSum += Mathf.Max(0f, hit.Multiplier) * Mathf.Max(0f, hit.Amount);
		if (hit.Multiplier > value.HighestMultiplier)
		{
			value.HighestMultiplier = hit.Multiplier;
		}
		if (hit.Bonus > value.HighestBonus)
		{
			value.HighestBonus = hit.Bonus;
		}
		if (hit.Sequence < value.FirstSequence)
		{
			value.FirstSequence = hit.Sequence;
		}
		_aggregates[key] = value;
	}

	private void LateUpdate()
	{
		if ((Object)(object)_instance != (Object)(object)this)
		{
			return;
		}
		UpdateVanillaAvailability();
		DamageNumbersIntegration.SetShowsHitBonus(Plugin.Enabled.Value && ResolveActiveRenderMode() != RenderMode.Vanilla);
		GameplayPhase gameplayPhase = GameStateProbe.Read();
		if (gameplayPhase == GameplayPhase.Loading)
		{
			if (!_clearedForLoading)
			{
				_clearedForLoading = true;
				ClearAllNumbers();
			}
			else
			{
				_aggregates.Clear();
				_perHitQueue.Clear();
			}
			return;
		}
		_clearedForLoading = false;
		SetScreenCanvasVisible(gameplayPhase == GameplayPhase.Running);
		FlushPending();
		ApplyRendererSelectionIfChanged();
		int maxActiveNumbers = GetMaxActiveNumbers();
		EnforcePoolLimit(_screenPool, maxActiveNumbers);
		EnforcePoolLimit(_worldPool, maxActiveNumbers);
		EnforcePoolLimit(_vanillaPool, GetMaxActiveVanillaNumbers());
		Camera mainCamera = GetMainCamera();
		float time = Time.time;
		for (int i = 0; i < _screenPool.Count; i++)
		{
			ScreenDamageNumberView screenDamageNumberView = _screenPool[i];
			if (screenDamageNumberView.IsActive)
			{
				screenDamageNumberView.Tick(mainCamera, time, Plugin.ScreenFloatSpeed.Value);
			}
		}
		for (int j = 0; j < _worldPool.Count; j++)
		{
			DamageNumberView damageNumberView = _worldPool[j];
			if (damageNumberView.IsActive)
			{
				damageNumberView.Tick(mainCamera, time);
			}
		}
		TickVanillaPool(mainCamera, time);
		MaybeLogDroppedBudget();
	}

	private void UpdateVanillaAvailability()
	{
		if (_vanillaBoundaryBroken || Plugin.Renderer.Value != RenderMode.Vanilla)
		{
			_vanillaReady = false;
			return;
		}
		try
		{
			VanillaDamageText.Poll();
			_vanillaReady = VanillaDamageText.IsReady;
		}
		catch (Exception e)
		{
			BreakVanillaBoundary(e);
		}
	}

	private void TickVanillaPool(Camera camera, float now)
	{
		if (_vanillaPool.Count == 0)
		{
			return;
		}
		try
		{
			for (int i = 0; i < _vanillaPool.Count; i++)
			{
				VanillaDamageNumberView vanillaDamageNumberView = _vanillaPool[i];
				if (vanillaDamageNumberView.IsActive)
				{
					vanillaDamageNumberView.Tick(camera, now);
				}
			}
		}
		catch (Exception e)
		{
			BreakVanillaBoundary(e);
		}
	}

	private void BreakVanillaBoundary(Exception e)
	{
		_vanillaReady = false;
		if (!_vanillaBoundaryBroken)
		{
			_vanillaBoundaryBroken = true;
			_vanillaPool.Clear();
			if ((Object)(object)_vanillaRoot != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_vanillaRoot).gameObject);
				_vanillaRoot = null;
			}
			Plugin.Log.LogWarning((object)("[DamageNumbers] The game's damage text renderer failed and was disabled; Renderer=Vanilla falls back to ScreenSpace. " + e));
		}
	}

	private void FlushPending()
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		if (_perHitQueue.Count == 0 && _aggregates.Count == 0)
		{
			return;
		}
		_shownRowsPerTarget.Clear();
		if (_perHitQueue.Count > 0)
		{
			_perHitQueue.Sort(PerHitBySequence);
			_perHitSpreadCounters.Clear();
			for (int i = 0; i < _perHitQueue.Count; i++)
			{
				PerHitEntry perHitEntry = _perHitQueue[i];
				PerHitSpreadOffset nextPerHitSpread = GetNextPerHitSpread(perHitEntry.SpreadKey);
				SpawnNumber(perHitEntry.Position, perHitEntry.Amount, perHitEntry.Multiplier, perHitEntry.Bonus, perHitEntry.DamageType, perHitEntry.Critical, useJitter: true, nextPerHitSpread.WorldOffset, nextPerHitSpread.ScreenOffset, 0, applyCrowding: true);
			}
			_perHitQueue.Clear();
		}
		if (_aggregates.Count <= 0)
		{
			return;
		}
		_flushEntries.Clear();
		foreach (KeyValuePair<AggregateKey, AggregateEntry> aggregate in _aggregates)
		{
			_flushEntries.Add(aggregate.Value);
		}
		_aggregates.Clear();
		_flushEntries.Sort(AggregateBySequence);
		for (int j = 0; j < _flushEntries.Count; j++)
		{
			AggregateEntry entry = _flushEntries[j];
			_shownRowsPerTarget.TryGetValue(entry.TargetId, out var value);
			if (SpawnNumber(entry.Position, entry.Amount, ResolveAggregateMultiplier(entry), entry.HighestBonus, entry.DamageType, entry.AnyCritical, useJitter: false, Vector3.zero, Vector2.zero, value, applyCrowding: true))
			{
				_shownRowsPerTarget[entry.TargetId] = value + 1;
			}
		}
		_flushEntries.Clear();
	}

	private bool SpawnNumber(Vector3 position, float amount, float hitMultiplier, float bonus, DamageTypes damageType, bool critical, bool useJitter, Vector3 extraWorldJitter, Vector2 extraScreenJitter, int stackRow, bool applyCrowding)
	{
		//IL_002a: 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_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: 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)
		//IL_005b: 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_006b: 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)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: 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_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: 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_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: 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_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: 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_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0207: Unknown result type (might be due to invalid IL or missing references)
		EnsureFrameCounters();
		RenderMode renderMode = ResolveActiveRenderMode();
		bool flag = renderMode == RenderMode.ScreenSpace || renderMode == RenderMode.Both;
		bool flag2 = renderMode == RenderMode.WorldTextMeshPro || renderMode == RenderMode.Both;
		bool flag3 = renderMode == RenderMode.Vanilla;
		Vector3 val = extraWorldJitter;
		Vector2 val2 = extraScreenJitter;
		if (useJitter)
		{
			if (Plugin.RandomJitter.Value > 0f)
			{
				Vector2 val3 = Random.insideUnitCircle * Plugin.RandomJitter.Value;
				val += new Vector3(val3.x, 0f, val3.y);
			}
			if (Plugin.ScreenJitter.Value > 0f)
			{
				val2 += Random.insideUnitCircle * Plugin.ScreenJitter.Value;
			}
		}
		Vector3 val4 = Vector3.zero;
		Vector3 val5 = Vector3.zero;
		if (stackRow > 0)
		{
			if (flag)
			{
				val2.y += (float)stackRow * GetScreenLineHeight();
			}
			if (flag2)
			{
				val4 = Vector3.up * ((float)stackRow * GetWorldLineHeight());
			}
			if (flag3)
			{
				val5 = Vector3.up * ((float)stackRow * GetVanillaLineHeight());
			}
		}
		bool flag4 = false;
		Vector2 screenPosition;
		if (applyCrowding && Plugin.EnableCrowdingProtection.Value)
		{
			flag4 = (flag ? TryGetCrowdingScreenPosition(position, val2, out screenPosition) : TryGetCrowdingScreenPosition(position + val + (flag3 ? val5 : val4), Vector2.zero, out screenPosition));
			if (flag4 && IsAreaCrowded(screenPosition))
			{
				_crowdingSkippedThisFrame++;
				return false;
			}
		}
		else
		{
			screenPosition = Vector2.zero;
		}
		if (flag3 && !_vanillaReady)
		{
			return false;
		}
		if (!TryConsumeSpawnBudget())
		{
			return false;
		}
		bool flag5;
		if (flag3)
		{
			flag5 = SpawnVanilla(amount, hitMultiplier, damageType, critical, position + val + val5);
		}
		else
		{
			string text = DamageNumberText.WithBonus(FormatDamage(amount), bonus);
			Color color = DamageNumberColor.ForMultiplier(hitMultiplier);
			flag5 = false;
			if (flag)
			{
				flag5 |= SpawnScreenWorld(text, position, val2, color);
			}
			if (flag2)
			{
				flag5 |= SpawnWorld(text, position, val + val4, color);
			}
		}
		if (flag5 && flag4)
		{
			_crowdingPositions.Add(screenPosition);
		}
		return flag5;
	}

	private float GetScreenLineHeight()
	{
		if (_screenLinePerPoint <= 0f)
		{
			ScreenDamageNumberView screenDamageNumberView = ((_screenPool.Count > 0) ? _screenPool[0] : CreateScreenView());
			if (screenDamageNumberView != null)
			{
				_screenLinePerPoint = screenDamageNumberView.LineHeightPerPoint;
			}
		}
		return _screenLinePerPoint * (float)Mathf.Max(8, Plugin.ScreenFontSize.Value);
	}

	private float GetWorldLineHeight()
	{
		if (_worldLinePerPoint <= 0f)
		{
			DamageNumberView damageNumberView = ((_worldPool.Count > 0) ? _worldPool[0] : CreateWorldView());
			if (damageNumberView != null)
			{
				_worldLinePerPoint = damageNumberView.LineHeightPerPoint;
			}
		}
		return _worldLinePerPoint * Plugin.FontSize.Value * Plugin.WorldScale.Value;
	}

	private float GetVanillaLineHeight()
	{
		if (_vanillaLineAtUnitScale <= 0f && _vanillaPool.Count > 0)
		{
			try
			{
				_vanillaLineAtUnitScale = _vanillaPool[0].LineHeightAtUnitScale;
			}
			catch (Exception e)
			{
				BreakVanillaBoundary(e);
			}
		}
		return _vanillaLineAtUnitScale * Mathf.Max(0.01f, Plugin.VanillaScale.Value);
	}

	private bool SpawnScreenWorld(string text, Vector3 worldPosition, Vector2 jitter, Color color)
	{
		//IL_000e: 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_0034: Unknown result type (might be due to invalid IL or missing references)
		ScreenDamageNumberView screenDamageNumberView = AcquireScreenView();
		if (screenDamageNumberView == null)
		{
			return false;
		}
		screenDamageNumberView.PlayWorld(text, worldPosition, jitter, Mathf.Max(0.05f, Plugin.Lifetime.Value), Mathf.Max(8, Plugin.ScreenFontSize.Value), color);
		return true;
	}

	private void SpawnScreenFixed(string text, Vector2 screenPosition, Color color)
	{
		//IL_000d: 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)
		AcquireScreenView()?.PlayFixed(text, screenPosition, Mathf.Max(0.05f, Plugin.Lifetime.Value), Mathf.Max(8, Plugin.ScreenFontSize.Value), color);
	}

	private bool SpawnWorld(string text, Vector3 worldPosition, Vector3 jitter, Color color)
	{
		//IL_000e: 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_0042: Unknown result type (might be due to invalid IL or missing references)
		DamageNumberView damageNumberView = AcquireWorldView();
		if (damageNumberView == null)
		{
			return false;
		}
		damageNumberView.Play(text, worldPosition, jitter, Mathf.Max(0.05f, Plugin.Lifetime.Value), Plugin.FloatSpeed.Value, Plugin.WorldScale.Value, Plugin.FontSize.Value, color);
		return true;
	}

	private bool SpawnVanilla(float amount, float hitMultiplier, DamageTypes damageType, bool critical, Vector3 worldPosition)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			VanillaDamageNumberView vanillaDamageNumberView = AcquireVanillaView();
			if (vanillaDamageNumberView == null)
			{
				return false;
			}
			Color color = (Plugin.VanillaUseDamageTypeColors.Value ? VanillaDamageText.GetDamageTypeColor(damageType) : DamageNumberColor.ForMultiplier(hitMultiplier));
			int amount2 = Mathf.Max(1, Mathf.RoundToInt(amount));
			_vanillaFlyIndex++;
			bool flyRight = _vanillaFlyIndex % 2 == 0;
			vanillaDamageNumberView.Play(amount2, color, worldPosition, Plugin.VanillaScale.Value, flyRight, critical);
			return true;
		}
		catch (Exception e)
		{
			BreakVanillaBoundary(e);
			return false;
		}
	}

	private void SetScreenCanvasVisible(bool visible)
	{
		if ((Object)(object)_screenCanvas != (Object)null && ((Behaviour)_screenCanvas).enabled != visible)
		{
			((Behaviour)_screenCanvas).enabled = visible;
		}
	}

	private void ClearAllNumbers()
	{
		_aggregates.Clear();
		_perHitQueue.Clear();
		_flushEntries.Clear();
		for (int i = 0; i < _screenPool.Count; i++)
		{
			_screenPool[i].Stop();
		}
		for (int j = 0; j < _worldPool.Count; j++)
		{
			_worldPool[j].Stop();
		}
		if (_vanillaPool.Count <= 0)
		{
			return;
		}
		try
		{
			for (int k = 0; k < _vanillaPool.Count; k++)
			{
				_vanillaPool[k].Stop();
			}
		}
		catch (Exception e)
		{
			BreakVanillaBoundary(e);
		}
	}

	private RenderMode ResolveActiveRenderMode()
	{
		RenderMode value = Plugin.Renderer.Value;
		if (value != RenderMode.Vanilla)
		{
			return value;
		}
		if (!_vanillaReady)
		{
			return RenderMode.ScreenSpace;
		}
		return RenderMode.Vanilla;
	}

	private VanillaDamageNumberView AcquireVanillaView()
	{
		if (_vanillaBoundaryBroken)
		{
			return null;
		}
		GameObject textPrefab = VanillaDamageText.TextPrefab;
		if ((Object)(object)textPrefab == (Object)null)
		{
			return null;
		}
		EnsureVanillaRoot();
		int maxActiveVanillaNumbers = GetMaxActiveVanillaNumbers();
		VanillaDamageNumberView vanillaDamageNumberView = null;
		VanillaDamageNumberView vanillaDamageNumberView2 = null;
		int num = 0;
		for (int i = 0; i < _vanillaPool.Count; i++)
		{
			VanillaDamageNumberView vanillaDamageNumberView3 = _vanillaPool[i];
			if (!vanillaDamageNumberView3.IsActive)
			{
				if (vanillaDamageNumberView == null)
				{
					vanillaDamageNumberView = vanillaDamageNumberView3;
				}
				continue;
			}
			num++;
			if (vanillaDamageNumberView2 == null || vanillaDamageNumberView3.StartTime < vanillaDamageNumberView2.StartTime)
			{
				vanillaDamageNumberView2 = vanillaDamageNumberView3;
			}
		}
		if (num < maxActiveVanillaNumbers)
		{
			if (vanillaDamageNumberView != null)
			{
				return vanillaDamageNumberView;
			}
			VanillaDamageNumberView vanillaDamageNumberView4 = VanillaDamageNumberView.TryCreate(_vanillaRoot, textPrefab);
			if (vanillaDamageNumberView4 == null)
			{
				return null;
			}
			_vanillaPool.Add(vanillaDamageNumberView4);
			return vanillaDamageNumberView4;
		}
		if (vanillaDamageNumberView2 != null)
		{
			vanillaDamageNumberView2.Stop();
			return vanillaDamageNumberView2;
		}
		return vanillaDamageNumberView;
	}

	private ScreenDamageNumberView AcquireScreenView()
	{
		EnsureScreenRoot();
		int maxActiveNumbers = GetMaxActiveNumbers();
		ScreenDamageNumberView screenDamageNumberView = null;
		ScreenDamageNumberView screenDamageNumberView2 = null;
		int num = 0;
		for (int i = 0; i < _screenPool.Count; i++)
		{
			ScreenDamageNumberView screenDamageNumberView3 = _screenPool[i];
			if (!screenDamageNumberView3.IsActive)
			{
				if (screenDamageNumberView == null)
				{
					screenDamageNumberView = screenDamageNumberView3;
				}
				continue;
			}
			num++;
			if (screenDamageNumberView2 == null || screenDamageNumberView3.StartTime < screenDamageNumberView2.StartTime)
			{
				screenDamageNumberView2 = screenDamageNumberView3;
			}
		}
		if (num < maxActiveNumbers)
		{
			if (screenDamageNumberView != null)
			{
				return screenDamageNumberView;
			}
			return CreateScreenView();
		}
		if (screenDamageNumberView2 != null)
		{
			screenDamageNumberView2.Stop();
			return screenDamageNumberView2;
		}
		return screenDamageNumberView;
	}

	private ScreenDamageNumberView CreateScreenView()
	{
		EnsureScreenRoot();
		ScreenDamageNumberView screenDamageNumberView = new ScreenDamageNumberView(_screenRoot, _screenSharedMaterial);
		if ((Object)(object)_screenSharedMaterial == (Object)null)
		{
			_screenSharedMaterial = screenDamageNumberView.FontSharedMaterial;
			screenDamageNumberView.SetSharedMaterial(_screenSharedMaterial);
		}
		_screenPool.Add(screenDamageNumberView);
		return screenDamageNumberView;
	}

	private DamageNumberView CreateWorldView()
	{
		EnsureWorldRoot();
		DamageNumberView damageNumberView = new DamageNumberView(_worldRoot, _worldSharedMaterial);
		if ((Object)(object)_worldSharedMaterial == (Object)null)
		{
			_worldSharedMaterial = damageNumberView.FontSharedMaterial;
			damageNumberView.SetSharedMaterial(_worldSharedMaterial);
		}
		_worldPool.Add(damageNumberView);
		return damageNumberView;
	}

	private DamageNumberView AcquireWorldView()
	{
		EnsureWorldRoot();
		int maxActiveNumbers = GetMaxActiveNumbers();
		DamageNumberView damageNumberView = null;
		DamageNumberView damageNumberView2 = null;
		int num = 0;
		for (int i = 0; i < _worldPool.Count; i++)
		{
			DamageNumberView damageNumberView3 = _worldPool[i];
			if (!damageNumberView3.IsActive)
			{
				if (damageNumberView == null)
				{
					damageNumberView = damageNumberView3;
				}
				continue;
			}
			num++;
			if (damageNumberView2 == null || damageNumberView3.StartTime < damageNumberView2.StartTime)
			{
				damageNumberView2 = damageNumberView3;
			}
		}
		if (num < maxActiveNumbers)
		{
			if (damageNumberView != null)
			{
				return damageNumberView;
			}
			return CreateWorldView();
		}
		if (damageNumberView2 != null)
		{
			damageNumberView2.Stop();
			return damageNumberView2;
		}
		return damageNumberView;
	}

	private Camera GetMainCamera()
	{
		if ((Object)(object)_cachedCamera != (Object)null && ((Behaviour)_cachedCamera).isActiveAndEnabled)
		{
			return _cachedCamera;
		}
		float unscaledTime = Time.unscaledTime;
		if (unscaledTime < _nextCameraLookupTime)
		{
			return null;
		}
		_nextCameraLookupTime = unscaledTime + 0.5f;
		_cachedCamera = Camera.main;
		return _cachedCamera;
	}

	private bool TryGetCrowdingScreenPosition(Vector3 worldPosition, Vector2 screenJitter, out Vector2 screenPosition)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//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_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: 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_003a: 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)
		Camera mainCamera = GetMainCamera();
		PrepareCrowdingPositions(mainCamera);
		if ((Object)(object)mainCamera == (Object)null)
		{
			screenPosition = Vector2.zero;
			return false;
		}
		Vector3 val = mainCamera.WorldToScreenPoint(worldPosition);
		if (val.z <= 0f)
		{
			screenPosition = Vector2.zero;
			return false;
		}
		screenPosition = new Vector2(val.x, val.y) + screenJitter;
		return true;
	}

	private void PrepareCrowdingPositions(Camera camera)
	{
		//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_005c: 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_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: 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_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		int frameCount = Time.frameCount;
		if (_crowdingFrame == frameCount)
		{
			return;
		}
		_crowdingFrame = frameCount;
		_crowdingPositions.Clear();
		if ((Object)(object)camera == (Object)null)
		{
			return;
		}
		RenderMode renderMode = ResolveActiveRenderMode();
		switch (renderMode)
		{
		case RenderMode.Vanilla:
			try
			{
				for (int k = 0; k < _vanillaPool.Count; k++)
				{
					VanillaDamageNumberView vanillaDamageNumberView = _vanillaPool[k];
					if (vanillaDamageNumberView.IsActive)
					{
						Vector3 val2 = camera.WorldToScreenPoint(vanillaDamageNumberView.CurrentWorldPosition);
						if (val2.z > 0f)
						{
							_crowdingPositions.Add(new Vector2(val2.x, val2.y));
						}
					}
				}
				break;
			}
			catch (Exception e)
			{
				BreakVanillaBoundary(e);
				break;
			}
		default:
			if (renderMode != RenderMode.Both)
			{
				for (int i = 0; i < _worldPool.Count; i++)
				{
					DamageNumberView damageNumberView = _worldPool[i];
					if (damageNumberView.IsActive)
					{
						Vector3 val = camera.WorldToScreenPoint(damageNumberView.CurrentWorldPosition);
						if (val.z > 0f)
						{
							_crowdingPositions.Add(new Vector2(val.x, val.y));
						}
					}
				}
				break;
			}
			goto case RenderMode.ScreenSpace;
		case RenderMode.ScreenSpace:
		{
			for (int j = 0; j < _screenPool.Count; j++)
			{
				ScreenDamageNumberView screenDamageNumberView = _screenPool[j];
				if (screenDamageNumberView.IsActive && screenDamageNumberView.IsVisible)
				{
					_crowdingPositions.Add(screenDamageNumberView.CurrentScreenPosition);
				}
			}
			break;
		}
		}
	}

	private bool IsAreaCrowded(Vector2 screenPosition)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		float num = Mathf.Clamp(Plugin.CrowdingRadius.Value, 8f, 120f);
		float num2 = num * num;
		int num3 = Mathf.Clamp(Plugin.MaxNearbyNumbers.Value, 1, 32);
		int num4 = 0;
		for (int i = 0; i < _crowdingPositions.Count; i++)
		{
			Vector2 val = _crowdingPositions[i] - screenPosition;
			if (!(((Vector2)(ref val)).sqrMagnitude > num2))
			{
				num4++;
				if (num4 >= num3)
				{
					return true;
				}
			}
		}
		return false;
	}

	private void EnsureScreenRoot()
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Expected O, but got Unknown
		if (!((Object)(object)_screenRoot != (Object)null))
		{
			GameObject val = new GameObject("SULFUR Damage Numbers Canvas", new Type[3]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler)
			});
			val.transform.SetParent(((Component)this).transform, false);
			Canvas component = val.GetComponent<Canvas>();
			component.renderMode = (RenderMode)0;
			component.overrideSorting = true;
			component.sortingOrder = 32760;
			CanvasScaler component2 = val.GetComponent<CanvasScaler>();
			component2.uiScaleMode = (ScaleMode)0;
			component2.scaleFactor = 1f;
			_screenRoot = val.GetComponent<RectTransform>();
			_screenCanvas = component;
			((Behaviour)component).enabled = GameStateProbe.Read() == GameplayPhase.Running;
		}
	}

	private void EnsureWorldRoot()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		if (!((Object)(object)_worldRoot != (Object)null))
		{
			GameObject val = new GameObject("SULFUR Damage Numbers World Pool");
			val.transform.SetParent(((Component)this).transform, false);
			_worldRoot = val.transform;
		}
	}

	private void EnsureVanillaRoot()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		if (!((Object)(object)_vanillaRoot != (Object)null))
		{
			GameObject val = new GameObject("SULFUR Damage Numbers Vanilla Pool");
			val.transform.SetParent(((Component)this).transform, false);
			_vanillaRoot = val.transform;
		}
	}

	private void ApplyRendererSelectionIfChanged()
	{
		RenderMode renderMode = ResolveActiveRenderMode();
		if (_hasAppliedRenderMode && renderMode == _lastAppliedRenderMode)
		{
			return;
		}
		_lastAppliedRenderMode = renderMode;
		_hasAppliedRenderMode = true;
		bool num = renderMode == RenderMode.ScreenSpace || renderMode == RenderMode.Both;
		bool flag = renderMode == RenderMode.WorldTextMeshPro || renderMode == RenderMode.Both;
		bool flag2 = renderMode == RenderMode.Vanilla;
		if (!num)
		{
			for (int i = 0; i < _screenPool.Count; i++)
			{
				_screenPool[i].Stop();
			}
		}
		if (!flag)
		{
			for (int j = 0; j < _worldPool.Count; j++)
			{
				_worldPool[j].Stop();
			}
		}
		if (flag2 || _vanillaPool.Count <= 0)
		{
			return;
		}
		try
		{
			for (int k = 0; k < _vanillaPool.Count; k++)
			{
				_vanillaPool[k].Stop();
			}
		}
		catch (Exception e)
		{
			BreakVanillaBoundary(e);
		}
	}

	private static void EnforcePoolLimit<T>(List<T> pool, int maxActive) where T : class, IDamageNumberView
	{
		int num = 0;
		for (int i = 0; i < pool.Count; i++)
		{
			if (pool[i].IsActive)
			{
				num++;
			}
		}
		while (num > maxActive)
		{
			T val = null;
			for (int j = 0; j < pool.Count; j++)
			{
				T val2 = pool[j];
				if (val2.IsActive && (val == null || val2.StartTime < val.StartTime))
				{
					val = val2;
				}
			}
			if (val != null)
			{
				val.Stop();
				num--;
				continue;
			}
			break;
		}
	}

	private void EnsureFrameCounters()
	{
		int frameCount = Time.frameCount;
		if (_spawnBudgetFrame != frameCount)
		{
			_spawnBudgetFrame = frameCount;
			_spawnedThisFrame = 0;
			_droppedThisFrame = 0;
			_crowdingSkippedThisFrame = 0;
		}
	}

	private bool TryConsumeSpawnBudget()
	{
		EnsureFrameCounters();
		int num = Mathf.Clamp(Plugin.MaxNumbersPerFrame.Value, 1, 256);
		if (_spawnedThisFrame >= num)
		{
			_droppedThisFrame++;
			return false;
		}
		_spawnedThisFrame++;
		return true;
	}

	private void MaybeLogDroppedBudget()
	{
		if (_spawnBudgetFrame == Time.frameCount && Plugin.DebugLog.Value && (_droppedThisFrame > 0 || _crowdingSkippedThisFrame > 0))
		{
			float unscaledTime = Time.unscaledTime;
			if (!(unscaledTime - _lastBudgetLogTime < 1f))
			{
				_lastBudgetLogTime = unscaledTime;
				Plugin.Log.LogWarning((object)("[DamageNumbers] frame " + _spawnBudgetFrame + ": droppedByFrameBudget=" + _droppedThisFrame + ", skippedByCrowding=" + _crowdingSkippedThisFrame + "."));
			}
		}
	}

	private static int GetMaxActiveNumbers()
	{
		return Mathf.Clamp(Plugin.MaxActiveNumbers.Value, 1, 256);
	}

	private static int GetMaxActiveVanillaNumbers()
	{
		return Mathf.Clamp(Plugin.MaxActiveNumbers.Value, 1, 64);
	}

	private static Vector3 GetDamagePosition(Unit target)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: 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)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		Npc val = (Npc)(object)((target is Npc) ? target : null);
		if ((Object)(object)val != (Object)null && (Object)(object)val.overhead != (Object)null)
		{
			return val.overhead.position;
		}
		return ((Component)target).transform.position + Vector3.up * 2f;
	}

	private PerHitSpreadOffset GetNextPerHitSpread(PerHitSpreadKey key)
	{
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: 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_00ac: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		if (!_perHitSpreadCounters.TryGetValue(key, out var value))
		{
			value = 0;
		}
		_perHitSpreadCounters[key] = value + 1;
		if (value <= 0)
		{
			return PerHitSpreadOffset.Zero;
		}
		float num = (float)value * 2.3999631f;
		float num2 = Mathf.Sqrt((float)value);
		Vector2 val = default(Vector2);
		((Vector2)(ref val))..ctor(Mathf.Cos(num), Mathf.Sin(num));
		float num3 = Mathf.Min(42f, Mathf.Max(0f, Plugin.PerHitStackScreenSpread.Value) * num2);
		float num4 = Mathf.Min(0.36f, Mathf.Max(0f, Plugin.PerHitStackWorldSpread.Value) * num2);
		return new PerHitSpreadOffset
		{
			ScreenOffset = val * num3,
			WorldOffset = new Vector3(val.x * num4, 0f, val.y * num4)
		};
	}

	private static string FormatDamage(float amount)
	{
		if (Plugin.ShowDecimals.Value)
		{
			float num = Mathf.Round(amount * 10f) / 10f;
			float num2 = Mathf.Round(num);
			if (Mathf.Abs(num - num2) <= 0.0001f)
			{
				int num3 = Mathf.RoundToInt(num2);
				if (num3 <= 0 && amount > 0f)
				{
					num3 = 1;
				}
				return num3.ToString(CultureInfo.InvariantCulture);
			}
			return num.ToString("0.0", CultureInfo.InvariantCulture);
		}
		int num4 = Mathf.RoundToInt(amount);
		if (num4 <= 0)
		{
			num4 = 1;
		}
		return num4.ToString(CultureInfo.InvariantCulture);
	}

	private static float ResolveAggregateMultiplier(AggregateEntry entry)
	{
		switch (Plugin.AggregateColorMode.Value)
		{
		case MergedColorMode.LastHit:
			return entry.LastMultiplier;
		case MergedColorMode.DamageWeightedAverage:
			if (entry.Amount > 0.0001f)
			{
				return entry.WeightedMultiplierSum / entry.Amount;
			}
			return entry.HighestMultiplier;
		default:
			return entry.HighestMultiplier;
		}
	}

	private void OnDestroy()
	{
		_aggregates.Clear();
		_flushEntries.Clear();
		_perHitQueue.Clear();
		_shownRowsPerTarget.Clear();
		_perHitSpreadCounters.Clear();
		_crowdingPositions.Clear();
		_screenPool.Clear();
		_worldPool.Clear();
		_vanillaPool.Clear();
		_screenRoot = null;
		_screenCanvas = null;
		_worldRoot = null;
		_vanillaRoot = null;
		_cachedCamera = null;
		_screenSharedMaterial = null;
		_worldSharedMaterial = null;
		if ((Object)(object)_instance == (Object)(object)this)
		{
			_instance = null;
		}
	}
}
internal interface IDamageNumberView
{
	bool IsActive { get; }

	float StartTime { get; }

	void Stop();
}
internal sealed class ScreenDamageNumberView : IDamageNumberView
{
	private static readonly Vector2[] OutlineOffsets = (Vector2[])(object)new Vector2[4]
	{
		new Vector2(-1.15f, -1.15f),
		new Vector2(-1.15f, 1.15f),
		new Vector2(1.15f, -1.15f),
		new Vector2(1.15f, 1.15f)
	};

	private readonly GameObject _gameObject;

	private readonly RectTransform _rectTransform;

	private readonly TextMeshProUGUI[] _outlineTexts;

	private readonly TextMeshProUGUI _text;

	private Vector3 _worldPosition;

	private Vector2 _screenPosition;

	private Vector2 _screenJitter;

	private float _lifetime;

	private bool _fixedScreen;

	public bool IsActive { get; private set; }

	public float StartTime { get; private set; }

	public bool IsVisible { get; private set; }

	public Vector2 CurrentScreenPosition { get; private set; }

	public Material FontSharedMaterial => ((TMP_Text)_text).fontSharedMaterial;

	public float LineHeightPerPoint { get; private set; }

	public ScreenDamageNumberView(RectTransform parent, Material sharedMaterial)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		_gameObject = new GameObject("SULFUR Damage Number Screen", new Type[1] { typeof(RectTransform) });
		_gameObject.transform.SetParent((Transform)(object)parent, false);
		_rectTransform = _gameObject.GetComponent<RectTransform>();
		_rectTransform.anchorMin = Vector2.zero;
		_rectTransform.anchorMax = Vector2.zero;
		_rectTransform.pivot = new Vector2(0.5f, 0.5f);
		_rectTransform.sizeDelta = new Vector2(240f, 80f);
		_outlineTexts = (TextMeshProUGUI[])(object)new TextMeshProUGUI[OutlineOffsets.Length];
		for (int i = 0; i < _outlineTexts.Length; i++)
		{
			_outlineTexts[i] = CreateTextLayer("Outline" + i, OutlineOffsets[i]);
		}
		_text = CreateTextLayer("Face", Vector2.zero);
		if ((Object)(object)sharedMaterial != (Object)null)
		{
			SetSharedMaterial(sharedMaterial);
		}
		else if ((Object)(object)((TMP_Text)_text).fontSharedMaterial != (Object)null)
		{
			SetOutlineSharedMaterial(((TMP_Text)_text).fontSharedMaterial);
		}
		LineHeightPerPoint = DamageNumberViews.MeasureLineHeightPerPoint((TMP_Text)(object)_text);
		Stop();
	}

	private TextMeshProUGUI CreateTextLayer(string name, Vector2 offset)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: 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_0062: 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_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[3]
		{
			typeof(RectTransform),
			typeof(CanvasRenderer),
			typeof(TextMeshProUGUI)
		});
		val.transform.SetParent((Transform)(object)_rectTransform, false);
		RectTransform component = val.GetComponent<RectTransform>();
		component.anchorMin = Vector2.zero;
		component.anchorMax = Vector2.one;
		component.offsetMin = Vector2.zero;
		component.offsetMax = Vector2.zero;
		component.pivot = new Vector2(0.5f, 0.5f);
		component.anchoredPosition = offset;
		TextMeshProUGUI component2 = val.GetComponent<TextMeshProUGUI>();
		((TMP_Text)component2).alignment = (TextAlignmentOptions)514;
		((TMP_Text)component2).fontStyle = (FontStyles)1;
		((TMP_Text)component2).textWrappingMode = (TextWrappingModes)0;
		((TMP_Text)component2).overflowMode = (TextOverflowModes)0;
		((Graphic)component2).raycastTarget = false;
		return component2;
	}

	public void SetSharedMaterial(Material sharedMaterial)
	{
		if (!((Object)(object)sharedMaterial == (Object)null))
		{
			((TMP_Text)_text).fontSharedMaterial = sharedMaterial;
			SetOutlineSharedMaterial(sharedMaterial);
		}
	}

	private void SetOutlineSharedMaterial(Material sharedMaterial)
	{
		for (int i = 0; i < _outlineTexts.Length; i++)
		{
			((TMP_Text)_outlineTexts[i]).fontSharedMaterial = sharedMaterial;
		}
	}

	public void PlayWorld(string value, Vector3 worldPosition, Vector2 jitter, float lifetime, int fontSize, Color color)
	{
		//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)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		_worldPosition = worldPosition;
		_screenJitter = jitter;
		_fixedScreen = false;
		PlayCommon(value, lifetime, fontSize, color);
	}

	public void PlayFixed(string value, Vector2 screenPosition, float lifetime, int fontSize, Color color)
	{
		//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)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		_screenPosition = screenPosition;
		_screenJitter = Vector2.zero;
		_fixedScreen = true;
		PlayCommon(value, lifetime, fontSize, color);
	}

	private void PlayCommon(string value, float lifetime, int fontSize, Color color)
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		StartTime = Time.time;
		_lifetime = Mathf.Max(0.05f, lifetime);
		color.a = 1f;
		((TMP_Text)_text).text = value;
		((TMP_Text)_text).fontSize = fontSize;
		((Graphic)_text).color = color;
		_text.canvasRenderer.SetAlpha(1f);
		for (int i = 0; i < _outlineTexts.Length; i++)
		{
			TextMeshProUGUI obj = _outlineTexts[i];
			((TMP_Text)obj).text = value;
			((TMP_Text)obj).fontSize = fontSize;
			((Graphic)obj).color = Color.black;
			obj.canvasRenderer.SetAlpha(1f);
			((Behaviour)obj).enabled = true;
		}
		((Behaviour)_text).enabled = true;
		_gameObject.SetActive(true);
		IsVisible = true;
		IsActive = true;
	}

	public void Tick(Camera camera, float now, float floatSpeed)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: 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_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: 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_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		if (!IsActive)
		{
			return;
		}
		float num = now - StartTime;
		if (num >= _lifetime)
		{
			Stop();
			return;
		}
		Vector2 val = default(Vector2);
		if (_fixedScreen)
		{
			val = _screenPosition;
		}
		else
		{
			if ((Object)(object)camera == (Object)null)
			{
				SetLayersEnabled(enabled: false);
				IsVisible = false;
				return;
			}
			Vector3 val2 = _worldPosition + Vector3.up * (num * 0.25f);
			Vector3 val3 = camera.WorldToScreenPoint(val2);
			if (val3.z <= 0f)
			{
				SetLayersEnabled(enabled: false);
				IsVisible = false;
				return;
			}
			((Vector2)(ref val))..ctor(val3.x, val3.y);
		}
		SetLayersEnabled(enabled: true);
		IsVisible = true;
		val += _screenJitter;
		val.y += num * floatSpeed;
		CurrentScreenPosition = val;
		_rectTransform.anchoredPosition = val;
		float alpha = Mathf.Clamp01(1f - num / _lifetime);
		_text.canvasRenderer.SetAlpha(alpha);
		for (int i = 0; i < _outlineTexts.Length; i++)
		{
			_outlineTexts[i].canvasRenderer.SetAlpha(alpha);
		}
	}

	private void SetLayersEnabled(bool enabled)
	{
		((Behaviour)_text).enabled = enabled;
		for (int i = 0; i < _outlineTexts.Length; i++)
		{
			((Behaviour)_outlineTexts[i]).enabled = enabled;
		}
	}

	public void Stop()
	{
		IsActive = false;
		IsVisible = false;
		if ((Object)(object)_text != (Object)null)
		{
			_text.canvasRenderer.SetAlpha(0f);
			((Behaviour)_text).enabled = false;
		}
		if (_outlineTexts != null)
		{
			for (int i = 0; i < _outlineTexts.Length; i++)
			{
				TextMeshProUGUI val = _outlineTexts[i];
				if (!((Object)(object)val == (Object)null))
				{
					val.canvasRenderer.SetAlpha(0f);
					((Behaviour)val).enabled = false;
				}
			}
		}
		if ((Object)(object)_gameObject != (Object)null)
		{
			_gameObject.SetActive(false);
		}
	}
}
internal struct DamageHit
{
	public int TargetId;

	public int Frame;

	public Vector3 Position;

	public int SourceId;

	public bool Melee;

	public DamageTypes DamageType;

	public bool Critical;

	public float Amount;

	public float Multiplier;

	public float Bonus;

	public int Sequence;

	public static int SourceIdOf(DamageSourceData sourceData)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: 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_0037: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)sourceData.sourceWeapon != (Object)null)
		{
			return ((Object)sourceData.sourceWeapon).GetInstanceID();
		}
		if (sourceData.instanceId != 0)
		{
			return sourceData.instanceId;
		}
		if ((Object)(object)sourceData.sourceUnit != (Object)null)
		{
			return ((Object)sourceData.sourceUnit).GetInstanceID();
		}
		return 0;
	}
}
internal struct PerHitSpreadKey : IEquatable<PerHitSpreadKey>
{
	private int _targetId;

	private int _frame;

	private int _sourceId;

	public static PerHitSpreadKey Create(DamageHit hit)
	{
		return new PerHitSpreadKey
		{
			_targetId = hit.TargetId,
			_frame = hit.Frame,
			_sourceId = hit.SourceId
		};
	}

	public bool Equals(PerHitSpreadKey other)
	{
		if (_targetId == other._targetId && _frame == other._frame)
		{
			return _sourceId == other._sourceId;
		}
		return false;
	}

	public override bool Equals(object obj)
	{
		if (obj is PerHitSpreadKey)
		{
			return Equals((PerHitSpreadKey)obj);
		}
		return false;
	}

	public override int GetHashCode()
	{
		return (((_targetId * 397) ^ _frame) * 397) ^ _sourceId;
	}
}
internal struct AggregateKey : IEquatable<AggregateKey>
{
	private int _targetId;

	private int _frame;

	private int _sourceId;

	private int _damageType;

	private bool _melee;

	public static AggregateKey Create(DamageHit hit)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		return new AggregateKey
		{
			_targetId = hit.TargetId,
			_frame = hit.Frame,
			_sourceId = hit.SourceId,
			_damageType = Convert.ToInt32(hit.DamageType),
			_melee = hit.Melee
		};
	}

	public bool Equals(AggregateKey other)
	{
		if (_targetId == other._targetId && _frame == other._frame && _sourceId == other._sourceId && _damageType == other._damageType)
		{
			return _melee == other._melee;
		}
		return false;
	}

	public override bool Equals(object obj)
	{
		if (obj is AggregateKey)
		{
			return Equals((AggregateKey)obj);
		}
		return false;
	}

	public override int GetHashCode()
	{
		return (((((((_targetId * 397) ^ _frame) * 397) ^ _sourceId) * 397) ^ _damageType) * 397) ^ (_melee ? 1 : 0);
	}
}
internal static class DamageNumberText
{
	private const string Gap = "<space=0.25em>";

	private const string LabelOpen = "<size=55%>";

	private const string LabelClose = "</size>";

	private static float _labelBonus;

	private static string _label;

	private static string _hiddenLabel;

	private static string _multiplySign;

	private static string MultiplySign
	{
		get
		{
			if (_multiplySign == null)
			{
				TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset;
				_multiplySign = (((Object)(object)defaultFontAsset != (Object)null && defaultFontAsset.HasCharacter('×', false, false)) ? "×" : "x");
			}
			return _multiplySign;
		}
	}

	public static string WithBonus(string number, float bonus)
	{
		if (bonus <= 0f)
		{
			return number;
		}
		if (bonus != _labelBonus)
		{
			_labelBonus = bonus;
			_label = "<size=55%>" + MultiplySign + bonus.ToString("0.##", CultureInfo.InvariantCulture) + "</size>";
			_hiddenLabel = "<alpha=#00>" + _label + "<alpha=#FF><space=0.25em>";
			_label = "<space=0.25em>" + _label;
		}
		return _hiddenLabel + number + _label;
	}
}
internal static class DamageNumberColor
{
	private static readonly Color NormalDamageColor = Color.white;

	private static readonly Color OrangeDamageColor = new Color(1f, 0.5f, 0f, 1f);

	public static Color ForMultiplier(float multiplier)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		if (!Plugin.UseWeakspotColors.Value)
		{
			return NormalDamageColor;
		}
		if (multiplier <= 0f)
		{
			return NormalDamageColor;
		}
		if (Plugin.TreatOneMultiplierAsNormal.Value && Mathf.Abs(multiplier - 1f) <= 0.0001f)
		{
			return NormalDamageColor;
		}
		if (multiplier >= 1.5f)
		{
			return Color.red;
		}
		if (multiplier >= 1f)
		{
			return OrangeDamageColor;
		}
		if (multiplier >= 0.75f)
		{
			return Color.yellow;
		}
		return NormalDamageColor;
	}
}
public struct ResolvedHit
{
	public object Target;

	public Vector3? Position;

	public object Weapon;

	public bool Melee;

	public float Damage;

	public int DamageType;

	public float HitMultiplier;

	public bool IsCritical;

	public float BonusMultiplier;

	public int DealtFrame;
}
public static class DamageNumbersIntegration
{
	private const float MinShownBonus = 1.005f;

	private const float MaxShownBonus = 99f;

	private static Func<bool> _isResolvingRemoteHit;

	private static Func<float> _currentHitBonus;

	private static bool _showsHitBonus;

	public static bool ShowsHitBonus => _showsHitBonus;

	internal static bool IsResolvingRemoteHit
	{
		get
		{
			if (_isResolvingRemoteHit != null)
			{
				return _isResolvingRemoteHit();
			}
			return false;
		}
	}

	internal static float CurrentHitBonus
	{
		get
		{
			if (_currentHitBonus == null)
			{
				return 0f;
			}
			return SanitizeBonus(_currentHitBonus());
		}
	}

	public static event Action ShowsHitBonusChanged;

	public static void SetRemoteHitScope(Func<bool> isResolvingRemoteHit)
	{
		_isResolvingRemoteHit = isResolvingRemoteHit;
	}

	public static void SetHitBonusScope(Func<float> currentHitBonus)
	{
		_currentHitBonus = currentHitBonus;
	}

	public static void ShowResolvedHit(ResolvedHit hit)
	{
		DamageLogPatch.ShowResolvedHit(hit);
	}

	internal static float SanitizeBonus(float bonus)
	{
		if (float.IsNaN(bonus) || float.IsInfinity(bonus) || bonus < 1.005f)
		{
			return 0f;
		}
		return Mathf.Min(bonus, 99f);
	}

	internal static void SetShowsHitBonus(bool shows)
	{
		if (_showsHitBonus == shows)
		{
			return;
		}
		_showsHitBonus = shows;
		Action showsHitBonusChanged = DamageNumbersIntegration.ShowsHitBonusChanged;
		if (showsHitBonusChanged == null)
		{
			return;
		}
		try
		{
			showsHitBonusChanged();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("[DamageNumbers] An integration failed while following the hit bonus display: " + ex));
		}
	}

	internal static void Reset()
	{
		_isResolvingRemoteHit = null;
		_currentHitBonus = null;
		SetShowsHitBonus(shows: false);
	}
}
internal sealed class DamageNumberView : IDamageNumberView
{
	private const int WorldFadeSteps = 10;

	private const float WorldOutlineOffsetFactor = 0.02f;

	private static readonly Vector2[] OutlineDirections = (Vector2[])(object)new Vector2[4]
	{
		new Vector2(-1f, -1f),
		new Vector2(-1f, 1f),
		new Vector2(1f, -1f),
		new Vector2(1f, 1f)
	};

	private readonly GameObject _gameObject;

	private readonly Transform _transform;

	private readonly TextMeshPro[] _outlineTexts;

	private readonly TextMeshPro _text;

	private Vector3 _basePosition;

	private Vector3 _jitter;

	private float _lifetime;

	private float _floatSpeed;

	private Color _baseColor;

	private int _lastFadeStep = -1;

	public bool IsActive { get; private set; }

	public float StartTime { get; private set; }

	public Vector3 CurrentWorldPosition => _transform.position;

	public Material FontSharedMaterial => ((TMP_Text)_text).fontSharedMaterial;

	public float LineHeightPerPoint { get; private set; }

	public DamageNumberView(Transform parent, Material sharedMaterial)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		_gameObject = new GameObject("SULFUR Damage Number World");
		_gameObject.transform.SetParent(parent, false);
		_transform = _gameObject.transform;
		_outlineTexts = (TextMeshPro[])(object)new TextMeshPro[OutlineDirections.Length];
		for (int i = 0; i < _outlineTexts.Length; i++)
		{
			_outlineTexts[i] = CreateTextLayer("Outline" + i, 0);
		}
		_text = CreateTextLayer("Face", 1);
		if ((Object)(object)sharedMaterial != (Object)null)
		{
			SetSharedMaterial(sharedMaterial);
		}
		else if ((Object)(object)((TMP_Text)_text).fontSharedMaterial != (Object)null)
		{
			SetOutlineSharedMaterial(((TMP_Text)_text).fontSharedMaterial);
		}
		LineHeightPerPoint = DamageNumberViews.MeasureLineHeightPerPoint((TMP_Text)(object)_text);
		Stop();
	}

	private TextMeshPro CreateTextLayer(string name, int sortingOrder)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[1] { typeof(TextMeshPro) });
		val.transform.SetParent(_transform, false);
		TextMeshPro component = val.GetComponent<TextMeshPro>();
		((TMP_Text)component).alignment = (TextAlignmentOptions)514;
		((TMP_Text)component).fontStyle = (FontStyles)1;
		((TMP_Text)component).textWrappingMode = (TextWrappingModes)0;
		((TMP_Text)component).overflowMode = (TextOverflowModes)0;
		component.sortingOrder = sortingOrder;
		return component;
	}

	public void SetSharedMaterial(Material sharedMaterial)
	{
		if (!((Object)(object)sharedMaterial == (Object)null))
		{
			((TMP_Text)_text).fontSharedMaterial = sharedMaterial;
			SetOutlineSharedMaterial(sharedMaterial);
		}
	}

	private void SetOutlineSharedMaterial(Material sharedMaterial)
	{
		for (int i = 0; i < _outlineTexts.Length; i++)
		{
			((TMP_Text)_outlineTexts[i]).fontSharedMaterial = sharedMaterial;
		}
	}

	public void Play(string value, Vector3 position, Vector3 jitter, float lifetime, float floatSpeed, float worldScale, float fontSize, Color color)
	{
		//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)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: 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_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: 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_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		_basePosition = position;
		_jitter = jitter;
		StartTime = Time.time;
		_lifetime = Mathf.Max(0.05f, lifetime);
		_floatSpeed = floatSpeed;
		_baseColor = color;
		_lastFadeStep = 10;
		_transform.position = position + jitter;
		_transform.localScale = Vector3.one * worldScale;
		color.a = 1f;
		((TMP_Text)_text).text = value;
		((TMP_Text)_text).fontSize = fontSize;
		((Graphic)_text).color = color;
		float num = Mathf.Max(0.05f, fontSize * 0.02f);
		for (int i = 0; i < _outlineTexts.Length; i++)
		{
			TextMeshPro obj = _outlineTexts[i];
			Vector2 val = OutlineDirections[i];
			obj.transform.localPosition = new Vector3(val.x * num, val.y * num, 0f);
			((TMP_Text)obj).text = value;
			((TMP_Text)obj).fontSize = fontSize;
			((Graphic)obj).color = Color.black;
			((Behaviour)obj).enabled = true;
		}
		((Behaviour)_text).enabled = true;
		_gameObject.SetActive(true);
		IsActive = true;
	}

	public void Tick(Camera camera, float now)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: 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_0046: 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_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: 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_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		if (!IsActive)
		{
			return;
		}
		float num = now - StartTime;
		if (num >= _lifetime)
		{
			Stop();
			return;
		}
		_transform.position = _basePosition + _jitter + Vector3.up * (num * _floatSpeed);
		if ((Object)(object)camera != (Object)null)
		{
			Vector3 val = _transform.position - ((Component)camera).transform.position;
			if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
			{
				_transform.rotation = Quaternion.LookRotation(val, ((Component)camera).transform.up);
			}
		}
		int num2 = Mathf.Clamp(Mathf.CeilToInt(Mathf.Clamp01(1f - num / _lifetime) * 10f), 0, 10);
		if (num2 != _lastFadeStep)
		{
			_lastFadeStep = num2;
			float a = (float)num2 / 10f;
			Color baseColor = _baseColor;
			baseColor.a = a;
			((Graphic)_text).color = baseColor;
			Color black = Color.black;
			black.a = a;
			for (int i = 0; i < _outlineTexts.Length; i++)
			{
				((Graphic)_outlineTexts[i]).color = black;
			}
		}
	}

	public void Stop()
	{
		IsActive = false;
		if ((Object)(object)_text != (Object)null)
		{
			((Behaviour)_text).enabled = false;
		}
		if (_outlineTexts != null)
		{
			for (int i = 0; i < _outlineTexts.Length; i++)
			{
				if ((Object)(object)_outlineTexts[i] != (Object)null)
				{
					((Behaviour)_outlineTexts[i]).enabled = false;
				}
			}
		}
		if ((Object)(object)_gameObject != (Object)null)
		{
			_gameObject.SetActive(false);
		}
	}
}
internal static class DamageNumberViews
{
	public static float MeasureLineHeightPerPoint(TMP_Text text)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)text == (Object)null || text.fontSize <= 0f)
		{
			return 0f;
		}
		float num = text.GetPreferredValues("0").y / text.fontSize;
		if (!float.IsNaN(num) && !float.IsInfinity(num) && !(num < 0f))
		{
			return num;
		}
		return 0f;
	}
}
internal static class DamageReceivePatch
{
	public static void Apply(Harmony harmony)
	{
		DamageLogPatch.Apply(harmony);
	}
}
internal enum GameplayPhase
{
	NoGame,
	Running,
	Loading,
	Other
}
internal static class GameStateProbe
{
	public static bool HasGameManager => (Object)(object)StaticInstance<GameManager>.Instance != (Object)null;

	public static GameplayPhase Read()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Invalid comparison between Unknown and I4
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Invalid comparison between Unknown and I4
		GameManager instance = StaticInstance<GameManager>.Instance;
		if ((Object)(object)instance == (Object)null)
		{
			return GameplayPhase.NoGame;
		}
		GameState gameState = instance.gameState;
		if ((int)gameState != 1)
		{
			if ((int)gameState == 3)
			{
				return GameplayPhase.Running;
			}
			return GameplayPhase.Other;
		}
		return GameplayPhase.Loading;
	}
}
[BepInPlugin("ryuka.sulfur.damagenumbers", "SULFUR Damage Numbers", "1.2.0")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "ryuka.sulfur.damagenumbers";

	public const string PluginName = "SULFUR Damage Numbers";

	public const string PluginVersion = "1.2.0";

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<DisplayMode> Mode;

	internal static ConfigEntry<RenderMode> Renderer;

	internal static ConfigEntry<MergedColorMode> AggregateColorMode;

	internal static ConfigEntry<bool> VanillaUseDamageTypeColors;

	internal static ConfigEntry<float> VanillaScale;

	internal static ConfigEntry<bool> UseWeakspotColors;

	internal static ConfigEntry<bool> TreatOneMultiplierAsNormal;

	internal static ConfigEntry<bool> RequireNpcTarget;

	internal static ConfigEntry<bool> RequireSourceUnitPlayer;

	internal static ConfigEntry<bool> ShowPlayerOwnedSecondaryEffects;

	internal static ConfigEntry<bool> ShowStatusEffectDamage;

	internal static ConfigEntry<float> MinDamageToShow;

	internal static ConfigEntry<bool> ShowDecimals;

	internal static ConfigEntry<float> Lifetime;

	internal static ConfigEntry<float> FloatSpeed;

	internal static ConfigEntry<float> WorldScale;

	internal static ConfigEntry<float> RandomJitter;

	internal static ConfigEntry<float> FontSize;

	internal static ConfigEntry<int> ScreenFontSize;

	internal static ConfigEntry<float> ScreenFloatSpeed;

	internal static ConfigEntry<float> ScreenJitter;

	internal static ConfigEntry<float> PerHitStackScreenSpread;

	internal static ConfigEntry<float> PerHitStackWorldSpread;

	internal static ConfigEntry<int> MaxActiveNumbers;

	internal static ConfigEntry<int> MaxNumbersPerFrame;

	internal static ConfigEntry<bool> EnableCrowdingProtection;

	internal static ConfigEntry<float> CrowdingRadius;

	internal static ConfigEntry<int> MaxNearbyNumbers;

	internal static ConfigEntry<bool> DebugLog;

	internal static ConfigEntry<bool> DebugSpawnTestOnF8;

	internal static ManualLogSource Log;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_042c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0436: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable damage number display.");
		Mode = ((BaseUnityPlugin)this).Config.Bind<DisplayMode>("General", "DamageDisplayMode", DisplayMode.PerTargetTotal, "PerTargetTotal = damage of one type from the same player attack source on the same target in the same frame is merged into one number; every further number on that target in the same frame (another damage type such as the Holy damage of Scroll of Crusader, or another attack source) is shown one line higher. PerHit = each damage event the game applies is shown separately. Crowding and frame-budget settings are independent visual performance limits applied after either mode.");
		Renderer = ((BaseUnityPlugin)this).Config.Bind<RenderMode>("Visual", "Renderer", RenderMode.ScreenSpace, "ScreenSpace uses pooled TextMeshProUGUI objects. WorldTextMeshPro uses pooled 3D TextMeshPro objects. Both renders both at the same time. Vanilla reuses the game's own damage text prefab, the one the target dummies use, with its original animation, font and damage-type colors; it shows whole numbers only and falls back to ScreenSpace if the game does not provide that prefab.");
		VanillaUseDamageTypeColors = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "VanillaUseDamageTypeColors", true, "Renderer=Vanilla only. True uses the game's damage-type colors exactly like the target dummies. False keeps this mod's weakspot multiplier colors on the vanilla animation.");
		VanillaScale = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "VanillaScale", 1f, "Renderer=Vanilla only. Scale applied to the game's damage text. 1.0 is the size the target dummies use.");
		UseWeakspotColors = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "UseWeakspotColors", true, "Use body-part multiplier colors. Red >= 1.5 (eyes), orange >= 1.0 (heads), yellow >= 0.75 (thorax), white below that (body, limbs) and for damage that carries no body part, such as status effects and explosions.");
		TreatOneMultiplierAsNormal = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "TreatOneMultiplierAsNormal", false, "If true, multiplier 1.0 is shown as white instead of orange. Head hits are 1.0, so this makes them white like body hits.");
		AggregateColorMode = ((BaseUnityPlugin)this).Config.Bind<MergedColorMode>("Visual", "AggregateColorMode", MergedColorMode.HighestMultiplier, "Color rule for PerTargetTotal. HighestMultiplier = strongest hit color. DamageWeightedAverage = damage-weighted multiplier. LastHit = last hit color.");
		RequireNpcTarget = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "RequireNpcTarget", true, "Only show damage numbers on Npc targets. Keep true to avoid barrels/objects if they also use Unit damage code.");
		RequireSourceUnitPlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "RequireSourceUnitPlayer", false, "If true, sourceData.sourceUnit must exist and be the player. Default false because some projectiles/explosions keep only the game's isPlayer flag.");
		ShowPlayerOwnedSecondaryEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowPlayerOwnedSecondaryEffects", true, "Show player-owned secondary damage that has no weapon or melee source, such as projectile explosions, if the game reports it as created by the player. Status damage over time is controlled by ShowStatusEffectDamage.");
		ShowStatusEffectDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("Filter", "ShowStatusEffectDamage", true, "Show damage over time from status effects the player applied, such as burning, poison and electrocution. These are the colored numbers the target dummies show.");
		MinDamageToShow = ((BaseUnityPlugin)this).Config.Bind<float>("Filter", "MinDamageToShow", 0.01f, "Final damage below this value will not be shown.");
		ShowDecimals = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "ShowDecimals", false, "Show one decimal place for non-integer damage. Integer values such as 5.0 are still shown as 5. Renderer=Vanilla always shows whole numbers.");
		Lifetime = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "Lifetime", 0.85f, "Damage number lifetime in seconds. Renderer=Vanilla follows the game's own animation instead.");
		FloatSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "FloatSpeed", 1.25f, "WorldTextMeshPro renderer only. World-space upward speed.");
		WorldScale = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "WorldScale", 0.045f, "WorldTextMeshPro renderer only. World-space scale of each 3D damage number.");
		RandomJitter = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "RandomJitter", 0.18f, "PerHit mode only. Random horizontal world-space offset for each number, useful for shotgun pellets. Not used by the ScreenSpace renderer.");
		FontSize = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "FontSize", 8f, "WorldTextMeshPro renderer only. TextMeshPro font size of the 3D damage numbers.");
		ScreenFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Visual", "ScreenFontSize", 30, "ScreenSpace renderer only. Screen-space TextMeshPro font size.");
		ScreenFloatSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "ScreenFloatSpeed", 70f, "ScreenSpace renderer only. Extra upward speed in pixels per second. Numbers also drift up slowly with the point above the target, so 0 does not hold them completely still.");
		ScreenJitter = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "ScreenJitter", 26f, "PerHit mode and ScreenSpace renderer only. Random screen-space offset in pixels for each number, useful for shotgun pellets.");
		PerHitStackScreenSpread = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "PerHitStackScreenSpread", 12.8f, "ScreenSpace renderer only. Extra deterministic screen-space spread in pixels for multiple PerHit numbers created on the same target in the same frame. The offset is capped at 42 pixels.");
		PerHitStackWorldSpread = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "PerHitStackWorldSpread", 0.06f, "Extra deterministic world-space spread for multiple PerHit numbers created on the same target in the same frame. Not used by the ScreenSpace renderer. The offset is capped at 0.36.");
		MaxActiveNumbers = ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "MaxActiveNumbers", 160, "Maximum active damage numbers in each enabled renderer. Values are hard-clamped to 1-256, or 1-64 for Renderer=Vanilla, and the oldest active number is reused when full.");
		MaxNumbersPerFrame = ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "MaxNumbersPerFrame", 32, "Maximum new logical damage numbers spawned in one frame. Values are hard-clamped to 1-256. Extra events are dropped to protect Unity's GC during extreme hit spam.");
		EnableCrowdingProtection = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "EnableCrowdingProtection", true, "Skip a new number when several active numbers already occupy the same small screen area. This is a visual limiter applied after the selected display mode; it does not merge PerHit damage or change PerTargetTotal aggregation.");
		CrowdingRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "CrowdingRadius", 36f, "Screen-space radius in pixels used by crowding protection. Values are hard-clamped to 8-120.");
		MaxNearbyNumbers = ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "MaxNearbyNumbers", 5, "Maximum existing numbers allowed inside the crowding radius before a new visually covered number is skipped. Values are hard-clamped to 1-32.");
		DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Print accepted/rejected damage events and rate-limit warnings to LogOutput.log.");
		DebugSpawnTestOnF8 = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugSpawnTestOnF8", false, "Press F8 in-game to spawn a test number at the screen center. The test number always uses the ScreenSpace style.");
		DamageNumberManager.EnsureExists();
		_harmony = new Harmony("ryuka.sulfur.damagenumbers");
		DamageLogPatch.Apply(_harmony);
		((BaseUnityPlugin)this).Logger.LogInfo((object)("SULFUR Damage Numbers 1.2.0 loaded. Renderer=" + Renderer.Value.ToString() + ", Mode=" + Mode.Value.ToString() + "."));
	}

	private void OnDestroy()
	{
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
			_harmony = null;
		}
		DamageLogPatch.Reset();
		DamageNumberManager.Shutdown();
		try
		{
			VanillaDamageText.Shutdown();
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Releasing the game's damage text assets failed: " + ex.Message));
		}
	}
}
public enum DisplayMode
{
	PerTargetTotal = 1,
	PerHit
}
public enum RenderMode
{
	ScreenSpace = 1,
	WorldTextMeshPro,
	Both,
	Vanilla
}
public enum MergedColorMode
{
	HighestMultiplier = 1,
	DamageWeightedAverage,
	LastHit
}
internal sealed class VanillaDamageNumberView : IDamageNumberView
{
	private const float FallbackClipSeconds = 2.5f;

	private const float ClipEndMarginSeconds = 0.1f;

	private readonly GameObject _root;

	private readonly Transform _rootTransform;

	private readonly DamageTextInstance _instance;

	private readonly Transform _textTransform;

	private readonly float _activeSeconds;

	public bool IsActive { get; private set; }

	public float StartTime { get; private set; }

	public Vector3 CurrentWorldPosition
	{
		get
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_textTransform != (Object)null))
			{
				return _rootTransform.position;
			}
			return _textTransform.position;
		}
	}

	public float LineHeightAtUnitScale { get; private set; }

	private VanillaDamageNumberView(GameObject root, DamageTextInstance instance, Transform textTransform, float lineHeightAtUnitScale, float activeSeconds)
	{
		_root = root;
		_rootTransform = root.transform;
		_instance = instance;
		_textTransform = textTransform;
		LineHeightAtUnitScale = lineHeightAtUnitScale;
		_activeSeconds = activeSeconds;
	}

	public static VanillaDamageNumberView TryCreate(Transform parent, GameObject textPrefab)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected O, but got Unknown
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)parent == (Object)null || (Object)(object)textPrefab == (Object)null)
		{
			return null;
		}
		GameObject val = new GameObject("SULFUR Vanilla Damage Number");
		val.transform.SetParent(parent, false);
		GameObject val2 = Object.Instantiate<GameObject>(textPrefab, val.transform);
		DamageTextInstance component = val2.GetComponent<DamageTextInstance>()

DamageSparks.SulfurTogether.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using Microsoft.CodeAnalysis;
using SULFURTogether.Api;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("DamageSparks.SulfurTogether")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a469c46ed4a85f643dc8ce04facb42c49b968abe")]
[assembly: AssemblyProduct("DamageSparks.SulfurTogether")]
[assembly: AssemblyTitle("DamageSparks.SulfurTogether")]
[assembly: AssemblyVersion("1.0.0.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 SulfurDamageNumbersMod.SulfurTogether
{
	internal sealed class NetCombatBridge : IDisposable
	{
		private sealed class HitGroup
		{
			public readonly int Frame;

			public readonly List<ResolvedHit> Hits = new List<ResolvedHit>();

			public int Unresolved;

			public HitGroup(int frame)
			{
				Frame = frame;
			}
		}

		private const int SupportedApiVersion = 1;

		private const int TeamMarksApiVersion = 2;

		public readonly int ApiVersion;

		private TeamBonusBridge _teamBonus;

		private readonly Dictionary<int, HitGroup> _groupBySequence = new Dictionary<int, HitGroup>();

		private readonly Dictionary<int, HitGroup> _groupByFrame = new Dictionary<int, HitGroup>();

		private NetCombatBridge(int apiVersion)
		{
			ApiVersion = apiVersion;
		}

		public static NetCombatBridge TryCreate()
		{
			int apiVersion = NetCombat.ApiVersion;
			if (apiVersion < 1)
			{
				return null;
			}
			NetCombatBridge netCombatBridge = new NetCombatBridge(apiVersion);
			NetCombat.LocalHitForwarded += netCombatBridge.OnLocalHitForwarded;
			NetCombat.LocalHitResolved += netCombatBridge.OnLocalHitResolved;
			DamageNumbersIntegration.SetRemoteHitScope((Func<bool>)netCombatBridge.IsResolvingRemoteHit);
			if (apiVersion >= 2)
			{
				try
				{
					netCombatBridge._teamBonus = TeamBonusBridge.Attach();
				}
				catch
				{
					netCombatBridge.Dispose();
					throw;
				}
			}
			return netCombatBridge;
		}

		public void Dispose()
		{
			if (_teamBonus != null)
			{
				_teamBonus.Dispose();
				_teamBonus = null;
			}
			NetCombat.LocalHitForwarded -= OnLocalHitForwarded;
			NetCombat.LocalHitResolved -= OnLocalHitResolved;
			DamageNumbersIntegration.SetRemoteHitScope((Func<bool>)null);
			_groupBySequence.Clear();
			_groupByFrame.Clear();
		}

		private bool IsResolvingRemoteHit()
		{
			return NetCombat.IsResolvingRemoteHit;
		}

		private void OnLocalHitForwarded(LocalHitForwardedArgs hit)
		{
			int frameCount = Time.frameCount;
			if (!_groupByFrame.TryGetValue(frameCount, out var value))
			{
				value = new HitGroup(frameCount);
				_groupByFrame[frameCount] = value;
			}
			value.Unresolved++;
			_groupBySequence[hit.RequestSeq] = value;
		}

		private void OnLocalHitResolved(LocalHitResolvedArgs result)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Invalid comparison between Unknown and I4
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if (_groupBySequence.TryGetValue(result.RequestSeq, out var value))
			{
				_groupBySequence.Remove(result.RequestSeq);
			}
			else
			{
				value = null;
			}
			if ((int)result.Outcome == 1)
			{
				Collect(result, value);
			}
			if (value != null && --value.Unresolved <= 0)
			{
				_groupByFrame.Remove(value.Frame);
				for (int i = 0; i < value.Hits.Count; i++)
				{
					DamageNumbersIntegration.ShowResolvedHit(value.Hits[i]);
				}
			}
		}

		private void Collect(LocalHitResolvedArgs result, HitGroup group)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: 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)
			float bonusMultiplier = ((_teamBonus != null) ? _teamBonus.MultiplierOf(result) : 1f);
			Report(group, new ResolvedHit
			{
				Target = result.Target,
				Position = result.HitPosition,
				Weapon = result.Weapon,
				Melee = result.Melee,
				Damage = result.Damage,
				DamageType = result.DamageType,
				HitMultiplier = result.HitMultiplier,
				IsCritical = result.IsCritical,
				BonusMultiplier = bonusMultiplier,
				DealtFrame = (group?.Frame ?? 0)
			});
			IReadOnlyList<SecondaryHit> secondary = result.Secondary;
			for (int i = 0; i < secondary.Count; i++)
			{
				SecondaryHit val = secondary[i];
				Report(group, new ResolvedHit
				{
					Target = result.Target,
					Position = result.HitPosition,
					Weapon = result.Weapon,
					Melee = result.Melee,
					Damage = ((SecondaryHit)(ref val)).Damage,
					DamageType = ((SecondaryHit)(ref val)).DamageType,
					HitMultiplier = 0f,
					IsCritical = ((SecondaryHit)(ref val)).IsCritical,
					DealtFrame = (group?.Frame ?? 0)
				});
			}
		}

		private static void Report(HitGroup group, ResolvedHit hit)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (group != null)
			{
				group.Hits.Add(hit);
			}
			else
			{
				DamageNumbersIntegration.ShowResolvedHit(hit);
			}
		}
	}
	[BepInPlugin("ryuka.sulfur.damagenumbers_sulfur_together", "Damage Sparks - SULFUR Together Adapter", "1.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class SulfurTogetherAdapterPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ryuka.sulfur.damagenumbers_sulfur_together";

		public const string PluginName = "Damage Sparks - SULFUR Together Adapter";

		private const string SulfurTogetherGuid = "com.ryuka.sulfur.together";

		private IDisposable _bridge;

		private void Awake()
		{
			try
			{
				_bridge = Attach(out var apiVersion);
				((BaseUnityPlugin)this).Logger.LogInfo((object)((_bridge != null) ? ("Connected to SULFUR Together's combat events (API " + apiVersion + ").") : "The installed SULFUR Together has an unknown combat API; co-op client damage numbers are unavailable."));
			}
			catch (Exception ex) when (ex is TypeLoadException || ex is MissingMethodException || ex is MissingFieldException || ex is TypeInitializationException || ex is BadImageFormatException)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("The installed SULFUR Together has no combat API (it needs 1.7.0 or newer); co-op client damage numbers are unavailable. (" + ex.GetType().Name + ": " + ex.Message + ")"));
			}
		}

		private void OnDestroy()
		{
			if (_bridge != null)
			{
				_bridge.Dispose();
				_bridge = null;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static IDisposable Attach(out int apiVersion)
		{
			NetCombatBridge netCombatBridge = NetCombatBridge.TryCreate();
			apiVersion = netCombatBridge?.ApiVersion ?? 0;
			return netCombatBridge;
		}
	}
	internal sealed class TeamBonusBridge : IDisposable
	{
		private IDisposable _hitPresenter;

		public static TeamBonusBridge Attach()
		{
			TeamBonusBridge teamBonusBridge = new TeamBonusBridge();
			DamageNumbersIntegration.SetHitBonusScope((Func<float>)teamBonusBridge.LocalHostHitMultiplier);
			DamageNumbersIntegration.ShowsHitBonusChanged += teamBonusBridge.FollowHitBonusDisplay;
			teamBonusBridge.FollowHitBonusDisplay();
			return teamBonusBridge;
		}

		public void Dispose()
		{
			DamageNumbersIntegration.ShowsHitBonusChanged -= FollowHitBonusDisplay;
			DamageNumbersIntegration.SetHitBonusScope((Func<float>)null);
			ReleaseHitPresenter();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public float MultiplierOf(LocalHitResolvedArgs result)
		{
			return result.TeamMultiplier;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private float LocalHostHitMultiplier()
		{
			return NetCombat.LocalHostHitMultiplier;
		}

		private void FollowHitBonusDisplay()
		{
			bool showsHitBonus = DamageNumbersIntegration.ShowsHitBonus;
			if (showsHitBonus != (_hitPresenter != null))
			{
				if (showsHitBonus)
				{
					_hitPresenter = RegisterHitPresenter();
				}
				else
				{
					ReleaseHitPresenter();
				}
			}
		}

		private void ReleaseHitPresenter()
		{
			if (_hitPresenter != null)
			{
				_hitPresenter.Dispose();
				_hitPresenter = null;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static IDisposable RegisterHitPresenter()
		{
			return NetTeamMarks.RegisterHitPresenter();
		}
	}
}