Decompiled source of SkillFloors v1.1.5

plugins/SkillFloors.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SkillFloors")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SkillFloors")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("1.1.5")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.5.0")]
[module: UnverifiableCode]
namespace SkillFloors;

[BepInPlugin("Armikur.mod.Valheim.SkillFloors", "SkillFloors", "1.1.5")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
internal class SkillFloors : BaseUnityPlugin
{
	public const string PluginName = "SkillFloors";

	public const string PluginGUID = "Armikur.mod.Valheim.SkillFloors";

	public const string PluginVersion = "1.1.5";

	private readonly Harmony HarmonyInstance = new Harmony("Armikur.mod.Valheim.SkillFloors");

	internal static ConfigEntry<float> Config_Rate;

	internal static ConfigEntry<bool> Config_Debug;

	internal static bool BookIsLoaded = false;

	public static Dictionary<SkillType, FloorValues> Floors_Book = new Dictionary<SkillType, FloorValues>();

	private void Awake()
	{
		CreateConfigValues();
		SFLog.Info("Armikur's SkillFloors mod 1.1.5 has loaded!");
		Assembly executingAssembly = Assembly.GetExecutingAssembly();
		HarmonyInstance.PatchAll(executingAssembly);
	}

	private void CreateConfigValues()
	{
		//IL_0031: 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_003e: Expected O, but got Unknown
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Expected O, but got Unknown
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Expected O, but got Unknown
		Config_Rate = ((BaseUnityPlugin)this).Config.Bind<float>("Server Config", "Floor XP Gain Rate", 0.15f, new ConfigDescription("Multiplier for how much XP floor skills gain relative to regular skills (e.g., 0.15 = 15%). Values over 1 will give more XP than normal; helpful for catching up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1.5f), new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		} }));
		Config_Debug = ((BaseUnityPlugin)this).Config.Bind<bool>("Client Config", "Enable Debug Logging", false, new ConfigDescription("Shows each skill floor increase in the BepInEx window.", (AcceptableValueBase)null, Array.Empty<object>()));
	}

	public static void FreshFloorsBook()
	{
		Floors_Book = new Dictionary<SkillType, FloorValues>();
		if (Config_Debug.Value)
		{
			SFLog.Warn("Fresh Floors Book created");
		}
	}

	public static void UpdateFloor(Skill skill, float skillXPGain)
	{
		//IL_0006: 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_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: 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_0197: Unknown result type (might be due to invalid IL or missing references)
		SkillType skill2 = skill.m_info.m_skill;
		if (!Floors_Book.TryGetValue(skill2, out var value))
		{
			value = new FloorValues();
			Floors_Book[skill2] = value;
		}
		float num = CalcFloorReqXP(value.Level);
		float nextLevelRequirement = skill.GetNextLevelRequirement();
		float num2 = Mathf.Floor(skill.m_level);
		if (value.Level >= num2)
		{
			value.Level = num2;
			value.XP = 0f;
			if (Config_Debug.Value)
			{
				SFLog.Info($"{skill2} floor clamped at {value.Level} ({value.XP}/{num}) | Skill: {skill.m_level} ({skill.m_accumulator}/{nextLevelRequirement})");
			}
			return;
		}
		value.XP += skillXPGain * Config_Rate.Value;
		if (value.XP >= num)
		{
			value.Level += 1f;
			value.XP = 0f;
			FloorGainNotify(skill2, value.Level);
			SFLog.Info($"{skill2} floor increased to {value.Level}!! | Skill: {skill.m_level} ({skill.m_accumulator}/{nextLevelRequirement} )");
		}
		if (Config_Debug.Value)
		{
			SFLog.Info($"{skill2} Floor: {value.Level} ({value.XP}/{num}) | Skill: {skill.m_level} ({skill.m_accumulator}/{nextLevelRequirement})");
		}
	}

	public static float GetFloorLevel(SkillType type)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		if (!Floors_Book.TryGetValue(type, out var value))
		{
			return 0f;
		}
		return value.Level;
	}

	public static float CalcFloorReqXP(float curFloorLevel)
	{
		return Mathf.Pow(Mathf.Floor(curFloorLevel + 1f), 1.5f) * 0.5f + 0.5f;
	}

	private static void FloorGainNotify(SkillType skillType, float floorLevel)
	{
		if (!((Object)(object)MessageHud.instance == (Object)null))
		{
			string arg = Localization.instance.Localize("$skill_" + ((object)(SkillType)(ref skillType)).ToString().ToLower());
			string text = $"<color=#7D9FB8>{arg} floor increased to {floorLevel}</color>";
			((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null);
		}
	}
}
internal static class SFLog
{
	private const string prefix = "[SkillFloors] ";

	public static void Info(string msg)
	{
		Logger.LogInfo((object)("[SkillFloors] " + msg));
	}

	public static void Warn(string msg)
	{
		Logger.LogWarning((object)("[SkillFloors] " + msg));
	}

	public static void Err(string msg)
	{
		Logger.LogError((object)("[SkillFloors] " + msg));
	}
}
public class FloorValues
{
	public float Level;

	public float XP;
}
public class JSONFloorValuesReference
{
	public float SkillFloors_Floor_Level;

	public float SkillFloors_Floor_XPProgress;
}
[HarmonyPatch(typeof(Skill), "Raise")]
public class Patch_SkillFloor_Raise
{
	private static void Postfix(Skill __instance, float factor)
	{
		float increseStep = __instance.m_info.m_increseStep;
		float skillGainRate = Game.m_skillGainRate;
		float skillXPGain = increseStep * factor * skillGainRate;
		SkillFloors.UpdateFloor(__instance, skillXPGain);
	}
}
[HarmonyPatch(typeof(Player), "OnDeath")]
public class Patch_Player_OnDeath
{
	private static void Postfix(Player __instance)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: 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_0043: Unknown result type (might be due to invalid IL or missing references)
		foreach (Skill skill2 in ((Character)__instance).GetSkills().GetSkillList())
		{
			SkillType skill = skill2.m_info.m_skill;
			float floorLevel = SkillFloors.GetFloorLevel(skill);
			if (skill2.m_level < floorLevel)
			{
				skill2.m_level = floorLevel;
				SFLog.Info($"{skill} hit its SkillFloor. Holding the line (I mean floor!) at level {floorLevel}!.");
			}
		}
	}
}
[HarmonyPatch(typeof(SkillsDialog), "Setup")]
public class Patch_SkillsDialog
{
	private static void Postfix(SkillsDialog __instance, Player player)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: 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_00b0: 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_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: 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)
		List<Skill> skillList = ((Character)player).GetSkills().GetSkillList();
		for (int i = 0; i < skillList.Count && i < __instance.m_elements.Count; i++)
		{
			SkillType skill = skillList[i].m_info.m_skill;
			float floorLevel = SkillFloors.GetFloorLevel(skill);
			GameObject val = __instance.m_elements[i];
			TMP_Text component = ((Component)Utils.FindChild(val.transform, "name", (IterativeSearchType)0)).GetComponent<TMP_Text>();
			if ((Object)(object)component != (Object)null && floorLevel > 0f)
			{
				string arg = Localization.instance.Localize("$skill_" + ((object)(SkillType)(ref skill)).ToString().ToLower());
				component.text = $"{arg} <color=#7D9FB8><size=85%>{Mathf.FloorToInt(floorLevel)}</size></color>";
			}
			if (!SkillFloors.Floors_Book.TryGetValue(skill, out var value))
			{
				continue;
			}
			GuiBar component2 = ((Component)Utils.FindChild(val.transform, "currentlevel", (IterativeSearchType)0)).GetComponent<GuiBar>();
			if ((Object)(object)component2 != (Object)null)
			{
				Transform obj = Utils.FindChild(val.transform, "floorlevel", (IterativeSearchType)0);
				GuiBar val2 = ((obj != null) ? ((Component)obj).GetComponent<GuiBar>() : null);
				GuiBar val3;
				if ((Object)(object)val2 != (Object)null)
				{
					val3 = val2;
				}
				else
				{
					GameObject obj2 = Object.Instantiate<GameObject>(((Component)component2).gameObject, ((Component)component2).transform.parent);
					((Object)obj2).name = "floorlevel";
					val3 = obj2.GetComponent<GuiBar>();
					RectTransform component3 = obj2.GetComponent<RectTransform>();
					component3.anchoredPosition -= new Vector2(0f, 2f);
					component3.SetSizeWithCurrentAnchors((Axis)1, 2f);
				}
				float num = SkillFloors.CalcFloorReqXP(value.Level);
				float value2 = Mathf.Clamp01(value.XP / num);
				val3.SetValue(value2);
				val3.SetColor(new Color(0.66f, 0.76f, 0.84f, 1f));
			}
		}
	}
}
public static class SaveData
{
	private const string SaveDataKey = "SkillFloors_Data";

