Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Carturs Combat Text v1.0.0
plugins/CarturCombatText/CarturCombatText.dll
Decompiled 2 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("CarturCombatText")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+625cefd5c240f899ff7c9c622c1d0dafcb666d59")] [assembly: AssemblyProduct("CarturCombatText")] [assembly: AssemblyTitle("CarturCombatText")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CarturCombatText { [BepInPlugin("com.jekkle.valheim.carturcombattext", "Cartur's Combat Text", "1.0.0")] public class Plugin : BaseUnityPlugin { private sealed class Tracked { public float BaseSize; public bool Crit; public Character Victim; public Vector3 Jitter; } private struct EnemyHit { public float Time; public Vector3 Point; public Character Victim; public bool Backstab; } [HarmonyPatch(typeof(DamageText), "Awake")] private static class LogVanillaValues { private static void Postfix(DamageText __instance) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)($"vanilla DamageText: maxTextDistance={__instance.m_maxTextDistance} " + $"smallFontDistance={__instance.m_smallFontDistance} " + $"textDuration={__instance.m_textDuration} " + $"fontSize large={__instance.m_largeFontSize} small={__instance.m_smallFontSize} " + $"- enemy hits will use maxTextDistance={80f}")); } } } [HarmonyPatch(typeof(DamageText), "Awake")] private static class LogFonts { private static void Postfix(DamageText __instance) { TMP_Text val = (((Object)(object)__instance.m_worldTextBase == (Object)null) ? null : __instance.m_worldTextBase.GetComponent<TMP_Text>()); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("combat text font in use: " + (((Object)(object)((val != null) ? val.font : null) == (Object)null) ? "unknown" : ((Object)val.font).name))); } List<string> list = new List<string>(); TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val2 in array) { if ((Object)(object)val2 != (Object)null && !list.Contains(((Object)val2).name)) { list.Add(((Object)val2).name); } } ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)string.Format("TMP font assets loaded ({0}): {1}", list.Count, string.Join(", ", list))); } } } [HarmonyPatch(typeof(Character), "ApplyDamage")] private static class MarkEnemyHit { private static float _saved = -1f; private static void Prefix(Character __instance, HitData hit, bool showDamageText) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (showDamageText && hit != null && !((Object)(object)__instance == (Object)null) && !__instance.IsPlayer() && !__instance.IsTamed()) { bool backstab = BackstabTimeField != null && (float)BackstabTimeField.GetValue(__instance) == Time.time; EnemyHits.Add(new EnemyHit { Time = Time.time, Point = hit.m_point, Victim = __instance, Backstab = backstab }); DamageText instance = DamageText.instance; if ((Object)(object)instance != (Object)null && instance.m_maxTextDistance < 80f) { _saved = instance.m_maxTextDistance; instance.m_maxTextDistance = 80f; } } } private static void Postfix() { if (!(_saved < 0f)) { DamageText instance = DamageText.instance; if ((Object)(object)instance != (Object)null) { instance.m_maxTextDistance = _saved; } _saved = -1f; } } } [HarmonyPatch(typeof(DamageText), "AddInworldText")] private static class TagNewText { private static void Postfix(DamageText __instance, TextType type, Vector3 pos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Invalid comparison between Unknown and I4 //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Invalid comparison between Unknown and I4 //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_00dc: 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_00e8: 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) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_012e: 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_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) if (!WasEnemyHit(pos, out var victim, out var backstab) || !(TextsField?.GetValue(__instance) is IList { Count: not 0 } list) || TextFieldField == null) { return; } object? value = TextFieldField.GetValue(list[list.Count - 1]); TMP_Text val = (TMP_Text)((value is TMP_Text) ? value : null); if (!((Object)(object)val == (Object)null) && !Live.ContainsKey(val)) { bool flag = (int)type == 7 || (int)type == 2 || backstab; if ((int)type == 0 && float.TryParse(val.text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && IsBigHit(result)) { flag = true; } Vector3 jitter = Vector3.zero; if (WorldPosField != null) { jitter = (Vector3)WorldPosField.GetValue(list[list.Count - 1]) - pos; } if ((int)type == 2) { Color color = ((Graphic)val).color; ((Graphic)val).color = new Color(1f, 0f, 0f, color.a); } else if (flag) { Color color2 = ((Graphic)val).color; ((Graphic)val).color = new Color(1f, 1f, 0f, color2.a); } if (flag) { val.fontStyle = (FontStyles)(val.fontStyle | 1); } if (flag && DurationField != null) { DurationField.SetValue(list[list.Count - 1], (float)DurationField.GetValue(list[list.Count - 1]) * 0.6f); } Live[val] = new Tracked { BaseSize = val.fontSize, Crit = flag, Victim = victim, Jitter = jitter }; } } } [HarmonyPatch(typeof(DamageText), "UpdateWorldTexts")] private static class AnchorToVictim { private static void Prefix(DamageText __instance) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) if (!(TextsField?.GetValue(__instance) is IList list) || TextFieldField == null || TimerField == null || WorldPosField == null) { return; } for (int i = 0; i < list.Count; i++) { object obj = list[i]; object? value = TextFieldField.GetValue(obj); TMP_Text val = (TMP_Text)((value is TMP_Text) ? value : null); if (val != null && !((Object)(object)val == (Object)null) && Live.TryGetValue(val, out var value2) && !((Object)(object)value2.Victim == (Object)null)) { float num = (float)TimerField.GetValue(obj); float num2 = (value2.Crit ? 2.5f : 1f); WorldPosField.SetValue(obj, value2.Victim.GetTopPoint() + value2.Jitter + Vector3.up * (num * num2)); } } } } [HarmonyPatch(typeof(DamageText), "UpdateWorldTexts")] private static class ScaleTexts { private static void Postfix(DamageText __instance) { if (!(TextsField?.GetValue(__instance) is IList list) || TextFieldField == null || TimerField == null) { return; } Dead.Clear(); foreach (KeyValuePair<TMP_Text, Tracked> item in Live) { if ((Object)(object)item.Key == (Object)null) { Dead.Add(item.Key); } } for (int i = 0; i < Dead.Count; i++) { Live.Remove(Dead[i]); } for (int j = 0; j < list.Count; j++) { object obj = list[j]; object? value = TextFieldField.GetValue(obj); TMP_Text val = (TMP_Text)((value is TMP_Text) ? value : null); if (val != null && !((Object)(object)val == (Object)null) && Live.TryGetValue(val, out var value2)) { float num = (value2.Crit ? (3f * CritCurve((float)TimerField.GetValue(obj))) : 2f); val.fontSize = value2.BaseSize * num; } } } } public const string PluginGuid = "com.jekkle.valheim.carturcombattext"; public const string PluginName = "Cartur's Combat Text"; public const string PluginVersion = "1.0.0"; public const float SizeScale = 2f; public const float CritSizeScale = 3f; private const float CritPeakScale = 3.5f; private const float CritScaleTime = 0.04f; private const float CritShrinkTime = 0.16f; private const float CritSettleScale = 0.9f; private const float CritSettleTime = 0.24f; private const float CritRiseSpeed = 2.5f; private const float CritDurationFactor = 0.6f; private const float EnemyTextDistance = 80f; private const float BigHitFactor = 2f; private const int BigHitSamples = 20; private static ManualLogSource Log; private static readonly Type WorldTextType = AccessTools.Inner(typeof(DamageText), "WorldTextInstance"); private static readonly FieldInfo TextsField = AccessTools.Field(typeof(DamageText), "m_worldTexts"); private static readonly FieldInfo TextFieldField = ((WorldTextType == null) ? null : AccessTools.Field(WorldTextType, "m_textField")); private static readonly FieldInfo TimerField = ((WorldTextType == null) ? null : AccessTools.Field(WorldTextType, "m_timer")); private static readonly FieldInfo WorldPosField = ((WorldTextType == null) ? null : AccessTools.Field(WorldTextType, "m_worldPos")); private static readonly FieldInfo DurationField = ((WorldTextType == null) ? null : AccessTools.Field(WorldTextType, "m_duration")); private static readonly FieldInfo BackstabTimeField = AccessTools.Field(typeof(Character), "m_backstabTime"); private static readonly Dictionary<TMP_Text, Tracked> Live = new Dictionary<TMP_Text, Tracked>(); private static readonly List<TMP_Text> Dead = new List<TMP_Text>(); private static readonly Queue<float> RecentHits = new Queue<float>(); private static float _recentSum; private const float PointLifetime = 2f; private static readonly List<EnemyHit> EnemyHits = new List<EnemyHit>(); private const float RiseSpeed = 1f; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; try { Harmony.CreateAndPatchAll(typeof(Plugin).Assembly, "com.jekkle.valheim.carturcombattext"); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded - scaling DamageText world text {2}x with a WoW-style crit pop.", "Cartur's Combat Text", "1.0.0", 2f)); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogWarning((object)string.Format("{0} failed to patch, leaving combat text vanilla: {1}", "Cartur's Combat Text", arg)); } } private static bool IsBigHit(float value) { bool result = RecentHits.Count >= 5 && _recentSum > 0f && value >= _recentSum / (float)RecentHits.Count * 2f; RecentHits.Enqueue(value); _recentSum += value; if (RecentHits.Count > 20) { _recentSum -= RecentHits.Dequeue(); } return result; } private static bool WasEnemyHit(Vector3 pos, out Character victim, out bool backstab) { //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) victim = null; backstab = false; float time = Time.time; for (int num = EnemyHits.Count - 1; num >= 0; num--) { if (time - EnemyHits[num].Time > 2f) { EnemyHits.RemoveAt(num); } else if (EnemyHits[num].Point == pos) { victim = EnemyHits[num].Victim; backstab = EnemyHits[num].Backstab; EnemyHits.RemoveAt(num); return true; } } return false; } private static float CritCurve(float age) { if (age < 0.04f) { return Mathf.Lerp(1f, 3.5f, age / 0.04f); } if (age < 0.16f) { return Mathf.Lerp(3.5f, 0.9f, (age - 0.04f) / 0.12f); } if (age < 0.24f) { return Mathf.Lerp(0.9f, 1f, (age - 0.16f) / 0.08f); } return 1f; } } }