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 BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using PerfectRandom.Sulfur.Core; using PerfectRandom.Sulfur.Core.Stats; using PerfectRandom.Sulfur.Core.Units; using TMPro; using UnityEngine; using UnityEngine.UI; [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.0.12.0")] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("1.0.12.0")] namespace SulfurDamageNumbersMod; internal static class DamageLogPatch { private static bool _applied; [ThreadStatic] private static bool _handlingPostfix; public static void Apply(Harmony harmony) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown if (!_applied) { MethodBase methodBase = TargetMethod(); if (methodBase == null) { Plugin.Log.LogError((object)"SULFUR Damage Numbers failed: Unit.CreateDamageLog(float, float, DamageTypes, Hitmesh.Data, DamageSourceData) was not found."); return; } MethodBase methodBase2 = methodBase; HarmonyMethod val = new HarmonyMethod(typeof(DamageLogPatch), "Postfix", (Type[])null); harmony.Patch(methodBase2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _applied = true; Plugin.Log.LogInfo((object)("SULFUR Damage Numbers patched " + methodBase.DeclaringType.FullName + "." + methodBase.Name + ".")); } } public static void Reset() { _applied = false; _handlingPostfix = false; } private static MethodBase TargetMethod() { 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 void Postfix(Unit __instance, float __0, float __1, DamageTypes __2, Data __3, DamageSourceData __4) { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || !Plugin.Enabled.Value || _handlingPostfix) { return; } _handlingPostfix = true; try { if (__0 < Plugin.MinDamageToShow.Value) { if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)("[DamageNumbers] reject low damage amount=" + __0 + " target=" + SafeName((Object)(object)__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 num = Mathf.Max(0f, ((Data)(ref __3)).GetShapeMultiplier()); if (Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)("[DamageNumbers] accept amount=" + __0 + " multiplier=" + num + " target=" + SafeName((Object)(object)__instance) + " source=" + DescribeSource(__4))); } DamageNumberManager.RegisterDamage(__instance, __4, __0, num); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[DamageNumbers] CreateDamageLog postfix failed: " + ex)); } finally { _handlingPostfix = false; } } 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; } reason = null; return true; } private static bool IsPlayerExecutedAttack(DamageSourceData sourceData, out string reason) { if (!sourceData.isPlayer) { reason = "sourceData.isPlayer is false"; return false; } if (Plugin.RequireSourceUnitPlayer.Value) { if ((Object)(object)sourceData.sourceUnit == (Object)null) { reason = "sourceUnit is null"; return false; } if (!sourceData.sourceUnit.isPlayer) { reason = "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 (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) { string text = ((!((Object)(object)sourceData.sourceUnit == (Object)null)) ? SafeName((Object)(object)sourceData.sourceUnit) : "null"); string text2 = ((!((Object)(object)sourceData.sourceWeapon == (Object)null)) ? ((Object)sourceData.sourceWeapon).name : "null"); return "isPlayer=" + sourceData.isPlayer + ", melee=" + sourceData.melee + ", weapon=" + text2 + ", unit=" + text + ", instanceId=" + sourceData.instanceId + ", name=" + sourceData.name; } private static string SafeName(Object obj) { return (!(obj == (Object)null)) ? obj.name : "null"; } } internal sealed class DamageNumberManager : MonoBehaviour { private struct AggregateEntry { public float Amount; public Vector3 Position; public int Frame; public float HighestMultiplier; public float LastMultiplier; public float WeightedMultiplierSum; } 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 static DamageNumberManager _instance; private readonly Dictionary _aggregates = new Dictionary(); private readonly List _flushBuffer = new List(); private readonly Dictionary _perHitSpreadCounters = new Dictionary(); private readonly List _screenPool = new List(); private readonly List _worldPool = new List(); private readonly List _crowdingPositions = new List(256); private int _perHitSpreadFrame = -1; 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; public static void EnsureExists() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("SULFUR Damage Number Manager"); Object.DontDestroyOnLoad((Object)(object)val); _instance = val.AddComponent(); } } 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) { //IL_0028: 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); } } } public static void SpawnScreenTestNumber() { //IL_0037: 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) 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) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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) //IL_006c: 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) Vector3 damagePosition = GetDamagePosition(target); if (Plugin.Mode.Value == DisplayMode.PerHit) { PerHitSpreadOffset nextPerHitSpread = GetNextPerHitSpread(target, sourceData); SpawnNumber(damagePosition, amount, hitMultiplier, useJitter: true, nextPerHitSpread.WorldOffset, nextPerHitSpread.ScreenOffset, applyCrowding: true); return; } AggregateKey key = AggregateKey.Create(target, sourceData); if (!_aggregates.TryGetValue(key, out var value)) { value = new AggregateEntry { Amount = 0f, Position = damagePosition, Frame = Time.frameCount, HighestMultiplier = 0f, LastMultiplier = 0f, WeightedMultiplierSum = 0f }; } value.Amount += amount; value.Position = damagePosition; value.Frame = Time.frameCount; value.LastMultiplier = hitMultiplier; value.WeightedMultiplierSum += Mathf.Max(0f, hitMultiplier) * Mathf.Max(0f, amount); if (hitMultiplier > value.HighestMultiplier) { value.HighestMultiplier = hitMultiplier; } _aggregates[key] = value; } private void LateUpdate() { FlushAggregates(); ApplyRendererSelectionIfChanged(); int maxActiveNumbers = GetMaxActiveNumbers(); EnforcePoolLimit(_screenPool, maxActiveNumbers); EnforcePoolLimit(_worldPool, maxActiveNumbers); 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); } } MaybeLogDroppedBudget(); } private void FlushAggregates() { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (_aggregates.Count == 0) { return; } _flushBuffer.Clear(); foreach (KeyValuePair aggregate in _aggregates) { if (aggregate.Value.Frame <= Time.frameCount) { _flushBuffer.Add(aggregate.Key); } } for (int i = 0; i < _flushBuffer.Count; i++) { AggregateKey key = _flushBuffer[i]; if (_aggregates.TryGetValue(key, out var value)) { SpawnNumber(value.Position, value.Amount, ResolveAggregateMultiplier(value), useJitter: false, Vector3.zero, Vector2.zero, applyCrowding: true); _aggregates.Remove(key); } } } private void SpawnNumber(Vector3 position, float amount, float hitMultiplier, bool useJitter, Vector3 extraWorldJitter, Vector2 extraScreenJitter, bool applyCrowding) { //IL_0006: 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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: 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) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) EnsureFrameCounters(); 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; } } bool flag = false; Vector2 screenPosition; if (applyCrowding && Plugin.EnableCrowdingProtection.Value) { flag = TryGetCrowdingScreenPosition(position, val2, out screenPosition); if (flag && IsAreaCrowded(screenPosition)) { _crowdingSkippedThisFrame++; return; } } else { screenPosition = Vector2.zero; } if (TryConsumeSpawnBudget()) { string text = FormatDamage(amount); Color color = DamageNumberColor.ForMultiplier(hitMultiplier); RenderMode value = Plugin.Renderer.Value; if (value == RenderMode.ScreenSpace || value == RenderMode.Both) { SpawnScreenWorld(text, position, val2, color); } if (value == RenderMode.WorldTextMeshPro || value == RenderMode.Both) { SpawnWorld(text, position, val, color); } if (flag) { _crowdingPositions.Add(screenPosition); } } } private void SpawnScreenWorld(string text, Vector3 worldPosition, Vector2 jitter, Color color) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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) AcquireScreenView()?.PlayWorld(text, worldPosition, jitter, Mathf.Max(0.05f, Plugin.Lifetime.Value), Mathf.Max(8, Plugin.ScreenFontSize.Value), color); } private void SpawnScreenFixed(string text, Vector2 screenPosition, Color color) { //IL_0010: 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) AcquireScreenView()?.PlayFixed(text, screenPosition, Mathf.Max(0.05f, Plugin.Lifetime.Value), Mathf.Max(8, Plugin.ScreenFontSize.Value), color); } private void SpawnWorld(string text, Vector3 worldPosition, Vector3 jitter, Color color) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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) AcquireWorldView()?.Play(text, worldPosition, jitter, Mathf.Max(0.05f, Plugin.Lifetime.Value), Plugin.FloatSpeed.Value, Plugin.WorldScale.Value, Plugin.FontSize.Value, color); } 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; } ScreenDamageNumberView screenDamageNumberView4 = new ScreenDamageNumberView(_screenRoot, _screenSharedMaterial); if ((Object)(object)_screenSharedMaterial == (Object)null) { _screenSharedMaterial = screenDamageNumberView4.FontSharedMaterial; screenDamageNumberView4.SetSharedMaterial(_screenSharedMaterial); } _screenPool.Add(screenDamageNumberView4); return screenDamageNumberView4; } if (screenDamageNumberView2 != null) { screenDamageNumberView2.Stop(); return screenDamageNumberView2; } return screenDamageNumberView; } 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; } DamageNumberView damageNumberView4 = new DamageNumberView(_worldRoot, _worldSharedMaterial); if ((Object)(object)_worldSharedMaterial == (Object)null) { _worldSharedMaterial = damageNumberView4.FontSharedMaterial; damageNumberView4.SetSharedMaterial(_worldSharedMaterial); } _worldPool.Add(damageNumberView4); return damageNumberView4; } 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_0028: 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_002e: 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) //IL_0020: 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_0061: 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_0041: 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) 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_00d2: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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 value = Plugin.Renderer.Value; if (value == RenderMode.ScreenSpace || value == RenderMode.Both) { for (int i = 0; i < _screenPool.Count; i++) { ScreenDamageNumberView screenDamageNumberView = _screenPool[i]; if (screenDamageNumberView.IsActive && screenDamageNumberView.IsVisible) { _crowdingPositions.Add(screenDamageNumberView.CurrentScreenPosition); } } return; } for (int j = 0; j < _worldPool.Count; j++) { DamageNumberView damageNumberView = _worldPool[j]; if (damageNumberView.IsActive) { Vector3 val = camera.WorldToScreenPoint(damageNumberView.CurrentWorldPosition); if (val.z > 0f) { _crowdingPositions.Add(new Vector2(val.x, val.y)); } } } } private bool IsAreaCrowded(Vector2 screenPosition) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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(); component.renderMode = (RenderMode)0; component.overrideSorting = true; component.sortingOrder = 32760; CanvasScaler component2 = val.GetComponent(); component2.uiScaleMode = (ScaleMode)0; component2.scaleFactor = 1f; _screenRoot = val.GetComponent(); } } private void EnsureWorldRoot() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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 ApplyRendererSelectionIfChanged() { RenderMode value = Plugin.Renderer.Value; if (_hasAppliedRenderMode && value == _lastAppliedRenderMode) { return; } _lastAppliedRenderMode = value; _hasAppliedRenderMode = true; bool flag = value == RenderMode.ScreenSpace || value == RenderMode.Both; bool flag2 = value == RenderMode.WorldTextMeshPro || value == RenderMode.Both; if (!flag) { for (int i = 0; i < _screenPool.Count; i++) { _screenPool[i].Stop(); } } if (!flag2) { for (int j = 0; j < _worldPool.Count; j++) { _worldPool[j].Stop(); } } } private static void EnforcePoolLimit(List pool, int maxActive) where T : class, IDamageNumberView { int num = 0; for (int i = 0; i < pool.Count; i++) { T val = pool[i]; if (val.IsActive) { num++; } } while (num > maxActive) { T val2 = (T)null; for (int j = 0; j < pool.Count; j++) { T val3 = pool[j]; if (val3.IsActive && (val2 == null || val3.StartTime < val2.StartTime)) { val2 = val3; } } if (val2 == null) { break; } val2.Stop(); num--; } } 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 Vector3 GetDamagePosition(Unit target) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_004a: 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) 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(Unit target, DamageSourceData sourceData) { //IL_0027: 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) //IL_00d5: 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_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) if (_perHitSpreadFrame != Time.frameCount) { _perHitSpreadFrame = Time.frameCount; _perHitSpreadCounters.Clear(); } PerHitSpreadKey key = PerHitSpreadKey.Create(target, sourceData); 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(); _flushBuffer.Clear(); _perHitSpreadCounters.Clear(); _crowdingPositions.Clear(); _screenPool.Clear(); _worldPool.Clear(); _screenRoot = null; _worldRoot = 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 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) _gameObject = new GameObject("SULFUR Damage Number Screen", new Type[1] { typeof(RectTransform) }); _gameObject.transform.SetParent((Transform)(object)parent, false); _rectTransform = _gameObject.GetComponent(); _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); } Stop(); } private TextMeshProUGUI CreateTextLayer(string name, Vector2 offset) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_004e: 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_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_0084: 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) 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(); 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(); ((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_0083: 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 val = _outlineTexts[i]; ((TMP_Text)val).text = value; ((TMP_Text)val).fontSize = fontSize; ((Graphic)val).color = Color.black; val.canvasRenderer.SetAlpha(1f); ((Behaviour)val).enabled = true; } ((Behaviour)_text).enabled = true; _gameObject.SetActive(true); IsVisible = true; IsActive = true; } public void Tick(Camera camera, float now, float floatSpeed) { //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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: 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 PerHitSpreadKey : IEquatable { private int _targetId; private int _frame; private int _sourceId; public static PerHitSpreadKey Create(Unit target, DamageSourceData sourceData) { int sourceId = 0; if ((Object)(object)sourceData.sourceWeapon != (Object)null) { sourceId = ((Object)sourceData.sourceWeapon).GetInstanceID(); } else if (sourceData.instanceId != 0) { sourceId = sourceData.instanceId; } else if ((Object)(object)sourceData.sourceUnit != (Object)null) { sourceId = ((Object)sourceData.sourceUnit).GetInstanceID(); } return new PerHitSpreadKey { _targetId = ((Object)target).GetInstanceID(), _frame = Time.frameCount, _sourceId = sourceId }; } public bool Equals(PerHitSpreadKey other) { return _targetId == other._targetId && _frame == other._frame && _sourceId == other._sourceId; } public override bool Equals(object obj) { return obj is PerHitSpreadKey && Equals((PerHitSpreadKey)obj); } public override int GetHashCode() { int targetId = _targetId; targetId = (targetId * 397) ^ _frame; return (targetId * 397) ^ _sourceId; } } internal struct AggregateKey : IEquatable { private int _targetId; private int _frame; private int _sourceId; private int _damageType; private bool _melee; public static AggregateKey Create(Unit target, DamageSourceData sourceData) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) int sourceId = 0; if ((Object)(object)sourceData.sourceWeapon != (Object)null) { sourceId = ((Object)sourceData.sourceWeapon).GetInstanceID(); } else if (sourceData.instanceId != 0) { sourceId = sourceData.instanceId; } else if ((Object)(object)sourceData.sourceUnit != (Object)null) { sourceId = ((Object)sourceData.sourceUnit).GetInstanceID(); } return new AggregateKey { _targetId = ((Object)target).GetInstanceID(), _frame = Time.frameCount, _sourceId = sourceId, _damageType = Convert.ToInt32(sourceData.damageType), _melee = sourceData.melee }; } public bool Equals(AggregateKey other) { return _targetId == other._targetId && _frame == other._frame && _sourceId == other._sourceId && _damageType == other._damageType && _melee == other._melee; } public override bool Equals(object obj) { return obj is AggregateKey && Equals((AggregateKey)obj); } public override int GetHashCode() { int targetId = _targetId; targetId = (targetId * 397) ^ _frame; targetId = (targetId * 397) ^ _sourceId; targetId = (targetId * 397) ^ _damageType; return (targetId * 397) ^ (_melee ? 1 : 0); } } 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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_004b: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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; } } 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 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); } Stop(); } private TextMeshPro CreateTextLayer(string name, int sortingOrder) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[1] { typeof(TextMeshPro) }); val.transform.SetParent(_transform, false); TextMeshPro component = val.GetComponent(); ((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_00cc: 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) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0109: 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 val = _outlineTexts[i]; Vector2 val2 = OutlineDirections[i]; val.transform.localPosition = new Vector3(val2.x * num, val2.y * num, 0f); ((TMP_Text)val).text = value; ((TMP_Text)val).fontSize = fontSize; ((Graphic)val).color = Color.black; ((Behaviour)val).enabled = true; } ((Behaviour)_text).enabled = true; _gameObject.SetActive(true); IsActive = true; } public void Tick(Camera camera, float now) { //IL_002f: 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_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) //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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: 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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0135: 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); } } float num2 = Mathf.Clamp01(1f - num / _lifetime); int num3 = Mathf.Clamp(Mathf.CeilToInt(num2 * 10f), 0, 10); if (num3 != _lastFadeStep) { _lastFadeStep = num3; float a = (float)num3 / 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 DamageReceivePatch { public static void Apply(Harmony harmony) { DamageLogPatch.Apply(harmony); } } [BepInPlugin("ryuka.sulfur.damagenumbers", "SULFUR Damage Numbers", "1.0.12")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "ryuka.sulfur.damagenumbers"; public const string PluginName = "SULFUR Damage Numbers"; public const string PluginVersion = "1.0.12"; internal static ConfigEntry Enabled; internal static ConfigEntry Mode; internal static ConfigEntry Renderer; internal static ConfigEntry AggregateColorMode; internal static ConfigEntry UseWeakspotColors; internal static ConfigEntry TreatOneMultiplierAsNormal; internal static ConfigEntry RequireNpcTarget; internal static ConfigEntry RequireSourceUnitPlayer; internal static ConfigEntry ShowPlayerOwnedSecondaryEffects; internal static ConfigEntry MinDamageToShow; internal static ConfigEntry ShowDecimals; internal static ConfigEntry Lifetime; internal static ConfigEntry FloatSpeed; internal static ConfigEntry WorldScale; internal static ConfigEntry RandomJitter; internal static ConfigEntry FontSize; internal static ConfigEntry ScreenFontSize; internal static ConfigEntry ScreenFloatSpeed; internal static ConfigEntry ScreenJitter; internal static ConfigEntry PerHitStackScreenSpread; internal static ConfigEntry PerHitStackWorldSpread; internal static ConfigEntry MaxActiveNumbers; internal static ConfigEntry MaxNumbersPerFrame; internal static ConfigEntry EnableCrowdingProtection; internal static ConfigEntry CrowdingRadius; internal static ConfigEntry MaxNearbyNumbers; internal static ConfigEntry DebugLog; internal static ConfigEntry DebugSpawnTestOnF8; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enable damage number display."); Mode = ((BaseUnityPlugin)this).Config.Bind("General", "DamageDisplayMode", DisplayMode.PerTargetTotal, "PerTargetTotal = same frame, same player attack source, same target damage is merged. PerHit = each real damage log is shown separately. Crowding and frame-budget settings are independent visual performance limits applied after either mode."); Renderer = ((BaseUnityPlugin)this).Config.Bind("Visual", "Renderer", RenderMode.ScreenSpace, "ScreenSpace uses pooled TextMeshProUGUI objects. WorldTextMeshPro uses pooled 3D TextMeshPro objects. Both renders both at the same time."); UseWeakspotColors = ((BaseUnityPlugin)this).Config.Bind("Visual", "UseWeakspotColors", true, "Use weakspot/body-part multiplier colors. Red >= 1.5, orange >= 1.0, yellow >= 0.75, white when no multiplier is available."); TreatOneMultiplierAsNormal = ((BaseUnityPlugin)this).Config.Bind("Visual", "TreatOneMultiplierAsNormal", false, "If true, multiplier 1.0 is shown as white instead of orange."); AggregateColorMode = ((BaseUnityPlugin)this).Config.Bind("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("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("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("Filter", "ShowPlayerOwnedSecondaryEffects", true, "Show player-owned secondary damage such as projectile explosions if the game reports them as created by the player."); MinDamageToShow = ((BaseUnityPlugin)this).Config.Bind("Filter", "MinDamageToShow", 0.01f, "Final damage below this value will not be shown."); ShowDecimals = ((BaseUnityPlugin)this).Config.Bind("Visual", "ShowDecimals", false, "Show one decimal place for non-integer damage. Integer values such as 5.0 are still shown as 5."); Lifetime = ((BaseUnityPlugin)this).Config.Bind("Visual", "Lifetime", 0.85f, "Damage number lifetime in seconds."); FloatSpeed = ((BaseUnityPlugin)this).Config.Bind("Visual", "FloatSpeed", 1.25f, "World-space upward speed for WorldTextMeshPro renderer."); WorldScale = ((BaseUnityPlugin)this).Config.Bind("Visual", "WorldScale", 0.045f, "World-space scale of each TextMeshPro damage number."); RandomJitter = ((BaseUnityPlugin)this).Config.Bind("Visual", "RandomJitter", 0.18f, "Random horizontal world-space offset. Useful for PerHit shotgun pellets."); FontSize = ((BaseUnityPlugin)this).Config.Bind("Visual", "FontSize", 8f, "TextMeshPro font size for WorldTextMeshPro renderer."); ScreenFontSize = ((BaseUnityPlugin)this).Config.Bind("Visual", "ScreenFontSize", 30, "Screen-space TextMeshPro font size."); ScreenFloatSpeed = ((BaseUnityPlugin)this).Config.Bind("Visual", "ScreenFloatSpeed", 70f, "Screen-space upward speed in pixels per second."); ScreenJitter = ((BaseUnityPlugin)this).Config.Bind("Visual", "ScreenJitter", 26f, "Random screen-space offset in pixels. Useful for PerHit shotgun pellets."); PerHitStackScreenSpread = ((BaseUnityPlugin)this).Config.Bind("Visual", "PerHitStackScreenSpread", 12.8f, "Extra deterministic screen-space spread in pixels for multiple PerHit numbers created on the same target in the same frame."); PerHitStackWorldSpread = ((BaseUnityPlugin)this).Config.Bind("Visual", "PerHitStackWorldSpread", 0.06f, "Extra deterministic world-space spread for multiple PerHit numbers created on the same target in the same frame."); MaxActiveNumbers = ((BaseUnityPlugin)this).Config.Bind("Performance", "MaxActiveNumbers", 160, "Maximum active damage numbers in each enabled renderer. Values are hard-clamped to 1-256 and the oldest active number is reused when full."); MaxNumbersPerFrame = ((BaseUnityPlugin)this).Config.Bind("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("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("Performance", "CrowdingRadius", 36f, "Screen-space radius in pixels used by crowding protection. Values are hard-clamped to 8-120."); MaxNearbyNumbers = ((BaseUnityPlugin)this).Config.Bind("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("Debug", "DebugLog", false, "Print accepted/rejected damage events and rate-limit warnings to LogOutput.log."); DebugSpawnTestOnF8 = ((BaseUnityPlugin)this).Config.Bind("Debug", "DebugSpawnTestOnF8", false, "Press F8 in-game to spawn a test number. Disabled by default in release builds."); DamageNumberManager.EnsureExists(); _harmony = new Harmony("ryuka.sulfur.damagenumbers"); DamageLogPatch.Apply(_harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Concat("SULFUR Damage Numbers 1.0.12 loaded. Renderer=", Renderer.Value, ", Mode=", Mode.Value, ".")); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); _harmony = null; } DamageLogPatch.Reset(); DamageNumberManager.Shutdown(); } } public enum DisplayMode { PerTargetTotal = 1, PerHit } public enum RenderMode { ScreenSpace = 1, WorldTextMeshPro, Both } public enum MergedColorMode { HighestMultiplier = 1, DamageWeightedAverage, LastHit }