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<AggregateKey, AggregateEntry> _aggregates = new Dictionary<AggregateKey, AggregateEntry>();
private readonly List<AggregateKey> _flushBuffer = new List<AggregateKey>();
private readonly Dictionary<PerHitSpreadKey, int> _perHitSpreadCounters = new Dictionary<PerHitSpreadKey, int>();
private readonly List<ScreenDamageNumberView> _screenPool = new List<ScreenDamageNumberView>();
private readonly List<DamageNumberView> _worldPool = new List<DamageNumberView>();
private readonly List<Vector2> _crowdingPositions = new List<Vector2>(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<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)
{
//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<AggregateKey, AggregateEntry> 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<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>();
}
}
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<T>(List<T> 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>();
_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<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_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<PerHitSpreadKey>
{
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<AggregateKey>
{
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<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_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<bool> Enabled;
internal static ConfigEntry<DisplayMode> Mode;
internal static ConfigEntry<RenderMode> Renderer;
internal static ConfigEntry<MergedColorMode> AggregateColorMode;
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<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_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<bool>("General", "Enabled", true, "Enable damage number display.");
Mode = ((BaseUnityPlugin)this).Config.Bind<DisplayMode>("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<RenderMode>("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<bool>("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<bool>("Visual", "TreatOneMultiplierAsNormal", false, "If true, multiplier 1.0 is shown as white instead of orange.");
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 such as projectile explosions if the game reports them as created by the player.");
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.");
Lifetime = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "Lifetime", 0.85f, "Damage number lifetime in seconds.");
FloatSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "FloatSpeed", 1.25f, "World-space upward speed for WorldTextMeshPro renderer.");
WorldScale = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "WorldScale", 0.045f, "World-space scale of each TextMeshPro damage number.");
RandomJitter = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "RandomJitter", 0.18f, "Random horizontal world-space offset. Useful for PerHit shotgun pellets.");
FontSize = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "FontSize", 8f, "TextMeshPro font size for WorldTextMeshPro renderer.");
ScreenFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Visual", "ScreenFontSize", 30, "Screen-space TextMeshPro font size.");
ScreenFloatSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "ScreenFloatSpeed", 70f, "Screen-space upward speed in pixels per second.");
ScreenJitter = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "ScreenJitter", 26f, "Random screen-space offset in pixels. Useful for PerHit shotgun pellets.");
PerHitStackScreenSpread = ((BaseUnityPlugin)this).Config.Bind<float>("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<float>("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<int>("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<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. 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
}