Decompiled source of RecentXpBars v0.13.0

plugins/ValheimRecentXpBars.dll

Decompiled a day ago
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<bool> CfgEnabled;

	public static ConfigEntry<int> CfgVerticalOffsetPx;

	public static ConfigEntry<float> CfgBarWidthFraction;

	public static ConfigEntry<float> 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<bool>("General", "Enabled", true, "Master toggle for the recent-xp HUD.");
		CfgVerticalOffsetPx = ((BaseUnityPlugin)this).Config.Bind<int>("Layout", "VerticalOffsetPx", 0, "Pixels above the bottom edge of the screen (1080p-reference units).");
		CfgBarWidthFraction = ((BaseUnityPlugin)this).Config.Bind<float>("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<float>("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<XpBarHud>();
		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<SkillEntry> _entries;

	public RecentSkillTracker(int capacity = 3)
	{
		_capacity = capacity;
		_entries = new List<SkillEntry>(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<SkillEntry> Snapshot()
	{
		return new List<SkillEntry>(_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<Skill>();
	}

	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<SkillEntry> 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();
	}
}