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 EilifPaths v1.4.0
EilifPaths.dll
Decompiled 2 hours agousing System; 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.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("EilifPaths")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.4.0.0")] [assembly: AssemblyInformationalVersion("1.4.0+5a86245ed19a4e0bda8f09872dec10e534468f66")] [assembly: AssemblyProduct("EilifPaths")] [assembly: AssemblyTitle("EilifPaths")] [assembly: AssemblyVersion("1.4.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 EilifPaths { internal static class BedFire { internal const float DefaultExtraRange = 8f; internal static ConfigEntry<float> ExtraRange; internal static void Bind(ConfigFile config) { ExtraRange = config.Bind<float>("Bed", "extraFireRange", 8f, "Extra metres of reach for the bed's \"needs a fire nearby\" check, added on top of the fireplace's own heat area. 0 = vanilla (the bed must sit inside the fire's heat collider, roughly touching it). 8 = the Eilif default, comfortably more than double the vanilla reach. Client-side; it only affects your own bed claims and sleeps."); } internal static string Describe() { float num = ((ExtraRange != null) ? ExtraRange.Value : 0f); if (!(num <= 0f)) { return "+" + num.ToString("0.##", CultureInfo.InvariantCulture) + "m"; } return "vanilla"; } } [HarmonyPatch(typeof(Bed), "CheckFire")] internal static class Patch_Bed_CheckFire { private static bool Prefix(Bed __instance, ref bool __result) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0055: Unknown result type (might be due to invalid IL or missing references) try { ConfigEntry<float> extraRange = BedFire.ExtraRange; if (extraRange == null) { return true; } float value = extraRange.Value; if (!(value > 0f)) { return true; } if ((Object)(object)__instance == (Object)null) { return true; } Vector3 position = ((Component)__instance).transform.position; EffectArea val = EffectArea.IsPointInsideArea(position, (Type)1, value); if (!Object.op_Implicit((Object)(object)val)) { return true; } LogHit(position, val, value); __result = true; return false; } catch (Exception ex) { try { if (EilifPathsPlugin.Log != null) { EilifPathsPlugin.Log.LogWarning((object)("[EilifPaths] bed fire check failed, using vanilla: " + ex.Message)); } } catch { } return true; } } private static void LogHit(Vector3 bedPos, EffectArea area, float extra) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) try { if (EilifPathsPlugin.Log != null) { float num = Vector3.Distance(bedPos, ((Component)area).transform.position); float radius = area.GetRadius(); EilifPathsPlugin.Log.LogInfo((object)("[EilifPaths] bed fire check passed with +" + extra.ToString("0.##", CultureInfo.InvariantCulture) + "m: heat source '" + ((Object)area).name + "' at " + num.ToString("0.##", CultureInfo.InvariantCulture) + "m, its own heat radius " + radius.ToString("0.##", CultureInfo.InvariantCulture) + "m.")); } } catch { } } } [BepInPlugin("net.eilif.paths", "Eilif Paths", "1.4.0")] public class EilifPathsPlugin : BaseUnityPlugin { public enum PathType { None, Path, PavedRoad, Wood, Stone, Iron, HardWood } public const string PluginGuid = "net.eilif.paths"; public const string PluginName = "Eilif Paths"; public const string PluginVersion = "1.4.0"; private const string OldModGuid = "Menthus.bepinex.plugins.UsefulPaths"; internal static ManualLogSource Log; private const float GroundCheckRate = 0.4f; internal static readonly Dictionary<PathType, ConfigEntry<float>> Movement = new Dictionary<PathType, ConfigEntry<float>>(); internal static readonly Dictionary<PathType, ConfigEntry<float>> StaminaDrain = new Dictionary<PathType, ConfigEntry<float>>(); internal static readonly Dictionary<PathType, ConfigEntry<float>> ActionStamina = new Dictionary<PathType, ConfigEntry<float>>(); internal static PathType Current { get; private set; } = PathType.None; internal static bool OldModPresent { get; private set; } private void BindSurface(PathType type, float move, float stam, float action) { string text = type.ToString(); Movement[type] = ((BaseUnityPlugin)this).Config.Bind<float>(text, "movement", move, "Speed multiplier while on " + text + " (1.0 = vanilla, >1 = faster)."); StaminaDrain[type] = ((BaseUnityPlugin)this).Config.Bind<float>(text, "staminadrain", stam, "Stamina-cost multiplier for ordinary movement while on " + text + ": running, jumping, swimming, dodging, being encumbered (1.0 = vanilla, <1 = drains less)."); ActionStamina[type] = ((BaseUnityPlugin)this).Config.Bind<float>(text, "actionstamina", action, "Stamina-cost multiplier for tools and weapons while on " + text + ": attacks, blocking, bow draw, building, hoe and cultivator terrain work, repairs, fishing. 1 = vanilla cost, 0 = free."); } private void Awake() { //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; BindSurface(PathType.Path, 1.4f, 0.25f, 1f); BindSurface(PathType.PavedRoad, 1.4f, 0.25f, 1f); BindSurface(PathType.Wood, 1.4f, 0.25f, 0f); BindSurface(PathType.Stone, 1.4f, 0.25f, 0f); BindSurface(PathType.Iron, 1.4f, 0.25f, 0f); BindSurface(PathType.HardWood, 1.4f, 0.25f, 0f); BedFire.Bind(((BaseUnityPlugin)this).Config); StationRange.Bind(((BaseUnityPlugin)this).Config); OldModPresent = Chainloader.PluginInfos != null && Chainloader.PluginInfos.ContainsKey("Menthus.bepinex.plugins.UsefulPaths"); if (OldModPresent) { Log.LogWarning((object)"========================================================================"); Log.LogWarning((object)"[EilifPaths] The OLD 'Useful_Paths' (Menthus.bepinex.plugins.UsefulPaths)"); Log.LogWarning((object)"[EilifPaths] mod is ALSO loaded. It is superseded by EilifPaths and its"); Log.LogWarning((object)"[EilifPaths] path/road detection is broken on current Valheim. To avoid"); Log.LogWarning((object)"[EilifPaths] DOUBLE-APPLYING bonuses on built floors, EilifPaths is ceding"); Log.LogWarning((object)"[EilifPaths] Wood/Stone/Iron/HardWood floors to the old mod and handling"); Log.LogWarning((object)"[EilifPaths] ONLY dirt Path + PavedRoad here. >>> Please DISABLE Useful_Paths"); Log.LogWarning((object)"[EilifPaths] in the r2modman UI (and re-export the pack) for correct values."); Log.LogWarning((object)"========================================================================"); } ((MonoBehaviour)this).InvokeRepeating("UpdateGround", 0f, 0.4f); Harmony val = new Harmony("net.eilif.paths"); int num = 0; int num2 = 0; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(EilifPathsPlugin).Assembly); foreach (Type type in typesFromAssembly) { try { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0) { num2++; val.CreateClassProcessor(type).Patch(); num++; } } catch (Exception ex) { Log.LogError((object)("[EilifPaths] could not apply patch class " + ((type != null) ? type.Name : "?") + ": " + ex.Message)); } } ToolStamina.Apply(val); Log.LogInfo((object)("[EilifPaths] Eilif Paths v1.4.0 loaded. Surfaces (speed / movement stamina / tool stamina): Path x" + F(Movement[PathType.Path]) + "/x" + F(StaminaDrain[PathType.Path]) + "/x" + F(ActionStamina[PathType.Path]) + ", PavedRoad x" + F(Movement[PathType.PavedRoad]) + "/x" + F(StaminaDrain[PathType.PavedRoad]) + "/x" + F(ActionStamina[PathType.PavedRoad]) + ", floors x" + F(Movement[PathType.Wood]) + "/x" + F(StaminaDrain[PathType.Wood]) + "/x" + F(ActionStamina[PathType.Wood]) + ". Polling every " + 0.4f.ToString("0.0", CultureInfo.InvariantCulture) + "s. Bed fire range: " + BedFire.Describe() + ". Workstation attachment range: " + StationRange.Describe() + ". Core patch classes: " + num + "/" + num2 + " applied.")); } private static string F(ConfigEntry<float> c) { return c.Value.ToString("0.##", CultureInfo.InvariantCulture); } private void UpdateGround() { //IL_00a0: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_00f8: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected I4, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) ToolStamina.SanityReset(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { SetCurrent(PathType.None); return; } PathType pathType = PathType.None; if (!OldModPresent) { try { Collider lastGroundCollider = ((Character)localPlayer).GetLastGroundCollider(); if ((Object)(object)lastGroundCollider != (Object)null) { WearNTear componentInParent = ((Component)lastGroundCollider).GetComponentInParent<WearNTear>(); if ((Object)(object)componentInParent != (Object)null) { MaterialType materialType = componentInParent.m_materialType; switch ((int)materialType) { case 0: pathType = PathType.Wood; break; case 1: pathType = PathType.Stone; break; case 2: pathType = PathType.Iron; break; case 3: pathType = PathType.HardWood; break; } } } } catch (Exception ex) { Log.LogWarning((object)("[EilifPaths] floor check failed: " + ex.Message)); } } if (pathType == PathType.None) { try { Vector3 position = ((Component)localPlayer).transform.position; Heightmap val = Heightmap.FindHeightmap(position); if ((Object)(object)val != (Object)null) { Vector3 val2 = position; val2.x -= 0.5f; val2.z -= 0.5f; int num = default(int); int num2 = default(int); val.WorldToVertexMask(val2, ref num, ref num2); Color paintMask = val.GetPaintMask(num, num2); if (paintMask.b > 0.5f) { pathType = PathType.PavedRoad; } else if (paintMask.r > 0.5f) { pathType = PathType.Path; } } } catch (Exception ex2) { Log.LogWarning((object)("[EilifPaths] paint check failed: " + ex2.Message)); } } SetCurrent(pathType); } private static void SetCurrent(PathType t) { if (t != Current) { Current = t; if (t == PathType.None) { Log.LogInfo((object)"[EilifPaths] terrain: None (vanilla speed/stamina)"); return; } float value = Movement[t].Value; float value2 = StaminaDrain[t].Value; float value3 = ActionStamina[t].Value; Log.LogInfo((object)(string.Format("[EilifPaths] terrain: {0} (x{1} speed, ", t, value.ToString("0.##", CultureInfo.InvariantCulture)) + "x" + value2.ToString("0.##", CultureInfo.InvariantCulture) + " movement stamina, x" + value3.ToString("0.##", CultureInfo.InvariantCulture) + " tool stamina)")); } } internal static float MoveMult() { if (Current != PathType.None) { return Movement[Current].Value; } return 1f; } internal static float StamMult() { if (Current != PathType.None) { return StaminaDrain[Current].Value; } return 1f; } internal static float ActionMult() { if (Current != PathType.None) { return ActionStamina[Current].Value; } return 1f; } internal static float CostMult() { if (Current == PathType.None) { return 1f; } if (ToolStamina.Active) { return ActionMult(); } if (ToolStamina.Degraded) { return Mathf.Max(ActionMult(), StamMult()); } return StamMult(); } } [HarmonyPatch(typeof(Player), "GetJogSpeedFactor")] internal static class Patch_GetJogSpeedFactor { private static void Postfix(ref float __result) { if (EilifPathsPlugin.Current != EilifPathsPlugin.PathType.None) { __result *= EilifPathsPlugin.MoveMult(); } } } [HarmonyPatch(typeof(Player), "GetRunSpeedFactor")] internal static class Patch_GetRunSpeedFactor { private static void Postfix(ref float __result) { if (EilifPathsPlugin.Current != EilifPathsPlugin.PathType.None) { __result *= EilifPathsPlugin.MoveMult(); } } } [HarmonyPatch(typeof(Player), "UseStamina")] internal static class Patch_UseStamina { private static void Prefix(Player __instance, ref float v) { try { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && EilifPathsPlugin.Current != EilifPathsPlugin.PathType.None) { v *= EilifPathsPlugin.CostMult(); } } catch { } } } [HarmonyPatch(typeof(Character), "UpdateWalking")] internal static class Patch_UpdateWalking { private static void Prefix(Character __instance, out float __state) { __state = (((Object)(object)__instance != (Object)null) ? __instance.m_walkSpeed : 0f); if ((Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && EilifPathsPlugin.Current != EilifPathsPlugin.PathType.None) { __instance.m_walkSpeed *= EilifPathsPlugin.MoveMult(); } } private static void Postfix(Character __instance, float __state) { if ((Object)(object)__instance != (Object)null) { __instance.m_walkSpeed = __state; } } } internal static class StationRange { internal const float DefaultExtraRange = 10f; internal static ConfigEntry<float> ExtraRange; private static readonly HashSet<string> Reported = new HashSet<string>(); internal static void Bind(ConfigFile config) { ExtraRange = config.Bind<float>("Workstation", "extraAttachmentRange", 10f, "Extra metres of reach between a crafting station and its upgrades/attachments, for EVERY station (workbench, forge, black forge, galdr table, artisan table, ...). Added on top of each attachment piece's own built-in distance, which is 5 m for most of them, so the default 10 gives about 15 m. Applies to both halves of the rule at once: where the game lets you place the attachment, and whether the station counts it towards its level. 0 = vanilla. Client-side; it only affects your own placement and your own crafting UI."); } internal static string Describe() { float v = ((ExtraRange != null) ? ExtraRange.Value : 0f); if (!IsActive(v)) { return "vanilla"; } return "+" + v.ToString("0.##", CultureInfo.InvariantCulture) + "m"; } internal static bool IsActive(float v) { if (v > 0f && !float.IsNaN(v)) { return !float.IsInfinity(v); } return false; } internal static void LogOnce(string prefabName, float baseRange, float newRange) { try { if (EilifPathsPlugin.Log != null) { string text = prefabName ?? "?"; if (!Reported.Contains(text)) { Reported.Add(text); EilifPathsPlugin.Log.LogInfo((object)("[EilifPaths] workstation attachment '" + text + "': reach " + baseRange.ToString("0.##", CultureInfo.InvariantCulture) + "m -> " + newRange.ToString("0.##", CultureInfo.InvariantCulture) + "m.")); } } } catch { } } } [HarmonyPatch(typeof(StationExtension), "Awake")] internal static class Patch_StationExtension_Awake { private static void Postfix(StationExtension __instance) { try { ConfigEntry<float> extraRange = StationRange.ExtraRange; if (extraRange == null) { return; } float value = extraRange.Value; if (StationRange.IsActive(value) && !((Object)(object)__instance == (Object)null)) { float maxStationDistance = __instance.m_maxStationDistance; if (!float.IsNaN(maxStationDistance) && !float.IsInfinity(maxStationDistance)) { StationRange.LogOnce(newRange: __instance.m_maxStationDistance = maxStationDistance + value, prefabName: CleanName(((Object)__instance).name), baseRange: maxStationDistance); } } } catch (Exception ex) { try { if (EilifPathsPlugin.Log != null) { EilifPathsPlugin.Log.LogWarning((object)("[EilifPaths] workstation attachment range failed, using vanilla: " + ex.Message)); } } catch { } } } private static string CleanName(string name) { if (string.IsNullOrEmpty(name)) { return "?"; } int num = name.IndexOf("(Clone)", StringComparison.Ordinal); if (num <= 0) { return name; } return name.Substring(0, num); } } internal static class ToolStamina { private sealed class Site { internal readonly Type Owner; internal readonly string Method; internal readonly Type[] Args; internal readonly string What; internal Site(Type owner, string method, Type[] args, string what) { Owner = owner; Method = method; Args = args; What = what; } internal string Label() { return ((Owner != null) ? Owner.Name : "?") + "." + Method; } } private static int s_depth; private static bool s_leakReported; internal static bool Active => s_depth > 0; internal static bool Degraded { get; private set; } private static void ScopePrefix() { try { if (s_depth < int.MaxValue) { s_depth++; } } catch { } } private static void ScopeFinalizer() { try { if (s_depth > 0) { s_depth--; } } catch { } } internal static void SanityReset() { if (s_depth == 0) { return; } int num = s_depth; s_depth = 0; if (s_leakReported) { return; } s_leakReported = true; try { if (EilifPathsPlugin.Log != null) { EilifPathsPlugin.Log.LogWarning((object)("[EilifPaths] tool/weapon stamina context was left open (depth " + num + ") and has been reset. Stamina costs are correct again; this notice is logged once.")); } } catch { } } private static Site[] Sites() { return new Site[9] { new Site(typeof(Attack), "Update", new Type[1] { typeof(float) }, "melee and ranged swings"), new Site(typeof(Attack), "FireProjectileBurst", new Type[0], "projectile bursts"), new Site(typeof(Humanoid), "BlockAttack", new Type[2] { typeof(HitData), typeof(Character) }, "blocking and parry"), new Site(typeof(Player), "UpdatePlacement", new Type[2] { typeof(bool), typeof(float) }, "building, hoe and cultivator, piece removal"), new Site(typeof(Player), "Repair", new Type[2] { typeof(ItemData), typeof(Piece) }, "repairs"), new Site(typeof(Player), "UpdateAttackBowDraw", new Type[2] { typeof(ItemData), typeof(float) }, "bow and crossbow draw"), new Site(typeof(Player), "UpdateActionQueue", new Type[1] { typeof(float) }, "crossbow reload"), new Site(typeof(FishingFloat), "FixedUpdate", new Type[0], "fishing"), new Site(typeof(SE_Harpooned), "UpdateStatusEffect", new Type[1] { typeof(float) }, "harpoon drag") }; } internal static void Apply(Harmony harmony) { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown try { if (harmony == null) { MarkDegraded("Harmony instance was null"); return; } MethodInfo methodInfo = AccessTools.Method(typeof(ToolStamina), "ScopePrefix", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(ToolStamina), "ScopeFinalizer", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { MarkDegraded("the patch bodies themselves could not be resolved"); return; } int num = 0; Site[] array = Sites(); Site[] array2 = array; foreach (Site site in array2) { try { MethodBase methodBase = AccessTools.Method(site.Owner, site.Method, site.Args, (Type[])null); if (methodBase == null) { MarkDegraded("could not find " + site.Label() + " (" + site.What + ")"); } else { harmony.CreateProcessor(methodBase).AddPrefix(new HarmonyMethod(methodInfo)).AddFinalizer(new HarmonyMethod(methodInfo2)) .Patch(); num++; } } catch (Exception ex) { MarkDegraded("could not hook " + site.Label() + " (" + site.What + "): " + ex.Message); } } try { if (EilifPathsPlugin.Log != null) { EilifPathsPlugin.Log.LogInfo((object)("[EilifPaths] tool/weapon stamina hooks: " + num + "/" + array.Length + " applied" + (Degraded ? " (DEGRADED - see the errors above)" : "") + ".")); } } catch { } } catch (Exception ex2) { MarkDegraded("hook setup failed: " + ex2.Message); } } private static void MarkDegraded(string reason) { Degraded = true; try { if (EilifPathsPlugin.Log != null) { EilifPathsPlugin.Log.LogError((object)("[EilifPaths] tool/weapon stamina hook problem: " + reason + ". Falling back to the safer of the two multipliers for charges that can no longer be classified (vanilla cost on paths and roads, 0.25 on built floors).")); } } catch { } } } }