using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("ValheimRecentXpBars")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ValheimRecentXpBars")] [assembly: AssemblyTitle("ValheimRecentXpBars")] [assembly: AssemblyVersion("1.0.0.0")] namespace ValheimRecentXpBars; [BepInPlugin("com.foxego.valheim.recentxpbars", "Recent XP Bars", "0.13.0")] public class Plugin : BaseUnityPlugin { public const string ModId = "com.foxego.valheim.recentxpbars"; public const string ModName = "Recent XP Bars"; public const string ModVersion = "0.13.0"; public static ConfigEntry CfgEnabled; public static ConfigEntry CfgVerticalOffsetPx; public static ConfigEntry CfgBarWidthFraction; public static ConfigEntry CfgUiScale; private Harmony _harmony; private GameObject _hudHost; public static RecentSkillTracker Tracker { get; private set; } private void Awake() { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown Tracker = new RecentSkillTracker(4); CfgEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master toggle for the recent-xp HUD."); CfgVerticalOffsetPx = ((BaseUnityPlugin)this).Config.Bind("Layout", "VerticalOffsetPx", 0, "Pixels above the bottom edge of the screen (1080p-reference units)."); CfgBarWidthFraction = ((BaseUnityPlugin)this).Config.Bind("Layout", "BarWidthFraction", 0.25f, "Each bar's width as a fraction of window width (0.25 = 1/4). Four bars are tiled across the bottom edge."); CfgUiScale = ((BaseUnityPlugin)this).Config.Bind("Layout", "UiScale", 1f, "Extra UI multiplier on top of resolution-based auto-scaling. 1.5 = 50% bigger."); _harmony = new Harmony("com.foxego.valheim.recentxpbars"); _harmony.PatchAll(typeof(SkillXpPatch)); _hudHost = new GameObject("RecentXpBarsHud"); _hudHost.AddComponent(); Object.DontDestroyOnLoad((Object)(object)_hudHost); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Recent XP Bars 0.13.0 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)_hudHost != (Object)null) { Object.Destroy((Object)(object)_hudHost); } } } public struct SkillEntry { public int SkillId; public int Level; public float Progress; public float LastGain; public float SessionTotal; } public class RecentSkillTracker { private readonly int _capacity; private readonly List _entries; public RecentSkillTracker(int capacity = 3) { _capacity = capacity; _entries = new List(capacity); } public void Record(int skillId, float gain, int newLevel, float newProgress) { int num = _entries.FindIndex((SkillEntry e) => e.SkillId == skillId); if (num >= 0) { SkillEntry item = _entries[num]; item.Level = newLevel; item.Progress = newProgress; item.LastGain = gain; item.SessionTotal += gain; _entries.RemoveAt(num); _entries.Insert(0, item); return; } SkillEntry skillEntry = default(SkillEntry); skillEntry.SkillId = skillId; skillEntry.Level = newLevel; skillEntry.Progress = newProgress; skillEntry.LastGain = gain; skillEntry.SessionTotal = gain; SkillEntry item2 = skillEntry; if (_entries.Count >= _capacity) { _entries.RemoveAt(_entries.Count - 1); } _entries.Insert(0, item2); } public List Snapshot() { return new List(_entries); } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] public static class SkillXpPatch { private struct Snapshot { public SkillType Type; public int Level; public float Progress; public bool Valid; } private static Snapshot _pre; private static Skill GetInnerSkill(Skills s, SkillType t) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) return Traverse.Create((object)s).Method("GetSkill", new object[1] { t }).GetValue(); } private static void Prefix(Skills __instance, SkillType skillType, float factor) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0021: 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_0037: Unknown result type (might be due to invalid IL or missing references) _pre = default(Snapshot); if ((int)skillType != 0 && (int)skillType != 999 && !(factor <= 0f)) { Skill innerSkill = GetInnerSkill(__instance, skillType); if (innerSkill != null) { Snapshot pre = default(Snapshot); pre.Type = skillType; pre.Level = (int)innerSkill.m_level; pre.Progress = innerSkill.GetLevelPercentage(); pre.Valid = true; _pre = pre; } } } private static void Postfix(Skills __instance, SkillType skillType, float factor) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected I4, but got Unknown if (!_pre.Valid || _pre.Type != skillType) { return; } Skill innerSkill = GetInnerSkill(__instance, skillType); if (innerSkill != null) { int num = (int)innerSkill.m_level; float levelPercentage = innerSkill.GetLevelPercentage(); float num2 = (float)(num - _pre.Level) + (levelPercentage - _pre.Progress); if (!(num2 <= 0f)) { Plugin.Tracker?.Record((int)skillType, num2, num, levelPercentage); } } } } public class XpBarHud : MonoBehaviour { private const float ReferenceHeight = 1080f; private const int RowHeightPx = 20; private const int FontSize = 12; private const int FramePx = 2; private const int TextPad = 10; private const int TickCount = 10; private const int GradientHeight = 64; private static readonly int RestedHash = StringExtensionMethods.GetStableHashCode("Rested"); private static Texture2D _frameTex; private static Texture2D _bgTex; private static Texture2D _fillTexRested; private static Texture2D _fillTexNotRested; private static Texture2D _tickTex; private static GUIStyle _leftStyle; private static GUIStyle _outlineStyle; private void OnGUI() { //IL_007e: 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) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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_0193: Unknown result type (might be due to invalid IL or missing references) if (Plugin.CfgEnabled == null || !Plugin.CfgEnabled.Value || (Object)(object)Player.m_localPlayer == (Object)null || Hud.IsUserHidden()) { return; } EnsureTextures(); EnsureStyle(); List list = Plugin.Tracker?.Snapshot(); if (list != null && list.Count != 0) { float num = Mathf.Max(0.5f, (float)Screen.height / 1080f) * Mathf.Max(0.1f, Plugin.CfgUiScale.Value); Matrix4x4 matrix = GUI.matrix; GUI.matrix = Matrix4x4.TRS(new Vector3(0f, (float)Screen.height, 0f), Quaternion.identity, new Vector3(num, num, 1f)) * Matrix4x4.Translate(new Vector3(0f, (float)(-Screen.height) / num, 0f)); int num2 = Mathf.RoundToInt((float)Screen.width / num); int num3 = Mathf.RoundToInt((float)Screen.height / num); float num4 = Mathf.Clamp(Plugin.CfgBarWidthFraction.Value, 0.05f, 1f); int num5 = Mathf.RoundToInt((float)num2 * num4); int num6 = Mathf.Max(0, Plugin.CfgVerticalOffsetPx.Value); int y = num3 - num6 - 20; SEMan sEMan = ((Character)Player.m_localPlayer).GetSEMan(); Texture2D fillTex = ((sEMan != null && sEMan.HaveStatusEffect(RestedHash)) ? _fillTexRested : _fillTexNotRested); for (int i = 0; i < list.Count; i++) { int x = i * num5; DrawRow(list[i], x, y, num5, fillTex); } GUI.matrix = matrix; } } private void DrawRow(SkillEntry e, int x, int y, int barWidth, Texture2D fillTex) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_00e5: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(new Rect((float)x, (float)y, (float)barWidth, 20f), (Texture)(object)_frameTex); int num = x + 2; int num2 = y + 2; int num3 = barWidth - 4; int num4 = 16; GUI.DrawTexture(new Rect((float)num, (float)num2, (float)num3, (float)num4), (Texture)(object)_bgTex); float num5 = Mathf.Clamp01(e.Progress); int num6 = Mathf.RoundToInt((float)num3 * num5); if (num6 > 0) { GUI.DrawTexture(new Rect((float)num, (float)num2, (float)num6, (float)num4), (Texture)(object)fillTex); } for (int i = 1; i < 10; i++) { GUI.DrawTexture(new Rect((float)(num + Mathf.RoundToInt((float)(num3 * i) / 10f)), (float)num2, 1f, (float)num4), (Texture)(object)_tickTex); } string arg = LocalizeSkillName(e.SkillId); string text = $"{arg} {e.Level}"; DrawLabelWithOutline(new Rect((float)(num + 10), (float)num2, (float)(num3 - 20), (float)num4), text); } private static void EnsureTextures() { //IL_0021: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_frameTex == (Object)null) { _frameTex = MakeSolidTex(new Color(0.5f, 0.36f, 0.2f, 0.3f)); } if ((Object)(object)_bgTex == (Object)null) { _bgTex = MakeGradientTex(new Color(0.06f, 0.05f, 0.03f, 1f), 0.25f); } if ((Object)(object)_fillTexRested == (Object)null) { _fillTexRested = MakeGradientTex(new Color(0.95f, 0.72f, 0.18f, 1f), 0.3f); } if ((Object)(object)_fillTexNotRested == (Object)null) { _fillTexNotRested = MakeGradientTex(new Color(0.95f, 0.2f, 0.15f, 1f), 0.4f); } if ((Object)(object)_tickTex == (Object)null) { _tickTex = MakeSolidTex(new Color(0f, 0f, 0f, 0.35f)); } } private static Texture2D MakeSolidTex(Color c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1) { hideFlags = (HideFlags)61 }; val.SetPixel(0, 0, c); val.Apply(); return val; } private static Texture2D MakeGradientTex(Color baseColor, float targetAlpha) { //IL_0003: 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_0011: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(1, 64) { hideFlags = (HideFlags)61 }; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; Color val2 = default(Color); for (int i = 0; i < 64; i++) { float num = Mathf.Abs((float)i / 63f - 0.5f) * 2f; float num2 = Mathf.Lerp(1.25f, 0.55f, num); ((Color)(ref val2))..ctor(Mathf.Clamp01(baseColor.r * num2), Mathf.Clamp01(baseColor.g * num2), Mathf.Clamp01(baseColor.b * num2), targetAlpha); val.SetPixel(0, i, val2); } val.Apply(); return val; } private static void EnsureStyle() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown if (_leftStyle == null) { GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 12, fontStyle = (FontStyle)1, alignment = (TextAnchor)3 }; val.normal.textColor = new Color(0.98f, 0.94f, 0.85f, 1f); _leftStyle = val; } if (_outlineStyle == null) { GUIStyle val2 = new GUIStyle(GUI.skin.label) { fontSize = 12, fontStyle = (FontStyle)1, alignment = (TextAnchor)3 }; val2.normal.textColor = new Color(0f, 0f, 0f, 1f); _outlineStyle = val2; } } private static void DrawLabelWithOutline(Rect r, string text) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) int[] array = new int[8] { -1, 0, 1, -1, 1, -1, 0, 1 }; int[] array2 = new int[8] { -1, -1, -1, 0, 0, 1, 1, 1 }; for (int i = 0; i < array.Length; i++) { GUI.Label(new Rect(((Rect)(ref r)).x + (float)array[i], ((Rect)(ref r)).y + (float)array2[i], ((Rect)(ref r)).width, ((Rect)(ref r)).height), text, _outlineStyle); } GUI.Label(r, text, _leftStyle); } private static string LocalizeSkillName(int skillId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) SkillType val = (SkillType)skillId; string text = ((object)(SkillType)(ref val)).ToString(); string text2 = "$skill_" + text.ToLowerInvariant(); try { if (Localization.instance != null) { string text3 = Localization.instance.Localize(text2); if (!string.IsNullOrEmpty(text3) && text3 != text2) { return text3; } } } catch { } return PrettifyEnumName(text); } private static string PrettifyEnumName(string name) { StringBuilder stringBuilder = new StringBuilder(name.Length + 4); for (int i = 0; i < name.Length; i++) { if (i > 0 && char.IsUpper(name[i]) && !char.IsUpper(name[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(name[i]); } return stringBuilder.ToString(); } }