	private const string SaveDataKey_OLDJSON = "SkillFloors_SkillFloorData_JSON";

	private const int SaveVersion = 1;

	public static bool IsMainScene()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		Scene activeScene = SceneManager.GetActiveScene();
		return ((Scene)(ref activeScene)).name.Equals("main");
	}

	public static void Save_Floors(Player player)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Expected I4, but got Unknown
		ZPackage val = new ZPackage();
		val.Write(1);
		val.Write(SkillFloors.Floors_Book.Count);
		foreach (KeyValuePair<SkillType, FloorValues> item in SkillFloors.Floors_Book)
		{
			val.Write((int)item.Key);
			val.Write(item.Value.Level);
			val.Write(item.Value.XP);
		}
		player.m_customData["SkillFloors_Data"] = val.GetBase64();
		if (!SkillFloors.Config_Debug.Value)
		{
			SFLog.Info("Floors data saved");
		}
		Log_Book("Floors data saved");
	}

	public static void Load_Floors(Player player)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		//IL_0051: 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)
		if (player.m_customData.TryGetValue("SkillFloors_Data", out var value))
		{
			ZPackage val = new ZPackage(value);
			int num = val.ReadInt();
			if (num != 1)
			{
				SFLog.Warn($"ZPackage found, unsupported save version {num}, aborting");
				return;
			}
			int num2 = val.ReadInt();
			for (int i = 0; i < num2; i++)
			{
				SkillType key = (SkillType)val.ReadInt();
				float level = val.ReadSingle();
				float xP = val.ReadSingle();
				SkillFloors.Floors_Book[key] = new FloorValues
				{
					Level = level,
					XP = xP
				};
			}
			if (!SkillFloors.Config_Debug.Value)
			{
				SFLog.Info("Floors data loaded");
			}
			Log_Book("ZPackage found, Floors data loaded");
		}
		else
		{
			SFLog.Warn("No ZPackage found, Trying JSON (old) system instead");
			if (!Load_Floors_OLDJSON(player))
			{
				SFLog.Warn("No Floors data found (normal for first use / new characters).");
			}
		}
	}

	private static bool Load_Floors_OLDJSON(Player player)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: 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_0043: Expected O, but got Unknown
		//IL_0049: Expected O, but got Unknown
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		if (!player.m_customData.TryGetValue("SkillFloors_SkillFloorData_JSON", out var value))
		{
			if (SkillFloors.Config_Debug.Value)
			{
				SFLog.Warn("Tried to load from JSON data but found none.");
			}
			return false;
		}
		try
		{
			JsonSerializerSettings val = new JsonSerializerSettings
			{
				Converters = new List<JsonConverter> { (JsonConverter)new StringEnumConverter() }
			};
			Dictionary<SkillType, JSONFloorValuesReference> dictionary = JsonConvert.DeserializeObject<Dictionary<SkillType, JSONFloorValuesReference>>(value, val);
			if (dictionary == null || dictionary.Count == 0)
			{
				SFLog.Warn("Found JSON data, but it was empty. Aborting JSON load.");
				return false;
			}
			if (SkillFloors.Config_Debug.Value)
			{
				SFLog.Warn($"JSON save has entries: {dictionary?.Count ?? (-1)}");
				SFLog.Warn("JSON (raw): \n " + value);
			}
			foreach (KeyValuePair<SkillType, JSONFloorValuesReference> item in dictionary)
			{
				SkillFloors.Floors_Book[item.Key] = new FloorValues
				{
					Level = item.Value.SkillFloors_Floor_Level,
					XP = item.Value.SkillFloors_Floor_XPProgress
				};
			}
			Save_Floors(player);
			player.m_customData.Remove("SkillFloors_SkillFloorData_JSON");
			SFLog.Warn("JSON migrated to ZPackage. Old JSON removed.");
			return true;
		}
		catch (Exception arg)
		{
			SFLog.Err($"JSON migration failed:\n{arg}");
			return false;
		}
	}

	private static void Log_Book(string headerMsg = null)
	{
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		if (!SkillFloors.Config_Debug.Value)
		{
			return;
		}
		if (!string.IsNullOrWhiteSpace(headerMsg))
		{
			SFLog.Warn(headerMsg);
		}
		Dictionary<SkillType, FloorValues> floors_Book = SkillFloors.Floors_Book;
		if (floors_Book.Count == 0)
		{
			SFLog.Warn("xxxxxx Floors Book Is Empty xxxxxx");
			return;
		}
		SFLog.Warn("****** Current Floors Book ******");
		foreach (KeyValuePair<SkillType, FloorValues> item in floors_Book)
		{
			SFLog.Info($"** {item.Key}: Floor: {item.Value.Level}, Floor XP: {item.Value.XP}");
		}
	}
}
[HarmonyPatch(typeof(Player), "Save")]
public class Patch_Player_Save
{
	private static void Prefix(Player __instance)
	{
		if ((Object)(object)__instance == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)__instance)
		{
			if (SkillFloors.Config_Debug.Value)
			{
				SFLog.Warn("skip save: non-local or null player");
			}
		}
		else if (!SaveData.IsMainScene())
		{
			if (SkillFloors.Config_Debug.Value)
			{
				SFLog.Warn("skip save: not in-world");
			}
		}
		else
		{
			SaveData.Save_Floors(__instance);
		}
	}
}
[HarmonyPatch(typeof(Player), "OnSpawned")]
public static class Patch_Player_OnSpawned
{
	private static void Postfix(Player __instance)
	{
		if ((Object)(object)__instance == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)__instance)
		{
			if (SkillFloors.Config_Debug.Value)
			{
				SFLog.Warn("skip load: non-local or null player");
			}
			return;
		}
		if (!SaveData.IsMainScene())
		{
			if (SkillFloors.Config_Debug.Value)
			{
				SFLog.Warn("skip load: not in-world");
			}
			return;
		}
		if (SkillFloors.BookIsLoaded)
		{
			if (SkillFloors.Config_Debug.Value)
			{
				SFLog.Warn("skip load: only load once per login");
			}
			return;
		}
		if (SkillFloors.Config_Debug.Value)
		{
			SFLog.Warn("Refreshing Book & loading Floors data");
		}
		SkillFloors.FreshFloorsBook();
		SaveData.Load_Floors(__instance);
		SkillFloors.BookIsLoaded = true;
	}
}
[HarmonyPatch(typeof(Game), "Logout")]
public static class Patch_Game_Logout
{
	private static void Prefix()
	{
		SkillFloors.BookIsLoaded = false;
		if (SkillFloors.Config_Debug.Value)
		{
			SFLog.Warn("\"Loaded\" state reset");
		}
	}
}